前言

OpenHarmony 应用和服务使用 Hvigor 作为工程的构建工具。 上一篇文章 hvigor的基本用法 介绍了 Hvigor 的基本用法和配置。本篇文章将介绍 Hvigor 的构建流程,通过修改脚本配置使 Hvigor 执行自定义任务。

Hvigor 的构建流程

  1. 加载命令行参数和环境变量;

  2. 初始化项目结构,创建 Project 和 Module 实例;

  3. 配置项目插件和任务流;

  4. 执行任务流;

// hvigor/index.js // LiftOff 命令行辅助工具 const cli = new LiftOff({     name: 'hvigor',     processTitle: make_title.makeTitle('hvigor', process.argv.slice(2)),     moduleName: exports.hvigorPath,// @ohos/hvigor-base     configName: "hvigorFile",     v8flags: v8flags,     extensions: interpret.jsVariants }); // cli options定义所有的可支持的命令行 const parser = yargs.usage("Usage", cli_options.cliOptions); // 解析命令行 const opts = parser.argv; function run() {     cli.prepare({         cwd: opts.cwd,         require: opts.require,         completion: opts.completion,    }, function (env) {         // help 指令         if (opts.help) {             yargs.usage('Usage: hvigor [options]')                .example('hvigor assembleApp', 'Do assembleApp task')                .help('h')                .alias('h', 'help')                .epilog('copyright 2021')                .argv;             exit.exit(0);        }         // version 指令         if (opts.version) {             _log.info('CLI version:', cliVersion);             _log.info('Local version:', env.modulePackage.version || 'Unknown');             exit.exit(0);        }         // 设置日志等级         evaluateLogLevel();          // 判断当前 nodejs工具版本信息         const LOWEST_VERSION = "v14.18.3";         if (process.version < LOWEST_VERSION) {             _log.warn(`node version: ${process.version}`);             _log.warn(`node version cannot be lower than ${LOWEST_VERSION}`);             process.exit(-1);        }         // 1. 加载命令行参数和环境变量         init_env_config_props.initEnvConfigProps(env, opts);         cli.execute(env, env.nodeFlags, function () {             return __awaiter(this, void 0, void 0, function* () {                 const taskBeginTime = process.hrtime();                 // 执行具体的 Hvigor 任务                 yield process_utils.processUtils(opts, env);                 const taskEndTime = process.hrtime(taskBeginTime);                 const realTime = pretty_hrtime.default)(taskEndTime);                 if (0 == profile_js.profile.executed) {                     _log.error(`No task found to execute in project!`);                }                 _log.info(`BUILD SUCCESSFUL in ${realTime}`);            });        });    }); }

执行具体的 Hvigor 的任务

// hvigor/src/process-utils.js function processUtils(opts, env) {     ···     // 2. 初始化项目结构,创建 Project 和 Module 实例;     init.init(env);     // 3. 配置项目插件和任务流;     const project = configuration.configuration(env);     // 4. 执行任务流;     const executeMode = env.configProps.get(modeAlias);     switch (executeMode) {        ···    } }

1. 加载命令行参数和环境变量

读取命令行参数,并把参数和配置文件的路径放入全局配置项中。

// hvigor/src/init-env-config-props.js function initEnvConfigProps(env, opts) {     ···     // 获取项目级别 build-profile.json5 文件路径,[项目路径]/build-profile.json5     const configFilePath = path.resolve(process.cwd(), hvigor_base.HvigorCommonConst.PROJECT_CONFIG_FILE);     ···     const properties = new Map();     // 获取命令行 prop(p) 参数,放入 Map 中     if (opts.prop !== undefined) {        [].concat(opts.prop).forEach((value) => {             const arr = value.split('=');             properties.set(arr[0], arr[1]);        });    }     // 把"项目级别 build-profile.json5 文件路径"、"命令行 prop(p) 参数集合"和"命令行 mode(m) 模式参数"配置进环境变量中     env.configProps = new Map([        [configFileName, configFilePath],        [propertiesAlias, properties],        [modeAlias, opts.mode]    ]);     return configFilePath; }

2. 初始化项目结构,创建 Project 和 Module 实例

工程结构实例化。

