OpenHarmony构建编译实战

系统 OpenHarmony
开源鸿蒙系统的南向开发,主要是针对设备或者终端的软件开发,例如各种智能家居设备、穿戴设备等。由于这些设备种类繁多,硬件配置和操作系统都各不相同,因此需要进行适配和优化,以确保设备能够顺利地运行开源鸿蒙系统。

想了解更多关于开源的内容,请访问:

51CTO 鸿蒙开发者社区

https://ost.51cto.com

前言

本人是一名大一学生,有幸被选拔进了深圳技术大学第一届开源鸿蒙菁英班,并在暑期培训进行线上分享,故将讲解的内容也制作成帖子发上来作为学习笔记。在准备分享的过程中,我基于学长们的先前成果,结合开源鸿蒙源码的最新版本进行了相应的调整和优化,帮助大家更好地理解和应用开源鸿蒙技术。

环境

  • OpenHarmony-4.0 源码
  • 九联 unionpi_whale 开发板

一、OpenHarmony源码的模块划分

1.OpenHarmony内核分类

系统分为三种不同体量的系统,本文主要讲解基于Linux的标准系统构建。

2.系统代码版本

Openharmony有主干代码与发行版代码两种,代码的获取请参考官方文档

3.OpenHarmony源码体系

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

4.各部分的关系

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

不同产品相同模块可以通过特性feature进行差异化定义。

5.南向开发的概念

开源鸿蒙系统的南向开发,主要是针对设备或者终端的软件开发,例如各种智能家居设备、穿戴设备等。由于这些设备种类繁多,硬件配置和操作系统都各不相同,因此需要进行适配和优化,以确保设备能够顺利地运行开源鸿蒙系统。

二、OpenHarmony源码的产品、子系统、部件、组件配置详解

1.产品

产品解决方案为基于开发板的完整产品,主要包含产品对OS的适配、部件拼装配置、启动配置和文件系统配置等。产品解决方案的源码路径规则为:vendor/{产品解决方案厂商}/{产品名称}。

vendor/company/product/config.json config.json为编译构建的主入口,包含了开发板、OS部件和内核等配置信息。

2.领域

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

  • vendor仓:存放厂商驱动代码,配置文件
  • device仓:存放硬件设备代码,板级配置和驱动代码

3.子系统

子系统是一个逻辑概念,它具体由对应的组件构成。配置规则如下:

{
  "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中指定子系统的路径和子系统名称。

4.部件(组件)

对子系统的进一步拆分,可复用的软件单元,它包含源码、配置文件、资源文件和编译脚本;能独立构建,以二进制方式集成,具备独立验证能力的二进制单元。

部件的bundle.json放在部件源码的根目录下。配置规则如下:

{
    "name": "@ohos/sensor_lite",	 # HPM部件英文名称,格式"@组织/部件名称"
    "description": "Sensor services",	# 部件功能一句话描述	
    "version": "3.1",		# 版本号,版本号与OpenHarmony版本号一致
    "license": "MIT",		# 部件License
    "publishAs": "code-segment",		 # HPM包的发布方式
    "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"
        ],
        "hisysevent_config": []           # 部件HiSysEvent打点配置文件编译入口
      }         
        "build": {				         # 编译相关配置
            "sub_component": [
                ""//base/sensors/sensor_lite/services:sensor_service"",  # 部件编译入口
            ],			                                                # 部件编译入口,模块在此处配置
            "inner_kits": [],						                   # 部件间接口
            "test": []							                       # 部件测试用例编译入口
        }
    }
 }

5.模块

编译子系统通过模块、部件和产品三层配置来实现编译和打包。模块就是编译子系统的一个目标,包括(动态库、静态库、配置文件、预编译模块等)。模块要定义属于哪个部件,一个模块只能归属于一个部件。这里列出了初学者常用的几个模版及其配置规则:

# C/C++模板
ohos_shared_library  # 动态库gn脚本
ohos_static_library  # 静态库gn脚本
ohos_executable      # 可执行文件gn脚本

# 配置文件
ohos_prebuilt_etc    # etc模块gn脚本

配置中只有sources和part_name是必选,其他都是可选的。

  • ohos_shared_library示例
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 = ""                # 必选,所属部件名称
  output_dir

  # Sanitizer配置,每项都是可选的,默认为false/空。
  sanitize = {
    # 各个Sanitizer开关
    cfi = [boolean]               # 控制流完整性检测
    cfi_cross_dso = [boolean]     # 开启跨so调用的控制流完整性检测
    integer_overflow = [boolean]  # 整数溢出检测
    boundary_sanitize = [boolean] # 边界检测
    ubsan = [boolean]             # 部分ubsan选项
    all_ubsan = [boolean]         # 全量ubsan选项
    ...

    debug = [boolean]             # 调测模式
    blocklist = [string]          # 屏蔽名单路径
  }

  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示例
