[wasm] Add support for using custom native libraries (#55797)
authorAnkit Jain <radical@gmail.com>
Mon, 19 Jul 2021 05:02:01 +0000 (01:02 -0400)
committerGitHub <noreply@github.com>
Mon, 19 Jul 2021 05:02:01 +0000 (01:02 -0400)
14 files changed:
src/mono/wasm/build/WasmApp.Native.targets
src/mono/wasm/build/WasmApp.props
src/mono/wasm/build/WasmApp.targets
src/tasks/WasmAppBuilder/PInvokeTableGenerator.cs
src/tests/BuildWasmApps/Wasm.Build.Tests/BuildTestBase.cs
src/tests/BuildWasmApps/Wasm.Build.Tests/NativeBuildTests.cs
src/tests/BuildWasmApps/Wasm.Build.Tests/NativeLibraryTests.cs [new file with mode: 0644]
src/tests/BuildWasmApps/Wasm.Build.Tests/data/Workloads.Directory.Build.targets
src/tests/BuildWasmApps/testassets/AppUsingNativeLib/Program.cs [new file with mode: 0644]
src/tests/BuildWasmApps/testassets/AppUsingNativeLib/native-lib.cpp [new file with mode: 0644]
src/tests/BuildWasmApps/testassets/AppUsingNativeLib/native-lib.h [new file with mode: 0644]
src/tests/BuildWasmApps/testassets/AppUsingSkiaSharp/Program.cs [new file with mode: 0644]
src/tests/BuildWasmApps/testassets/mono.png [new file with mode: 0644]
src/tests/BuildWasmApps/testassets/native-libs/native-lib.o [new file with mode: 0644]

index 306cc52..2d89733 100644 (file)
@@ -18,6 +18,7 @@
     </_WasmBuildNativeCoreDependsOn>
 
     <WasmBuildNativeOnlyDependsOn>
+      _InitializeCommonProperties;
       _PrepareForWasmBuildNativeOnly;
       _WasmBuildNativeCore;
     </WasmBuildNativeOnlyDependsOn>
   <Target Name="WasmBuildNativeOnly" DependsOnTargets="$(WasmBuildNativeOnlyDependsOn)" Condition="'$(WasmBuildNative)' == 'true'" />
 
   <Target Name="_PrepareForWasmBuildNativeOnly">
+    <MakeDir Directories="$(_WasmIntermediateOutputPath)" />
+
     <ItemGroup>
       <_WasmAssembliesInternal Remove="@(_WasmAssembliesInternal)" />
       <_WasmAssembliesInternal Include="@(WasmAssembliesToBundle->Distinct())" />
     </ItemGroup>
   </Target>
 
-
   <Target Name="_SetupEmscripten">
     <PropertyGroup>
       <_EMSDKMissingPaths Condition="'$(_EMSDKMissingPaths)' == '' and ('$(EmscriptenSdkToolsPath)' == '' or !Exists('$(EmscriptenSdkToolsPath)'))">%24(EmscriptenSdkToolsPath)=$(EmscriptenSdkToolsPath) </_EMSDKMissingPaths>
 
     <PropertyGroup>
       <WasmBuildNative Condition="'$(RunAOTCompilation)' == 'true'">true</WasmBuildNative>
-      <WasmBuildNative Condition="'$(WasmBuildNative)' == '' and '$(PublishTrimmed)' != 'true'">false</WasmBuildNative>
-      <WasmBuildNative Condition="'$(WasmBuildNative)' == '' and '$(Configuration)' == 'Release'">true</WasmBuildNative>
+      <WasmBuildNative Condition="'$(WasmBuildNative)' == '' and @(NativeFileReference->Count()) > 0" >true</WasmBuildNative>
+      <WasmBuildNative Condition="'$(WasmBuildNative)' == '' and '$(PublishTrimmed)' != 'true'"   >false</WasmBuildNative>
+      <WasmBuildNative Condition="'$(WasmBuildNative)' == '' and '$(Configuration)' == 'Release'" >true</WasmBuildNative>
       <WasmBuildNative Condition="'$(WasmBuildNative)' == ''">false</WasmBuildNative>
     </PropertyGroup>
 
 
       <EmccCompileOptimizationFlag Condition="'$(EmccCompileOptimizationFlag)' == ''">$(_EmccOptimizationFlagDefault)</EmccCompileOptimizationFlag>
       <EmccLinkOptimizationFlag    Condition="'$(EmccLinkOptimizationFlag)' == ''"   >-O0 -s ASSERTIONS=$(_EmccAssertionLevelDefault)</EmccLinkOptimizationFlag>
+
+      <_EmccCompileRsp>$(_WasmIntermediateOutputPath)emcc-compile.rsp</_EmccCompileRsp>
     </PropertyGroup>
 
     <ItemGroup>
       <_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" />
+
+      <!-- Adding optimization flag at the top, so it gets precedence -->
+      <_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="&quot;-I%(_EmccIncludePaths.Identity)&quot;" />
+      <_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)" />
     </ItemGroup>
 
