diff --git a/Directory.Packages.props b/Directory.Packages.props
index ea7fe3fa0ddf..ab8b018890fd 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -75,7 +75,6 @@
-
diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml
index b7a4f9ea2415..5b5244bfa658 100644
--- a/eng/Version.Details.xml
+++ b/eng/Version.Details.xml
@@ -15,187 +15,187 @@
878e6de43abbb6e988ce2134bce1100c8c911674
-
+
https://github.com/dotnet/runtime
- 86a1911d7cbff65fc82b373e7fece572c9f0fd52
+ b0118bebe385cb68f272a32ca6a5c5bb53c9b14f
-
+
https://github.com/dotnet/runtime
- 86a1911d7cbff65fc82b373e7fece572c9f0fd52
+ b0118bebe385cb68f272a32ca6a5c5bb53c9b14f
-
+
https://github.com/dotnet/runtime
- 86a1911d7cbff65fc82b373e7fece572c9f0fd52
+ b0118bebe385cb68f272a32ca6a5c5bb53c9b14f
-
+
https://github.com/dotnet/runtime
- 86a1911d7cbff65fc82b373e7fece572c9f0fd52
+ b0118bebe385cb68f272a32ca6a5c5bb53c9b14f
-
+
https://github.com/dotnet/runtime
- 86a1911d7cbff65fc82b373e7fece572c9f0fd52
+ b0118bebe385cb68f272a32ca6a5c5bb53c9b14f
-
+
https://github.com/dotnet/runtime
- 86a1911d7cbff65fc82b373e7fece572c9f0fd52
+ b0118bebe385cb68f272a32ca6a5c5bb53c9b14f
-
+
https://github.com/dotnet/runtime
- 86a1911d7cbff65fc82b373e7fece572c9f0fd52
+ b0118bebe385cb68f272a32ca6a5c5bb53c9b14f
-
+
https://github.com/dotnet/runtime
- 86a1911d7cbff65fc82b373e7fece572c9f0fd52
+ b0118bebe385cb68f272a32ca6a5c5bb53c9b14f
-
+
https://github.com/dotnet/runtime
- 86a1911d7cbff65fc82b373e7fece572c9f0fd52
+ b0118bebe385cb68f272a32ca6a5c5bb53c9b14f
-
+
https://github.com/dotnet/emsdk
- c42ff642a91a8aa4345de1728c3fa585ec13e1e6
+ 19c9523f5c2dd091b49959700723af795d6ad2b4
-
+
https://github.com/dotnet/emsdk
- c42ff642a91a8aa4345de1728c3fa585ec13e1e6
+ 19c9523f5c2dd091b49959700723af795d6ad2b4
-
+
https://github.com/dotnet/msbuild
- 367149434a51b18724b7a92b419644fabf06604f
+ 195e9289b39c4e5cd5d25e9422fcc4a93f95cefa
-
+
https://github.com/dotnet/msbuild
- 367149434a51b18724b7a92b419644fabf06604f
+ 195e9289b39c4e5cd5d25e9422fcc4a93f95cefa
-
+
https://github.com/dotnet/msbuild
- 367149434a51b18724b7a92b419644fabf06604f
+ 195e9289b39c4e5cd5d25e9422fcc4a93f95cefa
-
+
https://github.com/dotnet/fsharp
- d11b46f7cdaa90ab5663bf16f7f54349204545a9
+ 27efc282d3c1e3abdf8322191e09d0fe466c489e
-
+
https://github.com/dotnet/fsharp
- d11b46f7cdaa90ab5663bf16f7f54349204545a9
+ 27efc282d3c1e3abdf8322191e09d0fe466c489e
-
+
https://github.com/dotnet/roslyn
- 7b18089cb8f4c5a16de67205236522ba86b0a1c0
+ e465daf014a2b82e87d36eaef0942a07c2e44812
-
+
https://github.com/dotnet/roslyn
- 7b18089cb8f4c5a16de67205236522ba86b0a1c0
+ e465daf014a2b82e87d36eaef0942a07c2e44812
-
+
https://github.com/dotnet/roslyn
- 7b18089cb8f4c5a16de67205236522ba86b0a1c0
+ e465daf014a2b82e87d36eaef0942a07c2e44812
-
+
https://github.com/dotnet/roslyn
- 7b18089cb8f4c5a16de67205236522ba86b0a1c0
+ e465daf014a2b82e87d36eaef0942a07c2e44812
-
+
https://github.com/dotnet/roslyn
- 7b18089cb8f4c5a16de67205236522ba86b0a1c0
+ e465daf014a2b82e87d36eaef0942a07c2e44812
-
+
https://github.com/dotnet/roslyn
- 7b18089cb8f4c5a16de67205236522ba86b0a1c0
+ e465daf014a2b82e87d36eaef0942a07c2e44812
-
+
https://github.com/dotnet/roslyn
- 7b18089cb8f4c5a16de67205236522ba86b0a1c0
+ e465daf014a2b82e87d36eaef0942a07c2e44812
-
+
https://github.com/dotnet/roslyn
- 7b18089cb8f4c5a16de67205236522ba86b0a1c0
+ e465daf014a2b82e87d36eaef0942a07c2e44812
-
+
https://github.com/dotnet/aspnetcore
- 7412c71cf881392233b5652310ab2b9e7fdf71eb
+ 7d5309210d8f7bae8fa074da495e9d009d67f1b4
-
+
https://github.com/dotnet/aspnetcore
- 7412c71cf881392233b5652310ab2b9e7fdf71eb
+ 7d5309210d8f7bae8fa074da495e9d009d67f1b4
-
+
https://github.com/nuget/nuget.client
- d01e48ace560689897c1c642591659126736feb8
+ e9355040881280a661c9d8f9428d07aba1d05d97
-
+
https://github.com/nuget/nuget.client
- d01e48ace560689897c1c642591659126736feb8
+ e9355040881280a661c9d8f9428d07aba1d05d97
-
+
https://github.com/nuget/nuget.client
- d01e48ace560689897c1c642591659126736feb8
+ e9355040881280a661c9d8f9428d07aba1d05d97
-
+
https://github.com/nuget/nuget.client
- d01e48ace560689897c1c642591659126736feb8
+ e9355040881280a661c9d8f9428d07aba1d05d97
-
+
https://github.com/nuget/nuget.client
- d01e48ace560689897c1c642591659126736feb8
+ e9355040881280a661c9d8f9428d07aba1d05d97
-
+
https://github.com/nuget/nuget.client
- d01e48ace560689897c1c642591659126736feb8
+ e9355040881280a661c9d8f9428d07aba1d05d97
-
+
https://github.com/nuget/nuget.client
- d01e48ace560689897c1c642591659126736feb8
+ e9355040881280a661c9d8f9428d07aba1d05d97
-
+
https://github.com/nuget/nuget.client
- d01e48ace560689897c1c642591659126736feb8
+ e9355040881280a661c9d8f9428d07aba1d05d97
-
+
https://github.com/nuget/nuget.client
- d01e48ace560689897c1c642591659126736feb8
+ e9355040881280a661c9d8f9428d07aba1d05d97
-
+
https://github.com/nuget/nuget.client
- d01e48ace560689897c1c642591659126736feb8
+ e9355040881280a661c9d8f9428d07aba1d05d97
-
+
https://github.com/nuget/nuget.client
- d01e48ace560689897c1c642591659126736feb8
+ e9355040881280a661c9d8f9428d07aba1d05d97
-
+
https://github.com/nuget/nuget.client
- d01e48ace560689897c1c642591659126736feb8
+ e9355040881280a661c9d8f9428d07aba1d05d97
-
+
https://github.com/nuget/nuget.client
- d01e48ace560689897c1c642591659126736feb8
+ e9355040881280a661c9d8f9428d07aba1d05d97
-
+
https://github.com/nuget/nuget.client
- d01e48ace560689897c1c642591659126736feb8
+ e9355040881280a661c9d8f9428d07aba1d05d97
-
+
https://github.com/nuget/nuget.client
- d01e48ace560689897c1c642591659126736feb8
+ e9355040881280a661c9d8f9428d07aba1d05d97
-
+
https://github.com/nuget/nuget.client
- d01e48ace560689897c1c642591659126736feb8
+ e9355040881280a661c9d8f9428d07aba1d05d97
-
+
https://github.com/nuget/nuget.client
- d01e48ace560689897c1c642591659126736feb8
+ e9355040881280a661c9d8f9428d07aba1d05d97
https://github.com/microsoft/vstest
@@ -215,130 +215,130 @@
56d28849af08dc3143d019694aa92f186b89d2ac
-
+
https://github.com/dotnet/runtime
- 86a1911d7cbff65fc82b373e7fece572c9f0fd52
+ b0118bebe385cb68f272a32ca6a5c5bb53c9b14f
-
+
https://github.com/dotnet/runtime
- 86a1911d7cbff65fc82b373e7fece572c9f0fd52
+ b0118bebe385cb68f272a32ca6a5c5bb53c9b14f
-
+
https://github.com/dotnet/runtime
- 86a1911d7cbff65fc82b373e7fece572c9f0fd52
+ b0118bebe385cb68f272a32ca6a5c5bb53c9b14f
-
+
https://github.com/dotnet/runtime
- 86a1911d7cbff65fc82b373e7fece572c9f0fd52
+ b0118bebe385cb68f272a32ca6a5c5bb53c9b14f
-
+
https://github.com/dotnet/runtime
- 86a1911d7cbff65fc82b373e7fece572c9f0fd52
+ b0118bebe385cb68f272a32ca6a5c5bb53c9b14f
-
+
https://github.com/dotnet/windowsdesktop
- d04391a8d9c6140c58dbd34009f231c258d65eb8
+ c8b67095ab5f6342f86e1fb32f8f8038dd381352
-
+
https://github.com/dotnet/windowsdesktop
- d04391a8d9c6140c58dbd34009f231c258d65eb8
+ c8b67095ab5f6342f86e1fb32f8f8038dd381352
-
+
https://github.com/dotnet/windowsdesktop
- d04391a8d9c6140c58dbd34009f231c258d65eb8
+ c8b67095ab5f6342f86e1fb32f8f8038dd381352
-
+
https://github.com/dotnet/windowsdesktop
- d04391a8d9c6140c58dbd34009f231c258d65eb8
+ c8b67095ab5f6342f86e1fb32f8f8038dd381352
-
+
https://github.com/dotnet/wpf
- b769d01cd9e41e755513d0c2cee577e838d5ab80
+ 1b852a4e44a0f60310fe1d6eda6b5fefb2f9ef38
-
+
https://github.com/dotnet/aspnetcore
- 7412c71cf881392233b5652310ab2b9e7fdf71eb
+ 7d5309210d8f7bae8fa074da495e9d009d67f1b4
-
+
https://github.com/dotnet/aspnetcore
- 7412c71cf881392233b5652310ab2b9e7fdf71eb
+ 7d5309210d8f7bae8fa074da495e9d009d67f1b4
-
+
https://github.com/dotnet/aspnetcore
- 7412c71cf881392233b5652310ab2b9e7fdf71eb
+ 7d5309210d8f7bae8fa074da495e9d009d67f1b4
-
+
https://github.com/dotnet/aspnetcore
- 7412c71cf881392233b5652310ab2b9e7fdf71eb
+ 7d5309210d8f7bae8fa074da495e9d009d67f1b4
-
+
https://github.com/dotnet/aspnetcore
- 7412c71cf881392233b5652310ab2b9e7fdf71eb
+ 7d5309210d8f7bae8fa074da495e9d009d67f1b4
-
+
https://github.com/dotnet/aspnetcore
- 7412c71cf881392233b5652310ab2b9e7fdf71eb
+ 7d5309210d8f7bae8fa074da495e9d009d67f1b4
-
+
https://github.com/dotnet/aspnetcore
- 7412c71cf881392233b5652310ab2b9e7fdf71eb
+ 7d5309210d8f7bae8fa074da495e9d009d67f1b4
-
+
https://github.com/dotnet/aspnetcore
- 7412c71cf881392233b5652310ab2b9e7fdf71eb
+ 7d5309210d8f7bae8fa074da495e9d009d67f1b4
-
+
https://github.com/dotnet/aspnetcore
- 7412c71cf881392233b5652310ab2b9e7fdf71eb
+ 7d5309210d8f7bae8fa074da495e9d009d67f1b4
-
+
https://github.com/dotnet/aspnetcore
- 7412c71cf881392233b5652310ab2b9e7fdf71eb
+ 7d5309210d8f7bae8fa074da495e9d009d67f1b4
-
+
https://github.com/dotnet/aspnetcore
- 7412c71cf881392233b5652310ab2b9e7fdf71eb
+ 7d5309210d8f7bae8fa074da495e9d009d67f1b4
-
+
https://github.com/dotnet/aspnetcore
- 7412c71cf881392233b5652310ab2b9e7fdf71eb
+ 7d5309210d8f7bae8fa074da495e9d009d67f1b4
-
+
https://github.com/dotnet/razor
- 843b9af51d1f4913193883b66a1b20f5562ddaa5
+ 53f8beaad5a205928f9bad24793e3dd44fe40c32
-
+
https://github.com/dotnet/razor
- 843b9af51d1f4913193883b66a1b20f5562ddaa5
+ 53f8beaad5a205928f9bad24793e3dd44fe40c32
-
+
https://github.com/dotnet/razor
- 843b9af51d1f4913193883b66a1b20f5562ddaa5
+ 53f8beaad5a205928f9bad24793e3dd44fe40c32
-
+
https://github.com/dotnet/razor
- 843b9af51d1f4913193883b66a1b20f5562ddaa5
+ 53f8beaad5a205928f9bad24793e3dd44fe40c32
-
+
https://github.com/dotnet/aspnetcore
- 7412c71cf881392233b5652310ab2b9e7fdf71eb
+ 7d5309210d8f7bae8fa074da495e9d009d67f1b4
-
+
https://github.com/dotnet/aspnetcore
- 7412c71cf881392233b5652310ab2b9e7fdf71eb
+ 7d5309210d8f7bae8fa074da495e9d009d67f1b4
-
+
https://github.com/dotnet/aspnetcore
- 7412c71cf881392233b5652310ab2b9e7fdf71eb
+ 7d5309210d8f7bae8fa074da495e9d009d67f1b4
-
+
https://github.com/dotnet/aspnetcore
- 7412c71cf881392233b5652310ab2b9e7fdf71eb
+ 7d5309210d8f7bae8fa074da495e9d009d67f1b4
https://github.com/dotnet/xdt
@@ -387,9 +387,9 @@
-
+
https://github.com/dotnet/source-build-externals
- f4e750201aaf6bad67391a52e8138bbd7abe3019
+ 5a273649709de76f61957e3d69e1f031e5ac82e2
@@ -445,61 +445,61 @@
-
+
https://github.com/dotnet/runtime
- 86a1911d7cbff65fc82b373e7fece572c9f0fd52
+ b0118bebe385cb68f272a32ca6a5c5bb53c9b14f
-
+
https://github.com/dotnet/runtime
- 86a1911d7cbff65fc82b373e7fece572c9f0fd52
+ b0118bebe385cb68f272a32ca6a5c5bb53c9b14f
-
+
https://github.com/dotnet/runtime
- 86a1911d7cbff65fc82b373e7fece572c9f0fd52
+ b0118bebe385cb68f272a32ca6a5c5bb53c9b14f
-
+
https://github.com/dotnet/runtime
- 86a1911d7cbff65fc82b373e7fece572c9f0fd52
+ b0118bebe385cb68f272a32ca6a5c5bb53c9b14f
-
+
https://github.com/dotnet/runtime
- 86a1911d7cbff65fc82b373e7fece572c9f0fd52
+ b0118bebe385cb68f272a32ca6a5c5bb53c9b14f
-
+
https://github.com/dotnet/arcade
- 87b015b938e5400d6e57afd7650348c17a764b73
+ 6eb545ab682b04dc72cf574252b7843ec657dd99
-
+
https://github.com/dotnet/arcade
- 87b015b938e5400d6e57afd7650348c17a764b73
+ 6eb545ab682b04dc72cf574252b7843ec657dd99
-
+
https://github.com/dotnet/arcade
- 87b015b938e5400d6e57afd7650348c17a764b73
+ 6eb545ab682b04dc72cf574252b7843ec657dd99
-
+
https://github.com/dotnet/arcade
- 87b015b938e5400d6e57afd7650348c17a764b73
+ 6eb545ab682b04dc72cf574252b7843ec657dd99
-
+
https://github.com/dotnet/arcade
- 87b015b938e5400d6e57afd7650348c17a764b73
+ 6eb545ab682b04dc72cf574252b7843ec657dd99
-
+
https://github.com/dotnet/arcade
- 87b015b938e5400d6e57afd7650348c17a764b73
+ 6eb545ab682b04dc72cf574252b7843ec657dd99
-
+
https://github.com/dotnet/runtime
- 86a1911d7cbff65fc82b373e7fece572c9f0fd52
+ b0118bebe385cb68f272a32ca6a5c5bb53c9b14f
diff --git a/eng/Versions.props b/eng/Versions.props
index 049d8f1e917c..b7ccbb72bb16 100644
--- a/eng/Versions.props
+++ b/eng/Versions.props
@@ -31,12 +31,12 @@
8.0.0
4.0.0
8.0.0
- 9.0.0-preview.4.24215.5
+ 9.0.0-preview.4.24218.7
4.3.0
4.3.0
4.0.5
7.0.3
- 9.0.0-preview.4.24215.5
+ 9.0.0-preview.4.24218.7
4.6.0
2.0.0-beta4.24209.3
0.4.0-alpha.24209.3
@@ -49,33 +49,33 @@
- 9.0.0-preview.4.24215.5
- 9.0.0-preview.4.24215.5
- 9.0.0-preview.4.24215.5
+ 9.0.0-preview.4.24218.7
+ 9.0.0-preview.4.24218.7
+ 9.0.0-preview.4.24218.7
$(MicrosoftNETCoreAppRuntimewinx64PackageVersion)
- 9.0.0-preview.4.24215.5
- 9.0.0-preview.4.24215.5
- 9.0.0-preview.4.24215.5
- 9.0.0-preview.4.24215.5
- 9.0.0-preview.4.24215.5
- 9.0.0-preview.4.24215.5
- 9.0.0-preview.4.24215.5
- 9.0.0-preview.4.24215.5
+ 9.0.0-preview.4.24218.7
+ 9.0.0-preview.4.24218.7
+ 9.0.0-preview.4.24218.7
+ 9.0.0-preview.4.24218.7
+ 9.0.0-preview.4.24218.7
+ 9.0.0-preview.4.24218.7
+ 9.0.0-preview.4.24218.7
+ 9.0.0-preview.4.24218.7
- 6.11.0-preview.1.23
- 6.11.0-preview.1.23
- 6.11.0-preview.1.23
- 6.11.0-preview.1.23
- 6.11.0-preview.1.23
- 6.11.0-preview.1.23
- 6.11.0-preview.1.23
- 6.11.0-preview.1.23
- 6.11.0-preview.1.23
- 6.11.0-preview.1.23
- 6.11.0-preview.1.23
- 6.11.0-preview.1.23
+ 6.11.0-preview.1.27
+ 6.11.0-preview.1.27
+ 6.11.0-preview.1.27
+ 6.11.0-preview.1.27
+ 6.11.0-preview.1.27
+ 6.11.0-preview.1.27
+ 6.11.0-preview.1.27
+ 6.11.0-preview.1.27
+ 6.11.0-preview.1.27
+ 6.11.0-preview.1.27
+ 6.11.0-preview.1.27
+ 6.11.0-preview.1.27
@@ -85,9 +85,9 @@
- 9.0.0-preview.4.24215.5
- 9.0.0-preview.4.24215.5
- 9.0.0-preview.4.24215.5
+ 9.0.0-preview.4.24218.7
+ 9.0.0-preview.4.24218.7
+ 9.0.0-preview.4.24218.7
@@ -109,8 +109,8 @@
then use that in Directory.Packages.props.
At usage sites, either we use MicrosoftBuildMinimumVersion, or MicrosoftBuildVersion in source-only modes. -->
- 17.11.0-preview-24216-02
- 17.11.0-preview-24216-02
+ 17.11.0-preview-24218-01
+ 17.11.0-preview-24218-01
$([System.IO.File]::ReadAllText('$(RepoRoot)src\Layout\redist\minimumMSBuildVersion').Trim())
@@ -128,37 +128,37 @@
- 12.8.400-beta.24216.7
+ 12.8.400-beta.24218.1
- 4.11.0-1.24216.11
- 4.11.0-1.24216.11
- 4.11.0-1.24216.11
- 4.11.0-1.24216.11
- 4.11.0-1.24216.11
- 4.11.0-1.24216.11
- 4.11.0-1.24216.11
+ 4.11.0-1.24218.2
+ 4.11.0-1.24218.2
+ 4.11.0-1.24218.2
+ 4.11.0-1.24218.2
+ 4.11.0-1.24218.2
+ 4.11.0-1.24218.2
+ 4.11.0-1.24218.2
- 9.0.0-preview.4.24216.18
- 9.0.0-preview.4.24216.18
- 9.0.0-preview.4.24216.18
- 9.0.0-preview.4.24216.18
- 9.0.0-preview.4.24216.18
- 9.0.0-preview.4.24216.18
- 9.0.0-preview.4.24216.18
+ 9.0.0-preview.4.24218.1
+ 9.0.0-preview.4.24218.1
+ 9.0.0-preview.4.24218.1
+ 9.0.0-preview.4.24218.1
+ 9.0.0-preview.4.24218.1
+ 9.0.0-preview.4.24218.1
+ 9.0.0-preview.4.24218.1
- 7.0.0-preview.24216.6
- 7.0.0-preview.24216.6
- 7.0.0-preview.24216.6
+ 7.0.0-preview.24218.3
+ 7.0.0-preview.24218.3
+ 7.0.0-preview.24218.3
- 9.0.0-preview.4.24216.1
+ 9.0.0-preview.4.24218.2
@@ -167,8 +167,8 @@
- 9.0.0-beta.24212.4
- 9.0.0-beta.24212.4
+ 9.0.0-beta.24217.5
+ 9.0.0-beta.24217.5
@@ -187,7 +187,7 @@
6.12.0
6.1.0
- 9.0.0-beta.24212.4
+ 9.0.0-beta.24217.5
4.18.4
1.3.2
8.0.0-beta.23607.1
@@ -203,7 +203,7 @@
- 9.0.0-preview.4.24209.5
+ 9.0.0-preview.4.24215.3
$(MicrosoftNETWorkloadEmscriptenCurrentManifest90100TransportPackageVersion)
9.0.100$([System.Text.RegularExpressions.Regex]::Match($(EmscriptenWorkloadManifestVersion), `-[A-z]*[\.]*\d*`))
diff --git a/eng/common/templates-official/job/job.yml b/eng/common/templates-official/job/job.yml
index c63e17e863ed..761acc5eb624 100644
--- a/eng/common/templates-official/job/job.yml
+++ b/eng/common/templates-official/job/job.yml
@@ -210,7 +210,7 @@ jobs:
- task: 1ES.PublishPipelineArtifact@1
inputs:
targetPath: 'artifacts/log'
- artifactName: ${{ coalesce(parameters.artifacts.publish.logs.name, 'Logs_Build_$(Agent.Os)_$(_BuildConfig)') }}
+ artifactName: ${{ coalesce(parameters.artifacts.publish.logs.name, 'Logs_Build_$(Agent.Os)_$(_BuildConfig)_Attempt$(System.JobAttempt)') }}
displayName: 'Publish logs'
continueOnError: true
condition: always()
diff --git a/eng/common/templates-official/job/onelocbuild.yml b/eng/common/templates-official/job/onelocbuild.yml
index ba9ba4930329..52b4d05d3f8d 100644
--- a/eng/common/templates-official/job/onelocbuild.yml
+++ b/eng/common/templates-official/job/onelocbuild.yml
@@ -56,7 +56,7 @@ jobs:
# If it's not devdiv, it's dnceng
${{ if ne(variables['System.TeamProject'], 'DevDiv') }}:
name: $(DncEngInternalBuildPool)
- image: 1es-windows-2022-pt
+ image: 1es-windows-2022
os: windows
steps:
diff --git a/eng/common/templates-official/job/source-build.yml b/eng/common/templates-official/job/source-build.yml
index c918720931f4..2180e97a284f 100644
--- a/eng/common/templates-official/job/source-build.yml
+++ b/eng/common/templates-official/job/source-build.yml
@@ -52,7 +52,7 @@ jobs:
${{ if eq(variables['System.TeamProject'], 'internal') }}:
name: $[replace(replace(eq(contains(coalesce(variables['System.PullRequest.TargetBranch'], variables['Build.SourceBranch'], 'refs/heads/main'), 'release'), 'true'), True, 'NetCore1ESPool-Svc-Internal'), False, 'NetCore1ESPool-Internal')]
- image: 1es-mariner-2-pt
+ image: 1es-mariner-2
os: linux
${{ if ne(parameters.platform.pool, '') }}:
diff --git a/eng/common/templates-official/post-build/post-build.yml b/eng/common/templates-official/post-build/post-build.yml
index d286e956bdfa..da1f40958b45 100644
--- a/eng/common/templates-official/post-build/post-build.yml
+++ b/eng/common/templates-official/post-build/post-build.yml
@@ -110,7 +110,7 @@ stages:
# If it's not devdiv, it's dnceng
${{ else }}:
name: $(DncEngInternalBuildPool)
- image: 1es-windows-2022-pt
+ image: 1es-windows-2022
os: windows
steps:
@@ -150,7 +150,7 @@ stages:
# If it's not devdiv, it's dnceng
${{ else }}:
name: $(DncEngInternalBuildPool)
- image: 1es-windows-2022-pt
+ image: 1es-windows-2022
os: windows
steps:
- template: setup-maestro-vars.yml
@@ -208,7 +208,7 @@ stages:
# If it's not devdiv, it's dnceng
${{ else }}:
name: $(DncEngInternalBuildPool)
- image: 1es-windows-2022-pt
+ image: 1es-windows-2022
os: windows
steps:
- template: setup-maestro-vars.yml
diff --git a/eng/common/templates-official/variables/pool-providers.yml b/eng/common/templates-official/variables/pool-providers.yml
index beab7d1bfba0..1f308b24efc4 100644
--- a/eng/common/templates-official/variables/pool-providers.yml
+++ b/eng/common/templates-official/variables/pool-providers.yml
@@ -23,7 +23,7 @@
#
# pool:
# name: $(DncEngInternalBuildPool)
-# image: 1es-windows-2022-pt
+# image: 1es-windows-2022
variables:
# Coalesce the target and source branches so we know when a PR targets a release branch
diff --git a/global.json b/global.json
index 8a9f0c336582..2fd7a79913d3 100644
--- a/global.json
+++ b/global.json
@@ -1,6 +1,6 @@
{
"tools": {
- "dotnet": "9.0.100-preview.3.24175.24",
+ "dotnet": "9.0.100-preview.3.24204.13",
"runtimes": {
"dotnet": [
"$(VSRedistCommonNetCoreSharedFrameworkx6490PackageVersion)"
@@ -14,8 +14,8 @@
}
},
"msbuild-sdks": {
- "Microsoft.DotNet.Arcade.Sdk": "9.0.0-beta.24212.4",
- "Microsoft.DotNet.Helix.Sdk": "9.0.0-beta.24212.4",
+ "Microsoft.DotNet.Arcade.Sdk": "9.0.0-beta.24217.5",
+ "Microsoft.DotNet.Helix.Sdk": "9.0.0-beta.24217.5",
"Microsoft.Build.NoTargets": "3.7.0"
}
}
diff --git a/src/Assets/WasmOverride/Directory.Build.props b/src/Assets/WasmOverride/Directory.Build.props
index 058246e40862..bd354b1cf02e 100644
--- a/src/Assets/WasmOverride/Directory.Build.props
+++ b/src/Assets/WasmOverride/Directory.Build.props
@@ -1 +1,5 @@
-
+
+
+ false
+
+
diff --git a/src/Assets/WasmOverride/Nuget.config b/src/Assets/WasmOverride/Nuget.config
index e38ddec24549..167bc27499e1 100644
--- a/src/Assets/WasmOverride/Nuget.config
+++ b/src/Assets/WasmOverride/Nuget.config
@@ -1,5 +1,6 @@
+
diff --git a/src/BlazorWasmSdk/Targets/Microsoft.NET.Sdk.BlazorWebAssembly.6_0.targets b/src/BlazorWasmSdk/Targets/Microsoft.NET.Sdk.BlazorWebAssembly.6_0.targets
index b05c2cb0239e..0e91e6d00b5f 100644
--- a/src/BlazorWasmSdk/Targets/Microsoft.NET.Sdk.BlazorWebAssembly.6_0.targets
+++ b/src/BlazorWasmSdk/Targets/Microsoft.NET.Sdk.BlazorWebAssembly.6_0.targets
@@ -124,8 +124,17 @@ Copyright (c) .NET Foundation. All rights reserved.
+
+
+
+
+
diff --git a/src/BuiltInTools/dotnet-format/dotnet-format.csproj b/src/BuiltInTools/dotnet-format/dotnet-format.csproj
index 14323f64e97a..e71139ce8b4c 100644
--- a/src/BuiltInTools/dotnet-format/dotnet-format.csproj
+++ b/src/BuiltInTools/dotnet-format/dotnet-format.csproj
@@ -18,11 +18,14 @@
true
+ win-x64;win-x86;osx-x64
+
- win-x64;win-x86;osx-x64
+ win-x64;win-x86;osx-x64
+ $(TargetRid)
diff --git a/src/BuiltInTools/dotnet-watch/CommandLineOptions.cs b/src/BuiltInTools/dotnet-watch/CommandLineOptions.cs
index e6ba6e3b2046..a3ed558588c6 100644
--- a/src/BuiltInTools/dotnet-watch/CommandLineOptions.cs
+++ b/src/BuiltInTools/dotnet-watch/CommandLineOptions.cs
@@ -63,7 +63,7 @@ internal sealed class CommandLineOptions
// dotnet watch specific options:
var quietOption = new CliOption("--quiet", "-q") { Description = Resources.Help_Quiet };
- var verboseOption = new CliOption("--verbose", "-v") { Description = Resources.Help_Verbose };
+ var verboseOption = new CliOption("--verbose") { Description = Resources.Help_Verbose };
var listOption = new CliOption("--list") { Description = Resources.Help_List };
var noHotReloadOption = new CliOption("--no-hot-reload") { Description = Resources.Help_NoHotReload };
var nonInteractiveOption = new CliOption("--non-interactive") { Description = Resources.Help_NonInteractive };
@@ -190,8 +190,7 @@ where name is not []
private static IReadOnlyList GetLaunchProcessArguments(ParseResult parseResult, IReadOnlyList watchOptions, out string? explicitCommand)
{
- explicitCommand = null;
- var launchProcessArguments = new List();
+ var launchArgumentsBuilder = new List();
foreach (var child in parseResult.CommandResult.Children)
{
@@ -204,14 +203,14 @@ private static IReadOnlyList GetLaunchProcessArguments(ParseResult parse
if (optionResult.Tokens.Count == 0)
{
- launchProcessArguments.Add(optionResult.IdentifierToken.Value);
+ launchArgumentsBuilder.Add(optionResult.IdentifierToken.Value);
}
else
{
foreach (var token in optionResult.Tokens)
{
- launchProcessArguments.Add(optionResult.IdentifierToken.Value);
- launchProcessArguments.Add(token.Value);
+ launchArgumentsBuilder.Add(optionResult.IdentifierToken.Value);
+ launchArgumentsBuilder.Add(token.Value);
}
}
}
@@ -223,6 +222,9 @@ private static IReadOnlyList GetLaunchProcessArguments(ParseResult parse
var dashDashIndex = IndexOf(parseResult.Tokens, t => t.Value == "--");
var unmatchedTokensBeforeDashDash = parseResult.UnmatchedTokens.Count - (dashDashIndex >= 0 ? parseResult.Tokens.Count - dashDashIndex - 1 : 0);
+ explicitCommand = null;
+ var dashDashInserted = false;
+
for (int i = 0; i < parseResult.UnmatchedTokens.Count; i++)
{
var token = parseResult.UnmatchedTokens[i];
@@ -234,12 +236,17 @@ private static IReadOnlyList GetLaunchProcessArguments(ParseResult parse
}
else
{
- launchProcessArguments.Add(token);
+ if (!dashDashInserted && i >= unmatchedTokensBeforeDashDash)
+ {
+ launchArgumentsBuilder.Add("--");
+ dashDashInserted = true;
+ }
+
+ launchArgumentsBuilder.Add(token);
}
}
- launchProcessArguments.Insert(0, explicitCommand ?? DefaultCommand);
- return launchProcessArguments;
+ return [explicitCommand ?? DefaultCommand, ..launchArgumentsBuilder];
}
private static int IndexOf(IReadOnlyList list, Func predicate)
diff --git a/src/Cli/Microsoft.DotNet.Cli.Utils/Microsoft.DotNet.Cli.Utils.csproj b/src/Cli/Microsoft.DotNet.Cli.Utils/Microsoft.DotNet.Cli.Utils.csproj
index fa12a6ababd3..041901a741d5 100644
--- a/src/Cli/Microsoft.DotNet.Cli.Utils/Microsoft.DotNet.Cli.Utils.csproj
+++ b/src/Cli/Microsoft.DotNet.Cli.Utils/Microsoft.DotNet.Cli.Utils.csproj
@@ -44,7 +44,6 @@
-
diff --git a/src/Cli/dotnet/commands/RestoringCommand.cs b/src/Cli/dotnet/commands/RestoringCommand.cs
index 23a12b54a95d..4e5904436640 100644
--- a/src/Cli/dotnet/commands/RestoringCommand.cs
+++ b/src/Cli/dotnet/commands/RestoringCommand.cs
@@ -61,11 +61,8 @@ private static RestoreCommand GetSeparateRestoreCommand(
}
IEnumerable restoreArguments = ["-target:Restore"];
- if (arguments != null)
- {
- (var newArgumentsToAdd, var existingArgumentsToForward) = ProcessForwardedArgumentsForSeparateRestore(arguments);
- restoreArguments = [.. restoreArguments, .. newArgumentsToAdd, .. existingArgumentsToForward];
- }
+ (var newArgumentsToAdd, var existingArgumentsToForward) = ProcessForwardedArgumentsForSeparateRestore(arguments);
+ restoreArguments = [.. restoreArguments, .. newArgumentsToAdd, .. existingArgumentsToForward];
return new RestoreCommand(restoreArguments, msbuildPath);
}
@@ -84,12 +81,13 @@ private static bool HasArgumentToExcludeFromRestore(IEnumerable argument
"TargetFramework"
];
+ // These arguments should lead to absolutely no output from the restore command
private static readonly string[] FlagsThatTriggerSilentRestore =
- [
- "getProperty",
- "getItem",
- "getTargetResult"
- ];
+ [
+ "getProperty",
+ "getItem",
+ "getTargetResult"
+ ];
// These arguments don't by themselves require that restore be run in a separate process,
// but if there is a separate restore process they shouldn't be passed to it
@@ -116,12 +114,12 @@ private static bool HasArgumentToExcludeFromRestore(IEnumerable argument
// that we need to compensate for, so we might yield new arguments that should be included in the overall restore call.
private static (string[] newArgumentsToAdd, string[] existingArgumentsToForward) ProcessForwardedArgumentsForSeparateRestore(IEnumerable forwardedArguments)
{
- HashSet newArgumentsToAdd = new();
+ // Separate restore should be silent in terminal logger - regardless of actual scenario
+ HashSet newArgumentsToAdd = new() { "-tlp:verbosity=quiet" };
List existingArgumentsToForward = new();
- foreach (var argument in forwardedArguments)
+ foreach (var argument in forwardedArguments ?? Enumerable.Empty())
{
-
if (!IsExcludedFromSeparateRestore(argument) && !IsExcludedFromRestore(argument))
{
existingArgumentsToForward.Add(argument);
@@ -165,6 +163,7 @@ private static bool IsExcludedFromRestore(string argument)
private static bool IsExcludedFromSeparateRestore(string argument)
=> FlagsToExcludeFromSeparateRestore.Any(p => argument.StartsWith(p, StringComparison.OrdinalIgnoreCase));
+ // These arguments should lead to absolutely no output from the restore command - regardless of loggers
private static bool TriggersSilentSeparateRestore(string argument)
=> FlagsThatTriggerSilentSeparateRestore.Any(p => argument.StartsWith(p, StringComparison.OrdinalIgnoreCase));
diff --git a/src/Microsoft.Dotnet.Sdk.Internal/Microsoft.Dotnet.Sdk.Internal.csproj b/src/Microsoft.Dotnet.Sdk.Internal/Microsoft.Dotnet.Sdk.Internal.csproj
new file mode 100644
index 000000000000..be453356a86c
--- /dev/null
+++ b/src/Microsoft.Dotnet.Sdk.Internal/Microsoft.Dotnet.Sdk.Internal.csproj
@@ -0,0 +1,12 @@
+
+
+
+
+
+ netstandard2.0
+ true
+ false
+
+
+
diff --git a/src/RazorSdk/update-test-baselines.ps1 b/src/RazorSdk/update-test-baselines.ps1
index 148963c95fc8..34a3746fa615 100644
--- a/src/RazorSdk/update-test-baselines.ps1
+++ b/src/RazorSdk/update-test-baselines.ps1
@@ -5,7 +5,7 @@ $TestProjects = "Microsoft.NET.Sdk.Razor.Tests", "Microsoft.NET.Sdk.BlazorWebAss
ForEach-Object { Join-Path -Path "$RepoRoot/test/" -ChildPath $_ };
if($Validate){
- $TestProjects | ForEach-Object { dotnet test --no-build -l "console;verbosity=normal" $_ --filter AspNetCore=BaselineTest }
+ $TestProjects | ForEach-Object { dotnet test --no-build -c Release -l "console;verbosity=normal" $_ --filter AspNetCore=BaselineTest }
}else {
- $TestProjects | ForEach-Object { dotnet test --no-build -l "console;verbosity=normal" $_ -e ASPNETCORE_TEST_BASELINES=true --filter AspNetCore=BaselineTest }
+ $TestProjects | ForEach-Object { dotnet test --no-build -c Release -l "console;verbosity=normal" $_ -e ASPNETCORE_TEST_BASELINES=true --filter AspNetCore=BaselineTest }
}
diff --git a/src/RazorSdk/update-test-baselines.sh b/src/RazorSdk/update-test-baselines.sh
index 3409aec0293a..e5a4c4dca386 100644
--- a/src/RazorSdk/update-test-baselines.sh
+++ b/src/RazorSdk/update-test-baselines.sh
@@ -51,12 +51,12 @@ if [ $Validate -eq 1 ]; then
echo "Running in validate mode"
for TestProject in "${TestProjects[@]}"; do
echo "Running dotnet test on $TestProject"
- dotnet test --no-build -l "console;verbosity=normal" "$TestProject" --filter AspNetCore=BaselineTest
+ dotnet test --no-build -c Release -l "console;verbosity=normal" "$TestProject" --filter AspNetCore=BaselineTest
done
else
echo "Running in non-validate mode"
for TestProject in "${TestProjects[@]}"; do
echo "Running dotnet test on $TestProject"
- dotnet test --no-build -l "console;verbosity=normal" "$TestProject" -e ASPNETCORE_TEST_BASELINES=true --filter AspNetCore=BaselineTest
+ dotnet test --no-build -c Release -l "console;verbosity=normal" "$TestProject" -e ASPNETCORE_TEST_BASELINES=true --filter AspNetCore=BaselineTest
done
fi
diff --git a/src/SourceBuild/README.md b/src/SourceBuild/README.md
new file mode 100644
index 000000000000..1b946e99077c
--- /dev/null
+++ b/src/SourceBuild/README.md
@@ -0,0 +1,18 @@
+# Source-Build
+
+This directory contains the .NET source build infrastructure.
+
+_content_ - source build infrastructure mirrored to [dotnet/dotnet](https://github.com/dotnet/dotnet)
+ [VMR](https://github.com/dotnet/arcade/blob/main/Documentation/UnifiedBuild/VMR-Design-And-Operation.md).
+
+_patches_ - repo patches needed for .NET source build. Typically these are ephemeral to workaround integration
+ issues. For more information, see the [Patch Guidelines](https://github.com/dotnet/source-build/blob/main/Documentation/patching-guidelines.md).
+
+For more information, see [dotnet/source-build](https://github.com/dotnet/source-build).
+
+## Local development workflow
+
+When making changes to the source build infrastructure, devs would typically make and test the
+changes in a local clone of [dotnet/dotnet](https://github.com/dotnet/dotnet). Once complete
+you would copy the changed files here and make a PR. To validate the end to end experience, you
+can synchronize the VMR with any changes made here by running [eng/vmr-sync.sh](https://github.com/dotnet/installer/blob/main/eng/vmr-sync.sh).
diff --git a/src/SourceBuild/content/.config/1espt/PipelineAutobaseliningConfig.yml b/src/SourceBuild/content/.config/1espt/PipelineAutobaseliningConfig.yml
new file mode 100644
index 000000000000..02aa0cd1f657
--- /dev/null
+++ b/src/SourceBuild/content/.config/1espt/PipelineAutobaseliningConfig.yml
@@ -0,0 +1,21 @@
+## DO NOT MODIFY THIS FILE MANUALLY. This is part of auto-baselining from 1ES Pipeline Templates. Go to [https://aka.ms/1espt-autobaselining] for more details.
+
+pipelines:
+ 1330:
+ retail:
+ source:
+ credscan:
+ lastModifiedDate: 2024-03-25
+ eslint:
+ lastModifiedDate: 2024-03-25
+ psscriptanalyzer:
+ lastModifiedDate: 2024-03-25
+ armory:
+ lastModifiedDate: 2024-03-25
+ binary:
+ credscan:
+ lastModifiedDate: 2024-03-25
+ binskim:
+ lastModifiedDate: 2024-03-25
+ spotbugs:
+ lastModifiedDate: 2024-03-25
diff --git a/src/SourceBuild/content/.config/guardian/.gdnbaselines b/src/SourceBuild/content/.config/guardian/.gdnbaselines
new file mode 100644
index 000000000000..db0eefad83cc
--- /dev/null
+++ b/src/SourceBuild/content/.config/guardian/.gdnbaselines
@@ -0,0 +1,1477 @@
+{
+ "properties": {
+ "helpUri": "https://eng.ms/docs/microsoft-security/security/azure-security/cloudai-security-fundamentals-engineering/security-integration/guardian-wiki/microsoft-guardian/general/baselines"
+ },
+ "version": "1.0.0",
+ "baselines": {
+ "default": {
+ "name": "default",
+ "createdDate": "2024-03-25 18:56:33Z",
+ "lastUpdatedDate": "2024-03-25 18:56:33Z"
+ }
+ },
+ "results": {
+ "5f3b52e23f96eb01bcfd73ead3cbaa2e1430de0006e5103109dd39bf9f292165": {
+ "signature": "5f3b52e23f96eb01bcfd73ead3cbaa2e1430de0006e5103109dd39bf9f292165",
+ "alternativeSignatures": [],
+ "target": "src/nuget-client/docs/cross-platform-debugging.md",
+ "line": 66,
+ "memberOf": [
+ "default"
+ ],
+ "tool": "credscan",
+ "ruleId": "CSCAN-GENERAL0060",
+ "createdDate": "2024-03-25 18:56:33Z",
+ "expirationDate": "2024-09-11 20:34:12Z",
+ "justification": "This error is baselined with an expiration date of 180 days from 2024-03-25 20:34:12Z"
+ },
+ "72b28f655eadc78b21ab36a7f572708315d8d909d1b460162511e37086288e30": {
+ "signature": "72b28f655eadc78b21ab36a7f572708315d8d909d1b460162511e37086288e30",
+ "alternativeSignatures": [
+ "60efb04c6e0431e477e792a96d32b30b3a309b4ee19fad084a015e2946985459"
+ ],
+ "target": "src/roslyn/eng/build.ps1",
+ "line": 340,
+ "memberOf": [
+ "default"
+ ],
+ "tool": "psscriptanalyzer",
+ "ruleId": "PSAvoidUsingConvertToSecureStringWithPlainText",
+ "createdDate": "2024-03-25 18:56:33Z",
+ "expirationDate": "2024-09-11 20:34:12Z",
+ "justification": "This error is baselined with an expiration date of 180 days from 2024-03-25 20:34:12Z"
+ },
+ "e0aafe4de4d762a800edeca36ed2144ed173a138d126423863b1710425b90b33": {
+ "signature": "e0aafe4de4d762a800edeca36ed2144ed173a138d126423863b1710425b90b33",
+ "alternativeSignatures": [],
+ "target": "src/aspire/tests/Aspire.Npgsql.EntityFrameworkCore.PostgreSQL.Tests/ConformanceTests.cs",
+ "line": 20,
+ "memberOf": [
+ "default"
+ ],
+ "tool": "credscan",
+ "ruleId": "CSCAN-GENERAL0030",
+ "createdDate": "2024-03-25 18:56:33Z",
+ "expirationDate": "2024-09-11 20:34:12Z",
+ "justification": "This error is baselined with an expiration date of 180 days from 2024-03-25 20:34:12Z"
+ },
+ "a5142e7bafbf664fdcb2d4d7071ca8427c7da0c8ba66cc7706c9c07b816f1201": {
+ "signature": "a5142e7bafbf664fdcb2d4d7071ca8427c7da0c8ba66cc7706c9c07b816f1201",
+ "alternativeSignatures": [],
+ "target": "src/aspire/tests/Aspire.Npgsql.Tests/ConformanceTests.cs",
+ "line": 17,
+ "memberOf": [
+ "default"
+ ],
+ "tool": "credscan",
+ "ruleId": "CSCAN-GENERAL0030",
+ "createdDate": "2024-03-25 18:56:33Z",
+ "expirationDate": "2024-09-11 20:34:12Z",
+ "justification": "This error is baselined with an expiration date of 180 days from 2024-03-25 20:34:12Z"
+ },
+ "b6aecc1d8697beab291f9925633b5ec3e37a088033efc7e93928fd9cac96cda4": {
+ "signature": "b6aecc1d8697beab291f9925633b5ec3e37a088033efc7e93928fd9cac96cda4",
+ "alternativeSignatures": [
+ "985838b2d1518f507c85ae0f635951bad92dde58eb24c252d7e56fb6ccda6191"
+ ],
+ "target": "src/aspnetcore/src/DataProtection/CreateTestCert.ps1",
+ "line": 11,
+ "memberOf": [
+ "default"
+ ],
+ "tool": "psscriptanalyzer",
+ "ruleId": "PSAvoidUsingConvertToSecureStringWithPlainText",
+ "createdDate": "2024-03-25 18:56:33Z",
+ "expirationDate": "2024-09-11 20:34:12Z",
+ "justification": "This error is baselined with an expiration date of 180 days from 2024-03-25 20:34:12Z"
+ },
+ "5c349e8f183364d99cde545c6da7549c9d6227957c820fcde8e8beb2b40de39c": {
+ "signature": "5c349e8f183364d99cde545c6da7549c9d6227957c820fcde8e8beb2b40de39c",
+ "alternativeSignatures": [
+ "8546393d391f4010c04ed43788c36626f870b02028937cf390014c660f657f7b"
+ ],
+ "target": "src/command-line-api/eng/common/SetupNugetSources.ps1",
+ "line": 38,
+ "memberOf": [
+ "default"
+ ],
+ "tool": "psscriptanalyzer",
+ "ruleId": "PSAvoidUsingUsernameAndPasswordParams",
+ "createdDate": "2024-03-25 18:56:33Z",
+ "expirationDate": "2024-09-11 20:34:12Z",
+ "justification": "This error is baselined with an expiration date of 180 days from 2024-03-25 20:34:12Z"
+ },
+ "a9e7b46f71cc21fd96e3bbb1c30a7beb36470f0a4c857794b4444856e54ffc2b": {
+ "signature": "a9e7b46f71cc21fd96e3bbb1c30a7beb36470f0a4c857794b4444856e54ffc2b",
+ "alternativeSignatures": [
+ "34597b8dc5d2e482d7178a50440f3b8815c44e510906dd92a4d31d434c87053b"
+ ],
+ "target": "src/command-line-api/eng/common/SetupNugetSources.ps1",
+ "line": 56,
+ "memberOf": [
+ "default"
+ ],
+ "tool": "psscriptanalyzer",
+ "ruleId": "PSAvoidUsingUsernameAndPasswordParams",
+ "createdDate": "2024-03-25 18:56:33Z",
+ "expirationDate": "2024-09-11 20:34:12Z",
+ "justification": "This error is baselined with an expiration date of 180 days from 2024-03-25 20:34:12Z"
+ },
+ "3df69ea15defeb820ba0823dc80513e75a79b049dee023b51dee4419cd1d2276": {
+ "signature": "3df69ea15defeb820ba0823dc80513e75a79b049dee023b51dee4419cd1d2276",
+ "alternativeSignatures": [
+ "deb5cfe250ae8f9c1bbcdf230c425dc071067ee26cc7b3d41b9fc078782febfc"
+ ],
+ "target": "src/command-line-api/eng/common/SetupNugetSources.ps1",
+ "line": 88,
+ "memberOf": [
+ "default"
+ ],
+ "tool": "psscriptanalyzer",
+ "ruleId": "PSAvoidUsingUsernameAndPasswordParams",
+ "createdDate": "2024-03-25 18:56:33Z",
+ "expirationDate": "2024-09-11 20:34:12Z",
+ "justification": "This error is baselined with an expiration date of 180 days from 2024-03-25 20:34:12Z"
+ },
+ "32eb952e8561b331092ec83b626102388202071d192f9eb22c233b06ea90d2a1": {
+ "signature": "32eb952e8561b331092ec83b626102388202071d192f9eb22c233b06ea90d2a1",
+ "alternativeSignatures": [
+ "cce04b0a7c54b775c1464a32d85804de7d1777ac2a6d21da8b080c29ac46162c"
+ ],
+ "target": "src/diagnostics/eng/common/SetupNugetSources.ps1",
+ "line": 38,
+ "memberOf": [
+ "default"
+ ],
+ "tool": "psscriptanalyzer",
+ "ruleId": "PSAvoidUsingUsernameAndPasswordParams",
+ "createdDate": "2024-03-25 18:56:33Z",
+ "expirationDate": "2024-09-11 20:34:12Z",
+ "justification": "This error is baselined with an expiration date of 180 days from 2024-03-25 20:34:12Z"
+ },
+ "7d3c27b6bd42f58e624890a1669c188c6afee080e6330673942d364641876d52": {
+ "signature": "7d3c27b6bd42f58e624890a1669c188c6afee080e6330673942d364641876d52",
+ "alternativeSignatures": [
+ "29d9fec9e2b10d721512bb68a68759baa33b6bbc0683a2d3d5cdcb74894917ee"
+ ],
+ "target": "src/diagnostics/eng/common/SetupNugetSources.ps1",
+ "line": 56,
+ "memberOf": [
+ "default"
+ ],
+ "tool": "psscriptanalyzer",
+ "ruleId": "PSAvoidUsingUsernameAndPasswordParams",
+ "createdDate": "2024-03-25 18:56:33Z",
+ "expirationDate": "2024-09-11 20:34:12Z",
+ "justification": "This error is baselined with an expiration date of 180 days from 2024-03-25 20:34:12Z"
+ },
+ "42c9ee2033a50a53e2fcc83cb08b3a8c38a5c9e2230414ae0590d3c8f45a9a8d": {
+ "signature": "42c9ee2033a50a53e2fcc83cb08b3a8c38a5c9e2230414ae0590d3c8f45a9a8d",
+ "alternativeSignatures": [
+ "44e2cd3b2773d63b155f2093202415744b74f8b5c47612ad92f2ffee3939cb2f"
+ ],
+ "target": "src/diagnostics/eng/common/SetupNugetSources.ps1",
+ "line": 88,
+ "memberOf": [
+ "default"
+ ],
+ "tool": "psscriptanalyzer",
+ "ruleId": "PSAvoidUsingUsernameAndPasswordParams",
+ "createdDate": "2024-03-25 18:56:33Z",
+ "expirationDate": "2024-09-11 20:34:12Z",
+ "justification": "This error is baselined with an expiration date of 180 days from 2024-03-25 20:34:12Z"
+ },
+ "11d1ee60346a9d183bdfead22ccfd12efe5b121e24d50ea479e6ed51c5b284f8": {
+ "signature": "11d1ee60346a9d183bdfead22ccfd12efe5b121e24d50ea479e6ed51c5b284f8",
+ "alternativeSignatures": [
+ "f1095a63813963bb218b2e2a11bcfdf33c712ac8e182a37903ac05aa27618431"
+ ],
+ "target": "src/scenario-tests/eng/common/SetupNugetSources.ps1",
+ "line": 38,
+ "memberOf": [
+ "default"
+ ],
+ "tool": "psscriptanalyzer",
+ "ruleId": "PSAvoidUsingUsernameAndPasswordParams",
+ "createdDate": "2024-03-25 18:56:33Z",
+ "expirationDate": "2024-09-11 20:34:12Z",
+ "justification": "This error is baselined with an expiration date of 180 days from 2024-03-25 20:34:12Z"
+ },
+ "df1e4e250d2fec7e1df1b811d11ee5fde3f74ad34a4cfbb3c347fa0fa1f5f021": {
+ "signature": "df1e4e250d2fec7e1df1b811d11ee5fde3f74ad34a4cfbb3c347fa0fa1f5f021",
+ "alternativeSignatures": [
+ "5721e98504beccf9da5a70c8ea673f82adfa53a36883886e1c81097e7d2dc3a8"
+ ],
+ "target": "src/scenario-tests/eng/common/SetupNugetSources.ps1",
+ "line": 56,
+ "memberOf": [
+ "default"
+ ],
+ "tool": "psscriptanalyzer",
+ "ruleId": "PSAvoidUsingUsernameAndPasswordParams",
+ "createdDate": "2024-03-25 18:56:33Z",
+ "expirationDate": "2024-09-11 20:34:12Z",
+ "justification": "This error is baselined with an expiration date of 180 days from 2024-03-25 20:34:12Z"
+ },
+ "697b46955be8c055972365abf2b21287b19595c0204245465e3d05869bdf3b2b": {
+ "signature": "697b46955be8c055972365abf2b21287b19595c0204245465e3d05869bdf3b2b",
+ "alternativeSignatures": [
+ "fcef36ad5176c97ff601e5636a327fb6a2d7e89b0767b2a3c515f6f10b21ed4a"
+ ],
+ "target": "src/scenario-tests/eng/common/SetupNugetSources.ps1",
+ "line": 88,
+ "memberOf": [
+ "default"
+ ],
+ "tool": "psscriptanalyzer",
+ "ruleId": "PSAvoidUsingUsernameAndPasswordParams",
+ "createdDate": "2024-03-25 18:56:33Z",
+ "expirationDate": "2024-09-11 20:34:12Z",
+ "justification": "This error is baselined with an expiration date of 180 days from 2024-03-25 20:34:12Z"
+ },
+ "148a534bb099170811f8dcc0d51c1caa399488739a5ee98fb12bee51c7a9244d": {
+ "signature": "148a534bb099170811f8dcc0d51c1caa399488739a5ee98fb12bee51c7a9244d",
+ "alternativeSignatures": [],
+ "target": "src/source-build-externals/src/xunit/appveyor.yml",
+ "line": 7,
+ "memberOf": [
+ "default"
+ ],
+ "tool": "credscan",
+ "ruleId": "CSCAN-GENERAL0130",
+ "createdDate": "2024-03-25 18:56:33Z",
+ "expirationDate": "2024-09-11 20:34:12Z",
+ "justification": "This error is baselined with an expiration date of 180 days from 2024-03-25 20:34:12Z"
+ },
+ "2960b8fc6b1f6665b5988544f1d44a05dfe83b9b39a14efef5e042d7a78e4e19": {
+ "signature": "2960b8fc6b1f6665b5988544f1d44a05dfe83b9b39a14efef5e042d7a78e4e19",
+ "alternativeSignatures": [
+ "b4177488d7a45f4a54472adf8bb97026f0799e61e10f580ea52fbbd74cf08f10"
+ ],
+ "target": "src/symreader/eng/common/SetupNugetSources.ps1",
+ "line": 38,
+ "memberOf": [
+ "default"
+ ],
+ "tool": "psscriptanalyzer",
+ "ruleId": "PSAvoidUsingUsernameAndPasswordParams",
+ "createdDate": "2024-03-25 18:56:33Z",
+ "expirationDate": "2024-09-11 20:34:12Z",
+ "justification": "This error is baselined with an expiration date of 180 days from 2024-03-25 20:34:12Z"
+ },
+ "c1c411bf7b80d684d2c444ed611f333f08f1073fbaaf4c6bd0238c16ffccbe4d": {
+ "signature": "c1c411bf7b80d684d2c444ed611f333f08f1073fbaaf4c6bd0238c16ffccbe4d",
+ "alternativeSignatures": [
+ "8ca9e6612eb3802d5c1fd93ce0f1de61c2559512966fc97dcbeb017d1942c0fe"
+ ],
+ "target": "src/symreader/eng/common/SetupNugetSources.ps1",
+ "line": 56,
+ "memberOf": [
+ "default"
+ ],
+ "tool": "psscriptanalyzer",
+ "ruleId": "PSAvoidUsingUsernameAndPasswordParams",
+ "createdDate": "2024-03-25 18:56:33Z",
+ "expirationDate": "2024-09-11 20:34:12Z",
+ "justification": "This error is baselined with an expiration date of 180 days from 2024-03-25 20:34:12Z"
+ },
+ "a0437af80b26a79fc6c7e101114a0a455bd0bc7a4e9ccea1fa3b355aaac07390": {
+ "signature": "a0437af80b26a79fc6c7e101114a0a455bd0bc7a4e9ccea1fa3b355aaac07390",
+ "alternativeSignatures": [
+ "495012003aa9faede4c4ad115a12784f6a8f549e1ebe976537b021d6e5296da9"
+ ],
+ "target": "src/symreader/eng/common/SetupNugetSources.ps1",
+ "line": 88,
+ "memberOf": [
+ "default"
+ ],
+ "tool": "psscriptanalyzer",
+ "ruleId": "PSAvoidUsingUsernameAndPasswordParams",
+ "createdDate": "2024-03-25 18:56:33Z",
+ "expirationDate": "2024-09-11 20:34:12Z",
+ "justification": "This error is baselined with an expiration date of 180 days from 2024-03-25 20:34:12Z"
+ },
+ "d43d77b6bd95a33891a3f76e3b20bf3001f300e0fe05b477faa4b74a35735740": {
+ "signature": "d43d77b6bd95a33891a3f76e3b20bf3001f300e0fe05b477faa4b74a35735740",
+ "alternativeSignatures": [],
+ "target": "src/aspire/tests/Shared/TestCertificates/testCert.pfx",
+ "line": 1,
+ "memberOf": [
+ "default"
+ ],
+ "tool": "credscan",
+ "ruleId": "CSCAN-GENERAL0020",
+ "createdDate": "2024-03-25 18:56:33Z",
+ "expirationDate": "2024-09-11 20:34:12Z",
+ "justification": "This error is baselined with an expiration date of 180 days from 2024-03-25 20:34:12Z"
+ },
+ "3c494c3cdc3d2ab897a96f5b498fbf1731ba2c6dcc73e49399083635bc084e8a": {
+ "signature": "3c494c3cdc3d2ab897a96f5b498fbf1731ba2c6dcc73e49399083635bc084e8a",
+ "alternativeSignatures": [],
+ "target": "src/aspnetcore/src/Shared/TestCertificates/aspnetdevcert.pfx",
+ "line": 1,
+ "memberOf": [
+ "default"
+ ],
+ "tool": "credscan",
+ "ruleId": "CSCAN-GENERAL0020",
+ "createdDate": "2024-03-25 18:56:33Z",
+ "expirationDate": "2024-09-11 20:34:12Z",
+ "justification": "This error is baselined with an expiration date of 180 days from 2024-03-25 20:34:12Z"
+ },
+ "8bb20ad2a210dc906e39ceb48b0a6a39b240878688ad6854161240ae3a597c87": {
+ "signature": "8bb20ad2a210dc906e39ceb48b0a6a39b240878688ad6854161240ae3a597c87",
+ "alternativeSignatures": [],
+ "target": "src/aspnetcore/src/Shared/TestCertificates/eku.client.pfx",
+ "line": 1,
+ "memberOf": [
+ "default"
+ ],
+ "tool": "credscan",
+ "ruleId": "CSCAN-GENERAL0020",
+ "createdDate": "2024-03-25 18:56:33Z",
+ "expirationDate": "2024-09-11 20:34:12Z",
+ "justification": "This error is baselined with an expiration date of 180 days from 2024-03-25 20:34:12Z"
+ },
+ "486f45a6c6f00cc927a87f4b7b122829bee893d6f523fed279e7e2deda450aff": {
+ "signature": "486f45a6c6f00cc927a87f4b7b122829bee893d6f523fed279e7e2deda450aff",
+ "alternativeSignatures": [],
+ "target": "src/aspnetcore/src/Shared/TestCertificates/eku.code_signing.pfx",
+ "line": 1,
+ "memberOf": [
+ "default"
+ ],
+ "tool": "credscan",
+ "ruleId": "CSCAN-GENERAL0020",
+ "createdDate": "2024-03-25 18:56:33Z",
+ "expirationDate": "2024-09-11 20:34:12Z",
+ "justification": "This error is baselined with an expiration date of 180 days from 2024-03-25 20:34:12Z"
+ },
+ "643ded93447723ad1faad1de45b19108d986db08e488174a5422f3f1f7f0f7a3": {
+ "signature": "643ded93447723ad1faad1de45b19108d986db08e488174a5422f3f1f7f0f7a3",
+ "alternativeSignatures": [],
+ "target": "src/aspnetcore/src/Shared/TestCertificates/eku.multiple_usages.pfx",
+ "line": 1,
+ "memberOf": [
+ "default"
+ ],
+ "tool": "credscan",
+ "ruleId": "CSCAN-GENERAL0020",
+ "createdDate": "2024-03-25 18:56:33Z",
+ "expirationDate": "2024-09-11 20:34:12Z",
+ "justification": "This error is baselined with an expiration date of 180 days from 2024-03-25 20:34:12Z"
+ },
+ "51d877cdee6cab498e1a0fd4c4dd2702b3e5da9eb308e0a631d2104e2d3d2a8c": {
+ "signature": "51d877cdee6cab498e1a0fd4c4dd2702b3e5da9eb308e0a631d2104e2d3d2a8c",
+ "alternativeSignatures": [],
+ "target": "src/aspnetcore/src/Shared/TestCertificates/eku.server.pfx",
+ "line": 1,
+ "memberOf": [
+ "default"
+ ],
+ "tool": "credscan",
+ "ruleId": "CSCAN-GENERAL0020",
+ "createdDate": "2024-03-25 18:56:33Z",
+ "expirationDate": "2024-09-11 20:34:12Z",
+ "justification": "This error is baselined with an expiration date of 180 days from 2024-03-25 20:34:12Z"
+ },
+ "849b1bea0ba2f8d1c24a58896b9d230ca317e8cff7e9540f73d578dd1aba12cb": {
+ "signature": "849b1bea0ba2f8d1c24a58896b9d230ca317e8cff7e9540f73d578dd1aba12cb",
+ "alternativeSignatures": [],
+ "target": "src/aspnetcore/src/Shared/TestCertificates/https-aspnet.key",
+ "line": 1,
+ "memberOf": [
+ "default"
+ ],
+ "tool": "credscan",
+ "ruleId": "CSCAN-GENERAL0020",
+ "createdDate": "2024-03-25 18:56:33Z",
+ "expirationDate": "2024-09-11 20:34:12Z",
+ "justification": "This error is baselined with an expiration date of 180 days from 2024-03-25 20:34:12Z"
+ },
+ "c535332f090c89ae22a79aac4b9d344333c5479b891b79d72d252611c9364450": {
+ "signature": "c535332f090c89ae22a79aac4b9d344333c5479b891b79d72d252611c9364450",
+ "alternativeSignatures": [],
+ "target": "src/aspnetcore/src/Shared/TestCertificates/https-dsa-protected.key",
+ "line": 1,
+ "memberOf": [
+ "default"
+ ],
+ "tool": "credscan",
+ "ruleId": "CSCAN-GENERAL0020",
+ "createdDate": "2024-03-25 18:56:33Z",
+ "expirationDate": "2024-09-11 20:34:12Z",
+ "justification": "This error is baselined with an expiration date of 180 days from 2024-03-25 20:34:12Z"
+ },
+ "bc8ce9f1fd92dd123db1b6f8c33d15123bc8f3c4cb4fd42c54b4a2c5210bb158": {
+ "signature": "bc8ce9f1fd92dd123db1b6f8c33d15123bc8f3c4cb4fd42c54b4a2c5210bb158",
+ "alternativeSignatures": [],
+ "target": "src/aspnetcore/src/Shared/TestCertificates/https-dsa.key",
+ "line": 1,
+ "memberOf": [
+ "default"
+ ],
+ "tool": "credscan",
+ "ruleId": "CSCAN-GENERAL0020",
+ "createdDate": "2024-03-25 18:56:33Z",
+ "expirationDate": "2024-09-11 20:34:12Z",
+ "justification": "This error is baselined with an expiration date of 180 days from 2024-03-25 20:34:12Z"
+ },
+ "9507f913c4445bebb9bdd471960011afa198c1e42d19c45d44aea58af61a137d": {
+ "signature": "9507f913c4445bebb9bdd471960011afa198c1e42d19c45d44aea58af61a137d",
+ "alternativeSignatures": [],
+ "target": "src/aspnetcore/src/Shared/TestCertificates/https-ecdsa-protected.key",
+ "line": 1,
+ "memberOf": [
+ "default"
+ ],
+ "tool": "credscan",
+ "ruleId": "CSCAN-GENERAL0020",
+ "createdDate": "2024-03-25 18:56:33Z",
+ "expirationDate": "2024-09-11 20:34:12Z",
+ "justification": "This error is baselined with an expiration date of 180 days from 2024-03-25 20:34:12Z"
+ },
+ "9ac9e0a5efc7c0d1e6d89422ca8e70e913eaba647ef42650f0bbc50da080a556": {
+ "signature": "9ac9e0a5efc7c0d1e6d89422ca8e70e913eaba647ef42650f0bbc50da080a556",
+ "alternativeSignatures": [],
+ "target": "src/aspnetcore/src/Shared/TestCertificates/https-ecdsa.key",
+ "line": 1,
+ "memberOf": [
+ "default"
+ ],
+ "tool": "credscan",
+ "ruleId": "CSCAN-GENERAL0020",
+ "createdDate": "2024-03-25 18:56:33Z",
+ "expirationDate": "2024-09-11 20:34:12Z",
+ "justification": "This error is baselined with an expiration date of 180 days from 2024-03-25 20:34:12Z"
+ },
+ "5d49433b67f4a2f1b6b323c498722d16aeda2d8781dafe9fcb9faaf9db4ee3e1": {
+ "signature": "5d49433b67f4a2f1b6b323c498722d16aeda2d8781dafe9fcb9faaf9db4ee3e1",
+ "alternativeSignatures": [],
+ "target": "src/aspnetcore/src/Shared/TestCertificates/https-ecdsa.key",
+ "line": 4,
+ "memberOf": [
+ "default"
+ ],
+ "tool": "credscan",
+ "ruleId": "CSCAN-GENERAL0020",
+ "createdDate": "2024-03-25 18:56:33Z",
+ "expirationDate": "2024-09-11 20:34:12Z",
+ "justification": "This error is baselined with an expiration date of 180 days from 2024-03-25 20:34:12Z"
+ },
+ "71ee57f56c77339e4a0cb3f5bdfd05d15191136fc8ad887cef26fe1488522529": {
+ "signature": "71ee57f56c77339e4a0cb3f5bdfd05d15191136fc8ad887cef26fe1488522529",
+ "alternativeSignatures": [],
+ "target": "src/aspnetcore/src/Shared/TestCertificates/https-rsa-protected.key",
+ "line": 1,
+ "memberOf": [
+ "default"
+ ],
+ "tool": "credscan",
+ "ruleId": "CSCAN-GENERAL0020",
+ "createdDate": "2024-03-25 18:56:33Z",
+ "expirationDate": "2024-09-11 20:34:12Z",
+ "justification": "This error is baselined with an expiration date of 180 days from 2024-03-25 20:34:12Z"
+ },
+ "74b8ab85959da39f4da6710c6375080c44c8929d6b68f59a06dede355aeffacc": {
+ "signature": "74b8ab85959da39f4da6710c6375080c44c8929d6b68f59a06dede355aeffacc",
+ "alternativeSignatures": [],
+ "target": "src/aspnetcore/src/Shared/TestCertificates/https-rsa.key",
+ "line": 1,
+ "memberOf": [
+ "default"
+ ],
+ "tool": "credscan",
+ "ruleId": "CSCAN-GENERAL0020",
+ "createdDate": "2024-03-25 18:56:33Z",
+ "expirationDate": "2024-09-11 20:34:12Z",
+ "justification": "This error is baselined with an expiration date of 180 days from 2024-03-25 20:34:12Z"
+ },
+ "ca37e6fb6eb26bdec92893b3f6b08f8f41e6241f573a1b8e14d4e4d4ff1d2c7a": {
+ "signature": "ca37e6fb6eb26bdec92893b3f6b08f8f41e6241f573a1b8e14d4e4d4ff1d2c7a",
+ "alternativeSignatures": [],
+ "target": "src/aspnetcore/src/Shared/TestCertificates/intermediate2_ca.key",
+ "line": 1,
+ "memberOf": [
+ "default"
+ ],
+ "tool": "credscan",
+ "ruleId": "CSCAN-GENERAL0020",
+ "createdDate": "2024-03-25 18:56:33Z",
+ "expirationDate": "2024-09-11 20:34:12Z",
+ "justification": "This error is baselined with an expiration date of 180 days from 2024-03-25 20:34:12Z"
+ },
+ "d7657bb2e0603e7b353f7b1e1d884306fe44f116c7665192fd6b33003333ef7a": {
+ "signature": "d7657bb2e0603e7b353f7b1e1d884306fe44f116c7665192fd6b33003333ef7a",
+ "alternativeSignatures": [],
+ "target": "src/aspnetcore/src/Shared/TestCertificates/intermediate_ca.key",
+ "line": 1,
+ "memberOf": [
+ "default"
+ ],
+ "tool": "credscan",
+ "ruleId": "CSCAN-GENERAL0020",
+ "createdDate": "2024-03-25 18:56:33Z",
+ "expirationDate": "2024-09-11 20:34:12Z",
+ "justification": "This error is baselined with an expiration date of 180 days from 2024-03-25 20:34:12Z"
+ },
+ "90b7e7a132c8df4864a9ee49670cbaec9cbc5f5c53bf009eba5a583e0934a24f": {
+ "signature": "90b7e7a132c8df4864a9ee49670cbaec9cbc5f5c53bf009eba5a583e0934a24f",
+ "alternativeSignatures": [],
+ "target": "src/aspnetcore/src/Shared/TestCertificates/leaf.com.key",
+ "line": 1,
+ "memberOf": [
+ "default"
+ ],
+ "tool": "credscan",
+ "ruleId": "CSCAN-GENERAL0020",
+ "createdDate": "2024-03-25 18:56:33Z",
+ "expirationDate": "2024-09-11 20:34:12Z",
+ "justification": "This error is baselined with an expiration date of 180 days from 2024-03-25 20:34:12Z"
+ },
+ "8a5003e2db05146eaba3a7fc7aba715f51a5506b741a6ac2662e47e39c6165aa": {
+ "signature": "8a5003e2db05146eaba3a7fc7aba715f51a5506b741a6ac2662e47e39c6165aa",
+ "alternativeSignatures": [],
+ "target": "src/aspnetcore/src/Shared/TestCertificates/no_extensions.pfx",
+ "line": 1,
+ "memberOf": [
+ "default"
+ ],
+ "tool": "credscan",
+ "ruleId": "CSCAN-GENERAL0020",
+ "createdDate": "2024-03-25 18:56:33Z",
+ "expirationDate": "2024-09-11 20:34:12Z",
+ "justification": "This error is baselined with an expiration date of 180 days from 2024-03-25 20:34:12Z"
+ },
+ "02590eb0efbb241f09bae58f60907c6dee5b33507519a4be87e168a458c2b9cb": {
+ "signature": "02590eb0efbb241f09bae58f60907c6dee5b33507519a4be87e168a458c2b9cb",
+ "alternativeSignatures": [],
+ "target": "src/aspnetcore/src/Shared/TestCertificates/root_ca.key",
+ "line": 1,
+ "memberOf": [
+ "default"
+ ],
+ "tool": "credscan",
+ "ruleId": "CSCAN-GENERAL0020",
+ "createdDate": "2024-03-25 18:56:33Z",
+ "expirationDate": "2024-09-11 20:34:12Z",
+ "justification": "This error is baselined with an expiration date of 180 days from 2024-03-25 20:34:12Z"
+ },
+ "df250772236d85876a9d789cca90b48e5eb79ad6cb13782465c8c88366c5d845": {
+ "signature": "df250772236d85876a9d789cca90b48e5eb79ad6cb13782465c8c88366c5d845",
+ "alternativeSignatures": [],
+ "target": "src/aspnetcore/src/Shared/TestCertificates/testCert.pfx",
+ "line": 1,
+ "memberOf": [
+ "default"
+ ],
+ "tool": "credscan",
+ "ruleId": "CSCAN-GENERAL0020",
+ "createdDate": "2024-03-25 18:56:33Z",
+ "expirationDate": "2024-09-11 20:34:12Z",
+ "justification": "This error is baselined with an expiration date of 180 days from 2024-03-25 20:34:12Z"
+ },
+ "0d9b21b17336fcaa4d1e6360b228eef8db6a2afe199d9db14a245eba8e59f9f7": {
+ "signature": "0d9b21b17336fcaa4d1e6360b228eef8db6a2afe199d9db14a245eba8e59f9f7",
+ "alternativeSignatures": [],
+ "target": "src/msbuild/src/Tasks.UnitTests/TestResources/mycert.pfx",
+ "line": 1,
+ "memberOf": [
+ "default"
+ ],
+ "tool": "credscan",
+ "ruleId": "CSCAN-GENERAL0020",
+ "createdDate": "2024-03-25 18:56:33Z",
+ "expirationDate": "2024-09-11 20:34:12Z",
+ "justification": "This error is baselined with an expiration date of 180 days from 2024-03-25 20:34:12Z"
+ },
+ "3cd8e3eb9b94b01c93591c685406ea91d9d31b16aace0f109734e4bacb3838f2": {
+ "signature": "3cd8e3eb9b94b01c93591c685406ea91d9d31b16aace0f109734e4bacb3838f2",
+ "alternativeSignatures": [],
+ "target": "src/aspnetcore/src/DefaultBuilder/test/Microsoft.AspNetCore.FunctionalTests/testCert.pfx",
+ "line": 1,
+ "memberOf": [
+ "default"
+ ],
+ "tool": "credscan",
+ "ruleId": "CSCAN-GENERAL0020",
+ "createdDate": "2024-03-25 18:56:33Z",
+ "expirationDate": "2024-09-11 20:34:12Z",
+ "justification": "This error is baselined with an expiration date of 180 days from 2024-03-25 20:34:12Z"
+ },
+ "e91e45a96bf36327f551eadf27d9598b3d058fc051b0f9f0f1da9420410dc79a": {
+ "signature": "e91e45a96bf36327f551eadf27d9598b3d058fc051b0f9f0f1da9420410dc79a",
+ "alternativeSignatures": [],
+ "target": "src/aspnetcore/src/Servers/IIS/tools/TestCert.pfx",
+ "line": 1,
+ "memberOf": [
+ "default"
+ ],
+ "tool": "credscan",
+ "ruleId": "CSCAN-GENERAL0020",
+ "createdDate": "2024-03-25 18:56:33Z",
+ "expirationDate": "2024-09-11 20:34:12Z",
+ "justification": "This error is baselined with an expiration date of 180 days from 2024-03-25 20:34:12Z"
+ },
+ "1c8a3d52bb83c1fbd1208b94663769b6452e73988540113dff20bfb4df4ca010": {
+ "signature": "1c8a3d52bb83c1fbd1208b94663769b6452e73988540113dff20bfb4df4ca010",
+ "alternativeSignatures": [],
+ "target": "src/aspnetcore/src/SignalR/common/Shared/testCert.pfx",
+ "line": 1,
+ "memberOf": [
+ "default"
+ ],
+ "tool": "credscan",
+ "ruleId": "CSCAN-GENERAL0020",
+ "createdDate": "2024-03-25 18:56:33Z",
+ "expirationDate": "2024-09-11 20:34:12Z",
+ "justification": "This error is baselined with an expiration date of 180 days from 2024-03-25 20:34:12Z"
+ },
+ "8839c1c5ab6e962faf123ad7b79a584170d6491855f69555664986a425984a36": {
+ "signature": "8839c1c5ab6e962faf123ad7b79a584170d6491855f69555664986a425984a36",
+ "alternativeSignatures": [],
+ "target": "src/aspnetcore/src/SignalR/common/Shared/testCertECC.pfx",
+ "line": 1,
+ "memberOf": [
+ "default"
+ ],
+ "tool": "credscan",
+ "ruleId": "CSCAN-GENERAL0020",
+ "createdDate": "2024-03-25 18:56:33Z",
+ "expirationDate": "2024-09-11 20:34:12Z",
+ "justification": "This error is baselined with an expiration date of 180 days from 2024-03-25 20:34:12Z"
+ },
+ "d2df996be35f179b45a3bdc28fcd9d5254a924ab52d2ca14b068bfea35e65284": {
+ "signature": "d2df996be35f179b45a3bdc28fcd9d5254a924ab52d2ca14b068bfea35e65284",
+ "alternativeSignatures": [],
+ "target": "src/source-build-externals/src/azure-activedirectory-identitymodel-extensions-for-dotnet/test/Certs/SelfSigned1024_SHA1.pfx",
+ "line": 1,
+ "memberOf": [
+ "default"
+ ],
+ "tool": "credscan",
+ "ruleId": "CSCAN-GENERAL0020",
+ "createdDate": "2024-03-25 18:56:33Z",
+ "expirationDate": "2024-09-11 20:34:12Z",
+ "justification": "This error is baselined with an expiration date of 180 days from 2024-03-25 20:34:12Z"
+ },
+ "7a20b01096651f581c51904be7cd1281150c40efc61352b70aabbca3c40ea177": {
+ "signature": "7a20b01096651f581c51904be7cd1281150c40efc61352b70aabbca3c40ea177",
+ "alternativeSignatures": [],
+ "target": "src/source-build-externals/src/azure-activedirectory-identitymodel-extensions-for-dotnet/test/Certs/SelfSigned1024_SHA256.pfx",
+ "line": 1,
+ "memberOf": [
+ "default"
+ ],
+ "tool": "credscan",
+ "ruleId": "CSCAN-GENERAL0020",
+ "createdDate": "2024-03-25 18:56:33Z",
+ "expirationDate": "2024-09-11 20:34:12Z",
+ "justification": "This error is baselined with an expiration date of 180 days from 2024-03-25 20:34:12Z"
+ },
+ "ef394eaa05e6eb5af1f5e523fd01f4e970f36cb2c7eacb3d363b47c9f70b0fec": {
+ "signature": "ef394eaa05e6eb5af1f5e523fd01f4e970f36cb2c7eacb3d363b47c9f70b0fec",
+ "alternativeSignatures": [],
+ "target": "src/source-build-externals/src/azure-activedirectory-identitymodel-extensions-for-dotnet/test/Certs/SelfSigned2048_SHA256.pfx",
+ "line": 1,
+ "memberOf": [
+ "default"
+ ],
+ "tool": "credscan",
+ "ruleId": "CSCAN-GENERAL0020",
+ "createdDate": "2024-03-25 18:56:33Z",
+ "expirationDate": "2024-09-11 20:34:12Z",
+ "justification": "This error is baselined with an expiration date of 180 days from 2024-03-25 20:34:12Z"
+ },
+ "cb83a69b59f2de3fa1d049750ddeb855030d5662c43a1c5fea6b95f01e21547f": {
+ "signature": "cb83a69b59f2de3fa1d049750ddeb855030d5662c43a1c5fea6b95f01e21547f",
+ "alternativeSignatures": [],
+ "target": "src/source-build-externals/src/azure-activedirectory-identitymodel-extensions-for-dotnet/test/Certs/SelfSigned2048_SHA256_2.pfx",
+ "line": 1,
+ "memberOf": [
+ "default"
+ ],
+ "tool": "credscan",
+ "ruleId": "CSCAN-GENERAL0020",
+ "createdDate": "2024-03-25 18:56:33Z",
+ "expirationDate": "2024-09-11 20:34:12Z",
+ "justification": "This error is baselined with an expiration date of 180 days from 2024-03-25 20:34:12Z"
+ },
+ "d08376cdfec80b5c0884a8b85f18c8b34ffee19f1395d12aef1ffc2821120f03": {
+ "signature": "d08376cdfec80b5c0884a8b85f18c8b34ffee19f1395d12aef1ffc2821120f03",
+ "alternativeSignatures": [],
+ "target": "src/source-build-externals/src/azure-activedirectory-identitymodel-extensions-for-dotnet/test/Certs/SelfSigned2048_SHA384.pfx",
+ "line": 1,
+ "memberOf": [
+ "default"
+ ],
+ "tool": "credscan",
+ "ruleId": "CSCAN-GENERAL0020",
+ "createdDate": "2024-03-25 18:56:33Z",
+ "expirationDate": "2024-09-11 20:34:12Z",
+ "justification": "This error is baselined with an expiration date of 180 days from 2024-03-25 20:34:12Z"
+ },
+ "ef471993ee9839701feba16b5b56a926545a165bf95224130d6c8a2bdafdd451": {
+ "signature": "ef471993ee9839701feba16b5b56a926545a165bf95224130d6c8a2bdafdd451",
+ "alternativeSignatures": [],
+ "target": "src/source-build-externals/src/azure-activedirectory-identitymodel-extensions-for-dotnet/test/Certs/SelfSigned2048_SHA512.pfx",
+ "line": 1,
+ "memberOf": [
+ "default"
+ ],
+ "tool": "credscan",
+ "ruleId": "CSCAN-GENERAL0020",
+ "createdDate": "2024-03-25 18:56:33Z",
+ "expirationDate": "2024-09-11 20:34:12Z",
+ "justification": "This error is baselined with an expiration date of 180 days from 2024-03-25 20:34:12Z"
+ },
+ "d742600df5b4b6b71f65cf0079b09ec36a5d58bb4b8b07923b13ab8458f68a15": {
+ "signature": "d742600df5b4b6b71f65cf0079b09ec36a5d58bb4b8b07923b13ab8458f68a15",
+ "alternativeSignatures": [],
+ "target": "src/source-build-externals/src/azure-activedirectory-identitymodel-extensions-for-dotnet/test/Microsoft.IdentityModel.TestUtils/KeyingMaterial.cs",
+ "line": 31,
+ "memberOf": [
+ "default"
+ ],
+ "tool": "credscan",
+ "ruleId": "CSCAN-GENERAL0060",
+ "createdDate": "2024-03-25 18:56:33Z",
+ "expirationDate": "2024-09-11 20:34:12Z",
+ "justification": "This error is baselined with an expiration date of 180 days from 2024-03-25 20:34:12Z"
+ },
+ "fd9536ec348269dbb12b813270403b7410fc13575d1cbb7770604dcf54ee776b": {
+ "signature": "fd9536ec348269dbb12b813270403b7410fc13575d1cbb7770604dcf54ee776b",
+ "alternativeSignatures": [],
+ "target": "src/source-build-externals/src/azure-activedirectory-identitymodel-extensions-for-dotnet/test/Microsoft.IdentityModel.TestUtils/KeyingMaterial.cs",
+ "line": 40,
+ "memberOf": [
+ "default"
+ ],
+ "tool": "credscan",
+ "ruleId": "CSCAN-GENERAL0060",
+ "createdDate": "2024-03-25 18:56:33Z",
+ "expirationDate": "2024-09-11 20:34:12Z",
+ "justification": "This error is baselined with an expiration date of 180 days from 2024-03-25 20:34:12Z"
+ },
+ "1614f1f0821f8900c7c8d2cb3a784272518ef6b86fb82070bda88b4bac9dbda8": {
+ "signature": "1614f1f0821f8900c7c8d2cb3a784272518ef6b86fb82070bda88b4bac9dbda8",
+ "alternativeSignatures": [],
+ "target": "src/source-build-externals/src/azure-activedirectory-identitymodel-extensions-for-dotnet/test/Microsoft.IdentityModel.TestUtils/KeyingMaterial.cs",
+ "line": 50,
+ "memberOf": [
+ "default"
+ ],
+ "tool": "credscan",
+ "ruleId": "CSCAN-GENERAL0060",
+ "createdDate": "2024-03-25 18:56:33Z",
+ "expirationDate": "2024-09-11 20:34:12Z",
+ "justification": "This error is baselined with an expiration date of 180 days from 2024-03-25 20:34:12Z"
+ },
+ "3479973e9ab00c84264e70c5d7290a5bfda506aafdb08c3277cb1df5db688ab9": {
+ "signature": "3479973e9ab00c84264e70c5d7290a5bfda506aafdb08c3277cb1df5db688ab9",
+ "alternativeSignatures": [],
+ "target": "src/source-build-externals/src/azure-activedirectory-identitymodel-extensions-for-dotnet/test/Microsoft.IdentityModel.TestUtils/KeyingMaterial.cs",
+ "line": 56,
+ "memberOf": [
+ "default"
+ ],
+ "tool": "credscan",
+ "ruleId": "CSCAN-GENERAL0060",
+ "createdDate": "2024-03-25 18:56:33Z",
+ "expirationDate": "2024-09-11 20:34:12Z",
+ "justification": "This error is baselined with an expiration date of 180 days from 2024-03-25 20:34:12Z"
+ },
+ "e5bd231f55be121ed2579da9651c4ec0e661a386991ef8463facf3588c306a06": {
+ "signature": "e5bd231f55be121ed2579da9651c4ec0e661a386991ef8463facf3588c306a06",
+ "alternativeSignatures": [],
+ "target": "src/source-build-externals/src/azure-activedirectory-identitymodel-extensions-for-dotnet/test/Microsoft.IdentityModel.TestUtils/KeyingMaterial.cs",
+ "line": 65,
+ "memberOf": [
+ "default"
+ ],
+ "tool": "credscan",
+ "ruleId": "CSCAN-GENERAL0060",
+ "createdDate": "2024-03-25 18:56:33Z",
+ "expirationDate": "2024-09-11 20:34:12Z",
+ "justification": "This error is baselined with an expiration date of 180 days from 2024-03-25 20:34:12Z"
+ },
+ "19661fe89c5e1f59089cdec06cbdf6bdda2439b52731dfd630b50ff5885d6223": {
+ "signature": "19661fe89c5e1f59089cdec06cbdf6bdda2439b52731dfd630b50ff5885d6223",
+ "alternativeSignatures": [],
+ "target": "src/source-build-externals/src/azure-activedirectory-identitymodel-extensions-for-dotnet/test/Microsoft.IdentityModel.TestUtils/KeyingMaterial.cs",
+ "line": 75,
+ "memberOf": [
+ "default"
+ ],
+ "tool": "credscan",
+ "ruleId": "CSCAN-GENERAL0060",
+ "createdDate": "2024-03-25 18:56:33Z",
+ "expirationDate": "2024-09-11 20:34:12Z",
+ "justification": "This error is baselined with an expiration date of 180 days from 2024-03-25 20:34:12Z"
+ },
+ "3bf81d57b0872ef6c8ac7878513cb8f58044e07238883f71f24b09542a8d1a07": {
+ "signature": "3bf81d57b0872ef6c8ac7878513cb8f58044e07238883f71f24b09542a8d1a07",
+ "alternativeSignatures": [],
+ "target": "src/source-build-externals/src/azure-activedirectory-identitymodel-extensions-for-dotnet/test/Microsoft.IdentityModel.TestUtils/KeyingMaterial.cs",
+ "line": 80,
+ "memberOf": [
+ "default"
+ ],
+ "tool": "credscan",
+ "ruleId": "CSCAN-GENERAL0020",
+ "createdDate": "2024-03-25 18:56:33Z",
+ "expirationDate": "2024-09-11 20:34:12Z",
+ "justification": "This error is baselined with an expiration date of 180 days from 2024-03-25 20:34:12Z"
+ },
+ "2e07fdf491a04fc8a6cc50ae299f8345a81798499bda961a118e974970bb71a8": {
+ "signature": "2e07fdf491a04fc8a6cc50ae299f8345a81798499bda961a118e974970bb71a8",
+ "alternativeSignatures": [],
+ "target": "src/source-build-externals/src/azure-activedirectory-identitymodel-extensions-for-dotnet/test/Microsoft.IdentityModel.TestUtils/KeyingMaterial.cs",
+ "line": 86,
+ "memberOf": [
+ "default"
+ ],
+ "tool": "credscan",
+ "ruleId": "CSCAN-GENERAL0020",
+ "createdDate": "2024-03-25 18:56:33Z",
+ "expirationDate": "2024-09-11 20:34:12Z",
+ "justification": "This error is baselined with an expiration date of 180 days from 2024-03-25 20:34:12Z"
+ },
+ "560fdab2979120fa3cfd2d9865d1ce2c0ba164982cc086cdc871b7e16fd12466": {
+ "signature": "560fdab2979120fa3cfd2d9865d1ce2c0ba164982cc086cdc871b7e16fd12466",
+ "alternativeSignatures": [],
+ "target": "src/source-build-externals/src/azure-activedirectory-identitymodel-extensions-for-dotnet/test/Microsoft.IdentityModel.TestUtils/KeyingMaterial.cs",
+ "line": 92,
+ "memberOf": [
+ "default"
+ ],
+ "tool": "credscan",
+ "ruleId": "CSCAN-GENERAL0020",
+ "createdDate": "2024-03-25 18:56:33Z",
+ "expirationDate": "2024-09-11 20:34:12Z",
+ "justification": "This error is baselined with an expiration date of 180 days from 2024-03-25 20:34:12Z"
+ },
+ "489c5d92c47b3c961db0d5c32426a4d5fb48311e46ae49791be0f6ef03ddfc6e": {
+ "signature": "489c5d92c47b3c961db0d5c32426a4d5fb48311e46ae49791be0f6ef03ddfc6e",
+ "alternativeSignatures": [],
+ "target": "src/source-build-externals/src/azure-activedirectory-identitymodel-extensions-for-dotnet/test/Microsoft.IdentityModel.TestUtils/KeyingMaterial.cs",
+ "line": 101,
+ "memberOf": [
+ "default"
+ ],
+ "tool": "credscan",
+ "ruleId": "CSCAN-GENERAL0060",
+ "createdDate": "2024-03-25 18:56:33Z",
+ "expirationDate": "2024-09-11 20:34:12Z",
+ "justification": "This error is baselined with an expiration date of 180 days from 2024-03-25 20:34:12Z"
+ },
+ "5890ec8b0f533c8186a824f1b46b2d99c2d54e7ed09917e5fedcdaea19b34706": {
+ "signature": "5890ec8b0f533c8186a824f1b46b2d99c2d54e7ed09917e5fedcdaea19b34706",
+ "alternativeSignatures": [],
+ "target": "src/source-build-externals/src/azure-activedirectory-identitymodel-extensions-for-dotnet/test/Microsoft.IdentityModel.TestUtils/KeyingMaterial.cs",
+ "line": 230,
+ "memberOf": [
+ "default"
+ ],
+ "tool": "credscan",
+ "ruleId": "CSCAN-GENERAL0140",
+ "createdDate": "2024-03-25 18:56:33Z",
+ "expirationDate": "2024-09-11 20:34:12Z",
+ "justification": "This error is baselined with an expiration date of 180 days from 2024-03-25 20:34:12Z"
+ },
+ "70a8cd9f176fa02a78470ae78c285f56b617060ec339f0c8dfda095a5b0fc6c9": {
+ "signature": "70a8cd9f176fa02a78470ae78c285f56b617060ec339f0c8dfda095a5b0fc6c9",
+ "alternativeSignatures": [],
+ "target": "src/source-build-externals/src/humanizer/src/Humanizer.Tests.Uwp/Humanizer.Tests.Uwp_TemporaryKey.pfx",
+ "line": 1,
+ "memberOf": [
+ "default"
+ ],
+ "tool": "credscan",
+ "ruleId": "CSCAN-GENERAL0020",
+ "createdDate": "2024-03-25 18:56:33Z",
+ "expirationDate": "2024-09-11 20:34:12Z",
+ "justification": "This error is baselined with an expiration date of 180 days from 2024-03-25 20:34:12Z"
+ },
+ "7afd296a74705d2e561497c0ab8c3750179c1eff425f496297d3b877ff718526": {
+ "signature": "7afd296a74705d2e561497c0ab8c3750179c1eff425f496297d3b877ff718526",
+ "alternativeSignatures": [],
+ "target": "src/source-build-externals/src/humanizer/src/Humanizer.Tests.Uwp.Runner/Humanizer.Tests.Uwp.Runner_TemporaryKey.pfx",
+ "line": 1,
+ "memberOf": [
+ "default"
+ ],
+ "tool": "credscan",
+ "ruleId": "CSCAN-GENERAL0020",
+ "createdDate": "2024-03-25 18:56:33Z",
+ "expirationDate": "2024-09-11 20:34:12Z",
+ "justification": "This error is baselined with an expiration date of 180 days from 2024-03-25 20:34:12Z"
+ },
+ "6ffd69e0724ed3b57e926224932b0aeeed7a834ee5dc1ad24c3262deb49172d7": {
+ "signature": "6ffd69e0724ed3b57e926224932b0aeeed7a834ee5dc1ad24c3262deb49172d7",
+ "alternativeSignatures": [],
+ "target": "src/aspnetcore/src/DataProtection/Extensions/test/TestFiles/TestCert.pfx",
+ "line": 1,
+ "memberOf": [
+ "default"
+ ],
+ "tool": "credscan",
+ "ruleId": "CSCAN-GENERAL0020",
+ "createdDate": "2024-03-25 18:56:33Z",
+ "expirationDate": "2024-09-11 20:34:12Z",
+ "justification": "This error is baselined with an expiration date of 180 days from 2024-03-25 20:34:12Z"
+ },
+ "ca298b6b96ebfae8f14e29993264310b89fd738b365c2e7ca04e7af1a5cd67b7": {
+ "signature": "ca298b6b96ebfae8f14e29993264310b89fd738b365c2e7ca04e7af1a5cd67b7",
+ "alternativeSignatures": [],
+ "target": "src/aspnetcore/src/DataProtection/Extensions/test/TestFiles/TestCert2.pfx",
+ "line": 1,
+ "memberOf": [
+ "default"
+ ],
+ "tool": "credscan",
+ "ruleId": "CSCAN-GENERAL0020",
+ "createdDate": "2024-03-25 18:56:33Z",
+ "expirationDate": "2024-09-11 20:34:12Z",
+ "justification": "This error is baselined with an expiration date of 180 days from 2024-03-25 20:34:12Z"
+ },
+ "8764975fced41745e1a9c844144b2aacd30d3fcd77a8c4b1067adc5f2cd8b990": {
+ "signature": "8764975fced41745e1a9c844144b2aacd30d3fcd77a8c4b1067adc5f2cd8b990",
+ "alternativeSignatures": [],
+ "target": "src/aspnetcore/src/DataProtection/Extensions/test/TestFiles/TestCert3.pfx",
+ "line": 1,
+ "memberOf": [
+ "default"
+ ],
+ "tool": "credscan",
+ "ruleId": "CSCAN-GENERAL0020",
+ "createdDate": "2024-03-25 18:56:33Z",
+ "expirationDate": "2024-09-11 20:34:12Z",
+ "justification": "This error is baselined with an expiration date of 180 days from 2024-03-25 20:34:12Z"
+ },
+ "7cc3b3782554547d94e0f2e8817dbf823a84de8a95b6b358bdd29067bcff64c3": {
+ "signature": "7cc3b3782554547d94e0f2e8817dbf823a84de8a95b6b358bdd29067bcff64c3",
+ "alternativeSignatures": [],
+ "target": "src/aspnetcore/src/DataProtection/Extensions/test/TestFiles/TestCert3WithoutPrivateKey.pfx",
+ "line": 1,
+ "memberOf": [
+ "default"
+ ],
+ "tool": "credscan",
+ "ruleId": "CSCAN-GENERAL0020",
+ "createdDate": "2024-03-25 18:56:33Z",
+ "expirationDate": "2024-09-11 20:34:12Z",
+ "justification": "This error is baselined with an expiration date of 180 days from 2024-03-25 20:34:12Z"
+ },
+ "db15b8200219494e427c3943404a81e931bf17af175823bc51da9b85b63a6831": {
+ "signature": "db15b8200219494e427c3943404a81e931bf17af175823bc51da9b85b63a6831",
+ "alternativeSignatures": [],
+ "target": "src/aspnetcore/src/DataProtection/Extensions/test/TestFiles/TestCertWithoutPrivateKey.pfx",
+ "line": 1,
+ "memberOf": [
+ "default"
+ ],
+ "tool": "credscan",
+ "ruleId": "CSCAN-GENERAL0020",
+ "createdDate": "2024-03-25 18:56:33Z",
+ "expirationDate": "2024-09-11 20:34:12Z",
+ "justification": "This error is baselined with an expiration date of 180 days from 2024-03-25 20:34:12Z"
+ },
+ "979582e68b87ec6e4cd7d90df4e05f01a6f9150ed07a0abc205112a241f0c16f": {
+ "signature": "979582e68b87ec6e4cd7d90df4e05f01a6f9150ed07a0abc205112a241f0c16f",
+ "alternativeSignatures": [],
+ "target": "src/diagnostics/src/SOS/SOS.UnitTests/Debuggees/WebApp3/testCert.pfx",
+ "line": 1,
+ "memberOf": [
+ "default"
+ ],
+ "tool": "credscan",
+ "ruleId": "CSCAN-GENERAL0020",
+ "createdDate": "2024-03-25 18:56:33Z",
+ "expirationDate": "2024-09-11 20:34:12Z",
+ "justification": "This error is baselined with an expiration date of 180 days from 2024-03-25 20:34:12Z"
+ },
+ "56b251ecea166720fac682142922d79e01699a8cc576683b6d8944dfd3158de2": {
+ "signature": "56b251ecea166720fac682142922d79e01699a8cc576683b6d8944dfd3158de2",
+ "alternativeSignatures": [],
+ "target": "src/nuget-client/test/NuGet.Core.Tests/NuGet.Protocol.Tests/Plugins/Messages/GetCredentialsResponseTests.cs",
+ "line": 72,
+ "memberOf": [
+ "default"
+ ],
+ "tool": "credscan",
+ "ruleId": "CSCAN-GENERAL0060",
+ "createdDate": "2024-03-25 18:56:33Z",
+ "expirationDate": "2024-09-11 20:34:12Z",
+ "justification": "This error is baselined with an expiration date of 180 days from 2024-03-25 20:34:12Z"
+ },
+ "cbcd76cbd2a6b0a8028fd6c3d1d11ee2e211519af2420396399c3c111a4da667": {
+ "signature": "cbcd76cbd2a6b0a8028fd6c3d1d11ee2e211519af2420396399c3c111a4da667",
+ "alternativeSignatures": [],
+ "target": "src/nuget-client/test/NuGet.Core.Tests/NuGet.Protocol.Tests/Plugins/Messages/GetCredentialsResponseTests.cs",
+ "line": 73,
+ "memberOf": [
+ "default"
+ ],
+ "tool": "credscan",
+ "ruleId": "CSCAN-GENERAL0060",
+ "createdDate": "2024-03-25 18:56:33Z",
+ "expirationDate": "2024-09-11 20:34:12Z",
+ "justification": "This error is baselined with an expiration date of 180 days from 2024-03-25 20:34:12Z"
+ },
+ "8060e90601237ded928ae57570ef982dd3a57e7bffbec010fcd37e0bda518f10": {
+ "signature": "8060e90601237ded928ae57570ef982dd3a57e7bffbec010fcd37e0bda518f10",
+ "alternativeSignatures": [],
+ "target": "src/nuget-client/test/NuGet.Core.Tests/NuGet.Protocol.Tests/Plugins/Messages/GetCredentialsResponseTests.cs",
+ "line": 90,
+ "memberOf": [
+ "default"
+ ],
+ "tool": "credscan",
+ "ruleId": "CSCAN-GENERAL0060",
+ "createdDate": "2024-03-25 18:56:33Z",
+ "expirationDate": "2024-09-11 20:34:12Z",
+ "justification": "This error is baselined with an expiration date of 180 days from 2024-03-25 20:34:12Z"
+ },
+ "bc4b0a2231b13e5472e1548250ef4d7174d130daf559bca81f5d3c2c0c169690": {
+ "signature": "bc4b0a2231b13e5472e1548250ef4d7174d130daf559bca81f5d3c2c0c169690",
+ "alternativeSignatures": [],
+ "target": "src/nuget-client/test/NuGet.Core.Tests/NuGet.Protocol.Tests/Plugins/Messages/GetCredentialsResponseTests.cs",
+ "line": 91,
+ "memberOf": [
+ "default"
+ ],
+ "tool": "credscan",
+ "ruleId": "CSCAN-GENERAL0060",
+ "createdDate": "2024-03-25 18:56:33Z",
+ "expirationDate": "2024-09-11 20:34:12Z",
+ "justification": "This error is baselined with an expiration date of 180 days from 2024-03-25 20:34:12Z"
+ },
+ "91f0752674a19d0db604c50e23950746ff4d231cc76e40a7fa0f53dd5e855f4d": {
+ "signature": "91f0752674a19d0db604c50e23950746ff4d231cc76e40a7fa0f53dd5e855f4d",
+ "alternativeSignatures": [],
+ "target": "src/nuget-client/test/NuGet.Core.Tests/NuGet.Protocol.Tests/Plugins/Messages/GetCredentialsResponseTests.cs",
+ "line": 109,
+ "memberOf": [
+ "default"
+ ],
+ "tool": "credscan",
+ "ruleId": "CSCAN-GENERAL0060",
+ "createdDate": "2024-03-25 18:56:33Z",
+ "expirationDate": "2024-09-11 20:34:12Z",
+ "justification": "This error is baselined with an expiration date of 180 days from 2024-03-25 20:34:12Z"
+ },
+ "889e33f8e52ecb1d2b98d28772c572c2a10f0dc0c22fcc03b1da03df008d5f91": {
+ "signature": "889e33f8e52ecb1d2b98d28772c572c2a10f0dc0c22fcc03b1da03df008d5f91",
+ "alternativeSignatures": [],
+ "target": "src/nuget-client/test/NuGet.Core.Tests/NuGet.Protocol.Tests/Plugins/Messages/GetCredentialsResponseTests.cs",
+ "line": 110,
+ "memberOf": [
+ "default"
+ ],
+ "tool": "credscan",
+ "ruleId": "CSCAN-GENERAL0060",
+ "createdDate": "2024-03-25 18:56:33Z",
+ "expirationDate": "2024-09-11 20:34:12Z",
+ "justification": "This error is baselined with an expiration date of 180 days from 2024-03-25 20:34:12Z"
+ },
+ "6969ae787ed7e1caef670545de569929814479666e60a227abcb36395c3d3f60": {
+ "signature": "6969ae787ed7e1caef670545de569929814479666e60a227abcb36395c3d3f60",
+ "alternativeSignatures": [],
+ "target": "src/nuget-client/test/NuGet.Core.Tests/NuGet.Protocol.Tests/Plugins/Messages/GetCredentialsResponseTests.cs",
+ "line": 111,
+ "memberOf": [
+ "default"
+ ],
+ "tool": "credscan",
+ "ruleId": "CSCAN-GENERAL0060",
+ "createdDate": "2024-03-25 18:56:33Z",
+ "expirationDate": "2024-09-11 20:34:12Z",
+ "justification": "This error is baselined with an expiration date of 180 days from 2024-03-25 20:34:12Z"
+ },
+ "81adfccafd8eb134b75b59de6d4d0d8198c4639a3972d8072b61c67c9e1a104d": {
+ "signature": "81adfccafd8eb134b75b59de6d4d0d8198c4639a3972d8072b61c67c9e1a104d",
+ "alternativeSignatures": [],
+ "target": "src/nuget-client/test/NuGet.Core.Tests/NuGet.Protocol.Tests/Plugins/Messages/GetCredentialsResponseTests.cs",
+ "line": 112,
+ "memberOf": [
+ "default"
+ ],
+ "tool": "credscan",
+ "ruleId": "CSCAN-GENERAL0060",
+ "createdDate": "2024-03-25 18:56:33Z",
+ "expirationDate": "2024-09-11 20:34:12Z",
+ "justification": "This error is baselined with an expiration date of 180 days from 2024-03-25 20:34:12Z"
+ },
+ "cf0223227addcd8b17bc89e4e5f0ee9174bca83c9ffc5b5493ef74940b33b58c": {
+ "signature": "cf0223227addcd8b17bc89e4e5f0ee9174bca83c9ffc5b5493ef74940b33b58c",
+ "alternativeSignatures": [],
+ "target": "src/nuget-client/test/NuGet.Core.Tests/NuGet.Protocol.Tests/Plugins/Messages/SetCredentialsRequestTests.cs",
+ "line": 49,
+ "memberOf": [
+ "default"
+ ],
+ "tool": "credscan",
+ "ruleId": "CSCAN-GENERAL0060",
+ "createdDate": "2024-03-25 18:56:33Z",
+ "expirationDate": "2024-09-11 20:34:12Z",
+ "justification": "This error is baselined with an expiration date of 180 days from 2024-03-25 20:34:12Z"
+ },
+ "fc000248c70e613f381ee770fb96dfd28e26766fa9627d28e5b9eb5fb314a3c8": {
+ "signature": "fc000248c70e613f381ee770fb96dfd28e26766fa9627d28e5b9eb5fb314a3c8",
+ "alternativeSignatures": [],
+ "target": "src/nuget-client/test/NuGet.Core.Tests/NuGet.Protocol.Tests/Plugins/Messages/SetCredentialsRequestTests.cs",
+ "line": 72,
+ "memberOf": [
+ "default"
+ ],
+ "tool": "credscan",
+ "ruleId": "CSCAN-GENERAL0060",
+ "createdDate": "2024-03-25 18:56:33Z",
+ "expirationDate": "2024-09-11 20:34:12Z",
+ "justification": "This error is baselined with an expiration date of 180 days from 2024-03-25 20:34:12Z"
+ },
+ "b93b213c68cbbde7c5d0f64b48ad5289efccfcb18e202930a256ac7dbdd2264a": {
+ "signature": "b93b213c68cbbde7c5d0f64b48ad5289efccfcb18e202930a256ac7dbdd2264a",
+ "alternativeSignatures": [],
+ "target": "src/roslyn/src/EditorFeatures/CSharpTest2/EmbeddedLanguages/Json/CSharpJsonParserTests_BasicTests.cs",
+ "line": 4207,
+ "memberOf": [
+ "default"
+ ],
+ "tool": "credscan",
+ "ruleId": "CSCAN-GENERAL0060",
+ "createdDate": "2024-03-25 18:56:33Z",
+ "expirationDate": "2024-09-11 20:34:12Z",
+ "justification": "This error is baselined with an expiration date of 180 days from 2024-03-25 20:34:12Z"
+ },
+ "5ac1e0f1847cf476ae1c810ac0a74ee5993d05873feca939c0df1ac691a1527c": {
+ "signature": "5ac1e0f1847cf476ae1c810ac0a74ee5993d05873feca939c0df1ac691a1527c",
+ "alternativeSignatures": [],
+ "target": "src/runtime/src/libraries/System.Security.Cryptography/tests/X509Certificates/TestData.cs",
+ "line": 3441,
+ "memberOf": [
+ "default"
+ ],
+ "tool": "credscan",
+ "ruleId": "CSCAN-GENERAL0060",
+ "createdDate": "2024-03-25 18:56:33Z",
+ "expirationDate": "2024-09-11 20:34:12Z",
+ "justification": "This error is baselined with an expiration date of 180 days from 2024-03-25 20:34:12Z"
+ },
+ "2987ea627ea4f2effb417244c4aac8f554bf42136d6ebe0fef1df440410be09c": {
+ "signature": "2987ea627ea4f2effb417244c4aac8f554bf42136d6ebe0fef1df440410be09c",
+ "alternativeSignatures": [],
+ "target": "src/runtime/src/libraries/System.Security.Cryptography/tests/X509Certificates/TestData.cs",
+ "line": 2743,
+ "memberOf": [
+ "default"
+ ],
+ "tool": "credscan",
+ "ruleId": "CSCAN-GENERAL0020",
+ "createdDate": "2024-03-25 18:56:33Z",
+ "expirationDate": "2024-09-11 20:34:12Z",
+ "justification": "This error is baselined with an expiration date of 180 days from 2024-03-25 20:34:12Z"
+ },
+ "4c2880cbace8da677942b80be039a982e356c18c827ebeddab265175c9640427": {
+ "signature": "4c2880cbace8da677942b80be039a982e356c18c827ebeddab265175c9640427",
+ "alternativeSignatures": [],
+ "target": "src/runtime/src/libraries/System.Security.Cryptography/tests/X509Certificates/TestData.cs",
+ "line": 2879,
+ "memberOf": [
+ "default"
+ ],
+ "tool": "credscan",
+ "ruleId": "CSCAN-GENERAL0020",
+ "createdDate": "2024-03-25 18:56:33Z",
+ "expirationDate": "2024-09-11 20:34:12Z",
+ "justification": "This error is baselined with an expiration date of 180 days from 2024-03-25 20:34:12Z"
+ },
+ "849ea2c1bdff1b36df8848a7f8898f11e981c3ba8f9b033abd9d1e8b1a03c8ca": {
+ "signature": "849ea2c1bdff1b36df8848a7f8898f11e981c3ba8f9b033abd9d1e8b1a03c8ca",
+ "alternativeSignatures": [],
+ "target": "src/runtime/src/libraries/System.Security.Cryptography/tests/X509Certificates/TestData.cs",
+ "line": 2962,
+ "memberOf": [
+ "default"
+ ],
+ "tool": "credscan",
+ "ruleId": "CSCAN-GENERAL0020",
+ "createdDate": "2024-03-25 18:56:33Z",
+ "expirationDate": "2024-09-11 20:34:12Z",
+ "justification": "This error is baselined with an expiration date of 180 days from 2024-03-25 20:34:12Z"
+ },
+ "1706aea6ec9bfbbe540cfab3713db9b1a9ebc7f475b49f2348860e3ee6d8e682": {
+ "signature": "1706aea6ec9bfbbe540cfab3713db9b1a9ebc7f475b49f2348860e3ee6d8e682",
+ "alternativeSignatures": [],
+ "target": "src/aspnetcore/src/DataProtection/DataProtection/test/Microsoft.AspNetCore.DataProtection.Tests/TestFiles/TestCert1.pfx",
+ "line": 1,
+ "memberOf": [
+ "default"
+ ],
+ "tool": "credscan",
+ "ruleId": "CSCAN-GENERAL0020",
+ "createdDate": "2024-03-25 18:56:33Z",
+ "expirationDate": "2024-09-11 20:34:12Z",
+ "justification": "This error is baselined with an expiration date of 180 days from 2024-03-25 20:34:12Z"
+ },
+ "8369054f43a96d4f463976b23773b2d22f8081d95f715161f019ceb58296d64a": {
+ "signature": "8369054f43a96d4f463976b23773b2d22f8081d95f715161f019ceb58296d64a",
+ "alternativeSignatures": [],
+ "target": "src/aspnetcore/src/DataProtection/DataProtection/test/Microsoft.AspNetCore.DataProtection.Tests/TestFiles/TestCert2.pfx",
+ "line": 1,
+ "memberOf": [
+ "default"
+ ],
+ "tool": "credscan",
+ "ruleId": "CSCAN-GENERAL0020",
+ "createdDate": "2024-03-25 18:56:33Z",
+ "expirationDate": "2024-09-11 20:34:12Z",
+ "justification": "This error is baselined with an expiration date of 180 days from 2024-03-25 20:34:12Z"
+ },
+ "4ab520e2ed77b36c88f9eeeb5473205fd7f8f0a761eacee4cbbff389368dad8f": {
+ "signature": "4ab520e2ed77b36c88f9eeeb5473205fd7f8f0a761eacee4cbbff389368dad8f",
+ "alternativeSignatures": [
+ "7ad51ffeb7d5438f15781162de5183c58d5db2d195f96e8c3527451adeb4e02c"
+ ],
+ "target": "src/aspnetcore/src/Hosting/Server.IntegrationTesting/src/Deployers/RemoteWindowsDeployer/RemotePSSessionHelper.ps1",
+ "line": 40,
+ "memberOf": [
+ "default"
+ ],
+ "tool": "psscriptanalyzer",
+ "ruleId": "PSAvoidUsingConvertToSecureStringWithPlainText",
+ "createdDate": "2024-03-25 18:56:33Z",
+ "expirationDate": "2024-09-11 20:34:12Z",
+ "justification": "This error is baselined with an expiration date of 180 days from 2024-03-25 20:34:12Z"
+ },
+ "96ab8bbaf30065be77880d679174d028a151c0fc888fca38a79038d341710563": {
+ "signature": "96ab8bbaf30065be77880d679174d028a151c0fc888fca38a79038d341710563",
+ "alternativeSignatures": [],
+ "target": "src/aspnetcore/src/Security/Authentication/Negotiate/samples/NegotiateAuthSample/Startup.cs",
+ "line": 30,
+ "memberOf": [
+ "default"
+ ],
+ "tool": "credscan",
+ "ruleId": "CSCAN-MSFT0090",
+ "createdDate": "2024-03-25 18:56:33Z",
+ "expirationDate": "2024-09-11 20:34:12Z",
+ "justification": "This error is baselined with an expiration date of 180 days from 2024-03-25 20:34:12Z"
+ },
+ "7bda7ae4037698bbdf188b4d3fb8a82e99063a8df2dcfe5f66ef747e3bd3f1a0": {
+ "signature": "7bda7ae4037698bbdf188b4d3fb8a82e99063a8df2dcfe5f66ef747e3bd3f1a0",
+ "alternativeSignatures": [],
+ "target": "src/aspnetcore/src/Security/Authentication/Negotiate/test/Negotiate.FunctionalTest/negotiateAuthCert.pfx",
+ "line": 1,
+ "memberOf": [
+ "default"
+ ],
+ "tool": "credscan",
+ "ruleId": "CSCAN-GENERAL0020",
+ "createdDate": "2024-03-25 18:56:33Z",
+ "expirationDate": "2024-09-11 20:34:12Z",
+ "justification": "This error is baselined with an expiration date of 180 days from 2024-03-25 20:34:12Z"
+ },
+ "a2dcb4a5d7e266049f9f865b27e69af19447e94f63ec9de8290763ddf901c756": {
+ "signature": "a2dcb4a5d7e266049f9f865b27e69af19447e94f63ec9de8290763ddf901c756",
+ "alternativeSignatures": [],
+ "target": "src/aspnetcore/src/Security/Authentication/Negotiate/test/Negotiate.Test/LdapSettingsValidationTests.cs",
+ "line": 25,
+ "memberOf": [
+ "default"
+ ],
+ "tool": "credscan",
+ "ruleId": "CSCAN-MSFT0090",
+ "createdDate": "2024-03-25 18:56:33Z",
+ "expirationDate": "2024-09-11 20:34:12Z",
+ "justification": "This error is baselined with an expiration date of 180 days from 2024-03-25 20:34:12Z"
+ },
+ "3aaf66a9a1f5bfba3b78eb7964ce8bf4dcabf7132465a31e219c6961f53dec56": {
+ "signature": "3aaf66a9a1f5bfba3b78eb7964ce8bf4dcabf7132465a31e219c6961f53dec56",
+ "alternativeSignatures": [],
+ "target": "src/aspnetcore/src/Middleware/WebSockets/test/ConformanceTests/AutobahnTestApp/TestResources/testCert.pfx",
+ "line": 1,
+ "memberOf": [
+ "default"
+ ],
+ "tool": "credscan",
+ "ruleId": "CSCAN-GENERAL0020",
+ "createdDate": "2024-03-25 18:56:33Z",
+ "expirationDate": "2024-09-11 20:34:12Z",
+ "justification": "This error is baselined with an expiration date of 180 days from 2024-03-25 20:34:12Z"
+ },
+ "c6ed8bf76382b72621892d895d0659eb8ed66ef400f5d38506a3b62129b0f60e": {
+ "signature": "c6ed8bf76382b72621892d895d0659eb8ed66ef400f5d38506a3b62129b0f60e",
+ "alternativeSignatures": [
+ "07873a6bbdd04caf121ed279cd4c24e55fb79ae3e86083c413b839d8d5e81cba"
+ ],
+ "target": "src/runtime/src/libraries/Common/tests/System/Net/Prerequisites/Deployment/setup_activedirectory_domaincontroller.ps1",
+ "line": 36,
+ "memberOf": [
+ "default"
+ ],
+ "tool": "psscriptanalyzer",
+ "ruleId": "PSAvoidUsingConvertToSecureStringWithPlainText",
+ "createdDate": "2024-03-25 18:56:33Z",
+ "expirationDate": "2024-09-11 20:34:12Z",
+ "justification": "This error is baselined with an expiration date of 180 days from 2024-03-25 20:34:12Z"
+ },
+ "c511a0f0c15b79748a62ee0198689e7a0c8c2af102730c337823f6cd52b3ec66": {
+ "signature": "c511a0f0c15b79748a62ee0198689e7a0c8c2af102730c337823f6cd52b3ec66",
+ "alternativeSignatures": [
+ "7142b2e2126a0c0e5bf2ad08e9e56d405620fbb9f12dfcd3f90a9dfcc30f8bf5"
+ ],
+ "target": "src/runtime/src/libraries/Common/tests/System/Net/Prerequisites/Deployment/setup_iisserver.ps1",
+ "line": 82,
+ "memberOf": [
+ "default"
+ ],
+ "tool": "psscriptanalyzer",
+ "ruleId": "PSAvoidUsingConvertToSecureStringWithPlainText",
+ "createdDate": "2024-03-25 18:56:33Z",
+ "expirationDate": "2024-09-11 20:34:12Z",
+ "justification": "This error is baselined with an expiration date of 180 days from 2024-03-25 20:34:12Z"
+ },
+ "bfe258b52e19062b9009a68549bff3b2c99a6105f493cbf14332b3366691d446": {
+ "signature": "bfe258b52e19062b9009a68549bff3b2c99a6105f493cbf14332b3366691d446",
+ "alternativeSignatures": [],
+ "target": "src/runtime/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSAKeyPemTests.cs",
+ "line": 54,
+ "memberOf": [
+ "default"
+ ],
+ "tool": "credscan",
+ "ruleId": "CSCAN-GENERAL0020",
+ "createdDate": "2024-03-25 18:56:33Z",
+ "expirationDate": "2024-09-11 20:34:12Z",
+ "justification": "This error is baselined with an expiration date of 180 days from 2024-03-25 20:34:12Z"
+ },
+ "52364f6839cf4bc824f1c82a31f3c7ee1cfb228383b3bee476ef7442526c0de8": {
+ "signature": "52364f6839cf4bc824f1c82a31f3c7ee1cfb228383b3bee476ef7442526c0de8",
+ "alternativeSignatures": [],
+ "target": "src/runtime/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSAKeyPemTests.cs",
+ "line": 358,
+ "memberOf": [
+ "default"
+ ],
+ "tool": "credscan",
+ "ruleId": "CSCAN-GENERAL0020",
+ "createdDate": "2024-03-25 18:56:33Z",
+ "expirationDate": "2024-09-11 20:34:12Z",
+ "justification": "This error is baselined with an expiration date of 180 days from 2024-03-25 20:34:12Z"
+ },
+ "cef12040ed2c91d3bd7eba717e6c4bff8e547cb6d2b40363f1d859b02c873276": {
+ "signature": "cef12040ed2c91d3bd7eba717e6c4bff8e547cb6d2b40363f1d859b02c873276",
+ "alternativeSignatures": [],
+ "target": "src/runtime/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/EC/ECKeyPemTests.cs",
+ "line": 61,
+ "memberOf": [
+ "default"
+ ],
+ "tool": "credscan",
+ "ruleId": "CSCAN-GENERAL0020",
+ "createdDate": "2024-03-25 18:56:33Z",
+ "expirationDate": "2024-09-11 20:34:12Z",
+ "justification": "This error is baselined with an expiration date of 180 days from 2024-03-25 20:34:12Z"
+ },
+ "ab290d190fa6582f2826c9b8524a4013ea67380f65328bc39c31dbcba59ec63c": {
+ "signature": "ab290d190fa6582f2826c9b8524a4013ea67380f65328bc39c31dbcba59ec63c",
+ "alternativeSignatures": [],
+ "target": "src/runtime/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/EC/ECKeyPemTests.cs",
+ "line": 168,
+ "memberOf": [
+ "default"
+ ],
+ "tool": "credscan",
+ "ruleId": "CSCAN-GENERAL0020",
+ "createdDate": "2024-03-25 18:56:33Z",
+ "expirationDate": "2024-09-11 20:34:12Z",
+ "justification": "This error is baselined with an expiration date of 180 days from 2024-03-25 20:34:12Z"
+ },
+ "710963884a4d1e73d1ff4da0db7e1c8e1eeae25fe4a9e3c1de8b5019bb5d9d74": {
+ "signature": "710963884a4d1e73d1ff4da0db7e1c8e1eeae25fe4a9e3c1de8b5019bb5d9d74",
+ "alternativeSignatures": [],
+ "target": "src/runtime/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/EC/ECKeyPemTests.cs",
+ "line": 412,
+ "memberOf": [
+ "default"
+ ],
+ "tool": "credscan",
+ "ruleId": "CSCAN-GENERAL0020",
+ "createdDate": "2024-03-25 18:56:33Z",
+ "expirationDate": "2024-09-11 20:34:12Z",
+ "justification": "This error is baselined with an expiration date of 180 days from 2024-03-25 20:34:12Z"
+ },
+ "db276fb1ea2a8e74e7ab80522362b8f402d361652ca823d7cab59465d038eb82": {
+ "signature": "db276fb1ea2a8e74e7ab80522362b8f402d361652ca823d7cab59465d038eb82",
+ "alternativeSignatures": [],
+ "target": "src/runtime/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/RSAKeyPemTests.cs",
+ "line": 229,
+ "memberOf": [
+ "default"
+ ],
+ "tool": "credscan",
+ "ruleId": "CSCAN-GENERAL0020",
+ "createdDate": "2024-03-25 18:56:33Z",
+ "expirationDate": "2024-09-11 20:34:12Z",
+ "justification": "This error is baselined with an expiration date of 180 days from 2024-03-25 20:34:12Z"
+ },
+ "807277ed6647bcdc3eab2e24d8fbcaca0a6506d2ed455248b0497ceb42831e30": {
+ "signature": "807277ed6647bcdc3eab2e24d8fbcaca0a6506d2ed455248b0497ceb42831e30",
+ "alternativeSignatures": [],
+ "target": "src/runtime/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/RSAKeyPemTests.cs",
+ "line": 328,
+ "memberOf": [
+ "default"
+ ],
+ "tool": "credscan",
+ "ruleId": "CSCAN-GENERAL0020",
+ "createdDate": "2024-03-25 18:56:33Z",
+ "expirationDate": "2024-09-11 20:34:12Z",
+ "justification": "This error is baselined with an expiration date of 180 days from 2024-03-25 20:34:12Z"
+ },
+ "685288ad132baddbcdf7a2201960f6d48805151f1aef2e094c4dbc16841b3e54": {
+ "signature": "685288ad132baddbcdf7a2201960f6d48805151f1aef2e094c4dbc16841b3e54",
+ "alternativeSignatures": [],
+ "target": "src/runtime/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/RSAKeyPemTests.cs",
+ "line": 489,
+ "memberOf": [
+ "default"
+ ],
+ "tool": "credscan",
+ "ruleId": "CSCAN-GENERAL0020",
+ "createdDate": "2024-03-25 18:56:33Z",
+ "expirationDate": "2024-09-11 20:34:12Z",
+ "justification": "This error is baselined with an expiration date of 180 days from 2024-03-25 20:34:12Z"
+ },
+ "e972eb97ea7ea4a9524820036a42557f13a48240b6083c7baa0d465c00adfdee": {
+ "signature": "e972eb97ea7ea4a9524820036a42557f13a48240b6083c7baa0d465c00adfdee",
+ "alternativeSignatures": [],
+ "target": "src/runtime/src/tests/FunctionalTests/Android/Device_Emulator/gRPC/grpc-dotnet/testassets/Certs/InteropTests/server1.key",
+ "line": 1,
+ "memberOf": [
+ "default"
+ ],
+ "tool": "credscan",
+ "ruleId": "CSCAN-GENERAL0020",
+ "createdDate": "2024-03-25 18:56:33Z",
+ "expirationDate": "2024-09-11 20:34:12Z",
+ "justification": "This error is baselined with an expiration date of 180 days from 2024-03-25 20:34:12Z"
+ },
+ "1b2c907630acaff5b6e62eb4895043c82e93d885331f1f46296812634fd30abe": {
+ "signature": "1b2c907630acaff5b6e62eb4895043c82e93d885331f1f46296812634fd30abe",
+ "alternativeSignatures": [],
+ "target": "src/runtime/src/tests/FunctionalTests/Android/Device_Emulator/gRPC/grpc-dotnet/testassets/Certs/InteropTests/server1.pfx",
+ "line": 1,
+ "memberOf": [
+ "default"
+ ],
+ "tool": "credscan",
+ "ruleId": "CSCAN-GENERAL0020",
+ "createdDate": "2024-03-25 18:56:33Z",
+ "expirationDate": "2024-09-11 20:34:12Z",
+ "justification": "This error is baselined with an expiration date of 180 days from 2024-03-25 20:34:12Z"
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/SourceBuild/content/.devcontainer/README.md b/src/SourceBuild/content/.devcontainer/README.md
new file mode 100644
index 000000000000..58f0361719dc
--- /dev/null
+++ b/src/SourceBuild/content/.devcontainer/README.md
@@ -0,0 +1,59 @@
+
+
+This Codespace can help you debug the source build of .NET. This build takes about
+45 minutes and, after completion, produces an archived .NET SDK located in
+`/workspaces/dotnet/artifacts/assets/Release`. In case you selected the `prebuilt-sdk`
+Codespace, the built-from-source SDK will already be there.
+
+## Build the SDK
+
+To build the repository, run one of the following:
+```bash
+# Microsoft based build
+./build.sh
+```
+or
+
+```bash
+# Building from source only
+./prep-source-build.sh && ./build.sh -sb
+```
+
+> Please note that, at this time, the build modifies some of the checked-in sources so it might
+be preferential to rebuild the Codespace between attempts (or reset the working tree changes).
+
+For more details, see the instructions at https://github.com/dotnet/dotnet.
+
+## Synchronize your changes in locally
+
+When debugging the build, you have two options how to test your changes in this environment.
+
+### Making changes to the VMR directly
+
+You can make the changes directly to the local checkout of the VMR at `/workspaces/dotnet`. You
+can then try to build the VMR and see if the change works for you.
+
+### Pull changes into the Codespace from your fork
+
+You can also make a fix in the individual source repository (e.g. `dotnet/runtime`) and push the
+fix into a branch; can be in your fork too. Once you have the commit pushed, you can pull this
+version of the repository into the Codespace by running:
+
+```
+/workspaces/synchronize-vmr.sh \
+ --repository : \
+ --remote :
+```
+
+You can now proceed building the VMR in the Codespace using instructions above. You can repeat
+this process and sync a new commit from your fork. Only note that, at this time, Source-Build
+modifies some of the checked-in sources so you'll need to revert the working tree changes
+between attempts.
diff --git a/src/SourceBuild/content/.devcontainer/devcontainer.json b/src/SourceBuild/content/.devcontainer/devcontainer.json
new file mode 100644
index 000000000000..b0e1db0a471d
--- /dev/null
+++ b/src/SourceBuild/content/.devcontainer/devcontainer.json
@@ -0,0 +1,22 @@
+// Container contains checked-out source code only
+{
+ "name": "Default",
+ "image": "mcr.microsoft.com/dotnet-buildtools/prereqs:fedora-39",
+ "hostRequirements": {
+ // A completely source built .NET is >64 GB with all the repos/artifacts
+ "storage": "128gb"
+ },
+ "customizations": {
+ "vscode": {
+ "extensions": [
+ "ms-dotnettools.csharp"
+ ]
+ },
+ "codespaces": {
+ "openFiles": [
+ ".devcontainer/README.md"
+ ]
+ }
+ },
+ "onCreateCommand": ".devcontainer/init.sh"
+}
diff --git a/src/SourceBuild/content/.devcontainer/init.sh b/src/SourceBuild/content/.devcontainer/init.sh
new file mode 100755
index 000000000000..9fa9e81f7602
--- /dev/null
+++ b/src/SourceBuild/content/.devcontainer/init.sh
@@ -0,0 +1,14 @@
+#!/usr/bin/env bash
+
+set -eux
+
+source="${BASH_SOURCE[0]}"
+script_root="$( cd -P "$( dirname "$source" )" && pwd )"
+
+workspace_dir=$(realpath "$script_root/../../")
+tmp_dir=$(realpath "$workspace_dir/tmp")
+vmr_dir=$(realpath "$workspace_dir/dotnet")
+
+cp "$vmr_dir/.devcontainer/synchronize-vmr.sh" "$workspace_dir"
+
+mkdir -p "$tmp_dir"
diff --git a/src/SourceBuild/content/.devcontainer/prebuilt-sdk/devcontainer.json b/src/SourceBuild/content/.devcontainer/prebuilt-sdk/devcontainer.json
new file mode 100644
index 000000000000..d7d96d1c191d
--- /dev/null
+++ b/src/SourceBuild/content/.devcontainer/prebuilt-sdk/devcontainer.json
@@ -0,0 +1,22 @@
+// Container contains a pre-built SDK
+{
+ "name": "Pre-built .NET SDK",
+ "image": "mcr.microsoft.com/dotnet-buildtools/prereqs:fedora-39",
+ "hostRequirements": {
+ // A completely source built .NET is >64 GB with all the repos/artifacts
+ "storage": "128gb"
+ },
+ "customizations": {
+ "vscode": {
+ "extensions": [
+ "ms-dotnettools.csharp"
+ ]
+ },
+ "codespaces": {
+ "openFiles": [
+ ".devcontainer/README.md"
+ ]
+ }
+ },
+ "onCreateCommand": ".devcontainer/prebuilt-sdk/init.sh"
+}
diff --git a/src/SourceBuild/content/.devcontainer/prebuilt-sdk/init.sh b/src/SourceBuild/content/.devcontainer/prebuilt-sdk/init.sh
new file mode 100644
index 000000000000..517ed62296b5
--- /dev/null
+++ b/src/SourceBuild/content/.devcontainer/prebuilt-sdk/init.sh
@@ -0,0 +1,9 @@
+#!/usr/bin/env bash
+
+source="${BASH_SOURCE[0]}"
+script_root="$( cd -P "$( dirname "$source" )" && pwd )"
+
+"$script_root"/../../prep-source-build.sh
+
+cp "$script_root/../synchronize-vmr.sh" "/workspaces/"
+"$script_root"/../../build.sh --online --clean-while-building || exit 0
diff --git a/src/SourceBuild/content/.devcontainer/synchronize-vmr.sh b/src/SourceBuild/content/.devcontainer/synchronize-vmr.sh
new file mode 100755
index 000000000000..091ae0e26d8d
--- /dev/null
+++ b/src/SourceBuild/content/.devcontainer/synchronize-vmr.sh
@@ -0,0 +1,4 @@
+#!/bin/bash
+
+(cd /workspaces/dotnet/src/installer \
+ && ./eng/vmr-sync.sh --vmr /workspaces/dotnet --tmp /workspaces/tmp $*)
diff --git a/src/SourceBuild/content/.gitignore b/src/SourceBuild/content/.gitignore
new file mode 100644
index 000000000000..1f77e38b1215
--- /dev/null
+++ b/src/SourceBuild/content/.gitignore
@@ -0,0 +1,6 @@
+/.dotnet
+/.packages
+/artifacts
+/prereqs/packages
+/src/nuget-client/NuGet.config
+*.binlog
diff --git a/src/SourceBuild/content/.sscignore b/src/SourceBuild/content/.sscignore
new file mode 100644
index 000000000000..47012eb58d6d
--- /dev/null
+++ b/src/SourceBuild/content/.sscignore
@@ -0,0 +1,6 @@
+{
+ "cfs" : [
+ "CFS0001",
+ "CFS0013"
+ ]
+}
diff --git a/src/SourceBuild/content/CODE_OF_CONDUCT.md b/src/SourceBuild/content/CODE_OF_CONDUCT.md
new file mode 100644
index 000000000000..775f221c98e1
--- /dev/null
+++ b/src/SourceBuild/content/CODE_OF_CONDUCT.md
@@ -0,0 +1,6 @@
+# Code of Conduct
+
+This project has adopted the code of conduct defined by the Contributor Covenant
+to clarify expected behavior in our community.
+
+For more information, see the [.NET Foundation Code of Conduct](https://dotnetfoundation.org/code-of-conduct).
diff --git a/src/SourceBuild/content/CONTRIBUTING.md b/src/SourceBuild/content/CONTRIBUTING.md
new file mode 100644
index 000000000000..1b23b661bc52
--- /dev/null
+++ b/src/SourceBuild/content/CONTRIBUTING.md
@@ -0,0 +1,7 @@
+Contributing
+============
+
+See [dotnet/runtime](./src/runtime/CONTRIBUTING.md) for general contribution guidelines such as allowed licenses.
+
+At this time, the VMR will not accept any changes and is a read-only mirror of the development repositories only.
+Please, make the changes in the respective development repositories (e.g., [dotnet/runtime](https://github.com/dotnet/runtime) or [dotnet/sdk](https://github.com/dotnet/sdk)) and they will get synchronized into the VMR automatically.
\ No newline at end of file
diff --git a/src/SourceBuild/content/Directory.Build.props b/src/SourceBuild/content/Directory.Build.props
new file mode 100644
index 000000000000..2a1adc7aca7b
--- /dev/null
+++ b/src/SourceBuild/content/Directory.Build.props
@@ -0,0 +1,192 @@
+
+
+
+ Release
+ true
+ dotnet
+
+
+
+ linux
+ osx
+ freebsd
+ netbsd
+ illumos
+ solaris
+ haiku
+ windows
+ $(BuildOS)
+ $(TargetOS)
+
+
+
+
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+
+ true
+
+ runtime
+
+
+
+
+
+ $([System.Runtime.InteropServices.RuntimeInformation]::ProcessArchitecture.ToString().ToLowerInvariant)
+
+ $(BuildArchitecture)
+
+ $(TargetArchitecture)
+
+ AnyCPU
+
+
+
+
+ $(__DistroRid)
+ $([System.Runtime.InteropServices.RuntimeInformation]::RuntimeIdentifier)
+ win-$([System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture.ToString().ToLowerInvariant)
+
+ $(BuildRid.Substring(0, $(BuildRid.LastIndexOf('-'))))-$(TargetArchitecture)
+ $(TargetRid)
+
+
+ false
+ true
+
+ $(__PortableTargetOS)-$(TargetArchitecture)
+ freebsd-$(TargetArchitecture)
+ osx-$(TargetArchitecture)
+ linux-$(TargetArchitecture)
+ linux-musl-$(TargetArchitecture)
+ win-$(TargetArchitecture)
+
+ $(PortableRid)
+ $(TargetOS)-$(TargetArchitecture)
+ win-$(TargetArchitecture)
+
+
+
+
+
+ $([MSBuild]::NormalizeDirectory('$(MSBuildThisFileDirectory)', '.packages'))
+ $(RestorePackagesPath)
+
+
+
+
+
+
+
+ $([MSBuild]::NormalizeDirectory('$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildProjectDirectory), 'global.json'))'))
+
+ $([MSBuild]::NormalizeDirectory('$(RepoRoot)', 'artifacts'))
+ $([MSBuild]::NormalizeDirectory('$(ArtifactsDir)', 'obj'))
+ $([MSBuild]::NormalizeDirectory('$(ArtifactsDir)', 'bin'))
+ $([MSBuild]::NormalizeDirectory('$(RepoRoot)', 'eng'))
+
+
+ $(Platform)
+ $(MSBuildProjectName)
+
+ $([System.IO.Path]::GetFullPath('$(ArtifactsBinDir)$(OutDirName)\'))
+ $(BaseOutputPath)$(Configuration)\
+ $(BaseOutputPath)$(PlatformName)\$(Configuration)\
+
+ $([System.IO.Path]::GetFullPath('$(ArtifactsObjDir)$(OutDirName)\'))
+ $(BaseIntermediateOutputPath)$(Configuration)\
+ $(BaseIntermediateOutputPath)$(PlatformName)\$(Configuration)\
+
+
+ net9.0
+
+
+
+
+
+
+
+ false
+
+ false
+
+ true
+
+ true
+
+ true
+
+
+
+ minimal
+
+ .cmd
+ .sh
+
+
+ .zip
+ .tar.gz
+
+
+
+ $([MSBuild]::NormalizeDirectory('$(RepoRoot)', 'src'))
+ $([MSBuild]::NormalizeDirectory('$(RepositoryEngineeringDir)', 'tools'))
+ $([MSBuild]::NormalizeDirectory('$(ToolsDir)', 'tasks'))
+ $([MSBuild]::NormalizeDirectory('$(RepoRoot)', 'repo-projects'))
+
+ $([MSBuild]::NormalizeDirectory('$(RepoRoot)', 'prereqs'))
+ $([MSBuild]::NormalizeDirectory('$(PrereqsDir)', 'keys'))
+ $([MSBuild]::NormalizeDirectory('$(PrereqsDir)', 'packages'))
+
+
+ $([MSBuild]::NormalizeDirectory('$(NuGetPackageRoot)', 'BootstrapPackages'))
+ $(NuGetPackageRoot)
+
+ $([MSBuild]::NormalizeDirectory('$(ArtifactsDir)', 'toolset', 'VSSdkResolvers'))
+ $([MSBuild]::NormalizeDirectory('$(ArtifactsObjDir)', 'Symbols'))
+ $([MSBuild]::NormalizeDirectory('$(ArtifactsObjDir)', 'AssetManifests'))
+ $([MSBuild]::NormalizeDirectory('$(ArtifactsDir)', 'assets', '$(Configuration)'))
+
+ $([MSBuild]::NormalizeDirectory('$(PrereqsPackagesDir)', 'prebuilt'))
+ $([MSBuild]::NormalizeDirectory('$(PrereqsPackagesDir)', 'previouslyRestored'))
+ $([MSBuild]::NormalizeDirectory('$(PrereqsPackagesDir)', 'previously-source-built'))
+ $([MSBuild]::EnsureTrailingSlash('$(CustomPrebuiltSourceBuiltPackagesPath)'))
+
+ $([MSBuild]::NormalizeDirectory('$(PrereqsDir)', 'git-info'))
+
+ $([MSBuild]::NormalizeDirectory('$(ArtifactsDir)', 'prebuilt-report'))
+ $([MSBuild]::NormalizeDirectory('$(PackageReportDir)', 'prebuilt-packages'))
+
+ $([MSBuild]::NormalizeDirectory('$(PrereqsPackagesDir)', 'reference'))
+ Private.SourceBuilt.Artifacts
+ Private.SourceBuilt.Prebuilts
+
+ $(ToolsDir)prebuilt-baseline.xml
+
+
+
+
+ $([MSBuild]::NormalizePath('$(ArtifactsBinDir)', 'Microsoft.DotNet.UnifiedBuild.Tasks', '$(Configuration)', 'Microsoft.DotNet.UnifiedBuild.Tasks.dll'))
+ $([MSBuild]::NormalizePath('$(ArtifactsBinDir)', 'Microsoft.DotNet.SourceBuild.Tasks.LeakDetection', '$(Configuration)', 'Microsoft.DotNet.SourceBuild.Tasks.LeakDetection.dll'))
+
+
+
+ .prebuilt.xml
+ $(PackageReportDir)poison-catalog.xml
+ $(PackageReportDir)poisoned.txt
+ $(PackageReportDir)poison-usage.xml
+
+
+
diff --git a/src/SourceBuild/content/Directory.Build.rsp b/src/SourceBuild/content/Directory.Build.rsp
new file mode 100644
index 000000000000..34e4c4fdded4
--- /dev/null
+++ b/src/SourceBuild/content/Directory.Build.rsp
@@ -0,0 +1 @@
+-tl:false
\ No newline at end of file
diff --git a/src/SourceBuild/content/Directory.Build.targets b/src/SourceBuild/content/Directory.Build.targets
new file mode 100644
index 000000000000..637d03e5fb14
--- /dev/null
+++ b/src/SourceBuild/content/Directory.Build.targets
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+ dotnet-sdk-
+
+
+
+
+
+
+
+
+ %(SdkTarballItem.Filename)%(SdkTarballItem.Extension)
+ %(SdkTarballItem.Identity)
+ $(SdkFilename.Replace('$(SdkFilenamePrefix)','').Replace('-$(TargetRid)$(ArchiveExtension)',''))
+
+
+
+
\ No newline at end of file
diff --git a/src/SourceBuild/content/Directory.Packages.props b/src/SourceBuild/content/Directory.Packages.props
new file mode 100644
index 000000000000..e9ab9c0e0e47
--- /dev/null
+++ b/src/SourceBuild/content/Directory.Packages.props
@@ -0,0 +1,25 @@
+
+
+
+ true
+
+ $(NoWarn);NU1507
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/SourceBuild/content/LICENSE.TXT b/src/SourceBuild/content/LICENSE.TXT
new file mode 100644
index 000000000000..a616ed188dfc
--- /dev/null
+++ b/src/SourceBuild/content/LICENSE.TXT
@@ -0,0 +1,23 @@
+The MIT License (MIT)
+
+Copyright (c) .NET Foundation and Contributors
+
+All rights reserved.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
\ No newline at end of file
diff --git a/src/SourceBuild/content/NuGet.config b/src/SourceBuild/content/NuGet.config
new file mode 100644
index 000000000000..5225fd440ff3
--- /dev/null
+++ b/src/SourceBuild/content/NuGet.config
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/SourceBuild/content/README.md b/src/SourceBuild/content/README.md
new file mode 100644
index 000000000000..a1082deb026d
--- /dev/null
+++ b/src/SourceBuild/content/README.md
@@ -0,0 +1,205 @@
+# dotnet/dotnet - Home of the .NET VMR
+
+This repository is a **Virtual Monolithic Repository (VMR)** which includes all the source code and infrastructure needed to build the .NET SDK.
+
+What this means:
+- **Monolithic** - a join of multiple repositories that make up the whole product, such as [dotnet/runtime](https://github.com/dotnet/runtime) or [dotnet/sdk](https://github.com/dotnet/sdk).
+- **Virtual** - a mirror (not replacement) of product repos where sources from those repositories are synchronized into.
+
+In the VMR, you can find:
+- source files of [each product repository](#list-of-components) which are mirrored inside of their respective directories under [`src/`](https://github.com/dotnet/dotnet/tree/main/src),
+- tooling that enables [building the whole .NET product from source](https://github.com/dotnet/source-build) on Linux platforms,
+- small customizations, in the form of [patches](https://github.com/dotnet/dotnet/tree/main/src/installer/src/SourceBuild/patches), applied on top of the original code to make the build possible,
+- *[in future]* E2E tests for the whole .NET product.
+
+Just like the development repositories, the VMR will have a release branch for every feature band (e.g. `release/8.0.1xx`).
+Similarly, VMR's `main` branch will follow `main` branches of product repositories (see [Synchronization Based on Declared Dependencies](src/arcade/Documentation/UnifiedBuild/VMR-Design-And-Operation.md#synchronization-based-on-declared-dependencies)).
+
+More in-depth documentation about the VMR can be found in [VMR Design And Operation](src/arcade/Documentation/UnifiedBuild/VMR-Design-And-Operation.md#layout).
+See also [dotnet/source-build](https://github.com/dotnet/source-build) for more information about our whole-product source-build.
+
+## Goals
+
+- The main purpose of the [dotnet/dotnet](https://github.com/dotnet/dotnet) repository is to have all source code necessary to build the .NET product available in one repository and identified by a single commit.
+- The VMR also aims to become the place from which we release and service future versions of .NET to reduce the complexity of the product construction process. This should allow our partners and and 3rd parties to easily build, test and modify .NET using their custom infrastructure as well as make the process available to the community.
+- Lastly, we hope to solve other problems that the current multi-repo setup brings:
+ - Enable the standard [down-/up-stream open-source model](src/arcade/Documentation/UnifiedBuild/VMR-Upstream-Downstream.md).
+ - Fulfill requirements of .NET distro builders such as RedHat or Canonical to natively include .NET in their distribution repositories.
+ - Simplify scenarios such as client-run testing of bug fixes and improvements. The build should work in an offline environment too for certain platforms.
+ - Enable developers to make and test changes spanning multiple repositories.
+ - More efficient pipeline for security fixes during the CVE pre-disclosure process.
+
+We will achieve these goals while keeping active coding work in the separate repos where it happens today. For example: ASP.NET features will continue to be developed in `dotnet/aspnetcore` and CLR features will be continue to be developed in `dotnet/runtime`. Each of these repos have their own distinct communities and processes, and aggregating development into a true mono-repo would work against that. Hence, the "virtual" monolithic repo: the VMR gives us the simplicity of a mono-repo for building and servicing the product, while active development of components of that product stays in its various existing repos. The day to day experience for typical contributors will not change.
+
+## Limitations
+
+**This is a work-in-progress.**
+There are considerable limitations to what is possible at the moment. For an extensive list of current limitations, please see [Temporary Mechanics](src/arcade/Documentation/UnifiedBuild/VMR-Design-And-Operation.md#temporary-mechanics).
+See the [Unified Build roadmap](src/arcade/Documentation/UnifiedBuild/Roadmap.md) for more details.
+
+### Supported platforms
+
+* 8.0
+ * source-build configuration on Linux
+* 9.0+ (WIP)
+ * source-build configuration on Linux
+ * non-source-build configuration on Linux, Mac, and Windows
+
+For the latest information about Source-Build support for new .NET versions, please check our [GitHub Discussions page](https://github.com/dotnet/source-build/discussions) for announcements.
+
+### Code flow
+For the time being, the source code only flows one way - from the development repos into the VMR.
+More details on this process:
+
+- [Source Synchronization Process](src/arcade/Documentation/UnifiedBuild/VMR-Design-And-Operation.md#source-synchronization-process)
+- [Synchronization Based on Declared Dependencies](src/arcade/Documentation/UnifiedBuild/VMR-Design-And-Operation.md#synchronization-based-on-declared-dependencies)
+- [Moving Code and Dependencies between the VMR and Development Repos](src/arcade/Documentation/UnifiedBuild/VMR-Design-And-Operation.md#moving-code-and-dependencies-between-the-vmr-and-development-repos)
+
+We expect the code flow to start working both ways in the .NET 9 timeframe.
+See the [Unified Build roadmap](src/arcade/Documentation/UnifiedBuild/Roadmap.md) for more details.
+
+### Contribution
+
+At this time, the VMR will not accept any changes and is a read-only mirror of the development repositories only.
+Please, make the changes in the respective development repositories (e.g., [dotnet/runtime](https://github.com/dotnet/runtime) or [dotnet/sdk](https://github.com/dotnet/sdk)) and they will get synchronized into the VMR automatically.
+
+## Dev instructions
+
+Please note that **this repository is a work-in-progress** and there are some usability issues connected to this.
+These can be nuisances such as some checked-in files getting modified by the build itself and similar.
+For the latest information about Source-Build support, please watch for announcements posted on our [GitHub Discussions page](https://github.com/dotnet/source-build/discussions).
+
+### Prerequisites
+
+The dependencies for building can be found [here](https://github.com/dotnet/runtime/blob/main/docs/workflow/requirements/).
+In case you don't want to / cannot prepare your environment per the requirements, consider [using Docker](#building-using-docker).
+
+### Building
+
+1. **Clone the repository**
+
+ ```bash
+ git clone https://github.com/dotnet/dotnet dotnet-dotnet
+ cd dotnet-dotnet
+ ```
+
+2. **Build the .NET SDK**
+
+ Choose one of the following build modes:
+
+ - **Microsoft based build**
+
+ For Unix:
+ ```bash
+ ./build.sh --clean-while-building
+ ```
+
+ For Windows:
+ ```cmd
+ .\build.cmd -cleanWhileBuilding
+ ```
+
+ - **Building from source**
+ ```bash
+ # Prep the source to build on your distro.
+ # This downloads a .NET SDK and a number of .NET packages needed to build .NET from source.
+ ./prep-source-build.sh
+
+ # Build the .NET SDK
+ ./build.sh -sb --clean-while-building
+ ```
+
+ The resulting SDK is placed at `artifacts/assets/Release/dotnet-sdk-9.0.100-[your-RID].tar.gz` (for Unix) or `artifacts/assets/Release/dotnet-sdk-9.0.100-[your-RID].zip` (for Windows).
+
+4. *(Optional)* **Unpack and install the .NET SDK**
+
+ For Unix:
+ ```bash
+ mkdir -p $HOME/dotnet
+ tar zxf artifacts/assets/Release/dotnet-sdk-9.0.100-[your-RID].tar.gz -C $HOME/dotnet
+ ln -s $HOME/dotnet/dotnet /usr/bin/dotnet
+ ```
+
+ For Windows:
+ ```cmd
+ mkdir %userprofile%\dotnet
+ tar -xf artifacts/assets/Release/dotnet-sdk-9.0.100-[your RID].zip -C %userprofile%\dotnet
+ set "PATH=%userprofile%\dotnet;%PATH%"
+ ```
+
+ To test your built SDK, run the following:
+
+ ```bash
+ dotnet --info
+ ```
+
+> [!NOTE]
+> Run `./build.sh --help` (for Unix) or `.\build.cmd -help` (for Windows) to see more information about supported build options.
+
+### Building using Docker
+
+You can also build the repository using a Docker image which has the required prerequisites inside.
+The example below creates a Docker volume named `vmr` and clones and builds the VMR there.
+
+```sh
+docker run --rm -it -v vmr:/vmr -w /vmr mcr.microsoft.com/dotnet-buildtools/prereqs:centos-stream8
+git clone https://github.com/dotnet/dotnet .
+
+# - Microsoft based build
+./build.sh --clean-while-building
+
+# - Building from source
+./prep-source-build.sh && ./build.sh -sb --clean-while-building
+
+mkdir -p $HOME/.dotnet
+tar -zxf artifacts/assets/Release/dotnet-sdk-9.0.100-centos.8-x64.tar.gz -C $HOME/.dotnet
+ln -s $HOME/.dotnet/dotnet /usr/bin/dotnet
+```
+
+### Codespaces
+
+You can also utilize [GitHub Codespaces](https://github.com/features/codespaces) where you can find preset containers in this repository.
+
+### Building from released sources
+
+You can also build from sources (and not from a context of a git repository), such as the ones you can acquire from a [dotnet/dotnet release](https://github.com/dotnet/dotnet/releases).
+In this case, you need to provide additional information which includes the original repository and commit hash the code was built from so that the SDK can provide a better debugging experience (think the `Step into..` functionality).
+Usually, this means the [dotnet/dotnet repository](https://github.com/dotnet/dotnet) together with the commit the release tag is connected to.
+
+In practice, this means that when calling the main build script, you need to provide additional arguments when building outside of a context of a git repository.
+Alternatively, you can also provide a manifest file where this information can be read from. This file (`release.json`) can be found attached with the [dotnet/dotnet release](https://github.com/dotnet/dotnet/releases).
+
+### Synchronizing code into the VMR
+
+Sometimes you want to make a change in a repository and test that change in the VMR. You could of course make the change in the VMR directly (locally, as the VMR is read-only for now) but in case it's already available in your repository, you can synchronize it into the VMR (again locally).
+
+To do this, you can either start a [dotnet/dotnet](https://github.com/dotnet/dotnet) Codespace - you will see instructions right after it starts. Alternatively, you can clone the repository locally and use the [vmr-sync.sh](src/installer/eng/vmr-sync.sh) or [vmr-sync.ps1](src/installer/eng/vmr-sync.ps1) script to pull your changes in. Please refer to the documentation in the script for more details.
+
+## List of components
+
+The full list of components synchronized into the VMR is [here (Components.md)](./Components.md).
+
+The repository also contains a [JSON manifest](https://github.com/dotnet/dotnet/blob/main/src/source-manifest.json) listing all components in a machine-readable format.
+
+## Filing Issues
+
+This repo does not accept issues as of now. Please file issues to the appropriate development repos.
+For issues with the VMR itself, please use the [source-build repository](https://github.com/dotnet/source-build).
+
+## Useful Links
+
+- Design documentation for the VMR - a set of documents describing the high-level design and the why's and how's
+ - [Design and Operation](src/arcade/Documentation/UnifiedBuild/VMR-Design-And-Operation.md)
+ - [Upstream/Downstream Relationships](src/arcade/Documentation/UnifiedBuild/VMR-Upstream-Downstream.md)
+ - [Code and Build Workflow](src/arcade/Documentation/UnifiedBuild/VMR-Code-And-Build-Workflow.md)
+ - [Strategy for Managing External Source Dependencies](src/arcade/Documentation/UnifiedBuild/VMR-Strategy-For-External-Source.md)
+- [.NET Source-Build](https://github.com/dotnet/source-build)
+- [What is .NET](https://dotnet.microsoft.com)
+
+## .NET Foundation
+
+.NET Runtime is a [.NET Foundation](https://www.dotnetfoundation.org/projects) project.
+
+## License
+
+.NET is licensed under the [MIT](LICENSE.TXT) license.
diff --git a/src/SourceBuild/content/SECURITY.md b/src/SourceBuild/content/SECURITY.md
new file mode 100644
index 000000000000..e0dfff56a956
--- /dev/null
+++ b/src/SourceBuild/content/SECURITY.md
@@ -0,0 +1,41 @@
+
+
+## Security
+
+Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/).
+
+If you believe you have found a security vulnerability in any Microsoft-owned repository that meets Microsoft's [Microsoft's definition of a security vulnerability](https://docs.microsoft.com/en-us/previous-versions/tn-archive/cc751383(v=technet.10)) of a security vulnerability, please report it to us as described below.
+
+## Reporting Security Issues
+
+**Please do not report security vulnerabilities through public GitHub issues.**
+
+Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://msrc.microsoft.com/create-report).
+
+If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the the [Microsoft Security Response Center PGP Key page](https://www.microsoft.com/en-us/msrc/pgp-key-msrc).
+
+You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://www.microsoft.com/msrc).
+
+Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue:
+
+ * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.)
+ * Full paths of source file(s) related to the manifestation of the issue
+ * The location of the affected source code (tag/branch/commit or direct URL)
+ * Any special configuration required to reproduce the issue
+ * Step-by-step instructions to reproduce the issue
+ * Proof-of-concept or exploit code (if possible)
+ * Impact of the issue, including how an attacker might exploit the issue
+
+This information will help us triage your report more quickly.
+
+If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://microsoft.com/msrc/bounty) page for more details about our active programs.
+
+## Preferred Languages
+
+We prefer all communications to be in English.
+
+## Policy
+
+Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://www.microsoft.com/en-us/msrc/cvd).
+
+
diff --git a/src/SourceBuild/content/build.cmd b/src/SourceBuild/content/build.cmd
new file mode 100644
index 000000000000..c70d2b3757e4
--- /dev/null
+++ b/src/SourceBuild/content/build.cmd
@@ -0,0 +1,9 @@
+@echo off
+setlocal
+
+set _args=%*
+if "%~1"=="-?" set _args=-help
+if "%~1"=="/?" set _args=-help
+
+powershell -ExecutionPolicy ByPass -NoProfile -Command "& '%~dp0eng\build.ps1'" %_args%
+exit /b %ERRORLEVEL%
\ No newline at end of file
diff --git a/src/SourceBuild/content/build.proj b/src/SourceBuild/content/build.proj
new file mode 100644
index 000000000000..bbcfd977b039
--- /dev/null
+++ b/src/SourceBuild/content/build.proj
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+
+
+
+ source-build
+ non-source-build
+
+
+
+
+
+
+
+
+
+
+ $(ArtifactsDir)VerticalManifest.xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/SourceBuild/content/build.sh b/src/SourceBuild/content/build.sh
new file mode 100755
index 000000000000..91555c83321a
--- /dev/null
+++ b/src/SourceBuild/content/build.sh
@@ -0,0 +1,408 @@
+#!/usr/bin/env bash
+
+# Stop script if unbound variable found (use ${var:-} if intentional)
+set -u
+
+# Stop script if command returns non-zero exit code.
+# Prevents hidden errors caused by missing error code propagation.
+set -e
+
+usage()
+{
+ echo "Common settings:"
+ echo " --binaryLog Create MSBuild binary log (short: -bl)"
+ echo " --configuration Build configuration: 'Debug' or 'Release' (short: -c)"
+ echo " --verbosity Msbuild verbosity: q[uiet], m[inimal], n[ormal], d[etailed], and diag[nostic] (short: -v)"
+ echo ""
+
+ echo "Actions:"
+ echo " --clean Clean the solution"
+ echo " --help Print help and exit (short: -h)"
+ echo " --test Run tests (short: -t)"
+ echo ""
+
+ echo "Source-only settings:"
+ echo " --source-only, --source-build Source-build the solution (short: -so, -sb)"
+ echo " --online Build using online sources"
+ echo " --poison Build with poisoning checks"
+ echo " --release-manifest A JSON file, an alternative source of Source Link metadata"
+ echo " --source-repository Source Link repository URL, required when building from tarball"
+ echo " --source-version Source Link revision, required when building from tarball"
+ echo " --with-packages Use the specified directory of previously-built packages"
+ echo " --with-sdk Use the SDK in the specified directory for bootstrapping"
+ echo ""
+
+ echo "Advanced settings:"
+ echo " --build-repo-tests Build repository tests. May not be supported with --source-only"
+ echo " --ci Set when running on CI server"
+ echo " --clean-while-building Cleans each repo after building (reduces disk space usage, short: -cwb)"
+ echo " --excludeCIBinarylog Don't output binary log (short: -nobl)"
+ echo " --prepareMachine Prepare machine for CI run, clean up processes after build"
+ echo " --use-mono-runtime Output uses the mono runtime"
+ echo ""
+ echo "Command line arguments not listed above are passed thru to msbuild."
+ echo "Arguments can also be passed in with a single hyphen."
+}
+
+source="${BASH_SOURCE[0]}"
+
+# resolve $source until the file is no longer a symlink
+while [[ -h "$source" ]]; do
+ scriptroot="$( cd -P "$( dirname "$source" )" && pwd )"
+ source="$(readlink "$source")"
+ # if $source was a relative symlink, we need to resolve it relative to the path where the
+ # symlink file was located
+ [[ $source != /* ]] && source="$scriptroot/$source"
+done
+scriptroot="$( cd -P "$( dirname "$source" )" && pwd )"
+
+packagesRestoredDir="$scriptroot/.packages/"
+
+# Common settings
+binary_log=false
+configuration='Release'
+verbosity='minimal'
+
+# Actions
+clean=false
+test=false
+
+# Source-only settings
+sourceOnly=false
+releaseManifest=''
+sourceRepository=''
+sourceVersion=''
+CUSTOM_PACKAGES_DIR=''
+CUSTOM_SDK_DIR=''
+packagesDir="$scriptroot/prereqs/packages/"
+packagesArchiveDir="${packagesDir}archive/"
+packagesPreviouslySourceBuiltDir="${packagesDir}previously-source-built/"
+
+# Advanced settings
+ci=false
+exclude_ci_binary_log=false
+prepare_machine=false
+
+properties=''
+while [[ $# > 0 ]]; do
+ opt="$(echo "${1/#--/-}" | tr "[:upper:]" "[:lower:]")"
+ case "$opt" in
+ # Common settings
+ -binarylog|-bl)
+ binary_log=true
+ ;;
+ -configuration|-c)
+ configuration=$2
+ shift
+ ;;
+ -verbosity|-v)
+ verbosity=$2
+ shift
+ ;;
+
+ # Actions
+ -clean)
+ clean=true
+ ;;
+ -help|-h|-\?|/?)
+ usage
+ exit 0
+ ;;
+ -test|-t)
+ test=true
+ ;;
+
+ # Source-only settings
+ -source-only|-source-build|-so|-sb)
+ sourceOnly=true
+ properties="$properties /p:DotNetBuildSourceOnly=true"
+ ;;
+ -online)
+ properties="$properties /p:DotNetBuildWithOnlineFeeds=true"
+ ;;
+ -poison)
+ properties="$properties /p:EnablePoison=true"
+ ;;
+ -release-manifest)
+ releaseManifest="$2"
+ shift
+ ;;
+ -source-repository)
+ sourceRepository="$2"
+ shift
+ ;;
+ -source-version)
+ sourceVersion="$2"
+ shift
+ ;;
+ -with-packages)
+ CUSTOM_PACKAGES_DIR="$(cd -P "$2" && pwd)"
+ if [ ! -d "$CUSTOM_PACKAGES_DIR" ]; then
+ echo "Custom prviously built packages directory '$CUSTOM_PACKAGES_DIR' does not exist"
+ exit 1
+ fi
+ shift
+ ;;
+ -with-sdk)
+ CUSTOM_SDK_DIR="$(cd -P "$2" && pwd)"
+ if [ ! -d "$CUSTOM_SDK_DIR" ]; then
+ echo "Custom SDK directory '$CUSTOM_SDK_DIR' does not exist"
+ exit 1
+ fi
+ if [ ! -x "$CUSTOM_SDK_DIR/dotnet" ]; then
+ echo "Custom SDK '$CUSTOM_SDK_DIR/dotnet' does not exist or is not executable"
+ exit 1
+ fi
+ shift
+ ;;
+
+ # Advanced settings
+ -build-tests)
+ properties="$properties /p:DotNetBuildTests=true"
+ ;;
+ -ci)
+ ci=true
+ ;;
+ -clean-while-building|-cwb)
+ properties="$properties /p:CleanWhileBuilding=true"
+ ;;
+ -excludecibinarylog|-nobl)
+ exclude_ci_binary_log=true
+ ;;
+ -preparemachine)
+ prepare_machine=true
+ ;;
+ -use-mono-runtime)
+ properties="$properties /p:SourceBuildUseMonoRuntime=true"
+ ;;
+ *)
+ properties="$properties $1"
+ ;;
+ esac
+
+ shift
+done
+
+if [[ "$ci" == true ]]; then
+ if [[ "$exclude_ci_binary_log" == false ]]; then
+ binary_log=true
+ fi
+fi
+
+# Never use the global nuget cache folder
+use_global_nuget_cache=false
+
+. "$scriptroot/eng/common/tools.sh"
+
+project="$scriptroot/build.proj"
+targets="/t:Build"
+
+# This repo uses the VSTest integration instead of the Arcade Test target
+if [[ "$test" == true ]]; then
+ project="$scriptroot/test/tests.proj"
+ targets="$targets;VSTest"
+fi
+
+function Build {
+ if [[ "$sourceOnly" != "true" ]]; then
+
+ InitializeToolset
+
+ # Manually unset NUGET_PACKAGES as InitializeToolset sets it unconditionally.
+ # The env var shouldn't be set so that the RestorePackagesPath msbuild property is respected.
+ unset NUGET_PACKAGES
+
+ local bl=""
+ if [[ "$binary_log" == true ]]; then
+ bl="/bl:\"$log_dir/Build.binlog\""
+ fi
+
+ MSBuild --restore \
+ $project \
+ $targets \
+ $bl \
+ /p:Configuration=$configuration \
+ $properties
+
+ ExitWithExitCode 0
+
+ else
+
+ if [ "$ci" == "true" ]; then
+ properties="$properties /p:ContinuousIntegrationBuild=true"
+ fi
+
+ if [ "$test" != "true" ]; then
+ "$CLI_ROOT/dotnet" build-server shutdown
+ "$CLI_ROOT/dotnet" msbuild "$scriptroot/eng/tools/init-build.proj" -bl:"$scriptroot/artifacts/log/$configuration/BuildMSBuildSdkResolver.binlog" -flp:LogFile="$scriptroot/artifacts/log/$configuration/BuildMSBuildSdkResolver.log" /t:ExtractToolsetPackages,BuildMSBuildSdkResolver $properties
+ # kill off the MSBuild server so that on future invocations we pick up our custom SDK Resolver
+ "$CLI_ROOT/dotnet" build-server shutdown
+ fi
+
+ # Point MSBuild to the custom SDK resolvers folder, so it will pick up our custom SDK Resolver
+ export MSBUILDADDITIONALSDKRESOLVERSFOLDER="$scriptroot/artifacts/toolset/VSSdkResolvers/"
+
+ local bl=""
+ if [[ "$binary_log" == true ]]; then
+ bl="/bl:\"$log_dir/Build.binlog\""
+ fi
+
+ "$CLI_ROOT/dotnet" msbuild --restore "$project" $bl $targets $properties
+ fi
+}
+
+if [[ "$clean" == true ]]; then
+ if [ -d "$artifacts_dir" ]; then
+ rm -rf $artifacts_dir
+ echo "Artifacts directory deleted."
+ fi
+ exit 0
+fi
+
+# Initialize __DistroRid and __PortableTargetOS
+source $scriptroot/eng/common/native/init-os-and-arch.sh
+source $scriptroot/eng/common/native/init-distro-rid.sh
+initDistroRidGlobal "$os" "$arch" ""
+
+# Source-only settings
+if [[ "$sourceOnly" == "true" ]]; then
+ # For build purposes, we need to make sure we have all the SourceLink information
+ if [ "$test" != "true" ]; then
+ GIT_DIR="$scriptroot/.git"
+ if [ -f "$GIT_DIR/index" ]; then # We check for index because if outside of git, we create config and HEAD manually
+ if [ -n "$sourceRepository" ] || [ -n "$sourceVersion" ] || [ -n "$releaseManifest" ]; then
+ echo "ERROR: Source Link arguments cannot be used in a git repository"
+ exit 1
+ fi
+ else
+ if [ -z "$releaseManifest" ]; then
+ if [ -z "$sourceRepository" ] || [ -z "$sourceVersion" ]; then
+ echo "ERROR: $scriptroot is not a git repository, either --release-manifest or --source-repository and --source-version must be specified"
+ exit 1
+ fi
+ else
+ if [ -n "$sourceRepository" ] || [ -n "$sourceVersion" ]; then
+ echo "ERROR: --release-manifest cannot be specified together with --source-repository and --source-version"
+ exit 1
+ fi
+
+ get_property() {
+ local json_file_path="$1"
+ local property_name="$2"
+ grep -oP '(?<="'$property_name'": ")[^"]*' "$json_file_path"
+ }
+
+ sourceRepository=$(get_property "$releaseManifest" sourceRepository) \
+ || (echo "ERROR: Failed to find sourceRepository in $releaseManifest" && exit 1)
+ sourceVersion=$(get_property "$releaseManifest" sourceVersion) \
+ || (echo "ERROR: Failed to find sourceVersion in $releaseManifest" && exit 1)
+
+ if [ -z "$sourceRepository" ] || [ -z "$sourceVersion" ]; then
+ echo "ERROR: sourceRepository and sourceVersion must be specified in $releaseManifest"
+ exit 1
+ fi
+ fi
+
+ # We need to add "fake" .git/ files when not building from a git repository
+ mkdir -p "$GIT_DIR"
+ echo '[remote "origin"]' > "$GIT_DIR/config"
+ echo "url=\"$sourceRepository\"" >> "$GIT_DIR/config"
+ echo "$sourceVersion" > "$GIT_DIR/HEAD"
+ fi
+ fi
+
+ # Support custom source built package locations
+ if [ "$CUSTOM_PACKAGES_DIR" != "" ]; then
+ if [ "$test" == "true" ]; then
+ properties="$properties /p:CustomSourceBuiltPackagesPath=$CUSTOM_PACKAGES_DIR"
+ else
+ properties="$properties /p:CustomPrebuiltSourceBuiltPackagesPath=$CUSTOM_PACKAGES_DIR"
+ fi
+ fi
+
+ if [ ! -d "$scriptroot/.git" ]; then
+ echo "ERROR: $scriptroot is not a git repository."
+ exit 1
+ fi
+
+ # Allow a custom SDK directory to be specified
+ if [ -d "$CUSTOM_SDK_DIR" ]; then
+ export SDK_VERSION=$("$CUSTOM_SDK_DIR/dotnet" --version)
+ export CLI_ROOT="$CUSTOM_SDK_DIR"
+ export _InitializeDotNetCli="$CLI_ROOT/dotnet"
+ export DOTNET_INSTALL_DIR="$CLI_ROOT"
+ echo "Using custom bootstrap SDK from '$CLI_ROOT', version '$SDK_VERSION'"
+ else
+ sdkLine=$(grep -m 1 'dotnet' "$scriptroot/global.json")
+ sdkPattern="\"dotnet\" *: *\"(.*)\""
+ if [[ $sdkLine =~ $sdkPattern ]]; then
+ export SDK_VERSION=${BASH_REMATCH[1]}
+ export CLI_ROOT="$scriptroot/.dotnet"
+ fi
+ fi
+
+ # Find the Arcade SDK version and set env vars for the msbuild sdk resolver
+ packageVersionsPath=''
+
+ if [[ "$CUSTOM_PACKAGES_DIR" != "" && -f "$CUSTOM_PACKAGES_DIR/PackageVersions.props" ]]; then
+ packageVersionsPath="$CUSTOM_PACKAGES_DIR/PackageVersions.props"
+ elif [ -d "$packagesArchiveDir" ]; then
+ sourceBuiltArchive=$(find "$packagesArchiveDir" -maxdepth 1 -name 'Private.SourceBuilt.Artifacts*.tar.gz')
+ if [ -f "${packagesPreviouslySourceBuiltDir}}PackageVersions.props" ]; then
+ packageVersionsPath=${packagesPreviouslySourceBuiltDir}PackageVersions.props
+ elif [ -f "$sourceBuiltArchive" ]; then
+ tar -xzf "$sourceBuiltArchive" -C /tmp PackageVersions.props
+ packageVersionsPath=/tmp/PackageVersions.props
+ fi
+ fi
+
+ if [ ! -f "$packageVersionsPath" ]; then
+ echo "Cannot find PackagesVersions.props. Debugging info:"
+ echo " Attempted archive path: $packagesArchiveDir"
+ echo " Attempted custom PVP path: $CUSTOM_PACKAGES_DIR/PackageVersions.props"
+ exit 1
+ fi
+
+ # Extract toolset packages
+
+ # Ensure that by default, the bootstrap version of the toolset SDK is used. Source-build infra
+ # projects use bootstrap toolset SDKs, and would fail to find it in the build. The repo
+ # projects overwrite this so that they use the source-built toolset SDK instad.
+
+ # 1. Microsoft.DotNet.Arcade.Sdk
+ arcadeSdkLine=$(grep -m 1 'MicrosoftDotNetArcadeSdkVersion' "$packageVersionsPath")
+ arcadeSdkPattern="(.*)"
+ if [[ $arcadeSdkLine =~ $arcadeSdkPattern ]]; then
+ export ARCADE_BOOTSTRAP_VERSION=${BASH_REMATCH[1]}
+
+ export SOURCE_BUILT_SDK_ID_ARCADE=Microsoft.DotNet.Arcade.Sdk
+ export SOURCE_BUILT_SDK_VERSION_ARCADE=$ARCADE_BOOTSTRAP_VERSION
+ export SOURCE_BUILT_SDK_DIR_ARCADE=$packagesRestoredDir/BootstrapPackages/microsoft.dotnet.arcade.sdk/$ARCADE_BOOTSTRAP_VERSION
+ fi
+
+ # 2. Microsoft.Build.NoTargets
+ notargetsSdkLine=$(grep -m 1 'Microsoft.Build.NoTargets' "$scriptroot/global.json")
+ notargetsSdkPattern="\"Microsoft\.Build\.NoTargets\" *: *\"(.*)\""
+ if [[ $notargetsSdkLine =~ $notargetsSdkPattern ]]; then
+ export NOTARGETS_BOOTSTRAP_VERSION=${BASH_REMATCH[1]}
+
+ export SOURCE_BUILT_SDK_ID_NOTARGETS=Microsoft.Build.NoTargets
+ export SOURCE_BUILT_SDK_VERSION_NOTARGETS=$NOTARGETS_BOOTSTRAP_VERSION
+ export SOURCE_BUILT_SDK_DIR_NOTARGETS=$packagesRestoredDir/BootstrapPackages/microsoft.build.notargets/$NOTARGETS_BOOTSTRAP_VERSION
+ fi
+
+ # 3. Microsoft.Build.Traversal
+ traversalSdkLine=$(grep -m 1 'Microsoft.Build.Traversal' "$scriptroot/global.json")
+ traversalSdkPattern="\"Microsoft\.Build\.Traversal\" *: *\"(.*)\""
+ if [[ $traversalSdkLine =~ $traversalSdkPattern ]]; then
+ export TRAVERSAL_BOOTSTRAP_VERSION=${BASH_REMATCH[1]}
+
+ export SOURCE_BUILT_SDK_ID_TRAVERSAL=Microsoft.Build.Traversal
+ export SOURCE_BUILT_SDK_VERSION_TRAVERSAL=$TRAVERSAL_BOOTSTRAP_VERSION
+ export SOURCE_BUILT_SDK_DIR_TRAVERSAL=$packagesRestoredDir/BootstrapPackages/microsoft.build.traversal/$TRAVERSAL_BOOTSTRAP_VERSION
+ fi
+
+ echo "Found bootstrap versions: SDK $SDK_VERSION, Arcade $ARCADE_BOOTSTRAP_VERSION, NoTargets $NOTARGETS_BOOTSTRAP_VERSION and Traversal $TRAVERSAL_BOOTSTRAP_VERSION"
+fi
+
+Build
diff --git a/src/SourceBuild/content/docs/license-scanning.md b/src/SourceBuild/content/docs/license-scanning.md
new file mode 100644
index 000000000000..6007776abcc4
--- /dev/null
+++ b/src/SourceBuild/content/docs/license-scanning.md
@@ -0,0 +1,24 @@
+# License Scanning
+
+The VMR is regularly scanned for license references to ensure that only open-source license are used where relevant.
+
+License scanning pipline: https://dev.azure.com/dnceng/internal/_build?definitionId=1301 (internal only)
+
+License scanning test: https://github.com/dotnet/dotnet/blob/main/test/Microsoft.DotNet.SourceBuild.SmokeTests/LicenseScanTests.cs
+
+By default, running the pipeline will scan all repos within the VMR which takes several hours to run.
+The pipeline can be triggered manually to target a specific repo within the VMR by setting the `specificRepoName` parameter.
+This value should be the name of the repo within the VMR (i.e. a name of a directory within https://github.com/dotnet/dotnet/tree/main/src).
+To test source modifications intended to resolve a license issue, apply the change in an internal branch of the VMR.
+Run this pipeline, targeting your branch, and set the `specificRepoName` parameter to the name of the repo containing the change.
+
+The output of the pipeline is a set of test results and logs.
+The logs are published as an artifact and can be found at test/Microsoft.DotNet/SourceBuild.SmokeTests/bin/Release/netX.0/logs.
+It consists of the following:
+ * `UpdatedLicenses..json`: This is the output of that gets compared to the stored baseline.
+ If they're the same, the test passes; if not, it fails. By comparing this file to the baseline, one can determine which new license
+ references have been introduced.
+ If everything is deemed to be acceptable, the developer can either update the allowed licenses, update the exclusions file, update the
+ baseline, or any combination.
+ * `scancode-results.json`: This is the raw output that comes from scancode. This file is useful for diagnostic purposes because it tells you
+ the exact line number of where a license has been detected in a file.
diff --git a/src/SourceBuild/content/eng/Build.props b/src/SourceBuild/content/eng/Build.props
new file mode 100644
index 000000000000..cb6ad7cc660c
--- /dev/null
+++ b/src/SourceBuild/content/eng/Build.props
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/src/SourceBuild/content/eng/Version.Details.xml b/src/SourceBuild/content/eng/Version.Details.xml
new file mode 100644
index 000000000000..b433058ab68f
--- /dev/null
+++ b/src/SourceBuild/content/eng/Version.Details.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+ https://github.com/dotnet/arcade
+ 689fb2d1b620a9df28c3384d9b208e3654a0f928
+
+
+
diff --git a/src/SourceBuild/content/eng/Versions.props b/src/SourceBuild/content/eng/Versions.props
new file mode 100644
index 000000000000..32d0f49c4c83
--- /dev/null
+++ b/src/SourceBuild/content/eng/Versions.props
@@ -0,0 +1,39 @@
+
+
+
+ 0.1.0
+ alpha.1
+ true
+
+
+
+ 9.0.100-preview.4.24179.1
+ 9.0.100-preview.4.24179.1
+
+ 2.0.0-beta4.24126.1
+
+ 17.8.3
+
+ 8.0.0
+ 8.0.0
+ 8.0.0
+
+ 13.0.3
+
+
diff --git a/src/SourceBuild/content/eng/allowed-sb-binaries.txt b/src/SourceBuild/content/eng/allowed-sb-binaries.txt
new file mode 100644
index 000000000000..a90f99dee185
--- /dev/null
+++ b/src/SourceBuild/content/eng/allowed-sb-binaries.txt
@@ -0,0 +1,59 @@
+# Contains the binaries that are allowed for source build
+
+# This file is used by the Binary Tool to remove binaries from the VMR
+# If importing a file, include the relative path to the file
+
+**/*.bmp
+**/*.doc
+**/*.docx
+**/*.gif
+**/*.ico
+**/*.jpg
+**/*.JPG
+**/*.pdf
+**/*.png
+**/*.PNG
+**/*.rtf
+**/*.snk
+**/*.vsd
+**/*.vsdx
+**/*.xlsx
+**/*.ttf
+**/*.cur
+**/*.icm
+**/*.reg
+
+# aspnetcore
+src/aspnetcore/src/**/samples/**/*.eot
+src/aspnetcore/src/**/samples/**/*.pfx
+src/aspnetcore/src/**/samples/**/*.woff*
+src/aspnetcore/src/**/Samples/**/*.woff*
+src/aspnetcore/src/Components/benchmarkapps/BlazingPizza.Server/wwwroot/css/font/quicksand-v8-latin-*.woff*
+src/aspnetcore/src/Components/Web.JS/dist/Release/blazor.*.js # JavaScript files with a null bytes
+src/aspnetcore/src/ProjectTemplates/Web.ProjectTemplates/**/app.db
+src/aspnetcore/src/submodules/Node-Externals/cache/**/* # https://github.com/dotnet/source-build/issues/4161
+
+# fsharp
+src/fsharp/src/fsi/fsi.res # Icon
+
+# razor
+src/razor/**/SampleApp/**/fonts/**/*.eot
+src/razor/**/SampleApp/**/fonts/**/*.otf
+src/razor/**/SampleApp/**/fonts/**/*.woff
+
+# roslyn
+src/roslyn/src/ExpressionEvaluator/Core/Source/ExpressionCompiler/Resources/WindowsProxy.winmd
+
+# runtime
+src/runtime/src/libraries/System.Diagnostics.EventLog/src/Messages/EventLogMessages.res # Icon
+src/runtime/src/libraries/System.Speech/src/**/*.upsmap # https://github.com/dotnet/runtime/issues/81692
+src/runtime/src/libraries/System.Text.Encoding.CodePages/src/Data/codepages.nlp # https://github.com/dotnet/runtime/issues/81693
+src/runtime/src/native/external/brotli/common/dictionary.bin.br
+
+# source-build-externals
+src/source-build-externals/src/humanizer/src/Humanizer.Tests**/*.pfx
+src/source-build-externals/src/newtonsoft-json/Src/Newtonsoft.Json.Tests/SpaceShipV2.bson
+src/source-build-externals/src/azure-activedirectory-identitymodel-extensions-for-dotnet/build/strongNameBypass.reg # UTF16-LE text file
+src/source-build-externals/src/azure-activedirectory-identitymodel-extensions-for-dotnet/build/strongNameBypass2.reg # UTF16-LE text file
+src/source-build-externals/src/azure-activedirectory-identitymodel-extensions-for-dotnet/test/Certs/*.pfx
+src/source-build-externals/src/azure-activedirectory-identitymodel-extensions-for-dotnet/test/Certs/*.cer
\ No newline at end of file
diff --git a/src/SourceBuild/content/eng/allowed-vmr-binaries.txt b/src/SourceBuild/content/eng/allowed-vmr-binaries.txt
new file mode 100644
index 000000000000..83284c283ad2
--- /dev/null
+++ b/src/SourceBuild/content/eng/allowed-vmr-binaries.txt
@@ -0,0 +1,143 @@
+# Contains the binaries that are allowed in the VMR
+
+# This file is used by the Binary Tool to detect new binaries that get added to the VMR
+
+# Import the allowed souce-build binaries (a stricter set).
+import:allowed-sb-binaries.txt
+
+**/testCert*.pfx
+**/TestCert*.pfx
+
+# arcade
+src/arcade/src/Microsoft.DotNet.*.Tests/**/*
+src/arcade/src/Microsoft.DotNet.NuGetRepack/tests/Resources/*.nupkg
+src/arcade/src/Microsoft.DotNet.NuGetRepack/tests/Resources/.signature.p7s
+
+# aspnetcore
+src/aspnetcore/src/submodules/MessagePack-CSharp/**/*.dll
+src/aspnetcore/src/SignalR/clients/java/signalr/gradle/wrapper/gradle-wrapper.jar
+src/aspnetcore/src/Components/**/testassets/**/*.woff*
+src/aspnetcore/src/Security/Authentication/Negotiate/test/Negotiate.FunctionalTest/negotiateAuthCert.pfx
+src/aspnetcore/src/**/test/**/*.cer
+src/aspnetcore/src/Shared/TestCertificates/*.pfx
+src/aspnetcore/src/Shared/TestCertificates/*.crt
+
+# cecil
+src/cecil/Test/Resources/assemblies/*.netmodule
+src/cecil/Test/Resources/assemblies/*.winmd
+src/cecil/Test/Resources/assemblies/*.exe
+src/cecil/Test/Resources/assemblies/*.dll
+src/cecil/Test/Resources/assemblies/*.pdb
+src/cecil/Test/Resources/assemblies/*.mdb
+src/cecil/rocks/Test/Resources/assemblies/*.dll
+src/cecil/symbols/**/Test/Resources/assemblies/*.exe
+src/cecil/symbols/**/Test/Resources/assemblies/*.pdb
+src/cecil/symbols/**/Test/Resources/assemblies/*.dll
+src/cecil/symbols/**/Test/Resources/assemblies/*.mdb
+
+# fsharp
+src/fsharp/tests/**/*.resources
+src/fsharp/tests/**/*.dll
+src/fsharp/tests/**/*.exe
+src/fsharp/tests/fsharp/core/resources/chimes.wav
+
+# msbuild
+src/msbuild/src/Tasks.UnitTests/TestResources/*.pfx
+src/msbuild/src/Tasks.UnitTests/AssemblyDependency/CacheFileSamples/Microsoft.VisualStudio.LanguageServices.Implementation.csprojAssemblyReference.cache
+
+# nuget-client
+src/nuget-client/test/EndToEnd/Packages/**/*.nupkg
+src/nuget-client/test/EndToEnd/Packages/**/*.zip
+src/nuget-client/test/EndToEnd/Packages/**/*.dll
+src/nuget-client/test/NuGet.Clients.Tests/NuGet.CommandLine.Test/compiler/resources/*.nupkg
+src/nuget-client/test/NuGet.Clients.Tests/NuGet.PackageManagement.UI.Test/Resources/customMetadata.jpeg
+src/nuget-client/test/NuGet.Core.Tests/NuGet.Commands.Test/compiler/resources/EmptyCertificateStore.p7b
+src/nuget-client/test/NuGet.Core.Tests/NuGet.Protocol.Tests/compiler/resources/*.dll
+src/nuget-client/test/NuGet.Core.FuncTests/NuGet.Packaging.FuncTest/compiler/resources/*.nupkg
+src/nuget-client/test/NuGet.Core.FuncTests/NuGet.Packaging.FuncTest/compiler/resources/*.zip
+src/nuget-client/test/NuGet.Core.FuncTests/Dotnet.Integration.Test/compiler/resources/*.nupkg
+src/nuget-client/test/TestUtilities/Test.Utility/compiler/resources/*.crt
+src/nuget-client/test/TestUtilities/Test.Utility/compiler/resources/.signature.p7s
+src/nuget-client/test/TestUtilities/Test.Utility/compiler/resources/*.nupkg
+src/nuget-client/test/TestUtilities/Test.Utility/compiler/resources/*.zip
+
+# razor
+src/razor/src/Razor/test/Microsoft.VisualStudio.Razor.IntegrationTests/TestFiles/BlazorProject.zip
+src/razor/src/Razor/benchmarks/Microsoft.AspNetCore.Razor.Microbenchmarks/Resources/project.razor.bin
+
+# roslyn
+src/roslyn/src/Compilers/Test/Resources/Core/**/*.metadata
+src/roslyn/src/Compilers/Test/Resources/Core/**/*.winmd
+src/roslyn/src/Compilers/Test/Resources/Core/**/*.mod
+src/roslyn/src/Compilers/Test/Resources/Core/**/*.netmodule
+src/roslyn/src/Compilers/Test/Resources/Core/**/*.obj
+src/roslyn/src/Compilers/Test/Resources/Core/**/*.dll
+src/roslyn/src/Compilers/Test/Resources/Core/**/*.exe
+src/roslyn/src/Compilers/Test/Resources/Core/**/*.Dll
+src/roslyn/src/Workspaces/MSBuildTest/Resources/Dlls/*.dll
+src/roslyn/**/CodeAnalysisTest/**/*.res
+src/roslyn/**/CodeAnalysisTest/**/*.blah
+src/roslyn/**/CodeAnalysisTest/**/*.RES
+
+# runtime
+src/runtime/src/mono/mono/eglib/test/*.txt
+src/runtime/src/mono/mono/tests/exiting/*.out
+src/runtime/src/**/tests/**/*.res
+src/runtime/src/**/tests/**/*.resources
+src/runtime/src/tests/FunctionalTests/Android/Device_Emulator/gRPC/grpc-dotnet/testassets/Certs/InteropTests/server1.pfx
+src/runtime/src/tests/FunctionalTests/Android/Device_Emulator/AOT_PROFILED/*.mibc
+src/runtime/src/tests/FunctionalTests/Android/Device_Emulator/AOT_PROFILED/*.nettrace
+src/runtime/src/libraries/System.Runtime.Serialization.Xml/tests/Canonicalization/baselines/ReaderWriter_C14N_BaselineXML_Binary.xml
+src/runtime/src/libraries/System.Reflection.Metadata/tests/Resources/NetModule/*.mod
+src/runtime/src/libraries/System.Reflection.Metadata/tests/Resources/WinRT/Lib.winmd
+src/runtime/src/libraries/System.Reflection.Metadata/tests/Resources/Misc/CPPClassLibrary2.obj
+src/runtime/src/libraries/System.Console/tests/TestData/ncursesFormats/s/screen-256color
+src/runtime/src/libraries/System.Console/tests/TestData/ncursesFormats/x/xterm
+src/runtime/src/mono/wasm/testassets/**/*.dat
+src/runtime/src/mono/wasm/testassets/**/*.o
+src/runtime/src/libraries/**/tests/**/*.dll
+src/runtime/src/libraries/**/tests/**/*.exe
+src/runtime/src/libraries/**/tests/**/*.pdb
+
+# sdk
+src/sdk/src/Assets/TestProjects/**/*.dat
+src/sdk/src/Assets/TestProjects/**/*.cache
+src/sdk/src/Assets/TestProjects/**/*.tlb
+src/sdk/src/Assets/TestPackages/dotnet-new/nupkg_templates/*
+src/sdk/test/Microsoft.DotNet.ShellShim.Tests/WpfBinaryTestAsssets/*.dll
+src/sdk/test/Microsoft.NET.Sdk.Publish.Tasks.Tests/Resources/*.zip
+
+# source-build-externals
+src/source-build-externals/src/application-insights*/WEB/Src/WindowsServer/WindowsServer.Tests/**/*.dll
+
+# symreader
+src/symreader/src/PdbTestResources/Resources/*
+
+# templating
+src/templating/test/Microsoft.TemplateEngine.TestTemplates/nupkg_templates/*.nupkg
+
+# test-templates
+src/test-templates/Templates/**/*.nupkg
+
+# vstest
+src/vstest/samples/Microsoft.TestPlatform.*/Adapter/Microsoft.VisualStudio.TestPlatform.*.dll
+src/vstest/temp/cpp/**/*.dll
+src/vstest/test/Microsoft.TestPlatform.CoreUtilities.UnitTests/TestAssets/*.exe
+src/vstest/test/Microsoft.TestPlatform.CoreUtilities.UnitTests/TestAssets/dotnetMac*
+src/vstest/test/Microsoft.TestPlatform.Utilities.UnitTests/TestFiles/fullcovered.coverage
+src/vstest/test/TestAssets/LegacySettingsUnitTestProject/DependencyAssembly/DependencyAssemblyForTest.dll
+
+# winforms
+src/winforms/src/System.Windows.Forms/tests/IntegrationTests/MauiTests/lib/*.dll
+src/winforms/src/System.Windows.Forms/tests/IntegrationTests/WinformsControlsTest/Resources/media.mpg
+src/winforms/src/System.Windows.Forms/tests/UnitTests/bitmaps/milkmateya01.emf
+src/winforms/src/System.Windows.Forms/tests/UnitTests/TestResources/VB6/SimpleControl.vb6
+src/winforms/src/System.Windows.Forms*/**/*.wmf
+src/winforms/src/System.Windows.Forms.Design/src/Resources/colordlg.data
+
+# wpf
+src/wpf/src/Microsoft.DotNet.Wpf/src/ReachFramework/Resources/generated/*.resources
+src/wpf/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Resources/Hyphenation/Hyphen_en.lex
+src/wpf/src/Microsoft.DotNet.Wpf/src/PresentationFramework/Resources/Hyphenation/Hyphen_en.hdict
+src/wpf/src/Microsoft.DotNet.Wpf/src/Shared/Tracing/resources/*.BIN
+src/wpf/src/Microsoft.DotNet.Wpf/src/Shared/Tracing/resources/*.bin
diff --git a/src/SourceBuild/content/eng/bootstrap/OverrideBootstrapVersions.props b/src/SourceBuild/content/eng/bootstrap/OverrideBootstrapVersions.props
new file mode 100644
index 000000000000..4da9c2172a88
--- /dev/null
+++ b/src/SourceBuild/content/eng/bootstrap/OverrideBootstrapVersions.props
@@ -0,0 +1,9 @@
+
+
+
+ 7.0.4-servicing.23107.6
+
+ $(NonshippingRuntimeVersionFor700)
+
+
diff --git a/src/SourceBuild/content/eng/bootstrap/buildBootstrapPreviouslySB.csproj b/src/SourceBuild/content/eng/bootstrap/buildBootstrapPreviouslySB.csproj
new file mode 100644
index 000000000000..f81507fe7a98
--- /dev/null
+++ b/src/SourceBuild/content/eng/bootstrap/buildBootstrapPreviouslySB.csproj
@@ -0,0 +1,193 @@
+
+
+
+
+
+
+ net7.0
+ $(MSBuildProjectDirectory)/artifacts/
+ $(MSBuildProjectDirectory)/artifacts/restoredPkgs/
+ $(MSBuildProjectDirectory)/artifacts/unpacked/
+ $(ArchiveDir)Private.SourceBuilt.Artifacts.Bootstrap.tar.gz
+
+
+
+
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ %(UnixRid.Identity)
+
+
+
+
+ %(UnixRid.Identity)
+
+
+
+
+
+
+ %(RuntimePackWithUnixRid.Identity).%(RuntimePackWithUnixRid.UnixRid)
+
+
+
+
+ %(PortablePackageWithUnixRid.Identity)
+
+
+
+
+ runtime.%(PortablePackageWithUnixRid.UnixRid).%(PortablePackageWithUnixRid.Identity)
+
+
+ runtime.%(PortablePackageWithUnixRid.UnixRid).runtime.native.%(PortablePackageWithUnixRid.Identity)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ @(SourceFileName->'%(Filename)')
+ $(DestinationFileName.Replace('$(PackageName.ToLower()).',''))
+
+
+
+
+
+ $([System.String]::concat('%3C','$(PackageName)','Version','%3E').Replace('.',''))
+ $([System.String]::concat('%3C','$(PackageName)','PackageVersion','%3E').Replace('.',''))
+ $(DestinationPath)PackageVersions.props
+
+
+
+
diff --git a/src/SourceBuild/content/eng/build.ps1 b/src/SourceBuild/content/eng/build.ps1
new file mode 100644
index 000000000000..90ec7afb474e
--- /dev/null
+++ b/src/SourceBuild/content/eng/build.ps1
@@ -0,0 +1,112 @@
+[CmdletBinding(PositionalBinding=$false)]
+Param(
+ # Common settings
+ [switch][Alias('bl')]$binaryLog,
+ [string][Alias('c')]$configuration = "Release",
+ [string][Alias('v')]$verbosity = "minimal",
+
+ # Actions
+ [switch]$clean,
+ [switch][Alias('h')]$help,
+ [switch][Alias('t')]$test,
+
+ # Advanced settings
+ [switch]$buildRepoTests,
+ [switch]$ci,
+ [switch][Alias('cwb')]$cleanWhileBuilding,
+ [switch][Alias('nobl')]$excludeCIBinarylog,
+ [switch] $prepareMachine,
+ [Parameter(ValueFromRemainingArguments=$true)][String[]]$properties
+)
+
+function Get-Usage() {
+ Write-Host "Common settings:"
+ Write-Host " -binaryLog Output binary log (short: -bl)"
+ Write-Host " -configuration Build configuration: 'Debug' or 'Release' (short: -c). [Default: Release]"
+ Write-Host " -verbosity Msbuild verbosity: q[uiet], m[inimal], n[ormal], d[etailed], and diag[nostic] (short: -v)"
+ Write-Host ""
+
+ Write-Host "Actions:"
+ Write-Host " -clean Clean the solution"
+ Write-Host " -help Print help and exit (short: -h)"
+ Write-Host " -test Run tests (repo tests omitted by default) (short: -t)"
+ Write-Host ""
+
+ Write-Host "Advanced settings:"
+ Write-Host " -buildRepoTests Build repository tests"
+ Write-Host " -ci Set when running on CI server"
+ Write-Host " -cleanWhileBuilding Cleans each repo after building (reduces disk space usage, short: -cwb)"
+ Write-Host " -excludeCIBinarylog Don't output binary log (short: -nobl)"
+ Write-Host " -prepareMachine Prepare machine for CI run, clean up processes after build"
+ Write-Host ""
+}
+
+$useGlobalNuGetCache=$false
+
+. $PSScriptRoot\common\tools.ps1
+
+if ($help) {
+ Get-Usage
+ exit 0
+}
+
+$project = Join-Path $RepoRoot "build.proj"
+$arguments = @()
+$targets = "/t:Build"
+
+# This repo uses the VSTest integration instead of the Arcade Test target
+if ($test) {
+ $project = Join-Path (Join-Path $RepoRoot "test") "tests.proj"
+ $targets += ";VSTest"
+}
+
+if ($buildRepoTests) {
+ $arguments += "/p:DotNetBuildTests=true"
+}
+
+if ($cleanWhileBuilding) {
+ $arguments += "/p:CleanWhileBuilding=true"
+}
+
+function Build {
+ InitializeToolset
+
+ # Manually unset NUGET_PACKAGES as InitializeToolset sets it unconditionally.
+ # The env var shouldn't be set so that the RestorePackagesPath msbuild property is respected.
+ $env:NUGET_PACKAGES=''
+
+ $bl = if ($binaryLog) { '/bl:' + (Join-Path $LogDir 'Build.binlog') } else { '' }
+
+ MSBuild -restore `
+ $project `
+ $bl `
+ $targets `
+ /p:Configuration=$configuration `
+ @properties `
+ @arguments
+}
+
+try {
+ if ($clean) {
+ if (Test-Path $ArtifactsDir) {
+ Remove-Item -Recurse -Force $ArtifactsDir
+ Write-Host 'Artifacts directory deleted.'
+ }
+ exit 0
+ }
+
+ if ($ci) {
+ if (-not $excludeCIBinarylog) {
+ $binaryLog = $true
+ }
+ }
+
+ Build
+}
+catch {
+ Write-Host $_.ScriptStackTrace
+ Write-PipelineTelemetryError -Category 'Build' -Message $_
+ ExitWithExitCode 1
+}
+
+ExitWithExitCode 0
diff --git a/src/SourceBuild/content/eng/build.sourcebuild.targets b/src/SourceBuild/content/eng/build.sourcebuild.targets
new file mode 100644
index 000000000000..ca13c113a9bb
--- /dev/null
+++ b/src/SourceBuild/content/eng/build.sourcebuild.targets
@@ -0,0 +1,165 @@
+
+
+
+
+
+
+
+
+
+
+ $(ArtifactsAssetsDir)dotnet-symbols-all-$(SourceBuiltSdkVersion)-$(TargetRid)$(ArchiveExtension)
+ $(ArtifactsAssetsDir)dotnet-symbols-sdk-$(SourceBuiltSdkVersion)-$(TargetRid)$(ArchiveExtension)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ $(BaseIntermediateOutputPath)SdkSymbols
+ $(BaseIntermediateOutputPath)Sdk
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ $(PackageReportDir)PrebuiltBurndownData.csv
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ $(ArtifactsAssetsDir)$(SourceBuiltPrebuiltsTarballName).$(SourceBuiltSdkVersion).$(TargetRid)$(ArchiveExtension)
+ $(ResultingPrebuiltPackagesDir)
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/SourceBuild/content/eng/detect-binaries.sh b/src/SourceBuild/content/eng/detect-binaries.sh
new file mode 100755
index 000000000000..59c78dd71e2c
--- /dev/null
+++ b/src/SourceBuild/content/eng/detect-binaries.sh
@@ -0,0 +1,139 @@
+#!/usr/bin/env bash
+
+### Usage: $0
+###
+### Prepares and runs the binary tooling to detect binaries in the VMR. Default behavior is to report any binaries
+### not found in the allowed-binaries file. To remove binaries not specified in the allowed-binaries file, pass --clean.
+###
+### Options:
+### --clean Clean the VMR of binaries not in the specified allowed-binaries file.
+### --allowed-binaries-file Path to the file containing the list of binaries to be
+### ignored for either cleaning or validating.
+### Defaults to eng/allowed-vmr-binaries.txt.
+### --log-level Set the log level for the binary tooling. Defaults to Debug.
+### --with-packages Use the specified directory as the packages source feed.
+### Defaults to online dotnet-public and dotnet-libraries feeds.
+### --with-sdk Use the specified directory as the dotnet SDK.
+### Defaults to .dotnet.
+
+set -euo pipefail
+IFS=$'\n\t'
+
+source="${BASH_SOURCE[0]}"
+REPO_ROOT="$( cd -P "$( dirname "$0" )/../" && pwd )"
+BINARY_TOOL="$REPO_ROOT/eng/tools/BinaryToolKit"
+
+function print_help () {
+ sed -n '/^### /,/^$/p' "$source" | cut -b 5-
+}
+
+defaultDotnetSdk="$REPO_ROOT/.dotnet"
+defaultAllowedBinariesFile="$REPO_ROOT/eng/allowed-vmr-binaries.txt"
+
+# Set default values
+allowedBinariesFile=$defaultAllowedBinariesFile
+mode='validate'
+logLevel='Debug'
+propsDir=''
+packagesDir=''
+dotnetSdk=$defaultDotnetSdk
+
+positional_args=()
+while :; do
+ if [ $# -le 0 ]; then
+ break
+ fi
+ lowerI="$(echo "$1" | awk '{print tolower($0)}')"
+ case $lowerI in
+ "-?"|-h|--help)
+ print_help
+ exit 0
+ ;;
+ --clean)
+ mode="clean"
+ ;;
+ --allowed-binaries-file)
+ allowedBinariesFile=$2
+ shift
+ ;;
+ --log-level)
+ logLevel=$2
+ shift
+ ;;
+ --with-packages)
+ packagesDir=$2
+ if [ ! -d "$packagesDir" ]; then
+ echo "ERROR: The specified packages directory does not exist."
+ exit 1
+ elif [ ! -f "$packagesDir/PackageVersions.props" ]; then
+ echo "ERROR: The specified packages directory does not contain PackageVersions.props."
+ exit 1
+ fi
+ shift
+ ;;
+ --with-sdk)
+ dotnetSdk=$2
+ if [ ! -d "$dotnetSdk" ]; then
+ echo "Custom SDK directory '$dotnetSdk' does not exist"
+ exit 1
+ fi
+ if [ ! -x "$dotnetSdk/dotnet" ]; then
+ echo "Custom SDK '$dotnetSdk/dotnet' does not exist or is not executable"
+ exit 1
+ fi
+ shift
+ ;;
+ *)
+ positional_args+=("$1")
+ ;;
+ esac
+
+ shift
+done
+
+function ParseBinaryArgs
+{
+ # Check allowed binaries file
+ if [ ! -f "$allowedBinariesFile" ]; then
+ echo "ERROR: The specified allowed-binaries file does not exist."
+ exit 1
+ fi
+
+ # Check dotnet sdk
+ if [ "$dotnetSdk" == "$defaultDotnetSdk" ]; then
+ if [ ! -d "$dotnetSdk" ]; then
+ . "$REPO_ROOT/eng/common/tools.sh"
+ InitializeDotNetCli true
+ fi
+ else if [ ! -x "$dotnetSdk/dotnet" ]; then
+ echo "'$dotnetSdk/dotnet' does not exist or is not executable"
+ exit 1
+ fi
+ fi
+
+ # Check the packages directory
+ if [ -z "$packagesDir" ]; then
+ # Use dotnet-public and dotnet-libraries feeds as the default packages source feeds
+ export ARTIFACTS_PATH="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public/nuget/v3/index.json;https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-libraries/nuget/v3/index.json"
+ else
+ packagesDir=$(realpath ${packagesDir})
+ export ARTIFACTS_PATH=$packagesDir
+ fi
+}
+
+function RunBinaryTool
+{
+ targetDir="$REPO_ROOT"
+ outputDir="$REPO_ROOT/artifacts/log/binary-report"
+ BinaryToolCommand=""$dotnetSdk/dotnet" run --project "$BINARY_TOOL" -c Release "$mode" "$targetDir" -o "$outputDir" -ab "$allowedBinariesFile" -l "$logLevel""
+
+ if [ -n "$packagesDir" ]; then
+ BinaryToolCommand=""$BinaryToolCommand" -p CustomPackageVersionsProps="$packagesDir/PackageVersions.props""
+ fi
+
+ # Run the Binary Tool
+ eval "$BinaryToolCommand"
+}
+
+ParseBinaryArgs
+RunBinaryTool
\ No newline at end of file
diff --git a/src/SourceBuild/content/eng/pipelines/ci.yml b/src/SourceBuild/content/eng/pipelines/ci.yml
new file mode 100644
index 000000000000..59ce02278571
--- /dev/null
+++ b/src/SourceBuild/content/eng/pipelines/ci.yml
@@ -0,0 +1,91 @@
+# This yml is used by these pipelines and triggers:
+# NOTE: the triggers are defined in the Azure DevOps UI as they are too complex
+#
+# - dotnet-source-build (public)
+# https://dev.azure.com/dnceng-public/public/_build?definitionId=240
+# - PR: ultralite build
+# - CI: release/* only, every batched commit, full build
+# - Schedule: main only, full build
+#
+# - dotnet-unified-build (public)
+# https://dev.azure.com/dnceng-public/public/_build?definitionId=278
+# - PR: lite build
+# - CI: release/* only, every batched commit, full build
+# - Schedule: main only, full build
+#
+# - dotnet-source-build (internal)
+# https://dev.azure.com/dnceng/internal/_build?definitionId=1219
+# - PR: ultralite build
+# - CI: release/* and internal/release/* only, every batched commit, full build
+# - Schedule: main only, full build
+#
+# - dotnet-source-build-lite (internal)
+# https://dev.azure.com/dnceng/internal/_build?definitionId=1299
+# - PR: release/* and main, lite build, on-demand trigger
+# - CI: main only, every batched commit, lite build
+#
+# - dotnet-unified-build (internal)
+# https://dev.azure.com/dnceng/internal/_build?definitionId=1330
+# - PR: lite build
+# - CI: release/*, internal/release/* and main, every batched commit, full build
+
+variables:
+# enable source-only build for pipelines that contain the -source-build string
+- name: isSourceOnlyBuild
+ value: ${{ contains(variables['Build.DefinitionName'], '-source-build') }}
+
+- name: isSourceOnlyBuildLite
+ value: ${{ contains(variables['Build.DefinitionName'], '-source-build-lite') }}
+
+- name: isScheduleTrigger
+ value: ${{ eq(variables['Build.Reason'], 'Schedule') }}
+
+- name: isPRTrigger
+ value: ${{ eq(variables['Build.Reason'], 'PullRequest') }}
+
+- template: /eng/common/templates-official/variables/pool-providers.yml@self
+
+resources:
+ repositories:
+ - repository: 1ESPipelineTemplates
+ type: git
+ name: 1ESPipelineTemplates/1ESPipelineTemplates
+ ref: refs/tags/release
+
+extends:
+ template: v1/1ES.Official.PipelineTemplate.yml@1ESPipelineTemplates
+ parameters:
+ sdl:
+ sourceAnalysisPool:
+ name: $(DncEngInternalBuildPool)
+ image: 1es-windows-2022
+ os: windows
+
+ codeql:
+ compiled:
+ enabled: true
+ # Runs analysis in the SDL stage and not every job
+ # https://eng.ms/docs/cloud-ai-platform/devdiv/one-engineering-system-1es/1es-docs/1es-pipeline-templates/features/sdlanalysis/codeql#improving-codeql-performance
+ runSourceLanguagesInSourceAnalysis: true
+
+ baseline:
+ baselineFile: $(Build.SourcesDirectory)\.config\guardian\.gdnbaselines
+
+ stages:
+ - ${{ if and(ne(variables.isPRTrigger, 'true'), eq(variables['System.TeamProject'], 'internal')) }}:
+ - template: /eng/pipelines/templates/stages/vmr-scan.yml@self
+
+ - template: /src/installer/eng/pipelines/templates/stages/vmr-build.yml@self
+ parameters:
+ isBuiltFromVmr: true
+ isSourceOnlyBuild: ${{ variables.isSourceOnlyBuild }}
+ ${{ if eq(variables.isScheduleTrigger, 'true') }}:
+ scope: full
+ ${{ elseif eq(variables.isSourceOnlyBuildLite, 'true') }}:
+ scope: lite
+ ${{ elseif and(eq(variables.isPRTrigger, 'true'), eq(variables.isSourceOnlyBuild, 'true')) }}:
+ scope: ultralite
+ ${{ elseif and(eq(variables.isPRTrigger, 'true'), ne(variables.isSourceOnlyBuild, 'true')) }}:
+ scope: lite
+ ${{ else }}:
+ scope: full
diff --git a/src/SourceBuild/content/eng/pipelines/pr.yml b/src/SourceBuild/content/eng/pipelines/pr.yml
new file mode 100644
index 000000000000..c8cc21beadf0
--- /dev/null
+++ b/src/SourceBuild/content/eng/pipelines/pr.yml
@@ -0,0 +1,53 @@
+# This yml is used by these PR pipelines and triggers:
+# NOTE: the triggers are defined in the Azure DevOps UI as they are too complex
+#
+# - dotnet-source-build (public)
+# - PR: ultralite build
+# - CI: release/* only, every batched commit, full build
+# - Schedule: main only, full build
+#
+# - dotnet-unified-build (public)
+# - PR: lite build
+# - CI: release/* only, every batched commit, full build
+# - Schedule: main only, full build
+#
+# - dotnet-source-build (internal)
+# - PR: ultralite build
+#
+# - dotnet-source-build-lite (internal)
+# - PR: release/* and main, lite build, on-demand trigger
+#
+# - dotnet-unified-build (internal)
+# - PR: lite build
+
+variables:
+# enable source-only build for pipelines that contain the -source-build string
+- name: isSourceOnlyBuild
+ value: ${{ contains(variables['Build.DefinitionName'], '-source-build') }}
+
+- name: isSourceOnlyBuildLite
+ value: ${{ contains(variables['Build.DefinitionName'], '-source-build-lite') }}
+
+- name: isScheduleTrigger
+ value: ${{ eq(variables['Build.Reason'], 'Schedule') }}
+
+- name: isPRTrigger
+ value: ${{ eq(variables['Build.Reason'], 'PullRequest') }}
+
+- template: /eng/common/templates/variables/pool-providers.yml@self
+
+stages:
+- template: /src/installer/eng/pipelines/templates/stages/vmr-build.yml
+ parameters:
+ isBuiltFromVmr: true
+ isSourceOnlyBuild: ${{ variables.isSourceOnlyBuild }}
+ ${{ if eq(variables.isScheduleTrigger, 'true') }}:
+ scope: full
+ ${{ elseif eq(variables.isSourceOnlyBuildLite, 'true') }}:
+ scope: lite
+ ${{ elseif and(eq(variables.isPRTrigger, 'true'), eq(variables.isSourceOnlyBuild, 'true')) }}:
+ scope: ultralite
+ ${{ elseif and(eq(variables.isPRTrigger, 'true'), ne(variables.isSourceOnlyBuild, 'true')) }}:
+ scope: lite
+ ${{ else }}:
+ scope: full
diff --git a/src/SourceBuild/content/eng/pipelines/source-build-sdk-diff-tests.yml b/src/SourceBuild/content/eng/pipelines/source-build-sdk-diff-tests.yml
new file mode 100644
index 000000000000..0303a17c0f2b
--- /dev/null
+++ b/src/SourceBuild/content/eng/pipelines/source-build-sdk-diff-tests.yml
@@ -0,0 +1,68 @@
+schedules:
+- cron: "0 11 * * 1-5"
+ displayName: Run on weekdays at 11am UTC
+ branches:
+ include:
+ - main
+
+# Relies on dotnet-source-build being in the same repo as this pipeline
+# https://learn.microsoft.com/en-us/azure/devops/pipelines/process/pipeline-triggers?view=azure-devops#branch-considerations
+resources:
+ pipelines:
+ - pipeline: dotnet-source-build
+ source: dotnet-source-build
+ trigger:
+ branches:
+ include:
+ - release/*.0.1xx*
+ - internal/release/*.0.1xx*
+
+pr: none
+trigger: none
+
+pool:
+ name: NetCore1ESPool-Svc-Internal
+ demands: ImageOverride -equals 1es-ubuntu-2004
+
+parameters:
+- name: dotnetDotnetRunId
+ displayName: 'Specific dotnet-dotnet run ID number (e.g `2108850`)'
+ type: string
+ default: ' '
+
+jobs:
+- template: templates/jobs/sdk-diff-tests.yml
+ parameters:
+ buildName: CentOSStream9_Offline_MsftSdk
+ targetRid: centos.9-x64
+ architecture: x64
+ dotnetDotnetRunId: ${{ parameters.dotnetDotnetRunId }}
+ includeArtifactsSize: true
+
+- template: templates/jobs/sdk-diff-tests.yml
+ parameters:
+ buildName: Alpine319_Online_MsftSdk
+ targetRid: alpine.3.19-x64
+ architecture: x64
+ dotnetDotnetRunId: ${{ parameters.dotnetDotnetRunId }}
+
+- template: templates/jobs/sdk-diff-tests.yml
+ parameters:
+ buildName: Fedora39_Offline_MsftSdk
+ targetRid: fedora.39-x64
+ architecture: x64
+ dotnetDotnetRunId: ${{ parameters.dotnetDotnetRunId }}
+
+- template: templates/jobs/sdk-diff-tests.yml
+ parameters:
+ buildName: Ubuntu2204_Offline_MsftSdk
+ targetRid: ubuntu.22.04-x64
+ architecture: x64
+ dotnetDotnetRunId: ${{ parameters.dotnetDotnetRunId }}
+
+- template: templates/jobs/sdk-diff-tests.yml
+ parameters:
+ buildName: Ubuntu2204Arm64_Offline_MsftSdk
+ targetRid: ubuntu.22.04-arm64
+ architecture: arm64
+ dotnetDotnetRunId: ${{ parameters.dotnetDotnetRunId }}
diff --git a/src/SourceBuild/content/eng/pipelines/templates/jobs/sdk-diff-tests.yml b/src/SourceBuild/content/eng/pipelines/templates/jobs/sdk-diff-tests.yml
new file mode 100644
index 000000000000..84841ee402b1
--- /dev/null
+++ b/src/SourceBuild/content/eng/pipelines/templates/jobs/sdk-diff-tests.yml
@@ -0,0 +1,167 @@
+parameters:
+- name: buildName
+ type: string
+
+- name: targetRid
+ type: string
+
+- name: architecture
+ type: string
+
+- name: dotnetDotnetRunId
+ type: string
+
+- name: includeArtifactsSize
+ type: boolean
+ default: false
+
+jobs:
+- job: ${{ parameters.buildName }}_${{ parameters.architecture }}
+ timeoutInMinutes: 150
+ pool:
+ name: NetCore1ESPool-Svc-Internal
+ demands: ImageOverride -equals 1es-ubuntu-2004
+ variables:
+ - template: ../variables/pipelines.yml
+ steps:
+ - script: |
+ dotnet_dotnet_build='${{ replace(parameters.dotnetDotnetRunId, ' ', '') }}'
+
+ if [[ -z "$dotnet_dotnet_build" ]]; then
+ dotnet_dotnet_build=$(az pipelines runs list --branch '$(Build.SourceBranch)' --organization '$(AZDO_ORG)' --project '$(AZDO_PROJECT)' --pipeline-ids '$(DOTNET_DOTNET_CI_PIPELINE_ID)' --status completed --top 1 --query "[].id" --output tsv)
+ fi
+
+ if [[ -z "$dotnet_dotnet_build" ]]; then
+ echo "Could not find a completed dotnet-dotnet build for branch '$(Build.SourceBranch)'"
+ exit 1
+ fi
+
+ echo "Dotnet-dotnet build: https://dev.azure.com/dnceng/internal/_build/results?buildId=$dotnet_dotnet_build&view=results"
+
+ installer_sha=$(az pipelines build tag list --organization '$(AZDO_ORG)' --project '$(AZDO_PROJECT)' --build-id $dotnet_dotnet_build --query "[?contains(@, 'installer')]" --output tsv | sed "s,installer ,,g")
+ installer_build=$(az pipelines runs list --organization '$(AZDO_ORG)' --project '$(AZDO_PROJECT)' --pipeline-ids '$(INSTALLER_OFFICIAL_CI_PIPELINE_ID)' --query "[?sourceVersion == '$installer_sha'].id" --output tsv)
+ if [[ -z "$installer_build" ]]; then
+ echo "Could not find a build of installer for commit '$installer_sha'"
+ exit 1
+ fi
+
+ echo "Installer build: https://dev.azure.com/dnceng/internal/_build/results?buildId=$installer_build&view=results"
+
+ echo "##vso[build.addbuildtag]installer-$installer_sha"
+ echo "##vso[task.setvariable variable=InstallerBuildId]$installer_build"
+ echo "##vso[task.setvariable variable=DotnetDotnetBuildId]$dotnet_dotnet_build"
+ displayName: Find associated builds
+ name: Get_Build_Ids
+ env:
+ AZURE_DEVOPS_EXT_PAT: $(System.AccessToken)
+
+ - template: ../steps/download-pipeline-artifact.yml
+ parameters:
+ patterns: '**/dotnet-sdk-+([0-9]).+([0-9]).+([0-9])?(-@(alpha|preview|rc|rtm)*)-linux*-${{ parameters.architecture }}.tar.gz'
+ displayName: Download MSFT SDK
+
+ - template: ../steps/download-vmr-artifact.yml
+ parameters:
+ buildName: ${{ parameters.buildName }}
+ architecture: ${{ parameters.architecture }}
+ patterns: '**/dotnet-sdk-+([0-9]).+([0-9]).+([0-9])?(-@(alpha|preview|rc|rtm)*)-${{ parameters.targetRid }}.tar.gz'
+ displayName: Download Source Build SDK
+
+ - template: ../steps/download-vmr-artifact.yml
+ parameters:
+ buildName: ${{ parameters.buildName }}
+ architecture: ${{ parameters.architecture }}
+ patterns: '**/Private.SourceBuilt.Artifacts.+([0-9]).+([0-9]).+([0-9])?(-@(alpha|preview|rc|rtm)*).${{ parameters.targetRid }}.tar.gz'
+ displayName: Download Source Built Artifacts
+
+ - script: |
+ find $(Pipeline.Workspace)/Artifacts -type f -exec mv {} $(Pipeline.Workspace)/Artifacts \;
+ displayName: Move Artifacts to root
+
+ - script: |
+ platform="linux"
+ if [[ ${{ parameters.targetRid }} =~ "alpine" ]]; then
+ platform="$platform-musl"
+ fi
+ msft_sdk_tarball_name=$(find "$(Pipeline.Workspace)/Artifacts" -name "dotnet-sdk-*-$platform-${{ parameters.architecture }}.tar.gz" -exec basename {} \;)
+
+ if [[ -z "$msft_sdk_tarball_name" ]]; then
+ echo "Microsoft SDK tarball does not exist in '$(Pipeline.Workspace)/Artifacts'. The associated build https://dev.azure.com/dnceng/internal/_build/results?buildId=$(InstallerBuildId) might have failed."
+ exit 1
+ fi
+
+ sdk_tarball_name=$(find "$(Pipeline.Workspace)/Artifacts" -name "dotnet-sdk-*-${{ parameters.targetRid }}.tar.gz" -exec basename {} \;)
+
+ if [[ -z "$sdk_tarball_name" ]]; then
+ echo "Source-build SDK tarball does not exist in '$(Pipeline.Workspace)/Artifacts'. The associated build https://dev.azure.com/dnceng/internal/_build/results?buildId=$(DotnetDotnetBuildId) might have failed"
+ exit 1
+ fi
+
+ artifacts_path=$(find "$(Pipeline.Workspace)/Artifacts" -name "Private.SourceBuilt.Artifacts.*.${{ parameters.targetRid }}.tar.gz" -exec basename {} \;)
+
+ if [[ -z "$artifacts_path" ]]; then
+ echo "Source-build artifacts path does not exist in '$(Pipeline.Workspace)/Artifacts'. The associated build https://dev.azure.com/dnceng/internal/_build/results?buildId=$(DotnetDotnetBuildId) might have failed"
+ exit 1
+ fi
+
+ eng/common/build.sh -bl --projects $(Build.SourcesDirectory)/test/Microsoft.DotNet.SourceBuild.SmokeTests/Microsoft.DotNet.SourceBuild.SmokeTests.csproj --restore
+
+ echo "##vso[task.setvariable variable=Platform]$platform"
+ echo "##vso[task.setvariable variable=MsftSdkTarballPath]$(Pipeline.Workspace)/Artifacts/$msft_sdk_tarball_name"
+ echo "##vso[task.setvariable variable=SdkTarballPath]$(Pipeline.Workspace)/Artifacts/$sdk_tarball_name"
+ echo "##vso[task.setvariable variable=SourceBuiltArtifactsPath]$(Pipeline.Workspace)/Artifacts/$artifacts_path"
+ displayName: Prepare Tests
+ workingDirectory: $(Build.SourcesDirectory)
+
+ - script: >
+ .dotnet/dotnet test
+ $(Build.SourcesDirectory)/test/Microsoft.DotNet.SourceBuild.SmokeTests/Microsoft.DotNet.SourceBuild.SmokeTests.csproj
+ --filter "Category=SdkContent"
+ --logger:'trx;LogFileName=$(Agent.JobName)_SDKDiffTests.trx'
+ --logger:'console;verbosity=detailed'
+ -c Release
+ -bl:$(Build.SourcesDirectory)/artifacts/log/Debug/BuildTests_$(date +"%m%d%H%M%S").binlog
+ -flp:LogFile=$(Build.SourcesDirectory)/artifacts/logs/BuildTests_$(date +"%m%d%H%M%S").log
+ -clp:v=m
+ /p:MsftSdkTarballPath=$(MsftSdkTarballPath)
+ /p:SdkTarballPath=$(SdkTarballPath)
+ /p:SourceBuiltArtifactsPath=$(SourceBuiltArtifactsPath)
+ /p:SmokeTestsWarnOnSdkContentDiffs=false
+ /p:SmokeTestsIncludeArtifactsSizeTests=${{ parameters.includeArtifactsSize }}
+ /p:TargetRid=${{ parameters.targetRid }}
+ /p:PortableRid=$(Platform)-${{ parameters.architecture }}
+ displayName: Run Tests
+ workingDirectory: $(Build.SourcesDirectory)
+
+ - script: |
+ set -x
+ targetFolder=$(Build.StagingDirectory)/BuildLogs/
+ mkdir -p ${targetFolder}
+ cd "$(Build.SourcesDirectory)"
+ find artifacts/log/ -type f -name "BuildTests*.binlog" -exec cp {} --parents -t ${targetFolder} \;
+ find artifacts/log/ -type f -name "BuildTests*.log" -exec cp {} --parents -t ${targetFolder} \;
+ find artifacts/TestResults/ -type f -name "*.binlog" -exec cp {} --parents -t ${targetFolder} \;
+ find artifacts/TestResults/ -type f -name "*.log" -exec cp {} --parents -t ${targetFolder} \;
+ find artifacts/TestResults/ -type f -name "*.diff" -exec cp {} --parents -t ${targetFolder} \;
+ find artifacts/TestResults/ -type f -name "Updated*.txt" -exec cp {} --parents -t ${targetFolder} \;
+ displayName: Prepare BuildLogs staging directory
+ continueOnError: true
+ condition: succeededOrFailed()
+
+ - publish: '$(Build.StagingDirectory)/BuildLogs'
+ artifact: $(Agent.JobName)_BuildLogs_Attempt$(System.JobAttempt)
+ displayName: Publish BuildLogs
+ continueOnError: true
+ condition: succeededOrFailed()
+
+ - task: PublishTestResults@2
+ displayName: Publish Test Results
+ condition: succeededOrFailed()
+ continueOnError: true
+ inputs:
+ testRunner: vSTest
+ testResultsFiles: '*.trx'
+ searchFolder: $(Build.SourcesDirectory)/test/Microsoft.DotNet.SourceBuild.SmokeTests/TestResults
+ mergeTestResults: true
+ publishRunAttachments: true
+ testRunTitle: $(Agent.JobName)
diff --git a/src/SourceBuild/content/eng/pipelines/templates/stages/vmr-scan.yml b/src/SourceBuild/content/eng/pipelines/templates/stages/vmr-scan.yml
new file mode 100644
index 000000000000..ca377cca8f3f
--- /dev/null
+++ b/src/SourceBuild/content/eng/pipelines/templates/stages/vmr-scan.yml
@@ -0,0 +1,43 @@
+stages:
+- stage: Tag_n_Scan
+ displayName: Tag & Scan
+ dependsOn: []
+ jobs:
+ - job: Tag_n_Scan
+ displayName: Tag & Scan
+ pool:
+ name: $(DncEngInternalBuildPool)
+ image: 1es-ubuntu-2004
+ os: linux
+
+ steps:
+ - checkout: self
+
+ - script: |
+ source ./eng/common/tools.sh
+ InitializeDotNetCli true
+ ./.dotnet/dotnet tool restore
+ displayName: Initialize tooling
+ workingDirectory: $(Build.SourcesDirectory)/src/installer
+
+ - script: |
+ set -e
+ sha=`./.dotnet/dotnet darc vmr get-version --vmr "$(Build.SourcesDirectory)" installer`
+ echo "##vso[build.addbuildtag]$sha"
+ displayName: Tag the build
+ workingDirectory: $(Build.SourcesDirectory)/src/installer
+
+ - script: |
+ ./eng/detect-binaries.sh
+ displayName: Scan for binaries
+ workingDirectory: $(Build.SourcesDirectory)
+ continueOnError: true
+
+ - script: >
+ ./.dotnet/dotnet darc vmr scan-cloaked-files
+ --vmr "$(Build.SourcesDirectory)"
+ --tmp "$(Agent.TempDirectory)"
+ || (echo '##[error]Found cloaked files in the VMR' && exit 1)
+ displayName: Scan for cloaked files
+ workingDirectory: $(Build.SourcesDirectory)/src/installer
+ continueOnError: true
\ No newline at end of file
diff --git a/src/SourceBuild/content/eng/pipelines/templates/steps/download-pipeline-artifact.yml b/src/SourceBuild/content/eng/pipelines/templates/steps/download-pipeline-artifact.yml
new file mode 100644
index 000000000000..21df363d7875
--- /dev/null
+++ b/src/SourceBuild/content/eng/pipelines/templates/steps/download-pipeline-artifact.yml
@@ -0,0 +1,35 @@
+parameters:
+- name: pipeline
+ type: string
+ default: $(INSTALLER_OFFICIAL_CI_PIPELINE_ID)
+
+- name: buildId
+ type: string
+ default: $(InstallerBuildId)
+
+- name: artifact
+ type: string
+ default: BlobArtifacts
+
+- name: patterns
+ type: string
+
+- name: displayName
+ type: string
+ default: Download Pipeline Artifact
+
+steps:
+- task: DownloadPipelineArtifact@2
+ inputs:
+ buildType: specific
+ buildVersionToDownload: specific
+ project: internal
+ pipeline: ${{ parameters.pipeline }}
+ buildId: ${{ parameters.buildId }}
+ artifact: ${{ parameters.artifact }}
+ patterns: ${{ parameters.patterns }}
+ allowPartiallySucceededBuilds: true
+ allowFailedBuilds: true
+ downloadPath: $(Pipeline.Workspace)/Artifacts
+ checkDownloadedFiles: true
+ displayName: ${{ parameters.displayName }}
diff --git a/src/SourceBuild/content/eng/pipelines/templates/steps/download-vmr-artifact.yml b/src/SourceBuild/content/eng/pipelines/templates/steps/download-vmr-artifact.yml
new file mode 100644
index 000000000000..2e2266c426d8
--- /dev/null
+++ b/src/SourceBuild/content/eng/pipelines/templates/steps/download-vmr-artifact.yml
@@ -0,0 +1,22 @@
+parameters:
+- name: buildName
+ type: string
+
+- name: architecture
+ type: string
+
+- name: patterns
+ type: string
+
+- name: displayName
+ type: string
+ default: Download VMR Artifact
+
+steps:
+- template: ../steps/download-pipeline-artifact.yml
+ parameters:
+ pipeline: $(DOTNET_DOTNET_CI_PIPELINE_ID)
+ buildId: $(DotnetDotnetBuildId)
+ artifact: ${{ parameters.buildName }}_${{ parameters.architecture }}_Artifacts
+ patterns: ${{ parameters.patterns }}
+ displayName: ${{ parameters.displayName }}
diff --git a/src/SourceBuild/content/eng/pipelines/templates/variables/pipelines.yml b/src/SourceBuild/content/eng/pipelines/templates/variables/pipelines.yml
new file mode 100644
index 000000000000..2db47a1beff7
--- /dev/null
+++ b/src/SourceBuild/content/eng/pipelines/templates/variables/pipelines.yml
@@ -0,0 +1,11 @@
+variables:
+- name: AZDO_PROJECT
+ value: internal
+- name: AZDO_ORG
+ value: https://dev.azure.com/dnceng/
+- name: INSTALLER_OFFICIAL_CI_PIPELINE_ID
+ value: 286
+- name: INSTALLER_TARBALL_BUILD_CI_PIPELINE_ID
+ value: 1011
+- name: DOTNET_DOTNET_CI_PIPELINE_ID
+ value: 1219
\ No newline at end of file
diff --git a/src/SourceBuild/content/eng/pipelines/vmr-license-scan.yml b/src/SourceBuild/content/eng/pipelines/vmr-license-scan.yml
new file mode 100644
index 000000000000..d73cceecff3e
--- /dev/null
+++ b/src/SourceBuild/content/eng/pipelines/vmr-license-scan.yml
@@ -0,0 +1,143 @@
+# Pipeline documentation at https://github.com/dotnet/dotnet/blob/main/docs/license-scanning.md
+
+schedules:
+- cron: "0 7 * * 1"
+ displayName: Run on Mondays at 7am UTC
+ branches:
+ include:
+ - main
+ - release/*.0.1xx*
+ - internal/release/*.0.1xx*
+
+pr: none
+trigger: none
+
+parameters:
+# Provides a way to scan a specific repo. If not provided, all repos of the VMR will be scanned.
+- name: specificRepoName
+ type: string
+ displayName: "Specific repo name to scan (e.g. runtime, sdk). If empty, scans all repos of the VMR."
+ default: " " # Set it to an empty string to allow it be an optional parameter
+
+variables:
+ installerRoot: '$(Build.SourcesDirectory)/src/installer'
+
+jobs:
+- job: Setup
+ pool:
+ name: NetCore1ESPool-Svc-Internal
+ demands: ImageOverride -equals 1es-ubuntu-2004
+ steps:
+ - script: |
+ vmrSrcDir="$(Build.SourcesDirectory)/src"
+
+ # Builds an Azure DevOps matrix definition. Each entry in the matrix is a path,
+ # allowing a job to be run for each src repo.
+ matrix=""
+
+ # Trim leading/trailing spaces from the repo name
+ specificRepoName=$(echo "${{ parameters.specificRepoName }}" | awk '{$1=$1};1')
+
+ # If the repo name is provided, only scan that repo.
+ if [ ! -z "$specificRepoName" ]; then
+ matrix="\"$specificRepoName\": { \"repoPath\": \"$vmrSrcDir/$specificRepoName\" }"
+ else
+ for dir in $vmrSrcDir/*/
+ do
+ if [ ! -z "$matrix" ]; then
+ matrix="$matrix,"
+ fi
+ repoName=$(basename $dir)
+ matrix="$matrix \"$repoName\": { \"repoPath\": \"$dir\" }"
+ done
+ fi
+
+ matrix="{ $matrix }"
+
+ echo "##vso[task.setvariable variable=matrix;isOutput=true]$matrix"
+ name: GetMatrix
+ displayName: Get Matrix
+
+- job: LicenseScan
+ dependsOn: Setup
+ pool:
+ name: NetCore1ESPool-Svc-Internal
+ demands: ImageOverride -equals 1es-ubuntu-2004
+ timeoutInMinutes: 420
+ strategy:
+ matrix: $[ dependencies.Setup.outputs['GetMatrix.matrix'] ]
+ steps:
+
+ - script: |
+ source ./eng/common/tools.sh
+ InitializeDotNetCli true
+ displayName: Install .NET SDK
+ workingDirectory: $(Build.SourcesDirectory)
+
+ - task: PipAuthenticate@1
+ displayName: 'Pip Authenticate'
+ inputs:
+ artifactFeeds: public/dotnet-public-pypi
+ onlyAddExtraIndex: false
+
+ - script: $(installerRoot)/eng/install-scancode.sh
+ displayName: Install Scancode
+
+ - script: >
+ $(Build.SourcesDirectory)/.dotnet/dotnet test
+ $(Build.SourcesDirectory)/test/Microsoft.DotNet.SourceBuild.SmokeTests/Microsoft.DotNet.SourceBuild.SmokeTests.csproj
+ --filter "FullyQualifiedName=Microsoft.DotNet.SourceBuild.SmokeTests.LicenseScanTests.ScanForLicenses"
+ --logger:'trx;LogFileName=$(Agent.JobName)_LicenseScan.trx'
+ --logger:'console;verbosity=detailed'
+ -c Release
+ -bl:$(Build.SourcesDirectory)/artifacts/log/Debug/BuildTests_$(date +"%m%d%H%M%S").binlog
+ -flp:LogFile=$(Build.SourcesDirectory)/artifacts/logs/BuildTests_$(date +"%m%d%H%M%S").log
+ -clp:v=m
+ /p:SmokeTestsLicenseScanPath=$(repoPath)
+ /p:SmokeTestsWarnOnLicenseScanDiffs=false
+ /p:TargetRid=linux-x64
+ /p:PortableRid=linux-x64
+ displayName: Run Tests
+ workingDirectory: $(Build.SourcesDirectory)
+
+ - script: |
+ set -x
+ targetFolder=$(Build.StagingDirectory)/BuildLogs/
+ mkdir -p ${targetFolder}
+ cd "$(Build.SourcesDirectory)"
+ find artifacts/log/ -type f -name "BuildTests*.binlog" -exec cp {} --parents -t ${targetFolder} \;
+ find artifacts/log/ -type f -name "BuildTests*.log" -exec cp {} --parents -t ${targetFolder} \;
+ find artifacts/TestResults/ -type f -name "*.binlog" -exec cp {} --parents -t ${targetFolder} \;
+ find artifacts/TestResults/ -type f -name "*.log" -exec cp {} --parents -t ${targetFolder} \;
+ echo "Updated:"
+ find artifacts/TestResults/ -type f -name "UpdatedLicenseExclusions*.txt"
+ find artifacts/TestResults/ -type f -name "UpdatedLicenseExclusions*.txt" -exec cp {} --parents -t ${targetFolder} \;
+ find artifacts/TestResults/ -type f -name "Updated*.json"
+ find artifacts/TestResults/ -type f -name "Updated*.json" -exec cp {} --parents -t ${targetFolder} \;
+ echo "Results:"
+ find artifacts/TestResults/ -type f -name "scancode-results*.json" -exec cp {} --parents -t ${targetFolder} \;
+ echo "All:"
+ ls -R artifacts/TestResults/
+ echo "BuildLogs:"
+ ls -R ${targetFolder}
+ displayName: Prepare BuildLogs staging directory
+ continueOnError: true
+ condition: succeededOrFailed()
+
+ - publish: '$(Build.StagingDirectory)/BuildLogs'
+ artifact: $(Agent.JobName)_BuildLogs_Attempt$(System.JobAttempt)
+ displayName: Publish BuildLogs
+ continueOnError: true
+ condition: succeededOrFailed()
+
+ - task: PublishTestResults@2
+ displayName: Publish Test Results
+ condition: succeededOrFailed()
+ continueOnError: true
+ inputs:
+ testRunner: vSTest
+ testResultsFiles: '*.trx'
+ searchFolder: $(Build.SourcesDirectory)/test/Microsoft.DotNet.SourceBuild.SmokeTests/TestResults
+ mergeTestResults: true
+ publishRunAttachments: true
+ testRunTitle: $(Agent.JobName)
diff --git a/src/SourceBuild/content/eng/restore-build-edited-files.ps1 b/src/SourceBuild/content/eng/restore-build-edited-files.ps1
new file mode 100644
index 000000000000..d817e177cf2e
--- /dev/null
+++ b/src/SourceBuild/content/eng/restore-build-edited-files.ps1
@@ -0,0 +1,45 @@
+<#
+.SYNOPSIS
+ Script to perform git restore for a set of commonly edited paths when building the VMR.
+
+.DESCRIPTION
+ This script restores the specified paths using git restore command. It provides options for logging, confirmation, and parameterized restore.
+
+.PARAMETER PathsToRestore
+ Specifies the paths to be restored. Default paths are:
+ - src/*/eng/common/*
+ - src/*global.json
+
+.PARAMETER LogPath
+ Specifies the path to save the log file. Default is 'restore.log' in the script directory.
+
+.PARAMETER NoPrompt
+ Indicates whether to skip the confirmation prompt. If specified, the script will restore the paths without confirmation.
+
+#>
+
+param (
+ [string[]]$PathsToRestore = @(
+ "src/*/eng/common/*",
+ "src/*global.json"
+ ),
+ [Alias("y")]
+ [switch]$NoPrompt = $false
+)
+
+# Confirmation prompt
+if (-not $NoPrompt) {
+ Write-Host "Will restore changes in the following paths:"
+ foreach ($path in $PathsToRestore) {
+ Write-Host " $path"
+ }
+ $choice = Read-Host "Do you want to proceed with restoring the paths? (Y/N)"
+ if (-not $($choice -ieq "Y")) {
+ exit 0
+ }
+}
+
+# Perform git restore for each path
+foreach ($path in $PathsToRestore) {
+ git -C (Split-Path -Path $PSScriptRoot -Parent) restore $path
+}
diff --git a/src/SourceBuild/content/eng/restore-build-edited-files.sh b/src/SourceBuild/content/eng/restore-build-edited-files.sh
new file mode 100755
index 000000000000..651256af7172
--- /dev/null
+++ b/src/SourceBuild/content/eng/restore-build-edited-files.sh
@@ -0,0 +1,80 @@
+#!/bin/bash
+
+# Help message
+show_help() {
+ echo "Script to perform git restore for a set of commonly edited paths when building the VMR."
+ echo ""
+ echo "Usage: $0 [-h] [-p ] [-n]"
+ echo ""
+ echo "Options:"
+ echo " -h, --help Show this help message and exit"
+ echo " -p, --path Specify the paths to be restored (default: src/*/eng/common/*, src/*global.json)"
+ echo " -y, --noprompt Skip the confirmation prompt"
+ echo ""
+ echo "Example:"
+ echo " $0 -p \"src/*/eng/common/*\" -p \"src/*global.json\""
+ echo ""
+ exit 0
+}
+
+source="${BASH_SOURCE[0]}"
+# resolve $source until the file is no longer a symlink
+while [[ -h "$source" ]]; do
+ scriptroot="$( cd -P "$( dirname "$source" )" && pwd )"
+ source="$(readlink "$source")"
+ # if $source was a relative symlink, we need to resolve it relative to the path where the
+ # symlink file was located
+ [[ $source != /* ]] && source="$scriptroot/$source"
+done
+scriptroot="$( cd -P "$( dirname "$source" )" && pwd )"
+
+# Default paths to restore
+DefaultPathsToRestore=(
+ "src/*/eng/common/*"
+ "src/*global.json"
+)
+PathsToRestore=()
+NoPrompt=false
+
+# Parse command line arguments
+while [[ $# -gt 0 ]]; do
+ case $1 in
+ -h|--help)
+ show_help
+ ;;
+ -p|--path)
+ shift
+ PathsToRestore+=("$1")
+ ;;
+ -y|--noprompt)
+ NoPrompt=true
+ ;;
+ *)
+ echo "Invalid option: $1"
+ show_help
+ ;;
+ esac
+ shift
+done
+
+# Use default paths if no paths are passed
+if [ ${#PathsToRestore[@]} -eq 0 ]; then
+ PathsToRestore=("${DefaultPathsToRestore[@]}")
+fi
+
+# Confirmation prompt
+if [ "$NoPrompt" = false ]; then
+ echo "Will restore changes in the following paths:"
+ for path in "${PathsToRestore[@]}"; do
+ echo " $path"
+ done
+ read -p "Do you want to proceed with restoring the paths? (Y/N)" choice
+ if [[ ! "$choice" =~ ^[Yy]$ ]]; then
+ exit 0
+ fi
+fi
+
+# Perform git restore for each path
+for path in "${PathsToRestore[@]}"; do
+ git -C "$(dirname "$scriptroot")" restore "$path"
+done
diff --git a/src/SourceBuild/content/eng/tools/BinaryToolKit/BinaryTool.cs b/src/SourceBuild/content/eng/tools/BinaryToolKit/BinaryTool.cs
new file mode 100644
index 000000000000..67907a22f342
--- /dev/null
+++ b/src/SourceBuild/content/eng/tools/BinaryToolKit/BinaryTool.cs
@@ -0,0 +1,123 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace BinaryToolKit;
+
+public class BinaryTool
+{
+ public async Task ExecuteAsync(
+ string targetDirectory,
+ string outputReportDirectory,
+ string? allowedBinariesFile,
+ Modes mode)
+ {
+ DateTime startTime = DateTime.Now;
+
+ Log.LogInformation($"Starting binary tool at {startTime} in {mode} mode");
+
+ // Parse args
+ targetDirectory = GetAndValidateFullPath(
+ "TargetDirectory",
+ targetDirectory,
+ isDirectory: true,
+ createIfNotExist: false,
+ isRequired: true)!;
+ outputReportDirectory = GetAndValidateFullPath(
+ "OutputReportDirectory",
+ outputReportDirectory,
+ isDirectory: true,
+ createIfNotExist: true,
+ isRequired: true)!;
+ allowedBinariesFile = GetAndValidateFullPath(
+ "AllowedBinariesFile",
+ allowedBinariesFile,
+ isDirectory: false,
+ createIfNotExist: false,
+ isRequired: false);
+
+ // Run the tooling
+ var detectedBinaries = await DetectBinaries.ExecuteAsync(targetDirectory, outputReportDirectory, allowedBinariesFile);
+
+ if (mode == Modes.Validate)
+ {
+ ValidateBinaries(detectedBinaries, outputReportDirectory);
+ }
+
+ else if (mode == Modes.Clean)
+ {
+ RemoveBinaries(detectedBinaries, targetDirectory);
+ }
+
+ Log.LogInformation("Finished all binary tasks. Took " + (DateTime.Now - startTime).TotalSeconds + " seconds.");
+
+ return Log.GetExitCode();
+ }
+
+ private string? GetAndValidateFullPath(
+ string parameterName,
+ string? path,
+ bool isDirectory,
+ bool createIfNotExist,
+ bool isRequired)
+ {
+ if (string.IsNullOrWhiteSpace(path))
+ {
+ if (isRequired)
+ {
+ Log.LogError($"Required path for '{parameterName}' is empty or contains whitespace.");
+ Environment.Exit(1);
+ }
+ return null;
+ }
+
+ string fullPath = Path.GetFullPath(path);
+ bool exists = isDirectory ? Directory.Exists(fullPath) : File.Exists(fullPath);
+
+ if (!exists)
+ {
+ if (createIfNotExist && isDirectory)
+ {
+ Log.LogInformation($"Creating directory '{fullPath}' for '{parameterName}'.");
+ Directory.CreateDirectory(fullPath);
+ }
+ else
+ {
+ Log.LogError($"{(isDirectory ? "Directory" : "File")} '{fullPath}' for '{parameterName}' does not exist.");
+ Environment.Exit(1);
+ }
+ }
+ return fullPath;
+ }
+
+ private static void ValidateBinaries(IEnumerable newBinaries, string outputReportDirectory)
+ {
+ if (newBinaries.Any())
+ {
+ string newBinariesFile = Path.Combine(outputReportDirectory, "NewBinaries.txt");
+
+ Log.LogDebug("New binaries:");
+
+ File.WriteAllLines(newBinariesFile, newBinaries);
+
+ foreach (var binary in newBinaries)
+ {
+ Log.LogDebug($" {binary}");
+ }
+
+ Log.LogError($"ERROR: {newBinaries.Count()} new binaries. Check '{newBinariesFile}' for details.");
+ }
+ }
+
+ private static void RemoveBinaries(IEnumerable binariesToRemove, string targetDirectory)
+ {
+ Log.LogInformation($"Removing binaries from '{targetDirectory}'...");
+
+ foreach (var binary in binariesToRemove)
+ {
+ File.Delete(Path.Combine(targetDirectory, binary));
+ Log.LogDebug($" {binary}");
+ }
+
+ Log.LogInformation($"Finished binary removal. Removed {binariesToRemove.Count()} binaries.");
+ }
+}
\ No newline at end of file
diff --git a/src/SourceBuild/content/eng/tools/BinaryToolKit/BinaryToolKit.csproj b/src/SourceBuild/content/eng/tools/BinaryToolKit/BinaryToolKit.csproj
new file mode 100644
index 000000000000..fa8d420c10c1
--- /dev/null
+++ b/src/SourceBuild/content/eng/tools/BinaryToolKit/BinaryToolKit.csproj
@@ -0,0 +1,26 @@
+
+
+
+ $(NetCurrent)
+ enable
+ enable
+
+ Exe
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/SourceBuild/content/eng/tools/BinaryToolKit/DetectBinaries.cs b/src/SourceBuild/content/eng/tools/BinaryToolKit/DetectBinaries.cs
new file mode 100644
index 000000000000..6e72960759d8
--- /dev/null
+++ b/src/SourceBuild/content/eng/tools/BinaryToolKit/DetectBinaries.cs
@@ -0,0 +1,245 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Diagnostics;
+using Microsoft.Extensions.FileSystemGlobbing;
+using System.Text.RegularExpressions;
+
+namespace BinaryToolKit;
+
+public static class DetectBinaries
+{
+ private const string Utf16Marker = "UTF-16";
+ private const int ChunkSize = 4096;
+ private static readonly Regex GitCleanRegex = new Regex(@"Would (remove|skip)( repository)? (.*)");
+
+ public static async Task> ExecuteAsync(
+ string targetDirectory,
+ string outputReportDirectory,
+ string? allowedBinariesFile)
+ {
+ Log.LogInformation($"Detecting binaries in '{targetDirectory}' not listed in '{allowedBinariesFile}'...");
+
+ var matcher = new Matcher(StringComparison.Ordinal);
+ matcher.AddInclude("**/*");
+ matcher.AddExcludePatterns(await GetIgnoredPatternsAsync(targetDirectory));
+
+ IEnumerable matchingFiles = matcher.GetResultsInFullPath(targetDirectory);
+
+ var tasks = matchingFiles
+ .Select(async file =>
+ {
+ return await IsBinaryAsync(file) ? file.Substring(targetDirectory.Length + 1) : null;
+ });
+
+ var binaryFiles = (await Task.WhenAll(tasks)).OfType();
+
+ var unmatchedBinaryFiles = GetUnmatchedBinaries(
+ binaryFiles,
+ allowedBinariesFile,
+ outputReportDirectory,
+ targetDirectory).ToList();
+
+ Log.LogInformation($"Finished binary detection.");
+
+ return unmatchedBinaryFiles;
+ }
+
+ private static async Task> GetIgnoredPatternsAsync(string targetDirectory)
+ {
+ string gitDirectory = Path.Combine(targetDirectory, ".git");
+ bool isGitRepo = Directory.Exists(gitDirectory);
+
+ try
+ {
+ if (!isGitRepo)
+ {
+ // Configure a fake git repo to use so that we can run git clean -ndx
+ await ExecuteProcessAsync("git", $"-C {targetDirectory} init -q");
+ }
+
+ await ExecuteProcessAsync("git", $"-C {targetDirectory} config --global safe.directory {targetDirectory}");
+
+ string output = await ExecuteProcessAsync("git", $"-C {targetDirectory} clean -ndx");
+
+ List ignoredPaths = output.Split(Environment.NewLine)
+ .Select(line => GitCleanRegex.Match(line))
+ .Where(match => match.Success)
+ .Select(match => match.Groups[3].Value)
+ .ToList();
+
+ if (isGitRepo)
+ {
+ ignoredPaths.Add(".git");
+ }
+
+ return ignoredPaths;
+ }
+ finally
+ {
+ // Ensure .git directory is deleted if it wasn't originally a git repo
+ if (!isGitRepo && Directory.Exists(gitDirectory))
+ {
+ Directory.Delete(gitDirectory, true);
+ }
+ }
+ }
+
+ private static async Task IsBinaryAsync(string filePath)
+ {
+ // Using the GNU diff heuristic to determine if a file is binary or not.
+ // For more details, refer to the GNU diff manual:
+ // https://www.gnu.org/software/diffutils/manual/html_node/Binary.html
+
+ using (FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read))
+ using (BinaryReader br = new BinaryReader(fs))
+ {
+ byte[] buffer = new byte[ChunkSize];
+ int bytesRead = br.Read(buffer, 0, ChunkSize);
+ for (int i = 0; i < bytesRead; i++)
+ {
+ if (buffer[i] == 0)
+ {
+ // Need to check that the file is not UTF-16 encoded
+ // because heuristic can return false positives
+ return await IsNotUTF16Async(filePath);
+ }
+ }
+ }
+ return false;
+ }
+
+ private static async Task IsNotUTF16Async(string file)
+ {
+ if (Environment.OSVersion.Platform == PlatformID.Unix)
+ {
+ string output = await ExecuteProcessAsync("file", $"\"{file}\"");
+ output = output.Split(":")[1].Trim();
+
+ if (output.Contains(Utf16Marker))
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private static async Task ExecuteProcessAsync(string executable, string arguments)
+ {
+ ProcessStartInfo psi = new ()
+ {
+ FileName = executable,
+ Arguments = arguments,
+ CreateNoWindow = true,
+ RedirectStandardOutput = true,
+ RedirectStandardError = true
+ };
+
+ var proc = Process.Start(psi)!;
+
+ string output = await proc.StandardOutput.ReadToEndAsync();
+ string error = await proc.StandardError.ReadToEndAsync();
+
+ await proc.WaitForExitAsync();
+
+ if (!string.IsNullOrEmpty(error))
+ {
+ Log.LogError(error);
+ }
+
+ return output;
+ }
+
+ private static IEnumerable GetUnmatchedBinaries(
+ IEnumerable searchFiles,
+ string? allowedBinariesFile,
+ string outputReportDirectory,
+ string targetDirectory)
+ {
+ HashSet unmatchedFiles = new HashSet(searchFiles);
+
+ var filesToPatterns = new Dictionary>();
+ ParseAllowedBinariesFile(allowedBinariesFile, ref filesToPatterns);
+
+ foreach (var fileToPatterns in filesToPatterns)
+ {
+ var patterns = fileToPatterns.Value;
+ HashSet unusedPatterns = new HashSet(patterns);
+
+ foreach (string pattern in patterns)
+ {
+ Matcher matcher = new Matcher(StringComparison.Ordinal);
+ matcher.AddInclude(pattern);
+
+ var matches = matcher.Match(targetDirectory, searchFiles);
+ if (matches.HasMatches)
+ {
+ unusedPatterns.Remove(pattern);
+ unmatchedFiles.ExceptWith(matches.Files.Select(file => file.Path));
+ }
+ }
+
+ UpdateAllowedBinariesFile(fileToPatterns.Key, outputReportDirectory, unusedPatterns);
+ }
+
+ return unmatchedFiles;
+ }
+
+ private static void ParseAllowedBinariesFile(string? file, ref Dictionary> result)
+ {
+ if (!File.Exists(file))
+ {
+ return;
+ }
+
+ if (!result.ContainsKey(file))
+ {
+ result[file] = new HashSet();
+ }
+
+ foreach (var line in File.ReadLines(file))
+ {
+ var trimmedLine = line.Trim();
+ if (string.IsNullOrWhiteSpace(trimmedLine) || trimmedLine.StartsWith("#"))
+ {
+ continue;
+ }
+
+ if (trimmedLine.StartsWith("import:"))
+ {
+ var importFile = trimmedLine.Substring("import:".Length).Trim();
+ if (!Path.IsPathFullyQualified(importFile))
+ {
+ var currentDirectory = Path.GetDirectoryName(file)!;
+ importFile = Path.Combine(currentDirectory, importFile);
+ }
+ if (result.ContainsKey(importFile))
+ {
+ Log.LogWarning($" Duplicate import {importFile}. Skipping.");
+ continue;
+ }
+
+ ParseAllowedBinariesFile(importFile, ref result);
+ }
+ else
+ {
+ result[file].Add(trimmedLine.Split('#')[0].Trim());
+ }
+ }
+ }
+
+ private static void UpdateAllowedBinariesFile(string? file, string outputReportDirectory, HashSet unusedPatterns)
+ {
+ if(File.Exists(file) && unusedPatterns.Any())
+ {
+ var lines = File.ReadAllLines(file);
+ var newLines = lines.Where(line => !unusedPatterns.Contains(line)).ToList();
+
+ string updatedFile = Path.Combine(outputReportDirectory, "Updated" + Path.GetFileName(file));
+
+ File.WriteAllLines(updatedFile, newLines);
+
+ Log.LogInformation($" Updated allowed binaries file '{Path.GetFileName(file)}' written to '{updatedFile}'");
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/SourceBuild/content/eng/tools/BinaryToolKit/Log.cs b/src/SourceBuild/content/eng/tools/BinaryToolKit/Log.cs
new file mode 100644
index 000000000000..10e0081ddc05
--- /dev/null
+++ b/src/SourceBuild/content/eng/tools/BinaryToolKit/Log.cs
@@ -0,0 +1,68 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using Microsoft.Extensions.Logging;
+
+namespace BinaryToolKit;
+
+public static class Log
+{
+ public static LogLevel Level = LogLevel.Information;
+
+ private static bool WarningLogged = false;
+
+ private static bool ErrorLogged = false;
+
+ private static readonly Lazy _logger = new Lazy(ConfigureLogger);
+
+ public static void LogDebug(string message)
+ {
+ _logger.Value.LogDebug(message);
+ }
+
+ public static void LogInformation(string message)
+ {
+ _logger.Value.LogInformation(message);
+ }
+
+ public static void LogWarning(string message)
+ {
+ _logger.Value.LogWarning(message);
+ WarningLogged = true;
+ }
+
+ public static void LogError(string message)
+ {
+ _logger.Value.LogError(message);
+ ErrorLogged = true;
+ }
+
+ private static ILogger ConfigureLogger()
+ {
+ using ILoggerFactory loggerFactory =
+ LoggerFactory.Create(builder =>
+ builder.AddSimpleConsole(options =>
+ {
+ options.SingleLine = true;
+ options.TimestampFormat = "HH:mm:ss ";
+ options.UseUtcTimestamp = true;
+ })
+ .SetMinimumLevel(Level));
+ return loggerFactory.CreateLogger("BinaryTool");
+ }
+
+ public static int GetExitCode()
+ {
+ if (ErrorLogged)
+ {
+ return 1;
+ }
+
+ if (WarningLogged)
+ {
+ return 2;
+ }
+
+ return 0;
+ }
+}
\ No newline at end of file
diff --git a/src/SourceBuild/content/eng/tools/BinaryToolKit/Modes.cs b/src/SourceBuild/content/eng/tools/BinaryToolKit/Modes.cs
new file mode 100644
index 000000000000..98eae85c8f3f
--- /dev/null
+++ b/src/SourceBuild/content/eng/tools/BinaryToolKit/Modes.cs
@@ -0,0 +1,10 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace BinaryToolKit;
+
+public enum Modes
+{
+ Validate,
+ Clean
+}
\ No newline at end of file
diff --git a/src/SourceBuild/content/eng/tools/BinaryToolKit/NuGet.config b/src/SourceBuild/content/eng/tools/BinaryToolKit/NuGet.config
new file mode 100644
index 000000000000..9d4d1db97065
--- /dev/null
+++ b/src/SourceBuild/content/eng/tools/BinaryToolKit/NuGet.config
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/src/SourceBuild/content/eng/tools/BinaryToolKit/Program.cs b/src/SourceBuild/content/eng/tools/BinaryToolKit/Program.cs
new file mode 100644
index 000000000000..5feb90aad74a
--- /dev/null
+++ b/src/SourceBuild/content/eng/tools/BinaryToolKit/Program.cs
@@ -0,0 +1,86 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.CommandLine;
+using Microsoft.Extensions.Logging;
+
+namespace BinaryToolKit;
+
+public class Program
+{
+ public static readonly CliArgument TargetDirectory = new("target-directory")
+ {
+ Description = "The directory to run the binary tooling on.",
+ Arity = ArgumentArity.ExactlyOne
+ };
+
+ public static readonly CliOption OutputReportDirectory = new("--output-directory", "-o")
+ {
+ Description = "The directory to output the report to.",
+ Arity = ArgumentArity.ZeroOrOne,
+ DefaultValueFactory = _ => Path.Combine(Directory.GetCurrentDirectory(), "binary-report")
+ };
+
+ public static readonly CliOption Level = new("--log-level", "-l")
+ {
+ Description = "The log level to run the tool in.",
+ Arity = ArgumentArity.ZeroOrOne,
+ DefaultValueFactory = _ => LogLevel.Information,
+ Recursive = true
+ };
+
+ public static readonly CliOption AllowedBinariesFile = new("--allowed-binaries-file", "-ab")
+ {
+ Description = "The file containing the list of allowed binaries that are ignored for cleaning or validating.\n",
+ Arity = ArgumentArity.ZeroOrOne
+ };
+
+ public static int ExitCode = 0;
+
+ public static async Task Main(string[] args)
+ {
+ var cleanCommand = CreateCommand("clean", "Clean the binaries in the target directory.");
+ var validateCommand = CreateCommand("validate", "Detect new binaries in the target directory.");
+
+ var rootCommand = new CliRootCommand("Tool for detecting, validating, and cleaning binaries in the target directory.")
+ {
+ Level,
+ cleanCommand,
+ validateCommand
+ };
+
+ SetCommandAction(cleanCommand, Modes.Clean);
+ SetCommandAction(validateCommand, Modes.Validate);
+
+ await rootCommand.Parse(args).InvokeAsync();
+
+ return ExitCode;
+ }
+
+ private static CliCommand CreateCommand(string name, string description)
+ {
+ return new CliCommand(name, description)
+ {
+ TargetDirectory,
+ OutputReportDirectory,
+ AllowedBinariesFile
+ };
+ }
+
+ private static void SetCommandAction(CliCommand command, Modes mode)
+ {
+ command.SetAction(async (result, CancellationToken) =>
+ {
+ Log.Level = result.GetValue(Level);
+
+ var binaryTool = new BinaryTool();
+
+ ExitCode = await binaryTool.ExecuteAsync(
+ result.GetValue(TargetDirectory)!,
+ result.GetValue(OutputReportDirectory)!,
+ result.GetValue(AllowedBinariesFile),
+ mode);
+ });
+ }
+}
\ No newline at end of file
diff --git a/src/SourceBuild/content/eng/tools/Directory.Build.props b/src/SourceBuild/content/eng/tools/Directory.Build.props
new file mode 100644
index 000000000000..63c892c312df
--- /dev/null
+++ b/src/SourceBuild/content/eng/tools/Directory.Build.props
@@ -0,0 +1,13 @@
+
+
+
+
+ true
+
+
+
+
+
diff --git a/src/SourceBuild/content/eng/tools/EmptySdk/Sdk/Sdk.props b/src/SourceBuild/content/eng/tools/EmptySdk/Sdk/Sdk.props
new file mode 100644
index 000000000000..25bc36ee8a1b
--- /dev/null
+++ b/src/SourceBuild/content/eng/tools/EmptySdk/Sdk/Sdk.props
@@ -0,0 +1,14 @@
+
+
+
+ $([MSBuild]::NormalizeDirectory('$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildProjectDirectory), 'global.json'))'))
+ $([MSBuild]::NormalizeDirectory('$(RepoRoot)', 'eng'))
+
+
+
+
+ false
+
+
+
+
diff --git a/src/SourceBuild/content/eng/tools/EmptySdk/Sdk/Sdk.targets b/src/SourceBuild/content/eng/tools/EmptySdk/Sdk/Sdk.targets
new file mode 100644
index 000000000000..8fda6a64c5bb
--- /dev/null
+++ b/src/SourceBuild/content/eng/tools/EmptySdk/Sdk/Sdk.targets
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/SourceBuild/content/eng/tools/init-build.proj b/src/SourceBuild/content/eng/tools/init-build.proj
new file mode 100644
index 000000000000..7dece577bd7e
--- /dev/null
+++ b/src/SourceBuild/content/eng/tools/init-build.proj
@@ -0,0 +1,179 @@
+
+
+
+
+
+
+ netstandard2.0
+ true
+ false
+
+
+
+
+
+
+
+
+ $([MSBuild]::NormalizeDirectory('$(PrereqsPackagesDir)', 'archive'))
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ROOTFS_DIR=$(ArtifactsObjDir)crossrootfs/arm
+ ROOTFS_DIR=$(ArtifactsObjDir)crossrootfs/armel
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ .source-built.xml
+ $(PackageReportDir)poison-source-built-catalog.xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/SourceBuild/content/eng/tools/tasks/Directory.Build.props b/src/SourceBuild/content/eng/tools/tasks/Directory.Build.props
new file mode 100644
index 000000000000..7c1442273f8d
--- /dev/null
+++ b/src/SourceBuild/content/eng/tools/tasks/Directory.Build.props
@@ -0,0 +1,21 @@
+
+
+
+
+
+ $(ReferencePackagesDir);$(PrebuiltPackagesPath);$(PrebuiltSourceBuiltPackagesPath)
+
+
+
+
+
+
+
+
diff --git a/src/SourceBuild/content/eng/tools/tasks/Microsoft.DotNet.SourceBuild.Tasks.LeakDetection/CatalogFileEntry.cs b/src/SourceBuild/content/eng/tools/tasks/Microsoft.DotNet.SourceBuild.Tasks.LeakDetection/CatalogFileEntry.cs
new file mode 100644
index 000000000000..268d50fc5a71
--- /dev/null
+++ b/src/SourceBuild/content/eng/tools/tasks/Microsoft.DotNet.SourceBuild.Tasks.LeakDetection/CatalogFileEntry.cs
@@ -0,0 +1,26 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Xml.Linq;
+
+namespace Microsoft.DotNet.SourceBuild.Tasks.LeakDetection
+{
+ internal class CatalogFileEntry
+ {
+ const string ElementName = "File";
+
+ internal string Path { get; set; }
+ internal byte[] OriginalHash { get; set; }
+ internal byte[] PoisonedHash { get; set; }
+
+ public XElement ToXml() => new XElement(ElementName,
+ new XAttribute(nameof(Path), Path),
+ new XAttribute(nameof(OriginalHash), OriginalHash.ToHexString()),
+ PoisonedHash == null ? null : new XAttribute(nameof(PoisonedHash), PoisonedHash.ToHexString())
+ );
+ }
+}
diff --git a/src/SourceBuild/content/eng/tools/tasks/Microsoft.DotNet.SourceBuild.Tasks.LeakDetection/CatalogPackageEntry.cs b/src/SourceBuild/content/eng/tools/tasks/Microsoft.DotNet.SourceBuild.Tasks.LeakDetection/CatalogPackageEntry.cs
new file mode 100644
index 000000000000..e52e1aaa28ff
--- /dev/null
+++ b/src/SourceBuild/content/eng/tools/tasks/Microsoft.DotNet.SourceBuild.Tasks.LeakDetection/CatalogPackageEntry.cs
@@ -0,0 +1,38 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Xml.Linq;
+
+namespace Microsoft.DotNet.SourceBuild.Tasks.LeakDetection
+{
+ internal class CatalogPackageEntry
+ {
+ const string ElementName = "Package";
+
+ internal string Path { get; set; }
+ internal string Id { get; set; }
+ internal string Version { get; set; }
+ internal byte[] OriginalHash { get; set; }
+ internal byte[] PoisonedHash { get; set; }
+ internal List Files { get; }
+
+ public CatalogPackageEntry()
+ {
+ this.Files = new List();
+ }
+
+ public XElement ToXml() => new XElement(ElementName,
+ new XAttribute(nameof(Path), Path),
+ new XAttribute(nameof(Id), Id),
+ new XAttribute(nameof(Version), Version),
+ new XAttribute(nameof(OriginalHash), OriginalHash.ToHexString()),
+ PoisonedHash == null ? null : new XAttribute(nameof(PoisonedHash), PoisonedHash.ToHexString()),
+ Files.Select(f => f.ToXml())
+ );
+ }
+}
diff --git a/src/SourceBuild/content/eng/tools/tasks/Microsoft.DotNet.SourceBuild.Tasks.LeakDetection/CheckForPoison.cs b/src/SourceBuild/content/eng/tools/tasks/Microsoft.DotNet.SourceBuild.Tasks.LeakDetection/CheckForPoison.cs
new file mode 100644
index 000000000000..b60804502c5c
--- /dev/null
+++ b/src/SourceBuild/content/eng/tools/tasks/Microsoft.DotNet.SourceBuild.Tasks.LeakDetection/CheckForPoison.cs
@@ -0,0 +1,479 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using Microsoft.Build.Framework;
+using Microsoft.Build.Utilities;
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.IO.Compression;
+using System.Linq;
+using System.Reflection;
+using System.Reflection.Metadata;
+using System.Reflection.PortableExecutable;
+using System.Security.Cryptography;
+using System.Text;
+using System.Text.RegularExpressions;
+using System.Xml;
+using System.Xml.Linq;
+
+namespace Microsoft.DotNet.SourceBuild.Tasks.LeakDetection
+{
+ public class CheckForPoison : Task
+ {
+ ///
+ /// The files to check for poison and/or hash matches. Zips and
+ /// nupkgs will be extracted and checked recursively.
+ /// %(Identity): Path to the initial set of files.
+ /// Add SourceBuildReferencePackage metadata and set to true to
+ /// indicate that the file comes from SBRP.
+ ///
+ [Required]
+ public ITaskItem[] FilesToCheck { get; set; }
+
+ ///
+ /// The path of the project directory to the FilesToCheck.
+ ///
+ [Required]
+ public string ProjectDirPath { get; set; }
+
+ ///
+ /// The output path for an XML poison report, if desired.
+ ///
+ public string PoisonReportOutputFilePath { get; set; }
+
+ ///
+ /// The path of a previously-generated file hash catalog, if
+ /// hash checked is desired. If not, only assembly attributes
+ /// and package marker file checked will be done.
+ ///
+ public string HashCatalogFilePath { get; set; }
+
+ ///
+ /// The marker file name to check for in poisoned nupkg files.
+ ///
+ public string MarkerFileName { get; set; }
+
+ ///
+ /// If true, fails the build if any poisoned files are found.
+ ///
+ public bool FailOnPoisonFound { get; set; }
+
+ ///
+ /// Use this directory instead of the system temp directory for staging.
+ /// Intended for Linux systems with limited /tmp space, like Azure VMs.
+ ///
+ public string OverrideTempPath { get; set; }
+
+ private static readonly string[] ZipFileExtensions =
+ {
+ ".zip",
+ ".nupkg",
+ };
+
+ private static readonly string[] TarFileExtensions =
+ {
+ ".tar",
+ };
+
+ private static readonly string[] TarGzFileExtensions =
+ {
+ ".tgz",
+ ".tar.gz",
+ };
+
+ private static readonly string[] FileNamesToSkip =
+ {
+ "_._",
+ "-.-",
+ ".bowerrc",
+ ".gitignore",
+ ".gitkeep",
+ ".rels",
+ "LICENSE",
+ "prefercliruntime",
+ "RunCsc",
+ "RunVbc",
+ };
+
+ private static readonly string[] FileExtensionsToSkip =
+ {
+ ".config",
+ ".cs",
+ ".cshtml",
+ ".csproj",
+ ".css",
+ ".db",
+ ".editorconfig",
+ ".eot",
+ ".fs",
+ ".fsproj",
+ ".h",
+ ".html",
+ ".ico",
+ ".js",
+ ".json",
+ ".map",
+ ".md",
+ ".nuspec",
+ ".otf",
+ ".png",
+ ".props",
+ ".proto",
+ ".proj",
+ ".psmdcp",
+ ".pubxml",
+ ".razor",
+ ".rtf",
+ ".scss",
+ ".sln",
+ ".svg",
+ ".targets",
+ ".transform",
+ ".ts",
+ ".ttf",
+ ".txt",
+ ".vb",
+ ".vbproj",
+ ".win32manifest",
+ ".woff",
+ ".woff2",
+ ".xaml",
+ ".xml",
+ };
+
+ private const string PoisonMarker = "POISONED";
+
+ private const string SbrpAttributeType = "System.Reflection.AssemblyMetadataAttribute";
+
+ private const string SbrpMetadataName = "IsSourceBuildReferencePackage";
+
+ private record CandidateFileEntry(string ExtractedPath, string DisplayPath, bool IsSourceBuildReferencePackage);
+
+ public override bool Execute()
+ {
+ IEnumerable poisons = GetPoisonedFiles(FilesToCheck, HashCatalogFilePath, MarkerFileName);
+
+ // if we should write out the poison report, do that
+ if (!string.IsNullOrWhiteSpace(PoisonReportOutputFilePath))
+ {
+ File.WriteAllText(PoisonReportOutputFilePath, (new XElement("PrebuiltLeakReport", poisons.OrderBy(p => p.Path).Select(p => p.ToXml()))).ToString());
+ }
+
+ if (FailOnPoisonFound && poisons.Count() > 0)
+ {
+ Log.LogError($"Forced build error: {poisons.Count()} marked files leaked to output. See complete report '{PoisonReportOutputFilePath}' for details.");
+ return false;
+ }
+ else if (poisons.Count() > 0)
+ {
+ Log.LogWarning($"{poisons.Count()} marked files leaked to output. See complete report '{PoisonReportOutputFilePath}' for details.");
+ }
+
+ return !Log.HasLoggedErrors;
+ }
+
+ ///
+ /// Internal helper to allow other tasks to check for poisoned files.
+ ///
+ /// Initial queue of candidate files (will be cleared when done)
+ /// File path to the file hash catalog
+ /// Marker file name to check for in poisoned nupkgs
+ /// List of poisoned packages and files found and reasons for each
+ internal IEnumerable GetPoisonedFiles(IEnumerable initialCandidates, string catalogedPackagesFilePath, string markerFileName)
+ {
+ IEnumerable catalogedPackages = ReadCatalog(catalogedPackagesFilePath);
+ var poisons = new List();
+ var candidateQueue = new Queue(initialCandidates.Select(candidate =>
+ new CandidateFileEntry(candidate.ItemSpec,
+ Utility.MakeRelativePath(candidate.ItemSpec, ProjectDirPath),
+ candidate.GetMetadata(SbrpMetadataName) == "true")));
+
+ if (!string.IsNullOrWhiteSpace(OverrideTempPath))
+ {
+ Directory.CreateDirectory(OverrideTempPath);
+ }
+ var tempDirName = Path.GetRandomFileName();
+ var tempDir = Directory.CreateDirectory(Path.Combine(OverrideTempPath ?? Path.GetTempPath(), tempDirName));
+
+ while (candidateQueue.Any())
+ {
+ var candidate = candidateQueue.Dequeue();
+
+ // if this is a zip or NuPkg, extract it, check for the poison marker, and
+ // add its contents to the list to be checked.
+ if (ZipFileExtensions.Concat(TarFileExtensions).Concat(TarGzFileExtensions).Any(e => candidate.ExtractedPath.ToLowerInvariant().EndsWith(e)))
+ {
+ Log.LogMessage($"Zip or NuPkg file to check: {candidate.ExtractedPath}");
+
+ var tempCheckingDir = Path.Combine(tempDir.FullName, Path.GetFileNameWithoutExtension(candidate.ExtractedPath));
+ PoisonedFileEntry result = ExtractAndCheckZipFileOnly(catalogedPackages, candidate, markerFileName, tempCheckingDir, candidateQueue);
+ if (result != null)
+ {
+ poisons.Add(result);
+ }
+ }
+ else
+ {
+ PoisonedFileEntry result = CheckSingleFile(catalogedPackages, candidate);
+ if (result != null)
+ {
+ poisons.Add(result);
+ }
+ }
+ }
+
+ tempDir.Delete(true);
+
+ return poisons;
+ }
+
+ private static PoisonedFileEntry CheckSingleFile(IEnumerable catalogedPackages, CandidateFileEntry candidate)
+ {
+ // skip some common files that get copied verbatim from nupkgs - LICENSE, _._, etc as well as
+ // file types that we never care about - text files, .gitconfig, etc.
+ var fileToCheck = candidate.ExtractedPath;
+
+ if (FileNamesToSkip.Any(f => Path.GetFileName(fileToCheck).ToLowerInvariant() == f.ToLowerInvariant()) ||
+ FileExtensionsToSkip.Any(e => Path.GetExtension(fileToCheck).ToLowerInvariant() == e.ToLowerInvariant()) ||
+ (new FileInfo(fileToCheck).Length == 0))
+ {
+ return null;
+ }
+
+ var poisonEntry = new PoisonedFileEntry();
+ poisonEntry.Path = candidate.DisplayPath;
+
+ // There seems to be some weird issues with using file streams both for hashing and assembly loading.
+ // Copy everything into a memory stream to avoid these problems.
+ var memStream = new MemoryStream();
+ using (var stream = File.OpenRead(fileToCheck))
+ {
+ stream.CopyTo(memStream);
+ }
+
+ memStream.Seek(0, SeekOrigin.Begin);
+ using (var sha = SHA256.Create())
+ {
+ poisonEntry.Hash = sha.ComputeHash(memStream);
+ }
+
+ foreach (var p in catalogedPackages)
+ {
+ // This hash can match either the original hash (we couldn't poison the file, or redownloaded it) or
+ // the poisoned hash (the obvious failure case of a poisoned file leaked).
+ foreach (var matchingCatalogedFile in p.Files.Where(f => f.OriginalHash.SequenceEqual(poisonEntry.Hash) || (f.PoisonedHash?.SequenceEqual(poisonEntry.Hash) ?? false)))
+ {
+ poisonEntry.Type |= PoisonType.Hash;
+ var match = new PoisonMatch
+ {
+ File = matchingCatalogedFile.Path,
+ Package = p.Path,
+ PackageId = p.Id,
+ PackageVersion = p.Version,
+ };
+ poisonEntry.Matches.Add(match);
+ }
+ }
+
+ try
+ {
+ AssemblyName asm = AssemblyName.GetAssemblyName(fileToCheck);
+ if (!candidate.IsSourceBuildReferencePackage && IsAssemblyFromSbrp(fileToCheck))
+ {
+ poisonEntry.Type |= PoisonType.SourceBuildReferenceAssembly;
+ }
+ else if (IsAssemblyPoisoned(fileToCheck))
+ {
+ poisonEntry.Type |= PoisonType.AssemblyAttribute;
+ }
+ }
+ catch
+ {
+ // this is fine, it's just not an assembly.
+ }
+
+ return poisonEntry.Type != PoisonType.None ? poisonEntry : null;
+ }
+
+ private static bool IsAssemblyPoisoned(string path)
+ {
+ byte[] buffer = File.ReadAllBytes(path);
+ byte[] marker = Encoding.UTF8.GetBytes(PoisonMarker);
+
+ // Start at end of file and go backwards
+ // Marker is likely at the end and this saves time when
+ // we encounter a poisoned file.
+ for (int j = buffer.Length - marker.Length; j >= 0; j--)
+ {
+ int i;
+ for (i = 0; i < marker.Length && buffer[j + i] == marker[i]; i++) ;
+ if (i == marker.Length)
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ private static bool IsAssemblyFromSbrp(string assemblyPath)
+ {
+ using var stream = new FileStream(assemblyPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
+ using var peReader = new PEReader(stream);
+
+ MetadataReader reader = peReader.GetMetadataReader();
+ return reader.CustomAttributes
+ .Select(attrHandle => reader.GetCustomAttribute(attrHandle))
+ .Any(attr => IsAttributeSbrp(reader, attr));
+ }
+
+ private static bool IsAttributeSbrp(MetadataReader reader, CustomAttribute attr)
+ {
+ string attributeType = string.Empty;
+
+ if (attr.Constructor.Kind == HandleKind.MemberReference)
+ {
+ var mref = reader.GetMemberReference((MemberReferenceHandle)attr.Constructor);
+ if (mref.Parent.Kind == HandleKind.TypeReference)
+ {
+ var tref = reader.GetTypeReference((TypeReferenceHandle)mref.Parent);
+ attributeType = $"{reader.GetString(tref.Namespace)}.{reader.GetString(tref.Name)}";
+ }
+ else if (mref.Parent.Kind == HandleKind.TypeDefinition)
+ {
+ var tdef = reader.GetTypeDefinition((TypeDefinitionHandle)mref.Parent);
+ attributeType = $"{reader.GetString(tdef.Namespace)}.{reader.GetString(tdef.Name)}";
+ }
+ }
+ else if (attr.Constructor.Kind == HandleKind.MethodDefinition)
+ {
+ var mdef = reader.GetMethodDefinition((MethodDefinitionHandle)attr.Constructor);
+ var tdef = reader.GetTypeDefinition(mdef.GetDeclaringType());
+ attributeType = $"{reader.GetString(tdef.Namespace)}.{reader.GetString(tdef.Name)}";
+ }
+
+ if (attributeType == SbrpAttributeType)
+ {
+ var decodedValue = attr.DecodeValue(DummyAttributeTypeProvider.Instance);
+ try
+ {
+ return decodedValue.FixedArguments[0].Value?.ToString() == "source" && decodedValue.FixedArguments[1].Value?.ToString() == "source-build-reference-packages";
+ }
+ catch
+ {
+ throw new InvalidOperationException($"{SbrpAttributeType} is not formatted properly with a key, value pair.");
+ }
+ }
+
+ return false;
+ }
+
+ private static PoisonedFileEntry ExtractAndCheckZipFileOnly(IEnumerable catalogedPackages, CandidateFileEntry candidate, string markerFileName, string tempDir, Queue futureFilesToCheck)
+ {
+ var poisonEntry = new PoisonedFileEntry();
+ var zipToCheck = candidate.ExtractedPath;
+ poisonEntry.Path = zipToCheck;
+
+ using (var sha = SHA256.Create())
+ using (var stream = File.OpenRead(zipToCheck))
+ {
+ poisonEntry.Hash = sha.ComputeHash(stream);
+ }
+
+ // first check for a matching poisoned or non-poisoned hash match:
+ // - non-poisoned is a potential error where the package was redownloaded.
+ // - poisoned is a use of a local package we were not expecting.
+ foreach (var matchingCatalogedPackage in catalogedPackages.Where(c => c.OriginalHash.SequenceEqual(poisonEntry.Hash) || (c.PoisonedHash?.SequenceEqual(poisonEntry.Hash) ?? false)))
+ {
+ poisonEntry.Type |= PoisonType.Hash;
+ var match = new PoisonMatch
+ {
+ Package = matchingCatalogedPackage.Path,
+ PackageId = matchingCatalogedPackage.Id,
+ PackageVersion = matchingCatalogedPackage.Version,
+ };
+ poisonEntry.Matches.Add(match);
+ }
+
+ // now extract and look for the marker file
+ if (ZipFileExtensions.Any(e => zipToCheck.ToLowerInvariant().EndsWith(e)))
+ {
+ ZipFile.ExtractToDirectory(zipToCheck, tempDir, true);
+ }
+ else if (TarFileExtensions.Any(e => zipToCheck.ToLowerInvariant().EndsWith(e)))
+ {
+ Directory.CreateDirectory(tempDir);
+ var psi = new ProcessStartInfo("tar", $"xf {zipToCheck} -C {tempDir}");
+ Process.Start(psi).WaitForExit();
+ }
+ else if (TarGzFileExtensions.Any(e => zipToCheck.ToLowerInvariant().EndsWith(e)))
+ {
+ Directory.CreateDirectory(tempDir);
+ var psi = new ProcessStartInfo("tar", $"xzf {zipToCheck} -C {tempDir}");
+ Process.Start(psi).WaitForExit();
+ }
+ else
+ {
+ throw new ArgumentOutOfRangeException($"Don't know how to decompress {zipToCheck}");
+ }
+
+ if (!string.IsNullOrWhiteSpace(markerFileName) && File.Exists(Path.Combine(tempDir, markerFileName)))
+ {
+ poisonEntry.Type |= PoisonType.NupkgFile;
+ }
+
+ foreach (var child in Directory.EnumerateFiles(tempDir, "*", SearchOption.AllDirectories))
+ {
+ string displayPath = $"{candidate.DisplayPath}/{child.Replace(tempDir, string.Empty).TrimStart(Path.DirectorySeparatorChar)}";
+
+ futureFilesToCheck.Enqueue(new CandidateFileEntry(child, displayPath, candidate.IsSourceBuildReferencePackage));
+ }
+
+ return poisonEntry.Type != PoisonType.None ? poisonEntry : null;
+ }
+
+ private static IEnumerable ReadCatalog(string hashCatalogFilePath)
+ {
+ // catalog is optional, we can also just check assembly properties or nupkg marker files
+ if (string.IsNullOrWhiteSpace(hashCatalogFilePath))
+ {
+ return Enumerable.Empty();
+ }
+
+ var doc = new XmlDocument();
+ using (var stream = File.OpenRead(hashCatalogFilePath))
+ {
+ doc.Load(stream);
+ }
+ var packages = new List();
+ var catalog = doc.FirstChild;
+ foreach (XmlElement p in catalog.ChildNodes)
+ {
+ var package = new CatalogPackageEntry
+ {
+ Id = p.Attributes["Id"].Value,
+ Version = p.Attributes["Version"].Value,
+ OriginalHash = p.Attributes["OriginalHash"].Value.ToBytes(),
+ PoisonedHash = p.Attributes["PoisonedHash"]?.Value?.ToBytes(),
+ Path = p.Attributes["Path"].Value,
+ };
+ packages.Add(package);
+ foreach (XmlNode f in p.ChildNodes)
+ {
+ var fEntry = new CatalogFileEntry
+ {
+ OriginalHash = f.Attributes["OriginalHash"].Value.ToBytes(),
+ PoisonedHash = f.Attributes["PoisonedHash"]?.Value?.ToBytes(),
+ Path = f.Attributes["Path"].Value,
+ };
+ package.Files.Add(fEntry);
+ }
+ }
+ return packages;
+ }
+ }
+}
diff --git a/src/SourceBuild/content/eng/tools/tasks/Microsoft.DotNet.SourceBuild.Tasks.LeakDetection/DummyAttributeTypeProvider.cs b/src/SourceBuild/content/eng/tools/tasks/Microsoft.DotNet.SourceBuild.Tasks.LeakDetection/DummyAttributeTypeProvider.cs
new file mode 100644
index 000000000000..158f9cddffaf
--- /dev/null
+++ b/src/SourceBuild/content/eng/tools/tasks/Microsoft.DotNet.SourceBuild.Tasks.LeakDetection/DummyAttributeTypeProvider.cs
@@ -0,0 +1,37 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Reflection;
+using System.Reflection.Metadata;
+using System.Reflection.Metadata.Ecma335;
+
+#nullable enable
+
+namespace Microsoft.DotNet.SourceBuild.Tasks.LeakDetection
+{
+
+ // An empty ICustomAttributeTypeProvider implementation is necessary to read metadata attribute values.
+ internal class DummyAttributeTypeProvider : ICustomAttributeTypeProvider
+ {
+ public static readonly DummyAttributeTypeProvider Instance = new();
+
+ public Type? GetPrimitiveType(PrimitiveTypeCode typeCode) => default(Type);
+
+ public Type? GetSystemType() => default(Type);
+
+ public Type? GetSZArrayType(Type? elementType) => default(Type);
+
+ public Type? GetTypeFromDefinition(MetadataReader reader, TypeDefinitionHandle handle, byte rawTypeKind) => default(Type);
+
+ public Type? GetTypeFromReference(MetadataReader reader, TypeReferenceHandle handle, byte rawTypeKind) => default(Type);
+
+ public Type? GetTypeFromSerializedName(string name) => default(Type);
+
+ public PrimitiveTypeCode GetUnderlyingEnumType(Type? type) => default(PrimitiveTypeCode);
+
+ public bool IsSystemType(Type? type) => default(bool);
+ }
+}
+
diff --git a/src/SourceBuild/content/eng/tools/tasks/Microsoft.DotNet.SourceBuild.Tasks.LeakDetection/MarkAndCatalogPackages.cs b/src/SourceBuild/content/eng/tools/tasks/Microsoft.DotNet.SourceBuild.Tasks.LeakDetection/MarkAndCatalogPackages.cs
new file mode 100644
index 000000000000..3f98cedaebf0
--- /dev/null
+++ b/src/SourceBuild/content/eng/tools/tasks/Microsoft.DotNet.SourceBuild.Tasks.LeakDetection/MarkAndCatalogPackages.cs
@@ -0,0 +1,181 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using Microsoft.Build.Framework;
+using Microsoft.Build.Utilities;
+using Microsoft.DotNet.UnifiedBuild.Tasks;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.IO.Compression;
+using System.Linq;
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Security.Cryptography;
+using System.Text;
+using System.Xml.Linq;
+
+namespace Microsoft.DotNet.SourceBuild.Tasks.LeakDetection
+{
+ public class MarkAndCatalogPackages : Task
+ {
+ private const string CatalogElementName = "HashCatalog";
+ private const string PoisonMarker = "POISONED by DotNetSourceBuild - Should not ship";
+
+ private readonly Type[] AssemblyPropertiesToReplace = new Type[] {
+ typeof(AssemblyProductAttribute),
+ typeof(AssemblyInformationalVersionAttribute),
+ typeof(AssemblyDescriptionAttribute),
+ typeof(AssemblyTitleAttribute)
+ };
+
+ ///
+ /// The name of the XML file to write the hash catalog out to,
+ /// for later checking build output against. This is optional -
+ /// if not used, assemblies will still be poisoned in their attributes.
+ ///
+ public string CatalogOutputFilePath { get; set; }
+
+ ///
+ /// The name of the marker file to drop in the nupkgs. This can vary
+ /// with the packages, so you can use ".prebuilt" for one set of packages
+ /// and ".source-built" for another if you would like to tell the difference
+ /// between two sets of poisoned packages.
+ ///
+ public string MarkerFileName { get; set; }
+
+ ///
+ /// The packages to poison and/or catalog:
+ /// %(Identity): Path to the nupkg.
+ ///
+ [Required]
+ public ITaskItem[] PackagesToMark { get; set; }
+
+ ///
+ /// Use this directory instead of the system temp directory for staging.
+ /// Intended for Linux systems with limited /tmp space, like Azure VMs.
+ ///
+ public string OverrideTempPath { get; set; }
+
+ public override bool Execute()
+ {
+ var tempDirName = Path.GetRandomFileName();
+ if (!string.IsNullOrWhiteSpace(OverrideTempPath))
+ {
+ Directory.CreateDirectory(OverrideTempPath);
+ }
+ var tempDir = Directory.CreateDirectory(Path.Combine(OverrideTempPath ?? Path.GetTempPath(), tempDirName));
+
+ var packageEntries = new List();
+
+ using (var sha = SHA256.Create())
+ {
+ foreach (var p in PackagesToMark)
+ {
+ var packageEntry = new CatalogPackageEntry();
+ packageEntries.Add(packageEntry);
+ packageEntry.Path = p.ItemSpec;
+ using (var stream = File.OpenRead(p.ItemSpec))
+ {
+ packageEntry.OriginalHash = sha.ComputeHash(stream);
+ }
+ var packageIdentity = ReadNuGetPackageInfos.ReadIdentity(p.ItemSpec);
+ packageEntry.Id = packageIdentity.Id;
+ packageEntry.Version = packageIdentity.Version.OriginalVersion;
+ var packageTempPath = Path.Combine(tempDir.FullName, Path.GetFileName(p.ItemSpec));
+ ZipFile.ExtractToDirectory(p.ItemSpec, packageTempPath, true);
+
+ foreach (string file in Directory.EnumerateFiles(packageTempPath, "*", SearchOption.AllDirectories))
+ {
+ // remove signatures so we don't later fail validation
+ if (Path.GetFileName(file) == ".signature.p7s")
+ {
+ File.Delete(file);
+ continue;
+ }
+
+ var catalogFileEntry = new CatalogFileEntry();
+ packageEntry.Files.Add(catalogFileEntry);
+ catalogFileEntry.Path = Utility.MakeRelativePath(file, packageTempPath);
+
+ // There seem to be some weird issues with using a file stream both for hashing and
+ // assembly loading, even closing it in between. Use a MemoryStream to avoid issues.
+ var memStream = new MemoryStream();
+ using (var stream = File.OpenRead(file))
+ {
+ stream.CopyTo(memStream);
+ }
+
+ // First get the original hash of the file
+ memStream.Seek(0, SeekOrigin.Begin);
+ catalogFileEntry.OriginalHash = sha.ComputeHash(memStream);
+
+ // Add poison marker to assemblies
+ try
+ {
+ AssemblyName asm = AssemblyName.GetAssemblyName(file);
+ Poison(file);
+
+ // then get the hash of the now-poisoned file
+ using (var stream = File.OpenRead(file))
+ {
+ catalogFileEntry.PoisonedHash = sha.ComputeHash(stream);
+ }
+ }
+ catch
+ {
+ // this is okay, it's not an assembly
+ }
+
+ }
+
+ if (!string.IsNullOrWhiteSpace(MarkerFileName))
+ {
+ var markerFilePath = Path.Combine(packageTempPath, MarkerFileName);
+
+ if (File.Exists(markerFilePath))
+ {
+ throw new ArgumentException($"Marker file name '{MarkerFileName}' is not sufficiently unique! Exists in '{p.ItemSpec}'.", nameof(MarkerFileName));
+ }
+
+ // mostly we just need to write something unique to this so it's not hashed as a matching file when we check it later.
+ // but it's also convenient to have the package catalog handy.
+ File.WriteAllText(markerFilePath, packageEntry.ToXml().ToString());
+ }
+
+ // create a temp file for this so if something goes wrong in the process we're not in too weird of a state
+ var poisonedPackageName = Path.GetFileName(p.ItemSpec) + ".poisoned";
+ var poisonedPackagePath = Path.Combine(tempDir.FullName, poisonedPackageName);
+ ZipFile.CreateFromDirectory(packageTempPath, poisonedPackagePath);
+
+ // Get the hash of the poisoned package (with poisoned marker file and poisoned assemblies inside)
+ using (var stream = File.OpenRead(poisonedPackagePath))
+ {
+ packageEntry.PoisonedHash = sha.ComputeHash(stream);
+ }
+ File.Delete(p.ItemSpec);
+ File.Move(poisonedPackagePath, p.ItemSpec);
+ Directory.Delete(packageTempPath, true);
+ }
+ }
+
+ // if we should write out the catalog, do that
+ if (!string.IsNullOrWhiteSpace(CatalogOutputFilePath))
+ {
+ var outputFileDir = Path.GetDirectoryName(CatalogOutputFilePath);
+ if (!Directory.Exists(outputFileDir))
+ {
+ Directory.CreateDirectory(outputFileDir);
+ }
+ File.WriteAllText(CatalogOutputFilePath, (new XElement("HashCatalog",
+ packageEntries.Select(p => p.ToXml()))).ToString());
+ }
+
+ tempDir.Delete(true);
+ return !Log.HasLoggedErrors;
+ }
+
+ private void Poison(string path) => File.AppendAllText(path, PoisonMarker);
+ }
+}
diff --git a/src/SourceBuild/content/eng/tools/tasks/Microsoft.DotNet.SourceBuild.Tasks.LeakDetection/Microsoft.DotNet.SourceBuild.Tasks.LeakDetection.csproj b/src/SourceBuild/content/eng/tools/tasks/Microsoft.DotNet.SourceBuild.Tasks.LeakDetection/Microsoft.DotNet.SourceBuild.Tasks.LeakDetection.csproj
new file mode 100644
index 000000000000..5eb9d5fcda91
--- /dev/null
+++ b/src/SourceBuild/content/eng/tools/tasks/Microsoft.DotNet.SourceBuild.Tasks.LeakDetection/Microsoft.DotNet.SourceBuild.Tasks.LeakDetection.csproj
@@ -0,0 +1,20 @@
+
+
+
+ $(NetCurrent)
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/SourceBuild/content/eng/tools/tasks/Microsoft.DotNet.SourceBuild.Tasks.LeakDetection/NuGet.Config b/src/SourceBuild/content/eng/tools/tasks/Microsoft.DotNet.SourceBuild.Tasks.LeakDetection/NuGet.Config
new file mode 100644
index 000000000000..dc7070857222
--- /dev/null
+++ b/src/SourceBuild/content/eng/tools/tasks/Microsoft.DotNet.SourceBuild.Tasks.LeakDetection/NuGet.Config
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/src/SourceBuild/content/eng/tools/tasks/Microsoft.DotNet.SourceBuild.Tasks.LeakDetection/PoisonMatch.cs b/src/SourceBuild/content/eng/tools/tasks/Microsoft.DotNet.SourceBuild.Tasks.LeakDetection/PoisonMatch.cs
new file mode 100644
index 000000000000..0191b3a260e3
--- /dev/null
+++ b/src/SourceBuild/content/eng/tools/tasks/Microsoft.DotNet.SourceBuild.Tasks.LeakDetection/PoisonMatch.cs
@@ -0,0 +1,28 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Xml.Linq;
+
+namespace Microsoft.DotNet.SourceBuild.Tasks.LeakDetection
+{
+ internal class PoisonMatch
+ {
+ const string ElementName = "Match";
+
+ internal string Package { get; set; }
+ internal string File { get; set; }
+ internal string PackageId { get; set; }
+ internal string PackageVersion { get; set; }
+
+ public XElement ToXml() => new XElement(ElementName,
+ Package == null ? null : new XAttribute(nameof(Package), Package),
+ PackageId == null ? null : new XAttribute(nameof(PackageId), PackageId),
+ PackageVersion == null ? null : new XAttribute(nameof(PackageVersion), PackageVersion),
+ File == null ? null: new XAttribute(nameof(File), File)
+ );
+ }
+}
diff --git a/src/SourceBuild/content/eng/tools/tasks/Microsoft.DotNet.SourceBuild.Tasks.LeakDetection/PoisonType.cs b/src/SourceBuild/content/eng/tools/tasks/Microsoft.DotNet.SourceBuild.Tasks.LeakDetection/PoisonType.cs
new file mode 100644
index 000000000000..4ac37e1cb2b0
--- /dev/null
+++ b/src/SourceBuild/content/eng/tools/tasks/Microsoft.DotNet.SourceBuild.Tasks.LeakDetection/PoisonType.cs
@@ -0,0 +1,16 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Microsoft.DotNet.SourceBuild.Tasks.LeakDetection
+{
+ [Flags]
+ internal enum PoisonType
+ {
+ None = 0,
+ Hash = 1,
+ AssemblyAttribute = 2,
+ NupkgFile = 4,
+ SourceBuildReferenceAssembly = 8,
+ }
+}
diff --git a/src/SourceBuild/content/eng/tools/tasks/Microsoft.DotNet.SourceBuild.Tasks.LeakDetection/PoisonedFileEntry.cs b/src/SourceBuild/content/eng/tools/tasks/Microsoft.DotNet.SourceBuild.Tasks.LeakDetection/PoisonedFileEntry.cs
new file mode 100644
index 000000000000..1d98fd8cb100
--- /dev/null
+++ b/src/SourceBuild/content/eng/tools/tasks/Microsoft.DotNet.SourceBuild.Tasks.LeakDetection/PoisonedFileEntry.cs
@@ -0,0 +1,36 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Xml.Linq;
+
+namespace Microsoft.DotNet.SourceBuild.Tasks.LeakDetection
+{
+ internal class PoisonedFileEntry
+ {
+ const string ElementName = "File";
+
+ internal byte[] Hash { get; set; }
+ internal string Path { get; set; }
+ internal PoisonType Type { get; set; }
+ internal List Matches { get; }
+
+ internal PoisonedFileEntry()
+ {
+ this.Matches = new List();
+ }
+
+ public XElement ToXml() => this.ToXml(ElementName);
+
+ protected XElement ToXml(string myElementName) => new XElement(myElementName,
+ new XAttribute(nameof(Path), Path),
+ new XElement(nameof(Hash), Hash.ToHexString()),
+ new XElement(nameof(Type), Type.ToString()),
+ Matches.Select(m => m.ToXml())
+ );
+ }
+}
diff --git a/src/SourceBuild/content/eng/tools/tasks/Microsoft.DotNet.SourceBuild.Tasks.LeakDetection/Utility.cs b/src/SourceBuild/content/eng/tools/tasks/Microsoft.DotNet.SourceBuild.Tasks.LeakDetection/Utility.cs
new file mode 100644
index 000000000000..48df5d2613ba
--- /dev/null
+++ b/src/SourceBuild/content/eng/tools/tasks/Microsoft.DotNet.SourceBuild.Tasks.LeakDetection/Utility.cs
@@ -0,0 +1,43 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Text;
+
+namespace Microsoft.DotNet.SourceBuild.Tasks.LeakDetection
+{
+ internal static class Utility
+ {
+ internal static string ToHexString(this byte[] bytes)
+ {
+ var sb = new StringBuilder();
+ foreach (var b in bytes)
+ {
+ sb.Append(b.ToString("x2"));
+ }
+ return sb.ToString();
+ }
+
+ internal static byte[] ToBytes(this string hex)
+ {
+ var bytes = new List();
+ for (var i = 0; i < hex.Length; i += 2)
+ {
+ bytes.Add(Convert.ToByte(hex.Substring(i, 2), 16));
+ }
+ return bytes.ToArray();
+ }
+
+ internal static string MakeRelativePath(string filePath, string relativeTo)
+ {
+ // Uri.MakeRelativeUri requires the last slash
+ if (!relativeTo.EndsWith("/") && !relativeTo.EndsWith("\\"))
+ {
+ relativeTo += Path.DirectorySeparatorChar;
+ }
+
+ var uri = new Uri(filePath);
+ var relativeToUri = new Uri(relativeTo);
+ return relativeToUri.MakeRelativeUri(uri).ToString();
+ }
+ }
+}
diff --git a/src/SourceBuild/content/eng/tools/tasks/Microsoft.DotNet.UnifiedBuild.MSBuildSdkResolver/Microsoft.DotNet.UnifiedBuild.MSBuildSdkResolver.csproj b/src/SourceBuild/content/eng/tools/tasks/Microsoft.DotNet.UnifiedBuild.MSBuildSdkResolver/Microsoft.DotNet.UnifiedBuild.MSBuildSdkResolver.csproj
new file mode 100644
index 000000000000..7e0cd3a59761
--- /dev/null
+++ b/src/SourceBuild/content/eng/tools/tasks/Microsoft.DotNet.UnifiedBuild.MSBuildSdkResolver/Microsoft.DotNet.UnifiedBuild.MSBuildSdkResolver.csproj
@@ -0,0 +1,24 @@
+
+
+
+ netstandard2.0
+
+
+
+
+
+
+
+
+
+ $([MSBuild]::NormalizePath('$(VSMSBuildSdkResolversDir)', '$(MSBuildProjectName)', '$(MSBuildProjectName).xml'))
+
+
+
+
+
+
diff --git a/src/SourceBuild/content/eng/tools/tasks/Microsoft.DotNet.UnifiedBuild.MSBuildSdkResolver/UnifiedBuildSdkResolver.cs b/src/SourceBuild/content/eng/tools/tasks/Microsoft.DotNet.UnifiedBuild.MSBuildSdkResolver/UnifiedBuildSdkResolver.cs
new file mode 100644
index 000000000000..21f38937a041
--- /dev/null
+++ b/src/SourceBuild/content/eng/tools/tasks/Microsoft.DotNet.UnifiedBuild.MSBuildSdkResolver/UnifiedBuildSdkResolver.cs
@@ -0,0 +1,200 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using Microsoft.Build.Framework;
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+
+namespace Microsoft.DotNet.UnifiedBuild.Tasks
+{
+ ///
+ /// Extends the SDK to handle "SOURCE_BUILT_SDK_*" override environment variables. Each override
+ /// should provide a set of 3 environment variables:
+ ///
+ /// SOURCE_BUILT_SDK_ID_EXAMPLE=Your.Sdk.Example
+ /// ID of the SDK nuget package to override.
+ ///
+ /// SOURCE_BUILT_SDK_DIR_EXAMPLE=/git/repo/bin/extracted/Your.Sdk.Example/
+ /// Directory where the sdk/Sdk.props and/or sdk/Sdk.targets files are located. This should be
+ /// the directory where the override SDK package is extracted.
+ ///
+ /// SOURCE_BUILT_SDK_VERSION_EXAMPLE=1.0.0-source-built
+ /// (Optional) Version of the SDK package to use. This is informational.
+ ///
+ public class UnifiedBuildSdkResolver : SdkResolver
+ {
+ public override string Name => nameof(UnifiedBuildSdkResolver);
+
+ public override int Priority => 0;
+
+ public override SdkResult Resolve(
+ SdkReference sdkReference,
+ SdkResolverContext resolverContext,
+ SdkResultFactory factory)
+ {
+ string sdkDescription = sdkReference.Name;
+ if (!string.IsNullOrEmpty(sdkReference.Version))
+ {
+ sdkDescription += $" {sdkReference.Version}";
+ }
+ if (!string.IsNullOrEmpty(sdkReference.MinimumVersion))
+ {
+ sdkDescription += $" (>= {sdkReference.MinimumVersion})";
+ }
+
+ SourceBuiltSdkOverride[] overrides = Environment.GetEnvironmentVariables()
+ .Cast()
+ .Select(SourceBuiltSdkOverride.Create)
+ .Where(o => o != null)
+ .ToArray();
+
+ void LogMessage(string message)
+ {
+ resolverContext.Logger.LogMessage($"[{Name}] {message}", MessageImportance.Low);
+ }
+
+ if (overrides.Any())
+ {
+ string separator = overrides.Length == 1 ? " " : Environment.NewLine;
+
+ LogMessage(
+ $"Looking for SDK {sdkDescription}. Detected config(s) in env:{separator}" +
+ string.Join(Environment.NewLine, overrides.Select(o => o.ToString())));
+ }
+
+ SourceBuiltSdkOverride[] matches = overrides
+ .Where(o => sdkReference.Name.Equals(o?.Id, StringComparison.OrdinalIgnoreCase))
+ .ToArray();
+
+ var unresolvableReasons = new List();
+
+ if (matches.Length != 1)
+ {
+ unresolvableReasons.Add(
+ $"{matches.Length} overrides found for '{sdkReference.Name}'");
+ }
+ else
+ {
+ SourceBuiltSdkOverride match = matches[0];
+ string[] matchProblems = match.GetValidationErrors().ToArray();
+
+ if (matchProblems.Any())
+ {
+ unresolvableReasons.Add($"Found match '{match.Group}' with problems:");
+ unresolvableReasons.AddRange(matchProblems);
+ }
+ else
+ {
+ LogMessage($"Overriding {sdkDescription} with '{match.Group}'");
+
+ return factory.IndicateSuccess(match.SdkDir, match.Version);
+ }
+ }
+
+ return factory.IndicateFailure(unresolvableReasons.Select(r => $"[{Name}] {r}"));
+ }
+
+ ///
+ /// Takes a directory path (not ending in a separator) and determines if it is the "sdk"
+ /// directory inside a SDK package with a case-insensitive comparison.
+ ///
+ private static bool IsSdkDirectory(string path)
+ {
+ return Path.GetFileName(path).Equals("sdk", StringComparison.OrdinalIgnoreCase);
+ }
+
+ private class SourceBuiltSdkOverride
+ {
+ private const string EnvPrefix = "SOURCE_BUILT_SDK_";
+ private const string EnvId = EnvPrefix + "ID_";
+ private const string EnvVersion = EnvPrefix + "VERSION_";
+ private const string EnvDir = EnvPrefix + "DIR_";
+
+ public static SourceBuiltSdkOverride Create(DictionaryEntry entry)
+ {
+ if (entry.Key is string key && key.StartsWith(EnvId))
+ {
+ // E.g. "ARCADE" from "SOURCE_BUILD_SDK_ID_ARCADE=Microsoft.DotNet.Arcade.Sdk".
+ string group = key.Substring(EnvId.Length);
+
+ if (string.IsNullOrEmpty(group))
+ {
+ return null;
+ }
+
+ string id = entry.Value as string;
+ string version = Environment.GetEnvironmentVariable(EnvVersion + group);
+ string dir = Environment.GetEnvironmentVariable(EnvDir + group);
+
+ if (string.IsNullOrEmpty(version))
+ {
+ version = "1.0.0-source-built";
+ }
+
+ string sdkDir = null;
+ if (!string.IsNullOrEmpty(dir))
+ {
+ sdkDir = Directory.EnumerateDirectories(dir).FirstOrDefault(IsSdkDirectory);
+ }
+
+ return new SourceBuiltSdkOverride
+ {
+ Group = group,
+ Id = id,
+ Version = version,
+ Dir = dir,
+ SdkDir = sdkDir,
+ };
+ }
+ return null;
+ }
+
+ ///
+ /// Name of the environment variable group, used to associate the multiple env vars.
+ ///
+ public string Group { get; set; }
+
+ ///
+ /// ID of the SDK package.
+ ///
+ public string Id { get; set; }
+
+ ///
+ /// Version of the source-built SDK package to use instead.
+ ///
+ public string Version { get; set; }
+
+ ///
+ /// Directory where the source-built SDK files are found, in extracted form.
+ ///
+ public string Dir { get; set; }
+
+ ///
+ /// Directory where the Sdk.props/Sdk.targets files are found, inside Dir.
+ ///
+ public string SdkDir { get; set; }
+
+ public override string ToString() => $"'{Group}' for '{Id}/{Version}' at '{Dir}'";
+
+ public IEnumerable GetValidationErrors()
+ {
+ if (string.IsNullOrEmpty(Id))
+ {
+ yield return $"'{EnvId}{Group}' not specified.";
+ }
+ if (string.IsNullOrEmpty(Dir))
+ {
+ yield return $"'{EnvDir}{Group}' not specified.";
+ }
+ else if (string.IsNullOrEmpty(SdkDir))
+ {
+ yield return $"Didn't find any 'sdk' directory in SDK package dir '{Dir}'";
+ }
+ }
+ }
+ }
+}
diff --git a/src/SourceBuild/content/eng/tools/tasks/Microsoft.DotNet.UnifiedBuild.Tasks/AddSourceToNuGetConfig.cs b/src/SourceBuild/content/eng/tools/tasks/Microsoft.DotNet.UnifiedBuild.Tasks/AddSourceToNuGetConfig.cs
new file mode 100644
index 000000000000..3b62bbe5faac
--- /dev/null
+++ b/src/SourceBuild/content/eng/tools/tasks/Microsoft.DotNet.UnifiedBuild.Tasks/AddSourceToNuGetConfig.cs
@@ -0,0 +1,65 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.IO;
+using System.Linq;
+using System.Xml;
+using System.Xml.Linq;
+using Microsoft.Build.Framework;
+using Microsoft.Build.Utilities;
+
+namespace Microsoft.DotNet.UnifiedBuild.Tasks
+{
+ /*
+ * This task adds a source to a well-formed NuGet.Config file. If a source with `SourceName` is already present, then
+ * the path of the source is changed. Otherwise, the source is added as the first source in the list, after any clear
+ * elements (if present).
+ */
+ public class AddSourceToNuGetConfig : Task
+ {
+ [Required]
+ public string NuGetConfigFile { get; set; }
+
+ [Required]
+ public string SourceName { get; set; }
+
+ [Required]
+ public string SourcePath { get; set; }
+
+ public override bool Execute()
+ {
+ string xml = File.ReadAllText(NuGetConfigFile);
+ string newLineChars = FileUtilities.DetectNewLineChars(xml);
+ XDocument d = XDocument.Parse(xml);
+ XElement packageSourcesElement = d.Root.Descendants().First(e => e.Name == "packageSources");
+ XElement toAdd = new XElement("add", new XAttribute("key", SourceName), new XAttribute("value", SourcePath));
+ XElement clearTag = new XElement("clear");
+
+ XElement exisitingSourceBuildElement = packageSourcesElement.Descendants().FirstOrDefault(e => e.Name == "add" && e.Attribute(XName.Get("key")).Value == SourceName);
+ XElement lastClearElement = packageSourcesElement.Descendants().LastOrDefault(e => e.Name == "clear");
+
+ if (exisitingSourceBuildElement != null)
+ {
+ exisitingSourceBuildElement.ReplaceWith(toAdd);
+ }
+ else if (lastClearElement != null)
+ {
+ lastClearElement.AddAfterSelf(toAdd);
+ }
+ else
+ {
+ packageSourcesElement.AddFirst(toAdd);
+ packageSourcesElement.AddFirst(clearTag);
+ }
+
+ using (var w = XmlWriter.Create(NuGetConfigFile, new XmlWriterSettings { NewLineChars = newLineChars, Indent = true }))
+ {
+ d.Save(w);
+ }
+
+ return true;
+ }
+ }
+}
diff --git a/src/SourceBuild/content/eng/tools/tasks/Microsoft.DotNet.UnifiedBuild.Tasks/BuildTask.cs b/src/SourceBuild/content/eng/tools/tasks/Microsoft.DotNet.UnifiedBuild.Tasks/BuildTask.cs
new file mode 100644
index 000000000000..e8155b07246e
--- /dev/null
+++ b/src/SourceBuild/content/eng/tools/tasks/Microsoft.DotNet.UnifiedBuild.Tasks/BuildTask.cs
@@ -0,0 +1,37 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using Microsoft.Build.Framework;
+using Microsoft.Build.Utilities;
+
+namespace Microsoft.DotNet.UnifiedBuild.Tasks
+{
+ public abstract partial class BuildTask : ITask
+ {
+ private TaskLoggingHelper _log = null;
+
+ internal TaskLoggingHelper Log
+ {
+ get { return _log ?? (_log = new TaskLoggingHelper(this)); }
+ }
+
+ public BuildTask()
+ {
+ }
+
+ public IBuildEngine BuildEngine
+ {
+ get;
+ set;
+ }
+
+ public ITaskHost HostObject
+ {
+ get;
+ set;
+ }
+
+ public abstract bool Execute();
+ }
+}
\ No newline at end of file
diff --git a/src/SourceBuild/content/eng/tools/tasks/Microsoft.DotNet.UnifiedBuild.Tasks/CreateSdkSymbolsLayout.cs b/src/SourceBuild/content/eng/tools/tasks/Microsoft.DotNet.UnifiedBuild.Tasks/CreateSdkSymbolsLayout.cs
new file mode 100644
index 000000000000..1887b8fb5e30
--- /dev/null
+++ b/src/SourceBuild/content/eng/tools/tasks/Microsoft.DotNet.UnifiedBuild.Tasks/CreateSdkSymbolsLayout.cs
@@ -0,0 +1,163 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Reflection.Metadata;
+using System.Reflection.PortableExecutable;
+using Microsoft.Build.Framework;
+using Microsoft.Build.Utilities;
+
+namespace Microsoft.DotNet.UnifiedBuild.Tasks
+{
+ // Creates a symbols layout that matches the SDK layout
+ public class CreateSdkSymbolsLayout : Task
+ {
+ ///
+ /// Path to SDK layout.
+ ///
+ [Required]
+ public string SdkLayoutPath { get; set; }
+
+ ///
+ /// Path to all source-built symbols, flat or with folder hierarchy.
+ ///
+ [Required]
+ public string AllSymbolsPath { get; set; }
+
+ ///
+ /// Path to SDK symbols layout - will be created if it doesn't exist.
+ ///
+ [Required]
+ public string SdkSymbolsLayoutPath { get; set; }
+
+ ///
+ /// If true, fails the build if any PDBs are missing.
+ ///
+ public bool FailOnMissingPDBs { get; set; }
+
+ public override bool Execute()
+ {
+ IList filesWithoutPDBs = GenerateSymbolsLayout(IndexAllSymbols());
+ if (filesWithoutPDBs.Count > 0)
+ {
+ LogErrorOrWarning(FailOnMissingPDBs, $"Did not find PDBs for the following SDK files:");
+ foreach (string file in filesWithoutPDBs)
+ {
+ LogErrorOrWarning(FailOnMissingPDBs, file);
+ }
+ }
+
+ return !Log.HasLoggedErrors;
+ }
+
+ private void LogErrorOrWarning(bool isError, string message)
+ {
+ if (isError)
+ {
+ Log.LogError(message);
+ }
+ else
+ {
+ Log.LogWarning(message);
+ }
+ }
+
+ private IList GenerateSymbolsLayout(Hashtable allPdbGuids)
+ {
+ List filesWithoutPDBs = new List();
+
+ if (Directory.Exists(SdkSymbolsLayoutPath))
+ {
+ Directory.Delete(SdkSymbolsLayoutPath, true);
+ }
+
+ foreach (string file in Directory.GetFiles(SdkLayoutPath, "*", SearchOption.AllDirectories))
+ {
+ if (file.EndsWith(".dll", StringComparison.InvariantCultureIgnoreCase) &&
+ !file.EndsWith(".resources.dll", StringComparison.InvariantCultureIgnoreCase))
+ {
+ string guid = string.Empty;
+ using var pdbStream = File.OpenRead(file);
+ using var peReader = new PEReader(pdbStream);
+ try
+ {
+ // Check if pdb is embedded
+ if (peReader.ReadDebugDirectory().Any(entry => entry.Type == DebugDirectoryEntryType.EmbeddedPortablePdb))
+ {
+ continue;
+ }
+
+ var debugDirectory = peReader.ReadDebugDirectory().First(entry => entry.Type == DebugDirectoryEntryType.CodeView);
+ var codeViewData = peReader.ReadCodeViewDebugDirectoryData(debugDirectory);
+ guid = $"{codeViewData.Guid.ToString("N").Replace("-", string.Empty)}";
+ }
+ catch (Exception e) when (e is BadImageFormatException || e is InvalidOperationException)
+ {
+ // Ignore binaries without debug info
+ continue;
+ }
+
+ if (guid != string.Empty)
+ {
+ string debugId = GetDebugId(guid, file);
+ if (!allPdbGuids.ContainsKey(debugId))
+ {
+ filesWithoutPDBs.Add(file.Substring(SdkLayoutPath.Length + 1));
+ }
+ else
+ {
+ // Copy matching pdb to symbols path, preserving sdk binary's hierarchy
+ string sourcePath = (string)allPdbGuids[debugId]!;
+ string destinationPath =
+ file.Replace(SdkLayoutPath, SdkSymbolsLayoutPath)
+ .Replace(Path.GetFileName(file), Path.GetFileName(sourcePath));
+
+ Directory.CreateDirectory(Path.GetDirectoryName(destinationPath)!);
+ File.Copy(sourcePath, destinationPath, true);
+ }
+ }
+ }
+ }
+
+ return filesWithoutPDBs;
+ }
+
+ public Hashtable IndexAllSymbols()
+ {
+ Hashtable allPdbGuids = new Hashtable();
+
+ foreach (string file in Directory.GetFiles(AllSymbolsPath, "*.pdb", SearchOption.AllDirectories))
+ {
+ using var pdbFileStream = File.OpenRead(file);
+ var metadataProvider = MetadataReaderProvider.FromPortablePdbStream(pdbFileStream);
+ var metadataReader = metadataProvider.GetMetadataReader();
+ if (metadataReader.DebugMetadataHeader == null)
+ {
+ continue;
+ }
+
+ var id = new BlobContentId(metadataReader.DebugMetadataHeader.Id);
+ string guid = $"{id.Guid:N}";
+ string debugId = GetDebugId(guid, file);
+ if (!string.IsNullOrEmpty(guid) && !allPdbGuids.ContainsKey(debugId))
+ {
+ allPdbGuids.Add(debugId, file);
+ }
+ }
+
+ return allPdbGuids;
+ }
+
+ ///
+ /// Calculates a debug Id from debug guid and filename. We use this as a key
+ /// in PDB hashtable. Guid is not enough due to collisions in several PDBs.
+ ///
+ private string GetDebugId(string guid, string file) =>
+ $"{guid}.{Path.GetFileNameWithoutExtension(file)}".ToLower();
+ }
+}
diff --git a/src/SourceBuild/content/eng/tools/tasks/Microsoft.DotNet.UnifiedBuild.Tasks/EnumerableExtensions.cs b/src/SourceBuild/content/eng/tools/tasks/Microsoft.DotNet.UnifiedBuild.Tasks/EnumerableExtensions.cs
new file mode 100644
index 000000000000..53efc719018d
--- /dev/null
+++ b/src/SourceBuild/content/eng/tools/tasks/Microsoft.DotNet.UnifiedBuild.Tasks/EnumerableExtensions.cs
@@ -0,0 +1,17 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Collections.Generic;
+using System.Linq;
+
+namespace Microsoft.DotNet.UnifiedBuild.Tasks
+{
+ internal static class EnumerableExtensions
+ {
+ public static IEnumerable NullAsEmpty(this IEnumerable source)
+ {
+ return source ?? Enumerable.Empty();
+ }
+ }
+}
diff --git a/src/SourceBuild/content/eng/tools/tasks/Microsoft.DotNet.UnifiedBuild.Tasks/FileUtilities.cs b/src/SourceBuild/content/eng/tools/tasks/Microsoft.DotNet.UnifiedBuild.Tasks/FileUtilities.cs
new file mode 100644
index 000000000000..6d534b935c4a
--- /dev/null
+++ b/src/SourceBuild/content/eng/tools/tasks/Microsoft.DotNet.UnifiedBuild.Tasks/FileUtilities.cs
@@ -0,0 +1,70 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+
+namespace Microsoft.DotNet.UnifiedBuild.Tasks
+{
+ public static class FileUtilities
+ {
+ public const string CR = "\r";
+ public const string CRLF = "\r\n";
+ public const string LF = "\n";
+
+ public static string DetectNewLineChars(string source)
+ {
+ if (source == null)
+ {
+ throw new ArgumentNullException(nameof(source));
+ }
+
+ if (source.Contains(CRLF))
+ {
+ return CRLF;
+ }
+ else if (source.Contains(LF))
+ {
+ return LF;
+ }
+ else if (source.Contains(CR))
+ {
+ return CR;
+ }
+ else
+ {
+ throw new ArgumentException("Unsupported new line characters", nameof(source));
+ }
+ }
+
+ public static string NormalizeNewLineChars(string source, string newLineChars)
+ {
+ if (source == null)
+ {
+ throw new ArgumentNullException(nameof(source));
+ }
+
+ source = source.Replace(CRLF, LF).Replace(CR, LF);
+ if (newLineChars == CRLF)
+ {
+ source = source.Replace(LF, CRLF);
+ }
+ else if (newLineChars == CR)
+ {
+ source = source.Replace(LF, CR);
+ }
+ else if (newLineChars != LF)
+ {
+ throw new ArgumentException("Unsupported new line characters", nameof(newLineChars));
+ }
+
+ // Ensure trailing new line on linux.
+ if (newLineChars == LF && !source.EndsWith(newLineChars))
+ {
+ source += newLineChars;
+ }
+
+ return source;
+ }
+ }
+}
diff --git a/src/SourceBuild/content/eng/tools/tasks/Microsoft.DotNet.UnifiedBuild.Tasks/Log.cs b/src/SourceBuild/content/eng/tools/tasks/Microsoft.DotNet.UnifiedBuild.Tasks/Log.cs
new file mode 100644
index 000000000000..67b7bae56a88
--- /dev/null
+++ b/src/SourceBuild/content/eng/tools/tasks/Microsoft.DotNet.UnifiedBuild.Tasks/Log.cs
@@ -0,0 +1,118 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using Microsoft.Build.Framework;
+using Microsoft.Build.Utilities;
+
+namespace Microsoft.DotNet.UnifiedBuild.Tasks
+{
+ internal class Log : ILog
+ {
+ private readonly TaskLoggingHelper _logger;
+ public Log(TaskLoggingHelper logger)
+ {
+ _logger = logger;
+ }
+
+ public void LogError(string message, params object[] messageArgs)
+ {
+ _logger.LogError(message, messageArgs);
+ }
+
+ public void LogMessage(string message, params object[] messageArgs)
+ {
+ _logger.LogMessage(message, messageArgs);
+ }
+
+ public void LogMessage(LogImportance importance, string message, params object[] messageArgs)
+ {
+ _logger.LogMessage((MessageImportance)importance, message, messageArgs);
+ }
+
+ public void LogWarning(string message, params object[] messageArgs)
+ {
+ _logger.LogWarning(message, messageArgs);
+ }
+
+ public bool HasLoggedErrors { get { return _logger.HasLoggedErrors; } }
+ }
+
+ public enum LogImportance
+ {
+ Low = MessageImportance.Low,
+ Normal = MessageImportance.Normal,
+ High = MessageImportance.High
+ }
+
+
+ public interface ILog
+ {
+ //
+ // Summary:
+ // Logs an error with the specified message.
+ //
+ // Parameters:
+ // message:
+ // The message.
+ //
+ // messageArgs:
+ // Optional arguments for formatting the message string.
+ //
+ // Exceptions:
+ // T:System.ArgumentNullException:
+ // message is null.
+ void LogError(string message, params object[] messageArgs);
+
+ //
+ // Summary:
+ // Logs a message with the specified string.
+ //
+ // Parameters:
+ // message:
+ // The message.
+ //
+ // messageArgs:
+ // The arguments for formatting the message.
+ //
+ // Exceptions:
+ // T:System.ArgumentNullException:
+ // message is null.
+ void LogMessage(string message, params object[] messageArgs);
+
+ //
+ // Summary:
+ // Logs a message with the specified string and importance.
+ //
+ // Parameters:
+ // importance:
+ // One of the enumeration values that specifies the importance of the message.
+ //
+ // message:
+ // The message.
+ //
+ // messageArgs:
+ // The arguments for formatting the message.
+ //
+ // Exceptions:
+ // T:System.ArgumentNullException:
+ // message is null.
+ void LogMessage(LogImportance importance, string message, params object[] messageArgs);
+
+ //
+ // Summary:
+ // Logs a warning with the specified message.
+ //
+ // Parameters:
+ // message:
+ // The message.
+ //
+ // messageArgs:
+ // Optional arguments for formatting the message string.
+ //
+ // Exceptions:
+ // T:System.ArgumentNullException:
+ // message is null.
+ void LogWarning(string message, params object[] messageArgs);
+ }
+}
diff --git a/src/SourceBuild/content/eng/tools/tasks/Microsoft.DotNet.UnifiedBuild.Tasks/MergeAssetManifests.cs b/src/SourceBuild/content/eng/tools/tasks/Microsoft.DotNet.UnifiedBuild.Tasks/MergeAssetManifests.cs
new file mode 100644
index 000000000000..4ee3fed5ee64
--- /dev/null
+++ b/src/SourceBuild/content/eng/tools/tasks/Microsoft.DotNet.UnifiedBuild.Tasks/MergeAssetManifests.cs
@@ -0,0 +1,104 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#nullable enable
+
+using Microsoft.Build.Utilities;
+using Microsoft.Build.Framework;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Xml.Linq;
+
+namespace Microsoft.DotNet.UnifiedBuild.Tasks
+{
+ public class MergeAssetManifests : Task
+ {
+ ///
+ /// AssetManifest paths
+ ///
+ [Required]
+ public required ITaskItem[] AssetManifest { get; init; }
+
+ ///
+ /// Merged asset manifest output path
+ ///
+ [Required]
+ public required string MergedAssetManifestOutputPath { get; init; }
+
+ ///
+ /// Azure DevOps build number
+ ///
+ public string VmrBuildNumber { get; set; } = string.Empty;
+
+ private static readonly string _buildIdAttribute = "BuildId";
+ private static readonly string _azureDevOpsBuildNumberAttribute = "AzureDevOpsBuildNumber";
+ private static readonly string[] _ignoredAttributes = [_buildIdAttribute, _azureDevOpsBuildNumberAttribute, "IsReleaseOnlyPackageVersion"];
+
+ public override bool Execute()
+ {
+ List assetManifestXmls = AssetManifest.Select(xmlPath => XDocument.Load(xmlPath.ItemSpec)).ToList();
+
+ VerifyAssetManifests(assetManifestXmls);
+
+ XElement mergedManifestRoot = assetManifestXmls.First().Root
+ ?? throw new ArgumentException("The root element of the asset manifest is null.");
+
+ // Set the BuildId and AzureDevOpsBuildNumber attributes to the value of VmrBuildNumber
+ mergedManifestRoot.SetAttributeValue(_buildIdAttribute, VmrBuildNumber);
+ mergedManifestRoot.SetAttributeValue(_azureDevOpsBuildNumberAttribute, VmrBuildNumber);
+
+ List packageElements = new();
+ List blobElements = new();
+
+ foreach (var assetManifestXml in assetManifestXmls)
+ {
+ packageElements.AddRange(assetManifestXml.Descendants("Package"));
+ blobElements.AddRange(assetManifestXml.Descendants("Blob"));
+ }
+
+ packageElements = packageElements.OrderBy(packageElement => packageElement.Attribute("Id")?.Value).ToList();
+ blobElements = blobElements.OrderBy(blobElement => blobElement.Attribute("Id")?.Value).ToList();
+
+ XDocument verticalManifest = new(new XElement(mergedManifestRoot.Name, mergedManifestRoot.Attributes(), packageElements, blobElements));
+
+ File.WriteAllText(MergedAssetManifestOutputPath, verticalManifest.ToString());
+
+ return !Log.HasLoggedErrors;
+ }
+
+ private static void VerifyAssetManifests(IReadOnlyList assetManifestXmls)
+ {
+ if (assetManifestXmls.Count == 0)
+ {
+ throw new ArgumentException("No asset manifests were provided.");
+ }
+
+ HashSet rootAttributes = assetManifestXmls
+ .First()
+ .Root?
+ .Attributes()
+ .Select(attribute => attribute.ToString())
+ .ToHashSet()
+ ?? throw new ArgumentException("The root element of the asset manifest is null.");
+
+ if (assetManifestXmls.Skip(1).Any(manifest => manifest.Root?.Attributes().Count() != rootAttributes.Count))
+ {
+ throw new ArgumentException("The asset manifests do not have the same number of root attributes.");
+ }
+
+ if (assetManifestXmls.Skip(1).Any(assetManifestXml =>
+ !assetManifestXml.Root?.Attributes().Select(attribute => attribute.ToString())
+ .All(attribute =>
+ // Ignore BuildId and AzureDevOpsBuildNumber attributes, they're different for different repos,
+ // TODO this should be fixed with https://github.com/dotnet/source-build/issues/3934
+ _ignoredAttributes.Any(ignoredAttribute => attribute.StartsWith(ignoredAttribute)) || rootAttributes.Contains(attribute))
+ ?? false))
+ {
+ throw new ArgumentException("The asset manifests do not have the same root attributes.");
+ }
+ }
+ }
+}
diff --git a/src/SourceBuild/content/eng/tools/tasks/Microsoft.DotNet.UnifiedBuild.Tasks/Microsoft.DotNet.UnifiedBuild.Tasks.csproj b/src/SourceBuild/content/eng/tools/tasks/Microsoft.DotNet.UnifiedBuild.Tasks/Microsoft.DotNet.UnifiedBuild.Tasks.csproj
new file mode 100644
index 000000000000..63880f2020cf
--- /dev/null
+++ b/src/SourceBuild/content/eng/tools/tasks/Microsoft.DotNet.UnifiedBuild.Tasks/Microsoft.DotNet.UnifiedBuild.Tasks.csproj
@@ -0,0 +1,16 @@
+
+
+
+ $(NetCurrent)
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/SourceBuild/content/eng/tools/tasks/Microsoft.DotNet.UnifiedBuild.Tasks/Models/VersionDetailsXml.cs b/src/SourceBuild/content/eng/tools/tasks/Microsoft.DotNet.UnifiedBuild.Tasks/Models/VersionDetailsXml.cs
new file mode 100644
index 000000000000..538340ec4a1d
--- /dev/null
+++ b/src/SourceBuild/content/eng/tools/tasks/Microsoft.DotNet.UnifiedBuild.Tasks/Models/VersionDetailsXml.cs
@@ -0,0 +1,39 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Xml.Serialization;
+
+namespace Microsoft.DotNet.UnifiedBuild.Tasks.Models
+{
+ [XmlRoot("Dependencies")]
+ public class VersionDetails
+ {
+ [XmlArray("ToolsetDependencies")]
+ public Dependency[] ToolsetDependencies { get; set; }
+ [XmlArray("ProductDependencies")]
+ public Dependency[] ProductDependencies { get; set; }
+ }
+
+ public class Dependency
+ {
+ [XmlAttribute]
+ public string Name { get; set; }
+ [XmlAttribute]
+ public string Version { get; set; }
+ [XmlAttribute]
+ public string CoherentParentDependency { get; set; }
+ [XmlAttribute]
+ public bool Pinned { get; set; }
+ // Uri type isn't serializable, so use a string instead
+ public string Uri { get; set; }
+ public string Sha { get; set; }
+ [XmlElement("RepoName")]
+ public string[] RepoNames { get; set; }
+
+ public override string ToString()
+ {
+ return $"{Name}@{Version} ({Uri}@{Sha})";
+ }
+ }
+}
diff --git a/src/SourceBuild/content/eng/tools/tasks/Microsoft.DotNet.UnifiedBuild.Tasks/ReadNuGetPackageInfos.cs b/src/SourceBuild/content/eng/tools/tasks/Microsoft.DotNet.UnifiedBuild.Tasks/ReadNuGetPackageInfos.cs
new file mode 100644
index 000000000000..59ce680e4e93
--- /dev/null
+++ b/src/SourceBuild/content/eng/tools/tasks/Microsoft.DotNet.UnifiedBuild.Tasks/ReadNuGetPackageInfos.cs
@@ -0,0 +1,54 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using Microsoft.Build.Framework;
+using Microsoft.Build.Utilities;
+using NuGet.Packaging;
+using NuGet.Packaging.Core;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace Microsoft.DotNet.UnifiedBuild.Tasks
+{
+ public class ReadNuGetPackageInfos : Task
+ {
+ [Required]
+ public string[] PackagePaths { get; set; }
+
+ ///
+ /// %(Identity): Path to the original nupkg.
+ /// %(PackageId): Identity of the package.
+ /// %(PackageVersion): Version of the package.
+ ///
+ [Output]
+ public ITaskItem[] PackageInfoItems { get; set; }
+
+ public override bool Execute()
+ {
+ PackageInfoItems = PackagePaths
+ .Select(p =>
+ {
+ PackageIdentity identity = ReadIdentity(p);
+ return new TaskItem(
+ p,
+ new Dictionary
+ {
+ ["PackageId"] = identity.Id,
+ ["PackageVersion"] = identity.Version.OriginalVersion
+ });
+ })
+ .ToArray();
+
+ return !Log.HasLoggedErrors;
+ }
+
+ public static PackageIdentity ReadIdentity(string nupkgFile)
+ {
+ using (var reader = new PackageArchiveReader(nupkgFile))
+ {
+ return reader.GetIdentity();
+ }
+ }
+ }
+}
diff --git a/src/SourceBuild/content/eng/tools/tasks/Microsoft.DotNet.UnifiedBuild.Tasks/RemoveInternetSourcesFromNuGetConfig.cs b/src/SourceBuild/content/eng/tools/tasks/Microsoft.DotNet.UnifiedBuild.Tasks/RemoveInternetSourcesFromNuGetConfig.cs
new file mode 100644
index 000000000000..ee379657f079
--- /dev/null
+++ b/src/SourceBuild/content/eng/tools/tasks/Microsoft.DotNet.UnifiedBuild.Tasks/RemoveInternetSourcesFromNuGetConfig.cs
@@ -0,0 +1,86 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Xml;
+using System.Xml.Linq;
+using Microsoft.Build.Framework;
+using Microsoft.Build.Utilities;
+
+namespace Microsoft.DotNet.UnifiedBuild.Tasks
+{
+ /*
+ * This task removes internet sources from a given NuGet.config. In the offline build mode, it removes all
+ * feeds that begin with http or https. In the online build mode, it removes only the internal dnceng feeds that
+ * source-build does not have access to.
+ */
+ public class RemoveInternetSourcesFromNuGetConfig : Task
+ {
+ [Required]
+ public string NuGetConfigFile { get; set; }
+
+ ///
+ /// Whether to work in offline mode (remove all internet sources) or online mode (remove only authenticated sources)
+ ///
+ public bool BuildWithOnlineFeeds { get; set; }
+
+ ///
+ /// A list of prefix strings that make the task keep a package source unconditionally. For
+ /// example, a source named 'darc-pub-dotnet-aspnetcore-e81033e' will be kept if the prefix
+ /// 'darc-pub-dotnet-aspnetcore-' is in this list.
+ ///
+ public string[] KeepFeedPrefixes { get; set; }
+
+ public override bool Execute()
+ {
+ string xml = File.ReadAllText(NuGetConfigFile);
+ string newLineChars = FileUtilities.DetectNewLineChars(xml);
+ XDocument d = XDocument.Parse(xml);
+ XElement packageSourcesElement = d.Root.Descendants().First(e => e.Name == "packageSources");
+ XElement disabledPackageSourcesElement = d.Root.Descendants().FirstOrDefault(e => e.Name == "disabledPackageSources");
+
+ IEnumerable local = packageSourcesElement.Descendants().Where(e =>
+ {
+ if (e.Name == "add")
+ {
+ string feedName = e.Attribute("key").Value;
+ if (KeepFeedPrefixes
+ ?.Any(prefix => feedName.StartsWith(prefix, StringComparison.OrdinalIgnoreCase))
+ == true)
+ {
+ return true;
+ }
+
+ string feedUrl = e.Attribute("value").Value;
+ if (BuildWithOnlineFeeds)
+ {
+ return !( feedUrl.StartsWith("https://pkgs.dev.azure.com/dnceng/_packaging", StringComparison.OrdinalIgnoreCase) ||
+ feedUrl.StartsWith("https://pkgs.dev.azure.com/dnceng/internal/_packaging", StringComparison.OrdinalIgnoreCase) );
+ }
+ else
+ {
+ return !(feedUrl.StartsWith("http://", StringComparison.OrdinalIgnoreCase) || feedUrl.StartsWith("https://", StringComparison.OrdinalIgnoreCase));
+ }
+ }
+
+ return true;
+ });
+
+ packageSourcesElement.ReplaceNodes(local.ToArray());
+
+ // Remove disabledPackageSources element so if any internal packages remain, they are used in source-build
+ disabledPackageSourcesElement?.ReplaceNodes(new XElement("clear"));
+
+ using (var w = XmlWriter.Create(NuGetConfigFile, new XmlWriterSettings { NewLineChars = newLineChars, Indent = true }))
+ {
+ d.Save(w);
+ }
+
+ return true;
+ }
+ }
+}
diff --git a/src/SourceBuild/content/eng/tools/tasks/Microsoft.DotNet.UnifiedBuild.Tasks/UpdateJson.cs b/src/SourceBuild/content/eng/tools/tasks/Microsoft.DotNet.UnifiedBuild.Tasks/UpdateJson.cs
new file mode 100644
index 000000000000..cffab0b2b05d
--- /dev/null
+++ b/src/SourceBuild/content/eng/tools/tasks/Microsoft.DotNet.UnifiedBuild.Tasks/UpdateJson.cs
@@ -0,0 +1,82 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.IO;
+using System.Linq;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+using Microsoft.Build.Framework;
+using Microsoft.Build.Utilities;
+
+namespace Microsoft.DotNet.UnifiedBuild.Tasks
+{
+ // Takes a path to a path to a json file and a
+ // string that represents a dotted path to an attribute
+ // and updates that attribute with the new value provided.
+ public class UpdateJson : Task
+ {
+ [Required]
+ public string JsonFilePath { get; set; }
+
+ [Required]
+ public string PathToAttribute { get; set; }
+
+ // New attribute value. May be null. If null,
+ // the token is removed.
+ public string NewAttributeValue { get; set; }
+
+ public bool SkipUpdateIfMissingKey { get; set; }
+
+ public override bool Execute()
+ {
+ // Using a character that isn't allowed in the package id
+ const char Delimiter = ':';
+
+ string json = File.ReadAllText(JsonFilePath);
+ string newLineChars = FileUtilities.DetectNewLineChars(json);
+ JObject jsonObj = JObject.Parse(json);
+
+ string[] escapedPathToAttributeParts = PathToAttribute.Split(Delimiter);
+ for (int i = 0; i < escapedPathToAttributeParts.Length; ++i)
+ {
+ escapedPathToAttributeParts[i] = escapedPathToAttributeParts[i];
+ }
+ UpdateAttribute(jsonObj, escapedPathToAttributeParts, NewAttributeValue);
+
+ File.WriteAllText(JsonFilePath, FileUtilities.NormalizeNewLineChars(jsonObj.ToString(), newLineChars));
+ return true;
+ }
+
+ private void UpdateAttribute(JToken jsonObj, string[] path, string newValue)
+ {
+ string pathItem = path[0];
+ if (jsonObj[pathItem] == null)
+ {
+ string message = $"Path item [{nameof(PathToAttribute)}] not found in json file.";
+ if (SkipUpdateIfMissingKey)
+ {
+ Log.LogMessage(MessageImportance.Low, $"Skipping update: {message} {pathItem}");
+ return;
+ }
+ throw new ArgumentException(message, pathItem);
+ }
+
+ if (path.Length == 1)
+ {
+ if (newValue == null)
+ {
+ jsonObj[pathItem].Parent.Remove();
+ }
+ else
+ {
+ jsonObj[pathItem] = newValue;
+ }
+ return;
+ }
+
+ UpdateAttribute(jsonObj[pathItem], path.Skip(1).ToArray(), newValue);
+ }
+ }
+}
diff --git a/src/SourceBuild/content/eng/tools/tasks/Microsoft.DotNet.UnifiedBuild.Tasks/UpdateNuGetConfigPackageSourcesMappings.cs b/src/SourceBuild/content/eng/tools/tasks/Microsoft.DotNet.UnifiedBuild.Tasks/UpdateNuGetConfigPackageSourcesMappings.cs
new file mode 100644
index 000000000000..91d514ac9fe6
--- /dev/null
+++ b/src/SourceBuild/content/eng/tools/tasks/Microsoft.DotNet.UnifiedBuild.Tasks/UpdateNuGetConfigPackageSourcesMappings.cs
@@ -0,0 +1,448 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Globalization;
+using System.IO;
+using System.IO.Compression;
+using System.Linq;
+using System.Xml;
+using System.Xml.Linq;
+using Microsoft.Build.Framework;
+using Microsoft.Build.Utilities;
+
+namespace Microsoft.DotNet.UnifiedBuild.Tasks
+{
+ /*
+ * This task updates the package source mappings in the NuGet.Config using the following logic:
+ * Add all packages from current source-build sources, i.e. source-built-*, reference-packages.
+ * For previously source-built sources (PSB), add only the packages that do not exist in any of the current source-built sources.
+ * Also add PSB packages if that package version does not exist in current package sources.
+ * In offline build, remove all existing package source mappings for online sources.
+ * In online build, add online source mappings for all discovered packages from local sources.
+ * In online build, if NuGet.config didn't originally have any mappings, additionally,
+ * add default "*" pattern to all online source mappings.
+ */
+ public class UpdateNuGetConfigPackageSourcesMappings : Task
+ {
+ [Required]
+ public string NuGetConfigFile { get; set; }
+
+ ///
+ /// Whether to work in offline mode (remove all internet sources) or online mode (remove only authenticated sources)
+ ///
+ public bool BuildWithOnlineFeeds { get; set; }
+
+ ///
+ /// A list of all source-build specific NuGet sources.
+ ///
+ public string[] SourceBuildSources { get; set; }
+
+ [Required]
+ public string SbrpRepoSrcPath { get; set; }
+
+ [Required]
+ public string SourceBuiltSourceNamePrefix { get; set; }
+
+ public string SbrpCacheSourceName { get; set; }
+
+ public string ReferencePackagesSourceName { get; set; }
+
+ public string PreviouslySourceBuiltSourceName { get; set; }
+
+ public string PrebuiltSourceName { get; set; }
+
+ public string[] CustomSources { get; set; }
+
+ // allSourcesPackages and oldSourceMappingPatterns contain 'package source', 'list of packages' mappings
+ private Dictionary> allSourcesPackages = [];
+ private Dictionary> oldSourceMappingPatterns = [];
+
+ // All other dictionaries are: 'package id', 'list of package versions'
+ private Dictionary> currentPackages = [];
+ private Dictionary> referencePackages = [];
+ private Dictionary> previouslySourceBuiltPackages = [];
+ private Dictionary> prebuiltPackages = [];
+
+ public override bool Execute()
+ {
+ string xml = File.ReadAllText(NuGetConfigFile);
+ string newLineChars = FileUtilities.DetectNewLineChars(xml);
+ XDocument document = XDocument.Parse(xml);
+ XElement pkgSourcesElement = document.Root.Descendants().FirstOrDefault(e => e.Name == "packageSources");
+ if (pkgSourcesElement == null)
+ {
+ Log.LogMessage(MessageImportance.Low, "Package sources are missing.");
+
+ return true;
+ }
+
+ XElement pkgSrcMappingElement = document.Root.Descendants().FirstOrDefault(e => e.Name == "packageSourceMapping");
+ if (pkgSrcMappingElement == null)
+ {
+ pkgSrcMappingElement = new XElement("packageSourceMapping");
+ document.Root.Add(pkgSrcMappingElement);
+ }
+
+ DiscoverPackagesFromAllSourceBuildSources(pkgSourcesElement);
+
+ // Discover all SBRP packages if source-build-reference-package-cache source is present in NuGet.config
+ XElement sbrpCacheSourceElement = GetElement(pkgSourcesElement, "add", SbrpCacheSourceName);
+ if (sbrpCacheSourceElement != null)
+ {
+ DiscoverPackagesFromSbrpCacheSource();
+ }
+
+ // If building online, enumerate any existing package source mappings and filter
+ // to remove packages that are present in any local source-build source
+ if (BuildWithOnlineFeeds && pkgSrcMappingElement != null)
+ {
+ GetExistingFilteredSourceMappings(pkgSrcMappingElement);
+ }
+
+ // Remove all packageSourceMappings
+ pkgSrcMappingElement.ReplaceNodes(new XElement("clear"));
+
+ XElement pkgSrcMappingClearElement = pkgSrcMappingElement.Descendants().FirstOrDefault(e => e.Name == "clear");
+
+ // Add package source mappings for local package sources
+ foreach (string packageSource in allSourcesPackages.Keys)
+ {
+ // Skip sources with zero package patterns
+ if (allSourcesPackages[packageSource]?.Count > 0)
+ {
+ pkgSrcMappingClearElement.AddAfterSelf(GetPackageMappingsElementForSource(packageSource));
+ }
+ }
+
+ // When building online add the filtered mappings from original online sources.
+ // If there are none, add default mappings for all online sources.
+ if (BuildWithOnlineFeeds)
+ {
+ foreach (var entry in oldSourceMappingPatterns)
+ {
+ // Skip sources with zero package patterns
+ if (entry.Value?.Count > 0)
+ {
+ pkgSrcMappingElement.Add(GetPackageMappingsElementForSource(entry.Key, entry.Value));
+ }
+ }
+
+ // Union all package sources to get the distinct list. These will get added to
+ // all custom sources and all online sources based on the following logic:
+ // If there were existing mappings for online feeds, add cummulative mappings
+ // from all feeds to these two.
+ // If there were no existing mappings, add default mappings for all online feeds.
+ List packagePatterns = pkgSrcMappingElement.Descendants()
+ .Where(e => e.Name == "packageSource")
+ .SelectMany(e => e.Descendants().Where(e => e.Name == "package"))
+ .Select(e => e.Attribute("pattern").Value)
+ .Distinct()
+ .ToList();
+
+ if (oldSourceMappingPatterns.Count == 0)
+ {
+ packagePatterns.Add("*");
+ }
+
+ AddMappingsForCustomSources(pkgSrcMappingElement, pkgSourcesElement, packagePatterns);
+
+ if (oldSourceMappingPatterns.Count == 0)
+ {
+ AddMappingsForOnlineSources(pkgSrcMappingElement, pkgSourcesElement, packagePatterns);
+ }
+ }
+
+ using (var writer = XmlWriter.Create(NuGetConfigFile, new XmlWriterSettings { NewLineChars = newLineChars, Indent = true }))
+ {
+ document.Save(writer);
+ }
+
+ return true;
+ }
+
+ private void AddMappingsForCustomSources(XElement pkgSrcMappingElement, XElement pkgSourcesElement, List packagePatterns)
+ {
+ if (CustomSources == null)
+ {
+ return;
+ }
+
+ foreach (string sourceName in CustomSources)
+ {
+ if (null != GetElement(pkgSourcesElement, "add", sourceName))
+ {
+ ReplaceSourceMappings(pkgSrcMappingElement, sourceName, packagePatterns);
+ }
+ }
+ }
+
+ private void ReplaceSourceMappings(XElement pkgSrcMappingElement, string sourceName, List packagePatterns)
+ {
+ XElement pkgSrc = new XElement("packageSource", new XAttribute("key", sourceName));
+ foreach (string packagePattern in packagePatterns)
+ {
+ pkgSrc.Add(new XElement("package", new XAttribute("pattern", packagePattern)));
+ }
+
+ XElement existingPkgSrcElement = GetElement(pkgSrcMappingElement, "packageSource", sourceName);
+ if (existingPkgSrcElement != null)
+ {
+ existingPkgSrcElement.ReplaceWith(pkgSrc);
+ }
+ else
+ {
+ pkgSrcMappingElement.Add(pkgSrc);
+ }
+ }
+
+ private void AddMappingsForOnlineSources(XElement pkgSrcMappingElement, XElement pkgSourcesElement, List packagePatterns)
+ {
+ foreach (string sourceName in pkgSourcesElement
+ .Descendants()
+ .Where(e => e.Name == "add" &&
+ !SourceBuildSources.Contains(e.Attribute("key").Value) &&
+ // SBRP Cache source is not in SourceBuildSources, skip it as it's not an online source
+ !(e.Attribute("key").Value == SbrpCacheSourceName))
+ .Select(e => e.Attribute("key").Value)
+ .Distinct())
+ {
+ ReplaceSourceMappings(pkgSrcMappingElement, sourceName, packagePatterns);
+ }
+ }
+
+ private XElement GetPackageMappingsElementForSource(string key, List value)
+ {
+ XElement pkgSrc = new XElement("packageSource", new XAttribute("key", key));
+ foreach (string pattern in value)
+ {
+ pkgSrc.Add(new XElement("package", new XAttribute("pattern", pattern)));
+ }
+
+ return pkgSrc;
+ }
+
+ private XElement GetPackageMappingsElementForSource(string packageSource)
+ {
+ bool isCurrentSourceBuiltSource =
+ packageSource.StartsWith(SourceBuiltSourceNamePrefix) ||
+ packageSource.Equals(SbrpCacheSourceName) ||
+ packageSource.Equals(ReferencePackagesSourceName);
+
+ XElement pkgSrc = new XElement("packageSource", new XAttribute("key", packageSource));
+ foreach (string packagePattern in allSourcesPackages[packageSource])
+ {
+ // Add all packages from current source-built sources.
+ // For previously source-built and prebuilt sources add only packages
+ // where version does not exist in current source-built sources.
+ if (isCurrentSourceBuiltSource || !currentPackages.ContainsKey(packagePattern))
+ {
+ pkgSrc.Add(new XElement("package", new XAttribute("pattern", packagePattern)));
+ }
+ else if (packageSource.Equals(PreviouslySourceBuiltSourceName))
+ {
+ AddPackageSourceMappingIfPackageVersionsNotInCurrentPackages(pkgSrc, packagePattern, previouslySourceBuiltPackages);
+ }
+ else if (packageSource.Equals(PrebuiltSourceName))
+ {
+ AddPackageSourceMappingIfPackageVersionsNotInCurrentPackages(pkgSrc, packagePattern, prebuiltPackages);
+ }
+ else // unknown/unexpected source
+ {
+ throw new UnreachableException($"Unexpected package source name: {packageSource}");
+ }
+ }
+
+ return pkgSrc;
+ }
+
+ private void AddPackageSourceMappingIfPackageVersionsNotInCurrentPackages(XElement pkgSrc, string packagePattern, Dictionary> packages)
+ {
+ foreach (string version in packages[packagePattern])
+ {
+ // If any package version is in current packages, skip this package pattern
+ if (currentPackages[packagePattern].Contains(version))
+ {
+ return;
+ }
+ }
+
+ pkgSrc.Add(new XElement("package", new XAttribute("pattern", packagePattern)));
+ }
+
+ private void DiscoverPackagesFromAllSourceBuildSources(XElement pkgSourcesElement)
+ {
+ foreach (string packageSource in SourceBuildSources)
+ {
+ XElement sourceElement = GetElement(pkgSourcesElement, "add", packageSource);
+ if (sourceElement == null)
+ {
+ continue;
+ }
+
+ string path = sourceElement.Attribute("value").Value;
+ if (!Directory.Exists(path))
+ {
+ continue;
+ }
+
+ string[] packages = Directory.GetFiles(path, "*.nupkg", SearchOption.AllDirectories);
+ foreach (string package in packages)
+ {
+ NupkgInfo info = GetNupkgInfo(package);
+ string id = info.Id.ToLower();
+ string version = info.Version.ToLower();
+
+ // Add package with version to appropriate hashtable
+ if (packageSource.StartsWith(SourceBuiltSourceNamePrefix))
+ {
+ AddToDictionary(currentPackages, id, version);
+ }
+ else if (packageSource.Equals(ReferencePackagesSourceName))
+ {
+ AddToDictionary(referencePackages, id, version);
+ }
+ else if (packageSource.Equals(PreviouslySourceBuiltSourceName))
+ {
+ AddToDictionary(previouslySourceBuiltPackages, id, version);
+ }
+ else if (packageSource.Equals(PrebuiltSourceName))
+ {
+ AddToDictionary(prebuiltPackages, id, version);
+ }
+ else // unknown/unexpected source
+ {
+ throw new UnreachableException($"Unexpected package source name: {packageSource}");
+ }
+
+ AddToDictionary(allSourcesPackages, packageSource, id);
+ }
+ }
+ }
+
+ private void DiscoverPackagesFromSbrpCacheSource()
+ {
+ // 'source-build-reference-package-cache' is a dynamic source, populated by SBRP build.
+ // Discover all SBRP packages from checked in nuspec files.
+
+ if (!Directory.Exists(SbrpRepoSrcPath))
+ {
+ throw new InvalidDataException(string.Format(CultureInfo.CurrentCulture, "SBRP repo root does not exist in expected path: {0}", SbrpRepoSrcPath));
+ }
+
+ string[] nuspecFiles = Directory.GetFiles(SbrpRepoSrcPath, "*.nuspec", SearchOption.AllDirectories);
+ foreach (string nuspecFile in nuspecFiles)
+ {
+ try
+ {
+ using Stream stream = File.OpenRead(nuspecFile);
+ NupkgInfo info = GetNupkgInfo(stream);
+ string id = info.Id.ToLower();
+ string version = info.Version.ToLower();
+
+ AddToDictionary(currentPackages, id, version);
+ AddToDictionary(allSourcesPackages, SbrpCacheSourceName, id);
+ }
+ catch (Exception ex)
+ {
+ throw new InvalidDataException(string.Format(CultureInfo.CurrentCulture, "Invalid nuspec file", nuspecFile), ex);
+ }
+ }
+ }
+
+ private XElement GetElement(XElement pkgSourcesElement, string name, string key)
+ {
+ return pkgSourcesElement.Descendants().FirstOrDefault(e => e.Name == name && e.Attribute("key").Value == key);
+ }
+
+ private void GetExistingFilteredSourceMappings(XElement pkgSrcMappingElement)
+ {
+ foreach (XElement packageSource in pkgSrcMappingElement.Descendants().Where(e => e.Name == "packageSource"))
+ {
+ List filteredPatterns = new List();
+ foreach (XElement package in packageSource.Descendants().Where(e => e.Name == "package"))
+ {
+ string pattern = package.Attribute("pattern").Value.ToLower();
+ if (!currentPackages.ContainsKey(pattern) &&
+ !referencePackages.ContainsKey(pattern) &&
+ !previouslySourceBuiltPackages.ContainsKey(pattern) &&
+ !prebuiltPackages.ContainsKey(pattern))
+ {
+ filteredPatterns.Add(pattern);
+ }
+ }
+
+ oldSourceMappingPatterns.Add(packageSource.Attribute("key").Value, filteredPatterns);
+ }
+ }
+
+ private void AddToDictionary(Dictionary> dictionary, string key, string value)
+ {
+ if (dictionary.TryGetValue(key, out List values))
+ {
+ if (!values.Contains(value))
+ {
+ values.Add(value);
+ }
+ }
+ else
+ {
+ dictionary.Add(key, [value]);
+ }
+ }
+
+ ///
+ /// Get nupkg info, id and version, from nupkg file.
+ ///
+ private NupkgInfo GetNupkgInfo(string path)
+ {
+ try
+ {
+ using Stream stream = File.OpenRead(path);
+ ZipArchive zipArchive = new(stream, ZipArchiveMode.Read);
+ foreach (ZipArchiveEntry entry in zipArchive.Entries)
+ {
+ if (entry.Name.EndsWith(".nuspec"))
+ {
+ using Stream nuspecFileStream = entry.Open();
+ return GetNupkgInfo(nuspecFileStream);
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ throw new InvalidDataException(string.Format(CultureInfo.CurrentCulture, "Invalid package", path), ex);
+ }
+
+ throw new InvalidDataException(string.Format(CultureInfo.CurrentCulture, "Did not extract nuspec file from package: {0}", path));
+ }
+
+ ///
+ /// Get nupkg info, id and version, from nuspec stream.
+ ///
+ private NupkgInfo GetNupkgInfo(Stream nuspecFileStream)
+ {
+ XDocument doc = XDocument.Load(nuspecFileStream, LoadOptions.PreserveWhitespace);
+ XElement metadataElement = doc.Descendants().First(c => c.Name.LocalName.ToString() == "metadata");
+ return new NupkgInfo(
+ metadataElement.Descendants().First(c => c.Name.LocalName.ToString() == "id").Value,
+ metadataElement.Descendants().First(c => c.Name.LocalName.ToString() == "version").Value);
+ }
+
+ private class NupkgInfo
+ {
+ public NupkgInfo(string id, string version)
+ {
+ Id = id;
+ Version = version;
+ }
+
+ public string Id { get; }
+ public string Version { get; }
+ }
+ }
+}
diff --git a/src/SourceBuild/content/eng/tools/tasks/Microsoft.DotNet.UnifiedBuild.Tasks/UsageReport/AnnotatedUsage.cs b/src/SourceBuild/content/eng/tools/tasks/Microsoft.DotNet.UnifiedBuild.Tasks/UsageReport/AnnotatedUsage.cs
new file mode 100644
index 000000000000..896be816cfcf
--- /dev/null
+++ b/src/SourceBuild/content/eng/tools/tasks/Microsoft.DotNet.UnifiedBuild.Tasks/UsageReport/AnnotatedUsage.cs
@@ -0,0 +1,34 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Xml.Linq;
+
+namespace Microsoft.DotNet.UnifiedBuild.Tasks.UsageReport
+{
+ public class AnnotatedUsage
+ {
+ public Usage Usage { get; set; }
+
+ public string Project { get; set; }
+ public string SourceBuildPackageIdCreator { get; set; }
+ public string ProdConPackageIdCreator { get; set; }
+ public bool EndsUpInOutput { get; set; }
+ public bool TestProjectByHeuristic { get; set; }
+ public bool TestProjectOnlyByHeuristic { get; set; }
+ public bool IsDirectDependency { get; set; }
+ public bool IsAutoReferenced { get; set; }
+
+ public XElement ToXml() => new XElement(
+ nameof(AnnotatedUsage),
+ Usage.ToXml().Attributes(),
+ Project.ToXAttributeIfNotNull(nameof(Project)),
+ SourceBuildPackageIdCreator.ToXAttributeIfNotNull(nameof(SourceBuildPackageIdCreator)),
+ ProdConPackageIdCreator.ToXAttributeIfNotNull(nameof(ProdConPackageIdCreator)),
+ TestProjectByHeuristic.ToXAttributeIfTrue(nameof(TestProjectByHeuristic)),
+ TestProjectOnlyByHeuristic.ToXAttributeIfTrue(nameof(TestProjectOnlyByHeuristic)),
+ IsDirectDependency.ToXAttributeIfTrue(nameof(IsDirectDependency)),
+ IsAutoReferenced.ToXAttributeIfTrue(nameof(IsAutoReferenced)),
+ EndsUpInOutput.ToXAttributeIfTrue(nameof(EndsUpInOutput)));
+ }
+}
diff --git a/src/SourceBuild/content/eng/tools/tasks/Microsoft.DotNet.UnifiedBuild.Tasks/UsageReport/Usage.cs b/src/SourceBuild/content/eng/tools/tasks/Microsoft.DotNet.UnifiedBuild.Tasks/UsageReport/Usage.cs
new file mode 100644
index 000000000000..b8817f2c22ad
--- /dev/null
+++ b/src/SourceBuild/content/eng/tools/tasks/Microsoft.DotNet.UnifiedBuild.Tasks/UsageReport/Usage.cs
@@ -0,0 +1,112 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using NuGet.Packaging.Core;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Xml.Linq;
+
+namespace Microsoft.DotNet.UnifiedBuild.Tasks.UsageReport
+{
+ public class Usage : IEquatable
+ {
+ public PackageIdentity PackageIdentity { get; set; }
+
+ public string AssetsFile { get; set; }
+
+ public bool IsDirectDependency { get; set; }
+
+ public bool IsAutoReferenced { get; set; }
+
+ ///
+ /// The Runtime ID this package is for, or null. Runtime packages (are assumed to) have the
+ /// id 'runtime.{rid}.{rest of id}'. We can't use a simple regex to grab this value since
+ /// the RID may have '.' in it, so this is saved in the build context where possible RIDs
+ /// are read from Microsoft.NETCore.Platforms.
+ ///
+ public string RuntimePackageRid { get; set; }
+
+ public XElement ToXml() => new XElement(
+ nameof(Usage),
+ PackageIdentity.ToXElement().Attributes(),
+ AssetsFile.ToXAttributeIfNotNull("File"),
+ IsDirectDependency.ToXAttributeIfTrue(nameof(IsDirectDependency)),
+ IsAutoReferenced.ToXAttributeIfTrue(nameof(IsAutoReferenced)),
+ RuntimePackageRid.ToXAttributeIfNotNull("Rid"));
+
+ public static Usage Parse(XElement xml) => new Usage
+ {
+ PackageIdentity = XmlParsingHelpers.ParsePackageIdentity(xml),
+ AssetsFile = xml.Attribute("File")?.Value,
+ IsDirectDependency = Convert.ToBoolean(xml.Attribute(nameof(IsDirectDependency))?.Value),
+ IsAutoReferenced = Convert.ToBoolean(xml.Attribute(nameof(IsAutoReferenced))?.Value),
+ RuntimePackageRid = xml.Attribute("Rid")?.Value
+ };
+
+ public static Usage Create(
+ string assetsFile,
+ PackageIdentity identity,
+ bool isDirectDependency,
+ bool isAutoReferenced,
+ IEnumerable possibleRuntimePackageRids)
+ {
+ return new Usage
+ {
+ AssetsFile = assetsFile,
+ PackageIdentity = identity,
+ IsDirectDependency = isDirectDependency,
+ IsAutoReferenced = isAutoReferenced,
+ RuntimePackageRid = possibleRuntimePackageRids
+ .Where(rid => identity.Id.StartsWith($"runtime.{rid}.", StringComparison.Ordinal))
+ .OrderByDescending(rid => rid.Length)
+ .FirstOrDefault()
+ };
+ }
+
+ public PackageIdentity GetIdentityWithoutRid()
+ {
+ if (!string.IsNullOrEmpty(RuntimePackageRid))
+ {
+ string prefix = $"runtime.{RuntimePackageRid}.";
+ if (PackageIdentity.Id.StartsWith(prefix, StringComparison.Ordinal))
+ {
+ return new PackageIdentity(
+ PackageIdentity.Id
+ .Remove(0, prefix.Length)
+ .Insert(0, "runtime.placeholder-rid."),
+ PackageIdentity.Version);
+ }
+ }
+ return PackageIdentity;
+ }
+
+ public override string ToString() =>
+ $"{PackageIdentity.Id} {PackageIdentity.Version} " +
+ (string.IsNullOrEmpty(RuntimePackageRid) ? "" : $"({RuntimePackageRid}) ") +
+ (string.IsNullOrEmpty(AssetsFile) ? "with unknown use" : $"used by '{AssetsFile}'");
+
+ public bool Equals(Usage other)
+ {
+ if (other is null)
+ {
+ return false;
+ }
+ if (ReferenceEquals(this, other))
+ {
+ return true;
+ }
+ return
+ Equals(PackageIdentity, other.PackageIdentity) &&
+ string.Equals(AssetsFile, other.AssetsFile, StringComparison.Ordinal) &&
+ string.Equals(RuntimePackageRid, other.RuntimePackageRid, StringComparison.Ordinal);
+ }
+
+ public override int GetHashCode() => (
+ PackageIdentity,
+ AssetsFile,
+ RuntimePackageRid
+ ).GetHashCode();
+ }
+}
diff --git a/src/SourceBuild/content/eng/tools/tasks/Microsoft.DotNet.UnifiedBuild.Tasks/UsageReport/UsageData.cs b/src/SourceBuild/content/eng/tools/tasks/Microsoft.DotNet.UnifiedBuild.Tasks/UsageReport/UsageData.cs
new file mode 100644
index 000000000000..4a2fc9ed9094
--- /dev/null
+++ b/src/SourceBuild/content/eng/tools/tasks/Microsoft.DotNet.UnifiedBuild.Tasks/UsageReport/UsageData.cs
@@ -0,0 +1,57 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using NuGet.Packaging.Core;
+using System.Linq;
+using System.Xml.Linq;
+
+namespace Microsoft.DotNet.UnifiedBuild.Tasks.UsageReport
+{
+ public class UsageData
+ {
+ public string CreatedByRid { get; set; }
+ public string[] ProjectDirectories { get; set; }
+ public PackageIdentity[] NeverRestoredTarballPrebuilts { get; set; }
+ public Usage[] Usages { get; set; }
+
+ public XElement ToXml() => new XElement(
+ nameof(UsageData),
+ CreatedByRid == null ? null : new XElement(
+ nameof(CreatedByRid),
+ CreatedByRid),
+ ProjectDirectories?.Any() != true ? null : new XElement(
+ nameof(ProjectDirectories),
+ ProjectDirectories
+ .Select(dir => new XElement("Dir", dir))),
+ NeverRestoredTarballPrebuilts?.Any() != true ? null : new XElement(
+ nameof(NeverRestoredTarballPrebuilts),
+ NeverRestoredTarballPrebuilts
+ .OrderBy(id => id)
+ .Select(id => id.ToXElement())),
+ Usages?.Any() != true ? null : new XElement(
+ nameof(Usages),
+ Usages
+ .OrderBy(u => u.PackageIdentity)
+ .ThenByOrdinal(u => u.AssetsFile)
+ .Select(u => u.ToXml())));
+
+ public static UsageData Parse(XElement xml) => new UsageData
+ {
+ CreatedByRid = xml.Element(nameof(CreatedByRid))
+ ?.Value,
+ ProjectDirectories = xml.Element(nameof(ProjectDirectories)) == null ? new string[] { } :
+ xml.Element(nameof(ProjectDirectories)).Elements()
+ .Select(x => x.Value)
+ .ToArray(),
+ NeverRestoredTarballPrebuilts = xml.Element(nameof(NeverRestoredTarballPrebuilts)) == null ? new PackageIdentity[] { } :
+ xml.Element(nameof(NeverRestoredTarballPrebuilts)).Elements()
+ .Select(XmlParsingHelpers.ParsePackageIdentity)
+ .ToArray(),
+ Usages = xml.Element(nameof(Usages)) == null ? new Usage[] { } :
+ xml.Element(nameof(Usages)).Elements()
+ .Select(Usage.Parse)
+ .ToArray()
+ };
+ }
+}
diff --git a/src/SourceBuild/content/eng/tools/tasks/Microsoft.DotNet.UnifiedBuild.Tasks/UsageReport/ValidateUsageAgainstBaseline.cs b/src/SourceBuild/content/eng/tools/tasks/Microsoft.DotNet.UnifiedBuild.Tasks/UsageReport/ValidateUsageAgainstBaseline.cs
new file mode 100644
index 000000000000..834e8b5563cb
--- /dev/null
+++ b/src/SourceBuild/content/eng/tools/tasks/Microsoft.DotNet.UnifiedBuild.Tasks/UsageReport/ValidateUsageAgainstBaseline.cs
@@ -0,0 +1,149 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using Microsoft.Build.Framework;
+using Microsoft.Build.Utilities;
+using NuGet.Packaging.Core;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Xml.Linq;
+
+namespace Microsoft.DotNet.UnifiedBuild.Tasks.UsageReport
+{
+ public class ValidateUsageAgainstBaseline : Task
+ {
+ [Required]
+ public string DataFile { get; set; }
+
+ [Required]
+ public string OutputBaselineFile { get; set; }
+
+ [Required]
+ public string OutputReportFile { get; set; }
+
+ public bool AllowTestProjectUsage { get; set; }
+
+ public string BaselineDataFile { get; set; }
+
+ public override bool Execute()
+ {
+ var used = UsageData.Parse(XElement.Parse(File.ReadAllText(DataFile)));
+
+ IEnumerable baselineUsages;
+ if (File.Exists(BaselineDataFile))
+ {
+ baselineUsages = UsageData.Parse(XElement.Parse(File.ReadAllText(BaselineDataFile))).Usages;
+ }
+ else
+ {
+ baselineUsages = Enumerable.Empty();
+ }
+
+ Comparison diff = Compare(
+ used.Usages.Select(u => u.GetIdentityWithoutRid()).Distinct(),
+ baselineUsages.Select(u => u.GetIdentityWithoutRid()).Distinct());
+
+ var report = new XElement("BaselineComparison");
+
+ bool tellUserToUpdateBaseline = false;
+
+ if (diff.Added.Any())
+ {
+ tellUserToUpdateBaseline = true;
+ Log.LogError(
+ $"{diff.Added.Length} new packages used not in baseline! See report " +
+ $"at {OutputReportFile} for more information. Package IDs are:\n" +
+ string.Join("\n", diff.Added.Select(u => u.ToString())));
+
+ // In the report, list full usage info, not only identity.
+ report.Add(
+ new XElement(
+ "New",
+ used.Usages
+ .Where(u => diff.Added.Contains(u.GetIdentityWithoutRid()))
+ .Select(u => u.ToXml())));
+ }
+ if (diff.Removed.Any())
+ {
+ tellUserToUpdateBaseline = true;
+ Log.LogMessage(
+ MessageImportance.High,
+ $"{diff.Removed.Length} packages in baseline weren't used!");
+
+ report.Add(new XElement("Removed", diff.Removed.Select(id => id.ToXElement())));
+ }
+ if (diff.Unchanged.Any())
+ {
+ Log.LogMessage(
+ MessageImportance.High,
+ $"{diff.Unchanged.Length} packages used as expected in the baseline.");
+ }
+
+ if (!AllowTestProjectUsage)
+ {
+ Usage[] testProjectUsages = used.Usages
+ .Where(WriteUsageReports.IsTestUsageByHeuristic)
+ .ToArray();
+
+ if (testProjectUsages.Any())
+ {
+ string[] projects = testProjectUsages
+ .Select(u => u.AssetsFile)
+ .Distinct()
+ .ToArray();
+
+ Log.LogError(
+ $"{testProjectUsages.Length} forbidden test usages found in " +
+ $"{projects.Length} projects:\n" +
+ string.Join("\n", projects));
+ }
+ }
+
+ // Simplify the used data to what is necessary for a baseline, to reduce file size.
+ foreach (var usage in used.Usages)
+ {
+ usage.AssetsFile = null;
+ }
+ used.Usages = used.Usages.Distinct().ToArray();
+
+ Directory.CreateDirectory(Path.GetDirectoryName(OutputBaselineFile));
+ File.WriteAllText(OutputBaselineFile, used.ToXml().ToString());
+
+ Directory.CreateDirectory(Path.GetDirectoryName(OutputReportFile));
+ File.WriteAllText(OutputReportFile, report.ToString());
+
+ if (tellUserToUpdateBaseline)
+ {
+ Log.LogWarning(
+ "Prebuilt usages are different from the baseline. If detected changes are " +
+ "acceptable, update baseline with:\n" +
+ $"cp '{OutputBaselineFile}' '{BaselineDataFile}'");
+ }
+
+ return !Log.HasLoggedErrors;
+ }
+
+ private static Comparison Compare(IEnumerable actual, IEnumerable baseline)
+ {
+ return new Comparison(actual.ToArray(), baseline.ToArray());
+ }
+
+ private class Comparison
+ {
+ public T[] Added { get; }
+ public T[] Removed { get; }
+ public T[] Unchanged { get; }
+
+ public Comparison(
+ IEnumerable actual,
+ IEnumerable baseline)
+ {
+ Added = actual.Except(baseline).ToArray();
+ Removed = baseline.Except(actual).ToArray();
+ Unchanged = actual.Intersect(baseline).ToArray();
+ }
+ }
+ }
+}
diff --git a/src/SourceBuild/content/eng/tools/tasks/Microsoft.DotNet.UnifiedBuild.Tasks/UsageReport/WritePackageUsageData.cs b/src/SourceBuild/content/eng/tools/tasks/Microsoft.DotNet.UnifiedBuild.Tasks/UsageReport/WritePackageUsageData.cs
new file mode 100644
index 000000000000..073c92644041
--- /dev/null
+++ b/src/SourceBuild/content/eng/tools/tasks/Microsoft.DotNet.UnifiedBuild.Tasks/UsageReport/WritePackageUsageData.cs
@@ -0,0 +1,284 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using Microsoft.Build.Framework;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+using NuGet.Packaging.Core;
+using NuGet.Versioning;
+using System;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.IO;
+using System.IO.Compression;
+using System.Linq;
+using System.Threading.Tasks;
+using Task = Microsoft.Build.Utilities.Task;
+
+namespace Microsoft.DotNet.UnifiedBuild.Tasks.UsageReport
+{
+ public class WritePackageUsageData : Task
+ {
+ public string[] RestoredPackageFiles { get; set; }
+ public string[] TarballPrebuiltPackageFiles { get; set; }
+ public string[] ReferencePackageFiles { get; set; }
+ public string[] SourceBuiltPackageFiles { get; set; }
+
+ ///
+ /// Specific PackageInfo items to check for usage. An alternative to passing lists of nupkgs
+ /// when the nupkgs have already been parsed to get package info items.
+ ///
+ /// %(Identity): Path to the original nupkg.
+ /// %(PackageId): Identity of the package.
+ /// %(PackageVersion): Version of the package.
+ ///
+ public ITaskItem[] NuGetPackageInfos { get; set; }
+
+ ///
+ /// runtime.json files (from Microsoft.NETCore.Platforms) to use to look for the set of all
+ /// possible runtimes. This is used to determine which part of a package id is its
+ /// 'runtime.{rid}.' prefix, if it has the prefix.
+ ///
+ public string[] PlatformsRuntimeJsonFiles { get; set; }
+
+ ///
+ /// Keep track of the RID built that caused these usages.
+ ///
+ public string TargetRid { get; set; }
+
+ ///
+ /// Project directories to scan for project.assets.json files. If these directories contain
+ /// one another, the project.assets.json files is reported as belonging to the first project
+ /// directory that contains it. For useful results, put the leafmost directories first.
+ ///
+ /// This isn't used here, but it's included in the usage data so report generation can
+ /// happen independently of commits that add/remove submodules.
+ ///
+ public string[] ProjectDirectories { get; set; }
+
+ ///
+ /// A root dir that contains all ProjectDirectories. This is used to find the relative path
+ /// of each usage.
+ ///
+ [Required]
+ public string RootDir { get; set; }
+
+ ///
+ /// project.assets.json files to ignore, for example, because they are checked-in assets not
+ /// generated during source-build and cause false positives.
+ ///
+ public string[] IgnoredProjectAssetsJsonFiles { get; set; }
+
+ ///
+ /// Output usage data JSON file path.
+ ///
+ [Required]
+ public string DataFile { get; set; }
+
+ ///
+ /// If passed, the path of the archive file to generate that includes a copy of all
+ /// project.asset.json files found.
+ ///
+ public string ProjectAssetsJsonArchiveFile { get; set; }
+
+ public override bool Execute()
+ {
+ DateTime startTime = DateTime.Now;
+ Log.LogMessage(MessageImportance.High, "Writing package usage data...");
+
+ string[] projectDirectoriesOutsideRoot = ProjectDirectories.NullAsEmpty()
+ .Where(dir => !dir.StartsWith(RootDir, StringComparison.Ordinal))
+ .ToArray();
+
+ if (projectDirectoriesOutsideRoot.Any())
+ {
+ throw new ArgumentException(
+ $"All ProjectDirectories must be in RootDir '{RootDir}', but found " +
+ string.Join(", ", projectDirectoriesOutsideRoot));
+ }
+
+ Log.LogMessage(MessageImportance.Low, "Finding set of RIDs...");
+
+ string[] possibleRids = PlatformsRuntimeJsonFiles.NullAsEmpty()
+ .SelectMany(ReadRidsFromRuntimeJson)
+ .Distinct()
+ .ToArray();
+
+ Log.LogMessage(MessageImportance.Low, "Reading package identities...");
+
+ PackageIdentity[] restored = RestoredPackageFiles.NullAsEmpty()
+ .Select(ReadNuGetPackageInfos.ReadIdentity)
+ .Distinct()
+ .ToArray();
+
+ PackageIdentity[] tarballPrebuilt = TarballPrebuiltPackageFiles.NullAsEmpty()
+ .Select(ReadNuGetPackageInfos.ReadIdentity)
+ .Distinct()
+ .ToArray();
+
+ PackageIdentity[] referencePackages = ReferencePackageFiles.NullAsEmpty()
+ .Select(ReadNuGetPackageInfos.ReadIdentity)
+ .Distinct()
+ .ToArray();
+
+ PackageIdentity[] sourceBuilt = SourceBuiltPackageFiles.NullAsEmpty()
+ .Select(ReadNuGetPackageInfos.ReadIdentity)
+ .Distinct()
+ .ToArray();
+
+ IEnumerable prebuilt = restored.Except(sourceBuilt).Except(referencePackages);
+
+ PackageIdentity[] toCheck = NuGetPackageInfos.NullAsEmpty()
+ .Select(item => new PackageIdentity(
+ item.GetMetadata("PackageId"),
+ NuGetVersion.Parse(item.GetMetadata("PackageVersion"))))
+ .Concat(prebuilt)
+ .ToArray();
+
+ Log.LogMessage(MessageImportance.Low, "Finding project.assets.json files...");
+
+ string[] assetFiles = Directory
+ .GetFiles(RootDir, "project.assets.json", SearchOption.AllDirectories)
+ .Select(GetPathRelativeToRoot)
+ .Except(IgnoredProjectAssetsJsonFiles.NullAsEmpty().Select(GetPathRelativeToRoot))
+ .ToArray();
+
+ if (!string.IsNullOrEmpty(ProjectAssetsJsonArchiveFile))
+ {
+ Log.LogMessage(MessageImportance.Low, "Archiving project.assets.json files...");
+
+ Directory.CreateDirectory(Path.GetDirectoryName(ProjectAssetsJsonArchiveFile));
+
+ using (var projectAssetArchive = new ZipArchive(
+ File.Open(
+ ProjectAssetsJsonArchiveFile,
+ FileMode.Create,
+ FileAccess.ReadWrite),
+ ZipArchiveMode.Create))
+ {
+ // Only one entry can be open at a time, so don't do this during the Parallel
+ // ForEach later.
+ foreach (var relativePath in assetFiles)
+ {
+ using (var stream = File.OpenRead(Path.Combine(RootDir, relativePath)))
+ using (Stream entryWriter = projectAssetArchive
+ .CreateEntry(relativePath, CompressionLevel.Optimal)
+ .Open())
+ {
+ stream.CopyTo(entryWriter);
+ }
+ }
+ }
+ }
+
+ Log.LogMessage(MessageImportance.Low, "Reading usage info...");
+
+ var usages = new ConcurrentBag();
+
+ Parallel.ForEach(
+ assetFiles,
+ assetFile =>
+ {
+ JObject jObj;
+
+ using (var file = File.OpenRead(Path.Combine(RootDir, assetFile)))
+ using (var reader = new StreamReader(file))
+ using (var jsonReader = new JsonTextReader(reader))
+ {
+ jObj = (JObject)JToken.ReadFrom(jsonReader);
+ }
+
+ var properties = new HashSet(
+ jObj.SelectTokens("$.targets.*").Children()
+ .Concat(jObj.SelectToken("$.libraries"))
+ .Select(t => ((JProperty)t).Name)
+ .Distinct(),
+ StringComparer.OrdinalIgnoreCase);
+
+ var directDependencies = jObj.SelectTokens("$.project.frameworks.*.dependencies").Children().Select(dep =>
+ new
+ {
+ name = ((JProperty)dep).Name,
+ target = dep.SelectToken("$..target")?.ToString(),
+ version = VersionRange.Parse(dep.SelectToken("$..version")?.ToString()),
+ autoReferenced = dep.SelectToken("$..autoReferenced")?.ToString() == "True",
+ })
+ .ToArray();
+
+ foreach (var identity in toCheck
+ .Where(id => properties.Contains(id.Id + "/" + id.Version.OriginalVersion)))
+ {
+ var directDependency =
+ directDependencies?.FirstOrDefault(
+ d => d.name == identity.Id &&
+ d.version.Satisfies(identity.Version));
+ usages.Add(Usage.Create(
+ assetFile,
+ identity,
+ directDependency != null,
+ directDependency?.autoReferenced == true,
+ possibleRids));
+ }
+ });
+
+ Log.LogMessage(MessageImportance.Low, "Searching for unused packages...");
+
+ foreach (PackageIdentity restoredWithoutUsagesFound in
+ toCheck.Except(usages.Select(u => u.PackageIdentity)))
+ {
+ usages.Add(Usage.Create(
+ null,
+ restoredWithoutUsagesFound,
+ false,
+ false,
+ possibleRids));
+ }
+
+ // Packages that were included in the tarball as prebuilts, but weren't even restored.
+ PackageIdentity[] neverRestoredTarballPrebuilts = tarballPrebuilt
+ .Except(restored)
+ .ToArray();
+
+ Log.LogMessage(MessageImportance.Low, $"Writing data to '{DataFile}'...");
+
+ var data = new UsageData
+ {
+ CreatedByRid = TargetRid,
+ Usages = usages.ToArray(),
+ NeverRestoredTarballPrebuilts = neverRestoredTarballPrebuilts,
+ ProjectDirectories = ProjectDirectories
+ ?.Select(GetPathRelativeToRoot)
+ .ToArray()
+ };
+
+ Directory.CreateDirectory(Path.GetDirectoryName(DataFile));
+ File.WriteAllText(DataFile, data.ToXml().ToString());
+
+ Log.LogMessage(
+ MessageImportance.High,
+ $"Writing package usage data... done. Took {DateTime.Now - startTime}");
+
+ return !Log.HasLoggedErrors;
+ }
+
+ private string GetPathRelativeToRoot(string path)
+ {
+ if (path.StartsWith(RootDir))
+ {
+ return path.Substring(RootDir.Length).Replace(Path.DirectorySeparatorChar, '/');
+ }
+
+ throw new ArgumentException($"Path '{path}' is not within RootDir '{RootDir}'");
+ }
+
+ private static string[] ReadRidsFromRuntimeJson(string path)
+ {
+ var root = JObject.Parse(File.ReadAllText(path));
+ return root["runtimes"]
+ .Values()
+ .Select(o => o.Name)
+ .ToArray();
+ }
+ }
+}
diff --git a/src/SourceBuild/content/eng/tools/tasks/Microsoft.DotNet.UnifiedBuild.Tasks/UsageReport/WriteUsageBurndownData.cs b/src/SourceBuild/content/eng/tools/tasks/Microsoft.DotNet.UnifiedBuild.Tasks/UsageReport/WriteUsageBurndownData.cs
new file mode 100644
index 000000000000..f99c8463d01a
--- /dev/null
+++ b/src/SourceBuild/content/eng/tools/tasks/Microsoft.DotNet.UnifiedBuild.Tasks/UsageReport/WriteUsageBurndownData.cs
@@ -0,0 +1,138 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using Microsoft.Build.Framework;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+using NuGet.Packaging.Core;
+using NuGet.Versioning;
+using System;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.IO.Compression;
+using System.Linq;
+using System.Net;
+using System.Net.Http;
+using System.Threading.Tasks;
+using System.Xml.Linq;
+using Task = Microsoft.Build.Utilities.Task;
+
+namespace Microsoft.DotNet.UnifiedBuild.Tasks.UsageReport
+{
+ public class WriteUsageBurndownData : Task
+ {
+ ///
+ /// Specifies the root directory for git.
+ /// Note: Requires a trailing "/" when specifying the directory.
+ ///
+ [Required]
+ public string RootDirectory { get; set; }
+
+ ///
+ /// Specifies the path to the prebuilt baseline file
+ /// to be used to generate the burndown.
+ ///
+ [Required]
+ public string PrebuiltBaselineFile { get; set; }
+
+ ///
+ /// Output data CSV file.
+ ///
+ [Required]
+ public string OutputFilePath { get; set; }
+
+ ///
+ /// Sends HTTP requests and receives HTTP responses.
+ ///
+ private readonly HttpClient client = new();
+
+ public override bool Execute() => ExecuteAsync().GetAwaiter().GetResult();
+
+ private async Task ExecuteAsync()
+ {
+ string baselineRelativeFileName = PrebuiltBaselineFile.Replace(RootDirectory, "");
+ string gitLogCommand = $"log --first-parent --pretty=format:%H,%f,%ci -- {PrebuiltBaselineFile}";
+
+ DateTime startTime = DateTime.Now;
+ Log.LogMessage(MessageImportance.High, "Generating summary usage burndown data...");
+
+
+ IEnumerable> getCommitTasks = ExecuteGitCommand(RootDirectory, gitLogCommand)
+ .Select(async commitLine =>
+ {
+ var splitLine = commitLine.Split(',');
+ var commit = new Commit()
+ {
+ Sha = splitLine[0],
+ Title = splitLine[1],
+ CommitDate = DateTime.Parse(splitLine[2])
+ };
+ string fileContents = await GetFileContentsAsync(baselineRelativeFileName, commit.Sha);
+ Usage[] usages = UsageData.Parse(XElement.Parse(fileContents)).Usages.NullAsEmpty().ToArray();
+ commit.PackageVersionCount = usages.Count();
+ commit.PackageCount = usages.GroupBy(i => i.PackageIdentity.Id).Select(grp => grp.First()).Count();
+ return commit;
+ });
+
+ Commit[] commits = await System.Threading.Tasks.Task.WhenAll(getCommitTasks);
+ IEnumerable data = commits.Select(c => c.ToString());
+
+ Directory.CreateDirectory(Path.GetDirectoryName(OutputFilePath));
+
+ File.WriteAllLines(OutputFilePath, data);
+
+ Log.LogMessage(
+ MessageImportance.High,
+ $"Generating summary usage burndown data at {OutputFilePath} done. Took {DateTime.Now - startTime}");
+
+ return !Log.HasLoggedErrors;
+ }
+
+ ///
+ /// Get the contents of a git file based on the commit sha.
+ ///
+ /// The relative path (from the git root) to the file.
+ /// The commit sha for the version of the file to get.
+ /// The contents of the specified file.
+ private Task GetFileContentsAsync(string relativeFilePath, string commitSha) =>
+ client.GetStringAsync($"https://raw.githubusercontent.com/dotnet/source-build/{commitSha}/{relativeFilePath.Replace('\\', '/')}");
+
+ ///
+ /// Executes a git command and returns the result.
+ ///
+ /// The working directory for the git command.
+ /// The git command to execute.
+ /// An array of the output lines of the git command.
+ private string[] ExecuteGitCommand(string workingDirectory, string command)
+ {
+ string[] returnData;
+ Process _process = new Process();
+ _process.StartInfo.FileName = "git";
+ _process.StartInfo.Arguments = command;
+ _process.StartInfo.WorkingDirectory = workingDirectory;
+ _process.StartInfo.RedirectStandardOutput = true;
+ _process.StartInfo.UseShellExecute = false;
+ _process.Start();
+ returnData = _process.StandardOutput.ReadToEnd().Split('\n');
+ _process.WaitForExit();
+ return returnData;
+ }
+
+ private class Commit
+ {
+ public string Sha { get; set; }
+ public string Title { get; set; }
+ public DateTime CommitDate { get; set; }
+ public int PackageVersionCount { get; set; }
+ public int PackageCount { get; set; }
+
+ public override string ToString()
+ {
+ return $"{Sha}, {Title}, {CommitDate}, {PackageVersionCount}, {PackageCount}";
+ }
+ }
+ }
+}
diff --git a/src/SourceBuild/content/eng/tools/tasks/Microsoft.DotNet.UnifiedBuild.Tasks/UsageReport/WriteUsageReports.cs b/src/SourceBuild/content/eng/tools/tasks/Microsoft.DotNet.UnifiedBuild.Tasks/UsageReport/WriteUsageReports.cs
new file mode 100644
index 000000000000..b22f5dac3609
--- /dev/null
+++ b/src/SourceBuild/content/eng/tools/tasks/Microsoft.DotNet.UnifiedBuild.Tasks/UsageReport/WriteUsageReports.cs
@@ -0,0 +1,304 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using Microsoft.Build.Framework;
+using Microsoft.Build.Utilities;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Xml.Linq;
+
+namespace Microsoft.DotNet.UnifiedBuild.Tasks.UsageReport
+{
+ public class WriteUsageReports : Task
+ {
+ private const string SnapshotPrefix = "PackageVersions.";
+ private const string SnapshotSuffix = ".Snapshot.props";
+
+ ///
+ /// Source usage data JSON file.
+ ///
+ [Required]
+ public string DataFile { get; set; }
+
+ ///
+ /// A set of "PackageVersions.{repo}.Snapshot.props" files. They are analyzed to find
+ /// packages built during source-build, and which repo built them. This info is added to the
+ /// report. New packages are associated to a repo by going through each PVP in ascending
+ /// file modification order.
+ ///
+ public ITaskItem[] PackageVersionPropsSnapshots { get; set; }
+
+ ///
+ /// Infos that associate packages to the ProdCon build they're from.
+ ///
+ /// %(PackageId): Identity of the package.
+ /// %(OriginBuildName): Name of the build that produced this package.
+ ///
+ public ITaskItem[] ProdConPackageInfos { get; set; }
+
+ ///
+ /// Path to a ProdCon build manifest file (build.xml) as an alternative way to pass
+ /// ProdConPackageInfos items.
+ ///
+ public string ProdConBuildManifestFile { get; set; }
+
+ ///
+ /// File containing the results of poisoning the prebuilts. Example format:
+ ///
+ /// MATCH: output built\dotnet-sdk-...\System.Collections.dll(hash 4b...31) matches one of:
+ /// intermediate\netstandard.library.2.0.1.nupkg\build\...\System.Collections.dll
+ ///
+ /// The usage report reads this file, looking for 'intermediate\*.nupkg' to annotate.
+ ///
+ public string PoisonedReportFile { get; set; }
+
+ [Required]
+ public string OutputDirectory { get; set; }
+
+ public override bool Execute()
+ {
+ UsageData data = UsageData.Parse(XElement.Parse(File.ReadAllText(DataFile)));
+
+ IEnumerable sourceBuildRepoOutputs = GetSourceBuildRepoOutputs();
+
+ // Map package id to the build name that created them in a ProdCon build.
+ var prodConPackageOrigin = new Dictionary(
+ StringComparer.OrdinalIgnoreCase);
+
+ foreach (ITaskItem item in ProdConPackageInfos.NullAsEmpty())
+ {
+ AddProdConPackage(
+ prodConPackageOrigin,
+ item.GetMetadata("PackageId"),
+ item.GetMetadata("OriginBuildName"));
+ }
+
+ if (File.Exists(ProdConBuildManifestFile))
+ {
+ var xml = XElement.Parse(File.ReadAllText(ProdConBuildManifestFile));
+ foreach (var x in xml.Descendants("Package"))
+ {
+ AddProdConPackage(
+ prodConPackageOrigin,
+ x.Attribute("Id")?.Value,
+ x.Attribute("OriginBuildName")?.Value);
+ }
+ }
+
+ var poisonNupkgFilenames = new HashSet(StringComparer.OrdinalIgnoreCase);
+
+ if (File.Exists(PoisonedReportFile))
+ {
+ foreach (string line in File.ReadAllLines(PoisonedReportFile))
+ {
+ string[] segments = line.Split('\\');
+ if (segments.Length > 2 &&
+ segments[0].Trim() == "intermediate" &&
+ segments[1].EndsWith(".nupkg"))
+ {
+ poisonNupkgFilenames.Add(Path.GetFileNameWithoutExtension(segments[1]));
+ }
+ }
+ }
+
+ var report = new XElement("AnnotatedUsages");
+
+ var annotatedUsages = data.Usages.NullAsEmpty()
+ .Select(usage =>
+ {
+ string id = usage.PackageIdentity.Id;
+ string version = usage.PackageIdentity.Version.OriginalVersion;
+
+ string pvpIdent = WritePackageVersionsProps.GetPropertyName(id, WritePackageVersionsProps.VersionPropertySuffix);
+
+ var sourceBuildCreator = new StringBuilder();
+ foreach (RepoOutput output in sourceBuildRepoOutputs)
+ {
+ foreach (PackageVersionPropsElement p in output.Built)
+ {
+ if (p.Name.Equals(pvpIdent, StringComparison.OrdinalIgnoreCase))
+ {
+ if (sourceBuildCreator.Length != 0)
+ {
+ sourceBuildCreator.Append(" ");
+ }
+ sourceBuildCreator.Append(output.Repo);
+ sourceBuildCreator.Append(" ");
+ sourceBuildCreator.Append(p.Name);
+ sourceBuildCreator.Append("/");
+ sourceBuildCreator.Append(p.Version);
+ }
+ }
+ }
+
+ prodConPackageOrigin.TryGetValue(id, out string prodConCreator);
+
+ return new AnnotatedUsage
+ {
+ Usage = usage,
+
+ Project = data.ProjectDirectories
+ ?.FirstOrDefault(p => usage.AssetsFile?.StartsWith(p) ?? false),
+
+ SourceBuildPackageIdCreator = sourceBuildCreator.Length == 0
+ ? null
+ : sourceBuildCreator.ToString(),
+
+ ProdConPackageIdCreator = prodConCreator,
+
+ TestProjectByHeuristic = IsTestUsageByHeuristic(usage),
+
+ EndsUpInOutput = poisonNupkgFilenames.Contains($"{id}.{version}")
+ };
+ })
+ .ToArray();
+
+ foreach (var onlyTestProjectUsage in annotatedUsages
+ .GroupBy(u => u.Usage.PackageIdentity)
+ .Where(g => g.All(u => u.TestProjectByHeuristic))
+ .SelectMany(g => g))
+ {
+ onlyTestProjectUsage.TestProjectOnlyByHeuristic = true;
+ }
+
+ report.Add(annotatedUsages.Select(u => u.ToXml()));
+
+ Directory.CreateDirectory(OutputDirectory);
+
+ File.WriteAllText(
+ Path.Combine(OutputDirectory, "annotated-usage.xml"),
+ report.ToString());
+
+ return !Log.HasLoggedErrors;
+ }
+
+ private RepoOutput[] GetSourceBuildRepoOutputs()
+ {
+ var pvpSnapshotFiles = PackageVersionPropsSnapshots.NullAsEmpty()
+ .Select(item =>
+ {
+ var content = File.ReadAllText(item.ItemSpec);
+ return new
+ {
+ Path = item.ItemSpec,
+ Content = content,
+ Xml = XElement.Parse(content)
+ };
+ })
+ .OrderBy(snapshot =>
+ {
+ // Get the embedded creation time if possible: the file's original metadata may
+ // have been destroyed by copying, zipping, etc.
+ string creationTime = snapshot.Xml
+ // Get all elements
+ .Elements()
+ // Select all the subelements
+ .SelectMany(e => e.Elements())
+ // Find all that match the creation time property name
+ .Where(e => e.Name == snapshot.Xml.GetDefaultNamespace().GetName(WritePackageVersionsProps.CreationTimePropertyName))
+ // There should be only one or zero
+ .SingleOrDefault()?.Value;
+
+ if (string.IsNullOrEmpty(creationTime))
+ {
+ Log.LogError($"No creation time property found in snapshot {snapshot.Path}");
+ return default(DateTime);
+ }
+
+ return new DateTime(long.Parse(creationTime));
+ })
+ .Select(snapshot =>
+ {
+ string filename = Path.GetFileName(snapshot.Path);
+ return new
+ {
+ Repo = filename.Substring(
+ SnapshotPrefix.Length,
+ filename.Length - SnapshotPrefix.Length - SnapshotSuffix.Length),
+ PackageVersionProp = PackageVersionPropsElement.Parse(snapshot.Xml)
+ };
+ })
+ .ToArray();
+
+ return pvpSnapshotFiles.Skip(1)
+ .Zip(pvpSnapshotFiles, (pvp, prev) => new RepoOutput
+ {
+ Repo = prev.Repo,
+ Built = pvp.PackageVersionProp.Except(prev.PackageVersionProp).ToArray()
+ })
+ .ToArray();
+ }
+
+ private void AddProdConPackage(
+ Dictionary packageOrigin,
+ string id,
+ string origin)
+ {
+ if (!string.IsNullOrEmpty(id) &&
+ !string.IsNullOrEmpty(origin))
+ {
+ packageOrigin[id] = origin;
+ }
+ }
+
+ public static bool IsTestUsageByHeuristic(Usage usage)
+ {
+ string[] assetsFileParts = usage.AssetsFile?.Split('/', '\\');
+
+ // If the dir name ends in Test(s), it's probably a test project.
+ // Ignore the first two segments to avoid classifying everything in "src/vstest".
+ // This also catches "test" dirs that contain many test projects.
+ if (assetsFileParts?.Skip(2).Any(p =>
+ p.EndsWith("Tests", StringComparison.OrdinalIgnoreCase) ||
+ p.EndsWith("Test", StringComparison.OrdinalIgnoreCase)) == true)
+ {
+ return true;
+ }
+
+ // CoreFX restores test dependencies during this sync project.
+ if (assetsFileParts?.Contains("XUnit.Runtime") == true)
+ {
+ return true;
+ }
+
+ return false;
+ }
+
+ private class RepoOutput
+ {
+ public string Repo { get; set; }
+ public PackageVersionPropsElement[] Built { get; set; }
+ }
+
+ private struct PackageVersionPropsElement
+ {
+ public static PackageVersionPropsElement[] Parse(XElement xml)
+ {
+ return xml
+ // Get the first PropertyGroup. The second PropertyGroup is 'extra properties', and the third group is the creation time.
+ // Only select the first because the extra properties are not built packages.
+ .Elements()
+ .First()
+ // Get all *PackageVersion property elements.
+ .Elements()
+ .Select(x => new PackageVersionPropsElement(
+ x.Name.LocalName,
+ x.Nodes().OfType().First().Value))
+ .ToArray();
+ }
+
+ public string Name { get; }
+ public string Version { get; }
+
+ public PackageVersionPropsElement(string name, string version)
+ {
+ Name = name;
+ Version = version;
+ }
+ }
+ }
+}
diff --git a/src/SourceBuild/content/eng/tools/tasks/Microsoft.DotNet.UnifiedBuild.Tasks/UsageReport/XmlParsingHelpers.cs b/src/SourceBuild/content/eng/tools/tasks/Microsoft.DotNet.UnifiedBuild.Tasks/UsageReport/XmlParsingHelpers.cs
new file mode 100644
index 000000000000..c252e8def176
--- /dev/null
+++ b/src/SourceBuild/content/eng/tools/tasks/Microsoft.DotNet.UnifiedBuild.Tasks/UsageReport/XmlParsingHelpers.cs
@@ -0,0 +1,45 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using NuGet.Packaging.Core;
+using NuGet.Versioning;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Xml.Linq;
+
+namespace Microsoft.DotNet.UnifiedBuild.Tasks.UsageReport
+{
+ internal static class XmlParsingHelpers
+ {
+ public static XElement ToXElement(this PackageIdentity ident) => new XElement(
+ "PackageIdentity",
+ new XAttribute("Id", ident.Id),
+ new XAttribute("Version", ident.Version.OriginalVersion));
+
+ public static XAttribute ToXAttributeIfNotNull(this object value, string name) =>
+ value == null ? null : new XAttribute(name, value);
+
+ public static XAttribute ToXAttributeIfTrue(this bool value, string name) =>
+ value == false ? null : new XAttribute(name, value);
+
+ public static PackageIdentity ParsePackageIdentity(XElement xml) => new PackageIdentity(
+ xml.Attribute("Id").Value,
+ NuGetVersion.Parse(xml.Attribute("Version").Value));
+
+ public static IOrderedEnumerable OrderByOrdinal(
+ this IEnumerable source,
+ Func selector)
+ {
+ return source.OrderBy(selector, StringComparer.Ordinal);
+ }
+
+ public static IOrderedEnumerable ThenByOrdinal(
+ this IOrderedEnumerable source,
+ Func selector)
+ {
+ return source.ThenBy(selector, StringComparer.Ordinal);
+ }
+ }
+}
diff --git a/src/SourceBuild/content/eng/tools/tasks/Microsoft.DotNet.UnifiedBuild.Tasks/WritePackageVersionsProps.cs b/src/SourceBuild/content/eng/tools/tasks/Microsoft.DotNet.UnifiedBuild.Tasks/WritePackageVersionsProps.cs
new file mode 100644
index 000000000000..2058d73fc61c
--- /dev/null
+++ b/src/SourceBuild/content/eng/tools/tasks/Microsoft.DotNet.UnifiedBuild.Tasks/WritePackageVersionsProps.cs
@@ -0,0 +1,295 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using Microsoft.Build.Framework;
+using Microsoft.Build.Utilities;
+using NuGet.Packaging;
+using NuGet.Packaging.Core;
+using NuGet.Versioning;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Text.RegularExpressions;
+using System.Xml;
+
+namespace Microsoft.DotNet.UnifiedBuild.Tasks
+{
+ public class VersionEntry
+ {
+ public string Name;
+ public string Version;
+ }
+
+ ///
+ /// Creates a props file that is used as the input for a repo-level build. The props file
+ /// includes package version numbers that should be used by the repo build and additional special properties.
+ ///
+ /// There are two types of input props that can be written:
+ /// - Versions of union of all packages produced by the builds are added. (AllPackages)
+ /// - Only versions of packages that are listed as dependencies of a repo are added. (DependenciesOnly)
+ ///
+ /// The former represents the current way that source build works for most repos. The latter represents the desired
+ /// methodology (PVP Flow). PVP flow closely matches how the product is built in non-source-build mode.
+ ///
+ public class WritePackageVersionsProps : Microsoft.Build.Utilities.Task
+ {
+ private static readonly Regex InvalidElementNameCharRegex = new Regex(@"(^|[^A-Za-z0-9])(?.)");
+
+ public const string CreationTimePropertyName = "BuildOutputPropsCreationTime";
+ public const string VersionPropertySuffix = "Version";
+ private const string VersionPropertyAlternateSuffix = "PackageVersion";
+ private const string PinnedAttributeName = "Pinned";
+ private const string DependencyAttributeName = "Dependency";
+ private const string NameAttributeName = "Name";
+
+ private const string AllPackagesVersionPropsFlowType = "AllPackages";
+ private const string DependenciesOnlyVersionPropsFlowType = "DependenciesOnly";
+ private const string DefaultVersionPropsFlowType = AllPackagesVersionPropsFlowType;
+
+ ///
+ /// Set of input nuget package files to generate version properties for.
+ ///
+ [Required]
+ public ITaskItem[] NuGetPackages { get; set; }
+
+ ///
+ /// File where the version properties should be written.
+ ///
+ [Required]
+ public string OutputPath { get; set; }
+
+ ///
+ /// Properties to add to the build output props, which may not exist as nupkgs.
+ /// FOr example, this is used to pass the version of the CLI toolset archives.
+ ///
+ /// %(Identity): Package identity.
+ /// %(Version): Package version.
+ ///
+ public ITaskItem[] ExtraProperties { get; set; }
+
+ ///
+ /// Indicates which properties will be written into the Version props file.
+ /// If AllPackages (Default), all packages from previously built repos will be written.
+ /// If DependenciesOnly, then only those packages appearing as dependencies in
+ /// Version.Details.xml will show up. The VersionsDetails property must be set to a
+ /// valid Version.Details.xml path when DependenciesOnly is used.
+ ///
+ public string VersionPropsFlowType { get; set; } = DefaultVersionPropsFlowType;
+
+ ///
+ /// If VersionPropsFlowType is set to DependenciesOnly, should be the path to the Version.Detail.xml file for the repo.
+ ///
+ public string VersionDetails { get; set; }
+
+ ///
+ /// Retrieve the set of the dependencies from the repo's Version.Details.Xml file.
+ ///
+ /// Hash set of dependency names.
+ private HashSet GetDependences()
+ {
+ XmlDocument document = new XmlDocument();
+
+ try
+ {
+ document.Load(VersionDetails);
+ }
+ catch (Exception e)
+ {
+ Log.LogErrorFromException(e);
+ return null;
+ }
+
+ HashSet dependencyNames = new HashSet(StringComparer.OrdinalIgnoreCase);
+
+ // Load the nodes, filter those that are not pinned, and
+ XmlNodeList dependencyNodes = document.DocumentElement.SelectNodes($"//{DependencyAttributeName}");
+
+ foreach (XmlNode dependency in dependencyNodes)
+ {
+ if (dependency.NodeType == XmlNodeType.Comment || dependency.NodeType == XmlNodeType.Whitespace)
+ {
+ continue;
+ }
+
+ bool isPinned = false;
+ XmlAttribute pinnedAttribute = dependency.Attributes[PinnedAttributeName];
+ if (pinnedAttribute != null && !bool.TryParse(pinnedAttribute.Value, out isPinned))
+ {
+ Log.LogError($"The '{PinnedAttributeName}' attribute is set but the value " +
+ $"'{pinnedAttribute.Value}' is not a valid boolean...");
+ return null;
+ }
+
+ if (isPinned)
+ {
+ continue;
+ }
+
+ var name = dependency.Attributes[NameAttributeName]?.Value?.Trim();
+
+ if (string.IsNullOrEmpty(name))
+ {
+ Log.LogError($"The '{NameAttributeName}' attribute must be specified.");
+ return null;
+ }
+
+ dependencyNames.Add(name);
+ }
+
+ return dependencyNames;
+ }
+
+ ///
+ /// Filter a set of input dependencies to those that appear in
+ ///
+ /// Input set of entries
+ /// Set of dependencies
+ /// Set of that appears in
+ private IEnumerable FilterNonDependencies(IEnumerable input, HashSet dependencies)
+ {
+ return input.Where(entry => dependencies.Contains(entry.Name));
+ }
+
+ public override bool Execute()
+ {
+ if (VersionPropsFlowType != AllPackagesVersionPropsFlowType &&
+ VersionPropsFlowType != DependenciesOnlyVersionPropsFlowType)
+ {
+ Log.LogError($"Valid version flow types are '{DependenciesOnlyVersionPropsFlowType}' and '{AllPackagesVersionPropsFlowType}'");
+ return !Log.HasLoggedErrors;
+ }
+
+ if (VersionPropsFlowType == DependenciesOnlyVersionPropsFlowType && (string.IsNullOrEmpty(VersionDetails) || !File.Exists(VersionDetails)))
+ {
+ Log.LogError($"When version flow type is DependenciesOnly, the VersionDetails task parameter must point to a valid path to the Version.Details.xml file for the repo. " +
+ "Provided file path '{VersionDetails}' does not exist.");
+ return !Log.HasLoggedErrors;
+ }
+
+ // First, obtain version information from the packages and additional assets that
+ // are provided.
+ var latestPackages = NuGetPackages
+ .Select(item =>
+ {
+ using (var reader = new PackageArchiveReader(item.GetMetadata("FullPath")))
+ {
+ return reader.GetIdentity();
+ }
+ })
+ .GroupBy(identity => identity.Id)
+ .Select(g => g.OrderBy(id => id.Version).Last())
+ .OrderBy(id => id.Id)
+ .Select(identity => new VersionEntry()
+ {
+ Name = identity.Id,
+ Version = identity.Version.ToString()
+ });
+
+ var packageElementsToWrite = latestPackages;
+
+ // Then, if version flow type is "DependenciesOnly", filter those
+ // dependencies that do not appear in the version.details.xml file.
+ if (VersionPropsFlowType == DependenciesOnlyVersionPropsFlowType)
+ {
+ var dependencies = GetDependences();
+
+ if (Log.HasLoggedErrors)
+ {
+ return false;
+ }
+
+ packageElementsToWrite = FilterNonDependencies(packageElementsToWrite, dependencies);
+ }
+
+ Directory.CreateDirectory(Path.GetDirectoryName(OutputPath));
+
+ using (var outStream = File.Open(OutputPath, FileMode.Create))
+ using (var sw = new StreamWriter(outStream, new UTF8Encoding(false)))
+ {
+ sw.WriteLine(@"");
+
+ WriteVersionEntries(sw, packageElementsToWrite, "packages");
+ WriteExtraProperties(sw);
+
+ sw.WriteLine(@" ");
+ sw.WriteLine($@" <{CreationTimePropertyName}>{DateTime.UtcNow.Ticks}{CreationTimePropertyName}>");
+ sw.WriteLine(@" ");
+
+ sw.WriteLine(@"");
+ }
+
+ return !Log.HasLoggedErrors;
+ }
+
+ ///
+ /// Write properties specified in the "ExtraProperties task parameter
+ ///
+ /// Stream writer
+ private void WriteExtraProperties(StreamWriter sw)
+ {
+ if (ExtraProperties == null)
+ {
+ return;
+ }
+
+ sw.WriteLine(@" ");
+ sw.WriteLine(@" ");
+
+ foreach (var extraProp in ExtraProperties ?? Enumerable.Empty())
+ {
+ string propertyName = extraProp.GetMetadata("Identity");
+ bool doNotOverwrite = false;
+ string overwriteCondition = string.Empty;
+ if (bool.TryParse(extraProp.GetMetadata("DoNotOverwrite"), out doNotOverwrite) && doNotOverwrite)
+ {
+ overwriteCondition = $" Condition=\"'$({propertyName})' == ''\"";
+ }
+ sw.WriteLine($" <{propertyName}{overwriteCondition}>{extraProp.GetMetadata("Version")}{propertyName}>");
+ }
+
+ sw.WriteLine(@" ");
+ }
+
+ ///
+ /// Write properties for the version numbers required for this repo.
+ ///
+ /// Stream writer
+ /// Version entries
+ private void WriteVersionEntries(StreamWriter sw, IEnumerable entries, string entryType)
+ {
+ if (!entries.Any())
+ {
+ return;
+ }
+
+ sw.WriteLine($" ");
+ if (VersionPropsFlowType == DependenciesOnlyVersionPropsFlowType)
+ {
+ sw.WriteLine(@" ");
+ }
+ sw.WriteLine(@" ");
+ foreach (var package in entries)
+ {
+ string propertyName = GetPropertyName(package.Name, VersionPropertySuffix);
+ string alternatePropertyName = GetPropertyName(package.Name, VersionPropertyAlternateSuffix);
+
+ sw.WriteLine($" <{propertyName}>{package.Version}{propertyName}>");
+ sw.WriteLine($" <{alternatePropertyName}>{package.Version}{alternatePropertyName}>");
+ }
+ sw.WriteLine(@" ");
+ }
+
+ public static string GetPropertyName(string id, string suffix)
+ {
+ string formattedId = InvalidElementNameCharRegex.Replace(
+ id,
+ match => match.Groups?["FirstPartChar"].Value.ToUpperInvariant()
+ ?? string.Empty);
+
+ return $"{formattedId}{suffix}";
+ }
+ }
+}
diff --git a/src/SourceBuild/content/global.json b/src/SourceBuild/content/global.json
new file mode 100644
index 000000000000..5ffcf53ed05c
--- /dev/null
+++ b/src/SourceBuild/content/global.json
@@ -0,0 +1,10 @@
+{
+ "tools": {
+ "dotnet": "9.0.100-preview.4.24178.10"
+ },
+ "msbuild-sdks": {
+ "Microsoft.Build.NoTargets": "3.7.0",
+ "Microsoft.Build.Traversal": "3.4.0",
+ "Microsoft.DotNet.Arcade.Sdk": "9.0.0-beta.24177.2"
+ }
+}
diff --git a/src/SourceBuild/content/prep-source-build.sh b/src/SourceBuild/content/prep-source-build.sh
new file mode 100755
index 000000000000..97dd1d0385f7
--- /dev/null
+++ b/src/SourceBuild/content/prep-source-build.sh
@@ -0,0 +1,250 @@
+#!/usr/bin/env bash
+
+### Usage: $0
+###
+### Prepares the environment for a source build by downloading Private.SourceBuilt.Artifacts.*.tar.gz,
+### installing the version of dotnet referenced in global.json,
+### and detecting binaries and removing any non-SB allowed binaries.
+###
+### Options:
+### --no-artifacts Exclude the download of the previously source-built artifacts archive
+### --no-bootstrap Don't replace portable packages in the download source-built artifacts
+### --no-prebuilts Exclude the download of the prebuilts archive
+### --no-sdk Exclude the download of the .NET SDK
+### --artifacts-rid The RID of the previously source-built artifacts archive to download
+### Default is centos.9-x64
+### --runtime-source-feed URL of a remote server or a local directory, from which SDKs and
+### runtimes can be downloaded
+### --runtime-source-feed-key Key for accessing the above server, if necessary
+###
+### Binary-Tooling options:
+### --no-binary-removal Don't remove non-SB allowed binaries
+### --with-sdk Use the SDK in the specified directory
+### Default is the .NET SDK
+### --with-packages Specified directory to use as the source feed for packages
+### Default is the previously source-built artifacts archive.
+
+set -euo pipefail
+IFS=$'\n\t'
+
+source="${BASH_SOURCE[0]}"
+REPO_ROOT="$( cd -P "$( dirname "$0" )" && pwd )"
+
+function print_help () {
+ sed -n '/^### /,/^$/p' "$source" | cut -b 5-
+}
+
+# SB prep default arguments
+defaultArtifactsRid='centos.9-x64'
+
+# Binary Tooling default arguments
+defaultDotnetSdk="$REPO_ROOT/.dotnet"
+defaultPackagesDir="$REPO_ROOT/prereqs/packages/previously-source-built"
+
+# SB prep arguments
+buildBootstrap=true
+downloadArtifacts=true
+downloadPrebuilts=true
+removeBinaries=true
+installDotnet=true
+artifactsRid=$defaultArtifactsRid
+runtime_source_feed='' # IBM requested these to support s390x scenarios
+runtime_source_feed_key='' # IBM requested these to support s390x scenarios
+
+# Binary Tooling arguments
+dotnetSdk=$defaultDotnetSdk
+packagesDir=$defaultPackagesDir
+
+positional_args=()
+while :; do
+ if [ $# -le 0 ]; then
+ break
+ fi
+ lowerI="$(echo "$1" | awk '{print tolower($0)}')"
+ case $lowerI in
+ "-?"|-h|--help)
+ print_help
+ exit 0
+ ;;
+ --no-bootstrap)
+ buildBootstrap=false
+ ;;
+ --no-artifacts)
+ downloadArtifacts=false
+ ;;
+ --no-prebuilts)
+ downloadPrebuilts=false
+ ;;
+ --no-sdk)
+ installDotnet=false
+ ;;
+ --artifacts-rid)
+ artifactsRid=$2
+ ;;
+ --runtime-source-feed)
+ runtime_source_feed=$2
+ shift
+ ;;
+ --runtime-source-feed-key)
+ runtime_source_feed_key=$2
+ shift
+ ;;
+ --no-binary-removal)
+ removeBinaries=false
+ ;;
+ --with-sdk)
+ dotnetSdk=$2
+ shift
+ ;;
+ --with-packages)
+ packagesDir=$2
+ shift
+ ;;
+ *)
+ positional_args+=("$1")
+ ;;
+ esac
+
+ shift
+done
+
+# Attempting to bootstrap without an SDK will fail. So either the --no-sdk flag must be passed
+# or a pre-existing .dotnet SDK directory must exist.
+if [ "$buildBootstrap" == true ] && [ "$installDotnet" == false ] && [ ! -d "$REPO_ROOT/.dotnet" ]; then
+ echo " ERROR: --no-sdk requires --no-bootstrap or a pre-existing .dotnet SDK directory. Exiting..."
+ exit 1
+fi
+
+# Check to make sure curl exists to download the archive files
+if ! command -v curl &> /dev/null
+then
+ echo " ERROR: curl not found. Exiting..."
+ exit 1
+fi
+
+# Check if Private.SourceBuilt artifacts archive exists
+artifactsBaseFileName="Private.SourceBuilt.Artifacts"
+packagesArchiveDir="$REPO_ROOT/prereqs/packages/archive/"
+if [ "$downloadArtifacts" == true ] && [ -f ${packagesArchiveDir}${artifactsBaseFileName}.*.tar.gz ]; then
+ echo " Private.SourceBuilt.Artifacts.*.tar.gz exists...it will not be downloaded"
+ downloadArtifacts=false
+fi
+
+# Check if Private.SourceBuilt prebuilts archive exists
+prebuiltsBaseFileName="Private.SourceBuilt.Prebuilts"
+if [ "$downloadPrebuilts" == true ] && [ -f ${packagesArchiveDir}${prebuiltsBaseFileName}.*.tar.gz ]; then
+ echo " Private.SourceBuilt.Prebuilts.*.tar.gz exists...it will not be downloaded"
+ downloadPrebuilts=false
+fi
+
+# Check if dotnet is installed
+if [ "$installDotnet" == true ] && [ -d "$REPO_ROOT/.dotnet" ]; then
+ echo " ./.dotnet SDK directory exists...it will not be installed"
+ installDotnet=false;
+fi
+
+function DownloadArchive {
+ archiveType="$1"
+ isRequired="$2"
+ artifactsRid="$3"
+
+ packageVersionsPath="$REPO_ROOT/eng/Versions.props"
+ notFoundMessage="No source-built $archiveType found to download..."
+
+ echo " Looking for source-built $archiveType to download..."
+ archiveVersionLine=$(grep -m 1 "" "$packageVersionsPath" || :)
+ versionPattern="(.*)"
+ if [[ $archiveVersionLine =~ $versionPattern ]]; then
+ archiveVersion="${BASH_REMATCH[1]}"
+
+ if [ "$archiveType" == "Prebuilts" ]; then
+ archiveRid=$defaultArtifactsRid
+ else
+ archiveRid=$artifactsRid
+ fi
+
+ archiveUrl="https://dotnetcli.azureedge.net/source-built-artifacts/assets/Private.SourceBuilt.$archiveType.$archiveVersion.$archiveRid.tar.gz"
+
+ echo " Downloading source-built $archiveType from $archiveUrl..."
+ (cd "$packagesArchiveDir" && curl -f --retry 5 -O "$archiveUrl")
+ elif [ "$isRequired" == true ]; then
+ echo " ERROR: $notFoundMessage"
+ exit 1
+ else
+ echo " $notFoundMessage"
+ fi
+}
+
+function BootstrapArtifacts {
+ DOTNET_SDK_PATH="$REPO_ROOT/.dotnet"
+
+ # Create working directory for running bootstrap project
+ workingDir=$(mktemp -d)
+ echo " Building bootstrap previously source-built in $workingDir"
+
+ # Copy bootstrap project to working dir
+ cp "$REPO_ROOT/eng/bootstrap/buildBootstrapPreviouslySB.csproj" "$workingDir"
+
+ # Copy NuGet.config from the installer repo to have the right feeds
+ cp "$REPO_ROOT/src/installer/NuGet.config" "$workingDir"
+
+ # Get PackageVersions.props from existing prev-sb archive
+ echo " Retrieving PackageVersions.props from existing archive"
+ sourceBuiltArchive=$(find "$packagesArchiveDir" -maxdepth 1 -name 'Private.SourceBuilt.Artifacts*.tar.gz')
+ if [ -f "$sourceBuiltArchive" ]; then
+ tar -xzf "$sourceBuiltArchive" -C "$workingDir" PackageVersions.props
+ fi
+
+ # Run restore on project to initiate download of bootstrap packages
+ "$DOTNET_SDK_PATH/dotnet" restore "$workingDir/buildBootstrapPreviouslySB.csproj" /bl:artifacts/log/prep-bootstrap.binlog /fileLoggerParameters:LogFile=artifacts/log/prep-bootstrap.log /p:ArchiveDir="$packagesArchiveDir" /p:BootstrapOverrideVersionsProps="$REPO_ROOT/eng/bootstrap/OverrideBootstrapVersions.props"
+
+ # Remove working directory
+ rm -rf "$workingDir"
+}
+
+# Check for the version of dotnet to install
+if [ "$installDotnet" == true ]; then
+ echo " Installing dotnet..."
+ use_installed_dotnet_cli=false
+ (source ./eng/common/tools.sh && InitializeDotNetCli true)
+fi
+
+# Read the eng/Versions.props to get the archives to download and download them
+if [ "$downloadArtifacts" == true ]; then
+ DownloadArchive Artifacts true $artifactsRid
+ if [ "$buildBootstrap" == true ]; then
+ BootstrapArtifacts
+ fi
+fi
+
+if [ "$downloadPrebuilts" == true ]; then
+ DownloadArchive Prebuilts false $artifactsRid
+fi
+
+if [ "$removeBinaries" == true ]; then
+
+ # If --with-packages is not passed, unpack PSB artifacts
+ if [[ $packagesDir == $defaultPackagesDir ]]; then
+ sourceBuiltArchive=$(find "$packagesArchiveDir" -maxdepth 1 -name 'Private.SourceBuilt.Artifacts*.tar.gz')
+
+ if [ ! -d "$packagesDir" ] && [ -f "$sourceBuiltArchive" ]; then
+ echo " Unpacking Private.SourceBuilt.Artifacts.*.tar.gz into $packagesDir"
+ mkdir -p "$packagesDir"
+ tar -xzf "$sourceBuiltArchive" -C "$packagesDir"
+ elif [ ! -f "$packagesDir/PackageVersions.props" ] && [ -f "$sourceBuiltArchive" ]; then
+ echo " Creating $packagesDir/PackageVersions.props..."
+ tar -xzf "$sourceBuiltArchive" -C "$packagesDir" PackageVersions.props
+ elif [ ! -f "$sourceBuiltArchive" ]; then
+ echo " ERROR: Private.SourceBuilt.Artifacts.*.tar.gz does not exist..."\
+ "Cannot remove non-SB allowed binaries. Either pass --with-packages or download the artifacts."
+ exit 1
+ fi
+ fi
+
+ "$REPO_ROOT/eng/detect-binaries.sh" \
+ --clean \
+ --allowed-binaries-file "$REPO_ROOT/eng/allowed-sb-binaries.txt" \
+ --with-packages $packagesDir \
+ --with-sdk $dotnetSdk \
+
+fi
\ No newline at end of file
diff --git a/src/SourceBuild/content/prereqs/keys/Newtonsoft.Json.snk b/src/SourceBuild/content/prereqs/keys/Newtonsoft.Json.snk
new file mode 100644
index 000000000000..c26e9e4eab46
Binary files /dev/null and b/src/SourceBuild/content/prereqs/keys/Newtonsoft.Json.snk differ
diff --git a/src/SourceBuild/content/prereqs/keys/NuGet.Client.snk b/src/SourceBuild/content/prereqs/keys/NuGet.Client.snk
new file mode 100644
index 000000000000..695f1b38774e
Binary files /dev/null and b/src/SourceBuild/content/prereqs/keys/NuGet.Client.snk differ
diff --git a/src/SourceBuild/content/prereqs/keys/README.md b/src/SourceBuild/content/prereqs/keys/README.md
new file mode 100644
index 000000000000..d83cf94916a9
--- /dev/null
+++ b/src/SourceBuild/content/prereqs/keys/README.md
@@ -0,0 +1,13 @@
+This directory contains the public key portion of keys used by different
+projects so we can public sign (also known as OSS signing) assemblies that need
+to be signed to have the correct strong name.
+
+These are used for projects which full sign their assemblies and don't have keys
+checked in.
+
+To extract a key, take an existing built binary for a project (e.g. download a
+nupkg from NuGet.org and then unpack one of the assemblies from it) and use `sn`:
+
+```
+sn -e
+```
diff --git a/src/SourceBuild/content/prereqs/packages/archive/_ b/src/SourceBuild/content/prereqs/packages/archive/_
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/src/SourceBuild/content/prereqs/packages/prebuilt/_ b/src/SourceBuild/content/prereqs/packages/prebuilt/_
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/src/SourceBuild/content/repo-projects/Directory.Build.props b/src/SourceBuild/content/repo-projects/Directory.Build.props
new file mode 100644
index 000000000000..5735e9fd53a7
--- /dev/null
+++ b/src/SourceBuild/content/repo-projects/Directory.Build.props
@@ -0,0 +1,252 @@
+
+
+
+
+
+ $(MSBuildProjectName)
+ $(GitInfoDir)$(RepositoryName).props
+ true
+ false
+
+ true
+
+
+
+
+
+
+ netstandard2.0
+ true
+
+ $([MSBuild]::NormalizeDirectory('$(SrcDir)', '$(RepositoryName)'))
+
+
+ $([MSBuild]::NormalizeDirectory('$(ArtifactsObjDir)', 'PackageVersions'))
+ $(PackageVersionsDir)PackageVersions.$(RepositoryName).props
+ $(PackageVersionsDir)PackageVersions.$(RepositoryName).Current.props
+ $(PackageVersionsDir)PackageVersions.$(RepositoryName).Previous.props
+ $(PackageVersionsDir)PackageVersions.$(RepositoryName).Snapshot.props
+ DependenciesOnly
+
+ $(ProjectDirectory)global.json
+ $(ProjectDirectory)NuGet.config
+ $(ProjectDirectory)NuGet.Config
+
+ $(NuGetConfigFile)
+
+ $(BaseIntermediateOutputPath)$([System.IO.Path]::GetFileName('$(OriginalNuGetConfigFile)'))
+
+ $([MSBuild]::NormalizeDirectory('$(AssetManifestsIntermediateDir)', '$(RepositoryName)'))
+ $([MSBuild]::NormalizeDirectory('$(IntermediateSymbolsRootDir)', '$(RepositoryName)'))
+
+ $([MSBuild]::NormalizeDirectory('$(ArtifactsDir)', 'source-built-sdks'))
+
+ source-build-reference-package-cache
+ source-built-
+
+
+ $([MSBuild]::ValueOrDefault('$(ARCADE_BOOTSTRAP_VERSION)', '$(ArcadeSdkVersion)'))
+
+
+
+ -
+ 0
+
+
+ --
+ false
+
+
+
+
+ $([MSBuild]::NormalizePath('$(ProjectDirectory)', 'eng', 'common', 'build$(ShellExtension)'))
+
+ $(FlagParameterPrefix)restore
+ $(BuildActions) $(FlagParameterPrefix)build
+ $(BuildActions) $(FlagParameterPrefix)pack
+ $(BuildActions) $(FlagParameterPrefix)publish
+
+ $(FlagParameterPrefix)ci
+ $(BuildArgs) $(FlagParameterPrefix)configuration $(Configuration)
+ $(BuildArgs) -bl
+ $(BuildArgs) /p:DotNetBuildRepo=true
+
+ $(BuildArgs) /p:DotNetBuildOrchestrator=true
+ $(BuildArgs) /p:CrossBuild=true
+
+ $(BuildArgs) /p:DotNetBuildTests=true
+ $(BuildArgs) /p:RestoreConfigFile=$(NuGetConfigFile)
+
+ $(BuildArgs) /p:SourceBuildUseMonoRuntime=$(SourceBuildUseMonoRuntime)
+
+ $(BuildArgs) /p:SourceBuiltAssetsDir=$(ArtifactsAssetsDir)
+ $(BuildArgs) /p:SourceBuiltAssetManifestsDir=$(RepoAssetManifestsDir)
+
+
+
+ $(BuildArgs) /p:SourceBuiltSymbolsDir=$(IntermediateSymbolsRepoDir)
+ $(BuildArgs) /p:ArcadeBuildFromSource=true
+ $(BuildArgs) /p:DotNetBuildSourceOnly=true
+ $(BuildArgs) /p:PreviouslySourceBuiltNupkgCacheDir="$(PrebuiltSourceBuiltPackagesPath)"
+ $(BuildArgs) /p:ReferencePackageNupkgCacheDir="$(ReferencePackagesDir)"
+
+
+
+ $([MSBuild]::NormalizeDirectory('$(ArtifactsDir)', 'msbuild-debug'))
+ $([MSBuild]::NormalizeDirectory('$(ArtifactsDir)', 'roslyn-debug'))
+
+ $([MSBuild]::NormalizeDirectory('$(ArtifactsDir)', 'aspnet-debug'))
+ $(AspNetRazorBuildServerLogDir)razor-build-server.log
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ source
+ 30000001-1
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/SourceBuild/content/repo-projects/Directory.Build.targets b/src/SourceBuild/content/repo-projects/Directory.Build.targets
new file mode 100644
index 000000000000..29512a83cd07
--- /dev/null
+++ b/src/SourceBuild/content/repo-projects/Directory.Build.targets
@@ -0,0 +1,742 @@
+
+
+
+
+
+ $(BuildArgs) /p:UseInnerClone=true
+ $(BuildArgs) /p:CopySrcInsteadOfClone=true
+ $(BuildArgs) /p:CopyWipIntoInnerSourceBuildRepo=true
+
+
+
+
+ $(BuildArgs) $(FlagParameterPrefix)msbuildEngine dotnet
+
+ $([MSBuild]::NormalizeDirectory('$(ArtifactsShippingPackagesDir)', '$(RepositoryName)'))
+ $(ReferencePackagesDir)
+ $([MSBuild]::NormalizeDirectory('$(ArtifactsNonShippingPackagesDir)', '$(RepositoryName)'))
+ $(ReferencePackagesDir)
+
+ $(BuildArgs) /p:SourceBuiltShippingPackagesDir=$(RepoArtifactsShippingPackagesDir)
+
+ $(BuildArgs) /p:SourceBuiltNonShippingPackagesDir=$(RepoArtifactsNonShippingPackagesDir.TrimEnd('\'))
+
+
+
+ $(BuildScript) $(BuildActions) $(BuildArgs)
+
+
+ $(ArtifactsLogDir)$(RepositoryName).log
+ $(BuildInParallel)
+
+ $(PackageReportDir)prebuilt-usage.xml
+ $(PackageReportDir)all-project-assets-json-files.zip
+ $(PackageReportDir)prodcon-build.xml
+
+
+
+
+
+ <_TransitiveRepositoryReference Include="@(RepositoryReference)" />
+
+
+
+
+
+
+
+
+ <_TransitiveRepositoryReference Include="@(_DependencyTransitiveRepositoryReference)"
+ RemoveMetadata="MSBuildSourceProjectFile;MSBuildSourceTargetName;OriginalItemSpec" />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ $(SourceBuiltSourceNamePrefix)%(Identity)
+ $(SourceBuiltSourceNamePrefix)transport-%(Identity)
+ $(ArtifactsShippingPackagesDir)/%(Identity)/
+ $(ArtifactsNonShippingPackagesDir)/%(Identity)/
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ExtraSources
+ $([MSBuild]::NormalizeDirectory('$(RepoRoot)', 'src', 'source-build-reference-packages', 'src'))
+
+
+
+ prebuilt
+ previously-source-built
+ reference-packages
+
+
+
+ <_CommonBuildSources Include="@(DependentRepoSourceName)" />
+ <_CommonBuildSources Include="$(ExtraSourcesNuGetSourceName)" Condition="'$(ExtraRestoreSourcePath)' != ''" />
+
+
+
+
+ true
+ net-sdk-supporting-feed
+ https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet9/nuget/v3/index.json
+
+
+
+ <_BuildSources Include="$(PrebuiltNuGetSourceName);$(PreviouslySourceBuiltNuGetSourceName);$(ReferencePackagesNuGetSourceName)"
+ Condition="'$(DotNetBuildSourceOnly)' == 'true'"/>
+ <_BuildSources Include="@(_CommonBuildSources)" />
+ <_BuildSources Include="$(NetSdkSupportingFeedName)"
+ Condition="'$(AddNetSdkSupportingFeed)' == 'true'" />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ <_PossibleCliVersionJsonPath Include="sdk:version" />
+ <_PossibleCliVersionJsonPath Include="tools:dotnet" />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ <_CurrentSourceBuiltPackages Include="@(DependentRepoPackageFile)"
+ Condition="!$([System.String]::Copy('%(Identity)').EndsWith('.symbols.nupkg'))" />
+ <_PreviouslyBuiltSourceBuiltPackages Include="$(PrebuiltSourceBuiltPackagesPath)*.nupkg" />
+
+
+
+
+
+ <_VersionDetailsXml Condition="'$(PackageVersionPropsFlowType)' == 'DependenciesOnly'">$(ProjectDirectory)/eng/Version.Details.xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+]]>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ <_DependentProject Include="@(TransitiveRepositoryReference -> '%(Identity).proj')" />
+
+
+
+
+
+
+
+
+ <_ArcadeSdkName>Microsoft.DotNet.Arcade.Sdk
+
+
+
+
+ <_BuiltArcadeSdkPackage Include="@(DependentRepoPackageFile)"
+ Condition="!$([System.String]::new('%(Identity)').EndsWith('.symbols.nupkg')) and $([System.String]::new('%(Identity)').Contains($(_ArcadeSdkName)))" />
+
+
+
+ <_BuiltArcadeSdkPackageVersion>$([System.String]::new('%(_BuiltArcadeSdkPackage.FileName)').Substring($([MSBuild]::Add($(_ArcadeSdkName.Length), 1))))
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ $(BuildCommand)
+ $(FullCommand) /v:$(LogVerbosity)
+ $(FullCommand) > $(RepoConsoleLogFile) 2>&1
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ <_InnerPackageCacheFiles Include="$(ProjectDirectory)artifacts/sb/package-cache/**/*" />
+
+
+
+
+
+
+
+
+
+
+
+ $([MSBuild]::NormalizeDirectory('$(ProjectDirectory)', 'artifacts', 'buildLogs'))
+ $([MSBuild]::NormalizeDirectory('$(ProjectDirectory)', 'artifacts', 'buildObj'))
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ <_ToolPackagesRoot>$(RepoArtifactsNonShippingPackagesDir)
+
+
+
+ <_ToolPackage
+ Condition="'%(BuiltSdkPackageOverride.Version)' == ''"
+ Include="$(_ToolPackagesRoot)%(BuiltSdkPackageOverride.Identity)*.nupkg"
+ Exclude="$(_ToolPackagesRoot)%(BuiltSdkPackageOverride.Identity)*.symbols.nupkg"
+ Id="%(BuiltSdkPackageOverride.Identity)" />
+ <_ToolPackage
+ Condition="'%(BuiltSdkPackageOverride.Version)' != ''"
+ Include="$(_ToolPackagesRoot)%(BuiltSdkPackageOverride.Identity).%(BuiltSdkPackageOverride.Version).nupkg"
+ Exclude="$(_ToolPackagesRoot)%(BuiltSdkPackageOverride.Identity).%(BuiltSdkPackageOverride.Version).symbols.nupkg"
+ Id="%(BuiltSdkPackageOverride.Identity)" />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ $([System.String]::Copy(%(Filename)).ToLowerInvariant())
+
+
+
+ $([System.String]::Copy(%(Filename)).ToLowerInvariant())
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ false
+
+
+
+
+
+
+
+
+
+ false
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ <_NextIndent>$(DependencyGraphIndent)__
+
+
+
+
+
+
+
+
+
+
+ <_DependencyGraphString>@(_DependencyGraphString, '')
+ <_LineBreak>%0a
+ $(DependencyGraphIndent)-_$(RepositoryName)$(_LineBreak)$(_DependencyGraphString)
+
+
+
+
diff --git a/src/SourceBuild/content/repo-projects/arcade.proj b/src/SourceBuild/content/repo-projects/arcade.proj
new file mode 100644
index 000000000000..1d22ddd47602
--- /dev/null
+++ b/src/SourceBuild/content/repo-projects/arcade.proj
@@ -0,0 +1,22 @@
+
+
+
+
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/SourceBuild/content/repo-projects/aspire.proj b/src/SourceBuild/content/repo-projects/aspire.proj
new file mode 100644
index 000000000000..e7a7534b1ad4
--- /dev/null
+++ b/src/SourceBuild/content/repo-projects/aspire.proj
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/SourceBuild/content/repo-projects/aspnetcore.proj b/src/SourceBuild/content/repo-projects/aspnetcore.proj
new file mode 100644
index 000000000000..f6d9f7bd54ef
--- /dev/null
+++ b/src/SourceBuild/content/repo-projects/aspnetcore.proj
@@ -0,0 +1,56 @@
+
+
+
+
+ $(ProjectDirectory)eng\build$(ShellExtension)
+
+ $(FlagParameterPrefix)restore $(FlagParameterPrefix)all $(FlagParameterPrefix)pack $(FlagParameterPrefix)publish
+
+ $(BuildActions) -NativeToolsOnMachine
+
+ $(BuildActions) $(FlagParameterPrefix)no-build-java
+
+ $(BuildArgs) $(FlagParameterPrefix)arch $(TargetArchitecture)
+ false
+
+ true
+
+
+
+ $(BuildArgs) /p:PortableBuild=$(PortableBuild)
+ $(BuildArgs) /p:TargetRuntimeIdentifier=$(TargetRid)
+ $(BuildArgs) $(FlagParameterPrefix)no-build-repo-tasks
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/SourceBuild/content/repo-projects/cecil.proj b/src/SourceBuild/content/repo-projects/cecil.proj
new file mode 100644
index 000000000000..9deac728bc91
--- /dev/null
+++ b/src/SourceBuild/content/repo-projects/cecil.proj
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/SourceBuild/content/repo-projects/command-line-api.proj b/src/SourceBuild/content/repo-projects/command-line-api.proj
new file mode 100644
index 000000000000..6526536abe82
--- /dev/null
+++ b/src/SourceBuild/content/repo-projects/command-line-api.proj
@@ -0,0 +1,17 @@
+
+
+
+ true
+
+ $(BuildArgs) $(FlagParameterPrefix)warnAsError $(ArcadeFalseBoolBuildArg)
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/SourceBuild/content/repo-projects/deployment-tools.proj b/src/SourceBuild/content/repo-projects/deployment-tools.proj
new file mode 100644
index 000000000000..c0c4fc56ea22
--- /dev/null
+++ b/src/SourceBuild/content/repo-projects/deployment-tools.proj
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/SourceBuild/content/repo-projects/diagnostics.proj b/src/SourceBuild/content/repo-projects/diagnostics.proj
new file mode 100644
index 000000000000..9deac728bc91
--- /dev/null
+++ b/src/SourceBuild/content/repo-projects/diagnostics.proj
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/SourceBuild/content/repo-projects/dotnet.proj b/src/SourceBuild/content/repo-projects/dotnet.proj
new file mode 100644
index 000000000000..25d66b251b8d
--- /dev/null
+++ b/src/SourceBuild/content/repo-projects/dotnet.proj
@@ -0,0 +1,18 @@
+
+
+
+
+ true
+
+
+
+
+
+
+
+
+
+
diff --git a/src/SourceBuild/content/repo-projects/emsdk.proj b/src/SourceBuild/content/repo-projects/emsdk.proj
new file mode 100644
index 000000000000..284971d4cb20
--- /dev/null
+++ b/src/SourceBuild/content/repo-projects/emsdk.proj
@@ -0,0 +1,21 @@
+
+
+
+
+ $(ProjectDirectory)build$(ShellExtension)
+
+ $(BuildArgs) /p:PackageRid=$(TargetRid)
+ $(BuildArgs) /p:AssetManifestOS=$(TargetOS)
+ $(BuildArgs) /p:PlatformName=$(TargetArchitecture)
+ $(BuildArgs) /p:ForceBuildManifestOnly=true
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/SourceBuild/content/repo-projects/fsharp.proj b/src/SourceBuild/content/repo-projects/fsharp.proj
new file mode 100644
index 000000000000..049ce263c406
--- /dev/null
+++ b/src/SourceBuild/content/repo-projects/fsharp.proj
@@ -0,0 +1,33 @@
+
+
+
+ true
+
+
+ $(ProjectDirectory)build$(ShellExtension)
+
+
+ $(FlagParameterPrefix)pack $(FlagParameterPrefix)publish
+
+ $(BuildArgs) /p:TreatWarningsAsErrors=false
+ $(BuildArgs) /p:GenerateResourceUsePreserializedResources=true
+
+
+ $(BuildArgs) $(FlagParameterPrefix)sourcebuild $(FlagParameterPrefix)tfm $(NetCurrent) /p:SourceBuildBootstrapTfm=$(NetCurrent)
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/SourceBuild/content/repo-projects/installer.proj b/src/SourceBuild/content/repo-projects/installer.proj
new file mode 100644
index 000000000000..55a15d4373dc
--- /dev/null
+++ b/src/SourceBuild/content/repo-projects/installer.proj
@@ -0,0 +1,150 @@
+
+
+
+
+ false
+
+
+ $(ProjectDirectory)build$(ShellExtension)
+
+
+ $(FlagParameterPrefix)pack $(FlagParameterPrefix)publish
+
+
+ $(BuildArgs) /p:NETCoreAppMaximumVersion=99.9
+
+ $(BuildArgs) /p:OSName=$(TargetRid.Substring(0, $(TargetRid.IndexOf("-"))))
+ $(BuildArgs) /p:PortableOSName=$(__PortableTargetOS)
+ $(BuildArgs) /p:Rid=$(TargetRid)
+ $(BuildArgs) /p:Architecture=$(TargetArchitecture)
+ $(BuildArgs) /p:DOTNET_INSTALL_DIR=$(DotNetRoot)
+
+ $(BuildArgs) /p:AspNetCoreInstallerRid=$(TargetRid)
+
+ $(BuildArgs) /p:PortableBuild=true
+ $(BuildArgs) /p:NetRuntimeRid=$(TargetRid)
+
+ $(BuildArgs) /p:SkipBuildingInstallers=true
+
+ $(BuildArgs) /p:PublicBaseURL=file:%2F%2F$(ArtifactsAssetsDir)
+
+ $(BuildArgs) /p:FallbackPublicBaseURL=https://dotnetbuilds.blob.core.windows.net/public/
+ $(BuildArgs) /p:UsePortableLinuxSharedFramework=false
+
+ $(BuildArgs) /p:PgoInstrument=true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ $([MSBuild]::NormalizeDirectory('$(BaseIntermediateOutputPath)', 'artifacts-layout'))
+
+
+ $(ArtifactsAssetsDir)$(SourceBuiltArtifactsTarballName).$(SourceBuiltSdkVersion).$(TargetRid)$(ArchiveExtension)
+ $(SourceBuiltLayoutDir).version
+ $(SourceBuiltLayoutDir)PackageVersions.props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/SourceBuild/content/repo-projects/msbuild.proj b/src/SourceBuild/content/repo-projects/msbuild.proj
new file mode 100644
index 000000000000..e50e9d7613a5
--- /dev/null
+++ b/src/SourceBuild/content/repo-projects/msbuild.proj
@@ -0,0 +1,25 @@
+
+
+
+ true
+
+ $(BuildArgs) $(FlagParameterPrefix)v $(LogVerbosity)
+ $(BuildArgs) $(FlagParameterPrefix)warnAsError $(ArcadeFalseBoolBuildArg)
+
+ $(BuildArgs) /p:EnableNgenOptimization=false
+
+ $(BuildArgs) /p:UsingToolMicrosoftNetCompilers=false
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/SourceBuild/content/repo-projects/nuget-client.proj b/src/SourceBuild/content/repo-projects/nuget-client.proj
new file mode 100644
index 000000000000..2676fa676e75
--- /dev/null
+++ b/src/SourceBuild/content/repo-projects/nuget-client.proj
@@ -0,0 +1,27 @@
+
+
+
+ $(KeysDir)NuGet.Client.snk
+ true
+
+
+ $([MSBuild]::NormalizePath('$(ProjectDirectory)', 'eng', 'source-build', 'build$(ShellExtension)'))
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/SourceBuild/content/repo-projects/razor.proj b/src/SourceBuild/content/repo-projects/razor.proj
new file mode 100644
index 000000000000..cdd00ec7c5b1
--- /dev/null
+++ b/src/SourceBuild/content/repo-projects/razor.proj
@@ -0,0 +1,20 @@
+
+
+
+
+ $(BuildArgs) /p:UsingToolMicrosoftNetCompilers=false
+
+ $(BuildArgs) /p:TargetRid=$(TargetRid)
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/SourceBuild/content/repo-projects/roslyn-analyzers.proj b/src/SourceBuild/content/repo-projects/roslyn-analyzers.proj
new file mode 100644
index 000000000000..57c6b4a5c39a
--- /dev/null
+++ b/src/SourceBuild/content/repo-projects/roslyn-analyzers.proj
@@ -0,0 +1,19 @@
+
+
+
+ true
+
+ $(BuildArgs) $(FlagParameterPrefix)warnAsError $(ArcadeFalseBoolBuildArg)
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/SourceBuild/content/repo-projects/roslyn.proj b/src/SourceBuild/content/repo-projects/roslyn.proj
new file mode 100644
index 000000000000..88d74f0a9e3f
--- /dev/null
+++ b/src/SourceBuild/content/repo-projects/roslyn.proj
@@ -0,0 +1,48 @@
+
+
+
+ true
+
+
+ $(FlagParameterPrefix)restore
+ $(BuildActions) $(FlagParameterPrefix)pack
+ $(BuildActions) $(FlagParameterPrefix)publish
+
+
+ $(ProjectDirectory)build$(ShellExtension)
+
+
+ $(BuildArgs) $(FlagParameterPrefix)officialBuildId $(OfficialBuildId)
+ $(BuildArgs) $(FlagParameterPrefix)officialSkipTests true
+ $(BuildArgs) $(FlagParameterPrefix)officialSkipApplyOptimizationData true
+ $(BuildArgs) $(FlagParameterPrefix)officialSourceBranchName placeholder
+ $(BuildArgs) $(FlagParameterPrefix)officialVisualStudioDropAccessToken placeholder
+ $(BuildArgs) /p:TargetRid=$(TargetRid)
+ $(BuildArgs) /p:TreatWarningsAsErrors=false
+ $(BuildArgs) /p:ApplyPartialNgenOptimization=false
+ $(BuildArgs) /p:EnablePartialNgenOptimization=false
+ $(BuildArgs) /p:PublishWindowsPdb=false
+
+
+ $(BuildArgs) /p:UsingToolMicrosoftNetCompilers=false
+
+ $(BuildArgs) /p:EnableNgenOptimization=false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/SourceBuild/content/repo-projects/runtime.proj b/src/SourceBuild/content/repo-projects/runtime.proj
new file mode 100644
index 000000000000..318fe6ad0878
--- /dev/null
+++ b/src/SourceBuild/content/repo-projects/runtime.proj
@@ -0,0 +1,53 @@
+
+
+
+ true
+
+ <_platformIndex>$(NETCoreSdkRuntimeIdentifier.LastIndexOf('-'))
+ $(NETCoreSdkRuntimeIdentifier.Substring(0, $(_platformIndex)))
+
+ <_platformIndex>$(NETCoreSdkPortableRuntimeIdentifier.LastIndexOf('-'))
+ $(NETCoreSdkPortableRuntimeIdentifier.Substring(0, $(_platformIndex)))-$(TargetArchitecture)
+
+
+ $(ProjectDirectory)build$(ShellExtension)
+
+ $(BuildArgs) $(FlagParameterPrefix)arch $(TargetArchitecture)
+ $(BuildArgs) $(FlagParameterPrefix)os $(TargetOS)
+ $(BuildArgs) /p:TargetRid=$(TargetRid)
+ $(BuildArgs) /p:RuntimeOS=$(RuntimeOS)
+ $(BuildArgs) /p:BaseOS=$(BaseOS)
+ $(BuildArgs) /p:DotNetBuildRuntimeWasmEnableThreads=true
+ $(BuildArgs) /p:DotNetBuildRuntimeNativeAOTRuntimePack=true
+ $(BuildArgs) $(FlagParameterPrefix)pgoinstrument
+
+
+
+ true
+ false
+
+ $(BuildArgs) /p:PortableBuild=$(PortableBuild)
+ $(BuildArgs) /p:SourceBuildNonPortable=$(BuildNonPortable)
+ $(BuildArgs) /p:UsingToolMicrosoftNetCompilers=false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/SourceBuild/content/repo-projects/scenario-tests.proj b/src/SourceBuild/content/repo-projects/scenario-tests.proj
new file mode 100644
index 000000000000..600ff4f6006b
--- /dev/null
+++ b/src/SourceBuild/content/repo-projects/scenario-tests.proj
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/SourceBuild/content/repo-projects/sdk.proj b/src/SourceBuild/content/repo-projects/sdk.proj
new file mode 100644
index 000000000000..f6579a28b578
--- /dev/null
+++ b/src/SourceBuild/content/repo-projects/sdk.proj
@@ -0,0 +1,41 @@
+
+
+
+ true
+
+ $(BuildArgs) /p:PackageProjectUrl=https://github.com/dotnet/sdk
+ $(BuildArgs) /p:PortableRid=$(PortableRid)
+ $(BuildArgs) /p:TargetRid=$(TargetRid)
+
+
+ $(BuildArgs) /p:NativeAotSupported=false
+ $(BuildArgs) $(FlagParameterPrefix)v $(LogVerbosity)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/SourceBuild/content/repo-projects/source-build-externals.proj b/src/SourceBuild/content/repo-projects/source-build-externals.proj
new file mode 100644
index 000000000000..023a6739cc84
--- /dev/null
+++ b/src/SourceBuild/content/repo-projects/source-build-externals.proj
@@ -0,0 +1,18 @@
+
+
+
+
+ true
+
+
+
+
+
+
+
+
diff --git a/src/SourceBuild/content/repo-projects/source-build-reference-packages.proj b/src/SourceBuild/content/repo-projects/source-build-reference-packages.proj
new file mode 100644
index 000000000000..87f39652cf73
--- /dev/null
+++ b/src/SourceBuild/content/repo-projects/source-build-reference-packages.proj
@@ -0,0 +1,34 @@
+
+
+
+
+ true
+
+
+ true
+
+ $(ArtifactsObjDir)source-build-reference-package-cache
+
+ $(BuildArgs) /p:MicrosoftNetCoreIlasmPackageRuntimeId=$(NETCoreSdkRuntimeIdentifier)
+ $(BuildArgs) /p:LocalNuGetPackageCacheDirectory=$(LocalNuGetPackageCacheDirectory)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/SourceBuild/content/repo-projects/sourcelink.proj b/src/SourceBuild/content/repo-projects/sourcelink.proj
new file mode 100644
index 000000000000..6e6127664f5d
--- /dev/null
+++ b/src/SourceBuild/content/repo-projects/sourcelink.proj
@@ -0,0 +1,19 @@
+
+
+
+ true
+
+
+ AllPackages
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/SourceBuild/content/repo-projects/symreader.proj b/src/SourceBuild/content/repo-projects/symreader.proj
new file mode 100644
index 000000000000..9deac728bc91
--- /dev/null
+++ b/src/SourceBuild/content/repo-projects/symreader.proj
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/SourceBuild/content/repo-projects/templating.proj b/src/SourceBuild/content/repo-projects/templating.proj
new file mode 100644
index 000000000000..82538bf0d034
--- /dev/null
+++ b/src/SourceBuild/content/repo-projects/templating.proj
@@ -0,0 +1,20 @@
+
+
+
+ true
+
+ $(BuildArgs) $(FlagParameterPrefix)v $(LogVerbosity)
+ $(BuildArgs) $(FlagParameterPrefix)warnAsError $(ArcadeFalseBoolBuildArg)
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/SourceBuild/content/repo-projects/test-templates.proj b/src/SourceBuild/content/repo-projects/test-templates.proj
new file mode 100644
index 000000000000..64bf44782cca
--- /dev/null
+++ b/src/SourceBuild/content/repo-projects/test-templates.proj
@@ -0,0 +1,15 @@
+
+
+
+ true
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/SourceBuild/content/repo-projects/vstest.proj b/src/SourceBuild/content/repo-projects/vstest.proj
new file mode 100644
index 000000000000..856aa8e46739
--- /dev/null
+++ b/src/SourceBuild/content/repo-projects/vstest.proj
@@ -0,0 +1,20 @@
+
+
+
+ true
+
+ $(BuildArgs) /p:SemanticVersioningV1=true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/SourceBuild/content/repo-projects/windowsdesktop.proj b/src/SourceBuild/content/repo-projects/windowsdesktop.proj
new file mode 100644
index 000000000000..a848cfa7a462
--- /dev/null
+++ b/src/SourceBuild/content/repo-projects/windowsdesktop.proj
@@ -0,0 +1,15 @@
+
+
+
+ true
+
+ $(BuildArgs) $(FlagParameterPrefix)v $(LogVerbosity)
+ $(BuildArgs) $(FlagParameterPrefix)warnAsError $(ArcadeFalseBoolBuildArg)
+
+
+
+
+
+
+
+
diff --git a/src/SourceBuild/content/repo-projects/winforms.proj b/src/SourceBuild/content/repo-projects/winforms.proj
new file mode 100644
index 000000000000..9981d3347f2b
--- /dev/null
+++ b/src/SourceBuild/content/repo-projects/winforms.proj
@@ -0,0 +1,15 @@
+
+
+
+ true
+
+ $(BuildArgs) $(FlagParameterPrefix)v $(LogVerbosity)
+ $(BuildArgs) $(FlagParameterPrefix)NativeToolsOnMachine
+
+
+
+
+
+
+
+
diff --git a/src/SourceBuild/content/repo-projects/wpf.proj b/src/SourceBuild/content/repo-projects/wpf.proj
new file mode 100644
index 000000000000..8e8cca9ce6d8
--- /dev/null
+++ b/src/SourceBuild/content/repo-projects/wpf.proj
@@ -0,0 +1,16 @@
+
+
+
+ true
+ false
+
+ $(BuildArgs) $(FlagParameterPrefix)v $(LogVerbosity)
+ $(BuildArgs) $(FlagParameterPrefix)warnAsError $(ArcadeFalseBoolBuildArg)
+
+
+
+
+
+
+
+
diff --git a/src/SourceBuild/content/repo-projects/xdt.proj b/src/SourceBuild/content/repo-projects/xdt.proj
new file mode 100644
index 000000000000..6083e10b0d3f
--- /dev/null
+++ b/src/SourceBuild/content/repo-projects/xdt.proj
@@ -0,0 +1,11 @@
+
+
+
+ true
+
+
+
+
+
+
+
diff --git a/src/SourceBuild/content/src/Directory.Build.props b/src/SourceBuild/content/src/Directory.Build.props
new file mode 100644
index 000000000000..d88986f1edf8
--- /dev/null
+++ b/src/SourceBuild/content/src/Directory.Build.props
@@ -0,0 +1,6 @@
+
+
+
+
+
diff --git a/src/SourceBuild/content/src/Directory.Build.targets b/src/SourceBuild/content/src/Directory.Build.targets
new file mode 100644
index 000000000000..0e594c6ed93c
--- /dev/null
+++ b/src/SourceBuild/content/src/Directory.Build.targets
@@ -0,0 +1,6 @@
+
+
+
+
+
diff --git a/src/SourceBuild/content/src/Directory.Packages.props b/src/SourceBuild/content/src/Directory.Packages.props
new file mode 100644
index 000000000000..10c05fd99a6c
--- /dev/null
+++ b/src/SourceBuild/content/src/Directory.Packages.props
@@ -0,0 +1,9 @@
+
+
+
+
+ false
+
+
+
diff --git a/src/SourceBuild/content/test/Directory.Build.props b/src/SourceBuild/content/test/Directory.Build.props
new file mode 100644
index 000000000000..12ee09caa8c7
--- /dev/null
+++ b/src/SourceBuild/content/test/Directory.Build.props
@@ -0,0 +1,15 @@
+
+
+
+
+ $([MSBuild]::NormalizeDirectory('$(RestorePackagesPath)', 'tests'))
+
+
+
+
+
+ enable
+ $(ArtifactsTestResultsDir)
+
+
+
diff --git a/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/ArtifactsSizeTest.cs b/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/ArtifactsSizeTest.cs
new file mode 100644
index 000000000000..d4dd073ce24a
--- /dev/null
+++ b/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/ArtifactsSizeTest.cs
@@ -0,0 +1,216 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.IO;
+using System.Linq;
+using System.Collections;
+using System.Collections.Generic;
+using System.Text.RegularExpressions;
+using System.Formats.Tar;
+using System.Text;
+using System.Threading.Tasks;
+using TestUtilities;
+using Xunit;
+using Xunit.Abstractions;
+
+namespace Microsoft.DotNet.SourceBuild.SmokeTests;
+
+[Trait("Category", "SdkContent")]
+public class ArtifactsSizeTest : SdkTests
+{
+ private const int SizeThresholdPercentage = 25;
+ private static readonly string BaselineFilePath = BaselineHelper.GetBaselineFilePath($"ArtifactsSizes/{Config.TargetRid}.txt");
+ private readonly Dictionary Baseline = new();
+ private Dictionary FilePathCountMap = new();
+ private StringBuilder Differences = new();
+
+ public ArtifactsSizeTest(ITestOutputHelper outputHelper) : base(outputHelper)
+ {
+ if (File.Exists(BaselineFilePath))
+ {
+ string[] baselineFileContent = File.ReadAllLines(BaselineFilePath);
+ foreach (string entry in baselineFileContent)
+ {
+ string[] splitEntry = entry.Split(':', StringSplitOptions.TrimEntries);
+ Baseline[splitEntry[0]] = long.Parse(splitEntry[1]);
+ }
+ }
+ else
+ {
+ Assert.Fail($"Baseline file `{BaselineFilePath}' does not exist. Please create the baseline file then rerun the test.");
+ }
+ }
+
+ [ConditionalFact(typeof(Config), nameof(Config.IncludeArtifactsSizeTests))]
+ public void CompareArtifactsToBaseline()
+ {
+ Assert.False(string.IsNullOrWhiteSpace(Config.SourceBuiltArtifactsPath));
+ Assert.False(string.IsNullOrWhiteSpace(Config.SdkTarballPath));
+
+ var tarEntries = ProcessSdkAndArtifactsTarballs();
+ ScanForDifferences(tarEntries);
+ UpdateBaselineFile();
+
+ // Must wait to report differences until after the baseline file is updated else a failure
+ // will cause the baseline file to not be updated.
+ ReportDifferences();
+ }
+
+ private Dictionary ProcessSdkAndArtifactsTarballs()
+ {
+ string tempTarballDir = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
+ Directory.CreateDirectory(tempTarballDir);
+
+ Utilities.ExtractTarball(Config.SdkTarballPath!, tempTarballDir, OutputHelper);
+ Utilities.ExtractTarball(Config.SourceBuiltArtifactsPath!, tempTarballDir, OutputHelper);
+
+ Dictionary tarEntries = Directory.EnumerateFiles(tempTarballDir, "*", SearchOption.AllDirectories)
+ .Where(filePath => !filePath.Contains("SourceBuildReferencePackages"))
+ .Select(filePath =>
+ {
+ string relativePath = filePath.Substring(tempTarballDir.Length + 1);
+ return (ProcessFilePath(relativePath), new FileInfo(filePath).Length);
+ })
+ .ToDictionary(tuple => tuple.Item1, tuple => tuple.Item2);
+
+ Directory.Delete(tempTarballDir, true);
+
+ return tarEntries;
+ }
+
+ private string ProcessFilePath(string originalPath)
+ {
+ string result = BaselineHelper.RemoveRids(originalPath);
+ result = BaselineHelper.RemoveVersions(result);
+
+ return AddDifferenciatingSuffix(result);
+ }
+
+ // Because version numbers are abstracted, it is possible to have duplicate FilePath entries.
+ // This code adds a numeric suffix to differentiate duplicate FilePath entries.
+ private string AddDifferenciatingSuffix(string filePath)
+ {
+ string[] patterns = {@"x\.y\.z", @"x\.y(?!\.z)"};
+ int matchIndex = -1;
+ string matchPattern = "";
+ foreach (string pattern in patterns)
+ {
+ MatchCollection matches = Regex.Matches(filePath, pattern);
+
+ if (matches.Count > 0)
+ {
+ if (matches[matches.Count - 1].Index > matchIndex)
+ {
+ matchIndex = matches[matches.Count - 1].Index;
+ matchPattern = matches[matches.Count - 1].Value;
+ }
+ }
+ }
+
+ if (matchIndex != -1)
+ {
+ int count = FilePathCountMap.TryGetValue(filePath, out count) ? count : 0;
+ FilePathCountMap[filePath] = count + 1;
+
+ if (count > 0)
+ {
+ return filePath.Substring(0, matchIndex) + $"{matchPattern}-{count}" + filePath.Substring(matchIndex + matchPattern.Length);
+ }
+ }
+
+ return filePath;
+ }
+
+ private void ScanForDifferences(Dictionary tarEntries)
+ {
+ foreach (var entry in tarEntries)
+ {
+ if (!Baseline.TryGetValue(entry.Key, out long baselineBytes))
+ {
+ TrackDifference($"{entry.Key} does not exist in baseline. It is {entry.Value} bytes. Adding it to the baseline file.");
+ Baseline.Add(entry.Key, entry.Value);
+ }
+ else
+ {
+ CompareFileSizes(entry.Key, entry.Value, baselineBytes);
+ }
+ }
+
+ foreach (var removedFile in Baseline.Keys.Except(tarEntries.Keys))
+ {
+ TrackDifference($"`{removedFile}` is no longer being produced. It was {Baseline[removedFile]} bytes.");
+ Baseline.Remove(removedFile);
+ }
+ }
+
+ private void CompareFileSizes(string filePath, long fileSize, long baselineSize)
+ {
+ // Only update the baseline with breaking differences. Non-breaking differences are file size changes
+ // less than the threshold percentage. This makes it easier to review the breaking changes and prevents
+ // inadvertently allowing small percentage changes to be accepted that can add up to a significant
+ // difference over time.
+ string breakingDifference = string.Empty;
+
+ if (fileSize == 0 && baselineSize != 0)
+ {
+ breakingDifference = $"'{filePath}' is now 0 bytes. It was {baselineSize} bytes.";
+ }
+ else if (fileSize != 0 && baselineSize == 0)
+ {
+ breakingDifference = $"'{filePath}' is no longer 0 bytes. It is now {fileSize} bytes.";
+ }
+ else if (baselineSize != 0 && (((fileSize - baselineSize) / (double)baselineSize) * 100) >= SizeThresholdPercentage)
+ {
+ breakingDifference =
+ $"'{filePath}' increased in size by more than {SizeThresholdPercentage}%. It was originally {baselineSize} bytes and is now {fileSize} bytes.";
+ }
+ else if (baselineSize != 0 && (((baselineSize - fileSize) / (double)baselineSize) * 100) >= SizeThresholdPercentage)
+ {
+ breakingDifference =
+ $"'{filePath}' decreased in size by more than {SizeThresholdPercentage}%. It was originally {baselineSize} bytes and is now {fileSize} bytes.";
+ }
+
+ if (!string.IsNullOrEmpty(breakingDifference))
+ {
+ TrackDifference(breakingDifference);
+ Baseline[filePath] = fileSize;
+ }
+ }
+
+ private void TrackDifference(string difference) => Differences.AppendLine(difference);
+
+ private void ReportDifferences()
+ {
+ if (Differences.Length > 0)
+ {
+ if (Config.WarnOnSdkContentDiffs)
+ {
+ OutputHelper.LogWarningMessage(Differences.ToString());
+ }
+ else
+ {
+ OutputHelper.WriteLine(Differences.ToString());
+ Assert.Fail("Differences were found in the artifacts sizes.");
+ }
+ }
+ }
+
+ private void UpdateBaselineFile()
+ {
+ try
+ {
+ string actualFilePath = Path.Combine(Config.LogsDirectory, $"UpdatedArtifactsSizes_{Config.TargetRid}.txt");
+ File.WriteAllLines(
+ actualFilePath,
+ Baseline
+ .OrderBy(kvp => kvp.Key)
+ .Select(kvp => $"{kvp.Key}: {kvp.Value}"));
+ }
+ catch (IOException ex)
+ {
+ throw new InvalidOperationException($"An error occurred while copying the baselines file: {BaselineFilePath}", ex);
+ }
+ }
+}
diff --git a/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/BaselineHelper.cs b/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/BaselineHelper.cs
new file mode 100644
index 000000000000..d9a355c2bb28
--- /dev/null
+++ b/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/BaselineHelper.cs
@@ -0,0 +1,135 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.Text.RegularExpressions;
+using Microsoft.Extensions.FileSystemGlobbing;
+using Xunit;
+using Xunit.Abstractions;
+
+namespace Microsoft.DotNet.SourceBuild.SmokeTests
+{
+ internal class BaselineHelper
+ {
+ private const string SemanticVersionPlaceholder = "x.y.z";
+ private const string SemanticVersionPlaceholderMatchingPattern = "*.*.*"; // wildcard pattern used to match on the version represented by the placeholder
+ private const string NonSemanticVersionPlaceholder = "x.y";
+ private const string NonSemanticVersionPlaceholderMatchingPattern = "*.*"; // wildcard pattern used to match on the version represented by the placeholder
+
+ public static void CompareEntries(string baselineFileName, IOrderedEnumerable actualEntries)
+ {
+ IEnumerable baseline = File.ReadAllLines(GetBaselineFilePath(baselineFileName));
+ string[] missingEntries = actualEntries.Except(baseline).ToArray();
+ string[] extraEntries = baseline.Except(actualEntries).ToArray();
+
+ string? message = null;
+ if (missingEntries.Length > 0)
+ {
+ message = $"Missing entries in '{baselineFileName}' baseline: {Environment.NewLine}{string.Join(Environment.NewLine, missingEntries)}{Environment.NewLine}{Environment.NewLine}";
+ }
+
+ if (extraEntries.Length > 0)
+ {
+ message += $"Extra entries in '{baselineFileName}' baseline: {Environment.NewLine}{string.Join(Environment.NewLine, extraEntries)}{Environment.NewLine}{Environment.NewLine}";
+ }
+
+ Assert.Null(message);
+ }
+
+ public static void CompareBaselineContents(string baselineFileName, string actualContents, ITestOutputHelper outputHelper, bool warnOnDiffs = false, string baselineSubDir = "")
+ {
+ string actualFilePath = Path.Combine(Config.LogsDirectory, $"Updated{baselineFileName}");
+ File.WriteAllText(actualFilePath, actualContents);
+
+ CompareFiles(GetBaselineFilePath(baselineFileName, baselineSubDir), actualFilePath, outputHelper, warnOnDiffs);
+ }
+
+ public static void CompareFiles(string expectedFilePath, string actualFilePath, ITestOutputHelper outputHelper, bool warnOnDiffs = false)
+ {
+ string baselineFileText = File.ReadAllText(expectedFilePath).Trim();
+ string actualFileText = File.ReadAllText(actualFilePath).Trim();
+
+ string? message = null;
+
+ if (baselineFileText != actualFileText)
+ {
+ // Retrieve a diff in order to provide a UX which calls out the diffs.
+ string diff = DiffFiles(expectedFilePath, actualFilePath, outputHelper);
+ string prefix = warnOnDiffs ? "##vso[task.logissue type=warning;]" : string.Empty;
+ message = $"{Environment.NewLine}{prefix}Expected file '{expectedFilePath}' does not match actual file '{actualFilePath}`. {Environment.NewLine}"
+ + $"{diff}{Environment.NewLine}";
+
+ if (warnOnDiffs)
+ {
+ outputHelper.WriteLine(message);
+ outputHelper.WriteLine("##vso[task.complete result=SucceededWithIssues;]");
+ }
+ }
+
+ if (!warnOnDiffs)
+ {
+ Assert.Null(message);
+ }
+ }
+
+ public static string DiffFiles(string file1Path, string file2Path, ITestOutputHelper outputHelper)
+ {
+ (Process Process, string StdOut, string StdErr) diffResult =
+ ExecuteHelper.ExecuteProcess("git", $"diff --no-index {file1Path} {file2Path}", outputHelper);
+
+ return diffResult.StdOut;
+ }
+
+ public static string GetAssetsDirectory() => Path.Combine(Directory.GetCurrentDirectory(), "assets");
+
+ public static string GetBaselineFilePath(string baselineFileName, string baselineSubDir = "") =>
+ Path.Combine(GetAssetsDirectory(), "baselines", baselineSubDir, baselineFileName);
+
+ public static string RemoveRids(string diff, bool isPortable = false) =>
+ isPortable ? diff.Replace(Config.PortableRid, "portable-rid") : diff.Replace(Config.TargetRid, "banana-rid");
+
+ public static string RemoveVersions(string source)
+ {
+ // Remove version numbers for examples like "roslyn4.1", "net8.0", and "netstandard2.1".
+ string pathSeparator = Regex.Escape(Path.DirectorySeparatorChar.ToString());
+ string result = Regex.Replace(source, $@"{pathSeparator}(net|roslyn)[1-9]+\.[0-9]+{pathSeparator}", match =>
+ {
+ string wordPart = match.Groups[1].Value;
+ return $"{Path.DirectorySeparatorChar}{wordPart}{NonSemanticVersionPlaceholder}{Path.DirectorySeparatorChar}";
+ });
+
+ // Remove semantic versions
+ // Regex source: https://semver.org/#is-there-a-suggested-regular-expression-regex-to-check-a-semver-string
+ // The regex from https://semver.org has been modified to account for the following:
+ // - The version should be preceded by a path separator, '.', '-', or '/'
+ // - The version should match a release identifier that begins with '.' or '-'
+ // - The version may have one or more release identifiers that begin with '.' or '-'
+ // - The version should end before a path separator, '.', '-', or '/'
+ Regex semanticVersionRegex = new(
+ @"(?<=[./-])(0|[1-9]\d*)\.(0|[1-9]\d*)(\.(0|[1-9]\d*))+"
+ + @"(((?:[-.]((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)))+"
+ + @"(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?"
+ + @"(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?(?=[/.-])");
+ return semanticVersionRegex.Replace(result, SemanticVersionPlaceholder);
+ }
+
+ ///
+ /// This returns a that can be used to match on a path whose versions have been removed via
+ /// .
+ ///
+ public static Matcher GetFileMatcherFromPath(string path)
+ {
+ path = path
+ .Replace(SemanticVersionPlaceholder, SemanticVersionPlaceholderMatchingPattern)
+ .Replace(NonSemanticVersionPlaceholder, NonSemanticVersionPlaceholderMatchingPattern);
+ Matcher matcher = new();
+ matcher.AddInclude(path);
+ return matcher;
+ }
+ }
+}
diff --git a/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/BasicScenarioTests.cs b/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/BasicScenarioTests.cs
new file mode 100644
index 000000000000..eb15bfb9b54c
--- /dev/null
+++ b/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/BasicScenarioTests.cs
@@ -0,0 +1,44 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Xunit;
+using Xunit.Abstractions;
+
+namespace Microsoft.DotNet.SourceBuild.SmokeTests;
+
+///
+/// Basic project create, build, run, publish scenario tests.
+/// for related web scenarios.
+/// They are encapsulated in a separate testclass so that they can be run in parallel.
+///
+public class BasicScenarioTests : SdkTests
+{
+ public BasicScenarioTests(ITestOutputHelper outputHelper) : base(outputHelper) { }
+
+ [Theory]
+ [MemberData(nameof(GetScenarioObjects))]
+ public void VerifyScenario(TestScenario scenario) => scenario.Execute(DotNetHelper);
+
+ public static IEnumerable