OpenHarmony构建系统-GN与子系统、部件、模块理论与实践

news/2025/2/23 13:21:28

理论

OpenHarmony源码体系

        OpenHarmony的源码架构基于模块化设计,为了方便系统的功能的增加和裁剪,设计了基于GN构建的模块系统。整个模块可从大到小划分为产品(product)、领域/子系统集(domain)、子系统(sub system)、部件(component)、模块/组件(module)、特性(feature)几个部分,这种模块化的树状编译框架,非常方便根据目标产品硬件资源的大小进行灵活的裁剪,从而实现统一OS,弹性部署的目标。

OpenHarmony系统架构

一个产品可以包含1~n个子系统(subsystem),一个子系统可以包含1~n个部件(component),一个部件可以包含1~n个模块(module),不同产品的中的相同部件可以编译不同的特性(feature),子系统集(domain)在源代码一级根目录有体现。

由于以上的特性,OpenHarmony只需要通过gn的配置实现无需删减代码达到裁剪系统的目的,提高系统整体稳定性。

产品(product)

产品解决方案为基于开发板的完整产品,主要包含产品对OS的适配、部件拼装配置、启动配置和文件系统配置等。源码路径为:/vender/{产品解决方案厂商}/{产品名称},使用命令tree -L 级数,如果tree -L 4

vender
 └──── company                          # 产品解决方案厂商,如:hihope
        ├──── product                   # 产品名称,如:rk3568
        │      ├──── init_configs
        │      │      ├──── etc         # init启动进程配置(可选,仅linux内核需要)
        │      │      └──── init.cfg    # 系统服务启动配置
        │      ├──── hals               # 产品解决方案OS适配
        │      ├──── BUILD.gn           # 产品编译脚本
        │      ├──── config.json        # 产品配置文件
        │      └──── fs.yml             # 文件系统打包配置
        ├──── ...

/vender/{产品解决方案厂商}/{产品名称}/init+configs/etc文件夹中包含rcS脚本、Sxxx脚本和fstab脚本。init进程在启动系统服务前执行这些脚本,执行流程为rcS—>fstab—>Sxxx,Sxxx脚本中的内容与开发板和产品需求有关,主要包含

产品配置规则:/vender/{产品解决方案厂商}/{产品名称}/config.json文件中可以配置产品所需系统的子系统,其中的inherit字段可以继承事先定义好的样板模板,模板位于productdefine/common目录下

领域/子系统集(domain)

OpenHarmony技术架构中有四大子系统集:“系统基本能力子系统集”、“基础软件服务子系统集”、“增强软件服务子系统集”、“硬件服务子系统集”。四大子系统不会直接出现在编译选项或者参数中,而是有对应的一级源代码文件夹:

“系统基本能力子系统集”对应源码foundation文件夹;“基础软件服务子系统集”和“硬件服务子系统集”对应源码base文件夹;“增强软件服务子系统集”对应源码domains文件夹。

├──applications                              # 应用程序
├──arkcompiler                               # ark编译器
├──base                                      # “基础软件服务子系统集”和“硬件服务子系统集”
├──build                                     # 编译目录
├──build.py -> build/lite/build.py           # 软链接
├──build.sh -> build/build_scripts/build.sh  # 软链接,标准系统编译入口
├──commonlibrary                             # 通用库
├──developtools                              # 开发工具
├──device                                    # 芯片相关
├──docs                                      # 文档md文件目录
├──domains                                   # 增强软件服务子系统集
├──drivers                                   # 驱动文件
├──foundation                                # “系统基本能力子系统集”
├──ide                                       # ide
├──interface                                 # 接口
├──kernel                                    # 内核,liteos-m,liteos-a,linux,uniproton
├──napi_generator                            # 代码生成工具
├──prebuilts                                 # 编译工具路径
├──productdefine                             # 产品定义
├──qemu-run -> vendor/ohemu/common/qemu-run  # qemu模拟器运行脚本
├──test                                      # 测试用例
├──third_party                               # 三方库
└──vendor                                    # 产品源码

子系统(SubSystem)

