Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions eng/Version.Details.xml
Original file line number Diff line number Diff line change
Expand Up @@ -125,19 +125,19 @@
<Uri>https://github.com/microsoft/vstest</Uri>
<Sha>b1452a516a80672fa814f4ebdf2151c0875d0b20</Sha>
</Dependency>
<Dependency Name="Microsoft.NET.ILLink.Tasks" Version="7.0.100-1.22354.1">
<Dependency Name="Microsoft.NET.ILLink.Tasks" Version="7.0.100-1.22357.2">
<Uri>https://github.com/dotnet/linker</Uri>
<Sha>ef2d0f25b72469b55925251a79f12bcbf98644bf</Sha>
<Sha>b6f1fb4d67f6aab530382e1973b898ba858709e0</Sha>
<SourceBuild RepoName="linker" ManagedOnly="true" />
</Dependency>
<Dependency Name="Microsoft.DotNet.ILCompiler" Version="7.0.0-preview.6.22356.1">
<Uri>https://github.com/dotnet/runtime</Uri>
<Sha>206dccb7945aaa3f26599fbe742de9022ca7ef91</Sha>
<SourceBuildTarball RepoName="runtime" ManagedOnly="true" />
</Dependency>
<Dependency Name="Microsoft.NET.ILLink.Analyzers" Version="7.0.100-1.22354.1">
<Dependency Name="Microsoft.NET.ILLink.Analyzers" Version="7.0.100-1.22357.2">
<Uri>https://github.com/dotnet/linker</Uri>
<Sha>ef2d0f25b72469b55925251a79f12bcbf98644bf</Sha>
<Sha>b6f1fb4d67f6aab530382e1973b898ba858709e0</Sha>
</Dependency>
<Dependency Name="System.CodeDom" Version="7.0.0-preview.6.22356.1">
<Uri>https://github.com/dotnet/runtime</Uri>
Expand Down
2 changes: 1 addition & 1 deletion eng/Versions.props
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@
</PropertyGroup>
<PropertyGroup>
<!-- Dependencies from https://github.com/mono/linker -->
<MicrosoftNETILLinkTasksPackageVersion>7.0.100-1.22354.1</MicrosoftNETILLinkTasksPackageVersion>
<MicrosoftNETILLinkTasksPackageVersion>7.0.100-1.22357.2</MicrosoftNETILLinkTasksPackageVersion>
<MicrosoftNETILLinkAnalyzerPackageVersion>$(MicrosoftNETILLinkTasksPackageVersion)</MicrosoftNETILLinkAnalyzerPackageVersion>
</PropertyGroup>
<PropertyGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ Copyright (c) .NET Foundation. All rights reserved.

<!-- Trimmer defaults -->
<PublishTrimmed Condition="'$(PublishTrimmed)' == ''">true</PublishTrimmed>
<TrimMode Condition="'$(TrimMode)' == ''">link</TrimMode>
<TrimMode Condition="'$(TrimMode)' == ''">partial</TrimMode>
<TrimmerRemoveSymbols Condition="'$(TrimmerRemoveSymbols)' == ''">false</TrimmerRemoveSymbols>

<!-- Static web assets defaults -->
Expand Down
82 changes: 60 additions & 22 deletions src/Tests/Microsoft.NET.Build.Tests/ReferenceExeTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ public ReferenceExeTests(ITestOutputHelper log) : base(log)

private bool TestWithPublish { get; set; } = false;

private bool PublishTrimmed = false;

private bool ReferenceExeInCode = false;

private TestProject MainProject { get; set; }

private TestProject ReferencedProject { get; set; }
Expand All @@ -44,10 +48,31 @@ private void CreateProjects()
};

MainProject.PackageReferences.Add(new TestPackageReference("Humanizer", "2.8.26"));
MainProject.SourceFiles["Program.cs"] = @"using Humanizer; System.Console.WriteLine(""MainProject"".Humanize());";
var mainProjectSrc = @"
using System;
using Humanizer;
Console.WriteLine(""MainProject"".Humanize());";

if (PublishTrimmed)
{
MainProject.AdditionalProperties["PublishTrimmed"] = "true";

// If we're fully trimming, unless the trimmed project contains an explicit reference in code
// to the referenced project, it will get trimmed away
if (ReferenceExeInCode)
{
mainProjectSrc += @"
// Always false, but the trimmer doesn't know that
if (string.Empty.Length > 0)
{
ReferencedExeProgram.Main();
}";

}
}

