diff --git a/src/libraries/Common/src/Interop/OSX/Interop.Libraries.cs b/src/libraries/Common/src/Interop/OSX/Interop.Libraries.cs
index 84bf6d6398da8..6c2a0ab78c8d7 100644
--- a/src/libraries/Common/src/Interop/OSX/Interop.Libraries.cs
+++ b/src/libraries/Common/src/Interop/OSX/Interop.Libraries.cs
@@ -9,13 +9,13 @@ internal static partial class Libraries
internal const string CoreFoundationLibrary = "/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation";
internal const string CoreServicesLibrary = "/System/Library/Frameworks/CoreServices.framework/CoreServices";
internal const string CFNetworkLibrary = "/System/Library/Frameworks/CFNetwork.framework/CFNetwork";
- internal const string libproc = "libproc";
+ internal const string libobjc = "/usr/lib/libobjc.dylib";
+ internal const string libproc = "/usr/lib/libproc.dylib";
internal const string LibSystemCommonCrypto = "/usr/lib/system/libcommonCrypto";
internal const string LibSystemKernel = "/usr/lib/system/libsystem_kernel";
internal const string Odbc32 = "libodbc.2.dylib";
internal const string SystemConfigurationLibrary = "/System/Library/Frameworks/SystemConfiguration.framework/SystemConfiguration";
internal const string AppleCryptoNative = "System.Security.Cryptography.Native.Apple";
internal const string MsQuic = "msquic";
-
}
}
diff --git a/src/libraries/Common/src/Interop/OSX/Interop.libobjc.cs b/src/libraries/Common/src/Interop/OSX/Interop.libobjc.cs
new file mode 100644
index 0000000000000..6658d2b4c5d40
--- /dev/null
+++ b/src/libraries/Common/src/Interop/OSX/Interop.libobjc.cs
@@ -0,0 +1,59 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#nullable enable
+
+using System;
+using System.Runtime.InteropServices;
+using nint = System.IntPtr;
+
+internal static partial class Interop
+{
+ internal static partial class libobjc
+ {
+#if TARGET_ARM64
+ private const string MessageSendStructReturnEntryPoint = "objc_msgSend";
+#else
+ private const string MessageSendStructReturnEntryPoint = "objc_msgSend_stret";
+#endif
+
+ [StructLayout(LayoutKind.Sequential)]
+ private struct NSOperatingSystemVersion
+ {
+ public nint majorVersion;
+ public nint minorVersion;
+ public nint patchVersion;
+ }
+
+ [DllImport(Libraries.libobjc)]
+ private static extern IntPtr objc_getClass(string className);
+ [DllImport(Libraries.libobjc)]
+ private static extern IntPtr sel_getUid(string selector);
+ [DllImport(Libraries.libobjc)]
+ private static extern IntPtr objc_msgSend(IntPtr basePtr, IntPtr selector);
+
+ internal static Version GetOperatingSystemVersion()
+ {
+ int major = 0;
+ int minor = 0;
+ int patch = 0;
+
+ IntPtr processInfo = objc_msgSend(objc_getClass("NSProcessInfo"), sel_getUid("processInfo"));
+
+ if (processInfo != IntPtr.Zero)
+ {
+ NSOperatingSystemVersion osVersion = get_operatingSystemVersion(processInfo, sel_getUid("operatingSystemVersion"));
+
+ major = osVersion.majorVersion.ToInt32();
+ minor = osVersion.minorVersion.ToInt32();
+ patch = osVersion.patchVersion.ToInt32();
+ }
+
+ return new Version(major, minor, patch);
+ }
+
+ [DllImport(Libraries.libobjc, EntryPoint = MessageSendStructReturnEntryPoint)]
+ private static extern NSOperatingSystemVersion get_operatingSystemVersion(IntPtr basePtr, IntPtr selector);
+ }
+}
diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems
index 951552c11ba49..23fd990bfb42c 100644
--- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems
+++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems
@@ -8,6 +8,7 @@
enable
+ true
@@ -1750,14 +1751,16 @@
-
+
+
+
-
-
+
+
@@ -1773,6 +1776,14 @@
+
+
+ Common\Interop\OSX\Interop.libobjc.cs
+
+
+ Common\Interop\OSX\Interop.Libraries.cs
+
+
diff --git a/src/libraries/System.Private.CoreLib/src/System/Environment.Unix.GetFolderPathCore.cs b/src/libraries/System.Private.CoreLib/src/System/Environment.GetFolderPathCore.Unix.cs
similarity index 100%
rename from src/libraries/System.Private.CoreLib/src/System/Environment.Unix.GetFolderPathCore.cs
rename to src/libraries/System.Private.CoreLib/src/System/Environment.GetFolderPathCore.Unix.cs
diff --git a/src/libraries/System.Private.CoreLib/src/System/Environment.OSVersion.OSX.cs b/src/libraries/System.Private.CoreLib/src/System/Environment.OSVersion.OSX.cs
new file mode 100644
index 0000000000000..0c8771938f041
--- /dev/null
+++ b/src/libraries/System.Private.CoreLib/src/System/Environment.OSVersion.OSX.cs
@@ -0,0 +1,18 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System
+{
+ public static partial class Environment
+ {
+ private static OperatingSystem GetOSVersion()
+ {
+ Version version = Interop.libobjc.GetOperatingSystemVersion();
+
+ // For compatibility reasons with Mono, PlatformID.Unix is returned on MacOSX. PlatformID.MacOSX
+ // is hidden from the editor and shouldn't be used.
+ return new OperatingSystem(PlatformID.Unix, version);
+ }
+ }
+}
diff --git a/src/libraries/System.Private.CoreLib/src/System/Environment.OSVersion.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/Environment.OSVersion.Unix.cs
new file mode 100644
index 0000000000000..0ccea27611aed
--- /dev/null
+++ b/src/libraries/System.Private.CoreLib/src/System/Environment.OSVersion.Unix.cs
@@ -0,0 +1,67 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System
+{
+ public static partial class Environment
+ {
+ private static OperatingSystem GetOSVersion() => GetOperatingSystem(Interop.Sys.GetUnixRelease());
+
+ // Tests exercise this method for corner cases via private reflection
+ private static OperatingSystem GetOperatingSystem(string release)
+ {
+ int major = 0, minor = 0, build = 0, revision = 0;
+
+ // Parse the uname's utsname.release for the first four numbers found.
+ // This isn't perfect, but Version already doesn't map exactly to all possible release
+ // formats, e.g. 2.6.19-1.2895.fc6
+ if (release != null)
+ {
+ int i = 0;
+ major = FindAndParseNextNumber(release, ref i);
+ minor = FindAndParseNextNumber(release, ref i);
+ build = FindAndParseNextNumber(release, ref i);
+ revision = FindAndParseNextNumber(release, ref i);
+ }
+
+ return new OperatingSystem(PlatformID.Unix, new Version(major, minor, build, revision));
+ }
+
+ private static int FindAndParseNextNumber(string text, ref int pos)
+ {
+ // Move to the beginning of the number
+ for (; pos < text.Length; pos++)
+ {
+ char c = text[pos];
+ if ('0' <= c && c <= '9')
+ {
+ break;
+ }
+ }
+
+ // Parse the number;
+ int num = 0;
+ for (; pos < text.Length; pos++)
+ {
+ char c = text[pos];
+ if ('0' > c || c > '9')
+ break;
+
+ try
+ {
+ num = checked((num * 10) + (c - '0'));
+ }
+ // Integer overflow can occur for example with:
+ // Linux nelknet 4.15.0-24201807041620-generic
+ // To form a valid Version, num must be positive.
+ catch (OverflowException)
+ {
+ return int.MaxValue;
+ }
+ }
+
+ return num;
+ }
+ }
+}
diff --git a/src/libraries/System.Private.CoreLib/src/System/Environment.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/Environment.Unix.cs
index c29a56c14b53f..81b516dd45f5d 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Environment.Unix.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Environment.Unix.cs
@@ -63,66 +63,6 @@ public static string MachineName
internal const string NewLineConst = "\n";
- private static OperatingSystem GetOSVersion() => GetOperatingSystem(Interop.Sys.GetUnixRelease());
-
- // Tests exercise this method for corner cases via private reflection
- private static OperatingSystem GetOperatingSystem(string release)
- {
- int major = 0, minor = 0, build = 0, revision = 0;
-
- // Parse the uname's utsname.release for the first four numbers found.
- // This isn't perfect, but Version already doesn't map exactly to all possible release
- // formats, e.g. 2.6.19-1.2895.fc6
- if (release != null)
- {
- int i = 0;
- major = FindAndParseNextNumber(release, ref i);
- minor = FindAndParseNextNumber(release, ref i);
- build = FindAndParseNextNumber(release, ref i);
- revision = FindAndParseNextNumber(release, ref i);
- }
-
- // For compatibility reasons with Mono, PlatformID.Unix is returned on MacOSX. PlatformID.MacOSX
- // is hidden from the editor and shouldn't be used.
- return new OperatingSystem(PlatformID.Unix, new Version(major, minor, build, revision));
- }
-
- private static int FindAndParseNextNumber(string text, ref int pos)
- {
- // Move to the beginning of the number
- for (; pos < text.Length; pos++)
- {
- char c = text[pos];
- if ('0' <= c && c <= '9')
- {
- break;
- }
- }
-
- // Parse the number;
- int num = 0;
- for (; pos < text.Length; pos++)
- {
- char c = text[pos];
- if ('0' > c || c > '9')
- break;
-
- try
- {
- num = checked((num * 10) + (c - '0'));
- }
- // Integer overflow can occur for example with:
- // Linux nelknet 4.15.0-24201807041620-generic
- // To form a valid Version, num must be positive.
- catch (OverflowException)
- {
- return int.MaxValue;
- }
- }
-
- return num;
- }
-
public static string SystemDirectory => GetFolderPathCore(SpecialFolder.System, SpecialFolderOption.None);
public static int SystemPageSize => CheckedSysConf(Interop.Sys.SysConfName._SC_PAGESIZE);
diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.OSX.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.Lock.OSX.cs
similarity index 100%
rename from src/libraries/System.Private.CoreLib/src/System/IO/FileStream.OSX.cs
rename to src/libraries/System.Private.CoreLib/src/System/IO/FileStream.Lock.OSX.cs
diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.Linux.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.Lock.Unix.cs
similarity index 100%
rename from src/libraries/System.Private.CoreLib/src/System/IO/FileStream.Linux.cs
rename to src/libraries/System.Private.CoreLib/src/System/IO/FileStream.Lock.Unix.cs
diff --git a/src/libraries/System.Runtime.Extensions/tests/System/EnvironmentTests.cs b/src/libraries/System.Runtime.Extensions/tests/System/EnvironmentTests.cs
index 3ea51bf15b4cf..5129caf6dc8e8 100644
--- a/src/libraries/System.Runtime.Extensions/tests/System/EnvironmentTests.cs
+++ b/src/libraries/System.Runtime.Extensions/tests/System/EnvironmentTests.cs
@@ -132,9 +132,9 @@ public void OSVersion_ValidVersion()
Assert.Contains(RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "Windows" : "Unix", versionString);
}
- // On Unix, we must parse the version from uname -r
+ // On non-OSX Unix, we must parse the version from uname -r
[Theory]
- [PlatformSpecific(TestPlatforms.AnyUnix)]
+ [PlatformSpecific(TestPlatforms.AnyUnix & ~TestPlatforms.OSX)]
[InlineData("2.6.19-1.2895.fc6", 2, 6, 19, 1)]
[InlineData("xxx1yyy2zzz3aaa4bbb", 1, 2, 3, 4)]
[InlineData("2147483647.2147483647.2147483647.2147483647", int.MaxValue, int.MaxValue, int.MaxValue, int.MaxValue)]
@@ -154,6 +154,19 @@ public void OSVersion_ParseVersion(string input, int major, int minor, int build
Assert.Equal(expected, actual);
}
+ [Fact]
+ [PlatformSpecific(TestPlatforms.OSX)]
+ public void OSVersion_ValidVersion_OSX()
+ {
+ Version version = Environment.OSVersion.Version;
+
+ // verify that the Environment.OSVersion.Version matches the current RID
+ Assert.Contains(version.ToString(2), RuntimeInformation.RuntimeIdentifier);
+
+ Assert.True(version.Build >= 0, "OSVersion Build should be non-negative");
+ Assert.Equal(-1, version.Revision); // Revision is never set on OSX
+ }
+
[Fact]
public void SystemPageSize_Valid()
{