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


一、背景与目标

emoji-segmentergoogle/emoji-segmenter)是 Google 开源的、基于 Ragel 语法生成的 C 语言 emoji 分段库,用于将文本流按 Unicode UTS #51(Unicode Emoji)语法切分为「文本展示」与「emoji 展示」的 run,被 Chromium、Pango 等用于决定某段文字用彩色 emoji 还是黑白字型。上游仅包含 Ragel 源文件(.rl)与已生成的 .c、无 CMake、无第三方依赖(仅 C + stdbool.h)。本文记录在 OpenHarmony 三方库共建仓库(tpc_c_cplusplus) 中,使用 lycium 交叉编译框架 将 emoji-segmenter 接入、提供适配 CMake 与公开头文件、编出静态库与 完整 UTS #51 测试套件(19 条用例),并在设备上通过 ctest 或直接运行 emoji_segmenter_test 完成验证的完整过程,以及目录名连字符、CRLF 换行符等常见问题的处理。

目标

  • 在 Mac/Linux 宿主机上交叉编译出适用于 OpenHarmony(armeabi-v7a / arm64-v8a)的 libemoji_segmenter.a 与头文件;
  • 提供与上游 API 一致的 emoji_presentation_scanner.h,供应用集成;
  • 编写依据 UTS #51 语法的 完整测试套件(text_run、text_emoji_run_with_vs、emoji_run_with_vs、emoji_run 等多类用例),编出 emoji_segmenter_test
  • 设备上可用 ctest 或 ./emoji_segmenter_test 跑测试,输出 19 条 [PASS] 表示全部通过。

二、环境与框架简介

环境搭建更详细的步骤(SDK 下载、环境变量、依赖工具安装等)可参考:OpenHarmony 交叉编译环境配置。下文仅列出与本文直接相关的要点。

2.1 编译环境搭建

  • 宿主机:建议 Mac(Darwin)或 Linux;需安装 gccg++cmakemakepkg-configwget 等。
  • OHOS SDK:需包含 native/llvmnative/build-tools/cmakenative/build/cmake/ohos.toolchain.cmake 等;环境变量指向 SDK 根目录,例如:
    export OHOS_SDK=/path/to/OpenHarmony/Sdk/20
    
  • lycium:每个三方库由目录下的 HPKBUILD 定义“下载 → 解压 → prepare → 编译 → 安装”;产物落在 lycium/usr/<pkgname>/<ARCH>/;设备侧测试由 HPKCHECK 中的 openharmonycheck() 定义。

2.2 emoji-segmenter 上游特点

  • 源码:使用 master 分支归档 https://github.com/google/emoji-segmenter/archive/refs/heads/master.tar.gz,解压后目录名为 emoji-segmenter-master;主要文件为 emoji_presentation_scanner.c(Ragel 生成)、emoji_presentation_scanner.rl(Ragel 语法)。
  • 构建:上游仅提供 Makefile(用于 ragel 重新生成 .c);本适配不依赖 Ragel,直接使用仓库内已提交的 .c,通过自备 CMakeLists.txt 编出静态库。
  • APIscan_emoji_presentation(p, pe, &is_emoji, &has_vs),迭代器类型 emoji_text_iter_t 由调用方或编译宏指定;上游无独立头文件,本适配在 prepare() 中生成 emoji_presentation_scanner.h 供应用包含。
  • 依赖:无第三方库,仅 C 与 stdbool.h。
  • 测试:上游无测试;本适配在 prepare() 中通过 heredoc 生成 emoji_segmenter_test.c,内含 19 条用例(字符类与 .rl 一致),覆盖 text_run、text_emoji_run_with_vs、emoji_run_with_vs、emoji_run 等 UTS #51 语法。

