Hvigor 应用编译

518 阅读8分钟

研究目标

  1. 定位编译核心节点代码,利于自定义脚本辅助工程应用开发
  2. 加深对当前应用工程的目录结构使用的理解

预备知识

  1. 了解HarmonyOS应用开发
  2. 了解HarmonyOS应用的工程目录结构
  3. 了解HSP,HAR,HAP概念
  4. 见过hvigor和hvigor-ohos-plugin

试验环境

  1. MacBook Pro: Apple M1
  2. DevEco Studio 3.1.1 Release
  3. HarmonyOS SDK API 9
  4. hvigor版本: 2.4.2
  5. hvigor-ohos-plugin版本: 2.4.2

编译方式

HarmonyOS应用编译有两种方式:1. 通过DevEco Studio菜单 2. 通过脚本命令。

这两种方式最终都是通过启动 "@ohos/hvigor" 包中的index.js来完成操作的。

当前hvigor暂不支持全局命令行工具,依赖工程中的hvigorw工具。

命令运行时的参数:

  1. 官方在【‘更新时间: 2024-03-30 10:25’ “hvigor命令行”】 这篇文章中针对命令中的参数有所描述【文档对参数描述极少,不推荐】
  2. 通过运行 hvigorw 脚本,添加‘--help’也可以看到相关参数的使用方法【推荐使用此种方式查看】

命令编译格式

./hvigorw [taskNames...] <options>

Options:
  -v, --version                     查看hvigor版本号
  -e, --error                       输出错误日志
  -w, --warn                        输出警告日志
  -i, --info                        输出信息日志
  -d, --debug                       输出调试日志
  --stacktrace                      输出所有堆栈信息
  -p, --prop <value>                定义额外属性
  -m, --mode <string>               指定编译范围: project, module
  -s, --sync                        同步插件信息
  --enable-build-script-type-check  启用生成脚本hvigorfile.ts类型检查
  --no-parallel                     禁止并行编译
  --no-incremental                  禁止增量编译
  --no-daemon                       禁用守护进程
  --stop-daemon                     关闭当前工程的守护进程
  --stop-daemon-all                 关闭所有工程的守护进程
  --status-daemon                   展示工程中所有守护进程的状态
  -h, --help                        查看命令的使用方法
Commands:
  version                           显示Hvigor版本号
  tasks                             显示所有模块任务
  taskTree                          以树状形式,显示所有模块任务

命令编译演示

首先进入到应用工程根目录下(通过DevEco 中的Terminal可以直接进入到根目录)

查看 hvigor 版本号

以下命令任意一个均可以

  1. sh hvigorw -v
  2. sh hvigorw --version
  3. sh hvigorw version

WX20240411-112646@2x.png

查看工程任务

sh hvigorw tasks

WX20240411-113251@2x.png

命令结果展示了HarmonyOS应用编译时,可以提供的服务类型.

任务分类

  1. Help tasks
  2. Other tasks
  3. hook tasks
  4. Sync tasks

hvigor将应用工程按照“应用”,“模块”划分方式来执行所有类型的任务分类。这一点从 "sh hvigorw tasks"和正常编译时的日志可以推测出来。

“sh hvigorw tasks”命令结果的结构如下

当前演示的应用工程共有3个模块:

  1. entry
  2. harlibrary
  3. hsplibrary
--------------------------------------------------------------------------
All tasks from hvigor node 'entry' <= 模块'entry'
--------------------------------------------------------------------------
Help tasks <= tasks和taskTree为任务名称,即“命令编译格式”中的taskName
-------------
tasks - Displays the tasks of the HvigorNode.
taskTree - Displays the task dag tree of the HvigorNode.

