build/common.ps1 (335 lines of code) (raw):
### Constants ###
$NuGetClientRoot = Split-Path -Path $PSScriptRoot -Parent
$CLIRoot = Join-Path $NuGetClientRoot cli
$DotNetInstall = Join-Path $CLIRoot 'dotnet-install.ps1'
$SdkTestingRoot = Join-Path $NuGetClientRoot ".test\dotnet"
$Artifacts = Join-Path $NuGetClientRoot artifacts
$Nupkgs = Join-Path $Artifacts nupkgs
$ConfigureJson = Join-Path $Artifacts configure.json
$BuiltInVsWhereExe = "${Env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe"
$VSVersion = $env:VisualStudioVersion
$DotNetExe = Join-Path $CLIRoot 'dotnet.exe'
Set-Alias dotnet $DotNetExe
Function Read-PackageSources {
param($NuGetConfig)
$xml = New-Object xml
$xml.Load($NuGetConfig)
$xml.SelectNodes('/configuration/packageSources/add') | `
? { $_.key -ne "BuildFeed" } | `
% { $_.value }
}
$PackageSources = Read-PackageSources (Join-Path $NuGetClientRoot 'NuGet.Config')
$OrigBgColor = $host.ui.rawui.BackgroundColor
$OrigFgColor = $host.ui.rawui.ForegroundColor
# MSBUILD has a nasty habit of leaving the foreground color red
Function Reset-Colors {
$host.ui.rawui.BackgroundColor = $OrigBgColor
$host.ui.rawui.ForegroundColor = $OrigFgColor
}
function Format-TeamCityMessage([string]$Text) {
$Text.Replace("|", "||").Replace("'", "|'").Replace("[", "|[").Replace("]", "|]").Replace("`n", "|n").Replace("`r", "|r")
}
Function Trace-Log($TraceMessage = '') {
Write-Host "[$(Trace-Time)]`t$TraceMessage" -ForegroundColor Cyan
}
Function Verbose-Log($VerboseMessage) {
Write-Verbose "[$(Trace-Time)]`t$VerboseMessage"
}
Function Error-Log {
param(
[string]$ErrorMessage,
[switch]$Fatal)
if (-not $Fatal) {
Write-Error "[$(Trace-Time)]`t$ErrorMessage"
}
else {
Write-Error "[$(Trace-Time)]`t[FATAL] $ErrorMessage" -ErrorAction Stop
}
}
Function Warning-Log($WarningMessage) {
Write-Warning "[$(Trace-Time)]`t$WarningMessage"
}
Function Trace-Time() {
$currentTime = Get-Date
$lastTime = $Global:LastTraceTime
$Global:LastTraceTime = $currentTime
"{0:HH:mm:ss} +{1:F0}" -f $currentTime, ($currentTime - $lastTime).TotalSeconds
}
$Global:LastTraceTime = Get-Date
Function Format-ElapsedTime($ElapsedTime) {
'{0:D2}:{1:D2}:{2:D2}' -f $ElapsedTime.Hours, $ElapsedTime.Minutes, $ElapsedTime.Seconds
}
Function Invoke-BuildStep {
[CmdletBinding()]
[Alias('ibs')]
param(
[Parameter(Mandatory = $True)]
[string]$BuildStep,
[Parameter(Mandatory = $True)]
[ScriptBlock]$Expression,
[Parameter(Mandatory = $False)]
[Alias('args')]
[Object[]]$Arguments,
[Alias('skip')]
[switch]$SkipExecution,
[switch]$Critical
)
if (-not $SkipExecution) {
if ($env:TEAMCITY_VERSION) {
Write-Output "##teamcity[blockOpened name='$BuildStep']"
}
Trace-Log "[BEGIN] $BuildStep"
$sw = [Diagnostics.Stopwatch]::StartNew()
$completed = $false
$PwdBefore = $PWD
try {
if (-not $Arguments) {
Invoke-Command $Expression -ErrorVariable err
}
else {
Invoke-Command $Expression -ArgumentList $Arguments -ErrorVariable err
}
$completed = $true
}
finally {
$sw.Stop()
Reset-Colors
if ($PWD -ne $PwdBefore) {
cd $PwdBefore
}
if (-not $err -and $completed) {
Trace-Log "[DONE +$(Format-ElapsedTime $sw.Elapsed)] $BuildStep"
}
elseif (-not $err) {
Trace-Log "[STOPPED +$(Format-ElapsedTime $sw.Elapsed)] $BuildStep"
}
else {
Trace-Log "[FAILED +$(Format-ElapsedTime $sw.Elapsed)] $BuildStep"
}
}
}
else {
Warning-Log "[SKIP] $BuildStep"
}
}
Function Download-DotNetInstallScript {
[CmdletBinding()]
param(
[switch]$Force
)
#If "-force" is specified, or dotnet.exe under cli folder doesn't exist, create cli folder and download dotnet-install.ps1 into cli folder.
if ($Force -or -not (Test-Path $DotNetExe)) {
Trace-Log "Downloading .NET CLI install script"
New-Item -ItemType Directory -Force -Path $CLIRoot | Out-Null
Download-FileWithRetry 'https://dot.net/v1/dotnet-install.ps1' -OutFile $DotNetInstall
}
}
Function Install-DotnetCLI {
[CmdletBinding()]
param(
[switch]$Force,
[switch]$SkipDotnetInfo
)
Download-DotNetInstallScript -Force:$Force
if (-not ([string]::IsNullOrEmpty($env:DOTNET_SDK_VERSIONS))) {
Trace-Log "Using environment variable DOTNET_SDK_VERSIONS instead of DotNetSdkVersions.txt. Value: '$env:DOTNET_SDK_VERSIONS'"
$CliBranchList = $env:DOTNET_SDK_VERSIONS -Split ";"
} else {
$CliBranchList = (Get-Content -Path "$NuGetClientRoot\build\DotNetSdkVersions.txt")
}
ForEach ($CliBranch in $CliBranchList) {
$CliBranch = $CliBranch.trim()
if ($CliBranch.StartsWith("#") -or $CliBranch.Equals("")) {
continue
}
Trace-Log "$DotNetInstall $CliBranch -InstallDir $CLIRoot -NoPath"
& powershell -NoProfile $DotNetInstall $CliBranch -InstallDir $CLIRoot -NoPath
if ($LASTEXITCODE -ne 0)
{
throw "dotnet-install.ps1 exited with non-zero exit code"
}
}
if (-not (Test-Path $DotNetExe)) {
Error-Log "Unable to find dotnet.exe. The CLI install may have failed." -Fatal
}
if ($SkipDotnetInfo -ne $true) {
# Display build info
& $DotNetExe --info
if ($LASTEXITCODE -ne 0)
{
throw "dotnet --info exited with non-zero exit code"
}
}
if ($env:CI -eq "true") {
Write-Host "##vso[task.setvariable variable=DOTNET_ROOT;isOutput=false;issecret=false;]$CLIRoot"
Write-Host "##vso[task.setvariable variable=DOTNET_MULTILEVEL_LOOKUP;isOutput=false;issecret=false;]0"
Write-Host "##vso[task.prependpath]$CLIRoot"
} else {
$env:DOTNET_ROOT=$CLIRoot
$env:DOTNET_MULTILEVEL_LOOKUP=0
if (-not $env:path.Contains($CLIRoot)) {
$env:path = $CLIRoot + ";" + $env:path
}
}
}
Function Install-DotNetSdksForTesting {
[CmdletBinding()]
param(
[switch]$Force
)
Download-DotNetInstallScript -Force:$Force
if (-not ([string]::IsNullOrEmpty($env:DOTNET_SDK_TEST_VERSIONS))) {
Trace-Log "Using environment variable DOTNET_SDK_TEST_VERSIONS instead of DotNetSdkTestVersions.txt. Value: '$env:DOTNET_SDK_TEST_VERSIONS'"
$SdkList = $env:DOTNET_SDK_TEST_VERSIONS -Split ";"
} else {
$SdkList = (Get-Content -Path "$NuGetClientRoot\build\DotNetSdkTestVersions.txt")
}
ForEach ($SdkItem in $SdkList) {
$SdkItem = $SdkItem.trim()
if ($SdkItem.StartsWith("#") -or $SdkItem.Equals("")) {
continue
}
if ([Environment]::Is64BitOperatingSystem) {
$arch = "x64";
}
else {
$arch = "x86";
}
Trace-Log "$DotNetInstall $SdkItem -InstallDir $SdkTestingRoot -Architecture $arch -NoPath"
& powershell -NoProfile $DotNetInstall $SdkItem -InstallDir $SdkTestingRoot -Architecture $arch -NoPath
if ($LASTEXITCODE -ne 0)
{
throw "dotnet-install.ps1 exited with non-zero exit code"
}
}
}
Function Get-LatestVisualStudioRoot {
if (Test-Path $BuiltInVsWhereExe) {
$installationPath = & $BuiltInVsWhereExe -latest -prerelease -property installationPath
$installationVersion = & $BuiltInVsWhereExe -latest -prerelease -property installationVersion
Verbose-Log "Found Visual Studio at '$installationPath' version '$installationVersion' with '$BuiltInVsWhereExe'"
# Set the fallback version
$majorVersion = "$installationVersion".Split('.')[0]
$script:FallbackVSVersion = "$majorVersion.0"
return $installationPath
}
Error-Log "Could not find a compatible Visual Studio Version because $BuiltInVsWhereExe does not exist" -Fatal
}
<#
.DESCRIPTION
Finds a suitable VSVersion based on the environment configuration,
if $VSVersion is set, that means we're running in a developer command prompt so we prefer that.
otherwise we pick the latest Visual Studio version available on the machine.
#>
Function Get-VSVersion() {
if (-not $VSVersion) {
if (-not $script:FallbackVSVersion) {
Verbose-Log "No fallback VS Version set yet. This means that we are running outside of a developer command prompt scope."
$_ = Get-LatestVisualStudioRoot
}
Verbose-Log "Using the fallback VS version '$script:FallbackVSVersion'"
$VSVersion = $script:FallbackVSVersion
}
return $VSVersion
}
Function Get-MSBuildExe {
# If there's a msbuild.exe on the path, use it.
if ($null -ne (Get-Command "msbuild.exe" -ErrorAction Ignore))
{
return "msbuild.exe"
}
# Otherwise, use VSWhere.exe to find the latest MSBuild.exe.
$MSBuildExe = & "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe" -latest -prerelease -requires Microsoft.Component.MSBuild -find MSBuild\**\bin\MSBuild.exe
if (Test-Path $MSBuildExe) {
Verbose-Log "Found MSBuild.exe at `"$MSBuildExe`""
return $MSBuildExe
}
else {
Error-Log 'Could not find MSBuild.exe' -Fatal
}
}
Function Test-BuildEnvironment {
$Installed = (Test-Path $DotNetExe)
if (-not $Installed) {
Error-Log 'Build environment is not configured. Please run configure.ps1 first.' -Fatal
}
}
Function Install-ProcDump {
[CmdletBinding()]
param()
if ($Env:OS -eq "Windows_NT")
{
Trace-Log "Downloading ProcDump..."
$ProcDumpZip = Join-Path $env:TEMP 'ProcDump.zip'
$TestDir = Join-Path $NuGetClientRoot '.test'
$ProcDumpDir = Join-Path $TestDir 'ProcDump'
Download-FileWithRetry 'https://download.sysinternals.com/files/Procdump.zip' -OutFile $ProcDumpZip
if (Test-Path $ProcDumpDir) {
Remove-Item $ProcDumpDir -Recurse -Force | Out-Null
}
New-Item $ProcDumpDir -ItemType Directory -Force | Out-Null
Expand-Archive $ProcDumpZip -DestinationPath $ProcDumpDir
if ($env:CI -eq "true") {
Write-Host "##vso[task.setvariable variable=PROCDUMP_PATH;isOutput=false;issecret=false;]$ProcDumpDir"
} else {
$env:PROCDUMP_PATH=$ProcDumpDir
}
}
}
Function Clear-PackageCache {
[CmdletBinding()]
param()
Trace-Log 'Cleaning local caches'
& dotnet nuget locals all --clear
}
Function Clear-Artifacts {
[CmdletBinding()]
param()
if ( Test-Path $Artifacts) {
Trace-Log 'Cleaning the Artifacts folder'
Remove-Item $Artifacts\* -Recurse -Force -Exclude 'configure.json'
}
}
Function Clear-Nupkgs {
[CmdletBinding()]
param()
if (Test-Path $Nupkgs) {
Trace-Log 'Cleaning nupkgs folder'
Remove-Item $Nupkgs\*.nupkg -Force
}
}
Function Download-FileWithRetry {
param
(
[Parameter(Mandatory = $true)]
[string] $Uri,
[Parameter(Mandatory = $true)]
[string] $OutFile,
[Parameter(Mandatory = $false)]
[int] $Retries = 5
)
while($true)
{
try
{
Trace-Log "Downloading '$Uri' to '$OutFile'"
Invoke-WebRequest $Uri -OutFile $OutFile
break
}
catch
{
$exceptionMessage = $_.Exception.Message
Warning-Log "Failed to download '$Uri': $exceptionMessage"
if ($Retries -gt 0) {
$Retries--
Trace-Log "Waiting 10 seconds before retrying. Retries left: $Retries"
Start-Sleep -Seconds 10
}
else
{
$exception = $_.Exception
throw $exception
}
}
}
}