2.3 为何目录名与 pkgname 使用下划线(emoji_segmenter)

  • 若使用 emoji-segmenter(连字符),在部分 lycium/build 脚本路径未加引号时,会被 shell 按单词拆成 emojisegmenter,导致路径被截断、source HPKBUILD 失败(如 .../thirdparty/emoji: command not found)。
  • 因此本库在 thirdparty 下目录名为 emoji_segmenter,HPKBUILD 中 pkgname=emoji_segmenter,产物路径为 usr/emoji_segmenter/<ARCH>/;编译命令为 ./build.sh emoji_segmenter

2.4 HPKBUILD / HPKCHECK 在本库中的角色

  • HPKBUILD:定义 pkgname=emoji_segmenter、pkgver=master、source、builddir=emoji-segmenter-master、buildtools=cmake;prepare() 中拷贝 CMakeLists.txt、生成 include/emoji_segmenter/emoji_presentation_scanner.h 与 emoji_segmenter_test.c(完整 19 条用例)、必要时修补上游 .c 的 #include;build() 中 cmake 打开 BUILD_TEST=ON 并 make;package() 中拷贝 libemoji_segmenter.a、头文件与 emoji_segmenter_test 到 usr/emoji_segmenter/$ARCH/。
  • HPKCHECK:设备上进入 ${builddir}/${ARCH}-build,若有 ctest 则执行 ctest --output-on-failure -V,否则直接运行 ./emoji_segmenter_test,以退出码作为测试结果。

三、接入步骤

3.1 创建三方库目录与文件

Workspace/tpc_c_cplusplus/thirdparty/ 下新建目录 emoji_segmenter(注意下划线),并准备:

文件 作用
HPKBUILD 包名、版本、源码、prepare/build/package/cleanbuild;prepare 中生成头文件与测试源码
HPKCHECK 设备上优先 ctest,否则执行 ./emoji_segmenter_test
CMakeLists.txt 编 libemoji_segmenter.a、emoji_segmenter_test,enable_testing + add_test
README.OpenSource 开源协议(Apache-2.0)、上游地址、版本(JSON 多行格式)
README_zh.md 中文说明:简介、产物、编译、测试、API 用法等
.gitignore 忽略 emoji-segmenter-master.tar.gz、emoji-segmenter-master/、*.log、last_error
.gitattributes 指定 HPKBUILD、HPKCHECK 等为 text eol=lf,避免 CRLF 导致 source 报错

3.2 emoji_segmenter 的 HPKBUILD 配置要点

emoji_segmenter 取值 / 说明
pkgname emoji_segmenter(下划线,避免路径被拆)
pkgver master
source https://github.com/google/emoji-segmenter/archive/refs/heads/master.tar.gz
builddir emoji-segmenter-master
buildtools cmake
archs armeabi-v7a、arm64-v8a
depends
  • prepare():拷贝 ${_pkgdir}/CMakeLists.txt 到 builddir;在 builddir 下用 heredoc 生成 include/emoji_segmenter/emoji_presentation_scanner.h(typedef、scan_emoji_presentation 声明);再用 heredoc 生成 emoji_segmenter_test.c(字符类枚举与 19 条用例表,覆盖 text_run、text_emoji_run_with_vs、emoji_run_with_vs、emoji_run)。若上游 .c 中 #include 不完整则补全为 <stdbool.h>。最后 mkdir -p $ARCH-build
  • build():cmake 传入 -DBUILD_SHARED_LIBS=OFF-DBUILD_TEST=ON,在 $ARCH-build 中 make,得到 libemoji_segmenter.a 与 emoji_segmenter_test。
  • package():拷贝 libemoji_segmenter.a 到 usr/emoji_segmenter/$ARCH/lib/,头文件到 usr/emoji_segmenter/$ARCH/include/emoji_segmenter/,emoji_segmenter_test 到 usr/emoji_segmenter/$ARCH/bin/

build() 核心片段

PKG_CONFIG_LIBDIR="${pkgconfigpath}" ${OHOS_SDK}/native/build-tools/cmake/bin/cmake "$@" \
    -DOHOS_ARCH=$ARCH -B$ARCH-build -S. \
    -DBUILD_SHARED_LIBS=OFF \
    -DBUILD_TEST=ON \
    -L > $buildlog 2>&1