-   <ItemGroup>
-     <_DotnetJSSrcFile Include="$(_WasmRuntimePackSrcDir)\*.js" />
-   </ItemGroup>
+    <Error Text="Could not find NativeFileReference %(NativeFileReference.Identity)" Condition="'%(NativeFileReference.Identity)' != '' and !Exists(%(NativeFileReference.Identity))" />
   </Target>
 
   <Target Name="_GeneratePInvokeTable">
     <ItemGroup>
+      <_WasmPInvokeModules Include="%(_WasmNativeFileForLinking.FileName)" Condition="'%(_WasmNativeFileForLinking.ScanForPInvokes)' != 'false'" />
+
       <_WasmPInvokeModules Include="libSystem.Native" />
       <_WasmPInvokeModules Include="libSystem.IO.Compression.Native" />
       <_WasmPInvokeModules Include="libSystem.Globalization.Native" />
 
   <Target Name="_WasmCompileNativeFiles">
     <ItemGroup>
-      <_EmccIncludePaths Include="$(_WasmIntermediateOutputPath.TrimEnd('\/'))" />
-      <_EmccIncludePaths Include="$(_WasmRuntimePackIncludeDir)mono-2.0" />
-      <_EmccIncludePaths Include="$(_WasmRuntimePackIncludeDir)wasm" />
-
-      <!-- Adding optimization flag at the top, so it gets precedence -->
-      <_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="&quot;-I%(_EmccIncludePaths.Identity)&quot;" />
-      <_EmccCFlags Include="-g" Condition="'$(WasmNativeDebugSymbols)' == 'true'" />
-      <_EmccCFlags Include="-s EXPORTED_FUNCTIONS='[@(_ExportedFunctions->'&quot;%(Identity)&quot;', ',')]'" 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)" />
     </ItemGroup>
 
     <PropertyGroup>
       <_EmBuilder Condition="$([MSBuild]::IsOSPlatform('WINDOWS'))">embuilder.bat</_EmBuilder>
       <_EmBuilder Condition="!$([MSBuild]::IsOSPlatform('WINDOWS'))">embuilder.py</_EmBuilder>
-      <_EmccCompileRsp>$(_WasmIntermediateOutputPath)emcc-compile.rsp</_EmccCompileRsp>
     </PropertyGroup>
 
     <WriteLinesToFile Lines="@(_EmccCFlags)" File="$(_EmccCompileRsp)" Overwrite="true" WriteOnlyWhenDifferent="true" />
 
     <Message Text="Compiling native assets with emcc. This may take a while ..." Importance="High" />
     <EmccCompile SourceFiles="@(_WasmSourceFileToCompile)" Arguments='"@$(_EmccDefaultFlagsRsp)" "@$(_EmccCompileRsp)"' EnvironmentVariables="@(EmscriptenEnvVars)" />
+
+    <ItemGroup>
+      <WasmNativeAsset Include="%(_WasmSourceFileToCompile.ObjectFile)" />
+    </ItemGroup>
   </Target>
 
   <ItemGroup Condition="'$(Configuration)' == 'Debug' and '@(_MonoComponent->Count())' == 0">
           Include="$(MicrosoftNetCoreAppRuntimePackRidNativeDir)\*.a"
           Exclude="@(_MonoRuntimeComponentDontLink->'$(MicrosoftNetCoreAppRuntimePackRidNativeDir)\%(Identity)')" />
 
