1. 为什么“编译成功”仍可能烧出错误镜像

OpenHarmony 设备开发中,经常出现一种看似矛盾的现象:源码已经修改,完整构建也输出 build success,烧录后板端却仍表现为旧问题。

原因通常不在编译器,而在交付链路:

源码
  -> GN/Ninja 中间产物
  -> kernel / rootfs / FIT
  -> 产品 OUT
  -> Windows 烧录目录
  -> 开发板运行版本

其中任何一层没有刷新,都可能把旧文件带入最终板卡。本文总结一套适用于 OpenHarmony 小型系统的镜像一致性检查方法。

2. 正确 DTS 不等于正确 FIT

项目中同时存在基础 DTB 和产品 DTB 时,最典型的问题是:产品 DTS 已经修好,但 kernel.mk 仍把基础 DTB 打进 uImage

只检查源码或构建目录中的 .dtb 不够,必须从最终 FIT 反向提取:

dumpimage -T flat_dt -p 0 -o embedded.dtb uImage
dtc -I dtb -O dts -o embedded.dts embedded.dtb

然后执行两类检查。

第一类是字节一致性:

cmp -- embedded.dtb expected-product.dtb

第二类是语义检查。例如某个 NAND 产品要求 eMMC、物理 SDIO0 和冲突显示外设关闭,物理 SDIO1 开启并带 non-removable,就应逐节点断言,而不是只判断文件名叫不叫 flash.dtb

还要注意 FIT 中镜像索引。某些产品的 Image 0 是设备树,Image 1 才是内核;索引用错会把正确镜像误判为失败。

3. NAND 分区、U-Boot 环境和烧录表必须一致

调整 kernel 分区时,不能只修改 nand_burn_table.xml。至少要同时核对:

  • 烧录表中的起始地址和长度;
  • U-Boot mtdparts
  • bootcmd 的 NAND 读取地址与长度;
  • rootfs 分区起点;
  • 擦除块对齐;
  • 文件对齐后的实际写入长度。

某次实践中,FIT 超过 4 MiB 后,烧录工具虽然显示写入成功,但板端回读发现 4 MiB 之后的尾部损坏,U-Boot 随后报 FIT 格式错误。最终通过 XZ 压缩把内核控制在边界以内,同时恢复运行时真正使用的 4 MiB kernel 布局,才让 kernel 与 rootfs 地址重新一致。

这说明“分区足够大”并不等于“烧录实现可以可靠完成单次大块写入”。对大镜像最好同时做整文件和分块校验。

4. rootfs 可能被时间戳和增量图卡住

另一个常见问题是源文件内容已更新,OUT 或 rootfs 仍保留旧版本。

如果 overlay 使用 cp -a,源文件的旧时间戳会被保留。旧 OUT 反而比正确源文件更新时,Ninja 可能判断复制动作无需执行。此时重新跑增量构建也不会刷新文件。

更稳妥的方式是:

cp -a --no-preserve=timestamps overlay/. "$PROJECT_ROOT/"

或者只清理已经确认失效的具体 stamp/object,再重建目标。不要为了刷新一个配置就删除整个 OUT,更不要用泛化的 git clean 处理包含用户改动的工作区。

对于关键运行文件,建议做三方比较:

cmp source/init.cfg "$OUT/etc/init.cfg"
cmp source/init.cfg "$OUT/rootfs/etc/init.cfg"

5. 不要用历史 error.log 否定当前成功构建

部分 OpenHarmony 构建目录在后续成功后仍会保留上一次失败的 error.log。因此,判断当前轮次是否成功,应以以下证据组合为准:

  • 本次命令退出码;
  • 独立时间戳日志中的 build success
  • 本轮目标是否实际重新编译/链接;
  • 产物时间、大小和哈希;
  • rootfs/UBIFS 是否重新生成。

旧错误日志应保留用于追踪,但不能把“文件非空”直接解释为当前构建失败。

6. OUT 与烧录目录必须作为一个交付事务

开发者经常在 Linux/WSL 中生成新 OUT,却让烧录工具继续使用 Windows 上一次选择的旧目录。最安全的做法是:每次 OUT 变化,都在同一轮同步完整五件套并逐项比较。

示意检查:

set -euo pipefail

for f in nand_burn_table.xml boot_image.bin nand_env.bin uImage rootfs_hi3516cv610.ubifs; do
    cmp -- "$OUT/$f" "$BURN_DIR/$f"
    sha256sum "$OUT/$f" "$BURN_DIR/$f"
done

同时解析烧录表,确认其中引用的文件都在同一目录存在,分区长度大于文件大小。只同步 uImage 或 rootfs,不能证明 boot/env/XML 与之匹配。

7. 烧录后先做版本指纹,再做功能测试

板端出现问题时,第一件事不是改代码,而是确认运行的确实是本轮镜像。

建议记录以下指纹:

uname -a
关键服务程序大小与校验和
关键共享库校验和
三个 KO 的大小、vermagic 和校验和
最终配置文件内容
设备树关键节点

如果板端缺少 sha256sum,可以使用主机和板端都支持的 md5sumcksum 加文件长度完成比对。版本不一致时,任何功能结果都只能算旧镜像证据,不能用于评价本轮修改。

8. 把验证分成五个独立状态

建议明确区分:

  1. 源码和配置已修改;
  2. 产品完整构建成功;
  3. 最终镜像静态检查通过;
  4. 烧录成功且板端版本指纹匹配;
  5. 冷启动、热重启和业务功能实测通过。

上一个状态不能自动推导下一个状态。比如 FIT 可以正常解析,不代表驱动已加载;板端文件哈希一致,也不代表 Wi-Fi 功能已经通过;扫描成功,也不能外推 DHCP 和断开流程成功。

9. 发布前检查清单

最后给出一份简化门禁:

  • 本次完整构建退出码为 0;
  • 最终 FIT 内嵌 DTB 与产品目标一致;
  • 关键设备树节点语义正确;
  • kernel、rootfs、XML、U-Boot 环境布局一致;
  • rootfs 中服务、库、KO、固件和配置来自本轮;
  • OUT 与烧录目录五件套逐字节一致;
  • 烧录后板端版本指纹匹配;
  • 冷启动和热重启分别验证;
  • 业务测试只声明实际执行过的范围。

10. 总结

镜像交付的本质不是“把几个文件复制过去”,而是建立从源码到板端运行状态的可追踪关系。最终 FIT、rootfs、分区布局、烧录目录和板端指纹缺一不可。

当每一层都有可重复的检查命令后,很多“同样的代码为什么这次不工作”的问题,都会变成可以迅速定位的版本或产物一致性问题。

Logo

社区规范:仅讨论OpenHarmony相关问题。

更多推荐