feat(compat.openblas): Windows support (import-lib + runtime DLL) + W… #156
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: | |
| paths: ["pkgs/**/*.lua", "tests/**", "README.md", ".github/workflows/validate.yml"] | |
| push: | |
| branches: [main] | |
| schedule: | |
| # nightly full regression — exercises every smoke suite regardless of diff | |
| - cron: "0 6 * * *" | |
| workflow_dispatch: | |
| env: | |
| # Bumped to 0.0.73: carries Windows runtime-DLL deployment, required by the | |
| # compat.openblas Windows recipe (bin/libopenblas.dll staged beside the .exe). | |
| MCPP_VERSION: "0.0.73" | |
| 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 | |
| done | |
| [ $fail -eq 0 ] && echo "All package files valid." | |
| 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 | |
| # ── Decide what to smoke-test ───────────────────────────────────────── | |
| # PR builds run ONLY the example projects whose package descriptor changed | |
| # (tests/examples/<pkg>/). Anything that can't be narrowed that way — a | |
| # changed pkg without an example yet, a scaffolding/CI change, or a | |
| # push/nightly/manual run — falls back to the full legacy smoke regression. | |
| detect: | |
| runs-on: ubuntu-latest | |
| outputs: | |
| examples: ${{ steps.f.outputs.examples }} | |
| full: ${{ steps.f.outputs.full }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: Install jq | |
| run: sudo apt-get install -y --no-install-recommends jq | |
| - id: f | |
| run: | | |
| full=false | |
| examples='[]' | |
| event="${{ github.event_name }}" | |
| if [ "$event" != "pull_request" ]; then | |
| # push to main / nightly schedule / manual dispatch → full regression | |
| full=true | |
| else | |
| git fetch -q origin "${{ github.base_ref }}" | |
| changed=$(git diff --name-only "origin/${{ github.base_ref }}...HEAD") | |
| echo "changed files:"; echo "$changed" | sed 's/^/ /' | |
| # scaffolding / CI change → full regression | |
| if echo "$changed" | grep -qE '^(tests/run_example\.sh|tests/smoke_.*\.sh|tests/check_mirror_urls\.lua|tests/list_cn_urls\.lua|\.github/workflows/validate\.yml)$'; then | |
| full=true | |
| fi | |
| sel="" | |
| while IFS= read -r file; do | |
| case "$file" in | |
| pkgs/*/*.lua) | |
| b=$(basename "$file" .lua); b=${b#compat.} | |
| if [ -d "tests/examples/$b" ]; then | |
| sel="$sel$b"$'\n' | |
| else | |
| # changed pkg with no example yet → cannot narrow, run full | |
| full=true | |
| fi | |
| ;; | |
| esac | |
| done <<< "$changed" | |
| if [ -n "$sel" ]; then | |
| examples=$(printf '%s' "$sel" | grep -v '^$' | sort -u | jq -R . | jq -sc .) | |
| fi | |
| fi | |
| echo "full=$full" >> "$GITHUB_OUTPUT" | |
| echo "examples=$examples" >> "$GITHUB_OUTPUT" | |
| echo "=> full=$full examples=$examples" | |
| # ── Fast path: per-changed-package example projects (Linux) ─────────── | |
| smoke-examples: | |
| needs: detect | |
| if: needs.detect.outputs.examples != '[]' | |
| runs-on: ubuntu-latest | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| pkg: ${{ fromJson(needs.detect.outputs.examples) }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Restore mcpp registry cache | |
| uses: actions/cache@v4 | |
| with: | |
| path: ~/.mcpp/registry | |
| key: mcpp-registry-${{ runner.os }}-${{ env.MCPP_VERSION }}-${{ hashFiles('pkgs/**/*.lua', 'tests/**', '.github/workflows/validate.yml') }} | |
| restore-keys: | | |
| mcpp-registry-${{ runner.os }}-${{ env.MCPP_VERSION }}- | |
| - name: Download 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 | |
| root="$PWD/mcpp-${MCPP_VERSION}-linux-x86_64" | |
| mkdir -p "$HOME/.mcpp/registry" | |
| cp -a "$root/registry/." "$HOME/.mcpp/registry/" | |
| echo "MCPP=$root/bin/mcpp" >> "$GITHUB_ENV" | |
| echo "MCPP_VENDORED_XLINGS=$root/registry/bin/xlings" >> "$GITHUB_ENV" | |
| echo "$root/bin" >> "$GITHUB_PATH" | |
| - name: Run example "${{ matrix.pkg }}" | |
| env: | |
| MCPP_INDEX_MIRROR: GLOBAL | |
| run: | | |
| "$MCPP" --version | |
| timeout 1800 bash tests/run_example.sh "${{ matrix.pkg }}" | |
| # ── Full regression (legacy whole-suite smoke) ──────────────────────── | |
| smoke-full-linux: | |
| needs: detect | |
| if: needs.detect.outputs.full == 'true' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Restore mcpp registry cache | |
| uses: actions/cache@v4 | |
| with: | |
| path: ~/.mcpp/registry | |
| key: mcpp-registry-${{ runner.os }}-${{ env.MCPP_VERSION }}-${{ hashFiles('pkgs/**/*.lua', 'tests/**', '.github/workflows/validate.yml') }} | |
| restore-keys: | | |
| mcpp-registry-${{ runner.os }}-${{ env.MCPP_VERSION }}- | |
| - name: Download 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 | |
| root="$PWD/mcpp-${MCPP_VERSION}-linux-x86_64" | |
| mkdir -p "$HOME/.mcpp/registry" | |
| cp -a "$root/registry/." "$HOME/.mcpp/registry/" | |
| echo "MCPP=$root/bin/mcpp" >> "$GITHUB_ENV" | |
| echo "MCPP_VENDORED_XLINGS=$root/registry/bin/xlings" >> "$GITHUB_ENV" | |
| echo "$root/bin" >> "$GITHUB_PATH" | |
| - name: Run compat smoke tests | |
| env: | |
| MCPP_INDEX_SMOKE_MCPP_HOME: ${{ runner.temp }}/mcpp-smoke-home | |
| MCPP_INDEX_SMOKE_CACHE_DIR: ${{ runner.temp }}/mcpp-smoke-cache | |
| run: | | |
| mkdir -p "$MCPP_INDEX_SMOKE_MCPP_HOME" | |
| "$MCPP" --version | |
| timeout 1800 bash tests/smoke_compat_core.sh | |
| timeout 1800 bash tests/smoke_compat_imgui.sh | |
| timeout 1800 bash tests/smoke_compat_archive.sh | |
| # GLFW/OpenGL window + imgui-module demos. These pull compat.glfw | |
| # (abi:glibc). The imgui-module smoke pins llvm@20.1.7 (clang/libc++); | |
| # on mcpp ≤0.0.67 that tripped the conflated `abi:` gate ("requires | |
| # abi=glibc … is abi=libc++") because a clang+libc++ toolchain on | |
| # *-linux-gnu was mislabelled. Fixed in mcpp 0.0.68 (dimensional ABI | |
| # model: libc and cxxstdlib are independent axes), so these are back in | |
| # the blocking set. The window run itself stays opt-in (needs a display). | |
| timeout 1800 bash tests/smoke_compat_imgui_window.sh | |
| timeout 1800 bash tests/smoke_imgui_module.sh | |
| smoke-portable: | |
| needs: detect | |
| if: needs.detect.outputs.full == 'true' | |
| name: smoke-${{ matrix.platform }} | |
| runs-on: ${{ matrix.os }} | |
| timeout-minutes: 60 | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - platform: macos | |
| os: macos-15 | |
| archive: mcpp-0.0.73-macosx-arm64.tar.gz | |
| root: mcpp-0.0.73-macosx-arm64 | |
| mcpp: bin/mcpp | |
| xlings: registry/bin/xlings | |
| - platform: windows | |
| os: windows-latest | |
| archive: mcpp-0.0.73-windows-x86_64.zip | |
| root: mcpp-0.0.73-windows-x86_64 | |
| mcpp: bin/mcpp.exe | |
| xlings: registry/bin/xlings.exe | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Restore mcpp registry cache | |
| uses: actions/cache@v4 | |
| with: | |
| path: ~/.mcpp/registry | |
| key: mcpp-registry-${{ runner.os }}-${{ env.MCPP_VERSION }}-${{ hashFiles('pkgs/**/*.lua', 'tests/**', '.github/workflows/validate.yml') }} | |
| restore-keys: | | |
| mcpp-registry-${{ runner.os }}-${{ env.MCPP_VERSION }}- | |
| - name: Download mcpp | |
| shell: bash | |
| env: | |
| MCPP_ARCHIVE: ${{ matrix.archive }} | |
| MCPP_ROOT: ${{ matrix.root }} | |
| run: | | |
| curl -L -fsS -o "$MCPP_ARCHIVE" \ | |
| "https://github.com/mcpp-community/mcpp/releases/download/v${MCPP_VERSION}/${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 | |
| - name: Run portable compat smoke tests | |
| shell: bash | |
| env: | |
| MCPP_INDEX_MIRROR: GLOBAL | |
| run: | | |
| "$MCPP" --version | |
| bash tests/smoke_compat_portable.sh |