看懂 ohpm 依赖树:版本范围、锁文件与本地覆盖
专栏:HarmonyOS 三方库适配实战(4/30)
本篇目标:从“我声明了哪个版本”进一步追到“工程最终安装了什么”。
摘要
直接依赖写的是1.2.0,工程里出现问题的却可能是它的某个间接依赖;两个模块声明相同包名,也可能解析到不同上下文。本文使用ohpm list、语义化版本、oh-package-lock.json5和overrides建立一条可复现的排查路径。你将学会保存依赖树、定位父依赖、临时替换本地适配版本,并避免用*或手改锁文件制造“只在我电脑上能跑”的工程。
适用范围与完成标志
本文适用于已经能够执行 ohpm、但需要解释“最终安装了什么”和“为什么是这个版本”的 HarmonyOS 工程。开始前应保存ohpm --version、当前声明文件和锁文件状态;不要先清理安装目录。
完成本篇后,你应该能从一个异常节点反查父依赖,说明版本来源,并用可回滚的overrides验证本地修复。覆盖成功只证明依赖解析符合预期,后续仍需构建和核心调用验证。
为什么只看oh-package.json5不够
模块声明:
{ "dependencies": { "feature-kit": "1.2.0" } }只说明当前模块直接依赖feature-kit@1.2.0。如果feature-kit又依赖text-core,真正参与构建的版本还要经过依赖解析。排错时至少要同时查看:
- 直接声明;
- 完整依赖树;
- 锁文件;
- 工程级覆盖配置;
- 当前命令针对哪个模块或 target。
图 1:依赖解析链(示意图)。声明文件、版本解析、锁文件和实际安装树必须一起看。
先把依赖树打印出来
在包含目标oh-package.json5的工程或模块上下文中执行。官方ohpm list文档说明:不指定包名时默认只展示直接依赖,-d控制深度,指定包名时可查看父依赖:
ohpm list ohpm list-d10ohpm list-johpm list默认查看直接依赖。-d 10将树展开到指定深度,适合阅读父子关系。-j使用 JSON 输出,适合保存和后续比较。
当前 ohpm 还提供递归查看工程所有模块依赖的能力:
ohpm list-r-r从 ohpm 5.2.0 起用于打印工程所有 module 的依赖。不同客户端版本支持的选项可能不同,执行前先记录ohpm --version,并以当前官方命令文档和本机ohpm help为准。
用一棵小树理解间接依赖
假设输出逻辑如下:
entry 1.0.0 └─┬ feature-kit 1.2.0 └── text-core 1.4.2如果错误堆栈指向text-core,不应在entry中盲目删除feature-kit。先回答:
- 谁把
text-core带进来? - 它的版本来自范围解析、锁文件还是覆盖?
- 另一个模块是否安装了不同版本?
- 适配补丁是针对
1.4.2还是别的上游版本?
上面的树是教学示例,不是本轮真实命令输出。
版本范围会改变什么
ohpm update按 semver 规则更新依赖。官方示例说明了常见范围的差异:
| 声明 | 含义示例 | 风险 |
|---|---|---|
"1.2.3" | 只接受精确版本 | 更新需要显式修改 |
"~1.2.3" | 通常允许补丁版本更新 | 补丁仍可能带来行为变化 |
"^1.2.3" | 通常允许同主版本内更新 | 解析结果可能随仓库变化 |
"*" | 使用当前可解析的最新版本 | 最不利于复现实验 |
对于适配教程和故障复现,建议先锁定精确版本。等测试与兼容策略成熟后,再决定是否放宽范围。
小于1.0.0的版本要格外谨慎。官方文档展示了^0.2.0与^0.4.0的解析差异,不能把所有^简化成“主版本不变即可”。
oh-package-lock.json5是证据,不是手工配置入口
官方oh-package.json5文档建议把oh-package-lock.json5提交到版本库,因为它记录准确依赖树,可帮助团队和 CI 获得一致版本。同时官方不建议手工编辑,也不建议让其他分析工具直接依赖其内部格式。
正确做法:
修改 oh-package.json5 或使用受支持的 ohpm 命令 ↓ 执行安装/更新 ↓ 检查依赖树和测试结果 ↓ 提交声明文件与更新后的锁文件错误做法是为了“把版本改回来”直接编辑锁文件。这样可能造成声明、解析元数据和安装目录不一致。
用overrides验证本地修复
适配过程中,经常需要让上层应用临时使用本地源码或 HAR。overrides必须写在项目根目录的工程级oh-package.json5,写在模块级文件中不会生效。下面只展示要新增的字段,其余字段保留当前工程真实值:
{ // 其余工程级字段保持不变 "overrides": { "text-core": "file:./adapters/text-core" } }或者指向本地 HAR:
{ // 其余工程级字段保持不变 "overrides": { "text-core": "file:./libs/text-core.har" } }这两段是字段片段,不是完整配置文件;包名和相对路径必须换成当前项目的真实值。覆盖前先保存一份依赖树,覆盖后重新安装并再次保存:
ohpm list-j>dependency-before.json ohpminstall--allohpm list-j>dependency-after.json ohpm list-d10>dependency-after.txt验证目标不是“命令没报错”,而是依赖树明确显示预期节点已经指向本地版本,随后目标模块完成构建和核心用例。
回滚时删除本次overrides条目,重新安装,再确认依赖树恢复为远程解析结果。不要把临时覆盖和本地绝对路径悄悄提交为长期方案。
图 2:本地复现实验记录。覆盖解析和 HAP 构建成功,但同时出现本地模块信息警告;实验后已回滚到本地 HAR。
一个可复用的依赖差异记录
每次变更前后保存以下内容:
## Dependency change - reason: <why this change is needed> - direct_dependency: <package and declared range> - affected_transitive_dependency: <package and resolved version> - before_tree: <evidence file> - after_tree: <evidence file> - lockfile_changed: <yes/no> - build_result: <pass/fail/unknown> - runtime_result: <pass/fail/not-tested> - rollback: <how to remove the override>覆盖是实验手段,不应悄悄变成永久补丁。确认修复后,要么发布正式适配版本并更新依赖,要么把覆盖原因、责任人和移除条件留在工程记录中。
故障复盘:今天没改代码,为什么突然构建失败
按下面顺序排查:
- 对比
oh-package.json5和锁文件的提交记录; - 对比
ohpm --version、SDK 和目标配置; - 保存当前
ohpm list -j; - 检查是否使用
*、范围版本或 tag; - 检查工程级
overrides、参数化配置和依赖重写; - 确认失败发生在安装、导入、构建还是运行阶段;
- 只在证据指向缓存时再执行清理。
若确需清理,官方ohpm clean默认会清理工程各模块的安装目录和锁文件;--keep-lockfile可保留锁文件。执行前先提交或备份需要的现场,不要把清理当作第一步:
ohpm clean --keep-lockfile本篇产物
- 一组查看直接、深层、JSON 和全模块依赖树的命令。
- 一套锁文件使用规则。
- 一个本地源码/HAR 覆盖模板和回滚记录。
进入下一篇前,应能从一个间接依赖反向找到父依赖,并说明它的版本为什么会被解析为当前值。
官方来源
- ohpm list:用于依赖树、深度、JSON、递归与 target 选项。访问日期:2026-10-15。
- oh-package.json5:用于
overrides、本地源码/HAR 和锁文件说明。访问日期:2026-10-15。 - ohpm update:用于 semver 更新行为与冲突处理选项。访问日期:2026-10-15。
- ohpm clean:用于清理范围及保留锁文件选项。访问日期:2026-10-15。
验证声明
本文已于 2026-10-15 核对华为官方ohpm list、oh-package.json5、ohpm update与ohpm clean文档,并在独立工程执行依赖安装、JSON/深度依赖树、本地源码覆盖、目标 SDK 构建和回滚。HAR 路径与本地源码路径均被ohpm list正确读回,覆盖状态下 HAP 构建退出码为 0;Hvigor 同时给出本地模块信息警告,因此覆盖只作为实验手段,最终已回滚到entry/libs/text-tool.har。未执行设备验证,也未运行会删除安装现场的ohpm clean。