diff --git a/zh-cn/device-dev/subsystems/figures/Ninja_Trace.png b/zh-cn/device-dev/subsystems/figures/Ninja_Trace.png new file mode 100644 index 0000000000000000000000000000000000000000..6444208247d4d737940845395d3e71da0bf8b481 Binary files /dev/null and b/zh-cn/device-dev/subsystems/figures/Ninja_Trace.png differ diff --git "a/zh-cn/device-dev/subsystems/figures/OpenHarmony\347\274\226\350\257\221\346\236\204\345\273\272\346\241\206\346\236\266.png" "b/zh-cn/device-dev/subsystems/figures/OpenHarmony\347\274\226\350\257\221\346\236\204\345\273\272\346\241\206\346\236\266.png" new file mode 100644 index 0000000000000000000000000000000000000000..3fba2c63324c5a94710c97560db95a96fcc1e61c Binary files /dev/null and "b/zh-cn/device-dev/subsystems/figures/OpenHarmony\347\274\226\350\257\221\346\236\204\345\273\272\346\241\206\346\236\266.png" differ diff --git a/zh-cn/device-dev/subsystems/figures/compilation_process.png b/zh-cn/device-dev/subsystems/figures/compilation_process.png new file mode 100644 index 0000000000000000000000000000000000000000..fd64610a17c3e27fa01dca049c226dd8bdbafae9 Binary files /dev/null and b/zh-cn/device-dev/subsystems/figures/compilation_process.png differ diff --git a/zh-cn/device-dev/subsystems/figures/dependency_classification.png b/zh-cn/device-dev/subsystems/figures/dependency_classification.png new file mode 100644 index 0000000000000000000000000000000000000000..f02f583c74d92dc656791ef9ffdfea8dee11daa3 Binary files /dev/null and b/zh-cn/device-dev/subsystems/figures/dependency_classification.png differ diff --git a/zh-cn/device-dev/subsystems/figures/module_addition_process.png b/zh-cn/device-dev/subsystems/figures/module_addition_process.png new file mode 100644 index 0000000000000000000000000000000000000000..c2e8fff89e80747d7c3d1de804face9bae2ef149 Binary files /dev/null and b/zh-cn/device-dev/subsystems/figures/module_addition_process.png differ diff --git a/zh-cn/device-dev/subsystems/figures/product_subsystem_component_module_relationships.png b/zh-cn/device-dev/subsystems/figures/product_subsystem_component_module_relationships.png new file mode 100644 index 0000000000000000000000000000000000000000..926799f40a3ff9b8a00156c75fed6f137d8766e2 Binary files /dev/null and b/zh-cn/device-dev/subsystems/figures/product_subsystem_component_module_relationships.png differ diff --git a/zh-cn/device-dev/subsystems/subsys-build-FAQ.md b/zh-cn/device-dev/subsystems/subsys-build-FAQ.md new file mode 100644 index 0000000000000000000000000000000000000000..27b6c0f60d3d118fec03d729056d32d295e6de06 --- /dev/null +++ b/zh-cn/device-dev/subsystems/subsys-build-FAQ.md @@ -0,0 +1,84 @@ +# 常见问题 + +## 常见编译问题和解决方法 + +### 编译构建过程中,提示“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 + ``` + + diff --git a/zh-cn/device-dev/subsystems/subsys-build-all.md b/zh-cn/device-dev/subsystems/subsys-build-all.md new file mode 100644 index 0000000000000000000000000000000000000000..f29849524414ae18d5ad033cefc184273557e288 --- /dev/null +++ b/zh-cn/device-dev/subsystems/subsys-build-all.md @@ -0,0 +1,332 @@ +# 编译构建指导 + +## 概述 + +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) + diff --git a/zh-cn/device-dev/subsystems/subsys-build-chip_solution.md b/zh-cn/device-dev/subsystems/subsys-build-chip_solution.md new file mode 100644 index 0000000000000000000000000000000000000000..23ec6c60076a63e5e0c26fd2891cda4820dbea3e --- /dev/null +++ b/zh-cn/device-dev/subsystems/subsys-build-chip_solution.md @@ -0,0 +1,95 @@ +# 芯片解决方案 +### 芯片解决方案配置规则 + +- 芯片解决方案是指基于某款开发板的完整解决方案,包含驱动、设备侧接口适配、开发板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,即可启动芯片解决方案的编译。 diff --git a/zh-cn/device-dev/subsystems/subsys-build-component.md b/zh-cn/device-dev/subsystems/subsys-build-component.md new file mode 100644 index 0000000000000000000000000000000000000000..f4d1e4277090ed78e21833f6b3fe37c669e3593d --- /dev/null +++ b/zh-cn/device-dev/subsystems/subsys-build-component.md @@ -0,0 +1,160 @@ +# 部件 +### 部件配置规则 + +部件的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 diff --git a/zh-cn/device-dev/subsystems/subsys-build-feature.md b/zh-cn/device-dev/subsystems/subsys-build-feature.md new file mode 100644 index 0000000000000000000000000000000000000000..51794dd34483fd1ef15d9a5d89dc77cff2c4e0a1 --- /dev/null +++ b/zh-cn/device-dev/subsystems/subsys-build-feature.md @@ -0,0 +1,70 @@ +# 特性 +### 特性配置规则 + +下面介绍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"] + } + ``` + diff --git a/zh-cn/device-dev/subsystems/subsys-build-gn-coding-style-and-best-practice.md b/zh-cn/device-dev/subsystems/subsys-build-gn-coding-style-and-best-practice.md index d93ffb34a803de4b7dc5e743dd9cac9e2c04f56d..06637998b5fdc5d844030686514f3c6fdeb41c3a 100644 --- a/zh-cn/device-dev/subsystems/subsys-build-gn-coding-style-and-best-practice.md +++ b/zh-cn/device-dev/subsystems/subsys-build-gn-coding-style-and-best-practice.md @@ -1,513 +1,513 @@ -# 构建系统编码规范与最佳实践 - -## 概述 - -gn是generate ninja的缩写,它是一个元编译系统(meta-build system),是ninja的前端,gn和ninja结合起来,完成OpenHarmony操作系统的编译任务。 - -### gn简介 - -- 目前采用gn的大型软件系统有:Chromium,Fuchsia和OpenHarmony。 -- gn语法自设计之初就自带局限性,比如不能求list的长度,不支持通配符等。这些局限性源于其 **有所为有所不为** 的设计哲学,见https://gn.googlesource.com/gn/+/main/docs/language.md#Design-philosophy。 所以在使用gn的过程中,如果发现某件事情用gn实现起来很复杂,请先停下来思考这件事情是否真的需要做。 -- 关于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。 - -### 总体原则 - -在保证功能可用的前提下,脚本需要满足易于阅读,便于维护,良好的扩展性和性能等要求。 - -## 代码风格 - -### 命名 - -总体上遵循Linux kernel的命名风格,即**小写字母+下划线**的命名风格。 - -#### 局部变量 - -我们这里对局部变量的定义为:在某作用域内,且不向下传递的变量。 - -为了更好的区别于全局变量,局部变量统一采用**下划线开头**。 - -``` -# 例1 -action("some_action") { - ... - # _output是个局部变量,所以使用下划线开头 - _output = "${target_out_dir}/${target_name}.out" - outputs = [ _output ] - args = [ - ... - "--output", - rebase_path(_output, root_build_dir), - ... - ] - ... -} -``` - -#### 全局变量 - -全局变量使用**小写字母**开头。 - -如果变量值可以被gn args修改,则需要使用declare_args来声明,否则不要使用declare_args。 - -``` -# 例2 -declare_args() { - # 可以通过gn args来修改some_feature的值 - some_feature = false -} -``` - -#### 目标命名 - -目标命名采用**小写字母+下划线**的命名方式。 - -模板中的**子目标**命名方式采用"${target_name}+双下划线+后缀"的命名方式。这样做有两点好处: - -- 加入"${target_name}"可以防止子目标重名。 - -- 加入双下划线可以很方便地区分出子目标属于哪一个模块,方便在出现问题时快速定位。 - - ``` - # 例3 - template("ohos_shared_library") { - # "{target_name}"(主目标名)+"__"(双下划线)+"notice"(后缀) - _notice_target = "${target_name}__notice" - collect_notice(_notice_target) { - ... - } - shared_library(target_name) { - ... - } - } - ``` - -#### 自定义模板的命名 - -推荐采用**动宾短语**的形式来命名。 - -``` -# 例4 -# Good -template("compile_resources") { - ... -} -``` - -### 格式化 - -gn脚本在提交之前需要执行格式化。格式化可以保证代码对齐,换行等风格的统一。使用gn自带的format工具即可。命令如下: - -```shell -$ gn format path-to-BUILD.gn -``` - -gn format会按照字母序对import文件做排序,如果想保证import的顺序,可以添加空注释行。 - -假设原来的import顺序为: - -``` -# 例5 -import("//b.gni") -import("//a.gni") -``` - -经过format之后变为: - -``` -import("//a.gni") -import("//b.gni") -``` - -如果想保证原有的import顺序,可以添加空注释行。 - -``` -import("//b.gni") -# Comment to keep import order -import("//a.gni") -``` - -## 编码实践 - -### 实践原则 - -编译脚本实质上完成了两件工作: - -1. **描述模块之间依赖关系(deps)** - - 实践过程中,最常出现的问题是**依赖关系缺失**。 - -2. **描述模块编译的规则(rule)** - - 实践过程中,容易出现的问题是**输入和输出不明确**。 - -依赖缺失会导致两个问题: - -- **概率性编译错误** - - ``` - # 例6 - # 依赖关系缺失,导致概率性编译出错 - shared_library("a") { - ... - } - shared_library("b") { - ... - ldflags = [ "-la" ] - deps = [] - ... - } - group("images") { - deps = [ ":b" ] - } - ``` - - 上面的例子中,libb.so在链接的时候会链接liba.so,实质上构成b依赖a,但是b的依赖列表(deps)却没有声明对a的依赖。由于编译是并发执行的,如果libb.so在链接的时候liba.so还没有编译出来,就会出现编译错误。 - - 由于liba.so也有可能在libb.so之前编译出来,所以依赖缺失导致的编译错误是概率性的。 - -- **依赖关系缺失导致模块没有参与编译** - - 还是上面的例子,如果我们指定ninja编译目标为images,由于images仅仅依赖b,所以a不会参与编译。由于b实质上依赖a, 这时b在链接时会出现必现错误。 - -有一种不太常见的问题是**过多的依赖**。**过多的依赖会降低并发,导致编译变慢**。见下面的例子: - -_compile_js_target不需要依赖 _compile_resource_target,增加这层依赖,会导致 _compile_js_target在 _compile_resource_target编译完成之后才能开始编译。 - -``` -# 例7 -# 过多的依赖导致编译变慢 -template("too_much_deps") { - ... - _gen_resource_target = "${target_name}__res" - action(_gen_resource_target) { - ... - } - - _compile_resource_target = "${target_name}__compile_res" - action(_compile_resource_target) { - deps = [":$_gen_resource_target"] - ... - } - - _compile_js_target = "${target_name}__js" - action(_compile_js_target) { - # 这个deps不需要 - deps = [":$_compile_resource_target"] - } -} -``` - -输入不明确会导致: - -- **代码修改了,但增量编译时却没有参与编译。** -- **当使用缓存时,代码发生变化,但是缓存仍然命中。** - -下面的例子中,foo.py引用了bar.py中的函数。bar.py实质上是foo.py的输入,需要将bar.py添加到implict_input_action的input或者depfile中去。否则,修改bar.py,模块implict_input_action将不会重新编译。 - -``` -# 例8 -action("implict_input_action") { - script = "//path-to-foo.py" - ... -} -``` - -``` -#!/usr/bin/env -# Contents of foo.py -import bar -... -bar.some_function() -... -``` - -输出不明确会导致: - -- **隐式的输出** -- **当使用缓存时,隐式输出无法从缓存中获得** - -下面的例子中,foo.py会生成两个文件,a.out和b.out,但是implict_output_action的输出只声明了a.out。这种情况下,b.out实质上就是一个隐式输出。缓存中只会存储a.out,不会存储b.out,当缓存命中时,b.out就编译不出来了。 - -``` -# 例9 -action("implict_output_action") { - outputs = ["${target_out_dir}/a.out"] - script = "//path-to-foo.py" - ... -} -``` - -``` -#!/usr/bin/env -# Contents of foo.py -... -write_file("b.out") -write_file("a.out") -... -``` - -### 模板 - -**不要使用gn的原生模板,使用编译系统提供的模板** - -所谓gn原生模板,是指source_set,shared_library, static_library, action, executable,group这六个模板。 - -不推荐使用原生模板的原因有二: - -- **原生模板是最小功能模板**,无法提供external_deps的解析,notice收集,安装信息生成等的额外功能,这些额外功能最好是随着模块编译时同时生成,所以必须对原生模板做额外的扩展才能满足实际的需求。 - -- 当输入文件依赖的文件发生变化时,gn原生的action模板不能自动感知不到这种编译,无法重新编译。见例8 - - 原生模板和编译系统提供的模板之间的对应关系: - -| 编译系统提供的模板 | 原生模板 | -|:------------------- | -------------- | -| ohos_shared_library | shared_library | -| ohos_source_set | source_set | -| ohos_executable | executable | -| ohos_static_library | static_library | -| action_with_pydeps | action | -| ohos_group | group | - -### 使用python脚本 - -action中的script推荐使用python脚本,不推荐使用shell脚本。相比于shell脚本,python脚本: - -- python语法友好,不会因为少写一个空格就导致奇怪的错误。 -- python脚本有很强的可读性。 -- 可维护性强,可调试。 -- OpenHarmony对python任务做了缓存,可以加快编译速度。 - -### rebase_path - -- 仅在向action的参数列表中(args)调用rebase_path。 - - ``` - # 例10 - template("foo") { - action(target_name) { - ... - args = [ - # 仅在args中调用rebase_path - "--bar=" + rebase_path(invoker.bar, root_build_dir), - ... - ] - ... - } - } - - foo("good") { - bar = something - ... - } - ``` - -- 同一变量做两次rebase_path会出现意想不到的结果。 - - ``` - # 例11 - template("foo") { - action(target_name) { - ... - args = [ - # bar被执行了两次rebase_path, 传递的bar的值已经不对了 - "--bar=" + rebase_path(invoker.bar, root_build_dir), - ... - ] - ... - } - } - - foo("bad") { - # 不要在这里调用rebase_path - bar = rebase_path(some_value,root_build_dir) - ... - } - ``` - -### 模块间数据分享 - -模块间数据分享是很常见的事情,比如A模块想要知道B模块的输出和deps。 - -- 同一BUILD.gn之间数据分享 - - 同一BUILD.gn之间数据可以通过定义全局变量的方式来共享。 - - 下面的例子中,模块a的输出是模块b的输入,可以通过定义全局变量的方式来共享给b - - ``` - # 例12 - _output_a = get_label_info(":a", "out_dir") + "/a.out" - action("a") { - outputs = _output_a - ... - } - action("b") { - inputs = [_output_a] - ... - } - ``` - -- 不同BUILD.gn之间数据分享 - - 不同BUILD.gn之间传递数据,最好的办法是将需要共享的数据保存成文件,然后不同模块之间通过文件来传递和共享数据。这种场景比较复杂,读者可以参照OpenHarmony的hap编译过程的write_meta_data。 - -### forward_variable_from - -- 自定义模板需要首先将testonly传递(forward)进来。因为该模板的target有可能被testonly的目标依赖。 - - ``` - # 例13 - # 自定义模板首先要传递testonly - template("foo") { - forward_variable_from(invoker, ["testonly"]) - ... - } - ``` - -- 不推荐使用*来forward变量,需要的变量应该**显式地,一个一个地**被forward进来。 - - ``` - # 例14 - # Bad,使用*forward变量 - template("foo") { - forward_variable_from(invoker, "*") - ... - } - - # Good, 显式地,一个一个地forward变量 - template("bar") { - # - forward_variable_from(invoker, [ - "testonly", - "deps", - ... - ]) - ... - } - ``` - -### target_name - -target_name会随着作用域变化而变化,使用时需要注意。 - -``` -# 例15 -# target_name会随着作用域变化而变化 -template("foo") { - # 此时打印出来的target_name为"${target_name}" - print(target_name) - _code_gen_target = "${target_name}__gen" - code_gen(_code_gen_target) { - # 此时打印出来的target_name为"${target_name}__gen" - print(target_name) - ... - } - _compile_gen_target = "${target_name}__compile" - compile(_compile_gen_target) { - # 此时打印出来的target_name为"${target_name}__compile" - print(target_name) - ... - } - ... -} -``` - -### public_configs - -如果模块需要向外export头文件,请使用public_configs。 - -``` -# 例16 -# b依赖a,会同时继承a的headers -config("headers") { - include_dirs = ["//path-to-headers"] - ... -} -shared_library("a") { - public_configs = [":headers"] - ... -} -executable("b") { - deps = [":a"] - ... -} -``` - -### template - -自定义模板中必须有一个子目标的名字是target_name。该子目标会作为template的主目标。其他子目标都应该被主目标依赖,否则子目标不会被编译。 - -``` -# 例17 -# 自定义模板中必须有一个子目标的名字是target_name -template("foo") { - _code_gen_target = "${target_name}__gen" - code_gen(_code_gen_target) { - ... - } - _compile_gen_target = "${target_name}__compile" - compile(_compile_gen_target) { - # 此时打印出来的target_name为"${target_name}__compile" - print(target_name) - ... - } - ... - group(target_name) { - deps = [ - # 由于_compile_gen_target依赖了_code_gen_target,所以主目标只需要依赖_compile_gen_target即可。 - ":$_compile_gen_target" - ] - } -} -``` - -### set_source_assignment_filter - -set_source_assignment_filter除了可以过滤sources,还可以用来过滤其他变量。过滤完成后记得将过滤器和sources置空。 - -``` -# 例18 -# 使用set_source_assignment_filter过滤依赖, 挑选label符合*:*_res的添加到依赖列表中 -_deps = [] -foreach(_possible_dep, invoker.deps) { - set_source_assignment_filter(["*:*_res"]) - _label = get_label_info(_possible_dep, "label_no_toolchain") - sources = [] - sources = [ _label ] - if (sources = []) { - _deps += _sources - } -} -sources = [] -set_source_assignment_filter([]) -``` - -最新版本上set_source_assignment_filter被filter_include和filter_exclude取代。 - -### 部件内依赖采用deps,跨部件依赖采用external_deps - -- 部件在OpenHarmony上指能提供某个能力的一组模块。 - -- 在模块定义的时候可以声明part_name,用来表明当前模块属于哪个部件。 - -- 每个部件会声明其inner-kit,供其他部件调用。部件innerkit的声明见源码中的bundle.json。 - -- 部件间依赖只能依赖innerkit,不能依赖非innerkit的模块。 - -- 如果a模块和b模块的part_name相同,那么a、b模块属于同一个部件,a,b模块之间的依赖关系可以用deps来声明。 - -- 如果a、b模块的part_name不同,那么a、b模块不属于同一个部件,a、b模块之间的依赖关系需要通过external_deps来声明,依赖方式为"部件名:模块名"的方式。见例19。 - - ``` - # 例19 - shared_library("a") { - ... - external_deps = ["part_name_of_b:b"] - ... - } - ``` +# 构建系统编码规范与最佳实践 + +## 概述 + +gn是generate ninja的缩写,它是一个元编译系统(meta-build system),是ninja的前端,gn和ninja结合起来,完成OpenHarmony操作系统的编译任务。 + +### gn简介 + +- 目前采用gn的大型软件系统有:Chromium,Fuchsia和OpenHarmony。 +- gn语法自设计之初就自带局限性,比如不能求list的长度,不支持通配符等。这些局限性源于其 **有所为有所不为** 的[设计哲学](https://gn.googlesource.com/gn/+/main/docs/language.md#Design-philosophy)。 所以在使用gn的过程中,如果发现某件事情用gn实现起来很复杂,请先停下来思考这件事情是否真的需要做。 +- 关于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)。 + +### 总体原则 + +在保证功能可用的前提下,脚本需要满足易于阅读,便于维护,良好的扩展性和性能等要求。 + +## 代码风格 + +### 命名 + +总体上遵循Linux kernel的命名风格,即**小写字母+下划线**的命名风格。 + +#### 局部变量 + +我们这里对局部变量的定义为:在某作用域内,且不向下传递的变量。 + +为了更好的区别于全局变量,局部变量统一采用**下划线开头**。 + +```shell +# 例1 +action("some_action") { + ... + # _output是个局部变量,所以使用下划线开头 + _output = "${target_out_dir}/${target_name}.out" + outputs = [ _output ] + args = [ + ... + "--output", + rebase_path(_output, root_build_dir), + ... + ] + ... +} +``` + +#### 全局变量 + +全局变量使用**小写字母**开头。 + +如果变量值可以被gn args修改,则需要使用declare_args来声明,否则不要使用declare_args。 + +```shell +# 例2 +declare_args() { + # 可以通过gn args来修改some_feature的值 + some_feature = false +} +``` + +#### 目标命名 + +目标命名采用**小写字母+下划线**的命名方式。 + +模板中的**子目标**命名方式采用"${target_name}+双下划线+后缀"的命名方式。这样做有两点好处: + +- 加入"${target_name}"可以防止子目标重名。 + +- 加入双下划线可以很方便地区分出子目标属于哪一个模块,方便在出现问题时快速定位。 + + ```shell + # 例3 + template("ohos_shared_library") { + # "{target_name}"(主目标名)+"__"(双下划线)+"notice"(后缀) + _notice_target = "${target_name}__notice" + collect_notice(_notice_target) { + ... + } + shared_library(target_name) { + ... + } + } + ``` + +#### 自定义模板的命名 + +推荐采用**动宾短语**的形式来命名。 + +```shell +# 例4 +# Good +template("compile_resources") { + ... +} +``` + +### 格式化 + +gn脚本在提交之前需要执行格式化。格式化可以保证代码对齐,换行等风格的统一。使用gn自带的format工具即可。命令如下: + +```shell +$ gn format path-to-BUILD.gn +``` + +gn format会按照字母序对import文件做排序,如果想保证import的顺序,可以添加空注释行。 + +假设原来的import顺序为: + +```shell +# 例5 +import("//b.gni") +import("//a.gni") +``` + +经过format之后变为: + +```shell +import("//a.gni") +import("//b.gni") +``` + +如果想保证原有的import顺序,可以添加空注释行。 + +```shell +import("//b.gni") +# Comment to keep import order +import("//a.gni") +``` + +## 编码实践 + +### 实践原则 + +编译脚本实质上完成了两件工作: + +1. **描述模块之间依赖关系(deps)** + + 实践过程中,最常出现的问题是**依赖关系缺失**。 + +2. **描述模块编译的规则(rule)** + + 实践过程中,容易出现的问题是**输入和输出不明确**。 + +依赖缺失会导致两个问题: + +- **概率性编译错误** + + ```shell + # 例6 + # 依赖关系缺失,导致概率性编译出错 + shared_library("a") { + ... + } + shared_library("b") { + ... + ldflags = [ "-la" ] + deps = [] + ... + } + group("images") { + deps = [ ":b" ] + } + ``` + + 上面的例子中,libb.so在链接的时候会链接liba.so,实质上构成b依赖a,但是b的依赖列表(deps)却没有声明对a的依赖。由于编译是并发执行的,如果libb.so在链接的时候liba.so还没有编译出来,就会出现编译错误。 + + 由于liba.so也有可能在libb.so之前编译出来,所以依赖缺失导致的编译错误是概率性的。 + +- **依赖关系缺失导致模块没有参与编译** + + 还是上面的例子,如果我们指定ninja编译目标为images,由于images仅仅依赖b,所以a不会参与编译。由于b实质上依赖a, 这时b在链接时会出现必现错误。 + +有一种不太常见的问题是**过多的依赖**。**过多的依赖会降低并发,导致编译变慢**。见下面的例子: + +_compile_js_target不需要依赖 _compile_resource_target,增加这层依赖,会导致 _compile_js_target在 _compile_resource_target编译完成之后才能开始编译。 + +```shell +# 例7 +# 过多的依赖导致编译变慢 +template("too_much_deps") { + ... + _gen_resource_target = "${target_name}__res" + action(_gen_resource_target) { + ... + } + + _compile_resource_target = "${target_name}__compile_res" + action(_compile_resource_target) { + deps = [":$_gen_resource_target"] + ... + } + + _compile_js_target = "${target_name}__js" + action(_compile_js_target) { + # 这个deps不需要 + deps = [":$_compile_resource_target"] + } +} +``` + +输入不明确会导致: + +- **代码修改了,但增量编译时却没有参与编译。** +- **当使用缓存时,代码发生变化,但是缓存仍然命中。** + +下面的例子中,foo.py引用了bar.py中的函数。bar.py实质上是foo.py的输入,需要将bar.py添加到implict_input_action的input或者depfile中去。否则,修改bar.py,模块implict_input_action将不会重新编译。 + +```shell +# 例8 +action("implict_input_action") { + script = "//path-to-foo.py" + ... +} +``` + +```shell +#!/usr/bin/env +# Contents of foo.py +import bar +... +bar.some_function() +... +``` + +输出不明确会导致: + +- **隐式的输出** +- **当使用缓存时,隐式输出无法从缓存中获得** + +下面的例子中,foo.py会生成两个文件,a.out和b.out,但是implict_output_action的输出只声明了a.out。这种情况下,b.out实质上就是一个隐式输出。缓存中只会存储a.out,不会存储b.out,当缓存命中时,b.out就编译不出来了。 + +```shell +# 例9 +action("implict_output_action") { + outputs = ["${target_out_dir}/a.out"] + script = "//path-to-foo.py" + ... +} +``` + +```shell +#!/usr/bin/env +# Contents of foo.py +... +write_file("b.out") +write_file("a.out") +... +``` + +### 模板 + +**不要使用gn的原生模板,使用编译子系统提供的模板** + +所谓gn原生模板,是指source_set,shared_library, static_library, action, executable,group这六个模板。 + +不推荐使用原生模板的原因有二: + +- **原生模板是最小功能模板**,无法提供external_deps的解析,notice收集,安装信息生成等的额外功能,这些额外功能最好是随着模块编译时同时生成,所以必须对原生模板做额外的扩展才能满足实际的需求。 + +- 当输入文件依赖的文件发生变化时,gn原生的action模板不能自动感知不到这种编译,无法重新编译。见例8 + + 原生模板和编译子系统提供的模板之间的对应关系: + +| 编译子系统提供的模板 | 原生模板 | +| :------------------ | -------------- | +| ohos_shared_library | shared_library | +| ohos_source_set | source_set | +| ohos_executable | executable | +| ohos_static_library | static_library | +| action_with_pydeps | action | +| ohos_group | group | + +### 使用python脚本 + +action中的script推荐使用python脚本,不推荐使用shell脚本。相比于shell脚本,python脚本: + +- python语法友好,不会因为少写一个空格就导致奇怪的错误。 +- python脚本有很强的可读性。 +- 可维护性强,可调试。 +- OpenHarmony对python任务做了缓存,可以加快编译速度。 + +### rebase_path + +- 仅在向action的参数列表中(args)调用rebase_path。 + + ```shell + # 例10 + template("foo") { + action(target_name) { + ... + args = [ + # 仅在args中调用rebase_path + "--bar=" + rebase_path(invoker.bar, root_build_dir), + ... + ] + ... + } + } + + foo("good") { + bar = something + ... + } + ``` + +- 同一变量做两次rebase_path会出现意想不到的结果。 + + ```shell + # 例11 + template("foo") { + action(target_name) { + ... + args = [ + # bar被执行了两次rebase_path, 传递的bar的值已经不对了 + "--bar=" + rebase_path(invoker.bar, root_build_dir), + ... + ] + ... + } + } + + foo("bad") { + # 不要在这里调用rebase_path + bar = rebase_path(some_value,root_build_dir) + ... + } + ``` + +### 模块间数据分享 + +模块间数据分享是很常见的事情,比如A模块想要知道B模块的输出和deps。 + +- 同一BUILD.gn之间数据分享 + + 同一BUILD.gn之间数据可以通过定义全局变量的方式来共享。 + + 下面的例子中,模块a的输出是模块b的输入,可以通过定义全局变量的方式来共享给b + + ```shell + # 例12 + _output_a = get_label_info(":a", "out_dir") + "/a.out" + action("a") { + outputs = _output_a + ... + } + action("b") { + inputs = [_output_a] + ... + } + ``` + +- 不同BUILD.gn之间数据分享 + + 不同BUILD.gn之间传递数据,最好的办法是将需要共享的数据保存成文件,然后不同模块之间通过文件来传递和共享数据。这种场景比较复杂,读者可以参照OpenHarmony的hap编译过程的write_meta_data。 + +### forward_variable_from + +- 自定义模板需要首先将testonly传递(forward)进来。因为该模板的target有可能被testonly的目标依赖。 + + ```shell + # 例13 + # 自定义模板首先要传递testonly + template("foo") { + forward_variable_from(invoker, ["testonly"]) + ... + } + ``` + +- 不推荐使用*来forward变量,需要的变量应该**显式地,一个一个地**被forward进来。 + + ```shell + # 例14 + # Bad,使用*forward变量 + template("foo") { + forward_variable_from(invoker, "*") + ... + } + + # Good, 显式地,一个一个地forward变量 + template("bar") { + # + forward_variable_from(invoker, [ + "testonly", + "deps", + ... + ]) + ... + } + ``` + +### target_name + +target_name会随着作用域变化而变化,使用时需要注意。 + +```shell +# 例15 +# target_name会随着作用域变化而变化 +template("foo") { + # 此时打印出来的target_name为"${target_name}" + print(target_name) + _code_gen_target = "${target_name}__gen" + code_gen(_code_gen_target) { + # 此时打印出来的target_name为"${target_name}__gen" + print(target_name) + ... + } + _compile_gen_target = "${target_name}__compile" + compile(_compile_gen_target) { + # 此时打印出来的target_name为"${target_name}__compile" + print(target_name) + ... + } + ... +} +``` + +### public_configs + +如果模块需要向外export头文件,请使用public_configs。 + +```shell +# 例16 +# b依赖a,会同时继承a的headers +config("headers") { + include_dirs = ["//path-to-headers"] + ... +} +shared_library("a") { + public_configs = [":headers"] + ... +} +executable("b") { + deps = [":a"] + ... +} +``` + +### template + +自定义模板中必须有一个子目标的名字是target_name。该子目标会作为template的主目标。其他子目标都应该被主目标依赖,否则子目标不会被编译。 + +```shell +# 例17 +# 自定义模板中必须有一个子目标的名字是target_name +template("foo") { + _code_gen_target = "${target_name}__gen" + code_gen(_code_gen_target) { + ... + } + _compile_gen_target = "${target_name}__compile" + compile(_compile_gen_target) { + # 此时打印出来的target_name为"${target_name}__compile" + print(target_name) + ... + } + ... + group(target_name) { + deps = [ + # 由于_compile_gen_target依赖了_code_gen_target,所以主目标只需要依赖_compile_gen_target即可。 + ":$_compile_gen_target" + ] + } +} +``` + +### set_source_assignment_filter + +set_source_assignment_filter除了可以过滤sources,还可以用来过滤其他变量。过滤完成后记得将过滤器和sources置空。 + +```shell +# 例18 +# 使用set_source_assignment_filter过滤依赖, 挑选label符合*:*_res的添加到依赖列表中 +_deps = [] +foreach(_possible_dep, invoker.deps) { + set_source_assignment_filter(["*:*_res"]) + _label = get_label_info(_possible_dep, "label_no_toolchain") + sources = [] + sources = [ _label ] + if (sources = []) { + _deps += _sources + } +} +sources = [] +set_source_assignment_filter([]) +``` + +最新版本上set_source_assignment_filter被filter_include和filter_exclude取代。 + +### 部件内依赖采用deps,跨部件依赖采用external_deps + +- 部件在OpenHarmony上指能提供某个能力的一组模块。 + +- 在模块定义的时候可以声明part_name,用来表明当前模块属于哪个部件。 + +- 每个部件会声明其inner_kits,供其他部件调用。部件inner_kits的声明见源码中的bundle.json。 + +- 部件间依赖只能依赖inner_kits,不能依赖非inner_kits的模块。 + +- 如果a模块和b模块的part_name相同,那么a、b模块属于同一个部件,a,b模块之间的依赖关系可以用deps来声明。 + +- 如果a、b模块的part_name不同,那么a、b模块不属于同一个部件,a、b模块之间的依赖关系需要通过external_deps来声明,依赖方式为"部件名:模块名"的方式。见例19。 + + ```shell + # 例19 + shared_library("a") { + ... + external_deps = ["part_name_of_b:b"] + ... + } + ``` \ No newline at end of file diff --git a/zh-cn/device-dev/subsystems/subsys-build-gn-hap-compilation-guide.md b/zh-cn/device-dev/subsystems/subsys-build-gn-hap-compilation-guide.md index 5a01b95c68a77f83c4ca4366e1ed2d0970e61304..dd3fcbf4a726c36a3cc67c1d6d3d653f6aa629ec 100644 --- a/zh-cn/device-dev/subsystems/subsys-build-gn-hap-compilation-guide.md +++ b/zh-cn/device-dev/subsystems/subsys-build-gn-hap-compilation-guide.md @@ -17,7 +17,7 @@ ## 开发指导 -### 编译系统提供的模板 +### 编译子系统提供的模板 #### ohos_hap 声明一个HAP目标,该目标会生成一个HAP,最终将会打包到system镜像中。 diff --git a/zh-cn/device-dev/subsystems/subsys-build-mini-lite.md b/zh-cn/device-dev/subsystems/subsys-build-mini-lite.md deleted file mode 100644 index a8cda2618b822f6123d84fe19e687310eb82ef9e..0000000000000000000000000000000000000000 --- a/zh-cn/device-dev/subsystems/subsys-build-mini-lite.md +++ /dev/null @@ -1,949 +0,0 @@ -# 轻量和小型系统编译构建指导 - -## 概述 - - 一个基于gn和ninja的构建系统,以支持OpenHarmony部件化开发为目标,提供以下基本功能: - -- 支持按部件拼装产品并编译。 - -- 独立构建芯片解决方案厂商源码。 - -- 独立构建单个部件。 - -### 基本概念 - -在使用编译构建子系统前,应了解如下基本概念: - -- 子系统 - 子系统是一个逻辑概念,它由一个或多个具体的部件组成。OpenHarmony整体遵从分层设计,从下向上依次为:内核层、系统服务层、框架层和应用层。系统功能按照“系统 > 子系统 > 部件”逐级展开,在多设备部署场景下,支持根据实际需求裁剪某些非必要的子系统或部件。 - -- 部件 - 系统最小的可复用、可配置、可裁剪的功能单元。部件具备目录独立可并行开发、可独立编译、可独立测试的特征。 - -- gn - Generate ninja的缩写,用于产生ninja文件。 - -- ninja - ninja是一个专注于速度的小型构建系统。 - -- hb - OpenHarmony的命令行工具,用来执行编译命令。 - -### 目录结构 - -``` -build/lite -├── components # 部件描述文件 -├── figures # readme中的图片 -├── hb # hb pip安装包源码 -├── make_rootfs # 文件系统镜像制作脚本 -├── config # 编译配置项 -│ ├── component # 部件相关的模板定义 -│ ├── kernel # 内核相关的编译配置 -│ └── subsystem # 子系统编译配置 -├── platform # ld脚本 -├── testfwk # 测试编译框架 -└── toolchain # 编译工具链配置,包括:编译器路径、编译选项、链接选项等 -``` - -### 构建流程 - -编译构建流程如下图所示,主要分设置和编译两步: - - **图1** 编译构建流程 - - ![zh-cn_image_0000001171796557](figures/zh-cn_image_0000001171796557.jpg) - -1. hb set: 设置OpenHarmony源码目录和要编译的产品。 - -2. hb build: 编译产品、开发板或者部件。编译主要过程如下: - - 1. 读取编译配置:根据产品选择的开发板,读取开发板config.gni文件内容,主要包括编译工具链、编译链接命令和选项等。 - 2. 调用gn:调用gn gen命令,读取产品配置生成产品解决方案out目录和ninja文件。 - 3. 调用ninja:调用ninja -C out/board/product启动编译。 - 4. 系统镜像打包:将部件编译产物打包,设置文件属性和权限,制作文件系统镜像。 - -## 配置规则 - -为了实现芯片解决方案、产品解决方案与OpenHarmony是解耦的、可插拔的,部件、芯片解决方案和产品解决方案的路径、目录树和配置需遵循一定的规则,具体如下: - -### 部件 - - 部件源码路径命名规则为:**{领域}/{子系统}/{部件}**,部件目录树规则如下: - -> ![icon-caution.gif](public_sys-resources/icon-caution.gif) **注意:** -> 部件的名称、源码路径、功能简介、是否必选、编译目标、RAM、ROM、编译输出、已适配的内核、可配置的特性和依赖等属性定义在build/lite/components目录下对应子系统的json文件中,新增部件时需要在对应子系统json文件中添加相应的部件定义。产品所配置的部件必须在某个子系统中被定义过,否则会校验失败。 - -``` -component -├── interfaces -│ ├── innerkits # 系统内接口,部件间使用 -│ └── kits # 应用接口,应用开发者使用 -├── frameworks # framework实现 -├── services # service实现 -└── BUILD.gn # 部件编译脚本 -``` - - 以泛sensor子系统的sensor服务部件为例,部件属性定义描述文件字段说明如下: - - ``` - { - "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" - ], - "third_party": [ # 部件依赖的三方开源软件 - "bounds_checking_function" - ] - } - "build": { # 编译相关配置 - "sub_component": [ - ""//base/sensors/sensor_lite/services:sensor_service"", # 部件编译入口 - ], # 部件编译入口,模块在此处配置 - "inner_kits": [], # 部件间接口 - "test": [] # 部件测试用例编译入口 - } - } - } - ``` - - 部件BUILD.gn的编写建议如下: - -- 编译目标名称与部件一致。 - -- 部件对外可配置的特性变量需声明在该部件BUILD.gn中,特性变量命名规则:ohos_{subsystem}_{component}_{feature}。特性在部件描述中也需要同步定义,在产品配置文件config.json中按需配置。 - -- 宏定义规则:OHOS_{SUBSYSTEM}_{COMPONENT}_{FEATURE} - - > ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:** - > 部件的编译脚本语言为gn,gn的基本用法请见[gn快速入门](https://gn.googlesource.com/gn/+/master/docs/quick_start.md)。部件即为gn定义的编译目标,可以为静态库、动态库、可执行文件或group。 - - 以图形的UI部件为例,foundation/graphic/ui/BUILD.gn文件如下: - - ``` - # 声明部件可配置的特性 - declare_args() { - enable_ohos_graphic_ui_animator = false # 动效特性开关 - ohos_ohos_graphic_ui_font = "vector" # 可配置的字体类型,vector或者bitmap - } - - # 部件基础功能 - shared_library("base") { - sources = [ - ...... - ] - include_dirs = [ - ...... - ] - } - - # 仅在animator开启时编译 - if(enable_ohos_graphic_ui_animator ) { - shared_library("animator") { - sources = [ - ...... - ] - include_dirs = [ - ...... - ] - deps = [ :base ] - } - } - ...... - # target名称建议与部件名称一致, 部件target类型可以是executable(bin文件),shared_library(动态库.so),static_library(静态库.a),group等等 - executable("ui") { - deps = [ - ":base" - ] - - # animator特性由产品配置 - if(enable_ohos_graphic_ui_animator ) { - deps += [ - "animator" - ] - } - } - ``` - -### 芯片解决方案 - -- 芯片解决方案是指基于某款开发板的完整解决方案,包含驱动、设备侧接口适配、开发板sdk等。 - -- 芯片解决方案是一个特殊的部件,源码路径规则为:**device/{开发板}/{芯片解决方案厂商}**。 - -- 芯片解决方案部件会随产品选择的开发板默认编译。 - -- 芯片解决方案目录树规则如下: - - ``` - device - └── board # 芯片解决方案厂商 - └── company # 开发板名称 - ├── 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的关键字段介绍如下: - - ``` - 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: 开发板配置的链接选项。 - ``` - -### 产品解决方案 - -产品解决方案为基于开发板的完整产品,主要包含产品对OS的适配、部件拼装配置、启动配置和文件系统配置等。产品解决方案的源码路径规则为:**vendor/{产品解决方案厂商}/{产品名称}**_。_产品解决方案也是一个特殊的部件。 - -产品解决方案的目录树规则如下: - -``` -vendor -└── company # 产品解决方案厂商 - ├── product # 产品名称 - │ ├── init_configs - │ │ ├── etc # init进程启动配置(可选,仅linux内核需要) - │ │ └── init.cfg # 系统服务启动配置 - │ ├── hals # 产品解决方案OS适配 - │ ├── BUILD.gn # 产品编译脚本 - │ └── config.json # 产品配置文件 - │ └── fs.yml # 文件系统打包配置 - └── ...... -``` - -> ![icon-caution.gif](public_sys-resources/icon-caution.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: 挂载命令 - - 该文件中的各个字段的解释如下: - - ``` - { - "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产品为例,配置文件如下: - - ``` - { - "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。它由多个列表组成,每个列表对应一个文件系统。字段说明如下: - - ``` - 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字段支持以下变量: - - - ${root_path} - 代码根目录,对应gn的${ohos_root_path} - - - ${out_path} - 产品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应该如下: - - ``` - group("product") { # target名称需与product名称即三级目录名称一致 - deps = [] - # 拷贝init配置 - deps += [ "init_configs" ] - # 其他 - ...... - } - ``` - -## 使用指导 - -### 前提条件 - -开发环境需安装gn、ninja构建工具、python 3.9.2及以上和hb。安装方法请见[搭建基础环境](../quick-start/quickstart-lite-env-setup.md)。 - -### hb工具使用说明 - -hb是OpenHarmony的命令行工具,用来执行编译命令。以下对hb的常用命令进行说明。 - - **hb set** - -``` -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** - -查看当前设置信息 - -``` -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** - -``` -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路径。 - -``` -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 -``` - -### 新增部件 - -本小节介绍如何新增一个部件,首先确定部件归属的子系统和部件名称,然后按如下步骤新增: - -1. 源码开发完成后,添加部件编译脚本。 - - 以编译部件hello_world可执行文件为例,applications/sample/hello_world/BUILD.gn可以写为: - - ``` - executable("hello_world") { - include_dirs = [ - "include", - ] - sources = [ - "src/hello_world.c" - ] - } - ``` - - 如上编译脚本,可编译出一个可在OpenHarmony上运行的名为hello_world的可执行文件。 - - 单独编译该部件,hb set任意选择一款产品,然后使用-T选项单独编译部件: - - ``` - hb build -f -T //applications/sample/hello_world - ``` - - 部件在开发板上功能验证完成后,可按步骤2-4将部件配置到产品中。 - -2. 添加部件描述。 - - 部件描述位于build/lite/components下,新增的部件需加入对应子系统的json文件中。一个部件描述必选的字段有: - - - component:部件名称。 - - description:部件的一句话功能描述。 - - optional:部件是否为系统可选。 - - dirs:部件源码路径。 - - targets:部件编译入口。 - - 以将hello_world部件加入应用子系统为例,在applications.json中添加hello_world对象: - - ``` - { - "components": [ - { - "component": "hello_world", - "description": "Hello world.", - "optional": "true", - "dirs": [ - "applications/sample/hello_world" - ], - "targets": [ - "//applications/sample/hello_world" - ] - }, - ... - ] - } - ``` - -3. 将部件配置到产品。 - - 产品的配置文件config.json位于vendor/company/product/下,产品配置文件需包含产品名称、OpenHarmony版本号、device厂商、开发板、内核类型、内核版本号,以及配置的子系统和部件。以将hello_world部件加入产品配置文件my_product.json中为例,加入hello_world对象: - - ``` - { - "product_name": "hello_world_test", - "ohos_version": "OpenHarmony 1.0", - "device_company": "hisilicon", - "board": "hispark_taurus", - "kernel_type": "liteos_a", - "kernel_version": "1.0.0", - "subsystems": [ - { - "subsystem": "applications", - "components": [ - { "component": "hello_world", "features":[] } - ] - }, - ... - ] - } - ``` - -4. 编译产品。 - - 1. 代码根目录输入hb set选择对应产品。 - - 2. 执行hb build。 - -### 新增芯片解决方案 - -编译构建支持添加新的芯片解决方案厂商,具体步骤如下: - -1. 创建芯片解决方案目录。 - 按照[芯片解决方案配置规则](#配置规则)创建目录,以芯片厂商realtek的“rtl8720“开发板为例, 在代码根目录执行: - - ``` - mkdir -p device/board/realtek/rtl8720 - ``` - -2. 创建内核适配目录,并编写开发板编译配置config.gni文件。 - 以realtek的“rtl8720“开发板的liteos_a适配为例,device/board/realtek/rtl8720/liteo_a/config.gni的内容如下: - - ``` - # 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 installed, it's not mandatory if you have added toolchain 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内容可以是: - - ``` - group("rtl8720") { # target类型也可以shared_library, static_library, executable - # 具体内容 - ...... - } - ``` - -4. 编译芯片解决方案。 - 在开发板目录下执行hb build,即可启动芯片解决方案的编译。 - -### 新增产品解决方案 - -编译构建支持芯片解决方案和部件的灵活拼装,形成定制化的产品解决方案。具体步骤如下: - -1. 创建产品目录 - 按照[产品解决方案配置规则](#配置规则)创建产品目录,以基于“rtl8720“开发板的wifiiot模组为例,在代码根目录执行: - - ``` - mkdir -p vendor/my_company/wifiiot - ``` - -2. 拼装产品 - 在新建的产品目录下新建config.json文件,以步骤1中的wifiiot为例,vendor/my_company/wifiiot/config.json可以是: - - ``` - { - "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":[] } # 选择的部件和部件特性 - ] - }, - ... - { - 更多子系统和部件 - } - ] - } - ``` - - 注意:编译构建系统编译前会对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文件如下: - - ``` - - - 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文件如下: - - ``` - # 需要打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文件打到对应目录中,再进行编译: - - ``` - hb build -f --patch - ``` - -8. 编写编译脚本 - 在产品目录下创建BUILD.gn文件,按产品实际情况编写脚本。以步骤1中的wifiiot为例,BUILD.gn示例如下: - - ``` - group("wifiiot") { # target名称与产品名一致 - deps = [] - # 拷贝init配置 - deps += [ "init_configs" ] - # 将hals加入编译 - deps += [ "hals" ] - # 其他 - ...... - } - ``` - -9. 编译产品。 - 在代码根目录执行hb set按提示选择新增的产品,然后执行hb build即可启动编译。 - -## 常见问题 - -### 编译构建过程中,提示“usr/sbin/ninja: invalid option -- w” - -- **现象描述:** - 编译失败,提示“usr/sbin/ninja: invalid option -- w”。 - -- **可能原因:** - 编译环境中ninja版本太低,不支持--w选项。 - -- **解决办法:** - 卸载环境中ninja和gn,按照[获取工具](../get-code/gettools-ide.md)。 - -### 编译构建过程中,提示“/usr/bin/ld: cannot find -lncurses” - -- **现象描述:** - 编译失败,提示“/usr/bin/ld: cannot find -lncurses”。 - -- **可能原因:** - 编译环境ncurses库缺失。 - -- **解决办法:** - - ``` - sudo apt-get install lib32ncurses5-dev - ``` - -### 编译构建过程中,提示“line 77: mcopy: command not found” - -- **现象描述:** - ​编译失败,提示“line 77: mcopy: command not found”。 - -- **可能原因:** - 编译环境未安装mcopy。 - -- **解决办法:** - - ``` - ​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所在目录。 - - ``` - which riscv32-unknown-elf-gcc - ``` - - 使用chmod命令修改目录权限为755。 - -### 编译构建过程中,提示“No module named 'Crypto'” - -- **现象描述:** - 编译失败,提示“No module named 'Crypto'”。 - -- **可能原因:** - python3未安装Crypto。 - -- **解决办法:** - - 1. 查询Python版本号。 - - ``` - python3 --version - ``` - - 2. 需使用python3.9.2以上版本,然后安装pycryptodome。 - - ``` - sudo pip3 install pycryptodome - ``` - -### 编译构建过程中,提示“xx.sh : xx unexpected operator” - -- **现象描述:** - 编译失败:“xx.sh [: xx unexpected operator”。 - -- **可能原因:** - 编译环境shell不是bash。 - -- **解决办法:** - - ``` - sudo rm -rf /bin/sh - sudo ln -s /bin/bash /bin/sh - ``` \ No newline at end of file diff --git a/zh-cn/device-dev/subsystems/subsys-build-module.md b/zh-cn/device-dev/subsystems/subsys-build-module.md new file mode 100644 index 0000000000000000000000000000000000000000..151a060f1f3850b89b439ce62d2c7e5806490339 --- /dev/null +++ b/zh-cn/device-dev/subsystems/subsys-build-module.md @@ -0,0 +1,505 @@ +# 模块 +## 模块配置规则 + +编译子系统通过模块、部件和产品三层配置来实现编译和打包。模块就是编译子系统的一个目标,包括(动态库、静态库、配置文件、预编译模块等)。模块要定义属于哪个部件,一个模块只能归属于一个部件。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/", # 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": "", # 部件名称 + "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 diff --git a/zh-cn/device-dev/subsystems/subsys-build-product.md b/zh-cn/device-dev/subsystems/subsys-build-product.md new file mode 100644 index 0000000000000000000000000000000000000000..154cdfdc9ea9734af4269474baa3f26f09812ef6 --- /dev/null +++ b/zh-cn/device-dev/subsystems/subsys-build-product.md @@ -0,0 +1,337 @@ +# 产品 +### 产品配置规则 + +产品解决方案为基于开发板的完整产品,主要包含产品对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 + ``` + + diff --git a/zh-cn/device-dev/subsystems/subsys-build-reference.md b/zh-cn/device-dev/subsystems/subsys-build-reference.md new file mode 100644 index 0000000000000000000000000000000000000000..6133338c0179edab789065329e8058c94c8b18b1 --- /dev/null +++ b/zh-cn/device-dev/subsystems/subsys-build-reference.md @@ -0,0 +1,143 @@ +# 参考信息 + +## 关于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 diff --git a/zh-cn/device-dev/subsystems/subsys-build-standard-large.md b/zh-cn/device-dev/subsystems/subsys-build-standard-large.md deleted file mode 100644 index 8c124330ebf6a5b7b201eb810cf6d859ace1b254..0000000000000000000000000000000000000000 --- a/zh-cn/device-dev/subsystems/subsys-build-standard-large.md +++ /dev/null @@ -1,807 +0,0 @@ -# 标准系统编译构建指导 - -## 概述 - -编译构建子系统提供了一个基于gn和ninja的编译构建框架。主要提供以下功能: - -- 构建不同芯片平台的产品。如:hispark_taurus_standard平台。 - -- 根据产品配置,按照部件组装并打包产品特性的能力。 - -### 基本概念 - -在了解编译构建子系统的能力前,应了解如下基本概念: -- 平台 - - 开发板和内核的组合,不同平台支持的子系统和部件不同。 - -- 子系统 - - OpenHarmony整体遵从分层设计,从下向上依次为:内核层、系统服务层、框架层和应用层。系统功能按照“系统 > 子系统 > 部件”逐级展开,在多设备部署场景下,支持根据实际需求裁剪某些非必要的子系统或部件。子系统是一个逻辑概念,它具体由对应的部件构成。 - -- 部件 - - 对子系统的进一步拆分,可复用的软件单元,它包含源码、配置文件、资源文件和编译脚本;能独立构建,以二进制方式集成,具备独立验证能力的二进制单元。 - -- gn - - Generate ninja的缩写,用于产生ninja文件。 - -- ninja - - ninja是一个专注于速度的小型构建系统。 - -### 运作机制 - -OpenHarmony侧的编译构建流程主要包括编译命令行解析,调用gn,执行ninja: - -- 命令行解析:解析待编译的产品名称,加载相关配置。 - -- 调用gn: 根据命令行解析的产品名称和编译类型,配置编译工具链和全局的编译选项。 - -- 执行ninja:启动编译并生成对应的产品版本。 - -### 约束与限制 - -- 需按照[源码获取](../get-code/sourcecode-acquire.md)指导下载全量源码(采用方式三获取)。 - -- 编译环境需要Ubuntu18.04及以上版本。 - -- 安装编译所需的程序包。 - 安装命令: - - ``` - # 推荐使用脚本,在家目录执行 - # ./build/build_scripts/env_setup.sh - # 不要用root执行,会将环境变量加到root。如果你的sheel不是bash或者zsh,执行完还需要你手动配置以下内容到你的环境变量中,cd ~到你用户家目录查看隐藏文件能显示你的用户环境变量文件。 - # 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=/root/.local/bin:$PATH - - # 如果不用脚本执行,则需要安装以下内容 - 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 python2.7 gcc-arm-none-eabi vim ssh locales doxygen - # python需要安装以下模块 - 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的配置内容导入到你用户的环境变量中 - ``` - - - -## 编译构建使用指导 - -### 目录结构 - -``` - -/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* - - -``` - -### 编译命令 - -- 代码根目录下执行全量版本的编译命令: - - ``` - - ./build.sh --product-name {product_name} - - ``` - - {product_name}为当前版本支持的平台。比如:hispark_taurus_standard等。 - - 编译完成后,结果镜像保存在 out/{device_name}/packages/phone/images/ 目录下。 - -- 编译命令支持选项:./build.sh -h - - ``` - -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个工作失败 - --sparse-image - --jobs=JOBS - --export-para=EXPORT_PARA - --build-only-gn # 只做gn解析,不运行ninja - --ccache # 可选 编译使用ccache,需要本地安装ccache - --fast-rebuild # 快速重建,default=False - --log-level=LOG_LEVEL # 指定编译期间的日志级别','三个级别可选:debug, info and error,default='info' - --device-type=DEVICE_TYPE # 指定设备类型,default='default' - --build-variant=BUILD_VARIANT # 指定设备操作模式,default='user' - - ``` - -### 开发步骤 - -1. 添加部件。 - 本节以添加一个自定义的部件为例,描述如何编译部件,编译库、编译可执行文件等。 - - 示例部件partA由feature1、feature2和feature3组成,feature1的编译目标为一个动态库,feature2的目标为一个可执行程序,feature3的目标为一个etc配置文件。 - - 示例部件partA的配置需要添加到一个子系统中,本次示例将添加到subsystem_examples子系统中(subsystem_examples子系统定义在test/examples/目录)。 - - 示例部件partA的完整目录结构如下: - - ``` - - 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,示例如下: - - ``` - - 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,示例如下: - - ``` - - 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,示例如下: - - ``` - - 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配置文件,在部件的根目录下。示例如下: - - ``` - { - "name": "@ohos/", # HPM部件英文名称,格式"@组织/部件名称" - "description": "xxxxxxxxxxxxxxxxxxx", # 部件功能一句话描述 - "version": "3.1", # 版本号,版本号与OpenHarmony版本号一致 - "license": "MIT", # 部件License - "publishAs": "code-segment", # HPM包的发布方式,当前默认都为code_segment - "segment": { - "destPath": "" - }, # 发布类型为code_segment时为必填项,定义发布类型code_segment的代码还原路径(源码路径) - "dirs": {}, # HPM包的目录结构,字段必填内容可以留空 - "scripts": {}, # HPM包定义需要执行的脚本,字段必填,值非必填 - "licensePath": "COPYING", # 指定该模块的版权申明路径 - "readmePath": { - "en": "README.rst" - }, # 该模块的reademe.opensource的路径 - "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": [], # 部件编译入口,模块在此处配置 - "inner_kits": [], # 部件间接口 - "test": [] # 部件测试用例编译入口 - } - } - } - ``` - -2. 将部件添加到产品配置中。 - 在产品的配置中添加部件,产品对应的配置文件://vendor/{product_company}/{product-name}/config.json。 - - 在产品配置文件中添加 "subsystem_examples:partA",表示该产品中会编译并打包partA到版本中。 - -3. 编译。 - 以编译hispark_taurus_standard为例,编译命令如下: - - ``` - - ./build.sh --product-name hispark_taurus_standard --ccache - - ``` - -4. 编译输出。 - 编译所生成的文件都归档在out/hispark_taurus/目录下,结果镜像输出在 out/hispark_taurus/packages/phone/images/ 目录下。 - -## 常见问题 - -### 如何将一个模块编译并打包到版本中? - -- 模块要指定part_name,指定它归属的部件,一个模块只能属于一个部件; - -- 部件的模块,要在部件配置的component.build.sub_component中,或者可以被component.build.sub_component中的模块依赖到; - -- 部件要加到对应产品的部件列表中。 - -### 关于deps、external_deps的使用 - -在添加一个模块的时候,需要在BUILD.gn中声明它的依赖,为了便于后续处理部件间依赖关系,我们将依赖分为两种——部件内依赖deps和部件间依赖external_deps。 - -### 依赖分类: - -- 1.部件内依赖: 现有模块module1属于部件part1,要添加一个属于部件part1的模块module2,module2依赖于module1,这种情况就属于部件内依赖。 - -- 2.部件间依赖: 现有模块module1属于部件part1,要添加一个模块module2,module2依赖于module1,module2属于部件part2。模块module2与模块module1分属于两个不同的部件,这种情况就属于部件间依赖。 - -- 部件内依赖示例: - - ``` - import("//build/ohos.gni") - ohos_shared_library("module1") { - …… - part_name = "part1" # 必选,所属部件名称 - …… - } - ``` - - ``` - import("//build/ohos.gni") - ohos_shared_library("module2") { - …… - deps = [ - "module1的gn target", - …… - ] # 部件内模块依赖 - part_name = "part1" # 必选,所属部件名称 - } - ``` - -- 部件间依赖示例: - - ``` - import("//build/ohos.gni") - ohos_shared_library("module1") { - …… - part_name = "part1" # 必选,所属部件名称 - …… - } - ``` - -- 模块1所属部件的bundle.json文件 - - ``` - { - "name": "@ohos/", # HPM部件英文名称,格式"@组织/部件名称" - "description": "xxxxxxxxxxxxxxxx", # 部件功能一句话描述 - "version": "3.1", # 版本号,版本号与OpenHarmony版本号一致 - "license": "MIT", # 部件License - "publishAs": "code-segment", # HPM包的发布方式,当前默认都为code_segment - "segment": { - "destPath": "" - }, # 发布类型为code_segment时为必填项,定义发布类型code_segment的代码还原路径(源码路径) - "dirs": {}, # HPM包的目录结构,字段必填内容可以留空 - "scripts": {}, # HPM包定义需要执行的脚本,字段必填,值非必填 - "licensePath "licensePath": "COPYING", - ": "COPYING", - "readmePath": { - "en": "README.rst" - }, - "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": ["part1"], # 部件编译入口,部件所有模块在此列表 - "inner_kits": [ # 部件间接口 - { - "header": { - "header_base": "头文件所属目录", # 头文件所属目录 - "header_files": [ - "头文件名" - ] # 头文件名列表 - }, - "name": "module1的gn target" - }, - ], - "test": [] # 部件测试用例编译入口 - } - } - } - ``` - - ``` - import("//build/ohos.gni") - ohos_shared_library("module2") { - …… - external_deps = [ - "part1:module1", - …… - ] # 部件间模块依赖,这里依赖的模块必须是依赖的部件声明在inner_kits中的模块 - part_name = "part2" # 必选,所属部件名称 - } - - ``` - -> ![icon-caution.gif](public_sys-resources/icon-caution.gif) **注意:** -> 部件间依赖要写在external_deps里面,格式为”部件名:模块名"的形式,并且依赖的模块必须是依赖的部件声明在inner_kits中的模块。 - - - - - -### 标准系统如何添加一个模块 - -要添加的模块可以分为以下三种情况,对原有的配置文件进行不同程度的修改。 - -- 在原有部件中添加一个模块 - -- 新建部件并在其中添加模块 - -- 新建子系统并在该子系统的部件下添加模块 - -**在原有部件中添加一个模块** - -1. 在模块目录下配置BUILD.gn,根据类型选择对应的模板。 - 这一步与在原有部件中添加一个模块的方法基本一致,只需注意该模块对应BUILD.gn文件中的part_name为新建部件的名称即可。 - -2. 修改bundle.json配置文件。"部件包含模块的gn目标" - - ``` - { - "name": "@ohos/", # 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": "", # 部件名称 - "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": [] # 部件测试用例编译入口 - } - } - } - ``` - - 注意无论哪种方式该bundle.json文件均在对应子系统所在文件夹下。 - -**新建部件并在其中添加一个模块** - -1. 在模块目录下配置BUILD.gn,根据类型选择对应的模板。这一步与新建部件并在其中添加模块中对应的步骤并无区别。 - -2. 新建一个bundle.json文件,bundle.json文件均在对应子系统所在文件夹下。bundle.json文件包含两个部分,第一部分subsystem说明了子系统的名称,parts定义了该子系统包含的部件,要添加一个部件,需要把该部件对应的内容添加进parts中去。添加的时候需要指明该部件包含的模块sub_component,假如有提供给其它部件的接口,需要在inner_kits中说明,假如有测试用例,需要在test中说明,inner_kits与test没有也可以不添加。 - -2. 在//vendor/{product_company}/{product-name}/config.json中添加对应的部件,直接添加到原有部件后即可。 - - ``` - "subsystems": [ - { - "subsystem": "部件所属子系统名", - "components": [ - { "component": "部件名1", "features":[] }, # 子系统下的原有部件1 - { "component": "部件名2", "features":[] }, # 子系统下的原有部件2 - { "component": "部件名new", "features":[] } # 子系统下的新增部件new - ] - }, - ....... - ] - ``` - -**新建子系统并在该子系统的部件下添加模块** - -1. 在模块目录下配置BUILD.gn,根据类型选择对应的模板。这一步与新建部件并在其中添加模块中对应的步骤并无区别。 - -2. 在新建的子系统目录下每个部件对应的文件夹下创建bundle.json文件,定义部件信息。这一步与新建部件并在其中添加模块中对应的步骤并无区别。 - -3. 修改build目录下的subsystem_config.json文件。 - - ``` - { - "子系统名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中添加对应的部件,直接添加到原有部件后即可。 - - ``` - "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": [] - } - ] - }, - ... - ] - ``` - 成功添加验证: - - 在输出文件夹的对应子系统文件夹下的部件文件夹下的BUILD.gn文件中module_list包含了新建模块的BUILD.gn中定义的目标。 - - 编译完成后打包到image中去,生成对应的so文件或者二进制文件。 - -**配置文件说明** - -OpenHarmony的配置文件主要有四个。 - -1. vendor\产品厂商\产品名\config.json - - ``` - { - "product_name": "MyProduct", - "version": "3.0", - "type": "standard", - "target_cpu": "arm", - "ohos_version": "OpenHarmony 1.0", - "device_company": "MyProductVendor", - "board": "MySOC", - "enable_ramdisk": true, - "subsystems": [ - { - "subsystem": "ace", - "components": [ - { "component": "ace_engine_lite", "features":[""] } - ] - }, - ... - ] - } - - ``` - 指明了产品名,产品厂商,产品设备,版本,要编译的系统类型,以及产品包含的子系统。 - -2. build目录下的subsystem_config.json文件。 - - ``` - { - "arkui": { - "path": "foundation/arkui", - "name": "arkui" - }, - "ai": { - "path": "foundation/ai", - "name": "ai" - }, - ...... - } - ``` - 该文件对子系统进行了说明,我们需要该子系统定义中的name与path,分别表示子系统的名称和所在文件夹路径。 - -3. 子系统中bundle.json文件。 - - ``` - { - "name": "@ohos/", # HPM部件英文名称,格式"@组织/部件名称" - "description": "xxxxxxxxxxxxxxxxxxx", # 部件功能一句话描述 - "version": "3.1", # 版本号,版本号与OpenHarmony版本号一致 - "license": "MIT", # 部件License - "publishAs": "code-segment", # HPM包的发布方式,当前默认都为code_segment - "segment": { - "destPath": "" - }, # 发布类型为code_segment时为必填项,定义发布类型code_segment的代码还原路径(源码路径) - "dirs": {}, # HPM包的目录结构,字段必填内容可以留空 - "scripts": {}, # HPM包定义需要执行的脚本,字段必填,值非必填 - "licensePath": "COPYING", - "readmePath": { - "en": "README.rst" - }, - "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": ["部件包含模块的gn目标"], # 部件编译入口 - "inner_kits": [], # 部件间接口 - "test": [] # 部件测试用例编译入口 - } - } - } - ``` - bundle.json文件定义了子系统包含的部件。 - - 每个部件定义它所包含的模块目标component.build.sub_component,以及部件间交互的接口component.build.inner_kits,测试用例component.build.test_list。部件包含的 - 模块目标component.build.sub_component是必须要说明的。 - -4. 每个模块对应的BUILD.gn文件。 - 可以使用提供的模板,也可以使用gn语法规则自定义编写。 - -### hap的编译 - -**hap包的构成** - -OpenHarmony上的hap包由资源,raw assets,js assets,native库,config.json等部分构成。 - -**编译系统提供的模板** - -编译系统提供了4个模板,用来编译hap包。 - -模板集成在ohos.gni中,使用之前需要引用build/ohos.gni - -ohos_resources - -- 声明一个资源目标。资源目标被restool编译之后会生成index文件,hap中会打包资源源文件和index文件。 - -- 该目标会同时生成资源编译后的ResourceTable.h,直接依赖该目标就可以引用该头文件 - -- 资源目标的目标名必须以"resources"或"resource"或"res"结尾,否则编译检查时会报错 - -- 支持的变量: - - 1. sources: 资源的路径,变量类型是list,可以写多个路径 - 2. hap_profile: 编译资源时需要提供对应hap包的config.json - 3. deps: 当前目标的依赖,可选 - -ohos_assets - -- 声明一个资产目标 - -- 注意拼写:assets不是assert - -- assets目标的目标名必须以"assets"或"asset"结尾 - -- 支持的变量: - - 1. sources:raw assets所在路径,变量类型是list,可以写多个路径 - 2. deps: 当前目标的依赖,可选 - -ohos_js_assets - -- 声明一个JS 资源目标,JS资源是L2 hap包的可执行部分 - -- JS assets目标的目标名必须以"assets"或"asset"结尾 - -- 支持的变量: - - 1. source_dir: JS 资源的路径,变量类型是string,只能写一个 - 2. deps: 当前目标的依赖,可选 - -ohos_hap - -- 声明一个hap目标,该目标会生成一个hap包,最终将会打包到system镜像中 - -- 支持的变量: - - 1. hap_profile: hap包的config.json - 2. deps: 当前目标的依赖 - 3. shared_libraries: 当前目标依赖的native库 - 4. hap_name: hap包的名字,可选,默认为目标名 - 5. final_hap_path: 用户可以制定生成的hap的位置,可选,final_hap_path中会覆盖hap_name - 6. subsystem_name: hap包从属的子系统名,需要和bundle.json中的名字对应,否则将导致无法安装到system镜像中 - 7. part_name: hap包从属的部件名,同subsystem_name - 8. js2abc: 是否需要将该hap包转换为ARK的字节码 - 签名篇见:[配置应用签名]( https://developer.harmonyos.com/cn/docs/documentation/doc-guides/ohos-debugging-and-running-0000001263040487#section17660437768) - 9. certificate_profile: hap对应的授权文件,用于签名 - 10. certificate_file: 证书文件,证书文件和授权文件,应用开发者需要去openharmony官网申请 - 11. keystore_path: keystore文件,用于签名 - 12. keystore_password: keystore的密码,用于签名 - 13. key_alias: key的别名 - 14. module_install_name:安装时的hap包名称 - 15. module_install_dir: 安装到system中的位置,默认安装在system/app目录下 - - **hap开发示例** - - ``` - import("//build/ohos.gni") # 引用ohos.gni - ohos_hap("clock") { - hap_profile = "./src/main/config.json" # config.json - deps = [ - ":clock_js_assets", # JS assets - ":clock_resources", # 资源 - ] - shared_libraries = [ - "//third_party/libpng:libpng", # native库 - ] - certificate_profile = "../signature/systemui.p7b" # Cer文件 - hap_name = "SystemUI-NavigationBar" # 名字 - part_name = "prebuilt_hap" - subsystem_name = "applications" - } - ohos_js_assets("clock_js_assets") { - source_dir = "./src/main/js/default" - } - ohos_resources("clock_resources") { - sources = [ "./src/main/resources" ] - hap_profile = "./src/main/config.json" - } - ``` - -### 开源软件Notice收集策略说明 - -**收集目标** - -只收集打包到镜像里面的模块对应的License;不打包的都不收集,比如构建过程使用的工具(如clang/python/ninja等)。 - -静态库本身是不会被打包的,一般是作为动态库或者可执行程序的一部分被打包到系统中的,为了确保完备,静态库的都会收集。 - -最终合并的NOTICE.txt要体现出镜像中每个文件都是用了哪些License,模块和License要有对应关系。 - -最终合并的NOTICE.txt文件在/system/etc/ 目录下。 - -**收集规则** - -按照优先级收集License - -1. 模块在BUILD.gn中直接声明自己使用的License文件,优先级最高。如下图示例: - - ``` - 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。 - -需要注意及检查的问题 - -1. 三方的开源软件,比如openssl,icu等,这部分软件基本上在源码目录下都是要求配置Readme.OpenSource,要检查Readme.OpenSource文件是否和BUILD.gn文件在同一个目录,以及Reame.OpenSource文件中配置的License文件是否存在以及真是有效。 - -2. 代码目录下,如果代码使用的不是Apache2.0 License,需要在目录下提供对应的License文件,或者直接在模块中指定license_file。 - -3. 如果BUILD.gn中添加的源码文件不是当前目录的,需要检查下源码文件所在仓下的license是否和BUILD.gn文件所在仓的一致,不一致的需要处理。 \ No newline at end of file diff --git a/zh-cn/device-dev/subsystems/subsys-build-subsystem.md b/zh-cn/device-dev/subsystems/subsys-build-subsystem.md new file mode 100644 index 0000000000000000000000000000000000000000..6314ee0bd648aac5957e9e141bfad0f7e0598b1e --- /dev/null +++ b/zh-cn/device-dev/subsystems/subsys-build-subsystem.md @@ -0,0 +1,32 @@ +# 子系统 +### 子系统配置规则 + +通过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中指定子系统的路径和子系统名称。 diff --git a/zh-cn/device-dev/subsystems/subsys-build.md b/zh-cn/device-dev/subsystems/subsys-build.md index 1652a6cf1a7fea85f54e9603dcd9944cce51aaaf..e617d4d7af161c0df2df707249d5961d5bc9ef54 100644 --- a/zh-cn/device-dev/subsystems/subsys-build.md +++ b/zh-cn/device-dev/subsystems/subsys-build.md @@ -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)** - **[编译构建Kconfig可视化配置指导](subsys-build-gn-kconfig-visual-config-guide.md)** +- **[参考信息](subsys-build-reference.md)** +- **[FAQ](subsys-build-faq.md)**