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

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

1. 环境

项值测量方式
操作系统HarmonyOS 6.1.0系统设置 → 关于本机
设备HUAWEI MateBook Pro(HAD-W32)/proc/cpuinfo(Hardware: HUAWEI KirinX90)
CPU 架构aarch64(8 核)uname -m / /proc/cpuinfo
内核HongMeng Kernel 1.12.0uname -a
编译器clang / clang++ 15.0.4(OHOS SDK 26.0.0.18)conan profile show + 构建日志 “The CXX compiler identification is Clang 15.0.4”
语言标准C++17(libc++)conan profile show
Conan2.29.1(OHOS 社区适配版,支持 os=OHOS 枚举)conan --version
Conan profileohos-aarch64(os=OHOS, os.version=6.0, arch=armv8, build_type=Release)conan profile list
构建规模19 个补丁;libInventor.so 5.5 MB(5,814,720 字节);全量全新构建约 20–30 分钟;消费者测试 14 断言2026-11-08 (ls / readelf / 日志)

任务:把 inventor 2.1.6.1(SGI Open Inventor,aumuell 官方维护 fork,CMake 构建,LGPL-2.1)——一个 30 年历史的 3D 场景图库——移植到鸿蒙 PC,鸿蒙 PC 无 X 显示服务器,整个 GL 渲染栈必须重定向到 OSMesa(Mesa 软件光栅化)做无头渲染;19 个源码补丁、自补 14 断言消费者测试 14/14 全绿,PR !17377 于 2026-10-20 创建、2026-10-23 合入主仓、2026-10-31 制品发布。

2. 适配过程

2.1 高难度挑战

命中以下特征之一,本项目命中两条:

  1. 强绑定私有系统接口、无跨平台抽象层:Open Inventor 2.1(1996 年)早于现代跨平台图形抽象——渲染层直接绑定桌面 GL + GLX(X11 窗口系统),GUI 绑定 libSoXt 绑定 Motif/Xt。鸿蒙 PC 没有 X 显示服务器,整个渲染入口(GLX)直接失效。
  2. 依赖链庞大、测试成本极高:6 个 host 依赖 + 1 个 tool 依赖(见 2.2),X11 头文件拆两个包、mesa 必须选特定的 OSMesa-only 变体与预编译 GLU 精确匹配;且上游零自动化测试(清点证据见 3.1),消费者测试要从零编写并覆盖真实渲染链路(建 OSMesa 上下文、渲染、读回像素),工作量比"跑上游测试套件看通过率"高一个量级。

同时说明:Open Inventor 有自己的跨平台框架(SoXt/SoWin 层),但它的"跨平台"是 1990 年代 Windows/X11 双平台的跨平台,没有为"无显示服务器"环境留任何抽象——这正是它无法按普通配置走通、必须做渲染后端级替换的原因。

2.2 依赖预检(W0)与依赖树

动手前第一步(在看源码之前):逐个 conan download <pkg> -r=ohpcd --only-recipe 核验 7 个依赖,全部已发布到制品仓,依赖链不断裂,可开工:

inventor/2.1.6.1(CMake)
├── mesa/24.3.4.1          (OSMesa-only 变体,整个 GL 栈的软件光栅核心)
├── glu/9.0.3              (预编译,以 gl_provider=osmesa 针对上面这个 mesa 构建)
├── libx11/6.4.0           (核心少数文件 include <X11/Xlib.h>,不用于渲染)
├── xorgproto/2025.1       (libx11 传递依赖,X11/X.h 协议头)
├── libjpeg/10             (图像解码,静态折入 libInventor)
├── libiconv/1.17.1        (字符集转换,静态折入 libInventor)
├── freetype/2.14.3.2.1    (FLI/FLC 动画解码器的字体渲染)
└── bison/3.8.2            (tool 依赖,源码预处理工具)

关键决策:选 mesa/24.3.4.1(OSMesa-only 变体)——制品仓预编译 glu/9.0.3 正是以 gl_provider=osmesa 针对这个精确变体构建的,只有精确匹配才能让"GL + GLU + 渲染"整栈无头可用。

复验:2026-11-08 对 7 个依赖逐项重跑,7/7 可消费(4.7 复现速查命令③即此核验)。

2.3 适配主线(4 提交链 + 时间线)

