build-utils.ps1 11.0 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 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
function GetPublishData() {
  if (Test-Path variable:global:_PublishData) {
    return $global:_PublishData
  }

  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) {
    return $data.branches.$branchName
  } else {
    return $null
  }
}

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

  if (Get-Member -InputObject $data.releases -Name $releaseName) {
    return $data.releases.$releaseName
  } else {
    return $null
  }
}

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 67
function Exec-Block([scriptblock]$cmd) {
    & $cmd
68 69 70 71

    # 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
72
    if ((-not $?) -or ($lastexitcode -ne 0)) {
J
Jared Parsons 已提交
73
        throw "Command failed to execute: $cmd"
J
Jared Parsons 已提交
74 75 76 77 78 79 80
    }
}

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

J
Jared Parsons 已提交
86 87 88 89 90 91 92 93 94
    $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

J
Jared Parsons 已提交
95 96 97 98 99 100
    $process = New-Object System.Diagnostics.Process
    $process.StartInfo = $startInfo
    $process.Start() | Out-Null

    $finished = $false
    try {
J
Jared Parsons 已提交
101 102 103 104 105 106 107 108 109
        # 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 已提交
110 111 112 113 114 115 116 117
        }

        while (-not $process.WaitForExit(100)) { 
            # Non-blocking loop done to allow ctr-c interrupts
        }

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

J
Jared Parsons 已提交
130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152
# 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) {
    Exec-CommandCore -command $command -commandArgs $commandargs -useConsole:$false
}

# 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) {
    Exec-CommandCore -command $command -commandArgs $commandargs -useConsole:$true
}

J
Jared Parsons 已提交
153 154 155 156 157
# 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 = "") {
    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() {
T
Tomáš Matoušek 已提交
162
  return Join-Path (InitializeDotNetCli -install:$true) "dotnet.exe"
J
Jared Parsons 已提交
163 164
}

J
Jared Parsons 已提交
165
function Get-VersionCore([string]$name, [string]$versionFile) {
J
Jared Parsons 已提交
166
    $name = $name.Replace(".", "")
J
Jared Parsons 已提交
167
    $name = $name.Replace("-", "")
J
Jared Parsons 已提交
168
    $nodeName = "$($name)Version"
J
Jared Parsons 已提交
169
    $x = [xml](Get-Content -raw $versionFile)
170 171 172
    $node = $x.SelectSingleNode("//Project/PropertyGroup/$nodeName")
    if ($node -ne $null) {
        return $node.InnerText
J
Jared Parsons 已提交
173 174
    }

J
Jared Parsons 已提交
175 176 177 178 179 180
    throw "Cannot find package $name in $versionFile"

}

# Return the version of the NuGet package as used in this repo
function Get-PackageVersion([string]$name) {
T
Tomáš Matoušek 已提交
181
    return Get-VersionCore $name (Join-Path $EngRoot "Versions.props")
J
Jared Parsons 已提交
182 183 184 185
}

# 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 已提交
186
function Get-PackagesDir() {
J
Jared Parsons 已提交
187 188 189 190 191 192 193 194 195 196 197
    $d = $null
    if ($env:NUGET_PACKAGES -ne $null) {
        $d = $env:NUGET_PACKAGES
    }
    else {
        $d = Join-Path $env:UserProfile ".nuget\packages\"
    }

    Create-Directory $d
    return $d
}
198

J
Jared Parsons 已提交
199 200 201 202 203 204 205 206
# Locate the directory of a specific NuGet package which is restored via our main 
# toolset values.
function Get-PackageDir([string]$name, [string]$version = "") {
    if ($version -eq "") {
        $version = Get-PackageVersion $name
    }

    $p = Get-PackagesDir
207
    $p = Join-Path $p $name.ToLowerInvariant()
J
Jared Parsons 已提交
208 209 210 211
    $p = Join-Path $p $version
    return $p
}

