当前位置: 首页 > news >正文

深入理解OpenHarmony中的BUILD.gn:从语法到模块化构建

在 OpenHarmony 中,BUILD.gn 文件是编译构建系统的核心配置文件,用于定义项目的结构、依赖关系、编译规则以及构建目标(如可执行文件、库文件、配置文件等)
后续熟悉OpenHarmony之后,你会发现BUILD.gn语法无处不在,掌握很有必要

1.BUILD.gn 在 OpenHarmony 中的角色

唯一入口:

每个可编译单元(子系统/部件/模块)必须提供 BUILD.gn,由 GN 工具链逐层加载,最终生成 Ninja 文件供 Ninja 执行增量编译。

声明目标:

通过预定义的 ohos_* 模板(executable、static_library …)告诉系统“这里要生成什么”以及“它依赖谁”。

隔离与复用:

变量、config、source_set 仅在当前文件或显式 public_configs/deps 中可见,天然支持模块化。

定义构建目标与输出类型

BUILD.gn 通过声明式语法指定需要构建的产物类型,例如:

可执行文件 (executable 或 ohos_executable):用于生成可直接运行的程序(如系统服务、命令行工具)。 共享库 (shared_library 或 ohos_shared_library):生成动态链接库(.so 文件),供其他模块调用 。 静态库
(static_library 或 ohos_static_library):生成静态链接库(.a 文件),在编译时链接到目标程序 。
配置文件/资源文件 (ohos_prebuilt_etc, ohos_copy,
action):定义策略文件、规则文件、配置文件等的生成、复制和安装路径。 预构建模块
(ohos_prebuilt):用于集成第三方预编译好的库或应用(如 HAP 包)。 构建组
(group):将多个相关目标组织在一起,便于统一管理依赖

2.BUILD.gn中常用的函数总结