// hvigor/src/lifecycle/init.js function init(env) {     // env.modulePath 是在命令行辅助工具 LiftOff 中加载的模块函数,"modulePath": "[项目路径]/node_modules/@ohos/hvigor-base/index.js",     const vigorConfigInst = require(env.modulePath).vigorConfigInst;     // 把从命令行加载来的 prop(p) 参数放入 vigorConfigInst 实体的 ExtraConfig 中     vigorConfigInst.setExtraConfig(env.configProps.get(propertiesAlias));     // 初始化项目,参数:[项目路径]/build-profile.json5,[项目路径]     vigorConfigInst.initRootProject(path.resolve(env.cwd, hvigor_base.HvigorCommonConst.PROJECT_CONFIG_FILE), env.cwd); }
// hvigor-base/index.js exports.vigorConfigInst = new VigorConfig(); class VigorConfig {     constructor() {         this._log = hvigor_log_js.HvigorLogger.getLogger(VigorConfig.name);         this._project = undefined;         this._projectDir = "";         this._projectConfigFile = "";         this._extraConfig = new Map();    }     ···     // 初始化项目,参数:[项目路径]/build-profile.json5,[项目路径]     initRootProject(projectConfigFile, projectRootDir) {         // 配置项目级别 build-profile.json5 文件路径         this._projectConfigFile = projectConfigFile;         // 配置项目根路径         this._projectDir = projectRootDir;         // 创建项目实例,参数:[项目名称],[项目路径]         const projectImpl = new project_impl_js.ProjectImpl(path.basename(projectRootDir), projectRootDir);         // 读取 [项目路径]/build-profile.json5 配置数据         const projectStructureOpt = json5_reader_js.Json5Reader.getJson5Obj(this._projectConfigFile);         ···         // 从配置文件中读取项目下的全部 Modules          projectStructureOpt.modules?.forEach(module => {             // 校验 Module 配置参数             validate_util_js.ValidateUtil.validateModule(module);             // 添加子项目,根据配置文件中 Modules 循环创建 Module 实例,参数:项目实例,Module 名称,Module 的路径             projectImpl.addSubProject(new module_impl_js.ModuleImpl(projectImpl, module.name, module.srcPath));        });         this._project = projectImpl;         return projectImpl;    } } class ValidateUtil {     static validateModule(module) {         if (module.name === undefined) {             this.logger.errorMessageExit(`Project level build-profile.json5 lose required property: module-name`);        }         if (module.srcPath === undefined) {             this.logger.errorMessageExit(`Project level build-profile.json5 lose required property: module-srcPath`);        }    } }

Project 实现类

// hvigor-base/src/impl/project-impl.js class ProjectImpl extends default_module_impl.DefaultModuleImpl {     constructor(moduleName, moduleDir) {         super(moduleName, moduleDir);         // 配置项目级别 build-profile.json5 文件路径         this._projectStructureFile = path.resolve(moduleDir, common_const.HvigorCommonConst.PROJECT_CONFIG_FILE);         this._subProjects = new Map();    }     ··· }

Module 实现类

// hvigor-base/src/impl/module-impl.js class ModuleImpl extends default_module_impl.DefaultModuleImpl {     constructor(project, moduleName, moduleDir) {         super(moduleName, moduleDir);         this._project = project;    }     ··· }

默认 Module 实现类

// hvigor-base/src/impl/default-module-impl.js class DefaultModuleImpl {     constructor(moduleName, modulePath) {         this._moduleName = moduleName;         this._modulePath = modulePath;         // 获取项目和模块的 package.json 文件路径         this._packageJsonPath = path.resolve(modulePath, "package.json");         // 获取项目和模块的 hvigorfile.js 文件路径         this._buildFilePath = path.resolve(modulePath, common_const.HvigorCommonConst.BUILD_FILE_NAME);    }     ··· }

3. 配置项目插件和任务流

加载 hvigorfile.js 文件中配置的任务脚本。

// hvigor/src/lifecycle/configuration.js function configuration(env) { // 整个项目的结构在 init 中初始化完成,读取 vigorConfigInst const vigorConfigInst = require(env.modulePath).vigorConfigInst; // 通过 vigorConfigInst 获取项目实例 const project = vigorConfigInst.getProject(); // 获取项目全部的 Module 实例,遍历加载任务脚本 for (const subModule of project.getAllSubProjects()) { // 获取 Module 的 hvigorfile.js 文件路径 const subModuleVigorFileJs = subModule.getBuildFilePath(); // 加载任务脚本 configModule(subModule, subModuleVigorFileJs); } // 获取 Project 的 hvigorfile.js 文件路径 const projectVigorFileJs = path.resolve(env.cwd, hvigor_base.HvigorCommonConst.BUILD_FILE_NAME); // 加载任务脚本 configModule(project, projectVigorFileJs); return project; } function configModule(module, hvigorFilePath) { // FA 模型 // 工程级别 // module.exports = require('@ohos/hvigor-ohos-plugin').legacyAppTasks // 模块级别 // module.exports = require('@ohos/hvigor-ohos-plugin').legacyHapTasks // module.exports = require('@ohos/hvigor-ohos-plugin').legacyHarTasks // Stage 模型 // 工程级别 // module.exports = require('@ohos/hvigor-ohos-plugin').appTasks // 模块级别 // module.exports = require('@ohos/hvigor-ohos-plugin').hapTasks // module.exports = require('@ohos/hvigor-ohos-plugin').harTasks // 加载 hvigorFile 任务脚本 const moduleExported = require(hvigorFilePath); // 配置 Project 和 Module 的任务流 const pluginNTasks = moduleExported(module); module.exported = pluginNTasks; // 绑定创建的 Plugin 对象到 Project 实例和 Module 实例上 module.bindPlugin(pluginNTasks.plugin); }

