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

OS version information #1017

Closed
blobor opened this Issue Feb 25, 2015 · 101 comments

Comments

Projects
None yet
@blobor

blobor commented Feb 25, 2015

I want to write cross-platform class library based on .NET Core. I need to invoke native libraries in Windows (kernel32) and in UNIX (libc).

Currently Environment class is missing OSversion property. Is there some way to determine in what OS currently my assembly is running?

@ellismg

This comment has been minimized.

Show comment
Hide comment
@ellismg

ellismg Feb 27, 2015

Contributor

This seems like a reasonable addition to me. I know we are nervous to add back version properties, but some way to figure out if you are on Linux/OSX/Windows seems reasonable.

Contributor

ellismg commented Feb 27, 2015

This seems like a reasonable addition to me. I know we are nervous to add back version properties, but some way to figure out if you are on Linux/OSX/Windows seems reasonable.

@blobor

This comment has been minimized.

Show comment
Hide comment
@blobor

blobor Feb 27, 2015

Thank you 👍

blobor commented Feb 27, 2015

Thank you 👍

@davkean

This comment has been minimized.

Show comment
Hide comment
@davkean

davkean Feb 28, 2015

Member

I've seen a similar request for ARM32 vs other platforms, checking pointer size is not good enough in that situation.

Member

davkean commented Feb 28, 2015

I've seen a similar request for ARM32 vs other platforms, checking pointer size is not good enough in that situation.

@HellBrick

This comment has been minimized.

Show comment
Hide comment
@HellBrick

HellBrick Feb 28, 2015

I would have upvoted this issue if it were possible =) I have a project that uses quite a few Environment.OSVersion.Platform checks in the common code that's supposed to work on both Windows and Linux, so missing this API would really hurt. (Don't really care for the Version, VersionString and ServicePack properties though.)

HellBrick commented Feb 28, 2015

I would have upvoted this issue if it were possible =) I have a project that uses quite a few Environment.OSVersion.Platform checks in the common code that's supposed to work on both Windows and Linux, so missing this API would really hurt. (Don't really care for the Version, VersionString and ServicePack properties though.)

@ellismg

This comment has been minimized.

Show comment
Hide comment
@ellismg

ellismg Mar 26, 2015

Contributor

I think we do need to expose something like the Platform your are running on (Linux/Windows/Mac) and the processor.

For example, Kestrel does the following to figure out if they are running on windows or not:

        public static bool IsWindows()
        {
#if DNXCORE50
            return true;
#else
            var p = (int)Environment.OSVersion.Platform;
            return (p != 4) && (p != 6) && (p != 128);
#endif
        }

Which is completely busted when you want to run .NET Core on CoreCLR. If we don't provide an API I can imagine the next best thing being just trying to P/Invoke to random Windows or Unix APIs and determine the answer that way.

I have also seen cases where parts of ASP.NET vNext sniff PROCESSOR_ARCHIECTURE environment variable to detect x86 vs x64. I have been moving that code to use IntPtr.Size, but as David says, there's no way to figure out ARM or not with that.

Contributor

ellismg commented Mar 26, 2015

I think we do need to expose something like the Platform your are running on (Linux/Windows/Mac) and the processor.

For example, Kestrel does the following to figure out if they are running on windows or not:

        public static bool IsWindows()
        {
#if DNXCORE50
            return true;
#else
            var p = (int)Environment.OSVersion.Platform;
            return (p != 4) && (p != 6) && (p != 128);
#endif
        }

Which is completely busted when you want to run .NET Core on CoreCLR. If we don't provide an API I can imagine the next best thing being just trying to P/Invoke to random Windows or Unix APIs and determine the answer that way.

I have also seen cases where parts of ASP.NET vNext sniff PROCESSOR_ARCHIECTURE environment variable to detect x86 vs x64. I have been moving that code to use IntPtr.Size, but as David says, there's no way to figure out ARM or not with that.

@Priya91 Priya91 assigned Priya91 and unassigned terrajobst Apr 17, 2015

@Priya91

This comment has been minimized.

Show comment
Hide comment
@Priya91

Priya91 Apr 17, 2015

Member

Exposing the desktop types in cloud platform, is not desired, as we need to keep adding support back to desktop. A good way to go will be to create a new contract, with the following api-definition:

namespace System.XPlatform
{
    public struct OSName
    {
        public OSName(string osName) { }
        public static OSName Windows { get; }
        public static OSName Linux { get; }
        public static OSName OSX { get; }
    }

    public static class RuntimeInfo
    {
         public static bool IsOSPlatform(OSName osName) { return default(bool); }
    }
}

cc @KrzysztofCwalina @ellismg @weshaggard @davkean @stephentoub : If this api design is agreeable i'll start implementing this.

Member

Priya91 commented Apr 17, 2015

Exposing the desktop types in cloud platform, is not desired, as we need to keep adding support back to desktop. A good way to go will be to create a new contract, with the following api-definition:

namespace System.XPlatform
{
    public struct OSName
    {
        public OSName(string osName) { }
        public static OSName Windows { get; }
        public static OSName Linux { get; }
        public static OSName OSX { get; }
    }

    public static class RuntimeInfo
    {
         public static bool IsOSPlatform(OSName osName) { return default(bool); }
    }
}

cc @KrzysztofCwalina @ellismg @weshaggard @davkean @stephentoub : If this api design is agreeable i'll start implementing this.

@davkean

This comment has been minimized.

Show comment
Hide comment
@davkean

davkean Apr 17, 2015

Member

Can you help me understand that conclusion? Adding enum values to .NET Framework ("desktop") is extremely easy, especially given NET Framework will never have to return them or plumb them through. In fact, it already contains the values (Unix/Linux, MacOS and Win32NT) that currently we're planning to port to.

Adding this into a new assembly is even more problematic, and we're now forced to introduce a new library (with one type!) that we'll need to build for each platform, even though we're already building mscorlib/CoreCLR for each one and it already contains the APIs.

I believe we should stick with the previous plan and expose the existing APIs.

