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模块。

命令选项:

  -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                        # 快速重建,default=False,直接使用ninja编译,跳过产品和gn解析
  --log-level=LOG_LEVEL               # 指定编译期间的日志级别','三个级别可选:debug, info and error,default='info'
  --device-type=DEVICE_TYPE             # 指定设备类型,default='default',用于固件适配
  --build-variant=BUILD_VARIANT           # 指定设备操作模式,default='user'

 

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 = []                   # 如重复冲突定义,后面的参数优先生效,也就是该配置项中优先生效
  cflags_c = []
  cflags_cc = []
  ldflags = []                  # 如重复冲突定义,前面参数优先生效,也就是ohos_template中预制参数优先生效
  configs = []                  # 配置项,应用于当前目标
  public_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                    #文件输出路径
​
​

 

子系统配置

oh子系统配置在build/subsystem_config.json中,子系统目录下需要存在 bundle.json文件或者ohos.build文件,是该目录组件作为子系统编译的先决条件。

*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": [],                         # 部件间接口
               "test": []                                # 部件测试用例编译入口
           }
       }
    }

 

 

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

  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. 在vendor/hihope/rk3568/config.json中添加对应的部件,可实现特性化定制。

      "subsystems": [
        {
          "subsystem": 子系统名,
          "components": [
            {
              "component": 部件名,
              "features": []    //特性化定制
            }
          ]
        }

 

成功添加验证:

+ 在输出文件夹的对应子系统文件夹下的部件文件夹下的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.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下的镜像名_image.conf.txt,每个系统镜像文件都是由一个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 文件安全策略
  • 18
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
OpenWrt是一个基于Linux操作系统的嵌入式系统,它允许用户自定义编译固件以满足各种需求。OpenWrt的编译框架提供了完整的编译环境和工具链,使用户能够根据自己的需求添加、删除和定制软件包,并生成适用于各种硬件平台的定制固件。 OpenWrt的编译框架主要包括以下几个组成部分: 1. 代码仓库: OpenWrt的代码仓库包含了所有的源代码和软件包。用户可以从代码仓库中获取最新的源代码,并根据需要选择所需的软件包。 2. 配置文件: OpenWrt的编译框架提供了一个默认的配置文件,用户可以根据自己的需求进行修改。配置文件包含了各种选项,如目标硬件平台、默认安装的软件包等。 3. Makefile: OpenWrt使用Makefile来管理编译过程。Makefile中定义了编译的规则和依赖关系,使得用户可以方便地控制编译过程。 4. 工具链: OpenWrt的编译框架使用交叉编译的方式进行编译。它提供了一个针对目标硬件平台的工具链,包括编译器、链接器、调试器等工具,使得用户可以在主机上进行编译和调试。 5. 定制化选项: OpenWrt的编译框架允许用户根据自己的需求进行软件包的自定义。用户可以选择添加自己的软件包,或者根据需要删除不需要的软件包。 总之,OpenWrt的编译框架提供了一个灵活和易于使用的环境,使用户能够根据自己的需求生成定制的嵌入式系统固件。无论是个人用户还是企业用户,都可以根据OpenWrt的编译框架进行定制和开发,以满足各种特定的应用场景。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值