From 84b9b9bb757c6f54cb35c497cdfa6d37ff547a79 Mon Sep 17 00:00:00 2001 From: Ankit Jain Date: Sun, 6 Jun 2021 20:25:32 -0400 Subject: [PATCH 1/6] [wasm] Don't use DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=1 .. because we want to test satellite assemblies which will needs cultures. --- .../BuildWasmApps/Wasm.Build.Tests/Wasm.Build.Tests.csproj | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/Wasm.Build.Tests.csproj b/src/tests/BuildWasmApps/Wasm.Build.Tests/Wasm.Build.Tests.csproj index 9ed56506d799fd..754415af6333d2 100644 --- a/src/tests/BuildWasmApps/Wasm.Build.Tests/Wasm.Build.Tests.csproj +++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/Wasm.Build.Tests.csproj @@ -20,8 +20,6 @@ <_PreCommand Condition="'$(OS)' != 'Windows_NT'">WasmBuildSupportDir=%24{HELIX_CORRELATION_PAYLOAD}/build <_PreCommand Condition="'$(OS)' == 'Windows_NT'">set WasmBuildSupportDir=%HELIX_CORRELATION_PAYLOAD%/build & - <_PreCommand Condition="'$(OS)' != 'Windows_NT'">$(_PreCommand) DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=1 - <_PreCommand Condition="'$(OS)' == 'Windows_NT'">$(_PreCommand) set DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=1 & From 54489629a64e1fda9133fad0c5b6fc92e143288b Mon Sep 17 00:00:00 2001 From: Ankit Jain Date: Sun, 6 Jun 2021 20:30:48 -0400 Subject: [PATCH 2/6] Wasm.Build.Tests: cleanup --- .../Wasm.Build.Tests/BuildTestBase.cs | 57 ++++++++++++++----- .../InvariantGlobalizationTests.cs | 2 +- .../Wasm.Build.Tests/MainWithArgsTests.cs | 2 +- .../Wasm.Build.Tests/NativeBuildTests.cs | 4 +- .../Wasm.Build.Tests/RebuildTests.cs | 2 +- .../SharedBuildPerTestClassFixture.cs | 4 +- .../Wasm.Build.Tests/WasmBuildAppTest.cs | 6 +- 7 files changed, 53 insertions(+), 24 deletions(-) diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/BuildTestBase.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/BuildTestBase.cs index b5f1f7e0b95f73..689db614dca74b 100644 --- a/src/tests/BuildWasmApps/Wasm.Build.Tests/BuildTestBase.cs +++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/BuildTestBase.cs @@ -8,9 +8,11 @@ using System.Diagnostics.CodeAnalysis; using System.IO; using System.Linq; +using System.Reflection; using System.Text; using Xunit; using Xunit.Abstractions; +using Xunit.Sdk; #nullable enable @@ -29,7 +31,6 @@ public abstract class BuildTestBase : IClassFixture test, string? buildDir=null, int expectedExitCode=0, string? args=null) + protected string RunAndTestWasmApp(BuildArgs buildArgs, + RunHost host, + string id, + Action? test=null, + string? buildDir = null, + int expectedExitCode = 0, + string? args = null, + Dictionary? envVars = null) { buildDir ??= _projectDir; - Dictionary? envVars = new(); + envVars ??= new(); envVars["XHARNESS_DISABLE_COLORED_OUTPUT"] = "true"; if (buildArgs.AOT) { @@ -180,6 +188,11 @@ protected void RunAndTestWasmApp(BuildArgs buildArgs, RunHost host, string id, A Assert.DoesNotContain("AOT: image 'System.Private.CoreLib' found.", output); Assert.DoesNotContain($"AOT: image '{buildArgs.ProjectName}' found.", output); } + + if (test != null) + test(output); + + return output; } protected static string RunWithXHarness(string testCommand, string testLogPath, string projectName, string bundleDir, @@ -209,6 +222,8 @@ protected static string RunWithXHarness(string testCommand, string testLogPath, args.Append($" --run {projectName}.dll"); args.Append($" {appArgs ?? string.Empty}"); + _testOutput.WriteLine(string.Empty); + _testOutput.WriteLine($"---------- Running with {testCommand} ---------"); var (exitCode, output) = RunProcess("dotnet", _testOutput, args: args.ToString(), workingDir: bundleDir, @@ -251,14 +266,21 @@ protected static void InitProjectDir(string dir) runtime-test.js ##EXTRA_PROPERTIES## + + ##EXTRA_ITEMS## + + ##INSERT_AT_END## "; - protected static BuildArgs GetBuildArgsWith(BuildArgs buildArgs, string? extraProperties=null, string projectTemplate=SimpleProjectTemplate) + protected static BuildArgs ExpandBuildArgs(BuildArgs buildArgs, string extraProperties="", string extraItems="", string insertAtEnd="", string projectTemplate=SimpleProjectTemplate) { if (buildArgs.AOT) - extraProperties = $"{extraProperties}\ntrue\n"; + extraProperties = $"{extraProperties}\ntrue\nfalse\n"; - string projectContents = projectTemplate.Replace("##EXTRA_PROPERTIES##", extraProperties ?? string.Empty); + string projectContents = projectTemplate + .Replace("##EXTRA_PROPERTIES##", extraProperties) + .Replace("##EXTRA_ITEMS##", extraItems) + .Replace("##INSERT_AT_END##", insertAtEnd); return buildArgs with { ProjectFileContents = projectContents }; } @@ -273,10 +295,10 @@ protected static BuildArgs GetBuildArgsWith(BuildArgs buildArgs, string? extraPr { if (useCache && _buildContext.TryGetBuildFor(buildArgs, out BuildProduct? product)) { - Console.WriteLine ($"Using existing build found at {product.BuildPath}, with build log at {product.LogFile}"); + Console.WriteLine ($"Using existing build found at {product.ProjectDir}, with build log at {product.LogFile}"); - Assert.True(product.Result, $"Found existing build at {product.BuildPath}, but it had failed. Check build log at {product.LogFile}"); - _projectDir = product.BuildPath; + Assert.True(product.Result, $"Found existing build at {product.ProjectDir}, but it had failed. Check build log at {product.LogFile}"); + _projectDir = product.ProjectDir; // use this test's id for the run logs _logPath = Path.Combine(s_logRoot, id); @@ -297,7 +319,6 @@ protected static BuildArgs GetBuildArgsWith(BuildArgs buildArgs, string? extraPr throw new Exception("_projectDir should be set, to use createProject=false"); } - StringBuilder sb = new(); sb.Append("publish"); sb.Append(s_defaultBuildArgs); @@ -305,6 +326,7 @@ protected static BuildArgs GetBuildArgsWith(BuildArgs buildArgs, string? extraPr sb.Append($" /p:Configuration={buildArgs.Config}"); string logFilePath = Path.Combine(_logPath, $"{buildArgs.ProjectName}.binlog"); + _testOutput.WriteLine($"-------- Building ---------"); _testOutput.WriteLine($"Binlog path: {logFilePath}"); sb.Append($" /bl:\"{logFilePath}\" /v:minimal /nologo"); if (buildArgs.ExtraBuildArgs != null) @@ -378,8 +400,14 @@ protected static void AssertDotNetWasmJs(string bundleDir, bool fromRuntimePack) { string nativeDir = GetRuntimeNativeDir(); - AssertFile(Path.Combine(nativeDir, "dotnet.wasm"), Path.Combine(bundleDir, "dotnet.wasm"), "Expected dotnet.wasm to be same as the runtime pack", same: fromRuntimePack); - AssertFile(Path.Combine(nativeDir, "dotnet.js"), Path.Combine(bundleDir, "dotnet.js"), "Expected dotnet.js to be same as the runtime pack", same: fromRuntimePack); + AssertNativeFile("dotnet.wasm"); + AssertNativeFile("dotnet.js"); + + void AssertNativeFile(string file) + => AssertFile(Path.Combine(nativeDir, file), + Path.Combine(bundleDir, file), + $"Expected {file} to be {(fromRuntimePack ? "the same as" : "different from")} the runtime pack", + same: fromRuntimePack); } protected static void AssertFilesDontExist(string dir, string[] filenames, string? label = null) @@ -464,6 +492,7 @@ public static (int exitCode, string buildOutput) RunProcess(string path, _testOutput.WriteLine($"Running {path} {args}"); Console.WriteLine($"Running: {path}: {args}"); Console.WriteLine($"WorkingDirectory: {workingDir}"); + _testOutput.WriteLine($"WorkingDirectory: {workingDir}"); StringBuilder outputBuilder = new (); var processStartInfo = new ProcessStartInfo { @@ -584,7 +613,7 @@ public static int Main() - + @@ -594,5 +623,5 @@ public static int Main() } public record BuildArgs(string ProjectName, string Config, bool AOT, string ProjectFileContents, string? ExtraBuildArgs); - public record BuildProduct(string BuildPath, string LogFile, bool Result); + public record BuildProduct(string ProjectDir, string LogFile, bool Result); } diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/InvariantGlobalizationTests.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/InvariantGlobalizationTests.cs index 6d01f20a8420e8..6bede4e16c2c5e 100644 --- a/src/tests/BuildWasmApps/Wasm.Build.Tests/InvariantGlobalizationTests.cs +++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/InvariantGlobalizationTests.cs @@ -49,7 +49,7 @@ private void TestInvariantGlobalization(BuildArgs buildArgs, bool? invariantGlob extraProperties = $"{extraProperties}{invariantGlobalization}"; buildArgs = buildArgs with { ProjectName = projectName }; - buildArgs = GetBuildArgsWith(buildArgs, extraProperties); + buildArgs = ExpandBuildArgs(buildArgs, extraProperties); if (dotnetWasmFromRuntimePack == null) dotnetWasmFromRuntimePack = !(buildArgs.AOT || buildArgs.Config == "Release"); diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/MainWithArgsTests.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/MainWithArgsTests.cs index df920577cea01b..b97a0490d2e872 100644 --- a/src/tests/BuildWasmApps/Wasm.Build.Tests/MainWithArgsTests.cs +++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/MainWithArgsTests.cs @@ -77,7 +77,7 @@ void TestMainWithArgs(string projectNamePrefix, string programText = projectContents.Replace("##CODE##", code); buildArgs = buildArgs with { ProjectName = projectName, ProjectFileContents = programText }; - buildArgs = GetBuildArgsWith(buildArgs); + buildArgs = ExpandBuildArgs(buildArgs); if (dotnetWasmFromRuntimePack == null) dotnetWasmFromRuntimePack = !(buildArgs.AOT || buildArgs.Config == "Release"); diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/NativeBuildTests.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/NativeBuildTests.cs index da7b33d0a0c351..9f87757175c970 100644 --- a/src/tests/BuildWasmApps/Wasm.Build.Tests/NativeBuildTests.cs +++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/NativeBuildTests.cs @@ -39,7 +39,7 @@ public void Relinking_ErrorWhenMissingEMSDK(BuildArgs buildArgs, string emsdkPat ProjectName = projectName, ExtraBuildArgs = $"/p:EMSDK_PATH={emsdkPath}" }; - buildArgs = GetBuildArgsWith(buildArgs, extraProperties: "true"); + buildArgs = ExpandBuildArgs(buildArgs, extraProperties: "true"); (_, string buildOutput) = BuildProject(buildArgs, initProject: () => File.WriteAllText(Path.Combine(_projectDir!, "Program.cs"), s_mainReturns42), @@ -54,7 +54,7 @@ private void NativeBuild(string projectNamePrefix, string projectContents, Build string projectName = $"{projectNamePrefix}_{buildArgs.Config}_{buildArgs.AOT}"; buildArgs = buildArgs with { ProjectName = projectName, ProjectFileContents = projectContents }; - buildArgs = GetBuildArgsWith(buildArgs, extraProperties: "true"); + buildArgs = ExpandBuildArgs(buildArgs, extraProperties: "true"); Console.WriteLine ($"-- args: {buildArgs}, name: {projectName}"); BuildProject(buildArgs, diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/RebuildTests.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/RebuildTests.cs index aebaa43ba5d97d..ecbcbdd75c05c2 100644 --- a/src/tests/BuildWasmApps/Wasm.Build.Tests/RebuildTests.cs +++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/RebuildTests.cs @@ -27,7 +27,7 @@ public void NoOpRebuild(BuildArgs buildArgs, bool nativeRelink, RunHost host, st bool dotnetWasmFromRuntimePack = !nativeRelink && !buildArgs.AOT; buildArgs = buildArgs with { ProjectName = projectName }; - buildArgs = GetBuildArgsWith(buildArgs, $"{(nativeRelink ? "true" : "false")}"); + buildArgs = ExpandBuildArgs(buildArgs, $"{(nativeRelink ? "true" : "false")}"); BuildProject(buildArgs, initProject: () => File.WriteAllText(Path.Combine(_projectDir!, "Program.cs"), s_mainReturns42), diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/SharedBuildPerTestClassFixture.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/SharedBuildPerTestClassFixture.cs index a491d972af28c5..e84a151bd5b02d 100644 --- a/src/tests/BuildWasmApps/Wasm.Build.Tests/SharedBuildPerTestClassFixture.cs +++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/SharedBuildPerTestClassFixture.cs @@ -20,7 +20,7 @@ public void CacheBuild(BuildArgs buildArgs, BuildProduct product) public void RemoveFromCache(string buildPath) { - KeyValuePair? foundKvp = _buildPaths.Where(kvp => kvp.Value.BuildPath == buildPath).SingleOrDefault(); + KeyValuePair? foundKvp = _buildPaths.Where(kvp => kvp.Value.ProjectDir == buildPath).SingleOrDefault(); if (foundKvp == null) throw new Exception($"Could not find build path {buildPath} in cache to remove."); @@ -36,7 +36,7 @@ public void Dispose() Console.WriteLine ($"============== DELETING THE BUILDS ============="); foreach (var kvp in _buildPaths.Values) { - RemoveDirectory(kvp.BuildPath); + RemoveDirectory(kvp.ProjectDir); } } diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/WasmBuildAppTest.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/WasmBuildAppTest.cs index 3778d1fcdaae30..03a48d903f66b8 100644 --- a/src/tests/BuildWasmApps/Wasm.Build.Tests/WasmBuildAppTest.cs +++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/WasmBuildAppTest.cs @@ -72,7 +72,7 @@ public void AOT_ErrorWhenMissingEMSDK(BuildArgs buildArgs, string emsdkPath, str ProjectName = projectName, ExtraBuildArgs = $"/p:EMSDK_PATH={emsdkPath}" }; - buildArgs = GetBuildArgsWith(buildArgs); + buildArgs = ExpandBuildArgs(buildArgs); (_, string buildOutput) = BuildProject(buildArgs, initProject: () => File.WriteAllText(Path.Combine(_projectDir!, "Program.cs"), s_mainReturns42), @@ -109,11 +109,11 @@ void TestMain(string projectName, BuildArgs buildArgs, RunHost host, string id, - string? extraProperties = null, + string extraProperties = "", bool? dotnetWasmFromRuntimePack = null) { buildArgs = buildArgs with { ProjectName = projectName }; - buildArgs = GetBuildArgsWith(buildArgs, extraProperties); + buildArgs = ExpandBuildArgs(buildArgs, extraProperties); if (dotnetWasmFromRuntimePack == null) dotnetWasmFromRuntimePack = !(buildArgs.AOT || buildArgs.Config == "Release"); From 065688a0e7f8868a2caadbae1b934b5dcfa928f8 Mon Sep 17 00:00:00 2001 From: Ankit Jain Date: Sun, 6 Jun 2021 20:31:56 -0400 Subject: [PATCH 3/6] [wasm] Fix handling satellite assemblies Currently we have a `@(WasmSatelliteAssemblies)` which gets populated by the user project. Or in case of blazor, it isn't used at all. This commit changes: - automatically, identify the satellite assemblies from the list of given assemblies - it does this based purely on filename ending in `*.resources.dll` right now - And it takes the parent directory as the culture - this doesn't try to validate that culture though. And we will have better integration with the build later, in which case we would have validated list of satellite assemblies Also, this fixes https://github.com/dotnet/runtime/issues/53431 : - Blazor passes a list of assemblies to the wasm targets for AOT, which includes the satellite assemblies. - We pass all of these to `MonoAOTCompiler`, which emits .bc files for each of them - but for satellite assemblies like `fr/foo.resources.dll`, and `cs/foo.resources.dll` - we get a single `foo.resources.dll.bc` - this causes the total number of input assemblies to not match the total number of .bc files produced! Fix: - remove the extra check (we'll add better ways for doing that in an upcoming change) - And don't include the satellite assemblies for the AOT process --- eng/testing/tests.wasm.targets | 19 +- src/mono/wasm/build/WasmApp.Native.targets | 4 +- src/mono/wasm/build/WasmApp.targets | 16 +- .../aot-tests/AOTTestProjectForHelix.proj | 8 +- src/tasks/WasmAppBuilder/WasmAppBuilder.cs | 7 + .../SatelliteAssembliesTests.cs | 179 ++++++++++++++++++ .../Wasm.Build.Tests/Wasm.Build.Tests.csproj | 3 + .../testassets/LibraryWithResources/Class1.cs | 8 + .../Directory.Build.props | 1 + .../Directory.Build.targets | 1 + .../LibraryWithResources.csproj | 10 + .../testassets/resx/words.es-ES.resx | 127 +++++++++++++ .../testassets/resx/words.ja-JP.resx | 127 +++++++++++++ .../BuildWasmApps/testassets/resx/words.resx | 127 +++++++++++++ 14 files changed, 611 insertions(+), 26 deletions(-) create mode 100644 src/tests/BuildWasmApps/Wasm.Build.Tests/SatelliteAssembliesTests.cs create mode 100644 src/tests/BuildWasmApps/testassets/LibraryWithResources/Class1.cs create mode 100644 src/tests/BuildWasmApps/testassets/LibraryWithResources/Directory.Build.props create mode 100644 src/tests/BuildWasmApps/testassets/LibraryWithResources/Directory.Build.targets create mode 100644 src/tests/BuildWasmApps/testassets/LibraryWithResources/LibraryWithResources.csproj create mode 100644 src/tests/BuildWasmApps/testassets/resx/words.es-ES.resx create mode 100644 src/tests/BuildWasmApps/testassets/resx/words.ja-JP.resx create mode 100644 src/tests/BuildWasmApps/testassets/resx/words.resx diff --git a/eng/testing/tests.wasm.targets b/eng/testing/tests.wasm.targets index ce10194388d550..703392561fc414 100644 --- a/eng/testing/tests.wasm.targets +++ b/eng/testing/tests.wasm.targets @@ -69,8 +69,7 @@ - - + @@ -124,17 +123,17 @@ -1 - - - + + <_SatelliteAssemblies Include="$(PublishDir)*\*.resources.dll" /> + <_SatelliteAssemblies CultureName="$([System.IO.Directory]::GetParent('%(Identity)').Name)" /> + <_SatelliteAssemblies TargetPath="%(CultureName)\%(FileName)%(Extension)" /> - + + + + - <_CopyLocalPaths Include="@(PublishItemsOutputGroupOutputs)" diff --git a/src/mono/wasm/build/WasmApp.Native.targets b/src/mono/wasm/build/WasmApp.Native.targets index 5c4ef04bf95720..66ee61e2a755e2 100644 --- a/src/mono/wasm/build/WasmApp.Native.targets +++ b/src/mono/wasm/build/WasmApp.Native.targets @@ -352,6 +352,7 @@ EMSCRIPTEN_KEEPALIVE void mono_wasm_load_profiler_aot (const char *desc) { mono_ @(MonoAOTCompilerDefaultAotArguments, ';') @(MonoAOTCompilerDefaultProcessArguments, ';') + <_AOT_InternalForceInterpretAssemblies Include="@(_WasmAssembliesInternal->WithMetadataValue('_InternalForceInterpret', 'true'))" /> <_WasmAssembliesInternal Remove="@(_WasmAssembliesInternal)" /> @@ -421,9 +422,6 @@ EMSCRIPTEN_KEEPALIVE void mono_wasm_load_profiler_aot (const char *desc) { mono_ <_WasmNativeFileForLinking Include="@(_BitcodeFile)" /> - - diff --git a/src/mono/wasm/build/WasmApp.targets b/src/mono/wasm/build/WasmApp.targets index 2141cce41b69b2..11c38d92f6e1d4 100644 --- a/src/mono/wasm/build/WasmApp.targets +++ b/src/mono/wasm/build/WasmApp.targets @@ -61,7 +61,6 @@ Public items: - @(WasmExtraFilesToDeploy) - Files to copy to $(WasmAppDir). (relative path can be set via %(TargetPath) metadata) - - @(WasmSatelliteAssemblies) - @(WasmFilesToIncludeInFileSystem) - Files to include in the vfs - @(WasmNativeAsset) - Native files to be added to `NativeAssets` in the bundle. @@ -115,6 +114,13 @@ <_WasmAssembliesInternal Include="@(WasmAssembliesToBundle->Distinct())" /> + + <_WasmSatelliteAssemblies Include="@(_WasmAssembliesInternal)" /> + <_WasmSatelliteAssemblies Remove="@(_WasmSatelliteAssemblies)" Condition="!$([System.String]::Copy('%(Identity)').EndsWith('.resources.dll'))" /> + + <_WasmSatelliteAssemblies CultureName="$([System.IO.Directory]::GetParent('%(Identity)').Name)" /> + + <_WasmAssembliesInternal Remove="@(_WasmSatelliteAssemblies)" /> @@ -142,7 +148,7 @@ MainJS="$(WasmMainJSPath)" Assemblies="@(_WasmAssembliesInternal)" InvariantGlobalization="$(InvariantGlobalization)" - SatelliteAssemblies="@(WasmSatelliteAssemblies)" + SatelliteAssemblies="@(_WasmSatelliteAssemblies)" FilesToIncludeInFileSystem="@(WasmFilesToIncludeInFileSystem)" IcuDataFileName="$(WasmIcuDataFileName)" RemoteSources="@(WasmRemoteSources)" @@ -193,11 +199,5 @@ - - - diff --git a/src/mono/wasm/data/aot-tests/AOTTestProjectForHelix.proj b/src/mono/wasm/data/aot-tests/AOTTestProjectForHelix.proj index f7b9d3fcc4fd0e..41ead6b8c2a558 100644 --- a/src/mono/wasm/data/aot-tests/AOTTestProjectForHelix.proj +++ b/src/mono/wasm/data/aot-tests/AOTTestProjectForHelix.proj @@ -28,17 +28,15 @@ - - - - + <_ExtraFiles Include="$(ExtraFilesPath)**\*" /> - <_SatelliteAssembliesForVFS Include="@(WasmSatelliteAssemblies)" /> + <_SatelliteAssembliesForVFS Include="$(OriginalPublishDir)**\*.resources.dll" /> + <_SatelliteAssembliesForVFS CultureName="$([System.IO.Directory]::GetParent('%(Identity)').Name)" /> <_SatelliteAssembliesForVFS TargetPath="%(CultureName)\%(FileName)%(Extension)" /> diff --git a/src/tasks/WasmAppBuilder/WasmAppBuilder.cs b/src/tasks/WasmAppBuilder/WasmAppBuilder.cs index 9ef7679c292b66..b0280175d2ce91 100644 --- a/src/tasks/WasmAppBuilder/WasmAppBuilder.cs +++ b/src/tasks/WasmAppBuilder/WasmAppBuilder.cs @@ -189,6 +189,13 @@ public override bool Execute () { string culture = assembly.GetMetadata("CultureName") ?? string.Empty; string fullPath = assembly.GetMetadata("Identity"); + if (string.IsNullOrEmpty(culture)) + { + Log.LogWarning($"Missing CultureName metadata for satellite assembly {fullPath}"); + continue; + } + // FIXME: validate the culture? + string name = Path.GetFileName(fullPath); string directory = Path.Combine(AppDir, config.AssemblyRoot, culture); Directory.CreateDirectory(directory); diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/SatelliteAssembliesTests.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/SatelliteAssembliesTests.cs new file mode 100644 index 00000000000000..bb2c8823e9a46a --- /dev/null +++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/SatelliteAssembliesTests.cs @@ -0,0 +1,179 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using System.IO; +using System.Linq; +using Xunit; +using Xunit.Abstractions; + +#nullable enable + +namespace Wasm.Build.Tests +{ + public class SatelliteAssembliesTests : BuildTestBase + { + public SatelliteAssembliesTests(ITestOutputHelper output, SharedBuildPerTestClassFixture buildContext) + : base(output, buildContext) + { + } + + public static IEnumerable SatelliteAssemblyTestData(bool aot, bool relinking, RunHost host) + => ConfigWithAOTData(aot) + .Multiply( + new object?[] { relinking, "es-ES", "got: hola" }, + new object?[] { relinking, null, "got: hello" }, + new object?[] { relinking, "ja-JP", "got: こんにちは" }) + .WithRunHosts(host) + .UnwrapItemsAsArrays(); + + [Theory] + [MemberData(nameof(SatelliteAssemblyTestData), parameters: new object[] { /*aot*/ false, /*relinking*/ false, RunHost.All })] + [MemberData(nameof(SatelliteAssemblyTestData), parameters: new object[] { /*aot*/ false, /*relinking*/ true, RunHost.All })] + [MemberData(nameof(SatelliteAssemblyTestData), parameters: new object[] { /*aot*/ true, /*relinking*/ false, RunHost.All })] + public void ResourcesFromMainAssembly(BuildArgs buildArgs, + bool nativeRelink, + string? argCulture, + string expectedOutput, + RunHost host, + string id) + { + string projectName = $"sat_asm_from_main_asm"; + bool dotnetWasmFromRuntimePack = !nativeRelink && !buildArgs.AOT; + + buildArgs = buildArgs with { ProjectName = projectName }; + buildArgs = ExpandBuildArgs(buildArgs, + projectTemplate: s_resourcesProjectTemplate, + extraProperties: $"{(nativeRelink ? "true" : "false")}", + extraItems: $""); + + BuildProject(buildArgs, + initProject: () => CreateProgramForCultureTest(projectName, "TestClass"), + dotnetWasmFromRuntimePack: dotnetWasmFromRuntimePack, + id: id); + + string output = RunAndTestWasmApp( + buildArgs, expectedExitCode: 42, + args: argCulture, + host: host, id: id); + + Assert.Contains(expectedOutput, output); + } + + [Theory] + [MemberData(nameof(SatelliteAssemblyTestData), parameters: new object[] { /*aot*/ false, /*relinking*/ false, RunHost.All })] + [MemberData(nameof(SatelliteAssemblyTestData), parameters: new object[] { /*aot*/ false, /*relinking*/ true, RunHost.All })] + [MemberData(nameof(SatelliteAssemblyTestData), parameters: new object[] { /*aot*/ true, /*relinking*/ false, RunHost.All })] + public void ResourcesFromProjectReference(BuildArgs buildArgs, + bool nativeRelink, + string? argCulture, + string expectedOutput, + RunHost host, + string id) + { + string projectName = $"sat_asm_proj_ref"; + bool dotnetWasmFromRuntimePack = !nativeRelink && !buildArgs.AOT; + + buildArgs = buildArgs with { ProjectName = projectName }; + buildArgs = ExpandBuildArgs(buildArgs, + projectTemplate: s_resourcesProjectTemplate, + extraProperties: $"{(nativeRelink ? "true" : "false")}", + extraItems: $""); + + BuildProject(buildArgs, + initProject: () => CreateProgramForCultureTest(projectName, "LibraryWithResources.Class1"), + dotnetWasmFromRuntimePack: dotnetWasmFromRuntimePack, + id: id); + + string output = RunAndTestWasmApp(buildArgs, + expectedExitCode: 42, + args: argCulture, + host: host, id: id); + + Assert.Contains(expectedOutput, output); + } + +#pragma warning disable xUnit1026 + [Theory] + [BuildAndRun(host: RunHost.None, aot: true)] + public void CheckThatSatelliteAssembliesAreNotAOTed(BuildArgs buildArgs, string id) + { + string projectName = $"check_sat_asm_not_aot"; + buildArgs = buildArgs with { ProjectName = projectName }; + buildArgs = ExpandBuildArgs(buildArgs, + projectTemplate: s_resourcesProjectTemplate, + extraProperties: $@" + -O0 + -O0", + extraItems: $""); + + System.Console.WriteLine ($"--- aot: {buildArgs.AOT}"); + BuildProject(buildArgs, + initProject: () => CreateProgramForCultureTest(projectName, "TestClass"), + dotnetWasmFromRuntimePack: false, + id: id); + + var bitCodeFileNames = Directory.GetFileSystemEntries(Path.Combine(_projectDir!, "obj"), "*.dll.bc", SearchOption.AllDirectories) + .Select(path => Path.GetFileName(path)) + .ToArray(); + + // sanity check, in case we change file extensions + Assert.Contains($"{projectName}.dll.bc", bitCodeFileNames); + + Assert.Empty(bitCodeFileNames.Where(file => file.EndsWith(".resources.dll.bc"))); + } +#pragma warning restore xUnit1026 + + private void CreateProgramForCultureTest(string projectName, string typeName) + => File.WriteAllText(Path.Combine(_projectDir!, "Program.cs"), + s_cultureResourceTestProgram + .Replace("##RESOURCE_NAME##", $"{projectName}.words") + .Replace("##TYPE_NAME##", typeName)); + + private const string s_resourcesProjectTemplate = + @$" + + {s_targetFramework} + Exe + true + runtime-test.js + ##EXTRA_PROPERTIES## + + + ##EXTRA_ITEMS## + + ##INSERT_AT_END## + "; + + private static string s_cultureResourceTestProgram = @" +using System; +using System.Runtime.CompilerServices; +using System.Globalization; +using System.Resources; +using System.Threading; + +namespace ResourcesTest +{ + public class TestClass + { + public static int Main(string[] args) + { + if (args.Length == 1) + { + string cultureToTest = args[0]; + var newCulture = new CultureInfo(cultureToTest); + Thread.CurrentThread.CurrentCulture = newCulture; + Thread.CurrentThread.CurrentUICulture = newCulture; + } + + var currentCultureName = Thread.CurrentThread.CurrentCulture.Name; + + var rm = new ResourceManager(""##RESOURCE_NAME##"", typeof(##TYPE_NAME##).Assembly); + Console.WriteLine($""For '{currentCultureName}' got: {rm.GetString(""hello"")}""); + + return 42; + } + } +}"; + } +} diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/Wasm.Build.Tests.csproj b/src/tests/BuildWasmApps/Wasm.Build.Tests/Wasm.Build.Tests.csproj index 754415af6333d2..20a1972430746f 100644 --- a/src/tests/BuildWasmApps/Wasm.Build.Tests/Wasm.Build.Tests.csproj +++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/Wasm.Build.Tests.csproj @@ -9,6 +9,7 @@ xunit false true + false TEST_DEBUG_CONFIG_ALSO false @@ -36,5 +37,7 @@ + + diff --git a/src/tests/BuildWasmApps/testassets/LibraryWithResources/Class1.cs b/src/tests/BuildWasmApps/testassets/LibraryWithResources/Class1.cs new file mode 100644 index 00000000000000..5ae850563f8ee8 --- /dev/null +++ b/src/tests/BuildWasmApps/testassets/LibraryWithResources/Class1.cs @@ -0,0 +1,8 @@ +using System; + +namespace LibraryWithResources +{ + public class Class1 + { + } +} diff --git a/src/tests/BuildWasmApps/testassets/LibraryWithResources/Directory.Build.props b/src/tests/BuildWasmApps/testassets/LibraryWithResources/Directory.Build.props new file mode 100644 index 00000000000000..058246e4086204 --- /dev/null +++ b/src/tests/BuildWasmApps/testassets/LibraryWithResources/Directory.Build.props @@ -0,0 +1 @@ + diff --git a/src/tests/BuildWasmApps/testassets/LibraryWithResources/Directory.Build.targets b/src/tests/BuildWasmApps/testassets/LibraryWithResources/Directory.Build.targets new file mode 100644 index 00000000000000..058246e4086204 --- /dev/null +++ b/src/tests/BuildWasmApps/testassets/LibraryWithResources/Directory.Build.targets @@ -0,0 +1 @@ + diff --git a/src/tests/BuildWasmApps/testassets/LibraryWithResources/LibraryWithResources.csproj b/src/tests/BuildWasmApps/testassets/LibraryWithResources/LibraryWithResources.csproj new file mode 100644 index 00000000000000..91961dd7d6b422 --- /dev/null +++ b/src/tests/BuildWasmApps/testassets/LibraryWithResources/LibraryWithResources.csproj @@ -0,0 +1,10 @@ + + + + net5.0 + + + + + + diff --git a/src/tests/BuildWasmApps/testassets/resx/words.es-ES.resx b/src/tests/BuildWasmApps/testassets/resx/words.es-ES.resx new file mode 100644 index 00000000000000..775397b15a2b9f --- /dev/null +++ b/src/tests/BuildWasmApps/testassets/resx/words.es-ES.resx @@ -0,0 +1,127 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + ciao + + + + hola + + diff --git a/src/tests/BuildWasmApps/testassets/resx/words.ja-JP.resx b/src/tests/BuildWasmApps/testassets/resx/words.ja-JP.resx new file mode 100644 index 00000000000000..c843811244c256 --- /dev/null +++ b/src/tests/BuildWasmApps/testassets/resx/words.ja-JP.resx @@ -0,0 +1,127 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + さようなら + + + + こんにちは + + diff --git a/src/tests/BuildWasmApps/testassets/resx/words.resx b/src/tests/BuildWasmApps/testassets/resx/words.resx new file mode 100644 index 00000000000000..c3d5a787420866 --- /dev/null +++ b/src/tests/BuildWasmApps/testassets/resx/words.resx @@ -0,0 +1,127 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + bye + + + + hello + + From eed6b88d2d3d4919082344fafaf4916fb1f489ce Mon Sep 17 00:00:00 2001 From: Ankit Jain Date: Sun, 6 Jun 2021 23:55:21 -0400 Subject: [PATCH 4/6] Fix resource names --- .../Wasm.Build.Tests/SatelliteAssembliesTests.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/SatelliteAssembliesTests.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/SatelliteAssembliesTests.cs index bb2c8823e9a46a..1d6b38be454eda 100644 --- a/src/tests/BuildWasmApps/Wasm.Build.Tests/SatelliteAssembliesTests.cs +++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/SatelliteAssembliesTests.cs @@ -48,7 +48,7 @@ public void ResourcesFromMainAssembly(BuildArgs buildArgs, extraItems: $""); BuildProject(buildArgs, - initProject: () => CreateProgramForCultureTest(projectName, "TestClass"), + initProject: () => CreateProgramForCultureTest($"{projectName}.words", "TestClass"), dotnetWasmFromRuntimePack: dotnetWasmFromRuntimePack, id: id); @@ -81,7 +81,7 @@ public void ResourcesFromProjectReference(BuildArgs buildArgs, extraItems: $""); BuildProject(buildArgs, - initProject: () => CreateProgramForCultureTest(projectName, "LibraryWithResources.Class1"), + initProject: () => CreateProgramForCultureTest("LibraryWithResources.words", "LibraryWithResources.Class1"), dotnetWasmFromRuntimePack: dotnetWasmFromRuntimePack, id: id); @@ -109,7 +109,7 @@ public void CheckThatSatelliteAssembliesAreNotAOTed(BuildArgs buildArgs, string System.Console.WriteLine ($"--- aot: {buildArgs.AOT}"); BuildProject(buildArgs, - initProject: () => CreateProgramForCultureTest(projectName, "TestClass"), + initProject: () => CreateProgramForCultureTest($"{projectName}.words", "TestClass"), dotnetWasmFromRuntimePack: false, id: id); @@ -124,10 +124,10 @@ public void CheckThatSatelliteAssembliesAreNotAOTed(BuildArgs buildArgs, string } #pragma warning restore xUnit1026 - private void CreateProgramForCultureTest(string projectName, string typeName) + private void CreateProgramForCultureTest(string resourceName, string typeName) => File.WriteAllText(Path.Combine(_projectDir!, "Program.cs"), s_cultureResourceTestProgram - .Replace("##RESOURCE_NAME##", $"{projectName}.words") + .Replace("##RESOURCE_NAME##", resourceName) .Replace("##TYPE_NAME##", typeName)); private const string s_resourcesProjectTemplate = From 337c74c8a2329faedbb31db34317764b8513708e Mon Sep 17 00:00:00 2001 From: Ankit Jain Date: Mon, 7 Jun 2021 00:18:53 -0400 Subject: [PATCH 5/6] Fix typos --- .../Wasm.Build.Tests/InvariantGlobalizationTests.cs | 4 ++-- src/tests/BuildWasmApps/Wasm.Build.Tests/WasmBuildAppTest.cs | 3 +++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/InvariantGlobalizationTests.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/InvariantGlobalizationTests.cs index 6bede4e16c2c5e..753f8c40c2af7d 100644 --- a/src/tests/BuildWasmApps/Wasm.Build.Tests/InvariantGlobalizationTests.cs +++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/InvariantGlobalizationTests.cs @@ -76,8 +76,8 @@ public static int Main() hasIcudt: invariantGlobalization == null || invariantGlobalization.Value == false); string expectedOutputString = invariantGlobalization == true - ? "False - en (ES)" - : "True - Invariant Language (Invariant Country)"; + ? "True - Invariant Language (Invariant Country)" + : "False - es (ES)"; RunAndTestWasmApp(buildArgs, expectedExitCode: 42, test: output => Assert.Contains(expectedOutputString, output), host: host, id: id); } diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/WasmBuildAppTest.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/WasmBuildAppTest.cs index 03a48d903f66b8..e426c9523d9f79 100644 --- a/src/tests/BuildWasmApps/Wasm.Build.Tests/WasmBuildAppTest.cs +++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/WasmBuildAppTest.cs @@ -88,6 +88,9 @@ public class TestClass { public static int Main() { Console.WriteLine($""tc: {Environment.TickCount}, tc64: {Environment.TickCount64}""); + + // if this gets printed, then we didn't crash! + Console.WriteLine(""Hello, World!""); return 42; } }"; From 19c8674eb91110df9571d79c738db62fe24e8ece Mon Sep 17 00:00:00 2001 From: Ankit Jain Date: Fri, 11 Jun 2021 18:17:37 -0400 Subject: [PATCH 6/6] address review feedback --- .../Wasm.Build.Tests/SatelliteAssembliesTests.cs | 2 +- .../BuildWasmApps/testassets/LibraryWithResources/Class1.cs | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/SatelliteAssembliesTests.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/SatelliteAssembliesTests.cs index 1d6b38be454eda..e9d973650dcae6 100644 --- a/src/tests/BuildWasmApps/Wasm.Build.Tests/SatelliteAssembliesTests.cs +++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/SatelliteAssembliesTests.cs @@ -23,7 +23,7 @@ public SatelliteAssembliesTests(ITestOutputHelper output, SharedBuildPerTestClas .Multiply( new object?[] { relinking, "es-ES", "got: hola" }, new object?[] { relinking, null, "got: hello" }, - new object?[] { relinking, "ja-JP", "got: こんにちは" }) + new object?[] { relinking, "ja-JP", "got: \u3053\u3093\u306B\u3061\u306F" }) .WithRunHosts(host) .UnwrapItemsAsArrays(); diff --git a/src/tests/BuildWasmApps/testassets/LibraryWithResources/Class1.cs b/src/tests/BuildWasmApps/testassets/LibraryWithResources/Class1.cs index 5ae850563f8ee8..4674bfd7d0b342 100644 --- a/src/tests/BuildWasmApps/testassets/LibraryWithResources/Class1.cs +++ b/src/tests/BuildWasmApps/testassets/LibraryWithResources/Class1.cs @@ -1,4 +1,7 @@ -using System; +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; namespace LibraryWithResources {