0.1 Filesystem Hierarchy

At first glance, a Bazel repository looks like an ordinary directory tree. Bazel gives several files and paths in that tree specific roles: a repository marker establishes the root, each BUILD.bazel creates a package, and bazel-* symlinks provide access to generated outputs outside the source tree. Bazelisk sits one step before that model: when a project uses it, .bazelversion tells the launcher which Bazel binary should read the repository.

One Tree, Four Roles

The four articles in this section answer different questions about the repository:

  • 0.1.1 Bazelisk & .bazelversion explains how a project selects the Bazel version that developers and CI run. .bazelversion configures a compatible launcher; it does not define Bazel's repository structure.
  • 0.1.2 Repository Root shows how marker files such as MODULE.bazel and REPO.bazel establish the directory from which package names and labels are resolved.
  • 0.1.3 Package explains how BUILD.bazel files partition the source tree. The nearest package boundary determines which package owns a file, and adding one can change file labels, recursive glob() results, and the dependencies needed across packages.
  • 0.1.4 Output Root separates the sources you edit from Bazel-managed output. Convenience symlinks at the repository root make generated artifacts easier to find without turning them into source files.

Repository Structure Has Meaning

The central model is narrower than "every special file is a boundary." Within the source tree, repository markers establish the coordinate system and BUILD.bazel files divide that tree into packages. The labels in 0.2 Language of Labels attach to those packages, and the declarations in 0.3 Starlark Syntax Basics describe targets inside them.

.bazelversion and the output symlinks support that model without defining package ownership. The former helps select the binary that runs Bazel; the latter point from the checkout to generated state. Keeping those roles separate makes it easier to tell whether a file affects launcher setup, repository structure, package structure, or output navigation.

How To Read The Section

For the fastest orientation in an unfamiliar checkout, start with 0.1.2 Repository Root and 0.1.3 Package. They explain where Bazel's coordinates begin and how the source tree is partitioned. Read 0.1.1 Bazelisk & .bazelversion when you need to understand which Bazel release a command will run, and 0.1.4 Output Root after a build when you need to distinguish generated artifacts from sources.

think

Predict: //app:bundle includes config/settings.json through glob(["**/*.json"]). A teammate adds app/config/BUILD.bazel without moving settings.json or changing app/BUILD.bazel. What happens to the file's label and to the parent package's glob?

Reveal

The new BUILD file makes app/config a separate package. The file's label changes from //app:config/settings.json to //app/config:settings.json, and the recursive glob in //app no longer includes it.

The file stayed in the same directory, but its package ownership changed. If //app:bundle still needs it, the new package must expose the file through a target or exports_files(), and //app:bundle must refer to that cross-package label.

key takeaway

A Bazel checkout combines four related but distinct roles: Bazelisk can select the Bazel binary, repository markers establish the root, BUILD.bazel files create packages, and output symlinks lead to generated state. Distinguishing those roles gives labels and BUILD declarations a concrete filesystem context.