Android 构建系统与增量编译(SMDT 双 SDK 联编)
1. 概述
1.1 功能简介
本文按 SMDT RK3588 3588A V3.1 板的实际产线流程编写,是 ch01-android-sdk-build.md(已按双 SDK 联编流程改写)的补充篇:
- 双 SDK 联编的构建系统分层(Soong / Kbuild / rkbuild 编排)
- 日常迭代中的增量出包标准路径
- 单模块快速验证、定向更新 vendor_boot 的高级路径
- clean 与编译环境重置(A16 / A14 分开处理)
- 增量链路上的四个断层点(上游产物更新后,下游不会自动跟着更新的位置)
1.2 应用场景
- 改了 framework / 系统应用,想知道最快的出包路径
- 改了 dts / 驱动 / kernel 模块,想定向更新某个镜像
- 增量产物「看起来没变」「打完包还是旧的」等问题排查
- 构建环境清理与重置
1.3 工作原理
A16(system 侧,Soong/Ninja) A14(vendor 侧)
lunch ssi_16_arm64-bp2a-userdebug lunch rk3588_u-userdebug
rkbuild_build ssi(make dist) ←→ rkbuild_build uboot / kernel vendor
↓ ↓
rkbuild_merge(target-files 合并)
↓
rkbuild_mkupdate(afptool/rkImageMaker)
↓
<A16>/rockdev/Image-rk3588_u/update.img增量机制分三层(Soong 是 Android 官方构建系统,用 Android.bp 描述模块,由 Ninja 按依赖图调度编译命令):
- Soong/Ninja(两侧 Android 树):按依赖图只重编变更模块,未改动模块直接复用
out/产物; - Kbuild(A14 kernel-6.1、u-boot):make 自身增量;
- rkbuild 编排:阶段间靠 target-files zip / merged-fastboot.zip 传递镜像。这些 zip 不会自动感知上游变化,这就是增量断层的根源(见 5.3)。
2. 硬件准备
| 组件 | 型号/规格 | 数量 | 备注 |
|---|---|---|---|
| 编译主机 | x86_64 Linux(Ubuntu 22.04,内核 6.8 实测) | 1 | ≥64 线程;RAM ≥256GB;NVMe 空闲 ≥500GB |
| 目标板 | SMDT RK3588 3588A V3.1 | 1 | 用于增量产物烧录验证 |
3. 软件准备
- 双 SDK 源码树已就位:A16
/home/diskj/xyp/ROCKCHIP_ANDROID16.0_SSI_SDK、A14/home/diskj/xyp/RK_Android14_SDK_Archive(见 ch01 §3.2) - 依赖包同 ch01 §3.1
- 磁盘:两棵树 out/ 各 100–150GB、/tmp ≥50GB;clean 前先确认磁盘余量
4. 操作步骤
4.1 两套环境与初始化
标准流程由 build_ssi16.sh 自动完成,不要手工 source/lunch 之后再跑脚本,否则环境会重复初始化。手工调试时,两侧必须在各自目录、各自的 shell 里初始化:
# shell 1:system 侧(A16)
cd /home/diskj/xyp/ROCKCHIP_ANDROID16.0_SSI_SDK
source build/envsetup.sh && lunch ssi_16_arm64-bp2a-userdebug
# shell 2:vendor 侧(A14)
cd /home/diskj/xyp/RK_Android14_SDK_Archive
source build/envsetup.sh && lunch rk3588_u-userdebug4.2 增量出包标准路径
| 改动内容 | 修改位置 | 编译命令 | 增量耗时 |
|---|---|---|---|
| 板级 dts / SMDT 驱动 | A16 board 资产(deploy 自动推 A14) | bash build_ssi16.sh all vendor | ~11 分钟 |
| Android framework / 系统应用 | A16 AOSP 树 | bash build_ssi16.sh all ssi | ~13 分钟 |
| kernel 深层补丁(A14 树直改) | A14 kernel-6.1/ 源文件 | bash build_ssi16.sh all vendor | ~11 分钟 |
| vendor HAL / device 配置 | A14 device/rockchip/、vendor/ | bash build_ssi16.sh all vendor | ~11 分钟 |
| u-boot | A14 u-boot/ | bash build_ssi16.sh all uboot | ~13 分钟 |
续跑机制:all <stage> 从指定阶段一直跑到 update;产物新鲜度以 .build_logs/.session_start 会话基准判定,发现陈旧镜像会告警。全量约 50 分钟,增量完整流程约 15.5 分钟。
4.3 单模块快速编译验证(不出包)
用来快速确认编译通过、bytecode 正确,产物不会自动进镜像:
cd /home/diskj/xyp/ROCKCHIP_ANDROID16.0_SSI_SDK
source build/envsetup.sh && lunch ssi_16_arm64-bp2a-userdebug
m -j256 SettingsProvider # Soong target 名(不是 SettingsProvider.apk)常见问题:framework 核心模块(services.core / framework.jar)的改动不会触发增量重编。m systemimage 只重打 system.img,不触发 services.core 的 javac(依赖图上没有直连)。改了 PMS.java 之类的文件后,必须先删 intermediates 再编:
rm -rf out/soong/.intermediates/frameworks/base/services/core/services.core \
out/target/product/ssi_16_arm64/obj/JAVA_LIBRARIES/services.core_intermediates \
out/target/common/obj/JAVA_LIBRARIES/services.core_intermediates
m -j256 services.core # framework.jar 同理怎么确认真的重编了:用 javap -p 查 bytecode 里的标记串;APK 资源类改动用 aapt2 dump resources 对比。出包仍需走 4.2 表对应的阶段。
4.4 定向更新 vendor_boot(kernel 模块级,高级路径)
模块改动要进 vendor_boot.img,需要手动刷新 target-files 暂存区(构建 system/vendor 两侧镜像的中间打包目录)。暂存区只在全量构建时自动刷新,这是构建系统的盲区,不是有意设计的特性:
# 1. 把新 .ko 拷入暂存区(build号见 config.json)
cp <新.ko> out/target/product/rk3588_u/obj/PACKAGING/target_files_intermediates/\
rk3588_u-target_files-202609/VENDOR_BOOT/RAMDISK/lib/modules/
# 2. 重编 kernel(在 A16 环境,rkbuild 跨树调度)
rkbuild_build kernel # 产出 rockdev/Image-rk3588_u/vendor_boot.img 新版
# 3. 刷新 merged zip 成员(否则 mkupdate 会用 zip 旧版覆盖磁盘新版!)
cd rockdev/Image-rk3588_u && zip ssi_16_arm64-images-merged-fastboot.zip vendor_boot.img
# (zip 报 "Local Entry CRC does not match CD" 属无害警告)
# 4. 打包
rkbuild_mkupdate验证链(每一步都要确认通过):kernel 树 .ko(strings 查标记串)→ 暂存区 .ko 的 md5 与之一致 → lz4 -d out/.../vendor_boot_intermediates/vendor_ramdisk.cpio.lz4 /tmp/vr.cpio && strings /tmp/vr.cpio | grep <标记> → rockdev/out 的 md5 一致 → 与 update.img 内比对。注意 vendor_ramdisk 是 lz4 压缩的,对 img 直接 strings 搜不到模块内容(dtb 里的 bootargs 才是明文)。
常规迭代不推荐走这条路径,直接
all vendor(~11 分钟)更稳妥;只有在想把单模块验证循环压到最短时才用。
4.5 Clean 与编译环境重置
两棵树独立清理,按需组合:
| 操作 | 命令(在对应树 lunch 环境下) | 作用 |
|---|---|---|
| 常规清理(推荐) | make installclean | 清已安装/暂存产物、保留 intermediates;修复过期 ninja 依赖图首选 |
| 深度清理 | make clean | 清 out 下已编译产物,保留源码 |
| 完全重置 | rm -rf out/ | 最彻底,下次等于首次全量(A16 ~数十分钟级) |
| 只重打镜像 | 重跑 build_ssi16.sh update | 不重编译,仅重新 mkupdate |
注意事项:
- clean 只动
out/,不动源码。但如果重建整棵 A14 树(重新解包),直接改在 A14 kernel 源文件上的深层补丁(pm_domains.c panic 降级、GPU SCMI 冻结补丁等,deploy_board.sh 不覆盖这些文件)会丢失,需要重打,清单见handoff_2026-09-21.md。 - 报
ninja: ... vendor_ramdisk/lib/modules/xxx.ko no known rule时,先在 A14make installclean后重跑 vendor 阶段。这个问题已确认多数是过期的 ninja 依赖图造成的偶发现象,通常不需要深清。 - 本产线未启用 ccache:Soong 体系下
USE_CCACHE对 Android.bp 模块基本无效(Kati→Soong 迁移后不再支持)。增量提速靠 out/ 的 Ninja 图加阶段续跑,kernel/u-boot 走 Kbuild 自身增量。旧文档里的 ccache 章节已不再适用。
4.6 构建日志与成败判定
只认日志标记,不认退出码(rkbuild heredoc 会吞掉失败继续往下跑,打印假的 "Build vendor ok!"):
| 阶段 | 成功标记 |
|---|---|
| ssi | Build ssi ok! |
| uboot | Build u-boot ok! |
| vendor | Build kernel ok! + Build android ok! + Build vendor ok! 三者齐全且无 failed |
| merge | Merge finished successfully |
| update | Making update.img OK. |
bash build_ssi16.sh analyze .build_logs/vendor_<STAMP>.log # 离线判定任意日志(含历史大日志)4.7 产物内容级校验
镜像文件的修改时间正常不代表内容正确。关键改动按「源 → 中间产物 → 镜像内」三层 md5 校验:
# 从 update.img 抽 super 分区(offset/size 取自 mkupdate 日志条目)
dd if=rockdev/Image-rk3588_u/update.img bs=1M skip=$((0xOFFSET/1048576)) \
count=$((0xSIZE/1048576+1)) of=/tmp/super.img status=none
simg2img /tmp/super.img /tmp/super_raw.img
out/host/linux-x86/bin/lpunpack /tmp/super_raw.img /tmp/super_parts/
# 抽出目标文件比对
debugfs -R "dump /system/priv-app/SettingsProvider/SettingsProvider.apk /tmp/sp.apk" /tmp/super_parts/system_a.img
md5sum /tmp/sp.apk out/target/product/ssi_16_arm64/system/priv-app/SettingsProvider/SettingsProvider.apk5. 代码详解
5.1 构建系统分层
| 层 | 体系 | 覆盖范围 | 增量机制 |
|---|---|---|---|
| A16 system 侧 | Soong/Ninja | frameworks/、system/、packages/ 等(Android.bp) | Ninja 依赖图 |
| A14 vendor 侧 | Soong/Ninja | device/rockchip/rk3588、vendor/(Android.bp/mk) | Ninja 依赖图 |
| A14 kernel/u-boot | Kbuild/make | kernel-6.1(GKI config)、u-boot | make 增量 |
| rkbuild 编排 | bash(vendorsetup.sh) | 两侧 lunch/构建/合并/打包调度 | 无增量感知,靠 zip 传递 |
rkbuild_* 是 device/rockchip/rockchip_generic/vendorsetup.sh 里的 bash 函数(非二进制):rkbuild setup / build / merge / mkupdate。build_ssi16.sh 对它们做了分阶段包装和标记判定。
5.2 关键目录
| 路径(相对各自树根) | 说明 |
|---|---|
out/soong/.intermediates/(A16) | Soong 中间产物;framework 深清入口 |
out/target/product/ssi_16_arm64/(A16) | system 侧镜像中间产物 + staging |
out/target/product/rk3588_u/(A14) | vendor 侧镜像中间产物 |
out/target/product/rk3588_u/obj/PACKAGING/target_files_intermediates/rk3588_u-target_files-202609/(A14) | target-files 暂存区,vendor_boot 模块断层点 |
kernel-6.1/(A14,in-tree 构建) | kernel 源码 + 构建产物 + SMDT dts/驱动部署目标 |
rockdev/Image-rk3588_u/(A16) | 最终镜像 + update.img + 两个 merged zip |
.build_logs/(A16) | 各阶段日志 + 会话基准 .session_start |
5.3 增量链路与四个断层点
一次改动的完整传播链:
模块源码 → out 中间产物 → system.img/vendor_boot.img → target-files zip
→ merged zip → rockdev 镜像 → update.img每个箭头都可能断(上游更新了,下游不知道):
| 断层点 | 症状 | 正确做法 |
|---|---|---|
| 模块 → system.img | m <module> 后镜像里还是旧的 | framework 改动走 ssi 阶段(4.2) |
| services.core 等核心模块 → javac | m systemimage 不触发重编 | 删 intermediates 再编(4.3) |
| 模块 → target-files 暂存区 | 定向重编后 vendor_boot 里模块还是旧的 | 手动刷暂存区(4.4) |
| rockdev 磁盘镜像 → merged zip → update.img | mkupdate 用 zip 旧版覆盖磁盘新版 | zip 刷新成员(4.4 步骤 3) |
build_ssi16.sh 的产物新鲜度检查(用会话基准比对 update.img/resource.img/uboot.img/vendor_boot.img/super.img 的 mtime)只能兜底告警,替代不了内容级校验(4.7)。
6. 常见问题
6.1 改了 framework / 系统应用,打出来的包还是旧的
原因: 链路上多个断层点叠加:m <module> 不重打 system.img,m systemimage 不触发 services.core 重编,也不重打 target_files。
解决: 按 4.2 表走 bash build_ssi16.sh all ssi(make dist 重打 target_files 和 system.img,merged zip 自动更新);核心模块先按 4.3 删 intermediates。
6.2 ninja: ... vendor_ramdisk/lib/modules/xxx.ko no known rule
原因: A14 kernel 模块清单与 .config 不同步,或 ninja 依赖图过期(历史案例 pwm-fan.ko,后来复查确认是偶发现象)。
解决: A14 侧 make installclean 后跑 bash build_ssi16.sh all vendor;仍复现则查 kernel-6.1/.config 里对应的 CONFIG_* 是否被改。
6.3 明明失败了却显示 "Build vendor ok!"
原因: rkbuild heredoc 吞掉 ./build.sh 的失败继续往下跑,最后停在误导性的 Missing file: target_files.zip,真实 ninja 错误埋在几百 KB 日志中部。
解决: 一律用 build_ssi16.sh(标记判定 + 错误摘录)或 analyze 子命令;不要只看 tail 几十行就下结论。
6.4 增量耗时异常 / 反复重编
原因: 改了公共配置(Android.bp 顶层、BoardConfig、dtsi 头文件)触发大范围重编;或首次跑触发 Soong/Kati 分析(kati regen 约 4 分钟属正常)。
解决: 确认改动范围最小化;make installclean 重置依赖图;耗时基线见 4.2(增量完整流程约 15.5 分钟)。
6.5 OOM / 磁盘耗尽
解决: JOBS=128(或更低)重跑;预检会在剩余 <20G 拒跑、<120G 告警;/tmp 需 ≥50GB。
6.6 交互 shell 跑构建脚本报 ugrep / Missing config 错
解决: 把流程写成独立 .sh 用 bash xxx.sh 跑;不把 rkbuild 输出接管道(重定向到日志文件);自己写校验脚本时避免 大输出 | grep -q(SIGPIPE 会造成误报),用 grep -c 把输出读完。
7. 相关文档
| 文档 | 说明 |
|---|---|
| ch01-android-sdk-build.md | 双 SDK 联编主流程(七阶段、产物、烧录、发布) |
| README_SMDT_MODIFICATIONS.md(A16 顶层) | SMDT 定制总览 |
| handoff_2026-09-21.md(A16 顶层) | 移交笔记(kernel 深层补丁清单、验证脚本) |
| RK3588_ANDROID16_BUILD_NOTES.md(旧16 树) | 历史实战笔记(断层点问题的演进实录) |
| 05hardware-reference/00硬件事实基线.md | 硬件事实清单 |
