Skip to content

Commit d1264e2

Browse files
authored
Allow using latest illink analyzer on netstandard projects (#33158)
Before net8.0, the illink analyzer shipped as a separate package from ILLink.Tasks. #32045 added logic to reference the separate package for tfms less than net8.0. This change tweaks the logic to be based on the package version resolved from KnownILLinkPack, rather than the tfm. This makes it possible to define KnownILLinkPack to pull in the latest analyzer, as a workaround when targeting unsupported TFMs. The scenario is still considered unsupported, but this at least makes it possible to work around. A new testcase shows the steps necessary to use the latest illink analyzer on a netstandard2.x project.
1 parent 80181b5 commit d1264e2

File tree

6 files changed

+111
-4
lines changed

6 files changed

+111
-4
lines changed
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
namespace System.Diagnostics.CodeAnalysis
5+
{
6+
/// <summary>
7+
/// Indicates that the specified method requires dynamic access to code that is not referenced
8+
/// statically, for example through <see cref="Reflection"/>.
9+
/// </summary>
10+
/// <remarks>
11+
/// This allows tools to understand which methods are unsafe to call when removing unreferenced
12+
/// code from an application.
13+
/// </remarks>
14+
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Class, Inherited = false)]
15+
internal sealed class RequiresUnreferencedCodeAttribute : Attribute
16+
{
17+
/// <summary>
18+
/// Initializes a new instance of the <see cref="RequiresUnreferencedCodeAttribute"/> class
19+
/// with the specified message.
20+
/// </summary>
21+
/// <param name="message">
22+
/// A message that contains information about the usage of unreferenced code.
23+
/// </param>
24+
public RequiresUnreferencedCodeAttribute(string message)
25+
{
26+
Message = message;
27+
}
28+
29+
/// <summary>
30+
/// Gets a message that contains information about the usage of unreferenced code.
31+
/// </summary>
32+
public string Message { get; }
33+
34+
/// <summary>
35+
/// Gets or sets an optional URL that contains more information about the method,
36+
/// why it requires unreferenced code, and what options a consumer has to deal with it.
37+
/// </summary>
38+
public string? Url { get; set; }
39+
}
40+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
using System.Diagnostics.CodeAnalysis;
2+
3+
class TrimmableNetStandardLibrary
4+
{
5+
public static void EntryPoint()
6+
{
7+
RequiresUnreferencedCode();
8+
}
9+
10+
11+
[RequiresUnreferencedCode("Requires unreferenced code")]
12+
static void RequiresUnreferencedCode()
13+
{
14+
}
15+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), testAsset.props))\testAsset.props" />
3+
4+
<PropertyGroup>
5+
<!-- Test infra replaces this with the value of CurrentTargetFramework so we can compare against it below. -->
6+
<CurrentTargetFramework>$(CurrentTargetFramework)</CurrentTargetFramework>
7+
<TargetFramework>netstandard2.0</TargetFramework>
8+
<IsTrimmable>true</IsTrimmable>
9+
<LangVersion>latest</LangVersion>
10+
<Nullable>enable</Nullable>
11+
<AnalysisLevel>latest</AnalysisLevel>
12+
</PropertyGroup>
13+
14+
<Target Name="_AddKnownILLinkPack" BeforeTargets="ProcessFrameworkReferences">
15+
<ItemGroup>
16+
<KnownILLinkPack Include="@(KnownILLinkPack)"
17+
Condition="'%(TargetFramework)' == '$(CurrentTargetFramework)'"
18+
TargetFramework="$(TargetFramework)" />
19+
</ItemGroup>
20+
</Target>
21+
22+
</Project>

src/Tasks/Microsoft.NET.Build.Tasks/ProcessFrameworkReferences.cs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Licensed to the .NET Foundation under one or more agreements.
1+
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

44
using System;
@@ -15,6 +15,7 @@
1515
using Microsoft.NET.Sdk.WorkloadManifestReader;
1616
using Newtonsoft.Json;
1717
using NuGet.Frameworks;
18+
using NuGet.Versioning;
1819

