回顾
在文章中已经分析openharmony的小型系统(liteos-a)编译过程,最主要的就是调用gn/ninja/makefs三个命令最终生成可烧录的镜像文件
从前面文件可以看到hb build调用的gn命令参数如下

这里详细分析一下gn工具在编译过程中的使用
GN工具
这里简单介绍下GN工具的使用方法,gn语法可以参考http://weharmonyos.com/openharmony/compile/gn/docs/
,已经熟悉的可以跳过
gn_cmd说明
从上面图中可以看到使用的命令类型为gn gen <output_dir> [options]
,此命令就是为了将所有需要的BUILD.gn文件生成为*.ninja文件供ninja进行编译; 可以通过gn help gen
命令查看详细的用法说明, 如下
itsenlin@itsenlin-PC:~/gn$ ./gn help gen
gn gen [--check] [<ide options>] <out_dir>
Generates ninja files from the current tree and puts them in the given output
directory.
......
itsenlin@itsenlin-PC:~/gn$
下面重点说一下gn_cmd中的[options]
dotfile
-
--root
显示指定编译系统的根目录 -
--dotfile
是为了显示指定dotfile的路径;如果不指定,gn会在当前目录下寻找,并且以.gn
所在目录为根目录处理转换工作
可以通过命令gn help dotfile
查看详细信息,截取部分如下
itsenlin@itsenlin-PC:~/gn$ ./gn help dotfile
.gn file
When gn starts, it will search the current directory and parent directories
for a file called ".gn". This indicates the source root. You can override
this detection by using the --root command-line argument
The .gn file in the source root will be executed. The syntax is the same as a
buildfile, but with very limited build setup-specific meaning.
If you specify --root, by default GN will look for the file .gn in that
directory. If you want to specify a different file, you can additionally pass
--dotfile:
gn gen out/Debug --root=/home/build --dotfile=/home/my_gn_file.gn
Variables
buildconfig [required]
Path to the build config file. This file will be used to set up the
build file execution environment for each toolchain.
root [optional]
Label of the root build target. The GN build will start by loading the
build file containing this target name. This defaults to "//:" which will
cause the file //BUILD.gn to be loaded.
......
Example .gn file contents
buildconfig = "//build/config/BUILDCONFIG.gn"
check_targets = [
"//doom_melon/*", # Check everything in this subtree.
"//tools:mind_controlling_ant", # Check this specific target.
]
root = "//:root"
secondary_source = "//build/config/temporary_buildfiles/"
default_args = {
# Default to release builds for this project.
is_debug = false
is_component_build = false
}
itsenlin@itsenlin-PC:~/gn$
dotfile中有很多内置变量,这里用到了两个:
buildconfig
和root
(其他的可以通过命令帮助信息中查看);这里的root
与前面介绍的--root
不一样,要注意这一点
liteos-a编译系统的dotfile内容如下:
itsenlin@itsenlin-PC:~/code/ohos_3.0$ cat build/lite/.gn
# The location of the build configuration file.
buildconfig = "//build/lite/config/BUILDCONFIG.gn"
# The source root location.
root = "//build/lite"
itsenlin@itsenlin-PC:~/code/ohos_3.0$
-
buildconfig
: 安装设置编译所需要的环境,像编译工具链 -
root
: 我理解BUILD.gn
文件是分散在各个模块源码路径下的,要想找到这些文件就需要有一个入口(或者要根),root就是指定这个入口BUILD.gn
文件所在路径 - 这里的
//
表示编译系统的根目录,就是前面--root
指定的目录,所以这里的//build
对应的就是/home/itsenlin/code/ohos_3.0/build
编译环境配置
liteos-a是嵌入式系统,而编译环境是linux系统,这就需要用到交叉编译方式,这个就可以在环境配置中指定target_os
、target_cpu
、board_cpu
等等信息
这些信息就是buildconfig
参数指定的BUILDCONFIG.gn
文件中配置的
除了以上信息,还配置了以下几个重要信息
- 默认编译工具链
set_default_toolchain(...)
- 默认的config信息
default_target_configs
- 可执行目标
executable
、静态库目标static_library
、动态库目标shared_library
的定义以及action
的定义
详细信息可以查看//build/lite/config/BUILDCONFIG.gn
文件内容
编译工具链--toolchain
toolchain定义源码编译需要的工具,像编译器、汇编器、连接器等等,一般在.gn所在目录下有一个toolchain
目录,里面的BUILD.gn
定义详细的编译工具链信息
这里目录结构如下:
├── toolchain
│ ├── BUILD.gn
│ ├── clang.gni
│ └── gcc.gni
从结构信息中可以看到定义了clang和gcc两种工具链,通过变量board_toolchain_type
来区分(此变量也是buildconfig中定义的),具体信息参见BUILD.gn文件内容,如下
import("//build/lite/toolchain/clang.gni")
import("//build/lite/toolchain/gcc.gni")
# Set toolchain configed by board.
if (board_toolchain != "" && use_board_toolchain) {
if (board_toolchain_type == "gcc") {
gcc_toolchain(board_toolchain) {
cc = ohos_current_cc_command
cxx = ohos_current_cxx_command
ar = ohos_current_ar_command
ld = ohos_current_ld_command
strip = ohos_current_strip_command
}
} else if (board_toolchain_type == "clang") {
clang_toolchain(board_toolchain) {
cc = ohos_current_cc_command
cxx = ohos_current_cxx_command
ar = ohos_current_ar_command
ld = ohos_current_ld_command
strip = ohos_current_strip_command
}
}
} else {
clang_toolchain("linux_x86_64_ohos_clang") {
cc = ohos_current_cc_command
cxx = ohos_current_cxx_command
ar = ohos_current_ar_command
ld = ohos_current_ld_command
strip = ohos_current_strip_command
}
}
可以做为一个模板,其他项目上想使用gn+ninja编译的话就可以参考这里的设置; 另外gn下的toolchain信息可以通过命令
gn help toolchain
查看
BUILD.gn文件入口
在.gn文件所在目录下的BUILD.gn就是入口,此文件做了以下几个事情
- 生成version-info文件,记录ohos-version以及ohos编译时间
- 通过
group
定义一组依赖,以生成最终的依赖树
可以通过gn help group
查看详细信息,如下
itsenlin@itsenlin-PC:~/code/ohos_3.0$ ~/gn/gn help group
group: Declare a named group of targets.
This target type allows you to create meta-targets that just collect a set of
dependencies into one named target. Groups can additionally specify configs
that apply to their dependents.
Variables
Deps: data_deps, deps, public_deps
Dependent configs: all_dependent_configs, public_configs
Example
group("all") {
deps = [
"//project:runner",
"//project:unit_tests",
]
}
itsenlin@itsenlin-PC:~/code/ohos_3.0$
这里重点说一下target为ohos
的group
如下:
group("ohos") {
deps = []
if (ohos_build_target == "") {
# Step 1: Read product configuration profile.
product_cfg = read_file("${product_path}/config.json", "json")
# Step 2: Loop subsystems configured by product.
foreach(product_configed_subsystem, product_cfg.subsystems) {
subsystem_name = product_configed_subsystem.subsystem
# Step 3: Read OS subsystems profile.
subsystem_info =
read_file("//build/lite/components/${subsystem_name}.json", "json")
# Step 4: Loop components configured by product.
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) {
component_found = true
}
}
# 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
}
}
# Step 6.1.2: Add valid component for compiling.
# Skip kernel target for userspace only scenario.
if (!ohos_build_userspace_only ||
(ohos_build_userspace_only && subsystem_name != "kernel" &&
subsystem_name != "vendor")) {
foreach(component_target, component.targets) {
deps += [ component_target ]
}
}
}
}
}
}
# Skip device target for userspace only scenario.
if (!ohos_build_userspace_only) {
# Step 7: Add device and product target by default.
deps += [ "${device_path}/../" ]
}
} else {
deps += string_split(ohos_build_target, "&&")
}
}
这里也比较好理解,里面就是读取一个配置文件,几级循环来处理配置文件中配置的内容。流程如下
- 读取开发板对应的config.json,从配置中可以看到ohos系统分为三级: board、subsystems、components,如下
{
"product_name": "ipcamera_hispark_taurus",
"ohos_version": "OpenHarmony 1.0",
"device_company": "hisilicon",
"board": "hispark_taurus",
"kernel_type": "liteos_a",
"kernel_version": "",
"subsystems": [
{
"subsystem": "aafwk",
"components": [
{ "component": "aafwk_lite", "features":[ "enable_ohos_appexecfwk_feature_ability = true" ] }
]
},
......
{
"subsystem": "kernel",
"components": [
{ "component": "liteos_a", "features":[] }
]
},
......
"third_party_dir": "//third_party",
"product_adapter_dir": "//vendor/hisilicon/hispark_taurus/hals"
}
- 对此board下的
subsystems
循环处理 - 读取
//build/lite/components/
目录下subsystem对应的配置文件,这里以kernel
为例,则读取的是//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",
"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"
]
}
},
{
"component": "liteos_m",
......
},
{
"component": "linux_hi3516dv300",
......
},
{
"component": "linux_4_1_9",
......
},
{
"component": "linux_hi3518ev300",
......
}
]
}
- 对subsystem下的
components
循环处理 - 做一些检查,然后将comonent里面定义的
targets
添加了group("ohos")
的依赖库中 - 上面所有subsystems处理完之后,将device的配置添加到默认依赖中
到此就将此board下各模块的依赖关系添加好了,然后GN
会将依赖树中所有的BUILD.gn生成对应的.ninja文件,并且在out的根目录下生成下面几个ninja的入口文件以及配置文件
itsenlin@itsenlin-PC:~/code/ohos_3.0/out/hispark_taurus/ipcamera_hispark_taurus$ tree -L 2
.
├── args.gn
├── build.log
├── build.ninja
├── build.ninja.d
├── config.gni
├── config.h
├── etc
│ └── version-info
├── kconfig_env.txt
├── kconfig_files.txt
├── NOTICE_FILE
│ ├── acts
│ ├── appexecfwk_services_lite
│ ├── bundle_daemon_lite
│ ├── kernel
│ └── tools
├── obj
│ ├── applications
│ ├── base
│ ├── build
│ ├── device
│ ├── drivers
│ ├── foundation
│ ├── kernel
│ ├── test
│ ├── third_party
│ ├── utils
│ └── vendor
├── test_info
│ ├── gen
│ └── module_list_files
└── toolchain.ninja
22 directories, 10 files
itsenlin@itsenlin-PC:~/code/ohos_3.0/out/hispark_taurus/ipcamera_hispark_taurus$
其中
config.h
、'config.gni'、'kconfig_env.txt'、'kconfig_files.txt'等 配置文件的生成原理可以参考liteos-a全局配置文件生成原理
网友评论