HarmonyOS鸿蒙原生包HNP全解析:从规范到实战的完整指南

初次接触鸿蒙三方库移植的小伙伴,可能对HNP.json这个文件及HNP包感到困惑。hnp包是必须的吗?必须有它吗?不打成这个包没法用吗?如何安装使用它?它是干嘛的?HNP(OpenHarmony Native Package)是 OpenHarmony 的原生包格式,用于打包原生程序和库。HNP 包本质上是一个 ZIP 文件,包含可执行文件、共享库、配置文件hnp.json和元数据。

鸿蒙HarmonyOS原生包HNP全解析:从规范到实战的完整指南,这个文章从头到尾细细讲来,给你答疑解惑,一文搞懂HNP的全貌。

更多交流学习,欢迎加入开源鸿蒙PC社区harmonypc.csdn.net/

欢迎在PC社区平台申请新建项目atomgit.com/OpenHarmony...

@TOC

1. 什么是 HNP?

1.1 定义

HNP(HarmonyOS Native Package / OpenHarmony Native Package) 是 OpenHarmony 系统的原生包格式,用于打包原生程序和库。为了便于一句话理解,你可以类比想象下 Windows 上的 MSI 安装包。它类似于 Windows 上的 MSI 安装包、Linux 上的 DEB/RPM 包,是鸿蒙生态中分发原生应用和库的标准化容器。

简单记住一句话:HNP 之于鸿蒙,如同 DEB 之于 Debian、MSI 之于 Windows------它是一个规范化的软件打包和分发格式。

关于HNP包的详细介绍,官方文档地址

atomgit.com/openharmony... atomgit.com/openharmony...

1.2 HNP 的核心价值

维度 说明
统一分发标准 简化跨设备、跨架构的依赖管理逻辑
提升兼容性 降低原生应用与系统的开发和部署成本
自动化管控 标准化元数据实现安装、升级、卸载全自动化
原子化安装卸载 作为 HAP 应用的组成部分,支持与宿主应用同生命周期管理
权限隔离机制 通过 public/private 类型区分,实现精细化的命令访问控制
环境自动集成 安装后自动创建软链接,无需手动配置 PATH 环境变量

1.3 HNP 不是必须的------理解它的定位

这里需要澄清一个常见的误区:不是所有三方库或命令行工具都必须打包为 HNP 格式。

场景 是否需要 HNP 说明
个人开发调试 ❌ 不需要 直接拷贝已签名的二进制到鸿蒙 PC 即可运行
通过 vcpkg 移植 ❌ 不需要 vcpkg 工作流中不涉及 HNP
通过 lycium 移植 ✅ 框架自动打包 lycium 编译完成后默认调用 hnpcli 打包为 HNP
集成到 HAP 应用分发 ✅ 必须 只有 HNP 包才能打入 HAP 中,由鸿蒙系统管理安装/卸载
通过应用市场分发 ✅ 必须 需要 HAP → HNP 的打包链路

你可以把它理解为一种"锦上添花"的规范化格式------不以 HNP 形式存在,二进制也能跑;但有了 HNP,才能融入鸿蒙的应用管理体系,管理其生命周期(安装、升级、卸载等)。

所以你问使用vcpkg构建的包,为何没见HNP的影子?因为它编译的so库,更多的是为了让鸿蒙的应用层使用arkts语言调用c/c++的底层库的接口使用的,这部分涉及NAPI的封装和调用,不在此文阐述。

当然vcpkg也可以构建命令行软件,那vcpkg的命令行软件需不需要HNP包?答案是根据你需要。如果是鸿蒙PC,你以文件发布你的命令行也可以。但如果是手机上总不能文件传来传去吧,得上架应用市场。那样你就需要进一步打包成HNP包,再打包成APP包上架。否则手机上可是无法直接安装使用的。


2. HNP 包的本质与格式

2.1 本质就是一个 ZIP 压缩包

HNP 包本质上就是 一个 ZIP 格式的压缩包(可以使用 zip 软件直接打开查看),内部包含:

