From bd0e1fdbfe54ddc91fc7457ce8159aced3e3775a Mon Sep 17 00:00:00 2001 From: Jerome Laban Date: Fri, 27 Jan 2023 17:25:53 -0500 Subject: [PATCH] feat(localization): Add support satellite resource assemblies publishing --- src/Uno.Wasm.Packager/packager.cs | 97 +++++++++++++++++-- src/Uno.Wasm.Sample/Program.cs | 5 + src/Uno.Wasm.Sample/sample.common.props | 121 +++++++++++++----------- 3 files changed, 160 insertions(+), 63 deletions(-) diff --git a/src/Uno.Wasm.Packager/packager.cs b/src/Uno.Wasm.Packager/packager.cs index 6308599d8..63aa62b8c 100644 --- a/src/Uno.Wasm.Packager/packager.cs +++ b/src/Uno.Wasm.Packager/packager.cs @@ -98,6 +98,9 @@ class AssemblyData { public string final_path; // Whenever to AOT this assembly public bool aot; + + // If not null, this is a satellite assembly + public string culture; } static List assemblies = new List (); @@ -255,7 +258,10 @@ static void Import (string ra, AssemblyKind kind) { return; } - if (!asm_map.Add (Path.GetFullPath (ra))) + var assemblyFullPath = Path.GetFullPath(ra); + var assemblyDirectory = Path.GetDirectoryName(assemblyFullPath); + + if (!asm_map.Add (assemblyFullPath)) return; Console.WriteLine($"Resolving {ra}"); ReaderParameters rp = new ReaderParameters(); @@ -317,14 +323,42 @@ static void Import (string ra, AssemblyKind kind) { } else { - Console.WriteLine($"Coukd not resolve {ar.Name}"); + Console.WriteLine($"Could not resolve {ar.Name}"); } } } + // Resolving satellite assemblies + if(kind == AssemblyKind.User) + { + string resourceFile = GetAssemblyResourceFileName(assemblyFullPath); + + foreach (var subDirectory in Directory.EnumerateDirectories(assemblyDirectory)) + { + var satelliteAssembly = Path.Combine(subDirectory, resourceFile); + if (!File.Exists(satelliteAssembly)) + { + continue; + } + + string cultureName = subDirectory.Substring(subDirectory.LastIndexOf(Path.DirectorySeparatorChar) + 1); + string culturePath = Path.Combine(assemblyDirectory, cultureName); + + var satelliteData = new AssemblyData() { name = resourceFile.Replace(".dll", ""), src_path = satelliteAssembly, culture = cultureName }; + assemblies.Add(satelliteData); + + file_list.Add(satelliteAssembly); + + Console.WriteLine($"Added satellite assembly {cultureName}/{resourceFile}"); + } + } + Console.WriteLine($"Resolved {ra}"); } + static string GetAssemblyResourceFileName(string assembly) + => Path.GetFileNameWithoutExtension(assembly) + ".resources.dll"; + void GenDriver (string builddir, List profilers, ExecMode ee_mode, bool link_icalls) { var symbols = new List (); foreach (var adata in assemblies) { @@ -774,7 +808,15 @@ int Run (string[] args) { Directory.Delete (bcl_dir, true); Directory.CreateDirectory (bcl_dir); foreach (var f in file_list) { - CopyFile(f, Path.Combine (bcl_dir, Path.GetFileName (f)), copyType); + + var fileName = Path.GetFileName(f); + + if(IsResourceAssembly(f, out var culture)) + { + fileName = Path.Combine(culture, fileName); + } + + CopyFile(f, Path.Combine (bcl_dir, fileName), copyType); } } @@ -885,7 +927,20 @@ int Run (string[] args) { _ => throw new Exception($"Unsupported asset type") }; - return $" {{ \"name\": \"{Path.GetFileName(f)}\", \"behavior\":\"{assetType}\", \"url\":\"{Path.GetFileName(f)}\" }}"; + string cultureField = null; + string culturePathPrefix = null; + + if (assetType is "assembly") + { + if(IsResourceAssembly(f, out var culture)) + { + assetType = "resource"; + cultureField = $", \"culture\":\"{Path.GetFileName(Path.GetDirectoryName(f))}\""; + culturePathPrefix = $"{culture}/"; + } + } + + return $" {{ \"name\": \"{Path.GetFileName(f)}\", \"behavior\":\"{assetType}\", \"url\":\"{culturePathPrefix}{Path.GetFileName(f)}\" {cultureField} }}"; })); var debugLevel = enable_debug ? " -1" : "0"; @@ -944,7 +999,7 @@ int Run (string[] args) { if (assembly == null) continue; string filename = Path.GetFileName (assembly); - if (filenames.ContainsKey (filename)) { + if (filenames.ContainsKey (filename) && !filename.EndsWith(".resources.dll", StringComparison.OrdinalIgnoreCase)) { Console.WriteLine ("Duplicate input assembly: " + assembly + " " + filenames [filename]); return 1; } @@ -1470,6 +1525,12 @@ int Run (string[] args) { if (assembly == null) continue; string filename = Path.GetFileName (assembly); + + if(a.culture is not null) + { + filename = Path.Combine(a.culture, filename); + } + var filename_noext = Path.GetFileNameWithoutExtension (filename); string filename_pdb = Path.ChangeExtension (filename, "pdb"); var source_file_path = Path.GetFullPath (assembly); @@ -1587,7 +1648,7 @@ int Run (string[] args) { if (link_icalls) { string icall_assemblies = ""; - foreach (var a in assemblies) { + foreach (var a in assemblies.Where(a => a.culture is null)) { if (a.name == "mscorlib" || a.name == "System") icall_assemblies += $"{a.linkout_path} "; } @@ -1596,7 +1657,7 @@ int Run (string[] args) { } if (gen_pinvoke) { string pinvoke_assemblies = ""; - foreach (var a in assemblies) + foreach (var a in assemblies.Where(a => a.culture is null)) pinvoke_assemblies += $"{a.linkout_path} "; ninja.WriteLine ($"build $builddir/pinvoke-table.h: cpifdiff $builddir/pinvoke-table.h.tmp"); @@ -1700,9 +1761,31 @@ int Run (string[] args) { return 0; } + private bool IsResourceAssembly(string f, out string culture) + { + if (f.EndsWith(".resources.dll", StringComparison.OrdinalIgnoreCase)) + { + var originalAssembly = Path.GetFileName(f.Replace(".resources.dll", ".dll", StringComparison.OrdinalIgnoreCase)); + + var resourceAssemblyDirectory = Path.GetDirectoryName(Path.GetDirectoryName(f)); + if (File.Exists(Path.Combine(resourceAssemblyDirectory, originalAssembly))) + { + culture = Path.GetFileName(Path.GetDirectoryName(f)); + + return true; + } + } + + culture = null; + return false; + } + static void CopyFile(string sourceFileName, string destFileName, CopyType copyType, string typeFile = "") { Console.WriteLine($"{typeFile}cp: {copyType} - {sourceFileName} -> {destFileName}"); + + Directory.CreateDirectory(Path.GetDirectoryName(destFileName)); + switch (copyType) { case CopyType.Always: diff --git a/src/Uno.Wasm.Sample/Program.cs b/src/Uno.Wasm.Sample/Program.cs index d5ccbe33b..d7616c206 100644 --- a/src/Uno.Wasm.Sample/Program.cs +++ b/src/Uno.Wasm.Sample/Program.cs @@ -18,6 +18,7 @@ using System.Threading; using System.Runtime.InteropServices; using System.Runtime.CompilerServices; +using System.Globalization; namespace Uno.Wasm.Sample { @@ -51,6 +52,10 @@ static void Main(string[] args) Console.WriteLine(typeof(Microsoft.Extensions.Logging.Abstractions.NullLogger)); + var r = new System.Resources.ResourceManager("FxResources.System.Web.Services.Description.SR", typeof(System.Web.Services.Description.Binding).Assembly); + Console.WriteLine($"Res(en): {r.GetString("WebDescriptionMissing", new CultureInfo("en-US"))}"); + Console.WriteLine($"Res(fr): {r.GetString("WebDescriptionMissing", new CultureInfo("fr-CA"))}"); + _t = new Timer(_ => { Console.WriteLine("message"); }, null, 5000, 5000); diff --git a/src/Uno.Wasm.Sample/sample.common.props b/src/Uno.Wasm.Sample/sample.common.props index 730c009ef..687ef7d32 100644 --- a/src/Uno.Wasm.Sample/sample.common.props +++ b/src/Uno.Wasm.Sample/sample.common.props @@ -1,70 +1,79 @@  - - - + + + - - - - + + + + - - - - + + + + + + + + Uno.Wasm.SampleNet - - - - - - - - <_AdditionalFile1 Include="SomeContent01.txt" /> - <_AdditionalFile1 Include="SomeContent02.txt" /> - <_AdditionalFile1 Include=".editorconfig" /> - <_AdditionalFile1 Include="nuget.config" /> - - <_duplicateValidationItems Include="$(WasmShellOutputPackagePath)\**\System.Private.CoreLib.clr" /> + + + - - - <_monoConfigJson>$([System.IO.File]::ReadAllText('$(WasmShellOutputPackagePath)\mono-config.json')) - <_duplicateValidation>@(_duplicateValidationItems) - - - - - - - - - - - - - - - - - - - false - true - TargetFramework - - - false - true - TargetFramework - - + + + <_AdditionalFile1 Include="SomeContent01.txt" /> + <_AdditionalFile1 Include="SomeContent02.txt" /> + <_AdditionalFile1 Include=".editorconfig" /> + <_AdditionalFile1 Include="nuget.config" /> + + <_duplicateValidationItems Include="$(WasmShellOutputPackagePath)\**\System.Private.CoreLib.clr" /> + + + + <_monoConfigJson>$([System.IO.File]::ReadAllText('$(WasmShellOutputPackagePath)\mono-config.json')) + <_duplicateValidation>@(_duplicateValidationItems) + + + + + + + + + + + + + + + + + + + + + + + false + true + TargetFramework + + + false + true + TargetFramework + +