import("//build/ohos.gni")
ohos_static_library("helloworld") {
  sources = ["file"]            # 后缀名是.c的相关文件
  include_dirs = ["dir"]        # 包含目录
  configs = []                  # 配置
  deps = []                     # 部件内模块依赖
  part_name = ""                # 部件名称
  subsystem_name = ""           # 子系统名称
  cflags = []

  external_deps = [             # 跨部件模块依赖定义,
  "part_name:module_name",      # 定义格式为 "部件名:模块名称"
  ]                             # 这里依赖的模块必须是依赖的部件声明在inner_kits中的模块。

  lib_dirs = []
  public_configs = []

  # Sanitizer配置,每项都是可选的,默认为false/空
  sanitize = {
    # 各个Sanitizer开关
    cfi = [boolean]               # 控制流完整性检测
    cfi_cross_dso = [boolean]     # 开启跨so调用的控制流完整性检测
    integer_overflow = [boolean]  # 整数溢出检测
    boundary_sanitize = [boolean] # 边界检测
    ubsan = [boolean]             # 部分ubsan选项
    all_ubsan = [boolean]         # 全量ubsan选项
    ...

    debug = [boolean]             # 调测模式
    blocklist = [string]          # 屏蔽名单路径
  }

  remove_configs = []
  no_default_deps = []
  license_file = []               # 后缀名是.txt的文件
  license_as_sources = []
  use_exceptions = []
}
  • ohos_executable示例
