kache 0.23.0

Zero-copy, content-addressed build cache for Rust, C/C++ and more, with S3 and shared-filesystem remotes.
---
title: C/C++ caching
description: Cache supported GCC, Clang, and clang-cl object compilations
---

Kache caches conservative, single-source C and C++ object compilations. If it cannot prove that an invocation is safe to cache, it runs the real compiler unchanged.

## Cargo build scripts

`kache init` covers both rustc and the objects a build script compiles through the `cc` crate. Without it, put a shim on `PATH`.

`kache init` sets `HOST_CC` and `HOST_CXX` in Cargo's config (and `CC_KNOWN_WRAPPER_CUSTOM=kache`). It does not set `CC` or `CXX`, so `cargo build --target` keeps the cross compiler the `cc` crate would have found.

PATH shims also cover Cargo's C bits when the crate invokes `cc` or `gcc` from `PATH` without `HOST_CC`.

## Make, CMake, autotools, PKGBUILD

Run `kache init` to enable C/C++ caching for terminal builds on Unix. It creates compiler links and offers to save the PATH setup for zsh, bash, or fish. It shows the files before changing them and backs up existing files.

```bash
kache init
```

Open a new terminal after setup, or run the activation command printed by init in the current terminal. Then use Make, CMake, or your existing build command. Compiler calls made through `PATH` go through Kache; explicit compiler paths are unchanged. Previously configured CMake build directories may need reconfiguration to select the compiler links.

Use `kache init --no-shell` to leave terminal setup alone while configuring Cargo. For managed dotfiles or another shell, use the manual setup:

```bash
kache install-shims
export PATH="$HOME/.local/lib/kache/shims:$PATH"
```

For `makepkg`, you can set the path directly:

```bash
# ~/.makepkg.conf
PATH="$HOME/.local/lib/kache/shims:$PATH"
```

The APT and AUR packages also install `/usr/lib/kache` with the same symlinks:

```bash
export PATH="/usr/lib/kache:$PATH"
```

