第二章:源码获取与编译
好,咱们直接进入正题。这一章要聊的,是每个Android开发者都绕不开的坎——源码获取与编译。说实话,我当年第一次搞AOSP编译的时候,光是环境就折腾了两天。嗯,现在回想起来,很多坑其实是可以避免的。今天我就把这些经验掰开揉碎了讲给你听。
2.1 AOSP源码下载:从哪来,怎么下?
AOSP(Android Open Source Project)的源码托管在Google的Gerrit上。你可能会问,直接去GitHub搜不行吗?其实Google官方维护的镜像站才是正主。我个人习惯用清华大学的镜像站,速度比官方快不少。
下载前,你得先确认一件事:你的硬盘够不够大?AOSP完整源码大概在30GB到50GB之间,加上编译产物,轻松突破100GB。我建议至少留出200GB的磁盘空间。
2.2 repo工具:Android的“超级Git”
AOSP不是单个Git仓库,而是由几百个Git仓库组成的“巨无霸”。Google专门搞了个repo工具来管理它们。说白了,repo就是Git的“包工头”,帮你一次性拉取、同步、切换所有仓库。
安装repo很简单:
mkdir ~/bin
PATH=~/bin:$PATH
curl https://storage.googleapis.com/git-repo-downloads/repo > ~/bin/repo
chmod a+x ~/bin/repo
然后初始化一个工作目录:
mkdir aosp
cd aosp
repo init -u https://aosp.tuna.tsinghua.edu.cn/platform/manifest -b android-13.0.0_r3
这里-b参数指定分支。分支怎么选?我后面会讲。
初始化完成后,执行:
repo sync -j8
-j8表示用8个线程并行下载。你的CPU核心数越多,这个数字可以设得越大。不过要注意,网络带宽才是瓶颈。我曾经在100M宽带上设-j32,结果路由器直接罢工了。
repo sync就行,它会断点续传。我第一次不知道,每次断了都删掉重来,白白浪费了一整天。
2.3 分支选择:别选错了版本
AOSP的分支命名规则其实挺清晰的。比如android-13.0.0_r3,13代表大版本,r3是第三个发布版本。你可能会问,选哪个好?
我的建议是:
- 学习用:选最新的稳定分支。比如现在Android 14已经出了,那就选
android-14.0.0_r1。 - 项目用:选你目标设备对应的分支。比如你要给Pixel 6做ROM,就得找Pixel 6对应的tag。
- 调试用:选你熟悉的版本。我习惯用Android 12,因为它的代码结构我闭着眼睛都能找到。
查看所有可用分支:
git ls-remote https://aosp.tuna.tsinghua.edu.cn/platform/manifest | grep android-
嗯,这里要注意,分支列表很长,建议用grep过滤一下。
2.4 编译环境搭建:Ubuntu下的准备工作
编译AOSP对系统有硬性要求。我推荐Ubuntu 20.04 LTS或22.04 LTS。别用太新的版本,有些依赖包可能还没适配。
首先,安装必要的依赖:
sudo apt update
sudo apt install git-core gnupg flex bison build-essential zip curl zlib1g-dev \
gcc-multilib g++-multilib libc6-dev-i386 libncurses5 lib32ncurses5-dev \
x11proto-core-dev libx11-dev lib32z1-dev libgl1-mesa-dev libxml2-utils \
xsltproc unzip fontconfig
然后安装OpenJDK。Android 13及以上需要JDK 11:
sudo apt install openjdk-11-jdk
验证安装:
java -version
输出应该是类似openjdk version "11.0.18"这样的。
/etc/security/limits.conf里加上:
* soft nofile 65536
* hard nofile 65536
然后重启或者重新登录。
2.5 make与m/mm/mmm:编译命令的“四兄弟”
编译AOSP,你一定会遇到这四个命令:make、m、mm、mmm。它们有什么区别?我刚开始也搞混过。
| 命令 | 作用 | 使用场景 |
|---|---|---|
make |
编译整个系统 | 第一次编译,或者需要全量编译时 |
m |
从源码根目录编译所有模块 | 修改了多个模块,想一次性编译 |
mm |
编译当前目录下的模块 | 只修改了一个模块,比如Settings |
mmm |
编译指定路径的模块 | 知道模块路径,但不在那个目录下 |
举个例子。假设你修改了packages/apps/Settings里的代码:
- 用
mm:先cd packages/apps/Settings,然后执行mm。 - 用
mmm:直接在根目录执行mmm packages/apps/Settings。
我个人更习惯用mmm,因为不用来回切换目录。
第一次全量编译,执行:
source build/envsetup.sh
lunch aosp_arm64-eng
make -j16
lunch命令选择编译目标。aosp_arm64-eng表示64位ARM架构的工程版。工程版有root权限,适合调试。
2.6 输出镜像烧录:把系统刷进设备
编译完成后,输出文件在out/target/product/<设备名>/目录下。最重要的几个文件:
system.img:系统分区镜像boot.img:内核和ramdiskvendor.img:厂商分区镜像userdata.img:用户数据分区
烧录前,先解锁设备的bootloader。不同设备解锁方式不同,但基本都要进fastboot模式:
adb reboot bootloader
然后执行:
fastboot flash system system.img
fastboot flash boot boot.img
fastboot flash vendor vendor.img
fastboot flash userdata userdata.img
fastboot reboot
如果你用的是模拟器,更简单:
emulator -kernel prebuilts/qemu-kernel/arm64/kernel-qemu -system out/target/product/generic/system.img -data out/target/product/generic/userdata.img
fastboot flash userdata会清空所有用户数据。我曾经手滑把同事的手机数据全清了,那场面...嗯,不提了。
2.7 避坑指南:我踩过的那些雷
最后,分享几个我亲身经历过的坑:
- 磁盘空间不足:编译到一半报错
No space left on device。解决办法:用df -h检查磁盘,清理out/目录或扩容。 - Python版本不对:AOSP需要Python 3.6以上。如果你系统默认Python是2.7,编译会报错。用
python --version检查,不行就装一个。 - 内存不够:编译时
ninja进程被OOM killer杀掉。建议至少16GB内存,32GB更稳。 - 网络超时:
repo sync时经常断。解决办法:用repo sync -j4降低并发,或者换个时间段再试。
嗯,这一章的内容就到这里。下一章我们会深入源码目录结构,看看AOSP这个庞然大物到底是怎么组织的。到时候你会发现,理解了目录结构,调试起来事半功倍。