From 5f0f9e491bdb1c7d57d149f517bbe3c85061fca0 Mon Sep 17 00:00:00 2001 From: Pranav K Date: Tue, 30 Jun 2020 07:36:14 -0700 Subject: [PATCH 1/2] Use linker extensibility to enable better trimming * Configure TrimmerDefaults=link if unspecified * Allow Microsoft.AspNetCore.* and Microsoft.Extensions.* packages to be trimmed. --- global.json | 4 +-- .../integrationtests/Assert.cs | 2 +- .../Wasm/WasmPublishIntegrationTest.cs | 25 ++++++++++++++++++- ...soft.NET.Sdk.Razor.Components.Wasm.targets | 21 ++++++++-------- .../test/testassets/blazorwasm/Program.cs | 1 + .../testassets/blazorwasm/blazorwasm.csproj | 8 ++++++ 6 files changed, 46 insertions(+), 15 deletions(-) diff --git a/global.json b/global.json index d3fad315e54c..26221c3cd3f4 100644 --- a/global.json +++ b/global.json @@ -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", diff --git a/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/Assert.cs b/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/Assert.cs index 8648ede045ec..6583ce89d055 100644 --- a/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/Assert.cs +++ b/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/Assert.cs @@ -519,7 +519,7 @@ private static IEnumerable GetDeclaredTypeNames(string assemblyPath) { 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 => { diff --git a/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/Wasm/WasmPublishIntegrationTest.cs b/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/Wasm/WasmPublishIntegrationTest.cs index dfaec7c8034b..fdcb60b864cd 100644 --- a/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/Wasm/WasmPublishIntegrationTest.cs +++ b/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/Wasm/WasmPublishIntegrationTest.cs @@ -8,8 +8,8 @@ using Microsoft.AspNetCore.Razor.Tasks; using Microsoft.AspNetCore.Testing; using Xunit; -using ResourceHashesByNameDictionary = System.Collections.Generic.Dictionary; using static Microsoft.AspNetCore.Razor.Design.IntegrationTests.ServiceWorkerAssert; +using ResourceHashesByNameDictionary = System.Collections.Generic.Dictionary; namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests { @@ -60,6 +60,8 @@ public async Task Publish_WithDefaultSettings_Works() 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 @@ public async Task Publish_WithTrimmingdDisabled_Works() 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 @@ public async Task Publish_HostedApp_DefaultSettings_Works() 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 @@ static string ParseWebFormattedHash(string webFormattedHash) } } + + 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( diff --git a/src/Razor/Microsoft.NET.Sdk.Razor/src/build/netstandard2.0/Microsoft.NET.Sdk.Razor.Components.Wasm.targets b/src/Razor/Microsoft.NET.Sdk.Razor/src/build/netstandard2.0/Microsoft.NET.Sdk.Razor.Components.Wasm.targets index fff9969bcb58..508b2aa76710 100644 --- a/src/Razor/Microsoft.NET.Sdk.Razor/src/build/netstandard2.0/Microsoft.NET.Sdk.Razor.Components.Wasm.targets +++ b/src/Razor/Microsoft.NET.Sdk.Razor/src/build/netstandard2.0/Microsoft.NET.Sdk.Razor.Components.Wasm.targets @@ -18,10 +18,13 @@ Copyright (c) .NET Foundation. All rights reserved. - true true true + + true + link + / true @@ -46,14 +49,6 @@ Copyright (c) .NET Foundation. All rights reserved. - - - $(IntermediateOutputPath)linked\ - $(IntermediateLinkDir)\ - - <_LinkSemaphore>$(IntermediateOutputPath)Link.semaphore - - @@ -305,18 +300,22 @@ Copyright (c) .NET Foundation. All rights reserved. - + <_BlazorTypeGranularTrimmerDescriptorFile>$(IntermediateOutputPath)typegranularity.trimmerdescriptor.xml <_BlazorTypeGranularAssembly - Include="@(ResolvedFileToPublish)" + Include="@(ManagedAssemblyToLink)" Condition="'%(Extension)' == '.dll' AND ($([System.String]::Copy('%(Filename)').StartsWith('Microsoft.AspNetCore.')) or $([System.String]::Copy('%(Filename)').StartsWith('Microsoft.Extensions.')))"> false all + + + + + + + From f68fb0b9262e6c5f65e882578e17f3909ec524a7 Mon Sep 17 00:00:00 2001 From: Pranav K Date: Tue, 30 Jun 2020 13:55:38 -0700 Subject: [PATCH 2/2] Make producing the trimmer root descriptor more incremental --- .../CreateBlazorTrimmerRootDescriptorFile.cs | 28 +++++++++++++------ 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/src/Razor/Microsoft.NET.Sdk.Razor/src/CreateBlazorTrimmerRootDescriptorFile.cs b/src/Razor/Microsoft.NET.Sdk.Razor/src/CreateBlazorTrimmerRootDescriptorFile.cs index 112d49a82b30..0f36be8a9d64 100644 --- a/src/Razor/Microsoft.NET.Sdk.Razor/src/CreateBlazorTrimmerRootDescriptorFile.cs +++ b/src/Razor/Microsoft.NET.Sdk.Razor/src/CreateBlazorTrimmerRootDescriptorFile.cs @@ -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 @@ public class CreateBlazorTrimmerRootDescriptorFile : Task 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); + + 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; + } + } - WriteRootDescriptor(fileStream); - return true; + 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 @@ internal void WriteRootDescriptor(Stream stream) OmitXmlDeclaration = true }; - using var writer = XmlWriter.Create(stream, xmlWriterSettings); - var xDocument = new XDocument(roots); - - xDocument.Save(writer); + return new XDocument(roots).Root.ToString(); } } }