From a1ee8aa75c7d3ef76285864dfc52684f637e0bee Mon Sep 17 00:00:00 2001 From: sunrisepeak Date: Sat, 25 Jul 2026 18:51:55 +0800 Subject: [PATCH 1/6] chore: migrate descriptors to SPEC-001 short-name identity (mcpp 0.0.106) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit mcpp 0.0.106 restores the canonical identity form: `namespace` carries the hierarchy, `name` is a single atomic segment. 0.0.105 briefly required the opposite (name spelled as the fully-qualified `.`), which was an encoding constraint rather than a design rule — it existed only because mcpp re-derived the wire name instead of using the literal it had already read. namespace = "compat", name = "compat.zlib" -> name = "zlib" 45 descriptors migrated; the 3 namespace-less public module packages (imgui/ffmpeg/opencv) are unchanged — a bare name IS their full identity. Three short names now repeat across namespaces: `imgui`, `ffmpeg` and `lua` (compat's versus the default namespace's, and compat's `lua` versus mcpplibs.capi's). That is legal — identity is the PAIR, and only the pair has to be unique. It needs xlings >= 0.4.69 (openxlings/xlings#381), which keys the index by (namespace, name); 0.0.106 bundles it. Verified against a local path index: bare `imgui` resolves to `mcpplibs:imgui` and `[dependencies.compat] imgui` resolves to `compat:imgui`, each installing to its own store dir. check_package_name.lua is inverted to match: `name` must be atomic, hierarchy belongs in `namespace`, and the legacy fully-qualified spelling stays accepted (the prefix is stripped before judging) so nothing published before this is invalidated. No filename lint — the filename is not part of identity. min_mcpp/latest_mcpp move to 0.0.106 in lock-step with the CI pin. This one is NOT optional: a client older than 0.0.106 re-derives `.`, misses the short-name key, and reports a bare E_NOT_FOUND with nothing pointing at the cause. Verified locally: all 48 descriptors pass the inverted lint; identity pairs are unique; no `name` still carries a dot; the three counterexamples (non-atomic short name, dotted name without a namespace, legacy FQN) each behave as specified. --- .github/workflows/validate.yml | 31 ++++++++----- docs/repository-and-schema.md | 24 +++++++++- index.toml | 4 +- pkgs/c/chriskohlhoff.asio.lua | 2 +- pkgs/c/cmdline.lua | 2 +- pkgs/c/compat.bzip2.lua | 2 +- pkgs/c/compat.cjson.lua | 2 +- pkgs/c/compat.eigen.lua | 2 +- pkgs/c/compat.ffmpeg.lua | 2 +- pkgs/c/compat.ftxui.lua | 2 +- pkgs/c/compat.glfw.lua | 2 +- pkgs/c/compat.glx-runtime.lua | 2 +- pkgs/c/compat.gtest.lua | 2 +- pkgs/c/compat.imgui.lua | 2 +- pkgs/c/compat.khrplatform.lua | 2 +- pkgs/c/compat.libarchive.lua | 2 +- pkgs/c/compat.lua.lua | 2 +- pkgs/c/compat.lz4.lua | 2 +- pkgs/c/compat.mbedtls.lua | 2 +- pkgs/c/compat.openblas.lua | 2 +- pkgs/c/compat.opengl.lua | 2 +- pkgs/c/compat.spdlog.lua | 2 +- pkgs/c/compat.x11.lua | 2 +- pkgs/c/compat.xau.lua | 2 +- pkgs/c/compat.xcb-proto.lua | 2 +- pkgs/c/compat.xcb.lua | 2 +- pkgs/c/compat.xcursor.lua | 2 +- pkgs/c/compat.xdmcp.lua | 2 +- pkgs/c/compat.xext.lua | 2 +- pkgs/c/compat.xfixes.lua | 2 +- pkgs/c/compat.xi.lua | 2 +- pkgs/c/compat.xinerama.lua | 2 +- pkgs/c/compat.xorgproto.lua | 2 +- pkgs/c/compat.xrandr.lua | 2 +- pkgs/c/compat.xrender.lua | 2 +- pkgs/c/compat.xtrans.lua | 2 +- pkgs/c/compat.xz.lua | 2 +- pkgs/c/compat.zlib.lua | 2 +- pkgs/c/compat.zstd.lua | 2 +- pkgs/f/fmtlib.fmt.lua | 2 +- pkgs/l/llmapi.lua | 2 +- pkgs/m/marzer.tomlplusplus.lua | 2 +- pkgs/m/mcpplibs.capi.lua.lua | 2 +- pkgs/n/nlohmann.json.lua | 2 +- pkgs/t/templates.lua | 2 +- pkgs/t/tensorvia-cpu.lua | 2 +- pkgs/t/tinyhttps.lua | 2 +- pkgs/x/xpkg.lua | 2 +- tests/check_package_name.lua | 81 ++++++++++++++++++++-------------- 49 files changed, 137 insertions(+), 93 deletions(-) diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index aba168e..197faf5 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -11,6 +11,16 @@ on: workflow_dispatch: env: + # 0.0.106: SPEC-001 package identity (mcpp#280). `package.name` is a SINGLE + # ATOMIC SEGMENT — all hierarchy lives in `package.namespace` — and mcpp + # addresses a package by the LITERAL name it read, so descriptors no longer + # repeat their namespace inside `name`. This index is migrated to the short + # form, which is why min_mcpp/latest_mcpp move in lock-step: an older client + # re-derives `.`, misses, and reports a bare E_NOT_FOUND. Bundles + # xlings 0.4.69, which keys its index by (namespace, name) so two packages + # sharing a short name in one index are both addressable (xlings#381) — this + # index now has three such pairs (imgui / ffmpeg / lua under compat vs the + # default namespace). # 0.0.102: windows command-line ceiling (mcpp#261 — the clang scan rule got # its P1689 JSON through shell redirection, which forced a `cmd /c` wrapper # and with it cmd.exe's 8191-char limit; clang-scan-deps -o removes both, and @@ -47,7 +57,7 @@ env: # 0.0.94 fixed feature-gated `sources` under `mcpp test` (mcpp#218); 0.0.91 # added standard = "c++fly" to the resolver grammar, so c++fly descriptors # get the lint WARN below, not a hard grammar-parse rejection. - MCPP_VERSION: "0.0.102" + MCPP_VERSION: "0.0.106" jobs: lint: @@ -90,14 +100,11 @@ jobs: if ! lua5.4 tests/check_mirror_urls.lua "$f"; then fail=1 fi - # 5. `name` must be the fully-qualified `.`. - # The split form (namespace = "chriskohlhoff", name = "asio") - # parses and passes `mcpp xpkg parse`, but xlings keys the index - # on the literal `name` while mcpp asks for the reconstructed - # FQN — they never meet, so the package is uninstallable on - # every platform (mcpp-community/mcpp#278). This check is cheap - # and catches in seconds what otherwise fails an hour into the - # workspace job. + # 5. `name` must be a SINGLE ATOMIC SEGMENT; hierarchy belongs in + # `namespace` (mcpp SPEC-001 §3.2). The legacy fully-qualified + # spelling stays accepted. Cheap second gate: it runs before the + # pinned mcpp is even downloaded, and mcpp >= 0.0.106 enforces + # the same rule inside `mcpp xpkg parse`. if ! lua5.4 tests/check_package_name.lua "$f"; then fail=1 fi @@ -207,21 +214,21 @@ jobs: ext: tar.gz mcpp: bin/mcpp xlings: registry/bin/xlings - mcpp_version: "0.0.102" # keep in sync with env.MCPP_VERSION + mcpp_version: "0.0.106" # keep in sync with env.MCPP_VERSION - platform: macos os: macos-15 suffix: macosx-arm64 ext: tar.gz mcpp: bin/mcpp xlings: registry/bin/xlings - mcpp_version: "0.0.102" # keep in sync with env.MCPP_VERSION + mcpp_version: "0.0.106" # keep in sync with env.MCPP_VERSION - platform: windows os: windows-latest suffix: windows-x86_64 ext: zip mcpp: bin/mcpp.exe xlings: registry/bin/xlings.exe - mcpp_version: "0.0.102" # keep in sync with env.MCPP_VERSION + mcpp_version: "0.0.106" # keep in sync with env.MCPP_VERSION env: MCPP_EFFECTIVE: ${{ matrix.mcpp_version }} steps: diff --git a/docs/repository-and-schema.md b/docs/repository-and-schema.md index b35c72b..4bca5b9 100644 --- a/docs/repository-and-schema.md +++ b/docs/repository-and-schema.md @@ -13,6 +13,7 @@ tests/examples// 每库测试工程(workspace 成员; 为包 ([target.'cfg(...)']) tests/*.cpp 行为断言(独立 main,退出码非 0 即失败) tests/check_mirror_urls.lua lint:GLOBAL+CN 表完整性,以及 CN 指向 mcpp-res +tests/check_package_name.lua lint:身份形态(name 为单一原子段,层级归 namespace) tests/list_cn_urls.lua 抽取 CN url,供 mirror-cn-reachable 使用 README.md 索引说明与贡献入口 .github/workflows/validate.yml CI:lint / mirror-cn-reachable / workspace(3 平台矩阵) @@ -37,6 +38,24 @@ tools/compat-ffmpeg/ 等 compat 大包的描述符再生成流水线 `package` 必填字段:`spec`、`namespace`、`name`、`description`、`licenses`、`repo`、`type="package"`、`xpm`、`mcpp`。 +### 包身份:`(namespace, name)` + +身份是二元组 —— **`namespace` 是点分层级路径,`name` 是单一原子段**。层级一律放 `namespace`(mcpp SPEC-001 §3.2,`docs/spec/package-identity.md`): + +```lua +namespace = "compat", name = "zlib" -- ✅ +namespace = "mcpplibs.capi", name = "lua" -- ✅ 多级命名空间 +namespace = "mcpplibs", name = "capi.lua" -- ❌ 短名仍带点 +``` + +最后一种被拒绝而非重新解读:`name` 里多出的点描述的是一个**没人声明过的命名空间**。mcpp 曾按最后一个点切分、静默造出 `(mcpplibs.capi, lua)`,0.0.106 起改为拒绝。 + +**兼容形态**:SPEC-001 之前发布的描述符把命名空间重复写在 `name` 里(`namespace="compat", name="compat.zlib"`),仍被接受 —— 前缀会先剥离再判定,wire key 是字面 `name`,两种写法都可安装。本仓已统一迁到短名形态。 + +**同短名不同命名空间可共存**:本仓现有三对 —— `compat:imgui` 与默认命名空间的 `imgui`、`compat:ffmpeg` 与 `ffmpeg`、`compat:lua` 与 `mcpplibs.capi:lua`。需要 xlings ≥ 0.4.69([xlings#381](https://github.com/openxlings/xlings/issues/381));`(namespace, name)` 唯一即可,`name` 本身不必唯一。 + +**文件名不参与解析**,可以任意。推荐 `.lua` 或 `..lua`(命中 mcpp 的快路径),但描述符按**声明的身份**被发现,叫别的名字也能解析。 + `xpm..<裸版本>`: - `url`:字符串,或 `{ GLOBAL=…, CN=… }` 表(本仓统一使用表形式)。 @@ -76,7 +95,9 @@ mcpp 跑 `xpkg parse`(strict:未知键即失败),所以需要更新文法/键的 - 触发条件:PR(改动 `pkgs/**/*.lua`、`tests/**`、`README.md` 或本 workflow)、push 至 main、nightly cron、手动触发。 - `env.MCPP_VERSION` 为全部 job 使用的 mcpp 版本,本地验证应与之对齐。 - `lint`(始终运行):lua 语法 `loadfile(f,'t')`;须含 `spec=`/`name=`/`xpm=`;禁止前导 v 版本;执行 - `check_mirror_urls.lua`;再用 CI pin 的 mcpp 对每个描述符跑 `mcpp xpkg parse`(strict,未知键即失败)。 + `check_mirror_urls.lua`;执行 `check_package_name.lua`(身份形态,见上文「包身份」);再用 CI pin 的 + mcpp 对每个描述符跑 `mcpp xpkg parse`(strict,未知键即失败)。mcpp ≥ 0.0.106 的 `xpkg parse` 自身 + 也强制身份形态,lua lint 因此是更早、更便宜的冗余闸门。 - `mirror-cn-reachable`(始终运行):逐个 `curl` CN url,均须返回 200。 - `workspace (linux|macos|windows)`:整个测试面就是一个 mcpp workspace,**唯一的构建/运行通道**—— 没有任何 shell 驱动的例外(公开模块包 imgui/ffmpeg/opencv/tinyhttps 也是普通成员,经成员级 @@ -96,6 +117,7 @@ for f in pkgs/*/*.lua; do for n in 'spec *=' 'name *=' 'xpm *='; do grep -q "$n" "$f" || { echo "MISS $n $f"; fail=1; }; done grep -nqE '\["v[0-9]+|\["[^"]+"\][[:space:]]*=[[:space:]]*"v[0-9]+' "$f" && { echo "LEADING-V $f"; fail=1; } lua5.4 tests/check_mirror_urls.lua "$f" >/dev/null 2>&1 || { echo "MIRROR $f"; fail=1; } + lua5.4 tests/check_package_name.lua "$f" || fail=1 done [ $fail -eq 0 ] && echo "ALL LINT PASS" ``` diff --git a/index.toml b/index.toml index 01fb233..d186557 100644 --- a/index.toml +++ b/index.toml @@ -7,5 +7,5 @@ # "floor first, new grammar after" rollout rule mechanically. [index] spec = "1" -min_mcpp = "0.0.102" -latest_mcpp = "0.0.102" +min_mcpp = "0.0.106" +latest_mcpp = "0.0.106" diff --git a/pkgs/c/chriskohlhoff.asio.lua b/pkgs/c/chriskohlhoff.asio.lua index bed725c..59532bb 100644 --- a/pkgs/c/chriskohlhoff.asio.lua +++ b/pkgs/c/chriskohlhoff.asio.lua @@ -46,7 +46,7 @@ package = { -- identity normalizes both spellings) but registers the index entry under -- `asio`, which no consumer request can ever hit → E_NOT_FOUND at install. -- See mcpp-community/mcpp#278; the lint in validate.yml enforces this. - name = "chriskohlhoff.asio", + name = "asio", description = "Standalone asio exposed as the C++23 module `asio` (separate compilation)", licenses = {"BSL-1.0"}, repo = "https://github.com/chriskohlhoff/asio", diff --git a/pkgs/c/cmdline.lua b/pkgs/c/cmdline.lua index 08aa79c..b6fd766 100644 --- a/pkgs/c/cmdline.lua +++ b/pkgs/c/cmdline.lua @@ -11,7 +11,7 @@ package = { spec = "1", namespace = "mcpplibs", - name = "mcpplibs.cmdline", + name = "cmdline", description = "A simple command-line parsing library/framework for modern C++", licenses = {"Apache-2.0"}, repo = "https://github.com/mcpplibs/cmdline", diff --git a/pkgs/c/compat.bzip2.lua b/pkgs/c/compat.bzip2.lua index ce81902..60d886e 100644 --- a/pkgs/c/compat.bzip2.lua +++ b/pkgs/c/compat.bzip2.lua @@ -1,7 +1,7 @@ package = { spec = "1", namespace = "compat", - name = "compat.bzip2", + name = "bzip2", description = "A freely available high-quality data compressor", licenses = {"bzip2-1.0.6"}, repo = "https://sourceware.org/bzip2/", diff --git a/pkgs/c/compat.cjson.lua b/pkgs/c/compat.cjson.lua index f8c1e49..38a7d04 100644 --- a/pkgs/c/compat.cjson.lua +++ b/pkgs/c/compat.cjson.lua @@ -11,7 +11,7 @@ package = { spec = "1", namespace = "compat", - name = "compat.cjson", + name = "cjson", description = "Ultralightweight JSON parser in ANSI C", licenses = {"MIT"}, repo = "https://github.com/DaveGamble/cJSON", diff --git a/pkgs/c/compat.eigen.lua b/pkgs/c/compat.eigen.lua index 2edfc13..87c1d2d 100644 --- a/pkgs/c/compat.eigen.lua +++ b/pkgs/c/compat.eigen.lua @@ -63,7 +63,7 @@ package = { spec = "1", namespace = "compat", - name = "compat.eigen", + name = "eigen", description = "C++ template library for linear algebra (header-only)", licenses = {"MPL-2.0"}, repo = "https://gitlab.com/libeigen/eigen", diff --git a/pkgs/c/compat.ffmpeg.lua b/pkgs/c/compat.ffmpeg.lua index 3f1cd71..df08cce 100644 --- a/pkgs/c/compat.ffmpeg.lua +++ b/pkgs/c/compat.ffmpeg.lua @@ -7,7 +7,7 @@ -- include_dirs_after (-idirafter, mcpp#249) so libc++ is not shadowed -- by ffmpeg's VERSION file on case-insensitive macOS. package = { - spec = "1", namespace = "compat", name = "compat.ffmpeg", + spec = "1", namespace = "compat", name = "ffmpeg", description = "FFmpeg 8.1.2 multimedia libraries, full source build (LGPL profile, multi-platform)", licenses = {"LGPL-2.1-or-later"}, repo = "https://ffmpeg.org", type = "package", xpm = { diff --git a/pkgs/c/compat.ftxui.lua b/pkgs/c/compat.ftxui.lua index 577a258..bcb180b 100644 --- a/pkgs/c/compat.ftxui.lua +++ b/pkgs/c/compat.ftxui.lua @@ -11,7 +11,7 @@ package = { spec = "1", namespace = "compat", - name = "compat.ftxui", + name = "ftxui", description = "C++ Functional Terminal User Interface (screen + dom + component)", licenses = {"MIT"}, repo = "https://github.com/ArthurSonzogni/FTXUI", diff --git a/pkgs/c/compat.glfw.lua b/pkgs/c/compat.glfw.lua index a744bfd..e4ef66f 100644 --- a/pkgs/c/compat.glfw.lua +++ b/pkgs/c/compat.glfw.lua @@ -1,7 +1,7 @@ package = { spec = "1", namespace = "compat", - name = "compat.glfw", + name = "glfw", description = "GLFW windowing and input library built from upstream sources", licenses = {"Zlib"}, repo = "https://github.com/glfw/glfw", diff --git a/pkgs/c/compat.glx-runtime.lua b/pkgs/c/compat.glx-runtime.lua index 1219f7a..2f70e56 100644 --- a/pkgs/c/compat.glx-runtime.lua +++ b/pkgs/c/compat.glx-runtime.lua @@ -1,7 +1,7 @@ package = { spec = "1", namespace = "compat", - name = "compat.glx-runtime", + name = "glx-runtime", description = "Host GLVND/GLX/OpenGL runtime adapter for mcpp Linux window applications", licenses = {"MIT"}, repo = "https://github.com/KhronosGroup/OpenGL-Registry", diff --git a/pkgs/c/compat.gtest.lua b/pkgs/c/compat.gtest.lua index 610e0ae..5b468e8 100644 --- a/pkgs/c/compat.gtest.lua +++ b/pkgs/c/compat.gtest.lua @@ -6,7 +6,7 @@ package = { spec = "1", namespace = "compat", - name = "compat.gtest", + name = "gtest", description = "Google's C++ test framework", licenses = {"BSD-3-Clause"}, repo = "https://github.com/google/googletest", diff --git a/pkgs/c/compat.imgui.lua b/pkgs/c/compat.imgui.lua index 63aa3f5..0c4a37e 100644 --- a/pkgs/c/compat.imgui.lua +++ b/pkgs/c/compat.imgui.lua @@ -1,7 +1,7 @@ package = { spec = "1", namespace = "compat", - name = "compat.imgui", + name = "imgui", description = "Dear ImGui immediate-mode GUI library core sources", licenses = {"MIT"}, repo = "https://github.com/ocornut/imgui", diff --git a/pkgs/c/compat.khrplatform.lua b/pkgs/c/compat.khrplatform.lua index 214ae77..7eb4b26 100644 --- a/pkgs/c/compat.khrplatform.lua +++ b/pkgs/c/compat.khrplatform.lua @@ -1,7 +1,7 @@ package = { spec = "1", namespace = "compat", - name = "compat.khrplatform", + name = "khrplatform", description = "Khronos KHR platform header for OpenGL/EGL compat packages", licenses = {"Khronos"}, repo = "https://github.com/KhronosGroup/EGL-Registry", diff --git a/pkgs/c/compat.libarchive.lua b/pkgs/c/compat.libarchive.lua index bea9dd1..7e69cf6 100644 --- a/pkgs/c/compat.libarchive.lua +++ b/pkgs/c/compat.libarchive.lua @@ -1,7 +1,7 @@ package = { spec = "1", namespace = "compat", - name = "compat.libarchive", + name = "libarchive", description = "Multi-format archive and compression library", licenses = {"BSD-2-Clause", "BSD-3-Clause", "BSD-2-Clause-NetBSD", "CC0-1.0", "OpenSSL", "Apache-2.0", "public-domain"}, repo = "https://libarchive.org/", diff --git a/pkgs/c/compat.lua.lua b/pkgs/c/compat.lua.lua index 32c9770..6f16975 100644 --- a/pkgs/c/compat.lua.lua +++ b/pkgs/c/compat.lua.lua @@ -12,7 +12,7 @@ package = { spec = "1", namespace = "compat", - name = "compat.lua", + name = "lua", description = "A powerful, efficient, lightweight, embeddable scripting language", licenses = {"MIT"}, repo = "https://www.lua.org", diff --git a/pkgs/c/compat.lz4.lua b/pkgs/c/compat.lz4.lua index 74b8732..d787fb2 100644 --- a/pkgs/c/compat.lz4.lua +++ b/pkgs/c/compat.lz4.lua @@ -1,7 +1,7 @@ package = { spec = "1", namespace = "compat", - name = "compat.lz4", + name = "lz4", description = "LZ4 extremely fast compression", licenses = {"BSD-2-Clause"}, repo = "https://github.com/lz4/lz4", diff --git a/pkgs/c/compat.mbedtls.lua b/pkgs/c/compat.mbedtls.lua index 1dc9dc8..18d87a2 100644 --- a/pkgs/c/compat.mbedtls.lua +++ b/pkgs/c/compat.mbedtls.lua @@ -9,7 +9,7 @@ package = { spec = "1", namespace = "compat", - name = "compat.mbedtls", + name = "mbedtls", description = "An open source, portable, easy to use, readable and flexible TLS library, and reference implementation of the PSA Cryptography API", licenses = {"Apache-2.0"}, repo = "https://github.com/Mbed-TLS/mbedtls", diff --git a/pkgs/c/compat.openblas.lua b/pkgs/c/compat.openblas.lua index b0cb64e..5ce8d9c 100644 --- a/pkgs/c/compat.openblas.lua +++ b/pkgs/c/compat.openblas.lua @@ -33,7 +33,7 @@ package = { spec = "1", namespace = "compat", - name = "compat.openblas", + name = "openblas", description = "OpenBLAS — optimized BLAS, built from source (BLAS-only, no Fortran/LAPACK)", licenses = {"BSD-3-Clause"}, repo = "https://github.com/OpenMathLib/OpenBLAS", diff --git a/pkgs/c/compat.opengl.lua b/pkgs/c/compat.opengl.lua index d2e806b..b1b8cbe 100644 --- a/pkgs/c/compat.opengl.lua +++ b/pkgs/c/compat.opengl.lua @@ -1,7 +1,7 @@ package = { spec = "1", namespace = "compat", - name = "compat.opengl", + name = "opengl", description = "Khronos OpenGL API headers for mcpp packages", licenses = {"MIT"}, repo = "https://github.com/KhronosGroup/OpenGL-Registry", diff --git a/pkgs/c/compat.spdlog.lua b/pkgs/c/compat.spdlog.lua index 66d6838..16e2eb0 100644 --- a/pkgs/c/compat.spdlog.lua +++ b/pkgs/c/compat.spdlog.lua @@ -43,7 +43,7 @@ package = { spec = "1", namespace = "compat", - name = "compat.spdlog", + name = "spdlog", description = "Fast C++ logging library (header-only by default, compiled via the `compiled` feature)", licenses = {"MIT"}, repo = "https://github.com/gabime/spdlog", diff --git a/pkgs/c/compat.x11.lua b/pkgs/c/compat.x11.lua index 6da6912..74fbced 100644 --- a/pkgs/c/compat.x11.lua +++ b/pkgs/c/compat.x11.lua @@ -1,7 +1,7 @@ package = { spec = "1", namespace = "compat", - name = "compat.x11", + name = "x11", description = "Xlib runtime library and public headers built from upstream sources", licenses = {"BSD-1-Clause", "HPND-sell-variant", "ISC", "MIT", "MIT-Open-Group", "X11"}, repo = "https://gitlab.freedesktop.org/xorg/lib/libx11", diff --git a/pkgs/c/compat.xau.lua b/pkgs/c/compat.xau.lua index b6bcb76..ef51db6 100644 --- a/pkgs/c/compat.xau.lua +++ b/pkgs/c/compat.xau.lua @@ -1,7 +1,7 @@ package = { spec = "1", namespace = "compat", - name = "compat.xau", + name = "xau", description = "X authorization file management library", licenses = {"MIT-Open-Group"}, repo = "https://gitlab.freedesktop.org/xorg/lib/libxau", diff --git a/pkgs/c/compat.xcb-proto.lua b/pkgs/c/compat.xcb-proto.lua index 64e1fd7..a5ddab0 100644 --- a/pkgs/c/compat.xcb-proto.lua +++ b/pkgs/c/compat.xcb-proto.lua @@ -1,7 +1,7 @@ package = { spec = "1", namespace = "compat", - name = "compat.xcb-proto", + name = "xcb-proto", description = "XCB protocol XML descriptions and xcbgen generator", licenses = {"MIT"}, repo = "https://gitlab.freedesktop.org/xorg/proto/xcbproto", diff --git a/pkgs/c/compat.xcb.lua b/pkgs/c/compat.xcb.lua index 964a3af..8673dc8 100644 --- a/pkgs/c/compat.xcb.lua +++ b/pkgs/c/compat.xcb.lua @@ -1,7 +1,7 @@ package = { spec = "1", namespace = "compat", - name = "compat.xcb", + name = "xcb", description = "X C Binding core library built from upstream sources", licenses = {"MIT"}, repo = "https://gitlab.freedesktop.org/xorg/lib/libxcb", diff --git a/pkgs/c/compat.xcursor.lua b/pkgs/c/compat.xcursor.lua index de7daa1..4b45151 100644 --- a/pkgs/c/compat.xcursor.lua +++ b/pkgs/c/compat.xcursor.lua @@ -1,7 +1,7 @@ package = { spec = "1", namespace = "compat", - name = "compat.xcursor", + name = "xcursor", description = "X cursor management runtime library and public headers", licenses = {"HPND-sell-variant"}, repo = "https://gitlab.freedesktop.org/xorg/lib/libxcursor", diff --git a/pkgs/c/compat.xdmcp.lua b/pkgs/c/compat.xdmcp.lua index 3364efe..4d27967 100644 --- a/pkgs/c/compat.xdmcp.lua +++ b/pkgs/c/compat.xdmcp.lua @@ -1,7 +1,7 @@ package = { spec = "1", namespace = "compat", - name = "compat.xdmcp", + name = "xdmcp", description = "X Display Manager Control Protocol library", licenses = {"MIT-Open-Group"}, repo = "https://gitlab.freedesktop.org/xorg/lib/libxdmcp", diff --git a/pkgs/c/compat.xext.lua b/pkgs/c/compat.xext.lua index 8d648b2..9484643 100644 --- a/pkgs/c/compat.xext.lua +++ b/pkgs/c/compat.xext.lua @@ -1,7 +1,7 @@ package = { spec = "1", namespace = "compat", - name = "compat.xext", + name = "xext", description = "X11 miscellaneous extensions runtime library and public headers", licenses = {"HPND-sell-variant", "ISC", "MIT", "MIT-Open-Group"}, repo = "https://gitlab.freedesktop.org/xorg/lib/libxext", diff --git a/pkgs/c/compat.xfixes.lua b/pkgs/c/compat.xfixes.lua index 98c5f79..3a582d4 100644 --- a/pkgs/c/compat.xfixes.lua +++ b/pkgs/c/compat.xfixes.lua @@ -1,7 +1,7 @@ package = { spec = "1", namespace = "compat", - name = "compat.xfixes", + name = "xfixes", description = "X Fixes extension runtime library and public headers", licenses = {"HPND-sell-variant"}, repo = "https://gitlab.freedesktop.org/xorg/lib/libxfixes", diff --git a/pkgs/c/compat.xi.lua b/pkgs/c/compat.xi.lua index 56698f3..ce98ea8 100644 --- a/pkgs/c/compat.xi.lua +++ b/pkgs/c/compat.xi.lua @@ -1,7 +1,7 @@ package = { spec = "1", namespace = "compat", - name = "compat.xi", + name = "xi", description = "X Input extension runtime library and public headers", licenses = {"MIT-Open-Group"}, repo = "https://gitlab.freedesktop.org/xorg/lib/libxi", diff --git a/pkgs/c/compat.xinerama.lua b/pkgs/c/compat.xinerama.lua index 04b0110..8e01473 100644 --- a/pkgs/c/compat.xinerama.lua +++ b/pkgs/c/compat.xinerama.lua @@ -1,7 +1,7 @@ package = { spec = "1", namespace = "compat", - name = "compat.xinerama", + name = "xinerama", description = "Xinerama extension runtime library and public headers", licenses = {"MIT-Open-Group"}, repo = "https://gitlab.freedesktop.org/xorg/lib/libxinerama", diff --git a/pkgs/c/compat.xorgproto.lua b/pkgs/c/compat.xorgproto.lua index 7070e5d..fe538ce 100644 --- a/pkgs/c/compat.xorgproto.lua +++ b/pkgs/c/compat.xorgproto.lua @@ -1,7 +1,7 @@ package = { spec = "1", namespace = "compat", - name = "compat.xorgproto", + name = "xorgproto", description = "X.Org protocol headers for X11-based compat packages", licenses = {}, repo = "https://gitlab.freedesktop.org/xorg/proto/xorgproto", diff --git a/pkgs/c/compat.xrandr.lua b/pkgs/c/compat.xrandr.lua index 47d9693..dcce36e 100644 --- a/pkgs/c/compat.xrandr.lua +++ b/pkgs/c/compat.xrandr.lua @@ -1,7 +1,7 @@ package = { spec = "1", namespace = "compat", - name = "compat.xrandr", + name = "xrandr", description = "X Resize, Rotate and Reflect extension runtime library and public headers", licenses = {"HPND-sell-variant"}, repo = "https://gitlab.freedesktop.org/xorg/lib/libxrandr", diff --git a/pkgs/c/compat.xrender.lua b/pkgs/c/compat.xrender.lua index 2728664..e0beffd 100644 --- a/pkgs/c/compat.xrender.lua +++ b/pkgs/c/compat.xrender.lua @@ -1,7 +1,7 @@ package = { spec = "1", namespace = "compat", - name = "compat.xrender", + name = "xrender", description = "X Rendering extension runtime library and public headers", licenses = {"HPND-sell-variant"}, repo = "https://gitlab.freedesktop.org/xorg/lib/libxrender", diff --git a/pkgs/c/compat.xtrans.lua b/pkgs/c/compat.xtrans.lua index 1a49e19..8fac7a9 100644 --- a/pkgs/c/compat.xtrans.lua +++ b/pkgs/c/compat.xtrans.lua @@ -1,7 +1,7 @@ package = { spec = "1", namespace = "compat", - name = "compat.xtrans", + name = "xtrans", description = "X.Org transport layer support headers used by Xlib", licenses = {"HPND-sell-variant", "MIT", "MIT-Open-Group"}, repo = "https://gitlab.freedesktop.org/xorg/lib/libxtrans", diff --git a/pkgs/c/compat.xz.lua b/pkgs/c/compat.xz.lua index ecf0317..979e12b 100644 --- a/pkgs/c/compat.xz.lua +++ b/pkgs/c/compat.xz.lua @@ -1,7 +1,7 @@ package = { spec = "1", namespace = "compat", - name = "compat.xz", + name = "xz", description = "XZ Utils liblzma compression library", licenses = {"0BSD"}, repo = "https://github.com/tukaani-project/xz", diff --git a/pkgs/c/compat.zlib.lua b/pkgs/c/compat.zlib.lua index 36cc834..548d2a2 100644 --- a/pkgs/c/compat.zlib.lua +++ b/pkgs/c/compat.zlib.lua @@ -1,7 +1,7 @@ package = { spec = "1", namespace = "compat", - name = "compat.zlib", + name = "zlib", description = "A compression library", licenses = {"Zlib"}, repo = "https://github.com/madler/zlib", diff --git a/pkgs/c/compat.zstd.lua b/pkgs/c/compat.zstd.lua index d75014c..94344f0 100644 --- a/pkgs/c/compat.zstd.lua +++ b/pkgs/c/compat.zstd.lua @@ -1,7 +1,7 @@ package = { spec = "1", namespace = "compat", - name = "compat.zstd", + name = "zstd", description = "Zstandard real-time compression algorithm", licenses = {"BSD-3-Clause", "GPL-2.0-only"}, repo = "https://github.com/facebook/zstd", diff --git a/pkgs/f/fmtlib.fmt.lua b/pkgs/f/fmtlib.fmt.lua index be3875d..c478fd2 100644 --- a/pkgs/f/fmtlib.fmt.lua +++ b/pkgs/f/fmtlib.fmt.lua @@ -42,7 +42,7 @@ package = { spec = "1", namespace = "fmtlib", - name = "fmtlib.fmt", + name = "fmt", description = "A modern formatting library for C++, exposed as C++23 module fmt", licenses = {"MIT"}, repo = "https://github.com/fmtlib/fmt", diff --git a/pkgs/l/llmapi.lua b/pkgs/l/llmapi.lua index 44661a5..55df7f6 100644 --- a/pkgs/l/llmapi.lua +++ b/pkgs/l/llmapi.lua @@ -4,7 +4,7 @@ package = { spec = "1", namespace = "mcpplibs", - name = "mcpplibs.llmapi", + name = "llmapi", description = "Modern C++ LLM API client with openai-compatible support", licenses = {"Apache-2.0"}, repo = "https://github.com/mcpplibs/llmapi", diff --git a/pkgs/m/marzer.tomlplusplus.lua b/pkgs/m/marzer.tomlplusplus.lua index d61e6eb..e37c240 100644 --- a/pkgs/m/marzer.tomlplusplus.lua +++ b/pkgs/m/marzer.tomlplusplus.lua @@ -29,7 +29,7 @@ package = { spec = "1", namespace = "marzer", - name = "marzer.tomlplusplus", + name = "tomlplusplus", description = "TOML config file parser and serializer for C++, exposed as C++23 module tomlplusplus", licenses = {"MIT"}, repo = "https://github.com/marzer/tomlplusplus", diff --git a/pkgs/m/mcpplibs.capi.lua.lua b/pkgs/m/mcpplibs.capi.lua.lua index ad22940..7ed9b8a 100644 --- a/pkgs/m/mcpplibs.capi.lua.lua +++ b/pkgs/m/mcpplibs.capi.lua.lua @@ -4,7 +4,7 @@ package = { spec = "1", namespace = "mcpplibs.capi", - name = "mcpplibs.capi.lua", + name = "lua", description = "C++23 module wrapping the Lua 5.4 C API — `import mcpplibs.capi.lua;`", licenses = {"Apache-2.0"}, repo = "https://github.com/mcpplibs/lua", diff --git a/pkgs/n/nlohmann.json.lua b/pkgs/n/nlohmann.json.lua index 31c14c6..d8e90d4 100644 --- a/pkgs/n/nlohmann.json.lua +++ b/pkgs/n/nlohmann.json.lua @@ -21,7 +21,7 @@ package = { spec = "1", namespace = "nlohmann", - name = "nlohmann.json", + name = "json", description = "JSON for Modern C++, exposed as C++23 module nlohmann.json", licenses = {"MIT"}, repo = "https://github.com/nlohmann/json", diff --git a/pkgs/t/templates.lua b/pkgs/t/templates.lua index 88bcf22..253567b 100644 --- a/pkgs/t/templates.lua +++ b/pkgs/t/templates.lua @@ -6,7 +6,7 @@ package = { spec = "1", namespace = "mcpplibs", - name = "mcpplibs.templates", + name = "templates", description = "Minimal C++23 modular hello library", licenses = {"Apache-2.0"}, repo = "https://github.com/mcpp-community/templates", diff --git a/pkgs/t/tensorvia-cpu.lua b/pkgs/t/tensorvia-cpu.lua index e7ada8b..fd0e9a8 100644 --- a/pkgs/t/tensorvia-cpu.lua +++ b/pkgs/t/tensorvia-cpu.lua @@ -9,7 +9,7 @@ -- the fully-qualified spelling. See mcpp-community/mcpp#278. package = { spec = "1", - name = "aimol.tensorvia-cpu", + name = "tensorvia-cpu", description = "CPU backend of Tensorvia, ported to C++23 modules", licenses = {"MIT"}, repo = "https://github.com/Aimol-l/Tensorvia-cpu", diff --git a/pkgs/t/tinyhttps.lua b/pkgs/t/tinyhttps.lua index 2dd213e..d5b7399 100644 --- a/pkgs/t/tinyhttps.lua +++ b/pkgs/t/tinyhttps.lua @@ -4,7 +4,7 @@ package = { spec = "1", namespace = "mcpplibs", - name = "mcpplibs.tinyhttps", + name = "tinyhttps", description = "Minimal C++23 HTTP/HTTPS client with SSE streaming support", licenses = {"Apache-2.0"}, repo = "https://github.com/mcpplibs/tinyhttps", diff --git a/pkgs/x/xpkg.lua b/pkgs/x/xpkg.lua index 6cfc42a..f70716d 100644 --- a/pkgs/x/xpkg.lua +++ b/pkgs/x/xpkg.lua @@ -4,7 +4,7 @@ package = { spec = "1", namespace = "mcpplibs", - name = "mcpplibs.xpkg", + name = "xpkg", description = "C++23 reference implementation of the xpkg V1 spec — `import mcpplibs.xpkg;`", licenses = {"Apache-2.0"}, repo = "https://github.com/openxlings/libxpkg", diff --git a/tests/check_package_name.lua b/tests/check_package_name.lua index f34f783..75c5116 100644 --- a/tests/check_package_name.lua +++ b/tests/check_package_name.lua @@ -1,29 +1,32 @@ --- Lint `package.name` against `package.namespace`. +-- Lint `package.name` against `package.namespace` (mcpp SPEC-001 §3.2). -- --- Rule: a namespaced descriptor MUST spell `name` as the fully-qualified --- `.`. The split form (namespace = "chriskohlhoff", --- name = "asio") parses fine and passes `mcpp xpkg parse` — mcpp's own --- identity layer normalizes both spellings to the same package — but it is --- NOT installable from an index: +-- Identity is the pair `(namespace, name)`: `namespace` is the dotted, +-- hierarchical path and `name` is a SINGLE ATOMIC SEGMENT. All depth belongs +-- in the namespace. -- --- * xlings/libxpkg keys the index on the literal `package.name` --- (libxpkg build_index → entries[package.name]), so the entry lands --- under `asio`; --- * mcpp asks xlings for the FQN it reconstructs from the consumer's --- `[dependencies.] `, i.e. `chriskohlhoff.asio` --- (mcpp src/build/prepare.cppm — "xlings resolves packages by the full --- qualified name (ns.shortName) as it appears in the index's name field"). +-- namespace = "chriskohlhoff", name = "asio" -- ok +-- namespace = "mcpplibs.capi", name = "lua" -- ok +-- namespace = "compat", name = "compat.zlib" -- ok (legacy form) +-- namespace = "mcpplibs", name = "capi.lua" -- REJECTED -- --- The two never meet → E_NOT_FOUND at install time, on every platform, after --- the workspace job has already burned an hour. No consumer-side spelling can --- work around it; the descriptor is the only place it can be fixed. +-- Why the last one is rejected rather than reinterpreted: a `name` carrying +-- dots the namespace does not account for describes a package whose namespace +-- nobody declared. mcpp used to split such a name on its LAST dot and silently +-- invent `(mcpplibs.capi, lua)`. Since 0.0.106 it refuses to guess. -- --- Upstream: mcpp-community/mcpp#278 (mcpp should either use the declared name --- or reject the split form in `mcpp xpkg parse`). Until that lands, this lint --- is the index's guard. +-- LEGACY FORM: descriptors published before SPEC-001 repeat the namespace +-- inside `name`. That prefix is stripped before judging, so they keep passing — +-- the wire key is the literal `name` either way, so they stay installable +-- unchanged. -- --- Zero-namespace packages (the public default-namespace module packages — --- imgui / ffmpeg / opencv) are unaffected: their bare `name` IS the FQN. +-- HISTORY: mcpp 0.0.105 briefly required the OPPOSITE (name must be the +-- fully-qualified form). That was an encoding constraint, not a design rule: it +-- existed only because mcpp re-derived the wire name instead of using the +-- literal it had already read. See mcpp-community/mcpp#278 and mcpp +-- docs/spec/package-identity.md. +-- +-- mcpp >= 0.0.106 enforces the same rule inside `mcpp xpkg parse`; this lint is +-- a cheaper, earlier second gate that runs before the pinned mcpp is fetched. -- -- Usage: lua5.4 tests/check_package_name.lua @@ -57,21 +60,33 @@ if type(ns) ~= "string" then os.exit(fail) end +local shortName = name if ns ~= "" then local prefix = ns .. "." - if name:sub(1, #prefix) ~= prefix then - err(string.format( - "package.name must be the fully-qualified '.': " .. - "namespace = %q but name = %q — write name = %q. " .. - "The split form registers the index entry under %q, which no " .. - "consumer request can ever resolve (E_NOT_FOUND at install). " .. - "See mcpp-community/mcpp#278.", - ns, name, prefix .. name, name)) - elseif #name == #prefix then - err(string.format( - "package.name = %q has an empty short name after the %q prefix", - name, prefix)) + if name:sub(1, #prefix) == prefix then + if #name == #prefix then + err(string.format( + "package.name = %q has an empty short name after the %q prefix", + name, prefix)) + os.exit(fail) + end + -- Legacy fully-qualified spelling: judge what follows the prefix. + shortName = name:sub(#prefix + 1) end end +if shortName:find(".", 1, true) then + local head = shortName:match("^(.*)%.[^.]*$") + local tail = shortName:match("([^.]*)$") + local suggestedNs = (ns ~= "") and (ns .. "." .. head) or head + err(string.format( + "package.name must be a single atomic segment — the hierarchy belongs " .. + "in package.namespace. namespace = %q, name = %q leaves short name %q, " .. + "which still contains a '.'. Identity is (namespace, name): `namespace` " .. + "is the dotted path, `name` is ONE segment — so this names a namespace " .. + "nobody declared. Write namespace = %q, name = %q. See mcpp " .. + "docs/spec/package-identity.md §3.2.", + ns, name, shortName, suggestedNs, tail)) +end + os.exit(fail) From 677d5039168ed584a84fd9520215ac7cdaa87475 Mon Sep 17 00:00:00 2001 From: sunrisepeak Date: Sat, 25 Jul 2026 19:39:49 +0800 Subject: [PATCH 2/6] fix: declare mcpplibs namespace explicitly on the public module packages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CI caught a real conflict the short-name migration introduced: build_index failed: duplicate package identity 'compat:ffmpeg' `imgui`, `ffmpeg` and `opencv` declared `namespace = ""`, meaning "inherit the index's default namespace". 11 workspace members register this repo as `[indices] compat = { path = ... }`, so under those members the repo default IS `compat` — and the namespace-less descriptors became `compat:ffmpeg` / `compat:imgui`, colliding head-on with the freshly shortened `compat.ffmpeg` and `compat.imgui`. The migration exposed it, but the fragility is older and independent of it: a namespace-less descriptor's identity depends on the NAME THE CONSUMER HAPPENS TO REGISTER THE INDEX UNDER. The same repo mounted as `compat` and as `default` gives those three packages two different identities. That is exactly what SPEC-001 exists to remove — identity should be what the descriptor declares. So rather than reverting the two colliding descriptors to the legacy spelling (which would have unblocked CI while leaving the trap armed), declare the namespace outright: namespace = "", name = "imgui" -> namespace = "mcpplibs" Identity is now context-free. Verified by simulating all three registration styles (repo default = mcpplibs / compat / default): zero duplicate identities in every case, where before `compat` produced two. Consumers are unaffected: a bare `imgui` still resolves via the default-namespace search path, and `[dependencies.compat] imgui` still resolves to compat's. Checked against the released mcpp 0.0.106 with the index mounted as `compat` — builds clean, no duplicate-identity error. 48/48 descriptors still pass the lua lint and `mcpp xpkg parse`. --- pkgs/f/ffmpeg.lua | 2 +- pkgs/i/imgui.lua | 2 +- pkgs/o/opencv.lua | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pkgs/f/ffmpeg.lua b/pkgs/f/ffmpeg.lua index 32ac725..46a0cb3 100644 --- a/pkgs/f/ffmpeg.lua +++ b/pkgs/f/ffmpeg.lua @@ -12,7 +12,7 @@ package = { spec = "1", name = "ffmpeg", - namespace = "", + namespace = "mcpplibs", description = "C++23 module package for FFmpeg (import ffmpeg.av) — full source build via compat.ffmpeg, C API unchanged", licenses = {"MIT"}, -- module layer; upstream via compat.ffmpeg is LGPL-2.1-or-later repo = "https://github.com/mcpplibs/ffmpeg-m", diff --git a/pkgs/i/imgui.lua b/pkgs/i/imgui.lua index dd1795d..ff183d3 100644 --- a/pkgs/i/imgui.lua +++ b/pkgs/i/imgui.lua @@ -5,7 +5,7 @@ package = { spec = "1", name = "imgui", - namespace = "", + namespace = "mcpplibs", description = "C++23 module package for Dear ImGui core and GLFW/OpenGL3 backends", licenses = {"MIT"}, repo = "https://github.com/mcpplibs/imgui-m", diff --git a/pkgs/o/opencv.lua b/pkgs/o/opencv.lua index 76d85cf..6f7c322 100644 --- a/pkgs/o/opencv.lua +++ b/pkgs/o/opencv.lua @@ -39,7 +39,7 @@ package = { spec = "1", name = "opencv", - namespace = "", + namespace = "mcpplibs", description = "C++23 module package for OpenCV 5 (import opencv.cv) — vendored full source build, C++ API unchanged", licenses = {"MIT"}, -- module layer; the vendored OpenCV itself is Apache-2.0 repo = "https://github.com/Sunrisepeak/opencv-m", From d3bd02b1ab4b6b80a0c7644ec53ec0fb231058f8 Mon Sep 17 00:00:00 2001 From: sunrisepeak Date: Sat, 25 Jul 2026 22:03:46 +0800 Subject: [PATCH 3/6] fix: update cross-package references left behind by the short-name migration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Shortening every `package.name` (SPEC-001) also changed how a descriptor must ADDRESS another package: `pkginfo.install_dir("compat:xcb-proto")`, not the old `compat:compat.xcb-proto`. Two install hooks kept the old spelling, and neither failed where the mistake was: * compat.xcb could not find xcb-proto's XML, so c_client.py never ran and the install aborted with a bare `[xcb] failed`. libxcb.so was never built, and CI reported it three levels downstream as `libX11.so: undefined reference to xcb_connection_has_error` in gui-stack, imgui-module and imgui-window. * compat.x11 only checked the lookup for nil, never `os.isdir`, so it carried on with a directory that does not exist and generated its keysym tables from no headers at all — silently, with a successful build. Both now try the SPEC-001 spelling first and keep the pre-SPEC-001 ones as fallbacks, so these descriptors still resolve against an unmigrated index. compat.x11 gets the same isdir guard compat.xcb already had. Add tests/check_cross_package_refs.lua to the lint job: it resolves every `pkginfo.install_dir`/`build_dep` string against the identities the repository actually declares. A reference into a foreign namespace (xim:python) is skipped, and a ladder of spellings only needs one that resolves, so legacy fallbacks stay legal. Matching is against the literal `package.name`, deliberately not a normalised form — normalising the dots away would have called the broken reference resolvable. Verified with mcpp 0.0.106 against this branch: gui-stack 5/5, imgui-module 1/1, imgui-window 1/1. Reverting the descriptor fix makes the lint flag exactly these two references and nothing else. --- .github/workflows/validate.yml | 8 ++ pkgs/c/compat.x11.lua | 12 ++- pkgs/c/compat.xcb.lua | 11 ++- tests/check_cross_package_refs.lua | 134 +++++++++++++++++++++++++++++ 4 files changed, 160 insertions(+), 5 deletions(-) create mode 100644 tests/check_cross_package_refs.lua diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index 197faf5..328e590 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -122,6 +122,14 @@ jobs: done [ $fail -eq 0 ] && echo "All package files valid." exit $fail + # ── Whole-repository check (needs every descriptor at once) ────── + # An install() hook addressing a sibling package does so by + # `:`, and a miss returns nil rather + # than raising — so a stale spelling surfaces far downstream (a broken + # libxcb showed up as a libX11 link error). Verified across the repo + # because it needs the full set of declared identities. + - name: Lint cross-package references + run: lua5.4 tests/check_cross_package_refs.lua pkgs/*/*.lua # ── Single-source-of-truth grammar check ───────────────────────── # `mcpp xpkg parse` uses EXACTLY the resolver's parser, so what # passes here is what builds for users of the pinned MCPP_VERSION. diff --git a/pkgs/c/compat.x11.lua b/pkgs/c/compat.x11.lua index 74fbced..68a163f 100644 --- a/pkgs/c/compat.x11.lua +++ b/pkgs/c/compat.x11.lua @@ -640,10 +640,16 @@ local function write_config(installdir) end local function generate_ks_tables(installdir) - local proto_dir = pkginfo.install_dir("compat:compat.xorgproto", "2025.1") + -- Addressed by declared identity `:` — see the + -- same ladder in compat.xcb.lua. The trailing spellings are pre-SPEC-001. + local proto_dir = pkginfo.install_dir("compat:xorgproto", "2025.1") + or pkginfo.install_dir("compat:compat.xorgproto", "2025.1") or pkginfo.install_dir("compat.xorgproto", "2025.1") - if not proto_dir then - log.error("compat.xorgproto@2025.1 install dir not found") + -- `os.isdir` matters as much as the nil check: a miss can hand back a + -- plausible path that was never populated, and the keysym tables would + -- then be generated empty instead of the build failing here. + if not proto_dir or not os.isdir(proto_dir) then + log.error("xorgproto@2025.1 install dir not found") return false end diff --git a/pkgs/c/compat.xcb.lua b/pkgs/c/compat.xcb.lua index 8673dc8..2f636c6 100644 --- a/pkgs/c/compat.xcb.lua +++ b/pkgs/c/compat.xcb.lua @@ -135,10 +135,17 @@ function install() os.tryrm(pkginfo.install_dir()) os.mv(srcroot, pkginfo.install_dir()) - local proto_dir = pkginfo.install_dir("compat:compat.xcb-proto", "1.17.0") + -- A cross-package lookup addresses the sibling by its DECLARED identity + -- `:`. Since SPEC-001 that is `compat:xcb-proto`; + -- the two `compat.xcb-proto` spellings below are the pre-SPEC-001 forms, + -- kept so this descriptor still resolves against an index that has not + -- migrated yet. Getting this wrong is silent until install time: the + -- lookup simply misses and codegen has no protocol XML to read. + local proto_dir = pkginfo.install_dir("compat:xcb-proto", "1.17.0") + or pkginfo.install_dir("compat:compat.xcb-proto", "1.17.0") or pkginfo.install_dir("compat.xcb-proto", "1.17.0") if not proto_dir or not os.isdir(proto_dir) then - log.error("compat.xcb-proto@1.17.0 install dir not found") + log.error("xcb-proto@1.17.0 install dir not found") return false end diff --git a/tests/check_cross_package_refs.lua b/tests/check_cross_package_refs.lua new file mode 100644 index 0000000..60504af --- /dev/null +++ b/tests/check_cross_package_refs.lua @@ -0,0 +1,134 @@ +-- Lint cross-package references inside descriptor hooks. +-- +-- An `install()` hook may address a SIBLING package to read its files: +-- +-- pkginfo.install_dir("compat:xcb-proto", "1.17.0") +-- pkginfo.build_dep("xim:python") +-- +-- The string is an ADDRESS, resolved as `:`. +-- Nothing type-checks it: a miss just returns nil, and the hook either bails +-- with a vague message or — worse — carries on with a path that was never +-- populated. Both happened for real. When SPEC-001 shortened every `name` +-- (`compat.xcb-proto` -> `xcb-proto`), two hooks kept addressing the old +-- spelling; `compat.xcb` failed to install, and `compat.x11` silently +-- generated empty keysym tables because it only checked for nil, not isdir. +-- CI saw it three levels downstream as `libX11.so: undefined reference to +-- xcb_connection_has_error`. +-- +-- So: whenever a descriptor addresses a package THIS repository publishes, +-- at least one spelling must match a declared identity. +-- +-- Two deliberate loosenings: +-- +-- * Descriptors write a LADDER of spellings — the canonical one first, then +-- pre-SPEC-001 fallbacks so the file also resolves against an index that +-- has not migrated. Only the group as a whole must resolve, never each +-- literal, or every legacy fallback would be flagged. +-- +-- * References into a namespace this repo does not publish (`xim:python`) +-- name a package in another index and cannot be checked here. If any +-- spelling in a group is foreign, the whole group is skipped — bare +-- `"python"` is a fallback for `"xim:python"`, not a local package. +-- +-- Matching mirrors what xlings actually does, and is deliberately NOT lenient: +-- `"compat:compat.xcb-proto"` must match a descriptor whose `name` is +-- literally `compat.xcb-proto`. Normalising the dots away here would have +-- called the broken reference resolvable and missed the very bug this exists +-- to catch. +-- +-- Usage: lua5.4 tests/check_cross_package_refs.lua pkgs/*/*.lua + +function import(...) + return setmetatable({}, {__index = function() return function() end end}) +end + +local files = {} +for i = 1, #arg do files[#files + 1] = arg[i] end +if #files == 0 then + io.stderr:write("usage: check_cross_package_refs.lua ...\n") + os.exit(2) +end + +-- ── Pass 1: every identity this repository declares ────────────────── +local declared = {} -- [":"] = true +local byName = {} -- [""] = true (bare addressing) +local ownedNs = {} -- [""] = true + +for _, f in ipairs(files) do + package = nil + local chunk = loadfile(f, "t") + if chunk then + -- `package = { ... }` is the first statement, so it is populated even + -- if some later top-level line blows up under the stubbed `import`. + pcall(chunk) + local p = package + if type(p) == "table" and type(p.name) == "string" then + local ns = type(p.namespace) == "string" and p.namespace or "" + declared[ns .. ":" .. p.name] = true + byName[p.name] = true + if ns ~= "" then ownedNs[ns] = true end + end + end +end + +-- ── Pass 2: the references, grouped by the package they address ────── +local fail = 0 +local function err(file, msg) + io.stderr:write(string.format("::error file=%s::%s\n", file, msg)) + fail = 1 +end + +for _, f in ipairs(files) do + local src = io.open(f, "r") + if src then + local text = src:read("a") + src:close() + + -- group key -> { foreign = bool, resolved = bool, spellings = {...} } + local groups, order = {}, {} + for fn, ref in text:gmatch("pkginfo%.([%w_]+)%s*%(%s*[\"']([^\"']+)[\"']") do + if fn == "install_dir" or fn == "build_dep" or fn == "dep_dir" then + local ns, rest = ref:match("^([^:]+):(.+)$") + local resolved, foreign + if ns then + foreign = not ownedNs[ns] + resolved = declared[ns .. ":" .. rest] == true + else + -- Bare address: no namespace stated, so it can only be + -- matched against literal names. + foreign = false + resolved = byName[ref] == true + end + + -- The addressed package, independent of spelling: drop the + -- namespace prefix and any legacy dotted lead-in. + local key = (rest or ref):match("([^.]+)$") + local g = groups[key] + if not g then + g = { foreign = false, resolved = false, spellings = {} } + groups[key] = g + order[#order + 1] = key + end + g.foreign = g.foreign or foreign + g.resolved = g.resolved or resolved + g.spellings[#g.spellings + 1] = ref + end + end + + for _, key in ipairs(order) do + local g = groups[key] + if not g.foreign and not g.resolved then + err(f, string.format( + "cross-package reference to %q resolves to no declared " .. + "identity. Tried: %s. An address is `:` — check the target descriptor's `name` " .. + "(SPEC-001 shortened these) and put the current spelling " .. + "first, keeping older ones as `or` fallbacks. See mcpp " .. + "docs/spec/package-identity.md §6.", + key, table.concat(g.spellings, ", "))) + end + end + end +end + +os.exit(fail) From 6ba8e751532dcf529e288fd2311cf8aa7e29c868 Mon Sep 17 00:00:00 2001 From: sunrisepeak Date: Sun, 26 Jul 2026 00:35:07 +0800 Subject: [PATCH 4/6] chore: re-pin to mcpp 0.0.107 (soname alias fix, mcpp#281) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 0.0.104-0.0.106 skipped the SONAME alias whenever ninja was given explicit goal targets, which is exactly how `mcpp test` drives a build — so gui-stack, imgui-module and imgui-window failed here on any of those versions, with or without this branch's descriptor changes (probe PRs #121 and #122 reproduced it over unmodified descriptors). --- .github/workflows/validate.yml | 12 ++++++------ index.toml | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index 328e590..76ce361 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -11,7 +11,7 @@ on: workflow_dispatch: env: - # 0.0.106: SPEC-001 package identity (mcpp#280). `package.name` is a SINGLE + # 0.0.107: SPEC-001 package identity (mcpp#280). `package.name` is a SINGLE # ATOMIC SEGMENT — all hierarchy lives in `package.namespace` — and mcpp # addresses a package by the LITERAL name it read, so descriptors no longer # repeat their namespace inside `name`. This index is migrated to the short @@ -57,7 +57,7 @@ env: # 0.0.94 fixed feature-gated `sources` under `mcpp test` (mcpp#218); 0.0.91 # added standard = "c++fly" to the resolver grammar, so c++fly descriptors # get the lint WARN below, not a hard grammar-parse rejection. - MCPP_VERSION: "0.0.106" + MCPP_VERSION: "0.0.107" jobs: lint: @@ -103,7 +103,7 @@ jobs: # 5. `name` must be a SINGLE ATOMIC SEGMENT; hierarchy belongs in # `namespace` (mcpp SPEC-001 §3.2). The legacy fully-qualified # spelling stays accepted. Cheap second gate: it runs before the - # pinned mcpp is even downloaded, and mcpp >= 0.0.106 enforces + # pinned mcpp is even downloaded, and mcpp >= 0.0.107 enforces # the same rule inside `mcpp xpkg parse`. if ! lua5.4 tests/check_package_name.lua "$f"; then fail=1 @@ -222,21 +222,21 @@ jobs: ext: tar.gz mcpp: bin/mcpp xlings: registry/bin/xlings - mcpp_version: "0.0.106" # keep in sync with env.MCPP_VERSION + mcpp_version: "0.0.107" # keep in sync with env.MCPP_VERSION - platform: macos os: macos-15 suffix: macosx-arm64 ext: tar.gz mcpp: bin/mcpp xlings: registry/bin/xlings - mcpp_version: "0.0.106" # keep in sync with env.MCPP_VERSION + mcpp_version: "0.0.107" # keep in sync with env.MCPP_VERSION - platform: windows os: windows-latest suffix: windows-x86_64 ext: zip mcpp: bin/mcpp.exe xlings: registry/bin/xlings.exe - mcpp_version: "0.0.106" # keep in sync with env.MCPP_VERSION + mcpp_version: "0.0.107" # keep in sync with env.MCPP_VERSION env: MCPP_EFFECTIVE: ${{ matrix.mcpp_version }} steps: diff --git a/index.toml b/index.toml index d186557..d5a05a1 100644 --- a/index.toml +++ b/index.toml @@ -7,5 +7,5 @@ # "floor first, new grammar after" rollout rule mechanically. [index] spec = "1" -min_mcpp = "0.0.106" -latest_mcpp = "0.0.106" +min_mcpp = "0.0.107" +latest_mcpp = "0.0.107" From 86a864cfbf720b5aa4b0ab19d5119a6e1c1e8f2f Mon Sep 17 00:00:00 2001 From: sunrisepeak Date: Sun, 26 Jul 2026 00:35:40 +0800 Subject: [PATCH 5/6] docs: keep the SPEC-001 comments pointing at 0.0.106 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The re-pin is for the soname-alias fix; SPEC-001 itself landed in 0.0.106 and `mcpp xpkg parse` has enforced the name rule since then. The version floor below still moves to 0.0.107 — that is the pin, not the rule's origin. --- .github/workflows/validate.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index 76ce361..d306a38 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -11,7 +11,7 @@ on: workflow_dispatch: env: - # 0.0.107: SPEC-001 package identity (mcpp#280). `package.name` is a SINGLE + # 0.0.106: SPEC-001 package identity (mcpp#280). `package.name` is a SINGLE # ATOMIC SEGMENT — all hierarchy lives in `package.namespace` — and mcpp # addresses a package by the LITERAL name it read, so descriptors no longer # repeat their namespace inside `name`. This index is migrated to the short @@ -103,7 +103,7 @@ jobs: # 5. `name` must be a SINGLE ATOMIC SEGMENT; hierarchy belongs in # `namespace` (mcpp SPEC-001 §3.2). The legacy fully-qualified # spelling stays accepted. Cheap second gate: it runs before the - # pinned mcpp is even downloaded, and mcpp >= 0.0.107 enforces + # pinned mcpp is even downloaded, and mcpp >= 0.0.106 enforces # the same rule inside `mcpp xpkg parse`. if ! lua5.4 tests/check_package_name.lua "$f"; then fail=1 From 00b38770d66f438a5258b45f9b4daf80b3541d2d Mon Sep 17 00:00:00 2001 From: sunrisepeak Date: Sun, 26 Jul 2026 03:11:28 +0800 Subject: [PATCH 6/6] chore: re-pin to mcpp 0.0.108 (Windows ninja goal-list overflow, mcpp#285) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Windows job has failed on the ffmpeg member since 0.0.104, independently of this branch: `mcpp test` named all 2281 of its compile units as ninja goals, Windows joined them into a 50,781-character cmd.exe command line (cap 8191), and the command never ran — a bare 127 from cmd.exe with no output from ninja or mcpp. 0.0.108 routes the goal set through build.ninja as a phony edge. Together with the soname-alias fix in 0.0.107 (which unblocked gui-stack, imgui-module and imgui-window on linux), this should be the last version-side blocker for this migration. --- .github/workflows/validate.yml | 8 ++++---- index.toml | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index d306a38..467bb51 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -57,7 +57,7 @@ env: # 0.0.94 fixed feature-gated `sources` under `mcpp test` (mcpp#218); 0.0.91 # added standard = "c++fly" to the resolver grammar, so c++fly descriptors # get the lint WARN below, not a hard grammar-parse rejection. - MCPP_VERSION: "0.0.107" + MCPP_VERSION: "0.0.108" jobs: lint: @@ -222,21 +222,21 @@ jobs: ext: tar.gz mcpp: bin/mcpp xlings: registry/bin/xlings - mcpp_version: "0.0.107" # keep in sync with env.MCPP_VERSION + mcpp_version: "0.0.108" # keep in sync with env.MCPP_VERSION - platform: macos os: macos-15 suffix: macosx-arm64 ext: tar.gz mcpp: bin/mcpp xlings: registry/bin/xlings - mcpp_version: "0.0.107" # keep in sync with env.MCPP_VERSION + mcpp_version: "0.0.108" # keep in sync with env.MCPP_VERSION - platform: windows os: windows-latest suffix: windows-x86_64 ext: zip mcpp: bin/mcpp.exe xlings: registry/bin/xlings.exe - mcpp_version: "0.0.107" # keep in sync with env.MCPP_VERSION + mcpp_version: "0.0.108" # keep in sync with env.MCPP_VERSION env: MCPP_EFFECTIVE: ${{ matrix.mcpp_version }} steps: diff --git a/index.toml b/index.toml index d5a05a1..da3834c 100644 --- a/index.toml +++ b/index.toml @@ -7,5 +7,5 @@ # "floor first, new grammar after" rollout rule mechanically. [index] spec = "1" -min_mcpp = "0.0.107" -latest_mcpp = "0.0.107" +min_mcpp = "0.0.108" +latest_mcpp = "0.0.108"