基于 lycium 在 OpenHarmony 上交叉编译 utfcpp 完整实践

欢迎加入 开源鸿蒙跨平台社区,与开发者一起共建鸿蒙三方库的开源生态。


一、背景与目标

utfcppUTF-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;需安装 gccg++cmakemakepkg-configwget 等。

  • OHOS SDK :需包含 native/llvmnative/build-tools/cmakenative/build/cmake/ohos.toolchain.cmake 等;环境变量指向 SDK 根目录,例如:

    bash 复制代码
    export 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.hutf8/ 子目录)。
  • 头文件库 :无 .cpp 需编成库,无静态库 .a;交付物仅为头文件 ,在 package() 中将 source/ 整份拷贝到 usr/utfcpp/<ARCH>/include/
  • 测试 :上游无现成测试可执行文件;在 lycium 中通过 prepare() 内 heredoc 生成 utfcpp/CMakeLists.txtutfcpp/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.txtutfcpp/utfcpp_test.cppmkdir utfcpp/$ARCH-buildbuild() 中 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.gzutfcpp-*/*.loglast_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.txtutfcpp/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-buildcd ${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。

四、编译流程与命令

  1. 进入 lycium 目录

    bash 复制代码
    cd /path/to/tpc_c_cplusplus/lycium
  2. 执行编译(仅编译 utfcpp):

    bash 复制代码
    ./build.sh utfcpp
  3. 产物位置

    • 头文件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 拷贝到设备再运行即可。


六、设备侧测试简要步骤

  1. utfcpp-4.0.9/utfcpp/arm64-v8a-build (aarch64 设备)或 armeabi-v7a-build (32 位 ARM 设备)下的 utfcpp_test 拷贝到开发板,例如:

    bash 复制代码
    hdc file send .../utfcpp-4.0.9/utfcpp/arm64-v8a-build/utfcpp_test /data/local/tmp/
    hdc shell chmod +x /data/local/tmp/utfcpp_test
  2. 在设备上执行:

    bash 复制代码
    /data/local/tmp/utfcpp_test
  3. 通过时输出示例:

    text 复制代码
    utf8::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 跨平台与三方库共建内容,欢迎在 开源鸿蒙跨平台社区 交流。


参考与相关链接

相关推荐
sTone873751 小时前
std::function/模板/裸函数指针选型指南
c++
无聊的小坏坏2 小时前
一文讲通:二分查找的边界处理
数据结构·c++·算法
二流小码农2 小时前
鸿蒙开发:独立开发者的烦恼之icon图标选择
android·ios·harmonyos
云深处@2 小时前
【C++11】包装器,智能指针
开发语言·c++
前端不太难2 小时前
HarmonyOS PC 多窗口最难的一层
华为·状态模式·harmonyos
十五年专注C++开发2 小时前
CMake进阶:SelectLibraryConfigurations模块
c++·cmake·自动化构建
木斯佳2 小时前
HarmonyOS 6实战(工程应用篇)—从被动响应到主动治理,如何使用HiAppEvent捕捉应用崩溃信息
华为·harmonyos
果粒蹬i2 小时前
【HarmonyOS】RN of HarmonyOS实战开发项目+TanStack缓存策略
缓存·华为·harmonyos
量子炒饭大师2 小时前
【C++入门】Cyber深度漫游者的初始链路——【类与对象】初始化成员列表
开发语言·c++·dubbo·类与对象·初始化成员列表