子系统是一个逻辑概念,它具体由对应的部件构成。在多设备部署场景下,支持根据实际需求裁剪某些非必要的子系统或部件。在build/subsystem_config.json中定义。

部件(component)

部件是对子系统的进一步拆分,可复用的软件单元,每一个部件单独存放一个文件夹,它包含源码、配置文件、资源文件和编译脚本;能独立构建,以二进制方式集成,具备独立验证能力的二进制单元。部件由对应源码文件夹下的bundle.json文件进行定义。

{
  "name": "@ohos/sensor_lite",                         # HPM部件英文名称,格式"@组织/部件名称"
  "description": "Obtaining sensor data",              # 部件功能一句话描述
  "version": "3.1",                                    # 版本号,版本号与OpenHarmony版本号一致
  "license": "Apache License 2.0",
  "publishAs": "code-segment",                         # HPM包的发布方式,当前默认都为code-segment
  "segment": {
    "destPath": "base/sensors/sensor_lite"             # 发布类型为code-segment时为必填项,定义发布类型code-segment的源码路径
  },
  "dirs": { 
    "base/sensors/sensor_lite"                         # HPM包的目录结构,字段必填内容可以留空
  },
  "scripts":{},                                        # HPM包定义需要执行的脚本,字段必填,值非必填
  "licensePath": "COPYING",
  "readmePath": {
    "en": "README.rst"
  },
  "component": {
    "name": "sensor_lite",                             # 部件名称
    "subsystem": "sensors",                            # 部件所属子系统
    "syscap": [],                                      # 部件为应用提供的系统能力
    "features": [],                                    # 部件对外的可配置特性列表,一般与build中sub_component对应,可供产品配置
    "adapted_system_type": [ "mini", small, standard], # 轻量(mini) 小型(small)和标准(standard),可以是多个
    "rom": "92KB",                                     # 部件ROM估值
    "ram": "~200KB",                                   # 部件RAM估值
    "deps": {
      "components": [                                  # 部件依赖的其他部件
        "hilog_lite",
        "ipc",
        "samgr_lite",
      ],
      "third_party": [ "bounds_checking_function" ]    # 部件依赖的三方开源软件
	  "hisysevent_config": []                          # 部件HiSysEvent打点配置文件编译入口
    },
    "build": {                                         # 编译相关配置
      "sub_component": [
        "//base/sensors/sensor_lite/services:sensor_service",
        "//base/sensors/sensor_lite/frameworks:sensor_lite"    
      ],                                               # 部件编译入口配置
	  "inner_kits": [],                                # 部件对外暴露的接口,用于其它部件或者模块进行引用
      "test": [
        "//base/sensors/sensor_lite/interfaces/kits/native:unittest"
      ]                                                # 部件测试用例编译入口
    }
  }
}

路径规则为:{领域/子系统集}/{子系统}/{部件},部件目录树规则如下:

component
 ├── interfaces
 │    │
 │    ├── innerkits          # 系统内接口,部件间使用
 │    │
 │    └── kits               # 应用接口,应用开发者使用
 ├── frameworks              # framework实现
 ├── services                # service实现
 └── BUILD.gn                # 部件编译脚本

模块(module)

模块就是编译子系统的一个编译目标,部件也可以是编译目标。模块属于哪个部件,在gn文件中由part_name指定。

ohos_shared_library("ace_napi") {        # ace_napi为模块名,同时也是编译目标
    deps = [ ":ace_napi_static" ]        # 模块的依赖,被依赖的对象即使没有被subsystem显式包含,也会被编译
    public_configs = [ ":ace_napi_config" ] # 模块配置参数,比如cflag
    if(!is_cross_platform_build) {
        public_deps = [ "//third_party/libuv:uv" ]
    }
    subsystem_name = "arkui"              # 模块所属部件所属子系统名称
    part_name = "napi"                    # 模块所属部件名称,一个模块只能属于一个部件
}

特性(feature)

特性是部件用于体现不同产品之间的差异。通常不同特性可以定义不同编译宏或者代码,从而影响到源代码中define的特性。


如vender/hihope/rk3568_mini_system/config.json中配置了dsoftbus

