Merge branch 'release/8.0.2xx'

This commit is contained in:
Jason Zhai 2023-10-26 02:24:07 -07:00
commit 2084a36452
21 changed files with 229 additions and 157 deletions

View file

@ -9,11 +9,11 @@
Basically: In this file, choose the highest version when resolving merge conflicts. Basically: In this file, choose the highest version when resolving merge conflicts.
--> -->
<PropertyGroup> <PropertyGroup>
<MicrosoftWindowsSDKNETRef10_0_17763PackageVersion>10.0.17763.29</MicrosoftWindowsSDKNETRef10_0_17763PackageVersion> <MicrosoftWindowsSDKNETRef10_0_17763PackageVersion>10.0.17763.31</MicrosoftWindowsSDKNETRef10_0_17763PackageVersion>
<MicrosoftWindowsSDKNETRef10_0_18362PackageVersion>10.0.18362.29</MicrosoftWindowsSDKNETRef10_0_18362PackageVersion> <MicrosoftWindowsSDKNETRef10_0_18362PackageVersion>10.0.18362.31</MicrosoftWindowsSDKNETRef10_0_18362PackageVersion>
<MicrosoftWindowsSDKNETRef10_0_19041PackageVersion>10.0.19041.29</MicrosoftWindowsSDKNETRef10_0_19041PackageVersion> <MicrosoftWindowsSDKNETRef10_0_19041PackageVersion>10.0.19041.31</MicrosoftWindowsSDKNETRef10_0_19041PackageVersion>
<MicrosoftWindowsSDKNETRef10_0_20348PackageVersion>10.0.20348.29</MicrosoftWindowsSDKNETRef10_0_20348PackageVersion> <MicrosoftWindowsSDKNETRef10_0_20348PackageVersion>10.0.20348.31</MicrosoftWindowsSDKNETRef10_0_20348PackageVersion>
<MicrosoftWindowsSDKNETRef10_0_22000PackageVersion>10.0.22000.29</MicrosoftWindowsSDKNETRef10_0_22000PackageVersion> <MicrosoftWindowsSDKNETRef10_0_22000PackageVersion>10.0.22000.31</MicrosoftWindowsSDKNETRef10_0_22000PackageVersion>
<MicrosoftWindowsSDKNETRef10_0_22621PackageVersion>10.0.22621.29</MicrosoftWindowsSDKNETRef10_0_22621PackageVersion> <MicrosoftWindowsSDKNETRef10_0_22621PackageVersion>10.0.22621.31</MicrosoftWindowsSDKNETRef10_0_22621PackageVersion>
</PropertyGroup> </PropertyGroup>
</Project> </Project>

View file

@ -127,7 +127,7 @@ jobs:
artifact: ${{ parameters.reuseBuildArtifactsFrom }}_${{ parameters.architecture }}_Artifacts artifact: ${{ parameters.reuseBuildArtifactsFrom }}_${{ parameters.architecture }}_Artifacts
patterns: | patterns: |
**/Private.SourceBuilt.Artifacts.*.tar.gz **/Private.SourceBuilt.Artifacts.*.tar.gz
**/dotnet-sdk-+([0-9]).+([0-9]).+([0-9])*.tar.gz **/dotnet-sdk-*.tar.gz
displayName: Download Previous Build displayName: Download Previous Build
- task: CopyFiles@2 - task: CopyFiles@2

View file

@ -6,6 +6,10 @@ trigger:
include: include:
- main - main
- release/* - release/*
exclude:
- release/*.0.2xx
- release/*.0.3xx
- release/*.0.4xx
resources: resources:
repositories: repositories:

View file

@ -95,7 +95,7 @@
DiscoverSymbolsTarballs; DiscoverSymbolsTarballs;
ExtractSymbolsTarballs"> ExtractSymbolsTarballs">
<PropertyGroup> <PropertyGroup>
<UnifiedSymbolsTarball>$(OutputPath)dotnet-symbols-$(MicrosoftSourceBuildIntermediateInstallerVersion)-$(TargetRid).tar.gz</UnifiedSymbolsTarball> <UnifiedSymbolsTarball>$(OutputPath)dotnet-symbols-all-$(MicrosoftSourceBuildIntermediateInstallerVersion)-$(TargetRid).tar.gz</UnifiedSymbolsTarball>
</PropertyGroup> </PropertyGroup>
<Exec Command="tar --numeric-owner -czf $(UnifiedSymbolsTarball) *" <Exec Command="tar --numeric-owner -czf $(UnifiedSymbolsTarball) *"
@ -109,13 +109,12 @@
AfterTargets="Build" AfterTargets="Build"
DependsOnTargets="RepackageSymbols"> DependsOnTargets="RepackageSymbols">
<ItemGroup> <ItemGroup>
<SdkTarballItem Include="$(OutputPath)dotnet-sdk-*$(TarBallExtension)" <SdkTarballItem Include="$(OutputPath)dotnet-sdk-*$(TarBallExtension)" />
Exclude="$(OutputPath)dotnet-sdk-symbols-*$(TarBallExtension)" />
</ItemGroup> </ItemGroup>
<PropertyGroup> <PropertyGroup>
<SdkSymbolsLayout>$(ArtifactsTmpDir)SdkSymbols</SdkSymbolsLayout> <SdkSymbolsLayout>$(ArtifactsTmpDir)SdkSymbols</SdkSymbolsLayout>
<SdkSymbolsTarball>$(OutputPath)dotnet-sdk-symbols-$(MicrosoftSourceBuildIntermediateInstallerVersion)-$(TargetRid).tar.gz</SdkSymbolsTarball> <SdkSymbolsTarball>$(OutputPath)dotnet-symbols-sdk-$(MicrosoftSourceBuildIntermediateInstallerVersion)-$(TargetRid).tar.gz</SdkSymbolsTarball>
<SdkLayout>$(ArtifactsTmpDir)Sdk</SdkLayout> <SdkLayout>$(ArtifactsTmpDir)Sdk</SdkLayout>
<SdkTarball>%(SdkTarballItem.Identity)</SdkTarball> <SdkTarball>%(SdkTarballItem.Identity)</SdkTarball>
</PropertyGroup> </PropertyGroup>
@ -187,8 +186,7 @@
<Target Name="RunSmokeTest"> <Target Name="RunSmokeTest">
<ItemGroup> <ItemGroup>
<SdkTarballItem Include="$(SourceBuiltTarBallPath)**/dotnet-sdk*$(TarBallExtension)" <SdkTarballItem Include="$(SourceBuiltTarBallPath)**/dotnet-sdk*$(TarBallExtension)" />
Exclude="$(SourceBuiltTarBallPath)**/dotnet-sdk-symbols*$(TarBallExtension)" />
<SourceBuiltArtifactsItem Include="$(SourceBuiltTarBallPath)**/Private.SourceBuilt.Artifacts.*$(TarBallExtension)" /> <SourceBuiltArtifactsItem Include="$(SourceBuiltTarBallPath)**/Private.SourceBuilt.Artifacts.*$(TarBallExtension)" />
</ItemGroup> </ItemGroup>

View file

@ -30,8 +30,8 @@
These URLs can't be composed from their base URL and version as we read them from the These URLs can't be composed from their base URL and version as we read them from the
prep.sh and pipeline scripts, outside of MSBuild. prep.sh and pipeline scripts, outside of MSBuild.
--> -->
<PrivateSourceBuiltArtifactsUrl>https://dotnetcli.azureedge.net/source-built-artifacts/assets/Private.SourceBuilt.Artifacts.8.0.100-rc.1.23455.1.centos.8-x64.tar.gz</PrivateSourceBuiltArtifactsUrl> <PrivateSourceBuiltArtifactsUrl>https://dotnetcli.azureedge.net/source-built-artifacts/assets/Private.SourceBuilt.Artifacts.8.0.100-rc.2.23502.1.centos.8-x64.tar.gz</PrivateSourceBuiltArtifactsUrl>
<PrivateSourceBuiltSdkUrl_CentOS8Stream>https://dotnetcli.azureedge.net/source-built-artifacts/sdks/dotnet-sdk-8.0.100-rc.1.23455.1-centos.8-x64.tar.gz</PrivateSourceBuiltSdkUrl_CentOS8Stream> <PrivateSourceBuiltSdkUrl_CentOS8Stream>https://dotnetcli.azureedge.net/source-built-artifacts/sdks/dotnet-sdk-8.0.100-rc.2.23502.1-centos.8-x64.tar.gz</PrivateSourceBuiltSdkUrl_CentOS8Stream>
<PrivateSourceBuiltPrebuiltsUrl>https://dotnetcli.azureedge.net/source-built-artifacts/assets/Private.SourceBuilt.Prebuilts.0.1.0-9.0.100-3.centos.8-x64.tar.gz</PrivateSourceBuiltPrebuiltsUrl> <PrivateSourceBuiltPrebuiltsUrl>https://dotnetcli.azureedge.net/source-built-artifacts/assets/Private.SourceBuilt.Prebuilts.0.1.0-9.0.100-3.centos.8-x64.tar.gz</PrivateSourceBuiltPrebuiltsUrl>
</PropertyGroup> </PropertyGroup>
</Project> </Project>

View file

@ -1,11 +1,23 @@
<Project> <Project>
<PropertyGroup> <PropertyGroup>
<!-- 7.0.0 produced improperly versioned runtime assets because the OfficialBuildId <!-- 7.0.0 produced improperly versioned runtime assets because the OfficialBuildId
was not set correctly. This is the actual shipping version that it should have been --> was not set correctly. This is the actual shipping version that it should have been -->
<NonshippingRuntimeVersionFor700>7.0.4-servicing.23107.6</NonshippingRuntimeVersionFor700> <NonshippingRuntimeVersionFor700>7.0.4-servicing.23107.6</NonshippingRuntimeVersionFor700>
<MicrosoftNETCoreTestHostVersion>$(NonshippingRuntimeVersionFor700)</MicrosoftNETCoreTestHostVersion>
<MicrosoftNETHostModelVersion>$(NonshippingRuntimeVersionFor700)</MicrosoftNETHostModelVersion> <Msft80RC2RuntimeVersion>8.0.0-rc.2.23479.6</Msft80RC2RuntimeVersion>
<MicrosoftNETCoreTestHostVersion>$(NonshippingRuntimeVersionFor700)</MicrosoftNETCoreTestHostVersion> <MicrosoftNETCoreAppCrossgen2Version>$(Msft80RC2RuntimeVersion)</MicrosoftNETCoreAppCrossgen2Version>
<RuntimeLinuxX64MicrosoftNETCoreTestHostVersion>$(NonshippingRuntimeVersionFor700)</RuntimeLinuxX64MicrosoftNETCoreTestHostVersion> <MicrosoftNETCoreAppHostPackageVersion>$(Msft80RC2RuntimeVersion)</MicrosoftNETCoreAppHostPackageVersion>
<MicrosoftNETCoreAppRuntimeVersion>$(Msft80RC2RuntimeVersion)</MicrosoftNETCoreAppRuntimeVersion>
<MicrosoftNETILLinkTasksVersion>$(Msft80RC2RuntimeVersion)</MicrosoftNETILLinkTasksVersion>
<MicrosoftDotNetILCompilerVersion>$(Msft80RC2RuntimeVersion)</MicrosoftDotNetILCompilerVersion>
<MicrosoftNETCoreDotNetAppHostVersion>$(Msft80RC2RuntimeVersion)</MicrosoftNETCoreDotNetAppHostVersion>
<MicrosoftNETCoreDotNetHostVersion>$(Msft80RC2RuntimeVersion)</MicrosoftNETCoreDotNetHostVersion>
<MicrosoftNETCoreDotNetHostPolicyVersion>$(Msft80RC2RuntimeVersion)</MicrosoftNETCoreDotNetHostPolicyVersion>
<MicrosoftNETCoreDotNetHostResolverVersion>$(Msft80RC2RuntimeVersion)</MicrosoftNETCoreDotNetHostResolverVersion>
<MicrosoftNETCoreILAsmVersion>$(Msft80RC2RuntimeVersion)</MicrosoftNETCoreILAsmVersion>
<MicrosoftNETCoreILDAsmVersion>$(Msft80RC2RuntimeVersion)</MicrosoftNETCoreILDAsmVersion>
<RuntimeNativeSystemIOPortsVersion>$(Msft80RC2RuntimeVersion)</RuntimeNativeSystemIOPortsVersion>
</PropertyGroup> </PropertyGroup>
</Project> </Project>

