01_OpenHarmony 6.1 源码学习与解读
项目开始,毫无疑问,一片茫然,不过好在这意味着前途坦荡。
相比于直接使用 AI 帮我基本完成这个工作,我想按部就班地“浪费”一些时间在锻炼我这颗木头人脑上。成为一名开源大师。
所以,这次的技术博客,我更愿意叫做学习笔记。
编译构建
到手这么巨大的代码,头发先掉了一半,我低头一看,掉落的头发赫然摆成五个大字母——“BUILD”。当我弄明白整个编译构建过程不就了解源代码的整体结构层次了吗?我去,不早说!
OpenHarmony 是一个基于 Gn 和 Ninja 的编译构建框架。等等,什么是 Gn?什么又是 ninja?那就学习一下!
Gn 和 Ninja
挺好奇 ninja 怎么读?查了查英文单词读作“嫩这”。
Ninja 是由 Google 开发的底层构建系统,目的是代替 make,这就显而易见了,这就是另一种“make”。既然用来代替 make,一定有一些优势在吧?确实,速度快,轻量级,以及更简单的语法。等等,语法?没错,Gn 此刻登上舞台,Generate ninja 的缩写,这是用于构建 .ninja 文件的一种元构建系统,简单的脚本语言(类似与 Python)。
在 OpenHarmony 源码中,根据产品配置,编译生成对应的镜像包。其中编译构建流程为:
- 使用 Gn 配置构建目标。
- Gn 运行后会生成
.ninja文件。 - 通过运行
.ninja来执行编译任务。
既然是脚本语言,那还看什么语法了,直接开始实战,找到根目录下的 Gn 文件:
import("//build/core/gn/ohos_exec_script_allowlist.gni")
# The location of the build configuration file.
buildconfig = "//build/config/BUILDCONFIG.gn"
# The source root location.
root = "//build/core/gn"
# The executable used to execute scripts in action and exec_script.
script_executable = "/usr/bin/env"
exec_script_whitelist = ohos_exec_script_config.exec_script_allowlist
# Enable OpenHarmony components for gn.
ohos_components_support = true
对新手很友好啊,如此精炼的代码,仅仅指定了 buildconfig (暂且按字面意思理解做构建配置)和 root (这个则更简单,如此庞大的代码总要有个“总管”吧),后两行注释贴心告诉我们是脚本的执行规则,暂且作为黑盒。我们接下来“连根拔起”。
BUILDCONFIG.gn
来到 build/ 目录下,我们找到了 BUILDCONFIG.gn 文件,虽然有1196行代码,但是里面注释很清楚,“WHAT IS THIS FILE?”,她说这是一个“master GN build configuration”,并解释说这个文件在 bulid/ 目录的构建参数和顶层的 .gn 文件之后进行加载,文件运行的上下文会在整个构建过程中的其他文件生效,也就是内部的变量其实是全局的!
PLATFORM SELECTION
现在来看首段代码:
if (host_os == "mac") {
check_mac_system_and_cpu_script =
rebase_path("//build/scripts/check_mac_system_and_cpu.py")
check_darwin_system_result =
exec_script(check_mac_system_and_cpu_script, [ "system" ], "string")
if (check_darwin_system_result != "") {
check_mac_host_cpu_result =
exec_script(check_mac_system_and_cpu_script, [ "cpu" ], "string")
if (check_mac_host_cpu_result != "") {
host_cpu = "arm64"
}
}
} else if (host_os == "linux") {
check_linux_cpu_script = rebase_path("//build/scripts/check_linux_cpu.py")
check_linux_cpu_result =
exec_script(check_linux_cpu_script, [ "cpu" ], "string")
if (check_linux_cpu_result != "") {
host_cpu = "arm64"
}
}
虽然我并不会 GN 语言,但是英文还是略懂一二,这段就是简单的读取了我们编译所用的主机 host 的系统信息和 CPU 架构信息,其中的实现是靠 bulid/scripts/ 下的脚本来实现。接下来使用 declare_args() 定义了一系列的全局变量,看名字大多是表达一些入口参数,如预加载输出目录 preloader_output_dir 等,方便后续复用和理解。
在后续代码中,
product_build_config =
read_file("${preloader_output_dir}/build_config.json", "json")
global_parts_info =
read_file("${preloader_output_dir}/parts_config.json", "json")
这两句起了至关重要的作用,它从 preloader 中取出一些关键信息,其中就包括许多 target 的产品配置信息,将它们注入全局变量中。为了尽快整体把握整个源代码,这部分细节不再追究。
等等,有一个细节至关重要:
if (target_cpu == "") { if (target_os == "ohos" || target_os == "android" || target_os == "ios") { target_cpu = "arm" } else { target_cpu = host_cpu } }可以看到它只是照顾到了“arm”,后续是否应该“else if”一个“riscv”?
BUILD FLAGS
继续贯穿上述“偷懒”的原则,后一大段代码配置了一些列的 flag,用于列出一些构建过程中的输入参数,每个都有它的默认值,如果与命令行中指定的值发生冲突,就会重写这个值。
if (custom_toolchain != "") {
set_default_toolchain(custom_toolchain)
} else if (_default_toolchain != "") {
set_default_toolchain(_default_toolchain)
}
注释中告诫我不能在这个文件中添加 flag,如果需要,可以在对应组建的 build.gn 中添加。嗯~规范。
同样的,也有值得注意的部分:
if ((target_os == "ohos" && target_cpu == "x86_64") || device_company == "emulator") { is_emulator = true } # different host platform tools directory. if (host_os == "linux") { if (host_cpu == "arm64") { host_platform_dir = "linux-aarch64" } else { host_platform_dir = "linux-x86_64" } } else if (host_os == "mac") { if (host_cpu == "arm64") { host_platform_dir = "darwin-arm64" } else { host_platform_dir = "darwin-x86_64" } } else { assert(false, "Unsupported host_os: $host_os") }同样的,后续也有可能需要完善,在此做个记录,以防日后需要用的时候找不到地方。
TOOLCHAIN SETUP
接下来,配置了默认的工具链,注释中提醒说我们要在不支持的操作系统和 CPU 上进行编译时要尽早配置工具链。虽然我可能用不到这一点,但是我希望我未来一定能用上。
这里的代码很简单,通过 host 和 target 的操作系统和 CPU 的架构不同组合来在 bulid/toolchain 中选择不同的工具链。同样的,这里的修改是否也在我们当前的任务列表中呢?
OS DEFINITIONS
这部分最简单,一对布尔值,为了方便,记录下 OS 的情况。太简单了,再写一行水字数。
SOURCES FILTERS
这是个文件过滤器,不知道这样称呼人家合适不合适,但是值得一提的是,使用的规则并非常见的 Regular Expressions,只支持 * 和 \b。
TARGET DEFAULTS
这里对每个特定类型的 target 设置了一些默认配置,这些值会自动配置在对应的 target 上,好消息是,注释中告诉了可以按照需求对 target 进行添加和删除。
读到这里我才感觉有点思路,前面的部分似乎都是为了最后这一操作做准备,除了主编译器和连接器,在这里还可以为了某一 target 定制一些配置,override 掉默认的配置,也可以直接修改一些默认的配置。
不知如此,这里还针对标准系统和轻量级的系统做了不同的处理,分别使用不同的逻辑进行组装。
令我感动的是,这里出现了我项目的关键词,第一次。
if (current_cpu == "arm64") { arch = "aarch64" } else if (current_cpu == "riscv32") { arch = "riscv32" } else if (current_cpu == "loongarch64") { arch = "loongarch64" }虽然仅仅有这一处,说明我前面的思考可能是对的。
总结一下 BUILDCONFIG.gn 的主要工作,先是 declare_args,全局构建参数,比如 product_name、device_name、use_sandbox;然后从 preloader 产物里读配置,最关键的是 build_config.json 和 parts_config.json;最后是把 target_os、target_cpu、product_toolchain 这些真正决定编译行为的变量灌进全局环境。这里不是“定义要编什么”,而是定义后面所有 BUILD.gn 解释代码时所处的世界。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐


所有评论(0)