开发规范

概述

本文档将介绍Bundle的基本概念以及如何按照规范定义Bundle。

定义

OpenHarmony软件以bundle作为基本单元,从系统角度看,凡是运行在OpenHarmony上的软件都可以定义为Bundle;一般来讲,根据Bundle的应用范围,可以分为:

  • 板级Bundle:如board、arch、mcu这些与设备硬件相关的Bundle。
  • 系统Bundle:一组独立功能的集合,如内核、文件系统、框架等。
  • 应用Bundle:直接面向用户提供服务的应用(如wifi_iot,ip_camera)。

从形式上看,Bundle是为复用而生,一切可以复用的模块都可以定义为Bundle,可以分为:

  • 源代码
  • 二进制
  • 代码片段
  • 发行版

Bundle划分原则

原则上应尽可能划分为细颗粒度的Bundle,以满足最大限度的复用。主要考虑以下几点:

  • 独立性:Bundle的功能应该相对独立,支持独立编译,可以单独对外提供接口和服务;
  • 耦合性:如果Bundle必须依赖其他的Bundle,才能对外提供服务,应考虑和被依赖的Bundle合并为一个Bundle。
  • 相关性:如果一组Bundle共同完成一项功能,且没有被其他Bundle依赖,未来也没有被依赖的可能,则可以考虑合并为一个Bundle。

Bundle依赖

Bundle的依赖关系分为两种:必选依赖和可选依赖。

  • 必选依赖:是指BundleA在完成某个功能时,必须引入BundleB,调用B的接口或服务配合才能完成。称B为A的必选依赖。
  • 可选依赖:是在BundleA在完成某个功能时,可以引入BundleC,也可以引入BundleD。C和D可以相互替换,称C和D为A的可选依赖。

Bundle构成

一个Bundle包一般包含如下内容:

  • Bundle包的代码或库(src目录下的代码文件)

  • ohos_bundles文件夹(存放依赖的Bundle,安装Bundle时自动生成,无需提交到代码库)

  • Bundle包的说明文件(README.md)

  • Bundle包元数据声明文件(bundle.json)

  • 开源许可文件(LICENSE)

    my-bundle
    

