Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WebAssembly TFM support #8186

Open
jeromelaban opened this issue Jun 4, 2019 · 46 comments
Open

WebAssembly TFM support #8186

jeromelaban opened this issue Jun 4, 2019 · 46 comments

Comments

@jeromelaban
Copy link

jeromelaban commented Jun 4, 2019

Details about Problem

There is no WebAssembly TFM available in NuGet, which makes the creation of libraries impossible at this point.

This prevent, for instance :

from being ported back to their original repositories for reviews and enable proper support for WebAssembly.

This impacts all mono-wasm based projects that needs to have a NuGet ecosystem, as well as mono-wasm itself, for platform specific implementations (e.g. WebSockets, Bindings, etc...)

References

@rrelyea
Copy link
Contributor

rrelyea commented Jun 4, 2019

Thanks for creating this issue and linking to previous discussions.
The direction here won't be decided by NuGet alone. Looping in @richlander @terrajobst @mhutch @anangaur (likely missing some others)

@richlander
Copy link

I appreciate the desire and need for this. At present, we're going to stick with netstandard as the TFM.

There are two motivators for not making progress here:

  • We are re-thinking how frameworks and platform targets are going to work in the .NET 5 world.
  • Web assembly isn't a singular target. We expect people to want both client and headless scenarios.

In short, more water needs to go under the bridge in order for us to have confidence on burning a TFM. It isn't something to be taken lightly. I realize that it causes pain in the short term.

@jeromelaban
Copy link
Author

@richlander I agree that it's not an issue to take lightly. The point is to somehow centralize this discussion as the issues have been spread out in many repos.

WebAssembly is indeed a large scenario, and sandboxed (browsers) and non-sandboxed (WASI+CLI) are to be considered, as well as would capabilities of the current WebAssembly environment (e.g. Threads or SIMD). The solution may not be a new TFM, in the same way Android/iOS/macOS TFMs may not be, with the complex versioning issues that are currently exhibited with their respective native and mono runtime dependencies.

This impacts the structure of libraries and their dependencies in a significant (breaking) way, with for instance with Rx which is currently build in a monolithic way, and cannot have netstandard 2.0 being an alias for a WebAssembly runtime.

@terrajobst
Copy link

terrajobst commented Jun 5, 2019

To be clear, we're not saying no. We're saying "we don't have enough data to make a decision yet". TFMs can fragment the world. And my team in particular has caused TFM fatigue de luxe. So I hope you understand why this is giving us pause.

@weitzhandler
Copy link

@terrajobst can we at least have even a temporary generalized wasm TFM so we can start working with wasm on projects that require Rx or other frameworks? Right now it's impossible to even extend those frameworks without forking it.

@terrajobst
Copy link

I don’t think so. After it’s shipped it’s quasi permanent.

@richlander
Copy link

richlander commented Jun 7, 2019

We are not going to do anything until we have confidence. No quasi-permanent solutions or known-not-right permanent solutions.

@terrajobst and I have been at the center of a lot of bad decisions (hopefully some good ones too, or just get rid of us!). There are times when we didn’t fully appreciate the ecosystem shaping role we have. There are many, many, things that should have a Wild West characteristic to them in our ecosystem. We want a 1000 flowers to bloom and all that.

TFMs are not like that. There should be as few as possible and no more. So many problems and hardship occur if we allow TFMs to proliferate.

While we appreciate that you folks are having pain now, it is small to the much more spread out pain that could occur if we make bad decisions on something as fundamental as a TFM.

@terrajobst has been enjoying my analogies lately ... let’s take another run at that ...

This is a bit like genetically engineering humans with a bunch of new blood types and not determining ahead of time whether any compatible blood types exist for the scenario when they are in the emergency room and need a blood transfusion. Oops! We didn’t want genetically engineered humans, anyway!

Did I do good @terrajobst?

@weitzhandler
Copy link

Thanks for your responses, they do make a lot of sense.
Can we just ask to prioritize this? Just bear in mind tat this issue means that wasm has broken NuGet.

@terrajobst
Copy link

Can we just ask to prioritize this? Just bear in mind tat this issue means that wasm has broken NuGet.

We're working on Web Assembly as a product, NuGet is a part of it but it's not at the top of our priority list ATM.

@weitzhandler
Copy link

Thanks for letting us know. Does help a lot!

@anangaur
Copy link
Member

anangaur commented Jun 13, 2019

Related feature suggestion on developercommunity: https://developercommunity.visualstudio.com/content/idea/598559/webassembly-tfm.html

@gulbanana
Copy link

if the solution is to be something other than a new TFM that’s fine, but we do need a solution!

@gulbanana
Copy link

