hnpcli 适配 OpenHarmony PC 完整指南

项目概述

什么是 hnpcli?

hnpcli (HarmonyOS Native Package CLI) 是一个用于制作 OpenHarmony Native Package (HNP) 的命令行工具。它是 OpenHarmony 生态系统中用于打包和分发原生应用的重要工具。

适配版本

  • 目标版本: OpenHarmony-v6.0.0.1-Release

  • 架构支持: arm64-v8a

  • 构建系统: CMake

  • 许可证: Apache-2.0

核心功能

  • 📦 打包原生应用为 HNP 格式

  • 🔍 验证 HNP 包完整性

  • 📋 管理 HNP 包元数据

  • 🗜️ 压缩和解压缩 HNP 包

技术架构

依赖关系

hnpcli
├── cJson (JSON 解析库)
├── libboundscheck (边界检查库)
└── zlib_static (压缩库,静态链接)

源码结构

hnpcli 的源码来自 OpenHarmony 的 startup_appspawn 仓库,主要包含以下组件:

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

适配过程详解

步骤 1:配置外部仓库

outerrepo/module.json 中添加 hnpcli 的配置:

{
    "name": "hnpcli",
    "branch": "ohos_6.0.0.1",
    "version": "6.0.0.1",
    "type": "git",
    "url": "https://gitcode.com/OpenHarmonyPCDeveloper/ohos_hnpcli.git"
}

配置说明

  • name: 模块名称,对应目录名

  • branch: Git 分支名,对应适配版本

  • version: 版本号,用于构建和打包

  • type: 源码获取方式,git 表示使用 Git 克隆

  • url: 源码仓库地址

步骤 2:创建 HPKBUILD 文件

outerrepo/hnpcli/HPKBUILD 中定义构建流程:

# 包基本信息
pkgname=hnpcli
pkgver=6.0.0.1
fullpkgver=OpenHarmony-v${pkgver}-Release
pkgrel=0
pkgdesc="hnp打包工具"
url="https://gitcode.com/openharmony/startup_appspawn"
archs=("arm64-v8a")
license=("Apache-2.0")
​
# 依赖声明
depends=("cJson" "libboundscheck" "zlib_static")
makedepends=()
​
# 源码配置
source="https://gitcode.com/openharmony/startup_appspawn.git"
autounpack=false      # 不使用自动解压
downloadpackage=false # 不使用自动下载
​
builddir=$pkgname-${pkgver}
download_and_patch_flag=true
licensefile=${builddir}/LICENSE

关键配置说明

  1. autounpack=false:

    • 因为使用 Git 克隆,不需要自动解压压缩包

    • 源码通过 prepare() 函数中的 git clone 获取

  2. downloadpackage=false:

    • 不使用标准的下载机制

    • 源码通过 Git 直接克隆

  3. depends:

    • 声明运行时依赖

    • Lycium 会自动构建这些依赖库

步骤 3:实现 prepare() 函数

prepare() 函数负责准备构建环境:

prepare() {
    # 检查源码目录是否存在
    if [ -d $builddir ]
    then
        echo ${builddir} is already exist
        rm -rf ${builddir}/$ARCH-build
    else
        if [ "$download_and_patch_flag" == true ]
        then
            # Git 克隆源码
            git clone $source $builddir
            if [ $? -ne 0 ]
            then
                return -1
            fi
            
            # 切换到指定版本标签
            pushd $builddir
                git reset --hard $fullpkgver
                if [ $? -ne 0 ]
                then
                    popd
                    return -2
                fi
            popd
            download_and_patch_flag=false
        fi
    fi
    
    # 创建构建目录
    mkdir -p ${builddir}/$ARCH-build
​
    # 设置交叉编译环境
    if [ $ARCH == "arm64-v8a" ]
    then
        export TARGET_PLATFORM=aarch64-linux-ohos
        setarm64ENV
    else
        echo "${ARCH} not support"
        return -1
    fi
}