4. 执行任务流

根据命令行 mode 参数判断要执行任务流的级别。

// hvigor/src/process-utils.js // 从环境变量中获取 mode(m) 参数 const executeMode = env.configProps.get(modeAlias); // 根据 mode 判断要执行任务流的级别 switch (executeMode) { case hvigor_base.HvigorBuildConst.PROJECT_MODE: // project execute_task.execute(project, true, opts).then(outputInfo); break; case hvigor_base.HvigorBuildConst.MODULE_MODE: // module execute_task.executeModuleTask(project, opts).then(outputInfo); break; case undefined: // 未配置 execute_task.execute(project, false, opts); execute_task.executeModuleTask(project, opts).then(outputInfo); break; default: _log.error(`Unknown mode '${executeMode}' specified in command line,Please check!`); }

执行任务

//hvigor/src/lifecycle/execute-task.js function executeModuleTask(project, opts) { // 获取 prop(p) 参数中 module 的取值 const modules = hvigor_base.vigorConfigInst.getExtraConfig().get("module"); // 如果不指定 module 则默认执行所有 Module 任务 if (modules === undefined) { // 从项目实例中获取全部 Module 并执行任务 yield Promise.all(project.getAllSubProjects().map((module) => { execute(module, false, opts); })).catch(error => { _log.error(error); }); } else { // 如果指定 module 则从项目级别 build-profile.json5 配置文件中查找匹配的 Module ,如果存在则执行任务 yield Promise.all(modules.split(",").map((value) => { const values = value.split("@"); const module = project.findModuleByName(values[0]); if (module === undefined) { _log.errorMessageExit(`Unknown module '${values[0]}' in command lines,Please check!`); } execute(module, true, opts); })).catch(error => { _log.error(error); }); } } // 执行任务流 function execute(model, shouldCheckTask, opts) { return __awaiter(this, void 0, void 0, function* () { // 从命令行获取需要执行的任务列表 const tasks = opts._; // 如果没配置则执行 default 任务 const toRun = tasks.length ? tasks : ['default']; ··· const moduleName = model.getName(); // [项目路径]/node_modules/@ohos/hvigor-base/index.js.Hvigor const HvigorClass = require(index.hvigorPath).Hvigor; // 项目打包工具的主入口 const vigorInst = new HvigorClass(); // 注册任务 const registryTasks = register_exports.registerExports(vigorInst, model.exported); if (needSync) { profile_js.profile.executed += 1; yield sync.sync(model, registryTasks, vigorInst); return; } // 筛选最终要执行的操作 const finallyToRun = toRun.filter((taskName) => { return registryTasks.has(taskName); }); // 指定具体要执行的模块,但是找不到对应的任务 if (!needSync && shouldCheckTask && finallyToRun.length === 0) { _log.errorMessageExit(`Task ${toRun} not found in module:${moduleName}.`); } // 未指定具体要执行的模块,没有可执行任务时直接返回 if (finallyToRun.length === 0) { _log.debug(`No task found to execute in ${moduleName}!`); return; } profile_js.profile.executed += finallyToRun.length; // series:串行 parallel:并行 const runMethod = opts.series ? 'series' : 'parallel'; // 执行任务流 vigorInst[runMethod](finallyToRun)(function (err) { if (err) { _log.errorExit(err.message); } }); }); }

注册任务

// hvigor/src/register-exports.js function registerExports(vigorInst, hvigorFileObj) { const registryTasks = new Map(); const taskNames = Object.keys(hvigorFileObj); if (taskNames.length) { taskNames.forEach((taskName) => { const exportObj = hvigorFileObj[taskName]; // plugin 不会被注册 if (exportObj instanceof Task) { const task = exportObj.registry(); vigorInst.task(taskName, task); registryTasks.set(taskName, exportObj instanceof DefaultSyncTask); } }); } return registryTasks; }

Hvigor 的插件和任务流

Build 任务脚本

