0.2.1 Label Anatomy
Every Bazel library, binary, test, and source file has an address called a label. Labels are how BUILD files declare dependencies, how the command line specifies what to build, and how Bazel's query tools navigate the dependency graph. To start, get comfortable with the common explicit apparent form1:
@repo_name//path/to/package:target_name
The form has three components separated by fixed delimiters: the repository, the package, and the target.
The single-@ repository name is apparent: it is the name visible from the
repository where the label is written. Bazel's canonical form begins with
@@ and uses a unique resolved repository name. You will usually write apparent
labels. The distinction matters when inspecting resolved external dependencies.1
The Three Components
@repo_name — The Repository
The first component identifies which repository the target lives in1. The repository concept maps to the directory tree anchored by a Bazel boundary marker, as described in 0.1.2 Repository Root.
Your project is the main repository. External dependencies pulled in via bazel_dep() in MODULE.bazel are also repositories, each fetched on demand into a location Bazel manages2. When referring to a target in the current repository, the @repo_name part is omitted entirely1:
//app:server
This is the form you'll see most often in your main repository. The leading
// signals "start from the root of the current repository". The same shorthand
also works from files evaluated inside an external repository.
When referring to a target in an external repository, the @ prefix and the repository name are required:
@rules_cc//cc:defs.bzl
//path/to/package — The Package
The second component is the package name, which is the path from the repository root to the directory containing the BUILD file1. This is the same concept as the package from 0.1.3 Package: the directory where a BUILD.bazel (or BUILD) file lives defines a package, and the directory's path relative to the root becomes the package's name.
For a BUILD.bazel file at app/backend/BUILD.bazel, the package component is //app/backend.
:target_name — The Target
The third component names a specific target within the package1. A target is anything declared in a BUILD file. For example, a rule invocation like cc_binary(name = "server", ...) creates a target named server. Source files in the package directory are also targets, referenced by their path relative to the BUILD file3:
//app:main.cc
The name attribute of a rule call defines the target name1. That name is local to the package, so two packages can each have a target named lib without conflict because the full label includes the package path.
What a Label Can Point To
Labels mostly address two kinds of targets2:
- Rule targets — created by calling a rule in a
BUILDfile (cc_binary,java_library,sh_test). These represent buildable units: something Bazel knows how to compile, link, test, or otherwise produce. - File targets — files owned by a package. Source files exist in the repository. Generated files, also called derived or output files, are produced by rules.2
Both are valid label references. //app:server might point to a cc_binary rule target, while //app:main.cc points to a source file in the same package. Bazel resolves the label the same way regardless of what it points to2. The next item, 0.2.2 Syntactic Sugar & Relative Labels, explains why those labels are often shortened in real BUILD files.
Targets Are Not Paths
A label names something in Bazel's target namespace, not an arbitrary path on disk. A file target corresponds to one actual file, but a rule target may correspond to zero, one, or many files. Some files on disk are deliberately hidden implementation details and have no label you can write in a BUILD file or on the command line.4
That is why rule outputs can be surprising at first. Some rules create first-class output file targets in the same package as the rule. For example, declaring java_binary(name = "foo", ...) also implicitly declares an output file target named foo_deploy.jar. Bazel can build it on demand, other targets can depend on it when the rule contract allows that, and query tools can observe it.4 Other generated files, such as low-level object files produced during compilation, are just internal artifacts. They may exist under Bazel's output tree, but they are not public target names.4
When writing a dependency, command-line target, or query expression, think in labels first and filesystem paths second. The rule-authoring version of this distinction appears later in 4.2.1 Rule Function, where custom rules decide which generated files become part of the target's public contract.
Putting It Together
Given this directory tree:
my-project/
├── MODULE.bazel
├── app/
│ ├── BUILD.bazel
│ ├── main.cc
│ └── server.cc
└── lib/
├── BUILD.bazel
└── utils.cc
The labels for targets in this project:
| Target | Label |
|---|---|
A cc_binary named server in app/BUILD.bazel | //app:server |
The source file main.cc | //app:main.cc |
A cc_library named utils in lib/BUILD.bazel | //lib:utils |
Dependencies between packages use these labels. The app/BUILD.bazel might declare:
cc_binary(
name = "server",
srcs = ["main.cc", "server.cc"],
deps = ["//lib:utils"],
)
deps = ["//lib:utils"] is a label reference. It tells Bazel that building //app:server requires first building //lib:utils.
File Labels in Subdirectories
When a source file lives in a subdirectory of the package (a subdirectory without its own BUILD file), the label uses the path relative to the BUILD file's directory1:
//my/app:testdata/input.txt
This is the file my/app/testdata/input.txt, owned by the package my/app because testdata/ has no BUILD file of its own (as covered in 0.1.3 Package).
A common mistake is trying to use a relative path to refer to a file in a different package. If testdata/ has its own BUILD file, it's a separate package, and the correct label is //my/app/testdata:input.txt, not testdata/input.txt1.
Reproduce the distinction between a valid label shape and a missing target with the //lib:missing alias.
A label is the address of any target in the build graph. In the common
@repo//package:target form, @repo identifies the repository, the package maps
to a directory with a BUILD file, and the target names a specific rule or file
there. Omit @repo for the current repository. //app:server is the form you
will use most in the main repository. Bzlmod's repository-name mapping belongs
to 3.1.3 Repo Mapping.
Check your understanding · 3 questions
1.Match each component of the label @rules_cc//cc:defs.bzl to its role:
Drag each answer onto the matching prompt, or click an answer and then click a prompt
2.True or false: label addressing and Bazel terminology.
Choose True or False for each sentence
3.A file lives at my/app/testdata/input.txt, and testdata/ has no BUILD file of its own. Which label is correct?
Select one answer
Footnotes
-
Labels — Full label anatomy, package and target name components, shorthand forms, file labels in subdirectories, cross-package reference rules ↩1 ↩2 ↩3 ↩4 ↩5 ↩6 ↩7 ↩8 ↩9
-
Repositories, workspaces, packages, and targets — Target types (source files, generated files, rules), package groups, file-to-package ownership ↩1 ↩2 ↩3 ↩4
-
Packages, Rules, Targets, and Labels — Label syntax
@repo//pkg:target, shorthand conventions, canonical vs apparent names are internal-only ↩ -
Common definitions — Implicit output targets, target namespace vs filesystem namespace, and hidden generated implementation files ↩1 ↩2 ↩3