From: Adam Sitnik Date: Fri, 7 Aug 2020 07:16:19 +0000 (+0200) Subject: Implement platform guards in OperatingSystem class (#40457) X-Git-Tag: submit/tizen/20210909.063632~6165 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=c073e574a6d144dfe5499bc2aa96122581250325;p=platform%2Fupstream%2Fdotnet%2Fruntime.git Implement platform guards in OperatingSystem class (#40457) * add Is$OsName methods to OperatingSystem class * add OperatingSystem.*VersionAtLeast methods * add IsOSPlatform and IsOSPlatformVersionAtLeast * add xml comments * address code review suggestions * move TARGET_* defines to a common file --- diff --git a/src/coreclr/clr.featuredefines.props b/src/coreclr/clr.featuredefines.props index b5979c8..c5119c8 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 4278e25..27bccfc 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 718f67c..6b3a5b5 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 @@ namespace System 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 b6e7005..c355baf 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 @@ namespace System.Tests 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 da360cb..aaaf4da 100644 --- a/src/libraries/System.Runtime/ref/System.Runtime.cs +++ b/src/libraries/System.Runtime/ref/System.Runtime.cs @@ -3044,6 +3044,24 @@ namespace System 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 e2435e5..af48a96 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