From d574b032793ae752387d32b97ff9840de17420a2 Mon Sep 17 00:00:00 2001 From: Ankit Jain Date: Mon, 19 Jul 2021 01:02:01 -0400 Subject: [PATCH] [wasm] Add support for using custom native libraries (#55797) --- src/mono/wasm/build/WasmApp.Native.targets | 79 +++++++------- src/mono/wasm/build/WasmApp.props | 1 + src/mono/wasm/build/WasmApp.targets | 10 +- .../WasmAppBuilder/PInvokeTableGenerator.cs | 18 +++- .../Wasm.Build.Tests/BuildTestBase.cs | 5 +- .../Wasm.Build.Tests/NativeBuildTests.cs | 3 +- .../Wasm.Build.Tests/NativeLibraryTests.cs | 96 ++++++++++++++++++ .../data/Workloads.Directory.Build.targets | 1 - .../testassets/AppUsingNativeLib/Program.cs | 21 ++++ .../AppUsingNativeLib/native-lib.cpp | 11 ++ .../testassets/AppUsingNativeLib/native-lib.h | 17 ++++ .../testassets/AppUsingSkiaSharp/Program.cs | 17 ++++ src/tests/BuildWasmApps/testassets/mono.png | Bin 0 -> 26462 bytes .../testassets/native-libs/native-lib.o | Bin 0 -> 542 bytes 14 files changed, 234 insertions(+), 45 deletions(-) create mode 100644 src/tests/BuildWasmApps/Wasm.Build.Tests/NativeLibraryTests.cs create mode 100644 src/tests/BuildWasmApps/testassets/AppUsingNativeLib/Program.cs create mode 100644 src/tests/BuildWasmApps/testassets/AppUsingNativeLib/native-lib.cpp create mode 100644 src/tests/BuildWasmApps/testassets/AppUsingNativeLib/native-lib.h create mode 100644 src/tests/BuildWasmApps/testassets/AppUsingSkiaSharp/Program.cs create mode 100644 src/tests/BuildWasmApps/testassets/mono.png create mode 100644 src/tests/BuildWasmApps/testassets/native-libs/native-lib.o diff --git a/src/mono/wasm/build/WasmApp.Native.targets b/src/mono/wasm/build/WasmApp.Native.targets index 306cc52e98707..2d89733447d12 100644 --- a/src/mono/wasm/build/WasmApp.Native.targets +++ b/src/mono/wasm/build/WasmApp.Native.targets @@ -18,6 +18,7 @@ + _InitializeCommonProperties; _PrepareForWasmBuildNativeOnly; _WasmBuildNativeCore; @@ -38,13 +39,14 @@ + + <_WasmAssembliesInternal Remove="@(_WasmAssembliesInternal)" /> <_WasmAssembliesInternal Include="@(WasmAssembliesToBundle->Distinct())" /> - <_EMSDKMissingPaths Condition="'$(_EMSDKMissingPaths)' == '' and ('$(EmscriptenSdkToolsPath)' == '' or !Exists('$(EmscriptenSdkToolsPath)'))">%24(EmscriptenSdkToolsPath)=$(EmscriptenSdkToolsPath) @@ -115,8 +117,9 @@ true - false - true + true + false + true false @@ -153,6 +156,8 @@ $(_EmccOptimizationFlagDefault) -O0 -s ASSERTIONS=$(_EmccAssertionLevelDefault) + + <_EmccCompileRsp>$(_WasmIntermediateOutputPath)emcc-compile.rsp @@ -161,15 +166,41 @@ <_EmccCommonFlags Include="-s DISABLE_EXCEPTION_CATCHING=0" /> <_EmccCommonFlags Include="-g" Condition="'$(WasmNativeStrip)' == 'false'" /> <_EmccCommonFlags Include="-v" Condition="'$(EmccVerbose)' != 'false'" /> + + <_EmccIncludePaths Include="$(_WasmIntermediateOutputPath.TrimEnd('\/'))" /> + <_EmccIncludePaths Include="$(_WasmRuntimePackIncludeDir)mono-2.0" /> + <_EmccIncludePaths Include="$(_WasmRuntimePackIncludeDir)wasm" /> + + + <_EmccCFlags Include="$(EmccCompileOptimizationFlag)" /> + <_EmccCFlags Include="@(_EmccCommonFlags)" /> + + <_EmccCFlags Include="-DENABLE_AOT=1" Condition="'$(RunAOTCompilation)' == 'true'" /> + <_EmccCFlags Include="-DDRIVER_GEN=1" Condition="'$(RunAOTCompilation)' == 'true'" /> + <_EmccCFlags Include="-DINVARIANT_GLOBALIZATION=1" Condition="'$(InvariantGlobalization)' == 'true'" /> + <_EmccCFlags Include="-DLINK_ICALLS=1" Condition="'$(WasmLinkIcalls)' == 'true'" /> + <_EmccCFlags Include="-DCORE_BINDINGS" /> + <_EmccCFlags Include="-DGEN_PINVOKE=1" /> + + <_EmccCFlags Include=""-I%(_EmccIncludePaths.Identity)"" /> + <_EmccCFlags Include="-g" Condition="'$(WasmNativeDebugSymbols)' == 'true'" /> + + <_EmccCFlags Include="$(EmccExtraCFlags)" /> + + <_WasmRuntimePackSrcFile Include="$(_WasmRuntimePackSrcDir)*.c" /> + <_WasmRuntimePackSrcFile ObjectFile="$(_WasmIntermediateOutputPath)%(FileName).o" /> + + <_DotnetJSSrcFile Include="$(_WasmRuntimePackSrcDir)\*.js" /> + <_WasmNativeFileForLinking Include="@(NativeFileReference)" /> - - <_DotnetJSSrcFile Include="$(_WasmRuntimePackSrcDir)\*.js" /> - + + <_WasmPInvokeModules Include="%(_WasmNativeFileForLinking.FileName)" Condition="'%(_WasmNativeFileForLinking.ScanForPInvokes)' != 'false'" /> + <_WasmPInvokeModules Include="libSystem.Native" /> <_WasmPInvokeModules Include="libSystem.IO.Compression.Native" /> <_WasmPInvokeModules Include="libSystem.Globalization.Native" /> @@ -194,38 +225,13 @@ - <_EmccIncludePaths Include="$(_WasmIntermediateOutputPath.TrimEnd('\/'))" /> - <_EmccIncludePaths Include="$(_WasmRuntimePackIncludeDir)mono-2.0" /> - <_EmccIncludePaths Include="$(_WasmRuntimePackIncludeDir)wasm" /> - - - <_EmccCFlags Include="$(EmccCompileOptimizationFlag)" /> - <_EmccCFlags Include="@(_EmccCommonFlags)" /> - - <_EmccCFlags Include="-DENABLE_AOT=1" Condition="'$(RunAOTCompilation)' == 'true'" /> - <_EmccCFlags Include="-DDRIVER_GEN=1" Condition="'$(RunAOTCompilation)' == 'true'" /> - <_EmccCFlags Include="-DINVARIANT_GLOBALIZATION=1" Condition="'$(InvariantGlobalization)' == 'true'" /> - <_EmccCFlags Include="-DLINK_ICALLS=1" Condition="'$(WasmLinkIcalls)' == 'true'" /> - <_EmccCFlags Include="-DCORE_BINDINGS" /> - <_EmccCFlags Include="-DGEN_PINVOKE=1" /> - <_EmccCFlags Include="-emit-llvm" /> - - <_EmccCFlags Include=""-I%(_EmccIncludePaths.Identity)"" /> - <_EmccCFlags Include="-g" Condition="'$(WasmNativeDebugSymbols)' == 'true'" /> - <_EmccCFlags Include="-s EXPORTED_FUNCTIONS='[@(_ExportedFunctions->'"%(Identity)"', ',')]'" Condition="@(_ExportedFunctions->Count()) > 0" /> - - <_EmccCFlags Include="$(EmccExtraCFlags)" /> - - <_WasmRuntimePackSrcFile Remove="@(_WasmRuntimePackSrcFile)" /> - <_WasmRuntimePackSrcFile Include="$(_WasmRuntimePackSrcDir)\*.c" /> - <_WasmRuntimePackSrcFile ObjectFile="$(_WasmIntermediateOutputPath)%(FileName).o" /> + <_WasmSourceFileToCompile Remove="@(_WasmSourceFileToCompile)" /> <_WasmSourceFileToCompile Include="@(_WasmRuntimePackSrcFile)" /> <_EmBuilder Condition="$([MSBuild]::IsOSPlatform('WINDOWS'))">embuilder.bat <_EmBuilder Condition="!$([MSBuild]::IsOSPlatform('WINDOWS'))">embuilder.py - <_EmccCompileRsp>$(_WasmIntermediateOutputPath)emcc-compile.rsp @@ -235,6 +241,10 @@ + + + + @@ -271,6 +281,8 @@ Include="$(MicrosoftNetCoreAppRuntimePackRidNativeDir)\*.a" Exclude="@(_MonoRuntimeComponentDontLink->'$(MicrosoftNetCoreAppRuntimePackRidNativeDir)\%(Identity)')" /> + <_WasmExtraJSFile Include="@(Content)" Condition="'%(Content.Extension)' == '.js'" /> + <_EmccLinkStepArgs Include="@(_EmccLDFlags)" /> <_EmccLinkStepArgs Include="--js-library "%(_DotnetJSSrcFile.Identity)"" /> <_EmccLinkStepArgs Include="--js-library "%(_WasmExtraJSFile.Identity)"" Condition="'%(_WasmExtraJSFile.Kind)' == 'js-library'" /> @@ -483,6 +495,5 @@ EMSCRIPTEN_KEEPALIVE void mono_wasm_load_profiler_aot (const char *desc) { mono_ - - + diff --git a/src/mono/wasm/build/WasmApp.props b/src/mono/wasm/build/WasmApp.props index 10b939d6da73c..bc45a6d54d73d 100644 --- a/src/mono/wasm/build/WasmApp.props +++ b/src/mono/wasm/build/WasmApp.props @@ -7,6 +7,7 @@ Publish + _InitializeCommonProperties; _BeforeWasmBuildApp; _WasmResolveReferences; _WasmAotCompileApp; diff --git a/src/mono/wasm/build/WasmApp.targets b/src/mono/wasm/build/WasmApp.targets index 10bd9ac7973c5..534caf9025bf1 100644 --- a/src/mono/wasm/build/WasmApp.targets +++ b/src/mono/wasm/build/WasmApp.targets @@ -80,6 +80,9 @@ false + + + <_WasmIntermediateOutputPath>$([MSBuild]::NormalizeDirectory($(IntermediateOutputPath), 'wasm')) <_BeforeWasmBuildAppDependsOn /> @@ -90,7 +93,7 @@ - + @@ -100,11 +103,12 @@ $([MSBuild]::NormalizeDirectory($(MicrosoftNetCoreAppRuntimePackRidDir))) $([MSBuild]::NormalizeDirectory($(MicrosoftNetCoreAppRuntimePackRidDir), 'native')) - <_WasmRuntimePackIncludeDir>$([MSBuild]::NormalizeDirectory($(MicrosoftNetCoreAppRuntimePackRidNativeDir), 'include')) <_WasmRuntimePackSrcDir>$([MSBuild]::NormalizeDirectory($(MicrosoftNetCoreAppRuntimePackRidNativeDir), 'src')) + + @@ -115,8 +119,6 @@ $(TargetFileName) $([MSBuild]::NormalizeDirectory($(WasmAppDir))) - - <_WasmIntermediateOutputPath>$([MSBuild]::NormalizeDirectory($(IntermediateOutputPath), 'wasm')) diff --git a/src/tasks/WasmAppBuilder/PInvokeTableGenerator.cs b/src/tasks/WasmAppBuilder/PInvokeTableGenerator.cs index bdd84c295ee7d..7ae4ca9920a2c 100644 --- a/src/tasks/WasmAppBuilder/PInvokeTableGenerator.cs +++ b/src/tasks/WasmAppBuilder/PInvokeTableGenerator.cs @@ -22,6 +22,8 @@ public class PInvokeTableGenerator : Task [Required] public string? OutputPath { get; set; } + private static char[] s_charsToReplace = new[] { '.', '-', }; + public override bool Execute() { Log.LogMessage(MessageImportance.Normal, $"Generating pinvoke table to '{OutputPath}'."); @@ -101,7 +103,7 @@ private void EmitPInvokeTable(StreamWriter w, Dictionary modules foreach (var module in modules.Keys) { - string symbol = module.Replace(".", "_") + "_imports"; + string symbol = ModuleNameToId(module) + "_imports"; w.WriteLine("static PinvokeImport " + symbol + " [] = {"); var assemblies_pinvokes = pinvokes. @@ -120,7 +122,7 @@ private void EmitPInvokeTable(StreamWriter w, Dictionary modules w.Write("static void *pinvoke_tables[] = { "); foreach (var module in modules.Keys) { - string symbol = module.Replace(".", "_") + "_imports"; + string symbol = ModuleNameToId(module) + "_imports"; w.Write(symbol + ","); } w.WriteLine("};"); @@ -130,6 +132,18 @@ private void EmitPInvokeTable(StreamWriter w, Dictionary modules w.Write("\"" + module + "\"" + ","); } w.WriteLine("};"); + + static string ModuleNameToId(string name) + { + if (name.IndexOfAny(s_charsToReplace) < 0) + return name; + + string fixedName = name; + foreach (char c in s_charsToReplace) + fixedName = fixedName.Replace(c, '_'); + + return fixedName; + } } private string MapType (Type t) diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/BuildTestBase.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/BuildTestBase.cs index 7d211ae618912..608aee1781715 100644 --- a/src/tests/BuildWasmApps/Wasm.Build.Tests/BuildTestBase.cs +++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/BuildTestBase.cs @@ -69,6 +69,7 @@ static BuildTestBase() public BuildTestBase(ITestOutputHelper output, SharedBuildPerTestClassFixture buildContext) { + Console.WriteLine($"{Environment.NewLine}-------- New test --------{Environment.NewLine}"); _buildContext = buildContext; _testOutput = output; _logPath = s_buildEnv.LogRootPath; // FIXME: @@ -216,7 +217,8 @@ protected static string RunWithXHarness(string testCommand, string testLogPath, [MemberNotNull(nameof(_projectDir), nameof(_logPath))] protected void InitPaths(string id) { - _projectDir = Path.Combine(AppContext.BaseDirectory, id); + if (_projectDir == null) + _projectDir = Path.Combine(AppContext.BaseDirectory, id); _logPath = Path.Combine(s_buildEnv.LogRootPath, id); Directory.CreateDirectory(_logPath); @@ -539,7 +541,6 @@ public static (int exitCode, string buildOutput) RunProcess(string path, var lastLines = outputBuilder.ToString().Split('\r', '\n').TakeLast(20); throw new XunitException($"Process timed out, output: {string.Join(Environment.NewLine, lastLines)}"); } - } lock (syncObj) diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/NativeBuildTests.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/NativeBuildTests.cs index 30e487f8171c0..989b90676ecdc 100644 --- a/src/tests/BuildWasmApps/Wasm.Build.Tests/NativeBuildTests.cs +++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/NativeBuildTests.cs @@ -31,9 +31,8 @@ private void NativeBuild(string projectNamePrefix, string projectContents, Build { string projectName = $"{projectNamePrefix}_{buildArgs.Config}_{buildArgs.AOT}"; - buildArgs = buildArgs with { ProjectName = projectName, ProjectFileContents = projectContents }; + buildArgs = buildArgs with { ProjectName = projectName }; buildArgs = ExpandBuildArgs(buildArgs, extraProperties: "true"); - Console.WriteLine ($"-- args: {buildArgs}, name: {projectName}"); BuildProject(buildArgs, initProject: () => File.WriteAllText(Path.Combine(_projectDir!, "Program.cs"), projectContents), diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/NativeLibraryTests.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/NativeLibraryTests.cs new file mode 100644 index 0000000000000..e06c099b19c4b --- /dev/null +++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/NativeLibraryTests.cs @@ -0,0 +1,96 @@ +// 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.IO; +using Xunit; +using Xunit.Abstractions; + +#nullable enable + +namespace Wasm.Build.Tests +{ + public class NativeLibraryTests : BuildTestBase + { + public NativeLibraryTests(ITestOutputHelper output, SharedBuildPerTestClassFixture buildContext) + : base(output, buildContext) + { + } + + [Theory] + [BuildAndRun(aot: false)] + [BuildAndRun(aot: true)] + public void ProjectWithNativeReference(BuildArgs buildArgs, RunHost host, string id) + { + string projectName = $"AppUsingNativeLib-a"; + buildArgs = buildArgs with { ProjectName = projectName }; + buildArgs = ExpandBuildArgs(buildArgs, extraItems: ""); + + if (!_buildContext.TryGetBuildFor(buildArgs, out BuildProduct? _)) + { + InitPaths(id); + if (Directory.Exists(_projectDir)) + Directory.Delete(_projectDir, recursive: true); + + Utils.DirectoryCopy(Path.Combine(BuildEnvironment.TestAssetsPath, "AppUsingNativeLib"), _projectDir); + File.Copy(Path.Combine(BuildEnvironment.TestAssetsPath, "native-libs", "native-lib.o"), Path.Combine(_projectDir, "native-lib.o")); + } + + BuildProject(buildArgs, + dotnetWasmFromRuntimePack: false, + id: id); + + string output = RunAndTestWasmApp(buildArgs, buildDir: _projectDir, expectedExitCode: 0, + test: output => {}, + host: host, id: id); + + Assert.Contains("print_line: 100", output); + Assert.Contains("from pinvoke: 142", output); + } + + [Theory] + [BuildAndRun(aot: false)] + [BuildAndRun(aot: true)] + public void ProjectUsingSkiaSharp(BuildArgs buildArgs, RunHost host, string id) + { + string projectName = $"AppUsingSkiaSharp"; + buildArgs = buildArgs with { ProjectName = projectName }; + buildArgs = ExpandBuildArgs(buildArgs, + extraItems: @$" + + + + + + "); + + string programText = @" +using System; +using SkiaSharp; + +public class Test +{ + public static int Main() + { + using SKFileStream skfs = new SKFileStream(""mono.png""); + using SKImage img = SKImage.FromEncodedData(skfs); + + Console.WriteLine ($""Size: {skfs.Length} Height: {img.Height}, Width: {img.Width}""); + return 0; + } +}"; + + BuildProject(buildArgs, + initProject: () => File.WriteAllText(Path.Combine(_projectDir!, "Program.cs"), programText), + dotnetWasmFromRuntimePack: false, + id: id); + + string output = RunAndTestWasmApp(buildArgs, buildDir: _projectDir, expectedExitCode: 0, + test: output => {}, + host: host, id: id, + args: "mono.png"); + + Assert.Contains("Size: 26462 Height: 599, Width: 499", output); + } + } +} diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/data/Workloads.Directory.Build.targets b/src/tests/BuildWasmApps/Wasm.Build.Tests/data/Workloads.Directory.Build.targets index 19f795a01f235..62e463bb19958 100644 --- a/src/tests/BuildWasmApps/Wasm.Build.Tests/data/Workloads.Directory.Build.targets +++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/data/Workloads.Directory.Build.targets @@ -2,7 +2,6 @@ PrepareForWasmBuild;$(WasmBuildAppDependsOn) <_MicrosoftNetCoreAppRefDir>$(AppRefDir)\ - Microsoft.NETCore.App diff --git a/src/tests/BuildWasmApps/testassets/AppUsingNativeLib/Program.cs b/src/tests/BuildWasmApps/testassets/AppUsingNativeLib/Program.cs new file mode 100644 index 0000000000000..5134392c9d8ca --- /dev/null +++ b/src/tests/BuildWasmApps/testassets/AppUsingNativeLib/Program.cs @@ -0,0 +1,21 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.InteropServices; +using System; +using System.Threading.Tasks; + +namespace SimpleConsole +{ + public class Test + { + public static int Main(string[] args) + { + Console.WriteLine ($"from pinvoke: {SimpleConsole.Test.print_line(100)}"); + return 0; + } + + [DllImport("native-lib")] + public static extern int print_line(int x); + } +} diff --git a/src/tests/BuildWasmApps/testassets/AppUsingNativeLib/native-lib.cpp b/src/tests/BuildWasmApps/testassets/AppUsingNativeLib/native-lib.cpp new file mode 100644 index 0000000000000..329a593279fe2 --- /dev/null +++ b/src/tests/BuildWasmApps/testassets/AppUsingNativeLib/native-lib.cpp @@ -0,0 +1,11 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#include "native-lib.h" +#include + +int print_line(int x) +{ + printf("print_line: %d\n", x); + return 42 + x; +} diff --git a/src/tests/BuildWasmApps/testassets/AppUsingNativeLib/native-lib.h b/src/tests/BuildWasmApps/testassets/AppUsingNativeLib/native-lib.h new file mode 100644 index 0000000000000..826637b3a2d81 --- /dev/null +++ b/src/tests/BuildWasmApps/testassets/AppUsingNativeLib/native-lib.h @@ -0,0 +1,17 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#ifndef _NATIVELIB_H_ +#define _NATIVELIB_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +int print_line(int x); + +#ifdef __cplusplus +} +#endif + +#endif // _NATIVELIB_H_ diff --git a/src/tests/BuildWasmApps/testassets/AppUsingSkiaSharp/Program.cs b/src/tests/BuildWasmApps/testassets/AppUsingSkiaSharp/Program.cs new file mode 100644 index 0000000000000..0aeedffaf6d98 --- /dev/null +++ b/src/tests/BuildWasmApps/testassets/AppUsingSkiaSharp/Program.cs @@ -0,0 +1,17 @@ +// 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 SkiaSharp; + +public class Test +{ + public static int Main() + { + using SKFileStream skfs = new SKFileStream("mono.png"); + using SKImage img = SKImage.FromEncodedData(skfs); + + Console.WriteLine ($"Size: {skfs.Length} Height: {img.Height}, Width: {img.Width}"); + return 0; + } +} diff --git a/src/tests/BuildWasmApps/testassets/mono.png b/src/tests/BuildWasmApps/testassets/mono.png new file mode 100644 index 0000000000000000000000000000000000000000..7469aec9d1fcfa933c0e6c9257d3ff0375744214 GIT binary patch literal 26462 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 JmFn<_{{hgsTz3Ef literal 0 HcmV?d00001 diff --git a/src/tests/BuildWasmApps/testassets/native-libs/native-lib.o b/src/tests/BuildWasmApps/testassets/native-libs/native-lib.o new file mode 100644 index 0000000000000000000000000000000000000000..10ccf42c5ff2397c2201e74ed6ffa7237ed4f24f GIT binary patch literal 542 zcmYLG&5qMB5T0?8rcEn0XSg^5qy;HSn=0MHt^C}Oc!aL&m^5t?S5CUio>Dk*;KYF& z2adc055Vj21~}eI$kzOP^Z6T(4XmjU0PySSbOLgX9d_5G)0X`15z7+w&Bc0M7PVk% zU5QGnI|6uyo0jqY`XGzC70T)iJUA$we$brHy#m7IlkwhP<0N|BV;!9!z?B~mij?dF zi&{?%NG%ErGNINbnFO}Z>;-|Q`YwR{slyeAA+&jXhZsJjb>T5P2Gqg~)p?<_XF z7>~dAy%1I~_RY=AEHCNP%met-*IYf^zNm9D{Mn~A*y#Od5+9HZqIXf#Dga+E=bKS{ z5Gk3lmPO|p!jB&Jlqe;SUcZ0y)^^+hx{t6Q0bW_|7U7qE3m6^r$s-r4!HEvJXd01) zn>%!P$?E(I&gRiHn$pWy9@@BxZ{tG6obj!Q^P-LUR>`V3R#7fxUW!H@6&DegRVY5{ zZ>U)rYTEX=F<4pNRG~4U?zI!VrRg$F(q)p)lg(V{&6XKUXKW^tt0Y@y)7jM~n=kl_ GXa4|FRg;1M literal 0 HcmV?d00001