[Build script] Polish powertoys build script (#40727)

<!-- 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
1. Add build parameters to build multiple types of installer
2. Add functionality to local cert management, to be able to export a
cert locally, so that the installer can be installed to other machine
3. Now the script does not need to be executed in root folder of
powertoys repo.


<!-- 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
- [X] **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
I build an installer locally, and verified packaged apps(New+,
powerRename, cmdpal/) etc can be installed successfully. And powertoys
can be installed without problem
This commit is contained in:
Kai Tao
2025-07-22 10:22:29 +08:00
committed by GitHub
parent 8479d0f084
commit ef159bcd4d
2 changed files with 99 additions and 27 deletions

View File

@@ -4,46 +4,74 @@ Build and package PowerToys (CmdPal and installer) for a specific platform and c
.DESCRIPTION .DESCRIPTION
This script automates the end-to-end build and packaging process for PowerToys, including: This script automates the end-to-end build and packaging process for PowerToys, including:
- Restoring and building all necessary solutions (CmdPal, BugReportTool, StylesReportTool, etc.) - Restoring and building all necessary solutions (CmdPal, BugReportTool, etc.)
- Cleaning up old output - Cleaning up old output
- Signing generated .msix packages - Signing generated .msix packages
- Building the WiX-based MSI and bootstrapper installers - Building the WiX-based MSI and bootstrapper installers
It is designed to work in local development. It is designed to work in local development.
The cert used to sign the packages is generated by
.PARAMETER Platform .PARAMETER Platform
Specifies the target platform for the build (e.g., 'arm64', 'x64'). Default is 'arm64'. Specifies the target platform for the build (e.g., 'arm64', 'x64'). Default is 'x64'.
.PARAMETER Configuration .PARAMETER Configuration
Specifies the build configuration (e.g., 'Debug', 'Release'). Default is 'Release'. 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).
.EXAMPLE .EXAMPLE
.\build-installer.ps1 .\build-installer.ps1
Runs the installer build pipeline for ARM64 Release (default). Runs the installer build pipeline for ARM64 Release (default).
.EXAMPLE .EXAMPLE
.\build-installer.ps1 -Platform x64 -Configuration Release .\build-installer.ps1 -Platform x64 -Configuration Release
Runs the pipeline for x64 Debug. 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.
.NOTES .NOTES
- Requires MSBuild, WiX Toolset, and Git to be installed and accessible from your environment.
- Make sure to run this script from a Developer PowerShell (e.g., VS2022 Developer PowerShell). - 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. - 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). - 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. - First time run need admin permission to trust the certificate.
- The built installer will be placed under: installer/PowerToysSetup/[Platform]/[Configuration]/UserSetup - The built installer will be placed under: installer/PowerToysSetup/[Platform]/[Configuration]/User[Machine]Setup
relative to the solution root directory. relative to the solution root directory.
- The installer can't be run right after the build, I need to copy it to another file before it can be run. - 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 ( param (
[string]$Platform = 'arm64', [string]$Platform = 'x64',
[string]$Configuration = 'Release' [string]$Configuration = 'Release',
[string]$PerUser = 'true'
) )
$repoRoot = Resolve-Path "$PSScriptRoot\..\.." # Find the PowerToys repository root automatically
Set-Location $repoRoot $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 { function RunMSBuild {
param ( param (
@@ -55,6 +83,7 @@ function RunMSBuild {
$Solution $Solution
"/p:Platform=`"$Platform`"" "/p:Platform=`"$Platform`""
"/p:Configuration=$Configuration" "/p:Configuration=$Configuration"
"/p:CIBuild=true"
'/verbosity:normal' '/verbosity:normal'
'/clp:Summary;PerformanceSummary;ErrorsOnly;WarningsOnly' '/clp:Summary;PerformanceSummary;ErrorsOnly;WarningsOnly'
'/nologo' '/nologo'
@@ -62,13 +91,18 @@ function RunMSBuild {
$cmd = $base + ($ExtraArgs -split ' ') $cmd = $base + ($ExtraArgs -split ' ')
Write-Host ("[MSBUILD] {0} {1}" -f $Solution, ($cmd -join ' ')) Write-Host ("[MSBUILD] {0} {1}" -f $Solution, ($cmd -join ' '))
& msbuild.exe @cmd
# Run MSBuild from the repository root directory
if ($LASTEXITCODE -ne 0) { Push-Location $repoRoot
Write-Error ("Build failed: {0} {1}" -f $Solution, $ExtraArgs) try {
exit $LASTEXITCODE & msbuild.exe @cmd
if ($LASTEXITCODE -ne 0) {
Write-Error ("Build failed: {0} {1}" -f $Solution, $ExtraArgs)
exit $LASTEXITCODE
}
} finally {
Pop-Location
} }
} }
function RestoreThenBuild { function RestoreThenBuild {
@@ -81,9 +115,9 @@ function RestoreThenBuild {
} }
Write-Host ("Make sure wix is installed and available") Write-Host ("Make sure wix is installed and available")
& "$PSScriptRoot\ensure-wix.ps1" & (Join-Path $PSScriptRoot "ensure-wix.ps1")
Write-Host ("[PIPELINE] Start | Platform={0} Configuration={1}" -f $Platform, $Configuration) Write-Host ("[PIPELINE] Start | Platform={0} Configuration={1} PerUser={2}" -f $Platform, $Configuration, $PerUser)
Write-Host '' Write-Host ''
$cmdpalOutputPath = Join-Path $repoRoot "$Platform\$Configuration\WinUI3Apps\CmdPal" $cmdpalOutputPath = Join-Path $repoRoot "$Platform\$Configuration\WinUI3Apps\CmdPal"
@@ -93,7 +127,7 @@ if (Test-Path $cmdpalOutputPath) {
Remove-Item $cmdpalOutputPath -Recurse -Force -ErrorAction Ignore Remove-Item $cmdpalOutputPath -Recurse -Force -ErrorAction Ignore
} }
RestoreThenBuild '.\PowerToys.sln' RestoreThenBuild 'PowerToys.sln'
$msixSearchRoot = Join-Path $repoRoot "$Platform\$Configuration" $msixSearchRoot = Join-Path $repoRoot "$Platform\$Configuration"
$msixFiles = Get-ChildItem -Path $msixSearchRoot -Recurse -Filter *.msix | $msixFiles = Get-ChildItem -Path $msixSearchRoot -Recurse -Filter *.msix |
@@ -101,22 +135,27 @@ Select-Object -ExpandProperty FullName
if ($msixFiles.Count) { if ($msixFiles.Count) {
Write-Host ("[SIGN] .msix file(s): {0}" -f ($msixFiles -join '; ')) Write-Host ("[SIGN] .msix file(s): {0}" -f ($msixFiles -join '; '))
& "$PSScriptRoot\cert-sign-package.ps1" -TargetPaths $msixFiles & (Join-Path $PSScriptRoot "cert-sign-package.ps1") -TargetPaths $msixFiles
} }
else { else {
Write-Warning "[SIGN] No .msix files found in $msixSearchRoot" Write-Warning "[SIGN] No .msix files found in $msixSearchRoot"
} }
RestoreThenBuild '.\tools\BugReportTool\BugReportTool.sln' RestoreThenBuild 'tools\BugReportTool\BugReportTool.sln'
RestoreThenBuild '.\tools\StylesReportTool\StylesReportTool.sln' RestoreThenBuild 'tools\StylesReportTool\StylesReportTool.sln'
Write-Host '[CLEAN] installer (keep *.exe)' Write-Host '[CLEAN] installer (keep *.exe)'
git clean -xfd -e '*.exe' -- .\installer\ | Out-Null 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' '/t:restore /p:RestorePackagesConfig=true'
RunMSBuild '.\installer\PowerToysSetup.sln' '/m /t:PowerToysInstaller /p:PerUser=true' RunMSBuild 'installer\PowerToysSetup.sln' "/m /t:PowerToysInstaller /p:PerUser=$PerUser"
RunMSBuild '.\installer\PowerToysSetup.sln' '/m /t:PowerToysBootstrapper /p:PerUser=true' RunMSBuild 'installer\PowerToysSetup.sln' "/m /t:PowerToysBootstrapper /p:PerUser=$PerUser"
Write-Host '[PIPELINE] Completed' Write-Host '[PIPELINE] Completed'

View File

@@ -152,4 +152,37 @@ function Export-CertificateFiles {
if (-not $CerPath -and -not $PfxPath) { if (-not $CerPath -and -not $PfxPath) {
Write-Warning "No output path specified. Nothing was exported." Write-Warning "No output path specified. Nothing was exported."
} }
}
# Main execution when script is run directly
if ($MyInvocation.InvocationName -ne '.') {
Write-Host "=== PowerToys Certificate Management ===" -ForegroundColor Green
Write-Host ""
# Ensure certificate exists and is trusted
Write-Host "Checking for existing certificate or creating new one..." -ForegroundColor Yellow
$cert = EnsureCertificate
if ($cert) {
# Export the certificate to a .cer file
$exportPath = Join-Path (Get-Location) "PowerToys-CodeSigning.cer"
Write-Host ""
Write-Host "Exporting certificate..." -ForegroundColor Yellow
Export-CertificateFiles -Certificate $cert -CerPath $exportPath
Write-Host ""
Write-Host "=== IMPORTANT NOTES ===" -ForegroundColor Red
Write-Host "The certificate has been exported to: $exportPath" -ForegroundColor White
Write-Host ""
Write-Host "To use this certificate for code signing, you need to:" -ForegroundColor Yellow
Write-Host "1. Import this certificate into 'Trusted People' store" -ForegroundColor White
Write-Host "2. Import this certificate into 'Trusted Root Certification Authorities' store" -ForegroundColor White
Write-Host "Certificate Details:" -ForegroundColor Green
Write-Host "Subject: $($cert.Subject)" -ForegroundColor White
Write-Host "Thumbprint: $($cert.Thumbprint)" -ForegroundColor White
Write-Host "Valid Until: $($cert.NotAfter)" -ForegroundColor White
} else {
Write-Error "Failed to create or find certificate. Please check the error messages above."
exit 1
}
} }