@@ -2039,6 +2039,177 @@ function Get-NugetSemanticVersion
20392039 $packageSemanticVersion
20402040}
20412041
2042+ # Get the paths to various WiX tools
2043+ function Get-WixPath
2044+ {
2045+ # # AppVeyor base image might update the version for Wix. Hence, we should
2046+ # # not hard code version numbers.
2047+ $wixToolsetBinPath = " ${env: ProgramFiles(x86)} \WiX Toolset *\bin"
2048+
2049+ Write-Verbose " Ensure Wix Toolset is present on the machine @ $wixToolsetBinPath "
2050+ if (-not (Test-Path $wixToolsetBinPath ))
2051+ {
2052+ throw " The latest version of Wix Toolset 3.11 is required to create MSI package. Please install it from https://github.com/wixtoolset/wix3/releases"
2053+ }
2054+
2055+ # # Get the latest if multiple versions exist.
2056+ $wixToolsetBinPath = (Get-ChildItem $wixToolsetBinPath ).FullName | Sort-Object - Descending | Select-Object - First 1
2057+
2058+ Write-Verbose " Initialize Wix executables..."
2059+ $wixHeatExePath = Join-Path $wixToolsetBinPath " heat.exe"
2060+ $wixMeltExePath = Join-Path $wixToolsetBinPath " melt.exe"
2061+ $wixTorchExePath = Join-Path $wixToolsetBinPath " torch.exe"
2062+ $wixPyroExePath = Join-Path $wixToolsetBinPath " pyro.exe"
2063+ $wixCandleExePath = Join-Path $wixToolsetBinPath " Candle.exe"
2064+ $wixLightExePath = Join-Path $wixToolsetBinPath " Light.exe"
2065+
2066+ return [PSCustomObject ] @ {
2067+ WixHeatExePath = $wixHeatExePath
2068+ WixMeltExePath = $wixMeltExePath
2069+ WixTorchExePath = $wixTorchExePath
2070+ WixPyroExePath = $wixPyroExePath
2071+ WixCandleExePath = $wixCandleExePath
2072+ WixLightExePath = $wixLightExePath
2073+ }
2074+
2075+ }
2076+
2077+ <#
2078+ . Synopsis
2079+ Creates a Windows installer MSP package from two MSIs and WIXPDB files
2080+ This only works on a Windows machine due to the usage of WiX.
2081+ . EXAMPLE
2082+ # This example shows how to produce a x64 patch from 6.0.2 to a theoretical 6.0.3
2083+ cd $RootPathOfPowerShellRepo
2084+ Import-Module .\build.psm1; Import-Module .\tools\packaging\packaging.psm1
2085+ New-MSIPatch -NewVersion 6.0.1 -BaselineMsiPath .\PowerShell-6.0.2-win-x64.msi -BaselineWixPdbPath .\PowerShell-6.0.2-win-x64.wixpdb -PatchMsiPath .\PowerShell-6.0.3-win-x64.msi -PatchWixPdbPath .\PowerShell-6.0.3-win-x64.wixpdb
2086+ #>
2087+ function New-MSIPatch
2088+ {
2089+ param (
2090+ [Parameter (Mandatory , HelpMessage = ' The version of the fixed or patch MSI.' )]
2091+ [ValidatePattern (" ^\d+\.\d+\.\d+$" )]
2092+ [string ] $NewVersion ,
2093+
2094+ [Parameter (Mandatory , HelpMessage = ' The path to the original or baseline MSI.' )]
2095+ [ValidateNotNullOrEmpty ()]
2096+ [ValidateScript ( {(Test-Path $_ ) -and $_ -like ' *.msi' })]
2097+ [string ] $BaselineMsiPath ,
2098+
2099+ [Parameter (Mandatory , HelpMessage = ' The path to the WIXPDB for the original or baseline MSI.' )]
2100+ [ValidateNotNullOrEmpty ()]
2101+ [ValidateScript ( {(Test-Path $_ ) -and $_ -like ' *.wixpdb' })]
2102+ [string ] $BaselineWixPdbPath ,
2103+
2104+ [Parameter (Mandatory , HelpMessage = ' The path to the fixed or patch MSI.' )]
2105+ [ValidateNotNullOrEmpty ()]
2106+ [ValidateScript ( {(Test-Path $_ ) -and $_ -like ' *.msi' })]
2107+ [string ] $PatchMsiPath ,
2108+
2109+ [Parameter (Mandatory , HelpMessage = ' The path to the WIXPDB for the fixed or patch MSI.' )]
2110+ [ValidateNotNullOrEmpty ()]
2111+ [ValidateScript ( {(Test-Path $_ ) -and $_ -like ' *.wixpdb' })]
2112+ [string ] $PatchWixPdbPath ,
2113+
2114+ [Parameter (HelpMessage = ' Path to the patch template WXS. Usually you do not need to specify this' )]
2115+ [ValidateNotNullOrEmpty ()]
2116+ [ValidateScript ( {Test-Path $_ })]
2117+ [string ] $PatchWxsPath = " $PSScriptRoot \..\..\assets\patch-template.wxs" ,
2118+
2119+ [Parameter (HelpMessage = ' Produce a delta patch instead of a full patch. Usually not worth it.' )]
2120+ [switch ] $Delta
2121+ )
2122+
2123+ $mspName = (Split-Path - Path $PatchMsiPath - Leaf).Replace(' .msi' , ' .fullpath.msp' )
2124+ $mspDeltaName = (Split-Path - Path $PatchMsiPath - Leaf).Replace(' .msi' , ' .deltapatch.msp' )
2125+
2126+ $wixPatchXmlPath = Join-Path $env: Temp " patch.wxs"
2127+ $wixBaselineOriginalPdbPath = Join-Path $env: Temp " baseline.original.wixpdb"
2128+ $wixBaselinePdbPath = Join-Path $env: Temp " baseline.wixpdb"
2129+ $wixBaselineBinariesPath = Join-Path $env: Temp " baseline.binaries"
2130+ $wixPatchOriginalPdbPath = Join-Path $env: Temp " patch.original.wixpdb"
2131+ $wixPatchPdbPath = Join-Path $env: Temp " patch.wixpdb"
2132+ $wixPatchBinariesPath = Join-Path $env: Temp " patch.binaries"
2133+ $wixPatchMstPath = Join-Path $env: Temp " patch.wixmst"
2134+ $wixPatchObjPath = Join-Path $env: Temp " patch.wixobj"
2135+ $wixPatchWixMspPath = Join-Path $env: Temp " patch.wixmsp"
2136+
2137+ $filesToCleanup = @ (
2138+ $wixPatchXmlPath
2139+ $wixBaselinePdbPath
2140+ $wixBaselineBinariesPath
2141+ $wixPatchPdbPath
2142+ $wixPatchBinariesPath
2143+ $wixPatchMstPath
2144+ $wixPatchObjPath
2145+ $wixPatchWixMspPath
2146+ $wixPatchOriginalPdbPath
2147+ $wixBaselineOriginalPdbPath
2148+ )
2149+
2150+ # cleanup from previous builds
2151+ Remove-Item - Path $filesToCleanup - Force - Recurse - ErrorAction SilentlyContinue
2152+
2153+ # Melt changes the original, so copy before running melt
2154+ Copy-Item - Path $BaselineWixPdbPath - Destination $wixBaselineOriginalPdbPath - Force
2155+ Copy-Item - Path $PatchWixPdbPath - Destination $wixPatchOriginalPdbPath - Force
2156+
2157+ [xml ] $filesAssetXml = Get-Content - Raw - Path " $PSScriptRoot \..\..\assets\files.wxs"
2158+ [xml ] $patchTemplateXml = Get-Content - Raw - Path $PatchWxsPath
2159+
2160+ # Update the patch version
2161+ $patchFamilyNode = $patchTemplateXml.Wix.Fragment.PatchFamily
2162+ $patchFamilyNode.SetAttribute (' Version' , $NewVersion )
2163+
2164+ # get all the file components from the files.wxs
2165+ $components = $filesAssetXml.GetElementsByTagName (' Component' )
2166+
2167+ # add all the file components to the patch
2168+ foreach ($component in $components )
2169+ {
2170+ $id = $component.Id
2171+ $componentRef = $patchTemplateXml.CreateElement (' ComponentRef' , ' http://schemas.microsoft.com/wix/2006/wi' )
2172+ $idAttribute = $patchTemplateXml.CreateAttribute (' Id' )
2173+ $idAttribute.Value = $id
2174+ $null = $componentRef.Attributes.Append ($idAttribute )
2175+ $null = $patchFamilyNode.AppendChild ($componentRef )
2176+ }
2177+
2178+ # save the updated patch xml
2179+ $patchTemplateXml.Save ($wixPatchXmlPath )
2180+
2181+ $wixPaths = Get-WixPath
2182+
2183+ Write-Log " Processing baseline msi..."
2184+ Start-NativeExecution - VerboseOutputOnError {& $wixPaths.wixMeltExePath - nologo $BaselineMsiPath $wixBaselinePdbPath - pdb $wixBaselineOriginalPdbPath - x $wixBaselineBinariesPath }
2185+
2186+ Write-Log " Processing patch msi..."
2187+ Start-NativeExecution - VerboseOutputOnError {& $wixPaths.wixMeltExePath - nologo $PatchMsiPath $wixPatchPdbPath - pdb $wixPatchOriginalPdbPath - x $wixPatchBinariesPath }
2188+
2189+ Write-Log " generate diff..."
2190+ Start-NativeExecution - VerboseOutputOnError {& $wixPaths.wixTorchExePath - nologo - p - xi $wixBaselinePdbPath $wixPatchPdbPath - out $wixPatchMstPath }
2191+
2192+ Write-Log " Compiling patch..."
2193+ Start-NativeExecution - VerboseOutputOnError {& $wixPaths.wixCandleExePath - nologo $wixPatchXmlPath - out $wixPatchObjPath }
2194+
2195+ Write-Log " Linking patch..."
2196+ Start-NativeExecution - VerboseOutputOnError {& $wixPaths.wixLightExePath - nologo $wixPatchObjPath - out $wixPatchWixMspPath }
2197+
2198+ if ($Delta.IsPresent )
2199+ {
2200+ Write-Log " Generating delta msp..."
2201+ Start-NativeExecution - VerboseOutputOnError {& $wixPaths.wixPyroExePath - nologo $wixPatchWixMspPath - out $mspDeltaName - t RTM $wixPatchMstPath }
2202+ }
2203+ else
2204+ {
2205+ Write-Log " Generating full msp..."
2206+ Start-NativeExecution - VerboseOutputOnError {& $wixPaths.wixPyroExePath - nologo $wixPatchWixMspPath - out $mspName - t RTM $wixPatchMstPath }
2207+ }
2208+
2209+ # cleanup temporary files
2210+ Remove-Item - Path $filesToCleanup - Force - Recurse - ErrorAction SilentlyContinue
2211+ }
2212+
20422213<#
20432214 . Synopsis
20442215 Creates a Windows installer MSI package and assumes that the binaries are already built using 'Start-PSBuild'.
@@ -2106,23 +2277,7 @@ function New-MSIPackage
21062277 [Switch ] $Force
21072278 )
21082279
2109- # # AppVeyor base image might update the version for Wix. Hence, we should
2110- # # not hard code version numbers.
2111- $wixToolsetBinPath = " ${env: ProgramFiles(x86)} \WiX Toolset *\bin"
2112-
2113- Write-Verbose " Ensure Wix Toolset is present on the machine @ $wixToolsetBinPath "
2114- if (-not (Test-Path $wixToolsetBinPath ))
2115- {
2116- throw " The latest version of Wix Toolset 3.11 is required to create MSI package. Please install it from https://github.com/wixtoolset/wix3/releases"
2117- }
2118-
2119- # # Get the latest if multiple versions exist.
2120- $wixToolsetBinPath = (Get-ChildItem $wixToolsetBinPath ).FullName | Sort-Object - Descending | Select-Object - First 1
2121-
2122- Write-Verbose " Initialize Wix executables - Heat.exe, Candle.exe, Light.exe"
2123- $wixHeatExePath = Join-Path $wixToolsetBinPath " Heat.exe"
2124- $wixCandleExePath = Join-Path $wixToolsetBinPath " Candle.exe"
2125- $wixLightExePath = Join-Path $wixToolsetBinPath " Light.exe"
2280+ $wixPaths = Get-WixPath
21262281
21272282 $ProductSemanticVersion = Get-PackageSemanticVersion - Version $ProductVersion
21282283 $isPreview = $ProductSemanticVersion -like ' *-*'
@@ -2190,16 +2345,16 @@ function New-MSIPackage
21902345 }
21912346
21922347 Write-Log " verifying no new files have been added or removed..."
2193- Start-NativeExecution - VerboseOutputOnError { & $wixHeatExePath dir $ProductSourcePath - dr $productDirectoryName - cg $productDirectoryName - gg - sfrag - srd - scom - sreg - out $wixFragmentPath - var env.ProductSourcePath - v}
2348+ Start-NativeExecution - VerboseOutputOnError { & $wixPaths . wixHeatExePath dir $ProductSourcePath - dr $productDirectoryName - cg $productDirectoryName - gg - sfrag - srd - scom - sreg - out $wixFragmentPath - var env.ProductSourcePath - v}
21942349 Test-FileWxs - FilesWxsPath $FilesWxsPath - HeatFilesWxsPath $wixFragmentPath
21952350
21962351 Write-Log " running candle..."
2197- Start-NativeExecution - VerboseOutputOnError { & $wixCandleExePath " $ProductWxsPath " " $FilesWxsPath " - out (Join-Path " $env: Temp " " \\" ) - ext WixUIExtension - ext WixUtilExtension - arch $ProductTargetArchitecture - v}
2352+ Start-NativeExecution - VerboseOutputOnError { & $wixPaths . wixCandleExePath " $ProductWxsPath " " $FilesWxsPath " - out (Join-Path " $env: Temp " " \\" ) - ext WixUIExtension - ext WixUtilExtension - arch $ProductTargetArchitecture - v}
21982353
21992354 Write-Log " running light..."
22002355 # suppress ICE61, because we allow same version upgrades
22012356 # suppress ICE57, this suppresses an error caused by our shortcut not being installed per user
2202- Start-NativeExecution - VerboseOutputOnError {& $wixLightExePath - sice:ICE61 - sice:ICE57 - out $msiLocationPath - pdbout $msiPdbLocationPath $wixObjProductPath $wixObjFragmentPath - ext WixUIExtension - ext WixUtilExtension - dWixUILicenseRtf= " $LicenseFilePath " }
2357+ Start-NativeExecution - VerboseOutputOnError {& $wixPaths . wixLightExePath - sice:ICE61 - sice:ICE57 - out $msiLocationPath - pdbout $msiPdbLocationPath $wixObjProductPath $wixObjFragmentPath - ext WixUIExtension - ext WixUtilExtension - dWixUILicenseRtf= " $LicenseFilePath " }
22032358
22042359 Remove-Item - ErrorAction SilentlyContinue $wixFragmentPath - Force
22052360 Remove-Item - ErrorAction SilentlyContinue $wixObjProductPath - Force
0 commit comments