// By default we don't create the app host on Mac for FDD. For these tests, we want to create it everywhere
MainProject.AdditionalProperties["UseAppHost"] = "true";

MainProject.SourceFiles["Program.cs"] = mainProjectSrc;

if (MainSelfContained)
{
Expand All @@ -62,16 +87,22 @@ private void CreateProjects()
IsExe = true,
};

ReferencedProject.AdditionalProperties["UseAppHost"] = "true";

if (ReferencedSelfContained)
{
ReferencedProject.RuntimeIdentifier = EnvironmentInfo.GetCompatibleRid();
}

// Use a lower version of a library in the referenced project
ReferencedProject.PackageReferences.Add(new TestPackageReference("Humanizer", "2.7.9"));
ReferencedProject.SourceFiles["Program.cs"] = @"using Humanizer; System.Console.WriteLine(""ReferencedProject"".Humanize());";
ReferencedProject.SourceFiles["Program.cs"] = @"
using Humanizer;
public class ReferencedExeProgram
{
public static void Main()
{
System.Console.WriteLine(""ReferencedProject"".Humanize());
}
}";

MainProject.ReferencedProjects.Add(ReferencedProject);
}
Expand Down Expand Up @@ -122,11 +153,23 @@ private void RunTest(string buildFailureCode = null, [CallerMemberName] string c
var referencedExeResult = new RunExeCommand(Log, referencedExePath)
.Execute();

referencedExeResult
.Should()
.Pass()
.And
.HaveStdOut("Referenced project");
// If we're trimming and didn't reference the exe in source we would expect it to be trimmed from the output
if (PublishTrimmed && !ReferenceExeInCode)
{
referencedExeResult
.Should()
.Fail()
.And
.HaveStdErrContaining("The application to execute does not exist");
}
else
{
referencedExeResult
.Should()
.Pass()
.And
.HaveStdOut("Referenced project");
}
}
else
{
Expand Down Expand Up @@ -238,25 +281,20 @@ public void ReferencedExeCanRunWhenPublished(bool selfContained)
RunTest();
}

[Fact]
public void ReferencedExeCanRunWhenPublishedWithTrimming()
[Theory]
[InlineData(true)]
[InlineData(false)]
public void ReferencedExeCanRunWhenPublishedWithTrimming(bool referenceExeInCode)
{
MainSelfContained = true;
ReferencedSelfContained = true;

TestWithPublish = true;
PublishTrimmed = true;
ReferenceExeInCode = referenceExeInCode;

CreateProjects();

if (MainSelfContained)
{
MainProject.AdditionalProperties["PublishTrimmed"] = "True";
}
if (ReferencedSelfContained)
{
ReferencedProject.AdditionalProperties["PublishTrimmed"] = "True";
}

RunTest();
}

Expand Down
107 changes: 101 additions & 6 deletions src/Tests/Microsoft.NET.Publish.Tests/GivenThatWeWantToRunILLink.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
using Xunit;
using Xunit.Abstractions;
using static Microsoft.NET.Publish.Tests.PublishTestUtils;
using System.Security.Permissions;

