
文章大纲引言一、GN常用的内置变量二、GN常用的内置函数三、CMake 重要语法1、生成动态库2、生成静态库3、生成OBJECT 库4、重要的函数和模块4.1、add_definitions4.2、execute_process4.3、add_dependencies4.4、install4.5、FetchContent四、GN 重要语法1、编译Target2、预构建Target3、group4、external_deps 和 deps五、编写bundle.json 配置模块信息1、inner_kits声明对外开放的动态库可选2、使用inner_kits引言BUILD.gn 是 Google 开发的一种构建脚本语言用于描述项目依赖关系、编译规则等信息以便由 ninja 构建系统生成构建文件并进行编译。华为基于gn进行了一些扩展并把其作为主要的构建系统换句话说无论是完全全采用gn来构建还是通过gn间接触发cmake等其他构建系统也好OpenHarmony中都是要先改成gn的移植主要有以下步骤GN 方式进行构建编写bundle.json 配置Module及其部件信息注册Module 到指定的subsystem一、GN常用的内置变量名称描述current_cpu当前工具链的处理器架构current_os当前工具链的操作系统类型current_toolchain表示当前使用的工具链default_toolchain表示默认使用的工具链target_cpu表示目标平台的CPU类型target_os表示目标平台的操作系统类型root_build_dir表示根目录的构建目录root_gen_dir表示根目录的生成目录root_out_dir表示根目录的输出目录target_out_dir表示目标文件的输出目录target_gen_dir表示中间文件的生成目录defines表示当前目标的预定义宏列表include_dirs表示当前目标的头文件搜索路径列表cflags表示当前目标的C语言编译选项列表cxxflags表示当前目标的C语言编译选项列表ldflags表示当前目标的链接选项列表asmflags表示当前目标的汇编语言编译选项列表libs表示当前目标依赖的库文件列表二、GN常用的内置函数名称描述assert()断言函数如果条件不成立则会抛出一个异常defined()判断变量是否已经定义exec_script()执行一个Python脚本get_label_info()获取标签信息例如标签的名称、路径、类型等等get_path_info()获取路径信息例如路径是否存在、是否是目录、是否是文件等等group()将一组目标文件组合成一个库文件import()导入其他GN构建文件read_file()读取文件内容read_json()读取JSON格式的文件read_path()读取路径中的内容返回一个字符串列表rebase_path()重新定位路径将路径中的某个部分替换为新的值write_file()写入文件内容template()处理字符串模板将模板中的变量替换为实际的值其功能类似与函数action()定义一个自定义的构建动作通过action调用python脚本完成期望动作action_foreach()针对每个元素执行一个自定义的构建动作executable()定义一个可执行文件shared_library()定义一个动态库static_library()定义一个静态库三、CMake 重要语法在 CMake 中add_library 命令用于创建库目标可以是静态库Static Library、动态库Shared Library或目标对象Object Library。1、生成动态库使用add_library(${target_name} SHARED ${ARGN})创建动态库它会生成一个共享对象文件如libexample.so。动态库在运行时被可执行文件加载而不是在链接时复制到可执行文件中。静态库和动态库可以直接被可执行文件或其他库目标链接动态库是一个共享对象文件用于在运行时共享适用于当你想要运行时共享代码节省内存占用或在不重启应用程序的情况下更新功能。动态库需要在编译对象文件后进行额外的生成共享对象。function(onnxruntime_add_shared_library target_name)add_library(${target_name} SHAREDA R G N ) o n n x r u n t i m e c o n f i g u r e t a r g e t ( {ARGN}) onnxruntime_configure_target(ARGN)onnxruntimeconfiguretarget({target_name})endfunction()2、生成静态库使用add_library(${target_name} STATIC ${ARGN})创建静态库它会将对象文件存档成一个单一的文件如libexample.a。静态库在链接时会被复制到最终的可执行文件中。静态库和动态库可以直接被可执行文件或其他库目标链接。静态库是一个存档文件包含所有成员对象文件适用于当你想要分发包含所有依赖的单一文件便于部署。静态库需要在编译对象文件后进行额外的存档步骤function(onnxruntime_add_static_library target_name)add_library(${target_name} STATICA R G N ) o n n x r u n t i m e c o n f i g u r e t a r g e t ( {ARGN}) onnxruntime_configure_target(ARGN)onnxruntimeconfiguretarget({target_name})endfunction()3、生成OBJECT 库使用add_library(${target_name} OBJECT ${ARGN})创建的是目标对象库它是一个包含编译后对象文件的集合。这些对象文件不会自动被存档成库文件.a或.so而是可以直接被其他库或可执行文件目标链接。对象库不能直接被链接它的对象文件需要被其他库或可执行文件显式链接。对象库实际上是不存档的它只是一组编译后的对象文件适用于当你想要在多个目标之间共享编译结果避免重复编译相同的代码。对象库不涉及额外的编译步骤来创建库文件。创建目标对象库的示例add_library(${target_name} OBJECT ${ARGN})${target_name}是库目标的名称${ARGN}是传递给add_library命令的所有额外参数通常是源文件列表。这样创建的对象库可以被其他目标通过target_sources命令添加到它们的源文件列表中。依赖头文件和库include_directories(${ONNXRUNTIME_INCLUDE_DIR}${REPO_ROOT}/include/onnxruntime/core/session)4、重要的函数和模块4.1、add_definitionsadd_definitions 函数用于向项目中添加编译器定义。这些定义会被转换为预处理器宏可以在源代码中使用 #ifdef、#ifndef、#define 等预处理器指令来控制条件编译。比如说add_definitions(-DOPENVINO_CONFIG_NPU1) 相当于是-DOPENVINO_CONFIG_NPU1在编译过程中对于所有的源文件都定义一个名为 OPENVINO_CONFIG_NPU 的宏并将其值设置为 1。在 C 源代码中你可以使用 #ifdef OPENVINO_CONFIG_NPU 或 #if defined(OPENVINO_CONFIG_NPU) 来检查这个宏是否被定义。#ifdefOPENVINO_CONFIG_NPU// 这部分代码只有在 OPENVINO_CONFIG_NPU 宏被定义时才会被编译// 实现与 NPU 相关的功能#endif4.2、execute_process用于执行一个外部进程并可以捕获其输出。它允许 CMake 脚本与系统上的其他命令行工具交互。execute_process(COMMANDcmd[args1[args2...]][WORKING_DIRECTORYdir][TIMEOUTseconds][RESULT_VARIABLEvariable][OUTPUT_VARIABLEvariable][ERROR_VARIABLEvariable][OUTPUT_STRIP_TRAILING_WHITESPACE][ERROR_STRIP_TRAILING_WHITESPACE][ENCODINGencoding][ECHO[STDOUT|STDERR]][RESULT_ERROR_VARIABLEvariable][OUTPUT_FILEfile][ERROR_FILEfile][USE_RESULT_VARIABLE][RESULT_CODEvariable][OUTPUT_QUIET][ERROR_QUIET][COMMAND_ERROR_IS_FATAL])execute_process 执行了 ls -l 命令并将输出和错误分别存储在 output 和 error 变量中。然后使用 message 函数打印输出结果。# 执行一个命令并捕获输出execute_process(COMMAND ls-l OUTPUT_VARIABLE output ERROR_VARIABLE error OUTPUT_STRIP_TRAILING_WHITESPACE)# 打印输出结果message(STATUSCommand Output:\n${output})if(error)message(STATUSCommand Error:\n${error})endif()4.3、add_dependencies是 CMake 中的一个指令用于定义目标通常是可执行程序或库之间的依赖关系。当你使用 add_dependencies 命令时你告诉 CMake 在构建某个目标之前需要先构建其他的目标。add_dependencies(onnxruntime_flatbuffers flatc)onnxruntime_flatbuffers 这个目标在构建之前需要先构建 flatc 目标。如果你还没有定义 onnxruntime_flatbuffers 和 flatc你需要先定义它们add_executable(flatc src/flatc.cpp)# 假设 onnxruntime_flatbuffers 依赖于由 flatc 生成的源代码add_library(onnxruntime_flatbuffers STATIC src/onnxruntime_generated.cpp # 其他源文件)# 设置 onnxruntime_flatbuffers 对 flatc 的依赖add_dependencies(onnxruntime_flatbuffers flatc)4.4、install用于指定在安装目标可执行文件、库、头文件等时应采取的操作。这个函数定义了如何将构建的文件和目录复制到系统的标准安装位置或者用户指定的任何位置。${CMAKE_INSTALL_LIBDIR} 共享库、静态库或者库相关的其他文件的标准目录。${CMAKE_INSTALL_BINDIR} 可执行文件的标准目录。除了可以通过CMake脚本配置还可以在命令行配置 -DCMAKE_INSTALL_LIBDIRsome/path 或 -DCMAKE_INSTALL_BINDIRsome/path 来指定自定义的安装路径。安装可执行文件到默认的 bin 目录install(TARGETS my_executable RUNTIME DESTINATION bin)安装库文件到默认的 lib 目录install(TARGETS my_library LIBRARY DESTINATION lib ARCHIVE DESTINATION lib)安装头文件到特定目录install(FILES my_header.h DESTINATION include)安装数据文件到特定目录install(FILES my_header.h DESTINATION include)设置文件权限install(FILES my_file.txt DESTINATION /etc/my_project PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ)4.5、FetchContentinclude(FetchContent) 是一个命令用于包含 FetchContent 模块。FetchContent 是 CMake 的一个内置模块它提供了一种机制来下载、缓存和包含外部项目或依赖项这些项目或依赖项可以是源代码仓库或二进制文件。使用 FetchContent 模块你可以将第三方库或项目作为依赖项集成到你的 CMake 项目中而无需手动下载和集成这些依赖项。这个模块会自动处理依赖项的下载和配置自动下载如果指定的依赖项不在缓存中FetchContent 会自动从指定的 URL 下载依赖项。缓存机制下载的依赖项会被缓存以避免未来的重新下载提高构建效率。可配置性你可以配置下载的源代码的子模块、分支、标签或提交以确保获取正确的依赖项版本。依赖项包含下载的依赖项可以作为子目录包含在项目中或者作为独立的外部项目使用。兼容性FetchContent 模块兼容 CMake 3.11 及以上版本。使用步骤include(FetchContent)FetchContent_Declare(子模块名) 获取项目。FetchContent_MakeAvailable(子模块)再引入我们的项目中target_link_libraries(主项目 PRIVATE 子模块::子模块)四、GN 重要语法GN 是以target 为基础对象构建组织的所以CMake中的动态库、静态库在GN中映射为对应的Target而华为为每一种Target定义了一套对应的模板直接按照相应语法配置即可。/build/templates/cxx/cxx.gniGN中通用的规则.gn 文件所在的目录是构建根目录。路径以“//” 开头的表示绝对路径以“/“或者则表示相对路径//path/to/proprietary_library:libproprietary 冒号前部分是库所在的目录绝对路径冒号后部分是该库在 BUILD.gn 文件中定义的Target名称gn中不支持以通配符的形式把相应的源文件打包起来gn中的源文件需要逐条列出头文件可以只配置对应目录declare_args 定义变量这个变量疑似是全局唯一的所有在同一个系统内部名称不能重复declare_args(){allow_sanitize_debugfalse}1、编译Targetohos_shared_library 编译动态库ohos_static_library编译静态库ohos_executable 编译可以执行文件ohos_source_set配置源集2、预构建Target/build/templates/cxx/prebuilt.gni所谓prebuilt 从某种程度上来说就是把现有的产物复制到对应的路径下复制过程中根据需要进行相关的依赖关系处理。ohos_prebuilt_executable 不额外配置路径的话会保存到${install_images}/bin目录下ohos_prebuilt_shared_library 不额外配置路径的话会保存到${install_images}/lib 或者lib64目录下ohos_prebuilt_static_library 不额外配置路径的话会保存到${install_images}/lib 或者lib64目录下ohos_prebuilt_etc 不额外配置路径的话会保存到${install_images}/etc目录下ohos_prebuilt_etc(“mnist.onnx”) {source “//third_party/onnxrt/test/demo/mnist.onnx”subsystem_name “third_party”part_name “onnx_runtime”install_images [ chipset_base_dir ] #会保存到 vendor/etc下install_enable true}3、groupgroup作用就是把所有的Target 的编译入口整合到一个group Target里在bundle.json里就不需要逐条去声明了只需要声明一个group就会触发。group(onnx_runtime){deps[:onnxrt_providers_shared,:onnxrt_1.18.0,:onnxrt_link,]if(enable_demo){deps[:onnxrt_demo,:mnist.onnx,]}}4、external_deps 和 depsexternal_deps 节点用于指定项目所依赖的外部非本Module内部库或模块。而deps 属性用于指定一个构建目标如 executable、shared_library、static_library 等所依赖的其他目标。这些依赖可以是同一项目内的其他构建目标也可以是通过 external_deps 声明的外部库。在添加一个模块的时候需要在BUILD.gn中声明它的依赖部件内依赖deps和部件间依赖external_deps。部件内依赖 现有模块module1属于部件part1要添加一个属于部件part1的模块module2module2依赖于module1这种情况就属于部件内依赖。import(“//build/ohos.gni”)ohos_shared_library(module1){…… part_namepart1# 必选所属部件名称 ……}import(//build/ohos.gni)ohos_shared_library(module2){…… deps[module1的gn target,……]# 部件内模块依赖 part_namepart1# 必选所属部件名称}部件间依赖 现有模块module1属于部件part1要添加一个模块module2module2依赖于module1module2属于部件part2。模块module2与模块module1分属于两个不同的部件这种情况就属于部件间依赖。import(//build/ohos.gni)ohos_shared_library(module1){…… part_namepart1# 必选所属部件名称 ……}import(//build/ohos.gni)ohos_shared_library(module2){…… external_deps[part1:module1,……]# 部件间模块依赖这里依赖的模块必须是依赖的部件声明在inner_kits中的模块 part_namepart2# 必选所属部件名称}五、编写bundle.json 配置模块信息OpenHarmony中以模块Module 进行组织其配置文件为bundle.json。{#HPM部件英文名称格式组织/部件名称name:ohos/alsa-lib,# 部件功能一句话描述description:The alsa-lib is a library to interface with ALSA in the Linux kernel and virtual devices using a plugin system.,# 版本号版本号与OpenHarmony版本号一致version:3.1,# 部件Licenselicense:GNU Lesser General Public License v2.1,#HPM包的发布方式当前默认都为code-segmentpublishAs:code-segment,# 发布类型为code-segment时为必填项定义发布类型code-segment的代码还原路径源码路径segment:{destPath:third_party/alsa-lib},#HPM包的目录结构字段必填内容可以留空dirs:{},#HPM包定义需要执行的脚本字段必填值非必填scripts:{},# 部件License路径字段和值非必填licensePath:COPYING,# 部件说明路径字段和值非必填readmePath:{en:README.md,zh:README_zh.md},# 部件属性component:{# 部件名称name:alsa-lib,# 部件所属子系统字段必填值非必填subsystem:thirdparty,# 部件为应用提供的系统能力在component下加入关键字syscap对内部配置相应的系统能力。系统能力若无赋值则默认为true若有赋值则按实际值为准。若值为true则表示该部件默认开启此系统能力若值为false则表明该部件默认关闭此系统能力。WIFI的STA、AP、和HotspotExt三个系统能力是打开的而P2P和Core是关闭的。syscap:[SystemCapability.Communication.WiFi.STA true,SystemCapability.Communication.WiFi.AP true,SystemCapability.Communication.WiFi.P2P false,SystemCapability.Communication.WiFi.Core false,SystemCapability.Communication.WiFi.HotspotExt],# 部件对外的可配置特性列表一般与build中的sub_component对应可供产品配置features:[],# 轻量(mini)小型(small)和标准(standard)可以是多个比如[standard,small],adapted_system_type:[standard],# 部件ROM值rom:950KB,# 部件RAM估值ram:988KB,deps:{components:[],# 部件依赖的其他部件third_party:[]# 部件依赖的其他部件},# 编译相关配置build:{# 部件编译入口模块在此处配置组件产物的so、静态库的BUILD.gn可执行文件不能在这里配置sub_component:[//third_party/alsa-lib:libasound],# 对外开放的模块在此处配置inner_kits:[],# 部件测试用例编译入口test:[]}}}1、inner_kits声明对外开放的动态库可选似乎目前OpenHarmony只支持以so的形式对外提供,也就是external_deps 部分中所依赖到的外部库本质上都是其他模块对外提供的bundle.json中的inner_kits部分的。2、使用inner_kits这样子声明之后就可以在external_deps里像 c_utils:c_utils这样直接形如”模块名:inner_kits “即 onnx_runtime:onnxrt_1.18.0使用这个inner_kits模块的bundle.json 下的 “component” 下的deps里components中配置上inner_kits所在Component的name根据具体情况再不同的子节点中配置即可有些可能需要配置在third_party下的。在使用这个inner_kits模块的BUILD.gn下配置需要引入的inner_kits的头文件和库Target