build-utils.ps1 10.2 KB
Newer Older
J
Jared Parsons 已提交
1 2 3 4 5
# Collection of powershell build utility functions that we use across our scripts.

Set-StrictMode -version 2.0
$ErrorActionPreference="Stop"

6
# Import Arcade functions
T
Tomáš Matoušek 已提交
7
. (Join-Path $PSScriptRoot "common\tools.ps1")
8

T
Tomáš Matoušek 已提交
9 10
$VSSetupDir = Join-Path $ArtifactsDir "VSSetup\$configuration"
$PackagesDir = Join-Path $ArtifactsDir "packages\$configuration"
11
$PublishDataUrl = "https://raw.githubusercontent.com/dotnet/roslyn/master/eng/config/PublishData.json"
T
Tomáš Matoušek 已提交
12 13 14 15

$binaryLog = if (Test-Path variable:binaryLog) { $binaryLog } else { $false }
$nodeReuse = if (Test-Path variable:nodeReuse) { $nodeReuse } else { $false }
$bootstrapDir = if (Test-Path variable:bootstrapDir) { $bootstrapDir } else { "" }
J
Jared Parsons 已提交
16
$bootstrapConfiguration = if (Test-Path variable:bootstrapConfiguration) { $bootstrapConfiguration } else { "Release" }
T
Tomáš Matoušek 已提交
17
$properties = if (Test-Path variable:properties) { $properties } else { @() }
18

