前言
很多新手拿到一个三方库想适配到鸿蒙PC,面对空白的 HPKBUILD 文件不知道从何下手。本文以 MediaInfo CLI 为例,不直接给最终代码,而是带你走一遍真实的思考过程:从搭骨架、填函数、跑构建、排错误,最终得到一个健壮的脚本。读完本文,你应该能为其他三方库独立写出规范的 HPKBUILD。
欢迎加入开源鸿蒙PC社区:https://harmonypc.csdn.net/
HPKBUILD介绍
lycium_plusplus(简称 lycium)是鸿蒙PC官方推荐的 C/C++ 三方库标准化构建框架,核心定位是在 x86_64 Linux 开发主机上,为 ARM64 架构的鸿蒙PC设备提供一站式交叉编译能力。
框架屏蔽了交叉编译的底层技术壁垒,包括:CPU 架构差异(x86_64 ↔ ARM64)、操作系统差异(Linux ↔ 鸿蒙PC)、C 标准库差异(glibc ↔ musl libc)、编译工具链差异(gcc ↔ clang)。开发者无需关注底层交叉编译细节,只需编写一个HPKBUILD 配置脚本,声明库名称、源码地址、编译规则与打包逻辑,框架即可自动完成全流程构建。
HPKBUILD 本质是遵循框架规范的 bash 脚本,仅需实现框架约定的 5 个核心函数,lycium 会按照固定生命周期顺序自动调用,完成编译全流程:无需深入理解 lycium 框架源码,只需掌握这 5 个标准函数的用法,即可快速完成鸿蒙PC 三方库的适配与编译。
bash
prepare() -> build() -> check() -> package() -> archive()
鸿蒙PC C/C++ 三方库移植适配资源表
已整理汇集社区鸿蒙PC C/C++ 三方库移植适配核心文档,结合本文 MediaInfo 实战案例配套学习,可快速掌握三方库完整适配流程,轻松上手移植开发
| 资源类型 | 描述 | 链接 |
|---|---|---|
| 三方库移植适配指导说明 | 介绍如何快速高效的移植一个 C/C++ 三方库到 OpenHarmony 上 | 👉 点击查看 |
| lycium 交叉编译框架 | lycium 交叉编译框架讲解 | 👉 点击查看 |
| 三方库适配模板文件 | 三方库适配模板文件可直接从仓库拷贝使用 | 👉 点击查看 |
| 标准化鸿蒙化适配方案 | 社区老师编写的通用适配方案,参考价值极强 | 👉 点击查看 |
MediaInfo适配前的调研
MediaInfo 三方库适配前期调研阶段,可借助 Vibe Code 高效完成全流程分析:只需将 MediaInfo 源码工程导入 Vibe Code,依托其代码检索、文件结构解析能力,快速自动识别项目 CMake、autotools 等多套构建系统;通过关键词全局检索一键筛选 CMake 编译配置选项,精准提取 BUILD_ZENLIB、BUILD_MEDIAINFOLIB 等关键编译开关;同时智能梳理项目模块层级与库依赖关系,自动识别 ZenLib、MediaInfoLib、zlib 等依赖组件,并可结合 鸿蒙PC系统环境快速判断 SDK 自带库与需手动适配的三方库,大幅缩减人工源码查阅、命令行检索与依赖梳理的时间,为后续 HPKBUILD 编写提供精准的调研依据
HPKBUILD 官方模板
HPKBUILD是Shell 编写的标准化编译构建脚本,也是 C/C++库鸿蒙化的核心,文件定义了:库的基础元信息、源码下载地址、交叉编译环境配置、编译构建逻辑、打包安装路径、测试准备、环境清理等所有核心流程。 鸿蒙官方提供了标准模板,同时业界已有大量实战案例(如 fftw3、tassl),所有适配均基于此模板修改,模板通用,案例可复用,如下是官方标准 HPKBUILD 模板
bash
# This is an example HPKBUILD file. Use this as a start to creating your own,
# and remove these comments.
# NOTE: Please fill out the license field for your package! If it is unknown,
# then please put 'unknown'.
# Contributor: Your Name <youremail@domain.com>
# Maintainer: Your Name <youremail@domain.com>
pkgname=NAME # 库名,必填
pkgver=VERSION # 库版本,必填
pkgrel=0 # 发布号,固定为0即可
pkgdesc=""# 库功能描述,必填
url=""# 库官网地址,必填
archs=("armeabi-v7a""arm64-v8a") # 鸿蒙主流CPU架构,固定这两个即可
license=() # 开源协议,必填
depends=() # 运行依赖库,无则留空,依赖库需适配同架构
makedepends=() # 编译依赖工具,无则留空
source="https://downloads.sourceforge.net/$pkgname/$pkgname-$pkgver.tar.gz"# 源码下载链接
downloadpackage=true# 是否自动下载源码包,默认true
autounpack=true# 是否自动解压源码包,默认true
buildtools= # 编译方式,支持cmake/configure/make,必填
builddir= # 源码解压后的目录名
packagename=$builddir.tar.gz # 源码包文件名
# 编译前环境准备:创建目录、设置环境变量、源码预处理
prepare() {
cd$builddir
cd${OLDPWD}
}
# 核心编译逻辑:交叉编译核心步骤,架构判断、编译命令执行
build() {
cd$builddir
${OHOS_SDK}/native/build-tools/cmake/bin/cmake "$@" -DOHOS_ARCH=$ARCH -B$ARCH-build -S./ -L
make -j4 -C $ARCH-build
ret=$?
cd$OLDPWD
return$ret
}
# 打包安装:将编译产物安装到鸿蒙指定目录
package() {
cd$builddir
make -C $ARCH-build install
cd$OLDPWD
}
# 测试说明:鸿蒙设备端测试的前置说明
check() {
echo"The test must be on an 鸿蒙PC device!"
}
# 环境清理:编译完成后清理构建产物
cleanbuild() {
rm -rf ${PWD}/$builddir
}
MediaInfo 适配 HPKBUILD案例
前提准备,需要提前准备源码放在Projects目录下 MediaInfo源码GitHub地址: https://github.com/MediaArea/MediaInfo
bash
#!/bin/bash
# 编译构建脚本:MediaInfo CLI 工具 (适配 鸿蒙PC 鸿蒙系统)
# 贡献者/维护者信息
# Contributor: Your Name <youremail@domain.com>
# Maintainer: Your Name <youremail@domain.com>
# 基础包配置
# 包名
pkgname=mediainfo
# 包版本
pkgver=25.03
# 包发布版本号
pkgrel=0
# 包描述:音视频文件技术信息和标签数据查看工具(命令行版)
pkgdesc="A convenient unified display of the most relevant technical and tag data for video and audio files (CLI version)"
# 项目源码地址
url="https://github.com/MediaArea/MediaInfo"
# 支持的架构:32位ARM / 64位ARM
archs=("armeabi-v7a" "arm64-v8a")
# 开源协议
license=("BSD-2-Clause")
# 运行依赖(无)
depends=()
# 编译依赖:需要cmake
makedepends=("cmake")
# 源码配置
# 使用本地源码,不自动解压
autounpack=false
# 不自动下载源码包
downloadpackage=false
# 编译工具
buildtools="cmake"
# 本地源码路径(从环境变量 LYCIUM_ROOT 指向项目目录)
srcpath="${LYCIUM_ROOT}/../Projects/MediaInfo"
# 编译目录名称 = 包名+版本
builddir=${pkgname}-${pkgver}
# 准备阶段:复制源码、打补丁
prepare() {
# 判断本地源码目录是否存在
if [ -d "$srcpath" ]; then
# 创建编译目录
mkdir -p "$builddir"
# 复制本地所有源码到编译目录
cp -rf "$srcpath"/* "$builddir/"
# 删除备份文件 .bak
find "$builddir" -name "*.bak" -type f -delete 2>/dev/null || true
# 删除 Windows 系统遗留的区域标识符文件
find "$builddir" -name "*:Zone.Identifier" -type f -delete 2>/dev/null || true
# 如果存在musl兼容补丁,则自动打补丁
if [ -f "../mediainfo-zenlib-musl.patch" ]; then
cd "$builddir"
patch -p1 < "../mediainfo-zenlib-musl.patch"
cd "$OLDPWD"
fi
else
# 源码不存在则报错退出
echo "ERROR: Source not found at $srcpath"
exit 1
fi
}
# 编译阶段:cmake + make
build() {
# 进入编译目录
cd "$builddir"
# cmake 配置
cmake "$@" \
-B"$ARCH-build" \ # 按架构区分构建目录
-S./Project/CMake/CLI/ \ # CLI 版源码路径
-D CMAKE_BUILD_TYPE=Release \ # 编译 Release 版本
-D CMAKE_INSTALL_PREFIX="/usr" \ # 安装路径前缀
-D BUILD_ZENLIB=ON \ # 编译依赖库 ZenLib
-D BUILD_MEDIAINFOLIB=ON \ # 编译核心库 MediaInfoLib
-D BUILD_ZLIB=ON \ # 编译 zlib
-D ZLIB_BUILD_SHARED=OFF \ # zlib 编译为静态库
-D ZLIB_BUILD_TESTING=OFF \ # 不编译 zlib 测试程序
-D MediaInfoLib_BUILD_STATIC=ON # MediaInfoLib 编译为静态库
# 多线程编译(4线程)
make -j4 -C "$ARCH-build"
# 记录 make 执行结果
ret=$?
# 返回原目录
cd "$OLDPWD"
# 返回编译结果状态码
return $ret
}
# 安装阶段:将编译产物安装到指定目录
package() {
cd "$builddir"
# 安装到目标架构目录(用于后续打包)
make -C "$ARCH-build" install DESTDIR="${LYCIUM_ROOT}/usr/${pkgname}/${ARCH}"
cd "$OLDPWD"
}
# 测试阶段:提示需在真机/设备上运行
check() {
echo "The test must be on an OpenHarmony device!"
}
# 归档打包阶段:生成 .tar.gz + .hnp 鸿蒙安装包
archive() {
# 指定 hnp 工具路径(鸿蒙 SDK 工具链)
export HNP_TOOL="${HNP_TOOL:-${OHOS_SDK}/toolchains/hnpcli}"
# 创建输出目录
mkdir -p "${LYCIUM_ROOT}/output/${ARCH}"
# 进入安装目录并打包成 tar.gz
pushd "${LYCIUM_ROOT}/usr/${pkgname}/${ARCH}" > /dev/null 2>&1
tar -zcf "${LYCIUM_ROOT}/output/${ARCH}/${pkgname}_${pkgver}.tar.gz" .
echo "Archive completed: ${LYCIUM_ROOT}/output/${ARCH}/${pkgname}_${pkgver}.tar.gz"
popd > /dev/null 2>&1
# 如果 hnpcli 工具存在,则生成鸿蒙 .hnp 包
if [ -f "${HNP_TOOL}" ]; then
cp "${LYCIUM_ROOT}/../thirdparty/${pkgname}/hnp.json" "${LYCIUM_ROOT}/usr/${pkgname}/${ARCH}/"
${HNP_TOOL} pack -i "${LYCIUM_ROOT}/usr/${pkgname}/${ARCH}" -o "${LYCIUM_ROOT}/output/${ARCH}/"
echo "Archive completed: ${LYCIUM_ROOT}/output/${ARCH}/${pkgname}.hnp"
else
# 缺少工具则跳过 hnp 打包
echo "Warning: hnpcli not found at ${HNP_TOOL}, skipping HNP generation"
fi
}
# 清理构建目录
cleanbuild() {
rm -rf "${PWD}/$builddir"
}
对比分析:MediaInfo HPKBUILD 相对于官方模板的代码增加
官方模板是通用骨架,只包含最基本的构建逻辑。MediaInfo 的 HPKBUILD 在官方模板基础上,针对实际项目需求做了必要的扩展。下面按函数逐个对比分析增加了哪些代码,以及为什么要增加。
prepare() 函数的增加项
官方模板:
bash
prepare() {
cd $builddir
cd ${OLDPWD}
}
MediaInfo 版本:
bash
# 准备构建环境函数:复制源码、清理冗余文件、应用补丁
prepare() {
# 判断源码目录是否存在
if [ -d "$srcpath" ]; then
# 创建构建目录
mkdir -p "$builddir"
# 将源码完整复制到构建目录
cp -rf "$srcpath"/* "$builddir/"
# 清理备份文件(.bak),忽略删除失败的情况
find "$builddir" -name "*.bak" -type f -delete 2>/dev/null || true
# 清理Windows系统产生的冗余标识文件,忽略删除失败
find "$builddir" -name "*:Zone.Identifier" -type f -delete 2>/dev/null || true
# 检查适配补丁文件是否存在,存在则执行补丁应用
if [ -f "../mediainfo-zenlib-musl.patch" ]; then
# 进入构建目录
cd "$builddir"
# 应用musl兼容补丁(适配鸿蒙PC的musl libc)
patch -p1 < "../mediainfo-zenlib-musl.patch"
# 返回原工作目录
cd "$OLDPWD"
fi
else
# 源码不存在,打印错误并退出
echo "ERROR: Source not found at $srcpath"
exit 1
fi
}
增加内容分析:
| 增加代码 | 作用 | 必要性 |
|---|---|---|
| srcpath="${LYCIUM_ROOT}/.../Projects/MediaInfo" | 定义本地源码路径 | 必需。官方模板用远程下载,MediaInfo 使用本地源码 |
| autounpack=false和downloadpackage=false | 禁用自动下载和解压 | 必需。配合本地源码使用 |
| mkdir -p "$builddir" | 创建构建目录 | 必需。官方模板由框架自动解压创建目录 |
| cp -rf " s r c p a t h " / ∗ " srcpath"/* " srcpath"/∗"builddir/" | 复制本地源码到构建目录 | 必需。替代官方模板的自动下载解压 |
| find ... -name "*.bak" -delete | 清理备份文件 | 建议。避免 Windows 传输产生的垃圾文件干扰构建 |
| find ... -name "*:Zone.Identifier" -delete | 清理 Windows 标识文件 | 建议。Windows 复制过来的文件会带此后缀,可能导致编译异常 |
| patch -p1 < ".../mediainfo-zenlib-musl.patch" | 应用 musl 兼容性补丁 | 核心必需。修复 ZenLib 的 pthread_cancel 在 musl libc 下不可用的问题 |
| if [ -d "$srcpath" ] ... else ... exit 1 | 源码存在性检查 | 建议。提前报错,避免后续编译出现更难以定位的错误 |
总结:prepare() 从官方模板的 2 行扩展到 16 行,核心变化是本地源码复制和musl 补丁应用
build() 函数的增加项
官方模板:
bash
build() {
cd $builddir
${OHOS_SDK}/native/build-tools/cmake/bin/cmake "$@" -DOHOS_ARCH=$ARCH -B$ARCH-build -S./ -L
make -j4 -C $ARCH-build
ret=$?
cd $OLDPWD
return $ret
}
MediaInfo 版本:
bash
# 编译函数:执行CMake配置与Make编译,完成库的构建
build() {
# 进入源码构建目录
cd "$builddir"
# CMake配置:指定编译参数、依赖开关、安装路径,生成编译文件
cmake "$@" \
-B"$ARCH-build" \ # 指定架构专属构建目录
-S./Project/CMake/CLI/ \ # 指定CMake源码路径(CLI工具)
-D CMAKE_BUILD_TYPE=Release \ # 编译Release发布版本
-D CMAKE_INSTALL_PREFIX="/usr" \ # 指定安装根路径为/usr
-D BUILD_ZENLIB=ON \ # 自动构建依赖ZenLib
-D BUILD_MEDIAINFOLIB=ON \ # 自动构建依赖MediaInfoLib
-D BUILD_ZLIB=ON \ # 自动构建依赖zlib
-D ZLIB_BUILD_SHARED=OFF \ # zlib编译为静态库
-D ZLIB_BUILD_TESTING=OFF \ # 关闭zlib测试用例
-D MediaInfoLib_BUILD_STATIC=ON # MediaInfoLib编译为静态库
# 多线程编译(-j4),进入架构构建目录执行make
make -j4 -C "$ARCH-build"
# 保存make命令的退出状态码(0=成功,非0=失败)
ret=$?
# 返回执行前的工作目录
cd "$OLDPWD"
# 将编译结果状态返回给框架
return $ret
}
增加内容分析:
| 增加代码 | 作用 | 必要性 |
|---|---|---|
| -S./Project/CMake/CLI/ | 指定 CMakeLists.txt 位置 | 必需。MediaInfo 的 CMakeLists.txt 不在源码根目录,而是在 Project/CMake/CLI/ 子目录中 |
| -D CMAKE_BUILD_TYPE=Release | 设置发布模式 | 建议。Release 模式生成优化后的产物,体积更小、运行更快 |
| -D CMAKE_INSTALL_PREFIX="/usr" | 设置安装前缀 | 建议。确保 make install 安装到正确的目录结构 |
| -D BUILD_ZENLIB=ON | 同时构建 ZenLib | 必需。MediaInfo CLI 依赖 ZenLib,此选项让 CMake 自动处理 |
| -D BUILD_MEDIAINFOLIB=ON | 同时构建 MediaInfoLib | 必需。MediaInfo CLI 依赖 MediaInfoLib |
| -D BUILD_ZLIB=ON | 同时构建 zlib | 建议。确保 zlib 能找到并正确链接 |
| -D ZLIB_BUILD_SHARED=OFF | 静态链接 zlib | 建议。避免运行时依赖动态库,产物更独立 |
| -D ZLIB_BUILD_TESTING=OFF | 跳过 zlib 测试 | 建议。减少构建时间 |
| -D MediaInfoLib_BUILD_STATIC=ON | 静态链接 MediaInfoLib | 建议。避免运行时依赖问题 |
| cmake 替代绝对路径 | 使用环境 PATH 中的 cmake | 可选。lycium 已配置 PATH,两种写法均可 |
总结:build() 的核心增加是指定 CMakeLists.txt 子目录位置和MediaInfo 特有的 7 个 CMake 选项。这些选项决定了依赖库如何构建、如何链接,是适配成功的关键。
package() 函数的增加项
官方模板:
bash
package() {
cd $builddir
make -C $ARCH-build install
cd $OLDPWD
}
MediaInfo 版本:
bash
# 安装打包函数:将编译好的文件安装到框架指定的输出目录
package() {
# 进入构建目录
cd "$builddir"
# 执行安装命令:将编译产物安装到 Lycium 框架的库输出目录
# DESTDIR 指定最终安装根目录,用于后续打包
make -C "$ARCH-build" install DESTDIR="${LYCIUM_ROOT}/usr/${pkgname}/${ARCH}"
# 返回原工作目录
cd "$OLDPWD"
}
增加内容分析:
| 增加代码 | 作用 | 必要性 |
|---|---|---|
| DESTDIR=" L Y C I U M R O O T / u s r / {LYCIUM_ROOT}/usr/ LYCIUMROOT/usr/{pkgname}/${ARCH}" | 指定安装目标目录 | 必需,官方模板的 make install 会直接安装到系统目录,加上 DESTDIR 后安装到 lycium 指定的产物目录,便于后续打包 |
补充说明:
官方模板没有 DESTDIR,这在某些场景下会导致产物安装到错误位置。lycium 框架期望产物统一放在
bash
${LYCIUM_ROOT}/usr/<pkgname>/<ARCH>/
目录中,所以 DESTDIR 是实际适配中的必要补充。
check() 函数
官方模板与 MediaInfo 版本完全一致:
bash
check() {
echo "The test must be on an OpenHarmony device!"
}
说明:check() 是占位函数,因为交叉编译的产物无法在 x86 构建机上运行。真机测试需要在 鸿蒙PC设备上进行,所以此处无需修改。
archive() 函数(可选)
官方模板:无此函数。
说明:官方模板只负责编译和安装,不包含打包逻辑。lycium 框架会自动处理基础打包,但如果需要生成 HNP 格式(鸿蒙PC原生安装包)或自定义 tar.gz 命名规则,则需要自行实现 archive() 函数。
可选补充:如果项目需要 HNP 打包,可在 HPKBUILD 中根据自己需求增加:
bash
# 归档打包函数:将编译完成的产物打包成压缩包,并生成HNP格式安装包
archive() {
# 定义HNP打包工具路径,优先使用环境变量,否则使用SDK默认路径
export HNP_TOOL="${HNP_TOOL:-${OHOS_SDK}/toolchains/hnpcli}"
# 创建输出目录,用于存放最终的打包文件
mkdir -p ${LYCIUM_ROOT}/output/$ARCH
# 进入编译产物目录,静默输出
pushd ${LYCIUM_ROOT}/usr/${pkgname}/${ARCH} > /dev/null 2>&1
# 将编译产物打包为tar.gz压缩包,存放至输出目录
tar -zcf ${LYCIUM_ROOT}/output/$ARCH/${pkgname}_${pkgver}.tar.gz .
# 返回原工作目录,静默输出
popd > /dev/null 2>&1
# 判断HNP工具是否存在,存在则执行HNP打包
if [ -f "${HNP_TOOL}" ]; then
# 复制HNP配置文件到产物目录
cp ${LYCIUM_ROOT}/../thirdparty/${pkgname}/hnp.json ${LYCIUM_ROOT}/usr/${pkgname}/${ARCH}/
# 调用HNP工具生成OpenHarmony标准安装包
${HNP_TOOL} pack -i ${LYCIUM_ROOT}/usr/${pkgname}/${ARCH} -o ${LYCIUM_ROOT}/output/$ARCH/
fi
}
变量声明区的增加项
| 增加变量 | 作用 | 必要性 |
|---|---|---|
| srcpath | 本地源码路径 | 使用本地源码时必需 |
| builddir= p k g n a m e − {pkgname}- pkgname−{pkgver} | 构建目录名 | 建议,头部定义更清晰,官方模板在 prepare() 中隐式处理 |
| makedepends=("cmake") | 声明编译依赖 | 必需,lycium 会检查 cmake 是否安装,未安装时报错提示 |
总结
通过对比可以得出结论:官方模板提供的是最小可行骨架,实际适配时必须根据三方库的具体情况进行扩展。
MediaInfo 的适配关键增加了四类内容:
- 本地源码处理:srcpath + cp + autounpack=false
- musl兼容性修复:mediainfo-zenlib-musl.patch + patch -p1
- 项目特有构建选项:-S./Project/CMake/CLI/ + 7 个 CMake 选项
- 产物打包输出:archive() 函数生成 tar.gz 和 HNP 安装包
没有写不出来的 HPKBUILD,只有不够深入的调研。祝大家在鸿蒙PC三方库适配的旅程中顺利!