python 复制代码
xxx.hnp
├── bin/
│   └── pngquant          # 可执行文件
├── lib/
│   └── libpng.so*        # 共享库
├── hnp.json              # 元数据配置文件
└── ...                    # 其他资源

通过 file 命令可以看到其真实类型:

bash 复制代码
$ file app.hnp
app.hnp: Zip archive data, at least v2.0 to extract, compression method=deflate

2.2 公有 HNP 与私有 HNP

HNP 包分为几种类型,区别如下:

类型 安装路径 环境变量 访问范围
公有 HNP /data/service/hnp/<name>_<version>/ 自动加入 PATH 所有应用可调用
私有 HNP /data/app/<bundleName>/<name>_<version>/ 不自动加入 PATH 仅宿主应用可调用
沙箱包(Sandbox) 实验性功能,提供进程级别的资源隔离 资料较少,注意以官方为准

公有 HNP 的二进制软链接会放到 /data/service/hnp/bin,该目录默认已在系统 PATH 中。


3. hnp.json 配置文件详解

hnp.json 是 HNP 包的核心元数据文件,位于包根目录,定义了软件包的身份、版本及安装规则。

3.1 最小配置示例

json 复制代码
{
    "type": "hnp-config",
    "name": "tree",
    "version": "2.2.1",
    "install": {}
}

3.2 官方标准配置示例(含软链接)

OpenHarmony 官方推荐的配置格式,包含 install.links 字段用于声明二进制软链接规则:

json 复制代码
{
    "type": "hnp-config",
    "name": "hnpsample",
    "version": "1.1",
    "install": {
        "links": [
            {
                "source": "/bin/hnpsample",
                "target": "hnpsample"
            }
        ]
    }
}

links 中的 source 是相对软件包根目录的源文件路径,target 是软链接文件名(生成在 bin 目录)。若未配置 links,默认对 bin 目录下所有二进制创建软链接。

3.3 完整配置示例(含可选字段)

json 复制代码
{
    "type": "hnp-config",
    "name": "pngquant",
    "version": "2.18.0",
    "arch": "arm64",
    "os": "ohos",
    "summary": "A lossy PNG compressor",
    "depends": ["libpng >= 1.6", "zlib >= 1.2"],
    "install": {
        "bins": ["bin/pngquant"],
        "libs": ["lib/*.so*"]
    }
}

3.4 各字段说明

字段 含义 是否必填 说明
type 配置文件类型标识 ✅ 必填 固定为 "hnp-config",工具通过它识别这是 HNP 配置
name 包名称 ✅ 必填 一般与上游项目名一致;不支持空格、特殊字符
version 包版本号 ✅ 必填 建议与 HPKBUILDpkgver 保持一致;不支持空格、特殊字符
arch 目标架构 可选 arm64x86_64
os 目标操作系统 可选 固定为 ohos
summary 简要描述 可选 包的用途说明
depends 依赖声明 可选 版本约束如 >= 1.6
install 安装规则 可选 空对象 {} 表示按默认目录结构安装
install.links 软链接规则(数组) 可选 每个对象含 source(相对路径)和 target(软链接名)

3.5 type 字段为什么重要?

type: "hnp-config" 是工具链识别配置文件类型的唯一标识hnpcli 等工具读到这个值,才知道按"HNP 配置"来解析。不要改成其他字符串,否则工具不认。

3.6 name vs pkgname:二者有何不同?

标识 所属文件 用途
pkgname HPKBUILD lycium 内部标识,用于目录名、构建命令、依赖解析
name hnp.json HNP 包的显示名称,面向最终用户

例如:

  • pkgname = "AES" → 构建命令 ./build.sh AES
  • name = "tiny-AES-c" → HNP 包名为 tiny-AES-c-1.0.0.hnp

3.7 version 不一致会怎样?