Other tasks <= clean,default@PreBuild, ...为任务名称,即“命令编译格式”中的taskName
-------------
clean
PreviewBuild
buildHotReloadResource
HotReloadBuild
default@PreBuild
default@MergeProfile - Merge app config manifest files.
default@BuildNativeWithCmake - Compile cpp source with Cmake.
default@BuildNativeWithNinja - Compile cpp source with Ninja.
default@MakePackInfo - Generate module pack.info.
default@ProcessLibs
default@GenerateMetadata
default@ProcessProfile - Process app config manifest file
default@ProcessResource
default@GenerateLoaderJson - Generate loader.json.
default@CompileResource - Compile project resource.
default@CompileArkTS - Compile ArkTS components for rich device in Stage Mode.
default@CompileJS - Compile ArkTS or JS components using Node.js for rich device in Stage Mode.
default@PackageHap - Build the Hap package.
default@SignHap
default@PreviewHookCompileResource
default@ReplacePreviewerPage
default@PreviewUpdateAssets - Update main_pages.json and module.json before PreviewBuild.
default@PreviewArkTS
default@HotReloadArkTS

hook tasks
-------------
compileNative
assembleHap
buildPreviewerResource

Sync tasks
-------------
init

> hvigor Finished :entry:tasks... after 1 ms 
--------------------------------------------------------------------------
All tasks from hvigor node 'harlibrary' <= 模块'harlibrary'
--------------------------------------------------------------------------
Help tasks
-------------
tasks - Displays the tasks of the HvigorNode.
taskTree - Displays the task dag tree of the HvigorNode.

Other tasks
-------------
clean
PreviewBuild
default@PreBuild
default@BuildNativeWithCmake - Compile cpp source with Cmake.
default@BuildNativeWithNinja - Compile cpp source with Ninja.
default@MergeProfile - Merge app config manifest files.
default@ProcessProfile - Process app config manifest file
default@ProcessResource
default@ProcessLibs
default@CompileResource - Compile project resource.
default@PackageHar - Build the har package.
default@GenerateLoaderJson - Generate loader.json.
default@PreviewHookCompileResource
default@PreviewUpdateAssets - Update main_pages.json and module.json before PreviewBuild.
default@PreviewArkTS

hook tasks
-------------
compileNative
assembleHar
buildPreviewerResource

Sync tasks
-------------
init

> hvigor Finished :harlibrary:tasks... after 1 ms 
--------------------------------------------------------------------------
All tasks from hvigor node 'hsplibrary' <= 模块'hsplibrary'
--------------------------------------------------------------------------
Help tasks
-------------
tasks - Displays the tasks of the HvigorNode.
taskTree - Displays the task dag tree of the HvigorNode.

Other tasks
-------------
clean
buildHotReloadResource
PreviewBuild
HotReloadBuild
default@PreBuild
default@MergeProfile - Merge app config manifest files.
default@BuildNativeWithCmake - Compile cpp source with Cmake.
default@BuildNativeWithNinja - Compile cpp source with Ninja.
default@MakePackInfo - Generate module pack.info.
default@ProcessLibs
default@GenerateMetadata
default@ProcessProfile - Process app config manifest file
default@ProcessResource
default@GenerateLoaderJson - Generate loader.json.
default@CompileResource - Compile project resource.
default@CompileArkTS - Compile ArkTS components for rich device in Stage Mode.
default@CompileJS - Compile ArkTS or JS components using Node.js for rich device in Stage Mode.
default@PackageHsp - Build the Hsp package.
default@PrepareSharedHarResource
default@SignHsp
default@PackageSharedHar
default@PreviewHookCompileResource
default@ReplacePreviewerPage
default@PreviewUpdateAssets - Update main_pages.json and module.json before PreviewBuild.
default@PreviewArkTS
default@HotReloadArkTS

hook tasks
-------------
compileNative
assembleHsp
buildPreviewerResource

Sync tasks
-------------
init

> hvigor Finished :hsplibrary:tasks... after 1 ms 
--------------------------------------------------------------------------
All tasks from hvigor node 'HarmonyLearn' <= 应用'hsplibrary'
--------------------------------------------------------------------------
Help tasks
-------------
tasks - Displays the tasks of the HvigorNode.
taskTree - Displays the task dag tree of the HvigorNode.

