AOSP Foundations
3 min read

Dependency Generation

Learn how Soong translates Android.bp modules into Ninja build graphs and how to debug incremental build failures.

The transition from a declarative Android.bp file to an executable build.ninja file is a complex orchestration handled by the Soong build engine. Understanding this dependency generation phase is crucial for resolving obscure build errors and preventing corrupt incremental builds.

How Soong Generates Ninja Files

When you execute the m command, Soong performs several distinct passes over the source tree:

  1. Parsing: Soong locates every single Android.bp file in the entire repository and parses the Blueprint syntax into a massive in-memory graph.
  2. Mutators: Soong applies "mutators" to the graph. For example, if you define an android_app module, a mutator will automatically inject a dependency on the core Java framework SDK, even if you did not explicitly write it in the .bp file.
  3. Resolution: Soong strictly checks that every requested dependency actually exists. If module A requires module B, but module B is missing or misspelled, Soong throws a fatal error here before compilation even begins.
  4. Ninja Generation: Finally, Soong outputs the massive text file containing the exact compiler paths and command-line flags for Ninja to execute.

Incremental Builds and Dependency Tracking

The primary reason Ninja is so incredibly fast at incremental builds (rebuilding only what has changed) is because it rigorously tracks file timestamps.

When Ninja reads the generated build.ninja file, it looks at the target output (e.g., libmath.so) and the input source file (e.g., math.cpp).

  • If the "last modified" timestamp on math.cpp is newer than libmath.so, Ninja knows the file has changed and recompiles it.
  • If the .so file is newer, Ninja safely skips the compilation entirely.

The Missing Dependency Bug

A common and frustrating bug in AOSP development is the "missing dependency" error.

If you use a custom Python script (via a genrule) to generate a C++ header file, but you forget to explicitly list that Python script as a dependency in your Android.bp file, Ninja will not know it needs to watch the script for changes. If you modify the script, Ninja will not see the timestamp change, and your C++ code will compile using the old, outdated header file, causing infuriating logic bugs.

Debugging Ninja Build Failures

If your build fails during the Ninja phase, it means the dependency graph was generated successfully, but the actual C++/Java compiler failed to execute the resulting command.

  1. Analyze the Command: Ninja will output the exact command it ran. Read the flags. Are you missing an include directory (-I)? Did it link the wrong architecture?
  2. Force Re-generation: Occasionally, the Soong dependency graph itself gets corrupted. You can force Soong to delete its internal cache and regenerate the .ninja files from scratch.
# Force Soong to discard the cached dependency graph and regenerate from scratch
m clean
# OR safely delete the intermediate files
rm -rf out/soong