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) {