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)