Drop-in for ninja
Parses your existing build.ninja unchanged. Generators (CMake, GN, Meson) keep working; symlink rninja in for ninja and your CI scripts don't move. Standard flags (-C, -j, -k, -d, -t) behave the same.
v0.1 · MIT · Rust
rninja is a Rust-powered drop-in replacement for Ninja. Same build.ninja, same flags, plus a content-addressed cache and an async scheduler that keeps every core busy.
// illustrative output. measure your own repo with time ninja target.
Warm incremental
2x–5x
faster wall clock on developer laptops
CI w/ remote cache
2x–5x
fewer compute minutes per pipeline
Cold full build
1.3x–2x
from the scheduler alone, no cache
// ranges quoted from the rninja docs. measure your repo for the real number.
// install
rninja ships on crates.io, npm, PyPI, and Homebrew. Prebuilt binaries are on the GitHub Releases page. Pick whatever your machine already has.
Homebrew
brew install \ neul-labs/tap/rninja
npm
npm install -g \ rninja-cli
PyPI
pip install rninja-cli
Cargo
cargo install rninja
// what you get
Parses your existing build.ninja unchanged. Generators (CMake, GN, Meson) keep working; symlink rninja in for ninja and your CI scripts don't move. Standard flags (-C, -j, -k, -d, -t) behave the same.
Every action is hashed over inputs, command line, and environment. Repeat builds skip the toolchain entirely. The sled-backed index is crash-safe, so an interrupted build doesn't corrupt the store.
The executor keeps cores busy where stock ninja stalls — single-threaded bookkeeping is moved off the hot path and the ready queue accounts for IO contention, not just job count.
Optional remote cache built on async-nng streams blobs between developer laptops and CI runners. First builds on fresh machines become mostly cached builds.
clean, compdb, graph, deps, query all behave like ninja. rninja adds -t config for inspecting or generating an rninja.toml, and a stats view for cache hit rate.
Cache lives under $XDG_CACHE_HOME/rninja. No daemon to install, no service to register. RNINJA_* env vars exist for power users; everyone else gets the speedup for free.
// how it works
01
Read your existing build.ninja with stock ninja semantics. Phony, restat, pools, depfiles all preserved.
02
Fingerprint inputs, command line, and the environment the rule actually observes. blake3 over deduplicated content.
03
Check the local sled index for a matching action; optionally pull the artifact from the remote cache.
04
On miss, run the toolchain through the tokio-driven scheduler. Outputs are tracked, never reordered against ninja semantics.
05
Write the artifact into the content-addressed blob store and update the index. Crash-safe at every step.
// audience
If your inner loop is "tweak one file, wait two minutes," the cache is for you. Most of those rebuilds are recomputing things ninja already produced.
Same commit, same flags, same toolchain — over and over. A shared remote cache turns the second pipeline into a near-no-op.
Different teams build overlapping subgraphs from the same sources. The cache shares results across them automatically.
You already chose ninja because the build planner is fast. rninja gives you back the cores ninja leaves idle plus the toolchain calls you can skip entirely.
// honest comparisons
Two grounded comparisons — the tool we replace and the tool that overlaps from a different angle. Fair rows, picks-when-them sections, no astroturf.
The baseline. rninja parses the same build.ninja and mirrors its flags — the difference is the cache, the scheduler, and the optional remote sharing layer.
A different category: compiler-call cache that wraps cc/rustc. rninja caches whole ninja actions. Sometimes you want both, often one is the right answer.
// FAQ
Yes — that is the design goal. rninja parses the same build.ninja format, accepts the same core flags (-C, -f, -j, -k, -l, -n, -t, -v, -d), preserves NINJA_STATUS, mirrors exit codes, and matches semantics for phony targets, depfiles, and restat. If a build works under ninja, it should work under rninja. Cache-specific options live behind RNINJA_* env vars so they cannot collide with stock ninja.
On warm incremental builds with high cache reuse, 2x to 5x. CI pipelines with a shared remote cache see a similar 2x to 5x reduction in compute minutes after the first job warms the store. Cold full builds on many cores still gain 1.3x to 2x from the smarter scheduler. Numbers assume large C/C++-style repos where compile and link dominate.
Every action gets a content hash over its full inputs (source bytes, includes, generated headers), the command line, and the environment that the rule observes. Identical inputs produce identical hashes, so a hit means a guaranteed-equivalent artifact. Non-deterministic outputs (timestamps, random seeds) reduce hit rate; fixing them pays back immediately.
No. Remote cache is opt-in via RNINJA_REMOTE_URL and RNINJA_CACHE_MODE. async-nng handles the transport, so a single small endpoint can serve multiple machines. You can also leave it disabled and use rninja purely for local caching plus the scheduler.
Caching is strictly additive. A miss falls through to the original ninja action; a hit only happens when inputs, command, and environment all match. The drop-in compatibility tests assert that builds produce identical artifacts and identical dependency tracking versus stock ninja. If anything diverges, that is a bug and -d explain will surface it.
Anything that emits a standard build.ninja — CMake, GN, Meson, and hand-written ninja files all work without modification. The README lists those generators explicitly. rninja does not regenerate the file; it executes it.
brew install neul-labs/tap/rninja, npm install -g rninja-cli, pip install rninja-cli, or cargo install rninja. Prebuilt binaries are also on the GitHub Releases page. From source: cargo install --path . inside a clone.
rninja is published on crates.io, npm, and PyPI, and is MIT licensed. Use the speedup table as your guide: it pays off best on repos that already feel slow under ninja. Run side-by-side (time ninja.orig target vs time rninja target) before flipping it on in CI.
Install rninja, point it at your existing build.ninja, and run a side-by-side build. If the numbers don't move, uninstall it — you've lost ten minutes.