Other tasks
-------------
PreBuildApp
MakeProjectPackInfo - Generate project pack.info.
GeneratePackRes - Build the 'pack.res' file
PackageApp - Build the app package.
SignApp
clean

hook tasks
-------------
assembleApp

Sync tasks
-------------
init

> hvigor Finished ::tasks... after 1 ms 
> hvigor BUILD SUCCESSFUL in 150 ms 

正常的编译日志

PreBuildApp是任务名称,红色部分是模块名称,蓝色部分是任务名称 WX20240411-114149@2x.png

编译整个应用

sh hvigorw assembleApp -m project

命令执行完成后,最终会在根目录下生成一个build文件夹

WX20240411-122221@2x.png

编译hap模块

hap模块,即module.json5 文件中的type, 要么是entry, 要么是feature.

按照官方定义的特征就是:包含Ability的模块

sh hvigorw assembleHap -m module

命令执行完成后,仅仅在每个模块下生成一个build文件夹

WX20240411-155955@2x.png

编译Release版本

这里用到了 -p 参数,指的是自定义属性

debuggable是属性,true是属性值。

sh hvigorw assembleHap -m module -p debuggable=false

如下两张图,左图是Debug版本,右图是Release版本,即debuggable=false

WX20240411-161000@2x.png WX20240411-161300@2x.png

编译输出Hvigor日志

sh hvigorw assembleHap -i -d -w -e

命令运行时的日志如下图,明显和我们平时编译时看到的日志形式不一样

WX20240411-203940@2x.png

如下这张图是sh hvigorw assembleHap命令执行时的日志,正常开发过程中的样子

WX20240411-203833@2x.png

输出hvigor日志,对于分析 hvigor 和 hvigor-ohos-plugin 有一定的帮助-From harvey

菜单编译

操作路径

  1. Build -> "Build Hap(s)/APP(s)" -> Build Hap(s)
  2. Build -> "Build Hap(s)/APP(s)" -> Build App(s)

编译时,在控制台"Run"栏中,可以在第一行看到编译命令,精简一下就是:

xxx/node xxx/hvigor.js --mode module -p product=default assembleHap

编译时效果 WX20240411-211146@2x.png

xxx/node:其完整路径是已安装的node文件路径

WX20240411-212047@2x.png

xxx/hvigor.js:是处于工程缓存目录下,应用只要编译一次,其缓存目录以后是不会发生变化的 WX20240411-212225@2x.png

在DevEco Studio中配置应用编译参数

WX20240412-085357@2x.png WX20240412-085640@2x.png WX20240412-085604@2x.png

编译脚本

应用工程编译脚本

WX20240411-215306@2x.png
  • 每个模块都有一个hvigorfile.ts, 这是编译模块任务的脚本

编译hap

export { hapTasks } from '@ohos/hvigor-ohos-plugin';

编译har

export { harTasks } from '@ohos/hvigor-ohos-plugin';

编译hsp

module.exports = require('@ohos/hvigor-ohos-plugin').hspTasks
  • 根目录下的hvigorfile.ts, 这是编译应用任务的脚本

编译APP

export { appTasks } from '@ohos/hvigor-ohos-plugin';
  • 根目录下的hvigor文件夹

hvigor-config.json5: 配置 hvigor 和 hvigor-ohos-plugin

hvigor-wrapper.js: 采用hvigorw / hvigorw.bat脚本编译时的启动脚本

脚本启动流程

WX20240411-224254@2x.png

工程根目录下的higorw脚本,通过shell命令加载 hvigor/hvigor-wrapper.js文件,hvigor-wrapper.js文件接收shell脚本传递过来的参数,启动.hvigor/.../hvigor.js编译入口文件,至此整个启动已完成。

如果需要在编译前插入js脚本,完全可以在higorw脚本中,使用"exec"命令先执行自己的js脚本。(From harvey)

