ITPub博客

首页 > 应用开发 > C/C++ > 技术解读:鸿蒙PC移植libadwaita1.8.8:从编译到合入实录

技术解读:鸿蒙PC移植libadwaita1.8.8:从编译到合入实录

C/C++ 作者:MerlinNet 时间:2026-10-19 09:56:39 0 删除 编辑

鸿蒙PC移植libadwaita1.8.8:无显示测试实录

| 元信息 | 值 |
| --- | --- |
| 对象 | libadwaita 1.8.8(GNOME Adwaita UI 组件库,C 原生) |
| 环境 | HUAWEI MateBook Pro(HAD-W32)/ HarmonyOS 6.1.0 / aarch64( uname -m 实测)/ clang 15 / 社区适配版 Conan 2.29.1 |
| 仓库 | build_in_harmonyos(AtomGit:OpenHarmonyPCDeveloper/build_in_harmonyos) |
| PR | #13574 |

摘要

把 GNOME 的 Adwaita UI 组件库 libadwaita 1.8.8 移植到鸿蒙 PC(aarch64):1 个 portability 补丁、10 个适配问题、9 项依赖解析。上游 63/63 测试实跑 全部因无显示服务阻塞 (SIGTRAP,全量证据链入仓可复现);其中 2 项纯计算测试的 105/105 公共 API 断言 1:1 移植执行全部通过 ;消费者探针 124/124 全过 。PR #13574 已合入 main,沉淀 4 条知识草稿。

libadwaita 是纯 UI 组件库,它的核心 API(widget、样式、动画)全部预设了显示服务器的存在;而鸿蒙 PC 目前没有显示服务。让它编译通过只花了约 7 分钟——真正的难点在于: 在一个"没有显示器"的环境里建立分层测试证据链 ——上游 63 个测试哪些能执行、哪些断言可以移植、如何证明"能验证的全验证了、不能验证的写清楚了"。本文是这条证据链的完整实录,包括 2 处我自己走错路的误判。

诚实声明 :以下内容在本环境中 未验证 ,本文不写成"验证通过": ① 61 个创建 widget 的上游测试(无显示服务,SIGTRAP 阻塞); ② 137 条 nearest_from_rgba 断言(私有 API,库未导出该符号); ③ 一切视觉渲染效果(无显示设备)。 本文结论限定于"无显示 API 子集"与"可移植断言子集"两个边界之内。

一、背景:为什么值得做

libadwaita 是 GNOME 42+ 的默认 UI 组件库,整个 GTK4 应用生态的视觉底座——GNOME 45+ 桌面应用的控件、主题、动画系统直接构建在它之上。它在本仓库生态链中的位置是 承上启下的关键一环 :

  • 生态价值 :基础库,下游受益面大。libadwaita 就位后,GTK4 应用层(含 C++ 绑定 gtkmm)才有完整的视觉组件依赖;

  • 补链价值 :本仓库此前已适配其 C++ 绑定 gtkmm 4.0.0.1,"无显示 API 探针"方法论(知识条目 E471)正是在那次任务中诞生的;C 原生核心库缺位,等于 GNOME 桌面链断在中间;

  • 难度价值 :无显示服务这一平台缺口,迫使测试策略整体重设计——不是"跑上游测试集"这么简单,而是要回答"上游测试集里到底哪部分属于消费者可验证的范围"。

二、环境与差异前置

2.1 环境

| 项 | 值 |
| --- | --- |
| 设备 | HUAWEI MateBook Pro(HAD-W32) |
| 系统 | HarmonyOS 6.1.0 |
| 架构 | aarch64( uname -m 实测;conan profile arch=armv8 交叉印证) |
| 工具链 | clang 15(conan MesonToolchain,libc++) |
| 构建工具 | 社区适配版 Conan 2.29.1(OHOS 枚举/三元组已适配)+ build_in_harmonyos |
| 上游源 | https://download.gnome.org/sources/libadwaita/1.8/libadwaita-1.8.8.tar.xz |
| SHA-256 | 9e64939959a071cb660090c0e1b380c51ca29e3cfb57f3f23ea42a9922eb6f41 |
| 构建规模 | meson 535 目标,clean-room 全量约 7 分钟 |

2.2 差异点(先列全,逐个在第三节展开)

