IMPORTANT: This tutorial is for legacy macros. If you only need to support Bazel 8 or newer, we recommend using symbolic macros instead; take a look at Creating a Symbolic Macro.
Imagine that you need to run a tool as part of your build. For example, you may want to generate or preprocess a source file, or compress a binary. In this tutorial, you are going to create a legacy macro that resizes an image.
Macros are suitable for simple tasks. If you want to do anything more complicated, for example add support for a new programming language, consider creating a rule. Rules give you more control and flexibility.
The easiest way to create a macro that resizes an image is to use a genrule
:
genrule(
name = "logo_miniature",
srcs = ["logo.png"],
outs = ["small_logo.png"],
cmd = "convert $< -resize 100x100 $@",
)
cc_binary(
name = "my_app",
srcs = ["my_app.cc"],
data = [":logo_miniature"],
)
If you need to resize more images, you may want to reuse the code. To do that,
define a function in a separate .bzl
file, and call the file miniature.bzl
:
def miniature(name, src, size = "100x100", **kwargs):
"""Create a miniature of the src image.
The generated file is prefixed with 'small_'.
"""
native.genrule(
name = name,
srcs = [src],
# Note that the line below will fail if `src` is not a filename string
outs = ["small_" + src],
cmd = "convert $< -resize " + size + " $@",
**kwargs
)
A few remarks:
By convention, legacy macros have a
name
argument, just like rules.To document the behavior of a legacy macro, use docstring like in Python.
To call a
genrule
, or any other native rule, prefix withnative.
.Use
**kwargs
to forward the extra arguments to the underlyinggenrule
(it works just like in Python). This is useful, so that a user can use standard attributes likevisibility
, ortags
.
Now, use the macro from the BUILD
file:
load("//path/to:miniature.bzl", "miniature")
miniature(
name = "logo_miniature",
src = "image.png",
)
cc_binary(
name = "my_app",
srcs = ["my_app.cc"],
data = [":logo_miniature"],
)
And finally, a warning note: the macro assumes that src
is a filename
string (otherwise, outs = ["small_" + src]
will fail). So src = "image.png"
works; but what happens if the BUILD
file instead used src =
"//other/package:image.png"
, or even src = select(...)
?
You should make sure to declare such assumptions in your macro's documentation. Unfortunately, legacy macros, especially large ones, tend to be fragile because it can be hard to notice and document all such assumptions in your code – and, of course, some users of the macro won't read the documentation. We recommend, if possible, instead using symbolic macros, which have built-in checks on attribute types.