WX20240412-002852@2x.png

hvigor-wrapper.js 文件中的部分内容

1.初始化工程目录

这些目录,在/Users/xxx/.hvigor

WX20240412-002331@2x.png

2.生成应用工程缓存名称,即 用户路径下/.hvigor/project_cache/xxx

生成规则:对工程完整路径进行md5加密 WX20240411-234336@2x.png

3.接收参数,hvigor-wrapper.js文件

WX20240411-234109@2x.png

4.启动hvigor.js WX20240412-002147@2x.png

hvigor和hvigor-ohos-plugin概况

hvigor.js是一切编译任务启动的入口

Object.defineProperty(exports, "__esModule", { value: true });
const init_project_workspace_js_1 = require("../src/cli/wrapper/init-project-workspace.js");
const prepare_node_path_js_1 = require("../src/cli/wrapper/prepare-node-path.js")from harvey_fly;
const cli_js_1 = require("../src/cli/main/cli.js");
const cliOptions = (0, cli_js_1.parseCommand)();
//1. 初始化工程, 
(0, init_project_workspace_js_1.initProjectWorkSpace)();
//2. 初始化节点任务
(0, prepare_node_path_js_1.initNodePath)();
//3. 启动编译
(0, cli_js_1.startHvigorBuild)(cliOptions);

为了方便后续梳理,针对.hvigor文件夹中的如下两个包做个简单的类图

xxx是自己电脑的账户名 /Users/harvey/.hvigor/project_caches/46ba120c223ec60d2326a333bd18143a/workspace/node_modules/.pnpm/@ohos+hvigor-ohos-plugin@2.4.2_@ohos+hvigor@2.4.2/node_modules/@ohos

/Users/harvey/.hvigor/project_caches/46ba120c223ec60d2326a333bd18143a/workspace/node_modules/.pnpm/@ohos+hvigor-ohos-plugin@2.4.2_@ohos+hvigor@2.4.2/node_modules/@ohos/hvigor-ohos-plugin

图中,白色背景为@ohos包中的内容,绿色背景为hvigor-ohos-plugin包中的内容。如下这张图不包含FA模式

Higor编译 (1).jpg

Hvigor编译2.jpg

.hvigor内部编译流程

从应用开发角度看,整个应用编译流程是比较清晰和简单的:1. 配置信息 2.启动任务通道 WX20240415-091130@2x.png

整体编译过程中,通过DevEco Studio打开应用工程可知,有两个插件依赖:1. @hvigor 2. @hvigor-ohos-plugin。 有一个应用工程入口脚本:hvigor-wrapper.js。有多个模块任务脚本:hvigorfile.ts。这些文件之间的关系是如何处理的?看下图

WX20240415-100302@2x.png

通过上边这个流程可以确认的是,hvigorfile.ts文件就是HarmonyOS留给应用开发者的自定义脚本入口,仅仅是如何克服在 hvigor2.4.2 编译系统中的使用问题而已。不过这点也没有多大的担心,在HarmonyOS Next中,应该已支持自定义编译脚本了。

在“开始执行pipeline”时,前期的配置信息阶段,会实例化一个ProjectImpl对象, 里边包含了关键的任务信息,hvigor将其称之为_taskContainer

WX20240415-103852@2x.png

"开始执行pipeline"完成后,真正的任务执行调度是通过TaskExecutor中的setInterval循环触发的。

WX20240415-104628@2x.png

结尾

如果还想继续深入了解hvigor编译应用流程,可以按照你所了解到的hap/har/hsp/app概念,沿着一个任务慢慢的研究。

注意:守护进程启动过程中,所产生的日志,都在 “xxx/.hvigor/daemon/log/2.4.2/”文件夹中,每启动一次工程会生成两个命名为 "daemon-进程号.log" 的文件,一个代表刚启动的主进程,一个代表守护进程。

祝好运!