为鸿蒙开发者写一个 nvm:hmvm 的设计与实现

做鸿蒙开发一段时间后,我遇到了一个让人头疼的问题 ------ 项目 A 用 command-line-tools 6.0.2 项目 B 用 6.1.0,每次切换都要手动改环境变量、重启终端。 于是我参考 nvm 的架构,用 AI 2小时搓了一个 Shell 实现了 hmvm,一条命令搞定多版本切换。

为什么要做这个

鸿蒙开发依赖 command-line-tools 工具链,它包含了 ohpmhvigorhdc 等核心工具。随着 HarmonyOS API 版本不断迭代,不同项目往往需要锁定不同版本:

  • 老项目跑在 API 22(command-line-tools 6.0.2)
  • 新项目要用 API 23(command-line-tools 6.1.0)

手动管理的方式是在 ~/.zshrc 里写死路径,每次切换都要编辑文件再 source。这不仅繁琐,还很容易出错------路径写错了、忘记 source 了,莫名其妙的构建失败会让你排查半小时。

Node.js 生态有 nvm,Flutter 生态有 fvm,为什么鸿蒙没有? 于是 hmvm 就这么诞生了。


怎么做的

使用工作流概览

在深入细节之前,先看整体使用流程:

流程图:

完整流程分五步:安装 hmvm → 配置 Shell → 导入版本 → 切换版本 → 设置全局默认

第一步:安装 hmvm

提供两种安装方式,推荐直接用 curl 一行搞定:

bash 复制代码
# 方式一:curl 远程安装
curl -o- https://raw.githubusercontent.com/SummerKaze/hmvm/main/install.sh | bash

# 方式二:手动 clone
git clone https://github.com/SummerKaze/hmvm.git ~/.hmvm
cd ~/.hmvm && ./install.sh

install.sh 会自动检测你的 Shell 类型(zsh/bash),在 ~/.zshrc~/.bashrc 末尾追加 source 指令,之后每次打开终端 hmvm 命令就能用了。

bash 复制代码
# 安装完成后执行(或重启终端)
source ~/.zshrc

第二步:导入已有版本(两种模式)

注意:目前 hmvm 暂不支持在线下载(华为账号鉴权比较复杂,待后续实现)。需要你先通过 DevEco Studio 或官网下载好 command-line-tools,再通过 hmvm 导入管理。