+      <_WasmExtraJSFile Include="@(Content)" Condition="'%(Content.Extension)' == '.js'" />
+
       <_EmccLinkStepArgs Include="@(_EmccLDFlags)" />
       <_EmccLinkStepArgs Include="--js-library &quot;%(_DotnetJSSrcFile.Identity)&quot;" />
       <_EmccLinkStepArgs Include="--js-library &quot;%(_WasmExtraJSFile.Identity)&quot;" Condition="'%(_WasmExtraJSFile.Kind)' == 'js-library'" />
@@ -483,6 +495,5 @@ EMSCRIPTEN_KEEPALIVE void mono_wasm_load_profiler_aot (const char *desc) { mono_
     <ParameterGroup>
       <EmccProperties ParameterType="Microsoft.Build.Framework.ITaskItem[]" Required="false" Output="true" />
     </ParameterGroup>
- </UsingTask>
-
+  </UsingTask>
 </Project>
index 10b939d..bc45a6d 100644 (file)
@@ -7,6 +7,7 @@
 
     <WasmBuildAppAfterThisTarget Condition="'$(WasmBuildAppAfterThisTarget)' == ''">Publish</WasmBuildAppAfterThisTarget>
     <WasmBuildAppDependsOn>
+        _InitializeCommonProperties;
         _BeforeWasmBuildApp;
         _WasmResolveReferences;
         _WasmAotCompileApp;
index 10bd9ac..534caf9 100644 (file)
@@ -80,6 +80,9 @@
     <!--<WasmStripAOTAssemblies Condition="'$(AOTMode)' == 'LLVMOnlyInterp'">false</WasmStripAOTAssemblies>-->
     <!--<WasmStripAOTAssemblies Condition="'$(WasmStripAOTAssemblies)' == ''">$(RunAOTCompilation)</WasmStripAOTAssemblies>-->
     <WasmStripAOTAssemblies>false</WasmStripAOTAssemblies>
+
+    <!-- emcc, and mono-aot-cross don't like relative paths for output files -->
+    <_WasmIntermediateOutputPath>$([MSBuild]::NormalizeDirectory($(IntermediateOutputPath), 'wasm'))</_WasmIntermediateOutputPath>
     <_BeforeWasmBuildAppDependsOn />
   </PropertyGroup>
 
@@ -90,7 +93,7 @@
 
   <Target Name="_WasmCoreBuild" BeforeTargets="WasmBuildApp" DependsOnTargets="$(WasmBuildAppDependsOn)" />
 
-  <Target Name="_BeforeWasmBuildApp" DependsOnTargets="$(_BeforeWasmBuildAppDependsOn)">
+  <Target Name="_InitializeCommonProperties">
     <Error Condition="'$(MicrosoftNetCoreAppRuntimePackDir)' == '' and ('%(ResolvedRuntimePack.PackageDirectory)' == '' or !Exists(%(ResolvedRuntimePack.PackageDirectory)))"
            Text="Could not find %25(ResolvedRuntimePack.PackageDirectory)=%(ResolvedRuntimePack.PackageDirectory)" />
 
       <MicrosoftNetCoreAppRuntimePackRidDir>$([MSBuild]::NormalizeDirectory($(MicrosoftNetCoreAppRuntimePackRidDir)))</MicrosoftNetCoreAppRuntimePackRidDir>
       <MicrosoftNetCoreAppRuntimePackRidNativeDir>$([MSBuild]::NormalizeDirectory($(MicrosoftNetCoreAppRuntimePackRidDir), 'native'))</MicrosoftNetCoreAppRuntimePackRidNativeDir>
 
-      <!-- FIXME: confirm that this won't get used before this -->
       <_WasmRuntimePackIncludeDir>$([MSBuild]::NormalizeDirectory($(MicrosoftNetCoreAppRuntimePackRidNativeDir), 'include'))</_WasmRuntimePackIncludeDir>
       <_WasmRuntimePackSrcDir>$([MSBuild]::NormalizeDirectory($(MicrosoftNetCoreAppRuntimePackRidNativeDir), 'src'))</_WasmRuntimePackSrcDir>
     </PropertyGroup>
+  </Target>
 
+  <Target Name="_BeforeWasmBuildApp" DependsOnTargets="$(_BeforeWasmBuildAppDependsOn)">
     <Error Condition="'$(IntermediateOutputPath)' == ''" Text="%24(IntermediateOutputPath) property needs to be set" />
     <Error Condition="!Exists('$(MicrosoftNetCoreAppRuntimePackRidDir)')" Text="MicrosoftNetCoreAppRuntimePackRidDir=$(MicrosoftNetCoreAppRuntimePackRidDir) doesn't exist" />
     <Error Condition="@(WasmAssembliesToBundle->Count()) == 0" Text="WasmAssembliesToBundle item is empty. No assemblies to process" />
       <WasmMainAssemblyFileName Condition="'$(WasmMainAssemblyFileName)' == ''">$(TargetFileName)</WasmMainAssemblyFileName>
 
       <WasmAppDir>$([MSBuild]::NormalizeDirectory($(WasmAppDir)))</WasmAppDir>
-      <!-- emcc, and mono-aot-cross don't like relative paths for output files -->
-      <_WasmIntermediateOutputPath>$([MSBuild]::NormalizeDirectory($(IntermediateOutputPath), 'wasm'))</_WasmIntermediateOutputPath>
     </PropertyGroup>
 
     <MakeDir Directories="$(_WasmIntermediateOutputPath)" />
index bdd84c2..7ae4ca9 100644 (file)
@@ -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 @@ public class PInvokeTableGenerator : Task
 
         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 @@ public class PInvokeTableGenerator : Task
         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 @@ public class PInvokeTableGenerator : Task
             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)