函数名说明示例用法
ohos_executable()构建可执行程序(如 app、bin)ohos_executable("hello") { sources = ["main.c"] }
ohos_static_library()构建静态库(.a)ohos_static_library("libfoo") { sources = ["foo.c"] }
ohos_shared_library()构建动态库(.so)ohos_shared_library("libbar") { sources = ["bar.c"] }
ohos_source_set()仅编译源文件,不打包为库ohos_source_set("utils") { sources = ["util.c"] }
ohos_group()逻辑分组,无实际构建产物ohos_group("modules") { deps = [":mod1", ":mod2"] }
ohos_test()构建测试目标(单元测试)ohos_test("test_foo") { sources = ["test_foo.c"] }
ohos_prebuilt_executable()使用预编译可执行文件ohos_prebuilt_executable("tool") { source = "tool.bin" }
ohos_prebuilt_shared_library()使用预编译 .so 文件ohos_prebuilt_shared_library("libxx") { source = "libxx.so" }
ohos_prebuilt_static_library()使用预编译 .a 文件ohos_prebuilt_static_library("libyy") { source = "libyy.a" }
3.使用注意事项;
模板快速场景使用禁区
ohos_executable产出可执行文件(/bin 或 /test)① 必须写 part_name,否则链接阶段直接丢弃;② 不要 sources += glob(*.c)——glob 会吃进临时文件;③ 依赖 .so 时,把 .so 目标写在 deps 而非 external_deps,否则运行时找不到。 4.ohos_executable默认不安装进开发板,如果有需要,需指定 install_enable = true
ohos_static_library把多份 .o 打包成 .a,供别的模块静态链接① 对外暴露头文件目录一定用 public_configs = [ ":my_inc" ],否则父目标找不到头文件;② 不要在该模板里 sources += third_party/xx.c,第三方代码用 ohos_prebuilt_static_library 或独立 source_set;③ 如果最终要进系统镜像,确认对应 part_nameohos.buildmodule_list 中,否则镜像里会缺库。
ohos_shared_library生成 .so 供运行时动态加载① 默认带 -fvisibility=hidden,想导出符号必须显式 __attribute__((visibility("default"))) 或用 export_symbols 文件;② 不要把 ohos_shared_library 目标放进 external_deps,只能放在 deps;③ 若对可执行文件 dlopen,确保该 .sopart_name 与可执行文件同属一个子系统,否则系统编译器会裁掉认为“无人用”。
ohos_source_set仅编译 .o 不打包,用于中间复用① 不能出现在 ohos.buildmodule_list 里,它没产物;② 别的目标 deps 引用它后,会自动把 .o 合并到最终产物,勿再手动加 .osources 造成重复定义;③ 不要给 source_setpart_name,写了也会被忽略。
ohos_test单元/集成测试可执行文件① 必须依赖 gtesthctest 之一,写在 external_deps = [ "gtest:gtest_main" ];② 测试二进制默认安装到 /system/bin,名字冲突会导致 hb 安装失败,加 output_name = "my_test" 区分;③ 想在 CI 里跑,记得在 test_config.json 中注册,否则 xdevice 不会拉起。
ohos_group逻辑分组,无产物① 仅用于聚合 deps,不能写 sources;② 不要给 grouppart_name,因为它不参与链接;③ 若只是为了 gn path 可视化,可以嵌套 group,但层级过深会导致 gn 解析变慢。
ohos_prebuilt_executable直接使用现成二进制(工具/固件)source = "xxx.bin" 路径必须相对于本 BUILD.gn 或写绝对路径 //;② 如果该二进制要进镜像,必须再写 install_images = [ "system" ],否则只编译不进包;③ 架构不匹配(如把 x86 工具放进 arm 产品)会在 hb build 最后打包阶段才报错,务必确认 target_cpu
ohos_prebuilt_static_library使用已有 .a① 必须写 output_name 且不含 lib 前缀和 .a 后缀,否则链接器找不到;② 如果 .a 是用 gcc 编译而当前板子用 clang,一定确认 ABI 一致,不然链接时报 undefined reference to __gnu_*;③ 不要把第三方 .a 直接 sources += ["libxx.a"],会被当成普通文件重新编译。
ohos_prebuilt_shared_library使用已有 .so① 同样需要 install_images = [ "system" ] 才能进镜像;② 若该 .so 还依赖其他 .so,务必把依赖也列进 deps,否则运行时 dlopen 失败;③ 版本升级时记得同步更新 .so 并执行 gn clean,避免旧 .so 被缓存。
4.常用字段总结:
字段名说明
sources源文件列表
deps内部依赖(同 BUILD.gn 中的目标)
external_deps外部部件依赖,格式为 "部件名:模块名"
public_configs对外暴露的编译配置(如头文件路径)
part_name必填,指定所属部件(如 "part_name = "my_part"
include_dirs头文件搜索路径(常用于 config()
5.使用实例:

1.ohos_prebuilt_etc(“系统内置的HAp名”),具体参applications\standard\hap\BUILD.gn

import("//build/ohos.gni")
ohos_prebuilt_etc("launcher_hap") {source = "Launcher.hap"module_install_dir = "app/com.ohos.launcher"part_name = "prebuilt_hap"subsystem_name = "applications"
}

2.ohos_shared_library(“动态库名”),可以参考foundation\CastEngine\castengine_wifi_display\services\BUILD.gn

ohos_shared_library("sharing_service") {install_enable = truesanitize = {cfi = truecfi_cross_dso = truedebug = falseboundary_sanitize = trueubsan = trueinteger_overflow = true}deps = ["agent:sharing_agent_srcs","configuration:sharing_configure_srcs","context:sharing_context_srcs","event:sharing_event_srcs","interaction:sharing_interaction_srcs","mediachannel:sharing_media_channel_srcs","mediaplayer:sharing_media_player_srcs","scheduler:sharing_scheduler_srcs",]deps += ["codec:sharing_codec","common:sharing_common","network:sharing_network","protocol/rtp:sharing_rtp","protocol/rtsp:sharing_rtsp","utils:sharing_utils","//third_party/cJSON:cjson_static",]if (sharing_framework_support_wfd) {deps += ["impl/scene/wfd:sharing_wfd_srcs","impl/screen_capture:sharing_screen_capture_srcs","impl/wfd:sharing_wfd_session_srcs",]}configs = [ "$SHARING_ROOT_DIR/tests:coverage_flags" ]external_deps = ["access_token:libaccesstoken_sdk","access_token:libnativetoken","access_token:libtoken_setproc","audio_framework:audio_capturer","audio_framework:audio_client","audio_framework:audio_renderer","graphic_2d:librender_service_base","graphic_surface:surface","hilog:libhilog","samgr:samgr_proxy",]subsystem_name = "castplus"part_name = "sharing_framework"
}

3.ohos_static_library("静态库名“),一般在third_party子系统中用的最多,比如third_party\zlib\BUILD.gn

ohos_static_library("libz") {sources = ["adler32.c","compress.c","contrib/minizip/ioapi.c","contrib/minizip/unzip.c","contrib/minizip/zip.c","crc32.c","crc32.h","deflate.c","deflate.h","gzclose.c","gzguts.h","gzlib.c","gzread.c","gzwrite.c","infback.c","inffast.c","inffast.h","inffixed.h","inflate.c","inflate.h","inftrees.c","inftrees.h","trees.c","trees.h","uncompr.c","zconf.h","zlib.h","zutil.c","zutil.h",]configs = [ ":zlib_config" ]public_configs = [ ":zlib_public_config" ]part_name = "zlib"subsystem_name = "thirdparty"
}

4.ohos_source_set(”仅编译,不打包“),一般在third_party子系统中用的最多。比如third_party\vk-gl-cts\external\openglcts\modules\BUILD.gn

ohos_source_set("glcts_source") {sources = [ "//third_party/vk-gl-cts/external/openglcts/modules/glcTestPackageRegistry.cpp" ]include_dirs = ["//third_party/vk-gl-cts/modules/glshared","//third_party/vk-gl-cts/modules/egl","//third_party/vk-gl-cts/modules/gles2","//third_party/vk-gl-cts/modules/gles3","//third_party/vk-gl-cts/modules/gles31","//third_party/vk-gl-cts/external/openglcts/modules/common","//third_party/vk-gl-cts/external/openglcts/modules/gl","//third_party/vk-gl-cts/external/openglcts/modules/gles2","//third_party/vk-gl-cts/external/openglcts/modules/gles3","//third_party/vk-gl-cts/external/openglcts/modules/gles31","//third_party/vk-gl-cts/external/openglcts/modules/gles32","//third_party/vk-gl-cts/external/openglcts/modules/glesext","//third_party/vk-gl-cts/external/openglcts/modules",]include_dirs += deqp_common_include_dirsdeps = ["//third_party/vk-gl-cts/external/openglcts/modules/common:libdeqp_glcts-common-nocontext-package","//third_party/vk-gl-cts/external/openglcts/modules/gl:libdeqp_glcts-gl","//third_party/vk-gl-cts/external/openglcts/modules/gles2:libdeqp_glcts-es2","//third_party/vk-gl-cts/external/openglcts/modules/gles3:libdeqp_glcts-es3","//third_party/vk-gl-cts/external/openglcts/modules/gles31:libdeqp_glcts-es31","//third_party/vk-gl-cts/external/openglcts/modules/gles32:libdeqp_glcts-es32","//third_party/vk-gl-cts/external/openglcts/modules/glesext:libdeqp_glcts-esext","//third_party/vk-gl-cts/modules/egl:libdeqp-egl","//third_party/vk-gl-cts/modules/gles2:libdeqp-gles2","//third_party/vk-gl-cts/modules/gles3:libdeqp-gles3","//third_party/vk-gl-cts/modules/gles31:libdeqp-gles31",]configs = [ ":glcts_config" ]
}

5.ohos_group(“某类组”),一般用于控制某类具有相同功能、含义的模块

文件foundation\CastEngine\castengine_wifi_display\tests\BUILD.gn:
group("test") {deps = [ "demo:demo_test" ]
}
文件foundation\CastEngine\castengine_wifi_display\tests\demo\BUILD.gn中集体管理demo_test这个分组
import("//build/ohos.gni")
import("//foundation/CastEngine/castengine_wifi_display/config.gni")
group("demo_test") {deps = ["delaytest:loop_back_demo","interaction:sharing_wfd_source_demo","network:sharing_network_client_demo","network:sharing_network_server_demo","network:udp_client_demo","network:udp_server_demo","rtp:sharing_rtp_dec_demo","rtp:sharing_rtp_enc_demo","wfd:sharing_wfd_demo",]
}
这里如果我想要让demo_test下的可执行测试程序都编译,我可以在文件foundation\CastEngine\castengine_wifi_display\BUILD.gn中添加如下:
group("sharing_packages") {deps = ["//foundation/CastEngine/castengine_wifi_display/interfaces/innerkits/native/wfd:sharingwfd_client","//foundation/CastEngine/castengine_wifi_display/interfaces/kits/js/wfd:sharingwfd_napi","//foundation/CastEngine/castengine_wifi_display/sa_profile:sharing_sa_profile","//foundation/CastEngine/castengine_wifi_display/services:sharing_services_package","//foundation/CastEngine/castengine_wifi_display/services/etc:sharing_service.rc",+  "//foundation/CastEngine/castengine_wifi_display/tests:test"  #添加这里可以让demo_test测试模块全部编译进去]
}
因为tests目录下依赖test分组。test分组又是demo:demo_test,所有最终实现模块之间的编译

其他关于测试和预编译的可以查阅源码目录下的BUILD.gn配置文件学习,具体不在介绍

“预编译”就是 “不把源码拿过来重新编译,而是直接拿别人已经编译好的产物(.a / .so / bin/.hap等编译产物)参与链接或打包”。


文章转载自:

http://eBMn7v8x.sjgsh.cn
http://snCNoo6i.sjgsh.cn
http://WOqWhyJE.sjgsh.cn
http://5u4WYsoz.sjgsh.cn
http://YZDckf23.sjgsh.cn
http://12w4eKio.sjgsh.cn
http://6W2nXtGZ.sjgsh.cn
http://JiBSfNDO.sjgsh.cn
http://EonWw16I.sjgsh.cn
http://u1tWNlnt.sjgsh.cn
http://CoXQatnS.sjgsh.cn
http://qV9XPlAd.sjgsh.cn
http://LJcnubGA.sjgsh.cn
http://rDQfXxnb.sjgsh.cn
http://mG7CGT9P.sjgsh.cn
http://VSZArvCX.sjgsh.cn
http://NHq7hgoY.sjgsh.cn
http://fuLYGN51.sjgsh.cn
http://obktAa8z.sjgsh.cn
http://aDUbY5kA.sjgsh.cn
http://7juMP16E.sjgsh.cn
http://00oRymRs.sjgsh.cn
http://7As63gai.sjgsh.cn
http://dbGJ4xHV.sjgsh.cn
http://hqIRgZhT.sjgsh.cn
http://0krG3pKJ.sjgsh.cn
http://s2feXcZD.sjgsh.cn
http://d9ztDa22.sjgsh.cn
http://1y0FuD8X.sjgsh.cn
http://AmZL4fSS.sjgsh.cn
http://www.dtcms.com/a/372165.html

相关文章:

  • 阴阳学:从入门到精通
  • vulhub通关笔记1—docker unauthorized-rce
  • ZYNQ PS XADC读取芯片内部温度值,电压值。
  • 每日一题(3)
  • 泛型编程(简单介绍,通俗易懂)
  • 扩散模型揭秘:生成式AI的核心与应用
  • 【Flink】Flink Runtime 架构设计
  • MySQL数据库同步
  • 使用 Spring Security 实现 OAuth2:一步一步的操作指南
  • Axure: 分组柱状图1
  • CEEMDAN-PSO-CNN-GRU 锂电池健康状态预测matlab
  • Spring Cloud Gateway 作为一个独立的服务进行部署吗
  • webrtc弱网-LossBasedBweV2类源码分析与算法原理
  • leetcode hot100 二叉搜索树
  • 杂学项目1、S32K144与上位机通信
  • GitHub自动化利器:Probot框架实战指南
  • 一款没有任何限制的免费远程手机控制手机的软件简介
  • 企云网多应用授权系统源码 正版查询系统源码
  • Windows netstat 命令使用说明
  • 软件工程:DO-178中的适航要求核心要素
  • Caffeine Count-Min Sketch TinyLFU实现:FrequencySketch
  • 【系统分析师】第7章-基础知识:软件工程(核心总结)
  • 【拍摄学习记录】00-总结记录
  • 探索 CSS 过渡:打造流畅网页交互体验
  • 大语言模型(LLM)的基本概念
  • unsloth FastLanguageModel类主要函数详解,具体作用和参数
  • HTTPS协议——对于HTTP的协议的加密
  • Qwen2.5-VL翻译
  • 碳纤维和短切碳纤维(中)
  • unsloth 笔记: training的时候进行evaluation