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:
parent
7f4b846e9f
commit
8768cab874
|
|
@ -1,9 +1,9 @@
|
||||||
{
|
{
|
||||||
"sdk": {
|
"sdk": {
|
||||||
"version": "5.0.100-preview.6.20310.4"
|
"version": "5.0.100-preview.7.20330.3"
|
||||||
},
|
},
|
||||||
"tools": {
|
"tools": {
|
||||||
"dotnet": "5.0.100-preview.6.20310.4",
|
"dotnet": "5.0.100-preview.7.20330.3",
|
||||||
"runtimes": {
|
"runtimes": {
|
||||||
"dotnet/x64": [
|
"dotnet/x64": [
|
||||||
"2.1.18",
|
"2.1.18",
|
||||||
|
|
|
||||||
|
|
@ -519,7 +519,7 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
|
||||||
{
|
{
|
||||||
using (var file = File.OpenRead(assemblyPath))
|
using (var file = File.OpenRead(assemblyPath))
|
||||||
{
|
{
|
||||||
var peReader = new PEReader(file);
|
using var peReader = new PEReader(file);
|
||||||
var metadataReader = peReader.GetMetadataReader();
|
var metadataReader = peReader.GetMetadataReader();
|
||||||
return metadataReader.TypeDefinitions.Where(t => !t.IsNil).Select(t =>
|
return metadataReader.TypeDefinitions.Where(t => !t.IsNil).Select(t =>
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -8,8 +8,8 @@ using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Razor.Tasks;
|
using Microsoft.AspNetCore.Razor.Tasks;
|
||||||
using Microsoft.AspNetCore.Testing;
|
using Microsoft.AspNetCore.Testing;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
using ResourceHashesByNameDictionary = System.Collections.Generic.Dictionary<string, string>;
|
|
||||||
using static Microsoft.AspNetCore.Razor.Design.IntegrationTests.ServiceWorkerAssert;
|
using static Microsoft.AspNetCore.Razor.Design.IntegrationTests.ServiceWorkerAssert;
|
||||||
|
using ResourceHashesByNameDictionary = System.Collections.Generic.Dictionary<string, string>;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
|
namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
|
||||||
{
|
{
|
||||||
|
|
@ -60,6 +60,8 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
|
||||||
serviceWorkerPath: Path.Combine("serviceworkers", "my-service-worker.js"),
|
serviceWorkerPath: Path.Combine("serviceworkers", "my-service-worker.js"),
|
||||||
serviceWorkerContent: "// This is the production service worker",
|
serviceWorkerContent: "// This is the production service worker",
|
||||||
assetsManifestPath: "custom-service-worker-assets.js");
|
assetsManifestPath: "custom-service-worker-assets.js");
|
||||||
|
|
||||||
|
VerifyTypeGranularTrimming(result, blazorPublishDirectory);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
@ -223,6 +225,10 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
|
||||||
serviceWorkerPath: Path.Combine("serviceworkers", "my-service-worker.js"),
|
serviceWorkerPath: Path.Combine("serviceworkers", "my-service-worker.js"),
|
||||||
serviceWorkerContent: "// This is the production service worker",
|
serviceWorkerContent: "// This is the production service worker",
|
||||||
assetsManifestPath: "custom-service-worker-assets.js");
|
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]
|
[Fact]
|
||||||
|
|
@ -309,6 +315,8 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
|
||||||
serviceWorkerPath: Path.Combine("serviceworkers", "my-service-worker.js"),
|
serviceWorkerPath: Path.Combine("serviceworkers", "my-service-worker.js"),
|
||||||
serviceWorkerContent: "// This is the production service worker",
|
serviceWorkerContent: "// This is the production service worker",
|
||||||
assetsManifestPath: "custom-service-worker-assets.js");
|
assetsManifestPath: "custom-service-worker-assets.js");
|
||||||
|
|
||||||
|
VerifyTypeGranularTrimming(result, blazorPublishDirectory);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[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)
|
private static BootJsonData ReadBootJsonData(MSBuildResult result, string path)
|
||||||
{
|
{
|
||||||
return JsonSerializer.Deserialize<BootJsonData>(
|
return JsonSerializer.Deserialize<BootJsonData>(
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,10 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// 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.
|
// 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.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
using System.Xml;
|
using System.Xml;
|
||||||
using System.Xml.Linq;
|
using System.Xml.Linq;
|
||||||
using Microsoft.Build.Framework;
|
using Microsoft.Build.Framework;
|
||||||
|
|
@ -21,16 +23,27 @@ namespace Microsoft.AspNetCore.Razor.Tasks
|
||||||
|
|
||||||
public override bool Execute()
|
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);
|
if (string.Equals(rootDescriptor, existing, StringComparison.Ordinal))
|
||||||
return true;
|
{
|
||||||
|
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");
|
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
|
// NOTE: Descriptor files don't include the file extension
|
||||||
// in the assemblyName.
|
// in the assemblyName.
|
||||||
|
|
@ -60,10 +73,7 @@ namespace Microsoft.AspNetCore.Razor.Tasks
|
||||||
OmitXmlDeclaration = true
|
OmitXmlDeclaration = true
|
||||||
};
|
};
|
||||||
|
|
||||||
using var writer = XmlWriter.Create(stream, xmlWriterSettings);
|
return new XDocument(roots).Root.ToString();
|
||||||
var xDocument = new XDocument(roots);
|
|
||||||
|
|
||||||
xDocument.Save(writer);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,10 +18,13 @@ Copyright (c) .NET Foundation. All rights reserved.
|
||||||
<UsingTask TaskName="Microsoft.AspNetCore.Razor.Tasks.CreateBlazorTrimmerRootDescriptorFile" AssemblyFile="$(RazorSdkBuildTasksAssembly)" />
|
<UsingTask TaskName="Microsoft.AspNetCore.Razor.Tasks.CreateBlazorTrimmerRootDescriptorFile" AssemblyFile="$(RazorSdkBuildTasksAssembly)" />
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<PublishTrimmed Condition="'$(PublishTrimmed)' == ''">true</PublishTrimmed>
|
|
||||||
<SelfContained>true</SelfContained>
|
<SelfContained>true</SelfContained>
|
||||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||||
|
|
||||||
|
<!-- Trimmer defaults -->
|
||||||
|
<PublishTrimmed Condition="'$(PublishTrimmed)' == ''">true</PublishTrimmed>
|
||||||
|
<TrimMode Condition="'$(TrimMode)' == ''">link</TrimMode>
|
||||||
|
|
||||||
<StaticWebAssetBasePath Condition="'$(StaticWebAssetBasePath)' == ''">/</StaticWebAssetBasePath>
|
<StaticWebAssetBasePath Condition="'$(StaticWebAssetBasePath)' == ''">/</StaticWebAssetBasePath>
|
||||||
<BlazorCacheBootResources Condition="'$(BlazorCacheBootResources)' == ''">true</BlazorCacheBootResources>
|
<BlazorCacheBootResources Condition="'$(BlazorCacheBootResources)' == ''">true</BlazorCacheBootResources>
|
||||||
|
|
||||||
|
|
@ -46,14 +49,6 @@ Copyright (c) .NET Foundation. All rights reserved.
|
||||||
<KnownFrameworkReference Remove="Microsoft.AspNetCore.App" />
|
<KnownFrameworkReference Remove="Microsoft.AspNetCore.App" />
|
||||||
</ItemGroup>
|
</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)' != ''" />
|
<Import Project="Microsoft.NET.Sdk.Razor.Components.ServiceWorkerAssetsManifest.targets" Condition="'$(ServiceWorkerAssetsManifest)' != ''" />
|
||||||
|
|
||||||
<Target Name="_ScrambleDotnetJsFileName" AfterTargets="ResolveRuntimePackAssets">
|
<Target Name="_ScrambleDotnetJsFileName" AfterTargets="ResolveRuntimePackAssets">
|
||||||
|
|
@ -312,18 +307,22 @@ Copyright (c) .NET Foundation. All rights reserved.
|
||||||
</Copy>
|
</Copy>
|
||||||
</Target>
|
</Target>
|
||||||
|
|
||||||
<Target Name="_CreateBlazorTrimmerRootDescriptorFiles" BeforeTargets="ILLink" AfterTargets="ComputeResolvedFilesToPublishList">
|
<Target Name="_BlazorWasmPrepareForLink" BeforeTargets="PrepareForILLink">
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<_BlazorTypeGranularTrimmerDescriptorFile>$(IntermediateOutputPath)typegranularity.trimmerdescriptor.xml</_BlazorTypeGranularTrimmerDescriptorFile>
|
<_BlazorTypeGranularTrimmerDescriptorFile>$(IntermediateOutputPath)typegranularity.trimmerdescriptor.xml</_BlazorTypeGranularTrimmerDescriptorFile>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<_BlazorTypeGranularAssembly
|
<_BlazorTypeGranularAssembly
|
||||||
Include="@(ResolvedFileToPublish)"
|
Include="@(ManagedAssemblyToLink)"
|
||||||
Condition="'%(Extension)' == '.dll' AND ($([System.String]::Copy('%(Filename)').StartsWith('Microsoft.AspNetCore.')) or $([System.String]::Copy('%(Filename)').StartsWith('Microsoft.Extensions.')))">
|
Condition="'%(Extension)' == '.dll' AND ($([System.String]::Copy('%(Filename)').StartsWith('Microsoft.AspNetCore.')) or $([System.String]::Copy('%(Filename)').StartsWith('Microsoft.Extensions.')))">
|
||||||
<Required>false</Required>
|
<Required>false</Required>
|
||||||
<Preserve>all</Preserve>
|
<Preserve>all</Preserve>
|
||||||
</_BlazorTypeGranularAssembly>
|
</_BlazorTypeGranularAssembly>
|
||||||
|
|
||||||
|
<ManagedAssemblyToLink
|
||||||
|
IsTrimmable="true"
|
||||||
|
Condition="'%(Extension)' == '.dll' AND ($([System.String]::Copy('%(Filename)').StartsWith('Microsoft.AspNetCore.')) or $([System.String]::Copy('%(Filename)').StartsWith('Microsoft.Extensions.')))" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<CreateBlazorTrimmerRootDescriptorFile
|
<CreateBlazorTrimmerRootDescriptorFile
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ namespace standalone
|
||||||
{
|
{
|
||||||
GC.KeepAlive(typeof(System.Text.Json.JsonSerializer));
|
GC.KeepAlive(typeof(System.Text.Json.JsonSerializer));
|
||||||
GC.KeepAlive(typeof(RazorClassLibrary.Class1));
|
GC.KeepAlive(typeof(RazorClassLibrary.Class1));
|
||||||
|
GC.KeepAlive(typeof(Microsoft.Extensions.Logging.ILogger));
|
||||||
#if REFERENCE_classlibrarywithsatelliteassemblies
|
#if REFERENCE_classlibrarywithsatelliteassemblies
|
||||||
GC.KeepAlive(typeof(classlibrarywithsatelliteassemblies.Class1));
|
GC.KeepAlive(typeof(classlibrarywithsatelliteassemblies.Class1));
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,14 @@
|
||||||
<ProjectReference Include="..\razorclasslibrary\RazorClassLibrary.csproj" />
|
<ProjectReference Include="..\razorclasslibrary\RazorClassLibrary.csproj" />
|
||||||
</ItemGroup>
|
</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>
|
<ItemGroup>
|
||||||
<!-- These assets should be treated as static web assets for publish purposes -->
|
<!-- These assets should be treated as static web assets for publish purposes -->
|
||||||
<Content Include="..\LinkBaseToWebRoot\**\*.js">
|
<Content Include="..\LinkBaseToWebRoot\**\*.js">
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue