ccache in Cross Builds: Check the Inputs That Must Invalidate Cached Objects

A useful compiler cache reuses an object only when the relevant compilation inputs still describe the same result. Before increasing hit rate, write down how compiler identity, generated headers, options and paths enter that decision. An unexplained cache hit is not evidence of reproducibility, and an explained miss can be the correct outcome.

This procedure pins ccache 4.10.2. Its primary manual and configuration semantics were inspected, but ccache was not available in the execution environment. The cache invalidation matrix below is proposed; no hit rate, timing improvement or cached build result is claimed.

Start with an observable baseline

Create a dedicated cache directory for a disposable build experiment rather than clearing a shared team cache. Record the ccache version, effective configuration, compiler path, full compile command, source hash and output hash. Keep the direct compiler invocation available as the reference result.

Choose a tiny translation unit that includes one owned generated header and exposes the header’s value in an inspectable function. Make the generator deterministic and complete it before compilation starts. A cache test cannot compensate for a generator racing the compiler.

Run the first and identical second compilations with diagnostic logging enabled for the experiment. Preserve statistics before and after, but also retain the per-invocation explanation. A global counter change can include unrelated compilations if the cache is shared.

Compiler and source identities feed controlled invalidation cases whose cached results must match direct compilation, with safeguards and path-output checks called out.
Original ccache qualification matrix, explicitly proposed rather than measured.

Make compiler identity a reviewed input

The ccache 4.10.2 manual documents a default compiler check based on modification time and size, with alternatives including content hashing and a command-derived identity. If ccache sees another wrapper instead of the real compiler, the identity being checked can be weaker than intended.

Put the actual compiler and wrapper chain in the input ledger. Ask which event changes that identity when the cross-compiler is upgraded. A wrapper script that remains byte-identical while selecting a different SDK deserves an explicit test.

For a proposed content-identity experiment, use two owned compiler-wrapper fixtures with controlled identities or two approved toolchain copies. Change only the selected identity input, then inspect the cache explanation and compare output with a direct compilation. Do not modify a shared installed compiler merely to manufacture a cache miss.

Test included headers and newly appearing headers separately

In direct mode, ccache records included-file paths and hashes in a manifest; preprocessor mode uses preprocessed output. The manual identifies a direct-mode caveat for headers that did not exist previously but would change include selection if created later.

First change the contents of an existing generated header while holding the source file fixed. Recompile and require the resulting object to reflect the new value. A semantic or disassembly check is useful alongside a byte comparison, because unrelated debug metadata can also change object bytes.

Then construct a separate include-search experiment: begin with a header selected from a later include directory, and introduce a same-named header in an earlier directory. Compare cached and uncached results. This deliberately tests the selection boundary rather than repeating a content-change test on a file already present in the manifest.

Keep timestamp safeguards intact during diagnosis

Freshly created or modified inputs can trigger ccache’s race-avoidance behavior. A slower path in that situation may be intentional. Preserve the log and actual file timing before deciding that the cache is malfunctioning.

Do not enable broad sloppiness settings simply to force a green hit-rate graph. Such options change which input differences are trusted or ignored. If the product build needs one, document the specific invariant that makes it acceptable and add a negative test for violating that invariant.

Test the normal generator-to-compiler dependency first. If the generated header can change during compilation, fix the build ordering. Caching an unstable input sequence more aggressively cannot make the compilation contract reliable.

Evaluate path normalization as an output change

The documented base_dir mechanism rewrites selected absolute paths relative to the working directory, and the manual warns about dependency-file effects. Treat path normalization as a change in the compiler invocation and its outputs, not just a cache-key cosmetic operation.

Build the same owned source tree from two locations. Inspect debug paths, diagnostics, dependency files and any __FILE__-derived application text. Decide which differences are permitted by the product before declaring the outputs interchangeable.

A hit across directories can be useful, but an incorrect dependency file can later prevent recompilation. Verify that the upstream build system still discovers changed headers after relocation. Keep that dependency test separate from the object-code comparison.

Use a one-input-at-a-time matrix

Controlled change Required observation
No change Explain reuse or the specific reason reuse was unavailable.
Existing generated header content Result agrees with a fresh uncached compilation.
Earlier include-search candidate appears Selected header and output remain correct.
Compiler identity or wrapper selection Cache policy recognizes the supported toolchain change.
ABI-affecting definition Consumer-visible layout or code follows the new definition.
Build directory changes Paths and dependency outputs satisfy the intended relocation contract.

For each row, retain the exact changed input, diagnostic log, direct-compiler reference and cached result. Do not require every row to produce the same counter category; direct and preprocessor paths can legitimately differ while remaining correct.

Close the cache review with bounded claims

If a mismatch appears, preserve the cache entry and inputs before rebuilding through an uncached path. Classify compiler identity, include selection, unsupported options and concurrent modification separately. Repeatedly wiping the cache can remove the symptom while losing the evidence needed to fix the policy.

The deliverable is a cache-input assumption ledger backed by controlled comparisons. It should identify what invalidates results, what path normalization changes and which configurations remain untested. Only after correctness is established should the team measure performance on its real workload; this article reports no such measurement.

Similar Posts