Skip to content

Commit 8768cab

Browse files
authored
Use linker extensibility to enable better trimming (#23512)
* Use linker extensibility to enable better trimming * Configure TrimmerDefaults=link if unspecified * Allow Microsoft.AspNetCore.* and Microsoft.Extensions.* packages to be trimmed. * Make producing the trimmer root descriptor more incremental
1 parent 7f4b846 commit 8768cab

File tree

7 files changed

+65
-24
lines changed

7 files changed

+65
-24
lines changed

global.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
{
22
"sdk": {
3-
"version": "5.0.100-preview.6.20310.4"
3+
"version": "5.0.100-preview.7.20330.3"
44
},
55
"tools": {
6-
"dotnet": "5.0.100-preview.6.20310.4",
6+
"dotnet": "5.0.100-preview.7.20330.3",
77
"runtimes": {
88
"dotnet/x64": [
99
"2.1.18",

src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/Assert.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -519,7 +519,7 @@ private static IEnumerable<string> GetDeclaredTypeNames(string assemblyPath)
519519
{
520520
using (var file = File.OpenRead(assemblyPath))
521521
{
522-
var peReader = new PEReader(file);
522+
using var peReader = new PEReader(file);
523523
var metadataReader = peReader.GetMetadataReader();
524524
return metadataReader.TypeDefinitions.Where(t => !t.IsNil).Select(t =>
525525
{

src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/Wasm/WasmPublishIntegrationTest.cs

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@
88
using Microsoft.AspNetCore.Razor.Tasks;
99
using Microsoft.AspNetCore.Testing;
1010
using Xunit;
11-
using ResourceHashesByNameDictionary = System.Collections.Generic.Dictionary<string, string>;
1211
using static Microsoft.AspNetCore.Razor.Design.IntegrationTests.ServiceWorkerAssert;
12+
using ResourceHashesByNameDictionary = System.Collections.Generic.Dictionary<string, string>;
1313

1414
namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
1515
{
@@ -60,6 +60,8 @@ public async Task Publish_WithDefaultSettings_Works()
6060
serviceWorkerPath: Path.Combine("serviceworkers", "my-service-worker.js"),
6161
serviceWorkerContent: "// This is the production service worker",
6262
assetsManifestPath: "custom-service-worker-assets.js");
63+
64+
VerifyTypeGranularTrimming(result, blazorPublishDirectory);
6365
}
6466

6567
[Fact]
@@ -223,6 +225,10 @@ public async Task Publish_WithTrimmingdDisabled_Works()
223225
serviceWorkerPath: Path.Combine("serviceworkers", "my-service-worker.js"),
224226
serviceWorkerContent: "// This is the production service worker",
225227
assetsManifestPath: "custom-service-worker-assets.js");
228+
229+
// Verify assemblies are not trimmed
230+
var loggingAssemblyPath = Path.Combine(blazorPublishDirectory, "_framework", "Microsoft.Extensions.Logging.Abstractions.dll");
231+
Assert.AssemblyContainsType(result, loggingAssemblyPath, "Microsoft.Extensions.Logging.Abstractions.NullLogger");
226232
}
227233

228234
[Fact]
@@ -309,6 +315,8 @@ public async Task Publish_HostedApp_DefaultSettings_Works()
309315
serviceWorkerPath: Path.Combine("serviceworkers", "my-service-worker.js"),
310316
serviceWorkerContent: "// This is the production service worker",
311317
assetsManifestPath: "custom-service-worker-assets.js");
318+
319+
VerifyTypeGranularTrimming(result, blazorPublishDirectory);
312320
}
313321

314322
[Fact]
@@ -693,6 +701,21 @@ static string ParseWebFormattedHash(string webFormattedHash)
693701
}
694702
}
695703

704+
705+
private void VerifyTypeGranularTrimming(MSBuildResult result, string blazorPublishDirectory)
706+
{
707+
var loggingAssemblyPath = Path.Combine(blazorPublishDirectory, "_framework", "Microsoft.Extensions.Logging.Abstractions.dll");
708+
Assert.FileExists(result, loggingAssemblyPath);
709+
710+
// ILogger is referenced by the app, so we expect it to be preserved
711+
Assert.AssemblyContainsType(result, loggingAssemblyPath, "Microsoft.Extensions.Logging.ILogger");
712+
// LogLevel is referenced by ILogger and therefore must be preserved.
713+
Assert.AssemblyContainsType(result, loggingAssemblyPath, "Microsoft.Extensions.Logging.LogLevel");
714+
715+
// NullLogger is not referenced by the app, and should be trimmed.
716+
Assert.AssemblyDoesNotContainType(result, loggingAssemblyPath, "Microsoft.Extensions.Logging.Abstractions.NullLogger");
717+
}
718+
696719
private static BootJsonData ReadBootJsonData(MSBuildResult result, string path)
697720
{
698721
return JsonSerializer.Deserialize<BootJsonData>(

src/Razor/Microsoft.NET.Sdk.Razor/src/CreateBlazorTrimmerRootDescriptorFile.cs

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
// Copyright (c) .NET Foundation. All rights reserved.
22
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
33

4+
using System;
45
using System.Collections.Generic;
56
using System.IO;
7+
using System.Linq;
68
using System.Xml;
79
using System.Xml.Linq;
810
using Microsoft.Build.Framework;
@@ -21,16 +23,27 @@ public class CreateBlazorTrimmerRootDescriptorFile : Task
2123

2224
public override bool Execute()
2325
{
24-
using var fileStream = File.Create(TrimmerFile.ItemSpec);
26+
var rootDescriptor = CreateRootDescriptorContents();
27+
if (File.Exists(TrimmerFile.ItemSpec))
28+
{
29+
var existing = File.ReadAllText(TrimmerFile.ItemSpec);
30+
31+
if (string.Equals(rootDescriptor, existing, StringComparison.Ordinal))
32+
{
33+
Log.LogMessage(MessageImportance.Low, "Skipping write to file {0} because contents would not change.", TrimmerFile.ItemSpec);
34+
// Avoid writing if the file contents are identical. This is required for build incrementalism.
35+
return !Log.HasLoggedErrors;
36+
}
37+
}
2538

26-
WriteRootDescriptor(fileStream);
27-
return true;
39+
File.WriteAllText(TrimmerFile.ItemSpec, rootDescriptor);
40+
return !Log.HasLoggedErrors;
2841
}
2942

30-
internal void WriteRootDescriptor(Stream stream)
43+
internal string CreateRootDescriptorContents()
3144
{
3245
var roots = new XElement("linker");
33-
foreach (var assembly in Assemblies)
46+
foreach (var assembly in Assemblies.OrderBy(a => a.ItemSpec))
3447
{
3548
// NOTE: Descriptor files don't include the file extension
3649
// in the assemblyName.
@@ -60,10 +73,7 @@ internal void WriteRootDescriptor(Stream stream)
6073
OmitXmlDeclaration = true
6174
};
6275

63-
using var writer = XmlWriter.Create(stream, xmlWriterSettings);
64-
var xDocument = new XDocument(roots);
65-
66-
xDocument.Save(writer);
76+
return new XDocument(roots).Root.ToString();
6777
}
6878
}
6979
}

src/Razor/Microsoft.NET.Sdk.Razor/src/build/netstandard2.0/Microsoft.NET.Sdk.Razor.Components.Wasm.targets

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,13 @@ Copyright (c) .NET Foundation. All rights reserved.
1818
<UsingTask TaskName="Microsoft.AspNetCore.Razor.Tasks.CreateBlazorTrimmerRootDescriptorFile" AssemblyFile="$(RazorSdkBuildTasksAssembly)" />
1919

2020
<PropertyGroup>
21-
<PublishTrimmed Condition="'$(PublishTrimmed)' == ''">true</PublishTrimmed>
2221
<SelfContained>true</SelfContained>
2322
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
2423

24+
<!-- Trimmer defaults -->
25+
<PublishTrimmed Condition="'$(PublishTrimmed)' == ''">true</PublishTrimmed>
26+
<TrimMode Condition="'$(TrimMode)' == ''">link</TrimMode>
27+
2528
<StaticWebAssetBasePath Condition="'$(StaticWebAssetBasePath)' == ''">/</StaticWebAssetBasePath>
2629
<BlazorCacheBootResources Condition="'$(BlazorCacheBootResources)' == ''">true</BlazorCacheBootResources>
2730

@@ -46,14 +49,6 @@ Copyright (c) .NET Foundation. All rights reserved.
4649
<KnownFrameworkReference Remove="Microsoft.AspNetCore.App" />
4750
</ItemGroup>
4851

49-
<!-- Temporary workaround until ILLink.targets are updated -->
50-
<PropertyGroup Condition=" '$(PublishTrimmed)' == 'true' ">
51-
<IntermediateLinkDir Condition=" '$(IntermediateLinkDir)' == '' ">$(IntermediateOutputPath)linked\</IntermediateLinkDir>
52-
<IntermediateLinkDir Condition=" !HasTrailingSlash('$(IntermediateLinkDir)') ">$(IntermediateLinkDir)\</IntermediateLinkDir>
53-
<!-- Used to enable incremental build for the linker target. -->
54-
<_LinkSemaphore>$(IntermediateOutputPath)Link.semaphore</_LinkSemaphore>
55-
</PropertyGroup>
56-
5752
<Import Project="Microsoft.NET.Sdk.Razor.Components.ServiceWorkerAssetsManifest.targets" Condition="'$(ServiceWorkerAssetsManifest)' != ''" />
5853

5954
<Target Name="_ScrambleDotnetJsFileName" AfterTargets="ResolveRuntimePackAssets">
@@ -312,18 +307,22 @@ Copyright (c) .NET Foundation. All rights reserved.
312307
</Copy>
313308
</Target>
314309

315-
<Target Name="_CreateBlazorTrimmerRootDescriptorFiles" BeforeTargets="ILLink" AfterTargets="ComputeResolvedFilesToPublishList">
310+
<Target Name="_BlazorWasmPrepareForLink" BeforeTargets="PrepareForILLink">
316311
<PropertyGroup>
317312
<_BlazorTypeGranularTrimmerDescriptorFile>$(IntermediateOutputPath)typegranularity.trimmerdescriptor.xml</_BlazorTypeGranularTrimmerDescriptorFile>
318313
</PropertyGroup>
319314

320315
<ItemGroup>
321316
<_BlazorTypeGranularAssembly
322-
Include="@(ResolvedFileToPublish)"
317+
Include="@(ManagedAssemblyToLink)"
323318
Condition="'%(Extension)' == '.dll' AND ($([System.String]::Copy('%(Filename)').StartsWith('Microsoft.AspNetCore.')) or $([System.String]::Copy('%(Filename)').StartsWith('Microsoft.Extensions.')))">
324319
<Required>false</Required>
325320
<Preserve>all</Preserve>
326321
</_BlazorTypeGranularAssembly>
322+
323+
<ManagedAssemblyToLink
324+
IsTrimmable="true"
325+
Condition="'%(Extension)' == '.dll' AND ($([System.String]::Copy('%(Filename)').StartsWith('Microsoft.AspNetCore.')) or $([System.String]::Copy('%(Filename)').StartsWith('Microsoft.Extensions.')))" />
327326
</ItemGroup>
328327

329328
<CreateBlazorTrimmerRootDescriptorFile

src/Razor/test/testassets/blazorwasm/Program.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ public static void Main(string[] args)
88
{
99
GC.KeepAlive(typeof(System.Text.Json.JsonSerializer));
1010
GC.KeepAlive(typeof(RazorClassLibrary.Class1));
11+
GC.KeepAlive(typeof(Microsoft.Extensions.Logging.ILogger));
1112
#if REFERENCE_classlibrarywithsatelliteassemblies
1213
GC.KeepAlive(typeof(classlibrarywithsatelliteassemblies.Class1));
1314
#endif

src/Razor/test/testassets/blazorwasm/blazorwasm.csproj

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,14 @@
2626
<ProjectReference Include="..\razorclasslibrary\RazorClassLibrary.csproj" />
2727
</ItemGroup>
2828

29+
<ItemGroup>
30+
<!--
31+
We need a Microsoft.* package to verify type granular trimming. We'll pick a fixed version to we can bake in some details about
32+
the contents of the package in our tests.
33+
-->
34+
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="3.1.0" />
35+
</ItemGroup>
36+
2937
<ItemGroup>
3038
<!-- These assets should be treated as static web assets for publish purposes -->
3139
<Content Include="..\LinkBaseToWebRoot\**\*.js">

0 commit comments

Comments
 (0)