Skip to content

Commit

Permalink
Add more trimming options to TrimMode and change defaults (#2856)
Browse files Browse the repository at this point in the history
The new default for trimming is equivalent to TrimmerDefaultAction=link.
Since we're trying to stay away from the term link and TrimmerDefaultAction
isn't a very clear name, this change repurposes TrimMode.

The two new TrimModes are 'full' and 'partial'. The new default is equivalent
to 'TrimMode=full'. These new modes are also recognized for earlier TFMs, but
the defaults are only changed for net7.0+.
  • Loading branch information
agocke authored Jul 7, 2022
1 parent 2898eba commit b6f1fb4
Show file tree
Hide file tree
Showing 4 changed files with 98 additions and 34 deletions.
12 changes: 9 additions & 3 deletions src/ILLink.Tasks/ILLink.Tasks.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,17 @@
</ItemGroup>

<ItemGroup>
<Content Include="sdk/Sdk.props" PackagePath="Sdk/" />
<Content Include="build/$(PackageId).props" PackagePath="build/" />
<Content Include="sdk/Sdk.props" PackagePath="Sdk/">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="build/$(PackageId).props" PackagePath="build/">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<!-- Note: this should not match the package name, because we don't want the targets
to be imported by nuget. The SDK will import them in the right order. -->
<Content Include="build/Microsoft.NET.ILLink.targets" PackagePath="build/" />
<Content Include="build/Microsoft.NET.ILLink.targets" PackagePath="build/">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>

<ItemGroup>
Expand Down
42 changes: 32 additions & 10 deletions src/ILLink.Tasks/LinkTask.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ public class ILLink : ToolTask
public ITaskItem OutputDirectory { get; set; }

/// <summary>
/// The subset of warnings that have to be turned off.
/// The subset of warnings that have to be turned off.
/// Maps to '--nowarn'.
/// </summary>
public string NoWarn { get; set; }
Expand Down Expand Up @@ -206,6 +206,7 @@ public class ILLink : ToolTask
/// <summary>
/// Sets the default action for assemblies which have not opted into trimming.
/// Maps to '--action'
/// </summary>
public string DefaultAction { get; set; }

/// <summary>
Expand Down Expand Up @@ -318,6 +319,23 @@ protected override string GenerateResponseFileCommands ()
args.AppendLine ("--singlewarn-");
}

string trimMode = TrimMode switch {
"full" => "link",
"partial" => "link",
var x => x
};
if (trimMode != null)
args.Append ("--trim-mode ").AppendLine (trimMode);

string defaultAction = TrimMode switch {
"full" => "link",
"partial" => "copy",
_ => DefaultAction
};
if (defaultAction != null)
args.Append ("--action ").AppendLine (defaultAction);


