系列导读:本文是 Lycium 适配系列的第四篇,介绍 HPKBUILD 编写完成后如何执行构建、分析构建日志、处理多依赖递归编译、获取产物并进行 HAP 集成。
欢迎加入【开源鸿蒙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. 构建执行
1.1 启动构建
HPKBUILD 编写完成后,执行构建命令:
shell
cd lycium
./build.sh mylib
框架将自动完成以下工作:
- 加载交叉编译环境(
envset.sh) - 解析依赖关系
- 下载源码、校验完整性、解压
- 为每个架构循环执行
prepare()→build()→package() - 记录构建结果
1.2 构建流程全解析
build_hpk.sh 中 builpackage() 函数的完整执行序列:
| 步骤 | 函数/组件 | 角色与具体工作 |
|---|---|---|
| 1 | cleanhpk() |
清理上次构建 --- 删除旧的构建目录、校验已缓存的源码包(校验失败则删除) |
| 2 | builddepends() |
依赖解析 --- 读取 depends[],检查每个依赖库是否已在 usr/ 下构建完成(通过 hpk_build.csv 记录);若未就绪则退出码 101,触发外层递归构建 |
| 3 | checkmakedepends() |
工具检测 --- 检查 makedepends[] 声明的命令是否存在(which),缺失则报错退出 |
| 4 | download() |
源码下载 --- 若 downloadpackage != false,用 wget $source -O $packagename 下载源码 |
| 5 | checksum() |
完整性校验 --- 若存在 SHA512SUM 文件,运行 sha512sum -c SHA512SUM,失败则退出 |
| 6 | unpack() |
解压 --- 若 autounpack != false,自动识别 tar.gz / tgz / tar.xz / tar.bz2 / zip 格式解压 |
| 7 | for arch in ${archs[@]} |
多架构循环 --- 为每个声明的架构顺序执行以下全部子步骤 |
| 7.1 | prepare() |
预处理 --- 创建编译目录、打 patch、设置软链接等 |
| 7.2 | cmakedependpath() / configuredependpath() |
依赖路径注入 --- 计算 $buildargs 和 $pkgconfigpath |
| 7.3 | build() |
编译 --- 调用 HPKBUILD 中定义的 build() 函数 |
| 7.4 | package() |
安装 --- 调用 HPKBUILD 中定义的 package() 函数 |
| 7.5 | archive()(可选) |
归档 --- 将产物打包为 output/<arch>/<pkgname>_<ver>.tar.gz,或执行 HNP 打包 |
| 7.6 | install_local()(可选) |
本地安装 --- 鸿蒙本机构建时安装到本地系统路径 |
| 7.7 | check()(可选) |
测试 --- 记录测试方法(如果 LYCIUM_BUILD_CHECK=true) |
| 7.8 | recoverpkgbuildenv()(可选) |
环境还原 --- 清理 prepare() 中设置的临时环境变量 |
| 7.9 | recordbuildlibs() |
记录构建结果 --- 将 <arch>,<pkgname>,<ver> 写入 usr/hpk_build.csv |
2. 构建日志分析
2.1 日志位置
每个架构生成独立的构建日志。日志文件的默认位置与 HPKBUILD 所在目录对应:
# 如果 HPKBUILD 在 thirdparty/mylib/ 下
thirdparty/mylib/mylib-1.0.0-arm64-v8a-lycium_build.log
# 如果 HPKBUILD 在 community/mylib/ 下
community/mylib/mylib-1.0.0-arm64-v8a-lycium_build.log
格式:<pkgname>-<pkgver>-<arch>-lycium_build.log
同时框架还会在 lycium/script/ 下生成一份日志归档副本,方便集中查看。
2.2 日志内容
日志包含 build() 中通过 >$buildlog 2>&1 重定向的所有输出。典型内容:
shell
# 查看构建日志(快速定位错误)
tail -100 thirdparty/mylib/mylib-1.0.0-arm64-v8a-lycium_build.log
# 搜索报错关键字
grep -i "error\|failed\|undefined reference" thirdparty/mylib/*.log
2.3 常见构建失败排查
| 错误模式 | 根因 | 解决方案 |
|---|---|---|
undefined reference to 'xxx' |
缺少依赖库或链接参数 | 检查 depends[] 是否完整,或 LDFLAGS 中缺少 -lxxx |
cannot find -lxxx |
链接器找不到依赖库 | 检查依赖库是否已构建,$buildargs 中的 CMAKE_FIND_ROOT_PATH 是否正确 |
unknown target triple |
clang 不认识架构 | 检查 envset.sh 中的 ARCH_TRIPLE 设置,或 archs[] 值拼写错误 |
fatal error: 'xxx.h' file not found |
缺少头文件 | 依赖库的头文件未安装到 usr/<dep>/<arch>/include/;或 include_directories 未正确设置 |
checking for xxx... no(configure 项目) |
configure 检测失败 | 检查 PKG_CONFIG_PATH 设置,或 --with-xxx 参数 |
cmake: not found |
OHOS SDK 路径未配置 | 使用 SDK 自带的 cmake ${OHOS_SDK}/native/build-tools/cmake/bin/cmake |
wget: command not found |
缺少 wget | apt install wget |
sha512sum: command not found |
缺少校验工具 | Linux: apt install coreutils,macOS: brew install coreutils |
2.4 日志调试技巧
shell
# 实时查看构建输出(不等待构建完成)
./build.sh mylib 2>&1 | tee build.log
# 只查看错误行
grep -E "^(.*error:|.*Error |make:.*\*\*\*)" script/mylib-*.log
# 对比不同架构的日志
diff script/mylib-1.0.0-arm64-v8a-lycium_build.log \
script/mylib-1.0.0-armeabi-v7a-lycium_build.log | less
# 查看构建记录
cat usr/hpk_build.csv
3. 多库递归构建
3.1 依赖传递机制
当构建的库有依赖(depends 非空)时,框架的依赖处理流程:
./build.sh libA
→ builddepends() 读取 libA/depends=("libB")
→ 检查 libB 是否已构建(查 hpk_build.csv)
→ 未构建 → 退出码 101
→ ./build.sh 捕获退出码 101
→ 将 libB 加入 LYCIUM_DEPEND_PKGNAMES
→ ./build.sh libA libB # 先构建 libB,待就绪后构建 libA
→ 递归直到所有依赖就绪
3.2 手动指定多库构建
shell
# 同时构建多个库
./build.sh libA libB libC
# 先构建依赖库,再构建目标库
./build.sh libB libC libA
3.3 依赖构建的注意事项
- 依赖顺序不重要:框架会自动按拓扑排序,先构建被依赖的库
- 共享依赖:多个库依赖同一个库时,只构建一次
- 循环依赖:A 依赖 B、B 依赖 A 会导致死循环,需要检查依赖设计
- 架构超集规则 :被依赖库的
archs必须是当前库archs的超集
bash
# 正确:libA 依赖 libB,libB 的 archs 覆盖 libA
# libA/HPKBUILD
archs=("arm64-v8a")
depends=("libB")
# libB/HPKBUILD
archs=("armeabi-v7a" "arm64-v8a") # ✓ 超集,覆盖 arm64-v8a
4. 产物获取
4.1 产物分布
构建完成后,产物分布在以下位置:
| 产物类型 | 路径 | 说明 |
|---|---|---|
| 头文件 (.h) | lycium/usr/<pkgname>/<arch>/include/ |
API 头文件,供 HAP 工程引用 |
| 动态库 (.so) | lycium/usr/<pkgname>/<arch>/lib/ |
运行时链接库 |
| 静态库 (.a) | lycium/usr/<pkgname>/<arch>/lib/ |
静态链接库 |
| pkg-config | lycium/usr/<pkgname>/<arch>/lib/pkgconfig/ |
.pc 依赖配置文件 |
| 可执行文件 | lycium/usr/<pkgname>/<arch>/bin/ |
工具或测试程序 |
| 归档包 | lycium/output/<arch>/<pkgname>_<ver>.tar.gz |
全部产物的 tar 压缩包 |
| HNP 包 | lycium/output/<arch>/<pkgname>.hnp |
鸿蒙 Native Package |
| 构建记录 | lycium/usr/hpk_build.csv |
所有已构建库的清单 |
| 构建日志 | lycium/script/<pkgname>-<ver>-<arch>-lycium_build.log |
调试用详细日志 |
4.2 产物验证
shell
# 检查产物结构
ls -la lycium/usr/mylib/arm64-v8a/
# 输出:
# drwxr-xr-x include/
# drwxr-xr-x lib/
# ├── libmylib.so
# ├── libmylib.a
# └── pkgconfig/
# └── mylib.pc
# 查看库文件信息(确认架构正确)
file lycium/usr/mylib/arm64-v8a/lib/libmylib.so
# 输出:ELF 64-bit LSB shared object, ARM aarch64, ...
# 查看导出的符号
llvm-nm -D lycium/usr/mylib/arm64-v8a/lib/libmylib.so | head -20
4.3 产物获取流程
构建完成
产物类型
动态库/静态库
头文件
pkg-config文件
HNP包
复制到 HAP 工程
验证链接参数
直接集成到 HAP
5. HAP 集成
5.1 集成步骤
应用开发者从 usr/ 获取产物后,集成到 HAP 工程:
| 步骤 | 操作 | 负责方 |
|---|---|---|
| 5.1 | 将 .so / .a 及头文件复制到 HAP 工程目录 |
应用开发者 |
| 5.2 | 在 HAP 工程的 CMakeLists.txt 中配置 target_link_libraries |
应用开发者 |
| 5.3 | 在 HAP 工程中 #include 相应的头文件 |
应用开发者 |
| 5.4 | 参考 docs/hap_integrate.md 验证集成正确性 |
应用开发者 |
5.2 CMakeLists.txt 集成示例
cmake
# HAP 工程 CMakeLists.txt
cmake_minimum_required(VERSION 3.16)
project(MyApp)
# 指定第三方库路径
set(MYLIB_PATH ${CMAKE_CURRENT_SOURCE_DIR}/libs/mylib)
# 添加头文件搜索路径
include_directories(${MYLIB_PATH}/include)
# 添加库文件搜索路径
link_directories(${MYLIB_PATH}/lib)
# 链接库
add_library(myapp SHARED src/main.cpp)
target_link_libraries(myapp mylib) # 链接 libmylib.so
更规范的 HAP 集成方式:
cmake
# 使用 target_include_directories 和 target_link_libraries
add_library(myapp SHARED src/main.cpp)
# 直接指定绝对路径(开发阶段)
target_include_directories(myapp PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/libs/mylib/arm64-v8a/include
)
target_link_directories(myapp PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/libs/mylib/arm64-v8a/lib
)
target_link_libraries(myapp PRIVATE
mylib
c
log # OHOS 日志库
)
5.3 HNP 集成
如果库提供了 HNP 包(output/<arch>/<pkgname>.hnp),集成更简单:
- 将
.hnp文件放入 HAP 工程的libs/目录 - 在
build-profile.json5中配置 native 库路径 - 构建工具会自动解包 HNP 并链接
5.4 hap_integrate.md 编写建议
作为适配者,为应用开发者提供清晰的集成文档:
markdown
# mylib HAP 集成指南
## 产物文件
- libmylib.so(arm64-v8a / armeabi-v7a)
- include/mylib.h
## 集成步骤
1. 将对应架构的 libmylib.so 复制到 `entry/libs/<arch>/`
2. 将 include/mylib.h 复制到 `entry/src/main/cpp/include/`
3. 在 CMakeLists.txt 中添加:
```cmake
target_link_libraries(entry PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/libs/${OHOS_ARCH}/libmylib.so)
API 说明
- mylib_init()
- mylib_process()
- mylib_cleanup()
6. 本机构建(DevBox)
当在 OpenHarmony 设备(DevBox)上直接构建时,使用 build_local.sh:
./build_local.sh mylib
该脚本的特点:
- 自动识别操作系统为
HarmonyOS - 设置
TARGET_HARMONYOS=true - 仅构建
arm64-v8a架构(其他架构自动跳过) - 产物安装到设备本地系统路径(而不是
usr/目录)
下篇预告
了解了构建流程和产物获取后,下一篇将用一张完整的流程图展示适配者、Lycium 框架和 OHOS SDK 三者之间的交互关系,并总结各环节的角色职责。