如果 hnp.jsonversionHPKBUILDpkgver 不一致:

  • 构建产物 :使用 pkgver 版本的源码
  • HNP 包元数据 :使用 hnp.json 中的 version
  • 结果:包名与内容不匹配,造成版本混乱

最佳实践:在 CI/CD 中添加版本一致性检查。

3.8 install 为空对象是什么意思?

install: {} 表示不在 JSON 里写复杂的安装映射 ,而是依赖 package() 函数已经放好的目录结构(如 lib/include/bin/ 等标准子目录)。这是最常用的配置方式。


4. hnpcli 打包工具

4.1 什么是 hnpcli?

hnpcli(HarmonyOS Native Package CLI)是官方提供的 HNP 包构建命令行工具,源码位于 OpenHarmony 的 startup_appspawn 仓库中。

4.2 基本用法

bash 复制代码
# 查看帮助
hnpcli -h

# 打包(目录中含 hnp.json 时)
hnpcli pack -i package_dir/ -o output.hnp

# 打包(目录中不含 hnp.json 时,手动指定名称和版本)
hnpcli pack -i package_dir/ -o output/ -n <软件名> -v <版本号>

# 解包
hnpcli unpack -i app.hnp -o output_dir/

# 查看包信息
hnpcli info app.hnp

4.3 在 SDK 中的位置

hnpcli 位于 OHOS SDK 的 toolchains/ 目录下:

bash 复制代码
export HNP_TOOL=${OHOS_SDK}/toolchains/hnpcli

4.4 技术架构

bash 复制代码
hnpcli
├── base/                  # 基础功能模块
│   ├── hnp_file.c         # 文件操作
│   ├── hnp_json.c         # JSON 处理
│   ├── hnp_log.c          # 日志系统
│   └── hnp_zip.c          # ZIP 压缩
├── pack/                  # 打包功能
│   ├── src/hnp_pack.c     # 打包实现
│   └── include/
└── hnpcli_main.c          # 主程序入口

依赖库:cJson(JSON 解析)、libboundscheck(边界检查)、zlib_static(压缩库,静态链接)

4.5 关键注意事项:rpath 配置

注意,打包到 HNP 中的二进制,需要添加 rpath 链接选项,确保运行时可自动加载 lib/ 目录下的依赖库:

cmake 复制代码
set(CMAKE_EXE_LINKER_FLAGS "-Wl,-rpath=\\$ORIGIN/../lib -Wl,--disable-new-dtags")

或在 Makefile / LDFLAGS 中:

bash 复制代码
LDFLAGS="-Wl,-rpath='$ORIGIN/../lib' -Wl,--disable-new-dtags"

$ORIGIN 是 ELF 约定的特殊变量,运行时展开为可执行文件所在的目录路径。../lib 则指向 HNP 包中的 lib 子目录,实现相对路径查找依赖,使二进制在任意安装位置都能正确加载共享库。这是让 HNP 包具备"可移动性"的关键配置。

4.6 打包限制

使用 hnpcli 打包时需注意以下限制:

限制项 上限 / 说明
路径长度 ≤ 4096 字符
包内文件数 ≤ 65535 个
HNP 文件大小 ≤ 4GB
目录名称 不支持中文字符
软件名 / 版本号 不支持空格、特殊字符
权限(Windows 打包) 默认赋予 others 可执行权限
权限(Linux/mac/OHOS 打包) 继承原文件 UGO 权限

5. HNP 打包完整流程

下面以 tree 工具为例,展示完整的 HNP 打包流程。

也可以参考猫哥的原始交叉编译方式 移植x265到鸿蒙PC平台的完整指南,文章地址:blog.csdn.net/qq8864/arti...

鸿蒙PC三方库移植: 移植PCRE2到鸿蒙PC平台的完整指南,文章地址:blog.csdn.net/qq8864/arti...

这两篇文章中的示例也完整展示了HNP包的打包使用方法。

5.1 总体步骤

perl 复制代码
源码编译 → 安装到 HNP 目录 → 复制 hnp.json → hnpcli pack 生成 .hnp → 可选同时生成 .tar.gz

