11#! /usr/bin/env bash
2- # End-to-end check for `dist-web` (#622 B3), worked around one engine defect .
2+ # End-to-end check for `dist-web` (#622 B3): the one-command form .
33#
4- # THE INTENDED FORM IS ONE COMMAND, AND IT IS NOT WHAT THIS SCRIPT RUNS:
4+ # mcpp pack --format web --target wasm32-emscripten
55#
6- # mcpp pack --target wasm32-emscripten --format web
6+ # An earlier revision of this script worked around a host-toolchain defect
7+ # that made every `build.mcpp` -- not only this member's -- fail to compile
8+ # under `--target wasm32-emscripten` (prepare.cppm's `host_tc_for_build_program`
9+ # resolved the row's own default pin as if it were a host-native compiler).
10+ # That is fixed (mcpp commit 23974c5e, "a build program under a target row's
11+ # pin resolves the host's compiler, not the row's", #622 e2e 657), so this
12+ # script now runs the real command directly and asserts its real output.
713#
8- # On the engine this member was developed and reviewed against
9- # (mcpp 2026.9.12.2, built from `feat/622-ui-framework-on-three-rows`),
10- # EVERY `build.mcpp` -- not only this member's -- fails to compile under
11- # `--target wasm32-emscripten`:
12- #
13- # error: build.mcpp failed to compile (exit 1):
14- # ... AssertionError (emcc.py, compile_source_file, phase_compile_inputs)
15- #
16- # Measured cause: `prepare.cppm`'s `host_tc_for_build_program` resolves the
17- # HOST toolchain for `build.mcpp` from the closure-captured `tcSpec`, read
18- # LAZILY at the point a `build.mcpp` is found to exist -- but the row's own
19- # default toolchain pin (`tcSpec = targetPinCandidate;`, prepare.cppm
20- # around line 7233, the same assignment the "target default for
21- # wasm32-emscripten, replacing your gcc@16.1.0" message reports) has ALREADY
22- # overwritten that variable by the time this runs, for every target whose
23- # row carries one. `em++` cannot compile a host-native binary under any
24- # invocation, so `build.mcpp` is asked to compile itself with a compiler
25- # that structurally cannot produce a program the host can run: this is not
26- # specific to `dist-web`, `mcpp.plugins`, or this fixture -- a project whose
27- # ENTIRE `build.mcpp` is `import std; int main(){}` fails identically.
28- # `riscv64`/Android rows are not obviously affected, because their default
29- # pins are cross-CONFIGURATIONS of a compiler family (gcc, clang) that also
30- # has a native mode; `emsdk`'s em++ has none. Reported rather than worked
31- # around in the engine: see this task's final report for the full
32- # repro and the suggested fix site.
33- #
34- # WHAT THIS SCRIPT MEASURES INSTEAD, split across the one seam the defect
35- # does not reach:
36- #
37- # 1. A REAL wasm32-emscripten build, of the SAME `src/main.cpp`, through a
38- # throwaway package with NO `build.mcpp` at all -- unaffected, because
39- # the defect is specific to a build.mcpp existing. `mcpp pack --format
40- # dir` stages the real `.js`/`.wasm` pair the emsdk payload produced.
41- # 2. This fixture's OWN `build.mcpp`, compiled for the HOST (an ordinary
42- # `mcpp build`, no `--target`) -- also unaffected, because the defect
43- # is specific to a CROSS target. This compiles to an ordinary ELF at
44- # `target/.build-mcpp/build.mcpp.bin`.
45- # 3. That compiled binary, run DIRECTLY (bypassing mcpp's own
46- # orchestration) with the environment contract `--target
47- # wasm32-emscripten --format web` would have set -- `mcpp::target_os()`
48- # and its siblings are `std::getenv` and nothing else
49- # (`hostprogram.cppm`), so this exercises exactly the code `plan_for`
50- # and `submit` run, against the REAL staged tree from step 1 plus one
51- # hand-placed file standing in for `mcpp::deploy` (which, like every
52- # `build.mcpp` directive, needs the orchestrator this defect removes).
53- # 4. Every `mcpp:action=` line step 3 printed, parsed with `jq` and
54- # EXECUTED by this script -- standing in for ninja, which never ran.
55- #
56- # The resulting `target/web/` directory, and the fact that `node` runs the
57- # `.js` inside it and prints `1-2-3`, are real; only the orchestration that
58- # would normally produce them is simulated here.
59- #
60- # Usage: MCPP=<fresh mcpp> ./check-web-plan.sh (run from this directory)
14+ # Usage: MCPP=<mcpp with the 23974c5e fix> ./check-web-plan.sh (run from
15+ # this directory)
6116set -e
6217
6318MCPP=" ${MCPP:- mcpp} "
64- HERE=" $PWD "
6519fail () { echo " FAIL: $1 " ; shift ; for f in " $@ " ; do echo " --- $f ---" ; cat " $f " 2> /dev/null; done ; exit 1; }
6620
67- command -v jq > /dev/null || fail " jq is required to parse the planned mcpp:action= lines" /dev/null
6821command -v node > /dev/null || fail " node is required to run the produced launcher" /dev/null
6922
70- echo " == step 1: a real wasm32-emscripten build, with no build.mcpp =="
71- REALDIR=$( mktemp -d)
72- mkdir -p " $REALDIR /src"
73- cat > " $REALDIR /mcpp.toml" << EOF
74- [package]
75- name = "web-consumer"
76- version = "0.1.0"
77-
78- [targets.web-consumer]
79- kind = "bin"
80- main = "src/main.cpp"
81- EOF
82- cp src/main.cpp " $REALDIR /src/main.cpp"
83- ( cd " $REALDIR " && " $MCPP " pack --target wasm32-emscripten --format dir > pack.log 2>&1 ) \
84- || fail " the real wasm32-emscripten build failed" " $REALDIR /pack.log"
85- STAGE=$( find " $REALDIR /target/dist" -maxdepth 1 -type d -name ' web-consumer-*-wasm32-emscripten' )
86- [ -n " $STAGE " ] || fail " no staged directory from the real build" " $REALDIR /pack.log"
87- [ -f " $STAGE /bin/web-consumer.js" ] || fail " the real build produced no bin/web-consumer.js"
88- [ -f " $STAGE /bin/web-consumer.wasm" ] || fail " the real build produced no bin/web-consumer.wasm"
89- echo " ok: real bin/web-consumer.js and bin/web-consumer.wasm, from the emsdk payload"
90-
91- # Stand in for `mcpp::deploy(generated, "assets/greeting.txt")`, which
92- # build.mcpp.bin below calls but which nothing here can act on, per this
93- # script's header comment.
94- mkdir -p " $STAGE /bin/assets"
95- printf ' hello from build.mcpp\n' > " $STAGE /bin/assets/greeting.txt"
96-
97- echo " == step 2: this fixture's build.mcpp, compiled for the HOST =="
9823rm -rf target
99- " $MCPP " build > build.log 2>&1 || fail " the host build of this fixture failed" build.log
100- BIN=target/.build-mcpp/build.mcpp.bin
101- [ -x " $BIN " ] || fail " no compiled build.mcpp.bin at $BIN " build.log
102- echo " ok: build.mcpp compiled to $BIN "
10324
104- echo " == step 3: run it directly under the wasm32-emscripten + --format web contract =="
105- OUT=$( mktemp -d)
106- PLANLOG=$( mktemp)
107- env -i PATH=" $PATH " \
108- MCPP_PACK_FORMAT=web \
109- MCPP_TARGET_OS=emscripten \
110- MCPP_PACK_STAGE_DIR=" $STAGE " \
111- MCPP_PKG_NAME=web-consumer \
112- MCPP_PKG_VERSION=0.1.0 \
113- MCPP_MANIFEST_DIR=" $HERE " \
114- MCPP_OUT_DIR=" $OUT " \
115- " $BIN " > " $PLANLOG " 2>&1
116- grep -q ' "id":"mcpp.dist.web.index"' " $PLANLOG " || fail " no index.html action was planned" " $PLANLOG "
117- n=$( grep -c ' "id":"mcpp.dist.web.file"' " $PLANLOG " )
118- [ " $n " -eq 3 ] || fail " expected 3 file actions (js, wasm, the deployed asset), got $n " " $PLANLOG "
119- echo " ok: 3 file actions and 1 index.html action planned"
25+ echo " == a plain build has no distribution edge =="
26+ " $MCPP " build --target wasm32-emscripten > build.log 2>&1 \
27+ || fail " mcpp build --target wasm32-emscripten failed" build.log
28+ if find target -type d -name web | grep -q . ; then
29+ echo " FAIL: a plain build produced a web/ directory" ; exit 1
30+ fi
31+ echo " ok: no web/ directory before packaging"
12032
121- echo " == step 4: execute the planned actions (standing in for ninja) =="
122- grep ' ^mcpp:action=' " $PLANLOG " | sed ' s/^mcpp:action=//' | while read -r line; do
123- mapfile -t argv < <( echo " $line " | jq -r ' .command[]' )
124- for i in " ${! argv[@]} " ; do
125- argv[$i ]=" ${argv[$i]// \$\{ mcpp.stage_dir\} / $STAGE } "
126- done
127- " ${argv[@]} "
128- done
33+ echo " == mcpp pack --format web --target wasm32-emscripten =="
34+ " $MCPP " pack --format web --target wasm32-emscripten > pack.log 2>&1 \
35+ || fail " mcpp pack --format web --target wasm32-emscripten failed" pack.log
36+ WEB=$( find target -type d -name web | head -1)
37+ [ -n " $WEB " ] || fail " no web/ directory was produced" pack.log
38+ echo " ok: $WEB "
12939
130- echo " == assertions on target/web/ =="
131- WEB=" $OUT /web"
40+ echo " == the file list is exactly the stem family, the deployed asset, and index.html =="
13241mapfile -t files < <( cd " $WEB " && find . -type f | sed ' s#^\./##' | sort)
13342want=(assets/greeting.txt index.html web-consumer.js web-consumer.wasm)
13443[ " ${files[*]} " = " ${want[*]} " ] || {
@@ -138,15 +47,31 @@ want=(assets/greeting.txt index.html web-consumer.js web-consumer.wasm)
13847 exit 1; }
13948echo " ok: exactly js, wasm, the deployed asset and index.html -- nothing else"
14049
50+ # THE DEPLOYED ASSET LANDS AT ITS STAGED PATH, NOT NESTED UNDER ITSELF.
51+ # `mcpp::deploy`'s `to` names a DIRECTORY (`assets`), and the file keeps its
52+ # own basename (`greeting.txt`) -- the same rule `[[runtime.deploy]]` and
53+ # `types.cppm`'s `DeployEntry` document. A `to` that names the full
54+ # destination path instead (`"assets/greeting.txt"`) deploys the file INTO a
55+ # directory of that name, landing it at `bin/assets/greeting.txt/greeting.txt`
56+ # -- a directory named after itself, holding the real file. Measured on this
57+ # fixture before `build.mcpp`'s deploy call was corrected. This step asserts
58+ # both directions: the file is a regular file at exactly its intended
59+ # relative path, and no such wrapper directory exists.
60+ [ -f " $WEB /assets/greeting.txt" ] \
61+ || fail " assets/greeting.txt is not a regular file at its staged path" pack.log
62+ [ -d " $WEB /assets/greeting.txt" ] \
63+ && fail " assets/greeting.txt is a directory -- the file landed inside a directory named after itself"
64+ grep -q ' hello from build.mcpp' " $WEB /assets/greeting.txt" \
65+ || fail " the deployed asset's content did not survive the copy" " $WEB /assets/greeting.txt"
66+ echo " ok: assets/greeting.txt is the file, at exactly its staged relative path"
67+
14168grep -q ' web-consumer.js' " $WEB /index.html" || fail " index.html does not load the launcher" " $WEB /index.html"
14269grep -q ' <title>web-consumer</title>' " $WEB /index.html" || fail " index.html's title is not the package name" " $WEB /index.html"
14370echo " ok: index.html loads the launcher and titles the page from [package] name"
14471
145- grep -q ' hello from build.mcpp' " $WEB /assets/greeting.txt" || fail " the deployed asset's content did not survive the copy" " $WEB /assets/greeting.txt"
146-
14772out=$( node " $WEB /web-consumer.js" )
14873[ " $out " = " 1-2-3" ] || fail " node did not print 1-2-3 (got: $out )"
14974echo " ok: node $WEB /web-consumer.js prints 1-2-3"
15075
151- rm -rf " $REALDIR " " $OUT " " $PLANLOG "
76+ rm -f build.log pack.log
15277echo " PASS: dist-web produces a static directory, node runs it, 1-2-3"
0 commit comments