dotnet / runtime

.NET is a cross-platform runtime for cloud, mobile, desktop, and IoT apps.
https://docs.microsoft.com/dotnet/core/
MIT License
14.55k stars 4.54k forks source link

[System.Runtime.InteropServices] Introduce broader platform definitions. #25172

Open migueldeicaza opened 6 years ago

migueldeicaza commented 6 years ago

Unity, Xamarin and Mono users suffers from a historic problem with platform detection. This patch introduces definitions for all the existing platforms that .NET supports beyond .NET Core via Unity and Xamarin.

To cope with the historical setting that OSX has been used in existing code to probe for an Apple OS (iOS, tvOS, watchOS), we introduce new values for all four apple platforms (macOS, iOS, tvOS, watchOS) and also return true when probed for the legacy OSX setting.

The same principle is applied to Linux, we will continue to return true, but the more specific versions of Tizen and Android are supported.

The list has been expanded to all the platforms currently supported by Mono and Unity.

Proposed Changes

The System.Runtime.InteropServices.OSPlatform is updated to surface the following public definitions:

        public static OSPlatform Android { get; } = new OSPlatform("ANDROID");

        public static OSPlatform iOS { get; } = new OSPlatform("IOS");

    // Returns true for Linux, Tizen and Android systems
        public static OSPlatform Linux { get; } = new OSPlatform("LINUX");

        public static OSPlatform macOS { get; } = new OSPlatform("MACOS");

        public static OSPlatform N3DS { get; } = new OSPlatform("3DS");

    // This one has historically been used as "Apple platforms", so it returns true
    // on iOS, tvOS, macOS, watchOS.
        public static OSPlatform OSX { get; } = new OSPlatform("OSX");

        public static OSPlatform PlayStation4 { get; } = new OSPlatform("PS4");

        public static OSPlatform PlayStationPortable2 { get; } = new OSPlatform("PSP2");

        public static OSPlatform PlayStationVita { get; } = new OSPlatform("PSVITA");

        public static OSPlatform Switch { get; } = new OSPlatform("SWITCH");

        public static OSPlatform Tizen { get; } = new OSPlatform("TIZEN");

        public static OSPlatform tvOS { get; } = new OSPlatform("TVOS");

        public static OSPlatform watchOS { get; } = new OSPlatform("WATCHOS");

        public static OSPlatform WebAssembly { get; } = new OSPlatform("WEBASSEMBLY");

        public static OSPlatform WiiU { get; } = new OSPlatform("WIIU");

        public static OSPlatform Windows { get; } = new OSPlatform("WINDOWS");

        public static OSPlatform XboxOne { get; } = new OSPlatform("XBOXONE");

Additionally, RuntimeInformation's IsOSPlatform method is modified to cope with the aliasing described before, and Android and Tizen systems are Linux systems, and macOS, tvOS, iOS and watchOS are reported as OSX systems.

Twist: we could make OSX deprecated as well, and introduce an additional AppleOS definition or a Darwin definition to cover Apple-based OSes.

PR

A pull request with the first batch is available here:

https://github.com/dotnet/corefx/pull/27378

karelz commented 6 years ago

@migueldeicaza is it critical for 2.1? (beside the fact we should have had it years ago)

danmoseley commented 6 years ago

@terrajobst did you review?

svick commented 6 years ago

Since you're already effectively creating a graph of platforms (where e.g. Android is a child of Linux and iOS is a child of OSX), would it make sense to introduce other parent nodes?

For example, there could be Nintendo for Switch and WiiU, or even PortableConsole for 3DS and PS Vita. Would this be useful? In other words, would it make sense for people to write code like if (IsOSPlatform(Nintendo))?

terrajobst commented 6 years ago

A few questions:

migueldeicaza commented 6 years ago

I would like to revisit my proposal to add additional operating system, and the heritage of the operating systems as follows:

Unix:

Linux (just to state explicitly, it was embedded in the patch):

Web:

Users should understand the DAG and the relationship, the idea is that you can make assumptions about the host OS, not only to P/Invoke, but about file locations or services available on the system.

For Windows, I would propose that we have two environments:

