MSI: add function to generate a MSP (#6445)

add a function to generate an MSP
This commit is contained in:
Travis Plunk 2018-03-26 14:39:48 -07:00 committed by GitHub
parent d301e62d3a
commit 701b919345
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 202 additions and 21 deletions

1
.gitignore vendored
View file

@ -66,3 +66,4 @@ TestsResults*.xml
# Resharper settings
PowerShell.sln.DotSettings.user
*.msp

24
assets/patch-template.wxs Normal file
View file

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Patch
AllowRemoval="yes"
Manufacturer="Microsoft Powershell"
MoreInfoURL="http://github.com/powershell/powershell"
DisplayName="Sample Patch"
Description="Small Update Patch"
Classification="Update"
>
<Media Id="5000" Cabinet="RTM.cab">
<PatchBaseline Id="RTM"/>
</Media>
<PatchFamilyRef Id="SamplePatchFamily"/>
</Patch>
<Fragment>
<PatchFamily Id='SamplePatchFamily' Version='6.1.1' Supersede='yes'>
<PropertyRef Id="ProductVersion"/>
</PatchFamily>
</Fragment>
</Wix>

View file

@ -5,7 +5,8 @@ CompanyName="Microsoft Corporation"
Copyright="Copyright (c) Microsoft Corporation. All rights reserved."
ModuleVersion="1.0.0"
PowerShellVersion="5.0"
CmdletsToExport=@("Start-PSPackage",'New-PSSignedBuildZip', 'New-UnifiedNugetPackage')
CmdletsToExport=@()
FunctionsToExport=@("Start-PSPackage",'New-PSSignedBuildZip', 'New-UnifiedNugetPackage', 'New-MSIPatch')
RootModule="packaging.psm1"
RequiredModules = @("build")
}

View file

