One of my projects had me working with the Android 9 build system. Unfortunately, it's a mess of indirection and new/old build code. In Android 9, an image is built with Soong, Kati, Blueprint, and Ninja. During the build, the and files get read by the build system to configure it. When creating your own build, you presumably include fragments found in build/make/target to set up standard apps. However, if you look into it, where to start isn't clear. At least it wasn't to me. Reading the mk files gets unwieldy with all the indirection so I created a small script to graph out how things are included.

Graph of Android 9 Build Includes

Here, it's easier to see which parts are at the top of the tree and how one include affects another.

The Python code to generate the graph is:

#!/usr/bin/env python
# Convert AOSP build include egrep output to visual graph via graphviz
# ---
# In aosp/build/make/, run something like:
#  egrep -R '(inherit|include)' target/ | $DEV/tools/ | dot -Tpng > build-includes.png

import re
import sys

lines = sys.stdin.readlines()

nodes = {}
pairs = []
for l in lines:
    l = l.replace("$(SRC_TARGET_DIR)", "target")
    l = l.replace("build/make/target", "target")
    m = re.match('^([-\.\w/_]+):(([\s ,-]|include|\$\(call|inherit|product|if|exists))*([-\.\w/_]+\.mk)\)?', l)
    if m:
        mfile =
        minclude =
        for k in (mfile,minclude):
            if not k in nodes:
                nodes[k] = len(nodes)
                sys.stderr.write('Dropped line: {}\n'.format(l))

print("strict digraph includes {")
for (k,v) in nodes.items():
    print("\tn{} [label=\"{}\" shape=box]".format(v, k))

for (mfile,minclude) in pairs:
    print("\tn{} -> n{}").format(nodes[mfile], nodes[minclude])


Thought I'd whip this up while I was waiting for the build system to complete my build. Hope it's helpful to someone.