时间事件
10-18立项:路径 C 论证、W0 依赖预检(7/7 已发布)
10-19 18:31配方首稿(conanfile.py v0)
10-19 ~ 10-20坑 1–5 清除(GL 头 / X11 头 / register / PRIiPTR / Freetype 大小写,见 2.4)
10-20 14:46消费者测试首版跑绿(初版 4 子节点,尚未含渲染段)
10-20 15:40PR !17377 创建(初版提交 ee8f905c)
10-20 ~ 10-21坑 6–7 清除(引用计数 UAF 崩溃 / GL_EMISSION 0x500),14 断言定稿
10-21 17:37首次 /rebuild → CI 第 1 轮 #99185 ❌(fork EAGAIN)
10-21 21:29日志诊断 → v1 加固提交 693df375
10-22v2 提交 7cd5b44b;16:55 /rebuild → CI 第 2 轮 #100395 ❌(同类失败)
10-22 夜v3 提交 c89e5dd4(补 CMakeConfigureLog.yaml 证据,见 2.5)
10-22 21:21/rebuild → CI 第 3 轮 #101248
10-23 05:48CI 第 3 轮 ✅ 全绿
10-23 21:07Clancy_Xie squash 合入(base main@c29e1da5,head c89e5dd4)
10-31 21:51/release → REL #15801 ✅,制品 inventor/2.1.6.1@ohpcd/stable 发布

初版配方(ee8f905c)做了五件事:① 选项门控——INVENTOR_SOXT/APPS/DEMOS/NODES/MAN=OFF,包只做库(libInventor.so + 公开头文件,纯库包无需 commands.json);② OSMesa 重定向——OPENGL_gl_LIBRARY 指向 libOSMesa.so(导出完整 OpenGL API);③ 19 个源码补丁(编号与说明以 conandata.yml 为准,全部无平台宏);④ 14 断言消费者测试;⑤ 构建韧性(v1 起加入,见 2.5)。

为什么不适配 libSoXt(范围决策):libSoXt 绑定 libx11/1.8.13 X11 族(libxt/Motif),与核心 + mesa 使用的 libx11/6.4.0 族二进制不兼容,且在无头鸿蒙 PC 上运行时本来也无用武之地——适配成本(引入第二套 X11 族)与收益(无头零使用)不成立。

2.4 逐坑四段式(现象 → 误判/自查 → 根因 → 修复)

坑 1:GL 头文件供应链(构建树里绕得最久的一个)

  • 现象 A(configure 阶段):CMake 报找不到 GL 头;排查发现 #include <GL/gl.h> 被解析成了 <OPENGL_INCLUDE_DIR>/GL/GL/gl.h——路径里多出一层 GL/ 前缀。
  • 误判/自查:第一反应怀疑 mesa 包头文件不全,去 conan 缓存里翻文件,翻了一阵没翻出来。后来读 CMake FindOpenGL 模块的实现才定位:OPENGL_INCLUDE_DIR 被 CMake 当作父目录,它自己会在后面拼 GL/gl.h;而我把这个变量直接指到了 GL 子目录,于是又拼了一层。
  • 根因:find_package(OpenGL) 要求用单个 OPENGL_INCLUDE_DIR 同时覆盖 OpenGL::GL 与 OpenGL::GLU,但 OHOS 生态里 GL 头散在两个包——mesa 出 gl.h/glext.h/osmesa.h,glu.h 在 glu 包里,一个目录装不下。
  • 修复(两步):① generate() 里把 mesa 与 glu 的 GL 头合并到一个目录(generators/gl-include/GL),OPENGL_INCLUDE_DIR 指它的父目录;② 同目录写入一个最小 GLX 兼容头(glx.h)——OSMesa-only 的 mesa 不提供 <GL/glx.h>,但 inventor 的公开头(SoOffscreenRenderer.h 等)引用它。GLX 路径在无头环境全是死代码,shim 用 no-op 桩实现(返回 NULL/0/False),恰好把 inventor 导向其既有的 NULL 错误处理路径(0 平台宏,符合 R8)。
  • 现象 B(消费者测试编译阶段):fatal error: 'GL/glu.h' file not found——公开头 include <GL/glu.h>,而预编译 glu/9.0.3 包的 package_info 未声明任何 include 目录(CMakeDeps 生成的 glu_INCLUDE_DIRS 为空),消费者永远解析不到。
  • 修复:package() 把 glu 包的 GLU 头拷入本包 include/GL/ 随包发布——GL 头供给在 inventor 包内自包含(与 GLX shim 同模式)。
  • 经验:CMake find 模块报"找不到头",先读模块源码确认找到的路径被怎么用(父目录 vs 目录本身),再查包布局;预编译包 package_info 漏声明 include 目录是常见暗坑,"随包自包含"是最稳的解法。

坑 2:X11 头拆包

  • 现象:find_package(X11) 不满足——CMake 内置 FindX11 模块假设 X11 头在单一 include 目录,而制品仓里 X11/X.h 在 xorgproto 包、X11/Xlib.h 在 libx11 包,两包拆分,谁单独都不完整。
  • 根因:上游 CMake 假设完整的 X11 开发树(Linux 发行版布局),而 Conan 制品生态里 X11 头按协议层(xorgproto)/实现层(libx11)拆包。
  • 修复:0003 补丁新增 cmake/FindX11.cmake,经 CMAKE_MODULE_PATH 遮蔽 CMake 同名内置模块,分别搜索两个包并暴露单一 X11::X11 目标覆盖两个 include 目录。注意:配方 generate() 里不能预置 X11_X11_* 变量——内置模块的 find_path 在变量已预置时会跳过搜索,而 libx11 的 include 目录里没有 X.h,预置反而坏掉(这条注释就写在 conanfile.py 里)。
  • 经验:制品仓包布局与发行版布局不一致时,"新增 Find 模块做遮蔽"比逐个 -D 传变量稳定——后者的变量语义跟 CMake 内置搜索逻辑强耦合,很脆。

