Skip to content

Commit 1317625

Browse files
committed
fix(build): key a package's device sources by its root, not by its bare name
Two packages in one graph may share a bare name and differ only by namespace — that is what namespaces are for — and the map that carries device sources to a build program was keyed by the bare name at both its write site and its two read sites. A collision would hand one package's device sources to the other's build program, and nothing would report it: the receiving program would emit actions for files it does not own, and the owner would emit none. Keyed by the package root instead, which is unique by construction. Found by re-reading the diff before merge; no test covered it, and a test that did would need two same-named packages in one graph, which is worth adding when a second consumer of this map appears.
1 parent 6115c35 commit 1317625

2 files changed

Lines changed: 32 additions & 18 deletions

File tree

.agents/docs/2026-09-05-multi-device-implementation-plan.md

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@
77

88
| 批次 | 仓库 | 状态 |
99
|---|---|---|
10-
| ⓪ 修已发布的错误示范 | mcpp | 🟡 T0.1/T0.2 ✅,T0.3 待载荷 |
11-
| ① 载荷 | xim-pkgindex | 🟡 **PR #759**(25 个包,已实测) |
12-
| ② 引擎 | mcpp | 🟡 进行中:C-3/C-4/C-5/C-7 ✅,C-1/C-2 以 action 角色落地,C-6/T2.8–T2.10 进行中 |
10+
| ⓪ 修已发布的错误示范 | mcpp | T0.1/T0.2 合入 `1e2137b`;T0.3 **#563** |
11+
| ① 载荷 | xim-pkgindex | **#759 已合入 `a4644a7`**,15 项 CI 全绿,25 个包;⚠️ 13.x 后端不可达由 **#760** |
12+
| ② 引擎 | mcpp | ✅ T2.1–T2.5、T2.7–T2.10 完成并各有判据;T2.6 部分(`accel` 已进 ABI 标签与指纹,`.a` 端到端判据待补) |
1313
| ③ 发布 | mcpp ||
1414
| ④ 适配面 | mcpp-index ||
1515
| ⑤ 框架 | mcpp-index ||
@@ -27,14 +27,14 @@
2727
|---|---|---|---|
2828
| T0.1 | 从全部用户文档删除 `[xlings] deps`,`[xlings.workspace]` 为唯一形式 | `git grep -c 'xlings\] deps' docs/` 为 0(中英双份) ||
2929
| T0.2 | `examples/09-cuda-kernel` 改用 `[xlings.workspace]` | 示例中不出现 `deps =` | T0.1 |
30-
| T0.3 | 该示例去 host 化(`-L/usr/...``/usr/local/cuda/bin/nvcc`) | 示例与规则包里 `grep -c '/usr'` 为 0 | T1.1 |
30+
| T0.3 | ✅ 示例去 host 化 |`mcpp build -v` 的命令行里 `/usr` CUDA 路径 **0 处**;nvcc 与两处 include 全来自 `xpkgs/xim-x-cuda-*`;`mcpp run``12 24 36 48` | T1.1 |
3131

3232
### ① 载荷(xim-pkgindex,批内并行)
3333

