Skip to content

Commit

Permalink
[8.0.0] Update macro tutorial, glossary, and related docs for symboli…
Browse files Browse the repository at this point in the history
…c macros (#24512)

Cherry-pick of

634f4ec

PiperOrigin-RevId: 700733747
Change-Id: Idab23068f679ce5b881c94c05b202b4526d59cca
  • Loading branch information
tetromino authored Nov 27, 2024
1 parent d64d7c3 commit 8d66f0b
Show file tree
Hide file tree
Showing 7 changed files with 252 additions and 53 deletions.
8 changes: 6 additions & 2 deletions site/en/_book.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -274,8 +274,10 @@ upper_tabs:
path: /extending/concepts/
- title: Rules
path: /extending/rules
- title: Macros
- title: Symbolic macros
path: /extending/macros
- title: Legacy macros
path: /extending/legacy-macros
- title: Depsets
path: /extending/depsets
- title: Aspects
Expand All @@ -296,8 +298,10 @@ upper_tabs:
contents:
- title: Creating a rule
path: /rules/rules-tutorial
- title: Creating a macro
- title: Creating a symbolic macro
path: /rules/macro-tutorial
- title: Creating a legacy macro
path: /rules/legacy-macro-tutorial
- title: Creating custom verbs
path: /rules/verbs-tutorial
- title: Starlark language
Expand Down
8 changes: 6 additions & 2 deletions site/en/extending/_index.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,10 @@ landing_page:
path: /extending/concepts/
- heading: Rules
path: /extending/rules
- heading: Macros
- heading: Symbolic macros
path: /extending/macros
- heading: Legacy macros
path: /extending/legacy-macros
- heading: Depsets
path: /extending/depsets
- heading: Aspects
Expand All @@ -75,8 +77,10 @@ landing_page:
items:
- heading: Creating a rule
path: /rules/rules-tutorial
- heading: Creating a macro
- heading: Creating a symbolic macro
path: /rules/macro-tutorial
- heading: Creating a legacy macro
path: /rules/legacy-macro-tutorial
- heading: Creating custom verbs
path: /rules/verbs-tutorial
- heading: Starlark language
Expand Down
41 changes: 24 additions & 17 deletions site/en/extending/concepts.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,24 +21,30 @@ Before learning the more advanced concepts, first:
* Learn how you can [share variables](/build/share-variables)
between two `BUILD` files.

## Macros and rules

A [macro](/extending/macros) is a function that instantiates rules. It is useful when a
`BUILD` file is getting too repetitive or too complex, as it allows you to reuse
some code. The function is evaluated as soon as the `BUILD` file is read. After
the evaluation of the `BUILD` file, Bazel has little information about macros:
if your macro generates a `genrule`, Bazel will behave as if you wrote the
`genrule`. As a result, `bazel query` will only list the generated `genrule`.
## Macros and rules {:#macros-and-rules}

A macro is a function that instantiates rules. Macros come in two flavors:
[symbolic macros](/extending/macros) (new in Bazel 8) and [legacy
macros](/extending/legacy-macros). The two flavors of macros are defined
differently, but behave almost the same from the point of view of a user. A
macro is useful when a `BUILD` file is getting too repetitive or too complex, as
it lets you reuse some code. The function is evaluated as soon as the `BUILD`
file is read. After the evaluation of the `BUILD` file, Bazel has little
information about macros. If your macro generates a `genrule`, Bazel will
behave *almost* as if you declared that `genrule` in the `BUILD` file. (The one
exception is that targets declared in a symbolic macro have [special visibility
semantics](/extending/macros#visibility): a symbolic macro can hide its internal
targets from the rest of the package.)

A [rule](/extending/rules) is more powerful than a macro. It can access Bazel
internals and have full control over what is going on. It may for example pass
information to other rules.

If you want to reuse simple logic, start with a macro. If a macro becomes
If you want to reuse simple logic, start with a macro; we recommend a symbolic
macro, unless you need to support older Bazel versions. If a macro becomes
complex, it is often a good idea to make it a rule. Support for a new language
is typically done with a rule. Rules are for advanced users, and most
users will never have to write one; they will only load and call existing
rules.
is typically done with a rule. Rules are for advanced users, and most users will
never have to write one; they will only load and call existing rules.

## Evaluation model {:#evaluation-model}

Expand Down Expand Up @@ -73,9 +79,9 @@ they will not be executed.

## Creating extensions

* [Create your first macro](/rules/macro-tutorial) in order to
reuse some code. Then [learn more about macros](/extending/macros) and
[using them to create "custom verbs"](/rules/verbs-tutorial).
* [Create your first macro](/rules/macro-tutorial) in order to reuse some code.
Then [learn more about macros](/extending/macros) and [using them to create
"custom verbs"](/rules/verbs-tutorial).

* [Follow the rules tutorial](/rules/rules-tutorial) to get started with rules.
Next, you can read more about the [rules concepts](/extending/rules).
Expand All @@ -89,8 +95,9 @@ them within reach:

## Going further

In addition to [macros](/extending/macros) and [rules](/extending/rules), you may want to write
[aspects](/extending/aspects) and [repository rules](/extending/repo).
In addition to [macros](/extending/macros) and [rules](/extending/rules), you
may want to write [aspects](/extending/aspects) and [repository
rules](/extending/repo).

* Use [Buildifier](https://github.com/bazelbuild/buildtools){: .external}
consistently to format and lint your code.
Expand Down
30 changes: 28 additions & 2 deletions site/en/reference/glossary.md
Original file line number Diff line number Diff line change
Expand Up @@ -329,14 +329,30 @@ create [packages](#package). [Macros](#macro) and certain functions like
build, the [analysis phase](#analysis-phase), to build up a [target
graph](#target-graph).

### Legacy macro {:#legacy-macro}

A flavor of [macro](#macro) which is declared as an ordinary
[Starlark](#starlark) function, and which runs as a side effect of executing a
`BUILD` file.

Legacy macros can do anything a function can. This means they can be convenient,
but they can also be harder to read, write, and use. A legacy macro might
unexpectedly mutate its arguments or fail when given a `select()` or ill-typed
argument.

Contrast with [symbolic macros](#symbolic-macro).

**See also:** [Legacy macro documentation](/extending/legacy-macros)

### Macro {:#macro}

A mechanism to compose multiple [rule](#rule) target declarations together under
a single [Starlark](#starlark) function. Enables reusing common rule declaration
a single [Starlark](#starlark) callable. Enables reusing common rule declaration
patterns across `BUILD` files. Expanded to the underlying rule target
declarations during the [loading phase](#loading-phase).

**See also:** [Macro documentation](/extending/macros)
Comes in two flavors: [symbolic macros](#symbolic-macro) (since Bazel 8) and
[legacy macros](#legacy-macro).

### Mnemonic {:#mnemonic}

Expand Down Expand Up @@ -587,6 +603,16 @@ for example, bazel `--host_jvm_debug` build. These flags modify the
startup flags causes a server restart. Startup flags are not specific to any
command.

### Symbolic macro {:#symbolic-macro}

A flavor of [macro](#macro) which is declared with a [rule](#rule)-like
[attribute](#attribute) schema, allows hiding internal declared
[targets](#target) from their own package, and enforces a predictable naming
pattern on the targets that the macro declares. Designed to avoid some of the
problems seen in large [legacy macro](#legacy-macro) codebases.

**See also:** [Symbolic macro documentation](/extending/macros)

### Target {:#target}

An object that is defined in a [`BUILD` file](#build-file) and identified by a
Expand Down
99 changes: 99 additions & 0 deletions site/en/rules/legacy-macro-tutorial.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
Project: /_project.yaml
Book: /_book.yaml

# Creating a Legacy Macro

{% include "_buttons.html" %}

IMPORTANT: This tutorial is for [*legacy macros*](/extending/legacy-macros). If
you only need to support Bazel 8 or newer, we recommend using [symbolic
macros](/extending/macros) instead; take a look at [Creating a Symbolic
Macro](../macro-tutorial).

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](/extending/rules). Rules give you more control and flexibility.

The easiest way to create a macro that resizes an image is to use a `genrule`:

```starlark
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`:

```starlark
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](https://www.python.org/dev/peps/pep-0257/) like in Python.

* To call a `genrule`, or any other native rule, prefix with `native.`.

* Use `**kwargs` to forward the extra arguments to the underlying `genrule`
(it works just like in
[Python](https://docs.python.org/3/tutorial/controlflow.html#keyword-arguments)).
This is useful, so that a user can use standard attributes like
`visibility`, or `tags`.

Now, use the macro from the `BUILD` file:

```starlark
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](/extending/macros), which have
built\-in checks on attribute types.
91 changes: 64 additions & 27 deletions site/en/rules/macro-tutorial.md
Original file line number Diff line number Diff line change
@@ -1,21 +1,26 @@
Project: /_project.yaml
Book: /_book.yaml

# Creating a Macro
# Creating a Symbolic Macro

{% include "_buttons.html" %}

IMPORTANT: This tutorial is for [*symbolic macros*](/extending/macros) – the new
macro system introduced in Bazel 8. If you need to support older Bazel versions,
you will want to write a [legacy macro](/extending/legacy-macros) instead; take
a look at [Creating a Legacy Macro](../legacy-macro-tutorial).

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 macro that resizes an image.
tutorial, you are going to create a symbolic 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](/extending/rules). Rules give you more control and flexibility.

The easiest way to create a macro that resizes an image is to use a `genrule`:

``` python
```starlark
genrule(
name = "logo_miniature",
srcs = ["logo.png"],
Expand All @@ -31,40 +36,72 @@ cc_binary(
```

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`:

``` python
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],
outs = ["small_" + src],
cmd = "convert $< -resize " + size + " $@",
**kwargs
)
define an *implementation function* and a *macro declaration* in a separate
`.bzl` file, and call the file `miniature.bzl`:

```starlark
# Implementation function
def _miniature_impl(name, visibility, src, size, **kwargs):
native.genrule(
name = name,
visibility = visibility,
srcs = [src],
outs = [name + "_small_" + src.name],
cmd = "convert $< -resize " + size + " $@",
**kwargs,
)

# Macro declaration
miniature = macro(
doc = """Create a miniature of the src image.
The generated file name will be prefixed with `name + "_small_"`.
""",
implementation = _miniature_impl,
# Inherit most of genrule's attributes (such as tags and testonly)
inherit_attrs = native.genrule,
attrs = {
"src": attr.label(
doc = "Image file",
allow_single_file = True,
# Non-configurable because our genrule's output filename is
# suffixed with src's name. (We want to suffix the output file with
# srcs's name because some tools that operate on image files expect
# the files to have the right file extension.)
configurable = False,
),
"size": attr.string(
doc = "Output size in WxH format",
default = "100x100",
),
# Do not allow callers of miniature() to set srcs, cmd, or outs -
# _miniature_impl overrides their values when calling native.genrule()
"srcs": None,
"cmd": None,
"outs": None,
},
)
```

A few remarks:

* By convention, macros have a `name` argument, just like rules.
* Symbolic macro implementation functions must have `name` and `visibility`
parameters. They should used for the macro's main target.

* To document the behavior of a macro, use
[docstring](https://www.python.org/dev/peps/pep-0257/) like in Python.
* To document the behavior of a symbolic macro, use `doc` parameters for
`macro()` and its attributes.

* To call a `genrule`, or any other native rule, use `native.`.
* To call a `genrule`, or any other native rule, use `native.`.

* Use `**kwargs` to forward the extra arguments to the underlying `genrule`
(it works just like in [Python](https://docs.python.org/3/tutorial/controlflow.html#keyword-arguments)).
This is useful, so that a user can use standard attributes like `visibility`,
or `tags`.
* Use `**kwargs` to forward the extra inherited arguments to the underlying
`genrule` (it works just like in
[Python](https://docs.python.org/3/tutorial/controlflow.html#keyword-arguments)).
This is useful so that a user can set standard attributes like `tags` or
`testonly`.

Now, use the macro from the `BUILD` file:

``` python
```starlark
load("//path/to:miniature.bzl", "miniature")

miniature(
Expand Down
Loading

0 comments on commit 8d66f0b

Please sign in to comment.