Language of Labels
If 0.1 Filesystem Hierarchy teaches you how Bazel sees the repository tree, this section teaches you how Bazel names things inside it. The first time you read real BUILD files or Bazel commands, the notation can feel dense:
//app:server
:lib
//...
//friends:__subpackages__
Those forms look related because they are related, but they do not all do the same job. Some are ordinary labels naming one exact target. Some are local shorthand. Some are target patterns on the command line. Some are visibility specifications defining who may depend on whom. The point of this section is to separate those jobs cleanly instead of treating them as punctuation to memorize.
Labels Turn The Tree Into A Graph
Packages, files, and repositories only become navigable build objects once Bazel can name them. Ordinary labels are the core naming system. Bazel then reuses closely related syntax for selecting sets of targets and for describing package boundaries.
When a BUILD file says deps = ["//lib:core"], it is not talking about a path the way a shell script would. It is naming a target in Bazel's graph. When a command says bazel test //..., it is not naming one target at all. It is asking Bazel for a target set derived from package structure. When visibility says //app:__subpackages__, it is not describing a file location to open or a target to build. It is describing which client packages may cross a boundary.
That is why this notation matters so early. Labels themselves show up in dependencies and load() statements, and close relatives of the same syntax show up in command-line selection, visibility rules, and later query output. Once that coordinate system clicks, BUILD files stop looking like strings pasted into lists and start looking like graph edges and boundaries with precise meaning.
One Notation Family, Four Jobs
This section is really one notation family reused for four jobs.
- 0.2.1 Label Anatomy introduces the full address form: repository, package, target. This is the anchor article for the whole section, because every shorthand and every special case is a variation on that structure.
- 0.2.2 Syntactic Sugar & Relative Labels explains why Bazel code usually omits parts of the label when context already supplies them. This is where
:lib,//pkg, and bare repository forms stop feeling cryptic. - 0.2.3 Target Patterns shifts from naming one target to selecting many. The syntax still resembles labels, but the job is different:
//pkg:all,//pkg/..., and exclusions are CLI selection language, not target addresses. - 0.2.4 Visibility, 0.2.5 package_group, 0.2.6 package() Function, and 0.2.7 exports_files() turn the same general vocabulary toward boundaries and APIs. They answer not just "what is this target called?" but "who is allowed to reach it?" and "how do raw files cross package boundaries?"
That spread is exactly why labels are worth a full section. They are not a tiny syntax detail you memorize once. They are the connective tissue between repository structure, BUILD-file declarations, and the everyday commands you type.
Why This Feels Hard At First
The difficulty is not that the rules are arbitrary. The difficulty is that similar-looking strings live in different contexts.
//app usually means the eponymous target //app:app, not "the package." But inside a package_group, //app does mean the package. //pkg/... looks like a path expression, but it is a target pattern. //visibility:public and //friends:__subpackages__ are written in label-shaped syntax, but they are visibility specifications, not ordinary targets you can build. Even the humble colon changes tone depending on where you are: :lib is a same-package shorthand in a BUILD file, while :all is a pattern operator on the command line.
Newcomers often think they are learning one rule and then keep hitting exceptions. A better way to see it is that Bazel reuses one coordinate style across adjacent problems. The surface notation stays similar because all of those problems are about addressing things in the build graph. What changes is the question being answered: one target, many targets, or allowed clients.
The Deep Point Of The Section
Every article here is trying to sharpen one sentence: in Bazel, references are part of the architecture.
If these forms look like anonymous strings, a BUILD file reads like a list of punctuation and paths. Once they read as precise references into a graph with ownership and access control, the same BUILD file starts to read like a small interface definition. A deps list is no longer "some paths." It is a declaration of exactly which targets this one relies on. A visibility list is no longer bureaucratic ceremony. It is the package API boundary made explicit and mechanically enforced. A target pattern is no longer shell shorthand. It is a way of asking Bazel for a slice of the graph.
That shift matters because it makes later topics feel natural. The output locations from 0.1.4 Output Root become easier to predict. The Starlark declarations in 0.3 Starlark Syntax Basics become easier to read. Later, advanced topics such as repo mapping and external repositories mostly build on the same naming model rather than replacing it.
How To Read The Section
If your main problem is "I keep seeing //foo:bar and do not know what part is what," start with 0.2.1 Label Anatomy and only then move to 0.2.2 Syntactic Sugar & Relative Labels. If the confusion comes from commands such as bazel test //... -//experimental/..., jump next to 0.2.3 Target Patterns. If what you really need is to understand package APIs and why Bazel is blocking one dependency, read 0.2.4 Visibility through 0.2.7 exports_files() as one cluster rather than isolated facts; those articles together explain how labels become policy.
It is also worth resisting one temptation here: do not chase every strange-looking label form immediately. At Level 0, the core forms are enough. Learn //pkg:target, :target, //..., and the basic visibility shapes first. The more exotic repository-name mechanics can wait until they solve a real problem for you.
When a label-like string confuses you, ask three questions in order: does it name one target, select a set of targets, or describe who may depend on a target? Most of the section becomes easier once you identify which of those three jobs the syntax is doing.
The label syntax family is not just Bazel's way of naming one target. It is the coordinate language Bazel uses to name targets, shorten local references, select slices of the graph, and express package boundaries. This section is where Bazel's punctuation stops being noise and starts becoming a coordinate system you can think in.
Items in this section · 7
CLI wildcards for selecting sets of targets: //..., :all, exclusions.
Package-level defaults: default_visibility, default_testonly.
Making raw source files visible outside the package.