visualfsharp/eng/build-utils.ps1

275 строки
10 KiB
PowerShell

# Collection of powershell build utility functions that we use across our scripts.
Set-StrictMode -version 2.0
$ErrorActionPreference="Stop"
# Import Arcade functions
. (Join-Path $PSScriptRoot "common\tools.ps1")
$VSSetupDir = Join-Path $ArtifactsDir "VSSetup\$configuration"
$PackagesDir = Join-Path $ArtifactsDir "packages\$configuration"
$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 { "" }
$bootstrapConfiguration = if (Test-Path variable:bootstrapConfiguration) { $bootstrapConfiguration } else { "Proto" }
$bootstrapTfm = if (Test-Path variable:bootstrapTfm) { $bootstrapTfm } else { "net472" }
$properties = if (Test-Path variable:properties) { $properties } else { @() }
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"
}
# Handy function for executing a command in powershell and throwing if it
# fails.
#
# 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 }
#
# Original sample came from: http://jameskovacs.com/2010/02/25/the-exec-problem/
function Exec-Block([scriptblock]$cmd) {
& $cmd
# 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"
}
}
function Exec-CommandCore([string]$command, [string]$commandArgs, [switch]$useConsole = $true) {
if ($useConsole) {
$exitCode = Exec-Process $command $commandArgs
if ($exitCode -ne 0) {
throw "Command failed to execute with exit code $($exitCode): $command $commandArgs"
}
return
}
$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
}
while (-not $process.WaitForExit(100)) {
# Non-blocking loop done to allow ctr-c interrupts
}
$finished = $true
if ($process.ExitCode -ne 0) {
throw "Command failed to execute with exit code $($process.ExitCode): $command $commandArgs"
}
}
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()
}
}
}
# 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
}
# 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"
}
# Ensure the proper .NET Core SDK is available. Returns the location to the dotnet.exe.
function Ensure-DotnetSdk() {
return Join-Path (InitializeDotNetCli -install:$true) "dotnet.exe"
}
function Get-VersionCore([string]$name, [string]$versionFile) {
$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
}
throw "Cannot find package $name in $versionFile"
}
# Return the version of the NuGet package as used in this repo
function Get-PackageVersion([string]$name) {
return Get-VersionCore $name (Join-Path $EngRoot "Versions.props")
}
# Locate the directory where our NuGet packages will be deployed. Needs to be kept in sync
# with the logic in Version.props
function Get-PackagesDir() {
$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
}
# 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
$p = Join-Path $p $name.ToLowerInvariant()
$p = Join-Path $p $version
return $p
}
function Run-MSBuild([string]$projectFilePath, [string]$buildArgs = "", [string]$logFileName = "", [switch]$parallel = $true, [switch]$summary = $true, [switch]$warnAsError = $true, [string]$configuration = $script:configuration, [string]$verbosity = $script:verbosity) {
# 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 ($binaryLog) {
if ($logFileName -eq "") {
$logFileName = [IO.Path]::GetFileNameWithoutExtension($projectFilePath)
}
$logFileName = [IO.Path]::ChangeExtension($logFileName, ".binlog")
$logFilePath = Join-Path $LogDir $logFileName
$args += " /bl:$logFilePath"
}
if ($official) {
$args += " /p:OfficialBuildId=" + $env:BUILD_BUILDNUMBER
}
if ($ci) {
$args += " /p:ContinuousIntegrationBuild=true"
}
$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 '$bootstrapTfm' compiler"
$dir = Join-Path $ArtifactsDir "Bootstrap"
Remove-Item -re $dir -ErrorAction SilentlyContinue
Create-Directory $dir
# prepare FsLex and Fsyacc and AssemblyCheck
$dotnetPath = InitializeDotNetCli
$dotnetExe = Join-Path $dotnetPath "dotnet.exe"
$buildToolsProject = "`"$RepoRoot\src\buildtools\buildtools.proj`""
$argNoRestore = if ($norestore) { " --no-restore" } else { "" }
$argNoIncremental = if ($rebuild) { " --no-incremental" } else { "" }
$args = "build $buildToolsProject -c $bootstrapConfiguration -v $verbosity -f netcoreapp3.1" + $argNoRestore + $argNoIncremental
if ($binaryLog) {
$logFilePath = Join-Path $LogDir "toolsBootstrapLog.binlog"
$args += " /bl:$logFilePath"
}
Exec-Console $dotnetExe $args
Copy-Item "$ArtifactsDir\bin\fslex\$bootstrapConfiguration\netcoreapp3.1" -Destination "$dir\fslex" -Force -Recurse
Copy-Item "$ArtifactsDir\bin\fsyacc\$bootstrapConfiguration\netcoreapp3.1" -Destination "$dir\fsyacc" -Force -Recurse
Copy-Item "$ArtifactsDir\bin\AssemblyCheck\$bootstrapConfiguration\netcoreapp3.1" -Destination "$dir\AssemblyCheck" -Force -Recurse
# prepare compiler
$protoProject = "`"$RepoRoot\proto.proj`""
$args = "build $protoProject -c $bootstrapConfiguration -v $verbosity -f $bootstrapTfm" + $argNoRestore + $argNoIncremental
if ($binaryLog) {
$logFilePath = Join-Path $LogDir "protoBootstrapLog.binlog"
$args += " /bl:$logFilePath"
}
Exec-Console $dotnetExe $args
Copy-Item "$ArtifactsDir\bin\fsc\$bootstrapConfiguration\$bootstrapTfm" -Destination "$dir\fsc" -Force -Recurse
Copy-Item "$ArtifactsDir\bin\fsi\$bootstrapConfiguration\$bootstrapTfm" -Destination "$dir\fsi" -Force -Recurse
return $dir
}