{
    "subsystem": "communication",
    "components": [
        { "component": "dsoftbus", "features":["dsoftbus_get_devicename=false"] }
     ]
}

 在/foundation/communication/dsoftbus/bundle.json中features下配置有dsoftbus_get_devicename

"features": [
    "dsoftbus_feature_conn_p2p",
    "dsoftbus_feature_conn_legacy",
    "dsoftbus_feature_disc_ble",
    "dsoftbus_feature_conn_br",
    "dsoftbus_feature_conn_ble",
    "dsoftbus_feature_lnn_net",
    "dsoftbus_feature_trans_udp_stream",
    "dsoftbus_feature_trans_udp_file",
    "dsoftbus_get_devicename",
    "dsoftbus_feature_product_config_path",
    "dsoftbus_feature_lnn_wifiservice_dependence",
    "dsoftbus_feature_protocol_newip",
    "dsoftbus_feature_ex_kits",
    "dsoftbus_feature_wifi_notify"
],

在foundation/communication/dsoftbus/core/adapter/core_adapter.gni中通过判断dsoftbus_get_devicename属性从而走向不同的逻辑

...
if (!dsoftbus_get_devicename) {         # 通过判断走不同的逻辑
    bus_center_core_adapter_src += [ 
"$dsoftbus_root_path/core/adapter/bus_center/src/lnn_settingdata_event_monitor_virtual.cpp"
    ]
    bus_center_core_adapter_inc +=
        [ "$dsoftbus_root_path/core/adapter/bus_center/include" ]
} else {
    bus_center_core_adapter_src += [ "$dsoftbus_root_path/core/adapter/bus_center/src/lnn_settingdata_event_monitor.cpp" ]

    bus_center_core_adapter_inc += [
      "$dsoftbus_root_path/adapter/common/bus_center/include",
      "$dsoftbus_root_path/core/adapter/bus_center/include",
    ]
    ...

实践

本节以添加一个自定义的部件为例,描述如何编译部件,编译库、编译可执行文件等。

单一任务部件

实例部件compdemo1由模块demo1model_bin模块组成,该模块的编译目标为一个可执行程序。

示例部件compdemo1完整目录结构如下

my_test
 └── componentdemo1
      ├── BUILD.gn
      ├── bundle.json
      ├── include
      │    └── model1.h
      └── src
           └── model1.c

1.编写gn脚本/my_test/componentdemo1/BUILD.gn

import("//build/ohos.gni")             # 导入编译模板,编译环境等依赖
ohos_executable("demo1model1_bin"){     # 可执行模块
    sources = [                        # 模块源码
        "src/model1.c"
    ]
    include_dirs = [                   # 模块依赖头文件
        "include"
    ]
    cflags = []
    cflags_c = []
    cflags_cc = []
    ldflags = []
    configs = []
    deps = []                          # 部件内部依赖
    external_deps = []                 # 跨部件的依赖,格式为"部件名:模块名"
    part_name = "compdemo1"            # 模块所属部件名称
    install_enable = true              # 是否安装(缺省默认不安装)
}

注:复制代码时需要删除#及注释,否则无法编译通过

2.对应src下创建model1.c文件,include下创建model1.h文件,文件内容如下,其实现了一个简单的打印

.model1.h

#ifndef MODEL_1_H
#define MODEL_1_H
#ifdef __cplusplus
#if __cplusplus
extern "C" {
#endif
#endif

void HelloPrint();

#ifdef __cplusplus
#if __cplusplus
}
#endif
#endif
#endif

model1.c

#include <stdio.h>
#include "model.h"

int main(int argc, char **argv)
{
    HelloPrint();
    return 0;
}

void HelloPrint()
{
    printf("This component dir is /my_test/componentdemo1, mycomptest->compdemo1->demo1model1_bin\n");
}

3.编写/my_test/componentdemo1/bundle.json

{
    "name": "@ohos/compdemo1",
    "description": "compdemo1 services",
    "version": "3.1",
    "license": "Apache License 2.0",
    "publishAs": "code-segment",
    "segment": {
        "destPath": "my_test/componentdemo1"
    },
    "dirs": {},
    "scripts": {},
    "component": {
        "name": "compdemo1",
        "subsystem": "mycomptest",
        "syscap": [],
        "features": [],
        "adapted_system_type": [ "mini", "small", "standard" ],
        "rom": "10KB",
        "ram": "10KB",    
        "deps": {
            "components": [],
            "third_party": [],
            "hisysevent_config": []
        },
        "build": {
            "sub_component": [
                "//my_test/componentdemo1:demo1model1_bin"
            ],
            "inner_kits": [],
            "test": []
        }
    }
 }

注:此处bundle.json中的"name": "@ohos/compdemo1"和"component": {中的"name": "compdemo1"对应BUILD.gn文件部件名称"part_name"对应的值;"sub_component": ["//my_test/componentdemo1:demo1model1_bin"]对应BUILD.gn中ohos_executable("demo1model1_bin")的值。

4.在/build/subsystem_config.json文件最后增加如下内容

  ,
  "mycomptest": {
    "path": "my_test",
    "name": "mycomptest"
  }
  ...
}

# 注:以上...}代表内容放在文件最后一级}下

  注:subsystem_config.json文件此处的"subsystem": "mycomptest"为bundle.json中的"subsystem": "mycomptest"。

5. 在/vender/{产品解决方案厂商}/{产品名称}/config.json文件最后增加如下内容,如rk3568芯片则路径为:/vendor/hihope/rk3568/config.json

    ,
    {
      "subsystem":"mycomptest",
      "components": [
        { 
          "component": "compdemo1",
          "features": []
        }
      ]
    }
  ...
  ]
}