@ -2039,6 +2039,177 @@ function Get-NugetSemanticVersion
$packageSemanticVersion
}
# Get the paths to various WiX tools
function Get-WixPath
{
## AppVeyor base image might update the version for Wix. Hence, we should
## not hard code version numbers.
$wixToolsetBinPath = "${env:ProgramFiles(x86)}\WiX Toolset *\bin"
Write-Verbose "Ensure Wix Toolset is present on the machine @ $wixToolsetBinPath"
if (-not (Test-Path $wixToolsetBinPath))
{
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"
}
## Get the latest if multiple versions exist.
$wixToolsetBinPath = (Get-ChildItem $wixToolsetBinPath).FullName | Sort-Object -Descending | Select-Object -First 1
Write-Verbose "Initialize Wix executables..."
$wixHeatExePath = Join-Path $wixToolsetBinPath "heat.exe"
$wixMeltExePath = Join-Path $wixToolsetBinPath "melt.exe"
$wixTorchExePath = Join-Path $wixToolsetBinPath "torch.exe"
$wixPyroExePath = Join-Path $wixToolsetBinPath "pyro.exe"
$wixCandleExePath = Join-Path $wixToolsetBinPath "Candle.exe"
$wixLightExePath = Join-Path $wixToolsetBinPath "Light.exe"
return [PSCustomObject] @{
WixHeatExePath = $wixHeatExePath
WixMeltExePath = $wixMeltExePath
WixTorchExePath = $wixTorchExePath
WixPyroExePath = $wixPyroExePath
WixCandleExePath = $wixCandleExePath
WixLightExePath = $wixLightExePath
}
}
<#
.Synopsis
Creates a Windows installer MSP package from two MSIs and WIXPDB files
This only works on a Windows machine due to the usage of WiX.
.EXAMPLE
# This example shows how to produce a x64 patch from 6.0.2 to a theoretical 6.0.3
cd $RootPathOfPowerShellRepo
Import-Module .\build.psm1; Import-Module .\tools\packaging\packaging.psm1
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
#>
function New-MSIPatch
{
param(
[Parameter(Mandatory, HelpMessage='The version of the fixed or patch MSI.')]
[ValidatePattern("^\d+\.\d+\.\d+$")]
[string] $NewVersion,
[Parameter(Mandatory, HelpMessage='The path to the original or baseline MSI.')]
[ValidateNotNullOrEmpty()]
[ValidateScript( {(Test-Path $_) -and $_ -like '*.msi'})]
[string] $BaselineMsiPath,
[Parameter(Mandatory, HelpMessage='The path to the WIXPDB for the original or baseline MSI.')]
[ValidateNotNullOrEmpty()]
[ValidateScript( {(Test-Path $_) -and $_ -like '*.wixpdb'})]
[string] $BaselineWixPdbPath,
[Parameter(Mandatory, HelpMessage='The path to the fixed or patch MSI.')]
[ValidateNotNullOrEmpty()]
[ValidateScript( {(Test-Path $_) -and $_ -like '*.msi'})]
[string] $PatchMsiPath,
[Parameter(Mandatory, HelpMessage='The path to the WIXPDB for the fixed or patch MSI.')]
[ValidateNotNullOrEmpty()]
[ValidateScript( {(Test-Path $_) -and $_ -like '*.wixpdb'})]
[string] $PatchWixPdbPath,
[Parameter(HelpMessage='Path to the patch template WXS. Usually you do not need to specify this')]
[ValidateNotNullOrEmpty()]
[ValidateScript( {Test-Path $_})]
[string] $PatchWxsPath = "$PSScriptRoot\..\..\assets\patch-template.wxs",
[Parameter(HelpMessage='Produce a delta patch instead of a full patch. Usually not worth it.')]
[switch] $Delta
)
$mspName = (Split-Path -Path $PatchMsiPath -Leaf).Replace('.msi','.fullpath.msp')
$mspDeltaName = (Split-Path -Path $PatchMsiPath -Leaf).Replace('.msi','.deltapatch.msp')
$wixPatchXmlPath = Join-Path $env:Temp "patch.wxs"
$wixBaselineOriginalPdbPath = Join-Path $env:Temp "baseline.original.wixpdb"
$wixBaselinePdbPath = Join-Path $env:Temp "baseline.wixpdb"
$wixBaselineBinariesPath = Join-Path $env:Temp "baseline.binaries"
$wixPatchOriginalPdbPath = Join-Path $env:Temp "patch.original.wixpdb"
$wixPatchPdbPath = Join-Path $env:Temp "patch.wixpdb"
$wixPatchBinariesPath = Join-Path $env:Temp "patch.binaries"
$wixPatchMstPath = Join-Path $env:Temp "patch.wixmst"
$wixPatchObjPath = Join-Path $env:Temp "patch.wixobj"
$wixPatchWixMspPath = Join-Path $env:Temp "patch.wixmsp"
$filesToCleanup = @(
$wixPatchXmlPath
$wixBaselinePdbPath
$wixBaselineBinariesPath
$wixPatchPdbPath
$wixPatchBinariesPath
$wixPatchMstPath
$wixPatchObjPath
$wixPatchWixMspPath
$wixPatchOriginalPdbPath
$wixBaselineOriginalPdbPath
)
# cleanup from previous builds
Remove-Item -Path $filesToCleanup -Force -Recurse -ErrorAction SilentlyContinue
# Melt changes the original, so copy before running melt
Copy-Item -Path $BaselineWixPdbPath -Destination $wixBaselineOriginalPdbPath -Force
Copy-Item -Path $PatchWixPdbPath -Destination $wixPatchOriginalPdbPath -Force
[xml] $filesAssetXml = Get-Content -Raw -Path "$PSScriptRoot\..\..\assets\files.wxs"
[xml] $patchTemplateXml = Get-Content -Raw -Path $PatchWxsPath
# Update the patch version
$patchFamilyNode = $patchTemplateXml.Wix.Fragment.PatchFamily
$patchFamilyNode.SetAttribute('Version', $NewVersion)
# get all the file components from the files.wxs
$components = $filesAssetXml.GetElementsByTagName('Component')
# add all the file components to the patch
foreach($component in $components)
{
$id = $component.Id
$componentRef = $patchTemplateXml.CreateElement('ComponentRef','http://schemas.microsoft.com/wix/2006/wi')
$idAttribute = $patchTemplateXml.CreateAttribute('Id')
$idAttribute.Value = $id
$null = $componentRef.Attributes.Append($idAttribute)
$null = $patchFamilyNode.AppendChild($componentRef)
}
# save the updated patch xml
$patchTemplateXml.Save($wixPatchXmlPath)
$wixPaths = Get-WixPath
Write-Log "Processing baseline msi..."
Start-NativeExecution -VerboseOutputOnError {& $wixPaths.wixMeltExePath -nologo $BaselineMsiPath $wixBaselinePdbPath -pdb $wixBaselineOriginalPdbPath -x $wixBaselineBinariesPath}
Write-Log "Processing patch msi..."
Start-NativeExecution -VerboseOutputOnError {& $wixPaths.wixMeltExePath -nologo $PatchMsiPath $wixPatchPdbPath -pdb $wixPatchOriginalPdbPath -x $wixPatchBinariesPath}
Write-Log "generate diff..."
Start-NativeExecution -VerboseOutputOnError {& $wixPaths.wixTorchExePath -nologo -p -xi $wixBaselinePdbPath $wixPatchPdbPath -out $wixPatchMstPath}
Write-Log "Compiling patch..."
Start-NativeExecution -VerboseOutputOnError {& $wixPaths.wixCandleExePath -nologo $wixPatchXmlPath -out $wixPatchObjPath}
Write-Log "Linking patch..."
Start-NativeExecution -VerboseOutputOnError {& $wixPaths.wixLightExePath -nologo $wixPatchObjPath -out $wixPatchWixMspPath}
if($Delta.IsPresent)
{
Write-Log "Generating delta msp..."
Start-NativeExecution -VerboseOutputOnError {& $wixPaths.wixPyroExePath -nologo $wixPatchWixMspPath -out $mspDeltaName -t RTM $wixPatchMstPath }
}
else
{
Write-Log "Generating full msp..."
Start-NativeExecution -VerboseOutputOnError {& $wixPaths.wixPyroExePath -nologo $wixPatchWixMspPath -out $mspName -t RTM $wixPatchMstPath }
}
# cleanup temporary files
Remove-Item -Path $filesToCleanup -Force -Recurse -ErrorAction SilentlyContinue
}
<#
.Synopsis
Creates a Windows installer MSI package and assumes that the binaries are already built using 'Start-PSBuild'.
@ -2106,23 +2277,7 @@ function New-MSIPackage
[Switch] $Force
)
## AppVeyor base image might update the version for Wix. Hence, we should
## not hard code version numbers.
$wixToolsetBinPath = "${env:ProgramFiles(x86)}\WiX Toolset *\bin"
Write-Verbose "Ensure Wix Toolset is present on the machine @ $wixToolsetBinPath"
if (-not (Test-Path $wixToolsetBinPath))
{
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"
}
## Get the latest if multiple versions exist.
$wixToolsetBinPath = (Get-ChildItem $wixToolsetBinPath).FullName | Sort-Object -Descending | Select-Object -First 1
Write-Verbose "Initialize Wix executables - Heat.exe, Candle.exe, Light.exe"
$wixHeatExePath = Join-Path $wixToolsetBinPath "Heat.exe"
$wixCandleExePath = Join-Path $wixToolsetBinPath "Candle.exe"
$wixLightExePath = Join-Path $wixToolsetBinPath "Light.exe"
$wixPaths = Get-WixPath
$ProductSemanticVersion = Get-PackageSemanticVersion -Version $ProductVersion
$isPreview = $ProductSemanticVersion -like '*-*'
@ -2190,16 +2345,16 @@ function New-MSIPackage
}
Write-Log "verifying no new files have been added or removed..."
Start-NativeExecution -VerboseOutputOnError { & $wixHeatExePath dir $ProductSourcePath -dr $productDirectoryName -cg $productDirectoryName -gg -sfrag -srd -scom -sreg -out $wixFragmentPath -var env.ProductSourcePath -v}
Start-NativeExecution -VerboseOutputOnError { & $wixPaths.wixHeatExePath dir $ProductSourcePath -dr $productDirectoryName -cg $productDirectoryName -gg -sfrag -srd -scom -sreg -out $wixFragmentPath -var env.ProductSourcePath -v}
Test-FileWxs -FilesWxsPath $FilesWxsPath -HeatFilesWxsPath $wixFragmentPath
Write-Log "running candle..."
Start-NativeExecution -VerboseOutputOnError { & $wixCandleExePath "$ProductWxsPath" "$FilesWxsPath" -out (Join-Path "$env:Temp" "\\") -ext WixUIExtension -ext WixUtilExtension -arch $ProductTargetArchitecture -v}
Start-NativeExecution -VerboseOutputOnError { & $wixPaths.wixCandleExePath "$ProductWxsPath" "$FilesWxsPath" -out (Join-Path "$env:Temp" "\\") -ext WixUIExtension -ext WixUtilExtension -arch $ProductTargetArchitecture -v}
Write-Log "running light..."
# suppress ICE61, because we allow same version upgrades
# suppress ICE57, this suppresses an error caused by our shortcut not being installed per user
Start-NativeExecution -VerboseOutputOnError {& $wixLightExePath -sice:ICE61 -sice:ICE57 -out $msiLocationPath -pdbout $msiPdbLocationPath $wixObjProductPath $wixObjFragmentPath -ext WixUIExtension -ext WixUtilExtension -dWixUILicenseRtf="$LicenseFilePath"}
Start-NativeExecution -VerboseOutputOnError {& $wixPaths.wixLightExePath -sice:ICE61 -sice:ICE57 -out $msiLocationPath -pdbout $msiPdbLocationPath $wixObjProductPath $wixObjFragmentPath -ext WixUIExtension -ext WixUtilExtension -dWixUILicenseRtf="$LicenseFilePath"}
Remove-Item -ErrorAction SilentlyContinue $wixFragmentPath -Force
Remove-Item -ErrorAction SilentlyContinue $wixObjProductPath -Force