|_ohos_bundles |_src |_bundle.json |_README.md |_LICENSE ```

代码文件

Bundle的代码文件和普通的代码目录没有差异。但要注意的是,Bundle中对外暴露的接口(头文件),会被其他Bundle所引用,需要单独在bundle.json的dirs中声明。

说明文件

README.md,为markdown格式的描述关于Bundle自述说明文件。(语法参考)

为了帮助他人在hpm上找到该Bundle,并更方便的使用它,在Bundle的根目录中包含一个README文件。

README文件可能包括如何安装,配置和使用Bundle包中的实例代码说明,以及可能会对用户有所帮助的任何其他信息。

每个Bundle的自述文件将显示在hpm系统的Bundle详情页面的描述中。

元数据描述文件

bundle.json文件是对当前Bundle的元数据描述,每个Bundle中必须包含一个bundle.json文件。

{
  "name": "@myorg/demo-bundle",
  "version": "1.0.0",
  "license": "MIT",
  "description": "bundle description",
  "keywords": ["hos"],
  "tags": ["applications", "drivers"],
  "author": {"name":"","email":"","url":""},
  "contributors":[{"name":"","email":"","url":""},{"name":"","email":"","url":""}],
  "homepage": "http://www.foo.bar.com",
  "repository": "https://git@gitee.com:foo/bar.git",
  "publishAs": "code-segment",
  "segment":{
     "destPath":"/the/dest/path"
  },
  "dirs": {
    "src": ["src/**/*.c"],
    "headers": ["headers/**/*.h"],
    "bin": ["bin/**/*.o"]
  },
  "scripts": {
    "build": "make"
  },
  "envs": {},
  "ohos": {
    "os": "2.0.0",
    "board": "hi3516",
    "kernel": "liteos-a"
  },
 "rom": "10240",
 "ram": "1024",
 "dependencies": {
    "@myorg/net":"1.0.0"
 }
}

bundle.json文件具有如下功能:

  • name:定义Bundle的名称,放到组织下, 以@开头,/分割,如:@myorg/mybundle

  • version:定义Bundle版本号,如1.0.0,需满足semver的标准。

  • description:一句话对Bundle进行简要的描述。

  • dependencies:定义Bundle的依赖Bundle。

  • envs: 定义Bundle编译时所需要的参数,包括全局参数以及依赖所需的参数。

  • scripts:定义在当前Bundle下能够执行的命令(如编译,构建,测试,烧录等)。

  • publishAs:定义Bundle的发布类型(source:源码,binary:二进制,distribution:发行版,code-segment:代码片段)。

  • segment: 仅针对code-segment类型的Bundle,定义Bundle的目标路径(即安装后,Bundle包中包含的文件复制到的目标路径)

  • dirs:定义发布时打包的目录结构(如头文件)。

  • ram&rom:统计相关信息:预计占用ROM和RAM信息。

  • ohos:描述OpenHarmony系统版本、开发板及内核的匹配关系(多个请用英文逗号的“,”分割)。

  • 定义其他扩展信息:作者,主页,代码仓库,许可协议,标签,关键字。

  • 对于发行版类型,还有个base,可以定义继承自的发行版。

Bundle管理

依赖关系

生成基础bundle.json以后,需要继续添加Bundle依赖来实现更复杂的功能。此时需要知道所依赖Bundle的名称和版本号,并且把它们定义在bundle.json里面的dependencies字段中。

{
    "name": "my-bundle",
    "version": "1.0.0",
    "dependencies": {
        "net": "1.0.0"
    }
}

上述示例中,my-bundleBundle依赖于net 1.0.0Bundle。在全局安装了 hpm CLI 工具之后,执行如下命令可以从远端仓库获取到依赖:

hpm install 

依赖获取以后,会保存到当前Bundle根目录下到ohos_bundles文件夹中。Bundle以及依赖之间会形成一个依赖关系的树状结构。全局安装了 hpm CLI 工具之后,在Bundle根目录下执行如下命令:

username@server MINGW64 /f/showcase/demo/demo
$ hpm list
+--demo@1.0.0
| +--@huawei/media@1.0.2
| +--@demo/sport_hi3518ev300_liteos_a@1.0.0
| | +--@demo/app@4.0.1
| | | +--@demo/build@4.0.1
| | | +--@demo/arm_harmonyeabi_gcc@4.0.0
| | +--@demo/liteos_a@4.0.0
| | | +--@demo/third_party_fatfs@4.0.0
| | | +--@demo/arm_harmonyeabi_gcc@4.0.0
| | +--@demo/init@4.0.0
| | +--@demo/dist_tools@4.0.0

还可以使用可视化的形式,来查看当前Bundle的依赖关系,执行如下命令:

hpm ui

会在本地启动一个web服务(默认会打开浏览器并进入项目页),点击侧边栏的项目依赖图标,打开页面,可以看到项目的依赖Bundle列表,点击右侧按钮切换到树状视图,就可以看到依赖关系的图形化展示(如下图)。

图 1 Bundle包依赖关系图

hpm操作命令参考

Bundle的全生命周期管理,可以通过hpm命令工具进行操作,hpm的操作命令如下(详细帮助可以执行 hpm -h学习):

表 1 hpm操作命令

命令类别

命令行

含义说明

版本查询

hpm -V 或 hpm --version

查看hpm-cli 版本号。

帮助查询

hpm -h 或 hpm --version

查看命令列表及帮助。

hpm -h

查看命令帮助。

创建

hpm init bundle

创建Bundle工程。

hpm init -t template

根据模板创建脚手架工程。

安装

hpm install 或hpm i

安装bundle.json中依赖的Bundle。

hpm install bundle@version

安装指定Bundle版本。

卸载

hpm uninstall bundle

删除depedencies依赖的Bundle。

hpm remove 或hpm rm bundlename

删除depedencies依赖的Bundle。

查看

hpm list 或者 hpm ls

显示当前Bundle/发行版所有的Bundle树。

hpm dependencies

生成当前Bundle/发行版依赖关系数据(在hpm ui也集成了该命令的调用,可以图形化的展示)

搜索

hpm search name

搜索Bundle,--json,可以以json格式输出 -type 可以设置搜索Bundle的类型,包括bundle,distribution,code-segment三种。

设置hpm配置项

hpm config set key value

设置配置值,如服务器地址,网络代理。

hpm config delete key

删除配置。

更新

hpm update

更新当前Bundle依赖的Bundle的版本。

hpm check-update

检查依赖的Bundle版本是否有更新。

编译

hpm build

编译Bundle/发行版。

hpm dist

针对发行版(distribution),发行版编译构建(依赖bundle.json的scripts中的dist脚本)。

打包

hpm pack

本地Bundle打包依赖。

烧录

hpm run flash

烧录固件(依赖bundle.json的scripts中的flash脚本)。

发布

hpm publish

发布Bundle,发布的Bundle在仓库中必须唯一,且版本唯一(需要账号登录)。

执行扩展命令

hpm run

执行bundle.json文件中定义的scripts脚本命令,支持多个命令可用 && 连接。

解压包

hpm extract

解压文件. 支持格式'zip','tar','tgz' 和'.tar.gz'

启动图形化界面

hpm ui

本地启动HPM UI,可通过-p参数指定端口,Windows平台下会启动默认的浏览器打开

多语言切换

hpm lang

切换中英文操作界面(同时支持命令行和UI)

转换为hpm包格式

hpm x2h

将一个maven格式或npm格式包转换成hpm的包格式,并发布到HPM

代码段还原或清理

hpm code clean|restore

针对依赖的代码段(code-segment)Bundle,执行清理或还原操作(即根据segment.destPath执行拷贝/删除操作)

生成秘钥

hpm gen-keys

生成公钥/私钥对,将公钥配置到HPM服务端,可以实现hpm-cli 免密登录,发布Bundle。

生成第三方开源说明

hpm gen-notice

根据每个Bundle的说明,生成一份合并后的第三方开源说明的合并文件。

Bundle版本

版本号命名规范

名称需要为全小写字母,中间可以使用中划线或者下划线分隔。比如 "bundle", "my_bundle"。

版本号的格式为 "主版本号.次版本号.修订号" 或 "主版本号.次版本号.修订号-先行版本号",比如 "1.0.0", "1.0.0-beta",详细规格可以参考 https://semver.org

版本发布

为了使Bundle能被其他开发者使用,Bundle需要上传到远端仓库。Bundle上传使用如下命令:

hpm publish

命令执行以后,系统会对的整个依赖关系进行检查,下载缺失依赖Bundle。依赖检查完成后,如果发布类型为binary,系统会对整个Bundle进行编译,生成二进制文件,然后打包上传。如果使其他上传类型,则直接根据定义的打包规则进行打包,然后上传。

注意:发布Bundle需要用户账号登录,需要先拥有hpm的系统账号后,并注册组织,申请组织认证通过后,才拥有发布的权限。

发行版

发行版通常是将一系列Bundle组合起来,成为编译可以运行的OpenHarmony解决方案镜像,里面包含了多个依赖的Bundle,以及脚本,用于描述如何完整编译、链接这些Bundle。

发行版本身通常不需要包含功能实现代码,仅包含bundle.json描述(设置publishAs为distribution)和一些编译脚本组成。

因为发行版编译的过程需要系统提供环境变量,所以发行版使用scripts脚本中内置的dist命令:

{
    "publishAs":"distribution",
    "scripts": {
        "dist": "script compile command"
    }
}

编译执行使用如下命令:

hpm dist 

重新定义一个发行版所具有的功能是一个复杂的过程,所以系统允许对发行版进行继承,从而在现有功能的基础上进行定制。继承发行版需要在bundle.json中定义base字段。

{
    "base": {
        "name": "dist_wifi_iot",
        "version": "1.0.0"
    }
}

上述定义表明当前Bundle继承自发行版Bundledist-wifi-iot 1.0.0。

发行版由很多的依赖Bundle组成,通过bundle.json中的dependencies段来描述,有些依赖是必须的,有些依赖则是根据可以需求增加或删除的。bundle.json中名称前带有?的依赖表示可选依赖,继承它的发行版,可以移除掉该可选Bundle,再增加别的Bundle进行替换。

{
    "dependencies": {
        "?my_bundle": "1.0.0"
    }
}

上述声明表示my_bundle依赖可以被移除。如果想要移除my_bundle,在上层依赖方需要使用excludes关键字来进行定义

{
    "excludes": [ "my_bundle" ]
}

依赖被移除后,就不会参入Bundle的构建过程。只有标记为可选的依赖才能够被移除,强行移除未被标记的依赖会出现错误提示。

环境变量说明

Bundle在编译的过程中需要依赖系统提供的环境变量来自定义输出,链接所需二进制文件等等。这里提出的环境变量均指根据需求把所需变量注入脚本执行的上下文中。所以在脚本中可以直接获取到变量的值。下面介绍当前系统存在的几种环境变量。

全局变量由bundle.json中的envs属性来定义。整个Bundle中的依赖都可以获取到全局变量定义的值。

{
    "envs": {
        "compileEnv": "arm"
    }
}

不同Bundle在引入依赖的过程中可以传入不同的参数,从而使依赖的编译可以满足当前Bundle的需求。依赖中定义的参数可以在对应依赖脚本执行的上下文中获取到。

{
    "dependencies": {
        "my-bundle": {
            "version": "1.0.0",
            "mode": "debug"
        }
    }
}

Bundle在链接二进制文件的时候,需要知道二进制文件在依赖中的路径,所以依赖的路径会作为环境变量传入编译Bundle中。

传入的环境变量的格式为DEP_BundleName,BundleName为依赖的名称,例如 DEP_first_bundle。

依赖中可以定义标签,对引入的依赖进行分组。在脚本中可以根据标签,获得这一组依赖的路径。定义的标签以#开头,具体定义的方式为:

{
    "dependencies": {
        "#tool": {
            "first-bundle": "1.0.0",
            "second-bundle": "1.0.0"
        },
        "#drivers": {
            "xx-bundle": "1.0.0",
            "yy-bundle": "1.0.0"
        }
    }
}

系统中存在两个固定环境变量:

  • DEP_OHOS_BUNDLES:表示ohos_bundles文件夹所在的路径。
  • DEP_BUNDLE_BASE:表示最外层Bundle的路径。