【鸿蒙PC三方库移植适配框架解读系列】第三篇:HPKBUILD 编写详解
本文是Lycium适配系列的核心篇,详细解析HPKBUILD文件的编写方法。HPKBUILD作为适配工作的核心文件,包含声明式元数据和过程式shell函数两部分。文章系统讲解了pkgname、pkgver等基本信息字段,架构声明、许可证、依赖关系的配置要点,以及源码下载相关参数的设置技巧。重点阐述了三种构建系统(CMake、Autotools、Makefile)的典型写法,并提供了完整的函数模板和
系列导读:本文是 Lycium 适配系列的第三篇,也是最核心的一篇。适配工作的 80% 都在编写 HPKBUILD,本文将深入讲解每一个字段和函数,以及三种构建系统的典型写法。
欢迎加入【开源鸿蒙PC社区】,一起共建鸿蒙化C/C++三方库生态。
前言
| 项目 | 说明 |
|---|---|
| macOS环境配置 | https://bxming.blog.csdn.net/article/details/159284830 |
| Ubuntu环境配置(或者windows + wsl) | https://bxming.blog.csdn.net /article/details/159284760 |
| lycium框架 | https://atomgit.com/OpenHarmonyPCDeveloper/lycium_plusplus.git |
| 应用平台 | HarmonyOS PC |
系列索引
| 篇章 | 标题 | 内容 |
|---|---|---|
| 第一篇 | 概述与环境配置 | Lycium 概念、构建机要求、OHOS SDK 配置 |
| 第二篇 | 项目结构与适配目录创建 | 目录结构、community vs thirdparty、创建适配目录 |
| 第三篇 | HPKBUILD 编写详解 | 元数据字段、过程函数、三种构建系统写法 |
| 第四篇 | 构建执行与产物获取 | 构建流程、日志分析、多库递归、HAP 集成 |
| 第五篇 | 流程图与角色职责 | 完整流程图、各角色职责、协作时序 |
| 第六篇 | 关键注意事项与最佳实践 | 依赖管理、架构超集、日志调试、外部适配仓 |
| 第七篇 | 快速参考与模板 | 入门步骤、模板、完整案例、检查清单 |
1. HPKBUILD 概览
HPKBUILD 是 Lycium 适配的灵魂文件。它本质上是一个 Bash 脚本,由两部分组成:
HPKBUILD = 声明式元数据 + 过程式 shell 函数
- 声明式元数据:用变量告诉框架"这是谁、从哪下载、依赖什么"
- 过程式函数:用 shell 函数告诉框架"怎么编译、怎么安装"
当执行 ./build.sh mylib 时,Lycium 框架按以下顺序执行:
- 解析元数据(读取
pkgname、archs、depends等) - 为每个架构依次调用
prepare()→build()→package() - 其中
build()执行时,框架已注入了交叉编译器环境变量和依赖搜索路径
2. 元数据字段详解
2.1 基本信息
| 字段 | 必填 | 类型 | 说明 | 示例 |
|---|---|---|---|---|
pkgname |
✓ | string | 库名,也是安装目录名 | mylib |
pkgver |
✓ | string | 库版本,与上游 tag 一致 | 1.2.3 |
pkgrel |
✓ | integer | 发布号,同一版本多次适配时递增 | 0 → 1 → 2 |
pkgdesc |
✗ | string | 库描述,简短说明用途 | "A JSON parsing library" |
url |
✗ | string | 官网/仓库链接 | "https://github.com/..." |
pkgrel 的使用场景:当 pkgver 不变,但 HPKBUILD 内容有修改(如修复了交叉编译参数、更新了 patch),递增 pkgrel 即可。例如第一次适配为 pkgrel=0,后续修复为 pkgrel=1。
2.2 架构声明
archs=("armeabi-v7a" "arm64-v8a")
规则:
- 框架会为
archs[]中每个架构顺序执行一遍完整的构建流程 - 每个架构独立构建、独立安装到
usr/<pkgname>/<arch>/目录下 - 常见组合:只做
arm64-v8a(最主流)、或同时支持armeabi-v7a和arm64-v8a
2.3 许可证
license=("MIT")
# 或
license=("Apache-2.0" "BSD-3-Clause")
注意事项:
- 遵循 SPDX 许可证标识符规范(
MIT、Apache-2.0、GPL-2.0-only等) - 多许可证时用数组列出所有
- 必须与
README.OpenSource中的声明一致
2.4 依赖声明
depends=("libA" "libB") # 运行时依赖
makedepends=("cmake") # 构建工具依赖
depends[]:运行时依赖库的pkgname。必须与对应 HPKBUILD 的pkgname完全一致。makedepends[]:构建时需要的命令。框架会在构建前用which检测这些命令是否存在。- 框架对
depends[]的处理:- 检查依赖库是否已构建(通过
usr/hpk_build.csv记录) - 若未构建,退出码 101 → 外层
build.sh自动先构建依赖库 - 依赖库的产物会被挂载到搜索路径(通过
$buildargs和$pkgconfigpath)
- 检查依赖库是否已构建(通过
2.5 源码相关
source="https://github.com/example/$pkgname/archive/refs/tags/v$pkgver.tar.gz"
builddir=$pkgname-$pkgver
packagename=$builddir.tar.gz
downloadpackage=true # 默认 true,框架自动 wget 下载
autounpack=true # 默认 true,框架自动识别格式解压
| 字段 | 必填 | 说明 | 技巧 |
|---|---|---|---|
source |
✓ | 源码下载 URL,支持变量替换($pkgname、$pkgver) |
GitHub 上 <name>/archive/refs/tags/v<ver>.tar.gz 是常见模式 |
builddir |
✓ | 解压后的目录名,构建时 cd 进入 |
通常 = $pkgname-$pkgver,但有时 tag 不含 v 前缀,需自行调整 |
packagename |
✓ | 下载后的压缩包文件名 | 通常 = $builddir.tar.gz |
downloadpackage |
✗ | 是否自动下载,默认 true |
设为 false 时需在 prepare() 中手动处理源码 |
autounpack |
✗ | 是否自动解压,默认 true |
设为 false 时需在 prepare() 中手动解压 |
变量替换机制:source 中的 $pkgname 和 $pkgver 会在运行时被实际值替换,无需硬编码。例如:
pkgname=mylib
pkgver=1.2.3
source="https://github.com/example/$pkgname/archive/refs/tags/v$pkgver.tar.gz"
# 实际下载:https://github.com/example/mylib/archive/refs/tags/v1.2.3.tar.gz
2.6 构建系统类型
buildtools=cmake # 或 configure / make
| 值 | 含义 | 框架行为 |
|---|---|---|
留空 或 cmake |
CMake 项目 | 生成 -DCMAKE_FIND_ROOT_PATH=... -DCMAKE_TOOLCHAIN_FILE=... 等 $buildargs |
configure |
autotools 项目 | 生成 --prefix=... 等 $buildargs |
make |
纯 Makefile 项目 | 框架只设置编译器环境变量,不自动注入额外的 $buildargs |
3. 过程函数详解
3.1 必备三函数
prepare() — 预处理
在 build() 之前调用,每架构一次。典型任务:
prepare() {
# 1. 创建 out-of-source 构建目录(CMake 项目推荐)
mkdir -p $builddir/$ARCH-build
# 2. 应用 patch
cd $builddir
patch -p1 < ${OLDPWD}/../mylib_oh_pkg.patch
cd $OLDPWD
# 3. 设置软链接(某些库需要特定目录结构)
# ln -sf ... ...
# 4. 生成必要的配置文件
# autoreconf -fi # autotools 项目需要
}
build() — 编译(核心)
每个架构执行一次,框架已设置好所有编译环境变量:
| 变量 | 说明 |
|---|---|
$CC |
交叉编译器(如 aarch64-linux-ohos-clang) |
$CFLAGS |
交叉编译标志(含 --sysroot) |
$ARCH |
当前架构名(如 arm64-v8a) |
$buildargs |
框架自动生成的构建参数(详见下文 3.3) |
$buildlog |
当前架构的详细日志文件路径 |
$pkgconfigpath |
所有依赖库的 pkgconfig 路径(冒号分隔) |
$MAKE |
默认 make -j$(nproc) |
build() {
cd $builddir
# CMake 项目(推荐 out-of-source 构建)
${OHOS_SDK}/native/build-tools/cmake/bin/cmake "$@" \
-DOHOS_ARCH=$ARCH -B$ARCH-build -S./ -L > $buildlog 2>&1
$MAKE -C $ARCH-build >> $buildlog 2>&1
ret=$?
cd $OLDPWD
return $ret
}
$buildlog的最佳实践:build()中第一次命令用>(覆盖),后续用>>(追加),确保日志文件保存完整的历史记录。
package() — 安装
package() {
cd $builddir
$MAKE -C $ARCH-build install >> $buildlog 2>&1
cd $OLDPWD
}
框架在调用 package() 前已设置了 DESTDIR 和 CMAKE_INSTALL_PREFIX,产物自动安装到 $LYCIUM_ROOT/usr/$pkgname/$ARCH/。
3.2 辅助函数
check() — 验证
描述如何在 OH 设备上手动验证库的功能正确性,框架不会自动执行。
check() {
# 示例:cJSON 的 check 说明
echo "在 OH 设备上执行以下命令验证:"
echo " 1. 编写一个简单测试程序,包含 cJSON.h"
echo " 2. 链接 libcjson.so"
echo " 3. 运行并检查输出"
}
archive() — 归档产物
将当前架构的编译产物打包为 tar.gz 归档,用于跨架构复用,避免重复编译。
archive() {
# 默认归档位置:output/<arch>/<pkgname>_<ver>.tar.gz
# 框架会自动将归档包安装到 target 目录
# 如需自定义打包逻辑,在此函数内实现
tar -czf ${OUTPUTDIR}/${arch}/${pkgname}_${pkgver}.tar.gz \
-C ${INSTALLDIR} usr/
}
归档策略:如果库是纯 C 静态库且不依赖特定架构特性,可以在
arm64-v8a构建后归档,然后在armeabi-v7a的prepare()中检查并使用该归档,跳过编译。
install_local() — 鸿蒙本机构建安装
当使用
build_local.sh(DevBox 设备内构建)时调用,将编译产物安装到设备系统路径。
install_local() {
# 安装头文件
cp -r include/* /system/library/include/ 2>/dev/null || true
# 安装动态库
cp -f $builddir/libcjson.so /system/library/ 2>/dev/null || true
}
cleanbuild() — 清理(必填)
cleanbuild() {
# 清理构建目录
rm -rf ${builddir}/$ARCH-build
# 清理编译产物
find . -type f -name '*.o' -delete
}
recoverpkgbuildenv() — 恢复环境变量
在
prepare()或build()中临时修改了环境变量时,在此函数中恢复。
recoverpkgbuildenv() {
# 示例:如果在 prepare 中 export 了临时变量,在此处 unset
unset TEMP_VAR
}
3.3 $buildargs 的内容
框架根据 buildtools 自动生成 $buildargs:
| 构建系统 | $buildargs 内容 |
|---|---|
| cmake | -DCMAKE_FIND_ROOT_PATH="<dep1>;<dep2>..." -DCMAKE_TOOLCHAIN_FILE=<path> -DCMAKE_INSTALL_PREFIX=<path> -G "Unix Makefiles" -DOHOS_ARCH=<arch> |
| configure | --prefix=$LYCIUM_ROOT/usr/$pkgname/$ARCH/ |
| make | 空(框架只设置编译器环境变量) |
注意:如果需要在
$buildargs中添加自定义参数(如-DBUILD_SHARED_LIBS=ON),可以在build()函数中用变量追加:export buildargs="$buildargs -DBUILD_SHARED_LIBS=ON"
同时 $pkgconfigpath 变量被设置为所有依赖库的 lib/pkgconfig 路径(冒号分隔)。
在 build() 中使用 $buildargs:
# CMake 项目
cmake "$@" $buildargs ... # "$@" 展开为 $buildargs,再加自定义参数
# configure 项目
./configure $buildargs ...
4. 三种构建系统的典型写法
4.1 CMake 项目(最常用)
适用场景:大多数现代 C/C++ 库(cJSON、libcurl、protobuf 等)
# HPKBUILD
pkgname=xx
buildtools=cmake # 不写或写 cmake
prepare() {
mkdir -p $builddir/$ARCH-build
}
build() {
cd $builddir
PKG_CONFIG_LIBDIR="${pkgconfigpath}" \
${OHOS_SDK}/native/build-tools/cmake/bin/cmake "$@" \
-DOHOS_ARCH=$ARCH \
-B$ARCH-build -S./ -L > $buildlog 2>&1
$MAKE -C $ARCH-build >> $buildlog 2>&1
ret=$?
cd $OLDPWD
return $ret
}
package() {
cd $builddir
$MAKE -C $ARCH-build install >> $buildlog 2>&1
cd $OLDPWD
}
cleanbuild() {
rm -rf ${PWD}/$builddir
}
CMake 构建的常见问题:
| 问题 | 解决方案 |
|---|---|
| 找不到依赖库 | 框架通过 $buildargs 注入 -DCMAKE_FIND_ROOT_PATH,但有些库要用 find_package 的特定变量,可能需要额外 -D<PKG>_DIR=... |
| 找不到 pkg-config 模块 | 设置 PKG_CONFIG_LIBDIR="${pkgconfigpath}" 传递给 cmake 的 build() 环境 |
| 编译器检测错误 | 显式指定 -DCMAKE_C_COMPILER=$CC -DCMAKE_CXX_COMPILER=$CXX |
| 测试编译本机代码 | add -DCMAKE_TRY_COMPILE_TARGET_TYPE=STATIC_LIBRARY 避免链接测试 |
4.2 configure(autotools)项目
适用场景:历史悠久的老牌 C 库(ncurses、readline、libffi 等)
# HPKBUILD
pkgname=xx
buildtools=configure
autounpack=true
prepare() {
cd $builddir
# 有些库需要先生成 configure 脚本
# autoreconf -fi
cd $OLDPWD
}
build() {
cd $builddir
# 有时需要设置 PKG_CONFIG_PATH
# export PKG_CONFIG_PATH="${pkgconfigpath}"
./configure "$@" $buildargs \
--host=${ARCH_TRIPLE} \
--target=${ARCH_TRIPLE} \
CC=$CC CXX=$CXX \
CFLAGS="$CFLAGS" \
LDFLAGS="$LDFLAGS" \
> $buildlog 2>&1
$MAKE >> $buildlog 2>&1
ret=$?
cd $OLDPWD
return $ret
}
package() {
cd $builddir
$MAKE install >> $buildlog 2>&1
cd $OLDPWD
}
cleanbuild() {
rm -rf ${PWD}/$builddir
}
autotools 项目的关键点:
archs变量会在运行时导出为OHOS_ARCH,可在envset.sh中查看到对应的ARCH_TRIPLE(如aarch64-linux-ohos)--host必须设置为交叉编译的目标三元组- 很多 autotools 项目需要
autoreconf -fi重新生成 configure 脚本,因为上游的 configure 可能不支持交叉编译到 OH - 某些项目的
configure脚本会检测/dev/random等 Linux 特有路径,需要 patch 或设置环境变量绕过
4.3 纯 Makefile 项目
适用场景:简单的 C 库,或非标准构建系统的库
# HPKBUILD
pkgname=xx
buildtools=make
prepare() {
mkdir -p $builddir/$ARCH-build
}
build() {
cd $builddir
# $MAKE 默认带 -j$(nproc),${MAKE_FLAGS} 可自定义
$MAKE CC=$CC CXX=$CXX AR=$AR \
CFLAGS="$CFLAGS" \
LDFLAGS="$LDFLAGS" \
>> $buildlog 2>&1
ret=$?
cd $OLDPWD
return $ret
}
package() {
cd $builddir
# make install 时需要指定安装前缀
$MAKE DESTDIR=$LYCIUM_ROOT/usr/$pkgname/$ARCH/ \
PREFIX=/ install >> $buildlog 2>&1
cd $OLDPWD
}
cleanbuild() {
rm -rf ${PWD}/$builddir
}
纯 Makefile 项目的注意事项:
- 必须手动传递
CC、CFLAGS:框架的环境变量不自动注入到 Makefile 中,需要在make命令行或通过export传递 - 安装前缀要小心:很多 Makefile 用
PREFIX而不是DESTDIR,甚至用自定义变量 - 先
make -n查看 Makefile 实际使用了哪些变量,再决定传入什么 - 如果 Makefile 编译选项固定(硬编码了 gcc),需要 patch 修改或通过 override 传入
5. 构建流程中的可用变量速查
在 HPKBUILD 的 prepare()、build()、package() 中,框架已设置以下变量供使用:
| 变量 | 来源 | 说明 |
|---|---|---|
$OHOS_ARCH |
envset.sh |
当前架构(如 arm64-v8a),与 $ARCH 相同 |
$ARCH_TRIPLE |
envset.sh |
编译器三元组(如 aarch64-linux-ohos) |
$CC / $CXX |
envset.sh |
交叉编译器路径 |
$CFLAGS / $CXXFLAGS |
envset.sh |
编译标志(含 --sysroot) |
$LDFLAGS |
envset.sh |
链接标志 |
$AR / $LD / $STRIP |
envset.sh |
LLVM 工具路径 |
$MAKE |
build_hpk.sh |
make -j$(nproc) |
$buildargs |
build_hpk.sh |
框架自动生成的构建参数 |
$pkgconfigpath |
build_hpk.sh |
所有依赖库的 pkgconfig 路径 |
$buildlog |
build_hpk.sh |
当前架构的日志文件路径 |
$LYCIUM_ROOT |
build.sh |
Lycium 框架根目录 |
$OLDPWD |
Bash 内置 | 进入 $builddir 前的目录 |
下篇预告
HPKBUILD 编写完成后,下一步就是执行构建。下一篇将介绍构建执行的完整流程、构建日志分析、多库递归依赖编译、产物获取以及 HAP 集成。
更多推荐



所有评论(0)