Another factor making it difficult to productionise blazor is testability. What would be ideal is a .net quasi-runtime which uses a webassembly host (v8?) to load mono.wasm and runs tests under that environment. Imagine a test project with <TargetFrameworks>netcoreapp3.0;monowasm</TargetFrameworks>, validating your components in both the server-side and client-side environments they might run on...

A TFM isn’t the only piece needed to make this work but it would certainly help.

@michael-hawker
Copy link

Want to call out the Windows Community Toolkit here as well.

It'd be great for project maintainers to just be able to accept a WASM project/build modification for their project than to create a bunch of work for folks coordinating across a fork.

FYI @onovotny, in case he has any input into these scenarios/processes.

@TheXenocide
Copy link

I've been following this discussion for awhile and still feel like there will need to be a solution for this at some point, but today I started pondering whether or not it would be appropriate to consider MonoWASM more of a runtime than a TFM? Surely there's still a framework difference between what is actually supported in WASM and what is supposed to be supported by .NET Standard 2.0, but perhaps it's worth considering whatever subset of the BCL winds up in the TFM to be more cross-platform (and therefore consumable as a dependency by some more inclusive subset like .NET Standard 2.0 or higher without needing to mash up TFMs and duplicate outputs into multiple TFMs in NuGet packages like PCL profiles had us doing), but then simultaneously allows us to provide runtime-specific implementations. Just as an example to illustrate the thought experiment for anybody not following, let's say there's a new TFM called "netsubset" that can be consumed by projects targeting netsubset, netstandard2.0+ or net472+. Then we have an RID like "monowasm" so we can make a single NuGet package with a single TFM and, if necessary, provide runtime-specific native implementations such that the Blazor version uses some JavaScript interop and the Windows version uses some C-compatible extern dll, etc.

Anyway, just some food for thought I wanted to get into the discussion before it passed out of my brain.

@TheXenocide
Copy link

Just a quick followup I forgot to mention, but this also allows a NuGet dependency of a Blazor-oriented class library to provide separate runtime-specific implementations for Client-side and Server-side Blazor without requiring multiple compilation passes, etc.

@mattleibow
Copy link

+1 on the RID. That is more accurate. I would have a .NET Standard library that has native dependencies. I would then but those native dependencies in a RID - one for mac, one for win and one for wasm.

@nkolev92
Copy link
Member

Triage: In the net5 spec, there's a comment that a tfm for wasm is not in the plans right now: https://github.com/dotnet/designs/blob/main/accepted/2020/net5/net5.md.

Assigning to @JonDouglas to confirm.

@JonDouglas
Copy link
Contributor

That's right, until there's a https://github.com/dotnet/designs proposed and agreed upon, there's not much the NuGet team can do here but continue to facilitate the discussion & collect upvotes.

If/when a proposal does happen, please ping the NuGet team so we can help provide some perspective from the packaging perspective.

@bricelam
Copy link

Note that there is a RID for Wasm that you can use.

  • runtimes/
    • browser-wasm/
      • lib/
        • net6.0/
          • MyLib.dll <-- This assembly is used on Wasm
  • lib/
    • net6.0-ios/
      • MyLib.dll <-- This assembly is used on iOS
    • net6.0/
      • MyLib.dll <-- This assembly is used everywhere else

You can also add net6.0 MSBuild files to include additional assets on Wasm. (This is what I did to enable SQLitePCLRaw)

<Project>
  <ItemGroup Condition=" '$(RuntimeIdentifier)' == 'browser-wasm' ">
    <NativeFileReference Include="$(MSBuildThisFileDirectory)..\..\runtimes\browser-wasm\nativeassets\net6.0\e_sqlite3.a" />
  </ItemGroup>
</Project>

@trungnt2910
Copy link

Currently it is possible to use a net6.0-browser TFM on NuGet. See this package for reference.

image

This is how the package was generated.

@nkolev92
Copy link
Member

Hey @trungnt2910, appreciate the community effort, but I felt that I have to point out that having lib\net6.0-browser0.1 does not guarantee the packages will work as expected.

NuGet only really string matches the platform part of the framework, so you wouldn't be getting a warning from NuGet about it.

Long term NuGet is only guaranteed to detect patterns that are recognized .NET frameworks.

@trungnt2910
Copy link

When I installed the NuGet package to a console project multitargeting Windows and Browser it still restores correctly though.

@nkolev92
Copy link
Member

That's because client currently treats the platform part as an opaque string.
That's an implementation detail. It may change in the future, if there's a need for NuGet to explicitly know about certain frameworks.

It's the .NET SDK that will validate supported frameworks.

@trungnt2910
Copy link

It's the .NET SDK that will validate supported frameworks.

I am aware of this. That is how I got dotnet pack for the library above to work in the first place.

That's an implementation detail.

