欢迎加入开源鸿蒙PC社区: https://harmonypc.csdn.net/

欢迎在PC社区平台申请新建项目:https://atomgit.com/OpenHarmonyPCDeveloper

适配对象wavplay 2.0(WAV 播放/录音 CLI)
环境HarmonyOS PC(aarch64)
仓库build_in_harmonyos(AtomGit)
PR#16191

摘要:适配对象是 wavplay 2.0——一个 2009 年的 Linux WAV 播放/录音 CLI(autotools 构建,上游无自动化测试)。核心难点不在交叉编译,而在上游音频后端强绑定 legacy OSS /dev/dsp ioctl 接口,OpenHarmony 上根本没有这个设备节点。本适配把音频后端整体重写为 OHOS 原生 OH_AudioRenderer / OH_AudioCapturer 纯回调模型:4 个补丁共 902 行,全部以能力宏 HAVE_OHOS_AUDIO 守卫;conan 第三方依赖 0 个;“下载→打补丁→编译→打包→消费者测试 8/8"全流程可由一条 conan create 命令复现。PR #16191(路径C 高难度挑战)于 2026-10-18 合入,关联 Issue #4142 联动关闭,制品已发布至 ohpcd 制品仓。诚实边界:当前开发 shell 环境下 [7]/[8] 用例断言的是"OHOS 后端可达 + 无崩溃/挂死/回退 OSS”,本文不主张"真实出声"(出声验收需完整音频服务运行时,见 7.4)。configure 能过、make 能过,直到真正播放的那一刻才暴露——上游的整条音频路径(/dev/dsp + SNDCTL_DSP_* ioctl)在 OpenHarmony 上不存在,音频子系统是另一套 API、另一个模型。这篇文章记录的是"重写音频后端并让它真实可用"的完整过程。

声明:验证章节(第 4 节)分层统计、口径分离——上游测试(0 项,无入口)、消费者测试(8/8)、CI 轮次(2 绿 3 作废)分别陈述,不做相加。[7]/[8] 不主张真实出声;CI 3 轮红均为 worker 基础设施故障(已标"作废"),与代码无关。

1. 背景:为什么值得做

  • 生态价值:wavplay/wavrec 是最经典的 Linux 命令行 WAV 工具(wavplay file.wav 一行出声),脚本与教学场景里它常被当作"默认存在"的基础设施——鸿蒙PC 上目前没有等价物,属于高频小工具缺口。
  • 真实先例:上游版本横幅保留 1997 年的版权声明(作者 Warren W. Gay, VE3WWG,见 4.2 实测输出);项目 2009 年最后发布后一直有使用与打包先例,适配它不是为了刷"能编译",而是补一个真实可用的命令行音频工具。
  • 选库理由:它表面上"很简单"——无第三方依赖、autotools、纯 C——难点被压缩到一个点上:音频后端。这个点恰好是路径C 的典型形态:强绑定平台私有接口(legacy OSS),无跨平台抽象层,只能重写而不是绕过。

2. 环境与差异前置

环境表(全部为当事机器实测,2026-10-19;CI worker 为集群机器,其内核 1.13.0 见 4.4,与本机不互比):

项值测量命令
设备型号HAD-W32(MATEBOOK PRO)param get const.product.model
系统OpenHarmony-6.1.0.115param get const.ohos.fullname
内核HongMeng Kernel 1.12.0uname -r
架构aarch64uname -m
编译器clang 15.0.4(OHOS SDK)clang --version
构建工具Conan 2.29.1conan --version
git / Python2.45.2 / 3.12.9git --version / python3 --version

差异点全表(每个差异点在 3.3 对应一个坑位;先看全貌,再逐个展开):