cc @terrajobst @nguerrera @jaredpar @ericstj the rest of the Framework Design Core.

Member

davkean commented Apr 17, 2015

Can you help me understand that conclusion? Adding enum values to .NET Framework ("desktop") is extremely easy, especially given NET Framework will never have to return them or plumb them through. In fact, it already contains the values (Unix/Linux, MacOS and Win32NT) that currently we're planning to port to.

Adding this into a new assembly is even more problematic, and we're now forced to introduce a new library (with one type!) that we'll need to build for each platform, even though we're already building mscorlib/CoreCLR for each one and it already contains the APIs.

I believe we should stick with the previous plan and expose the existing APIs.

cc @terrajobst @nguerrera @jaredpar @ericstj the rest of the Framework Design Core.

@ellismg

This comment has been minimized.

Show comment
Hide comment
@ellismg

ellismg Apr 17, 2015

Contributor

Can you help me understand that conclusion? Adding enum values to .NET Framework ("desktop") is extremely easy, especially given NET Framework will never have to return them or plumb them through. In fact, it already contains the values (Unix/Linux, MacOS and Win32NT) that currently we're planning to port to.

The BSD port of CoreCLR is underway by the community now and they are making good progress. I assume as soon as they are done, we will want to be answer the question "am I running on BSD". So we would have to version the contract anyway. With the above design we can say call RuntimeInfo.IsOSPlatform(new OSName("BSD")) even if we can't quickly rev the contract.

Adding this into a new assembly is even more problematic, and we're now forced to introduce a new library (with one type!) that we'll need to build for each platform, even though we're already building mscorlib/CoreCLR for each one and it already contains the APIs.

Not having a dependency on the runtime when possible feels like a win to me. I don't understand the pushback about having to build multiple times. We're already going to be in that boat for many pieces of CoreFX.

I also see no reason why long term the implementation of the contract could not be a façade over the runtime itself, if we find that that's the correct factoring.

Contributor

ellismg commented Apr 17, 2015

Can you help me understand that conclusion? Adding enum values to .NET Framework ("desktop") is extremely easy, especially given NET Framework will never have to return them or plumb them through. In fact, it already contains the values (Unix/Linux, MacOS and Win32NT) that currently we're planning to port to.

The BSD port of CoreCLR is underway by the community now and they are making good progress. I assume as soon as they are done, we will want to be answer the question "am I running on BSD". So we would have to version the contract anyway. With the above design we can say call RuntimeInfo.IsOSPlatform(new OSName("BSD")) even if we can't quickly rev the contract.

Adding this into a new assembly is even more problematic, and we're now forced to introduce a new library (with one type!) that we'll need to build for each platform, even though we're already building mscorlib/CoreCLR for each one and it already contains the APIs.

Not having a dependency on the runtime when possible feels like a win to me. I don't understand the pushback about having to build multiple times. We're already going to be in that boat for many pieces of CoreFX.

I also see no reason why long term the implementation of the contract could not be a façade over the runtime itself, if we find that that's the correct factoring.

@davkean

This comment has been minimized.

Show comment
Hide comment
@davkean

davkean Apr 17, 2015

Member

Not having a dependency on the runtime when possible feels like a win to me. I don't understand the pushback about having to build multiple times.

For every port to a new OS, the runtime is going to have to rev, so I'm not understanding that concern. It's not just building multiple times; it's creating the contract, the multiple projects, NuGet packages, etc. It feels like we're adding complexity to an area that is not needed. The APIs already exist and given the large amount of code already in mscorlib that probably needs to do something similar, we'd probably need to keep them around anyway - or put these new APIs into mscorib.

With the above design we can say call RuntimeInfo.IsOSPlatform(new OSName("BSD")) even if we can't quickly rev the contract.

You can achieve the same thing with enums OSVersion.Platform == (PlatformID)10. In the same way that '10' is a magic handshake, so is the hardcoded BSD.

Member

davkean commented Apr 17, 2015

Not having a dependency on the runtime when possible feels like a win to me. I don't understand the pushback about having to build multiple times.

For every port to a new OS, the runtime is going to have to rev, so I'm not understanding that concern. It's not just building multiple times; it's creating the contract, the multiple projects, NuGet packages, etc. It feels like we're adding complexity to an area that is not needed. The APIs already exist and given the large amount of code already in mscorlib that probably needs to do something similar, we'd probably need to keep them around anyway - or put these new APIs into mscorib.

With the above design we can say call RuntimeInfo.IsOSPlatform(new OSName("BSD")) even if we can't quickly rev the contract.

You can achieve the same thing with enums OSVersion.Platform == (PlatformID)10. In the same way that '10' is a magic handshake, so is the hardcoded BSD.

@ellismg

This comment has been minimized.

Show comment
Hide comment
@ellismg

ellismg Apr 17, 2015

Contributor

@Priya91 I really like what you have here. My only piece of feedback is around the namespace. Could we use something like System.Runtime.InteropServices instead? Do you have an idea for the contract name itself?

Contributor

ellismg commented Apr 17, 2015

@Priya91 I really like what you have here. My only piece of feedback is around the namespace. Could we use something like System.Runtime.InteropServices instead? Do you have an idea for the contract name itself?

@Priya91

This comment has been minimized.

Show comment
Hide comment
@Priya91

Priya91 Apr 17, 2015

Member

@ellismg : Yes, system.runtime.interopservices sounds good. Since these apis relate to runtime environment information how about System.Runtime.Environment..

Member

Priya91 commented Apr 17, 2015

@ellismg : Yes, system.runtime.interopservices sounds good. Since these apis relate to runtime environment information how about System.Runtime.Environment..

@ellismg

This comment has been minimized.

Show comment
Hide comment
@ellismg

ellismg Apr 17, 2015

Contributor

I like that a lot!

Contributor

ellismg commented Apr 17, 2015

I like that a lot!

@davkean

This comment has been minimized.

Show comment
Hide comment
@davkean

davkean Apr 17, 2015

Member

(Side note that will clash with Environment class in the System namespace)

Member

davkean commented Apr 17, 2015

