1.概述 官方文档
编译构建工具DevEco Hvigor(以下简称Hvigor)是一款基于TS实现的构建任务编排工具,主要提供任务管理机制,包括任务注册编排、工程模型管理、配置管理等关键能力,提供专用于构建和测试应用的流程和可配置设置。
2.构建系统生命周期
2.1.工程结构定义
Hvigor将工程解析为一个树形结构,项目为树的根节点,项目中的每个模块为树的叶子节点,树最多为两层,模块中不能包含其他模块,在Hvigor的定义中统称项目或模块为一个node(节点)。
在构建最开始的初始化阶段,会通过hvigorconfig.ts文件以及工程级build-profile.json5文件中的配置来构造出一个树形结构存储项目的工程结构,工程级build-profile.json5文件和hvigorconfig.ts文件均可以配置多模块。
2.2.hvigor脚本文件
构建的生命周期中hvigor使用两个脚本文件来完成插件、任务以及生命周期hook的注册:
- hvigorconfig.ts:此文件在整个项目中只有根目录下存在一份,不是构建必须的文件并且默认不存在,如有需要可自行创建,此文件被解析执行的时间较早,可用于在hvigor生命周期刚开始时操作某些数据。
- hvigorfile.ts:此文件在每个node下都有一份,是构建的必须文件,在此文件中可以注册插件、任务以及生命周期hook等操作。
3.角色的区分
3.1.hvigor与hvigor-ohos-plugin的关系
概述部分提到了hvigor提供任务注册编排以及配置管理等任务管理机制,它负责控制任务的执行流程,但是并不包含每一个任务的具体业务逻辑,具体逻辑是由hvigor-ohos-plugin提供的。
hvigor和hvigor-ohos-plugin的关系可以通过下图来说明,hvigor接受任务的注册并编排任务执行顺序,并按照顺序依次调用hvigor-ohos-plugin中的插件来执行任务。如果您定制了自己的任务逻辑插件并将其注册,hvigor-ohos-plugin也会调用您的个性化插件来完成编译构建流程。
在hvigor执行构建的过程中,hvigor-ohos-plugin会向hvigor进行任务的注册,hvigor会根据构建的任务执行有向图依次调用对应的插件来执行相应任务,在完成编译、打包、签名等一系列任务后,hvigor也就正式完成了构建。
4.hvigor生命周期
hvigor一次完整的编译构建流程。hvigor的编译构建过程有三个不同的阶段,分为初始化、配置和执行,hvigor会按顺序运行这些阶段。
1.初始化
说明:
此阶段主要目的为初始化项目的编译参数,构造出项目结构的树形数据模型,每个node为一个HvigorNode对象。
- 根据命令参数和hvigor-config.json5文件中的配置,设置hvigor的构建参数,并构造出hvigor对象和hvigorConfig对象:
- hvigor对象贯穿整个hvigor生命周期,从最开始创建出来一直到此次构建结束才被销毁;
- hvigorConfig对象用于表示hvigor对项目结构的抽象,是hvigor的简单配置对象,用于动态添加或删除节点,它也会保存对每个节点的描述对象(nodeDescriptor对象)。
- 通过项目根目录下的build-profile.json5文件,创建出rootNodeDescriptor实例,并通过其中的modules字段,来初始化出工程中所有模块的NodeDescriptor对象实例。
- 执行项目根目录下的hvigorconfig.ts文件,可以在此文件中通过hvigor的相关API来为生命周期注册hook或在构建开始时进行其他处理。
- 根据节点描述对象构造出每个节点的HvigorNode对象实例。
2.配置
说明
此阶段开始时,所有的node都已经加载完毕,但每个node中还没有加载插件(plugin)、任务(task)和DAG图,此阶段的主要目的就是加载出这些内容。
- 执行每个node中的hvigorfile.ts文件,为每个node添加plugin(向hvigor注册任务),执行plugin的apply方法,并添加plugin的上下文。
- 根据前一步加载出的plugin和task,根据任务执行的依赖关系构造出DAG图。
3.执行
- 执行选定的任务。
- 任务之间的依赖关系决定了任务执行顺序。
- 任务可以并行执行。
5.生命周期及hook点
在hvigor的生命周期中,以下多个hook点可供您使用,便于您在对应的时机调用某些逻辑。
在下图中所有绿色标记的线框为可以使用的hook点。每个hook点的使用方式请参考基础构建能力。
6.自定义插件开发流程
hvigor主要提供了两种方式以实现插件的开发:
两种方式的核心逻辑实现类似,都是以TS文件编写Task任务方法,区别主要在共享和应用方式上。
插件项目 | 代码开发 | 共享方式 | 插件使用 | 特点总结 | |
---|---|---|---|---|---|
基于hvigorfile脚本 | 不创建项目 | 直接编辑工程/模块中hvigorfile.ts文件 | 不发布,复制代码实现共享 | 代码逻辑直接应用于编辑工程/模块 | 开发使用快速;共享复用不方便 |
基于typescript项目 | 新建npm项目 | 新建custom-plugin.ts文件 | npm打包发布共享,或离线包共享 | hvigor-config.json5中配置插件依赖,或安装离线包 | 易于分发、共享和维护;发布使用流程相对多 |
7.自定义插件(删除某个模块/删除模块的依赖项)
7.1.基于hvigorfile脚本开发
7.2.创建工程根目录下的hvigorconfig.ts文件(该文件默认是没有的,需自行创建)
7.3.结合基础构建能力对齐配置文件进行操作,也可以通过查看源码进行编写。
7.4.hvigor源码位置:/Applications/DevEco-Studio.app/Contents/tools/hvigor/hvigor-ohos-plugin
- hvigorconfig.ts文件
import { hvigor } from '@ohos/hvigor';
import { OhosPluginId , OhosHapContext} from '@ohos/hvigor-ohos-plugin';
export { HvigorPlugin, HvigorNode, HvigorTask, HvigorTaskContext, Task, TaskInput, TaskOutput, } from '@ohos/hvigor';
console.error('ych' , `----------获取跟工程 build-profile.json5 中的所有描述对象-----------`)
//获取所有的node描述对象的数组
const allNodeDescriptors : HvigorNodeDescriptor[]= hvigor.getHvigorConfig().getAllNodeDescriptor();
for (let i = 0; i < allNodeDescriptors.length; i++) {
console.error('ych' , `当前的allNodeDescriptors的节点对象名称为:${allNodeDescriptors[i].name} ----- 路径为:${allNodeDescriptors[i].srcPath}`)
}
console.error('ych' , `-----------删除节点对象名称为:tools-----------`)
const hvigorConfig = hvigor.getHvigorConfig();
hvigorConfig.excludeNodeByName('tools');
console.error('ych' , `----------删除后 获取跟工程 build-profile.json5 中的所有描述对象-----------`)
//获取所有的node描述对象的数组
const deleteAllNodeDescriptors : HvigorNodeDescriptor[]= hvigor.getHvigorConfig().getAllNodeDescriptor();
for (let i = 0; i < deleteAllNodeDescriptors.length; i++) {
console.error('ych' , `当前的allNodeDescriptors的节点对象名称为:${deleteAllNodeDescriptors[i].name} ----- 路径为:${deleteAllNodeDescriptors[i].srcPath}`)
}
- 某个节点下的hvigorfile文件,这里采用entry
import { hapTasks } from '@ohos/hvigor-ohos-plugin';
import { OhosPluginId } from '@ohos/hvigor-ohos-plugin';
export { HvigorPlugin, HvigorNode, HvigorTask, HvigorTaskContext, Task, TaskInput, TaskOutput } from '@ohos/hvigor';
export default {
system: hapTasks, /* Built-in plugin of Hvigor. It cannot be modified. */
plugins:[
entryPlugin()
] /* Custom plugin to extend the functionality of Hvigor. */
}
//实现自定义插件
function entryPlugin():HvigorPlugin {
return {
pluginId : 'entryPlugin',
apply : (node: HvigorNode) => {
console.error('ych' , `entryPlugin ------${node.getNodeName()}`)
//1.通过OhosPluginId获取Hap的上下文
let hapCtx = node.getContext(OhosPluginId.OHOS_HAP_PLUGIN) as OhosHapContext
//2.通过上下文获取getDependenciesOpt依赖项
let dependencies = hapCtx.getDependenciesOpt()
console.log('------------------- 获取依赖项信息 --------------------')
console.log(dependencies)
console.log('------------------- 设置依赖项 --------------------')
//3.重制新的依赖项
hapCtx.setDependenciesOpt({
'home': 'file:../home',
})
//还可以操作其他配置文件build-profile.json5等
//let buildProfile = hapCtx.getBuildProfileOpt()
}
}
}
7.5.最终.hap包中并不包含tools模块,包的大小也发生改变。
8.总结
- 1.我们可以在编译构建时通过自定义插件修改模块的配置文件信息。
- 删除依赖,删除模块。
- 替换配置项,应用包名等等
- 2.学习hvigor自定义插件,也可以参考 HMRouter路由框架
9.FAQ
- 为什么通过动态加载的方式 + 删除模块 + 删除依赖项,在hvigor执行时期会出现问题。
期望时某个模块是通过动态加载方式加载某个本地模块下的文件,但是在编译打包时想要删除这个模块。目前还存在问题,还在继续探索。