Skip to content

feat: Windows support — LLVM/Clang + MSVC STL + full CI pipeline #43

feat: Windows support — LLVM/Clang + MSVC STL + full CI pipeline

feat: Windows support — LLVM/Clang + MSVC STL + full CI pipeline #43

Workflow file for this run

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