feat: Windows support — LLVM/Clang + MSVC STL + full CI pipeline #43
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: ci-windows | |
| # Windows CI for mcpp — validates LLVM/Clang as the Windows toolchain. | |
| # Uses xmake to bootstrap mcpp from source (until xlings mcpp ships | |
| # Windows-native fixes), then self-hosts, tests, and packages. | |
| on: | |
| push: | |
| branches: [ main ] | |
| pull_request: | |
| branches: [ main ] | |
| workflow_dispatch: | |
| concurrency: | |
| group: ci-windows-${{ github.ref }} | |
| cancel-in-progress: true | |
| jobs: | |
| build-and-test: | |
| name: build + test (windows x64, self-host) | |
| runs-on: windows-latest | |
| timeout-minutes: 45 | |
| env: | |
| MCPP_HOME: C:\Users\runneradmin\.mcpp | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: System info | |
| shell: bash | |
| run: | | |
| echo "OS: $(uname -s)" | |
| echo "Arch: $(uname -m)" | |
| echo "Runner: $RUNNER_OS" | |
| - name: Install xlings | |
| shell: bash | |
| env: | |
| XLINGS_NON_INTERACTIVE: '1' | |
| XLINGS_VERSION: '0.4.30' | |
| run: | | |
| WORK=$(mktemp -d) | |
| zipfile="xlings-${XLINGS_VERSION}-windows-x86_64.zip" | |
| curl -fsSL -o "${WORK}/${zipfile}" \ | |
| "https://github.com/d2learn/xlings/releases/download/v${XLINGS_VERSION}/${zipfile}" | |
| cd "${WORK}" | |
| unzip -q "${zipfile}" | |
| "$WORK/xlings-${XLINGS_VERSION}-windows-x86_64/subos/default/bin/xlings.exe" self install | |
| echo "$USERPROFILE/.xlings/subos/default/bin" >> "$GITHUB_PATH" | |
| echo "$USERPROFILE/.xlings/bin" >> "$GITHUB_PATH" | |
| - name: Install LLVM + xmake via xlings | |
| shell: bash | |
| run: | | |
| xlings.exe --version | |
| xlings.exe install llvm -y || xlings.exe install llvm@20.1.7 -y | |
| xlings.exe install xmake -y | |
| - name: Bootstrap mcpp with xmake (MSVC) | |
| shell: pwsh | |
| run: | | |
| @" | |
| add_rules("mode.release") | |
| set_languages("c++23") | |
| package("cmdline") | |
| set_homepage("https://github.com/mcpplibs/cmdline") | |
| add_urls("https://github.com/mcpplibs/cmdline/archive/refs/tags/`$(version).tar.gz") | |
| add_versions("0.0.1", "3fb2f5495c1a144485b3cbb2e43e27059151633460f702af0f3851cbff387ef0") | |
| on_install(function (package) | |
| import("package.tools.xmake").install(package) | |
| end) | |
| package_end() | |
| add_requires("cmdline 0.0.1") | |
| target("mcpp") | |
| set_kind("binary") | |
| add_files("src/main.cpp") | |
| add_files("src/**.cppm") | |
| add_packages("cmdline") | |
| add_includedirs("src/libs/json") | |
| set_policy("build.c++.modules", true) | |
| "@ | Out-File -Encoding utf8 xmake.lua | |
| xmake f -p windows -m release -y | |
| xmake build -y mcpp | |
| $mcpp = Get-ChildItem -Recurse build -Filter mcpp.exe -ErrorAction SilentlyContinue | Select-Object -First 1 | |
| if ($mcpp) { | |
| Write-Host ":: mcpp.exe built at: $($mcpp.FullName)" | |
| & $mcpp.FullName --version | |
| "MCPP_BOOTSTRAP=$($mcpp.FullName)" | Out-File -Append $env:GITHUB_ENV | |
| } else { | |
| exit 1 | |
| } | |
| - name: Self-host — mcpp builds itself | |
| shell: bash | |
| run: | | |
| # Save bootstrap binary, clean xmake artifacts | |
| mkdir -p /tmp/mcpp-bootstrap | |
| cp "$MCPP_BOOTSTRAP" /tmp/mcpp-bootstrap/mcpp.exe | |
| MCPP_EXE="/tmp/mcpp-bootstrap/mcpp.exe" | |
| rm -rf build xmake.lua .xmake | |
| XLINGS_WIN=$(cygpath -w "$USERPROFILE/.xlings/subos/default/bin/xlings.exe") | |
| export MCPP_VENDORED_XLINGS="$XLINGS_WIN" | |
| # Pre-seed LLVM from global xlings | |
| MCPP_XPKGS="$USERPROFILE/.mcpp/registry/data/xpkgs" | |
| XLINGS_XPKGS="$USERPROFILE/.xlings/data/xpkgs" | |
| if [ -d "$XLINGS_XPKGS/xim-x-llvm" ]; then | |
| mkdir -p "$MCPP_XPKGS" | |
| rm -rf "$MCPP_XPKGS/xim-x-llvm" | |
| cp -r "$XLINGS_XPKGS/xim-x-llvm" "$MCPP_XPKGS/xim-x-llvm" | |
| fi | |
| "$MCPP_EXE" build | |
| SELF_MCPP=$(find target -name "mcpp.exe" -path "*/bin/*" | head -1) | |
| test -n "$SELF_MCPP" || { echo "FAIL: no mcpp.exe"; exit 1; } | |
| SELF_MCPP=$(cd "$(dirname "$SELF_MCPP")" && pwd)/$(basename "$SELF_MCPP") | |
| echo "Self-hosted binary: $SELF_MCPP" | |
| "$SELF_MCPP" --version | |
| echo "MCPP_SELF=$SELF_MCPP" >> "$GITHUB_ENV" | |
| # NOTE: `mcpp test` (unit tests via gtest) requires clang-scan-deps | |
| # for module dependency scanning. The xlings LLVM package does not | |
| # yet ship clang-scan-deps on Windows. Skip until available. | |
| # - name: Unit + integration tests via mcpp test | |
| - name: E2E suite | |
| shell: bash | |
| run: | | |
| export MCPP="$MCPP_SELF" | |
| export MCPP_VENDORED_XLINGS=$(cygpath -w "$USERPROFILE/.xlings/subos/default/bin/xlings.exe") | |
| export MCPP_E2E_TOOLCHAIN_MIRROR=GLOBAL | |
| "$MCPP_SELF" self config --mirror GLOBAL 2>/dev/null || true | |
| "$MCPP_SELF" toolchain default llvm@20.1.7 2>/dev/null || true | |
| bash tests/e2e/run_all.sh | |
| - name: Self-host smoke (freshly-built mcpp builds itself again) | |
| shell: bash | |
| run: | | |
| export MCPP_VENDORED_XLINGS=$(cygpath -w "$USERPROFILE/.xlings/subos/default/bin/xlings.exe") | |
| "$MCPP_SELF" build | |
| "$MCPP_SELF" --version | |
| echo ":: Self-host smoke PASS" | |
| - name: Package Windows release zip | |
| id: package | |
| shell: bash | |
| run: | | |
| VERSION=$(awk -F '"' '/^version[[:space:]]*=/{print $2; exit}' mcpp.toml) | |
| WRAPPER="mcpp-${VERSION}-windows-x86_64" | |
| ZIPNAME="${WRAPPER}.zip" | |
| STAGING=$(mktemp -d) | |
| mkdir -p "$STAGING/$WRAPPER/bin" "$STAGING/$WRAPPER/registry/bin" | |
| cp "$MCPP_SELF" "$STAGING/$WRAPPER/bin/mcpp.exe" | |
| printf '@echo off\r\n"%%~dp0bin\\mcpp.exe" %%*\r\n' > "$STAGING/$WRAPPER/mcpp.bat" | |
| cp README.md "$STAGING/$WRAPPER/" 2>/dev/null || true | |
| cp LICENSE "$STAGING/$WRAPPER/" 2>/dev/null || true | |
| XLINGS_EXE="$USERPROFILE/.xlings/subos/default/bin/xlings.exe" | |
| [ -f "$XLINGS_EXE" ] && cp "$XLINGS_EXE" "$STAGING/$WRAPPER/registry/bin/xlings.exe" | |
| mkdir -p dist | |
| (cd "$STAGING" && 7z a -tzip "$ZIPNAME" "$WRAPPER") | |
| cp "$STAGING/$ZIPNAME" "dist/$ZIPNAME" | |
| (cd dist && sha256sum "$ZIPNAME" > "$ZIPNAME.sha256") | |
| echo "zipname=$ZIPNAME" >> "$GITHUB_OUTPUT" | |
| ls -la dist/ | |
| - name: Smoke-test the packaged zip | |
| shell: bash | |
| run: | | |
| ZIPNAME="${{ steps.package.outputs.zipname }}" | |
| WRAPPER="${ZIPNAME%.zip}" | |
| SMOKE=$(mktemp -d) | |
| (cd "$SMOKE" && unzip -q "$GITHUB_WORKSPACE/dist/$ZIPNAME") | |
| "$SMOKE/$WRAPPER/bin/mcpp.exe" --version | |
| test -f "$SMOKE/$WRAPPER/registry/bin/xlings.exe" | |
| test -f "$SMOKE/$WRAPPER/mcpp.bat" | |
| echo "Smoke-test passed" | |
| - name: Upload artifact | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: mcpp-windows-x86_64 | |
| path: | | |
| dist/*.zip | |
| dist/*.sha256 |