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:
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:
[GLOBAL]
build_file_prelude_globs = [
"pants-plugins/macros.py",
]
Now, in BUILD files, you can use the new macros:
python2_library(
name="app_py2",
sources=["app_py2.py"],
)
python3_library(
name="app_py3",
sources=["app_py3.py"],
)
A macro can create multiple targets:
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:
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:
- pants-plugins/macros.py
- project/BUILD
def custom_python_library(has_type_hints: bool = True, **kwargs):
if has_type_hints:
kwargs["tags"] = kwargs.get("tags", []) + ["type_checked"]
python_library(**kwargs)
custom_python_library(
has_type_hints=False,
)