5.2 环境变量配置

bash 复制代码
export OHOS_SDK="/path/to/ohos-sdk"
export HNP_TOOL=${OHOS_SDK}/toolchains/hnpcli
export HNP_PUBLIC_PATH=/data/service/hnp
export ARCHIVE_PATH=${WORK_ROOT}/output

5.3 路径规范

HNP 包的安装路径遵循严格的命名规范:

xml 复制代码
${HNP_PUBLIC_PATH}/<组件名称>.org/<组件名称>_<版本号>

例如:/data/service/hnp/tree.org/tree_2.2.1

这种规范化的路径设计保证了唯一性、可追溯性和标准化。

5.4 保存-修改-恢复模式

通过临时修改 PREFIX 环境变量,让 make install 将产物安装到 HNP 路径:

bash 复制代码
# 保存原始值
sys_prefix=${PREFIX}

# 临时修改为 HNP 路径
export PREFIX=${TREE_INSTALL_HNP_PATH}

# 构建和安装
make clean
make VERBOSE=1
make install

# 恢复原始值
export PREFIX=${sys_prefix}

5.5 双重打包策略

脚本同时生成两种格式的产物:

bash 复制代码
# 复制 hnp.json 到安装目录
cp hnp.json ${TREE_INSTALL_HNP_PATH}/

# 1. 生成 HNP 格式包(供鸿蒙系统直接安装)
${HNP_TOOL} pack -i ${TREE_INSTALL_HNP_PATH} -o ${ARCHIVE_PATH}/

# 2. 生成 tar.gz 压缩包(通用场景)
tar -zvcf ${ARCHIVE_PATH}/ohos_tree_2.2.1.tar.gz tree_2.2.1/
格式 用途
xxx.hnp 面向 HarmonyOS 生态,支持包管理器的自动化安装
xxx.tar.gz 通用性强,便于手动部署和跨平台分发

5.6 在 lycium 框架中的自动打包

使用 lycium 框架时,编译完成后 archive() 函数会默认调用 hnpcli 将产物打包为 HNP:

bash 复制代码
# archive() 函数
archive() {
    mkdir -p ${LYCIUM_ROOT}/output/$ARCH
    pushd $LYCIUM_ROOT/usr/$pkgname/$ARCH
        tar -zvcf ${LYCIUM_ROOT}/output/$ARCH/${pkgname}_${pkgver}.tar.gz *
    popd
    cp hnp.json $LYCIUM_ROOT/usr/$pkgname/$ARCH
    ${HNP_TOOL} pack -i ${LYCIUM_ROOT}/usr/$pkgname/$ARCH -o ${LYCIUM_ROOT}/output/$ARCH/
}

5.7 最终产物目录结构

bash 复制代码
output/
├── tree.hnp                 # HNP 格式包(ZIP)
└── ohos_tree_2.2.1.tar.gz  # tar.gz 压缩包

usr/hnpcli/arm64-v8a/
├── bin/
│   └── hnpcli              # 可执行文件(ELF ARM64)
└── hnp.json                 # HNP 配置文件

6. 将 HNP 嵌入 HAP 应用

注意:截止目前HNP 包不能直接独立安装,必须嵌入到 HAP 应用包中发布 。这是官方推荐的也是当前唯一可行的部署方式。我目前还没有发现通过命令行能安装HNP包的,如果你网上其他文章看到过说可以,那是错误的。

6.1 总体流程

复制代码
Native 软件源码 → 交叉编译 → hnpcli 打包为 .hnp → 嵌入 HAP → 签名 HAP → 安装运行

6.2 DevEco Studio 中嵌入 HNP

操作步骤:

① 下载 DevEco Studio,创建 HAP 工程

② 在 HAP 工程根目录新建 hnp 文件夹,按设备 ABI 创建子目录,存放 hnp 包:

bash 复制代码
HAP 工程根目录
└── hnp                      # hnp 根目录
    └── arm64-v8a            # 设备 ABI 目录
        ├── python.hnp       # hnp 包
        └── sub_dir          # 子目录(可选)
            └── test.hnp