index 7d211ae..608aee1 100644 (file)
@@ -69,6 +69,7 @@ namespace Wasm.Build.Tests
 
         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 @@ namespace Wasm.Build.Tests
         [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 @@ namespace Wasm.Build.Tests
                         var lastLines = outputBuilder.ToString().Split('\r', '\n').TakeLast(20);
                         throw new XunitException($"Process timed out, output: {string.Join(Environment.NewLine, lastLines)}");
                     }
-
                 }
 
                 lock (syncObj)
index 30e487f..989b906 100644 (file)
@@ -31,9 +31,8 @@ namespace Wasm.Build.Tests
         {
             string projectName = $"{projectNamePrefix}_{buildArgs.Config}_{buildArgs.AOT}";
 
-            buildArgs = buildArgs with { ProjectName = projectName, ProjectFileContents = projectContents };
+            buildArgs = buildArgs with { ProjectName = projectName };
             buildArgs = ExpandBuildArgs(buildArgs, extraProperties: "<WasmBuildNative>true</WasmBuildNative>");
-            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 (file)
index 0000000..e06c099
--- /dev/null
@@ -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: "<NativeFileReference Include=\"native-lib.o\" />");
+
+            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: @$"
+                                <PackageReference Include=""SkiaSharp"" Version=""2.80.3"" />
+                                <PackageReference Include=""SkiaSharp.NativeAssets.WebAssembly"" Version=""2.80.3"" />
+
+                                <NativeFileReference Include=""$(SkiaSharpStaticLibraryPath)\2.0.9\*.a"" />
+                                <WasmFilesToIncludeInFileSystem Include=""{Path.Combine(BuildEnvironment.TestAssetsPath, "mono.png")}"" />
+                            ");
+
+            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);
+        }
+    }
+}
index 19f795a..62e463b 100644 (file)
@@ -2,7 +2,6 @@
   <PropertyGroup>
     <WasmBuildAppDependsOn>PrepareForWasmBuild;$(WasmBuildAppDependsOn)</WasmBuildAppDependsOn>
     <_MicrosoftNetCoreAppRefDir>$(AppRefDir)\</_MicrosoftNetCoreAppRefDir>
-    <LocalFrameworkOverrideName>Microsoft.NETCore.App</LocalFrameworkOverrideName>
   </PropertyGroup>
 
   <Target Name="PrepareForWasmBuild">
diff --git a/src/tests/BuildWasmApps/testassets/AppUsingNativeLib/Program.cs b/src/tests/BuildWasmApps/testassets/AppUsingNativeLib/Program.cs
new file mode 100644 (file)
index 0000000..5134392
--- /dev/null
@@ -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 (file)
index 0000000..329a593
--- /dev/null
@@ -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 <stdio.h>
+
+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 (file)
index 0000000..826637b
--- /dev/null
@@ -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 (file)
index 0000000..0aeedff
--- /dev/null
@@ -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 (file)
index 0000000..7469aec
Binary files /dev/null and b/src/tests/BuildWasmApps/testassets/mono.png differ
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 (file)
index 0000000..10ccf42
Binary files /dev/null and b/src/tests/BuildWasmApps/testassets/native-libs/native-lib.o differ