J
Jared Parsons 已提交
212
function Run-MSBuild([string]$projectFilePath, [string]$buildArgs = "", [string]$logFileName = "", [switch]$parallel = $true, [switch]$summary = $true, [switch]$warnAsError = $true, [string]$configuration = $script:configuration) {
T
Tomáš Matoušek 已提交
213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245
    # 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"
    }

    if ($summary) {
        $args += " /consoleloggerparameters:Verbosity=minimal;summary"
    } else {        
        $args += " /consoleloggerparameters:Verbosity=minimal"
    }

    if ($parallel) {
        $args += " /m"
    }

    if ($skipAnalyzers) {
        $args += " /p:UseRoslynAnalyzers=false"
    }

    if ($binaryLog) {
        if ($logFileName -eq "") {
            $logFileName = [IO.Path]::GetFileNameWithoutExtension($projectFilePath)
        }
        $logFileName = [IO.Path]::ChangeExtension($logFileName, ".binlog")
        $logFilePath = Join-Path $LogDir $logFileName
        $args += " /bl:$logFilePath"
    }

246 247
    if ($officialBuildId) {
        $args += " /p:OfficialBuildId=" + $officialBuildId
T
Tomáš Matoušek 已提交
248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277
    }

    if ($ci) {
        $args += " /p:ContinuousIntegrationBuild=true"
    }

    if ($bootstrapDir -ne "") {
        $args += " /p:BootstrapBuildPath=$bootstrapDir"
    }

    $args += " $buildArgs"
    $args += " $projectFilePath"
    $args += " $properties"

    $buildTool = InitializeBuildTool
    Exec-Console $buildTool.Path "$($buildTool.Command) $args"
}

# 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.
function Make-BootstrapBuild() {
    Write-Host "Building bootstrap compiler"

    $dir = Join-Path $ArtifactsDir "Bootstrap"
    Remove-Item -re $dir -ErrorAction SilentlyContinue
    Create-Directory $dir

J
Jared Parsons 已提交
278
    $packageName = "Microsoft.Net.Compilers.Toolset"
T
Tomáš Matoušek 已提交
279 280
    $projectPath = "src\NuGet\$packageName\$packageName.Package.csproj"

T
Tomáš Matoušek 已提交
281
    Run-MSBuild $projectPath "/restore /t:Pack /p:DotNetUseShippingVersions=true /p:InitialDefineConstants=BOOTSTRAP /p:PackageOutputPath=`"$dir`" /p:EnableNgenOptimization=false" -logFileName "Bootstrap" -configuration $bootstrapConfiguration
T
Tomáš Matoušek 已提交
282
    $packageFile = Get-ChildItem -Path $dir -Filter "$packageName.*.nupkg"
T
Tomáš Matoušek 已提交
283 284 285 286 287 288 289
    Unzip "$dir\$packageFile" $dir

    Write-Host "Cleaning Bootstrap compiler artifacts"
    Run-MSBuild $projectPath "/t:Clean" -logFileName "BootstrapClean"

    return $dir
}
S
Sam Harwell 已提交
290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316

Add-Type -AssemblyName 'System.Drawing'
Add-Type -AssemblyName 'System.Windows.Forms'
function Capture-Screenshot($path) {
    $width = [System.Windows.Forms.Screen]::PrimaryScreen.Bounds.Width
    $height = [System.Windows.Forms.Screen]::PrimaryScreen.Bounds.Height

    $bitmap = New-Object System.Drawing.Bitmap $width, $height
    try {
        $graphics = [System.Drawing.Graphics]::FromImage($bitmap)
        try {
            $graphics.CopyFromScreen( `
                [System.Windows.Forms.Screen]::PrimaryScreen.Bounds.X, `
                [System.Windows.Forms.Screen]::PrimaryScreen.Bounds.Y, `
                0, `
                0, `
                $bitmap.Size, `
                [System.Drawing.CopyPixelOperation]::SourceCopy)
        } finally {
            $graphics.Dispose()
        }

        $bitmap.Save($path, [System.Drawing.Imaging.ImageFormat]::Png)
    } finally {
        $bitmap.Dispose()
    }
}