【节前发一篇,祝大家新年快乐】别再瞎搜了!libflac 鸿蒙交叉编译 + 报错解决一文通关
本文介绍了将libflac音频编解码库适配到OpenHarmony平台的完整流程。主要内容包括:1)环境准备和源码分析;2)编写HPKBUILD构建脚本,解决aarch64架构识别问题;3)修复C++11窄化转换错误;4)配置交叉编译环境;5)验证功能正确性。适配过程中解决了config.guess文件更新、类型转换和浮点ABI设置等关键问题,最终实现了该库在OpenHarmony平台的稳定运行。
[!NOTE]
加入开源鸿蒙跨平台社区:
・共享三方库移植方案、跨平台应用开发
・互助解决编译 / 运行问题
・第一时间获取鸿蒙三方库适配模板
一个人走得快,一群人走得远!
开始前先按照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=libflac
pkgver=master
pkgrel=0
pkgdesc="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=true
downloadpackage=true
buildtools="configure"
builddir=${pkgname}-${pkgver}
packagename=${builddir}.tar.gz
关键点说明:
buildtools="configure": 指定构建方式为 configure(Autotools)autounpack=true: 自动解压源码包downloadpackage=true: 自动下载源码包
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=false
fi
问题背景:
- 旧版本的
config.guess和config.sub(2003年)不支持aarch64架构 - 在交叉编译时会出现
Invalid configuration 'aarch64-linux': machine 'aarch64' not recognized错误
解决方案:
- 从 GNU 官方获取最新版本的
config.guess和config.sub(2025年版本) - 在
prepare()函数中替换旧文件 - 添加验证步骤确保文件更新成功
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=false
fi
问题背景:
- 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 {
bool VorbisComment::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-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
关键点说明:
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>&1
ret=$?
if [ $ret -ne 0 ]; then
cd $OLDPWD
return $ret
fi
# 在交叉编译阶段一并构建测试可执行文件
$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/sh
set -e
cd "$(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_libFLAC
run_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_picture
LIBFLAC_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
关键点说明:
- 创建自定义测试脚本,直接运行已编译的测试程序
- 处理特殊测试情况:
test_libFLAC和test_libFLAC++在 root 环境下可能报 “are you running as root?”,这是环境差异,可以忽略test_seeking需要参数,无参数时显示 usage,视为正常
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 install时会尝试安装文档,但doc/html/api目录不存在(需要 Doxygen 生成)- 导致安装失败:
make[4]: *** [Makefile:600: install-data-local] Error 1
解决方案:
- 临时修改 Makefile,从
SUBDIRS中移除doc目录 - 安装完成后恢复原始 Makefile
HPKBUILD完整代码
pkgname=libflac
pkgver=master
pkgrel=0
pkgdesc="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=true
downloadpackage=true
buildtools="configure"
builddir=${pkgname}-${pkgver}
packagename=${builddir}.tar.gz
source envset.sh
host=
patchflag=true
narrowingpatchflag=true
prepare() {
# 更新 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/sh
set -e
cd "$(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_libFLAC
run_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_picture
LIBFLAC_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>&1
logfile=${LYCIUM_THIRDPARTY_ROOT}/${pkgname}/${pkgname}_${ARCH}_${OHOS_SDK_VER}_test.log
关键点说明:
- 加载 HPKBUILD 中的变量(如
pkgname、builddir等) - 设置日志文件路径,包含架构和 SDK 版本信息
2. openharmonycheck() 函数
2.1 日志初始化
# 初始化日志文件
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 "========================================" | 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同时输出到屏幕和日志文件,提供实时反馈 - 记录测试开始时间、架构和 SDK 版本信息
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 失败,尝试直接运行各个测试程序
# 逐个运行测试用例...
fi
fi
测试策略:
- 优先: 使用构建阶段创建的
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}
关键点说明:
- 统计总测试数、通过数、失败数
- 记录测试完成时间和日志文件路径
- 使用
tee同时输出到屏幕和日志
HPKCHECK完整代码
source HPKBUILD > /dev/null 2>&1
logfile=${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 架构
解决方案:
- 从 GNU 官方获取最新版本(2025年)
- 在
prepare()函数中替换旧文件 - 添加验证步骤确保更新成功
补丁文件: 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 - 从
SUBDIRS中移除doc目录 - 安装完成后恢复原始 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}同时输出到屏幕和日志文件 - 添加测试进度显示(
[TEST X/6]) - 添加测试状态标记(
[PASS]、[FAIL]、[SKIP]) - 添加测试总结信息
问题 6: 特殊测试用例处理
问题描述:
test_libFLAC和test_libFLAC++在 root 环境下报 “are you running as root?”test_seeking无参数时显示 usage
解决方案:
- 创建辅助函数
run_test_maybe_root()处理 root 环境警告 - 创建辅助函数
run_test_maybe_usage()处理 usage 输出 - 将这些情况视为正常,不标记为失败
更多推荐




所有评论(0)