【鸿蒙 PC三方库构建系统】解决 OpenHarmony SHA 库编译问题:从动态链接错误到静态链接优化
鸿蒙PC移植coost:AtomCode搞定3个交叉编译坑
欢迎加入【开源鸿蒙PC社区】,一起共建鸿蒙化C/C++三方库生态。
欢迎在【PC社区】平台贡献你的项目。
源码仓库: https://github.com/idealvin/coost v2025_05_23 — 跨平台 C++ 基础库(前身为 co/cocoyaxi)
适配平台: 鸿蒙PC| 测试SDK: API 20适配仓库地址:https://atomgit.com/allincoding/coost

前置说明
| 项目 | 说明 |
|---|---|
| 适配库 | coost v2025_05_23(Cross-platform base library) |
| 目标平台 | 鸿蒙PC |
| SDK 版本 | API 20 |
| 构建系统 | CMake |
| 依赖 | 零外部依赖 |
| 许可证 | MIT |
| 核心工具 | lycium_plusplus + AtomCode Skills |
你将从本文获得什么
- ✅ 一份可直接使用的 coost HPKBUILD 交叉编译脚本(arm64-v8a + x86_64)
- ✅ 3 个踩坑实录(32-bit 拦截、unix 宏冲突、无 install 目标)
- ✅ 一个通用技巧:CMake 无 install 目标时如何手动打包(
readlink反查 lycium 根目录) - ✅
nm联调排查法:交叉编译特定问题定位方法
核心步骤
Step 1:生成 HPKBUILD 骨架
/new-package coost v2025_05_23 https://github.com/idealvin/coost
AtomCode 自动生成 /home/lycium_plusplus/thirdparty/coost/HPKBUILD:
| 字段 | 自动生成值 |
|---|---|
pkgname |
coost |
pkgver |
v2025_05_23 |
archs |
armeabi-v7a arm64-v8a x86_64 |
buildtools |
cmake |
builddir |
coost-2025_05_23 |
license |
MIT |
Step 2:首次编译 — 发现问题
cd /home/lycium_plusplus/lycium
./build.sh coost
输出:
Compileing OpenHarmony armeabi-v7a coost v2025_05_23 libs...
ERROR during : build ...
arm64-v8a 和 x86_64 正常,但 armeabi-v7a(32-bit ARM)失败了。查看编译日志发现 coost 明确禁止 32-bit 平台。
Step 3:修复并完成全架构编译
修复 1:从 archs 移除 armeabi-v7a,添加 -Uunix 编译标志解决宏冲突。
修复 2:package() 中添加手动文件复制逻辑,绕过 CMake 无 install 目标的问题。
最终 HPKBUILD 完整版见下文 通用模板 章节。
执行编译:
./build.sh coost
输出:
Compileing OpenHarmony arm64-v8a coost v2025_05_23 libs...
The test must be on an OpenHarmony device!
Compileing OpenHarmony x86_64 coost v2025_05_23 libs...
The test must be on an OpenHarmony device!
Build coost v2025_05_23 end!
ALL JOBS DONE!!!
双架构编译通过,产物已部署到 lycium 输出目录。
全流程 Mermaid
产物验证
# ABI 验证
readelf -h /home/lycium_plusplus/lycium/usr/coost/arm64-v8a/lib/libco.a | grep -E "Machine|Class"
# 输出: ELF64 / AArch64
# 头文件验证
ls /home/lycium_plusplus/lycium/usr/coost/arm64-v8a/include/co/ | wc -l
# 输出: 41
踩坑专区
问题 1:coost 明确拒绝 32-bit 平台
现象:
In file included from .../sock.cc:4:
.../bitset.h:6:2: error: "32-bit platform not supported"
#error "32-bit platform not supported"
^
.../sched.h:110:13: error: static assertion failed due to requirement 'N < 64':
static_assert(N < 64, "");
^ ~~~~~~
根因:coost 的 bitset.h 中使用 std::bitset<64>,硬编码要求 64-bit 环境。sched.h 中的协程调度器 N < 64 编译期断言直接拒绝 32 位 ARM。这是 coost 的设计决定,不可配置。
排查过程:报错指向 bitset.h:6(#error)和 sched.h:110(static_assert),均为编译期硬编码,不存在条件编译宏可绕过。
修复方案:
- archs=("armeabi-v7a" "arm64-v8a" "x86_64")
+ archs=("arm64-v8a" "x86_64")
经验总结:部分现代 C++ 库为简化设计主动放弃 32-bit 支持。HPKBUILD 的 archs 数组只需列出现实支持的架构即可。始终先用 ./build.sh <name> 全架构扫描一次,看哪个架构先失败。
问题 2:OHOS 编译器 unix 宏冲突
现象:
.../include/co/time.h:41:17: error: expected unqualified-id
extern xx::Unix unix;
^
<built-in>:423:14: note: expanded from here
#define unix 1
^
根因:OHOS SDK 的 BiSheng/clang 编译器在 Linux 下内建了 #define unix 1 预定义宏。coost 的 time.h 中使用 unix 作为变量名(属于 xx 命名空间),展开后变成 xx::Unix 1;,导致语法错误。
这是 musl libc 生态的经典问题——glibc 生态在这些宏的历史由来已久,musl 类的工具链继承了相同的行为。GCC 和 Clang 均预设此宏,不是 OHOS 特有的。
排查过程:错误信息 note: expanded from here 指向 <built-in>,意味着是编译器内建宏。用 echo | gcc -dM -E - 确认:
# 验证(在 OHOS SDK 环境中)
echo | /home/ohpkg/linux/native/llvm/bin/clang -dM -E - | grep unix
# 输出: #define unix 1
修复方案:
$OHOS_SDK/native/build-tools/cmake/bin/cmake "$@" \
-B$ARCH-build -S./ \
+ -DCMAKE_CXX_FLAGS="-Uunix" \
+ -DCMAKE_C_FLAGS="-Uunix" \
> $buildlog 2>&1
-Uunix 的作用是 Undefine 名为 unix 的宏,在 CMake 编译标志层面解决了宏名冲突。
经验总结:交叉编译遇到 <built-in> 宏冲突时,通用解法思路:
| 宏 | 冲突场景 | 修复 |
|---|---|---|
unix |
变量名/命名空间成员名 | -Uunix |
linux |
变量名 | -Ulinux |
i386/__i386__ |
平台检测 | -U__i386__ |
__arm__ |
平台检测 | 视需求决定 |
最佳实践:任何包含 #include <time.h> 或与 POSIX 时间接口交互的库,在 OHOS 交叉编译时都建议加上 -Uunix——它不可能影响运行时行为,只会解除一个编译器预定义宏的干扰。
问题 3:CMake 无 install 目标
现象:
package() 阶段:
make: *** No rule to make target 'install'. Stop.
根因:coost 的 CMakeLists.txt 没有定义 install() 命令——它只编译产生 libco.a,没有预设安装逻辑。而 lycium 框架的默认 package() 函数执行 make -C $ARCH-build install,自然会失败。
排查过程:
grep -n install /path/to/coost/CMakeLists.txt
# 输出只有一行 INSTALL_INTERFACE 生成器表达式,无 install()
查看 coost 的 CMakeLists.txt 后发现它仅有:
target_include_directories(co INTERFACE
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/../include>
$<INSTALL_INTERFACE:$<INSTALL_PREFIX>/${CMAKE_INSTALL_INCLUDEDIR>>
)
只定义了 INSTALL_INTERFACE,但未定义实际的 install() 规则。
修复方案——手动复制 .a 和头文件:
package() {
- cd $builddir
- $MAKE -C $ARCH-build install >> $buildlog 2>&1
+ # 在 cd 之前获取 lycium 根目录
+ local _lycium_root="$(cd $(dirname $(readlink -f ${PWD}/build_hpk.sh))/..; pwd)"
+ cd $builddir
+ local _prefix="${_lycium_root}/usr/$pkgname/$ARCH"
+ mkdir -p ${_prefix}/lib
+ mkdir -p ${_prefix}/include
+ cp -rf include/co ${_prefix}/include/
+ cp -f $ARCH-build/src/libco.a ${_prefix}/lib/
}
关键点 1:readlink -f ${PWD}/build_hpk.sh 反查符号链接指向的真实路径。原因是 lycium 框架在 thirdparty/<name>/ 目录下创建了 build_hpk.sh → /home/lycium_plusplus/lycium/script/build_hpk.sh 的符号链接。通过 readlink -f 可以从符号链接反查到 lycium 根目录,而不需要依赖 $LYCIUM_ROOT 环境变量(该变量在 bash build_hpk.sh 子进程中可能不可用)。
关键点 2:coost 的静态库产物在 $ARCH-build/src/libco.a(子目录 src/),而非 cmake 默认的顶层。这是 coost 的 add_subdirectory(src) 构建结构决定的。
经验总结:CMake 无 install 目标的情况在小型 C++ 库中很常见。手动复制 .a + include/ 是通用解法。关键是要在 package() 阶段正确获取 lycium 输出路径。推荐使用 readlink -f build_hpk.sh 反查的方式——它与 lycium 框架的调用机制完全解耦,无论子进程如何执行都能正确推导。
通用模板(拿来即用)
完整 HPKBUILD
# lycium_plusplus/thirdparty/coost/HPKBUILD
pkgname=coost
pkgver=v2025_05_23
pkgrel=0
pkgdesc="A tiny boost library in the style of C++11 — coost (formerly co/cocoyaxi)"
url="https://github.com/idealvin/coost"
archs=("arm64-v8a" "x86_64") # coost 不支持 32-bit
license=("MIT")
depends=()
makedepends=()
source="https://github.com/idealvin/coost/archive/refs/tags/${pkgver}.tar.gz"
autounpack=true
downloadpackage=true
patchflag=false
buildtools="cmake"
builddir=$pkgname-${pkgver:1}
packagename=$builddir.tar.gz
prepare() {
mkdir -p $builddir/$ARCH-build
}
build() {
cd $builddir
$OHOS_SDK/native/build-tools/cmake/bin/cmake "$@" \
-B$ARCH-build -S./ \
-DCMAKE_CXX_FLAGS="-Uunix" \ # 解除 OHOS 编译器 unix 宏冲突
-DCMAKE_C_FLAGS="-Uunix" \
> $buildlog 2>&1
$MAKE -C $ARCH-build VERBOSE=1 >> $buildlog 2>&1
ret=$?
cd $OLDPWD
return $ret
}
package() {
# 通过 build_hpk.sh 符号链接反查 lycium 根目录
local _lycium_root="$(cd $(dirname $(readlink -f ${PWD}/build_hpk.sh))/..; pwd)"
cd $builddir
local _prefix="${_lycium_root}/usr/$pkgname/$ARCH"
mkdir -p ${_prefix}/lib
mkdir -p ${_prefix}/include
cp -rf include/co ${_prefix}/include/
cp -f $ARCH-build/src/libco.a ${_prefix}/lib/
ret=$?
cd $OLDPWD
return $ret
}
check() {
echo "The test must be on an OpenHarmony device!"
}
cleanbuild() {
rm -rf ${PWD}/$builddir
}
手动打包模板(适用于任何 CMake 无 install 的库)
package() {
local _lycium_root="$(cd $(dirname $(readlink -f ${PWD}/build_hpk.sh))/..; pwd)"
cd $builddir
local _prefix="${_lycium_root}/usr/$pkgname/$ARCH"
mkdir -p ${_prefix}/lib ${_prefix}/include
# 根据实际产物路径调整下面两行
cp -rf include/<lib> ${_prefix}/include/ # 头文件目录
cp -f $ARCH-build/<path>/lib<a>.a ${_prefix}/lib/ # 静态库路径
cd $OLDPWD
}
编译器预设宏排查脚本
#!/bin/bash
# 用法: ./check_builtin_macros.sh [compiler_path]
# 默认: OHOS clang
CC=${1:-/home/ohpkg/linux/native/llvm/bin/clang}
$CC -dM -E - < /dev/null | sort | grep -E 'unix|linux|i386|arm'
延伸思考
同类 C++ 基础库适配对比
| 对比维度 | coost (cocoyaxi) | abseil-cpp | folly |
|---|---|---|---|
| 构建系统 | CMake | CMake | CMake |
| 外部依赖 | 零依赖 | 零依赖 | fmt, gflags, glog, … |
| 32-bit 支持 | ❌ 不支持 | ✅ 支持 | ✅ 支持 |
| OHOS 适配难度 | ★★☆ 中 | ★★★ 中 | ★★★★ 高 |
| 核心难点 | unix 宏 + 无 install |
105 个 .a 合并 | 大量 C++17 特性适配 |
| 许可证 | MIT | Apache-2.0 | Apache-2.0 |
coost 的适配复杂度之所以是"中"而非"低",主要在于无 install 目标这一设计决策增加了打包步骤。但整体而言,零依赖 + CMake 的标准构建流程使其仍然是新手友好的鸿蒙适配入门库。
通用方法论:CMake 库的四种 install 模式
| 模式 | 特征 | 常见库举例 | package() 策略 |
|---|---|---|---|
| A. 有完整的 install() | make install 正常执行 |
zstd, libhv, simdjson | 直接使用 make install |
| B. 有 install() 但路径不对 | 安装到错误位置 | 部分 autotools 迁移库 | 调整 DESTDIR 或 CMAKE_INSTALL_PREFIX |
| C. 有 INSTALL_INTERFACE 但无 install() | make install 报错 |
coost | 手动复制(本文方案) |
| D. 无任何安装逻辑 | 只有编译目标 | 轻量级单头文件库 | 手动复制 + 生成 cmake config |
总结
coost(cocoyaxi)的鸿蒙适配之旅遇到了三个典型问题:32-bit 拒绝编译、OHOS 编译器的 unix 宏冲突、以及 CMake 无 install 目标的手动打包。每一个问题都对应了不同的知识域——库设计限制、跨平台宏兼容性、以及 lycium 框架的 package 机制。这正是"写一个 HPKBUILD"和"让 HPKBUILD 在真实环境中通过"之间的鸿沟。
AtomCode Skills 的 new-package skill 自动生成了 80% 的骨架代码,剩下的 20%——那 3 个踩坑修复——才是真正需要人类判断力的地方。
金句:交叉编译的坑往往不在你预期的地方——不是 CMake 语法,不是链接器脚本,而是
<built-in>中那个你没有意识到的#define unix 1。
你在移植三方库时遇到过编译器内建宏冲突的问题吗?欢迎在评论区分享你的排查思路。
如果本文对你有帮助,请点赞、收藏、转发支持一下~
更多推荐

所有评论(0)