Skip to content

compile-cache-testclient

A standalone client and validation harness for the compile cache. This is test infrastructure, not a shipped product: no install() rule names it, no package carries it, it is not built by default, and it is not supported for general use. It is documented here for contributors working on the canonicalization contract. If you are looking for something to run, you want fastcache-cc or fastcache-compile-node.

It is the reference localizer. It links the same PathCanon / CompileValue code the server uses, so the canonicalization parity contract is exercised rather than reimplemented — if the client and server ever disagree about how a path is canonicalized, this is what catches it.

Building

Not built by default. It is built by the linux and clang-tidy CI jobs, though, so that it cannot quietly stop compiling — which is exactly what happened before (issue #84).

cmake --preset clang-debug -DFASTCACHED_BUILD_TESTCLIENT=ON
cmake --build --preset clang-debug --target compile-cache-testclient

Usage

compile-cache-testclient <store|fetch|drop> --port N [--key K] [--prefetch-group C]
    --srcroot P --buildtree Q [--compiler cl|clang-cl] [--source F] [--out OBJ]

Run compile-cache-testclient --help for the generated reference; it is rendered from the same option table the parser matches against, so it cannot fall out of step with what the tool accepts.

Options take their value either as the next argument or joined with =, the same as the daemon's own parser. An unrecognised option is an error rather than something silently ignored.

Flag Default
--host 127.0.0.1
--port none — required
--key empty
--prefetch-group default
--srcroot, --buildtree empty
--compiler cl on Windows, cc elsewhere
--source empty
--out store: a temp path; fetch: skip writing when empty

store

Compiles the source, captures the object file and the compiler's dependency record, frames them as a CompileValue, and STOREs it through a running fastcached — carrying the producer's source-root and build-tree layout.

Both driver families work, and the difference is not cosmetic: an MSVC driver is asked for /showIncludes and reports on its own output, while a GNU driver is asked for -MD -MF and writes a depfile. The stored region is tagged with whichever grammar produced it (PathCanon knows both), so a value stored by either can be localized by either. The family is chosen from the --compiler name, not from the host — clang-cl takes MSVC spellings on Linux too.

fetch

FETCHes the canonical value, localizes every path to the consumer's layout, optionally writes the object out, and verifies that each localized dependency path resolves on disk. A path that does not resolve is a failure.

Which paths are checked comes from the launcher's own Cc::ReplayedDependencyPaths, so this tool asks exactly the question a real cache hit asks. That matters in both directions: it understands a depfile as well as /showIncludes (a scanner that knew only the latter reported includePathsChecked=0 on POSIX and validated nothing while still exiting 0), and it does not report a toolchain path outside both roots, or a canonical token the region walker declined to localize, as missing — neither is this build's to have.

drop

Sends one CACHE-DROP for --key: the endpoint removes the key from its own tier and nowhere else. It exists so an end-to-end case can take a stored object away while the launcher's direct-mode manifest that points at it stays — the state an operator creates by removing a wrong object, and one no launcher run can produce on its own.

Exit codes

Code Meaning
0 Success
2 Usage error, socket failure, malformed value, or localization failure
3 The compiler failed during store
4 FETCH miss, or DROP miss — there was nothing to remove, which is an answer rather than a failure
5 FETCH hit, but at least one localized header path did not resolve on disk

Code 5 is the interesting one: it means the cache returned an entry whose dependency paths do not exist on the consuming machine — exactly the corruption the canonicalization layer exists to prevent.

Cross-depth validation

run-crossdepth.ps1 proves the core value-portability guarantee: an object compiled at a deep checkout path (mimicking a CI runner nested deeper) is usable when fetched from a shallow checkout. That is the scenario that poisons a build tool's dependency database when absolute paths from a different checkout depth are replayed verbatim.

# Synthetic: generated source, no project files involved.
pwsh src/apps/compile-cache-testclient/run-crossdepth.ps1 -Synthetic

# Rooted at real checkouts. Validates the real per-machine paths WITHOUT
# compiling or reading any project source: a generated tree is laid under a
# throwaway subfolder of each root and removed afterwards.
pwsh src/apps/compile-cache-testclient/run-crossdepth.ps1 `
    -CheckoutRoots "D:\proj","D:\repo\proj"

Privacy rule

This tool and its harness must never print, log, or persist project-private information — no project source, no proprietary header names, no absolute checkout paths — into anything under version control. The harness prints generic status and counts only; its working files live in temp directories and throwaway subfolders that are cleaned up afterwards.