Skip to main content
Version: 2.3 (deprecated)

Macros

Reducing boilerplate in BUILD files.


When to use a macro

Macros are useful to reduce boilerplate in BUILD files. For example, if you keep using the same value for a field, you can use a macro.

If you instead want to add support for a new language, or do something more complex than a macro allows, create a new target type.

If you are already using a target type, but need to store additional metadata for your plugin, add a new field to the target type.

How to add a macro

Macros are defined in Python files that act like a normal BUILD file. They have access to all the symbols you normally have registered in a BUILD file, such as all of your target types.

Macros cannot import other modules, just like BUILD files cannot have import statements.

To define a new macro, add a function with def and the name of the new symbol. Usually, the last line of the macro will create a new target, like this:

pants-plugins/macros.py
def python2_library(**kwargs):
kwargs["interpreter_constraints"] = ["==2.7.*"]
python_library(**kwargs)

def python3_library(**kwargs):
kwargs["interpreter_constraints"] = [">=3.5"]
python_library(**kwargs)

Then, add this file to the option build_file_prelude_globs in the [GLOBAL] scope:

pants.toml
[GLOBAL]
build_file_prelude_globs = [
"pants-plugins/macros.py",
]

Now, in BUILD files, you can use the new macros:

project/BUILD
python2_library(
name="app_py2",
sources=["app_py2.py"],
)

python3_library(
name="app_py3",
sources=["app_py3.py"],
)

A macro can create multiple targets:

pants-plugins/macros.py
def python23_tests(name, **kwargs):
kwargs.pop("interpreter_constraints", None)

python_tests(
name=f"{name}_py2",
interpreter_constraints=["==2.7.*"],
**kwargs,
)

python_tests(
name=f"{name}_py3",
interpreter_constraints=[">=3.5"],
**kwargs,
)

A macro can perform validation:

pants-plugins/macros.py
def custom_python_library(**kwargs):
if "2.7" in kwargs.get("interpreter_constraints", ""):
raise ValueError("Python 2.7 is banned!")
python_library(**kwargs)

A macro can take new parameters to generate the target dynamically. For example:

def custom_python_library(has_type_hints: bool = True, **kwargs):
if has_type_hints:
kwargs["tags"] = kwargs.get("tags", []) + ["type_checked"]
python_library(**kwargs)