| # | 差异点 | 上游假设 | 鸿蒙 PC 实际情况 |
| --- | --- | --- | --- |
| 1 | 显示服务 | Wayland/X11 显示服务器存在 | 无显示服务;GTK4 仅 Broadway 后端可用,broadwayd 受签名约束不可运行 |
| 2 | GLib 版本 | 测试代码用 2.86+ API( g_log_get_always_fatal ) | 制品仓 glib/2.84.0.1.1,只有 2.80+ 的 setter |
| 3 | glib .pc 完整性 | glib-2.0.pc 自带 -lintl | PkgConfigDeps 生成只有 -lglib-2.0 ;1.8.x 新代码直调 g_libintl_* , --no-undefined 下必炸 |
| 4 | glib 二进制工具 | 系统工具可直接执行 | 裸 2.84.0.1 包的工具未签名(OHOS 上 “Operation not permitted”),须用 .1.1(已签名) |
| 5 | 依赖版本图 | appstream 钉 pango/1.50.14 + zstd/1.5.7.1 | 与 gtk4 链(pango/1.56.4 + zstd/1.5.7)不可共存的版本冲突 |
| 6 | 组件键引用 | 各配方组件键写法一致 | libxmlb 以无 ::gobject 后缀键引用 glib 组件 |
| 7 | appstream 获取 | subproject 源码 clone 可兜底 | 1.8.x 起为 required 依赖,离线 clone 必败 |
| 8 | 符号可见性 | 测试可用库的全部符号 | 测试链接内部静态库可用私有 API;消费者只能链接 .so 导出面 |
| 9 | 构建缓存 | 单用户干净缓存 | 同机多任务共享 conan 缓存,存在陈旧产物污染风险 |

三、适配过程

3.1 侦察(动手前需要检查的三项)

  • 依赖发布检查 (W0):对依赖链顶层逐个 conan download --only-recipe 探测制品仓——gtk4/4.22.4、glib/2.84.0.1.1、appstream/1.2.0 均已发布,依赖链不断裂,可开工;

  • 构建系统识别 :meson(GNOME 标准族),交叉注意点是它的 dependency() → pkg-config 解析链和 subproject fallback 行为;

  • 测试策略 :清点 tests/meson.build 的 test_names = 63 项 ,其中绝大多数创建 widget。策略定为" 全量构建 + 全量实跑 + 证据留档 + 纯计算子集断言移植 "——先把 63 个测试二进制全部编出来、全部跑一遍留证,再谈移植。

3.2 依赖树

libadwaita/1.8.8(meson,535 目标)
├── gtk4/4.22.4                主依赖(transitive_headers/libs;Broadway-only 后端)
│     └── 图内固定 pango/1.56.4、zstd/1.5.7、zlib/1.3.1.1、fontconfig/2.18.1.1
├── glib/2.84.0.1.1            shared + override(bin 工具已签名,OHOS 可执行)
├── appstream/1.2.0            1.8.x 新增 required(必须产出 .pc,阻断 subproject clone)
│     └── libxmlb/0.3.19       以无后缀键引用 glib 组件 → 键别名注入
├── pango/1.56.4               override(SONAME libpango-1.0.so.0 兼容 1.50.14 链接)
├── zstd/1.5.7                 override(SONAME libzstd.so.1 兼容 1.5.7.1 链接)
├── fribidi/1.0.16             文本双向算法
└── sassc/3.6.2                build-time(样式编译)

