前言
OpenHarmony 應(yīng)用和服務(wù)使用 Hvigor 作為工程的構(gòu)建工具线婚。本篇文章將介紹 Hvigor 的構(gòu)建流程瞻润,通過(guò)修改腳本配置使 Hvigor 執(zhí)行自定義任務(wù)。
Hvigor 的構(gòu)建流程
- 加載命令行參數(shù)和環(huán)境變量赔退;
- 初始化項(xiàng)目結(jié)構(gòu)橄维,創(chuàng)建 Project 和 Module 實(shí)例;
- 配置項(xiàng)目插件和任務(wù)流盒至;
- 執(zhí)行任務(wù)流酗洒;
// 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);
}
// 設(shè)置日志等級(jí)
evaluateLogLevel();
// 判斷當(dāng)前 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. 加載命令行參數(shù)和環(huán)境變量
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();
// 執(zhí)行具體的 Hvigor 任務(wù)
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}`);
});
});
});
}
執(zhí)行具體的 Hvigor 的任務(wù)
// hvigor/src/process-utils.js
function processUtils(opts, env) {
···
// 2. 初始化項(xiàng)目結(jié)構(gòu)浸船,創(chuàng)建 Project 和 Module 實(shí)例;
init.init(env);
// 3. 配置項(xiàng)目插件和任務(wù)流寝蹈;
const project = configuration.configuration(env);
// 4. 執(zhí)行任務(wù)流李命;
const executeMode = env.configProps.get(modeAlias);
switch (executeMode) {
···
}
}
1. 加載命令行參數(shù)和環(huán)境變量
讀取命令行參數(shù),并把參數(shù)和配置文件的路徑放入全局配置項(xiàng)中箫老。
// hvigor/src/init-env-config-props.js
function initEnvConfigProps(env, opts) {
···
// 獲取項(xiàng)目級(jí)別 build-profile.json5 文件路徑封字,[項(xiàng)目路徑]/build-profile.json5
const configFilePath = path.resolve(process.cwd(), hvigor_base.HvigorCommonConst.PROJECT_CONFIG_FILE);
···
const properties = new Map();
// 獲取命令行 prop(p) 參數(shù),放入 Map 中
if (opts.prop !== undefined) {
[].concat(opts.prop).forEach((value) => {
const arr = value.split('=');
properties.set(arr[0], arr[1]);
});
}
// 把"項(xiàng)目級(jí)別 build-profile.json5 文件路徑"耍鬓、"命令行 prop(p) 參數(shù)集合"和"命令行 mode(m) 模式參數(shù)"配置進(jìn)環(huán)境變量中
env.configProps = new Map([
[configFileName, configFilePath],
[propertiesAlias, properties],
[modeAlias, opts.mode]
]);
return configFilePath;
}
2. 初始化項(xiàng)目結(jié)構(gòu)阔籽,創(chuàng)建 Project 和 Module 實(shí)例
工程結(jié)構(gòu)實(shí)例化。
// hvigor/src/lifecycle/init.js
function init(env) {
// env.modulePath 是在命令行輔助工具 LiftOff 中加載的模塊函數(shù)牲蜀,"modulePath": "[項(xiàng)目路徑]/node_modules/@ohos/hvigor-base/index.js",
const vigorConfigInst = require(env.modulePath).vigorConfigInst;
// 把從命令行加載來(lái)的 prop(p) 參數(shù)放入 vigorConfigInst 實(shí)體的 ExtraConfig 中
vigorConfigInst.setExtraConfig(env.configProps.get(propertiesAlias));
// 初始化項(xiàng)目笆制,參數(shù):[項(xiàng)目路徑]/build-profile.json5,[項(xiàng)目路徑]
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();
}
···
// 初始化項(xiàng)目涣达,參數(shù):[項(xiàng)目路徑]/build-profile.json5在辆,[項(xiàng)目路徑]
initRootProject(projectConfigFile, projectRootDir) {
// 配置項(xiàng)目級(jí)別 build-profile.json5 文件路徑
this._projectConfigFile = projectConfigFile;
// 配置項(xiàng)目根路徑
this._projectDir = projectRootDir;
// 創(chuàng)建項(xiàng)目實(shí)例,參數(shù):[項(xiàng)目名稱]度苔,[項(xiàng)目路徑]
const projectImpl = new project_impl_js.ProjectImpl(path.basename(projectRootDir), projectRootDir);
// 讀取 [項(xiàng)目路徑]/build-profile.json5 配置數(shù)據(jù)
const projectStructureOpt = json5_reader_js.Json5Reader.getJson5Obj(this._projectConfigFile);
···
// 從配置文件中讀取項(xiàng)目下的全部 Modules
projectStructureOpt.modules?.forEach(module => {
// 校驗(yàn) Module 配置參數(shù)
validate_util_js.ValidateUtil.validateModule(module);
// 添加子項(xiàng)目匆篓,根據(jù)配置文件中 Modules 循環(huán)創(chuàng)建 Module 實(shí)例,參數(shù):項(xiàng)目實(shí)例寇窑,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 實(shí)現(xiàn)類
// hvigor-base/src/impl/project-impl.js
class ProjectImpl extends default_module_impl.DefaultModuleImpl {
constructor(moduleName, moduleDir) {
super(moduleName, moduleDir);
// 配置項(xiàng)目級(jí)別 build-profile.json5 文件路徑
this._projectStructureFile = path.resolve(moduleDir, common_const.HvigorCommonConst.PROJECT_CONFIG_FILE);
this._subProjects = new Map();
}
···
}
Module 實(shí)現(xiàn)類
// hvigor-base/src/impl/module-impl.js
class ModuleImpl extends default_module_impl.DefaultModuleImpl {
constructor(project, moduleName, moduleDir) {
super(moduleName, moduleDir);
this._project = project;
}
···
}
默認(rèn) Module 實(shí)現(xiàn)類
// hvigor-base/src/impl/default-module-impl.js
class DefaultModuleImpl {
constructor(moduleName, modulePath) {
this._moduleName = moduleName;
this._modulePath = modulePath;
// 獲取項(xiàng)目和模塊的 package.json 文件路徑
this._packageJsonPath = path.resolve(modulePath, "package.json");
// 獲取項(xiàng)目和模塊的 hvigorfile.js 文件路徑
this._buildFilePath = path.resolve(modulePath, common_const.HvigorCommonConst.BUILD_FILE_NAME);
}
···
}
3. 配置項(xiàng)目插件和任務(wù)流
加載 hvigorfile.js 文件中配置的任務(wù)腳本。
// hvigor/src/lifecycle/configuration.js
function configuration(env) {
// 整個(gè)項(xiàng)目的結(jié)構(gòu)在 init 中初始化完成甩骏,讀取 vigorConfigInst
const vigorConfigInst = require(env.modulePath).vigorConfigInst;
// 通過(guò) vigorConfigInst 獲取項(xiàng)目實(shí)例
const project = vigorConfigInst.getProject();
// 獲取項(xiàng)目全部的 Module 實(shí)例窗市,遍歷加載任務(wù)腳本
for (const subModule of project.getAllSubProjects()) {
// 獲取 Module 的 hvigorfile.js 文件路徑
const subModuleVigorFileJs = subModule.getBuildFilePath();
// 加載任務(wù)腳本
configModule(subModule, subModuleVigorFileJs);
}
// 獲取 Project 的 hvigorfile.js 文件路徑
const projectVigorFileJs = path.resolve(env.cwd, hvigor_base.HvigorCommonConst.BUILD_FILE_NAME);
// 加載任務(wù)腳本
configModule(project, projectVigorFileJs);
return project;
}
function configModule(module, hvigorFilePath) {
// FA 模型
// 工程級(jí)別
// module.exports = require('@ohos/hvigor-ohos-plugin').legacyAppTasks
// 模塊級(jí)別
// module.exports = require('@ohos/hvigor-ohos-plugin').legacyHapTasks
// module.exports = require('@ohos/hvigor-ohos-plugin').legacyHarTasks
// Stage 模型
// 工程級(jí)別
// module.exports = require('@ohos/hvigor-ohos-plugin').appTasks
// 模塊級(jí)別
// module.exports = require('@ohos/hvigor-ohos-plugin').hapTasks
// module.exports = require('@ohos/hvigor-ohos-plugin').harTasks
// 加載 hvigorFile 任務(wù)腳本
const moduleExported = require(hvigorFilePath);
// 配置 Project 和 Module 的任務(wù)流
const pluginNTasks = moduleExported(module);
module.exported = pluginNTasks;
// 綁定創(chuàng)建的 Plugin 對(duì)象到 Project 實(shí)例和 Module 實(shí)例上
module.bindPlugin(pluginNTasks.plugin);
}
4. 執(zhí)行任務(wù)流
根據(jù)命令行 mode 參數(shù)判斷要執(zhí)行任務(wù)流的級(jí)別。
// hvigor/src/process-utils.js
// 從環(huán)境變量中獲取 mode(m) 參數(shù)
const executeMode = env.configProps.get(modeAlias);
// 根據(jù) mode 判斷要執(zhí)行任務(wù)流的級(jí)別
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!`);
}
執(zhí)行任務(wù)
//hvigor/src/lifecycle/execute-task.js
function executeModuleTask(project, opts) {
// 獲取 prop(p) 參數(shù)中 module 的取值
const modules = hvigor_base.vigorConfigInst.getExtraConfig().get("module");
// 如果不指定 module 則默認(rèn)執(zhí)行所有 Module 任務(wù)
if (modules === undefined) {
// 從項(xiàng)目實(shí)例中獲取全部 Module 并執(zhí)行任務(wù)
yield Promise.all(project.getAllSubProjects().map((module) => {
execute(module, false, opts);
})).catch(error => {
_log.error(error);
});
}
else {
// 如果指定 module 則從項(xiàng)目級(jí)別 build-profile.json5 配置文件中查找匹配的 Module 饮笛,如果存在則執(zhí)行任務(wù)
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);
});
}
}
// 執(zhí)行任務(wù)流
function execute(model, shouldCheckTask, opts) {
return __awaiter(this, void 0, void 0, function* () {
// 從命令行獲取需要執(zhí)行的任務(wù)列表
const tasks = opts._;
// 如果沒(méi)配置則執(zhí)行 default 任務(wù)
const toRun = tasks.length ? tasks : ['default'];
···
const moduleName = model.getName();
// [項(xiàng)目路徑]/node_modules/@ohos/hvigor-base/index.js.Hvigor
const HvigorClass = require(index.hvigorPath).Hvigor;
// 項(xiàng)目打包工具的主入口
const vigorInst = new HvigorClass();
// 注冊(cè)任務(wù)
const registryTasks = register_exports.registerExports(vigorInst, model.exported);
if (needSync) {
profile_js.profile.executed += 1;
yield sync.sync(model, registryTasks, vigorInst);
return;
}
// 篩選最終要執(zhí)行的操作
const finallyToRun = toRun.filter((taskName) => {
return registryTasks.has(taskName);
});
// 指定具體要執(zhí)行的模塊,但是找不到對(duì)應(yīng)的任務(wù)
if (!needSync && shouldCheckTask && finallyToRun.length === 0) {
_log.errorMessageExit(`Task ${toRun} not found in module:${moduleName}.`);
}
// 未指定具體要執(zhí)行的模塊,沒(méi)有可執(zhí)行任務(wù)時(shí)直接返回
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';
// 執(zhí)行任務(wù)流
vigorInst[runMethod](finallyToRun)(function (err) {
if (err) {
_log.errorExit(err.message);
}
});
});
}
注冊(cè)任務(wù)
// 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 不會(huì)被注冊(cè)
if (exportObj instanceof Task) {
const task = exportObj.registry();
vigorInst.task(taskName, task);
registryTasks.set(taskName, exportObj instanceof DefaultSyncTask);
}
});
}
return registryTasks;
}
Hvigor 的插件和任務(wù)流
Build 任務(wù)腳本
// hvigor-ohos-plugin/index.js
// Stage 模型的 hap 任務(wù)流
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 任務(wù)流
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 任務(wù)流
const appTasks = (module) => {
const plugin = plugin_factory_js.PluginFactory.getAppPlugin(module);
return {
plugin,
assembleApp: plugin.assembleApp,
clean: plugin.clean,
sync: plugin.sync
};
};
// FA 模型的 app 任務(wù)流
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) {
// 創(chuàng)建 AppPlugin 實(shí)例
return new app_plugin_js.AppPlugin(project, isFaMode);
}
static getHapPlugin(module, isFaMode = false) {
// 創(chuàng)建 HppPlugin 實(shí)例
return new hap_plugin_js.HapPlugin(module, isFaMode);
}
static getHarPlugin(module, isFaMode = false) {
// 創(chuàng)建 HarPlugin 實(shí)例
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 打包的任務(wù)流
this.assembleHap = new assemble_hap_js.AssembleHap(this._taskService, this.isFaMode);
// Clean 任務(wù)
this.clean = new clean_js.Clean(this._taskService);
// Native 代碼編譯任務(wù)
this.compileNative = new compile_native_js.CompileNative(this._taskService);
// Module 級(jí)別的 Sync 任務(wù)
this.sync = new sync_module_js.SyncModule(this._module);
// Hap 在 Priviewer 模式構(gòu)建的任務(wù)流
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 打包的任務(wù)流
this.assembleHar = new assemble_har_js.AssembleHar(this._taskService, this.isFaMode);
// 子 Har 打包的任務(wù)流
this.assembleSubHar = new assemble_sub_har_js.AssembleSubHar(this._taskService, this.isFaMode);
// Clean 任務(wù)
this.clean = new clean_js.Clean(this._taskService);
// Har 在 Priviewer 模式構(gòu)建的任務(wù)流
this.buildHarPreviewerRes = new build_har_previewer_res_js.BuildHarPreviewerRes(this._taskService, this.isFaMode);
}
}
初始化 ModulePlugin 結(jié)構(gòu)實(shí)例
// 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) {
// 創(chuàng)建 LegacyProjectModelImpl 實(shí)例,LegacyProjectModelImpl 為單例咨察,同一個(gè)項(xiàng)目只會(huì)創(chuàng)建一次
this._projectModel = legacy_project_model_impl_js.LegacyProjectModelImpl.getInstance(project.getModuleDir(), project.getName());
// 創(chuàng)建 LegacyModuleModelImpl 實(shí)例
this._moduleModel = new legacy_module_model_impl_js.LegacyModuleModelImpl(module.getModuleDir(), module.getName(), this._projectModel);
// 檢查任務(wù)腳本和模塊級(jí)別 build-profile.json5 中 apiType 是否匹配
this.checkModuleStatus(hap_extra_info_js.ApiType.FA);
}
// Stage 模型
else {
// 創(chuàng)建 ProjectModelImpl 實(shí)例,ProjectModelImpl 為單例,同一個(gè)項(xiàng)目只會(huì)創(chuàng)建一次
this._projectModel = project_model_impl_js.ProjectModelImpl.getInstance(project.getModuleDir(), project.getName());
// 創(chuàng)建 ModuleModelImpl 實(shí)例
this._moduleModel = new module_model_impl_js.ModuleModelImpl(module.getModuleDir(), module.getName(), this._projectModel);
// 檢查任務(wù)腳本和模塊級(jí)別 build-profile.json5 中 apiType 是否匹配
this.checkModuleStatus(hap_extra_info_js.ApiType.STAGE);
}
// 創(chuàng)建 Module 任務(wù)服務(wù)實(shí)例
this._taskService = new module_task_service_js.ModuleTaskService(this._projectModel, this._moduleModel);
// 初始化任務(wù)
this.initTasks();
}
checkModuleStatus(apiType) {
// 獲取模塊級(jí)別 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());
}
}
···
}
創(chuàng)建 FA 模型 LegacyModuleModelImpl 實(shí)例
FA 模型的模塊持久化數(shù)據(jù)模型,包含模塊源碼數(shù)據(jù),配置數(shù)據(jù)等
// 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 路徑實(shí)例
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);
// 創(chuàng)建 [模塊路徑]/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 配置項(xiàng)
const configJsonObj = project_file_reader_js.ProjectFileReader.getJson5Obj(configJsonPath);
// 獲取配置中 module.abilities 配置項(xiàng)
const abilityObjs = configJsonObj.module.abilities ?
configJsonObj.module.abilities : [];
const legacyAbilities = [];
for (let i = 0; i < abilityObjs.length; i++) {
// 創(chuàng)建 FA模型 Ability 實(shí)例
legacyAbilities.push(new legacy_ability_model_impl_js.LegacyAbilityModelImpl(configJsonPath, abilityObjs[i].name));
}
// 驗(yàn)證是否有重名的 Ability缎浇,如果重名則會(huì)報(bào)錯(cuò)并退出
LegacyModuleModelImpl.validateSameNameAbility(legacyAbilities);
// 讀取 module.js 配置信息
const jsObjs = configJsonObj.module.js ? configJsonObj.module.js : [];
// 查找 js 配置項(xiàng)中 type 為 form 的配置項(xiàng)
for (let i = 0; i < jsObjs.length; i++) {
if ('form' !== jsObjs[i].type) {
continue;
}
// 創(chuàng)建 FA模型 Form 實(shí)例
legacyAbilities.push(new legacy_form_model_impl_js.LegacyFormModelImpl(configJsonPath, jsObjs[i]));
}
this._legacyAbilitiesMap.set(targetName, legacyAbilities);
}
}
創(chuàng)建 Stage 模型 ModuleModelImpl 實(shí)例
Stage 模型的模塊數(shù)據(jù)管理類
// 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() {
// 創(chuàng)建 [模塊路徑]/src/main 文件夾下配置文件 module.json5 路徑和 resources 路徑實(shí)例
const defaultTargetSourceSet = new target_source_set_impl_js.TargetSourceSetImpl(path.default.resolve(this._sourceRootDir, "main"));
// defaultTargetName = 'default'
this.targetSourceSetMap.set(defaultTargetName, defaultTargetSourceSet);
// 創(chuàng)建 [模塊路徑]/src/ohosTest 文件夾下配置文件 module.json5 路徑和 resources 路徑實(shí)例
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 模塊的核心數(shù)據(jù)管理類
Hvigor 工程的 module 模塊的核心數(shù)據(jù)管理類
// 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);
// 項(xiàng)目 LegacyProjectModelImpl 實(shí)例
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 驗(yàn)證配置文件是否正確扎拣,錯(cuò)誤則退出
this.moduleBuildProfileCheck(moduleBuildProfilePath);
// Module 的 src 路徑
this._sourceRootDir = path.default.resolve(modulePath, "src");
}
}
創(chuàng)建 Module 任務(wù)服務(wù)實(shí)例
基于持久化 Module 的模型層提供的數(shù)據(jù),經(jīng)過(guò)處理后,提供給打包hap任務(wù)流需要使用的服務(wù)和數(shù)據(jù)
// 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 = () => {
// 默認(rèn)不配置target時(shí),執(zhí)行所有的target
const targets = ["all"];
// 從命令行 prop(p) 參數(shù)中獲取 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)) {
// 默認(rèn)不配置target時(shí),不打包ohosTest的包
needPack = targetName !== "ohosTest";
}
const checkApplyProduct = (targetProduct) => {
// 獲取模塊級(jí)別 build-profile.json5 配置文件中對(duì)應(yīng)的 target 的 applyToProducts
let products = this._projectModel?.getTargetApplyProducts(this._moduleModel.getName(), targetName);
// 如果沒(méi)有配置applyToProducts則默認(rèn)是default
if (products === undefined) {
products = ["default"];
}
// 如果存在則需要打包
return products.includes(targetProduct);
};
return checkApplyProduct(targetProduct.name) && needPack;
};
// 初始化 Hap 模塊打包流的 target 數(shù)據(jù)集合
this.initTargetData = () => {
// 根據(jù)命令行參數(shù)計(jì)算需要的 target 項(xiàng)
const curModuleConfigTargets = this.computeTargets();
// 獲取命令行 prop(p) 中 buildRoot 參數(shù)
let buildRoot = hvigor_base.vigorConfigInst.getExtraConfig().get("buildRoot");
if (!buildRoot) {
// 默認(rèn)打包路徑 build
buildRoot = build_directory_const_js.BuildDirConst.BUILD_ROOT;
}
// 讀取模塊級(jí)別 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 如果未配置則默認(rèn)為 default
const targetProduct = find_target_product.findTargetProduct(this._projectModel);
let hasTargetNeedPack = false;
targets.forEach((target) => {
const targetName = target.name;
// 驗(yàn)證可打包的 target
const needPack = this.checkNeedPack(targetProduct, targetName, curModuleConfigTargets);
if (needPack) {
hasTargetNeedPack = true;
}
// 創(chuàng)建路徑信息實(shí)例
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;
// 實(shí)例化 SDK 工具信息實(shí)例
this._sdkInfo = new sdk_info.SdkInfo(this._compileSdkVersion, this.getModuleRequiredSDKs());
// 初始化 Target 數(shù)據(jù)
this.initTargetData();
}
getModuleRequiredSDKs() {
// 默認(rèn)加載 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 實(shí)例
// 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;
}
···
}
根據(jù)命令行中的配置找到項(xiàng)目級(jí)別 build-profile.json5 中的 Product
// hvigor-ohos-plugin/src/common/find-target-product.js
function findTargetProduct(projectModuleModel) {
// 從命令行 prop(p) 參數(shù)中獲取 Product 配置
const configProduct = hvigor_base.vigorConfigInst.getExtraConfig().get("product");
// 沒(méi)有配置時(shí)默認(rèn)是default
const targetProduct = configProduct ? configProduct : "default";
_log.debug(`Find product from build-profile.json: %s`, targetProduct);
// 檢查項(xiàng)目級(jí)別 build-profile.json5 配置 app.products 項(xiàng)中是否有該 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 模型,創(chuàng)建不同的 ProjectModel 實(shí)例,參數(shù):項(xiàng)目路徑二蓝,項(xiàng)目名稱
this._projectModel = isFaMode ?
legacy_project_model_impl_js.LegacyProjectModelImpl.getInstance(project.getModuleDir(), project.getName()) :
project_model_impl_js.ProjectModelImpl.getInstance(project.getModuleDir(), project.getName());
// 創(chuàng)建項(xiàng)目任務(wù)服務(wù)實(shí)例
this._taskService = new project_task_service_js.ProjectTaskService(project, this._projectModel);
// 創(chuàng)建具體任務(wù)實(shí)例
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;
}
}
創(chuàng)建 FA 模型 LegacyProjectModelImpl 實(shí)例
FA 模型的工程持久化數(shù)據(jù)模型,包含工程源碼數(shù)據(jù)指厌,配置數(shù)據(jù)等
// 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("工程模型還未初始化刊愚,請(qǐng)正確傳遞工程路徑");
}
if (name === null || name === undefined) {
throw new Error("工程模型還未初始化,請(qǐng)正確傳遞工程名稱");
}
LegacyProjectModelImpl.instance = new LegacyProjectModelImpl(projectPath, name);
}
return LegacyProjectModelImpl.instance;
}
···
}
創(chuàng)建 Stage 模型 ProjectModelImpl 實(shí)例
Stage 模型的工程持久化數(shù)據(jù)模型踩验,包含工程源碼數(shù)據(jù)鸥诽,配置數(shù)據(jù)等
// 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);
// 創(chuàng)建 App 級(jí)資源實(shí)例
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("工程模型還未初始化商玫,請(qǐng)正確傳遞工程路徑");
}
if (name === null || name === undefined) {
throw new Error("工程模型還未初始化,請(qǐng)正確傳遞工程名稱");
}
ProjectModelImpl.instance = new ProjectModelImpl(projectPath, name);
}
return ProjectModelImpl.instance;
}
···
}
Project 模塊的核心數(shù)據(jù)管理類
// 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();
// 項(xiàng)目路徑
this.projectPath = projectPath;
// 項(xiàng)目名稱
this.name = name;
// 設(shè)置 [項(xiàng)目路徑]/build-profile.json5 文件路徑
this._profilePath = path.default.resolve(this.projectPath, common_const_js.CommonConst.PROFILE_JSON5);
// 驗(yàn)證文件是否存在牡借,是否符合規(guī)范
this.projectBuildProfileCheck(this._profilePath);
// 讀取 [項(xiàng)目路徑]/build-profile.json5 文件數(shù)據(jù)
this._profileOptions = project_file_reader_js.ProjectFileReader.getJson5Obj(this.getProfilePath());
// 檢查配置文件中 SDK 版本信息拳昌,compileApiVersion 大于 8,compatibleApiVersion 小于等于 compileApiVersion
this.projectStatusCheck();
// 加載 [項(xiàng)目路徑]/build-profile.json5 文件中 Modules 配置信息钠龙,創(chuàng)建 ModuleModelImpl 實(shí)例并設(shè)置進(jìn) subModels 中
this.initSubProject();
}
···
}
創(chuàng)建 Project 任務(wù)服務(wù)實(shí)例
基于持久化 project 的模型層提供的數(shù)據(jù)炬藤,經(jīng)過(guò)處理后,提供給打包app任務(wù)流需要使用的服務(wù)和數(shù)據(jù)
// hvigor-ohos-plugin/src/tasks/service/project-task-service.js
class ProjectTaskService extends task_service.TaskService {
constructor(project, projectModel) {
super(projectModel);
// 初始化 Project 中 Module 需要打包的目標(biāo)項(xiàng)集合
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) {
// 獲取項(xiàng)目級(jí)別 build-profile.json5 中 modules 里配置的 name 匹配的配置項(xiàng)
const appModuleOpt = this._projectModel?.getModuleProfileOpt(moduleName);
// 獲取 module 配置中 targets 配置項(xiàng)
const appModuleConfigTargets = appModuleOpt?.targets;
// 獲取 module 的 taskService 中 targetData 信息
const targetDataMap = plugin.getTaskService().getTargetDataMap();
// 遍歷 Module 中需要打包的目標(biāo)項(xiàng)
targetDataMap.forEach((targetData, targetName, targetMap) => {
// 該target需要打包,并且在項(xiàng)目級(jí)別 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 如果未配置則默認(rèn)為 default
this._targetProduct = find_target_product.findTargetProduct(projectModel);
// 創(chuàng)建 SDK 工具實(shí)例,傳入編譯版本和 toolchains 工具名稱
this._sdkInfo = new sdk_info_js.SdkInfo(projectModel.getCompileApiVersion(), [sdkmanager_common.ComponentPath.TOOLCHAINS]);
// 創(chuàng)建路徑信息實(shí)例
this._pathInfo = new project_path_info_iml.ProjectPathInfoIml(projectModel, this._targetProduct.name);
this._productDataMap = new Map();
this.initProductData();
}
···
}
Hvigor 自定義任務(wù)
創(chuàng)建任務(wù)
繼承 Task 類實(shí)現(xiàn)自定義任務(wù)碴里。創(chuàng)建自定義任務(wù) 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;
修改任務(wù)腳本
修改任務(wù)腳本把自定義任務(wù)加入到任務(wù)流中。
// 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;
}
執(zhí)行任務(wù)
執(zhí)行命令 node node_modules\@ohos\hvigor\bin\hvigor.js command
效果如下: