<# .SYNOPSIS Geopro Windows 安装包一键打包工具。 .DESCRIPTION 把已构建的 geopro_desktop 部署目录打包成单个 Inno Setup 安装程序: 1) stage —— 将 build/release/src/app 复制到 installer\staging,剔除构建产物 (CMakeFiles / *_autogen / *.pdb / *.log / *.cmake) 2) deploy —— 在 staging 上跑 windeployqt 补齐 Qt 运行时缺件 (D3Dcompiler_47 / opengl32sw / WebEngine QML / 各插件)。 自动绕过 ADS 卡死问题(qt6advanceddocking.dll 被误判为 Qt 模块)。 3) redist —— 确保 VC++ 运行时安装器 vc_redist.x64.exe 就位(缺则从 VS 复制) 4) compile —— 调用 ISCC 编译 geopro.iss,输出到 installer\dist\ 工具链(Qt / ISCC / VS 的 vc_redist)全部自动定位,便于换机复用。 .PARAMETER Version 产品版本号(默认 3.0.0)。最终文件名为 Geopro_Setup_-.exe。 .PARAMETER Rebuild 先执行 build.bat rebuild 做一次干净重编,再打包。默认使用现有构建产物。 .PARAMETER SkipDeploy 跳过 windeployqt 步骤(仅当确认 staging 已补齐时使用,不推荐)。 .PARAMETER QtPrefix Qt 安装前缀。默认从 CMakePresets.json 的 CMAKE_PREFIX_PATH 解析。 .EXAMPLE powershell -ExecutionPolicy Bypass -File installer\build_installer.ps1 .EXAMPLE powershell -ExecutionPolicy Bypass -File installer\build_installer.ps1 -Version 3.1.0 -Rebuild #> [CmdletBinding()] param( [string]$Version = '3.0.0', [switch]$Rebuild, [switch]$SkipDeploy, [string]$QtPrefix ) $ErrorActionPreference = 'Stop' $InstallerDir = $PSScriptRoot $RepoRoot = Split-Path $InstallerDir -Parent $BuildAppDir = Join-Path $RepoRoot 'build\release\src\app' $StageDir = Join-Path $InstallerDir 'staging' $RedistDir = Join-Path $InstallerDir 'redist' $DistDir = Join-Path $InstallerDir 'dist' $IssFile = Join-Path $InstallerDir 'geopro.iss' $ExeName = 'geopro_desktop.exe' $BuildDate = Get-Date -Format 'yyyyMMdd' function Info($m){ Write-Host "[pack] $m" -ForegroundColor Cyan } function Warn($m){ Write-Host "[pack] $m" -ForegroundColor Yellow } function Die($m){ Write-Host "[pack] ERROR: $m" -ForegroundColor Red; exit 1 } # --- 0. 可选:干净重编 ------------------------------------------------------- if ($Rebuild) { Info '执行 build.bat rebuild(干净重编)...' & (Join-Path $RepoRoot 'build.bat') rebuild if ($LASTEXITCODE -ne 0) { Die "build.bat rebuild 失败 (exit $LASTEXITCODE)" } } # --- 1. 定位 Qt / windeployqt ---------------------------------------------- if (-not $QtPrefix) { $presets = Join-Path $RepoRoot 'CMakePresets.json' if (Test-Path $presets) { try { $j = Get-Content $presets -Raw | ConvertFrom-Json foreach ($p in $j.configurePresets) { if ($p.cacheVariables.CMAKE_PREFIX_PATH) { $QtPrefix = $p.cacheVariables.CMAKE_PREFIX_PATH; break } } } catch { } } } if (-not $QtPrefix) { $QtPrefix = 'D:/Qt/6.11.1/msvc2022_64' } $QtBin = Join-Path ($QtPrefix -replace '/','\') 'bin' $WinDeploy = Join-Path $QtBin 'windeployqt.exe' # --- 2. 定位 ISCC ----------------------------------------------------------- $IsccCandidates = @( "$env:LOCALAPPDATA\Programs\Inno Setup 6\ISCC.exe", "${env:ProgramFiles(x86)}\Inno Setup 6\ISCC.exe", "$env:ProgramFiles\Inno Setup 6\ISCC.exe" ) $Iscc = $IsccCandidates | Where-Object { Test-Path $_ } | Select-Object -First 1 if (-not $Iscc) { $Iscc = (Get-Command ISCC.exe -ErrorAction SilentlyContinue).Source } if (-not $Iscc) { Die '未找到 Inno Setup (ISCC.exe)。请先安装:winget install --id JRSoftware.InnoSetup -e' } # --- 3. 校验构建产物 -------------------------------------------------------- $BuiltExe = Join-Path $BuildAppDir $ExeName if (-not (Test-Path $BuiltExe)) { Die "未找到构建产物 $BuiltExe`n请先构建:build.bat app(或加 -Rebuild 参数)" } Info "构建产物: $BuiltExe ($([math]::Round((Get-Item $BuiltExe).Length/1MB,2)) MB, 修改于 $((Get-Item $BuiltExe).LastWriteTime))" # --- 4. stage:复制部署目录、剔除构建产物 ----------------------------------- Info 'stage 部署副本(剔除 CMakeFiles / *_autogen / *.pdb / *.log / *.cmake)...' if (Test-Path $StageDir) { Remove-Item $StageDir -Recurse -Force } New-Item -ItemType Directory -Force $StageDir | Out-Null robocopy $BuildAppDir $StageDir /E ` /XD CMakeFiles geopro_desktop_autogen ` /XF *.pdb *.log cmake_install.cmake *.ilk *.exp ` /NFL /NDL /NJH /NJS /MT:8 | Out-Null if ($LASTEXITCODE -ge 8) { Die "robocopy 失败 (exit $LASTEXITCODE)" } # --- 5. windeployqt 补齐 Qt 运行时(绕过 ADS 卡死) ------------------------- if (-not $SkipDeploy) { if (-not (Test-Path $WinDeploy)) { Die "未找到 windeployqt: $WinDeploy(用 -QtPrefix 指定 Qt 路径)" } Info 'windeployqt 补齐 Qt 运行时缺件...' # qt6advanceddocking.dll 名字带 qt6 前缀,windeployqt 会误当 Qt 模块去 Qt\bin 找它并报错中止。 # 临时把它拷进 Qt\bin 让 windeployqt 能读其依赖(实为 Qt6Core/Gui/Widgets),跑完即删。 $adsName = 'qt6advanceddocking.dll' $adsTmp = Join-Path $QtBin $adsName $adsPreexisted = Test-Path $adsTmp if (-not $adsPreexisted) { Copy-Item (Join-Path $StageDir $adsName) $adsTmp -Force } try { & $WinDeploy --release --no-translations --compiler-runtime (Join-Path $StageDir $ExeName) | Out-Null if ($LASTEXITCODE -ne 0) { Die "windeployqt 失败 (exit $LASTEXITCODE)" } } finally { if (-not $adsPreexisted) { Remove-Item $adsTmp -Force -ErrorAction SilentlyContinue } } } # --- 6. VC++ 运行时安装器就位 ----------------------------------------------- New-Item -ItemType Directory -Force $RedistDir | Out-Null $VcRedist = Join-Path $RedistDir 'vc_redist.x64.exe' if (-not (Test-Path $VcRedist)) { Info 'vc_redist.x64.exe 缺失,从 Visual Studio 复制...' $vswhere = "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe" $vsPath = & $vswhere -all -prerelease -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath | Select-Object -Last 1 $found = Get-ChildItem (Join-Path $vsPath 'VC\Redist') -Filter 'vc_redist.x64.exe' -Recurse -ErrorAction SilentlyContinue | Sort-Object FullName -Descending | Select-Object -First 1 if (-not $found) { Die '未找到 vc_redist.x64.exe,请手动放入 installer\redist\' } Copy-Item $found.FullName $VcRedist -Force } # --- 7. ISCC 编译 ----------------------------------------------------------- New-Item -ItemType Directory -Force $DistDir | Out-Null $stageMB = [math]::Round((Get-ChildItem $StageDir -Recurse -File | Measure-Object Length -Sum).Sum/1MB,1) Info "staging 载荷 $stageMB MB,开始用 Inno Setup 编译安装包..." & $Iscc "/DAppVersion=$Version" "/DBuildDate=$BuildDate" $IssFile if ($LASTEXITCODE -ne 0) { Die "ISCC 编译失败 (exit $LASTEXITCODE)" } # --- 8. 收尾报告 ------------------------------------------------------------ $out = Join-Path $DistDir "Geopro_Setup_$Version-$BuildDate.exe" if (Test-Path $out) { Info '打包完成 ✓' Write-Host " 安装包: $out" Write-Host " 大小 : $([math]::Round((Get-Item $out).Length/1MB,1)) MB" } else { Warn "ISCC 返回成功,但未找到预期产物 $out(检查 installer\dist\)" }