提交 2b2696b6 编写于 作者: X xxlight

docs:openharmony/docs仓下编译构建文档整理

Signed-off-by: Nxxlight <xiaoxiaoliang2@huawei.com>
Change-Id: Iacdef000e71b742a5a1c3a4896f73e71687a0932
上级 aca19f3d
# 常见问题
## 常见编译问题和解决方法
### 编译构建过程中,提示“usr/sbin/ninja: invalid option -- w”
- **现象描述:** 编译失败,提示“usr/sbin/ninja: invalid option -- w”。
- **可能原因:** 编译环境中ninja版本太低,不支持--w选项。
- **解决办法:** 卸载环境中ninja和gn,按照[获取工具](../../device-dev/get-code/gettools-ide.md)
### 编译构建过程中,提示“/usr/bin/ld: cannot find -lncurses”
- **现象描述:** 编译失败,提示“/usr/bin/ld: cannot find -lncurses”。
- **可能原因:** 编译环境ncurses库缺失。
- **解决办法:**
```shell
sudo apt-get install lib32ncurses5-dev
```
### 编译构建过程中,提示“line 77: mcopy: command not found”
- **现象描述:** 编译失败,提示“line 77: mcopy: command not found”。
- **可能原因:** 编译环境未安装mcopy。
- **解决办法:**
```shell
sudo apt-get install dosfstools mtools
```
### 编译构建过程中,提示“riscv32-unknown-elf-gcc: error trying to exec 'cc1': execvp: No such file or directory”
- **现象描述:** 编译失败,提示“riscv32-unknown-elf-gcc: error trying to exec 'cc1': execvp: No such file or directory”。
- **可能原因:** 当前用户对riscv编译器路径下的文件访问权限不够。
- **解决办法:** 查询gcc_riscv32所在目录。
```shell
which riscv32-unknown-elf-gcc
```
使用chmod命令修改目录权限为755。
### 编译构建过程中,提示“No module named 'Crypto'”
- **现象描述:** 编译失败,提示“No module named 'Crypto'”。
- **可能原因:** python3未安装Crypto。
- **解决办法:**
1. 查询Python版本号。
```shell
python3 --version
```
2. 需使用python3.9.2以上版本,然后安装pycryptodome。
```shell
sudo pip3 install pycryptodome
```
### 编译构建过程中,提示“xx.sh : xx unexpected operator”
- **现象描述:** 编译失败:“xx.sh [: xx unexpected operator”。
- **可能原因:** 编译环境shell不是bash。
- **解决办法:**
```shell
sudo rm -rf /bin/sh
sudo ln -s /bin/bash /bin/sh
```
# 编译构建指导
## 概述
OpenHarmony编译子系统是以GN和Ninja构建为基座,对构建和配置粒度进行部件化抽象、对内建模块进行功能增强、对业务模块进行功能扩展的系统,该系统提供以下基本功能:
- 以部件为最小粒度拼装产品和独立编译。
- 支持轻量、小型、标准三种系统的解决方案级版本构建,以及用于支撑应用开发者使用IDE开发的SDK开发套件的构建。
- 支持芯片解决方案厂商的灵活定制和独立编译。
### 适用范围
本指导适用于轻量、小型、标准三种系统。[芯片解决方案配置规则](subsys-build-chip_solution.md#芯片解决方案配置规则)[新增并编译芯片解决方案](subsys-build-chip_solution.md#新增并编译芯片解决方案)主要和轻量系统、小型系统相关,其他内容都是通用的。
### 基本概念及包含关系
在了解编译构建子系统的能力前,应了解如下基本概念:
- 平台:开发板和内核的组合,不同平台支持的子系统和部件不同。
- 产品:产品是包含一系列部件的集合,编译后产品的镜像包可以运行在不同的开发板上。
- 子系统:OpenHarmony整体遵从分层设计,从下向上依次为:内核层、系统服务层、框架层和应用层(详见[OpenHarmony技术架构](https://gitee.com/openharmony#技术架构))。系统功能按照“系统 > 子系统 > 部件”逐级展开,在多设备部署场景下,支持根据实际需求裁剪某些非必要的子系统或部件。子系统是一个逻辑概念,它具体由对应的部件构成。
- 部件:对子系统的进一步拆分,可复用的软件单元,它包含源码、配置文件、资源文件和编译脚本;能独立构建,以二进制方式集成,具备独立验证能力的二进制单元。需要注意的是下文中的芯片解决方案本质是一种特殊的部件。
- 模块:模块就是编译子系统的一个编译目标,部件也可以是编译目标。
- 特性:特性是部件用于体现不同产品之间的差异。
- GN:Generate Ninja的缩写,用于产生Ninja文件。
- Ninja:Ninja是一个专注于速度的小型构建系统。
- hb:OpenHarmony的命令行工具,用来执行编译命令。
基于以上概念,编译子系统通过配置来实现编译和打包,该子系统主要包括:模块、部件、子系统、产品。
**图1** 产品、子系统、部件和模块间关系
![产品子系统部件模块关系](figures/product_subsystem_component_module_relationships.png)
图1体现了编译子系统的各部分关系,主要体现为:
- 子系统是某个路径下所有部件的集合,一个部件只能属于一个子系统。
- 部件是模块的集合,一个模块只能归属于一个部件。
- 通过产品配置文件配置一个产品包含的部件列表,部件不同的产品配置可以复用。
- 部件可以在不同的产品中实现有差异,通过变体或者特性feature实现。
- 模块就是编译子系统的一个编译目标,部件也可以是编译目标。
### 运作机制
编译构建可以编译产品、部件和模块,但是不能编译子系统。编译构建流程如下图所示,主要分设置和编译两步:
**图2** 编译构建流程
![编译流程](figures/compilation_process.png)
1. hb set: 设置要编译的产品。
2. hb build: 编译产品、开发板或者部件。编译主要过程如下:
1. 读取编译配置:根据产品选择的开发板,读取开发板config.gni文件内容,主要包括编译工具链、编译链接命令和选项等。
2. 调用GN:调用gn gen命令,读取产品配置生成产品解决方案out目录和Ninja文件。
3. 调用Ninja:调用ninja -C out/board/product启动编译。
4. 系统镜像打包:将部件编译产物打包,设置文件属性和权限,制作文件系统镜像。
### 约束限制
编译环境目前主要支持Ubuntu18.04和Ubuntu20.04(Ubuntu22.04暂不支持)。
### 环境配置
安装编译所需的程序包。 安装命令:
- 安装方式一:使用脚本,在当前工程目录执行
```shell
./build/build_scripts/env_setup.sh
```
- 安装方式二:apt-get和pip3 install命令安装
```shell
apt-get update -y
apt-get install -y apt-utils binutils bison flex bc build-essential make mtd-utils gcc-arm-linux-gnueabi u-boot-tools python3.9.2 python3-pip git zip unzip curl wget gcc g++ ruby dosfstools mtools default-jre default-jdk scons python3-distutils perl openssl libssl-dev cpio git-lfs m4 ccache zlib1g-dev tar rsync liblz4-tool genext2fs binutils-dev device-tree-compiler e2fsprogs git-core gnupg gnutls-bin gperf lib32ncurses5-dev libffi-dev zlib* libelf-dev libx11-dev libgl1-mesa-dev lib32z1-dev xsltproc x11proto-core-dev libc6-dev-i386 libxml2-dev lib32z-dev libdwarf-dev
apt-get install -y grsync xxd libglib2.0-dev libpixman-1-dev kmod jfsutils reiserfsprogs xfsprogs squashfs-tools pcmciautils quota ppp libtinfo-dev libtinfo5 libncurses5 libncurses5-dev libncursesw5 libstdc++6 gcc-arm-none-eabi vim ssh locales doxygen
# python需要安装以下模块,repo文件在上一章节约束与限制的源码获取中得到。
chmod +x /usr/bin/repo
pip3 install --trusted-host https://repo.huaweicloud.com -i https://repo.huaweicloud.com/repository/pypi/simple requests setuptools pymongo kconfiglib pycryptodome ecdsa ohos-build pyyaml prompt_toolkit==1.0.14 redis json2html yagmail python-jenkins
pip3 install esdk-obs-python --trusted-host pypi.org
pip3 install six --upgrade --ignore-installed six
#还需要安装llvm,hc-gen,gcc_riscv32,Ninja,node-v14.15.4-linux-x64,GN,如果用户使用的shell环境不是bash或者zsh的配置,则需要配置以下环境变量:
# export PATH=/home/tools/llvm/bin:$PATH
# export PATH=/home/tools/hc-gen:$PATH
# export PATH=/home/tools/gcc_riscv32/bin:$PATH
# export PATH=/home/tools/ninja:$PATH
# export PATH=/home/tools/node-v12.20.0-linux-x64/bin:$PATH
# export PATH=/home/tools/gn:$PATH
# export PATH=~/.local/bin:$PATH
```
![icon-note.gif](public_sys-resources/icon-note.gif)**注意**:上述安装ohos-build的过程中会安装编译工具hb,但有时会出现hb安装不成功的情况,若安装不成功,则按照[hb安装](../../device-dev/quick-start/quickstart-lite-env-setup.md#安装hb)重新安装。
## 配置规则
为了实现芯片解决方案、产品解决方案与OpenHarmony是解耦的、可插拔的,子系统、产品、部件、芯片解决方案、模块和特性需遵循一定的规则,具体配置规则见如下链接:
- [产品配置规则](subsys-build-product.md#产品配置规则)
- [子系统配置规则](subsys-build-subsystem.md#子系统配置规则)
- [部件配置规则](subsys-build-component.md#部件配置规则)
- [模块配置规则](subsys-build-module.md#模块配置规则)
- [芯片解决方案配置规则](subsys-build-chip_solution.md#芯片解决方案配置规则)
- [特性配置规则](subsys-build-feature.md#特性配置规则)
## 编译构建使用指导
### 目录结构
```shell
/build # 编译构建主目录
├── __pycache__
├── build_scripts/ # 编译相关的python脚本
├── common/
├── config/ # 编译相关的配置项
├── core
│ ├── gn/ # 编译入口BUILD.gn配置
└── build_scripts/
├── docs
gn_helpers.py*
lite/ # hb和preloader入口
misc/
├── ohos # OpenHarmony编译打包流程配置
│ ├── kits # kits编译打包模板和处理流程
│ ├── ndk # ndk模板和处理流程
│ ├── notice # notice模板和处理流程
│ ├── packages # 版本打包模板和处理流程
│ ├── sa_profile # sa模板和处理流程
│ ├── sdk # sdk模板和处理流程,包括sdk中包含的模块配置
│ └── testfwk # 测试相关的处理
├── ohos.gni* # 汇总了常用的gni文件,方便各个模块一次性import
├── ohos_system.prop
├── ohos_var.gni*
├── prebuilts_download.sh*
├── print_python_deps.py*
├── scripts/
├── subsystem_config.json
├── subsystem_config_example.json
├── templates/ # c/c++编译模板定义
├── test.gni*
├── toolchain # 编译工具链配置
├── tools # 常用工具
├── version.gni
├── zip.py*
```
### 编译命令
首先,在源码根目录下执行prebuilts脚本进行预编译,安装编译器及二进制工具。
```shell
bash build/prebuilts_download.sh
```
接着,使用命令行方式或hb方式执行编译命令。
1.命令行方式
- 代码根目录下执行全量版本的编译命令:
```shell
./build.sh --product-name {product_name}
```
{product_name}为当前版本支持的平台。比如:hispark_taurus_standard等。
编译完成后,结果镜像保存在 out/{device_name}/packages/phone/images/ 目录下。
- 编译命令支持选项:./build.sh
```shell
-h, --help # 显示帮助信息并退出
--source-root-dir=SOURCE_ROOT_DIR # 指定路径
--product-name=PRODUCT_NAME # 指定产品名
--device-name=DEVICE_NAME # 指定装置名称
--target-cpu=TARGET_CPU # 指定cpu
--target-os=TARGET_OS # 指定操作系统
-T BUILD_TARGET, --build-target=BUILD_TARGET # 指定编译目标,可以指定多个
--gn-args=GN_ARGS # GN参数,支持指定多个
--ninja-args=NINJA_ARGS # Ninja参数,支持指定多个
-v, --verbose # 生成时显示所有命令行
--keep-ninja-going # 让Ninja持续到1000000个工作失败
--jobs=JOBS
--export-para=EXPORT_PARA
--build-only-gn # 只做GN解析,不运行Ninja
--ccache # 可选 如果使用ccache,需要本地安装ccache
--fast-rebuild # 快速重建,默认值为False
--log-level=LOG_LEVEL # 指定编译期间的日志级别','三个级别可选:debug, info and error,default='info'
--device-type=DEVICE_TYPE # 指定设备类型,默认值为'default'
--build-variant=BUILD_VARIANT # 指定设备操作模式,默认值为'user'
```
2.hb方式
hb是OpenHarmony的命令行工具,用来执行编译命令。以下对hb的常用命令进行说明。
**hb set**
设置要编译的产品
```shell
hb set -h
usage: hb set [-h] [-root [ROOT_PATH]] [-p]
optional arguments:
-h, --help show this help message and exit
-root [ROOT_PATH], --root_path [ROOT_PATH]
Set OHOS root path
-p, --product Set OHOS board and kernel
```
- hb set 后无参数,进入默认设置流程
- hb set -root dir可直接设置代码根目录
- hb set -p设置要编译的产品
**hb env**
查看当前设置信息
```shell
hb env
[OHOS INFO] root path: xxx
[OHOS INFO] board: hispark_taurus
[OHOS INFO] kernel: liteos
[OHOS INFO] product: ipcamera
[OHOS INFO] product path: xxx/vendor/hisilicon/ipcamera
[OHOS INFO] device path: xxx/device/hisilicon/hispark_taurus/sdk_linux_4.19
```
**hb build**
编译产品、部件、模块或芯片解决方案。
```shell
hb build -h
usage: hb build [-h] [-b BUILD_TYPE] [-c COMPILER] [-t [TEST [TEST ...]]] [-cpu TARGET_CPU] [--dmverity] [--tee]
[-p PRODUCT] [-f] [-n] [-T [TARGET [TARGET ...]]] [-v] [-shs] [--patch] [--compact-mode]
[--gn-args GN_ARGS] [--keep-ninja-going] [--build-only-gn] [--log-level LOG_LEVEL] [--fast-rebuild]
[--device-type DEVICE_TYPE] [--build-variant BUILD_VARIANT]
[component [component ...]]
positional arguments:
component name of the component, mini/small only
optional arguments:
-h, --help show this help message and exit
-b BUILD_TYPE, --build_type BUILD_TYPE
release or debug version, mini/small only
-c COMPILER, --compiler COMPILER
specify compiler, mini/small only
-t [TEST [TEST ...]], --test [TEST [TEST ...]]
compile test suit
-cpu TARGET_CPU, --target-cpu TARGET_CPU
select cpu
--dmverity enable dmverity
--tee Enable tee
-p PRODUCT, --product PRODUCT
build a specified product with {product_name}@{company}
-f, --full full code compilation
-n, --ndk compile ndk
-T [TARGET [TARGET ...]], --target [TARGET [TARGET ...]]
compile single target
-v, --verbose show all command lines while building
-shs, --sign_haps_by_server
sign haps by server
--patch apply product patch before compiling
--compact-mode compatible with standard build system set to false if we use build.sh as build entrance
--gn-args GN_ARGS specifies gn build arguments, eg: --gn-args="foo="bar" enable=true blah=7"
--keep-ninja-going keeps ninja going until 1000000 jobs fail
--build-only-gn only do gn parse, donot run ninja
--log-level LOG_LEVEL
specifies the log level during compilationyou can select three levels: debug, info and error
--fast-rebuild it will skip prepare, preloader, gn_gen steps so we can enable it only when there is no change
for gn related script
--device-type DEVICE_TYPE
specifies device type
--build-variant BUILD_VARIANT
specifies device operating mode
```
- hb build后无参数,会按照设置好的代码路径、产品进行编译,编译选项使用与之前保持一致。-f 选项将删除当前产品所有编译产品,等同于hb clean + hb build.
- hb build {component_name}:基于设置好的产品对应的单板、内核,单独编译部件(e.g.:hb build kv_store)。
- hb build -p ipcamera@hisilicon:免set编译产品,该命令可以跳过set步骤,直接编译产品。
- 在device/board/device_company下单独执行hb build会进入内核选择界面,选择完成后会根据当前路径的单板、选择的内核编译出仅包含内核、驱动的镜像。
**hb clean**
清除out目录对应产品的编译产物,仅保留args.gn、build.log。清除指定路径可输入路径参数:hb clean out/board/product,默认将清除当前hb set的产品对应out路径。
```shell
hb clean
usage: hb clean [-h] [out_path]
positional arguments:
out_path clean a specified path.
optional arguments:
-h, --help show this help message and exit
```
### 新增并编译不同配置
根据上一节的配置规则新增相应配置并进行编译,主要包含产品、部件、芯片解决方案和模块四个粒度。具体如下:
- [新增并编译产品](subsys-build-product.md#新增并编译产品)
- [新增并编译部件](subsys-build-component.md#新增并编译部件)
- [新增并编译模块](subsys-build-module.md#新增并编译模块)
- [新增并编译芯片解决方案](subsys-build-chip_solution.md#新增并编译芯片解决方案)
## 常见问题
- [常见编译问题和解决方法](subsys-build-FAQ.md#常见编译问题和解决方法)
## 参考信息
- [关于deps、external_deps的使用](subsys-build-reference.md#关于deps、external_deps的使用)
- [开源软件Notice收集策略说明](subsys-build-reference.md#开源软件notice收集策略说明)
- [加快本地编译的一些参数](subsys-build-reference.md#加快本地编译的一些参数)
- [查看NinjaTrace](subsys-build-reference.md#查看ninjatrace)
# 芯片解决方案
### 芯片解决方案配置规则
- 芯片解决方案是指基于某款开发板的完整解决方案,包含驱动、设备侧接口适配、开发板sdk等。
- 芯片解决方案是一个特殊的部件,源码路径规则为:**device/{开发板}/{芯片解决方案厂商}**
- 芯片解决方案部件会随产品选择的开发板默认编译。
- 芯片解决方案目录树规则如下:
```shell
device
└── board
└── company # 芯片解决方案厂商
└── hispark_aries # 开发板名称
├── BUILD.gn # 编译脚本
├── hals # OS南向接口适配
├── linux # 可选,linux内核版本
│ └── config.gni # linux版本编译配置
└── liteos_a # 可选,liteos内核版本
└── config.gni # liteos_a版本编译配置
```
![icon-note.gif](public_sys-resources/icon-note.gif)**注意**:config.gni为开发板编译相关的配置,编译时会采用该配置文件中的参数编译所有OS部件,编译阶段系统全局可见。
- config.gni的关键字段介绍如下:
```shell
kernel_type: 开发板使用的内核类型,例如:“liteos_a”, “liteos_m”, “linux”。
kernel_version: 开发使用的内核版本,例如:“4.19”。
board_cpu: 开发板CPU类型,例如:“cortex-a7”, “riscv32”。
board_arch: 开发芯片arch, 例如: “armv7-a”, “rv32imac”。
board_toolchain: 开发板自定义的编译工具链名称,例如:“gcc-arm-none-eabi”。若为空,则使用默认为ohos-clang。
board_toolchain_prefix:编译工具链前缀,例如:“gcc-arm-none-eabi”。
board_toolchain_type: 编译工具链类型,目前支持gcc和clang。例如:“gcc” ,“clang”。
board_cflags: 开发板配置的c文件编译选项。
board_cxx_flags: 开发板配置的cpp文件编译选项。
board_ld_flags: 开发板配置的链接选项。
```
### 新增并编译芯片解决方案
编译构建支持添加新的芯片解决方案厂商,具体步骤如下:
1. 创建芯片解决方案目录。 按照芯片解决方案配置规则创建目录,以芯片厂商realtek的“rtl8720“开发板为例, 在代码根目录执行:
```shell
mkdir -p device/board/realtek/rtl8720
```
2. 创建内核适配目录,并编写开发板编译配置config.gni文件。 以realtek的“rtl8720“开发板的liteos_a适配为例,device/board/realtek/rtl8720/liteo_a/config.gni的内容如下:
```shell
# Kernel type, e.g. "linux", "liteos_a", "liteos_m".
kernel_type = "liteos_a"
# Kernel version.
kernel_version = "3.0.0"
# Board CPU type, e.g. "cortex-a7", "riscv32".
board_cpu = "real-m300"
# Board arch, e.g. "armv7-a", "rv32imac".
board_arch = ""
# Toolchain name used for system compiling.
# E.g. gcc-arm-none-eabi, arm-linux-harmonyeabi-gcc, ohos-clang, riscv32-unknown-elf.
# Note: The default toolchain is "ohos-clang". It's not mandatory if you use the default toochain.
board_toolchain = "gcc-arm-none-eabi"
# The toolchain path instatlled, it's not mandatory if you have added toolchian path to your ~/.bashrc.
board_toolchain_path =
rebase_path("//prebuilts/gcc/linux-x86/arm/gcc-arm-none-eabi/bin",
root_build_dir)
# Compiler prefix.
board_toolchain_prefix = "gcc-arm-none-eabi-"
# Compiler type, "gcc" or "clang".
board_toolchain_type = "gcc"
# Board related common compile flags.
board_cflags = []
board_cxx_flags = []
board_ld_flags = []
```
3. 编写编译脚本。 在开发板目录下创建BUILD.gn,target名称应与开发板名称一致。以realtek的rtl8720开发板为例,device/board/realtek/rtl8720/BUILD.gn内容可以是:
```shell
group("rtl8720") { # target类型也可以shared_library, static_library, executable
# 具体内容
......
}
```
4. 编译芯片解决方案。 在开发板目录下执行hb build,即可启动芯片解决方案的编译。
# 部件
### 部件配置规则
部件的bundle.json放在部件源码的根目录下。以泛sensor子系统的sensor服务部件为例,部件属性定义描述文件字段说明如下:
```shell
{
"name": "@ohos/sensor_lite", # HPM部件英文名称,格式"@组织/部件名称"
"description": "Sensor services", # 部件功能一句话描述
"version": "3.1", # 版本号,版本号与OpenHarmony版本号一致
"license": "MIT", # 部件License
"publishAs": "code-segment", # HPM包的发布方式,当前默认都为code-segment
"segment": {
"destPath": ""
}, # 发布类型为code-segment时为必填项,定义发布类型code-segment的代码还原路径(源码路径)
"dirs": {"base/sensors/sensor_lite"}, # HPM包的目录结构,字段必填内容可以留空
"scripts": {}, # HPM包定义需要执行的脚本,字段必填,值非必填
"licensePath": "COPYING",
"readmePath": {
"en": "README.rst"
},
"component": { # 部件属性
"name": "sensor_lite", # 部件名称
"subsystem": "", # 部件所属子系统
"syscap": [], # 部件为应用提供的系统能力
"features": [], # 部件对外的可配置特性列表,一般与build中的sub_component对应,可供产品配置
"adapted_system_type": [], # 轻量(mini)小型(small)和标准(standard),可以是多个
"rom": "92KB", # 部件ROM值
"ram": "~200KB", # 部件RAM估值
"deps": {
"components": [ # 部件依赖的其他部件
"samgr_lite",
"ipc_lite"
],
"third_party": [ # 部件依赖的三方开源软件
"bounds_checking_function"
]
}
"build": { # 编译相关配置
"sub_component": [
""//base/sensors/sensor_lite/services:sensor_service"", # 部件编译入口
], # 部件编译入口,模块在此处配置
"inner_kits": [], # 部件间接口
"test": [] # 部件测试用例编译入口
}
}
}
```
> ![icon-note.gif](public_sys-resources/icon-note.gif)**注意**:lite上旧的部件在build/lite/components目录下对应子系统的json文件中,路径规则为:**{领域}/{子系统}/{部件}**,部件目录树规则如下:
```shell
component
├── interfaces
│ ├── innerkits # 系统内接口,部件间使用
│ └── kits # 应用接口,应用开发者使用
├── frameworks # framework实现
├── services # service实现
└── BUILD.gn # 部件编译脚本
```
部件配置中需要配置部件的名称、源码路径、功能简介、是否必选、编译目标、RAM、ROM、编译输出、已适配的内核、可配置的特性和依赖等属性定义。
新增部件时需要在对应子系统json文件中添加相应的部件定义。产品所配置的部件必须在某个子系统中被定义过,否则会校验失败。
### 新增并编译部件
1. 添加部件。 本节以添加一个自定义的部件为例,描述如何编译部件,编译库、编译可执行文件等。
示例部件partA由feature1、feature2和feature3组成,feature1的编译目标为一个动态库,feature2的目标为一个可执行程序,feature3的目标为一个etc配置文件。
示例部件partA的配置需要添加到一个子系统中,本次示例将添加到subsystem_examples子系统中(subsystem_examples子系统定义在test/examples/目录)。
示例部件partA的完整目录结构如下:
```shell
test/examples/partA
├── feature1
│ ├── BUILD.gn
│ ├── include
│ │ └── helloworld1.h
│ └── src
│ └── helloworld1.cpp
├── feature2
│ ├── BUILD.gn
│ ├── include
│ │ └── helloworld2.h
│ └── src
│ └── helloworld2.cpp
└── feature3
├── BUILD.gn
└── src
└── config.conf
```
示例1:编写动态库gn脚本test/examples/partA/feature1/BUILD.gn,示例如下:
```shell
config("helloworld_lib_config") {
include_dirs = [ "include" ]
}
ohos_shared_library("helloworld_lib") {
sources = [
"include/helloworld1.h",
"src/helloworld1.cpp",
]
public_configs = [ ":helloworld_lib_config" ]
part_name = "partA"
}
```
示例2:编写可执行文件gn脚本test/examples/partA/feature2/BUILD.gn,示例如下:
```shell
ohos_executable("helloworld_bin") {
sources = [
"src/helloworld2.cpp"
]
include_dirs = [ "include" ]
deps = [ # 依赖部件内模块
"../feature1:helloworld_lib"
]
external_deps = [ "partB:module1" ] # (可选)如果有跨部件的依赖,格式为“部件名:模块名”
install_enable = true # 可执行程序缺省不安装,需要安装时需要指定
part_name = "partA"
}
```
示例3:编写etc模块gn脚本test/examples/partA/feature3/BUILD.gn,示例如下:
```shell
ohos_prebuilt_etc("feature3_etc") {
source = "src/config.conf"
relative_install_dir = "init" #可选,模块安装相对路径,相对于默认安装路径;默认在/system/etc目录
part_name = "partA"
}
```
示例4:在部件的bundle.json中添加模块配置:test/examples/bundle.json。每个部件都有一个bundle.json配置文件,在部件的根目录下。示例见:[部件的bundle.json](subsys-build-component.md#部件配置规则)
2. 将部件添加到产品配置中。 在产品的配置中添加部件,产品对应的配置文件://vendor/{product_company}/{product-name}/config.json。
在产品配置文件中添加 "subsystem_examples:partA",表示该产品中会编译并打包partA到版本中。
3. 编译。 主要有两种编译方式,[命令行方式和hb方式](subsys-build-all.md#编译命令),下面以命令行方式为例:
部件可以使用"--build-target 部件名"进行单独编译,以编译产品hispark_taurus_standard的musl部件为例,编译命令如下:
```
./build.sh --product-name hispark_taurus_standard --build-target musl --ccache
```
也可以编译相应产品,以编译hispark_taurus_standard为例,编译命令如下:
```shell
./build.sh --product-name hispark_taurus_standard --ccache
```
4. 编译输出。 编译所生成的文件都归档在out/hispark_taurus/目录下,结果镜像输出在 out/hispark_taurus/packages/phone/images/ 目录下。
\ No newline at end of file
# 特性
### 特性配置规则
下面介绍feature的声明、定义以及使用方法。
- feature的声明
在部件的bundle.json文件中通过feature_list来声明部件的feature列表,每个feature都必须以"**{部件名}**"开头。示例如下:
```
{
"name": "@ohos/xxx",
"component": {
"name": "partName",
"subsystem": "subsystemName",
"features": [
"{partName}_feature_A"
]
}
}
```
features中可以为部件声明多个feature。
- feature的定义
在部件内可通过以下方式定义feature的默认值:
```
declare_args() {
{partName}_feature_A = true
}
```
该值是此部件的默认值,产品可以在部件列表中重载该feature的值。
feature需给部件内多个模块使用时,建议把feature定义在部件的全局gni文件中,各个模块的BUILD.gn中import该gni文件。
- feature的使用
BUILD.gn文件中可通过以下方式进行根据feature决定部分代码或模块参与编译:
```
if ({partName}_feature_A) {
sources += [ "xxx.c" ]
}
# 某个特性引入的依赖,需要通过该feature进行隔离
if ({partName}_feature_A) {
deps += [ "xxx" ]
external_deps += [ "xxx" ]
}
# bundle.json中不支持if判断,如果bundle.json中包含的sub_component需要被裁减,可以定义group进行裁减判断
group("testGroup") {
deps = []
if ({partName}_feature_A) {
deps += [ "xxx" ]
}
}
```
也可以通过以下方式为模块定义代码宏进行代码级差异化配置:
```
if ({partName}_feature_A) {
defines += ["FEATUREA_DEFINE"]
}
```
...@@ -7,12 +7,12 @@ gn是generate ninja的缩写,它是一个元编译系统(meta-build system ...@@ -7,12 +7,12 @@ gn是generate ninja的缩写,它是一个元编译系统(meta-build system
### gn简介 ### gn简介
- 目前采用gn的大型软件系统有:Chromium,Fuchsia和OpenHarmony。 - 目前采用gn的大型软件系统有:Chromium,Fuchsia和OpenHarmony。
- gn语法自设计之初就自带局限性,比如不能求list的长度,不支持通配符等。这些局限性源于其 **有所为有所不为**设计哲学,见https://gn.googlesource.com/gn/+/main/docs/language.md#Design-philosophy。 所以在使用gn的过程中,如果发现某件事情用gn实现起来很复杂,请先停下来思考这件事情是否真的需要做。 - gn语法自设计之初就自带局限性,比如不能求list的长度,不支持通配符等。这些局限性源于其 **有所为有所不为**[设计哲学](https://gn.googlesource.com/gn/+/main/docs/language.md#Design-philosophy)。 所以在使用gn的过程中,如果发现某件事情用gn实现起来很复杂,请先停下来思考这件事情是否真的需要做。
- 关于gn的更多详情见gn官方文档,见https://gn.googlesource.com/gn/+/main/docs/。 - 关于gn的更多详情见[gn官方文档](https://gn.googlesource.com/gn/+/main/docs/)
### 本文的目标读者和覆盖范围 ### 本文的目标读者和覆盖范围
目标读者为OpenHarmony的开发者。本文主要讨论gn的编码风格和使用gn过程中容易出现的问题,不讨论gn的语法,如需了解gn基础知识,见gn reference文档,见https://gn.googlesource.com/gn/+/main/docs/reference.md。 目标读者为OpenHarmony的开发者。本文主要讨论gn的编码风格和使用gn过程中容易出现的问题,不讨论gn的语法,如需了解gn基础知识,见[gn reference文档](https://gn.googlesource.com/gn/+/main/docs/reference.md)
### 总体原则 ### 总体原则
...@@ -30,7 +30,7 @@ gn是generate ninja的缩写,它是一个元编译系统(meta-build system ...@@ -30,7 +30,7 @@ gn是generate ninja的缩写,它是一个元编译系统(meta-build system
为了更好的区别于全局变量,局部变量统一采用**下划线开头** 为了更好的区别于全局变量,局部变量统一采用**下划线开头**
``` ```shell
# 例1 # 例1
action("some_action") { action("some_action") {
... ...
...@@ -53,7 +53,7 @@ action("some_action") { ...@@ -53,7 +53,7 @@ action("some_action") {
如果变量值可以被gn args修改,则需要使用declare_args来声明,否则不要使用declare_args。 如果变量值可以被gn args修改,则需要使用declare_args来声明,否则不要使用declare_args。
``` ```shell
# 例2 # 例2
declare_args() { declare_args() {
# 可以通过gn args来修改some_feature的值 # 可以通过gn args来修改some_feature的值
...@@ -71,7 +71,7 @@ declare_args() { ...@@ -71,7 +71,7 @@ declare_args() {
- 加入双下划线可以很方便地区分出子目标属于哪一个模块,方便在出现问题时快速定位。 - 加入双下划线可以很方便地区分出子目标属于哪一个模块,方便在出现问题时快速定位。
``` ```shell
# 例3 # 例3
template("ohos_shared_library") { template("ohos_shared_library") {
# "{target_name}"(主目标名)+"__"(双下划线)+"notice"(后缀) # "{target_name}"(主目标名)+"__"(双下划线)+"notice"(后缀)
...@@ -89,7 +89,7 @@ declare_args() { ...@@ -89,7 +89,7 @@ declare_args() {
推荐采用**动宾短语**的形式来命名。 推荐采用**动宾短语**的形式来命名。
``` ```shell
# 例4 # 例4
# Good # Good
template("compile_resources") { template("compile_resources") {
...@@ -109,7 +109,7 @@ gn format会按照字母序对import文件做排序,如果想保证import的 ...@@ -109,7 +109,7 @@ gn format会按照字母序对import文件做排序,如果想保证import的
假设原来的import顺序为: 假设原来的import顺序为:
``` ```shell
# 例5 # 例5
import("//b.gni") import("//b.gni")
import("//a.gni") import("//a.gni")
...@@ -117,14 +117,14 @@ import("//a.gni") ...@@ -117,14 +117,14 @@ import("//a.gni")
经过format之后变为: 经过format之后变为:
``` ```shell
import("//a.gni") import("//a.gni")
import("//b.gni") import("//b.gni")
``` ```
如果想保证原有的import顺序,可以添加空注释行。 如果想保证原有的import顺序,可以添加空注释行。
``` ```shell
import("//b.gni") import("//b.gni")
# Comment to keep import order # Comment to keep import order
import("//a.gni") import("//a.gni")
...@@ -148,7 +148,7 @@ import("//a.gni") ...@@ -148,7 +148,7 @@ import("//a.gni")
- **概率性编译错误** - **概率性编译错误**
``` ```shell
# 例6 # 例6
# 依赖关系缺失,导致概率性编译出错 # 依赖关系缺失,导致概率性编译出错
shared_library("a") { shared_library("a") {
...@@ -177,7 +177,7 @@ import("//a.gni") ...@@ -177,7 +177,7 @@ import("//a.gni")
_compile_js_target不需要依赖 _compile_resource_target,增加这层依赖,会导致 _compile_js_target在 _compile_resource_target编译完成之后才能开始编译。 _compile_js_target不需要依赖 _compile_resource_target,增加这层依赖,会导致 _compile_js_target在 _compile_resource_target编译完成之后才能开始编译。
``` ```shell
# 例7 # 例7
# 过多的依赖导致编译变慢 # 过多的依赖导致编译变慢
template("too_much_deps") { template("too_much_deps") {
...@@ -208,7 +208,7 @@ template("too_much_deps") { ...@@ -208,7 +208,7 @@ template("too_much_deps") {
下面的例子中,foo.py引用了bar.py中的函数。bar.py实质上是foo.py的输入,需要将bar.py添加到implict_input_action的input或者depfile中去。否则,修改bar.py,模块implict_input_action将不会重新编译。 下面的例子中,foo.py引用了bar.py中的函数。bar.py实质上是foo.py的输入,需要将bar.py添加到implict_input_action的input或者depfile中去。否则,修改bar.py,模块implict_input_action将不会重新编译。
``` ```shell
# 例8 # 例8
action("implict_input_action") { action("implict_input_action") {
script = "//path-to-foo.py" script = "//path-to-foo.py"
...@@ -216,7 +216,7 @@ action("implict_input_action") { ...@@ -216,7 +216,7 @@ action("implict_input_action") {
} }
``` ```
``` ```shell
#!/usr/bin/env #!/usr/bin/env
# Contents of foo.py # Contents of foo.py
import bar import bar
...@@ -232,7 +232,7 @@ bar.some_function() ...@@ -232,7 +232,7 @@ bar.some_function()
下面的例子中,foo.py会生成两个文件,a.out和b.out,但是implict_output_action的输出只声明了a.out。这种情况下,b.out实质上就是一个隐式输出。缓存中只会存储a.out,不会存储b.out,当缓存命中时,b.out就编译不出来了。 下面的例子中,foo.py会生成两个文件,a.out和b.out,但是implict_output_action的输出只声明了a.out。这种情况下,b.out实质上就是一个隐式输出。缓存中只会存储a.out,不会存储b.out,当缓存命中时,b.out就编译不出来了。
``` ```shell
# 例9 # 例9
action("implict_output_action") { action("implict_output_action") {
outputs = ["${target_out_dir}/a.out"] outputs = ["${target_out_dir}/a.out"]
...@@ -241,7 +241,7 @@ action("implict_output_action") { ...@@ -241,7 +241,7 @@ action("implict_output_action") {
} }
``` ```
``` ```shell
#!/usr/bin/env #!/usr/bin/env
# Contents of foo.py # Contents of foo.py
... ...
...@@ -252,7 +252,7 @@ write_file("a.out") ...@@ -252,7 +252,7 @@ write_file("a.out")
### 模板 ### 模板
**不要使用gn的原生模板,使用编译系统提供的模板** **不要使用gn的原生模板,使用编译子系统提供的模板**
所谓gn原生模板,是指source_set,shared_library, static_library, action, executable,group这六个模板。 所谓gn原生模板,是指source_set,shared_library, static_library, action, executable,group这六个模板。
...@@ -262,10 +262,10 @@ write_file("a.out") ...@@ -262,10 +262,10 @@ write_file("a.out")
- 当输入文件依赖的文件发生变化时,gn原生的action模板不能自动感知不到这种编译,无法重新编译。见例8 - 当输入文件依赖的文件发生变化时,gn原生的action模板不能自动感知不到这种编译,无法重新编译。见例8
原生模板和编译系统提供的模板之间的对应关系: 原生模板和编译子系统提供的模板之间的对应关系:
| 编译系统提供的模板 | 原生模板 | | 编译子系统提供的模板 | 原生模板 |
|:------------------- | -------------- | | :------------------ | -------------- |
| ohos_shared_library | shared_library | | ohos_shared_library | shared_library |
| ohos_source_set | source_set | | ohos_source_set | source_set |
| ohos_executable | executable | | ohos_executable | executable |
...@@ -286,7 +286,7 @@ action中的script推荐使用python脚本,不推荐使用shell脚本。相比 ...@@ -286,7 +286,7 @@ action中的script推荐使用python脚本,不推荐使用shell脚本。相比
- 仅在向action的参数列表中(args)调用rebase_path。 - 仅在向action的参数列表中(args)调用rebase_path。
``` ```shell
# 例10 # 例10
template("foo") { template("foo") {
action(target_name) { action(target_name) {
...@@ -308,7 +308,7 @@ action中的script推荐使用python脚本,不推荐使用shell脚本。相比 ...@@ -308,7 +308,7 @@ action中的script推荐使用python脚本,不推荐使用shell脚本。相比
- 同一变量做两次rebase_path会出现意想不到的结果。 - 同一变量做两次rebase_path会出现意想不到的结果。
``` ```shell
# 例11 # 例11
template("foo") { template("foo") {
action(target_name) { action(target_name) {
...@@ -339,7 +339,7 @@ action中的script推荐使用python脚本,不推荐使用shell脚本。相比 ...@@ -339,7 +339,7 @@ action中的script推荐使用python脚本,不推荐使用shell脚本。相比
下面的例子中,模块a的输出是模块b的输入,可以通过定义全局变量的方式来共享给b 下面的例子中,模块a的输出是模块b的输入,可以通过定义全局变量的方式来共享给b
``` ```shell
# 例12 # 例12
_output_a = get_label_info(":a", "out_dir") + "/a.out" _output_a = get_label_info(":a", "out_dir") + "/a.out"
action("a") { action("a") {
...@@ -360,7 +360,7 @@ action中的script推荐使用python脚本,不推荐使用shell脚本。相比 ...@@ -360,7 +360,7 @@ action中的script推荐使用python脚本,不推荐使用shell脚本。相比
- 自定义模板需要首先将testonly传递(forward)进来。因为该模板的target有可能被testonly的目标依赖。 - 自定义模板需要首先将testonly传递(forward)进来。因为该模板的target有可能被testonly的目标依赖。
``` ```shell
# 例13 # 例13
# 自定义模板首先要传递testonly # 自定义模板首先要传递testonly
template("foo") { template("foo") {
...@@ -371,7 +371,7 @@ action中的script推荐使用python脚本,不推荐使用shell脚本。相比 ...@@ -371,7 +371,7 @@ action中的script推荐使用python脚本,不推荐使用shell脚本。相比
- 不推荐使用*来forward变量,需要的变量应该**显式地,一个一个地**被forward进来。 - 不推荐使用*来forward变量,需要的变量应该**显式地,一个一个地**被forward进来。
``` ```shell
# 例14 # 例14
# Bad,使用*forward变量 # Bad,使用*forward变量
template("foo") { template("foo") {
...@@ -395,7 +395,7 @@ action中的script推荐使用python脚本,不推荐使用shell脚本。相比 ...@@ -395,7 +395,7 @@ action中的script推荐使用python脚本,不推荐使用shell脚本。相比
target_name会随着作用域变化而变化,使用时需要注意。 target_name会随着作用域变化而变化,使用时需要注意。
``` ```shell
# 例15 # 例15
# target_name会随着作用域变化而变化 # target_name会随着作用域变化而变化
template("foo") { template("foo") {
...@@ -421,7 +421,7 @@ template("foo") { ...@@ -421,7 +421,7 @@ template("foo") {
如果模块需要向外export头文件,请使用public_configs。 如果模块需要向外export头文件,请使用public_configs。
``` ```shell
# 例16 # 例16
# b依赖a,会同时继承a的headers # b依赖a,会同时继承a的headers
config("headers") { config("headers") {
...@@ -442,7 +442,7 @@ executable("b") { ...@@ -442,7 +442,7 @@ executable("b") {
自定义模板中必须有一个子目标的名字是target_name。该子目标会作为template的主目标。其他子目标都应该被主目标依赖,否则子目标不会被编译。 自定义模板中必须有一个子目标的名字是target_name。该子目标会作为template的主目标。其他子目标都应该被主目标依赖,否则子目标不会被编译。
``` ```shell
# 例17 # 例17
# 自定义模板中必须有一个子目标的名字是target_name # 自定义模板中必须有一个子目标的名字是target_name
template("foo") { template("foo") {
...@@ -470,7 +470,7 @@ template("foo") { ...@@ -470,7 +470,7 @@ template("foo") {
set_source_assignment_filter除了可以过滤sources,还可以用来过滤其他变量。过滤完成后记得将过滤器和sources置空。 set_source_assignment_filter除了可以过滤sources,还可以用来过滤其他变量。过滤完成后记得将过滤器和sources置空。
``` ```shell
# 例18 # 例18
# 使用set_source_assignment_filter过滤依赖, 挑选label符合*:*_res的添加到依赖列表中 # 使用set_source_assignment_filter过滤依赖, 挑选label符合*:*_res的添加到依赖列表中
_deps = [] _deps = []
...@@ -495,15 +495,15 @@ set_source_assignment_filter([]) ...@@ -495,15 +495,15 @@ set_source_assignment_filter([])
- 在模块定义的时候可以声明part_name,用来表明当前模块属于哪个部件。 - 在模块定义的时候可以声明part_name,用来表明当前模块属于哪个部件。
- 每个部件会声明其inner-kit,供其他部件调用。部件innerkit的声明见源码中的bundle.json。 - 每个部件会声明其inner_kits,供其他部件调用。部件inner_kits的声明见源码中的bundle.json。
- 部件间依赖只能依赖innerkit,不能依赖非innerkit的模块。 - 部件间依赖只能依赖inner_kits,不能依赖非inner_kits的模块。
- 如果a模块和b模块的part_name相同,那么a、b模块属于同一个部件,a,b模块之间的依赖关系可以用deps来声明。 - 如果a模块和b模块的part_name相同,那么a、b模块属于同一个部件,a,b模块之间的依赖关系可以用deps来声明。
- 如果a、b模块的part_name不同,那么a、b模块不属于同一个部件,a、b模块之间的依赖关系需要通过external_deps来声明,依赖方式为"部件名:模块名"的方式。见例19。 - 如果a、b模块的part_name不同,那么a、b模块不属于同一个部件,a、b模块之间的依赖关系需要通过external_deps来声明,依赖方式为"部件名:模块名"的方式。见例19。
``` ```shell
# 例19 # 例19
shared_library("a") { shared_library("a") {
... ...
......
...@@ -17,7 +17,7 @@ ...@@ -17,7 +17,7 @@
## 开发指导 ## 开发指导
### 编译系统提供的模板 ### 编译系统提供的模板
#### ohos_hap #### ohos_hap
声明一个HAP目标,该目标会生成一个HAP,最终将会打包到system镜像中。 声明一个HAP目标,该目标会生成一个HAP,最终将会打包到system镜像中。
......
# 模块
## 模块配置规则
编译子系统通过模块、部件和产品三层配置来实现编译和打包。模块就是编译子系统的一个目标,包括(动态库、静态库、配置文件、预编译模块等)。模块要定义属于哪个部件,一个模块只能归属于一个部件。Openharmony使用定制化的Gn模板来配置模块规则,Gn语法相关的基础知识请参考[官网手册](https://gn.googlesource.com/gn/+/main/docs/reference.md)
以下是常用的模块配置规则:
```
# C/C++模板
ohos_shared_library
ohos_static_library
ohos_executable
ohos_source_set
# 预编译模板:
ohos_prebuilt_executable
ohos_prebuilt_shared_library
ohos_prebuilt_static_library
#hap模板
ohos_hap
ohos_app_scope
ohos_js_assets
ohos_resources
#其他常用模板
#配置文件
ohos_prebuild_etc
#sa配置
ohos_sa_profile
```
ohos开头的模板与内建模板的差异主要在于:推荐使用ohos定制模板。
### C/C++模板示例
ohos开头的模板对应的.gni文件路径在:openharmony/build/templates/cxx/cxx.gni。
ohos_shared_library示例
```shell
import("//build/ohos.gni")
ohos_shared_library("helloworld") {
sources = ["file"]
include_dirs = [] # 如有重复头文件定义,优先使用前面路径头文件
cflags = [] # 如重复冲突定义,后面的参数优先生效,也就是该配置项中优先生效
cflags_c = []
cflags_cc = []
ldflags = [] # 如重复冲突定义,前面参数优先生效,也就是ohos_template中预制参数优先生效
configs = []
deps = [] # 部件内模块依赖
external_deps = [ # 跨部件模块依赖定义,
"part_name:module_name", # 定义格式为 "部件名:模块名称"
] # 这里依赖的模块必须是依赖的部件声明在inner_kits中的模块
output_name = [string] # 模块输出名
output_extension = [] # 模块名后缀
module_install_dir = [] # 缺省在/system/lib64或/system/lib下, 模块安装路径,模块安装路径,从system/,vendor/后开始指定
relative_install_dir = [] # 模块安装相对路径,相对于/system/lib64或/system/lib;如果有module_install_dir配置时,该配置不生效
part_name = [string] # 必选,所属部件名称
output_dir
# Sanitizer variables
cfi = [boolean]
scs = [boolean]
scudo = []
ubsan = []
boundary_sanitize = []
integer_overflow_sanitize = []
testonly = [boolean]
license_as_sources = []
license_file = [] # 后缀名是.txt的文件
remove_configs = []
no_default_deps = []
install_images = []
install_enable = [boolean]
symlink_target_name = []
version_script = []
use_exceptions = []
}
```
ohos_static_library示例
```shell
import("//build/ohos.gni")
ohos_static_library("helloworld") {
sources = ["file"] # 后缀名是.c的相关文件
include_dirs = ["dir"] # 包含目录
configs = [] # 配置
deps = [] # 部件内模块依赖
part_name = [string] # 部件名称
subsystem_name = [string] # 子系统名称
cflags = []
external_deps = [ # 跨部件模块依赖定义,
"part_name:module_name", # 定义格式为 "部件名:模块名称"
] # 这里依赖的模块必须是依赖的部件声明在inner_kits中的模块
lib_dirs = []
public_configs = []
# Sanitizer variables
cfi = [boolean]
scs = [boolean]
scudo = []
ubsan = []
boundary_sanitize = []
integer_overflow_sanitize = []
remove_configs = []
no_default_deps = []
license_file = [] # 后缀名是.txt的文件
license_as_sources = []
use_exceptions = []
}
```
ohos_executable示例
```shell
import("//build/ohos.gni")
ohos_executable("helloworld") {
configs = [] # 配置
part_name = [string] # 部件名称
subsystem_name = [string] # 子系统名称
deps = [] # 部件内模块依赖
external_deps = [ # 跨部件模块依赖定义,
"part_name:module_name", # 定义格式为 "部件名:模块名称"
] # 这里依赖的模块必须是依赖的部件声明在inner_kits中的模块
ohos_test = []
test_output_dir = []
# Sanitizer variables
cfi = [boolean]
scs = [boolean]
scudo = []
ubsan = []
boundary_sanitize = []
integer_overflow_sanitize = []
testonly = [boolean]
license_as_sources = []
license_file = [] # 后缀名是.txt的文件
remove_configs = []
static_link = []
install_images = []
module_install_dir = [] # 模块安装路径,从system/,vendor/后开始指定
relative_install_dir = []
symlink_target_name = []
output_dir = [directory] # 存放输出文件的目录
install_enable = [boolean]
version_script = []
use_exceptions = []
}
```
ohos_source_set示例
```shell
import("//build/ohos.gni")
ohos_source_set("helloworld") {
sources = ["file"] # 后缀名是.c的相关文件
include_dirs = [] # 包含目录
configs = [] # 配置
public = [] # .h类型头文件
defines = []
public_configs = []
part_name = [string] # 部件名称
subsystem_name = [string] # 子系统名称
deps = [] # 部件内模块依赖
external_deps = [ # 跨部件模块依赖定义,
"part_name:module_name", # 定义格式为 "部件名:模块名称"
] # 这里依赖的模块必须是依赖的部件声明在inner_kits中的模块
# Sanitizer variables
cfi = [boolean]
scs = [boolean]
scudo = []
ubsan = []
boundary_sanitize = []
integer_overflow_sanitize = []
testonly = [boolean]
license_as_sources = []
license_file = []
remove_configs = []
no_default_deps = []
license_file = [] # 后缀名是.txt的文件
license_as_sources = []
use_exceptions = []
}
```
![icon-note.gif](public_sys-resources/icon-note.gif)**注意**:只有sources和part_name是必选,其他都是可选的。
### 预编译模板示例
预编译模板的.gni相关文件路径在:openharmony/build/templates/cxx/prebuilt.gni。
ohos_prebuilt_executable示例
```shell
import("//build/ohos.gni")
ohos_prebuilt_executable("helloworld") {
sources = ["file"] # 源
output = []
install_enable = [boolean]
deps = [] # 部件内模块依赖
public_configs = []
subsystem_name = [string] # 子系统名
part_name = [string] # 部件名
testonly = [boolean]
visibility = []
install_images = []
module_install_dir = [] # 模块安装路径,从system/,vendor/后开始指定
relative_install_dir = [] # 模块安装相对路径,相对于system/etc;如果有module_install_dir配置时,该配置不生效
symlink_target_name = []
license_file = [] # 后缀名是.txt的文件
license_as_sources = []
}
```
ohos_prebuilt_shared_library示例
```shell
import("//build/ohos.gni")
ohos_prebuilt_shared_library("helloworld") {
sources = ["file"] # 一般是后缀为.so的文件
output = []
install_enable = [boolean]
deps = [] # 部件内模块依赖
public_configs = []
subsystem_name = [string] # 子系统名
part_name = [string] # 部件名
testonly = [boolean]
visibility = []
install_images = []
module_install_dir = [] # 模块安装路径,从system/,vendor/后开始指定
relative_install_dir = [] # 模块安装相对路径,相对于system/etc;如果有module_install_dir配置时,该配置不生效
symlink_target_name = [string]
license_file = [string] # 后缀名是.txt的文件
license_as_sources = []
}
```
ohos_prebuilt_static_library示例
```shell
import("//build/ohos.gni")
ohos_prebuilt_static_library("helloworld") {
sources = ["file"] # 一般是后缀为.so的文件
output = []
deps = [] # 部件内模块依赖
public_configs = []
subsystem_name = [string] # 子系统名
part_name = [string] # 部件名
testonly = [boolean]
visibility = []
license_file = [string] # 后缀名是.txt的文件
license_as_sources = []
}
```
![icon-note.gif](public_sys-resources/icon-note.gif)**注意**:只有sources和part_name是必选,其他都是可选的。
### Hap模板
hap模板详见:[ HAP编译构建指导](https://gitee.com/openharmony/docs/blob/master/zh-cn/device-dev/subsystems/subsys-build-gn-hap-compilation-guide.md)
### 其他常用模板
ohos_prebuilt_etc示例:
```shell
import("//build/ohos.gni")
ohos_prebuilt_etc("helloworld") {
# ohos_prebuilt_etc模板最常用属性:
sources = ["file"]
module_install_dir = [] # 模块安装路径,从system/,vendor/后开始指定
subsystem_name = [string] # 子系统名
part_name = [string] # 必选,所属部件名称
install_images = []
relative_install_dir = [] # 模块安装相对路径,相对于system/etc;如果有module_install_dir配置时,该配置不生效
# ohos_prebuilt_etc模板不常用属性:
deps = [] # 部件内模块依赖
testonly = [boolean]
visibility = []
public_configs = []
symlink_target_name = [string]
license_file = [string]
license_as_sources = []
}
```
ohos_sa_profile示例:
```shell
import("//build/ohos.gni")
ohos_sa_profile("helloworld") {
sources = [".xml"] # xml文件
part_name = [string] # 部件名
subsystem_name = [string] # 子系统名
}
```
![icon-note.gif](public_sys-resources/icon-note.gif)**注意**:只有sources和part_name是必选,其他都是可选的。
## 新增并编译模块
新建模块可以分为以下三种情况。主要的添加逻辑如下面的流程图所示,若没有子系统则需新建子系统并在该子系统的部件下添加模块,若没有部件则需新建部件并在其中添加模块,否则直接在原有部件中添加模块即可,需要注意的是芯片解决方案作为特殊部件是没有对应子系统的。
- 在原有部件中添加一个模块
- 新建部件并在其中添加模块
- 新建子系统并在该子系统的部件下添加模块
![模块添加流程](figures/module_addition_process.png)
**在原有部件中添加一个模块**
1. 在模块目录下配置BUILD.gn,根据模板类型选择对应的gn模板。
2. 修改bundle.json配置文件。
```shell
{
"name": "@ohos/<component_name>", # HPM部件英文名称,格式"@组织/部件名称"
"description": "xxxxxxxxxxxxxxxxxxx", # 部件功能一句话描述
"version": "3.1", # 版本号,版本号与OpenHarmony版本号一致
"license": "MIT", # 部件License
"publishAs": "code-segment", # HPM包的发布方式,当前默认都为code-segment
"segment": {
"destPath": "third_party/nghttp2"
}, # 发布类型为code-segment时为必填项,定义发布类型code-segment的代码还原路径(源码路径)
"dirs": {}, # HPM包的目录结构,字段必填内容可以留空
"scripts": {}, # HPM包定义需要执行的脚本,字段必填,值非必填
"licensePath": "COPYING",
"readmePath": {
"en": "README.rst"
},
"component": { # 部件属性
"name": "<component_name>", # 部件名称
"subsystem": , # 部件所属子系统
"syscap": [], # 部件为应用提供的系统能力
"features": [], # 部件对外的可配置特性列表,一般与build中的sub_component对应,可供产品配置
"adapted_system_type": [], # 轻量(mini)小型(small)和标准(standard),可以是多个
"rom": "xxxKB" # ROM基线,没有基线写当前值
"ram": "xxxKB", # RAM基线,没有基线写当前值
"deps": {
"components": [], # 部件依赖的其他部件
"third_party": [] # 部件依赖的三方开源软件
},
"build": { # 编译相关配置
"sub_component": [
"//foundation/arkui/napi:napi_packages", # 原有模块1
"//foundation/arkui/napi:napi_packages_ndk" # 原有模块2
"//foundation/arkui/napi:new" # 新增模块new
], # 部件编译入口,模块在此处配置
"inner_kits": [], # 部件间接口
"test": [] # 部件测试用例编译入口
}
}
}
```
![icon-note.gif](public_sys-resources/icon-note.gif)**注意**:无论哪种方式该bundle.json文件均在对应子系统所在文件夹下。
3. 成功添加验证:编译完成后打包到image中去,生成对应的so文件或者二进制文件。
**新建部件并在其中添加一个模块**
1. 在模块目录下配置BUILD.gn,根据模板类型选择对应的gn模板。这一步与在原有部件中添加一个模块的方法基本一致,只需注意该模块对应BUILD.gn文件中的part_name为新建部件的名称即可。
2. 新建一个bundle.json文件,bundle.json文件均在对应子系统所在文件夹下。
3. 在vendor/{product_company}/{product-name}/config.json中添加对应的部件,直接添加到原有部件后即可。
```shell
"subsystems": [
{
"subsystem": "部件所属子系统名",
"components": [
{ "component": "部件名1", "features":[] }, # 子系统下的原有部件1
{ "component": "部件名2", "features":[] }, # 子系统下的原有部件2
{ "component": "部件名new", "features":[] } # 子系统下的新增部件new
]
},
.
]
```
4. 成功添加验证:编译完成后打包到image中去,生成对应的so文件或者二进制文件。
**新建子系统并在该子系统的部件下添加模块**
1. 在模块目录下配置BUILD.gn,根据模板类型选择对应的gn模板。这一步与新建部件并在其中添加模块中对应的步骤并无区别。
2. 在新建的子系统目录下每个部件对应的文件夹下创建bundle.json文件,定义部件信息。这一步与新建部件并在其中添加模块中对应的步骤并无区别。
3. 修改build目录下的subsystem_config.json文件。
```shell
{
"子系统名1": { # 原有子系统1
"path": "子系统目录1",
"name": "子系统名1"
},
"子系统名2": { # 原有子系统2
"path": "子系统目录2",
"name": "子系统名2"
},
"子系统名new": { # 新增子系统new
"path": "子系统目录new",
"name": "子系统名new"
},
}
```
该文件定义了有哪些子系统以及这些子系统所在文件夹路径,添加子系统时需要说明子系统path与name,分别表示子系统路径和子系统名。
4. 在vendor/{product_company}/{product-name}目录下的产品配置如product-name是hispark_taurus_standard时,在config.json中添加对应的部件,直接添加到原有部件后即可。
```shell
"subsystems": [
{
"subsystem": "arkui", # 原有的子系统名
"components": [ # 单个子系统下的所有部件集合
{
"component": "ace_engine_standard", # 原有的部件名
"features": []
},
{
"component": "napi", # 原有的部件名
"features": []
}
{
"component": "component_new1", # 原有子系统新增的的部件名component_new1
"features": []
}
]
},
{
"subsystem": "subsystem_new", # 新增的子系统名
"components": [
{
"component": "component_new2", # 新增子系统新增的的部件名component_new2
"features": []
}
]
},
]
```
4. 成功添加验证:编译完成后打包到image中去,生成对应的so文件或者二进制文件。
**编译模块**
主要有两种编译方式,[命令行方式和hb方式](subsys-build-all.md#编译命令),这里以命令行方式为例。
模块可以使用“--build-target 模块名"单独编译,编译命令如下:
```shell
./build.sh --build-target 模块名
```
也可以编译相应产品,以编译hispark_taurus_standard为例,编译命令如下:
```shell
./build.sh --product-name hispark_taurus_standard --build-target 模块名 --ccache
```
还可以编译模块所在的部件:
```shell
./build.sh --product-name hispark_taurus_standard --build-target musl --build-target 模块名 --ccache
```
\ No newline at end of file
# 产品
### 产品配置规则
产品解决方案为基于开发板的完整产品,主要包含产品对OS的适配、部件拼装配置、启动配置和文件系统配置等。产品解决方案的源码路径规则为:**vendor/{产品解决方案厂商}/{产品名称}**_。
产品解决方案的目录树规则如下:
```shell
vendor
└── company # 产品解决方案厂商
├── product # 产品名称
│ ├── init_configs
│ │ ├── etc # init进程启动配置(可选,仅linux内核需要)
│ │ └── init.cfg # 系统服务启动配置
│ ├── hals # 产品解决方案OS适配
│ ├── BUILD.gn # 产品编译脚本
│ └── config.json # 产品配置文件
│ └── fs.yml # 文件系统打包配置
└── ......
```
> ![icon-note.gif](public_sys-resources/icon-note.gif)**注意**:新增产品须按如上的规则创建目录和文件,编译构建系统将按该规则扫描已配置的产品。
关键的目录和文件详细介绍如下:
1. **vendor/company/product/init_configs/etc** 该文件夹中包含rcS脚本,Sxxx脚本和fstab脚本。init进程在启动系统服务之前执行这些脚本。执行的流程为“rcS->fstab->S00-xxx“。Sxxx脚本中的内容与开发板和产品需要有关,主要包括设备节点的创建、创建目录、扫描设备节点、修改文件权限等等。这些文件在产品编译的BUILD.gn中按需拷贝到产品out目录中,最终打包到rootfs镜像中。
2. **vendor/company/product/init_configs/init.cfg** init进程启动服务的配置文件,当前支持解析的命令有:
- start: 启动某个服务
- mkdir: 创建文件夹
- chmod: 修改指定路径/文件的权限
- chown: 修改指定路径/文件的属组
- mount: 挂载命令
该文件中的各个字段的解释如下:
```shell
{
"jobs" : [{ # job数组,一个job对应一个命令集合。job的执行顺序:pre-init -> init -> post-init。
"name" : "pre-init",
"cmds" : [
"mkdir /storage/data", # 创建目录
"chmod 0755 /storage/data", # 修改权限,权限值的格式为0xxx, 如0755
"mkdir /storage/data/log",
"chmod 0755 /storage/data/log",
"chown 4 4 /storage/data/log", # 修改属组,第一个数字为uid, 第二个数字为gid
......
"mount vfat /dev/mmcblock0 /sdcard rw,umask=000" # 挂载,格式为: mount [文件系统类型] [source] [target] [flags] [data]
# 其中flags仅支持:nodev、noexec、nosuid和rdonly
]
}, {
"name" : "init",
"cmds" : [ # 按cmds数组顺序启动启动服务
"start shell", # 注意:start与服务名称之间有且只有一个空格
......
"start service1"
]
}, {
"name" : "post-init", # 最后执行的job, init进程启动完成后的处理(如驱动初始化后再mount设备)
"cmds" : []
}
],
"services" : [{ # service数组,一个service对应一个进程
"name" : "shell", # 服务名称
"path" : ["/sbin/getty", "-n", "-l", "/bin/sh", "-L", "115200", "ttyS000", "vt100"], # 可执行文件全路径,path必须为第一个元素
"uid" : 0, # 进程的uid,须与二进制文件的uid保持一致
"gid" : 0, # 进程的gid,须与二进制文件的gid保持一致
"once" : 0, # 是否为一次性进程,1:进程退出后,init不在重新拉起。0:常驻进程,进程若退出,init将重新拉起
"importance" : 0, # 是否为关键进程,1:是关键进程,若进程退出,init将会重启单板。0:非关键进程,若进程退出,init不会重启单板
"caps" : [4294967295]
},
......
]
}
```
3. **vendor/company/product/init_configs/hals** 解决方案厂商对OS的适配,需要实现的接口请见各个部件的readme说明文档。
4. **vendor/company/product/config.json** config.json为编译构建的主入口,包含了开发板、OS部件和内核等配置信息。
以基于hispark_taurus开发板的ipcamera产品为例,配置文件如下:
```shell
{
"product_name": "ipcamera", # 产品名称
"version": "3.0", # config.json的版本号, 固定"3.0"
"type": "small", # 系统类型, 可选[mini, small, standard]
"ohos_version": "OpenHarmony 1.0", # 选择的OS版本
"device_company": "hisilicon", # 芯片厂商
"board": "hispark_taurus", # 开发板名称
"kernel_type": "liteos_a", # 选择的内核类型
"kernel_version": "3.0.0", # 选择的内核版本
"subsystems": [
{
"subsystem": "aafwk", # 选择的子系统
"components": [
{ "component": "ability", "features":[ "enable_ohos_appexecfwk_feature_ability = true" ] } # 选择的部件和部件特性配置
]
},
{
......
}
......
更多子系统和部件
}
}
```
5. **vendor/company/product/fs.yml** 该文件用于配置文件系统镜像制作过程,将编译产物打包成文件系统镜像,比如用户态根文件系统rootfs.img和可读写的userfs.img。它由多个列表组成,每个列表对应一个文件系统。字段说明如下:
```shell
fs_dir_name: 必填,声明文件系统文件名, 如rootfs、userfs
fs_dirs: 选填,配置out下文件目录与文件系统文件目录的映射关系,每个文件目录对应一个列表
source_dir: 选填,out下目标文件目录,若缺失则将根据target_dir在文件系统下创建空目录
target_dir: 必填,文件系统下对应文件目录
ignore_files:选填,声明拷贝忽略文件
dir_mode: 选填,文件目录权限,默认755
file_mode: 选填,该文件目录下所有文件的权限,默认555
fs_filemode: 选填,配置需要特殊声明权限的文件,每个文件对应一个列表
file_dir: 必填,文件系统下具体文件路径
file_mode: 必填,文件权限声明
fs_symlink: 选填,配置文件系统软连接
fs_make_cmd: 必填,配置需要制作文件系统脚本,OS提供的脚本在build/lite/make_rootfs下, 支持linux,liteos内核和ext4、jffs2、vfat格式。也支持芯片解决方案厂商自定义。
fs_attr: 选填,根据配置项动态调整文件系统
```
其中fs_symlink、fs_make_cmd字段支持以下变量:
- rootpath代码根目录,对应gn的{ohos_root_path}
- outpath产品out目录,对应gn的{root_out_dir}
- ${fs_dir} 文件系统目录,由以下变量拼接而成
- ${root_path}
- ${fs_dir_name}
> ![icon-note.gif](public_sys-resources/icon-note.gif)**注意**:fs.yml是可选的,对于没有文件系统的设备可不配置。
6. **vendor/company/product/BUILD.gn** 产品编译的入口,主要用于编译解决方案厂商源码和拷贝启动配置文件。如果某个产品被选择为要编译的产品,那么对应产品目录下的BUILD.gn会默认编译。一个典型的产品编译BUILD.gn应该如下:
```shell
group("product") { # target名称需与product名称即三级目录名称一致
deps = []
deps += [ "init_configs" ] # 拷贝init配置
...... # 其他
}
```
### 新增并编译产品
编译构建支持芯片解决方案和部件的灵活拼装,形成定制化的产品解决方案。具体步骤如下:
1. 创建产品目录 按照产品配置规则创建产品目录,以基于“rtl8720“开发板的wifiiot模组为例,在代码根目录执行:
```shell
mkdir -p vendor/my_company/wifiiot
```
2. 拼装产品 在新建的产品目录下新建config.json文件,以步骤1中的wifiiot为例,vendor/my_company/wifiiot/config.json可以是:
```shell
{
"product_name": "wifiiot", # 产品名称
"version": "3.0", # config.json的版本号, 固定"3.0"
"type": "small", # 系统类型, 可选[mini, small, standard]
"ohos_version": "OpenHarmony 1.0", # 使用的OS版本
"device_company": "realtek", # 芯片解决方案厂商名称
"board": "rtl8720", # 开发板名称
"kernel_type": "liteos_m", # 选择的内核类型
"kernel_version": "3.0.0", # 选择的内核版本
"subsystems": [
{
"subsystem": "kernel", # 选择的子系统
"components": [
{ "component": "liteos_m", "features":[] } # 选择的部件和部件特性
]
},
...
{
更多子系统和部件
}
]
}
```
![icon-note.gif](public_sys-resources/icon-note.gif)**注意**:编译构建系统编译前会对device_company,board,kernel_type,kernel_version、subsystem、component字段进行有效性检查,其中device_company,board,kernel_type,kernel_version应与已知的芯片解决方案匹配,subsystem、component应与build/lite/components下的部件描述匹配。
3. 适配OS接口 在产品目录下创建hals目录,并将产品解决方案对OS适配的源码和编译脚本放入该目录下。
4. 配置系统服务 在产品目录下创建init_configs目录,并在init_configs目录下创建init.cfg文件,按需配置要启动的系统服务。
5. 配置init进程(仅linux内核需要) 在init_configs目录下创建etc目录,然后在etc下创建init.d文件夹和fstab文件。最后按产品需求在init.d文件下创建并编辑rcS文件和Sxxx文件。
6. 配置文件系统镜像(可选,仅支持文件系统的开发板需要) 在产品目录下创建fs.yml文件。fs.yml需按产品实际情况配置,一个典型的fs.yml文件如下:
```shell
-
fs_dir_name: rootfs # 镜像的名称
fs_dirs:
-
# 将编译生成的out/my_board/my_product/bin目录下的文件拷贝到rootfs/bin中,并忽略测试bin
source_dir: bin
target_dir: bin
ignore_files:
- Test.bin
- TestSuite.bin
-
# 将编译生成的out/my_board/my_product/libs目录下的文件拷贝到rootfs/lib中,忽略所有.a文件,并设置文件和文件夹的权限为644和755
source_dir: libs
target_dir: lib
ignore_files:
- .a
dir_mode: 755
file_mode: 644
-
source_dir: usr/lib
target_dir: usr/lib
ignore_files:
- .a
dir_mode: 755
file_mode: 644
-
source_dir: config
target_dir: etc
-
source_dir: system
target_dir: system
-
source_dir: sbin
target_dir: sbin
-
source_dir: usr/bin
target_dir: usr/bin
-
source_dir: usr/sbin
target_dir: usr/sbin
-
# 创建一个proc空目录
target_dir: proc
-
target_dir: mnt
-
target_dir: opt
-
target_dir: tmp
-
target_dir: var
-
target_dir: sys
-
source_dir: etc
target_dir: etc
-
source_dir: vendor
target_dir: vendor
-
target_dir: storage
fs_filemode:
-
file_dir: lib/ld-uClibc-0.9.33.2.so
file_mode: 555
-
file_dir: lib/ld-2.24.so
file_mode: 555
-
file_dir: etc/init.cfg
file_mode: 400
fs_symlink:
-
# 在rootfs/lib下创建软连接ld-musl-arm.so.1 -> libc.so
source: libc.so
link_name: ${fs_dir}/lib/ld-musl-arm.so.1
-
source: mksh
link_name: ${fs_dir}/bin/sh
-
source: mksh
link_name: ${fs_dir}/bin/shell
fs_make_cmd:
# 使用脚本将rootfs制作为ext4格式的image
- ${root_path}/build/lite/make_rootfs/rootfsimg_linux.sh ${fs_dir} ext4
-
fs_dir_name: userfs
fs_dirs:
-
source_dir: storage/etc
target_dir: etc
-
source_dir: data
target_dir: data
fs_make_cmd:
- ${root_path}/build/lite/make_rootfs/rootfsimg_linux.sh ${fs_dir} ext4
```
7. 配置产品Patch(可选,视产品涉及部件是否需要打补丁而定) 在产品目录下创建patch.yml文件。patch.yml需按产品实际情况配置,一个典型的patch.yml文件如下:
```shell
# 需要打patch的路径
foundation/communication/dsoftbus:
# 该路径下需要打的patch存放路径
- foundation/communication/dsoftbus/1.patch
- foundation/communication/dsoftbus/2.patch
third_party/wpa_supplicant:
- third_party/wpa_supplicant/1.patch
- third_party/wpa_supplicant/2.patch
- third_party/wpa_supplicant/3.patch
...
```
配置完成后,编译时增加--patch参数,即可在产品编译前将配置的Patch文件打到对应目录中,再进行编译:
```shell
hb build -f --patch
```
8. 编写编译脚本 在产品目录下创建BUILD.gn文件,按产品实际情况编写脚本。以步骤1中的wifiiot为例,BUILD.gn示例如下:
```shell
group("wifiiot") { # target名称与产品名一致
deps = []
deps += [ "init_configs" ] # 拷贝init配置
deps += [ "hals" ] # 将hals加入编译
...... # 其他
}
```
9. 编译产品。 主要有两种编译方式,[命令行方式和hb方式](subsys-build-all.md#编译命令),这里以命令行方式为例,假设编译的产品名是hispark_taurus_standard,则编译命令是:
```
./build.sh --product-name hispark_taurus_standard --ccache
```
# 参考信息
## 关于deps、external_deps的使用
在添加一个模块的时候,需要在BUILD.gn中声明它的依赖,为了便于后续处理部件间依赖关系,我们将依赖分为两种——部件内依赖deps和部件间依赖external_deps。
**依赖分类**
![依赖关系分类](figures/dependency_classification.png)
如上图所示,主要分为部件内依赖(图左)和部件间依赖(图右)。
- 部件内依赖: 现有模块module1属于部件part1,要添加一个属于部件part1的模块module2,module2依赖于module1,这种情况就属于部件内依赖。
- 部件间依赖: 现有模块module1属于部件part1,要添加一个模块module2,module2依赖于module1,module2属于部件part2。模块module2与模块module1分属于两个不同的部件,这种情况就属于部件间依赖。
- 部件内依赖示例:
```shell
import("//build/ohos.gni")
ohos_shared_library("module1") {
……
part_name = "part1" # 必选,所属部件名称
……
}
```
```shell
import("//build/ohos.gni")
ohos_shared_library("module2") {
……
deps = [
"module1的gn target",
……
] # 部件内模块依赖
part_name = "part1" # 必选,所属部件名称
}
```
- 部件间依赖示例:
```shell
import("//build/ohos.gni")
ohos_shared_library("module1") {
……
part_name = "part1" # 必选,所属部件名称
……
}
```
```shell
import("//build/ohos.gni")
ohos_shared_library("module2") {
……
external_deps = [
"part1:module1",
……
] # 部件间模块依赖,这里依赖的模块必须是依赖的部件声明在inner_kits中的模块
part_name = "part2" # 必选,所属部件名称
}
```
![icon-note.gif](public_sys-resources/icon-note.gif)**注意**:部件间依赖要写在external_deps里面,格式为”部件名:模块名"的形式,并且依赖的模块必须是依赖的部件声明在inner_kits中的模块。
## 开源软件Notice收集策略说明
开源软件Notice是与项目开源相关的文件,收集这些文件的目的是为了符合开源的规范。
**收集目标**
只收集打包到镜像里面的模块对应的License;不打包的都不收集,比如构建过程使用的工具(如clang、python、ninja等)都是不收集的。
静态库本身是不会被打包的,一般是作为动态库或者可执行程序的一部分被打包到系统中的,为了确保完备,静态库的都会收集。
最终合并的NOTICE.txt要体现出镜像中每个文件都是用了哪些License,模块和License要有对应关系。
最终合并的NOTICE.txt文件在/system/etc/ 目录下。
**收集规则**
按照优先级收集License,以下由1到4,优先级依次降低。
1. 模块在BUILD.gn中直接声明自己使用的License文件,优先级最高。如下示例:
```shell
ohos_shared_library("example") {
...
license_file = "path-to-license-file"
...
}
```
2. 如果模块没有显式声明,那么编译脚本会在BUILD.gn所在的当前目录中查找Readme.OpenSource文件,解析该文件,找出该文件中声明的license,将其作为模块的License。 如果Readme.OpenSource文件中配置的license文件不存在,直接报错。
3. 如果Readme.OpenSource文件不存在,编译脚本会从当前目录开始,向上层目录寻找(一直找到源码的根目录),默认查找License、Copyright、Notice三个文件,如果找到,则将其作为模块的License。
4. 如果上面三种方式都未找到license,则使用默认的license作为该模块的license;默认license是Apache2.0 License。
需要注意及检查的问题
- 三方的开源软件,比如openssl,icu等,这部分软件基本上在源码目录下都要求配置Readme.OpenSource,要检查Readme.OpenSource文件是否和BUILD.gn文件在同一个目录,以及Readme.OpenSource文件中配置的License文件是否存在以及真实有效。
- 代码目录下,如果代码使用的不是Apache2.0 License,需要在目录下提供对应的License文件,或者直接在模块中指定license_file。
- 如果BUILD.gn中添加的源码文件不是当前目录的,需要检查下源码文件所在仓下的license是否和BUILD.gn文件所在仓的一致。
## 加快本地编译的一些参数
编译时,适当选择添加以下的编译参数可以加快编译的过程。
- **添加--ccache参数**:
- 原理:ccache会缓存c/c++编译的编译输出,下一次在编译输入不变的情况下,直接复用缓存的产物。
- 安装:
- 快速安装:执行sudo apt-get install ccache命令。
- [官网下载](https://ccache.dev/download.html),下载二进制文件,把ccache所在路径配置到环境变量。
- 使用:执行./build.sh --product-name 产品名 --ccache命令。
- **添加--fast-rebuild参数**
- 原理:编译流程主要分为:preloader->loader->gn->ninja这四个过程,在本地没有修改gn和产品配置相关文件的前提下,添加--fast-rebuild会让你直接从ninja编译开始。
- 使用:执行./build.sh --product-name 产品名 --fast-rebuild命令。
- **添加enable_notice_collection=false参数**
- 原理:省略掉收集开源软件模块的license的过程。
- 使用:执行./build.sh --product-name 产品名 --gn-args --enable_notice_collection=false --ccache命令。
- **添加--build-target参数**
- 该参数用于指定编译模块,如何找模块的名字:
- 相关仓下BUILD.gn中关注group、ohos_shared_library、ohos_executable等关键字。
- ./build.sh --product-name 产品名 --build-target 模块名 --build-only-gn生成build.ninja,然后去该文件中查找相关模块名。
- 使用:执行./build.sh --product-name 产品名 --build-target ark_js_host_linux_tools_packages命令。
## 查看NinjaTrace
out/rk3568/.ninja_log文件记录了每个模块编译的开始和结束时间(ms),结束时间和开始时间间隔越短表示模块的编译时间越短,编译性能越高。
从左到右分别表示:start time|end time|mtime|command hash。
![Ninja_Trace](figures/Ninja_Trace.png)
图形化显示编译时间。
- 本地打开ninja trace:
解压out/rk3568/build.trace.gz,将build.trace拖到chrome的trace链接chrome://tracing/打开即可。
- 在CI网站ci.openharmony.cn/events上打开ninja trace:
CI上每个编译的输出里面有build.trace.html可直接打开,具体方法是:
1. 点击静态检查下的“成功”;
2. 点击输出列的“输出”即可在左侧的build_trace列看到build.trace.html文件,单击该文件即可打开。
\ No newline at end of file
# 子系统
### 子系统配置规则
通过build仓下的subsystem_config.json可以查看所有子系统的配置规则。
```json
{
"arkui": {
"path": "foundation/arkui", # 路径
"name": "arkui" # 子系统名
},
"ai": {
"path": "foundation/ai",
"name": "ai"
},
"account": {
"path": "base/account",
"name": "account"
},
"distributeddatamgr": {
"path": "foundation/distributeddatamgr",
"name": "distributeddatamgr"
},
"security": {
"path": "base/security",
"name": "security"
},
...
}
```
子系统的配置规则主要是在build/subsystem_config.json中指定子系统的路径和子系统名称。
...@@ -2,8 +2,17 @@ ...@@ -2,8 +2,17 @@
- **[轻量和小型系统编译构建指导](subsys-build-mini-lite.md)**
- **[标准系统编译构建指导](subsys-build-standard-large.md)** - **[编译构建指导](subsys-build-all.md)**
- **[产品](subsys-build-product.md)**
- **[子系统](subsys-build-subsystem.md)**
- **[部件](subsys-build-component.md)**
- **[模块](subsys-build-module.md)**
- **[芯片解决方案](subsys-build-chip_solution.md)**
- **[特性](subsys-build-feature.md)**
- **[HAP编译构建指导](subsys-build-gn-hap-compilation-guide.md)**
- **[构建系统编码规范与最佳实践](subsys-build-gn-coding-style-and-best-practice.md)** - **[构建系统编码规范与最佳实践](subsys-build-gn-coding-style-and-best-practice.md)**
- **[编译构建Kconfig可视化配置指导](subsys-build-gn-kconfig-visual-config-guide.md)** - **[编译构建Kconfig可视化配置指导](subsys-build-gn-kconfig-visual-config-guide.md)**
- **[参考信息](subsys-build-reference.md)**
- **[FAQ](subsys-build-faq.md)**
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册