Nix packages include the same symlinks in `${kache}/shims`, also available at `${kache}/lib/kache`. Add that directory to `PATH` using the [Nix configuration example](/docs/getting-started/installation#nix); no `kache install-shims` step is needed.

`kache install-shims` creates `cc`, `c++`, `gcc`, `g++`, `clang`, and `clang++`. Keep the real compilers later in `PATH`; Kache skips any entry that resolves back to itself.

Versioned and target-prefixed names (`gcc-13`, `x86_64-pc-linux-gnu-gcc`) work if you create them by hand or scan `PATH`:

```bash
kache install-shims --from-path
kache install-shims --force ~/.local/lib/kache/shims
```

Omitting the directory uses `~/.local/lib/kache/shims`. `--from-path` adds a symlink for every compiler-shaped name already on `PATH` that is not Kache itself.

This is a native symlink to the `kache` binary. Do not wrap Kache in a shell script; that would spawn a shell per compile.

Kache is a larger process than ccache. On a tree of many tiny `.c` files, measure both before replacing ccache.

`kache doctor` checks the current terminal's PATH. If init saved shell setup but doctor still reports inactive shims, open a new terminal or run the activation command. Init cannot change its parent shell's environment.

If Homebrew shims were created by an older Kache version, run `kache init` once after upgrading. It replaces Kache-owned links to a versioned Cellar keg with links through Homebrew's `opt` path, which stays valid across later formula upgrades. Other files in the shim directory are left alone.

## Prefix method

For a single project that honors `CC` and `CXX`:

```bash
export CC="kache cc"
export CXX="kache c++"
export CC_KNOWN_WRAPPER_CUSTOM=kache
```

The `cc` crate needs `CC_KNOWN_WRAPPER_CUSTOM` so it keeps the real compiler argument. PATH shims do not need that variable, because the compiler name stays `gcc`.

For clang-cl driver mode:

```powershell
$env:CC = "kache clang-cl"
```

Windows has no `install-shims` generator yet. Use `CC`/`CXX`, or copy `kache.exe` to `gcc.exe` and put that directory first on `PATH`.

## Cached invocations

Supported shapes compile one source into one object:

```bash
cc -c src/foo.c -o build/foo.o
c++ -c src/foo.cpp -o build/foo.o
clang-cl /c foo.c /Fofoo.obj
```

The key includes the content of the source and of every header the compile reads, the compiler identity, modeled code-generation options, and normalized paths. Header changes therefore invalidate the entry.

Kache learns the read set without a separate preprocessor run. The first time it sees an invocation it compiles right away, asking GCC or Clang for a dependency file alongside the object (a caller's own `-MD` file serves when present), then fingerprints the files that compile read and keys from their digest. Peers compiling the same unit in other build directories wait for that record instead of compiling too. When a record exists but a file in it changed, Kache runs the preprocessor to rediscover the read set; the key it derives is the same one a compile would, so an entry left by an earlier state of the tree is still found. A read set that includes a file the assembler opens on its own, or a file modified while the build ran, is compiled and not cached. `KACHE_DEFERRED_DISCOVERY=0` keeps the preprocessor as the discovery on every miss.

Kache recognizes GCC, Clang, Apple Clang, clang-cl, versioned names such as `gcc-13`, and target-prefixed compiler names. It can probe other wrapper names to identify the underlying compiler family.

## Preprocessor memos

Kache remembers each unit's source and header inputs so an unchanged build can derive its key without running the compiler. Identical inputs share one database row, including the local file stamp and the raw and prefix-mapped content hashes. A hit checks the stamp first, then raw content, then mapped content.

A memo names Cargo build directories by role rather than by path: the build script's `OUT_DIR`, the target directory, and the `OUT_DIR` of each crate the unit includes from. Build directories and Cargo commands that give a dependency a different metadata hash, such as `cargo check` and `cargo test`, share one memo per unit.

GC removes memos unused for 30 days and then removes inputs with no references. Successful hits refresh usage at most once a day per memo.

Upgrading from the JSON memo format discards those memos once; units rediscover their inputs on the next build. Compiled artifact entries remain available. SQLite reuses the freed pages. `kache doctor --repair` also compacts a large, sparse index when enough disk space is available; run it after builds finish to avoid lock contention.

## Portability

GCC and Clang entries can be reused across worktrees when source and build paths normalize cleanly. Set `KACHE_BASE_DIR` when the automatic roots do not cover a container mount or out-of-tree layout:

```bash
KACHE_BASE_DIR="$PWD" cmake --build build
```

A configured remote publishes GCC and Clang objects the same way it publishes Rust entries. clang-cl debug objects stay on the machine because CodeView records paths that clang-cl does not remap.

## Passthroughs

Kache currently passes through:

- link and whole-program steps, unless `KACHE_CACHE_CC_LINKS` or `[cache] cache_cc_links` is on
- multi-source or multi-architecture invocations
- response files
- precompiled headers and modules
- coverage and split-DWARF builds
- assembly, including inline `asm` in C, that reads another file with `.incbin` or `.include`, or that uses assembler macros and repetitions (`.macro`, `.irp`, `.irpc`, `.rept`, `.altmacro`) able to generate those directives: the assembler reads the file after preprocessing, so the key cannot see it. Inline `asm` whose text is a computed expression rather than a string literal is passed through for the same reason. These compiles run the compiler directly, skipping a configured `KACHE_FALLBACK` wrapper, because a wrapper that keys on preprocessor output cannot see the file either.
- flags it has not classified

Inspect passthrough reasons in the monitor or logs:

```bash
kache monitor
KACHE_LOG=kache=debug make
```

You can opt a known-safe flag into the local model:

```toml title=".kache.toml"
[cc]
extra_allowlist_flags = ["-ffunction-sections"]
```

Kache folds the spelling into the key. Do not allow flags whose meaning depends on the host, such as `-march=native`.