Files
PowerToys/tools/build/build-installer.ps1
Peiyao Zhao 64dc8e0f27 [Installer] Upgrade the installer from WiX3 to WiX5 (#40877)
<!-- Enter a brief description/summary of your PR here. What does it
fix/what does it change/how was it tested (even manually, if necessary)?
-->
## Summary of the Pull Request
Background: The current PowerToys installer is built using Wix3, which
has now been deprecated. To improve security, service quality, and
community support, we’re upgrading the installer to Wix5.

Implementation:
Created Wix5-based projects(PowerToysSetupVext and
PowerToysSetupCustomActionsVNext) within the installer while retaining
the existing Wix3 project. Both versions are built to generate separate
installation packages. The Wix3-related code will be removed after
successful release testing confirms no issues.

Special case:
Wix5 has removed the property for 'ShowFilesInUse'. Now, whenever a file
is in use during installation, a FilesInUse pop-upwill automatically
appear asking for the next step. To ensure this doesn't interfere with
scenarios that require silent installation (e.g. Winget method), we’ve
handled it using the bafunction approach.



<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist

- [ ] Closes: #xxx
- [ ] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [ ] **Tests:** Added/updated and all pass
- [ ] **Localization:** All end-user-facing strings can be localized
- [ ] **Dev docs:** Added/updated
- [ ] **New binaries:** Added on the required places
- [ ] [JSON for
signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json)
for new binaries
- [ ] [WXS for
installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs)
for new binaries and localization folder
- [ ] [YML for CI
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml)
for new test projects
- [ ] [YML for signed
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml)
- [ ] **Documentation updated:** If checked, please file a pull request
on [our docs
repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys)
and link it here: #xxx

<!-- Provide a more detailed description of the PR, other things fixed,
or any additional comments/features here -->
## Detailed Description of the Pull Request / Additional comments

<!-- Describe how you validated the behavior. Add automated tests
wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed

---------

Co-authored-by: Jerry Xu <n.xu@outlook.com>
Co-authored-by: Kai Tao <69313318+vanzue@users.noreply.github.com>
Co-authored-by: leileizhang <leilzh@microsoft.com>
Co-authored-by: Kai Tao (from Dev Box) <kaitao@microsoft.com>
Co-authored-by: vanzue <vanzue@outlook.com>
2025-08-25 18:39:11 +08:00

169 lines
5.8 KiB
PowerShell

<#
.SYNOPSIS
Build and package PowerToys (CmdPal and installer) for a specific platform and configuration LOCALLY.
.DESCRIPTION
This script automates the end-to-end build and packaging process for PowerToys, including:
- Restoring and building all necessary solutions (CmdPal, BugReportTool, etc.)
- Cleaning up old output
- Signing generated .msix packages
- Building the WiX v5 (VNext) MSI and bootstrapper installers
It is designed to work in local development.
The cert used to sign the packages is generated by
.PARAMETER Platform
Specifies the target platform for the build (e.g., 'arm64', 'x64'). Default is 'x64'.
.PARAMETER Configuration
Specifies the build configuration (e.g., 'Debug', 'Release'). Default is 'Release'.
.PARAMETER PerUser
Specifies whether to build a per-user installer (true) or machine-wide installer (false). Default is true (per-user).
.PARAMETER InstallerSuffix
Specifies the suffix for the installer naming (e.g., 'wix5', 'vnext'). Default is 'wix5'.
.EXAMPLE
.\build-installer.ps1
Runs the installer build pipeline for x64 Release with default suffix (wix5).
.EXAMPLE
.\build-installer.ps1 -Platform x64 -Configuration Release
Runs the pipeline for x64 Release.
.EXAMPLE
.\build-installer.ps1 -Platform x64 -Configuration Release -PerUser false
Runs the pipeline for x64 Release with machine-wide installer.
.EXAMPLE
.\build-installer.ps1 -Platform x64 -Configuration Release -InstallerSuffix vnext
Runs the pipeline for x64 Release with 'vnext' suffix.
.NOTES
- Make sure to run this script from a Developer PowerShell (e.g., VS2022 Developer PowerShell).
- Generated MSIX files will be signed using cert-sign-package.ps1.
- This script will clean previous outputs under the build directories and installer directory (except *.exe files).
- First time run need admin permission to trust the certificate.
- The built installer will be placed under: installer/PowerToysSetupVNext/[Platform]/[Configuration]/User[Machine]Setup
relative to the solution root directory.
- To run the full installation in other machines, call "./cert-management.ps1" to export the cert used to sign the packages.
And trust the cert in the target machine.
#>
param (
[string]$Platform = 'x64',
[string]$Configuration = 'Release',
[string]$PerUser = 'true',
[string]$InstallerSuffix = 'wix5'
)
# Find the PowerToys repository root automatically
$scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Definition
$repoRoot = $scriptDir
# Navigate up from the script location to find the repo root
# Script is typically in tools\build, so go up two levels
while ($repoRoot -and -not (Test-Path (Join-Path $repoRoot "PowerToys.sln"))) {
$parentDir = Split-Path -Parent $repoRoot
if ($parentDir -eq $repoRoot) {
# Reached the root of the drive, PowerToys.sln not found
Write-Error "Could not find PowerToys repository root. Make sure this script is in the PowerToys repository."
exit 1
}
$repoRoot = $parentDir
}
if (-not $repoRoot -or -not (Test-Path (Join-Path $repoRoot "PowerToys.sln"))) {
Write-Error "Could not locate PowerToys.sln. Please ensure this script is run from within the PowerToys repository."
exit 1
}
Write-Host "PowerToys repository root detected: $repoRoot"
function RunMSBuild {
param (
[string]$Solution,
[string]$ExtraArgs
)
$base = @(
$Solution
"/p:Platform=$Platform"
"/p:Configuration=$Configuration"
"/p:CIBuild=true"
'/verbosity:normal'
'/clp:Summary;PerformanceSummary;ErrorsOnly;WarningsOnly'
'/nologo'
)
$cmd = $base + ($ExtraArgs -split ' ')
Write-Host ("[MSBUILD] {0} {1}" -f $Solution, ($cmd -join ' '))
# Run MSBuild from the repository root directory
Push-Location $repoRoot
try {
& msbuild.exe @cmd
if ($LASTEXITCODE -ne 0) {
Write-Error ("Build failed: {0} {1}" -f $Solution, $ExtraArgs)
exit $LASTEXITCODE
}
} finally {
Pop-Location
}
}
function RestoreThenBuild {
param ([string]$Solution)
# 1) restore
RunMSBuild $Solution '/t:restore /p:RestorePackagesConfig=true'
# 2) build -------------------------------------------------
RunMSBuild $Solution '/m'
}
# WiX v5 projects use WixToolset.Sdk via NuGet/MSBuild; a separate WiX 3 installation is not required here.
Write-Host ("[PIPELINE] Start | Platform={0} Configuration={1} PerUser={2}" -f $Platform, $Configuration, $PerUser)
Write-Host ''
$cmdpalOutputPath = Join-Path $repoRoot "$Platform\$Configuration\WinUI3Apps\CmdPal"
if (Test-Path $cmdpalOutputPath) {
Write-Host "[CLEAN] Removing previous output: $cmdpalOutputPath"
Remove-Item $cmdpalOutputPath -Recurse -Force -ErrorAction Ignore
}
RestoreThenBuild 'PowerToys.sln'
$msixSearchRoot = Join-Path $repoRoot "$Platform\$Configuration"
$msixFiles = Get-ChildItem -Path $msixSearchRoot -Recurse -Filter *.msix |
Select-Object -ExpandProperty FullName
if ($msixFiles.Count) {
Write-Host ("[SIGN] .msix file(s): {0}" -f ($msixFiles -join '; '))
& (Join-Path $PSScriptRoot "cert-sign-package.ps1") -TargetPaths $msixFiles
}
else {
Write-Warning "[SIGN] No .msix files found in $msixSearchRoot"
}
RestoreThenBuild 'tools\BugReportTool\BugReportTool.sln'
RestoreThenBuild 'tools\StylesReportTool\StylesReportTool.sln'
Write-Host '[CLEAN] installer (keep *.exe)'
Push-Location $repoRoot
try {
git clean -xfd -e '*.exe' -- .\installer\ | Out-Null
} finally {
Pop-Location
}
RunMSBuild 'installer\PowerToysSetup.sln' '/t:restore /p:RestorePackagesConfig=true'
RunMSBuild 'installer\PowerToysSetup.sln' "/m /t:PowerToysInstallerVNext /p:PerUser=$PerUser /p:InstallerSuffix=$InstallerSuffix"
RunMSBuild 'installer\PowerToysSetup.sln' "/m /t:PowerToysBootstrapperVNext /p:PerUser=$PerUser /p:InstallerSuffix=$InstallerSuffix"
Write-Host '[PIPELINE] Completed'