// hvigor-ohos-plugin/index.js // Stage 模型的 hap 任务流 const hapTasks = (module) => { const plugin = plugin_factory_js.PluginFactory.getHapPlugin(module); return { plugin, assembleHap: plugin.assembleHap, clean: plugin.clean, compileNative: plugin.compileNative, sync: plugin.sync, buildPreviewerResource: plugin.buildPreviewerRes }; }; // FA 模型的 hap 任务流 const legacyHapTasks = (module) => { const plugin = plugin_factory_js.PluginFactory.getHapPlugin(module, true); return { plugin, assembleHap: plugin.assembleHap, clean: plugin.clean, compileNative: plugin.compileNative, buildPreviewerResource: plugin.buildPreviewerRes, sync: plugin.sync }; }; // Stage 模型的 app 任务流 const appTasks = (module) => { const plugin = plugin_factory_js.PluginFactory.getAppPlugin(module); return { plugin, assembleApp: plugin.assembleApp, clean: plugin.clean, sync: plugin.sync }; }; // FA 模型的 app 任务流 const legacyAppTasks = (module) => { const plugin = plugin_factory_js.PluginFactory.getAppPlugin(module, true); return { plugin, assembleApp: plugin.assembleApp, clean: plugin.clean, sync: plugin.sync }; }; // Stage 模型的 Har 插件 const harTasks = (module) => { const plugin = plugin_factory_js.PluginFactory.getHarPlugin(module); return { plugin, clean: plugin.clean, assembleHar: plugin.assembleHar, assembleSubHar: plugin.assembleSubHar, buildPreviewerResource: plugin.buildHarPreviewerRes }; }; // FA 模型的 Har 插件 const legacyHarTasks = (module) => { const plugin = plugin_factory_js.PluginFactory.getHarPlugin(module, true); return { plugin, clean: plugin.clean, assembleHar: plugin.assembleHar, assembleSubHar: plugin.assembleSubHar, buildPreviewerResource: plugin.buildHarPreviewerRes }; };

Plugin 工厂类

// hvigor-ohos-plugin/src/plugin/factory/plugin-factory.js class PluginFactory { static getAppPlugin(project, isFaMode = false) { // 创建 AppPlugin 实例 return new app_plugin_js.AppPlugin(project, isFaMode); } static getHapPlugin(module, isFaMode = false) { // 创建 HppPlugin 实例 return new hap_plugin_js.HapPlugin(module, isFaMode); } static getHarPlugin(module, isFaMode = false) { // 创建 HarPlugin 实例 return new har_plugin_js.HarPlugin(module, isFaMode); } }

HapPlugin & HarPlugin

// hvigor-ohos-plugin/src/plugin/hap-plugin.js class HapPlugin extends abstract_module_plugin_js.AbstractModulePlugin { constructor(module, isFaMode) { super(module, isFaMode); } initTasks() { // Hap 打包的任务流 this.assembleHap = new assemble_hap_js.AssembleHap(this._taskService, this.isFaMode); // Clean 任务 this.clean = new clean_js.Clean(this._taskService); // Native 代码编译任务 this.compileNative = new compile_native_js.CompileNative(this._taskService); // Module 级别的 Sync 任务 this.sync = new sync_module_js.SyncModule(this._module); // Hap 在 Priviewer 模式构建的任务流 this.buildPreviewerRes = new build_previewer_res_js.BuildPreviewerRes(this._taskService, this.isFaMode); } }
// hvigor-ohos-plugin/src/plugin/har-plugin.js class HarPlugin extends abstract_module_plugin_js.AbstractModulePlugin { constructor(module, isFaMode) { super(module, isFaMode); } initTasks() { // Har 打包的任务流 this.assembleHar = new assemble_har_js.AssembleHar(this._taskService, this.isFaMode); // 子 Har 打包的任务流 this.assembleSubHar = new assemble_sub_har_js.AssembleSubHar(this._taskService, this.isFaMode); // Clean 任务 this.clean = new clean_js.Clean(this._taskService); // Har 在 Priviewer 模式构建的任务流 this.buildHarPreviewerRes = new build_har_previewer_res_js.BuildHarPreviewerRes(this._taskService, this.isFaMode); } }

初始化 ModulePlugin 结构实例

// hvigor-ohos-plugin/src/plugin/common/abstract-module-plugin.js class AbstractModulePlugin { constructor(module, isFaMode) { this._log = ohos_logger_js.OhosLogger.getLogger(AbstractModulePlugin.name); this._module = module; const project = module.getProject(); this.isFaMode = isFaMode; // FA 模型 if (this.isFaMode) { // 创建 LegacyProjectModelImpl 实例,LegacyProjectModelImpl 为单例,同一个项目只会创建一次 this._projectModel = legacy_project_model_impl_js.LegacyProjectModelImpl.getInstance(project.getModuleDir(), project.getName()); // 创建 LegacyModuleModelImpl 实例 this._moduleModel = new legacy_module_model_impl_js.LegacyModuleModelImpl(module.getModuleDir(), module.getName(), this._projectModel); // 检查任务脚本和模块级别 build-profile.json5 中 apiType 是否匹配 this.checkModuleStatus(hap_extra_info_js.ApiType.FA); } // Stage 模型 else { // 创建 ProjectModelImpl 实例,ProjectModelImpl 为单例,同一个项目只会创建一次 this._projectModel = project_model_impl_js.ProjectModelImpl.getInstance(project.getModuleDir(), project.getName()); // 创建 ModuleModelImpl 实例 this._moduleModel = new module_model_impl_js.ModuleModelImpl(module.getModuleDir(), module.getName(), this._projectModel); // 检查任务脚本和模块级别 build-profile.json5 中 apiType 是否匹配 this.checkModuleStatus(hap_extra_info_js.ApiType.STAGE); } // 创建 Module 任务服务实例 this._taskService = new module_task_service_js.ModuleTaskService(this._projectModel, this._moduleModel); // 初始化任务 this.initTasks(); } checkModuleStatus(apiType) { // 获取模块级别 build-profile.json5 中 apiType 配置信息 const curApiType = this._moduleModel.getProfileOpt().apiType === undefined ? hap_extra_info_js.ApiType.STAGE : this._moduleModel.getProfileOpt().apiType; if (curApiType !== apiType) { this._log._buildError("The 'hvigorfile.js' import is not match the apiType!") ._solution(`Change the apiType to '${apiType}'.`) ._file(this._moduleModel.getProfilePath()) ._printErrorAndExit(this._moduleModel.getName()); } } ··· }

