三方库 Emoji Segmenter 在 OpenHarmony 的 lycium 适配与测试
本文介绍了将Google开源的emoji-segmenter库适配到OpenHarmony平台的完整过程。该C语言库基于Ragel语法生成,用于按Unicode标准切分文本与emoji。通过lycium交叉编译框架,提供了CMake支持、头文件适配和19条UTS #51测试用例,最终生成静态库和测试程序。详细说明了环境搭建、目录命名规范、构建脚本配置等关键步骤,确保在OpenHarmony设备上通
欢迎加入 开源鸿蒙跨平台社区,与开发者一起共建鸿蒙三方库的开源生态。
一、背景与目标
emoji-segmenter(google/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;需安装
gcc、g++、cmake、make、pkg-config、wget等。 - OHOS SDK:需包含
native/llvm、native/build-tools/cmake、native/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 编出静态库。
- API:
scan_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 按单词拆成
emoji与segmenter,导致路径被截断、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-build 或 arm64-v8a-build):
- 若存在 ctest 命令,则执行
ctest --output-on-failure -V; - 若无 ctest,则执行
./emoji_segmenter_test,以可执行文件退出码作为测试结果。
这样既支持 ctest 的“一条测试”展示(对应整份 19 条用例),也保证在无 ctest 环境下仍能跑通全部用例并看到逐条 [PASS]/[FAIL]。
四、编译流程与命令
-
进入 lycium 目录:
cd /path/to/tpc_c_cplusplus/lycium -
执行编译(仅编译 emoji_segmenter,注意参数为下划线):
./build.sh emoji_segmenter -
脚本将依次:检查 OHOS_SDK、下载/解压 emoji-segmenter-master、对每个 ARCH 执行 prepare → cmake → make → package,并写入
lycium/usr/hpk_build.csv。
-
产物位置:
- 静态库与头文件:
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)拷贝到设备,在设备上进入该目录后执行:

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.

退出码 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 拆成两个词(emoji 与 segmenter),路径被截断;若 HPKBUILD 再带 CRLF 换行,行尾 \r 会加剧解析错误。
解决:
- 将 thirdparty 下目录名改为 emoji_segmenter(下划线),HPKBUILD 中 pkgname=emoji_segmenter,编译时执行
./build.sh emoji_segmenter。 - 将 HPKBUILD、HPKCHECK 等统一为 Unix 换行(LF),例如:
tr -d '\r' < HPKBUILD > HPKBUILD.tmp && mv HPKBUILD.tmp HPKBUILD - 在仓库内添加 .gitattributes,指定
HPKBUILD text eol=lf、HPKCHECK 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-build 或 emoji-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.a、emoji_presentation_scanner.h 与 emoji_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 与内部多用例的区分。更多鸿蒙三方库共建内容,欢迎在 开源鸿蒙跨平台社区 交流。
参考与相关链接
- emoji-segmenter 上游:https://github.com/google/emoji-segmenter(Apache-2.0)
- Unicode UTS #51(Emoji):Unicode Technical Standard #51
- tpc_c_cplusplus / lycium:OpenHarmony 三方库 C/C++ 共建仓库及交叉编译框架
- 环境搭建:OpenHarmony 交叉编译环境配置
- 开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net/
- AtomGit 仓库地址:https://atomgit.com/oh-tpc/emoji-segmenter
更多推荐
所有评论(0)