| Project: /_project.yaml |
| Book: /_book.yaml |
| {# disableFinding("Currently") #} |
| {# disableFinding(TODO) #} |
| |
| # Macros |
| |
| {% include "_buttons.html" %} |
| |
| This page covers the basics of using macros and includes typical use cases, |
| debugging, and conventions. |
| |
| A macro is a function called from the `BUILD` file that can instantiate rules. |
| Macros are mainly used for encapsulation and code reuse of existing rules and |
| other macros. |
| |
| Macros come in two flavors: symbolic macros, which are described on this page, |
| and [legacy macros](legacy-macros.md). Where possible, we recommend using |
| symbolic macros for code clarity. |
| |
| Symbolic macros offer typed arguments (string to label conversion, relative to |
| where the macro was called) and the ability to restrict and specify the |
| visibility of targets created. They are designed to be amenable to lazy |
| evaluation (which will be added in a future Bazel release). Symbolic macros are |
| available by default in Bazel 8. Where this document mentions `macros`, it's |
| referring to **symbolic macros**. |
| |
| An executable example of symbolic macros can be found in the |
| [examples repository](https://github.com/bazelbuild/examples/tree/main/macros). |
| |
| ## Usage {:#usage} |
| |
| Macros are defined in `.bzl` files by calling the |
| [`macro()`](https://bazel.build/rules/lib/globals/bzl.html#macro) function with |
| two required parameters: `attrs` and `implementation`. |
| |
| ### Attributes {:#attributes} |
| |
| `attrs` accepts a dictionary of attribute name to [attribute |
| types](https://bazel.build/rules/lib/toplevel/attr#members), which represents |
| the arguments to the macro. Two common attributes – `name` and `visibility` – |
| are implicitly added to all macros and are not included in the dictionary passed |
| to `attrs`. |
| |
| ```starlark |
| # macro/macro.bzl |
| my_macro = macro( |
| attrs = { |
| "deps": attr.label_list(mandatory = True, doc = "The dependencies passed to the inner cc_binary and cc_test targets"), |
| "create_test": attr.bool(default = False, configurable = False, doc = "If true, creates a test target"), |
| }, |
| implementation = _my_macro_impl, |
| ) |
| ``` |
| |
| Attribute type declarations accept the |
| [parameters](https://bazel.build/rules/lib/toplevel/attr#parameters), |
| `mandatory`, `default`, and `doc`. Most attribute types also accept the |
| `configurable` parameter, which determines whether the attribute accepts |
| `select`s. If an attribute is `configurable`, it will parse non-`select` values |
| as an unconfigurable `select` – `"foo"` will become |
| `select({"//conditions:default": "foo"})`. Learn more in [selects](#selects). |
| |
| #### Attribute inheritance {:#attribute-inheritance} |
| |
| Macros are often intended to wrap a rule (or another macro), and the macro's |
| author often wants to forward the bulk of the wrapped symbol's attributes |
| unchanged, using `**kwargs`, to the macro's main target (or main inner macro). |
| |
| To support this pattern, a macro can *inherit attributes* from a rule or another |
| macro by passing the [rule](https://bazel.build/rules/lib/builtins/rule) or |
| [macro symbol](https://bazel.build/rules/lib/builtins/macro) to `macro()`'s |
| `inherit_attrs` argument. (You can also use the special string `"common"` |
| instead of a rule or macro symbol to inherit the [common attributes defined for |
| all Starlark build |
| rules](https://bazel.build/reference/be/common-definitions#common-attributes).) |
| Only public attributes get inherited, and the attributes in the macro's own |
| `attrs` dictionary override inherited attributes with the same name. You can |
| also *remove* inherited attributes by using `None` as a value in the `attrs` |
| dictionary: |
| |
| ```starlark |
| # macro/macro.bzl |
| my_macro = macro( |
| inherit_attrs = native.cc_library, |
| attrs = { |
| # override native.cc_library's `local_defines` attribute |
| "local_defines": attr.string_list(default = ["FOO"]), |
| # do not inherit native.cc_library's `defines` attribute |
| "defines": None, |
| }, |
| ... |
| ) |
| ``` |
| |
| The default value of non-mandatory inherited attributes is always overridden to |
| be `None`, regardless of the original attribute definition's default value. If |
| you need to examine or modify an inherited non-mandatory attribute – for |
| example, if you want to add a tag to an inherited `tags` attribute – you must |
| make sure to handle the `None` case in your macro's implementation function: |
| |
| ```starlark |
| # macro/macro.bzl |
| def _my_macro_impl(name, visibility, tags, **kwargs): |
| # Append a tag; tags attr is an inherited non-mandatory attribute, and |
| # therefore is None unless explicitly set by the caller of our macro. |
| my_tags = (tags or []) + ["another_tag"] |
| native.cc_library( |
| ... |
| tags = my_tags, |
| **kwargs, |
| ) |
| ... |
| ``` |
| |
| ### Implementation {:#implementation} |
| |
| `implementation` accepts a function which contains the logic of the macro. |
| Implementation functions often create targets by calling one or more rules, and |
| they are usually private (named with a leading underscore). Conventionally, |
| they are named the same as their macro, but prefixed with `_` and suffixed with |
| `_impl`. |
| |
| Unlike rule implementation functions, which take a single argument (`ctx`) that |
| contains a reference to the attributes, macro implementation functions accept a |
| parameter for each argument. |
| |
| ```starlark |
| # macro/macro.bzl |
| def _my_macro_impl(name, visibility, deps, create_test): |
| cc_library( |
| name = name + "_cc_lib", |
| deps = deps, |
| ) |
| |
| if create_test: |
| cc_test( |
| name = name + "_test", |
| srcs = ["my_test.cc"], |
| deps = deps, |
| ) |
| ``` |
| |
| If a macro inherits attributes, its implementation function *must* have a |
| `**kwargs` residual keyword parameter, which can be forwarded to the call that |
| invokes the inherited rule or submacro. (This helps ensure that your macro won't |
| be broken if the rule or macro which from which you are inheriting adds a new |
| attribute.) |
| |
| ### Declaration {:#declaration} |
| |
| Macros are declared by loading and calling their definition in a `BUILD` file. |
| |
| ```starlark |
| |
| # pkg/BUILD |
| |
| my_macro( |
| name = "macro_instance", |
| deps = ["src.cc"] + select( |
| { |
| "//config_setting:special": ["special_source.cc"], |
| "//conditions:default": [], |
| }, |
| ), |
| create_tests = True, |
| ) |
| ``` |
| |
| This would create targets |
| `//pkg:macro_instance_cc_lib` and`//pkg:macro_instance_test`. |
| |
| Just like in rule calls, if an attribute value in a macro call is set to `None`, |
| that attribute is treated as if it was omitted by the macro's caller. For |
| example, the following two macro calls are equivalent: |
| |
| ```starlark |
| # pkg/BUILD |
| my_macro(name = "abc", srcs = ["src.cc"], deps = None) |
| my_macro(name = "abc", srcs = ["src.cc"]) |
| ``` |
| |
| This is generally not useful in `BUILD` files, but is helpful when |
| programmatically wrapping a macro inside another macro. |
| |
| ## Details {:#usage-details} |
| |
| ### Naming conventions for targets created {:#naming} |
| |
| The names of any targets or submacros created by a symbolic macro must |
| either match the macro's `name` parameter or must be prefixed by `name` followed |
| by `_` (preferred), `.` or `-`. For example, `my_macro(name = "foo")` may only |
| create files or targets named `foo`, or prefixed by `foo_`, `foo-` or `foo.`, |
| for example, `foo_bar`. |
| |
| Targets or files that violate macro naming convention can be declared, but |
| cannot be built and cannot be used as dependencies. |
| |
| Non-macro files and targets within the same package as a macro instance should |
| *not* have names that conflict with potential macro target names, though this |
| exclusivity is not enforced. We are in the progress of implementing |
| [lazy evaluation](#laziness) as a performance improvement for Symbolic macros, |
| which will be impaired in packages that violate the naming schema. |
| |
| ### Restrictions {:#restrictions} |
| |
| Symbolic macros have some additional restrictions compared to legacy macros. |
| |
| Symbolic macros |
| |
| * must take a `name` argument and a `visibility` argument |
| * must have an `implementation` function |
| * may not return values |
| * may not mutate their arguments |
| * may not call `native.existing_rules()` unless they are special `finalizer` |
| macros |
| * may not call `native.package()` |
| * may not call `glob()` |
| * may not call `native.environment_group()` |
| * must create targets whose names adhere to the [naming schema](#naming) |
| * can't refer to input files that weren't declared or passed in as an argument |
| * can't refer to private targets of their callers (see |
| [visibility and macros](#visibility) for more details). |
| |
| ### Visibility and macros {:#visibility} |
| |
| The [visibility](/concepts/visibility) system helps protect the implementation |
| details of both (symbolic) macros and their callers. |
| |
| By default, targets created in a symbolic macro are visible within the macro |
| itself, but not necessarily to the macro's caller. The macro can "export" a |
| target as a public API by forwarding the value of its own `visibility` |
| attribute, as in `some_rule(..., visibility = visibility)`. |
| |
| The key ideas of macro visibility are: |
| |
| 1. Visibility is checked based on what macro declared the target, not what |
| package called the macro. |
| |
| * In other words, being in the same package does not by itself make one |
| target visible to another. This protects the macro's internal targets |
| from becoming dependencies of other macros or top-level targets in the |
| package. |
| |
| 1. All `visibility` attributes, on both rules and macros, automatically |
| include the place where the rule or macro was called. |
| |
| * Thus, a target is unconditionally visible to other targets declared in the |
| same macro (or the `BUILD` file, if not in a macro). |
| |
| In practice, this means that when a macro declares a target without setting its |
| `visibility`, the target defaults to being internal to the macro. (The package's |
| [default visibility](/reference/be/functions#package.default_visibility) does |
| not apply within a macro.) Exporting the target means that the target is visible |
| to whatever the macro's caller specified in the macro's `visibility` attribute, |
| plus the package of the macro's caller itself, as well as the macro's own code. |
| Another way of thinking of it is that the visibility of a macro determines who |
| (aside from the macro itself) can see the macro's exported targets. |
| |
| ```starlark |
| # tool/BUILD |
| ... |
| some_rule( |
| name = "some_tool", |
| visibility = ["//macro:__pkg__"], |
| ) |
| ``` |
| |
| ```starlark |
| # macro/macro.bzl |
| |
| def _impl(name, visibility): |
| cc_library( |
| name = name + "_helper", |
| ... |
| # No visibility passed in. Same as passing `visibility = None` or |
| # `visibility = ["//visibility:private"]`. Visible to the //macro |
| # package only. |
| ) |
| cc_binary( |
| name = name + "_exported", |
| deps = [ |
| # Allowed because we're also in //macro. (Targets in any other |
| # instance of this macro, or any other macro in //macro, can see it |
| # too.) |
| name + "_helper", |
| # Allowed by some_tool's visibility, regardless of what BUILD file |
| # we're called from. |
| "//tool:some_tool", |
| ], |
| ... |
| visibility = visibility, |
| ) |
| |
| my_macro = macro(implementation = _impl, ...) |
| ``` |
| |
| ```starlark |
| # pkg/BUILD |
| load("//macro:macro.bzl", "my_macro") |
| ... |
| |
| my_macro( |
| name = "foo", |
| ... |
| ) |
| |
| some_rule( |
| ... |
| deps = [ |
| # Allowed, its visibility is ["//pkg:__pkg__", "//macro:__pkg__"]. |
| ":foo_exported", |
| # Disallowed, its visibility is ["//macro:__pkg__"] and |
| # we are not in //macro. |
| ":foo_helper", |
| ] |
| ) |
| ``` |
| |
| If `my_macro` were called with `visibility = ["//other_pkg:__pkg__"]`, or if |
| the `//pkg` package had set its `default_visibility` to that value, then |
| `//pkg:foo_exported` could also be used within `//other_pkg/BUILD` or within a |
| macro defined in `//other_pkg:defs.bzl`, but `//pkg:foo_helper` would remain |
| protected. |
| |
| A macro can declare that a target is visible to a friend package by passing |
| `visibility = ["//some_friend:__pkg__"]` (for an internal target) or |
| `visibility = visibility + ["//some_friend:__pkg__"]` (for an exported one). |
| Note that it is an antipattern for a macro to declare a target with public |
| visibility (`visibility = ["//visibility:public"]`). This is because it makes |
| the target unconditionally visible to every package, even if the caller |
| specified a more restricted visibility. |
| |
| All visibility checking is done with respect to the innermost currently running |
| symbolic macro. However, there is a visibility delegation mechanism: If a macro |
| passes a label as an attribute value to an inner macro, any usages of the label |
| in the inner macro are checked with respect to the outer macro. See the |
| [visibility page](/concepts/visibility#symbolic-macros) for more details. |
| |
| Remember that legacy macros are entirely transparent to the visibility system, |
| and behave as though their location is whatever BUILD file or symbolic macro |
| they were called from. |
| |
| #### Finalizers and visibility {:#finalizers-and-visibility} |
| |
| Targets declared in a rule finalizer, in addition to seeing targets following |
| the usual symbolic macro visibility rules, can *also* see all targets which are |
| visible to the finalizer target's package. |
| |
| This means that if you migrate a `native.existing_rules()`-based legacy macro to |
| a finalizer, the targets declared by the finalizer will still be able to see |
| their old dependencies. |
| |
| However, note that it's possible to declare a target in a symbolic macro such |
| that a finalizer's targets cannot see it under the visibility system – even |
| though the finalizer can *introspect* its attributes using |
| `native.existing_rules()`. |
| |
| ### Selects {:#selects} |
| |
| If an attribute is `configurable` (the default) and its value is not `None`, |
| then the macro implementation function will see the attribute value as wrapped |
| in a trivial `select`. This makes it easier for the macro author to catch bugs |
| where they did not anticipate that the attribute value could be a `select`. |
| |
| For example, consider the following macro: |
| |
| ```starlark |
| my_macro = macro( |
| attrs = {"deps": attr.label_list()}, # configurable unless specified otherwise |
| implementation = _my_macro_impl, |
| ) |
| ``` |
| |
| If `my_macro` is invoked with `deps = ["//a"]`, that will cause `_my_macro_impl` |
| to be invoked with its `deps` parameter set to `select({"//conditions:default": |
| ["//a"]})`. If this causes the implementation function to fail (say, because the |
| code tried to index into the value as in `deps[0]`, which is not allowed for |
| `select`s), the macro author can then make a choice: either they can rewrite |
| their macro to only use operations compatible with `select`, or they can mark |
| the attribute as nonconfigurable (`attr.label_list(configurable = False)`). The |
| latter ensures that users are not permitted to pass a `select` value in. |
| |
| Rule targets reverse this transformation, and store trivial `select`s as their |
| unconditional values; in the above example, if `_my_macro_impl` declares a rule |
| target `my_rule(..., deps = deps)`, that rule target's `deps` will be stored as |
| `["//a"]`. This ensures that `select`-wrapping does not cause trivial `select` |
| values to be stored in all targets instantiated by macros. |
| |
| If the value of a configurable attribute is `None`, it does not get wrapped in a |
| `select`. This ensures that tests like `my_attr == None` still work, and that |
| when the attribute is forwarded to a rule with a computed default, the rule |
| behaves properly (that is, as if the attribute were not passed in at all). It is |
| not always possible for an attribute to take on a `None` value, but it can |
| happen for the `attr.label()` type, and for any inherited non-mandatory |
| attribute. |
| |
| ## Finalizers {:#finalizers} |
| |
| A rule finalizer is a special symbolic macro which – regardless of its lexical |
| position in a BUILD file – is evaluated in the final stage of loading a package, |
| after all non-finalizer targets have been defined. Unlike ordinary symbolic |
| macros, a finalizer can call `native.existing_rules()`, where it behaves |
| slightly differently than in legacy macros: it only returns the set of |
| non-finalizer rule targets. The finalizer may assert on the state of that set or |
| define new targets. |
| |
| To declare a finalizer, call `macro()` with `finalizer = True`: |
| |
| ```starlark |
| def _my_finalizer_impl(name, visibility, tags_filter): |
| for r in native.existing_rules().values(): |
| for tag in r.get("tags", []): |
| if tag in tags_filter: |
| my_test( |
| name = name + "_" + r["name"] + "_finalizer_test", |
| deps = [r["name"]], |
| data = r["srcs"], |
| ... |
| ) |
| continue |
| |
| my_finalizer = macro( |
| attrs = {"tags_filter": attr.string_list(configurable = False)}, |
| implementation = _impl, |
| finalizer = True, |
| ) |
| ``` |
| |
| ## Laziness {:#laziness} |
| |
| IMPORTANT: We are in the process of implementing lazy macro expansion and |
| evaluation. This feature is not available yet. |
| |
| Currently, all macros are evaluated as soon as the BUILD file is loaded, which |
| can negatively impact performance for targets in packages that also have costly |
| unrelated macros. In the future, non-finalizer symbolic macros will only be |
| evaluated if they're required for the build. The prefix naming schema helps |
| Bazel determine which macro to expand given a requested target. |
| |
| ## Migration troubleshooting {:#troubleshooting} |
| |
| Here are some common migration headaches and how to fix them. |
| |
| * Legacy macro calls `glob()` |
| |
| Move the `glob()` call to your BUILD file (or to a legacy macro called from the |
| BUILD file), and pass the `glob()` value to the symbolic macro using a |
| label-list attribute: |
| |
| ```starlark |
| # BUILD file |
| my_macro( |
| ..., |
| deps = glob(...), |
| ) |
| ``` |
| |
| * Legacy macro has a parameter that isn't a valid starlark `attr` type. |
| |
| Pull as much logic as possible into a nested symbolic macro, but keep the |
| top level macro a legacy macro. |
| |
| * Legacy macro calls a rule that creates a target that breaks the naming schema |
| |
| That's okay, just don't depend on the "offending" target. The naming check will |
| be quietly ignored. |
| |