|
| 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