Skip to main content

Build something else using an image

When dealing with upstream-adjacent software, it can be very useful to build a buck2 target using some system-installed tools (likely from dnf repositories).

For example, if you want to build a UKI, you can either hope and pray that systemd-ukify is installed on your build host and that the version is roughly what you have tested, or you can use an antlir2 image to get a well-known version every single time.

genrule_in_image

antlir2 offers a rule that behaves similarly to buck2's default genrule named genrule_in_image.

Just like a buck_genrule, this accepts a bash string attribute that holds a script that will be run to produce the rule output. The same macro expansions (like $(location), $(exe), etc) all work.

The main addition to the buck_genrule api is that genrule_in_image accepts an attribute that controls the environment in which the script is run (exe_layer or layer).

Cross-compilation

In most cases, you should use exe_layer= instead of layer= to choose what image layer your command will run in. This will give you a layer optimized for your build host and should generally be more performant, since you'll be running binaries built for your actual host architecture.

In the rarer occasions where you need to run tools built for the target platform (eg, aarch64 while your build host is x86_64), you can use layer=, which will cause the genrule_in_image to emulate your target architecture (including installing rpms built for the target architecture).

Examples

Produce a single file / directory

Produce a single output file (or directory) by running a command inside of an image, using inputs from other rules.

my/team/BUCK
load("//antlir/antlir2/bzl/image:defs.bzl", "image")
load("//antlir/antlir2/bzl/feature:defs.bzl", "feature")
load("//antlir/antlir2/genrule_in_image:genrule_in_image.bzl", "genrule_in_image")

image.layer(
name = "layer",
features = [
feature.rpms_install(rpms = ["systemd-ukify"]),
"//antlir/antlir2/genrule_in_image:prep",
]
)

export_file(
name = "vmlinuz",
)
export_file(
name = "initrd.cpio",
)

genrule_in_image(
name = "uki",
out = "uki",
exe_layer = ":layer",
bash = """
systemd-ukify build \
--linux $(location :vmlinuz) \
--initrd $(location :initrd.cpio) \
--output $OUT
""",
)

Produce a set of named outputs

Produce a set of named output files / directories by running a script inside of an image.

my/team/BUCK
load("//antlir/antlir2/bzl/image:defs.bzl", "image")
load("//antlir/antlir2/bzl/feature:defs.bzl", "feature")
load("//antlir/antlir2/genrule_in_image:genrule_in_image.bzl", "genrule_in_image")

image.layer(
name = "layer",
features = [
feature.rpms_install(rpms = ["systemd-ukify"]),
"//antlir/antlir2/genrule_in_image:prep",
]
)

export_file(
name = "vmlinuz",
)
export_file(
name = "initrd.cpio",
)

genrule_in_image(
name = "out",
outs = {
"uki": "uki",
"key": "key",
},
exe_layer = ":layer",
bash = """
systemd-ukify genkey --output $OUT/key

systemd-ukify sign \
--linux $(location :vmlinuz) \
--initrd $(location :initrd.cpio) \
--output $OUT/uki
""",
)

# The individual outputs are available as buck subtargets
export_file(
name = "uki",
src = ":out[uki]",
)