扩展阅读:
如何开发一个Hvigor插件,此处不再详细介绍。详情可查看“扩展阅读”列举的官方参考文档。
本文主要对 Hvigor插件中的重要组件 以及 实战项目“HelloPlugin编译插件”的情况做一个概述。
一、基本概念
在鸿蒙项目的编译过程中,Hvigor是基于 任务(Task) 对项目进行自动化构建的。
- 任务是Hvigor构建过程中的基本执行单元,它定义了构建项目时需要执行的具体工作。 Task可以完成多种操作,比如源码编译任务,打包任务或签名任务等。
- HvigorPlugin 定义了插件的基本范式,是多个Task的合集
- 任务是存在依赖关系的,Hvigor在执行任何任务之前会构建任务依赖图,所有任务会形成一个有向无环图(DAG), 例如 :HAP基础任务流程图
查看任务树: 会根据工程编译中注册的任务树以下图形式输出
hvigorw taskTree
二、HelloPlugin编译插件分析
2.1 模块
HelloPlugin编译插件包含2个模块,分别为:
@hello-plugin/hello-hvigor-common:作为一个公共部分,抽取插件开发的基类(如BasePlugin、BaseTask等),定义插件开发的基本结构,维护公共能力以及工具类等。@hello-plugin/hello-hvigor-plugin:面向业务的编译过程,负责将业务编译产物中module.json5文件中关于xxx的信息进行补全或者删除
@hello-plugin/hello-hvigor-plugin在package.json文件中声明@hello-plugin/hello-hvigor-common模块的依赖项,即可引入。
如:
{
"name": "@hello-plugin/hello-hvigor-plugin",
// 略...
"dependencies": {
"@hello-plugin/hello-hvigor-common": "file:../HelloCommon",
},
"bundledDependencies": [
"@hello-plugin/hello-hvigor-common"
]
}
其中,bundledDependencies字段是一个包含依赖包名的数组对象,在发布时会将这个数组中声明的依赖包打到最终的发布包里。
2.2 产物/交付件
HelloPlugin编译插件包含1个交付件,为:
@hello-plugin/hello-hvigor-plugin
其中,@hello-plugin/hello-hvigor-common 模块作为本地依赖项,在发布插件时,会打包到上述插件的交付件中
2.3 发布
编译插件由 npm 进行打包和发布的。
- npm 安装依赖:
npm install - npm 发布:
npm publish - npm 打包:
npm pack - npm 仓:略...
2.4 调用
每个Task可以声明前置/后置Task,用于说明Task在任务依赖图内的位置,即期望自己的Task运行在这些前置task的后面/后置task的前面。
业务在使用插件时,会在业务工程的module级的hvigorfile.ts文件中调用编译插件的接口(如:genHelloHspCompilePlugin()),然后Hvigor会将上述Task注册到编译过程中。
当编译工程时,会根据Task声明的顺序,在合适的时机执行。
三、HelloPlugin编译插件实战
此处以一个自定义Task为例子,介绍了如何使用 @hello-plugin/hello-hvigor-common 中的基类
3.1 @hello-plugin/hello-hvigor-common中的基类
3.1.1 BaseTask.ts
export abstract class BaseTask {
// 任务名称
name: string;
// 任务前置依赖,先执行 preDependency 的任务,再执行 自定义任务
preDependency: string = '';
// 任务后置依赖,先执行 自定义任务,再执行 postDependency 的任务
postDependency: string = '';
constructor(name: string, preDependency: string, postDependency: string) {
this.name = name;
this.preDependency = preDependency;
this.postDependency = postDependency;
}
abstract run(context: ModuleContextType, moduleName: string, modulePath: string, targetName: string, productName: string): void;
}
// 注:export type ModuleContextType = OhosHarContext | OhosHapContext | OhosHspContext;
3.1.2 BaseTaskGroup.ts
/**
* BaseTaskGroup : 绑定 context 和 Task 的关系,
* 即只有在编译 App/Hap/Hsp/Har(context) 下才执行这些Task
*/
export abstract class BaseTaskGroup {
groupId: string = '';
constructor(groupId: string) {
this.groupId = groupId;
}
/**
* 获取 Task 工作时的 contextType
* 如:在har包的module中,不适合执行与hap相关的task
*/
abstract requireContextType(): Set<string>;
/**
* 向 Group 添加待执行的 Task
*/
abstract requireTask(): Array<BaseTask>;
}
3.1.3 BaseHelloPlugin
const TAG = "BaseHelloPlugin";
export abstract class BaseHelloPlugin implements HvigorPlugin {
pluginId: string = '';
protected plugin: PluginDefine | undefined;
protected taskGroupList: Array<BaseTaskGroup> = [];
constructor(pluginId: string) {
this.pluginId = pluginId;
}
registerTaskGroup(taskGroup: BaseTaskGroup): this {
this.taskGroupList.push(taskGroup)
return this;
}
apply(node: HvigorNode): void | Promise<void> {
Logger.info(TAG, `begin to apply pluginId=${this.pluginId}`);
this.plugin = new PluginDefine(node);
const targetContext = this.plugin.getCurrentModuleCxt();
if (targetContext === undefined) {
Logger.info(TAG, `Context is undefined. exit current task`);
return;
}
// 注册任务
this.filterTask(this.plugin, this.taskGroupList).forEach((task) => {
targetContext.targets((target: Target) => {
try {
this.registerTask(node, task, targetContext, target);
} catch (e) {
Logger.error(TAG, `register Task exception: ${JSON.stringify(e)}, ${e}`)
}
});
})
}
/**
* 制定过滤规则,在 taskGroupList 中选择部分task 进行执行
* 默认规则:按照 task 支持的 context 进行过滤,即只有task 的 context 与当前插件集成方工程构建的需求一致时,才执行。
* 子类可以按需 override 这个方法,设置自定义的规则
*/
protected filterTask(plugin: PluginDefine, taskGroupList: Array<BaseTaskGroup>): Array<BaseTask> {
let ret: Array<BaseTask> = []
let cxtType: string = plugin.getCurrentCxtType();
if (cxtType === undefined) {
return ret;
}
Logger.info(TAG, `filter current Context: ${cxtType}`)
// 对 taskGroupList 进行过滤,将符合当前场景的Group
for (let group of taskGroupList) {
let requiredCxtSet = group.requireContextType();
if (requiredCxtSet.has(cxtType)) {
Logger.info(TAG, `filter taskGroup : ${group.groupId}`)
ret = ret.concat(group.requireTask())
}
}
return ret;
}
/**
* 向当前节点注册 task
*/
private registerTask(node: HvigorNode, task: BaseTask, targetContext: ModuleContextType, target: Target): void {
const targetName = target.getTargetName();
const productName = target.getCurrentProduct().getProductName()
// 校验 依赖的 前后任务 是否存在
let preDep = `${targetName}@${task.preDependency}`;
let postDep = `${targetName}@${task.postDependency}`;
Logger.info(TAG, `begin to register Task: taskName=${task.name}, targetName:${targetName},
productName:${productName}, preDep=${preDep}, postDep=${postDep}`);
if (node.getTaskByName(preDep) === undefined ||
node.getTaskByName(postDep) === undefined) {
Logger.error(TAG, `task on depending not exist. preDep:${preDep}, postDep:${postDep}`)
return;
}
// 注册自己的任务task
node.registerTask(
{
// 任务名称
name: task.name,
// 任务执行体
run: (taskContext: HvigorTaskContext) => {
try {
Logger.info(TAG, "begin to execute Task. taskName=" + task.name + " moduleName=" + taskContext.moduleName);
task.run(targetContext, taskContext.moduleName, taskContext.modulePath, targetName, productName);
Logger.info(TAG, "end to execute Task. taskName=" + task.name);
} catch (e) {
Logger.error(TAG, `execute Task exception: ${JSON.stringify(e)}, ${e}`);
}
},
dependencies: [`${targetName}@${task.preDependency}`],
postDependencies: [`${targetName}@${task.postDependency}`],
}
);
}
}
3.2 @hello-plugin/hello-hvigor-plugin中的实现类
3.2.1 自定义Task:GenCodeTask
const TAG = 'GenCodeTask'
const TASK_NAME = 'GenCodeTask'
const TASK_NAME_HAR_PRE_BUILD = "PreBuild"
const TASK_NAME_HAR_BUILD_NATIVE_WITH_CMAKE = "BuildNativeWithCmake"
export class GenCodeTask extends BaseTask {
constructor() {
// 设置 自定义Task名称、前置Task 和 后置Task
super(TASK_NAME, TASK_NAME_HAR_PRE_BUILD, TASK_NAME_HAR_BUILD_NATIVE_WITH_CMAKE);
}
run(context: ModuleContextType, moduleName: string, modulePath: string, targetName: string, productName: string): void {
// do something
}
}
3.2.2 自定义Group:GenCodeTaskGroup
const GROUP_ID = "GenCodeTaskGroup"
export class GenCodeTaskGroup extends BaseTaskGroup {
constructor() {
super(GROUP_ID);
}
requireContextType(): Set<string> {
return new Set<string>([OhosPluginId.OHOS_HAR_PLUGIN]);
}
requireTask(): Array<BaseTask> {
return [
new GenCodeTask(),
];
}
}
3.2.3 自定义Plugin:HelloGenCodePlugin
const PLUGIN_ID = "HelloGenCodePlugin"
export class HelloGenCodePlugin extends BaseHelloPlugin {
constructor() {
super(PLUGIN_ID);
}
}
/**
* 对外接口
* 在 Index.ts 文件中将接口导出即可
*/
export function genHelloGenCodePlugin(): HvigorPlugin {
return new HelloGenCodePlugin()
.registerTaskGroup(new GenCodeTaskGroup())
}
3.3 工程项目实战
详见GitHub(待补充)