From 52d5551e4f2e7fd04200c339b46f076599079fcd Mon Sep 17 00:00:00 2001 From: "Radjammin@gmail.com" Date: Wed, 6 May 2026 22:06:42 -0400 Subject: [PATCH 01/11] Improvements for the deploy windows script. --- windows/deploy_windows.ps1 | 495 ++++++++++++++++--------------------- 1 file changed, 214 insertions(+), 281 deletions(-) diff --git a/windows/deploy_windows.ps1 b/windows/deploy_windows.ps1 index 487e02f9fb..5605014986 100644 --- a/windows/deploy_windows.ps1 +++ b/windows/deploy_windows.ps1 @@ -1,9 +1,7 @@ param ( # Replace default path with system Qt installation folder if necessary - [string] $QtInstallPath32 = "C:\Qt\5.15.2", - [string] $QtInstallPath64 = "C:\Qt\6.8.1", - [string] $QtCompile32 = "msvc2019", - [string] $QtCompile64 = "msvc2022_64", + [string]$QtInstallPath32="C:\Qt\5.15.2", [string]$QtInstallPath64="C:\Qt\6.8.1", + [string]$QtCompile32="msvc2019", [string]$QtCompile64="msvc2022_64", # Important: # - Do not update ASIO SDK without checking for license-related changes. # - Do not copy (parts of) the ASIO SDK into the Jamulus source tree without @@ -11,9 +9,11 @@ param ( # # The following version pinnings are semi-automatically checked for # updates. Verify .github/workflows/bump-dependencies.yaml when changing those manually: - [string] $AsioSDKUrl = "https://download.steinberg.net/sdk_downloads/ASIO-SDK_2.3.4_2025-10-15.zip", - [string] $NsisUrl = "https://downloads.sourceforge.net/project/nsis/NSIS%203/3.12/nsis-3.12.zip", - [string] $BuildOption = "" + [string]$AsioSDKUrl="https://download.steinberg.net/sdk_downloads/ASIO-SDK_2.3.4_2025-10-15.zip", + [string]$NsisUrl="https://downloads.sourceforge.net/project/nsis/NSIS%203/3.12/nsis-3.12.zip", + [string]$BuildOption="", + # Toggles for debugging and targeted builds + [switch]$DebugMode, [switch]$Skip64Bit, [switch]$Skip32Bit ) # Fail early on all errors @@ -33,340 +33,273 @@ $DeployPath = "$RootPath\deploy" $WindowsPath ="$RootPath\windows" $AppName = "Jamulus" +# --- Centralized Logging Function --- +Function Write-Log { + param([Parameter(Mandatory=$true)][AllowNull()][AllowEmptyString()][string]$Message, [ValidateSet("INFO","WARN","ERROR","STEP","DEBUG")][string]$Level="INFO") + if ($null -eq $Message) { $Message = "" } + $Stamp = (Get-Date).ToString("yyyy-MM-dd HH:mm:ss"); $Log = "[$Stamp] [$Level] $Message" + switch ($Level) { + "INFO" { Write-Host $Log } + "STEP" { Write-Host "`n>>> [$Stamp] $Message" -ForegroundColor Cyan } + "WARN" { Write-Host $Log -ForegroundColor Yellow } + "ERROR" { Write-Host $Log -ForegroundColor Red } + "DEBUG" { if ($DebugMode) { Write-Host $Log -ForegroundColor DarkGray } } + } +} + # Execute native command with errorlevel handling Function Invoke-Native-Command { - param( - [string] $Command, - [string[]] $Arguments - ) - - & "$Command" @Arguments + param([string]$Command, [string[]]$Arguments) + Write-Log "Executing: $Command $($Arguments -join ' ')" -Level DEBUG + $Output = [System.Collections.Generic.List[string]]::new() + + & "$Command" @Arguments 2>&1 | ForEach-Object { + $Line = $_.ToString(); $Output.Add($Line) + if ($DebugMode) { Write-Host " $Line" -ForegroundColor DarkGray } + elseif ($Line.Length -lt 120 -and $Line -notmatch "warning C\d+" -and -not [string]::IsNullOrWhiteSpace($Line)) { + Write-Host " $Line" -ForegroundColor DarkGray + } + } - if ($LastExitCode -Ne 0) - { - Throw "Native command $Command returned with exit code $LastExitCode" + if ($LastExitCode -Ne 0) { + $ErrMsg = "Native command $Command returned exit code $LastExitCode" + Write-Log $ErrMsg -Level ERROR + if (-not $DebugMode) { + Write-Log "--- Command Output Log ---" -Level ERROR + $Output | ForEach-Object { Write-Log $_ -Level ERROR } + } + Throw $ErrMsg } } # Cleanup existing build folders -Function Clean-Build-Environment -{ - if (Test-Path -Path $BuildPath) { Remove-Item -Path $BuildPath -Recurse -Force } - if (Test-Path -Path $DeployPath) { Remove-Item -Path $DeployPath -Recurse -Force } - - New-Item -Path $BuildPath -ItemType Directory - New-Item -Path $DeployPath -ItemType Directory +Function Clean-Build-Environment { + Write-Log "Cleaning Build Environment..." -Level STEP + if (Test-Path $BuildPath) { Remove-Item $BuildPath -Recurse -Force } + if (Test-Path $DeployPath) { Remove-Item $DeployPath -Recurse -Force } + New-Item $BuildPath, $DeployPath -ItemType Directory | Out-Null + Write-Log "Build and Deploy directories initialized." } -# For sourceforge links we need to get the correct mirror (especially NISIS) Thanks: https://www.powershellmagazine.com/2013/01/29/pstip-retrieve-a-redirected-url/ +# For sourceforge links we need to get the correct mirror (especially NISIS) Function Get-RedirectedUrl { - param( - [Parameter(Mandatory=$true)] - [string] $url - ) - - $numAttempts = 10 - $sleepTime = 10 - $maxSleepTime = 80 - for ($attempt = 1; $attempt -le $numAttempts; $attempt++) { + param([Parameter(Mandatory=$true)][string]$url) + $sleep = 10; $maxSleep = 80 + for ($i = 1; $i -le 10; $i++) { try { - $request = [System.Net.WebRequest]::Create($url) - $request.AllowAutoRedirect=$true - $response=$request.GetResponse() - $response.ResponseUri.AbsoluteUri - $response.Close() - return + $req = [System.Net.WebRequest]::Create($url); $req.AllowAutoRedirect = $true + $res = $req.GetResponse(); $redirect = $res.ResponseUri.AbsoluteUri; $res.Close() + return $redirect } catch { - if ($attempt -lt $numAttempts) { - Write-Warning "Caught error: $_" - Write-Warning "Get-RedirectedUrl: Fetch attempt #${attempt}/${numAttempts} for $url failed, trying again in ${sleepTime}s" - Start-Sleep -Seconds $sleepTime - $sleepTime = [Math]::Min($sleepTime * 2, $maxSleepTime) + if ($i -lt 10) { + Write-Log "Fetch attempt $i/10 for $url failed, retrying in ${sleep}s" -Level WARN + Start-Sleep -Seconds $sleep; $sleep = [Math]::Min($sleep * 2, $maxSleep) continue } - Write-Error "Get-RedirectedUrl: All ${numAttempts} fetch attempts for $url failed, failing whole call" - throw - } - } -} - -function Initialize-Module-Here ($m) { # see https://stackoverflow.com/a/51692402 - - # If module is imported say that and do nothing - if (Get-Module | Where-Object {$_.Name -eq $m}) { - Write-Output "Module $m is already imported." - } - else { - - # If module is not imported, but available on disk then import - if (Get-Module -ListAvailable | Where-Object {$_.Name -eq $m}) { - Import-Module $m - } - else { - - # If module is not imported, not available on disk, but is in online gallery then install and import - if (Find-Module -Name $m | Where-Object {$_.Name -eq $m}) { - Install-Module -Name $m -Force -Verbose -Scope CurrentUser - Import-Module $m - } - else { - - # If module is not imported, not available and not in online gallery then abort - Write-Output "Module $m not imported, not available and not in online gallery, exiting." - EXIT 1 - } + Write-Log "All 10 fetch attempts for $url failed" -Level ERROR; throw } } } # Download and uncompress dependency in ZIP format -Function Install-Dependency -{ - param( - [Parameter(Mandatory=$true)] - [string] $Uri, - [Parameter(Mandatory=$true)] - [string] $Destination - ) - - if (Test-Path -Path "$WindowsPath\$Destination") - { - echo "Using ${WindowsPath}\${Destination} from previous run (e.g. actions/cache)" - return - } +Function Install-Dependency { + param([Parameter(Mandatory=$true)][string]$Uri, [Parameter(Mandatory=$true)][string]$Destination) + if (Test-Path "$WindowsPath\$Destination") { Write-Log "Using cached ${WindowsPath}\${Destination}"; return } + $TempFile = [System.IO.Path]::GetTempFileName() + ".zip" - $TempFileName = [System.IO.Path]::GetTempFileName() + ".zip" - $TempPath = [System.IO.Path]::GetTempPath() - $TempGuid = [System.Guid]::NewGuid() # Create a unique empty directory to unpack into - $TempDir = (Join-Path $TempPath $TempGuid) - New-Item -ItemType Directory -Path $TempDir + $TempDir = Join-Path ([System.IO.Path]::GetTempPath()) ([System.Guid]::NewGuid().ToString()) + New-Item -ItemType Directory -Path $TempDir | Out-Null - if ($Uri -Match "downloads.sourceforge.net") - { - $Uri = Get-RedirectedUrl -URL $Uri - } + if ($Uri -Match "downloads.sourceforge.net") { $Uri = Get-RedirectedUrl $Uri } + Write-Log "Downloading $Uri..." + Invoke-WebRequest -Uri $Uri -OutFile $TempFile + Write-Log "Extracting to $WindowsPath\$Destination..." + Expand-Archive -Path $TempFile -DestinationPath $TempDir -Force - Invoke-WebRequest -Uri $Uri -OutFile $TempFileName - echo $TempFileName - Expand-Archive -Path $TempFileName -DestinationPath $TempDir -Force - echo $WindowsPath\$Destination - # Because we unpacked into a new directory, we can use * for the directory in the archive, - # so that we do not need to know the directory name the archive was packed from. - Move-Item -Path "$TempDir\*" -Destination "$WindowsPath\$Destination" -Force - Remove-Item -Path $TempDir -Recurse -Force - Remove-Item -Path $TempFileName -Force + Move-Item "$TempDir\*" "$WindowsPath\$Destination" -Force + Remove-Item $TempDir, $TempFile -Recurse -Force } -# Install VSSetup (Visual Studio detection), ASIO SDK and NSIS Installer -Function Install-Dependencies -{ - if (-not (Get-PackageProvider -Name nuget).Name -eq "nuget") { - Install-PackageProvider -Name "Nuget" -Scope CurrentUser -Force - } - Initialize-Module-Here -m "VSSetup" - Install-Dependency -Uri $NsisUrl -Destination "..\libs\NSIS\NSIS-source" +# Install ASIO SDK and NSIS Installer +Function Install-Dependencies { + Write-Log "Installing Dependencies..." -Level STEP + Install-Dependency $NsisUrl "..\libs\NSIS\NSIS-source" if ($BuildOption -Notmatch "jack") { - # Don't download ASIO SDK on Jamulus JACK builds to save - # resources and to be extra-sure license-wise. - Install-Dependency -Uri $AsioSDKUrl -Destination "..\libs\ASIOSDK2" - } + # Don't download ASIO SDK on Jamulus JACK builds to save resources. + Install-Dependency $AsioSDKUrl "..\libs\ASIOSDK2" + } else { Write-Log "Skipping ASIO SDK (JACK build detected)." } } # Setup environment variables and build tool paths -Function Initialize-Build-Environment -{ - param( - [Parameter(Mandatory=$true)] - [string] $BuildArch - ) - - # Look for Visual Studio/Build Tools 2017 or later (version 15.0 or above) - $VsInstallPath = Get-VSSetupInstance | ` - Select-VSSetupInstance -Product "*" -Version "15.0" -Latest | ` - Select-Object -ExpandProperty "InstallationPath" - - if ($VsInstallPath -Eq "") { $VsInstallPath = "" } - - if ($BuildArch -Eq "x86_64") - { - $VcVarsBin = "$VsInstallPath\VC\Auxiliary\build\vcvars64.bat" - } - else - { - $VcVarsBin = "$VsInstallPath\VC\Auxiliary\build\vcvars32.bat" - } +Function Initialize-Build-Environment { + param([Parameter(Mandatory=$true)][string]$BuildArch) - "" - "**********************************************************************" - "Using Visual Studio/Build Tools environment settings located at" - $VcVarsBin - "**********************************************************************" - "" - - if (-Not (Test-Path -Path $VcVarsBin)) - { - Throw "Microsoft Visual Studio ($BuildArch variant) is not installed. " + ` - "Please install Visual Studio 2017 or above it before running this script." - } + # Use native vswhere.exe to find VS2017+ installations with C++ build tools + $vswhere = "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe" + if (-not (Test-Path $vswhere)) { Throw "vswhere.exe not found. Visual Studio 2017 or above is required." } + + $VsPath = & $vswhere -latest -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath + $VsVer = & $vswhere -latest -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationVersion + + if ([string]::IsNullOrWhiteSpace($VsPath)) { Throw "Could not locate a Visual Studio installation with C++ build tools." } + + # Dynamically determine the correct Platform Toolset based on VS version to avoid hardcoded MSBuild failures + if ($VsVer -match "^17\.") { Set-Item Env:VsPlatformToolset "v143" } + elseif ($VsVer -match "^16\.") { Set-Item Env:VsPlatformToolset "v142" } + else { Set-Item Env:VsPlatformToolset "v141" } + + $VcVars = if ($BuildArch -eq "x86_64") { "$VsPath\VC\Auxiliary\build\vcvars64.bat" } else { "$VsPath\VC\Auxiliary\build\vcvars32.bat" } + + Write-Log "Using VS environment: $VcVars (Toolset: $Env:VsPlatformToolset)" + if (-not (Test-Path $VcVars)) { Throw "MSVC environment script ($BuildArch) not found at $VcVars" } # Import environment variables set by vcvarsXX.bat into current scope $EnvDump = [System.IO.Path]::GetTempFileName() - Invoke-Native-Command -Command "cmd" ` - -Arguments ("/c", "`"$VcVarsBin`" && set > `"$EnvDump`"") - - foreach ($_ in Get-Content -Path $EnvDump) - { - if ($_ -Match "^([^=]+)=(.*)$") - { - Set-Item "Env:$($Matches[1])" $Matches[2] + Invoke-Native-Command "cmd" ("/c", "`"$VcVars`" && set > `"$EnvDump`"") + Get-Content $EnvDump | Where-Object { $_ -match "^([^=]+)=(.*)$" } | ForEach-Object { Set-Item "Env:$($Matches[1])" $Matches[2] } + Remove-Item $EnvDump -Force +} + +# Resolve Qt path by falling back to Registry lookups if the default path is missing +Function Resolve-Qt-Path { + param([Parameter(Mandatory=$true)][string]$DefaultPath) + if (Test-Path $DefaultPath) { return $DefaultPath } + Write-Log "Qt path '$DefaultPath' not found. Searching registry..." -Level DEBUG + $QtVer = Split-Path $DefaultPath -Leaf + $RegPaths = "HKCU:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*", "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*", "HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*" + foreach ($Reg in $RegPaths) { + $Installs = Get-ItemProperty $Reg -ErrorAction SilentlyContinue | Where-Object { $_.DisplayName -match "Qt" -and $_.InstallLocation } + foreach ($Inst in $Installs) { + $TryPath = Join-Path $Inst.InstallLocation $QtVer + if (Test-Path $TryPath) { Write-Log "Discovered Qt at: $TryPath"; return $TryPath } } } - - Remove-Item -Path $EnvDump -Force + Write-Log "Could not locate Qt $QtVer in registry." -Level WARN; return $DefaultPath } # Setup Qt environment variables and build tool paths -Function Initialize-Qt-Build-Environment -{ - param( - [Parameter(Mandatory=$true)] - [string] $QtInstallPath, - [Parameter(Mandatory=$true)] - [string] $QtCompile - ) - - $QtMsvcSpecPath = "$QtInstallPath\$QtCompile\bin" - +Function Initialize-Qt-Build-Environment { + param([Parameter(Mandatory=$true)][string]$QtInstallPath, [Parameter(Mandatory=$true)][string]$QtCompile) + $QtBin = "$QtInstallPath\$QtCompile\bin" # Setup Qt executables paths for later calls - Set-Item Env:QtQmakePath "$QtMsvcSpecPath\qmake.exe" - Set-Item Env:QtWinDeployPath "$QtMsvcSpecPath\windeployqt.exe" - - "**********************************************************************" - "Using Qt binaries for Visual C++ located at" - $QtMsvcSpecPath - "**********************************************************************" - "" - - if (-Not (Test-Path -Path $Env:QtQmakePath)) - { - Throw "The Qt binaries for Microsoft Visual C++ 2017 or above could not be located at $QtMsvcSpecPath. " + ` - "Please install Qt with support for MSVC 2017 or above before running this script," + ` - "then call this script with the Qt install location, for example C:\Qt\5.15.2" + Set-Item Env:QtQmakePath "$QtBin\qmake.exe"; Set-Item Env:QtWinDeployPath "$QtBin\windeployqt.exe" + + if (Get-Command "jom.exe" -ErrorAction SilentlyContinue) { Set-Item Env:QtJomPath "jom.exe" } + else { + $QtRoot = Split-Path $QtInstallPath -Parent + $JomExes = "$QtRoot\Tools\QtCreator\bin\jom\jom.exe", "$QtRoot\Tools\jom\jom.exe", "C:\Qt\Tools\QtCreator\bin\jom\jom.exe", "D:\Qt\Tools\QtCreator\bin\jom\jom.exe" + $FoundJom = $JomExes | Where-Object { Test-Path $_ } | Select-Object -First 1 + if ($FoundJom) { Write-Log "Discovered jom: $FoundJom"; Set-Item Env:QtJomPath $FoundJom } else { Set-Item Env:QtJomPath "" } } + Write-Log "Using Qt binaries: $QtBin" + if (-not (Test-Path $Env:QtQmakePath)) { Throw "Qt MSVC binaries not found at $QtBin" } } # Build Jamulus x86_64 and x86 -Function Build-App -{ - param( - [Parameter(Mandatory=$true)] - [string] $BuildConfig, - [Parameter(Mandatory=$true)] - [string] $BuildArch - ) - - Invoke-Native-Command -Command "$Env:QtQmakePath" ` - -Arguments ("$RootPath\$AppName.pro", "CONFIG+=$BuildConfig $BuildArch $BuildOption", ` - "-o", "$BuildPath\Makefile") - - Set-Location -Path $BuildPath - if (Get-Command "jom.exe" -ErrorAction SilentlyContinue) - { - echo "Building with jom /J ${Env:NUMBER_OF_PROCESSORS}" - Invoke-Native-Command -Command "jom" -Arguments ("/J", "${Env:NUMBER_OF_PROCESSORS}", "$BuildConfig") +Function Build-App { + param([Parameter(Mandatory=$true)][string]$Cfg, [Parameter(Mandatory=$true)][string]$Arch) + Write-Log "Building App ($Arch) config: $Cfg" + + $QmkCfg = "CONFIG+=$Cfg $Arch $BuildOption" + if (-not $DebugMode) { $QmkCfg += " silent" } + + Invoke-Native-Command $Env:QtQmakePath ("$RootPath\$AppName.pro", $QmkCfg, "-o", "$BuildPath\Makefile") + + Set-Location $BuildPath + $MkArgs = @($Cfg) + if (-not $DebugMode) { $MkArgs += "/S" } + + if ($Env:QtJomPath) { + $Cores = [Math]::Max(1, [Math]::Floor([int]$Env:NUMBER_OF_PROCESSORS / 2)) + Write-Log "Building with jom /J $Cores (half of $Env:NUMBER_OF_PROCESSORS cores)" + Invoke-Native-Command $Env:QtJomPath (@("/J", "$Cores") + $MkArgs) + } else { + Write-Log "Building with nmake (sequential)" -Level WARN + Invoke-Native-Command "nmake" $MkArgs } - else - { - echo "Building with nmake (install Qt jom if you want parallel builds)" - Invoke-Native-Command -Command "nmake" -Arguments ("$BuildConfig") - } - Invoke-Native-Command -Command "$Env:QtWinDeployPath" ` - -Arguments ("--$BuildConfig", "--compiler-runtime", "--dir=$DeployPath\$BuildArch", - "$BuildPath\$BuildConfig\$AppName.exe") - Move-Item -Path "$BuildPath\$BuildConfig\$AppName.exe" -Destination "$DeployPath\$BuildArch" -Force - Invoke-Native-Command -Command "nmake" -Arguments ("clean") - Set-Location -Path $RootPath + Write-Log "Deploying Qt dependencies..." + Invoke-Native-Command $Env:QtWinDeployPath ("--$Cfg", "--compiler-runtime", "--dir=$DeployPath\$Arch", "$BuildPath\$Cfg\$AppName.exe") + Move-Item "$BuildPath\$Cfg\$AppName.exe" "$DeployPath\$Arch" -Force + Invoke-Native-Command "nmake" (@("clean") + $MkArgs) + Set-Location $RootPath } # Build and deploy Jamulus 64bit and 32bit variants -function Build-App-Variants -{ - foreach ($_ in ("x86_64", "x86")) - { - $OriginalEnv = Get-ChildItem Env: - if ($_ -eq "x86") - { - Initialize-Build-Environment -BuildArch $_ - Initialize-Qt-Build-Environment -QtInstallPath $QtInstallPath32 -QtCompile $QtCompile32 - } - else - { - Initialize-Build-Environment -BuildArch $_ - Initialize-Qt-Build-Environment -QtInstallPath $QtInstallPath64 -QtCompile $QtCompile64 +function Build-App-Variants { + Write-Log "Starting Application Builds" -Level STEP + + $ArchsToBuild = @() + if (-not $Skip64Bit) { $ArchsToBuild += "x86_64" } else { Write-Log "Skipping 64-bit build (-Skip64Bit used)." -Level INFO } + if (-not $Skip32Bit) { $ArchsToBuild += "x86" } else { Write-Log "Skipping 32-bit build (-Skip32Bit used)." -Level INFO } + + if ($ArchsToBuild.Count -eq 0) { + Write-Log "All application builds skipped." -Level WARN + return + } + + foreach ($Arch in $ArchsToBuild) { + Write-Log "Configuring environment for $Arch..." + $OrigEnv = Get-ChildItem Env: + + try { + $Path = if ($Arch -eq "x86") { $QtInstallPath32 } else { $QtInstallPath64 } + $Comp = if ($Arch -eq "x86") { $QtCompile32 } else { $QtCompile64 } + Initialize-Build-Environment $Arch + Initialize-Qt-Build-Environment (Resolve-Qt-Path $Path) $Comp + Build-App "release" $Arch + } catch { + Write-Log "Build failed for ${Arch}: $_" -Level WARN + } finally { + # Ensure the environment is reset before the next loop, even if this arch fails + $OrigEnv | ForEach-Object { Set-Item "Env:$($_.Name)" $_.Value } } - Build-App -BuildConfig "release" -BuildArch $_ - $OriginalEnv | % { Set-Item "Env:$($_.Name)" $_.Value } } } # Build Windows installer -Function Build-Installer -{ - param( - [string] $BuildOption - ) - - foreach ($_ in Get-Content -Path "$RootPath\$AppName.pro") - { - if ($_ -Match "^VERSION *= *(.*)$") - { - $AppVersion = $Matches[1] - break - } - } +Function Build-Installer { + param([string]$BuildOption) + Write-Log "Building Windows Installer" -Level STEP - if ($BuildOption -ne "") - { - Invoke-Native-Command -Command "$RootPath\libs\NSIS\NSIS-source\makensis" ` - -Arguments ("/v4", "/DAPP_NAME=$AppName", "/DAPP_VERSION=$AppVersion", ` - "/DROOT_PATH=$RootPath", "/DWINDOWS_PATH=$WindowsPath", "/DDEPLOY_PATH=$DeployPath", ` - "/DBUILD_OPTION=$BuildOption", ` - "$WindowsPath\installer.nsi") - } - else - { - Invoke-Native-Command -Command "$RootPath\libs\NSIS\NSIS-source\makensis" ` - -Arguments ("/v4", "/DAPP_NAME=$AppName", "/DAPP_VERSION=$AppVersion", ` - "/DROOT_PATH=$RootPath", "/DWINDOWS_PATH=$WindowsPath", "/DDEPLOY_PATH=$DeployPath", ` - "$WindowsPath\installer.nsi") + # --- SAFETY GATE: Do not build if no executables were generated --- + $Has64 = Test-Path "$DeployPath\x86_64\$AppName.exe" + $Has32 = Test-Path "$DeployPath\x86\$AppName.exe" + + if (-not $Has64 -and -not $Has32) { + Write-Log "No built binaries found in $DeployPath. Skipping installer to avoid creating an empty package." -Level ERROR + Throw "Installer build aborted: No application binaries found." } -} -# Build and copy NS-Process dll -Function Build-NSProcess -{ - if (!(Test-Path -path "$RootPath\libs\NSIS\nsProcess.dll")) { + $Ver = (Get-Content "$RootPath\$AppName.pro" | Where-Object { $_ -match "^VERSION *= *(.*)$" } | ForEach-Object { $Matches[1] } | Select-Object -First 1) - echo "Building nsProcess..." + $NsisVerb = if ($DebugMode) { "/v4" } else { "/v2" } + $NsisArgs = @($NsisVerb, "/DAPP_NAME=$AppName", "/DAPP_VERSION=$Ver", "/DROOT_PATH=$RootPath", "/DWINDOWS_PATH=$WindowsPath", "/DDEPLOY_PATH=$DeployPath") - $OriginalEnv = Get-ChildItem Env: - Initialize-Build-Environment -BuildArch "x86" + if ($BuildOption) { $NsisArgs += "/DBUILD_OPTION=$BuildOption" } + Invoke-Native-Command "$RootPath\libs\NSIS\NSIS-source\makensis" (@($NsisArgs) + "$WindowsPath\installer.nsi") +} - Invoke-Native-Command -Command "msbuild" ` - -Arguments ("$RootPath\libs\NSIS\nsProcess\nsProcess.sln", '/p:Configuration="Release UNICODE"', ` - "/p:Platform=Win32") +# Build and copy NS-Process dll +Function Build-NSProcess { + if (Test-Path "$RootPath\libs\NSIS\nsProcess.dll") { Write-Log "nsProcess.dll exists, skipping."; return } + Write-Log "Building nsProcess.dll..." -Level STEP + $OrigEnv = Get-ChildItem Env:; Initialize-Build-Environment "x86" - Move-Item -Path "$RootPath\libs\NSIS\nsProcess\Release\nsProcess.dll" -Destination "$RootPath\libs\NSIS\nsProcess.dll" -Force - Remove-Item -Path "$RootPath\libs\NSIS\nsProcess\Release\" -Force -Recurse - $OriginalEnv | % { Set-Item "Env:$($_.Name)" $_.Value } - } + # Force msbuild to retarget the solution dynamically to match installed toolchain + Invoke-Native-Command "msbuild" ("$RootPath\libs\NSIS\nsProcess\nsProcess.sln", "/p:Configuration=Release UNICODE", "/p:Platform=Win32", "/p:PlatformToolset=$Env:VsPlatformToolset") + + Move-Item "$RootPath\libs\NSIS\nsProcess\Release\nsProcess.dll" "$RootPath\libs\NSIS\nsProcess.dll" -Force + Remove-Item "$RootPath\libs\NSIS\nsProcess\Release\" -Force -Recurse + $OrigEnv | ForEach-Object { Set-Item "Env:$($_.Name)" $_.Value } } -Clean-Build-Environment -Install-Dependencies -Build-App-Variants -Build-NSProcess -Build-Installer -BuildOption $BuildOption +try { + Clean-Build-Environment; Install-Dependencies; Build-App-Variants + Build-NSProcess; Build-Installer $BuildOption + Write-Log "Build process completed successfully." -Level STEP +} catch { + Write-Log "Pipeline failed: $_" -Level ERROR; exit 1 +} From 11e27c9693f0f51f446fcc8fac4501c770ed89ff Mon Sep 17 00:00:00 2001 From: JaminShanti Date: Thu, 7 May 2026 21:31:27 -0400 Subject: [PATCH 02/11] Potential fix for pull request finding Exiting comment, not mine. Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- windows/deploy_windows.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/windows/deploy_windows.ps1 b/windows/deploy_windows.ps1 index 5605014986..3423f313cc 100644 --- a/windows/deploy_windows.ps1 +++ b/windows/deploy_windows.ps1 @@ -81,7 +81,7 @@ Function Clean-Build-Environment { Write-Log "Build and Deploy directories initialized." } -# For sourceforge links we need to get the correct mirror (especially NISIS) +# For sourceforge links we need to get the correct mirror (especially NSIS) Function Get-RedirectedUrl { param([Parameter(Mandatory=$true)][string]$url) $sleep = 10; $maxSleep = 80 From c29ed94a798681ac7017efcd5ddd484d17194ef8 Mon Sep 17 00:00:00 2001 From: "Radjammin@gmail.com" Date: Thu, 7 May 2026 21:37:48 -0400 Subject: [PATCH 03/11] Execute native command with errorlevel handling and memory-safe logging --- windows/deploy_windows.ps1 | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/windows/deploy_windows.ps1 b/windows/deploy_windows.ps1 index 3423f313cc..bf0ff74eca 100644 --- a/windows/deploy_windows.ps1 +++ b/windows/deploy_windows.ps1 @@ -47,28 +47,30 @@ Function Write-Log { } } -# Execute native command with errorlevel handling +# Execute native command with errorlevel handling and memory-safe logging Function Invoke-Native-Command { - param([string]$Command, [string[]]$Arguments) - Write-Log "Executing: $Command $($Arguments -join ' ')" -Level DEBUG - $Output = [System.Collections.Generic.List[string]]::new() - - & "$Command" @Arguments 2>&1 | ForEach-Object { - $Line = $_.ToString(); $Output.Add($Line) - if ($DebugMode) { Write-Host " $Line" -ForegroundColor DarkGray } - elseif ($Line.Length -lt 120 -and $Line -notmatch "warning C\d+" -and -not [string]::IsNullOrWhiteSpace($Line)) { - Write-Host " $Line" -ForegroundColor DarkGray + param([string]$Cmd, [string[]]$Args, [int]$Max = 500) + Write-Log "Executing: $Cmd $($Args -join ' ')" -Level DEBUG + $Buf = [Collections.Generic.Queue[string]]::new() + + & $Cmd @Args 2>&1 | ForEach-Object { + $L = $_.ToString(); $Buf.Enqueue($L) + if ($Buf.Count -gt $Max) { [void]$Buf.Dequeue() } + + # Filtered real-time output + if ($DebugMode) { Write-Host " $L" -ForegroundColor DarkGray } + elseif ($L.Length -lt 120 -and $L -notmatch "warning C\d+" -and $L.Trim()) { + Write-Host " $L" -ForegroundColor DarkGray } } - if ($LastExitCode -Ne 0) { - $ErrMsg = "Native command $Command returned exit code $LastExitCode" - Write-Log $ErrMsg -Level ERROR + if ($LastExitCode -ne 0) { + Write-Log "Command $Cmd failed with exit code $LastExitCode" -Level ERROR if (-not $DebugMode) { - Write-Log "--- Command Output Log ---" -Level ERROR - $Output | ForEach-Object { Write-Log $_ -Level ERROR } + Write-Log "--- Last $Max lines ---" -Level ERROR + $Buf | ForEach-Object { Write-Log $_ -Level ERROR } } - Throw $ErrMsg + Throw "Native command failed." } } From 863e748e65e6a4e8fa79c05655947037ae00b094 Mon Sep 17 00:00:00 2001 From: "Radjammin@gmail.com" Date: Thu, 7 May 2026 21:44:21 -0400 Subject: [PATCH 04/11] fixed cleanup step --- windows/deploy_windows.ps1 | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/windows/deploy_windows.ps1 b/windows/deploy_windows.ps1 index bf0ff74eca..4ee3bebb74 100644 --- a/windows/deploy_windows.ps1 +++ b/windows/deploy_windows.ps1 @@ -225,7 +225,9 @@ Function Build-App { Write-Log "Deploying Qt dependencies..." Invoke-Native-Command $Env:QtWinDeployPath ("--$Cfg", "--compiler-runtime", "--dir=$DeployPath\$Arch", "$BuildPath\$Cfg\$AppName.exe") Move-Item "$BuildPath\$Cfg\$AppName.exe" "$DeployPath\$Arch" -Force - Invoke-Native-Command "nmake" (@("clean") + $MkArgs) + $CleanArgs = @("clean") + if (-not $DebugMode) { $CleanArgs += "/S" } + Invoke-Native-Command "nmake" $CleanArgs Set-Location $RootPath } From 7159342b9b1a6a50b196ec49706c35ca5e5c63a8 Mon Sep 17 00:00:00 2001 From: "Radjammin@gmail.com" Date: Thu, 7 May 2026 21:48:23 -0400 Subject: [PATCH 05/11] one change on the logging --- windows/deploy_windows.ps1 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/windows/deploy_windows.ps1 b/windows/deploy_windows.ps1 index 4ee3bebb74..e99ee86af8 100644 --- a/windows/deploy_windows.ps1 +++ b/windows/deploy_windows.ps1 @@ -49,11 +49,11 @@ Function Write-Log { # Execute native command with errorlevel handling and memory-safe logging Function Invoke-Native-Command { - param([string]$Cmd, [string[]]$Args, [int]$Max = 500) - Write-Log "Executing: $Cmd $($Args -join ' ')" -Level DEBUG + param([string]$Cmd, [string[]]$Arguments, [int]$Max = 500) # Fixed: Do not use reserved $Args + Write-Log "Executing: $Cmd $($Arguments -join ' ')" -Level DEBUG $Buf = [Collections.Generic.Queue[string]]::new() - & $Cmd @Args 2>&1 | ForEach-Object { + & $Cmd @Arguments 2>&1 | ForEach-Object { $L = $_.ToString(); $Buf.Enqueue($L) if ($Buf.Count -gt $Max) { [void]$Buf.Dequeue() } From 77895b3dfa0ebb0b70f8d292b51150f30a76a8c0 Mon Sep 17 00:00:00 2001 From: "Radjammin@gmail.com" Date: Fri, 8 May 2026 16:16:49 -0400 Subject: [PATCH 06/11] Improve PowerShell exception management --- windows/deploy_windows.ps1 | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/windows/deploy_windows.ps1 b/windows/deploy_windows.ps1 index e99ee86af8..fc499c0a76 100644 --- a/windows/deploy_windows.ps1 +++ b/windows/deploy_windows.ps1 @@ -48,18 +48,16 @@ Function Write-Log { } # Execute native command with errorlevel handling and memory-safe logging -Function Invoke-Native-Command { - param([string]$Cmd, [string[]]$Arguments, [int]$Max = 500) # Fixed: Do not use reserved $Args +Function Invoke-Native-Command([string]$Cmd, [string[]]$Arguments, [int]$Max = 500) { Write-Log "Executing: $Cmd $($Arguments -join ' ')" -Level DEBUG $Buf = [Collections.Generic.Queue[string]]::new() + $Local:ErrorActionPreference = "Continue" & $Cmd @Arguments 2>&1 | ForEach-Object { - $L = $_.ToString(); $Buf.Enqueue($L) + $Buf.Enqueue(($L = $_.ToString())) if ($Buf.Count -gt $Max) { [void]$Buf.Dequeue() } - # Filtered real-time output - if ($DebugMode) { Write-Host " $L" -ForegroundColor DarkGray } - elseif ($L.Length -lt 120 -and $L -notmatch "warning C\d+" -and $L.Trim()) { + if ($DebugMode -or ($L.Length -lt 120 -and $L -notmatch "warning C\d+" -and $L.Trim())) { Write-Host " $L" -ForegroundColor DarkGray } } From e1d6d2f9c2781788813107f77b8ccc30dd761cf1 Mon Sep 17 00:00:00 2001 From: "Radjammin@gmail.com" Date: Sat, 9 May 2026 17:56:12 -0400 Subject: [PATCH 07/11] Improve PowerShell automation object exception message --- windows/deploy_windows.ps1 | 56 +++++++++++++++++++++----------------- 1 file changed, 31 insertions(+), 25 deletions(-) diff --git a/windows/deploy_windows.ps1 b/windows/deploy_windows.ps1 index fc499c0a76..0abf3c65ea 100644 --- a/windows/deploy_windows.ps1 +++ b/windows/deploy_windows.ps1 @@ -47,28 +47,31 @@ Function Write-Log { } } -# Execute native command with errorlevel handling and memory-safe logging -Function Invoke-Native-Command([string]$Cmd, [string[]]$Arguments, [int]$Max = 500) { - Write-Log "Executing: $Cmd $($Arguments -join ' ')" -Level DEBUG - $Buf = [Collections.Generic.Queue[string]]::new() - $Local:ErrorActionPreference = "Continue" - - & $Cmd @Arguments 2>&1 | ForEach-Object { - $Buf.Enqueue(($L = $_.ToString())) - if ($Buf.Count -gt $Max) { [void]$Buf.Dequeue() } - - if ($DebugMode -or ($L.Length -lt 120 -and $L -notmatch "warning C\d+" -and $L.Trim())) { - Write-Host " $L" -ForegroundColor DarkGray +# Execute native command with errorlevel handling +Function Invoke-Native-Command { + param([Parameter(Mandatory)][string]$Command, [string[]]$Arguments, [switch]$SuppressStdErr) + Write-Log "Executing: $Command $($Arguments -join ' ')" "DEBUG" + $Out = [Collections.Generic.List[string]]::new() + + $PrevEA = $ErrorActionPreference; $ErrorActionPreference = "Continue" + try { + & $Command @Arguments 2>&1 | ForEach-Object { + $IsErr = $_ -is [Management.Automation.ErrorRecord] + if ($IsErr -and $SuppressStdErr) { return } + + $Line = if ($IsErr) { $_.TargetObject -as [string] } else { $_.ToString() } + if ([string]::IsNullOrWhiteSpace($Line)) { return } + + $Out.Add($Line) + $Color = if ($IsErr -and $DebugMode) { "DarkYellow" } else { "DarkGray" } + if ($DebugMode -or -not $IsErr) { Write-Host " $Line" -ForegroundColor $Color } } - } + } finally { $ErrorActionPreference = $PrevEA } - if ($LastExitCode -ne 0) { - Write-Log "Command $Cmd failed with exit code $LastExitCode" -Level ERROR - if (-not $DebugMode) { - Write-Log "--- Last $Max lines ---" -Level ERROR - $Buf | ForEach-Object { Write-Log $_ -Level ERROR } - } - Throw "Native command failed." + if ($LastExitCode) { + Write-Log ($Err = "Native command $Command failed (Exit: $LastExitCode)") "ERROR" + if (-not $DebugMode) { $Out.ForEach({ Write-Log $_ "ERROR" }) } + throw $Err } } @@ -81,7 +84,7 @@ Function Clean-Build-Environment { Write-Log "Build and Deploy directories initialized." } -# For sourceforge links we need to get the correct mirror (especially NSIS) +# For sourceforge links we need to get the correct mirror (especially NISIS) Function Get-RedirectedUrl { param([Parameter(Mandatory=$true)][string]$url) $sleep = 10; $maxSleep = 80 @@ -208,7 +211,9 @@ Function Build-App { Invoke-Native-Command $Env:QtQmakePath ("$RootPath\$AppName.pro", $QmkCfg, "-o", "$BuildPath\Makefile") Set-Location $BuildPath - $MkArgs = @($Cfg) + + # Add /NOLOGO to stop the Microsoft header from being generated + $MkArgs = @("/NOLOGO", $Cfg) if (-not $DebugMode) { $MkArgs += "/S" } if ($Env:QtJomPath) { @@ -223,9 +228,10 @@ Function Build-App { Write-Log "Deploying Qt dependencies..." Invoke-Native-Command $Env:QtWinDeployPath ("--$Cfg", "--compiler-runtime", "--dir=$DeployPath\$Arch", "$BuildPath\$Cfg\$AppName.exe") Move-Item "$BuildPath\$Cfg\$AppName.exe" "$DeployPath\$Arch" -Force - $CleanArgs = @("clean") - if (-not $DebugMode) { $CleanArgs += "/S" } - Invoke-Native-Command "nmake" $CleanArgs + + # Use the new switch to drop the 'Could Not Find' stderr stream entirely + Invoke-Native-Command "nmake" (@("clean") + $MkArgs) -SuppressStdErr + Set-Location $RootPath } From fc1ffbeb0fa38fe7acbf396d631c698cdc6c07c1 Mon Sep 17 00:00:00 2001 From: "Radjammin@gmail.com" Date: Sat, 9 May 2026 18:14:55 -0400 Subject: [PATCH 08/11] Adding back comment --- windows/deploy_windows.ps1 | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/windows/deploy_windows.ps1 b/windows/deploy_windows.ps1 index 0abf3c65ea..ed9b064143 100644 --- a/windows/deploy_windows.ps1 +++ b/windows/deploy_windows.ps1 @@ -33,7 +33,6 @@ $DeployPath = "$RootPath\deploy" $WindowsPath ="$RootPath\windows" $AppName = "Jamulus" -# --- Centralized Logging Function --- Function Write-Log { param([Parameter(Mandatory=$true)][AllowNull()][AllowEmptyString()][string]$Message, [ValidateSet("INFO","WARN","ERROR","STEP","DEBUG")][string]$Level="INFO") if ($null -eq $Message) { $Message = "" } @@ -84,7 +83,7 @@ Function Clean-Build-Environment { Write-Log "Build and Deploy directories initialized." } -# For sourceforge links we need to get the correct mirror (especially NISIS) +# For sourceforge links we need to get the correct mirror (especially NISIS) Thanks: https://www.powershellmagazine.com/2013/01/29/pstip-retrieve-a-redirected-url/ Function Get-RedirectedUrl { param([Parameter(Mandatory=$true)][string]$url) $sleep = 10; $maxSleep = 80 From 281eb3a4fff1b94dba1271c7fd887e06aa3dadb8 Mon Sep 17 00:00:00 2001 From: "Radjammin@gmail.com" Date: Mon, 11 May 2026 09:25:53 -0400 Subject: [PATCH 09/11] return formatting for PR review --- windows/deploy_windows.ps1 | 466 ++++++++++++++++++++++++++----------- 1 file changed, 334 insertions(+), 132 deletions(-) diff --git a/windows/deploy_windows.ps1 b/windows/deploy_windows.ps1 index ed9b064143..667440b372 100644 --- a/windows/deploy_windows.ps1 +++ b/windows/deploy_windows.ps1 @@ -1,7 +1,9 @@ param ( # Replace default path with system Qt installation folder if necessary - [string]$QtInstallPath32="C:\Qt\5.15.2", [string]$QtInstallPath64="C:\Qt\6.8.1", - [string]$QtCompile32="msvc2019", [string]$QtCompile64="msvc2022_64", + [string] $QtInstallPath32 = "C:\Qt\5.15.2", + [string] $QtInstallPath64 = "C:\Qt\6.8.1", + [string] $QtCompile32 = "msvc2019", + [string] $QtCompile64 = "msvc2022_64", # Important: # - Do not update ASIO SDK without checking for license-related changes. # - Do not copy (parts of) the ASIO SDK into the Jamulus source tree without @@ -9,11 +11,13 @@ param ( # # The following version pinnings are semi-automatically checked for # updates. Verify .github/workflows/bump-dependencies.yaml when changing those manually: - [string]$AsioSDKUrl="https://download.steinberg.net/sdk_downloads/ASIO-SDK_2.3.4_2025-10-15.zip", - [string]$NsisUrl="https://downloads.sourceforge.net/project/nsis/NSIS%203/3.12/nsis-3.12.zip", - [string]$BuildOption="", + [string] $AsioSDKUrl = "https://download.steinberg.net/sdk_downloads/ASIO-SDK_2.3.4_2025-10-15.zip", + [string] $NsisUrl = "https://downloads.sourceforge.net/project/nsis/NSIS%203/3.12/nsis-3.12.zip", + [string] $BuildOption = "", # Toggles for debugging and targeted builds - [switch]$DebugMode, [switch]$Skip64Bit, [switch]$Skip32Bit + [switch] $DebugMode, + [switch] $Skip64Bit, + [switch] $Skip32Bit ) # Fail early on all errors @@ -33,10 +37,21 @@ $DeployPath = "$RootPath\deploy" $WindowsPath ="$RootPath\windows" $AppName = "Jamulus" -Function Write-Log { - param([Parameter(Mandatory=$true)][AllowNull()][AllowEmptyString()][string]$Message, [ValidateSet("INFO","WARN","ERROR","STEP","DEBUG")][string]$Level="INFO") +Function Write-Log +{ + param( + [Parameter(Mandatory=$true)] + [AllowNull()] + [AllowEmptyString()] + [string] $Message, + [ValidateSet("INFO","WARN","ERROR","STEP","DEBUG")] + [string] $Level = "INFO" + ) + if ($null -eq $Message) { $Message = "" } - $Stamp = (Get-Date).ToString("yyyy-MM-dd HH:mm:ss"); $Log = "[$Stamp] [$Level] $Message" + $Stamp = (Get-Date).ToString("yyyy-MM-dd HH:mm:ss") + $Log = "[$Stamp] [$Level] $Message" + switch ($Level) { "INFO" { Write-Host $Log } "STEP" { Write-Host "`n>>> [$Stamp] $Message" -ForegroundColor Cyan } @@ -47,12 +62,21 @@ Function Write-Log { } # Execute native command with errorlevel handling -Function Invoke-Native-Command { - param([Parameter(Mandatory)][string]$Command, [string[]]$Arguments, [switch]$SuppressStdErr) +Function Invoke-Native-Command +{ + param( + [Parameter(Mandatory=$true)] + [string] $Command, + [string[]] $Arguments, + [switch] $SuppressStdErr + ) + Write-Log "Executing: $Command $($Arguments -join ' ')" "DEBUG" $Out = [Collections.Generic.List[string]]::new() - $PrevEA = $ErrorActionPreference; $ErrorActionPreference = "Continue" + $PrevEA = $ErrorActionPreference + $ErrorActionPreference = "Continue" + try { & $Command @Arguments 2>&1 | ForEach-Object { $IsErr = $_ -is [Management.Automation.ErrorRecord] @@ -65,248 +89,426 @@ Function Invoke-Native-Command { $Color = if ($IsErr -and $DebugMode) { "DarkYellow" } else { "DarkGray" } if ($DebugMode -or -not $IsErr) { Write-Host " $Line" -ForegroundColor $Color } } - } finally { $ErrorActionPreference = $PrevEA } + } finally { + $ErrorActionPreference = $PrevEA + } if ($LastExitCode) { - Write-Log ($Err = "Native command $Command failed (Exit: $LastExitCode)") "ERROR" - if (-not $DebugMode) { $Out.ForEach({ Write-Log $_ "ERROR" }) } + $Err = "Native command $Command failed (Exit: $LastExitCode)" + Write-Log $Err "ERROR" + if (-not $DebugMode) { + $Out.ForEach({ Write-Log $_ "ERROR" }) + } throw $Err } } # Cleanup existing build folders -Function Clean-Build-Environment { - Write-Log "Cleaning Build Environment..." -Level STEP - if (Test-Path $BuildPath) { Remove-Item $BuildPath -Recurse -Force } - if (Test-Path $DeployPath) { Remove-Item $DeployPath -Recurse -Force } - New-Item $BuildPath, $DeployPath -ItemType Directory | Out-Null - Write-Log "Build and Deploy directories initialized." +Function Clean-Build-Environment +{ + Write-Log "Cleaning Build Environment..." "STEP" + + if (Test-Path -Path $BuildPath) { Remove-Item -Path $BuildPath -Recurse -Force } + if (Test-Path -Path $DeployPath) { Remove-Item -Path $DeployPath -Recurse -Force } + + New-Item -Path $BuildPath -ItemType Directory | Out-Null + New-Item -Path $DeployPath -ItemType Directory | Out-Null + + Write-Log "Build and Deploy directories initialized." "INFO" } -# For sourceforge links we need to get the correct mirror (especially NISIS) Thanks: https://www.powershellmagazine.com/2013/01/29/pstip-retrieve-a-redirected-url/ -Function Get-RedirectedUrl { - param([Parameter(Mandatory=$true)][string]$url) - $sleep = 10; $maxSleep = 80 +# For sourceforge links we need to get the correct mirror (especially NSIS) Thanks: https://www.powershellmagazine.com/2013/01/29/pstip-retrieve-a-redirected-url/ +Function Get-RedirectedUrl +{ + param( + [Parameter(Mandatory=$true)] + [string] $url + ) + + $sleep = 10 + $maxSleep = 80 + for ($i = 1; $i -le 10; $i++) { try { - $req = [System.Net.WebRequest]::Create($url); $req.AllowAutoRedirect = $true - $res = $req.GetResponse(); $redirect = $res.ResponseUri.AbsoluteUri; $res.Close() + $req = [System.Net.WebRequest]::Create($url) + $req.AllowAutoRedirect = $true + $res = $req.GetResponse() + $redirect = $res.ResponseUri.AbsoluteUri + $res.Close() return $redirect } catch { if ($i -lt 10) { - Write-Log "Fetch attempt $i/10 for $url failed, retrying in ${sleep}s" -Level WARN - Start-Sleep -Seconds $sleep; $sleep = [Math]::Min($sleep * 2, $maxSleep) + Write-Log "Fetch attempt $i/10 for $url failed, retrying in ${sleep}s" "WARN" + Start-Sleep -Seconds $sleep + $sleep = [Math]::Min($sleep * 2, $maxSleep) continue } - Write-Log "All 10 fetch attempts for $url failed" -Level ERROR; throw + Write-Log "All 10 fetch attempts for $url failed" "ERROR" + throw } } } # Download and uncompress dependency in ZIP format -Function Install-Dependency { - param([Parameter(Mandatory=$true)][string]$Uri, [Parameter(Mandatory=$true)][string]$Destination) - if (Test-Path "$WindowsPath\$Destination") { Write-Log "Using cached ${WindowsPath}\${Destination}"; return } +Function Install-Dependency +{ + param( + [Parameter(Mandatory=$true)] + [string] $Uri, + [Parameter(Mandatory=$true)] + [string] $Destination + ) + + if (Test-Path -Path "$WindowsPath\$Destination") + { + Write-Log "Using cached ${WindowsPath}\${Destination}" "INFO" + return + } + $TempFile = [System.IO.Path]::GetTempFileName() + ".zip" # Create a unique empty directory to unpack into $TempDir = Join-Path ([System.IO.Path]::GetTempPath()) ([System.Guid]::NewGuid().ToString()) New-Item -ItemType Directory -Path $TempDir | Out-Null - if ($Uri -Match "downloads.sourceforge.net") { $Uri = Get-RedirectedUrl $Uri } - Write-Log "Downloading $Uri..." + if ($Uri -Match "downloads.sourceforge.net") + { + $Uri = Get-RedirectedUrl -url $Uri + } + + Write-Log "Downloading $Uri..." "INFO" Invoke-WebRequest -Uri $Uri -OutFile $TempFile - Write-Log "Extracting to $WindowsPath\$Destination..." + + Write-Log "Extracting to $WindowsPath\$Destination..." "INFO" Expand-Archive -Path $TempFile -DestinationPath $TempDir -Force - Move-Item "$TempDir\*" "$WindowsPath\$Destination" -Force - Remove-Item $TempDir, $TempFile -Recurse -Force + Move-Item -Path "$TempDir\*" -Destination "$WindowsPath\$Destination" -Force + Remove-Item -Path $TempDir -Recurse -Force + Remove-Item -Path $TempFile -Force } # Install ASIO SDK and NSIS Installer -Function Install-Dependencies { - Write-Log "Installing Dependencies..." -Level STEP - Install-Dependency $NsisUrl "..\libs\NSIS\NSIS-source" +Function Install-Dependencies +{ + Write-Log "Installing Dependencies..." "STEP" + Install-Dependency -Uri $NsisUrl -Destination "..\libs\NSIS\NSIS-source" if ($BuildOption -Notmatch "jack") { # Don't download ASIO SDK on Jamulus JACK builds to save resources. - Install-Dependency $AsioSDKUrl "..\libs\ASIOSDK2" - } else { Write-Log "Skipping ASIO SDK (JACK build detected)." } + Install-Dependency -Uri $AsioSDKUrl -Destination "..\libs\ASIOSDK2" + } else { + Write-Log "Skipping ASIO SDK (JACK build detected)." "INFO" + } } # Setup environment variables and build tool paths -Function Initialize-Build-Environment { - param([Parameter(Mandatory=$true)][string]$BuildArch) +Function Initialize-Build-Environment +{ + param( + [Parameter(Mandatory=$true)] + [string] $BuildArch + ) # Use native vswhere.exe to find VS2017+ installations with C++ build tools $vswhere = "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe" - if (-not (Test-Path $vswhere)) { Throw "vswhere.exe not found. Visual Studio 2017 or above is required." } + if (-not (Test-Path -Path $vswhere)) + { + Throw "vswhere.exe not found. Visual Studio 2017 or above is required." + } $VsPath = & $vswhere -latest -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath $VsVer = & $vswhere -latest -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationVersion - if ([string]::IsNullOrWhiteSpace($VsPath)) { Throw "Could not locate a Visual Studio installation with C++ build tools." } + if ([string]::IsNullOrWhiteSpace($VsPath)) + { + Throw "Could not locate a Visual Studio installation with C++ build tools." + } # Dynamically determine the correct Platform Toolset based on VS version to avoid hardcoded MSBuild failures if ($VsVer -match "^17\.") { Set-Item Env:VsPlatformToolset "v143" } elseif ($VsVer -match "^16\.") { Set-Item Env:VsPlatformToolset "v142" } else { Set-Item Env:VsPlatformToolset "v141" } - $VcVars = if ($BuildArch -eq "x86_64") { "$VsPath\VC\Auxiliary\build\vcvars64.bat" } else { "$VsPath\VC\Auxiliary\build\vcvars32.bat" } + if ($BuildArch -eq "x86_64") + { + $VcVarsBin = "$VsPath\VC\Auxiliary\build\vcvars64.bat" + } + else + { + $VcVarsBin = "$VsPath\VC\Auxiliary\build\vcvars32.bat" + } + + Write-Log "Using VS environment: $VcVarsBin (Toolset: $Env:VsPlatformToolset)" "INFO" - Write-Log "Using VS environment: $VcVars (Toolset: $Env:VsPlatformToolset)" - if (-not (Test-Path $VcVars)) { Throw "MSVC environment script ($BuildArch) not found at $VcVars" } + if (-not (Test-Path -Path $VcVarsBin)) + { + Throw "MSVC environment script ($BuildArch) not found at $VcVarsBin" + } # Import environment variables set by vcvarsXX.bat into current scope $EnvDump = [System.IO.Path]::GetTempFileName() - Invoke-Native-Command "cmd" ("/c", "`"$VcVars`" && set > `"$EnvDump`"") - Get-Content $EnvDump | Where-Object { $_ -match "^([^=]+)=(.*)$" } | ForEach-Object { Set-Item "Env:$($Matches[1])" $Matches[2] } - Remove-Item $EnvDump -Force + Invoke-Native-Command -Command "cmd" -Arguments ("/c", "`"$VcVarsBin`" && set > `"$EnvDump`"") + + foreach ($_ in Get-Content -Path $EnvDump) + { + if ($_ -match "^([^=]+)=(.*)$") + { + Set-Item "Env:$($Matches[1])" $Matches[2] + } + } + + Remove-Item -Path $EnvDump -Force } # Resolve Qt path by falling back to Registry lookups if the default path is missing -Function Resolve-Qt-Path { - param([Parameter(Mandatory=$true)][string]$DefaultPath) - if (Test-Path $DefaultPath) { return $DefaultPath } - Write-Log "Qt path '$DefaultPath' not found. Searching registry..." -Level DEBUG +Function Resolve-Qt-Path +{ + param( + [Parameter(Mandatory=$true)] + [string] $DefaultPath + ) + + if (Test-Path -Path $DefaultPath) { return $DefaultPath } + + Write-Log "Qt path '$DefaultPath' not found. Searching registry..." "DEBUG" $QtVer = Split-Path $DefaultPath -Leaf - $RegPaths = "HKCU:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*", "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*", "HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*" - foreach ($Reg in $RegPaths) { + $RegPaths = "HKCU:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*", + "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*", + "HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*" + + foreach ($Reg in $RegPaths) + { $Installs = Get-ItemProperty $Reg -ErrorAction SilentlyContinue | Where-Object { $_.DisplayName -match "Qt" -and $_.InstallLocation } - foreach ($Inst in $Installs) { + foreach ($Inst in $Installs) + { $TryPath = Join-Path $Inst.InstallLocation $QtVer - if (Test-Path $TryPath) { Write-Log "Discovered Qt at: $TryPath"; return $TryPath } + if (Test-Path -Path $TryPath) + { + Write-Log "Discovered Qt at: $TryPath" "INFO" + return $TryPath + } } } - Write-Log "Could not locate Qt $QtVer in registry." -Level WARN; return $DefaultPath + + Write-Log "Could not locate Qt $QtVer in registry." "WARN" + return $DefaultPath } # Setup Qt environment variables and build tool paths -Function Initialize-Qt-Build-Environment { - param([Parameter(Mandatory=$true)][string]$QtInstallPath, [Parameter(Mandatory=$true)][string]$QtCompile) - $QtBin = "$QtInstallPath\$QtCompile\bin" +Function Initialize-Qt-Build-Environment +{ + param( + [Parameter(Mandatory=$true)] + [string] $QtInstallPath, + [Parameter(Mandatory=$true)] + [string] $QtCompile + ) + + $QtMsvcSpecPath = "$QtInstallPath\$QtCompile\bin" + # Setup Qt executables paths for later calls - Set-Item Env:QtQmakePath "$QtBin\qmake.exe"; Set-Item Env:QtWinDeployPath "$QtBin\windeployqt.exe" + Set-Item Env:QtQmakePath "$QtMsvcSpecPath\qmake.exe" + Set-Item Env:QtWinDeployPath "$QtMsvcSpecPath\windeployqt.exe" - if (Get-Command "jom.exe" -ErrorAction SilentlyContinue) { Set-Item Env:QtJomPath "jom.exe" } - else { + if (Get-Command "jom.exe" -ErrorAction SilentlyContinue) + { + Set-Item Env:QtJomPath "jom.exe" + } + else + { $QtRoot = Split-Path $QtInstallPath -Parent - $JomExes = "$QtRoot\Tools\QtCreator\bin\jom\jom.exe", "$QtRoot\Tools\jom\jom.exe", "C:\Qt\Tools\QtCreator\bin\jom\jom.exe", "D:\Qt\Tools\QtCreator\bin\jom\jom.exe" - $FoundJom = $JomExes | Where-Object { Test-Path $_ } | Select-Object -First 1 - if ($FoundJom) { Write-Log "Discovered jom: $FoundJom"; Set-Item Env:QtJomPath $FoundJom } else { Set-Item Env:QtJomPath "" } + $JomExes = "$QtRoot\Tools\QtCreator\bin\jom\jom.exe", + "$QtRoot\Tools\jom\jom.exe", + "C:\Qt\Tools\QtCreator\bin\jom\jom.exe", + "D:\Qt\Tools\QtCreator\bin\jom\jom.exe" + + $FoundJom = $JomExes | Where-Object { Test-Path -Path $_ } | Select-Object -First 1 + if ($FoundJom) + { + Write-Log "Discovered jom: $FoundJom" "INFO" + Set-Item Env:QtJomPath $FoundJom + } + else + { + Set-Item Env:QtJomPath "" + } + } + + Write-Log "Using Qt binaries: $QtMsvcSpecPath" "INFO" + + if (-not (Test-Path -Path $Env:QtQmakePath)) + { + Throw "Qt MSVC binaries not found at $QtMsvcSpecPath" } - Write-Log "Using Qt binaries: $QtBin" - if (-not (Test-Path $Env:QtQmakePath)) { Throw "Qt MSVC binaries not found at $QtBin" } } # Build Jamulus x86_64 and x86 -Function Build-App { - param([Parameter(Mandatory=$true)][string]$Cfg, [Parameter(Mandatory=$true)][string]$Arch) - Write-Log "Building App ($Arch) config: $Cfg" - - $QmkCfg = "CONFIG+=$Cfg $Arch $BuildOption" +Function Build-App +{ + param( + [Parameter(Mandatory=$true)] + [string] $BuildConfig, + [Parameter(Mandatory=$true)] + [string] $BuildArch + ) + + Write-Log "Building App ($BuildArch) config: $BuildConfig" "INFO" + + $QmkCfg = "CONFIG+=$BuildConfig $BuildArch $BuildOption" if (-not $DebugMode) { $QmkCfg += " silent" } - Invoke-Native-Command $Env:QtQmakePath ("$RootPath\$AppName.pro", $QmkCfg, "-o", "$BuildPath\Makefile") + Invoke-Native-Command -Command "$Env:QtQmakePath" ` + -Arguments ("$RootPath\$AppName.pro", $QmkCfg, "-o", "$BuildPath\Makefile") - Set-Location $BuildPath + Set-Location -Path $BuildPath # Add /NOLOGO to stop the Microsoft header from being generated - $MkArgs = @("/NOLOGO", $Cfg) + $MkArgs = @("/NOLOGO", $BuildConfig) if (-not $DebugMode) { $MkArgs += "/S" } - if ($Env:QtJomPath) { + if ($Env:QtJomPath) + { $Cores = [Math]::Max(1, [Math]::Floor([int]$Env:NUMBER_OF_PROCESSORS / 2)) - Write-Log "Building with jom /J $Cores (half of $Env:NUMBER_OF_PROCESSORS cores)" - Invoke-Native-Command $Env:QtJomPath (@("/J", "$Cores") + $MkArgs) - } else { - Write-Log "Building with nmake (sequential)" -Level WARN - Invoke-Native-Command "nmake" $MkArgs + Write-Log "Building with jom /J $Cores (half of $Env:NUMBER_OF_PROCESSORS cores)" "INFO" + Invoke-Native-Command -Command "$Env:QtJomPath" -Arguments (@("/J", "$Cores") + $MkArgs) } + else + { + Write-Log "Building with nmake (sequential)" "WARN" + Invoke-Native-Command -Command "nmake" -Arguments $MkArgs + } + + Write-Log "Deploying Qt dependencies..." "INFO" + Invoke-Native-Command -Command "$Env:QtWinDeployPath" ` + -Arguments ("--$BuildConfig", "--compiler-runtime", "--dir=$DeployPath\$BuildArch", "$BuildPath\$BuildConfig\$AppName.exe") - Write-Log "Deploying Qt dependencies..." - Invoke-Native-Command $Env:QtWinDeployPath ("--$Cfg", "--compiler-runtime", "--dir=$DeployPath\$Arch", "$BuildPath\$Cfg\$AppName.exe") - Move-Item "$BuildPath\$Cfg\$AppName.exe" "$DeployPath\$Arch" -Force + Move-Item -Path "$BuildPath\$BuildConfig\$AppName.exe" -Destination "$DeployPath\$BuildArch" -Force # Use the new switch to drop the 'Could Not Find' stderr stream entirely - Invoke-Native-Command "nmake" (@("clean") + $MkArgs) -SuppressStdErr + Invoke-Native-Command -Command "nmake" -Arguments (@("clean") + $MkArgs) -SuppressStdErr - Set-Location $RootPath + Set-Location -Path $RootPath } # Build and deploy Jamulus 64bit and 32bit variants -function Build-App-Variants { - Write-Log "Starting Application Builds" -Level STEP +function Build-App-Variants +{ + Write-Log "Starting Application Builds" "STEP" $ArchsToBuild = @() - if (-not $Skip64Bit) { $ArchsToBuild += "x86_64" } else { Write-Log "Skipping 64-bit build (-Skip64Bit used)." -Level INFO } - if (-not $Skip32Bit) { $ArchsToBuild += "x86" } else { Write-Log "Skipping 32-bit build (-Skip32Bit used)." -Level INFO } + if (-not $Skip64Bit) { $ArchsToBuild += "x86_64" } else { Write-Log "Skipping 64-bit build (-Skip64Bit used)." "INFO" } + if (-not $Skip32Bit) { $ArchsToBuild += "x86" } else { Write-Log "Skipping 32-bit build (-Skip32Bit used)." "INFO" } - if ($ArchsToBuild.Count -eq 0) { - Write-Log "All application builds skipped." -Level WARN + if ($ArchsToBuild.Count -eq 0) + { + Write-Log "All application builds skipped." "WARN" return } - foreach ($Arch in $ArchsToBuild) { - Write-Log "Configuring environment for $Arch..." - $OrigEnv = Get-ChildItem Env: + foreach ($Arch in $ArchsToBuild) + { + Write-Log "Configuring environment for $Arch..." "INFO" + $OriginalEnv = Get-ChildItem Env: try { - $Path = if ($Arch -eq "x86") { $QtInstallPath32 } else { $QtInstallPath64 } - $Comp = if ($Arch -eq "x86") { $QtCompile32 } else { $QtCompile64 } - Initialize-Build-Environment $Arch - Initialize-Qt-Build-Environment (Resolve-Qt-Path $Path) $Comp - Build-App "release" $Arch + if ($Arch -eq "x86") + { + $Path = $QtInstallPath32 + $Comp = $QtCompile32 + } + else + { + $Path = $QtInstallPath64 + $Comp = $QtCompile64 + } + + Initialize-Build-Environment -BuildArch $Arch + Initialize-Qt-Build-Environment -QtInstallPath (Resolve-Qt-Path -DefaultPath $Path) -QtCompile $Comp + Build-App -BuildConfig "release" -BuildArch $Arch } catch { - Write-Log "Build failed for ${Arch}: $_" -Level WARN + Write-Log "Build failed for ${Arch}: $_" "WARN" } finally { # Ensure the environment is reset before the next loop, even if this arch fails - $OrigEnv | ForEach-Object { Set-Item "Env:$($_.Name)" $_.Value } + $OriginalEnv | ForEach-Object { Set-Item "Env:$($_.Name)" $_.Value } } } } # Build Windows installer -Function Build-Installer { - param([string]$BuildOption) - Write-Log "Building Windows Installer" -Level STEP +Function Build-Installer +{ + param( + [string] $BuildOption + ) + + Write-Log "Building Windows Installer" "STEP" # --- SAFETY GATE: Do not build if no executables were generated --- - $Has64 = Test-Path "$DeployPath\x86_64\$AppName.exe" - $Has32 = Test-Path "$DeployPath\x86\$AppName.exe" + $Has64 = Test-Path -Path "$DeployPath\x86_64\$AppName.exe" + $Has32 = Test-Path -Path "$DeployPath\x86\$AppName.exe" - if (-not $Has64 -and -not $Has32) { - Write-Log "No built binaries found in $DeployPath. Skipping installer to avoid creating an empty package." -Level ERROR + if (-not $Has64 -and -not $Has32) + { + Write-Log "No built binaries found in $DeployPath. Skipping installer to avoid creating an empty package." "ERROR" Throw "Installer build aborted: No application binaries found." } - $Ver = (Get-Content "$RootPath\$AppName.pro" | Where-Object { $_ -match "^VERSION *= *(.*)$" } | ForEach-Object { $Matches[1] } | Select-Object -First 1) + foreach ($_ in Get-Content -Path "$RootPath\$AppName.pro") + { + if ($_ -Match "^VERSION *= *(.*)$") + { + $AppVersion = $Matches[1] + break + } + } $NsisVerb = if ($DebugMode) { "/v4" } else { "/v2" } - $NsisArgs = @($NsisVerb, "/DAPP_NAME=$AppName", "/DAPP_VERSION=$Ver", "/DROOT_PATH=$RootPath", "/DWINDOWS_PATH=$WindowsPath", "/DDEPLOY_PATH=$DeployPath") + $NsisArgs = @($NsisVerb, "/DAPP_NAME=$AppName", "/DAPP_VERSION=$AppVersion", ` + "/DROOT_PATH=$RootPath", "/DWINDOWS_PATH=$WindowsPath", "/DDEPLOY_PATH=$DeployPath") + + if ($BuildOption -ne "") + { + $NsisArgs += "/DBUILD_OPTION=$BuildOption" + } - if ($BuildOption) { $NsisArgs += "/DBUILD_OPTION=$BuildOption" } - Invoke-Native-Command "$RootPath\libs\NSIS\NSIS-source\makensis" (@($NsisArgs) + "$WindowsPath\installer.nsi") + Invoke-Native-Command -Command "$RootPath\libs\NSIS\NSIS-source\makensis" ` + -Arguments (@($NsisArgs) + "$WindowsPath\installer.nsi") } # Build and copy NS-Process dll -Function Build-NSProcess { - if (Test-Path "$RootPath\libs\NSIS\nsProcess.dll") { Write-Log "nsProcess.dll exists, skipping."; return } - Write-Log "Building nsProcess.dll..." -Level STEP - $OrigEnv = Get-ChildItem Env:; Initialize-Build-Environment "x86" +Function Build-NSProcess +{ + if (-not (Test-Path -Path "$RootPath\libs\NSIS\nsProcess.dll")) + { + Write-Log "Building nsProcess.dll..." "STEP" + + $OriginalEnv = Get-ChildItem Env: + Initialize-Build-Environment -BuildArch "x86" - # Force msbuild to retarget the solution dynamically to match installed toolchain - Invoke-Native-Command "msbuild" ("$RootPath\libs\NSIS\nsProcess\nsProcess.sln", "/p:Configuration=Release UNICODE", "/p:Platform=Win32", "/p:PlatformToolset=$Env:VsPlatformToolset") + # Force msbuild to retarget the solution dynamically to match installed toolchain + Invoke-Native-Command -Command "msbuild" ` + -Arguments ("$RootPath\libs\NSIS\nsProcess\nsProcess.sln", '/p:Configuration="Release UNICODE"', ` + "/p:Platform=Win32", "/p:PlatformToolset=$Env:VsPlatformToolset") - Move-Item "$RootPath\libs\NSIS\nsProcess\Release\nsProcess.dll" "$RootPath\libs\NSIS\nsProcess.dll" -Force - Remove-Item "$RootPath\libs\NSIS\nsProcess\Release\" -Force -Recurse - $OrigEnv | ForEach-Object { Set-Item "Env:$($_.Name)" $_.Value } + Move-Item -Path "$RootPath\libs\NSIS\nsProcess\Release\nsProcess.dll" -Destination "$RootPath\libs\NSIS\nsProcess.dll" -Force + Remove-Item -Path "$RootPath\libs\NSIS\nsProcess\Release\" -Force -Recurse + + $OriginalEnv | ForEach-Object { Set-Item "Env:$($_.Name)" $_.Value } + } + else + { + Write-Log "nsProcess.dll exists, skipping." "INFO" + } } try { - Clean-Build-Environment; Install-Dependencies; Build-App-Variants - Build-NSProcess; Build-Installer $BuildOption - Write-Log "Build process completed successfully." -Level STEP + Clean-Build-Environment + Install-Dependencies + Build-App-Variants + Build-NSProcess + Build-Installer -BuildOption $BuildOption + Write-Log "Build process completed successfully." "STEP" } catch { - Write-Log "Pipeline failed: $_" -Level ERROR; exit 1 + Write-Log "Pipeline failed: $_" "ERROR" + exit 1 } From a0e2ebd15205a36c2a2566862453afa64391188c Mon Sep 17 00:00:00 2001 From: "Radjammin@gmail.com" Date: Mon, 11 May 2026 09:32:49 -0400 Subject: [PATCH 10/11] return formatting for PR review more formatting reverted --- windows/deploy_windows.ps1 | 228 ++++++++++++++++++------------------- 1 file changed, 109 insertions(+), 119 deletions(-) diff --git a/windows/deploy_windows.ps1 b/windows/deploy_windows.ps1 index 667440b372..e9bf5cf93f 100644 --- a/windows/deploy_windows.ps1 +++ b/windows/deploy_windows.ps1 @@ -77,7 +77,8 @@ Function Invoke-Native-Command $PrevEA = $ErrorActionPreference $ErrorActionPreference = "Continue" - try { + try + { & $Command @Arguments 2>&1 | ForEach-Object { $IsErr = $_ -is [Management.Automation.ErrorRecord] if ($IsErr -and $SuppressStdErr) { return } @@ -89,35 +90,34 @@ Function Invoke-Native-Command $Color = if ($IsErr -and $DebugMode) { "DarkYellow" } else { "DarkGray" } if ($DebugMode -or -not $IsErr) { Write-Host " $Line" -ForegroundColor $Color } } - } finally { + } + finally + { $ErrorActionPreference = $PrevEA } - if ($LastExitCode) { - $Err = "Native command $Command failed (Exit: $LastExitCode)" + if ($LastExitCode -Ne 0) + { + $Err = "Native command $Command returned with exit code $LastExitCode" Write-Log $Err "ERROR" if (-not $DebugMode) { $Out.ForEach({ Write-Log $_ "ERROR" }) } - throw $Err + Throw $Err } } # Cleanup existing build folders Function Clean-Build-Environment { - Write-Log "Cleaning Build Environment..." "STEP" - if (Test-Path -Path $BuildPath) { Remove-Item -Path $BuildPath -Recurse -Force } if (Test-Path -Path $DeployPath) { Remove-Item -Path $DeployPath -Recurse -Force } New-Item -Path $BuildPath -ItemType Directory | Out-Null New-Item -Path $DeployPath -ItemType Directory | Out-Null - - Write-Log "Build and Deploy directories initialized." "INFO" } -# For sourceforge links we need to get the correct mirror (especially NSIS) Thanks: https://www.powershellmagazine.com/2013/01/29/pstip-retrieve-a-redirected-url/ +# For sourceforge links we need to get the correct mirror (especially NISIS) Thanks: https://www.powershellmagazine.com/2013/01/29/pstip-retrieve-a-redirected-url/ Function Get-RedirectedUrl { param( @@ -125,25 +125,31 @@ Function Get-RedirectedUrl [string] $url ) - $sleep = 10 - $maxSleep = 80 - - for ($i = 1; $i -le 10; $i++) { - try { - $req = [System.Net.WebRequest]::Create($url) - $req.AllowAutoRedirect = $true - $res = $req.GetResponse() - $redirect = $res.ResponseUri.AbsoluteUri - $res.Close() + $numAttempts = 10 + $sleepTime = 10 + $maxSleepTime = 80 + for ($attempt = 1; $attempt -le $numAttempts; $attempt++) + { + try + { + $request = [System.Net.WebRequest]::Create($url) + $request.AllowAutoRedirect=$true + $response=$request.GetResponse() + $redirect = $response.ResponseUri.AbsoluteUri + $response.Close() return $redirect - } catch { - if ($i -lt 10) { - Write-Log "Fetch attempt $i/10 for $url failed, retrying in ${sleep}s" "WARN" - Start-Sleep -Seconds $sleep - $sleep = [Math]::Min($sleep * 2, $maxSleep) + } + catch + { + if ($attempt -lt $numAttempts) + { + Write-Warning "Caught error: $_" + Write-Warning "Get-RedirectedUrl: Fetch attempt #${attempt}/${numAttempts} for $url failed, trying again in ${sleepTime}s" + Start-Sleep -Seconds $sleepTime + $sleepTime = [Math]::Min($sleepTime * 2, $maxSleepTime) continue } - Write-Log "All 10 fetch attempts for $url failed" "ERROR" + Write-Error "Get-RedirectedUrl: All ${numAttempts} fetch attempts for $url failed, failing whole call" throw } } @@ -161,14 +167,15 @@ Function Install-Dependency if (Test-Path -Path "$WindowsPath\$Destination") { - Write-Log "Using cached ${WindowsPath}\${Destination}" "INFO" + Write-Log "Using ${WindowsPath}\${Destination} from previous run (e.g. actions/cache)" "INFO" return } - $TempFile = [System.IO.Path]::GetTempFileName() + ".zip" - + $TempFileName = [System.IO.Path]::GetTempFileName() + ".zip" + $TempPath = [System.IO.Path]::GetTempPath() + $TempGuid = [System.Guid]::NewGuid() # Create a unique empty directory to unpack into - $TempDir = Join-Path ([System.IO.Path]::GetTempPath()) ([System.Guid]::NewGuid().ToString()) + $TempDir = (Join-Path $TempPath $TempGuid) New-Item -ItemType Directory -Path $TempDir | Out-Null if ($Uri -Match "downloads.sourceforge.net") @@ -177,14 +184,16 @@ Function Install-Dependency } Write-Log "Downloading $Uri..." "INFO" - Invoke-WebRequest -Uri $Uri -OutFile $TempFile + Invoke-WebRequest -Uri $Uri -OutFile $TempFileName Write-Log "Extracting to $WindowsPath\$Destination..." "INFO" - Expand-Archive -Path $TempFile -DestinationPath $TempDir -Force + Expand-Archive -Path $TempFileName -DestinationPath $TempDir -Force + # Because we unpacked into a new directory, we can use * for the directory in the archive, + # so that we do not need to know the directory name the archive was packed from. Move-Item -Path "$TempDir\*" -Destination "$WindowsPath\$Destination" -Force Remove-Item -Path $TempDir -Recurse -Force - Remove-Item -Path $TempFile -Force + Remove-Item -Path $TempFileName -Force } # Install ASIO SDK and NSIS Installer @@ -193,10 +202,14 @@ Function Install-Dependencies Write-Log "Installing Dependencies..." "STEP" Install-Dependency -Uri $NsisUrl -Destination "..\libs\NSIS\NSIS-source" - if ($BuildOption -Notmatch "jack") { - # Don't download ASIO SDK on Jamulus JACK builds to save resources. + if ($BuildOption -Notmatch "jack") + { + # Don't download ASIO SDK on Jamulus JACK builds to save + # resources and to be extra-sure license-wise. Install-Dependency -Uri $AsioSDKUrl -Destination "..\libs\ASIOSDK2" - } else { + } + else + { Write-Log "Skipping ASIO SDK (JACK build detected)." "INFO" } } @@ -216,10 +229,10 @@ Function Initialize-Build-Environment Throw "vswhere.exe not found. Visual Studio 2017 or above is required." } - $VsPath = & $vswhere -latest -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath + $VsInstallPath = & $vswhere -latest -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath $VsVer = & $vswhere -latest -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationVersion - if ([string]::IsNullOrWhiteSpace($VsPath)) + if ([string]::IsNullOrWhiteSpace($VsInstallPath)) { Throw "Could not locate a Visual Studio installation with C++ build tools." } @@ -229,29 +242,36 @@ Function Initialize-Build-Environment elseif ($VsVer -match "^16\.") { Set-Item Env:VsPlatformToolset "v142" } else { Set-Item Env:VsPlatformToolset "v141" } - if ($BuildArch -eq "x86_64") + if ($BuildArch -Eq "x86_64") { - $VcVarsBin = "$VsPath\VC\Auxiliary\build\vcvars64.bat" + $VcVarsBin = "$VsInstallPath\VC\Auxiliary\build\vcvars64.bat" } else { - $VcVarsBin = "$VsPath\VC\Auxiliary\build\vcvars32.bat" + $VcVarsBin = "$VsInstallPath\VC\Auxiliary\build\vcvars32.bat" } - Write-Log "Using VS environment: $VcVarsBin (Toolset: $Env:VsPlatformToolset)" "INFO" + "" + "**********************************************************************" + "Using Visual Studio/Build Tools environment settings located at" + $VcVarsBin + "**********************************************************************" + "" - if (-not (Test-Path -Path $VcVarsBin)) + if (-Not (Test-Path -Path $VcVarsBin)) { - Throw "MSVC environment script ($BuildArch) not found at $VcVarsBin" + Throw "Microsoft Visual Studio ($BuildArch variant) is not installed. " + ` + "Please install Visual Studio 2017 or above it before running this script." } # Import environment variables set by vcvarsXX.bat into current scope $EnvDump = [System.IO.Path]::GetTempFileName() - Invoke-Native-Command -Command "cmd" -Arguments ("/c", "`"$VcVarsBin`" && set > `"$EnvDump`"") + Invoke-Native-Command -Command "cmd" ` + -Arguments ("/c", "`"$VcVarsBin`" && set > `"$EnvDump`"") foreach ($_ in Get-Content -Path $EnvDump) { - if ($_ -match "^([^=]+)=(.*)$") + if ($_ -Match "^([^=]+)=(.*)$") { Set-Item "Env:$($Matches[1])" $Matches[2] } @@ -290,7 +310,6 @@ Function Resolve-Qt-Path } } - Write-Log "Could not locate Qt $QtVer in registry." "WARN" return $DefaultPath } @@ -325,7 +344,6 @@ Function Initialize-Qt-Build-Environment $FoundJom = $JomExes | Where-Object { Test-Path -Path $_ } | Select-Object -First 1 if ($FoundJom) { - Write-Log "Discovered jom: $FoundJom" "INFO" Set-Item Env:QtJomPath $FoundJom } else @@ -334,11 +352,17 @@ Function Initialize-Qt-Build-Environment } } - Write-Log "Using Qt binaries: $QtMsvcSpecPath" "INFO" + "**********************************************************************" + "Using Qt binaries for Visual C++ located at" + $QtMsvcSpecPath + "**********************************************************************" + "" - if (-not (Test-Path -Path $Env:QtQmakePath)) + if (-Not (Test-Path -Path $Env:QtQmakePath)) { - Throw "Qt MSVC binaries not found at $QtMsvcSpecPath" + Throw "The Qt binaries for Microsoft Visual C++ 2017 or above could not be located at $QtMsvcSpecPath. " + ` + "Please install Qt with support for MSVC 2017 or above before running this script," + ` + "then call this script with the Qt install location, for example C:\Qt\5.15.2" } } @@ -352,8 +376,6 @@ Function Build-App [string] $BuildArch ) - Write-Log "Building App ($BuildArch) config: $BuildConfig" "INFO" - $QmkCfg = "CONFIG+=$BuildConfig $BuildArch $BuildOption" if (-not $DebugMode) { $QmkCfg += " silent" } @@ -362,7 +384,6 @@ Function Build-App Set-Location -Path $BuildPath - # Add /NOLOGO to stop the Microsoft header from being generated $MkArgs = @("/NOLOGO", $BuildConfig) if (-not $DebugMode) { $MkArgs += "/S" } @@ -378,13 +399,12 @@ Function Build-App Invoke-Native-Command -Command "nmake" -Arguments $MkArgs } - Write-Log "Deploying Qt dependencies..." "INFO" Invoke-Native-Command -Command "$Env:QtWinDeployPath" ` - -Arguments ("--$BuildConfig", "--compiler-runtime", "--dir=$DeployPath\$BuildArch", "$BuildPath\$BuildConfig\$AppName.exe") + -Arguments ("--$BuildConfig", "--compiler-runtime", "--dir=$DeployPath\$BuildArch", ` + "$BuildPath\$BuildConfig\$AppName.exe") Move-Item -Path "$BuildPath\$BuildConfig\$AppName.exe" -Destination "$DeployPath\$BuildArch" -Force - # Use the new switch to drop the 'Could Not Find' stderr stream entirely Invoke-Native-Command -Command "nmake" -Arguments (@("clean") + $MkArgs) -SuppressStdErr Set-Location -Path $RootPath @@ -393,44 +413,23 @@ Function Build-App # Build and deploy Jamulus 64bit and 32bit variants function Build-App-Variants { - Write-Log "Starting Application Builds" "STEP" - - $ArchsToBuild = @() - if (-not $Skip64Bit) { $ArchsToBuild += "x86_64" } else { Write-Log "Skipping 64-bit build (-Skip64Bit used)." "INFO" } - if (-not $Skip32Bit) { $ArchsToBuild += "x86" } else { Write-Log "Skipping 32-bit build (-Skip32Bit used)." "INFO" } - - if ($ArchsToBuild.Count -eq 0) + foreach ($_ in ("x86_64", "x86")) { - Write-Log "All application builds skipped." "WARN" - return - } + if (($_ -eq "x86_64" -and $Skip64Bit) -or ($_ -eq "x86" -and $Skip32Bit)) { continue } - foreach ($Arch in $ArchsToBuild) - { - Write-Log "Configuring environment for $Arch..." "INFO" $OriginalEnv = Get-ChildItem Env: - - try { - if ($Arch -eq "x86") - { - $Path = $QtInstallPath32 - $Comp = $QtCompile32 - } - else - { - $Path = $QtInstallPath64 - $Comp = $QtCompile64 - } - - Initialize-Build-Environment -BuildArch $Arch - Initialize-Qt-Build-Environment -QtInstallPath (Resolve-Qt-Path -DefaultPath $Path) -QtCompile $Comp - Build-App -BuildConfig "release" -BuildArch $Arch - } catch { - Write-Log "Build failed for ${Arch}: $_" "WARN" - } finally { - # Ensure the environment is reset before the next loop, even if this arch fails - $OriginalEnv | ForEach-Object { Set-Item "Env:$($_.Name)" $_.Value } + if ($_ -eq "x86") + { + Initialize-Build-Environment -BuildArch $_ + Initialize-Qt-Build-Environment -QtInstallPath (Resolve-Qt-Path -DefaultPath $QtInstallPath32) -QtCompile $QtCompile32 } + else + { + Initialize-Build-Environment -BuildArch $_ + Initialize-Qt-Build-Environment -QtInstallPath (Resolve-Qt-Path -DefaultPath $QtInstallPath64) -QtCompile $QtCompile64 + } + Build-App -BuildConfig "release" -BuildArch $_ + $OriginalEnv | % { Set-Item "Env:$($_.Name)" $_.Value } } } @@ -441,15 +440,12 @@ Function Build-Installer [string] $BuildOption ) - Write-Log "Building Windows Installer" "STEP" - # --- SAFETY GATE: Do not build if no executables were generated --- $Has64 = Test-Path -Path "$DeployPath\x86_64\$AppName.exe" $Has32 = Test-Path -Path "$DeployPath\x86\$AppName.exe" if (-not $Has64 -and -not $Has32) { - Write-Log "No built binaries found in $DeployPath. Skipping installer to avoid creating an empty package." "ERROR" Throw "Installer build aborted: No application binaries found." } @@ -463,29 +459,32 @@ Function Build-Installer } $NsisVerb = if ($DebugMode) { "/v4" } else { "/v2" } - $NsisArgs = @($NsisVerb, "/DAPP_NAME=$AppName", "/DAPP_VERSION=$AppVersion", ` - "/DROOT_PATH=$RootPath", "/DWINDOWS_PATH=$WindowsPath", "/DDEPLOY_PATH=$DeployPath") if ($BuildOption -ne "") { - $NsisArgs += "/DBUILD_OPTION=$BuildOption" + Invoke-Native-Command -Command "$RootPath\libs\NSIS\NSIS-source\makensis" ` + -Arguments (@($NsisVerb, "/DAPP_NAME=$AppName", "/DAPP_VERSION=$AppVersion", ` + "/DROOT_PATH=$RootPath", "/DWINDOWS_PATH=$WindowsPath", "/DDEPLOY_PATH=$DeployPath", ` + "/DBUILD_OPTION=$BuildOption") + "$WindowsPath\installer.nsi") + } + else + { + Invoke-Native-Command -Command "$RootPath\libs\NSIS\NSIS-source\makensis" ` + -Arguments (@($NsisVerb, "/DAPP_NAME=$AppName", "/DAPP_VERSION=$AppVersion", ` + "/DROOT_PATH=$RootPath", "/DWINDOWS_PATH=$WindowsPath", "/DDEPLOY_PATH=$DeployPath") + "$WindowsPath\installer.nsi") } - - Invoke-Native-Command -Command "$RootPath\libs\NSIS\NSIS-source\makensis" ` - -Arguments (@($NsisArgs) + "$WindowsPath\installer.nsi") } # Build and copy NS-Process dll Function Build-NSProcess { - if (-not (Test-Path -Path "$RootPath\libs\NSIS\nsProcess.dll")) + if (!(Test-Path -path "$RootPath\libs\NSIS\nsProcess.dll")) { - Write-Log "Building nsProcess.dll..." "STEP" + Write-Log "Building nsProcess..." "INFO" $OriginalEnv = Get-ChildItem Env: Initialize-Build-Environment -BuildArch "x86" - # Force msbuild to retarget the solution dynamically to match installed toolchain Invoke-Native-Command -Command "msbuild" ` -Arguments ("$RootPath\libs\NSIS\nsProcess\nsProcess.sln", '/p:Configuration="Release UNICODE"', ` "/p:Platform=Win32", "/p:PlatformToolset=$Env:VsPlatformToolset") @@ -493,22 +492,13 @@ Function Build-NSProcess Move-Item -Path "$RootPath\libs\NSIS\nsProcess\Release\nsProcess.dll" -Destination "$RootPath\libs\NSIS\nsProcess.dll" -Force Remove-Item -Path "$RootPath\libs\NSIS\nsProcess\Release\" -Force -Recurse - $OriginalEnv | ForEach-Object { Set-Item "Env:$($_.Name)" $_.Value } - } - else - { - Write-Log "nsProcess.dll exists, skipping." "INFO" + $OriginalEnv | % { Set-Item "Env:$($_.Name)" $_.Value } } } -try { - Clean-Build-Environment - Install-Dependencies - Build-App-Variants - Build-NSProcess - Build-Installer -BuildOption $BuildOption - Write-Log "Build process completed successfully." "STEP" -} catch { - Write-Log "Pipeline failed: $_" "ERROR" - exit 1 -} +Clean-Build-Environment +Install-Dependencies +Build-App-Variants +Build-NSProcess +Build-Installer -BuildOption $BuildOption +Write-Log "Build process completed successfully." "STEP" From bf720ac7fbf80f7da3804fa0b43d02a501b0c5d5 Mon Sep 17 00:00:00 2001 From: "Radjammin@gmail.com" Date: Wed, 13 May 2026 20:38:20 -0400 Subject: [PATCH 11/11] addressing errors in Build for Windows codeQL step --- windows/deploy_windows.ps1 | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/windows/deploy_windows.ps1 b/windows/deploy_windows.ps1 index e9bf5cf93f..ce03945165 100644 --- a/windows/deploy_windows.ps1 +++ b/windows/deploy_windows.ps1 @@ -405,7 +405,10 @@ Function Build-App Move-Item -Path "$BuildPath\$BuildConfig\$AppName.exe" -Destination "$DeployPath\$BuildArch" -Force - Invoke-Native-Command -Command "nmake" -Arguments (@("clean") + $MkArgs) -SuppressStdErr + # Updated clean args to prevent recompiling the app during the clean phase + $CleanArgs = @("clean", "/NOLOGO") + if (-not $DebugMode) { $CleanArgs += "/S" } + Invoke-Native-Command -Command "nmake" -Arguments $CleanArgs -SuppressStdErr Set-Location -Path $RootPath } @@ -430,6 +433,10 @@ function Build-App-Variants } Build-App -BuildConfig "release" -BuildArch $_ $OriginalEnv | % { Set-Item "Env:$($_.Name)" $_.Value } + + # Wipe the build directory so the next architecture starts entirely fresh + if (Test-Path -Path $BuildPath) { Remove-Item -Path $BuildPath -Recurse -Force } + New-Item -Path $BuildPath -ItemType Directory | Out-Null } }