# 部件化编译最佳实践 OpenHarmony的软件以部件作为基本功能单元,但为了组装出不同形态的产品,部件可能需要提供不同规格的版本。本文主要介绍以下几个方面的编译实践方法: - 如何通过feature裁减部件的部分功能? - 如何判断当前产品中是否包含某个依赖的部件? - 部件的inner_kits裁减规则要求 - 部件的napi模块裁减方法 - 如何按需配置部件的系统能力? ## 1. 使用feature配置进行部件差异化配置 ​ feature主要用于描述部件在不同产品下的差异化行为。产品在装配部件时,可以通过配置不同的feature来使用部件不同的特性集。 ### 1.1 产品如何配置部件的feature ​ 当前系统有两个版本的产品配置文件格式,配置方法分别如下: - V2产品配置文件 ​ productdefine/common/products/xxx.json目录下V2版本产品配置文件中为部件配置不同feature的示例如下: ```go { ... "parts": { "subsytemName:partName": { "feature": [ "{partName}_feature_A": false ] } } } ``` - V3产品配置文件 ​ vendor/xxx/config.json目录下V3版本产品配置文件中为部件配置不同feature的示例如下: ```go { ... "subsystems": [{ "subsystem": "subsystemName", "components": [{ "component": "partName", "features":[ "{partName}_feature_A=false" ] }] }] } ``` ### 1.2 部件开发人员如何使用feature进行部件的特性隔离 ​ 下面介绍feature的声明、定义以及使用方法。 - feature的声明 在部件的bundle.json文件中通过feature_list来声明部件的feature列表,每个feature都必须以"**{部件名}_**"开头。示例如下: ```go { "name": "@ohos/xxx", "component": { "name": "partName", "subsystem": "subsystemName", "features": [ "{partName}_feature_A" ] } } ``` ​ features中可以为部件声明多个feature。 - feature的定义 在部件内可通过以下方式定义feature的默认值: ```go declare_args() { {partName}_feature_A = true } ``` 该值是此部件的默认值,产品可以在部件列表中重载该feature的值。 feature需给部件内多个模块使用时,建议把feature定义在部件的全局gni文件中,各个模块的BUILD.gn中import该gni文件。 - feature的使用 BUILD.gn文件中可通过以下方式进行根据feature决定部分代码或模块参与编译: ```go 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" ] } } ``` 也可以通过以下方式为模块定义代码宏进行代码级差异化编译: ```go if ({partName}_feature_A) { defines += ["FEATUREA_DEFINE"] } ``` - 可裁剪模块使用独立BUILD.gn文件 实际裁剪部件中的单个模块时,该模块的BUILD.gn不能和其它必选模块混合在同一个BUILD.gn脚本中。 ```go shared_library("requiredA") { deps = ["dependB"] } shared_library("optionalC") { deps = ["dependD"] } ``` 如上图所示,由于编译扫描部件时是以BUILD.gn文件为单位的,只要requiredA模块被引用,编译时可选的optionalC以及dependD模块都会被引入并被依赖。因此,建议可选模块使用单独的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默认值可自动跟随裁减部件而同步更新。 ```go 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。 注意:访问global_parts_info中的部件名称中如果包含有"-"或".",需转化为"_"。global_parts_info的具体内容可查看out/preloader/productName/parts_info.json文件。 ## 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,产品配置中可通过以下方法来修改: - V2产品配置文件 ​ productdefine/common/products/xxx.json目录下的V2版本产品配置文件配置示例如下: ```go { "product_name": "productName", "version": "2.0", "product_company": "xxx", "support_jsapi": false, ... } ``` - V3产品配置文件 ​ vendor/xxx/config.json目录下的V3版本产品配置文件配置示例如下: ```go { "product_name": "productName", "version": "3.0", "device_company": "xxx", "support_jsapi": false, ... } ``` ### 4.2 部件开发人员如何使用support_jsapi进行部件的特性隔离 support_jsapi是一个全局的feature,每个部件都可以参考1.2章的方式使用进行代码隔离。 ```go group("jsapi_group") { deps = [] if (support_jsapi) { deps += [ "real_jsapi_module" ] } } ``` ## 5.如何按需配置部件的系统能力 系统能力即SystemCapability,又称syscap,是部件向开发者提供的接口的集合。 ### 5.1 部件配置系统能力 部件配置系统能力是为了方便某个特定部件是否要打开或关闭特定的系统能力。 部件配置系统能力的位置在部件目录下的bundle.json,配置示例如下所示: ```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,则表明该部件默认关闭此系统能力 以上配置表明,WIFI的STA、AP、和HotspotExt三个系统能力是打开的,而P2P和Core是关闭的。 ### 5.2 产品配置系统能力 产品配置系统能力是为了方便某个特定产品是否要打开或关闭特定的系统能力,若无配置,则以部件侧的配置为准,产品配置系统能力的位置在vender/{company}/{product}/config.json。 如果要对产品的系统能力进行精细化配置,可在产品配置中加入syscap关键字,并对要配置的系统能力赋值。产品侧的配置优先级大于部件系统能力默认配置,若某一个系统能力在部件侧默认配置为false,在产品侧配置为true,则这个系统能力的最终配置为true。示例如下: ```json { "subsystem": "communication", "components": [ ... { "component": "wifi", "features": [], "syscap": [ "SystemCapability.Communication.WiFi.AP = false", "SystemCapability.Communication.WiFi.P2P = true", "SystemCapability.Communication.WiFi.HotspotExt = false" ] }, ... ] } ``` 以上配置表明,WiFi的AP和HotspotExt系统能力是关闭的,而P2P是打开的。综合部件侧的配置,最终STA、P2P为打开系统能力,而AP、Core和HotspotExt为关闭的系统能力。 ### 5.3 部件侧配置和产品侧配置的作用 一般来说,当产品没有特性化差异的时候,我们仅需在部件侧配置系统能力,部件侧配置的系统能力是我们的基础,只有当产品存在特性化差异,需要关闭某个默认打开的系统能力或打开某个系统默认关闭的系统能力时,我们才会需要在产品侧配置。 ## 6. 部件feature与SystemCapability的关系 1) feature是面向南向设备装配时的部件能力差异化配置,一个部件可以为不同品类的设备提供不同的feature集合。 2) SystemCapability是面向北向应用开发的能力呈现。 3) 无论选择部件的多少个feature,部件对外呈现的SystemCapability不能变化;如果feature影响部件对外的SystemCapability,则需要把该feature相关的部分拆分成独立的部件提供相应的SystemCapability。