坑 3:register 存储类(C++17)

  • 现象:首次全量编译报出大量错误,清一色 register 相关——C++17 移除了 register 关键字,clang 15 + cppstd=17 下是硬错误。
  • 根因:Open Inventor 2.1 是 1996 年的代码,register 存储类贯穿使用。
  • 修复:批量清理——12 个文件共 134 处(0004–0006、0008、0011–0018,从 SoInput/SoOutput 到 NURBS 细分库),纯机械删除、无语义变化。口径说明:PR 正文⑥与 notes.md 写"14 处(patch 0002-0015)",为陈旧口径(编号与处数均与实文件不符);本文以补丁实文件计数为准(对补丁删除行按词边界统计 = 134 处)。
  • 经验:这类规模化机械删除不要手改文件——脚本生成补丁,删完全树 grep 验证零残留(本文 4.7 复现①可整体重放)。

坑 4:PRIiPTR 宏拼接

  • 现象:SoText2.c++/SoText3.c++ 编译错误——字符串字面量 "%"PRIiPTR"," 在 C++11+ 里被解析成用户定义字面量(字符串字面量后紧跟标识符是保留语法),直接编译失败。
  • 根因:<inttypes.h> 的 PRI* 宏不能紧跟字符串字面量使用。
  • 修复:0009/0010 两个补丁,各 1 处,加空格隔开:"%" PRIiPTR ","。
  • 经验:这类"字符串 + 宏"拼接错误只在 C++11+ 暴露(C99 时代能编译),老代码升标准时的典型坑族,可用 grep '"%[A-Z]' 全树扫。

坑 5:Freetype 大小写

  • 现象:find_package(Freetype) 找不到 Conan 包——CMake 内置模块名是 Freetype(大写 F),而 Conan 包 slug 是 freetype(小写),CMakeDeps 生成的 config 文件是 freetype-config.cmake,大小写对不上,find_package 落空。
  • 修复:0019 补丁把上游 CMakeLists 的 find_package(freetype) 与 freetype::freetype 目标名小写化,对齐 Conan 包命名(即 find_package(freetype) + freetype::freetype)。
  • 经验:消费制品仓现成包时,"包 slug ↔ find_package 名 ↔ 目标名"三者大小写一致性要单独核一遍——CMake find 模块惯用名(Freetype/JPEG/X11…)与 Conan slug 经常大小写不同。

坑 6:SGI 2.1 引用计数约定——消费者测试首崩(UAF)

  • 现象:消费者测试加上渲染段后崩溃(SIGSEGV),落点在 SoCallbackAction::getTextureWrapS——栈在渲染 action 内部很深的位置,第一眼很像"库的渲染路径崩了"。
  • 误判(自曝)/纠正:最初怀疑库的渲染 bug,花时间读 SoCallbackAction 源码找问题,没有收获。改用对照组实验:去掉渲染段跑 → 不崩;加回渲染段 → 必崩,于是把方向从"库坏了"切到"我的用法"。
  • 根因:SGI 2.1 的引用计数约定——new 出来的节点 refCount 是 0(不是 1);SoAction::apply() 会先对节点 ref、cleanUp() 再 unref。对0 计数的根节点,apply 做 0→1、cleanUp 做 1→0——apply 结束时根节点被销毁,下一个 action 就是 use-after-free。SoAction.c++ 455–459 行有一段 DEBUG 警告正是针对这个约定。
  • 修复:消费者在构造场景后显式 root->ref()(与 SGI 官方示例的用法一致),重读文件后的场景同理;test.cpp 里加注释说明该约定,防止后踩。修复后崩溃消失,包产物手动连跑 5 次全部 exit 0(崩溃回归稳定性确认)。
  • 经验:"库崩了"的怀疑与证据打架时,对照组实验(摘掉一段重跑)比继续读库源码便宜一个数量级;C++ 库深栈崩溃第一要查内存所有权约定,而不是崩溃点当前行。

