Skip to content

descriptor spec 与安装目标口径不一致:package.name 写成 split 形式(namespace + 短名)的索引包无法被任何消费端安装,且 lint 静默放行 #278

Description

@Sunrisepeak

摘要

mcpp 内部两条规则对同一份 xpkg 描述符的身份口径不一致:

  • 身份归一化层认为 namespace = "chriskohlhoff" + name = "asio"(split 形式)与 name = "chriskohlhoff.asio"(FQN 形式)等价、都合法;
  • 安装目标构造层却硬假设描述符的 name 字面值就等于 ns + "." + shortName

结果:一个用 split 形式书写的索引包能通过 mcpp xpkg parse、能通过 mcpp 自己的身份校验闸门,却没有任何消费端写法能把它装上 —— 一律 E_NOT_FOUND

真实事故:mcpp-index 的 pkgs/c/chriskohlhoff.asio.luaname"chriskohlhoff.asio" 改成 "asio"(namespace 不变),lint job 全绿,三个平台的 workspace job 全部在跑满 20~58 分钟后挂掉。

复现

索引侧 pkgs/c/chriskohlhoff.asio.lua:

package = {
    spec      = "1",
    namespace = "chriskohlhoff",
    name      = "asio",          -- split 形式
    xpm = { linux = { ["1.38.1"] = { url = ..., sha256 = ... } }, ... },
    mcpp = { ... },
}

消费端 mcpp.toml:

[indices]
chriskohlhoff = { path = "../../.." }

[dependencies.chriskohlhoff]
asio = "1.38.1"

mcpp build(mcpp 0.0.102,vendored xlings 0.4.67):

error: xlings install_packages failed (exit 1) for 'chriskohlhoff.asio@1.38.1'
       with 1 index repo configured [chriskohlhoff -> <index path>]
  xlings reported: E_NOT_FOUND: package 'chriskohlhoff:chriskohlhoff.asio@1.38.1' not found

name 改回 "chriskohlhoff.asio",同一条命令、同一个 pin 立即通过(消费端一个字没动)。

根因

(1) 身份归一化接受 split 形式 —— src/manifest/xpkg.cppm:649-671 canonical_xpkg_identity:

// Step 2 — A declared name already prefixed with the namespace IS the FQN;
//          otherwise the namespace is prepended.
std::string prefix = ns + ".";
fqn = name.starts_with(prefix) ? name : ns + "." + name;

(ns="chriskohlhoff", name="asio")(ns="chriskohlhoff", name="chriskohlhoff.asio") 归一化到同一身份。设计文档 .agents/docs/2026-06-26-identity-first-resolution-no-filename.md 也明确写了两种拼写等价。所以 mcpp xpkg parse 打印的是 package chriskohlhoff.asio (namespace 'chriskohlhoff') —— 看起来完全正常。

(2) 安装目标构造假设 name 已是 FQN 形式 —— src/build/prepare.cppm:1713-1717 的注释把这个假设写得很明白:

// xlings resolves packages by the full qualified name (ns.shortName)
// as it appears in the index's name field. Use fqname, not the
// map key (which may be a bare short name for default-ns deps).
auto fqname = ns.empty() ? shortName : std::format("{}.{}", ns, shortName);

以及 :1752-1757:

// For custom indices, use indexName:fullPackageName@version so
// xlings resolves the package by the descriptor's name field while
// still selecting the project-added index.
target = std::format("{}:{}@{}", idxSpec->name, fqname, version);

而 xlings/libxpkg 建索引时 key 就是 package.name 的字面值(libxpkg/src/xpkg-loader.cppm:643-647,index.entries[pkg.name])—— 也就是说 libxpkg 完全是按 mcpp 注释里写的契约在做事,偏差在 mcpp 这一侧:它接受了 split 形式,却按 FQN 形式去点名。

(3) mcpp 手里其实有 name 的字面值,却没用。 src/build/prepare.cppm:1580-1588 loadVersionDep 在调 xlings 之前就已经把描述符读进来了(readLuaContent()read_xpkg_lua_from_path),并靠 canonical_xpkg_identity 校验才放行 —— 把 name 改成一个真正不匹配的值(如 "ZZZ")时,报的是 mcpp 自己的 dependency '...': not found in local index,根本走不到 xlings。所以 luaContent(含 name = "asio")在作用域内,只是构造 target 时没有使用。

消费端无解

实测四种写法(mcpp 0.0.102,索引为上述 split 形式描述符),没有任何一种能装上:

消费端写法 发给 xlings 的 target 结果
[dependencies.chriskohlhoff] + asio chriskohlhoff:chriskohlhoff.asio@1.38.1 E_NOT_FOUND
[dependencies.chriskohlhoff] + "chriskohlhoff.asio" ns 拼两遍 → chriskohlhoff.chriskohlhoff.asio,mcpp 侧即失败
[dependencies] + "chriskohlhoff.asio"(点式选择器) chriskohlhoff:chriskohlhoff.asio@1.38.1 E_NOT_FOUND(归一化回 ns/short,同第一行)
[indices] default + 裸 asio 身份闸门失败(描述符 ns=chriskohlhoff ≠ 默认 ns)

原因是两条约束同时成立:身份闸门要求消费端的 ns == 描述符的 namespace;xlings 的 key == 描述符的 name。于是 fqname 恒为 chriskohlhoff.asio 而 key 恒为 asio,消费端能调的 ns 和短名都被钉死,够不着。等价于:name 必须 == namespace + "." + <短名>,而这一点目前没有任何地方强制。

期望

二选一(倾向 B):

A. 让 target 用描述符声明的 name loadVersionDepluaContent 已在作用域内,manifest/xpkg.cppm 也已有取 name 的现成函数;构造 target 时改用它,split 形式就真正可用了。代价:同一个包在索引里出现两种合法名字,mcpp search、store 目录名(package_store_name = ns + "-x-" + name)、错误信息都会跟着分叉。

B. 收紧规范:index 描述符的 name 必须写 FQN 形式,split 形式在 mcpp xpkg parse 报错。 也就是把 canonical_xpkg_identity 的 Step 2 从"容忍"改成"校验",让这类描述符在 lint 阶段(秒级)就被拦下,而不是三个平台跑满一小时后才炸。这也与 prepare.cppm 现有注释里的契约一致。

不论选哪个,mcpp xpkg parse 都应该能发现这个问题 —— 目前它只在 name 缺失时报错(src/cli/cmd_xpkg.cppm:80-85),对 split 形式静默放行,索引 CI 因此完全失去了防护。

环境

  • mcpp 0.0.102(vendored xlings 0.4.67)复现;0.0.103(xlings 0.4.68)解析路径无变更(git diff v0.0.102..v0.0.103 -- src/pm src/build/prepare.cppm src/manifest/xpkg.cppm 只动了 index_spec.cppm 的 artifact/source 字段),同样不可用。
  • 注意一个容易误判的陷阱:如果本机全局 ~/.mcpp/registry/data/mcpplibs 还停在改动之前的快照(旧描述符里 name 仍是 FQN 形式),本地 mcpp build 会从那份旧索引满足请求而显得正常,只有干净 registry 的 CI 会炸。排查时可以用一个只存在于 path index 的版本号做探针来排除遮蔽。
  • 事故现场:mcpplibs/mcpp-index run 30121144277(linux/macos/windows 三平台同一处失败)。

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions