diff --git a/src/Assets/TestProjects/TrimmableNetstandardLibrary/RequiresUnreferencedCodeAttribute.cs b/src/Assets/TestProjects/TrimmableNetstandardLibrary/RequiresUnreferencedCodeAttribute.cs
new file mode 100644
index 000000000000..c370f6d7e825
--- /dev/null
+++ b/src/Assets/TestProjects/TrimmableNetstandardLibrary/RequiresUnreferencedCodeAttribute.cs
@@ -0,0 +1,40 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace System.Diagnostics.CodeAnalysis
+{
+ ///
+ /// Indicates that the specified method requires dynamic access to code that is not referenced
+ /// statically, for example through .
+ ///
+ ///
+ /// This allows tools to understand which methods are unsafe to call when removing unreferenced
+ /// code from an application.
+ ///
+ [AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Class, Inherited = false)]
+ internal sealed class RequiresUnreferencedCodeAttribute : Attribute
+ {
+ ///
+ /// Initializes a new instance of the class
+ /// with the specified message.
+ ///
+ ///
+ /// A message that contains information about the usage of unreferenced code.
+ ///
+ public RequiresUnreferencedCodeAttribute(string message)
+ {
+ Message = message;
+ }
+
+ ///
+ /// Gets a message that contains information about the usage of unreferenced code.
+ ///
+ public string Message { get; }
+
+ ///
+ /// Gets or sets an optional URL that contains more information about the method,
+ /// why it requires unreferenced code, and what options a consumer has to deal with it.
+ ///
+ public string? Url { get; set; }
+ }
+}
diff --git a/src/Assets/TestProjects/TrimmableNetstandardLibrary/TrimmableNetstandardLibrary.cs b/src/Assets/TestProjects/TrimmableNetstandardLibrary/TrimmableNetstandardLibrary.cs
new file mode 100644
index 000000000000..45b4c5507776
--- /dev/null
+++ b/src/Assets/TestProjects/TrimmableNetstandardLibrary/TrimmableNetstandardLibrary.cs
@@ -0,0 +1,15 @@
+using System.Diagnostics.CodeAnalysis;
+
+class TrimmableNetStandardLibrary
+{
+ public static void EntryPoint()
+ {
+ RequiresUnreferencedCode();
+ }
+
+
+ [RequiresUnreferencedCode("Requires unreferenced code")]
+ static void RequiresUnreferencedCode()
+ {
+ }
+}
diff --git a/src/Assets/TestProjects/TrimmableNetstandardLibrary/TrimmableNetstandardLibrary.csproj b/src/Assets/TestProjects/TrimmableNetstandardLibrary/TrimmableNetstandardLibrary.csproj
new file mode 100644
index 000000000000..0f3ed99f87eb
--- /dev/null
+++ b/src/Assets/TestProjects/TrimmableNetstandardLibrary/TrimmableNetstandardLibrary.csproj
@@ -0,0 +1,22 @@
+
+
+
+
+
+ $(CurrentTargetFramework)
+ netstandard2.0
+ true
+ latest
+ enable
+ latest
+
+
+
+
+
+
+
+
+
diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/ProcessFrameworkReferences.cs b/src/Tasks/Microsoft.NET.Build.Tasks/ProcessFrameworkReferences.cs
index 02d50f672f2f..79b9e340942f 100644
--- a/src/Tasks/Microsoft.NET.Build.Tasks/ProcessFrameworkReferences.cs
+++ b/src/Tasks/Microsoft.NET.Build.Tasks/ProcessFrameworkReferences.cs
@@ -1,4 +1,4 @@
-// Licensed to the .NET Foundation under one or more agreements.
+// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
@@ -15,6 +15,7 @@
using Microsoft.NET.Sdk.WorkloadManifestReader;
using Newtonsoft.Json;
using NuGet.Frameworks;
+using NuGet.Versioning;
namespace Microsoft.NET.Build.Tasks
{
@@ -729,8 +730,11 @@ private ToolPackSupport AddToolPack(
}
// Before net8.0, ILLink analyzers shipped in a separate package.
- // Add the analyzer package with version taken from KnownILLinkPack.
- if (normalizedTargetFrameworkVersion < new Version(8, 0) && toolPackType is ToolPackType.ILLink)
+ // Add the analyzer package with version taken from KnownILLinkPack if the version is less than 8.0.0.
+ // The version comparison doesn't consider prerelease labels, so 8.0.0-foo will be considered equal to 8.0.0 and
+ // will not get the extra analyzer package reference.
+ if (toolPackType is ToolPackType.ILLink &&
+ new VersionComparer(VersionComparison.Version).Compare(NuGetVersion.Parse(packVersion), new NuGetVersion(8, 0, 0)) < 0)
{
var analyzerPackage = new TaskItem("Microsoft.NET.ILLink.Analyzers");
analyzerPackage.SetMetadata(MetadataKeys.Version, packVersion);
diff --git a/src/Tests/Microsoft.NET.Publish.Tests/GivenThatWeWantToRunILLink.cs b/src/Tests/Microsoft.NET.Publish.Tests/GivenThatWeWantToRunILLink.cs
index 14f7a7105f2e..77f09877abc1 100644
--- a/src/Tests/Microsoft.NET.Publish.Tests/GivenThatWeWantToRunILLink.cs
+++ b/src/Tests/Microsoft.NET.Publish.Tests/GivenThatWeWantToRunILLink.cs
@@ -161,6 +161,26 @@ public void ILLink_fails_when_no_matching_pack_is_found(string targetFramework)
.And.HaveStdOutContaining("error NETSDK1195");
}
+ [RequiresMSBuildVersionTheory("17.0.0.32901")]
+ // TODO: enable netstandard2.0 once ProcessFrameworkReferences is fixed to run
+ // when tool packs are referenced. See https://github.com/dotnet/sdk/pull/33062.
+ // Currently netstandard2.0 skips ProcessFrameworkReference because FrameworkReference
+ // is empty.
+ // [InlineData("netstandard2.0")]
+ [InlineData("netstandard2.1")]
+ public void ILLink_can_use_latest_with_unsupported_target_framework(string targetFramework)
+ {
+ var projectName = "TrimmableNetstandardLibrary";
+ var testAsset = _testAssetsManager.CopyTestAsset(projectName)
+ .WithSource()
+ .WithTargetFramework(targetFramework);
+
+ var buildCommand = new BuildCommand(testAsset);
+ buildCommand.Execute("/p:IsTrimmable=true")
+ .Should().Pass()
+ .And.HaveStdOutContaining("warning IL2026");
+ }
+
[RequiresMSBuildVersionTheory("17.0.0.32901")]
[MemberData(nameof(SupportedTfms), MemberType = typeof(PublishTestUtils))]
public void PrepareForILLink_can_set_IsTrimmable(string targetFramework)
diff --git a/src/Tests/Microsoft.NET.TestFramework/TestAsset.cs b/src/Tests/Microsoft.NET.TestFramework/TestAsset.cs
index a30bbb04deee..fb01ed4e293f 100644
--- a/src/Tests/Microsoft.NET.TestFramework/TestAsset.cs
+++ b/src/Tests/Microsoft.NET.TestFramework/TestAsset.cs
@@ -100,7 +100,13 @@ public TestAsset WithSource()
File.Copy(srcFile, destFile, true);
}
- string[][] Properties = { new string[] { "TargetFramework", "$(CurrentTargetFramework)", ToolsetInfo.CurrentTargetFramework }, new string[] { "RuntimeIdentifier", "$(LatestWinRuntimeIdentifier)", ToolsetInfo.LatestWinRuntimeIdentifier }, new string[] { "RuntimeIdentifier", "$(LatestLinuxRuntimeIdentifier)", ToolsetInfo.LatestLinuxRuntimeIdentifier }, new string[] { "RuntimeIdentifier", "$(LatestMacRuntimeIdentifier)", ToolsetInfo.LatestMacRuntimeIdentifier }, new string[] { "RuntimeIdentifier", "$(LatestRuntimeIdentifiers)", ToolsetInfo.LatestRuntimeIdentifiers } };
+ string[][] Properties = {
+ new string[] { "TargetFramework", "$(CurrentTargetFramework)", ToolsetInfo.CurrentTargetFramework },
+ new string[] { "CurrentTargetFramework", "$(CurrentTargetFramework)", ToolsetInfo.CurrentTargetFramework },
+ new string[] { "RuntimeIdentifier", "$(LatestWinRuntimeIdentifier)", ToolsetInfo.LatestWinRuntimeIdentifier },
+ new string[] { "RuntimeIdentifier", "$(LatestLinuxRuntimeIdentifier)", ToolsetInfo.LatestLinuxRuntimeIdentifier },
+ new string[] { "RuntimeIdentifier", "$(LatestMacRuntimeIdentifier)", ToolsetInfo.LatestMacRuntimeIdentifier },
+ new string[] { "RuntimeIdentifier", "$(LatestRuntimeIdentifiers)", ToolsetInfo.LatestRuntimeIdentifiers } };
foreach (string[] property in Properties)
{