鸿蒙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

image-20260628145255612

前置说明

项目 说明
适配库 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 编译标志解决宏冲突。

修复 2package() 中添加手动文件复制逻辑,绕过 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

armeabi-v7a 失败

修改 archs

添加编译标志

CMake 无 install 目标

new-package 生成 HPKBUILD

./build.sh coost

排查: 32-bit 不支持
unix 宏冲突

移除 armeabi-v7a

-Uunix 解除宏冲突

package 阶段失败

手动复制 .a + 头文件

./build.sh coost 重试

arm64-v8a ✓ / x86_64 ✓

产物验证

# 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:110static_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/
  }

关键点 1readlink -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 迁移库 调整 DESTDIRCMAKE_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


你在移植三方库时遇到过编译器内建宏冲突的问题吗?欢迎在评论区分享你的排查思路。

如果本文对你有帮助,请点赞、收藏、转发支持一下~

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