基于hvigor插件定制构建
场景描述
在编译构建的过程中如何插入使用者需要的自定义构建任务,使用者可以通过什么方式获取扩展编译构建参数从而在运行时获取自定义的参数以及自定义修改编译产物属性。
能力说明
hvigor-ohos-plugin插件支持在hvigorfile.ts里插入使用者的自定义构建任务,并且在运行时获取到自定义的一些编译参数,以及实现修改产物属性的功能。
一、构建生命周期以及任务流程
1.构建生命周期
hvigor有三个不同的阶段,分为初始化、配置和执行,hvigor会按顺序运行这些阶段。
初始化:
- 根据命令参数和hvigor-config.json5文件中的配置,设置hvigor的构建参数,并构造出hvigor对象,此对象贯穿整个hvigor生命周期,从最开始创建出来一直到此次构建结束才被销毁。
 - 通过项目根目录下的build-profile.json5文件,创建出rootNodeDescriptor实例,并通过其中的modules字段,来初始化出工程中所有模块的NodeDescriptor对象实例。
 - 执行项目根目录下的hvigorconfig.ts文件,可以在此文件中通过hvigor的相关API来为生命周期注册hook或在构建开始时进行其他处理。
 - 构造出每个节点的HvigorNode对象实例。
 
配置:
- 执行每个node中的hvigorfile.ts文件,为每个node添加plugin,执行plugin的apply方法,并添加plugin的上下文。
 - 根据前一步加载出的plugin和task,构造出DAG图。
 
hvigor在执行任何任务之前会构建任务依赖图,所有任务会形成一个有向无环图(DAG),如下示例图,任务之间的依赖关系用箭头进行表示:

执行:
- 执行选定的任务。
 - 任务之间的依赖关系决定了任务执行顺序。
 - 任务可以并行执行。
 
2.HAP基础任务流程

任务详细说明可参阅官网.
二、获取自定义参数:
方式1(生成BuildProfile类文件):
HAP/HSP运行时获取编译构建参数。
生成BuildProfile类文件。
当前有以下几种方式可以生成BuildProfile类文件:
选中需要编译的模块,在菜单栏选择“Build > Generate Build Profile ${moduleName}”。
在菜单栏选择“Build > Build Hap(s)/APP(s) > Build Hap(s)”或“Build > Build Hap(s)/APP(s) > Build APP(s)”。

生成BuildProfile类文件后,在代码中可以通过如下方式引入该文件:
- import BuildProfile from'BuildProfile'。
 - 通过如下方式获取到构建参数:@State message: string = BuildProfile.BUNDLE_NAME。
 - 默认参数
 
生成BuildProfile类文件时,Hvigor会根据当前工程构建的配置信息生成一部分默认参数,开发者可以在代码中直接使用。
表1-1
参数名  | 类型  | 说明  | 
BUNDLE_NAME  | string  | 应用的Bundle名称。  | 
BUNDLE_TYPE  | string  | 应用的Bundle类型。  | 
VERSION_CODE  | number  | 应用的版本号。  | 
VERSION_NAME  | string  | 应用版本号的文字描述。  | 
TARGET_NAME  | string  | Target名称。  | 
PRODUCT_NAME  | string  | Product名称。  | 
BUILD_MODE_NAME  | string  | 编译模式。  | 
DEBUG  | boolean  | 应用是否可调试。  | 
- 自定义参数
 
开发者可以在模块级的build-profile.json5文件中增加自定义参数,在生成BuildProfile类文件后,在代码中使用自定义参数。
自定义参数可以在buildOption、buildOptionSet、targets节点下的arkOptions子节点中通过增加buildProfileFields字段实现,自定义参数通过key-value键值对的方式配置,其中value取值仅支持number、string、boolean类型。

方式2(hvigor API):
getBuildMode: () => string==>通过该方法获取编译模式(相关文档简单给出一些API)
const node = getNode(__filename); 
const appContext = node.getContext(OhosPluginId.OHOS_APP_PLUGIN) as OhosAppContext; 
const buildMode = appContext.getBuildMode(); 
console.log(buildMode);另外也提供了相关获取扩展参数的API:示例:getExtParams4.1.2+ ==>获取全部的-p扩展参数对象。
getExtParams(): Record<string, string> =>命令行中所有配置的-p参数集合对象。
import { hvigor } from '@ohos/hvigor'; 
const extParams = hvigor.getParameter().getExtParams(); 
console.log(extParams['key']); 
// 执行命令./hvigorw --sync -p key=hello,控制台打印:hello三、修改打包产物属性
hvigor-ohos-plugin插件支持在hvigorfile.ts里接收部分编译配置,以实现动态配置构建配置、并使能到构建的过程与结果中。
在hvigorfile.ts中,我们约定在导出的对象中的config.ohos属性里接收编译的配置,配置在overrides项中的参数,其优先级会高于在配置项中的对应字段。
工程级
appOpt:对应app.json5里的配置项字段。bundleName, bundleType, icon, label, vendor, versionCode, versionName。
模块级
buildOption:对应build-profile.json5里的buildOption配置项。
arkOptions, externalNativeOptions, napiLibFilterOption, nativeLib,resOptions, sourcOption。
方法一:通过overrides动态修改配置项中的参数
export default { 
  system: appTasks, 
  plugins: [], 
  config: { 
    ohos: { 
      overrides:{ 
        appOpt: { 
          versionCode: getVersionCode(), 
          versionName: getVersionName(), 
          icon: getIcon() 
        } //app.json中的内容 
      } 
    } 
  } 
} 
 