$MAKE VERBOSE=1 -C $ARCH-build >> $buildlog 2>&1

3.3 CMakeLists.txt 要点

  • 使用上游 emoji_presentation_scanner.c 编静态库,编译定义与上游 Makefile 一致:emoji_text_iter_t=char*EMOJI_LINKAGE=(空,以导出符号)。
  • BUILD_TEST=ON 时编 emoji_segmenter_test,并 enable_testing()add_test(NAME emoji_segmenter_test COMMAND emoji_segmenter_test),便于设备上 ctest 统一驱动。
  • 测试可执行文件设置 LINK_FLAGS "-static",避免设备上动态链接器路径问题。

3.4 HPKCHECK 设备侧测试逻辑

设备上进入 ${builddir}/${ARCH}-build(即 emoji-segmenter-master/armeabi-v7a-buildarm64-v8a-build):

  • 若存在 ctest 命令,则执行 ctest --output-on-failure -V
  • 若无 ctest,则执行 ./emoji_segmenter_test,以可执行文件退出码作为测试结果。

这样既支持 ctest 的“一条测试”展示(对应整份 19 条用例),也保证在无 ctest 环境下仍能跑通全部用例并看到逐条 [PASS]/[FAIL]。


四、编译流程与命令

  1. 进入 lycium 目录

    cd /path/to/tpc_c_cplusplus/lycium
    
  2. 执行编译(仅编译 emoji_segmenter,注意参数为下划线):

    ./build.sh emoji_segmenter
    
  3. 脚本将依次:检查 OHOS_SDK、下载/解压 emoji-segmenter-master、对每个 ARCH 执行 prepare → cmake → make → package,并写入 lycium/usr/hpk_build.csv

    image-20260212094422898

  4. 产物位置

    • 静态库与头文件:lycium/usr/emoji_segmenter/armeabi-v7a/lycium/usr/emoji_segmenter/arm64-v8a/(lib/libemoji_segmenter.a、include/emoji_segmenter/emoji_presentation_scanner.h)。
    • 测试可执行文件:lycium/usr/emoji_segmenter/<ARCH>/bin/emoji_segmenter_test,或构建目录内 thirdparty/emoji_segmenter/emoji-segmenter-master/<ARCH>-build/emoji_segmenter_test

五、设备侧测试

5.1 使用 ctest

将对应架构的构建目录(如 emoji-segmenter-master/arm64-v8a-build)拷贝到设备,在设备上进入该目录后执行:

image-20260212094451434

cd /path/to/emoji-segmenter-master/arm64-v8a-build
ctest

通过时输出示例:1/1 Test #1: emoji_segmenter_test ............. Passed。ctest 只登记了一条测试(一个可执行文件),该可执行文件内部运行 19 条用例。

5.2 直接运行测试可执行文件

emoji_segmenter_test 拷贝到设备后执行:

./emoji_segmenter_test

通过时输出示例:

