Kaynağa Gözat

Port "MicroBuild v2: Enable CodeCoverage." to release/1.1

Also refactor to enable all build definitions, including POGO for NuGet publication, to be written in a consistent way using similar scripts and parameters.
Doug Ilijev 9 yıl önce
ebeveyn
işleme
8b7e00975a

+ 0 - 1
Build/Common.Build.Default.props

@@ -43,7 +43,6 @@
   <!-- Change configuration name to output to a different folder -->
   <!-- POGO instrumentation -->
   <PropertyGroup>
-    <IsPogoBuild />
     <IsPogoBuild Condition="('$(POGO_TYPE)'=='PGO' OR '$(POGO_TYPE)'=='PGI')">true</IsPogoBuild>
     <OutDirName Condition="'$(IsPogoBuild)'=='true'">$(Configuration.ToLower())_pogo</OutDirName>
   </PropertyGroup>

+ 65 - 0
Build/scripts/add_msbuild_path.cmd

@@ -0,0 +1,65 @@
+::-------------------------------------------------------------------------------------------------------
+:: Copyright (C) Microsoft. All rights reserved.
+:: Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+::-------------------------------------------------------------------------------------------------------
+
+:: add_msbuild_path.cmd
+::
+:: Locate msbuild.exe and add it to the PATH
+
+set USE_MSBUILD_12=%1
+
+if "%USE_MSBUILD_12%" == "True" (
+    echo Skipping Dev14 and trying Dev12...
+    goto :LABEL_USE_MSBUILD_12
+)
+
+where /q msbuild.exe
+if "%ERRORLEVEL%" == "0" (
+    goto :SkipMsBuildSetup
+)
+
+REM Try Dev14 first
+set MSBUILD_VERSION=14.0
+set MSBUILD_PATH="%ProgramFiles%\msbuild\%MSBUILD_VERSION%\Bin\x86"
+
+if not exist %MSBUILD_PATH%\msbuild.exe (
+    set MSBUILD_PATH="%ProgramFiles(x86)%\msbuild\%MSBUILD_VERSION%\Bin"
+)
+
+if not exist %MSBUILD_PATH%\msbuild.exe (
+    set MSBUILD_PATH="%ProgramFiles(x86)%\msbuild\%MSBUILD_VERSION%\Bin\amd64"
+)
+
+if exist %MSBUILD_PATH%\msbuild.exe (
+    goto :MSBuildFound
+)
+
+echo Dev14 not found, trying Dev12...
+
+:LABEL_USE_MSBUILD_12
+set MSBUILD_VERSION=12.0
+set MSBUILD_PATH="%ProgramFiles%\msbuild\%MSBUILD_VERSION%\Bin\x86"
+echo Dev14 not found, trying Dev %MSBUILD_VERSION%
+
+if not exist %MSBUILD_PATH%\msbuild.exe (
+    set MSBUILD_PATH="%ProgramFiles(x86)%\msbuild\%MSBUILD_VERSION%\Bin"
+)
+
+if not exist %MSBUILD_PATH%\msbuild.exe (
+    set MSBUILD_PATH="%ProgramFiles(x86)%\msbuild\%MSBUILD_VERSION%\Bin\amd64"
+)
+
+if not exist %MSBUILD_PATH%\msbuild.exe (
+    echo Can't find msbuild.exe in %MSBUILD_PATH%
+    goto :SkipMsBuildSetup
+)
+
+:MSBuildFound
+echo MSBuild located at %MSBUILD_PATH%
+
+set PATH=%MSBUILD_PATH%;%PATH%
+set USE_MSBUILD_12=
+set MSBUILD_PATH=
+
+:SkipMsBuildSetup

+ 16 - 4
Build/scripts/compose_build.ps1

@@ -17,17 +17,29 @@ param (
 # Aggregate build metadata and produce build.json
 #
 
-$outputJsonFile = Join-Path -Path $rootPath -ChildPath "build.json"
+$outputJsonFile = Join-Path $rootPath "build.json"
 $buildInfo = New-Object System.Object
 
-$changeJson = (Get-ChildItem -Path $rootPath "change.json" -Recurse)[0] | % { $_.FullName }
+$changeJson = (Get-ChildItem -Path $rootPath "change.json" -Recurse)[0].FullName
+$changeText = (Get-ChildItem -Path $rootPath "change.txt"  -Recurse)[0].FullName
+Copy-Item -Verbose -Force -Path $changeJson -Destination $rootPath
+Copy-Item -Verbose -Force -Path $changeText -Destination $rootPath
+
 $changeInfo = (Get-Content $changeJson) -join "`n" | ConvertFrom-Json
 
+# Recursively locate ${arch}_${flavor}.json and move to $rootPath.
+# This ensures that in the rebuild scenario, we don't have duplication of *.json files
+# between the partially-composed root and the metadata directories.
+
+Get-ChildItem -Path $rootPath "*.json" -Recurse `
+    | ? { -not ($_.Name -in @("change.json", "build.json")) } `
+    | % { Move-Item -Verbose -Force -Path $_.FullName -Destination $rootPath }
+
 # Determine the overall build status. Mark the build as "passed" until "failed" is encountered.
 $overallBuildStatus = "passed"
 
 $files = Get-ChildItem -Path $rootPath "*.json" -Recurse `
-    | ? { ($_.Name -ne "change.json") -and ($_.Name -ne "build.json") } `
+    | ? { -not ($_.Name -in @("change.json", "build.json")) } `
     | % { $_.FullName }
 $builds = New-Object System.Collections.ArrayList
 foreach ($file in $files) {
@@ -44,4 +56,4 @@ $buildInfo | Add-Member -type NoteProperty -name change -value $changeInfo
 $buildInfo | Add-Member -type NoteProperty -name builds -value $builds
 
 $buildInfo | ConvertTo-Json | Write-Output
-$buildInfo | ConvertTo-Json | Out-File $outputJsonFile -Encoding ascii
+$buildInfo | ConvertTo-Json | Out-File $outputJsonFile -Encoding Ascii

+ 82 - 20
Build/scripts/finalize_build.ps1

@@ -8,16 +8,12 @@
 # This script is run as the final step in a build definition
 # to clean up and produce metadata about the build.
 
-#
-# Copy _pogo binary folder, if present.
-#
+param (
+    $corePathSegment = "" # e.g. "core"
+)
 
-$PogoFolder = Join-Path $Env:BinariesDirectory "bin\${Env:BuildType}_pogo"
-if (Test-Path $PogoFolder) {
-    $BinDropPath = Join-Path $Env:DropPath "bin"
-    Write-Output "Copying `"$PogoFolder`" to `"$BinDropPath`"..."
-    Copy-Item $PogoFolder $BinDropPath -Recurse -Force
-}
+$sourcesDir = $Env:BUILD_SOURCESDIRECTORY
+$coreSourcesDir = Join-Path $sourcesDir $corePathSegment
 
 #
 # Clean up the sentinel which previously marked this build flavor as incomplete.
