Openharmony 编译框架

简介

编译构建子系统提供了一个基于Gn和ninja的编译构建框架。根据产品配置,编译生成对应的镜像包。其中编译构建流程为

preloader->loader->gn->ninja

  1. build文件夹下的subsystem_config.json文件,主要包含子系统名称与路径信息,在preloader阶段被加载,根据子系统名称和路径信息查找该路径下的bundle.json和ohos.build文件。

    加载vendor/厂商/设备下的config.json,配置文件主要包含产品名称,产品厂商,产品设备名,产品类型,产品对应子系统路径,产品所包含的部件等信息。

  2. 使用Gn配置构建目标。

  3. Gn运行后会生成ninja文件。

  4. 通过运行ninja来执行编译任务。

 

约束与限制

- 编译环境需要Ubuntu18.04及以上版本。

- 安装编译所需的程序包。

安装命令:

sudo apt-get install bison ccache default-jdk flex gcc-arm-linux-gnueabi gcc-arm-none-eabi genext2fs liblz4-tool libssl-dev libtinfo5 mtd-utils mtools openssl ruby scons unzip u-boot-tools zip

 

编译方式和命令选项

编译方式分为源码根目录的build.sh执行脚本和hb命令方式,最终编译逻辑都会走build子系统下的hb模块。

1、build.sh执行命令选项

  -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               # 指定操作系统 ohos,android,ios,linux,chromeos
  -T BUILD_TARGET, --build-target=BUILD_TARGET    # 指定编译目标,可以指定多个模块
  --gn-args=GN_ARGS                 # gn参数,支持指定多个
  --ninja-args=NINJA_ARGS              # ninja参数,支持指定多个
    --ninja-args=-v                 # 可以在build.log里面查看每一个编译目标的详细编译命令
    --ninja-args=-dkeeprsp          # 该命令在编译目标报错且会生成rsp文件时,保留目标的rsp文件
  --keep-ninja-going                 # 让ninja持续到1000000个工作失败
  --jobs=JOBS                           # 指定编译线程数
  --export-para=EXPORT_PARA         #Deprecated废弃
  --build-only-gn                       # 只做gn解析,不运行ninja
  --ccache                              # 可选  编译使用ccache,需要本地安装ccache,未变化的部件则不会执行编译
  --fast-rebuild                        # 快速重建,default=False,直接使用ninja编译,跳过产品和gn解析
  --log-level=LOG_LEVEL               # 指定编译期间的日志级别','三个级别可选:debug, info and error,default='info'
  --device-type=DEVICE_TYPE             # 指定设备类型,default='default',用于固件适配
  --build-variant=BUILD_VARIANT           # 指定设备操作模式(root,user),默认是root,设为user将关闭设备调试模式

 

2、使用hb工具编译

OH3.2 安装:

python3 -m pip install --user ohos-build==0.4.6

OH4.x 安装:

python3 -m pip install --user build/hb

 

hb工具使用说明:

  • hb set:设置要编译的产品

  • hb env:查看当前设置信息

  • hb clean:清除 out 目录对应产品的编译产物,仅保留 args.gn、build.log

  • hb build:编译产品、开发板或者部件

  • hb build 后无参数,会按照设置好的代码路径、产品进行编译,编译选项使用与之前保持一致。-f 选项将删除当前产品所有编译产品,等同于 hb clean + hb build。

  • hb build {component_name},基于设置好的产品对应的单板、内核,单独编译组件,例如:hb build kv_store。

  • hb build -p ipcamera@hisilicon,免 set 编译产品,该命令可以跳过 set 步骤,直接编译产品。

 

 

子系统介绍

OpenHarmony系统功能层级由子系统->部件->组件依次展开,每个子系统下可以包含多个部件,每个部件可以可以包含多个组件,组件即最小编译单元。

1、oh子系统配置

OH子系统配置在build/subsystem_config.json中,包含oh底座所有的子系统,通常不需要去修改。

productdefine目录下是针对产品的子系统配置,如果在subsystem_config.json中没有配置某子系统,productdefine下的配置又添加了该子系统,会导致编译错误。

#build/subsystem_config.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"
  },
  "useriam": {
    "path": "base/useriam",
    "name": "useriam"
  },
  ......

2、产品子系统特性化配置

产品编译入口配置文件:vendor/厂商/产品/config.json

