Skip to content

Commit

Permalink
feat(localization): Add support satellite resource assemblies publishing
Browse files Browse the repository at this point in the history
  • Loading branch information
jeromelaban committed Jan 27, 2023
1 parent e72efbd commit bd0e1fd
Show file tree
Hide file tree
Showing 3 changed files with 160 additions and 63 deletions.
97 changes: 90 additions & 7 deletions src/Uno.Wasm.Packager/packager.cs
Original file line number Diff line number Diff line change
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,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<string> profilers, ExecMode ee_mode, bool link_icalls) {
var symbols = new List<string> ();
foreach (var adata in assemblies) {
Expand Down Expand Up @@ -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);
}
}

Expand Down Expand Up @@ -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";

Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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} ";
}
Expand All @@ -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");
Expand Down Expand Up @@ -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:
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
121 changes: 65 additions & 56 deletions src/Uno.Wasm.Sample/sample.common.props
Original file line number Diff line number Diff line change
@@ -1,70 +1,79 @@
<?xml version="1.0" encoding="utf-8" ?>
<Project>
<Import Project="..\Uno.Wasm.Bootstrap\build\Uno.Wasm.Bootstrap.props" />
<Import Project="..\Uno.Wasm.Bootstrap\build\Uno.Wasm.Bootstrap.targets" />
<Import Project="..\Uno.Wasm.Bootstrap.DevServer\build\Uno.Wasm.Bootstrap.DevServer.targets" />
<Import Project="..\Uno.Wasm.Bootstrap\build\Uno.Wasm.Bootstrap.props" />
<Import Project="..\Uno.Wasm.Bootstrap\build\Uno.Wasm.Bootstrap.targets" />
<Import Project="..\Uno.Wasm.Bootstrap.DevServer\build\Uno.Wasm.Bootstrap.DevServer.targets" />

<ItemGroup>
<None Include="WasmScripts\**\*.js" />
<None Include="WasmCSS\**\*.css" />
</ItemGroup>
<ItemGroup>
<None Include="WasmScripts\**\*.js" />
<None Include="WasmCSS\**\*.css" />
</ItemGroup>

<ItemGroup>
<EmbeddedResource Include="WasmScripts\**\*.js" />
<EmbeddedResource Include="WasmCSS\**\*.css" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="System.Web.Services.Description" Version="4.10.0" />
</ItemGroup>

<ItemGroup>
<EmbeddedResource Include="WasmScripts\**\*.js" />
<EmbeddedResource Include="WasmCSS\**\*.css" />
</ItemGroup>

<PropertyGroup>
<AssemblyName>Uno.Wasm.SampleNet</AssemblyName>
</PropertyGroup>

<ItemGroup>
<WasmShellMonoEnvironment Include="MONO_GC_PARAMS" Value="soft-heap-limit=512m,nursery-size=64m,evacuation-threshold=66,major=marksweep" />
<WasmShellMonoEnvironment Include="MONO_LOG_LEVEL" Value="debug" />
<WasmShellMonoEnvironment Include="MONO_LOG_MASK" Value="gc" />
</ItemGroup>

<Target Name="AfterBuildValidation" DependsOnTargets="BuildDist" AfterTargets="AfterBuild">
<ItemGroup>
<_AdditionalFile1 Include="SomeContent01.txt" />
<_AdditionalFile1 Include="SomeContent02.txt" />
<_AdditionalFile1 Include=".editorconfig" />
<_AdditionalFile1 Include="nuget.config" />

<_duplicateValidationItems Include="$(WasmShellOutputPackagePath)\**\System.Private.CoreLib.clr" />
<WasmShellMonoEnvironment Include="MONO_GC_PARAMS" Value="soft-heap-limit=512m,nursery-size=64m,evacuation-threshold=66,major=marksweep" />
<WasmShellMonoEnvironment Include="MONO_LOG_LEVEL" Value="debug" />
<WasmShellMonoEnvironment Include="MONO_LOG_MASK" Value="gc" />
</ItemGroup>

<PropertyGroup>
<_monoConfigJson>$([System.IO.File]::ReadAllText('$(WasmShellOutputPackagePath)\mono-config.json'))</_monoConfigJson>
<_duplicateValidation>@(_duplicateValidationItems)</_duplicateValidation>
</PropertyGroup>

<Error Condition="!exists('$(WasmShellOutputPackagePath)\AdditionalContent\%(_AdditionalFile1.Identity)')" Text="%(_AdditionalFile1.Identity) does not exist in $(WasmShellOutputPackagePath)" />
<Error Condition="exists('$(WasmShellOutputPackagePath)\AdditionalContent\SomeContent04.txt')" Text="AdditionalContent/SomeContent04.tx should not exist in $(WasmShellOutputPackagePath)" />
<Error Condition="!exists('$(WasmShellOutputPackagePath)\..\web.config')" Text="web.config should exist in $(WasmShellOutputPackagePath)\.." />

<Error Condition="exists('$(WasmShellOutputDistPath)\AdditionalContent\%(_AdditionalFile1.Identity)')" Text="%(_AdditionalFile1.Identity) should not exist in $(WasmShellOutputDistPath)" />
<Error Condition="!exists('$(WasmShellOutputDistPath)\AdditionalContent\SomeContent03.txt')" Text="AdditionalContent/SomeContent03.txt does not exist in $(WasmShellOutputDistPath)" />
<Error Condition="exists('$(WasmShellOutputDistPath)\AdditionalContent\SomeContent04.txt')" Text="AdditionalContent/SomeContent04.txt should not exist in $(WasmShellOutputDistPath)" />

