Buildozer
Buildozer is a command line tool to rewrite multiple
Bazel BUILD files using
standard commands.
Installation
- Build a binary and put it into your $GOPATH/bin:
go install github.com/bazelbuild/buildtools/buildozer@latest
Usage
buildozer [OPTIONS] ['command args' | -f FILE ] label-list
Here, label-list
is a space-separated list of Bazel labels, for example
//path/to/pkg1:rule1 relative/path/to/pkg2:rule2
. In addition to the Bazel
label syntax for specifying a package, Buildozer also allows the package part to
refer to a BUILD-like file, for example //WORKSPACE:all
or
toolchains/BUILD.tpl:host_toolchain
.
When -f FILE
is used, buildozer reads commands from FILE
(-
for stdin).
Format: lines of |
-separated sets of commands and labels (command args|label|label...
).
When the label is a single '*', then the command will be applied to all
elements of label-list from the command line.
You should specify at least one command and one target. Buildozer will execute
all commands on all targets. Commands are executed in order, files are processed
in parallel.
Targets
Targets look like Bazel labels, but there can be some differences in presence of
macros.
- Use the label notation to refer to a rule:
//buildtools/buildozer:edit
- Use the
__pkg__
suffix to refer to the package declaration:
//buildtools/buildozer:__pkg__
- Use an asterisk to refer to all rules in a file:
//pkg:*
- Use
...
to refer to all descendant BUILD files in a directory: //pkg/...:*
- Use percent to refer to all rules of a certain kind:
//pkg:%java_library
- Use percent-and-number to refer to a rule that begins at a certain line:
//pkg:%123
. - Use
-
for the package name if you want to process standard input stream
instead of a file: -:all_tests
.
Options
OPTIONS include the following options:
-stdout
: write changed BUILD file to stdout-buildifier
: format output using a specific buildifier binary. If empty, use built-in formatter.-k
: apply all commands, even if there are failures-quiet
: suppress informational messages-shorten_labels
: convert added labels to short form, e.g. //foo:bar => :bar-types
: Filter the targets, keeping only those of the given types, e.g.
buildozer -types go_library,go_binary 'print rule' '//buildtools/buildozer:*'
-eol-comments=false
: When adding new comments, put them on a separate line.
See buildozer -help
for the full list.
Edit commands
Buildozer supports the following commands('command args'
):
add <attr> <value(s)>
: Adds value(s) to a list attribute of a rule. If a
value is already present in the list, it is not added.new_load <path> <[to=]from(s)>
: Add a load statement for the given path,
importing the symbols. Before using this, make sure to run
buildozer 'fix movePackageToTop'
. Afterwards, consider running
buildozer 'fix unusedLoads'
.replace_load <path> <[to=]from(s)>
: Similar to new_load
, but removes
existing load statements for the requested symbols before adding new loads.substitute_load <old_regexp> <new_template>
Replaces modules of loads which
match old_regexp
according to new_template
. The regular expression must
follow RE2 syntax.
new_template
may be a simple replacement string, but it may also expand
numbered or named groups using $0
or $x
.comment <attr>? <value>? <comment>
: Add a comment to a rule, an attribute,
or a specific value in a list. Spaces in the comment should be escaped with
backslashes.print_comment <attr>? <value>?
delete
: Delete a rule.fix <fix(es)>?
: Apply a fix.move <old_attr> <new_attr> <value(s)>
: Moves value(s)
from the list old_attr
to the list new_attr
. The wildcard *
matches all values.new <rule_kind> <rule_name> [(before|after) <relative_rule_name>]
: Add a
new rule at the end of the BUILD file (before/after <relative_rule>
). The
identifier __pkg__
can be used to position rules relative to package().print <attr(s)>
remove <attr>
: Removes attribute attr
. The wildcard *
matches all
attributes except name
.remove <attr> <value(s)>
: Removes value(s)
from the list attr
. The
wildcard *
matches all attributes. Lists containing none of the value(s)
are
not modified.remove_comment <attr>? <value>?
: Removes the comment attached to the rule,
an attribute, or a specific value in a list.remove_if_equal <attr> <value>
: Removes the attribute attr
if its value
is equal to value
.rename <old_attr> <new_attr>
: Rename the old_attr
to new_attr
which must
not yet exist.replace <attr> <old_value> <new_value>
: Replaces old_value
with new_value
in the list attr
. Wildcard *
matches all attributes. Lists not containing
old_value
are not modified.substitute <attr> <old_regexp> <new_template>
: Replaces strings which
match old_regexp
in the list attr
according to new_template
. Wildcard
*
matches all attributes. The regular expression must follow
RE2 syntax. new_template
may
be a simple replacement string, but it may also expand numbered or named
groups using $0
or $x
. Lists without strings that match old_regexp
are not modified.set <attr> <value(s)>
: Sets the value of an attribute. If the attribute
was already present, its old value is replaced.set_if_absent <attr> <value(s)>
: Sets the value of an attribute. If the
attribute was already present, no action is taken.set kind <value>
: Set the target type to value.set_select <attr> <key_1> <value_1> <key_n> <value_n>
copy <attr> <from_rule>
: Copies the value of attr
between rules. If it
exists in the to_rule
, it will be overwritten.copy_no_overwrite <attr> <from_rule>
: Copies the value of attr
between
rules. If it exists in the to_rule
, no action is taken.dict_add <attr> <(key:value)(s)>
: Sets the value of a key for the dict
attribute attr
. If the key was already present, it will not be overwrittendict_set <attr> <(key:value)(s)>
: Sets the value of a key for the dict
attribute attr
. If the key was already present, its old value is replaced.dict_remove <attr> <key(s)>
: Deletes the key for the dict attribute attr
.dict_list_add <attr> <key> <value(s)>
: Adds value(s) to the list in the
dict attribute attr
.
The following commands only apply to MODULE.bazel
files (e.g. the target
//MODULE.bazel:__pkg__
):
use_repo_add [dev] <extension .bzl file> <extension name> <repo(s)>
:
Ensures that the given repositories generated by the given extension are
imported via use_repo
. If the dev
argument is given, extension usages
with dev_dependency = True
will be considered instead.use_repo_remove [dev] <extension .bzl file> <extension name> <repo(s)>
:
Ensures that the given repositories generated by the given extension are
not imported via use_repo
. If the dev
argument is given, extension
usages with dev_dependency = True
will be considered instead.
Here, <attr>
represents an attribute (being add
ed/rename
d/delete
d etc.),
e.g.: srcs
, <value(s)>
represents values of the attribute and so on.
A '?' indicates that the preceding argument is optional.
The fix command without a fix specified applied to all eligible fixes.
Use //path/to/pkg:__pkg__
as label for file level changes like new_load
and
new
.
A transformation can be applied to all rules of a particular kind by using
%rule_kind
at the end of the label(see examples below).
Examples
buildozer 'add deps //base' //pkg:rule //pkg:rule2
buildozer 'new_load //tools/build_rules:build_test.bzl build_test' //pkg:__pkg__
buildozer 'replace_load @rules_build//build:defs.bzl build_test' //pkg:__pkg__
buildozer 'substitute_load ^@([^/]*)//([^:].*)$ //third_party/build_defs/${1}/${2}' //pkg:__pkg__
buildozer 'set default_visibility //visibility:public' //pkg:__pkg__
buildozer 'set kind java_library' //pkg:%gwt_module
buildozer 'replace deps //pkg_v1 //pkg_v2' //pkg:rule
buildozer 'substitute deps //old/(.*) //new/${1}' //pkg:rule
buildozer 'remove deps foo' //pkg:%cc_library
buildozer 'remove testonly' '//pkg:*'
buildozer 'comment timeout Delete\ this\ after\ 2015-12-31.' //pkg:rule_test
buildozer 'new java_library foo' //pkg:__pkg__
buildozer 'new cc_binary new_bin before tests' //:__pkg__
buildozer 'copy testonly protolib' //pkg:py_protolib
buildozer 'set compile 1' 'set srcmap 1' //pkg:rule
buildozer 'set_if_absent allowv1syntax 1' //pkg:%soy_js
buildozer 'add new_attr def_val' //:%cc_binary
Print commands
These commands are not modifying files, Buildifier returns 0 after a successful
execution.
print <attribute(s)>
: For each target, prints the value of the attributes
(see below).print_comment <attr>? <value>?
: Prints a comment associated with a rule,
an attribute or a specific value in a list.
The print command prints the value of the attributes. If a target doesn't have
the attribute, a warning is printed on stderr.
There are some special attributes in the print
command:
kind
: displays the name of the functionlabel
: the fully qualified labelrule
: the entire rule definitionstartline
: the line number on which the rule begins in the BUILD fileendline
: the line number on which the rule ends in the BUILD filepath
: the absolute path to the BUILD file that contains the rules
Examples
# Print the kind of a target
buildozer 'print kind' base # output: cc_library
# Print the name of all cc_library in //base
buildozer 'print name' base:%cc_library
# Get the default visibility of the //base package
buildozer 'print default_visibility' base:%package
# Print labels of cc_library targets in //base that have a deps attribute
buildozer 'print label deps' base:%cc_library 2>/dev/null | cut -d' ' -f1
# Print the list of labels in //base that explicitly set the testonly attribute:
buildozer 'print label testonly' 'base:*' 2>/dev/null
# Print the entire definition (including comments) of the //base:heapcheck rule:
buildozer 'print rule' //base:heapcheck
Converting labels
Buildozer works at the syntax-level. It doesn't evaluate the BUILD files. If you
need to query the information Bazel has, please use bazel query
. If you have a
list of Bazel labels, chances are that some of them are generated by BUILD
extensions. Labels in Buildozer are slightly different from labels in Bazel.
Bazel cares about the generated code, while Buildozer looks at the BUILD file
before macro expansion.
To see the expanded BUILD files, try:
bazel query --output=build //path/to/BUILD
Do multiple changes at once
Use buildozer -f <file>
to load a list of commands from a file. The usage is
just like arguments on the command-line, except that arguments are separated by
|
. Lines that start with #
are ignored.
$ cat /tmp/cmds
# a comment
new cc_library foo|//buildtools/buildozer/BUILD
add deps //base //strings|add srcs foo.cc|//buildtools/buildozer:foo
add deps :foo|//buildtools/buildozer
$ buildozer -f /tmp/cmds
fixed //buildtools/buildozer/BUILD
The list of commands will typically be generated and can be large. This is
efficient: Commands are grouped so that each file is modified once. Files are
processed in parallel.
Error code
The return code is:
0
on success, if changes were made or only readonly commands were executed1
when there is a usage error2
when at least one command has failed3
on success, when no changes were made
Source Structure
buildozer/main.go
: Entry point for the buildozer binaryedit/buildozer.go
: Implementation of functions for the buildozer commandsedit/edit.go
: Library functions to perform various operations on ASTs. These- functions are called by the impl functions in buildozer.go
edit/fix.go
: Functions for various fixes for the buildozer 'fix <fix(es)>'
command, like cleaning unused loads, changing labels to canonical notation, etc.edit/types.go
: Type information for attributes