namespace Microsoft.NET.Publish.Tests
{
Expand Down Expand Up @@ -187,6 +188,10 @@ public void PrepareForILLink_can_set_TrimMode(string targetFramework)
[RequiresMSBuildVersionTheory("17.0.0.32901")]
[InlineData("net5.0", "link")]
[InlineData(ToolsetInfo.CurrentTargetFramework, "copyused")]
[InlineData("net6.0", "full")]
[InlineData(ToolsetInfo.CurrentTargetFramework, "full")]
[InlineData("net6.0", "partial")]
[InlineData(ToolsetInfo.CurrentTargetFramework, "partial")]
public void ILLink_respects_global_TrimMode(string targetFramework, string trimMode)
{
var projectName = "HelloWorld";
Expand All @@ -210,7 +215,7 @@ public void ILLink_respects_global_TrimMode(string targetFramework, string trimM
File.Exists(publishedDll).Should().BeTrue();
File.Exists(isTrimmableDll).Should().BeTrue();
DoesImageHaveMethod(isTrimmableDll, "UnusedMethodToRoot").Should().BeTrue();
if (trimMode == "link") {
if (trimMode is "link" or "full" or "partial") {
// Check that the assembly was trimmed at the member level
DoesImageHaveMethod(isTrimmableDll, "UnusedMethod").Should().BeFalse();
} else {
Expand Down Expand Up @@ -269,7 +274,8 @@ public void ILLink_respects_TrimmableAssembly(string targetFramework)
}

[RequiresMSBuildVersionTheory("17.0.0.32901")]
[MemberData(nameof(Net6Plus), MemberType = typeof(PublishTestUtils))]
[InlineData("net6.0")]
[InlineData("net7.0")]
public void ILLink_respects_IsTrimmable_attribute(string targetFramework)
{
string projectName = "HelloWorld";
Expand All @@ -287,7 +293,16 @@ public void ILLink_respects_IsTrimmable_attribute(string targetFramework)

// Only unused non-trimmable assemblies are kept
File.Exists(unusedTrimmableDll).Should().BeFalse();
DoesImageHaveMethod(unusedNonTrimmableDll, "UnusedMethod").Should().BeTrue();
if (targetFramework == "net6.0")
{
// In net6.0 the default is to keep assemblies not marked trimmable
DoesImageHaveMethod(unusedNonTrimmableDll, "UnusedMethod").Should().BeTrue();
}
else
{
// In net7.0+ the default is to keep assemblies not marked trimmable
File.Exists(unusedNonTrimmableDll).Should().BeFalse();
}
}

[RequiresMSBuildVersionTheory("17.0.0.32901")]
Expand All @@ -298,6 +313,7 @@ public void ILLink_IsTrimmable_metadata_can_override_attribute(string targetFram
var rid = EnvironmentInfo.GetCompatibleRid(targetFramework);
var testProject = CreateTestProjectWithIsTrimmableAttributes(targetFramework, projectName);
var testAsset = _testAssetsManager.CreateTestProject(testProject, identifier: targetFramework)
.WithProjectChanges(project => SetGlobalTrimMode(project, "partial"))
.WithProjectChanges(project => SetMetadata(project, "UnusedTrimmableAssembly", "IsTrimmable", "false"))
.WithProjectChanges(project => SetMetadata(project, "UnusedNonTrimmableAssembly", "IsTrimmable", "true"));

Expand All @@ -316,7 +332,7 @@ public void ILLink_IsTrimmable_metadata_can_override_attribute(string targetFram
}

[RequiresMSBuildVersionTheory("17.0.0.32901")]
[MemberData(nameof(Net6Plus), MemberType = typeof(PublishTestUtils))]
[InlineData("net6.0")]
public void ILLink_TrimMode_applies_to_IsTrimmable_assemblies(string targetFramework)
{
string projectName = "HelloWorld";
Expand All @@ -343,6 +359,47 @@ public void ILLink_TrimMode_applies_to_IsTrimmable_assemblies(string targetFrame
DoesImageHaveMethod(unusedNonTrimmableDll, "UnusedMethod").Should().BeTrue();
}

[RequiresMSBuildVersionTheory("17.0.0.32901")]
[InlineData(ToolsetInfo.CurrentTargetFramework, "full")]
[InlineData(ToolsetInfo.CurrentTargetFramework, "partial")]
public void ILLink_TrimMode_new_options(string targetFramework, string trimMode)
{
string projectName = "HelloWorld";
var rid = EnvironmentInfo.GetCompatibleRid(targetFramework);
var testProject = CreateTestProjectWithIsTrimmableAttributes(targetFramework, projectName);
var testAsset = _testAssetsManager.CreateTestProject(testProject, identifier: targetFramework + trimMode)
.WithProjectChanges(project => SetGlobalTrimMode(project, trimMode));

var publishCommand = new PublishCommand(testAsset);
publishCommand.Execute($"/p:RuntimeIdentifier={rid}", "-bl").Should().Pass();

var publishDirectory = publishCommand.GetOutputDirectory(targetFramework: targetFramework, runtimeIdentifier: rid).FullName;

var trimmableDll = Path.Combine(publishDirectory, "TrimmableAssembly.dll");
var nonTrimmableDll = Path.Combine(publishDirectory, "NonTrimmableAssembly.dll");
var unusedTrimmableDll = Path.Combine(publishDirectory, "UnusedTrimmableAssembly.dll");
var unusedNonTrimmableDll = Path.Combine(publishDirectory, "UnusedNonTrimmableAssembly.dll");

// Trimmable assemblies are trimmed at member level
DoesImageHaveMethod(trimmableDll, "UnusedMethod").Should().BeFalse();
DoesImageHaveMethod(trimmableDll, "UsedMethod").Should().BeTrue();
File.Exists(unusedTrimmableDll).Should().BeFalse();
if (trimMode is "full")
{
DoesImageHaveMethod(nonTrimmableDll, "UnusedMethod").Should().BeFalse();
File.Exists(unusedNonTrimmableDll).Should().BeFalse();
}
else if (trimMode is "partial")
{
DoesImageHaveMethod(nonTrimmableDll, "UnusedMethod").Should().BeTrue();
DoesImageHaveMethod(unusedNonTrimmableDll, "UnusedMethod").Should().BeTrue();
}
else
{
Assert.True(false, "unexpected value");
}
}

[RequiresMSBuildVersionTheory("17.0.0.32901")]
[InlineData(ToolsetInfo.CurrentTargetFramework)]
public void ILLink_can_set_TrimmerDefaultAction(string targetFramework)
Expand Down Expand Up @@ -868,8 +925,10 @@ public void ILLink_runs_incrementally(string targetFramework)
}

[RequiresMSBuildVersionTheory("17.0.0.32901")]
[MemberData(nameof(SupportedTfms), MemberType = typeof(PublishTestUtils))]
public void ILLink_defaults_keep_nonframework(string targetFramework)
[InlineData("netcoreapp3.1")]
[InlineData("net5.0")]
[InlineData("net6.0")]
public void ILLink_old_defaults_keep_nonframework(string targetFramework)
{
var projectName = "HelloWorld";
var referenceProjectName = "ClassLibForILLink";
Expand Down Expand Up @@ -903,6 +962,42 @@ public void ILLink_defaults_keep_nonframework(string targetFramework)
DoesDepsFileHaveAssembly(depsFile, unusedFrameworkAssembly).Should().BeFalse();
}

[RequiresMSBuildVersionFact("17.0.0.32901")]
public void ILLink_net7_defaults_trim_nonframework()
{
string targetFramework = "net7.0";
var projectName = "HelloWorld";
var referenceProjectName = "ClassLibForILLink";
var rid = EnvironmentInfo.GetCompatibleRid(targetFramework);

var testProject = CreateTestProjectForILLinkTesting(targetFramework, projectName, referenceProjectName);
var testAsset = _testAssetsManager.CreateTestProject(testProject, identifier: targetFramework);

var publishCommand = new PublishCommand(testAsset);
publishCommand.Execute("/v:n", $"/p:RuntimeIdentifier={rid}", "/p:PublishTrimmed=true").Should().Pass();

var publishDirectory = publishCommand.GetOutputDirectory(targetFramework: targetFramework, runtimeIdentifier: rid).FullName;
var intermediateDirectory = publishCommand.GetIntermediateDirectory(targetFramework: targetFramework, runtimeIdentifier: rid).FullName;
var linkedDirectory = Path.Combine(intermediateDirectory, "linked");

Directory.Exists(linkedDirectory).Should().BeTrue();

var linkedDll = Path.Combine(linkedDirectory, $"{projectName}.dll");
var publishedDll = Path.Combine(publishDirectory, $"{projectName}.dll");
var unusedDll = Path.Combine(publishDirectory, $"{referenceProjectName}.dll");
var unusedFrameworkDll = Path.Combine(publishDirectory, $"{unusedFrameworkAssembly}.dll");

File.Exists(linkedDll).Should().BeTrue();
File.Exists(publishedDll).Should().BeTrue();
File.Exists(unusedDll).Should().BeFalse();
File.Exists(unusedFrameworkDll).Should().BeFalse();

var depsFile = Path.Combine(publishDirectory, $"{projectName}.deps.json");
DoesDepsFileHaveAssembly(depsFile, projectName).Should().BeTrue();
DoesDepsFileHaveAssembly(depsFile, referenceProjectName).Should().BeFalse();
DoesDepsFileHaveAssembly(depsFile, unusedFrameworkAssembly).Should().BeFalse();
}

[RequiresMSBuildVersionTheory("17.0.0.32901")]
[MemberData(nameof(SupportedTfms), MemberType = typeof(PublishTestUtils))]
public void ILLink_does_not_include_leftover_artifacts_on_second_run(string targetFramework)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,9 @@ Copyright (C) Microsoft Corporation. All rights reserved.
<PublishIntermediateOutputPath Condition="'$(DockerPublish)' == 'true'">obj/Docker/publish/</PublishIntermediateOutputPath>

<EFSQLScriptsFolderName Condition="$(EFSQLScriptsFolderName) == ''">EFSQLScripts</EFSQLScriptsFolderName>

<!-- If TrimMode has not already been set, default to partial trimming, as AspNet is not currently fully trim-compatible -->
<TrimMode Condition="'$(PublishTrimmed)' == 'true' and '$(TrimMode)' == ''">partial</TrimMode>
</PropertyGroup>

<!--
Expand Down