Chapter 2: Source Code and Build System¶
The Android Open Source Project ships hundreds of millions of lines of code across thousands of Git repositories. Building it demands a bespoke toolchain that has evolved over more than a decade, from recursive GNU Make, to the Soong/Blueprint meta-build system, and most recently toward Bazel. This chapter walks through the entire pipeline: fetching the source, understanding the three layers of the build system, configuring a product, defining modules, producing images, and running the result on an emulator.
Every path and code snippet in this chapter was verified against the AOSP
android16-qpr2-release branch. Where we quote source files, we give their
location relative to the tree root so you can read along on your own checkout.
2.1 Getting the Source¶
2.1.1 Prerequisites and Hardware Requirements¶
Before you can fetch AOSP, your workstation needs to meet some baseline requirements:
| Resource | Minimum | Recommended |
|---|---|---|
| Disk space (source only) | 250 GB | 400 GB (with one build output) |
| Disk space (with build) | 400 GB | 600 GB+ (SSD strongly preferred) |
| RAM | 32 GB | 64 GB+ |
| CPU | 4 cores | 16+ cores (build is highly parallel) |
| OS | Ubuntu 20.04+ / macOS (Intel or Apple Silicon) | Ubuntu 22.04 LTS |
| File system | Case-sensitive (ext4 on Linux) | ext4 or APFS (macOS) |
The build system requires a case-sensitive file system. On macOS, APFS is case-sensitive by default on separate volumes; on Linux ext4 is case-sensitive natively. Using NTFS or HFS+ (case-insensitive) will cause subtle failures.
You will also need the following packages on a Debian/Ubuntu host:
sudo apt-get install git-core gnupg flex bison build-essential \
zip curl zlib1g-dev libc6-dev-i386 x11proto-core-dev \
libx11-dev lib32z1-dev libgl1-mesa-dev libxml2-utils \
xsltproc unzip fontconfig python3
2.1.2 The repo Tool¶
AOSP is not a single Git repository. It is a collection of hundreds of Git
repositories stitched together by a tool called repo. repo is a Python
wrapper around Git that manages:
- Fetching and synchronizing many Git repositories at once
- Maintaining a manifest file that describes which repositories exist at which paths and which branches/tags they should track
- Providing convenience commands for topic branches, uploading code reviews to Gerrit, and other multi-repository workflows
Install repo as follows:
# Create a directory for the repo tool
mkdir -p ~/bin
export PATH=~/bin:$PATH
# Download the latest repo launcher
curl https://storage.googleapis.com/git-repo-downloads/repo > ~/bin/repo
chmod a+x ~/bin/repo
The repo launcher is a small bootstrap script. On first use it downloads
the full repo implementation from https://gerrit.googlesource.com/git-repo.
Why not a single Git repository? Git scales poorly to repositories with millions of files. Even with Git's efficient object storage, cloning, branching, and status checks become painfully slow on a monolithic repo of AOSP's size. The multi-repository approach also enables:
- Independent project history: Each subsystem (framework, kernel, external libraries) has its own commit history and can be branched independently.
- Selective checkouts: You can sync only the parts of the tree you need.
- Access control: Different repositories can have different owners and review requirements.
- Forking and rebasing: When AOSP imports upstream projects (e.g., BoringSSL, ICU, LLVM), it is cleaner to keep them as separate repositories.
The key repo subcommands you should know:
| Command | Purpose |
|---|---|
repo init |
Initialize a new workspace |
repo sync |
Fetch and update all repositories |
repo start |
Start a new topic branch across repos |
repo upload |
Upload changes for code review (Gerrit) |
repo status |
Show working tree status across all repos |
repo diff |
Show unified diff across all repos |
repo forall |
Run a command in every repository |
repo info |
Show information about the manifest |
repo manifest |
Output the resolved manifest |
repo branches |
Show existing topic branches |
repo prune |
Delete merged topic branches |
Some examples of repo forall:
# Find all repositories that have uncommitted changes
repo forall -c 'git status --short' | grep -v "^$"
# Count total lines of code across all C/C++ files
repo forall -c 'find . -name "*.cpp" -o -name "*.c" -o -name "*.h" \
| xargs wc -l 2>/dev/null' | tail -1
# Run git gc in every repository (compact storage)
repo forall -c 'git gc --auto'
2.1.3 Initializing a Workspace: repo init¶
The first step to getting the source is initializing a workspace directory:
mkdir aosp && cd aosp
# Initialize with a specific branch
repo init -u https://android.googlesource.com/platform/manifest \
-b android16-qpr2-release
Key flags for repo init:
| Flag | Purpose |
|---|---|
-u URL |
Manifest repository URL |
-b BRANCH |
Branch or tag to check out |
-m MANIFEST |
Manifest file within the repository (default: default.xml) |
--depth=N |
Shallow clone depth (saves disk/time) |
--partial-clone |
Use Git partial clones (downloads blobs on demand) |
--clone-filter=blob:limit=10M |
Only fetch blobs under 10 MB eagerly |
-g GROUP |
Sync only projects in a specific group |
--repo-rev=REV |
Pin the repo tool itself to a specific version |
After repo init, a .repo/ directory is created in your workspace:
aosp/
.repo/
manifests/ <-- The manifest Git repository
default.xml <-- The primary manifest file
GLOBAL-PREUPLOAD.cfg
manifests.git/ <-- Bare clone of the manifest repo
manifest.xml <-- Symlink to the active manifest
repo/ <-- The repo tool's own source code
project.list <-- Cached list of project paths
project-objects/ <-- Shared bare Git repos (if using --reference)
projects/ <-- Bare Git repos for each project
2.1.4 The Manifest File¶
The manifest file is the single source of truth for what repositories make up the tree and where they go. Understanding the manifest is crucial because it defines the shape of your entire source tree -- which projects exist, which branches they track, and how they are organized into directories.
The current AOSP default manifest at
.repo/manifests/default.xml (1,045 lines) begins:
<?xml version="1.0" encoding="UTF-8"?>
<manifest>
<remote name="aosp"
fetch=".."
review="https://android-review.googlesource.com/" />
<default revision="android16-qpr2-release"
remote="aosp"
sync-j="4" />
<superproject name="platform/superproject" remote="aosp"
revision="android-latest-release"/>
<contactinfo bugurl="go/repo-bug" />
<!-- BEGIN open-source projects -->
<project path="build/make" name="platform/build" groups="pdk,sysui-studio" >
<linkfile src="CleanSpec.mk" dest="build/CleanSpec.mk" />
<linkfile src="buildspec.mk.default" dest="build/buildspec.mk.default" />
<linkfile src="core" dest="build/core" />
<linkfile src="envsetup.sh" dest="build/envsetup.sh" />
<linkfile src="target" dest="build/target" />
<linkfile src="tools" dest="build/tools" />
</project>
<project path="build/blueprint" name="platform/build/blueprint"
groups="pdk,tradefed" />
<project path="build/soong" name="platform/build/soong"
groups="pdk,tradefed,sysui-studio" >
<linkfile src="root.bp" dest="Android.bp" />
<linkfile src="bootstrap.bash" dest="bootstrap.bash" />
</project>
...
</manifest>
Source: .repo/manifests/default.xml
Key elements of the manifest:
| Element | Purpose |
|---|---|
<remote> |
Defines a Git server (name, fetch URL, Gerrit review URL) |
<default> |
Sets default revision, remote, and sync parallelism |
<project> |
One Git repository: maps name (server-side) to path (local) |
<linkfile> |
Creates a symlink after checkout (used heavily by build/make) |
<copyfile> |
Copies a file after checkout |
<superproject> |
Points to a Git superproject for atomic snapshots |
<include> |
Includes another manifest fragment |
groups |
Assigns projects to groups for selective sync |
Notice the <linkfile> entries for build/make: they create symlinks at
top-level paths like build/envsetup.sh, build/core/, and build/target/
so that legacy scripts can find them at their historical locations.
Also noteworthy: the build/soong project creates two critical symlinks:
root.bpbecomesAndroid.bpat the tree root (the entry point for Soong)bootstrap.bashbecomesbootstrap.bashat the tree root
The manifest also defines sync-j="4" in the <default> element, meaning
repo sync will use 4 parallel fetch threads by default. You can override
this on the command line with -j16 or higher for faster syncs on good network
connections.
Manifest Structure Deep Dive¶
A manifest file is hierarchical. Understanding each element in detail:
The <remote> element:
name: A label used by<project>elements to indicate which server hosts the repository.fetch: The base URL for fetching.".."means "relative to the manifest URL", so if the manifest is athttps://android.googlesource.com/platform/manifest, then".."resolves tohttps://android.googlesource.com/.review: The Gerrit code review server URL. This is used byrepo uploadto submit changes for review.
The <default> element:
revision: Default branch/tag for all projects that do not specify their own.remote: Default remote for all projects.sync-j: Default parallelism for sync operations.
The <project> element:
<project path="build/make" name="platform/build" groups="pdk,sysui-studio" >
<linkfile src="envsetup.sh" dest="build/envsetup.sh" />
</project>
path: Where in the local tree this repository is checked out.name: The repository name on the server (appended to the remote's fetch URL).groups: Comma-separated list of groups this project belongs to.revision: (optional) Override the default revision for this project.clone-depth: (optional) Shallow clone depth.- Children:
<linkfile>,<copyfile>,<annotation>.
The <superproject> element:
This points to a Git superproject that tracks the SHA-1 of every constituent repository at a specific point in time. It enables atomic snapshots of the entire tree, which is useful for reproducible builds and bisection.
Local Manifests¶
You can customize the manifest without modifying the original by creating
local manifest files in .repo/local_manifests/. For example, to add a
custom project:
<!-- .repo/local_manifests/my_projects.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<manifest>
<project path="vendor/mycompany"
name="mycompany/vendor"
remote="myremote"
revision="main" />
<remote name="myremote"
fetch="https://github.com/mycompany/" />
</manifest>
To remove a project defined in the upstream manifest:
This is particularly useful when OEMs or SoC vendors need to add their proprietary components to the tree without forking the upstream manifest.
2.1.5 Syncing the Source: repo sync¶
Once initialized, fetch all the source code:
For a full AOSP checkout this downloads approximately 100 GB of Git data (compressed). The first sync takes 1-3 hours depending on network speed. You can substantially reduce this with partial clones:
# Partial clone: blobs are fetched on demand
repo init -u https://android.googlesource.com/platform/manifest \
-b android16-qpr2-release \
--partial-clone \
--clone-filter=blob:limit=10M
repo sync -c -j16 --no-tags
Key flags for repo sync:
| Flag | Purpose |
|---|---|
-j N |
Number of parallel fetch jobs |
-c |
Fetch only the current branch (faster) |
--no-tags |
Skip fetching tags (saves time/space) |
--optimized-fetch |
Only fetch projects that changed |
--prune |
Remove stale branches |
-f |
Continue even if a project fails |
2.1.6 Partial Sync and Groups¶
You do not always need the entire tree. The manifest assigns projects to groups, and you can sync only specific groups:
# Sync only PDK (Platform Development Kit) projects
repo init -u https://android.googlesource.com/platform/manifest \
-b android16-qpr2-release \
-g pdk
repo sync -j16
Groups visible in the manifest include pdk, tradefed, cts, device,
vendor, and device-specific groups like yukawa and hikey.
You can also sync individual projects:
# Sync only the frameworks/base repository
repo sync frameworks/base
# Sync multiple specific projects
repo sync frameworks/base packages/apps/Settings system/core
Group operations:
# Sync everything EXCEPT device-specific projects
repo init ... -g default,-device
# Sync only PDK and tradefed groups
repo init ... -g pdk,tradefed
# List all projects and their groups
repo list -g
The group system works with both inclusion and exclusion. The special group
default includes projects that have no explicit group assigned. Prefixing
a group name with - excludes it.
Disk space savings with groups:
| Sync Configuration | Approximate Size |
|---|---|
| Full sync (all groups) | ~100 GB (Git data) |
PDK only (-g pdk) |
~60 GB |
| Minimal build system only | ~20 GB |
| Partial clone + current branch only | ~30 GB |
2.1.7 Working with Topic Branches¶
When developing across multiple repositories, repo provides topic branch
management:
# Start a topic branch in specific projects
repo start my-feature frameworks/base packages/apps/Settings
# Start a topic branch in all projects
repo start my-feature --all
# Check the status of all topic branches
repo branches
# Upload changes for code review
repo upload
# Delete merged topic branches
repo prune
The repo upload command packages your local commits and pushes them to
Gerrit for code review. Gerrit is Google's web-based code review tool, and
all AOSP contributions go through Gerrit at
https://android-review.googlesource.com/.
2.1.8 The Repository Layout¶
After a full sync, the top-level directory structure looks like this:
aosp/
Android.bp <-- Symlink to build/soong/root.bp
art/ <-- Android Runtime (ART)
bionic/ <-- C library, dynamic linker, libm
bootable/ <-- Recovery, bootloader libraries
build/ <-- Build system
blueprint/ <-- Blueprint parser and framework
make/ <-- Make-based build system (legacy + glue)
soong/ <-- Soong build system (Go)
pesto/ <-- Bazel experiments
release/ <-- Release configuration
cts/ <-- Compatibility Test Suite
dalvik/ <-- Dalvik (historical, mostly superseded by ART)
development/ <-- Developer tools and samples
device/ <-- Device-specific configuration
generic/ <-- Emulator targets (goldfish, cuttlefish)
google/ <-- Pixel and Google devices
external/ <-- Third-party open-source projects
frameworks/ <-- Android framework
base/ <-- Core framework (Java + native)
native/ <-- SurfaceFlinger, Binder, etc.
hardware/ <-- HAL definitions and implementations
kernel/ <-- Kernel build configuration and prebuilts
libcore/ <-- Core Java libraries (OpenJDK)
libnativehelper/ <-- JNI helper library
packages/ <-- System apps and services
apps/ <-- Settings, Launcher, Camera, etc.
modules/ <-- Mainline modules (APEX packages)
prebuilts/ <-- Prebuilt compilers, SDKs, tools
system/ <-- Low-level system components (init, adb, etc.)
tools/ <-- Various development tools
vendor/ <-- Vendor-specific code
The Android.bp file at the root is actually a symlink into build/soong/:
// build/soong/root.bp
// Soong finds all Android.bp and Blueprints files in the source tree,
// subdirs= and optional_subdirs= are obsolete and this file no longer
// needs a list of the top level directories that may contain Android.bp
// files.
Source: build/soong/root.bp
This seemingly empty file is important: it signals to Soong that this is the
root of the source tree, and that Soong should recursively discover all
Android.bp files beneath it.
2.2 Build System Architecture¶
2.2.1 Historical Evolution¶
The AOSP build system has gone through three major generations:
timeline
title AOSP Build System Evolution
2008-2015 : GNU Make
: Android.mk files
: Recursive make
: Slow, hard to maintain
2015-2020 : Soong (Make + Blueprint)
: Android.bp files
: Declarative modules
: Go-based build logic
2020-present : Soong + Bazel (experimental)
: Mixed builds
: bp2build conversion
: Remote Build Execution (RBE)
Generation 1: GNU Make (2008-2015).
The original Android build system was pure GNU Make. Every module was described
in an Android.mk file using Make variables and include directives. A typical
Android.mk file looked like:
# Legacy Android.mk format (still supported but deprecated)
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := libexample
LOCAL_SRC_FILES := example.cpp
LOCAL_SHARED_LIBRARIES := liblog libutils
LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
LOCAL_CFLAGS := -Wall -Werror
include $(BUILD_SHARED_LIBRARY)
The system worked but suffered from well-known Make problems:
- Slow incremental builds: Make had to re-evaluate the entire dependency graph on every invocation, parsing thousands of Makefile includes.
- Fragile variable scoping: Make variables are global by default, leading to subtle bugs when two modules accidentally shared a variable name.
- Difficulty with parallelism: Recursive Make is inherently sequential across directory boundaries.
- No dependency enforcement: Any Makefile could reference any variable from any other Makefile, making it impossible to reason about module boundaries.
- Poor error messages: When something went wrong in the deeply nested include chains, error messages were nearly indecipherable.
At its peak, the Make-based build system had over 10,000 Android.mk files
and took hours to parse before even starting compilation.
Generation 2: Soong/Blueprint (2015-present).
Google introduced Soong as a replacement, with a three-layer architecture
(described below). Modules are now declared in Android.bp files using a
simple, declarative, JSON-like syntax. The build logic itself is written in Go.
Make is still present as a thin glue layer for product configuration and image
assembly, but new modules should always be defined in Android.bp.
The migration from Make to Soong has been gradual: the androidmk tool
performs automated conversion, and both systems coexist. Over successive
Android releases, more modules have been converted. As of the current release,
the vast majority of platform modules use Android.bp.
The key insight behind Soong is the separation of declaration from logic.
In Make, the build file format is the programming language -- you declare
modules and write build logic in the same files. In Soong, the Android.bp
files are purely declarative (no conditionals, no loops), and all build logic
lives in Go code within the Soong binary. This makes Android.bp files much
simpler and less error-prone.
Generation 3: Bazel (2020-present, experimental).
Google has been working on migrating the build to Bazel, the open-source
version of their internal Blaze build system. This effort is tracked through
the build/pesto/ directory and tools like bp2build. As of the current
release, Bazel is used for kernel builds (Kleaf) and select experiments, but
the platform build remains Soong-driven.
The migration to Bazel is motivated by:
- Build hermeticity: Bazel sandboxes each build action, ensuring reproducibility.
- Remote execution: Build actions can be distributed across a cluster of machines.
- Content-addressable caching: Build results can be shared across developers, CI systems, and even different branches.
- Scalability: Bazel is designed for extremely large codebases (Google's internal monorepo has billions of lines of code).
However, migrating a build system as complex as AOSP's is a multi-year effort, and Soong will remain the primary build system for the foreseeable future.
2.2.2 The Three-Layer Architecture¶
The modern AOSP build system consists of three layers, each implemented in a different technology:
graph TB
subgraph "Layer 1: Blueprint"
BP[build/blueprint/]
BPC["context.go<br/>5,781 lines"]
BPP[parser/]
BPG[proptools/]
BP --> BPC
BP --> BPP
BP --> BPG
end
subgraph "Layer 2: Soong"
SG[build/soong/]
SGC[cc/ - C/C++ modules]
SGJ[java/ - Java modules]
SGA[apex/ - APEX modules]
SGR[rust/ - Rust modules]
SGP[python/ - Python modules]
SGF[filesystem/ - Image building]
SG --> SGC
SG --> SGJ
SG --> SGA
SG --> SGR
SG --> SGP
SG --> SGF
end
subgraph "Layer 3: Make Glue"
MK[build/make/]
MKC[core/ - Build logic]
MKT[target/ - Product configs]
MKL[tools/ - Utilities]
MK --> MKC
MK --> MKT
MK --> MKL
end
subgraph "Output"
NJ[Ninja Manifest]
IMG[system.img, vendor.img, ...]
end
BPP -->|parses| ABPF[Android.bp files]
BPC -->|drives| SG
SG -->|generates| NJ
MK -->|product config| SG
NJ -->|executes| IMG
style BP fill:#4a90d9,color:#fff
style SG fill:#50b848,color:#fff
style MK fill:#e8a838,color:#fff
style NJ fill:#888,color:#fff
Let us examine each layer in detail.
2.2.3 Layer 1: Blueprint (build/blueprint/)¶
Blueprint is the meta-build framework -- a Go library that provides the machinery for parsing module definition files, resolving dependencies, running mutators, and generating Ninja build rules. Blueprint is not Android-specific; it is a general-purpose tool.
The doc.go file in build/blueprint/ describes the framework:
// Blueprint is a meta-build system that reads in Blueprints files that
// describe modules that need to be built, and produces a Ninja
// (https://ninja-build.org/) manifest describing the commands that need
// to be run and their dependencies. Where most build systems use built-in
// rules or a domain-specific language to describe the logic how modules are
// converted to build rules, Blueprint delegates this to per-project build
// logic written in Go.
Source: build/blueprint/doc.go
The core of Blueprint is context.go (5,781 lines, ~89 KB), which defines the
Context struct -- the central state object that orchestrates the entire build
process through four phases:
// A Context contains all the state needed to parse a set of Blueprints files
// and generate a Ninja file. The process of generating a Ninja file proceeds
// through a series of four phases. Each phase corresponds with a some methods
// on the Context object
//
// Phase Methods
// ------------ -------------------------------------------
// 1. Registration RegisterModuleType, RegisterSingletonType
//
// 2. Parse ParseBlueprintsFiles, Parse
//
// 3. Generate ResolveDependencies, PrepareBuildActions
//
// 4. Write WriteBuildFile
Source: build/blueprint/context.go, lines 70-84
The four phases in detail:
graph LR
A[1. Registration] --> B[2. Parse]
B --> C[3. Generate]
C --> D[4. Write]
A -->|"RegisterModuleType<br/>RegisterSingletonType"| A
B -->|"Parse Android.bp<br/>files recursively"| B
C -->|"ResolveDependencies<br/>Run Mutators<br/>PrepareBuildActions"| C
D -->|"WriteBuildFile<br/>output: build.ninja"| D
style A fill:#4a90d9,color:#fff
style B fill:#50b848,color:#fff
style C fill:#e8a838,color:#fff
style D fill:#d94a4a,color:#fff
-
Registration: Module types (e.g.,
cc_binary,java_library) and singletons are registered with the Context. Each module type maps to a Go factory function. -
Parse: All
Android.bpfiles in the tree are discovered and parsed. The Blueprint parser reads the JSON-like syntax and populates Go structs using reflection. -
Generate: Dependencies between modules are resolved. Mutators run in registration order -- they can visit modules top-down or bottom-up to propagate information or split modules into variants (e.g., one per target architecture). Then each module generates its build actions.
-
Write: The accumulated build actions are serialized into a Ninja manifest file.
Key directories under build/blueprint/:
| Directory/File | Purpose |
|---|---|
context.go |
Core orchestration (5,781 lines) |
parser/ |
Blueprint file parser |
proptools/ |
Property reflection and manipulation utilities |
pathtools/ |
File path utilities and glob matching |
depset/ |
Dependency set implementation (like Bazel depsets) |
bpfmt/ |
Blueprint file formatter |
bpmodify/ |
Programmatic Blueprint file modification tool |
bootstrap/ |
Self-bootstrapping logic |
gobtools/ |
Go binary tools for serialization |
gotestmain/ |
Test main generator |
gotestrunner/ |
Test runner utilities |
metrics/ |
Build metrics and event handling |
incremental.go |
Incremental build support |
live_tracker.go |
Live file tracking for dependencies |
Blueprint Mutators¶
Mutators are one of the most important concepts in Blueprint. A mutator is a function that visits modules and can modify them. Mutators are used for:
- Variant creation: A single module declaration can be split into
multiple variants. For example, a
cc_libraryis split into device and host variants, and further into architecture variants (arm64, x86_64, etc.). - Dependency propagation: Information from one module can be pushed into its dependents (or vice versa).
- Property defaulting: Default values can be computed based on global build configuration.
Mutators run in a specific order:
graph LR
A["Pre-deps<br/>Mutators"] --> B["Dependency<br/>Resolution"]
B --> C["Post-deps<br/>Mutators"]
C --> D["Final-deps<br/>Mutators"]
D --> E["Generate<br/>Build Actions"]
style A fill:#4a90d9,color:#fff
style B fill:#50b848,color:#fff
style C fill:#e8a838,color:#fff
style D fill:#d94a4a,color:#fff
style E fill:#9b59b6,color:#fff
- Pre-deps mutators run before dependencies are resolved. They can add dependencies or create variants.
- Dependency resolution matches dependency names to actual modules.
- Post-deps mutators run after dependencies are resolved. They can access dependency information.
- Final-deps mutators run last, for late-stage modifications.
The APEX system, for example, uses post-deps mutators to create separate variants of libraries for each APEX they appear in:
// From build/soong/apex/apex.go
func RegisterPostDepsMutators(ctx android.RegisterMutatorsContext) {
ctx.BottomUp("apex_unique", apexUniqueVariationsMutator)
ctx.BottomUp("mark_platform_availability", markPlatformAvailability)
ctx.InfoBasedTransition("apex",
android.NewGenericTransitionMutatorAdapter(&apexTransitionMutator{}))
}
Source: build/soong/apex/apex.go, lines 64-70
Blueprint Providers¶
Providers are Blueprint's mechanism for passing information between modules. When a module generates build actions, it can set provider data that its dependents can then read. This is more structured than Make's global variables:
// Provider declaration (from build/soong/cc/cc.go)
var CcObjectInfoProvider = blueprint.NewProvider[CcObjectInfo]()
// Setting a provider (in the generating module)
ctx.SetProvider(CcObjectInfoProvider, CcObjectInfo{
ObjFiles: objFiles,
TidyFiles: tidyFiles,
KytheFiles: kytheFiles,
})
// Reading a provider (in a dependent module)
if info, ok := ctx.OtherModuleProvider(dep, CcObjectInfoProvider); ok {
// Use info.ObjFiles, etc.
}
2.2.4 Layer 2: Soong (build/soong/)¶
Soong is Android's build system proper. It is built on top of Blueprint,
registering Android-specific module types, mutators, and singletons. The
build/soong/ directory contains 74 subdirectories, organized by the type of
module or build functionality they handle.
From build/soong/README.md:
Soong is one of the build systems used in Android, which is controlled
by files called Android.bp. There is also the legacy Make-based build
system that is controlled by files called Android.mk.
Android.bp file are JSON-like declarative descriptions of "modules" to
build; a "module" is the basic unit of building that Soong understands,
similarly to how "target" is the basic unit of building for Make.
Source: build/soong/README.md, lines 1-8
The build logic is described further:
The build logic is written in Go using the blueprint framework.
Build logic receives module definitions parsed into Go structures
using reflection and produces build rules. The build rules are
collected by blueprint and written to a ninja build file.
Source: build/soong/README.md, lines 610-614
Key subdirectories of build/soong/:
| Directory | Purpose | Key Files |
|---|---|---|
cc/ |
C/C++ module types (cc_binary, cc_library, etc.) |
cc.go, library.go, binary.go |
java/ |
Java/Kotlin module types (java_library, android_app, etc.) |
java.go, app.go, sdk_library.go |
apex/ |
APEX module type (3,001 lines in apex.go) |
apex.go, builder.go, key.go |
rust/ |
Rust module types | rust.go, library.go |
python/ |
Python module types | python.go |
sh/ |
Shell script module types | sh_binary.go |
genrule/ |
Generic build rule modules | genrule.go |
android/ |
Core Soong framework (module base classes, arch handling) | module.go, arch.go, paths.go |
filesystem/ |
Image file building | filesystem.go |
ui/ |
Build UI and progress reporting | build.go |
cmd/ |
Command-line entry points | soong_build/, soong_ui/ |
bpf/ |
BPF program compilation | bpf.go |
sdk/ |
SDK snapshot generation | sdk.go |
snapshot/ |
Vendor snapshot management | snapshot.go |
linkerconfig/ |
Linker namespace configuration | linkerconfig.go |
aconfig/ |
Build flags (aconfig) integration | aconfig.go |
bin/ |
Shell scripts for m, mm, mmm, etc. |
m, mm, mmm |
kernel/ |
Kernel-related build logic | kernel.go |
Inside the Go Code: Module Registration¶
Each module type is registered with Soong by a Go init() function. Let us
look at how the three major module families register themselves:
C/C++ modules (build/soong/cc/cc.go, 4,778 lines):
// This file contains the module types for compiling C/C++ for Android,
// and converts the properties into the flags and filenames necessary to
// pass to the compiler. The final creation of the rules is handled in
// builder.go
package cc
Source: build/soong/cc/cc.go, lines 15-19
The C/C++ module system defines extensive data structures for tracking
compilation state. For example, the LinkerInfo struct captures all linking
dependencies:
type LinkerInfo struct {
WholeStaticLibs []string
StaticLibs []string // modules to statically link
SharedLibs []string // modules to dynamically link
HeaderLibs []string // header-only dependencies
SystemSharedLibs []string
...
}
Source: build/soong/cc/cc.go, lines 81-99
The cc/ directory contains over 30 Go files, each handling a different
aspect of C/C++ compilation:
| File | Purpose | Lines |
|---|---|---|
cc.go |
Core module types and properties | 4,778 |
builder.go |
Ninja rule generation | ~2,000 |
binary.go |
cc_binary implementation |
~500 |
library.go |
cc_library implementation |
~2,000 |
sanitize.go |
ASan/TSan/UBSan integration | ~1,500 |
ndk_sysroot.go |
NDK sysroot management | ~400 |
stl.go |
C++ STL selection | ~300 |
cmake_snapshot.go |
CMake project generation | ~400 |
check.go |
Build consistency checks | ~200 |
Java modules (build/soong/java/java.go, 4,070 lines):
// This file contains the module types for compiling Java for Android,
// and converts the properties into the flags and filenames necessary
// to pass to the Module. The final creation of the rules is handled
// in builder.go
package java
func registerJavaBuildComponents(ctx android.RegistrationContext) {
ctx.RegisterModuleType("java_defaults", DefaultsFactory)
ctx.RegisterModuleType("java_library", LibraryFactory)
ctx.RegisterModuleType("java_library_static", LibraryStaticFactory)
ctx.RegisterModuleType("java_library_host", LibraryHostFactory)
ctx.RegisterModuleType("java_binary", BinaryFactory)
ctx.RegisterModuleType("java_binary_host", BinaryHostFactory)
ctx.RegisterModuleType("java_test", TestFactory)
ctx.RegisterModuleType("java_test_helper_library", TestHelperLibraryFactory)
ctx.RegisterModuleType("java_test_host", TestHostFactory)
ctx.RegisterModuleType("java_test_import", JavaTestImportFactory)
ctx.RegisterModuleType("java_import", ImportFactory)
ctx.RegisterModuleType("java_import_host", ImportFactoryHost)
ctx.RegisterModuleType("java_device_for_host", DeviceForHostFactory)
ctx.RegisterModuleType("java_host_for_device", HostForDeviceFactory)
ctx.RegisterModuleType("dex_import", DexImportFactory)
ctx.RegisterModuleType("java_api_library", ApiLibraryFactory)
ctx.RegisterModuleType("java_api_contribution", ApiContributionFactory)
...
}
Source: build/soong/java/java.go, lines 50-70
Genrule modules (build/soong/genrule/genrule.go, 1,042 lines):
// A genrule module takes a list of source files ("srcs" property), an
// optional list of tools ("tools" property), and a command line ("cmd"
// property), to generate output files ("out" property).
package genrule
func RegisterGenruleBuildComponents(ctx android.RegistrationContext) {
ctx.RegisterModuleType("genrule_defaults", defaultsFactory)
ctx.RegisterModuleType("gensrcs", GenSrcsFactory)
ctx.RegisterModuleType("genrule", GenRuleFactory)
...
}
Source: build/soong/genrule/genrule.go, lines 15-67
The genrule module type is particularly useful for code generation, protocol
buffer compilation, AIDL interface generation, and any other case where you
need to run an arbitrary command to produce source files.
Soong Build Flow Internals¶
When soong_ui starts a build, it proceeds through these internal steps:
graph TB
subgraph "soong_ui (build driver)"
UI1[Parse command-line flags]
UI2[Set up logging & metrics]
UI3[Check environment]
UI4[Run Soong phase]
UI5[Run Kati phase]
UI6[Combine ninja files]
UI7[Run Ninja]
UI8[Report results]
UI1 --> UI2 --> UI3 --> UI4 --> UI5 --> UI6 --> UI7 --> UI8
end
subgraph "soong_build (Soong proper)"
SB1[Bootstrap: compile soong_build itself]
SB2[Discover all Android.bp files]
SB3[Parse Android.bp files into AST]
SB4[Instantiate Go module objects]
SB5[Run pre-deps mutators]
SB6[Resolve all dependencies]
SB7[Run post-deps mutators]
SB8[Run final-deps mutators]
SB9["Call GenerateAndroidBuildActions<br/>on every module"]
SB10[Write out/soong/build.ninja]
SB1 --> SB2 --> SB3 --> SB4 --> SB5 --> SB6 --> SB7 --> SB8 --> SB9 --> SB10
end
UI4 --> SB1
style UI4 fill:#4a90d9,color:#fff
style SB1 fill:#50b848,color:#fff
style SB9 fill:#e8a838,color:#fff
The key step is GenerateAndroidBuildActions. This is the method that every module type must implement. It examines the module's properties, resolves its dependencies, and emits Ninja build rules (compile commands, link commands, file copies, etc.).
The entry point for the build is build/soong/soong_ui.bash:
#!/bin/bash -eu
source $(cd $(dirname $BASH_SOURCE) &> /dev/null && pwd)/../make/shell_utils.sh
require_top
# To track how long we took to startup.
case $(uname -s) in
Darwin)
export TRACE_BEGIN_SOONG=`$TOP/prebuilts/build-tools/path/darwin-x86/date +%s%3N`
;;
*)
export TRACE_BEGIN_SOONG=$(date +%s%N)
;;
esac
setup_cog_env_if_needed
set_network_file_system_type_env_var
# Save the current PWD for use in soong_ui
export ORIGINAL_PWD=${PWD}
export TOP=$(gettop)
source ${TOP}/build/soong/scripts/microfactory.bash
soong_build_go soong_ui android/soong/cmd/soong_ui
soong_build_go mk2rbc android/soong/mk2rbc/mk2rbc
soong_build_go rbcrun rbcrun/rbcrun
soong_build_go release-config android/soong/cmd/release_config/release_config
cd ${TOP}
exec "$(getoutdir)/soong_ui" "$@"
Source: build/soong/soong_ui.bash
This script bootstraps the Go-based build system: it first compiles soong_ui
(the build driver) and several helper tools, then executes soong_ui which
orchestrates the entire build.
2.2.5 Layer 3: Make Glue (build/make/)¶
Although Soong handles module compilation, GNU Make (via Kati, a Make clone optimized for Android) still plays an important role:
- Product configuration:
PRODUCT_*variables,BoardConfig.mk, and device makefiles are still written in Make. - Image assembly: The rules for combining compiled artifacts into partition
images (
system.img,vendor.img, etc.) are in Make. - Legacy modules: Some modules still use
Android.mk(though this is decreasing with every release).
The build/make/ directory contains 25 top-level entries:
| Directory/File | Purpose |
|---|---|
core/ |
Core build logic (includes, rules, module definitions) |
target/ |
Product and board configuration files |
tools/ |
Build utilities (releasetools, signapk, etc.) |
envsetup.sh |
Shell environment setup script (1,187 lines) |
common/ |
Shared build logic |
packaging/ |
Package assembly rules |
Changes.md |
Build system change log |
shell_utils.sh |
Shell utility functions |
The relationship between these layers during a build is:
sequenceDiagram
participant Dev as Developer
participant UI as soong_ui
participant SB as soong_build
participant BP as Blueprint
participant Kati as Kati (Make)
participant Ninja as Ninja
Dev->>UI: m droid (or just m)
UI->>SB: Run Soong build phase
SB->>BP: Parse all Android.bp files
BP-->>SB: Module definitions
SB->>SB: Run mutators
SB->>SB: Generate build rules
SB-->>UI: out/soong/build.ninja
UI->>Kati: Run Make phase
Kati->>Kati: Parse Android.mk files
Kati->>Kati: Process product config
Kati-->>UI: out/build-<product>.ninja
UI->>UI: Combine ninja files
UI->>Ninja: Execute combined ninja
Ninja->>Ninja: Build all targets
Ninja-->>Dev: out/target/product/<device>/
2.2.6 The Soong README: Module Definitions¶
The Soong README (build/soong/README.md) provides the authoritative reference
for Android.bp syntax. Here are the key elements it documents.
Module structure:
The README states: "Every module must have a name property, and the value
must be unique across all Android.bp files."
Source: build/soong/README.md, lines 43-48
Variables:
gzip_srcs = ["src/test/minigzip.c"],
cc_binary {
name: "gzip",
srcs: gzip_srcs,
shared_libs: ["libz"],
stl: "none",
}
"Variables are scoped to the remainder of the file they are declared in, as well as any child Android.bp files. Variables are immutable with one exception -- they can be appended to with a += assignment, but only before they have been referenced."
Source: build/soong/README.md, lines 76-91
Types supported:
| Type | Syntax |
|---|---|
| Bool | true or false |
| Integer | 42 |
| String | "hello" |
| List of strings | ["a", "b", "c"] |
| Map | {key1: "val", key2: ["val2"]} |
Comments: Both /* */ and // styles are supported.
Defaults modules:
cc_defaults {
name: "gzip_defaults",
shared_libs: ["libz"],
stl: "none",
}
cc_binary {
name: "gzip",
defaults: ["gzip_defaults"],
srcs: ["src/test/minigzip.c"],
}
Source: build/soong/README.md, lines 126-142
Defaults modules allow sharing properties across multiple module definitions, reducing duplication.
2.3 envsetup.sh and lunch¶
2.3.1 Sourcing envsetup.sh¶
Every AOSP build session begins by sourcing the environment setup script:
This script lives at build/make/envsetup.sh (1,187 lines) and is symlinked
to the top-level build/envsetup.sh via the manifest's <linkfile> directive.
The script does the following on load:
- Locates the tree root using the
_gettop_oncefunction:
function _gettop_once
{
local TOPFILE=build/make/core/envsetup.mk
if [ -n "$TOP" -a -f "$TOP/$TOPFILE" ] ; then
# The following circumlocution ensures we remove symlinks from TOP.
(cd "$TOP"; PWD= /bin/pwd)
else
if [ -f $TOPFILE ] ; then
PWD= /bin/pwd
else
local HERE=$PWD
local T=
while [ \( ! \( -f $TOPFILE \) \) -a \( "$PWD" != "/" \) ]; do
\cd ..
T=`PWD= /bin/pwd -P`
done
\cd "$HERE"
if [ -f "$T/$TOPFILE" ]; then
echo "$T"
fi
fi
fi
}
Source: build/make/envsetup.sh, lines 18-43
The function walks up the directory tree looking for build/make/core/envsetup.mk
as a sentinel file. This is the canonical way the build system identifies the
root of an AOSP checkout.
-
Sources
shell_utils.sh: Imports common shell utilities. -
Sets global paths via
set_global_paths():
function set_global_paths()
{
...
ANDROID_GLOBAL_BUILD_PATHS=$T/build/soong/bin
ANDROID_GLOBAL_BUILD_PATHS+=:$T/build/bazel/bin
ANDROID_GLOBAL_BUILD_PATHS+=:$T/development/scripts
ANDROID_GLOBAL_BUILD_PATHS+=:$T/prebuilts/devtools/tools
# add kernel specific binaries
if [ $(uname -s) = Linux ] ; then
ANDROID_GLOBAL_BUILD_PATHS+=:$T/prebuilts/misc/linux-x86/dtc
ANDROID_GLOBAL_BUILD_PATHS+=:$T/prebuilts/misc/linux-x86/libufdt
fi
...
export PATH=$ANDROID_GLOBAL_BUILD_PATHS:$PATH
}
Source: build/make/envsetup.sh, lines 259-317
This adds build tools, Bazel binaries, emulator prebuilts, and device tree
compiler (dtc) to PATH.
- Sources vendor setup scripts via
source_vendorsetup():
function source_vendorsetup() {
...
for dir in device vendor product; do
for f in $(cd "$T" && test -d $dir && \
find -L $dir -maxdepth 4 -name 'vendorsetup.sh' 2>/dev/null \
| sort); do
if [[ -z "$allowed" || "$allowed_files" =~ $f ]]; then
echo "including $f"; . "$T/$f"
else
echo "ignoring $f, not in $allowed"
fi
done
done
...
}
Source: build/make/envsetup.sh, lines 1061-1090
This discovers and executes vendorsetup.sh files under device/, vendor/,
and product/ directories. These scripts typically add device-specific lunch
combos or set up vendor-specific environment variables.
-
Adds shell completions via
addcompletions()for commands likelunch,m,adb, andfastboot. -
Optionally restores previous lunch if
USE_LEFTOVERS=1is set.
2.3.2 Key Functions Defined by envsetup.sh¶
The script defines many shell functions that become available after sourcing. Here are the most important ones:
| Function | Purpose |
|---|---|
lunch |
Select build target (product, release, variant) |
tapas |
Configure unbundled app build |
banchan |
Configure unbundled APEX build |
m |
Build from the top of the tree (delegates to soong_ui.bash) |
mm |
Build modules in the current directory |
mmm |
Build modules in specified directories |
croot |
cd to the top of the tree |
gomod |
cd to a specific module's directory |
godir |
cd to a directory matching a pattern |
adb |
Wrapper that ensures tree's adb is used |
fastboot |
Wrapper that ensures tree's fastboot is used |
make |
Redirects to soong_ui.bash --make-mode |
printconfig |
Display current build configuration |
leftovers |
Restore previous lunch selection |
The make function is notable -- it intercepts the system make command:
function get_make_command()
{
# If we're in the top of an Android tree, use soong_ui.bash instead of make
if [ -f build/soong/soong_ui.bash ]; then
# Always use the real make if -C is passed in
for arg in "$@"; do
if [[ $arg == -C* ]]; then
echo command make
return
fi
done
echo build/soong/soong_ui.bash --make-mode
else
echo command make
fi
}
function make()
{
_wrap_build $(get_make_command "$@") "$@"
}
Source: build/make/envsetup.sh, lines 1010-1030
This means that typing make in an AOSP tree actually invokes soong_ui.bash
--make-mode, not GNU Make directly.
2.3.3 The lunch Command¶
lunch is the pivotal command that selects your build target. It sets three
fundamental variables:
| Variable | Purpose | Example |
|---|---|---|
TARGET_PRODUCT |
Which device/product to build for | aosp_arm64 |
TARGET_RELEASE |
Release configuration | trunk_staging |
TARGET_BUILD_VARIANT |
Build variant (eng/userdebug/user) | eng |
The lunch function supports two formats:
# New format (recommended): positional arguments
lunch aosp_arm64 trunk_staging eng
# Legacy format: dash-separated
lunch aosp_arm64-trunk_staging-eng
If release and variant are omitted, they default to trunk_staging and eng
respectively:
function lunch()
{
...
# Handle the new format.
if [[ -z $legacy ]]; then
product=$1
release=$2
if [[ -z $release ]]; then
release=trunk_staging
fi
variant=$3
if [[ -z $variant ]]; then
variant=eng
fi
fi
# Validate the selection and set all the environment stuff
_lunch_meat $product $release $variant
...
}
Source: build/make/envsetup.sh, lines 550-596
The _lunch_meat function does the heavy lifting:
function _lunch_meat()
{
local product=$1
local release=$2
local variant=$3
TARGET_PRODUCT=$product \
TARGET_RELEASE=$release \
TARGET_BUILD_VARIANT=$variant \
TARGET_BUILD_APPS= \
build_build_var_cache
if [ $? -ne 0 ]
then
if [[ "$product" =~ .*_(eng|user|userdebug) ]]
then
echo "Did you mean -${product/*_/}? (dash instead of underscore)"
fi
return 1
fi
export TARGET_PRODUCT=$(_get_build_var_cached TARGET_PRODUCT)
export TARGET_BUILD_VARIANT=$(_get_build_var_cached TARGET_BUILD_VARIANT)
export TARGET_RELEASE=$release
export TARGET_BUILD_TYPE=release
export TARGET_BUILD_APPS=
set_stuff_for_environment
...
}
Source: build/make/envsetup.sh, lines 447-491
This function:
- Invokes
soong_ui.bash --dumpvars-modeto resolve and cache build variables - Exports
TARGET_PRODUCT,TARGET_BUILD_VARIANT,TARGET_RELEASE, andTARGET_BUILD_TYPE - Calls
set_stuff_for_environment(), which sets upPATH,JAVA_HOME,ANDROID_PRODUCT_OUT, and other environment variables - Prints the current configuration
2.3.4 Build Variants¶
The three build variants control what is included and how it is built:
| Variant | Description | ro.debuggable |
adb |
Optimizations |
|---|---|---|---|---|
user |
Production build. Limited access. | 0 |
Off by default | Full |
userdebug |
Like user, but with root access and debug tools. | 1 |
On | Full |
eng |
Development build. Extra tools, no optimization. | 1 |
On | Reduced |
The variant is used to select which packages are installed. For example,
eng-only packages include development tools like strace, while user
builds exclude them.
2.3.5 envsetup.mk and config.mk¶
After lunch sets the environment variables, the build system's Make layer
reads them through build/make/core/envsetup.mk and build/make/core/config.mk.
envsetup.mk establishes fundamental build variables:
# Variables we check:
# HOST_BUILD_TYPE = { release debug }
# TARGET_BUILD_TYPE = { release debug }
# and we output a bunch of variables, see the case statement at
# the bottom for the full list
# OUT_DIR is also set to "out" if it's not already set.
# ...
# The product defaults to generic on hardware
ifeq ($(TARGET_PRODUCT),)
TARGET_PRODUCT := aosp_arm64
endif
# the variant -- the set of files that are included for a build
ifeq ($(strip $(TARGET_BUILD_VARIANT)),)
TARGET_BUILD_VARIANT := eng
endif
Source: build/make/core/envsetup.mk, lines 1-85
It also detects the host configuration:
# HOST_OS
ifneq (,$(findstring Linux,$(UNAME)))
HOST_OS := linux
endif
ifneq (,$(findstring Darwin,$(UNAME)))
HOST_OS := darwin
endif
# HOST_ARCH
ifneq (,$(findstring x86_64,$(UNAME)))
HOST_ARCH := x86_64
HOST_2ND_ARCH := x86
HOST_IS_64_BIT := true
endif
Source: build/make/core/envsetup.mk, lines 122-183
And defines the partition output directories:
TARGET_COPY_OUT_SYSTEM := system
TARGET_COPY_OUT_SYSTEM_DLKM := system_dlkm
TARGET_COPY_OUT_DATA := data
TARGET_COPY_OUT_VENDOR := $(_vendor_path_placeholder)
TARGET_COPY_OUT_PRODUCT := $(_product_path_placeholder)
TARGET_COPY_OUT_SYSTEM_EXT := $(_system_ext_path_placeholder)
TARGET_COPY_OUT_ODM := $(_odm_path_placeholder)
Source: build/make/core/envsetup.mk, lines 254-289
config.mk is the top-level configuration include. It starts with a guard
that prevents direct invocation:
ifndef KATI
$(warning Directly using config.mk from make is no longer supported.)
$(warning )
$(warning If you are just attempting to build, you probably need to re-source envsetup.sh:)
$(warning )
$(warning $$ source build/envsetup.sh)
$(error done)
endif
BUILD_SYSTEM :=$= build/make/core
BUILD_SYSTEM_COMMON :=$= build/make/common
Source: build/make/core/config.mk, lines 1-22
The ifndef KATI guard tells us an important detail: the Make-based build does
not use standard GNU Make. It uses Kati, a Make implementation written in
Go that is faster and more compatible with Android's build patterns.
2.3.6 Kati: The Make Replacement¶
Kati (build/kati/ in older trees, now part of the prebuilts) is Google's
Make-compatible build tool. It was created to address the performance problems
with GNU Make on the Android build:
- Faster parsing: Kati parses Makefiles much faster than GNU Make.
- Better caching: Kati caches parsed Makefile results between invocations.
- Ninja generation: Rather than executing build commands directly, Kati generates a Ninja manifest, which Ninja then executes.
- Compatibility: Kati aims to be a drop-in replacement for GNU Make, though it intentionally does not support some rarely-used Make features.
In the AOSP build, Kati handles:
- Product configuration (
PRODUCT_*variables) - Board configuration (
BOARD_*variables) - Image assembly rules
- Remaining
Android.mkmodules
The output of Kati is out/build-<TARGET_PRODUCT>.ninja, which is combined
with Soong's out/soong/build.ninja into a single out/combined-<TARGET_PRODUCT>.ninja
that Ninja executes.
2.3.7 How Build Variables Flow¶
Understanding the flow of build variables is essential for debugging build configuration issues:
graph TB
subgraph "Shell Environment"
ENV["TARGET_PRODUCT<br/>TARGET_BUILD_VARIANT<br/>TARGET_RELEASE"]
end
subgraph "soong_ui"
SUI["Reads environment<br/>variables"]
end
subgraph "Soong (Go)"
SGO["Reads config via<br/>soong.<TARGET_PRODUCT>.variables"]
end
subgraph "Kati (Make)"
KAT["envsetup.mk reads<br/>TARGET_PRODUCT etc."]
PC["product_config.mk<br/>resolves product MK files"]
BC["board_config.mk<br/>loads BoardConfig.mk"]
KAT --> PC --> BC
end
subgraph "Ninja"
NIN["Receives resolved<br/>variables as build<br/>rule parameters"]
end
ENV -->|lunch exports| SUI
SUI -->|passes to| SGO
SUI -->|passes to| KAT
SGO -->|soong/build.ninja| NIN
BC -->|build-product.ninja| NIN
style ENV fill:#4a90d9,color:#fff
style SGO fill:#50b848,color:#fff
style KAT fill:#e8a838,color:#fff
style NIN fill:#d94a4a,color:#fff
The variable resolution chain in the Make layer is:
build/make/core/config.mkis the top-level entry point- It includes
build/make/core/envsetup.mk, which readsTARGET_PRODUCTandTARGET_BUILD_VARIANTfrom the environment envsetup.mkincludesproduct_config.mk, which finds and loads the product's makefile (e.g.,build/make/target/product/aosp_arm64.mk)- The product makefile uses
inherit-productto pull in base configurations board_config.mklocates and loadsBoardConfig.mkfor the device- All resolved variables are then available for image assembly and as inputs
to Soong via
soong.<TARGET_PRODUCT>.variables(e.g.out/soong/soong.aosp_cf_x86_64_phone.variablesfor anaosp_cf_x86_64_phone-trunk_staging-userdebuglunch combo)
The key variable resolution happens in envsetup.mk:
# Read the product specs so we can get TARGET_DEVICE and other
# variables that we need in order to locate the output files.
include $(BUILD_SYSTEM)/product_config.mk
SDK_HOST_ARCH := x86
TARGET_OS := linux
# Some board configuration files use $(PRODUCT_OUT)
TARGET_OUT_ROOT := $(OUT_DIR)/target
TARGET_PRODUCT_OUT_ROOT := $(TARGET_OUT_ROOT)/product
PRODUCT_OUT := $(TARGET_PRODUCT_OUT_ROOT)/$(TARGET_DEVICE)
include $(BUILD_SYSTEM)/board_config.mk
Source: build/make/core/envsetup.mk, lines 349-368
This is where PRODUCT_OUT -- the directory where all build outputs for the
target device go -- is computed. For aosp_arm64, this resolves to
out/target/product/generic_arm64/.
2.3.8 The tapas and banchan Commands¶
In addition to lunch, envsetup.sh provides two specialized commands for
unbundled builds:
tapas -- Build unbundled apps:
# Build the Camera app for ARM64
tapas Camera arm64 eng
# Build multiple apps
tapas Camera Gallery arm64 userdebug
The tapas function (build/make/envsetup.sh, lines 676-747) configures an
unbundled app build. It sets TARGET_BUILD_APPS to the specified app names,
which tells the build system to only build those apps (and their dependencies)
rather than the entire platform.
banchan -- Build unbundled APEXes:
# Build the Wi-Fi APEX for ARM64
banchan com.android.wifi arm64 eng
# Build multiple APEXes
banchan com.android.wifi com.android.bt arm64 userdebug
The banchan function (build/make/envsetup.sh, lines 749-811) is similar
to tapas but specialized for APEX modules. It uses module_arm64 (or the
appropriate architecture variant) as the product, since APEXes are largely
device-independent.
Both commands are useful for:
- Mainline module development (working on a specific APEX)
- App development within the AOSP tree
- Faster builds (only building what you need)
- CI/CD pipelines that test individual modules
2.3.9 The leftovers Command¶
The leftovers command restores your previous lunch selection:
function leftovers()
{
...
local dot_leftovers="$(getoutdir)/.leftovers"
...
local product release variant
IFS=" " read -r product release variant < "$dot_leftovers"
echo "$INFO: Loading previous lunch: $product $release $variant"
lunch $product $release $variant
}
Source: build/make/envsetup.sh, lines 598-642
When you run lunch, it saves your selection to out/.leftovers. The next
time you source envsetup.sh, you can either:
- Run
leftoversmanually to restore the previous selection - Set
USE_LEFTOVERS=1in your shell profile to auto-restore
This is particularly useful when you are always building for the same target and do not want to type the full lunch command every time.
2.4 Android.bp Module Definitions¶
2.4.1 The Blueprint Language¶
Android.bp files use a simple, declarative syntax that intentionally avoids
conditionals and control flow. As the Soong README explains:
"By design, Android.bp files are very simple. There are no conditionals or control flow statements -- any complexity is handled in build logic written in Go."
Source: build/soong/README.md, lines 27-28
This design decision pushes complexity into the build system's Go code, where it can be properly tested and maintained, rather than scattering it across thousands of build files.
2.4.2 Module Types¶
AOSP defines dozens of module types. The most commonly used are:
C/C++ Module Types (defined in build/soong/cc/):
| Module Type | Purpose |
|---|---|
cc_binary |
Native executable |
cc_library |
Native shared and/or static library |
cc_library_shared |
Shared library only (.so) |
cc_library_static |
Static library only (.a) |
cc_library_headers |
Header-only library |
cc_test |
Native test executable (gtest) |
cc_benchmark |
Native benchmark (google-benchmark) |
cc_defaults |
Shared defaults for cc modules |
cc_prebuilt_binary |
Prebuilt native binary |
cc_prebuilt_library_shared |
Prebuilt shared library |
Java/Kotlin Module Types (defined in build/soong/java/):
| Module Type | Purpose |
|---|---|
java_library |
Java library (.jar) |
java_library_static |
Static Java library |
android_library |
Android library (aar) |
android_app |
Android application (APK) |
android_test |
Android instrumentation test |
java_defaults |
Shared defaults for Java modules |
java_sdk_library |
SDK library with stubs |
Other Important Module Types:
| Module Type | Defined In | Purpose |
|---|---|---|
apex |
build/soong/apex/ |
APEX module package |
apex_key |
build/soong/apex/ |
Signing key for APEX |
rust_binary |
build/soong/rust/ |
Rust executable |
rust_library |
build/soong/rust/ |
Rust library |
python_binary_host |
build/soong/python/ |
Python host tool |
sh_binary |
build/soong/sh/ |
Shell script binary |
genrule |
build/soong/genrule/ |
Custom build rule |
filegroup |
build/soong/android/ |
Group of source files |
prebuilt_etc |
build/soong/etc/ |
File installed to /etc |
bpf |
build/soong/bpf/ |
BPF program |
You can generate a complete, current list of module types and their properties by running:
2.4.3 The package Module¶
Each directory with an Android.bp file forms a package. You can control
package-level settings with the package module:
package {
default_team: "trendy_team_android_settings_app",
default_applicable_licenses: ["packages_apps_Settings_license"],
default_visibility: [":__subpackages__"],
}
Source: packages/apps/Settings/Android.bp, lines 1-4
The package module does not have a name property -- its name is
automatically set to the path of its directory. Package-level settings include:
default_visibility: Controls what other packages can see modules in this package.default_applicable_licenses: Specifies the license that applies to all modules in this package.default_team: The team responsible for this package (used for code ownership tracking).
2.4.4 The license Module¶
AOSP requires license declarations for all modules. The license module type
declares the licensing terms:
license {
name: "packages_apps_Settings_license",
visibility: [":__subpackages__"],
license_kinds: [
"SPDX-license-identifier-Apache-2.0",
],
license_text: [
"NOTICE",
],
}
Source: packages/apps/Settings/Android.bp, lines 8-17
This ensures that the build system can track which licenses apply to every binary and library, enabling automated compliance checking.
2.4.5 The filegroup Module¶
Filegroups provide a way to name a collection of source files so they can be referenced from other modules:
Source: packages/modules/Wifi/apex/Android.bp, lines 54-57
Other modules can reference this filegroup using the :name syntax in their
srcs or other file-list properties.
2.4.6 The genrule Module¶
The genrule module type runs arbitrary commands to generate source files:
genrule {
name: "statslog-settings-java-gen",
tools: ["stats-log-api-gen"],
cmd: "$(location stats-log-api-gen) --java $(out) --module settings" +
" --javaPackage com.android.settings.core.instrumentation" +
" --javaClass SettingsStatsLog",
out: ["com/android/settings/core/instrumentation/SettingsStatsLog.java"],
}
Source: packages/apps/Settings/Android.bp, lines 24-30
Key genrule properties:
tools: Host tools used by the command (resolved to their output paths)tool_files: Additional tool input filessrcs: Input source filescmd: The command to run. Special variables:$(location <tool>): Path to a tool binary$(in): All input files$(out): All output files$(genDir): The output directoryout: List of output files (relative to genDir)
The gensrcs variant runs the command once per input file, which is useful
for batch transformations.
2.4.7 Walkthrough: A C/C++ Module¶
The Soong README includes a canonical example:
Source: build/soong/README.md, lines 35-41
Let us examine the common properties for C/C++ modules:
cc_library_shared {
name: "libexample",
// Source files -- supports globs and path expansions
srcs: [
"src/*.cpp",
":generated_sources", // Output of another module
],
// Header search paths (relative to module directory)
local_include_dirs: ["include"],
export_include_dirs: ["include/public"],
// Dependencies
shared_libs: [ // Shared library dependencies
"libbase",
"liblog",
],
static_libs: [ // Static library dependencies
"libfoo_static",
],
header_libs: [ // Header-only dependencies
"libhardware_headers",
],
// Compiler flags
cflags: ["-Wall", "-Werror"],
cppflags: ["-std=c++20"],
// Architecture-specific configuration
arch: {
arm: {
srcs: ["arm_specific.cpp"],
},
arm64: {
cflags: ["-DARCH_ARM64"],
},
x86_64: {
srcs: ["x86_specific.cpp"],
},
},
// Target-specific (device vs. host)
target: {
android: {
shared_libs: ["libcutils"],
},
host: {
cflags: ["-DHOST_BUILD"],
},
},
// Visibility control
visibility: ["//frameworks/base:__subpackages__"],
// APEX packaging
apex_available: [
"com.android.runtime",
"//apex_available:platform",
],
}
The arch and target blocks are how conditionals work in Android.bp.
Rather than if/else statements, properties are nested under architecture or
target selectors, and the build system merges them with the top-level
properties at build time.
2.4.8 Walkthrough: An Android App¶
Here is a real-world example from the Settings app:
android_library {
name: "Settings-core",
defaults: [
"SettingsLib-search-defaults",
"SettingsLintDefaults",
"SpaPrivilegedLib-defaults",
],
srcs: [
"src/**/*.java",
"src/**/*.kt",
],
exclude_srcs: [
"src/com/android/settings/biometrics/fingerprint2/lib/**/*.kt",
],
javac_shard_size: 50,
use_resource_processor: true,
resource_dirs: [
"res",
"res-export",
"res-product",
],
optional_uses_libs: ["com.android.extensions.appfunctions"],
static_libs: [
"androidx.compose.runtime_runtime-livedata",
"androidx.lifecycle_lifecycle-livedata-ktx",
"androidx.navigation_navigation-fragment-ktx",
"gson",
"guava",
"BiometricsSharedLib",
"SystemUIUnfoldLib",
"WifiTrackerLib",
...
],
}
Source: packages/apps/Settings/Android.bp, lines 47-100+
Key observations:
defaultspulls in shared configuration from multiple defaults modules.srcsuses glob patterns (**/*.java) to match all Java and Kotlin files recursively.exclude_srcsremoves specific files from the glob results.javac_shard_sizecontrols compilation parallelism by splitting the source into shards of 50 files each.static_libslists compile-time dependencies that are bundled into the output.use_resource_processorenables Android resource processing.
2.4.9 Walkthrough: An APEX Module¶
Here is the Wi-Fi APEX module:
apex_defaults {
name: "com.android.wifi-defaults",
androidManifest: ":com.android.wifi-androidManifest",
bootclasspath_fragments: ["com.android.wifi-bootclasspath-fragment"],
systemserverclasspath_fragments: [
"com.android.wifi-systemserverclasspath-fragment",
],
compat_configs: ["wifi-compat-config"],
prebuilts: [
"cacerts_wfa",
"mainline_supplicant_conf",
"mainline_supplicant_rc",
],
key: "com.android.wifi.key",
certificate: ":com.android.wifi.certificate",
apps: [
"OsuLogin",
"ServiceWifiResources",
"WifiDialog",
],
jni_libs: [
"libservice-wifi-jni",
],
defaults: ["r-launched-apex-module"],
compressible: true,
}
apex {
name: "com.android.wifi",
defaults: ["com.android.wifi-defaults"],
manifest: "apex_manifest.json",
}
apex_key {
name: "com.android.wifi.key",
public_key: "com.android.wifi.avbpubkey",
private_key: "com.android.wifi.pem",
}
Source: packages/modules/Wifi/apex/Android.bp, lines 21-79
This demonstrates the APEX pattern:
apex_defaultsdefines shared configurationapexis the actual module that produces the.apexfileapex_keyprovides the signing key- The APEX bundles apps, JNI libraries, prebuilt files, bootclasspath fragments, and compatibility configurations
2.4.10 Namespaces¶
For large trees where module name collisions might occur, Soong supports namespaces:
soong_namespace {
imports: [
"hardware/google/pixel",
"device/google/gs201/powerstats",
],
}
cc_binary {
name: "android.hardware.power.stats-service.pixel",
defaults: ["powerstats_pixel_binary_defaults"],
srcs: ["*.cpp"],
}
Source: build/soong/README.md, lines 258-279
The README explains: "The name of a namespace is the path of its directory." Name resolution first checks the module's own namespace, then searches imported namespaces in order, and finally falls back to the global namespace.
2.4.11 Visibility Control¶
Module visibility controls which other modules can depend on a given module:
cc_library {
name: "libinternal",
visibility: [
"//frameworks/base:__subpackages__",
"//packages/apps/Settings:__pkg__",
],
}
The visibility system supports several patterns:
| Pattern | Meaning |
|---|---|
["//visibility:public"] |
Anyone can use this module |
["//visibility:private"] |
Only the same package |
["//some/package:__pkg__"] |
Only modules in some/package |
["//project:__subpackages__"] |
Modules in project or its sub-packages |
[":__subpackages__"] |
Shorthand for the current package's sub-packages |
Source: build/soong/README.md, lines 308-374
2.4.12 Conditionals and Select Statements¶
Android.bp files deliberately lack traditional conditionals. Instead, Soong
provides several mechanisms:
Architecture selectors (the arch property):
Target selectors (the target property):
cc_library {
...
target: {
android: { shared_libs: ["libcutils"] },
host: { cflags: ["-DHOST_BUILD"] },
},
}
Select statements (newer mechanism):
cc_library {
...
srcs: select(arch(), {
"arm64": ["arm64_impl.cpp"],
"x86_64": ["x86_impl.cpp"],
default: ["generic_impl.cpp"],
}),
}
The Soong README recommends select statements over the older
soong_config_module_type mechanism:
"Select statement is a new mechanism for supporting conditionals, which is easier to write and maintain and reduces boilerplate code. It is recommended to use select statements instead of soong_config_module_type."
Source: build/soong/README.md, lines 444-448
Soong config variables (for vendor modules):
soong_config_module_type {
name: "acme_cc_defaults",
module_type: "cc_defaults",
config_namespace: "acme",
variables: ["board"],
bool_variables: ["feature"],
properties: ["cflags", "srcs"],
}
These variables are set from BoardConfig.mk:
Source: build/soong/README.md, lines 452-568
2.4.13 The bpfmt Formatter¶
Soong includes a canonical formatter for Android.bp files:
The canonical format uses 4-space indents, newlines after every element in a multi-element list, and always includes trailing commas.
2.4.14 Converting Android.mk to Android.bp¶
The androidmk tool performs a first-pass conversion:
From the README:
"The tool converts variables, modules, comments, and some conditionals, but any custom Makefile rules, complex conditionals or extra includes must be converted by hand."
Source: build/soong/README.md, lines 389-399
2.5 The Build Graph¶
2.5.1 Build Commands: m, mm, mmm¶
After lunching, you invoke the build using the m, mm, or mmm commands.
These are shell scripts in build/soong/bin/:
m -- Build from the top of the tree:
#!/bin/bash
source $(cd $(dirname $BASH_SOURCE) &> /dev/null && pwd)/../../make/shell_utils.sh
require_top
_wrap_build "$TOP/build/soong/soong_ui.bash" --build-mode --all-modules \
--dir="$(pwd)" "$@"
exit $?
Source: build/soong/bin/m
mm -- Build modules in the current directory:
#!/bin/bash
source $(cd $(dirname $BASH_SOURCE) &> /dev/null && pwd)/../../make/shell_utils.sh
require_top
_wrap_build "$TOP/build/soong/soong_ui.bash" --build-mode --modules-in-a-dir \
--dir="$(pwd)" "$@"
exit $?
Source: build/soong/bin/mm
mmm -- Build modules in specified directories:
#!/bin/bash
source $(cd $(dirname $BASH_SOURCE) &> /dev/null && pwd)/../../make/shell_utils.sh
require_top
_wrap_build "$TOP/build/soong/soong_ui.bash" --build-mode --modules-in-dirs \
--dir="$(pwd)" "$@"
exit $?
Source: build/soong/bin/mmm
All three commands delegate to soong_ui.bash with different --build-mode
flags. The key difference:
| Command | Scope | Example |
|---|---|---|
m |
Entire tree | m or m droid or m Settings |
mm |
Current directory only | cd frameworks/base && mm |
mmm |
Specified directory(ies) | mmm packages/apps/Settings |
You can also pass specific module names to m:
# Build specific modules
m Settings framework-minus-apex services
# Build a specific image
m systemimage
# "droid" is the default target -- builds everything
m droid
# Build nothing (just run the build system setup)
m nothing
2.5.2 The Build Pipeline¶
A complete build proceeds through several phases:
graph TB
subgraph "Phase 1: Bootstrap"
A[soong_ui.bash] --> B["Compile soong_ui<br/>via microfactory"]
B --> C[Execute soong_ui]
end
subgraph "Phase 2: Soong"
C --> D["Parse all Android.bp<br/>files in tree"]
D --> E["Run mutators<br/>arch, apex, etc."]
E --> F["Generate<br/>out/soong/build.ninja"]
end
subgraph "Phase 3: Kati"
C --> G["Run Kati on<br/>Android.mk files"]
G --> H["Process product config<br/>PRODUCT_PACKAGES, etc."]
H --> I["Generate<br/>out/build-product.ninja"]
end
subgraph "Phase 4: Ninja"
F --> J[Combine ninja files]
I --> J
J --> K["Execute Ninja<br/>parallel compilation"]
end
subgraph "Phase 5: Image Assembly"
K --> L["Compile sources<br/>C/C++, Java, Rust, etc."]
L --> M["Link binaries<br/>and libraries"]
M --> N["Package APKs<br/>and APEXes"]
N --> O["Build filesystem<br/>images"]
end
subgraph "Output"
O --> P[system.img]
O --> Q[vendor.img]
O --> R[boot.img]
O --> S[super.img]
O --> T[userdata.img]
end
style A fill:#4a90d9,color:#fff
style D fill:#50b848,color:#fff
style G fill:#e8a838,color:#fff
style K fill:#d94a4a,color:#fff
style O fill:#9b59b6,color:#fff
2.5.3 Ninja: The Low-Level Build Executor¶
Neither Soong nor Kati actually compiles anything. They are build graph generators -- they produce Ninja manifest files. Ninja is the low-level build executor that does the actual work.
Ninja was created by Evan Martin at Google specifically for the Chrome/Chromium
build. It is designed for one thing: executing a build graph as fast as
possible. Unlike Make, Ninja does not discover or compute the build graph --
it expects a pre-computed .ninja file and simply executes it.
Ninja is designed for speed:
- It reads a
.ninjafile that describes all build edges (rules and their dependencies) - It determines the minimal set of outdated targets (using file timestamps)
- It executes build commands in parallel, respecting dependency order
- It provides a compact, real-time progress display
- It has extremely fast startup time (sub-second even for large builds)
Ninja File Format¶
A Ninja file consists of rules and build edges:
# Rule definition: how to compile a C file
rule cc
command = clang -c $cflags -o $out $in
description = CC $out
# Build edge: apply the rule to specific files
build out/obj/foo.o: cc src/foo.c
cflags = -Wall -O2
# Another rule: linking
rule link
command = clang -o $out $in $ldflags
description = LINK $out
# Build edge: link object files into a binary
build out/bin/myapp: link out/obj/foo.o out/obj/bar.o
ldflags = -lm
The Ninja files generated by Soong and Kati are enormous -- the combined file for a full AOSP build can be hundreds of megabytes.
The combined Ninja file is generated at:
You can inspect the build graph using Ninja's built-in tools:
# Show all commands needed to build a target
prebuilts/build-tools/linux-x86/bin/ninja \
-f out/combined-aosp_arm64.ninja \
-t commands out/target/product/generic_arm64/system.img
# Show the dependency graph for a target
ninja -f out/combined-aosp_arm64.ninja -t graph libcutils > deps.dot
# Show build rules for a specific output
ninja -f out/combined-aosp_arm64.ninja -t query <output-file>
The showcommands function in envsetup.sh provides a convenient wrapper:
2.5.4 The Output Directory¶
All build artifacts are placed under the out/ directory (or $OUT_DIR if
overridden):
out/
.module_paths/ <-- Module path cache
soong/
.intermediates/ <-- Soong intermediate outputs
build.ninja <-- Soong-generated ninja file
docs/ <-- Generated documentation
target/
product/
<device>/ <-- Device-specific outputs
android-info.txt <-- Device metadata
boot.img <-- Kernel + ramdisk
dtbo.img <-- Device Tree Blob Overlay
init_boot.img <-- Init boot image (Android 13+)
obj/ <-- Native object files
ramdisk.img <-- Root filesystem ramdisk
super.img <-- Dynamic partitions container
system/ <-- Staged system partition contents
system.img <-- System partition image
userdata.img <-- User data partition image
vendor/ <-- Staged vendor partition contents
vendor.img <-- Vendor partition image
vendor_boot.img <-- Vendor boot image
product.img <-- Product partition image
system_ext.img <-- System extension partition image
recovery.img <-- Recovery image
vbmeta.img <-- Verified Boot metadata
symbols/ <-- Unstripped binaries (for debugging)
testcases/ <-- Test binaries
host/
linux-x86/ <-- Host tools built during the build
bin/ <-- Host binaries (adb, fastboot, etc.)
testcases/ <-- Host test cases
combined-<product>.ninja <-- Combined ninja manifest
build-<product>.ninja <-- Kati-generated ninja manifest
verbose.log.gz <-- Build log (if enabled)
error.log <-- Error log
dist/ <-- Distribution artifacts
The key images in out/target/product/<device>/:
| Image | Purpose |
|---|---|
system.img |
Core Android OS (framework, apps, libraries) |
vendor.img |
Hardware-specific HALs and firmware |
boot.img |
Kernel + generic ramdisk |
vendor_boot.img |
Vendor-specific ramdisk |
init_boot.img |
Generic ramdisk (Android 13+, GKI) |
super.img |
Dynamic partitions container (holds system, vendor, product, etc.) |
userdata.img |
Initial user data partition |
product.img |
Product-specific customizations |
system_ext.img |
System extensions (OEM additions to the system partition) |
recovery.img |
Recovery mode image |
vbmeta.img |
Android Verified Boot metadata |
dtbo.img |
Device tree blob overlays |
2.5.5 The Soong Intermediates Directory¶
The out/soong/.intermediates/ directory is where Soong stores intermediate
build artifacts. Each module gets its own subdirectory, organized by the
module's path in the source tree:
out/soong/.intermediates/
frameworks/base/core/java/
framework-minus-apex/
android_common/
javac/ <-- Java compilation outputs
dex/ <-- DEX conversion outputs
combined/ <-- Combined JAR
external/zlib/
libz/
android_arm64_armv8-a_shared/ <-- Device shared lib variant
libz.so
android_arm64_armv8-a_static/ <-- Device static lib variant
libz.a
linux_glibc_x86_64_shared/ <-- Host shared lib variant
libz.so
packages/apps/Settings/
Settings/
android_common/
Settings.apk
The directory structure reflects the module variants created by mutators.
A single cc_library like libz may have many variants:
android_arm64_armv8-a_shared: Device ARM64, shared libraryandroid_arm64_armv8-a_static: Device ARM64, static librarylinux_glibc_x86_64_shared: Host Linux x86_64, shared library- And potentially more for sanitizers, APEX variants, etc.
This directory can grow very large (100+ GB for a full build). The m clean
command deletes the entire out/ directory.
2.5.6 Dynamic Partitions and super.img¶
Modern Android (10+) uses dynamic partitions: instead of fixed-size
individual partitions, a single super.img contains a logical volume manager
that allocates space to system, vendor, product, and other partitions
dynamically. This is configured in BoardConfig.mk:
# From device/generic/goldfish/board/BoardConfigCommon.mk:
# emulator needs super.img
BOARD_BUILD_SUPER_IMAGE_BY_DEFAULT := true
# 8G + 8M
BOARD_SUPER_PARTITION_SIZE ?= 8598323200
BOARD_SUPER_PARTITION_GROUPS := emulator_dynamic_partitions
BOARD_EMULATOR_DYNAMIC_PARTITIONS_PARTITION_LIST := \
system \
system_dlkm \
system_ext \
product \
vendor
# 8G
BOARD_EMULATOR_DYNAMIC_PARTITIONS_SIZE ?= 8589934592
Source: device/generic/goldfish/board/BoardConfigCommon.mk, lines 44-72
2.6 Product Configuration¶
2.6.1 The Product Configuration Hierarchy¶
An AOSP product is defined through a hierarchy of Make files that specify what packages to install, what properties to set, and how to configure the board hardware. The hierarchy flows from generic to specific:
graph TB
subgraph "Generic (build/make/target/product/)"
BASE[base.mk]
BS[base_system.mk]
BSE[base_system_ext.mk]
BV[base_vendor.mk]
BP[base_product.mk]
C64[core_64_bit.mk]
GS[generic_system.mk]
AOSP[aosp_arm64.mk]
BASE --> BS
BASE --> BSE
BASE --> BV
BASE --> BP
C64 --> AOSP
GS --> AOSP
end
subgraph "Device-specific (device/)"
DEV[device/generic/goldfish/]
AP[AndroidProducts.mk]
PROD[product/*.mk]
BOARD[board/BoardConfigCommon.mk]
BDEV[board/emu64a/BoardConfig.mk]
DEV --> AP
DEV --> PROD
DEV --> BOARD
BOARD --> BDEV
end
AOSP -->|inherit-product| GS
AOSP -->|inherit-product| C64
style BASE fill:#4a90d9,color:#fff
style AOSP fill:#50b848,color:#fff
style DEV fill:#e8a838,color:#fff
2.6.2 The inherit-product Mechanism¶
The inherit-product function is the backbone of product configuration. It
includes another product makefile and inherits all its variable settings:
This is similar to class inheritance in object-oriented programming. The inheritance chain can be deep -- a typical product makefile might inherit from 5-10 other makefiles, each adding or overriding specific settings.
Important rules about inherit-product:
- Variables like
PRODUCT_PACKAGESare appended, not overridden. - Variables like
PRODUCT_NAMEare overridden by the last assignment. - The order of
inherit-productcalls matters for override behavior. inherit-product-if-existsis a variant that silently skips if the file does not exist (useful for optional vendor components).
The inheritance pattern follows a layered approach:
graph BT
base["base.mk<br/>Core packages"]
sys["base_system.mk<br/>System partition"]
sext["base_system_ext.mk<br/>System_ext partition"]
vend["base_vendor.mk<br/>Vendor partition"]
prod["base_product.mk<br/>Product partition"]
core64["core_64_bit.mk<br/>64-bit arch"]
gsys["generic_system.mk<br/>GSI config"]
handheld["handheld_system.mk<br/>Phone/tablet features"]
tele["telephony_system.mk<br/>Telephony support"]
aosp["aosp_arm64.mk<br/>Final product"]
base --> sys
base --> sext
base --> vend
base --> prod
sys --> gsys
sext --> gsys
handheld --> gsys
tele --> gsys
gsys --> aosp
core64 --> aosp
vend --> aosp
prod --> aosp
style aosp fill:#50b848,color:#fff
style base fill:#4a90d9,color:#fff
style gsys fill:#e8a838,color:#fff
2.6.3 Product Makefiles in build/make/target/product/¶
This directory contains the generic product definitions that real device products inherit from. Key files:
| File | Purpose |
|---|---|
base.mk |
Inherits all base partition makefiles |
base_system.mk |
Defines base system packages (PRODUCT_PACKAGES) |
base_system_ext.mk |
Defines base system_ext packages |
base_vendor.mk |
Defines base vendor packages |
base_product.mk |
Defines base product packages |
core_64_bit.mk |
Enables 64-bit architecture support |
core_64_bit_only.mk |
64-bit only (no 32-bit support) |
generic_system.mk |
Generic System Image (GSI) configuration |
aosp_arm64.mk |
AOSP product for ARM64 |
aosp_x86_64.mk |
AOSP product for x86_64 |
aosp_riscv64.mk |
AOSP product for RISC-V 64 |
The base.mk file is a simple aggregator:
# This makefile is suitable to inherit by products that don't need to be
# split up by partition.
$(call inherit-product, $(SRC_TARGET_DIR)/product/base_system.mk)
$(call inherit-product, $(SRC_TARGET_DIR)/product/base_system_ext.mk)
$(call inherit-product, $(SRC_TARGET_DIR)/product/base_vendor.mk)
$(call inherit-product, $(SRC_TARGET_DIR)/product/base_product.mk)
Source: build/make/target/product/base.mk, lines 17-23
The base_system.mk file defines PRODUCT_PACKAGES -- the packages installed
into the system partition. This is a very long list (hundreds of entries) that
includes the fundamental components of Android:
# Base modules and settings for the system partition.
PRODUCT_PACKAGES += \
abx \
aconfigd-system \
adbd_system_api \
aflags \
am \
android.hidl.base-V1.0-java \
android.hidl.manager-V1.0-java \
android.system.suspend-service \
android.test.base \
android.test.mock \
android.test.runner \
apexd \
...
com.android.adbd \
com.android.adservices \
com.android.appsearch \
com.android.bt \
com.android.conscrypt \
com.android.i18n \
com.android.media \
com.android.media.swcodec \
com.android.wifi \
...
framework \
framework-graphics \
...
Source: build/make/target/product/base_system.mk, lines 18-100+
Notice that many APEX modules (com.android.wifi, com.android.media, etc.)
are listed directly in PRODUCT_PACKAGES. They are treated as first-class
installable packages.
2.6.4 A Concrete Product: aosp_arm64¶
The aosp_arm64.mk product definition shows how all the pieces come together:
# The system image of aosp_arm64-userdebug is a GSI for the devices with:
# - ARM 64 bits user space
# - 64 bits binder interface
# - system-as-root
# - VNDK enforcement
# - compatible property override enabled
#
# All components inherited here go to system image
#
$(call inherit-product, $(SRC_TARGET_DIR)/product/core_64_bit.mk)
$(call inherit-product, $(SRC_TARGET_DIR)/product/generic_system.mk)
# Enable mainline checking for exact this product name
ifeq (aosp_arm64,$(TARGET_PRODUCT))
PRODUCT_ENFORCE_ARTIFACT_PATH_REQUIREMENTS := relaxed
endif
#
# All components inherited here go to system_ext image
#
$(call inherit-product, $(SRC_TARGET_DIR)/product/handheld_system_ext.mk)
$(call inherit-product, $(SRC_TARGET_DIR)/product/telephony_system_ext.mk)
# pKVM
$(call inherit-product-if-exists, \
packages/modules/Virtualization/apex/product_packages.mk)
#
# All components inherited here go to product image
#
$(call inherit-product, $(SRC_TARGET_DIR)/product/aosp_product.mk)
#
# All components inherited here go to vendor or vendor_boot image
#
$(call inherit-product, $(SRC_TARGET_DIR)/board/generic_arm64/device.mk)
AB_OTA_UPDATER := true
AB_OTA_PARTITIONS ?= system
#
# Special settings for GSI releasing
#
ifeq (aosp_arm64,$(TARGET_PRODUCT))
MODULE_BUILD_FROM_SOURCE ?= true
$(call inherit-product, $(SRC_TARGET_DIR)/product/gsi_release.mk)
PRODUCT_SOONG_DEFINED_SYSTEM_IMAGE := aosp_system_image
USE_SOONG_DEFINED_SYSTEM_IMAGE := true
endif
PRODUCT_NAME := aosp_arm64
PRODUCT_DEVICE := generic_arm64
PRODUCT_BRAND := Android
PRODUCT_MODEL := AOSP on ARM64
PRODUCT_NO_BIONIC_PAGE_SIZE_MACRO := true
Source: build/make/target/product/aosp_arm64.mk
Key things to notice:
- Partition-organized inheritance: Comments clearly mark which inherited makefiles contribute to which partition (system, system_ext, product, vendor).
inherit-product: The$(call inherit-product, ...)function includes another product makefile and inherits its variable settings.PRODUCT_NAME: The final product name used in lunch combos.PRODUCT_DEVICE: The device name, which determines whichBoardConfig.mkto use.
2.6.5 Key PRODUCT_* Variables¶
| Variable | Purpose | Example |
|---|---|---|
PRODUCT_NAME |
Product name | aosp_arm64 |
PRODUCT_DEVICE |
Device name (matches device/<vendor>/<name>/) |
generic_arm64 |
PRODUCT_BRAND |
Brand string | Android |
PRODUCT_MODEL |
Model string | AOSP on ARM64 |
PRODUCT_PACKAGES |
List of modules to install | Settings framework ... |
PRODUCT_COPY_FILES |
Files to copy into the image | src:dest pairs |
PRODUCT_PROPERTY_OVERRIDES |
System properties to set | ro.foo=bar |
PRODUCT_BOOT_JARS |
Jars in BOOTCLASSPATH | framework core-oj ... |
PRODUCT_SOONG_NAMESPACES |
Soong namespaces to expose to Make | hardware/google/pixel |
PRODUCT_ENFORCE_ARTIFACT_PATH_REQUIREMENTS |
Enforce path conventions | relaxed or true |
PRODUCT_MANIFEST_FILES |
Device manifest fragments | VINTF manifest paths |
2.6.6 PRODUCT_COPY_FILES¶
The PRODUCT_COPY_FILES variable copies files from the source tree into the
output image at specific paths:
PRODUCT_COPY_FILES += \
device/generic/goldfish/data/etc/config.ini:config.ini \
device/generic/goldfish/display_settings.xml:$(TARGET_COPY_OUT_VENDOR)/etc/display_settings.xml \
frameworks/native/data/etc/android.hardware.wifi.xml:$(TARGET_COPY_OUT_VENDOR)/etc/permissions/android.hardware.wifi.xml
The format is source:destination, where destination is relative to
PRODUCT_OUT. The TARGET_COPY_OUT_* variables help place files into the
correct partition:
| Variable | Expands To | Partition |
|---|---|---|
TARGET_COPY_OUT_SYSTEM |
system |
System |
TARGET_COPY_OUT_VENDOR |
vendor |
Vendor |
TARGET_COPY_OUT_PRODUCT |
product |
Product |
TARGET_COPY_OUT_SYSTEM_EXT |
system_ext |
System Extension |
TARGET_COPY_OUT_ODM |
odm |
ODM |
2.6.7 PRODUCT_PROPERTY_OVERRIDES¶
System properties (ro.*, persist.*, etc.) are set through product
configuration:
PRODUCT_PROPERTY_OVERRIDES += \
ro.hardware.egl=mesa \
ro.opengles.version=196610 \
debug.hwui.renderer=skiagl \
persist.sys.dalvik.vm.lib.2=libart.so
These end up in various build.prop or default.prop files on the device.
2.6.8 Release Configuration¶
The AOSP build system has a relatively new release configuration mechanism
managed through build/release/. This system allows different "releases"
(e.g., trunk_staging, next, ap3a) to control feature flags and
configuration variants without changing product makefiles.
The release is specified as the second argument to lunch:
Release configuration files define which features are enabled for a particular release, using aconfig flags and release-specific build flags.
2.6.9 Device Configuration: Goldfish (Emulator)¶
The goldfish emulator device is defined in device/generic/goldfish/. Its
AndroidProducts.mk lists the available products:
PRODUCT_MAKEFILES := \
$(LOCAL_DIR)/64bitonly/product/sdk_phone64_x86_64.mk \
$(LOCAL_DIR)/64bitonly/product/sdk_phone16k_x86_64.mk \
$(LOCAL_DIR)/64bitonly/product/sdk_phone64_x86_64_minigbm.mk \
$(LOCAL_DIR)/64bitonly/product/sdk_phone64_x86_64_riscv64.mk \
$(LOCAL_DIR)/64bitonly/product/sdk_tablet_arm64.mk \
$(LOCAL_DIR)/64bitonly/product/sdk_tablet_x86_64.mk \
$(LOCAL_DIR)/64bitonly/product/sdk_phone64_arm64.mk \
$(LOCAL_DIR)/64bitonly/product/sdk_phone64_arm64_minigbm.mk \
$(LOCAL_DIR)/64bitonly/product/sdk_phone16k_arm64.mk \
$(LOCAL_DIR)/64bitonly/product/sdk_phone64_arm64_riscv64.mk \
$(LOCAL_DIR)/64bitonly/product/sdk_slim_x86_64.mk \
$(LOCAL_DIR)/64bitonly/product/sdk_slim_arm64.mk \
Source: device/generic/goldfish/AndroidProducts.mk
2.6.10 BoardConfig.mk¶
The BoardConfig.mk file defines hardware-level configuration for a device.
For the goldfish ARM64 emulator:
# arm64 emulator specific definitions
TARGET_ARCH := arm64
TARGET_ARCH_VARIANT := armv8-a
TARGET_CPU_VARIANT := generic
TARGET_CPU_ABI := arm64-v8a
TARGET_2ND_ARCH_VARIANT := armv8-a
TARGET_2ND_CPU_VARIANT := generic
include device/generic/goldfish/board/BoardConfigCommon.mk
BOARD_BOOTIMAGE_PARTITION_SIZE := 0x02000000
BOARD_USERDATAIMAGE_PARTITION_SIZE := 576716800
Source: device/generic/goldfish/board/emu64a/BoardConfig.mk
The common configuration shared across all goldfish variants:
include build/make/target/board/BoardConfigGsiCommon.mk
BOARD_VENDOR_SEPOLICY_DIRS += device/generic/goldfish/sepolicy/vendor
SYSTEM_EXT_PRIVATE_SEPOLICY_DIRS += \
device/generic/goldfish/sepolicy/system_ext/private
TARGET_BOOTLOADER_BOARD_NAME := goldfish_$(TARGET_ARCH)
NUM_FRAMEBUFFER_SURFACE_BUFFERS := 3
# Build OpenGLES emulation guest and host libraries
BUILD_EMULATOR_OPENGL := true
BUILD_QEMU_IMAGES := true
# Build and enable the OpenGL ES View renderer
USE_OPENGL_RENDERER := true
# Emulator doesn't support sparse image format
TARGET_USERIMAGES_SPARSE_EXT_DISABLED := true
# emulator is Non-A/B device
AB_OTA_UPDATER := none
AB_OTA_PARTITIONS :=
# emulator needs super.img
BOARD_BUILD_SUPER_IMAGE_BY_DEFAULT := true
# 8G + 8M
BOARD_SUPER_PARTITION_SIZE ?= 8598323200
BOARD_SUPER_PARTITION_GROUPS := emulator_dynamic_partitions
BOARD_EMULATOR_DYNAMIC_PARTITIONS_PARTITION_LIST := \
system \
system_dlkm \
system_ext \
product \
vendor
BOARD_SYSTEMIMAGE_FILE_SYSTEM_TYPE := $(EMULATOR_RO_PARTITION_FS)
BOARD_PRODUCTIMAGE_FILE_SYSTEM_TYPE := $(EMULATOR_RO_PARTITION_FS)
BOARD_SYSTEM_EXTIMAGE_FILE_SYSTEM_TYPE := $(EMULATOR_RO_PARTITION_FS)
BOARD_USES_SYSTEM_DLKMIMAGE := true
BOARD_SYSTEM_DLKMIMAGE_FILE_SYSTEM_TYPE := erofs
#vendor boot
BOARD_INCLUDE_DTB_IN_BOOTIMG := false
BOARD_BOOT_HEADER_VERSION := 4
BOARD_MKBOOTIMG_ARGS += --header_version $(BOARD_BOOT_HEADER_VERSION)
BOARD_VENDOR_BOOTIMAGE_PARTITION_SIZE := 0x06000000
BOARD_RAMDISK_USE_LZ4 := true
Source: device/generic/goldfish/board/BoardConfigCommon.mk
Key BOARD_* variables:
| Variable | Purpose |
|---|---|
TARGET_ARCH |
Primary architecture (arm64, x86_64, etc.) |
TARGET_ARCH_VARIANT |
Architecture variant (armv8-a, etc.) |
TARGET_CPU_VARIANT |
CPU variant (generic, cortex-a53, etc.) |
TARGET_CPU_ABI |
CPU ABI string (arm64-v8a, x86_64, etc.) |
BOARD_BOOTIMAGE_PARTITION_SIZE |
Boot partition size |
BOARD_SUPER_PARTITION_SIZE |
Super partition size |
BOARD_SUPER_PARTITION_GROUPS |
Dynamic partition groups |
BOARD_SYSTEMIMAGE_FILE_SYSTEM_TYPE |
System image filesystem (ext4, erofs) |
BOARD_BOOT_HEADER_VERSION |
Boot image header version |
BOARD_VENDOR_SEPOLICY_DIRS |
Vendor SEPolicy directories |
2.6.11 The Product Configuration Flow¶
The complete flow from lunch to a configured build:
graph TB
L[lunch aosp_arm64] --> TP[TARGET_PRODUCT=aosp_arm64]
TP --> AP["Find AndroidProducts.mk<br/>that lists aosp_arm64.mk"]
AP --> PM[Load aosp_arm64.mk]
PM --> IP1["inherit-product:<br/>core_64_bit.mk"]
PM --> IP2["inherit-product:<br/>generic_system.mk"]
PM --> IP3["inherit-product:<br/>aosp_product.mk"]
PM --> IP4["inherit-product:<br/>device.mk"]
IP1 --> ARCH["Set TARGET_ARCH=arm64<br/>and multilib config"]
IP2 --> SYS["Set base system<br/>PRODUCT_PACKAGES"]
IP3 --> PRD["Set product-specific<br/>packages"]
IP4 --> DEV[Load device config]
DEV --> BC["BoardConfig.mk<br/>TARGET_ARCH, partition sizes,<br/>filesystem types, SEPolicy"]
ARCH --> BUILD[Build System Ready]
SYS --> BUILD
PRD --> BUILD
BC --> BUILD
style L fill:#4a90d9,color:#fff
style BUILD fill:#50b848,color:#fff
style BC fill:#e8a838,color:#fff
2.7 APEX: Modular System Components¶
2.7.1 What is APEX?¶
APEX (Android Pony EXpress) is a container format introduced in Android 10 that allows system components to be updated independently of the full OS. Before APEX, updating a system library or runtime required a full OTA (over-the-air) update. With APEX, individual components -- like the ART runtime, the Wi-Fi stack, or the DNS resolver -- can be updated through the Google Play Store or a similar mechanism.
An APEX file is a special kind of Android package that contains:
- Native shared libraries (
.sofiles) - Executables
- Java libraries (JARs)
- Android apps (APKs)
- Configuration files
- A manifest describing the package
- A signing key for verified boot integration
2.7.2 APEX Architecture¶
graph TB
subgraph "APEX File (.apex)"
M[apex_manifest.json]
AM[AndroidManifest.xml]
PL[apex_payload.img]
SIG[META-INF/ signatures]
subgraph "apex_payload.img (ext4 or erofs)"
LIB["lib/<br/>lib64/"]
BIN[bin/]
APP[app/]
ETC[etc/]
JARS[javalib/]
FW[framework/]
end
PL --> LIB
PL --> BIN
PL --> APP
PL --> ETC
PL --> JARS
PL --> FW
end
subgraph "On Device"
STORE[/apex/com.android.wifi/]
CURR[current/ -> v340000000]
V1["v340000000/<br/>lib64/<br/>bin/<br/>app/<br/>etc/"]
STORE --> CURR
CURR --> V1
end
style M fill:#4a90d9,color:#fff
style PL fill:#50b848,color:#fff
style STORE fill:#e8a838,color:#fff
2.7.3 APEX Lifecycle on Device¶
Understanding how APEX works at runtime helps explain the build-time requirements:
graph TB
subgraph "Boot Sequence"
A[Device boots]
B[init starts apexd]
C["apexd scans /system/apex/<br/>and /data/apex/"]
D["For each APEX:<br/>verify signature"]
E["Mount apex_payload.img<br/>as loop device"]
F["Bind-mount to<br/>/apex/{name}/current/"]
G["Update linker<br/>configuration"]
H["System uses libraries<br/>from /apex/{name}/"]
A --> B --> C --> D --> E --> F --> G --> H
end
subgraph "Update Flow"
U1["New APEX pushed<br/>to /data/apex/"]
U2["apexd verifies<br/>new APEX"]
U3["On next boot:<br/>new APEX activated"]
U4["Old APEX retained<br/>for rollback"]
U1 --> U2 --> U3 --> U4
end
style A fill:#4a90d9,color:#fff
style E fill:#50b848,color:#fff
style U1 fill:#e8a838,color:#fff
- At boot,
apexd(the APEX daemon) scans for APEX files. - Each APEX file's signature is verified using the pre-installed public key.
- The
apex_payload.imginside each APEX is mounted as a loop device. - The mounted filesystem is bind-mounted to
/apex/<name>/current/. - Libraries and binaries from the APEX are made available to the system through the linker configuration.
Pre-installed APEXes live in /system/apex/. When an update is received
(e.g., through the Play Store), the new APEX is stored in /data/apex/ and
activated on the next boot. The old version is retained for rollback.
2.7.4 APEX in the Build System¶
The APEX build logic lives in build/soong/apex/. The main file, apex.go
(3,001 lines), defines the module types and build logic:
// package apex implements build rules for creating the APEX files which
// are container for lower-level system components.
// See https://source.android.com/devices/tech/ota/apex
package apex
func init() {
registerApexBuildComponents(android.InitRegistrationContext)
}
func registerApexBuildComponents(ctx android.RegistrationContext) {
ctx.RegisterModuleType("apex", BundleFactory)
ctx.RegisterModuleType("apex_test", TestApexBundleFactory)
ctx.RegisterModuleType("apex_vndk", vndkApexBundleFactory)
ctx.RegisterModuleType("apex_defaults", DefaultsFactory)
ctx.RegisterModuleType("prebuilt_apex", PrebuiltFactory)
ctx.RegisterModuleType("override_apex", OverrideApexFactory)
ctx.RegisterModuleType("apex_set", apexSetFactory)
ctx.PreDepsMutators(RegisterPreDepsMutators)
ctx.PostDepsMutators(RegisterPostDepsMutators)
}
Source: build/soong/apex/apex.go, lines 17-58
The apexBundleProperties struct defines all the properties an APEX module can
declare:
type apexBundleProperties struct {
// Json manifest file describing meta info of this APEX bundle.
Manifest *string `android:"path"`
// AndroidManifest.xml file used for the zip container
AndroidManifest proptools.Configurable[string] `android:"path,..."`
// Determines the file contexts file for setting security contexts
File_contexts *string `android:"path"`
// Canned fs config file for customizing file uid/gid/mod/capabilities
Canned_fs_config proptools.Configurable[string] `android:"path,..."`
ApexNativeDependencies
Multilib apexMultilibProperties
// List of runtime resource overlays (RROs)
Rros []string
// List of bootclasspath fragments
Bootclasspath_fragments proptools.Configurable[[]string]
// List of systemserverclasspath fragments
Systemserverclasspath_fragments proptools.Configurable[[]string]
// List of java libraries
Java_libs []string
// List of sh binaries
Sh_binaries []string
// List of platform_compat_config files
Compat_configs []string
// List of filesystem images
Filesystems []string
...
}
Source: build/soong/apex/apex.go, lines 72-120
The full apexBundleProperties struct also includes properties for controlling
the APEX update behavior:
// Whether this APEX is considered updatable or not. When set to true,
// this will enforce additional rules for making sure that the APEX is
// truly updatable. To be updatable, min_sdk_version should be set as
// well. This will also disable the size optimizations like symlinking
// to the system libs. Default is true.
Updatable *bool
// Whether this APEX can use platform APIs or not. Can be set to true
// only when `updatable: false`. Default is false.
Platform_apis *bool
// Whether this APEX is installable to one of the partitions like
// system, vendor, etc. Default: true.
Installable *bool
// The type of filesystem to use. Either 'ext4', 'f2fs' or 'erofs'.
// Default 'ext4'.
Payload_fs_type *string
Source: build/soong/apex/apex.go, lines 125-147
The ApexNativeDependencies struct defines what goes inside the APEX:
type ApexNativeDependencies struct {
// List of native libraries embedded inside this APEX.
Native_shared_libs proptools.Configurable[[]string]
// List of JNI libraries embedded inside this APEX.
Jni_libs proptools.Configurable[[]string]
// List of rust dyn libraries embedded inside this APEX.
Rust_dyn_libs []string
// List of native executables embedded inside this APEX.
Binaries proptools.Configurable[[]string]
// List of native tests embedded inside this APEX.
Tests []string
// List of filesystem images embedded inside this APEX bundle.
Filesystems []string
// List of prebuilt_etcs embedded inside this APEX bundle.
Prebuilts proptools.Configurable[[]string]
}
Source: build/soong/apex/apex.go, lines 188-209
Note the use of proptools.Configurable[[]string] -- this is a type that
supports the newer select statement conditional mechanism, allowing the list
of dependencies to vary based on build configuration.
2.7.5 Declaring an APEX Module¶
Here is the complete pattern for declaring an APEX, using the Wi-Fi module as our example:
// Step 1: Define the signing key
apex_key {
name: "com.android.wifi.key",
public_key: "com.android.wifi.avbpubkey",
private_key: "com.android.wifi.pem",
}
// Step 2: Define the certificate
android_app_certificate {
name: "com.android.wifi.certificate",
certificate: "com.android.wifi",
}
// Step 3: Define defaults (optional, but recommended)
apex_defaults {
name: "com.android.wifi-defaults",
bootclasspath_fragments: ["com.android.wifi-bootclasspath-fragment"],
systemserverclasspath_fragments: [
"com.android.wifi-systemserverclasspath-fragment"
],
key: "com.android.wifi.key",
certificate: ":com.android.wifi.certificate",
apps: ["OsuLogin", "ServiceWifiResources", "WifiDialog"],
jni_libs: ["libservice-wifi-jni"],
compressible: true,
}
// Step 4: Define the APEX itself
apex {
name: "com.android.wifi",
defaults: ["com.android.wifi-defaults"],
manifest: "apex_manifest.json",
}
Source: packages/modules/Wifi/apex/Android.bp
2.7.6 How Modules Declare APEX Availability¶
When a library or binary should be available inside an APEX, it uses the
apex_available property:
cc_library {
name: "libwifi-jni",
srcs: ["*.cpp"],
shared_libs: ["liblog", "libbase"],
// This library can be used in the wifi APEX and the platform
apex_available: [
"com.android.wifi",
"//apex_available:platform",
],
}
The special value //apex_available:platform means the module can also be
used outside any APEX (i.e., directly on the system partition). Without this,
a module is restricted to APEX usage only.
The APEX build system uses a mutator to create separate build variants for each APEX a module appears in. This ensures that dependencies are properly isolated per-APEX.
2.7.7 Key APEX Modules in AOSP¶
As seen in base_system.mk, many core Android components are delivered as
APEX modules:
| APEX Name | Component |
|---|---|
com.android.adbd |
Android Debug Bridge daemon |
com.android.art |
Android Runtime (ART) |
com.android.bt |
Bluetooth stack |
com.android.conscrypt |
TLS/SSL provider |
com.android.i18n |
Internationalization (ICU) |
com.android.media |
Media framework |
com.android.media.swcodec |
Software codecs |
com.android.mediaprovider |
Media storage |
com.android.os.statsd |
Statistics daemon |
com.android.permission |
Permission controller |
com.android.resolv |
DNS resolver |
com.android.sdkext |
SDK extensions |
com.android.tethering |
Tethering and connectivity |
com.android.wifi |
Wi-Fi stack |
com.android.neuralnetworks |
Neural Networks HAL |
com.android.virt |
Virtualization framework |
2.8 Bazel in AOSP¶
2.8.1 Why Bazel?¶
Bazel is Google's open-source build system, evolved from their internal system Blaze. It offers several advantages over Soong:
- Hermeticity: Builds are sandboxed and reproducible.
- Remote execution: Build actions can be distributed across a cluster.
- Caching: Build results can be shared across developers and CI.
- Scalability: Designed for repositories with billions of lines of code.
- Language support: First-class support for many languages through Starlark rules.
Google has been working to migrate parts of AOSP's build to Bazel, but this is an incremental, multi-year effort.
2.8.2 Current Status¶
As of the current AOSP release, Bazel's role in the platform build is experimental and limited:
- Kernel builds (Kleaf): The kernel build system has been migrated to Bazel (see Section 2.9).
- Select external projects: Some external projects like Skia maintain Bazel build files alongside their Soong definitions.
- Build experiments: The
build/pesto/experiments/directory contains experimental Bazel integration tests. - bp2build: A tool that converts
Android.bpfiles to BazelBUILDfiles has been developed, though its use remains limited.
2.8.3 bp2build: Converting Android.bp to BUILD¶
The bp2build tool (part of build/soong/) automatically converts
Android.bp module definitions into Bazel BUILD.bazel files. This is the
primary mechanism for the Soong-to-Bazel migration.
The conversion works by:
- Parsing all
Android.bpfiles (same as Soong does normally) - For each module type that has a registered Bazel conversion, generating the equivalent Bazel rule
- Writing
BUILD.bazelfiles alongside theAndroid.bpfiles
Not all module types have Bazel equivalents yet. The conversion is opt-in and incremental -- only modules that have been explicitly enabled for Bazel conversion are included.
Example conversion:
Android.bp:
Generated BUILD.bazel:
2.8.4 The build/pesto/ Directory¶
The Bazel integration experiments live in build/pesto/:
This directory is intentionally sparse -- the primary Bazel work is in the kernel build system (Kleaf) and in individual projects that maintain their own Bazel build files.
2.8.5 Build Performance with RBE¶
AOSP already supports RBE for some build actions through Soong's remoteexec
package (build/soong/remoteexec/). To enable RBE:
# Source the RBE setup script
source build/make/rbesetup.sh
# Set RBE-specific environment variables
export USE_RBE=1
export RBE_SERVICE=... # Your RBE endpoint
export RBE_DIR=... # RBE client directory
# Build with RBE
m -j200 # Higher parallelism since work is distributed
With RBE configured and a remote worker pool available, build times can decrease dramatically:
| Build Type | Local (16 cores) | With RBE (~500 cores) |
|---|---|---|
| Full clean build | 3-4 hours | 30-45 minutes |
| Incremental (small change) | 5-15 minutes | 2-5 minutes |
| Incremental (framework) | 20-40 minutes | 10-15 minutes |
The performance gains come from:
- Distributing compilation across many machines
- Caching compilation results (cache hit = zero cost)
- Reduced I/O contention (remote machines have fast storage)
2.8.6 Skia's Bazel Build¶
One of the more mature Bazel integrations is in the Skia graphics library
(external/skia/bazel/). This directory contains a complete Bazel build
system for Skia, with files like:
external/skia/bazel/
BUILD.bazel <-- Top-level build file
Makefile <-- Compatibility wrapper
buildrc <-- Bazel configuration
cipd_deps.bzl <-- CIPD dependency definitions
common_config_settings/ <-- Shared configuration
cpp_modules.bzl <-- C++ module definitions
deps.json <-- Dependency metadata
deps_parser/ <-- Dependency parser tool
device_specific_configs/ <-- Per-device configurations
external/ <-- External dependency rules
flags.bzl <-- Build flag definitions
gcs_mirror.bzl <-- Google Cloud Storage mirror rules
This shows the pattern for projects that want to support both Soong (for integration with the AOSP build) and Bazel (for standalone development or remote execution).
2.8.7 Remote Build Execution (RBE)¶
One of Bazel's key advantages is support for Remote Build Execution (RBE).
AOSP already supports RBE for some build actions through Soong's remoteexec
package (build/soong/remoteexec/). The build/make/rbesetup.sh script
helps configure RBE credentials and endpoints.
RBE works by:
- Analyzing the build graph to identify actions that can run remotely
- Uploading action inputs to a Content Addressable Store (CAS)
- Executing the action on a remote worker
- Downloading the outputs (or retrieving them from the action cache)
For large builds, RBE can dramatically reduce build times by distributing compilation across hundreds of machines.
2.8.8 Mixed Builds¶
The long-term migration plan involves mixed builds where Soong and Bazel coexist:
graph LR
subgraph "Current State"
ABP[Android.bp] --> Soong[Soong]
AMK[Android.mk] --> Kati[Kati]
Soong --> NJ1[Ninja]
Kati --> NJ1
end
subgraph "Mixed Build (Transition)"
ABP2[Android.bp] --> BP2B[bp2build]
BP2B --> BUILD[BUILD.bazel]
BUILD --> Bazel[Bazel]
ABP2 --> Soong2[Soong]
Soong2 --> NJ2[Ninja]
Bazel --> NJ2
end
subgraph "Future (Goal)"
BUILD2[BUILD.bazel] --> Bazel2[Bazel]
Bazel2 --> NJ3[Ninja / Direct]
end
style Soong fill:#50b848,color:#fff
style Bazel fill:#4a90d9,color:#fff
style Bazel2 fill:#4a90d9,color:#fff
In a mixed build, some modules are built by Soong and some by Bazel, with the
results combined into a single Ninja manifest. The bp2build tool
automatically generates BUILD.bazel files from Android.bp definitions.
2.9 Kleaf -- Kernel Build System¶
2.9.1 Overview¶
Kleaf is AOSP's Bazel-based kernel build system. Unlike the platform build (which uses Soong), the kernel build has been fully migrated to Bazel. Kleaf provides hermetic, reproducible kernel builds with support for:
- Multiple architectures (ARM64, ARM, x86_64, i386, RISC-V 64)
- The Generic Kernel Image (GKI) architecture
- Custom and user-provided toolchains
- Remote build execution
- Incremental builds
2.9.2 Toolchain Configuration¶
Kleaf's toolchain configuration lives in
prebuilts/clang/host/linux-x86/kleaf/. The key files are:
architecture_constants.bzl defines the supported architectures:
"""List of supported architectures by Kleaf."""
ArchInfo = provider(
"An architecture for a clang toolchain.",
fields = {
"name": "a substring of the name of the toolchain.",
"target_os": "OS of the target platform",
"target_cpu": "CPU of the target platform",
"target_libc": "libc of the target platform",
},
)
SUPPORTED_ARCHITECTURES = [
ArchInfo(
name = "1_linux_musl_x86_64",
target_os = "linux",
target_cpu = "x86_64",
target_libc = "musl",
),
ArchInfo(
name = "2_linux_x86_64",
target_os = "linux",
target_cpu = "x86_64",
target_libc = "glibc",
),
ArchInfo(
name = "android_arm64",
target_os = "android",
target_cpu = "arm64",
target_libc = None,
),
ArchInfo(
name = "android_arm",
target_os = "android",
target_cpu = "arm",
target_libc = None,
),
ArchInfo(
name = "android_x86_64",
target_os = "android",
target_cpu = "x86_64",
target_libc = None,
),
ArchInfo(
name = "android_i386",
target_os = "android",
target_cpu = "i386",
target_libc = None,
),
ArchInfo(
name = "android_riscv64",
target_os = "android",
target_cpu = "riscv64",
target_libc = None,
),
]
Source: prebuilts/clang/host/linux-x86/kleaf/architecture_constants.bzl
Note the inclusion of riscv64 -- this reflects AOSP's ongoing work to support
the RISC-V architecture, which is expected to become increasingly important for
Android devices.
clang_toolchain.bzl defines the actual Clang toolchain rules:
"""Defines a cc toolchain for kernel build, based on clang."""
load("@kernel_toolchain_info//:dict.bzl", "VARS")
load("@rules_cc//cc/toolchains:cc_toolchain.bzl", "cc_toolchain")
load(":clang_config.bzl", "clang_config")
_CC_TOOLCHAIN_TYPE = Label("@bazel_tools//tools/cpp:toolchain_type")
def _clang_toolchain_internal(
name,
clang_version,
arch,
clang_pkg,
clang_all_binaries,
clang_includes,
linker_files = None,
sysroot_label = None,
sysroot_dir = None,
...):
"""Defines a cc toolchain for kernel build, based on clang.
Args:
name: name of the toolchain
clang_version: value of `CLANG_VERSION`, e.g. `r475365b`.
arch: an ArchInfo object to look up extra kwargs.
...
"""
Source: prebuilts/clang/host/linux-x86/kleaf/clang_toolchain.bzl, lines 15-50
2.9.3 Toolchain Resolution¶
Kleaf supports two types of toolchains, as described in its README:
-
Default toolchains: Named
{target_os}_{target_cpu}_clang_toolchain, these are the fallback toolchains when no version is specified. -
User toolchains: Provided via
--user_clang_toolchainflag, these override the defaults for development or testing.
The resolution process follows Bazel's standard toolchain resolution:
For a build without any flags or transitions, Bazel uses
"single-platform builds" by default, so the target platform is
the same as the execution platform with two constraint values:
(linux, x86_64).
In Kleaf, if a target is built with --config=android_{cpu}, or
is wrapped in an android_filegroup with a given cpu, the target
platform has two constraint values (android, {cpu}).
Source: prebuilts/clang/host/linux-x86/kleaf/README.md, lines 88-99
2.9.4 Building a Kernel with Kleaf¶
To build a kernel using Kleaf, you use Bazel commands (typically wrapped by
a build/kernel/build.sh or tools/bazel script):
# Build the GKI kernel for ARM64
tools/bazel run //common:kernel_aarch64_dist
# Build with a custom toolchain
tools/bazel run --user_clang_toolchain=/path/to/toolchain \
//common:kernel_aarch64_dist
# Build kernel modules for a specific device
tools/bazel run //private/google-modules/soc/gs201:zuma_dist
# Build with debugging enabled
tools/bazel run //common:kernel_aarch64_debug_dist
The Kleaf build system defines several key Bazel rules:
| Rule | Purpose |
|---|---|
kernel_build |
Build a kernel binary |
kernel_modules |
Build kernel modules (.ko files) |
kernel_images |
Build boot images |
kernel_modules_install |
Install modules to a staging directory |
kernel_uapi_headers |
Generate userspace API headers |
ddk_module |
Build a Device Driver Kit module |
android_filegroup |
Group files with Android platform annotations |
Kleaf Build Configuration¶
Kleaf uses Bazel's configuration system to handle different build variants:
# Example from a kernel BUILD.bazel file
kernel_build(
name = "kernel_aarch64",
outs = [
"Image",
"Image.lz4",
"System.map",
"vmlinux",
"vmlinux.symvers",
],
build_config = "build.config.gki.aarch64",
module_outs = [
# GKI modules
"drivers/block/virtio_blk.ko",
"drivers/net/virtio_net.ko",
"fs/erofs/erofs.ko",
...
],
)
The build_config file specifies kernel configuration options (similar to
the traditional defconfig mechanism but adapted for Bazel).
2.9.5 Relationship with GKI¶
The Generic Kernel Image (GKI) is Android's approach to standardizing the kernel across devices. Kleaf is the build system that produces GKI kernels.
graph TB
subgraph "GKI Architecture"
GKI["Generic Kernel Image<br/>common kernel binary"]
VK["Vendor Kernel Modules<br/>device-specific .ko files"]
DT["Device Tree / DTBO<br/>hardware description"]
GKI --> BOOT[boot.img]
VK --> VBOOT[vendor_boot.img]
DT --> DTBO[dtbo.img]
end
subgraph "Kleaf Build"
KB[kernel_build rule]
KM[kernel_modules rule]
KI[kernel_images rule]
KB --> GKI
KM --> VK
KI --> BOOT
KI --> VBOOT
end
subgraph "Toolchain"
CLANG[Clang/LLVM toolchain]
KLEAF[Kleaf toolchain rules]
KLEAF --> CLANG
CLANG --> KB
end
style GKI fill:#4a90d9,color:#fff
style KB fill:#50b848,color:#fff
style CLANG fill:#e8a838,color:#fff
GKI separates the kernel into:
- A generic kernel binary (built from the Android Common Kernel source) that is the same across all devices using the same Android version
- Vendor kernel modules (
.kofiles) that contain device-specific drivers
Kleaf builds both components, with the generic kernel being the primary GKI artifact and vendor modules being built separately for each device.
2.9.6 GKI Compliance and Stability¶
The GKI architecture imposes strict requirements on kernel modules:
- KMI (Kernel Module Interface) stability: The interface between the generic kernel and vendor modules must remain stable within a GKI release. Vendor modules compiled against GKI 6.1 must work with any GKI 6.1 kernel.
- Symbol lists: The GKI kernel exports a defined set of symbols that vendor modules can use. The symbol list is version-controlled.
- Module signing: All GKI modules must be signed with the GKI signing key.
- ABI monitoring: Automated tools compare the kernel ABI between builds to detect breaking changes.
Kleaf integrates these requirements into its build rules, automatically checking KMI compliance and generating signed modules.
2.9.7 Kleaf vs. Traditional Kernel Build¶
Comparing the traditional kernel build with Kleaf:
| Aspect | Traditional (build/build.sh) |
Kleaf (Bazel) |
|---|---|---|
| Build tool | Shell scripts + Make | Bazel |
| Hermeticity | Depends on host tools | Fully hermetic |
| Caching | None (or manual) | Built-in content-addressable |
| Remote execution | No | Yes (via RBE) |
| Incremental builds | Limited | Full Bazel incremental |
| Toolchain management | Manual | Bazel toolchain rules |
| Reproducibility | Best-effort | Guaranteed |
| Configuration | build.config files | Bazel configs + build.config |
| Multi-device support | Sequential | Parallel |
The migration to Kleaf has been one of AOSP's most successful Bazel integrations, demonstrating the benefits of Bazel's hermetic build model.
2.9.8 Key Kleaf Files¶
| File | Purpose |
|---|---|
BUILD.bazel |
Top-level toolchain declarations |
clang_toolchain.bzl |
Clang toolchain rule definitions |
architecture_constants.bzl |
Supported architecture definitions |
clang_config.bzl |
Clang configuration (flags, features) |
clang_toolchain_repository.bzl |
Repository rules for user toolchains |
common.bzl |
Common utilities |
linux.bzl |
Linux-specific configuration |
android.bzl |
Android-specific configuration |
empty_toolchain.bzl |
No-op toolchain for unsupported platforms |
template_BUILD.bazel |
Template for generated BUILD files |
2.10 Try It: Build AOSP for the Emulator¶
This section provides a step-by-step guide to building AOSP from source and running it on the Android Emulator. This is the fastest way to get a working AOSP build and start making changes.
2.10.1 System Preparation¶
Step 1: Ensure you have the prerequisites.
You need a Linux machine (Ubuntu 22.04 LTS recommended) with at least 32 GB of RAM, 400 GB of free disk space (SSD strongly recommended), and a multicore CPU.
# Install required packages (Ubuntu/Debian)
sudo apt-get update
sudo apt-get install -y git-core gnupg flex bison build-essential \
zip curl zlib1g-dev libc6-dev-i386 libncurses5 \
lib32z1-dev libgl1-mesa-dev libxml2-utils xsltproc unzip \
fontconfig python3 python3-pip openjdk-21-jdk
# Install repo
mkdir -p ~/bin
curl https://storage.googleapis.com/git-repo-downloads/repo > ~/bin/repo
chmod a+x ~/bin/repo
export PATH=~/bin:$PATH
# Configure git (required by repo)
git config --global user.name "Your Name"
git config --global user.email "you@example.com"
Step 2: Create a working directory.
2.10.2 Fetching the Source¶
Step 3: Initialize the repo workspace.
# Initialize with the latest release branch
repo init -u https://android.googlesource.com/platform/manifest \
-b android16-qpr2-release
# For a faster initial sync, use partial clones:
# repo init -u https://android.googlesource.com/platform/manifest \
# -b android16-qpr2-release \
# --partial-clone \
# --clone-filter=blob:limit=10M
Step 4: Sync the source.
# Full sync -- this takes 1-3 hours on a good connection
repo sync -c -j$(nproc) --no-tags
# For subsequent syncs (much faster):
# repo sync -c -j$(nproc) --no-tags --optimized-fetch
2.10.3 Setting Up the Build Environment¶
Step 5: Source envsetup.sh.
You will see output like:
including device/generic/goldfish/vendorsetup.sh
including device/google/cuttlefish/vendorsetup.sh
...
Step 6: Select a build target with lunch.
For the emulator, use one of these targets:
# ARM64 emulator (recommended for Apple Silicon Macs or ARM servers)
lunch aosp_arm64-trunk_staging-eng
# x86_64 emulator (recommended for Intel/AMD hosts -- faster emulation)
lunch sdk_phone64_x86_64-trunk_staging-eng
# Shorthand (uses defaults: trunk_staging release, eng variant)
lunch aosp_arm64
The output will show the build configuration:
============================================
PLATFORM_VERSION_CODENAME=VanillaIceCream
PLATFORM_VERSION=16
PRODUCT_SOONG_NAMESPACES=...
TARGET_PRODUCT=aosp_arm64
TARGET_BUILD_VARIANT=eng
TARGET_ARCH=arm64
TARGET_ARCH_VARIANT=armv8-a
TARGET_CPU_VARIANT=generic
HOST_OS=linux
HOST_OS_EXTRA=...
HOST_ARCH=x86_64
OUT_DIR=out
============================================
2.10.4 Building¶
Step 7: Start the build.
# Build everything (the "droid" target is the default)
m -j$(nproc)
# Or equivalently:
m droid -j$(nproc)
The -j flag controls parallelism. On a 16-core machine with 64 GB RAM, a
first build takes approximately 2-4 hours. Subsequent incremental builds are
much faster (minutes for small changes).
Build progress is displayed in a compact format:
[ 1% 245/24532] //frameworks/base/core/java:framework-minus-apex
[ 2% 489/24532] //external/protobuf:libprotobuf-java-nano
...
[ 99% 24500/24532] //build/make/target/product:system_image
[100% 24532/24532] Build completed successfully
Common build targets:
| Target | What it builds |
|---|---|
m or m droid |
Full platform build (all images) |
m systemimage |
Just the system image |
m vendorimage |
Just the vendor image |
m bootimage |
Just the boot image |
m Settings |
Just the Settings app module |
m framework-minus-apex |
Just the framework JAR |
m nothing |
Run build system setup only (no compilation) |
m clean |
Delete the entire out/ directory |
Step 8: Verify the build outputs.
ls out/target/product/generic_arm64/
# You should see:
# android-info.txt boot.img ramdisk.img super.img
# system.img userdata.img vendor.img vendor_boot.img
# ...
2.10.5 Running the Emulator¶
Step 9: Launch the emulator.
The emulator will:
- Locate the built images in
$ANDROID_PRODUCT_OUT - Start a QEMU-based virtual machine
- Boot Android using your freshly built images
Useful emulator flags:
# Specify RAM size
emulator -memory 4096
# Disable GPU acceleration (if you have driver issues)
emulator -gpu swiftshader_indirect
# Use a specific skin/resolution
emulator -skin 1080x1920
# Enable verbose kernel logs
emulator -show-kernel
# Wipe user data (fresh start)
emulator -wipe-data
# Run headless (no GUI window)
emulator -no-window
2.10.6 Making Changes and Rebuilding¶
Step 10: Make a change and rebuild incrementally.
The power of building from source is the ability to modify anything. For example, to add a system property:
# Edit a file
vi frameworks/base/core/java/android/os/Build.java
# Rebuild just the affected module
m framework-minus-apex
# Or rebuild everything (Ninja will only rebuild what changed)
m
For a change to a system app:
# Edit Settings source
vi packages/apps/Settings/src/com/android/settings/Settings.java
# Rebuild just Settings
m Settings
# Push the rebuilt APK to a running emulator
adb install -r out/target/product/generic_arm64/system/priv-app/Settings/Settings.apk
# Or reboot the emulator to pick up all changes
adb reboot
2.10.7 Debugging Build Failures¶
Build failures in AOSP can be daunting due to the size of the codebase. Here are strategies for diagnosing common issues:
Missing dependencies:
error: frameworks/base/core/java/android/os/Foo.java:5: error: cannot find symbol
import com.android.internal.bar.Baz;
This usually means a dependency is missing in the module's Android.bp file.
Check what module provides the missing class:
# Search for the class definition
grep -rn "class Baz" frameworks/ --include="*.java"
# Or use the module index
allmod | grep -i baz
Soong/Blueprint parse errors:
error: build/soong/cc/cc.go:123: module "libfoo": depends on "libbar" which
is not visible to this module
This is a visibility error. The depended-upon module needs to add the
depending module's package to its visibility list.
Ninja execution errors:
The actual compiler error will be in the output. You can re-run the failing command directly:
Out-of-memory during build:
If Ninja gets killed by the OOM killer, reduce parallelism:
# Limit to 8 parallel jobs (instead of auto-detecting CPU count)
m -j8
# Or set a memory limit per job
export NINJA_STATUS="[%f/%t %r] "
Stale build outputs:
If you suspect the build cache is corrupted:
# Delete Soong intermediates for a specific module
rm -rf out/soong/.intermediates/frameworks/base/core/java/framework-minus-apex/
# Or delete all intermediates (forces full rebuild)
m clean
# Nuclear option: delete everything
rm -rf out/
2.10.8 Debugging Soong Itself¶
Soong provides built-in debugging support for when you need to understand or modify the build system itself.
Generating documentation:
This generates HTML documentation for all registered module types and their properties.
Debugging with Delve:
From build/soong/README.md:
# Debug soong_build (the main Soong binary)
SOONG_DELVE=5006 m nothing
# Debug only specific steps
SOONG_DELVE=2345 SOONG_DELVE_STEPS='build,modulegraph' m
# Debug soong_ui (the build driver)
SOONG_UI_DELVE=5006 m nothing
Then connect with a debugger (e.g., IntelliJ IDEA or dlv connect :5006).
Querying the module graph:
# Generate the module graph
m json-module-graph
# Generate queryable module info
m module-info
# The output is at:
# out/target/product/<device>/module-info.json
The module-info.json file contains machine-readable information about every
module in the build, including paths, dependencies, and installed locations.
2.10.9 Using Cuttlefish Instead of Goldfish¶
While this chapter focused on the Goldfish emulator (the traditional AOSP emulator), Google also maintains Cuttlefish -- a virtual device that runs in a cloud-friendly environment:
# Build for Cuttlefish
lunch aosp_cf_x86_64_phone trunk_staging eng
m
# Launch Cuttlefish (requires specific host setup)
launch_cvd
Cuttlefish advantages:
- Runs as a real virtual machine (using KVM/crosvm)
- More accurate hardware emulation
- Better suited for CI/CD pipelines
- Supports multiple concurrent instances
- Can run headless on servers
Cuttlefish disadvantages:
- Requires more host setup
- Needs KVM support
- Not as widely available as the Goldfish emulator
2.10.10 Useful Development Commands¶
After sourcing envsetup.sh and running lunch, many convenience commands
are available:
# Navigate the tree
croot # cd to tree root
gomod <module> # cd to a module's directory
godir <pattern> # cd to a directory matching a pattern
# Query the build system
get_build_var TARGET_PRODUCT # Print a build variable
get_build_var PRODUCT_OUT # Print the output directory
pathmod <module> # Print a module's source path
outmod <module> # Print a module's output path
allmod # List all modules
refreshmod # Refresh the module index
# Search the source tree
cgrep <pattern> # Search C/C++ files
jgrep <pattern> # Search Java files
resgrep <pattern> # Search resource XML files
sgrep <pattern> # Search all source files
# Debug and inspect
showcommands <target> # Show Ninja commands for a target
aninja # Run Ninja directly with arguments
2.10.11 Build Performance Tips¶
-
Use an SSD. The build performs millions of small I/O operations. An SSD vs. HDD can mean a 2-5x speed difference.
-
Maximize RAM. 64 GB is recommended. With 32 GB, you may need to limit parallelism (
-j8instead of-j$(nproc)on a 16-core machine). -
Use
ccache. Theccachetool caches compilation results: -
Use a separate output directory. If your source is on a network drive, put the output on a local SSD:
-
Consider
--skip-soong-tests. During development, you can skip test generation: -
Use incremental builds. After the first full build, subsequent builds only recompile changed modules. Ninja is very efficient at detecting what needs rebuilding.
-
Use
mmfor focused development. When working on a single module,mmis much faster thanmbecause it skips the Kati phase.
2.10.12 Incremental Development Workflow¶
For day-to-day development, the typical workflow is:
graph TB
subgraph "Initial Setup (once)"
S1[repo init + sync]
S2[source envsetup.sh]
S3[lunch target]
S4[m -- full build]
S1 --> S2 --> S3 --> S4
end
subgraph "Development Loop (repeated)"
D1[Edit source files]
D2[m -- incremental build]
D3[Test on emulator]
D4[Debug and iterate]
D1 --> D2 --> D3 --> D4 --> D1
end
subgraph "Testing"
T1[atest -- run tests]
T2[adb shell -- manual testing]
T3[CTS/VTS -- compliance testing]
end
S4 --> D1
D3 --> T1
D3 --> T2
style S4 fill:#4a90d9,color:#fff
style D2 fill:#50b848,color:#fff
style T1 fill:#e8a838,color:#fff
The atest tool:
atest is AOSP's test runner that automatically discovers, builds, and runs
tests:
# Run all tests for a module
atest SettingsTests
# Run a specific test class
atest SettingsTests:com.android.settings.wifi.WifiSettingsTest
# Run tests with verbose output
atest -v FrameworksCoreTests
# List available tests
atest --list-modules
Pushing individual files:
For rapid iteration, you can push individual files to a running device without rebuilding:
# Push a rebuilt shared library
adb push out/target/product/generic_arm64/system/lib64/libfoo.so /system/lib64/
# Push a rebuilt app
adb install -r out/target/product/generic_arm64/system/app/Settings/Settings.apk
# Restart the system server to pick up framework changes
adb shell stop && adb shell start
# Or reboot entirely
adb reboot
Note: Pushing files directly only works on eng or userdebug builds where
the system partition is writable (or you can use adb remount).
2.10.13 Understanding Build Output Messages¶
During a build, Soong prints progress in a compact format. Understanding these messages helps diagnose where the build spends its time:
The fields are:
47%-- Percentage of build edges completed11523/24532-- Completed edges / total edges//frameworks/base/core/java:framework-minus-apex-- The module being builtmetalava-- The tool being run (metalava is the API documentation tool)
If the build appears stuck at a particular percentage, it is likely waiting for a long-running action to complete. Common bottlenecks include:
- D8/R8 dexing: Converting Java bytecode to DEX format
- Metalava: API compatibility checking
- Linking large binaries: Especially the framework JAR
- Image building: Creating filesystem images
You can see which actions are currently running by pressing any key during the build (Ninja will print the active actions).
2.10.14 Parallel Build Configuration¶
The AOSP build respects several parallelism controls:
# Ninja parallelism (number of simultaneous build actions)
m -j$(nproc) # Use all CPU cores (default)
m -j8 # Limit to 8 parallel actions
m -j1 # Sequential build (for debugging)
# Soong parallelism (internal to the build system setup)
# Controlled automatically based on available resources
# Java compilation sharding (in Android.bp)
android_library {
name: "Settings-core",
javac_shard_size: 50, // Compile in shards of 50 files
}
The optimal -j value depends on your machine:
- With 64+ GB RAM: use
-j$(nproc)or higher - With 32 GB RAM: use
-j$(( $(nproc) / 2 )) - With 16 GB RAM: use
-j4to-j8
Memory is often the bottleneck, not CPU. Each compiler instance can use 1-2 GB of memory, so with 32 GB of RAM you can safely run about 16 parallel compilation jobs.
2.11 Advanced Topics¶
2.11.1 The soong.<TARGET_PRODUCT>.variables Bridge¶
Soong and Kati need to share configuration information. Kati writes a JSON
file that Soong reads. The path is keyed by TARGET_PRODUCT (the product
component of your lunch combo — for
lunch aosp_cf_x86_64_phone-trunk_staging-userdebug,
TARGET_PRODUCT=aosp_cf_x86_64_phone):
The path is constructed in build/make/core/config.mk:1255:
For the aosp_cf_x86_64_phone lunch combo above, the file is
out/soong/soong.aosp_cf_x86_64_phone.variables. A typical payload looks like:
{
"Platform_sdk_version": 35,
"Platform_sdk_codename": "VanillaIceCream",
"Platform_version_active_codenames": ["VanillaIceCream"],
"DeviceName": "generic_arm64",
"DeviceArch": "arm64",
"DeviceArchVariant": "armv8-a",
"DeviceCpuVariant": "generic",
"DeviceSecondaryArch": "",
"Aml_abis": ["arm64-v8a"],
"Eng": true,
"Debuggable": true,
...
}
This file bridges the Make world (where product configuration lives) with the
Go world (where module compilation happens). When you change a product
variable in a .mk file, it flows through this file to affect Soong's
behavior. (Soong falls back to a plain out/soong/soong.variables only when
TARGET_PRODUCT is unset; a lunched build always writes the
product-suffixed file.)
2.11.2 ABI Stability and VNDK¶
The Android build system enforces ABI (Application Binary Interface) stability through several mechanisms:
- VNDK (Vendor Native Development Kit): A set of system libraries that vendors can depend on with guaranteed ABI stability across Android versions.
- AIDL interfaces: Stable IPC interfaces between system and vendor partitions.
- HIDL interfaces: Hardware Abstraction Layer interfaces (legacy, being replaced by AIDL).
- System SDK: Stable Java APIs for vendor applications.
The build system tracks which modules are part of the VNDK and enforces dependency rules:
Vendor modules can only depend on VNDK libraries and their own private libraries. The build system rejects dependencies that would cross the system/vendor boundary through non-stable interfaces.
2.11.3 Build Flags and Feature Gates¶
AOSP uses aconfig (Android Configuration) for feature flags:
// Flag declaration (in .aconfig file)
package: "com.android.settings.flags"
flag {
name: "new_wifi_page"
namespace: "settings_ui"
description: "Enable the redesigned WiFi settings page"
bug: "b/123456789"
}
Feature flags are resolved at build time based on the release configuration:
// Using a flag in Android.bp
cc_library {
name: "libwifi_settings",
srcs: select(release_flag("RELEASE_NEW_WIFI_PAGE"), {
true: ["new_wifi_page.cpp"],
default: ["old_wifi_page.cpp"],
}),
}
This mechanism allows the same source tree to produce different builds depending on the release configuration, without requiring separate branches.
2.11.4 Build System Metrics¶
The AOSP build system collects detailed metrics about build performance:
# Build with metrics collection
m --build-event-log=build_event.log
# View build metrics
cat out/soong_build_metrics.pb | protoc --decode=...
Key metrics include:
- Total build time
- Time spent in each phase (Soong, Kati, Ninja)
- Number of modules processed
- Cache hit rates
- Memory usage peaks
- I/O statistics
These metrics are invaluable for identifying build performance bottlenecks and tracking improvements across releases.
2.11.5 Reproducible Builds¶
AOSP strives for reproducible builds -- given the same source code and build environment, the output should be identical. This is achieved through:
- Fixed timestamps: Build outputs use deterministic timestamps rather than the current time.
- Sorted inputs: File lists and directory traversals are sorted to eliminate ordering-dependent variations.
- Hermetic toolchain: Prebuilt compilers and tools are checked into the repository.
- Sandboxed builds: Soong restricts access to files outside the declared inputs.
- BUILD_DATETIME_FILE: A fixed build timestamp used across all build rules.
Reproducibility is important for:
- Security auditing (verifying that a binary matches its source)
- CI/CD caching (identical inputs produce identical outputs)
- Regulatory compliance (some markets require reproducible builds)
2.11.6 Build System Internals: Module Variant Architecture¶
One of the most complex aspects of the build system is module variant
management. A single cc_library declaration can expand into many variants:
graph TB
LIB[cc_library: libfoo]
subgraph "Architecture Variants"
ARM64[android_arm64]
X86[android_x86_64]
HOST[linux_glibc_x86_64]
end
subgraph "Link Type Variants"
SHARED[shared]
STATIC[static]
end
subgraph "APEX Variants"
PLATFORM[platform]
WIFI_APEX[com.android.wifi]
MEDIA_APEX[com.android.media]
end
subgraph "Sanitizer Variants"
NORMAL[normal]
ASAN[asan]
HWASAN[hwasan]
end
LIB --> ARM64
LIB --> X86
LIB --> HOST
ARM64 --> SHARED
ARM64 --> STATIC
SHARED --> PLATFORM
SHARED --> WIFI_APEX
SHARED --> MEDIA_APEX
PLATFORM --> NORMAL
PLATFORM --> ASAN
PLATFORM --> HWASAN
style LIB fill:#4a90d9,color:#fff
style ARM64 fill:#50b848,color:#fff
style SHARED fill:#e8a838,color:#fff
style PLATFORM fill:#d94a4a,color:#fff
A single cc_library can thus expand into dozens of variants, each producing
its own binary. The mutator system handles this expansion systematically:
- Architecture mutator: Creates one variant per target architecture (arm64, x86_64, etc.) plus host variants.
- Link type mutator: Creates shared and static library variants.
- APEX mutator: Creates one variant per APEX the library appears in, plus a platform variant.
- Sanitizer mutator: Creates variants for ASan, TSan, HWSan, etc.
- Image mutator: Creates variants for different partition images.
This is why the out/soong/.intermediates/ directory is so large -- it
contains separate build artifacts for every variant of every module.
Summary¶
This chapter covered the complete lifecycle of an AOSP build, from fetching the source to running the result on an emulator. Here are the key takeaways:
graph LR
subgraph "Source Management"
REPO[repo tool]
MANIFEST[Manifest XML]
GIT[Git repositories]
end
subgraph "Build System"
BP["Blueprint<br/>Generic framework"]
SOONG["Soong<br/>Android-specific"]
MAKE["Make/Kati<br/>Product config + legacy"]
NINJA["Ninja<br/>Execution"]
end
subgraph "Configuration"
ENVSETUP[envsetup.sh]
LUNCH[lunch command]
PRODUCT[Product .mk files]
BOARD[BoardConfig.mk]
end
subgraph "Outputs"
IMAGES[Partition images]
APEX_OUT[APEX packages]
end
REPO --> GIT
MANIFEST --> REPO
ENVSETUP --> LUNCH
LUNCH --> PRODUCT
PRODUCT --> BOARD
BP --> SOONG
SOONG --> NINJA
MAKE --> NINJA
BOARD --> MAKE
NINJA --> IMAGES
NINJA --> APEX_OUT
style BP fill:#4a90d9,color:#fff
style SOONG fill:#50b848,color:#fff
style MAKE fill:#e8a838,color:#fff
style NINJA fill:#d94a4a,color:#fff
Key files to remember:
| File | Purpose |
|---|---|
.repo/manifests/default.xml |
Manifest defining all repositories |
build/make/envsetup.sh |
Shell environment setup (1,187 lines) |
build/soong/soong_ui.bash |
Build system entry point |
build/soong/README.md |
Soong/Android.bp reference documentation |
build/blueprint/context.go |
Blueprint core (5,781 lines) |
build/make/core/envsetup.mk |
Core build variable setup |
build/make/core/config.mk |
Build configuration entry point |
build/make/target/product/*.mk |
Generic product definitions |
build/soong/apex/apex.go |
APEX build logic (3,001 lines) |
prebuilts/clang/host/linux-x86/kleaf/ |
Kernel build toolchain rules |
Three things the build system does:
- Parses thousands of
Android.bpandAndroid.mkfiles to build a dependency graph of all modules in the tree. - Configures the build based on the selected product, architecture, and variant, using product makefiles and board configuration.
- Executes the build through Ninja, which orchestrates parallel compilation of C/C++, Java, Kotlin, Rust, and other languages, then assembles the results into flashable partition images.
In the next chapter, we will explore the runtime architecture of Android --
what happens when these images boot on a device, from the bootloader through
init to the fully running Android system.
2.12 Build System Reference Tables¶
This section provides consolidated reference tables for quick lookup during development.
2.12.1 Complete List of Common Build Commands¶
| Command | Purpose | Example |
|---|---|---|
source build/envsetup.sh |
Initialize build environment | Run once per terminal session |
lunch <target> |
Select build target | lunch aosp_arm64 |
m |
Build from tree root | m or m droid |
m <module> |
Build a specific module | m Settings |
m <image> |
Build a specific image | m systemimage |
mm |
Build current directory | cd frameworks/base && mm |
mmm <dir> |
Build specified directory | mmm packages/apps/Settings |
m clean |
Delete output directory | |
m nothing |
Run setup only | Useful for checking config |
m soong_docs |
Generate module docs | Output in out/soong/docs/ |
m json-module-graph |
Generate module graph | |
m module-info |
Generate module index | |
atest <test> |
Run a test | atest SettingsTests |
croot |
cd to tree root | |
gomod <module> |
cd to module's source | gomod Settings |
pathmod <module> |
Print module's path | pathmod Settings |
outmod <module> |
Print module's output path | outmod Settings |
allmod |
List all modules | |
refreshmod |
Refresh module index | |
printconfig |
Show build configuration | |
get_build_var <var> |
Print a build variable | get_build_var TARGET_PRODUCT |
showcommands <target> |
Show build commands | |
bpfmt -w . |
Format Android.bp files | |
androidmk Android.mk |
Convert mk to bp | |
tapas <app> |
Build unbundled app | tapas Camera eng |
banchan <apex> |
Build unbundled APEX | banchan com.android.wifi arm64 |
2.12.2 Key Environment Variables¶
| Variable | Set By | Purpose |
|---|---|---|
TOP |
envsetup.sh | Root of the source tree |
TARGET_PRODUCT |
lunch | Product name (e.g., aosp_arm64) |
TARGET_BUILD_VARIANT |
lunch | Build variant (eng/userdebug/user) |
TARGET_RELEASE |
lunch | Release configuration |
TARGET_BUILD_TYPE |
lunch | Always release |
TARGET_BUILD_APPS |
tapas/banchan | Unbundled app/APEX names |
ANDROID_PRODUCT_OUT |
lunch | Path to device output directory |
ANDROID_HOST_OUT |
lunch | Path to host tools output |
ANDROID_BUILD_TOP |
envsetup.sh | Same as TOP (deprecated) |
ANDROID_JAVA_HOME |
lunch | Path to JDK |
OUT_DIR |
User (optional) | Override output directory (default: out) |
USE_CCACHE |
User (optional) | Enable ccache (1 to enable) |
CCACHE_DIR |
User (optional) | ccache directory location |
SOONG_DELVE |
User (optional) | Debug port for soong_build |
SOONG_UI_DELVE |
User (optional) | Debug port for soong_ui |
NINJA_STATUS |
User (optional) | Custom Ninja status format |
2.12.3 Common Android.bp Properties for cc_library¶
| Property | Type | Purpose |
|---|---|---|
name |
string | Module name (must be unique) |
srcs |
list of strings | Source files (supports globs) |
exclude_srcs |
list of strings | Files to exclude from srcs |
generated_sources |
list of strings | Source-generating modules |
generated_headers |
list of strings | Header-generating modules |
cflags |
list of strings | C/C++ compiler flags |
cppflags |
list of strings | C++ only compiler flags |
conlyflags |
list of strings | C only compiler flags |
asflags |
list of strings | Assembly flags |
ldflags |
list of strings | Linker flags |
shared_libs |
list of strings | Shared library dependencies |
static_libs |
list of strings | Static library dependencies |
whole_static_libs |
list of strings | Static libs included entirely |
header_libs |
list of strings | Header-only dependencies |
runtime_libs |
list of strings | Runtime-only shared libraries |
local_include_dirs |
list of strings | Private include paths |
export_include_dirs |
list of strings | Public include paths |
export_shared_lib_headers |
list of strings | Transitively export headers |
stl |
string | C++ STL selection |
host_supported |
bool | Build for host too |
device_supported |
bool | Build for device (default: true) |
vendor |
bool | Install to vendor partition |
vendor_available |
bool | Available to vendor modules |
recovery_available |
bool | Available in recovery |
apex_available |
list of strings | APEX modules this can be in |
min_sdk_version |
string | Minimum SDK version |
defaults |
list of strings | Defaults modules to inherit from |
visibility |
list of strings | Visibility rules |
enabled |
bool | Whether the module is enabled |
arch |
map | Architecture-specific properties |
target |
map | Target-specific properties (android/host) |
multilib |
map | Multilib properties (lib32/lib64) |
sanitize |
map | Sanitizer configuration |
strip |
map | Strip configuration |
pack_relocations |
bool | Pack relocations (default: true) |
allow_undefined_symbols |
bool | Allow undefined symbols |
nocrt |
bool | Don't link C runtime startup |
no_libcrt |
bool | Don't link compiler runtime |
stubs |
map | Generate stubs for versioning |
vndk |
map | VNDK configuration |
2.12.4 Common Android.bp Properties for android_app¶
| Property | Type | Purpose |
|---|---|---|
name |
string | Module name |
srcs |
list of strings | Java/Kotlin source files |
resource_dirs |
list of strings | Android resource directories |
asset_dirs |
list of strings | Asset directories |
manifest |
string | AndroidManifest.xml path |
static_libs |
list of strings | Static Java library dependencies |
libs |
list of strings | Compile-time-only dependencies |
platform_apis |
bool | Use platform (hidden) APIs |
certificate |
string | Signing certificate |
privileged |
bool | Install as privileged app |
overrides |
list of strings | Apps this replaces |
required |
list of strings | Modules that must be installed too |
dex_preopt |
map | DEX pre-optimization settings |
optimize |
map | ProGuard/R8 optimization |
aaptflags |
list of strings | Extra AAPT flags |
package_name |
string | Override package name |
sdk_version |
string | SDK version to build against |
min_sdk_version |
string | Minimum SDK version |
target_sdk_version |
string | Target SDK version |
uses_libs |
list of strings | Shared library dependencies |
optional_uses_libs |
list of strings | Optional shared library deps |
jni_libs |
list of strings | JNI native libraries |
use_resource_processor |
bool | Enable resource processor |
javac_shard_size |
int | Files per javac shard |
errorprone |
map | Error-prone checker config |
2.12.5 Directory Structure Quick Reference¶
| Path | Contents |
|---|---|
art/ |
Android Runtime (ART VM, dex2oat, etc.) |
bionic/ |
C library (libc, libm, libdl, linker) |
bootable/ |
Recovery, bootloader libraries |
build/blueprint/ |
Blueprint meta-build framework |
build/make/ |
Make-based build system and product config |
build/soong/ |
Soong build system (Go) |
build/pesto/ |
Bazel integration experiments |
build/release/ |
Release configuration |
cts/ |
Compatibility Test Suite |
dalvik/ |
Dalvik VM (historical) |
development/ |
Developer tools and samples |
device/ |
Device configurations |
device/generic/goldfish/ |
Emulator (Goldfish) device |
device/google/cuttlefish/ |
Virtual device (Cuttlefish) |
external/ |
Third-party projects (700+ repos) |
frameworks/base/ |
Core Android framework |
frameworks/native/ |
Native framework (SurfaceFlinger, Binder) |
frameworks/av/ |
Audio/Video framework |
hardware/interfaces/ |
HIDL/AIDL HAL definitions |
kernel/ |
Kernel build config and prebuilts |
libcore/ |
Core Java libraries (OpenJDK-based) |
packages/apps/ |
System applications |
packages/modules/ |
Mainline modules (APEX) |
packages/providers/ |
Content providers |
packages/services/ |
System services |
prebuilts/ |
Prebuilt tools (Clang, JDK, SDK, etc.) |
system/core/ |
Core system utilities (init, adb, logcat) |
system/extras/ |
Additional system utilities |
system/sepolicy/ |
SELinux policy |
tools/ |
Development tools |
vendor/ |
Vendor-specific code |
Glossary of Build System Terms¶
| Term | Definition |
|---|---|
| ABI | Application Binary Interface. The binary-level interface between two program modules, defining data types, sizes, alignment, calling conventions, and system call numbers. |
| AIDL | Android Interface Definition Language. Used to define stable IPC interfaces between system components. |
| Android.bp | Blueprint file format used by Soong. Declarative, JSON-like syntax for defining build modules. |
| Android.mk | Legacy Make-based module definition format. Still supported but being phased out in favor of Android.bp. |
| APEX | Android Pony EXpress. A container format for independently updatable system components. |
| Blueprint | The meta-build framework underlying Soong. A Go library for parsing module definitions and generating Ninja manifests. |
| BoardConfig.mk | Device-level configuration file that defines architecture, partition sizes, and hardware features. |
| bp2build | Tool that converts Android.bp files to Bazel BUILD files for the Soong-to-Bazel migration. |
| bpfmt | Blueprint file formatter (analogous to gofmt for Go). |
| Context | The central state object in Blueprint that orchestrates the four build phases. |
| Cuttlefish | A cloud-friendly Android virtual device (alternative to the Goldfish emulator). |
| Dynamic Partitions | A logical volume system that allows flexible partition sizing within a single super.img. |
| GKI | Generic Kernel Image. A standardized kernel binary shared across devices of the same Android version. |
| Goldfish | The traditional Android emulator device, based on QEMU. |
| GSI | Generic System Image. A system.img that should work on any device compliant with Project Treble. |
| HIDL | Hardware Interface Definition Language. Legacy HAL interface language being replaced by AIDL. |
| Kati | A Make-compatible build tool written in Go, used by AOSP instead of GNU Make. |
| Kleaf | Bazel-based kernel build system. The name is a portmanteau of "kernel" and "leaf" (Bazel). |
| KMI | Kernel Module Interface. The stable ABI between the GKI kernel and vendor kernel modules. |
| Mainline | The Android project for delivering OS component updates via the Play Store using APEX and APK. |
| Manifest | An XML file defining the set of Git repositories that make up the AOSP source tree. |
| Module | The basic unit of building in Soong. Analogous to a "target" in Make or Bazel. |
| Mutator | A Blueprint function that visits modules to modify them (e.g., creating architecture variants). |
| Ninja | A fast, low-level build execution tool. Soong and Kati generate Ninja manifests; Ninja executes them. |
| PDK | Platform Development Kit. A subset of AOSP used by hardware partners for early device bring-up. |
| Provider | Blueprint's mechanism for passing structured data between modules in the dependency graph. |
| RBE | Remote Build Execution. Distributes build actions across a cluster for faster builds. |
| repo | A Python tool that manages multiple Git repositories using a manifest file. |
| Soong | Android's primary build system, built on top of Blueprint. Processes Android.bp files. |
| soong_ui | The build system driver/entry point. Orchestrates Soong, Kati, and Ninja. |
| super.img | The container image for dynamic partitions. Contains system, vendor, product, etc. |
| Treble | The Android architecture that separates the OS framework from vendor-specific code, enabling faster updates. |
| Variant | One of multiple builds of the same module (e.g., arm64 shared, arm64 static, x86_64 shared, etc.). |
| VNDK | Vendor Native Development Kit. A set of system libraries with guaranteed ABI stability for vendors. |
Further Reading¶
In-Tree Documentation¶
These files are available in your AOSP checkout and provide authoritative reference information:
build/soong/README.md-- Comprehensive Soong and Android.bp reference (738 lines). Covers module syntax, variables, conditionals, namespaces, visibility, and debugging.build/blueprint/doc.go-- Blueprint framework architecture overview. Explains the meta-build concept, four build phases, and mutator system.build/make/Changes.md-- Chronological log of build system changes, deprecated variables, and migration guides.build/make/README.md-- Make layer documentation and links.build/soong/docs/best_practices.md-- Best practices for writing Android.bp files, including how to remove conditionals.build/soong/docs/selects.md-- Detailed documentation for select statements (the new conditional mechanism).build/soong/docs/perf.md-- Build performance optimization guide.build/soong/docs/compdb.md-- Generating compile_commands.json for IDE integration (VSCode, CLion, etc.).prebuilts/clang/host/linux-x86/kleaf/README.md-- Kleaf toolchain documentation for kernel builds.
External Resources¶
- Android Source website: https://source.android.com/setup/build -- Official getting started guide for building AOSP.
- Android Build Cookbook: https://source.android.com/setup/build/building -- Step-by-step build instructions.
- APEX documentation: https://source.android.com/devices/tech/ota/apex -- Official APEX architecture and development guide.
- GKI documentation: https://source.android.com/devices/architecture/kernel/generic-kernel-image -- Generic Kernel Image architecture.
- Project Treble: https://source.android.com/devices/architecture -- The vendor/system partition split architecture.
- Repo tool repository: https://gerrit.googlesource.com/git-repo/ -- Source code and documentation for the repo tool.
- Ninja build system: https://ninja-build.org/ -- Ninja's documentation and design philosophy.
- Bazel documentation: https://bazel.build/ -- Comprehensive Bazel build system documentation.
- Gerrit Code Review: https://android-review.googlesource.com/ -- The AOSP code review platform.
- Android CI: https://ci.android.com/ -- Continuous integration dashboard showing latest build status.
- Android Code Search: https://cs.android.com/ -- Web-based code search for the entire AOSP tree.
Generated Documentation¶
After building, these additional resources are available:
# Module type reference (HTML)
m soong_docs
# Output: out/soong/docs/soong_build.html
# Module dependency graph (JSON)
m json-module-graph
# Output: out/soong/module_graph.json
# Module info database
m module-info
# Output: out/target/product/<device>/module-info.json
# Installed file list
# Output: out/target/product/<device>/installed-files.txt