创建 FA 模型 LegacyModuleModelImpl 实例

FA 模型的模块持久化数据模型,包含模块源码数据,配置数据等

// hvigor-ohos-plugin/src/model/module/legacy-module-model-impl.js class LegacyModuleModelImpl extends core_module_model_impl_js.CoreModuleModelImpl { constructor(modulePath, moduleName, parentProject) { super(modulePath, moduleName, parentProject); this._legacyAbilitiesMap = new Map(); this.initDefaultTargetSourceSet(); } ··· initDefaultTargetSourceSet() { // 配置 [模块路径]/src/main 文件夹下配置文件 config.json 路径和 resources 路径实例 const defaultTargetSourceSet = new legacy_target_source_set_impl_js.LegacyTargetSourceSetImpl(path.default.resolve(this._sourceRootDir, "main")); // defaultTargetName = 'default' this.targetSourceSetMap.set(defaultTargetName, defaultTargetSourceSet); this.initAbilityInfo(defaultTargetName, defaultTargetSourceSet); // 创建 [模块路径]/src/ohosTest 文件夹下配置文件 config.json 路径和 resources 路径 const ohosTestTargetSourceSet = new legacy_target_source_set_impl_js.LegacyTargetSourceSetImpl(path.default.resolve(this._sourceRootDir, "ohosTest")); // ohosTestTargetName = 'ohosTest' this.targetSourceSetMap.set(ohosTestTargetName, ohosTestTargetSourceSet); // 初始化 Ability 信息 this.initAbilityInfo(ohosTestTargetName, ohosTestTargetSourceSet); } ··· // 初始化 Module 中的 Ability 信息 initAbilityInfo(targetName, targetSourceSet) { ··· // 读取 config.json 配置项 const configJsonObj = project_file_reader_js.ProjectFileReader.getJson5Obj(configJsonPath); // 获取配置中 module.abilities 配置项 const abilityObjs = configJsonObj.module.abilities ? configJsonObj.module.abilities : []; const legacyAbilities = []; for (let i = 0; i < abilityObjs.length; i++) { // 创建 FA模型 Ability 实例 legacyAbilities.push(new legacy_ability_model_impl_js.LegacyAbilityModelImpl(configJsonPath, abilityObjs[i].name)); } // 验证是否有重名的 Ability,如果重名则会报错并退出 LegacyModuleModelImpl.validateSameNameAbility(legacyAbilities); // 读取 module.js 配置信息 const jsObjs = configJsonObj.module.js ? configJsonObj.module.js : []; // 查找 js 配置项中 type 为 form 的配置项 for (let i = 0; i < jsObjs.length; i++) { if ('form' !== jsObjs[i].type) { continue; } // 创建 FA模型 Form 实例 legacyAbilities.push(new legacy_form_model_impl_js.LegacyFormModelImpl(configJsonPath, jsObjs[i])); } this._legacyAbilitiesMap.set(targetName, legacyAbilities); } }

创建 Stage 模型 ModuleModelImpl 实例

Stage 模型的模块数据管理类

// hvigor-ohos-plugin/src/model/module/module-model-impl.js class ModuleModelImpl extends core_module_model_impl_js.CoreModuleModelImpl { constructor(modulePath, moduleName, parentProject) { super(modulePath, moduleName, parentProject); this.initDefaultTargetSourceSet(); } initDefaultTargetSourceSet() { // 创建 [模块路径]/src/main 文件夹下配置文件 module.json5 路径和 resources 路径实例 const defaultTargetSourceSet = new target_source_set_impl_js.TargetSourceSetImpl(path.default.resolve(this._sourceRootDir, "main")); // defaultTargetName = 'default' this.targetSourceSetMap.set(defaultTargetName, defaultTargetSourceSet); // 创建 [模块路径]/src/ohosTest 文件夹下配置文件 module.json5 路径和 resources 路径实例 const ohosTestTargetSourceSet = new target_source_set_impl_js.TargetSourceSetImpl(path.default.resolve(this._sourceRootDir, "ohosTest")); // OHOS_TEST_TARGET = 'ohosTest' this.targetSourceSetMap.set(common_const_js.DefaultTargetConst.OHOS_TEST_TARGET, ohosTestTargetSourceSet); } ··· }

Module 模块的核心数据管理类

Hvigor 工程的 module 模块的核心数据管理类

