Skip to content

Commit 49c4ebc

Browse files
committed
docs(plan): batch ④ landed, and three readings the engine batch produced
The adapters and the rename are in mcpp-index#347; the plan table says so and records the three findings that came out of measuring them — the display spelling that leaked into a value, the two upstream couplings the CUDA recipes now carry, and the path-index namespace behaviour that made local verification look like a resolution failure. The verification script travels with the plan rather than living only in a scratch directory: it is the thing that found the `(none)` defect, and it is what the release is checked with inside a sandbox.
1 parent 79c3d51 commit 49c4ebc

2 files changed

Lines changed: 196 additions & 3 deletions

File tree

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

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
| ① 载荷 | xim-pkgindex |**#759 已合入 `a4644a7`**,15 项 CI 全绿,25 个包;⚠️ 13.x 后端不可达由 **#760**|
1212
| ② 引擎 | mcpp | ✅ T2.1–T2.5、T2.7–T2.10 完成并各有判据;T2.6 部分(`accel` 已进 ABI 标签与指纹,`.a` 端到端判据待补) |
1313
| ③ 发布 | mcpp ||
14-
| ④ 适配面 | mcpp-index | |
14+
| ④ 适配面 | mcpp-index | 🟡 T4.1/T4.4 ✅(**#347 已合入 `8a9ca64`**);T4.2 待 ① 的 pocl/lavapipe;T4.3 待 ③ |
1515
| ⑤ 框架 | mcpp-index ||
1616
| ⑥ 生态验证 | 沙箱 ||
1717

@@ -69,10 +69,10 @@
6969

7070
| # | 任务 | 判据 | 依赖 |
7171
|---|---|---|---|
72-
| T4.1 | `compat.cuda-runtime``compat.cuda-driver` 改名 + `repo` 改正 | 旧名保留一个跳转期 | T3.1 |
72+
| T4.1 | `compat.cuda-runtime``compat.cuda-driver` 改名 + `repo` 改正 | ✅ 旧条目冻结保留;工作区成员 `tests/examples/cuda-driver` **同时依赖新旧两个名字**,让跳转期这条承诺有判据(此前它只是一句注释) | |
7373
| T4.2 | `compat.vulkan-icd` / `compat.opencl-icd`(缺失时回落载荷) | 无卡机器上 dlopen 到软件实现 | T1.4, T1.5 |
7474
| T4.3 | `rules-cuda` / `rules-hip` / `rules-sycl` / `rules-spirv` 进索引 | 消费者一行依赖即可用 | T3.1, T2.8 |
75-
| T4.4 | `compat.cublas` / `cudnn` / `nccl` / `onemkl` | 闭包校验通过 | T1.7 |
75+
| T4.4 | 🟡 `compat.cudart` + `cublas`/`cufft`/`curand`/`cusolver`/`cusparse`| ✅ e2e 判据=新成员 `tests/examples/cuda-curand`:无卡机器断言库能加载并应答,有卡再断言 [0,1] 与均值。⚠️ `cudnn`/`nccl`/`onemkl` 仍缺 xim 载荷 | T1.7 |
7676

7777
### ⑤ 框架(mcpp-index,依赖 ④)
7878

@@ -142,6 +142,9 @@
142142
| 2026-09-05 | **`-B` 的守卫收敛为 `gcc::binutils_prefix_dir`,并只对 GCC 作答** | 三处副本(registry / gcc / flags),其中一处注释写着「Mirrors the guard in build/flags.cppm」。clang 的命令行本就不带 `-B`,所以对 clang 作答会描述一个没人传的开关 |
143143
| 2026-09-05 | ⚠️ **构建程序 helper 在 Linux 上改用 `DT_RPATH`** | RUNPATH 只对 helper **自己**的 needed 生效;它 `dlopen` 的宿主库的依赖(`libdl.so.2`)按私有 loader 的默认搜索,搜不到。这是规则包能读到驱动版本的前提 |
144144
| 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)` 是「保留源里的符号链接」而不是「建一个」 |
145+
| 2026-09-05 | ⚠️⚠️ **「没有加速器」被写成了显示用的 `(none)`** | `accel_str` 为空集打印 `(none)` 是给 ABI 标签读的;`resolvedAccel` 把这个拼法当值传了出去 ⇒ ①`MCPP_ACCEL=(none)` 到达**每一个从未提过加速器的工程**,与手册承诺的空串矛盾;②指纹里 `if (!accel.empty())` 恒真,给所有工程都追加了 `#accel=(none)`。判据只能靠构建程序**写文件**取得 —— 它的 stdout 只在非零退出时才打印。e2e 605 第四段标题写着「变量与 layer 都清空」却只测了 layer,这就是它逃过套件的原因 |
146+
| 2026-09-05 | ④ 的 T4.1/T4.4 落地为 mcpp-index #347 并合入 | 六个新包 + 一次改名;两处上游耦合写进配方(`crt/` 在编译器组件里;NVIDIA 的 `.so``RUNPATH=$ORIGIN` 会关掉继承的 RPATH ⇒ 要一并 farm glibc 三个存根) |
147+
| 2026-09-05 | ⚠️ **path 索引里包的命名空间由「索引名」决定,而不是描述符里的 `namespace`** | 本地验证时 `[indices] localidx = { path = ... }``compat.cudart` 解析不到,而诊断说「a package with this name exists under another namespace: compat.cudart」—— 把索引名改成 `compat` 即通。诊断本身值得单独修 |
145148
| 2026-09-05 | e2e 317 的等待窗从 2s 放宽到 5s | 到达「五次短失败」下界最少要 1.25s(4×250ms 重启延迟 + 5×50ms 轮询),2s 窗只给每次 spawn 留 150ms;main 上 macOS **连续两次**在此失败而本分支同码两次通过 —— 判据由 runner 负载决定。5s 窗留 750ms |
146149
| 2026-09-05 | e2e 602 声明 `requires: unix-shell`,并以 `MCPP_OFFLINE=1` 运行 | doctor 在 Windows 上整段不产出(载荷只有 linux 构建;Windows 工具包的上界是 `_MSC_VER` 区间,报告尚未读它);隔离 home 下 doctor 会把整套引导 + 工具链装进临时目录:实测 229s / 1.4 GB |
147150
| 2026-09-05 | ⚠️ 核心改动:`--offline` 下跳过首次沙箱引导 | `load_or_init` 在空 home 里克隆索引、经 `xlings install` 装 ninja/patchelf,全部走网络,违反 `--offline`「绝不碰网络」的承诺。实测 offline 空 home 26s / 126 MB → 0.3s;e2e 604 带对照(已引导的 home 不提示);文档中英各补一句 |
Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
#!/usr/bin/env bash
2+
# Ecosystem verification for mcpp 2026.9.5.2, run inside a fresh xlings subos.
3+
#
4+
# ⚠️ EVERY ASSERTION CARRIES ITS OWN `|| fail`. A previous run of this kind was
5+
# reported green because the transport dropped `set -euo pipefail` from the
6+
# first line and the closing banner printed unconditionally. A script whose
7+
# "pass" means "nothing failed" degrades, when that line is gone, into one whose
8+
# "pass" means "it reached the last line", and the two read identically.
9+
set -uo pipefail
10+
11+
VER="${MCPP_VERIFY_VERSION:?set MCPP_VERIFY_VERSION}"
12+
# The store path is the default and the point (see A); MCPP_VERIFY_BIN exists
13+
# so this script can be rehearsed against a working-tree build before a release
14+
# exists, and is never what a release is verified with.
15+
STORE="${MCPP_VERIFY_BIN:-$HOME/.xlings/data/xpkgs/xim-x-mcpp/$VER/bin/mcpp}"
16+
17+
fails=0
18+
fail() { printf 'ASSERT-FAIL: %s\n' "$1"; fails=$((fails + 1)); }
19+
ok() { printf 'ok: %s\n' "$1"; }
20+
have() { command -v "$1" >/dev/null 2>&1; }
21+
22+
section() { printf '\n== %s ==\n' "$1"; }
23+
24+
# ── A. the released binary is the one under test ────────────────────────────
25+
#
26+
# ⭐ The store path, never the shim. `xlings install` has been observed to prune
27+
# bare-name shims that mcpp itself installed, so a shim on PATH may resolve to
28+
# an older version and the whole run would measure the wrong binary.
29+
section "A. identity"
30+
[ -x "$STORE" ] || fail "no released binary at $STORE"
31+
got=$("$STORE" --version 2>&1 | head -1)
32+
[ "$got" = "mcpp $VER" ] || fail "version is '$got', not 'mcpp $VER'"
33+
[ "$got" = "mcpp $VER" ] && ok "$got from the store path"
34+
35+
# ── B. the accelerator surface exists on all three verbs ────────────────────
36+
section "B. the device axis reaches run and test"
37+
for verb in build run test; do
38+
"$STORE" "$verb" --help 2>&1 | grep -q -- '--no-accel' \
39+
|| fail "\`mcpp $verb --help\` does not mention --no-accel"
40+
done
41+
[ "$fails" -eq 0 ] && ok "build, run and test all take --accel/--no-accel"
42+
43+
# ── C. a project that states a floor nothing satisfies is refused ───────────
44+
#
45+
# The point is that the refusal happens BEFORE a compile, and names both values.
46+
section "C. version floors are compared before compiling"
47+
proj=$(mktemp -d)
48+
mkdir -p "$proj/src"
49+
cat > "$proj/mcpp.toml" <<'EOF'
50+
[package]
51+
name = "floor-probe"
52+
version = "0.1.0"
53+
54+
[language]
55+
standard = "c++23"
56+
modules = true
57+
import_std = true
58+
59+
[build]
60+
sources = ["src/*.cpp"]
61+
62+
[targets.floor-probe]
63+
kind = "bin"
64+
main = "src/main.cpp"
65+
EOF
66+
cat > "$proj/src/main.cpp" <<'EOF'
67+
int main() { return 0; }
68+
EOF
69+
cat > "$proj/build.mcpp" <<'EOF'
70+
import std;
71+
import mcpp;
72+
int main() {
73+
mcpp::fact("probe.quantity", "1.0");
74+
mcpp::floor("probe.quantity >= 9.0");
75+
return 0;
76+
}
77+
EOF
78+
out=$(cd "$proj" && "$STORE" build 2>&1)
79+
printf '%s\n' "$out" | grep -q "requires probe.quantity >= 9.0" \
80+
|| fail "an unmet floor did not refuse the build (output: $(printf '%s' "$out" | tail -3 | tr '\n' ' '))"
81+
printf '%s\n' "$out" | grep -q "and this machine has 1.0" \
82+
|| fail "the refusal did not state the measured value"
83+
printf '%s\n' "$out" | grep -qi "compiling floor-probe" \
84+
&& fail "the build compiled before the floor was compared"
85+
printf '%s\n' "$out" | grep -q "requires probe.quantity >= 9.0" && ok "an unmet floor refuses before compiling, naming both values"
86+
87+
# ── C'. the control: a floor that IS met does not refuse ────────────────────
88+
sed -i 's/probe.quantity >= 9.0/probe.quantity >= 0.5/' "$proj/build.mcpp"
89+
out=$(cd "$proj" && "$STORE" build 2>&1)
90+
printf '%s\n' "$out" | grep -q "Finished" \
91+
|| fail "a satisfied floor still refused the build (output: $(printf '%s' "$out" | tail -3 | tr '\n' ' '))"
92+
printf '%s\n' "$out" | grep -q "Finished" && ok "a satisfied floor builds — the check measures the floor, not the machine"
93+
94+
# ── D. a constrained glob narrows, and --no-accel excludes it ───────────────
95+
#
96+
# ⚠️ THE BUILD PROGRAM WRITES A FILE RATHER THAN PRINTING. mcpp shows a build
97+
# program's stdout only when it exits non-zero, so a probe that printed its
98+
# answer would be invisible on every successful build — and a grep that never
99+
# matches is indistinguishable from one that matches nothing.
100+
section "D. constrained source globs"
101+
proj2=$(mktemp -d)
102+
mkdir -p "$proj2/src/kernels"
103+
cat > "$proj2/mcpp.toml" <<'EOF'
104+
[package]
105+
name = "glob-probe"
106+
version = "0.1.0"
107+
accelerators = ["cuda"]
108+
109+
[language]
110+
standard = "c++23"
111+
modules = true
112+
import_std = true
113+
114+
[build]
115+
accel = "cuda12.9+{sm_89}"
116+
sources = [
117+
"src/*.cpp",
118+
{ glob = "src/kernels/**/*.cu", accel = "cuda12.9+{sm_89}" },
119+
]
120+
121+
[targets.glob-probe]
122+
kind = "bin"
123+
main = "src/main.cpp"
124+
EOF
125+
cat > "$proj2/src/main.cpp" <<'EOF'
126+
extern "C" int answer();
127+
int main() { return answer() == 7 ? 0 : 1; }
128+
EOF
129+
cat > "$proj2/src/answer.cpp" <<'EOF'
130+
extern "C" int answer() { return 7; }
131+
EOF
132+
cat > "$proj2/src/kernels/k.cu" <<'EOF'
133+
// Never compiled here: no rule package is imported, so a build that hands this
134+
// file to the engine's compile rules is a build that narrowed wrongly.
135+
#error "the device glob must not reach the engine's compile rules"
136+
EOF
137+
cat > "$proj2/build.mcpp" <<'EOF'
138+
import std;
139+
import mcpp;
140+
int main() {
141+
mcpp::rerun_if_env_changed("MCPP_PROBE_OUT");
142+
const char* out = std::getenv("MCPP_PROBE_OUT");
143+
if (out && *out) {
144+
std::ofstream f(out, std::ios::trunc);
145+
f << "DEVICE_SOURCES=[" << mcpp::device_sources() << "]\n";
146+
f << "ACCEL=[" << mcpp::accel() << "]\n";
147+
}
148+
return 0;
149+
}
150+
EOF
151+
probe_out="$proj2/probe.txt"
152+
out=$(cd "$proj2" && MCPP_PROBE_OUT="$probe_out" "$STORE" build 2>&1)
153+
printf '%s\n' "$out" | grep -q "Finished" \
154+
|| fail "the device build failed (output: $(printf '%s' "$out" | tail -4 | tr '\n' ' '))"
155+
grep -q "DEVICE_SOURCES=\[src/kernels/k.cu\]" "$probe_out" 2>/dev/null \
156+
|| fail "the device source did not reach the build program (probe: $(cat "$probe_out" 2>/dev/null | tr '\n' ' '))"
157+
grep -q "DEVICE_SOURCES=\[src/kernels/k.cu\]" "$probe_out" 2>/dev/null \
158+
&& ok "a constrained glob reaches the build program as a device source"
159+
160+
rm -f "$probe_out"
161+
out=$(cd "$proj2" && MCPP_PROBE_OUT="$probe_out" "$STORE" build --no-accel 2>&1)
162+
printf '%s\n' "$out" | grep -q "Finished" \
163+
|| fail "--no-accel did not build (output: $(printf '%s' "$out" | tail -4 | tr '\n' ' '))"
164+
grep -q "DEVICE_SOURCES=\[\]" "$probe_out" 2>/dev/null \
165+
|| fail "--no-accel did not empty the device source list (probe: $(cat "$probe_out" 2>/dev/null | tr '\n' ' '))"
166+
grep -q "ACCEL=\[\]" "$probe_out" 2>/dev/null \
167+
|| fail "--no-accel did not empty the accel axis"
168+
grep -q "DEVICE_SOURCES=\[\]" "$probe_out" 2>/dev/null \
169+
&& ok "--no-accel excludes the constrained glob and still builds"
170+
171+
out=$(cd "$proj2" && "$STORE" build --accel 'cuda12.9+{sm_80}' 2>&1)
172+
printf '%s\n' "$out" | grep -q "does not cover" \
173+
|| fail "an accel that does not cover the glob was not refused"
174+
printf '%s\n' "$out" | grep -q "does not cover" && ok "an accel outside the constraint is refused naming both"
175+
176+
# ── E. the core holds no vendor probe ───────────────────────────────────────
177+
section "E. no vendor tool is launched by the engine"
178+
out=$("$STORE" self doctor 2>&1)
179+
printf '%s\n' "$out" | grep -qiE "nvcc|cuda toolkit|cicc" \
180+
&& fail "self doctor still reports a device toolkit section"
181+
printf '%s\n' "$out" | grep -qiE "nvcc|cuda toolkit|cicc" || ok "self doctor is silent about device toolkits"
182+
183+
# ── verdict ────────────────────────────────────────────────────────────────
184+
printf '\n'
185+
if [ "$fails" -eq 0 ]; then
186+
printf 'ALL ASSERTIONS PASSED for mcpp %s\n' "$VER"
187+
exit 0
188+
fi
189+
printf '%d ASSERTION(S) FAILED for mcpp %s\n' "$fails" "$VER"
190+
exit 1

0 commit comments

Comments
 (0)