OpenHarmony鸿蒙硬件Hi3861-构建系统-BUILD.gn

介绍

在 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 构建流程中的作用

  1. 开发者编写代码(如led.c)和对应目录的BUILD.gn,定义目标、依赖、编译选项。
  2. 执行hb build命令时,构建系统(hb工具)调用GN解析所有BUILD.gn文件。
  3. GN根据BUILD.gn的规则,生成Ninja脚本(.ninja),明确编译顺序、命令和依赖。
  4. Ninja执行脚本,调用编译器(如arm-none-eabi-gcc)编译源码,最终生成固件或可执行程序。

常见错误与调试(开发必备)

  1. “未找到目标”错误
    原因:deps中依赖的模块路径错误(如拼写错误、路径不存在)。
    解决:检查依赖路径是否以//开头(根目录)或相对路径正确,确保依赖模块的BUILD.gn已定义对应目标。

  2. “头文件未找到”错误
    原因:include_dirs未包含头文件所在目录。
    解决:在BUILD.gn中添加include_dirs = [ "头文件目录路径" ]。

  3. 编译选项不生效
    原因: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。


注意事项

  1. 路径规范
    deps 中的路径必须是 绝对路径(以 // 开头),且目标必须存在。

  2. 部件(Part)归属
    每个模块需通过 part_name 声明属于哪个“部件”,这关系到权限、安全域和产品配置。

  3. 避免循环依赖
    GN 会检查依赖图,循环依赖会导致构建失败。

  4. 不要手动修改 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

📚 官方参考:


原文出处:/show_1GW2Dc9FY2kN.html