欢迎加入 开源鸿蒙跨平台社区,与开发者一起共建鸿蒙三方库的开源生态。
一、背景与目标
utfcpp (UTF-8 with C++ in a Portable Way)是 GitHub 上的仅头文件 C++ 库,用于便携地处理 UTF-8 编码字符串,支持合法性检查、按码点迭代、UTF-8 与 UTF-16/UTF-32 互转等,无第三方依赖,兼容 C++98 及更新标准。本文记录在 OpenHarmony 三方库共建仓库(tpc_c_cplusplus) 中,使用 lycium 交叉编译框架 将 utfcpp 接入、安装头文件并仿照 komrad36-CRC 方式用 CMake 编出测试可执行文件、在设备上运行验证的完整过程,以及遇到的问题与解决方案。
目标:
- 在 Mac/Linux 宿主机上完成 utfcpp 的"下载 → 解压 → 安装头文件";
- 使用 CMake 编出适用于 OpenHarmony(armeabi-v7a / arm64-v8a)的测试可执行文件 utfcpp_test,build 目录结构与 CRC 一致(含 CMakeCache、Makefile、CMakeFiles 等);
- 测试可在开发板上通过
./utfcpp_test执行,输出utf8::is_valid与码点个数,退出码 0 表示通过。
二、环境与框架简介
环境搭建 更详细的步骤可参考:OpenHarmony 交叉编译环境配置。下文仅列出与本文直接相关的要点。
2.1 编译环境搭建
-
宿主机 :建议 Mac(Darwin)或 Linux;需安装
gcc、g++、cmake、make、pkg-config、wget等。 -
OHOS SDK :需包含
native/llvm、native/build-tools/cmake、native/build/cmake/ohos.toolchain.cmake等;环境变量指向 SDK 根目录,例如:bashexport OHOS_SDK=/path/to/OpenHarmony/Sdk/20 -
lycium :每个三方库由目录下的 HPKBUILD 定义"下载 → 解压 → prepare → 编译 → 安装";产物落在
lycium/usr/<pkgname>/<ARCH>/。
2.2 utfcpp 上游特点
- 源码 :使用 release 归档
https://github.com/nemtrif/utfcpp/archive/refs/tags/v4.0.9.tar.gz,解压后为utfcpp-4.0.9;主头文件在 source/ 下(utf8.h及utf8/子目录)。 - 头文件库 :无 .cpp 需编成库,无静态库 .a;交付物仅为头文件 ,在 package() 中将
source/整份拷贝到usr/utfcpp/<ARCH>/include/。 - 测试 :上游无现成测试可执行文件;在 lycium 中通过 prepare() 内 heredoc 生成
utfcpp/CMakeLists.txt、utfcpp/utfcpp_test.cpp,再使用 CMake 仅编出 utfcpp_test,与 komrad36-CRC 的"build 目录含 CMake + 测试二进制"结构一致。
2.3 HPKBUILD / HPKCHECK 在本库中的角色
- HPKBUILD :定义 pkgname=utfcpp、pkgver=4.0.9、source、builddir=utfcpp-4.0.9、buildtools=cmake;prepare() 中在
$builddir下生成utfcpp/CMakeLists.txt与utfcpp/utfcpp_test.cpp并mkdir utfcpp/$ARCH-build;build() 中 cmake -Butfcpp/ A R C H − b u i l d − S u t f c p p + m a k e ; ∗ ∗ p a c k a g e ( ) ∗ ∗ 中拷贝 ∗ ∗ s o u r c e / ∗ ∗ 到 ' u s r / u t f c p p / ARCH-build -S utfcpp + make;**package()** 中拷贝 **source/** 到 `usr/utfcpp/ ARCH−build−Sutfcpp+make;∗∗package()∗∗中拷贝∗∗source/∗∗到'usr/utfcpp/ARCH/include/` 并删除源码压缩包;cleanbuild() 删除解压目录与压缩包。 - HPKCHECK :设备上进入
${builddir}/utfcpp/${ARCH}-build执行./utfcpp_test,以退出码作为测试结果。
三、接入步骤
3.1 创建三方库目录与文件
在 Workspace/tpc_c_cplusplus/thirdparty/ 下新建目录 utfcpp,并准备:
| 文件 | 作用 |
|---|---|
| HPKBUILD | 包名、源码、prepare/build/package/cleanbuild;prepare 中 heredoc 生成 CMake 与测试源码 |
| HPKCHECK | 设备上进入 utfcpp/$ARCH-build 执行 ./utfcpp_test |
| README.OpenSource | 开源协议、上游地址(JSON 多行格式) |
| README_zh.md | 中文说明:简介、产物、编译、测试、头文件用法等 |
| .gitignore | 忽略 utfcpp-*.tar.gz、utfcpp-*/、*.log、last_error |
3.2 HPKBUILD 配置要点与重要代码
| 项 | utfcpp 取值 / 说明 |
|---|---|
| pkgname | utfcpp |
| pkgver | 4.0.9(使用 release tag) |
| source | https://github.com/nemtrif/utfcpp/archive/refs/tags/v${pkgver}.tar.gz |
| builddir | utfcpp-4.0.9 |
| buildtools | cmake |
| archs | armeabi-v7a、arm64-v8a |
| depends | 无 |
prepare():heredoc 生成 CMake 与测试源码
在 prepare() 中 cd $builddir 后,用两个 heredoc (<< 'ENDCMAKE' / 'ENDTEST')分别写入 utfcpp/CMakeLists.txt、utfcpp/utfcpp_test.cpp。结束符须单独占一行、行首无空格,且使用单引号避免变量展开。
CMakeLists.txt 关键内容(仅编测试可执行文件,头文件来自上级 source/,静态链接便于设备运行):
bash
cat > utfcpp/CMakeLists.txt << 'ENDCMAKE'
cmake_minimum_required(VERSION 3.12)
project(utfcpp_test CXX)
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../source)
add_executable(utfcpp_test utfcpp_test.cpp)
set_target_properties(utfcpp_test PROPERTIES LINK_FLAGS "-static")
enable_testing()
add_test(NAME utfcpp_test COMMAND utfcpp_test)
ENDCMAKE
utfcpp_test.cpp 关键内容(调用 utf8::is_valid、utf8::distance,退出码 0 表示通过):
bash
cat > utfcpp/utfcpp_test.cpp << 'ENDTEST'
#include <iostream>
#include <string>
#include "utf8.h"
int main() {
std::string s = "Hello UTF-8 \xe4\xbd\xa0\xe5\xa5\xbd";
bool ok = utf8::is_valid(s.begin(), s.end());
int n = (int)utf8::distance(s.begin(), s.end());
std::cout << "utf8::is_valid: " << (ok ? "true" : "false") << std::endl;
std::cout << "utf8::distance (code points): " << n << std::endl;
return ok ? 0 : 1;
}
ENDTEST
最后 mkdir -p utfcpp/$ARCH-build 并 cd ${OLDPWD}。
build()
与 CRC 同构:cmake 使用 -Butfcpp/ A R C H − b u i l d − S u t f c p p ∗ ∗ , m a k e 在 ∗ ∗ u t f c p p / ARCH-build -S utfcpp**,make 在 **utfcpp/ ARCH−build−Sutfcpp∗∗,make在∗∗utfcpp/ARCH-build 中执行,得到 CMakeCache.txt、Makefile、CMakeFiles 及 utfcpp_test(头文件库无 .a):
bash
build() {
cd $builddir
PKG_CONFIG_LIBDIR="${pkgconfigpath}" ${OHOS_SDK}/native/build-tools/cmake/bin/cmake "$@" \
-DOHOS_ARCH=$ARCH -Butfcpp/$ARCH-build -S utfcpp -L > $buildlog 2>&1
ret=$?
if [ $ret -ne 0 ]; then
cd $OLDPWD
return $ret
fi
$MAKE VERBOSE=1 -C utfcpp/$ARCH-build >> $buildlog 2>&1
ret=$?
cd $OLDPWD
return $ret
}
package()
仅安装头文件:将解压后的 source/ 整份拷贝到 usr/utfcpp/$ARCH/include/,并删除源码压缩包:
bash
package() {
cd $builddir
mkdir -p $LYCIUM_ROOT/usr/$pkgname/$ARCH/include
if [ -d source ]; then
cp -r source/. $LYCIUM_ROOT/usr/$pkgname/$ARCH/include/
fi
cd ${OLDPWD}
rm -f ${PWD}/$packagename
}
cleanbuild()
bash
cleanbuild() {
rm -rf ${PWD}/$builddir
rm -f ${PWD}/$packagename
}
HPKCHECK:设备上执行 utfcpp_test
设备上进入 utfcpp-4.0.9/utfcpp/${ARCH}-build 后直接运行 ./utfcpp_test,以退出码作为测试结果:
bash
source HPKBUILD > /dev/null 2>&1
logfile=${LYCIUM_THIRDPARTY_ROOT}/${pkgname}/${pkgname}_${ARCH}_${OHOS_SDK_VER}_test.log
checkprepare() { return 0; }
openharmonycheck() {
res=0
cd ${builddir}/utfcpp/${ARCH}-build
echo "start test times: `date`" >> ${logfile} 2>&1
if [ -f ./utfcpp_test ]; then
./utfcpp_test >> ${logfile} 2>&1
res=$?
else
echo "No utfcpp_test executable, skip test" >> ${logfile} 2>&1
res=1
fi
cd $OLDPWD
echo "end test times: `date`" >> ${logfile} 2>&1
return $res
}
3.3 与 CRC 的对比:头文件库无 .a,build 目录仅含测试与 CMake 产物
- komrad36-CRC :编出静态库 libkomrad36_crc.a + 测试 CRC32Test,故
CRC/<ARCH>-build/下既有 CMake/Makefile 又有 .a 与可执行文件。 - utfcpp :仅头文件库 ,无 .cpp 编成库,故无 lib*.a ;build 目录(
utfcpp/<ARCH>-build/)内只有 CMake 生成文件 + utfcpp_test,与 CRC 的"目录结构 + 测试可执行文件"一致,便于统一测试流程与 HPKCHECK。
四、编译流程与命令
-
进入 lycium 目录:
bashcd /path/to/tpc_c_cplusplus/lycium -
执行编译(仅编译 utfcpp):
bash./build.sh utfcpp
-
产物位置:
- 头文件 :
lycium/usr/utfcpp/armeabi-v7a/include/、lycium/usr/utfcpp/arm64-v8a/include/(含 utf8.h、utf8/ 等)。 - 测试可执行文件 :
thirdparty/utfcpp/utfcpp-4.0.9/utfcpp/<ARCH>-build/utfcpp_test;同目录下含 CMakeCache.txt、Makefile、CMakeFiles、cmake_install.cmake、CTestTestfile.cmake 等(与 CRC 的 build 目录结构一致)。 - 编译成功后源码压缩包
utfcpp-4.0.9.tar.gz会被自动删除。
- 头文件 :
五、遇到的问题与解决方案
问题一:HPKBUILD 报 "syntax error near unexpected token {" / "cleanbuild: command not found"
现象 :执行 ./build.sh utfcpp 时出现 HPKBUILD: line 24: syntax error near unexpected token '{'、prepare() {,以及 cleanbuild: command not found。
原因 :HPKBUILD 使用 Windows 换行(CRLF,\r\n) 。行尾的 \r 导致 bash 解析异常(例如 prepare() {\r 中 { 与换行被错误处理),source 未完成,cleanbuild 等函数未定义。
解决 :将 HPKBUILD 转为 Unix 换行(LF):
bash
sed 's/\r$//' thirdparty/utfcpp/HPKBUILD > thirdparty/utfcpp/HPKBUILD.tmp && mv thirdparty/utfcpp/HPKBUILD.tmp thirdparty/utfcpp/HPKBUILD
建议 HPKCHECK、README 等一并转为 LF,避免类似问题。
问题二:build 目录只有 utfcpp_test,没有"库文件"
现象:与 komrad36-CRC 对比时,发现 CRC 的 build 目录下有 libkomrad36_crc.a 和 CRC32Test,而 utfcpp 的 build 目录只有 utfcpp_test 及 CMake/Makefile 等。
原因 :utfcpp 是纯头文件库 ,没有需要编译成 .a 的源文件;交付物就是头文件,在 package() 中已拷贝到 usr/utfcpp/<ARCH>/include/。build 阶段仅为了与 CRC 同构 而用 CMake 编出测试可执行文件,因此不会也不应出现 libutfcpp.a。
解决:属预期行为。若希望 build 目录"看起来"与 CRC 一致,已通过"CMake + utfcpp 子目录 + $ARCH-build"实现(CMakeCache、Makefile、CMakeFiles、utfcpp_test 等);唯一差别即无 .a 文件。
问题三:设备上运行 utfcpp_test 报 "No such file or directory"(文件存在)
现象 :设备上 ls 能看到 utfcpp_test,但执行 ./utfcpp_test 提示 "No such file or directory"。
原因 :可执行文件为动态链接 ,ELF 中记录的动态链接器路径与设备上实际路径不一致,内核加载时找不到解释器。
解决 :在 utfcpp 的 CMakeLists.txt 中已为 utfcpp_test 设置 静态链接 :
set_target_properties(utfcpp_test PROPERTIES LINK_FLAGS "-static")
重新编译后将新的 utfcpp_test 拷贝到设备再运行即可。
六、设备侧测试简要步骤
-
将 utfcpp-4.0.9/utfcpp/arm64-v8a-build (aarch64 设备)或 armeabi-v7a-build (32 位 ARM 设备)下的 utfcpp_test 拷贝到开发板,例如:
bashhdc file send .../utfcpp-4.0.9/utfcpp/arm64-v8a-build/utfcpp_test /data/local/tmp/ hdc shell chmod +x /data/local/tmp/utfcpp_test -
在设备上执行:
bash/data/local/tmp/utfcpp_test -
通过时输出示例:
textutf8::is_valid: true utf8::distance (code points): 14退出码 0 表示测试通过。

七、总结
- utfcpp 在 OpenHarmony 上的交叉编译依赖 lycium 的 HPKBUILD/HPKCHECK;通过 prepare() 内 heredoc 生成 CMake 与测试源码,package() 安装头文件,build() 用 CMake 编出 utfcpp_test,实现与 komrad36-CRC 同构的 build 目录(CMake + Makefile + 测试可执行文件),便于统一测试流程。
- 典型问题:HPKBUILD 的 CRLF 导致语法错误与 cleanbuild 未定义;头文件库无 .a 属预期;设备上动态链接器路径问题通过测试程序静态链接解决。
- 最终产物:lycium/usr/utfcpp/
<ARCH>/include/ 下的头文件可供应用集成(#include "utf8.h");utfcpp-4.0.9/utfcpp/<ARCH>-build/utfcpp_test 用于设备端功能验证。
若你在使用 lycium 接入其他头文件库 或希望"测试可执行文件 + build 目录结构"与现有库(如 CRC)一致,可参考本文的 prepare/build/package 与 HPKCHECK 写法。更多 OpenHarmony 跨平台与三方库共建内容,欢迎在 开源鸿蒙跨平台社区 交流。
参考与相关链接
- utfcpp 上游:https://github.com/nemtrif/utfcpp
- tpc_c_cplusplus / lycium:OpenHarmony 三方库 C/C++ 共建仓库及交叉编译框架
- 环境搭建 :OpenHarmony 交叉编译环境配置
- 开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net/