Open guptay1 opened 4 years ago
cc @yowl
@guptay1 I dont know that you can create the wasm from a dotnet publish
as I don't think there is a RID (-r
option) that will work, but you can do something like, substituting for where you have built corert, and your project name:
"E:\GitHub\corert\Tools\dotnetcli\dotnet.exe" msbuild /m /ConsoleLoggerParameters:ForceNoAlign "/p:IlcPath=E:\GitHub\corert\bin\WebAssembly.wasm.Debug" "/p:Configuration=Debug" "/p:OSGroup=WebAssembly" "/p:Platform=wasm" "/p:FrameworkLibPath=E:\GitHub\corert\bin\WebAssembly.wasm.Debug\lib" "/p:FrameworkObjPath=E:\GitHub\corert\bin\obj\WebAssembly.wasm.Debug\Framework" /p:NativeCodeGen=wasm wasmh.csproj /t:LinkNative
The csproj for this looks like:
<Project>
<Import Project="Sdk.props" Sdk="Microsoft.NET.Sdk" />
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
<TargetsUnix>true</TargetsUnix>
</PropertyGroup>
<Import Project="Sdk.targets" Sdk="Microsoft.NET.Sdk" />
<Import Project="$(IlcPath)\build\Microsoft.NETCore.Native.targets" />
</Project>
I've got net 5 preview installed but netcoreapp3.1
should also be fine.
@yowl I'm also using .NET 5 Preview. The LLVM bitcode file is getting created. I wasn't able to create it before. Now when I run the command:
emcc HelloWorld.bc -s WASM=1 -o HelloWasm.html
I get the following error in the browser console:
wasm streaming compile failed: TypeError: Failed to execute 'compile' on 'WebAssembly': Incorrect response MIME type. Expected 'application/wasm'
falling back to ArrayBuffer instantiation
Also tried the following emcc command from the documentation:
emcc HelloWasm.bc -s ALLOW_MEMORY_GROWTH=1 C:\corert\bin\WebAssembly.wasm.Debug\sdk\libPortableRuntime.bc C:\corert\bin\WebAssembly.wasm.Debug\sdk\libbootstrappercpp.bc -s WASM=1 -o HelloWasm.html
where actually libPortableRuntime.bc -> libPortableRuntime.a and libbootstrappercpp.bc->libbootstrappercpp.a after build.cmd. This emcc command gives many errors with a general format like:
error: undefined symbol: CoreLibNative_GetEnv (referenced by top-level compiled C/C++ code)
warning: _CoreLibNative_GetEnv may need to be added to EXPORTED_FUNCTIONS if it arrives from a system library
Am I missing something here?
Hmm, the /t:LinkNative
should have built the wasm with everything linked in for you, did it not do that?
@yowl No, it didn't
The 3 archive, .a
files are in corert\bin\WebAssembly.wasm.Debug\sdk
@yowl Yes. I was pointing out that the documentation says that they are bitcode files
Ah ok, right its out of date. So under your project folder you dont have bin\wasm\Debug\net5.0\native
?
I do, but it's empty. There is no content in it when ideally it should've had the html, js and wasm file, Right?
can you run set
and check that EMSDK is set?
I do, but it's empty. There is no content in it when ideally it should've had the html, js and wasm file, Right?
Yes it should
Yes, it is set.
That doesn't look right, you should have 3:
EMSDK=E:/GitHub/emsdk
EMSDK_NODE=E:\GitHub\emsdk\node\12.18.1_64bit\bin\node.exe
EMSDK_PYTHON=E:\GitHub\emsdk\python\3.7.4-pywin32_64bit\python.exe
That doesn't look right, you should have 3:
EMSDK=E:/GitHub/emsdk EMSDK_NODE=E:\GitHub\emsdk\node\12.18.1_64bit\bin\node.exe EMSDK_PYTHON=E:\GitHub\emsdk\python\3.7.4-pywin32_64bit\python.exe
Lemme Check
Without EMSDK env var, this condition will not be true and linking will not occur: <Exec Command=""$(EMSDK)/upstream/emscripten/emcc.bat" $(EmccArgs)" Condition="'$(NativeCodeGen)' == 'wasm' and '$(EMSDK)' != '' and '$(OS)' == 'Windows_NT'" />
(from Microsoft.NETCore.Native.targets
)
Without EMSDK env var, this condition will not be true and linking will not occur:
<Exec Command=""$(EMSDK)/upstream/emscripten/emcc.bat" $(EmccArgs)" Condition="'$(NativeCodeGen)' == 'wasm' and '$(EMSDK)' != '' and '$(OS)' == 'Windows_NT'" />
(fromMicrosoft.NETCore.Native.targets
)
Added the EMSDK variable using emsdk_env.bat. I can generate the files in the bin now but on Running it on live server, it throws the following errors:
Yeah, unfortunately Console
is not working. I'm kind of hoping with the move to runtimelab and .net 5 this will get better. You can make it work by linking in some other stuff, its a bit of a hack. What you can try is running the link with an extra library libSystem.Native.a
You can get this libSystem.Native.a
from the artifacts from the runtime pipeline I think. https://dnceng.visualstudio.com/public/_build
However looking now I can't see the browser_wasm artifact. libSystem.Native.a.zip Here's an old one
Yeah, unfortunately
Console
is not working. I'm kind of hoping with the move to runtimelab and .net 5 this will get better. You can make it work by linking in some other stuff, its a bit of a hack. What you can try is running the link with an extra librarylibSystem.Native.a
You can get thislibSystem.Native.a
from the artifacts from the runtime pipeline I think. https://dnceng.visualstudio.com/public/_buildHowever looking now I can't see the browser_wasm artifact. libSystem.Native.a.zip Here's an old one
So, adding this archive file to the command emcc HelloWasm.bc -s ALLOW_MEMORY_GROWTH=1 C:\corert\bin\WebAssembly.wasm.Debug\sdk\libPortableRuntime.a C:\corert\bin\WebAssembly.wasm.Debug\sdk\libbootstrappercpp.a -s WASM=1 -o HelloWasm.html
will resolve the error?
mm, actually it's a bit more complicated you need basically all the dlls from the browser-wasm build which I can't find now. The easiest thing to do is to replace System.Console
#if CODEGEN_WASM
using System.Runtime.InteropServices;
using Console=BringUpTest.Console;
#endif
If CODEGEN_WASM
is not set for your csproj you can define it, or just remove the #if if you dont care about running the code for other targets.
Then
#if CODEGEN_WASM
internal class Console
{
private static unsafe void PrintString(string s)
{
int length = s.Length;
fixed (char* curChar = s)
{
for (int i = 0; i < length; i++)
{
TwoByteStr curCharStr = new TwoByteStr();
curCharStr.first = (byte)(*(curChar + i));
printf((byte*)&curCharStr, null);
}
}
}
internal static void WriteLine(string s)
{
PrintString(s);
PrintString("\n");
}
internal static void WriteLine(string format, string p)
{
PrintString(string.Format(format, p));
PrintString("\n");
}
}
struct TwoByteStr
{
public byte first;
public byte second;
}
[DllImport("*")]
private static unsafe extern int printf(byte* str, byte* unused);
#endif
This is what I'm doing for the test projects.
This is what I'm doing for the test projects.
@yowl Can you point out one such test project that you might've made available on open source? I saw your snakewasm project but it didn't have any documentation to exactly understand what you actually did. Also, the hack above, I understand what you did there in the code but didn't understand the imports and CODEGEN_WASM. Can you elaborate more on that?
I'm referring to the CoreRT test projects that are enabled for Wasm, e.g. https://github.com/dotnet/corert/tree/master/tests/src/Simple/HelloWasm.
For the imports, the normal Console
is defined in System
which already has a using
so to make the compiler use the replacement Console, when it sees Console.WriteLine
, you need to explicitly say which Console
is wanted. System.Runtime.InteropServices
is there because we are using the DllImport
attribute.
CODEGEN_WASM
is a constant that is defined in the project file, using something like
<DefineConstants Condition="$(NativeCodeGen) == 'wasm'">$(DefineConstants);CODEGEN_WASM</DefineConstants>
in a PropertyGroup
e.g.. https://github.com/dotnet/corert/blob/9fd573816ac81719f32f4b5635896a651a5c91ce/tests/src/Simple/SimpleTest.targets#L10
I'm referring to the CoreRT test projects that are enabled for Wasm, e.g. https://github.com/dotnet/corert/tree/master/tests/src/Simple/HelloWasm. For the imports, the normal
Console
is defined inSystem
which already has ausing
so to make the compiler use the replacement Console, when it seesConsole.WriteLine
, you need to explicitly say whichConsole
is wanted.System.Runtime.InteropServices
is there because we are using theDllImport
attribute.
CODEGEN_WASM
is a constant that is defined in the project file, using something like<DefineConstants Condition="$(NativeCodeGen) == 'wasm'">$(DefineConstants);CODEGEN_WASM</DefineConstants>
in a
PropertyGroup
e.g..
@yowl Did the above thing. The Console error is resolved but now I am getting a call stack size exceeded exception. Also, as you can see that the discussion is going too long and apparently there are too many details and too many hacks which aren't present in the documentation. Can you please write a sample app which won't take long ( a Hello World is also fine) based on the above discussion? Most of the things are discussed here so it will become pretty easy for me and the future readers who want to understand c# and wasm together. Or maybe point to some documentation which outlines this for new devs?
@yowl Any intuitions for how to clear the above call stack size exceeded error? What can be the possible cause of this and how to resolve?
You can start with https://github.com/yowl/WasmHelloWorld
You can start with https://github.com/yowl/WasmHelloWorld
@yowl This works. Couldn't figure out why my code wasn't working because I was doing exactly the same. Probably something I might be missing. I just need some more info to move a step further. If I want to use System.ServiceModel.Http package in my solution, then how can I go about it? I don't think directly adding the package to the project will do it. Since we are using corert, there must be some additions which have to be made to it?
Also, I have a WCF service which I want to add as a Connected service in my ConsoleApp. Is this supported in Wasm considering the calling to service and the fetching happens over http requests??
If I want to use System.ServiceModel.Http package in my solution, then how can I go about it?
I don't think WCF would even work when targeting Windows/Linux. Trying it with WASM might be a stretch. WCF has a custom implementation for .NET Native that we haven't hooked up into CoreRT yet. Besides some NuGet configuration kung-fu so that we pick up the UWP version of WCF (if that even works), we also lack support for the compile-time version of DispatchProxy
(#279). WCF as-is will try to Reflection.Emit and that won't work.
If I want to use System.ServiceModel.Http package in my solution, then how can I go about it?
I don't think WCF would even work when targeting Windows/Linux. Trying it with WASM might be a stretch. WCF has a custom implementation for .NET Native that we haven't hooked up into CoreRT yet. Besides some NuGet configuration kung-fu so that we pick up the UWP version of WCF (if that even works), we also lack support for the compile-time version of
DispatchProxy
(#279). WCF as-is will try to Reflection.Emit and that won't work.
@MichalStrehovsky I'm targeting only wasm for now. Do you think that the support will be there when .NET 5 will be released?
@MichalStrehovsky I'm targeting only wasm for now. Do you think that the support will be there when .NET 5 will be released?
This repo has an experimental project in it with no official shipping schedule. The WASM support in it is even more experimental. I would look into the thing that is officially supported, which is Mono's WASM. Try it with the latest .NET 5 SDK and if it doesn't work, file an issue in the dotnet/runtime repo.
@MichalStrehovsky I'm targeting only wasm for now. Do you think that the support will be there when .NET 5 will be released?
This repo has an experimental project in it with no official shipping schedule. The WASM support in it is even more experimental. I would look into the thing that is officially supported, which is Mono's WASM. Try it with the latest .NET 5 SDK and if it doesn't work, file an issue in the dotnet/runtime repo.
@MichalStrehovsky The plan was to use mono-wasm. But it lacks documentation. The documentation which is present dates back to almost 3 years in which the mentioned things don't work or might've moved to some other location or made private. This repo had a much better documentation to get new devs get started with wasm easily and it seems straightforward with a bunch of hacks which make perfect sense. If you know about similar documentation for mono and where can I find it, can you share?
I suggest opening an issue about the lack of documentation in the dotnet/runtime repo. But yeah, the team's priority is Blazor so they mostly just care that Blazor-integrated bits work.
They have some non-Blazor samples here: https://github.com/dotnet/runtime/tree/master/src/mono/netcore/sample/wasm. AFAIK the WASM targeting runtime/SDK is only buildable on Linux because they're all Linux/mac people. (Once you build the WASM runtime, you can use Windows.)
I suggest opening an issue about the lack of documentation in the dotnet/runtime repo. But yeah, the team's priority is Blazor so they mostly just care that Blazor-integrated bits work.
They have some non-Blazor samples here: https://github.com/dotnet/runtime/tree/master/src/mono/netcore/sample/wasm. AFAIK the WASM targeting runtime/SDK is only buildable on Linux because they're all Linux/mac people. (Once you build the WASM runtime, you can use Windows.)
Yes, all I could find was Blazor whenever c# and wasm comes together. There is no direct c# to wasm translation being talked about except in corert. Guess I should open up an issue with dotnet/runtime repo requesting documentation.
Also, you are right. The targeting runtime/SDK needs macOS. I couldn't find the artifacts for other environment except macOS. The article and the github repo which mentions about it is also 3 years old which seems like a dead project as no new commits are made.
After following the instructions on https://github.com/morganbr/corert/blob/master/Documentation/how-to-build-WebAssembly.md and https://github.com/dotnet/corert/blob/master/Documentation/how-to-build-WebAssembly.md, I tried building a simple hello world app instead of using the sample. Added the nuget packages and set up of Emscripten. Not able to generate the .bc file and hence no wasm file can be generated using emcc. All pre-requisites and setups are working. Can someone point out how to do it from scratch for a hello world app without using the samples?