T
Tomáš Matoušek 已提交
19 20 21 22 23 24 25
function GetProjectOutputBinary([string]$fileName, [string]$projectName = "", [string]$configuration = $script:configuration, [string]$tfm = "net472", [string]$rid = "", [bool]$published = $false) {
  $projectName = if ($projectName -ne "") { $projectName } else { [System.IO.Path]::GetFileNameWithoutExtension($fileName) }
  $publishDir = if ($published) { "publish\" } else { "" }
  $ridDir = if ($rid -ne "") { "$rid\" } else { "" }
  return Join-Path $ArtifactsDir "bin\$projectName\$configuration\$tfm\$ridDir$publishDir$fileName"
}

26 27
function GetPublishData() {
  if (Test-Path variable:global:_PublishData) {
28
  return $global:_PublishData
29 30 31 32 33 34 35 36 37 38 39 40
  }

  Write-Host "Downloading $PublishDataUrl"
  $content = (Invoke-WebRequest -Uri $PublishDataUrl -UseBasicParsing).Content

  return $global:_PublishData = ConvertFrom-Json $content
}

function GetBranchPublishData([string]$branchName) {
  $data = GetPublishData

  if (Get-Member -InputObject $data.branches -Name $branchName) {
41
  return $data.branches.$branchName
42
  } else {
43
  return $null
44 45 46 47 48 49 50
  }
}

function GetReleasePublishData([string]$releaseName) {
  $data = GetPublishData

  if (Get-Member -InputObject $data.releases -Name $releaseName) {
51
  return $data.releases.$releaseName
52
  } else {
53
  return $null
54 55 56
  }
}

57
# Handy function for executing a command in powershell and throwing if it 
T
Tomáš Matoušek 已提交
58
# fails.
J
Jared Parsons 已提交
59 60 61 62 63
#
# Use this when the full command is known at script authoring time and 
# doesn't require any dynamic argument build up.  Example:
#
#   Exec-Block { & $msbuild Test.proj }
64 65
# 
# Original sample came from: http://jameskovacs.com/2010/02/25/the-exec-problem/
J
Jared Parsons 已提交
66
function Exec-Block([scriptblock]$cmd) {
67
  & $cmd
68

69 70 71 72 73 74
  # Need to check both of these cases for errors as they represent different items
  # - $?: did the powershell script block throw an error
  # - $lastexitcode: did a windows command executed by the script block end in error
  if ((-not $?) -or ($lastexitcode -ne 0)) {
    throw "Command failed to execute: $cmd"
  }
J
Jared Parsons 已提交
75 76 77
}

function Exec-CommandCore([string]$command, [string]$commandArgs, [switch]$useConsole = $true) {
78 79 80 81
  if ($useConsole) {
    $exitCode = Exec-Process $command $commandArgs
    if ($exitCode -ne 0) { 
      throw "Command failed to execute with exit code $($exitCode): $command $commandArgs" 
J
Jared Parsons 已提交
82
    }
83 84
    return
  }
J
Jared Parsons 已提交
85

86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110
  $startInfo = New-Object System.Diagnostics.ProcessStartInfo
  $startInfo.FileName = $command
  $startInfo.Arguments = $commandArgs

  $startInfo.UseShellExecute = $false
  $startInfo.WorkingDirectory = Get-Location
  $startInfo.RedirectStandardOutput = $true
  $startInfo.CreateNoWindow = $true

  $process = New-Object System.Diagnostics.Process
  $process.StartInfo = $startInfo
  $process.Start() | Out-Null

  $finished = $false
  try {
    # The OutputDataReceived event doesn't fire as events are sent by the 
    # process in powershell.  Possibly due to subtlties of how Powershell
    # manages the thread pool that I'm not aware of.  Using blocking
    # reading here as an alternative which is fine since this blocks 
    # on completion already.
    $out = $process.StandardOutput
    while (-not $out.EndOfStream) {
      $line = $out.ReadLine()
      Write-Output $line
    }
J
Jared Parsons 已提交
111

112 113 114
    while (-not $process.WaitForExit(100)) { 
      # Non-blocking loop done to allow ctr-c interrupts
    }
J
Jared Parsons 已提交
115

116 117 118
    $finished = $true
    if ($process.ExitCode -ne 0) { 
      throw "Command failed to execute with exit code $($process.ExitCode): $command $commandArgs" 
J
Jared Parsons 已提交
119
    }
120 121
  }
  finally {
M
Maher Jendoubi 已提交
122
    # If we didn't finish then an error occurred or the user hit ctrl-c.  Either
123 124 125
    # way kill the process
    if (-not $finished) {
      $process.Kill()
J
Jared Parsons 已提交
126
    }
127
  }
J
Jared Parsons 已提交
128 129
}

J
Jared Parsons 已提交
130 131 132 133 134 135 136 137 138 139
# Handy function for executing a windows command which needs to go through 
# windows command line parsing.  
#
# Use this when the command arguments are stored in a variable.  Particularly 
# when the variable needs reparsing by the windows command line. Example:
#
#   $args = "/p:ManualBuild=true Test.proj"
#   Exec-Command $msbuild $args
# 
function Exec-Command([string]$command, [string]$commandArgs) {
140
  Exec-CommandCore -command $command -commandArgs $commandargs -useConsole:$false
J
Jared Parsons 已提交
141 142 143 144 145 146 147 148 149
}

# Functions exactly like Exec-Command but lets the process re-use the current 
# console. This means items like colored output will function correctly.
#
# In general this command should be used in place of
#   Exec-Command $msbuild $args | Out-Host
#
function Exec-Console([string]$command, [string]$commandArgs) {
150
  Exec-CommandCore -command $command -commandArgs $commandargs -useConsole:$true
J
Jared Parsons 已提交
151 152
}

J
Jared Parsons 已提交
153 154 155 156
# Handy function for executing a powershell script in a clean environment with 
# arguments.  Prefer this over & sourcing a script as it will both use a clean
# environment and do proper error checking
function Exec-Script([string]$script, [string]$scriptArgs = "") {
157
  Exec-Command "powershell" "-noprofile -executionPolicy RemoteSigned -file `"$script`" $scriptArgs"
158 159
}

160
# Ensure the proper .NET Core SDK is available. Returns the location to the dotnet.exe.
J
Jared Parsons 已提交
161
function Ensure-DotnetSdk() {
162 163
  $dotnetInstallDir = (InitializeDotNetCli -install:$true)
  $dotnetTestPath = Join-Path $dotnetInstallDir "dotnet.exe"
F
Fred Silberberg 已提交
164
  if (Test-Path -Path $dotnetTestPath) {
165 166 167 168
    return $dotnetTestPath
  }

  $dotnetTestPath = Join-Path $dotnetInstallDir "dotnet"
F
Fred Silberberg 已提交
169
  if (Test-Path -Path $dotnetTestPath) {
170 171 172 173
    return $dotnetTestPath
  }

  throw "Could not find dotnet executable in $dotnetInstallDir"
J
Jared Parsons 已提交
174 175
}

J
Jared Parsons 已提交
176
function Get-VersionCore([string]$name, [string]$versionFile) {
177 178 179 180 181 182 183 184
  $name = $name.Replace(".", "")
  $name = $name.Replace("-", "")
  $nodeName = "$($name)Version"
  $x = [xml](Get-Content -raw $versionFile)
  $node = $x.SelectSingleNode("//Project/PropertyGroup/$nodeName")
  if ($node -ne $null) {
    return $node.InnerText
  }
J
Jared Parsons 已提交
185

186
  throw "Cannot find package $name in $versionFile"
J
Jared Parsons 已提交
187 188 189 190 191

}

# Return the version of the NuGet package as used in this repo
function Get-PackageVersion([string]$name) {
192
  return Get-VersionCore $name (Join-Path $EngRoot "Versions.props")
J
Jared Parsons 已提交
193 194 195 196
}

# Locate the directory where our NuGet packages will be deployed.  Needs to be kept in sync
# with the logic in Version.props
J
Jared Parsons 已提交
197
function Get-PackagesDir() {
198 199 200 201 202 203 204
  $d = $null
  if ($env:NUGET_PACKAGES -ne $null) {
    $d = $env:NUGET_PACKAGES
  }
  else {
    $d = Join-Path $env:UserProfile ".nuget\packages\"
  }
J
Jared Parsons 已提交
205

206 207
  Create-Directory $d
  return $d
J
Jared Parsons 已提交
208
}
209

J
Jared Parsons 已提交
210 211 212
# Locate the directory of a specific NuGet package which is restored via our main 
# toolset values.
function Get-PackageDir([string]$name, [string]$version = "") {
213 214 215
  if ($version -eq "") {
    $version = Get-PackageVersion $name
  }
J
Jared Parsons 已提交
216

217 218 219 220
  $p = Get-PackagesDir
  $p = Join-Path $p $name.ToLowerInvariant()
  $p = Join-Path $p $version
  return $p
J
Jared Parsons 已提交
221 222
}

J
Jared Parsons 已提交
223
function Run-MSBuild([string]$projectFilePath, [string]$buildArgs = "", [string]$logFileName = "", [switch]$parallel = $true, [switch]$summary = $true, [switch]$warnAsError = $true, [string]$configuration = $script:configuration) {
224 225 226 227 228 229 230 231 232
  # Because we override the C#/VB toolset to build against our LKG package, it is important
  # that we do not reuse MSBuild nodes from other jobs/builds on the machine. Otherwise,
  # we'll run into issues such as https://github.com/dotnet/roslyn/issues/6211.
  # MSBuildAdditionalCommandLineArgs=
  $args = "/p:TreatWarningsAsErrors=true /nologo /nodeReuse:false /p:Configuration=$configuration ";

  if ($warnAsError) {
    $args += " /warnaserror"
  }
T
Tomáš Matoušek 已提交
233

234 235 236 237 238
  if ($summary) {
    $args += " /consoleloggerparameters:Verbosity=minimal;summary"
  } else {        
    $args += " /consoleloggerparameters:Verbosity=minimal"
  }
T
Tomáš Matoušek 已提交
239

240 241 242
  if ($parallel) {
    $args += " /m"
  }
T
Tomáš Matoušek 已提交
243

244 245 246
  if ($skipAnalyzers) {
    $args += " /p:UseRoslynAnalyzers=false"
  }
T
Tomáš Matoušek 已提交
247

248 249 250
  if ($binaryLog) {
    if ($logFileName -eq "") {
      $logFileName = [IO.Path]::GetFileNameWithoutExtension($projectFilePath)
T
Tomáš Matoušek 已提交
251
    }
252 253 254 255
    $logFileName = [IO.Path]::ChangeExtension($logFileName, ".binlog")
    $logFilePath = Join-Path $LogDir $logFileName
    $args += " /bl:$logFilePath"
  }
T
Tomáš Matoušek 已提交
256

257 258 259
  if ($officialBuildId) {
    $args += " /p:OfficialBuildId=" + $officialBuildId
  }
T
Tomáš Matoušek 已提交
260

261 262 263
  if ($ci) {
    $args += " /p:ContinuousIntegrationBuild=true"
  }
T
Tomáš Matoušek 已提交
264

265 266 267
  if ($bootstrapDir -ne "") {
    $args += " /p:BootstrapBuildPath=$bootstrapDir"
  }
T
Tomáš Matoušek 已提交
268

269 270 271
  $args += " $buildArgs"
  $args += " $projectFilePath"
  $args += " $properties"
T
Tomáš Matoušek 已提交
272

273 274
  $buildTool = InitializeBuildTool
  Exec-Console $buildTool.Path "$($buildTool.Command) $args"
T
Tomáš Matoušek 已提交
275 276 277 278 279 280 281
}

# Create a bootstrap build of the compiler.  Returns the directory where the bootstrap build
# is located.
#
# Important to not set $script:bootstrapDir here yet as we're actually in the process of
# building the bootstrap.
282
function Make-BootstrapBuild([switch]$force32 = $false) {
283
  Write-Host "Building bootstrap compiler"
T
Tomáš Matoušek 已提交
284

285 286 287
  $dir = Join-Path $ArtifactsDir "Bootstrap"
  Remove-Item -re $dir -ErrorAction SilentlyContinue
  Create-Directory $dir
T
Tomáš Matoušek 已提交
288

289 290
  $packageName = "Microsoft.Net.Compilers.Toolset"
  $projectPath = "src\NuGet\$packageName\$packageName.Package.csproj"
291
  $force32Flag = if ($force32) { " /p:BOOTSTRAP32=true" } else { "" }
T
Tomáš Matoušek 已提交
292

293
  Run-MSBuild $projectPath "/restore /t:Pack /p:RoslynEnforceCodeStyle=false /p:UseRoslynAnalyzers=false /p:DotNetUseShippingVersions=true /p:InitialDefineConstants=BOOTSTRAP /p:PackageOutputPath=`"$dir`" /p:EnableNgenOptimization=false /p:PublishWindowsPdb=false $force32Flag" -logFileName "Bootstrap" -configuration $bootstrapConfiguration
294 295
  $packageFile = Get-ChildItem -Path $dir -Filter "$packageName.*.nupkg"
  Unzip "$dir\$packageFile" $dir
T
Tomáš Matoušek 已提交
296

297 298
  Write-Host "Cleaning Bootstrap compiler artifacts"
  Run-MSBuild $projectPath "/t:Clean" -logFileName "BootstrapClean"
T
Tomáš Matoušek 已提交
299

300
  return $dir
T
Tomáš Matoušek 已提交
301
}