mitts/build

Rust first · reads your existing Cargo.toml

Build work,
accounted for.

Mitts plans a Cargo workspace itself and calls rustc directly. Every compile action is keyed by its full set of inputs, stored once in a machine-wide immutable store, and restored into every workspace that needs it — across projects, worktrees and branches. If an input can't be accounted for, the action is rebuilt, not guessed.

action / rlibinputs tracked
  1. srcsource set · Cargo.toml
  2. depsdependency action keys
  3. toolrustc · target triple
  4. cfgfeatures · profile · flags
  5. envtracked environment
action keysha256(inputs)
shared storecas/ab/3f…immutable
workspacetarget/mitts/isolated
01

The same crates, compiled again and again.

Cargo's cache lives in each workspace's target/. Anything that creates a new one starts from zero.

worktrees

A second checkout

A new clone or git worktree of the same repository rebuilds every dependency.

branches

Switching back

A branch that changes Cargo.lock evicts work you'll need again after the switch back.

projects

Shared dependencies

Two projects using the same serde, syn or tokio build and store them twice.

agents

Parallel work

Coding agents in separate worktrees each pay the full build, at the same time.

  • A shared CARGO_TARGET_DIR serializes builds behind one lock, and feature sets overwrite each other.
  • A compiler wrapper such as sccache sees one rustc call at a time: keys depend on absolute paths unless normalized, linking crates aren't cached, and Cargo still runs every build script.

Mitts replaces Cargo's executor, not the compiler. It sees the whole unit graph — so it can key each unit by its dependencies, cache linking units and build-script runs, and let builds share one store safely.

02

Reuse follows the inputs.

Five phases, from your manifests to restored outputs. A cache hit counts only when the effective inputs match exactly.

  1. resolveReuse Cargo.lock, or resolve against the crates.io sparse index.
  2. fetchDownload .crate archives in parallel, verify, unpack once per machine.
  3. planUnits — lib, bin, test, build script, proc macro — with features and edges, by Cargo's resolver rules.
  4. keySHA-256 over toolchain, target, profile, flags, features, sources, dependency keys and environment.
  5. executeParallel DAG of direct rustc calls, pipelined on .rmeta. Hit: verify and restore. Miss: lock the key, compile, publish atomically.
keys

Keys chain down the graph

A unit's key includes its dependencies' keys, not their bytes. A change anywhere below a unit changes its key.

build scripts

Scripts are cached too

Their directives enter the key of the unit they configure; their rerun-if-* declarations decide when a cached run is reused.

fast path

Nothing changed, nothing done

An unchanged workspace skips planning and keying. Any mismatch falls back to the full keyed build.

03

A neutral core, language adapters on top.

The store, action cache, locks and GC know nothing about Cargo. The Rust adapter turns a Cargo workspace into keyed actions; other languages plug into the same core.

04

What invalidates the cache.

Every input below must match for a hit. If Mitts can't see an input, it does not reuse the result.

InputTracked as
Toolchainrustc -vV output
Target & profiletarget triple, profile settings, RUSTFLAGS and Cargo config rustflags
Featuresthe unit's resolved feature set, resolver 1 or 2 as Cargo would
Sourcescontent hashes of package files and every file rustc reports reading
Dependenciesaction keys of all dependency units
EnvironmentCARGO_*, RUST_*, wrappers, and every variable read through env! / option_env!, with <unset> recorded
Cargo configurationthe .cargo/config(.toml) files Cargo would read
Build scriptsdirectives, OUT_DIR, rerun-if-changed contents and rerun-if-env-changed values
05

Where it sits.

How each tool reuses work. A qualitative comparison, not a performance claim.

CargoCargo + sccacheMitts
Reads Cargo.toml / Cargo.lock as isyesyesyes
Reuse across workspaces and worktreesno, per target/yes, per rustc callyes, per unit
Key depends on the checkout path—yes, unless normalizedno for registry crates; workspace crates when not eligible
Caches bin, test and proc-macro units—noyes
Caches build-script runsnonoyes
Concurrent builds of one dependencywait on the target/ lockboth may compile on a missone compiles, the other reuses
Remote / team cachenoyesplanned
Windowsyesyesno

Bazel and Buck2 solve a larger problem — hermetic, multi-language builds with remote execution — but need their own build files instead of Cargo.toml.

06

A deliberate cache boundary.

Mitts caches the actions it can describe completely. Everything else builds normally, and mitts explain says why.

  • Library, binary, test and proc-macro units with matching keys
  • Build scripts, re-run only when a declared rerun-if input changes
  • Concurrent builds compile each shared unit once
  • Your own crates across checkouts, when their key can be made path-independent
  • cdylib, dylib, staticlib crates and untrackable env! reads
  • Inputs a build script reads without declaring them
  • Windows, cross-compilation and a remote cache — not yet

Unknown inputs are never treated as unchanged. If Mitts can't prove an action is compatible, it builds it — and unsupported Cargo features fail loudly instead of guessing.

07

A small CLI.

No new manifest and no second lockfile. Build from source, then point it at a Cargo workspace. Cargo and rust-analyzer keep working next to it.

$ cargo install --path .   # from a clone of the repository
$ cd your-workspace
$ mitts build
mitts build
Compile the package and its dependencies into target/mitts/
mitts run
Build and run a binary of the local package
mitts check · clippy · fmt
Type-check, lint and format without producing binaries
mitts test · coverage
Unit, integration and doc tests with Cargo-style target selection; LLVM source-based coverage
mitts explain
The build plan, every unit, and why it is cached or not
mitts fetch
Prefetch dependencies into the global source store
mitts gc
Prune old or unreferenced objects within storage quotas
mitts clean
Remove this workspace's Mitts artifacts
08

Where it stands.

Pre-release. Milestones M0–M6 are complete; hardening for real projects and a reproducible benchmark study are in progress. No performance numbers are published until that study is — with fixed hardware, cold, warm and changed-input runs, and the stronger of Cargo and sccache as the baseline.

  1. M1Uncached build correctness
  2. M2Content-addressed store & action cache
  3. M3Parallel fetcher & lockfile resolution
  4. M4Native parallel DAG runner
  5. M5Pilot, deduplication & conservative GC
  6. M6Second adapter: C/C++
  7. M7Hardening for real projects
  8. M8Reproducible benchmarks
  9. M9Remote & team cache
author

Alexander Panasenko

prod.codes/aboutalex@prod.codes