View file

@ -47,6 +47,8 @@
<!-- These packages don't actually exist --> <!-- These packages don't actually exist -->
<ExcludedPackage Include="runtime.linux-musl-x64.runtime.native.System.IO.Ports" /> <ExcludedPackage Include="runtime.linux-musl-x64.runtime.native.System.IO.Ports" />
<ExcludedPackage Include="runtime.linux-musl-arm64.runtime.native.System.IO.Ports" /> <ExcludedPackage Include="runtime.linux-musl-arm64.runtime.native.System.IO.Ports" />
<PackageDownload Include="Microsoft.NET.ILLink.Tasks" Version="[$(MicrosoftNETILLinkTasksVersion)]" />
</ItemGroup> </ItemGroup>
<Target Name="GetPackagesToDownload" <Target Name="GetPackagesToDownload"

View file

@ -12,8 +12,11 @@ using System.IO;
using System.IO.Compression; using System.IO.Compression;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using System.Reflection.Metadata;
using System.Reflection.PortableExecutable;
using System.Security.Cryptography; using System.Security.Cryptography;
using System.Text; using System.Text;
using System.Text.RegularExpressions;
using System.Xml; using System.Xml;
using System.Xml.Linq; using System.Xml.Linq;
@ -147,6 +150,10 @@ namespace Microsoft.DotNet.SourceBuild.Tasks.LeakDetection
private const string PoisonMarker = "POISONED"; private const string PoisonMarker = "POISONED";
private const string SbrpAttributeType = "System.Reflection.AssemblyMetadataAttribute";
private const string SbrpAttributeValuePattern = "source\\s?source\\-build\\-reference\\-packages";
private record CandidateFileEntry(string ExtractedPath, string DisplayPath); private record CandidateFileEntry(string ExtractedPath, string DisplayPath);
public override bool Execute() public override bool Execute()
@ -298,7 +305,11 @@ namespace Microsoft.DotNet.SourceBuild.Tasks.LeakDetection
try try
{ {
AssemblyName asm = AssemblyName.GetAssemblyName(fileToCheck); AssemblyName asm = AssemblyName.GetAssemblyName(fileToCheck);
if (IsAssemblyPoisoned(fileToCheck)) if (!candidate.DisplayPath.Contains("SourceBuildReferencePackages") && IsAssemblyFromSbrp(fileToCheck))
{
poisonEntry.Type |= PoisonType.SourceBuildReferenceAssembly;
}
else if (IsAssemblyPoisoned(fileToCheck))
{ {
poisonEntry.Type |= PoisonType.AssemblyAttribute; poisonEntry.Type |= PoisonType.AssemblyAttribute;
} }
@ -332,6 +343,41 @@ namespace Microsoft.DotNet.SourceBuild.Tasks.LeakDetection
return false; 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)
{
MemberReference mref = reader.GetMemberReference((MemberReferenceHandle)attr.Constructor);
if (mref.Parent.Kind == HandleKind.TypeReference)
{
TypeReference tref = reader.GetTypeReference((TypeReferenceHandle)mref.Parent);
attributeType = $"{reader.GetString(tref.Namespace)}.{reader.GetString(tref.Name)}";
}
}
if (attributeType == SbrpAttributeType)
{
BlobReader blobReader = reader.GetBlobReader(attr.Value);
string attributeValue = Encoding.UTF8.GetString(blobReader.ReadBytes(blobReader.Length));
attributeValue = Regex.Replace(attributeValue, @"\p{C}+", string.Empty);
return Regex.IsMatch(attributeValue, SbrpAttributeValuePattern);
}
return false;
}
private static PoisonedFileEntry ExtractAndCheckZipFileOnly(IEnumerable<CatalogPackageEntry> catalogedPackages, CandidateFileEntry candidate, string markerFileName, string tempDir, Queue<CandidateFileEntry> futureFilesToCheck) private static PoisonedFileEntry ExtractAndCheckZipFileOnly(IEnumerable<CatalogPackageEntry> catalogedPackages, CandidateFileEntry candidate, string markerFileName, string tempDir, Queue<CandidateFileEntry> futureFilesToCheck)
{ {
var poisonEntry = new PoisonedFileEntry(); var poisonEntry = new PoisonedFileEntry();

View file

@ -155,6 +155,7 @@ namespace Microsoft.DotNet.SourceBuild.Tasks.LeakDetection
} }
File.Delete(p.ItemSpec); File.Delete(p.ItemSpec);
File.Move(poisonedPackagePath, p.ItemSpec); File.Move(poisonedPackagePath, p.ItemSpec);
Directory.Delete(packageTempPath, true);
} }
} }