(Side note that will clash with Environment class in the System namespace)

@ellismg

This comment has been minimized.

Show comment
Hide comment
@ellismg

ellismg Apr 17, 2015

Contributor

@davkean What clashes? If the contract is called System.Runtime.Environment?

Contributor

ellismg commented Apr 17, 2015

@davkean What clashes? If the contract is called System.Runtime.Environment?

@davkean

This comment has been minimized.

Show comment
Hide comment
@davkean

davkean Apr 17, 2015

Member

Ah, I thought you were talking about the namespace.

Member

davkean commented Apr 17, 2015

Ah, I thought you were talking about the namespace.

@Priya91

This comment has been minimized.

Show comment
Hide comment
@Priya91

Priya91 Apr 17, 2015

Member

Ok, sounds like we have reached a conclusion with the latest proposal. I'll mark the issue with accepting PRs. Going along with the API review process @blobor: will you be providing PR, or only contributing the issue?

Member

Priya91 commented Apr 17, 2015

Ok, sounds like we have reached a conclusion with the latest proposal. I'll mark the issue with accepting PRs. Going along with the API review process @blobor: will you be providing PR, or only contributing the issue?

@weshaggard

This comment has been minimized.

Show comment
Hide comment
@weshaggard

weshaggard Apr 17, 2015

Member

The API looks good overall to me as well but I was curious about why you need the OSName struct? Is there a particular reason that is necessary instead of just using a string? We could still use OSName as a place holder for predefined sets.

Member

weshaggard commented Apr 17, 2015

The API looks good overall to me as well but I was curious about why you need the OSName struct? Is there a particular reason that is necessary instead of just using a string? We could still use OSName as a place holder for predefined sets.

@Priya91

This comment has been minimized.

Show comment
Hide comment
@Priya91

Priya91 Apr 17, 2015

Member

@weshaggard : Any string parsing that has to be done, can be abstracted in OSName instead of IsOSPlatform, moreover, we expect a string that represents an OSName, instead of any string.

Member

Priya91 commented Apr 17, 2015

@weshaggard : Any string parsing that has to be done, can be abstracted in OSName instead of IsOSPlatform, moreover, we expect a string that represents an OSName, instead of any string.

@ellismg

This comment has been minimized.

Show comment
Hide comment
@ellismg

ellismg Apr 17, 2015

Contributor

It also helps with discoverability. You know you need an OSName and that guides you to the static properties.

Contributor

ellismg commented Apr 17, 2015

It also helps with discoverability. You know you need an OSName and that guides you to the static properties.

@akoeplinger

This comment has been minimized.

Show comment
Hide comment
@akoeplinger

akoeplinger Apr 18, 2015

Member

How is this supposed to work under the hood, i.e. will it end up calling uname?
Linux also seems to be quite broad (there are many different flavors), are there plans to expose more detailed info?

Just to confirm I understand this correctly: this can't be put into Environment because it'd mean it needs to be backported into Desktop .NET ?

Member

akoeplinger commented Apr 18, 2015

How is this supposed to work under the hood, i.e. will it end up calling uname?
Linux also seems to be quite broad (there are many different flavors), are there plans to expose more detailed info?

Just to confirm I understand this correctly: this can't be put into Environment because it'd mean it needs to be backported into Desktop .NET ?

@ellismg

This comment has been minimized.

Show comment
Hide comment
@ellismg

ellismg Apr 18, 2015

Contributor

How is this supposed to work under the hood, i.e. will it end up calling uname ?
Linux also seems to be quite broad (there are many different flavors), are there plans to expose more detailed info?

The current thinking is we would provide a different implementation assembly for each platform, which would hard-code the result, so we wouldn't call uname. We don't have current plans to expose something like Distro information from this type, but that's more because we don't understand the scenarios where one might want to do that.

We are hesitant, however, to do something like expose a Version of the OS your are running on.

Just to confirm I understand this correctly: this can't be put into Environment because it'd mean it needs to be backported into Desktop .NET?

Yeah, that is the basic problem.

Contributor

ellismg commented Apr 18, 2015

How is this supposed to work under the hood, i.e. will it end up calling uname ?
Linux also seems to be quite broad (there are many different flavors), are there plans to expose more detailed info?

The current thinking is we would provide a different implementation assembly for each platform, which would hard-code the result, so we wouldn't call uname. We don't have current plans to expose something like Distro information from this type, but that's more because we don't understand the scenarios where one might want to do that.

We are hesitant, however, to do something like expose a Version of the OS your are running on.

Just to confirm I understand this correctly: this can't be put into Environment because it'd mean it needs to be backported into Desktop .NET?

Yeah, that is the basic problem.

@blobor

This comment has been minimized.

Show comment
Hide comment
@blobor

blobor Apr 18, 2015

@Priya91 For now, only contributing the issue.

blobor commented Apr 18, 2015

@Priya91 For now, only contributing the issue.

@Priya91

This comment has been minimized.

Show comment
Hide comment
@Priya91

Priya91 Jun 2, 2015

Member

@richlander Forgive my ignorance, but can you define what you are expecting by use-case. This is what i think you are expecting:

Scenario: Develop a wallpaper changer app that runs on linux and windows, a very basic code sample:

namespace MyApp
{
    internal enum OSEnum
    {
        Windows, 
        Linux
    }

    public class WallpaperChanger
    {
        private void ChangeWindowsWallpaper() { }
        private void ChangeLinuxWallpaper() { }

        private OSEnum DetectOS()
        {
            /*In the absence of .NET Core platform detection API, I will have some custom logic here.*/
            return default(OSEnum);
        }

        internal void ShowImagesFromWeb() { }

        public void StartApp()
        {
            ShowImagesFromWeb();
            /*Wait for image select event to fire*/

            switch(DetectOS())
            {
                case OSEnum.Linux:
                    ChangeLinuxWallpaper();
                    break;

                case OSEnum.Windows:
                    ChangeWindowsWallpaper();
                    break;

                default:
                    throw new PlatformNotSupportedException();
            }
        }
    }
}
Member

