diff options
Diffstat (limited to 'scripts')
| -rw-r--r-- | scripts/azure-pipelines/analyze-test-results.ps1 | 778 | ||||
| -rw-r--r-- | scripts/azure-pipelines/azure-pipelines.yml | 56 | ||||
| -rw-r--r-- | scripts/azure-pipelines/generate-skip-list.ps1 | 72 | ||||
| -rw-r--r-- | scripts/azure-pipelines/windows/azure-pipelines.yml | 66 | ||||
| -rw-r--r-- | scripts/azure-pipelines/windows/ci-step.ps1 | 163 | ||||
| -rw-r--r-- | scripts/azure-pipelines/windows/create-vmss.ps1 | 458 | ||||
| -rw-r--r-- | scripts/azure-pipelines/windows/initialize-environment.ps1 | 93 | ||||
| -rw-r--r-- | scripts/azure-pipelines/windows/provision-image.ps1 | 447 | ||||
| -rw-r--r-- | scripts/azure-pipelines/windows/sysprep.ps1 | 17 | ||||
| -rw-r--r-- | scripts/ci.baseline.txt | 108 |
10 files changed, 2237 insertions, 21 deletions
diff --git a/scripts/azure-pipelines/analyze-test-results.ps1 b/scripts/azure-pipelines/analyze-test-results.ps1 new file mode 100644 index 000000000..9e6d09d20 --- /dev/null +++ b/scripts/azure-pipelines/analyze-test-results.ps1 @@ -0,0 +1,778 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: MIT +# + +<# +.SYNOPSIS +Analyze the test results as output by the CI system. + +.DESCRIPTION +Takes the set of port test results from $logDir, +and the baseline from $baselineFile, and makes certain that the set +of failures we expected are exactly the set of failures we got. +Then, uploads the logs from any unexpected failures. + +.PARAMETER logDir +Directory of xml test logs to analyze. + +.PARAMETER failurelogDir +Path to the failure logs that need to be published to azure for inspection. + +.PARAMETER outputDir +Where to write out the results of the analysis. + +.PARAMETER allResults +Include tests that have no change from the baseline in the output. + +.PARAMETER errorOnRegression +Output an error on test regressions. +This will give a clean message in the build pipeline. + +.PARAMETER noTable +Don't create or upload the markdown table of results + +.PARAMETER triplets +A list of triplets to analyze; defaults to all triplets. + +.PARAMETER baselineFile +The path to the ci.baseline.txt file in the vcpkg repository. +#> +[CmdletBinding()] +Param( + [Parameter(Mandatory = $true)] + [string]$logDir, + [Parameter(Mandatory = $true)] + [string]$failurelogDir, + [Parameter(Mandatory = $true)] + [string]$outputDir, + [switch]$allResults, + [switch]$errorOnRegression, + [switch]$noTable, + [string[]]$triplets = @(), + [Parameter(Mandatory = $true)] + [string]$baselineFile +) + +$ErrorActionPreference = 'Stop' + +if ( -not (Test-Path $logDir) ) { + [System.Console]::Error.WriteLine("Log directory does not exist: $logDir") + exit +} +if ( -not (Test-Path $outputDir) ) { + [System.Console]::Error.WriteLine("output directory does not exist: $outputDir") + exit +} + +if ( $triplets.Count -eq 0 ) { + $triplets = @( + "x64-linux", + "x64-osx", + "arm-uwp", + "arm64-windows", + "x64-osx", + "x64-uwp", + "x64-windows-static", + "x64-windows", + "x86-windows" + ) +} + + +<# +.SYNOPSIS +Creates an object the represents the test run. + +.DESCRIPTION +build_test_results takes an XML file of results from the CI run, +and constructs an object based on that XML file for further +processing. + +.OUTPUTS +An object with the following elements: + assemblyName: + assemblyStartDate: + assemblyStartTime: + assemblyTime: + collectionName: + collectionTime: + allTests: A hashtable with an entry for each port tested + The key is the name of the port + The value is an object with the following elements: + name: Name of the port (Does not include the triplet name) + result: Pass/Fail/Skip result from xunit + time: Test time in seconds + originalResult: Result as defined by Build.h in vcpkg source code + abi_tag: The port hash + features: The features installed + +.PARAMETER xmlFilename +The path to the XML file to parse. +#> +function build_test_results { + [CmdletBinding()] + Param + ( + [string]$xmlFilename + ) + if ( ($xmlFilename.Length -eq 0) -or ( -not( Test-Path $xmlFilename))) { + #write-error "Missing file: $xmlFilename" + return $null + } + + Write-Verbose "building test hash for $xmlFilename" + + [xml]$xmlContents = Get-Content $xmlFilename + + # This currently only supports one collection per assembly, which is the way + # the vcpkg tests are designed to run in the pipeline. + $xmlAssembly = $xmlContents.assemblies.assembly + $assemblyName = $xmlAssembly.name + $assemblyStartDate = $xmlAssembly."run-date" + $assemblyStartTime = $xmlAssembly."run-time" + $assemblyTime = $xmlAssembly.time + $xmlCollection = $xmlAssembly.collection + $collectionName = $xmlCollection.name + $collectionTime = $xmlCollection.time + + $allTestResults = @{ } + foreach ( $test in $xmlCollection.test) { + $name = ($test.name -replace ":.*$") + + # Reconstruct the original BuildResult enumeration (defined in Build.h) + # failure.message - why the test failed (valid only on test failure) + # reason - why the test was skipped (valid only when the test is skipped) + # case BuildResult::POST_BUILD_CHECKS_FAILED: + # case BuildResult::FILE_CONFLICTS: + # case BuildResult::BUILD_FAILED: + # case BuildResult::EXCLUDED: + # case BuildResult::CASCADED_DUE_TO_MISSING_DEPENDENCIES: + $originalResult = "NULLVALUE" + switch ($test.result) { + "Skip" { + $originalResult = $test.reason.InnerText + } + "Fail" { + $originalResult = $test.failure.message.InnerText + } + "Pass" { + $originalResult = "SUCCEEDED" + } + } + + $abi_tag = "" + $features = "" + foreach ( $trait in $test.traits.trait) { + switch ( $trait.name ) { + "abi_tag" { $abi_tag = $trait.value } + "features" { $features = $trait.value } + } + } + + # If additional fields get saved in the XML, then they should be added to this hash + # also consider using a PSCustomObject here instead of a hash + $testHash = @{ name = $name; result = $test.result; time = $test.time; originalResult = $originalResult; abi_tag = $abi_tag; features = $features } + $allTestResults[$name] = $testHash + } + + return @{ + assemblyName = $assemblyName; + assemblyStartDate = $assemblyStartDate; + assemblyStartTime = $assemblyStartTime; + assemblyTime = $assemblyTime; + collectionName = $collectionName; + collectionTime = $collectionTime; + allTests = $allTestResults + } +} + +<# +.SYNOPSIS +Creates an object that represents the baseline expectations. + +.DESCRIPTION +build_baseline_results converts the baseline file to an object representing +the expectations set up by the baseline file. It records four states: + 1) fail + 2) skip + 3) ignore + 4) pass -- this is represented by not being recorded +In other words, if a port is not contained in the object returned by this +cmdlet, expect it to pass. + +.OUTPUTS +An object containing the following fields: + collectionName: the triplet + fail: ports marked as fail + skip: ports marked as skipped + ignore: ports marked as ignore + +.PARAMETER baselineFile +The path to vcpkg's ci.baseline.txt. + +.PARAMETER triplet +The triplet to create the result object for. +#> +function build_baseline_results { + [CmdletBinding()] + Param( + $baselineFile, + $triplet + ) + #read in the file, strip out comments and blank lines and spaces, leave only the current triplet + #remove comments, remove empty lines, remove whitespace, then keep only those lines for $triplet + $baseline_list_raw = Get-Content -Path $baselineFile ` + | Where-Object { -not ($_ -match "\s*#") } ` + | Where-Object { -not ( $_ -match "^\s*$") } ` + | ForEach-Object { $_ -replace "\s" } ` + | Where-Object { $_ -match ":$triplet=" } + + #filter to skipped and trim the triplet + $skip_hash = @{ } + foreach ( $port in $baseline_list_raw | ? { $_ -match "=skip$" } | % { $_ -replace ":.*$" }) { + if ($skip_hash[$port] -ne $null) { + [System.Console]::Error.WriteLine("$($port):$($triplet) has multiple definitions in $baselineFile") + } + $skip_hash[$port] = $true + } + $fail_hash = @{ } + $baseline_list_raw | ? { $_ -match "=fail$" } | % { $_ -replace ":.*$" } | ? { $fail_hash[$_] = $true } | Out-Null + $ignore_hash = @{ } + $baseline_list_raw | ? { $_ -match "=ignore$" } | % { $_ -replace ":.*$" } | ? { $ignore_hash[$_] = $true } | Out-Null + + return @{ + collectionName = $triplet; + skip = $skip_hash; + fail = $fail_hash; + ignore = $ignore_hash + } +} + +<# +.SYNOPSIS +Analyzes the results of the current run against the baseline. + +.DESCRIPTION +combine_results compares the results to the baselie, and generates the results +for the CI -- whether it should pass or fail. + +.OUTPUTS +An object containing the following: +(Note that this is not the same data structure as build_test_results) + assemblyName: + assemblyStartDate: + assemblyStartTime: + assemblyTime: + collectionName: + collectionTime: + allTests: A hashtable of each port with a different status from the baseline + The key is the name of the port + The value is an object with the following data members: + name: The name of the port + result: xunit test result Pass/Fail/Skip + message: Human readable message describing the test result + time: time the current test results took to run. + baselineResult: + currentResult: + features: + ignored: list of ignored tests + +.PARAMETER baseline +The baseline object to use from build_baseline_results. + +.PARAMETER current +The results object to use from build_test_results. +#> +function combine_results { + [CmdletBinding()] + Param + ( + $baseline, + $current + ) + + if ($baseline.collectionName -ne $current.collectionName) { + Write-Warning "Comparing mismatched collections $($baseline.collectionName) and $($current.collectionName)" + } + + $currentTests = $current.allTests + + # lookup table with the results of all of the tests + $allTestResults = @{ } + + $ignoredList = @() + + Write-Verbose "analyzing $($currentTests.count) tests" + + foreach ($key in $currentTests.keys) { + Write-Verbose "analyzing $key" + + $message = $null + $result = $null + $time = $null + $currentResult = $null + $features = $currentTest.features + + $baselineResult = "Pass" + if ($baseline.fail[$key] -ne $null) { + Write-Verbose "$key is failing" + $baselineResult = "Fail" + } + elseif ( $baseline.skip[$key] -ne $null) { + Write-Verbose "$key is skipped" + $baselineResult = "Skip" + } + elseif ( $baseline.ignore[$key] -ne $null) { + $baselineResult = "ignore" + } + + $currentTest = $currentTests[$key] + + if ( $currentTest.result -eq $baselineResult) { + Write-Verbose "$key has no change from baseline" + $currentResult = $currentTest.result + if ($allResults) { + # Only marking regressions as failures but keep the skipped status + if ($currentResult -eq "Skip") { + $result = "Skip" + } + else { + $result = "Pass" + } + $message = "No change from baseline" + $time = $currentTest.time + } + } + elseif ( $baselineResult -eq "ignore") { + if ( $currentTest.result -eq "Fail" ) { + Write-Verbose "ignoring failure on $key" + $ignoredList += $key + } + } + else { + Write-Verbose "$key had a change from the baseline" + + $currentResult = $currentTest.result + # Test exists in both test runs but does not match. Determine if this is a regression + # Pass -> Fail = Fail (Regression) + # Pass -> Skip = Skip + # Fail -> Pass = Fail (need to update baseline) + # Fail -> Skip = Skip + # Skip -> Fail = Fail (Should not happen) + # Skip -> Pass = Fail (should not happen) + + $lookupTable = @{ + 'Pass' = @{ + 'Fail' = @('Fail', "Test passes in baseline but fails in current run. If expected update ci.baseline.txt with '$($key):$($current.collectionName)=fail'"); + 'Skip' = @($null, 'Test was skipped due to missing dependencies') + }; + 'Fail' = @{ + 'Pass' = @('Fail', "Test fails in baseline but now passes. Update ci.baseline.txt with '$($key):$($current.collectionName)=pass'"); + 'Skip' = @($null, 'Test fails in baseline but is skipped in current run') + }; + 'Skip' = @{ + 'Fail' = @('Skip', "Test is skipped in baseline but fails in current run. Results are ignored") + 'Pass' = @('Skip', "Test is skipped in baseline but passes in current run. Results are ignored") + } + } + $resultList = $lookupTable[$baselineResult][$currentResult] + $result = $resultList[0] + $message = $resultList[1] + $time = $currentTest.time + Write-Verbose ">$key $message" + } + + if ($result -ne $null) { + Write-Verbose "Adding $key to result list" + $allTestResults[$key] = @{ name = $key; result = $result; message = $message; time = $time; abi_tag = $currentTest.abi_tag; baselineResult = $baselineResult; currentResult = $currentResult; features = $features } + } + } + + return @{ + assemblyName = $current.assemblyName; + assemblyStartDate = $current.assemblyStartDate; + assemblyStartTime = $current.assemblyStartTime; + assemblyTime = $current.assemblyTime; + collectionName = $current.collectionName; + collectionTime = $current.collectionTime; + allTests = $allTestResults; + ignored = $ignoredList + } +} + +<# +.SYNOPSIS +Takes the combined results object and writes it to an xml file. + +.DESCRIPTION +write_xunit_results takes the results object from combine_results, and writes the +results XML file to the correct location for the CI system to pick it up. + +.PARAMETER combined_results +The results object from combine_results. +#> +function write_xunit_results { + [CmdletBinding()] + Param( + $combined_results + ) + $allTests = $combined_results.allTests + $triplet = $combined_results.collectionName + + $filePath = "$outputDir\$triplet.xml" + if (Test-Path $filePath) { + Write-Verbose "removing old file $filepath" + rm $filePath + } + Write-Verbose "output filename: $filepath" + + $xmlWriter = New-Object System.Xml.XmlTextWriter($filePath, $Null) + $xmlWriter.Formatting = "Indented" + $xmlWriter.IndentChar = "`t" + + $xmlWriter.WriteStartDocument() + $xmlWriter.WriteStartElement("assemblies") + $xmlWriter.WriteStartElement("assembly") + $xmlWriter.WriteAttributeString("name", $combined_results.assemblyName) + $xmlWriter.WriteAttributeString("run-date", $combined_results.assemblyStartDate) + $xmlWriter.WriteAttributeString("run-time", $combined_results.assemblyStartTime) + $xmlWriter.WriteAttributeString("time", $combined_results.assemblyTime) + + $xmlWriter.WriteStartElement("collection") + $xmlWriter.WriteAttributeString("name", $triplet) + $xmlWriter.WriteAttributeString("time", $combined_results.collectionTime) + + foreach ($testName in $allTests.Keys) { + $test = $allTests[$testName] + + $xmlWriter.WriteStartElement("test") + + $fullTestName = "$($testName):$triplet" + $xmlWriter.WriteAttributeString("name", $fullTestName) + $xmlWriter.WriteAttributeString("method", $fullTestName) + $xmlWriter.WriteAttributeString("time", $test.time) + $xmlWriter.WriteAttributeString("result", $test.result) + + switch ($test.result) { + "Pass" { } # Do nothing + "Fail" { + $xmlWriter.WriteStartElement("failure") + $xmlWriter.WriteStartElement("message") + $xmlWriter.WriteCData($test.message) + $xmlWriter.WriteEndElement() #message + $xmlWriter.WriteEndElement() #failure + } + "Skip" { + $xmlWriter.WriteStartElement("reason") + $xmlWriter.WriteCData($test.message) + $xmlWriter.WriteEndElement() #reason + } + } + + $xmlWriter.WriteEndElement() # test + } + + + $xmlWriter.WriteEndElement() # collection + $xmlWriter.WriteEndElement() # assembly + $xmlWriter.WriteEndElement() # assemblies + $xmlWriter.WriteEndDocument() + $xmlWriter.Flush() + $xmlWriter.Close() +} + +<# +.SYNOPSIS +Saves the failure logs, and prints information to the screen for CI. + +.DESCRIPTION +save_failure_logs takes the combined_results object, saves the failure +logs to the correct location for the CI to pick them up, and writes pretty +information to the screen for the CI logs, so that one knows what's wrong. + +.PARAMETER combined_results +The results object from combine_results. +#> +function save_failure_logs { + [CmdletBinding()] + Param( + $combined_results + ) + $triplet = $combined_results.collectionName + $allTests = $combined_results.allTests + + # abi_tags of missing results (if any exist) + $missing_results = @() + + foreach ($testName in $allTests.Keys) { + $test = $allTests[$testName] + if ($test.result -eq "Fail") { + $path_to_failure_Logs = Join-Path "$outputDir" "failureLogs" + if ( -not (Test-Path $path_to_failure_Logs)) { + mkdir $path_to_failure_Logs | Out-Null + } + $path_to_triplet_Logs = Join-Path $path_to_failure_Logs "$triplet" + if ( -not (Test-Path $path_to_triplet_Logs)) { + mkdir $path_to_triplet_Logs | Out-Null + } + + $abi_tag = $test.abi_tag + $sourceDirectory = Join-Path "$failurelogDir" "$($abi_tag.substring(0,2))" + $sourceFilename = Join-Path $sourceDirectory "$abi_tag.zip" + Write-Verbose "searching for $sourceFilename" + + if ( Test-Path $sourceFilename) { + Write-Verbose "found failure log file" + + Write-Verbose "Uncompressing $sourceFilename to $outputDir\failureLogs\$triplet\" + Write-Host "Uncompressing $sourceFilename to $outputDir\failureLogs\$triplet\" + + $destination = Join-Path (Join-Path "$outputDir" "failureLogs") "$triplet" + + Expand-Archive -Path $sourceFilename -Destination $destination -Force + } + elseif ($test.currentState -eq "Pass") { + # The port is building, but is marked as expected to fail. There are no failure logs. + # Write a log with instructions how to fix it. + Write-Verbose "The port is building but marked as expected to fail, adding readme.txt with fixit instructions" + + $out_filename = Join-Path (Join-Path (Join-Path (Join-Path "$outputDir" "failureLogs") "$triplet") "$($test.name)") "readme.txt" + + $message = "Congradulations! The port $($test.name) builds for $triplet!`n" + $message += "For the CI tests to recognize this, please update ci.baseline.txt in your PR.`n" + $message += "Remove the line that looks like this:`n" + $message += " $($test.name):$triplet=fail`n" + $message | Out-File $out_filename -Encoding ascii + } + else { + $missing_results += $test.abi_tag + Write-Verbose "Missing failure logs for $($test.name)" + Join-Path (Join-Path (Join-Path "$outputDir" "failureLogs") "$triplet" ) "$($test.name)" | % { mkdir $_ } | Out-Null + } + + + + if ((Convert-Path "$outputDir\failureLogs\$triplet\$($test.name)" | Get-ChildItem).count -eq 0) { + Write-Verbose "The logs are empty, adding readme.txt" + + $readme_path = Join-Path (Join-Path (Join-Path (Join-Path "$outputDir" "failureLogs") "$triplet") "$($test.name)") "readme.txt" + + $message = "There are no build logs for $($test.name) build.`n" + $message += "This is usually because the build failed early and outside of a task that is logged.`n" + $message += "See the console output logs from vcpkg for more information on the failure.`n" + $message += "If the console output of the $($test.name) is missing you can trigger a rebuild`n" + $message += "in the test system by making any whitespace change to one of the files under`n" + $message += "the ports/$($test.name) directory or by asking a member of the vcpkg team to remove the`n" + $message += "tombstone for abi tag $abi_tag`n" + $message | Out-File $readme_path -Encoding ascii + } + } + } + + if ($missing_results.count -ne 0) { + $missing_tag_filename = Join-Path (Join-Path (Join-Path "$outputDir" "failureLogs") "$triplet") "missing_abi_tags.txt" + $missing_results | Out-File -FilePath $missing_tag_filename -Encoding ascii + } + Write-Verbose "$triplet logs saved: $(Get-ChildItem $outputDir\failureLogs\$triplet\ -ErrorAction Ignore)" + +} + +<# +.SYNOPSIS +Writes a pretty summary table to the CI log. + +.DESCRIPTION +Takes a hashtable which maps triplets to objects returned by the combine_results +cmdlet, and a list of missing triplets, and prints a really pretty summary table +to the CI logs. + +.PARAMETER complete_results +A hashtable which maps triplets to combine_results objects. + +.PARAMETER missing_triplets +A list of missing triplets. +#> +function write_summary_table { + [CmdletBinding()] + Param( + $complete_results, + $missing_triplets + ) + + $table = "" + + foreach ($triplet in $complete_results.Keys) { + $triplet_results = $complete_results[$triplet] + + if ($triplet_results.allTests.count -eq 0) { + $table += "$triplet CI build test results are clean`n`n" + } + else { + $portWidth = $triplet.length + #calculate the width of the first column + foreach ($testName in $triplet_results.allTests.Keys) { + $test = $triplet_results.allTests[$testName] + if ($portWidth -lt $test.name.length) { + $portWidth = $test.name.length + } + } + + # the header + $table += "|{0,-$portWidth}|result|features|notes`n" -f $triplet + $table += "|{0}|----|--------|-----`n" -f ("-" * $portWidth) + + # add each port results + foreach ($testName in $triplet_results.allTests.Keys | Sort-Object) { + $test = $triplet_results.allTests[$testName] + $notes = "" + if ($test.result -eq 'Fail') { + $notes = "**Regression**" + } + elseif ($test.result -eq 'Skip') { + if ($test.currentResult -eq 'Fail') { + $notes = "Previously skipped, not a regression" + } + else { + $notes = "Missing port dependency" + } + } + $notes = $test.message + $table += "|{0,-$portWidth}|{1,-4}|{2}|{3}`n" -f $test.name, $test.currentResult, $test.features, $notes + } + $table += "`n" + } + if ($triplet_results.ignored.Count -ne 0) { + $table += "The following build failures were ignored: $($triplet_results.ignored)`n" + } + } + + # Add list of missing triplets to the table + foreach ($triplet in $missing_triplets.Keys) { + $table += "$triplet results are inconclusive because it is missing logs from test run`n`n" + } + + $table +} + +<# +.SYNOPSIS +Writes short errors to the CI logs. + +.DESCRIPTION +write_errors_for_summary takes a hashtable from triplets to combine_results +objects, and writes short errors to the CI logs. + +.PARAMETER complete_results +A hashtable from triplets to combine_results objects. +#> +function write_errors_for_summary { + [CmdletBinding()] + Param( + $complete_results + ) + + $failure_found = $false + + Write-Verbose "preparing error output for Azure Devops" + + foreach ($triplet in $complete_results.Keys) { + $triplet_results = $complete_results[$triplet] + + Write-Verbose "searching $triplet triplet" + + # add each port results + foreach ($testName in $triplet_results.allTests.Keys) { + $test = $triplet_results.allTests[$testName] + + Write-Verbose "checking $($testName):$triplet $($test.result)" + + if ($test.result -eq 'Fail') { + $failure_found = $true + if ($test.currentResult -eq "pass") { + [System.Console]::Error.WriteLine( ` + "PASSING, REMOVE FROM FAIL LIST: $($test.name):$triplet ($baselineFile)" ` + ) + } + else { + [System.Console]::Error.WriteLine( ` + "REGRESSION: $($test.name):$triplet. If expected, add $($test.name):$triplet=fail to $baselineFile." ` + ) + } + } + } + } +} + + +$complete_results = @{ } +$missing_triplets = @{ } +foreach ( $triplet in $triplets) { + Write-Verbose "looking for $triplet logs" + + # The standard name for logs is: + # <triplet>.xml + # for example: + # x64-linux.xml + + $current_test_hash = build_test_results( Convert-Path "$logDir\$($triplet).xml" ) + $baseline_results = build_baseline_results -baselineFile $baselineFile -triplet $triplet + + if ($current_test_hash -eq $null) { + [System.Console]::Error.WriteLine("Missing $triplet test results in current test run") + $missing_triplets[$triplet] = "test" + } + else { + Write-Verbose "combining results..." + $complete_results[$triplet] = combine_results -baseline $baseline_results -current $current_test_hash + } +} + +Write-Verbose "done analizing results" + +# If there is only one triplet, add the triplet name to the result table file +if ($triplets.Count -eq 1) { + $result_table_name = $triplets[0] +} +else { + $result_table_name = "" +} + +if (-not $noTable) { + $table_path = Join-Path "$outputDir" "result_table$result_table_name.md" + + write_summary_table -complete_results $complete_results -missing_triplets $missing_triplets | Out-File -FilePath $table_path -Encoding ascii + + Write-Host "" + cat $table_path + + Write-Host "##vso[task.addattachment type=Distributedtask.Core.Summary;name=$result_table_name issue summary;]$table_path" +} + +foreach ( $triplet in $complete_results.Keys) { + $combined_results = $complete_results[$triplet] + if ( $failurelogDir -ne "") { + save_failure_logs -combined_results $combined_results + } + + write_xunit_results -combined_results $combined_results +} + + +# emit error last. Unlike the table output this is going to be seen in the "status" section of the pipeline +# and needs to be formatted for a single line. +if ($errorOnRegression) { + write_errors_for_summary -complete_results $complete_results + + if ($missing_triplets.Count -ne 0) { + $regression_log_directory = Join-Path "$outputDir" "failureLogs" + if ( -not (Test-Path $regression_log_directory)) { + mkdir $regression_log_directory | Out-Null + } + $file_path = Join-Path $regression_log_directory "missing_test_results.txt" + $message = "Test logs are missing for the following triplets: $($hash.Keys | %{"$($_)($($hash[$_]))"})`n" + $message += "Without this information the we are unable to determine if the build has regressions. `n" + $message += "Missing test logs are sometimes the result of failures in the pipeline infrastructure. `n" + $message += "If you beleave this is the case please alert a member of the vcpkg team to investigate. `n" + $message | Out-File $file_path -Encoding ascii + } +} diff --git a/scripts/azure-pipelines/azure-pipelines.yml b/scripts/azure-pipelines/azure-pipelines.yml new file mode 100644 index 000000000..c6c8a49f0 --- /dev/null +++ b/scripts/azure-pipelines/azure-pipelines.yml @@ -0,0 +1,56 @@ +# Copyright (c) Microsoft Corporation.
+# SPDX-License-Identifier: MIT
+#
+
+variables:
+ clean-tombstones: 'false'
+stages:
+ - stage: Clean_Tombstones
+ displayName: 'Clean Tombstones'
+ pool:
+ name: PrWin-2020-04-21-1
+ jobs:
+ - job:
+ displayName: 'Clean Tombstones'
+ condition: eq(variables['clean-tombstones'], 'true')
+ timeoutInMinutes: 10
+ steps:
+ - task: PowerShell@2
+ displayName: 'Initialize Environment'
+ inputs:
+ filePath: 'scripts/azure-pipelines/windows/initialize-environment.ps1'
+ - powershell: |
+ Remove-Item archives\fail -Force -Recurse
+ displayName: 'Delete archives\fail'
+
+ - stage: Build
+ jobs:
+ - template: windows/azure-pipelines.yml
+ parameters:
+ triplet: x86-windows
+ jobName: x86_windows
+
+ - template: windows/azure-pipelines.yml
+ parameters:
+ triplet: x64-windows
+ jobName: x64_windows
+
+ - template: windows/azure-pipelines.yml
+ parameters:
+ triplet: x64-windows-static
+ jobName: x64_windows_static
+
+ - template: windows/azure-pipelines.yml
+ parameters:
+ triplet: x64-uwp
+ jobName: x64_uwp
+
+ - template: windows/azure-pipelines.yml
+ parameters:
+ triplet: arm64-windows
+ jobName: arm64_windows
+
+ - template: windows/azure-pipelines.yml
+ parameters:
+ triplet: arm-uwp
+ jobName: arm_uwp
diff --git a/scripts/azure-pipelines/generate-skip-list.ps1 b/scripts/azure-pipelines/generate-skip-list.ps1 new file mode 100644 index 000000000..98c868eb9 --- /dev/null +++ b/scripts/azure-pipelines/generate-skip-list.ps1 @@ -0,0 +1,72 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: MIT +# + +<# +.SYNOPSIS +Generates a list of ports to skip in the CI. + +.DESCRIPTION +generate-skip-list takes a triplet, and the path to the ci.baseline.txt +file, and generates a skip list string to pass to vcpkg. + +.PARAMETER Triplet +The triplet to find skipped ports for. + +.PARAMETER BaselineFile +The path to the ci.baseline.txt file. +#> +[CmdletBinding()] +Param( + [string]$Triplet, + [string]$BaselineFile +) + +$ErrorActionPreference = 'Stop' + +if (-not (Test-Path -Path $BaselineFile)) { + Write-Error "Unable to find baseline file $BaselineFile" +} + +#read in the file, strip out comments and blank lines and spaces +$baselineListRaw = Get-Content -Path $BaselineFile ` + | Where-Object { -not ($_ -match "\s*#") } ` + | Where-Object { -not ( $_ -match "^\s*$") } ` + | ForEach-Object { $_ -replace "\s" } + +############################################################### +# This script is running at the beginning of the CI test, so do a little extra +# checking so things can fail early. + +#verify everything has a valid value +$missingValues = $baselineListRaw | Where-Object { -not ($_ -match "=\w") } + +if ($missingValues) { + Write-Error "The following are missing values: $missingValues" +} + +$invalidValues = $baselineListRaw ` + | Where-Object { -not ($_ -match "=(skip|pass|fail|ignore)$") } + +if ($invalidValues) { + Write-Error "The following have invalid values: $invalidValues" +} + +$baselineForTriplet = $baselineListRaw ` + | Where-Object { $_ -match ":$Triplet=" } + +# Verify there are no duplicates (redefinitions are not allowed) +$file_map = @{ } +foreach ($port in $baselineForTriplet | ForEach-Object { $_ -replace ":.*$" }) { + if ($null -ne $file_map[$port]) { + Write-Error ` + "$($port):$($Triplet) has multiple definitions in $baselineFile" + } + $file_map[$port] = $true +} + +# Format the skip list for the command line +$skip_list = $baselineForTriplet ` + | Where-Object { $_ -match "=skip$" } ` + | ForEach-Object { $_ -replace ":.*$" } +[string]::Join(",", $skip_list) diff --git a/scripts/azure-pipelines/windows/azure-pipelines.yml b/scripts/azure-pipelines/windows/azure-pipelines.yml new file mode 100644 index 000000000..1913f0c9b --- /dev/null +++ b/scripts/azure-pipelines/windows/azure-pipelines.yml @@ -0,0 +1,66 @@ +# Copyright (c) Microsoft Corporation.
+# SPDX-License-Identifier: MIT
+#
+
+jobs:
+- job: ${{ parameters.jobName }}
+ pool:
+ name: PrWin-2020-04-21-1
+
+ variables:
+ triplet: '${{ parameters.triplet }}'
+
+ timeoutInMinutes: 1440 # 1 day
+
+ steps:
+ - task: PowerShell@2
+ displayName: 'Initialize Environment'
+ inputs:
+ filePath: 'scripts/azure-pipelines/windows/initialize-environment.ps1'
+
+ - powershell: |
+ $baselineFile = "$(System.DefaultWorkingDirectory)\scripts\ci.baseline.txt"
+ $skipList = $(System.DefaultWorkingDirectory)\scripts\azure-pipelines\generate-skip-list.ps1 -Triplet "$(triplet)" -BaselineFile $baselineFile
+ Write-Host "baseline file: $baselineFile"
+ Write-Host "skip list: $skipList"
+ $(System.DefaultWorkingDirectory)\scripts\azure-pipelines\windows\ci-step.ps1 -Triplet "$(triplet)" -ExcludePorts $skipList
+ Write-Host "CI test script is complete"
+ errorActionPreference: continue
+ displayName: '** Build vcpkg and test ports **'
+
+ - powershell: |
+ $baseName = "$(triplet)"
+ $outputPathRoot = "$(System.ArtifactsDirectory)\raw xml results"
+ if(-not (Test-Path $outputPathRoot))
+ {
+ Write-Host "creating $outputPathRoot"
+ mkdir $outputPathRoot | Out-Null
+ }
+
+ $xmlPath = "$(System.DefaultWorkingDirectory)\test-full-ci.xml"
+ $outputXmlPath = "$outputPathRoot\$baseName.xml"
+
+ cp $xmlPath $(Build.ArtifactStagingDirectory)
+ Move-Item $xmlPath -Destination $outputXmlPath
+
+ # already in DevOps, no need for extra copies
+ rm $(System.DefaultWorkingDirectory)\console-out.txt -ErrorAction Ignore
+
+ Remove-Item "$(System.DefaultWorkingDirectory)\buildtrees\*" -Recurse -errorAction silentlycontinue
+ Remove-Item "$(System.DefaultWorkingDirectory)\packages\*" -Recurse -errorAction silentlycontinue
+ Remove-Item "$(System.DefaultWorkingDirectory)\installed\*" -Recurse -errorAction silentlycontinue
+ displayName: 'Collect logs and cleanup build'
+
+ - task: PowerShell@2
+ displayName: 'Analyze results and prepare test logs'
+ inputs:
+ failOnStderr: true
+ filePath: 'scripts/azure-pipelines/analyze-test-results.ps1'
+ arguments: '-baselineFile ''$(System.DefaultWorkingDirectory)\scripts\ci.baseline.txt'' -logDir ''$(System.ArtifactsDirectory)\raw xml results'' -failurelogDir ''archives\fail'' -outputDir ''$(Build.ArtifactStagingDirectory)'' -errorOnRegression -triplets ''$(triplet)'''
+
+ - task: PublishBuildArtifacts@1
+ displayName: 'Publish Artifact: $(triplet) port build failure logs'
+ inputs:
+ PathtoPublish: '$(Build.ArtifactStagingDirectory)\failureLogs'
+ ArtifactName: '$(triplet) port build failure logs'
+ condition: failed()
diff --git a/scripts/azure-pipelines/windows/ci-step.ps1 b/scripts/azure-pipelines/windows/ci-step.ps1 new file mode 100644 index 000000000..0e07895e0 --- /dev/null +++ b/scripts/azure-pipelines/windows/ci-step.ps1 @@ -0,0 +1,163 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: MIT +# + +<# +.SYNOPSIS +Runs the bootstrap and port install parts of the vcpkg CI for Windows + +.DESCRIPTION +There are multiple steps to the vcpkg CI; this is the most important one. +First, it runs `boostrap-vcpkg.bat` in order to build the tool itself; it +then installs either all of the ports specified, or all of the ports excluding +those which are passed in $ExcludePorts. Then, it runs `vcpkg ci` to access the +data, and prints all of the failures and successes to the Azure console. + +.PARAMETER Triplet +The triplet to run the installs for -- one of the triplets known by vcpkg, like +`x86-windows` and `x64-windows`. + +.PARAMETER OnlyIncludePorts +The set of ports to install. + +.PARAMETER ExcludePorts +If $OnlyIncludePorts is not passed, this set of ports is used to exclude ports to +install from the set of all ports. + +.PARAMETER AdditionalVcpkgFlags +Flags to pass to vcpkg in addition to the ports to install, and the triplet. +#> +[CmdletBinding()] +param( + [Parameter(Mandatory = $true)][string]$Triplet, + [string]$OnlyIncludePorts = '', + [string]$ExcludePorts = '', + [string]$AdditionalVcpkgFlags = '' +) + +Set-StrictMode -Version Latest + +$scriptsDir = Split-Path -parent $script:MyInvocation.MyCommand.Definition + +<# +.SYNOPSIS +Gets the first parent directory D of $startingDir such that D/$filename is a file. + +.DESCRIPTION +Get-FileRecursivelyUp Looks for a directory containing $filename, starting in +$startingDir, and then checking each parent directory of $startingDir in turn. +It returns the first directory it finds. +If the file is not found, the empty string is returned - this is likely to be +a bug. + +.PARAMETER startingDir +The directory to start looking for $filename in. + +.PARAMETER filename +The filename to look for. +#> +function Get-FileRecursivelyUp() { + [CmdletBinding()] + param( + [Parameter(Mandatory = $true)][string]$startingDir, + [Parameter(Mandatory = $true)][string]$filename + ) + + $currentDir = $startingDir + + while ($currentDir.Length -gt 0 -and -not (Test-Path "$currentDir\$filename")) { + Write-Verbose "Examining $currentDir for $filename" + $currentDir = Split-Path $currentDir -Parent + } + + if ($currentDir.Length -eq 0) { + Write-Warning "None of $startingDir's parent directories contain $filename. This is likely a bug." + } + + Write-Verbose "Examining $currentDir for $filename - Found" + return $currentDir +} + +<# +.SYNOPSIS +Removes a file or directory, with backoff in the directory case. + +.DESCRIPTION +Remove-Item -Recurse occasionally fails spuriously; in order to get around this, +we remove with backoff. At a maximum, we will wait 180s before giving up. + +.PARAMETER Path +The path to remove. +#> +function Remove-VcpkgItem { + [CmdletBinding()] + param([Parameter(Mandatory = $true)][string]$Path) + + if ([string]::IsNullOrEmpty($Path)) { + return + } + + if (Test-Path $Path) { + # Remove-Item -Recurse occasionally fails. This is a workaround + if ((Get-Item $Path) -is [System.IO.DirectoryInfo]) { + Remove-Item $Path -Force -Recurse -ErrorAction SilentlyContinue + for ($i = 0; $i -le 60 -and (Test-Path $Path); $i++) { # ~180s max wait time + Start-Sleep -m (100 * $i) + Remove-Item $Path -Force -Recurse -ErrorAction SilentlyContinue + } + + if (Test-Path $Path) { + Write-Error "$Path was unable to be fully deleted." + throw; + } + } + else { + Remove-Item $Path -Force + } + } +} + +$vcpkgRootDir = Get-FileRecursivelyUp $scriptsDir .vcpkg-root + +Write-Host "Bootstrapping vcpkg ..." +& "$vcpkgRootDir\bootstrap-vcpkg.bat" -Verbose +if (!$?) { throw "bootstrap failed" } +Write-Host "Bootstrapping vcpkg ... done." + +$ciXmlPath = "$vcpkgRootDir\test-full-ci.xml" +$consoleOuputPath = "$vcpkgRootDir\console-out.txt" +Remove-VcpkgItem $ciXmlPath + +$env:VCPKG_FEATURE_FLAGS = "binarycaching" + +if (![string]::IsNullOrEmpty($OnlyIncludePorts)) { + ./vcpkg install --triplet $Triplet $OnlyIncludePorts $AdditionalVcpkgFlags ` + "--x-xunit=$ciXmlPath" | Tee-Object -FilePath "$consoleOuputPath" +} +else { + $exclusions = "" + if (![string]::IsNullOrEmpty($ExcludePorts)) { + $exclusions = "--exclude=$ExcludePorts" + } + + if ( $Triplet -notmatch "x86-windows" -and $Triplet -notmatch "x64-windows" ) { + # WORKAROUND: the x86-windows flavors of these are needed for all + # cross-compilation, but they are not auto-installed. + # Install them so the CI succeeds + ./vcpkg install "protobuf:x86-windows" "boost-build:x86-windows" "sqlite3:x86-windows" + if (-not $?) { throw "Failed to install protobuf & boost-build & sqlite3" } + } + + # Turn all error messages into strings for output in the CI system. + # This is needed due to the way the public Azure DevOps turns error output to pipeline errors, + # even when told to ignore error output. + ./vcpkg ci $Triplet $AdditionalVcpkgFlags "--x-xunit=$ciXmlPath" $exclusions 2>&1 ` + | ForEach-Object { + if ($_ -is [System.Management.Automation.ErrorRecord]) { $_.ToString() } else { $_ } + } + + # Phasing out the console output (it is already saved in DevOps) Create a dummy file for now. + Set-Content -LiteralPath "$consoleOuputPath" -Value '' +} + +Write-Host "CI test is complete" diff --git a/scripts/azure-pipelines/windows/create-vmss.ps1 b/scripts/azure-pipelines/windows/create-vmss.ps1 new file mode 100644 index 000000000..099c7dbfb --- /dev/null +++ b/scripts/azure-pipelines/windows/create-vmss.ps1 @@ -0,0 +1,458 @@ +# Copyright (c) Microsoft Corporation.
+# SPDX-License-Identifier: MIT
+#
+#
+
+<#
+.SYNOPSIS
+Creates a Windows virtual machine scale set, set up for vcpkg's CI.
+
+.DESCRIPTION
+create-vmss.ps1 creates an Azure Windows VM scale set, set up for vcpkg's CI
+system. See https://docs.microsoft.com/en-us/azure/virtual-machine-scale-sets/overview
+for more information.
+
+This script assumes you have installed Azure tools into PowerShell by following the instructions
+at https://docs.microsoft.com/en-us/powershell/azure/install-az-ps?view=azps-3.6.1
+or are running from Azure Cloud Shell.
+#>
+
+$Location = 'SouthCentralUS'
+$Prefix = 'PrWin-' + (Get-Date -Format 'yyyy-MM-dd')
+$VMSize = 'Standard_F16s_v2'
+$ProtoVMName = 'PROTOTYPE'
+$LiveVMPrefix = 'BUILD'
+$WindowsServerSku = '2019-Datacenter'
+$InstalledDiskSizeInGB = 1024
+$ErrorActionPreference = 'Stop'
+
+$ProgressActivity = 'Creating Scale Set'
+$TotalProgress = 12
+$CurrentProgress = 1
+
+<#
+.SYNOPSIS
+Returns whether there's a name collision in the resource group.
+
+.DESCRIPTION
+Find-ResourceGroupNameCollision takes a list of resources, and checks if $Test
+collides names with any of the resources.
+
+.PARAMETER Test
+The name to test.
+
+.PARAMETER Resources
+The list of resources.
+#>
+function Find-ResourceGroupNameCollision {
+ [CmdletBinding()]
+ Param([string]$Test, $Resources)
+
+ foreach ($resource in $Resources) {
+ if ($resource.ResourceGroupName -eq $Test) {
+ return $true
+ }
+ }
+
+ return $false
+}
+
+<#
+.SYNOPSIS
+Attempts to find a name that does not collide with any resources in the resource group.
+
+.DESCRIPTION
+Find-ResourceGroupName takes a set of resources from Get-AzResourceGroup, and finds the
+first name in {$Prefix, $Prefix-1, $Prefix-2, ...} such that the name doesn't collide with
+any of the resources in the resource group.
+
+.PARAMETER Prefix
+The prefix of the final name; the returned name will be of the form "$Prefix(-[1-9][0-9]*)?"
+#>
+function Find-ResourceGroupName {
+ [CmdletBinding()]
+ Param([string] $Prefix)
+
+ $resources = Get-AzResourceGroup
+ $result = $Prefix
+ $suffix = 0
+ while (Find-ResourceGroupNameCollision -Test $result -Resources $resources) {
+ $suffix++
+ $result = "$Prefix-$suffix"
+ }
+
+ return $result
+}
+
+<#
+.SYNOPSIS
+Creates a randomly generated password.
+
+.DESCRIPTION
+New-Password generates a password, randomly, of length $Length, containing
+only alphanumeric characters (both uppercase and lowercase).
+
+.PARAMETER Length
+The length of the returned password.
+#>
+function New-Password {
+ Param ([int] $Length = 32)
+
+ $Chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+ $result = ''
+ for ($idx = 0; $idx -lt $Length; $idx++) {
+ # NOTE: this should probably use RNGCryptoServiceProvider
+ $result += $Chars[(Get-Random -Minimum 0 -Maximum $Chars.Length)]
+ }
+
+ return $result
+}
+
+<#
+.SYNOPSIS
+Waits for the shutdown of the specified resource.
+
+.DESCRIPTION
+Wait-Shutdown takes a VM, and checks if there's a 'PowerState/stopped'
+code; if there is, it returns. If there isn't, it waits ten seconds and
+tries again.
+
+.PARAMETER ResourceGroupName
+The name of the resource group to look up the VM in.
+
+.PARAMETER Name
+The name of the virtual machine to wait on.
+#>
+function Wait-Shutdown {
+ [CmdletBinding()]
+ Param([string]$ResourceGroupName, [string]$Name)
+
+ Write-Host "Waiting for $Name to stop..."
+ while ($true) {
+ $Vm = Get-AzVM -ResourceGroupName $ResourceGroupName -Name $Name -Status
+ $highestStatus = $Vm.Statuses.Count
+ for ($idx = 0; $idx -lt $highestStatus; $idx++) {
+ if ($Vm.Statuses[$idx].Code -eq 'PowerState/stopped') {
+ return
+ }
+ }
+
+ Write-Host "... not stopped yet, sleeping for 10 seconds"
+ Start-Sleep -Seconds 10
+ }
+}
+
+<#
+.SYNOPSIS
+Sanitizes a name to be used in a storage account.
+
+.DESCRIPTION
+Sanitize-Name takes a string, and removes all of the '-'s and
+lowercases the string, since storage account names must have no
+'-'s and must be completely lowercase alphanumeric. It then makes
+certain that the length of the string is not greater than 24,
+since that is invalid.
+
+.PARAMETER RawName
+The name to sanitize.
+#>
+function Sanitize-Name {
+ [CmdletBinding()]
+ Param(
+ [string]$RawName
+ )
+
+ $result = $RawName.Replace('-', '').ToLowerInvariant()
+ if ($result.Length -gt 24) {
+ Write-Error 'Sanitized name for storage account $result was too long.'
+ }
+
+ return $result
+}
+
+####################################################################################################
+Write-Progress `
+ -Activity $ProgressActivity `
+ -Status 'Creating resource group' `
+ -PercentComplete (100 / $TotalProgress * $CurrentProgress++)
+
+$ResourceGroupName = Find-ResourceGroupName $Prefix
+$AdminPW = New-Password
+New-AzResourceGroup -Name $ResourceGroupName -Location $Location
+$AdminPWSecure = ConvertTo-SecureString $AdminPW -AsPlainText -Force
+$Credential = New-Object System.Management.Automation.PSCredential ("AdminUser", $AdminPWSecure)
+
+####################################################################################################
+Write-Progress `
+ -Activity $ProgressActivity `
+ -Status 'Creating virtual network' `
+ -PercentComplete (100 / $TotalProgress * $CurrentProgress++)
+
+$allowHttp = New-AzNetworkSecurityRuleConfig `
+ -Name AllowHTTP `
+ -Description 'Allow HTTP(S)' `
+ -Access Allow `
+ -Protocol Tcp `
+ -Direction Outbound `
+ -Priority 1008 `
+ -SourceAddressPrefix * `
+ -SourcePortRange * `
+ -DestinationAddressPrefix * `
+ -DestinationPortRange @(80, 443)
+
+$allowDns = New-AzNetworkSecurityRuleConfig `
+ -Name AllowDNS `
+ -Description 'Allow DNS' `
+ -Access Allow `
+ -Protocol * `
+ -Direction Outbound `
+ -Priority 1009 `
+ -SourceAddressPrefix * `
+ -SourcePortRange * `
+ -DestinationAddressPrefix * `
+ -DestinationPortRange 53
+
+$allowStorage = New-AzNetworkSecurityRuleConfig `
+ -Name AllowStorage `
+ -Description 'Allow Storage' `
+ -Access Allow `
+ -Protocol * `
+ -Direction Outbound `
+ -Priority 1010 `
+ -SourceAddressPrefix VirtualNetwork `
+ -SourcePortRange * `
+ -DestinationAddressPrefix Storage `
+ -DestinationPortRange *
+
+$denyEverythingElse = New-AzNetworkSecurityRuleConfig `
+ -Name DenyElse `
+ -Description 'Deny everything else' `
+ -Access Deny `
+ -Protocol * `
+ -Direction Outbound `
+ -Priority 1011 `
+ -SourceAddressPrefix * `
+ -SourcePortRange * `
+ -DestinationAddressPrefix * `
+ -DestinationPortRange *
+
+$NetworkSecurityGroupName = $ResourceGroupName + 'NetworkSecurity'
+$NetworkSecurityGroup = New-AzNetworkSecurityGroup `
+ -Name $NetworkSecurityGroupName `
+ -ResourceGroupName $ResourceGroupName `
+ -Location $Location `
+ -SecurityRules @($allowHttp, $allowDns, $allowStorage, $denyEverythingElse)
+
+$SubnetName = $ResourceGroupName + 'Subnet'
+$Subnet = New-AzVirtualNetworkSubnetConfig `
+ -Name $SubnetName `
+ -AddressPrefix "10.0.0.0/16" `
+ -NetworkSecurityGroup $NetworkSecurityGroup
+
+$VirtualNetworkName = $ResourceGroupName + 'Network'
+$VirtualNetwork = New-AzVirtualNetwork `
+ -Name $VirtualNetworkName `
+ -ResourceGroupName $ResourceGroupName `
+ -Location $Location `
+ -AddressPrefix "10.0.0.0/16" `
+ -Subnet $Subnet
+
+####################################################################################################
+Write-Progress `
+ -Activity $ProgressActivity `
+ -Status 'Creating archives storage account' `
+ -PercentComplete (100 / $TotalProgress * $CurrentProgress++)
+
+$StorageAccountName = Sanitize-Name $ResourceGroupName
+
+New-AzStorageAccount `
+ -ResourceGroupName $ResourceGroupName `
+ -Location $Location `
+ -Name $StorageAccountName `
+ -SkuName 'Standard_LRS' `
+ -Kind StorageV2
+
+$StorageAccountKeys = Get-AzStorageAccountKey `
+ -ResourceGroupName $ResourceGroupName `
+ -Name $StorageAccountName
+
+$StorageAccountKey = $StorageAccountKeys[0].Value
+
+$StorageContext = New-AzStorageContext `
+ -StorageAccountName $StorageAccountName `
+ -StorageAccountKey $StorageAccountKey
+
+$ArchivesFiles = New-AzStorageShare -Name 'archives' -Context $StorageContext
+Set-AzStorageShareQuota -ShareName 'archives' -Context $StorageContext -Quota 5120
+$LogFiles = New-AzStorageShare -Name 'logs' -Context $StorageContext
+Set-AzStorageShareQuota -ShareName 'logs' -Context $StorageContext -Quota 64
+
+####################################################################################################
+Write-Progress `
+ -Activity 'Creating prototype VM' `
+ -PercentComplete (100 / $TotalProgress * $CurrentProgress++)
+
+$NicName = $ResourceGroupName + 'NIC'
+$Nic = New-AzNetworkInterface `
+ -Name $NicName `
+ -ResourceGroupName $ResourceGroupName `
+ -Location $Location `
+ -Subnet $VirtualNetwork.Subnets[0]
+
+$VM = New-AzVMConfig -Name $ProtoVMName -VMSize $VMSize
+$VM = Set-AzVMOperatingSystem `
+ -VM $VM `
+ -Windows `
+ -ComputerName $ProtoVMName `
+ -Credential $Credential `
+ -ProvisionVMAgent `
+ -EnableAutoUpdate
+
+$VM = Add-AzVMNetworkInterface -VM $VM -Id $Nic.Id
+$VM = Set-AzVMSourceImage `
+ -VM $VM `
+ -PublisherName 'MicrosoftWindowsServer' `
+ -Offer 'WindowsServer' `
+ -Skus $WindowsServerSku `
+ -Version latest
+
+$InstallDiskName = $ProtoVMName + "InstallDisk"
+$VM = Add-AzVMDataDisk `
+ -Vm $VM `
+ -Name $InstallDiskName `
+ -Lun 0 `
+ -Caching ReadWrite `
+ -CreateOption Empty `
+ -DiskSizeInGB $InstalledDiskSizeInGB `
+ -StorageAccountType 'StandardSSD_LRS'
+
+$VM = Set-AzVMBootDiagnostic -VM $VM -Disable
+New-AzVm `
+ -ResourceGroupName $ResourceGroupName `
+ -Location $Location `
+ -VM $VM
+
+####################################################################################################
+Write-Progress `
+ -Activity $ProgressActivity `
+ -Status 'Running provisioning script provision-image.ps1 in VM' `
+ -PercentComplete (100 / $TotalProgress * $CurrentProgress++)
+
+Invoke-AzVMRunCommand `
+ -ResourceGroupName $ResourceGroupName `
+ -VMName $ProtoVMName `
+ -CommandId 'RunPowerShellScript' `
+ -ScriptPath "$PSScriptRoot\provision-image.ps1" `
+ -Parameter @{AdminUserPassword = $AdminPW; `
+ StorageAccountName=$StorageAccountName; `
+ StorageAccountKey=$StorageAccountKey;}
+
+####################################################################################################
+Write-Progress `
+ -Activity $ProgressActivity `
+ -Status 'Restarting VM' `
+ -PercentComplete (100 / $TotalProgress * $CurrentProgress++)
+
+Restart-AzVM -ResourceGroupName $ResourceGroupName -Name $ProtoVMName
+
+####################################################################################################
+Write-Progress `
+ -Activity $ProgressActivity `
+ -Status 'Running provisioning script sysprep.ps1 in VM' `
+ -PercentComplete (100 / $TotalProgress * $CurrentProgress++)
+
+Invoke-AzVMRunCommand `
+ -ResourceGroupName $ResourceGroupName `
+ -VMName $ProtoVMName `
+ -CommandId 'RunPowerShellScript' `
+ -ScriptPath "$PSScriptRoot\sysprep.ps1"
+
+####################################################################################################
+Write-Progress `
+ -Activity $ProgressActivity `
+ -Status 'Waiting for VM to shut down' `
+ -PercentComplete (100 / $TotalProgress * $CurrentProgress++)
+
+Wait-Shutdown -ResourceGroupName $ResourceGroupName -Name $ProtoVMName
+
+####################################################################################################
+Write-Progress `
+ -Activity $ProgressActivity `
+ -Status 'Converting VM to Image' `
+ -PercentComplete (100 / $TotalProgress * $CurrentProgress++)
+
+Stop-AzVM `
+ -ResourceGroupName $ResourceGroupName `
+ -Name $ProtoVMName `
+ -Force
+
+Set-AzVM `
+ -ResourceGroupName $ResourceGroupName `
+ -Name $ProtoVMName `
+ -Generalized
+
+$VM = Get-AzVM -ResourceGroupName $ResourceGroupName -Name $ProtoVMName
+$PrototypeOSDiskName = $VM.StorageProfile.OsDisk.Name
+$ImageConfig = New-AzImageConfig -Location $Location -SourceVirtualMachineId $VM.ID
+$Image = New-AzImage -Image $ImageConfig -ImageName $ProtoVMName -ResourceGroupName $ResourceGroupName
+
+####################################################################################################
+Write-Progress `
+ -Activity $ProgressActivity `
+ -Status 'Deleting unused VM and disk' `
+ -PercentComplete (100 / $TotalProgress * $CurrentProgress++)
+
+Remove-AzVM -Id $VM.ID -Force
+Remove-AzDisk -ResourceGroupName $ResourceGroupName -DiskName $PrototypeOSDiskName -Force
+Remove-AzDisk -ResourceGroupName $ResourceGroupName -DiskName $InstallDiskName -Force
+
+####################################################################################################
+Write-Progress `
+ -Activity $ProgressActivity `
+ -Status 'Creating scale set' `
+ -PercentComplete (100 / $TotalProgress * $CurrentProgress++)
+
+$VmssIpConfigName = $ResourceGroupName + 'VmssIpConfig'
+$VmssIpConfig = New-AzVmssIpConfig -SubnetId $Nic.IpConfigurations[0].Subnet.Id -Primary -Name $VmssIpConfigName
+$VmssName = $ResourceGroupName + 'Vmss'
+$Vmss = New-AzVmssConfig `
+ -Location $Location `
+ -SkuCapacity 6 `
+ -SkuName $VMSize `
+ -SkuTier 'Standard' `
+ -Overprovision $false `
+ -UpgradePolicyMode Manual
+
+$Vmss = Add-AzVmssNetworkInterfaceConfiguration `
+ -VirtualMachineScaleSet $Vmss `
+ -Primary $true `
+ -IpConfiguration $VmssIpConfig `
+ -NetworkSecurityGroupId $NetworkSecurityGroup.Id `
+ -Name $NicName
+
+$Vmss = Set-AzVmssOsProfile `
+ -VirtualMachineScaleSet $Vmss `
+ -ComputerNamePrefix $LiveVMPrefix `
+ -AdminUsername 'AdminUser' `
+ -AdminPassword $AdminPW `
+ -WindowsConfigurationProvisionVMAgent $true `
+ -WindowsConfigurationEnableAutomaticUpdate $true
+
+$Vmss = Set-AzVmssStorageProfile `
+ -VirtualMachineScaleSet $Vmss `
+ -OsDiskCreateOption 'FromImage' `
+ -OsDiskCaching ReadWrite `
+ -ImageReferenceId $Image.Id
+
+New-AzVmss `
+ -ResourceGroupName $ResourceGroupName `
+ -Name $VmssName `
+ -VirtualMachineScaleSet $Vmss
+
+####################################################################################################
+Write-Progress -Activity $ProgressActivity -Completed
+Write-Host "Location: $Location"
+Write-Host "Resource group name: $ResourceGroupName"
+Write-Host "User name: AdminUser"
+Write-Host "Using generated password: $AdminPW"
+Write-Host 'Finished!'
diff --git a/scripts/azure-pipelines/windows/initialize-environment.ps1 b/scripts/azure-pipelines/windows/initialize-environment.ps1 new file mode 100644 index 000000000..b86006a9c --- /dev/null +++ b/scripts/azure-pipelines/windows/initialize-environment.ps1 @@ -0,0 +1,93 @@ +# Copyright (c) Microsoft Corporation.
+# SPDX-License-Identifier: MIT
+#
+<#
+.SYNOPSIS
+Sets up the environment to run other vcpkg CI steps in an Azure Pipelines job.
+
+.DESCRIPTION
+This script maps network drives from infrastructure and cleans out anything that
+might have been leftover from a previous run.
+
+.PARAMETER ForceAllPortsToRebuildKey
+A subdirectory / key to use to force a build without any previous run caching,
+if necessary.
+#>
+
+[CmdletBinding()]
+Param(
+ [string]$ForceAllPortsToRebuildKey = ''
+)
+
+$StorageAccountName = $env:StorageAccountName
+$StorageAccountKey = $env:StorageAccountKey
+
+function Remove-DirectorySymlink {
+ Param([string]$Path)
+ if (Test-Path $Path) {
+ [System.IO.Directory]::Delete($Path)
+ }
+}
+
+Write-Host 'Setting up archives mount'
+if (-Not (Test-Path W:)) {
+ net use W: "\\$StorageAccountName.file.core.windows.net\archives" /u:"AZURE\$StorageAccountName" $StorageAccountKey
+}
+
+Write-Host 'Setting up logs mount'
+if (-Not (Test-Path L:)) {
+ net use L: "\\$StorageAccountName.file.core.windows.net\logs" /u:"AZURE\$StorageAccountName" $StorageAccountKey
+}
+
+Write-Host 'Creating downloads directory'
+mkdir D:\downloads -ErrorAction SilentlyContinue
+
+# Delete entries in the downloads folder, except:
+# those in the 'tools' folder
+# those last accessed in the last 30 days
+Get-ChildItem -Path D:\downloads -Exclude "tools" `
+ | Where-Object{ $_.LastAccessTime -lt (get-Date).AddDays(-30) } `
+ | ForEach-Object{Remove-Item -Path $_ -Recurse -Force}
+
+# Msys sometimes leaves a database lock file laying around, especially if there was a failed job
+# which causes unrelated failures in jobs that run later on the machine.
+# work around this by just removing the vcpkg installed msys2 if it exists
+if( Test-Path D:\downloads\tools\msys2 )
+{
+ Write-Host "removing previously installed msys2"
+ Remove-Item D:\downloads\tools\msys2 -Recurse -Force
+}
+
+Write-Host 'Setting up archives path...'
+if ([string]::IsNullOrWhiteSpace($ForceAllPortsToRebuildKey))
+{
+ $archivesPath = 'W:\'
+}
+else
+{
+ $archivesPath = "W:\force\$ForceAllPortsToRebuildKey"
+ if (-Not (Test-Path $fullPath)) {
+ Write-Host 'Creating $archivesPath'
+ mkdir $archivesPath
+ }
+}
+
+Write-Host "Linking archives => $archivesPath"
+Remove-DirectorySymlink archives
+cmd /c "mklink /D archives $archivesPath"
+
+Write-Host 'Linking installed => E:\installed'
+Remove-DirectorySymlink installed
+Remove-Item E:\installed -Recurse -Force -ErrorAction SilentlyContinue
+mkdir E:\installed
+cmd /c "mklink /D installed E:\installed"
+
+Write-Host 'Linking downloads => D:\downloads'
+Remove-DirectorySymlink downloads
+cmd /c "mklink /D downloads D:\downloads"
+
+Write-Host 'Cleaning buildtrees'
+Remove-Item buildtrees\* -Recurse -Force -errorAction silentlycontinue
+
+Write-Host 'Cleaning packages'
+Remove-Item packages\* -Recurse -Force -errorAction silentlycontinue
diff --git a/scripts/azure-pipelines/windows/provision-image.ps1 b/scripts/azure-pipelines/windows/provision-image.ps1 new file mode 100644 index 000000000..9a33461ee --- /dev/null +++ b/scripts/azure-pipelines/windows/provision-image.ps1 @@ -0,0 +1,447 @@ +# Copyright (c) Microsoft Corporation.
+# SPDX-License-Identifier: MIT
+
+<#
+.SYNOPSIS
+Sets up a machine to be an image for a scale set.
+
+.DESCRIPTION
+provision-image.ps1 runs on an existing, freshly provisioned virtual machine,
+and sets that virtual machine up as a vcpkg build machine. After this is done,
+(outside of this script), we take that machine and make it an image to be copied
+for setting up new VMs in the scale set.
+
+This script must either be run as admin, or one must pass AdminUserPassword;
+if the script is run with AdminUserPassword, it runs itself again as an
+administrator.
+
+.PARAMETER AdminUserPassword
+The administrator user's password; if this is $null, or not passed, then the
+script assumes it's running on an administrator account.
+
+.PARAMETER StorageAccountName
+The name of the storage account. Stored in the environment variable %StorageAccountName%.
+Used by the CI system to access the global storage.
+
+.PARAMETER StorageAccountKey
+The key of the storage account. Stored in the environment variable %StorageAccountKey%.
+Used by the CI system to access the global storage.
+#>
+param(
+ [string]$AdminUserPassword = $null,
+ [string]$StorageAccountName = $null,
+ [string]$StorageAccountKey = $null
+)
+
+$ErrorActionPreference = 'Stop'
+
+<#
+.SYNOPSIS
+Gets a random file path in the temp directory.
+
+.DESCRIPTION
+Get-TempFilePath takes an extension, and returns a path with a random
+filename component in the temporary directory with that extension.
+
+.PARAMETER Extension
+The extension to use for the path.
+#>
+Function Get-TempFilePath {
+ Param(
+ [String]$Extension
+ )
+
+ if ([String]::IsNullOrWhiteSpace($Extension)) {
+ throw 'Missing Extension'
+ }
+
+ $tempPath = [System.IO.Path]::GetTempPath()
+ $tempName = [System.IO.Path]::GetRandomFileName() + '.' + $Extension
+ return Join-Path $tempPath $tempName
+}
+
+if (-not [string]::IsNullOrEmpty($AdminUserPassword)) {
+ Write-Host "AdminUser password supplied; switching to AdminUser"
+ $PsExecPath = Get-TempFilePath -Extension 'exe'
+ Write-Host "Downloading psexec to $PsExecPath"
+ & curl.exe -L -o $PsExecPath -s -S https://live.sysinternals.com/PsExec64.exe
+ $PsExecArgs = @(
+ '-u',
+ 'AdminUser',
+ '-p',
+ $AdminUserPassword,
+ '-accepteula',
+ '-h',
+ 'C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe',
+ '-ExecutionPolicy',
+ 'Unrestricted',
+ '-File',
+ $PSCommandPath
+ )
+
+ if (-Not ([string]::IsNullOrWhiteSpace($StorageAccountName))) {
+ $PsExecArgs += '-StorageAccountName'
+ $PsExecArgs += $StorageAccountName
+ }
+
+ if (-Not ([string]::IsNullOrWhiteSpace($StorageAccountKey))) {
+ $PsExecArgs += '-StorageAccountKey'
+ $PsExecArgs += $StorageAccountKey
+ }
+
+ Write-Host "Executing $PsExecPath " + @PsExecArgs
+
+ $proc = Start-Process -FilePath $PsExecPath -ArgumentList $PsExecArgs -Wait -PassThru
+ Write-Host 'Cleaning up...'
+ Remove-Item $PsExecPath
+ exit $proc.ExitCode
+}
+
+$VisualStudioBootstrapperUrl = 'https://aka.ms/vs/16/release/vs_enterprise.exe'
+$Workloads = @(
+ 'Microsoft.VisualStudio.Workload.NativeDesktop',
+ 'Microsoft.VisualStudio.Workload.Universal',
+ 'Microsoft.VisualStudio.Component.VC.Tools.x86.x64',
+ 'Microsoft.VisualStudio.Component.VC.Tools.ARM',
+ 'Microsoft.VisualStudio.Component.VC.Tools.ARM64',
+ 'Microsoft.VisualStudio.Component.VC.ATL',
+ 'Microsoft.VisualStudio.Component.VC.ATLMFC',
+ 'Microsoft.VisualStudio.Component.VC.v141.x86.x64.Spectre',
+ 'Microsoft.VisualStudio.Component.Windows10SDK.18362',
+ 'Microsoft.Net.Component.4.8.SDK',
+ 'Microsoft.Component.NetFX.Native'
+)
+
+$MpiUrl = 'https://download.microsoft.com/download/A/E/0/AE002626-9D9D-448D-8197-1EA510E297CE/msmpisetup.exe'
+
+$CudaUrl = 'https://developer.download.nvidia.com/compute/cuda/10.1/Prod/local_installers/cuda_10.1.243_426.00_win10.exe'
+$CudaFeatures = 'nvcc_10.1 cuobjdump_10.1 nvprune_10.1 cupti_10.1 gpu_library_advisor_10.1 memcheck_10.1 ' + `
+ 'nvdisasm_10.1 nvprof_10.1 visual_profiler_10.1 visual_studio_integration_10.1 cublas_10.1 cublas_dev_10.1 ' + `
+ 'cudart_10.1 cufft_10.1 cufft_dev_10.1 curand_10.1 curand_dev_10.1 cusolver_10.1 cusolver_dev_10.1 cusparse_10.1 ' + `
+ 'cusparse_dev_10.1 nvgraph_10.1 nvgraph_dev_10.1 npp_10.1 npp_dev_10.1 nvrtc_10.1 nvrtc_dev_10.1 nvml_dev_10.1 ' + `
+ 'occupancy_calculator_10.1 fortran_examples_10.1'
+
+$BinSkimUrl = 'https://www.nuget.org/api/v2/package/Microsoft.CodeAnalysis.BinSkim/1.6.0'
+
+$ErrorActionPreference = 'Stop'
+$ProgressPreference = 'SilentlyContinue'
+
+<#
+.SYNOPSIS
+Writes a message to the screen depending on ExitCode.
+
+.DESCRIPTION
+Since msiexec can return either 0 or 3010 successfully, in both cases
+we write that installation succeeded, and which exit code it exited with.
+If msiexec returns anything else, we write an error.
+
+.PARAMETER ExitCode
+The exit code that msiexec returned.
+#>
+Function PrintMsiExitCodeMessage {
+ Param(
+ $ExitCode
+ )
+
+ # 3010 is probably ERROR_SUCCESS_REBOOT_REQUIRED
+ if ($ExitCode -eq 0 -or $ExitCode -eq 3010) {
+ Write-Host "Installation successful! Exited with $ExitCode."
+ }
+ else {
+ Write-Error "Installation failed! Exited with $ExitCode."
+ }
+}
+
+<#
+.SYNOPSIS
+Install Visual Studio.
+
+.DESCRIPTION
+InstallVisualStudio takes the $Workloads array, and installs it with the
+installer that's pointed at by $BootstrapperUrl.
+
+.PARAMETER Workloads
+The set of VS workloads to install.
+
+.PARAMETER BootstrapperUrl
+The URL of the Visual Studio installer, i.e. one of vs_*.exe.
+
+.PARAMETER InstallPath
+The path to install Visual Studio at.
+
+.PARAMETER Nickname
+The nickname to give the installation.
+#>
+Function InstallVisualStudio {
+ Param(
+ [String[]]$Workloads,
+ [String]$BootstrapperUrl,
+ [String]$InstallPath = $null,
+ [String]$Nickname = $null
+ )
+
+ try {
+ Write-Host 'Downloading Visual Studio...'
+ [string]$bootstrapperExe = Get-TempFilePath -Extension 'exe'
+ curl.exe -L -o $bootstrapperExe -s -S $BootstrapperUrl
+ Write-Host "Installing Visual Studio..."
+ $args = @('/c', $bootstrapperExe, '--quiet', '--norestart', '--wait', '--nocache')
+ foreach ($workload in $Workloads) {
+ $args += '--add'
+ $args += $workload
+ }
+
+ if (-not ([String]::IsNullOrWhiteSpace($InstallPath))) {
+ $args += '--installpath'
+ $args += $InstallPath
+ }
+
+ if (-not ([String]::IsNullOrWhiteSpace($Nickname))) {
+ $args += '--nickname'
+ $args += $Nickname
+ }
+
+ $proc = Start-Process -FilePath cmd.exe -ArgumentList $args -Wait -PassThru
+ PrintMsiExitCodeMessage $proc.ExitCode
+ }
+ catch {
+ Write-Error "Failed to install Visual Studio! $($_.Exception.Message)"
+ }
+}
+
+<#
+.SYNOPSIS
+Install a .msi file.
+
+.DESCRIPTION
+InstallMSI takes a url where an .msi lives, and installs that .msi to the system.
+
+.PARAMETER Name
+The name of the thing to install.
+
+.PARAMETER Url
+The URL at which the .msi lives.
+#>
+Function InstallMSI {
+ Param(
+ [String]$Name,
+ [String]$Url
+ )
+
+ try {
+ Write-Host "Downloading $Name..."
+ [string]$msiPath = Get-TempFilePath -Extension 'msi'
+ curl.exe -L -o $msiPath -s -S $Url
+ Write-Host "Installing $Name..."
+ $args = @('/i', $msiPath, '/norestart', '/quiet', '/qn')
+ $proc = Start-Process -FilePath 'msiexec.exe' -ArgumentList $args -Wait -PassThru
+ PrintMsiExitCodeMessage $proc.ExitCode
+ }
+ catch {
+ Write-Error "Failed to install $Name! $($_.Exception.Message)"
+ }
+}
+
+<#
+.SYNOPSIS
+Unpacks a zip file to $Dir.
+
+.DESCRIPTION
+InstallZip takes a URL of a zip file, and unpacks the zip file to the directory
+$Dir.
+
+.PARAMETER Name
+The name of the tool being installed.
+
+.PARAMETER Url
+The URL of the zip file to unpack.
+
+.PARAMETER Dir
+The directory to unpack the zip file to.
+#>
+Function InstallZip {
+ Param(
+ [String]$Name,
+ [String]$Url,
+ [String]$Dir
+ )
+
+ try {
+ Write-Host "Downloading $Name..."
+ [string]$zipPath = Get-TempFilePath -Extension 'zip'
+ curl.exe -L -o $zipPath -s -S $Url
+ Write-Host "Installing $Name..."
+ Expand-Archive -Path $zipPath -DestinationPath $Dir -Force
+ }
+ catch {
+ Write-Error "Failed to install $Name! $($_.Exception.Message)"
+ }
+}
+
+<#
+.SYNOPSIS
+Installs MPI
+
+.DESCRIPTION
+Downloads the MPI installer located at $Url, and installs it with the
+correct flags.
+
+.PARAMETER Url
+The URL of the installer.
+#>
+Function InstallMpi {
+ Param(
+ [String]$Url
+ )
+
+ try {
+ Write-Host 'Downloading MPI...'
+ [string]$installerPath = Get-TempFilePath -Extension 'exe'
+ curl.exe -L -o $installerPath -s -S $Url
+ Write-Host 'Installing MPI...'
+ $proc = Start-Process -FilePath $installerPath -ArgumentList @('-force', '-unattend') -Wait -PassThru
+ $exitCode = $proc.ExitCode
+ if ($exitCode -eq 0) {
+ Write-Host 'Installation successful!'
+ }
+ else {
+ Write-Error "Installation failed! Exited with $exitCode."
+ }
+ }
+ catch {
+ Write-Error "Failed to install MPI! $($_.Exception.Message)"
+ }
+}
+
+<#
+.SYNOPSIS
+Installs NVIDIA's CUDA Toolkit.
+
+.DESCRIPTION
+InstallCuda installs the CUDA Toolkit with the features specified as a
+space separated list of strings in $Features.
+
+.PARAMETER Url
+The URL of the CUDA installer.
+
+.PARAMETER Features
+A space-separated list of features to install.
+#>
+Function InstallCuda {
+ Param(
+ [String]$Url,
+ [String]$Features
+ )
+
+ try {
+ Write-Host 'Downloading CUDA...'
+ [string]$installerPath = Get-TempFilePath -Extension 'exe'
+ curl.exe -L -o $installerPath -s -S $Url
+ Write-Host 'Installing CUDA...'
+ $proc = Start-Process -FilePath $installerPath -ArgumentList @('-s ' + $Features) -Wait -PassThru
+ $exitCode = $proc.ExitCode
+ if ($exitCode -eq 0) {
+ Write-Host 'Installation successful!'
+ }
+ else {
+ Write-Error "Installation failed! Exited with $exitCode."
+ }
+ }
+ catch {
+ Write-Error "Failed to install CUDA! $($_.Exception.Message)"
+ }
+}
+
+<#
+.SYNOPSIS
+Partitions a new physical disk.
+
+.DESCRIPTION
+Takes the disk $DiskNumber, turns it on, then partitions it for use with label
+$Label and drive letter $Letter.
+
+.PARAMETER DiskNumber
+The number of the disk to set up.
+
+.PARAMETER Letter
+The drive letter at which to mount the disk.
+
+.PARAMETER Label
+The label to give the disk.
+#>
+Function New-PhysicalDisk {
+ Param(
+ [int]$DiskNumber,
+ [string]$Letter,
+ [string]$Label
+ )
+
+ if ($Letter.Length -ne 1) {
+ throw "Bad drive letter $Letter, expected only one letter. (Did you accidentially add a : ?)"
+ }
+
+ try {
+ Write-Host "Attempting to online physical disk $DiskNumber"
+ [string]$diskpartScriptPath = Get-TempFilePath -Extension 'txt'
+ [string]$diskpartScriptContent =
+ "SELECT DISK $DiskNumber`r`n" +
+ "ONLINE DISK`r`n"
+
+ Write-Host "Writing diskpart script to $diskpartScriptPath with content:"
+ Write-Host $diskpartScriptContent
+ Set-Content -Path $diskpartScriptPath -Value $diskpartScriptContent
+ Write-Host 'Invoking DISKPART...'
+ & diskpart.exe /s $diskpartScriptPath
+
+ Write-Host "Provisioning physical disk $DiskNumber as drive $Letter"
+ [string]$diskpartScriptContent =
+ "SELECT DISK $DiskNumber`r`n" +
+ "ATTRIBUTES DISK CLEAR READONLY`r`n" +
+ "CREATE PARTITION PRIMARY`r`n" +
+ "FORMAT FS=NTFS LABEL=`"$Label`" QUICK`r`n" +
+ "ASSIGN LETTER=$Letter`r`n"
+ Write-Host "Writing diskpart script to $diskpartScriptPath with content:"
+ Write-Host $diskpartScriptContent
+ Set-Content -Path $diskpartScriptPath -Value $diskpartScriptContent
+ Write-Host 'Invoking DISKPART...'
+ & diskpart.exe /s $diskpartScriptPath
+ }
+ catch {
+ Write-Error "Failed to provision physical disk $DiskNumber as drive $Letter! $($_.Exception.Message)"
+ }
+}
+
+Write-Host "AdminUser password not supplied; assuming already running as AdminUser"
+
+New-PhysicalDisk -DiskNumber 2 -Letter 'E' -Label 'install disk'
+
+Write-Host 'Disabling pagefile...'
+wmic computersystem set AutomaticManagedPagefile=False
+wmic pagefileset delete
+
+Write-Host 'Configuring AntiVirus exclusions...'
+Add-MPPreference -ExclusionPath C:\
+Add-MPPreference -ExclusionPath D:\
+Add-MPPreference -ExclusionPath E:\
+Add-MPPreference -ExclusionProcess ninja.exe
+Add-MPPreference -ExclusionProcess clang-cl.exe
+Add-MPPreference -ExclusionProcess cl.exe
+Add-MPPreference -ExclusionProcess link.exe
+Add-MPPreference -ExclusionProcess python.exe
+
+InstallVisualStudio -Workloads $Workloads -BootstrapperUrl $VisualStudioBootstrapperUrl -Nickname 'Stable'
+InstallMpi -Url $MpiUrl
+InstallCuda -Url $CudaUrl -Features $CudaFeatures
+InstallZip -Url $BinSkimUrl -Name 'BinSkim' -Dir 'C:\BinSkim'
+if (-Not ([string]::IsNullOrWhiteSpace($StorageAccountName))) {
+ Write-Host 'Storing storage account name to environment'
+ Set-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager\Environment' `
+ -Name StorageAccountName `
+ -Value $StorageAccountName
+}
+if (-Not ([string]::IsNullOrWhiteSpace($StorageAccountKey))) {
+ Write-Host 'Storing storage account key to environment'
+ Set-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager\Environment' `
+ -Name StorageAccountKey `
+ -Value $StorageAccountKey
+}
diff --git a/scripts/azure-pipelines/windows/sysprep.ps1 b/scripts/azure-pipelines/windows/sysprep.ps1 new file mode 100644 index 000000000..c0965350d --- /dev/null +++ b/scripts/azure-pipelines/windows/sysprep.ps1 @@ -0,0 +1,17 @@ +# Copyright (c) Microsoft Corporation.
+# SPDX-License-Identifier: MIT
+#
+
+<#
+.SYNOPSIS
+Prepares the virtual machine for imaging.
+
+.DESCRIPTION
+Runs the `sysprep` utility to prepare the system for imaging.
+See https://docs.microsoft.com/en-us/windows-hardware/manufacture/desktop/sysprep--system-preparation--overview
+for more information.
+#>
+
+$ErrorActionPreference = 'Stop'
+Write-Host 'Running sysprep'
+& C:\Windows\system32\sysprep\sysprep.exe /oobe /generalize /shutdown
diff --git a/scripts/ci.baseline.txt b/scripts/ci.baseline.txt index 386bf5829..7f796173a 100644 --- a/scripts/ci.baseline.txt +++ b/scripts/ci.baseline.txt @@ -45,10 +45,14 @@ 7zip:x64-osx=fail
7zip:x64-uwp=fail
abseil:arm-uwp=fail
-ace:arm64-windows=fail
+# ace is failing because the port's attempt to make yasm available is not succeeding
ace:arm-uwp=fail
+ace:arm64-windows=fail
ace:x64-osx=fail
ace:x64-uwp=fail
+ace:x64-windows-static=fail
+ace:x64-windows=fail
+ace:x86-windows=fail
activemq-cpp:x64-windows-static=fail
akali:x64-uwp=fail
akali:arm-uwp=fail
@@ -270,7 +274,7 @@ cppcms:x64-osx=fail cppcms:x64-windows-static=fail
cppfs:arm-uwp=fail
cppfs:x64-uwp=fail
-cppgraphqlgen:arm-uwp=fail
+cppgraphqlgen:arm-uwp=ignore
cppgraphqlgen:x64-uwp=ignore
cppkafka:x64-linux=ignore
cppmicroservices:arm64-windows=fail
@@ -305,8 +309,8 @@ cudnn:x64-windows-static=fail cudnn:x86-windows=fail
date:arm64-windows=fail
dbow2:x64-osx=fail
-dcmtk:arm64-windows=fail
dcmtk:arm-uwp=fail
+dcmtk:arm64-windows=fail
dcmtk:x64-uwp=fail
detours:x64-linux=fail
detours:x64-osx=fail
@@ -337,6 +341,7 @@ dlfcn-win32:x64-linux=fail dlfcn-win32:x64-osx=fail
dlfcn-win32:x64-uwp=fail
dmlc:arm-uwp=fail
+dmlc:arm64-windows=ignore
dmlc:x64-uwp=fail
dmlc:x64-windows-static=ignore
dmlc:x86-windows=ignore
@@ -435,11 +440,11 @@ fdlibm:arm-uwp=fail fdlibm:x64-uwp=fail
fftw3:arm-uwp=fail
fftw3:x64-uwp=fail
+# ffmpeg on arm64 is currently failing due to an internal compiler error
+ffmpeg:arm64-windows=fail
field3d:x64-windows=fail
field3d:x64-windows-static=fail
field3d:x86-windows=fail
-fizz:x64-windows=fail
-fizz:x64-windows-static=fail
flint:x64-linux=fail
flint:x64-osx=fail
fltk:arm-uwp=fail
@@ -457,11 +462,6 @@ fmilib:x64-uwp=fail fmilib:x64-windows=ignore
fmilib:x64-windows-static=ignore
fmilib:x86-windows=ignore
-# Folly fails due to a compiler bug in MSVC 19.22.27905, fixed in newer releases
-folly:arm64-windows=fail
-folly:x86-windows=fail
-folly:x64-windows=fail
-folly:x64-windows-static=fail
foonathan-memory:arm64-windows=fail
foonathan-memory:arm-uwp=fail
foonathan-memory:x64-uwp=fail
@@ -602,6 +602,7 @@ hwloc:x64-linux=fail hwloc:x64-osx=fail
hwloc:x64-uwp=fail
hyperscan:x64-linux=ignore
+# hypre has a conflict with 'superlu' port
hypre:x64-linux=fail
hypre:x64-osx=fail
icu:arm64-windows=fail
@@ -957,10 +958,15 @@ libuuid:x86-windows=fail libuv:arm64-windows=fail
libuv:arm-uwp=fail
libuv:x64-uwp=fail
-libvpx:arm64-windows=fail
+# libvpx is failing because the port's attempt to make yasm available is not succeeding
libvpx:arm-uwp=fail
+libvpx:arm64-windows=fail
libvpx:x64-linux=fail
libvpx:x64-osx=fail
+libvpx:x64-uwp=fail
+libvpx:x64-windows-static=fail
+libvpx:x64-windows=fail
+libvpx:x86-windows=fail
libwandio:x86-windows=fail
libwandio:x64-windows=fail
libwandio:x64-windows-static=fail
@@ -995,9 +1001,6 @@ llvm:arm64-windows=fail llvm:arm-uwp=fail
llvm:x64-uwp=fail
llvm:x64-linux=ignore
-# disable them temporarily and wait for fix
-llvm:x64-windows=fail
-llvm:x64-windows-static=fail
# installing iconv makes building llvm fail; needs to be fixed
llvm:x64-osx=ignore
lmdb:arm64-windows=fail
@@ -1122,8 +1125,12 @@ mozjpeg:x64-uwp = skip mozjpeg:x64-windows = skip
mozjpeg:x64-windows-static = skip
mozjpeg:x86-windows = skip
+# mpg123 is failing because the port's attempt to make yasm available is not succeeding
mpg123:arm-uwp=fail
mpg123:x64-uwp=fail
+mpg123:x64-windows-static=fail
+mpg123:x64-windows=fail
+mpg123:x86-windows=fail
mpir:arm64-windows=fail
mpir:arm-uwp=fail
mpir:x64-uwp=fail
@@ -1413,9 +1420,13 @@ polyhook2:x64-linux=fail polyhook2:x64-uwp=fail
polyhook2:x64-osx=fail
portable-snippets:arm-uwp=fail
-portaudio:arm64-windows=fail
+# Portaudio was broken by Ninja 1.9.0 https://github.com/ninja-build/ninja/pull/1406
portaudio:arm-uwp=fail
+portaudio:arm64-windows=fail
portaudio:x64-uwp=fail
+portaudio:x64-windows-static=fail
+portaudio:x64-windows=fail
+portaudio:x86-windows=fail
portmidi:arm64-windows=fail
portmidi:arm-uwp=fail
portmidi:x64-linux=fail
@@ -1435,6 +1446,9 @@ protobuf-c:x64-windows-static=fail protobuf-c:x64-uwp=fail
protobuf-c:arm64-windows=fail
protobuf-c:arm-uwp=fail
+# proxygen fails with "Target 'Windows' not supported by proxygen!"
+proxygen:x64-windows=fail
+proxygen:x64-windows-static=fail
ptex:arm-uwp=fail
ptex:x64-linux=fail
ptex:x64-osx=fail
@@ -1459,8 +1473,40 @@ qhull:x64-uwp=ignore qpid-proton:arm-uwp=fail
qpid-proton:x64-uwp=fail
qpid-proton:x64-windows-static=fail
+#qt5-activeqt is skipped because it conflicts with qt5-declarative:
+# Starting package 142/820: qt5-declarative:x86-windows
+# Building package qt5-declarative[core]:x86-windows...
+# Using cached binary package: C:\agent\_work\1\s\archives\94\9428e63fede20a51ac0631a9d94d8773e593dd06.zip
+# Building package qt5-declarative[core]:x86-windows... done
+# Installing package qt5-declarative[core]:x86-windows...
+# The following files are already installed in C:/agent/_work/1/s/installed/x86-windows and are in conflict with qt5-declarative:x86-windows
+#
+# Installed by qt5-activeqt:x86-windows
+# tools/qt5/bin/Qt5Gui.dll
+# tools/qt5/bin/Qt5Widgets.dll
+# tools/qt5/bin/bz2.dll
+# tools/qt5/bin/freetype.dll
+# tools/qt5/bin/glib-2.dll
+# tools/qt5/bin/harfbuzz.dll
+# tools/qt5/bin/jpeg62.dll
+# tools/qt5/bin/libcharset.dll
+# tools/qt5/bin/libiconv.dll
+# tools/qt5/bin/libintl.dll
+# tools/qt5/bin/libpng16.dll
+# tools/qt5/bin/pcre.dll
+# tools/qt5/bin/plugins/imageformats/qgif.dll
+# tools/qt5/bin/plugins/imageformats/qico.dll
+# tools/qt5/bin/plugins/imageformats/qjpeg.dll
+# tools/qt5/bin/plugins/platforms/qwindows.dll
+# tools/qt5/bin/plugins/styles/qwindowsvistastyle.dll
+qt5-activeqt:arm-uwp=skip
+qt5-activeqt:arm64-windows=skip
qt5-activeqt:x64-linux=fail
qt5-activeqt:x64-osx=fail
+qt5-activeqt:x64-uwp=skip
+qt5-activeqt:x64-windows-static=skip
+qt5-activeqt:x64-windows=skip
+qt5-activeqt:x86-windows=skip
qt5-macextras:x64-linux=fail
qt5-macextras:x64-windows=fail
qt5-macextras:x64-windows-static=fail
@@ -1524,7 +1570,6 @@ redis-plus-plus:x64-windows-static=fail redis-plus-plus:arm64-windows=fail
replxx:arm-uwp=fail
replxx:x64-uwp=fail
-replxx:arm64-windows=fail
reproc:arm-uwp=fail
reproc:x64-uwp=fail
restbed:arm-uwp=fail
@@ -1561,7 +1606,6 @@ sciter:x64-uwp=fail sciter:x64-windows-static=fail
scnlib:arm-uwp=fail
scnlib:x64-uwp=fail
-scnlib:x86-windows=fail
scylla-wrapper:arm64-windows=fail
scylla-wrapper:arm-uwp=fail
scylla-wrapper:x64-linux=fail
@@ -1584,6 +1628,8 @@ sdl2-mixer:x64-uwp=fail sdl2-net:arm-uwp=fail
sdl2-net:x64-uwp=fail
seal:arm-uwp=fail
+# https://github.com/microsoft/vcpkg/issues/10918
+seal:x64-windows-static=fail
seal:x64-uwp=fail
secp256k1:x64-linux=fail
secp256k1:x64-osx=fail
@@ -1676,9 +1722,30 @@ stormlib:arm-uwp=fail stormlib:x64-uwp=fail
stxxl:arm-uwp=fail
stxxl:x64-uwp=fail
-superlu:arm64-windows=fail
-superlu:arm-uwp=fail
-superlu:x64-uwp=fail
+# Sundials was broken by Ninja 1.9.0 https://github.com/ninja-build/ninja/pull/1406
+sundials:arm64-windows=fail
+sundials:x64-windows=fail
+sundials:x86-windows=fail
+# Conflicts between ports:
+#The following files are already installed in C:/agent/_work/1/s/installed/x64-windows-static
+# and are in conflict with superlu:x64-windows-static
+#
+#Installed by hypre:x64-windows-static
+# include/slu_Cnames.h
+# include/slu_cdefs.h
+# include/slu_dcomplex.h
+# include/slu_ddefs.h
+# include/slu_scomplex.h
+# include/slu_sdefs.h
+# include/slu_util.h
+# include/slu_zdefs.h
+# include/supermatrix.h
+superlu:arm-uwp=skip
+superlu:arm-windows=skip
+superlu:arm64-windows=skip
+superlu:x64-uwp=skip
+superlu:x64-windows-static=skip
+superlu:x64-windows=skip
systemc:arm64-windows=fail
systemc:arm-uwp=fail
systemc:x64-uwp=fail
@@ -1907,4 +1974,3 @@ zkpp:arm-uwp=fail c4core:arm-uwp=fail
c4core:arm64-windows=fail
c4core:x64-osx=fail
-
|
