Skip to content

Commit 39053b9

Browse files
committed
fix(emit): the selector digest separates values with newlines
A \x1f separator followed by `f`, `c` or `a` (`\x1ffeatures`, `\x1fcap`, `\x1faccel`) is one longer hex escape; clang refuses it as out of range and every clang host failed to build the branch. GCC accepted it, which is why the Linux build did not see it. The sandbox verification script for the release is added alongside.
1 parent 5cfb03a commit 39053b9

2 files changed

Lines changed: 136 additions & 2 deletions

File tree

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
#!/usr/bin/env bash
2+
# Ecosystem verification for the design record
3+
# 2026-09-14-636-build-database-and-the-latest-xlings.md, against a PUBLISHED
4+
# mcpp and the xlings it pins.
5+
#
6+
# B64=$(base64 -w0 .agents/docs/2026-09-14-636-verify.sh)
7+
# xlings subos new verify-636 # once
8+
# xlings subos use verify-636 --sandbox --cmd \
9+
# "echo $B64 | base64 -d > /tmp/v.sh && MCPP_VERIFY_VERSION=<mcpp> XLINGS_VERIFY_VERSION=<xlings> bash /tmp/v.sh"
10+
#
11+
# The sandbox starts from an empty $HOME and a fresh /tmp and shares the xlings
12+
# data directory, so the published mcpp is addressed by its store path. The mcpp
13+
# home is removed first: every section reads this release, not a previous run.
14+
# A section that cannot run says so and is listed again in the summary.
15+
set -u
16+
17+
VER="${MCPP_VERIFY_VERSION:?set MCPP_VERIFY_VERSION}"
18+
XVER="${XLINGS_VERIFY_VERSION:?set XLINGS_VERIFY_VERSION}"
19+
STORE="${MCPP_VERIFY_BIN:-$HOME/.xlings/data/xpkgs/xim-x-mcpp/$VER/bin/mcpp}"
20+
21+
fails=0
22+
skipped=""
23+
fail() { printf 'ASSERT-FAIL: %s\n' "$1"; fails=$((fails + 1)); }
24+
ok() { printf 'ok: %s\n' "$1"; }
25+
section() { printf '\n== %s ==\n' "$1"; }
26+
skip() { printf 'NOT RUN: %s\n' "$1"; skipped="$skipped
27+
- $1"; }
28+
29+
root=/tmp/verify-636
30+
rm -rf "$root" "$HOME/.mcpp"; mkdir -p "$root"
31+
REG="$HOME/.mcpp/registry"
32+
33+
section "A. the published mcpp, the CN mirror, and the xlings it vendors"
34+
if [ ! -x "$STORE" ]; then
35+
fail "no mcpp at $STORE"; printf '\nfails=%d (nothing else can run)\n' "$fails"; exit 1
36+
fi
37+
got=$("$STORE" --version 2>&1 | head -1)
38+
case "$got" in *"$VER"*) ok "mcpp --version says $got" ;; *) fail "mcpp --version says '$got', expected $VER" ;; esac
39+
"$STORE" self config --mirror CN > "$root/mirror.log" 2>&1 && ok "mcpp self config --mirror CN" || { fail "mcpp self config --mirror CN"; tail -3 "$root/mirror.log"; }
40+
grep -q '"mirror": "CN"' "$REG/.xlings.json" 2>/dev/null && ok "the registry's xlings configuration reads mirror CN" || fail "the registry's .xlings.json does not read mirror CN"
41+
"$STORE" self env > "$root/env.log" 2>&1
42+
grep -q "xlings pinned = $XVER" "$root/env.log" && ok "the pin is xlings $XVER" || { fail "mcpp self env does not pin xlings $XVER"; grep 'xlings pinned' "$root/env.log"; }
43+
vend=$("$REG/bin/xlings" --version 2>/dev/null | head -1)
44+
case "$vend" in *"$XVER"*) ok "the vendored xlings is $vend" ;; *) fail "the vendored xlings is '$vend', expected $XVER" ;; esac
45+
46+
section "B. a hookless package after a hook package: its own archive, nothing else"
47+
d=$root/b; rm -rf "$d"; mkdir -p "$d/src"
48+
cat > "$d/mcpp.toml" <<'EOF'
49+
[package]
50+
name = "eco636"
51+
version = "0.1.0"
52+
standard = "c++23"
53+
54+
[dependencies]
55+
mcpplibs.cmdline = "0.0.1"
56+
EOF
57+
printf 'import std;\nimport mcpplibs.cmdline;\nint main() { std::println("1-2-3"); }\n' > "$d/src/main.cpp"
58+
if (cd "$d" && "$STORE" build > build.log 2>&1); then
59+
ok "a project with a toolchain install and a hookless mcpp-index dependency builds"
60+
swept=$(find "$REG/data/xpkgs" -mindepth 3 -maxdepth 3 -name '*.lock' 2>/dev/null)
61+
[ -z "$swept" ] && ok "no store payload carries a download sidecar" || { fail "store payloads carry download sidecars"; printf '%s\n' "$swept" | head -5; }
62+
cmd=$(find "$REG/data/xpkgs" -maxdepth 2 -path '*cmdline*' -type d | tail -1)
63+
if [ -n "$cmd" ]; then
64+
top=$(ls -A "$cmd" | grep -v '^\.' | tr '\n' ' ')
65+
case "$top" in
66+
*.tar.gz*|*.zip*) fail "the cmdline payload holds an archive: $top" ;;
67+
*) ok "the cmdline payload's top level is its own archive's entries: $top" ;;
68+
esac
69+
[ -n "$(find "$cmd" -mindepth 2 -maxdepth 2 -name mcpp.toml)" ] && ok "the top-level directory is kept, so */mcpp.toml matches" || fail "no */mcpp.toml under $cmd"
70+
else
71+
skip "B: the cmdline payload directory was not found"
72+
fi
73+
(cd "$d" && "$STORE" run > run.log 2>&1); grep -qx '1-2-3' "$d/run.log" && ok "mcpp run prints 1-2-3" || { fail "mcpp run"; tail -3 "$d/run.log"; }
74+
else
75+
fail "the dependency build failed"; tail -8 "$d/build.log"
76+
fi
77+
78+
section "C. mcpp emit build-database writes nothing and describes the plan"
79+
d=$root/c; rm -rf "$d"; mkdir -p "$d/src" "$d/tests"
80+
cat > "$d/mcpp.toml" <<'EOF'
81+
[package]
82+
name = "hello"
83+
version = "0.1.0"
84+
standard = "c++23"
85+
EOF
86+
printf 'export module hello.greet;\nexport import :detail;\nimport std;\nexport std::string greet();\n' > "$d/src/greet.cppm"
87+
printf 'export module hello.greet:detail;\nexport int answer() { return 42; }\n' > "$d/src/detail.cppm"
88+
printf 'module hello.greet;\nstd::string greet() { return "hi"; }\n' > "$d/src/greet_impl.cpp"
89+
printf 'import hello.greet;\nimport std;\nint main() { std::println("{}", greet()); }\n' > "$d/src/main.cpp"
90+
printf 'int main() { return 0; }\n' > "$d/tests/test_smoke.cpp"
91+
before=$(cd "$d" && find . -type f | sort | xargs sha256sum | sha256sum)
92+
if (cd "$d" && "$STORE" emit build-database --format json > "$root/c.json" 2> "$root/c.err"); then
93+
after=$(cd "$d" && find . -type f | sort | xargs sha256sum | sha256sum)
94+
[ "$before" = "$after" ] && ok "the project tree is unchanged" || fail "the project tree changed"
95+
if python3 - "$root/c.json" <<'EOF'
96+
import json, os, sys
97+
e = json.load(open(sys.argv[1]))
98+
assert e["kind"] == "mcpp.build-database" and "write-project" not in e["effects"]
99+
db = e["data"]["database"]
100+
sets = {s["name"]: s for s in db["sets"]}
101+
assert set(sets) == {"hello", "hello:test", "mcpp:std"}, sorted(sets)
102+
roles = {os.path.basename(u["source"]): u["ide"]["role"] for u in sets["hello"]["translation-units"]}
103+
assert roles == {"greet.cppm": "module-interface", "detail.cppm": "module-partition-interface",
104+
"greet_impl.cpp": "module-implementation", "main.cpp": "non-module"}, roles
105+
std = sets["mcpp:std"]["translation-units"][0]
106+
assert std["provides"] == {"std": ""} and os.path.isfile(std["source"]), std
107+
print("sets", sorted(sets), "units", sum(len(s["translation-units"]) for s in db["sets"]))
108+
EOF
109+
then ok "the S1 document has the sets, roles and std unit SPEC-005 states"
110+
else fail "the S1 document"; fi
111+
else
112+
fail "emit build-database failed"; tail -5 "$root/c.err"
113+
fi
114+
115+
section "D. xlings reports and repairs a payload that holds another package's download"
116+
cmd=$(find "$REG/data/xpkgs" -maxdepth 2 -path '*cmdline*' -type d | tail -1)
117+
if [ -n "$cmd" ]; then
118+
printf 'x' > "$cmd/intruder-1.0-linux-x86_64.tar.gz"; : > "$cmd/intruder-1.0-linux-x86_64.tar.gz.lock"
119+
XLINGS_HOME="$REG" "$REG/bin/xlings" self doctor > "$root/doctor.log" 2>&1
120+
grep -qi 'intruder\|swept\|download' "$root/doctor.log" && ok "self doctor reports the swept payload" || { fail "self doctor did not report it"; tail -8 "$root/doctor.log"; }
121+
XLINGS_HOME="$REG" "$REG/bin/xlings" self doctor --fix > "$root/fix.log" 2>&1
122+
[ ! -e "$cmd/intruder-1.0-linux-x86_64.tar.gz.lock" ] && ok "self doctor --fix leaves no download sidecar in the payload" || { fail "the sidecar is still there after --fix"; tail -8 "$root/fix.log"; }
123+
else
124+
skip "D: section B produced no cmdline payload to seed"
125+
fi
126+
127+
section "E. Windows: the probe reaches no shell"
128+
skip "E: a Linux sandbox cannot run cmd.exe; e2e 687 and XlingsVersionPin.ProbeReadsStandardOutputThroughTheLauncher are the Windows criteria (CI)"
129+
130+
printf '\n== summary ==\nfails=%d\n' "$fails"
131+
[ -n "$skipped" ] && printf 'not run:%s\n' "$skipped"
132+
[ "$fails" -eq 0 ]

src/cli/cmd_build.cppm

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -403,9 +403,11 @@ export int cmd_emit_build_database(const mcpplibs::cmdline::ParsedArgs& parsed)
403403
if (std::filesystem::exists(sp.root / "build.mcpp", ec)) ranBuildPrograms = true;
404404
}
405405
}
406+
// One line per selector: a value never spans lines, and a `\x1f` separator
407+
// before `f`, `c` or `a` reads as a longer hex escape (clang refuses it).
406408
const auto selector = std::format(
407-
"spec={}\x1ftarget={}\x1ftoolchain={}\x1fprofile={}\x1ffeatures={}\x1f"
408-
"cap={}\x1faccel={}\x1fstatic={}\x1fpackage={}\x1fworkspace={}",
409+
"spec={}\ntarget={}\ntoolchain={}\nprofile={}\nfeatures={}\n"
410+
"cap={}\naccel={}\nstatic={}\npackage={}\nworkspace={}",
409411
spec, ov.target_triple, mcpp::platform::env::get("MCPP_TOOLCHAIN").value_or(""),
410412
ov.profile, ov.features, ov.capabilities, ov.accel, ov.force_static,
411413
ov.package_filter, parsed.is_flag_set("workspace"));

0 commit comments

Comments
 (0)