注:以上...]}代表以上内容放在文件中最后一级数组中

注:config.json文件中此处的"subsystem":"mycomptest"为subsystem_config.json文件中的"name": "mycomptest";"component": "compdemo1"为BUILD.gn中的part_name = "compdemo1"。

6.编译

可以使用整编命令

./build.sh --product-name rk3568 --no-prebuilt-sdk

也可以使用“–build-target 模块名"单独编译,编译命令如下:

./build.sh  --product-name rk3568 --build-target demo1model1_bin --ccache

还可以编译模块所在的部件:

./build.sh --product-name rk3568 --build-target compdemo1 --ccache

7.烧录到设备后,输入模块命令即可打印

demo1model1_bin

组合任务部件

示例部件compdemo2由demo2model1_lib模块、demo2model2_bin模块和demo2model3_config模块组成,demo2model1_lib模块的编译目标为一个动态库,demo2model2_bin模块的目标为一个可执行程序,demo2model2_conf模块的目标为一个etc配置文件。

示例部件compdemo2的完整目录结构如下

my_test/componentdemo2
 ├── a
 │    ├── BUILD.gn
 │    ├── include
 │    │    └── model1.h
 │    └── src
 │         └── model1.cpp
 ├── b
 │    ├── BUILD.gn
 │    ├── include
 │    │    └── model2.h
 │    └── src
 │         └── model2.cpp
 ├── c
 │    ├── BUILD.gn
 │    └── src
 │         └── model3.conf
 ├── BUILD.gn
 └── bundle.json

demo2model1_lib模块编写

1.编写/my_test/componentdemo2/a/BUILD.gn

import("//build/ohos.gni")
config("demo2model1_lib_config"){
    include_dirs = [ "include" ]
}
ohos_shared_library("demo2model1_lib"){
    sources = [
        "include/model1.h",
        "src/model1.cpp"
    ]
    public_configs = [ ":demo2model1_lib_config" ]
    deps = []
    external_deps = []
    part_name = "compdemo2"
}

2.对应src下创建model1.c文件,include下创建model1.h文件,文件内容如下

model1.h

#ifndef MODEL_1_H
#define MODEL_1_H
#ifdef __cplusplus
#if __cplusplus
extern "C" {
#endif
#endif

void HelloPrintModel1();

#ifdef __cplusplus
#if __cplusplus
}
#endif
#endif
#endif

 model1.cpp

#include <stdio.h>
#include "model1.h"

int main(int argc, char **argv)
{
    HelloPrintModel1();
    return 0;
}