@@ -25,30 +21,96 @@ if (Test-Path $PogoFolder) {
 
 Remove-Item -Path $Env:FlavorBuildIncompleteFile -Force
 
+#
+# Copy all logs to DropPath
+#
+
+$buildlogsDropPath = Join-Path $Env:DropPath "buildlogs"
+$logsDropPath = Join-Path $Env:DropPath "logs"
+$testlogsDropPath = Join-Path $Env:DropPath "testlogs"
+
+New-Item -Force -ItemType Directory -Path $buildlogsDropPath
+New-Item -Force -ItemType Directory -Path $logsDropPath
+New-Item -Force -ItemType Directory -Path $testlogsDropPath
+
+$buildlogsSourcePath = Join-Path $Env:BinariesDirectory "buildlogs"
+$logsSourcePath = Join-Path $Env:BinariesDirectory "logs"
+$testlogsSourcePath = Join-Path $Env:BinariesDirectory "testlogs"
+
+if (Test-Path $buildlogsSourcePath) {
+    Copy-Item -Verbose -Recurse -Force (Join-Path $buildlogsSourcePath "*") $buildlogsDropPath
+}
+if (Test-Path $logsSourcePath) {
+    Copy-Item -Verbose -Recurse -Force (Join-Path $logsSourcePath "*") $logsDropPath
+}
+if (Test-Path $testlogsSourcePath) {
+    Copy-Item -Verbose -Recurse -Force (Join-Path $testlogsSourcePath "*") $testlogsDropPath
+}
+
 #
 # Create build status JSON file for this flavor.
 #
 
-$BuildLogsPath = Join-Path $Env:DropPath "buildlogs"
-$buildFlavorErrFile = Join-Path $BuildLogsPath "build_${Env:BuildPlatform}${Env:BuildConfiguration}.err"
+$buildErrFile = Join-Path $buildLogsDropPath "build.${Env:BuildName}.err"
+$testSummaryFile = Join-Path $testLogsDropPath "summary.${Env:BuildName}.log"
 
-# If build_{}{}.err contains any text then there were build errors and we record that the build failed.
-$BuildFailed = $false
-if (Test-Path $buildFlavorErrFile) {
-    $BuildFailed = (Get-Item $buildFlavorErrFile).length -gt 0
+# if build.*.err contains any text then there were build errors
+$BuildSucceeded = $true
+if (Test-Path $buildErrFile) {
+    # build errors file has non-zero length iff the build failed
+    $BuildSucceeded = (Get-Item $buildErrFile).length -eq 0
 }
 
-$status = "passed"
-if ($BuildFailed) {
-    $status = "failed"
+# if summary.*.err contains any text then there were test failures
+$TestsPassed = $true
+if (Test-Path $testSummaryFile) {
+    # summary file has non-zero length iff the tests failed
+    $TestsPassed = (Get-Item $testSummaryFile).length -eq 0
 }
 
-$buildFlavorJsonFile = Join-Path $Env:DropPath "${Env:BuildType}.json"
+$Status = "passed"
+if ((-not $BuildSucceeded) -or (-not $TestsPassed)) {
+    $Status = "failed"
+}
+
+$buildFlavorJsonFile = Join-Path $Env:DropPath "${Env:BuildName}.json"
 $buildFlavorJson = New-Object System.Object
 
-$buildFlavorJson | Add-Member -type NoteProperty -name status -value $status
+$buildFlavorJson | Add-Member -type NoteProperty -name buildName -value $Env:BuildName
+$buildFlavorJson | Add-Member -type NoteProperty -name status -value $Status
+$buildFlavorJson | Add-Member -type NoteProperty -name BuildSucceeded -value $BuildSucceeded
+$buildFlavorJson | Add-Member -type NoteProperty -name testsPassed -value $TestsPassed
 $buildFlavorJson | Add-Member -type NoteProperty -name arch -value $Env:BuildPlatform
 $buildFlavorJson | Add-Member -type NoteProperty -name flavor -value $Env:BuildConfiguration
+$buildFlavorJson | Add-Member -type NoteProperty -name subtype -value $Env:BuildSubtype
 
 $buildFlavorJson | ConvertTo-Json | Write-Output
 $buildFlavorJson | ConvertTo-Json | Out-File $buildFlavorJsonFile -Encoding ascii
+
+#
+# Copy outputs to metadata directory
+#
+
+$metadataDir = Join-Path $coreSourcesDir "metadata"
+New-Item -Force -ItemType Directory -Path $metadataDir
+
+Copy-Item -Verbose -Force (Join-Path $sourcesDir "ComputedEnvironment.cmd") $metadataDir
+Copy-Item -Verbose -Force (Join-Path $coreSourcesDir "Build\scripts\compose_build.ps1") $metadataDir
+Copy-Item -Verbose -Force (Join-Path $Env:BinariesDirectory "change.json") $metadataDir
+Copy-Item -Verbose -Force (Join-Path $Env:BinariesDirectory "change.txt") $metadataDir
+Copy-Item -Verbose -Force $buildFlavorJsonFile $metadataDir
+
+# Search for *.nuspec files and copy them to $metadataDir
+Get-ChildItem -Path (Join-Path $sourcesDir "Build") "*.nuspec" `
+    | % { Copy-Item -Verbose -Force $_.FullName $metadataDir }
+
+#
+# Copy POGO directory if present for this build
+#
+
+$PogoFolder = Join-Path $Env:BinariesDirectory "bin\${Env:BuildType}_pogo"
+if (Test-Path $PogoFolder) {
+    $BinDropPath = Join-Path $Env:DropPath "bin"
+    Write-Output "Copying `"$PogoFolder`" to `"$BinDropPath`"..."
+    Copy-Item -Verbose $PogoFolder $BinDropPath -Recurse -Force
+}

+ 110 - 51
Build/scripts/init_build.ps1

@@ -8,62 +8,99 @@
 # Run this as the very first step in the build to configure the environment.
 # This is distinct from the Pre-Build script as there may be more non-script steps that need to be
 # taken before setting up and running the build.
-# For example, this script creates a cmd script which should be run to initialize environment variables.
+# For example, this script creates a cmd script which should be run to initialize environment variables
+# before running the Pre-Build script.
 
 param (
-    [string]$envconfig = "ComputedEnvironment.cmd",
+    [string]$envConfigScript = "ComputedEnvironment.cmd",
 
-    [string[]]$SupportedPogoBuildTypes = @("x64_release", "x86_release"),
+    [string[]]$supportedPogoBuildTypes = @("x64_release", "x86_release"),
 
     [Parameter(Mandatory=$True)]
     [string]$oauth
 )
 
-$branch = $env:BUILD_SOURCEBRANCH
+# If $Env:BuildType is specified, extract BuildPlatform and BuildConfiguration
+# Otherwise, if $Env:BuildPlatform and $Env:BuildConfiguration are specified, construct $BuildType
+$BuildPlatform = $Env:BuildPlatform
+$BuildConfiguration = $Env:BuildConfiguration
+$BuildType = $Env:BuildType
+$BuildSubtype = "default" # will remain as "default" or become e.g. "pogo", "codecoverage"
+
+if (Test-Path Env:\BuildType) {
+    $BuildType = $Env:BuildType
+    $buildTypeSegments = $BuildType.split("_")
+    $BuildPlatform = $buildTypeSegments[0]
+    $BuildConfiguration = $buildTypeSegments[1]
+    if ($buildTypeSegments[2]) {
+        # overwrite with new value if it exists, otherwise keep as "default"
+        $BuildSubtype = $buildTypeSegments[2]
+    }
+
+    if ($BuildConfiguration -eq "codecoverage") {
+        $BuildConfiguration = "test" # codecoverage builds are actually "test" configuration
+        $BuildSubtype = "codecoverage" # keep information about codecoverage in the subtype
+    }
+
+    if (-not ($BuildSubtype -in @("default","pogo","codecoverage"))) {
+        Write-Error "Unsupported BuildSubtype: $BuildSubtype"
+    }
+} elseif ((Test-Path Env:\BuildPlatform) -and (Test-Path Env:\BuildConfiguration)) {
+    $BuildPlatform = $Env:BuildPlatform
+    $BuildConfiguration = $Env:BuildConfiguration
+    $BuildType = "${BuildPlatform}_${BuildConfiguration}"
+} else {
+    Write-Error (@"
+
+    Not enough information about BuildType:
+        BuildType={0}
+        BuildPlatform={1}
+        BuildConfiguration={2}
+
+"@ -f $Env:BuildType, $Env:BuildPlatform, $Env:BuildConfiguration)
+
+    exit 1
+}
+
+# set up required variables and import pre_post_util.ps1
+$arch = $BuildPlatform
+$flavor = $BuildConfiguration
+$OuterScriptRoot = $PSScriptRoot # Used in pre_post_util.ps1
+. "$PSScriptRoot\pre_post_util.ps1"
+
+$gitExe = GetGitPath
+
+$BuildName = ConstructBuildName -arch $BuildPlatform -flavor $BuildConfiguration -subtype $BuildSubtype
+
+$branch = $Env:BUILD_SOURCEBRANCH
 if (-not $branch) {
-    $branch = $(git rev-parse --symbolic-full-name HEAD)
+    $branch = iex "$gitExe rev-parse --symbolic-full-name HEAD"
 }
 
 $BranchName = $branch.split('/',3)[2]
 $BranchPath = $BranchName.replace('/','\')
-$CommitHash = $env:BUILD_SOURCEVERSION
+$CommitHash = $Env:BUILD_SOURCEVERSION
 if (-not $CommitHash) {
-    $CommitHash = $(git rev-parse HEAD)
+    $CommitHash = iex "$gitExe rev-parse HEAD"
 }
 
-$Username = $(git log $CommitHash -1 --pretty=%ae).split('@')[0]
-$CommitDateTime = [DateTime]$(git log $CommitHash -1 --pretty=%aD)
+$Username = (iex "$gitExe log $CommitHash -1 --pretty=%ae").split('@')[0]
+$CommitDateTime = [DateTime]$(iex "$gitExe log $CommitHash -1 --pretty=%aD")
 $CommitTime = Get-Date $CommitDateTime -Format yyMMdd.HHmm
 
 #
-# (borrowed from pre_build.ps1)
-# Get PushID and PushDate from VSO
-# TODO (doilij) refactor this into a method in a util script.
+# Get Build Info
 #
 
-# Get the git remote path and construct the rest API URI
-$remote = (iex "git remote -v")[0].split()[1].replace("_git", "_apis/git/repositories");
-$remote = $remote.replace("mshttps", "https")
-
-# Get the pushId and push date time to use that for build number and build date time
-$uri = ("{0}/commits/{1}?api-version=1.0" -f $remote, $commitHash)
-$oauthToken = Get-Content $oauth
-$header = @{ Authorization=("Basic {0}" -f $oauthToken) }
-$info = Invoke-RestMethod -Headers $header -Uri $uri -Method GET
+$info = GetBuildInfo $oauth $CommitHash
 
 $BuildPushDate = [datetime]$info.push.date
 $PushDate = Get-Date $BuildPushDate -Format yyMMdd.HHmm
-$buildPushId = $info.push.pushId
-$buildPushIdPart1 = [int]([math]::Floor($buildPushId / 65536))
-$buildPushIdPart2 = [int]($buildPushId % 65536)
 
-$PushID = "{0}.{1}" -f $buildPushIdPart1.ToString("00000"), $buildPushIdPart2.ToString("00000")
-$VersionString = "${Env:VERSION_MAJOR}.${Env:VERSION_MINOR}.${PushID}"
-$PreviewVersionString = "${VersionString}-preview"
+$buildPushId, $buildPushIdPart1, $buildPushIdPart2, $buildPushIdString = GetBuildPushId $info
 
-#
-# (end code borrowed from pre_build.ps1)
-#
+$VersionString = "${Env:VERSION_MAJOR}.${Env:VERSION_MINOR}.${buildPushIdString}"
+$PreviewVersionString = "${VersionString}-preview"
 
 # unless it is a build branch, subdivide the output directory by month
 if ($BranchPath.StartsWith("build")) {
@@ -72,36 +109,35 @@ if ($BranchPath.StartsWith("build")) {
     $YearAndMonth = (Get-Date $BuildPushDate -Format yyMM) + "\"
 }
 
-$BuildIdentifier = "${PushID}_${PushDate}_${Username}_${CommitHash}"
+$BuildIdentifier = "${buildPushIdString}_${PushDate}_${Username}_${CommitHash}"
 $ComputedDropPathSegment = "${BranchPath}\${YearAndMonth}${BuildIdentifier}"
-$BuildType = "${Env:BuildPlatform}_${Env:BuildConfiguration}"
 $BinariesDirectory = "${Env:BUILD_SOURCESDIRECTORY}\Build\VcBuild"
 
 # Create a sentinel file for each build flavor to track whether the build is complete.
 # * ${arch}_${flavor}.incomplete       # will be deleted when the build of this flavor completes
 
 $buildIncompleteFileContentsString = @"
-{0} in progress.
+{0} is incomplete.
+This could mean that the build is in progress, or that it was unable to run to completion.
 The contents of this directory should not be relied on until the build completes.
 "@
 
-$DropPath = Join-Path $env:DROP_ROOT $ComputedDropPathSegment
+$DropPath = Join-Path $Env:DROP_ROOT $ComputedDropPathSegment
 New-Item -ItemType Directory -Force -Path $DropPath
 New-Item -ItemType Directory -Force -Path (Join-Path $Env:BUILD_SOURCESDIRECTORY "test\logs")
-New-Item -ItemType Directory -Force -Path (Join-Path $BinariesDirectory "testlogs")
 New-Item -ItemType Directory -Force -Path (Join-Path $BinariesDirectory "buildlogs")
 New-Item -ItemType Directory -Force -Path (Join-Path $BinariesDirectory "logs")
 
-$flavorBuildIncompleteFile = Join-Path $DropPath "${BuildType}.incomplete"
+$FlavorBuildIncompleteFile = Join-Path $DropPath "${BuildType}.incomplete"
 
-if (-not (Test-Path $flavorBuildIncompleteFile)) {
+if (-not (Test-Path $FlavorBuildIncompleteFile)) {
     ($buildIncompleteFileContentsString -f "Build of ${BuildType}") `
-        | Out-File $flavorBuildIncompleteFile -Encoding Ascii
+        | Out-File $FlavorBuildIncompleteFile -Encoding Ascii
 }
 
-$PogoConfig = $SupportedPogoBuildTypes -contains "${Env:BuildPlatform}_${Env:BuildConfiguration}"
+$PogoConfig = $supportedPogoBuildTypes -contains "${Env:BuildPlatform}_${Env:BuildConfiguration}"
 
-# Write the $envconfig script.
+# Write the $envConfigScript
 
 @"
 set BranchName=${BranchName}
@@ -109,7 +145,7 @@ set BranchPath=${BranchPath}
 set YearAndMonth=${YearAndMonth}
 set BuildIdentifier=${BuildIdentifier}
 
-set PushID=${PushID}
+set buildPushIdString=${buildPushIdString}
 set VersionString=${VersionString}
 set PreviewVersionString=${PreviewVersionString}
 set PushDate=${PushDate}
@@ -120,30 +156,49 @@ set CommitHash=${CommitHash}
 set ComputedDropPathSegment=${ComputedDropPathSegment}
 set BinariesDirectory=${BinariesDirectory}
 set DropPath=${DropPath}
+
 set BuildType=${BuildType}
+set BuildPlatform=${BuildPlatform}
+set BuildConfiguration=${BuildConfiguration}
+set BuildSubtype=${BuildSubtype}
+set BuildName=${BuildName}
 
-set FlavorBuildIncompleteFile=${flavorBuildIncompleteFile}
+set FlavorBuildIncompleteFile=${FlavorBuildIncompleteFile}
 
 set PogoConfig=${PogoConfig}
+
 "@ `
-    | Out-File $envconfig -Encoding Ascii
+    | Out-File $envConfigScript -Encoding Ascii
 
-# Use the MBv2 environment to construct a MBv1 VSO environment
+# Use the VSTS environment vars to construct a backwards-compatible VSO build environment
 # for the sake of reusing the pre-build and post-build scripts as they are.
 
 @"
 set TF_BUILD_SOURCEGETVERSION=LG:${branch}:${CommitHash}
 set TF_BUILD_DROPLOCATION=${BinariesDirectory}
-set TF_BUILD_BINARIESDIRECTORY=${BinariesDirectory}
+
 set TF_BUILD_SOURCESDIRECTORY=${Env:BUILD_SOURCESDIRECTORY}
+REM set TF_BUILD_BUILDDIRECTORY=${Env:AGENT_BUILDDIRECTORY}\b  # note: inferred location works
+set TF_BUILD_BINARIESDIRECTORY=${BinariesDirectory}
 
 set TF_BUILD_BUILDDEFINITIONNAME=${Env:BUILD_DEFINITIONNAME}
 set TF_BUILD_BUILDNUMBER=${Env:BUILD_BUILDNUMBER}
 set TF_BUILD_BUILDURI=${Env:BUILD_BUILDURI}
 "@ `
-    | Out-File $envconfig -Encoding Ascii -Append
-
-# Set VSO variables that can be consumed by other VSO tasks.
+    | Out-File $envConfigScript -Encoding Ascii -Append
+
+# Export VSO variables that can be consumed by other VSO tasks where the task
+# definition in VSO itself needs to know the value of the variable.
+# If the task definition itself doesn't need to know the value of the variables,
+# the variables that are added to the environment via the script generated above
+# will be interpolated when the tasks run the associated command line with the
+# given parameters.
+#
+# For example, for a Publish Artifacts task, VSO itself needs to know
+# the value of DropPath in order to construct links to the artifacts correctly.
+# Thus, we export a variable called VSO_DropPath (VSO_ prefix by convention)
+# that the VSO build definition, not just the command environment, will know about.
+#
 # Uses command syntax documented here:
 # https://github.com/Microsoft/vso-agent-tasks/blob/master/docs/authoring/commands.md
 # Lines written to stdout that match this pattern are interpreted with this command syntax.
@@ -154,7 +209,11 @@ Write-Output "##vso[task.setvariable variable=VSO_DropPath;]${DropPath}"
 Write-Output "Setting VSO variable VSO_VersionString = ${VersionString}"
 Write-Output "##vso[task.setvariable variable=VSO_VersionString;]${VersionString}"
 
-# TODO (doilij): move this up and assign values
+#
+# Clean up files that might have been left behind from a previous build.
+#
 
-# Inferable Environment (if not specified, inferred by pre_post_util.ps1):
-#   $Env:TF_BUILD_BUILDDIRECTORY    (a.k.a. $objpath)
+if ((Test-Path Env:\BUILD_BINARIESDIRECTORY) -and (Test-Path "$Env:BUILD_BINARIESDIRECTORY"))
+{
+    Remove-Item -Verbose "${Env:BUILD_BINARIESDIRECTORY}\*" -Recurse
+}

+ 52 - 0
Build/scripts/locate_msbuild.ps1

@@ -0,0 +1,52 @@
+#-------------------------------------------------------------------------------------------------------
+# Copyright (C) Microsoft. All rights reserved.
+# Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+#-------------------------------------------------------------------------------------------------------
+
+# Locate-MSBuild
+#
+# Locate and return the preferred location of MSBuild on this machine.
+
+. $PSScriptRoot\util.ps1
+
+# helper to try to locate a single version
+function Locate-MSBuild-Version([string]$version) {
+    $msbuildTemplate = "{0}\msbuild\{1}\Bin\{2}\msbuild.exe"
+    $msbuildUnscoped = "{0}\msbuild\{1}\Bin\msbuild.exe"
+
+    $msbuildExe = $msbuildTemplate -f "${Env:ProgramFiles}", $version, "x86"
+    $_ = WriteMessage "Trying `"$msbuildExe`""
+    if (Test-Path $msbuildExe) { return $msbuildExe }
+
+    $msbuildExe = $msbuildUnscoped -f "${Env:ProgramFiles(x86)}", $version
+    $_ = WriteMessage "Trying `"$msbuildExe`""
+    if (Test-Path $msbuildExe) { return $msbuildExe }
+
+    $msbuildExe = $msbuildTemplate -f "${Env:ProgramFiles(x86)}", $version, "amd64"
+    $_ = WriteMessage "Trying `"$msbuildExe`""
+    if (Test-Path $msbuildExe) { return $msbuildExe }
+
+    return "" # didn't find it so return empty string
+}
+
+function Locate-MSBuild() {
+    $msbuildExe = "msbuild.exe"
+    if (Get-Command $msbuildExe -ErrorAction SilentlyContinue) { return $msbuildExe }
+
+    $msbuildExe = Locate-MSBuild-Version("14.0")
+    if ($msbuildExe -and (Test-Path $msbuildExe)) {
+        $_ = WriteMessage "Found `"$msbuildExe`""
+        return $msbuildExe
+    }
+
+    $_ = WriteMessage "Dev14 not found, trying Dev12..."
+
+    $msbuildExe = Locate-MSBuild-Version("12.0")
+    if ($msbuildExe -and (Test-Path $msbuildExe)) {
+        $_ = WriteMessage "Found `"$msbuildExe`""
+        return $msbuildExe
+    }
+
+    WriteErrorMessage "Can't find msbuild.exe."
+    return "" # return empty string
+}

+ 7 - 3
Build/scripts/pgo/pogo_training.ps1

@@ -21,17 +21,21 @@ param (
     [Parameter(Mandatory=$True)]
     [string]$arch,
 
+    [ValidateSet("default", "codecoverage", "pogo")]
+    [string]$subtype = "default",
+
     # force callers to specify this in case of future use
     [Parameter(Mandatory=$True)]
     [string]$flavor,
 
-    [string]$vcinstallroot = ${env:ProgramFiles(x86)},
+    [string]$vcinstallroot = ${Env:ProgramFiles(x86)},
     [string]$vcbinpath = "Microsoft Visual Studio 14.0\VC\bin",
     [string]$dllname = "pgort140.dll",
     [string]$dllCheckName = "pgort*.dll"
 )
 
-if (${Env:PogoConfig} -eq "False") {
+$pogoConfig = ($subtype -eq "pogo") -or (${Env:PogoConfig} -eq "True")
+if (-not $pogoConfig) {
     Write-Host "---- Not a Pogo Config. Skipping step."
     return 0
 }
@@ -55,7 +59,7 @@ for ($i = 0; $i -lt $scenarios.Length; $i = $i + 1) {
     $items = @()
     if (Test-Path $path -PathType Container) {
         # *.js files in directories
-        $items = Get-ChildItem -Path $path -Filter "*.js" | % {join-path $path $_ }
+        $items = Get-ChildItem -Path $path -Filter "*.js" | % { join-path $path $_ }
     } else {
         $items = @($path)
     }

+ 143 - 0
Build/scripts/pogo_build.ps1

@@ -0,0 +1,143 @@
+#-------------------------------------------------------------------------------------------------------
+# Copyright (C) Microsoft. All rights reserved.
+# Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+#-------------------------------------------------------------------------------------------------------
+
+# Use this script to run a POGO build for the given BuildType (arch, flavor, subtype)
+
+param (
+    [ValidateSet("x86", "x64", "arm")]
+    [Parameter(Mandatory=$True)]
+    [string]$arch,
+
+    # We do not use ValidateSet here because this $flavor param is used to name the BuildConfuration
+    # from the solution file. MsBuild will determine whether it is valid.
+    [Parameter(Mandatory=$True)]
+    [string]$flavor,
+
+    [ValidateSet("default", "codecoverage", "pogo")]
+    [string]$subtype = "pogo",
+
+    [Parameter(Mandatory=$True)]
+    [string]$solutionFile,
+
+    [switch]$clean,
+
+    # $binDir will be inferred if not provided.
+    [string]$binDir = "",
+    [string]$buildlogsSubdir = "buildlogs",
+
+    # Assume NuGet is on the path, otherwise the caller must specify an explicit path.
+    [string]$nugetExe = "NuGet.exe",
+
+    [string]$logFile = "",
+
+    #
+    # POGO training parameters
+    #
+
+    [string[]]$scenarios = @(),
+
+    [Parameter(Mandatory=$True)]
+    [string]$binpath,
+
+    [string]$binaryName = "ch.exe"
+)
+
+#
+# Configure logging
+#
+
+$OuterScriptRoot = $PSScriptRoot
+. $PSScriptRoot\pre_post_util.ps1
+
+$buildName = ConstructBuildName -arch $arch -flavor $flavor -subtype $subtype
+
+if (($logFile -eq "") -and (Test-Path Env:\TF_BUILD_BINARIESDIRECTORY)) {
+    $logFile = "${Env:TF_BUILD_BINARIESDIRECTORY}\logs\pogo_build.${buildName}.log"
+}
+
+if (($logFile -ne "") -and (Test-Path $logFile)) {
+    Remove-Item $logFile -Force
+}
+
+#
+# Only continue with this build if it is a valid pogo build configuration
+#
+
+if ($subtype -ne "pogo") {
+    WriteMessage "This build's subtype is not pogo (subtype: $subtype). Skipping build."
+    exit 0
+}
+
+if ($scenarios.Length -eq 0) {
+    WriteMessage "No training scenarios selected. Please specify training scenarios using the -scenarios parameter."
+    exit 0
+}
+
+#
+# NuGet restore
+#
+
+ExecuteCommand "& $nugetExe restore $solutionFile -NonInteractive"
+
+#
+# Setup
+#
+
+$msbuildExe = Locate-MSBuild
+if (-not $msbuildExe) {
+    WriteErrorMessage "Error: Could not find msbuild.exe -- exiting (1)..."
+    exit 1
+}
+
+$binDir = UseValueOrDefault "$binDir" "${Env:BinariesDirectory}" "${Env:BUILD_SOURCESDIRECTORY}\Build\VcBuild"
+$buildlogsPath = Join-Path $binDir $buildlogsSubdir
+
+$defaultParams = "$solutionFile /nologo /m /nr:false /p:platform=`"${arch}`" /p:configuration=`"${flavor}`""
+$loggingParams = @(
+    "/fl1 `"/flp1:logfile=${buildlogsPath}\build.${buildName}.log;verbosity=normal`"",
+    "/fl2 `"/flp2:logfile=${buildlogsPath}\build.${buildName}.err;errorsonly`"",
+    "/fl3 `"/flp3:logfile=${buildlogsPath}\build.${buildName}.wrn;warningsonly`"",
+    "/verbosity:normal"
+    ) -join " "
+
+$targets = ""
+if ($clean) {
+    $targets += "`"/t:Clean,Rebuild`""
+}
+
+$binary = Join-Path $binpath $binaryName
+
+#
+# Build
+#
+
+function Build($targets="", $pogoParams) {
+    $buildCommand = "& `"$msbuildExe`" $targets $defaultParams $loggingParams $pogoParams"
+    ExecuteCommand "$buildCommand"
+    if ($global:LastExitCode -ne 0) {
+        WriteErrorMessage "Failed msbuild command:`n$buildCommand`n"
+        WriteErrorMessage "Build failed. Exiting..."
+        exit 1
+    }
+}
+
+Build -pogoParams "`"/p:POGO_TYPE=PGI`"" -targets "$targets"
+
+$scenariosParamValue = $scenarios -join ','
+$pogoTrainingCommand = "& `"${PSScriptRoot}\pgo\pogo_training.ps1`" -arch $arch -flavor $flavor -subtype $subtype -binary $binary -scenarios $scenariosParamValue"
+ExecuteCommand "$pogoTrainingCommand"
+
+Build -pogoParams "`"/p:POGO_TYPE=PGO`""
+
+#
+# Clean up
+#
+
+if (("$binpath" -ne "") -and (Test-Path $binpath)) {
+    # remove *.pgc, *.pgd, and pgort*
+    Get-ChildItem -Recurse -Path $binpath "*" `
+        | ? { $_.Name -match "(.*\.pg[cd]|pgort.*)" } `
+        | % { Remove-Item -Force $_.FullName }
+}

+ 31 - 24
Build/scripts/post_build.ps1

@@ -19,6 +19,9 @@ param (
     [ValidateSet("debug", "release", "test", "codecoverage", "*")]
     [string]$flavor = "*",
 
+    [ValidateSet("default", "codecoverage", "pogo")]
+    [string]$subtype = "default",
+
     [string]$srcpath = "",
     [string]$binpath = "",
     [string]$objpath = "",
@@ -34,9 +37,6 @@ param (
     [string[]]$pogo = @(),
     [string]$pogoscript = "",
 
-    # Support output folders with e.g. _pogo suffix
-    [string]$buildTypeSuffix = "",
-
     [switch]$noaction
 )
 
@@ -46,65 +46,72 @@ if ($arch -eq "*") {
 
     . "$PSScriptRoot\util.ps1"
     foreach ($arch in ("x86", "x64", "arm")) {
-        ExecuteCommand "$PSScriptRoot\post_build.ps1 -arch $arch -flavor $flavor -srcpath ""$srcpath"" -binpath ""$binpath"" -objpath ""$objpath"" -srcsrvcmdpath ""$srcsrvcmdpath"" -bvtcmdpath ""$bvtcmdpath"" -repo ""$repo""" -logFile ""$logFile"";
+        ExecuteCommand "$PSScriptRoot\post_build.ps1 -arch $arch -flavor $flavor -srcpath ""$srcpath"" -binpath ""$binpath"" -objpath ""$objpath"" -srcsrvcmdpath ""$srcsrvcmdpath"" -bvtcmdpath ""$bvtcmdpath"" -repo ""$repo""" -logFile ""$logFile""
     }
 
 } elseif ($flavor -eq "*") {
 
     . "$PSScriptRoot\util.ps1"
     foreach ($flavor in ("debug", "test", "release")) {
-        ExecuteCommand "$PSScriptRoot\post_build.ps1 -arch $arch -flavor $flavor -srcpath ""$srcpath"" -binpath ""$binpath"" -objpath ""$objpath"" -srcsrvcmdpath ""$srcsrvcmdpath"" -bvtcmdpath ""$bvtcmdpath"" -repo ""$repo""" -logFile ""$logFile"";
+        ExecuteCommand "$PSScriptRoot\post_build.ps1 -arch $arch -flavor $flavor -srcpath ""$srcpath"" -binpath ""$binpath"" -objpath ""$objpath"" -srcsrvcmdpath ""$srcsrvcmdpath"" -bvtcmdpath ""$bvtcmdpath"" -repo ""$repo""" -logFile ""$logFile""
     }
 
 } else {
-    $OuterScriptRoot = $PSScriptRoot;
+
+    $OuterScriptRoot = $PSScriptRoot
     . "$PSScriptRoot\pre_post_util.ps1"
 
     if (($logFile -eq "") -and (Test-Path Env:\TF_BUILD_BINARIESDIRECTORY)) {
-        $logFile = "${Env:TF_BUILD_BINARIESDIRECTORY}\logs\post_build_${arch}_${flavor}.log"
+        $logFile = "${Env:TF_BUILD_BINARIESDIRECTORY}\logs\post_build.${Env:BuildName}.log"
         if (Test-Path -Path $logFile) {
             Remove-Item $logFile -Force
         }
     }
 
     WriteMessage "======================================================================================"
-    WriteMessage "Post build script for $arch $flavor";
+    WriteMessage "Post build script for $arch $flavor"
     WriteMessage "======================================================================================"
-    $bvtcmdpath =  UseValueOrDefault $bvtcmdpath "" (Resolve-Path "$PSScriptRoot\..\..\test\runcitests.cmd");
 
-    WriteCommonArguments;
+    $bvtcmdpath =  UseValueOrDefault $bvtcmdpath "" (Resolve-Path "$PSScriptRoot\..\..\test\runcitests.cmd")
+
+    WriteCommonArguments
     WriteMessage "BVT Command  : $bvtcmdpath"
     WriteMessage ""
 
-    $srcsrvcmd = ("{0} {1} {2} {3}\bin\{4}_{5}{6}\*.pdb" -f $srcsrvcmdpath, $repo, $srcpath, $binpath, $arch, $flavor, $buildTypeSuffix);
-    $pogocmd = ("{0} {1} {2}" -f $pogoscript, $arch, $flavor);
-    $prefastlog = ("{0}\logs\PrefastCheck_{1}_{2}.log" -f $binpath, $arch, $flavor);
-    $prefastcmd = "$PSScriptRoot\check_prefast_error.ps1 -directory $objpath -logFile $prefastlog";
+    $buildName = ConstructBuildName -arch $arch -flavor $flavor -subtype $subtype
+    $srcsrvcmd = ("{0} {1} {2} {3}\bin\{4}\*.pdb" -f $srcsrvcmdpath, $repo, $srcpath, $binpath, $buildName)
+    $prefastlog = ("{0}\logs\PrefastCheck.{1}.log" -f $binpath, $buildName)
+    $prefastcmd = "$PSScriptRoot\check_prefast_error.ps1 -directory $objpath -logFile $prefastlog"
 
     # generate srcsrv
     if ((Test-Path $srcsrvcmdpath) -and (Test-Path $srcpath) -and (Test-Path $binpath)) {
-        ExecuteCommand($srcsrvcmd);
+        ExecuteCommand($srcsrvcmd)
     }
 
-    # do PoGO
+    # do POGO
     $doPogo=$False
     for ($i=0; $i -lt $pogo.length; $i=$i+2) {
         if (($pogo[$i] -eq $arch) -and ($pogo[$i+1] -eq $flavor)) {
             $doPogo=$True
         }
     }
-    if ($doPogo) {
-        WriteMessage "Building pogo for $arch $flavor"
-        ExecuteCommand($pogocmd);
-    }
 
-    # run test
-    ExecuteCommand("$bvtcmdpath -$arch$flavor");
+    if ($subtype -ne "codecoverage") {
+        if ($doPogo -and ("$pogoscript" -ne "")) {
+            WriteMessage "Building pogo for $arch $flavor"
+            $pogocmd = ("{0} {1} {2}" -f $pogoscript, $arch, $flavor)
+            ExecuteCommand($pogocmd)
+        }
+
+        # run tests
+        ExecuteCommand("$bvtcmdpath -$arch$flavor")
+    }
 
     # check prefast
-    ExecuteCommand($prefastcmd);
+    ExecuteCommand($prefastcmd)
+
+    WriteMessage ""
 
-    WriteMessage "";
 }
 
 exit $global:exitcode

+ 70 - 54
Build/scripts/pre_build.ps1

@@ -26,31 +26,39 @@
 #   $Env:TF_BUILD_BUILDURI
 
 param (
-    [ValidateSet("x86", "x64", "arm", "")]
-    [string]$arch = "",
-    [ValidateSet("debug", "release", "test", "codecoverage", "")]
-    [string]$flavor = "",
+    [Parameter(Mandatory=$True)]
+    [ValidateSet("x86", "x64", "arm")]
+    [string]$arch,
+    [Parameter(Mandatory=$True)]
+    [ValidateSet("debug", "release", "test", "codecoverage")]
+    [string]$flavor,
+
+    [ValidateSet("default", "codecoverage", "pogo")]
+    [string]$subtype = "default",
 
     [string]$srcpath = "",
     [string]$binpath = "",
     [string]$objpath = "",
     [string]$logFile = "",
+
+    [Parameter(Mandatory=$True)]
     [string]$oauth
 )
 
-$OuterScriptRoot = $PSScriptRoot;
+$OuterScriptRoot = $PSScriptRoot # Used in pre_post_util.ps1
 . "$PSScriptRoot\pre_post_util.ps1"
+
 if (($logFile -eq "") -and (Test-Path Env:\TF_BUILD_BINARIESDIRECTORY)) {
-    if (-not(Test-Path -Path "$Env:TF_BUILD_BINARIESDIRECTORY\logs")) {
-        $dummy = New-Item -Path "$Env:TF_BUILD_BINARIESDIRECTORY\logs" -ItemType Directory -Force
+    if (-not(Test-Path -Path "${Env:TF_BUILD_BINARIESDIRECTORY}\logs")) {
+        $dummy = New-Item -Path "${Env:TF_BUILD_BINARIESDIRECTORY}\logs" -ItemType Directory -Force
     }
-    $logFile = "$Env:TF_BUILD_BINARIESDIRECTORY\logs\pre_build_${arch}_${flavor}.log"
+    $logFile = "${Env:TF_BUILD_BINARIESDIRECTORY}\logs\pre_build.${Env:BuildName}.log"
     if (Test-Path -Path $logFile) {
         Remove-Item $logFile -Force
     }
 }
 
-WriteCommonArguments;
+WriteCommonArguments
 
 #
 # Create packages.config files
@@ -63,10 +71,10 @@ $packagesConfigFileText = @"
 </packages>
 "@
 
-$PackagesFiles = Get-ChildItem -Path $Env:TF_BUILD_SOURCESDIRECTORY *.vcxproj -Recurse `
+$packagesFiles = Get-ChildItem -Path $Env:TF_BUILD_SOURCESDIRECTORY *.vcxproj -Recurse `
     | % { Join-Path $_.DirectoryName "packages.config" }
 
-foreach ($file in $PackagesFiles) {
+foreach ($file in $packagesFiles) {
     if (-not (Test-Path $file)) {
         Write-Output $packagesConfigFileText | Out-File $file -Encoding utf8
     }
@@ -79,68 +87,70 @@ foreach ($file in $PackagesFiles) {
 if (Test-Path Env:\TF_BUILD_SOURCEGETVERSION)
 {
     $commitHash = ($Env:TF_BUILD_SOURCEGETVERSION).split(':')[2]
-    $gitExe = GetGitPath;
+    $gitExe = GetGitPath
 
     $outputDir = $Env:TF_BUILD_DROPLOCATION
     if (-not(Test-Path -Path $outputDir)) {
         $dummy = New-Item -Path $outputDir -ItemType Directory -Force
     }
 
-    Push-Location $srcpath;
-    $outputFile = Join-Path -Path $outputDir -ChildPath "change.txt"
-
-    Write-Output "TF_BUILD_BUILDDEFINITIONNAME = $Env:TF_BUILD_BUILDDEFINITIONNAME" | Out-File $outputFile
-    Write-Output "TF_BUILD_BUILDNUMBER = $Env:TF_BUILD_BUILDNUMBER" | Out-File $outputFile -Append
-    Write-Output "TF_BUILD_SOURCEGETVERSION = $Env:TF_BUILD_SOURCEGETVERSION" | Out-File $outputFile -Append
-    Write-Output "TF_BUILD_BUILDURI = $Env:TF_BUILD_BUILDURI" | Out-File $outputFile -Append
-    Write-Output "" | Out-File $outputFile -Append
+    Push-Location $srcpath
 
-    # Get the git remote path and construct the rest API URI
-    $remote = (iex "$gitExe remote -v")[0].split()[1].replace("_git", "_apis/git/repositories");
-    $remote = $remote.replace("mshttps", "https");
+    $info = GetBuildInfo $oauth $commitHash
 
-    # Get the pushId and push date time to use that for build number and build date time
-    $uri = ("{0}/commits/{1}?api-version=1.0" -f $remote, $commitHash)
-    $oauthToken = Get-Content $oauth;
-    $header = @{Authorization=("Basic {0}" -f $oauthToken) }
-    $info = Invoke-RestMethod -Headers $header -Uri $uri -Method GET
+    $BuildDate = ([datetime]$info.push.date).toString("yyMMdd-HHmm")
 
-    $buildDate = ([datetime]$info.push.date).toString("yyMMdd-HHmm")
-    $buildPushId = $info.push.pushId
-    $buildPushIdPart1 = [int]([math]::Floor($buildPushId / 65536))
-    $buildPushIdPart2 = [int]($buildPushId % 65536)
-    $buildPushIdString = "{0}.{1}" -f $buildPushIdPart1.ToString("00000"), $buildPushIdPart2.ToString("00000")
-
-    Write-Output "PushId = $buildPushId $buildPushIdString" | Out-File $outputFile -Append
-    Write-Output "PushDate = $buildDate"                    | Out-File $outputFile -Append
-    Write-Output ""                                         | Out-File $outputFile -Append
+    $buildPushId, $buildPushIdPart1, $buildPushIdPart2, $buildPushIdString = GetBuildPushId $info
 
     # commit message
     $command = "$gitExe log -1 --name-status -p $commitHash"
-    iex $command | Out-File $outputFile -Append
+    $CommitMessage = iex $command
+
+    $changeTextFile = Join-Path -Path $outputDir -ChildPath "change.txt"
+
+    $changeTextFileContent = @"
+TF_BUILD_BUILDDEFINITIONNAME = $Env:TF_BUILD_BUILDDEFINITIONNAME
+TF_BUILD_BUILDNUMBER = $Env:TF_BUILD_BUILDNUMBER
+TF_BUILD_SOURCEGETVERSION = $Env:TF_BUILD_SOURCEGETVERSION
+TF_BUILD_BUILDURI = $Env:TF_BUILD_BUILDURI
+
+PushId = $buildPushId $buildPushIdString
+PushDate = $buildDate
+
+$CommitMessage
+"@
+
+    Write-Output "-----"
+    Write-Output $changeTextFile
+    Write-Output $changeTextFileContent
+    Write-Output $changeTextFileContent | Out-File $changeTextFile -Encoding utf8 -Append
+
     Pop-Location
 
     # commit hash
-    $buildCommit = ($Env:TF_BUILD_SOURCEGETVERSION).SubString(14);
-    $commitHash = $buildCommit.Split(":")[1]
+    $buildCommit = ($Env:TF_BUILD_SOURCEGETVERSION).SubString(14)
+    $CommitHash = $buildCommit.Split(":")[1]
 
     $outputJsonFile = Join-Path -Path $outputDir -ChildPath "change.json"
     $changeJson = New-Object System.Object
 
     $changeJson | Add-Member -type NoteProperty -name BuildDefinitionName -value $Env:TF_BUILD_BUILDDEFINITIONNAME
     $changeJson | Add-Member -type NoteProperty -name BuildNumber -value $Env:TF_BUILD_BUILDNUMBER
-    $changeJson | Add-Member -type NoteProperty -name BuildDate -value $buildDate
     $changeJson | Add-Member -type NoteProperty -name BuildUri -value $Env:TF_BUILD_BUILDURI
+    $changeJson | Add-Member -type NoteProperty -name BuildDate -value $BuildDate
     $changeJson | Add-Member -type NoteProperty -name Branch -value $Env:BranchName
-    $changeJson | Add-Member -type NoteProperty -name CommitHash -value $commitHash
-    $changeJson | Add-Member -type NoteProperty -name PushId -value $buildPushId
-    $changeJson | Add-Member -type NoteProperty -name PushIdPart1 -value $buildPushIdPart1
-    $changeJson | Add-Member -type NoteProperty -name PushIdPart2 -value $buildPushIdPart2
-    $changeJson | Add-Member -type NoteProperty -name PushIdString -value $buildPushIdString
-    $changeJson | Add-Member -type NoteProperty -name SourceGetVersion -value $Env:TF_BUILD_SOURCEGETVERSION
-
+    $changeJson | Add-Member -type NoteProperty -name CommitHash -value $CommitHash
+    $changeJson | Add-Member -type NoteProperty -name PushId -value $BuildPushId
+    $changeJson | Add-Member -type NoteProperty -name PushIdPart1 -value $BuildPushIdPart1
+    $changeJson | Add-Member -type NoteProperty -name PushIdPart2 -value $BuildPushIdPart2
+    $changeJson | Add-Member -type NoteProperty -name PushIdString -value $BuildPushIdString
+    $changeJson | Add-Member -type NoteProperty -name Username -value $Env:Username
+    $changeJson | Add-Member -type NoteProperty -name CommitMessage -value $CommitMessage
+
+    Write-Output "-----"
+    Write-Output $outputJsonFile
     $changeJson | ConvertTo-Json | Write-Output
-    $changeJson | ConvertTo-Json | Out-File $outputJsonFile -Encoding ascii
+    $changeJson | ConvertTo-Json | Out-File $outputJsonFile -Encoding Ascii
 
     $buildInfoOutputDir = $objpath
     if (-not(Test-Path -Path $buildInfoOutputDir)) {
@@ -148,8 +158,8 @@ if (Test-Path Env:\TF_BUILD_SOURCEGETVERSION)
     }
 
     # generate build version prop file
-    $buildInfoOutputFile = Join-Path -Path $buildInfoOutputDir -ChildPath "Chakra.Generated.BuildInfo.props"
-    $propsFile = @"
+    $propsFile = Join-Path -Path $buildInfoOutputDir -ChildPath "Chakra.Generated.BuildInfo.props"
+    $propsFileTemplate = @"
 <?xml version="1.0" encoding="utf-16"?>
 <Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
   <PropertyGroup>
@@ -162,13 +172,19 @@ if (Test-Path Env:\TF_BUILD_SOURCEGETVERSION)
   </PropertyGroup>
 </Project>
 "@
-    Write-Output ($propsFile -f $binpath, $objpath, $buildPushIdPart1, $buildPushIdPart2, $buildCommit, $buildDate) | Out-File $buildInfoOutputFile
+
+    $propsFileContent = $propsFileTemplate -f $binpath, $objpath, $buildPushIdPart1, $buildPushIdPart2, $buildCommit, $buildDate
+
+    Write-Output "-----"
+    Write-Output $propsFile
+    Write-Output $propsFileContent
+    Write-Output $propsFileContent | Out-File $propsFile
 }
 
 #
 # Clean up code analysis summary files in case they get left behind
 #
 
-if (Test-Path $objpath) {
-    Get-ChildItem $objpath -include vc.nativecodeanalysis.all.xml -recurse | Remove-Item
+if (($objpath -ne "") -and (Test-Path $objpath)) {
+    Get-ChildItem $objpath -Include vc.nativecodeanalysis.all.xml -Recurse | Remove-Item -Verbose
 }

+ 54 - 22
Build/scripts/pre_post_util.ps1

@@ -4,34 +4,66 @@
 #-------------------------------------------------------------------------------------------------------
 
 . "$PSScriptRoot\util.ps1"
-
-function UseValueOrDefault($value, $defaultvalue, $defaultvalue2) {
-    if ($value -ne "") {
-        return $value;
-    } elseif ($defaultvalue -ne "") {
-        return $defaultvalue;
-    }
-    return $defaultvalue2;
-}
+. "$PSScriptRoot\locate_msbuild.ps1"
 
 function WriteCommonArguments() {
-    WriteMessage "Source Path  : $srcpath"
-    WriteMessage "Object Path  : $objpath"
+    WriteMessage "  Source Path: $srcpath"
+    WriteMessage "  Object Path: $objpath"
     WriteMessage "Binaries Path: $binpath"
 }
 
-function GetGitPath() {
-    $gitExe = "git.exe"
+function GetBuildInfo($oauth, $commitHash) {
+    # Get the git remote path and construct the rest API URI
+    $gitExe = GetGitPath
+    $remote = (iex "$gitExe remote -v")[0].split()[1].replace("_git", "_apis/git/repositories")
+    $remote = $remote.replace("mshttps", "https")
+
+    # Get the pushId and push date time to use that for build number and build date time
+    $uri = ("{0}/commits/{1}?api-version=1.0" -f $remote, $commitHash)
+    $oauthToken = Get-Content $oauth
+    $header = @{Authorization=("Basic {0}" -f $oauthToken) }
+    $info = Invoke-RestMethod -Headers $header -Uri $uri -Method GET
+
+    return $info
+}
+
+function GetBuildPushId($info) {
+    $buildPushId = $info.push.pushId
+    $buildPushIdPart1 = [int]([math]::Floor($buildPushId / 65536))
+    $buildPushIdPart2 = [int]($buildPushId % 65536)
+    $buildPushIdString = "{0}.{1}" -f $buildPushIdPart1.ToString("00000"), $buildPushIdPart2.ToString("00000")
 
-    if (!(Get-Command $gitExe -ErrorAction SilentlyContinue)) {
-        $gitExe = "C:\1image\Git\bin\git.exe"
-        if (!(Test-Path $gitExe)) {
-            throw "git.exe not found in path- aborting."
-        }
+    return @($buildPushId, $buildPushIdPart1, $buildPushIdPart2, $buildPushIdString)
+}
+
+function ConstructBuildName($arch, $flavor, $subtype) {
+    if ($subtype -eq "codecoverage") {
+        # TODO eliminate tools' dependency on this particular formatting exception
+        # Normalize the $BuildName of even if the $BuildType is e.g. x64_test_codecoverage
+        return "${arch}_codecoverage"
+    } elseif ($subtype -eq "pogo") {
+        return "${arch}_${flavor}_${subtype}"
+    } else {
+        return "${arch}_${flavor}"
     }
-    return $gitExe;
 }
 
-$srcpath = UseValueOrDefault $srcpath "$env:TF_BUILD_SOURCESDIRECTORY" (Resolve-Path "$OuterScriptRoot\..\..");
-$objpath = UseValueOrDefault $objpath "$env:TF_BUILD_BUILDDIRECTORY" "${srcpath}\Build\VcBuild\obj\${arch}_${flavor}";
-$binpath = UseValueOrDefault $binpath "$env:TF_BUILD_BINARIESDIRECTORY" "${srcpath}\Build\VcBuild";
+# Compute paths
+
+if (("$arch" -eq "") -or ("$flavor" -eq "") -or ("$OuterScriptRoot" -eq ""))
+{
+    WriteErrorMessage @"
+
+    Required variables not set before script was included:
+        `$arch = $arch
+        `$flavor = $flavor
+        `$OuterScriptRoot = $OuterScriptRoot
+
+"@
+
+    throw "Cannot continue - required variables not set."
+}
+
+$srcpath = UseValueOrDefault $srcpath "$env:TF_BUILD_SOURCESDIRECTORY" (Resolve-Path "$OuterScriptRoot\..\..")
+$objpath = UseValueOrDefault $objpath "$env:TF_BUILD_BUILDDIRECTORY" "${srcpath}\Build\VcBuild\obj\${arch}_${flavor}"
+$binpath = UseValueOrDefault $binpath "$env:TF_BUILD_BINARIESDIRECTORY" "${srcpath}\Build\VcBuild"

+ 83 - 0
Build/scripts/run_build.ps1

@@ -0,0 +1,83 @@
+#-------------------------------------------------------------------------------------------------------
+# Copyright (C) Microsoft. All rights reserved.
+# Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+#-------------------------------------------------------------------------------------------------------
+
+# Use this script to run a build command for the given BuildType (arch, flavor, subtype)
+
+param (
+    [ValidateSet("x86", "x64", "arm")]
+    [Parameter(Mandatory=$True)]
+    [string]$arch,
+
+    # We do not use ValidateSet here because this $flavor param is used to name the BuildConfuration
+    # from the solution file. MsBuild will determine whether it is valid.
+    [Parameter(Mandatory=$True)]
+    [string]$flavor,
+
+    [ValidateSet("default", "codecoverage", "pogo")]
+    [string]$subtype = "default",
+
+    [Parameter(Mandatory=$True)]
+    [string]$solutionFile = "",
+
+    [switch]$clean,
+
+    # $binDir will be inferred if not provided.
+    [string]$binDir = "",
+    [string]$buildlogsSubdir = "buildlogs",
+
+    # assume NuGet is on the path, otherwise the caller must specify an explicit path
+    [string]$nugetExe = "NuGet.exe",
+
+    [string]$logFile = ""
+)
+
+$OuterScriptRoot = $PSScriptRoot
+. $PSScriptRoot\pre_post_util.ps1
+
+if (($logFile -eq "") -and (Test-Path Env:\TF_BUILD_BINARIESDIRECTORY)) {
+    $logFile = "${Env:TF_BUILD_BINARIESDIRECTORY}\logs\run_build.${Env:BuildName}.log"
+    if (Test-Path -Path $logFile) {
+        Remove-Item $logFile -Force
+    }
+}
+
+#
+# NuGet restore
+#
+
+ExecuteCommand "& $nugetExe restore $solutionFile -NonInteractive"
+
+#
+# Setup and build
+#
+
+$msbuildExe = Locate-MSBuild
+if (-not $msbuildExe) {
+    WriteErrorMessage "Could not find msbuild.exe -- exiting..."
+    exit 1
+}
+
+$binDir = UseValueOrDefault "$binDir" "${Env:BinariesDirectory}" "${Env:BUILD_SOURCESDIRECTORY}\Build\VcBuild"
+$buildlogsPath = Join-Path $binDir $buildlogsSubdir
+
+$defaultParams = "$solutionFile /nologo /m /nr:false /p:platform=`"${arch}`" /p:configuration=`"${flavor}`""
+$loggingParams = @(
+    "/fl1 `"/flp1:logfile=${buildlogsPath}\build.${Env:BuildName}.log;verbosity=normal`"",
+    "/fl2 `"/flp2:logfile=${buildlogsPath}\build.${Env:BuildName}.err;errorsonly`"",
+    "/fl3 `"/flp3:logfile=${buildlogsPath}\build.${Env:BuildName}.wrn;warningsonly`"",
+    "/verbosity:normal"
+    ) -join " "
+
+$targets = ""
+if ($clean) {
+    $targets += "`"/t:Clean,Rebuild`""
+}
+
+if ($subtype -eq "codecoverage") {
+    $subtypeParams = "/p:ENABLE_CODECOVERAGE=true"
+}
+
+$buildCommand = "& `"$msbuildExe`" $targets $defaultParams $loggingParams $subtypeParams"
+ExecuteCommand "$buildCommand"

+ 28 - 5
Build/scripts/util.ps1

@@ -3,6 +3,29 @@
 # Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
 #-------------------------------------------------------------------------------------------------------
 
+function UseValueOrDefault($value, $defaultvalue, $defaultvalue2) {
+    if ($value -ne "") {
+        return $value
+    } elseif ($defaultvalue -ne "") {
+        return $defaultvalue
+    } else {
+        return $defaultvalue2
+    }
+}
+
+function GetGitPath() {
+    $gitExe = "git.exe"
+
+    if (!(Get-Command $gitExe -ErrorAction SilentlyContinue)) {
+        $gitExe = "C:\1image\Git\bin\git.exe"
+        if (!(Test-Path $gitExe)) {
+            throw "git.exe not found in path -- aborting."
+        }
+    }
+
+    return $gitExe
+}
+
 function WriteMessage($str) {
     Write-Output $str
     if ($logFile -ne "") {
@@ -11,7 +34,7 @@ function WriteMessage($str) {
 }
 
 function WriteErrorMessage($str) {
-    $host.ui.WriteErrorLine($str);
+    $host.ui.WriteErrorLine($str)
     if ($logFile -ne "") {
         Write-Output $str | Out-File $logFile -Append
     }
@@ -19,17 +42,17 @@ function WriteErrorMessage($str) {
 
 function ExecuteCommand($cmd) {
     if ($cmd -eq "") {
-        return;
+        return
     }
     WriteMessage "-------------------------------------"
     WriteMessage "Running $cmd"
     if ($noaction) {
-        return;
+        return
     }
     Invoke-Expression $cmd
-    if($lastexitcode -ne 0) {
+    if ($lastexitcode -ne 0) {
         WriteErrorMessage "ERROR: Command failed: exit code $LastExitCode"
-        $global:exitcode = $LastExitCode;
+        $global:exitcode = $LastExitCode
     }
     WriteMessage ""
 }

+ 1 - 53
test/jenkins.build.init.cmd

@@ -36,56 +36,4 @@ if not [%1]==[] (
 :: Set up msbuild.exe
 :: ========================================
 
-if "%JENKINS_USE_MSBUILD_12%" == "True" (
-    echo Skipping Dev14 and trying Dev12...
-    goto :LABEL_USE_MSBUILD_12
-)
-
-where /q msbuild.exe
-IF "%ERRORLEVEL%" == "0" (
-    goto :SkipMsBuildSetup
-)
-
-REM Try Dev14 first
-set MSBUILD_VERSION=14.0
-set MSBUILD_PATH="%ProgramFiles%\msbuild\%MSBUILD_VERSION%\Bin\x86"
-
-if not exist %MSBUILD_PATH%\msbuild.exe (
-    set MSBUILD_PATH="%ProgramFiles(x86)%\msbuild\%MSBUILD_VERSION%\Bin"
-)
-
-if not exist %MSBUILD_PATH%\msbuild.exe (
-    set MSBUILD_PATH="%ProgramFiles(x86)%\msbuild\%MSBUILD_VERSION%\Bin\amd64"
-)
-
-if exist %MSBUILD_PATH%\msbuild.exe (
-    goto :MSBuildFound
-)
-
-echo Dev14 not found, trying Dev %MSBUILD_VERSION%
-
-:LABEL_USE_MSBUILD_12
-set MSBUILD_VERSION=12.0
-set MSBUILD_PATH="%ProgramFiles%\msbuild\%MSBUILD_VERSION%\Bin\x86"
-
-if not exist %MSBUILD_PATH%\msbuild.exe (
-    set MSBUILD_PATH="%ProgramFiles(x86)%\msbuild\%MSBUILD_VERSION%\Bin"
-)
-
-if not exist %MSBUILD_PATH%\msbuild.exe (
-    set MSBUILD_PATH="%ProgramFiles(x86)%\msbuild\%MSBUILD_VERSION%\Bin\amd64"
-)
-
-if not exist %MSBUILD_PATH%\msbuild.exe (
-    echo Can't find msbuild.exe in %MSBUILD_PATH%
-    goto :SkipMsBuildSetup
-)
-
-:MSBuildFound
-echo MSBuild located at %MSBUILD_PATH%
-
-set PATH=%MSBUILD_PATH%;%PATH%
-set JENKINS_USE_MSBUILD_12=
-set MSBUILD_PATH=
-
-:SkipMsBuildSetup
+call %REPO_ROOT%\Build\scripts\add_msbuild_path.cmd %JENKINS_USE_MSBUILD_12%