|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.ComponentModel;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.Serialization;
using System.Runtime.Versioning;
namespace System
{
public sealed class OperatingSystem : ISerializable, ICloneable
{
private const string OSPlatformName =
#if TARGET_BROWSER
"BROWSER"
#elif TARGET_WASI
"WASI"
#elif TARGET_WINDOWS
"WINDOWS"
#elif TARGET_OSX
"OSX"
#elif TARGET_MACCATALYST
"MACCATALYST"
#elif TARGET_IOS
"IOS"
#elif TARGET_TVOS
"TVOS"
#elif TARGET_ANDROID
"ANDROID"
#elif TARGET_LINUX
"LINUX"
#elif TARGET_FREEBSD
"FREEBSD"
#elif TARGET_NETBSD
"NETBSD"
#elif TARGET_ILLUMOS
"ILLUMOS"
#elif TARGET_SOLARIS
"SOLARIS"
#else
#error Unknown OS, add a corresponding TARGET_* constant to System.Private.CoreLib.Shared.projitems
#endif
;
private readonly Version _version;
private readonly PlatformID _platform;
private readonly string? _servicePack;
private string? _versionString;
public OperatingSystem(PlatformID platform, Version version) : this(platform, version, null)
{
}
internal OperatingSystem(PlatformID platform, Version version, string? servicePack)
{
if (platform < PlatformID.Win32S || platform > PlatformID.Other)
{
throw new ArgumentOutOfRangeException(nameof(platform), platform, SR.Format(SR.Arg_EnumIllegalVal, platform));
}
ArgumentNullException.ThrowIfNull(version);
_platform = platform;
_version = version;
_servicePack = servicePack;
}
[Obsolete(Obsoletions.LegacyFormatterImplMessage, DiagnosticId = Obsoletions.LegacyFormatterImplDiagId, UrlFormat = Obsoletions.SharedUrlFormat)]
[EditorBrowsable(EditorBrowsableState.Never)]
public void GetObjectData(SerializationInfo info, StreamingContext context)
{
throw new PlatformNotSupportedException();
}
public PlatformID Platform => _platform;
public string ServicePack => _servicePack ?? string.Empty;
public Version Version => _version;
public object Clone() => new OperatingSystem(_platform, _version, _servicePack);
public override string ToString() => VersionString;
public string VersionString
{
get
{
if (_versionString == null)
{
string os;
switch (_platform)
{
case PlatformID.Win32S: os = "Microsoft Win32S "; break;
case PlatformID.Win32Windows: os = (_version.Major > 4 || (_version.Major == 4 && _version.Minor > 0)) ? "Microsoft Windows 98 " : "Microsoft Windows 95 "; break;
case PlatformID.Win32NT: os = "Microsoft Windows NT "; break;
case PlatformID.WinCE: os = "Microsoft Windows CE "; break;
case PlatformID.Unix: os = "Unix "; break;
case PlatformID.Xbox: os = "Xbox "; break;
case PlatformID.MacOSX: os = "Mac OS X "; break;
case PlatformID.Other: os = "Other "; break;
default:
Debug.Fail($"Unknown platform {_platform}");
os = "<unknown> "; break;
}
Span<char> stackBuffer = stackalloc char[128];
_versionString = string.IsNullOrEmpty(_servicePack) ?
string.Create(null, stackBuffer, $"{os}{_version}") :
string.Create(null, stackBuffer, $"{os}{_version.ToString(3)} {_servicePack}");
}
return _versionString;
}
}
/// <summary>
/// Indicates whether the current application is running on the specified platform.
/// </summary>
/// <param name="platform">Case-insensitive platform name. Examples: Browser, Linux, FreeBSD, Android, iOS, macOS, tvOS, watchOS, Windows.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool IsOSPlatform(string platform)
{
ArgumentNullException.ThrowIfNull(platform);
return platform.Equals(OSPlatformName, StringComparison.OrdinalIgnoreCase)
#if TARGET_OSX
|| platform.Equals("MACOS", StringComparison.OrdinalIgnoreCase)
#elif TARGET_MACCATALYST
|| platform.Equals("IOS", StringComparison.OrdinalIgnoreCase)
#endif
;
}
/// <summary>
/// Check for the OS with a >= version comparison. Used to guard APIs that were added in the given OS release.
/// </summary>
/// <param name="platform">Case-insensitive platform name. Examples: Browser, Linux, FreeBSD, Android, iOS, macOS, tvOS, watchOS, Windows.</param>
/// <param name="major">Major OS version number.</param>
/// <param name="minor">Minor OS version number (optional).</param>
/// <param name="build">Build OS version number (optional).</param>
/// <param name="revision">Revision OS version number (optional).</param>
public static bool IsOSPlatformVersionAtLeast(string platform, int major, int minor = 0, int build = 0, int revision = 0)
=> IsOSPlatform(platform) && IsOSVersionAtLeast(major, minor, build, revision);
/// <summary>
/// Indicates whether the current application is running as WASM in a Browser.
/// </summary>
[NonVersionable]
public static bool IsBrowser() =>
#if TARGET_BROWSER
true;
#else
false;
#endif
/// <summary>
/// Indicates whether the current application is running as WASI.
/// </summary>
[NonVersionable]
public static bool IsWasi() =>
#if TARGET_WASI
true;
#else
false;
#endif
/// <summary>
/// Indicates whether the current application is running on Linux.
/// </summary>
[NonVersionable]
public static bool IsLinux() =>
#if TARGET_LINUX && !TARGET_ANDROID
true;
#else
false;
#endif
/// <summary>
/// Indicates whether the current application is running on FreeBSD.
/// </summary>
[NonVersionable]
public static bool IsFreeBSD() =>
#if TARGET_FREEBSD
true;
#else
false;
#endif
/// <summary>
/// Check for the FreeBSD version (returned by 'uname') with a >= version comparison. Used to guard APIs that were added in the given FreeBSD release.
/// </summary>
public static bool IsFreeBSDVersionAtLeast(int major, int minor = 0, int build = 0, int revision = 0)
=> IsFreeBSD() && IsOSVersionAtLeast(major, minor, build, revision);
/// <summary>
/// Indicates whether the current application is running on Android.
/// </summary>
[NonVersionable]
public static bool IsAndroid() =>
#if TARGET_ANDROID
true;
#else
false;
#endif
/// <summary>
/// Check for the Android API level (returned by 'ro.build.version.sdk') with a >= version comparison. Used to guard APIs that were added in the given Android release.
/// </summary>
public static bool IsAndroidVersionAtLeast(int major, int minor = 0, int build = 0, int revision = 0)
=> IsAndroid() && IsOSVersionAtLeast(major, minor, build, revision);
/// <summary>
/// Indicates whether the current application is running on iOS or MacCatalyst.
/// </summary>
[SupportedOSPlatformGuard("maccatalyst")]
[NonVersionable]
public static bool IsIOS() =>
#if TARGET_IOS || TARGET_MACCATALYST
true;
#else
false;
#endif
/// <summary>
/// Check for the iOS/MacCatalyst version (returned by 'libobjc.get_operatingSystemVersion') with a >= version comparison. Used to guard APIs that were added in the given iOS release.
/// </summary>
[SupportedOSPlatformGuard("maccatalyst")]
[NonVersionable]
public static bool IsIOSVersionAtLeast(int major, int minor = 0, int build = 0)
=> IsIOS() && IsOSVersionAtLeast(major, minor, build, 0);
/// <summary>
/// Indicates whether the current application is running on macOS.
/// </summary>
[NonVersionable]
public static bool IsMacOS() =>
#if TARGET_OSX
true;
#else
false;
#endif
internal static bool IsApplePlatform() =>
#if TARGET_OSX || TARGET_MACCATALYST || TARGET_IOS || TARGET_TVOS
true;
#else
false;
#endif
/// <summary>
/// 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.
/// </summary>
public static bool IsMacOSVersionAtLeast(int major, int minor = 0, int build = 0)
=> IsMacOS() && IsOSVersionAtLeast(major, minor, build, 0);
/// <summary>
/// Indicates whether the current application is running on Mac Catalyst.
/// </summary>
[NonVersionable]
public static bool IsMacCatalyst() =>
#if TARGET_MACCATALYST
true;
#else
false;
#endif
/// <summary>
/// Check for the Mac Catalyst version (iOS version as presented in Apple documentation) with a >= version comparison. Used to guard APIs that were added in the given Mac Catalyst release.
/// </summary>
public static bool IsMacCatalystVersionAtLeast(int major, int minor = 0, int build = 0)
=> IsMacCatalyst() && IsOSVersionAtLeast(major, minor, build, 0);
/// <summary>
/// Indicates whether the current application is running on tvOS.
/// </summary>
[NonVersionable]
public static bool IsTvOS() =>
#if TARGET_TVOS
true;
#else
false;
#endif
/// <summary>
/// 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.
/// </summary>
public static bool IsTvOSVersionAtLeast(int major, int minor = 0, int build = 0)
=> IsTvOS() && IsOSVersionAtLeast(major, minor, build, 0);
/// <summary>
/// Indicates whether the current application is running on watchOS.
/// </summary>
[NonVersionable]
public static bool IsWatchOS() =>
#if TARGET_WATCHOS
true;
#else
false;
#endif
/// <summary>
/// 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.
/// </summary>
public static bool IsWatchOSVersionAtLeast(int major, int minor = 0, int build = 0)
=> IsWatchOS() && IsOSVersionAtLeast(major, minor, build, 0);
/// <summary>
/// Indicates whether the current application is running on Windows.
/// </summary>
[NonVersionable]
public static bool IsWindows() =>
#if TARGET_WINDOWS
true;
#else
false;
#endif
/// <summary>
/// Check for the Windows version (returned by 'RtlGetVersion') with a >= version comparison. Used to guard APIs that were added in the given Windows release.
/// </summary>
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;
}
// Unspecified build component is to be treated as zero
int currentBuild = current.Build < 0 ? 0 : current.Build;
build = build < 0 ? 0 : build;
if (currentBuild != build)
{
return currentBuild > build;
}
// Unspecified revision component is to be treated as zero
int currentRevision = current.Revision < 0 ? 0 : current.Revision;
revision = revision < 0 ? 0 : revision;
return currentRevision >= revision;
}
}
}
|