Skip to content

Build Windows

Build Windows #3

Workflow file for this run

name: Build Windows
permissions:
contents: read
pull-requests: read
on:
workflow_call:
inputs:
game:
required: true
type: string
description: "Game to build (GeneralsMD, Generals)"
preset:
required: false
type: string
default: "win64-modern"
description: "CMake preset for Windows"
workflow_dispatch:
inputs:
game:
type: choice
description: "Game to build"
options:
- GeneralsMD
- Generals
default: GeneralsMD
preset:
type: choice
description: "CMake preset for Windows"
options:
- win64-modern
default: win64-modern
jobs:
build:
name: ${{ inputs.game }}@${{ inputs.preset }}
runs-on: windows-latest
timeout-minutes: 120
steps:
- name: Checkout Code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Cache vcpkg
id: cache-vcpkg
uses: actions/cache@v4
with:
path: C:\vcpkg
key: vcpkg-windows-${{ runner.os }}-${{ hashFiles('vcpkg.json', 'vcpkg-lock.json') }}
restore-keys: |
vcpkg-windows-${{ runner.os }}-
- name: Bootstrap vcpkg
shell: pwsh
run: |
Write-Host "Setting up vcpkg..."
if (-not (Test-Path "C:\vcpkg\vcpkg.exe")) {
Write-Host "Cloning vcpkg from GitHub..."
git clone https://github.com/microsoft/vcpkg.git C:\vcpkg
git -C C:\vcpkg checkout ffc071e0c08432c60c9b64f00334c0227667931b
Write-Host "Running vcpkg bootstrap..."
& C:\vcpkg\bootstrap-vcpkg.bat -disableMetrics
} else {
Write-Host "Using cached vcpkg"
}
"VCPKG_ROOT=C:\vcpkg" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
Write-Host "vcpkg ready at C:\vcpkg"
- name: Install Vulkan SDK
uses: jakoch/install-vulkan-sdk-action@v1
with:
vulkan_version: 1.3.283.0
install_runtime: true
cache: true
destination: ${{ github.workspace }}/vulkan-sdk
- name: Setup MSVC (x64)
uses: ilammy/msvc-dev-cmd@v1
with:
arch: x64
- name: Install Ninja
uses: seanmiddleditch/gha-setup-ninja@v5
- name: Configure CMake (Windows)
shell: pwsh
run: |
New-Item -ItemType Directory -Force -Path logs | Out-Null
Write-Host "Configuring with preset: ${{ inputs.preset }}"
cmake --preset ${{ inputs.preset }} 2>&1 | Tee-Object -FilePath logs/configure_windows.log
$status = $LASTEXITCODE
if ($status -ne 0) {
Write-Host "CMake configuration failed; last 50 lines:"
Get-Content logs/configure_windows.log | Select-Object -Last 50
exit $status
}
Write-Host "CMake configuration complete."
- name: Build ${{ inputs.game }}
shell: pwsh
run: |
New-Item -ItemType Directory -Force -Path logs | Out-Null
$jobs = [Environment]::ProcessorCount
Write-Host "Building with $jobs parallel jobs..."
if ("${{ inputs.game }}" -eq "GeneralsMD") {
cmake --build build/${{ inputs.preset }} --target z_generals -j $jobs 2>&1 |
Tee-Object -FilePath logs/build_zh_windows.log
$status = $LASTEXITCODE
$logFile = "logs/build_zh_windows.log"
} else {
cmake --build build/${{ inputs.preset }} --target g_generals -j $jobs 2>&1 |
Tee-Object -FilePath logs/build_generals_windows.log
$status = $LASTEXITCODE
$logFile = "logs/build_generals_windows.log"
}
if ($status -eq 0) {
Write-Host "Build completed successfully."
} else {
Write-Host "Build failed; last 100 lines of $logFile :"
Get-Content $logFile | Select-Object -Last 100
exit $status
}
- name: Verify Build Artifacts
shell: pwsh
run: |
if ("${{ inputs.game }}" -eq "GeneralsMD") {
$binary = "build/${{ inputs.preset }}/GeneralsMD/GeneralsXZH.exe"
$displayName = "GeneralsXZH.exe"
} else {
$binary = "build/${{ inputs.preset }}/Generals/GeneralsX.exe"
$displayName = "GeneralsX.exe"
}
if (Test-Path $binary) {
$sizeMB = [Math]::Round((Get-Item $binary).Length / 1MB, 2)
Write-Host "Binary built: $displayName ($sizeMB MB)"
} else {
Write-Host "Binary NOT found: $binary"
Write-Host "Listing build output directory:"
Get-ChildItem "build/${{ inputs.preset }}" -Recurse -Filter "*.exe" -ErrorAction SilentlyContinue |
Format-Table FullName, Length -AutoSize
exit 1
}
- name: Deploy Bundle (Binary + DLLs)
if: success()
shell: pwsh
run: |
if ("${{ inputs.game }}" -eq "GeneralsMD") {
$binary = "build/${{ inputs.preset }}/GeneralsMD/GeneralsXZH.exe"
} else {
$binary = "build/${{ inputs.preset }}/Generals/GeneralsX.exe"
}
$deployDir = "$env:RUNNER_TEMP\GeneralsX-${{ inputs.game }}-deploy"
New-Item -ItemType Directory -Force -Path $deployDir | Out-Null
Write-Host "Creating deployment bundle at: $deployDir"
# Executable
Copy-Item $binary $deployDir -Force
Write-Host " Copied executable"
# DXVK DLLs (Windows pre-built x64 release)
$dxvkDir = "build/${{ inputs.preset }}/_deps/dxvk_win_release-src/x64"
if (Test-Path $dxvkDir) {
foreach ($dll in @("d3d8.dll","d3d9.dll","d3d10core.dll","d3d11.dll","dxgi.dll")) {
$src = Join-Path $dxvkDir $dll
if (Test-Path $src) { Copy-Item $src $deployDir -Force; Write-Host " Copied $dll" }
}
} else {
Write-Host " DXVK directory not found (will be fetched on configure): $dxvkDir"
}
# SDL3.dll
$sdl3Dll = "build/${{ inputs.preset }}/_deps/sdl3-build/SDL3.dll"
if (Test-Path $sdl3Dll) {
Copy-Item $sdl3Dll $deployDir -Force
Write-Host " Copied SDL3.dll"
}
# GeneralsX @bugfix felipebraz 05/03/2026 Include optional SDL3_image runtime DLL in CI bundle when available.
$sdl3ImageDll = "build/${{ inputs.preset }}/_deps/SDL3_image-build/SDL3_image.dll"
if (Test-Path $sdl3ImageDll) {
Copy-Item $sdl3ImageDll $deployDir -Force
Write-Host " Copied SDL3_image.dll"
} else {
Write-Host " SDL3_image.dll not found (optional)"
}
# OpenAL32.dll
$openalDll = Get-ChildItem "build/${{ inputs.preset }}/_deps" `
-Filter "OpenAL32.dll" -Recurse -ErrorAction SilentlyContinue | Select-Object -First 1
if ($openalDll) {
Copy-Item $openalDll.FullName $deployDir -Force
Write-Host " Copied OpenAL32.dll"
}
# GeneralsX @bugfix felipebraz 05/03/2026 Include optional FFmpeg runtime DLLs to match local deploy behavior.
$targetDir = if ("${{ inputs.game }}" -eq "GeneralsMD") { "GeneralsMD" } else { "Generals" }
$buildGameDir = "build/${{ inputs.preset }}/$targetDir"
Get-ChildItem $buildGameDir -Filter "av*.dll" -ErrorAction SilentlyContinue | ForEach-Object {
Copy-Item $_.FullName $deployDir -Force
Write-Host " Copied $($_.Name)"
}
Get-ChildItem $buildGameDir -Filter "sw*.dll" -ErrorAction SilentlyContinue | ForEach-Object {
Copy-Item $_.FullName $deployDir -Force
Write-Host " Copied $($_.Name)"
}
# GeneralsX @bugfix felipebraz 05/03/2026 Copy optional dxvk.conf for consistent runtime defaults.
if (Test-Path "dxvk.conf") {
Copy-Item "dxvk.conf" $deployDir -Force
Write-Host " Copied dxvk.conf"
}
Write-Host "Bundle ready at: $deployDir"
Get-ChildItem $deployDir | Format-Table Name, Length -AutoSize
$archivePath = "$env:RUNNER_TEMP\bundle-windows-${{ inputs.game }}-${{ inputs.preset }}.zip"
Write-Host "Creating archive: $archivePath"
Compress-Archive -Path "$deployDir\*" -DestinationPath $archivePath -Force
$sizeMB = [Math]::Round((Get-Item $archivePath).Length / 1MB, 2)
Write-Host "Archive size: $sizeMB MB"
- name: Upload Build Logs
if: always()
uses: actions/upload-artifact@v4
with:
name: build-logs-windows-${{ inputs.game }}
path: logs/
retention-days: 7
- name: Upload Deployment Bundle
if: success()
uses: actions/upload-artifact@v4
with:
name: bundle-windows-${{ inputs.game }}-${{ inputs.preset }}.zip
path: ${{ runner.temp }}/bundle-windows-${{ inputs.game }}-${{ inputs.preset }}.zip
retention-days: 7
if-no-files-found: warn