Gn是什么?
它是Google用来维护chromium项目的编译工具,现在相关的开源项目都基于gn来进行编译管理。目前一些大型系统的都会使用gn,例如谷歌,鸿蒙。
Gn就是一个构建脚本生成器,是之前gyp的升级版本。
并且gn是基于c++编写,效率要比基于python的gyp快了近20倍。
更多技术文章,全网首发公众号“摸鱼IT”,希望大家关注、转发、点赞!谷歌gn编译文件的使用简介
官网文档参考:https://gn.googlesource.com/gn/+/master/docs
参考文档:鸿蒙内核源码分析(GN应用篇) | GN语法及在鸿蒙的使用 | 百篇博客分析OpenHarmony源码 | v60.02 - 掘金
GN文件的作用
对于一套编译工具,要将源代码编译成最终可以执行文件,需要三个部分构件系统:GN、Ninja、GCC(这里也可以是其他的编译器)
GN作为编译逻辑部分。通过gn会将整个项目串联在一起,生成build.ninja文件。
Ninja作为构建中间部分,用最简单最快速的方式将编译文件编译参数传递给编译系统,由Ninja负责执行编译过程。
GCC作为编译部分,通常随编译系统而变化。
接下来介绍gn文件中几个重要的部分和语法:
引用 | Import
.gn是源文件;.gni是头文件,类似C++中的头文件.h 通过import进行引用
import("//build/lite/config/hap_pack.gni")
目标项 | Targets
目标是构建图中的一个节点。它通常表示将生成某种可执行文件或库文件。整个构建是由一个个的目标组成。
以下是一些常使用的内置目标:
action:运行一个脚本产生一个文件
executable:生成可执行文件
shared_library:动态库,一个.dll或.so
static_library:静态库,一个.lib文件或者.a
app:可执行程序
android_apk:生成一个APK
shared_library("cameraApp") #生成一个cameraApp的动态库
配置项 | Configs
记录完成目标项所需的配置信息, 配置信息可以包括flags,defines,include_dirs等,但是不包括sources和deps/public_deps等依赖性文件。例如:
configs = [
":config_json_creator_test",
"$ace_root:ace_test_config",
]
源文件 | Sources
这个标签的意思是列出来需要编译的源文件,当然,可以在其中使用条件语句进行条件编译。
sources = [ #需要编译的源码
"cameraApp/src/main/cpp/camera_ability.cpp",
"cameraApp/src/main/cpp/camera_ability_slice.cpp",
"cameraApp/src/main/cpp/camera_manager.cpp",
]
依赖项 | Deps
即编译Targets所用到的依赖,依赖应按字母顺序排列。
当前文件中的 Deps 应首先写入,并且不能使用文件名限定。
其他 deps 应始终使用完全限定的路径名,除非出于某种原因需要相对路径名。
deps = [
"${aafwk_lite_path}/frameworks/ability_lite:aafwk_abilitykit_lite",
"${appexecfwk_lite_path}/frameworks/bundle_lite:bundle",
"//foundation/distributeddatamgr/kv_store/interfaces/inner_api/kv_store:kv_store",
"//foundation/graphic/surface",
"//foundation/graphic/ui:lite_ui",
"//foundation/graphic/utils:lite_graphic_utils",
"//foundation/multimedia/camera_lite/frameworks:camera_lite",
"//foundation/multimedia/media_lite/frameworks/recorder_lite:recorder_lite",
"//foundation/systemabilitymgr/samgr_lite/samgr:samgr",
]
替换 $
使用$支持简单的变量替换,其中美元符号后的单词被替换为变量的值。如果没有非变量名字符来终止变量名称,可以选择{}包围名称。更复杂的表达式不被支持,仅支持变量名称替换。
a = "moyupath"
b = "$a/king.cc" #b -> "moyupath/king.cc"
c = "king${a}bar.cc" #c -> "kingmoyupathbar.cc"
清单 [ ]
没有办法得到一个列表的长度。如果你发现自己想要做这种事情,那么你就是想在构建中做太多的工作。
列表支持追加:
a = [ "first" ]
a += [ "second" ] # [ "first", "second" ]
a += [ "third", "fourth" ] # [ "first", "second", "third", "fourth" ]
b = a + [ "fifth" ] # [ "first", "second", "third", "fourth", "fifth" ]
a = [ "first" ]a += [ "second" ] # [ "first", "second" ]a += [ "third", "fourth" ] # [ "first", "second", "third", "fourth" ]b = a + [ "fifth" ] # [ "first", "second", "third", "fourth", "fifth" ]
代码示例
#目的是要得到项目各个模块的编译入口
group("ohos") {
deps = []
if (ohos_build_target == "") {
# Step 1: Read product configuration profile.
# 第一步:读取配置文件product_path的值来源于根目录的ohos_config.json,如下,内容由 hb set 命令生成
# {
# "root_path": "/home/openharmony",
# "board": "hispark_aries",
# "kernel": "liteos_a",
# "product": "ipcamera_hispark_aries",
# "product_path": "/home/openharmony/vendor/hisilicon/hispark_aries",
# "device_path": "/home/openharmony/device/hisilicon/hispark_aries/sdk_liteos",
# "patch_cache": null
#}
product_cfg = read_file("${product_path}/config.json", "json")
# Step 2: Loop subsystems configured by product.
# 第二步:循环处理各自子系统,config.json中子系统部分格式如下hb
#"subsystems": [
# {
# "subsystem": "aafwk",
# "components": [
# { "component": "ability", "features":[ "enable_ohos_appexecfwk_feature_ability = false" ] }
# ]
# },
# ...
# {
# "subsystem": "distributed_schedule",
# "components": [
# { "component": "system_ability_manager", "features":[] },
# { "component": "foundation", "features":[] },
# { "component": "distributed_schedule", "features":[] }
# ]
# },
# {
# "subsystem": "kernel",
# "components": [
# { "component": "liteos_a", "features":[] }
# ]
# },
#]
foreach(product_configed_subsystem, product_cfg.subsystems) {#对子系统数组遍历操作
subsystem_name = product_configed_subsystem.subsystem #读取一个子系统 aafwk,hiviewdfx,security ==
subsystem_info = {
}
# Step 3: Read OS subsystems profile.
# 第三步: 读取各个子系统的配置文件
subsystem_info =
read_file("//build/lite/components/${subsystem_name}.json", "json")
# Step 4: Loop components configured by product.
# 第四步: 循环读取子系统内各控件的配置信息
# 此处以内核为例://build/lite/components/kernel.json"
# "components": [
# {
# "component": "liteos_a", # 组件名称
# "description": "liteos-a kernel", # 组件一句话功能描述
# "optional": "false", # 组件是否为最小系统必选
# "dirs": [ # 组件源码路径
# "kernel/liteos_a"
# ],
# "targets": [ # 组件编译入口
# "//kernel/liteos_a:kernel"
# ],
# "rom": "1.98MB", # 组件ROM值
# "ram": "", # 组件RAM估值
# "output": [ # 组件编译输出
# "liteos.bin"
# ],
# "adapted_board": [ # 组件已适配的主板
# "hispark_aries",
# "hispark_taurus",
# "hi3518ev300",
# "hi3516dv300",
# ],
# "adapted_kernel": [ "liteos_a" ], # 组件已适配的内核
# "features": [], # 组件可配置的特性
# "deps": {
# "components": [], # 组件依赖的其他组件
# "third_party": [ # 组件依赖的三方开源软件
# "FreeBSD",
# "musl",
# "zlib",
# "FatFs",
# "Linux_Kernel",
# "lwip",
# "NuttX",
# "mtd-utils"
# ]
# }
# },
# ]
foreach(product_configed_component,
product_configed_subsystem.components) { #遍历项目控件数组
# Step 5: Check whether the component configured by product is exist.
# 第五步: 检查控件配置信息是否存在
component_found = false #初始为不存在
foreach(system_component, subsystem_info.components) {#项目控件和子系统中的控件遍历对比
if (product_configed_component.component ==
system_component.component) { #找到了liteos_a
component_found = true
}
}
#如果没找到的信息,则打印项目控件查找失败日志
assert(
component_found,
"Component \"${product_configed_component.component}\" not found" +
", please check your product configuration.")
# Step 6: Loop OS components and check validity of product configuration.
# 第六步: 检查子系统控件的有效性并遍历控件组,处理各个控件
foreach(component, subsystem_info.components) {
kernel_valid = false #检查内核
board_valid = false #检查开发板
# Step 6.1: Skip component which not configured by product.
if (component.component == product_configed_component.component) {
# Step 6.1.1: Loop OS components adapted kernel type.
foreach(component_adapted_kernel, component.adapted_kernel) {
if (component_adapted_kernel == product_cfg.kernel_type &&
kernel_valid == false) { #内核检测是否已适配
kernel_valid = true
}
}
# 如果内核未适配,则打印未适配日志
assert(
kernel_valid,
"Invalid component configed, ${subsystem_name}:${product_configed_component.component} " + "not available for kernel: ${product_cfg.kernel_type}!")
# Step 6.1.2: Add valid component for compiling.
# 添加有效组件进行编译
foreach(component_target, component.targets) {//遍历组件的编译入口
deps += [ component_target ] #添加到编译列表中
}
}
}
}
}
# Step 7: Add device and product target by default.
# 第七步: 添加设备和项目的编译单元
# "product_path": "/home/openharmony/vendor/hisilicon/hispark_aries",
# "device_path": "/home/openharmony/device/hisilicon/hispark_aries/sdk_liteos",
deps += [
"${device_path}/../", #添加 //device/hisilicon/hispark_aries 进入编译项
"${product_path}" #添加 //vendor/hisilicon/hispark_aries 进入编译项
]
} else {#编译指定的组件,例如 hb build -T targetA&&targetB
deps += string_split(ohos_build_target, "&&")
}
}
openharmony\applications\sample\camera\cameraApp下BUILD.gn文件:
import("//build/lite/config/hap_pack.gni")
#编的库名称
shared_library("cameraApp") {
sources = [ #需要编译的源码
"cameraApp/src/main/cpp/camera_ability.cpp",
"cameraApp/src/main/cpp/camera_ability_slice.cpp",
"cameraApp/src/main/cpp/camera_manager.cpp",
]
deps = [#引用三方组件库
"${aafwk_lite_path}/frameworks/ability_lite:aafwk_abilitykit_lite",
"${appexecfwk_lite_path}/frameworks/bundle_lite:bundle",
"//foundation/distributeddatamgr/kv_store/interfaces/inner_api/kv_store:kv_store",
"//foundation/graphic/surface",
"//foundation/graphic/ui:lite_ui",
"//foundation/graphic/utils:lite_graphic_utils",
"//foundation/multimedia/camera_lite/frameworks:camera_lite",
"//foundation/multimedia/media_lite/frameworks/recorder_lite:recorder_lite",
"//foundation/systemabilitymgr/samgr_lite/samgr:samgr",
]
include_dirs = [#需要的头文件
"cameraApp/src/main/cpp",
"${aafwk_lite_path}/interfaces/kits/ability_lite",
"${appexecfwk_lite_path}/interfaces/kits/bundle_lite",
"${aafwk_lite_path}/interfaces/kits/want_lite",
"//foundation/multimedia/camera_lite/interfaces/kits",
"//foundation/multimedia/camera_lite/interfaces/kits",
]
ldflags = [
"-L$ohos_root_path/sysroot/usr/lib",
"-Wl,-rpath-link=$ohos_root_path/sysroot/usr/lib",
"-lstdc++",
"-lcamera_lite",
"-lsurface",
"-lrecorder_lite",
]
defines = [
"ENABLE_WINDOW=1",
"ABILITY_WINDOW_SUPPORT",
]
}
hap_pack("cameraApp_hap") {
deps = [ ":cameraApp" ]
mode = "hap"
json_path = "cameraApp/src/main/config.json"
ability_so_path = "$root_out_dir/libcameraApp.so"
force = "true"
cert_profile = "cert/camera_AppProvision_Release.p7b"
resources_path = "cameraApp/src/main/resources"
hap_name = "cameraApp"
privatekey = "HOS Application Provision Release"
}
如上就简单介绍了gn文件的基本使用方法,小伙伴们快快学习使用起来!