// hvigor-ohos-plugin/src/model/module/core-module-model-impl.js class CoreModuleModelImpl { constructor(modulePath, moduleName, parentProject) { this._log = ohos_logger_js.OhosLogger.getLogger(CoreModuleModelImpl.name); this.targetSourceSetMap = new Map(); // 模块名称 this.name = moduleName; // 模块路径 this.modulePath = path.default.resolve(modulePath); // 项目 LegacyProjectModelImpl 实例 this.parentProject = parentProject; // Module 的 hvigorfile.js 文件路径 this.buildProfilePath = path.default.resolve(this.modulePath, this.getBuildProfileName()); // Module 的 build-profile.json5 文件路径 const moduleBuildProfilePath = path.default.resolve(this.modulePath, common_const_js.CommonConst.PROFILE_JSON5); // 使用 Schema 验证配置文件是否正确,错误则退出 this.moduleBuildProfileCheck(moduleBuildProfilePath); // Module 的 src 路径 this._sourceRootDir = path.default.resolve(modulePath, "src"); } }

创建 Module 任务服务实例

基于持久化 Module 的模型层提供的数据,经过处理后,提供给打包hap任务流需要使用的服务和数据

// hvigor-ohos-plugin/src/tasks/service/module-task-service.js class ModuleTaskService extends task_service.TaskService { constructor(projectModel, moduleModel) { super(projectModel); this._log = ohos_logger_js.OhosLogger.getLogger(ModuleTaskService.name); this.computeTargets = () => { // 默认不配置target时,执行所有的target const targets = ["all"]; // 从命令行 prop(p) 参数中获取 module 配置 const configModules = hvigor_base.vigorConfigInst.getExtraConfig().get("module"); if (configModules === undefined) { return targets; } // 例:entry@phone@free 则 targets为 ['phone','free'] const modules = configModules.split(","); for (let i = 0; i < modules.length; i++) { const module = modules[i]; const values = module.split("@"); if (this._moduleModel.getName() !== values[0]) { continue; } for (let j = 1; j < values.length; j++) { targets[j - 1] = values[j]; } } return targets; }; this.checkNeedPack = (targetProduct, targetName, curModuleConfigTargets) => { let needPack = false; // 如果在 prop(p) module 中配置则需要打包 if (curModuleConfigTargets.indexOf(targetName) > -1) { needPack = true; } if ((curModuleConfigTargets.indexOf("all") > -1)) { // 默认不配置target时,不打包ohosTest的包 needPack = targetName !== "ohosTest"; } const checkApplyProduct = (targetProduct) => { // 获取模块级别 build-profile.json5 配置文件中对应的 target 的 applyToProducts let products = this._projectModel?.getTargetApplyProducts(this._moduleModel.getName(), targetName); // 如果没有配置applyToProducts则默认是default if (products === undefined) { products = ["default"]; } // 如果存在则需要打包 return products.includes(targetProduct); }; return checkApplyProduct(targetProduct.name) && needPack; }; // 初始化 Hap 模块打包流的 target 数据集合 this.initTargetData = () => { // 根据命令行参数计算需要的 target 项 const curModuleConfigTargets = this.computeTargets(); // 获取命令行 prop(p) 中 buildRoot 参数 let buildRoot = hvigor_base.vigorConfigInst.getExtraConfig().get("buildRoot"); if (!buildRoot) { // 默认打包路径 build buildRoot = build_directory_const_js.BuildDirConst.BUILD_ROOT; } // 读取模块级别 build-profile.json5 的 targets let targets = this._moduleModel.getProfileOpt().targets; if (targets === undefined) { targets = [{ name: "default", }, { name: "ohosTest" }]; } else { const targetNames = targets.map(target => { return target.name; }); if (!targetNames.includes("default")) { targets.push({ name: "default", }); } if (!targetNames.includes("ohosTest")) { targets.push({ name: "ohosTest" }); } } // 获取命令行中配置的需要打包的 product 如果未配置则默认为 default const targetProduct = find_target_product.findTargetProduct(this._projectModel); let hasTargetNeedPack = false; targets.forEach((target) => { const targetName = target.name; // 验证可打包的 target const needPack = this.checkNeedPack(targetProduct, targetName, curModuleConfigTargets); if (needPack) { hasTargetNeedPack = true; } // 创建路径信息实例 const pathInfo = new module_path_info_iml.ModulePathInfoIml(this._moduleModel, targetName, targetProduct.name, buildRoot); this._targetDataMap.set(targetName, [new hap_task_target_data.ModuleTargetData(this._moduleModel, targetName, pathInfo, targetProduct), needPack]); }); if (!hasTargetNeedPack) { this._log._buildError(`Current product is ${targetProduct.name},There is no executable target!`) ._solution(`Please check the module targets ${util.format(targets)} applyToProducts field`) ._file(this._projectModel.getProfilePath()) ._printErrorAndExit(this._moduleModel.getName()); } }; this._moduleModel = moduleModel; this._targetDataMap = new Map(); // 获取额外信息, isSupportHos 和 isStageMode this._hapExtraInfo = project_extra_info_service_js.ProjectExtraInfoService.getProjectExtraInfoByPath(this._moduleModel); // 获取编译 SDK 版本信息 this._compileSdkVersion = this._projectModel.getProfileOpt().app.compileSdkVersion; // 实例化 SDK 工具信息实例 this._sdkInfo = new sdk_info.SdkInfo(this._compileSdkVersion, this.getModuleRequiredSDKs()); // 初始化 Target 数据 this.initTargetData(); } getModuleRequiredSDKs() { // 默认加载 Toolchains const sdkComponents = [sdkmanager_common.ComponentPath.TOOLCHAINS]; for (const key of this._moduleModel.getSourceSetByTargetName(common_const_js.DefaultTargetConst.DEFAULT_TARGET).getCodeMap().keys()) { // 按照目录存在就加载的逻辑,例如main目录下存在 ets目录则加载 ets SDK 实例 // CodeType["JS"] = "js"; // CodeType["ETS"] = "ets"; // CodeType["CPP"] = "cpp"; if (code_type_enum.CodeType.CPP === key) { if (this._moduleModel.getProfileOpt().buildOption.externalNativeOptions) { sdkComponents.push(code_type_enum.CodeType.getSDKComponentName(key)); } } else { sdkComponents.push(code_type_enum.CodeType.getSDKComponentName(key)); } } this._log.debug(`${this._moduleModel.getName()} require SDK: ${sdkComponents.join(" ").toLowerCase()}`); return sdkComponents; } ··· }

