From 24cd94cbfb5de3e3c8b5e4cda26785a58b783d89 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 12 Sep 2024 09:23:15 -0700 Subject: [PATCH] [release/9.0] [browser] Fix processing of satellite assemblies from referenced assembly during publish (#107398) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fix related asset computation for satellites from reference (not projectReference) where ResolvedFrom contains {RawFileName} * WBT * Don't remove files with DestinationSubDirectory from ResolvedFileToPublish because they can be resource assemblies from References * Feedback * Fix condition * We can't check for RawFileName, because ProjectReference satellites goes through the same path and are not discovered by nested publish * Comment about "{RawFileName}" * Explicitly set ResolveAssemblyReferencesFindRelatedSatellites=true for nested publish --------- Co-authored-by: Marek FiĊĦera Co-authored-by: Larry Ewing Co-authored-by: Jeff Schwartz --- .../TestAppScenarios/SatelliteLoadingTests.cs | 41 +++++++++++++++++++ src/mono/wasm/build/WasmApp.Common.targets | 2 +- .../App/SatelliteAssembliesTest.cs | 12 ++---- .../App/WasmBasicTestApp.csproj | 1 + .../WasmBasicTestApp/Library/Json.cs | 3 ++ .../ResourceLibrary/ResourceAccessor.cs | 19 +++++++++ .../ResourceLibrary/ResourceLibrary.csproj | 7 ++++ .../{App => ResourceLibrary}/words.es-ES.resx | 0 .../{App => ResourceLibrary}/words.resx | 0 .../ComputeWasmBuildAssets.cs | 7 +++- 10 files changed, 81 insertions(+), 11 deletions(-) create mode 100644 src/mono/wasm/testassets/WasmBasicTestApp/ResourceLibrary/ResourceAccessor.cs create mode 100644 src/mono/wasm/testassets/WasmBasicTestApp/ResourceLibrary/ResourceLibrary.csproj rename src/mono/wasm/testassets/WasmBasicTestApp/{App => ResourceLibrary}/words.es-ES.resx (100%) rename src/mono/wasm/testassets/WasmBasicTestApp/{App => ResourceLibrary}/words.resx (100%) diff --git a/src/mono/wasm/Wasm.Build.Tests/TestAppScenarios/SatelliteLoadingTests.cs b/src/mono/wasm/Wasm.Build.Tests/TestAppScenarios/SatelliteLoadingTests.cs index 5e15cc53841ad..3acee24e88536 100644 --- a/src/mono/wasm/Wasm.Build.Tests/TestAppScenarios/SatelliteLoadingTests.cs +++ b/src/mono/wasm/Wasm.Build.Tests/TestAppScenarios/SatelliteLoadingTests.cs @@ -11,6 +11,7 @@ using Microsoft.Playwright; using Xunit.Abstractions; using Xunit; +using System.Xml.Linq; #nullable enable @@ -38,4 +39,44 @@ public async Task LoadSatelliteAssembly() m => Assert.Equal("es-ES with satellite: hola", m) ); } + + [Fact] + public async Task LoadSatelliteAssemblyFromReference() + { + CopyTestAsset("WasmBasicTestApp", "SatelliteLoadingTestsFromReference", "App"); + + // Replace ProjectReference with Reference + var appCsprojPath = Path.Combine(_projectDir!, "WasmBasicTestApp.csproj"); + var appCsproj = XDocument.Load(appCsprojPath); + + var projectReference = appCsproj.Descendants("ProjectReference").Where(pr => pr.Attribute("Include")?.Value?.Contains("ResourceLibrary") ?? false).Single(); + var itemGroup = projectReference.Parent!; + projectReference.Remove(); + + var reference = new XElement("Reference"); + reference.SetAttributeValue("Include", "..\\ResourceLibrary\\bin\\Release\\net9.0\\ResourceLibrary.dll"); + itemGroup.Add(reference); + + appCsproj.Save(appCsprojPath); + + // Build the library + var libraryCsprojPath = Path.GetFullPath(Path.Combine(_projectDir!, "..", "ResourceLibrary")); + new DotNetCommand(s_buildEnv, _testOutput) + .WithWorkingDirectory(libraryCsprojPath) + .WithEnvironmentVariable("NUGET_PACKAGES", _nugetPackagesDir) + .ExecuteWithCapturedOutput("build -c Release") + .EnsureSuccessful(); + + // Publish the app and assert + PublishProject("Release"); + + var result = await RunSdkStyleAppForPublish(new(Configuration: "Release", TestScenario: "SatelliteAssembliesTest")); + Assert.Collection( + result.TestOutput, + m => Assert.Equal("default: hello", m), + m => Assert.Equal("es-ES without satellite: hello", m), + m => Assert.Equal("default: hello", m), + m => Assert.Equal("es-ES with satellite: hola", m) + ); + } } diff --git a/src/mono/wasm/build/WasmApp.Common.targets b/src/mono/wasm/build/WasmApp.Common.targets index 2641a239ac4e5..cee4e3c1bea03 100644 --- a/src/mono/wasm/build/WasmApp.Common.targets +++ b/src/mono/wasm/build/WasmApp.Common.targets @@ -442,7 +442,7 @@ + Properties="_WasmInNestedPublish_UniqueProperty_XYZ=true;;WasmBuildingForNestedPublish=true;DeployOnBuild=;_IsPublishing=;_WasmIsPublishing=$(_IsPublishing);ResolveAssemblyReferencesFindRelatedSatellites=true"> diff --git a/src/mono/wasm/testassets/WasmBasicTestApp/App/SatelliteAssembliesTest.cs b/src/mono/wasm/testassets/WasmBasicTestApp/App/SatelliteAssembliesTest.cs index b8dc5b0dbf884..23f3df8098cc5 100644 --- a/src/mono/wasm/testassets/WasmBasicTestApp/App/SatelliteAssembliesTest.cs +++ b/src/mono/wasm/testassets/WasmBasicTestApp/App/SatelliteAssembliesTest.cs @@ -2,25 +2,19 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; -using System.Globalization; -using System.Threading.Tasks; -using System.Resources; using System.Runtime.InteropServices.JavaScript; +using System.Threading.Tasks; public partial class SatelliteAssembliesTest { [JSExport] public static async Task Run() { - var rm = new ResourceManager("WasmBasicTestApp.words", typeof(Program).Assembly); - TestOutput.WriteLine("default: " + rm.GetString("hello", CultureInfo.CurrentCulture)); - TestOutput.WriteLine("es-ES without satellite: " + rm.GetString("hello", new CultureInfo("es-ES"))); + ResourceLibrary.ResourceAccessor.Read(TestOutput.WriteLine, false); await LoadSatelliteAssemblies(new[] { "es-ES" }); - rm = new ResourceManager("WasmBasicTestApp.words", typeof(Program).Assembly); - TestOutput.WriteLine("default: " + rm.GetString("hello", CultureInfo.CurrentCulture)); - TestOutput.WriteLine("es-ES with satellite: " + rm.GetString("hello", new CultureInfo("es-ES"))); + ResourceLibrary.ResourceAccessor.Read(TestOutput.WriteLine, true); } [JSImport("INTERNAL.loadSatelliteAssemblies")] diff --git a/src/mono/wasm/testassets/WasmBasicTestApp/App/WasmBasicTestApp.csproj b/src/mono/wasm/testassets/WasmBasicTestApp/App/WasmBasicTestApp.csproj index f93102b467a45..e16a8c4948ca5 100644 --- a/src/mono/wasm/testassets/WasmBasicTestApp/App/WasmBasicTestApp.csproj +++ b/src/mono/wasm/testassets/WasmBasicTestApp/App/WasmBasicTestApp.csproj @@ -16,6 +16,7 @@ + diff --git a/src/mono/wasm/testassets/WasmBasicTestApp/Library/Json.cs b/src/mono/wasm/testassets/WasmBasicTestApp/Library/Json.cs index cd496b96a3fc9..d2cffde92c9e4 100644 --- a/src/mono/wasm/testassets/WasmBasicTestApp/Library/Json.cs +++ b/src/mono/wasm/testassets/WasmBasicTestApp/Library/Json.cs @@ -1,3 +1,6 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + using System.Text.Json; using System.Text.Json.Serialization; diff --git a/src/mono/wasm/testassets/WasmBasicTestApp/ResourceLibrary/ResourceAccessor.cs b/src/mono/wasm/testassets/WasmBasicTestApp/ResourceLibrary/ResourceAccessor.cs new file mode 100644 index 0000000000000..27073846c66ae --- /dev/null +++ b/src/mono/wasm/testassets/WasmBasicTestApp/ResourceLibrary/ResourceAccessor.cs @@ -0,0 +1,19 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Globalization; +using System.Threading.Tasks; +using System.Resources; + +namespace ResourceLibrary; + +public static class ResourceAccessor +{ + public static void Read(Action testOuput, bool hasSatellites) + { + var rm = new ResourceManager("ResourceLibrary.words", typeof(ResourceAccessor).Assembly); + testOuput($"default: {rm.GetString("hello", CultureInfo.CurrentCulture)}"); + testOuput($"es-ES {(hasSatellites ? "with" : "without")} satellite: {rm.GetString("hello", new CultureInfo("es-ES"))}"); + } +} diff --git a/src/mono/wasm/testassets/WasmBasicTestApp/ResourceLibrary/ResourceLibrary.csproj b/src/mono/wasm/testassets/WasmBasicTestApp/ResourceLibrary/ResourceLibrary.csproj new file mode 100644 index 0000000000000..3d5e0e2093c1a --- /dev/null +++ b/src/mono/wasm/testassets/WasmBasicTestApp/ResourceLibrary/ResourceLibrary.csproj @@ -0,0 +1,7 @@ + + + net9.0 + Library + true + + diff --git a/src/mono/wasm/testassets/WasmBasicTestApp/App/words.es-ES.resx b/src/mono/wasm/testassets/WasmBasicTestApp/ResourceLibrary/words.es-ES.resx similarity index 100% rename from src/mono/wasm/testassets/WasmBasicTestApp/App/words.es-ES.resx rename to src/mono/wasm/testassets/WasmBasicTestApp/ResourceLibrary/words.es-ES.resx diff --git a/src/mono/wasm/testassets/WasmBasicTestApp/App/words.resx b/src/mono/wasm/testassets/WasmBasicTestApp/ResourceLibrary/words.resx similarity index 100% rename from src/mono/wasm/testassets/WasmBasicTestApp/App/words.resx rename to src/mono/wasm/testassets/WasmBasicTestApp/ResourceLibrary/words.resx diff --git a/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/ComputeWasmBuildAssets.cs b/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/ComputeWasmBuildAssets.cs index e8daa16e60813..cb070606f49c0 100644 --- a/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/ComputeWasmBuildAssets.cs +++ b/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/ComputeWasmBuildAssets.cs @@ -110,7 +110,12 @@ public override bool Execute() assetCandidate.SetMetadata("AssetTraitName", "Culture"); assetCandidate.SetMetadata("AssetTraitValue", inferredCulture); assetCandidate.SetMetadata("RelativePath", $"_framework/{inferredCulture}/{satelliteAssembly.GetMetadata("FileName")}{satelliteAssembly.GetMetadata("Extension")}"); - assetCandidate.SetMetadata("RelatedAsset", Path.GetFullPath(Path.Combine(OutputPath, "wwwroot", "_framework", Path.GetFileName(assetCandidate.GetMetadata("ResolvedFrom"))))); + + var resolvedFrom = assetCandidate.GetMetadata("ResolvedFrom"); + if (resolvedFrom == "{RawFileName}") // Satellite assembly found from `` element + resolvedFrom = candidate.GetMetadata("OriginalItemSpec"); + + assetCandidate.SetMetadata("RelatedAsset", Path.GetFullPath(Path.Combine(OutputPath, "wwwroot", "_framework", Path.GetFileName(resolvedFrom)))); assetCandidates.Add(assetCandidate); continue;