4. 编译框架解析:hb工具、build.sh与产品配置
编译框架这东西,说白了就是一套“搭积木”的规则。你写好了代码,怎么把它变成能烧录的镜像?OpenHarmony 的编译系统,我刚开始接触时也觉得挺绕的。但摸清楚之后,你会发现它其实很清晰。
今天我们就把它拆开来看。核心就三块:hb 工具(你敲的命令)、build.sh(背后的脚本)、产品配置与 BoardConfig(告诉系统你要给谁编译)。
4.1 hb 工具:你与编译系统的对话窗口
hb 是 OpenHarmony 的编译入口命令。你想想看,一个项目有成百上千个模块,总不能每次都手动调用编译器吧?hb 就是那个帮你打理一切的管家。
它的工作流程其实很简单:
- 解析你给的参数(比如
hb build -f) - 读取产品配置(你要编译哪个开发板?)
- 调用 GN + Ninja 完成真正的编译
核心命令速查表
| 命令 | 作用 | 我常用的场景 |
|---|---|---|
hb set |
选择产品与编译目标 | 第一次搭建环境时必用 |
hb build |
执行编译 | 日常开发,加 -f 全量编译 |
hb build -T <target> |
编译指定模块 | 只改了一个驱动,不想等全量编译 |
hb clean |
清理编译产物 | 遇到奇怪错误时,先 clean 再 build |
小技巧:我个人习惯在项目根目录下直接敲 hb set,它会弹出一个交互式菜单让你选产品。但如果你在 CI 环境里,可以用 hb set --product rk3568 直接指定,省去交互步骤。
4.2 build.sh 脚本分析:别被它吓到
很多人一打开 build.sh 就头大,几百行 shell 脚本。其实它就是个“包装器”。
我拆解一下它的核心逻辑:
- 环境检查:看看 Python、GN、Ninja 装没装,版本对不对。
- 参数转发:你传进来的
--product-name、--target-cpu等参数,它原封不动传给hb。 - 调用 hb:最后一行核心代码,其实就是
python3 build/hb/main.py加上你传的参数。
我曾经遇到一个问题:在 Docker 里编译,build.sh 报错说找不到 Python。查了半天,发现是脚本里写死了 python,但 Docker 镜像里只有 python3。改一下软链接就好了。嗯,这种环境问题最坑人。
避坑指南:不要直接修改 build.sh 本身!如果你需要定制编译流程,建议在 ohos_config.json 或产品配置里做文章。我曾经为了加一个编译参数直接改了 build.sh,结果下次 repo sync 时被覆盖了,排查了半天。
4.3 产品配置与 BoardConfig:告诉系统你是谁
这是编译系统里最核心的部分。你想想看,编译系统怎么知道你要给 rk3568 编译,还是给 hi3516 编译?答案就在产品配置里。
产品配置通常放在 vendor/<厂商>/<产品名>/ 目录下。核心文件有两个:
| 文件 | 作用 | 关键内容 |
|---|---|---|
config.json |
产品级配置 | 产品名称、版本、子系统列表、组件列表 |
BoardConfig.mk |
板级配置 | CPU 架构、内存布局、Bootloader 参数、串口配置 |
我举个例子,这是 rk3568 的 config.json 片段:
{
"product_name": "rk3568",
"version": "3.0",
"subsystems": [
{
"subsystem": "kernel",
"components": [
{ "component": "linux-5.10", "features": [] }
]
},
{
"subsystem": "graphic",
"components": [
{ "component": "graphic_standard", "features": [] }
]
}
]
}
而 BoardConfig.mk 里,你会看到类似这样的内容:
# 芯片架构
TARGET_ARCH := arm64
TARGET_CPU := cortex-a55
# 内存配置
BOARD_KERNEL_BASE := 0x00200000
BOARD_KERNEL_OFFSET := 0x00080000
# 串口调试
BOARD_UART_PORT := 2
BOARD_UART_BAUDRATE := 1500000
关键理解:config.json 决定“我要编译哪些功能模块”,BoardConfig.mk 决定“这些模块在硬件上怎么跑”。两者缺一不可。
4.4 编译目标选择:别每次都全量编译
很多新手一上来就 hb build -f,然后去喝杯咖啡等半小时。其实大部分时候你不需要全量编译。
编译目标的选择,我总结为三个层次:
- 全量编译:
hb build -f,第一次编译或大版本升级时用。 - 增量编译:直接
hb build,只编译修改过的文件。日常开发首选。 - 单模块编译:
hb build -T <模块名>,只编译你关心的那个模块。比如你只改了图形驱动,就只编 graphic 子系统。
怎么知道模块名叫什么?我教你一个办法:
# 查看所有可编译的模块
hb build -T list
它会列出一大串名字,找到你需要的那个就行。
我的习惯:调试驱动时,我会先 hb build -T kernel 确认内核能编过,再 hb build -T my_driver 单独编驱动模块。这样每次编译不超过 2 分钟,效率高很多。
4.5 知识结构图:编译框架全景
下面这张图,是我梳理的编译框架核心流程。你看一眼,就能明白各个组件之间的关系。
4.6 常见问题与避坑
最后,分享几个我踩过的坑:
- hb set 后找不到产品:检查
vendor/目录下有没有对应的产品文件夹。我曾经手误删了vendor/hihope,结果hb set列表里空空如也。 - BoardConfig.mk 修改不生效:记得执行
hb clean后再编译。GN 会缓存配置,不清理的话它还用旧的。 - 单模块编译失败:先确认模块名写对了。用
hb build -T list查一下,别自己猜名字。
重要提醒:如果你修改了 config.json 里的组件列表,一定要全量编译一次。增量编译不会重新生成依赖关系,可能导致链接错误。我因为这个浪费过一整天。
好了,编译框架的核心内容就这些。记住:hb 是入口,配置是灵魂,目标选择是效率。把这三点吃透,编译这块你就基本拿下了。