HashSet<string> assemblyNames = new HashSet<string> (StringComparer.OrdinalIgnoreCase);
foreach (var assembly in AssemblyPaths) {
var assemblyPath = assembly.ItemSpec;
Expand All @@ -329,10 +347,20 @@ protected override string GenerateResponseFileCommands ()

args.Append ("-reference ").AppendLine (Quote (assemblyPath));

string trimMode = assembly.GetMetadata ("TrimMode");
if (!String.IsNullOrEmpty (trimMode)) {
string assemblyTrimMode = assembly.GetMetadata ("TrimMode");
string isTrimmable = assembly.GetMetadata ("IsTrimmable");
if (string.IsNullOrEmpty (assemblyTrimMode)) {
if (isTrimmable.Equals ("true", StringComparison.OrdinalIgnoreCase)) {
// isTrimmable ~= true
assemblyTrimMode = trimMode;
} else if (isTrimmable.Equals ("false", StringComparison.OrdinalIgnoreCase)) {
// isTrimmable ~= false
assemblyTrimMode = "copy";
}
}
if (!string.IsNullOrEmpty (assemblyTrimMode)) {
args.Append ("--action ");
args.Append (trimMode);
args.Append (assemblyTrimMode);
args.Append (' ').AppendLine (Quote (assemblyName));
}

Expand Down Expand Up @@ -447,12 +475,6 @@ protected override string GenerateResponseFileCommands ()
if (_removeSymbols == false)
args.AppendLine ("-b");

if (TrimMode != null)
args.Append ("--trim-mode ").AppendLine (TrimMode);

if (DefaultAction != null)
args.Append ("--action ").AppendLine (DefaultAction);

if (CustomSteps != null) {
foreach (var customStep in CustomSteps) {
args.Append ("--custom-step ");
Expand Down
39 changes: 18 additions & 21 deletions src/ILLink.Tasks/build/Microsoft.NET.ILLink.targets
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@ Copyright (c) .NET Foundation. All rights reserved.
<PropertyGroup Condition="'$(SuppressTrimAnalysisWarnings)' == '' And '$(PublishTrimmed)' == 'true' And '$(EnableTrimAnalyzer)' != 'true'">
<!-- Trim analysis warnings are suppressed for .NET < 6. -->
<SuppressTrimAnalysisWarnings Condition="$([MSBuild]::VersionLessThan('$(TargetFrameworkVersion)', '6.0'))">true</SuppressTrimAnalysisWarnings>
<!-- Suppress for WPF/WinForms (unless linking everything) -->
<SuppressTrimAnalysisWarnings Condition="'$(TrimmerDefaultAction)' != 'link' And ('$(UseWpf)' == 'true' Or '$(UseWindowsForms)' == 'true')">true</SuppressTrimAnalysisWarnings>
<!-- Suppress for WPF/WinForms -->
<SuppressTrimAnalysisWarnings Condition="'$(UseWpf)' == 'true' Or '$(UseWindowsForms)' == 'true'">true</SuppressTrimAnalysisWarnings>
<!-- Otherwise, for .NET 6+, warnings are on by default -->
<SuppressTrimAnalysisWarnings Condition="'$(SuppressTrimAnalysisWarnings)' == ''">false</SuppressTrimAnalysisWarnings>
</PropertyGroup>
Expand Down Expand Up @@ -159,7 +159,7 @@ Copyright (c) .NET Foundation. All rights reserved.
ReferenceAssemblyPaths="@(ReferencePath)"
RootAssemblyNames="@(TrimmerRootAssembly)"
TrimMode="$(TrimMode)"
DefaultAction="$(TrimmerDefaultAction)"
DefaultAction="$(_TrimmerDefaultAction)"
RemoveSymbols="$(TrimmerRemoveSymbols)"
FeatureSettings="@(_TrimmerFeatureSettings)"
CustomData="@(_TrimmerCustomData)"
Expand Down Expand Up @@ -226,16 +226,23 @@ Copyright (c) .NET Foundation. All rights reserved.
<ILLinkWarningLevel Condition=" '$(ILLinkWarningLevel)' == '' ">0</ILLinkWarningLevel>
</PropertyGroup>

<!-- Defaults for .NET < 6 -->
<PropertyGroup Condition=" $([MSBuild]::VersionLessThan('$(TargetFrameworkVersion)', '6.0')) ">
<TrimMode Condition=" '$(TrimMode)' == '' ">copyused</TrimMode>
<!-- Action is the same regardless of whether the assembly has an IsTrimmable attribute. (The attribute didn't exist until .NET 6.) -->
<TrimmerDefaultAction>$(TrimMode)</TrimmerDefaultAction>
<!-- In .NET 7, TrimmerDefaultAction is deprecated. TrimMode can be used for the supported configurations. -->
<Warning Condition="'$(TrimmerDefaultAction)' != '' And $([MSBuild]::VersionGreaterThan('$(TargetFrameworkVersion)', '6.0'))"
Text="Property 'TrimmerDefaultAction' is deprecated in .NET 7 and will be ignored. Use TrimMode instead." />

<!-- Set up TrimMode and TrimmerDefaultAction. -->
<PropertyGroup>
<TrimMode Condition="'$(TrimMode)' == '' And $([MSBuild]::VersionLessThan($(TargetFrameworkVersion), '6.0'))">copyused</TrimMode>
<TrimMode Condition="'$(TrimMode)' == '' And $([MSBuild]::VersionEquals($(TargetFrameworkVersion), '6.0'))">partial</TrimMode>
<TrimMode Condition="'$(TrimMode)' == ''">full</TrimMode>

<!-- Set TrimmerDefaultAction for compat in < 7.0 -->
<_TrimmerDefaultAction Condition="$([MSBuild]::VersionLessThan($(TargetFrameworkVersion), '7.0'))">$(TrimmerDefaultAction)</_TrimmerDefaultAction>
<_TrimmerDefaultAction Condition="'$(_TrimmerDefaultAction)' == '' And $([MSBuild]::VersionLessThan($(TargetFrameworkVersion), '6.0'))">$(TrimMode)</_TrimmerDefaultAction>
<_TrimmerDefaultAction Condition="'$(_TrimmerDefaultAction)' == '' And $([MSBuild]::VersionEquals($(TargetFrameworkVersion), '6.0'))">copy</_TrimmerDefaultAction>
</PropertyGroup>

<PropertyGroup>
<TrimMode Condition=" '$(TrimMode)' == '' ">link</TrimMode>
<!-- For .NET 6+, assemblies without IsTrimmable attribute get the "copy" action. -->
<TrimmerDefaultAction Condition=" '$(TrimmerDefaultAction)' == '' ">copy</TrimmerDefaultAction>
<ILLinkTreatWarningsAsErrors Condition=" '$(ILLinkTreatWarningsAsErrors)' == '' ">$(TreatWarningsAsErrors)</ILLinkTreatWarningsAsErrors>
<_ExtraTrimmerArgs>--skip-unresolved true $(_ExtraTrimmerArgs)</_ExtraTrimmerArgs>
<TrimmerSingleWarn Condition=" '$(TrimmerSingleWarn)' == '' ">true</TrimmerSingleWarn>
Expand Down Expand Up @@ -296,16 +303,6 @@ Copyright (c) .NET Foundation. All rights reserved.
</ManagedAssemblyToLink>
</ItemGroup>

<!-- In .NET6+, set the action explicitly for any with IsTrimmable MSBuild metadata -->
<ItemGroup Condition=" $([MSBuild]::VersionGreaterThanOrEquals('$(TargetFrameworkVersion)', '6.0')) ">
<ManagedAssemblyToLink Condition=" '%(ManagedAssemblyToLink.IsTrimmable)' == 'true' And '%(ManagedAssemblyToLink.TrimMode)' == '' ">
<TrimMode>$(TrimMode)</TrimMode>
</ManagedAssemblyToLink>
<ManagedAssemblyToLink Condition=" '%(ManagedAssemblyToLink.IsTrimmable)' == 'false' And '%(ManagedAssemblyToLink.TrimMode)' == '' ">
<TrimMode>$(TrimmerDefaultAction)</TrimMode>
</ManagedAssemblyToLink>
</ItemGroup>

<ItemGroup>
<!-- Don't collapse to a single warning for the intermediate assembly.
This just sets metadata on the items in ManagedAssemblyToLink that came from IntermediateAssembly. -->
Expand Down
39 changes: 39 additions & 0 deletions test/ILLink.Tasks.Tests/ILLink.Tasks.Tests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,45 @@ public class TaskArgumentTests
}
};

[Theory]
[InlineData ("full", AssemblyAction.Link)]
[InlineData ("partial", AssemblyAction.Copy)]
public void TrimModeFullAndPartial (string trimMode, AssemblyAction expectedDefaultAction)
{
var task = new MockTask () {
TrimMode = trimMode
};
using (var driver = task.CreateDriver ()) {
Assert.Equal (AssemblyAction.Link, driver.Context.TrimAction);
Assert.Equal (expectedDefaultAction, driver.Context.DefaultAction);
}
}

[Theory]
[InlineData ("full")]
[InlineData ("partial")]
public void TrimModeAssemblyPaths (string trimMode)
{
var assemblyPaths = new ITaskItem[] {
new TaskItem("Assembly1.dll", new Dictionary<string, string> {{ "IsTrimmable", "true" }}),
new TaskItem("Assembly2.dll", new Dictionary<string, string> ()),
new TaskItem("Assembly3.dll", new Dictionary<string, string> {{ "IsTrimmable", "false" }}),
};
var task = new MockTask () {
TrimMode = trimMode,
AssemblyPaths = assemblyPaths
};
using var driver = task.CreateDriver ();
var context = driver.Context;
var references = driver.GetReferenceAssemblies ();
Assert.Equal ("", assemblyPaths[0].GetMetadata ("TrimMode"));
Assert.Equal (AssemblyAction.Link, context.Actions["Assembly1"]);
Assert.Equal ("", assemblyPaths[1].GetMetadata ("TrimMode"));
Assert.False (context.Actions.ContainsKey ("Assembly2"));
Assert.Equal ("", assemblyPaths[2].GetMetadata ("TrimMode"));
Assert.Equal (AssemblyAction.Copy, context.Actions["Assembly3"]);
}

[Theory]
[MemberData (nameof (AssemblyPathsCases))]
public void TestAssemblyPaths (ITaskItem[] assemblyPaths)
Expand Down

0 comments on commit b6f1fb4

Please sign in to comment.