View file

@ -11,5 +11,6 @@ namespace Microsoft.DotNet.SourceBuild.Tasks.LeakDetection
Hash = 1, Hash = 1,
AssemblyAttribute = 2, AssemblyAttribute = 2,
NupkgFile = 4, NupkgFile = 4,
SourceBuildReferenceAssembly = 8,
} }
} }

View file

@ -57,7 +57,7 @@ namespace Microsoft.DotNet.Build.Tasks
private void LogErrorOrWarning(bool isError, string message) private void LogErrorOrWarning(bool isError, string message)
{ {
if (FailOnMissingPDBs) if (isError)
{ {
Log.LogError(message); Log.LogError(message);
} }
@ -104,14 +104,15 @@ namespace Microsoft.DotNet.Build.Tasks
if (guid != string.Empty) if (guid != string.Empty)
{ {
if (!allPdbGuids.ContainsKey(guid)) string debugId = GetDebugId(guid, file);
if (!allPdbGuids.ContainsKey(debugId))
{ {
filesWithoutPDBs.Add(file.Substring(SdkLayoutPath.Length + 1)); filesWithoutPDBs.Add(file.Substring(SdkLayoutPath.Length + 1));
} }
else else
{ {
// Copy matching pdb to symbols path, preserving sdk binary's hierarchy // Copy matching pdb to symbols path, preserving sdk binary's hierarchy
string sourcePath = (string)allPdbGuids[guid]!; string sourcePath = (string)allPdbGuids[debugId]!;
string destinationPath = string destinationPath =
file.Replace(SdkLayoutPath, SdkSymbolsLayoutPath) file.Replace(SdkLayoutPath, SdkSymbolsLayoutPath)
.Replace(Path.GetFileName(file), Path.GetFileName(sourcePath)); .Replace(Path.GetFileName(file), Path.GetFileName(sourcePath));
@ -142,13 +143,21 @@ namespace Microsoft.DotNet.Build.Tasks
var id = new BlobContentId(metadataReader.DebugMetadataHeader.Id); var id = new BlobContentId(metadataReader.DebugMetadataHeader.Id);
string guid = $"{id.Guid:N}"; 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; return allPdbGuids;
} }
/// <summary>
/// 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.
/// </summary>
private string GetDebugId(string guid, string file) =>
$"{guid}.{Path.GetFileNameWithoutExtension(file)}".ToLower();
} }
} }

View file

