diff --git a/eng/ManualVersions.props b/eng/ManualVersions.props
index fb41f0bad..57a4b6415 100644
--- a/eng/ManualVersions.props
+++ b/eng/ManualVersions.props
@@ -9,11 +9,11 @@
Basically: In this file, choose the highest version when resolving merge conflicts.
-->
- 10.0.17763.29
- 10.0.18362.29
- 10.0.19041.29
- 10.0.20348.29
- 10.0.22000.29
- 10.0.22621.29
+ 10.0.17763.31
+ 10.0.18362.31
+ 10.0.19041.31
+ 10.0.20348.31
+ 10.0.22000.31
+ 10.0.22621.31
diff --git a/eng/common/sdk-task.ps1 b/eng/common/sdk-task.ps1
index 6c4ac6fec..91f8196cc 100644
--- a/eng/common/sdk-task.ps1
+++ b/eng/common/sdk-task.ps1
@@ -64,7 +64,7 @@ try {
$GlobalJson.tools | Add-Member -Name "vs" -Value (ConvertFrom-Json "{ `"version`": `"16.5`" }") -MemberType NoteProperty
}
if( -not ($GlobalJson.tools.PSObject.Properties.Name -match "xcopy-msbuild" )) {
- $GlobalJson.tools | Add-Member -Name "xcopy-msbuild" -Value "17.6.0-2" -MemberType NoteProperty
+ $GlobalJson.tools | Add-Member -Name "xcopy-msbuild" -Value "17.7.2-1" -MemberType NoteProperty
}
if ($GlobalJson.tools."xcopy-msbuild".Trim() -ine "none") {
$xcopyMSBuildToolsFolder = InitializeXCopyMSBuild $GlobalJson.tools."xcopy-msbuild" -install $true
diff --git a/eng/common/tools.ps1 b/eng/common/tools.ps1
index aa74ab4a8..84cfe7cd9 100644
--- a/eng/common/tools.ps1
+++ b/eng/common/tools.ps1
@@ -379,13 +379,13 @@ function InitializeVisualStudioMSBuild([bool]$install, [object]$vsRequirements =
}
# Minimum VS version to require.
- $vsMinVersionReqdStr = '17.6'
+ $vsMinVersionReqdStr = '17.7'
$vsMinVersionReqd = [Version]::new($vsMinVersionReqdStr)
# If the version of msbuild is going to be xcopied,
# use this version. Version matches a package here:
- # https://dev.azure.com/dnceng/public/_artifacts/feed/dotnet-eng/NuGet/RoslynTools.MSBuild/versions/17.6.0-2
- $defaultXCopyMSBuildVersion = '17.6.0-2'
+ # https://dev.azure.com/dnceng/public/_artifacts/feed/dotnet-eng/NuGet/RoslynTools.MSBuild/versions/17.7.2-1
+ $defaultXCopyMSBuildVersion = '17.7.2-1'
if (!$vsRequirements) {
if (Get-Member -InputObject $GlobalJson.tools -Name 'vs') {
diff --git a/eng/install-scancode.sh b/eng/install-scancode.sh
new file mode 100755
index 000000000..9b705f624
--- /dev/null
+++ b/eng/install-scancode.sh
@@ -0,0 +1,22 @@
+#!/bin/bash
+
+set -euo pipefail
+
+# https://scancode-toolkit.readthedocs.io/en/latest/getting-started/install.html#installation-as-a-library-via-pip
+
+pyEnvPath="/tmp/scancode-env"
+python3 -m venv $pyEnvPath
+source $pyEnvPath/bin/activate
+pip install scancode-toolkit
+deactivate
+
+# Setup a script which executes scancode in the virtual environment
+cat > /usr/local/bin/scancode << EOF
+#!/bin/bash
+set -euo pipefail
+source $pyEnvPath/bin/activate
+scancode "\$@"
+deactivate
+EOF
+
+chmod +x /usr/local/bin/scancode
diff --git a/eng/pipelines/source-build-license-scan.yml b/eng/pipelines/source-build-license-scan.yml
new file mode 100644
index 000000000..bcf7e84f1
--- /dev/null
+++ b/eng/pipelines/source-build-license-scan.yml
@@ -0,0 +1,137 @@
+# Pipeline documentation at https://github.com/dotnet/dotnet/blob/main/docs/license-scanning.md
+
+schedules:
+- cron: "0 7 * * 1-5"
+ displayName: Run on weekdays at 7am UTC
+ branches:
+ include:
+ - main
+ - release/*
+
+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: $(Build.SourcesDirectory)/prep.sh --no-artifacts --no-bootstrap --no-prebuilts
+ displayName: 'Install .NET SDK'
+
+ - 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
+ -e SMOKE_TESTS_LICENSE_SCAN_PATH=$(repoPath)
+ -e SMOKE_TESTS_RUNNING_IN_CI=true
+ -e SMOKE_TESTS_WARN_LICENSE_SCAN_DIFFS=false
+ -e SMOKE_TESTS_TARGET_RID=linux-x64
+ -e SMOKE_TESTS_PORTABLE_RID=linux-x64
+ displayName: Run Tests
+ workingDirectory: $(Build.SourcesDirectory)
+
+ - script: |
+ set -x
+ targetFolder=$(Build.StagingDirectory)/BuildLogs/
+ mkdir -p ${targetFolder}
+ cd "$(Build.SourcesDirectory)"
+ find artifacts/ -type f -name "BuildTests*.binlog" -exec cp {} --parents -t ${targetFolder} \;
+ find artifacts/ -type f -name "BuildTests*.log" -exec cp {} --parents -t ${targetFolder} \;
+ echo "Updated:"
+ find test/ -type f -name "Updated*.json"
+ find test/ -type f -name "Updated*.json" -exec cp {} --parents -t ${targetFolder} \;
+ echo "Results:"
+ find test/ -type f -name "scancode-results*.json"
+ find test/ -type f -name "scancode-results*.json" -exec cp {} --parents -t ${targetFolder} \;
+ echo "All:"
+ ls -R test/
+ 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/eng/pipelines/templates/jobs/vmr-build.yml b/eng/pipelines/templates/jobs/vmr-build.yml
index f11af8c4b..041a47580 100644
--- a/eng/pipelines/templates/jobs/vmr-build.yml
+++ b/eng/pipelines/templates/jobs/vmr-build.yml
@@ -127,7 +127,7 @@ jobs:
artifact: ${{ parameters.reuseBuildArtifactsFrom }}_${{ parameters.architecture }}_Artifacts
patterns: |
**/Private.SourceBuilt.Artifacts.*.tar.gz
- **/dotnet-sdk-+([0-9]).+([0-9]).+([0-9])*.tar.gz
+ **/dotnet-sdk-*.tar.gz
displayName: Download Previous Build
- task: CopyFiles@2
diff --git a/eng/pipelines/vmr-sync-internal.yml b/eng/pipelines/vmr-sync-internal.yml
index 176f98910..0a10a518e 100644
--- a/eng/pipelines/vmr-sync-internal.yml
+++ b/eng/pipelines/vmr-sync-internal.yml
@@ -5,6 +5,10 @@ trigger:
branches:
include:
- internal/release/*
+ exclude:
+ - internal/release/*.0.2xx
+ - internal/release/*.0.3xx
+ - internal/release/*.0.4xx
resources:
repositories:
diff --git a/eng/pipelines/vmr-sync.yml b/eng/pipelines/vmr-sync.yml
index aa12adcbe..6659887f1 100644
--- a/eng/pipelines/vmr-sync.yml
+++ b/eng/pipelines/vmr-sync.yml
@@ -6,6 +6,10 @@ trigger:
include:
- main
- release/*
+ exclude:
+ - release/*.0.2xx
+ - release/*.0.3xx
+ - release/*.0.4xx
resources:
repositories:
diff --git a/src/SourceBuild/content/build.proj b/src/SourceBuild/content/build.proj
index 6b67024d0..193bbd6f0 100644
--- a/src/SourceBuild/content/build.proj
+++ b/src/SourceBuild/content/build.proj
@@ -92,7 +92,7 @@
DiscoverSymbolsTarballs;
ExtractSymbolsTarballs">
- $(OutputPath)dotnet-symbols-$(MicrosoftSourceBuildIntermediateInstallerVersion)-$(TargetRid).tar.gz
+ $(OutputPath)dotnet-symbols-all-$(MicrosoftSourceBuildIntermediateInstallerVersion)-$(TargetRid).tar.gz
-
+
$(ArtifactsTmpDir)SdkSymbols
- $(OutputPath)dotnet-sdk-symbols-$(MicrosoftSourceBuildIntermediateInstallerVersion)-$(TargetRid).tar.gz
+ $(OutputPath)dotnet-symbols-sdk-$(MicrosoftSourceBuildIntermediateInstallerVersion)-$(TargetRid).tar.gz
$(ArtifactsTmpDir)Sdk
%(SdkTarballItem.Identity)
@@ -183,8 +182,7 @@
-
+
diff --git a/src/SourceBuild/content/docs/license-scanning.md b/src/SourceBuild/content/docs/license-scanning.md
new file mode 100644
index 000000000..6007776ab
--- /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/tools/tasks/Microsoft.DotNet.SourceBuild.Tasks.LeakDetection/MarkAndCatalogPackages.cs b/src/SourceBuild/content/eng/tools/tasks/Microsoft.DotNet.SourceBuild.Tasks.LeakDetection/MarkAndCatalogPackages.cs
index 270fb11f9..32abcae71 100644
--- 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
@@ -155,6 +155,7 @@ namespace Microsoft.DotNet.SourceBuild.Tasks.LeakDetection
}
File.Delete(p.ItemSpec);
File.Move(poisonedPackagePath, p.ItemSpec);
+ Directory.Delete(packageTempPath, true);
}
}
diff --git a/src/SourceBuild/content/eng/tools/tasks/Microsoft.DotNet.SourceBuild.Tasks.XPlat/CreateSdkSymbolsLayout.cs b/src/SourceBuild/content/eng/tools/tasks/Microsoft.DotNet.SourceBuild.Tasks.XPlat/CreateSdkSymbolsLayout.cs
index 8802dd543..7e00bbf30 100644
--- a/src/SourceBuild/content/eng/tools/tasks/Microsoft.DotNet.SourceBuild.Tasks.XPlat/CreateSdkSymbolsLayout.cs
+++ b/src/SourceBuild/content/eng/tools/tasks/Microsoft.DotNet.SourceBuild.Tasks.XPlat/CreateSdkSymbolsLayout.cs
@@ -57,7 +57,7 @@ namespace Microsoft.DotNet.Build.Tasks
private void LogErrorOrWarning(bool isError, string message)
{
- if (FailOnMissingPDBs)
+ if (isError)
{
Log.LogError(message);
}
@@ -104,14 +104,15 @@ namespace Microsoft.DotNet.Build.Tasks
if (guid != string.Empty)
{
- if (!allPdbGuids.ContainsKey(guid))
+ 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[guid]!;
+ string sourcePath = (string)allPdbGuids[debugId]!;
string destinationPath =
file.Replace(SdkLayoutPath, SdkSymbolsLayoutPath)
.Replace(Path.GetFileName(file), Path.GetFileName(sourcePath));
@@ -142,13 +143,21 @@ namespace Microsoft.DotNet.Build.Tasks
var id = new BlobContentId(metadataReader.DebugMetadataHeader.Id);
string guid = $"{id.Guid:N}";
- if (!string.IsNullOrEmpty(guid) && !allPdbGuids.ContainsKey(guid))
+ string debugId = GetDebugId(guid, file);
+ if (!string.IsNullOrEmpty(guid) && !allPdbGuids.ContainsKey(debugId))
{
- allPdbGuids.Add(guid, file);
+ 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/test/Microsoft.DotNet.SourceBuild.SmokeTests/BaselineHelper.cs b/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/BaselineHelper.cs
index b8426c2a3..49ee98d73 100644
--- a/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/BaselineHelper.cs
+++ b/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/BaselineHelper.cs
@@ -41,12 +41,12 @@ namespace Microsoft.DotNet.SourceBuild.SmokeTests
Assert.Null(message);
}
- public static void CompareBaselineContents(string baselineFileName, string actualContents, ITestOutputHelper outputHelper, bool warnOnDiffs = false)
+ public static void CompareBaselineContents(string baselineFileName, string actualContents, ITestOutputHelper outputHelper, bool warnOnDiffs = false, string baselineSubDir = "")
{
- string actualFilePath = Path.Combine(DotNetHelper.LogsDirectory, $"Updated{baselineFileName}");
+ string actualFilePath = Path.Combine(TestBase.LogsDirectory, $"Updated{baselineFileName}");
File.WriteAllText(actualFilePath, actualContents);
- CompareFiles(GetBaselineFilePath(baselineFileName), actualFilePath, outputHelper, warnOnDiffs);
+ CompareFiles(GetBaselineFilePath(baselineFileName, baselineSubDir), actualFilePath, outputHelper, warnOnDiffs);
}
public static void CompareFiles(string expectedFilePath, string actualFilePath, ITestOutputHelper outputHelper, bool warnOnDiffs = false)
@@ -87,7 +87,8 @@ namespace Microsoft.DotNet.SourceBuild.SmokeTests
public static string GetAssetsDirectory() => Path.Combine(Directory.GetCurrentDirectory(), "assets");
- public static string GetBaselineFilePath(string baselineFileName) => Path.Combine(GetAssetsDirectory(), "baselines", baselineFileName);
+ public static string GetBaselineFilePath(string baselineFileName, string baselineSubDir = "") =>
+ Path.Combine(GetAssetsDirectory(), "baselines", baselineSubDir, baselineFileName);
public static string RemoveNetTfmPaths(string source)
{
diff --git a/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/BasicScenarioTests.cs b/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/BasicScenarioTests.cs
index 4f1aafc68..d2280add4 100644
--- a/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/BasicScenarioTests.cs
+++ b/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/BasicScenarioTests.cs
@@ -15,7 +15,7 @@ namespace Microsoft.DotNet.SourceBuild.SmokeTests;
/// for related web scenarios.
/// They are encapsulated in a separate testclass so that they can be run in parallel.
///
-public class BasicScenarioTests : SmokeTests
+public class BasicScenarioTests : SdkTests
{
public BasicScenarioTests(ITestOutputHelper outputHelper) : base(outputHelper) { }
diff --git a/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/Config.cs b/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/Config.cs
index 85dd0eae3..8a3c6ce73 100644
--- a/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/Config.cs
+++ b/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/Config.cs
@@ -20,7 +20,9 @@ internal static class Config
public const string SourceBuiltArtifactsPathEnv = "SMOKE_TESTS_SOURCEBUILT_ARTIFACTS_PATH";
public const string TargetRidEnv = "SMOKE_TESTS_TARGET_RID";
public const string WarnSdkContentDiffsEnv = "SMOKE_TESTS_WARN_SDK_CONTENT_DIFFS";
+ public const string WarnLicenseScanDiffsEnv = "SMOKE_TESTS_WARN_LICENSE_SCAN_DIFFS";
public const string RunningInCIEnv = "SMOKE_TESTS_RUNNING_IN_CI";
+ public const string LicenseScanPathEnv = "SMOKE_TESTS_LICENSE_SCAN_PATH";
public static string DotNetDirectory { get; } =
Environment.GetEnvironmentVariable(DotNetDirectoryEnv) ?? Path.Combine(Directory.GetCurrentDirectory(), ".dotnet");
@@ -31,15 +33,18 @@ internal static class Config
public static string? PrereqsPath { get; } = Environment.GetEnvironmentVariable(PrereqsPathEnv);
public static string? CustomPackagesPath { get; } = Environment.GetEnvironmentVariable(CustomPackagesPathEnv);
public static string? SdkTarballPath { get; } = Environment.GetEnvironmentVariable(SdkTarballPathEnv);
- public static string SourceBuiltArtifactsPath { get; } = Environment.GetEnvironmentVariable(SourceBuiltArtifactsPathEnv) ??
- throw new InvalidOperationException($"'{Config.SourceBuiltArtifactsPathEnv}' must be specified");
+ public static string? SourceBuiltArtifactsPath { get; } = Environment.GetEnvironmentVariable(SourceBuiltArtifactsPathEnv);
public static string TargetRid { get; } = Environment.GetEnvironmentVariable(TargetRidEnv) ??
throw new InvalidOperationException($"'{Config.TargetRidEnv}' must be specified");
public static string TargetArchitecture { get; } = TargetRid.Split('-')[1];
public static bool WarnOnSdkContentDiffs { get; } =
bool.TryParse(Environment.GetEnvironmentVariable(WarnSdkContentDiffsEnv), out bool warnOnSdkContentDiffs) && warnOnSdkContentDiffs;
+ public static bool WarnOnLicenseScanDiffs { get; } =
+ bool.TryParse(Environment.GetEnvironmentVariable(WarnLicenseScanDiffsEnv), out bool warnOnLicenseScanDiffs) && warnOnLicenseScanDiffs;
// Indicates whether the tests are being run in the context of a CI pipeline
public static bool RunningInCI { get; } =
bool.TryParse(Environment.GetEnvironmentVariable(RunningInCIEnv), out bool runningInCI) && runningInCI;
+
+ public static string? LicenseScanPath { get; } = Environment.GetEnvironmentVariable(LicenseScanPathEnv);
}
diff --git a/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/DebugTests.cs b/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/DebugTests.cs
index 939339207..c556b0e90 100644
--- a/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/DebugTests.cs
+++ b/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/DebugTests.cs
@@ -12,7 +12,7 @@ using Xunit.Abstractions;
namespace Microsoft.DotNet.SourceBuild.SmokeTests;
-public class DebugTests : SmokeTests
+public class DebugTests : SdkTests
{
private record ScanResult(string FileName, bool HasDebugInfo, bool HasDebugAbbrevs, bool HasFileSymbols, bool HasGnuDebugLink);
diff --git a/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/DotNetFormatTests.cs b/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/DotNetFormatTests.cs
index 86c6f2650..4ec0ad325 100644
--- a/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/DotNetFormatTests.cs
+++ b/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/DotNetFormatTests.cs
@@ -8,7 +8,7 @@ using Xunit.Abstractions;
namespace Microsoft.DotNet.SourceBuild.SmokeTests;
-public class DotNetFormatTests : SmokeTests
+public class DotNetFormatTests : SdkTests
{
private const string TestFileName = "FormatTest.cs";
private const string UnformattedFileName = "FormatTestUnformatted.cs";
diff --git a/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/DotNetHelper.cs b/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/DotNetHelper.cs
index 7c1a8673c..f35440d4c 100644
--- a/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/DotNetHelper.cs
+++ b/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/DotNetHelper.cs
@@ -3,14 +3,12 @@
// 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.Net;
using System.Net.Http;
using System.Net.Sockets;
-using System.Threading.Tasks;
using Xunit;
using Xunit.Abstractions;
@@ -21,7 +19,6 @@ internal class DotNetHelper
private static readonly object s_lockObj = new();
public static string DotNetPath { get; } = Path.Combine(Config.DotNetDirectory, "dotnet");
- public static string LogsDirectory { get; } = Path.Combine(Directory.GetCurrentDirectory(), "logs");
public static string PackagesDirectory { get; } = Path.Combine(Directory.GetCurrentDirectory(), "packages");
public static string ProjectsDirectory { get; } = Path.Combine(Directory.GetCurrentDirectory(), $"projects-{DateTime.Now:yyyyMMddHHmmssffff}");
@@ -56,11 +53,6 @@ internal class DotNetHelper
{
Directory.CreateDirectory(PackagesDirectory);
}
-
- if (!Directory.Exists(LogsDirectory))
- {
- Directory.CreateDirectory(LogsDirectory);
- }
}
}
@@ -261,7 +253,7 @@ internal class DotNetHelper
fileName += $"-{differentiator}";
}
- return $"/bl:{Path.Combine(LogsDirectory, $"{fileName}.binlog")}";
+ return $"/bl:{Path.Combine(TestBase.LogsDirectory, $"{fileName}.binlog")}";
}
private static bool DetermineIsMonoRuntime(string dotnetRoot)
diff --git a/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/DotNetWatchTests.cs b/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/DotNetWatchTests.cs
index a65f6d7e0..e30909a9b 100644
--- a/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/DotNetWatchTests.cs
+++ b/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/DotNetWatchTests.cs
@@ -9,7 +9,7 @@ using Xunit.Abstractions;
namespace Microsoft.DotNet.SourceBuild.SmokeTests;
-public class DotNetWatchTests : SmokeTests
+public class DotNetWatchTests : SdkTests
{
public DotNetWatchTests(ITestOutputHelper outputHelper) : base(outputHelper) { }
diff --git a/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/LicenseScanTests.cs b/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/LicenseScanTests.cs
new file mode 100644
index 000000000..6ddf668c5
--- /dev/null
+++ b/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/LicenseScanTests.cs
@@ -0,0 +1,291 @@
+// 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.Text.Json;
+using System.Text.Json.Serialization;
+using System.Text.RegularExpressions;
+using Xunit;
+using Xunit.Abstractions;
+
+namespace Microsoft.DotNet.SourceBuild.SmokeTests;
+
+///
+/// Scans the VMR for licenses and compares them to a baseline. This ensures that only open-source licenses are used for relevant files.
+///
+///
+/// Each sub-repo of the VMR is scanned separately because of the amount of time it takes.
+/// When scanning is run, the test provides a list of files for the scanner to ignore. These include binary file types. It also includes
+/// .il/.ildump file types which are massive, causing the scanner to choke and don't include license references anyway.
+/// Once the scanner returns the results, a filtering process occurs. First, any detected license that is in the allowed list of licenses
+/// is filtered out. The test defines a list of such licenses that all represent open-source licenses. Next, a license exclusions file is
+/// applied to the filtering. This file contains a set of paths for which certain detected licenses are to be ignored. Such a path can be
+/// defined to ignore all detected licenses or specific ones. These exclusions are useful for ignoring false positives where the scanning
+/// tool has detected something in the file that makes it think it's a license reference when that's not actually the intent. Other cases
+/// that are excluded are when the license is meant as configuration or test data and not actually applying to the code. These exclusions
+/// further filter down the set of the detected licenses for each file. Everything that's left at this point is reported. It gets compared
+/// to a baseline file (which is defined for each sub-repo). If the filtered results differ from what's defined in the baseline, the test fails.
+///
+/// Rules for determining how to resolve a detected license:
+/// 1. If it's an allowed open-source license, add it to the list of allowed licenses in LicenseScanTests.cs.
+/// 2. If the file shouldn't be scanned as a general rule because of its file type (e.g. image file), add it to the list of excluded file types in LicenseScanTests.cs.
+/// 3. Add it to LicenseExclusions.txt if the referenced license is one of the following:
+/// a. Not applicable (e.g. test data)
+/// b. False positive
+/// 4. If the license is not allowed for open-souce, the license needs to be fixed. Everything else should go in the baseline file.
+///
+public class LicenseScanTests : TestBase
+{
+ private const string BaselineSubDir = "licenses";
+
+ private static readonly string[] s_allowedLicenseExpressions = new string[]
+ {
+ "apache-1.1", // https://opensource.org/license/apache-1-1/
+ "apache-2.0", // https://opensource.org/license/apache-2-0/
+ "apache-2.0 WITH apple-runtime-library-exception", // https://github.com/nexB/scancode-toolkit/blob/develop/src/licensedcode/data/licenses/apple-runtime-library-exception.LICENSE
+ "apache-2.0 WITH llvm-exception", // https://foundation.llvm.org/relicensing/LICENSE.txt
+ "apsl-2.0", // https://opensource.org/license/apsl-2-0-php/
+ "boost-1.0", // https://opensource.org/license/bsl-1-0/
+ "bsd-new", // https://opensource.org/license/BSD-3-clause/
+ "bsd-original", // https://github.com/nexB/scancode-toolkit/blob/develop/src/licensedcode/data/licenses/bsd-original.LICENSE
+ "bsd-original-uc", // https://github.com/nexB/scancode-toolkit/blob/develop/src/licensedcode/data/licenses/bsd-original-uc.LICENSE
+ "bsd-simplified", // https://opensource.org/license/bsd-2-clause/
+ "bytemark", // https://github.com/nexB/scancode-toolkit/blob/develop/src/licensedcode/data/licenses/bytemark.LICENSE
+ "bzip2-libbzip-2010", // https://github.com/nexB/scancode-toolkit/blob/develop/src/licensedcode/data/licenses/bzip2-libbzip-2010.LICENSE
+ "cc0-1.0", // https://creativecommons.org/publicdomain/zero/1.0/legalcode
+ "cc-by-3.0", // https://creativecommons.org/licenses/by/3.0/legalcode
+ "cc-by-sa-3.0", // https://creativecommons.org/licenses/by-sa/3.0/legalcode
+ "cc-by-sa-4.0", // https://creativecommons.org/licenses/by-sa/4.0/legalcode
+ "cc-pd", // https://creativecommons.org/publicdomain/mark/1.0/
+ "epl-1.0", // https://opensource.org/license/epl-1-0/
+ "generic-cla", // https://github.com/nexB/scancode-toolkit/blob/develop/src/licensedcode/data/licenses/generic-cla.LICENSE
+ "gpl-1.0-plus", // https://opensource.org/license/gpl-1-0/
+ "gpl-2.0", // https://opensource.org/license/gpl-2-0/
+ "ietf", // https://github.com/nexB/scancode-toolkit/blob/develop/src/licensedcode/data/licenses/ietf.LICENSE
+ "gpl-2.0-plus WITH autoconf-simple-exception-2.0", // https://github.com/nexB/scancode-toolkit/blob/develop/src/licensedcode/data/rules/gpl-2.0-plus_with_autoconf-simple-exception-2.0_8.RULE
+ "gpl-2.0 WITH gcc-linking-exception-2.0", // https://github.com/nexB/scancode-toolkit/blob/develop/src/licensedcode/data/rules/gpl-2.0_with_gcc-linking-exception-2.0_6.RULE
+ "isc", // https://opensource.org/license/isc-license-txt/
+ "iso-8879", // https://github.com/nexB/scancode-toolkit/blob/develop/src/licensedcode/data/licenses/iso-8879.LICENSE
+ "lgpl-2.0-plus", // https://opensource.org/license/lgpl-2-0/
+ "lgpl-2.1", // https://opensource.org/license/lgpl-2-1/
+ "lgpl-2.1-plus", // https://opensource.org/license/lgpl-2-1/
+ "mit", // https://opensource.org/license/mit/
+ "mit-addition", // https://github.com/nexB/scancode-toolkit/blob/develop/src/licensedcode/data/licenses/mit-addition.LICENSE
+ "ms-patent-promise", // https://github.com/nexB/scancode-toolkit/blob/develop/src/licensedcode/data/licenses/ms-patent-promise.LICENSE
+ "ms-lpl", // https://github.com/nexB/scancode-toolkit/blob/develop/src/licensedcode/data/licenses/ms-lpl.LICENSE
+ "ms-pl", // https://opensource.org/license/ms-pl-html/
+ "ms-rl", // https://opensource.org/license/ms-rl-html/
+ "newton-king-cla", // https://github.com/nexB/scancode-toolkit/blob/develop/src/licensedcode/data/licenses/newton-king-cla.LICENSE
+ "ngpl", // https://opensource.org/license/nethack-php/
+ "object-form-exception-to-mit", // https://github.com/nexB/scancode-toolkit/blob/develop/src/licensedcode/data/licenses/object-form-exception-to-mit.LICENSE
+ "ofl-1.1", // https://opensource.org/license/ofl-1-1/
+ "osf-1990", // https://fedoraproject.org/wiki/Licensing:MIT?rd=Licensing/MIT#HP_Variant
+ "public-domain", // https://github.com/nexB/scancode-toolkit/blob/develop/src/licensedcode/data/licenses/public-domain.LICENSE
+ "public-domain-disclaimer", // https://github.com/nexB/scancode-toolkit/blob/develop/src/licensedcode/data/licenses/public-domain-disclaimer.LICENSE
+ "python", // https://opensource.org/license/python-2-0/
+ "rpl-1.5", // https://opensource.org/license/rpl-1-5/
+ "sax-pd", // https://github.com/nexB/scancode-toolkit/blob/develop/src/licensedcode/data/licenses/sax-pd.LICENSE
+ "unicode", // https://opensource.org/license/unicode-inc-license-agreement-data-files-and-software/
+ "unicode-mappings", // https://github.com/nexB/scancode-toolkit/blob/develop/src/licensedcode/data/licenses/unicode-mappings.LICENSE
+ "uoi-ncsa", // https://opensource.org/license/uoi-ncsa-php/
+ "w3c-software-19980720", // https://opensource.org/license/w3c/
+ "w3c-software-doc-20150513", // https://opensource.org/license/w3c/
+ "warranty-disclaimer", // https://github.com/nexB/scancode-toolkit/blob/develop/src/licensedcode/data/licenses/warranty-disclaimer.LICENSE
+ "x11", // https://github.com/nexB/scancode-toolkit/blob/develop/src/licensedcode/data/licenses/x11.LICENSE
+ "zlib" // https://opensource.org/license/zlib/
+ };
+
+ private static readonly string[] s_ignoredFilePatterns = new string[]
+ {
+ "*.bin",
+ "*.bmp",
+ "*.bson",
+ "*.db",
+ "*.dic",
+ "*.eot",
+ "*.gif",
+ "*.ico",
+ "*.jpg",
+ "*.il",
+ "*.ildump",
+ "*.lss",
+ "*.nlp",
+ "*.otf",
+ "*.pdf",
+ "*.pfx",
+ "*.png",
+ "*.snk",
+ "*.ttf",
+ "*.vsd",
+ "*.vsdx",
+ "*.winmd",
+ "*.woff",
+ "*.woff2",
+ "*.xlsx",
+ };
+
+ private readonly string _targetRepo;
+
+ public LicenseScanTests(ITestOutputHelper outputHelper) : base(outputHelper)
+ {
+ Assert.NotNull(Config.LicenseScanPath);
+ _targetRepo = new DirectoryInfo(Config.LicenseScanPath).Name;
+ }
+
+ [SkippableFact(Config.LicenseScanPathEnv, skipOnNullOrWhiteSpaceEnv: true)]
+ public void ScanForLicenses()
+ {
+ Assert.NotNull(Config.LicenseScanPath);
+
+ string OriginalScancodeResultsPath = Path.Combine(LogsDirectory, "scancode-results-original.json");
+ string FilteredScancodeResultsPath = Path.Combine(LogsDirectory, "scancode-results-filtered.json");
+
+ // Scancode Doc: https://scancode-toolkit.readthedocs.io/en/latest/index.html
+ string ignoreOptions = string.Join(" ", s_ignoredFilePatterns.Select(pattern => $"--ignore {pattern}"));
+ ExecuteHelper.ExecuteProcessValidateExitCode(
+ "scancode",
+ $"--license --strip-root --only-findings {ignoreOptions} --json-pp {OriginalScancodeResultsPath} {Config.LicenseScanPath}",
+ OutputHelper);
+
+ JsonDocument doc = JsonDocument.Parse(File.ReadAllText(OriginalScancodeResultsPath));
+ ScancodeResults? scancodeResults = doc.Deserialize();
+ Assert.NotNull(scancodeResults);
+
+ FilterFiles(scancodeResults);
+
+ JsonSerializerOptions options = new()
+ {
+ WriteIndented = true
+ };
+ string json = JsonSerializer.Serialize(scancodeResults, options);
+ File.WriteAllText(FilteredScancodeResultsPath, json);
+
+ string baselineName = $"Licenses.{_targetRepo}.json";
+
+ string baselinePath = BaselineHelper.GetBaselineFilePath(baselineName, BaselineSubDir);
+ if (!File.Exists(baselinePath))
+ {
+ Assert.Fail($"No license baseline file exists for repo '{_targetRepo}'. Expected file: {baselinePath}");
+ }
+
+ BaselineHelper.CompareBaselineContents(baselineName, json, OutputHelper, Config.WarnOnLicenseScanDiffs, BaselineSubDir);
+ }
+
+ private LicenseExclusion ParseLicenseExclusion(string rawExclusion)
+ {
+ string[] parts = rawExclusion.Split('|', StringSplitOptions.RemoveEmptyEntries);
+
+ Match repoNameMatch = Regex.Match(parts[0], @"(?<=src/)[^/]+");
+
+ Assert.True(repoNameMatch.Success);
+
+ // The path in the exclusion file is rooted from the VMR. But the path in the scancode results is rooted from the
+ // target repo within the VMR. So we need to strip off the beginning part of the path.
+ Match restOfPathMatch = Regex.Match(parts[0], @"(?<=src/[^/]+/).*");
+ string path = restOfPathMatch.Value;
+
+ if (parts.Length == 0 || parts.Length > 2)
+ {
+ throw new Exception($"Invalid license exclusion: '{rawExclusion}'");
+ }
+
+ if (parts.Length > 1)
+ {
+ string[] licenseExpressions = parts[1].Split(',', StringSplitOptions.RemoveEmptyEntries);
+ return new LicenseExclusion(repoNameMatch.Value, path, licenseExpressions);
+ }
+ else
+ {
+ return new LicenseExclusion(repoNameMatch.Value, path, Enumerable.Empty());
+ }
+ }
+
+ private void FilterFiles(ScancodeResults scancodeResults)
+ {
+ IEnumerable rawExclusions = Utilities.ParseExclusionsFile("LicenseExclusions.txt");
+ IEnumerable exclusions = rawExclusions
+ .Select(exclusion => ParseLicenseExclusion(exclusion))
+ .Where(exclusion => exclusion.Repo == _targetRepo)
+ .ToList();
+
+ // This will filter out files that we don't want to include in the baseline.
+ // Filtering can happen in two ways:
+ // 1. There are a set of allowed license expressions that apply to all files. If a file has a match on one of those licenses,
+ // that license will not be considered.
+ // 2. The LicenseExclusions.txt file contains a list of files and the licenses that should be excluded from those files.
+ // Once the license expression filtering has been applied, if a file has any licenses left, it will be included in the baseline.
+ // In that case, the baseline will list all of the licenses for that file, even if some were originally excluded during this processing.
+ // In other words, the baseline will be fully representative of the licenses that apply to the files that are listed there.
+
+ for (int i = scancodeResults.Files.Count - 1; i >= 0; i--)
+ {
+ ScancodeFileResult file = scancodeResults.Files[i];
+
+ // A license expression can be a logical expression, e.g. "(MIT OR Apache-2.0)"
+ // For our purposes, we just care about the license involved, not the semantics of the expression.
+ // Parse out all the expression syntax to just get the license names.
+ string[] licenses = file.LicenseExpression?
+ .Replace("(", string.Empty)
+ .Replace(")", string.Empty)
+ .Replace(" AND ", ",")
+ .Replace(" OR ", ",")
+ .Split(",", StringSplitOptions.RemoveEmptyEntries)
+ .Select(license => license.Trim())
+ .ToArray()
+ ?? Array.Empty();
+
+ // First check whether the file's licenses can all be matched with allowed expressions
+ IEnumerable disallowedLicenses = licenses
+ .Where(license => !s_allowedLicenseExpressions.Contains(license, StringComparer.OrdinalIgnoreCase));
+
+ if (!disallowedLicenses.Any())
+ {
+ scancodeResults.Files.Remove(file);
+ }
+ else
+ {
+ // There are some licenses that are not allowed. Now check whether the file is excluded.
+
+ IEnumerable matchingExclusions =
+ Utilities.GetMatchingFileExclusions(file.Path, exclusions, exclusion => exclusion.Path);
+
+ IEnumerable excludedLicenses = matchingExclusions.SelectMany(exclusion => exclusion.LicenseExpressions);
+ // If no licenses are explicitly specified, it means they're all excluded.
+ if (matchingExclusions.Any() && !excludedLicenses.Any())
+ {
+ scancodeResults.Files.Remove(file);
+ }
+ else
+ {
+ IEnumerable remainingLicenses = disallowedLicenses.Except(excludedLicenses);
+
+ if (!remainingLicenses.Any())
+ {
+ scancodeResults.Files.Remove(file);
+ }
+ }
+ }
+ }
+ }
+
+ private record LicenseExclusion(string Repo, string Path, IEnumerable LicenseExpressions);
+
+ private class ScancodeResults
+ {
+ [JsonPropertyName("files")]
+ public List Files { get; set; } = new();
+ }
+
+ private class ScancodeFileResult
+ {
+ [JsonPropertyName("path")]
+ public string Path { get; set; } = string.Empty;
+
+ [JsonPropertyName("detected_license_expression")]
+ public string? LicenseExpression { get; set; }
+ }
+}
diff --git a/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/OmniSharpTests.cs b/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/OmniSharpTests.cs
index 48cdb1d50..416a1e1ca 100644
--- a/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/OmniSharpTests.cs
+++ b/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/OmniSharpTests.cs
@@ -16,10 +16,10 @@ namespace Microsoft.DotNet.SourceBuild.SmokeTests;
///
/// OmniSharp tests to ensure it works with a source-built sdk.
///
-public class OmniSharpTests : SmokeTests
+public class OmniSharpTests : SdkTests
{
// Update version as new releases become available: https://github.com/OmniSharp/omnisharp-roslyn/releases
- private const string OmniSharpReleaseVersion = "1.39.8";
+ private const string OmniSharpReleaseVersion = "1.39.10";
private string OmniSharpDirectory { get; } = Path.Combine(Directory.GetCurrentDirectory(), nameof(OmniSharpTests));
diff --git a/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/PoisonTests.cs b/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/PoisonTests.cs
index 6eb8203f5..cf2e1182b 100644
--- a/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/PoisonTests.cs
+++ b/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/PoisonTests.cs
@@ -9,7 +9,7 @@ using Xunit.Abstractions;
namespace Microsoft.DotNet.SourceBuild.SmokeTests
{
- public class PoisonTests : SmokeTests
+ public class PoisonTests : SdkTests
{
public PoisonTests(ITestOutputHelper outputHelper) : base(outputHelper) { }
diff --git a/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/SdkContentTests.cs b/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/SdkContentTests.cs
index d3b0c8a26..29bb7dbc5 100644
--- a/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/SdkContentTests.cs
+++ b/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/SdkContentTests.cs
@@ -17,7 +17,7 @@ using Xunit.Abstractions;
namespace Microsoft.DotNet.SourceBuild.SmokeTests;
[Trait("Category", "SdkContent")]
-public class SdkContentTests : SmokeTests
+public class SdkContentTests : SdkTests
{
private const string MsftSdkType = "msft";
private const string SourceBuildSdkType = "sb";
@@ -98,7 +98,7 @@ public class SdkContentTests : SmokeTests
if (sbVersion is not null &&
msftVersion is not null &&
sbVersion >= msftVersion &&
- IsFileExcluded(assemblyPath, assemblyVersionDiffFilters))
+ Utilities.IsFileExcluded(assemblyPath, assemblyVersionDiffFilters))
{
sbSdkAssemblyVersions.Remove(assemblyPath);
msftSdkAssemblyVersions.Remove(assemblyPath);
@@ -182,7 +182,7 @@ public class SdkContentTests : SmokeTests
string relativePath = Path.GetRelativePath(sbSdkPath, file);
string normalizedPath = BaselineHelper.RemoveVersions(relativePath);
- if (!IsFileExcluded(normalizedPath, exclusionFilters))
+ if (!Utilities.IsFileExcluded(normalizedPath, exclusionFilters))
{
sbSdkAssemblyVersions.Add(normalizedPath, GetVersion(assemblyName));
}
@@ -208,32 +208,13 @@ public class SdkContentTests : SmokeTests
}
private static IEnumerable RemoveExclusions(IEnumerable files, IEnumerable exclusions) =>
- files.Where(item => !IsFileExcluded(item, exclusions));
-
- private static bool IsFileExcluded(string filePath, IEnumerable exclusions) =>
- exclusions.Any(p => FileSystemName.MatchesSimpleExpression(p, filePath));
+ files.Where(item => !Utilities.IsFileExcluded(item, exclusions));
private static IEnumerable GetSdkDiffExclusionFilters(string sdkType) =>
- ParseExclusionsFile("SdkFileDiffExclusions.txt", sdkType);
+ Utilities.ParseExclusionsFile("SdkFileDiffExclusions.txt", sdkType);
private static IEnumerable GetSdkAssemblyVersionDiffExclusionFilters() =>
- ParseExclusionsFile("SdkAssemblyVersionDiffExclusions.txt");
-
- private static IEnumerable ParseExclusionsFile(string exclusionsFileName, string? prefix = null)
- {
- string exclusionsFilePath = Path.Combine(BaselineHelper.GetAssetsDirectory(), exclusionsFileName);
- int prefixSkip = prefix?.Length + 1 ?? 0;
- return File.ReadAllLines(exclusionsFilePath)
- // process only specific sdk exclusions if a prefix is provided
- .Where(line => prefix is null || line.StartsWith(prefix + ","))
- .Select(line =>
- {
- // Ignore comments
- var index = line.IndexOf('#');
- return index >= 0 ? line[prefixSkip..index].TrimEnd() : line[prefixSkip..];
- })
- .ToList();
- }
+ Utilities.ParseExclusionsFile("SdkAssemblyVersionDiffExclusions.txt");
private static string RemoveDiffMarkers(string source)
{
diff --git a/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/SmokeTests.cs b/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/SdkTests.cs
similarity index 66%
rename from src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/SmokeTests.cs
rename to src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/SdkTests.cs
index 3c197262b..ba3381360 100644
--- a/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/SmokeTests.cs
+++ b/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/SdkTests.cs
@@ -7,16 +7,14 @@ using Xunit.Abstractions;
namespace Microsoft.DotNet.SourceBuild.SmokeTests;
///
-/// Shared base class for all smoke tests.
+/// Shared base class for all SDK-based smoke tests.
///
-public abstract class SmokeTests
+public abstract class SdkTests : TestBase
{
internal DotNetHelper DotNetHelper { get; }
- internal ITestOutputHelper OutputHelper { get; }
- protected SmokeTests(ITestOutputHelper outputHelper)
+ protected SdkTests(ITestOutputHelper outputHelper) : base(outputHelper)
{
DotNetHelper = new DotNetHelper(outputHelper);
- OutputHelper = outputHelper;
}
}
diff --git a/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/SourceBuiltArtifactsTests.cs b/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/SourceBuiltArtifactsTests.cs
index 9fbbbc781..8ab1a9600 100644
--- a/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/SourceBuiltArtifactsTests.cs
+++ b/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/SourceBuiltArtifactsTests.cs
@@ -13,13 +13,14 @@ using Xunit.Abstractions;
namespace Microsoft.DotNet.SourceBuild.SmokeTests;
-public class SourceBuiltArtifactsTests : SmokeTests
+public class SourceBuiltArtifactsTests : SdkTests
{
public SourceBuiltArtifactsTests(ITestOutputHelper outputHelper) : base(outputHelper) { }
- [Fact]
+ [SkippableFact(Config.SourceBuiltArtifactsPathEnv, skipOnNullOrWhiteSpaceEnv: true)]
public void VerifyVersionFile()
{
+ Assert.NotNull(Config.SourceBuiltArtifactsPath);
string outputDir = Path.Combine(Directory.GetCurrentDirectory(), "sourcebuilt-artifacts");
Directory.CreateDirectory(outputDir);
try
diff --git a/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/SourcelinkTests.cs b/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/SourcelinkTests.cs
index f3a39242b..0fc8735d9 100644
--- a/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/SourcelinkTests.cs
+++ b/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/SourcelinkTests.cs
@@ -14,7 +14,16 @@ using Xunit.Abstractions;
namespace Microsoft.DotNet.SourceBuild.SmokeTests;
-public class SourcelinkTests : SmokeTests
+///
+/// Separate test collection for Sourcelink tests. This is needed due to intra-test parallelization,
+/// which can cause less CPU time to be allocated to other tests.
+/// This would make other tests run too long and fail due to timeouts.
+///
+[CollectionDefinition(nameof(SourcelinkTestCollection), DisableParallelization = true)]
+public class SourcelinkTestCollection { }
+
+[Collection(nameof(SourcelinkTestCollection))]
+public class SourcelinkTests : SdkTests
{
private static string SourcelinkRoot { get; } = Path.Combine(Directory.GetCurrentDirectory(), nameof(SourcelinkTests));
@@ -23,27 +32,43 @@ public class SourcelinkTests : SmokeTests
///
/// Verifies that all symbols have valid sourcelinks.
///
- [Fact]
+ [SkippableFact(Config.SourceBuiltArtifactsPathEnv, skipOnNullOrWhiteSpaceEnv: true)]
public void VerifySourcelinks()
{
- if (Directory.Exists(SourcelinkRoot))
+ try
+ {
+ if (Directory.Exists(SourcelinkRoot))
+ {
+ Directory.Delete(SourcelinkRoot, true);
+ }
+ Directory.CreateDirectory(SourcelinkRoot);
+
+ string symbolsRoot = Directory.CreateDirectory(Path.Combine(SourcelinkRoot, "symbols")).FullName;
+
+ // We are validating dotnet-symbols-all-*.tar.gz which contains all source-built symbols, including
+ // SDK-specific symbols that are also packaged in dotnet-symbols-sdk-*.tar.gz.
+ Utilities.ExtractTarball(
+ Utilities.GetFile(Path.GetDirectoryName(Config.SourceBuiltArtifactsPath), "dotnet-symbols-all-*.tar.gz"),
+ symbolsRoot,
+ OutputHelper);
+
+ IList failedFiles = ValidateSymbols(symbolsRoot, InitializeSourcelinkTool());
+
+ if (failedFiles.Count > 0)
+ {
+ OutputHelper.WriteLine($"Sourcelink verification failed for the following files:");
+ foreach (string file in failedFiles)
+ {
+ OutputHelper.WriteLine(file);
+ }
+ }
+
+ Assert.True(failedFiles.Count == 0);
+ }
+ finally
{
Directory.Delete(SourcelinkRoot, true);
}
- Directory.CreateDirectory(SourcelinkRoot);
-
- IList failedFiles = ValidateSymbols(ExtractSymbolsPackages(GetAllSymbolsPackages()), InitializeSourcelinkTool());
-
- if (failedFiles.Count > 0)
- {
- OutputHelper.WriteLine($"Sourcelink verification failed for the following files:");
- foreach (string file in failedFiles)
- {
- OutputHelper.WriteLine(file);
- }
- }
-
- Assert.True(failedFiles.Count == 0);
}
///
@@ -53,6 +78,8 @@ public class SourcelinkTests : SmokeTests
/// Path to sourcelink tool binary.
private string InitializeSourcelinkTool()
{
+ Assert.NotNull(Config.SourceBuiltArtifactsPath);
+
const string SourcelinkToolPackageNamePattern = "dotnet-sourcelink*nupkg";
const string SourcelinkToolBinaryFilename = "dotnet-sourcelink.dll";
@@ -65,38 +92,6 @@ public class SourcelinkTests : SmokeTests
return Utilities.GetFile(extractedToolPath, SourcelinkToolBinaryFilename);
}
- private IEnumerable GetAllSymbolsPackages()
- {
- /*
- At the moment we validate sourcelinks from runtime symbols package.
- The plan is to make symbols, from all repos, available in source-build artifacts.
- Once that's available, this code will be modified to validate all available symbols.
- Tracking issue: https://github.com/dotnet/source-build/issues/3612
- */
-
- // Runtime symbols package lives in the same directory as PSB artifacts.
- // i.e. /artifacts/x64/Release/runtime/dotnet-runtime-symbols-fedora.36-x64-8.0.0-preview.7.23355.7.tar.gz
- yield return Utilities.GetFile(Path.GetDirectoryName(Config.SourceBuiltArtifactsPath), "dotnet-runtime-symbols-*.tar.gz");
- }
-
- ///
- /// Extracts symbols packages to subdirectories of the common symbols root directory.
- ///
- /// Path to common symbols root directory.
- private string ExtractSymbolsPackages(IEnumerable packages)
- {
- string symbolsRoot = Directory.CreateDirectory(Path.Combine(SourcelinkRoot, "symbols")).FullName;
-
- foreach (string package in packages)
- {
- Assert.True(package.EndsWith(".tar.gz"), $"Package extension is not supported: {package}");
- DirectoryInfo targetDirInfo = Directory.CreateDirectory(Path.Combine(symbolsRoot, Path.GetFileNameWithoutExtension(package)));
- Utilities.ExtractTarball(package, targetDirInfo.FullName, OutputHelper);
- }
-
- return symbolsRoot;
- }
-
private IList ValidateSymbols(string path, string sourcelinkToolPath)
{
Assert.True(Directory.Exists(path), $"Path, with symbol files to validate, does not exist: {path}");
@@ -112,7 +107,7 @@ public class SourcelinkTests : SmokeTests
OutputHelper,
logOutput: false,
excludeInfo: true, // Exclude info messages, as there can be 1,000+ processes
- millisecondTimeout: 5000,
+ millisecondTimeout: 60000,
configureCallback: (process) => DotNetHelper.ConfigureProcess(process, null));
if (executeResult.Process.ExitCode != 0)
diff --git a/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/TestBase.cs b/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/TestBase.cs
new file mode 100644
index 000000000..963f07109
--- /dev/null
+++ b/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/TestBase.cs
@@ -0,0 +1,25 @@
+// 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.IO;
+using Xunit.Abstractions;
+
+namespace Microsoft.DotNet.SourceBuild.SmokeTests;
+
+public abstract class TestBase
+{
+ public static string LogsDirectory { get; } = Path.Combine(Directory.GetCurrentDirectory(), "logs");
+
+ public ITestOutputHelper OutputHelper { get; }
+
+ public TestBase(ITestOutputHelper outputHelper)
+ {
+ OutputHelper = outputHelper;
+
+ if (!Directory.Exists(LogsDirectory))
+ {
+ Directory.CreateDirectory(LogsDirectory);
+ }
+ }
+}
diff --git a/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/Utilities.cs b/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/Utilities.cs
index a701e730f..53efa2655 100644
--- a/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/Utilities.cs
+++ b/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/Utilities.cs
@@ -8,6 +8,8 @@ using System.Collections.Generic;
using System.Formats.Tar;
using System.IO;
using System.IO.Compression;
+using System.IO.Enumeration;
+using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Xunit;
@@ -17,6 +19,37 @@ namespace Microsoft.DotNet.SourceBuild.SmokeTests;
public static class Utilities
{
+ ///
+ /// Returns whether the given file path is excluded by the given exclusions using glob file matching.
+ ///
+ public static bool IsFileExcluded(string filePath, IEnumerable exclusions) =>
+ GetMatchingFileExclusions(filePath, exclusions, exclusion => exclusion).Any();
+
+ public static IEnumerable GetMatchingFileExclusions(string filePath, IEnumerable exclusions, Func getExclusionExpression) =>
+ exclusions.Where(exclusion => FileSystemName.MatchesSimpleExpression(getExclusionExpression(exclusion), filePath));
+
+ ///
+ /// Parses a common file format in the test suite for listing file exclusions.
+ ///
+ /// Name of the exclusions file.
+ /// When specified, filters the exclusions to those that begin with the prefix value.
+ public static IEnumerable ParseExclusionsFile(string exclusionsFileName, string? prefix = null)
+ {
+ string exclusionsFilePath = Path.Combine(BaselineHelper.GetAssetsDirectory(), exclusionsFileName);
+ int prefixSkip = prefix?.Length + 1 ?? 0;
+ return File.ReadAllLines(exclusionsFilePath)
+ // process only specific exclusions if a prefix is provided
+ .Where(line => prefix is null || line.StartsWith(prefix + ","))
+ .Select(line =>
+ {
+ // Ignore comments
+ var index = line.IndexOf('#');
+ return index >= 0 ? line[prefixSkip..index].TrimEnd() : line[prefixSkip..];
+ })
+ .Where(line => !string.IsNullOrEmpty(line))
+ .ToList();
+ }
+
public static void ExtractTarball(string tarballPath, string outputDir, ITestOutputHelper outputHelper)
{
// TarFile doesn't properly handle hard links (https://github.com/dotnet/runtime/pull/85378#discussion_r1221817490),
diff --git a/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/WebScenarioTests.cs b/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/WebScenarioTests.cs
index 654fe9552..3d6e9b23c 100644
--- a/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/WebScenarioTests.cs
+++ b/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/WebScenarioTests.cs
@@ -16,7 +16,7 @@ namespace Microsoft.DotNet.SourceBuild.SmokeTests;
/// for related basic scenarios.
/// They are encapsulated in a separate testclass so that they can be run in parallel.
///
-public class WebScenarioTests : SmokeTests
+public class WebScenarioTests : SdkTests
{
public WebScenarioTests(ITestOutputHelper outputHelper) : base(outputHelper) { }
diff --git a/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/assets/LicenseExclusions.txt b/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/assets/LicenseExclusions.txt
new file mode 100644
index 000000000..e0efc77dd
--- /dev/null
+++ b/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/assets/LicenseExclusions.txt
@@ -0,0 +1,257 @@
+# Contains the list of files to be excluded from license scanning.
+#
+# This list is processed using FileSystemName.MatchesSimpleExpression
+#
+# Format:
+# Exclude the file entirely from license scanning:
+#
+# Exclude a specific detected license expression from the scan results for the file:
+# |[,...]
+
+#
+# arcade
+#
+
+# False positive
+src/arcade/Documentation/UnifiedBuild/Foundational-Concepts.md
+src/arcade/src/Microsoft.DotNet.Build.Tasks.Installers/src/BuildFPMToolPreReqs.cs|json
+src/arcade/src/Microsoft.DotNet.Build.Tasks.Installers/build/rpm_templates/copyright|cecill-c
+src/arcade/src/SignCheck/SignCheck/THIRD-PARTY-NOTICES.TXT
+
+# Doesn't apply to code
+src/arcade/src/Microsoft.DotNet.Arcade.Sdk/tools/Licenses/*
+
+# Applies to installer, not source
+src/arcade/src/Microsoft.DotNet.Build.Tasks.Installers/build/wix/eula.rtf
+
+#
+# aspnetcore
+#
+
+# Line 1 is a generic statement about license applicability that is being detected as "unknown"
+src/aspnetcore/src/Components/THIRD-PARTY-NOTICES.txt|unknown
+
+# Windows installer files that have a reference to a URL for license
+src/aspnetcore/src/Installers/Windows/**/*.wxl|unknown-license-reference
+src/aspnetcore/src/Installers/Windows/**/*.wxs|unknown-license-reference
+
+# License reference used in configuration, but not applying to code
+src/aspnetcore/src/Mvc/Settings.StyleCop|unknown-license-reference
+src/aspnetcore/src/submodules/MessagePack-CSharp/stylecop.json|unknown
+
+#
+# command-line-api
+#
+
+# False positive
+src/command-line-api/System.CommandLine.sln|json
+
+#
+# deployment-tools
+#
+
+# False positive
+src/deployment-tools/THIRD-PARTY-NOTICES.TXT|unknown-license-reference
+
+#
+# diagnostics
+#
+
+# False positive
+src/diagnostics/THIRD-PARTY-NOTICES.TXT|codesourcery-2004
+
+#
+# format
+#
+
+# False positive
+src/format/THIRD-PARTY-NOTICES.TXT|unknown-license-reference
+
+#
+# fsharp
+#
+
+# False positive
+src/fsharp/tests/EndToEndBuildTests/ProvidedTypes/ProvidedTypes.fs|unknown-license-reference
+src/fsharp/tests/service/data/TestTP/ProvidedTypes.fs|unknown-license-reference
+src/fsharp/vsintegration/tests/MockTypeProviders/DummyProviderForLanguageServiceTesting/ProvidedTypes.fs|unknown-license-reference
+
+# Applies to installer, not source
+src/fsharp/setup/resources/eula/*.rtf
+
+#
+# installer
+#
+
+# False positive
+src/installer/src/core-sdk-tasks/BuildFPMToolPreReqs.cs|json
+src/installer/src/redist/targets/packaging/osx/clisdk/resources/en.lproj/welcome.html|cecill-c
+src/installer/THIRD-PARTY-NOTICES|proprietary-license
+
+# Configuration, doesn't apply to source directly
+src/installer/src/VirtualMonoRepo/THIRD-PARTY-NOTICES.template.txt
+src/installer/src/redist/targets/packaging/**/*.json
+
+#
+# msbuild
+#
+
+# License reference used in build configuration, but not applying to code
+src/msbuild/src/Directory.Build.props|ms-net-library-2018-11
+
+# False positive
+src/msbuild/src/Build/Instance/ProjectItemInstance.cs|generic-exception
+
+#
+# nuget-client
+#
+
+# False positive
+src/nuget-client/build/NOTICES.txt|other-copyleft
+src/nuget-client/README.md|unknown-license-reference
+src/nuget-client/src/NuGet.Clients/NuGet.PackageManagement.UI/Resources.Designer.cs|unknown-license-reference
+src/nuget-client/src/NuGet.Clients/NuGet.PackageManagement.UI/Actions/UIActionEngine.cs|unknown-license-reference
+src/nuget-client/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/Services/NuGetPackageFileService.cs|proprietary-license
+src/nuget-client/src/NuGet.Clients/NuGet.VisualStudio.Internal.Contracts/Formatters/LicenseMetadataFormatter.cs|proprietary-license
+src/nuget-client/src/NuGet.Core/NuGet.Packaging/PackageCreation/Authoring/LicenseMetadata.cs|unknown-license-reference
+src/nuget-client/src/NuGet.Core/NuGet.Packaging/Rules/DefaultManifestValuesRule.cs|unknown-license-reference
+src/nuget-client/test/TestExtensions/GenerateLicenseList/Program.cs|json
+
+# Test data
+src/nuget-client/test/**/resources/*.json
+src/nuget-client/test/**/resources/*.xml
+src/nuget-client/test/NuGet.Clients.Tests/NuGet.PackageManagement.UI.Test/PackageLicenseUtilitiesTests.cs
+src/nuget-client/test/NuGet.Core.Tests/NuGet.Packaging.Test/DefaultManifestValuesRuleTests.cs
+src/nuget-client/test/NuGet.Core.Tests/NuGet.Packaging.Test/LicensesTests/LicenseExpressionTokenizerTests.cs
+src/nuget-client/test/NuGet.Core.Tests/NuGet.Packaging.Test/LicensesTests/NuGetLicenseExpressionParserTests.cs
+src/nuget-client/test/NuGet.Core.Tests/NuGet.Packaging.Test/LicensesTests/NuGetLicenseTests.cs
+src/nuget-client/test/TestUtilities/Test.Utility/JsonData.cs
+
+#
+# roslyn-analyzers
+#
+
+# Build asset, not applicable to source
+src/roslyn-analyzers/assets/EULA.txt|ms-net-library
+
+#
+# roslyn
+#
+
+# Test data
+src/roslyn/src/Analyzers/VisualBasic/Tests/FileHeaders/FileHeaderTests.vb|unknown-license-reference
+src/roslyn/src/EditorFeatures/CSharpTest2/EmbeddedLanguages/RegularExpressions/Regex_RealWorldPatterns.json
+
+# False positive
+src/roslyn/src/Compilers/**/Portable/Symbols/NonMissingModuleSymbol.*|proprietary-license
+src/roslyn/src/NuGet/ThirdPartyNotices.rtf|json
+
+# Applicable to installer, not source
+src/roslyn/src/Setup/Roslyn.ThirdPartyNotices/ThirdPartyNotices.rtf
+src/roslyn/src/Setup/Roslyn.VsixLicense/EULA.rtf
+
+#
+# runtime
+#
+
+# Doc describing licenses, not applicable to source
+src/runtime/docs/project/copyright.md
+src/runtime/docs/project/glossary.md
+
+# Doc that references a license, not applicable to source
+src/runtime/src/coreclr/nativeaot/docs/compiling.md|openssl-ssleay
+
+# Installer asset, not applicable to source
+src/runtime/src/installer/pkg/LICENSE-MSFT.TXT
+src/runtime/src/installer/pkg/THIRD-PARTY-NOTICES.TXT
+
+# False positive
+src/runtime/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/LicFileLicenseProvider.cs|proprietary-license
+src/runtime/src/libraries/System.Configuration.ConfigurationManager/tests/Mono/LongValidatorTest.cs|json
+src/runtime/src/libraries/System.Net.Sockets/tests/FunctionalTests/Connect.cs|other-permissive
+src/runtime/src/libraries/System.Net.Sockets/tests/FunctionalTests/UdpClientTest.cs|other-permissive
+src/runtime/src/libraries/System.Net.Sockets/tests/FunctionalTests/SendReceive/SendReceive.cs|other-permissive
+src/runtime/src/libraries/System.Reflection.Metadata/tests/Resources/README.md|unknown-license-reference
+src/runtime/src/libraries/System.Runtime/tests/TestModule/README.md|unknown-license-reference
+src/runtime/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/XmlLicenseTransform.cs|proprietary-license
+src/runtime/src/libraries/System.ServiceModel.Syndication/tests/BasicScenarioTests.cs|unknown-license-reference
+src/runtime/src/mono/mono/mini/mini-posix.c|unknown-license-reference
+src/runtime/src/mono/mono/mini/mini-windows.c|unknown-license-reference
+src/runtime/src/native/external/libunwind/doc/libunwind-ia64.*|generic-exception
+src/runtime/src/tests/JIT/Performance/CodeQuality/V8/Crypto/Crypto.cs|unknown-license-reference
+
+# Test data
+src/runtime/src/libraries/System.Private.Xml.Linq/tests/XDocument.Common/InputSpace.cs|other-permissive
+src/runtime/src/libraries/System.Private.Xml.Linq/tests/XDocument.Common/THIRD-PARTY-NOTICE|other-permissive
+src/runtime/src/libraries/System.ServiceModel.Syndication/tests/TestFeeds/AtomFeeds/*.xml
+src/runtime/src/libraries/System.Text.RegularExpressions/tests/FunctionalTests/AttRegexTests.cs|other-permissive
+
+# Reference to a license, not applicable to source
+src/runtime/src/libraries/System.Text.Json/roadmap/images/core-components.txt|unknown-license-reference
+src/runtime/src/libraries/System.Text.Json/roadmap/images/higher-level-components.txt
+
+# Sample data
+src/runtime/src/mono/sample/wasm/browser-webpack/package-lock.json
+
+#
+# source-build-externals
+#
+
+# False positive
+src/source-build-externals/src/abstractions-xunit/README.md|free-unknown
+src/source-build-externals/src/application-insights/NETCORE/ThirdPartyNotices.txt|unknown
+src/source-build-externals/src/azure-activedirectory-identitymodel-extensions-for-dotnet/benchmark/Microsoft.IdentityModel.Benchmarks/CreateTokenTests.cs|proprietary-license
+src/source-build-externals/src/azure-activedirectory-identitymodel-extensions-for-dotnet/src/Microsoft.IdentityModel.JsonWebTokens/JsonClaimValueTypes.cs|proprietary-license
+src/source-build-externals/src/azure-activedirectory-identitymodel-extensions-for-dotnet/src/Microsoft.IdentityModel.Tokens.Saml/Saml/ClaimProperties.cs|proprietary-license
+src/source-build-externals/src/azure-activedirectory-identitymodel-extensions-for-dotnet/src/Microsoft.IdentityModel.Tokens.Saml/Saml/SamlTokenUtilities.cs|proprietary-license
+src/source-build-externals/src/azure-activedirectory-identitymodel-extensions-for-dotnet/src/Microsoft.IdentityModel.Tokens.Saml/Saml2/ClaimProperties.cs|proprietary-license
+src/source-build-externals/src/azure-activedirectory-identitymodel-extensions-for-dotnet/src/System.IdentityModel.Tokens.Jwt/JsonClaimValueTypes.cs|proprietary-license
+src/source-build-externals/src/humanizer/readme.md|free-unknown
+src/source-build-externals/src/humanizer/NuSpecs/*.nuspec*
+src/source-build-externals/src/xunit/README.md|free-unknown
+src/source-build-externals/src/xunit/src/xunit.assert/Asserts/README.md|free-unknown
+src/source-build-externals/src/xunit/xunit.sln|json
+
+# A patch which removes the license usage but contains references to the removed license as part of the patch reference lines
+src/source-build-externals/patches/application-insights/0002-Remove-WebGrease-from-TPN-2816.patch
+
+#
+# source-build-reference-packages
+#
+
+# False positive
+src/source-build-reference-packages/src/targetPacks/ILsrc/microsoft.netcore.app.ref/3.*/THIRD-PARTY-NOTICES.TXT|codesourcery-2004
+src/source-build-reference-packages/src/targetPacks/ILsrc/netstandard.library/1.6.1/ThirdPartyNotices.txt|unknown
+src/source-build-reference-packages/src/targetPacks/ILsrc/netstandard.library/2.0.*/THIRD-PARTY-NOTICES.TXT|unknown
+src/source-build-reference-packages/src/targetPacks/ILsrc/netstandard.library.ref/2.1.0/THIRD-PARTY-NOTICES.TXT|codesourcery-2004
+src/source-build-reference-packages/src/textOnlyPackages/src/microsoft.codeanalysis.collections/4.2.0-1.22102.8/ThirdPartyNotices.rtf|json
+src/source-build-reference-packages/src/textOnlyPackages/src/microsoft.netcore.*/1.*/ThirdPartyNotices.txt|unknown
+
+# Contains references to licenses which are not applicable to the source
+src/source-build-reference-packages/src/packageSourceGenerator/PackageSourceGeneratorTask/RewriteNuspec.cs|unknown-license-reference,ms-net-library-2018-11
+src/source-build-reference-packages/src/textOnlyPackages/src/microsoft.private.intellisense/8.0.0-preview-20230918.1/IntellisenseFiles/windowsdesktop/1033/PresentationCore.xml|proprietary-license
+
+#
+# sourcelink
+#
+
+# False positive
+src/sourcelink/docs/GitSpec/GitSpec.md|unknown-license-reference
+
+#
+# test-templates
+#
+
+# Not applicable to source
+src/test-templates/Templates/**/*.vstemplate
+
+#
+# vstest
+#
+
+# False positive
+src/vstest/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/ProcDumpArgsBuilder.cs|proprietary-license
+
+# Build asset, but not applying to code
+src/vstest/src/package/Microsoft.CodeCoverage/ThirdPartyNoticesCodeCoverage.txt
+src/vstest/src/package/Microsoft.VisualStudio.TestTools.TestPlatform.V2.CLI/License.rtf
diff --git a/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/assets/baselines/licenses/Licenses.arcade.json b/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/assets/baselines/licenses/Licenses.arcade.json
new file mode 100644
index 000000000..6941fa698
--- /dev/null
+++ b/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/assets/baselines/licenses/Licenses.arcade.json
@@ -0,0 +1,3 @@
+{
+ "files": []
+}
\ No newline at end of file
diff --git a/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/assets/baselines/licenses/Licenses.aspnetcore.json b/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/assets/baselines/licenses/Licenses.aspnetcore.json
new file mode 100644
index 000000000..6941fa698
--- /dev/null
+++ b/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/assets/baselines/licenses/Licenses.aspnetcore.json
@@ -0,0 +1,3 @@
+{
+ "files": []
+}
\ No newline at end of file
diff --git a/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/assets/baselines/licenses/Licenses.cecil.json b/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/assets/baselines/licenses/Licenses.cecil.json
new file mode 100644
index 000000000..6941fa698
--- /dev/null
+++ b/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/assets/baselines/licenses/Licenses.cecil.json
@@ -0,0 +1,3 @@
+{
+ "files": []
+}
\ No newline at end of file
diff --git a/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/assets/baselines/licenses/Licenses.command-line-api.json b/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/assets/baselines/licenses/Licenses.command-line-api.json
new file mode 100644
index 000000000..6941fa698
--- /dev/null
+++ b/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/assets/baselines/licenses/Licenses.command-line-api.json
@@ -0,0 +1,3 @@
+{
+ "files": []
+}
\ No newline at end of file
diff --git a/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/assets/baselines/licenses/Licenses.deployment-tools.json b/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/assets/baselines/licenses/Licenses.deployment-tools.json
new file mode 100644
index 000000000..6941fa698
--- /dev/null
+++ b/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/assets/baselines/licenses/Licenses.deployment-tools.json
@@ -0,0 +1,3 @@
+{
+ "files": []
+}
\ No newline at end of file
diff --git a/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/assets/baselines/licenses/Licenses.diagnostics.json b/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/assets/baselines/licenses/Licenses.diagnostics.json
new file mode 100644
index 000000000..6941fa698
--- /dev/null
+++ b/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/assets/baselines/licenses/Licenses.diagnostics.json
@@ -0,0 +1,3 @@
+{
+ "files": []
+}
\ No newline at end of file
diff --git a/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/assets/baselines/licenses/Licenses.emsdk.json b/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/assets/baselines/licenses/Licenses.emsdk.json
new file mode 100644
index 000000000..6941fa698
--- /dev/null
+++ b/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/assets/baselines/licenses/Licenses.emsdk.json
@@ -0,0 +1,3 @@
+{
+ "files": []
+}
\ No newline at end of file
diff --git a/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/assets/baselines/licenses/Licenses.format.json b/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/assets/baselines/licenses/Licenses.format.json
new file mode 100644
index 000000000..6941fa698
--- /dev/null
+++ b/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/assets/baselines/licenses/Licenses.format.json
@@ -0,0 +1,3 @@
+{
+ "files": []
+}
\ No newline at end of file
diff --git a/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/assets/baselines/licenses/Licenses.fsharp.json b/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/assets/baselines/licenses/Licenses.fsharp.json
new file mode 100644
index 000000000..6941fa698
--- /dev/null
+++ b/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/assets/baselines/licenses/Licenses.fsharp.json
@@ -0,0 +1,3 @@
+{
+ "files": []
+}
\ No newline at end of file
diff --git a/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/assets/baselines/licenses/Licenses.installer.json b/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/assets/baselines/licenses/Licenses.installer.json
new file mode 100644
index 000000000..6941fa698
--- /dev/null
+++ b/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/assets/baselines/licenses/Licenses.installer.json
@@ -0,0 +1,3 @@
+{
+ "files": []
+}
\ No newline at end of file
diff --git a/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/assets/baselines/licenses/Licenses.msbuild.json b/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/assets/baselines/licenses/Licenses.msbuild.json
new file mode 100644
index 000000000..6941fa698
--- /dev/null
+++ b/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/assets/baselines/licenses/Licenses.msbuild.json
@@ -0,0 +1,3 @@
+{
+ "files": []
+}
\ No newline at end of file
diff --git a/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/assets/baselines/licenses/Licenses.nuget-client.json b/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/assets/baselines/licenses/Licenses.nuget-client.json
new file mode 100644
index 000000000..6941fa698
--- /dev/null
+++ b/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/assets/baselines/licenses/Licenses.nuget-client.json
@@ -0,0 +1,3 @@
+{
+ "files": []
+}
\ No newline at end of file
diff --git a/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/assets/baselines/licenses/Licenses.razor.json b/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/assets/baselines/licenses/Licenses.razor.json
new file mode 100644
index 000000000..6941fa698
--- /dev/null
+++ b/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/assets/baselines/licenses/Licenses.razor.json
@@ -0,0 +1,3 @@
+{
+ "files": []
+}
\ No newline at end of file
diff --git a/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/assets/baselines/licenses/Licenses.roslyn-analyzers.json b/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/assets/baselines/licenses/Licenses.roslyn-analyzers.json
new file mode 100644
index 000000000..6941fa698
--- /dev/null
+++ b/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/assets/baselines/licenses/Licenses.roslyn-analyzers.json
@@ -0,0 +1,3 @@
+{
+ "files": []
+}
\ No newline at end of file
diff --git a/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/assets/baselines/licenses/Licenses.roslyn.json b/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/assets/baselines/licenses/Licenses.roslyn.json
new file mode 100644
index 000000000..6941fa698
--- /dev/null
+++ b/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/assets/baselines/licenses/Licenses.roslyn.json
@@ -0,0 +1,3 @@
+{
+ "files": []
+}
\ No newline at end of file
diff --git a/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/assets/baselines/licenses/Licenses.runtime.json b/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/assets/baselines/licenses/Licenses.runtime.json
new file mode 100644
index 000000000..472899b4c
--- /dev/null
+++ b/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/assets/baselines/licenses/Licenses.runtime.json
@@ -0,0 +1,20 @@
+{
+ "files": [
+ {
+ "path": "src/tests/GC/Scenarios/GCBench/THIRD-PARTY-NOTICES",
+ "detected_license_expression": "unknown-license-reference"
+ },
+ {
+ "path": "src/tests/JIT/Performance/CodeQuality/Benchstones/BenchF/LLoops/THIRD-PARTY-NOTICES",
+ "detected_license_expression": "unknown-license-reference"
+ },
+ {
+ "path": "src/tests/JIT/Performance/CodeQuality/Benchstones/MDBenchF/MDLLoops/THIRD-PARTY-NOTICES",
+ "detected_license_expression": "unknown-license-reference"
+ },
+ {
+ "path": "src/tests/JIT/Performance/CodeQuality/V8/Richards/THIRD-PARTY-NOTICES",
+ "detected_license_expression": "unknown-license-reference"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/assets/baselines/licenses/Licenses.sdk.json b/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/assets/baselines/licenses/Licenses.sdk.json
new file mode 100644
index 000000000..6941fa698
--- /dev/null
+++ b/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/assets/baselines/licenses/Licenses.sdk.json
@@ -0,0 +1,3 @@
+{
+ "files": []
+}
\ No newline at end of file
diff --git a/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/assets/baselines/licenses/Licenses.source-build-externals.json b/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/assets/baselines/licenses/Licenses.source-build-externals.json
new file mode 100644
index 000000000..42c3e6895
--- /dev/null
+++ b/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/assets/baselines/licenses/Licenses.source-build-externals.json
@@ -0,0 +1,12 @@
+{
+ "files": [
+ {
+ "path": "src/application-insights/LOGGING/ThirdPartyNotices.txt",
+ "detected_license_expression": "unknown AND apache-2.0 AND mit AND bsd-new"
+ },
+ {
+ "path": "src/application-insights/WEB/ThirdPartyNotices.txt",
+ "detected_license_expression": "bsd-new AND mit AND ms-pl AND apache-2.0 AND (cc-by-3.0-us AND cc-by-3.0 AND mit) AND ms-net-library AND ms-rl"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/assets/baselines/licenses/Licenses.source-build-reference-packages.json b/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/assets/baselines/licenses/Licenses.source-build-reference-packages.json
new file mode 100644
index 000000000..6941fa698
--- /dev/null
+++ b/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/assets/baselines/licenses/Licenses.source-build-reference-packages.json
@@ -0,0 +1,3 @@
+{
+ "files": []
+}
\ No newline at end of file
diff --git a/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/assets/baselines/licenses/Licenses.sourcelink.json b/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/assets/baselines/licenses/Licenses.sourcelink.json
new file mode 100644
index 000000000..6941fa698
--- /dev/null
+++ b/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/assets/baselines/licenses/Licenses.sourcelink.json
@@ -0,0 +1,3 @@
+{
+ "files": []
+}
\ No newline at end of file
diff --git a/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/assets/baselines/licenses/Licenses.symreader.json b/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/assets/baselines/licenses/Licenses.symreader.json
new file mode 100644
index 000000000..6941fa698
--- /dev/null
+++ b/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/assets/baselines/licenses/Licenses.symreader.json
@@ -0,0 +1,3 @@
+{
+ "files": []
+}
\ No newline at end of file
diff --git a/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/assets/baselines/licenses/Licenses.templating.json b/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/assets/baselines/licenses/Licenses.templating.json
new file mode 100644
index 000000000..6941fa698
--- /dev/null
+++ b/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/assets/baselines/licenses/Licenses.templating.json
@@ -0,0 +1,3 @@
+{
+ "files": []
+}
\ No newline at end of file
diff --git a/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/assets/baselines/licenses/Licenses.test-templates.json b/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/assets/baselines/licenses/Licenses.test-templates.json
new file mode 100644
index 000000000..6941fa698
--- /dev/null
+++ b/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/assets/baselines/licenses/Licenses.test-templates.json
@@ -0,0 +1,3 @@
+{
+ "files": []
+}
\ No newline at end of file
diff --git a/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/assets/baselines/licenses/Licenses.vstest.json b/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/assets/baselines/licenses/Licenses.vstest.json
new file mode 100644
index 000000000..6941fa698
--- /dev/null
+++ b/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/assets/baselines/licenses/Licenses.vstest.json
@@ -0,0 +1,3 @@
+{
+ "files": []
+}
\ No newline at end of file
diff --git a/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/assets/baselines/licenses/Licenses.xdt.json b/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/assets/baselines/licenses/Licenses.xdt.json
new file mode 100644
index 000000000..6941fa698
--- /dev/null
+++ b/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/assets/baselines/licenses/Licenses.xdt.json
@@ -0,0 +1,3 @@
+{
+ "files": []
+}
\ No newline at end of file
diff --git a/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/assets/baselines/licenses/Licenses.xliff-tasks.json b/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/assets/baselines/licenses/Licenses.xliff-tasks.json
new file mode 100644
index 000000000..6941fa698
--- /dev/null
+++ b/src/SourceBuild/content/test/Microsoft.DotNet.SourceBuild.SmokeTests/assets/baselines/licenses/Licenses.xliff-tasks.json
@@ -0,0 +1,3 @@
+{
+ "files": []
+}
\ No newline at end of file
diff --git a/src/redist/targets/GeneratePKG.targets b/src/redist/targets/GeneratePKG.targets
index 60336eb5f..9cab8a0a9 100644
--- a/src/redist/targets/GeneratePKG.targets
+++ b/src/redist/targets/GeneratePKG.targets
@@ -95,7 +95,8 @@
x86_64
- 10.13
+
+ 10.15
11.0