diff options
8 files changed, 140 insertions, 1 deletions
diff --git a/src/Components/WebAssembly/Build/src/targets/Blazor.MonoRuntime.props b/src/Components/WebAssembly/Build/src/targets/Blazor.MonoRuntime.props index 45223bca93..453deddacd 100644 --- a/src/Components/WebAssembly/Build/src/targets/Blazor.MonoRuntime.props +++ b/src/Components/WebAssembly/Build/src/targets/Blazor.MonoRuntime.props @@ -9,6 +9,7 @@ <_BlazorRuntimeBinOutputPath>$(_BaseBlazorRuntimeOutputPath)_bin\</_BlazorRuntimeBinOutputPath> <_BlazorRuntimeWasmOutputPath>$(_BaseBlazorRuntimeOutputPath)wasm\</_BlazorRuntimeWasmOutputPath> <_BlazorBuiltInBclLinkerDescriptor>$(MSBuildThisFileDirectory)BuiltInBclLinkerDescriptor.xml</_BlazorBuiltInBclLinkerDescriptor> + <_BlazorCollationLinkerDescriptor>$(MSBuildThisFileDirectory)CollationLinkerDescriptor.xml</_BlazorCollationLinkerDescriptor> <_BlazorBootJsonName>blazor.boot.json</_BlazorBootJsonName> </PropertyGroup> diff --git a/src/Components/WebAssembly/Build/src/targets/Blazor.MonoRuntime.targets b/src/Components/WebAssembly/Build/src/targets/Blazor.MonoRuntime.targets index 1b7614bbae..8cb135f996 100644 --- a/src/Components/WebAssembly/Build/src/targets/Blazor.MonoRuntime.targets +++ b/src/Components/WebAssembly/Build/src/targets/Blazor.MonoRuntime.targets @@ -218,6 +218,10 @@ <_BlazorLinkerRoot Include="@(_BlazorRuntimeCopyLocalItems)" Condition="'%(_BlazorRuntimeCopyLocalItems.IsLinkable)' != 'true'" /> </ItemGroup> + <!-- When specifically requested, include the linker substitutions file that strips out collation information.--> + <PropertyGroup Condition="'$(BlazorWebAssemblyPreserveCollationData)' == 'false'"> + <AdditionalMonoLinkerOptions>$(AdditionalMonoLinkerOptions) --substitutions "$(_BlazorCollationLinkerDescriptor)"</AdditionalMonoLinkerOptions> + </PropertyGroup> </Target> <UsingTask TaskName="BlazorCreateRootDescriptorFile" AssemblyFile="$(_BlazorTasksPath)" /> diff --git a/src/Components/WebAssembly/Build/src/targets/CollationLinkerDescriptor.xml b/src/Components/WebAssembly/Build/src/targets/CollationLinkerDescriptor.xml new file mode 100644 index 0000000000..693bdb6768 --- /dev/null +++ b/src/Components/WebAssembly/Build/src/targets/CollationLinkerDescriptor.xml @@ -0,0 +1,21 @@ +<linker> + <!-- This file disables exclusions that remove collation data --> + + <assembly fullname="mscorlib"> + <resource name="collation.cjkCHS.bin" action="remove"/> + <resource name="collation.cjkCHT.bin" action="remove"/> + <resource name="collation.cjkJA.bin" action="remove"/> + <resource name="collation.cjkKO.bin" action="remove"/> + <resource name="collation.cjkKOlv2.bin" action="remove"/> + <resource name="collation.core.bin" action="remove"/> + <resource name="collation.tailoring.bin" action="remove"/> + + <type fullname="System.Globalization.CompareInfo"> + <method signature="System.Boolean get_IgnoreCaseNotSupported()" body="stub" value="true"/> + </type> + + <type fullname="System.Globalization.CompareInfo"> + <method signature="System.Boolean get_UseManagedCollation()" body="stub" value="false"/> + </type> + </assembly> +</linker>
\ No newline at end of file diff --git a/src/Components/WebAssembly/Build/test/BuildIntegrationTests/Assert.cs b/src/Components/WebAssembly/Build/test/BuildIntegrationTests/Assert.cs index 75c223f742..aaf3e508b5 100644 --- a/src/Components/WebAssembly/Build/test/BuildIntegrationTests/Assert.cs +++ b/src/Components/WebAssembly/Build/test/BuildIntegrationTests/Assert.cs @@ -534,7 +534,7 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Build { 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 => { @@ -544,6 +544,44 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Build } } + public static void AssemblyContainsResource(MSBuildResult result, string assemblyPath, string resourceName) + { + if (result == null) + { + throw new ArgumentNullException(nameof(result)); + } + + assemblyPath = Path.Combine(result.Project.DirectoryPath, Path.Combine(assemblyPath)); + + var resources = GetAssemblyResourceNames(assemblyPath); + Assert.Contains(resourceName, resources); + } + + public static void AssemblyDoesNotContainResource(MSBuildResult result, string assemblyPath, string resourceName) + { + if (result == null) + { + throw new ArgumentNullException(nameof(result)); + } + + assemblyPath = Path.Combine(result.Project.DirectoryPath, Path.Combine(assemblyPath)); + + var resources = GetAssemblyResourceNames(assemblyPath); + Assert.DoesNotContain(resourceName, resources); + } + + private static IEnumerable<string> GetAssemblyResourceNames(string assemblyPath) + { + using var file = File.OpenRead(assemblyPath); + using var peReader = new PEReader(file); + var metadataReader = peReader.GetMetadataReader(); + return metadataReader.ManifestResources.Where(r => !r.IsNil).Select(r => + { + var resource = metadataReader.GetManifestResource(r); + return metadataReader.GetString(resource.Name); + }).ToArray(); + } + public static void AssemblyHasAttribute(MSBuildResult result, string assemblyPath, string fullTypeName) { if (result == null) diff --git a/src/Components/WebAssembly/Build/test/BuildIntegrationTests/PublishIntegrationTest.cs b/src/Components/WebAssembly/Build/test/BuildIntegrationTests/PublishIntegrationTest.cs index cb9957fcbc..20c7bed354 100644 --- a/src/Components/WebAssembly/Build/test/BuildIntegrationTests/PublishIntegrationTest.cs +++ b/src/Components/WebAssembly/Build/test/BuildIntegrationTests/PublishIntegrationTest.cs @@ -60,6 +60,9 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Build serviceWorkerPath: Path.Combine("serviceworkers", "my-service-worker.js"), serviceWorkerContent: "// This is the production service worker", assetsManifestPath: "custom-service-worker-assets.js"); + + // When publishing without linker, we expect to have collation information present in mscorlib.dll + Assert.AssemblyContainsResource(result, Path.Combine(blazorPublishDirectory, "_framework", "_bin", "mscorlib.dll"), "collation.cjkCHS.bin"); } [Fact] @@ -99,6 +102,31 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Build // Verify web.config Assert.FileExists(result, publishDirectory, "web.config"); Assert.FileCountEquals(result, 1, publishDirectory, "*", SearchOption.TopDirectoryOnly); + + // When publishing with linker, we expect to have collation information present in mscorlib.dll + Assert.AssemblyContainsResource(result, Path.Combine(blazorPublishDirectory, "_framework", "_bin", "mscorlib.dll"), "collation.cjkCHS.bin"); + } + + [Fact] + public async Task Publish_LinkerEnabledAndBlazorWebAssemblyPreserveCollationDataSet_CollationInformationIsPreserved() + { + // Arrange + using var project = ProjectDirectory.Create("standalone", additionalProjects: new[] { "razorclasslibrary" }); + project.Configuration = "Release"; + project.AddProjectFileContent( +@"<PropertyGroup> + <BlazorWebAssemblyPreserveCollationData>false</BlazorWebAssemblyPreserveCollationData> +</PropertyGroup>"); + var result = await MSBuildProcessManager.DotnetMSBuild(project, "Publish"); + + Assert.BuildPassed(result); + + var publishDirectory = project.PublishOutputDirectory; + + var blazorPublishDirectory = Path.Combine(publishDirectory, "wwwroot"); + + // When publishing with BlazorWebAssemblyPreserveCollationData=false, collation information should be stripped out. + Assert.AssemblyDoesNotContainResource(result, Path.Combine(blazorPublishDirectory, "_framework", "_bin", "mscorlib.dll"), "collation.cjkCHS.bin"); } [Fact] diff --git a/src/Components/test/E2ETest/Tests/WebAssemblyStringComparisonTest.cs b/src/Components/test/E2ETest/Tests/WebAssemblyStringComparisonTest.cs new file mode 100644 index 0000000000..9a38268403 --- /dev/null +++ b/src/Components/test/E2ETest/Tests/WebAssemblyStringComparisonTest.cs @@ -0,0 +1,35 @@ +// 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 BasicTestApp; +using Microsoft.AspNetCore.Components.E2ETest.Infrastructure; +using Microsoft.AspNetCore.Components.E2ETest.Infrastructure.ServerFixtures; +using Microsoft.AspNetCore.E2ETesting; +using OpenQA.Selenium; +using Xunit; +using Xunit.Abstractions; + +namespace Microsoft.AspNetCore.Components.E2ETest.Tests +{ + public class WebAssemblyStringComparisonTest : ServerTestBase<ToggleExecutionModeServerFixture<Program>> + { + public WebAssemblyStringComparisonTest( + BrowserFixture browserFixture, + ToggleExecutionModeServerFixture<Program> serverFixture, + ITestOutputHelper output) + : base(browserFixture, serverFixture, output) + { + } + + [Fact] + public void InvariantCultureWorksAsExpected() + { + Navigate(ServerPathBase, noReload: false); + Browser.MountTestComponent<StringComparisonComponent>(); + + var result = Browser.Exists(By.Id("results")); + + Assert.Equal("Ordinal: False Invariant: True", result.Text); + } + } +} diff --git a/src/Components/test/testassets/BasicTestApp/Index.razor b/src/Components/test/testassets/BasicTestApp/Index.razor index 85dfc1f22f..7a0c524cf8 100644 --- a/src/Components/test/testassets/BasicTestApp/Index.razor +++ b/src/Components/test/testassets/BasicTestApp/Index.razor @@ -71,6 +71,7 @@ <option value="BasicTestApp.RouterTest.NavigationManagerComponent">NavigationManager Test</option> <option value="BasicTestApp.RouterTest.TestRouter">Router</option> <option value="BasicTestApp.RouterTest.TestRouterWithAdditionalAssembly">Router with additional assembly</option> + <option value="BasicTestApp.StringComparisonComponent">StringComparison</option> <option value="BasicTestApp.SvgComponent">SVG</option> <option value="BasicTestApp.SvgWithChildComponent">SVG with child component</option> <option value="BasicTestApp.TextOnlyComponent">Plain text</option> diff --git a/src/Components/test/testassets/BasicTestApp/StringComparisonComponent.razor b/src/Components/test/testassets/BasicTestApp/StringComparisonComponent.razor new file mode 100644 index 0000000000..7983a8558e --- /dev/null +++ b/src/Components/test/testassets/BasicTestApp/StringComparisonComponent.razor @@ -0,0 +1,11 @@ +@{ + // This test verifies that invariant cultue works correctly in WebAssembly environments. The test case is based on the discussions here: https://stackoverflow.com/a/20085219 + var string1 = "Strasse"; + var string2 = "Straße"; + + var ordinalComparison = string1.Equals(string2, StringComparison.Ordinal); + var invariantComparison = string1.Equals(string2, StringComparison.InvariantCulture); +} + +<div id="results">Ordinal: @ordinalComparison Invariant: @invariantComparison</div> + |