diff --git a/global.json b/global.json index d3fad315e5..26221c3cd3 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 8648ede045..6583ce89d0 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 @@ 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 => { 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 dfaec7c803..fdcb60b864 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 System.Threading.Tasks; 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 @@ 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( diff --git a/src/Razor/Microsoft.NET.Sdk.Razor/src/CreateBlazorTrimmerRootDescriptorFile.cs b/src/Razor/Microsoft.NET.Sdk.Razor/src/CreateBlazorTrimmerRootDescriptorFile.cs index 112d49a82b..0f36be8a9d 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 @@ 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(); } } } 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 1d293e3ef4..9c06ac8210 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 - - @@ -312,18 +307,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 + + + + + + +