Zygote
AOSP source tournerd

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.

12 minAPI 1–37 · 17interactiveverified 2026-10-27

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#

  • repo manages the many Git repositories that make up AOSP. repo init chooses a manifest and a branch; repo sync fetches the projects.
  • source build/envsetup.sh loads the build commands into your shell; lunch selects a product, a release configuration and a build variant.
  • The target format is product-release_config-variant, for example aosp_cf_x86_64_only_phone-aosp_current-userdebug.
  • m builds 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#

Loading diagram…
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 -j8

repo 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-userdebug

envsetup.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:

PartExampleWhat it selects
Productaosp_cf_x86_64_only_phoneWhich device configuration and which packages are included
Release configurationaosp_currentWhich feature-flag values the build uses
Build variantuserdebugOptimisation, 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#

m

That single command builds the default target for the chosen product. Under the hood it runs soong_ui, which:

  1. Reads every Android.bp in the tree and evaluates the module definitions.
  2. Resolves dependencies between modules.
  3. Generates a Ninja file describing every action.
  4. 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 SystemUI

The 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#

Architecture changes12 releases
APIVersionWhat changed
268.0 Oreo
  • Project Treble: the vendor interface separates vendor and system code
  • Background execution limits
  • Broadcast and service restrictions
  • HIDL for HALs
278.1 Oreo
  • Android Go: a lighter configuration of the same platform
289 Pie
  • App Standby Buckets
  • Mainline groundwork
  • Non-SDK interface restrictions (greylists) begin
  • BiometricPrompt
2910
  • Project Mainline: APEX and updatable system modules
  • Scoped storage
  • Bubbles
  • Project Mainline starts shipping through Play
3011
  • GKI: the Generic Kernel Image splits the kernel into core + vendor modules
  • App hibernation
  • Package visibility filtering
  • Incremental app installs
3112
  • Bluetooth permissions split into the nearby-devices family
  • Exact alarm permissions
  • Foreground service launch restrictions
3212L
  • Window size classes and activity embedding
3313 Tiramisu
  • Granular media permissions replace READ_EXTERNAL_STORAGE
  • The notification permission becomes a runtime permission
  • Predictive back gesture
3414 Upside Down Cake
  • Foreground service types are mandatory
  • Selected photos access via READ_MEDIA_VISUAL_USER_SELECTED
3515 Vanilla Ice Cream
  • 16 KB page size support for native libraries
  • Edge-to-edge is enforced when targeting 35
3616 Baklava
  • The release moves to Q2 (June) with a Q4 feature drop
  • Two SDK releases per year starting with 36
  • Desktop windowing arrives in developer options then stable
3717 Cinnamon Bun
  • Strict per-device app memory limits are enforced by the system
  • Generational garbage collection is added to ART's Concurrent Mark-Compact collector
  • A lock-free MessageQueue for apps targeting API 37
  • Compose-first: new platform and Jetpack APIs target Compose only; View libraries enter maintenance mode
  • Hybrid APK signing (v3.2) combines classical and ML-DSA post-quantum signatures
  • Native library dynamic code loading must be read-only

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#

Curiosity corner#

  • m is a shell function, not a binary. It comes from envsetup.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#

The standard sequence
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
m
Example 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 Android.bp instead of a script? 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.

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.

  1. docsConfiguration (aconfig) overviewsource.android.comaccessed 2026-10-27↩
  2. docsBuild Androidsource.android.comaccessed 2026-10-27↩
  3. docsAndroid platform architecture (AOSP)source.android.comaccessed 2026-10-27↩
  4. docsModular system componentssource.android.comaccessed 2026-10-27↩
  5. sourceAndroid Code Search (cs.android.com)cs.android.comaccessed 2026-10-27↩