一、AOSP构建系统概述
大家好,我是你们这门课的老朋友。今天咱们聊聊AOSP构建系统——说白了,就是那个能把几万个源文件变成手机里能跑的系统镜像的“大管家”。
我记得刚入行那会儿,第一次编译AOSP,看着终端里滚动的日志,完全不知道背后发生了什么。后来踩了不少坑,才慢慢摸清门道。今天我就把这些经验掰开揉碎了讲给你听。
1.1 什么是AOSP构建系统
AOSP构建系统,本质上是一套自动化工具链。它的任务很简单:把C++、Java、Kotlin、Rust这些源代码,加上资源文件、配置文件、依赖库,最终打包成你能刷到手机里的系统镜像。
但实际做起来,远比想象中复杂。你想想看,一个完整的Android系统包含:
- 几百个模块:从底层内核模块到上层应用
- 多种语言:C/C++、Java、Kotlin、Rust、Go
- 跨平台编译:在x86 Linux上编译出ARM架构的代码
- 复杂的依赖关系:一个模块改了,可能引发连锁反应
构建系统就是负责把这些乱七八糟的东西理清楚。我习惯把它比作一个建筑工地的总指挥——它知道每块砖该放哪,哪个工人先干活,哪个材料还没到货。
核心职责:
- 解析模块定义(告诉系统“我要编译什么”)
- 管理依赖关系(“这个模块需要先编译那个模块”)
- 执行编译动作(调用gcc、javac、rustc等编译器)
- 打包输出产物(生成system.img、boot.img等)
1.2 构建系统的历史演变
这部分很有意思。AOSP构建系统经历了三次大的迭代,每一次都是为了解决前一代的痛点。我亲身经历了从Android.mk到Soong的切换,那段时间真是痛并快乐着。
第一阶段:Android.mk时代(Android 1.0 - 7.0)
最早的构建系统基于GNU Make。每个模块写一个Android.mk文件,语法类似这样:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := hello_world
LOCAL_SRC_FILES := hello.c
LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
include $(BUILD_EXECUTABLE)
嗯,看着挺简洁对吧?但实际项目里,问题就来了:
- Make语法太古老:变量展开顺序诡异,调试起来想砸电脑
- 增量编译慢:改一个头文件,整个模块都得重编
- 依赖管理靠猜:经常出现“我明明改了代码,为什么没重新编译”的灵异事件
我曾经在一个项目里,全量编译要4个小时。改一行日志代码,等编译结果要20分钟。那段时间我养成了一个习惯——写代码之前先泡杯咖啡,因为编译完刚好能喝。
第二阶段:Soong时代(Android 7.0 - 8.0过渡)
Google终于受不了Make了。他们搞出了Soong——用Go语言写的构建系统。模块定义文件从Android.mk变成了Android.bp:
cc_binary {
name: "hello_world",
srcs: ["hello.c"],
include_dirs: ["include"],
}
你看,Android.bp用的是类似JSON的声明式语法。好处很明显:
- 语法清晰:没有Make那些诡异的变量展开规则
- 解析快:Go语言并发解析,比Make快一个数量级
- 依赖明确:模块之间的依赖关系一目了然
但Soong有个问题——它只负责解析Android.bp,真正的编译工作还是交给Ninja去干。Soong相当于一个“翻译官”,把Android.bp翻译成Ninja能理解的.ninja文件。
小提示:Soong这个名字怎么来的?我查过资料,它是“So Not GNU Make”的缩写。Google工程师的幽默感,你懂的。
第三阶段:Blueprint时代(Android 8.0至今)
等等,你可能要问:不是说Soong吗?怎么又冒出个Blueprint?
其实Blueprint是Soong的底层框架。你可以这么理解:
- Blueprint:负责解析
Android.bp文件,生成模块依赖图 - Soong:基于Blueprint,添加了Android特有的编译逻辑(比如APK打包、资源编译)
说白了,Blueprint是Soong的“引擎”,Soong是Blueprint的“车身”。Google把底层解析逻辑抽出来做成Blueprint,方便其他项目复用。现在Chromium、Fuchsia等项目也在用Blueprint。
我个人的建议是:你不需要深究Blueprint的每一行代码,但一定要理解它的核心思想——声明式配置 + 依赖图驱动。这个思想贯穿了整个AOSP构建系统。
1.3 构建系统在AOSP中的角色
构建系统在AOSP里到底扮演什么角色?我用一张图来说明:
从这张图你能看到,构建系统处于整个AOSP开发流程的中心位置。它连接了源代码和最终产物,决定了你的开发效率。
具体来说,构建系统承担了以下几个关键角色:
| 角色 | 说明 | 我踩过的坑 |
|---|---|---|
| 依赖管理器 | 分析模块间的依赖关系,决定编译顺序 | 曾经有个模块依赖写反了,编译了3小时才发现 |
| 增量编译器 | 只重新编译修改过的模块,节省时间 | 头文件依赖没配好,改个宏导致全量重编 |
| 交叉编译工具 | 在x86主机上编译出ARM/ARM64的代码 | 忘了装交叉编译器,报错找了半天 |
| 产物打包器 | 把编译好的文件打包成镜像/APK | 打包脚本少了个文件,刷机后系统起不来 |
避坑指南:千万不要觉得构建系统只是“跑个脚本而已”。我见过太多人因为不重视构建系统,把大量时间浪费在等待编译上。记住:你对构建系统理解得越深,你的开发效率就越高。
好了,这一章的内容就到这里。构建系统就像一座冰山——我们看到的只是水面上的部分,水面下还有庞大的知识体系等着我们去探索。
公众号:蓝海资料掘金营,微信deep3321