3434
| # | 任务 | 版本 | 判据 | 依赖 |
3535
|---|---|---|---|---|
3636
| T1.1 | ✅ 24 个 CUDA 组件(编译/运行/调试/分析/算子库) | 12.9 线 + 13.3 线 | ✅ 载荷编 sm_89 并在 4080 上跑出 `12 24 36 48`;两线并存可切换 ||
37-
| T1.2 | `llvm-offload`(补 slim 载荷缺的 offload 工具) | 22.1.8 | `clang -x cuda -fgpu-rdc` 编链通过 ||
37+
| T1.2 | `llvm-offload` | 22.1.8 | ⚠️ 已有替代:`dpcpp@7.1.0` 载荷自带全套 offload 工具,需要 RDC 的工程可用它。独立 `llvm-offload` 仍待做 ||
3838
| T1.3 |`dpcpp` | 7.1.0 |`sycl-ls``[cuda:gpu] NVIDIA CUDA BACKEND` ||
3939
| T1.4 | `pocl` | 7.2 | `clinfo` 出现 CPU 设备 ||
4040
| T1.5 | `mesa-lavapipe` | 25.2.8+ | `vulkaninfo` 出现 `PHYSICAL_DEVICE_TYPE_CPU` ||
@@ -49,15 +49,15 @@
4949
| # | 任务 | 判据 | 依赖 |
5050
|---|---|---|---|
5151
| T2.1 |**C-1 设备目标原语** —— 以既有 `mcpp::action``role = "artifact"` 落地,不新增 target kind | ✅ artifact 角色的产物不进链接(ninja_backend 既有);理由见「动态更新记录」 ||
52-
| T2.2 | 🟡 **C-2 二次链接边** —— 以「object 角色的 action 以其它 action 的产物为输入」落地,核心只管顺序与指纹 | e2e 待补:通用链式 action(无厂商);C9 在 4080 上用 nvcc `-rdc=true` + `-dlink` 实测 | T2.1 |
52+
| T2.2 | **C-2 二次链接边** —— 以「object 角色的 action 以其它 action 的产物为输入」落地,核心只管顺序与指纹 | e2e 607:artifact 角色产物被 object 角色消费,中间产物不进链接线,顺序由图保证。⚠️ C9(nvcc `-rdc=true` + `-dlink` 真机)本机做不到 —— 见下方记录 | T2.1 |
5353
| T2.3 |**C-3 逐 glob 收窄** `sources = [{ glob, accel }]` | ✅ e2e 606 四段:覆盖 ⇒ 编译且设备源到达构建程序;`--no-accel` ⇒ 整条 glob 排除;非子集 ⇒ 拒绝并点名两侧(`accel-mismatch`);空集 ⇒ 拒绝点名 glob。6 条单测 ||
5454
| T2.4 |**C-4 `exclusive` 能力声明** | ✅ e2e 601:独占对被拒并点名双方;**对照** —— 不声明的两个提供者照常共存。3 条单测 + 中英文档 + `exclusive-capability` 进机器接口契约页 ||
5555
| T2.5 |**C-5 载荷可用性机制**:探针通道 `mcpp::fact` / `mcpp::floor`(协议 v7),核心只比较;根工程的构建程序说完后再查一次 | ✅ e2e 605:根 build.mcpp 陈述的下界被比较并拒绝(两侧取值 + `version-floor-unmet`);对照:满足则构建 ||
56-
| T2.6 | **C-6 含设备代码的归档** | `.a``accel` 随包传播(C13) | T2.1 |
56+
| T2.6 | 🟡 **C-6 含设备代码的归档** | `accel` 已是 `pack::AbiTag` 第四维并进指纹;`.a` 随包传播的端到端判据待补 | T2.1 |
5757
| T2.7 |**C-7 `accel` 维语法开放**(#562) | ✅ 5 条单测 `AccelOpenGrammar.*`;`floor>=` 为中性拼法 ||
58-
| T2.8 | **把 CUDA 探针搬进规则包** | 核心 grep 不到厂商名字(C15);卸掉规则包 doctor 安静(C16) | T2.5 |
59-
| T2.9 | **`accel` 表达驱动下界** | PTX 版本高于驱动构建前拒绝(C20) | T2.5, T2.7 |
60-
| T2.10 | **未指定设备目标的构建期诊断** | 报「没有为任何可用设备编」而非运行期(C19) | T2.7 |
58+
| T2.8 | **把 CUDA 探针搬进规则包** | `test_core_vendor_probes`:剥注释后 `src/` 无厂商工具名(自带分母,枚举 < 100 文件即判失败);doctor 的设备节与 `mcpp.toolchain.devicehost` 一并删除;同样的读数由 rules-cuda 产出 | T2.5 |
59+
| T2.9 | **`accel` 表达驱动下界** | ✅ 实测:`fact=cuda.driver=12.4` + `floor=cuda.driver >= 13.0` ⇒ 13.3 工具包在编译前被拒;PTX 高于驱动警告(点名架构集合仍可运行) | T2.5, T2.7 |
60+
| T2.10 | **未指定设备目标的构建期诊断** | `accel` 不含架构 ⇒ 规则包在提交任何 action 前拒绝并说明;`--no-accel` 走 CPU 实现而非「编了但没设备码」 | T2.7 |
6161

6262
### ③ 发布(mcpp)
6363

@@ -130,9 +130,18 @@
130130
| 2026-09-05 | T1.1/T1.3/T1.6/T1.7 落地为 xim PR #759(25 包) | 实测通过:载荷编 sm_89 并在 4080 上跑通;两条线并存可切 |
131131
| 2026-09-05 | `cuda-cccl` 用显式 per-version URL 而非模板 | 上游把组件从 `cuda_cccl` 改名为 `cccl`,目录名进 URL,一个模板 404 |
132132
| 2026-09-05 | 配方用 `io.popen` 列文件 | `os.files``config()` 沙箱里不可用(`attempt to call a nil value`),`llvm.lua` 也用 popen |
133+
| 2026-09-05 | ⚠️⚠️ T2.5 的驱动取数**撤出核心** | 仓库自带的 `test_runtime_contract` 不变量禁止「厂商词 + 探针启动」在 `src/` 共现,抓住了我写的 `nvidia-smi` 调用。**这条规则先于本工作存在且是对的** —— 关系留下并单测,取数改由声明抵达,归规则包通道 |
134+
| 2026-09-05 | T0.3 完成,并暴露一处「几乎为真」 | nvcc 自动加的是**它自己的** `../include`,12.x 那里有 `crt/` 却没有 `cuda_runtime.h`(在 cudart 组件)。不显式传载荷 include ⇒ nvcc 从 `/usr/include` 取头、连带读宿主的 `host_config.h`,**用着载荷的编译器却报宿主工具包的错** |
135+
| 2026-09-05 | ① 全部完成并合入 | 25 个载荷;五个命令(nvcc/cuda-gdb/ncu/nsys/sycl-ls)从 shim 可达 |
133136
| 2026-09-05 | T2.4 完成 | `exclusive` 是列表不是布尔:一个包可提供多项能力而只有部分独占。schema 警告而非报错,因为绑定期那一处才是执行者 |
134137
| 2026-09-05 | ⚠️ 本机 shim 被 #582 剪掉一次 | 25 次 `xlings install``mcpp` 等 7 个裸名 shim 消失,store 完好。重装即恢复 —— 又一次受控复现,补进 issue |
135138
| 2026-09-05 | `libcublas` 暂不拆 static | 上游一个归档同时含 shared 与 static,拆分需要重打包并 re-host,与「不 re-host」冲突;先按上游形态发,拆分单列 |
139+
| 2026-09-05 | ⚠️⚠️ **C9(RDC 真机)在本机不可测,判据改为通用形状** | nvcc 路线在这台机器上**两个工具包都用不了**:12.9 满足驱动而不满足 C 库,13.3 满足 C 库而不满足驱动。C9 需要 nvcc 才能构造,于是它的判据退回到 e2e 607 所测的**通用链式 action**(与厂商无关的同一形状),真机 RDC 留给有 13.x 驱动的机器 |
140+
| 2026-09-05 | ⚠️⚠️ **12.9 工具包 + glibc ≥ 2.41 是一个不能配的对** | 12.9 的 `crt/math_functions.h` 为宿主重声明 C23 的 `cospi`/`sinpi`/`rsqrt` **不带 `noexcept`**,glibc 2.41+ 带,而 C++17 起它是函数类型的一部分 ⇒ 六条 `exception specification is incompatible`,点名两个头文件而不给结论。13.3 不再重声明,同一份 C 库下干净通过。规则包读 `bits/mathcalls.h` 直接拒这一对 |
141+
| 2026-09-05 | **引擎新增两个环境变量而不是复用 `MCPP_TARGET_SYSROOT`** | 后者是**档位**事实(裸机 C 库载荷,宿主目标为空),已有消费者按它判断档位;第二个编译器要的是**环境**事实。复用会把两个含义压进一个键 —— 正是 [[a-value-acquires-the-receiving-layers-requirements]] 那条 |
142+
| 2026-09-05 | **`-B` 的守卫收敛为 `gcc::binutils_prefix_dir`,并只对 GCC 作答** | 三处副本(registry / gcc / flags),其中一处注释写着「Mirrors the guard in build/flags.cppm」。clang 的命令行本就不带 `-B`,所以对 clang 作答会描述一个没人传的开关 |
143+
| 2026-09-05 | ⚠️ **构建程序 helper 在 Linux 上改用 `DT_RPATH`** | RUNPATH 只对 helper **自己**的 needed 生效;它 `dlopen` 的宿主库的依赖(`libdl.so.2`)按私有 loader 的默认搜索,搜不到。这是规则包能读到驱动版本的前提 |
144+
| 2026-09-05 | ⚠️⚠️ **① 的 13.x 载荷装完不能用** —— xim #760 | nvcc 用 `$(TOP)/nvvm/bin/cicc` 找自己的后端,而 13.x 把 `nvvm/``crt/` 拆成了独立包=独立载荷根。**载荷完整、`nvcc --version` 正常、组件都装了**,编译时 `exit 127`。修法在 `install()` 里把这两个目录链回来;`os.exists`/`os.ln` 在配方沙箱里都不存在,`os.cp(symlink=true)` 是「保留源里的符号链接」而不是「建一个」 |
136145
| 2026-09-05 | e2e 317 的等待窗从 2s 放宽到 5s | 到达「五次短失败」下界最少要 1.25s(4×250ms 重启延迟 + 5×50ms 轮询),2s 窗只给每次 spawn 留 150ms;main 上 macOS **连续两次**在此失败而本分支同码两次通过 —— 判据由 runner 负载决定。5s 窗留 750ms |
137146
| 2026-09-05 | e2e 602 声明 `requires: unix-shell`,并以 `MCPP_OFFLINE=1` 运行 | doctor 在 Windows 上整段不产出(载荷只有 linux 构建;Windows 工具包的上界是 `_MSC_VER` 区间,报告尚未读它);隔离 home 下 doctor 会把整套引导 + 工具链装进临时目录:实测 229s / 1.4 GB |
138147
| 2026-09-05 | ⚠️ 核心改动:`--offline` 下跳过首次沙箱引导 | `load_or_init` 在空 home 里克隆索引、经 `xlings install` 装 ninja/patchelf,全部走网络,违反 `--offline`「绝不碰网络」的承诺。实测 offline 空 home 26s / 126 MB → 0.3s;e2e 604 带对照(已引导的 home 不提示);文档中英各补一句 |

src/build/prepare.cppm

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6519,6 +6519,10 @@ prepare_build(bool print_fingerprint,
65196519
// package name -> device-kind sources of its effective source set, filled
65206520
// by the narrowing pass after feature application and read at both
65216521
// build-program run sites (MCPP_DEVICE_SOURCES).
6522+
// Keyed by the package's ROOT DIRECTORY, not by its name. Two packages in
6523+
// one graph may share a bare name and differ only by namespace — that is
6524+
// what namespaces are for — and a name key would hand one package's
6525+
// device sources to the other's build program with nothing reporting it.
65226526
std::map<std::string, std::vector<std::string>> deviceSourcesByPackage;
65236527
auto checkVersionFloors = [&]() -> std::optional<std::string> {
65246528
std::map<std::string, std::pair<std::string, std::string>> facts; // name -> (version, who)
@@ -6966,7 +6970,7 @@ prepare_build(bool print_fingerprint,
69666970
if (mcpp::classify(f, extTable) != mcpp::SourceKind::Device) continue;
69676971
device.push_back(f.lexically_relative(pkg.root).generic_string());
69686972
}
6969-
deviceSourcesByPackage[pkg.manifest.package.name] = std::move(device);
6973+
deviceSourcesByPackage[pkg.root.string()] = std::move(device);
69706974
}
69716975
}
69726976
activeFeaturesByPackage.resize(packages.size());
@@ -7512,14 +7516,15 @@ prepare_build(bool print_fingerprint,
75127516
};
75137517
mcpp::build::BuildProgramEnv bpEnv;
75147518
bpEnv.targetTriple = resolvedTargetCanonical;
7515-
// The payload ROOT (not the driver), the target's C library, and the
7516-
// three answers that keep a board package from hardcoding a toolchain
7517-
// or a libc. All four in one call — see fill_target_build_env.
7518-
fill_target_build_env(bpEnv, tc ? &*tc : nullptr);
7519-
bpEnv.toolsBin = projectSubosBin;
7519+
// The payload ROOT (not the driver), the target's C library, and
7520+
// the three answers that keep a board package from hardcoding a
7521+
// toolchain or a libc. All four in one call — see
7522+
// fill_target_build_env.
7523+
fill_target_build_env(bpEnv, tc ? &*tc : nullptr);
7524+
bpEnv.toolsBin = projectSubosBin;
75207525
bpEnv.profile = effectiveProfile;
75217526
bpEnv.accel = resolvedAccel();
7522-
if (auto dit = deviceSourcesByPackage.find(pkg.manifest.package.name); dit != deviceSourcesByPackage.end())
7527+
if (auto dit = deviceSourcesByPackage.find(pkg.root.string()); dit != deviceSourcesByPackage.end())
75237528
bpEnv.deviceSources = dit->second;
75247529
bpEnv.features = feature_closure(pkg.manifest, req, depDefaultFeatures);
75257530
bpEnv.artifactsDir = workRoot / "target" / ".build-mcpp" / "deps"
@@ -8434,7 +8439,7 @@ prepare_build(bool print_fingerprint,
84348439
bpEnv.toolsBin = projectSubosBin;
84358440
bpEnv.profile = effectiveProfile;
84368441
bpEnv.accel = resolvedAccel();
8437-
if (auto dit = deviceSourcesByPackage.find(m->package.name); dit != deviceSourcesByPackage.end())
8442+
if (auto dit = deviceSourcesByPackage.find(root->string()); dit != deviceSourcesByPackage.end())
84388443
bpEnv.deviceSources = dit->second;
84398444
// Set explicitly rather than relying on build_dir()'s root-relative
84408445
// default: under BuildOverrides::work_dir the package root is shared

0 commit comments

Comments
 (0)