Skip to content

Commit 95c52dc

Browse files
committed
docs: the sandbox verification script of 2026.9.30.2
Eight sections against the published release: the three #732 cases, index packages built with it, the status row's count against the units the cache placed, #744's source choice, and the planning timers. Run locally against the branch: 8 passed; against 2026.9.30.1 as the control, every section marked CHANGE fails.
1 parent a0ef3c2 commit 95c52dc

1 file changed

Lines changed: 151 additions & 0 deletions

File tree

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
#!/usr/bin/env bash
2+
# Sandbox verification of mcpp 2026.9.30.2 (.agents/docs/2026-09-30-build-
3+
# wall-time-progress-count-and-hang-plan.md), run against the PUBLISHED release
4+
# inside an xlings sandbox:
5+
#
6+
# B64=$(base64 -w0 .agents/docs/2026-09-30-build-wall-time-verify.sh)
7+
# xlings subos new v9302 2>/dev/null || true
8+
# xlings subos use v9302 --sandbox --cmd "echo $B64 | base64 -d > /tmp/v.sh && VER=2026.9.30.2 bash /tmp/v.sh"
9+
#
10+
# VER selects the release under test. Running it with VER=2026.9.30.1 is the
11+
# control: every section marked CHANGE must fail there, and every other section
12+
# must pass on both.
13+
#
14+
# Every probe directory is removed at the start of its section, because the
15+
# sandbox's $HOME persists between runs of the same subos. A section that does
16+
# not run is reported as SKIP and counted separately from a pass.
17+
set -u
18+
VER="${VER:-2026.9.30.2}"
19+
W="$HOME/v9302"
20+
fails=0; passes=0; skips=0
21+
pass() { echo "PASS $1"; passes=$((passes+1)); }
22+
fail() { echo "FAIL $1"; [ -n "${2:-}" ] && [ -f "$2" ] && tail -15 "$2"; fails=$((fails+1)); }
23+
skip() { echo "SKIP $1"; skips=$((skips+1)); }
24+
25+
# ── 0. the release under test, from the published channel ──────────────────
26+
if [ -n "${MCPP_OVERRIDE:-}" ]; then
27+
MCPP="$MCPP_OVERRIDE"
28+
else
29+
xlings config --mirror CN >/dev/null 2>&1 || true
30+
xlings update >/dev/null 2>&1 || true
31+
xlings install "mcpp@$VER" -y > /tmp/v9302-install.log 2>&1 || true
32+
MCPP="$HOME/.xlings/data/xpkgs/xim-x-mcpp/$VER/bin/mcpp"
33+
fi
34+
if [ ! -x "$MCPP" ]; then
35+
echo "FATAL: mcpp $VER is not installable from the index"; tail -20 /tmp/v9302-install.log; exit 2
36+
fi
37+
got=$("$MCPP" --version 2>&1 | head -1)
38+
case "$got" in *"$VER"*) pass "0 installed: $got";; *) fail "0 version: $got";; esac
39+
"$MCPP" self config --mirror CN >/dev/null 2>&1 || true
40+
mkdir -p "$W"
41+
42+
module_file() { printf 'export module boost;\nexport int value() { return %s; }\n' "$2" > "$1"; }
43+
main_file() { printf '#include <cstdio>\nimport boost;\nint main() { std::printf("%%d\\n", value()); }\n' > "$1"; }
44+
bin_of() { find "$1" -path '*/bin/*' -name "$2" -type f | head -1; }
45+
46+
# ── 1. CHANGE: #732, an artifacts program with its own module of one name ──
47+
rm -rf "$W/s1"; mkdir -p "$W/s1/app/src" "$W/s1/updater/src"; cd "$W/s1"
48+
module_file app/src/boost.cppm 1; main_file app/src/main.cpp
49+
module_file updater/src/boost.cppm 2; main_file updater/src/main.cpp
50+
printf '[package]\nname = "updater"\nversion = "0.1.0"\n\n[targets.updater]\nkind = "bin"\nmain = "src/main.cpp"\n' > updater/mcpp.toml
51+
printf '[package]\nname = "app"\nversion = "0.1.0"\n\n[dependencies]\nupdater = { path = "../updater", artifacts = ["updater"] }\n\n[targets.app]\nkind = "bin"\nmain = "src/main.cpp"\n' > app/mcpp.toml
52+
if (cd app && "$MCPP" build > ../s1.log 2>&1) \
53+
&& [ "$("$(bin_of app/target app)")" = 1 ] && [ "$("$(bin_of app/target updater)")" = 2 ]; then
54+
pass "1 CHANGE: the app and its artifacts updater each have their own module boost"
55+
else fail "1 CHANGE: two programs, one module name" s1.log; fi
56+
57+
# ── 2. #732, two providers in one program are refused ───────────────────────
58+
rm -rf "$W/s2"; mkdir -p "$W/s2/lib1/src" "$W/s2/lib2/src" "$W/s2/prog/src"; cd "$W/s2"
59+
module_file lib1/src/boost.cppm 1; module_file lib2/src/boost.cppm 2
60+
for l in lib1 lib2; do printf '[package]\nname = "%s"\nversion = "0.1.0"\n' "$l" > $l/mcpp.toml; done
61+
printf 'int main() { return 0; }\n' > prog/src/main.cpp
62+
printf '[package]\nname = "prog"\nversion = "0.1.0"\n\n[dependencies]\nlib1 = { path = "../lib1" }\nlib2 = { path = "../lib2" }\n\n[targets.prog]\nkind = "bin"\nmain = "src/main.cpp"\n' > prog/mcpp.toml
63+
if (cd prog && "$MCPP" build > ../s2.log 2>&1); then fail "2 two providers in one program were accepted" s2.log
64+
elif grep -q "module 'boost' is provided by package" s2.log; then pass "2 two providers in one program are refused"
65+
else fail "2 the refusal does not name the module" s2.log; fi
66+
67+
# ── 3. CHANGE: #732, two workspace members with one module name ─────────────
68+
rm -rf "$W/s3"; mkdir -p "$W/s3/one/src" "$W/s3/two/src"; cd "$W/s3"
69+
module_file one/src/boost.cppm 5; main_file one/src/main.cpp
70+
module_file two/src/boost.cppm 6; main_file two/src/main.cpp
71+
printf '[workspace]\nmembers = ["one", "two"]\n' > mcpp.toml
72+
for m in one two; do printf '[package]\nname = "%s"\nversion = "0.1.0"\n\n[targets.%s]\nkind = "bin"\nmain = "src/main.cpp"\n' "$m" "$m" > $m/mcpp.toml; done
73+
if "$MCPP" build --workspace > s3.log 2>&1 \
74+
&& [ "$("$(bin_of target one)")" = 5 ] && [ "$("$(bin_of target two)")" = 6 ]; then
75+
pass "3 CHANGE: two workspace members each have their own module boost"
76+
else fail "3 CHANGE: two workspace members, one module name" s3.log; fi
77+
78+
# ── 4. index packages build and run with the release ───────────────────────
79+
rm -rf "$W/s4"; mkdir -p "$W/s4/src"; cd "$W/s4"
80+
printf '[package]\nname = "eco"\nversion = "0.1.0"\n\n[dependencies]\n"compat.zlib" = "*"\n"mcpplibs.cmdline" = "*"\n\n[targets.eco]\nkind = "bin"\nmain = "src/main.cpp"\n' > mcpp.toml
81+
cat > src/main.cpp <<'EOF'
82+
#include <cstdio>
83+
#include <zlib.h>
84+
import mcpplibs.cmdline;
85+
int heavy();
86+
int main() { std::printf("zlib %s\n", zlibVersion()); return heavy() == 0; }
87+
EOF
88+
# One unit that takes a few seconds to compile, so that section 5's build
89+
# outlives the half second before the status row is first drawn.
90+
cat > src/heavy.cpp <<'EOF'
91+
#include <format>
92+
#include <regex>
93+
#include <string>
94+
int heavy() {
95+
std::regex r("([a-z]+)-([0-9]+)");
96+
std::smatch m;
97+
std::string s = std::format("{}-{}", "mcpp", 2026);
98+
return std::regex_match(s, m, r) ? static_cast<int>(m.size()) : 0;
99+
}
100+
EOF
101+
if "$MCPP" build > s4.log 2>&1 && "$(bin_of target eco)" | grep -q '^zlib '; then
102+
pass "4 compat.zlib and mcpplibs.cmdline from the index build and run"
103+
else fail "4 index packages" s4.log; fi
104+
105+
# ── 5. CHANGE: the status row counts the work of the build ─────────────────
106+
# A pty makes the status row appear; the project of section 4 has a
107+
# dependency the global cache serves after its first build.
108+
cd "$W/s4"
109+
if command -v script > /dev/null 2>&1 && [ -f s4.log ] && grep -q 'Finished' s4.log; then
110+
"$MCPP" clean > /dev/null 2>&1
111+
MCPP_PROGRESS=plain script -qefc "stty cols 160 rows 40; $MCPP build" s5.pty > /dev/null 2>&1
112+
rows=$(sed 's/\x1b\[[0-9;?]*[A-Za-z]//g' s5.pty | tr '\r' '\n')
113+
# The units the cache placed, from the `Cached ... (N units)` lines; the
114+
# Building total must not include them (2026.9.30.1 counted each one).
115+
placed=$(echo "$rows" | grep -a '^ *Cached ' | grep -ao '[0-9]* units\?)' | grep -o '^[0-9]*' \
116+
| awk '{s += $1} END {print s + 0}')
117+
building=$(echo "$rows" | grep -ao 'Building [0-9]*/[0-9]*' | head -1)
118+
total=${building##*/}
119+
if [ -n "$total" ] && [ "$placed" -gt 0 ] && [ "$total" -lt "$placed" ]; then
120+
pass "5 CHANGE: Building counts $total steps, not the $placed units placed from the cache"
121+
else fail "5 CHANGE: the status row (first Building: '$building', units placed: $placed)" s5.pty; fi
122+
else skip "5 no script(1) or section 4 did not build"; fi
123+
124+
# ── 6. CHANGE: #744, the vendored xlings comes from the newest source ──────
125+
rm -rf "$W/s6"; mkdir -p "$W/s6/rel/bin" "$W/s6/rel/registry/bin" "$W/s6/pathbin"; cd "$W/s6"
126+
NINJA=$(ls "$HOME"/.mcpp/registry/data/xpkgs/xim-x-ninja/*/ninja 2>/dev/null | head -1)
127+
REAL="$HOME/.mcpp/registry/bin/xlings"
128+
if [ -n "$NINJA" ] && [ -x "$REAL" ]; then
129+
older=$("$NINJA" --version | head -1)
130+
export_home="$W/s6/home"
131+
cp "$MCPP" rel/bin/mcpp
132+
cp "$NINJA" rel/registry/bin/xlings
133+
cp "$REAL" pathbin/xlings
134+
MCPP_HOME="$export_home" MCPP_OFFLINE=1 rel/bin/mcpp self env > setup.log 2>&1 || true
135+
mkdir -p "$export_home/registry/bin"; rm -f "$export_home/registry/bin/xlings"; cp "$NINJA" "$export_home/registry/bin/xlings"
136+
env -u MCPP_VENDORED_XLINGS MCPP_HOME="$export_home" MCPP_OFFLINE=1 PATH="$W/s6/pathbin:/usr/bin:/bin" \
137+
rel/bin/mcpp self env > s6.out 2> s6.err || true
138+
if grep -q "vendored xlings $older -> .* from PATH" s6.err; then
139+
pass "6 CHANGE: a newer xlings on PATH replaced the vendored one past an older released copy"
140+
else fail "6 CHANGE: #744" s6.err; fi
141+
else skip "6 no ninja payload or vendored xlings to stand in"; fi
142+
143+
# ── 7. planning states its phases ──────────────────────────────────────────
144+
cd "$W/s4" && touch src/main.cpp
145+
if MCPP_VERBOSE=1 "$MCPP" build > s7.log 2>&1 && grep -q 'build/stage: plan scan' s7.log; then
146+
pass "7 CHANGE: planning states its phases under build/stage"
147+
else fail "7 CHANGE: the planning phase timers" s7.log; fi
148+
149+
echo
150+
echo "RESULT: $passes passed, $fails failed, $skips skipped (mcpp $VER)"
151+
[ "$fails" -eq 0 ]

0 commit comments

Comments
 (0)