③ 配置 module.json5 文件 ,在 module 字段下添加 hnpPackages,声明 hnp 包路径及类型:

json 复制代码
"hnpPackages": [
    {
        "package": "python.hnp",           // ABI 目录下的相对路径
        "type": "public"                   // 公有类型(所有应用可访问)
    },
    {
        "package": "sub_dir/test.hnp",
        "type": "private"                  // 私有类型(仅当前 HAP 可访问)
    }
]

④ 编译 HAP 包 :点击 DevEco Studio 菜单栏 Build > Build Hap(s)/APP(s) > Build Hap(s)

6.3 使用命令行打包 HAP(含 HNP)

在自动化构建场景下,使用 app_packing_tool.jar 打包 HAP,通过 --hnp-path 参数指定 hnp 根目录:

bash 复制代码
java -jar app_packing_tool.jar \
    --mode hap \
    --json-path <module.json路径> \
    --resources-path <资源路径> \
    --ets-path <ets文件目录> \
    --index-path <resources.index路径> \
    --pack-info-path <pack.info路径> \
    --out-path <输出路径/srcEntrance.hap> \
    --force true \
    --compress-level 5 \
    --pkg-context-path <pkgContextInfo.json路径> \
    --hnp-path <hnp根目录>     # ← 关键:指定 hnp 包路径

--hnp-path 参数说明:指定 hnp 根目录路径后,打包工具自动将目录下的所有 hnp 包打入 HAP 中。

优秀的打包 HAP(带HNP)范例,可参考社区仓:

gitcode.com/OpenHarmony... gitcode.com/OpenHarmony...

6.4 HNP 在 HAP 中的安装路径

类型 安装路径 环境变量 访问范围
公有 HNP /data/service/hnp/<name>_<version>/ HNP_PUBLIC_HOME=/data/service/hnp 所有应用可访问
私有 HNP /data/app/<name>_<version>/ HNP_PRIVATE_HOME=/data/app 仅嵌入它的 HAP 应用可访问

环境变量优先级HNP_PRIVATE_HOME > HNP_PUBLIC_HOME。当同名二进制同时存在于公有和私有路径时,优先执行私有路径。

6.5 卸载与冲突规则

  • 同步卸载:卸载 HAP 时,其嵌入的所有 hnp 包会同步卸载(若二进制被其他应用占用,卸载失败)
  • 同名冲突 :同名公有包(hnp.jsonname 相同)不可重复安装,后安装的 HAP 需将其改为私有包或卸载前序 HAP

7. HAP 签名流程

HAP 包(含嵌入的 HNP 包)必须经过签名才能在鸿蒙设备上安装运行。

7.1 操作步骤

① 配置签名信息

参考华为官方文档应用/服务签名的"手动签名"章节,准备签名所需配置。

② 下载签名工具

bash 复制代码
# 从 OpenHarmony 官方仓库下载
wget https://gitcode.com/openharmony/developtools_hapsigner/blob/master/dist/hap-sign-tool.jar

③ 执行签名命令

bash 复制代码
java -jar hap-sign-tool.jar sign \
    -profile <签名配置> \
    -out <输出文件> \
    -inFile <待签名HAP>

详细参数配置请参考 hap-sign-tool README_ZH


8. HNP 包的安装与部署

6.1 安装方式

HNP 包有几种安装和使用方式:

方式一:集成到 HAP 应用(官方推荐)

HNP 包需打入 HAP 应用包中,通过应用市场或 hdc install 安装。系统会将 HNP 包解压到对应目录:

复制代码
HAP 应用包 → 包含 HNP → hdc install → 系统解压 HNP 到目标路径
  • 公有 HNP 安装路径:/data/app/el1/bundle/<bundleName>/hnppublic/
  • 私有 HNP 安装路径:/data/app/el1/bundle/<bundleName>/hnp/

