mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-08-29 01:59:34 +02:00
## Summary of the Pull Request Adds a native C++ multi-call shim that exposes existing PowerToys CLIs through `PATH`. The shims are installed under the PowerToys `bin` subfolder and follow the `PowerToys.<ModuleName>.CLI.exe` naming convention. The launcher preserves the raw argument tail, shares the caller's console, and returns the target process exit code. | PATH-visible command | Target executable | | --- | --- | | `PowerToys.FancyZones.CLI.exe` | `FancyZonesCLI.exe` | | `PowerToys.ImageResizer.CLI.exe` | `WinUI3Apps/PowerToys.ImageResizerCLI.exe` | | `PowerToys.FileLocksmith.CLI.exe` | `FileLocksmithCLI.exe` | | `PowerToys.PowerDisplay.CLI.exe` | `WinUI3Apps/PowerToys.PowerDisplay.Cli.exe` | proof of this work: <img width="1044" height="294" alt="image" src="https://github.com/user-attachments/assets/b659c552-5c08-4430-85c3-eba48f286eb0" /> <img width="1137" height="244" alt="image" src="https://github.com/user-attachments/assets/5fed493f-dc30-428d-a618-bf612ccf3635" /> <img width="1727" height="868" alt="image" src="https://github.com/user-attachments/assets/5c32fd2e-4a3a-4138-b968-fb5434eebec3" /> ## PR Checklist - [x] Closes: #48634 - [x] **Communication:** Discussed with core contributors in this PR - [x] **Tests:** Added/updated and all pass - [ ] **Localization:** CLI diagnostic messages are not localized - [x] **Dev docs:** Updated CLI naming and installation conventions - [x] **New binaries:** Added on the required places - [x] Signing JSON - [x] WiX installer entries - [x] CI builds through `PowerToys.slnx`; no dedicated YML step is required - [x] The existing release pipeline covers the solution and installer - [x] **Documentation updated:** `doc/devdocs/cli-conventions.md` ## Detailed Description of the Pull Request / Additional comments - Uses one native launcher binary for all commands and resolves the target from the invoked shim filename. - Installs PATH-visible shims under `PowerToys\bin`. - Keeps the existing module CLI binaries and their deployment locations unchanged. - Rejects the previous unsuffixed and `*cli` command aliases. ## Validation Steps Performed - Built `tools/CliShim.UnitTests/CliShim.UnitTests.vcxproj` in `Release|x64`: 0 warnings, 0 errors. - Ran `CliShim.UnitTests.dll` with `vstest.console.exe`: 5/5 tests passed. - Verified the CLI manifest, WiX command names, and `bin` installation directory are synchronized. - Ran `git diff --check`. --------- Co-authored-by: Yu Leng <yuleng@microsoft.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: d11c4221-248f-44a9-85fb-7017ed43f4ce
437 lines
20 KiB
PowerShell
437 lines
20 KiB
PowerShell
<#
|
|
.SYNOPSIS
|
|
Build and package PowerToys (CmdPal and installer) for a specific platform and configuration LOCALLY.
|
|
|
|
.DESCRIPTION
|
|
Builds and packages PowerToys (CmdPal and installer) locally. Handles solution build, signing, and WiX installer generation.
|
|
|
|
.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).
|
|
|
|
.EXAMPLE
|
|
.\build-installer.ps1
|
|
Runs the installer build pipeline for x64 Release.
|
|
|
|
.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.
|
|
|
|
.NOTES
|
|
- Generated MSIX files will be signed using cert-sign-package.ps1.
|
|
- If the working tree is not clean, the script will prompt before continuing (use -Force to skip the prompt).
|
|
- Use the -Clean parameter to clean build outputs (bin/obj) and MSBuild outputs.
|
|
- 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]$Version,
|
|
[switch]$Force,
|
|
[switch]$EnableCmdPalAOT,
|
|
[switch]$Clean,
|
|
[switch]$SkipBuild,
|
|
[switch]$Help
|
|
)
|
|
|
|
if ($Help) {
|
|
Write-Host "Usage: .\build-installer.ps1 [-Platform <x64|arm64>] [-Configuration <Release|Debug>] [-PerUser <true|false>] [-Version <0.0.1>] [-Force] [-EnableCmdPalAOT] [-Clean] [-SkipBuild]"
|
|
Write-Host " -Platform Target platform (default: auto-detect or x64)"
|
|
Write-Host " -Configuration Build configuration (default: Release)"
|
|
Write-Host " -PerUser Build per-user installer (default: true)"
|
|
Write-Host " -Version Sets the PowerToys version (default: from src\Version.props)"
|
|
Write-Host " -Force Continue even if the git working tree is not clean (skips the interactive prompt)."
|
|
Write-Host " -EnableCmdPalAOT Enable AOT compilation for CmdPal (slower build)"
|
|
Write-Host " -Clean Clean output directories before building"
|
|
Write-Host " -SkipBuild Skip building the main solution and tools (assumes they are already built)"
|
|
Write-Host " -Help Show this help message"
|
|
exit 0
|
|
}
|
|
|
|
# Ensure helpers are available
|
|
. "$PSScriptRoot\build-common.ps1"
|
|
|
|
# Initialize Visual Studio dev environment
|
|
if (-not (Ensure-VsDevEnvironment)) { exit 1 }
|
|
|
|
# Auto-detect platform when not provided
|
|
if (-not $Platform -or $Platform -eq '') {
|
|
try {
|
|
$Platform = Get-DefaultPlatform
|
|
Write-Host ("[AUTO-PLATFORM] Detected platform: {0}" -f $Platform)
|
|
} catch {
|
|
Write-Warning "Failed to auto-detect platform; defaulting to x64"
|
|
$Platform = 'x64'
|
|
}
|
|
}
|
|
|
|
# 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.slnx"))) {
|
|
$parentDir = Split-Path -Parent $repoRoot
|
|
if ($parentDir -eq $repoRoot) {
|
|
# Reached the root of the drive, PowerToys.slnx 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.slnx"))) {
|
|
Write-Error "Could not locate PowerToys.slnx. Please ensure this script is run from within the PowerToys repository."
|
|
exit 1
|
|
}
|
|
|
|
Write-Host "PowerToys repository root detected: $repoRoot"
|
|
|
|
# Safety check: avoid mixing build outputs with existing local changes unless the user confirms.
|
|
if (-not $Force) {
|
|
Push-Location $repoRoot
|
|
try {
|
|
$gitStatus = $null
|
|
$gitRelevantStatus = @()
|
|
try {
|
|
$gitStatus = git status --porcelain=v1 --untracked-files=all --ignore-submodules=all
|
|
} catch {
|
|
Write-Warning ("[GIT] Failed to query git status: {0}" -f $_.Exception.Message)
|
|
}
|
|
|
|
if ($gitStatus -and $gitStatus.Length -gt 0) {
|
|
foreach ($line in $gitStatus) {
|
|
if (-not $line) { continue }
|
|
|
|
# Porcelain v1 format: XY <path>
|
|
# We only care about changes that affect the working tree (Y != ' ') or untracked files (??).
|
|
# Index-only changes (staged, Y == ' ') are ignored per user request.
|
|
if ($line.StartsWith('??')) {
|
|
$gitRelevantStatus += $line
|
|
continue
|
|
}
|
|
|
|
if ($line.StartsWith('!!')) {
|
|
continue
|
|
}
|
|
|
|
if ($line.Length -ge 2) {
|
|
$workTreeStatus = $line[1]
|
|
if ($workTreeStatus -ne ' ') {
|
|
$gitRelevantStatus += $line
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if ($gitRelevantStatus.Count -gt 0) {
|
|
Write-Warning "[GIT] Working tree is NOT clean."
|
|
Write-Warning "[GIT] This build will generate untracked files and may modify tracked files, which can mix with your current changes."
|
|
Write-Host "[GIT] Unstaged/untracked status (first 50 lines):"
|
|
$gitRelevantStatus | Select-Object -First 50 | ForEach-Object { Write-Host (" {0}" -f $_) }
|
|
|
|
$shouldContinue = $false
|
|
try {
|
|
$choices = [System.Management.Automation.Host.ChoiceDescription[]]@(
|
|
(New-Object System.Management.Automation.Host.ChoiceDescription "&Yes", "Continue the build."),
|
|
(New-Object System.Management.Automation.Host.ChoiceDescription "&No", "Cancel the build.")
|
|
)
|
|
$decision = $Host.UI.PromptForChoice("Working tree not clean", "Continue anyway?", $choices, 1)
|
|
$shouldContinue = ($decision -eq 0)
|
|
} catch {
|
|
Write-Warning "[GIT] Interactive prompt not available."
|
|
Write-Error "Refusing to proceed with a dirty working tree. Re-run with -Force to continue anyway."
|
|
exit 1
|
|
}
|
|
|
|
if (-not $shouldContinue) {
|
|
Write-Host "[GIT] Cancelled by user."
|
|
exit 1
|
|
}
|
|
}
|
|
} finally {
|
|
Pop-Location
|
|
}
|
|
}
|
|
|
|
$cmdpalOutputPath = Join-Path $repoRoot "$Platform\$Configuration\WinUI3Apps\CmdPal"
|
|
$buildOutputPath = Join-Path $repoRoot "$Platform\$Configuration"
|
|
|
|
# Clean should be done first before any other steps
|
|
if ($Clean) {
|
|
if (Test-Path $cmdpalOutputPath) {
|
|
Write-Host "[CLEAN] Removing previous output: $cmdpalOutputPath"
|
|
Remove-Item $cmdpalOutputPath -Recurse -Force -ErrorAction Ignore
|
|
}
|
|
if (Test-Path $buildOutputPath) {
|
|
Write-Host "[CLEAN] Removing previous build output: $buildOutputPath"
|
|
Remove-Item $buildOutputPath -Recurse -Force -ErrorAction Ignore
|
|
}
|
|
|
|
Write-Host "[CLEAN] Cleaning solution (msbuild /t:Clean)..."
|
|
RunMSBuild 'PowerToys.slnx' '/t:Clean' $Platform $Configuration
|
|
}
|
|
|
|
try {
|
|
if ($Version) {
|
|
Write-Host "[VERSION] Setting PowerToys version to $Version using versionSetting.ps1..."
|
|
$versionScript = Join-Path $repoRoot ".pipelines\versionSetting.ps1"
|
|
if (Test-Path $versionScript) {
|
|
& $versionScript -versionNumber $Version -DevEnvironment 'Local'
|
|
if (-not $?) {
|
|
Write-Error "versionSetting.ps1 failed"
|
|
exit 1
|
|
}
|
|
} else {
|
|
Write-Error "Could not find versionSetting.ps1 at: $versionScript"
|
|
exit 1
|
|
}
|
|
}
|
|
|
|
Write-Host "[VERSION] Setting up versioning using Microsoft.Windows.Terminal.Versioning..."
|
|
|
|
# Check for nuget.exe - download to AppData if not available
|
|
$nugetDownloaded = $false
|
|
$nugetPath = $null
|
|
if (-not (Get-Command nuget -ErrorAction SilentlyContinue)) {
|
|
Write-Warning "nuget.exe not found in PATH. Attempting to download..."
|
|
$nugetUrl = "https://dist.nuget.org/win-x86-commandline/latest/nuget.exe"
|
|
$nugetDir = Join-Path $env:LOCALAPPDATA "PowerToys\BuildTools"
|
|
if (-not (Test-Path $nugetDir)) { New-Item -ItemType Directory -Path $nugetDir -Force | Out-Null }
|
|
$nugetPath = Join-Path $nugetDir "nuget.exe"
|
|
if (-not (Test-Path $nugetPath)) {
|
|
try {
|
|
Invoke-WebRequest $nugetUrl -OutFile $nugetPath
|
|
$nugetDownloaded = $true
|
|
} catch {
|
|
Write-Error "Failed to download nuget.exe. Please install it manually and add to PATH."
|
|
exit 1
|
|
}
|
|
}
|
|
$env:Path += ";$nugetDir"
|
|
}
|
|
|
|
# Install Terminal versioning package to AppData
|
|
$versioningDir = Join-Path $env:LOCALAPPDATA "PowerToys\BuildTools\.versioning"
|
|
if (-not (Test-Path $versioningDir)) { New-Item -ItemType Directory -Path $versioningDir -Force | Out-Null }
|
|
|
|
$configFile = Join-Path $repoRoot ".pipelines\release-nuget.config"
|
|
|
|
# Install the package
|
|
# Use -ExcludeVersion to make the path predictable
|
|
nuget install Microsoft.Windows.Terminal.Versioning -ConfigFile $configFile -OutputDirectory $versioningDir -ExcludeVersion -NonInteractive
|
|
|
|
$versionRoot = Join-Path $versioningDir "Microsoft.Windows.Terminal.Versioning"
|
|
$setupScript = Join-Path $versionRoot "build\Setup.ps1"
|
|
|
|
if (Test-Path $setupScript) {
|
|
& $setupScript -ProjectDirectory (Join-Path $repoRoot "src\modules\cmdpal") -Verbose
|
|
} else {
|
|
Write-Error "Could not find Setup.ps1 in $versionRoot"
|
|
}
|
|
|
|
# WiX v5 projects use WixToolset.Sdk via NuGet/MSBuild; no separate WiX installation is required.
|
|
Write-Host ("[PIPELINE] Start | Platform={0} Configuration={1} PerUser={2}" -f $Platform, $Configuration, $PerUser)
|
|
Write-Host ''
|
|
|
|
$commonArgs = '/p:CIBuild=true /p:IsPipeline=true'
|
|
|
|
if ($EnableCmdPalAOT) {
|
|
$commonArgs += " /p:EnableCmdPalAOT=true"
|
|
}
|
|
|
|
# No local projects found (or continuing) - build full solution and tools
|
|
if (-not $SkipBuild) {
|
|
RestoreThenBuild 'PowerToys.slnx' $commonArgs $Platform $Configuration
|
|
}
|
|
|
|
$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"
|
|
}
|
|
|
|
# Generate DSC v2 manifests (PowerToys.Settings.DSC.Schema.Generator)
|
|
# The csproj PostBuild event is skipped on ARM64, so we run it manually here if needed.
|
|
if ($Platform -eq 'arm64') {
|
|
Write-Host "[DSC] Manually generating DSC v2 manifests for ARM64..."
|
|
|
|
# 1. Get Version
|
|
$versionPropsPath = Join-Path $repoRoot "src\Version.props"
|
|
[xml]$versionProps = Get-Content $versionPropsPath
|
|
$ptVersion = $versionProps.Project.PropertyGroup.Version
|
|
# Package versions need four components. Preserve explicit preview build components.
|
|
$ptVersionFull = if (($ptVersion.ToCharArray() | Where-Object { $_ -eq '.' }).Count -eq 2) { "$ptVersion.0" } else { $ptVersion }
|
|
|
|
# 2. Build the Generator
|
|
$generatorProj = Join-Path $repoRoot "src\dsc\PowerToys.Settings.DSC.Schema.Generator\PowerToys.Settings.DSC.Schema.Generator.csproj"
|
|
RunMSBuild $generatorProj "/t:Build" $Platform $Configuration
|
|
|
|
# 3. Define paths
|
|
# The generator output path is in the project's bin folder
|
|
$generatorExe = Join-Path $repoRoot "src\dsc\PowerToys.Settings.DSC.Schema.Generator\bin\$Platform\$Configuration\PowerToys.Settings.DSC.Schema.Generator.exe"
|
|
|
|
if (-not (Test-Path $generatorExe)) {
|
|
Write-Warning "Could not find generator at expected path: $generatorExe"
|
|
Write-Warning "Searching in build output..."
|
|
$found = Get-ChildItem -Path (Join-Path $repoRoot "$Platform\$Configuration") -Filter "PowerToys.Settings.DSC.Schema.Generator.exe" -Recurse | Select-Object -First 1
|
|
if ($found) {
|
|
$generatorExe = $found.FullName
|
|
}
|
|
}
|
|
|
|
$settingsLibDll = Join-Path $repoRoot "$Platform\$Configuration\WinUI3Apps\PowerToys.Settings.UI.Lib.dll"
|
|
|
|
$dscGenDir = Join-Path $repoRoot "src\dsc\Microsoft.PowerToys.Configure\Generated\Microsoft.PowerToys.Configure\$ptVersionFull"
|
|
if (-not (Test-Path $dscGenDir)) {
|
|
New-Item -ItemType Directory -Path $dscGenDir -Force | Out-Null
|
|
}
|
|
|
|
$outPsm1 = Join-Path $dscGenDir "Microsoft.PowerToys.Configure.psm1"
|
|
$outPsd1 = Join-Path $dscGenDir "Microsoft.PowerToys.Configure.psd1"
|
|
|
|
# 4. Run Generator
|
|
if (Test-Path $generatorExe) {
|
|
Write-Host "[DSC] Executing: $generatorExe"
|
|
|
|
$generatorDir = Split-Path -Parent $generatorExe
|
|
$winUI3AppsDir = Join-Path $repoRoot "$Platform\$Configuration\WinUI3Apps"
|
|
|
|
# Copy dependencies from WinUI3Apps to Generator directory to satisfy WinRT/WinUI3 dependencies
|
|
# This avoids "Class not registered" errors without polluting the WinUI3Apps directory which is used for packaging.
|
|
if (Test-Path $winUI3AppsDir) {
|
|
Write-Host "[DSC] Copying dependencies from $winUI3AppsDir to $generatorDir"
|
|
Get-ChildItem -Path $winUI3AppsDir -Filter "*.dll" | ForEach-Object {
|
|
$destPath = Join-Path $generatorDir $_.Name
|
|
if (-not (Test-Path $destPath)) {
|
|
Copy-Item -Path $_.FullName -Destination $destPath -Force
|
|
}
|
|
}
|
|
# Also copy resources.pri if it exists, as it might be needed for resource lookup
|
|
$priFile = Join-Path $winUI3AppsDir "resources.pri"
|
|
if (Test-Path $priFile) {
|
|
Copy-Item -Path $priFile -Destination $generatorDir -Force
|
|
}
|
|
}
|
|
|
|
Push-Location $generatorDir
|
|
try {
|
|
# Now we can use the local DLLs
|
|
$localSettingsLibDll = Join-Path $generatorDir "PowerToys.Settings.UI.Lib.dll"
|
|
|
|
if (Test-Path $localSettingsLibDll) {
|
|
Write-Host "[DSC] Using local DLL: $localSettingsLibDll"
|
|
& $generatorExe $localSettingsLibDll $outPsm1 $outPsd1
|
|
} else {
|
|
# Fallback (shouldn't happen if copy succeeded or build was correct)
|
|
Write-Warning "[DSC] Local DLL not found, falling back to: $settingsLibDll"
|
|
& $generatorExe $settingsLibDll $outPsm1 $outPsd1
|
|
}
|
|
|
|
if ($LASTEXITCODE -ne 0) {
|
|
Write-Error "DSC v2 generation failed with exit code $LASTEXITCODE"
|
|
exit 1
|
|
}
|
|
} finally {
|
|
Pop-Location
|
|
}
|
|
|
|
Write-Host "[DSC] DSC v2 manifests generated successfully."
|
|
} else {
|
|
Write-Error "Could not find generator executable at $generatorExe"
|
|
exit 1
|
|
}
|
|
}
|
|
|
|
# Generate DSC manifest files
|
|
Write-Host '[DSC] Generating DSC manifest files...'
|
|
$dscScriptPath = Join-Path $repoRoot '.\tools\build\generate-dsc-manifests.ps1'
|
|
if (Test-Path $dscScriptPath) {
|
|
& $dscScriptPath -BuildPlatform $Platform -BuildConfiguration $Configuration -RepoRoot $repoRoot
|
|
if ($LASTEXITCODE -ne 0) {
|
|
Write-Error "DSC manifest generation failed with exit code $LASTEXITCODE"
|
|
exit 1
|
|
}
|
|
Write-Host '[DSC] DSC manifest files generated successfully'
|
|
} else {
|
|
Write-Warning "[DSC] DSC manifest generator script not found at: $dscScriptPath"
|
|
}
|
|
|
|
if (-not $SkipBuild) {
|
|
RestoreThenBuild 'tools\BugReportTool\BugReportTool.sln' $commonArgs $Platform $Configuration
|
|
RestoreThenBuild 'tools\StylesReportTool\StylesReportTool.sln' $commonArgs $Platform $Configuration
|
|
}
|
|
|
|
$cliShimPath = Join-Path $buildOutputPath 'CliShim\PowerToys.CliShim.exe'
|
|
if (-not (Test-Path -LiteralPath $cliShimPath -PathType Leaf)) {
|
|
Write-Error "CLI shim not found at '$cliShimPath'. Build PowerToys.slnx (tools\CliShim\CliShim.vcxproj) before building the installer."
|
|
exit 1
|
|
}
|
|
|
|
# Nothing else validates the target half of the shim manifest: CliShim.vcxproj only compares
|
|
# command names against CliShims.wxs, and the launcher tests fabricate their target at whatever
|
|
# path the manifest declares, so they cannot falsify it. The target CLIs are harvested into the
|
|
# MSI by directory glob rather than authored by hand, so moving or renaming one leaves every
|
|
# other gate green and ships a shim that exits 9010 for every user. This is the first point where
|
|
# the whole product tree exists, so resolve each mapping here exactly the way the shim will at
|
|
# run time - relative to its own bin folder - against the layout the build output mirrors.
|
|
$cliShimBinDir = Join-Path $buildOutputPath 'bin'
|
|
$cliShimManifest = [xml](Get-Content -LiteralPath (Join-Path $repoRoot 'tools\CliShim\CliShimManifest.props') -Raw)
|
|
foreach ($shim in $cliShimManifest.Project.ItemGroup.CliShim) {
|
|
$resolvedTarget = [System.IO.Path]::GetFullPath([System.IO.Path]::Combine($cliShimBinDir, $shim.RelativeTarget))
|
|
if (-not (Test-Path -LiteralPath $resolvedTarget -PathType Leaf)) {
|
|
Write-Error "CLI shim '$($shim.Include)' maps to '$($shim.RelativeTarget)', which resolves to '$resolvedTarget' and does not exist. Update tools\CliShim\CliShimManifest.props to match where that CLI is built."
|
|
exit 1
|
|
}
|
|
}
|
|
|
|
# Set NUGET_PACKAGES environment variable if not set, to help wixproj find heat.exe
|
|
if (-not $env:NUGET_PACKAGES) {
|
|
$env:NUGET_PACKAGES = Join-Path $env:USERPROFILE ".nuget\packages"
|
|
Write-Host "[ENV] Set NUGET_PACKAGES to $env:NUGET_PACKAGES"
|
|
}
|
|
|
|
RunMSBuild 'installer\PowerToysSetup.slnx' "$commonArgs /t:restore /p:RestorePackagesConfig=true" $Platform $Configuration
|
|
|
|
RunMSBuild 'installer\PowerToysSetup.slnx' "$commonArgs /m /t:PowerToysInstallerVNext /p:PerUser=$PerUser" $Platform $Configuration
|
|
|
|
# Fix: WiX v5 locally puts the MSI in an 'en-us' subfolder, but the Bootstrapper expects it in the root of UserSetup/MachineSetup.
|
|
# We move it up one level to match expectations.
|
|
$setupType = if ($PerUser -eq 'true') { 'UserSetup' } else { 'MachineSetup' }
|
|
$msiParentDir = Join-Path $repoRoot "installer\PowerToysSetupVNext\$Platform\$Configuration\$setupType"
|
|
$msiEnUsDir = Join-Path $msiParentDir "en-us"
|
|
|
|
if (Test-Path $msiEnUsDir) {
|
|
Write-Host "[FIX] Moving MSI files from $msiEnUsDir to $msiParentDir"
|
|
Get-ChildItem -Path $msiEnUsDir -Filter *.msi | Move-Item -Destination $msiParentDir -Force
|
|
}
|
|
|
|
RunMSBuild 'installer\PowerToysSetup.slnx' "$commonArgs /m /t:PowerToysBootstrapperVNext /p:PerUser=$PerUser" $Platform $Configuration
|
|
|
|
} finally {
|
|
# No git cleanup; leave workspace state as-is.
|
|
}
|
|
|
|
Write-Host '[PIPELINE] Completed'
|