From 15705efaf3c3cd02297f4ad593a64366c2b7cef8 Mon Sep 17 00:00:00 2001 From: Steve Sanderson Date: Mon, 6 Jan 2020 16:37:41 +0000 Subject: [PATCH 1/8] Make apps smaller via type-granularity linking --- src/Components/Authorization/src/LinkerConfig.xml | 6 ++++++ .../Blazor/Blazor/src/Hosting/WebAssemblyHost.cs | 5 +++++ src/Components/Blazor/Blazor/src/LinkerConfig.xml | 6 ++++++ .../Blazor/Build/src/targets/Blazor.MonoRuntime.targets | 5 ++++- src/Components/Blazor/Validation/src/LinkerConfig.xml | 6 ++++++ src/Components/Components/src/LinkerConfig.xml | 6 ++++++ src/Components/Directory.Build.props | 7 +++++++ src/Components/Forms/src/LinkerConfig.xml | 6 ++++++ src/Components/Web/src/LinkerConfig.xml | 6 ++++++ 9 files changed, 52 insertions(+), 1 deletion(-) create mode 100644 src/Components/Authorization/src/LinkerConfig.xml create mode 100644 src/Components/Blazor/Blazor/src/LinkerConfig.xml create mode 100644 src/Components/Blazor/Validation/src/LinkerConfig.xml create mode 100644 src/Components/Components/src/LinkerConfig.xml create mode 100644 src/Components/Forms/src/LinkerConfig.xml create mode 100644 src/Components/Web/src/LinkerConfig.xml diff --git a/src/Components/Authorization/src/LinkerConfig.xml b/src/Components/Authorization/src/LinkerConfig.xml new file mode 100644 index 000000000000..8fa428c2165f --- /dev/null +++ b/src/Components/Authorization/src/LinkerConfig.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/Components/Blazor/Blazor/src/Hosting/WebAssemblyHost.cs b/src/Components/Blazor/Blazor/src/Hosting/WebAssemblyHost.cs index 3a2ccfbaaee2..b90878fdde36 100644 --- a/src/Components/Blazor/Blazor/src/Hosting/WebAssemblyHost.cs +++ b/src/Components/Blazor/Blazor/src/Hosting/WebAssemblyHost.cs @@ -19,6 +19,11 @@ internal class WebAssemblyHost : IWebAssemblyHost public WebAssemblyHost(IServiceProvider services, IJSRuntime runtime) { + // To ensure JS-invoked methods don't get linked out, have a reference to their enclosing types + GC.KeepAlive(typeof(EntrypointInvoker)); + GC.KeepAlive(typeof(JSInteropMethods)); + GC.KeepAlive(typeof(WebAssemblyEventDispatcher)); + Services = services ?? throw new ArgumentNullException(nameof(services)); _runtime = runtime ?? throw new ArgumentNullException(nameof(runtime)); } diff --git a/src/Components/Blazor/Blazor/src/LinkerConfig.xml b/src/Components/Blazor/Blazor/src/LinkerConfig.xml new file mode 100644 index 000000000000..1a4565933bc1 --- /dev/null +++ b/src/Components/Blazor/Blazor/src/LinkerConfig.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/Components/Blazor/Build/src/targets/Blazor.MonoRuntime.targets b/src/Components/Blazor/Build/src/targets/Blazor.MonoRuntime.targets index 0a3ba70a2374..099b7fe53c1a 100644 --- a/src/Components/Blazor/Build/src/targets/Blazor.MonoRuntime.targets +++ b/src/Components/Blazor/Build/src/targets/Blazor.MonoRuntime.targets @@ -171,7 +171,10 @@ Outputs="$(_BlazorLinkerOutputCache)"> - <_BlazorDependencyAssembly Include="@(_BlazorDependencyInput)" IsLinkable="$([System.String]::Copy('%(FileName)').StartsWith('System.'))" /> + <_BlazorDependencyAssembly Include="@(_BlazorDependencyInput)" /> + <_BlazorDependencyAssembly IsLinkable="true" Condition="$([System.String]::Copy(%(Filename)).StartsWith('System.'))" /> + <_BlazorDependencyAssembly IsLinkable="true" Condition="$([System.String]::Copy(%(Filename)).StartsWith('Microsoft.AspNetCore.'))" /> + <_BlazorDependencyAssembly IsLinkable="true" Condition="$([System.String]::Copy(%(Filename)).StartsWith('Microsoft.Extensions.'))" /> <_BlazorAssemblyToLink Include="@(_WebAssemblyBCLAssembly)" /> <_BlazorAssemblyToLink Include="@(_BlazorDependencyAssembly)" Condition="'%(_BlazorDependencyAssembly.IsLinkable)' == 'true'" /> diff --git a/src/Components/Blazor/Validation/src/LinkerConfig.xml b/src/Components/Blazor/Validation/src/LinkerConfig.xml new file mode 100644 index 000000000000..5830117d1e67 --- /dev/null +++ b/src/Components/Blazor/Validation/src/LinkerConfig.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/Components/Components/src/LinkerConfig.xml b/src/Components/Components/src/LinkerConfig.xml new file mode 100644 index 000000000000..ed3834417b2f --- /dev/null +++ b/src/Components/Components/src/LinkerConfig.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/Components/Directory.Build.props b/src/Components/Directory.Build.props index ef310ac60dcd..6a9ba12902da 100644 --- a/src/Components/Directory.Build.props +++ b/src/Components/Directory.Build.props @@ -27,4 +27,11 @@ false + + + + diff --git a/src/Components/Forms/src/LinkerConfig.xml b/src/Components/Forms/src/LinkerConfig.xml new file mode 100644 index 000000000000..9a9aa6146fd2 --- /dev/null +++ b/src/Components/Forms/src/LinkerConfig.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/Components/Web/src/LinkerConfig.xml b/src/Components/Web/src/LinkerConfig.xml new file mode 100644 index 000000000000..5c0641ff2334 --- /dev/null +++ b/src/Components/Web/src/LinkerConfig.xml @@ -0,0 +1,6 @@ + + + + + + From eb47df7c3771dc46d46ed067bc74ed20ba0deaa0 Mon Sep 17 00:00:00 2001 From: Steve Sanderson Date: Wed, 8 Jan 2020 13:54:55 +0000 Subject: [PATCH 2/8] Generate the typegranularity linker config based on referenced assemblies --- .../Authorization/src/LinkerConfig.xml | 6 -- .../Blazor/Blazor/src/LinkerConfig.xml | 6 -- .../GenerateTypeGranularityLinkingConfig.cs | 61 +++++++++++++++++++ .../src/targets/Blazor.MonoRuntime.targets | 13 +++- .../Blazor/Validation/src/LinkerConfig.xml | 6 -- .../Components/src/LinkerConfig.xml | 6 -- src/Components/Directory.Build.props | 7 --- src/Components/Forms/src/LinkerConfig.xml | 6 -- src/Components/Web/src/LinkerConfig.xml | 6 -- 9 files changed, 72 insertions(+), 45 deletions(-) delete mode 100644 src/Components/Authorization/src/LinkerConfig.xml delete mode 100644 src/Components/Blazor/Blazor/src/LinkerConfig.xml create mode 100644 src/Components/Blazor/Build/src/Tasks/GenerateTypeGranularityLinkingConfig.cs delete mode 100644 src/Components/Blazor/Validation/src/LinkerConfig.xml delete mode 100644 src/Components/Components/src/LinkerConfig.xml delete mode 100644 src/Components/Forms/src/LinkerConfig.xml delete mode 100644 src/Components/Web/src/LinkerConfig.xml diff --git a/src/Components/Authorization/src/LinkerConfig.xml b/src/Components/Authorization/src/LinkerConfig.xml deleted file mode 100644 index 8fa428c2165f..000000000000 --- a/src/Components/Authorization/src/LinkerConfig.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/src/Components/Blazor/Blazor/src/LinkerConfig.xml b/src/Components/Blazor/Blazor/src/LinkerConfig.xml deleted file mode 100644 index 1a4565933bc1..000000000000 --- a/src/Components/Blazor/Blazor/src/LinkerConfig.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/src/Components/Blazor/Build/src/Tasks/GenerateTypeGranularityLinkingConfig.cs b/src/Components/Blazor/Build/src/Tasks/GenerateTypeGranularityLinkingConfig.cs new file mode 100644 index 000000000000..934d8f2c48da --- /dev/null +++ b/src/Components/Blazor/Build/src/Tasks/GenerateTypeGranularityLinkingConfig.cs @@ -0,0 +1,61 @@ +// 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.IO; +using System.Xml; +using Microsoft.Build.Framework; +using Microsoft.Build.Utilities; + +namespace Microsoft.AspNetCore.Blazor.Build.Tasks +{ + public class GenerateTypeGranularityLinkingConfig : Task + { + [Required] + public ITaskItem[] Assemblies { get; set; } + + [Required] + public string OutputPath { get; set; } + + public override bool Execute() + { + using (var fileStream = File.Open(OutputPath, FileMode.Create)) + using (var xmlWriter = XmlWriter.Create(fileStream, new XmlWriterSettings { Indent = true })) + { + xmlWriter.WriteStartDocument(); + xmlWriter.WriteComment(" THIS IS A GENERATED FILE - DO NOT EDIT MANUALLY "); + + xmlWriter.WriteStartElement("linker"); + + foreach (var assembly in Assemblies) + { + if (assembly.GetMetadata("TypeGranularity").Equals("true", StringComparison.Ordinal)) + { + AddTypeGranularityConfig(xmlWriter, assembly); + } + } + + xmlWriter.WriteEndElement(); // linker + + xmlWriter.WriteEndDocument(); + xmlWriter.Flush(); + } + + return true; + } + + private void AddTypeGranularityConfig(XmlWriter xmlWriter, ITaskItem assembly) + { + xmlWriter.WriteStartElement("assembly"); + xmlWriter.WriteAttributeString("fullname", Path.GetFileNameWithoutExtension(assembly.ItemSpec)); + + xmlWriter.WriteStartElement("type"); + xmlWriter.WriteAttributeString("fullname", "*"); + xmlWriter.WriteAttributeString("preserve", "all"); + xmlWriter.WriteAttributeString("required", "false"); + xmlWriter.WriteEndElement(); // type + + xmlWriter.WriteEndElement(); // assembly + } + } +} diff --git a/src/Components/Blazor/Build/src/targets/Blazor.MonoRuntime.targets b/src/Components/Blazor/Build/src/targets/Blazor.MonoRuntime.targets index 099b7fe53c1a..edf7c06ca9f4 100644 --- a/src/Components/Blazor/Build/src/targets/Blazor.MonoRuntime.targets +++ b/src/Components/Blazor/Build/src/targets/Blazor.MonoRuntime.targets @@ -160,6 +160,7 @@ + <_BlazorDependencyAssembly Include="@(_BlazorDependencyInput)" /> <_BlazorDependencyAssembly IsLinkable="true" Condition="$([System.String]::Copy(%(Filename)).StartsWith('System.'))" /> - <_BlazorDependencyAssembly IsLinkable="true" Condition="$([System.String]::Copy(%(Filename)).StartsWith('Microsoft.AspNetCore.'))" /> - <_BlazorDependencyAssembly IsLinkable="true" Condition="$([System.String]::Copy(%(Filename)).StartsWith('Microsoft.Extensions.'))" /> + <_BlazorDependencyAssembly IsLinkable="true" TypeGranularity="true" Condition="$([System.String]::Copy(%(Filename)).StartsWith('Microsoft.AspNetCore.'))" /> + <_BlazorDependencyAssembly IsLinkable="true" TypeGranularity="true" Condition="$([System.String]::Copy(%(Filename)).StartsWith('Microsoft.Extensions.'))" /> <_BlazorAssemblyToLink Include="@(_WebAssemblyBCLAssembly)" /> <_BlazorAssemblyToLink Include="@(_BlazorDependencyAssembly)" Condition="'%(_BlazorDependencyAssembly.IsLinkable)' == 'true'" /> @@ -204,6 +205,14 @@ <_DotNetHostFileName Condition=" '$(OS)' == 'Windows_NT' ">dotnet.exe + + <_TypeGranularityLinkingConfig>$(BlazorIntermediateOutputPath)linker.typegranularityconfig.xml + + + + + + - - - - - diff --git a/src/Components/Components/src/LinkerConfig.xml b/src/Components/Components/src/LinkerConfig.xml deleted file mode 100644 index ed3834417b2f..000000000000 --- a/src/Components/Components/src/LinkerConfig.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/src/Components/Directory.Build.props b/src/Components/Directory.Build.props index 6a9ba12902da..ef310ac60dcd 100644 --- a/src/Components/Directory.Build.props +++ b/src/Components/Directory.Build.props @@ -27,11 +27,4 @@ false - - - - diff --git a/src/Components/Forms/src/LinkerConfig.xml b/src/Components/Forms/src/LinkerConfig.xml deleted file mode 100644 index 9a9aa6146fd2..000000000000 --- a/src/Components/Forms/src/LinkerConfig.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/src/Components/Web/src/LinkerConfig.xml b/src/Components/Web/src/LinkerConfig.xml deleted file mode 100644 index 5c0641ff2334..000000000000 --- a/src/Components/Web/src/LinkerConfig.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - From 6d8a65475df237ccb52e3026de800fa21fca0598 Mon Sep 17 00:00:00 2001 From: Steve Sanderson Date: Wed, 8 Jan 2020 15:56:50 +0000 Subject: [PATCH 3/8] Fix --- .../Blazor/Build/src/targets/Blazor.MonoRuntime.targets | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Components/Blazor/Build/src/targets/Blazor.MonoRuntime.targets b/src/Components/Blazor/Build/src/targets/Blazor.MonoRuntime.targets index edf7c06ca9f4..89665f808a25 100644 --- a/src/Components/Blazor/Build/src/targets/Blazor.MonoRuntime.targets +++ b/src/Components/Blazor/Build/src/targets/Blazor.MonoRuntime.targets @@ -173,9 +173,9 @@ <_BlazorDependencyAssembly Include="@(_BlazorDependencyInput)" /> - <_BlazorDependencyAssembly IsLinkable="true" Condition="$([System.String]::Copy(%(Filename)).StartsWith('System.'))" /> - <_BlazorDependencyAssembly IsLinkable="true" TypeGranularity="true" Condition="$([System.String]::Copy(%(Filename)).StartsWith('Microsoft.AspNetCore.'))" /> - <_BlazorDependencyAssembly IsLinkable="true" TypeGranularity="true" Condition="$([System.String]::Copy(%(Filename)).StartsWith('Microsoft.Extensions.'))" /> + <_BlazorDependencyAssembly IsLinkable="true" Condition="$([System.String]::Copy('%(Filename)').StartsWith('System.'))" /> + <_BlazorDependencyAssembly IsLinkable="true" TypeGranularity="true" Condition="$([System.String]::Copy('%(Filename)').StartsWith('Microsoft.AspNetCore.'))" /> + <_BlazorDependencyAssembly IsLinkable="true" TypeGranularity="true" Condition="$([System.String]::Copy('%(Filename)').StartsWith('Microsoft.Extensions.'))" /> <_BlazorAssemblyToLink Include="@(_WebAssemblyBCLAssembly)" /> <_BlazorAssemblyToLink Include="@(_BlazorDependencyAssembly)" Condition="'%(_BlazorDependencyAssembly.IsLinkable)' == 'true'" /> From 1fefa907555f1c02724c17e9f694b20e9610e2af Mon Sep 17 00:00:00 2001 From: Steve Sanderson Date: Thu, 9 Jan 2020 14:39:12 +0000 Subject: [PATCH 4/8] Pre-filter the assembly list when calling GenerateTypeGranularityLinkingConfig Co-Authored-By: Pranav K --- .../Blazor/Build/src/targets/Blazor.MonoRuntime.targets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Components/Blazor/Build/src/targets/Blazor.MonoRuntime.targets b/src/Components/Blazor/Build/src/targets/Blazor.MonoRuntime.targets index 89665f808a25..ecd5f0de6041 100644 --- a/src/Components/Blazor/Build/src/targets/Blazor.MonoRuntime.targets +++ b/src/Components/Blazor/Build/src/targets/Blazor.MonoRuntime.targets @@ -208,7 +208,7 @@ <_TypeGranularityLinkingConfig>$(BlazorIntermediateOutputPath)linker.typegranularityconfig.xml - + From 1e94281a6c4c5af6f203685254c1529f2d2139a6 Mon Sep 17 00:00:00 2001 From: Steve Sanderson Date: Thu, 9 Jan 2020 14:42:05 +0000 Subject: [PATCH 5/8] CR feedback --- .../src/Tasks/GenerateTypeGranularityLinkingConfig.cs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/Components/Blazor/Build/src/Tasks/GenerateTypeGranularityLinkingConfig.cs b/src/Components/Blazor/Build/src/Tasks/GenerateTypeGranularityLinkingConfig.cs index 934d8f2c48da..2c9c390d69e5 100644 --- a/src/Components/Blazor/Build/src/Tasks/GenerateTypeGranularityLinkingConfig.cs +++ b/src/Components/Blazor/Build/src/Tasks/GenerateTypeGranularityLinkingConfig.cs @@ -29,10 +29,7 @@ public override bool Execute() foreach (var assembly in Assemblies) { - if (assembly.GetMetadata("TypeGranularity").Equals("true", StringComparison.Ordinal)) - { - AddTypeGranularityConfig(xmlWriter, assembly); - } + AddTypeGranularityConfig(xmlWriter, assembly); } xmlWriter.WriteEndElement(); // linker @@ -49,6 +46,8 @@ private void AddTypeGranularityConfig(XmlWriter xmlWriter, ITaskItem assembly) xmlWriter.WriteStartElement("assembly"); xmlWriter.WriteAttributeString("fullname", Path.GetFileNameWithoutExtension(assembly.ItemSpec)); + // We match all types in the assembly, and for each one, tell the linker to preserve all + // its members (preserve=all) but only if there's some reference to the type (required=false) xmlWriter.WriteStartElement("type"); xmlWriter.WriteAttributeString("fullname", "*"); xmlWriter.WriteAttributeString("preserve", "all"); From 30926f60066ca2a466958b3a40f6d0f89f677a36 Mon Sep 17 00:00:00 2001 From: Steve Sanderson Date: Thu, 9 Jan 2020 14:53:01 +0000 Subject: [PATCH 6/8] CR: Switch to use XElement --- .../GenerateTypeGranularityLinkingConfig.cs | 46 +++++++------------ 1 file changed, 17 insertions(+), 29 deletions(-) diff --git a/src/Components/Blazor/Build/src/Tasks/GenerateTypeGranularityLinkingConfig.cs b/src/Components/Blazor/Build/src/Tasks/GenerateTypeGranularityLinkingConfig.cs index 2c9c390d69e5..d98afd88dfe2 100644 --- a/src/Components/Blazor/Build/src/Tasks/GenerateTypeGranularityLinkingConfig.cs +++ b/src/Components/Blazor/Build/src/Tasks/GenerateTypeGranularityLinkingConfig.cs @@ -1,9 +1,8 @@ // 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.IO; -using System.Xml; +using System.Xml.Linq; using Microsoft.Build.Framework; using Microsoft.Build.Utilities; @@ -19,42 +18,31 @@ public class GenerateTypeGranularityLinkingConfig : Task public override bool Execute() { - using (var fileStream = File.Open(OutputPath, FileMode.Create)) - using (var xmlWriter = XmlWriter.Create(fileStream, new XmlWriterSettings { Indent = true })) - { - xmlWriter.WriteStartDocument(); - xmlWriter.WriteComment(" THIS IS A GENERATED FILE - DO NOT EDIT MANUALLY "); - - xmlWriter.WriteStartElement("linker"); - - foreach (var assembly in Assemblies) - { - AddTypeGranularityConfig(xmlWriter, assembly); - } - - xmlWriter.WriteEndElement(); // linker + var linkerElement = new XElement("linker"); + linkerElement.Add(new XComment(" THIS IS A GENERATED FILE - DO NOT EDIT MANUALLY ")); - xmlWriter.WriteEndDocument(); - xmlWriter.Flush(); + foreach (var assembly in Assemblies) + { + var assemblyElement = CreateTypeGranularityConfig(assembly); + linkerElement.Add(assemblyElement); } + using var fileStream = File.Open(OutputPath, FileMode.Create); + new XDocument(linkerElement).Save(fileStream); + return true; } - private void AddTypeGranularityConfig(XmlWriter xmlWriter, ITaskItem assembly) + private XElement CreateTypeGranularityConfig(ITaskItem assembly) { - xmlWriter.WriteStartElement("assembly"); - xmlWriter.WriteAttributeString("fullname", Path.GetFileNameWithoutExtension(assembly.ItemSpec)); - // We match all types in the assembly, and for each one, tell the linker to preserve all // its members (preserve=all) but only if there's some reference to the type (required=false) - xmlWriter.WriteStartElement("type"); - xmlWriter.WriteAttributeString("fullname", "*"); - xmlWriter.WriteAttributeString("preserve", "all"); - xmlWriter.WriteAttributeString("required", "false"); - xmlWriter.WriteEndElement(); // type - - xmlWriter.WriteEndElement(); // assembly + return new XElement("assembly", + new XAttribute("fullname", Path.GetFileNameWithoutExtension(assembly.ItemSpec)), + new XElement("type", + new XAttribute("fullname", "*"), + new XAttribute("preserve", "all"), + new XAttribute("required", "false"))); } } } From 63f351602af9d14a31dd30bfb3f43f855992b3ec Mon Sep 17 00:00:00 2001 From: Steve Sanderson Date: Thu, 9 Jan 2020 14:59:29 +0000 Subject: [PATCH 7/8] Super minor tweak --- .../Build/src/Tasks/GenerateTypeGranularityLinkingConfig.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Components/Blazor/Build/src/Tasks/GenerateTypeGranularityLinkingConfig.cs b/src/Components/Blazor/Build/src/Tasks/GenerateTypeGranularityLinkingConfig.cs index d98afd88dfe2..8a56b7fc3deb 100644 --- a/src/Components/Blazor/Build/src/Tasks/GenerateTypeGranularityLinkingConfig.cs +++ b/src/Components/Blazor/Build/src/Tasks/GenerateTypeGranularityLinkingConfig.cs @@ -18,8 +18,8 @@ public class GenerateTypeGranularityLinkingConfig : Task public override bool Execute() { - var linkerElement = new XElement("linker"); - linkerElement.Add(new XComment(" THIS IS A GENERATED FILE - DO NOT EDIT MANUALLY ")); + var linkerElement = new XElement("linker", + new XComment(" THIS IS A GENERATED FILE - DO NOT EDIT MANUALLY ")); foreach (var assembly in Assemblies) { From 60b6f24370032349568f405bd60e060a18e41a49 Mon Sep 17 00:00:00 2001 From: Steve Sanderson Date: Fri, 10 Jan 2020 09:57:14 +0000 Subject: [PATCH 8/8] Update src/Components/Blazor/Build/src/targets/Blazor.MonoRuntime.targets Co-Authored-By: Pranav K --- .../Blazor/Build/src/targets/Blazor.MonoRuntime.targets | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Components/Blazor/Build/src/targets/Blazor.MonoRuntime.targets b/src/Components/Blazor/Build/src/targets/Blazor.MonoRuntime.targets index ecd5f0de6041..4bdb2f431156 100644 --- a/src/Components/Blazor/Build/src/targets/Blazor.MonoRuntime.targets +++ b/src/Components/Blazor/Build/src/targets/Blazor.MonoRuntime.targets @@ -211,6 +211,7 @@ +