1920
namespace Microsoft.NET.Build.Tasks
2021
{
@@ -746,8 +747,11 @@ private ToolPackSupport AddToolPack(
746747
}
747748

748749
// Before net8.0, ILLink analyzers shipped in a separate package.
749-
// Add the analyzer package with version taken from KnownILLinkPack.
750-
if (normalizedTargetFrameworkVersion < new Version(8, 0) && toolPackType is ToolPackType.ILLink)
750+
// Add the analyzer package with version taken from KnownILLinkPack if the version is less than 8.0.0.
751+
// The version comparison doesn't consider prerelease labels, so 8.0.0-foo will be considered equal to 8.0.0 and
752+
// will not get the extra analyzer package reference.
753+
if (toolPackType is ToolPackType.ILLink &&
754+
new VersionComparer(VersionComparison.Version).Compare(NuGetVersion.Parse(packVersion), new NuGetVersion(8, 0, 0)) < 0)
751755
{
752756
var analyzerPackage = new TaskItem("Microsoft.NET.ILLink.Analyzers");
753757
analyzerPackage.SetMetadata(MetadataKeys.Version, packVersion);

src/Tests/Microsoft.NET.Publish.Tests/GivenThatWeWantToRunILLink.cs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,26 @@ public void ILLink_fails_when_no_matching_pack_is_found(string targetFramework)
161161
.And.HaveStdOutContaining("error NETSDK1195");
162162
}
163163

164+
[RequiresMSBuildVersionTheory("17.0.0.32901")]
165+
// TODO: enable netstandard2.0 once ProcessFrameworkReferences is fixed to run
166+
// when tool packs are referenced. See https://github.com/dotnet/sdk/pull/33062.
167+
// Currently netstandard2.0 skips ProcessFrameworkReference because FrameworkReference
168+
// is empty.
169+
// [InlineData("netstandard2.0")]
170+
[InlineData("netstandard2.1")]
171+
public void ILLink_can_use_latest_with_unsupported_target_framework(string targetFramework)
172+
{
173+
var projectName = "TrimmableNetstandardLibrary";
174+
var testAsset = _testAssetsManager.CopyTestAsset(projectName)
175+
.WithSource()
176+
.WithTargetFramework(targetFramework);
177+
178+
var buildCommand = new BuildCommand(testAsset);
179+
buildCommand.Execute("/p:IsTrimmable=true")
180+
.Should().Pass()
181+
.And.HaveStdOutContaining("warning IL2026");
182+
}
183+
164184
[RequiresMSBuildVersionTheory("17.0.0.32901")]
165185
[MemberData(nameof(SupportedTfms), MemberType = typeof(PublishTestUtils))]
166186
public void PrepareForILLink_can_set_IsTrimmable(string targetFramework)

src/Tests/Microsoft.NET.TestFramework/TestAsset.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,13 @@ public TestAsset WithSource()
100100
File.Copy(srcFile, destFile, true);
101101
}
102102

103-
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 } };
103+
string[][] Properties = {
104+
new string[] { "TargetFramework", "$(CurrentTargetFramework)", ToolsetInfo.CurrentTargetFramework },
105+
new string[] { "CurrentTargetFramework", "$(CurrentTargetFramework)", ToolsetInfo.CurrentTargetFramework },
106+
new string[] { "RuntimeIdentifier", "$(LatestWinRuntimeIdentifier)", ToolsetInfo.LatestWinRuntimeIdentifier },
107+
new string[] { "RuntimeIdentifier", "$(LatestLinuxRuntimeIdentifier)", ToolsetInfo.LatestLinuxRuntimeIdentifier },
108+
new string[] { "RuntimeIdentifier", "$(LatestMacRuntimeIdentifier)", ToolsetInfo.LatestMacRuntimeIdentifier },
109+
new string[] { "RuntimeIdentifier", "$(LatestRuntimeIdentifiers)", ToolsetInfo.LatestRuntimeIdentifiers } };
104110

105111
foreach (string[] property in Properties)
106112
{

0 commit comments

Comments
 (0)