欢迎加入 开源鸿蒙跨平台社区,与开发者一起共建鸿蒙三方库的开源生态。
一、背景与目标
komrad36/CRC 是 GitHub 上的 Fast CRC32 开源库,提供多种 CRC-32C(多项式 0x82f63b78)实现,上游侧重 x86 性能(SSE、硬件指令等)。本文记录在 OpenHarmony 三方库共建仓库(tpc_c_cplusplus) 中,使用 lycium 交叉编译框架 将其可移植子集接入、编译、打包并在设备上运行测试的完整过程,以及遇到的问题与解决方案。
目标:
- 在 Mac/Linux 宿主机上交叉编译出适用于 OpenHarmony(armeabi-v7a / arm64-v8a)的静态库与测试可执行文件;
- 仅编译 naive C++ 与 tabular 等可移植实现,不包含 x86 汇编与硬件指令;
- 测试可在开发板上通过
./CRC32Test执行,输出[PASS]/[FAIL]及 "All N tests passed."。
二、环境与框架简介
环境搭建 更详细的步骤可参考: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 komrad36-CRC 上游特点
- 源码 :使用分支归档
https://github.com/komrad36/CRC/archive/refs/heads/master.tar.gz,解压后为CRC-master。 - 可移植部分 :
naive_methods_cpp.cpp(逐字节)、tabular_methods.cpp(查表 1/2/4/8/16 字节);不编 x86 汇编、_mm_crc32_*、golden_intel/amd 等。 - 构建 :上游为 Visual Studio 工程;在 lycium 中通过 prepare() 内 heredoc 生成
CRC/CMakeLists.txt、CRC/main_test.cpp、CRC/crc32.h,再使用 CMake 编译静态库与测试可执行文件CRC32Test。
2.3 HPKBUILD / HPKCHECK 在本库中的角色
- HPKBUILD :定义 pkgname=komrad36-CRC、source、builddir=CRC-master、archs、buildtools=cmake;prepare() 中
cd $builddir后通过多个cat > CRC/xxx << 'ENDxxx'生成 CMake 与测试源码,build() 中 cmake + make,package() 中拷贝libkomrad36_crc.a与crc32.h到usr,并删除源码压缩包 ;cleanbuild() 删除解压目录与压缩包。 - HPKCHECK :设备上进入
${builddir}/CRC/${ARCH}-build执行./CRC32Test,以退出码作为测试结果。
三、接入步骤
3.1 创建三方库目录与文件
在 Workspace/tpc_c_cplusplus/thirdparty/ 下新建目录 komrad36-CRC,并准备:
| 文件 | 作用 |
|---|---|
| HPKBUILD | 包名、源码、prepare/build/package/cleanbuild;prepare 中 heredoc 生成 CMake 与测试 |
| HPKCHECK | 设备上执行 ./CRC32Test |
| README.OpenSource | 开源协议、上游地址(JSON 多行格式) |
| README_zh.md | 中文说明:简介、产物、编译、测试、CRC 约定(init 0)等 |
| .gitignore | 忽略 CRC-master.tar.gz、CRC-master/、*.log、last_error |
3.2 HPKBUILD 配置要点与重要代码
在 2.3 的通用结构基础上,komrad36-CRC 的取值与差异如下。本库无独立 patch 文件 ,所有构建与测试相关文件均在 prepare() 内通过 heredoc 生成。
| 项 | komrad36-CRC 取值 / 说明 |
|---|---|
| pkgname | komrad36-CRC |
| pkgver | master(上游无 tag,用分支) |
| source | https://github.com/komrad36/CRC/archive/refs/heads/master.tar.gz |
| builddir | CRC-master |
| buildtools | cmake |
| archs | armeabi-v7a、arm64-v8a |
| depends | 无 |
prepare():heredoc 生成 CMake、测试源码与头文件
上游为 Visual Studio 工程,无现成 CMake。在 prepare() 中 cd $builddir 后,用三个 heredoc (<< 'ENDCMAKE' / 'ENDTEST' / 'ENDHEAD')分别写入 CRC/CMakeLists.txt、CRC/main_test.cpp、CRC/crc32.h。结束符必须单独占一行、行首无空格,且使用单引号形式避免变量展开。
CMakeLists.txt 关键内容(仅编可移植源文件、静态链接测试、注册 ctest):
bash
cat > CRC/CMakeLists.txt << 'ENDCMAKE'
cmake_minimum_required(VERSION 3.12)
project(komrad36_crc CXX)
set(PORTABLE_SOURCES naive_methods_cpp.cpp tabular_methods.cpp)
add_library(komrad36_crc STATIC ${PORTABLE_SOURCES})
target_include_directories(komrad36_crc PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
add_executable(CRC32Test main_test.cpp)
target_link_libraries(CRC32Test PRIVATE komrad36_crc)
# 静态链接,避免设备上动态链接器路径与 SDK 不一致导致 "No such file or directory"
set_target_properties(CRC32Test PROPERTIES LINK_FLAGS "-static")
enable_testing()
add_test(NAME CRC32Test COMMAND CRC32Test)
ENDCMAKE
main_test.cpp 关键内容(表驱动、期望值 0x58e3fa20):
本库 CRC 使用初始值 0 ,故 "123456789" 的期望值为 0x58e3fa20(非常见 0xe3069283)。测试仅调用 option_5 / option_6,避免 option_9/10 对 9 字节越界。
bash
cat > CRC/main_test.cpp << 'ENDTEST'
...
struct TestCase {
const char* name;
const char* data;
uint32_t len;
uint32_t expected_crc;
};
/* 本实现使用初始值 0,标准 0xe3069283 对应 init=0xFFFFFFFF */
static const TestCase kCases[] = {
{ "empty", "", 0, 0u },
{ "123456789", "123456789", 9, 0x58e3fa20u },
};
int main() {
printf("CRC32Test (init0, expect 123456789=0x58e3fa20)\n");
...
for (...) {
uint32_t r5 = option_5_naive_cpp(t->data, t->len);
uint32_t r6 = option_6_tabular_1_byte(t->data, t->len);
if (r5 == t->expected_crc && r6 == t->expected_crc)
printf("[PASS] %s\n", t->name);
else
printf("[FAIL] %s expected 0x%08x got ...\n", ...);
}
...
}
ENDTEST
crc32.h :声明 option_5_naive_cpp 以及 option_6~option_10_tabular_* 共六个接口(略,见仓库 HPKBUILD)。最后 mkdir -p CRC/$ARCH-build 并 cd ${OLDPWD}。
build()
源码与构建目录在 $builddir/CRC 下,cmake 使用 -BCRC/$ARCH-build -S CRC,make 在 CRC/$ARCH-build 中执行:
bash
build() {
cd $builddir
PKG_CONFIG_LIBDIR="${pkgconfigpath}" ${OHOS_SDK}/native/build-tools/cmake/bin/cmake "$@" \
-DOHOS_ARCH=$ARCH -BCRC/$ARCH-build -S CRC -L > $buildlog 2>&1
ret=$?
if [ $ret -ne 0 ]; then
cd $OLDPWD
return $ret
fi
$MAKE VERBOSE=1 -C CRC/$ARCH-build >> $buildlog 2>&1
ret=$?
cd $OLDPWD
return $ret
}
package()
拷贝静态库与头文件到 usr,并删除源码压缩包(便于后续自行打包):
bash
package() {
cd $builddir
mkdir -p $LYCIUM_ROOT/usr/$pkgname/$ARCH/lib
mkdir -p $LYCIUM_ROOT/usr/$pkgname/$ARCH/include
[ -f CRC/$ARCH-build/libkomrad36_crc.a ] && cp -f CRC/$ARCH-build/libkomrad36_crc.a $LYCIUM_ROOT/usr/$pkgname/$ARCH/lib/
[ -f CRC/crc32.h ] && cp -f CRC/crc32.h $LYCIUM_ROOT/usr/$pkgname/$ARCH/include/
cd ${OLDPWD}
rm -f ${PWD}/$packagename
}
cleanbuild()
删除解压目录与压缩包,保证下次编译重新下载或使用新包:
bash
cleanbuild() {
rm -rf ${PWD}/$builddir
rm -f ${PWD}/$packagename
}
HPKCHECK:设备上执行 CRC32Test
设备上进入 CRC-master/CRC/${ARCH}-build 后直接运行 ./CRC32Test,以退出码作为测试结果;不依赖 ctest,避免宿主机路径问题:
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}/CRC/${ARCH}-build
echo "start test times: `date`" >> ${logfile} 2>&1
if [ -f ./CRC32Test ]; then
./CRC32Test >> ${logfile} 2>&1
res=$?
else
echo "No CRC32Test executable, skip test" >> ${logfile} 2>&1
res=1
fi
cd $OLDPWD
echo "end test times: `date`" >> ${logfile} 2>&1
return $res
}
3.3 测试用例与 CRC 约定
- 测试在 prepare() 中生成的
main_test.cpp内,仅使用 option_5_naive_cpp 与 option_6_tabular_1_byte(按字节处理,无对齐与越界问题);option_9/option_10 按 8/16 字节整块读取,对长度 9 会越界导致 SIGSEGV,故不在测试中调用。 - 本库 CRC 使用初始值 0 ,因此
"123456789"的正确结果为 0x58e3fa20 ,而非常见文档中"初始值 0xFFFFFFFF"下的 0xe3069283。测试期望值需写 0x58e3fa20。
四、编译流程与命令
-
进入 lycium 目录:
bashcd /path/to/tpc_c_cplusplus/lycium -
执行编译(仅编译 komrad36-CRC):
bash./build.sh komrad36-CRC
-
产物位置:
- 静态库与头文件:
lycium/usr/komrad36-CRC/armeabi-v7a/、lycium/usr/komrad36-CRC/arm64-v8a/(lib、include)。 - 测试可执行文件:
thirdparty/komrad36-CRC/CRC-master/CRC/<ARCH>-build/CRC32Test,需拷贝到设备后执行。 - 编译成功后源码压缩包
CRC-master.tar.gz会被自动删除。 
- 静态库与头文件:
五、遇到的问题与解决方案
问题一:HPKBUILD 报 "syntax error near unexpected token {" / "cleanbuild: command not found"
现象 :执行 ./build.sh komrad36-CRC 时出现 HPKBUILD: line 24: syntax error near unexpected token '{'、prepare() {,以及 cleanbuild: command not found;路径被截断为 komrad3 与 6-CRC。
原因 :
① heredoc 结束符若带前导空格或非单独一行,shell 不认为结束,后续 prepare() { 被当作 heredoc 内容,导致语法错,source 未完成,cleanbuild 未定义。
② 路径中含 komrad36-CRC 时,未加引号的变量在部分 shell 下会把 36 解析为文件描述符重定向 (如 36>),路径被拆成 komrad3 与 6-CRC。
③ HPKBUILD 使用 Windows 换行(CRLF) 时,heredoc 结束行实际为 ENDCMAKE\r,与 ENDCMAKE 不匹配,heredoc 不结束,同样导致解析错乱。
解决 :
① 三个 heredoc 使用结束符 ENDCMAKE / ENDTEST / ENDHEAD,单独占一行、行首无空格 ,且使用 << 'DELIM' 形式。
② 在 lycium/build.sh 与 lycium/script/build_hpk.sh 中,对所有涉及路径的变量与数组加双引号 (如 source "${PWD}/HPKBUILD"、cd "${notdonelist[$i]}"、hpkPaths[...]="$job" 等);build.sh 开头增加"非 bash 则用 bash 重新执行"的逻辑,避免 sh 下数组与引号解析差异。
③ 将 HPKBUILD 转为 Unix 换行(LF) :
sed 's/\r$//' HPKBUILD > HPKBUILD.tmp && mv HPKBUILD.tmp HPKBUILD
问题二:package() 报 "cp: -r not specified; omitting directory"
现象:编译日志中出现 "cp: -r not specified; omitting directory" 的警告。
原因 :package() 中 cp 命令误写了两个目标目录(重复的 $LYCIUM_ROOT/usr/$pkgname/$ARCH/lib/),cp 将第二个参数当作目录导致该警告。
解决 :只保留一个目标目录,例如:
cp -f CRC/$ARCH-build/libkomrad36_crc.a $LYCIUM_ROOT/usr/$pkgname/$ARCH/lib/
问题三:设备上运行 CRC32Test 报 "No such file or directory"(文件存在)
现象 :设备上 ls 能看到 CRC32Test,但执行 ./CRC32Test 提示 "No such file or directory"。
原因 :可执行文件为动态链接 ,其 ELF 中记录的动态链接器路径 (如 /lib/ld-musl-*.so)与设备上的实际路径不一致,内核在加载时找不到解释器,从而报"找不到文件"(实为找不到解释器)。
解决 :在 CMake 中为 CRC32Test 增加静态链接 :
set_target_properties(CRC32Test PROPERTIES LINK_FLAGS "-static")
重新编译后,将新的 CRC32Test 拷贝到设备再运行。
问题四:设备上运行 CRC32Test 报 Signal 7 或 Signal 11
现象 :在 aarch64 设备上运行 armeabi-v7a-build 下的 CRC32Test 报 Signal 7 (SIGBUS);或运行 arm64-v8a-build 下的 CRC32Test 报 Signal 11(SIGSEGV)。
原因:
- Signal 7:设备为 aarch64(64 位) ,却运行了 armeabi-v7a(32 位) 的二进制,架构不匹配。
- Signal 11:测试中对
"123456789"(9 字节)调用了 option_9_tabular_8_bytes 或 option_10_tabular_16_bytes ,二者按 8/16 字节整块读取且不检查剩余长度,会越界导致 SIGSEGV。
解决:
- 在 aarch64 设备上只运行 arm64-v8a-build 下的 CRC32Test,不要运行 armeabi-v7a-build。
- 测试中仅调用 option_5 与 option_6(按字节处理),不调用 option_9/option_10;或仅对长度≥16 的缓冲区测试 option_10。
问题五:测试一直显示 "[FAIL] 123456789 expected 0xe3069283 got naive=0x58e3fa20 tabular=0x58e3fa20"
现象:已把 HPKBUILD 中期望值改为 0x58e3fa20,但设备上仍输出 "expected 0xe3069283"。
原因 :
① 期望值写错 :本库 CRC 使用初始值 0 ,"123456789" 的正确结果是 0x58e3fa20 ;常见文档中的 0xe3069283 对应的是"初始值 0xFFFFFFFF"的 CRC-32C,不能混用。
② 设备上跑的是旧二进制 :HPKBUILD 中的测试源码是在 prepare() 里生成的;修改 HPKBUILD 后必须重新完整编译 (会先 clean 再解压、prepare、生成新的 main_test.cpp 并编译),再把新生成的 CRC32Test 拷贝到设备。若未重新拷贝,设备上仍是旧的 CRC32Test,其内嵌的期望值仍是 0xe3069283。
解决 :
① 在 HPKBUILD 的测试用例中,将 "123456789" 的期望值设为 0x58e3fa20 ,并在注释中说明"本实现使用初始值 0"。
② 修改 HPKBUILD 后执行一次完整 ./build.sh komrad36-CRC,从 thirdparty/komrad36-CRC/CRC-master/CRC/<ARCH>-build/CRC32Test 拷贝新 的 CRC32Test 到设备后再执行。
③ 可在测试程序开头增加一行固定输出(如 printf("CRC32Test (init0, expect 123456789=0x58e3fa20)\n");),用于确认设备上运行的是新版本二进制。
六、设备侧测试简要步骤
-
将 arm64-v8a-build (aarch64 设备)或 armeabi-v7a-build (32 位 ARM 设备)下的 CRC32Test 拷贝到开发板,例如:
bashhdc file send .../CRC-master/CRC/arm64-v8a-build/CRC32Test /data/local/tmp/ hdc shell chmod +x /data/local/tmp/CRC32Test -
在设备上执行:
bash/data/local/tmp/CRC32Test -
通过时输出示例:
textCRC32Test (init0, expect 123456789=0x58e3fa20) [PASS] empty [PASS] 123456789 All 2 tests passed.
七、总结
- komrad36-CRC 在 OpenHarmony 上的交叉编译依赖 lycium 的 HPKBUILD/HPKCHECK;通过 prepare() 内 heredoc 生成 CMake、测试源码与头文件,无需维护独立 patch 文件,即可完成可移植子集的构建与测试。
- 典型问题 :HPKBUILD 的 CRLF、heredoc 结束符、路径中
36被解析为重定向导致路径被拆;设备上动态链接器路径不一致(No such file or directory)、架构/二进制不一致(Signal 7)、测试越界(Signal 11)、期望值与本库"初始值 0"不一致(0xe3069283 vs 0x58e3fa20)、以及修改 HPKBUILD 后未重新编译与拷贝导致的"旧二进制"现象,均可通过换行符统一、路径加引号、静态链接、仅测 option_5/6、修正期望值为 0x58e3fa20 并重新拷贝新二进制等方式规避。 - 最终产物:lycium/usr/komrad36-CRC/
<ARCH>/ 下的静态库与头文件可供应用集成;CRC-master/CRC/<ARCH>-build/CRC32Test 用于设备端功能验证。
若你在使用 lycium 接入其他 C/C++ 三方库时遇到"路径被拆""heredoc 解析错""设备上找不到可执行文件"或"测试期望值与实现约定不一致"等问题,可参考本文的排查思路与修改方式。更多 OpenHarmony 跨平台与三方库共建内容,欢迎在 开源鸿蒙跨平台社区 交流。
参考与相关链接
- komrad36/CRC 上游:https://github.com/komrad36/CRC
- tpc_c_cplusplus / lycium:OpenHarmony 三方库 C/C++ 共建仓库及交叉编译框架
- 环境搭建 :OpenHarmony 交叉编译环境配置
- 开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net/