ct-backends¶
Build backend architecture and selection guide¶
- Author:
- Date:
2026-04-02
- Version:
10.0.6
- 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.
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) |
slurm |
.ct-slurm-traces.json |
sbatch |
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 |
slurm |
Y |
Y |
Y |
Y |
Y (local) |
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; the Slurm backend distributes compile rules but always links locally.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/slurm,--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/Slurm builtin engine; Ninja approximates early cutoff via
restat = 1.
BACKENDS¶
make (default)¶
Generates a non-recursive Makefile following Peter Miller’s Recursive Make Considered Harmful design.
When to use: General-purpose default. 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.
shake (builtin)¶
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.
slurm (HPC)¶
Extends the Shake backend to distribute compile rules across an HPC cluster via Slurm job arrays. Link and library rules still run locally.
When to use: Large codebases on HPC clusters where distributing compilation across many nodes dramatically reduces wall-clock time.
Features:
Distributes compile rules via
sbatch --arrayDynamic memory estimation per rule based on include complexity
Memory tiers with automatic OOM retry (doubles memory and resubmits)
Job chunking for large submissions (
--slurm-max-array, default 1000)Polls
sacctfor job completionTraces recorded for successfully compiled outputs (incremental rebuilds across cluster jobs)
Slurm logs written to
<diagnostics-dir>/<invocation-id>/slurm-ct-<chunk>-<task>.out(defaults to<bindir>/diagnostics/<invocation-id>/; cleaned on success)
Requires: Slurm (sbatch in PATH).
Configuration options:
--slurm-partition— Slurm partition name--slurm-account— Slurm account for billing--slurm-time— job time limit--slurm-mem— maximum memory per job (also the OOM retry ceiling)--slurm-cpus— CPUs per task--slurm-poll-interval— seconds betweensacctpolls--slurm-export— comma-separated env vars passed viasbatch --export=. Default propagates a curated allowlist (PATH,HOME,USER,LANG,LC_ALL,CC,CXX,CPATH,LD_LIBRARY_PATH) instead of the submitter’s full environment, so unrelated state does not leak into compute nodes.LD_LIBRARY_PATHis included because non-system compilers (Spack, Lmod, custom installs) typically need it to locate their shared libs.
Overriding the default export list:
--slurm-export=ALL— propagate the full submitter environment (legacy behavior; use sparingly, leaks unrelated state).--slurm-export=NONE— fully isolated environment.For Lmod/Spack sites, extend the default explicitly, e.g.:
--slurm-export=PATH,HOME,USER,LANG,LC_ALL,CC,CXX,CPATH,LD_LIBRARY_PATH,MODULEPATH,LMOD_CMD,LMOD_DIR,SPACK_ROOT
File-locking semantics: For the slurm backend, “file locking” means
(a) local link/library steps go through FileLock + atomic_link on
the submitter, and (b) compute-node compiles use atomic temp+rename
(compile to $OUT.$SLURM_JOB_ID.$SLURM_ARRAY_TASK_ID.tmp, then
mv -f onto the final .o) – the same correctness guarantee that
atomic_compile provides locally, without requiring a cross-node lock
on the compute path.
Limitations: Link rules cannot be distributed and always run locally.
FILE LOCKING¶
File locking enables multiple users and build hosts to share compiled object files safely. It is supported by the Make, Ninja, Shake, and Slurm 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 and Slurm backends use 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 all
backends accept it consistently.
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 (matches ccache/sccache contract). An in-place compiler swap usually changescompiler_identityand invalidates the cache implicitly.
CHOOSING A BACKEND¶
Scenario |
Recommended backend |
|---|---|
General-purpose builds |
|
Large incremental rebuilds |
|
Shared multi-user caches |
|
HPC cluster distribution |
|
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
Distribute compilation across a Slurm cluster:
ct-cake --backend=slurm --slurm-partition=build --slurm-mem=8G
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)