ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

OpenHarmony学习笔记【篇一:编译构建机制】

OpenHarmony学习笔记【篇一:编译构建机制】 在学习纯血鸿蒙系统各个模块之前我们需要先了解纯血鸿蒙的编译构建机制。本篇主要基于鸿蒙系统的编译和各模块的编译配置两个维度去进行梳理。另外编译构建相关代码在如下目录一、编译入口我们先看看纯血鸿蒙系统的开源代码官方介绍说明即直接执行./build.sh --product-name xxx 即可开始完整的鸿蒙OS全量编译但是在开源代码的根目录中并没有找到build.sh文件。1、开源代码build.sh如上编译命令执行的build/build_scripts/build.sh脚本其主要逻辑如下几步https://xrefandroid.com/OpenHarmony-v6.0-Release/xref/build/build_scripts/build.sh步骤一环境检查步骤二工具包初始化步骤三命令参数解析步骤四编译入口如上鸿蒙系统最核心的编译入口其实就在build/hb/main.py build 脚本的调用。2、黄区项目build_system.sh如下介绍一下我在HW黄区看到的纯血鸿蒙系统的编译模式。还是如上的机制在根目录中存在build.sh和build_system.sh等脚本使用方式同上类似。我看到的这个项目的编译命令如下./build_system.sh --abi-type generic_arm_32 --device-type hisi_xxx --ccache虽然有些参数的变化但是不影响最终目的还是指定product name如上命令中编译的是generic_arm_32这个项目。这一块的定义同安卓基本一致即在device目录下会初始当前项目至于安卓编译三部曲这块我没有具体深入研究大概率是不支持了。如上build_system.sh脚本中有一段核心代码${PYTHON3}${source_root_dir}/build/hb/main.py / build --product-name ${product_name}${main_params}||{ echo -e ${HMOS_BUILD_INFO} exit 1 }这段代码直接调用了根目录/build/hb/main.py城西根据如上命令拼接出来之后的命令变成/build/hb/main.py build --product-name generic_arm_32 all_phone_standard如上鸿蒙系统最核心的编译入口其实就在build/hb/main.py build脚本的调用。3、编译入口build/hb/main.py如上代码因为传递的是build参数所以执行的第93行代码如果is_indep_build为真就进行独立构建系统否则就普通构建两则区别如下常规构建_init_build_module:139独立构建_init_indep_build_module:195触发hb set选产品后hb build组件目录下hb build 组件名 -i或--indep-build前提完整 OpenHarmony 源码树只需要单个组件仓库强制检查当前目录存在build/indep_configs/build_indep.sh否则报you are running hb build command in wrong dir!退出组装链路OHOSPreloader → OHOSLoader加载产品/部件清单→Gn→NinjaPreuiltsService预建件Hpm包管理器自动拉依赖的二进制 prebuilts IndepBuild 服务依赖来源源码树内全量源码从 repo.harmonyos.com 下载依赖组件的预编译产物本地只 gn/ninja 编当前组件适用整机/整镜像构建、产品级集成组件级开发、CI 单仓验证不用拉几十 GB 全树后续的编译构建就不详细介绍了因为我也不是很清楚。只需要知道编译构建子系统提供了一个基于Gn和ninja的编译构建框架根据产品配置编译生成对应的镜像包其中编译构建流程为使用Gn配置构建目标。Gn运行后会生成ninja文件。通过运行ninja来执行编译任务。详细参考官方文档说明https://gitee.com/openharmony/build/blob/master/README_zh.md二、编译结构在接触鸿蒙系统源代码之后经常会看到XXX子系统。官方文档对它的定性是子系统是一个逻辑概念它具体由对应的部件构成 子系统是某个路径下所有部件的集合一个部件只能属于一个子系统。1、subsystem子系统我们先来看看子系统长什么样子的源代码build/subsystem-config.json文件中是开源主干的子系统注册表配置了如下子系统//build/subsystem_config.json { 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 }, useriam: { path: base/useriam, name: useriam }, accesscontrol: { path: base/accesscontrol, name: accesscontrol }, startup: { path: base/startup, name: startup }, hiviewdfx: { path: base/hiviewdfx, name: hiviewdfx }, utils: { path: utils, name: utils }, commonlibrary: { path: commonlibrary, name: commonlibrary }, bundlemanager: { path: foundation/bundlemanager, name: bundlemanager }, appexecfwk: { path: foundation/appexecfwk, name: appexecfwk }, ability: { path: foundation/ability, name: ability }, notification: { path: base/notification, name: notification }, communication: { path: foundation/communication, name: communication }, location: { path: base/location, name: location }, systemabilitymgr: { path: foundation/systemabilitymgr, name: systemabilitymgr }, hdf: { path: drivers, name: hdf }, updater: { path: base/update, name: updater }, developtools: { path: developtools, name: developtools }, sensors: { path: base/sensors, name: sensors }, graphic: { path: foundation/graphic, name: graphic }, window: { path: foundation/window, name: window }, time: { path: base/time, name: time }, inputmethod: { path: base/inputmethod, name: inputmethod }, request: { path: base/request, name: request }, print: { path: base/print, name: print }, theme: { path: base/theme, name: theme }, multimedia: { path: foundation/multimedia, name: multimedia }, castplus: { path: foundation/CastEngine, name: castplus }, multimodalinput: { path: foundation/multimodalinput, name: multimodalinput }, telephony: { path: base/telephony, name: telephony }, global: { path: base/global, name: global }, powermgr: { path: base/powermgr, name: powermgr }, usb: { path: base/usb, name: usb }, applications: { path: applications, name: applications }, xts: { path: test/xts, name: xts }, ostest: { path: test/ostest, name: ostest }, testfwk: { path: test/testfwk, name: testfwk }, distributedhardware: { path: foundation/distributedhardware, name: distributedhardware }, arkcompiler: { path: arkcompiler, name: arkcompiler }, iothardware: { path: base/iothardware, name: iothardware }, kernel: { path: kernel, name: kernel }, msdp: { path: base/msdp, name: msdp }, deviceprofile: { path: foundation/deviceprofile, name: deviceprofile }, filemanagement: { path: foundation/filemanagement, name: filemanagement }, resourceschedule: { path: foundation/resourceschedule, name: resourceschedule }, barrierfree: { path: foundation/barrierfree/accessibility, name: barrierfree }, customization: { path: base/customization, name: customization }, web: { path: base/web, name: web }, thirdparty: { path: third_party, name: thirdparty }, ide: { path: ide, name: ide }, advertising: { path: domains/advertising, name: advertising }, tee: { path: base/tee, name: tee }, llvmproject: { path: toolchain/llvm-project, name: llvmproject }, sdk: { path: interface/sdk-js, name: sdk } }如上配置了OpenHarmony开源主干上面的57个子系统。每一个子系统都有两部分组成path代码路径name子系统名称由此可见所以子系统 ≈ 一张目录 → 领域名的映射表 部件的逻辑分组不是一个有独立构建产物的实体。Q1可以自定义subsystem吗build/subsystem_config.json 里 57 个是主干定义的子系统startup、powermgr、security……除此之外芯片方案商和产品厂商可以在 vendor/、device/ 目录下放自己的子系统配置扩展注册。证据在构建脚本 hb/util/loader/subsystem_scan.py它读主干配置后还会 merge 一份 example_subsystem_config且 _check_path_prefix() 明确只允许新增路径以 vendor/device 开头。Q2编译时会读subsystem_config然后逐个子系统编译吗编译的时候会读取subsystem_config配置文件但没有针对每个子系统编译这个动作。 读它的目的是拿到扫描路径表。subsystem_scan.py源码逻辑子系统全程只是去哪里找部件的路径索引从不参与编不编的决策。 决定编译范围的永远是产品配置里的部件清单——qemu-min 产品的 startup 子系统下只勾了 init 和 startup_l2 两个部件这个子系统下其余部件即使存在也不编。Q3能单独编译某个子系统吗不能。官方文档原话编译构建可以编译产品、部件和模块但是不能编译子系统。因为编一个子系统语义不成立子系统是逻辑分组构建系统只为部件生成 GN group target没有子系统级的聚合 target同一个子系统下不同部件被不同产品按 features 裁剪编译需要的 toolchainarm/arm64、内核、features 开关、inner_kits 可见性全部来自产品上下文——脱离产品谈编子系统这些参数无处来。Q4终极理解OpenHarmony的子系统的设计目的是站在架构分层、代码分类的角度上引申出来的。即相关联的代码我放在一起统一用子系统来进行命名用来给工程师看的。等同于Android中的目录划分例如framework/base可以把它当成一个子系统但是却无法直接单编译framework/base。2、bundle部件一个子系统可能包含多个部件而官方对bundle部件的描述内容如下{ name: ohos/component_name, # HPM部件英文名称格式组织/部件名称 description: xxxxxxxxxxxxxxxxxxx, # 部件功能一句话描述 version: 3.1, # 版本号版本号与OpenHarmony版本号一致 license: MIT, # 部件License publishAs: code-segment, # HPM包的发布方式当前默认都为code_segment segment: { destPath: }, # 发布类型为code_segment时为必填项定义发布类型code_segment的代码还原路径源码路径 dirs: {}, # HPM包的目录结构字段必填内容可以留空 scripts: {}, # HPM包定义需要执行的脚本字段必填值非必填 licensePath: COPYING, readmePath: { en: README.rst }, component: { # 部件属性 name: component_name, # 部件名称 subsystem: , # 部件所属子系统 syscap: [], # 部件为应用提供的系统能力 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: [] # 部件测试用例编译入口 } } }1bundle.json配置解读如上配置主要可以划分为两部分部件的一些基本信息部件内部的一些定义。配置1部件发布声明我们先看看audio_framework部件的一些基本信息name: ohos/audio_frameworkohos表示这个部件的发布者是OpenHarmony官方维护只要是官方开发的都是ohos用来表示一个机构或者组织类似于android中的Google一样。当然在vendor/里面可能还有其他一些企业或者公司开发的一些部件例如hihope/xxx表示润和公司。publishAs: code-segmentpublishAs发的是什么装到哪类比 Androidcode-segment源代码片段整目录原样打包按 destPath 还原进源码树要再编译repo sync 拉下来的单个 git projectsource源码但按dirs声明挑选打包src/headers 分离留在依赖目录独立编译早期 npm 风格的源码库 / 独立模块binary预编译二进制.so/.a带 branch/os/arch/variant 维度binarys/destPath/分支/OS/架构/variant/版本/不编译直接链接厂商 prebuilt blobvendor/xxx/proprietary里只给 .so 不给源码的库distribution发行版不含代码只有 bundle.json 依赖清单 scripts.dist编译脚本hpm dist拉齐一批部件编出整机镜像当前工程根AOSP 的设备 manifest 编译出的 factory image 整包plugin / model / chip-definition / templateHPM 生态扩展类型工具插件、AI 模型、芯片定义、工程模板与系统部件关系不大——配置2部件属性定义接下来再看看另外一部分component的配置这里面对当前部件的一些属性进行具体的定义同样先看看component前面的字段name: audio_frameworkcomponent里面的name表示当前部件的名字必须和前面的name一致这里表示是audio_framework部件。subsystem: multimedia这里的subsystem表示当前所属的子系统注意要和前文的subsystem-config.json定义的子系统要对齐这里表示当前部件属于multimedia子系统。当然当前代码路径也是在subsystem-config.json定义的path里面。adapted_system_type: [ standard ]adapted_system_type表示当前部件使用于鸿蒙什么系统standard表示标准鸿蒙系统从这里[]来看可以配置多个系统即某些部件可以配置为支持多套内核系统。syscap: 系统能力清单如上配置的SystemCapability.Multimedia.Audio.XXX 是部件向系统注册我提供哪些能力的标准格式子系统Multimedia→ 部件Audio→ 具体能力Renderer 渲染、Capturer 录音、等运行时谁用应用/三方开发者调 canIUse(SystemCapability.Multimedia.Audio.Spatialization) 查询设备是否具备该能力设备上也可 param get const.SystemCapability.Multimedia.Audio.Renderer 直接看与Android 对照就是 Android 的 PackageManager.hasSystemFeature() 机制——APK 在 AndroidManifest 里 uses-feature android:nameandroid.hardware.usb.host系统在 /system/etc/permissions/ 下放 feature XML运行时查询。features编译裁剪开关清单这 15 个 audio_framework_feature_xxx 是功能编译开关命名规律固定部件名_feature_功能。feature 的完整生命周期如下部件声明bundle.json features 数组列出开关名注意只列名字默认值不在这里。产品可覆盖产品 config.json 勾选部件时可写features: [audio_framework_feature_distributed_audiotrue]。强校验loader.py:230 _check_product_part_feature——产品若配了部件 bundle.json 里没声明的 feature编译直接报错 2006The product use a feature that is not supported by this part。BUILD.gn 消费feature 名直接变成 GN 变量。audio_framework 自己的services/audio_service/BUILD.gn767 行里满屏实证类似于Android的编译宏控开关if (audio_framework_feature_offline_effect) { defines [ FEATURE_OFFLINE_EFFECT ] // :284-285 } if (audio_framework_feature_inner_capturer) { defines [ HAS_FEATURE_INNERCAPTURER ] // :288-289 } if (audio_framework_feature_distributed_audio true) { // :361 ... # 整个分布式音频源码块编/不编 } if (!audio_framework_feature_new_engine_flag) { # :405 旧引擎代码块 ... }配置3部件编译入口PS如上每一行配置都是一个模块的编译入口即一个部件是由多个模块组成。在继续看看build的inner_kits字段他是一个数组参考如下代码看起来是为每个模块指定了头文件的路径。配置4模块依赖inner_kitsinner_kits 是部件对外开的接口窗口——跨部件依赖的唯一合法通道。部件之间想互相调用不能直接 deps 别人的模块必须走两扇登记提供方在 inner_kits 里开窗使用方在 BUILD.gn 里用 external_deps 走窗。这是 OH 部件化解耦的强制机制。inner_kits每个条目的结构如下{ name: //foundation/multimedia/av_session/frameworks/native/session:avsession_client, header: { header_files: [avsession_manager.h, av_session.h, ...5个...], header_base: //foundation/multimedia/av_session/interfaces/inner_api/native/session/include } }name窗口后面是哪个 GN 目标——必须是个可链接的库这里是avsession_clientohos_shared_library。别的部件 external_deps 写的就是这个目标名。header.header_base公开头文件所在的目录include 根。header.header_files这个目录里允许被别人看到的头文件白名单——这就是你问的为什么有无数个 header。2multimedia有哪些部件先看看subsystem_config.json里面对multimedia子系统是如何定义的其源码路径在foundation/multimedia目录下该目录下有如下多个子目录那么如上目录下是是不是每个目录都有一个bundle.json呢foundation\multimedia\audio_framework\bundle.json{ name: ohos/audio_framework, description: Audio standard provides managers and provides the audio resources to application for play/record audio, version: 4.0, license: Apache License 2.0, publishAs: code-segment, segment: { destPath: foundation/multimedia/audio_framework },foundation\multimedia\audio_lite\bundle.jsonfoundation\multimedia\av_codec\bundle.json{ name: ohos/av_codec, description: Media standard provides atomic capabilities, version: 3.1, license: Apache License 2.0, publishAs: code-segment, segment: { destPath: foundation/multimedia/av_codec }, dirs: {}, scripts: {}, component: { name: av_codec, subsystem: multimedia, adapted_system_type: [ standard ],foundation\multimedia\av_session\bundle.json{ name: ohos/av_session, description: Audio and Video Session Management, version: 4.0, license: Apache License 2.0, publishAs: code-segment, segment: { destPath: foundation/multimedia/av_session }, dirs: {}, scripts: {}, component: { name: av_session, subsystem: multimedia, syscap: [ SystemCapability.Multimedia.AVSession.AVCast, SystemCapability.Multimedia.AVSession.Core, SystemCapability.Multimedia.AVSession.ExtendedDisplayCast, SystemCapability.Multimedia.AVSession.Manager, SystemCapability.Multimedia.AVSession.AVInputCast false ], features: [ av_session_enable_start_stop_on_demand ], adapted_system_type: [ standard ], rom: 3000KB, ram: 5120KB, hisysevent_config: [ //foundation/multimedia/av_session/hisysevent.yaml ],每个目录下都有一个这样的bundle.json即每个目录下都是一个部件而multimedia子系统有16个部件其中一些部件适用于standard有一些部件适用于mini或者small。3、模块1BUILD.gn配置解读总结
返回列表