模式一:完整复制安装(--from

将 command-line-tools 目录完整复制到 hmvm 的管理目录下,安全独立,适合需要隔离的场景。

bash 复制代码
hmvm install 6.1.0 --from /path/to/command-line-tools

输出示例:

arduino 复制代码
Installing HarmonyOS command-line-tools v6.1.0 from /path/to/command-line-tools...
Installed HarmonyOS command-line-tools v6.1.0 successfully.
Now using HarmonyOS command-line-tools v6.1.0

缺点是占用额外磁盘空间,command-line-tools 通常有好几 GB。

模式二:符号链接安装(--link,推荐)

不复制文件,只创建一个符号链接指向原始目录。安装瞬间完成,零磁盘占用。

bash 复制代码
hmvm install 6.0.2 --from /Applications/DevEco-Studio.app/Contents/sdk/../command-line-tools --link

输出示例:

arduino 复制代码
Linking HarmonyOS command-line-tools v6.0.2 from /path/...
Linked HarmonyOS command-line-tools v6.0.2 successfully.
Now using HarmonyOS command-line-tools v6.0.2

卸载符号链接版本时(hmvm uninstall),只删除链接本身,原始目录不受影响。

第三步:查看已安装版本

bash 复制代码
hmvm ls

输出一张美观的表格,展示每个版本的 codelinter / ohpm / hstack / hvigor / API 版本号:

bash 复制代码
Cache directory:  /Users/h1007/.hmvm/versions/clt
Directory Size: 6.1G

┌─────────────┬────────────┬───────┬────────┬────────┬─────┬────────┬───────┐
│ Version     │ codelinter │ ohpm  │ hstack │ hvigor │ API │ Global │ Local │
├─────────────┼────────────┼───────┼────────┼────────┼─────┼────────┼───────┤
│ 6.0.2       │ 6.0.240    │ 6.0.1 │ 5.1.0  │ 6.22.3 │ 22  │        │ ●     │
├─────────────┼────────────┼───────┼────────┼────────┼─────┼────────┼───────┤
│ 6.1.0       │ 6.0.240    │ 6.1.1 │ 5.1.0  │ 6.23.2 │ 23  │ ●      │       │
└─────────────┴────────────┴───────┴────────┴────────┴─────┴────────┴───────┘
  • Global ●:全局默认版本(新建终端自动激活)
  • Local ●:当前 shell 中手动激活的版本

第四步:切换版本

bash 复制代码
hmvm use 6.0.2        # 当前 shell 立即生效
hmvm use 6.1.0

切换后工具链版本立即变化:

bash 复制代码
$ hmvm use 6.0.2
Now using HarmonyOS command-line-tools v6.0.2
$ hvigorw --version
6.22.3

$ hmvm use 6.1.0
Now using HarmonyOS command-line-tools v6.1.0
$ hvigorw --version
6.23.2

hmvm use 的底层逻辑是替换 PATH 中的工具链路径,并写入以下环境变量:

变量 作用
DEVECO_NODE_HOME tool/node 路径,供 hvigor 识别 Node 运行时
DEVECO_SDK_HOME sdk 路径
HDC_SDK_PATH HDC 调试工具链路径
HMVM_CURRENT 当前激活版本号(供 hmvm current 读取)

第五步:设置全局默认版本

新建终端时不想每次手动 hmvm use?设置全局默认版本:

bash 复制代码
hmvm global 6.1.0

之后每次打开新终端,hmvm 会静默激活该版本。

进阶:项目级版本锁定(.hmvmrc)

在项目根目录创建 .hmvmrc,写入版本号:

复制代码
6.0.2

进入项目后执行 hmvm use(无参数)即可自动按 .hmvmrc 切换:

bash 复制代码
cd ~/my-harmony-project
hmvm use
# Now using HarmonyOS command-line-tools v6.0.2

或者在切换时带上 --save 参数,一步写入 .hmvmrc

bash 复制代码
hmvm use 6.0.2 --save

完整命令速查

命令 说明
hmvm install <version> --from <path> 复制安装
hmvm install <version> --from <path> --link 符号链接安装(零拷贝)
hmvm use [<version>] [--save] 切换版本(无参数读取 .hmvmrc)
hmvm global [<version>] 设置/查看全局默认版本
hmvm ls / hmvm list 列出已安装版本(表格)
hmvm current 查看当前激活版本
hmvm uninstall <version> 卸载版本
hmvm alias <name> [<version>] 设置/查看/删除别名
hmvm which 显示工具命令路径

踩坑总结

坑一:符号链接版本的元数据问题

符号链接版本(--link)有个特殊之处:通过 readlink 得到的路径是原始目录,而不是 hmvm 管理目录下的版本路径,所以无法用常规方式读取版本信息。

解决方案 :旁路元数据文件。安装符号链接版本时,hmvm 会在 versions/clt/ 目录下生成一个 .meta_v6.0.2.txt 文件,存储该版本的 codelinter、ohpm 等版本信息。hmvm ls 会懒加载生成这些元数据(首次执行稍慢,后续读缓存)。

bash 复制代码
$HMVM_DIR/versions/clt/
├── v6.1.0/               # 完整复制安装
├── v6.0.2 -> /path/...   # 符号链接安装
└── .meta_v6.0.2.txt      # 旁路元数据(自动生成)

在 zsh 中,如果开启了 CHASE_LINKS 选项(某些 zsh 配置会开启),$PWD 会跟随符号链接解析到真实路径,导致从路径推断当前版本的逻辑失效。

解决方案 :不依赖路径推断,改用环境变量 $HMVM_CURRENThmvm use 切换时直接把版本号写入该变量,hmvm current 读取时直接返回,绕开了路径解析的坑。

bash 复制代码
hmvm current   # 读取 $HMVM_CURRENT,而非从 PATH 逆推

坑三:PATH 路径堆积

如果在同一个 shell 中多次调用 hmvm use,PATH 中的工具链路径会不断追加,形成类似这样的路径:

bash 复制代码
/home/user/.hmvm/versions/clt/v6.1.0/bin:/home/user/.hmvm/versions/clt/v6.0.2/bin:/usr/local/bin:...

解决方案 :参考 nvm 的 hmvm_change_path 函数。每次切换时,先从 PATH 中删除旧版本的路径,再插入新版本的路径,保证 PATH 中始终只有一个 hmvm 工具链路径。

坑四:在线下载暂不支持

华为的 command-line-tools 下载需要账号鉴权,无法简单 curl,这是目前最大的局限性。当前版本只支持本地导入,在线下载功能列在 TODO 里,后续会通过模拟登录或 API Token 方式实现。


总结与互动

hmvm 目前已经稳定支持我的日常开发工作,在多个鸿蒙项目间切换版本只需一条命令。代码是纯 Shell 实现,POSIX 兼容,没有任何外部依赖,macOS 和 Linux 都能用。

项目地址:github.com/SummerKaze/...

如果你也在做鸿蒙开发,欢迎试用并提 issue 或 PR。有几个问题想听听大家的看法:

  • 你现在是怎么管理 command-line-tools 版本的?
  • 在线下载这个功能你有需求吗?有没有已知的华为账号鉴权方案?
  • 有没有其他希望加入的功能,比如自动检测 .hmvmrc 切换(进入目录自动触发)?

欢迎在评论区交流!

相关推荐
在人间耕耘2 天前
HarmonyOS Vision Kit 视觉AI实战:把官方 Demo 改造成一套能长期复用的组件库
人工智能·深度学习·harmonyos
王码码20352 天前
Flutter for OpenHarmony:socket_io_client 实时通信的事实标准(Node.js 后端的最佳拍档) 深度解析与鸿蒙适配指南
android·flutter·ui·华为·node.js·harmonyos
HarmonyOS_SDK2 天前
【FAQ】HarmonyOS SDK 闭源开放能力 — Ads Kit
harmonyos
Swift社区2 天前
如何利用 ArkUI 框架优化鸿蒙应用的渲染性能
华为·harmonyos
特立独行的猫a2 天前
uni-app x跨平台开发实战:开发鸿蒙HarmonyOS影视票房榜组件完整实现过程
华为·uni-app·harmonyos·轮播图·uniapp-x
盐焗西兰花2 天前
鸿蒙学习实战之路-STG系列(5/11)-守护策略管理-添加与修改策略
服务器·学习·harmonyos
盐焗西兰花2 天前
鸿蒙学习实战之路-STG系列(4/11)-应用选择页功能详解
服务器·学习·harmonyos
lbb 小魔仙2 天前
鸿蒙跨平台项目实战篇03:React Native Bundle增量更新详解
react native·react.js·harmonyos
特立独行的猫a2 天前
uni-app x跨平台开发实战:开发鸿蒙HarmonyOS滚动卡片组件,scroll-view无法滚动踩坑全记录
华为·uni-app·harmonyos·uniapp-x