Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/libraries/Common/src/Interop/OSX/Interop.Libraries.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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";

}
}
59 changes: 59 additions & 0 deletions src/libraries/Common/src/Interop/OSX/Interop.libobjc.cs
Original file line number Diff line number Diff line change
@@ -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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
</PropertyGroup>
<PropertyGroup>
<Nullable>enable</Nullable>
<IsOSXLike Condition="'$(TargetsOSX)' == 'true' or '$(TargetsiOS)' == 'true' or '$(TargetstvOS)' == 'true'">true</IsOSXLike>
</PropertyGroup>
<ItemGroup>
<Compile Include="$(MSBuildThisFileDirectory)Internal\IO\File.cs" />
Expand Down Expand Up @@ -1750,14 +1751,16 @@
<Compile Include="$(MSBuildThisFileDirectory)System\Diagnostics\Tracing\RuntimeEventSourceHelper.Unix.cs" Condition="'$(FeaturePerfTracing)' == 'true'" />
<Compile Include="$(MSBuildThisFileDirectory)System\Environment.NoRegistry.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Environment.Unix.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Environment.Unix.GetFolderPathCore.cs" Condition="'$(TargetsiOS)' != 'true' and '$(TargetstvOS)' != 'true'" />
<Compile Include="$(MSBuildThisFileDirectory)System\Environment.OSVersion.OSX.cs" Condition="'$(IsOSXLike)' == 'true'" />
<Compile Include="$(MSBuildThisFileDirectory)System\Environment.OSVersion.Unix.cs" Condition="'$(IsOSXLike)' != 'true'" />
<Compile Include="$(MSBuildThisFileDirectory)System\Environment.GetFolderPathCore.Unix.cs" Condition="'$(TargetsiOS)' != 'true' and '$(TargetstvOS)' != 'true'" />
<Compile Include="$(MSBuildThisFileDirectory)System\Globalization\CultureData.Unix.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Globalization\CultureInfo.Unix.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Globalization\HijriCalendar.Unix.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Guid.Unix.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\IO\DriveInfoInternal.Unix.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\IO\FileStream.OSX.cs" Condition="'$(TargetsOSX)' == 'true' or '$(TargetsiOS)' == 'true' or '$(TargetstvOS)' == 'true'" />
<Compile Include="$(MSBuildThisFileDirectory)System\IO\FileStream.Linux.cs" Condition="'$(TargetsOSX)' != 'true' and '$(TargetsiOS)' != 'true' and '$(TargetstvOS)' != 'true'" />
<Compile Include="$(MSBuildThisFileDirectory)System\IO\FileStream.Lock.OSX.cs" Condition="'$(IsOSXLike)' == 'true'" />
<Compile Include="$(MSBuildThisFileDirectory)System\IO\FileStream.Lock.Unix.cs" Condition="'$(IsOSXLike)' != 'true'" />
<Compile Include="$(MSBuildThisFileDirectory)System\IO\FileStream.Unix.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\IO\Path.Unix.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\IO\PathInternal.Unix.cs" />
Expand All @@ -1773,6 +1776,14 @@
<Compile Include="$(MSBuildThisFileDirectory)System\Threading\TimerQueue.Unix.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\TimeZoneInfo.Unix.cs" />
</ItemGroup>
<ItemGroup Condition="'$(IsOSXLike)' == 'true'">
<Compile Include="$(CommonPath)Interop\OSX\Interop.libobjc.cs">
<Link>Common\Interop\OSX\Interop.libobjc.cs</Link>
</Compile>
<Compile Include="$(CommonPath)Interop\OSX\Interop.Libraries.cs">
<Link>Common\Interop\OSX\Interop.Libraries.cs</Link>
</Compile>
</ItemGroup>
<ItemGroup Condition="'$(FeatureHardwareIntrinsics)' == 'true' and ('$(Platform)' == 'x64' or ('$(Platform)' == 'x86' and '$(TargetsUnix)' != 'true'))">
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\Intrinsics\X86\Aes.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\Intrinsics\X86\Avx.cs" />
Expand Down
Original file line number Diff line number Diff line change
@@ -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);
}
}
}
Original file line number Diff line number Diff line change
@@ -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;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand All @@ -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
Comment thread
eerhardt marked this conversation as resolved.
}

[Fact]
public void SystemPageSize_Valid()
{
Expand Down