@ -23,12 +23,6 @@ public class DotNetFormatTests : SdkTests
// [Fact] // [Fact]
public void FormatProject() public void FormatProject()
{ {
if (Config.TargetRid.Contains("alpine"))
{
// Skipping this test on Alpine due to https://github.com/dotnet/format/issues/1945
return;
}
string unformattedCsFilePath = Path.Combine(BaselineHelper.GetAssetsDirectory(), UnformattedFileName); string unformattedCsFilePath = Path.Combine(BaselineHelper.GetAssetsDirectory(), UnformattedFileName);
string projectDirectory = DotNetHelper.ExecuteNew("console", nameof(FormatProject), "C#"); string projectDirectory = DotNetHelper.ExecuteNew("console", nameof(FormatProject), "C#");

View file

@ -73,6 +73,7 @@ public class LicenseScanTests : TestBase
"lgpl-2.0-plus", // https://opensource.org/license/lgpl-2-0/ "lgpl-2.0-plus", // https://opensource.org/license/lgpl-2-0/
"lgpl-2.1", // https://opensource.org/license/lgpl-2-1/ "lgpl-2.1", // https://opensource.org/license/lgpl-2-1/
"lgpl-2.1-plus", // https://opensource.org/license/lgpl-2-1/ "lgpl-2.1-plus", // https://opensource.org/license/lgpl-2-1/
"lzma-sdk-9.22", // https://github.com/nexB/scancode-toolkit/blob/develop/src/licensedcode/data/licenses/lzma-sdk-9.22.LICENSE
"mit", // https://opensource.org/license/mit/ "mit", // https://opensource.org/license/mit/
"mit-addition", // https://github.com/nexB/scancode-toolkit/blob/develop/src/licensedcode/data/licenses/mit-addition.LICENSE "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-patent-promise", // https://github.com/nexB/scancode-toolkit/blob/develop/src/licensedcode/data/licenses/ms-patent-promise.LICENSE
@ -142,17 +143,16 @@ public class LicenseScanTests : TestBase
{ {
Assert.NotNull(Config.LicenseScanPath); Assert.NotNull(Config.LicenseScanPath);
string OriginalScancodeResultsPath = Path.Combine(LogsDirectory, "scancode-results-original.json"); string scancodeResultsPath = Path.Combine(LogsDirectory, "scancode-results.json");
string FilteredScancodeResultsPath = Path.Combine(LogsDirectory, "scancode-results-filtered.json");
// Scancode Doc: https://scancode-toolkit.readthedocs.io/en/latest/index.html // Scancode Doc: https://scancode-toolkit.readthedocs.io/en/latest/index.html
string ignoreOptions = string.Join(" ", s_ignoredFilePatterns.Select(pattern => $"--ignore {pattern}")); string ignoreOptions = string.Join(" ", s_ignoredFilePatterns.Select(pattern => $"--ignore {pattern}"));
ExecuteHelper.ExecuteProcessValidateExitCode( ExecuteHelper.ExecuteProcessValidateExitCode(
"scancode", "scancode",
$"--license --strip-root --only-findings {ignoreOptions} --json-pp {OriginalScancodeResultsPath} {Config.LicenseScanPath}", $"--license --strip-root --only-findings {ignoreOptions} --json-pp {scancodeResultsPath} {Config.LicenseScanPath}",
OutputHelper); OutputHelper);
JsonDocument doc = JsonDocument.Parse(File.ReadAllText(OriginalScancodeResultsPath)); JsonDocument doc = JsonDocument.Parse(File.ReadAllText(scancodeResultsPath));
ScancodeResults? scancodeResults = doc.Deserialize<ScancodeResults>(); ScancodeResults? scancodeResults = doc.Deserialize<ScancodeResults>();
Assert.NotNull(scancodeResults); Assert.NotNull(scancodeResults);
@ -163,7 +163,6 @@ public class LicenseScanTests : TestBase
WriteIndented = true WriteIndented = true
}; };
string json = JsonSerializer.Serialize(scancodeResults, options); string json = JsonSerializer.Serialize(scancodeResults, options);
File.WriteAllText(FilteredScancodeResultsPath, json);
string baselineName = $"Licenses.{_targetRepo}.json"; string baselineName = $"Licenses.{_targetRepo}.json";

View file

@ -14,6 +14,15 @@ using Xunit.Abstractions;
namespace Microsoft.DotNet.SourceBuild.SmokeTests; namespace Microsoft.DotNet.SourceBuild.SmokeTests;
/// <summary>
/// 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.
/// </summary>
[CollectionDefinition(nameof(SourcelinkTestCollection), DisableParallelization = true)]
public class SourcelinkTestCollection { }
[Collection(nameof(SourcelinkTestCollection))]
public class SourcelinkTests : SdkTests public class SourcelinkTests : SdkTests
{ {
private static string SourcelinkRoot { get; } = Path.Combine(Directory.GetCurrentDirectory(), nameof(SourcelinkTests)); private static string SourcelinkRoot { get; } = Path.Combine(Directory.GetCurrentDirectory(), nameof(SourcelinkTests));
@ -27,24 +36,40 @@ public class SourcelinkTests : SdkTests
// [Fact] // [Fact]
public void VerifySourcelinks() 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<string> 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.Delete(SourcelinkRoot, true);
} }
Directory.CreateDirectory(SourcelinkRoot);
IList<string> 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);
} }
/// <summary> /// <summary>
@ -54,6 +79,8 @@ public class SourcelinkTests : SdkTests
/// <returns>Path to sourcelink tool binary.</returns> /// <returns>Path to sourcelink tool binary.</returns>
private string InitializeSourcelinkTool() private string InitializeSourcelinkTool()
{ {
Assert.NotNull(Config.SourceBuiltArtifactsPath);
const string SourcelinkToolPackageNamePattern = "dotnet-sourcelink*nupkg"; const string SourcelinkToolPackageNamePattern = "dotnet-sourcelink*nupkg";
const string SourcelinkToolBinaryFilename = "dotnet-sourcelink.dll"; const string SourcelinkToolBinaryFilename = "dotnet-sourcelink.dll";
@ -66,38 +93,6 @@ public class SourcelinkTests : SdkTests
return Utilities.GetFile(extractedToolPath, SourcelinkToolBinaryFilename); return Utilities.GetFile(extractedToolPath, SourcelinkToolBinaryFilename);
} }
private IEnumerable<string> 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. <repo-root>/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");
}
/// <summary>
/// Extracts symbols packages to subdirectories of the common symbols root directory.
/// </summary>
/// <returns>Path to common symbols root directory.</returns>
private string ExtractSymbolsPackages(IEnumerable<string> 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<string> ValidateSymbols(string path, string sourcelinkToolPath) private IList<string> ValidateSymbols(string path, string sourcelinkToolPath)
{ {
Assert.True(Directory.Exists(path), $"Path, with symbol files to validate, does not exist: {path}"); Assert.True(Directory.Exists(path), $"Path, with symbol files to validate, does not exist: {path}");
@ -113,7 +108,7 @@ public class SourcelinkTests : SdkTests
OutputHelper, OutputHelper,
logOutput: false, logOutput: false,
excludeInfo: true, // Exclude info messages, as there can be 1,000+ processes excludeInfo: true, // Exclude info messages, as there can be 1,000+ processes
millisecondTimeout: 5000, millisecondTimeout: 60000,
configureCallback: (process) => DotNetHelper.ConfigureProcess(process, null)); configureCallback: (process) => DotNetHelper.ConfigureProcess(process, null));
if (executeResult.Process.ExitCode != 0) if (executeResult.Process.ExitCode != 0)

View file

@ -28,8 +28,9 @@ src/arcade/src/Microsoft.DotNet.Build.Tasks.Installers/build/wix/eula.rtf
# aspnetcore # aspnetcore
# #
# Line 1 is a generic statement about license applicability that is being detected as "unknown" # A generic statement about license applicability that is being detected as "unknown"
src/aspnetcore/src/Components/THIRD-PARTY-NOTICES.txt|unknown src/aspnetcore/src/Components/THIRD-PARTY-NOTICES.txt|unknown
src/aspnetcore/THIRD-PARTY-NOTICES.txt|unknown
# Windows installer files that have a reference to a URL for license # 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/**/*.wxl|unknown-license-reference
@ -167,7 +168,7 @@ src/runtime/src/installer/pkg/THIRD-PARTY-NOTICES.TXT
# False positive # False positive
src/runtime/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/LicFileLicenseProvider.cs|proprietary-license 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.Configuration.ConfigurationManager/tests/Mono/LongValidatorTest.cs|embedthis-extension
src/runtime/src/libraries/System.Net.Sockets/tests/FunctionalTests/Connect.cs|other-permissive 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/UdpClientTest.cs|other-permissive
src/runtime/src/libraries/System.Net.Sockets/tests/FunctionalTests/SendReceive/SendReceive.cs|other-permissive src/runtime/src/libraries/System.Net.Sockets/tests/FunctionalTests/SendReceive/SendReceive.cs|other-permissive
@ -226,10 +227,11 @@ src/source-build-reference-packages/src/targetPacks/ILsrc/netstandard.library/2.
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/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.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 src/source-build-reference-packages/src/textOnlyPackages/src/microsoft.netcore.*/1.*/ThirdPartyNotices.txt|unknown
src/source-build-reference-packages/src/textOnlyPackages/src/microsoft.private.intellisense/8.0.*/IntellisenseFiles/*/1033/System.Security.Permissions.xml|unknown-license-reference
# Contains references to licenses which are not applicable to the source # 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/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 src/source-build-reference-packages/src/textOnlyPackages/src/microsoft.private.intellisense/8.0.*/IntellisenseFiles/windowsdesktop/1033/PresentationCore.xml|proprietary-license
# #
# sourcelink # sourcelink

View file

@ -48,6 +48,7 @@ index ------------
./packs/NETStandard.Library.Ref/x.y.z/ref/netstandard2.1/System.Xml.XPath.XDocument.dll ./packs/NETStandard.Library.Ref/x.y.z/ref/netstandard2.1/System.Xml.XPath.XDocument.dll
./sdk-manifests/ ./sdk-manifests/
./sdk-manifests/x.y.z/ ./sdk-manifests/x.y.z/
-./sdk-manifests/x.y.z/
-./sdk-manifests/x.y.z/ -./sdk-manifests/x.y.z/
./sdk-manifests/x.y.z/microsoft.net.workload.emscripten.current/ ./sdk-manifests/x.y.z/microsoft.net.workload.emscripten.current/
./sdk-manifests/x.y.z/microsoft.net.workload.emscripten.current/x.y.z/ ./sdk-manifests/x.y.z/microsoft.net.workload.emscripten.current/x.y.z/

View file

@ -1 +1,14 @@
<PrebuiltLeakReport /> <PrebuiltLeakReport>
<File Path="artifacts/x64/Release/dotnet-sdk-x.y.z-banana-rid.tar.gz/sdk/x.y.z/DotnetTools/dotnet-format/Microsoft.Bcl.AsyncInterfaces.dll">
<Type>SourceBuildReferenceAssembly</Type>
</File>
<File Path="artifacts/x64/Release/Private.SourceBuilt.Artifacts.x.y.z/dotnet-format.x.y.z.nupkg/tools/netx.y/any/Microsoft.Bcl.AsyncInterfaces.dll">
<Type>SourceBuildReferenceAssembly</Type>
</File>
<File Path="artifacts/x64/Release/Private.SourceBuilt.Artifacts.x.y.z/Microsoft.TestPlatform.CLI.x.y.z/contentFiles/any/netx.y/Microsoft.Extensions.DependencyModel.dll">
<Type>SourceBuildReferenceAssembly</Type>
</File>
<File Path="artifacts/x64/Release/Private.SourceBuilt.Artifacts.x.y.z/Microsoft.TestPlatform.CLI.x.y.z/contentFiles/any/netx.y/Microsoft.Extensions.FileSystemGlobbing.dll">
<Type>SourceBuildReferenceAssembly</Type>
</File>
</PrebuiltLeakReport>

View file

@ -22,18 +22,24 @@ namespace Microsoft.DotNet.Build.Tasks
/// <summary> /// <summary>
/// Replaces files that have the same content with hard links. /// Replaces files that have the same content with hard links.
/// </summary> /// </summary>
public sealed class ReplaceDuplicateFilesWithHardLinks : Task public sealed class ReplaceFilesWithSymbolicLinks : Task
{ {
/// <summary> /// <summary>
/// The path to the directory. /// The path to the directory to recursively search for files to replace with symbolic links.
/// </summary> /// </summary>
[Required] [Required]
public string Directory { get; set; } = ""; public string Directory { get; set; } = "";
/// <summary>
/// The path to the directory with files to link to.
/// </summary>
[Required]
public string LinkToFilesFrom { get; set; } = "";
#if NETFRAMEWORK #if NETFRAMEWORK
public override bool Execute() public override bool Execute()
{ {
Log.LogError($"{nameof(ReplaceDuplicateFilesWithHardLinks)} is not supported on .NET Framework."); Log.LogError($"{nameof(ReplaceFilesWithSymbolicLinks)} is not supported on .NET Framework.");
return false; return false;
} }
#else #else
@ -41,7 +47,7 @@ namespace Microsoft.DotNet.Build.Tasks
{ {
if (OperatingSystem.IsWindows()) if (OperatingSystem.IsWindows())
{ {
Log.LogError($"{nameof(ReplaceDuplicateFilesWithHardLinks)} is not supported on Windows."); Log.LogError($"{nameof(ReplaceFilesWithSymbolicLinks)} is not supported on Windows.");
return false; return false;
} }
@ -51,52 +57,36 @@ namespace Microsoft.DotNet.Build.Tasks
return false; return false;
} }
if (!System.IO.Directory.Exists(LinkToFilesFrom))
{
Log.LogError($"'{LinkToFilesFrom}' does not exist.");
return false;
}
// Find all non-empty, non-symbolic link files. // Find all non-empty, non-symbolic link files.
IEnumerable<FileInfo> fse = new FileSystemEnumerable<FileInfo>( string[] files = new FileSystemEnumerable<string>(
Directory, Directory,
(ref FileSystemEntry entry) => (FileInfo)entry.ToFileSystemInfo(), (ref FileSystemEntry entry) => entry.ToFullPath(),
new EnumerationOptions() new EnumerationOptions()
{ {
AttributesToSkip = FileAttributes.ReparsePoint, AttributesToSkip = FileAttributes.ReparsePoint,
RecurseSubdirectories = true RecurseSubdirectories = true
}) })
{ {
ShouldIncludePredicate = (ref FileSystemEntry entry) => !entry.IsDirectory ShouldIncludePredicate = (ref FileSystemEntry entry) => !entry.IsDirectory
&& entry.Length > 0 && entry.Length > 0
}; }.ToArray();
// Group them by file size. foreach (var file in files)
IEnumerable<string?[]> filesGroupedBySize = fse.GroupBy(file => file.Length,
file => file.FullName,
(size, files) => files.ToArray());
// Replace files with same content with hard link.
foreach (var files in filesGroupedBySize)
{ {
for (int i = 0; i < files.Length; i++) string fileName = Path.GetFileName(file);
// Look for a file with the same name in LinkToFilesFrom
// and replace it with a symbolic link if it has the same content.
string targetFile = Path.Combine(LinkToFilesFrom, fileName);
if (File.Exists(targetFile) && FilesHaveSameContent(file, targetFile))
{ {
string? path1 = files[i]; ReplaceByLinkTo(file, targetFile);
if (path1 is null)
{
continue; // already linked.
}
for (int j = i + 1; j < files.Length; j++)
{
string? path2 = files[j];
if (path2 is null)
{
continue; // already linked.
}
// note: There's no public API we can use to see if paths are already linked.
// We treat those paths as unlinked files, and link them again.
if (FilesHaveSameContent(path1, path2))
{
ReplaceByLink(path1, path2);
files[j] = null;
}
}
} }
} }
@ -138,34 +128,30 @@ namespace Microsoft.DotNet.Build.Tasks
} }
} }
void ReplaceByLink(string path1, string path2) void ReplaceByLinkTo(string path, string pathToTarget)
{ {
// To link, the target mustn't exist. Make a backup, so we can restore it when linking fails. // To link, the target mustn't exist. Make a backup, so we can restore it when linking fails.
string path2Backup = $"{path2}.pre_link_backup"; string backupFile = $"{path}.pre_link_backup";
File.Move(path2, path2Backup); File.Move(path, backupFile);
int rv = SystemNative_Link(path1, path2); try
if (rv != 0)
{ {
var ex = new Win32Exception(); // Captures the LastError. string relativePath = Path.GetRelativePath(Path.GetDirectoryName(path)!, pathToTarget);
File.CreateSymbolicLink(path, relativePath);
Log.LogError($"Unable to link '{path2}' to '{path1}.': {ex}"); File.Delete(backupFile);
File.Move(path2Backup, path2); Log.LogMessage(MessageImportance.Normal, $"Linked '{path}' to '{relativePath}'.");
throw ex;
} }
else catch (Exception ex)
{ {
File.Delete(path2Backup); Log.LogError($"Unable to link '{path}' to '{pathToTarget}.': {ex}");
Log.LogMessage(MessageImportance.Normal, $"Linked '{path1}' and '{path2}'."); File.Move(backupFile, path);
throw;
} }
} }
// This native method is used by the runtime to create hard links. It is not exposed through a public .NET API.
[DllImport("libSystem.Native", SetLastError = true)]
static extern int SystemNative_Link(string source, string link);
#endif #endif
} }
} }

