第二章:源码获取与编译

好,咱们直接进入正题。这一章要聊的,是每个Android开发者都绕不开的坎——源码获取与编译。说实话,我当年第一次搞AOSP编译的时候,光是环境就折腾了两天。嗯,现在回想起来,很多坑其实是可以避免的。今天我就把这些经验掰开揉碎了讲给你听。

2.1 AOSP源码下载:从哪来,怎么下?

AOSP(Android Open Source Project)的源码托管在Google的Gerrit上。你可能会问,直接去GitHub搜不行吗?其实Google官方维护的镜像站才是正主。我个人习惯用清华大学的镜像站,速度比官方快不少。

下载前,你得先确认一件事:你的硬盘够不够大?AOSP完整源码大概在30GB到50GB之间,加上编译产物,轻松突破100GB。我建议至少留出200GB的磁盘空间。

注意:千万别用Windows直接搞AOSP。虽然WSL2勉强能用,但坑多到你怀疑人生。老老实实上Ubuntu,这是最省心的选择。

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"这样的。

重要:别忘了设置文件描述符限制。AOSP编译会打开大量文件,默认的1024根本不够用。在/etc/security/limits.conf里加上:
*               soft    nofile          65536
*               hard    nofile          65536
然后重启或者重新登录。

2.5 make与m/mm/mmm:编译命令的“四兄弟”

编译AOSP,你一定会遇到这四个命令:makemmmmmm。它们有什么区别?我刚开始也搞混过。

命令 作用 使用场景
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权限,适合调试。

经验之谈:第一次编译时间很长,我的i7-12700H + 32GB内存,全量编译大概需要40分钟。如果你用笔记本,记得插上电源,不然编译到一半没电就悲剧了。

2.6 输出镜像烧录:把系统刷进设备

编译完成后,输出文件在out/target/product/<设备名>/目录下。最重要的几个文件:

  • system.img:系统分区镜像
  • boot.img:内核和ramdisk
  • vendor.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这个庞然大物到底是怎么组织的。到时候你会发现,理解了目录结构,调试起来事半功倍。