Building AOSP
What a build actually involves: repo and the manifest, envsetup and lunch, Soong and Ninja, and why a full build is measured in hours and hundreds of gigabytes. This page explains the pipeline rather than pretending you can follow along on a laptop.
Building AOSP is not a step you add to a tutorial. It is an operation with real resource requirements, a build graph with hundreds of thousands of nodes, and a result that only boots on a target you chose in advance. This page explains the pipeline so that the commands make sense when you do have a machine for them.
TL;DR#
repomanages the many Git repositories that make up AOSP.repo initchooses a manifest and a branch;repo syncfetches the projects.source build/envsetup.shloads the build commands into your shell;lunchselects a product, a release configuration and a build variant.- The target format is
product-release_config-variant, for exampleaosp_cf_x86_64_only_phone-aosp_current-userdebug. mbuilds the selected target. Soong generates a Ninja graph, and Ninja executes it.- Cuttlefish is a virtual device used for platform work and CI, which is why AOSP builds are usually targeted at it rather than at a phone.
Mental model#
Think of a large printing press.
The paper order is the manifest: which projects, at which revisions, make up this edition.
The press setup is envsetup.sh and lunch: choose the paper, the ink, the binding and the
number of copies.
The typesetting is Soong: it turns the manuscript (source and Android.bp files) into a page
layout (the Ninja graph).
The printing is Ninja: it executes the layout, in parallel, reusing anything that has not changed.
And the proof copy is the emulator or Cuttlefish image you boot to see whether the edition is right.
Where the analogy breaks#
A printing press produces identical copies. An AOSP build produces a system image whose contents depend on the target, the release configuration and a large set of feature flags. Two builds from the same source can differ in behaviour because the configuration differs, which is exactly what trunk-stable development relies on.
The big picture#
Text description of this diagram
A timeline of the platform's architecture eras. The architecture lane marks Project Butter in 2012, ART in 2014, Treble in 2017 and adaptive-first in 2026; the update lane marks Treble, Mainline, GKI and the two-SDK-release schedule from 2025. Read against a build, the timeline explains why the build system changed: as the platform split into modules and release configurations, the build had to become more declarative and more configurable.
Deep dive#
Getting the source: repo and the manifest#
AOSP is not a single Git repository. It is a set of projects described by a manifest, and
repo is the tool that reads the manifest and manages the projects as one tree.
The two commands that start everything:
repo init --partial-clone --no-use-superproject -b android-latest-release \
-u https://android.googlesource.com/platform/manifest
repo sync -c -j8repo init fetches the manifest for the branch you chose and writes a .repo directory. repo sync fetches the projects the manifest names. The -c flag limits the sync to the current branch,
which is faster; -j8 is the parallelism.
The manifest is the important idea. It pins each project to a revision, so "the AOSP tree" is a specific, reproducible set of commits rather than "whatever was on main when you cloned".
Selecting a target: envsetup and lunch#
source build/envsetup.sh
lunch aosp_cf_x86_64_only_phone-aosp_current-userdebugenvsetup.sh defines the build commands (m, mm, mmm, lunch, croot and others) in your
shell. It is a shell script, not a binary, and it has to be sourced rather than executed.
lunch selects three things, which is why the target has three parts separated by dashes:
| Part | Example | What it selects |
|---|---|---|
| Product | aosp_cf_x86_64_only_phone | Which device configuration and which packages are included |
| Release configuration | aosp_current | Which feature-flag values the build uses |
| Build variant | userdebug | Optimisation, debuggability and which apps are installed |
The release configuration is the trunk-stable mechanism: the same source, built with a different configuration, produces a different release. That is why the target string has a middle part at all.1
The build variants are worth knowing:
user— release-like: production settings, no root, limited debugging.userdebug— the default for platform development: debuggable, root available, close to release performance.eng— engineering: extra tools and debug settings, not representative of performance.
Building: Soong and Ninja#
mThat single command builds the default target for the chosen product. Under the hood it runs
soong_ui, which:
- Reads every
Android.bpin the tree and evaluates the module definitions. - Resolves dependencies between modules.
- Generates a Ninja file describing every action.
- Runs Ninja, which executes the actions in parallel with caching.
Android.bp is deliberately declarative: no conditionals, no arbitrary code. Conditional behaviour
is expressed with select or with variants, which keeps the graph analysable. That is the whole
reason the format exists: a build system that can be analysed can be made fast and correct.
Building one module instead of everything is much faster, and the command takes module names:
m SystemUIThe output lands in out/, with the selected target under out/target/product/. The system image
is assembled there, along with the boot image and the vendor image for the target.
Running the result#
The build produces images, not a running system. To run them you need a target:
- Cuttlefish is a virtual device that runs on a Linux host, with virtualised hardware instead of a phone. Platform teams use it for development and CI because it is reproducible and does not require a physical device.2
- A physical device requires an unlocked bootloader and a build that matches the device's configuration, which in practice means the OEM's tree rather than plain AOSP.
- The emulator is the simplest target for a first build, and the one most tutorials use.
Booting a build is where the boot sequence from the rest of this site becomes concrete: init reads
its configuration, starts the native services, Zygote starts, and system_server comes up.
Why the build is large#
Three properties make an AOSP build expensive:
- The graph is huge. Every module is a node, and the framework alone has thousands.
- The inputs are huge. The source tree is measured in hundreds of gigabytes with build output, and the output includes compiled native libraries for several architectures.
- The toolchain is large. Clang, the Java toolchain, and the various code generators are all part of the build environment.
That is why the documentation does not give a single "requirements" number that fits everyone: the answer depends on whether you build one module, one target, or the world, and on how much of the tree you have synced.
Reproducibility and release configurations#
Two builds of the same source with the same configuration should produce the same result. That property is what makes it possible to verify a build and to investigate a difference. It is also why the release configuration is part of the target string: without it, "the same build" would be ambiguous.
The configuration also explains a common confusion. If you build aosp_current and someone else
builds a release configuration, your images can behave differently even though the source is
identical. The flags are the difference.
How it evolved#
| API | Version | What changed |
|---|---|---|
| 26 | 8.0 Oreo |
|
| 27 | 8.1 Oreo |
|
| 28 | 9 Pie |
|
| 29 | 10 |
|
| 30 | 11 |
|
| 31 | 12 |
|
| 32 | 12L |
|
| 33 | 13 Tiramisu |
|
| 34 | 14 Upside Down Cake |
|
| 35 | 15 Vanilla Ice Cream |
|
| 36 | 16 Baklava |
|
| 37 | 17 Cinnamon Bun |
|
The architecture rows since Android 8.0 explain the build's direction: as the platform gained modules and release configurations, the build gained declarative module definitions and flag-based selection.
Under the hood#
- the Go implementation of Soong, which reads Android.bp and emits Ninja
- the legacy Make build and the Kati compatibility layer
- release_config_map.textprotothe release configurations that lunch selects between
Curiosity corner#
mis a shell function, not a binary. It comes fromenvsetup.sh, which is why you have to source that script before it exists.- The target string encodes the whole build identity. Product, release configuration and variant — change any one and you have a different build.
- Cuttlefish exists because phones are bad CI machines. A virtual device is reproducible and can run in a data centre.
Try it#
repo init --partial-clone --no-use-superproject -b android-latest-release -u https://android.googlesource.com/platform/manifest
repo sync -c -j8
source build/envsetup.sh
lunch aosp_cf_x86_64_only_phone-aosp_current-userdebug
mExample output
Illustrative — this takes hours and a large disk.Build it#
N01 — Build and boot AOSP is the hands-on version, marked as not tested in this run.
Check yourself#
Three questions to answer without looking back. Open each one only after you have tried.
1. What does a Manager object in your app actually hold?
A Binder proxy to a service, not the service itself. The policy lives on the service side, usually
in system_server, and every call crosses a process boundary.
2. Why does the build use a declarative
Because a declarative module definition can be analysed. Soong can build a complete dependency
graph before running anything, which is what makes Ninja parallel and incremental. Conditionals in
a script would make the graph depend on execution.Android.bp instead of a script?
3. What is the difference between reading a facade and reading a service?
The facade defines the public contract — validation, defaults, deprecation. The service implements
the behaviour and holds the state. A bug in behaviour is almost always on the service side; a
compatibility question is almost always on the facade side.
Sources#
Sources
Every version number, API level, class name and date on this page is traced to one of these. Pages are re-verified against them; last_verified above says when.
- docsConfiguration (aconfig) overviewsource.android.comaccessed 2026-10-27↩
- docsBuild Androidsource.android.comaccessed 2026-10-27↩
- docsAndroid platform architecture (AOSP)source.android.comaccessed 2026-10-27↩
- docsModular system componentssource.android.comaccessed 2026-10-27↩
- sourceAndroid Code Search (cs.android.com)cs.android.comaccessed 2026-10-27↩