0.2.2 Syntactic Sugar & Relative Labels
The explicit apparent form @repo//package:target is precise but verbose. In daily use, most of those components can be omitted. Bazel defines a set of shorthand conventions that make BUILD files more readable and CLI invocations faster to type. Understanding which parts can be dropped, and where, prevents a class of common mistakes.
The Eponymous Target Shorthand
When a target has the same name as the last component of its package path, the :target part can be omitted1. These two labels are equivalent:
//my/app/lib:lib
//my/app/lib
Prefer //x over //x:x when referring to an eponymous target2. Naming a package's primary target after its directory shortens deps lists and CLI commands, and makes the "main thing" in a package discoverable by name alone3. If a package has no natural eponymous target, an alias() can create one3. But don't create an eponymous target just for the shorthand if nothing in the package warrants it2.
This shorthand applies in BUILD files, .bzl files, and on the command line. //my/app always means //my/app:app, even if no such target exists1. It never means "all targets in the package" or "the package itself." A common mistake in BUILD files is using //my/app to refer to the package. It refers to the target named app1.
The only context where //my/app means "the package" is inside package_group specifications and in .bzl files that expect a package argument1.
Relative Labels Inside BUILD Files
Inside a BUILD file, you can reference targets in the same package without the //package prefix1. The package part and optionally the colon can be dropped:
cc_binary(
name = "server",
srcs = ["main.cc", "server.cc"],
deps = [":utils"],
)
Here :utils refers to a target named utils in the same package. The full form would be //my/app:utils if this BUILD file lives in my/app/.
Convention matters: source files are referenced without a colon prefix ("main.cc"), while rule targets and generated files use the colon (:utils, :gen_header)2. The colon is not syntactically required for either. It is a readability convention that distinguishes inputs you wrote from outputs that Bazel produces1.
cc_library(
name = "lib",
srcs = ["x.cc"],
hdrs = [":gen_header"],
)
Trace: Your //my/app/BUILD.bazel has referenced a data file as "testdata/golden.txt" for months, and the build is green. A teammate adds testdata/BUILD.bazel for an unrelated fixture target. The next build of //my/app fails, even though nobody touched your BUILD file. What changed?
Reveal
The file moved in Bazel's ownership model. Before testdata/BUILD.bazel existed, testdata/golden.txt belonged to the parent package and the relative label resolved inside //my/app. After the BUILD file appeared, testdata/ became its own package, so the file's graph coordinate is //my/app/testdata:golden.txt.
The bytes did not change, but the package boundary did. Relative labels cannot cross that boundary. From the parent package, use the full cross-package label //my/app/testdata:golden.txt, or keep the fixture directory package-free if those files are meant to remain owned by //my/app.
Relative labels cannot cross package boundaries1. If testdata/ is a separate package (it has its own BUILD file), you cannot write testdata/input.txt from the parent package's BUILD file. You must use the full label //my/app/testdata:input.txt1.
Relative Labels on the Command Line
On the command line, labels without a // prefix are resolved relative to the current working directory4. If your working directory is foo/:
| You type | Bazel resolves to |
|---|---|
:foo | //foo:foo |
bar:wiz | //foo/bar:wiz |
This is a convenience for quick iteration. bazel run :dev_server from the
backend/ package is equivalent to bazel run //backend:dev_server3. Forms
without a colon can be ambiguous because Bazel resolves them against the package
boundaries that actually exist. In BUILD files, all cross-package references
must use the // form3.
Current-Repository Default
For Level 0, the explicit form for targets in the current repository is simply
//pkg:target. In your main repository, that is the normal syntax you will read
and write in BUILD files and most command lines.
Bare Repository Name
When a repository publishes a single rule from a .bzl file with the same name as the repository, a cascading shorthand applies. For example, with @bazel_env providing bazel_env.bzl that exports a bazel_env rule5:
load("@bazel_env//:bazel_env.bzl", "bazel_env")
And on the command line:
bazel run @bazel_env
This resolves to @bazel_env//:bazel_env, the eponymous target shorthand applied to the root package of the external repository5. This convention keeps load statements short and enables an npx-like bazel run @tool pattern5.
Shorthand Reference
| Full form | Shorthand | Where valid |
|---|---|---|
//app/lib:lib | //app/lib | Everywhere |
//app:utils | :utils | Current package context: in //app/BUILD, or on the CLI from app/ |
//app:main.cc | main.cc | In //app/BUILD (source-file convention) |
@repo//pkg:target | //pkg:target | Same repository |
@repo//:repo | @repo | Everywhere |
Most labels you encounter in practice are shortened. The two rules to internalize: omit the repository when you're in the same repo (//pkg:target), and omit the target name when it matches the directory name (//pkg). Inside BUILD files, :target is the relative form. On the command line, labels without // resolve from your working directory.
Check your understanding · 2 questions
1.True or false: label shorthand rules.
Choose True or False for each sentence
2.Match each label shorthand to the full canonical label it expands to:
Drag each answer onto the matching prompt, or click an answer and then click a prompt
Footnotes
-
Labels — Shorthand forms, relative labels, eponymous target convention,
@@//for main repository, cross-package reference rules ↩1 ↩2 ↩3 ↩4 ↩5 ↩6 ↩7 ↩8 -
BUILD Style Guide — Colon convention for source vs rule targets, eponymous target naming, prefer local references in same package ↩1 ↩2 ↩3
-
Packages, Rules, Targets, and Labels — Terminal labels relative to working directory, BUILD file labels require
//, default target shorthand ↩1 ↩2 ↩3 ↩4 -
Build programs with Bazel — CLI target patterns resolved relative to working directory,
//-prefixed patterns resolved from workspace root ↩ -
Developer Tooling in Monorepos with bazel_env — Bare repo name shorthand
@bazel_env == @bazel_env//:bazel_env, .bzl naming convention for single-rule modules ↩1 ↩2 ↩3