feat: add mcpplibs.grpc 1.83.0 (Form A, grpc-m) (#151) #544
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: validate | |
| on: | |
| pull_request: | |
| # mcpp.toml and index.toml carry the workspace member list, the inherited | |
| # [indices] redirect and the client version floor — a change to any of them | |
| # can break every member, so they gate the run like the descriptors do. | |
| paths: ["pkgs/**/*.lua", "tests/**", "README.md", "README.zh-CN.md", "mcpp.toml", "index.toml", ".github/workflows/validate.yml"] | |
| push: | |
| branches: [main] | |
| schedule: | |
| # nightly full regression — exercises every workspace member regardless of diff | |
| - cron: "0 6 * * *" | |
| workflow_dispatch: | |
| env: | |
| # 2026.8.3.1: on macOS, a global object that touches std::cout during static | |
| # init crashes on sight (mcpp#336). Mach-O has no priority-ordered init | |
| # section and libc++'s <iostream> carries no ios_base::Init guard of its own, | |
| # so the streams are still all-zero when an archive member's initializer | |
| # runs. mcpp now links a generated object FIRST whose constructor brings them | |
| # up. It is not fixable package-side — std::ios_base::Init is only | |
| # forward-declared in libc++'s <ios> — so boost-ext.ut genuinely requires | |
| # this floor on macOS, and min_mcpp/latest_mcpp move with the pin as they | |
| # always have (a consumer below the floor would get a segfault with no | |
| # diagnostic, which is worse than E0006). | |
| # 2026.8.3.3 is the pin rather than .3.1: .3.2/.3.3 are cross-compilation | |
| # fixes (PE artifact naming, -static host-vs-target) that no leg of this | |
| # matrix exercises, so taking the newest of the train costs nothing. | |
| # 0.0.109: a bare dependency's wire address takes BOTH halves from the | |
| # descriptor the identity gate accepted (mcpp#286). This is the client-side | |
| # other half of the SPEC-001 migration below: mcpp used to take the NAME from | |
| # the descriptor and the NAMESPACE from the request, so a bare `gtest = | |
| # "1.15.2"` addressed `mcpplibs:gtest` — a key no index has. It only ever | |
| # worked because the pre-migration literal `package.name` read | |
| # "compat.gtest", which the hardcoded `compat.<short>` retry then caught. | |
| # Short names removed that coincidence and left every bare request against | |
| # this index broken on 0.0.108, which is why min_mcpp moves to 0.0.109. | |
| # Note this index cannot cover that spelling itself: the `[indices]` redirect | |
| # is keyed by the REQUEST's namespace, so a bare dependency resolves from the | |
| # published remote index rather than the checkout under test. Bare-name | |
| # resolution against a short-name index is upstream's e2e 165; what moving | |
| # the floor buys here is that consumers of THIS index get a client that can | |
| # address it. | |
| # 0.0.106: SPEC-001 package identity (mcpp#280). `package.name` is a SINGLE | |
| # ATOMIC SEGMENT — all hierarchy lives in `package.namespace` — and mcpp | |
| # addresses a package by the LITERAL name it read, so descriptors no longer | |
| # repeat their namespace inside `name`. This index is migrated to the short | |
| # form, which is why min_mcpp/latest_mcpp move in lock-step: an older client | |
| # re-derives `<ns>.<short>`, misses, and reports a bare E_NOT_FOUND. Bundles | |
| # xlings 0.4.69, which keys its index by (namespace, name) so two packages | |
| # sharing a short name in one index are both addressable (xlings#381) — this | |
| # index now has three such pairs (imgui / ffmpeg / lua under compat vs the | |
| # default namespace). | |
| # 0.0.102: windows command-line ceiling (mcpp#261 — the clang scan rule got | |
| # its P1689 JSON through shell redirection, which forced a `cmd /c` wrapper | |
| # and with it cmd.exe's 8191-char limit; clang-scan-deps -o removes both, and | |
| # $local_includes-carrying rules now fall back to response files). The pin | |
| # matters here because a package consumed FROM the registry sits under a | |
| # ~124-char xpkgs path instead of its own ~23-char checkout, which is what | |
| # pushed the vendored-opencv scan command over the line. Also: purview-include | |
| # depfile tracking extended to Clang (#257 — stale BMI reuse), OS-conditional | |
| # `[build].flags` (#258), and per-OS splices keyed on the resolved target | |
| # rather than the host (#254). | |
| # 0.0.101: per-feature per-glob flags + per-OS features (mcpp#253) — what | |
| # lets opencv select its dnn gemm backend per platform. | |
| # 0.0.99: feature dep/feat forwarding (mcpp#243 — a feature can open a | |
| # feature OF a dependency, e.g. opencv `dnn` forwarding compat.opencv/dnn); | |
| # vendored xlings 0.4.67 for the >=2 index_repo install fix (mcpp#238 / | |
| # openxlings/xlings#374); build.mcpp compiled program named `.exe` on | |
| # Windows (mcpp#230 secondary surface, after the 0.0.96 scanner crash fix). | |
| # 0.0.98: closes the obj-path disambiguation follow-ups that gated the | |
| # source-build compat.opencv unification — #240 (link inputs now follow | |
| # the disambiguated object names, so a dependency + consumer sharing a | |
| # source basename like `src/main.cpp` no longer 'obj/main.o missing') and | |
| # #239 (absolute/`..` dep-generated source paths sanitized component-wise | |
| # so objects stay under obj/). Also: `MCPP_DEP_<NAME>_DIR` build.mcpp | |
| # contract (#241), consumer-side `default-features = false` (#242), and a | |
| # loud unknown-mcpp-key warning with did-you-mean (#237, replaces the | |
| # silent-ignore at build time). Carried from 0.0.97: default-namespace | |
| # index redirect (`[indices] default = { path }`), which turned the public | |
| # module packages (imgui/ffmpeg/opencv/tinyhttps) into ordinary workspace | |
| # members and retired the per-package reseeding smoke shells + their | |
| # dedicated jobs; synchronous nasm bootstrap (mcpp#232 — the `mcpp index | |
| # update` pre-step is gone), obj-path disambiguation (#233), spacey-defines | |
| # quoting (#234), purview-include depfile tracking (#235). Older floors of | |
| # note: 0.0.96 fixed the windows scanner symlink-escape crash (mcpp#230); | |
| # 0.0.94 fixed feature-gated `sources` under `mcpp test` (mcpp#218); 0.0.91 | |
| # added standard = "c++fly" to the resolver grammar, so c++fly descriptors | |
| # get the lint WARN below, not a hard grammar-parse rejection. | |
| MCPP_VERSION: "2026.8.3.3" | |
| jobs: | |
| lint: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Install lua | |
| run: sudo apt-get install -y --no-install-recommends lua5.4 | |
| - name: Lint package descriptors | |
| run: | | |
| fail=0 | |
| for f in pkgs/*/*.lua; do | |
| # 1. Lua syntax check — load (= compile) without executing. | |
| # `loadfile(name, 't')` rejects bytecode and parses text only. | |
| if ! lua5.4 -e "assert(loadfile('$f', 't'))" >/dev/null 2>&1; then | |
| echo "::error file=$f::lua syntax error" | |
| fail=1 | |
| fi | |
| # 2. xpkg V1 baseline: the file has to populate `package = { ... }` | |
| # with at least `spec`, `name`, and an `xpm` table. Form A vs | |
| # Form B (mcpp = "<path>" / mcpp = { ... }) is descriptor-author | |
| # choice and not enforced here. | |
| for needle in 'spec *=' 'name *=' 'xpm *='; do | |
| if ! grep -q "$needle" "$f"; then | |
| echo "::error file=$f::missing required field ($needle)" | |
| fail=1 | |
| fi | |
| done | |
| # 3. Package version identifiers and dependency versions should be | |
| # bare versions ("1.2.3"), not upstream tag names ("v1.2.3"). | |
| # Download URLs may still contain refs/tags/v* when upstream | |
| # uses that tag spelling. | |
| if grep -nE '\["v[0-9]+|\["[^"]+"\][[:space:]]*=[[:space:]]*"v[0-9]+' "$f"; then | |
| echo "::error file=$f::version identifiers must not use a leading v" | |
| fail=1 | |
| fi | |
| # 4. Mirror table sanity: when a download `url` is written as a | |
| # { GLOBAL=..., CN=... } table, both regions must be present and | |
| # the CN entry must point at the gitcode mcpp-res mirror. | |
| if ! lua5.4 tests/check_mirror_urls.lua "$f"; then | |
| fail=1 | |
| fi | |
| # 5. `name` must be a SINGLE ATOMIC SEGMENT; hierarchy belongs in | |
| # `namespace` (mcpp SPEC-001 §3.2). The legacy fully-qualified | |
| # spelling stays accepted. Cheap second gate: it runs before the | |
| # pinned mcpp is even downloaded, and mcpp >= 0.0.106 enforces | |
| # the same rule inside `mcpp xpkg parse`. | |
| if ! lua5.4 tests/check_package_name.lua "$f"; then | |
| fail=1 | |
| fi | |
| # 6. c++fly admission policy (mcpp design 2026-07-14 §11-Q2, v1): | |
| # c++fly means "toolchain's latest level + every experimental | |
| # gate" — deliberately toolchain-dependent, so a published | |
| # package built with it is not reproducible for consumers. | |
| # Policy: WARN (never fail) and observe ecosystem usage before | |
| # deciding whether to tighten. Two spellings: `language = ` is | |
| # the descriptor's inline mcpp-segment key; `standard = ` covers | |
| # mcpp.toml content embedded in heredoc/generated_files blocks. | |
| if grep -nE '\b(language|standard)[[:space:]]*=[[:space:]]*"c\+\+fly"' "$f" >/dev/null; then | |
| 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)" | |
| fi | |
| done | |
| [ $fail -eq 0 ] && echo "All package files valid." | |
| exit $fail | |
| # ── Whole-repository check (needs every descriptor at once) ────── | |
| # An install() hook addressing a sibling package does so by | |
| # `<namespace>:<literal package.name>`, and a miss returns nil rather | |
| # than raising — so a stale spelling surfaces far downstream (a broken | |
| # libxcb showed up as a libX11 link error). Verified across the repo | |
| # because it needs the full set of declared identities. | |
| - name: Lint cross-package references | |
| run: lua5.4 tests/check_cross_package_refs.lua pkgs/*/*.lua | |
| # ── Single-source-of-truth grammar check ───────────────────────── | |
| # `mcpp xpkg parse` uses EXACTLY the resolver's parser, so what | |
| # passes here is what builds for users of the pinned MCPP_VERSION. | |
| # Strict by default: unknown mcpp-segment keys fail (they would be | |
| # silently ignored at build time). This also mechanically enforces | |
| # the rollout rule "floor first, new grammar after": descriptors | |
| # needing a newer grammar cannot pass a lint pinned to an older mcpp. | |
| - name: Download pinned mcpp | |
| run: | | |
| curl -L -fsS -o mcpp.tar.gz \ | |
| "https://github.com/mcpp-community/mcpp/releases/download/v${MCPP_VERSION}/mcpp-${MCPP_VERSION}-linux-x86_64.tar.gz" | |
| tar -xzf mcpp.tar.gz | |
| echo "MCPP=$PWD/mcpp-${MCPP_VERSION}-linux-x86_64/bin/mcpp" >> "$GITHUB_ENV" | |
| - name: Parse descriptors with the resolver grammar (mcpp xpkg parse) | |
| run: | | |
| fail=0 | |
| for f in pkgs/*/*.lua; do | |
| if ! "$MCPP" xpkg parse "$f" > /dev/null; then | |
| echo "::error file=$f::mcpp xpkg parse failed (resolver grammar)" | |
| fail=1 | |
| fi | |
| done | |
| [ $fail -eq 0 ] && echo "All descriptors parse with mcpp ${MCPP_VERSION}." | |
| exit $fail | |
| mirror-cn-reachable: | |
| # Closed-loop guard for the CN mirror: every CN url referenced by a | |
| # descriptor must be a live, downloadable gitcode release asset. | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Install lua | |
| run: sudo apt-get install -y --no-install-recommends lua5.4 | |
| - name: Check CN mirror assets are reachable | |
| run: | | |
| fail=0 | |
| # collect unique CN urls across all descriptors | |
| : > /tmp/cn.tsv | |
| for f in pkgs/*/*.lua; do | |
| lua5.4 tests/list_cn_urls.lua "$f" >> /tmp/cn.tsv || true | |
| done | |
| sort -u /tmp/cn.tsv -o /tmp/cn.tsv | |
| total=$(grep -c . /tmp/cn.tsv || true) | |
| echo "checking $total CN mirror url(s)" | |
| while IFS=$'\t' read -r url sha; do | |
| [ -z "$url" ] && continue | |
| # follow redirects; gitcode release assets resolve to object storage | |
| code=$(curl -fsSL -o /dev/null -w '%{http_code}' --retry 2 --max-time 60 "$url" || echo "000") | |
| if [ "$code" != "200" ]; then | |
| echo "::error::CN mirror unreachable ($code): $url" | |
| fail=1 | |
| else | |
| echo "ok: $url" | |
| fi | |
| done < /tmp/cn.tsv | |
| [ $fail -eq 0 ] && echo "All CN mirror urls reachable." | |
| exit $fail | |
| # ── The whole test surface, as a mcpp workspace ─────────────────────── | |
| # mcpp-index is a mcpp [workspace]; every per-library test project under | |
| # tests/examples/ is a member. `mcpp test --workspace` builds + runs each | |
| # member's tests/ (behavioral assertions) on each OS — members self-gate by | |
| # `[target.'cfg(...)']` (e.g. the X11/glfw stack is linux-only, openblas is | |
| # windows-only), so one command covers the matrix with no shell driver. | |
| # The ~/.mcpp/registry cache carries the built compat packages (xpkgs) across | |
| # runs, so repeat builds are fast. | |
| # | |
| # timeout-minutes is sized for the COLD build, not the cached path. The opencv | |
| # module package carries a from-source OpenCV 5 build, and each feature variant | |
| # re-keys the store into a full recompile, so a full run (forced whenever this | |
| # workflow file changes — e.g. a version bump) serially builds three OpenCV | |
| # variants on one runner: the opencv-module base member plus the `unifont` and | |
| # `dnn` feature members. The registry cache (restore-keys prefix below) | |
| # amortizes those across subsequent runs. 150 covers the one-time cold full | |
| # build with headroom; it is a ceiling, not a target. | |
| workspace: | |
| name: workspace (${{ matrix.platform }}) | |
| runs-on: ${{ matrix.os }} | |
| timeout-minutes: 150 | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| # Archive names are derived from env.MCPP_VERSION in the Download | |
| # step — bumping the pin is a ONE-line change (hardcoded versions | |
| # here once 404'd a pin bump). | |
| - platform: linux | |
| os: ubuntu-latest | |
| suffix: linux-x86_64 | |
| ext: tar.gz | |
| mcpp: bin/mcpp | |
| xlings: registry/bin/xlings | |
| mcpp_version: "2026.8.3.3" # keep in sync with env.MCPP_VERSION | |
| - platform: macos | |
| os: macos-15 | |
| suffix: macosx-arm64 | |
| ext: tar.gz | |
| mcpp: bin/mcpp | |
| xlings: registry/bin/xlings | |
| mcpp_version: "2026.8.3.3" # keep in sync with env.MCPP_VERSION | |
| - platform: windows | |
| os: windows-latest | |
| suffix: windows-x86_64 | |
| ext: zip | |
| mcpp: bin/mcpp.exe | |
| xlings: registry/bin/xlings.exe | |
| mcpp_version: "2026.8.3.3" # keep in sync with env.MCPP_VERSION | |
| env: | |
| MCPP_EFFECTIVE: ${{ matrix.mcpp_version }} | |
| steps: | |
| # Full history: the member-selection step below diffs against the PR | |
| # base to decide which workspace members to test. | |
| - uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| # The cache key is computed ONCE, here, instead of inline in the cache | |
| # step. `hashFiles()` globs the WORKING TREE, and actions/cache | |
| # re-evaluates its `key` in the post (save) step — i.e. AFTER the build, | |
| # when `tests/**` no longer matches 80-odd tracked sources but tens of | |
| # thousands of build-output files under tests/examples/*/target and | |
| # .mcpp (multi-GB; .gitignore does not apply to hashFiles). Hashing that | |
| # tree blew past the runner's 120s template-evaluation cap on windows | |
| # and failed an otherwise all-green job: | |
| # "hashFiles('pkgs/**/*.lua, tests/**, .github/workflows/validate.yml') | |
| # couldn't finish within 120 seconds" | |
| # `git ls-files -s` reads the INDEX, so it sees exactly the tracked | |
| # inputs, never build output, and reports blob SHAs git already has — | |
| # no file content is read at all. Freezing the result in the job env | |
| # also guarantees the save step keys on the same string the restore | |
| # step used, no matter what the build left behind. | |
| - name: Compute registry cache key | |
| shell: bash | |
| run: | | |
| # git hash-object rather than sha256sum/cut: git is already a hard | |
| # requirement here (checkout ran), coreutils on the windows leg is | |
| # only a Git-Bash convenience. | |
| h=$(git ls-files -s -- 'pkgs/**/*.lua' 'tests/**' '.github/workflows/validate.yml' \ | |
| | git hash-object --stdin) | |
| echo "REGISTRY_CACHE_KEY=mcpp-registry-${{ runner.os }}-${{ env.MCPP_EFFECTIVE }}-$h" >> "$GITHUB_ENV" | |
| - name: Restore mcpp registry cache | |
| uses: actions/cache@v4 | |
| with: | |
| # Holds toolchains AND the built compat packages (data/xpkgs), so a | |
| # repeat `mcpp test` rebuilds little. | |
| path: ~/.mcpp/registry | |
| key: ${{ env.REGISTRY_CACHE_KEY }} | |
| restore-keys: | | |
| mcpp-registry-${{ runner.os }}-${{ env.MCPP_EFFECTIVE }}- | |
| - name: Download mcpp | |
| shell: bash | |
| env: | |
| MCPP_ARCHIVE: mcpp-${{ env.MCPP_EFFECTIVE }}-${{ matrix.suffix }}.${{ matrix.ext }} | |
| MCPP_ROOT: mcpp-${{ env.MCPP_EFFECTIVE }}-${{ matrix.suffix }} | |
| run: | | |
| curl -L -fsS -o "$MCPP_ARCHIVE" \ | |
| "https://github.com/mcpp-community/mcpp/releases/download/v${MCPP_EFFECTIVE}/${MCPP_ARCHIVE}" | |
| case "$MCPP_ARCHIVE" in | |
| *.zip) powershell -NoProfile -Command "Expand-Archive -Force -Path '${MCPP_ARCHIVE}' -DestinationPath '.'" ;; | |
| *) tar -xzf "$MCPP_ARCHIVE" ;; | |
| esac | |
| root="$PWD/$MCPP_ROOT" | |
| mkdir -p "$HOME/.mcpp/registry" | |
| cp -a "$root/registry/." "$HOME/.mcpp/registry/" | |
| if [[ "$RUNNER_OS" == "Windows" ]]; then | |
| echo "MCPP=$(cygpath -m "$root/${{ matrix.mcpp }}")" >> "$GITHUB_ENV" | |
| echo "MCPP_VENDORED_XLINGS=$(cygpath -m "$root/${{ matrix.xlings }}")" >> "$GITHUB_ENV" | |
| echo "$(cygpath -m "$root/bin")" >> "$GITHUB_PATH" | |
| else | |
| echo "MCPP=$root/${{ matrix.mcpp }}" >> "$GITHUB_ENV" | |
| echo "MCPP_VENDORED_XLINGS=$root/${{ matrix.xlings }}" >> "$GITHUB_ENV" | |
| echo "$root/bin" >> "$GITHUB_PATH" | |
| fi | |
| # compat.ffmpeg / compat.opencv5 carry NASM .asm sources. No host | |
| # install and no index-refresh pre-step needed: mcpp >= 0.0.97 | |
| # resolves nasm itself through the same synchronous gate as the | |
| # toolchain (index refresh + install + payload check BEFORE the build | |
| # plans, mcpp#232). The sandbox copy lands in ~/.mcpp/registry, so | |
| # the cache carries it across runs. | |
| # ── Selective member testing ────────────────────────────────────── | |
| # `mcpp test --workspace` builds every member (opencv, ffmpeg, …) and | |
| # dominates CI wall-clock, while a PR almost always touches one | |
| # package. Map changed files → affected members and test only those: | |
| # pkgs/<x>/<lib>.lua → members whose mcpp.toml references <lib> | |
| # tests/examples/<m>/** → member <m> | |
| # Run the FULL workspace when the change can affect everything: | |
| # non-PR events (push to main, the nightly cron, dispatch), this | |
| # workflow file (it carries the mcpp version pins, so a version bump | |
| # always re-validates every package), a non-member edit to the | |
| # workspace manifest, or shared test scripts. Docs-only and tools/-only | |
| # changes select nothing. | |
| # Note: bash 3.2 on macOS runners — no associative arrays here. | |
| - name: Select affected workspace members | |
| shell: bash | |
| run: | | |
| full() { echo "MEMBERS=__ALL__" >> "$GITHUB_ENV"; echo "full run: $1"; exit 0; } | |
| [ "${{ github.event_name }}" = "pull_request" ] || full "event=${{ github.event_name }}" | |
| base="origin/${{ github.base_ref }}" | |
| changed=$(git diff --name-only "$base"...HEAD) | |
| printf 'changed files vs %s:\n%s\n' "$base" "$changed" | |
| sel="" | |
| add() { case " $sel " in *" $1 "*) ;; *) sel="$sel $1" ;; esac; } | |
| while IFS= read -r f; do | |
| [ -n "$f" ] || continue | |
| case "$f" in | |
| .github/workflows/validate.yml|tests/*.sh) full "$f" ;; | |
| mcpp.toml) | |
| # Workspace manifest. Every new-package PR appends to the | |
| # members list, so that alone must NOT force a full run: | |
| # select the added members; anything else in this file | |
| # (indices, settings) affects everyone → full. | |
| if ! diff -q <(git show "$base:mcpp.toml" | grep -v 'tests/examples/') \ | |
| <(grep -v 'tests/examples/' mcpp.toml) >/dev/null; then | |
| full "mcpp.toml non-member change" | |
| fi | |
| for p in $(comm -13 <(git show "$base:mcpp.toml" | grep -o 'tests/examples/[A-Za-z0-9._-]*' | sort -u) \ | |
| <(grep -o 'tests/examples/[A-Za-z0-9._-]*' mcpp.toml | sort -u)); do | |
| add "${p#tests/examples/}" | |
| done ;; | |
| tests/examples/*) | |
| m=${f#tests/examples/}; m=${m%%/*} | |
| # A deleted/renamed member dir implies a mcpp.toml edit, | |
| # which already forces a full run above. | |
| [ -d "tests/examples/$m" ] && add "$m" ;; | |
| pkgs/*.lua|pkgs/*/*.lua) | |
| lib=$(basename "$f" .lua); lib=${lib#compat.} | |
| hit=0 | |
| for mt in tests/examples/*/mcpp.toml; do | |
| if grep -q "$lib" "$mt"; then add "$(basename "$(dirname "$mt")")"; hit=1; fi | |
| done | |
| [ "$hit" = 1 ] || echo "note: no workspace member exercises $f" ;; | |
| # tools/ holds OFFLINE descriptor-generation and publishing | |
| # helpers (tools/compat-*/, tools/gtc/, publish_mcpp_index.sh). | |
| # Nothing under it is consumed by a package build: when one of | |
| # them actually changes a package, the generated pkgs/*.lua | |
| # changes with it and the rule above selects the right members. | |
| # So a tools/ edit alone selects nothing rather than forcing a | |
| # full workspace rebuild. | |
| *.md|docs/*|.agents/*|.github/*|tools/*) : ;; | |
| *) full "unclassified change: $f" ;; | |
| esac | |
| done <<EOF | |
| $changed | |
| EOF | |
| sel=${sel# } | |
| echo "MEMBERS=$sel" >> "$GITHUB_ENV" | |
| echo "selected members: ${sel:-<none>}" | |
| # ── Refresh the PUBLISHED index before testing ──────────────────── | |
| # Most members resolve everything from this checkout, but a member that | |
| # redirects a namespace other than `compat` gets the REST from the | |
| # published index — and nothing here ever refreshed it. The snapshot in | |
| # play is whatever the pinned mcpp release vendored (the Download step | |
| # `cp -a`s the release's registry/ over ~/.mcpp/registry, on top of the | |
| # restored cache), so it is by construction older than main, and it | |
| # never moves: the cache is saved with that same stale copy inside it. | |
| # | |
| # mcpp does refresh on a miss for a DIRECT dependency, which is why this | |
| # went unnoticed — the gap is a Form-A package's TRANSITIVE dependency. | |
| # tests/examples/godot-cpp-module hit it head-on: the module package's | |
| # own compat.godot-cpp dep resolved against a snapshot predating the | |
| # commit that added it, and failed with `index: local index <sha> (never | |
| # refreshed)` even though the artifact had already been republished. | |
| # (Older members never noticed: their compat packages have been in the | |
| # index far longer than any snapshot.) | |
| - name: Refresh the published package index | |
| shell: bash | |
| env: | |
| MCPP_INDEX_MIRROR: GLOBAL | |
| run: | | |
| "$MCPP" index update | |
| - name: mcpp test (workspace or affected members) | |
| shell: bash | |
| env: | |
| MCPP_INDEX_MIRROR: GLOBAL | |
| # Dependencies build inside each member's own target/ instead of | |
| # through the global package build cache (mcpp >= 2026.7.30.2). | |
| # That cache is unusable here: mcpp#233's object-path disambiguation | |
| # fires on basename collisions across the WHOLE build dir — i.e. on | |
| # which packages the CONSUMER pulls in — while the cache key covers | |
| # only the dependency itself, so one entry can hold two different | |
| # layouts. `tests/examples/archive` pulls zlib AND bzip2 (both ship | |
| # compress.c) and stores obj/compat_zlib/zlib-1.3.2/compress.o; | |
| # every zlib consumer without bzip2 then asks the same key for a | |
| # flat obj/compress.o and ninja dies at graph time with | |
| # "missing and no known rule to make it". Reproduced both ways round | |
| # on 2026.8.3.3 and filed as mcpp-community/mcpp#344; drop this once | |
| # it lands. `local` still caches the std BMI, which is the expensive | |
| # one — only package entries are bypassed. | |
| MCPP_BUILD_CACHE: local | |
| run: | | |
| "$MCPP" --version | |
| # No `timeout` wrapper: absent on macOS runners; job-level timeout-minutes bounds it. | |
| if [ "$MEMBERS" = "__ALL__" ]; then | |
| "$MCPP" test --workspace | |
| elif [ -z "$MEMBERS" ]; then | |
| echo "No workspace member affected by this change — nothing to test." | |
| else | |
| rc=0 | |
| for m in $MEMBERS; do | |
| echo "::group::mcpp test -p $m" | |
| "$MCPP" test -p "$m" || rc=1 | |
| echo "::endgroup::" | |
| done | |
| exit $rc | |
| fi | |
| # install()-driven packages (openssl, openblas) build through their own | |
| # Make/Configure system, whose output xim's interface mode swallows; a | |
| # failed hook surfaces only as `E_INTERNAL: [<pkg>] failed:`. Each writes | |
| # a log into its install prefix, so on failure surface those — otherwise | |
| # diagnosing a platform-specific build break costs a full CI round-trip | |
| # per guess. | |
| - name: Dump install() build logs on failure | |
| if: failure() | |
| shell: bash | |
| run: | | |
| found=0 | |
| while IFS= read -r log; do | |
| found=1 | |
| echo "::group::$log" | |
| tail -80 "$log" | |
| echo "::endgroup::" | |
| done < <(find tests/examples "$HOME/.mcpp/registry" -name 'mcpp_*_build.log' 2>/dev/null) | |
| [ "$found" = 1 ] || echo "no install() build logs found" |