持续集成:CI 环境搭建、自动化编译、编译结果验证
各位同学,今天我们来聊聊持续集成。说实话,我刚入行那会儿,Android 编译还是件挺「手工」的事——改完代码,自己敲 make,等半小时,然后祈祷别报错。后来项目大了,团队人多了,这种搞法根本撑不住。于是,CI 就成了必需品。
CI 是什么?说白了就是:你提交代码,机器自动帮你编译、测试、出报告。你不用守着终端,也不用担心漏掉什么。我个人的经验是,CI 搭得好,能省下至少 30% 的调试时间。
CI 环境搭建:选工具、配机器
先说说环境。Android 编译对机器要求不低。我建议至少 16 核 CPU、64GB 内存、500GB 以上 SSD。磁盘空间尤其重要——一次完整编译可能吃掉 200GB。
工具方面,主流选择有 Jenkins、GitLab CI、GitHub Actions。我个人偏爱 GitLab CI,因为它和代码仓库集成得紧,配置也简单。不过 Jenkins 更灵活,适合定制需求多的团队。
搭建步骤大致如下:
- 准备编译服务器:装好 Ubuntu 20.04 或 22.04,安装 JDK、Python、Git、Repo 等基础工具。
- 安装 CI 工具:以 GitLab CI 为例,注册 Runner,配置 executor 为 shell 或 docker。
- 配置编译环境:下载 Android 源码,设置环境变量,确保
lunch和make能跑通。 - 编写 CI 配置文件:在项目根目录创建
.gitlab-ci.yml。
ninja。嗯,从那以后我养成了习惯——先在服务器上手动跑一遍完整编译,再写 CI 脚本。
自动化编译:写脚本、定策略
自动化编译的核心是 CI 脚本。拿 GitLab CI 举例,一个典型的 .gitlab-ci.yml 长这样:
stages:
- build
- test
- verify
build_aosp:
stage: build
script:
- source build/envsetup.sh
- lunch aosp_arm64-eng
- make -j$(nproc) 2>&1 | tee build.log
artifacts:
paths:
- out/
expire_in: 1 week
这里有几个关键点:
- 并行编译:
-j$(nproc)用满所有 CPU 核心。我见过有人写死-j8,结果换了台 32 核机器反而慢了——浪费啊。 - 日志输出:用
tee把日志存下来,方便排查问题。 - 产物保留:编译出来的镜像、符号表等,设置过期时间,别把磁盘撑爆。
你可能会问:每次提交都全量编译?那太慢了。没错,所以 CI 里要加增量编译策略。比如只编译改动过的模块:
build_module:
stage: build
script:
- source build/envsetup.sh
- lunch aosp_arm64-eng
- mmm packages/apps/Settings/ # 只编译 Settings 模块
- make -j$(nproc) snod # 重新打包 system.img
这样做的好处是快——全量编译可能要 1 小时,增量编译可能只要 5 分钟。但要注意,模块间的依赖关系要理清楚。我曾经踩过坑:只编译了 Settings,没编译它的依赖库,结果打包出来的镜像跑不起来。
编译结果验证:不只是「没报错」
编译通过了,是不是就万事大吉?当然不是。我见过太多「编译成功,但刷机就黑屏」的案例。所以,验证环节必不可少。
验证分几个层次:
| 验证类型 | 检查内容 | 常用工具 |
|---|---|---|
| 编译日志检查 | 是否有 warning、error、undefined reference | grep、awk |
| 产物完整性检查 | 镜像文件是否存在、大小是否合理 | ls -lh、file 命令 |
| 签名验证 | APK 是否已签名、签名是否匹配 | apksigner、jarsigner |
| 基本功能测试 | 刷机后能否开机、ADB 能否连接 | fastboot、adb |
举个例子,我写过一个简单的验证脚本:
#!/bin/bash
# 验证编译产物
OUT_DIR=out/target/product/generic_arm64
# 检查 boot.img 是否存在且大于 10MB
if [ ! -f "$OUT_DIR/boot.img" ] || [ $(stat -c%s "$OUT_DIR/boot.img") -lt 10485760 ]; then
echo "ERROR: boot.img 缺失或太小"
exit 1
fi
# 检查 system.img 是否存在
if [ ! -f "$OUT_DIR/system.img" ]; then
echo "ERROR: system.img 缺失"
exit 1
fi
# 检查 APK 签名
for apk in $OUT_DIR/system/app/*/*.apk; do
if ! apksigner verify "$apk" &>/dev/null; then
echo "WARNING: $apk 未签名"
fi
done
echo "验证通过"
SVG 流程图:CI 流水线概览
下面这张图展示了典型的 Android CI 流水线。你可以看到,从代码提交到最终验证,每一步都有明确的输入输出。
核心要点:
- CI 环境要稳定,最好用专用编译服务器,别跟开发机混用。
- 自动化编译要支持增量,别每次都全量编译——时间就是金钱。
- 验证环节不能省,至少检查产物是否存在、大小是否合理、签名是否有效。
好了,这一章就到这里。CI 这东西,搭起来不难,但想搭得好、用得顺,还是得在实践中慢慢调。你想想看,如果每次提交都能自动编译、自动验证,是不是省心多了?