介绍
在 OpenHarmony 项目中,BUILD.gn 文件是 GN(Generate Ninja)构建系统 的核心配置文件,用于描述模块的构建规则,包括源文件、依赖库、编译选项、输出目标类型(如可执行文件、静态库、动态库)等。
在OpenHarmony(尤其是轻量/小型系统设备开发,如Hi3861、Hi3516等开发板)中,BUILD.gn文件是构建系统(基于GN+Ninja)的核心配置文件,用于定义“模块如何被编译、链接、依赖哪些资源”,最终指导构建系统生成可执行程序、库文件或固件镜像。
简单来说,BUILD.gn的作用是告诉构建系统:“这个目录下有哪些源码文件、需要编译成什么产物、依赖哪些其他模块、用什么编译选项”,是连接开发者代码与最终产物的“桥梁”。
应用场景
场景 1:添加应用程序
- 在
applications/sample/wifi-iot/app创建模块; - 编写
BUILD.gn; - 该目录用于存放 Hi3861 的上层应用程序,每个子目录对应一个独立功能模块
场景 2:集成第三方 C/C++ 库
- 将第三方库放入
//third_party/xxx/; - 为其编写
BUILD.gn; - 其他模块通过
deps = ["//third_party/xxx:libxxx"]引用。
核心功能
定义构建目标(Target)
明确当前模块要生成的产物类型,OpenHarmony中常见目标类型:
executable("目标名"):生成可执行程序(如Hi3861的LED控制程序、UART调试工具)。static_library("目标名"):生成静态库(.a文件,如通用算法库,编译时被直接嵌入可执行程序)。shared_library("目标名"):生成动态库(.so文件,如驱动框架库,运行时动态加载,轻量系统中较少用)。- OpenHarmony扩展目标(如
lite_component):将模块打包为系统组件,最终集成到固件中。
示例(Hi3861的LED驱动示例):
# 定义一个可执行程序目标,名为"led_demo"
executable("led_demo") {
sources = [ "led.c", "main.c" ] # 源码文件
deps = [ "//drivers/peripheral/gpio:gpio" ] # 依赖GPIO驱动模块
}
声明源码与资源文件
通过sources字段指定当前模块的源码文件(C/C++/汇编等),构建系统会自动编译这些文件。支持通配符(如"*.c")和子目录(如"src/*.c")。
示例:
static_library("utils") {
# 包含当前目录的所有.c文件和src目录的helper.c
sources = [
"*.c",
"src/helper.c"
]
}
管理依赖关系(deps)
通过deps字段声明当前模块依赖的其他模块(如驱动库、系统API库),构建系统会先编译依赖的模块,再编译当前模块,确保依赖正确。
依赖路径规则:
- 用
//表示OpenHarmony源码根目录(如//kernel/liteos_m表示内核模块)。 - 用相对路径表示同一项目内的其他模块(如
../common:log表示上一级目录的log模块)。
示例(依赖日志库和GPIO驱动):
executable("sensor_demo") {
sources = [ "sensor.c" ]
deps = [
"//utils/native/lite:log", # 系统日志库
"//drivers/peripheral/gpio:gpio" # GPIO驱动模块
]
}
配置编译选项
定义编译、链接阶段的参数(如宏定义、优化级别、头文件路径),确保代码按预期编译。常用字段:
cflags/cxxflags:C/C++编译选项(如-O2优化、-Wall开启警告)。defines:预编译宏定义(如-DDEBUG,等价于代码中的#define DEBUG)。include_dirs:头文件搜索路径(告诉编译器去哪里找#include的文件)。ldflags:链接选项(如指定链接脚本、库路径)。
示例(调试模式配置):
executable("uart_test") {
sources = [ "uart.c" ]
cflags = [ "-O0", "-g" ] # 关闭优化,保留调试信息
defines = [ "UART_DEBUG" ] # 启用调试宏
include_dirs = [ "include", "//drivers/uart/include" ] # 头文件路径
}
适配多设备/平台
通过条件判断(if-else)为不同设备(如Hi3861、Hi3516)或平台(如Linux、LiteOS)生成差异化构建规则。
示例(区分Hi3861和Hi3516的编译逻辑):
executable("device_demo") {
sources = [ "common.c" ]
if (board_name == "hi3861") {
sources += [ "hi3861_specific.c" ] # 仅Hi3861添加该文件
cflags += [ "-DHI3861" ]
} else if (board_name == "hi3516") {
sources += [ "hi3516_specific.c" ] # 仅Hi3516添加该文件
}
}
集成到OpenHarmony系统组件
轻量/小型系统中,需通过lite_component将模块添加到系统组件列表,最终被打包进固件镜像(OHOS_Image)。
示例:
# 引入OpenHarmony的组件模板
import("//build/lite/config/component/lite_component.gni")
# 定义一个系统组件,名为"app_demos",包含前面的led_demo目标
lite_component("app_demos") {
features = [ ":led_demo" ] # 关联到前面定义的可执行程序
}
BUILD.gn 构建流程中的作用
- 开发者编写代码(如
led.c)和对应目录的BUILD.gn,定义目标、依赖、编译选项。 - 执行
hb build命令时,构建系统(hb工具)调用GN解析所有BUILD.gn文件。 - GN根据
BUILD.gn的规则,生成Ninja脚本(.ninja),明确编译顺序、命令和依赖。 - Ninja执行脚本,调用编译器(如
arm-none-eabi-gcc)编译源码,最终生成固件或可执行程序。
常见错误与调试(开发必备)
“未找到目标”错误
原因:deps中依赖的模块路径错误(如拼写错误、路径不存在)。
解决:检查依赖路径是否以//开头(根目录)或相对路径正确,确保依赖模块的BUILD.gn已定义对应目标。“头文件未找到”错误
原因:include_dirs未包含头文件所在目录。
解决:在BUILD.gn中添加include_dirs = [ "头文件目录路径" ]。编译选项不生效
原因:cflags等字段写在目标外部(全局配置需用config模板)。
解决:确保编译选项定义在executable/static_library等目标内部,或通过config统一管理。
BUILD.gn 的典型结构
# 示例:一个简单的动态库 BUILD.gn
import("//build/ohos.gni") # 导入 OpenHarmony 构建全局配置
ohos_shared_library("my_native_lib") { # 定义一个动态库目标
sources = [
"src/mylib.cpp",
"src/utils.c",
]
include_dirs = [
"include",
]
defines = [ "MYLIB_VERSION=1" ]
deps = [
"//third_party/libc++:libc++", # 依赖 C++ 标准库
"//base/hiviewdfx/hilog/native:libhilog", # 依赖鸿蒙日志库
]
# 指定输出 so 名称(可选)
output_name = "libmy_native_lib"
}
🔔 注意:OpenHarmony 对 GN 做了扩展,提供了如
ohos_shared_library、ohos_executable等封装好的模板,简化开发。
关键字段说明(OpenHarmony 扩展)
| 字段 | 说明 |
|---|---|
sources |
源文件列表(C/C++/汇编) |
include_dirs |
头文件搜索路径 |
cflags / cxxflags |
C / C++ 编译选项(如 -Wall, -O2) |
defines |
预处理宏定义 |
deps |
依赖的其他 GN 目标(路径格式://目录/子目录:目标名) |
public_deps |
公共依赖(传递给依赖本模块的其他模块) |
configs |
引用预定义的编译配置(如优化级别、调试符号) |
output_name |
输出文件名(默认为目标名) |
part_name |
(重要)所属“部件(part)”名称,用于 OpenHarmony 的模块化管理 |
install_enable |
是否安装到系统镜像(true/false) |
与 OpenHarmony 构建体系的关系
OpenHarmony 使用 GN + Ninja 作为底层构建引擎,整体流程如下:
源代码 + BUILD.gn
↓
hb (OpenHarmony Build 工具)
↓
GN → 生成 build.ninja
↓
Ninja → 编译生成 .so / .exe / .a
↓
打包进系统镜像(如 system.img)
📌 开发者通常使用
hb build命令,它会自动调用 GN 和 Ninja。
注意事项
路径规范
deps中的路径必须是 绝对路径(以 // 开头),且目标必须存在。部件(Part)归属
每个模块需通过part_name声明属于哪个“部件”,这关系到权限、安全域和产品配置。避免循环依赖
GN 会检查依赖图,循环依赖会导致构建失败。不要手动修改 build.ninja
所有构建逻辑应写在BUILD.gn中,由 GN 自动生成 Ninja 文件。
示例:完整 OpenHarmony Native 模块结构
//my_company/my_feature/
├── BUILD.gn
├── src/
│ ├── my_feature.cpp
│ └── utils.c
└── include/
└── my_feature.h
对应的 BUILD.gn:
import("//build/ohos.gni")
ohos_shared_library("my_feature") {
sources = [
"src/my_feature.cpp",
"src/utils.c",
]
include_dirs = [ "include" ]
deps = [ "//base/hiviewdfx/hilog/native:libhilog" ]
part_name = "my_product" # 必须与 productdefine 中的部件名一致
install_enable = true
}
总结
| 问题 | 答案 |
|---|---|
BUILD.gn 是什么? |
OpenHarmony 中基于 GN 的模块构建配置文件 |
| 谁在用它? | 系统开发者、Native 库开发者、驱动/服务开发者 |
| 为什么重要? | 决定了代码如何被编译、链接、安装,是构建系统的“蓝图” |
| 和 Android 有什么区别? | Android 用 Soong/Bazel/Make,OpenHarmony 统一用 GN+Ninja |
📚 官方参考: