Skip to content

fix(pm): install_path 跨命名空间返回了别的包的目录 (2026.8.6.1) - #364

Merged
speak-agent merged 2 commits into
mainfrom
fix/legacy-scan-crosses-namespace
Aug 6, 2026
Merged

fix(pm): install_path 跨命名空间返回了别的包的目录 (2026.8.6.1)#364
speak-agent merged 2 commits into
mainfrom
fix/legacy-scan-crosses-namespace

Conversation

@speak-agent

Copy link
Copy Markdown
Member

症状

Fetcher::install_path(ns, shortName, version) 回答的是"这个包装在哪"。它最后那道 legacy 扫描匹配任何以 -x-<shortName> 结尾的目录、不看命名空间:

std::string suffix2 = std::format("-x-{}", shortName);
if (suffix2 != suffix1 && dirname.ends_with(suffix2)) return dirname;

于是查 ocornut:imgui@1.92.8 返回 compat-x-imgui/1.92.8 —— 另一个仅仅短名相同的包。调用方随后跳过安装("已存在"),并把那个包的源码树当作本包读取。

危险形态是静默的

我是在一个 Form B 邻居上撞见的,所以错误的 verdir 里没有 mcpp.toml,构建停在 index entry has no mcpp = ... field —— 一个指向错误原因的诊断,我因此在错误方向上找了一阵。

当两个包的源码都在 verdir 里时,没有任何报错。e2e 192 在旧二进制上红的正是这个:

   Compiling acme.widget v1.5.0
    Finished dev [unoptimized + debuginfo] in 0.07s
FAIL: build succeeded, so acme:widget was satisfied from somewhere —
      the only widget payload on disk belongs to compat.

编译的是 compat 的源码,冠以 acme.widget 的名字,然后成功。

为什么现在才够得到

install_path 同时匹配版本,所以同短名的两个包只有在版本也相同时才撞。而生态里 module 层用的是打包计数(imgui@0.0.6),compat 用的是上游版本(compat.imgui@1.92.8)—— 永远不撞。

把 module 层版本对齐到上游(mcpp-index#163)之后它们重合了。这个 bug 是被那次对齐逼出来的,不是新引入的,而它挡住了整个迁移。

修法

bare -x-<shortName> 这一支现在要求目录自身的命名空间前缀是调用方确实问过的(请求的 ns,或旧式 index-prefixed 布局里的 index 名)。-x-<ns>.<name> 那一支把命名空间带在后缀里,无需前缀约束。保住它存在的两种旧布局,拒掉它从不该服务的那一种。

顺带修诊断:<verdir> 是字面占位符,无法区分"Form B 忘了写 mcpp 字段"与"解析到的 verdir 根本不是这个包的"。现在打印真实路径,并在后一种情况下点名。

验证

  • 单测 4 条(tests/unit/test_fallback_legacy_dirs.cpp),含两条防止修过头的:仍要找得到自己命名空间的短名布局、以及 index-prefixed 旧布局。
  • e2e 192,实测旧二进制红、修复后绿。第一版 e2e 把 payload 种进了项目沙箱而不是全局 store,在两个二进制上都过 —— 空测试,已改正,注释里记了这个坑。
  • 真实复现同环境 A/B:store 里有 compat-x-imgui/1.92.8 时,ocornut:imgui@1.92.8 在旧二进制上失败、在修复后正常安装并构建。
  • 59 个单测全过。

…#346)

Every mcpp CI job builds either mcpp itself (tens of TUs) or a synthetic e2e
project (single digits). Link-line length, the response-file path and ninja
graph size over a large object set therefore had no coverage at all, and the
whole command-length defect family surfaced in the ecosystem instead:

    #274  ninja goals argv 50781 chars vs cmd.exe 8191
    #247  Windows CreateProcess 32 KiB
    #345  POSIX MAX_ARG_STRLEN 128 KiB
    #360  link.exe LNK1170, response-file line capped at 128 KiB

190 asserts the shape of the generated rule at 25 objects. 191 asserts the
scale: 1400 C TUs whose object list is 140 KiB, past the largest command-line
ceiling in cmdlimits.cppm, required to link and to run.

The regime is asserted rather than assumed. Object naming, the disambiguation
prefix and the file count all influence how large the list actually is, so the
response file's size is checked against the ceiling directly: if it ever falls
back under, the test reports that it has stopped covering the axis instead of
passing quietly.

Verified to fail without the fix — reverting the generated cxx_link rule to
its pre-#345 inline form on this project reproduces the original symptom
verbatim: `ninja: fatal: posix_spawn: Argument list too long`.

Cost: 1.3s wall on a developer machine. Padded file names carry the object
paths to ~100 bytes so the ceiling is reached at a file count this small,
bounded on the other side by Windows MAX_PATH.
`Fetcher::install_path(ns, shortName, version)` 回答的是"**这个**包装在哪"。它最后
那道 legacy 扫描却匹配任何以 `-x-<shortName>` 结尾的目录、不看命名空间,于是查
`ocornut:imgui@1.92.8` 会返回 `compat-x-imgui/1.92.8` —— 另一个仅仅短名相同的包。
调用方随后(a)认为"已安装"而跳过安装,(b)把那个包的源码树当作本包读取。

**危险形态是静默的。** 我是在一个 Form B 邻居上撞见的,所以错误的 verdir 里没有
mcpp.toml,构建停在 "index entry has no `mcpp = ...` field" —— 一个指向错误原因的
诊断。而当两个包的源码都在 verdir 里时,没有任何报错:构建**编译了另一个包的源码
并成功**。e2e 192 在旧二进制上正是这么红的:

    Compiling acme.widget v1.5.0
    Finished dev [unoptimized + debuginfo] in 0.07s
    FAIL: build succeeded, so acme:widget was satisfied from somewhere —
          the only widget payload on disk belongs to compat.

**为什么一直够不到。** install_path 同时匹配版本,所以同短名的两个包只有在版本也相
同的时候才会撞;而生态里 module 层用的是打包计数(`imgui@0.0.6`),compat 用的是上
游版本(`compat.imgui@1.92.8`),永远不撞。把 module 层的版本对齐到上游
(mcpp-index#163)之后它们就重合了 —— 这个 bug 是被那次对齐**逼出来**的,不是新引入的。

修法:bare `-x-<shortName>` 这一支现在要求目录自身的命名空间前缀是调用方确实问过的
(请求的 ns,或旧式 index-prefixed 布局里的 index 名)。`-x-<ns>.<name>` 那一支把命
名空间带在后缀里,无需前缀约束。这保住了它存在的两种旧布局,拒掉的正是它从不该服务
的那一种。

顺带修诊断:`<verdir>` 是个字面占位符,无法区分"包是 Form B 而你忘了写 mcpp 字段"
与"mcpp 解析到的 verdir 根本不是这个包的"。现在打印真实路径,并在后一种情况下点名。

验证:
* 单测 4 条(tests/unit/test_fallback_legacy_dirs.cpp),含"仍要找得到自己命名空间的
  短名布局"与"index-prefixed 旧布局"两条防止修过头。
* e2e 192,实测旧二进制红、修复后绿。
* 真实复现同环境 A/B:store 里有 compat-x-imgui/1.92.8 时,ocornut:imgui@1.92.8 在
  旧二进制上失败、在修复后正常安装并构建。
* 59 个单测全过。
@speak-agent
speak-agent merged commit eb196a2 into main Aug 6, 2026
18 checks passed
@speak-agent
speak-agent deleted the fix/legacy-scan-crosses-namespace branch August 6, 2026 03:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant