一文通关:Abseil-cpp 鸿蒙交叉编译 + CTest 验证 + 常见报错解决
本文介绍了将 Abseil C++ 库适配到 OpenHarmony 平台的完整流程。主要内容包括: 环境准备:配置 OHOS NDK、CMake 和 GTest 依赖;源码分析:Abseil 采用 CMake 构建,依赖 GTest 进行测试;补丁处理:解决 musl 兼容性、时区路径、宽字符等平台差异问题;构建脚本:详细解析 HPKBUILD 中的 CMake 配置、路径修正关键步骤。
[!NOTE]
加入开源鸿蒙跨平台社区:
・共享三方库移植方案、跨平台应用开发
・互助解决编译 / 运行问题
・第一时间获取鸿蒙三方库适配模板
一个人走得快,一群人走得远!
说明
建议先按 C/C++ 三方库鸿蒙化适配一篇搞定:从环境到交叉编译、在鸿蒙设备上快速验证由 lycium 交叉编译的 C/C++ 三方库 搭好 Lycium 交叉编译 + 设备验证 闭环;本文侧重 Abseil 特有 CMake/GTest/CTest 与补丁细节。
Abseil 与标准库互补,提供容器、字符串、时间、Status / StatusOr、日志、flags 等模块,gRPC、Protobuf 等生态常用。本适配在 OHOS NDK + CMake 下产出 armeabi-v7a / arm64-v8a 静态库,并保留 GTest 单测 以便设备上 CTest 回归。
- 构建方式:上游原生 CMake,无 Autotools。
- 测试依赖:GoogleTest 必须由 Lycium 先编好,配置期 不下载 gtest 源码。
- 平台差异:musl、32 位指针、wchar_t 为 32 位、时区 tzdata 路径与索引项长度、death test / NaN 等与 Linux+glibc 不同,由补丁与脚本消化。
适配流程概述
三方库适配到 OpenHarmony 平台主要包括以下步骤(与参考博文一致):
- 环境准备:安装 Command Line Tools,配置
OHOS_SDK,准备 CMake、Make 等。 - 源码分析:确认 Abseil 为 CMake 工程、测试依赖 GTest、LTS 版本与 C++17 要求。
- 编写 HPKBUILD:下载、打补丁、CMake 配置、
DartConfiguration.tcl修正、编译与安装。 - 编写 HPKCHECK(可选增强):检查静态库、
*_test数量、CTest 配置与符号;设备实跑可接hdc/adb。 - 问题排查:设备 CTest 路径、macOS
sed、主机误跑 ARM ELF 等。 - 文档编写:
README_zh.md、开源信息、hnp.json等。
HPKBUILD 详细解析
HPKBUILD 是 lycium 的构建脚本,定义下载、补丁、编译、安装与打包。
1. 基本信息配置
pkgname=abseil-cpp
pkgver=20260107.1
pkgrel=0
pkgdesc="Abseil C++ standard library extensions from Google"
url="https://github.com/abseil/abseil-cpp"
archs=("armeabi-v7a" "arm64-v8a")
license=("Apache-2.0")
depends=("googletest")
makedepends=()
source="https://github.com/abseil/abseil-cpp/archive/refs/tags/${pkgver}.tar.gz"
关键点说明
pkgname:与thirdparty下目录名一致。archs:OH 常用 armeabi-v7a、arm64-v8a。depends:必须先用./build.sh googletest装好 GTest;build_hpk会把依赖安装根加入CMAKE_FIND_ROOT_PATH,供find_package(GTest)。source:官方 tag 源码包 URL。
2. 构建工具与目录配置
autounpack=true
downloadpackage=true
builddir=abseil-cpp-${pkgver}
packagename=${builddir}.tar.gz
patchflag=true
关键点说明
- Abseil 使用 CMake(由
build()中直接调用${OHOS_SDK}/.../cmake,无需buildtools=字段)。 builddir解压后与版本号对应,构建树为$builddir/$ARCH-build。
3. prepare() 函数(编译前准备)
3.1 应用主补丁 abseil-cpp_oh_pkg.patch
prepare() {
if $patchflag; then
cd $builddir
patch -p1 < ../abseil-cpp_oh_pkg.patch
cd "$OLDPWD"
patchflag=false
fi
问题背景
- 上游单测默认假设 glibc / x86 / UTF-16 wchar_t / Android 时区格式 等,在 OHOS 上会出现 death test 失败、宽字符期望不符、tzdata 索引项长度不一致(48 vs 52 字节)等。
解决方案
- 使用汇总补丁
abseil-cpp_oh_pkg.patch(CMake 测试路径、__OHOS__分支、cctz、utf8、charconv、容器测试等)。升级 LTS 时务必patch -p1 --dry-run逐文件合并。
3.2 应用补充补丁 abseil-cpp_str_format_glibc_test.patch
(cd "$builddir" && patch -p1 --forward < ../abseil-cpp_str_format_glibc_test.patch) || true
rm -f "$builddir/absl/strings/internal/str_format/convert_test.cc.rej"
mkdir -p $builddir/$ARCH-build
}
问题背景
str_format单测在部分环境要求「与 glibc 完全一致的%a特征」,OHOS libc 与 glibc 不同,需与主补丁中的探测逻辑配合。
解决方案
- 独立小补丁用
patch --forward,已应用时退出码 1 用|| true忽略;删除.rej避免脏树。
4. build() 函数(CMake、CTest 路径修正)
4.1 CMake 与 GTest 选项
build() {
cd $builddir
${OHOS_SDK}/native/build-tools/cmake/bin/cmake "$@" \
-B$ARCH-build -S./ \
-DABSL_BUILD_TESTING=ON \
-DABSL_USE_EXTERNAL_GOOGLETEST=ON \
-DABSL_FIND_GOOGLETEST=ON \
-DABSL_RUN_mobile_TEST=OFF \
-DBUILD_SHARED_LIBS=OFF \
-DCMAKE_CXX_STANDARD=17 \
> $buildlog 2>&1
关键点说明
ABSL_USE_EXTERNAL_GOOGLETEST=ON+ABSL_FIND_GOOGLETEST=ON:禁止 CMake 下载 GTest。ABSL_BUILD_TESTING=ON:生成*_test,供设备 CTest。BUILD_SHARED_LIBS=OFF:静态库,便于北向工程链接。ABSL_RUN_mobile_TEST=OFF:若 CMake 提示该变量未使用,可忽略。"$@":由build_hpk.sh传入 toolchain、CMAKE_INSTALL_PREFIX等。
4.2 修正 DartConfiguration.tcl(设备 CTest 必看)
_fix_dart_ctest_paths() {
local _dcf="$1"
[ -f "$_dcf" ] || return 0
if sed --version >/dev/null 2>&1; then
sed -i 's|^BuildDirectory:.*|BuildDirectory: .|' "$_dcf"
sed -i 's|^SourceDirectory:.*|SourceDirectory: .|' "$_dcf"
else
sed -i '' 's|^BuildDirectory:.*|BuildDirectory: .|' "$_dcf"
sed -i '' 's|^SourceDirectory:.*|SourceDirectory: .|' "$_dcf"
fi
}
_fix_dart_ctest_paths "$ARCH-build/DartConfiguration.tcl"
$MAKE -C $ARCH-build >> $buildlog 2>&1
ret=$?
_fix_dart_ctest_paths "$ARCH-build/DartConfiguration.tcl"
问题背景
- CMake 会把构建机绝对路径写入
DartConfiguration.tcl。设备上跑 CTest 时仍向该路径写Testing/Temporary,出现 Cannot create directory /Users/…。
解决方案
- 将
BuildDirectory、SourceDirectory改为.;设备上必须在 构建目录根 执行ctest。 - macOS 上 BSD
sed -i需sed -i '',否则替换可能不生效;脚本用sed --version区分 GNU/BSD。 - make 后再修一次:防止构建过程中 CMake 重跑覆盖该文件。
5. package()、check()、archive()、cleanbuild()
package():make install安装到lycium/usr/abseil-cpp/<ARCH>/(头文件、libabsl_*.a、CMake 包等)。check():主机上仅ctest -N枚举测试;不执行 ARM ELF(避免 Syntax error: word unexpected)。archive():打 tar 包并hnpcli pack,配合hnp.json。cleanbuild():删除源码构建目录。
HPKBUILD 完整代码
# Copyright (c) 2026 unisources
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# Contributor: allincoding <3384684593@qq.com>
# Maintainer: allincoding <3384684593@qq.com>
pkgname=abseil-cpp
pkgver=20260107.1
pkgrel=0
pkgdesc="Abseil C++ standard library extensions from Google"
url="https://github.com/abseil/abseil-cpp"
archs=("armeabi-v7a" "arm64-v8a")
license=("Apache-2.0")
# 由 build_hpk 的 cmakedependpath 注入 CMAKE_FIND_ROOT_PATH,指向已安装的
# lycium/usr/googletest/<ARCH>/,供 find_package(GTest);勿再配置期下载 gtest。
depends=("googletest")
makedepends=()
source="https://github.com/abseil/abseil-cpp/archive/refs/tags/${pkgver}.tar.gz"
autounpack=true
downloadpackage=true
builddir=abseil-cpp-${pkgver}
packagename=${builddir}.tar.gz
patchflag=true
prepare() {
if $patchflag; then
cd $builddir
patch -p1 < ../abseil-cpp_oh_pkg.patch
cd "$OLDPWD"
patchflag=false
fi
# 独立补丁:每次 prepare 执行;--forward 避免已打补丁时交互/误反转;|| true 忽略已应用时的退出码 1。
(cd "$builddir" && patch -p1 --forward < ../abseil-cpp_str_format_glibc_test.patch) || true
rm -f "$builddir/absl/strings/internal/str_format/convert_test.cc.rej"
mkdir -p $builddir/$ARCH-build
}
build() {
cd $builddir
# build_hpk.sh 传入 $@ 包含 toolchain 和 install prefix
${OHOS_SDK}/native/build-tools/cmake/bin/cmake "$@" \
-B$ARCH-build -S./ \
-DABSL_BUILD_TESTING=ON \
-DABSL_USE_EXTERNAL_GOOGLETEST=ON \
-DABSL_FIND_GOOGLETEST=ON \
-DABSL_RUN_mobile_TEST=OFF \
-DBUILD_SHARED_LIBS=OFF \
-DCMAKE_CXX_STANDARD=17 \
> $buildlog 2>&1
# CTest 默认把绝对路径写入 DartConfiguration.tcl;设备上无该路径时会无法创建
# Testing/Temporary。改为 BuildDirectory: . / SourceDirectory: . 后,须在
# $ARCH-build 目录下执行 ctest(日志为 ./Testing/Temporary/LastTest.log)。
# macOS 的 sed -i 必须带 '' 备份后缀参数,否则整段 s 命令会被当成后缀而不生效。
_fix_dart_ctest_paths() {
local _dcf="$1"
[ -f "$_dcf" ] || return 0
if sed --version >/dev/null 2>&1; then
sed -i 's|^BuildDirectory:.*|BuildDirectory: .|' "$_dcf"
sed -i 's|^SourceDirectory:.*|SourceDirectory: .|' "$_dcf"
else
sed -i '' 's|^BuildDirectory:.*|BuildDirectory: .|' "$_dcf"
sed -i '' 's|^SourceDirectory:.*|SourceDirectory: .|' "$_dcf"
fi
}
_fix_dart_ctest_paths "$ARCH-build/DartConfiguration.tcl"
$MAKE -C $ARCH-build >> $buildlog 2>&1
ret=$?
# make 期间若触发 CMake 重新配置,会重写 DartConfiguration.tcl,需再修一次。
_fix_dart_ctest_paths "$ARCH-build/DartConfiguration.tcl"
cd $OLDPWD
return $ret
}
package() {
cd $builddir
$MAKE -C $ARCH-build install >> $buildlog 2>&1
ret=$?
cd $OLDPWD
return $ret
}
check() {
# 交叉编译生成的是 ARM/ARM64 ELF,在 x86 构建机上执行 ctest 会被 shell 误当脚本解析
# (报 Syntax error: word unexpected)。测试可执行文件已在 build 阶段全部链接成功。
# 仅列出已注册的测试(不运行),设备上实跑仍由 HPKCHECK / ctest 完成。
# 在 $ARCH-build 下调用 ctest(不用 --test-dir),与 BuildDirectory: . 一致,日志提示为相对路径。
pushd "$builddir/$ARCH-build" >/dev/null || return 1
echo "check: $ARCH — ctest 工作目录: $(pwd)" >> "$buildlog"
echo "check: 失败时详细输出(相对路径): ./Testing/Temporary/LastTest.log" >> "$buildlog"
echo "check: skip executing tests on host; ctest -N (list only)" >> "$buildlog"
${OHOS_SDK}/native/build-tools/cmake/bin/ctest -N >> "$buildlog" 2>&1
ret=$?
popd >/dev/null || true
return $ret
}
archive() {
mkdir -p ${LYCIUM_ROOT}/output/$ARCH
pushd ${LYCIUM_ROOT}/usr/$pkgname/$ARCH
tar -zvcf ${LYCIUM_ROOT}/output/$ARCH/${pkgname}_${pkgver}.tar.gz *
popd
cp ${PWD}/hnp.json ${LYCIUM_ROOT}/usr/$pkgname/$ARCH/
${OHOS_SDK}/toolchains/hnpcli pack \
-i ${LYCIUM_ROOT}/usr/$pkgname/$ARCH \
-o ${LYCIUM_ROOT}/output/$ARCH/ \
-n $pkgname -v $pkgver
}
cleanbuild() {
rm -rf ${PWD}/$builddir
}
鸿蒙化补丁说明(与「问题排查」呼应)
主补丁按模块可概括为下表(细节以 patch 为准):
| 方向 | 说明 |
|---|---|
CMake / absl_cc_test |
外部 GTest 时条件包含;add_test 使用相对二进制目录路径,CTest 可找到 bin 下用例。 |
ABSL_OPTION_HARDENED |
Release 单测仍需要部分 hardening 行为。 |
__OHOS__ + death test |
fork/退出码与 GTest 不匹配处对 EXPECT_DEATH_IF_SUPPORTED 做 no-op 或放宽。 |
| 32 位 / SOO | raw_hash_set_test、low_level_hash_test 等限制 OOM 风险或对齐 golden。 |
| 宽字符 / UTF-8 | wchar_t 为 32 位时 surrogate 按错误标量处理;日志宽字符串期望分支。 |
| charconv NaN | musl 与 from_chars 的 quiet NaN payload 可能不同,改为均 isnan。 |
| cctz / tzdata | 增加 /system/etc/zoneinfo/tzdata 等路径;兼容 48/52 字节索引项。 |
| 日志 / VLOG / flags | StderrThreshold 与 flag 同步;VLOG 用独立编译单元测 ABSL_MAX_VLOG_VERBOSITY。 |
补丁abseil-cpp_str_format_glibc_test.patch:GlibcHasCorrectTraits 在非 glibc x86_64 上改为校验 缓存与即时探测一致,避免误报。
HPKCHECK 详细解析
HPKCHECK 用于在 CI 或本机记录测试结果、检查产物;设备上完整 CTest 仍需将 $ARCH-build 整树同步到设备后在构建根目录执行 ctest(见下一节)。
1. 初始化
source HPKBUILD > /dev/null 2>&1
logfile=${LYCIUM_THIRDPARTY_ROOT}/${pkgname}/${pkgname}_${ARCH}_${OHOS_SDK_VER}_test.log
关键点说明
source HPKBUILD复用pkgname、builddir等变量。- 日志路径含架构与 SDK 版本,便于多环境归档。
2. checkprepare() 与 openharmonycheck()
checkprepare():检查libabsl_base.a等是否存在;统计*_test数量是否大于 0。openharmonycheck():列出测试二进制、检查CTestTestfile.cmake、用llvm-nm抽查静态库符号;若存在hdc/adb可扩展为推送与远程执行(脚本内留有注释示例)。
HPKCHECK 完整代码
# Copyright (c) 2026 unisources
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# Contributor: allincoding <3384684593@qq.com>
# Maintainer: allincoding <3384684593@qq.com>
source HPKBUILD > /dev/null 2>&1
logfile=${LYCIUM_THIRDPARTY_ROOT}/${pkgname}/${pkgname}_${ARCH}_${OHOS_SDK_VER}_test.log
checkprepare() {
# 验证关键库文件和测试二进制存在
for lib in libabsl_base libabsl_status libabsl_strings libabsl_flags; do
[ -f "${LYCIUM_ROOT}/usr/${pkgname}/${ARCH}/lib/${lib}.a" ] || return 1
done
# 验证测试二进制存在(cmake build testing 产物)
if [ -d "${LYCIUM_THIRDPARTY_ROOT}/${pkgname}/${builddir}/${ARCH}-build" ]; then
# 统计测试二进制数量
testbin_count=$(find "${LYCIUM_THIRDPARTY_ROOT}/${pkgname}/${builddir}/${ARCH}-build" \
-name "*_test" -o -name "*_benchmark" 2>/dev/null | wc -l)
echo "Test binaries found: ${testbin_count}" >> ${logfile}
[ ${testbin_count} -gt 0 ] || return 1
else
echo "Build directory not found: ${ARCH}-build" >> ${logfile}
return 1
fi
return 0
}
# 在 OpenHarmony 设备上执行测试
# 通过 ctest 运行 gtest 测试用例
openharmonycheck() {
res=0
build_dir="${LYCIUM_THIRDPARTY_ROOT}/${pkgname}/${builddir}/${ARCH}-build"
if [ ! -d "$build_dir" ]; then
echo "FAIL: Build directory $build_dir not found" >> ${logfile}
return 1
fi
# 列出所有测试二进制
echo "=== Test binaries ===" >> ${logfile}
find "$build_dir" -name "*_test" -type f >> ${logfile} 2>&1
# 尝试运行 ctest(需要在设备上执行,此处仅验证配置)
echo "=== ctest configuration ===" >> ${logfile}
if [ -f "$build_dir/CTestTestfile.cmake" ]; then
echo "CTest configured: OK" >> ${logfile}
# 显示已注册的测试数量
test_count=$(grep "add_test" "$build_dir/CTestTestfile.cmake" 2>/dev/null | wc -l)
echo "Registered tests: ${test_count}" >> ${logfile}
else
echo "WARN: CTestTestfile.cmake not found (tests may not be CMake-based)" >> ${logfile}
fi
# 验证关键库可被测试用例链接(ldd 等效检查)
echo "=== Library linkage check ===" >> ${logfile}
for lib in libabsl_base libabsl_status libabsl_strings; do
libpath="${LYCIUM_ROOT}/usr/${pkgname}/${ARCH}/lib/${lib}.a"
if [ -f "$libpath" ]; then
# nm 检查库符号完整性
if ${OHOS_SDK}/native/llvm/bin/llvm-nm "$libpath" 2>/dev/null | head -3 >> ${logfile}; then
echo "OK: ${lib}.a symbols readable" >> ${logfile}
else
echo "WARN: Cannot read symbols from ${lib}.a" >> ${logfile}
fi
else
echo "FAIL: ${lib}.a not found" >> ${logfile}
res=1
fi
done
# 设备端实际运行测试(需要 hdc 等工具,此处检查是否可用)
echo "=== Device test execution ===" >> ${logfile}
if command -v hdc &>/dev/null || command -v adb &>/dev/null; then
echo "Device tool available - running actual tests on device" >> ${logfile}
# 设备端 ctest 实际执行(需要在设备上运行ARM二进制)
# 注意:交叉编译产物的测试必须在 OpenHarmony ARM 设备上运行
# 此处通过远程命令执行
if command -v hdc &>/dev/null; then
# 将测试二进制推送到设备并执行
echo "Using hdc for device test..." >> ${logfile}
# 实际设备测试命令示例(需要根据设备IP/序列号调整)
# hdc -s <serial> file send ${build_dir}/some_test /vendor/bin/
# hdc -s <serial> shell /vendor/bin/some_test
elif command -v adb &>/dev/null; then
echo "Using adb for device test..." >> ${logfile}
fi
else
echo "INFO: No device tool (hdc/adb) found on build machine" >> ${logfile}
echo "INFO: For actual device testing, run manually:" >> ${logfile}
echo " adb push ${build_dir}/*_test /vendor/bin/" >> ${logfile}
echo " adb shell /vendor/bin/<test_binary>" >> ${logfile}
fi
return $res
}
执行交叉编译构建
在 lycium 目录执行(googletest 须先于 abseil-cpp):
./build.sh googletest abseil-cpp
成功后:
- 安装树:
lycium/usr/abseil-cpp/<ARCH>/ - 构建树:
thirdparty/abseil-cpp/abseil-cpp-<ver>/<ARCH>-build/
构建日志可参考 thirdparty/abseil-cpp/abseil-cpp-<ver>-<ARCH>-lycium_build.log。

全量推送到 OHOS 设备执行测试
将 abseil-cpp-<ver>/<ARCH>-build 整目录同步到设备(含 CTestTestfile.cmake、DartConfiguration.tcl、absl/**/CTestTestfile.cmake、bin/*_test),在设备上:
cd /data/<你的同步路径>/<ARCH>-build
ctest
# 失败重跑:
ctest --rerun-failed --output-on-failure
亦可按团队流程将 tpc_c_cplusplus 推到设备后,在 lycium 下执行 ./test.sh abseil-cpp(若已接入),并查看 ${pkgname}_${ARCH}_${OHOS_SDK_VER}_test.log。

业务工程 CMake 集成示例
list(APPEND CMAKE_PREFIX_PATH "/path/to/lycium/usr/abseil-cpp/arm64-v8a")
find_package(absl CONFIG REQUIRED)
target_link_libraries(your_target PRIVATE
absl::strings
absl::status
)
工程需 C++17+;pthread 等按 OHOS NDK 工程惯例配置。
遇到的问题和解决方案总结
问题 1:设备上 CTest 报错无法创建 /Users/.../Testing/Temporary
错误信息示例
Cannot create directory /Users/.../armeabi-v7a-build/Testing/Temporary
Cannot create log file: LastTest.log
原因:DartConfiguration.tcl 中 BuildDirectory 仍为构建机绝对路径。
解决方案
- 使用当前 HPKBUILD 中的
_fix_dart_ctest_paths(含 make 后二次修复)。 - 或在设备树中手改
BuildDirectory:、SourceDirectory:为.,并 cd 到构建根 再执行ctest。
问题 2:macOS 上构建后 DartConfiguration.tcl 仍未被改写
原因:BSD sed -i 语法与 GNU 不同,sed -i 's/.../' 可能未正确执行 in-place 替换。
解决方案:脚本中通过 sed --version 分支,macOS 使用 sed -i ''。
问题 3:在 x86 构建机上直接 ctest 跑用例失败
现象:如 Syntax error: word unexpected(shell 误执行 ARM ELF)。
原因:测试二进制为 ARM/ARM64,不能在主机上直接跑。
解决方案:主机 check() 仅用 ctest -N;实跑在 OHOS 设备 构建目录下 ctest。
问题 4:CMake 警告 ABSL_RUN_mobile_TEST 未使用
原因:上游未消费该变量。
解决方案:可忽略;保留该选项意在关闭不适用的移动端测试开关(若未来上游识别会生效)。
问题 5:升级 Abseil LTS 后补丁冲突
解决方案
- 干净 tarball 上
patch -p1 --dry-run。 - 按本文「鸿蒙化补丁说明」分模块合并。
- 全量设备 CTest 回归。
参考链接
更多推荐


所有评论(0)