方式二:在 HAP 应用中调用 Native 二进制

在 HAP 应用的 C++ 代码中通过 execvexecve 执行 HNP 中的二进制:

c 复制代码
pid_t child = fork();
if (child < 0) {
    OH_LOG_ERROR(LOG_APP, "fork failed %d", errno);
    return;
}
if (child == 0) {
    // 公有 hnp:路径已在 PATH 中
    // 私有 hnp:使用完整路径
    int ret = execv("/data/app/test.org/test_1.1/bin/testBin", NULL);
    OH_LOG_ERROR(LOG_APP, "execv failed errno %d", errno);
    exit(errno);
} else {
    int status;
    if (waitpid(child, &status, 0) == -1) {
        OH_LOG_ERROR(LOG_APP, "waitpid failed errno %d", errno);
        return;
    }
}

方式三:通过 hdc shell 调试访问

bash 复制代码
# 连接设备
hdc list targets

# 进入设备终端
hdc shell

# 公有 hnp 路径(已加入 PATH)
cd /data/service/hnp

# 私有 hnp 路径
cd /data/app/el1/bundle/<bundleName>/hnp

6.2 当前的限制

⚠️ 重要说明:截至目前,HNP 包不能单独通过命令行安装。

网上有些文章说可以直接用命令行安装 HNP 包,这个描述是错的 。当前 HNP 必须被打包成 HAP 或 APP 的应用包,通过应用安装流程来部署。尚没有 hnpcli install xxx.hnp 这样的命令可以直接安装。

6.3 ELF 签名------运行的前置条件

所有在鸿蒙系统上运行的 ELF 文件和共享库必须经过签名 。签名信息存储在 ELF 文件的 .codesign 段中:

bash 复制代码
# 手动签名
binary-sign-tool sign -selfSign 1 -inFile my_program -outFile my_program

# 验证签名
llvm-readelf -S hello | grep codesign
# 应输出:.codesign PROGBITS ...(签名段存在)

编译时自动签名 :通过封装 ld.lld 链接器,在链接时自动注入 --code-sign 参数:

bash 复制代码
# 替换 ld.lld 为封装脚本
cd ohos-sdk/linux/native/llvm/bin
rm ld.lld
lld_absolute_path=$(realpath lld)
printf '#!/bin/bash\nexec -a "$0" %s --code-sign "$@"\n' "$lld_absolute_path" > ld.lld
chmod 0755 ld.lld

9. 从其他系统理解 HNP

为了便于理解,如果你熟悉其他操作系统,可以用类比来快速理解 HNP:

系统 安装包格式 包管理器 能否直接运行二进制?
Windows .msi / .exe winget / choco ✅ 可以
Linux (Debian) .deb apt / dpkg ✅ 可以
Linux (Red Hat) .rpm yum / dnf ✅ 可以
OpenHarmony .hnp HAP 集成 ✅ 可以(需签名)

核心思维:就像在 Linux 上,你的程序可以不以 .deb 形式存在,直接 ./binary 也能跑;在鸿蒙上同样如此------二进制文件只要经过签名,直接拷贝到鸿蒙 PC 上运行完全没问题。HNP 包是为规范化分发、生命周期管理而生的。


10. HNP 与主流包管理方案对比

特性 HNP Linux DEB macOS PKG Windows MSI
多架构支持 单包多架构 需分开发布 需分开发布 需分开发布
依赖隔离 沙箱环境 共享系统库 有限隔离 有限隔离
安装粒度 原子操作 依赖 dpkg 状态 原子操作 原子操作
权限控制 声明式配置 全或无 部分支持 部分支持
开发友好度 统一工具链 各发行版差异 Xcode 依赖 VS 依赖
ELF 签名 ✅ 强制要求 ❌ 不需要 ❌ 不需要 ❌ 不需要
部署方式 嵌入 HAP 分发 apt/dpkg 直接安装 独立安装器 独立安装器

11. 最佳实践与常见问题

11.1 最佳实践

