From 2278ed1699fba490789eac069db3372b7145be98 Mon Sep 17 00:00:00 2001 From: Will Smith Date: Thu, 4 Nov 2021 13:31:02 -0700 Subject: [PATCH 1/4] Added CI job for deterministic builds --- azure-pipelines.yml | 10 + eng/pipelines/checkout-windows-task.yml | 11 + eng/pipelines/publish-logs.yml | 17 ++ eng/test-determinism.cmd | 2 + eng/test-determinism.ps1 | 369 ++++++++++++++++++++++++ 5 files changed, 409 insertions(+) create mode 100644 eng/pipelines/checkout-windows-task.yml create mode 100644 eng/pipelines/publish-logs.yml create mode 100644 eng/test-determinism.cmd create mode 100644 eng/test-determinism.ps1 diff --git a/azure-pipelines.yml b/azure-pipelines.yml index b6a89be2e94..60c9e1b097b 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -355,6 +355,16 @@ stages: - script: .\tests\EndToEndBuildTests\EndToEndBuildTests.cmd -c Release displayName: End to end build tests + # Determinism + - job: Determinism_Debug + pool: + vmImage: windows-latest + steps: + - checkout: self + clean: true + - script: .\eng\test-determinism.cmd -configuration Debug + displayName: Determinism tests with Debug configuration + # Up-to-date - disabled due to it being flaky #- job: UpToDate_Windows # pool: diff --git a/eng/pipelines/checkout-windows-task.yml b/eng/pipelines/checkout-windows-task.yml new file mode 100644 index 00000000000..6d3842a11b1 --- /dev/null +++ b/eng/pipelines/checkout-windows-task.yml @@ -0,0 +1,11 @@ +# Shallow checkout sources on Windows +steps: + - checkout: none + + - script: | + @echo on + git init + git remote add origin "$(Build.Repository.Uri)" + git fetch --progress --no-tags --depth=1 origin "$(Build.SourceVersion)" + git checkout "$(Build.SourceVersion)" + displayName: Shallow Checkout \ No newline at end of file diff --git a/eng/pipelines/publish-logs.yml b/eng/pipelines/publish-logs.yml new file mode 100644 index 00000000000..3f5c5e6d2c4 --- /dev/null +++ b/eng/pipelines/publish-logs.yml @@ -0,0 +1,17 @@ +# Build on windows desktop +parameters: +- name: jobName + type: string + default: '' +- name: configuration + type: string + default: 'Debug' + +steps: + - task: PublishPipelineArtifact@1 + displayName: Publish Logs + inputs: + targetPath: '$(Build.SourcesDirectory)/artifacts/log/${{ parameters.configuration }}' + artifactName: '${{ parameters.jobName }} Attempt $(System.JobAttempt) Logs' + continueOnError: true + condition: not(succeeded()) \ No newline at end of file diff --git a/eng/test-determinism.cmd b/eng/test-determinism.cmd new file mode 100644 index 00000000000..863e8bd5ca3 --- /dev/null +++ b/eng/test-determinism.cmd @@ -0,0 +1,2 @@ +@echo off +powershell -noprofile -executionPolicy RemoteSigned -file "%~dp0\test-determinism.ps1" %* \ No newline at end of file diff --git a/eng/test-determinism.ps1 b/eng/test-determinism.ps1 new file mode 100644 index 00000000000..2436ec39068 --- /dev/null +++ b/eng/test-determinism.ps1 @@ -0,0 +1,369 @@ +[CmdletBinding(PositionalBinding=$false)] +param([string]$configuration = "Debug", + [string]$msbuildEngine = "vs", + [string]$altRootDrive = "q:", + [switch]$help, + [switch]$norestore, + [switch]$rebuild) + +Set-StrictMode -version 2.0 +$ErrorActionPreference = "Stop" + +function Print-Usage() { + Write-Host "Usage: test-determinism.ps1" + Write-Host " -configuration Build configuration ('Debug' or 'Release')" + Write-Host " -msbuildEngine Msbuild engine to use to run build ('dotnet', 'vs', or unspecified)." + Write-Host " -bootstrapDir Directory containing the bootstrap compiler" + Write-Host " -altRootDrive The drive we build on (via subst) for verifying pathmap implementation" +} + +if ($help) { + Print-Usage + exit 0 +} + +# List of binary names that should be skipped because they have a known issue that +# makes them non-deterministic. +$script:skipList = @() +function Run-Build([string]$rootDir, [string]$logFileName) { + + # Clean out the previous run + Write-Host "Cleaning binaries" + $stopWatch = [System.Diagnostics.StopWatch]::StartNew() + Remove-Item -Recurse (Get-BinDir $rootDir) -ErrorAction SilentlyContinue + Remove-Item -Recurse (Get-ObjDir $rootDir) -ErrorAction SilentlyContinue + $stopWatch.Stop() + Write-Host "Cleaning took $($stopWatch.Elapsed)" + + $solution = Join-Path $rootDir "VisualFSharp.sln" + + if ($logFileName -eq "") { + $logFileName = [IO.Path]::GetFileNameWithoutExtension($projectFilePath) + } + $logFileName = [IO.Path]::ChangeExtension($logFileName, ".binlog") + $logFilePath = Join-Path $LogDir $logFileName + + Stop-Processes + + Write-Host "Building $solution using $bootstrapDir" + MSBuild $toolsetBuildProj ` + /p:Projects=$solution ` + /p:Restore=true ` + /p:Build=true ` + /p:DebugDeterminism=true ` + /p:Features="debug-determinism" ` + /p:DeployExtension=false ` + /p:RepoRoot=$rootDir ` + /p:TreatWarningsAsErrors=true ` + /p:BootstrapBuildPath=$bootstrapDir ` + /p:RunAnalyzers=false ` + /p:RunAnalyzersDuringBuild=false ` + /p:RestoreUseStaticGraphEvaluation=true ` + /bl:$logFilePath + + Stop-Processes +} + +function Get-ObjDir([string]$rootDir) { + return Join-Path $rootDir "artifacts\obj" +} + +function Get-BinDir([string]$rootDir) { + return Join-Path $rootDir "artifacts\bin" +} + +# Return all of the files that need to be processed for determinism under the given +# directory. +function Get-FilesToProcess([string]$rootDir) { + $objDir = Get-ObjDir $rootDir + foreach ($item in Get-ChildItem -re -in *.dll,*.exe,*.pdb,*.sourcelink.json $objDir) { + $filePath = $item.FullName + $fileName = Split-Path -leaf $filePath + $relativeDirectory = Split-Path -parent $filePath + $relativeDirectory = $relativeDirectory.Substring($objDir.Length) + $relativeDirectory = $relativeDirectory.TrimStart("\") + + if ($skipList.Contains($fileName)) { + continue; + } + + $fileId = $filePath.Substring($objDir.Length).Replace("\", ".").TrimStart(".") + $fileHash = (Get-FileHash $filePath -algorithm MD5).Hash + + $data = @{} + $data.Hash = $fileHash + $data.Content = [IO.File]::ReadAllBytes($filePath) + $data.FileId = $fileId + $data.FileName = $fileName + $data.FilePath = $filePath + $data.RelativeDirectory = $relativeDirectory + + $keyFilePath = $filePath + ".key" + $keyFileName = Split-Path -leaf $keyFilePath + if (Test-Path $keyFilePath) { + $data.KeyFileName = $keyFileName + $data.KeyFilePath = $keyFilePath + $data.KeyFileContent = [IO.File]::ReadAllBytes($keyFilePath) + } + else { + $data.KeyFileName = "" + $data.KeyFilePath = "" + $data.KeyFileContent = $null + } + + Write-Output $data + } +} + +# This will build up the map of all of the binaries and their respective hashes. +function Record-Binaries([string]$rootDir) { + $stopWatch = [System.Diagnostics.StopWatch]::StartNew() + Write-Host "Recording file hashes" + + $map = @{ } + foreach ($fileData in Get-FilesToProcess $rootDir) { + Write-Host "`t$($fileData.FileId) = $($fileData.Hash)" + $map[$fileData.FileId] = $fileData + } + $stopWatch.Stop() + Write-Host "Recording took $($stopWatch.Elapsed)" + return $map +} + +# This is a sanity check to ensure that we're actually putting the right entries into +# the core data map. Essentially to ensure things like if we change our directory layout +# that this test fails beacuse we didn't record the binaries we intended to record. +function Test-MapContents($dataMap) { + + # Sanity check to ensure we didn't return a false positive because we failed + # to examine any binaries. + if ($dataMap.Count -lt 40) { + throw "Didn't find the expected count of binaries" + } + + # Test for some well known binaries + $list = @( + "FSharp.Core.dll", + "FSharp.Compiler.Service.dll") + + foreach ($fileName in $list) { + $found = $false + foreach ($value in $dataMap.Values) { + if ($value.FileName -eq $fileName) { + $found = $true + break; + } + } + + if (-not $found) { + throw "Did not find the expected binary $fileName" + } + } +} + +function Test-Build([string]$rootDir, $dataMap, [string]$logFileName) { + Run-Build $rootDir -logFile $logFileName + + $errorList = @() + $allGood = $true + + Write-Host "Testing the binaries" + $stopWatch = [System.Diagnostics.StopWatch]::StartNew() + foreach ($fileData in Get-FilesToProcess $rootDir) { + $fileId = $fileData.FileId + $fileName = $fileData.FileName + $filePath = $fileData.FilePath + $relativeDir = $fileData.RelativeDirectory + + if (-not $dataMap.Contains($fileId)) { + Write-Host "ERROR! Missing entry in map $fileId->$filePath" + $allGood = $false + continue + } + + $oldfileData = $datamap[$fileId] + if ($fileData.Hash -ne $oldFileData.Hash) { + Write-Host "`tERROR! $relativeDir\$fileName contents don't match" + $allGood = $false + $errorList += $fileName + + $errorCurrentDirLeft = Join-Path $errorDirLeft $relativeDir + Create-Directory $errorCurrentDirLeft + $errorCurrentDirRight = Join-Path $errorDirRight $relativeDir + Create-Directory $errorCurrentDirRight + + # Save out the original and baseline for investigation + [IO.File]::WriteAllBytes((Join-Path $errorCurrentDirLeft $fileName), $oldFileData.Content) + Copy-Item $filePath (Join-Path $errorCurrentDirRight $fileName) + + # Copy the key files if available too + $keyFileName = $oldFileData.KeyFileName + if ($keyFileName -ne "") { + [IO.File]::WriteAllBytes((Join-Path $errorCurrentDirLeft $keyFileName), $oldFileData.KeyFileContent) + Copy-Item $fileData.KeyFilePath (Join-Path $errorCurrentDirRight $keyFileName) + } + + continue + } + + Write-Host "`tVerified $relativeDir\$fileName" + } + + if (-not $allGood) { + Write-Host "Determinism failed for the following binaries:" + foreach ($name in $errorList) { + Write-Host "`t$name" + } + + Write-Host "Archiving failure information" + $zipFile = Join-Path $LogDir "determinism.zip" + Add-Type -Assembly "System.IO.Compression.FileSystem"; + [System.IO.Compression.ZipFile]::CreateFromDirectory($script:errorDir, $zipFile, "Fastest", $true); + + Write-Host "Please send $zipFile to compiler team for analysis" + exit 1 + } + + $stopWatch.Stop() + Write-Host "Testing took $($stopWatch.Elapsed)" +} + +function Run-Test() { + # Run the initial build so that we can populate the maps + Run-Build $RepoRoot -logFileName "Initial" -useBootstrap + $dataMap = Record-Binaries $RepoRoot + Test-MapContents $dataMap + + # Run a test against the source in the same directory location + Test-Build -rootDir $RepoRoot -dataMap $dataMap -logFileName "test1" + + # Run another build in a different source location and verify that path mapping + # allows the build to be identical. To do this we'll copy the entire source + # tree under the artifacts\q directory and run a build from there. + # Write-Host "Building in a different directory" + # Exec-Command "subst" "$altRootDrive $(Split-Path -parent $RepoRoot)" + # try { + # $altRootDir = Join-Path "$($altRootDrive)\" (Split-Path -leaf $RepoRoot) + # Test-Build -rootDir $altRootDir -dataMap $dataMap -logFileName "test2" + # } + # finally { + # Exec-Command "subst" "$altRootDrive /d" + # } +} + +function Test-IsAdmin { + ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator") +} + +function TryDownloadDotnetFrameworkSdk() { + # If we are not running as admin user, don't bother grabbing ndp sdk -- since we don't need sn.exe + $isAdmin = Test-IsAdmin + Write-Host "TryDownloadDotnetFrameworkSdk -- Test-IsAdmin = '$isAdmin'" + if ($isAdmin -eq $true) + { + # Get program files(x86) location + if (${env:ProgramFiles(x86)} -eq $null) { + $programFiles = $env:ProgramFiles + } + else { + $programFiles = ${env:ProgramFiles(x86)} + } + + # Get windowsSDK location for x86 + $windowsSDK_ExecutablePath_x86 = $env:WindowsSDK_ExecutablePath_x86 + $newWindowsSDK_ExecutablePath_x86 = Join-Path "$programFiles" "Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.8 Tools" + + if ($windowsSDK_ExecutablePath_x86 -eq $null) { + $snPathX86 = Join-Path $newWindowsSDK_ExecutablePath_x86 "sn.exe" + } + else { + $snPathX86 = Join-Path $windowsSDK_ExecutablePath_x86 "sn.exe" + $snPathX86Exists = Test-Path $snPathX86 -PathType Leaf + if ($snPathX86Exists -ne $true) { + $windowsSDK_ExecutablePath_x86 = null + $snPathX86 = Join-Path $newWindowsSDK_ExecutablePath_x86 "sn.exe" + } + } + + $windowsSDK_ExecutablePath_x64 = $env:WindowsSDK_ExecutablePath_x64 + $newWindowsSDK_ExecutablePath_x64 = Join-Path "$programFiles" "Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.8 Tools\x64" + + if ($windowsSDK_ExecutablePath_x64 -eq $null) { + $snPathX64 = Join-Path $newWindowsSDK_ExecutablePath_x64 "sn.exe" + } + else { + $snPathX64 = Join-Path $windowsSDK_ExecutablePath_x64 "sn.exe" + $snPathX64Exists = Test-Path $snPathX64 -PathType Leaf + if ($snPathX64Exists -ne $true) { + $windowsSDK_ExecutablePath_x86 = null + $snPathX64 = Join-Path $newWindowsSDK_ExecutablePath_x64 "sn.exe" + } + } + + $snPathX86Exists = Test-Path $snPathX86 -PathType Leaf + Write-Host "pre-dl snPathX86Exists : $snPathX86Exists - '$snPathX86'" + if ($snPathX86Exists -ne $true) { + DownloadDotnetFrameworkSdk + } + + $snPathX86Exists = Test-Path $snPathX86 -PathType Leaf + if ($snPathX86Exists -eq $true) { + if ($windowsSDK_ExecutablePath_x86 -ne $newWindowsSDK_ExecutablePath_x86) { + $windowsSDK_ExecutablePath_x86 = $newWindowsSDK_ExecutablePath_x86 + # x86 environment variable + Write-Host "set WindowsSDK_ExecutablePath_x86=$WindowsSDK_ExecutablePath_x86" + [System.Environment]::SetEnvironmentVariable("WindowsSDK_ExecutablePath_x86","$newWindowsSDK_ExecutablePath_x86",[System.EnvironmentVariableTarget]::Machine) + $env:WindowsSDK_ExecutablePath_x86 = $newWindowsSDK_ExecutablePath_x86 + } + } + + # Also update environment variable for x64 + $snPathX64Exists = Test-Path $snPathX64 -PathType Leaf + if ($snPathX64Exists -eq $true) { + if ($windowsSDK_ExecutablePath_x64 -ne $newWindowsSDK_ExecutablePath_x64) { + $windowsSDK_ExecutablePath_x64 = $newWindowsSDK_ExecutablePath_x64 + # x64 environment variable + Write-Host "set WindowsSDK_ExecutablePath_x64=$WindowsSDK_ExecutablePath_x64" + [System.Environment]::SetEnvironmentVariable("WindowsSDK_ExecutablePath_x64","$newWindowsSDK_ExecutablePath_x64",[System.EnvironmentVariableTarget]::Machine) + $env:WindowsSDK_ExecutablePath_x64 = $newWindowsSDK_ExecutablePath_x64 + } + } + } +} + +try { + . (Join-Path $PSScriptRoot "build-utils.ps1") + + # Create all of the logging directories + $errorDir = Join-Path $LogDir "DeterminismFailures" + $errorDirLeft = Join-Path $errorDir "Left" + $errorDirRight = Join-Path $errorDir "Right" + + Create-Directory $LogDir + Create-Directory $errorDirLeft + Create-Directory $errorDirRight + + $ci = $true + $runAnalyzers = $false + $binaryLog = $true + $officialBuildId = "" + $nodeReuse = $false + $properties = @() + $script:bootstrap = $true + $script:bootstrapConfiguration = "Proto" + + $buildTool = InitializeBuildTool + $toolsetBuildProj = InitializeToolset + TryDownloadDotnetFrameworkSdk + + $bootstrapDir = Make-BootstrapBuild + + Run-Test + exit 0 +} +catch { + Write-Host $_ + Write-Host $_.Exception + Write-Host $_.ScriptStackTrace + exit 1 +} From 6100d06271bb29af9152bc976ca97db29ce84b08 Mon Sep 17 00:00:00 2001 From: Vlad Zarytovskii Date: Tue, 23 Nov 2021 14:02:25 +0100 Subject: [PATCH 2/4] Attempt to fix deterministic checks --- azure-pipelines.yml | 21 +- eng/pipelines/checkout-windows-task.yml | 11 -- eng/pipelines/publish-logs.yml | 17 -- eng/test-determinism.ps1 | 248 +++++++++++++----------- 4 files changed, 152 insertions(+), 145 deletions(-) delete mode 100644 eng/pipelines/checkout-windows-task.yml delete mode 100644 eng/pipelines/publish-logs.yml diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 9fe08ff4500..118e6891389 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -356,12 +356,27 @@ stages: # Determinism - job: Determinism_Debug pool: - vmImage: windows-latest + name: NetCore1ESPool-Public + demands: ImageOverride -equals Build.Windows.Amd64.VS2022.Pre.Open + timeoutInMinutes: 90 steps: - - checkout: self - clean: true + - checkout: none + - script: | + @echo on + git init + git remote add origin "$(Build.Repository.Uri)" + git fetch --progress --no-tags --depth=1 origin "$(Build.SourceVersion)" + git checkout "$(Build.SourceVersion)" + displayName: Shallow checkout - script: .\eng\test-determinism.cmd -configuration Debug displayName: Determinism tests with Debug configuration + - task: PublishPipelineArtifact@1 + displayName: Publish Determinism Logs + inputs: + targetPath: '$(Build.SourcesDirectory)/artifacts/log/Debug' + artifactName: 'Determinism_Debug Attempt $(System.JobAttempt) Logs' + continueOnError: true + condition: not(succeeded()) # Up-to-date - disabled due to it being flaky #- job: UpToDate_Windows diff --git a/eng/pipelines/checkout-windows-task.yml b/eng/pipelines/checkout-windows-task.yml deleted file mode 100644 index 6d3842a11b1..00000000000 --- a/eng/pipelines/checkout-windows-task.yml +++ /dev/null @@ -1,11 +0,0 @@ -# Shallow checkout sources on Windows -steps: - - checkout: none - - - script: | - @echo on - git init - git remote add origin "$(Build.Repository.Uri)" - git fetch --progress --no-tags --depth=1 origin "$(Build.SourceVersion)" - git checkout "$(Build.SourceVersion)" - displayName: Shallow Checkout \ No newline at end of file diff --git a/eng/pipelines/publish-logs.yml b/eng/pipelines/publish-logs.yml deleted file mode 100644 index 3f5c5e6d2c4..00000000000 --- a/eng/pipelines/publish-logs.yml +++ /dev/null @@ -1,17 +0,0 @@ -# Build on windows desktop -parameters: -- name: jobName - type: string - default: '' -- name: configuration - type: string - default: 'Debug' - -steps: - - task: PublishPipelineArtifact@1 - displayName: Publish Logs - inputs: - targetPath: '$(Build.SourcesDirectory)/artifacts/log/${{ parameters.configuration }}' - artifactName: '${{ parameters.jobName }} Attempt $(System.JobAttempt) Logs' - continueOnError: true - condition: not(succeeded()) \ No newline at end of file diff --git a/eng/test-determinism.ps1 b/eng/test-determinism.ps1 index 2436ec39068..508e9109c5d 100644 --- a/eng/test-determinism.ps1 +++ b/eng/test-determinism.ps1 @@ -1,10 +1,10 @@ -[CmdletBinding(PositionalBinding=$false)] +[CmdletBinding(PositionalBinding = $false)] param([string]$configuration = "Debug", - [string]$msbuildEngine = "vs", - [string]$altRootDrive = "q:", - [switch]$help, - [switch]$norestore, - [switch]$rebuild) + [string]$msbuildEngine = "vs", + [string]$altRootDrive = "q:", + [switch]$help, + [switch]$norestore, + [switch]$rebuild) Set-StrictMode -version 2.0 $ErrorActionPreference = "Stop" @@ -23,19 +23,23 @@ if ($help) { } # List of binary names that should be skipped because they have a known issue that -# makes them non-deterministic. +# makes them non-deterministic. $script:skipList = @() function Run-Build([string]$rootDir, [string]$logFileName) { # Clean out the previous run - Write-Host "Cleaning binaries" + Write-Host "Cleaning binaries in $rootDir" + $binDir = Get-BinDir $rootDir + $objDir = Get-ObjDir $rootDir $stopWatch = [System.Diagnostics.StopWatch]::StartNew() - Remove-Item -Recurse (Get-BinDir $rootDir) -ErrorAction SilentlyContinue - Remove-Item -Recurse (Get-ObjDir $rootDir) -ErrorAction SilentlyContinue + Write-Host "Cleaning binaries in $binDir" + Remove-Item -Recurse $binDir -ErrorAction SilentlyContinue + Write-Host "Cleaning binaries in $objDir" + Remove-Item -Recurse $objDir -ErrorAction SilentlyContinue $stopWatch.Stop() Write-Host "Cleaning took $($stopWatch.Elapsed)" - $solution = Join-Path $rootDir "VisualFSharp.sln" + $solution = Join-Path $rootDir "FSharp.sln" if ($logFileName -eq "") { $logFileName = [IO.Path]::GetFileNameWithoutExtension($projectFilePath) @@ -47,28 +51,35 @@ function Run-Build([string]$rootDir, [string]$logFileName) { Write-Host "Building $solution using $bootstrapDir" MSBuild $toolsetBuildProj ` - /p:Projects=$solution ` - /p:Restore=true ` - /p:Build=true ` - /p:DebugDeterminism=true ` - /p:Features="debug-determinism" ` - /p:DeployExtension=false ` - /p:RepoRoot=$rootDir ` - /p:TreatWarningsAsErrors=true ` - /p:BootstrapBuildPath=$bootstrapDir ` - /p:RunAnalyzers=false ` - /p:RunAnalyzersDuringBuild=false ` - /p:RestoreUseStaticGraphEvaluation=true ` - /bl:$logFilePath + /p:Configuration=$configuration ` + /p:Projects=$solution ` + /p:RepoRoot=$rootDir ` + /p:Restore=true ` + /p:Build=true ` + /p:Rebuild=true ` + /p:Pack=false ` + /p:Sign=false ` + /p:Publish=false ` + /p:ContinuousIntegrationBuild=false ` + /p:OfficialBuildId="" ` + /p:QuietRestore=false ` + /p:DotNetBuildFromSource=false ` + /p:DebugDeterminism=true ` + /p:Features="debug-determinism" ` + /p:DeployExtension=false ` + /p:BootstrapBuildPath=$bootstrapDir ` + /p:RunAnalyzers=false ` + /p:RunAnalyzersDuringBuild=false ` + /bl:$logFilePath Stop-Processes } -function Get-ObjDir([string]$rootDir) { +function Get-ObjDir([string]$rootDir) { return Join-Path $rootDir "artifacts\obj" } -function Get-BinDir([string]$rootDir) { +function Get-BinDir([string]$rootDir) { return Join-Path $rootDir "artifacts\bin" } @@ -76,8 +87,8 @@ function Get-BinDir([string]$rootDir) { # directory. function Get-FilesToProcess([string]$rootDir) { $objDir = Get-ObjDir $rootDir - foreach ($item in Get-ChildItem -re -in *.dll,*.exe,*.pdb,*.sourcelink.json $objDir) { - $filePath = $item.FullName + foreach ($item in Get-ChildItem -re -in *.dll, *.exe, *.pdb, *.sourcelink.json $objDir) { + $filePath = $item.FullName $fileName = Split-Path -leaf $filePath $relativeDirectory = Split-Path -parent $filePath $relativeDirectory = $relativeDirectory.Substring($objDir.Length) @@ -100,7 +111,7 @@ function Get-FilesToProcess([string]$rootDir) { $keyFilePath = $filePath + ".key" $keyFileName = Split-Path -leaf $keyFilePath - if (Test-Path $keyFilePath) { + if (Test-Path $keyFilePath) { $data.KeyFileName = $keyFileName $data.KeyFilePath = $keyFilePath $data.KeyFileContent = [IO.File]::ReadAllBytes($keyFilePath) @@ -121,7 +132,7 @@ function Record-Binaries([string]$rootDir) { Write-Host "Recording file hashes" $map = @{ } - foreach ($fileData in Get-FilesToProcess $rootDir) { + foreach ($fileData in Get-FilesToProcess $rootDir) { Write-Host "`t$($fileData.FileId) = $($fileData.Hash)" $map[$fileData.FileId] = $fileData } @@ -131,9 +142,9 @@ function Record-Binaries([string]$rootDir) { } # This is a sanity check to ensure that we're actually putting the right entries into -# the core data map. Essentially to ensure things like if we change our directory layout -# that this test fails beacuse we didn't record the binaries we intended to record. -function Test-MapContents($dataMap) { +# the core data map. Essentially to ensure things like if we change our directory layout +# that this test fails beacuse we didn't record the binaries we intended to record. +function Test-MapContents($dataMap) { # Sanity check to ensure we didn't return a false positive because we failed # to examine any binaries. @@ -146,16 +157,16 @@ function Test-MapContents($dataMap) { "FSharp.Core.dll", "FSharp.Compiler.Service.dll") - foreach ($fileName in $list) { + foreach ($fileName in $list) { $found = $false - foreach ($value in $dataMap.Values) { - if ($value.FileName -eq $fileName) { + foreach ($value in $dataMap.Values) { + if ($value.FileName -eq $fileName) { $found = $true break; } } - if (-not $found) { + if (-not $found) { throw "Did not find the expected binary $fileName" } } @@ -182,7 +193,7 @@ function Test-Build([string]$rootDir, $dataMap, [string]$logFileName) { } $oldfileData = $datamap[$fileId] - if ($fileData.Hash -ne $oldFileData.Hash) { + if ($fileData.Hash -ne $oldFileData.Hash) { Write-Host "`tERROR! $relativeDir\$fileName contents don't match" $allGood = $false $errorList += $fileName @@ -237,8 +248,8 @@ function Run-Test() { # Run a test against the source in the same directory location Test-Build -rootDir $RepoRoot -dataMap $dataMap -logFileName "test1" - # Run another build in a different source location and verify that path mapping - # allows the build to be identical. To do this we'll copy the entire source + # Run another build in a different source location and verify that path mapping + # allows the build to be identical. To do this we'll copy the entire source # tree under the artifacts\q directory and run a build from there. # Write-Host "Building in a different directory" # Exec-Command "subst" "$altRootDrive $(Split-Path -parent $RepoRoot)" @@ -256,79 +267,78 @@ function Test-IsAdmin { } function TryDownloadDotnetFrameworkSdk() { - # If we are not running as admin user, don't bother grabbing ndp sdk -- since we don't need sn.exe - $isAdmin = Test-IsAdmin - Write-Host "TryDownloadDotnetFrameworkSdk -- Test-IsAdmin = '$isAdmin'" - if ($isAdmin -eq $true) - { - # Get program files(x86) location - if (${env:ProgramFiles(x86)} -eq $null) { - $programFiles = $env:ProgramFiles - } - else { - $programFiles = ${env:ProgramFiles(x86)} - } - - # Get windowsSDK location for x86 - $windowsSDK_ExecutablePath_x86 = $env:WindowsSDK_ExecutablePath_x86 - $newWindowsSDK_ExecutablePath_x86 = Join-Path "$programFiles" "Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.8 Tools" - - if ($windowsSDK_ExecutablePath_x86 -eq $null) { - $snPathX86 = Join-Path $newWindowsSDK_ExecutablePath_x86 "sn.exe" - } - else { - $snPathX86 = Join-Path $windowsSDK_ExecutablePath_x86 "sn.exe" - $snPathX86Exists = Test-Path $snPathX86 -PathType Leaf - if ($snPathX86Exists -ne $true) { - $windowsSDK_ExecutablePath_x86 = null - $snPathX86 = Join-Path $newWindowsSDK_ExecutablePath_x86 "sn.exe" - } - } - - $windowsSDK_ExecutablePath_x64 = $env:WindowsSDK_ExecutablePath_x64 - $newWindowsSDK_ExecutablePath_x64 = Join-Path "$programFiles" "Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.8 Tools\x64" - - if ($windowsSDK_ExecutablePath_x64 -eq $null) { - $snPathX64 = Join-Path $newWindowsSDK_ExecutablePath_x64 "sn.exe" - } - else { - $snPathX64 = Join-Path $windowsSDK_ExecutablePath_x64 "sn.exe" - $snPathX64Exists = Test-Path $snPathX64 -PathType Leaf - if ($snPathX64Exists -ne $true) { - $windowsSDK_ExecutablePath_x86 = null - $snPathX64 = Join-Path $newWindowsSDK_ExecutablePath_x64 "sn.exe" - } - } - - $snPathX86Exists = Test-Path $snPathX86 -PathType Leaf - Write-Host "pre-dl snPathX86Exists : $snPathX86Exists - '$snPathX86'" - if ($snPathX86Exists -ne $true) { - DownloadDotnetFrameworkSdk - } - - $snPathX86Exists = Test-Path $snPathX86 -PathType Leaf - if ($snPathX86Exists -eq $true) { - if ($windowsSDK_ExecutablePath_x86 -ne $newWindowsSDK_ExecutablePath_x86) { - $windowsSDK_ExecutablePath_x86 = $newWindowsSDK_ExecutablePath_x86 - # x86 environment variable - Write-Host "set WindowsSDK_ExecutablePath_x86=$WindowsSDK_ExecutablePath_x86" - [System.Environment]::SetEnvironmentVariable("WindowsSDK_ExecutablePath_x86","$newWindowsSDK_ExecutablePath_x86",[System.EnvironmentVariableTarget]::Machine) - $env:WindowsSDK_ExecutablePath_x86 = $newWindowsSDK_ExecutablePath_x86 - } - } - - # Also update environment variable for x64 - $snPathX64Exists = Test-Path $snPathX64 -PathType Leaf - if ($snPathX64Exists -eq $true) { - if ($windowsSDK_ExecutablePath_x64 -ne $newWindowsSDK_ExecutablePath_x64) { - $windowsSDK_ExecutablePath_x64 = $newWindowsSDK_ExecutablePath_x64 - # x64 environment variable - Write-Host "set WindowsSDK_ExecutablePath_x64=$WindowsSDK_ExecutablePath_x64" - [System.Environment]::SetEnvironmentVariable("WindowsSDK_ExecutablePath_x64","$newWindowsSDK_ExecutablePath_x64",[System.EnvironmentVariableTarget]::Machine) - $env:WindowsSDK_ExecutablePath_x64 = $newWindowsSDK_ExecutablePath_x64 - } - } + # If we are not running as admin user, don't bother grabbing ndp sdk -- since we don't need sn.exe + $isAdmin = Test-IsAdmin + Write-Host "TryDownloadDotnetFrameworkSdk -- Test-IsAdmin = '$isAdmin'" + if ($isAdmin -eq $true) { + # Get program files(x86) location + if ($null -eq ${env:ProgramFiles(x86)}) { + $programFiles = $env:ProgramFiles } + else { + $programFiles = ${env:ProgramFiles(x86)} + } + + # Get windowsSDK location for x86 + $windowsSDK_ExecutablePath_x86 = $env:WindowsSDK_ExecutablePath_x86 + $newWindowsSDK_ExecutablePath_x86 = Join-Path "$programFiles" "Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.8 Tools" + + if ($null -eq $windowsSDK_ExecutablePath_x86) { + $snPathX86 = Join-Path $newWindowsSDK_ExecutablePath_x86 "sn.exe" + } + else { + $snPathX86 = Join-Path $windowsSDK_ExecutablePath_x86 "sn.exe" + $snPathX86Exists = Test-Path $snPathX86 -PathType Leaf + if ($snPathX86Exists -ne $true) { + $windowsSDK_ExecutablePath_x86 = null + $snPathX86 = Join-Path $newWindowsSDK_ExecutablePath_x86 "sn.exe" + } + } + + $windowsSDK_ExecutablePath_x64 = $env:WindowsSDK_ExecutablePath_x64 + $newWindowsSDK_ExecutablePath_x64 = Join-Path "$programFiles" "Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.8 Tools\x64" + + if ($null -eq $windowsSDK_ExecutablePath_x64) { + $snPathX64 = Join-Path $newWindowsSDK_ExecutablePath_x64 "sn.exe" + } + else { + $snPathX64 = Join-Path $windowsSDK_ExecutablePath_x64 "sn.exe" + $snPathX64Exists = Test-Path $snPathX64 -PathType Leaf + if ($snPathX64Exists -ne $true) { + $windowsSDK_ExecutablePath_x86 = null + $snPathX64 = Join-Path $newWindowsSDK_ExecutablePath_x64 "sn.exe" + } + } + + $snPathX86Exists = Test-Path $snPathX86 -PathType Leaf + Write-Host "pre-dl snPathX86Exists : $snPathX86Exists - '$snPathX86'" + if ($snPathX86Exists -ne $true) { + DownloadDotnetFrameworkSdk + } + + $snPathX86Exists = Test-Path $snPathX86 -PathType Leaf + if ($snPathX86Exists -eq $true) { + if ($windowsSDK_ExecutablePath_x86 -ne $newWindowsSDK_ExecutablePath_x86) { + $windowsSDK_ExecutablePath_x86 = $newWindowsSDK_ExecutablePath_x86 + # x86 environment variable + Write-Host "set WindowsSDK_ExecutablePath_x86=$WindowsSDK_ExecutablePath_x86" + [System.Environment]::SetEnvironmentVariable("WindowsSDK_ExecutablePath_x86", "$newWindowsSDK_ExecutablePath_x86", [System.EnvironmentVariableTarget]::Machine) + $env:WindowsSDK_ExecutablePath_x86 = $newWindowsSDK_ExecutablePath_x86 + } + } + + # Also update environment variable for x64 + $snPathX64Exists = Test-Path $snPathX64 -PathType Leaf + if ($snPathX64Exists -eq $true) { + if ($windowsSDK_ExecutablePath_x64 -ne $newWindowsSDK_ExecutablePath_x64) { + $windowsSDK_ExecutablePath_x64 = $newWindowsSDK_ExecutablePath_x64 + # x64 environment variable + Write-Host "set WindowsSDK_ExecutablePath_x64=$WindowsSDK_ExecutablePath_x64" + [System.Environment]::SetEnvironmentVariable("WindowsSDK_ExecutablePath_x64", "$newWindowsSDK_ExecutablePath_x64", [System.EnvironmentVariableTarget]::Machine) + $env:WindowsSDK_ExecutablePath_x64 = $newWindowsSDK_ExecutablePath_x64 + } + } + } } try { @@ -349,13 +359,23 @@ try { $officialBuildId = "" $nodeReuse = $false $properties = @() - $script:bootstrap = $true - $script:bootstrapConfiguration = "Proto" $buildTool = InitializeBuildTool $toolsetBuildProj = InitializeToolset TryDownloadDotnetFrameworkSdk + $dotnetPath = InitializeDotNetCli + $env:DOTNET_ROOT = "$dotnetPath" + Get-Item -Path Env: + + $script:bootstrap = $true + $script:bootstrapConfiguration = "Proto" + $script:bootstrapTfm = "net472" + + if ($script:msbuildEngine -eq "dotnet") { + $script.bootstrapTfm = "net5.0" + } + $bootstrapDir = Make-BootstrapBuild Run-Test From 2f85a84fb893c277c81982618ab8925b6a065550 Mon Sep 17 00:00:00 2001 From: Vlad Zarytovskii Date: Tue, 23 Nov 2021 15:36:39 +0100 Subject: [PATCH 3/4] Turn off rebuild --- eng/test-determinism.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eng/test-determinism.ps1 b/eng/test-determinism.ps1 index 508e9109c5d..164c7bb13fe 100644 --- a/eng/test-determinism.ps1 +++ b/eng/test-determinism.ps1 @@ -56,7 +56,7 @@ function Run-Build([string]$rootDir, [string]$logFileName) { /p:RepoRoot=$rootDir ` /p:Restore=true ` /p:Build=true ` - /p:Rebuild=true ` + /p:Rebuild=false ` /p:Pack=false ` /p:Sign=false ` /p:Publish=false ` From 57936535d52952cb9f1ad78a211720785b8fd07f Mon Sep 17 00:00:00 2001 From: Vlad Zarytovskii Date: Tue, 23 Nov 2021 15:45:26 +0100 Subject: [PATCH 4/4] Update test-determinism.ps1 --- eng/test-determinism.ps1 | 1 + 1 file changed, 1 insertion(+) diff --git a/eng/test-determinism.ps1 b/eng/test-determinism.ps1 index 164c7bb13fe..45b0cc396fb 100644 --- a/eng/test-determinism.ps1 +++ b/eng/test-determinism.ps1 @@ -64,6 +64,7 @@ function Run-Build([string]$rootDir, [string]$logFileName) { /p:OfficialBuildId="" ` /p:QuietRestore=false ` /p:DotNetBuildFromSource=false ` + /p:Deterministic=true ` /p:DebugDeterminism=true ` /p:Features="debug-determinism" ` /p:DeployExtension=false `