Priya91 commented Jun 2, 2015

@richlander Forgive my ignorance, but can you define what you are expecting by use-case. This is what i think you are expecting:

Scenario: Develop a wallpaper changer app that runs on linux and windows, a very basic code sample:

namespace MyApp
{
    internal enum OSEnum
    {
        Windows, 
        Linux
    }

    public class WallpaperChanger
    {
        private void ChangeWindowsWallpaper() { }
        private void ChangeLinuxWallpaper() { }

        private OSEnum DetectOS()
        {
            /*In the absence of .NET Core platform detection API, I will have some custom logic here.*/
            return default(OSEnum);
        }

        internal void ShowImagesFromWeb() { }

        public void StartApp()
        {
            ShowImagesFromWeb();
            /*Wait for image select event to fire*/

            switch(DetectOS())
            {
                case OSEnum.Linux:
                    ChangeLinuxWallpaper();
                    break;

                case OSEnum.Windows:
                    ChangeWindowsWallpaper();
                    break;

                default:
                    throw new PlatformNotSupportedException();
            }
        }
    }
}
@richlander

This comment has been minimized.

Show comment
Hide comment
@richlander

richlander Jun 2, 2015

Member

@Priya91 These examples from @HellBrick are what I was looking for: #1017 (comment) describe actual platform differences that one must workaround. I'm sure that there are others.

Your example, while appreciated, displays a particular technique for differentiating behavior for an OS, but isn't really a use-case, since I cannot see the code that needs to differ. If I could see that, then it would be more clear which patterns and APIs would satisfy it. As it stands, I just have to trust you that the coding pattern that you chose is the right one.

Member

richlander commented Jun 2, 2015

@Priya91 These examples from @HellBrick are what I was looking for: #1017 (comment) describe actual platform differences that one must workaround. I'm sure that there are others.

Your example, while appreciated, displays a particular technique for differentiating behavior for an OS, but isn't really a use-case, since I cannot see the code that needs to differ. If I could see that, then it would be more clear which patterns and APIs would satisfy it. As it stands, I just have to trust you that the coding pattern that you chose is the right one.

@Priya91

This comment has been minimized.

Show comment
Hide comment
@Priya91

Priya91 Jun 2, 2015

Member

@richlander : In the previous example, to set the wallpaper i will have to interop into the linux/windows specific library and call the native api to actually do the change. The actual code that changes the wallpaper is irrelevant here, the focus should be on which library you are going to choose to interop to based on the platform. Those platform specific differences given by @HellBrick should be answered by the various framework apis that manipulate that data, for example, filesystem has path.directoryseparatorchar which will give the right path, similarly process.start to handle the differences, etc. I wouldn't even need to use this api for such scenarios, when i can directly call the cross plat .net framework api for that. It is in examples like above that this API is actually useful.

Member

Priya91 commented Jun 2, 2015

@richlander : In the previous example, to set the wallpaper i will have to interop into the linux/windows specific library and call the native api to actually do the change. The actual code that changes the wallpaper is irrelevant here, the focus should be on which library you are going to choose to interop to based on the platform. Those platform specific differences given by @HellBrick should be answered by the various framework apis that manipulate that data, for example, filesystem has path.directoryseparatorchar which will give the right path, similarly process.start to handle the differences, etc. I wouldn't even need to use this api for such scenarios, when i can directly call the cross plat .net framework api for that. It is in examples like above that this API is actually useful.

@nexussays

This comment has been minimized.

Show comment
Hide comment
@nexussays

nexussays Jun 2, 2015

Member

@whoisj

User-agent is kind of a hack and makes for a terrible API.

But so is this. That's exactly my point. Just because it's hidden behind enums and classes instead of strings doesn't make the conceptual intent of the API any different.
What is good about user-agent is that it is obvious that it is a hack and everyone uses feature checking now. That is, now one would check "does X exist in my current environment" versus "is my current environment Internet Explorer." (to wit, "am I running Linux" vs "is this filesystem case sensitive")