<Error Condition="$(_monoConfigJson.Contains('System.IO.Pipes.dll'))" Text="mono-config.json is incorrectly filtered" />

<Error Condition="$(_duplicateValidation.Contains(';'))" Text="Duplicate System.Private.CoreLib.clr detected" />

<Message Importance="high" Text="Output dist validated" />
</Target>

<ItemGroup>
<ProjectReference Include="..\Uno.Wasm.Bootstrap.Cli\Uno.Wasm.Bootstrap.Cli.csproj">
<ReferenceOutputAssembly>false</ReferenceOutputAssembly>
<SkipGetTargetFrameworkProperties>true</SkipGetTargetFrameworkProperties>
<UndefineProperties>TargetFramework</UndefineProperties>
</ProjectReference>
<ProjectReference Include="..\Uno.Wasm.Bootstrap\Uno.Wasm.Bootstrap.csproj">
<ReferenceOutputAssembly>false</ReferenceOutputAssembly>
<SkipGetTargetFrameworkProperties>true</SkipGetTargetFrameworkProperties>
<UndefineProperties>TargetFramework</UndefineProperties>
</ProjectReference>
</ItemGroup>
<Target Name="AfterBuildValidation" DependsOnTargets="BuildDist" AfterTargets="AfterBuild">
<ItemGroup>
<_AdditionalFile1 Include="SomeContent01.txt" />
<_AdditionalFile1 Include="SomeContent02.txt" />
<_AdditionalFile1 Include=".editorconfig" />
<_AdditionalFile1 Include="nuget.config" />

<_duplicateValidationItems Include="$(WasmShellOutputPackagePath)\**\System.Private.CoreLib.clr" />
</ItemGroup>

<PropertyGroup>
<_monoConfigJson>$([System.IO.File]::ReadAllText('$(WasmShellOutputPackagePath)\mono-config.json'))</_monoConfigJson>
<_duplicateValidation>@(_duplicateValidationItems)</_duplicateValidation>
</PropertyGroup>

<Error Condition="!exists('$(WasmShellOutputPackagePath)\AdditionalContent\%(_AdditionalFile1.Identity)')" Text="%(_AdditionalFile1.Identity) does not exist in $(WasmShellOutputPackagePath)" />
<Error Condition="exists('$(WasmShellOutputPackagePath)\AdditionalContent\SomeContent04.txt')" Text="AdditionalContent/SomeContent04.tx should not exist in $(WasmShellOutputPackagePath)" />
<Error Condition="!exists('$(WasmShellOutputPackagePath)\..\web.config')" Text="web.config should exist in $(WasmShellOutputPackagePath)\.." />

<Error Condition="exists('$(WasmShellOutputDistPath)\AdditionalContent\%(_AdditionalFile1.Identity)')" Text="%(_AdditionalFile1.Identity) should not exist in $(WasmShellOutputDistPath)" />
<Error Condition="!exists('$(WasmShellOutputDistPath)\AdditionalContent\SomeContent03.txt')" Text="AdditionalContent/SomeContent03.txt does not exist in $(WasmShellOutputDistPath)" />
<Error Condition="exists('$(WasmShellOutputDistPath)\AdditionalContent\SomeContent04.txt')" Text="AdditionalContent/SomeContent04.txt should not exist in $(WasmShellOutputDistPath)" />

<Error Condition="!exists('$(WasmShellOutputDistPath)\managed\fr\System.Web.Services.Description.resources.clr')"
Text="managed\fr\System.Web.Services.Description.resources.clr does not exist in $(WasmShellOutputDistPath)" />
<Error Condition="!exists('$(WasmShellOutputDistPath)\managed\pt-BR\System.Web.Services.Description.resources.clr')"
Text="managed\pt-BR\System.Web.Services.Description.resources.clr does not exist in $(WasmShellOutputDistPath)" />

<Error Condition="$(_monoConfigJson.Contains('System.IO.Pipes.dll'))" Text="mono-config.json is incorrectly filtered" />

<Error Condition="$(_duplicateValidation.Contains(';'))" Text="Duplicate System.Private.CoreLib.clr detected" />

<Message Importance="high" Text="Output dist validated" />
</Target>

<ItemGroup>
<ProjectReference Include="..\Uno.Wasm.Bootstrap.Cli\Uno.Wasm.Bootstrap.Cli.csproj">
<ReferenceOutputAssembly>false</ReferenceOutputAssembly>
<SkipGetTargetFrameworkProperties>true</SkipGetTargetFrameworkProperties>
<UndefineProperties>TargetFramework</UndefineProperties>
</ProjectReference>
<ProjectReference Include="..\Uno.Wasm.Bootstrap\Uno.Wasm.Bootstrap.csproj">
<ReferenceOutputAssembly>false</ReferenceOutputAssembly>
<SkipGetTargetFrameworkProperties>true</SkipGetTargetFrameworkProperties>
<UndefineProperties>TargetFramework</UndefineProperties>
</ProjectReference>
</ItemGroup>

</Project>

0 comments on commit bd0e1fd

Please sign in to comment.