import("//build/ohos.gni")
ohos_executable("helloworld") {
  configs = []                       # 配置  
  part_name = ""                     # 部件名称 
  subsystem_name = ""                # 子系统名称
  deps = []                          # 部件内模块依赖

  external_deps = [                  # 跨部件模块依赖定义,
  "part_name:module_name",           # 定义格式为 "部件名:模块名称"
  ]                                  # 这里依赖的模块必须是依赖的部件声明在inner_kits中的模块。
  ohos_test = []
  test_output_dir = []

  # Sanitizer配置,每项都是可选的,默认为false/空
  sanitize = {
    # 各个Sanitizer开关
    cfi = [boolean]               # 控制流完整性检测
    cfi_cross_dso = [boolean]     # 开启跨so调用的控制流完整性检测
    integer_overflow = [boolean]  # 整数溢出检测
    boundary_sanitize = [boolean] # 边界检测
    ubsan = [boolean]             # 部分ubsan选项
    all_ubsan = [boolean]         # 全量ubsan选项
    ...

    debug = [boolean]             # 调测模式
    blocklist = [string]          # 屏蔽名单路径
  }

  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_prebuilt_etc示例
import("//build/ohos.gni")
ohos_prebuilt_etc("helloworld") {
  # ohos_prebuilt_etc模板最常用属性:
  source = "file"                          # 指定单个原文件
  module_install_dir = ""                  # 模块安装路径,从system/,vendor/后开始指定
  subsystem_name = ""                      # 子系统名
  part_name = ""                           # 必选,所属部件名称
  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 = []
}

6.特性

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

三、子系统样例源码实战

1.单模块示例

本示例目标是编写一个helloworld程序。在OpenHarmony源码目录下创建子系统文件夹Mysample,其下创建部件文件夹Myhello,下设include和src文件夹分别用于存放头文件和源码。创建如下文件:

(忽略图片中我自己的其他部件)

Mysample/Myhello/include/helloworld.h

#ifndef HELLOWORLD_H //条件编译指令
#define HELLOWORLD_H
#ifdef __cplusplus //C++ 编译器
#if __cplusplus
extern "C" { //表示下面的代码将以 C 语言的方式进行编译
#endif
#endif

void HelloPrint();

#ifdef __cplusplus
#if __cplusplus
}
#endif
#endif
#endif // HELLOWORLD_H

Mysample/Myhello/src/helloworld.c

#include <stdio.h>
#include "helloworld.h"
// #include "../include/helloworld.h"

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

void HelloPrint()
{
    printf("\n\n");
    printf("\n\t\tHello World!\n");
    printf("\n\n");
}

在部件文件夹Myhello下创建配置文件:

Mysample/Myhello/BUILD.gn

import("//build/ohos.gni")  # 导入编译模板
ohos_executable("helloworld") { # 可执行模块,target组件、模块
  sources = [       # 模块源码
    "src/helloworld.c"
  ]
  include_dirs = [  # 模块依赖头文件目录
    "include" 
  ]
  cflags = []
  cflags_c = []
  cflags_cc = []
  ldflags = []
  configs = []
  deps =[]    # 部件内部依赖
  part_name = "Myhello"    # 所属部件名称,必选
  install_enable = true  # 是否默认安装(缺省默认不安装),可选
}

Mysample/Myhello/bundle.json

{
    "name": "@ohos/Myhello", 
    "description": "Hello world example.",
    "version": "3.1",
    "license": "Apache License 2.0",
    "publishAs": "code-segment",
    "segment": {
        "destPath": "Mysample/Myhello"
    },
    "dirs": {},
    "scripts": {},
    "component": {
        "name": "Myhello",
        "subsystem": "Mysample",
        "syscap": [],
        "features": [],
        "adapted_system_type": [ "mini", "small", "standard" ],
        "rom": "10KB",
        "ram": "10KB",
        "deps": {
            "components": [],
            "third_party": []
        },
        "build": {
            "sub_component": [
                "//Mysample/Myhello:helloworld"
            ],
            "inner_kits": [],
            "test": []
        }
    }
}

修改模块配置文件:

vendor/unionman/unionpi_whale/config.json

{
      "subsystem": "Mysample",
      "components": [
        {
          "component": "Myhello",
          "features": []
        }
      ]
    }

修改子系统配置文件:

build/subsystem_config.json

"Mysample": {
    "path": "Mysample",
    "name": "Mysample"
  },

2.多模块示例

本示例目标是带大家使用几个编译模板。在OpenHarmony源码目录下创建子系统文件夹Mysample,其下创建部件文件夹Mycomponent,下设module1、module2和’module3`文件夹分别用于3个模块。创建如下文件:

  • 需要注意的是:每个模块有自己的配置文件BUILD.gn,组件还需要一个group模板的BUILD.gn。

Mysample/Mycomponent/module1/include/hello1.h

#ifndef HELLO1_H //条件编译指令
#define HELLO1_H
#ifdef __cplusplus //C++ 编译器
#if __cplusplus
extern "C" { //表示下面的代码将以 C 语言的方式进行编译
#endif
#endif

void HelloPrint1();

#ifdef __cplusplus
#if __cplusplus
}
#endif
#endif
#endif // HELLO1_H

Mysample/Mycomponent/module1/src/hello1.cpp

#include <iostream>
#include "hello1.h"

void HelloPrint1()
{
    printf("\n\n");
    printf("\n\t\tHello1!\n");
    printf("\n\n");
}

Mysample/Mycomponent/module1/BUILD.gn

import("//build/ohos.gni")

config("hello_lib_config") {
 include_dirs = [ "include" ]
}

ohos_shared_library("hello_lib") {
  sources = [
    "include/hello1.h",
    "src/hello1.cpp",
  ]
  public_configs = [ ":hello_lib_config" ]
  part_name = "Mycomponent"
}

Mysample/Mycomponent/module2/include/hello2.h

#ifndef HELLO2_H //条件编译指令
#define HELLO2_H
#ifdef __cplusplus //C++ 编译器
#if __cplusplus
extern "C" { //表示下面的代码将以 C 语言的方式进行编译
#endif
#endif

void HelloPrint2();

#ifdef __cplusplus
#if __cplusplus
}
#endif
#endif
#endif // HELLO2_H

Mysample/Mycomponent/module2/src/hello2.cpp

#include <iostream>
#include "hello1.h"
#include "hello2.h"

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

void HelloPrint2()
{
    HelloPrint1();
    printf("\n\n");
    printf("\n\t\tHello2!\n");
    printf("\n\n");
}

Mysample/Mycomponent/module2/BUILD.gn

import("//build/ohos.gni")

ohos_executable("hello_bin") {
  sources = [
    "src/hello2.cpp"
  ]
  include_dirs = [ "include" ]
  deps = [                                # 依赖部件内模块
    "../module1:hello_lib"
  ]
  install_enable = true                   # 可执行程序缺省不安装,需要安装时需要指定
  part_name = "Mycomponent"
}

Mysample/Mycomponent/module3/src/config.conf

var_a = 10
var_b = 20
var_c = 30

Mysample/Mycomponent/module3/BUILD.gn

import("//build/ohos.gni")

ohos_prebuilt_etc("hello_etc") {
  source = "src/config.conf"
  relative_install_dir = "init"    #可选,模块安装相对路径,相对于默认安装路径;默认在/system/etc目录
  part_name = "Mycomponent"
}

在部件文件夹Mycomponent下创建配置文件:

Mysample/Mycomponent/BUILD.gn

import("//build/ohos.gni")

group("mycomponent"){
    deps = [
        "module1:hello_lib",
        "module2:hello_bin",
        "module3:hello_etc"
    ]
}

Mysample/Mycomponent/bundle.json

{
    "name": "@ohos/Mycomponent", 
    "description": "Hello world example.",
    "version": "3.1",
    "license": "Apache License 2.0",
    "publishAs": "code-segment",
    "segment": {
        "destPath": "Mysample/Mycomponent"
    },
    "dirs": {},
    "scripts": {},
    "component": {
        "name": "Mycomponent",
        "subsystem": "Mysample",
        "syscap": [],
        "features": [],
        "adapted_system_type": [ "mini", "small", "standard" ],
        "rom": "10KB",
        "ram": "10KB",
        "deps": {
            "components": [],
            "third_party": []
        },
        "build": {
            "sub_component": [
                "//Mysample/Mycomponent:mycomponent"
            ],
            "inner_kits": [],
            "test": []
        }
    }
}
修改模块配置文件:

vendor/unionman/unionpi_whale/config.json

{
      "subsystem": "Mysample",
      "components": [
        {
          "component": "Myhello",
          "features": []
        },
        {
          "component": "Mycomponent",
          "features": []
        }
      ]
    },

3.编译

  • 命令行方式
./build.sh --product-name {product_name} #全量编译
./build.sh --product-name {product_name}  --build-target {target_name} #单独编译部件
./build.sh --product-name {product_name}  --build-target {target_name} --fast-rebuild #快速重建
  • hb方式
hb set #设置编译参数
hb build #全量编译
hb build -T {target_name} #单独编译部件
hb build -T {target_name} --fast-rebuild #快速重建
  • 我们这里使用hb方式来进行编译。在终端输入命令hb set,选择standard和unionpi_whale,在终端输入命令hb build -T Myhello。耗时1:26,编译成功日志如下:

  • 由于编译Myhello部件的已经检查了一遍编译关系,而我们又没有改动编译构建文件,故可以使用快速重建命令hb build -T Mycomponent --fast-rebuild编译Mycomponent部件。编译成功日志如下:

  • 可见所用时间大大减少。
  • 编译产物在out/board/product目录下。

4.烧录

  • 全量烧录: 适合更新版本或者代码大变动
    打包镜像->RKDevTool烧录。
  • HDC工具:适合代码更新时单独发送所需文件(以helloworld为例)
  • 找到可执行文件helloworld,并将其放置到电脑hdc.exe同级目录下。
  • 连接设备:将开发板上电,并连接电脑。

  • 从hdc文件夹下进入终端,输入hdc list targets检查是否连接好,检测到设备后输-入hdc smode授予进程root权限,再输入hdc shell mount -o rw,remount /挂载分区,并且赋予可写权限。
  • 输入hdc shell进入开发板终端,mkdir sample创建文件夹,exit退出终端。
  • hdc file send ./helloworld /sample/传输文件。(将当前目录下的hello文件传输到开发板的sample目录下)
  • hdc shell再次进入开发板终端,cd sample进入文件夹,chmod 777 *给程序赋予可执行权限。

5.烧录测试并执行

  • 烧录:

  • whale开发板烧录口为蓝色USB口上层口,使用USBtoUSB线烧录。
  • 需要注意:可执行文件可以发送到任意目录,而动态库文件libhello_lib.z.so需要发送到/system/lib/目录下。
  • 使用命令./可执行程序名执行程序。在终端查看输出结果。

  • 测试成功

总结

在本人看来,入门开源鸿蒙南向设备学习,有很大可能会被这么多的配置选项和新概念劝退,所以在暑期培训我选择分享这部分的内容,希望对大家有帮助。

想了解更多关于开源的内容,请访问:

51CTO 鸿蒙开发者社区

https://ost.51cto.com

责任编辑:jianghua 来源: 51CTO 鸿蒙开发者社区
相关推荐

2021-12-02 10:05:01

鸿蒙HarmonyOS应用

2021-06-28 09:38:50

鸿蒙HarmonyOS应用

2023-06-12 15:43:44

鸿蒙智能家居开发

2023-07-17 16:13:21

组件模块开发的鸿蒙

2021-07-27 11:54:51

windows系统OpenHarmony

2022-01-11 15:41:18

鸿蒙HarmonyOS应用

2023-08-11 14:06:58

鸿蒙Windows

2023-07-27 14:38:33

开源鸿蒙

2024-08-08 15:46:34

2023-07-28 15:32:26

鸿蒙操作系统

2022-05-27 15:04:53

鸿蒙操作系统

2022-05-07 16:13:59

DevEcoTool鸿蒙

2023-02-09 15:28:19

鸿蒙编译速度

2022-05-11 15:08:52

驱动开发系统移植

2023-08-07 15:13:09

设备开发鸿蒙

2023-02-01 16:28:30

Linux内核鸿蒙

2022-04-25 09:10:50

RK3568鸿蒙

2021-07-02 14:16:04

鸿蒙HarmonyOS应用

2023-03-08 15:33:11

鸿蒙操作系统

2022-11-11 09:37:58

数据转码应用开发
点赞
收藏

51CTO技术栈公众号