docs/rules/bzl-style.mdx
This page covers basic style guidelines for Starlark and also includes information on macros and rules.
Starlark is a language that defines how software is built, and as such it is both a programming and a configuration language.
You will use Starlark to write BUILD files, macros, and build rules. Macros and
rules are essentially meta-languages - they define how BUILD files are written.
BUILD files are intended to be simple and repetitive.
All software is read more often than it is written. This is especially true for
Starlark, as engineers read BUILD files to understand dependencies of their
targets and details of their builds. This reading will often happen in passing,
in a hurry, or in parallel to accomplishing some other task. Consequently,
simplicity and readability are very important so that users can parse and
comprehend BUILD files quickly.
When a user opens a BUILD file, they quickly want to know the list of targets in
the file; or review the list of sources of that C++ library; or remove a
dependency from that Java binary. Each time you add a layer of abstraction, you
make it harder for a user to do these tasks.
BUILD files are also analyzed and updated by many different tools. Tools may not
be able to edit your BUILD file if it uses abstractions. Keeping your BUILD
files simple will allow you to get better tooling. As a code base grows, it
becomes more and more frequent to do changes across many BUILD files in order to
update a library or do a cleanup.
Important: Do not create a variable or macro just to avoid some amount of
repetition in BUILD files. Your BUILD file should be easily readable both by
developers and tools. The
DRY{: .external} principle doesn't
really apply here.
When in doubt, follow the PEP 8 style guide where possible. In particular, use four rather than two spaces for indentation to follow the Python convention.
Since
Starlark is not Python,
some aspects of Python style do not apply. For example, PEP 8 advises that
comparisons to singletons be done with is, which is not an operator in
Starlark.
Document files and functions using docstrings{: .external}.
Use a docstring at the top of each .bzl file, and a docstring for each public
function.
Rules and aspects, along with their attributes, as well as providers and their
fields, should be documented using the doc argument.
[a-z][a-z0-9_]*), such as cc_library.As in BUILD files, there is no strict line length limit as labels can be long.
When possible, try to use at most 79 characters per line (following Python's
style guide, PEP 8). This guideline
should not be enforced strictly: editors should display more than 80 columns,
automated changes will frequently introduce longer lines, and humans shouldn't
spend time splitting lines that are already readable.
In keyword arguments, spaces around the equal sign are preferred:
def fct(name, srcs):
filtered_srcs = my_filter(source = srcs)
native.cc_library(
name = name,
srcs = filtered_srcs,
testonly = True,
)
Prefer values True and False (rather than of 1 and 0) for boolean values
(such as when using a boolean attribute in a rule).
Do not use the print() function in production code; it is only intended for
debugging, and will spam all direct and indirect users of your .bzl file. The
only exception is that you may submit code that uses print() if it is disabled
by default and can only be enabled by editing the source -- for example, if all
uses of print() are guarded by if DEBUG: where DEBUG is hardcoded to
False. Be mindful of whether these statements are useful enough to justify
their impact on readability.
A macro is a function which instantiates one or more rules during the loading phase. In general, use rules whenever possible instead of macros. The build graph seen by the user is not the same as the one used by Bazel during the build - macros are expanded before Bazel does any build graph analysis.
Because of this, when something goes wrong, the user will need to understand
your macro's implementation to troubleshoot build problems. Additionally, bazel query results can be hard to interpret because targets shown in the results
come from macro expansion. Finally, aspects are not aware of macros, so tooling
depending on aspects (IDEs and others) might fail.
A safe use for macros is for defining additional targets intended to be referenced directly at the Bazel CLI or in BUILD files: In that case, only the end users of those targets need to know about them, and any build problems introduced by macros are never far from their usage.
For macros that define generated targets (implementation details of the macro which are not supposed to be referred to at the CLI or depended on by targets not instantiated by that macro), follow these best practices:
name argument and define a target with that name.
That target becomes that macro's main target.<name> or _<name>. For example, using
name = '%s_bar' % (name).//visibility:private), andmanual tag to avoid expansion in wildcard targets (:all,
..., :*, etc).name should only be used to derive names of targets defined by the
macro, and not for anything else. For example, don't use the name to derive
a dependency or input file that is not generated by the macro itself.name should be the first argument when defining a macro.deps, name as you would the attribute (see below).Engineers often write macros when the Starlark API of relevant rules is insufficient for their specific use case, regardless of whether the rule is defined within Bazel in native code, or in Starlark. If you're facing this problem, ask the rule author if they can extend the API to accomplish your goals.
As a rule of thumb, the more macros resemble the rules, the better.
See also macros.
py_extension. For most languages, typical rules include:
*_library - a compilation unit or "module".*_binary - a target producing an executable or a deployment unit.*_test - a test target. This can include multiple tests. Expect all
tests in a *_test target to be variations on the same theme, for
example, testing a single library.*_import: a target encapsulating a pre-compiled artifact, such as a
.jar, or a .dll that is used during compilation.srcs: label_list, allowing files: source files, typically
human-authored.deps: label_list, typically not allowing files: compilation
dependencies.data: label_list, allowing files: data files, such as test data etc.runtime_deps: label_list: runtime dependencies that are not needed
for compilation.doc keyword argument to the
attribute's declaration (attr.label_list() or similar).myrule the name _myrule_impl.