Skip to content

ci(windows): stub __imp_getenv for freestanding link; bump test timeo… #205

ci(windows): stub __imp_getenv for freestanding link; bump test timeo…

ci(windows): stub __imp_getenv for freestanding link; bump test timeo… #205

name: Windows LLVM Build
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
# Automatically cancel any previous workflow on new push.
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}-${{ github.event_name }}
cancel-in-progress: true
jobs:
build-and-test:
name: ${{ matrix.flavour }} on Windows with LLVM
runs-on: windows-latest
strategy:
fail-fast: false
matrix:
include:
# Standard pass: clang (GCC-style driver) + lld. Full UCRT,
# no sanitizers (see Configure step for why). Verifies the
# build works with default CRT linkage. This is the
# historically-proven config for this workflow.
- flavour: standard
cc: clang
cc_ld: lld
setup_extra: ''
verify_libc_diet: 'no'
# Freestanding pass: clang-cl (MSVC driver) + lld-link.
# Needed because the freestanding link args in meson.build
# are MSVC syntax (/NODEFAULTLIB:..., /ENTRY:misra_start,
# /SUBSYSTEM:CONSOLE) and only clang-cl forwards them to
# the linker. MSVC syntax also requires lld in MSVC mode
# (lld-link), not the generic lld dispatcher. Both
# conditions come together at have_misra_start_win in
# meson.build (clang-cl + x86_64).
- flavour: freestanding
cc: clang-cl
cc_ld: lld-link
setup_extra: ''
verify_libc_diet: 'yes'
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.x'
- name: Install dependencies
run: |
pip install meson ninja
shell: pwsh
- name: Download and install LLVM/Clang
run: |
# Download Clang
Invoke-WebRequest -Uri "https://github.com/llvm/llvm-project/releases/download/llvmorg-18.1.8/clang+llvm-18.1.8-x86_64-pc-windows-msvc.tar.xz" -OutFile "clang.tar.xz"
# Extract the archive (using 7zip which is available on Windows runners)
7z x clang.tar.xz
7z x clang.tar
# Move to a simpler path
Move-Item "clang+llvm-18.1.8-x86_64-pc-windows-msvc" "C:\clang"
# Add to PATH
echo "C:\clang\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
shell: pwsh
- name: Verify Clang installation
run: |
clang --version
clang++ --version
lld --version
llvm-ar --version
shell: pwsh
- name: Configure with Clang
run: |
# Set environment variables for Meson to use Clang with lld linker.
# `CC_LD` is meson's first-class way to pick a non-default
# linker (https://mesonbuild.com/howtox.html). Doing this via
# env vars (not -Dc_link_args=...) avoids meson bug #14640
# where setting c_link_args suppresses b_sanitize injection.
$env:CC = "${{ matrix.cc }}"
$env:CXX = "${{ matrix.cc }}"
$env:CC_LD = "${{ matrix.cc_ld }}"
$env:CXX_LD = "${{ matrix.cc_ld }}"
$env:AR = "llvm-ar"
$env:RANLIB = "llvm-ranlib"
# Sanitizers are intentionally NOT enabled on this workflow.
# Two distinct things conspire against them:
#
# 1. AddressSanitizer + clang on Windows + meson is known to
# misbehave at link time (the bundled VS2022 clang's asan
# runtime ABIs don't match upstream).
#
# 2. UndefinedBehaviorSanitizer fails the link because the
# `windows-latest` runner has VS2022 on PATH, so
# lld-link's LIB-env-var search picks up Microsoft's
# modified `clang_rt.ubsan_standalone-x86_64.lib` (with
# __coe_win::ContinueOnError / __coe_win::RawWrite
# references that only MS's proprietary "Continue On
# Error" stubs provide) instead of our installed LLVM
# 18.1.8's clang_rt. Pointing LIB at our LLVM's
# lib\windows directory before VS's is possible but
# fragile; not worth the complexity.
#
# Both matrix entries (standard + freestanding) therefore run
# without sanitizers. ASan coverage on Windows lives in the
# MSVC matrix entry.
meson setup build --backend ninja `
-Dwarning_level=2 -Db_lundef=false ${{ matrix.setup_extra }}
shell: pwsh
- name: Build
run: |
meson compile -C build
- name: Test
run: |
meson test -C build --print-errorlogs
# Libc-diet assertion: Bin/ tools must import only OS-provided
# platform DLLs. Anything UCRT/vcruntime (ucrtbase.dll,
# vcruntime140.dll, msvcp140.dll, etc.) means the diet regressed.
# Allowed: kernel32 + ws2_32 (Sockets) + dbghelp (Backtrace) +
# advapi32 (some Win32 calls pull it). The meson auto-injected
# user32/gdi32/etc. show up in the link line but typically don't
# end up in IAT unless we actually call them.
#
# Uses llvm-readobj (already on PATH from the LLVM install step).
# dumpbin would require VS dev-prompt to be sourced first.
- name: Verify libc-diet (Bin/ tools import only platform DLLs)
if: matrix.verify_libc_diet == 'yes'
run: |
$allowed = @(
'KERNEL32.dll', 'WS2_32.dll', 'DBGHELP.DLL', 'ADVAPI32.dll',
'USER32.dll', 'GDI32.dll', 'WINSPOOL.DRV', 'SHELL32.dll',
'ole32.dll', 'OLEAUT32.dll', 'COMDLG32.dll'
)
$forbidden_pattern = '^(ucrtbase|ucrtbased|vcruntime\d+|vcruntime\d+d|msvcp\d+|msvcp\d+d|msvcr\d+|msvcr\d+d|api-ms-win-crt-)'
$failed = 0
$bins = @('beam.exe', 'resolve.exe')
foreach ($bn in $bins) {
$path = "build\$bn"
if (-not (Test-Path $path)) {
Write-Host "::warning::$path not built, skipping libc-diet check"
continue
}
# llvm-readobj --coff-imports prints one block per imported
# DLL with a 'Name: <dll>' line at the top of each.
$imports = & llvm-readobj --coff-imports $path 2>$null `
| Select-String -Pattern '^\s*Name:\s*(\S+)' `
| ForEach-Object { $_.Matches[0].Groups[1].Value }
$bad = @()
foreach ($imp in $imports) {
if ($imp -match $forbidden_pattern) {
$bad += "$imp (UCRT/vcruntime)"
} else {
$ok = $false
foreach ($a in $allowed) {
if ($imp -ieq $a) { $ok = $true; break }
}
if (-not $ok) {
$bad += "$imp (not in platform allowlist)"
}
}
}
if ($bad.Count -gt 0) {
Write-Host "::error::$bn imports forbidden DLLs (libc-diet broken):"
foreach ($b in $bad) { Write-Host " $b" }
$failed = 1
} else {
Write-Host "OK: $bn imports $($imports -join ', ')"
}
}
exit $failed
shell: pwsh
- name: Upload test logs
# Always upload so we can audit which tests actually ran. Without
# the artifact we have no way to tell a green run with N tests
# from a green run with 0 tests.
if: always()
uses: actions/upload-artifact@v4
with:
name: test-logs-windows-llvm-${{ matrix.flavour }}
path: build/meson-logs/testlog.txt