坑 7:GL_EMISSION 0x500——渲染阶段唯一的"硬拒绝"

  • 现象:崩溃修复后渲染跑起来了,但 glGetError() 报 GL_INVALID_OPERATION(0x500)——调用点是 SoGLLazyElement 的 glMaterialfv(GL_EMISSION)。蹊跷的是:同一个 switch 里其他材质参数(AMBIENT/DIFFUSE/SPECULAR/SHININESS)走同一个 glMaterialfv 都成功,唯独 EMISSION 被拒。
  • 根因(后端限制,非适配缺陷):mesa/24.3.4.1 OSMesa/softpipe 的固定功能管线不支持写入 GL_EMISSION,在 API 层直接拒绝。AI 检视也追问过"为什么同一 glMaterialfv 只拒 EMISSION,根因是否真是后端"——裁决证据:同 switch 其余 4 个参数走同一个 glMaterialfv 写入全部成功(排除调用代码问题),结合实测 0x500,"OSMesa/softpipe 固定管线后端硬限制、非适配缺陷"的判定成立。
  • 修复:0007 补丁在源码层跳过该 GL 写入(无平台宏),保留懒状态缓存的记账(数值继续被跟踪,其他回放路径可用);限制在 PR 与本文如实披露:该后端不渲染自发光材质。
  • 关联已知限制(非测试失败):同一 softpipe 固定管线对点光/定向光的漫反射、镜面项均输出 0——PHONG 场景只剩环境光剪影(实测 10/255);BASE_COLOR 着色模型可完整渲染(23716 红色像素实证)。因此消费者测试选 BASE_COLOR 模型,验证渲染链路完整性。
  • 经验:"同一函数调用、一个参数失败其余成功"的模式,先怀疑该参数在该后端的能力边界,而不是调用代码;后端限制必须写进测试设计与 PR 正文,测试模型要选得避开限制,通过率数字才干净。

坑 8:CI worker fork EAGAIN——两轮红与三个配方版本(环境因素)

  • 现象:CI 第 1 轮 #99185(10-21)红,失败点在 CMake 编译器 try-compile:/bin/sh: can't fork - try again,CMake 判定"编译器损坏"——但编译器其实根本没跑起来;第 2 轮 #100395(10-22)红,措辞变成 Resource temporarily unavailable,同类失败。
  • 误判/纠正:最初按"配方问题"排查(编译链问题),后来在本机做压力实验复现:构建失败时刻 /proc/loadavg = 24.37(8 核机器),50 次 fork 突发有被拒绝的——本机复现证明这是"进程创建资源耗尽"的环境事件,不是配方缺陷。但难点在后半句:CI worker 上的瞬态压力无法"按需复现",配方必须能在失败当下识别并重试,否则瞬态失败就永远表现为红轮。
  • 根因(两层):① 环境层——CI worker 并发构建,进程表/内存瞬间打满时 fork 返回 EAGAIN,make/sh 报 can’t fork / Resource temporarily unavailable;② 工具层——Conan 把命令失败包成裸的 ConanException("Error 1 while executing"),真实报错走日志流、不进异常文本,任何"看异常文本"的检测都会漏。
  • 修复(v1→v3 三个版本,机制见 2.5):v1 按异常文本签名门控有限重试(退避 0/30/90s)→ 第 2 轮仍漏(异常文本是裸的);v2 增加实机资源探针(/proc/loadavg 1 分钟均值 > 2×核数,或 50-fork 突发被拒)→ 探针有时间窗口(压力是瞬态,探测时 worker 可能已恢复,读数正常 → 漏);v3 增加 CMakeConfigureLog.yaml 磁盘证据——CMake ≥ 3.28 会把每次 try-compile 失败的完整输出(含 make/sh 全文)落盘,fork-EAGAIN 措辞在盘上、确定、无竞态 → 第 3 轮绿。
  • 不变式:重试门只开给 6 个瞬态环境签名(can’t fork / cannot fork / too many processes / Cannot allocate memory / No space left on device / Resource temporarily unavailable),确定性编译错误第一次就快速失败——永不掩盖真错误、永不把失败变通过;最坏给 3600s 构建预算加约 2–3 分钟,且只花在环境失败路径上。
  • 归因结论:两轮红均为基础设施因素(worker 瞬态进程资源耗尽),非配方缺陷——证据链 = 本机压力复现(loadavg 24.37 + fork 突发被拒)+ 失败措辞签名 + v3 磁盘证据判定上线后第 3 轮绿。
  • 经验:共享 CI 上"配方对、红是环境的"是常态,治本手段是把检测做确定(磁盘证据 > 实时探针 > 异常文本);红轮归因必须带证据行,不能写"重试后转绿"。

2.5 构建韧性加固(v1 → v3)

坑 8 的教训固化进配方的 build():configure/build 两步都套一个 _run_step_resilient 包装,检测是三层 OR 门,命中任一层才允许重试:

层检测内容强度
① 异常文本签名异常消息含 6 个瞬态签名之一最弱:Conan 的异常是裸文本,这层多数时候抓不到(只兜"少数会内嵌输出的路径")
② CMake 磁盘证据(configure 步)扫 CMakeFiles/CMakeConfigureLog.yaml——CMake ≥ 3.28 会把每次 try-compile 失败的完整输出落盘最强:确定性、无竞态、无时间窗口
③ 实机资源探针(build 步)/proc/loadavg 1 分钟均值 > 2×核数,或 50-fork 突发被拒中:覆盖构建步与持续性压力,对瞬态压力有时间窗口