void HelloPrintModel1()
{
    printf("This component dir is /my_test/componentdemo2/a mycomptest->compdemo1->demo2model1_lib\n");
}

3.编写/my_test/componentdemo2/b/BUILD.gn

import("//build/ohos.gni")
ohos_executable("demo2model2_bin"){
    sources = [
        "src/model2.cpp"
    ]
    include_dirs = [
        "include"
    ]
    deps = [ "../a:demo2model1_lib" ]
    external_deps = []
    part_name = "compdemo2"
    install_enable = true
}

 4.对应src下创建model2.c文件,include下创建model2.h文件,文件内容如下

model2.h

#ifndef MODEL_2_H
#define MODEL_2_H
#ifdef __cplusplus
#if __cplusplus
extern "C" {
#endif
#endif

void HelloPrintModel2();

#ifdef __cplusplus
#if __cplusplus
}
#endif
#endif
#endif

model2.cpp

#include <stdio.h>
#include "model2.h"
#include "model1.h"

int main(int argc, char **argv)
{
    HelloPrintModel2();
    return 0;
}

void HelloPrintModel2()
{
    printf("This component dir is /my_test/componentdemo2/b mycomptest->compdemo1->demo2model2_bin begin\n");
    HelloPrintModel1();
    printf("This component dir is /my_test/componentdemo2/b mycomptest->compdemo1->demo2model2_bin end\n");
}

5.编写/my_test/componentdemo2/c/BUILD.gn

import("//build/ohos.gni")
ohos_prebuilt_etc("demo2model3_conf"){
    source = "src/model3.conf"
    relative_install_dir = "init"      # 模块安装相对路径,相对于system/etc;此时路径为system/etc/init下,如果有module_install_dir配置时,该配置不生效。
    part_name = "compdemo2"
}

注:复制代码时#备注需要全部删除,否则无法编译通过

 6.对应src下创建model3.conf文件,文件内容示例如下

var_a=1
var_b=2

7.编写/my_test/componentdemo2/BUILD.gn

import("//build/ohos.gni")
group("compdemo2_group"){
    deps = [
        "a:demo2model1_lib",
        "b:demo2model2_bin",
        "c:demo2model3_conf",
    ]
}

注:dps数组中的添加格式:目录名:模块名

8.编写/my_test/componentdemo2/bundle.json

{
    "name": "@ohos/compdemo2",
    "description": "compdemo2 services",
    "version": "3.1",
    "license": "Apache License 2.0",
    "publishAs": "code-segment",
    "segment": {
        "destPath": "my_test/componentdemo2"
    },
    "dirs": {},
    "scripts": {},
    "component": {
        "name": "compdemo2",
        "subsystem": "mycomptest",
        "syscap": [],
        "features": [],
        "adapted_system_type": [ "mini", "small", "standard" ],
        "rom": "10KB",
        "ram": "10KB",    
        "deps": {
            "components": [],
            "third_party": [],
            "hisysevent_config": []
        },
        "build": {
            "sub_component": [
                "//my_test/componentdemo2:compdemo2_group"
            ],
            "inner_kits": [],
            "test": []
        }
    }
 }

注:此处"sub_component": ["//my_test/componentdemo2:compdemo2_group"]对应BUILD.gn中group("compdemo2_group")的值。 

9..在/build/subsystem_config.json文件最后增加如下内容

  ,
  "mycomptest": {
    "path": "my_test",
    "name": "mycomptest"
  }
  ...
}

# 注:以上...}代表内容放在文件最后一级}下

 注:subsystem_config.json文件此处的"subsystem": "mycomptest"为bundle.json中的"subsystem": "mycomptest"。

10. 在/vender/{产品解决方案厂商}/{产品名称}/config.json文件最后增加如下内容,如rk3568芯片则路径为:/vendor/hihope/rk3568/config.json

    ,
    {
      "subsystem":"mycomptest",
      "components": [
        { 
          "component": "compdemo2",
          "features": []
        }
      ]
    }
  ...
  ]
}

注:以上...]}代表以上内容放在文件中最后一级数组中

