鸿蒙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/coostAtomCode 自动生成/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-|grepunix# 输出: #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-ninstall/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/HPKBUILDpkgname=coostpkgver=v2025_05_23pkgrel=0pkgdesc="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-bitlicense=("MIT")depends=()makedepends=()source="https://github.com/idealvin/coost/archive/refs/tags/${pkgver}.tar.gz"autounpack=truedownloadpackage=truepatchflag=falsebuildtools="cmake"builddir=$pkgname-${pkgver:1}packagename=$builddir.tar.gzprepare(){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"\>$buildlog2>&1$MAKE-C$ARCH-buildVERBOSE=1>>$buildlog2>&1ret=$?cd$OLDPWDreturn$ret}package(){# 通过 build_hpk.sh 符号链接反查 lycium 根目录local_lycium_root="$(cd$(dirname$(readlink-f${PWD}/build_hpk.sh))/..; pwd)"cd$builddirlocal_prefix="${_lycium_root}/usr/$pkgname/$ARCH"mkdir-p${_prefix}/libmkdir-p${_prefix}/includecp-rfinclude/co${_prefix}/include/cp-f$ARCH-build/src/libco.a${_prefix}/lib/ret=$?cd$OLDPWDreturn$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$builddirlocal_prefix="${_lycium_root}/usr/$pkgname/$ARCH"mkdir-p${_prefix}/lib${_prefix}/include# 根据实际产物路径调整下面两行cp-rfinclude/<lib>${_prefix}/include/# 头文件目录cp-f$ARCH-build/<path>/lib<a>.a${_prefix}/lib/# 静态库路径cd$OLDPWD}编译器预设宏排查脚本
#!/bin/bash# 用法: ./check_builtin_macros.sh [compiler_path]# 默认: OHOS clangCC=${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-packageskill 自动生成了 80% 的骨架代码,剩下的 20%——那 3 个踩坑修复——才是真正需要人类判断力的地方。
金句:交叉编译的坑往往不在你预期的地方——不是 CMake 语法,不是链接器脚本,而是
<built-in>中那个你没有意识到的#define unix 1。
你在移植三方库时遇到过编译器内建宏冲突的问题吗?欢迎在评论区分享你的排查思路。
如果本文对你有帮助,请点赞、收藏、转发支持一下~