6060 MCPP_VERSION : " 0.0.107"
6161
6262jobs :
63- lint :
64- runs-on : ubuntu-latest
65- steps :
66- - uses : actions/checkout@v4
67- - name : Install lua
68- run : sudo apt-get install -y --no-install-recommends lua5.4
69- - name : Lint package descriptors
70- run : |
71- fail=0
72- for f in pkgs/*/*.lua; do
73- # 1. Lua syntax check — load (= compile) without executing.
74- # `loadfile(name, 't')` rejects bytecode and parses text only.
75- if ! lua5.4 -e "assert(loadfile('$f', 't'))" >/dev/null 2>&1; then
76- echo "::error file=$f::lua syntax error"
77- fail=1
78- fi
79- # 2. xpkg V1 baseline: the file has to populate `package = { ... }`
80- # with at least `spec`, `name`, and an `xpm` table. Form A vs
81- # Form B (mcpp = "<path>" / mcpp = { ... }) is descriptor-author
82- # choice and not enforced here.
83- for needle in 'spec *=' 'name *=' 'xpm *='; do
84- if ! grep -q "$needle" "$f"; then
85- echo "::error file=$f::missing required field ($needle)"
86- fail=1
87- fi
88- done
89- # 3. Package version identifiers and dependency versions should be
90- # bare versions ("1.2.3"), not upstream tag names ("v1.2.3").
91- # Download URLs may still contain refs/tags/v* when upstream
92- # uses that tag spelling.
93- if grep -nE '\["v[0-9]+|\["[^"]+"\][[:space:]]*=[[:space:]]*"v[0-9]+' "$f"; then
94- echo "::error file=$f::version identifiers must not use a leading v"
95- fail=1
96- fi
97- # 4. Mirror table sanity: when a download `url` is written as a
98- # { GLOBAL=..., CN=... } table, both regions must be present and
99- # the CN entry must point at the gitcode mcpp-res mirror.
100- if ! lua5.4 tests/check_mirror_urls.lua "$f"; then
101- fail=1
102- fi
103- # 5. `name` must be a SINGLE ATOMIC SEGMENT; hierarchy belongs in
104- # `namespace` (mcpp SPEC-001 §3.2). The legacy fully-qualified
105- # spelling stays accepted. Cheap second gate: it runs before the
106- # pinned mcpp is even downloaded, and mcpp >= 0.0.106 enforces
107- # the same rule inside `mcpp xpkg parse`.
108- if ! lua5.4 tests/check_package_name.lua "$f"; then
109- fail=1
110- fi
111- # 6. c++fly admission policy (mcpp design 2026-07-14 §11-Q2, v1):
112- # c++fly means "toolchain's latest level + every experimental
113- # gate" — deliberately toolchain-dependent, so a published
114- # package built with it is not reproducible for consumers.
115- # Policy: WARN (never fail) and observe ecosystem usage before
116- # deciding whether to tighten. Two spellings: `language = ` is
117- # the descriptor's inline mcpp-segment key; `standard = ` covers
118- # mcpp.toml content embedded in heredoc/generated_files blocks.
119- if grep -nE '\b(language|standard)[[:space:]]*=[[:space:]]*"c\+\+fly"' "$f" >/dev/null; then
120- echo "::warning file=$f::declares C++ standard \"c++fly\" (experimental playground mode) — toolchain-dependent and non-reproducible for consumers; published packages should pin a concrete standard (c++23/c++26)"
121- fi
122- done
123- [ $fail -eq 0 ] && echo "All package files valid."
124- exit $fail
125- # ── Whole-repository check (needs every descriptor at once) ──────
126- # An install() hook addressing a sibling package does so by
127- # `<namespace>:<literal package.name>`, and a miss returns nil rather
128- # than raising — so a stale spelling surfaces far downstream (a broken
129- # libxcb showed up as a libX11 link error). Verified across the repo
130- # because it needs the full set of declared identities.
131- - name : Lint cross-package references
132- run : lua5.4 tests/check_cross_package_refs.lua pkgs/*/*.lua
133- # ── Single-source-of-truth grammar check ─────────────────────────
134- # `mcpp xpkg parse` uses EXACTLY the resolver's parser, so what
135- # passes here is what builds for users of the pinned MCPP_VERSION.
136- # Strict by default: unknown mcpp-segment keys fail (they would be
137- # silently ignored at build time). This also mechanically enforces
138- # the rollout rule "floor first, new grammar after": descriptors
139- # needing a newer grammar cannot pass a lint pinned to an older mcpp.
140- - name : Download pinned mcpp
141- run : |
142- curl -L -fsS -o mcpp.tar.gz \
143- "https://github.com/mcpp-community/mcpp/releases/download/v${MCPP_VERSION}/mcpp-${MCPP_VERSION}-linux-x86_64.tar.gz"
144- tar -xzf mcpp.tar.gz
145- echo "MCPP=$PWD/mcpp-${MCPP_VERSION}-linux-x86_64/bin/mcpp" >> "$GITHUB_ENV"
146- - name : Parse descriptors with the resolver grammar (mcpp xpkg parse)
147- run : |
148- fail=0
149- for f in pkgs/*/*.lua; do
150- if ! "$MCPP" xpkg parse "$f" > /dev/null; then
151- echo "::error file=$f::mcpp xpkg parse failed (resolver grammar)"
152- fail=1
153- fi
154- done
155- [ $fail -eq 0 ] && echo "All descriptors parse with mcpp ${MCPP_VERSION}."
156- exit $fail
157-
158- mirror-cn-reachable :
159- # Closed-loop guard for the CN mirror: every CN url referenced by a
160- # descriptor must be a live, downloadable gitcode release asset.
161- runs-on : ubuntu-latest
162- steps :
163- - uses : actions/checkout@v4
164- - name : Install lua
165- run : sudo apt-get install -y --no-install-recommends lua5.4
166- - name : Check CN mirror assets are reachable
167- run : |
168- fail=0
169- # collect unique CN urls across all descriptors
170- : > /tmp/cn.tsv
171- for f in pkgs/*/*.lua; do
172- lua5.4 tests/list_cn_urls.lua "$f" >> /tmp/cn.tsv || true
173- done
174- sort -u /tmp/cn.tsv -o /tmp/cn.tsv
175- total=$(grep -c . /tmp/cn.tsv || true)
176- echo "checking $total CN mirror url(s)"
177- while IFS=$'\t' read -r url sha; do
178- [ -z "$url" ] && continue
179- # follow redirects; gitcode release assets resolve to object storage
180- code=$(curl -fsSL -o /dev/null -w '%{http_code}' --retry 2 --max-time 60 "$url" || echo "000")
181- if [ "$code" != "200" ]; then
182- echo "::error::CN mirror unreachable ($code): $url"
183- fail=1
184- else
185- echo "ok: $url"
186- fi
187- done < /tmp/cn.tsv
188- [ $fail -eq 0 ] && echo "All CN mirror urls reachable."
189- exit $fail
190-
191- # ── The whole test surface, as a mcpp workspace ───────────────────────
192- # mcpp-index is a mcpp [workspace]; every per-library test project under
193- # tests/examples/ is a member. `mcpp test --workspace` builds + runs each
194- # member's tests/ (behavioral assertions) on each OS — members self-gate by
195- # `[target.'cfg(...)']` (e.g. the X11/glfw stack is linux-only, openblas is
196- # windows-only), so one command covers the matrix with no shell driver.
197- # The ~/.mcpp/registry cache carries the built compat packages (xpkgs) across
198- # runs, so repeat builds are fast.
199- #
200- # timeout-minutes is sized for the COLD build, not the cached path. The opencv
201- # module package carries a from-source OpenCV 5 build, and each feature variant
202- # re-keys the store into a full recompile, so a full run (forced whenever this
203- # workflow file changes — e.g. a version bump) serially builds three OpenCV
204- # variants on one runner: the opencv-module base member plus the `unifont` and
205- # `dnn` feature members. The registry cache (restore-keys prefix below)
206- # amortizes those across subsequent runs. 150 covers the one-time cold full
207- # build with headroom; it is a ceiling, not a target.
20863 workspace :
20964 name : workspace (${{ matrix.platform }})
21065 runs-on : ${{ matrix.os }}
@@ -213,30 +68,16 @@ jobs:
21368 fail-fast : false
21469 matrix :
21570 include :
216- # Archive names are derived from env.MCPP_VERSION in the Download
217- # step — bumping the pin is a ONE-line change (hardcoded versions
218- # here once 404'd a pin bump).
219- - platform : linux
220- os : ubuntu-latest
221- suffix : linux-x86_64
222- ext : tar.gz
223- mcpp : bin/mcpp
224- xlings : registry/bin/xlings
225- mcpp_version : " 0.0.107" # keep in sync with env.MCPP_VERSION
226- - platform : macos
227- os : macos-15
228- suffix : macosx-arm64
229- ext : tar.gz
230- mcpp : bin/mcpp
231- xlings : registry/bin/xlings
232- mcpp_version : " 0.0.107" # keep in sync with env.MCPP_VERSION
71+ # PROBE: only Windows, and pinned to the instrumented debug build
72+ # published by mcpp's debug-win workflow (branch
73+ # debug/win127-instrumentation, prerelease tag v0.0.107-dbg).
23374 - platform : windows
23475 os : windows-latest
23576 suffix : windows-x86_64
23677 ext : zip
23778 mcpp : bin/mcpp.exe
23879 xlings : registry/bin/xlings.exe
239- mcpp_version : " 0.0.107" # keep in sync with env.MCPP_VERSION
80+ mcpp_version : " 0.0.107-dbg "
24081 env :
24182 MCPP_EFFECTIVE : ${{ matrix.mcpp_version }}
24283 steps :
@@ -360,71 +201,8 @@ jobs:
360201 MCPP_INDEX_MIRROR : GLOBAL
361202 run : |
362203 "$MCPP" --version
363- # PROBE (do not merge): the Windows job dies with a bare 127 the
364- # instant compat:ffmpeg finishes downloading, printing nothing. Run
365- # that one member on its own, keep the shell alive past the failure,
366- # and record the exit status plus free disk on either side.
367- echo "=== free space before ==="; df -h . || true
204+ echo "=== ffmpeg member, instrumented ==="
368205 rc=0
369- "$MCPP" test -p tests/examples/ffmpeg || rc=$?
206+ "$MCPP" test -p tests/examples/ffmpeg 2>&1 | tail -120 || rc=$?
370207 echo "=== ffmpeg member exit status: $rc ==="
371- echo "=== free space after ==="; df -h . || true
372- # mcpp returns the xlings child's exit code, so 127 may well be
373- # xlings'. Drive xlings directly on the same package and show what it
374- # says — mcpp swallows the child's stderr on this path.
375- # MCPP_VERBOSE=1 prints the exact xlings invocation (xlings.cppm:923).
376- # This path otherwise swallows the child's stderr, which is why the
377- # 127 arrives with no output at all.
378- echo "=== retry under MCPP_VERBOSE ==="
379- vrc=0
380- MCPP_VERBOSE=1 "$MCPP" test -p tests/examples/ffmpeg 2>&1 | tail -60 || vrc=$?
381- echo "=== verbose retry exit status: $vrc ==="
382- # Third attempt. The two before this failed on my own path mistakes
383- # (a relative ninja path, then looking under $HOME instead of the
384- # extracted release, which is the registry mcpp actually uses). List
385- # every candidate this time and require an absolute one.
386- echo "=== ninja candidates ==="
387- find "$PWD" "$HOME" -name 'ninja.exe' 2>/dev/null | head -5
388- NJ=$(find "$PWD" "$HOME" -name 'ninja.exe' 2>/dev/null | head -1)
389- BN=$(find "$PWD/tests/examples/ffmpeg/target" -maxdepth 3 -name build.ninja 2>/dev/null | head -1)
390- echo "build.ninja: $BN"
391- echo "ninja: $NJ"
392- # cmd.exe caps a command line at 8191 chars and ninja spawns through
393- # it. ffmpeg is the only member big enough for that to bite, so
394- # measure rather than assume.
395- if [ -n "$BN" ]; then
396- echo "=== build.ninja size / longest line ==="
397- wc -c "$BN"
398- awk '{ if (length($0) > m) { m = length($0); ml = NR } } END { print "longest line: " m " chars (line " ml ")" }' "$BN"
399- fi
400- if [ -n "$BN" ] && [ -n "$NJ" ]; then
401- "$NJ" --version || echo "(ninja --version failed rc=$?)"
402- nrc=0
403- "$NJ" -C "$(dirname "$BN")" -j2 2>&1 | tail -40 || nrc=$?
404- echo "=== manual ninja exit status: $nrc ==="
405- fi
406- echo "=== locating xlings ==="
407- XL=$(find . "$HOME" -name 'xlings.exe' -path '*registry/bin*' 2>/dev/null | head -1)
408- echo "xlings: $XL"
409- if [ -n "$XL" ]; then
410- "$XL" --version || true
411- xrc=0
412- "$XL" install "compat:ffmpeg@8.1.2" -y || xrc=$?
413- echo "=== xlings direct exit status: $xrc ==="
414- fi
415208 exit $rc
416- # No `timeout` wrapper: absent on macOS runners; job-level timeout-minutes bounds it.
417- if [ "$MEMBERS" = "__ALL__" ]; then
418- "$MCPP" test --workspace
419- elif [ -z "$MEMBERS" ]; then
420- echo "No workspace member affected by this change — nothing to test."
421- else
422- rc=0
423- for m in $MEMBERS; do
424- echo "::group::mcpp test -p $m"
425- "$MCPP" test -p "$m" || rc=1
426- echo "::endgroup::"
427- done
428- exit $rc
429- fi
430-
0 commit comments