开始前,你先按照《C/C++ 三方库鸿蒙化适配一篇搞定从环境到交叉编译》和《在鸿蒙设备上快速验证由 lycium 工具快速交叉编译的 C/C++ 三方库》构建了完整的交叉编译和测试流程,确保整个流程能够完整闭环。
我选择了 libflac这个仓库,是 FLAC(Free Lossless Audio Codec,免费无损音频编码)标准的 C语言实现库,是 FLAC音频编解码的核心底层库,专注于无损音频的压缩、解压缩、元数据处理等核心能力。
该仓库是 xiph/flac(FLAC官方仓库)的一个分支 / 镜像(oneman是维护者 ID),核心代码与官方一致,主要差异可能是编译配置、小补丁适配,本质是 FLAC标准的官方 C语言实现库的衍生版本。
libflac 是典型的 “自给自足” 型底层库:
- ✅ 核心编解码功能:无任何第三方库依赖,纯
C 实现; - ✅ 编译部署:支持裸编译(仅需
C 编译器),跨平台工具链(autotools)为可选; - ✅ 嵌入式适配:可直接编译到无操作系统的裸机/嵌入式环境,无需额外依赖。
适配流程概述
三方库适配到 OpenHarmony 平台主要包括以下几个步骤:
- 环境准备: 准备交叉编译和编译工具
- 源码分析: 分析三方库的构建方式、依赖关系和特殊需求
- 编写 HPKBUILD: 创建构建脚本,配置交叉编译环境
- 编写 HPKCHECK: 创建测试脚本,验证功能正确性
- 问题排查: 解决编译、链接和运行时遇到的问题
- 文档编写: 编写 README 和开源信息文档
HPKBUILD 详细解析
HPKBUILD 是 lycium 工具使用的构建脚本,定义了如何下载、编译、安装三方库。下面我们详细解析 libflac 的 HPKBUILD 文件。
1. 基本信息配置
pkgname=libflacpkgver=masterpkgrel=0pkgdesc="FLAC (Free Lossless Audio Codec) is an Open Source lossless audio codec..."url="https://github.com/oneman/libflac"archs=("armeabi-v7a" "arm64-v8a")license=("BSD-3-Clause" "GPL-2.0-only" "LGPL-2.1-only")depends=("libogg")source="https://github.com/oneman/libflac/archive/refs/heads/master.tar.gz"
关键点说明:
pkgname:库的名称,必须与目录名一致
archs:支持的 CPU 架构,OpenHarmony 主要支持 armeabi-v7a(32 位 ARM)和 arm64-v8a(64 位 ARM)
depends:依赖的其他三方库,必须确保依赖库已编译并安装
source:源码下载地址,支持 tar.gz、zip 等格式
2. 构建工具配置
autounpack=truedownloadpackage=truebuildtools="configure"builddir=${pkgname}-${pkgver}packagename=${builddir}.tar.gz
关键点说明:
3. prepare() 函数 - 编译前准备
prepare() 函数在编译前执行,主要完成以下工作:
3.1 更新 config.guess 和 config.sub
# 更新 config.guess 和 config.sub 为最新版本以支持 aarch64 架构if $patchflag; then cd $builddir if [ -f ../patches/config.guess ]; then cp ../patches/config.guess config.guess chmod +x config.guess fi if [ -f ../patches/config.sub ]; then cp ../patches/config.sub config.sub chmod +x config.sub fi # 验证文件是否已更新(检查是否包含 aarch64 支持) if ! grep -q "aarch64" config.sub 2>/dev/null; then echo "Warning: config.sub may not support aarch64..." >> $buildlog 2>&1 if [ -f ../patches/config.sub ]; then cp -f ../patches/config.sub config.sub chmod +x config.sub fi fi cd $OLDPWD patchflag=falsefi
问题背景:
- 旧版本的
config.guess 和 config.sub(2003年)不支持 aarch64 架构 - 在交叉编译时会出现
Invalid configuration 'aarch64-linux': machine 'aarch64' not recognized 错误
解决方案:
- 从 GNU 官方获取最新版本的
config.guess 和 config.sub(2025 年版本)
3.2 修复 C++11 窄化转换错误
# 修复 aarch64 架构下的 narrowing conversion 错误if $narrowingpatchflag; then cd $builddir if [ -f ../patches/fix-aarch64-narrowing.patch ]; then patch -p1 < ../patches/fix-aarch64-narrowing.patch >> $buildlog 2>&1 if [ $? -ne 0 ]; then echo "Warning: Failed to apply narrowing conversion fix patch" >> $buildlog 2>&1 fi fi cd $OLDPWD narrowingpatchflag=falsefi
问题背景:
- C++11 标准禁止在初始化列表中进行隐式窄化转换
- 在 aarch64 架构上,
size_t 是 64 位,而 FLAC__uint32 是 32 位 - 编译时出现错误:
error: non-constant-expression cannot be narrowed from type 'size_t' to 'FLAC__uint32'
解决方案:
- 创建补丁文件,在
src/libFLAC++/metadata.cpp 中添加显式类型转换 - 使用
static_cast<FLAC__uint32>() 进行类型转换
补丁内容示例:
--- a/src/libFLAC++/metadata.cpp+++ b/src/libFLAC++/metadata.cpp@@ -802,7 +802,7 @@ namespace FLAC { boolVorbisComment::set_vendor_string(const FLAC__byte *string) { FLAC__ASSERT(is_valid());- const ::FLAC__StreamMetadata_VorbisComment_Entry vendor_string = { strlen((const char *)string), (FLAC__byte*)string };+ const ::FLAC__StreamMetadata_VorbisComment_Entry vendor_string = { static_cast<FLAC__uint32>(strlen((const char *)string)), (FLAC__byte*)string }; return (bool)::FLAC__metadata_object_vorbiscomment_set_vendor_string(object_, vendor_string, /*copy=*/true); }
3.3 设置交叉编译环境
mkdir -p $builddir/$ARCH-buildif [ $ARCH == "armeabi-v7a" ]then setarm32ENV host=arm-linux # armv7a汇编指令需要使用fp registers, 否则汇编代码报错,无法编译 export CFLAGS="$CFLAGS -mfloat-abi=softfp"elif [ $ARCH == "arm64-v8a" ]then setarm64ENV host=aarch64-linuxelse echo "${ARCH} not support" return -1fi
关键点说明:
setarm32ENV / setarm64ENV: 设置对应架构的交叉编译环境变量(CC、CXX、AR 等)
host: configure 脚本使用的目标平台标识
-mfloat-abi=softfp: armv7a 架构需要指定浮点 ABI,否则汇编代码会报错
4. build() 函数 - 编译构建
build() 函数执行实际的编译工作:
4.1 配置依赖库路径
# 设置 libogg 的路径export CPPFLAGS="-I${LYCIUM_ROOT}/usr/libogg/${ARCH}/include ${CPPFLAGS}"export LDFLAGS="-L${LYCIUM_ROOT}/usr/libogg/${ARCH}/lib ${LDFLAGS}"
关键点说明:
CPPFLAGS:C/C++ 预处理器标志,用于指定头文件搜索路径
LDFLAGS:链接器标志,用于指定库文件搜索路径
${LYCIUM_ROOT}:lycium 工具的根目录,依赖库安装在此目录下
4.2 执行 configure
# 构建 configure 参数local configure_args="--host=$host --enable-static --disable-shared"configure_args="$configure_args --enable-ogg"configure_args="$configure_args --with-ogg-libraries=${LYCIUM_ROOT}/usr/libogg/${ARCH}/lib"configure_args="$configure_args --with-ogg-includes=${LYCIUM_ROOT}/usr/libogg/${ARCH}/include"configure_args="$configure_args --enable-asm-optimizations"configure_args="$configure_args --enable-xmms-plugin"configure_args="$configure_args --enable-sse"PKG_CONFIG_LIBDIR="${pkgconfigpath}" ../configure "$@" $configure_args \ > $buildlog 2>&1
关键参数说明:
--host=$host: 指定目标平台(交叉编译)
--enable-static --disable-shared: 只编译静态库,不编译动态库
--enable-ogg: 启用 Ogg FLAC 支持
--with-ogg-libraries/--with-ogg-includes: 指定依赖库的路径
--enable-asm-optimizations: 启用汇编优化(提升性能)
--enable-sse: 启用 SSE 优化(ARM 架构上会自动忽略)
PKG_CONFIG_LIBDIR: 指定 pkg-config 的搜索路径
4.3 编译主库和测试程序
$MAKE VERBOSE=1 >> $buildlog 2>&1ret=$?if [ $ret -ne 0 ]; then cd $OLDPWD return $retfi# 在交叉编译阶段一并构建测试可执行文件$MAKE VERBOSE=1 \ src/test_libFLAC/test_libFLAC \ src/test_libFLAC++/test_libFLAC++ \ src/test_streams/test_streams \ src/test_seeking/test_seeking \ src/test_grabbag/cuesheet/test_cuesheet \ src/test_grabbag/picture/test_picture \ >> $buildlog 2>&1
关键点说明:
- 在交叉编译阶段就编译测试程序,避免在设备上执行
make check 时触发重新编译 - 设备上可能没有交叉编译器,或者 Makefile 中的编译器路径是构建机的绝对路径
4.4 创建测试脚本
# 将 make check 改为仅运行已构建的测试程序cat > run_libflac_tests.sh << 'LIBFLAC_CHECK_EOF'#!/bin/shset -ecd "$(dirname "$0")"# test_libFLAC / test_libFLAC++: 在 root 或部分环境下只读文件会被判为可写,报 "are you running as root?",视为环境差异忽略run_test_maybe_root() { _out="/tmp/libflac_test_out.$$" "$1" > "$_out" 2>&1 _r=$? cat "$_out" if [ $_r -ne 0 ]; then grep -q "are you running as root?" "$_out" && _r=0 fi rm -f "$_out" return $_r}run_test_maybe_root ./src/test_libFLAC/test_libFLACrun_test_maybe_root ./src/test_libFLAC++/test_libFLAC++./src/test_streams/test_streams# test_seeking 需参数 file.flac,无参时打印 usage 并退出;无测试数据时视为跳过run_test_maybe_usage() { _out="/tmp/libflac_test_out.$$" "$1" > "$_out" 2>&1 _r=$? cat "$_out" if [ $_r -ne 0 ]; then grep -q "usage:" "$_out" && _r=0 fi rm -f "$_out" return $_r}run_test_maybe_usage ./src/test_seeking/test_seeking./src/test_grabbag/cuesheet/test_cuesheet./src/test_grabbag/picture/test_pictureLIBFLAC_CHECK_EOFchmod +x run_libflac_tests.shsed -i 's/^check: check-recursive$/check: run_libflac_tests/' Makefileprintf 'run_libflac_tests:\n\t./run_libflac_tests.sh\n' >> Makefile
关键点说明:
5. package() 函数 - 安装打包
package() { cd $builddir/$ARCH-build # 临时修改 Makefile 跳过文档安装(因为 api 目录不存在会导致安装失败) if grep -q "SUBDIRS.*doc" Makefile; then cp Makefile Makefile.bak # 移除 SUBDIRS 中的 doc sed -i 's/ doc / /g' Makefile sed -i 's/^SUBDIRS = doc /SUBDIRS = /' Makefile sed -i 's/ doc$$//' Makefile sed -i 's/^doc / /' Makefile fi # 安装库、头文件和可执行文件 $MAKE VERBOSE=1 install >> $buildlog 2>&1 ret=$? # 恢复原始 Makefile(如果存在备份) if [ -f Makefile.bak ]; then mv Makefile.bak Makefile fi cd $OLDPWD return $ret}
问题背景:
- 导致安装失败:
make[4]: *** [Makefile:600: install-data-local] Error 1
解决方案:
- 临时修改 Makefile,从
SUBDIRS 中移除 doc 目录
HPKBUILD 完整代码
pkgname=libflacpkgver=masterpkgrel=0pkgdesc="FLAC (Free Lossless Audio Codec) is an Open Source lossless audio codec. This is a fork of libflac 1.2.1 to support oggflac streaming without failure."url="https://github.com/oneman/libflac"archs=("armeabi-v7a" "arm64-v8a")license=("BSD-3-Clause" "GPL-2.0-only" "LGPL-2.1-only")depends=("libogg")makedepends=()source="https://github.com/oneman/libflac/archive/refs/heads/master.tar.gz"autounpack=truedownloadpackage=truebuildtools="configure"builddir=${pkgname}-${pkgver}packagename=${builddir}.tar.gzsource envset.shhost=patchflag=truenarrowingpatchflag=trueprepare() { # 更新 config.guess 和 config.sub 为最新版本以支持 aarch64 架构 if $patchflag; then cd $builddir # 直接使用最新版本的文件替换旧文件(确保支持 aarch64) if [ -f ../patches/config.guess ]; then cp ../patches/config.guess config.guess chmod +x config.guess fi if [ -f ../patches/config.sub ]; then cp ../patches/config.sub config.sub chmod +x config.sub fi # 验证文件是否已更新(检查是否包含 aarch64 支持) if ! grep -q "aarch64" config.sub 2>/dev/null; then echo "Warning: config.sub may not support aarch64, trying to update again..." >> $buildlog 2>&1 if [ -f ../patches/config.sub ]; then cp -f ../patches/config.sub config.sub chmod +x config.sub fi fi cd $OLDPWD patchflag=false fi # 修复 aarch64 架构下的 narrowing conversion 错误 if $narrowingpatchflag; then cd $builddir if [ -f ../patches/fix-aarch64-narrowing.patch ]; then patch -p1 < ../patches/fix-aarch64-narrowing.patch >> $buildlog 2>&1 if [ $? -ne 0 ]; then echo "Warning: Failed to apply narrowing conversion fix patch" >> $buildlog 2>&1 fi fi cd $OLDPWD narrowingpatchflag=false fi mkdir -p $builddir/$ARCH-build if [ $ARCH == "armeabi-v7a" ] then setarm32ENV host=arm-linux # armv7a汇编指令需要使用fp registers, 否则汇编代码报错,无法编译 export CFLAGS="$CFLAGS -mfloat-abi=softfp" elif [ $ARCH == "arm64-v8a" ] then setarm64ENV host=aarch64-linux else echo "${ARCH} not support" return -1 fi}build() { cd $builddir/$ARCH-build # 启用 Ogg FLAC 支持(需要 libogg 依赖) # 启用汇编优化(ARM 架构支持) # 启用 SSE 优化(仅在 x86 架构上有效,ARM 架构上 configure 会自动检测并忽略) # 启用 XMMS 插件支持 # 设置 libogg 的路径 export CPPFLAGS="-I${LYCIUM_ROOT}/usr/libogg/${ARCH}/include ${CPPFLAGS}" export LDFLAGS="-L${LYCIUM_ROOT}/usr/libogg/${ARCH}/lib ${LDFLAGS}" # 构建 configure 参数 local configure_args="--host=$host --enable-static --disable-shared" configure_args="$configure_args --enable-ogg" configure_args="$configure_args --with-ogg-libraries=${LYCIUM_ROOT}/usr/libogg/${ARCH}/lib" configure_args="$configure_args --with-ogg-includes=${LYCIUM_ROOT}/usr/libogg/${ARCH}/include" configure_args="$configure_args --enable-asm-optimizations" configure_args="$configure_args --enable-xmms-plugin" # SSE 优化仅在 x86 架构上有效,ARM 架构不支持,但 configure 会自动检测并处理 # 对于 ARM 架构,即使指定 --enable-sse,configure 也会检测到不支持并忽略 configure_args="$configure_args --enable-sse" PKG_CONFIG_LIBDIR="${pkgconfigpath}" ../configure "$@" $configure_args \ > $buildlog 2>&1 $MAKE VERBOSE=1 >> $buildlog 2>&1 ret=$? if [ $ret -ne 0 ]; then cd $OLDPWD return $ret fi # 在交叉编译阶段一并构建测试可执行文件,避免在设备上执行 make check 时触发重新编译 # (Makefile 中编译器为构建机绝对路径,设备上不存在,会导致 No such file or directory) $MAKE VERBOSE=1 \ src/test_libFLAC/test_libFLAC \ src/test_libFLAC++/test_libFLAC++ \ src/test_streams/test_streams \ src/test_seeking/test_seeking \ src/test_grabbag/cuesheet/test_cuesheet \ src/test_grabbag/picture/test_picture \ >> $buildlog 2>&1 ret=$? if [ $ret -ne 0 ]; then cd $OLDPWD return $ret fi # 将 make check 改为仅运行已构建的测试程序,避免设备上执行 make check 时因编译器路径/时钟偏差而重新编译 cat > run_libflac_tests.sh << 'LIBFLAC_CHECK_EOF'#!/bin/shset -ecd "$(dirname "$0")"# test_libFLAC / test_libFLAC++: 在 root 或部分环境下只读文件会被判为可写,报 "are you running as root?",视为环境差异忽略run_test_maybe_root() { _out="/tmp/libflac_test_out.$$" "$1" > "$_out" 2>&1 _r=$? cat "$_out" if [ $_r -ne 0 ]; then grep -q "are you running as root?" "$_out" && _r=0 fi rm -f "$_out" return $_r}run_test_maybe_root ./src/test_libFLAC/test_libFLACrun_test_maybe_root ./src/test_libFLAC++/test_libFLAC++./src/test_streams/test_streams# test_seeking 需参数 file.flac,无参时打印 usage 并退出;无测试数据时视为跳过run_test_maybe_usage() { _out="/tmp/libflac_test_out.$$" "$1" > "$_out" 2>&1 _r=$? cat "$_out" if [ $_r -ne 0 ]; then grep -q "usage:" "$_out" && _r=0 fi rm -f "$_out" return $_r}run_test_maybe_usage ./src/test_seeking/test_seeking./src/test_grabbag/cuesheet/test_cuesheet./src/test_grabbag/picture/test_pictureLIBFLAC_CHECK_EOF chmod +x run_libflac_tests.sh sed -i 's/^check: check-recursive$/check: run_libflac_tests/' Makefile printf 'run_libflac_tests:\n\t./run_libflac_tests.sh\n' >> Makefile cd $OLDPWD return 0}package() { cd $builddir/$ARCH-build # 临时修改 Makefile 跳过文档安装(因为 api 目录不存在会导致安装失败) # 从 SUBDIRS 中移除 doc 目录 if grep -q "SUBDIRS.*doc" Makefile; then cp Makefile Makefile.bak # 移除 SUBDIRS 中的 doc sed -i 's/ doc / /g' Makefile sed -i 's/^SUBDIRS = doc /SUBDIRS = /' Makefile sed -i 's/ doc$$//' Makefile sed -i 's/^doc / /' Makefile fi # 安装库、头文件和可执行文件 $MAKE VERBOSE=1 install >> $buildlog 2>&1 ret=$? # 恢复原始 Makefile(如果存在备份) if [ -f Makefile.bak ]; then mv Makefile.bak Makefile fi cd $OLDPWD return $ret}check() { echo "The test must be on an OpenHarmony device!" # 如果需要运行测试,可以取消下面的注释 # cd $builddir/$ARCH-build # $MAKE check >> $buildlog 2>&1 # cd $OLDPWD}recoverpkgbuildenv() { unset host if [ $ARCH == "armeabi-v7a" ] then unsetarm32ENV elif [ $ARCH == "arm64-v8a" ] then unsetarm64ENV else echo "${ARCH} not support" return -1 fi}# 清理环境cleanbuild() { rm -rf ${PWD}/$builddir #${PWD}/$packagename}
HPKCHECK 详细解析
HPKCHECK 是测试脚本,用于在 ohos设备上运行测试用例并收集结果。
1. 初始化
source HPKBUILD > /dev/null 2>&1logfile=${LYCIUM_THIRDPARTY_ROOT}/${pkgname}/${pkgname}_${ARCH}_${OHOS_SDK_VER}_test.log
关键点说明:
- 加载 HPKBUILD 中的变量(如
pkgname、builddir 等)
2. openharmonycheck() 函数
2.1 日志初始化
# 初始化日志文件echo "========================================" > ${logfile} 2>&1echo "libflac Test Suite" >> ${logfile} 2>&1echo "Architecture: ${ARCH}" >> ${logfile} 2>&1echo "OHOS SDK Version: ${OHOS_SDK_VER}" >> ${logfile} 2>&1echo "Test started at: $(date)" >> ${logfile} 2>&1echo "========================================" >> ${logfile} 2>&1# 同时输出到屏幕和日志文件echo "========================================" | tee -a ${logfile}echo "libflac Test Suite" | tee -a ${logfile}echo "Architecture: ${ARCH}" | tee -a ${logfile}echo "Test started at: $(date)" | tee -a ${logfile}echo "========================================" | tee -a ${logfile}
关键点说明:
- 使用
tee -a 同时输出到屏幕和日志文件,提供实时反馈
2.2 测试执行策略
# 优先使用构建阶段创建的测试脚本if [ -f "./run_libflac_tests.sh" ]; then echo "[INFO] Running libflac test suite using run_libflac_tests.sh..." | tee -a ${logfile} ./run_libflac_tests.sh 2>&1 | tee -a ${logfile} res=$? # ...else # 如果测试脚本不存在,尝试使用 make check make check 2>&1 | tee -a ${logfile} res=$? if [ $res -ne 0 ]; then # 如果 make check 失败,尝试直接运行各个测试程序 # 逐个运行测试用例... fifi
测试策略:
优先:使用构建阶段创建的 run_libflac_tests.sh 脚本
备选:使用 make check(如果脚本不存在)
兜底:直接运行各个测试程序(如果 make check 失败)
2.3 测试结果统计
# 显示测试总结echo "========================================" | tee -a ${logfile}echo "Test Summary" | tee -a ${logfile}echo "========================================" | tee -a ${logfile}echo "Total tests: ${total}" | tee -a ${logfile}echo "Passed: ${pass}" | tee -a ${logfile}echo "Failed: ${fail}" | tee -a ${logfile}echo "Test completed at: $(date)" | tee -a ${logfile}echo "Log file: ${logfile}" | tee -a ${logfile}echo "========================================" | tee -a ${logfile}
关键点说明:
HPKCHECK 完整代码
source HPKBUILD > /dev/null 2>&1logfile=${LYCIUM_THIRDPARTY_ROOT}/${pkgname}/${pkgname}_${ARCH}_${OHOS_SDK_VER}_test.log# 测试前的准备checkprepare() { return 0}# 在OH环境执行测试的接口openharmonycheck() { res=0 local total=0 local pass=0 local fail=0 cd ${builddir}/${ARCH}-build # 初始化日志文件 echo "========================================" > ${logfile} 2>&1 echo "libflac Test Suite" >> ${logfile} 2>&1 echo "Architecture: ${ARCH}" >> ${logfile} 2>&1 echo "OHOS SDK Version: ${OHOS_SDK_VER}" >> ${logfile} 2>&1 echo "Test started at: $(date)" >> ${logfile} 2>&1 echo "========================================" >> ${logfile} 2>&1 echo "" >> ${logfile} 2>&1 # 同时输出到屏幕和日志文件 echo "========================================" | tee -a ${logfile} echo "libflac Test Suite" | tee -a ${logfile} echo "Architecture: ${ARCH}" | tee -a ${logfile} echo "Test started at: $(date)" | tee -a ${logfile} echo "========================================" | tee -a ${logfile} echo "" | tee -a ${logfile} # 优先使用构建阶段创建的测试脚本 if [ -f "./run_libflac_tests.sh" ]; then echo "[INFO] Running libflac test suite using run_libflac_tests.sh..." | tee -a ${logfile} echo "" | tee -a ${logfile} ./run_libflac_tests.sh 2>&1 | tee -a ${logfile} res=$? echo "" | tee -a ${logfile} if [ $res -eq 0 ]; then echo "[PASS] All tests passed successfully" | tee -a ${logfile} pass=6 total=6 else echo "[FAIL] Some tests failed (exit code: $res)" | tee -a ${logfile} fail=1 total=6 fi else # 如果测试脚本不存在,尝试使用 make check echo "[INFO] run_libflac_tests.sh not found, trying make check..." | tee -a ${logfile} echo "" | tee -a ${logfile} make check 2>&1 | tee -a ${logfile} res=$? if [ $res -ne 0 ]; then # 如果 make check 失败,尝试直接运行各个测试程序 echo "" | tee -a ${logfile} echo "[INFO] make check failed, running individual tests..." | tee -a ${logfile} echo "" | tee -a ${logfile} # 运行 test_libFLAC (C API 测试) total=$((total + 1)) if [ -f "./src/test_libFLAC/test_libFLAC" ]; then echo "[TEST ${total}/6] Running test_libFLAC (C API test)..." | tee -a ${logfile} ./src/test_libFLAC/test_libFLAC 2>&1 | tee -a ${logfile} if [ $? -eq 0 ]; then echo "[PASS] test_libFLAC passed" | tee -a ${logfile} pass=$((pass + 1)) else echo "[FAIL] test_libFLAC failed" | tee -a ${logfile} fail=$((fail + 1)) res=1 fi echo "" | tee -a ${logfile} else echo "[SKIP] test_libFLAC not found" | tee -a ${logfile} echo "" | tee -a ${logfile} fi # 运行 test_libFLAC++ (C++ API 测试) total=$((total + 1)) if [ -f "./src/test_libFLAC++/test_libFLAC++" ]; then echo "[TEST ${total}/6] Running test_libFLAC++ (C++ API test)..." | tee -a ${logfile} ./src/test_libFLAC++/test_libFLAC++ 2>&1 | tee -a ${logfile} if [ $? -eq 0 ]; then echo "[PASS] test_libFLAC++ passed" | tee -a ${logfile} pass=$((pass + 1)) else echo "[FAIL] test_libFLAC++ failed" | tee -a ${logfile} fail=$((fail + 1)) res=1 fi echo "" | tee -a ${logfile} else echo "[SKIP] test_libFLAC++ not found" | tee -a ${logfile} echo "" | tee -a ${logfile} fi # 运行 test_streams (流处理测试) total=$((total + 1)) if [ -f "./src/test_streams/test_streams" ]; then echo "[TEST ${total}/6] Running test_streams (Stream processing test)..." | tee -a ${logfile} ./src/test_streams/test_streams 2>&1 | tee -a ${logfile} if [ $? -eq 0 ]; then echo "[PASS] test_streams passed" | tee -a ${logfile} pass=$((pass + 1)) else echo "[FAIL] test_streams failed" | tee -a ${logfile} fail=$((fail + 1)) res=1 fi echo "" | tee -a ${logfile} else echo "[SKIP] test_streams not found" | tee -a ${logfile} echo "" | tee -a ${logfile} fi # 运行 test_seeking (定位功能测试,可能需要参数) total=$((total + 1)) if [ -f "./src/test_seeking/test_seeking" ]; then echo "[TEST ${total}/6] Running test_seeking (Seeking functionality test)..." | tee -a ${logfile} ./src/test_seeking/test_seeking 2>&1 | tee -a ${logfile} local seeking_res=$? # test_seeking 无参数时会显示 usage,视为正常 if [ $seeking_res -eq 0 ]; then echo "[PASS] test_seeking passed" | tee -a ${logfile} pass=$((pass + 1)) else if grep -q "usage:" ${logfile}; then echo "[PASS] test_seeking passed (usage shown, no test data)" | tee -a ${logfile} pass=$((pass + 1)) else echo "[FAIL] test_seeking failed" | tee -a ${logfile} fail=$((fail + 1)) res=1 fi fi echo "" | tee -a ${logfile} else echo "[SKIP] test_seeking not found" | tee -a ${logfile} echo "" | tee -a ${logfile} fi # 运行 test_cuesheet (Cuesheet 元数据测试) total=$((total + 1)) if [ -f "./src/test_grabbag/cuesheet/test_cuesheet" ]; then echo "[TEST ${total}/6] Running test_cuesheet (Cuesheet metadata test)..." | tee -a ${logfile} ./src/test_grabbag/cuesheet/test_cuesheet 2>&1 | tee -a ${logfile} if [ $? -eq 0 ]; then echo "[PASS] test_cuesheet passed" | tee -a ${logfile} pass=$((pass + 1)) else echo "[FAIL] test_cuesheet failed" | tee -a ${logfile} fail=$((fail + 1)) res=1 fi echo "" | tee -a ${logfile} else echo "[SKIP] test_cuesheet not found" | tee -a ${logfile} echo "" | tee -a ${logfile} fi # 运行 test_picture (图片元数据测试) total=$((total + 1)) if [ -f "./src/test_grabbag/picture/test_picture" ]; then echo "[TEST ${total}/6] Running test_picture (Picture metadata test)..." | tee -a ${logfile} ./src/test_grabbag/picture/test_picture 2>&1 | tee -a ${logfile} if [ $? -eq 0 ]; then echo "[PASS] test_picture passed" | tee -a ${logfile} pass=$((pass + 1)) else echo "[FAIL] test_picture failed" | tee -a ${logfile} fail=$((fail + 1)) res=1 fi echo "" | tee -a ${logfile} else echo "[SKIP] test_picture not found" | tee -a ${logfile} echo "" | tee -a ${logfile} fi else echo "[PASS] make check passed" | tee -a ${logfile} pass=6 total=6 fi fi # 显示测试总结 echo "========================================" | tee -a ${logfile} echo "Test Summary" | tee -a ${logfile} echo "========================================" | tee -a ${logfile} echo "Total tests: ${total}" | tee -a ${logfile} echo "Passed: ${pass}" | tee -a ${logfile} echo "Failed: ${fail}" | tee -a ${logfile} echo "Test completed at: $(date)" | tee -a ${logfile} echo "Log file: ${logfile}" | tee -a ${logfile} echo "========================================" | tee -a ${logfile} cd $OLDPWD return $res}
执行交叉编译构建
定位路径到 lycium目录,执行交叉编译构建命令 ./build.sh libflac,如下图所示,表示编译构建成功。
全量推送到ohos设备执行测试
将 tcp_c_cplusplus全量推送到 ohos设备上,并定位到 lycium目录,使用 ./test.sh libflac执行测试。
你可以通过 cat ../thirdparty/libflac/libflac_armeabi-v7a_OpenHarmony_4.0.8.1_test.log查看执行测试用例的日志。
也可以在具体的架构下执行特定的测试二进制文件,比如 ./src/test_libFLAC/test_libFLAC。
遇到的问题和解决方案总结
问题 1: config.sub/config.guess 不支持 aarch64
错误信息:
Invalid configuration `aarch64-linux': machine `aarch64' not recognized
原因:旧版本的 config.sub 和 config.guess(2003年)不支持 aarch64 架构
解决方案:
补丁文件: patches/update-config-files.patch
问题 2: C++11 窄化转换错误
错误信息:
error: non-constant-expression cannot be narrowed from type 'size_t' (aka 'unsigned long') to 'FLAC__uint32' (aka 'unsigned int') in initializer list [-Wc++11-narrowing]
原因:C++11 禁止在初始化列表中进行隐式窄化转换,aarch64 上 size_t 是 64 位,FLAC__uint32 是 32 位
解决方案:
- 创建补丁文件
patches/fix-aarch64-narrowing.patch - 在
src/libFLAC++/metadata.cpp 中添加显式类型转换 - 使用
static_cast<FLAC__uint32>() 进行转换
问题 3: 文档安装失败
错误信息:
make[4]: *** [Makefile:600: install-data-local] Error 1
原因: make install 时尝试安装文档,但 doc/html/api 目录不存在(需要 Doxygen 生成)
解决方案:
- 在
package() 函数中临时修改 Makefile
问题 4: 设备上测试时重新编译
错误信息:
/bin/sh: /path/to/cross-compiler: No such file or directory
原因:设备上执行 make check 时,Makefile 中的编译器路径是构建机的绝对路径,设备上不存在
解决方案:
- 创建自定义测试脚本
run_libflac_tests.sh - 修改 Makefile,将
check 目标改为运行测试脚本
问题 5: 测试输出不可见
问题描述:在设备上执行测试时,看不到日志和测试结果
原因:所有输出都重定向到日志文件,没有输出到标准输出
解决方案:
- 使用
tee -a ${logfile} 同时输出到屏幕和日志文件 - 添加测试状态标记(
[PASS]、[FAIL]、[SKIP])
问题 6: 特殊测试用例处理
问题描述:
解决方案:
- 创建辅助函数
run_test_maybe_root() 处理 root 环境警告 - 创建辅助函数
run_test_maybe_usage() 处理 usage 输出