#差异点上游假设鸿蒙PC 实际情况处理
1build machine 识别2009 版 config.guess 认识 Linux build machineuname -s = HarmonyOS,不识别坑位 1:显式 triplet
2音频设备/dev/dsp(legacy OSS)存在无 /dev/dsp;音频走 OH_AudioRenderer / OH_AudioCapturer坑位 2:重写后端
3设备锁sysv 信号量(union semun)sys/sem.h 无 union semun;锁由 audio service 管理坑位 3:no-op
4设备复位ioctl(SNDCTL_DSP_RESET)HAVE_OS_UNKNOWN 下不 include soundcard.h,宏未声明坑位 4:守卫
5锁返回值语义失败返回 -1,调用方退出no-op 实现须兼容调用方判断坑位 5:返回 0
6临时目录POSIX mktemp -d 可写HMDFS:mktemp -d 建出的目录不可写坑位 6:PATH shim
7GUIX11 客户端无 X11侦察决策:--enable-nox(只建 CLI)

3. 适配过程

3.1 侦察(动手前)

  1. 依赖发布状态(W0):conan 第三方依赖 0 个 → 无 R36 依赖阻塞(依赖树见 3.2)。
  2. 构建系统:autotools(autoconf 2.61,2009 年版)→ 先查 config.guess 年代 → 坑位 1。
  3. 测试策略:清点上游测试入口——顶层无 tests/ 目录、Makefile.am 无 check target(证据见 4.1)→ 消费者测试必须自补(4.2)。
  4. GUI:X11 客户端在 OHOS 无 X11 → --enable-nox,只构建 wavplay / wavrec / wavplay-config(差异表 7)。

3.2 依赖树

wavplay/2.0 (application, CLI: wavplay / wavrec / wavplay-config)
├─ conan 第三方依赖:(无)
├─ 系统库: libohaudio.so (OHOS 系统音频库, -lohaudio 链接)
│    └─ 运行时: OHOS audio service (audio_server, 需完整系统环境)
└─ X11 系 (libx11/libxmu/libxt/motif): --enable-nox 未启用, 不需要

3.3 坑位(现象 → 定位 → 方案 → 经验)

坑位 1:config.guess 不认识鸿蒙PC(差异 1)

  • 现象:./configure 报 cannot guess build type。

  • 定位(10-15 探索日;分钟级记录不可恢复,方法可复现):翻 tarball 里的 config.guess 版本——autoconf 2.61(2009 年),其识别表里没有 uname -s 返回的 HarmonyOS;同时确认 OHOS SDK clang 的默认 target 已 baked-in aarch64-unknown-linux-ohos + sysroot,因此不需要 --target/--sysroot。

  • 方案(最小修改,不动 config.guess 源文件):configure 显式传 triplet(conanfile.py generate() 内):

    tc.configure_args.extend([
        "--build=aarch64-unknown-linux-gnu",
        "--host=aarch64-unknown-linux-gnu",
    ])
    

    config.sub 接受该 triplet,后续全部正常。

  • 经验:新系统 + 老 autotools,第一查 config.guess/config.sub 的年代;先试显式 --build/--host,再谈改文件。另一条:决定传不传 --target 之前,先查宿主工具链的"默认 target"(本例 SDK 已 baked-in,传了反而多余)。

  • 入档:已记录于归档 notes.md(knowledge_gained 第 1/2 条),未入正式知识草稿(见第 5 节)。