根据命令行中的配置找到项目级别 build-profile.json5 中的 Product

// hvigor-ohos-plugin/src/common/find-target-product.js function findTargetProduct(projectModuleModel) { // 从命令行 prop(p) 参数中获取 Product 配置 const configProduct = hvigor_base.vigorConfigInst.getExtraConfig().get("product"); // 没有配置时默认是default const targetProduct = configProduct ? configProduct : "default"; _log.debug(`Find product from build-profile.json: %s`, targetProduct); // 检查项目级别 build-profile.json5 配置 app.products 项中是否有该 product const mProduct = array_util_js.getElementFromArr(projectModuleModel.getProfileOpt().app.products, targetProduct); if (!mProduct) { _log._buildError(`Can not find product ${targetProduct}, please check!`) ._solution('Please check attribute products from build-profile.json5.') ._file(projectModuleModel.getProfilePath()) ._printErrorAndExit(projectModuleModel.getName()); } return mProduct; }

AppPlugin

// hvigor-ohos-plugin/src/plugin/app-plugin.js class AppPlugin { constructor(project, isFaMode) { this._module = project; this._moduleName = project.getName(); // 判断是否为 FA 模型,创建不同的 ProjectModel 实例,参数:项目路径,项目名称 this._projectModel = isFaMode ? legacy_project_model_impl_js.LegacyProjectModelImpl.getInstance(project.getModuleDir(), project.getName()) : project_model_impl_js.ProjectModelImpl.getInstance(project.getModuleDir(), project.getName()); // 创建项目任务服务实例 this._taskService = new project_task_service_js.ProjectTaskService(project, this._projectModel); // 创建具体任务实例 this.assembleApp = new assemble_app_js.AssembleApp(this._taskService, isFaMode); this.clean = new clean_js.Clean(this._taskService); this.sync = new sync_project_js.SyncProject(project); } getTaskService() { return this._taskService; } }

创建 FA 模型 LegacyProjectModelImpl 实例

FA 模型的工程持久化数据模型,包含工程源码数据,配置数据等

// hvigor-ohos-plugin/src/model/project/legacy-project-model-impl.js class LegacyProjectModelImpl extends core_project_model_impl_js.CoreProjectModelImpl { constructor(projectPath, name) { super(projectPath, name); } static getInstance(projectPath, name) { if (!LegacyProjectModelImpl.instance) { if (projectPath === null || projectPath === undefined) { throw new Error("工程模型还未初始化,请正确传递工程路径"); } if (name === null || name === undefined) { throw new Error("工程模型还未初始化,请正确传递工程名称"); } LegacyProjectModelImpl.instance = new LegacyProjectModelImpl(projectPath, name); } return LegacyProjectModelImpl.instance; } ··· }

创建 Stage 模型 ProjectModelImpl 实例

Stage 模型的工程持久化数据模型,包含工程源码数据,配置数据等

// hvigor-ohos-plugin/src/model/project/project-model-impl.js class ProjectModelImpl extends core_project_model_impl_js.CoreProjectModelImpl { constructor(projectPath, name) { super(projectPath, name); // 创建 App 级资源实例 this.appRes = new app_res_model_impl_js.AppResModelImpl(path.default.resolve(projectPath, "AppScope")); } static getInstance(projectPath, name) { if (!ProjectModelImpl.instance) { if (projectPath === null || projectPath === undefined) { throw new Error("工程模型还未初始化,请正确传递工程路径"); } if (name === null || name === undefined) { throw new Error("工程模型还未初始化,请正确传递工程名称"); } ProjectModelImpl.instance = new ProjectModelImpl(projectPath, name); } return ProjectModelImpl.instance; } ··· }