重试策略:最多 3 次(退避 0/30/90s),仅当某层命中才重试;3 次都失败则报 [resilience] ... transient environment condition, NOT a recipe or code defect. Re-run once the machine is idle.。探针自身任何异常都降级为"无证据"(检测机制永远不会弄坏构建)。

三个版本的演进,本质是把检测一层层往"磁盘证据"挪:异常文本(在内存里,而且被 Conan 包装得只剩一句 Error)→ 实时探针(物理读数,但瞬态压力有窗口)→ configure log(在盘上,而且是 CMake 替我们把完整输出记好了)。如果你也遇到"本地绿、CI 偶红",这套三层结构可以直接照搬。

2.6 构建结果

8 个坑清完后,conan create 全流程(全新源码下载 + SHA-256 校验 + 19 补丁 + CMake configure + build + package + 消费者测试):

[PASS] scene graph: 5 children added
……(14 断言全表见 3.2)
0 check(s) failed
Build Summary: Total 1, Success 1, Failed 0, Pass rate: 100.0% (1/1)

产物:libInventor.so(5,814,720 字节,arm64)+ 公开头文件(含 include/GL/ 随包的 glx.h shim 与 glu.h,GL 头供给自包含)。完整日志在仓库 archives/i/inventor/2.1.6.1/reports/w3-local-ci.log,可逐行核对(含 14 条 [PASS] 原文与 Build Summary)。

3. 验证

3.1 上游测试清点(R51 三步法·第一步)

上游没有任何自动化测试——清点证据(非推断):

  • 仓库无 tests/ / test/ 目录;顶层 CMakeLists.txt 的 add_subdirectory 仅含 tools/libimage/libFL/lib/[libSoXt]/doc/apps/data,无测试子目录;
  • 全树 grep -rn "enable_testing|add_test|ctest" --include=CMakeLists.txt 无命中;
  • apps/ 是交互式示例程序(需要显示环境),无法在无头环境自动执行。

结论:上游测试用例总数 = 0,通过率不适用(N/A);按 R51 在 test_package/MANIFEST.yml 如实声明 upstream_tests: [],并自补消费者测试(3.2)。上游测试修改 = 0(无东西可改,这是申报红线)。

3.2 消费者测试(14 断言,自补)

test_package/test.cpp 模拟业务上层组件调用 inventor 的公开 API(SoDB/SoSeparator/SoMaterial/SoCube/SoLightModel/SoDirectionalLight/SoCamera/SoOffscreenRenderer/SoOutput/SoInput/SbMatrix/SbRotation 等),共 14 断言:

#断言类别
1scene graph: 5 children added场景图构造
2–4SoOutput openFile 写 / SoInput openFile 读 / SoDB::readAll 读回序列化 / 反序列化
5–8round-trip: root is SoSeparator / 5 children preserved / SoCube present / cube width preserved序列化往返(场景持久化)
9–10OSMesa: context created / context made current渲染(离屏上下文)
11–12render: scene visible in framebuffer / red cube drawn(23716 红色像素)渲染(真实像素验证)
13–14SbMatrix makeIdentity / SbRotation +90° 把 X 轴映射到 Y数学库

设计说明:① 场景 5 子节点(lightModel/camera/material/light/cube),着色模型选 BASE_COLOR(避开 softpipe 固定管线漫反射/镜面项为 0 的限制,见坑 7);② 渲染段建 OSMesa 上下文 + SoGLRenderAction 渲染 + glReadPixels 读回,验证红色主导像素数 > 100(实测 23716)——真实像素,不是"调用没报错";③ 场景根按 SGI 2.1 所有权约定显式 ref()(见坑 6)。

执行统计(三步法·第二步,全部真机 OHOS aarch64):

执行结果
W3 本地 CI(conan create,全新源码 + 19 补丁)14/14 PASS,exit 0
conan-verify G2(配方验证 conan create)14/14 PASS
conan 包产物手动运行(LD_LIBRARY_PATH = conan 包 lib)14/14 PASS,exit 0 × 连续 5 次(崩溃回归稳定性确认)

总通过率:100%(14/14)≥ 路径 C 要求的 90%。共 6 轮验证全绿(PR 正文②口径,其中含包产物手动连跑 5 次);失败用例 = 0,跳过用例 = 0,未通过用例说明:无(修复过程中曾失败的 3 个问题——GL_EMISSION / UAF / GLU 头——均已修复,证据见 2.4 与 test-report.md §4)。

2026-11-08(本文写作时)快层复跑同样 14/14,日志已留档:
在这里插入图片描述

图1:鸿蒙 PC 真机消费者测试快层复跑,14/14 PASS(证明产物真机可用、渲染链路完整;不能证明全量全新构建——4.7 ① 与 w3-local-ci.log 互补)

3.3 产物核验(符号面,2026-11-08 readelf/nm 实测)

