前言
OpenHarmony 应用和服务使用 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.e