编译提速与最小重建—【万物智能之开源鸿蒙OpenHarmony系统实战开发系列教程】
三条路会选了。今天解决最烦的那件事:改一行代码不必等四十分钟。按改动选最小重建。


现象:你改了超声波驱动里一个超时时间,跑 ./build.sh --product-name rk3568_evb --ccache,31 到 39 分钟过去了,刷进去测,发现改的不是那个文件。
原因:全量构建的时间几乎全花在用户态重链和大镜像上。system.img 一点六 GB,thinLTO 的大 .so 一个能编七分钟。内核 boot_linux 自己大约八分钟。你改的那个 hcsr04.c 跟这些无关。
| 动作 | 大约耗时 |
|---|---|
| 全量含内核 | ≈ 39 min |
| 全量不含内核 | ≈ 31 min |
| 内核 boot_linux 整段 | ≈ 8 min |
| 增量 make Image(改 1 个 .c) | 1–2 min |
| 只编 dtb | 几十秒 |
| make M= 单个 .ko | 10–30 s |
档选错,轻则浪费时间,重则刷上去的还是旧 dtb。

现象:改 .c 一次要半小时
原因
build.sh 默认把用户态、镜像打包全走一遍。GN 并不知道你只关心这个 ko。
代码:档 1
ROOT=/path/to/OpenHarmony
export PATH=$ROOT/prebuilts/clang/ohos/linux-x86_64/llvm/bin:$PATH
export KBUILD_OUTPUT=$ROOT/out/kernel/OBJ/linux-5.10
cd $ROOT/out/kernel/src_tmp/linux-5.10
make LLVM=1 LLVM_IAS=1 CROSS_COMPILE=aarch64-linux-gnu- ARCH=arm64 \
M=drivers/misc modules -j$(nproc)
$KBUILD_OUTPUT/scripts/sign-file sha1 \
$KBUILD_OUTPUT/certs/signing_key.pem \
$KBUILD_OUTPUT/certs/signing_key.x509 \
drivers/misc/evb_misc.ko
hdc file send drivers/misc/evb_misc.ko /data/evb_misc.ko
hdc shell "rmmod evb_misc 2>/dev/null; insmod /data/evb_misc.ko; dmesg | tail -20"
增量 make 不要 rm -rf OBJ,否则 signing_key.pem 被重新生成,旧 ko 全废。.ko 是 aarch64;用户态测试程序必须 armv7 + musl。
现象:改了 DTS,刷完 /proc/device-tree 没变
原因
U-Boot 真正喂给内核的 dtb,经常是 resource 分区(p4)里的 rk-kernel.dtb,不是 boot_linux 里那个 toybrick.dtb。只刷 p5,p4 仍是旧屏。
GN 的 is_kernel_change() 看的 DEPS 不含 device/board/。只改板级 dts,ninja 可能根本不编 dtb。
代码:档 2
ROOT=/path/to/OpenHarmony
export PATH=$ROOT/prebuilts/clang/ohos/linux-x86_64/llvm/bin:$PATH
export KBUILD_OUTPUT=$ROOT/out/kernel/OBJ/linux-5.10
SRC=$ROOT/out/kernel/src_tmp/linux-5.10
cd $SRC
cp $ROOT/device/board/rk/rk3568_evb/kernel/rk3568-evb-linux.dts \
arch/arm64/boot/dts/rockchip/rk3568-evb-linux.dts
make LLVM=1 LLVM_IAS=1 CROSS_COMPILE=aarch64-linux-gnu- ARCH=arm64 \
O=$KBUILD_OUTPUT rockchip/rk3568-evb-linux.dtb -j$(nproc)
目标必须带 rockchip/ 前缀。编完用 dtc 反编译,不要只看 make exit 码:
DTC=$KBUILD_OUTPUT/scripts/dtc/dtc
$DTC -I dtb -O dts -o /tmp/now.dts \
$KBUILD_OUTPUT/arch/arm64/boot/dts/rockchip/rk3568-evb-linux.dtb
grep -n 'hdmi\|lvds-panel' /tmp/now.dts | head
cp $KBUILD_OUTPUT/arch/arm64/boot/dts/rockchip/rk3568-evb-linux.dtb \
$SRC/boot_linux/extlinux/toybrick.dtb
genext2fs -B 4096 -b 16384 -d $SRC/boot_linux -i 8192 -U $SRC/boot_linux.img
cp $SRC/boot_linux.img $ROOT/out/rk3568_evb/packages/phone/images/boot_linux.img
resource 单独打:
resource_tool --pack --image=resource.img rk-kernel.dtb logo.bmp logo_kernel.bmp
cp resource.img $ROOT/out/rk3568_evb/packages/phone/images/resource.img
改内核 C 而不是 dts:
make LLVM=1 LLVM_IAS=1 CROSS_COMPILE=aarch64-linux-gnu- ARCH=arm64 \
O=$KBUILD_OUTPUT Image -j$(nproc)
刷之前:
hdc shell "cat /proc/partitions"
hdc file send boot_linux.img /data/boot_linux.img
hdc file send resource.img /data/resource.img
hdc shell "dd if=/data/boot_linux.img of=/dev/block/mmcblk1p5 bs=4M conv=fsync"
hdc shell "dd if=/data/resource.img of=/dev/block/mmcblk1p4 bs=4M conv=fsync"
hdc shell "dd if=/data/resource.img of=/dev/block/mmcblk0p4 bs=4M conv=fsync"
hdc shell reboot
写成 /dev/mmcblk1p5(少了 block),dd 会在 /dev 建普通文件,返回成功,重启还是旧内核。by-name 永远指向 eMMC。
现象:改了 .so,板上 strings 仍是旧的
原因
--build-target 只编这个目标,不会 install 进 packages/phone/vendor。直接 ninja 还可能撞 stale multiple rules。
代码:档 3
cd $ROOT
./build.sh --product-name rk3568_evb --build-target camera_pipeline_core --ccache
cp out/rk3568_evb/device_rk3568_evb/device_rk3568_evb/libcamera_pipeline_core.z.so \
out/rk3568_evb/packages/phone/vendor/lib/
打 vendor.img 以 toolchain.ninja 里那条为准。打完 debugfs 核对:
debugfs -R "dump /lib/libcamera_pipeline_core.z.so /tmp/x.so" \
packages/phone/images/vendor.img
strings /tmp/x.so | grep '你埋的那句日志'
hdc file send vendor.img /data/vendor.img
hdc shell "dd if=/data/vendor.img of=/dev/block/mmcblk1p8 bs=4M conv=fsync && reboot"
uhdf HCS 也落在 vendor,改之前删 hcb:
rm -f out/rk3568_evb/gen/vendor/rk/rk3568_evb/hdf_config/uhdf/hdf_default.hcb
rm -f out/rk3568_evb/packages/phone/vendor/etc/hdfconfig/hdf_default.hcb
rm -f out/rk3568_evb/packages/phone/images/vendor.img
khdf 的 HCS 编进内核,刷 boot_linux,别和 vendor 搞混。
现象:改了 build_kernel.sh,.config 还是旧值
原因
is_kernel_change() 的 DEPS 不含 device/board/。只改 board 下的脚本、dts、HCS,不会 rm -rf OBJ 重新 defconfig。增量 make 用旧 .config。
代码:三种删除效果不同
# 强制「内核变了」——改 board 脚本 / dts / §13 CONFIG
rm -rf out/kernel/checkpoint
./build.sh --product-name rk3568_evb --ccache --build-target kernel --fast-rebuild --disable-package-image
# 内核树脏了
# rm -rf out/kernel
# 核弹,31–39 min,交付或 part 列表错了才用
# rm -rf out
| 你改了什么 | 删什么 | 重建什么 | 上板 |
|---|---|---|---|
| 某个 .c,当 ko 调 | 什么都别删 | make M= + sign-file | hdc send + insmod |
| 板级 dts / 时序 | checkpoint,或直 make dtb | dtb + boot_linux + **resource** | p5 + p4 |
| 内核 C 定稿内置 | checkpoint | Image + boot_linux | p5 |
| khdf HCS | hcb + checkpoint | boot_linux | p5 |
| uhdf HCS / vendor 库 | uhdf hcb | vendor.img | p8 |
| 说不清、要出交付包 | 视污染范围 | 全量 | 整张 sd_image |
grep CONFIG_EVB_MISC out/kernel/OBJ/linux-5.10/.config
strings out/kernel/OBJ/linux-5.10/vmlinux | grep evb_misc | head
编完核产物,不要核心情:
grep CONFIG_EVB_MISC out/kernel/OBJ/linux-5.10/.config
strings out/kernel/OBJ/linux-5.10/vmlinux | grep evb_misc | head
stat out/rk3568_evb/packages/phone/images/boot_linux.img
stat out/rk3568_evb/packages/phone/images/resource.img
.config 是 =y 而 vmlinux 里没有符号,合成了但没链进。删 checkpoint 再编。
现象:--build-target phone_boot_image 报 unknown target
你从别处抄到 phone_boot_image,以为能只打 boot。
原因
这个产品的 GN 目标不叫这个名字。hb tool --ls 列出来的才算数。内核那一段是 action("kernel"),挂在板级 device/board/rk/rk3568_evb/kernel/BUILD.gn 上,跑的是 build_kernel.sh。随便猜一个 phone_*_image,ninja 回你 unknown,然后你以为只能全量。
代码
cd $ROOT
hb tool --ls | grep -iE 'kernel|boot|vendor|system'
只想让内核 action 跑一遍、不打大镜像:
./build.sh --product-name rk3568_evb --ccache \
--build-target kernel --fast-rebuild --disable-package-image
--fast-rebuild 跳过 gn gen,省两三分钟。--disable-package-image 不生成 system.img / vendor.img。改的是 board 下的 dts 或 build_kernel.sh 时,这一条仍然可能判定「内核没变」——DEPS 不含 board,所以还是要先删 checkpoint。
直接 ninja 而不走 build.sh,偶尔撞 multiple rules generate xxx。不要加 -w dupbuild=warn 把警告吞掉当成功,走 build.sh 重新 gn gen。
现象:主机 dtb 新,boot_linux.img 里仍是旧的
你 make rockchip/rk3568-evb-linux.dtb 成功了,dtc 反编译看得到新节点。刷 p5 之后 /proc/device-tree 还是旧的。
原因
dtb 编出来在 OBJ 里。boot_linux.img 是一份 64 MiB 的 ext2,里面的文件叫 extlinux/toybrick.dtb。你没覆盖它、没 genext2fs,镜像还是上一份。显示相关还要打 resource 里的 rk-kernel.dtb,那才是 U-Boot 常喂给内核的。
整段 make-ohos.sh 会 make rockchip_linux_defconfig 再编 Image,纯 dts 改动用不上,几分钟到八分钟。外科就是:拷 dts → make dtb → 覆盖 toybrick.dtb → genext2fs。
管道后面写 echo exit=$? 看到的是 echo 的 0,不是 make 的。无关的 rk628 一类 dts 报错也可能混在日志里。看产物时间戳,用 debugfs 抠出来对。
代码
debugfs -R "dump /extlinux/toybrick.dtb /tmp/in-img.dtb" \
$ROOT/out/rk3568_evb/packages/phone/images/boot_linux.img
cmp $KBUILD_OUTPUT/arch/arm64/boot/dts/rockchip/rk3568-evb-linux.dtb /tmp/in-img.dtb
$DTC -I dtb -O dts -o /tmp/in-img.dts /tmp/in-img.dtb
grep -n 'status' /tmp/in-img.dts | head
cmp 对不上,说明 genext2fs 打的是另一个目录,或者你 cp 进 boot_linux/extlinux/ 之前 dtb 还是旧的。
dtb 目标必须带 rockchip/ 前缀。写成 make rk3568-evb-linux.dtb,make 去 dts 根目录找,报没有规则。包装脚本里的 ${dtb}.img 能解析子目录,你手动单编必须带前缀。
权威源永远是 device/board/。src_tmp 是副本。下次 build_kernel.sh 真跑起来,会从 board 再拷一次,把你在副本上的改动盖掉。
diff -u \
device/board/rk/rk3568_evb/kernel/rk3568-evb-linux.dts \
out/kernel/src_tmp/linux-5.10/arch/arm64/boot/dts/rockchip/rk3568-evb-linux.dts
有 diff 就先拷再编。不要在副本上改。
现象:用户态小工具在板上 No such file or directory
你用交叉 gcc 编了一个读 /dev/evb_misc 的 CLI,file 说它是 ELF,hdc 送上去,一跑这个错。
原因
内核 64 位,用户态 32 位。这不是「两个 ABI 都能跑」,是两套世界。ldd 在主机上看 aarch64 的解释器,板上没有那条路径。动态链接器必须是:
/system/lib/ld-musl-arm.so.1
档 3 的 .so 同理。你若用 aarch64 工具链编 camera pipeline,能链过,板上 dlopen 失败,进程退 255。走产品的 GN 目标,不要自己 clang 瞎编一份塞进去。
代码
# 错
aarch64-linux-gnu-gcc -o evb_misc_cli evb_misc_cli.c
# 对:走 SDK native 的 armv7 musl
clang --target=arm-linux-ohos -march=armv7-a \
--sysroot=<ohos-sdk-native> \
-o evb_misc_cli evb_misc_cli.c
file evb_misc_cli
# ELF 32-bit LSB executable, ARM, EABI5
hdc file send evb_misc_cli /data/evb_misc_cli
hdc shell "chmod 755 /data/evb_misc_cli; /data/evb_misc_cli"
档 1 的 .ko 不要用这条 clang。模块必须 aarch64,跟 vmlinux 同套 LLVM。vermagic 必须和正在跑的内核一致:
modinfo drivers/misc/evb_misc.ko | grep vermagic
hdc shell "cat /proc/version"
llvm-strip --strip-debug drivers/misc/evb_misc.ko
两边都是 5.10.184 这一类字符串,才能 insmod。差一个补丁号,内核直接拒。
vendor 镜像里的 so 打完也要 file:
debugfs -R "dump /lib/libcamera_pipeline_core.z.so /tmp/x.so" \
packages/phone/images/vendor.img
file /tmp/x.so
# 期望 ELF 32-bit LSB shared object, ARM
file 若报 64-bit ARM aarch64,这份 so 上不了板。
现象:改了 input_config.hcs,触摸分辨率纹丝不动
原因
khdf 和 uhdf 两套配置,进两张镜像。顶层 hdf.hcs 几乎全是 #include,hc-gen 的依赖常常只盯这一份。你改了 input/input_config.hcs,顶层没动,hcb 不重生,Image 里仍是旧 hex。
khdf 刷 boot_linux。uhdf 刷 vendor。改错边、刷错分区,日志看起来一切正常。
代码
find out/kernel -name '*.hcb' -print
rm -f out/kernel/src_tmp/linux-5.10/drivers/hdf/khdf/*.hcb
rm -rf out/kernel/checkpoint
uhdf:
rm -f out/rk3568_evb/gen/vendor/rk/rk3568_evb/hdf_config/uhdf/hdf_default.hcb
rm -f out/rk3568_evb/packages/phone/vendor/etc/hdfconfig/hdf_default.hcb
rm -f out/rk3568_evb/packages/phone/images/vendor.img
改 khdf 等于改内核。改 uhdf 等于改 vendor。HCS 那篇把管道画过,这里只强调:子文件不在依赖里,必须先删 hcb。
hdc 刷分区前的固定动作
hdc shell "cat /proc/partitions"
hdc shell "mount | grep ' on / '"
hdc shell "cat /proc/version"
hdc shell "ls -l /dev/block/mmcblk0p4 /dev/block/mmcblk1p4 /dev/block/mmcblk0p5 /dev/block/mmcblk1p5"
SD 启动:boot_linux → mmcblk1p5,resource → mmcblk1p4,system → mmcblk1p7,vendor → mmcblk1p8。eMMC 把 1 换成 0。U-Boot 经常去 eMMC 找 p4/p5,所以你「从 SD 起」时,改显示仍可能要写 mmcblk0p4。
hdc shell "dd if=/data/boot_linux.img of=/dev/block/mmcblk1p5 bs=4M conv=fsync"
hdc shell "sync; reboot"
纯 dts 改动 /proc/version 不变(没换 Image),要读节点:
hdc shell "cat /proc/device-tree/hdmi@fe0a0000/status; echo"
hdc shell "find /proc/device-tree -name status | grep -i hdmi"
hdc shell "strings /vendor/lib/xxx.so | grep 标记"
不要用镜像文件的 mtime 当证据。
一张图记住三档
改的是内核 .c,还在调逻辑
→ 档 1 make M= + sign-file + insmod 10–30 s
改的是 dts / 内置驱动 / khdf
→ 档 2 make dtb 或 Image + genext2fs
显示还要重打 resource,刷 p4+p5 1–8 min
改的是 vendor 的 so / uhdf
→ 档 3 --build-target + cp + 打 vendor.img
debugfs dump 核对后再刷 p8 数分钟
交付、回归、说不清
→ 全量 build.sh 31–39 min
档选错的典型:改 dts 跑全量(付钱给 userspace);改 so 只刷 boot_linux(分区根本不含那份 so);改 ko 去删 out/(钥匙换了,循环从秒级变回 39 分钟)。
gn gen 自己就要 2–4 分钟,扫六万多个 target。--fast-rebuild 跳过它。驱动迭代连 gn 都不必每次跑。
现象对照
| 现象 | 原因 | 做法 |
|---|---|---|
| 改一行等 39 min | 全量在重链 userspace | 按改动选档 1/2/3 |
| insmod Key rejected | 删过 OBJ,钥匙换了 | 板级固定 certs;增量 make 别 rm OBJ |
| 改 dts 全量成功但板上旧 | ninja 没重跑 / 没刷 resource | 删 checkpoint 或直 make dtb;刷 p4 |
| `make rk3568-evb-linux.dtb` 无规则 | 目标不带 `rockchip/` 前缀 | `make ... rockchip/rk3568-evb-linux.dtb` |
| 主机 dtb 新,img 里旧 | 没覆盖 `toybrick.dtb` 或没 genext2fs | debugfs dump 再 cmp |
| `dd` 成功 md5 对,重启旧 | 写到 `/dev/mmcblkXpY` 普通文件 | `/dev/block/`,先 cat partitions |
| by-name 刷了没效果 | by-name 指向 eMMC | 用当前启动盘的 mmcblkNpY |
| `--build-target` 编了 so 板上旧 | 没 install、没重打 vendor | cp 进 packages/phone/vendor/lib,debugfs 验 |
| so 是 aarch64 | 没用产品 GN,自己交叉错了 | 32 位 ARM EABI5,file 核对 |
| CLI `No such file or directory` | 用户态用了 aarch64 链接器 | armv7 + musl |
| 改 HCS 没反应 | 子文件不在 make 依赖;khdf/uhdf 刷错分区 | 删对应 hcb |
| 改第 13 段 CONFIG,.config 旧 | is_kernel_change 不看 board | 删 checkpoint |
| ninja multiple rules | 直接 ninja 脏图 | 走 build.sh 重新 gn gen |
| `phone_boot_image` unknown | 目标名不是这个 | `hb tool --ls`;kernel + 三个开关 |
| make 管道 `exit=0`,dtb 没变 | `echo exit=$?` 看到的是 echo | 看产物时间戳 + dtc |
| 整段 make-ohos.sh 为换 dtb | 它会重做 defconfig 和 Image | 外科 genext2fs |
| 删了整个 out | 你想省事 | 驱动迭代不要删 out,31 min 买回来 |
档 1 解决「我在调逻辑」。档 2 解决「我在改硬件描述」。档 3 解决「我在改用户态库」。全量留给交付和回归。显示那几篇会反复用到档 2 加刷 p4,因为生效 dtb 不在你以为的那个分区。
系列第 15 篇 · 芯片:瑞芯微 RK3568 · OpenHarmony 4.1(API 11) · Linux 5.10
更多推荐

所有评论(0)