2.3.1 Hermeticity
Here, input hermeticity means that the inputs Bazel records are the inputs the action really uses: declared source files, declared tools, and the configuration that selects them. That expands the one-line teaser from P.2.4 Five Properties into the property Bazel needs for fast builds to stay trustworthy. If something outside that recorded set can influence the output, Bazel is no longer reusing a proven result when it hits the cache. It is reusing a guess.1,2,3
Hermetic means "the recorded inputs are the real inputs"
Hermeticity breaks into two pieces: isolation from the host machine and stable identity of the inputs themselves.1 In practice, that means Bazel should not accidentally depend on whichever compiler, interpreter, system library, or helper binary happens to be installed on the machine that ran the build. Tools are part of the build input just as much as source files are.1,4
That fits the artifact-based model from P.2.1 Task-Based vs Artifact-Based and the action-planning model from 2.2.3 Static Action Graph. Bazel can only reason correctly about an action if the action's important inputs are explicit before execution starts.2,5 If a rule finds a compiler through $PATH, reads a system library from the host, or relies on a machine-specific helper discovered at runtime, two machines may appear to be building the same target while actually running different builds.4,5
Why this is really about cache correctness
Hermeticity matters because Bazel's cache only tracks what Bazel knows. A remote cache entry is safe to reuse only when the same action with the same inputs would produce the same output on any machine.3 If an action secretly reads an undeclared file, Bazel has no reason to invalidate the cached result when that file changes. The build may stay fast, but the answer can be wrong.3,6
That is why hidden inputs are more dangerous than ordinary cache misses. A cache miss costs time. A false cache hit spreads a bad result. The consequence is direct: once an incorrect cache entry reaches a shared remote cache, every developer can start reusing it.6 The mechanics of the key itself come next in 2.4.3 What Makes a Cache Hit, but the trust model starts here: Bazel's recorded inputs must match reality.
Hidden inputs are broader than source files
The most common leak paths are the host environment, not the repository:
- Tool discovery through
$PATH,JAVA_HOME, system compilers, or machine-specific libraries instead of toolchains or declared inputs.4,5 - Inherited environment variables or workspace-status data that makes output depend on the machine, user, or current time.7
- Repository and workspace rules that inspect the host with operations such as
execute,which,os, or host-based symlinks, then bake those results into later build steps.8
Concrete C++ examples are the declared generated and runtime inputs in L2.1.7 Generated Sources, Data & Runfiles and the framework, production, and fixture dependencies in L2.2.2 Testing C++ with Bazel: GoogleTest, Catch2 & Boost.Test.
This is why "works on my machine" is a hermeticity smell, not just an onboarding annoyance. It usually means some effective input was never modeled. Bazel can often mask that for a while on one machine, then expose it the moment the build runs elsewhere or the cache is shared more broadly.3,4
Enforcement is the next topic, not the whole topic
As 2.2.1 Loading, Analysis & Execution set up, enforcement happens when actions actually run. Bazel usually catches undeclared file reads by running actions under sandboxing, which is why 2.3.2 Sandboxing comes immediately after this article.6 But sandboxing is an enforcement mechanism, not the definition of hermeticity. The principle starts earlier: use explicit tools, explicit dependencies, and configuration that does not quietly inherit the host.1,5,8
It also does not solve every way two builds can diverge. Timestamps, randomization, and similar sources of output variation belong in 2.3.3 Non-Determinism Sources, while flags and environment-shaping defaults show up later in 3.2.6 Hermeticity Settings.7
Repository rules can break hermeticity before any compile starts
Hermeticity problems are not limited to normal execution-phase actions. Repository rules run on the host while Bazel is resolving external dependencies, and they are allowed to do things ordinary build actions are not. That is why execute, download without a hash, os, which, and host-dependent symlinks are all red flags in a workspace audit.8
You do not need that machinery to use Bazel at Level 2, but it explains why hermeticity shows up again later in 6.3 Remote Execution Infrastructure and 4.9 Repository Rules. The principle is the same even when the mechanism changes: if host facts can leak into the build without being modeled, correctness becomes conditional on where the build happened.5,8
Input hermeticity means Bazel's declared world matches the real world. Once source files, tools, and configuration are explicit, Bazel can identify equivalent actions. 2.3.3 Non-Determinism Sources covers the separate requirement that equivalent executions produce stable bytes. If a build still depends on the host by accident, a cache hit can be fast for the wrong reason.1,3,6
Check your understanding · 3 questions
1.Why is a false cache hit more dangerous than a cache miss in a hermetic build system?
Select one answer
2.Which of the following are common hidden-input leak paths that break hermeticity?
Select all that apply
3.True or false: the scope and limits of hermeticity.
Choose True or False for each sentence
Footnotes
-
Hermeticity — definition, isolation, source identity, and why tools and dependencies must not come from the host by accident ↩1 ↩2 ↩3 ↩4 ↩5
-
Software Engineering at Google — Ch.18: Build Systems and Build Philosophy — artifact-based builds reuse outputs only when the system knows what those outputs depend on, and undeclared or nondeterministic behavior defeats that guarantee ↩1 ↩2
-
Distributed Builds — remote caching and remote execution require self-describing environments and the same inputs producing the same outputs on any machine ↩1 ↩2 ↩3 ↩4 ↩5
-
Implicit Dependencies in Build Systems — system compilers and libraries are hidden inputs that break reproducibility and cacheability unless made explicit ↩1 ↩2 ↩3 ↩4
-
Adapting Bazel Rules for Remote Execution — use toolchains instead of
PATHorJAVA_HOME, and avoid implicit dependencies that only happen to work in one execution environment ↩1 ↩2 ↩3 ↩4 ↩5 -
Sandboxing — undeclared inputs can produce incorrect incremental builds and poison shared remote cache entries, so Bazel uses sandboxing to catch them ↩1 ↩2 ↩3 ↩4
-
How to keep a Bazel project hermetic? — environment variables, workspace status, and timestamps or archive metadata as common hermeticity leaks ↩1 ↩2
-
Finding Non-Hermetic Behavior in WORKSPACE Rules — repository rules can leak host-machine facts through
execute,which,os,symlink, and similar operations ↩1 ↩2 ↩3 ↩4