项实测值结论
动态依赖 NEEDEDlibOSMesa.so.8、libGLU.so.1、libX11.so.6、libfreetype.so.6、libc++_shared.so、libc.soGL 栈全部解析到 OSMesa(105 个 gl* 未定义符号由 libOSMesa.so.8 满足);X11 客户端库保留(核心少数文件使用,不用于渲染)
iconv 未定义符号0libiconv 已静态折入(NEEDED 无此项且无未定义符号)
jpeg 未定义符号0libjpeg 已静态折入(同上)
SONAME无条目上游未设 SOVERSION——L5 咨询项,非阻断(见 3.4)
include/GL/glx.h(shim)+ glu.h 随包GL 头供给自包含,下游消费者直接以公开头编译

3.4 验证门禁矩阵

conan-verify 8 门禁(10-22 19:25 执行,G2 为全新 conan create):

门禁结果说明
G0 预构建风险评估✅知识图谱命中 E092(CMake 不认识 HarmonyOS 作为目标平台、无对应 platform module)——配方已处理
G1 配方完整性✅name/version/source/build/package/package_info/export_sources/补丁调用/test_package + MANIFEST 齐全
G2 conan create✅全新构建成功(reports/conan-create.log)
G3 系统同名库隔离✅纯库包(无 bin/),无系统同名库冲突
G4 产物完整性✅bin/lib/include 产物检查通过
L2/L4/L5跳过当时无 cache 路径(由 W3 补位,下表)

W3 本地 CI(10-22 19:27 执行,.ci-result.json 由 finish 生成,exit 0):

