Packages
Bazel uses files named BUILD.bazel
(or BUILD
) to describe the source files which may be built or tested. Any directory containing a BUILD
file is called a “package” in Bazel. This includes subdirectories unless they have their own BUILD
file.
repo/ # This is the "//" package
├── BUILD.bazel
├── ...
├── animations # Here is the "//animations" package
│ ├── BUILD.bazel
│ ├── browser # Here is "//animations/browser"
│ │ ├── BUILD.bazel
│ │ └── ...
| | ...
│ ├── src
│ │ └── index.ts # No BUILD here, so still the "animations" package
│ ├── test
│ │ ├── BUILD.bazel
│ │ └── ...
Naming
We prefer BUILD.bazel
over BUILD
to be more explicit, to allow for tooling to select with *.bazel
, and to avoid colliding with a directory named build
on case-insensitive systems.
However in written text it's easier to abbreviate them by writing just BUILD
to mean either file.
Packages are encapsulated, so:
glob
stays within the package. It is a function wildcard patterns for filenames such as**/*.txt
.- Source files are “owned” by their package and aren't visible outside (unless you expose them with
exports_files
). - Output files are always written to the
bazel-out
folder within the same package.
Rules
Bazel defines a “Rule” as follows:
A schema for defining rule targets in aBUILD
file, such assh_library
. From the perspective of aBUILD
file author, a rule consists of a set of attributes and black box logic. The logic tells the rule target how to produce output artifacts and pass information to other rule targets.
Rules are provided by “rulesets”, which are Bazel plugins loaded from the MODULE.bazel
file, such as rules_shell
. You may think of a rule as a factory function or a constructor. Call it from BUILD
to describe your sources. The result is a type of target called a “rule target”.
A rule is generally designed to be used with a particular bazel
command, making it buildable, runnable, and/or testable. A naming convention typically hints which one it is:
A Rule Named | is invoked with | to |
foo_library | bazel build | populate bazel-out |
foo_binary | bazel run | have side-effects |
foo_test | bazel test | assert exit 0 |
Note that unlike other build systems, most rules do not allow you to give procedural logic for what to do - that’s the job of the rule implementation logic. They are "bare facts". They only describe the source files and their dependencies.
Rule example
sh_binary(
name = "run_me",
srcs = ["my_script.bash"],
env = {"ENV": "dev"},
data = ["my.json"],
deps = ["//lib:bash_helpers"],
)
The arguments to this sh_binary
rule are called "attributes".
Some attributes are common for most rules:
name
is always required. Use this in a label to refer to the targetsrcs
typically means files in the source treedeps
typically means other rule targets which are needed at build timedata
is likedeps
but is only needed at runtime
Other attributes are particular to the rule implementation.
env
is an attribute present on all executable targets, providing environment variables when the target is executed withbazel run
Other targets
Aside from rule targets, there are a few other types of target:
- Source files: refer to an individual file in the source tree.
- Build output files (so long as they are pre-declared).
- Package groups, which are useful with Bazel’s visibility feature.
Labels
Labels are the way to reference a target from the command-line or in a BUILD
file. You can think of labels very much like URIs which refer to resources on the internet, and have a special syntax with segments like https
which is the “scheme”.
You type these frequently, so it’s worth learning the syntax:
┌ package name ┐
v v
@my_repo//my_library/utils:draw_circle
^ ^
| └- target
|
└- "apparent" repository name (optional)
Sometimes you might see a label starting with a double-@
sign. This is a “canonical” repository name, and you should not need to use it. This is a way to bypass the visibility restriction, but the name of the resulting repositories is brittle and hard to predict.
Label shorthand
If the working directory is in the same repository, //my_library/util:draw_circle
//
means the root of that repository.- On the command line, labels can be relative to the working directory
- Each package has a default label, named after the package
You can usually use this shorthand to save typing.
For example you could just cd backend; bazel run devserver
rather than
bazel run //backend/devserver:devserver
.
Every package should have a thoughtfully chosen default target, to save typing and make an ergonomic experience for developers interacting with Bazel in your project.
You can use alias
to introduce an indirection, for example if you'd like users to be able to
bazel run backend
from the repository root, then you add an alias
:
alias(
name = "backend", # the default target for the backend package
actual = "//backend/devserver",
)
You can get a reminder of the syntax with bazel help target-syntax
Target Patterns
On the command-line, you may want to refer to groups of targets. There are some special syntax:
:all
or:*
means “all targets in this package”...
means “all targets recursively in this package and subpackages”//...
means “all targets in this repository”