// about
A build executor, not a build planner.
ninja is an executor for build graphs that someone else generated. It is famous for being small and fast — and it is. rninja keeps that contract: parse the same build.ninja, run the same edges, expose the same flags. What changes is what happens inside the executor.
Why rewrite an already-fast tool
ninja is hard to beat on dispatch overhead. It is much easier to beat on work avoidance. Most incremental builds in C/C++ and Rust monorepos re-run compilers and linkers over inputs that haven't actually changed in any way that affects the output. The cost of those redundant calls vastly exceeds anything the scheduler itself spends.
rninja's thesis is that the executor is the right place to put a cache. The scheduler already knows which actions are about to run, what their inputs are, and what they will produce. Putting a content-addressed store at that layer means the cache understands the graph natively — it doesn't have to wrap every compiler invocation in a tool that re-derives what the graph already said.
What stays the same
- The format.
build.ninjais parsed unchanged. CMake, GN, Meson, and hand-written graphs all keep working. There is no generator step to update. - The flags.
-C,-f,-j,-k,-l,-n,-t,-v,-dall behave like stock ninja. Exit codes match so existing CI scripts don't move. - The semantics. Phony targets, depfile parsing, restat handling, generator edges — all preserved. If a build is correct under ninja, it stays correct under rninja.
What changes
- The scheduler. tokio-driven, designed to keep CPUs, disk, and network busy at the same time. ninja's planner is good; its scheduler is conservative. rninja takes more risks where they pay off.
- The cache. Content-addressed action cache backed by sled for the index and a deduplicated blob store for artifacts. A hit means the entire action is skipped, not just one compiler call.
- The transport. Optional remote cache built on async-nng. CI nodes and developer laptops can share artifacts without standing up additional infrastructure beyond an endpoint.
What we don't do
rninja does not regenerate build.ninja. It does not replace your CMake or Meson. It does not invent a new build description language. It is not a remote execution service that runs your compiles in someone else's cloud. The scope on purpose is narrow: be a faster, cache-aware executor that drops into the slot where ninja already sits.
Where we are
rninja is published on crates.io, npm, PyPI, and Homebrew under MIT. The repo lives at github.com/neul-labs/rninja. Documentation is at rninja.docs.neullabs.com. The roadmap is in the open and bug reports get triaged in the issue tracker.
If you build with ninja today, the lowest-risk experiment is one command: install rninja, run time rninja target next to time ninja.orig target, and read the numbers. If they don't move, you've lost ten minutes. If they do, the path to CI rollout is the same as installing any other binary.
Neul Labs
rninja is part of the Neul Labs family of Rust-flavored developer tools. We build things that drop in where slower tools already live and earn their keep on measured numbers. If that is interesting, the GitHub org has the sibling projects.