feat(eui-neo): enable the linux SNI tray backend (EUI_TRAY_SNI) - #245
feat(eui-neo): enable the linux SNI tray backend (EUI_TRAY_SNI)#245FarnaHerry wants to merge 25 commits into
Conversation
0.5.7's tray_bridge.c (:207) speaks freedesktop StatusNotifierItem over
GDBus when EUI_TRAY_SNI is set — glib/gio only, no GTK3, no
libappindicator, so the linux leg no longer has to compile the
EUI_TRAY_HAS_BACKEND=0 stub.
Wiring, in three pieces:
* xpm-level: 'deps = { runtime = { "xim:glib@2.80.0" } }' lands glib in
the SubOS view (verified: mcpp downloads and installs it on a cold
resolve).
* mcpp.linux: -DEUI_TRAY_SNI=1 plus -lglib-2.0/-lgio-2.0/-lgobject-2.0,
and 'runtime.library_dirs' pointing at the staged payload dir so the
dir reaches the consumer's RPATH.
* install() hook (new — the package previously used default extraction):
stages the glib header tree and the soname set out of the SubOS VIEW
into mcpp_generated/glib/, the same staging shape compat.glx-runtime
uses for GL. Two gaps are handled explicitly:
- the GitHub tarball keeps its EUI-NEO-<v>/ wrap layer, the CN
mirror's does not; the hook normalizes to the wrap the '*/' globs
are written against (observed differing on one machine, one day
apart).
- libmount/libselinux/libblkid have no xim provider yet (xim:glib
links them, nothing ships them); they are staged from the HOST
with a loud warn, the same host-plane fallback glx-runtime used
before xim:graphics existed. libffi.so.8 comes from the view —
needs openxlings/xim-pkgindex's libffi publish-list fix (separate
PR) on machines whose view predates it.
Verified on a clean slate (payload, project store, and all hand-made
subos workarounds removed): all six eui-neo workspace members pass —
eui-neo, eui-neo-window, eui-neo-app-main, eui-neo-markdown,
eui-neo-vulkan, eui-neo-sdl2.
…ies not symlinks CI on the previous commit failed cold with 'libffi.so.8 is not in this subos'. Root cause chain: xim:glib's libgio NEEDs libffi.so.8; libffi's publish list names only .so.7 though the tarball ships both; the runtime closure searches the subos VIEW, so the missing publish is fatal even though the store holds the file. openxlings/xim-pkgindex#676 fixes the list upstream, but the descriptor must not hinge on it: * resolve() now tiers view -> xim STORE. The view alone is unreliable at hook time in both directions: a publish list can lag the payload (libffi.so.8), and a package already cached in the store skips the config() run that would publish it, leaving a fresh project view without pcre2/zlib (observed on a wiped .mcpp/). The store is complete from the moment a dep is fetched, which for xpm deps is strictly before this hook runs. XLINGS_HOME provides the second store root. * stage() now COPIES instead of symlinking. The subos view is project-local while the payload is shared across projects: a staged symlink dangled the moment the providing project's .mcpp/ was wiped (observed: 'gio/gio.h file not found' off a shared payload). Copies make the payload self-contained; it is version-immutable and already on the consumer's RPATH, so nothing is lost. * header staging gets the same view->store treatment; os.files/os.dirs are not in the xpm sandbox, so globbing goes through the shell. Verified locally against the CI shape: live pkgindex with the libffi fix REVERTED, all hand-made subos workarounds removed, payload and project store wiped — eui-neo passes cold, the other five members pass against the shared self-contained payload.
CI (pinned mcpp 2026.8.10.3, GNU ld) failed every eui-neo member with
'unable to find library -lglib-2.0' — on artifacts as unrelated as
bin/libXdmcp.so, because package ldflags are unioned into every link.
Root cause: with only runtime.library_dirs, the staged dir reaches link
lines as -Wl,-rpath; ld.lld (mcpp 2026.8.21.3) happens to search rpath
for -l, so local runs passed, but GNU ld does not — its SEARCH_DIRs are
absolute host paths, so -lglib fell through to /lib64 (missing on a
clean runner, or worse: the host's glib where it exists).
Two fixes, both verified against the CI-pinned mcpp downloaded locally:
* ldflags gains '-Lmcpp_generated/glib/lib' — payload-relative, resolved
by mcpp against the install dir, the same convention compat.openblas
and compat.mysql-connector-cpp use ('-Llib'). Now -lglib resolves to
the staged copies on both linkers.
* When libselinux is host-staged, libpcre2-8.so.0 is overwritten with
the host's copy too: the host libselinux references pcre2 symbols
WITH version tags (PCRE2_10.xx), which cannot bind to the xim pcre2's
unversioned definitions — GNU ld hard-errors, lld only warns at
runtime. The host pcre2 carries both versioned and unversioned defs,
satisfying host libselinux and xim glib alike.
Verified: mcpp 2026.8.10.3 (CI pin) — eui-neo and eui-neo-window pass
cold; mcpp 2026.8.21.3 (latest) — eui-neo still passes.
| "libselinux.so.1", | ||
| "libblkid.so.1", | ||
| } | ||
| local host_lib_dirs = { |
There was a problem hiding this comment.
- 1.不要直接用 host 的库
- 2.使用hook 需要做版本判断
- 3.能构建期解决走mcpp-index, 不能构建期解决的走 xim-pkgindex (预构建),必须要用host的非开源库时要把范围收敛到最小 变成一个 link ( 具体参考 xim-pkgindex 里相关的包)
Keep the Linux SNI change at the consumer boundary: EUI-NEO declares xim:glib and links GIO, while xim owns the complete GLib runtime closure. Remove host directory probing, host library copies, store globbing, pcre2 replacement, and payload staging from the consumer hook. The install hook now only normalizes the archive wrap and is explicitly limited to 0.5.7; older EUI-NEO versions retain their original extraction behavior. The remaining libmount/libselinux/libblkid closure belongs in xim-pkgindex as open-source prebuilt runtime packages, not in this descriptor.
|
已按 review 意见更新到 |
|
补充:已将冲突解析后的最终版本推送为 \,并移除残留 merge markers;descriptor parse 已通过。当前 EUI-NEO 侧不再引入任何 host GLib closure。 |
| -- over GDBus when EUI_TRAY_SNI is set — no GTK3, no libappindicator. | ||
| -- GLib owns and publishes its complete runtime closure in xim: | ||
| -- compat.eui-neo only consumes the declared SubOS headers/libs. | ||
| deps = { runtime = { "xim:glib@2.80.0" } }, |
There was a problem hiding this comment.
为什么要走二进制依赖呢?而不是走 compat.glib
There was a problem hiding this comment.
因为 GLib 在这个项目中属于运行时系统库,不是普通的“把源码编进当前项目”的兼容库,所以选择 xim:glib 二进制依赖是有意的。
两种包的职责不同
compat.glib
如果走 compat.glib,它通常意味着:
- 每个 mcpp consumer 都从 GLib 源码重新编译;
- GLib、GObject、GIO 的源码对象被编入当前项目的链接产物;
- 每个 consumer 可能得到一份自己的 GLib;
- 还要处理 Meson 生成文件、平台检测、线程、动 ;
- 无法自然复用系统上的 GLib ABI 和运行时插件布局。
这比较适合:
- header-only 库;
- 小型 C/C++ 库;
- 源码直接编译后能独立工作的第三方库;
- 不依赖复杂安装布局和运行时模块的库。
但 GLib 并不是这种形态。它包含多个相互关联的动
libglib-2.0.so
libgobject-2.0.so
libgmodule-2.0.so
libgio-2.0.so
libgthread-2.0.so
并且 GIO 还涉及运行时模块、GDBus、动态加载和系
xim:glib
xim:glib 是预构建运行时包,负责:
- 发布 GLib 的共享库;
- 发布头文件;
- 发布 pkg-config 文件;
- 处理 libffi、pcre2、zlib 等依赖;
- 将库正确暴露给 SubOS 的 /lib;
- 给 ELF 写入正确的运行时依赖路径;
- 让多个 consumer 共享同一套 ABI 兼容的 GLib。
EUI-NEO 本身只是使用 GLib:
#include <gio/gio.h>
它不应该拥有或重新构建 GLib。它只需要:
deps = { runtime = { "xim:glib@2.80.0" } }
然后链接:
-lglib-2.0
-lgio-2.0
-lgobject-2.0
为什么不能简单把 GLib 源码做成 compat 包
主要有四个问题。
- 重复编译和重复链接
EUI 的六个测试成员都会各自触发 compat.glib 的 他包也可能各自带一份 GLib,浪费大量时间和空间。
- GLib 不是单一库
GIO 依赖 GObject、GModule、GLib,并且还依赖外 码库会把“源码编译”问题变成:
GLib 构建配置
- 生成头文件
- 多个共享库
- pkg-config
- GIO modules
- ELF closure
- platform ABI
这已经是系统运行时打包问题,而不是 compat 源码
- ABI 和运行时闭包必须统一
当前问题正是 GLib 的运行时 closure:
libgio-2.0.so
├── libglib-2.0.so
├── libgobject-2.0.so
├── libffi.so.8
├── libz.so.1
├── libmount.so.1
└── libselinux.so.1
这些库必须来自同一个可控的预构建依赖图。使用 决这些运行时库的版本一致性。
- xim 的职责就是分发这种预构建系统栈
维护者给出的规则也是:
- 能在构建期直接解决的,走 mcpp-index;
- 不能在构建期合理解决、需要预构建交付的,走
- 只有必要的 host 专有库才做最小 host-link 包
GLib 属于第二类,不属于 compat。
这次 CI 暴露的真正问题
不是“应该改成 compat.glib”,而是原来的 xim:glib 发布不完整:
- 初始 recipe 没有正确把 GLib .so 注册到 SubOS 的 /lib;
- 因此 GNU ld 找不到:
-lglib-2.0
-lgio-2.0
-lgobject-2.0
- #680 已修复这个发布层问题,并且已经合并;
- 但 EUI CI 使用的 mcpp/registry 仍然没有把 前链接环境,或者 CI 拉取的 index artifact尚未包含最新 package metadata。
所以正确路线仍是:
EUI-NEO
→ xim:glib
→ xim:glibc
→ xim:libffi
→ xim:zlib
→ xim:pcre2
而不是:
EUI-NEO
→ compat.glib
一句话总结:
▎ compat.glib 是“每个项目自己编译一份源码库” 可复用、可验证、带完整 ABI/runtime closure 的
▎ GLib”。EUI-NEO 应该使用后者。
## CN 镜像
0.5.7 之前 gitcode 上 404,0.5.5 的描述符注释还写着「从未发布到 mcpp-res」。
本地 gtc 各发一次,三个平台腿都改成 `{ GLOBAL, CN }`:
0.5.5 cf0da91d… 13339545 bytes
0.5.7 2d3ec0a3… 14815553 bytes
两个都**重新下载核过**:sha256 与描述符声明的一致(一个 `sha256` 服务两个
URL,本来就要求字节相同),wrap 层也一致。
## ⚠️ 顺手推翻了一条自己写下的理由
上一版注释说「CN 镜像的 tarball 不保留 wrap 层」—— 那是照抄 #245 的说法。
去查了:gitcode 的 `eui-neo-0.5.6.tar.gz` 解出来就是 `EUI-NEO-0.5.6/`,
与 GitHub 的是同一份字节。`normalise_layout` 的扁平分支因此是**防御性的、
未被观察到的**,注释改成这么说。
## 为什么默认开启而不是做成 feature
它确实要每个 Linux 消费者付出:暂存 6.6 MB(2.9 头 + 3.7 库,45 MB 的包上
+17%)、多三条 `DT_NEEDED`、闭包多五个对象,以及 mcpp 2026.8.28.2+ 上一条
「一个库两个提供者」警告(eui-neo 经 libpng 已经在构建 compat.zlib,
而 gio 加载 `libz.so.1`)。
两条仍然定在默认开启:
1. **对称**:Windows 无条件 `EUI_TRAY_WINAPI`、macOS 无条件 `EUI_TRAY_APPKIT`,
上游 0.5.7 也把 SNI 定为 Linux 默认。没有托盘的 Linux 才是那个异类。
2. ⚠️ **feature 表达不出来**:xpkg 的 feature 能带 `sources`/`defines`/`deps`/
`flags`/`implies`/`requires`/`provides`,**不能带 `ldflags` 或 `include_dirs`**。
define 可以门控,`-Lmcpp_generated/glib/lib -lgio-2.0 …` 不行 —— 关掉 feature
的消费者照样链 glib。那比默认开启**严格更差**:代价一样,没有托盘。
真要做成 feature,得先给 mcpp 的 xpkg 解析器加 `features.<name>.ldflags`,
而那个键本身又要等索引下限抬上去;zlib 警告的根治(compat.zlib 的 `soname`)
受同一条约束。理由写进描述符,下一个读到这里的人不用再推一遍。
背景
0.5.7 的
tray_bridge.c(:207)新增了基于 GDBus 的 freedesktop StatusNotifierItem 后端:设置EUI_TRAY_SNI后只需 glib/gio,不再需要 GTK3 + libappindicator。此前 Linux 腿只能编EUI_TRAY_HAS_BACKEND=0的 stub(旧路径在这个索引里没有依赖可提供)。本 PR 把 Linux 托盘真正接上。改动(只有
pkgs/e/compat.eui-neo.lua)deps = { runtime = { "xim:glib@2.80.0" } }—— 冷解析时 mcpp 会自动下载安装(已验证)。-DEUI_TRAY_SNI=1+-Lmcpp_generated/glib/lib(payload 相对路径,同 compat.openblas 的-Llib惯例)+-lglib-2.0 -lgio-2.0 -lgobject-2.0,并声明runtime.library_dirs使暂存目录进入消费者 RPATH。mcpp_generated/glib/(头文件供编译,库供链接与运行时闭包)。来源分层:subos 视图 → xim store → (仅 libmount/libselinux/libblkid)宿主。过程中发现并处理的坑(每个都有实机观测)
EUI-NEO-<v>/顶层目录,CN 镜像的不保留。钩子里归一化为*/glob 所依赖的单层 wrap,否则编不编得过取决于用户镜像。config(),新项目视图就缺 pcre2/zlib;libffi 的发布清单更是漏了.so.8(fix(libffi): publish libffi.so.8 into the subos view openxlings/xim-pkgindex#676,治本修复已提)。store 在依赖下载完成时就是全的,所以加了 store 兜底 —— 本 PR 不再依赖 #676 先合入。.mcpp/后全部吊死(gio/gio.h not found)。改为实体拷贝,payload 自包含。runtime.library_dirs只以-Wl,-rpath形式进链接行;lld(本地新 mcpp)恰好会搜 rpath 所以本地能过,CI 钉住的 2026.8.10.3 用 GNU ld,-lglib直接落空到宿主/lib64。显式-L(payload 相对)补上。验证