fix: skip self-host on Windows (mcpp build system lacks MSVC support) #10
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 validation CI for mcpp. | |
| # Step 1: Verify xlings LLVM toolchain capabilities on Windows. | |
| # Step 2: xmake bootstrap to produce first mcpp.exe. | |
| # Step 3: Package into a distributable zip (same layout as Linux/macOS). | |
| # NOTE: self-host (mcpp building itself) not yet possible — needs MSVC toolchain support. | |
| on: | |
| push: | |
| branches: [ feat/windows-support ] | |
| pull_request: | |
| branches: [ main ] | |
| paths: | |
| - 'src/toolchain/**' | |
| - 'src/build/**' | |
| - 'src/cli.cppm' | |
| - '.github/workflows/ci-windows.yml' | |
| workflow_dispatch: | |
| concurrency: | |
| group: ci-windows-${{ github.ref }} | |
| cancel-in-progress: true | |
| jobs: | |
| windows-llvm-validation: | |
| name: Windows x64 — LLVM toolchain validation | |
| runs-on: windows-latest | |
| timeout-minutes: 45 | |
| 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}" | |
| XLINGS_DIR="${WORK}/xlings-${XLINGS_VERSION}-windows-x86_64" | |
| echo "xlings dir: $XLINGS_DIR" | |
| ls "$XLINGS_DIR/bin/" | |
| "$XLINGS_DIR/subos/default/bin/xlings.exe" self install | |
| echo "$USERPROFILE/.xlings/subos/default/bin" >> "$GITHUB_PATH" | |
| echo "$USERPROFILE/.xlings/bin" >> "$GITHUB_PATH" | |
| - name: Verify xlings | |
| shell: bash | |
| run: | | |
| xlings.exe --version || xlings --version || echo "xlings not found in PATH" | |
| - name: Install LLVM via xlings | |
| shell: bash | |
| run: | | |
| xlings.exe install llvm -y || xlings.exe install llvm@20.1.7 -y || { | |
| echo "::warning::xlings install llvm failed" | |
| exit 1 | |
| } | |
| # Find LLVM root | |
| LLVM_ROOT=$(find "$USERPROFILE/.xlings" -path "*/xpkgs/xim-x-llvm/*/bin/clang.exe" 2>/dev/null | head -1) | |
| if [ -n "$LLVM_ROOT" ]; then | |
| LLVM_ROOT=$(dirname "$(dirname "$LLVM_ROOT")") | |
| fi | |
| echo "LLVM_ROOT=$LLVM_ROOT" | |
| echo "LLVM_ROOT=$LLVM_ROOT" >> "$GITHUB_ENV" | |
| - name: Inspect LLVM package structure | |
| shell: bash | |
| run: | | |
| echo "=== bin/ ===" | |
| ls "$LLVM_ROOT/bin/" | grep -iE "clang|lld|llvm-ar|scan" | head -20 | |
| echo "=== lib/ ===" | |
| ls "$LLVM_ROOT/lib/" 2>/dev/null | head -10 | |
| echo "=== include/c++/ ===" | |
| ls "$LLVM_ROOT/include/c++/" 2>/dev/null || echo "(no libc++ headers)" | |
| echo "=== share/libc++/ ===" | |
| find "$LLVM_ROOT" -name "std.cppm" 2>/dev/null || echo "(no std.cppm)" | |
| echo "=== clang++.cfg ===" | |
| cat "$LLVM_ROOT/bin/clang++.cfg" 2>/dev/null || echo "(no cfg)" | |
| echo "=== clang version ===" | |
| "$LLVM_ROOT/bin/clang++.exe" --version 2>/dev/null || "$LLVM_ROOT/bin/clang++" --version 2>/dev/null | |
| echo "=== Target triple ===" | |
| "$LLVM_ROOT/bin/clang++.exe" -dumpmachine 2>/dev/null || "$LLVM_ROOT/bin/clang++" -dumpmachine 2>/dev/null | |
| - name: Test — non-module C++ compilation (clang++) | |
| shell: bash | |
| run: | | |
| WORK=$(mktemp -d) | |
| cd "$WORK" | |
| CXX="$LLVM_ROOT/bin/clang++.exe" | |
| test -x "$CXX" || CXX="$LLVM_ROOT/bin/clang++" | |
| cat > main.cpp << 'EOF' | |
| #include <iostream> | |
| int main() { | |
| std::cout << "Hello from clang++ on Windows!" << std::endl; | |
| return 0; | |
| } | |
| EOF | |
| echo "=== Compile ===" | |
| "$CXX" -std=c++23 -o hello.exe main.cpp 2>&1 || { | |
| echo "::warning::clang++ compilation failed, trying with MSVC headers" | |
| # clang++ on Windows may need MSVC include paths | |
| "$CXX" -std=c++23 --target=x86_64-pc-windows-msvc -o hello.exe main.cpp 2>&1 || true | |
| } | |
| if [ -f hello.exe ]; then | |
| echo "=== Run ===" | |
| ./hello.exe | |
| else | |
| echo "::warning::Compilation did not produce hello.exe" | |
| fi | |
| - name: Test — clang-cl compilation | |
| shell: bash | |
| run: | | |
| WORK=$(mktemp -d) | |
| cd "$WORK" | |
| CLANGCL="$LLVM_ROOT/bin/clang-cl.exe" | |
| test -x "$CLANGCL" || CLANGCL="$LLVM_ROOT/bin/clang-cl" | |
| cat > main.cpp << 'EOF' | |
| #include <iostream> | |
| int main() { | |
| std::cout << "Hello from clang-cl on Windows!" << std::endl; | |
| return 0; | |
| } | |
| EOF | |
| echo "=== Compile with clang-cl ===" | |
| "$CLANGCL" /std:c++latest /EHsc main.cpp /Fe:hello.exe 2>&1 || { | |
| echo "::warning::clang-cl compilation failed" | |
| echo "clang-cl may need Visual Studio installation for MSVC headers/libs" | |
| } | |
| if [ -f hello.exe ]; then | |
| echo "=== Run ===" | |
| ./hello.exe | |
| fi | |
| - name: Check libc++ availability for import std | |
| shell: bash | |
| run: | | |
| echo "=== Checking for libc++ in LLVM package ===" | |
| find "$LLVM_ROOT" -name "*.cppm" 2>/dev/null | head -5 || echo "No .cppm files found" | |
| find "$LLVM_ROOT" -path "*/c++/v1" -type d 2>/dev/null | head -3 || echo "No libc++ include dir" | |
| find "$LLVM_ROOT" -name "libc++*" 2>/dev/null | head -5 || echo "No libc++ library files" | |
| echo | |
| echo "=== Checking MSVC STL for import std ===" | |
| # Visual Studio on GitHub runners includes MSVC STL with module support | |
| VSDIR="/c/Program Files/Microsoft Visual Studio/2022/Enterprise" | |
| if [ -d "$VSDIR" ]; then | |
| echo "Visual Studio found at: $VSDIR" | |
| find "$VSDIR" -name "std.ixx" 2>/dev/null | head -3 || echo "No std.ixx found" | |
| find "$VSDIR" -path "*/modules" -name "*.ixx" 2>/dev/null | head -5 || echo "No .ixx module files" | |
| else | |
| echo "Visual Studio not found at expected path" | |
| ls "/c/Program Files/Microsoft Visual Studio/" 2>/dev/null || true | |
| fi | |
| - name: Summary | |
| shell: bash | |
| run: | | |
| echo "=== Windows LLVM Validation Summary ===" | |
| echo "LLVM Root: $LLVM_ROOT" | |
| echo "Clang version: $("$LLVM_ROOT/bin/clang++.exe" --version 2>/dev/null | head -1 || echo 'N/A')" | |
| echo "Target: $("$LLVM_ROOT/bin/clang++.exe" -dumpmachine 2>/dev/null || echo 'N/A')" | |
| echo | |
| echo "Has libc++: $([ -d "$LLVM_ROOT/include/c++/v1" ] && echo YES || echo NO)" | |
| echo "Has std.cppm: $(find "$LLVM_ROOT" -name 'std.cppm' 2>/dev/null | head -1 | grep -q . && echo YES || echo NO)" | |
| echo "Has clang-scan-deps: $([ -f "$LLVM_ROOT/bin/clang-scan-deps.exe" ] && echo YES || echo NO)" | |
| echo "Has clang-cl: $([ -f "$LLVM_ROOT/bin/clang-cl.exe" ] && echo YES || echo NO)" | |
| echo "Has lld-link: $([ -f "$LLVM_ROOT/bin/lld-link.exe" ] && echo YES || echo NO)" | |
| - name: Install xmake via xlings | |
| shell: bash | |
| run: | | |
| xlings.exe install xmake -y || xlings install xmake -y | |
| xmake.exe --version || xmake --version | |
| - name: Bootstrap mcpp with xmake (MSVC) | |
| id: bootstrap | |
| shell: pwsh | |
| run: | | |
| # Generate xmake.lua for bootstrap | |
| @" | |
| 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 | |
| # Export for subsequent steps | |
| "MCPP_BOOTSTRAP=$($mcpp.FullName)" | Out-File -Append $env:GITHUB_ENV | |
| } else { | |
| Write-Host ":: Build produced files:" | |
| Get-ChildItem -Recurse build -Filter *.exe | ForEach-Object { Write-Host " $($_.FullName)" } | |
| exit 1 | |
| } | |
| # NOTE: full self-host (`mcpp build` building itself) is not yet | |
| # possible on Windows — mcpp's build system defaults to gcc which | |
| # is unavailable here. Once mcpp.toml gains a `windows = "msvc"` | |
| # toolchain override (and detect.cppm handles MSVC output), the | |
| # self-host step can be re-enabled. For now we package the xmake- | |
| # bootstrapped binary, which is functionally identical. | |
| - name: Package Windows release zip | |
| id: package | |
| shell: bash | |
| run: | | |
| VERSION=$(awk -F '"' '/^version[[:space:]]*=/{print $2; exit}' mcpp.toml) | |
| PLAT="windows-x86_64" | |
| WRAPPER="mcpp-${VERSION}-${PLAT}" | |
| ZIPNAME="${WRAPPER}.zip" | |
| echo "Packaging $ZIPNAME ..." | |
| STAGING=$(mktemp -d) | |
| mkdir -p "$STAGING/$WRAPPER/bin" | |
| mkdir -p "$STAGING/$WRAPPER/registry/bin" | |
| # Binary | |
| cp "$MCPP_BOOTSTRAP" "$STAGING/$WRAPPER/bin/mcpp.exe" | |
| # Launcher batch script (equivalent to the shell wrapper on Linux/macOS) | |
| printf '@echo off\r\n"%%~dp0bin\\mcpp.exe" %%*\r\n' > "$STAGING/$WRAPPER/mcpp.bat" | |
| # Metadata | |
| cp README.md "$STAGING/$WRAPPER/" 2>/dev/null || true | |
| cp LICENSE "$STAGING/$WRAPPER/" 2>/dev/null || true | |
| # Bundle xlings | |
| XLINGS_EXE="$USERPROFILE/.xlings/subos/default/bin/xlings.exe" | |
| if [ -f "$XLINGS_EXE" ]; then | |
| cp "$XLINGS_EXE" "$STAGING/$WRAPPER/registry/bin/xlings.exe" | |
| echo "Bundled xlings.exe" | |
| else | |
| echo "::warning::xlings.exe not found at $XLINGS_EXE" | |
| fi | |
| # Create zip | |
| mkdir -p dist | |
| (cd "$STAGING" && powershell.exe -Command \ | |
| "Compress-Archive -Path '$WRAPPER' -DestinationPath '$WRAPPER.zip'") | |
| cp "$STAGING/$ZIPNAME" "dist/$ZIPNAME" | |
| # SHA256 | |
| (cd dist && sha256sum "$ZIPNAME" > "$ZIPNAME.sha256") | |
| echo "version=$VERSION" >> "$GITHUB_OUTPUT" | |
| echo "zipname=$ZIPNAME" >> "$GITHUB_OUTPUT" | |
| ls -la dist/ | |
| - name: Smoke-test the packaged zip | |
| shell: bash | |
| run: | | |
| VERSION="${{ steps.package.outputs.version }}" | |
| ZIPNAME="${{ steps.package.outputs.zipname }}" | |
| WRAPPER="${ZIPNAME%.zip}" | |
| SMOKE=$(mktemp -d) | |
| (cd "$SMOKE" && unzip -q "$GITHUB_WORKSPACE/dist/$ZIPNAME") | |
| echo "=== Layout ===" | |
| find "$SMOKE/$WRAPPER" -type f | |
| echo "=== Version check ===" | |
| "$SMOKE/$WRAPPER/bin/mcpp.exe" --version | |
| echo "=== xlings bundled ===" | |
| test -f "$SMOKE/$WRAPPER/registry/bin/xlings.exe" | |
| echo "=== Launcher ===" | |
| 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 |