View file

@ -35,7 +35,7 @@
<UsingTask TaskName="GetDependencyInfo" AssemblyFile="$(CoreSdkTaskDll)"/> <UsingTask TaskName="GetDependencyInfo" AssemblyFile="$(CoreSdkTaskDll)"/>
<UsingTask TaskName="GetLinuxNativeInstallerDependencyVersions" AssemblyFile="$(CoreSdkTaskDll)"/> <UsingTask TaskName="GetLinuxNativeInstallerDependencyVersions" AssemblyFile="$(CoreSdkTaskDll)"/>
<UsingTask TaskName="GetRuntimePackRids" AssemblyFile="$(CoreSdkTaskDll)"/> <UsingTask TaskName="GetRuntimePackRids" AssemblyFile="$(CoreSdkTaskDll)"/>
<UsingTask TaskName="ReplaceDuplicateFilesWithHardLinks" AssemblyFile="$(CoreSdkTaskDll)"/> <UsingTask TaskName="ReplaceFilesWithSymbolicLinks" AssemblyFile="$(CoreSdkTaskDll)"/>
<UsingTask TaskName="ReplaceFileContents" AssemblyFile="$(CoreSdkTaskDll)" /> <UsingTask TaskName="ReplaceFileContents" AssemblyFile="$(CoreSdkTaskDll)" />
<UsingTask TaskName="TarGzFileCreateFromDirectory" AssemblyFile="$(CoreSdkTaskDll)" /> <UsingTask TaskName="TarGzFileCreateFromDirectory" AssemblyFile="$(CoreSdkTaskDll)" />
<UsingTask TaskName="UpdateRuntimeConfig" AssemblyFile="$(CoreSdkTaskDll)" /> <UsingTask TaskName="UpdateRuntimeConfig" AssemblyFile="$(CoreSdkTaskDll)" />

