第3章 Makefile/SOONG构建系统错误:Kati/Ninja执行失败、错误日志关键行定位

核心故障现象:AOSP构建过程中,Kati或Ninja进程异常中止,终端输出类似ninja: build stopped: subcommand failed、kati: *** missing separator或ninja: error: 'path/to/target' missing and no known rule to make it。构建立即退出,不生成最终镜像。

3.1 现象识别

Kati与Ninja是AOSP构建系统的两层执行引擎:Kati将Makefile转换为Ninja文件,Ninja负责任务调度与并行执行。任何一层失败都会导致构建中断。典型错误表现:

  • Kati阶段:输出*** missing separator、*** missing target、*** failed to read inputs。
  • Ninja阶段:输出FAILED: …,随后跟具体的编译/链接命令和退出码,最终ninja: build stopped: subcommand failed。
  • 混合模式:先报SOONG配置解析错误,再导致Ninja未生成正确构建图。

确认错误阶段的方法:查看构建日志的前几行——若在[ 1/ 12] …之前就报错,通常是Kati失败;若已经出现大量[ 1/ 500]后才中断,则是Ninja执行中的子任务失败。

3.2 数据采集

定位关键行需要以下核心数据源:

数据源 路径/命令 关键内容
构建标准错误输出 终端直接重定向 Kati/Ninja错误消息,含行号、文件名
out/build.log tail -200 out/build.log 完整构建记录,含失败命令的完整参数
out/build_error.log 自动生成(部分AOSP版本) 仅错误行,便于快速扫描
详细输出模式 make showcommands 2>&1 | tee build.log 显示每个子进程的完整命令行
采集技巧:若错误信息过长被截断,使用ninja -v -j1 target 2>&1逐步运行,可避免并行日志混乱。

3.3 原因定位

使用故障树分析法(Fault Tree Analysis)从失败关键行逐步回溯:

Kati/Ninja执行失败 故障树 Kati/Ninja 执行失败 Kati 解析错误 Ninja 子任务失败 SOONG 配置异常 Makefile语法错误 变量未定义/空格歧义 依赖缺失(头文件/库找不到) 编译器/链接器返回非零退出码 输出文件冲突/权限不足 SOONG_*.mk 缺失模块定义 product/variant 配置冲突 关键判断:从错误日志首条红线开始,按上述树形逐层匹配模式

根据故障树,定位时使用以下判断规则表:

关键行模式 所属分支 下一步动作
*** missing separator / *** missing target Kati解析错误 检查对应Makefile的冒号、制表符
ninja: error: '…' missing and no known rule Ninja子任务失败 确认该文件是否在.mk中注册
FAILED: … clang … exit code 1 Ninja子任务失败 分析编译器错误输出(如未定义引用)
soong_build: error: … undefined variable SOONG配置异常 检查Android.bp或boardconfig.mk

3.4 修复动作

根据定位到的根因执行对应修复:

  • Makefile语法错误:使用kati -C --ninja解析特定Makefile,查看具体行。修复空格/制表符混合、漏掉的变量定义。
  • 依赖缺失:安装对应库(如sudo apt install libncurses5-dev)或通过PRODUCT_PACKAGES添加缺失模块。
  • 编译器错误:根据失败命令的完整stderr,修复源码错误或更新工具链(如使用export CC=clang-14)。
  • SOONG配置冲突:检查userspace_*、vendor_*路径是否重复,或TARGET_PRODUCT与target_board_common.mk不匹配。
  • 资源不足:增加swap、清理out/目录、降低并行度(make -j4)。

3.5 复测

修复后执行:

make clean && source build/envsetup.sh && lunch aosp_cf_x86_64_phone-userdebug && make -j8 2>&1 | tee rebuild.log

观察新日志中是否不再出现原错误关键行。若仍有其他错误,重复数据采集 → 原因定位循环。对于Ninja部分失败,可只重新构建受影响模块:

mma -j8 2>&1 | tail -50
注意:不要盲目执行make clean,除非确认缓存导致问题;优先只清理单个模块的中间文件(rm -rf out/target/product/<device>/obj/<module>)。

3.6 预防

建立以下实践减少此类错误发生:

  • Makefile规范性检查:在CI中集成kati --lint,禁止制表符与空格混用。
  • 构建环境标准化:使用Docker/Cuttlefish固定编译器版本、Ninja版本(≥1.10)。
  • 增量构建隔离:为每个功能分支创建独立out/目录,避免并行构建冲突。
  • 日志保留策略:始终保留out/build.log,并在失败时自动上传至分析系统。
  • 依赖声明完整:在Android.bp中使用shared_libs、static_libs正确列出所有外部模块。
经验口诀:
Kati报错查语法,Ninja挂掉找依赖;
SOONG配置看目标,日志关键行是路牌。