| --- | |
| title: 'Macros' | |
| --- | |
| 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/versions/9.3.0/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/versions/9.3.0/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/versions/9.3.0/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/versions/9.3.0/rules/lib/builtins/rule) or | |
| [macro symbol](https://bazel.build/versions/9.3.0/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/versions/9.3.0/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](/versions/9.3.0/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](/versions/9.3.0/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](/versions/9.3.0/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. | |