(鸿蒙)编译构建工具—开发Hvigor插件

1.概述   官方文档

  编译构建工具DevEco Hvigor(以下简称Hvigor)是一款基于TS实现的构建任务编排工具,主要提供任务管理机制,包括任务注册编排、工程模型管理、配置管理等关键能力,提供专用于构建和测试应用的流程和可配置设置。

2.构建系统生命周期

2.1.工程结构定义

Hvigor将工程解析为一个树形结构,项目为树的根节点,项目中的每个模块为树的叶子节点,树最多为两层,模块中不能包含其他模块,在Hvigor的定义中统称项目或模块为一个node(节点)。

image.png

在构建最开始的初始化阶段,会通过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对象。

  1. 根据命令参数和hvigor-config.json5文件中的配置,设置hvigor的构建参数,并构造出hvigor对象和hvigorConfig对象:
    • hvigor对象贯穿整个hvigor生命周期,从最开始创建出来一直到此次构建结束才被销毁;
    • hvigorConfig对象用于表示hvigor对项目结构的抽象,是hvigor的简单配置对象,用于动态添加或删除节点,它也会保存对每个节点的描述对象(nodeDescriptor对象)。
  2. 通过项目根目录下的build-profile.json5文件,创建出rootNodeDescriptor实例,并通过其中的modules字段,来初始化出工程中所有模块的NodeDescriptor对象实例。
  3. 执行项目根目录下的hvigorconfig.ts文件,可以在此文件中通过hvigor的相关API来为生命周期注册hook或在构建开始时进行其他处理。
  4. 根据节点描述对象构造出每个节点的HvigorNode对象实例。

2.配置

说明
此阶段开始时,所有的node都已经加载完毕,但每个node中还没有加载插件(plugin)、任务(task)和DAG图,此阶段的主要目的就是加载出这些内容。

  1. 执行每个node中的hvigorfile.ts文件,为每个node添加plugin(向hvigor注册任务),执行plugin的apply方法,并添加plugin的上下文。
  2. 根据前一步加载出的plugin和task,根据任务执行的依赖关系构造出DAG图。

3.执行

  • 执行选定的任务。
  • 任务之间的依赖关系决定了任务执行顺序。
  • 任务可以并行执行。

5.生命周期及hook点

在hvigor的生命周期中,以下多个hook点可供您使用,便于您在对应的时机调用某些逻辑。

在下图中所有绿色标记的线框为可以使用的hook点。每个hook点的使用方式请参考基础构建能力

image.png

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执行时期会出现问题。
    期望时某个模块是通过动态加载方式加载某个本地模块下的文件,但是在编译打包时想要删除这个模块。目前还存在问题,还在继续探索。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容