function getVersionCode() { 
  // return 100000+Date.now().getYear(); 
  return 1; 
} 
 
function getVersionName() { 
  // return `1.0.0${Date.now().getTime()}`; 
  return "2"; 
} 
 
function getIcon() { 
  // return 100000+Date.now().getYear(); 
  // return "C:\\Users\\xxx\\DevEcoStudioProjects\\DemoHvigorFile\\AppScope\\resources\\base\\media\\app_icon.png"; 
  return "C:\\Users\\xxx\\DevEcoStudioProjects\\DemoHvigorFile\\AppScope\\resources\\base\\media\\huawei.png"; 
} 
 
 
export default { 
  system: appTasks, 
  plugins:[customPlugin()] 
};
方法二:通过hvigor API 去修改
通过odesEvaluated hook获取插件向node中注册的context,然后通过这个context操作一些任务。
首先根据hvigorfile.ts路径获取当前节点->getNode(__filename)。
然后根据pluginId获取当前节点上指定插件的上下文接口信息->getContext()。
获取当前构建指定的BuildMode ->getBuildMode()。
最后根据编译模式去修改app.json5里的配置项字段从而修改产物属性。
import { appTasks, OhosPluginId, OhosAppContext } from '@ohos/hvigor-ohos-plugin'; 
import { hvigor, getNode } from '@ohos/hvigor'; 
 
hvigor.nodesEvaluated(() => { 
  const node = getNode(__filename); 
  const appContext = node.getContext(OhosPluginId.OHOS_APP_PLUGIN) as OhosAppContext; 
  const buildMode = appContext.getBuildMode(); 
  console.log(buildMode); 
  console.log('projectName:', appContext.getProjectName()); 
  const appJson5 = appContext.getAppJsonOpt(); 
  if (appContext.getBuildMode() === 'debug') { 
    appJson5.app.versionName = '1.0.0'; 
  } else { 
    appJson5.app.versionName = '1.0.1'; 
  } 
  appContext.setAppJsonOpt(appJson5); 
}); 
 
export default { 
  system: appTasks, /* Built-in plugin of Hvigor. It cannot be modified. */ 
  plugins:[] /* Custom plugin to extend the functionality of Hvigor. */ 
}四、自定义构建任务
通过在hvigorfile.ts里插入自定义任务。
首先在hvigorfile.ts文件中只需定义一个pluginId(任务Id)和name(任务名称),并实现其中的run方法,即可创建一个任务。
然后当前node节点添加插件并执行插件的apply方法,然后调用接口pluginContext中的registerTask方法,在此方法中去编写自定义任务,设置任务名称。
实现其中的run方法如下demo打印任务名称,模块名称moduleName,模块的绝对路径modulePath。
最后在设置任务插入执行流程的位置。
import { hapTasks } from '@ohos/hvigor-ohos-plugin'; 
 
export function customPluginFunction1(str?: string) { 
  return { 
    pluginId: 'CustomPluginID1', 
    apply(pluginContext) { 
      //注册自定义任务 接口pluginContext 方法registerTask 
      pluginContext.registerTask({ 
        // 编写自定义任务 
        name: 'customTask1', 
        run: (taskContext) => { 
          // taskContext.moduleName; 
          // taskContext.modulePath; 
          //接口 taskContext  模块名称 moduleName 模块的绝对路径 modulePath 
          console.log('customTask1: ',taskContext.moduleName, taskContext.modulePath); 
        }, 
        // 确认自定义任务插入位置 
        dependencies: ['default@BuildJS'], 
        postDependencies: ['default@CompileArkTS'] 
      }) 
    } 
  } 
} 
export function customPluginFunction2(str?: string) { 
  return { 
    pluginId: 'CustomPluginID2', 
    apply(pluginContext) { 
      pluginContext.registerTask({ 
        name: 'customTask2', 
        run: (taskContext) => { 
          console.log('customTask2: ',taskContext.moduleName, taskContext.modulePath); 
        }, 
        dependencies: ['default@BuildJS'], 
        postDependencies: ['default@CompileArkTS'] 
      }) 
    } 
  } 
} 
export function customPluginFunction3(str?: string) { 
  return { 
    pluginId: 'CustomPluginID3', 
    apply(pluginContext) { 
      pluginContext.registerTask({ 
        name: 'customTask3', 
        run: (taskContext) => { 
          console.log('customTask3: ',taskContext.moduleName, taskContext.modulePath); 
        }, 
        dependencies: ['customTask1'], 
        postDependencies: ['customTask2'] 
 
      }) 
    } 
  } 
} 
export default { 
  system: hapTasks, // Hvigor内置插件,不可修改 
  plugins: [customPluginFunction1(), customPluginFunction2(),customPluginFunction3()]       // 自定义插件 
}定制任务插入位置效果:





