Windows:

Architecture is already handled elsewhere (RuntimeInformtion.OSArchitecture and RuntimeInformation.ProcessArchitecture).

AOT flag: This is not intended to capture whether the code is AOT or not, it might be useful to provide that as a separate API (RuntimeInformation.Capabilities? which would be an Flags enum with JIT, Interpreter, AOT). For example, we are introducing Interpretation to iOS, despite being AOT.

Sony platforms: those use different operating systems and the host platform have different capabilities, which Unity developers account for

PSP can get out of the list.

kumpera commented 6 years ago

A bit of feedback on the WebAssembly front.

Mono will be adopting, for now, the proposed OSPlatform name - "WEBASSEMBLY".

WebAssembly has an additional quirk that maybe makes using OSPlaform ("WEBASSEMBLY") not ideal. There are multiple deployment targets and those are interesting enough to be feature detectable. This includes browsers, node, electron/code and other non-web embeddings.

zwcloud commented 5 years ago

@terrajobst Any updates on this?

eerhardt commented 5 years ago

This feels exactly what the concept of RuntimeIdentifier (RID) is trying to solve. Would it be more appropriate to solve this problem by enhancing the existing RID system/design? And making it an official API in corefx?

That way we don’t need to have the compatibility graph hard-coded. And we don’t have duplicate/competing concepts in our platform (RIDs vs this proposal).

I believe the RID design already covers all of @terrajobst’s concerns above as well.

migueldeicaza commented 5 years ago

Sure - that could work, except RID currently does not cover some of the scenarios already described above.

In any case, the inspiration might come from RIDs, but needs to ultimately be surfaced here.

eerhardt commented 5 years ago

Can you elaborate on which scenarios aren’t supported by the RID concept/design? Do you mean just adding the above platforms (iOS, switch, tvOS, etc) to the graph? Or is there something lacking about the design?

In my opinion it makes more sense to have a graph of these platforms which can be serviced/added to over time, as opposed to new C# properties for each supported platform. That way we don’t need to add new APIs when we add a new platform.

am11 commented 4 years ago

IMO, RID could be used as documented for additional platforms supported by Mono as @eerhardt has pointed out. (the wording of doc could probably be updated to reflect os or kernel name instead of os name).

There are two unfortunate cases with RID, which probably cannot be rectified now (post-release) but could be avoided for future platforms:

  1. disparity in RID design doc and RID in practice in case of linux-musl-{arch} series. According to the doc, architecture-aware RID should have been linux-x64-musl (additional qualifier comes at the end) and architecture-agnostic one is correct, linux-musl. This is analogous to the existing win-aot and win-x64-aot and other such aot examples. Now we have to special case musllibc architecture-agnostic case, while tokenizing (as of now) 387 keys.
    curl -s https://raw.githubusercontent.com/dotnet/runtime/bd98efe6adf4638d699f92d4b24bc6cdd5e0ca47/src/libraries/pkg/Microsoft.NETCore.Platforms/runtime.json | jq '.runtimes | keys | length'
  2. macos instead of osx, so when watchos, ios and tvos are included, they don't look odd.
eerhardt commented 4 years ago

Now we have to special case musllibc architecture-agnostic case, while tokenizing (as of now) 387 keys.

In the current RID design, the strings are not meant to be tokenized. In the RID documentation you linked above it defines a RID as: "A RID is an opaque string that identifies a platform."

However, there is a proposal to change the opaqueness.

am11 commented 4 years ago

Sure, thanks for pointing it out. I think even for human readability, musl case looks a bit odd. Do you think linux-musl-64 goes against the documented naming [os name].[version]-[architecture]-[additional qualifiers] and other RIDs win-x64-aot etc. and should have been linux-x64-musl instead? I understand it is probably now too late to change it, but if we had spotted it earlier, would we have corrected it or has it been considered just a free-form string?

danmoseley commented 4 years ago

@eerhard not disagreeing they're publicly opaque but note that we "parse" them some ourselves https://github.com/dotnet/runtime/blob/890c3b6ac0e7bb2fc9b4aa73201d417dd628b109/src/installer/Directory.Build.props#L228