OpenHarmony应用构建工具Hvigor的构建流程

前言

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.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

效果如下:


为了能让大家更好的学习鸿蒙 (Harmony OS) 开发技术,这边特意整理了《鸿蒙 (Harmony OS)开发学习手册》(共计890页),希望对大家有所帮助:https://qr21.cn/FV7h05

《鸿蒙 (Harmony OS)开发学习手册》

入门必看:https://qr21.cn/FV7h05

  1. 应用开发导读(ArkTS)
  2. ......

HarmonyOS 概念:https://qr21.cn/FV7h05

  1. 系统定义
  2. 技术架构
  3. 技术特性
  4. 系统安全

如何快速入门?:https://qr21.cn/FV7h05

  1. 基本概念
  2. 构建第一个ArkTS应用
  3. 构建第一个JS应用
  4. ......

开发基础知识:https://qr21.cn/FV7h05

  1. 应用基础知识
  2. 配置文件
  3. 应用数据管理
  4. 应用安全管理
  5. 应用隐私保护
  6. 三方应用调用管控机制
  7. 资源分类与访问
  8. 学习ArkTS语言
  9. ......

基于ArkTS 开发:https://qr21.cn/FV7h05

1.Ability开发

2.UI开发

3.公共事件与通知

4.窗口管理

5.媒体

6.安全

7.网络与链接

8.电话服务

9.数据管理

10.后台任务(Background Task)管理

11.设备管理

12.设备使用信息统计

13.DFX

14.国际化开发

15.折叠屏系列

16.......

相关推荐
沈剑心2 小时前
如何在鸿蒙系统上实现「沉浸式」页面?
前端·harmonyos
Georgewu2 小时前
【HarmonyOS】鸿蒙应用加载读取csv文件
前端·harmonyos
Georgewu3 小时前
【HarmonyOS】 鸿蒙图片或视频保存相册
前端·harmonyos
准橙考典6 小时前
如何考驾照?
物联网·安全·华为·自动驾驶·汽车
川石教育8 小时前
鸿蒙开发-ArkTS 中使用 filter 组件
harmonyos·鸿蒙·鸿蒙应用开发·鸿蒙开发·鸿蒙开发培训·arkts语言
李洋-蛟龙腾飞公司8 小时前
HarmonyOS Next 应用元服务开发-分布式数据对象迁移数据权限与基础数据
分布式·华为·harmonyos
Damon小智8 小时前
HarmonyOS NEXT 技术实践-实现音乐服务卡片
华为·harmonyos·鸿蒙·harmonyos next·服务卡片
play_big_knife8 小时前
鸿蒙项目云捐助第十七讲云捐助我的页面上半部分的实现
华为·harmonyos·鸿蒙·云开发·鸿蒙开发·鸿蒙next·华为云开发
~央千澈~9 小时前
优雅草央千澈-关于蓝湖如何快速的标注交互原型是如何使用的-如何使用蓝湖设计交互原型和整个软件项目的流程逻辑-实践项目详细说明
ui·交互·蓝湖
军训猫猫头14 小时前
20.抽卡只有金,带保底(WPF) C#
ui·c#·wpf