From d7460ed70ece8021b7fa7203efd455890aee8e2f Mon Sep 17 00:00:00 2001 From: sunrisepeak Date: Wed, 29 Jul 2026 09:01:38 +0800 Subject: [PATCH 1/2] docs: correct the eui-neo mutually-exclusive-backend row in README MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two things were wrong in it. It named `opengl`/`vulkan` and `glfw`/`sdl2` as if all four were features. They are not: compat.eui-neo declares `vulkan`, `sdl2`, `network`, `markdown`, `app-main` and `app-main-sdl2`. OpenGL and GLFW are the defaults, expressed by NOT naming a feature — there is no `opengl` and no `glfw` to name. The row described a design that was tried and abandoned. It also said "`default` feature 在 mcpp 上不可用", which contradicts the "恒开的 interface define" row two lines below, where compat.curl uses `default = { implies = ... }` precisely because it always applies. The accurate claim is narrower: `default` cannot express a mutually exclusive choice — its own defines/sources/deps are inert, and what it implies cannot be overridden by a named feature. That second half is a liability here and an asset there, which the row now says explicitly and cross-references by name rather than by position. Co-authored-by: SPeak Agent <248744407+speak-agent@users.noreply.github.com> --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index cae6c1d..0823743 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,7 @@ mcpp self config --mirror CN # 切换至国内镜像,默认使用 GLOBAL 上 | 全源码直编 + 生成 config(仅缺口平台) | [`compat.curl`](pkgs/c/compat.curl.lua)(win32 用上游签入 config,unix 生成) · [`compat.sdl2`](pkgs/c/compat.sdl2.lua)(win/mac 用上游签入 config,linux 生成 + 手工开 X11) | | 补索引空缺的头文件包 | [`compat.glx-headers`](pkgs/c/compat.glx-headers.lua)(libglvnd 的 `GL/glx.h`,Khronos registry 不含,SDL 的 X11 后端必需) | | C++ 应用框架 compat(依赖复用索引内既有包) | [`compat.eui-neo`](pkgs/e/compat.eui-neo.lua)(上游 `3rd/` 自带 8 个 vendored 依赖,此处一个不编,全部改指索引内同版本 `compat.*`) | -| 互斥后端(同包多后端二选一) | [`compat.eui-neo`](pkgs/e/compat.eui-neo.lua) 的 `opengl`/`vulkan` 与 `glfw`/`sdl2`。**`default` feature 在 mcpp 上不可用**(带 `defines/sources/deps` 完全不生效;带 `implies` 反而恒生效),可行解是用 `-DMCPP_FEATURE_` 在强制包含头里做前置判定。另注意 `cflags` 只作用于 C TU,C++ 需 `cxxflags` | +| 互斥后端(同包多后端二选一) | [`compat.eui-neo`](pkgs/e/compat.eui-neo.lua):`vulkan` / `sdl2` 各自**替换**默认的 OpenGL / GLFW,默认后端由"不点名任何 feature"表达,并不存在 `opengl`/`glfw` feature。`default` feature 表达不了互斥 —— 它自带的 `defines`/`sources`/`deps` 完全不生效,而 `implies` 又恒生效、无法被点名的 feature 覆盖(后者反而正好是本表『恒开的 interface define』一行的解法)。可行解是读 mcpp 本就会传的 `-DMCPP_FEATURE_`,在强制包含头里做前置判定。另注意 `cflags` 只作用于 C TU,C++ 需 `cxxflags` —— 只写进 `cflags` 的后端 define 到不了任何 `.cpp` | | 宿主运行时适配(不 vendor 驱动) | [`compat.glx-runtime`](pkgs/c/compat.glx-runtime.lua) · [`compat.vulkan-runtime`](pkgs/c/compat.vulkan-runtime.lua)(mcpp 产物跑在自带 glibc 下,裸 soname 的 `dlopen` 够不到宿主驱动;用符号链接农场 + `runtime.library_dirs` 打通。注意 farm 只放带版本号的 soname —— `library_dirs` 同时进链接行) | | 恒开的 interface define | [`compat.curl`](pkgs/c/compat.curl.lua) 的 `CURL_STATICLIB`:`cflags` 恒开但包私有,feature `defines` 可达消费端但需点名 —— `default = { implies = … }` 无条件生效,恰好两者兼得 | | 单包多 major(形态随版本切换) | [`compat.catch2`](pkgs/c/compat.catch2.lua)(3.x 编 `src/catch2/` 出静态库;2.x 走 `single_include/` header-only) | From 088ec8a418f648d0bede084adbedb39fad1bcd7e Mon Sep 17 00:00:00 2001 From: sunrisepeak Date: Wed, 29 Jul 2026 09:09:46 +0800 Subject: [PATCH 2/2] docs: fix broken references and align README/docs with what the index now holds MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Link and path fixes: * `docs/04-schema-xpkg-extension.md` in the mcpp repo is a 404 and has no commit history — it never lived at that path. Three places pointed at it (README, docs/README, docs/repository-and-schema). Replaced with what is actually authoritative today: `mcpp xpkg parse`, which is the parser CI runs and which fails on unknown mcpp-segment keys rather than ignoring them, plus mcpp's docs/spec/ for semantics. * docs/package-types.md pointed at `pkgs/m/mcpplibs.xpkg.lua`, which does not exist; the file is `pkgs/x/xpkg.lua`. * `docs/spec/package-identity.md` read like a path in this repo. It is mcpp's, and is now a qualified link. Alignment. The shape catalogue listed four forms while the README example table had grown to ten rows, and README still advertised "四类". The three shapes the index actually grew are now documented, each with the reason it exists rather than just its shape: * E — generated config for a full-source build. Works because these upstreams compile unselected backends to empty TUs; generate only where upstream has a gap, and generate with this index's toolchain (a host `cc` once produced a curl config asserting ssize_t did not exist). * F — shared-library compat. `soname` is load-bearing when something else dlopens the library: SDL2 opens libvulkan.so.1 itself, and against a static loader an application ends up with two of them. Also why the declaration sits in the linux block — a shared target propagates -fPIC, which clang rejects for the msvc target. * G — host runtime adapter. Why it is needed at all (mcpp runs under its own glibc, so a bare-soname dlopen never searches the host path), plus the two traps: versioned sonames only, because runtime.library_dirs also lands on the link line (mcpp#304), and the closure has to be complete or the executable stops starting. Every relative link and heading anchor in README and docs/ re-checked against GitHub's slug rules, and every external link fetched: all resolve. Co-authored-by: SPeak Agent <248744407+speak-agent@users.noreply.github.com> --- README.md | 5 +-- docs/README.md | 2 +- docs/package-types.md | 62 +++++++++++++++++++++++++++++++++-- docs/repository-and-schema.md | 4 +-- 4 files changed, 66 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 0823743..7c9b526 100644 --- a/README.md +++ b/README.md @@ -61,10 +61,11 @@ mcpp self config --mirror CN # 切换至国内镜像,默认使用 GLOBAL 上 细节文档位于 [`docs/`](docs/),供人工与 agent 共同使用: -- [库形态与描述符模板](docs/package-types.md):C 源码、header-only、模块、外部 Form-A 四类模板与样例。 +- [库形态与描述符模板](docs/package-types.md):各类形态的描述符模板与样例,以及最小工程的写法。 - [CN 镜像闭环](docs/cn-mirror.md):`gtc` 与 gitcode 操作,以及无 `mcpp-res` 权限时的回退方案。 - [仓库结构与 schema 与 CI](docs/repository-and-schema.md):字段速查、选跑机制与本地 lint。 -- 字段规范见 [mcpp 扩展字段文档](https://github.com/mcpp-community/mcpp/blob/main/docs/04-schema-xpkg-extension.md)。 +- 字段的**权威判定**是 `mcpp xpkg parse`(CI 用的就是它:未知的 mcpp 段字段直接失败,而不是被静默忽略); + 语义与约束见 mcpp 仓的 [`docs/spec/`](https://github.com/mcpp-community/mcpp/tree/main/docs/spec)。 > 提交 PR 后,`validate` 自动执行 lint 并按改动库选跑对应 workspace 成员(整个测试面是一个 mcpp > workspace,公开模块包 `imgui`/`ffmpeg`/`opencv`/`tinyhttps` 也是普通成员——`compat` 的重定向声明在 diff --git a/docs/README.md b/docs/README.md index 16233f1..f2f9615 100644 --- a/docs/README.md +++ b/docs/README.md @@ -10,4 +10,4 @@ | [repository-and-schema.md](repository-and-schema.md) | 仓库布局、描述符 schema 速查、`validate.yml` CI 行为、本地 lint 复现、案例索引 | > 包的字段规范(`mcpp = { … }` 扩展)以上游为准,见 -> [mcpp docs/04-schema-xpkg-extension.md](https://github.com/mcpp-community/mcpp/blob/main/docs/04-schema-xpkg-extension.md)。 +> `mcpp xpkg parse`(CI 使用的同一个解析器),语义与约束见 mcpp 仓的 [`docs/spec/`](https://github.com/mcpp-community/mcpp/tree/main/docs/spec)。 diff --git a/docs/package-types.md b/docs/package-types.md index 3c48557..3dffe24 100644 --- a/docs/package-types.md +++ b/docs/package-types.md @@ -3,7 +3,7 @@ 编写描述符前,应先判定库所属的形态,再选用对应模板。`mcpp = {}` 内的所有路径均为**相对 verdir 的 GLOB**: 前导 `*` 用于吸收 tarball 的 `-/` wrap 层;`*` 匹配单段,`**` 匹配跨段(例如 `*/blas/*.cpp` 合法)。 -四种形态的判定要点如下: +A–D 是四种**基础**形态,先按它们判定;E–G 是在基础形态之上叠加的处理方式,按需组合。 | 形态 | 特征 | 样例 | 关键字段 | |---|---|---|---| @@ -11,6 +11,11 @@ | **B. header-only** | 纯头文件,无需编译 | `pkgs/c/compat.eigen.lua`、`compat.opengl.lua`、`compat.khrplatform.lua` | `include_dirs` 与 anchor 源 | | **C. C++23 module** | 暴露 `import x.y;` | `pkgs/n/nlohmann.json.lua` | `modules` 与 `generated_files` 或源 `.cppm` | | **D. 外部 Form-A 模块仓** | 上游自带 mcpp 描述符,独立仓库 | `pkgs/i/imgui.lua`、`pkgs/m/mcpplibs.*` | `mcpp = ""`(Form A) | +| **E. 生成 config 的全源码直编** | 上游用 configure/CMake 生成配置头,此处以 `generated_files` 落一份快照 | `pkgs/c/compat.libpng.lua`、`compat.curl.lua`、`compat.sdl2.lua`、`compat.ffmpeg.lua` | `generated_files` + `include_dirs` | +| **F. 共享库 compat** | 必须是**唯一**的那个 `.so`(会被第三方 `dlopen`) | `pkgs/c/compat.x11.lua` 等 X11 家族、`compat.vulkan.lua`(linux) | `targets = { kind = "shared", soname = … }` | +| **G. 宿主运行时适配** | 驱动之类无法 vendor 的东西,只做符号链接农场 + 元数据 | `pkgs/c/compat.glx-runtime.lua`、`compat.vulkan-runtime.lua` | `runtime.library_dirs` / `capabilities` | + +完整的样例索引见[根 README 的「参考示例」表](../README.md#参考示例lua-描述符)。 A、B、C 三类共用的骨架(`package` 头与 `xpm`)如下: @@ -122,7 +127,60 @@ modules 冲突),应配合 `import std;`。 上游或独立仓库自带 mcpp 描述符,本仓仅充当指针:`mcpp = "<相对或远程路径>"`(Form A,而非内联的 Form B)。新增的 独立库通常归属于另一仓库(如 `mcpplibs/imgui-m`),本仓只负责登记。写法可参照 `pkgs/i/imgui.lua` 与 -`pkgs/m/mcpplibs.xpkg.lua`。 +`pkgs/x/xpkg.lua`。 + +--- + +## E. 生成 config 的全源码直编(`compat.curl` / `compat.sdl2`) + +上游用 configure 或 CMake 生成一份配置头,而本仓要的是「列出 .c 文件」。可行的前提是这类库把**未选中的后端 +编成空 TU**(curl 的 `vtls/gtls.c` 从头到尾是 `#ifdef USE_GNUTLS`,SDL 的 `src/video/windows/*.c` 同理),于是 +源码列表可以是朴素的 glob,配置全部落在一份 `generated_files` 快照里。 + +只在**上游有缺口的平台**生成:curl 签入了 `lib/config-win32.h`(Windows 无需生成),SDL 签入了 +`SDL_config_windows.h` / `SDL_config_macosx.h`(只有 linux 落到无用的 `SDL_config_minimal.h`)。生成时务必 +用**本索引的工具链**跑 configure —— 用宿主 `cc` 生成的 curl 配置曾断言 `ssize_t` 不存在,导致 curl 编不过自己 +的配置。 + +## F. 共享库 compat(`compat.x11` 家族 / `compat.vulkan`) + +当这个库会被第三方 `dlopen` 时,它必须是进程里**唯一**的那一个,静态链接会出问题。 + +```lua +targets = { ["vulkan"] = { kind = "shared", soname = "libvulkan.so.1" } }, +``` + +`soname` 不是可选项:SDL2 的 `SDL_CreateWindow(SDL_WINDOW_VULKAN)` 会 `dlopen("libvulkan.so.1")` 并用它解析 +surface 创建。若 loader 是静态的,应用最终会有两个 loader —— 自己那份建 instance,SDL 那份建 surface —— +`createSurface` 拿到一个对方没见过的 instance 而失败。 + +声明位置也有讲究:`kind = "shared"` 会把 `-fPIC` 传播给消费者,而 clang 对 msvc 目标直接拒绝该选项。因此 +`compat.vulkan` 把它写在 **linux 块内**,Windows 走另一套(链接预生成的 import library)。平台块里的 `targets` +会覆盖顶层声明,`compat.ffmpeg` 亦如此。 + +## G. 宿主运行时适配(`compat.glx-runtime` / `compat.vulkan-runtime`) + +GPU 驱动无法打包 —— ICD 必须匹配机器上的内核驱动。本仓的既定立场是把它建模为**宿主能力**,而不是假装厂商 +驱动是可再分发的普通包(见 `.agents/docs/2026-06-03-gl-runtime-packages-plan.md`)。这类包不 vendor 任何东西, +只做符号链接农场加元数据: + +```lua +runtime = { + library_dirs = { "mcpp_generated//lib" }, + capabilities = { "vulkan.icd.driver" }, +}, +``` + +之所以需要它:mcpp 的产物跑在**自带的 glibc** 下(`interp` 指向 `xim-x-glibc`,rpath 只覆盖 mcpp 自己的树), +因此裸 soname 的 `dlopen` 根本不搜索宿主库路径 —— loader 能找到全部 ICD manifest,却一个驱动都打不开。 + +两个反复踩到的细节: + +- **农场里只放带版本号的 soname**(`lib*.so.*`)。`runtime.library_dirs` 同时进**链接行**,一个裸 `libxcb.so` + 会遮蔽本仓自己的 `compat.xcb`,链接报 `undefined reference to XauDisposeAuth`(mcpp#304)。带版本号的名字对 + 链接器不可见,而恰好是 `dlopen` 要的。 +- **闭包必须完整**。农场里有 `libxcb.so.1` 却没有它依赖的 `libXau.so.6`,会遮蔽掉本来能解析的宿主副本,可执行 + 文件直接起不来。 --- diff --git a/docs/repository-and-schema.md b/docs/repository-and-schema.md index eb624ec..abebf9d 100644 --- a/docs/repository-and-schema.md +++ b/docs/repository-and-schema.md @@ -31,7 +31,7 @@ tools/compat-ffmpeg/ 等 compat 大包的描述符再生成流水线 `/home/speak/workspace/github/mcpp-community/mcpp`)。`mcpp --version` 应与 CI 对齐;feature 与 glob 行为以 `src/manifest.cppm`、`src/modgraph/scanner.cppm`、`src/build/prepare.cppm` 为准。 - xpkg 扩展 schema(权威): - https://github.com/mcpp-community/mcpp/blob/main/docs/04-schema-xpkg-extension.md(对应本仓 `.xpkgindex.json` 的 + https://github.com/mcpp-community/mcpp/tree/main/docs/spec(对应本仓 `.xpkgindex.json` 的 “mcpp ext” 链接)。V1 xpkg spec 见 `d2learn/xim-pkgindex` 的 `docs/V1/xpackage-spec.md`(url-template 约在第 172 行)。 - CN 镜像组织:gitcode `mcpp-res`。 @@ -41,7 +41,7 @@ tools/compat-ffmpeg/ 等 compat 大包的描述符再生成流水线 ### 包身份:`(namespace, name)` -身份是二元组 —— **`namespace` 是点分层级路径,`name` 是单一原子段**。层级一律放 `namespace`(mcpp SPEC-001 §3.2,`docs/spec/package-identity.md`): +身份是二元组 —— **`namespace` 是点分层级路径,`name` 是单一原子段**。层级一律放 `namespace`(mcpp SPEC-001 §3.2,见 mcpp 仓的 [`docs/spec/package-identity.md`](https://github.com/mcpp-community/mcpp/blob/main/docs/spec/package-identity.md)): ```lua namespace = "compat", name = "zlib" -- ✅