Emoji Segmenter tests (UTS #51), 19 cases
  [PASS] text_run: EMOJI
  [PASS] text_run: EMOJI_TEXT_PRESENTATION
  ...
  [PASS] emoji_run: emoji_zwj_sequence
  [PASS] emoji_run: combining_enclosing_circle_backslash
All 19 tests passed.

image-20260212094509696

退出码 0 表示全部通过。ctest 与直接运行是同一套测试:ctest 负责执行该二进制并看退出码;二进制内部负责 19 条用例的打印与汇总。


六、遇到的问题与解决方案

问题一:路径被截断、HPKBUILD source 报 “command not found” / “syntax error near unexpected token”

现象:执行 ./build.sh emoji-segmenter 时出现多行 .../thirdparty/emoji: command not found 以及 syntax error near unexpected token '{'(prepare() 附近),导致 cleanbuild 等未定义。

原因:目录名 emoji-segmenter 中的连字符在部分脚本中未被引号包裹,被 shell 拆成两个词(emojisegmenter),路径被截断;若 HPKBUILD 再带 CRLF 换行,行尾 \r 会加剧解析错误。

解决

  1. thirdparty 下目录名改为 emoji_segmenter(下划线),HPKBUILD 中 pkgname=emoji_segmenter,编译时执行 ./build.sh emoji_segmenter
  2. 将 HPKBUILD、HPKCHECK 等统一为 Unix 换行(LF),例如:
    tr -d '\r' < HPKBUILD > HPKBUILD.tmp && mv HPKBUILD.tmp HPKBUILD
    
  3. 在仓库内添加 .gitattributes,指定 HPKBUILD text eol=lfHPKCHECK text eol=lf,避免再次引入 CRLF。

问题二:ctest 显示 1 个测试,而运行 emoji_segmenter_test 显示 19 条 [PASS],是否一致?

现象:设备上执行 ctest 得到 “1/1 Test #1: emoji_segmenter_test … Passed”;直接运行 ./emoji_segmenter_test 得到 19 行 [PASS]。

原因ctest 以“可执行文件”为粒度,我们只 add_test 了一条(COMMAND emoji_segmenter_test);emoji_segmenter_test 内部自己实现了 19 条用例并打印。两者是同一套测试:ctest 负责跑这一个二进制并看退出码,二进制负责 19 条逻辑与输出。

结论:结果一致;若希望 ctest 也显示 19 条,需在 CMake 中为每条用例单独 add_test(例如通过脚本或参数区分),当前采用“一条 ctest + 内部 19 条”的方式,便于维护。


问题三:设备上应在哪个目录执行 ctest?

现象:在 thirdparty/emoji_segmenter 或 emoji-segmenter-master 根目录执行 ctest,提示 “No test configuration file found!”。

原因:CTest 的配置文件在构建目录中生成,即 emoji-segmenter-master/armeabi-v7a-buildemoji-segmenter-master/arm64-v8a-build

解决:在设备上先进入对应架构的构建目录再执行 ctest 或 ./emoji_segmenter_test。HPKCHECK 已采用“先 cd 到 b u i l d d i r / {builddir}/ builddir/{ARCH}-build”再执行的方式。


七、总结

  • Google emoji-segmenter 在 OpenHarmony 上的交叉编译依赖 lycium 的 HPKBUILD/HPKCHECK 机制。上游无 CMake、无头文件、无测试,本适配通过自备 CMakeLists.txt、在 prepare() 中生成 头文件完整 UTS #51 测试套件(19 条用例),在宿主机完成全量构建,得到 libemoji_segmenter.aemoji_presentation_scanner.hemoji_segmenter_test
  • 典型问题:目录名连字符导致路径被拆,需改为 emoji_segmenter(下划线);HPKBUILD 的 CRLF 导致脚本解析错误,需统一为 LF 并可用 .gitattributes 固化;ctest 显示 1 条而程序内 19 条属同一套测试的不同展示层级;设备上测试需在构建目录内执行 ctest 或 emoji_segmenter_test。
  • 最终产物:lycium/usr/emoji_segmenter/<ARCH>/ 下的静态库与头文件可供应用集成(需自实现「码点 → 字符类」映射);emoji-segmenter-master/<ARCH>-build/emoji_segmenter_test 用于设备端功能验证,支持 ctest 或直接运行,19 条用例覆盖 UTS #51 主要语法。

若你在使用 lycium 接入其他无 CMake、无测试的上游 C 库时,可参考本文的“自备 CMake + prepare 内生成头文件与测试”的方式,以及目录命名、CRLF、ctest 与内部多用例的区分。更多鸿蒙三方库共建内容,欢迎在 开源鸿蒙跨平台社区 交流。


参考与相关链接

Logo

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

更多推荐