3.3 坑 1:pango/zstd 双版本冲突(对应差异 #5)

  • 现象 : conan create 直接报版本冲突——appstream/1.2.0 配方钉 pango/1.50.14 + zstd/1.5.7.1(compose/vips 链),gtk4/4.22.4 链钉 pango/1.56.4 + zstd/1.5.7(meson 要求 pango ≥ 1.56),两图不可共存。

  • 定位 :对比两份配方的 requires 钉住版本,确认冲突是"两个已发布配方各钉各的",不是本包写错。

  • 方案 :主配方 self.requires("pango/1.56.4", override=True) + zstd/1.5.7 override,统一图到 gtk4 链( 不降版本 ——libadwaita 1.8 的 meson 硬性要求 pango ≥ 1.56)。

  • 经验 :override 前必须先确认 预编译二进制的运行期符号 。appstream 预编译包链接的是 1.50.14/1.5.7.1,之所以能跑在 1.56.4/1.5.7 上,是因为 pango 1.x 保持 libpango-1.0.so.0 、zstd 1.x 保持 libzstd.so.1 的 SONAME。先查 SONAME 再 override,顺序反了就是埋雷。

3.4 坑 2:glib 组件键冲突(对应差异 #6)

  • 现象 :meson 依赖解析失败——appstream 的传递依赖 libxmlb/0.3.19 以无 ::gobject 后缀的键引用 glib 组件,而 conan 组件模型里只有带后缀的键。

  • 定位 :顺着报错找到 libxmlb 配方里 glib 无后缀键的引用点;这是"传递依赖的键写法和当前 conan 组件模型不一致",不是 libadwaita 的问题。

  • 方案 :主配方 generate() 注入 4 组键别名( glib::gobject → glib 、 glib::gmodule 、 glib::gthread 、 glib::gio ),test_package 同款注入(消费者侧同样要解析这个图)。

  • 经验 :传递依赖引用"另一种写法"的组件键时,修复点在 当前主配方的 generate() (图的所有权在这里),不要去改上游配方。

3.5 坑 3: g_libintl_* 链接失败(对应差异 #3)

  • 现象 :链接阶段 undefined reference to g_libintl_dngettext (meson 对库链接带 --no-undefined ,一个都藏不住)。

  • 定位 : nm -D 查 glib 包——libintl.so.8 里有 12 个 T(文本定义)的 g_libintl_* 符号;再查源码——1.8.x 新增的 adw-main.c / adw-tab-overview.c 直调 g_libintl_dngettext (1.2.0 没有这些直调,所以旧版本幸免)。根因是 PkgConfigDeps 生成的 glib-2.0.pc 的 Libs 只有 -lglib-2.0 ,缺 -lintl 。

  • 方案 : _normalize_glib_pc() ——在 PkgConfigDeps 之后强制回写 6 个 glib 系 .pc:prefix 统一指向 glib/2.84.0.1.1 包目录,glib-2.0.pc 的 Libs 补 -lintl 。glib 包自带 libintl.so.8,自洽闭环,不引入外部 libintl。

  • 经验 :PkgConfigDeps 产出的 .pc 是"按 conan 组件模型生成的",不一定等于上游 .pc 的完整 Libs。 先 nm 目标包的真实符号面,再决定 .pc 要补什么 ——这是本包所有 .pc 修改的统一方法论。

3.6 坑 4: g_log_get_always_fatal() 链接错误(对应差异 #2)

  • 现象 :两个测试二进制(test-flap / test-preferences-window)链接失败: g_log_get_always_fatal 未定义。

  • 定位 :该 getter 是 GLib 2.86+ 才有的符号;ohpcd 的 glib/2.84.0.1.1 只有 g_log_set_always_fatal (2.80+,稳定 ABI:setter 内部强制 |= G_LOG_LEVEL_ERROR 并 返回旧 mask )。

  • 方案 :唯一补丁 0001-tests-ignore-deprecations.h.patch (portability,+5/-1):测试辅助头里改用 setter 并捕获返回值作为"旧基线",与上游"抑制 ERROR-fatal 再恢复"语义等价。 0 处平台宏 (不是 #ifdef __OHOS__ 分支,是版本兼容)。

  • 经验 :API 版本差 只影响测试代码 时,用 portability 补丁修测试文件,不动主库——补丁面最小、语义可对照、评审可核。

3.7 坑 5:GdkRGBA 成员名(20 个编译错误)

  • 现象 :消费者测试一次报 20 个编译错误—— GdkRGBA 没有 .r/.g/.b/.a 成员。

  • 定位 :读 gtk4 头文件的结构体定义:成员名是 .red/.green/.blue/.alpha 。

  • 方案 :改成员名(纯笔误级,20 个错误同源)。

  • 经验 :GTK4 相对 GTK3 的类型命名变化点(成员名、API 更名)要在 写消费者测试前 翻一次头文件确认,别按 GTK3 的肌肉记忆写。

3.8 坑 6:easing 端点断言失败(容差对齐,不是放宽)

  • 现象 :探针 B 段(easing 端点检查)2 条断言失败: ease_in_elastic(0) 与 0 偏差 -4.88e-4, ease_in_out_elastic(0) 偏差 8.48e-5。

  • 定位 :把上游 adw-easing.c 的 elastic 族公式代 t=0 手算——该族公式 没有 t==0 特判 ,端点值数学上就不是精确 0。再翻上游自己的 tests/test-easing.c:断言写法是 g_assert_cmpfloat_with_epsilon(..., 0, 0.005) —— 上游自己的容差就是 0.005 。

  • 方案 :探针端点容差对齐上游契约 0.005。强调一下:这不是"把测试改松",是"和上游用同一把尺子"——如果库的端点偏差超过 0.005,上游自己的测试同样会挂。

  • 经验 :测试失败先查 上游自己的断言容差/契约 ,"与上游同源"比"自己放宽"在评审上站得住得多。

3.9 坑 7:探针 SIGSEGV——误判自曝①

  • 现象 :探针 D 段(spring 物理参数)崩溃:SIGSEGV,fault @0x1,崩溃点在 g_object_run_dispose 内部。

  • 误判(自曝) :第一反应是 怀疑 gobject 链或平台内存子系统有问题 ——毕竟"释放一个对象就崩"很像环境损坏。这个方向查了半天没有新证据。

  • 纠正 :停手,做 对照组实验 ——D0/D1 用真正的 GObject( g_object_new 创建、 g_object_unref 释放)走完全相同的释放路径:全部正常。对照成立,说明 glib/gobject 链本身健康,问题在 测试代码自己 : AdwSpringParams 是 GBoxed ( G_DEFINE_BOXED_TYPE + gatomicrefcount),不是 GObject;对它调用 g_object_unref() 是未定义行为——gobject 把这段内存当 GObject 解读,内部字段被当弱引用 datalist 指针解引用,fault @0x1 这种极低地址正是"数据位模式被误读成指针"的典型签名。

  • 方案 :改用 adw_spring_params_unref(sp) 。D 段 5 条断言(damping_ratio/mass/stiffness/派生量 damping == 2ζ√(mk) == 20.0)全过。

  • 经验 :① 写释放代码前先确认类型是 GObject / GBoxed / 裸 struct,三者 unref 路径完全不同;② “库坏了"还是"我写错了”,对照组实验是最快的裁决方式 ——比继续读库源码便宜一个数量级。

  • 入档 :本案已入档知识草稿 E429(错误签名 SIGSEGV|g_object_run_dispose|fault @0x|GBoxed ,含对照组结论),条目见第五节。

3.10 坑 8:.so 行为偏离源码——误判自曝②

  • 现象 :探针实测的 easing 值与源码公式手算对不上;但源码 md5 一致、.so 反汇编与公式一致——“代码对、结果错”。

  • 误判(自曝) :先怀疑 源码被改过 (查 md5)、怀疑 编译产物异常 (反汇编逐条对公式)——都排除了,一度陷进"平台浮点行为差异"的假设里打转。

  • 纠正 :换个问题问自己——“ 运行的到底是不是我刚编出来的 .so ”。一查 conan 缓存:这台机器同机多任务共享缓存,09-14 的旧会话残留了 libadwaita/1.8.8 的旧缓存条目(旧 recipe revision / 旧源码),conan 目录名哈希碰撞导致 陈旧 build/package 目录被复用 ,探针加载的是陈旧 .so。

  • 方案 : conan remove "libadwaita/*" -c 全清 + clean-room 重建,实测值与公式完全一致。

  • 经验 :① 共享缓存机器上,正式构建前先清本包旧条目;② “ 反汇编一致但运行不一致 "这个签名,第一嫌疑永远是" 实际加载的产物来源 ”,而不是浮点/平台行为。

  • 入档 :本案已入档知识草稿 E430;其中一条工具层细节特别值得注意—— conan 不会重新校验已存在 source 目录的 sha256(只校验下载时刻) ——这正是陈旧目录能被长期复用的工具层根因。

3.11 坑 9:appstream subproject 全源码 clone(对应差异 #7)

  • 现象 :meson setup 阶段开始 clone appstream 的 全部源码 (离线环境必败,且拖死构建)。

  • 定位 :1.8.x 起 appstream 从可选变成 required 依赖 (meson 里无 required:false ,且带 subproject fallback)——PkgConfigDeps 没产出 appstream.pc,meson 的 dependency("appstream") 就落到 fallback 分支去 clone。

  • 方案 :appstream/1.2.0 制品仓已发布,PkgConfigDeps 产出 appstream.pc 后 dependency() 直接命中,fallback 被阻断。

  • 经验 :meson 的 subproject fallback 是个"静默陷阱"——它不报错,只是开始下载。GNOME 系配方里凡看到构建阶段出现 git clone,先查哪个 dependency() 没被 .pc 满足。

3.12 坑 10: gtk_doc 选项 deprecated

  • 现象 :meson 配置报 gtk_doc 选项错误。

  • 定位 :1.8 起该选项 deprecated,查 meson_options.txt 确认替代项。

  • 方案 : -Ddocumentation=false (本任务不产出文档)。

  • 经验 :GNOME 系构建选项随大版本轮换,选项报错先翻 meson_options.txt ,不要凭旧版本记忆传参。

3.13 构建结果

10 个坑清完后, conan create 全流程:

conan create archives/l/libadwaita/1.8.8/conanfile.py
--build
=
missing
  → meson
535
目标(含全部
63
个上游测试二进制)0 FAILED,约
7
分钟(clean-room)
  → test_package: libadwaita-1.8.8 no-display probe:
checks
=
124
PASS
(
105
/105 upstream-ported assertions included
)
→ 产物:libadwaita-1.so、libadwaita-1-internal.a、85 个头文件(OHOS 签名)
在这里插入图片描述
图1 :真机 conan create 完整构建 + 消费者测试全过(证明:配方在真机环境可构建、124/124 全过;不能证明:下游可消费性、视觉渲染)

四、验证(分层,禁止相加)

口径声明:下面 5 层 分别统计、分别陈述 ,不做任何相加或合并。“63 实跑”、“105 移植”、"124 消费者"是三个不同口径的数字,混在一起说就是造假。

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

  • 清点 : tests/meson.build 的 test_names 列表 = 63 项 ,全部构建成功(535 目标 0 FAILED);

  • 实跑 : 63/63 执行(env: GSETTINGS_BACKEND=memory GTK_A11Y=none ,每测试 10s 超时);

  • 通过 : 0 ;

  • 失败模式(63/63 同因,非断言失败) :

gtk_test_init → gtk_init →
Gtk-WARNING: Failed to open display →
g_log fatal → __builtin_trap() → SIGTRAP(rc=-5,注意不是 exit 1)
  • 分类 :63 项中 61 项创建 widget (display 依赖,无显示服务下无法执行); 2 项纯计算 (test-easing / test-accent-color,见 4.2/4.3);

  • 次要环境问题 (非致命,如实记录): Fontconfig error: Cannot load default config file: File not found ——fontconfig/2.18.1.1 包缺默认 fonts.conf,显示链路可用前需处理;

  • 证据(已入仓,可复现) : archives/l/libadwaita/1.8.8/upstream-tests/ ——summary.txt(63 行,一行一测试:rc + 首行关键输出)+ 63 份 .out 全量原始输出 + run-upstream-tests.py 复现 runner + README.md。runner 对任意含 build-release/tests/ 的构建目录一条命令重跑;找不到测试二进制时报错退出(exit 1), 不会静默空跑 。

4.2 断言 1:1 移植执行(可移植子集 = 公共 API 断言)

63 项里只有 2 项是纯计算测试(无 widget、无显示依赖),它们的 公共 API 断言 已逐条移植到消费者测试(test_package/test.c)中无显示执行:

| 上游测试 | 断言构成 | 移植执行结果 |
| --- | --- | --- |
| test-easing | 35 个 AdwEasing 值 × f(0)≈0 / f(1)≈1,共 70 条 | 70/70 PASS |
| test-accent-color | to_rgba 精确 hex 9 + to_standalone_rgba 18 + rgba_to_standalone 8,共 35 条 | 35/35 PASS |
| 合计 | 105 条公共 API 断言 | 105/105(100%)PASS |

移植是 断言级 1:1 (数值、容差、判定方向逐条对照上游源文件,test.c 内每条断言注释了上游行号),例如 easing 端点:

ok
&=
check
(
fabs
(
adw_easing_ease
(
e
,
0.0
)
)
<=
0.005
,
"easing f(0)~=0 (eps 0.005)"
)
;

口径边界(这里比较重要) :这是"上游测试的公共 API 断言 "在无显示环境被执行, 不是"上游测试二进制"被执行 (那些二进制因 gtk_test_init 前置 display 初始化而无法启动)。两个口径在本文和 PR 里始终分开陈述。

4.3 私有 API 137 条:分类留证,不冒充移植

上游 test-accent-color.c 另有 137 条断言 (nearest_from_rgba roundtrip 10 + 各桌面调色板 127)调用 adw_accent_color_nearest_from_rgba() 。这个函数:

  • 仅声明于私有头 src/adw-accent-color-private.h ,公共头 adwaita/ 目录无此声明;

  • 未从 libadwaita-1.so 导出 —— nm -D --defined-only libadwaita-1.so.0 | grep nearest 输出为空;同一编译单元里的兄弟函数(to_rgba / to_standalone_rgba / rgba_to_standalone)均正常导出;

  • 上游测试二进制之所以能用它:meson 把 tests 链接到 内部静态库 libadwaita-1-internal.a(全部符号、无可见性过滤)。

因此这 137 条 消费者上下文不可执行 (消费者只能链接已安装的 .so)。处理:不入移植数字、不做"通过"声明,按"私有 API 子集"分类留证(函数名 + 私有头路径 + nm -D 证据 + 上游可用的原因),随 63/63 台账一并入仓。 为什么不让测试去链接内部 .a :那不是真实消费场景,且内部 .a 的符号面不代表公共接口——那样做会让"测试通过"的声明失真。

4.4 消费者测试(124 checks)

test_package/test.c 共 124 项 checks ,ok 累积决定进程退出码(任一项失败即非零退出,CI 可拦截):

| 段 | 内容 | 数量 | 级别 |
| --- | --- | --- | --- |
| A | 版本/初始化态:adw_get_major/minor/micro_version、版本宏、ADW_CHECK_VERSION 正/反、adw_is_initialized()==FALSE | 7 | L1 |
| B | easing:上游 1:1 移植 70 + 解析值/单调性扩展 7 | 77 | L2 |
| C | accent-color:上游 1:1 移植(公共 API)35 | 35 | L2 |
| D | spring 物理参数:new + damping_ratio/mass/stiffness + 派生量 damping == 2ζ√(mk) == 20.0(GBoxed,adw_spring_params_unref 释放) | 5 | L2 |
libadwaita-1.8.8 no-display probe: checks=124 PASS (105/105 upstream-ported assertions included)
L1/L2 声明 :A 段(版本断言)属 L1 级别, 单独不能宣称功能完成 ;实质功能验证由 B/C/D 段承担(真实 API + 数值结果断言,L2)。D 段的 2ζ√(mk) 是 spring 模型的物理定义式,属于"结果断言"而非"能跑就算"。
在这里插入图片描述
图2 :消费者测试二进制直接运行(证明:交付物可独立执行且 124/124 全过;不能证明:功能面完整、视觉渲染)

4.5 产物核验

| 项 | 结果 |
| --- | --- |
| 构建目标 | meson 535 目标 0 FAILED(含 63 个上游测试二进制) |
| 库产物 | libadwaita-1.so(动态)、libadwaita-1-internal.a(内部静态,供上游测试链接) |
| 头文件 | 85 个(公共 API 面) |
| 符号面 | nm -D 核验:公共 API 符号齐全;私有 API(如 nearest_from_rgba)不导出(4.3 的实证基础) |
| 签名 | OHOS 共享库签名(配方 _sign_shared_libs ) |
| 构建耗时 | clean-room 全量约 7 分钟;warm 缓存(包命中、仅重编测试)约 2 分钟 |

4.6 CI 与评审(完整轮次记录,以及失败轮)

| 轮次 | 构建号 | head | 结果 | 说明 |
| --- | --- | --- | --- | --- |
| 1 | #63822 | 3bdd8cc9 | ✅ 通过 | conan-build-test / L0 / L1 全绿 |
| 2 | #63893 | 4b021501c | ⚠️ 作废 | worker CI-021 OOM: zsh:1: fork failed: out of memory ,连 git --version 都 fork 不出——基础设施故障,与代码无关,重触发 |
| 3 | #63909 | aed5e885 | ✅ 通过 | 重触发后全绿(换 worker) |
| 4 | #66403 | 85273d54 | ✅ 通过 | 测试强化提交(124 checks + 证据链入仓)后全绿 |

检视记录 :平台 AI 检视曾多次基础设施故障(“审查未能完成”);另做了一轮本地 deep review(correctness/security/performance/tests_contracts 四维度),9 条发现(P2×3 + P3×6)全部处置——runner 参数化 + 空跑保护(exit 1)、README 复现命令修正、test.c include 显式化;检视报告已发 PR 讨论区。

合并 :PR #13574 经评审(Clancy_Xie)批准合入 main;关联 Issue #3562(【高难度挑战】候选申请)自动关闭。

在这里插入图片描述
图3 :PR #13574 已合并页

4.7 复现速查

以下 3 条命令可在同环境复现本文全部核心数字(命令 1、2 于本文发布前一日复核可运行;实跑 env 由 runner 内部固定为实测值):

cd
<
仓库根目录 build_in_harmonyos
>
conan create archives/l/libadwaita/1.8.8
--build
=
missing
python3 archives/l/libadwaita/1.8.8/upstream-tests/run-upstream-tests.py
<
BUILD_DIR
>
nm
-D
--defined-only
<
已安装 libadwaita 包目录
>
/lib/libadwaita-1.so.0
|
grep
nearest
口径对应:命令 1 → 3.13/4.4(124),命令 2 → 4.1(63/63),命令 3 → 4.3(137 分类)。读者可尝试复现(emm。。

五、知识沉淀(4 条草稿,随 PR 入仓)

| 编号 | 知识 | 来源坑位 | 复用价值 |
| --- | --- | --- | --- |
| E429 | 对 GBoxed 调用 g_object_unref 致 SIGSEGV——类型混淆易被误判为 gobject 链损坏;对照组实验是裁决手段 | 坑 7 | 所有 GLib 系 C 库的探针编写(GObject/GBoxed 判别) |
| E430 | 共享 conan 缓存陈旧 .so 污染——"反汇编一致但运行不一致"先查实际加载产物来源;clean-room 重建定案 | 坑 8 | 多任务并行共享缓存的所有构建环境 |
| E431 | PkgConfigDeps 生成的 glib-2.0.pc 缺 -lintl 致 g_libintl_* 链接失败(–no-undefined);先 nm 目标包真实符号面再补 .pc | 坑 3 | 所有依赖 glib 且代码直调 intl 函数的包 |
| E432 | 上游测试链接内部静态库可用私有符号——消费者移植测试断言前须先 nm -D 过滤已导出符号(公私二分如实申报) | 4.3 节 | 所有"上游测试可用、公共 API 不可用"符号的移植场景(GNOME/GTK 系普遍存在 private 头) |

草稿状态说明:4 条均为 .kv + .md 配套草稿,随 PR 入仓、待主编 promote 为正式条目(走"草稿→审核→正式"知识生命周期);编号 E429-E432 与正式库无冲突(graph validate 通过)。

六、FAQ

Q1:上游 63 个测试 0 通过,合理吗? 合理,且已证明是 平台阻塞而非断言失败 :63/63 同因 SIGTRAP(gtk_init “Failed to open display”),rc=-5(trap 信号),不是 exit 1(断言失败退出码)。全量证据链已入仓(summary.txt + 63 份 .out + 复现 runner),任何人可在同类环境一条命令复跑。"0/0 没跑"和"63/63 全跑了但被平台卡死"是两回事——本文是后者。

Q2:105/105 移植断言,算"上游测试通过"吗? 不算,本文从不这样声称。准确口径:上游 2 项纯计算测试的 公共 API 断言子集 在消费者上下文 1:1 执行通过。上游测试二进制本身因 display 前置初始化无法启动(见 4.1)。两个口径全文分开陈述,PR 与 Issue 同口径。

Q3:137 条私有 API 断言为什么不"想办法"跑一下? 技术上可以(链接内部 .a),但那是 伪消费场景 :内部 .a 的符号面不代表公共接口,消费者拿不到它。跑了也只能证明"内部实现如此",不能证明"库对消费者可用"。如实分类留证(4.3)。

Q4:124 项消费者 checks 是不是空壳测试(print 一下就算过)? 不是。判定标准:B/C/D 段是 真实 API 调用 + 数值结果断言 (easing 数学值、颜色 hex 精确值、spring 物理定义式 2ζ√(mk)=20.0),任何一条数值不对进程即非零退出(ok 累积,CI 可拦截)——这是 L2 级验证。A 段版本断言是 L1 级,已单独声明"不能宣称功能完成"。test.c 全文在 PR 中可逐行复核。

Q5:libadwaita 是 UI 库,核心不是渲染吗?没验证渲染算适配完成吗? 不算渲染完成,本文也这么写 (诚实声明)。当前可用面 = 无显示 API 子集(版本/easing 数学/颜色换算/spring 物理参数,已验证 124/124);widget 渲染属显示链路可用后的下一阶段(届时上游 63 测试全集 + 视觉走查覆盖)。配方层面(依赖解析/构建/产物/签名/消费者链接)已完整,这是"库已就位、渲染待显示服务"的明确分界,而非"验证通过"的模糊声明。

七、总结

7.1 量化盘点

| 维度 | 数字 |
| --- | --- |
| 补丁 | 1 个(portability,+5/-1,0 处平台宏) |
| 适配问题 | 10 个(含 2 处误判纠正:对照组排除法 / 缓存污染 clean-room) |
| 依赖解析 | 9 项(3 组 override + 组件键别名 + .pc 回写 + subproject 阻断) |
| 构建 | meson 535 目标 0 FAILED,约 7 分钟(clean-room) |
| 上游测试 | 63 清点 / 63 实跑 / 0 通过(63/63 display 阻塞 SIGTRAP,证据链入仓) |
| 断言移植 | 105/105 公共 API 断言 1:1 移植执行全过(70 + 35) |
| 私有 API | 137 条分类留证(nm -D 实证,不冒充移植) |
| 消费者测试 | 124/124 checks(L1×7 + L2×117) |
| 知识沉淀 | 4 条草稿(E429-E432) |
| CI | 4 轮(3 绿 + 1 基础设施故障作废);PR 已合入 main |

7.2 可复用方法(4 条)

  • display 依赖测试的三级分类 + nm -D 第一闸 :上游测试先分 display 依赖 / 纯计算-公共 API / 纯计算-私有 API 三级;移植前 nm -D 过滤导出符号,公私二分如实申报(本文 63 = 61 + 70/105 口径 + 137 留证);

  • "与上游契约对齐"的容差原则 :测试失败先翻上游自己的断言容差(本文 eps 0.005 同源),"同源"比"放宽"可辩护;

  • 对照组裁决法 :崩溃/异常先建最小对照组(真 GObject vs 疑点类型、新构建 vs 缓存产物),再决定查库还是查自己——两处误判都是靠它止损的;

  • R51 证据链入仓 :测试台账 = 清点 + 实跑 + 逐项原始输出 + 可复现 runner,全部进仓库——评审看到的不是"我说 63/63",是"63 份 .out 在仓里"。

7.3 流程 checklist(无显示 UI 库适配通用)

  • 依赖发布检查(conan download 逐个探测)再动手

  • 清点上游测试清单,预判 display 依赖占比,定"实跑 + 证据 + 移植"策略

  • pc 修改前先 nm 目标包真实符号面

  • 消费者测试分 L1/L2 级,L1 段带"不能宣称功能完成"声明

  • 上游证据链(.out 全量 + runner)入仓

参考链接

  • PR(已合并):https://atomgit.com/OpenHarmonyPCDeveloper/build_in_harmonyos/merge_requests/13574

  • Issue(已关闭):https://atomgit.com/OpenHarmonyPCDeveloper/build_in_harmonyos/issues/3562

  • 仓库:https://atomgit.com/OpenHarmonyPCDeveloper/build_in_harmonyos

  • 上游源码:https://mirror.nju.edu.cn/gnome/sources/libadwaita/1.8/libadwaita-1.8.8.tar.xz


转载声明:本文为转载整理,原文版权归原作者及原站点所有。

原文来源:CSDN HarmonyOS

原创地址:https://blog.csdn.net/2401_87935351/article/details/167040231

原文作者:2401_87935351;如有侵权,请联系管理员删除。

上一篇: 没有了~
下一篇: 没有了~
请登录后发表评论 登录
全部评论

注册时间:2020-12-12

  • 博文量
    1
  • 访问量
    194