2

I'm trying to define a bazel rule that will build 2 different cc_binaries for 2 different platforms with just 1 bazel build invocation. I'm struggling with how to define the transition properly and attach it.

I would like ultimately to be able to do something like:

cc_binary(
    name = "binary_platform_a"
    ...
)

cc_binary(
    name = "binary_platform_b"
    ...
)

my_custom_multi_arch_rule(
    name = "multiarch_build",
    binary_a = ":binary_platform_a",
    binary_b = ":binary_platform_b",
    ...
)

I have deduced from bazel documents: [https://bazel.build/rules/config#user-defined-transitions] that I need to do something like the following in a defs.bzl:

def _impl(settings, attr):
    _ignore = (settings, attr)
    return {
        "Platform A": {"//command_line_option:platform": "platform_a"},
        "Platform B": {"//command_line_option:platform": "platform_b"},
    }

multi_arch_transition = transition(
    implementation = _impl,
    inputs = [],
    outputs = ["//command_line_option:platform"]
)

def _rule_impl(ctx):
    # How to implement this?

my_custom_multi_arch_rule = rule(
    implementation = _rule_impl,
    attrs = {
        "binary_a": attr.label(cfg = multi_arch_transition)
        "binary_b": attr.label(cfg = multi_arch_transition)
        ...
    })

The best-case final scenario would be able to issue:

bazel build //path/to/my:multiarch_build

and it successfully builds my 2 separate binaries for their respective platforms.

seth
  • 31
  • 1

2 Answers2

0

Use ctx.split_attr.<attr name>[<transition key>] to get the configured Target object representing a particular arch configuration of a binary.

def _rule_impl(ctx):
   binary_a_platform_a = ctx.split_attr.binary_a["Platform A"]
   binary_a_platform_b = ctx.split_attr.binary_b["Platform B"]
   # ...

   return [DefaultInfo(files = depset([binary_a_platform_a, binary_b_platform_b, ...]))]

https://bazel.build/rules/config#accessing-attributes-with-transitions

Jin
  • 12,748
  • 3
  • 36
  • 41
0

With a few typos and other fixes, I got this to work using both you and jin's examples.

def _impl(settings, attr):
    _ignore = (settings, attr)

    # I'm using my default platform and a custom ARM
    # platform I have as platforms A and B.
    return {
        "Platform A": {"//command_line_option:platforms": "@local_config_platform//:host"},            
        "Platform B": {"//command_line_option:platforms": "//platforms:arm64"},
    }

multi_arch_transition = transition(
    implementation = _impl,
    inputs = [],
    outputs = ["//command_line_option:platforms"],
)

def _rule_impl(ctx):
    binary_a_platform_a = ctx.split_attr.binary_a["Platform A"]
    binary_b_platform_b = ctx.split_attr.binary_b["Platform B"]
    files = binary_a_platform_a.files.to_list() + binary_b_platform_b.files.to_list()

    return [DefaultInfo(
        files = depset(direct = files),
    )]

my_custom_multi_arch_rule = rule(
    implementation = _rule_impl,
    attrs = {
        "_allowlist_function_transition": attr.label(
            default = "@bazel_tools//tools/allowlists/function_transition_allowlist",
        ),
        "binary_a": attr.label(cfg = multi_arch_transition),
        "binary_b": attr.label(cfg = multi_arch_transition),
    },
)
Gonzalo Lucero
  • 1,717
  • 17
  • 16