HarmonyOS鸿蒙原生HNP包全解析:从规范到实战的完整指南
初次接触鸿蒙三方库移植的小伙伴,可能对HNP.json这个文件及HNP包感到困惑。hnp包是必须的吗?必须有它吗?不打成这个包没法用吗?如何安装使用它?它是干嘛的?HNP(OpenHarmony Native Package)是 OpenHarmony 的原生包格式,用于打包原生程序和库。HNP 包本质上是一个 ZIP 文件,包含可执行文件、共享库、配置文件hnp.json和元数据。这篇文章从头到尾细细讲来,给你答疑解惑,一文搞懂HNP包的全貌。
《HarmonyOS鸿蒙原生HNP包全解析:从规范到实战的完整指南》,这个文章从头到尾细细讲来,给你答疑解惑,一文搞懂HNP包的全貌。
更多交流学习,欢迎加入开源鸿蒙PC社区:https://harmonypc.csdn.net/
欢迎在PC社区平台申请新建项目:https://atomgit.com/OpenHarmonyPCDeveloper
猫哥的博客:https://blog.csdn.net/qq8864
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包的详细介绍,官方文档地址:
https://atomgit.com/openharmony/startup_appspawn/blob/master/service/hnp/README_zh.md
https://atomgit.com/openharmony/startup_appspawn/blob/master/service/hnp/pack/README_zh.md
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 软件直接打开查看),内部包含:
xxx.hnp
├── bin/
│ └── pngquant # 可执行文件
├── lib/
│ └── libpng.so* # 共享库
├── hnp.json # 元数据配置文件
└── ... # 其他资源
通过 file 命令可以看到其真实类型:
$ 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 最小配置示例
{
"type": "hnp-config",
"name": "tree",
"version": "2.2.1",
"install": {}
}
3.2 官方标准配置示例(含软链接)
OpenHarmony 官方推荐的配置格式,包含 install.links 字段用于声明二进制软链接规则:
{
"type": "hnp-config",
"name": "hnpsample",
"version": "1.1",
"install": {
"links": [
{
"source": "/bin/hnpsample",
"target": "hnpsample"
}
]
}
}
links中的source是相对软件包根目录的源文件路径,target是软链接文件名(生成在 bin 目录)。若未配置links,默认对bin目录下所有二进制创建软链接。
3.3 完整配置示例(含可选字段)
{
"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 |
包版本号 | ✅ 必填 | 建议与 HPKBUILD 的 pkgver 保持一致;不支持空格、特殊字符 |
arch |
目标架构 | 可选 | 如 arm64、x86_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 AESname = "tiny-AES-c"→ HNP 包名为tiny-AES-c-1.0.0.hnp
3.7 version 不一致会怎样?
如果 hnp.json 的 version 与 HPKBUILD 的 pkgver 不一致:
- 构建产物:使用
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 基本用法
# 查看帮助
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/ 目录下:
export HNP_TOOL=${OHOS_SDK}/toolchains/hnpcli
4.4 技术架构
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/ 目录下的依赖库:
set(CMAKE_EXE_LINKER_FLAGS "-Wl,-rpath=\\$ORIGIN/../lib -Wl,--disable-new-dtags")
或在 Makefile / LDFLAGS 中:
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平台的完整指南,文章地址:https://blog.csdn.net/qq8864/article/details/157426019
[鸿蒙PC三方库移植]: 移植PCRE2到鸿蒙PC平台的完整指南,文章地址:https://blog.csdn.net/qq8864/article/details/157425977
这两篇文章中的示例也完整展示了HNP包的打包使用方法。
5.1 总体步骤
源码编译 → 安装到 HNP 目录 → 复制 hnp.json → hnpcli pack 生成 .hnp → 可选同时生成 .tar.gz
5.2 环境变量配置
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 包的安装路径遵循严格的命名规范:
${HNP_PUBLIC_PATH}/<组件名称>.org/<组件名称>_<版本号>
例如:/data/service/hnp/tree.org/tree_2.2.1
这种规范化的路径设计保证了唯一性、可追溯性和标准化。
5.4 保存-修改-恢复模式
通过临时修改 PREFIX 环境变量,让 make install 将产物安装到 HNP 路径:
# 保存原始值
sys_prefix=${PREFIX}
# 临时修改为 HNP 路径
export PREFIX=${TREE_INSTALL_HNP_PATH}
# 构建和安装
make clean
make VERBOSE=1
make install
# 恢复原始值
export PREFIX=${sys_prefix}
5.5 双重打包策略
脚本同时生成两种格式的产物:
# 复制 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:
# 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 最终产物目录结构
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 包:
HAP 工程根目录
└── hnp # hnp 根目录
└── arm64-v8a # 设备 ABI 目录
├── python.hnp # hnp 包
└── sub_dir # 子目录(可选)
└── test.hnp
③ 配置 module.json5 文件,在 module 字段下添加 hnpPackages,声明 hnp 包路径及类型:
"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 根目录:
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)范例,可参考社区仓:
https://gitcode.com/OpenHarmonyPCDeveloper/GitNext
https://gitcode.com/OpenHarmonyPCDeveloper/DevBox
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.json中name相同)不可重复安装,后安装的 HAP 需将其改为私有包或卸载前序 HAP
7. HAP 签名流程
HAP 包(含嵌入的 HNP 包)必须经过签名才能在鸿蒙设备上安装运行。
7.1 操作步骤
① 配置签名信息
参考华为官方文档应用/服务签名的"手动签名"章节,准备签名所需配置。
② 下载签名工具
# 从 OpenHarmony 官方仓库下载
wget https://gitcode.com/openharmony/developtools_hapsigner/blob/master/dist/hap-sign-tool.jar
③ 执行签名命令
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++ 代码中通过 execv、execve 执行 HNP 中的二进制:
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 调试访问
# 连接设备
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 段中:
# 手动签名
binary-sign-tool sign -selfSign 1 -inFile my_program -outFile my_program
# 验证签名
llvm-readelf -S hello | grep codesign
# 应输出:.codesign PROGBITS ...(签名段存在)
编译时自动签名:通过封装 ld.lld 链接器,在链接时自动注入 --code-sign 参数:
# 替换 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.json 的 version 与 HPKBUILD 的 pkgver 保持一致:
#!/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.json 或 python -m json.tool 验证 |
| 编译后的二进制无法运行 | ELF 未签名 | 检查 .codesign 段是否存在,配置自动签名 |
深度问答:HNP 命令行程序依赖很多 so 库,安装后能找到吗?
这是一个非常典型的问题。HNP 包中的命令能否找到依赖的 so 库,取决于三个关键因素。
情况一:so 库在同一个 HNP 包内(标准场景)
HNP 包的标准目录结构天然支持多 so 依赖:
command.hnp
├── bin/
│ └── mycommand ← 可执行文件
├── lib/
│ ├── libfoo.so ← 依赖的 so 库
│ └── libbar.so.1
└── hnp.json
关键在于编译时必须设置 $ORIGIN rpath:
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 的协作关系
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 华为开发者官方文档
- 应用/服务手动签名:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/ide-signing-0000001587684945-V5
- 鸿蒙 PC 开发者社区:https://harmonypc.csdn.net/
12.3 社区参考文章
- CSDN 猫哥 - 鸿蒙 PC 三方库移植系列(HNP 概念通俗解读):https://blog.csdn.net/transformer2023/article/details/155594592
- hnpcli 适配 OpenHarmony PC 完整指南(源码编译 hnpcli):https://blog.csdn.net/jianguo888888/article/details/156315773
- 开源鸿蒙 HNP 打包规范揭秘(跨平台范式分析):https://blog.csdn.net/hhh00/article/details/155470477
- 读懂 hnp.json(字段语义详细讲解):https://blog.csdn.net/weixin_45822171/article/details/160085995
- 鸿蒙PC Native软件包开发指南(HAP 嵌入 HNP 官方实践):https://blog.csdn.net/g310773517/article/details/154828607
- 鸿蒙 PC 的 vcpkg 交叉编译 + HNP:https://blog.csdn.net/qq8864/article/details/157426019
- HNP 包的另一个理解:https://blog.csdn.net/m0_57525346/article/details/154846430
12.4 其他参考
- Qt Wiki - Solutions to build 3rdparty for HarmonyOS/OpenHarmony:https://wiki.qt.io/Solutions_to_build_3rdparty_for_HarmonyOS/OpenHarmony
一句话总结 HNP: HNP 是鸿蒙原生应用的标准化打包格式,本质是 ZIP 包,通过
hnp.json描述元数据,用hnpcli工具生成。它不能独立安装,必须嵌入 HAP 应用通过应用市场分发。不以 HNP 形式存在的签名二进制也能直接运行,但打包为 HNP 后可融入鸿蒙的 HAP 应用管理体系,实现原子化安装卸载和生命周期管理。
更多推荐



所有评论(0)