AOSP Foundations
3 min read

Ninja Build Graph

Understand how the Ninja build system executes the final phase of the AOSP compilation pipeline.

While Make and Soong are responsible for defining what needs to be built, neither of them actually compile C++ code or package APKs. Their ultimate goal is simply to generate a set of instructions.

Those final instructions are handed to Ninja, the master executor in the AOSP build pipeline.

What is Ninja?

Ninja is a small, incredibly fast build system with a maniacal focus on speed. Unlike Make, which attempts to evaluate logic, read timestamps, and figure out dependencies on the fly, Ninja is completely dumb by design.

It does not have variables, logic, or complex macros. It expects a pre-calculated, explicitly defined list of exact shell commands to run, formatted in a .ninja text file. Because the file is already fully resolved, Ninja can begin executing compiler commands almost instantly, perfectly parallelizing the workload across your CPU cores.

How Ninja Works in AOSP

When you type m to build Android:

  1. Kati reads the legacy Android.mk files and generates a file named build-<target>.ninja.
  2. Soong reads the modern Android.bp files and generates a file named soong_build.ninja.
  3. A master build.ninja file merges these two separate outputs together.
  4. The Ninja executable (prebuilts/build-tools/linux-x86/bin/ninja) reads the master file and begins executing the parallel compilation.

Rules and Build Statements

If you open a generated build.ninja file, you will see it consists almost entirely of two simple concepts: Rules and Build Statements.

1. Rules

A rule defines a command-line template. For example, how to invoke the C++ compiler:

rule cc_compile
  command = prebuilts/clang/.../bin/clang++ -c $in -o $out
  description = Compiling C++ $in

2. Build Statements

A build statement applies a specific input file to a rule to generate a specific output file.

build out/soong/.../main.o: cc_compile frameworks/base/main.cpp

This tells Ninja: "To generate main.o, use the cc_compile rule, and feed it main.cpp as the input."

Ninja's Role in Debugging

You will almost never write a .ninja file manually. Your job as an AOSP developer is to write clean Android.bp files.

However, understanding Ninja is absolutely critical for debugging. When a build fails, the terminal output is generated directly by Ninja. The error message will show the exact, fully-resolved command line that Ninja executed (e.g., the massive clang++ command with all 50 include directories attached) right before it crashed. Copying and analyzing that exact Ninja command is the fastest way to diagnose C++ compiler errors.

# You can manually invoke the prebuilt ninja tool to trace dry-runs
prebuilts/build-tools/linux-x86/bin/ninja -n -d explain