It would be better to have a "platform string" and admit where a kludge is a kludge than to have an enum which is out of date or provides a misleading API. ([edit] The point here being that an enum would have to be provided by corefx and I don't think the BCL should have any notion of what platforms it can run on.)

@HellBrick

One might argue that checking the platform type is not the best way to solve some of these problems

Yes, that is in fact exactly what I have been arguing.

Member

nexussays commented Jun 2, 2015

@whoisj

User-agent is kind of a hack and makes for a terrible API.

But so is this. That's exactly my point. Just because it's hidden behind enums and classes instead of strings doesn't make the conceptual intent of the API any different.
What is good about user-agent is that it is obvious that it is a hack and everyone uses feature checking now. That is, now one would check "does X exist in my current environment" versus "is my current environment Internet Explorer." (to wit, "am I running Linux" vs "is this filesystem case sensitive")

It would be better to have a "platform string" and admit where a kludge is a kludge than to have an enum which is out of date or provides a misleading API. ([edit] The point here being that an enum would have to be provided by corefx and I don't think the BCL should have any notion of what platforms it can run on.)

@HellBrick

One might argue that checking the platform type is not the best way to solve some of these problems

Yes, that is in fact exactly what I have been arguing.

@nexussays

This comment has been minimized.

Show comment
Hide comment
@nexussays

nexussays Jun 2, 2015

Member

@richlander

That's not an implementation detail. I'm interested in exploring the scenarios where the currently simple API might fall down.

Thank you :)


These are the examples @HellBrick provided, all of which (IMO) need a different and specific check.

  1. Choosing what folders to use for the data stored by our app: they are obviously different between Windows and Linux.

You want something like Environment.SpecialFolders. Also is it a guarantee that all distros of Linux are the same?

  1. Lowercasing all file names on Linux, to make it easier to live with the case-sensitive file system.

If this is a concern you should just do this universally to avoid the problem altogether. Clearly you need some FileSystem entity to check for capabilities of the particular environment you find yourself it. NTFS is case sensitive but Win32 isn't. There are Linux file systems that allow case-insensitivity. You probably don't care about that, would others?

  1. Adding an executable flag to a file if running on Unix.

Seems like this should be a property of a cross-platform file interaction API. Also isn't this a Posix API? Does OSX care about the executable bit?

  1. Using a different native library on Unix.

Available universally on any Linux distro? (if so, then yes, valid use-case for this feature!)

  1. Changing the way a child process is launched (on Windows it's just %path% %arguments%, on Unix it's mono %path% %arguments%.

This should absolutely be handled by some API, you shouldn't have to pass strings around like this.

  1. Switching in a different HttpClientHandler on Unix to work around a Mono bug.

Admittedly an edge case and not recommended behavior, no?

Member

nexussays commented Jun 2, 2015

@richlander

That's not an implementation detail. I'm interested in exploring the scenarios where the currently simple API might fall down.

Thank you :)


These are the examples @HellBrick provided, all of which (IMO) need a different and specific check.

  1. Choosing what folders to use for the data stored by our app: they are obviously different between Windows and Linux.

You want something like Environment.SpecialFolders. Also is it a guarantee that all distros of Linux are the same?

  1. Lowercasing all file names on Linux, to make it easier to live with the case-sensitive file system.

If this is a concern you should just do this universally to avoid the problem altogether. Clearly you need some FileSystem entity to check for capabilities of the particular environment you find yourself it. NTFS is case sensitive but Win32 isn't. There are Linux file systems that allow case-insensitivity. You probably don't care about that, would others?

  1. Adding an executable flag to a file if running on Unix.

Seems like this should be a property of a cross-platform file interaction API. Also isn't this a Posix API? Does OSX care about the executable bit?

  1. Using a different native library on Unix.

Available universally on any Linux distro? (if so, then yes, valid use-case for this feature!)

  1. Changing the way a child process is launched (on Windows it's just %path% %arguments%, on Unix it's mono %path% %arguments%.

This should absolutely be handled by some API, you shouldn't have to pass strings around like this.

  1. Switching in a different HttpClientHandler on Unix to work around a Mono bug.

Admittedly an edge case and not recommended behavior, no?

@nexussays

This comment has been minimized.

Show comment
Hide comment
@nexussays

nexussays Jun 2, 2015

Member

@Priya91 I know this was a quick example you just tossed together, so don't take this personally, but that is the sort of code that everyone should be moving away from. One's code should not be littered with platform checks. It shouldn't even be lightly dusted with platform checks. You would want some IDesktopWallpaper API which each platform implements and which you call from your application code -- application code that is agnostic to the platform on which it is running.

Member

nexussays commented Jun 2, 2015

@Priya91 I know this was a quick example you just tossed together, so don't take this personally, but that is the sort of code that everyone should be moving away from. One's code should not be littered with platform checks. It shouldn't even be lightly dusted with platform checks. You would want some IDesktopWallpaper API which each platform implements and which you call from your application code -- application code that is agnostic to the platform on which it is running.

@mellinoe

This comment has been minimized.

Show comment
Hide comment
@mellinoe

mellinoe Jun 3, 2015

Contributor

Coming into the discussion a bit late, but it seems like the main use case we are aiming to enable (and correct me if I'm wrong) is letting a library developer create implementations for multiple platforms without multiple compilations, and therefore without multiple release binaries. To me, that seems to be the one "thing" this library is aiming to solve. Apologies if this seems obvious, I thought I'd mention it anyways to try to get some clarity/consensus on that point. So to me, the use case is:

"Creating a cross-platform library which uses platform-specific logic, without shipping platform-specific binaries"

or even simpler:

"Use platform-specific logic within a platform-agnostic assembly"

A few more thoughts that pop to my mind:

  • As a library dev, shipping a single portable assembly that works everywhere is MUCH easier than shipping one-per-platform, when you think of build complexity, packaging, distribution, support, etc.
    • If you only have a single instance where you need to account for platform differences in a giant codebase, it is easier to swallow the downsides of this "runtime-detection" approach than to bite the bullet and build/ship multiple binaries.
    • If the "Wallpaper" example above was a part of a giant, platform-agnostic imaging library (bear with me...), it might be palpable for the dev to just say "screw it" and throw in a runtime platform check, rather than ship multiple binaries for that single difference.
  • The single "fat-assembly" will have wasted space, dead code, etc. when running on any single platform. There is also an obvious runtime cost to using this approach and is conceptually very "messy" when compared with conditional compilation. The overhead of this obviously depends on how much you actually make use of these runtime checks.
  • Our framework assemblies are going to be platform-specific; i.e. we will not be using this feature in the core libraries. So for someone releasing an app with a bundled runtime/framework, they will already need platform-specific distributions and the platform-portability of a single dependency is unimportant to them. It could actually be a downside due to the second bullet point above.

@nexussays brings up a very good point regarding the potential usages of the library. When you are pivoting around file paths or file names, you are really trying to make a decision based on the machine's file system behavior, not the name of the operating system. By pivoting on the operating system name, you are relying on your prior knowledge of OS and filesystem differences. From an API purity and usage standpoint, that is not ideal. It would be awesome if the libraries could abstract these important differences in a more meaningful and user-friendly way, but that's obviously not an easy problem in all cases.

Contributor

mellinoe commented Jun 3, 2015

Coming into the discussion a bit late, but it seems like the main use case we are aiming to enable (and correct me if I'm wrong) is letting a library developer create implementations for multiple platforms without multiple compilations, and therefore without multiple release binaries. To me, that seems to be the one "thing" this library is aiming to solve. Apologies if this seems obvious, I thought I'd mention it anyways to try to get some clarity/consensus on that point. So to me, the use case is:

"Creating a cross-platform library which uses platform-specific logic, without shipping platform-specific binaries"

or even simpler:

"Use platform-specific logic within a platform-agnostic assembly"

A few more thoughts that pop to my mind:

  • As a library dev, shipping a single portable assembly that works everywhere is MUCH easier than shipping one-per-platform, when you think of build complexity, packaging, distribution, support, etc.
    • If you only have a single instance where you need to account for platform differences in a giant codebase, it is easier to swallow the downsides of this "runtime-detection" approach than to bite the bullet and build/ship multiple binaries.
    • If the "Wallpaper" example above was a part of a giant, platform-agnostic imaging library (bear with me...), it might be palpable for the dev to just say "screw it" and throw in a runtime platform check, rather than ship multiple binaries for that single difference.
  • The single "fat-assembly" will have wasted space, dead code, etc. when running on any single platform. There is also an obvious runtime cost to using this approach and is conceptually very "messy" when compared with conditional compilation. The overhead of this obviously depends on how much you actually make use of these runtime checks.
  • Our framework assemblies are going to be platform-specific; i.e. we will not be using this feature in the core libraries. So for someone releasing an app with a bundled runtime/framework, they will already need platform-specific distributions and the platform-portability of a single dependency is unimportant to them. It could actually be a downside due to the second bullet point above.

@nexussays brings up a very good point regarding the potential usages of the library. When you are pivoting around file paths or file names, you are really trying to make a decision based on the machine's file system behavior, not the name of the operating system. By pivoting on the operating system name, you are relying on your prior knowledge of OS and filesystem differences. From an API purity and usage standpoint, that is not ideal. It would be awesome if the libraries could abstract these important differences in a more meaningful and user-friendly way, but that's obviously not an easy problem in all cases.

@richlander

This comment has been minimized.

Show comment
Hide comment
@richlander

richlander Jun 3, 2015

Member

@Priya91 Your point is also my point. You and @nexussays say that most of the platform-specific operations would be covered by platform-agnostic facades, with platform-specific implementations, provided by .NET Core. That's definitely what we want.

So, your use case then is:

"My app relies on OS (or otherwise provided) functionality that is not provided by .NET Core and that differs by OS and that requires differentiated calls per OS."

Without seeing the actual P/Invokes, it was hard to see what the scenario was.

My question, then, is what the other scenarios are. I'm wondering if there are some cases where .NET Core APIs return results that are platform-specific that require differentiated handling.

Member

richlander commented Jun 3, 2015

@Priya91 Your point is also my point. You and @nexussays say that most of the platform-specific operations would be covered by platform-agnostic facades, with platform-specific implementations, provided by .NET Core. That's definitely what we want.

So, your use case then is:

"My app relies on OS (or otherwise provided) functionality that is not provided by .NET Core and that differs by OS and that requires differentiated calls per OS."

Without seeing the actual P/Invokes, it was hard to see what the scenario was.

My question, then, is what the other scenarios are. I'm wondering if there are some cases where .NET Core APIs return results that are platform-specific that require differentiated handling.

@nexussays

This comment has been minimized.

Show comment
Hide comment
@nexussays

nexussays Jun 3, 2015

Member

@mellinoe (I doubt anyone minds you jumping in late. I jumped in like a week ago and now I'm trying to hijack the thread :))

That seems like a very good explanation; working off of that, my pushback is on the "use platform-specific logic" part, in which I will continuously ask: what logic? and why?

We should consider any time you need to "break out" of .NET and use platform-specific logic to be a "hole" in CoreFX. Could be it's an edge case that no one has addressed, could be it's too expensive, maybe it's already an open issue. Point being we ("we" the community) should address each such case individually and figure out what .NET Core API can provide for this platform-specific functionality.

It would be awesome if the libraries could abstract these important differences in a more meaningful and user-friendly way, but that's obviously not an easy problem in all cases.

I agree, but I'm saying show me those cases; specifically, enumerate what those cases are and then we can address them individually. Once we come across some platform-specific functionality that stumps this question then we can say ok, so we can't integrate this use-case into .NET, what's the next level of abstraction up that we can implement.

We've basically started at the highest possible level of abstraction with some abstract notion of "Operating System" in which Linux is a singular entity. That's my objection.

Member

nexussays commented Jun 3, 2015

@mellinoe (I doubt anyone minds you jumping in late. I jumped in like a week ago and now I'm trying to hijack the thread :))

That seems like a very good explanation; working off of that, my pushback is on the "use platform-specific logic" part, in which I will continuously ask: what logic? and why?

We should consider any time you need to "break out" of .NET and use platform-specific logic to be a "hole" in CoreFX. Could be it's an edge case that no one has addressed, could be it's too expensive, maybe it's already an open issue. Point being we ("we" the community) should address each such case individually and figure out what .NET Core API can provide for this platform-specific functionality.

It would be awesome if the libraries could abstract these important differences in a more meaningful and user-friendly way, but that's obviously not an easy problem in all cases.

I agree, but I'm saying show me those cases; specifically, enumerate what those cases are and then we can address them individually. Once we come across some platform-specific functionality that stumps this question then we can say ok, so we can't integrate this use-case into .NET, what's the next level of abstraction up that we can implement.

We've basically started at the highest possible level of abstraction with some abstract notion of "Operating System" in which Linux is a singular entity. That's my objection.

@HellBrick

This comment has been minimized.

Show comment
Hide comment
@HellBrick

HellBrick Jun 3, 2015

You want something like Environment.SpecialFolders. Also is it a guarantee that all distros of Linux are the same?

Not exactly. The concept is similar, but the folders that we use are specific to our system and Environment.SpecialFolders wouldn't really help to calculate them. And they actually are distro-agnostic, so possible migration to another distro shouldn't be a problem for us. Think of these paths as of our own conventions where to put our custom stuff, and these conventions are different between Windows and Linux.

If this is a concern you should just do this universally to avoid the problem altogether. Clearly you need some FileSystem entity to check for capabilities of the particular environment you find yourself it. NTFS is case sensitive but Win32 isn't. There are Linux file systems that allow case-insensitivity. You probably don't care about that, would others?

I'm with you on this one; like I said earlier, something like FileSystem.IsCaseSensitive would be perfect here.

Seems like this should be a property of a cross-platform file interaction API. Also isn't this a Posix API? Does OSX care about the executable bit?

I don't know if this flag is important to OSX, since we only use Windows and Linux servers. But come to think of it, I've never tested what will happen if I try to append Executable attribute to a file on Windows. If it works fine, then the platform check isn't really needed here.

Available universally on any Linux distro? (if so, then yes, valid use-case for this feature!)

We deploy this dependency with our app, both Windows and Ubuntu versions, and then choose one in runtime to avoid managing 2 separate builds and deployments. I'm not really sure if it works on the other distros, but even if it doesn't, it's hard to imagine scenario where we would choose to use two different Linux distros simultaneously in our cluster. Worst-case scenario, we could be forced to replace the platform type check with a distro check here, but I really don't think it will ever come to this.

This should absolutely be handled by some API, you shouldn't have to pass strings around like this.

Sorry, I don't get your point here. We're just launching a child process, and on different platforms we use different executables that obviously have slightly different argument strings. Some better API to manage argument strings would obviously be of help here, but it has nothing to do with the platform-dependent choice we're discussing here.

Admittedly an edge case and not recommended behavior, no?

It sure it is an edge case, but this is still a good example of a completely unpredictable yet real problem the platform check could help with. In case of this specific bug checking the platform is way easier than analyzing the types loaded into the current app domain to detect we're running on Mono, since in our case we're running on Mono if and only if we're running on Linux.

HellBrick commented Jun 3, 2015

You want something like Environment.SpecialFolders. Also is it a guarantee that all distros of Linux are the same?

Not exactly. The concept is similar, but the folders that we use are specific to our system and Environment.SpecialFolders wouldn't really help to calculate them. And they actually are distro-agnostic, so possible migration to another distro shouldn't be a problem for us. Think of these paths as of our own conventions where to put our custom stuff, and these conventions are different between Windows and Linux.

If this is a concern you should just do this universally to avoid the problem altogether. Clearly you need some FileSystem entity to check for capabilities of the particular environment you find yourself it. NTFS is case sensitive but Win32 isn't. There are Linux file systems that allow case-insensitivity. You probably don't care about that, would others?

I'm with you on this one; like I said earlier, something like FileSystem.IsCaseSensitive would be perfect here.

Seems like this should be a property of a cross-platform file interaction API. Also isn't this a Posix API? Does OSX care about the executable bit?

I don't know if this flag is important to OSX, since we only use Windows and Linux servers. But come to think of it, I've never tested what will happen if I try to append Executable attribute to a file on Windows. If it works fine, then the platform check isn't really needed here.

Available universally on any Linux distro? (if so, then yes, valid use-case for this feature!)

We deploy this dependency with our app, both Windows and Ubuntu versions, and then choose one in runtime to avoid managing 2 separate builds and deployments. I'm not really sure if it works on the other distros, but even if it doesn't, it's hard to imagine scenario where we would choose to use two different Linux distros simultaneously in our cluster. Worst-case scenario, we could be forced to replace the platform type check with a distro check here, but I really don't think it will ever come to this.

This should absolutely be handled by some API, you shouldn't have to pass strings around like this.

Sorry, I don't get your point here. We're just launching a child process, and on different platforms we use different executables that obviously have slightly different argument strings. Some better API to manage argument strings would obviously be of help here, but it has nothing to do with the platform-dependent choice we're discussing here.

Admittedly an edge case and not recommended behavior, no?

It sure it is an edge case, but this is still a good example of a completely unpredictable yet real problem the platform check could help with. In case of this specific bug checking the platform is way easier than analyzing the types loaded into the current app domain to detect we're running on Mono, since in our case we're running on Mono if and only if we're running on Linux.

@gistofj

This comment has been minimized.

Show comment
Hide comment
@gistofj

gistofj Jun 3, 2015

A simple use-case for knowing what platform you're on (Linux, Mac, or Windows): scanning the file system to compare files present with a list of expected files present.

On Mac and/or Linux you're best off grabbing a bunch of strings (paths) then stating them as necessary. The same logic flow on Windows is a nightmare (performance is horrible). On Windows you want to use GetFirst/GetNext logic to traverse the tree caching data as you go.

The logic is different, it simply has to be or one platform will suffer performance penalties and have unhappy users. Can this be hidden behind some API facade? Sure - does it need to be? No. Will there be plenty of other topics like this? Yes.

Is this API sufficient? No. Is it a start in the right direction? I think so, but I'd like to see if flushed out more before we proceed.

gistofj commented Jun 3, 2015

A simple use-case for knowing what platform you're on (Linux, Mac, or Windows): scanning the file system to compare files present with a list of expected files present.

On Mac and/or Linux you're best off grabbing a bunch of strings (paths) then stating them as necessary. The same logic flow on Windows is a nightmare (performance is horrible). On Windows you want to use GetFirst/GetNext logic to traverse the tree caching data as you go.

The logic is different, it simply has to be or one platform will suffer performance penalties and have unhappy users. Can this be hidden behind some API facade? Sure - does it need to be? No. Will there be plenty of other topics like this? Yes.

Is this API sufficient? No. Is it a start in the right direction? I think so, but I'd like to see if flushed out more before we proceed.

@AbhishekTripathi

This comment has been minimized.

Show comment
Hide comment
@AbhishekTripathi

AbhishekTripathi Jun 9, 2015

@Priya91 I know this was a quick example you just tossed together, so don't take this personally, but that is the sort of code that everyone should be moving away from. One's code should not be littered with platform checks. It shouldn't even be lightly dusted with platform checks. You would want some IDesktopWallpaper API which each platform implements and which you call from your application code -- application code that is agnostic to the platform on which it is running.

@nexussays do you mind providing some pseudo code? I think your point is no different from what @Priya91 says. The actual logic to change the wallpaper is abstracted. It could be a native call or some IsA kind implementation. The jist is that you need to identify which of the descendants or the native API to call based on the current platform.

var wallpaperChanger = WallpaperChangerFactory( DetectOS() ) as IWallpaperChanger;
wallpaperChanger.ChangeWallpaper();

I am sorry if I didn't understand your point. May I please request you to elaborate a bit more?

AbhishekTripathi commented Jun 9, 2015

@Priya91 I know this was a quick example you just tossed together, so don't take this personally, but that is the sort of code that everyone should be moving away from. One's code should not be littered with platform checks. It shouldn't even be lightly dusted with platform checks. You would want some IDesktopWallpaper API which each platform implements and which you call from your application code -- application code that is agnostic to the platform on which it is running.

@nexussays do you mind providing some pseudo code? I think your point is no different from what @Priya91 says. The actual logic to change the wallpaper is abstracted. It could be a native call or some IsA kind implementation. The jist is that you need to identify which of the descendants or the native API to call based on the current platform.

var wallpaperChanger = WallpaperChangerFactory( DetectOS() ) as IWallpaperChanger;
wallpaperChanger.ChangeWallpaper();

I am sorry if I didn't understand your point. May I please request you to elaborate a bit more?

@gluck

This comment has been minimized.

Show comment
Hide comment
@gluck

gluck Jun 9, 2015

Having listened to the API review (thanks for hosting it !), I'm wondering if uname -s (aka sysname) would be a better fit for the reasons below (not mentioned during the talk):

  • even the review board is having a hard time deciding what is a platform and what's not (e.g. Raspberry Pi, Open/Free BSD), relying on "whatever uname -s gives you is what you get here" has a very low adoption barrier (and avoids re-writing new mapping tables such as the uname one )
    • that will also prevent the "will we accept this PR to add FooBar as an OSName value" that was mentioned and I think scared everyone
  • I believe this feature will be used mostly when porting to Unix systems
    • Windows has no use of it - yet - plus it already has APIs for accessing the Windows families
    • uname is the de-facto standard on Unix, openly accessible on NodeJS & Python, devs are used to that
    • dnx uses PInvoke to Uname to get platform information, and Mono.Posix exposes it, people need that
    • the chance that one can write a better API are small-ish

Also using uname, you may lack information, you may lack flexibility (won't be able to hack your own values in), you may lack enum niceties, but you can't make it wrong (on Unix).

I'm not 100% sure on this one, and I don't like the fact that it'd be an simple wrapper over the OS, but given the uncertainty/confusion around the topic during the review (flags/exclusive or not, versions or not, feature/capability or not), I can't help thinking than something else will fail.

gluck commented Jun 9, 2015

Having listened to the API review (thanks for hosting it !), I'm wondering if uname -s (aka sysname) would be a better fit for the reasons below (not mentioned during the talk):

  • even the review board is having a hard time deciding what is a platform and what's not (e.g. Raspberry Pi, Open/Free BSD), relying on "whatever uname -s gives you is what you get here" has a very low adoption barrier (and avoids re-writing new mapping tables such as the uname one )
    • that will also prevent the "will we accept this PR to add FooBar as an OSName value" that was mentioned and I think scared everyone
  • I believe this feature will be used mostly when porting to Unix systems
    • Windows has no use of it - yet - plus it already has APIs for accessing the Windows families
    • uname is the de-facto standard on Unix, openly accessible on NodeJS & Python, devs are used to that
    • dnx uses PInvoke to Uname to get platform information, and Mono.Posix exposes it, people need that
    • the chance that one can write a better API are small-ish

Also using uname, you may lack information, you may lack flexibility (won't be able to hack your own values in), you may lack enum niceties, but you can't make it wrong (on Unix).

I'm not 100% sure on this one, and I don't like the fact that it'd be an simple wrapper over the OS, but given the uncertainty/confusion around the topic during the review (flags/exclusive or not, versions or not, feature/capability or not), I can't help thinking than something else will fail.

@terrajobst

This comment has been minimized.

Show comment
Hide comment
@terrajobst

terrajobst Jun 10, 2015

Member

We've reviewed this proposal today. It has been approved with feedback.

Member

terrajobst commented Jun 10, 2015

We've reviewed this proposal today. It has been approved with feedback.

@yufeih

This comment has been minimized.

Show comment
Hide comment
@yufeih

yufeih Jun 12, 2015

Please also consider these use cases if corefx runs on Android/iOS:

  • Get the platform name/version of an app and report it back to the server for usage analysis
  • Get the platform name/version of an app to generate a crash report in case of exceptions

The API can only test if we are running on a given platform, but there is no way to retrieve the current platform.

yufeih commented Jun 12, 2015

Please also consider these use cases if corefx runs on Android/iOS:

  • Get the platform name/version of an app and report it back to the server for usage analysis
  • Get the platform name/version of an app to generate a crash report in case of exceptions

The API can only test if we are running on a given platform, but there is no way to retrieve the current platform.

@Priya91

This comment has been minimized.

Show comment
Hide comment
@Priya91

Priya91 Jun 15, 2015

Member

Closing this issue, as the scope of this issue was to get a query api into corefx to query for the underlying os platform.

Member

Priya91 commented Jun 15, 2015

Closing this issue, as the scope of this issue was to get a query api into corefx to query for the underlying os platform.

@Priya91 Priya91 closed this Jun 15, 2015

shrayasr added a commit to shrayasr/itko that referenced this issue Nov 25, 2015

Use the right HOME based on OS
- Adds a new dependency to
  System.Runtime.InteropServices.RuntimeInformation.

More info can be found
  - On this issue: dotnet/corefx#1017
  - On this PR: dotnet/corefx#1999

jp7677 added a commit to jp7677/NLog that referenced this issue May 29, 2016

Use RuntimeInformation.IsOSPlatform to determine the runtime platform…
…. Using OperationSystem is not sufficient, e.g. it returns just "fedora" on Fedora Linux. See also dotnet/corefx#1017.

304NotModified added a commit to NLog/NLog that referenced this issue May 31, 2016

Core clr platform detection (#1488)
* Use RuntimeInformation.IsOSPlatform to determine the runtime platform. Using OperationSystem is not sufficient, e.g. it returns just "fedora" on Fedora Linux. See also  dotnet/corefx#1017.

* Remove whitespaces.

@karelz karelz modified the milestone: 1.0.0-rtm Dec 3, 2016

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