① 版本一致性 在 CI/CD 中添加版本检查脚本,确保 hnp.jsonversionHPKBUILDpkgver 保持一致:

bash 复制代码
#!/bin/bash
pkgver=$(grep "^pkgver=" HPKBUILD | cut -d= -f2)
hnp_ver=$(jq -r .version hnp.json)
if [ "$pkgver" != "$hnp_ver" ]; then
    echo "ERROR: Version mismatch!"
    echo "  HPKBUILD: $pkgver"
    echo "  hnp.json: $hnp_ver"
    exit 1
fi

② 最小化原则 如果不需要特殊安装规则,保持 install 为空对象,依赖默认目录结构。

③ 即使暂时不用 HNP,也保留最小化配置 保留一个最简的 hnp.json,以备将来接入 HNP 打包流水线。

④ 路径管理 始终使用环境变量,避免硬编码路径;遵循 HNP 路径规范:<组件>.org/<组件>_<版本>

⑤ 环境隔离 使用"保存-修改-恢复"模式管理 PREFIX 等关键环境变量,确保不污染全局构建环境。

11.2 常见问题

问题 原因 解决方案
hnpcli: command not found HNP_TOOL 环境变量未设置或路径错误 检查 $OHOS_SDK/toolchains/hnpcli 是否存在
hnp.json not found 文件不存在或复制路径错误 检查 archive() 中的复制路径
[ERROR] source dir path is invalid hnpcli pack 的输入目录不存在 确认产物目录 usr/xxx/arm64-v8a/ 已生成
Invalid JSON hnp.json 格式错误 使用 jq . hnp.jsonpython -m json.tool 验证
编译后的二进制无法运行 ELF 未签名 检查 .codesign 段是否存在,配置自动签名

深度问答:HNP 命令行程序依赖很多 so 库,安装后能找到吗?

这是一个非常典型的问题。HNP 包中的命令能否找到依赖的 so 库,取决于三个关键因素。

情况一:so 库在同一个 HNP 包内(标准场景)

HNP 包的标准目录结构天然支持多 so 依赖:

python 复制代码
command.hnp
├── bin/
│   └── mycommand              ← 可执行文件
├── lib/
│   ├── libfoo.so              ← 依赖的 so 库
│   └── libbar.so.1
└── hnp.json

关键在于编译时必须设置 $ORIGIN rpath

bash 复制代码
LDFLAGS="-Wl,-rpath='$ORIGIN/../lib' -Wl,--disable-new-dtags"

$ORIGIN 是 ELF 的运行时变量,展开为可执行文件实际路径所在的目录(跟随软链接解析到真实路径)。假设安装后:

  • 软链接:/data/service/hnp/bin/mycommand → 实际二进制 /data/service/hnp/mycommand.org/1.0.0/bin/mycommand
  • 运行软链接时,$ORIGIN 解析为实际二进制目录 /data/service/hnp/mycommand.org/1.0.0/bin/
  • $ORIGIN/../lib/data/service/hnp/mycommand.org/1.0.0/lib/可以找到

所以,只要 so 和自己打包在同一个 HNP 中,且正确设置了 rpath,安装后该命令可以正常找到依赖。

情况二:so 库在另一个 HNP 包中(跨包依赖)

这是更棘手但实践中较少遇到的场景:

场景 能否找到 so 原因
so 在同一个 HNP 包lib/ ✅ 能 $ORIGIN/../lib 直接覆盖
so 在另一个公有 HNP 包lib/ ❌ 可能找不到 $ORIGIN/../lib 指向自己的 lib,不跨包
so 是 musl libc 等系统库 ✅ 能 系统标准路径,链接器自动搜索

解决方案有三种:

方案 做法 优点 缺点
① 合并打包(推荐) 将所有依赖的 so 与主二进制打入同一个 HNP 最简单可靠,环境一致 可能有少量冗余
② rpath 多路径 编译时指定多个搜索路径 不重复打包 硬编码路径,升级需重编
③ HAP 端 LD_LIBRARY_PATH HAP 代码中 setenv("LD_LIBRARY_PATH", "...", 1) 运行时灵活 自动化场景繁琐