View file

@ -569,11 +569,12 @@
<ResolveAssemblyReference AssemblyFiles="@(AssembliesToResolve)" Silent="$(ResolveAssemblyReferencesSilent)" AssemblyInformationCacheOutputPath="$(RedistLayoutPath)sdk\$(Version)\SDKPrecomputedAssemblyReferences.cache" SearchPaths="{RawFileName}" WarnOrErrorOnTargetArchitectureMismatch="$(ResolveAssemblyWarnOrErrorOnTargetArchitectureMismatch)" /> <ResolveAssemblyReference AssemblyFiles="@(AssembliesToResolve)" Silent="$(ResolveAssemblyReferencesSilent)" AssemblyInformationCacheOutputPath="$(RedistLayoutPath)sdk\$(Version)\SDKPrecomputedAssemblyReferences.cache" SearchPaths="{RawFileName}" WarnOrErrorOnTargetArchitectureMismatch="$(ResolveAssemblyWarnOrErrorOnTargetArchitectureMismatch)" />
</Target> </Target>
<!-- Replace duplicate files with hard links so that when the same files from a runtime pack <!-- Replace files from the runtime packs with symbolic links to the corresponding shared framework files (and hostfxr) to reduce the size of the runtime pack directories. -->
and the corresponding shared frameworks are included in a distro package their data is shared instead of duplicated. --> <Target Name="ReplaceBundledRuntimePackFilesWithSymbolicLinks" DependsOnTargets="LayoutBundledComponents"
<Target Name="ReplaceDuplicateFilesWithHardLinks" DependsOnTargets="LayoutBundledComponents" Condition="'$(BundleRuntimePacks)' == 'true' and !$([MSBuild]::IsOSPlatform('WINDOWS'))">
Condition="'$(BundleRuntimePacks)' == 'true' and !$([MSBuild]::IsOSPlatform('WINDOWS'))"> <ReplaceFilesWithSymbolicLinks Directory="$(RedistLayoutPath)/packs/Microsoft.NETCore.App.Runtime.$(SharedFrameworkRid)/$(MicrosoftNETCoreAppRuntimePackageVersion)" LinkToFilesFrom="$(RedistLayoutPath)/shared/Microsoft.NETCore.App/$(MicrosoftNETCoreAppRuntimePackageVersion)" />
<ReplaceDuplicateFilesWithHardLinks Directory="$(RedistLayoutPath)" /> <ReplaceFilesWithSymbolicLinks Directory="$(RedistLayoutPath)/packs/Microsoft.NETCore.App.Runtime.$(SharedFrameworkRid)/$(MicrosoftNETCoreAppRuntimePackageVersion)" LinkToFilesFrom="$(RedistLayoutPath)/host/fxr/$(MicrosoftNETCoreAppRuntimePackageVersion)" />
<ReplaceFilesWithSymbolicLinks Directory="$(RedistLayoutPath)/packs/Microsoft.AspNetCore.App.Runtime.$(SharedFrameworkRid)/$(MicrosoftAspNetCoreAppRuntimePackageVersion)" LinkToFilesFrom="$(RedistLayoutPath)/shared/Microsoft.AspNetCore.App/$(MicrosoftAspNetCoreAppRuntimePackageVersion)" />
</Target> </Target>
<Target Name="GenerateLayout" <Target Name="GenerateLayout"
@ -593,7 +594,7 @@
CrossgenLayout; CrossgenLayout;
LayoutAppHostTemplate; LayoutAppHostTemplate;
GeneratePrecomputedRarCache; GeneratePrecomputedRarCache;
ReplaceDuplicateFilesWithHardLinks" ReplaceBundledRuntimePackFilesWithSymbolicLinks"
BeforeTargets="AfterBuild"> BeforeTargets="AfterBuild">
</Target> </Target>

View file

@ -245,6 +245,14 @@
Skip="true" Skip="true"
Issue="" Issue=""
Reason="Cannot run with non-existent LastRuntimeFrameworkVersion"/> Reason="Cannot run with non-existent LastRuntimeFrameworkVersion"/>
<Method Name="Microsoft.NET.Publish.Tests.GivenThatWeWantToRunILLink.IsTrimmable_warns_when_expected_for_not_correctly_multitargeted_libraries"
Skip="true"
Issue=""
Reason="Cannot run with non-existent LastRuntimeFrameworkVersion"/>
<Method Name="Microsoft.NET.Publish.Tests.GivenThatWeWantToPublishASingleFileApp.EnableSingleFile_warns_when_expected_for_not_correctly_multitargeted_libraries"
Skip="true"
Issue=""
Reason="Cannot run with non-existent LastRuntimeFrameworkVersion"/>
<Method Name="Microsoft.NET.Publish.Tests.GivenThatWeWantToRunILLink.ILLink_can_use_latest_with_unsupported_target_framework" <Method Name="Microsoft.NET.Publish.Tests.GivenThatWeWantToRunILLink.ILLink_can_use_latest_with_unsupported_target_framework"
Skip="true" Skip="true"
Issue="" Issue=""