|  | # Copyright 2019 The Bazel Authors. All rights reserved. | 
|  | # | 
|  | # Licensed under the Apache License, Version 2.0 (the "License"); | 
|  | # you may not use this file except in compliance with the License. | 
|  | # You may obtain a copy of the License at | 
|  | # | 
|  | #    http://www.apache.org/licenses/LICENSE-2.0 | 
|  | # | 
|  | # Unless required by applicable law or agreed to in writing, software | 
|  | # distributed under the License is distributed on an "AS IS" BASIS, | 
|  | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
|  | # See the License for the specific language governing permissions and | 
|  | # limitations under the License. | 
|  |  | 
|  | # Set path constants. These are relative to the Bazel source tree root. | 
|  | $Icon = 'site\images\favicon.ico' | 
|  | $ArtworkBig = 'scripts\packages\msi\dialog.bmp' | 
|  | $ArtworkSmall = 'scripts\packages\msi\banner.bmp' | 
|  | $Licence = 'scripts\packages\msi\license.rtf' | 
|  | $Guids = 'scripts\packages\msi\guids.txt' | 
|  | $Wxs = 'scripts\packages\msi\bazelmsi.wxs' | 
|  |  | 
|  | # Logs a message to stdout. | 
|  | function Log-Info { | 
|  | param ( | 
|  | [string]$msg | 
|  | ) | 
|  | $basename = $PSCommandPath.Substring($PSCommandPath.LastIndexOfAny('/\') + 1) | 
|  | $time = (Get-Date).ToString('HH:mm:ss') | 
|  | Write-Host "INFO[$basename $time] $msg" | 
|  | } | 
|  |  | 
|  | function Replace-Slashes { | 
|  | param ( | 
|  | [string]$s | 
|  | ) | 
|  | return $s.Replace('/', '\') | 
|  | } | 
|  |  | 
|  | # Computes the release name from the 'BazelExe'. | 
|  | # 'BazelExe' must already have been validated to match this regex. | 
|  | # 'ReleaseName' is like "0.28.0rc5" | 
|  | # 'Version' is the SemVer part of 'ReleaseName', e.g. "0.28.0" | 
|  | function Compute-RelaseNameAndVersion { | 
|  | param ( | 
|  | [Parameter(Mandatory=$true)][string]$BazelExe | 
|  | ) | 
|  |  | 
|  | $rel = [regex]::Match($BazelExe, "bazel-([^-]+)-windows.*\.exe$").captures.groups[1].value | 
|  | $ver = [regex]::Match($rel, "^([0-9]+\.[0-9]+\.[0-9]+)(rc[0-9]+)?$").captures.groups[1].value | 
|  | return $rel, $ver | 
|  | } | 
|  |  | 
|  | # Generates a new GUID, prints it as uppercase. | 
|  | # The WiX Toolkit expects uppercase GUIDs in the .wxs file. | 
|  | function Generate-Guid { | 
|  | return [guid]::NewGuid().ToString().ToUpper() | 
|  | } | 
|  |  | 
|  | # Returns the UpgradeGuid for this release. | 
|  | function Get-UpgradeGuid { | 
|  | param ( | 
|  | [Parameter(Mandatory=$true)][string]$release_name | 
|  | ) | 
|  |  | 
|  | $d = @{} | 
|  | $result = $null | 
|  | foreach ($line in Get-Content -Path $Guids) { | 
|  | $line = $line.Split('#')[0] | 
|  | if ($line) { | 
|  | $k, $v = $line.Split(' ', 2) | 
|  | # Ensure all version prefixes in the file are unique. | 
|  | if ($d.ContainsKey($k)) { | 
|  | throw "Duplicate relase prefix $k in $Guids" | 
|  | } | 
|  | else { | 
|  | $d[$k] = $null | 
|  | } | 
|  | if (! $v) { | 
|  | throw "Null key '$v' in line '$line'" | 
|  | } | 
|  | # Ensure all GUIDs in the file are unique. We can use the same | 
|  | # hashtable because the value domains are distinct. | 
|  | if ($d.ContainsKey($v)) { | 
|  | throw "Duplicate GUID $v in $Guids" | 
|  | } | 
|  | else { | 
|  | $d[$v] = $null | 
|  | } | 
|  | if (! $result -and $release_name.StartsWith($k)) { | 
|  | $result = $v | 
|  | # Do not return yet, so we check that all GUIDs are unique. | 
|  | } | 
|  | } | 
|  | } | 
|  | if ($result) { | 
|  | return $result | 
|  | } | 
|  | else { | 
|  | throw "UpgradeGuid for $release_name not found in $Guids" | 
|  | } | 
|  | } | 
|  |  | 
|  | # Returns the Bazel version (as a SemVer string) from the release name. | 
|  | function Get-BazelVersion { | 
|  | if ($ReleaseName -match "rc[0-9]+$") { | 
|  | return $ReleaseName.Substring(0, $ReleaseName.LastIndexOf('rc')) | 
|  | } | 
|  | else { | 
|  | return $ReleaseName | 
|  | } | 
|  | } | 
|  |  | 
|  | # Downloads and extracts the WiX Toolkit. | 
|  | # Returns the path where the tools are (e.g. "candle.exe"). | 
|  | function Download-Wix { | 
|  | # Use TLS1.2 for HTTPS (fixes an issue where later steps can't connect to github.com). | 
|  | # See https://github.com/bazelbuild/continuous-integration/blob/master/buildkite/setup-windows.ps1 | 
|  | [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 | 
|  |  | 
|  | $wix_dir = $(New-Item -Path "${WorkDir}\_wix" -ItemType Directory -Force).FullName | 
|  | $zip_file = "$wix_dir\wix311.zip" | 
|  | (New-Object Net.WebClient).DownloadFile( | 
|  | 'https://github.com/wixtoolset/wix3/releases/download/wix3111rtm/wix311-binaries.zip', | 
|  | $zip_file) | 
|  |  | 
|  | $actual_sha = (Get-FileHash -Path $zip_file -Algorithm SHA256).Hash.ToString() | 
|  | $expected_sha = '37F0A533B0978A454EFB5DC3BD3598BECF9660AAF4287E55BF68CA6B527D051D' | 
|  | if ($actual_sha -ne $expected_sha) { | 
|  | throw "Bad checksum: wix311-binaries.zip SHA256 is $actual_sha, expected $expected_sha" | 
|  | } | 
|  |  | 
|  | Expand-Archive -Path $zip_file -DestinationPath $wix_dir -Force | 
|  |  | 
|  | return $wix_dir | 
|  | } | 
|  |  | 
|  | # Creates the .wixobj file using candle.exe (the "compiler"). | 
|  | function Run-Candle { | 
|  | param ( | 
|  | [Parameter(Mandatory=$true)][string]$wix_root | 
|  | ) | 
|  |  | 
|  | $out = "${WorkDir}\bazelmsi.wixobj" | 
|  |  | 
|  | $output = & "${wix_root}\candle.exe" ` | 
|  | -nologo ` | 
|  | -arch x64 ` | 
|  | "-dBAZEL_EXE=$BazelExe" ` | 
|  | "-dICON=$Icon" ` | 
|  | "-dUPGRADE_GUID=$(Get-UpgradeGuid $ReleaseName)" ` | 
|  | "-dRELEASE_NAME=$ReleaseName" ` | 
|  | "-dVERSION=$(Get-BazelVersion)" ` | 
|  | "-dRANDOM_GUID_1=$(Generate-Guid)" ` | 
|  | "-dRANDOM_GUID_2=$(Generate-Guid)" ` | 
|  | -o $out ` | 
|  | $Wxs | 
|  | if (! $?) { | 
|  | throw $output | 
|  | } | 
|  | return $out | 
|  | } | 
|  |  | 
|  | # Creates the .msi file using light.exe (the "linker"). | 
|  | function Run-Light { | 
|  | param ( | 
|  | [Parameter(Mandatory=$true)][string]$wix_root, | 
|  | [Parameter(Mandatory=$true)][string]$wixobj | 
|  | ) | 
|  |  | 
|  | $output = & "${wix_root}\light.exe" ` | 
|  | -nologo ` | 
|  | -ext WixUIExtension ` | 
|  | '-cultures:en-us' ` | 
|  | "-dWixUILicenseRtf=$Licence" ` | 
|  | "-dWixUIDialogBmp=$ArtworkBig" ` | 
|  | "-dWixUIBannerBmp=$ArtworkSmall" ` | 
|  | -o $OutMsi ` | 
|  | $wixobj | 
|  | if (! $?) { | 
|  | throw $output | 
|  | } | 
|  | } | 
|  |  | 
|  | function Make-Msi { | 
|  | param ( | 
|  | [Parameter(Mandatory=$true)] | 
|  | [ValidatePattern("bazel-[0-9]+\.[0-9]+\.[0-9]+(rc[1-9]|rc[1-9][0-9]+)?-windows-x86_64.exe$")] | 
|  | [string] | 
|  | $BazelExe, | 
|  |  | 
|  | [Parameter(Mandatory=$true)][string]$WorkDir, | 
|  | [Parameter(Mandatory=$true)][string]$OutMsi | 
|  | ) | 
|  |  | 
|  | $ReleaseName, $Version = Compute-RelaseNameAndVersion $BazelExe | 
|  |  | 
|  | Log-Info 'Downloading WiX Toolkit' | 
|  | $wix_root = Download-Wix | 
|  |  | 
|  | Log-Info 'Creating wixobj' | 
|  | $wixobj = Run-Candle -wix_root $wix_root | 
|  |  | 
|  | Log-Info 'Creating msi' | 
|  | Run-Light -wix_root $wix_root -wixobj $wixobj | 
|  |  | 
|  | Log-Info "Done: $OutMsi" | 
|  | } |