FreeBSD: Ccache quick setup, usage, and caveats

Ccache speeds up recompilation by caching previous compiler outputs and reusing them when the same inputs appear again. Properly configured, it produces identical output to a no‑cache build—the only difference should be speed.

Why use ccache

Faster rebuilds: Recompiling unchanged files becomes near‑instant thanks to cache hits.
Safe by design: Aims to match compiler output exactly, with documented caveats when that’s not possible.
Fits team workflows: Works as a transparent wrapper around compilers; can be enabled per‑project or system‑wide.

Install and basic configuration
Install ccache (pkg or ports):
pkg install ccache

This provides the ccache binary and libexec wrappers for use with system or ports builds.

Set cache directory and PATH (shell env):
export CCACHE_DIR=/var/cache/ccache
export CCACHE_LOGFILE=/var/log/ccache.log
export PATH=/usr/local/libexec/ccache:$PATH

Using the libexec path ensures the compiler calls go through ccache; setting a shared cache dir/log helps with observability.

Now we create a /var/cache/ccache in zfs
# zfs create -o mountpoint=/var/cache/ccache zroot/ccache
# zfs set dedup=on compression=lz4 atime=off recordsize=128K zroot/ccache

Now we set permissions for system-wide builds:

# chown -R root:wheel /var/cache/ccache
# chmod 775 /var/cache/ccache
Initialize ccache:
# ccache -M 10G
Set cache size limit to 10.0 GB

Adjust size based on disk; larger caches yield more hits for big codebases.

System builds and ports: enabling ccache
World/kernel builds

Enable ccache for buildworld/buildkernel: Add to /etc/src.conf:

WITH_CCACHE_BUILD=yes
CCACHE_DIR=/var/cache/ccache

Alternatively, export CC/CXX via wrappers for targeted use:

export CC=/usr/local/libexec/ccache/cc
export CXX=/usr/local/libexec/ccache/c++

Wrapping compilers is the canonical approach; many setups use libexec wrappers for world builds to gain cache hits across iterative rebuilds.

Ports tree builds

Enable ccache in /etc/make.conf:

.if !defined(NO_CCACHE)
CC=/usr/local/libexec/ccache/cc
CXX=/usr/local/libexec/ccache/c++
.endif

# Avoid recursive ccache when building the ccache port itself:
.if ${.CURDIR:M*/ports/devel/ccache}
NO_CCACHE=yes
.endif

This standard pattern enables ccache for ports while skipping the ccache port to avoid weird recursion.

Best practices for FreeBSD setups

Put cache on fast storage: SSD or a dedicated ZFS dataset improves hit latency; pair with an adequate cache size for large trees.
Match compilers and flags: Cache keys include compiler, flags, and environment; consistent toolchains maximize hit rate.
Use logs to tune: CCACHE_LOGFILE helps diagnose misses (e.g., preprocessor differences, path mismatches).
Combine with meta mode (advanced): For kernel/module work, META_MODE reduces redundant rebuilds; ccache then accelerates the remaining compiles. Use both judiciously and expect diminishing returns for tiny modules that already build fast.

Common caveats and troubleshooting

Non‑deterministic inputs: Build steps that embed timestamps, randomized macros, or absolute paths can reduce cache hits.
Compiler masquerade issues: Ensure the PATH or CC/CXX wrappers are consistent across invocations; mismatched paths lead to misses.
Building ccache itself: Exclude ccache from being wrapped during its own build to avoid recursion and logging noise.
Kernel modules: For single‑module development, linking often dominates; ccache helps compile times but won’t speed the link step.

Quick commands cheat sheet

Enable globally (session):

export PATH=/usr/local/libexec/ccache:$PATH
export CCACHE_DIR=/var/cache/ccache
ccache -M 10G
World build with ccache:
make -C /usr/src buildworld buildkernel

Check stats:

ccache -s

If you missed my article on new ports setup, here are the ports after portsnap, git how-to, and zfs tips