第四章 编译框架入门:理解hb工具链、认识BUILD.gn文件、执行首次全量编译、分析编译输出产物

好,到了这一步,我们终于要开始跟编译系统打交道了。说实话,很多初学者一听到「编译框架」四个字就头大,觉得这东西又深又玄。其实没那么可怕。你想想看,编译说白了就是把我们写的C代码、配置文件,变成能在开发板上跑起来的二进制文件。鸿蒙用的这套工具链叫hb,搭配gn + ninja这套组合拳。我个人觉得,理解它比理解Linux内核的Kbuild要轻松不少。

4.1 hb工具链:鸿蒙的编译管家

hb是鸿蒙专门开发的编译命令行工具。它的全称是HarmonyOS Builder。你不需要记住这个全称,只要知道它是我们操作编译流程的入口就行。

我刚开始接触鸿蒙移植时,第一件事就是确认hb能不能用。怎么确认?很简单:

hb --help

如果系统返回了帮助信息,说明环境没问题。如果提示command not found,那就要回到上一章,检查Python环境和ohos_build的安装情况。

hb的核心命令其实就几个:

命令 作用
hb set 选择产品/开发板配置
hb build 执行编译
hb clean 清理编译产物
hb env 查看当前编译环境变量

嗯,这里要注意:hb set这一步很多人会忽略。我曾经见过一个同事,直接hb build,结果编译出来的是默认的Hi3516DV300的镜像,跟他的板子完全不搭。所以每次编译前,记得先hb set确认一下目标平台。

小技巧: 如果你不确定当前选了什么产品,可以用 hb env 查看。它会输出当前的产品名、内核版本、工具链路径等信息。

4.2 认识BUILD.gn文件:编译的「配方」

BUILD.gn文件是鸿蒙编译系统的核心。它用GN(Generate Ninja)语法写成。说白了,它就是告诉编译器:你要编译哪些源文件、链接哪些库、生成什么目标。

我习惯把BUILD.gn比作「菜谱」。菜谱告诉你需要什么食材、怎么切、怎么炒。BUILD.gn告诉你需要哪些.c文件、依赖哪些.h头文件、最终生成一个.bin还是.so

一个典型的BUILD.gn文件长这样:

import("//build/lite/config/component/lite_component.gni")

ohos_executable("hello_world") {
  sources = [
    "src/main.c",
    "src/utils.c",
  ]
  include_dirs = [
    "include",
    "//kernel/liteos_a/include",
  ]
  deps = [
    "//base/iot_hardware:iot_hardware",
  ]
}

我来拆解一下:

  • ohos_executable:这是鸿蒙定义的一个模板,表示我们要生成一个可执行文件。
  • sources:源文件列表。注意路径是相对于当前BUILD.gn文件所在目录的。
  • include_dirs:头文件搜索路径。如果漏了某个头文件路径,编译时会报fatal error: xxx.h: No such file or directory
  • deps:依赖的其他模块。比如你用了鸿蒙的Wi-Fi接口,就要把对应的组件加进来。
避坑指南: 我曾经在移植一个传感器驱动时,死活编译不过。查了半天,发现是include_dirs里少写了一个//。GN的路径写法中,//表示代码根目录,//开头是绝对路径,没有//的是相对路径。这个细节很容易翻车。

4.3 执行首次全量编译

好,理论说完了,我们来动真格的。第一次编译,我建议你执行全量编译。全量编译会从零开始,把所有代码都编译一遍。虽然慢,但能确保整个环境是通的。

步骤很简单:

  1. 进入代码根目录:cd ~/openharmony
  2. 选择产品:hb set,然后选择你的开发板(比如Hi3861或Hi3516)
  3. 执行编译:hb build -f

这里-f参数表示全量编译(full build)。如果不加,默认是增量编译,只编译修改过的文件。

第一次编译的时间会比较长。我记得我第一次编译Hi3516的镜像,用了将近40分钟。当时我盯着屏幕,看着一行行日志往上滚,心里既期待又忐忑。如果一切顺利,最后你会看到类似这样的输出:

[1/1234] CXX obj/base/startup/init/init.o
[2/1234] CC obj/kernel/liteos_a/kernel.o
...
[1234/1234] STAMP obj/build/lite/ohos.build.stamp

===== build successful =====

看到build successful,恭喜你,编译环境完全没问题。

重要: 如果编译过程中报错,不要慌。先看错误日志的最后几行,那里通常会给出具体原因。常见的错误有:缺少依赖库、头文件路径不对、Python版本不匹配。大部分问题都可以通过搜索引擎解决。

4.4 分析编译输出产物

编译成功后,产物在哪里?默认情况下,鸿蒙的编译输出放在out/目录下。具体路径取决于你选择的产品。比如:

  • Hi3861:out/hispark_pegasus/wifiiot_hispark_pegasus/
  • Hi3516DV300:out/hispark_taurus/ipcamera_hispark_taurus/

在这个目录里,你会看到一堆文件。我挑几个最重要的说说:

文件/目录 说明
OHOS_Image.bin 鸿蒙内核镜像,烧录到板子的核心文件
rootfs.img 根文件系统镜像,包含系统服务和用户程序
userfs.img 用户数据分区镜像
*.bin / *.elf 各个组件的可执行文件或符号文件
build.log 完整的编译日志,排查问题时很有用

你可能会问:我怎么知道哪个文件是干什么的?嗯,有个小技巧:用file命令查看文件类型。比如:

file out/hispark_pegasus/wifiiot_hispark_pegasus/OHOS_Image.bin

它会告诉你这个文件是二进制数据、ELF文件还是其他格式。这在我们后续做烧录和调试时非常有用。

个人经验: 我每次编译完,都会先看一眼build.log的大小。如果日志文件特别小,说明编译可能没跑完就结束了。正常的全量编译日志应该有几千行。另外,out/目录下的*.ninja文件也值得关注,它们记录了编译的依赖关系图,有时候能帮你理解为什么某个文件被重新编译了。

好了,这一章的内容就到这里。你掌握了hb的基本用法,看懂了BUILD.gn的骨架,也完成了第一次全量编译。接下来,我们就可以开始真正的移植工作了。下一章,我们会聊聊如何为新的开发板创建产品目录和配置文件——这一步,是移植的起点。