社区实践建议: 对于大多数命令行工具,静态链接或将所需 so 全部打包到同一个 HNP 的 lib/是最省心的方式。这也是为什么社区工具(tree、pngquant、axel 等)在移植鸿蒙时通常采用静态链接或单包全量依赖的策略------环境一致性远胜于动态链接的灵活性。

11.3 HNP 与 lycium 的协作关系

scss 复制代码
HPKBUILD (构建逻辑)
    ↓
package() (安装产物到 usr/ 目录)
    ↓
archive() (复制 hnp.json + 打包)
    ↓
hnpcli pack (读取 hnp.json,生成 .hnp 文件)
  • HPKBUILD 决定构建什么
  • hnp.json 决定如何打包和分发
  • 两者通过 archive() 函数连接

注意:hnp.json 不是给 C 编译器看的,而是给打包/分发链路 用的。没有 archive() 或没有 hnpcli 时,你仍然可以正常交叉编译和安装到 usr/...,只是不会生成 HNP 侧产物。


12. 官方参考资料与完整链接

12.1 OpenHarmony 官方仓库(权威来源)

以下是 OpenHarmony 社区官方仓,是 HNP 相关规范的一手信息来源:

仓库 / 文档 说明 链接
startup_appspawn HNP 功能主仓库(含 hnp 规范、hnpcli 源码、打包工具) GitCode
→ HNP README(中文) HNP 功能说明、设计理念、使用方式 GitCode
→ HNP Pack 工具 README hnpcli 打包工具的详细使用指南 GitCode
developtools_packing_tool HAP 打包工具(app_packing_tool.jar GitCode
developtools_hapsigner HAP 签名工具(hap-sign-tool.jar GitCode
→ 签名工具 README_ZH 签名工具使用说明 GitCode

12.2 华为开发者官方文档

12.3 社区参考文章

12.4 其他参考


一句话总结 HNP: HNP 是鸿蒙原生应用的标准化打包格式,本质是 ZIP 包,通过 hnp.json 描述元数据,用 hnpcli 工具生成。它不能独立安装,必须嵌入 HAP 应用通过应用市场分发。不以 HNP 形式存在的签名二进制也能直接运行,但打包为 HNP 后可融入鸿蒙的 HAP 应用管理体系,实现原子化安装卸载和生命周期管理。

相关推荐
nashane5 小时前
HarmonyOS 6商城开发学习:剪贴板权限频繁弹窗的根治——从“自动嗅探“改为“用户主动触发“模型
华为·harmonyos
国服第二切图仔5 小时前
HarmonyOS APP《画伴梦工厂》开发第37篇-GridRow-GridCol——响应式网格布局
华为·harmonyos
痕忆丶5 小时前
openharmony开发基础之5.0.1版本文件管理器复制粘贴框架调用流程
harmonyos
国服第二切图仔6 小时前
HarmonyOS APP《画伴梦工厂》开发第31篇-语音识别实战——SpeechRecognitionEngine+AudioCapturer
语音识别·xcode·harmonyos
TrisighT8 小时前
Electron 鸿蒙 PC 上点外链唤醒应用,我试了 6 种写法只有 1 种能跑
前端·electron·harmonyos
TrisighT9 小时前
Electron 跑鸿蒙 PC 上,这 4 个 API 的行为跟 Windows 完全不一样——但文档一行都没写
windows·electron·harmonyos
蓝速科技11 小时前
蓝速科技 RISC-V 鸿蒙信创工控终端深度评测
科技·harmonyos·risc-v
zjxcq52011 小时前
鸿蒙 ArkUI 实战:Swiper 组件构建高性能轮播图
android·华为·harmonyos
TrisighT1 天前
DevEco Code 写鸿蒙 ArkTS 确实快,但我试了三天后把默认引擎换成了 Cursor
ai编程·harmonyos·cursor