0.1.3 Package

Inside the repository root marked by MODULE.bazel (0.1.2 Repository Root), Bazel organizes source code into packages. A package is a directory that contains a BUILD file. That file turns the directory into a unit of code that Bazel knows how to build1.

The BUILD File

The BUILD file is a declarative file written in Starlark that defines targets, the things Bazel can build, test, or run2. A typical BUILD file lists rules like java_library, cc_binary, or sh_test, each describing a set of source files and their dependencies. The syntax and structure of BUILD files are covered in 0.3 Starlark Syntax Basics. The file's presence creates the package.

Two filenames are valid3:

FileNotes
BUILD.bazelPreferred. Explicit and unambiguous.
BUILDAlso valid. If both exist, BUILD.bazel takes precedence.

BUILD.bazel is the recommended choice because a directory named build/ on a case-insensitive filesystem (macOS, Windows) would collide with a file named BUILD4. Most newer projects use BUILD.bazel exclusively.

What Belongs to a Package

A package includes all files in its directory and all subdirectories beneath it unless a subdirectory has its own BUILD file, which makes it a separate package1. No file can belong to two packages at once. Every file belongs to the package whose BUILD file is in the nearest ancestor directory.

Consider this directory tree:

myproject/
├── BUILD.bazel
├── lib.cc
├── util/
│   ├── helper.cc
│   └── helper.h
└── tests/
    ├── BUILD.bazel
    ├── test.cc
    └── data/
        └── fixture.json

Two packages exist here:

PackageFiles it owns
//myprojectlib.cc, util/helper.cc, util/helper.h
//myproject/teststest.cc, data/fixture.json

util/ has no BUILD file, so its contents belong to //myproject. tests/data/ has no BUILD file either, but since tests/ does, fixture.json belongs to //myproject/tests, not to the root package1.

glob() patterns in a BUILD file also respect these boundaries. They never descend into subdirectories that have their own BUILD file4.

BEFORE
myproject/
//myproject
BUILD.bazel
lib.cc
util/ ← no BUILD file
helper.cc
helper.h
add util/BUILD.bazel
AFTER
myproject/
//myproject
BUILD.bazel
lib.cc
cross-package deps now required
util/
//myproject/util
BUILD.bazel NEW
helper.cc
helper.h
File ownership shifts
glob() stops at the new package
Cross-package deps required

Adding a BUILD File Splits a Package

Package boundaries are entirely determined by the presence of BUILD files. Adding util/BUILD.bazel to the tree above has three immediate effects:

  1. File ownership shifts. helper.cc and helper.h move from //myproject to //myproject/util. Labels like //myproject:util/helper.cc become invalid and must be replaced with //myproject/util:helper.cc or a named target in //myproject/util.

  2. Parent globs retract. A glob(["**/*.cc"]) in the root BUILD.bazel previously matched util/helper.cc. After the split, it no longer does because glob() never crosses into subpackages4. The root package's srcs lists silently shrink.

  3. Cross-package references require explicit dependencies. The root package can no longer treat util/helper.cc as a local file. It must declare a dependency on a target in //myproject/util, subject to visibility rules (0.2.4 Visibility).

Conversely, removing a BUILD file merges a package back into its parent. The files rejoin the parent's scope, and its globs pick them up again.

Package Names

A package's name is the path from the repository root to the directory containing the BUILD file5. In the example above, the two package names are myproject and myproject/tests. These paths become part of the label that identifies every target, covered in 0.2 Language of Labels.

The Root Package

When a BUILD file sits at the repository root itself — next to MODULE.bazel (0.1.2 Repository Root) — the package name is the empty string. This package is best kept free of source code, so that all meaningful packages have descriptive names6.

In practice, the root package often contains project-wide configuration or tooling rather than application source. Targets and files there use labels such as //:target because the package path before the colon is empty. H.1.1 Repository Models discusses how to choose a repository-wide layout.

Package Encapsulation

Source files are owned by their package4. Crossing that boundary requires an explicit target relationship. 0.2.4 Visibility explains which targets may depend on each other, while 0.2.7 exports_files() covers exposing a raw source file to another package.

How Many Packages?

Many repositories put BUILD files near buildable source7, but there is no rule that every directory must become a package. Choosing package granularity is a repository-design decision covered in H.1.1 Repository Models. Here, learn to derive the boundary from the BUILD files that actually exist.

Three small failure examples make the package boundary concrete: a malformed BUILD file, a glob() that stops matching files when a subpackage appears, and labels that name missing packages or repositories.

key takeaway

The presence of a BUILD file is the single mechanism that creates a package boundary. Use BUILD.bazel as the filename to avoid case-sensitivity issues. Every file belongs to exactly one package, and package boundaries control file ownership, visibility, and glob scope across the entire repository.

Check your understanding · 3 questions

1.Which BUILD file name is preferred, and why?

Select one answer

2.True or false: how package boundaries and file ownership work.

Choose True or False for each sentence

A subdirectory without a BUILD file has its files owned by the nearest ancestor package.
Adding a BUILD file to a subdirectory splits the parent package and causes glob() in the parent to stop matching that subdirectory.
A source file can belong to two packages at once if both BUILD files reference it.
The root package — the BUILD file at the repository root — has an empty string as its package name.

3.What happens when you add util/BUILD.bazel to a project where //myproject previously owned util/?

Select all that apply

0 of 3 answered

Footnotes

  1. Repositories, workspaces, packages, and targets — Package definition, file ownership, and the directory tree example 1 2 3

  2. BUILD files — BUILD file as the program that defines a package

  3. BUILD files — BUILD vs BUILD.bazel naming and precedence

  4. Bazel Training 101 (Part 9): Packages, Rules, Targets, and Labels — BUILD.bazel preference, package encapsulation, glob behavior 1 2 3 4

  5. Bazel Glossary — Package name as BUILD file path relative to repo root

  6. Labels — Root package label syntax and recommendation to keep it free of source code

  7. Best Practices — Every directory with buildable files should be a package