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
This commit is contained in:
Pranav K 2020-06-30 20:39:58 -07:00 committed by GitHub
parent 7f4b846e9f
commit 8768cab874
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 65 additions and 24 deletions

View File

@ -1,9 +1,9 @@
{
"sdk": {
"version": "5.0.100-preview.6.20310.4"
"version": "5.0.100-preview.7.20330.3"
},
"tools": {
"dotnet": "5.0.100-preview.6.20310.4",
"dotnet": "5.0.100-preview.7.20330.3",
"runtimes": {
"dotnet/x64": [
"2.1.18",

View File

@ -519,7 +519,7 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
{
using (var file = File.OpenRead(assemblyPath))
{
var peReader = new PEReader(file);
using var peReader = new PEReader(file);
var metadataReader = peReader.GetMetadataReader();
return metadataReader.TypeDefinitions.Where(t => !t.IsNil).Select(t =>
{

View File

@ -8,8 +8,8 @@ using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor.Tasks;
using Microsoft.AspNetCore.Testing;
using Xunit;
using ResourceHashesByNameDictionary = System.Collections.Generic.Dictionary<string, string>;
using static Microsoft.AspNetCore.Razor.Design.IntegrationTests.ServiceWorkerAssert;
using ResourceHashesByNameDictionary = System.Collections.Generic.Dictionary<string, string>;
namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
{
@ -60,6 +60,8 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
serviceWorkerPath: Path.Combine("serviceworkers", "my-service-worker.js"),
serviceWorkerContent: "// This is the production service worker",
assetsManifestPath: "custom-service-worker-assets.js");
VerifyTypeGranularTrimming(result, blazorPublishDirectory);
}
[Fact]
@ -223,6 +225,10 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
serviceWorkerPath: Path.Combine("serviceworkers", "my-service-worker.js"),
serviceWorkerContent: "// This is the production service worker",
assetsManifestPath: "custom-service-worker-assets.js");
// Verify assemblies are not trimmed
var loggingAssemblyPath = Path.Combine(blazorPublishDirectory, "_framework", "Microsoft.Extensions.Logging.Abstractions.dll");
Assert.AssemblyContainsType(result, loggingAssemblyPath, "Microsoft.Extensions.Logging.Abstractions.NullLogger");
}
[Fact]
@ -309,6 +315,8 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
serviceWorkerPath: Path.Combine("serviceworkers", "my-service-worker.js"),
serviceWorkerContent: "// This is the production service worker",
assetsManifestPath: "custom-service-worker-assets.js");
VerifyTypeGranularTrimming(result, blazorPublishDirectory);
}
[Fact]
@ -693,6 +701,21 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
}
}
private void VerifyTypeGranularTrimming(MSBuildResult result, string blazorPublishDirectory)
{
var loggingAssemblyPath = Path.Combine(blazorPublishDirectory, "_framework", "Microsoft.Extensions.Logging.Abstractions.dll");
Assert.FileExists(result, loggingAssemblyPath);
// ILogger is referenced by the app, so we expect it to be preserved
Assert.AssemblyContainsType(result, loggingAssemblyPath, "Microsoft.Extensions.Logging.ILogger");
// LogLevel is referenced by ILogger and therefore must be preserved.
Assert.AssemblyContainsType(result, loggingAssemblyPath, "Microsoft.Extensions.Logging.LogLevel");
// NullLogger is not referenced by the app, and should be trimmed.
Assert.AssemblyDoesNotContainType(result, loggingAssemblyPath, "Microsoft.Extensions.Logging.Abstractions.NullLogger");
}
private static BootJsonData ReadBootJsonData(MSBuildResult result, string path)
{
return JsonSerializer.Deserialize<BootJsonData>(

View File

@ -1,8 +1,10 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Xml;
using System.Xml.Linq;
using Microsoft.Build.Framework;
@ -21,16 +23,27 @@ namespace Microsoft.AspNetCore.Razor.Tasks
public override bool Execute()
{
using var fileStream = File.Create(TrimmerFile.ItemSpec);
var rootDescriptor = CreateRootDescriptorContents();
if (File.Exists(TrimmerFile.ItemSpec))
{
var existing = File.ReadAllText(TrimmerFile.ItemSpec);
WriteRootDescriptor(fileStream);
return true;
if (string.Equals(rootDescriptor, existing, StringComparison.Ordinal))
{
Log.LogMessage(MessageImportance.Low, "Skipping write to file {0} because contents would not change.", TrimmerFile.ItemSpec);
// Avoid writing if the file contents are identical. This is required for build incrementalism.
return !Log.HasLoggedErrors;
}
}
File.WriteAllText(TrimmerFile.ItemSpec, rootDescriptor);
return !Log.HasLoggedErrors;
}
internal void WriteRootDescriptor(Stream stream)
internal string CreateRootDescriptorContents()
{
var roots = new XElement("linker");
foreach (var assembly in Assemblies)
foreach (var assembly in Assemblies.OrderBy(a => a.ItemSpec))
{
// NOTE: Descriptor files don't include the file extension
// in the assemblyName.
@ -60,10 +73,7 @@ namespace Microsoft.AspNetCore.Razor.Tasks
OmitXmlDeclaration = true
};
using var writer = XmlWriter.Create(stream, xmlWriterSettings);
var xDocument = new XDocument(roots);
xDocument.Save(writer);
return new XDocument(roots).Root.ToString();
}
}
}

View File

@ -18,10 +18,13 @@ Copyright (c) .NET Foundation. All rights reserved.
<UsingTask TaskName="Microsoft.AspNetCore.Razor.Tasks.CreateBlazorTrimmerRootDescriptorFile" AssemblyFile="$(RazorSdkBuildTasksAssembly)" />
<PropertyGroup>
<PublishTrimmed Condition="'$(PublishTrimmed)' == ''">true</PublishTrimmed>
<SelfContained>true</SelfContained>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
<!-- Trimmer defaults -->
<PublishTrimmed Condition="'$(PublishTrimmed)' == ''">true</PublishTrimmed>
<TrimMode Condition="'$(TrimMode)' == ''">link</TrimMode>
<StaticWebAssetBasePath Condition="'$(StaticWebAssetBasePath)' == ''">/</StaticWebAssetBasePath>
<BlazorCacheBootResources Condition="'$(BlazorCacheBootResources)' == ''">true</BlazorCacheBootResources>
@ -46,14 +49,6 @@ Copyright (c) .NET Foundation. All rights reserved.
<KnownFrameworkReference Remove="Microsoft.AspNetCore.App" />
</ItemGroup>
<!-- Temporary workaround until ILLink.targets are updated -->
<PropertyGroup Condition=" '$(PublishTrimmed)' == 'true' ">
<IntermediateLinkDir Condition=" '$(IntermediateLinkDir)' == '' ">$(IntermediateOutputPath)linked\</IntermediateLinkDir>
<IntermediateLinkDir Condition=" !HasTrailingSlash('$(IntermediateLinkDir)') ">$(IntermediateLinkDir)\</IntermediateLinkDir>
<!-- Used to enable incremental build for the linker target. -->
<_LinkSemaphore>$(IntermediateOutputPath)Link.semaphore</_LinkSemaphore>
</PropertyGroup>
<Import Project="Microsoft.NET.Sdk.Razor.Components.ServiceWorkerAssetsManifest.targets" Condition="'$(ServiceWorkerAssetsManifest)' != ''" />
<Target Name="_ScrambleDotnetJsFileName" AfterTargets="ResolveRuntimePackAssets">
@ -312,18 +307,22 @@ Copyright (c) .NET Foundation. All rights reserved.
</Copy>
</Target>
<Target Name="_CreateBlazorTrimmerRootDescriptorFiles" BeforeTargets="ILLink" AfterTargets="ComputeResolvedFilesToPublishList">
<Target Name="_BlazorWasmPrepareForLink" BeforeTargets="PrepareForILLink">
<PropertyGroup>
<_BlazorTypeGranularTrimmerDescriptorFile>$(IntermediateOutputPath)typegranularity.trimmerdescriptor.xml</_BlazorTypeGranularTrimmerDescriptorFile>
</PropertyGroup>
<ItemGroup>
<_BlazorTypeGranularAssembly
Include="@(ResolvedFileToPublish)"
Include="@(ManagedAssemblyToLink)"
Condition="'%(Extension)' == '.dll' AND ($([System.String]::Copy('%(Filename)').StartsWith('Microsoft.AspNetCore.')) or $([System.String]::Copy('%(Filename)').StartsWith('Microsoft.Extensions.')))">
<Required>false</Required>
<Preserve>all</Preserve>
</_BlazorTypeGranularAssembly>
<ManagedAssemblyToLink
IsTrimmable="true"
Condition="'%(Extension)' == '.dll' AND ($([System.String]::Copy('%(Filename)').StartsWith('Microsoft.AspNetCore.')) or $([System.String]::Copy('%(Filename)').StartsWith('Microsoft.Extensions.')))" />
</ItemGroup>
<CreateBlazorTrimmerRootDescriptorFile

View File

@ -8,6 +8,7 @@ namespace standalone
{
GC.KeepAlive(typeof(System.Text.Json.JsonSerializer));
GC.KeepAlive(typeof(RazorClassLibrary.Class1));
GC.KeepAlive(typeof(Microsoft.Extensions.Logging.ILogger));
#if REFERENCE_classlibrarywithsatelliteassemblies
GC.KeepAlive(typeof(classlibrarywithsatelliteassemblies.Class1));
#endif

View File

@ -26,6 +26,14 @@
<ProjectReference Include="..\razorclasslibrary\RazorClassLibrary.csproj" />
</ItemGroup>
<ItemGroup>
<!--
We need a Microsoft.* package to verify type granular trimming. We'll pick a fixed version to we can bake in some details about
the contents of the package in our tests.
-->
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="3.1.0" />
</ItemGroup>
<ItemGroup>
<!-- These assets should be treated as static web assets for publish purposes -->
<Content Include="..\LinkBaseToWebRoot\**\*.js">