{
  "product_name": "laphone",    //编译时输入的产品名
  "device_company": "hys",
  "device_build_path": "device/board/hys/laphone",      //关联device目录下的设备子系统,device下对应也配置了ohos.build
  "target_cpu": "arm64",
  "type": "standard",   //产品类型
  "version": "3.0",
  "board": "laphone",   //device_name,编译结果out/$device_name/
  "api_version": 8,
  "enable_ramdisk": true,
  "build_selinux": false,
  "build_seccomp": false,
  "device_stack_size": 8388608,
  "inherit": [ "productdefine/common/inherit/rich.json", "productdefine/common/inherit/chipset_common.json" ],  //关联的OH系统中的子系统部件配置
  "subsystems": [
    {
      "subsystem": "security",
      "components": [
        {
          "component": "selinux_adapter",
          "features": [     //部件特性配置
            "selinux_enforce = false"
          ],
          "syscap": [   //部件系统能力配置
            ...
          ]
        }
      ]
    },
    ...
  ]

配置vendor/厂商/产品/目录的子系统部件

#vendor/厂商/产品/ohos.build
{
  "subsystem": "产品子系统名",
  "parts": {
    "部件名1": {
      "module_list": [
        模块1,
        模块2,
        ...
      ]
    },
    "部件名2":{
      "module_list": [
        模块1,
        模块2,
        ...
      ]
    },
    ...
  }
}

配置产品特性化的子系统部件

 

3、子系统部件配置

为OH底座子系统配置部件

#productdefine/common/base/standard_system.json
{
  "subsystems": [
    {
      "subsystem": "hdf",
      "components": [
        { "component": "hdf_core" },
        { "component": "drivers_interface_huks" },
        { "component": "drivers_peripheral_huks" }
      ]
    },
    {
      "subsystem": "communication",
      "components": [
        { "component": "ipc" },
        { "component": "dsoftbus" }
      ]
    },
    ......

 

子系统部件配置文件

#   bundle.json
   {
       "name": "@ohos/<component_name>",                 # HPM部件英文名称,格式"@组织/部件名称"
       "description": "xxxxxxxxxxxxxxxxxxx",             # 部件功能一句话描述
       "version": "3.1",                                 # 版本号,版本号与OpenHarmony版本号一致
       "license": "Apache License 2.0",                  # 部件License
       "publishAs": "code-segment",                      # HPM包的发布方式,当前默认都为code_segment
       "segment": {
           "destPath": ""
       },                                                # 发布类型为code_segment时为必填项,定义发布类型code_segment的代码还原路径(源码路径)
       "dirs": {},                                       # HPM包的目录结构,字段必填内容可以留空
       "scripts": {},                                    # HPM包定义需要执行的脚本,字段必填,值非必填
       "component": {                                    # 部件属性
           "name": "<component_name>",                   # 部件名称
           "subsystem": "",                          `    # 部件所属子系统
           "syscap": ["SystemCapability.Ai.AiEngine"],   # 部件为应用提供的系统能力
           "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": [
                    {
                    
                    }
               ],                         # 部件间接口
               "inner_api": [
               {
                   "type":"so"
                   "name":"//foundation/ai/neural_network_runtime:nnrt_target"
                   "header": {
                            "header_files": [],
                            "header_base":"//foundation/ai/neural_network_runtime/interfaces/innerkits/c"
                        }
                   }
               ],                         # 系统内部件间接口
               "test": []                                # 部件测试用例编译入口
           }
       }
    }

 

4、组件(模块)配置

oh系统部件包含的组件配置文件BUILD.gn介绍

# C/C++模板
ohos_shared_library #编译生成动态库
ohos_static_library #编译生成静态库
ohos_executable     #编译生成可执行文件
ohos_source_set     #资源集合
ohos_unittest   
​
# 预编译模板,使用已生成的二进制文件:
ohos_prebuilt_executable        
ohos_prebuilt_shared_library
ohos_prebuilt_static_library
​
#hap模板
ohos_hap
ohos_app_scope  #下面几项都服务于ohos_hap
ohos_js_assets
ohos_resources
​
#其他常用模板
ohos_prebuilt_etc   #用于安装配置文件
ohos_copy
​
#sa配置
ohos_sa_profile
​
#其他常用模版
group           #模块组合,可将一个组合作为一个组件
config          #配置项,可用于其他模版的configs配置项
action          #行为执行,可用于执行脚本
​
print           #调试用打印
foreach
...

 

ohos_shared_library示例:

import("//build/ohos.gni")
ohos_shared_library("helloworld") {
  sources = ["file"]            # 包含的C或C++文件,如:["",""]
  include_dirs = []             # 如有重复头文件定义,优先使用前面路径头文件
  cflags = []                   # 指定工具链编译参数,对c、c++源码均生效,如重复冲突定义,后面的参数优先生效,也就是该配置项中优先生效
  cflags_c = []                 # 仅c源码均生效
  cflags_cc = []                # 仅c++源码均生效
  ldflags = []                  # 用于链接阶段的参数,如重复冲突定义,前面参数优先生效,也就是ohos_template中预制参数优先生效
  configs = []                  # 配置项,应用于当前目标
  public_configs = []           # 配置项,应用于所有依赖当前配置的目标
  deps = []                     # 部件内模块依赖,用于添加同一部件下的模块
​
  external_deps = [             # 跨部件模块依赖定义,"部件名:模块名称"
  ]                             # 这里依赖的模块必须是依赖的部件声明在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                    #文件输出路径
​
​

 

新建部件并在其中添加模块

  1. 在模块目录下配置BUILD.gn,根据类型选择对应的模板。

这一步与在原有部件中添加一个模块的方法基本一致,只需注意该模块对应BUILD.gn文件中的part_name为新建部件的名称即可。

 

  1. 修改或者新建ohos.build配置文件,参考vendor/hihope/rk3568/ohos.build

  {
     "subsystem": "子系统名",
     "parts": {
       "新建部件名": {
         "module_list": [
           "部件包含模块的gn目标"
         ],
         "inner_kits": [
         ],
         "test_list": [
           "测试用例",
         ]
       }
     }
   }
​
​

当前oh部件配置文件已基本使用bundle.json,ohos.build配置文件已基本被替代。

 

新建子系统并在该子系统的部件下添加模块

  1. 在模块目录下配置BUILD.gn,根据类型选择对应的模板。

  2. 在新建的子系统目录下每个部件对应的文件夹下创建bundle.json文件,定义部件信息。这一步与新建部件并在其中添加模块中对应的步骤并无区别。

  3. 修改build目录下的subsystem_config.json文件

{
    "子系统名": {
        "path": "子系统目录",
        "name": "子系统名",
        ...
    }
}

该文件定义了有哪些子系统以及这些子系统所在文件夹路径,添加子系统时需要说明子系统path与name,分别表示子系统路径和子系统名。

  1. 在子系统目录下添加需要创建的部件配置文件bundle.json。

  2. 添加部件下的模块配置BUILD.gn文件,将模块添加到bundle.json中。

  1. 在vendor/hihope/rk3568/config.json中添加对应的部件,可实现特性化定制。

      "subsystems": [
        {
          "subsystem": 子系统名,
          "components": [
            {
              "component": 部件名,
              ...
            }
          ]
        }

 

成功添加验证:

+ 在输出文件夹的对应子系统文件夹下的部件文件夹下的BUILD.gn文件中module_list包含了新建模块的BUILD.gn中定义的目标。

+ 编译完成后打包到image中去,生成对应的so文件或者二进制文件

 

部件化编译实践

1. 使用feature配置进行部件差异化配置

feature主要用于描述部件在不同产品下的差异化行为。产品在装配部件时,可以通过配置不同的feature来使用部件不同的特性集。

1.1 产品如何配置部件的feature

当前系统两个版本的产品配置文件格式,配置方法分别如下:

- V3产品配置文件

vendor/xxx/config.json目录下V3版本产品配置文件中为部件配置不同feature的示例如下:

{
 ...
 "subsystems": [
  "subsystem": "subsystemName",
   "components": [{
    "component": "partName",
    "features":[ "{partName}_feature_A=false" ]
   }]
 }]
}

 

1.2 部件开发人员如何使用feature进行部件的特性隔离

下面介绍feature的声明、定义以及使用方法。

在部件的bundle.json文件中通过feature_list来声明部件的feature列表,每个feature都必须以"*{部件名}_*"开头。示例如下:

{
 "name": "@ohos/xxx",
 "component": {
  "name": "partName",
  "subsystem": "subsystemName",
  "features": [
   "{partName}_feature_A"   // features中可以为部件声明多个feature。
  ]
 }
}
​
​

在部件内可通过以下方式定义feature的默认值,一般是在gni头文件中:

 declare_args() {
  {partName}_feature_A = true
 }

 

该值是此部件的默认值,产品可以在部件列表中重载该feature的值。

feature需给部件内多个模块使用时,建议把feature定义在部件的全局gni文件中,各个模块的BUILD.gn中import该gni文件。

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"]
 }

- 可裁剪模块使用独立BUILD.gn文件,该模块的BUILD.gn不能和其它必选模块混合在同一个BUILD.gn脚本中。

 

2. 如何判断当前产品中是否包含某个依赖的部件

如果部件A/B/C/D等都依赖部件O,而部件O可被裁减;则部件A/B/C/D等都需要定义裁减部件O时的隔离feature。在这种情况下,产品配置人员裁减部件O时,还需要手动关闭部件A/B/C/D为支持裁减部件O所定义的feature开关,装配时比较麻烦。

为了解决该问题,编译框架将当前产品支持的所有部件都加载到了global_parts_info全局变量中,每个部件的编译脚本中可以通过部件名称判断该部件是否被裁减,然后自动修改部件的feature默认值,实现部件feature默认值可自动跟随裁减部件而同步更新。

declare_args() {
 "{partName}_feature_A" = true
 if (!defined(global_parts_info.{subsystem_O}_{partName_O})) {  //子系统名+部件名
  {partName}_feature_A = false
 }
}

如上图所示,部件A的{partName}_feature_A特性默认为true,如果部件O被裁减,则该feature自动配置为false。

 

3. inner_kits模块裁剪处理策略

inner_kits是部件之间依赖的接口集合,部件内的模块及特性裁剪不能影响inner_kits接口:

- 所属部件没被裁剪,但内部有部分模块被裁剪:

此场景要求inner_kits接口不能有任何缺失,确保依赖该inner_kits的外部模块可正常编译,运行时可以内部实现上有差异。

- 所属部件被裁剪:

依赖该inner_kits的模块需要一起被裁剪。

 

4. napi模块需支持统一裁剪方式

部分IoT无屏产品不支持应用安装,不需要编译system_kits提供JS API。因此,每个子系统的napi模块需保证可独立裁剪。其裁剪方式通过全局的support_jsapi开关进行隔离。

4.1 产品如何配置关闭所有的jsapi模块编译

support_jsapi的默认值为true,产品配置中可通过以下方法来修改:

vendor/xxx/config.json目录下的V3版本产品配置文件配置示例如下:

{
  "product_name": "productName",
  "version": "3.0",
  "device_company": "xxx",
  "support_jsapi": false,
  ...
}

 

4.2 部件开发人员如何使用support_jsapi进行部件的特性隔离

support_jsapi是一个全局的feature,每个部件都可以参考1.2章的方式使用进行代码隔离。

group("jsapi_group") {
 deps = []
 if (support_jsapi) {
  deps += [ "real_jsapi_module" ]
 }
}

 

5、配置系统能力

5.1 部件配置系统能力

部件配置系统能力是为了方便某个特定部件是否要打开或关闭特定的系统能力。

部件配置系统能力的位置在部件目录下的bundle.json,配置示例如下所示:

 "component": {
    "name": "wifi",
    "subsystem": "communication",
    "syscap": [
      "SystemCapability.Communication.WiFi.STA = true",
      "SystemCapability.Communication.WiFi.AP = true",
      "SystemCapability.Communication.WiFi.P2P = false",
      "SystemCapability.Communication.WiFi.Core = false",
      "SystemCapability.Communication.WiFi.HotspotExt"
     ]
    ],
    ...
 }

在component下加入syscap这一关键字,内部配置相应的系统能力,系统能力若无赋值,则默认为true,若有赋值,则按实际值为准。若值为true,则表示该部件默认开启此系统能力,若值为false,则表明该部件默认关闭此系统能力。

SystemCapability是面向北向应用开发的能力呈现。

 

5.2 产品配置系统能力

产品配置系统能力是为了方便某个特定产品是否要打开或关闭特定的系统能力,若无配置,则以部件侧的配置为准,产品配置系统能力的位置在vender/{company}/{product}/config.json。

 

如果要对产品的系统能力进行精细化配置,可在产品配置中加入syscap关键字,并对要配置的系统能力赋值。产品侧的配置优先级大于部件系统能力默认配置,若某一个系统能力在部件侧默认配置为false,在产品侧配置为true,则这个系统能力的最终配置为true。示例如下:

{
   "subsystem": "communication",
   "components": [
    ...
    {
     "component": "wifi",
     "features": [],
     "syscap": [
      "SystemCapability.Communication.WiFi.AP = false",
      "SystemCapability.Communication.WiFi.P2P = true",
      "SystemCapability.Communication.WiFi.HotspotExt = false"
     ]
    },
    ...

 

生成镜像

生成镜像由//build/ohos/images/mkimage模块完成。

含文件系统的镜像配置在build/ohos/images/mkimage下的 ” 镜像名_image.conf.txt “,文件配置包含挂载路径,镜像大小,文件系统类型等。厂商定制配置在vendor产品下的image_conf目录下。

build/ohos/images/mkimage/system_image_conf.txt

/
1610612224
--fs_type=ext4
#可选配置
--dac_config ../../build/ohos/images/mkimage/dac.txt    // path uid gid cap
--file_context obj/base/security/selinux/file_contexts.bin  //base selinux 文件安全策略
Logo

社区规范:仅讨论OpenHarmony相关问题。

更多推荐