diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index aba168e..467bb51 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.108" 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 @@ -115,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. @@ -207,21 +222,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.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.102" # 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.102" # 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/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..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.102" -latest_mcpp = "0.0.102" +min_mcpp = "0.0.108" +latest_mcpp = "0.0.108" 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..68a163f 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", @@ -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.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..2f636c6 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", @@ -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/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/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/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/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/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/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", 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_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) 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)