坑位 2:音频后端 /dev/dsp 在鸿蒙PC 不存在(差异 2,核心)

  • 现象:src/file.c 的 OpenDSP/CloseDSP/PlayDSP/RecordDSP 全部基于 legacy OSS 接口——open("/dev/dsp") + ioctl(SNDCTL_DSP_*)。OHOS 无 /dev/dsp;HAVE_OS_UNKNOWN 下不 include soundcard.h,SNDCTL_DSP_* 宏全部未定义。
  • 定位(10-15,先做 PoC 再定方向):① 直接 open("/dev/dsp") 确认设备节点不存在;② 检查 SDK sysroot 的 libohaudio.so——22KB 接口桩:169 个导出符号只映射 3 个唯一地址,桩函数反汇编仅 ret。结论:流创建必须走系统 audio service(完整运行时),开发 shell 里桩函数只会返回失败码——这同时决定了测试用例的判定设计(见 4.2 [7]/[8])。方向定为重写为 OH_AudioRenderer / OH_AudioCapturer 回调 API,而不是找 shim。
  • 方案(补丁 0002,约 340 行新增;原 OSS 实现以 #ifndef HAVE_OHOS_AUDIO 包裹,非 OHOS 构建行为与上游完全一致):
    1. 格式映射(8bit→AUDIOSTREAM_SAMPLE_U8、16bit→AUDIOSTREAM_SAMPLE_S16LE、Mono/Stereo、PCM→AUDIOSTREAM_ENCODING_TYPE_RAW);12/24/32bit 明确报错——OHOS 仅支持 8/16bit,属平台限制而非适配缺陷。
    2. 播放:OH_AudioRenderer_OnWriteDataCallbackAdvanced 纯回调——回调返回本次填充字节数,返回 0 = 播放完毕(EOF);数据源 = WAV 文件按剩余字节分片。
    3. 录音:OH_AudioCapturer_OnReadDataCallback 纯回调——回调写入文件,累计字节数达到 duration 对应值后停止。
    4. 并发同步(补丁 0001 扩展 DSPFILE):pthread mutex/cond + 写文件句柄 + 剩余字节 + 停止标志;主线程 wait 回调完成/超时。
    5. 超时安全网:duration×3 + 30s 实时超时,防 audio service 无响应时死等。
  • 经验:新系统的音频适配 = 格式映射表 + 回调模型重构,两件事。动手前先分清"接口缺失"与"接口存在但运行时/权限缺失"——本例是前者(/dev/dsp 整个不存在),重写才是正解;后者才考虑 shim/等待环境。回调模型必须回答一个问题:“所有退出路径(正常结束/用户停止/超时)上,在途回调怎么办?”——本条答案迭代过两次,见 3.4 误判自曝。
  • 入档:已记录于归档 notes.md(knowledge_gained 第 3/5 条),未入正式知识草稿(见第 5 节)。

坑位 3:sysv 信号量 union semun 缺失(差异 3)

  • 现象:src/locks.c 编译报 union semun 未定义——OHOS 的 sys/sem.h 没有这个结构(legacy OSS 的设备互斥锁实现)。
  • 定位:音频设备锁的职责在 OHOS 由 audio service(binder)接管,userspace 不需要再做 sysv 锁。
  • 方案(补丁 0003):原实现以 #ifndef HAVE_OHOS_AUDIO 包裹;OHOS 侧 no-op 实现(OpenDSPLocks/LockDSP/UnlockDSP)。
  • 经验:上层(service)接管锁时,userspace 锁层应 no-op,而不是"模拟";no-op 的返回值语义见坑位 5。
  • 入档:已记录于归档 notes.md(knowledge_gained 第 4 条),未入正式知识草稿(见第 5 节)。

坑位 4:SNDCTL_DSP_RESET 未声明(差异 4)

  • 现象:src/server.c 两处 ioctl(dfile->fd, SNDCTL_DSP_RESET, 0) 编译报错(宏未声明)。
  • 定位:OHOS 下设备复位由 renderer 生命周期管理,userspace 不发 reset ioctl。
  • 方案(补丁 0004):两处 ioctl 以 #ifndef HAVE_OHOS_AUDIO 包裹。
  • 经验:头文件不 include 的平台,引用其宏的所有 ioctl 要逐个找出来加守卫——"未声明"是一个清单,不是一个点;修完第一个错误后重编译确认,别假设同类错误只有一个。

坑位 5:no-op 锁的返回值语义(差异 5,误判自曝)

  • 现象:坑位 3 的 no-op 实现做完后,wavplay -i 仍报 Unable to get audio locking semaphores 直接退出。
  • 误判(自曝)→ 纠正:最初认为"no-op 随便返回个值",OpenDSPLocks 返回了 -1;调用方把 -1 判定为"锁失败"(默认 IPCKey 非零,锁路径必然被调用)→ 整个流程直接终止。改为返回 0(合法伪 IPC ID,Lock/UnlockDSP 同为 no-op)后正常。
  • 方案:补丁 0003 中 OpenDSPLocks 返回 0。
  • 经验:no-op 实现的返回值必须匹配调用方的判断语义——"我没做事,就返回 -1"是经典暗坑;写 stub 之前把调用方的判断链读一遍。
  • 入档:已记录于归档 notes.md(knowledge_gained 第 4 条),未入正式知识草稿(见第 5 节)。

坑位 6:HMDFS 下 mktemp -d 目录不可写(差异 6)

  • 现象:conan create 的 configure 阶段,autoconf 的 config.status 用 mktemp -d 建临时目录后,往里写文件报 Permission denied,流程中断。
  • 定位:HMDFS 平台特性——chmod 不生效(权限固定 660,access(X_OK) 恒 False),且原生 mktemp -d 建出的目录属主不可写。
  • 方案(conanfile.py generate(),不改系统):生成一个 mkdir-based 的 mktemp shim(遵守 mktemp -d <template> 契约,随机后缀 6 位),放进配方私有目录(build_folder 子目录),用 Conan Environment 把该目录 prepend 到 PATH——只影响本次构建。
  • 经验:新文件系统的临时目录问题用"私有 shim + PATH 注入"解决,不改系统 mktemp;shim 随配方走(在构建目录里),不做全局安装。

3.4 误判与纠正(检视驱动,两条)

  • 误判 1:R8 平台宏。初版补丁用 #ifdef __OHOS__ 平台宏守卫 → AI 检视首轮(10-16 13:31)给出 3 条行级 High:patches 默认禁止平台宏(R8)。纠正(R4 commit 8e4aa498a):全部改为能力宏 HAVE_OHOS_AUDIO(由 conanfile -DHAVE_OHOS_AUDIO 注入,构建系统提供)——非 OHOS 构建不提供该宏,行为与上游零差异、ABI 零差异。经验:平台相关代码用"能力宏"(表达能力)守卫,不用"平台宏"(表达身份)守卫——后者把代码绑死在特定平台名上,同一能力在别的构建里无法复用;能力宏也让"为什么保留双后端"不言自明。
  • 误判 2:超时路径 use-after-free。初版 PlayDSP 的超时路径不等在途写回调返回就释放 DSPFILE/dspbuf——回调线程可能还在引用已释放内存。同样由检视首轮发现。纠正:所有退出路径(正常完成/用户停止/超时)先置停止标志、等待回调返回(排空在途回调)再释放资源;同时把 wavinfo 的 DataBytes 结算补齐。经验:回调模型的退出路径 = 并发模型;写"同步等待"代码时,枚举全部退出路径,逐一核对"在途回调是否已排空"。
  • 闭环:R4 修复推送后 14:36 复评"上次问题已修复,无新问题";最终版 21:52 再次 /ai review force → 22:20 “✅ 未发现问题”(全表见 4.4)。

4. 验证

口径声明:本章各层数字分别统计、陈述:上游测试(0 项,无入口)、消费者测试(8 项)、CI 轮次(5 轮,2 绿 3 作废)

4.1 上游测试(R51 三步法台账)

  • 清点(2026-10-19/20 真机复核,可复现,命令见 4.5 ②):源码树顶层无 tests/ 目录;Makefile.am 无 check target(grep 0 命中);无 ctest/pytest 注册(2009 年纯 C 项目,无测试框架)。→ 上游自动化测试 = 0 项。
  • 实跑:N/A——没有可执行的测试入口
  • 结论:0/0,通过率不适用(不写"100%")。按赛事规则自行补消费者测试用例(见 4.2)。

4.2 消费者测试(8/8)

用例在 test_package/consumer_test.c,模拟上层应用调用 wavplay / wavrec 的公开 CLI(经 PATH 调用打包产物)。[1]-[6] 不触音频设备,[7][8] 走 OHOS 音频后端。fixture 由测试程序运行时就地生成(1 秒 440Hz 16bit 单声道 PCM,生成后校验 44+n·bps 字节数防截断文件),无外部依赖。

#用例级别断言
[1]wavplay --versionL1输出含 “Version 2.0”
[2]wavplay --helpL1输出含 “Usage:”
[3]wavplay -i <fixture>L2输出含 “Sampling Rate:”(WAV 头解析)
[4]wavplay -i <fixture>L2输出含 “44100 Hz”(采样率正确)
[5]wavplay -i <fixture>L2精确字段匹配 “Bits:\t\t16\n”(位深逐字段)
[6]wavrec --versionL1输出含 “Version 2.0”
[7]wavplay <fixture>L2OHOS 播放后端可达(判定设计见下)
[8]wavrec -t 2000 <fixture>L2OHOS 录音后端可达(判定设计见下)
  • 分级说明:L1 = 版本/帮助断言,单靠 L1 不能宣称功能完成;本包属多媒体类,[3]-[8] 为 L2(真实 API + 结果断言)。
  • [7]/[8] 的环境自适应判定设计(依据坑位 2 的 PoC 结论——SDK 的 libohaudio.so 是接口桩,开发 shell 里流创建必然失败):
    • 完整 OHOS 系统:真实播放/录音、exit 0 → PASS (audio backend OK, real output);
    • 桩 SDK 环境:OHOS 后端 init 失败但给出干净的 “OHOS:” 错误(正常退出,非信号)→ PASS (OHOS audio backend reached, clean error on stub SDK)——证明走的是 OHOS 代码路径,而不是回退到 OSS 路径;
    • 任何环境:崩溃(signal)/ 挂死 / 失败输出中不提 OHOS 后端(即回退 OSS)→ FAIL。
  • 实测输出(2026-10-19 本机复跑,逐字):
[6] wavrec --version
  $ wavrec --version 2>&1

wavplay/wavrec Version 2.0
was written by and is
Copyright (C) 1997 by Warren W. Gay VE3WWG

    PASS
[7] wavplay (OHOS audio backend reachability)
  $ wavplay test440.wav 2>&1
Pathname:	test440.wav
OHOS: OH_AudioStreamBuilder_Create: -1810251816
Device:		/dev/dsp
Sampling Rate:	44100 Hz
Mode:		Mono
Samples:	44100
Bits:		16

    PASS (OHOS audio backend reached, clean error on stub SDK)
[8] wavrec -t 2000 (OHOS audio backend reachability)
  $ wavrec -t 2000 rec_test.wav 2>&1
OHOS: OH_AudioStreamBuilder_Create(capturer): -1488572440
    PASS (OHOS audio backend reached, clean error on stub SDK)

=== RESULT: ALL PASS (0 failure(s)) ===

(OHOS: …: <错误码> 行的数值跨运行非确定——桩实现特征,判定以 PASS 行为准。10-16 W3 验证与 10-19 复跑两份日志的 PASS 行完全一致。)

  • 诚实边界:当前环境下 [7]/[8] 断言的是"OHOS 后端可达 + 无崩溃/挂死 + 未回退 OSS",不主张真实出声;出声/录音验收需完整音频服务运行时(audio_server + HAP 沙箱 + 权限),见 7.4。

4.3 产物核验(2026-10-19 真机实测)

项结果
包内容bin/wavplay(55744 B)、bin/wavrec(55744 B)、bin/wavplay-config(813 B)、share/man/man1/、licenses/COPYING
lib/ 与 include/无(纯 CLI 应用包:不导出库、不导出头文件——上游 include/ 为 noinst_HEADERS)
二进制格式ELF 64-bit arm64,musl 动态链接,not stripped
符号面(readelf -d NEEDED)libohaudio.so + libc.so(wavplay 与 wavrec 一致)
构建确定性本地 10-19 复跑与 CI 绿轮(10-16)的 recipe revision(17f68276…)与 package ID(6b129b06…)完全一致
构建耗时本地全量构建+测试约一分钟

4.4 CI 与评审

CI 全轮次表(5 轮,失败轮不删;基础设施故障标"作废"并注明原因,证据 = 各轮评论内嵌的完整日志):

轮次构建Workerhead结果说明
1#94743CI-01238308ee3d(初版)✅ 通过7 步全绿(初版,后按检视意见修订)
2#94903CI-0348e4aa498a(R4)❌ 作废(基础设施)configure 阶段 can't fork - try again(worker 进程资源耗尽);bot 自动分类误判为"编译失败 Autotools configure";全日志零编译/链接错误
3#95032CI-0018e4aa498a(R4)❌ 作废(基础设施)make 阶段 fork: retry: Resource temporarily unavailable(EAGAIN);bot 误判为"编译失败 (make/gcc)";全日志零编译/链接错误
4#95322CI-0038e4aa498a(R4)❌ 作废(基础设施)worker 不可达(SSH bridge exec failed: connection lost before ready),3 个步骤全失败;bot 误判为"源码下载超时"
5#95849CI-0018e4aa498a(R4)✅ 通过最终版:worker-prep / git-clone / checkout-pr / conan-setup / conan-build-test / cleanup 全过,test_package、L0、L1 通过

3 轮作废的共同根因是 worker 集群侧的资源耗尽/不可达,与本配方无关;集群侧具体资源状态不在本文验证范围。另:第 5 轮 configure 摘要显示 CI worker 内核为 HongMeng Kernel 1.13.0(集群机器,与第 2 节本机实测 1.12.0 不互比)。

AI 检视记录(atomgit-bot,3 轮):

时间(10-16)轮次意见处理
13:31首轮(初版)5 条:3 条行级 High(R8 __OHOS__ 平台宏)+ 2 条摘要级(超时路径 UAF 风险、wavinfo 未结算;另涉死代码、消费者用例覆盖不足)全部在 8e4aa498a(R4)修复,讨论串逐条回复闭环(见 3.4)
14:36复评(R4 版)“上次问题已修复,无新问题”闭环
21:52→22:20终检(最终 CI 绿后)“✅ 未发现问题”闭环

合入记录:PR #16191 于 2026-10-18 合入,head 8e4aa498a,base main;关联 Issue #4142 随合入联动关闭;合入后 release 流水线发布至 ohpcd 制品仓在这里插入图片描述

图1:PR #16191

4.5 复现速查(3 条命令)

前置:PR #16191 已合入 main,以下命令在仓库根目录执行(配方位于 archives/w/wavplay/2.0/)。

① 全量构建 + 消费者测试(与 CI 同构形态;本机实测 42 秒,源码已缓存):

cd <仓库根目录>
conan create archives/w/wavplay/2.0 -pr:h=ci/conan/profiles/ohos-aarch64 -pr:b=ci/conan/profiles/ohos-aarch64 --build=missing --build="wavplay/*"

期望输出(尾部):

=== RESULT: ALL PASS (0 failure(s)) ===

(上方 8 个 PASS 行,其中 [7]/[8] 在当前开发 shell 为 PASS (OHOS audio backend reached, clean error on stub SDK);在完整音频运行时环境下为 PASS (audio backend OK, real output)。)
在这里插入图片描述

图2:conan create 全量构建 + 消费者测试

② 上游"无测试入口"证据链复跑(源码在 conan 缓存中):

SRC=$(conan cache path "wavplay/2.0#17f68276fc06051a68cd38d806332e0b" --folder=source)
ls "$SRC"
grep -c check "$SRC/Makefile.am"

期望输出:ls 列表中无 tests/ 目录;grep -c check 输出 0(退出码 1)。

③ 产物符号面核验:

PKG=$(conan cache path "wavplay/2.0:6b129b068851896169fae42d399fa922d239ce8a")
readelf -d "$PKG/bin/wavplay" | grep NEEDED

期望输出:

 0x0000000000000001 (NEEDED)             Shared library: [libohaudio.so]
 0x0000000000000001 (NEEDED)             Shared library: [libc.so]

口径对应:命令 ① → 4.2(消费者测试 8/8)与 4.3(产物与耗时);命令 ② → 4.1(上游 0/0 清点证据);命令 ③ → 4.3(符号面 NEEDED)。三条命令均已在本机验证产出期望输出。

5. 知识沉淀

本 PR 未随附正式知识草稿(已核验:knowledge/drafts/ 下无 wavplay/OHAudio 相关条目),沉淀知识记录于归档 manifest.yaml 的 knowledge_gained 与 notes.md:

#知识来源坑位复用价值
12009 版 config.guess 不识别 HarmonyOS build machine;显式 --build/--host=aarch64-unknown-linux-gnu 绕过(config.sub 接受)坑位 1高——所有老 autotools 包在鸿蒙PC 上
2OHOS SDK clang 已 baked-in aarch64-unknown-linux-ohos + sysroot,无需显式 --target/--sysroot坑位 1高——所有 OHOS 交叉编译配方
3OSS /dev/dsp 后端重写为 OH_AudioRenderer/OH_AudioCapturer(纯回调);播放用 OnWriteDataCallbackAdvanced(返回填充字节数,0=EOF),录音用 OnReadDataCallback坑位 2中高——所有 legacy OSS 类音频工具
4OHOS 音频设备锁由 audio service(binder)管理,userspace no-op;OpenDSPLocks 须返回 0(非 -1),否则调用方误判锁失败坑位 3/5中——带设备锁的音频包
5OHOS 仅支持 8/16bit PCM;HAVE_OS_UNKNOWN 下不 include soundcard.h,SNDCTL_DSP_* 引用处须 #ifndef HAVE_OHOS_AUDIO 包裹坑位 2/4中——所有引用 soundcard.h 宏的包

补充一条同样具通用性的坑(见坑位 6,记录于配方 conanfile.py 注释):HMDFS 下 mktemp -d 建出的目录属主不可写——解法是 mkdir-based 私有 shim + PATH 注入,不改系统。以上 6 条本次均未提草稿,后续可单独登记(kv+md 各一)。

6. FAQ

Q1:零依赖,是不是太简单?
难度不在依赖链,在音频后端:上游音频路径是 Linux legacy OSS ioctl 接口,OHOS 无等价物,必须整体重写(约 340 行新实现 + 回调并发同步)。依赖为零是上游设计事实,不是适配工作量小。

Q2:上游测试 0/0 合理吗?
合理且如实:2009 年项目无测试基础设施——顶层无 tests/、Makefile.am 无 check target(证据可复现,见 4.5 ②)。0/0 的含义是"无入口",不是"没跑";按赛事规则以消费者测试补位(8 项,见 4.2)。

Q3:[7]/[8] 报错也算 PASS?
判定是环境自适应设计(见 4.2):桩环境断言"OHOS 路径被执行 + 干净报错",比"碰巧退出码为 0"更严格——崩溃、挂死、回退 OSS 路径在任何环境都判 FAIL。当前环境能断言的边界就是"后端可达 + 进程稳定",本文没有超出这个边界做主张。

Q4:消费者测试 8/8 能证明音频功能可用吗?
不能证明"出声"。能证明:OHOS 后端代码路径真实可达、格式映射与回调逻辑编译链接正确、进程在任何环境下稳定退出。真实出声/录音验收需完整音频服务运行时,列入 7.4。

Q5:为什么不用 __OHOS__ 平台宏?
项目约定(R8):patches 默认禁止平台宏,平台相关代码用能力宏守卫。HAVE_OHOS_AUDIO 由构建系统提供:非 OHOS 构建不提供该宏 → OSS 后端原样编译,行为与上游零差异;这正是"保留双后端"的工程含义(详见 3.4 误判 1)。

Q6:3 轮 CI 红都跟代码无关吗?
3 轮失败日志分别是 can't fork - try again、fork: retry: Resource temporarily unavailable(EAGAIN)、SSH bridge exec failed: connection lost before ready——全部是 worker 集群侧故障(进程资源耗尽/不可达),且与最终绿的 #95849 代码完全相同(同一 commit 8e4aa498a)。CI bot 的自动分类曾把这 3 轮误判为"编译失败",原文日志有详细信息。

7. 总结

7.1 量化盘点

维度数字
上游自动化测试0 项(无入口,见 4.1)
补丁4 个文件 / 902 行(全部 HAVE_OHOS_AUDIO 能力宏守卫)
PR 规模15 个文件 / +1632 行(其中补丁 902 行,其余为配方、测试包与归档元数据)
conan 第三方依赖0
适配坑位6 个(其中 2 个为检视驱动的误判纠正)
消费者测试8/8(100%)
本地全量构建+测试耗时42 秒(2026-10-19 实测,源码已缓存)
AI 检视3 轮,5 条意见,全部闭环
CI 轮次5 轮(2 绿,3 轮基础设施故障作废)
知识草稿0(6 条知识记录于归档 notes/manifest 与配方注释)

7.2 可复用方法

  1. 能力宏双后端守卫:新后端以构建系统提供的能力宏守卫(非平台宏);非目标平台不提供宏 → 行为/ABI 与上游零差异,检视友好。
  2. 回调式音频适配三件套:格式映射表(位深/声道/编码)+ 回调并发同步(mutex/cond/停止标志)+ 实时超时安全网(duration×3+30s);所有退出路径先排空在途回调再释放(防 UAF)。
  3. 环境自适应判定:同一用例对不同环境断言不同期望(完整系统=真实出声 rc=0 / 桩环境=干净报错 / 崩溃·挂死·回退 OSS=任何环境 FAIL),一个测试二进制任何环境都能跑绿。
  4. 老 autotools × 新系统两件套:显式 --build/--host triplet 绕过老 config.guess;HMDFS 类临时目录问题用配方私有 mktemp shim + PATH 注入。
  5. no-op 返回值语义核对:上层接管职责时,userspace stub 返回调用方判定为"成功"的值(本例 0),不是 -1——写 stub 前先读调用方判断链。

7.3 流程 checklist(通用)

  • 适配前先确认依赖是否全部发布(本例 0 依赖,无 R36 阻塞)
  • 清点上游测试入口并留证据(目录 + Makefile target,命令可复现)
  • 老构建系统先查 config.guess/config.sub 年代
  • 音频/硬件类:先确认目标平台"等价接口是否存在"(/dev/dsp 类不存在 → 重写,而非 shim)
  • 平台差异用能力宏守卫,不用平台宏(R8)
  • 写 no-op/stub 前读调用方判断链(返回值语义)
  • 回调/并发代码:枚举全部退出路径,确认在途回调已排空
  • 消费者测试用环境自适应判定(真实出声 / 干净报错 / 负例 FAIL)
  • CI 红先看原始日志归类(bot 分类可能误判;代码失败与基础设施故障分开,后者标"作废")
  • PR 合入不是终点:复审+ release 发布 + 知识登记

7.4 后续工作

  • 真实出声/录音验收需完整 OHOS 音频服务运行时(audio_server + HAP 沙箱 + 权限);本文证据止于"OHOS 后端可达 + 无崩溃/挂死 + 未回退 OSS"。
  • 两条最具通用性的坑(config.guess 显式 triplet、HMDFS mktemp shim)可单独登记知识草稿。

参考链接(HEAD 302→200):

  • PR:https://atomgit.com/OpenHarmonyPCDeveloper/build_in_harmonyos/merge_requests/16191
  • Issue:https://atomgit.com/OpenHarmonyPCDeveloper/build_in_harmonyos/issues/4142
  • 仓库:https://atomgit.com/OpenHarmonyPCDeveloper/build_in_harmonyos
  • 上游源码:https://sourceforge.net/projects/wavplay/ (下载地址 https://download.sourceforge.net/project/wavplay/Release%20Downloads/wavplay-2.0.tar.gz ,SHA-256 256ea0b581d587b482e0c928869e3896845a5299b8e124c01749b4b9edfeb552)
Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