注:config.json文件中此处的"subsystem":"mycomptest"为subsystem_config.json文件中的"name": "mycomptest";"component": "compdemo2"为BUILD.gn中的part_name = "compdemo2"。

 11.编译

可以使用整编命令

./build.sh --product-name rk3568 --no-prebuilt-sdk

也可以使用“–build-target 模块名"单独编译,编译命令如下:

./build.sh  --product-name rk3568 --build-target demo2model1_bin --ccache

还可以编译模块所在的部件:

./build.sh --product-name rk3568 --build-target compdemo2 --ccache

7.烧录到设备后,输入模块命令即可打印

后记:以上模块烧录后生成文件所在目录为:

模块类型生成文件目录
ohos_shared_library/system/lib
ohos_executable/system/bin
ohos_prebuilt_etc/system/etc/


http://www.niftyadmin.cn/n/5863432.html

相关文章

TensorFlow 是一个由 Google 开发的开源机器学习库

TensorFlow 是一个由 Google 开发的开源机器学习库&#xff0c;被广泛应用于深度学习和人工智能领域。它的基本概念包括以下几点&#xff1a; 张量&#xff08;Tensors&#xff09;&#xff1a;在 TensorFlow 中&#xff0c;数据的基本单位是张量&#xff0c;它类似于多维数组或…

JVM预热

阿里电商平台每年的各种大促活动&#xff0c;对于Java技术来说&#xff0c;其中重要一个操作环节就是预热操作。 目录 预热是什么&#xff1f;为什么要预热&#xff1f; java 程序不预热和预热的调用对比 预热是什么&#xff1f; 预热是指&#xff0c;在 JVM 启动后&#xff0…

(二)趣学设计模式 之 工厂方法模式!

目录 一、 啥是工厂方法模式&#xff1f;二、 为什么要用工厂方法模式&#xff1f;三、 工厂方法模式怎么实现&#xff1f;四、 工厂方法模式的应用场景五、 工厂方法模式的优点和缺点六、 总结 &#x1f31f;我的其他文章也讲解的比较有趣&#x1f601;&#xff0c;如果喜欢博…

Unity贴图与模型相关知识

一、贴图 1.贴图的类型与形状 贴图类型 贴图形状 2.在Unity中可使用一张普通贴图来生成对应的法线贴图&#xff08;但并不规范&#xff09; 复制一张该贴图将复制后的贴图类型改为Normal Map 3.贴图的sRGB与Alpha sRGB&#xff1a;勾选此选项代表此贴图存储于Gamma空间中…

蓝桥杯真题 - 缴纳过路费 - 题解

题目链接&#xff1a;https://www.lanqiao.cn/problems/19736/learning/ 个人评价&#xff1a;难度 2 星&#xff08;满星&#xff1a;5&#xff09; 前置知识&#xff1a;并查集 整体思路 按边权从小到大处理&#xff0c;将处理过的边的两个端点合入一个并查集中&#xff1b…

深入解析设计模式之单例模式

深入解析设计模式之单例模式 在软件开发的复杂世界里&#xff0c;设计模式是开发者手中的得力工具&#xff0c;它们是对常见问题的总结和通用解决方案。单例模式作为其中一种基础且常用的设计模式&#xff0c;在各类应用中扮演着重要角色。 一、单例模式的定义与概念 单例模…

CNewMenu::QueryContextMenu函数分析之新建菜单项的创建

CNewMenu::QueryContextMenu函数分析之新建菜单项的创建 第一部分&#xff1a; HRESULT CNewMenu::QueryContextMenu(HMENU hmenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags) { // if they want the default menu only (CMF_DEFAULTONLY) OR //…

靶场之路-Kioptix Level-1 mod_ssl 缓冲区溢出漏洞

声明 学习视频来自B站UP主 泷羽sec,如涉及侵泷羽sec权马上删除文章笔记的只是方便各位师傅学习知识,以下网站涉及学习内容,其他的都与本人无关,切莫逾越法律红线,否则后果自负 一、准备工作 首先使用 vmware 导入靶机文件&#xff0c; 然后网络模式改成 nat 模式即可 我们打…