门禁结果说明
L0 质量门禁pass子项证据(CI #99185 日志):7 个依赖包逐一 “dependency in registry: ok”、版本不在制品仓(首次发布)、库包无需 commands.json、分支名与包一致
L1 测试执行pass消费者测试 14/14
L1e 破坏性反向验证(R34)pass移走 lib/ 与 include/ 后测试必须失败——证明测试真实依赖 inventor 包,不是空壳
L2 smoke test跳过无 bin/(纯库包)
L4 安全扫描warnsprintf 为存量代码警告,非阻断
L5 产物基线咨询无 SONAME(上游未设 SOVERSION),非阻断
Build Summary1/1,100%—

4. 评审与合入

4.1 评审要求

要求本案
测试用例通过率 ≥ 90%14/14 = 100%(见 3.2)
未通过用例必须详细说明,且不能因适配问题导致失败未通过用例 = 0;修复过程中曾失败的 3 个问题均已修复并留证据(test-report.md §4)

4.2 CI 完整轮次表(4 轮,红轮不删)

轮次构建号WorkerSHA(配方版本)触发结果说明
1#99185CI-022ee8f905c(初版)10-21 17:37 /rebuild❌CMake try-compile /bin/sh: can't fork - try again(worker 进程创建资源耗尽,基础设施因素)
2#100395CI-0127cd5b44b(v2)10-22 16:55 /rebuild❌同类失败,措辞 Resource temporarily unavailable(v2 探针时间窗口漏检,基础设施因素)
3#101248CI-002c89e5dd4(v3)10-22 21:21 /rebuild✅10-23 05:48 完成,全绿:Conan test_package / L0 / L1 全部通过
4REL #15801REL-011合入后 main10-31 21:51 /release✅conan-build-test / conan-upload / sign-artifacts 全过,制品发布

口径说明:CI 是评论触发型(/rebuild、/release 由人评论触发);前 3 轮是 PR 校验(不 upload,只构建 + 测试 + 门禁),第 4 轮是合入 main 后的正式发布流水线(build → upload → 签名)。两轮红的归因证据见坑 8。

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

时间结果处理
10-20 15:41首次检视失败(task-386526/386527,平台临时故障)/ai review force 重触发
10-21 15:13重检:未发现问题—
10-22 21:21(v3 版)2 条建议(均为非阻断建议)在讨论串内回复,均已关闭

2 条建议(v3 版,各摘录要点):

  1. 0007 补丁:无条件删除 GL_EMISSION 写入后,懒状态缓存仍记录"已发送",自发光材质会永久性失效;并追问根因——同一 switch 同一 glMaterialfv,为何只有 EMISSION 返回 GL_INVALID_OPERATION,"删除调用掩盖症状"是否定位到了真因;
  2. test.cpp:151:CHECK 只累计失败计数、不中断执行,OSMesaMakeCurrent 失败后仍会渲染并读未初始化的像素缓冲(UB),存在假通过风险。

处理(两条均在讨论串回复并关闭;合入 head c89e5dd4 的代码未改动):① OSMesa/softpipe 固定管线对 GL_EMISSION 的硬拒绝是实测后端限制(0x500,裁决证据见坑 7),懒状态记账是有意保留,限制已如实披露在 PR 正文⑥与本文;② MakeCurrent 返回值有 CHECK 断言,且其后的非黑/红色像素断言对渲染结果构成有效门禁——假通过需要未初始化的栈内存恰好出现 >100 个红色主导像素,该风险已接受并在本文披露。

4.4 人工评审与合入

  • 评审:Clancy_Xie 审批通过(approved);
  • 合入:Clancy_Xie,squash merge,2026-10-23 21:07;base = main@c29e1da5,head = c89e5dd4(v3 配方);
  • 合入前置检查:CI 状态通过(第 3 轮绿)+ 检视意见全部关闭。
    在这里插入图片描述

图2:PR !17377 合入页

4.5 制品发布

10-30 18:11 首次 /release 因平台 API 限流作废(机器人提示 PR 状态查询失败,该轮未触发);10-31 21:51 重新 /release → REL #15801(worker REL-011)全步骤通过(21:56 完成),其中 conan-upload + sign-artifacts 完成制品上传与 GPG 签名。此后 inventor/2.1.6.1@ohpcd/stable 从制品仓直接可消费(2026-11-08 复验:该包消费者测试 14/14、7 依赖 7/7 可消费)。

4.6 评审记录(总览)

角色人/工具意见/操作处理闭环
AI 检视(第 1 轮)atomgit-bot检视失败(平台临时故障)/ai review force 重触发✅
AI 检视(第 2 轮)atomgit-bot未发现问题—✅
AI 检视(第 3 轮)atomgit-bot2 条建议(0007 GL_EMISSION 记账 / test.cpp:151 未初始化缓冲)讨论串内回复说明(合入 head 代码未改动)✅
人工评审Clancy_Xieapproved—✅
合入审批Clancy_Xiesquash merge(2026-10-23 21:07)—✅
CIohpcd-robot3 轮 PR 校验(2 红 1 绿)+ 1 轮 release(绿)两轮红归因基础设施因素(证据见坑 8),绿轮随 v3 判定上线✅

检视意见全部闭环、CI 绿——"检视意见闭环 + CI 绿"两个硬条件均满足。

4.7 复现速查

前置:build_in_harmonyos 仓库(main 分支,本配方已合入 main)+ OHOS 适配版 conan(社区源安装,支持 os=OHOS 枚举)+ 制品仓 7 个依赖已发布(截至 2026-11-08 复验 7/7)。

① 全量构建 + 消费者测试(全新源码 + 19 补丁,约 20–30 分钟):

conan create archives/i/inventor/2.1.6.1 -pr:h=ci/conan/profiles/ohos-aarch64 -pr:b=ci/conan/profiles/ohos-aarch64 --build=missing --user=ohpcd --channel=stable

预期:14 行 [PASS] + 0 check(s) failed + Build Summary: Pass rate: 100.0% (1/1)。

② 消费者快层(需 ① 已完成或缓存已有该包,约 1 分钟):

conan test archives/i/inventor/2.1.6.1/test_package "inventor/2.1.6.1@ohpcd/stable" -pr:h=ci/conan/profiles/ohos-aarch64 -pr:b=ci/conan/profiles/ohos-aarch64

预期:14 行 [PASS] + rendered pixels: red-dominant 23716, non-black 23716 + 0 check(s) failed(即图1 的画面)。

③ 依赖基线复验(7 个依赖逐一,约 1 分钟):

for d in mesa/24.3.4.1 glu/9.0.3 libx11/6.4.0 libjpeg/10 libiconv/1.17.1 freetype/2.14.3.2.1 bison/3.8.2; do conan download "$d" -r=ohpcd --only-recipe && echo "OK $d"; done

预期:7 行 OK <pkg>。

收尾(口径映射):① 对应 §2.6/§3.2/§3.4(全新构建 + 14/14 + 门禁矩阵,完整日志在仓库 reports/w3-local-ci.log);② 对应 §3.2 与图1(OSMesa 无头渲染,23716 红色像素);③ 对应 §2.2(适配前置:7 依赖可消费基线)。三条命令分别验证"能构建、能运行、依赖链不断"——全部通过即本机当前状态与 PR 合入时一致。

5. 结论

5.1 量化盘点

维度数字
源码补丁19 个(0 平台宏)
机械清理规模register 134 处(12 文件)+ PRIiPTR 2 处
消费者测试14 断言,14/14 = 100%(≥ 路径 C 要求的 90%)
验证轮次共 6 轮全绿(其中含包产物手动连跑 5 次,PR 正文②口径)
CI3 轮 PR 校验(2 红 1 绿,两轮红均为基础设施因素)+ 1 轮 release(绿)
提交链4 个(初版 → v1 → v2 → v3),squash 合入
制品libInventor.so 5.5 MB(5,814,720 字节)+ 自包含公开头
坑8 个(见 2.4,其中 2 个误判)
时间线10-18 立项 → 10-23 合入(6 天)→ 10-31 制品发布

5.2 可复用方法

  1. 整栈 GL 重定向模式(无 X 显示服务器的平台):OPENGL_gl_LIBRARY → libOSMesa(导出完整 OpenGL API)+ 多包 GL 头合并到单一目录 + 缺失头(glx.h shim / glu.h)随包发布——包自包含,下游无需感知平台差异;
  2. 瞬态失败检测三层结构(本地绿、CI 偶红):异常文本(最弱)→ 实时资源探针(有时间窗口)→ 磁盘证据 CMakeConfigureLog.yaml(确定、无竞态,最强);重试门只开给瞬态签名,确定性失败第一次就 fail fast,永不掩盖;
  3. 上游无测试的三步法:清点证据(目录 + grep,非推断)+ 如实声明(MANIFEST.yml upstream_tests: [])+ 自补消费者测试——消费者测试要验证真实像素/真实输出,不是"调用没报错";
  4. 对照组实验裁决"库的问题 vs 我的用法":崩溃怀疑与证据打架时,摘段重跑比读库源码便宜一个数量级;深栈崩溃第一要查内存所有权约定(SGI 2.1 的 refCount=0 约定);
  5. CI 红轮不删、归因带证据行:"基础设施因素"的结论需要本机压力复现 + 失败措辞签名 + 修复后绿轮对照。

5.3 已知限制

  • 自发光材质(GL_EMISSION)不渲染:OSMesa/softpipe 固定管线硬拒绝(0x500),0007 源码级跳过,限制已披露;
  • 固定管线光照仅环境光项:点光/定向光的漫反射、镜面项输出 0,PHONG 场景 = 环境光剪影(实测 10/255),BASE_COLOR 模型完整可用(23716 红色像素);
  • 无 SONAME:上游未设 SOVERSION,L5 咨询项,不影响 conan 依赖管理;
  • 渲染为软件光栅化:性能面向功能验证与轻量场景,非实时交互渲染。

5.4 后续工作

  • GL_EMISSION 记账改进(GL 写入成功才同步 glState,避免"已同步"假记账——AI 检视指出的方向);
  • 补 SOVERSION/SONAME(需评估上游兼容性);
  • GUI 方向(libSoXt)依赖平台侧 Motif/X11 生态建设,超出本案范围。

6. FAQ

Q1:这个库在鸿蒙 PC 上能干什么?为什么没有窗口?
它是无头 3D 场景图库:构建场景(几何/材质/光照/相机节点)、序列化、离屏渲染到像素缓冲。鸿蒙 PC 无 X 显示服务器,GUI 绑定 libSoXt(Motif/Xt)未适配(其 X11 族与核心依赖二进制不兼容,且无头环境无用武之地),所以没有窗口形态——这是平台边界,不是功能缺失。

Q2:渲染性能如何?
OSMesa 是软件光栅化(softpipe),性能量级面向功能验证与轻量场景处理(场景序列化、离屏出图、CAD 模型解析),不是实时交互渲染。

Q3:为什么 PHONG 场景是"剪影"?
softpipe 固定管线对点光/定向光的漫反射、镜面项输出 0,只有环境光项生效(实测 10/255)。需要完整光照效果时,选 BASE_COLOR 着色模型(可完整渲染)或等后端能力升级。

Q4:没有 SONAME 会不会影响链接?
上游 CMake 未设 SOVERSION,属 L5 咨询项(非阻断):conan 依赖管理按包路径解析而非 SONAME,消费者侧无影响;后续方案见 5.4。

Q5:为什么选 OSMesa 而不是 EGL/GLES?
inventor 2.1 的渲染层是桌面 GL(OpenGL 1.x 固定管线 + GLX 入口),迁 GLES 等于重写整个渲染层(fork 级改动);OSMesa 是桌面 GL 的软件光栅化实现,源码侧只需"把 GL 库指到 libOSMesa",且与制品仓预编译 GLU 精确匹配。

Q6:14/14 的结果能复现吗?
能,3 条命令,见 4.7:① 约 20–30 分钟(全量)、② 约 1 分钟(快层)、③ 约 1 分钟(依赖基线)。

7. 参考链接

链接说明可达性核验
https://github.com/aumuell/open-inventor上游仓库(SGI Open Inventor 官方维护 fork)curl 200(2026-11-08 )
https://github.com/aumuell/open-inventor/archive/refs/tags/v2.1.6.1.tar.gz上游源码 tarball(sha256: 02e9b2d4c7dd16b36be2b3f16d98805408d6b5d525b4397456fe5fed26636cc3)本机 curl HEAD 200(2026-11-08,1 次重定向至 codeload.github.com)
https://atomgit.com/OpenHarmonyPCDeveloper/build_in_harmonyos/merge_requests/17377本案 PR(已合入)curl 200(2026-11-08)
https://ohpcd.atomgit.com/ci?id=101248合入前最终绿轮(第 3 轮)取自 PR 评论线程(ohpcd-robot)
https://ohpcd.atomgit.com/ci?id=15801发布流水线(第 4 轮,upload + 签名)取自 PR 评论线程(ohpcd-robot)
https://harmonypc.csdn.net开源鸿蒙 PC 社区curl 200(2026-11-08)
Logo

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

更多推荐