This is a desired feature, especially in the future when projects like MAUI for GTK, which uses a custom net6.0-gtk framework gets released.

@nkolev92
Copy link
Member

I need to make a correction.
Adding new platforms is ok for net5.0 and later frameworks. Tizen is a good example for that.

Adding custom mappings would have a different bar but it doesn't like the project has that intent.

@trungnt2910
Copy link

Adding new platforms is ok for net5.0 and later frameworks. Tizen is a good example for that.

If it's legal, then my example net6.0-browser should be too, because it uses the same approach as Tizen of installing a custom SDK to enable the TFM.

Adding custom mappings would have a different bar

If by custom mappings you mean stuff like monowasm1.0 or somerandombrowserTFMname6.0 then no, I don't intend to add these.

@bricelam
Copy link

Please see #8186 (comment) for an approach that works with the existing WebAssembly build tools for .NET. If you're building your own toolchain for Wasm on .NET, I strongly recommend you layer it on top of the existing tools being developed in the dotnet/runtime repo. Dividing the ecosystem now will only harm the long-term success of Wasm on .NET.

@trungnt2910
Copy link

Please see #8186 (comment) for an approach

I've read this comment before, this seems more like a workaround. In scenarios such as multitargeting in a single project, it is not as transparent as having a browser TFM.

I strongly recommend you layer it on top of the existing tools being developed in the dotnet/runtime repo

This is exactly what I'm doing. Similar to what .NET 6 mobile seems to do (reference some .NET bindings for Java/Objective-C libraries, and build using Mono toolchains), I'm simply extending wasm-experimental and create a type safe binding for JavaScript APIs in C#.

@bricelam
Copy link

@radical Does mono-wasm plan to add any .NET projections of Wasm/Wasi/JavaScript APIs? If this API surface is significant (like the NDK bindings on Android), I agree this would warrant a new TFM. But if the base .NET API is sufficient for exposing the underlying platform's functionality, then using a RID seems more consistent--I don't consider the Linux and macOS RIDs to be a workaround.

@yowl
Copy link

yowl commented Oct 13, 2022

I don't know what mono are planning, nor exactly how this relates to new TFMs/Rids, but what I noticed with Wasi and Wasm in the browser was that WASI are based on cloudlibc where as Wasm in the browser (using emscripten) is based on muslc. These 2 libc implementations use different header constants making a single src/libraries/Common/src/Interop/Unix/System.Native/Interop.OpenFlags.cs for example, tricky. I wrote up a little of what I found for the NativeAOT-LLVM experiment at dotnet/runtimelab#1850

@trungnt2910
Copy link

But if the base .NET API is sufficient for exposing the underlying platform's functionality

Saying that the base .NET API (all the .NET classes along with System.Runtime.InteropServices.JavaScript.JSImport) is sufficient for exposing the browser's functionality seems to be equivalent to claiming that built-in .NET classes and DllImport is enough to make use of Windows's functionality. It's possible to just DllImport every Win32 API we need to build Windows applications; there's no need to reference System.Windows.Forms and use the complex net6.0-windows framework.

@bricelam
Copy link

Yes, I think you get my point--the Win32 windowing APIs represents something significant that should be surfaced via .NET APIs (WinForms).

I don't know if Wasm has anything like that. That's why I'm asking. Just surfacing something like alert doesn't warrant an entire TFM--that could just be a .net6.0 NuGet package that throws outside of the browser-wasm runtime.

@trungnt2910
Copy link

I don't know if Wasm has anything like that.

What I'm talking about is specifically WASM on the browser, and I also believe that the OP meant the same when he opens this issue concerning Uno Platform libraries.

Just surfacing something like alert

