Soong 构建系统原理

非常好的问题!我们来深入解析 AOSP 的 Soong 构建系统,它的编译原理、设计思想以及与传统构建系统的区别。

🌟 一句话总结

Soong 是 Google 为 AOSP 量身打造的下一代构建系统,它使用 Go 语言编写,基于 Ninja 构建引擎,通过 Android.bp 文件描述模块依赖和构建规则,目标是替代旧的 Make 系统(Android.mk),实现更快、更清晰、更可维护的系统级编译。


🔧 Soong 的核心组成

组件 作用
soong_ui 用户接口,替代 make,调用 soong_build
soong_build (Go) Soong 的核心逻辑,解析 Android.bp
Blueprint Soong 的底层框架(由 Google 开发),用于解析 .bp 文件
Android.bp 构建配置文件(类似 JSON 的语法)
Ninja 实际执行编译命令的底层引擎
Kati 将旧的 Android.mk 转成 Ninja 规则(兼容层)

🔄 Soong 的编译流程(原理详解)

✅ 阶段 1:解析阶段(Parse)

1
m

执行时,soong_ui 会启动,它做以下事情:

  1. 扫描整个 AOSP 源码树中的 Android.bpAndroid.mk 文件
  2. Android.bp 解析成内部的模块结构(Module Graph)
  3. Android.mk 通过 kati 转换为等效的 Soong 模块
  4. 生成一个巨大的 build.ninja 文件

🔍 这个过程类似于:
Android.bp + Android.mkSoong 中间表示build.ninja


✅ 阶段 2:生成 Ninja 文件

Soong 会生成多个 Ninja 文件:

  • out/combined_build.ninja:总入口
  • out/soong/build.ninja:由 Soong 生成的规则
  • out/kati/Android.*.ninja:由 kati 从 Android.mk 生成的规则

这些 .ninja 文件定义了:

  • 哪些文件需要编译
  • 编译命令(如 clang, aapt2, javac
  • 输出路径(如 out/target/product/...

✅ 阶段 3:执行构建(Build)

Ninja 根据 build.ninja 文件,并行执行编译命令

1
ninja -f out/combined_build.ninja <target>
  • Ninja 只编译发生变化的文件
  • 支持高度并行(-j 参数)
  • 依赖关系由 Soong 提前计算好

📐 Soong 的核心设计理念

特性 说明
🧱 模块化(Modular) 每个组件(APK、库、可执行文件)都是一个独立模块
📄 声明式配置(Declarative) Android.bp 是纯数据文件,不包含逻辑
高性能解析 Go 编写,比 shell 解析 Makefile 快得多
🔗 依赖自动分析 自动计算模块依赖图,避免循环依赖
🧩 可扩展性 支持自定义模块类型(如 android_app, cc_library
🔄 增量构建 结合 Ninja,只重新编译变化部分

📄 Android.bp vs Android.mk

特性 Android.bp (Soong) Android.mk (Make)
语法 类 JSON,声明式 Makefile,命令式
可读性 ✅ 高(结构清晰) ❌ 低(shell 混杂)
执行速度 ✅ 快(Go 解析) ❌ 慢(shell 解析)
逻辑控制 ❌ 不支持 if/for ✅ 支持 shell 脚本
模块依赖 ✅ 自动分析 ❌ 手动管理易出错
扩展性 ✅ 支持 Blueprint 语法 ⚠️ 依赖 Make 函数

示例对比

Android.bp

1
2
3
4
5
6
android_app {
name: "MyApp",
srcs: ["src/**/*.java"],
manifest: "AndroidManifest.xml",
certificate: "platform",
}

Android.mk

1
2
3
4
5
6
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := MyApp
LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_CERTIFICATE := platform
include $(BUILD_PACKAGE)

👉 Android.bp 更简洁、安全、易维护。


🧠 Soong 的“编译原理”本质

Soong 本身 不直接编译代码,它是一个:

构建规则生成器(Build Rule Generator)

它的原理类似于:

1
[Android.bp] → Soong (Go) → [build.ninja] → Ninja → [调用 clang/aapt2/javac/...] → 最终产物

所以:

  • Soong 负责 “计划”:谁依赖谁?怎么编译?
  • Ninja 负责 “执行”:运行编译命令
  • 编译器(clang、javac)负责 “真正编译”

🛠 Soong 支持的模块类型(常见)

模块类型 用途
android_app 编译 APK(带资源)
cc_library C/C++ 静态库
cc_library_shared C/C++ 动态库(.so)
java_library Java 源码(.jar)
java_import Java 库(.jar)
android_library Android Java 库(带资源)
android_library_import 导入预编译的 .aar
prebuilt_apk 导入已编译的 APK
filegroup 文件集合

🧪 Soong 的优势(为什么 AOSP 要换它?)

优势 说明
更快的解析速度 Go 比 shell 快 10 倍以上
更好的并行构建 Ninja 天生支持高度并行
更清晰的依赖管理 模块化设计,避免“Make hell”
更强的类型检查 Android.bp 有 schema 验证
更容易自动化 JSON-like 结构,适合工具处理
统一构建系统 未来完全替代 Make,不再混合

🚫 Soong 的局限性

限制 说明
❌ 不支持复杂逻辑 不能写 if-else 或循环(有意为之)
❌ 宏和变量较少 不能像 Make 那样灵活
❌ 调试困难 错误信息有时不直观
❌ 学习成本 需要理解 Blueprint 语法

💡 所以 AOSP 仍然保留 Android.mk 兼容层(通过 kati


✅ 总结:Soong 编译原理图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
+------------------+     +------------------+     +------------------+
| Android.bp | | Android.mk | | Prebuilts (.so, .jar) |
+------------------+ +------------------+ +------------------+
| | |
+-----------+-----------+------------------------+
|
+-----------v-----------+
| Soong (Go) | ← 解析所有模块,构建依赖图
+-----------+-----------+
|
+-----------v-----------+
| Generate build.ninja| ← 生成 Ninja 构建规则
+-----------+-----------+
|
+-----------v-----------+
| Ninja Engine | ← 并行执行编译命令
+-----------+-----------+
|
+-----------v-----------+
| clang / aapt2 / javac | ← 真正的编译工具
+-----------+-----------+
|
+-----------v-----------+
| Out: APK, SO, BIN |
+-----------------------+

🛠️ 小技巧:查看 Soong 生成的 Ninja 文件

1
2
3
4
5
# 查看生成的 Ninja 文件
cat out/soong/build.ninja | grep "MyApplication"

# 查看某个模块的编译命令
ninja -v MyApplication

✅ 一句话收尾

Soong 是 AOSP 的“构建大脑”,它用声明式配置管理千万行代码的依赖关系,再交给 Ninja 这个“执行引擎”高效编译,最终构建出完整的 Android 系统镜像。

如果你是系统开发者,理解 Soong 是掌握 AOSP 编译的关键!