Missing Linux `TargetFrameworkMoniker`
See original GitHub issueIs your feature request related to a problem? Please describe.
I am trying to create a cross-platform project with platform-specific implementations of several classes.
For most platforms, it would be possible to use the TargetFrameworkMoniker
to conditionally include files like MAUI does (see e.g. https://github.com/dotnet/maui/blob/main/src/Essentials/src/Essentials.csproj). However, no TargetFrameworkMoniker
is available for Linux.
This currently being unavailable also has the consequence that APIs like https://github.com/dotnet/runtime/pull/69980/files need an [UnsupportedPlatform("windows")]
attribute rather than only making the APIs available on Unix OSes.
Describe the solution you’d like
-
A
net7.0-linux
TargetFrameworkMoniker
-
alternative considered:
OperatingSystem.IsLinux()
- this relies on runtime analysis and makes methods larger or requires a wrapper that returns a platform-specific implementation at runtime. This also has the limitation that the called / referenced code needs to be referenced even when being compiled for Windows and vice versa - which leads to impossible situations. Especially for technology like bluetooth or other device support code you need to reference platform-specific TFMs which I believe you can’t do from a “neutral”net7.0
project. -
alternative considered: using inverse #if to assume that
if it is not Windows, iOS, Android, MacOS - it must be Linux
Pseudocode:
#if !(WINDOWS || MACOS || IOS || ANDROID)
. This seems brittle.
Additional context
These pages have no pointers about workarounds for this case:
Issue Analytics
- State:
- Created a year ago
- Reactions:4
- Comments:6 (2 by maintainers)
Top GitHub Comments
This feature definitely needs more attention, as it is making it unreasonable difficult to work on multi-target libraries and applications.
Note, this issue and my comment is not about having platform specific API build-in, but about creating cross-platform projects that achieve these APIs by themselves or referencing platform specific packages.
While in theory I would agree with @mhutch and the original design idea, it doesn’t seem to reflect how Target Frameworks are used in reality.
For instance, MAUI supports multiple target frameworks - Windows, Mac Catalyst, Android, iOS, and Tizen. If a developer needs to create a library for MAUI and use native APIs, they need to target these frameworks. If a developer needs to create a single-project that will work on all of these frameworks - they can use multitargeting and include all of these targets in the same project.
It works pretty well until you want to extend the list of available frameworks. While it’s not a problem in MAUI (yet), it is already a problem in frameworks like Avalonia and Uno. We support Linux and browser targets as well. Which means we already cannot treat plain “net6.0” as Linux, because it can be a browser as well. As a reference, this PR is blocked because of missing TFM support.
In the end, we have a mix of:
In order to support multiple platforms, developers now need to have a mix of TFM with #if/else directives for the well-supported platforms and OperatingSystem.IsLinux/IsBrowser runtime calls for “net6.0” (i.e. “other”). Not to mention, it gets complicated with NuGet packages that need to be somehow referenced from the “net6.0” for both Linux and browser (and without browser AOT compiled rightfully complaining about it).
If the .NET ecosystem switches from TFM to RID - that’s fine, but it seems to require way more changes including availability of platform-specific APIs.
As an example, there is an interesting project that has to build their own workload with custom TFM in order to achieve expected behavior.
I think what’s actually missing here is the ability to compile for multiple RIDs.
TFMs are about the APIs that are available for your code to compile against, RIDs are about what platform/architecture your code runs on. If your implementation is specific to the platform that you run on, then should be a RID-specific implementation. However, we currently have no tooling for compiling a project for multiple RIDs, and packages that contain multiple RID-specific implementations currently must be constructed from the output of multiple projects. The lack of multi-RID support has les to folks using TFMs for this purpose instead.
We have two mechanisms for platform-specific APIs. We can put them in a platform-specific TFM, which is a good solution when exposing a large feature “area” such as an app model that is tightly bound to a single platform. Alternatively, we can make the API available on all platforms but only provide implementations on a subset of platforms. In this case, we use annotations and analyzers to indicate to the developer when they may need runtime checks in their code before calling the API. This is a lighter weight solution as it does not force consuming code to have platform-specific builds, and is great for smaller scale platform-specific feature such as a individual method to set a platform-specific option on a cross-platform feature. There is of course a continuum between these extremes and there’s not necessarily a “right” answer to which solution should be used. However, we do not yet have any Linux-specific APIs where a TFM would clearly be the better of the two options, which is why we don’t have the TFM (yet).