鸿蒙PC高难度挑战:KDE KWayland 29 项上游测试跑通 OHOS——AF_UNIX shim × DT_NEEDED 归一化 × 三态振荡探测
欢迎加入开源鸿蒙PC社区: https://harmonypc.csdn.net/
欢迎在PC社区平台申请新建项目:https://atomgit.com/OpenHarmonyPCDeveloper
| 项 | 值 |
|---|---|
| 适配对象 | kf5wayland 5.116.0(KDE Frameworks 5 KWayland,Wayland 协议客户端/服务端双角色库) |
| 平台 | 鸿蒙 PC:HUAWEI MateBook Pro(HAD-W32)/ HarmonyOS 6.1.0.117(SP68C00E100R13P3)/ aarch64 |
| 工具链 | clang 15.0.4(aarch64-unknown-linux-ohos)+ libc++ / C++17 / Conan 2.29.1(OHOS 适配版) |
| 配方仓库 | build_in_harmonyos(AtomGit) |
| PR | #13869 |
摘要:kf5wayland 5.116.0(KDE 的 Wayland 协议库)适配鸿蒙 PC 要过三道坎:上游 Qt6 分支的 4 处缺陷(上游 CMake 名义双支持 Qt5/Qt6,Qt6 分支却在 find_package、硬编码 Qt5:: 目标、版本钉死变量等处遗漏——configure 必然失败,4 个最小补丁修复)、OHOS 的 AF_UNIX 平台事实(filesystem namespace bind 一律 EPERM,且策略以 1-40 分钟为周期三态振荡——LD_PRELOAD shim + bind+connect 回环探测 + ctest 双尝试)、打包阶段的二进制修复(依赖 .so 无 SONAME → DT_NEEDED 烙上原构建机绝对路径 → .dynstr 同长度原位归一化 + 原位重签)。最终:上游 47 个 ctest 目标本机实注册 46(1 项 weston 门控),排除 17 项逐项技术原因(9 确定性 + 2 负载翻转 + 6 批量负载敏感,双窗口隔离复跑交叉验证),实跑 29 项全部通过 29/29(四次独立实测 64.31 / 64.63 / 64.42 / 65.43 sec,第四次为 2026-10-18 真机重跑、途中活体重现三态振荡);消费者测试 1/1(5 断言,含 .codesign 平台断言);CI 12 个可见轮次 5 绿 + 3 平台解析器假阳性 + 4 环境故障(作废注因),PR 合入 main 并发布制品仓。
开场定性:KWayland 是"协议绑定库"——不画像素,管 Wayland 合成器的客户端/服务端协议消息面。这类库的适配难度不在单文件编译,而在三层"上游不知道平台、平台不承诺稳定"叠加:上游 Qt6 分支的半成品参数化(缺陷在上游自己的代码里)、OHOS AF_UNIX 策略随时间振荡(同一份代码不同时刻跑,要么绿要么全灭)、制品仓二进制的契约破损(无 SONAME 依赖)。三层每一层都要源码级/字节级定位,"试到能跑"的路径走不通。全文所有数字均可在仓库文件与 CI 记录中溯源。
声明:
- 上游 47 个 ctest 目标中 18 项未运行:1 项 weston 门控未注册(上游边界,OHOS 无 weston)+ 17 项排除(逐项双窗口实测技术原因,见 4.1 矩阵);其中 9 项确定性失败含 2 项由 qtbase 6.11.1.2 testlib qCritical→qFatal 行为变化导致(上游 issue 检索无公开记录)——“29/29"指"本机可跑的 29 项全过”,不是"上游测试全过"。
- 15 个交互测试程序(xdgtest/paneltest/waylandservertest 等)需真实 Wayland 合成器环境,上游自身不注册 ctest,本次适配未引入 weston/KWin 依赖链——核心功能边界见 4.1。
- 全部结论限定于:鸿蒙 PC aarch64 真机+ 本构建配置(Qt6 谱系,5 个 host 依赖 + 2 个 build 依赖)。
一、背景:为什么值得做
KWayland 是 KDE 桌面的 Wayland 协议库(client + server 双角色):KDE 的 Wayland 组件(plasma-wayland-protocols、kwin 等)靠它接上合成器协议面。鸿蒙 PC 桌面生态的窗口合成/输入协议/表面管理需要这一层协议库;本次适配还携带 plasma-wayland-protocols 1.9.0(制品仓无此包——配方携带官方 tarball + 合成 Config/ConfigVersion 暴露 PLASMA_WAYLAND_PROTOCOLS_DIR,libkscreen/6.7.2 仓内先例模式),下游 KDE Wayland 依赖链从这一层开始可逐级推进。
选 kf5wayland 做适配,理由三层:
- 生态价值:协议库是桌面合成栈的底座,下游链条(KWin → plasma 组件)长,一个库解锁一整条线;
- 谱系互补:仓库有在做的 Qt5 谱系(kwayland5 / 自建 qt 5.15.19.2 Wayland 面),Qt6 谱系(qtbase/6.11.1.2 + qtwayland/6.11.1)由本 PR 提供——同一上游两条谱系,给生态第二选择(Qt6 线依赖闭包更小:5 个 host 依赖全在制品仓、零新增前置包,且裁掉 Qt5-only 的 xkbcommon 直接依赖);
- 难度覆盖:同时具备三类典型高难度特征——上游代码缺陷(Qt6 分支遗漏,需源码级补丁)、平台策略不稳定(AF_UNIX 三态振荡,需运行时探测 + 自我重试)、二进制契约破损(无 SONAME DT_NEEDED,需打包阶段字节级修复)。
二、环境与差异前置
环境表:
| 项 | 实测值 | 测量命令 / 来源 |
|---|---|---|
| 设备 | HUAWEI MateBook Pro(HAD-W32) | param get const.product.model |
| 系统 | HarmonyOS 6.1.0.117(SP68C00E100R13P3),OpenHarmony-6.1.0.115,API 23 | param get const.product.software.version / const.ohos.fullname |
| 内核 | HongMeng Kernel 1.12.0 #1 SMP | uname -a |
| 架构 | aarch64 | uname -m |
| 编译器 | clang 15.0.4(Target: aarch64-unknown-linux-ohos,/data/service/hnp/bin/clang)+ libc++ | clang --version |
| 构建工具 | ctest 4.1.2 / readelf(GNU 2.45) | 配方构建日志 / 实测 |
| 包管理 | Conan 2.29.1(OHOS 社区适配版,os=OHOS 有效枚举) | conan --version |
| CI profile | os=OHOS 6.0 / arch=armv8 / cppstd 17 / Release | ci/conan/profiles/ohos-aarch64(profile os.version=6.0 为 CI 构建目标设置,≠ 设备系统版本) |
| 上游源 | 官方:download.kde.org/stable/frameworks/5.116/kwayland-5.116.0.tar.xz(本机不可达);实际使用/引用:bfsu 镜像(国内可达,curl -sI = 200,Content-Length 337,540) | conandata.yml 双源 + curl -sI(2026-10-18 实测) |
| 源码 sha256 | 88b5970c2d6f6d5f46e6ffac66233bf23f2fcf016dea39fffbd9f47b1fc0aed8(与本机留存的官方 tarball 副本逐字节一致) | conandata.yml + sha256sum(实测) |
| 构建规模 | 构建树 149MB;包内 lib 共 3.6MB(client 1.5MB / server 2.0MB) | du -sh / ls -lh(实测) |
差异点总表(每个差异点对应第三节坑位;#3/#5 同属坑4 的 AF_UNIX 处理设计):
| # | 差异点 | 上游/通用假设 | 鸿蒙 PC 实际情况 | 对应坑位 |
|---|---|---|---|---|
| 1 | Qt6 分支 CMake | 上游双支持 Qt5/Qt6(Qt${QT_MAJOR_VERSION} 参数化模式是上游代码) | Qt6 分支 4 处遗漏:find_package(QtWaylandScanner) 只在 Qt5 分支 / Qt5::Gui+Qt5::Concurrent 硬编码 / KDE_INSTALL_INCLUDEDIR_KF5 在 Qt6 下为空致安装路径塌缩 / INSTALL_INTERFACE 含空 _KF5 分量 → configure 必然失败 | 坑1+ 坑2 |
| 2 | C++ 名称遮蔽 | C 头 C/C++ 共用无碍 | wayland client 协议头声明同名 request 函数 wl_shell_surface_resize,C++ 下遮蔽枚举类型名;Qt6 头重组另丢测试文件传递 include | 坑3 |
| 3 | AF_UNIX socket | filesystem unix socket 可用 | OHOS 用户态无允许 fs AF_UNIX bind 的目录(/tmp EROFS / HMDFS EPERM / /dev/shm EACCES);abstract namespace 可用;libwayland 只认 fs 名 | 坑4(LD_PRELOAD shim) |
| 4 | AF_UNIX 策略稳定性 | 平台策略稳定 | 三态振荡(abs-OK / fs 分裂态 / abs-EPERM),周期 1-40 min;bind-only 探测误判,29 项 2 sec 内全灭(3 次实证) | 坑5(回环探测 + 双尝试) |
| 5 | socket 路径长度 | 路径长度无限制 | libwayland 在 bind 前校验解析后全路径 ≤108 字节(sun_path);conan 深路径 + XDG 前缀 + 50 字符名 → 超限(库内校验,shim 拦不到) | 坑4(配套:sun_path 108 短路径探测) |
| 6 | 依赖 .so SONAME | 依赖带 SONAME | 制品仓 expat .so 无 DT_SONAME → DT_NEEDED 烙上原构建机绝对路径 → L5b 门禁 fail + 跨机不可解析 | 坑6(同长归一 + 重签) |
| 7 | 宿主工具签名 | 环境工具可直接用 | wayland-scanner 未签名被拒载;.pc 前缀指向原构建机 | 坑7(E429 四步法 + .pc 重定位) |
| 8 | Qt 6.11 testlib 行为 | qCritical 非致命 | testlib 将 qCritical 按 qFatal 执行(qtestcase.cpp:2599 实证)→ KF5(2023)测试套件 qCritical 错误路径整二进制 SIGABRT(2 例实证) | 4.1 排除矩阵 |
三、适配过程
侦察(动手前):W0 依赖发布预检 14/14 全部已发布制品仓(2026-09-28,拉代码前执行)——无需先适配任何前置依赖;构建系统为 CMake + ECM(conan create 一把流,ctest 内嵌);第二源 plasma-wayland-protocols 1.9.0 制品仓无此包(已实证),按仓内先例(libkscreen/6.7.2 模式)配方携带官方 tarball + 合成 Config/ConfigVersion 暴露 PLASMA_WAYLAND_PROTOCOLS_DIR;测试策略定为"本机可运行子集真机实跑 + 消费者测试 + 排除矩阵逐项技术原因 + 分层如实申报",47/46/17/29 双口径从侦察阶段就锁定(口径声明见 4.1)。
依赖树:
kf5wayland/5.116.0
├── requires(host,5 个)
│ ├── qtbase/6.11.1.2 # Qt6 谱系基座(qtwayland 严格 pin .2 非 .3)
│ ├── qtwayland/6.11.1 # Qt Wayland 支持库
│ ├── wayland/1.22.0 # Wayland 协议实现(client+server)
│ ├── wayland-protocols/1.36 # 协议 XML 定义
│ └── egl/1.0.5
├── build_requires(2 个)
│ ├── ecm/6.20.0 # Extra CMake Modules(KDE 构建基础设施)
│ └── libxml2/2.15.3.1 # wayland-scanner 动态链接 libxml2(build 上下文同架构供给)
└── 第二源(不入制品仓,recipe 携带)
└── plasma-wayland-protocols/1.9.0 # 官方 tarball + 合成 Config 暴露 PLASMA_WAYLAND_PROTOCOLS_DIR
坑1|Qt6 分支漏 find_package(QtWaylandScanner)(configure 必然失败)
- 现象:交叉 configure 失败——
src/server/CMakeLists.txt:233调用ecm_add_qtwayland_server_protocol报"函数未定义"。 - 定位:上游 CMake 名义双支持 Qt5/Qt6,主体是
Qt${QT_MAJOR_VERSION}参数化模式(这是上游自己的代码),但find_package(QtWaylandScanner REQUIRED)(该函数的提供者,ECM FindQtWaylandScanner)只写在if (QT_MAJOR_VERSION STREQUAL "5")分支里——Qt6 分支整段缺位,函数从未被定义。 - 方案:补丁 0001(顶层 CMakeLists.txt,+2 行):else() 分支补同一 find_package。零平台宏。
- 经验:上游"双版本支持"的 CMake 常是手工参数化,
find_package类调用容易被版本分支门控遗漏;交叉构建报"函数未定义"先查函数定义处的 find_package 是否在同一个条件分支,而不是怀疑工具链。
坑2|server 侧三处缺陷(硬编码 Qt5:: + 版本看紧变量 + 空分量路径)
- 现象:坑1 修完后继续推进:server 目标链接
Qt5::Gui/Qt5::Concurrent(Qt6 下目标不存在);即便换掉,KDE_INSTALL_INCLUDEDIR_KF5在 Qt6 下为空 →install(...)与ecm_generate_pri_file两处的安装路径塌缩为绝对路径/KWayland/Server,cmake --install无权限失败。 - 定位:
src/server/CMakeLists.txt三处缺陷逐一比对顶层与 src/client 的写法——顶层与 client 本就用通用变量KDE_INSTALL_INCLUDEDIR_KF和Qt${QT_MAJOR_VERSION}::,server 是上游 Qt6 分支漏改的孤岛;第三处target_include_directories的 INSTALL_INTERFACE 是${KDE_INSTALL_INCLUDEDIR_KF}/KWayland;${KDE_INSTALL_INCLUDEDIR_KF5}/KWayland/Server双分量拼接,空变量拼出含空分量的路径。 - 方案:补丁 0002(5 行):①
Qt5::→Qt${QT_MAJOR_VERSION}::×2;②KDE_INSTALL_INCLUDEDIR_KF5→KDE_INSTALL_INCLUDEDIR_KF×2;③ INSTALL_INTERFACE 收敛为单一通用变量分量。 - 经验:上游 Qt5/Qt6 双支持的判定信号 = 是否存在
Qt5::前缀与*_KF5后缀版本看紧变量;DESTINATION ${空变量}/...塌缩成绝对路径是"变量版本钉死"的通用症状,install 阶段无权限失败时优先怀疑路径变量而不是权限本身。
坑3|C++ 同名遮蔽 + Qt6 头重组丢 include(2 个 2 行级补丁)
- 现象:编译期
src/server/shell_interface.cpp报wl_shell_surface_resize相关类型错误;测试文件tests/renderingservertest.cpp报QFile未声明。 - 定位:wayland client 协议头声明了同名 request 函数
wl_shell_surface_resize,C++ 下它遮蔽了同名枚举类型——模板实参resizeCallback<wl_shell_surface_resize>与形参edgesToQtEdges(wl_shell_surface_resize edges)两处按函数名解析;C 语义下函数/枚举不冲突,C++ 统一命名空间才暴露。renderingservertest 侧:Qt6 头文件重组后<QFile>的传递 include 链断开(仅测试文件,库代码不受影响)。 - 方案:补丁 0003(2 行):两处改用
enum wl_shell_surface_resize显式限定类型名;补丁 0004(+1 行):补#include <QFile>。零逻辑/断言改动——上游测试用例修改口径如实申报:是,1 个文件 1 行。 - 经验:"C 头在 C++ 下编译失败"先查同名遮蔽(函数 vs 类型 vs 宏,C++ 统一命名空间);测试文件独有的 include 错误多为 Qt 版本间头重组的传递依赖断裂,补显式 include 即可,不要动库代码。
坑4|OHOS 无 filesystem AF_UNIX bind(LD_PRELOAD shim,新平台代码)
- 现象:29 项 display 类测试全部起不来:
The Wayland display cannot be started!(QFATAL,2 sec 内全灭)。 - 定位:逐目录实测 OHOS 用户态 AF_UNIX filesystem namespace:
/tmpEROFS(只读)、HMDFS 目录 EPERM、/dev/shmEACCES——没有任何一个可写目录允许 filesystem AF_UNIX bind;abstract namespace(sun_path[0]=='\0',@前缀名)bind/connect 正常。而 libwayland 的 display 实现只构造 filesystem 路径名——改它 = 改依赖库源码,边界外。 - 方案:LD_PRELOAD shim(
libkf5bindshim.so,conanfile 内嵌约 85 行 C 源码,clang -shared -fPIC -O2编译,18.7KB)——拦截bind/connect:filesystem 名(sun_path[0] != '\0')→ abstract 名@+basename(strrchr('/')归一:服务端 XDG 前缀名与客户端裸名归一到同一 abstract 名,两侧必然相遇,与前后缀无关);bind 时在原路径创建存根普通文件,满足测试的 socket 文件存在性断言(服务端 release unlink 时一并清掉);dlsym(RTLD_NEXT, ...)句柄在 constructor(单线程加载期)初始化——懒初始化在 QThread 首调 bind/connect 竞争 → 非确定性 SIGSEGV(实测两轮复现,详见 3.9)。 - 配套(sun_path 108 短路径探测,差异点 #5):libwayland 在 bind 前对解析后 socket 全路径校验 ≤108 字节(POSIX sun_path 上限;库内校验,shim 拦不到)——conan 缓存构建树路径深(69+ 字符前缀)+ XDG_RUNTIME_DIR 前缀 + 测试最长 50 字符 socket 名 → 超限 → display.start() 直接失败。conanfile 的
_short_dir候选按序探测/tmp/<name>→$HOME/.kf5/<name>(makedirs + .probe 写读,首个可写胜出;OHOS /tmp 只读时自然落到 H O M E / . k f 5 ) + 长度断言 ‘ l e n ( x d g ) + 1 + 50 ≤ 108 ‘ f a i l − f a s t (实例值 ‘ HOME/.kf5)+ 长度断言 `len(xdg)+1+50 ≤ 108` fail-fast(实例值 ` HOME/.kf5)+长度断言‘len(xdg)+1+50≤108‘fail−fast(实例值‘HOME/.kf5/xfullxdg` 38 字符,38+1+50=89≤108)——不让错误深到 libwayland 层变成晦涩的 socket path 报错。 - 经验:OHOS 上 AF_UNIX 依赖库的适配首选 abstract namespace 重映射而不是要求平台开放 fs bind;shim 内 RTLD_NEXT 必须 constructor 期初始化;"basename 归一到同一 abstract 名"是让 server/client 两侧在不同命名习惯下必然相遇的关键;socket 路径长度在 conan 深路径环境下必须提前 fail-fast 校验。
- 入档:本案入档知识草稿 DRAFT-14402(severity 3;rec:ld-preload-abstract-socket-remap;forbid:懒 dlsym 初始化)与 DRAFT-14404(severity 2;rec:short-xdg-candidate-probe),条目见第五节。
坑5|AF_UNIX 策略三态振荡(回环探测 + ctest 双尝试,新范式)
- 现象:同一份代码、同一个构建树,不同时刻跑 ctest 要么 29/29 全绿要么 2 sec 全灭。
- 定位:2026-10-01 实测时间线(notes.md 在案):20:16 abs-OK → 20:32 abs-EPERM(abstract 也被拒,29/29 QFATAL)→ 21:00 fs-EPERM/abs-OK → 21:33 fs-OK——OHOS 对 AF_UNIX 的策略呈三态振荡(abstract-OK / filesystem 分裂态 / abstract-EPERM),周期 1-40 分钟。更隐蔽的是分裂态:fs bind-OK 但 fs connect-EPERM——bind-only 探测会把分裂态误判为"fs 可用",不带 shim 直接开跑就是 29 项 2 sec 全灭。这个误判实证了三次:10-01 23:25、10-01 23:31(mode log 三行时间戳 23:31:14 abstract → 23:31:18 filesystem → 23:31:20 abstract,已备份在案)、10-18 14:03(本文写作日真机重跑:attempt 1 rc=8 → 重探 + shim 后 attempt 2 29/29,65.43 sec)。
- 方案:conanfile 三层设计——①
_afunix_namespace_probe:每 namespace 做完整 bind+listen+connect 回环自检(abstract 优先 → fs 回退 → None),探测的是"能连"不是"能绑";② ctest 双尝试:attempt 1 失败后重新探测再 attempt 2(运行中策略可能翻转),LD_PRELOAD=libkf5bindshim.so按探测结果 set/pop;③ 探测结果为 None(双 namespace 全灭窗口)时如实报错而不是硬跑。 - 经验:平台策略不稳定时,"探测→条件路径→失败重探"是自我修复的最小完备形态;探测必须覆盖完整连接路径(bind-only 探测在任何"分裂态"平台上都是陷阱)。该 shim 为上游不存在、Conan center 不存在的新平台后端代码;"运行时环境探测 + 条件代码路径 + 自我重试"范式可推广至 OHOS 上其他 AF_UNIX 依赖库的适配。
- 入档:与坑4 同条目 DRAFT-14402(三态振荡 + 回环探测 + 双尝试为其核心内容)。
坑6|依赖 .so 无 SONAME → DT_NEEDED 烙上原构建机绝对路径(.dynstr 同长度归一 + 原位重签,新构建路径)
- 现象:CI L5b SONAME 门禁 fail;且构建产物 libKF5WaylandClient.so 的 DT_NEEDED 里出现一条完整绝对路径(
/home/.../conan 缓存下的 libexpat.so 全路径)而不是基名——跨机消费者按路径解析必然失败。 - 定位:依赖 expat 的 .so 没有 DT_SONAME。ELF 链接规则:目标无 SONAME 时,链接器把链接期使用的完整路径写入 DT_NEEDED。问题出在制品仓上游产物,本库无法要求它补 SONAME;
--as-needed也不行——kf5wayland 两个 .so 各有约 16 个未定义XML_*符号,直接引用无法剔除。 - 方案:
_normalize_dyn_needed_paths(conanfile 打包阶段字节级原位补丁)——扫描 .so 所有 NUL 结尾串,凡/开头且 basename 匹配^lib.*\.so(\.\d+)*$且 basename 不超原长的,同长度替换为 basename(NUL 补零):dynamic section 的 DT_NEEDED 偏移不变(只动 .dynstr 内容),这是"不打补丁工具链"的前提下唯一安全的原位写法。归一化必须先于签名执行(签名覆盖文件字节,顺序反了归一化作废)。随后_sign_elf_in_place:objcopy --remove-section .codesign(幂等,与 release 流水线再签兼容)→ binary-sign-tool 自签 → 校验产物为可运行 ELF 且 .codesign 已添加。package() 对 lib/ 下每个非符号链接 .so 按"归一化 → 重签"固定顺序执行。 - 实证(2026-10-18 本机 readelf):libKF5WaylandClient.so 11 个 NEEDED 全为基名(含归一化后的
libexpat.so,另 libQt6Core/Gui/Concurrent/WaylandClient.so.6、libwayland-client/server/cursor.so.0、libxkbcommon.so.0、libc++_shared.so、libc.so)+ SONAME=libKF5WaylandClient.so +[40] .codesign PROGBITS在案。可加载性闭环:29 项 ctest 测试二进制实际加载并执行构建树 .so 全部通过(29/29 即加载实证)。 - 经验:制品仓产物"无 SONAME"的通用修复 = .dynstr 同长度原位归一(同长是硬约束:长了会挪动后续串与偏移);任何字节级 .so 补丁都必须排在签名前;patchelf 不可用(改写会破坏 OHOS 签名校验拒载)。
- 入档:本案入档知识草稿 DRAFT-14403(severity 2;rec:dynstr-inplace-basename-resign;forbid:patchelf),条目见第五节。
坑7|未签名宿主工具被拒载 + .pc 跨机前缀(E429 四步法 + .pc 重定位)
- 现象:build 上下文 wayland-scanner 执行被平台拒载(二进制未签名);且 wayland/wayland-protocols 的 .pc 文件 prefix 指向原构建机路径,pkg-config 跨机不可用。
- 定位:OHOS 拒载未签名二进制是平台强制(ELF 签名,CI 侧对应 sign.sh)——build 上下文工具同样受此约束,"编译机工具"与"产物"在签名要求上没有豁免;.pc 前缀则是 Conan 缓存路径跨机不可移植的常规症状。
- 方案:命中存量知识 E429 四步法:① 复制到私有位置 ② objcopy 去旧 .codesign ③ binary-sign-tool 自签 ④ chmod 755;签名版 scanner 置于 PATH 首位,
_prepare_host_scanner带--versionfail-fast 预检——broken scanner 在构建开始就暴露而不是中途发现。配套构建环境处理:.pc 跨机 prefix 重定位(PKG_CONFIG_PATH 暴露 fixed_pkgconfig 目录)+ wayland-protocols.pc 合成 + Qt mkspecs 兼容垫片。 - 经验:OHOS 上"任何要执行的二进制"(含构建期宿主工具)都默认未签名不可执行,四步法应作为 build 上下文工具的标准预检步骤固化进配方;fail-fast 预检(
--version)是低成本高收益的失败提前暴露手段。 - 入档:存量知识 E429 命中(非新草稿),条目见第五节。
3.9 误判自曝(单列)
- bind-only 探测误判分裂态(三次实证):最初探测只测 fs bind 成功即判定"fs 可用、免 shim"——分裂态下(fs bind-OK + connect-EPERM)该判定成立但结论错误,29 项 2 sec 全灭。10-01 23:31 mode log 三行(abstract→filesystem→abstract,4 秒两跳)坐实探测本身在振荡中不可信。修复 = 回环探测(坑5)。教训:平台策略振荡期,任何单次探测结果都只是采样值,必须用"完整连接路径 + 失败重探"兜底。
- connectionDied 簇启发式误归因(口径演进):09-29 初版排除表用源码 grep
connectionDiedSpy枚举出"connectionDied 簇 11 项"作归因——高负载下该簇"每轮全量换项翻转、重试 3/3 败",暴露了簇归因是启发式不是机制。10-01 A1 双窗口隔离复跑(低负载窗口逐项重跑 17 项)细化为 9 确定性 + 2 负载翻转 + 6 批量负载敏感(notes.md 明言"替代旧表 connectionDied 簇 11 推断")。教训:测试失败归因必须做负载隔离复跑,源码 grep 出的"簇"只是嫌疑名单不是结论。 - 懒 dlsym 初始化 → 非确定性 SIGSEGV:shim 初版把
dlsym(RTLD_NEXT)放在首次 bind/connect 调用时懒初始化——QThread 场景下首调竞争,SIGSEGV 间歇出现。两轮复现后改 constructor 初始化根治。教训:LD_PRELOAD shim 的全局状态一律 constructor 期初始化(已写进 DRAFT-14402 forbid 字段)。 - 归一化/签名顺序:初版把重签放在归一化前,归一化改的字节被签名覆盖校验打回——顺序固定为"归一化 → 重签"后一次通过。教训:字节级 .so 补丁链必须显式定序,签名永远是最后一步。
四、验证(分层,禁止相加)
口径声明:上游 ctest、消费者测试(test_package)、CI 轮次、制品层检查是四个互不包含的集合,各层分别统计、分别陈述,不做相加/改名/拼接。ctest 双口径全程并列申报:
- 47 = 上游 ctest 目标总数(tests/autotests/:client 42 + server 5,Qt Test 框架)
- 46 = 本机实注册数(1 项
testWaylandFullscreenShell被上游find_program(WESTON)门控未注册——上游边界,OHOS 无 weston,非适配裁剪) - 17 = 隔离 -E 排除项(逐项技术原因见 4.1 矩阵,双窗口隔离复跑交叉验证)
- 29 = 实跑项数,29/29 全部通过(以实注册 46 为分母 100%;以上游 47 为分母 61.7%——未运行的 18 项逐项原因见 4.1,不做无口径声明的并列)
4.1 上游测试(R51 三步法台账:清点 → 实跑 → 如实回填)
清点(上游测试资产):
| 测试集 | 规模 | 注册状态 |
|---|---|---|
| tests/autotests/ ctest(Qt Test 框架) | 47 项(client 42 + server 5) | 本机实注册 46(1 项 weston 门控) |
| tests/ 交互测试程序 | 15 项(xdgtest/paneltest/waylandservertest 等) | 上游自身不注册 ctest(需真实 Wayland 合成器);本次适配未引入 weston/KWin 依赖链——与上游行为一致 |
实跑(29 项,四次独立测试):四次构建源码/4 补丁/测试内容完全一致、构建树不同(ctest 4.1.2,QT_QPA_PLATFORM=offscreen):
| 轮 | 构建产物 | 时间 |
|---|---|---|
| v7 ctest -V 单遍 | v4 构建树 | 2026-10-01 23:25-23:26,64.31 sec,29/29 |
| v8 ctest -V 单遍(双尝试) | v4 构建树 | 2026-10-01 23:31-23:32(attempt 1 23:31:18 2 sec 全灭 → 重探 → attempt 2),64.63 sec,29/29 |
| v5 conan create 复验 | v5 构建(最终 conanfile,回环探测版) | 2026-10-01 23:44-23:51,64.42 sec,rc=0,0 ERROR |
| 2026-10-18 真机重跑(本文写作日,4.3 S1) | v4 构建树 | 14:05-14:06(attempt 1 rc=8 → 重探 + shim),65.43 sec,29/29 |
逐项耗时(v7 29 行表):最慢 testIdle 46.69 sec / testTextInput 5.81 sec / testFakeInput 3.17 sec / testXdgShellStable 2.34 sec / testXdgShellV5 2.06 sec / testDragAndDrop 0.60 sec / testWindowmanagement 0.57 sec,其余 23 项均 ≤0.30 sec。
排除矩阵(17 项逐项技术原因,双窗口隔离复跑交叉验证)——窗口 A 23:26:47-23:27:41(load 21.03)/ 窗口 B 23:32:25-23:33:09(load 20.35),两窗口均 fs full-OK,逐项签名证明测试真实执行、无连接类失败污染:
| 分类 | 项 | 判定依据 |
|---|---|---|
| 9 确定性失败(双窗同 FAIL,与负载/窗口无关) | Registry(SEGFAULT)/ Surface(SIGSEGV,SEGV 取证在案)/ Seat(QFATAL qtestcase.cpp:2599)/ SubSurface(Test failed)/ PlasmaShell(Failed;上游佐证 plasma-wayland-protocols MR !48、KWayland MR !75)/ PlasmaWindowModel(Failed)/ PointerConstraints(QFATAL 2599)/ QtSurfaceExtension(Test failed)/ DataDevice(Failed) | 其中 Seat/PointerConstraints = qtbase 6.11.1.2 Qt 6.11 testlib 将 qCritical 按 qFatal 执行(qtestcase.cpp:2599 实证)→ KF5(2023)测试套件按旧 Qt 非致命假设写的错误路径 qCritical 整二进制 SIGABRT;A5 上游 issue 检索无公开记录(差异点 #8) |
| 2 负载/时序翻转(窗口 A FAIL → 窗口 B PASS) | WaylandShell(B: 1 passed 555ms)/ Region(B: 9 passed 13ms) | 高负载窗口间歇失败、隔离重跑通过 |
| 6 批量负载敏感(隔离单项通过;高负载批量下间歇失败——QSignalSpy 时序预算对共享机负载敏感) | Error(4 passed)/ XdgShellV6(2 passed)/ SubCompositor(5 passed)/ Compositor(4 passed)/ DataSource(4 passed)/ ShmPool(10 passed,纯单元测试) | 隔离重跑逐项 PASS |
| 1 未注册 | testWaylandFullscreenShell | 上游 find_program(WESTON) 门控,OHOS 无 weston(上游边界,非适配裁剪) |
合计 17 排除 + 1 未注册 = 18 未运行;46 - 17 = 29 实跑。逐项原始输出:仓库 reports/ctest-excluded-retest.txt(300KB)。
口径演进(如实申报):09-29 初版排除表用源码 grep connectionDiedSpy 枚举"connectionDied 簇 11 项"作归因(test-report.md 在案);高负载下该簇"每轮全量换项翻转、重试 3/3 败",10-01 A1 双窗口隔离复跑细化为上表 9+2+6(notes.md 明言"替代旧表 connectionDied 簇 11 推断")。误判复盘见 3.9-2。排除总数 18 / 实跑 29 / -E 列表内容三处(pr-body.md / manifest.yaml / conanfile.py)字符级一致未变。
4.2 消费者测试(conan L1 test_package)
1 例 5 断言,1/1 通过(0.06 sec,v5 日志 L113870-72):
KWayland::Client::Compositor构造 + metaObject 非空KWayland::Client::Surface构造 + metaObject 非空KWayland::Server::Display构造 + metaObject 非空 +Display::createCompositor()工厂返回非空- 版本头宏
KWAYLAND_VERSION_STRING编译输出(kwayland_version.h 存在且生效) - 鸿蒙平台断言:打包 libKF5WaylandClient.so 含非空 .codesign ELF section(自实现 ELF64 section 表解析:e_shoff/shentsize/shnum/shstrndx → shstrtab → 匹配 section 名 + size>0;纯文件 I/O,无运行时链接、无合成器、离线安全;读 section 比 dlopen 稳,不依赖 LD_LIBRARY_PATH/rpath)
CMake:find_package(KF5Wayland 5.116.0 REQUIRED CONFIG)(主配方 cmake_find_mode=none,上游自装 Config)+ 链接 KF5::WaylandClient/KF5::WaylandServer + IMPORTED_LOCATION/_RELEASE/_DEBUG 三级回退。R34 实质依赖满足(构造 + 工厂调用 + 元对象断言,非空壳)。
4.3 29 项 ctest 真机情况

4.4 制品层验证(DT_NEEDED 归一化 + OHOS 签名)
制品已发布制品仓(ohpcd):recipe efb63e55af46cd8f6c4ca6af1e072737 + package a95b9ba7cf6f162be435d1f1321449e1f531df8a(armv8 / Release / clang / cppstd 17 / libc++),上传 2026-10-10 15:16:53 UTC(北京 23:16:53,合入后约 30 分钟,release 流水线产物)。制品层两个硬产物(坑6)2026-10-18 本机 readelf 实证:
- libKF5WaylandClient.so 11 个 NEEDED 全为基名(含归一化后的
libexpat.so;另 libQt6Concurrent.so.6、libxkbcommon.so.0、libQt6WaylandClient.so.6、libQt6Gui.so.6、libQt6Core.so.6、libwayland-cursor.so.0、libwayland-client.so.0、libwayland-server.so.0、libc++_shared.so、libc.so) SONAME= libKF5WaylandClient.so +[40] .codesign PROGBITS在案(签名被平台接受)

4.5 CI(12 个可见轮次完整表,环境故障标"作废"注因)
口径:以 PR 讨论区 ohpcd-robot 可见构建评论为口径(10-05/10-06/10-08 有 5 次
/rebuild触发未见对应构建评论——调度静默、无构建号,不入表)。
| # | 构建 | 时间(+08) | 结果 | 备注 |
|---|---|---|---|---|
| 1 | #64995 | 09-29 | test_package 标"失败"(7 步全 ✅) | 平台解析器假阳性(下表 4 证据链) |
| 2 | #64999 | 09-29 | 同上 | 同上假阳性 |
| 3 | #65646 | 09-30 | 同上 | 同上假阳性 |
| 4 | #65770 | 09-30 | worker-prep/cleanup 失败 | 环境故障,作废 |
| 5 | #65774 | 09-30 | 全绿(SHA 00de530b) | 首绿 |
| 6 | #66515 | 10-02 | 全绿(SHA 738281a7) | 评论 192407784,7 步全 ✅ |
| 7 | #68883 | 10-03 | worker-prep 失败 | SSH bridge connection refused,环境故障,作废 |
| 8 | #74940 | 10-09 | 全绿 | |
| 9 | #74941 | 10-09 | 全绿 | |
| 10 | #75766 | 10-10 | 网络问题(SSH bridge refused) | 环境故障,作废 |
| 11 | #76176 | 10-10 | 网络问题(SSH bridge refused) | 环境故障,作废 |
| 12 | #80067 | 10-10 | 全绿 | 合入前终绿 |
→ 2026-10-10 合入 main(clancyxie,squash)
解析器假阳性判定(#64995/#64999/#65646 三轮同型,4 证据链):① 三轮均"7 个构建步骤全 ✅(含 conan-build-test ✅)“却标 test_package"失败”——自相矛盾;② 本地 W3/CI 模拟(.ci-result.json 含日志 SHA256 留档)为绿;③ 同配方后续 CI(#65774/#66515/#80067)全绿;④ 失败评论未附任何 test_package 断言输出(真实测试失败必附断言行)。→ 定性平台评论解析器假阳性,非测试失败。
检视:atomgit-bot AI 检视跳过 4 次(09-29 ×2 checkout 超单步时间上限(共享盘拥塞);10-05 / 10-09 ×2 变更过大——15 个可审文件、8678 行,预估 8 轮分片审查超处理范围)。由本地 AI 检视(无 P0/P1,结论发 PR 讨论区评论 192420215)+ W4 四轮独立审计(4.6)+ 人工评审(clancyxie 合入)补位。
4.6 W4 Pre-PR 独立审计
4 轮(09-29 ×2 / 09-30 ×1 / 10-02 ×1,上下文隔离 worker 子任务),终判 verdict=pass(P0=0 / P1=0 / P2=6 / P3=4);17 个被审文件 sha256 佐证在案。欺骗检测类别一全过:R34 实质测试(非空壳)/ R9 无空壳 / sha256 真实 / 4 补丁无平台宏 / 无二进制入 PR。
本地 conan-verify 门禁(如实申报):G0-G4 PASS;L2/L4/L5 = PASS(跳过,无 cache 路径,工具原始输出,verdict 以判定为准)——最终发布态的 L4/L5 以 CI 平台侧扫描为准(#65774/#66515/#80067 三轮全绿实证)。
4.7 复现速查
#命令 1|全量构建 + 29 项 ctest + 消费者测试门禁(约 15-20 分钟,v4 实测 14 分钟含 ctest;依赖取制品仓)
cd <build_in_harmonyos 仓库根>
conan create archives/k/kf5wayland/5.116.0 --build=kf5wayland/*
#期望输出:AF_UNIX namespace probe (attempt 1/2): abstract(或 filesystem;双尝试设计内)
#→ 100% tests passed, 0 tests failed out of 29 + Total Test time (real) = 64.xx sec
#→ 100% tests passed, 0 tests failed out of 1 + kf5wayland 5.116.0: ... .codesign platform assertion passed
#命令 2|29 项 ctest 证据链独立重跑(约 3 分钟,需 v4 构建树存活;S1 截图命令源)
sh /storage/Users/currentUser/tmp/kf5-a1-v8.sh
grep -E "tests passed|Total Test time" /storage/Users/currentUser/tmp/kf5-ctest-full-raw-v8.txt | tail -2
#期望输出:100% tests passed, 0 tests failed out of 29 + Total Test time (real) = 64-66 sec
#命令 3|制品层验证:DT_NEEDED 全基名 + .codesign(秒级,S2 截图命令源)
PKG=$(conan cache path "kf5wayland/5.116.0#efb63e55af46cd8f6c4ca6af1e072737:a95b9ba7cf6f162be435d1f1321449e1f531df8a" | tail -1) && readelf -d $PKG/lib/libKF5WaylandClient.so | grep -E 'SONAME|NEEDED' && readelf -S $PKG/lib/libKF5WaylandClient.so | grep codesign
#期望输出:11 个 NEEDED 全基名(含 libexpat.so)+ SONAME ... [libKF5WaylandClient.so] + [40] .codesign PROGBITS
口径对应:命令 1 → 4.1(四次独立实测同源)+ 4.2(消费者测试 1/1)+ 4.5(与 CI conan-build-test 同源);命令 2 → 4.1 第 4 个数据点 / 4.3 S1(独立于命令 1);命令 3 → 4.4 制品层(坑6 两个硬产物)。
五、知识沉淀
| 编号 | 知识(错误签名摘要) | 来源坑位 | 复用价值 |
|---|---|---|---|
| DRAFT-14402 | OHOS 拒绝 filesystem AF_UNIX bind(EPERM);不可改源码的依赖库用 LD_PRELOAD shim 重映射 abstract namespace(含三态振荡 + 回环探测 + 双尝试) | 坑4/坑5 | OHOS 上所有 AF_UNIX 依赖库适配(severity 3;forbid:懒 dlsym 初始化——首调竞争非确定性 SIGSEGV) |
| DRAFT-14403 | 制品仓 .so 无 DT_SONAME → 下游 DT_NEEDED 烙上绝对路径;.dynstr 同长度原位归一化 + 原位重签 | 坑6 | 任何消费无 SONAME 制品的下游包(severity 2;forbid:patchelf——改写破坏 OHOS 签名校验拒载) |
| DRAFT-14404 | libwayland sun_path 108 硬限 × conan 深路径 → 短路径候选探测 + fail-fast | 坑4(配套) | conan 缓存构建路径下凡用 XDG_RUNTIME_DIR socket 的库(severity 2) |
| DRAFT-14405 | Qt 6.11 testlib 将 qCritical 按 qFatal 执行 → KF5(2023)测试套件 qCritical 错误路径整二进制 SIGABRT | 4.1 排除矩阵(差异点 #8) | Qt6 谱系上所有旧 KF5 测试套件(severity 2;forbid:把 qCritical 降级 qWarning——改测试语义) |
| E002 | /tmp 只读(TMPDIR 隔离) | 坑4 配套(存量命中) | 全部含 /tmp 逻辑的库 |
| E429 | 未签名工具四步修复(复制→去旧签→自签→chmod) | 坑7(存量命中) | OHOS 上全部 build 上下文宿主工具 |
状态:4 条草稿(.kv + .md 配套)已随 PR #13869 入仓,待主编 promote。
六、FAQ
Q1:29/29 是不是"上游测试全过"?
A:不是。口径:47 = 上游总数;46 = 本机实注册(1 项 weston 门控,上游边界);17 排除(9 确定性 + 2 负载翻转 + 6 批量负载敏感,双窗口隔离复跑交叉验证,逐项技术原因见 4.1 矩阵);29 实跑全过。"29/29"指本机可跑的 29 项全过。其中 2 项(Seat/PointerConstraints)根因在依赖链:qtbase 6.11.1.2 testlib 将 qCritical 按 qFatal 执行(qtestcase.cpp:2599 实证),上游 issue 检索无公开记录——如实申报,不改测试语义。
Q2:15 个交互测试程序(xdgtest 等)没跑,算适配完成吗?
A:上游自身就不注册这些项进 ctest(需真实 Wayland 合成器环境);上游在非合成器环境同样不 ctest 验证它们。本次适配未引入 weston/KWin 依赖链——边界与上游对齐,不宣称"全功能完成"(开场诚实声明第 2 条)。
Q3:CI 有 3 轮标"失败",为什么说通过?
A:#64995/#64999/#65646 三轮按 4 证据链判定为平台评论解析器假阳性(7 步全 ✅ 却标 test_package 失败自相矛盾 / 本地模拟绿 / 同配方后续 #65774/#66515/#80067 三轮全绿 / 失败评论无断言输出)。验收依据是 5 个全绿轮次;4 个环境故障轮标"作废"注因;12 轮完整表不删(4.5)。
Q4:本地 L2/L4/L5 门禁"跳过",算全过吗?
A:不算"本地 8 门禁全 PASS"。本地工具因无 cache 路径跳过 L2/L4/L5(工具原始输出,verdict PASS 是工具判定);最终发布态的 L4/L5 以 CI 平台侧扫描为准(#65774/#66515/#80067 三轮全绿实证)。4.6 如实申报。
Q5:所谓"新平台代码"具体是什么?不就是一个 shim?
A:三块——① LD_PRELOAD AF_UNIX shim(约 85 行 C,上游不存在、Conan center 不存在的新平台后端代码);② 运行时回环探测 + 条件代码路径 + 自我重试(双尝试)——范式级,可推广至 OHOS 上其他 AF_UNIX 依赖库;③ DT_NEEDED .dynstr 同长度原位归一化 + 原位重签——新构建路径,不 patch 工具链。4 个补丁共 10 行全部是对上游缺陷的最小修复(零平台宏)。
Q6:为什么选 Qt6 谱系而不是继续 Qt5 谱系?
A:仓库已有在做的 Qt5 谱系(kwayland5 / 自建 qt 5.15.19.2 Wayland 面,Qt5WaylandClient 缺失);Qt6 谱系(qtbase/6.11.1.2 + qtwayland/6.11.1)互补,依赖闭包更小(5 个 host 依赖全在制品仓、零新增前置包,且裁掉 Qt5-only 的 xkbcommon 直接依赖)——同一上游两条谱系,给生态第二选择。
七、总结
7.1 量化盘点
| 维度 | 数字 |
|---|---|
| 补丁 | 4 个(0001 +2 行 / 0002 5 行 / 0003 2 行 / 0004 +1 行,共 10 行,零平台宏) |
| 平台差异点 | 8 类(二、差异表;坑1–坑7 + 4.1 排除矩阵) |
| 新平台代码 | 3 块(AF_UNIX shim ~85 行 C / 回环探测 + 双尝试范式 / DT_NEEDED 归一化 + 重签) |
| 构建 | conan create 约 15-20 分钟(v4 实测 14 min 含 ctest);构建树 149MB |
| 上游测试 | 47/46/17/29 双口径;29/29 × 4 独立实测(64.31 / 64.63 / 64.42 / 65.43 sec) |
| 消费者测试 | 1/1(5 断言,含 .codesign 平台断言) |
| CI | 12 个可见轮次:5 绿 + 3 解析器假阳性 + 4 环境作废;2026-10-10 合入 |
| 制品 | 制品仓 ohpcd 发布(recipe efb63e55… / package a95b9ba7…),2026-10-10 23:16(+08)上传 |
| 知识 | 4 条草稿(DRAFT-14402–14405)+ 2 存量命中(E002/E429)随 PR 入仓 |
数字包含关系(防误读):47 = 46 实注册 + 1 未注册(weston 门控);46 = 29 实跑 + 17 排除;“18 未运行” = 17 排除 + 1 未注册(三者是同一集合的三种切法,禁止并列相加);29/29 的 4 个耗时是 4 个独立数据点(不同时刻/不同构建树),不是 4 轮平均;CI 5 绿 + 3 假阳性 + 4 作废 = 12 可见轮次(10-05/06/08 的 5 次 /rebuild 调度静默事件不计入 12)。
7.2 可复用方法(5 条)
- 零平台宏可移植策略:4 补丁全部对齐上游自身参数化写法(无
__OHOS__),未来同步上游零冲突面。 - 不可改源码依赖库的 LD_PRELOAD abstract-namespace 重映射(DRAFT-14402):shim 的 RTLD_NEXT 必须 constructor 期初始化;basename 归一到同一 abstract 名让两侧必然相遇。
- 平台策略振荡的探测范式(DRAFT-14402):完整连接路径回环探测 + 失败重探 + 双尝试——bind-only 探测在任何"分裂态"平台上都是陷阱。
- 无 SONAME 制品修复(DRAFT-14403):.dynstr 同长度原位归一(同长是硬约束)+ 补丁后重签(patchelf 不可用)。
- 测试失败归因的负载隔离复跑:双窗口交叉验证 + 排除矩阵逐项技术原因(源码 grep 的"簇"只是嫌疑名单,不是结论)。
7.3 流程 checklist(通用,可直接套用)
- 适配前依赖发布预检(W0)
- 动手前锁定双口径(上游总数 / 实注册 / 排除 / 实跑)
- 侦察:构建系统 + 测试资产清点(R51 第一步)
- 补丁对齐上游参数化风格(零平台宏)
- 消费者测试实质依赖 + 平台断言(.codesign)
- 测试失败归因做负载隔离复跑(双窗口交叉验证)
- CI 完整轮次表(不删失败轮,环境故障标"作废"注因)
- W4 独立审计(P0/P1 清零)
- 知识草稿入档(错误签名 + 根因 + 方案 + forbid)
- 制品仓发布 + 制品层跨机核验(readelf NEEDED/SONAME/.codesign)
7.4 后续工作
- 15 个交互测试程序需真实 Wayland 合成器(weston/KWin)——引入合成器依赖链为单独任务;testWaylandFullscreenShell 的 weston 门控在 weston 适配后可注册实跑。
- qtbase 6.11.1.2 testlib qCritical→qFatal 行为(Seat/PointerConstraints 2 项)——上游 issue 检索无公开记录,若确认属 Qt 行为变化应提交 Qt 社区反馈;本适配不改测试语义。
- 17 项排除矩阵不属于本 PR 范围:PlasmaShell/PlasmaWindowModel 簇有上游 MR 佐证(plasma-wayland-protocols !48 / KWayland !75),上游修复后可复验转正。
- CI 5 次 /rebuild 调度静默事件(10-05/06/08 无构建评论)——口径以 PR 讨论区可见轮次为界,如平台侧有调度记录可补核入表。
参考链接
- PR:https://atomgit.com/OpenHarmonyPCDeveloper/build_in_harmonyos/merge_requests/13869 (注:PR 正文为合入时口径——初版"排除 18/connectionDied 簇 11";最终口径以仓库 pr-body.md 双口径 9+2+6 为准,演进过程见 3.9-2)
- 配方仓库:https://atomgit.com/OpenHarmonyPCDeveloper/build_in_harmonyos
- 上游源(软件出处):KDE 官方 https://download.kde.org/stable/frameworks/5.116/kwayland-5.116.0.tar.xz / 国内可达镜像https://mirrors.bfsu.edu.cn/kde/Attic/frameworks/5.116/kwayland-5.116.0.tar.xz
- 鸿蒙 PC 社区:https://harmonypc.csdn.net/ ;项目申请:https://atomgit.com/OpenHarmonyPCDeveloper
更多推荐




所有评论(0)