关键点

  1. Git 版本控制:

    git clone $source $builddir
    git reset --hard $fullpkgver
    • 克隆整个仓库

    • 切换到指定的版本标签(OpenHarmony-v6.0.0.1-Release

  2. 构建目录管理:

    mkdir -p ${builddir}/$ARCH-build
    • 为每个架构创建独立的构建目录

    • 支持多架构并行构建

  3. 环境变量设置:

    setarm64ENV
    • 设置交叉编译工具链(CC, CXX, AR 等)

    • 设置编译选项(CFLAGS, CXXFLAGS, LDFLAGS)

步骤 4:实现 build() 函数

build() 函数执行实际的编译过程:

build() {
    # 设置构建目录环境变量(供 CMakeLists.txt 使用)
    export HNPCLI_BUILD_DIR=${builddir}
    
    # 配置 CMake
    ${CMAKE} "$@" \
        -DCMAKE_PREFIX_PATH="$LYCIUM_ROOT/usr/cJson/$ARCH:$LYCIUM_ROOT/usr/libboundscheck/$ARCH:$LYCIUM_ROOT/usr/zlib_static/$ARCH" \
        -B$ARCH-build -S./ -L
​
    # 执行编译
    pushd ${ARCH}-build
        ${MAKE} VERBOSE=1
    ret=$?
    popd
    return $ret
}

关键点

  1. 环境变量传递:

    export HNPCLI_BUILD_DIR=${builddir}
    • CMakeLists.txt 需要使用源码目录路径

    • 通过环境变量传递给 CMake

  2. 依赖路径配置:

    -DCMAKE_PREFIX_PATH="$LYCIUM_ROOT/usr/cJson/$ARCH:$LYCIUM_ROOT/usr/libboundscheck/$ARCH:$LYCIUM_ROOT/usr/zlib_static/$ARCH"
    • 告诉 CMake 在哪里查找依赖库

    • 使用冒号分隔多个路径

  3. CMake 参数:

    • -B$ARCH-build: 指定构建目录

    • -S./: 指定源码目录(当前目录)

    • -L: 列出所有 CMake 变量(调试用)

步骤 5:CMakeLists.txt 配置

CMakeLists.txt 定义了如何构建 hnpcli:

cmake_minimum_required(VERSION 3.10)
project(hnpcli)
​
set(CMAKE_C_STANDARD 17)
add_definitions(-DHNP_CLI)
​
# 查找依赖库
find_library(MYBOUNDS_CHECK libboundscheck.a)
find_library(MYJSON libcjson.a)
find_library(MYZLIB libz_static.a)
​
# 查找头文件目录
find_path(BOUNDS_CHECK_INCLUDE_DIR securec.h)
find_path(CJSON_INCLUDE_DIR cjson/cJSON.h)
find_path(ZLIB_INCLUDE_DIR zlib.h)
​
# 获取源码路径
set(STARTUP_APPSPAWN_PATH $ENV{HNPCLI_BUILD_DIR})
​
# 设置包含目录
include_directories(
    ${CMAKE_CURRENT_SOURCE_DIR}/${STARTUP_APPSPAWN_PATH}/service/hnp/base
    ${CMAKE_CURRENT_SOURCE_DIR}/${STARTUP_APPSPAWN_PATH}/service/hnp/pack/src
    ${CMAKE_CURRENT_SOURCE_DIR}/${STARTUP_APPSPAWN_PATH}/service/hnp/pack/include
    ${CMAKE_CURRENT_SOURCE_DIR}/${STARTUP_APPSPAWN_PATH}/service/hnp/base
    ${BOUNDS_CHECK_INCLUDE_DIR}
    ${ZLIB_INCLUDE_DIR}
    ${CJSON_INCLUDE_DIR}/cjson
)
​
# 添加可执行文件
add_executable(hnpcli
    ${CMAKE_CURRENT_SOURCE_DIR}/${STARTUP_APPSPAWN_PATH}/service/hnp/base/hnp_file.c
    ${CMAKE_CURRENT_SOURCE_DIR}/${STARTUP_APPSPAWN_PATH}/service/hnp/base/hnp_json.c
    ${CMAKE_CURRENT_SOURCE_DIR}/${STARTUP_APPSPAWN_PATH}/service/hnp/base/hnp_log.c
    ${CMAKE_CURRENT_SOURCE_DIR}/${STARTUP_APPSPAWN_PATH}/service/hnp/base/hnp_zip.c
    ${CMAKE_CURRENT_SOURCE_DIR}/${STARTUP_APPSPAWN_PATH}/service/hnp/hnpcli_main.c
    ${CMAKE_CURRENT_SOURCE_DIR}/${STARTUP_APPSPAWN_PATH}/service/hnp/pack/src/hnp_pack.c
)
​
# 链接库
target_link_libraries(hnpcli
    ${MYBOUNDS_CHECK}
    ${MYJSON}
    ${MYZLIB}
)
​
# 安装规则
install(TARGETS hnpcli
    DESTINATION bin
)

关键点

  1. 依赖库查找:

    find_library(MYBOUNDS_CHECK libboundscheck.a)
    find_library(MYJSON libcjson.a)
    find_library(MYZLIB libz_static.a)
    • 查找静态库文件

    • 通过 CMAKE_PREFIX_PATH 指定的路径搜索

  2. 源码路径处理:

    set(STARTUP_APPSPAWN_PATH $ENV{HNPCLI_BUILD_DIR})
    • 从环境变量获取源码目录

    • 因为源码在 Git 仓库的子目录中

  3. 源文件路径:

    ${CMAKE_CURRENT_SOURCE_DIR}/${STARTUP_APPSPAWN_PATH}/service/hnp/base/hnp_file.c
    • 使用完整路径引用源文件

    • CMakeLists.txt 在 outerrepo/hnpcli/ 目录

    • 源文件在 hnpcli-6.0.0.1/service/hnp/ 目录

步骤 6:实现 package() 函数

package() 函数安装编译产物:

package() {
    pushd $ARCH-build
        $MAKE VERBOSE=1 install PREFIX=$LYCIUM_ROOT/usr/$pkgname/$ARCH >> $buildlog 2>&1
        ret=$?
    popd
    return $ret
}

说明

  • 使用 CMake 的 install 目标

  • 安装到 lycium/usr/hnpcli/arm64-v8a/bin/hnpcli

  • 日志输出到构建日志文件

步骤 7:实现 archive() 函数

archive() 函数生成最终的打包产物:

archive() {
    # 创建输出目录
    mkdir -p ${LYCIUM_ROOT}/output/$ARCH
    
    # 创建 tar.gz 归档
    pushd $LYCIUM_ROOT/usr/$pkgname/$ARCH
        tar -zvcf ${LYCIUM_ROOT}/output/$ARCH/${pkgname}_${pkgver}.tar.gz *
    popd
    
    # 复制 HNP 配置文件
    cp hnp.json $LYCIUM_ROOT/usr/$pkgname/$ARCH
    
    # 生成 HNP 包
    ${HNP_TOOL} pack -i ${LYCIUM_ROOT/usr/$pkgname/$ARCH -o ${LYCIUM_ROOT}/output/$ARCH/
}

说明

  • 生成两种格式的包:

    1. tar.gz: 二进制归档包

    2. .hnp: HarmonyOS Native Package

步骤 8:配置 hnp.json

hnp.json 定义 HNP 包的元数据:

{
    "type": "hnp-config",
    "name": "hnpcli",
    "version": "6.0.0.1",
    "install": {}
}

字段说明

  • type: 固定为 "hnp-config"

  • name: 包名称,必须与 pkgname 一致

  • version: 版本号,必须与 pkgver 一致

  • install: 安装规则(空对象表示安装所有文件)

构建流程

完整构建命令

cd lycium
./build.sh hnpcli

构建过程详解


开始构建

读取 module.json

Git 克隆源码

切换到指定版本

构建依赖库

cJson

libboundscheck

zlib_static

准备构建环境

配置 CMake

编译 hnpcli

安装到usr目录

生成tar.gz

生成hnp包

完成

构建日志示例

Build OS Darwin
OHOS_SDK=/Users/jianguo/Library/OpenHarmony/Sdk/20
CLANG_VERSION=15.0.4
Start building hnpcli 6.0.0.1!
Compileing OpenHarmony arm64-v8a hnpcli 6.0.0.1 libs...
hnpcli-6.0.0.1 is already exist
-- The C compiler identification is Clang 15.0.4
-- The CXX compiler identification is Clang 15.0.4
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: /Users/jianguo/Library/OpenHarmony/Sdk/20/native/llvm/bin/aarch64-linux-ohos-clang - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- BOUNDS_CHECK library found at: /Users/jianguo/Desktop/harmony/tpc/lycium_plusplus/lycium/usr/libboundscheck/arm64-v8a/lib/libboundscheck.a
-- CJSON library found at: /Users/jianguo/Desktop/harmony/tpc/lycium_plusplus/lycium/usr/cJson/arm64-v8a/lib/libcjson.a
-- ZLIB library found at: /Users/jianguo/Desktop/harmony/tpc/lycium_plusplus/lycium/usr/zlib_static/arm64-v8a/lib/libz_static.a
-- Configuring done
-- Generating done
-- Build files have been written to: arm64-v8a-build
[ 14%] Building C object CMakeFiles/hnpcli.dir/hnpcli-6.0.0.1/service/hnp/base/hnp_file.c.o
[ 28%] Building C object CMakeFiles/hnpcli.dir/hnpcli-6.0.0.1/service/hnp/base/hnp_json.c.o
[ 42%] Building C object CMakeFiles/hnpcli.dir/hnpcli-6.0.0.1/service/hnp/base/hnp_log.c.o
[ 57%] Building C object CMakeFiles/hnpcli.dir/hnpcli-6.0.0.1/service/hnp/base/hnp_zip.c.o
[ 71%] Building C object CMakeFiles/hnpcli.dir/hnpcli-6.0.0.1/service/hnp/hnpcli_main.c.o
[ 85%] Building C object CMakeFiles/hnpcli.dir/hnpcli-6.0.0.1/service/hnp/pack/src/hnp_pack.c.o
[100%] Linking C executable hnpcli
[100%] Built target hnpcli
Install the project...
-- Install configuration: ""
-- Installing: /Users/jianguo/Desktop/harmony/tpc/lycium_plusplus/lycium/usr/hnpcli/arm64-v8a/bin/hnpcli
Build hnpcli 6.0.0.1 end!
ALL JOBS DONE!!!

构建产物

目录结构

lycium/
├── output/
│   └── arm64-v8a/
│       ├── hnpcli.hnp              # HNP 归档包
│       └── hnpcli_6.0.0.1.tar.gz  # 二进制归档包
└── usr/
    └── hnpcli/
        └── arm64-v8a/
            ├── bin/
            │   └── hnpcli          # 可执行文件
            └── hnp.json             # HNP 配置文件

产物说明

1. 可执行文件
$ file lycium/usr/hnpcli/arm64-v8a/bin/hnpcli
hnpcli: ELF 64-bit LSB pie executable, ARM aarch64, version 1 (SYSV), dynamically linked, interpreter /lib/ld-musl-aarch64.so.1, with debug_info, not stripped

特点

  • ARM64 架构

  • 动态链接到 musl C 库

  • 包含调试信息

2. tar.gz 归档包
$ tar -tzf lycium/output/arm64-v8a/hnpcli_6.0.0.1.tar.gz
bin/hnpcli
hnp.json

用途

  • 二进制分发

  • 手动安装

  • 备份和归档

3. HNP 包
$ file lycium/output/arm64-v8a/hnpcli.hnp
hnpcli.hnp: Zip archive data, at least v2.0 to extract, compression method=deflate

用途

  • HarmonyOS 系统直接安装

  • 包管理工具使用

  • 应用分发

关键技术点

1. Git 源码管理

为什么使用 Git 而不是压缩包?

  • ✅ 源码在大型仓库的子目录中(startup_appspawn)

  • ✅ 需要精确的版本控制(特定标签)

  • ✅ 便于后续更新和维护

  • ✅ 支持补丁和修改

实现方式

# 克隆仓库
git clone https://gitcode.com/openharmony/startup_appspawn.git hnpcli-6.0.0.1

# 切换到指定版本
cd hnpcli-6.0.0.1
git reset --hard OpenHarmony-v6.0.0.1-Release

2. 环境变量传递

问题:CMakeLists.txt 需要知道源码目录路径

解决方案:通过环境变量传递

# HPKBUILD 中
export HNPCLI_BUILD_DIR=${builddir}

# CMakeLists.txt 中
set(STARTUP_APPSPAWN_PATH $ENV{HNPCLI_BUILD_DIR})

3. 依赖路径配置

问题:CMake 需要找到依赖库

解决方案:使用 CMAKE_PREFIX_PATH

-DCMAKE_PREFIX_PATH="$LYCIUM_ROOT/usr/cJson/$ARCH:$LYCIUM_ROOT/usr/libboundscheck/$ARCH:$LYCIUM_ROOT/usr/zlib_static/$ARCH"

工作原理

  • CMake 会在这些路径下搜索 lib/include/ 目录

  • find_library()find_path() 会自动搜索

4. 静态库链接

为什么使用静态库?

  • ✅ 减少运行时依赖

  • ✅ 简化部署

  • ✅ 提高兼容性

链接方式

target_link_libraries(hnpcli
    ${MYBOUNDS_CHECK}  # libboundscheck.a
    ${MYJSON}          # libcjson.a
    ${MYZLIB}          # libz_static.a
)

常见问题与解决方案

问题 1:Git 克隆失败

症状

git clone $source $builddir
ERROR: Failed to clone repository

解决方案

  1. 检查网络连接

  2. 验证仓库 URL

  3. 检查 Git 凭据

# 手动测试
git clone https://gitcode.com/openharmony/startup_appspawn.git test

问题 2:版本标签不存在

症状

git reset --hard OpenHarmony-v6.0.0.1-Release
fatal: Could not resolve HEAD to a revision

解决方案

  1. 检查标签是否存在

  2. 使用正确的标签格式

# 列出所有标签
git tag -l

# 检查特定标签
git show-ref --tags | grep 6.0.0.1

问题 3:依赖库未找到

症状

CMake Error: Could not find libboundscheck.a

解决方案

  1. 确保依赖库已构建

  2. 检查 CMAKE_PREFIX_PATH

  3. 验证库文件路径

# 检查依赖库是否存在
ls -lh lycium/usr/libboundscheck/arm64-v8a/lib/

# 验证 CMake 路径
cmake -DCMAKE_PREFIX_PATH="..." -L | grep CMAKE_PREFIX_PATH

问题 4:源文件路径错误

症状

CMake Error: Cannot find source file: hnp_file.c

解决方案

  1. 检查 HNPCLI_BUILD_DIR 环境变量

  2. 验证源文件路径

  3. 确认 Git 克隆成功

# 检查环境变量
echo $HNPCLI_BUILD_DIR

# 验证源文件
ls -lh hnpcli-6.0.0.1/service/hnp/base/hnp_file.c

问题 5:HNP 打包失败

症状

[ERROR][HNP] source dir path is invalid

解决方案

  1. 检查产物目录是否存在

  2. 验证 hnp.json 文件

  3. 确认 HNP_TOOL 环境变量

# 检查产物目录
ls -lh lycium/usr/hnpcli/arm64-v8a/

# 验证 hnp.json
cat lycium/usr/hnpcli/arm64-v8a/hnp.json

# 检查 HNP_TOOL
echo $HNP_TOOL

最佳实践

1. 版本管理

  • ✅ 使用 Git 标签管理版本

  • ✅ 在 HPKBUILD 中明确指定版本

  • ✅ 保持版本号一致性

2. 依赖管理

  • ✅ 明确声明所有依赖

  • ✅ 使用静态库减少运行时依赖

  • ✅ 验证依赖库版本兼容性

3. 构建优化

  • ✅ 使用并行编译加速构建

  • ✅ 缓存构建产物

  • ✅ 清理中间文件

4. 测试验证

  • ✅ 验证可执行文件格式

  • ✅ 测试基本功能

  • ✅ 检查产物完整性

使用示例

构建 hnpcli

cd lycium
./build.sh hnpcli

使用 hnpcli 打包应用

# 设置环境变量
export HNP_TOOL=lycium/usr/hnpcli/arm64-v8a/bin/hnpcli

# 打包应用
$HNP_TOOL pack -i /path/to/app -o /path/to/output

验证 HNP 包

# 查看包信息
$HNP_TOOL info /path/to/app.hnp

# 解压包
$HNP_TOOL unpack -i /path/to/app.hnp -o /path/to/output

总结

适配要点

  1. 源码获取:使用 Git 克隆而非压缩包

  2. 版本控制:通过 Git 标签精确控制版本

  3. 依赖管理:明确声明并自动构建依赖

  4. 路径处理:通过环境变量传递源码路径

  5. 静态链接:使用静态库简化部署

技术亮点

  • ✅ 灵活的源码管理方式

  • ✅ 完善的依赖处理机制

  • ✅ 标准化的构建流程

  • ✅ 多样化的产物格式

适用场景

  • OpenHarmony 原生应用打包

  • 命令行工具开发

  • 系统工具构建

  • 依赖 CMake 的项目

适配版本: OpenHarmony-v6.0.0.1-Release 关键词: hnpcli, OpenHarmony, HNP, 打包工具, CMake, 交叉编译, Lycium

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