Project 模块的核心数据管理类

// hvigor-ohos-plugin/src/model/project/project-model-impl.js class CoreProjectModelImpl { constructor(projectPath, name) { this._log = ohos_logger_js.OhosLogger.getLogger(CoreProjectModelImpl.name); this.subModels = new Map(); // 项目路径 this.projectPath = projectPath; // 项目名称 this.name = name; // 设置 [项目路径]/build-profile.json5 文件路径 this._profilePath = path.default.resolve(this.projectPath, common_const_js.CommonConst.PROFILE_JSON5); // 验证文件是否存在,是否符合规范 this.projectBuildProfileCheck(this._profilePath); // 读取 [项目路径]/build-profile.json5 文件数据 this._profileOptions = project_file_reader_js.ProjectFileReader.getJson5Obj(this.getProfilePath()); // 检查配置文件中 SDK 版本信息,compileApiVersion 大于 8,compatibleApiVersion 小于等于 compileApiVersion this.projectStatusCheck(); // 加载 [项目路径]/build-profile.json5 文件中 Modules 配置信息,创建 ModuleModelImpl 实例并设置进 subModels 中 this.initSubProject(); } ··· }

创建 Project 任务服务实例

基于持久化 project 的模型层提供的数据,经过处理后,提供给打包app任务流需要使用的服务和数据

// hvigor-ohos-plugin/src/tasks/service/project-task-service.js class ProjectTaskService extends task_service.TaskService { constructor(project, projectModel) { super(projectModel); // 初始化 Project 中 Module 需要打包的目标项集合 this.initProductData = () => { // 遍历所有模块 this._project?.getSubProjects().forEach((value) => { const moduleName = value.getName(); const plugin = value.getPlugin(); if (plugin === undefined) { throw new Error(`Cannot find build file 'hvigorfile.js' in module ${moduleName}`); } const moduleTargetDataArr = []; if (plugin instanceof hap_plugin.HapPlugin) { // 获取项目级别 build-profile.json5 中 modules 里配置的 name 匹配的配置项 const appModuleOpt = this._projectModel?.getModuleProfileOpt(moduleName); // 获取 module 配置中 targets 配置项 const appModuleConfigTargets = appModuleOpt?.targets; // 获取 module 的 taskService 中 targetData 信息 const targetDataMap = plugin.getTaskService().getTargetDataMap(); // 遍历 Module 中需要打包的目标项 targetDataMap.forEach((targetData, targetName, targetMap) => { // 该target需要打包,并且在项目级别 build-profile.json5 的 targets 中配置 if (targetData[1] && array_util.getElementFromArr(appModuleConfigTargets, targetName) !== undefined) { moduleTargetDataArr.push(targetData[0]); } }); this._productDataMap.set(moduleName, moduleTargetDataArr); } }); }; this._project = project; // 获取命令行中配置的需要打包的 product 如果未配置则默认为 default this._targetProduct = find_target_product.findTargetProduct(projectModel); // 创建 SDK 工具实例,传入编译版本和 toolchains 工具名称 this._sdkInfo = new sdk_info_js.SdkInfo(projectModel.getCompileApiVersion(), [sdkmanager_common.ComponentPath.TOOLCHAINS]); // 创建路径信息实例 this._pathInfo = new project_path_info_iml.ProjectPathInfoIml(projectModel, this._targetProduct.name); this._productDataMap = new Map(); this.initProductData(); } ··· }

Hvigor 自定义任务

创建任务

继承 Task 类实现自定义任务。创建自定义任务 command.js 文件,输出工程名称。

// command.js const hvigor_base = require("@ohos/hvigor-base"); class Command extends hvigor_base.Task { _taskService = null _logger = hvigor_base.HvigorLogger.getLogger(Command.name); constructor(taskService) { super(); this._taskService = taskService; } registry = () => { return this.doTaskAction; } doTaskAction = (cb) => { this._logger.info(`CustomCommand`); this._logger.info(`ProjectName : ${this._taskService.getProjectModel().getName()}`); cb(); } } exports.Command = Command;

修改任务脚本

修改任务脚本把自定义任务加入到任务流中。

// hvigorfile const command_js = require('./command'); const hapTasks = require('@ohos/hvigor-ohos-plugin').hapTasks; module.exports = (module) => { const tasks = hapTasks(module); const taskService = tasks['plugin'].getTaskService(); tasks.command = new command_js.Command(taskService); return tasks; }

执行任务

执行命令 node node_modules\@ohos\hvigor\bin\hvigor.js command

效果如下:

 

 

Logo

社区规范:仅讨论OpenHarmony相关问题。

更多推荐