From 3bbd4bcbf0856633b9314d5443644ccc47e72cc1 Mon Sep 17 00:00:00 2001 From: sunrisepeak Date: Sat, 25 Jul 2026 07:26:11 +0800 Subject: [PATCH] =?UTF-8?q?fix(pkgs):=20chriskohlhoff.asio=20name=20?= =?UTF-8?q?=E5=9B=9E=E5=BD=92=20FQN=20=E5=BD=A2=E5=BC=8F=20+=20lint=20?= =?UTF-8?q?=E5=AE=88=E5=8D=AB=20(mcpp#278)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #109 把 pkgs/c/chriskohlhoff.asio.lua 的 name 从 "chriskohlhoff.asio" 改成 "asio"(namespace 不变),这是该 commit 对描述符的唯一功能性改动, 其余全是注释重写。结果三平台 workspace job 全部在 tests/examples/asio-module 上失败: E_NOT_FOUND: package 'chriskohlhoff:chriskohlhoff.asio@1.38.1' not found 原因是 mcpp 内部两条规则对同一份描述符的口径不一致:身份归一化层 (manifest/xpkg.cppm canonical_xpkg_identity)认为 split 形式 (namespace="chriskohlhoff" + name="asio")与 FQN 形式等价、都合法, 而安装目标构造层(build/prepare.cppm)硬假设 name 字面值就等于 ns + "." + shortName。xlings/libxpkg 按前者的 name 字面值建索引 key (entries[package.name] = "asio"),mcpp 却拿 "chriskohlhoff.asio" 去点名, 两者永远不相交。 实测四种消费端写法(点式选择器、ns 表内点式、default 索引裸名、别名索引 裸名)全部无解 —— ns 被身份闸门钉死、短名被 xpm 版本表钉死,fqname 恒为 chriskohlhoff.asio 而 key 恒为 asio。等价约束是 name 必须 == namespace + "." + <短名>,只能在描述符里修。 改动: * pkgs/c/chriskohlhoff.asio.lua — name 写回 FQN 形式(与索引内其余 47 个 namespaced 包一致)。文件路径、namespace、消费端 mcpp.toml 均不动。 * pkgs/t/tensorvia-cpu.lua — 同类隐患(ns="aimol" + name="tensorvia-cpu"), 今天没被任何 workspace member 消费所以没暴露,但同样装不上;一并修正。 该文件是 `mcpp emit xpkg` 生成后手工补了 namespace 才落入 split 形式。 * tests/check_package_name.lua + validate.yml rule 5 — namespace 非空时 强制 name 以 "." 开头。lint 秒级拦下,不再让这类错误跑满 三平台一小时后才炸。 验证(CI 钉住的 mcpp 0.0.102 + vendored xlings 0.4.67,冷 store 冷索引缓存): tests/examples/asio-module → 5 passed; 0 failed。lint 全量重放 exit 0, 负向测试(把 name 改回 split 形式)如期报错。 上游已提 mcpp-community/mcpp#278:mcpp 要么构造 target 时用描述符声明的 name(loadVersionDep 里 luaContent 已在作用域内),要么收紧规范让 `mcpp xpkg parse` 直接拒绝 split 形式。本 lint 是上游对齐前索引侧的守卫。 --- .github/workflows/validate.yml | 13 +++++- pkgs/c/chriskohlhoff.asio.lua | 10 ++++- pkgs/t/tensorvia-cpu.lua | 9 +++- tests/check_package_name.lua | 77 ++++++++++++++++++++++++++++++++++ 4 files changed, 106 insertions(+), 3 deletions(-) create mode 100644 tests/check_package_name.lua diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index 3a05167..aba168e 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -90,7 +90,18 @@ jobs: if ! lua5.4 tests/check_mirror_urls.lua "$f"; then fail=1 fi - # 5. c++fly admission policy (mcpp design 2026-07-14 §11-Q2, v1): + # 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. + if ! lua5.4 tests/check_package_name.lua "$f"; then + fail=1 + fi + # 6. c++fly admission policy (mcpp design 2026-07-14 §11-Q2, v1): # c++fly means "toolchain's latest level + every experimental # gate" — deliberately toolchain-dependent, so a published # package built with it is not reproducible for consumers. diff --git a/pkgs/c/chriskohlhoff.asio.lua b/pkgs/c/chriskohlhoff.asio.lua index e4ae0fb..bed725c 100644 --- a/pkgs/c/chriskohlhoff.asio.lua +++ b/pkgs/c/chriskohlhoff.asio.lua @@ -38,7 +38,15 @@ package = { spec = "1", namespace = "chriskohlhoff", - name = "asio", + -- `name` MUST be the fully-qualified `.`: xlings keys its + -- index on this literal (libxpkg build_index → entries[package.name]), + -- while mcpp asks for the FQN it reconstructs from the consumer's + -- `[dependencies.] `. Writing the split form ("asio") is legal + -- per mcpp's own descriptor spec (manifest/xpkg.cppm canonical_xpkg_ + -- 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", 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/t/tensorvia-cpu.lua b/pkgs/t/tensorvia-cpu.lua index 7d43990..e7ada8b 100644 --- a/pkgs/t/tensorvia-cpu.lua +++ b/pkgs/t/tensorvia-cpu.lua @@ -1,8 +1,15 @@ -- AUTO-GENERATED by `mcpp emit xpkg`. Do not edit by hand. -- Source: mcpp.toml @ v0.1.1 +-- +-- Hand-edit on top of the generated file: `mcpp emit xpkg` writes the bare +-- project name and no `namespace`; the `namespace = "aimol"` line below was +-- added when the package was filed into this index, which made `name` the +-- split form and left the package uninstallable (xlings keyed the entry under +-- `tensorvia-cpu` while mcpp asks for `aimol.tensorvia-cpu`). `name` is now +-- the fully-qualified spelling. See mcpp-community/mcpp#278. package = { spec = "1", - name = "tensorvia-cpu", + name = "aimol.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/tests/check_package_name.lua b/tests/check_package_name.lua new file mode 100644 index 0000000..f34f783 --- /dev/null +++ b/tests/check_package_name.lua @@ -0,0 +1,77 @@ +-- Lint `package.name` against `package.namespace`. +-- +-- 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: +-- +-- * 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"). +-- +-- 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. +-- +-- 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. +-- +-- Zero-namespace packages (the public default-namespace module packages — +-- imgui / ffmpeg / opencv) are unaffected: their bare `name` IS the FQN. +-- +-- Usage: lua5.4 tests/check_package_name.lua + +function import(...) + return setmetatable({}, {__index = function() return function() end end}) +end + +local path = assert(arg[1], "usage: check_package_name.lua ") +package = nil +local chunk = assert(loadfile(path, "t")) +chunk() + +local p = package +if type(p) ~= "table" then os.exit(0) end + +local fail = 0 +local function err(msg) + io.stderr:write(string.format("::error file=%s::%s\n", path, msg)) + fail = 1 +end + +local name = p.name +local ns = p.namespace or "" + +if type(name) ~= "string" or name == "" then + err("package.name must be a non-empty string") + os.exit(fail) +end +if type(ns) ~= "string" then + err("package.namespace must be a string") + os.exit(fail) +end + +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)) + end +end + +os.exit(fail)