Skip to content

wayland 1.26.0:源码构建 + C++23 模块层(Form A fork) - #290

Merged
Sunrisepeak merged 6 commits into
mainfrom
feat/wayland-source-modules
Aug 30, 2026
Merged

wayland 1.26.0:源码构建 + C++23 模块层(Form A fork)#290
Sunrisepeak merged 6 commits into
mainfrom
feat/wayland-source-modules

Conversation

@Sunrisepeak

@Sunrisepeak Sunrisepeak commented Aug 30, 2026

Copy link
Copy Markdown
Member

wayland 从"绑定 xim:wayland"改为源码构建,并带上 C++23 模块层。承接 #289

为什么它没能像 libdrm 那样直接内联

wayland 大部分是生成出来的:protocol/wayland.xml 描述每个接口,wayland-scanner 从它生成约 13000 行 —— 两个库的主体。而生成器是同一棵树里的 C 程序,必须先被编译才能跑。

内联描述符没有构建步骤;install() 也做不到 —— mcpp 是在消费者构建期才编译包源码,别的包安装时不存在任何包产物。build.mcpp 正是为此而设,而它只存在于真实 mcpp 工程里。所以走 Form A fork:mcpplibs/wayland,和 mcpplibs/grpc-m 同形态。

仓的布局按"上游隔离"来:

upstream/    wayland 1.26.0 逐字节原样,永不改动
mcpp/        这个 fork 加的全部内容(config.h、模块、四个成员的 manifest)

更新上游 = 换掉一个目录。CI 每次都用上游自己的 meson 重建 upstream/,所以"没改上游文件"是有测试的,不是承诺。

四个包,不是一个

产出
freedesktop.wayland-scanner 协议代码生成器(kind = "bin")
freedesktop.wayland libwayland-client.so.0 + import wayland.client;
freedesktop.wayland-server libwayland-server.so.0 + import wayland.server;
freedesktop.wayland-util import wayland.util; —— 宏,变成模块能导出的实体

client 与 server 必须是两个文件:它们是两个不同 SONAME,而 Mesa 的 libEGL_mesa两者都有 DT_NEEDED。而 mcpp 把包的源码编一次、让每个库 target 链接全部对象 —— 实测:feature 门控的第二个 target 照样拿到该 feature 的对象。mcpp 自己的告警点了办法:"split into a workspace member"

模块层与库同包(opencv 那种形态),所以是一库一包,而不是 C 一个、模块一个。

模块层不增加 API

import wayland.client; 替掉 #include <wayland-client.h>,其余什么都不变 —— 导出名全是上游的、拼写一样、语义一样,没有包装类型、没有 RAII、没有改名。导出列表是从公开头生成的,不是手维护,所以版本一动不会悄悄少一个名字。

新增的 tests/examples/wayland/tests/modules.cpp 一个 wayland 头都不 include,纯 import 调用原样 API —— 少一个导出就编不过,改过名也编不过。它和原来那个基于头文件的测试同时跑,因为模块层是增量不是替代。

宏是唯一过不去的东西:export 命名的是实体而宏不是。wayland.util 把每个映射成它实际是的东西:

模块里
WL_MARSHAL_FLAG_DESTROY inline constexpr —— 拼写完全不变
wl_container_of(p, sample, member) wl_container_of<&T::member>(p)
wl_list_for_each(p, head, member) for (T *p : wl_list_each<&T::member>(head))
wl_list_for_each_safe / _reverse / _reverse_safe 对应的 wl_list_each_* range
wl_array_for_each(p, array) for (T *p : wl_array_each<T>(array))

_safe 保留了上游"循环体内可删除当前元素"的保证,并且测到了wayland-util 包不链接任何库(它就是模板和常量),所以它的测试自己接链表,不为测试引入依赖。

生成的协议代码是签入的,不是构建产物

wayland-client.h 里有 #include "wayland-client-protocol.h",所以生成的头是这个包的公开接口 —— 每个 include <wayland-client.h> 的消费者都要它在自己的 include 路径上。

build.mcpp 给不了这个:mcpp::include_dir() 按设计只作用于本包 TU、不传播给消费者(docs/07 明说了"消费者要看见的 include 目录属于公开接口,应当写在声明式清单里")。先用 build.mcpp 做过,结果是包自己编过了、消费者编不过:

upstream/src/wayland-client.h:40: fatal error: wayland-client-protocol.h: No such file or directory

生成期还另有一个竞态(mcpp#534):action 的产物成了 ninja 节点,但该包的编译边没有 order-only 依赖指向它们,于是 wayland-server.c:334: error: 'WL_DISPLAY_ERROR' undeclared

两个问题同源:把确定性产物当成了构建产物。它由固定的 wayland.xml 唯一决定,所以签入 mcpp/generated/,用会传播的普通 include_dirs 声明,并由 CI 每轮重新生成后 diff —— 不可能漂移。

freedesktop.wayland-scanner 仍然是独立的包:compositor 要为 wayland-protocols 的 XML 生成绑定,需要的正是这个工具,而按名字请求它就把版本钉死成同一个。

一条实测记录

1.26.0 比 1.23.1 多一个构建修正:scanner.c 现在调用 strndup(),而 -std=c11 会把它藏起来,所以 scanner 需要 -D_GNU_SOURCE=(空值:event-loop.c 不自己定义它,而 wayland-client.c/connection.c 自己定义,空值两边都不冲突)。

版本

1.26.0,上游当前发布号 —— 也比生态的 xim:wayland@1.23.1 新,顺带绕开 mcpp-community/mcpp#533(store 已安装查找忽略 namespace)。旧的 compat.wayland@2026.08.30 随本 PR 移除。

验证

  • 本地端到端:freedesktop 走本仓 checkout、compat.libffi/expat已发布索引 → scanner 被构建并执行 → 两个库产出 → 两个测试(头文件版 + 模块版)全绿
  • fork 仓 CI:gcc + llvm 双工具链,校验 scanner 版本、生成物出处、两个 SONAME、client/server 符号面不重叠、三个模块都产出 BMI,并用上游 meson 重建 upstream/
  • CN 镜像已上传并回取逐字节比对一致

Refs #289, #291

@Sunrisepeak
Sunrisepeak force-pushed the feat/wayland-source-modules branch from b4c2e5d to 89be039 Compare August 30, 2026 03:17
@Sunrisepeak
Sunrisepeak merged commit 775fa81 into main Aug 30, 2026
11 checks passed
Sunrisepeak pushed a commit to mcpp-community/mcpp that referenced this pull request Aug 30, 2026
mcpplibs/mcpp-index#290 replaced compat.wayland with freedesktop.wayland and
freedesktop.wayland-server, built from source out of mcpplibs/wayland.

They are two packages because they are two SONAMEs and Mesa's libEGL_mesa has
DT_NEEDED on both, and because mcpp links every library target in a package
against all of that package's sources — one package cannot emit two libraries
with disjoint contents. So the -lwayland-server escape hatch this example used
to demonstrate is gone: a compositor asks for the server package by name.

The closure is re-measured. Four of the twelve entries are now this project's
own build output — libdrm, libffi and both wayland libraries — and the first of
those is the interesting one: Mesa's libgbm has an absolute RUNPATH into the
payload's libdrm and still binds to ours, because a soname already in the link
map is reused.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants