Skip to content

Commit

Permalink
Merge pull request #675 from unoplatform/mergify/bp/release/stable/7.…
Browse files Browse the repository at this point in the history
…0/pr-665

feat(localization): Add support satellite resource assemblies publishing (backport #665)
  • Loading branch information
jeromelaban authored Feb 2, 2023
2 parents f42b841 + 3e5dcbd commit 9e08347
Show file tree
Hide file tree
Showing 10 changed files with 211 additions and 68 deletions.
3 changes: 3 additions & 0 deletions doc/linker-configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,11 @@ By default, some features are linked out as those are not likely to be used in a
- `EventSourceSupport`
- `EnableUnsafeUTF7Encoding`
- `HttpActivityPropagationSupport`
- `InvariantGlobalization`

If you need to enable any of those features, you can set the following in your csproj first `PropertyGroup`:
```xml
<EventSourceSupport>true</EventSourceSupport>
```

Setting `InvariantGlobalization` to true will remove all satellite assemblies from the final package.
7 changes: 7 additions & 0 deletions src/Uno.Wasm.Bootstrap/ShellTask.cs
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,8 @@ public partial class ShellTask_v0 : Microsoft.Build.Utilities.Task
[Microsoft.Build.Framework.Required]
public bool MonoILLinker { get; set; }

public bool InvariantGlobalization { get; set; } = false;

public bool EmccLinkOptimization { get; set; }

public string? EmccLinkOptimizationLevel { get; set; }
Expand Down Expand Up @@ -881,6 +883,11 @@ private void RunPackager()
packagerParams.Add($"\"--linker-optimization-level={GetEmccLinkerOptimizationLevel()}\"");
packagerParams.Add($"\"{AlignPath(Path.GetFullPath(Assembly))}\"");

if (InvariantGlobalization)
{
packagerParams.Add("--invariant-globalization");
}

var packagerResponseFile = Path.Combine(workAotPath, "packager.rsp");
File.WriteAllLines(packagerResponseFile, packagerParams.Select(p => p.Replace("\\", "/")));

Expand Down
116 changes: 108 additions & 8 deletions src/Uno.Wasm.Packager/packager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ public bool DefaultValue {
}

class Driver {
static bool enable_debug, enable_linker;
static bool enable_debug, enable_linker, invariant_globalization;
static string app_prefix, framework_prefix, bcl_tools_prefix, bcl_facades_prefix, out_prefix;
static List<string> bcl_prefixes;
static HashSet<string> asm_map = new HashSet<string> ();
Expand Down Expand Up @@ -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<AssemblyData> assemblies = new List<AssemblyData> ();
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -317,14 +323,48 @@ 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(!invariant_globalization && 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.Replace("\\", "/"),
culture = cultureName,
aot = false
};

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<string> profilers, ExecMode ee_mode, bool link_icalls) {
var symbols = new List<string> ();
foreach (var adata in assemblies) {
Expand Down Expand Up @@ -408,6 +448,7 @@ class WasmOptions {
public bool EnableDedup = true;
public bool EmccLinkOptimizations = false;
public bool EnableWasmExceptions = false;
public bool InvariantGlobalization = false;
}

int Run (string[] args) {
Expand Down Expand Up @@ -544,6 +585,7 @@ int Run (string[] args) {
AddFlag (p, new BoolFlag ("collation", "enable unicode collation support", opts.EnableCollation, b => opts.EnableCollation = b));
AddFlag (p, new BoolFlag ("icu", "enable .NET 5+ ICU", opts.EnableICU, b => opts.EnableICU = b));
AddFlag (p, new BoolFlag ("emcc-link-optimization", "enable emcc link-time optimizations", opts.EmccLinkOptimizations, b => opts.EmccLinkOptimizations = b));
AddFlag (p, new BoolFlag ("invariant-globalization", "enables invariant globalization", opts.InvariantGlobalization, b => opts.InvariantGlobalization = b));
p.Add(new ResponseFileSource());

var new_args = p.Parse (args).ToArray ();
Expand Down Expand Up @@ -584,6 +626,7 @@ int Run (string[] args) {
enable_threads = opts.EnableThreads;
enable_dynamic_runtime = opts.EnableDynamicRuntime;
enable_simd = opts.Simd;
invariant_globalization = opts.InvariantGlobalization;

// Dedup is disabled by default https://github.com/dotnet/runtime/issues/48814
enable_dedup = opts.EnableDedup;
Expand Down Expand Up @@ -740,6 +783,14 @@ int Run (string[] args) {
foreach (var ass in assemblies) {
if (aot_assemblies == "" || to_aot.ContainsKey (ass.name)) {
ass.aot = true;

if(ass.culture is not null)
{
// Satellite assemblies cannot be AOTed as they're
// implicitly duplicates.
ass.aot = false;
}

to_aot.Remove (ass.name);
}
}
Expand Down Expand Up @@ -774,7 +825,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);
}
}

Expand Down Expand Up @@ -885,7 +944,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";

Expand Down Expand Up @@ -944,7 +1016,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;
}
Expand Down Expand Up @@ -1470,6 +1542,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);
Expand Down Expand Up @@ -1587,7 +1665,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} ";
}
Expand All @@ -1596,7 +1674,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");
Expand Down Expand Up @@ -1700,9 +1778,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:
Expand Down
5 changes: 5 additions & 0 deletions src/Uno.Wasm.Sample/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
using System.Threading;
using System.Runtime.InteropServices;
using System.Runtime.CompilerServices;
using System.Globalization;

namespace Uno.Wasm.Sample
{
Expand Down Expand Up @@ -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);
Expand Down
Loading

0 comments on commit 9e08347

Please sign in to comment.