Skip to content
rninja

v0.1 · MIT · Rust

Build faster.
Cache smarter.
Drop-in ready.

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.

$ ln -s $(which rninja) /usr/local/bin/ninja # symlink in place
$ cmake -G Ninja -B out/Release
$ ninja -C out/Release -j8
[1/2412] Compiling src/render/pipeline.cc
[2/2412] Compiling src/render/shader.cc cache hit
[3/2412] Compiling src/render/mesh.cc cache hit
...
[2412/2412] Linking target render_test cache hit
done in 11.4s // ninja baseline: 47.8s

// 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

Pick your package manager

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

Drop-in compatible, plus the parts ninja leaves out

Compatibility

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.

Caching

Content-addressed local cache

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.

Scheduling

Tokio-based async scheduler

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.

Remote cache

Share artifacts across machines

Optional remote cache built on async-nng streams blobs between developer laptops and CI runners. First builds on fresh machines become mostly cached builds.

Subtools

All of ninja -t, plus extras

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.

Zero config

Sensible defaults out of the box

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

Five steps to a cached build

01

Parse

Read your existing build.ninja with stock ninja semantics. Phony, restat, pools, depfiles all preserved.

02

Hash

Fingerprint inputs, command line, and the environment the rule actually observes. blake3 over deduplicated content.

03

Lookup

Check the local sled index for a matching action; optionally pull the artifact from the remote cache.

04

Build

On miss, run the toolchain through the tokio-driven scheduler. Outputs are tracked, never reordered against ninja semantics.

05

Store

Write the artifact into the content-addressed blob store and update the index. Crash-safe at every step.

// audience

Built for the teams already on ninja

C/C++ projects with multi-minute incremental builds

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.

CI pipelines running many builds per day

Same commit, same flags, same toolchain — over and over. A shared remote cache turns the second pipeline into a near-no-op.

Monorepos with shared code across teams

Different teams build overlapping subgraphs from the same sources. The cache shares results across them automatically.

Game studios and perf-sensitive teams on ninja

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.

// FAQ

Questions before you swap the binary

[+] Is rninja really drop-in?

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.

[+] What speedup should I expect?

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.

[+] How does the cache decide what is reusable?

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.

[+] Do I need to run a server for the remote cache?

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.

[+] What about correctness — can caching mask a build bug?

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.

[+] Which generators are supported?

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.

[+] How do I install 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.

[+] Is rninja production-ready?

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.

Stop paying twice for the same compile.

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.