Skip to content

Commit 5731d82

Browse files
committed
docs(plan): round 7 的沙箱验证脚本 —— 五节,跳过的那几节在 summary 里重列
1 parent 13b67da commit 5731d82

1 file changed

Lines changed: 247 additions & 0 deletions

File tree

Lines changed: 247 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,247 @@
1+
#!/usr/bin/env bash
2+
# Ecosystem verification for round 7: one package, one version -- and a rule
3+
# package that brings its own environment. Against a PUBLISHED mcpp and a
4+
# PUBLISHED mcpp:plugins.
5+
#
6+
# # The sandbox has an EMPTY $HOME and a fresh /tmp, so this file is not
7+
# # visible from inside it. Pass the script itself in:
8+
# B64=$(base64 -w0 <this file>)
9+
# xlings subos use verify-966 --sandbox --cmd \
10+
# "echo $B64 | base64 -d > /tmp/v.sh && MCPP_VERIFY_VERSION=2026.9.6.6 bash /tmp/v.sh"
11+
#
12+
# mcpp is addressed by its STORE path, which is the one thing the sandbox does
13+
# share: the xlings data directory. A bare `mcpp` is not on PATH in there.
14+
#
15+
# WHY A SANDBOX IS THE RIGHT PLACE FOR THIS ONE. Every criterion below is about
16+
# what gets INSTALLED. On a machine that has built any of this before, the
17+
# payload is in the registry already and "one version" reads the same whether
18+
# the engine unified anything or not. A sandbox has an empty registry, so the
19+
# question can be asked at all.
20+
#
21+
# EVERY CRITERION NAMES THE OBJECT IT SELECTED, and every section that did not
22+
# run is listed again in the summary. "0 assertions failed" printed by a script
23+
# that skipped four sections is the failure mode this shape exists to prevent.
24+
set -u
25+
26+
VER="${MCPP_VERIFY_VERSION:?set MCPP_VERIFY_VERSION}"
27+
STORE="${MCPP_VERIFY_BIN:-$HOME/.xlings/data/xpkgs/xim-x-mcpp/$VER/bin/mcpp}"
28+
PLUGINS="${MCPP_VERIFY_PLUGINS:-0.2.4}"
29+
30+
fails=0
31+
skipped=""
32+
fail() { printf 'ASSERT-FAIL: %s\n' "$1"; fails=$((fails + 1)); }
33+
ok() { printf 'ok: %s\n' "$1"; }
34+
section() { printf '\n== %s ==\n' "$1"; }
35+
skip() { printf 'NOT RUN: %s\n' "$1"; skipped="$skipped
36+
- $1"; }
37+
38+
work=$(mktemp -d)
39+
trap 'rm -rf "$work"' EXIT
40+
41+
# The store this run installs into, so "how many versions of X are here" is a
42+
# question about THIS run and not about the machine.
43+
XPKGS="$HOME/.mcpp/registry/data/xpkgs"
44+
count_versions() { ls -1 "$XPKGS/xim-x-$1" 2>/dev/null | wc -l | tr -d ' '; }
45+
list_versions() { ls -1 "$XPKGS/xim-x-$1" 2>/dev/null | sort | tr '\n' ' '; }
46+
47+
# ── A. identity ─────────────────────────────────────────────────────────────
48+
section "A. identity"
49+
if [ ! -x "$STORE" ]; then
50+
fail "no released binary at $STORE"
51+
printf 'FAIL: nothing further can run\n'; exit 1
52+
fi
53+
got=$("$STORE" --version 2>&1 | head -1)
54+
if [ "$got" = "mcpp $VER" ]; then ok "$got from $STORE"
55+
else fail "version is '$got' at $STORE"; fi
56+
57+
"$STORE" self config --mirror "${MCPP_VERIFY_MIRROR:-CN}" >/dev/null 2>&1 || true
58+
59+
# ── B. a range is installed AND answered ────────────────────────────────────
60+
#
61+
# Engine-only: no rule package, no index entry beyond the tool itself. This is
62+
# the gap that made a floor unusable -- the payload arrived and `xpkg_dir`
63+
# answered "".
64+
section "B. a range is installed and answered (engine only)"
65+
mkdir -p "$work/b/src"
66+
printf 'int main(){return 0;}\n' > "$work/b/src/main.cpp"
67+
cat > "$work/b/build.mcpp" <<'EOF'
68+
#include <cstdio>
69+
#include <string>
70+
import mcpp;
71+
int main() {
72+
const char* d = mcpp::xpkg_dir("xim", "shaderc");
73+
std::string out = std::string(mcpp::manifest_dir()) + "/answered.txt";
74+
std::FILE* f = std::fopen(out.c_str(), "w");
75+
if (f == nullptr) return 3;
76+
std::fprintf(f, "%s\n", d == nullptr ? "" : d);
77+
std::fclose(f);
78+
return 0;
79+
}
80+
EOF
81+
cat > "$work/b/mcpp.toml" <<'EOF'
82+
[package]
83+
name = "rangecheck"
84+
version = "0.1.0"
85+
[xlings.workspace]
86+
"xim:shaderc" = ">=2026.1"
87+
[build]
88+
sources = ["src/main.cpp"]
89+
[targets.rangecheck]
90+
kind = "bin"
91+
main = "src/main.cpp"
92+
EOF
93+
if ( cd "$work/b" && "$STORE" build >build.log 2>&1 ); then
94+
if [ -s "$work/b/answered.txt" ] && grep -q 'xim-x-shaderc' "$work/b/answered.txt"; then
95+
ok "a range was answered: $(cat "$work/b/answered.txt")"
96+
else
97+
fail "the range installed but xpkg_dir answered '$(cat "$work/b/answered.txt" 2>/dev/null)'"
98+
fi
99+
else
100+
fail "a project declaring a range did not build"
101+
tail -5 "$work/b/build.log"
102+
fi
103+
104+
# …and the other direction, so a build that treats the range as a literal is
105+
# not read as a pass.
106+
mkdir -p "$work/b2/src"
107+
printf 'int main(){return 0;}\n' > "$work/b2/src/main.cpp"
108+
sed 's/>=2026.1/>=2099.1/' "$work/b/mcpp.toml" > "$work/b2/mcpp.toml"
109+
if ( cd "$work/b2" && "$STORE" build >build.log 2>&1 ); then
110+
fail "an unsatisfiable range was accepted"
111+
else
112+
if grep -q 'not found in the synced index' "$work/b2/build.log"; then
113+
ok "an unsatisfiable range is refused, naming it"
114+
else
115+
fail "refused, but not for the stated reason: $(grep -m1 error "$work/b2/build.log")"
116+
fi
117+
fi
118+
119+
# ── C. one edge, and the rule brings its environment ────────────────────────
120+
section "C. one edge, no [xlings.workspace] anywhere in the project"
121+
mk_spirv_project() { # $1 = dir, $2 = extra manifest text
122+
mkdir -p "$1/src" "$1/shaders"
123+
cat > "$1/shaders/scale.comp" <<'EOF'
124+
#version 450
125+
layout(local_size_x = 64) in;
126+
layout(std430, binding = 0) buffer Data { float v[]; };
127+
layout(push_constant) uniform Push { float a; uint n; } push;
128+
void main() {
129+
const uint i = gl_GlobalInvocationID.x;
130+
if (i >= push.n) return;
131+
v[2u * push.n + i] = push.a * v[i] + v[push.n + i];
132+
}
133+
EOF
134+
cat > "$1/src/main.cpp" <<'EOF'
135+
#include <cstdint>
136+
#include <cstdio>
137+
#include "scale_comp.h"
138+
int main() {
139+
const std::uint32_t magic = scale_comp_spv[0];
140+
std::printf("magic=%08x\n", magic);
141+
return magic == 0x07230203u ? 0 : 1;
142+
}
143+
EOF
144+
cat > "$1/build.mcpp" <<'EOF'
145+
import std;
146+
import mcpp;
147+
import mcpp.rules.spirv;
148+
int main() {
149+
mcpp::rerun_if_changed_glob("shaders/**/*.comp");
150+
mcpp::rules::spirv::options opt;
151+
opt.includes = { "shaders" };
152+
return mcpp::rules::spirv::compile(opt) ? 0 : 1;
153+
}
154+
EOF
155+
cat > "$1/mcpp.toml" <<EOF
156+
[package]
157+
name = "onedge"
158+
version = "0.1.0"
159+
accelerators = ["vulkan"]
160+
161+
[language]
162+
standard = "c++23"
163+
modules = true
164+
import_std = true
165+
166+
[build-dependencies.mcpp]
167+
plugins = { version = "$PLUGINS", features = ["rules-spirv"], host-module = true }
168+
169+
[build]
170+
accel = "vulkan1.2"
171+
sources = [
172+
"src/*.cpp",
173+
{ glob = "shaders/*.comp", accel = "vulkan1.2" },
174+
]
175+
$2
176+
[targets.onedge]
177+
kind = "bin"
178+
main = "src/main.cpp"
179+
EOF
180+
}
181+
182+
mk_spirv_project "$work/c" ""
183+
if ( cd "$work/c" && "$STORE" build >build.log 2>&1 ); then
184+
if grep -q 'entries declared by dependencies' "$work/c/build.log"; then
185+
ok "the payload came from the rule: $(grep -m1 'entries declared by dependencies' "$work/c/build.log")"
186+
else
187+
fail "it built, but nothing was provisioned from the graph -- the rule declared no payload"
188+
fi
189+
out=$( cd "$work/c" && "$STORE" run 2>&1 | tail -1 )
190+
if [ "$out" = "magic=07230203" ]; then ok "and it runs: $out"
191+
else fail "ran and printed '$out'"; fi
192+
else
193+
# THE SKIP IS RECOGNISED BY THE RESOLUTION ERROR, NOT BY "it failed and the
194+
# log mentions plugins". `E_NOT_FOUND` with the wire address is a sentence
195+
# only the resolver writes; a compile or link failure cannot produce it, so
196+
# a genuine defect is never filed as "not published yet".
197+
if grep -q "E_NOT_FOUND: package 'mcpp:plugins@$PLUGINS' not found" "$work/c/build.log"; then
198+
skip "C: mcpp:plugins@$PLUGINS is not in the index yet"
199+
skip "D: needs C"
200+
skip "E: needs C"
201+
else
202+
fail "a project writing only the plugins edge did not build"
203+
tail -15 "$work/c/build.log"
204+
fi
205+
fi
206+
207+
# ── D. the override: one version, and the nearer declaration wins ───────────
208+
if [ -d "$work/c/target" ]; then
209+
section "D. the project's own pin overrides the rule's, and only one is installed"
210+
mk_spirv_project "$work/d" '
211+
[target.'"'"'cfg(accelerator = "vulkan")'"'"'.xlings.workspace]
212+
"xim:glslang" = "15.1.0"
213+
'
214+
if ( cd "$work/d" && "$STORE" build >build.log 2>&1 ); then
215+
n=$(count_versions glslang)
216+
if [ "$n" = "1" ]; then ok "one glslang installed: $(list_versions glslang)"
217+
else fail "two declarations of one package installed $n versions: $(list_versions glslang)"; fi
218+
else
219+
fail "the override did not build"
220+
tail -10 "$work/d/build.log"
221+
fi
222+
223+
# ── E. a pin below the rule's floor is refused, naming both ─────────────
224+
section "E. a pin below the rule's stated floor is refused"
225+
mk_spirv_project "$work/e" '
226+
[target.'"'"'cfg(accelerator = "vulkan")'"'"'.xlings.workspace]
227+
"xim:glslang" = "1.0.0"
228+
'
229+
if ( cd "$work/e" && "$STORE" build >build.log 2>&1 ); then
230+
fail "a pin below the rule's floor was accepted"
231+
else
232+
miss=""
233+
for needle in 'xim:glslang' '1.0.0' 'mcpp' 'drop the pin'; do
234+
grep -q -- "$needle" "$work/e/build.log" || miss="$miss $needle"
235+
done
236+
if [ -z "$miss" ]; then ok "refused, naming both sides and the way out"
237+
else fail "refused, but the message omits:$miss"; fi
238+
fi
239+
fi
240+
241+
# ── summary ────────────────────────────────────────────────────────────────
242+
section "summary"
243+
printf '%s assertion(s) failed\n' "$fails"
244+
if [ -n "$skipped" ]; then
245+
printf 'sections that did NOT run:%s\n' "$skipped"
246+
fi
247+
[ "$fails" -eq 0 ] || exit 1

0 commit comments

Comments
 (0)