ct-backends¶
Build backend architecture and selection guide¶
- Author:
- Date:
2026-04-02
- Version:
13.1.4
- Manual section:
7
- Manual group:
developers
DESCRIPTION¶
compiletools supports multiple build backends that generate native build files for different build systems. All backends share the same dependency analysis, magic flag extraction, and header dependency tracking — only the build file generation and execution differ.
Select a backend with:
ct-cake --backend=<name>
Use ct-list-backends to discover which backends are installed and available.
REMOVED BACKENDS¶
slurm — the Slurm build backend (--backend=slurm), which distributed
compilation across an HPC cluster by submitting sbatch jobs, was removed
in 12.0.0. The last release to include the Slurm backend was 11.0.0; pin
compiletools==11.0.0 if you still require it.
Running compiletools inside a Slurm allocation is unaffected: every ct-*
tool still honours Slurm CPU affinity and cgroup limits when choosing default
parallelism. Only the backend that submitted Slurm jobs was removed.
BACKEND SUMMARY¶
Name |
Build file |
Tool |
|---|---|---|
make |
Makefile |
make |
ninja |
build.ninja |
ninja |
cmake |
CMakeLists.txt |
cmake |
bazel |
BUILD.bazel |
bazel |
shake |
.ct-traces.json |
(builtin) |
FEATURE MATRIX¶
Y = supported, N = not supported, D = supported and on by default.
Name |
File-lock |
–build-only-changed |
–realclean |
PCH cache |
Libraries |
Test runner |
CA outputs |
Early cutoff |
|---|---|---|---|---|---|---|---|---|
make |
D |
Y |
Y |
Y |
Y |
Y |
N |
N |
ninja |
Y |
Y |
Y |
Y |
Y |
Y |
N |
Y (restat) |
cmake |
N |
Y |
Y |
Y |
Y |
Y |
N |
N |
bazel |
N |
Y |
Y |
Y |
Y |
Y |
N |
N |
shake |
Y |
Y |
Y |
Y |
Y |
Y |
Y |
Y |
Notes:
File-lock —
--file-lockingenables multi-user object CAS / PCH CAS caches. CMake/Bazel manage their own coordination and skip this layer (see FILE LOCKING below).–build-only-changed — restrict the graph to a whitespace-separated set of source files supplied on the command line. Implemented in the shared base class, so all backends honor it.
–realclean — selectively remove only this build’s products from an object CAS (and
.gchfiles from a PCH CAS), instead ofrm -rfof the whole tree. Inherited from the base backend; the Make backend additionally generates arealcleanrule in the Makefile.PCH cache — content-addressable precompiled header cache via
--cas-pchdir; the compile rules carry the per-cmd_hash.gchpaths, so all backends produce and consume the cache transparently.Libraries — static and dynamic library targets via
--static/--dynamic. All backends support both.Test runner — test execution is part of the build graph: each test runs as soon as its executable is linked, in parallel with ongoing compiles, links, and other tests.
--serialise-testsforces one-at-a-time execution (graph chaining for make/ninja/shake,--local_test_jobs=1for bazel, custom-commandDEPENDSfor cmake). See ct-cake(1) Test execution scheduling.CA outputs / Early cutoff — content-addressable filenames and build skipping when an output’s bytes are unchanged. Native to the Shake builtin engine; Ninja approximates early cutoff via
restat = 1.
BACKENDS¶
make¶
Generates a non-recursive Makefile following Peter Miller’s Recursive Make Considered Harmful design.
When to use: Widely available, well-understood
output, good diagnostic support via --trace (GNU Make 4.0+).
Features:
File locking enabled by default for multi-user shared caches
.DELETE_ON_ERRORto clean partial outputs on failureImplicit rules disabled (
-rR) for predictable behaviourOutput synchronisation per target (
--output-sync=target, Make 4.0+)--shufflesupport (Make 4.4+) to detect missing dependencies in CISelective build via
--build-only-changedWraps compile and link commands with
ct-lock-helperwhen file locking is enabled
Requires: GNU Make (make in PATH).
ninja¶
Generates a build.ninja file targeting the Ninja build system.
When to use: Large incremental rebuilds where Ninja’s minimal overhead outperforms Make. Ninja re-evaluates the build graph faster than Make on projects with many targets.
Features:
Depfile tracking via
-MMD -MFand Ninja’sdeps = gccintegrationrestat = 1on rules to support early cutoff when outputs are unchangedFile locking via
ct-lock-helperwrappers (optional)Selective build via
--build-only-changed
Requires: Ninja (ninja in PATH).
cmake¶
Generates a CMakeLists.txt and builds via CMake in an out-of-source
build directory ({objdir}/cmake-build).
When to use: Integration with CMake-based toolchains or IDEs that expect
CMake projects. Useful when downstream consumers need a CMakeLists.txt.
Features:
Aggregates low-level compile/link rules into high-level CMake targets (
add_executable,add_library)Separates flags into
target_include_directories,target_compile_options,target_link_directories,target_link_libraries, andtarget_link_optionsAutomatically detects C, C++, or both languages
Passes user-configured
CCandCXXto CMakeEnforces out-of-source builds
Requires: CMake 3.15+ (cmake in PATH).
Limitations: File locking is disabled — CMake manages its own coordination.
bazel¶
Generates BUILD.bazel, WORKSPACE, and MODULE.bazel files
targeting Bazel.
When to use: Projects that need Bazel’s hermeticity, remote caching, or remote execution capabilities.
Features:
Aggregates rules into
cc_binaryandcc_librarytargets using@rules_ccStrips
-Ipaths (Bazel manages includes internally)Resolves relative
-Lpaths to absolute for Bazel’s sandboxSources outside the workspace are copied to
ext/Outputs in
bazel-bin/are copied to the final destination
Requires: Bazel or Bazelisk (bazel or bazelisk in PATH).
Limitations: File locking is disabled — Bazel manages its own
sandboxing and caching. //#GIT= externals cloned to the sibling
default (../<name>, outside the gitroot) are invisible to Bazel’s
hermetic sandbox and the build fails to resolve their headers; pass
--externals-dir pointing inside the workspace instead (see
ct-fetch(1), “Externals directory”). -isystem paths outside the
workspace are rejected for the same reason.
shake (builtin, default)¶
A self-executing backend implementing the Build Systems a la Carte (Mokhov, Mitchell, Jones 2018) rebuild strategy with verifying traces and early cutoff. No external build tool is required.
When to use: Best rebuild precision. Ideal for large projects where avoiding unnecessary rebuilds matters most, and for shared caches on network filesystems where content-addressable outputs prevent stale reuse.
Features:
Content-addressable outputs: compile rules produce files whose names encode source hash, dependency hash, and macro state — existence implies correctness, skipping expensive content checks
Verifying traces: records content hashes of all inputs, outputs, and commands in
.ct-traces.json; on rebuild, verifies all input hashes before re-executingEarly cutoff: if a rebuilt output is byte-identical to the previous version, dependents are not rebuilt
Async execution with
asyncio.Semaphorelimiting concurrencyFile locking via
FileLockfor multi-user shared cachesAtomic output creation via temp file + rename
Requires: Nothing — builtin to compiletools.
FILE LOCKING¶
File locking enables multiple users and build hosts to share compiled object files safely. It is supported by the Make, Ninja, and Shake backends.
Enable with --file-locking (on by default for Make). The locking
strategy is auto-detected based on the target filesystem:
lockdir — NFS, Lustre (mkdir-based, works everywhere)
fcntl — GPFS (kernel-managed cross-node locks)
cifs — CIFS/SMB (exclusive file creation)
flock — local filesystems like ext4, xfs, btrfs
See ct-lock-helper (1) and ct-cleanup-locks (1) for details.
CONTENT-ADDRESSABLE OUTPUTS¶
The Shake backend uses content-addressable object file naming:
basename_filehash_dephash_macrohash.o
Because the filename encodes all inputs (source content, dependency hashes, and macro state), if the file exists it is guaranteed correct. This eliminates the need for content verification on cache hits and prevents stale reuse on low-resolution filesystems.
All backends additionally cache the output of the link step in
cas-exedir (executables, .a static libraries, .so shared
libraries). The link rule writes directly to
<cas-exedir>/<linkkey[:2]>/<basename>_<linkkey>.<ext>; a downstream
symlink rule then publishes the user-facing bin/<variant>/<name>
(or bin/<variant>/lib<name>.{a,so}) as a hard link via
ct-cas-publish, with a symlink fallback only on EXDEV. The
linker-artefact key folds in linker identity, canonicalized LDFLAGS,
sorted gitroot-canonical object paths, the ar binary identity,
SOURCE_DATE_EPOCH / LD_LIBRARY_PATH / LIBRARY_PATH /
LD_PRELOAD, and (for executables) the canonical bindir path. See
the Linker-artefact caching (cas-exedir) section in ct-cake(1).
MTIME VS CAS REBUILD MODE¶
The --use-mtime boolean controls whether the generated build files
preserve classical Make/Ninja mtime-based prerequisite semantics on top
of the CAS layer. Default is --no-use-mtime (equivalently
--use-mtime=False): compile, link, ar, and link-shared rules drop
their sources/objects from prerequisites; only PCH/BMI artefacts remain
as order-only deps so build ordering is preserved. Existence of the
CAS artefact on disk is the sole rebuild signal. This mode is the
recommended setting for shared CI caches because a fresh git
checkout (every source has mtime=now) hits the cache instead of
re-running the producer.
Pass --use-mtime=True to restore legacy mtime semantics for
interactive workflows where re-touching a source should force a
rebuild even when the producer key would not change. The flag is
registered in apptools.add_output_directory_arguments so every
backend’s parser accepts it, but only make and ninja honor it:
their rule emitters consume the prereq list as a literal mtime
comparison. cmake, bazel, and shake use their own change detection
and cannot deliver “touch to force rebuild” semantics, so
--use-mtime=True against one of those (shake is the default
backend) is a hard ValueError at backend construction rather
than a silently ignored no-op — pair it with an explicit
--backend=make or --backend=ninja.
Caveats of CAS-only mode:
Generated headers must exist before
ct-cakeruns its headerdep pass; absent headers are dropped fromdep_hashand only force a recompile on the next invocation.make -tandninja -t restatare inappropriate — they create empty files at target paths whose recipes have no real prerequisites in CAS-only mode, corrupting the cache. Use--use-mtime=Trueif you need to mark a build up-to-date without running it.System /
/usr/includeheaders are not in the cache key (ccache and sccache likewise exclude default system headers). An in-place compiler swap usually changescompiler_identityand invalidates the cache implicitly.The
CPATH/C_INCLUDE_PATH/CPLUS_INCLUDE_PATH/OBJC_INCLUDE_PATHvalues ARE in the compile cache key (ccache hashes the same four, though verbatim — compiletools first canonicalizes each path entry so a gitroot-prefixed entry hashes identically across checkouts), and headers found via those directories are walked and content-hashed intodep_hashlike any-Iheader. One residual gap: the header walk keeps a single flat search list for C and C++ alike, so a basename present in both aC_INCLUDE_PATHdir and aCPLUS_INCLUDE_PATHdir with differing content may hash the copy the compiler does not use.
CHOOSING A BACKEND¶
Scenario |
Recommended backend |
|---|---|
General-purpose builds |
|
Large incremental rebuilds |
|
Shared multi-user caches |
|
CMake IDE integration |
|
Bazel ecosystem integration |
|
Maximum rebuild precision |
|
EXAMPLES¶
Build with Ninja backend:
ct-cake --backend=ninja
Build with Shake backend and file locking:
ct-cake --backend=shake --file-locking
List available backends:
ct-list-backends
ct-list-backends --all --style=pretty
REFERENCES¶
Andrey Mokhov, Neil Mitchell, Simon Peyton Jones. Build Systems a la Carte. Proc. ACM Program. Lang., Vol. 2, ICFP, Article 79, September 2018. https://doi.org/10.1145/3236774
Peter Miller. Recursive Make Considered Harmful. 2008. https://api.semanticscholar.org/CorpusID:54117644
SEE ALSO¶
ct-cake (1), ct-list-backends (1), ct-lock-helper (1),
ct-cleanup-locks (1)