Build Predictably with Ninja: A Safe First Run and Useful Diagnostics
You will finish with a repeatable Ninja build workflow: check the generated build file, preview what would run, build one target, and inspect the dependency graph when something goes wrong. The examples use Ninja 1.11.1 from the installed ninja-build package.
The route
Jump straight to the step you need, or tick off Done means at the end.
Allow 10 to 15 minutes. You need a shell, a project that already has a build.ninja file, and permission to read and write that project's build outputs. This guide assumes the build file was generated by a tool such as CMake or another project generator. Ninja is an execution engine, not a compiler or a general project configuration tool.
Checkpoint
All commands below are ordinary user commands. Do not use sudo for a normal build. Elevated privileges can leave root-owned outputs behind and can make a build recipe run with access it should not have.
1. Confirm the program and build directory
Check the installed version, then move to the directory containing the generated manifest. Substitute your real build directory for /path/to/build:
$ ninja --version
1.11.1
$ test -f /path/to/build/build.ninja && echo 'build.ninja found'
build.ninja found
Ninja 1.11.1 reads build.ninja in the current directory by default. The -C option is a useful alternative when you want to stay in your source directory:
$ ninja -C /path/to/build --version
1.11.1
-C changes directory before Ninja does anything else. It is easy to overlook that a relative target name is then resolved from the build directory, not from the directory where you typed the command.
2. Preview the default build
With no target named, Ninja builds the manifest's default target. Preview it first with -n:
$ ninja -C /path/to/build -n
[1/3] cc -c src/main.c -o obj/main.o
[2/3] cc -c src/util.c -o obj/util.o
[3/3] cc obj/main.o obj/util.o -o app
Dry-run mode does not run the commands, but Ninja acts as if they succeeded for the purpose of displaying the planned work. The exact progress count and command descriptions depend on the project. A dry run is a checkpoint, not proof that the compiler, linker or other tools will succeed.
If the output contains a command you did not expect, stop and inspect the generated files or the generator's configuration. Do not edit a generated manifest casually: the next configuration step may overwrite your change.
3. Build one target
When the dry run looks right, name the output you need. This limits the work and makes the result easier to verify:
$ ninja -C /path/to/build app
[1/3] cc -c src/main.c -o obj/main.o
[2/3] cc -c src/util.c -o obj/util.o
[3/3] cc obj/main.o obj/util.o -o app
A later run usually reports that there is nothing to do if the inputs and command descriptions have not changed:
$ ninja -C /path/to/build app
ninja: no work to do.
Verify the output independently when that matters. For an executable, for example:
$ test -x /path/to/build/app && echo 'app is executable'
app is executable
Ninja decides what is out of date from the dependency information in the manifest and its build logs. A successful Ninja exit status means the declared commands completed; it does not certify the application's runtime behaviour.
4. Control parallel work deliberately
Ninja runs jobs in parallel by default, using the available CPU count. Usually that is the fastest choice. Use -j when you need a predictable limit, for example on a shared machine or a memory-heavy link:
$ ninja -C /path/to/build -j2 app
The value is the maximum number of jobs Ninja starts concurrently. -j0 means unlimited, which is rarely a good first response to a slow build. The -l option prevents new jobs from starting when the system load average is above the supplied value:
$ ninja -C /path/to/build -j4 -l8 app
These options affect this invocation only. If the project generator has encoded a pool or another concurrency constraint in build.ninja, that constraint still applies.
5. See full commands and keep going after a failure
Ninja normally prints a short description for each job. Add -v when you need the complete command line, including flags and expanded paths:
$ ninja -C /path/to/build -v app
[1/1] cc -O2 -Iinclude -c src/main.c -o obj/main.o
When one command fails, Ninja normally stops after that failure. Use -k to let it continue until a chosen number of jobs have failed. This is useful for collecting several independent compiler errors:
$ ninja -C /path/to/build -k5 app
ninja: build stopped: subcommand failed.
-k0 means keep going without a failure limit. It can produce a large amount of output and does not make dependent jobs safe to run when their prerequisites failed. Read the first actionable compiler or generator error rather than jumping to the final summary.
6. Inspect targets before changing anything
The -t option runs a Ninja subtool. List the tools available in this installed version:
$ ninja -t list
ninja subtools:
browse browse dependency graph in a web browser
clean clean built files
commands list all commands required to rebuild given targets
inputs list all inputs required to rebuild given targets
...
The exact spacing and full list can vary, so treat the local output as authoritative. Two read-only tools are particularly useful:
$ ninja -C /path/to/build -t commands app
cc -c src/main.c -o obj/main.o
cc -c src/util.c -o obj/util.o
cc obj/main.o obj/util.o -o app
$ ninja -C /path/to/build -t query app
app:
input: link
obj/main.o
obj/util.o
outputs:
commands prints the commands Ninja would use to rebuild a target. query shows the target's direct inputs and outputs. Use these before modifying a manifest or deleting an output. They help distinguish a missing input from a stale or incorrectly generated dependency.
7. Handle failure without making it worse
Do not run a clean subtool as a first troubleshooting step. Cleaning removes generated files and can make the next diagnosis harder. If you intentionally need to remove built files, inspect the available help first:
$ ninja -C /path/to/build -t clean -h
Confirm the directory and the cleanup scope before accepting a destructive operation. Keep source files and generated configuration available until you have a reproducible replacement build. There is no undo command for files removed by a clean operation; restore them by rebuilding, regenerating, or recovering them from version control as appropriate.
For a missing compiler or command, rerun with -v and test the named program with command -v. For a missing input, use the path shown by -t query and check it without changing state:
$ command -v cc
/usr/bin/cc
$ test -r /path/to/build/src/main.c && echo readable
If the manifest itself is stale, rerun the project generator using that project's documented command, then repeat the dry run. Do not guess generator flags from Ninja's command-line options: Ninja cannot repair a configuration decision made by another tool.
Done means
ninja --versionreports the installed version you expected, here 1.11.1.- The build directory and
build.ninjawere checked before any work ran. - A dry run was reviewed before the real build.
- The required target completed, and its output was checked independently.
- Parallelism was limited deliberately where CPU or memory pressure required it.
-v,-t commandsand-t queryare available for the next failure.- No elevated privilege or destructive clean operation was used without a specific reason.