HarmonyOS 二开前先查本地依赖闭环:一个零依赖 oh-package 审计器

举报
yd_299903619 发表于 2026/11/11 08:16:00 2026/11/11
【摘要】 从一个真实 HarmonyOS 公开工程出发,介绍只用 Python 标准库审计 oh-package.json5 中 file: 本地依赖的路径、包清单、别名和 main 入口,并给出 5 项测试与实际审计结果。

HarmonyOS 二开前先查本地依赖闭环:一个零依赖 oh-package 审计器

问题从哪里来

HarmonyOS 工程常用 oh-package.json5 里的 file: 依赖拆分公共模块和业务模块,例如:

{
  "dependencies": {
    "common": "file:../common",
    "routedesign": "file:../feature/RouteDesign"
  }
}

这种写法在单仓库开发时很方便,但它把一组隐式契约留给了构建阶段:路径必须存在、目标目录必须有包清单、依赖别名要和目标 name 一致、配置了 main 时入口文件也不能缺。任意一项断裂,真正的问题往往被后续的依赖解析错误、模块找不到或构建中断掩盖。

本次用鸿蒙开发实践中心的公开案例仓库做验证:

它不是用肉眼逐项核对,而是把“本地依赖闭环”变成一个可重复、可进 CI 的检查。

审计器检查什么

脚本只用 Python 标准库,递归读取工程内所有 oh-package.json5,跳过 node_modules 和 oh_modules。对每一条 file: 依赖执行四项检查:

  1. 目标目录是否存在;
  2. 目标目录中是否存在 oh-package.json5;
  3. 依赖别名是否与目标包的 name 一致;
  4. 若目标包声明了非空 main,对应文件是否存在。

远程依赖(例如 @ohos/axios)不参与本地路径检查,实际构建仍负责它们的下载与版本解析。

核心实现

解析 JSON5 时不能直接用 json.loads(),因为真实工程常见单引号、注释和尾逗号。这里采用范围有限的扫描策略:先屏蔽注释,再定位 dependencies 对应的对象,最后只提取该对象内的字符串键值对。

def _object_after_field(text: str, field_name: str) -> str | None:
    masked = _mask_comments(text)
    pattern = re.compile(
        QUOTED_OR_BARE_FIELD.format(re.escape(field_name))
    )
    match = pattern.search(masked)
    if not match:
        return None

    brace = masked.find("{", match.end())
    depth = 0
    quote = None
    escaped = False
    for i in range(brace, len(masked)):
        ch = masked[i]
        if quote:
            if escaped:
                escaped = False
            elif ch == "\\":
                escaped = True
            elif ch == quote:
                quote = None
            continue
        if ch in {'"', "'"}:
            quote = ch
        elif ch == "{":
            depth += 1
        elif ch == "}":
            depth -= 1
            if depth == 0:
                return masked[brace + 1:i]
    return None

真正解析路径时,以当前包清单所在的目录为基准,而不是以执行命令的目录为基准:

target = (package_file.parent / raw_target).resolve()
if not target.is_dir():
    error("DEP002", "目标目录不存在")

target_manifest = target / "oh-package.json5"
if not target_manifest.is_file():
    error("DEP003", "目标目录缺少 oh-package.json5")

这样无论从工程根目录、IDE 终端还是 CI 工作目录启动脚本,判断结果都一致。

失败信息要能直接定位

脚本给每类问题一个稳定编号:

编号 含义
DEP001 file: 后没有路径
DEP002 目标目录不存在
DEP003 目标目录缺少 oh-package.json5
DEP004 目标包缺少 name
DEP005 依赖别名与目标包 name 不一致
DEP006 目标包的 main 文件不存在

有错误时退出码为 1,可以直接作为 CI 或提交前检查的一步:

python3 harmony_dep_audit.py .

需要接入其他工具时输出 JSON:

python3 harmony_dep_audit.py . --json

本次实测

项目包含 5 个单元测试,覆盖有效依赖、目录缺失、清单缺失、别名不一致和远程依赖忽略,运行结果:

test_alias_must_match_target_name ... ok
test_missing_target_directory ... ok
test_remote_dependencies_are_ignored ... ok
test_target_without_package_manifest ... ok
test_valid_local_dependency ... ok

Ran 5 tests in 0.010s
OK

对公开案例仓库执行审计:

[PASS] HarmonyOS local dependency audit
root=/private/tmp/bustravel.u2diep
packages=9 local_edges=12

这说明该快照中的 12 条 file: 依赖均能解析到同名目标包;它不证明整个 HarmonyOS 工程可以编译,也不替代 DevEco Studio、SDK、签名和真机/模拟器验证。它只回答一个更窄但很容易被忽略的问题:本地模块引用关系是否闭环。

适用的接入方式

  • 在模板仓库克隆完成后先运行一次,尽早暴露空目录、漏提交模块和错误相对路径。
  • 在 CI 中作为快速静态检查,先于耗时的 HAP 构建执行。
  • 在二次开发拆分模块后运行,防止依赖别名和包名逐渐漂移。
  • 将 JSON 输出接入看板,统计新增或未修复的依赖错误。

边界与后续

当前实现刻意不完整解析整个 JSON5 语法,只提取 dependencies 中的字符串键值对,因此不处理复杂表达式或非字符串依赖声明。它也不会执行 ohpm install、构建 HAP 或判断业务功能是否正确。

下一步可以增加:

  1. 检测循环本地依赖;
  2. 把 main 入口指向的目录与模块类型交叉校验;
  3. 为历史错误建立基线,只阻止新增问题;
  4. 输出 Graphviz 或 Mermaid 依赖图。

这类检查不会提高业务代码的复杂度,却能减少“代码看着都在,构建却找不到模块”的排查成本。对 HarmonyOS 模板二开来说,先让依赖图自洽,再进入真实构建和设备验证,顺序更可靠。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。