Files
archived-Reclass/scripts/build.ps1
noita-player 7b9b140823 Fix MCP use-after-free, scanner chunk overlap, build scripts
- MCP bridge: guard against use-after-free when client disconnects
  during sendJson flush by re-checking m_client after write
- Scanner engine: fix chunk overlap advancing past region end on
  final chunk; fix fallback region flags for providers without
  enumerateRegions
- Build scripts: prefer GCC MinGW over LLVM-MinGW in PATH detection
2026-03-08 22:44:36 -07:00

513 lines
18 KiB
PowerShell

# PowerShell script to build Reclass
# Automatically detects Qt installation and configures build environment
#Requires -Version 5.1
[CmdletBinding()]
param(
[Parameter(Mandatory=$false)]
[string]$QtDir,
[Parameter(Mandatory=$false)]
[ValidateSet('Release', 'Debug', 'RelWithDebInfo', 'MinSizeRel')]
[string]$BuildType = 'Release',
[Parameter(Mandatory=$false)]
[switch]$Clean,
[Parameter(Mandatory=$false)]
[switch]$Rebuild
)
$ErrorActionPreference = "Stop"
# ──────────────────────────────────────────────────────────────────────────────
# Functions
# ──────────────────────────────────────────────────────────────────────────────
function Write-ColorOutput {
param([string]$Message, [ConsoleColor]$Color = [ConsoleColor]::White)
Write-Host $Message -ForegroundColor $Color
}
function Find-QtInstallation {
$found = @()
# Query registry for Qt installations
$registryPaths = @(
"HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall",
"HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall"
)
foreach ($regPath in $registryPaths) {
if (Test-Path $regPath) {
Get-ChildItem -Path $regPath -ErrorAction SilentlyContinue | ForEach-Object {
$displayName = (Get-ItemProperty -Path $_.PSPath -Name DisplayName -ErrorAction SilentlyContinue).DisplayName
if ($displayName -and $displayName -match 'Qt') {
$installLocation = (Get-ItemProperty -Path $_.PSPath -Name InstallLocation -ErrorAction SilentlyContinue).InstallLocation
if ($installLocation -and (Test-Path $installLocation)) {
# Check if this is a Qt root directory, scan for MinGW installations
Get-ChildItem -Path $installLocation -Directory -ErrorAction SilentlyContinue | Where-Object {
$_.Name -match '^\d+\.\d+\.\d+$'
} | ForEach-Object {
Get-ChildItem -Path $_.FullName -Directory -ErrorAction SilentlyContinue | Where-Object {
$_.Name -match 'mingw'
} | ForEach-Object {
$qmakePath = Join-Path $_.FullName "bin\qmake.exe"
if (Test-Path $qmakePath) {
$found += $_.FullName
}
}
}
}
}
}
}
}
# Fallback: scan common paths if registry search found nothing
if ($found.Count -eq 0) {
$commonPaths = @(
"C:\Qt",
"D:\Qt",
"E:\Qt",
"F:\Qt",
"$env:USERPROFILE\Qt",
"$env:ProgramFiles\Qt",
"${env:ProgramFiles(x86)}\Qt"
)
foreach ($basePath in $commonPaths) {
if (Test-Path $basePath) {
Get-ChildItem -Path $basePath -Directory -ErrorAction SilentlyContinue | Where-Object {
$_.Name -match '^\d+\.\d+\.\d+$'
} | ForEach-Object {
Get-ChildItem -Path $_.FullName -Directory -ErrorAction SilentlyContinue | Where-Object {
$_.Name -match 'mingw'
} | ForEach-Object {
$qmakePath = Join-Path $_.FullName "bin\qmake.exe"
if (Test-Path $qmakePath) {
$found += $_.FullName
}
}
}
}
}
}
return $found | Select-Object -Unique
}
function Select-QtDirectory {
Add-Type -AssemblyName System.Windows.Forms
$folderBrowser = New-Object System.Windows.Forms.FolderBrowserDialog
$folderBrowser.Description = "Select Qt MinGW installation directory (e.g., C:\Qt\6.10.2\mingw_64)"
$folderBrowser.RootFolder = [System.Environment+SpecialFolder]::MyComputer
$folderBrowser.ShowNewFolderButton = $false
if ($folderBrowser.ShowDialog() -eq [System.Windows.Forms.DialogResult]::OK) {
return $folderBrowser.SelectedPath
}
return $null
}
function Get-QtDirectory {
# 1. Check if provided as parameter
if ($QtDir) {
Write-ColorOutput "Using Qt directory from parameter: $QtDir" Cyan
return $QtDir
}
# 2. Check environment variable and try to find MinGW under it
if ($env:QTDIR) {
$qtdirPath = $env:QTDIR
Write-ColorOutput "Found QTDIR environment variable: $qtdirPath" Cyan
# Check if QTDIR directly points to a valid MinGW installation
$qmake = Join-Path $qtdirPath "bin\qmake.exe"
if ((Test-Path $qmake) -and ($qtdirPath.ToLower() -match 'mingw')) {
Write-ColorOutput "QTDIR points to valid MinGW installation." Green
return $qtdirPath
}
# QTDIR might point to Qt root, search for MinGW subdirectories
Write-ColorOutput "QTDIR appears to be Qt root directory. Searching for MinGW installations..." Yellow
$foundMinGW = @()
Get-ChildItem -Path $qtdirPath -Directory -ErrorAction SilentlyContinue | Where-Object {
$_.Name -match '^\d+\.\d+\.\d+$'
} | ForEach-Object {
Get-ChildItem -Path $_.FullName -Directory -ErrorAction SilentlyContinue | Where-Object {
$_.Name -match 'mingw'
} | ForEach-Object {
$qmakePath = Join-Path $_.FullName "bin\qmake.exe"
if (Test-Path $qmakePath) {
$foundMinGW += $_.FullName
}
}
}
if ($foundMinGW.Count -eq 1) {
Write-ColorOutput "Found MinGW installation under QTDIR: $($foundMinGW[0])" Green
$response = Read-Host "Use this installation? (Y/n)"
if ($response -match '^(y|yes|)$' -or [string]::IsNullOrWhiteSpace($response)) {
return $foundMinGW[0]
}
} elseif ($foundMinGW.Count -gt 1) {
Write-ColorOutput "Found multiple MinGW installations under QTDIR:" Green
for ($i = 0; $i -lt $foundMinGW.Count; $i++) {
Write-Host " [$($i+1)] $($foundMinGW[$i])"
}
do {
$choice = Read-Host "Select Qt installation (1-$($foundMinGW.Count))"
$choiceNum = 0
$valid = [int]::TryParse($choice, [ref]$choiceNum) -and $choiceNum -ge 1 -and $choiceNum -le $foundMinGW.Count
} until ($valid)
return $foundMinGW[$choiceNum - 1]
}
Write-ColorOutput "No MinGW installations found under QTDIR." Yellow
}
# 3. Try to auto-detect
Write-ColorOutput "Searching for Qt installations..." Yellow
$detected = Find-QtInstallation
if ($detected.Count -eq 1) {
Write-ColorOutput "Found Qt installation: $($detected[0])" Green
$response = Read-Host "Use this installation? (Y/n)"
if ($response -match '^(y|yes|)$' -or [string]::IsNullOrWhiteSpace($response)) {
return $detected[0]
}
} elseif ($detected.Count -gt 1) {
Write-ColorOutput "Found multiple Qt installations:" Green
for ($i = 0; $i -lt $detected.Count; $i++) {
Write-Host " [$($i+1)] $($detected[$i])"
}
Write-Host " [0] Browse for directory manually"
do {
$choice = Read-Host "Select Qt installation (1-$($detected.Count), or 0 to browse)"
$choiceNum = 0
$valid = [int]::TryParse($choice, [ref]$choiceNum) -and $choiceNum -ge 0 -and $choiceNum -le $detected.Count
} until ($valid)
if ($choiceNum -gt 0) {
return $detected[$choiceNum - 1]
}
}
# 4. Manual input or browse
Write-ColorOutput "No Qt MinGW installation auto-detected." Yellow
Write-Host ""
Write-Host "Options:"
Write-Host " [1] Browse for Qt MinGW directory"
Write-Host " [2] Enter path manually"
Write-Host " [3] Exit"
do {
$choice = Read-Host "Select option (1-3)"
} until ($choice -match '^[123]$')
switch ($choice) {
"1" {
$dir = Select-QtDirectory
if ($dir) { return $dir }
Write-ColorOutput "No directory selected. Exiting." Red
exit 1
}
"2" {
$dir = Read-Host "Enter Qt MinGW directory path (e.g., C:\Qt\6.10.2\mingw_64)"
if ($dir) { return $dir }
Write-ColorOutput "No path entered. Exiting." Red
exit 1
}
"3" {
Write-ColorOutput "Exiting." Yellow
exit 0
}
}
}
function Validate-QtDirectory {
param([string]$Dir)
$qmake = Join-Path $Dir "bin\qmake.exe"
if (-not (Test-Path $qmake)) {
Write-ColorOutput "ERROR: qmake.exe not found in $Dir\bin\" Red
Write-ColorOutput "Please ensure you selected a valid Qt installation directory (e.g., C:\Qt\6.10.2\mingw_64)" Red
return $false
}
# Validate it's a MinGW build
$dirLower = $Dir.ToLower()
if ($dirLower -notmatch 'mingw') {
Write-ColorOutput "ERROR: Selected Qt installation is not MinGW-based." Red
Write-ColorOutput "This project requires Qt with MinGW compiler (e.g., C:\Qt\6.10.2\mingw_64)" Red
Write-ColorOutput "Selected path: $Dir" Red
return $false
}
return $true
}
function Find-BuildTool {
param([string]$QtDir, [string]$ToolName)
$qtRoot = Split-Path (Split-Path $QtDir -Parent) -Parent
$toolsDir = Join-Path $qtRoot "Tools"
if (Test-Path $toolsDir) {
# Search for tool in Qt Tools directory
$toolExe = Get-ChildItem -Path $toolsDir -Recurse -Filter "$ToolName.exe" -ErrorAction SilentlyContinue | Select-Object -First 1
if ($toolExe) {
return $toolExe.DirectoryName
}
}
# Check system PATH
$cmd = Get-Command "$ToolName.exe" -ErrorAction SilentlyContinue
if ($cmd) {
return Split-Path $cmd.Source -Parent
}
return $null
}
function Find-MinGWDirectory {
param([string]$QtDir)
$qtRoot = Split-Path (Split-Path $QtDir -Parent) -Parent
$toolsDir = Join-Path $qtRoot "Tools"
if (Test-Path $toolsDir) {
# Prefer GCC-based MinGW (has g++.exe); exclude llvm-mingw. Prefer 64-bit, then newest.
$mingwToolDirs = Get-ChildItem -Path $toolsDir -Directory -ErrorAction SilentlyContinue | Where-Object {
$_.Name -match '^mingw\d+_\d+$'
} | Sort-Object -Property @{ Expression = { if ($_.Name -match '_64$') { 1 } else { 0 } }; Descending = $true }, Name -Descending
foreach ($dir in $mingwToolDirs) {
$testBin = Join-Path $dir.FullName "bin\g++.exe"
if (Test-Path $testBin) {
return Join-Path $dir.FullName "bin"
}
}
}
return $null
}
# ──────────────────────────────────────────────────────────────────────────────
# Main Script
# ──────────────────────────────────────────────────────────────────────────────
Write-ColorOutput "`n========================================" Cyan
Write-ColorOutput "Reclass Build Script" Cyan
Write-ColorOutput "========================================`n" Cyan
# Get script directory and project root
$scriptDir = Split-Path -Parent $PSCommandPath
$projectRoot = Split-Path -Parent $scriptDir
$buildDir = Join-Path $projectRoot "build"
# Get Qt directory
$selectedQtDir = Get-QtDirectory
if (-not $selectedQtDir) {
Write-ColorOutput "ERROR: No Qt directory provided." Red
exit 1
}
# Validate Qt directory
if (-not (Validate-QtDirectory $selectedQtDir)) {
exit 1
}
Write-ColorOutput "`nQt Directory: $selectedQtDir" Green
Write-ColorOutput "Build Type: $BuildType" Green
# Find build tools
Write-ColorOutput "`nSearching for build tools..." Cyan
$cmakeDir = Find-BuildTool $selectedQtDir "cmake"
$ninjaDir = Find-BuildTool $selectedQtDir "ninja"
$mingwDir = Find-MinGWDirectory $selectedQtDir
$missingTools = @()
if (-not $cmakeDir) {
$missingTools += "CMake"
Write-ColorOutput " CMake: NOT FOUND" Red
} else {
Write-ColorOutput " CMake: $cmakeDir" Green
}
if (-not $ninjaDir) {
$missingTools += "Ninja"
Write-ColorOutput " Ninja: NOT FOUND" Yellow
Write-ColorOutput " (Will try default CMake generator)" Gray
} else {
Write-ColorOutput " Ninja: $ninjaDir" Green
}
if (-not $mingwDir) {
Write-ColorOutput " MinGW: NOT FOUND (may cause build issues)" Yellow
} else {
Write-ColorOutput " MinGW: $mingwDir" Green
}
if ($missingTools.Count -gt 0 -and $missingTools -contains "CMake") {
Write-ColorOutput "`nERROR: CMake is required but not found." Red
Write-ColorOutput "Please install CMake or ensure it's in your PATH." Red
Write-ColorOutput "You can download it from: https://cmake.org/download/" Red
exit 1
}
# Build PATH environment
$pathDirs = @()
if ($cmakeDir) { $pathDirs += $cmakeDir }
if ($ninjaDir) { $pathDirs += $ninjaDir }
if ($mingwDir) { $pathDirs += $mingwDir }
$pathDirs += (Join-Path $selectedQtDir "bin")
$pathDirs += $env:Path
$env:Path = ($pathDirs -join ";")
Write-ColorOutput "`nEnvironment configured." Green
# Handle clean/rebuild
if ($Rebuild) {
$Clean = $true
}
if ($Clean -and (Test-Path $buildDir)) {
Write-ColorOutput "`nCleaning build directory..." Yellow
try {
Remove-Item -Path $buildDir -Recurse -Force -ErrorAction Stop
Write-ColorOutput "Build directory cleaned." Green
} catch {
Write-ColorOutput "ERROR: Failed to clean build directory: $_" Red
exit 1
}
}
# Create build directory
if (-not (Test-Path $buildDir)) {
Write-ColorOutput "`nCreating build directory..." Cyan
New-Item -ItemType Directory -Path $buildDir -Force | Out-Null
}
# Change to build directory
Push-Location $buildDir
try {
Write-ColorOutput "`nConfiguring CMake..." Cyan
# Prepare CMake command
$cmakeArgs = @(
".."
"-DCMAKE_BUILD_TYPE=$BuildType"
"-DCMAKE_PREFIX_PATH=$($selectedQtDir -replace '\\', '/')"
)
# Add generator if Ninja is available
if ($ninjaDir) {
$cmakeArgs = @("-G", "Ninja") + $cmakeArgs
Write-ColorOutput "Using Ninja generator" Gray
}
Write-ColorOutput "Command: cmake $($cmakeArgs -join ' ')`n" Gray
& cmake $cmakeArgs
if ($LASTEXITCODE -ne 0) {
Write-ColorOutput "`nERROR: CMake configuration failed with exit code $LASTEXITCODE" Red
exit 1
}
Write-ColorOutput "`nCMake configuration completed successfully.`n" Green
# Build
Write-ColorOutput "Building Reclass..." Cyan
$cores = (Get-CimInstance -ClassName Win32_Processor).NumberOfLogicalProcessors
if (-not $cores -or $cores -lt 1) {
$cores = 4
}
Write-ColorOutput "Command: cmake --build . --config $BuildType -j$cores`n" Gray
& cmake --build . --config $BuildType "-j$cores"
if ($LASTEXITCODE -ne 0) {
Write-ColorOutput "`nERROR: Build failed with exit code $LASTEXITCODE" Red
exit 1
}
Write-ColorOutput "`nBuild completed successfully!`n" Green
# Find executable
Write-ColorOutput "Locating executable..." Cyan
$exePaths = @(
(Join-Path $buildDir "Reclass.exe"),
(Join-Path $buildDir "$BuildType\Reclass.exe")
)
$exePath = $null
foreach ($path in $exePaths) {
if (Test-Path $path) {
$exePath = $path
break
}
}
if ($exePath) {
$fileInfo = Get-Item $exePath
$sizeMB = [math]::Round($fileInfo.Length / 1MB, 2)
Write-ColorOutput "Executable: $exePath" Green
Write-ColorOutput "Size: $sizeMB MB" Gray
# Run windeployqt to copy Qt dependencies
Write-ColorOutput "`nRunning windeployqt..." Cyan
$windeployqt = Join-Path $selectedQtDir "bin\windeployqt.exe"
if (Test-Path $windeployqt) {
$exeDir = Split-Path $exePath -Parent
Write-ColorOutput "Command: `"$windeployqt`" `"$exePath`"`n" Gray
& $windeployqt $exePath
if ($LASTEXITCODE -eq 0) {
Write-ColorOutput "windeployqt completed successfully." Green
# Count deployed files
$deployedFiles = Get-ChildItem -Path $exeDir -Recurse -File | Where-Object {
$_.Name -ne "Reclass.exe" -and $_.Extension -match '\.(dll|qm)$'
}
if ($deployedFiles) {
Write-ColorOutput "Deployed $($deployedFiles.Count) Qt dependency files." Gray
}
} else {
Write-ColorOutput "WARNING: windeployqt failed with exit code $LASTEXITCODE" Yellow
Write-ColorOutput "Application may not run without Qt DLLs in PATH" Yellow
}
} else {
Write-ColorOutput "WARNING: windeployqt.exe not found at $windeployqt" Yellow
Write-ColorOutput "Application may not run without Qt DLLs in PATH" Yellow
}
} else {
Write-ColorOutput "WARNING: Could not locate Reclass.exe" Yellow
}
} catch {
Write-ColorOutput "`nERROR: Build failed with exception: $_" Red
exit 1
} finally {
Pop-Location
}
Write-ColorOutput "`n========================================" Cyan
Write-ColorOutput "Build completed successfully!" Cyan
Write-ColorOutput "========================================`n" Cyan
if ($exePath) {
Write-ColorOutput "Run the application with:" White
Write-ColorOutput " .\build\Reclass.exe`n" Cyan
}