aboutsummaryrefslogtreecommitdiffhomepage
path: root/site/docs/skylark/macros.md
blob: 9d32c77beeab69dd29351a58fa67de255080bcc9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
# Macros

## Macro creation

A macro is a function called from the BUILD file. It can instantiate native
or Skylark rules. Macros don't give additional power, they are just used for
encapsulation and code reuse. By the end of the loading phase, macros don't
exist anymore, and Bazel sees only the set of rules they created.

Native rules can be instantiated from the `native` module, e.g.

```python
def my_macro(name, visibility=None):
  native.cc_library(
    name = name,
    srcs = ["main.cc"],
    visibility = visibility,
  )
```

If you need to know the package name (i.e. which BUILD file is calling the
macro), use the constant [PACKAGE_NAME](lib/globals.html#PACKAGE_NAME).

## Examples

* [Macro creating native rules](cookbook.md#macro_native).

* [Macro creating Skylark rules](cookbook.md#macro_skylark).

## Debugging

* `bazel query --output=build //my/path:all` will show you how the BUILD
file looks like after evaluation. All macros, globs, loops are expanded.

* You can also use [print](lib/globals.html#print) for debugging. It displays
the message as a warning during the loading phase. Except in rare cases, remove
your `print` calls before submitting the code to the depot.

## Errors

If you want to throw an error, use the [fail](lib/globals.html#fail) function.
Explain clearly to the user what went wrong and how to fix their BUILD file. It
is not possible to catch an error.

```
def my_macro(name, deps, visibility=None):
  if len(deps) < 2:
    fail("Expected at least two values in deps")
  # ...
```

## Conventions

* All public functions (functions that don't start with underscore) that
instantiate rules must have a `name` argument. This argument should not be
optional (don't give a default value).

* Public functions should use a docstring following [Python
  conventions](https://google-styleguide.googlecode.com/svn/trunk/pyguide.html?showone=Comments#Comments).

* In BUILD files, the `name` argument of the macros must a be a keyword
  argument (not a positional argument).

* The `name` attribute of rules generated by a macro should include the name
  argument as a prefix. For example, `macro(name = "foo")` can generate a
  cc_library `foo` and a genrule `foo_gen`.

* Macros should have an optional `visibility` argument.

## Full example

The typical use-case for a macro is when you want to reuse a genrule, e.g.

```
genrule(
    name = "file",
    outs = ["file.txt"],
    cmd = "$(location generator) some_arg > $@",
    tools = [":generator"],
)
```

If you want to generate another file with different arguments, you may want to
extract this code to a function.

The BUILD file will become simply:

```
load("/path/generator", "file_generator")

file_generator(
    name = "file",
    arg = "some_arg",
)
```

In order to keep BUILD files clean and declarative, you must put the function in
a separate `.bzl` file. For example, write the definition of the macro in
`path/generator.bzl`:

```
def my_macro(name, arg, visibility=None):
  native.genrule(
    name = name,
    outs = [name + ".txt"],
    cmd = "$(location generator) %s > $@" % arg,
    tools = ["//test:generator"],
    visibility = visibility,
  )
```

When you want to investigate what a macro does, use the following command to
see the expanded form:

```
$ bazel query --output=build :file
# /absolute/path/test/ext.bzl:42:3
genrule(
  name = "file",
  tools = ["//test:generator"],
  outs = ["//test:file.txt"],
  cmd = "$(location generator) some_arg > $@",
)
```