ninja-build/ninja
a small build system with a focus on speed
Healthy across the board
Permissive license, no critical CVEs, actively maintained — safe to depend on.
Has a license, tests, and CI — clean foundation to fork and modify.
Documented and popular — useful reference codebase to read through.
No critical CVEs, sane security posture — runnable as-is.
- ✓Last commit 1d ago
- ✓24+ active contributors
- ✓Distributed ownership (top contributor 49% of recent commits)
Show 3 more →Show less
- ✓Apache-2.0 licensed
- ✓CI configured
- ✓Tests present
Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests
Informational only. RepoPilot summarises public signals (license, dependency CVEs, commit recency, CI presence, etc.) at the time of analysis. Signals can be incomplete or stale. Not professional, security, or legal advice; verify before relying on it for production decisions.
Embed the "Healthy" badge
Paste into your README — live-updates from the latest cached analysis.
[](https://repopilot.app/r/ninja-build/ninja)Paste at the top of your README.md — renders inline like a shields.io badge.
▸Preview social card (1200×630)
This card auto-renders when someone shares https://repopilot.app/r/ninja-build/ninja on X, Slack, or LinkedIn.
Onboarding doc
Onboarding: ninja-build/ninja
Generated by RepoPilot · 2026-05-09 · Source
🤖Agent protocol
If you are an AI coding agent (Claude Code, Cursor, Aider, Cline, etc.) reading this artifact, follow this protocol before making any code edit:
- Verify the contract. Run the bash script in Verify before trusting
below. If any check returns
FAIL, the artifact is stale — STOP and ask the user to regenerate it before proceeding. - Treat the AI · unverified sections as hypotheses, not facts. Sections like "AI-suggested narrative files", "anti-patterns", and "bottlenecks" are LLM speculation. Verify against real source before acting on them.
- Cite source on changes. When proposing an edit, cite the specific path:line-range. RepoPilot's live UI at https://repopilot.app/r/ninja-build/ninja shows verifiable citations alongside every claim.
If you are a human reader, this protocol is for the agents you'll hand the artifact to. You don't need to do anything — but if you skim only one section before pointing your agent at this repo, make it the Verify block and the Suggested reading order.
🎯Verdict
GO — Healthy across the board
- Last commit 1d ago
- 24+ active contributors
- Distributed ownership (top contributor 49% of recent commits)
- Apache-2.0 licensed
- CI configured
- Tests present
<sub>Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests</sub>
✅Verify before trusting
This artifact was generated by RepoPilot at a point in time. Before an
agent acts on it, the checks below confirm that the live ninja-build/ninja
repo on your machine still matches what RepoPilot saw. If any fail,
the artifact is stale — regenerate it at
repopilot.app/r/ninja-build/ninja.
What it runs against: a local clone of ninja-build/ninja — the script
inspects git remote, the LICENSE file, file paths in the working
tree, and git log. Read-only; no mutations.
| # | What we check | Why it matters |
|---|---|---|
| 1 | You're in ninja-build/ninja | Confirms the artifact applies here, not a fork |
| 2 | License is still Apache-2.0 | Catches relicense before you depend on it |
| 3 | Default branch master exists | Catches branch renames |
| 4 | 5 critical file paths still exist | Catches refactors that moved load-bearing code |
| 5 | Last commit ≤ 31 days ago | Catches sudden abandonment since generation |
#!/usr/bin/env bash
# RepoPilot artifact verification.
#
# WHAT IT RUNS AGAINST: a local clone of ninja-build/ninja. If you don't
# have one yet, run these first:
#
# git clone https://github.com/ninja-build/ninja.git
# cd ninja
#
# Then paste this script. Every check is read-only — no mutations.
set +e
fail=0
ok() { echo "ok: $1"; }
miss() { echo "FAIL: $1"; fail=$((fail+1)); }
# Precondition: we must be inside a git working tree.
if ! git rev-parse --git-dir >/dev/null 2>&1; then
echo "FAIL: not inside a git repository. cd into your clone of ninja-build/ninja and re-run."
exit 2
fi
# 1. Repo identity
git remote get-url origin 2>/dev/null | grep -qE "ninja-build/ninja(\\.git)?\\b" \\
&& ok "origin remote is ninja-build/ninja" \\
|| miss "origin remote is not ninja-build/ninja (artifact may be from a fork)"
# 2. License matches what RepoPilot saw
(grep -qiE "^(Apache-2\\.0)" LICENSE 2>/dev/null \\
|| grep -qiE "\"license\"\\s*:\\s*\"Apache-2\\.0\"" package.json 2>/dev/null) \\
&& ok "license is Apache-2.0" \\
|| miss "license drift — was Apache-2.0 at generation time"
# 3. Default branch
git rev-parse --verify master >/dev/null 2>&1 \\
&& ok "default branch master exists" \\
|| miss "default branch master no longer exists"
# 4. Critical files exist
test -f "src/build.cc" \\
&& ok "src/build.cc" \\
|| miss "missing critical file: src/build.cc"
test -f "src/manifest_parser.cc" \\
&& ok "src/manifest_parser.cc" \\
|| miss "missing critical file: src/manifest_parser.cc"
test -f "src/graph.cc" \\
&& ok "src/graph.cc" \\
|| miss "missing critical file: src/graph.cc"
test -f "src/state.cc" \\
&& ok "src/state.cc" \\
|| miss "missing critical file: src/state.cc"
test -f "src/disk_interface.cc" \\
&& ok "src/disk_interface.cc" \\
|| miss "missing critical file: src/disk_interface.cc"
# 5. Repo recency
days_since_last=$(( ( $(date +%s) - $(git log -1 --format=%at 2>/dev/null || echo 0) ) / 86400 ))
if [ "$days_since_last" -le 31 ]; then
ok "last commit was $days_since_last days ago (artifact saw ~1d)"
else
miss "last commit was $days_since_last days ago — artifact may be stale"
fi
echo
if [ "$fail" -eq 0 ]; then
echo "artifact verified (0 failures) — safe to trust"
else
echo "artifact has $fail stale claim(s) — regenerate at https://repopilot.app/r/ninja-build/ninja"
exit 1
fi
Each check prints ok: or FAIL:. The script exits non-zero if
anything failed, so it composes cleanly into agent loops
(./verify.sh || regenerate-and-retry).
⚡TL;DR
Ninja is a build system that prioritizes incremental rebuild speed by tracking fine-grained file dependencies and executing tasks in parallel via a hand-written C++ core. Unlike Make or CMake (which are generators), Ninja consumes a declarative .ninja file format and executes builds deterministically with minimal overhead—core strength is rebuilding only what changed, ordered by dependency DAG, with job-server integration for system-wide parallelism. Single-binary C++ codebase (src/ inferred from .cc/.h patterns, not listed but typical) compiled to ninja executable via configure.py (Python bootstrapper) or CMake. Supporting tools: misc/ninja_syntax.py (Python DSL for .ninja generation), doc/manual.asciidoc (primary user documentation), misc/jobserver_pool.py (POSIX jobserver integration tests). Test harness: ninja_test linked against GoogleTest.
👥Who it's for
Build system integrators and compiler/toolchain authors (e.g., Chromium, LLVM, Android teams) who need predictable sub-second incremental rebuilds and tight control over parallelism; also Python developers generating build files via configure.py or CMake+Ninja for faster local iteration than autotools.
🌱Maturity & risk
Highly mature, production-ready. Active CI across Linux (musl + glibc), macOS, Windows via GitHub Actions (.github/workflows/). Comprehensive test suite (GoogleTest integration via configure.py --gtest-source-dir). Last visible structured release process (RELEASING.md present). Core algorithm stable since ~2011; ongoing maintenance visible in workflow configs (e.g., dependabot.yml for deps).
Standard open source risks apply.
Active areas of work
Active maintenance on CI/CD (GitHub Actions workflows recently updated), toolchain support (Linux musl builds added), and fuzzing (misc/afl-fuzz/ directory). Dependabot watching dependencies. No major language rewrites; focus on correctness and platform coverage.
🚀Get running
git clone https://github.com/ninja-build/ninja.git
cd ninja
./configure.py --bootstrap
# or via CMake:
cmake -Bbuild-cmake -DBUILD_TESTING=OFF
cmake --build build-cmake
The ./ninja binary is immediately usable; no install step needed.
Daily commands:
Bootstrap: ./configure.py --bootstrap generates ninja binary + build.ninja, then ./ninja runs subsequent builds. With tests: ./configure.py --bootstrap --gtest-source-dir=/path/to/googletest && ./ninja all && ./ninja_test. Documentation: ./configure.py && ninja manual doc/manual.html (requires asciidoc, xsltproc). Doxygen API docs: ./configure.py && ninja doxygen (requires doxygen).
🗺️Map of the codebase
src/build.cc— Core build execution engine that orchestrates task scheduling, dependency resolution, and command execution—the heart of Ninja's speed optimization.src/manifest_parser.cc— Parses .ninja build files into an in-memory dependency graph; any changes to the build language syntax flow through here.src/graph.cc— Manages the dependency graph data structure and algorithms for detecting cycles, computing critical paths, and validating correctness.src/state.cc— Central state manager tracking all nodes, edges, rules, and build metadata; loaded by the parser and consumed by the build engine.src/disk_interface.cc— Abstracts filesystem operations (stat, read, write) enabling testing and cross-platform file handling—critical for reproducibility.src/depfile_parser.cc— Parses compiler-generated .d dependency files; performance-sensitive since it's called for every generated dependency.src/build_log.cc— Persistent build log that enables incremental builds by tracking which inputs changed; determines what gets rebuilt.
🧩Components & responsibilities
- ManifestParser — Tokenizes and parses .ninja build file
🛠️How to make changes
Add a new built-in Ninja command (e.g., 'ninja -t mycmd')
- Add command handler function in src/ninja.cc (after 'int main'). Follow the pattern: static int HandleMyCmd(const Options& options, ...). (
src/ninja.cc) - Register the command in the tool dispatch logic (search for 'if (options.tool == "clean")') by adding another else-if for your command name. (
src/ninja.cc) - If your command modifies the graph or needs build state, instantiate State and load the manifest via ManifestParser (see 'HandleClean' for reference). (
src/state.cc)
Add a new build variable or function available in .ninja files
- Add evaluation logic in src/eval_env.cc (EvalEnv::LookupVariable or a new method) to handle the variable substitution. (
src/eval_env.cc) - If it's a built-in function (like 'pkgconfig'), add parsing and function call handling in src/manifest_parser.cc around variable parsing. (
src/manifest_parser.cc) - Update the Bindings class in src/eval_env.h to register the new variable/function with proper documentation. (
src/eval_env.h) - Add a test case in src/manifest_parser_test.cc or src/eval_env_test.cc to verify variable expansion. (
src/manifest_parser_test.cc)
Add support for a new file format (e.g., parsing .dyndep files or alternative dependency formats)
- Create a new parser class: src/myformat_parser.cc and src/myformat_parser.h following the pattern of depfile_parser.cc or dyndep_parser.cc. (
src/dyndep_parser.cc) - Implement parsing logic to populate Edge and Node objects or return a structured result (see DyndepFile struct for reference). (
src/dyndep_parser.cc) - Call your parser from the appropriate build phase: either in build.cc (during edge execution) or in manifest_parser.cc (during initial loading). (
src/build.cc) - Add unit tests in src/myformat_parser_test.cc with sample input files in the test/ or misc/ directory. (
src/build_test.cc)
🔧Why these technologies
- C++11 (standard library, no external deps) — Maximum portability and minimal deployment footprint; Ninja is a single binary with no runtime dependencies, critical for embedded build environments.
- Memory-mapped files (build_log, deps_log) — O(1) append and read performance for incremental build metadata; avoids parsing overhead on every invocation.
- Process pools and job server support — Parallel task execution with fair resource allocation across multiple Ninja processes and GNU make coordination.
- Custom manifest parser (not YAML/JSON) — Tight control over syntax for minimal overhead; specialized for build semantics (e.g., build edges, implicit deps, variable scoping).
⚖️Trade-offs already made
-
Single-threaded task dispatch loop vs. thread pool
- Why: Simplicity and determinism in a build tool where scheduling decisions are critical.
- Consequence: Parallelism is via spawned subprocesses; thread-safe shared memory unnecessary. Trades raw throughput for predictability.
-
No built-in scripting language (Python, Lua); rely on shell for actions
- Why: Minimal binary size and avoidance of dynamic language interpretation overhead; shell is ubiquitous.
- Consequence: Complex logic pushed to build file authors or external tools; no runtime dynamic dispatch within Ninja itself.
-
Persistent build log format is append-only, not relational
- Why: Fast sequential writes and simple reconstruction of build state.
- Consequence: Log can grow unbounded; 'ninja -t clean' or manual cleanup required periodically. No point-in-time rollback.
-
No remote build execution or distributed cache (by design)
- Why: Keep Ninja focused and lightweight; delegate to external tools (CMake, Bazel, or custom wrappers).
- Consequence: Users needing distributed builds must orchestrate via higher-level systems; Ninja is single-machine only.
🚫Non-goals (don't propose these)
- Remote build execution or distributed caching
- Built-in scripting language or plugin system
- Automatic dependency discovery (requires compiler integration or dyndep files)
- Support for non-Unix platforms (no first-class Windows SDK integration; basic cmd.exe support only)
- IDE integration or GUI frontend (Ninja is CLI-only; editors must invoke via tool integration)
🪤Traps & gotchas
Bootstrap order: configure.py must run before ninja binary exists; if CMake path chosen, CMakeLists.txt requires C++ compiler + cmake ≥3.x (version not specified—assume recent). GoogleTest path: --gtest-source-dir expects full source tree (not pre-built library; see README note). Documentation build: asciidoc + xsltproc are non-obvious external deps not in standard toolchain; dblatex required separately for PDF. Platform quirks: POSIX jobserver (misc/jobserver_test.py) only on Unix; Windows has separate job object abstraction. Fuzzing: misc/afl-fuzz/ requires AFL++ or libFuzzer setup, not standard. No hidden environment variables evident from CONTRIBUTING.md snippet.
🏗️Architecture
💡Concepts to learn
- Directed Acyclic Graph (DAG) dependency engine — Ninja's core execution model: topological sort of build rules ensures correct incremental rebuilds; understanding DAG semantics (cycles forbidden, implicit vs explicit deps) is essential to debugging build failures and performance issues
- POSIX make jobserver protocol — Ninja respects GNU make's jobserver (misc/jobserver_pool.py) to avoid oversubscription when nested builds run; critical in large build farms where Ninja itself is called from another build system (e.g., Bazel → Ninja → compiler)
- Incremental build state tracking (.ninja_deps) — Ninja persists implicit dependencies discovered at build time (e.g., #include chains, generated headers) in .ninja_deps file; reused to skip rebuilds; misunderstanding this causes phantom rebuilds or stale binaries
- Variable expansion and scoping (Ninja manifest language) — Ninja's DSL supports variable substitution with scope (global, rule, build-statement levels); subtle scoping rules (e.g., $out in rules vs build rules) differ from Make; source of common integration bugs in generated .ninja files
- Work queue scheduling and parallelism limits (depth-first vs breadth-first) — Ninja executes builds with configurable job limits (-j flag) and internally schedules tasks to maximize cache locality; understanding queue strategy affects build time on multicore systems and shared CI machines
- Manifest fingerprinting and regeneration detection — Ninja detects when build.ninja itself changes and reruns the generator (configure.py or CMake); implicit dependency on generator correctness; changes to build rules may invalidate cached .ninja_deps hashes
- ANSI escape code terminal output and CI detection — Ninja emits colored output and progress counters; respects NO_COLOR env var and CI environment detection; understanding output parsing matters for CI log aggregation and IDE integration (LSP servers watching Ninja output)
🔗Related repos
bazelbuild/bazel— High-performance build system that internally uses Ninja as a backend execution engine for deterministic, parallelized builds; consumers of Ninja often integrate Bazel on topfacebook/buck2— Meta's successor to Buck; also uses Ninja as execution backend and shares DAG-based build model; similar problem domain of fast incremental rebuildsninja-build/ninja-test-cases— Companion repo (if it exists; check GitHub) with end-to-end build system test suites; commonly linked from Ninja issues for regression reproductioncmake/cmake— Meta-build generator that emits Ninja files; CMakeLists.txt in Ninja repo builds Ninja itself—tight integration point for cross-platform build configurationchromium/chromium— Major historical consumer of Ninja; Chromium build system (gn) generates .ninja files; exemplar of how to scale Ninja to massive monorepos with thousands of targets
🪄PR ideas
To work on one of these in Claude Code or Cursor, paste:
Implement the "<title>" PR idea from CLAUDE.md, working through the checklist as the task list.
Add comprehensive unit tests for src/build_log.cc
The repo has src/build_log_test.cc and src/build_log_perftest.cc, but examining the file structure suggests the build_log module handles critical persistence logic. A new contributor could expand test coverage for edge cases: log file corruption recovery, concurrent write handling, and format migration scenarios. This is high-value because build log integrity is essential to Ninja's correctness.
- [ ] Review existing src/build_log_test.cc to identify untested code paths
- [ ] Add tests for malformed log file recovery scenarios
- [ ] Add tests for concurrent access patterns and race conditions
- [ ] Add tests for log file format version upgrades
- [ ] Verify all tests pass with
ninja build_log_test
Implement GitHub Actions workflow for FreeBSD/OpenBSD testing
The repo has CI workflows for linux-musl.yml, linux.yml, macos.yml, and windows.yml in .github/workflows/, but notably lacks BSD system testing. Ninja as a build system needs to support Unix-like platforms comprehensively. Adding FreeBSD CI coverage would catch platform-specific issues in src/posix*.cc and subprocess handling.
- [ ] Create .github/workflows/freebsd.yml using GitHub's FreeBSD runner support
- [ ] Add steps to build Ninja via ./configure.py and run tests with ninja -t test
- [ ] Include platform-specific dependency installation for FreeBSD
- [ ] Mirror the test structure from existing linux.yml and macos.yml workflows
- [ ] Document the new workflow in CONTRIBUTING.md
Add documentation for the Ninja manifest fuzzer and AFL integration
The repo contains misc/afl-fuzz/ and misc/manifest_fuzzer.cc files, indicating fuzzing infrastructure exists, but there's no documented guide for contributors wanting to run or extend fuzzing. This is a gap because security-conscious contributors may want to contribute fuzz cases. A PR could add a doc/FUZZING.md guide explaining how to use AFL with the provided token sets.
- [ ] Create doc/FUZZING.md documenting the AFL setup and token files in misc/afl-fuzz-tokens/
- [ ] Explain how to build the fuzzer: compile manifest_fuzzer.cc with AFL instrumentation
- [ ] Document the purpose of each token file (kw_build, kw_rule, misc_dollar, etc.)
- [ ] Add instructions for running long-term fuzzing campaigns
- [ ] Reference this doc from CONTRIBUTING.md in the testing section
🌿Good first issues
- Add Python type hints to misc/ninja_syntax.py and misc/ninja_syntax_test.py—currently untyped Python 3; enables better IDE support and catches generator bugs early. Small scope, high impact for contributors using external tools.
- Expand doc/manual.asciidoc with a 'Common Mistakes' section documenting pitfalls like implicit dependency chains, variable scoping in subninja, and pool exhaustion—currently missing best-practices guide referenced by downstream tools.
- Create misc/benchmark_suite.ninja—a realistic multi-file C++ project for regression testing incremental rebuild times; currently only misc/long-slow-build.ninja; helps catch performance regressions in parser or DAG engine.
⭐Top contributors
Click to expand
Top contributors
- @jhasse — 49 commits
- @bradking — 9 commits
- @ildus — 7 commits
- @dependabot[bot] — 5 commits
- @jamesl-10 — 4 commits
📝Recent commits
Click to expand
Recent commits
aa4c15c— Merge pull request #2722 from elliotgoodrich/small-string-utils (jhasse)68398e1— Merge pull request #2738 from ninja-build/dependabot/github_actions/actions/upload-artifact-7 (jhasse)b7b5d31— Merge pull request #2744 from braydenkrus/patch-1 (jhasse)02e3fd2— Merge pull request #2765 from jhasse/builddir-target (jhasse)f19dc4a— Address comments (jhasse)a830ae5— Merge pull request #2726 from jamesl-10/ninja-parallelism (jhasse)def9560— Merge pull request #2764 from skeeto/fix-recovery (nico)cedf78d— Merge pull request #2739 from jhasse/exclude-validation-edges-from-compdb (jhasse)93e431a— Fall back to $builddir/target when target lookup fails (jhasse)947aade— Merge pull request #2753 from bradking/explain-dyndep (jhasse)
🔒Security observations
Ninja's codebase is relatively well-structured for a build system. The primary security concerns relate to inherent build system risks: command injection through user-defined rules, path traversal vulnerabilities in output/dependency handling, and validation of external inputs (depfiles, .ninja files). No obvious hardcoded credentials, exposed ports, or Docker misconfigurations were identified. The project follows good practices with static analysis tools (.clang-tidy) configured. Recommendations focus on input validation, path normalization, and safe subprocess execution patterns. The project would benefit from security-focused code review of parsing and execution components.
- Medium · Potential Command Injection in Build System —
src/build.cc, src/clparser.cc, src/depfile_parser.cc. As a build system that executes build commands, Ninja processes user-defined build rules and commands from .ninja files. The presence of files like clparser.cc and depfile_parser.cc suggest parsing of external input. Build systems are inherently vulnerable to command injection if user input (variable substitution, file paths) is not properly sanitized before shell execution. Fix: Ensure all variable substitution and path handling uses safe APIs that don't invoke shell interpretation. Implement strict input validation for all parsed .ninja file content. Use subprocess execution with argument arrays rather than shell string concatenation. - Medium · Potential Path Traversal in Build Output/Dependency Handling —
src/disk_interface.cc, src/deps_log.cc, src/build_log.cc. The build system processes paths from .ninja files and dependency files (deps_log, build_log). Without proper path normalization and validation, an attacker could craft malicious .ninja files with path traversal sequences (../) to write files outside the intended build directory. Fix: Implement strict path validation and normalization. Reject paths containing '..' or other traversal sequences. Use canonical path resolution and verify all output paths remain within the intended build directory. - Medium · Unvalidated External File Input (Depfiles) —
src/depfile_parser.cc, src/depfile_parser.in.cc. The depfile_parser processes .d files generated by compilers which are external, potentially untrusted input. Malformed or maliciously crafted depfiles could cause buffer overflows, infinite loops, or excessive memory consumption. Fix: Implement strict validation and bounds checking for all depfile parsing. Add size limits and timeout mechanisms. Use safe string parsing practices and validate all parsed data before use. - Low · Potential Information Disclosure via Error Messages —
src/build.cc, src/build_log.cc. Build systems may expose sensitive information (absolute paths, environment variables, compiler versions) in error messages and logs that could be captured in build artifacts or logs. Fix: Review error messages and logging to ensure sensitive paths and environment details are not unnecessarily exposed. Implement sanitization of file paths in user-facing output when appropriate. - Low · Use of Python in Build Scripts Without Sandboxing —
configure.py, misc/ci.py, misc/ninja_syntax.py. Multiple Python files (configure.py, ninja_syntax.py, misc/ci.py) execute Python code which could be vulnerable if processing untrusted input. The bootstrap process runs Python code directly. Fix: Review Python scripts for injection vulnerabilities. Ensure subprocess calls use argument arrays, not shell strings. Validate all external input before passing to Python functions that execute code.
LLM-derived; treat as a starting point, not a security audit.
👉Where to read next
- Open issues — current backlog
- Recent PRs — what's actively shipping
- Source on GitHub
Generated by RepoPilot. Verdict based on maintenance signals — see the live page for receipts. Re-run on a new commit to refresh.