Did the TypeScript project generate 18000 lines just to surface an alert or two?
(I'm citing just the DOM library as functionality in the ES core libraries are equivalent to what .NET already exposes).

@TheXenocide
Copy link

TheXenocide commented Oct 24, 2022

Correct me if I'm wrong, but WASM is a runtime implementation designed to support the same IL as the current windows/linux/etc. runtimes. The TFM is about "non-native" reference contracts (in quotes because there are runtime-specific implementations of those libraries), so netcoreapp3.1, net5, net6, etc. are essentially API levels of common behavior expectations and win10, browser-wasm, linux, etc. are platform specific runtime implementations. Has there been a change in direction with these composite framework/runtime strings or are we still expecting TFM to fundamentally define API shape and RID to define platform-specific implementation behavior? I'm a little confused about what net6-browser means as a TFM, as opposed to net6 as a TFM and browser (or browser-wasm) as a RID. I believe the goal with the current TFM design choices has been to reduce the types of fragmentation that happened with PCL profiles and prior mentioned "known-not-right" patterns that we've tried to avoid since and want to make sure we're not slipping back into the dark ages I remember loathing oh-so-well 😅

@TheXenocide
Copy link

TheXenocide commented Oct 24, 2022

Side note, it does appear that runtime.json has both browser and browser-wasm definitions (with an import from browser-wasm to browser), so I'm assuming there's some magic string behavior regarding the net6-browser "TFM" which is actually a TFM+RID? It might be helpful to update documentation with guidance for how browser wasm packages are expected to be authored (e.g. the runtime catalog), though I feel like it looks like we landed on a "browser" RID which matches what I would hope for over a whole new TFM.

@trungnt2910
Copy link

what net6-browser means as a TFM

.NET 6 with browser-specific APIs included, so that, for example, System.Runtime.InteropServices.JavaScript.JsHost.GlobalThis is guaranteed to point to the window object, and possibly enable DOM library C# bindings.

For cross-platform things that are consistent between platforms, such as Console.WriteLine("Hello World") or extracting .zip files, a browser specific TFM is not needed, similar to how net6.0 console apps without the -windows part run well on Windows.

The current .NET 7 JavaScript APIs are a bit too limited (you cannot invoke member functions,...) and seems to be designed for cases where a large part of the library is written in JavaScript. It does not allow writing Web application logic without a single line of JS.

If the correct approach is just using net7.0 for all browser apps, I wonder why .NET can't just expose some System.Runtime.InteropServices.Java.Lang.Object API for all .NET, mark it with [SupportedOSPlatform("android")] and build all Android apps based on that.

@TheXenocide
Copy link

Those sound like runtime features to me (they even have runtime in the namespace). Perhaps there are use cases I'm not familiar with for libraries that don't intend to be cross-platform; I'm mostly concerned with the complexities of authoring cross-platform libraries myself (like a .NET 6/7/8 library that supports both client-side blazor, server-side blazor, MAUI on Android, MAUI on iOS, etc. with a netstandard2.x reference contract and native implementations of UI behaviors and so on). The TFM, to me, is a BCL/compatibility level, and the RID allows for platform-speicific implementations of libraries built on top of minimum BCL compatibility levels. The browser is just one of many platforms .NET 6 supports. In practice, I have found this model far superior to all the permutations and complexity we had when we were making new profiles for every platform years back and am hoping we can avoid falling back into the same trap. Either way, multi-target projects make for a much easier means of organization for multi-platform code, but I am still concerned about the packaging/compatibility validation aspects becoming overly complex.

@trungnt2910
Copy link

trungnt2910 commented Oct 26, 2022

Those sound like runtime features to me (they even have runtime in the namespace).

This library, which is simply an OOP wrapper against a bunch of methods decorated with "Runtime" feature attributes, a remnant of the "dark ages", is currently one of the things that are behind the "overly complex" net6.0-windows TFM.

Why can't we just distribute this library as a NuGet package for net6.0, and possibly throw some old Mono implementation of WinForms for non-Windows RIDs?

It all leads to bricelam's previous comment:

If this API surface is significant (like the NDK bindings on Android), I agree this would warrant a new TFM.

The API surface is significant, and there has been previous attempts to bring C# bindings for the browser APIs (such as the now-abandoned Xamarin.WebSharp).

Seems like the only reason why net6.0-browser is not officially a thing because there isn't a binding like Xamarin.Android or System.Windows.Forms officially maintained. This doesn't mean that there shouldn't be such a library (see my comments above, I won't discuss more about this as it's a bit off-topic for a NuGet issue).

The only thing I would request in this issue is for NuGet and nuget.org to keep the current state, and not do something like

if (!_whitelistedPlatformNames.Contains(platformString))
{
    FailWhateverPackageOperation($"Unrecognized Platform: {platformString}");
}

Furthermore:

Perhaps there are use cases I'm not familiar with for libraries that don't intend to be cross-platform; I'm mostly concerned with the complexities of authoring cross-platform libraries myself.

I'm actually also concerned with authoring cross-platform libraries myself. Something like a .NET 6/7 ToastNotification library for the Uno Platform in a single project, targeting net7.0-windows; net7.0-android; net7.0-macos; net7.0-ios; net7.0-tizen as well as net6.0-gtk for the GTK head and net7.0-browser for the WASM head, instead of having to split into separate packages for frameworks without a TFM.

Also, I would like to write code like:

Window.Instance.Location.Href = "https://www.youtube.com/watch?v=dQw4w9WgXcQ"

with all the Visual Studio Intellisense features and strongly typed-ness of C#, instead of having to do this by exporting a function from a JavaScript module, distribute it with the website, and then use [JSImport] to call it from managed code.

@JonDouglas
Copy link
Contributor

Please feel free to leave feedback on the following PR which is currently open related to this topic:

dotnet/designs#289

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests