| def declare_transtion(attrs, flag_overrides = None, append_to_flags = None, executable = True): |
| """A helper that drastically simplifies declaration of a transition. |
| |
| A transition in Bazel is a way to force changes to the way the build is |
| evaluated for all dependencies of a given rule. |
| |
| Imagine the following simple dependency graph: |
| |
| ->: depends on |
| a -> b -> c |
| |
| Normally, if you set `defines` on a, they couldn't apply to b or c because |
| they are dependencies of a. There's no way for b or c to know about a's |
| settings, because they don't even know a exists! |
| |
| We can fix this via a transition! If we put a transition in front of `a` |
| that sets --copts=-DFOO=42, we're telling Bazel to build a and all of its |
| dependencies under that configuration. |
| |
| Note: Flags must be referenced as e.g. `//command_line_option:copt` in |
| transitions. |
| |
| `declare_transition()` eliminates the frustrating amount of boilerplate. All |
| you need to do is provide a set of attrs, and then a `flag_overrides` |
| dictionary that tells `declare_transition()` which attrs to pull flag values |
| from. The common `src` attr tells the transition which build rule to apply |
| the transition to. |
| """ |
| |
| def _flag_override_impl(settings, attrs): |
| final_overrides = {} |
| if flag_overrides != None: |
| final_overrides = { |
| key: str(getattr(attrs, value)) |
| for key, value in flag_overrides.items() |
| } |
| if append_to_flags != None: |
| for flag, field in append_to_flags.items(): |
| accumulated_flags = final_overrides.get(flag, settings.get(flag, [])) |
| accumulated_flags.extend( |
| [str(val) for val in getattr(attrs, field)], |
| ) |
| final_overrides[flag] = accumulated_flags |
| return final_overrides |
| |
| output_flags = [] |
| if flag_overrides != None: |
| output_flags.extend(flag_overrides.keys()) |
| if append_to_flags != None: |
| output_flags.extend(append_to_flags.keys()) |
| _transition = transition( |
| implementation = _flag_override_impl, |
| inputs = append_to_flags.keys() if append_to_flags != None else [], |
| outputs = output_flags, |
| ) |
| |
| def _symlink_artifact_impl(ctx): |
| out = ctx.actions.declare_file(ctx.label.name) |
| if executable: |
| ctx.actions.symlink(output = out, target_file = ctx.executable.src) |
| return [DefaultInfo(files = depset([out]), executable = out)] |
| |
| ctx.actions.symlink( |
| output = out, |
| target_file = ctx.attr.src[0][DefaultInfo].files.to_list()[0], |
| ) |
| return [DefaultInfo(files = depset([out]))] |
| |
| return rule( |
| implementation = _symlink_artifact_impl, |
| executable = executable, |
| attrs = { |
| "src": attr.label( |
| cfg = _transition, |
| executable = executable, |
| mandatory = True, |
| ), |
| } | attrs, |
| ) |
| |
| # This transition is applied before building the boot_stage2 image. |
| rp2040_bootloader_binary = declare_transtion( |
| attrs = { |
| "_malloc": attr.label(default = "//bazel:empty_cc_lib"), |
| # This could be shared, but we don't in order to make it clearer that |
| # a transition is in use. |
| "_allowlist_function_transition": attr.label( |
| default = "@bazel_tools//tools/allowlists/function_transition_allowlist", |
| ), |
| }, |
| flag_overrides = { |
| # We don't want --custom_malloc to ever apply to the bootloader, so |
| # always explicitly override it here. |
| "//command_line_option:custom_malloc": "_malloc", |
| }, |
| ) |
| |
| # This transition sets SDK configuration options required to build test binaries |
| # for the kitchen_sink suite of tests. |
| kitchen_sink_test_binary = declare_transtion( |
| attrs = { |
| "bt_stack_config": attr.label(mandatory = True), |
| "lwip_config": attr.label(mandatory = True), |
| # This could be shared, but we don't in order to make it clearer that |
| # a transition is in use. |
| "_allowlist_function_transition": attr.label( |
| default = "@bazel_tools//tools/allowlists/function_transition_allowlist", |
| ), |
| }, |
| flag_overrides = { |
| "@pico-sdk//bazel/config:PICO_BTSTACK_CONFIG": "bt_stack_config", |
| "@pico-sdk//bazel/config:PICO_LWIP_CONFIG": "lwip_config", |
| }, |
| ) |
| |
| # This transition sets SDK configuration options required to build test binaries |
| # for the pico_float_test suite of tests. |
| pico_float_test_binary = declare_transtion( |
| attrs = { |
| "pico_printf_impl": attr.string(), |
| "extra_copts": attr.string_list(), |
| # This could be shared, but we don't in order to make it clearer that |
| # a transition is in use. |
| "_allowlist_function_transition": attr.label( |
| default = "@bazel_tools//tools/allowlists/function_transition_allowlist", |
| ), |
| }, |
| flag_overrides = { |
| "@pico-sdk//bazel/config:PICO_DEFAULT_PRINTF_IMPL": "pico_printf_impl", |
| }, |
| append_to_flags = { |
| "//command_line_option:copt": "extra_copts", |
| }, |
| ) |
| |
| # This is a general purpose transition that applies the listed copt flags to |
| # all transitive dependencies. |
| extra_copts_for_all_deps = declare_transtion( |
| attrs = { |
| "extra_copts": attr.string_list(), |
| # This could be shared, but we don't in order to make it clearer that |
| # a transition is in use. |
| "_allowlist_function_transition": attr.label( |
| default = "@bazel_tools//tools/allowlists/function_transition_allowlist", |
| ), |
| }, |
| append_to_flags = { |
| "//command_line_option:copt": "extra_copts", |
| }, |
| ) |