Bazel Book

Label Anatomy

Every target in Bazel — every 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, it is enough to get comfortable with the fully written label form1:

@repo_name//path/to/package:target_name

Three components separated by fixed delimiters: the repository, the package, and the target.

@rules_cc // cc/toolchains : compiler
REPOSITORY
Which codebase?
Omit for main repo
//app:server
PACKAGE
Which directory?
Has a BUILD file
cc/toolchains/
TARGET
Which rule or file?
name = "..." in BUILD
cc_binary(...)

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 main repository, the @repo_name part is omitted entirely1:

//app:server

This is the form you'll see most often. The leading // signals "start from the root of the current 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 path from the repository root to the directory containing the BUILD file — the package name1. 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 — 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 — 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 address two kinds of targets2:

  • Rule targets — created by calling a rule in a BUILD file (cc_binary, java_library, sh_test). These represent buildable units: something Bazel knows how to compile, link, test, or otherwise produce.
  • File targets — source files that exist in the repository. Every source file in a package is implicitly a target, addressable by label2. Generated files (outputs of rules) are also targets, but you rarely reference them by label directly.

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.

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:

TargetLabel
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.

Repository vs Workspace

Two terms come up frequently in Bazel documentation and they are easy to conflate4:

  • Repository — the directory tree rooted at a Bazel boundary marker. Your project is one repository. Each external dependency is another repository. A repository has a single root, a single set of packages, and a unique name.
  • Workspace — the main repository plus all its external repositories taken together. When Bazel resolves a label like @rules_cc//cc:defs.bzl, it's looking across the workspace — finding the rules_cc repository and navigating to the target within it.

In practice, one Bazel repository maps to one Git repository, and most daily work stays within the main repo. The workspace concept becomes relevant when external dependencies enter the picture — covered in depth in 3.1 Dependency Management.

One Advanced Caveat

Official Bazel docs make a further distinction between the everyday repository name you write (@repo) and an internal canonical repository name written with @@repo5. That matters later for Bzlmod, repo mapping, and some error messages.

You do not need that distinction yet. The practical forms to internalize are:

  • //pkg:target for the main repository
  • @repo//pkg:target for an external repository

If @@... ever shows up in later levels or in diagnostics, read it as a more explicit internal repo identity, not as the normal syntax you should start writing in BUILD files.

key takeaway

A label is the address of any target in the build graph: @repo//package:target. The repository component identifies which codebase, the package component maps to a directory with a BUILD file, and the target component names a specific rule or file within that package. For targets in the main repository, omit the @repo part — //app:server is the form you'll use most.

Check your understanding

1.What are the three components of a full Bazel label?

2.What does the label //app:server refer to?

3.What kinds of things can a label point to?

Answer all questions to check

Footnotes

  1. 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

  2. Repositories, workspaces, packages, and targets — Target types (source files, generated files, rules), package groups, file-to-package ownership 1 2 3 4

  3. Packages, Rules, Targets, and Labels — Label syntax @repo//pkg:target, shorthand conventions, canonical vs apparent names are internal-only

  4. Bazel Glossary — Repository vs workspace distinction and the broader workspace containing the main repo plus external repos

  5. External dependencies overview — Canonical repository names (@@, globally unique) vs apparent names (@, per-repo mapping)