[2020-02] Bump msbuild to track mono-2019-12 (#19661)
[mono-project.git] / eng / common / tools.ps1
blob23a3fd53d7a52aaf7038866a1662947c57946b2d
1 # Initialize variables if they aren't already defined.
2 # These may be defined as parameters of the importing script, or set after importing this script.
4 # CI mode - set to true on CI server for PR validation build or official build.
5 [bool]$ci = if (Test-Path variable:ci) { $ci } else { $false }
7 # Build configuration. Common values include 'Debug' and 'Release', but the repository may use other names.
8 [string]$configuration = if (Test-Path variable:configuration) { $configuration } else { 'Debug' }
10 # Set to true to output binary log from msbuild. Note that emitting binary log slows down the build.
11 # Binary log must be enabled on CI.
12 [bool]$binaryLog = if (Test-Path variable:binaryLog) { $binaryLog } else { $ci }
14 # Set to true to use the pipelines logger which will enable Azure logging output.
15 # https://github.com/Microsoft/azure-pipelines-tasks/blob/master/docs/authoring/commands.md
16 # This flag is meant as a temporary opt-opt for the feature while validate it across
17 # our consumers. It will be deleted in the future.
18 [bool]$pipelinesLog = if (Test-Path variable:pipelinesLog) { $pipelinesLog } else { $ci }
20 # Turns on machine preparation/clean up code that changes the machine state (e.g. kills build processes).
21 [bool]$prepareMachine = if (Test-Path variable:prepareMachine) { $prepareMachine } else { $false }
23 # True to restore toolsets and dependencies.
24 [bool]$restore = if (Test-Path variable:restore) { $restore } else { $true }
26 # Adjusts msbuild verbosity level.
27 [string]$verbosity = if (Test-Path variable:verbosity) { $verbosity } else { 'minimal' }
29 # Set to true to reuse msbuild nodes. Recommended to not reuse on CI.
30 [bool]$nodeReuse = if (Test-Path variable:nodeReuse) { $nodeReuse } else { !$ci }
32 # Configures warning treatment in msbuild.
33 [bool]$warnAsError = if (Test-Path variable:warnAsError) { $warnAsError } else { $true }
35 # Specifies which msbuild engine to use for build: 'vs', 'dotnet' or unspecified (determined based on presence of tools.vs in global.json).
36 [string]$msbuildEngine = if (Test-Path variable:msbuildEngine) { $msbuildEngine } else { $null }
38 # True to attempt using .NET Core already that meets requirements specified in global.json
39 # installed on the machine instead of downloading one.
40 [bool]$useInstalledDotNetCli = if (Test-Path variable:useInstalledDotNetCli) { $useInstalledDotNetCli } else { $true }
42 # Enable repos to use a particular version of the on-line dotnet-install scripts.
43 # default URL: https://dot.net/v1/dotnet-install.ps1
44 [string]$dotnetInstallScriptVersion = if (Test-Path variable:dotnetInstallScriptVersion) { $dotnetInstallScriptVersion } else { 'v1' }
46 # True to use global NuGet cache instead of restoring packages to repository-local directory.
47 [bool]$useGlobalNuGetCache = if (Test-Path variable:useGlobalNuGetCache) { $useGlobalNuGetCache } else { !$ci }
49 # An array of names of processes to stop on script exit if prepareMachine is true.
50 $processesToStopOnExit = if (Test-Path variable:processesToStopOnExit) { $processesToStopOnExit } else { @('msbuild', 'dotnet', 'vbcscompiler') }
52 $disableConfigureToolsetImport = if (Test-Path variable:disableConfigureToolsetImport) { $disableConfigureToolsetImport } else { $null }
54 set-strictmode -version 2.0
55 $ErrorActionPreference = 'Stop'
56 [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
58 function Create-Directory([string[]] $path) {
59 if (!(Test-Path $path)) {
60 New-Item -path $path -force -itemType 'Directory' | Out-Null
64 function Unzip([string]$zipfile, [string]$outpath) {
65 Add-Type -AssemblyName System.IO.Compression.FileSystem
66 [System.IO.Compression.ZipFile]::ExtractToDirectory($zipfile, $outpath)
69 # This will exec a process using the console and return it's exit code.
70 # This will not throw when the process fails.
71 # Returns process exit code.
72 function Exec-Process([string]$command, [string]$commandArgs) {
73 $startInfo = New-Object System.Diagnostics.ProcessStartInfo
74 $startInfo.FileName = $command
75 $startInfo.Arguments = $commandArgs
76 $startInfo.UseShellExecute = $false
77 $startInfo.WorkingDirectory = Get-Location
79 $process = New-Object System.Diagnostics.Process
80 $process.StartInfo = $startInfo
81 $process.Start() | Out-Null
83 $finished = $false
84 try {
85 while (-not $process.WaitForExit(100)) {
86 # Non-blocking loop done to allow ctr-c interrupts
89 $finished = $true
90 return $global:LASTEXITCODE = $process.ExitCode
92 finally {
93 # If we didn't finish then an error occurred or the user hit ctrl-c. Either
94 # way kill the process
95 if (-not $finished) {
96 $process.Kill()
101 # createSdkLocationFile parameter enables a file being generated under the toolset directory
102 # which writes the sdk's location into. This is only necessary for cmd --> powershell invocations
103 # as dot sourcing isn't possible.
104 function InitializeDotNetCli([bool]$install, [bool]$createSdkLocationFile) {
105 if (Test-Path variable:global:_DotNetInstallDir) {
106 return $global:_DotNetInstallDir
109 # Don't resolve runtime, shared framework, or SDK from other locations to ensure build determinism
110 $env:DOTNET_MULTILEVEL_LOOKUP=0
112 # Disable first run since we do not need all ASP.NET packages restored.
113 $env:DOTNET_SKIP_FIRST_TIME_EXPERIENCE=1
115 # Disable telemetry on CI.
116 if ($ci) {
117 $env:DOTNET_CLI_TELEMETRY_OPTOUT=1
120 # Source Build uses DotNetCoreSdkDir variable
121 if ($env:DotNetCoreSdkDir -ne $null) {
122 $env:DOTNET_INSTALL_DIR = $env:DotNetCoreSdkDir
125 # Find the first path on %PATH% that contains the dotnet.exe
126 if ($useInstalledDotNetCli -and (-not $globalJsonHasRuntimes) -and ($env:DOTNET_INSTALL_DIR -eq $null)) {
127 $dotnetCmd = Get-Command 'dotnet.exe' -ErrorAction SilentlyContinue
128 if ($dotnetCmd -ne $null) {
129 $env:DOTNET_INSTALL_DIR = Split-Path $dotnetCmd.Path -Parent
133 $dotnetSdkVersion = $GlobalJson.tools.dotnet
135 # Use dotnet installation specified in DOTNET_INSTALL_DIR if it contains the required SDK version,
136 # otherwise install the dotnet CLI and SDK to repo local .dotnet directory to avoid potential permission issues.
137 if ((-not $globalJsonHasRuntimes) -and ($env:DOTNET_INSTALL_DIR -ne $null) -and (Test-Path(Join-Path $env:DOTNET_INSTALL_DIR "sdk\$dotnetSdkVersion"))) {
138 $dotnetRoot = $env:DOTNET_INSTALL_DIR
139 } else {
140 $dotnetRoot = Join-Path $RepoRoot '.dotnet'
142 if (-not (Test-Path(Join-Path $dotnetRoot "sdk\$dotnetSdkVersion"))) {
143 if ($install) {
144 InstallDotNetSdk $dotnetRoot $dotnetSdkVersion
145 } else {
146 Write-PipelineTelemetryError -Category 'InitializeToolset' -Message "Unable to find dotnet with SDK version '$dotnetSdkVersion'"
147 ExitWithExitCode 1
151 $env:DOTNET_INSTALL_DIR = $dotnetRoot
154 # Creates a temporary file under the toolset dir.
155 # The following code block is protecting against concurrent access so that this function can
156 # be called in parallel.
157 if ($createSdkLocationFile) {
158 do {
159 $sdkCacheFileTemp = Join-Path $ToolsetDir $([System.IO.Path]::GetRandomFileName())
161 until (!(Test-Path $sdkCacheFileTemp))
162 Set-Content -Path $sdkCacheFileTemp -Value $dotnetRoot
164 try {
165 Rename-Item -Force -Path $sdkCacheFileTemp 'sdk.txt'
166 } catch {
167 # Somebody beat us
168 Remove-Item -Path $sdkCacheFileTemp
172 # Add dotnet to PATH. This prevents any bare invocation of dotnet in custom
173 # build steps from using anything other than what we've downloaded.
174 # It also ensures that VS msbuild will use the downloaded sdk targets.
175 $env:PATH = "$dotnetRoot;$env:PATH"
177 # Make Sure that our bootstrapped dotnet cli is available in future steps of the Azure Pipelines build
178 Write-PipelinePrependPath -Path $dotnetRoot
180 Write-PipelineSetVariable -Name 'DOTNET_MULTILEVEL_LOOKUP' -Value '0'
181 Write-PipelineSetVariable -Name 'DOTNET_SKIP_FIRST_TIME_EXPERIENCE' -Value '1'
183 return $global:_DotNetInstallDir = $dotnetRoot
186 function GetDotNetInstallScript([string] $dotnetRoot) {
187 $installScript = Join-Path $dotnetRoot 'dotnet-install.ps1'
188 if (!(Test-Path $installScript)) {
189 Create-Directory $dotnetRoot
190 $ProgressPreference = 'SilentlyContinue' # Don't display the console progress UI - it's a huge perf hit
191 Invoke-WebRequest "https://dot.net/$dotnetInstallScriptVersion/dotnet-install.ps1" -OutFile $installScript
194 return $installScript
197 function InstallDotNetSdk([string] $dotnetRoot, [string] $version, [string] $architecture = '') {
198 InstallDotNet $dotnetRoot $version $architecture
201 function InstallDotNet([string] $dotnetRoot,
202 [string] $version,
203 [string] $architecture = '',
204 [string] $runtime = '',
205 [bool] $skipNonVersionedFiles = $false,
206 [string] $runtimeSourceFeed = '',
207 [string] $runtimeSourceFeedKey = '') {
209 $installScript = GetDotNetInstallScript $dotnetRoot
210 $installParameters = @{
211 Version = $version
212 InstallDir = $dotnetRoot
215 if ($architecture) { $installParameters.Architecture = $architecture }
216 if ($runtime) { $installParameters.Runtime = $runtime }
217 if ($skipNonVersionedFiles) { $installParameters.SkipNonVersionedFiles = $skipNonVersionedFiles }
219 try {
220 & $installScript @installParameters
222 catch {
223 Write-PipelineTelemetryError -Category 'InitializeToolset' -Message "Failed to install dotnet runtime '$runtime' from public location."
225 # Only the runtime can be installed from a custom [private] location.
226 if ($runtime -and ($runtimeSourceFeed -or $runtimeSourceFeedKey)) {
227 if ($runtimeSourceFeed) { $installParameters.AzureFeed = $runtimeSourceFeed }
229 if ($runtimeSourceFeedKey) {
230 $decodedBytes = [System.Convert]::FromBase64String($runtimeSourceFeedKey)
231 $decodedString = [System.Text.Encoding]::UTF8.GetString($decodedBytes)
232 $installParameters.FeedCredential = $decodedString
235 try {
236 & $installScript @installParameters
238 catch {
239 Write-PipelineTelemetryError -Category 'InitializeToolset' -Message "Failed to install dotnet runtime '$runtime' from custom location '$runtimeSourceFeed'."
240 ExitWithExitCode 1
242 } else {
243 ExitWithExitCode 1
249 # Locates Visual Studio MSBuild installation.
250 # The preference order for MSBuild to use is as follows:
252 # 1. MSBuild from an active VS command prompt
253 # 2. MSBuild from a compatible VS installation
254 # 3. MSBuild from the xcopy tool package
256 # Returns full path to msbuild.exe.
257 # Throws on failure.
259 function InitializeVisualStudioMSBuild([bool]$install, [object]$vsRequirements = $null) {
260 if (Test-Path variable:global:_MSBuildExe) {
261 return $global:_MSBuildExe
264 if (!$vsRequirements) { $vsRequirements = $GlobalJson.tools.vs }
265 $vsMinVersionStr = if ($vsRequirements.version) { $vsRequirements.version } else { '15.9' }
266 $vsMinVersion = [Version]::new($vsMinVersionStr)
268 # Try msbuild command available in the environment.
269 if ($env:VSINSTALLDIR -ne $null) {
270 $msbuildCmd = Get-Command 'msbuild.exe' -ErrorAction SilentlyContinue
271 if ($msbuildCmd -ne $null) {
272 # Workaround for https://github.com/dotnet/roslyn/issues/35793
273 # Due to this issue $msbuildCmd.Version returns 0.0.0.0 for msbuild.exe 16.2+
274 $msbuildVersion = [Version]::new((Get-Item $msbuildCmd.Path).VersionInfo.ProductVersion.Split([char[]]@('-', '+'))[0])
276 if ($msbuildVersion -ge $vsMinVersion) {
277 return $global:_MSBuildExe = $msbuildCmd.Path
280 # Report error - the developer environment is initialized with incompatible VS version.
281 throw "Developer Command Prompt for VS $($env:VisualStudioVersion) is not recent enough. Please upgrade to $vsMinVersionStr or build from a plain CMD window"
285 # Locate Visual Studio installation or download x-copy msbuild.
286 $vsInfo = LocateVisualStudio $vsRequirements
287 if ($vsInfo -ne $null) {
288 $vsInstallDir = $vsInfo.installationPath
289 $vsMajorVersion = $vsInfo.installationVersion.Split('.')[0]
291 InitializeVisualStudioEnvironmentVariables $vsInstallDir $vsMajorVersion
292 } else {
294 if (Get-Member -InputObject $GlobalJson.tools -Name 'xcopy-msbuild') {
295 $xcopyMSBuildVersion = $GlobalJson.tools.'xcopy-msbuild'
296 $vsMajorVersion = $xcopyMSBuildVersion.Split('.')[0]
297 } else {
298 $vsMajorVersion = $vsMinVersion.Major
299 $xcopyMSBuildVersion = "$vsMajorVersion.$($vsMinVersion.Minor).0-alpha"
302 $vsInstallDir = $null
303 if ($xcopyMSBuildVersion.Trim() -ine "none") {
304 $vsInstallDir = InitializeXCopyMSBuild $xcopyMSBuildVersion $install
306 if ($vsInstallDir -eq $null) {
307 throw 'Unable to find Visual Studio that has required version and components installed'
311 $msbuildVersionDir = if ([int]$vsMajorVersion -lt 16) { "$vsMajorVersion.0" } else { "Current" }
312 return $global:_MSBuildExe = Join-Path $vsInstallDir "MSBuild\$msbuildVersionDir\Bin\msbuild.exe"
315 function InitializeVisualStudioEnvironmentVariables([string] $vsInstallDir, [string] $vsMajorVersion) {
316 $env:VSINSTALLDIR = $vsInstallDir
317 Set-Item "env:VS$($vsMajorVersion)0COMNTOOLS" (Join-Path $vsInstallDir "Common7\Tools\")
319 $vsSdkInstallDir = Join-Path $vsInstallDir "VSSDK\"
320 if (Test-Path $vsSdkInstallDir) {
321 Set-Item "env:VSSDK$($vsMajorVersion)0Install" $vsSdkInstallDir
322 $env:VSSDKInstall = $vsSdkInstallDir
326 function InstallXCopyMSBuild([string]$packageVersion) {
327 return InitializeXCopyMSBuild $packageVersion -install $true
330 function InitializeXCopyMSBuild([string]$packageVersion, [bool]$install) {
331 $packageName = 'RoslynTools.MSBuild'
332 $packageDir = Join-Path $ToolsDir "msbuild\$packageVersion"
333 $packagePath = Join-Path $packageDir "$packageName.$packageVersion.nupkg"
335 if (!(Test-Path $packageDir)) {
336 if (!$install) {
337 return $null
340 Create-Directory $packageDir
341 Write-Host "Downloading $packageName $packageVersion"
342 $ProgressPreference = 'SilentlyContinue' # Don't display the console progress UI - it's a huge perf hit
343 Invoke-WebRequest "https://dotnet.myget.org/F/roslyn-tools/api/v2/package/$packageName/$packageVersion/" -OutFile $packagePath
344 Unzip $packagePath $packageDir
347 return Join-Path $packageDir 'tools'
351 # Locates Visual Studio instance that meets the minimal requirements specified by tools.vs object in global.json.
353 # The following properties of tools.vs are recognized:
354 # "version": "{major}.{minor}"
355 # Two part minimal VS version, e.g. "15.9", "16.0", etc.
356 # "components": ["componentId1", "componentId2", ...]
357 # Array of ids of workload components that must be available in the VS instance.
358 # See e.g. https://docs.microsoft.com/en-us/visualstudio/install/workload-component-id-vs-enterprise?view=vs-2017
360 # Returns JSON describing the located VS instance (same format as returned by vswhere),
361 # or $null if no instance meeting the requirements is found on the machine.
363 function LocateVisualStudio([object]$vsRequirements = $null){
364 if (Get-Member -InputObject $GlobalJson.tools -Name 'vswhere') {
365 $vswhereVersion = $GlobalJson.tools.vswhere
366 } else {
367 $vswhereVersion = '2.5.2'
370 $vsWhereDir = Join-Path $ToolsDir "vswhere\$vswhereVersion"
371 $vsWhereExe = Join-Path $vsWhereDir 'vswhere.exe'
373 if (!(Test-Path $vsWhereExe)) {
374 Create-Directory $vsWhereDir
375 Write-Host 'Downloading vswhere'
376 Invoke-WebRequest "https://github.com/Microsoft/vswhere/releases/download/$vswhereVersion/vswhere.exe" -OutFile $vswhereExe
379 if (!$vsRequirements) { $vsRequirements = $GlobalJson.tools.vs }
380 $args = @('-latest', '-prerelease', '-format', 'json', '-requires', 'Microsoft.Component.MSBuild', '-products', '*')
382 if (Get-Member -InputObject $vsRequirements -Name 'version') {
383 $args += '-version'
384 $args += $vsRequirements.version
387 if (Get-Member -InputObject $vsRequirements -Name 'components') {
388 foreach ($component in $vsRequirements.components) {
389 $args += '-requires'
390 $args += $component
394 $vsInfo =& $vsWhereExe $args | ConvertFrom-Json
396 if ($lastExitCode -ne 0) {
397 return $null
400 # use first matching instance
401 return $vsInfo[0]
404 function InitializeBuildTool() {
405 if (Test-Path variable:global:_BuildTool) {
406 return $global:_BuildTool
409 if (-not $msbuildEngine) {
410 $msbuildEngine = GetDefaultMSBuildEngine
413 # Initialize dotnet cli if listed in 'tools'
414 $dotnetRoot = $null
415 if (Get-Member -InputObject $GlobalJson.tools -Name 'dotnet') {
416 $dotnetRoot = InitializeDotNetCli -install:$restore
419 if ($msbuildEngine -eq 'dotnet') {
420 if (!$dotnetRoot) {
421 Write-PipelineTelemetryError -Category 'InitializeToolset' -Message "/global.json must specify 'tools.dotnet'."
422 ExitWithExitCode 1
424 $buildTool = @{ Path = Join-Path $dotnetRoot 'dotnet.exe'; Command = 'msbuild'; Tool = 'dotnet'; Framework = 'netcoreapp2.1' }
425 } elseif ($msbuildEngine -eq "vs") {
426 try {
427 $msbuildPath = InitializeVisualStudioMSBuild -install:$restore
428 } catch {
429 Write-PipelineTelemetryError -Category 'InitializeToolset' -Message $_
430 ExitWithExitCode 1
433 $buildTool = @{ Path = $msbuildPath; Command = ""; Tool = "vs"; Framework = "net472" }
434 } else {
435 Write-PipelineTelemetryError -Category 'InitializeToolset' -Message "Unexpected value of -msbuildEngine: '$msbuildEngine'."
436 ExitWithExitCode 1
439 return $global:_BuildTool = $buildTool
442 function GetDefaultMSBuildEngine() {
443 # Presence of tools.vs indicates the repo needs to build using VS msbuild on Windows.
444 if (Get-Member -InputObject $GlobalJson.tools -Name 'vs') {
445 return 'vs'
448 if (Get-Member -InputObject $GlobalJson.tools -Name 'dotnet') {
449 return 'dotnet'
452 Write-PipelineTelemetryError -Category 'InitializeToolset' -Message "-msbuildEngine must be specified, or /global.json must specify 'tools.dotnet' or 'tools.vs'."
453 ExitWithExitCode 1
456 function GetNuGetPackageCachePath() {
457 if ($env:NUGET_PACKAGES -eq $null) {
458 # Use local cache on CI to ensure deterministic build,
459 # use global cache in dev builds to avoid cost of downloading packages.
460 if ($useGlobalNuGetCache) {
461 $env:NUGET_PACKAGES = Join-Path $env:UserProfile '.nuget\packages'
462 } else {
463 $env:NUGET_PACKAGES = Join-Path $RepoRoot '.packages'
467 return $env:NUGET_PACKAGES
470 # Returns a full path to an Arcade SDK task project file.
471 function GetSdkTaskProject([string]$taskName) {
472 return Join-Path (Split-Path (InitializeToolset) -Parent) "SdkTasks\$taskName.proj"
475 function InitializeNativeTools() {
476 if (-Not (Test-Path variable:DisableNativeToolsetInstalls) -And (Get-Member -InputObject $GlobalJson -Name "native-tools")) {
477 $nativeArgs= @{}
478 if ($ci) {
479 $nativeArgs = @{
480 InstallDirectory = "$ToolsDir"
483 & "$PSScriptRoot/init-tools-native.ps1" @nativeArgs
487 function InitializeToolset() {
488 if (Test-Path variable:global:_ToolsetBuildProj) {
489 return $global:_ToolsetBuildProj
492 $nugetCache = GetNuGetPackageCachePath
494 $toolsetVersion = $GlobalJson.'msbuild-sdks'.'Microsoft.DotNet.Arcade.Sdk'
495 $toolsetLocationFile = Join-Path $ToolsetDir "$toolsetVersion.txt"
497 if (Test-Path $toolsetLocationFile) {
498 $path = Get-Content $toolsetLocationFile -TotalCount 1
499 if (Test-Path $path) {
500 return $global:_ToolsetBuildProj = $path
504 if (-not $restore) {
505 Write-PipelineTelemetryError -Category 'InitializeToolset' -Message "Toolset version $toolsetVersion has not been restored."
506 ExitWithExitCode 1
509 $buildTool = InitializeBuildTool
511 $proj = Join-Path $ToolsetDir 'restore.proj'
512 $bl = if ($binaryLog) { '/bl:' + (Join-Path $LogDir 'ToolsetRestore.binlog') } else { '' }
514 '<Project Sdk="Microsoft.DotNet.Arcade.Sdk"/>' | Set-Content $proj
516 MSBuild-Core $proj $bl /t:__WriteToolsetLocation /clp:ErrorsOnly`;NoSummary /p:__ToolsetLocationOutputFile=$toolsetLocationFile
518 $path = Get-Content $toolsetLocationFile -TotalCount 1
519 if (!(Test-Path $path)) {
520 throw "Invalid toolset path: $path"
523 return $global:_ToolsetBuildProj = $path
526 function ExitWithExitCode([int] $exitCode) {
527 if ($ci -and $prepareMachine) {
528 Stop-Processes
530 exit $exitCode
533 function Stop-Processes() {
534 Write-Host 'Killing running build processes...'
535 foreach ($processName in $processesToStopOnExit) {
536 Get-Process -Name $processName -ErrorAction SilentlyContinue | Stop-Process
541 # Executes msbuild (or 'dotnet msbuild') with arguments passed to the function.
542 # The arguments are automatically quoted.
543 # Terminates the script if the build fails.
545 function MSBuild() {
546 if ($pipelinesLog) {
547 $buildTool = InitializeBuildTool
549 # Work around issues with Azure Artifacts credential provider
550 # https://github.com/dotnet/arcade/issues/3932
551 if ($ci -and $buildTool.Tool -eq 'dotnet') {
552 dotnet nuget locals http-cache -c
554 $env:NUGET_PLUGIN_HANDSHAKE_TIMEOUT_IN_SECONDS = 20
555 $env:NUGET_PLUGIN_REQUEST_TIMEOUT_IN_SECONDS = 20
556 Write-PipelineSetVariable -Name 'NUGET_PLUGIN_HANDSHAKE_TIMEOUT_IN_SECONDS' -Value '20'
557 Write-PipelineSetVariable -Name 'NUGET_PLUGIN_REQUEST_TIMEOUT_IN_SECONDS' -Value '20'
560 $toolsetBuildProject = InitializeToolset
561 $path = Split-Path -parent $toolsetBuildProject
562 $path = Join-Path $path (Join-Path $buildTool.Framework 'Microsoft.DotNet.Arcade.Sdk.dll')
563 $args += "/logger:$path"
566 MSBuild-Core @args
570 # Executes msbuild (or 'dotnet msbuild') with arguments passed to the function.
571 # The arguments are automatically quoted.
572 # Terminates the script if the build fails.
574 function MSBuild-Core() {
575 if ($ci) {
576 if (!$binaryLog) {
577 Write-PipelineTelemetryError -Category 'Build' -Message 'Binary log must be enabled in CI build.'
578 ExitWithExitCode 1
581 if ($nodeReuse) {
582 Write-PipelineTelemetryError -Category 'Build' -Message 'Node reuse must be disabled in CI build.'
583 ExitWithExitCode 1
587 $buildTool = InitializeBuildTool
589 $cmdArgs = "$($buildTool.Command) /m /nologo /clp:Summary /v:$verbosity /nr:$nodeReuse /p:ContinuousIntegrationBuild=$ci"
591 if ($warnAsError) {
592 $cmdArgs += ' /warnaserror /p:TreatWarningsAsErrors=true'
594 else {
595 $cmdArgs += ' /p:TreatWarningsAsErrors=false'
598 foreach ($arg in $args) {
599 if ($arg -ne $null -and $arg.Trim() -ne "") {
600 $cmdArgs += " `"$arg`""
604 $exitCode = Exec-Process $buildTool.Path $cmdArgs
606 if ($exitCode -ne 0) {
607 Write-PipelineTelemetryError Category 'Build' -Message 'Build failed.'
609 $buildLog = GetMSBuildBinaryLogCommandLineArgument $args
610 if ($buildLog -ne $null) {
611 Write-Host "See log: $buildLog" -ForegroundColor DarkGray
614 ExitWithExitCode $exitCode
618 function GetMSBuildBinaryLogCommandLineArgument($arguments) {
619 foreach ($argument in $arguments) {
620 if ($argument -ne $null) {
621 $arg = $argument.Trim()
622 if ($arg.StartsWith('/bl:', "OrdinalIgnoreCase")) {
623 return $arg.Substring('/bl:'.Length)
626 if ($arg.StartsWith('/binaryLogger:', 'OrdinalIgnoreCase')) {
627 return $arg.Substring('/binaryLogger:'.Length)
632 return $null
635 . $PSScriptRoot\pipeline-logging-functions.ps1
637 $RepoRoot = Resolve-Path (Join-Path $PSScriptRoot '..\..')
638 $EngRoot = Resolve-Path (Join-Path $PSScriptRoot '..')
639 $ArtifactsDir = Join-Path $RepoRoot 'artifacts'
640 $ToolsetDir = Join-Path $ArtifactsDir 'toolset'
641 $ToolsDir = Join-Path $RepoRoot '.tools'
642 $LogDir = Join-Path (Join-Path $ArtifactsDir 'log') $configuration
643 $TempDir = Join-Path (Join-Path $ArtifactsDir 'tmp') $configuration
644 $GlobalJson = Get-Content -Raw -Path (Join-Path $RepoRoot 'global.json') | ConvertFrom-Json
645 # true if global.json contains a "runtimes" section
646 $globalJsonHasRuntimes = if ($GlobalJson.tools.PSObject.Properties.Name -Match 'runtimes') { $true } else { $false }
648 Create-Directory $ToolsetDir
649 Create-Directory $TempDir
650 Create-Directory $LogDir
652 Write-PipelineSetVariable -Name 'Artifacts' -Value $ArtifactsDir
653 Write-PipelineSetVariable -Name 'Artifacts.Toolset' -Value $ToolsetDir
654 Write-PipelineSetVariable -Name 'Artifacts.Log' -Value $LogDir
655 Write-PipelineSetVariable -Name 'TEMP' -Value $TempDir
656 Write-PipelineSetVariable -Name 'TMP' -Value $TempDir
658 $env:TEMP=$TempDir
659 $env:TMP=$TempDir
661 # Import custom tools configuration, if present in the repo.
662 # Note: Import in global scope so that the script set top-level variables without qualification.
663 if (!$disableConfigureToolsetImport) {
664 $configureToolsetScript = Join-Path $EngRoot 'configure-toolset.ps1'
665 if (Test-Path $configureToolsetScript) {
666 . $configureToolsetScript
667 if ((Test-Path variable:failOnConfigureToolsetError) -And $failOnConfigureToolsetError) {
668 if ((Test-Path variable:LastExitCode) -And ($LastExitCode -ne 0)) {
669 Write-PipelineTelemetryError -Category 'Build' -Message 'configure-toolset.ps1 returned a non-zero exit code'
670 ExitWithExitCode $LastExitCode