diff --git a/src/coreclr/clr.featuredefines.props b/src/coreclr/clr.featuredefines.props
index b5979c8d239013..c5119c80424d23 100644
--- a/src/coreclr/clr.featuredefines.props
+++ b/src/coreclr/clr.featuredefines.props
@@ -66,9 +66,5 @@
$(DefineConstants);PROFILING_SUPPORTED
$(DefineConstants);FEATURE_PROFAPI_ATTACH_DETACH
-
- $(DefineConstants);TARGET_UNIX
- $(DefineConstants);TARGET_WINDOWS
- $(DefineConstants);TARGET_OSX
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 1715d433f8d0bc..f7bb250fe0d32e 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
@@ -15,6 +15,17 @@
true
$(MSBuildThisFileDirectory)ILLink\
+
+ $(DefineConstants);TARGET_UNIX
+ $(DefineConstants);TARGET_WINDOWS
+ $(DefineConstants);TARGET_OSX
+ $(DefineConstants);TARGET_IOS
+ $(DefineConstants);TARGET_TVOS
+ $(DefineConstants);TARGET_BROWSER
+ $(DefineConstants);TARGET_ANDROID
+ $(DefineConstants);TARGET_LINUX
+ $(DefineConstants);TARGET_FREEBSD
+
diff --git a/src/libraries/System.Private.CoreLib/src/System/OperatingSystem.cs b/src/libraries/System.Private.CoreLib/src/System/OperatingSystem.cs
index 718f67cc9096f5..6b3a5b5682f48b 100644
--- a/src/libraries/System.Private.CoreLib/src/System/OperatingSystem.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/OperatingSystem.cs
@@ -8,6 +8,10 @@ namespace System
{
public sealed class OperatingSystem : ISerializable, ICloneable
{
+#if TARGET_UNIX && !TARGET_OSX
+ private static readonly string s_osPlatformName = Interop.Sys.GetUnixName();
+#endif
+
private readonly Version _version;
private readonly PlatformID _platform;
private readonly string? _servicePack;
@@ -79,5 +83,193 @@ public string VersionString
return _versionString;
}
}
+
+ ///
+ /// Indicates whether the current application is running on the specified platform.
+ ///
+ /// Case-insensitive platform name. Examples: Browser, Linux, FreeBSD, Android, iOS, macOS, tvOS, watchOS, Windows.
+ public static bool IsOSPlatform(string platform)
+ {
+ if (platform == null)
+ {
+ throw new ArgumentNullException(nameof(platform));
+ }
+
+#if TARGET_BROWSER
+ return platform.Equals("BROWSER", StringComparison.OrdinalIgnoreCase);
+#elif TARGET_WINDOWS
+ return platform.Equals("WINDOWS", StringComparison.OrdinalIgnoreCase);
+#elif TARGET_OSX
+ return platform.Equals("OSX", StringComparison.OrdinalIgnoreCase) || platform.Equals("MACOS", StringComparison.OrdinalIgnoreCase);
+#elif TARGET_UNIX
+ return platform.Equals(s_osPlatformName, StringComparison.OrdinalIgnoreCase);
+#else
+#error Unknown OS
+#endif
+ }
+
+ ///
+ /// Check for the OS with a >= version comparison. Used to guard APIs that were added in the given OS release.
+ ///
+ /// Case-insensitive platform name. Examples: Browser, Linux, FreeBSD, Android, iOS, macOS, tvOS, watchOS, Windows.
+ /// Major OS version number.
+ /// Minor OS version number (optional).
+ /// Build OS version number (optional).
+ /// Revision OS version number (optional).
+ public static bool IsOSPlatformVersionAtLeast(string platform, int major, int minor = 0, int build = 0, int revision = 0)
+ => IsOSPlatform(platform) && IsOSVersionAtLeast(major, minor, build, revision);
+
+ ///
+ /// Indicates whether the current application is running as WASM in a Browser.
+ ///
+ public static bool IsBrowser() =>
+#if TARGET_BROWSER
+ true;
+#else
+ false;
+#endif
+
+ ///
+ /// Indicates whether the current application is running on Linux.
+ ///
+ public static bool IsLinux() =>
+#if TARGET_LINUX
+ true;
+#else
+ false;
+#endif
+
+ ///
+ /// Indicates whether the current application is running on FreeBSD.
+ ///
+ public static bool IsFreeBSD() =>
+#if TARGET_FREEBSD
+ true;
+#else
+ false;
+#endif
+
+ ///
+ /// Check for the FreeBSD version (returned by 'uname') with a >= version comparison. Used to guard APIs that were added in the given FreeBSD release.
+ ///
+ public static bool IsFreeBSDVersionAtLeast(int major, int minor = 0, int build = 0, int revision = 0)
+ => IsFreeBSD() && IsOSVersionAtLeast(major, minor, build, revision);
+
+ ///
+ /// Indicates whether the current application is running on Android.
+ ///
+ public static bool IsAndroid() =>
+#if TARGET_ANDROID
+ true;
+#else
+ false;
+#endif
+
+ ///
+ /// Check for the Android version (returned by 'uname') with a >= version comparison. Used to guard APIs that were added in the given Android release.
+ ///
+ public static bool IsAndroidVersionAtLeast(int major, int minor = 0, int build = 0, int revision = 0)
+ => IsAndroid() && IsOSVersionAtLeast(major, minor, build, revision);
+
+ ///
+ /// Indicates whether the current application is running on iOS.
+ ///
+ public static bool IsIOS() =>
+#if TARGET_IOS
+ true;
+#else
+ false;
+#endif
+
+ ///
+ /// Check for the iOS version (returned by 'libobjc.get_operatingSystemVersion') with a >= version comparison. Used to guard APIs that were added in the given iOS release.
+ ///
+ public static bool IsIOSVersionAtLeast(int major, int minor = 0, int build = 0)
+ => IsIOS() && IsOSVersionAtLeast(major, minor, build, 0);
+
+ ///
+ /// Indicates whether the current application is running on macOS.
+ ///
+ public static bool IsMacOS() =>
+#if TARGET_OSX
+ true;
+#else
+ false;
+#endif
+
+ ///
+ /// Check for the macOS version (returned by 'libobjc.get_operatingSystemVersion') with a >= version comparison. Used to guard APIs that were added in the given macOS release.
+ ///
+ public static bool IsMacOSVersionAtLeast(int major, int minor = 0, int build = 0)
+ => IsMacOS() && IsOSVersionAtLeast(major, minor, build, 0);
+
+ ///
+ /// Indicates whether the current application is running on tvOS.
+ ///
+ public static bool IsTvOS() =>
+#if TARGET_TVOS
+ true;
+#else
+ false;
+#endif
+
+ ///
+ /// Check for the tvOS version (returned by 'libobjc.get_operatingSystemVersion') with a >= version comparison. Used to guard APIs that were added in the given tvOS release.
+ ///
+ public static bool IsTvOSVersionAtLeast(int major, int minor = 0, int build = 0)
+ => IsTvOS() && IsOSVersionAtLeast(major, minor, build, 0);
+
+ ///
+ /// Indicates whether the current application is running on watchOS.
+ ///
+ public static bool IsWatchOS() =>
+#if TARGET_WATCHOS
+ true;
+#else
+ false;
+#endif
+
+ ///
+ /// Check for the watchOS version (returned by 'libobjc.get_operatingSystemVersion') with a >= version comparison. Used to guard APIs that were added in the given watchOS release.
+ ///
+ public static bool IsWatchOSVersionAtLeast(int major, int minor = 0, int build = 0)
+ => IsWatchOS() && IsOSVersionAtLeast(major, minor, build, 0);
+
+ ///
+ /// Indicates whether the current application is running on Windows.
+ ///
+ public static bool IsWindows() =>
+#if TARGET_WINDOWS
+ true;
+#else
+ false;
+#endif
+
+ ///
+ /// Check for the Windows version (returned by 'RtlGetVersion') with a >= version comparison. Used to guard APIs that were added in the given Windows release.
+ ///
+ public static bool IsWindowsVersionAtLeast(int major, int minor = 0, int build = 0, int revision = 0)
+ => IsWindows() && IsOSVersionAtLeast(major, minor, build, revision);
+
+ private static bool IsOSVersionAtLeast(int major, int minor, int build, int revision)
+ {
+ Version current = Environment.OSVersion.Version;
+
+ if (current.Major != major)
+ {
+ return current.Major > major;
+ }
+ if (current.Minor != minor)
+ {
+ return current.Minor > minor;
+ }
+ if (current.Build != build)
+ {
+ return current.Build > build;
+ }
+
+ return current.Revision >= revision
+ || (current.Revision == -1 && revision == 0); // it is unavailable on OSX and Environment.OSVersion.Version.Revision returns -1
+ }
}
}
diff --git a/src/libraries/System.Runtime.Extensions/tests/System/OperatingSystemTests.cs b/src/libraries/System.Runtime.Extensions/tests/System/OperatingSystemTests.cs
index b6e7005d62f3b3..c355baf8e2aeac 100644
--- a/src/libraries/System.Runtime.Extensions/tests/System/OperatingSystemTests.cs
+++ b/src/libraries/System.Runtime.Extensions/tests/System/OperatingSystemTests.cs
@@ -7,6 +7,19 @@ namespace System.Tests
{
public static class OperatingSystemTests
{
+ private static readonly string[] AllKnownPlatformNames = new[]
+ {
+ "Android",
+ "macOS",
+ "iOS",
+ "tvOS",
+ "watchOS",
+ "Windows",
+ "Linux",
+ "FreeBSD",
+ "Browser"
+ };
+
[Theory]
[InlineData(PlatformID.Other, "1.0.0.0")]
[InlineData(PlatformID.MacOSX, "1.2")]
@@ -47,5 +60,158 @@ public static void Clone()
Assert.Equal(os.Version, os2.Version);
Assert.Equal(os.VersionString, os2.VersionString);
}
+
+ [Fact]
+ public static void IsOSPlatform_InvalidArgs_Throws()
+ {
+ AssertExtensions.Throws("platform", () => OperatingSystem.IsOSPlatform(null));
+ }
+
+ [Fact]
+ public static void IsOSPlatformVersionAtLeast_InvalidArgs_Throws()
+ {
+ AssertExtensions.Throws("platform", () => OperatingSystem.IsOSPlatformVersionAtLeast(null, 1));
+ }
+
+ [Fact, PlatformSpecific(TestPlatforms.Browser)]
+ public static void TestIsOSPlatform_Browser() => TestIsOSPlatform("BROWSER", OperatingSystem.IsBrowser);
+
+ [Fact, PlatformSpecific(TestPlatforms.Browser)]
+ public static void TestIsOSVersionAtLeast_Browser() => TestIsOSVersionAtLeast("BROWSER");
+
+ [Fact, PlatformSpecific(TestPlatforms.Linux)]
+ public static void TestIsOSPlatform_Linux() => TestIsOSPlatform("Linux", OperatingSystem.IsLinux);
+
+ [Fact, PlatformSpecific(TestPlatforms.Linux)]
+ public static void TestIsOSVersionAtLeast_Linux() => TestIsOSVersionAtLeast("Linux");
+
+ [Fact, PlatformSpecific(TestPlatforms.FreeBSD)]
+ public static void TestIsOSPlatform_FreeBSD() => TestIsOSPlatform("FreeBSD", OperatingSystem.IsFreeBSD);
+
+ [Fact, PlatformSpecific(TestPlatforms.FreeBSD)]
+ public static void TestIsOSVersionAtLeast_FreeBSD() => TestIsOSVersionAtLeast("FreeBSD");
+
+ [Fact, PlatformSpecific(TestPlatforms.Android)]
+ public static void TestIsOSPlatform_Android() => TestIsOSPlatform("Android", OperatingSystem.IsAndroid);
+
+ [Fact, PlatformSpecific(TestPlatforms.Android)]
+ public static void TestIsOSVersionAtLeast_Android() => TestIsOSVersionAtLeast("Android");
+
+ [Fact, PlatformSpecific(TestPlatforms.iOS)]
+ public static void TestIsOSPlatform_IOS() => TestIsOSPlatform("iOS", OperatingSystem.IsIOS);
+
+ [Fact, PlatformSpecific(TestPlatforms.iOS)]
+ public static void TestIsOSVersionAtLeast_IOS() => TestIsOSVersionAtLeast("iOS");
+
+ [Fact, PlatformSpecific(TestPlatforms.OSX)]
+ public static void TestIsOSPlatform_MacOS() => TestIsOSPlatform("macOS", OperatingSystem.IsMacOS);
+
+ [Fact, PlatformSpecific(TestPlatforms.OSX)]
+ public static void TestIsOSVersionAtLeast_MacOS() => TestIsOSVersionAtLeast("macOS");
+
+ [Fact, PlatformSpecific(TestPlatforms.OSX)]
+ public static void OSX_Is_Treated_as_macOS()
+ {
+ // we prefer "macOS", but still accept "OSX"
+
+ Assert.True(OperatingSystem.IsOSPlatform("OSX"));
+
+ AssertVersionChecks(true, (major, minor, build, revision) => OperatingSystem.IsOSPlatformVersionAtLeast("OSX", major, minor, build, revision));
+ AssertVersionChecks(true, (major, minor, build, revision) => OperatingSystem.IsOSPlatformVersionAtLeast("osx", major, minor, build, revision));
+ AssertVersionChecks(true, (major, minor, build) => OperatingSystem.IsOSPlatformVersionAtLeast("OSX", major, minor, build));
+ AssertVersionChecks(true, (major, minor, build) => OperatingSystem.IsOSPlatformVersionAtLeast("osx", major, minor, build));
+ }
+
+ [Fact, PlatformSpecific(TestPlatforms.tvOS)]
+ public static void TestIsOSPlatform_TvOS() => TestIsOSPlatform("tvOS", OperatingSystem.IsTvOS);
+
+ [Fact, PlatformSpecific(TestPlatforms.tvOS)]
+ public static void TestIsOSVersionAtLeast_TvOS() => TestIsOSVersionAtLeast("tvOS");
+
+ [Fact, PlatformSpecific(TestPlatforms.Windows)]
+ public static void TestIsOSPlatform_Windows() => TestIsOSPlatform("Windows", OperatingSystem.IsWindows);
+
+ [Fact, PlatformSpecific(TestPlatforms.Windows)]
+ public static void TestIsOSVersionAtLeast_Windows() => TestIsOSVersionAtLeast("Windows");
+
+ private static void TestIsOSPlatform(string currentOSName, Func currentOSCheck)
+ {
+ foreach (string platfromName in AllKnownPlatformNames)
+ {
+ bool expected = currentOSName.Equals(platfromName, StringComparison.OrdinalIgnoreCase);
+
+ Assert.Equal(expected, OperatingSystem.IsOSPlatform(platfromName));
+ Assert.Equal(expected, OperatingSystem.IsOSPlatform(platfromName.ToUpper()));
+ Assert.Equal(expected, OperatingSystem.IsOSPlatform(platfromName.ToLower()));
+ }
+
+ Assert.True(currentOSCheck());
+
+ bool[] allResults = new bool[]
+ {
+ OperatingSystem.IsBrowser(),
+ OperatingSystem.IsLinux(),
+ OperatingSystem.IsFreeBSD(),
+ OperatingSystem.IsAndroid(),
+ OperatingSystem.IsIOS(),
+ OperatingSystem.IsMacOS(),
+ OperatingSystem.IsTvOS(),
+ OperatingSystem.IsWatchOS(),
+ OperatingSystem.IsWindows()
+ };
+
+ Assert.Single(allResults, true);
+ }
+
+ private static void TestIsOSVersionAtLeast(string currentOSName)
+ {
+ foreach (string platfromName in AllKnownPlatformNames)
+ {
+ bool isCurrentOS = currentOSName.Equals(platfromName, StringComparison.OrdinalIgnoreCase);
+
+ AssertVersionChecks(isCurrentOS, (major, minor, build, revision) => OperatingSystem.IsOSPlatformVersionAtLeast(platfromName, major, minor, build, revision));
+ AssertVersionChecks(isCurrentOS, (major, minor, build, revision) => OperatingSystem.IsOSPlatformVersionAtLeast(platfromName.ToLower(), major, minor, build, revision));
+ AssertVersionChecks(isCurrentOS, (major, minor, build, revision) => OperatingSystem.IsOSPlatformVersionAtLeast(platfromName.ToUpper(), major, minor, build, revision));
+ }
+
+ AssertVersionChecks(currentOSName.Equals("Android", StringComparison.OrdinalIgnoreCase), OperatingSystem.IsAndroidVersionAtLeast);
+ AssertVersionChecks(currentOSName.Equals("iOS", StringComparison.OrdinalIgnoreCase), OperatingSystem.IsIOSVersionAtLeast);
+ AssertVersionChecks(currentOSName.Equals("macOS", StringComparison.OrdinalIgnoreCase), OperatingSystem.IsMacOSVersionAtLeast);
+ AssertVersionChecks(currentOSName.Equals("tvOS", StringComparison.OrdinalIgnoreCase), OperatingSystem.IsTvOSVersionAtLeast);
+ AssertVersionChecks(currentOSName.Equals("watchOS", StringComparison.OrdinalIgnoreCase), OperatingSystem.IsWatchOSVersionAtLeast);
+ AssertVersionChecks(currentOSName.Equals("Windows", StringComparison.OrdinalIgnoreCase), OperatingSystem.IsWindowsVersionAtLeast);
+ }
+
+ private static void AssertVersionChecks(bool isCurrentOS, Func isOSVersionAtLeast)
+ {
+ Version current = Environment.OSVersion.Version;
+
+ Assert.False(isOSVersionAtLeast(current.Major + 1, current.Minor, current.Build, current.Revision));
+ Assert.False(isOSVersionAtLeast(current.Major, current.Minor + 1, current.Build, current.Revision));
+ Assert.False(isOSVersionAtLeast(current.Major, current.Minor, current.Build + 1, current.Revision));
+ Assert.False(isOSVersionAtLeast(current.Major, current.Minor, current.Build, Math.Max(current.Revision + 1, 1))); // OSX Revision reports -1
+
+ Assert.Equal(isCurrentOS, isOSVersionAtLeast(current.Major, current.Minor, current.Build, current.Revision));
+
+ Assert.Equal(isCurrentOS, isOSVersionAtLeast(current.Major - 1, current.Minor, current.Build, current.Revision));
+ Assert.Equal(isCurrentOS, isOSVersionAtLeast(current.Major, current.Minor - 1, current.Build, current.Revision));
+ Assert.Equal(isCurrentOS, isOSVersionAtLeast(current.Major, current.Minor, current.Build - 1, current.Revision));
+ Assert.Equal(isCurrentOS, isOSVersionAtLeast(current.Major, current.Minor, current.Build, current.Revision - 1));
+ }
+
+ private static void AssertVersionChecks(bool isCurrentOS, Func isOSVersionAtLeast)
+ {
+ Version current = Environment.OSVersion.Version;
+
+ Assert.False(isOSVersionAtLeast(current.Major + 1, current.Minor, current.Build));
+ Assert.False(isOSVersionAtLeast(current.Major, current.Minor + 1, current.Build));
+ Assert.False(isOSVersionAtLeast(current.Major, current.Minor, current.Build + 1));
+
+ Assert.Equal(isCurrentOS, isOSVersionAtLeast(current.Major, current.Minor, current.Build));
+
+ Assert.Equal(isCurrentOS, isOSVersionAtLeast(current.Major - 1, current.Minor, current.Build));
+ Assert.Equal(isCurrentOS, isOSVersionAtLeast(current.Major, current.Minor - 1, current.Build));
+ Assert.Equal(isCurrentOS, isOSVersionAtLeast(current.Major, current.Minor, current.Build - 1));
+ }
}
}
diff --git a/src/libraries/System.Runtime/ref/System.Runtime.cs b/src/libraries/System.Runtime/ref/System.Runtime.cs
index da360cbad7cde7..aaaf4da863d939 100644
--- a/src/libraries/System.Runtime/ref/System.Runtime.cs
+++ b/src/libraries/System.Runtime/ref/System.Runtime.cs
@@ -3044,6 +3044,24 @@ public OperatingSystem(System.PlatformID platform, System.Version version) { }
public object Clone() { throw null; }
public void GetObjectData(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { }
public override string ToString() { throw null; }
+ public static bool IsOSPlatform(string platform) { throw null; }
+ public static bool IsOSPlatformVersionAtLeast(string platform, int major, int minor = 0, int build = 0, int revision = 0) { throw null; }
+ public static bool IsBrowser() { throw null; }
+ public static bool IsLinux() { throw null; }
+ public static bool IsFreeBSD() { throw null; }
+ public static bool IsFreeBSDVersionAtLeast(int major, int minor = 0, int build = 0, int revision = 0) { throw null; }
+ public static bool IsAndroid() { throw null; }
+ public static bool IsAndroidVersionAtLeast(int major, int minor = 0, int build = 0, int revision = 0) { throw null; }
+ public static bool IsIOS() { throw null; }
+ public static bool IsIOSVersionAtLeast(int major, int minor = 0, int build = 0) { throw null; }
+ public static bool IsMacOS() { throw null; }
+ public static bool IsMacOSVersionAtLeast(int major, int minor = 0, int build = 0) { throw null; }
+ public static bool IsTvOS() { throw null; }
+ public static bool IsTvOSVersionAtLeast(int major, int minor = 0, int build = 0) { throw null; }
+ public static bool IsWatchOS() { throw null; }
+ public static bool IsWatchOSVersionAtLeast(int major, int minor = 0, int build = 0) { throw null; }
+ public static bool IsWindows() { throw null; }
+ public static bool IsWindowsVersionAtLeast(int major, int minor = 0, int build = 0, int revision = 0) { throw null; }
}
public partial class OperationCanceledException : System.SystemException
{
diff --git a/src/mono/netcore/System.Private.CoreLib/System.Private.CoreLib.csproj b/src/mono/netcore/System.Private.CoreLib/System.Private.CoreLib.csproj
index 95e1e72845ad16..167457a62ba2cf 100644
--- a/src/mono/netcore/System.Private.CoreLib/System.Private.CoreLib.csproj
+++ b/src/mono/netcore/System.Private.CoreLib/System.Private.CoreLib.csproj
@@ -132,12 +132,6 @@
$(DefineConstants);FEATURE_MANAGED_ETW_CHANNELS
$(DefineConstants);FEATURE_PERFTRACING
$(DefineConstants);FEATURE_DEFAULT_INTERFACES
- $(DefineConstants);TARGET_UNIX
- $(DefineConstants);TARGET_WINDOWS
- $(DefineConstants);TARGET_OSX
- $(DefineConstants);TARGET_IOS
- $(DefineConstants);TARGET_TVOS
- $(DefineConstants);TARGET_BROWSER