diff --git a/JetBrains.HabitatDetector/src/HabitatInfo.cs b/JetBrains.HabitatDetector/src/HabitatInfo.cs index c0a5ca1..bbf158c 100644 --- a/JetBrains.HabitatDetector/src/HabitatInfo.cs +++ b/JetBrains.HabitatDetector/src/HabitatInfo.cs @@ -6,15 +6,21 @@ namespace JetBrains.HabitatDetector { [SuppressMessage("ReSharper", "MemberCanBePrivate.Global")] + [SuppressMessage("ReSharper", "InconsistentNaming")] public static partial class HabitatInfo { - public static JetClrImplementation ClrImplementation => Helper.ClrImplementation; - public static Version? MonoVersion => Helper.MonoVersion; public static JetPlatform Platform => Helper.Platform; public static JetArchitecture OSArchitecture => Helper.OSArchitecture; public static JetArchitecture ProcessArchitecture => Helper.ProcessArchitecture; - public static JetLinuxLibC? LinuxLibC => Helper.LinuxLibC; public static string OSName => Helper.OSName; + + public static JetClrImplementation ClrImplementation => Helper.ClrImplementation; + public static Version? MonoVersion => Helper.MonoVersion; + + public static JetLinuxLibC? LinuxLibC => Helper.LinuxLibC; + + public static Version? MacOSVersion => Helper.MacOSVersion; + public static uint? WindowsBuildNumber => Helper.WindowsBuildNumber; public static JetWindowsInstallationType? WindowsInstallationType => Helper.WindowsInstallationType; public static bool? WindowsIsUserAdministrator => Helper.WindowsIsUserAdministrator; diff --git a/JetBrains.HabitatDetector/src/Impl/Helper.cs b/JetBrains.HabitatDetector/src/Impl/Helper.cs index 506161f..a5eaa58 100644 --- a/JetBrains.HabitatDetector/src/Impl/Helper.cs +++ b/JetBrains.HabitatDetector/src/Impl/Helper.cs @@ -1,5 +1,6 @@ using System; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Reflection; using JetBrains.HabitatDetector.Impl.Linux; using JetBrains.HabitatDetector.Impl.MacOsX; @@ -8,15 +9,21 @@ namespace JetBrains.HabitatDetector.Impl { + [SuppressMessage("ReSharper", "InconsistentNaming")] internal static class Helper { - internal static readonly JetClrImplementation ClrImplementation; - internal static readonly Version? MonoVersion; internal static readonly JetPlatform Platform; internal static readonly JetArchitecture OSArchitecture; internal static readonly JetArchitecture ProcessArchitecture; - internal static readonly JetLinuxLibC? LinuxLibC; internal static readonly string OSName; + + internal static readonly JetClrImplementation ClrImplementation; + internal static readonly Version? MonoVersion; + + internal static readonly JetLinuxLibC? LinuxLibC; + + internal static readonly Version? MacOSVersion; + internal static readonly uint? WindowsBuildNumber; internal static readonly JetWindowsInstallationType? WindowsInstallationType; internal static readonly bool? WindowsIsUserAdministrator; @@ -72,7 +79,8 @@ static Helper() case JetPlatform.MacOsX: ProcessArchitecture = unameArchitecture; OSArchitecture = MacOsHelper.GetRunningUnderRosetta2() ? JetArchitecture.Arm64 : unameArchitecture; // Note(ww898): Process under Rosetta2 works only on ARM64 host! - OSName = MacOsHelper.GetOSName(unameArchitecture); + MacOSVersion = NormalizeVersion(MacOsHelper.GetOSVersion(unameArchitecture)); + OSName = MacOsHelper.GetOSName(MacOSVersion); break; default: throw new PlatformNotSupportedException($"Unsupported platform {Platform}"); } @@ -89,17 +97,22 @@ static Helper() var n = displayNameStr.IndexOf(' '); var versionStr = n >= 0 ? displayNameStr.Substring(0, n) : displayNameStr; + Version? monoVersion; #if NET20 || NET30 || NET35 try { - MonoVersion = new Version(versionStr); + monoVersion = new Version(versionStr); } catch { + monoVersion = null; } #else - Version.TryParse(versionStr, out MonoVersion); + Version.TryParse(versionStr, out monoVersion); #endif + + if (monoVersion != null) + MonoVersion = NormalizeVersion(monoVersion); } #endif @@ -109,6 +122,12 @@ static Helper() ClrImplementation = isNetCore ? JetClrImplementation.NetCore : JetClrImplementation.NetFramework; } + internal static Version NormalizeVersion(Version version) => version.Build > 0 + ? version.Revision == -1 + ? version + : new(version.Major, version.Minor, version.Build) + : new(version.Major, version.Minor); + internal static unsafe JetArchitecture GetProcessArchitecture(int processId) => Platform switch { JetPlatform.FreeBSD or JetPlatform.Linux => OSArchitecture, diff --git a/JetBrains.HabitatDetector/src/Impl/MacOsX/MacOsHelper.cs b/JetBrains.HabitatDetector/src/Impl/MacOsX/MacOsHelper.cs index 0cece4f..a4def89 100644 --- a/JetBrains.HabitatDetector/src/Impl/MacOsX/MacOsHelper.cs +++ b/JetBrains.HabitatDetector/src/Impl/MacOsX/MacOsHelper.cs @@ -45,7 +45,7 @@ internal static class MacOsHelper return new string(Encoding.UTF8.GetChars(buf)); } - private static unsafe Version GetOSVersion(JetArchitecture processArchitecture) + internal static unsafe Version GetOSVersion(JetArchitecture processArchitecture) { var processInfo = LibObjC.objc_msgSend(LibObjC.objc_getClass("NSProcessInfo"), LibObjC.sel_getUid("processInfo")); var operationSystemVersionUid = LibObjC.sel_getUid("operatingSystemVersion"); @@ -73,14 +73,17 @@ private static unsafe Version GetOSVersion(JetArchitecture processArchitecture) } // Note(ww898): Compatibility with older software! See also SYSTEM_VERSION_COMPAT=1. Big Sur and newer OSes always returns 10.16!!! - return major == 10 && minor == 16 - ? new Version(11, 0, patch) - : new Version(major, minor, patch); + if (major == 10 && minor == 16) + { + major = 11; + minor = 0; + } + + return new Version(major, minor, patch); } - internal static string GetOSName(JetArchitecture processArchitecture) + internal static string GetOSName(Version version) { - var version = GetOSVersion(processArchitecture); var build = GetSysctlKernOsVersion(); var builder = new StringBuilder(version.Major switch @@ -114,7 +117,7 @@ internal static string GetOSName(JetArchitecture processArchitecture) }); builder.Append(' ').Append(version.Major).Append('.').Append(version.Minor); - if (version.Build != 0) + if (version.Build > 0) builder.Append('.').Append(version.Build); if (build != null) builder.Append(' ').Append(build); diff --git a/JetBrains.HabitatDetector/src/Impl/Unix/UnixHelper.cs b/JetBrains.HabitatDetector/src/Impl/Unix/UnixHelper.cs index 1deb4fe..fe74c71 100644 --- a/JetBrains.HabitatDetector/src/Impl/Unix/UnixHelper.cs +++ b/JetBrains.HabitatDetector/src/Impl/Unix/UnixHelper.cs @@ -1,9 +1,11 @@ using System; +using System.Diagnostics.CodeAnalysis; using System.Runtime.InteropServices; using System.Text; namespace JetBrains.HabitatDetector.Impl.Unix { + [SuppressMessage("ReSharper", "InconsistentNaming")] internal static class UnixHelper { internal record struct UnameInfo(JetPlatform Platform, string Sysname, string Release, JetArchitecture Architecture); diff --git a/JetBrains.HabitatDetector/src/Impl/Windows/WindowsHelper.cs b/JetBrains.HabitatDetector/src/Impl/Windows/WindowsHelper.cs index dba36b7..9273bf3 100644 --- a/JetBrains.HabitatDetector/src/Impl/Windows/WindowsHelper.cs +++ b/JetBrains.HabitatDetector/src/Impl/Windows/WindowsHelper.cs @@ -1,10 +1,12 @@ using System; using System.ComponentModel; +using System.Diagnostics.CodeAnalysis; using System.Text; using System.Text.RegularExpressions; namespace JetBrains.HabitatDetector.Impl.Windows { + [SuppressMessage("ReSharper", "InconsistentNaming")] internal static class WindowsHelper { internal static unsafe JetArchitecture GetProcessArchitecture() diff --git a/JetBrains.HabitatDetector/src/JetBrains.HabitatDetector.csproj b/JetBrains.HabitatDetector/src/JetBrains.HabitatDetector.csproj index a2b3f5c..9313330 100644 --- a/JetBrains.HabitatDetector/src/JetBrains.HabitatDetector.csproj +++ b/JetBrains.HabitatDetector/src/JetBrains.HabitatDetector.csproj @@ -27,9 +27,9 @@ Supported frameworks: • Mono 5.10 and later https://github.com/JetBrains/habitat-detector icon.png - jetbrains habitat detect windows linux macos freebsd netstandard netframework + jetbrains windows linux macos freebsd netstandard netframework Apache-2.0 - 1.2.1 + 1.3.0 diff --git a/JetBrains.HabitatDetector/tests/HabitatInfoTest.cs b/JetBrains.HabitatDetector/tests/HabitatInfoTest.cs index 85fcba0..8a2fe4d 100644 --- a/JetBrains.HabitatDetector/tests/HabitatInfoTest.cs +++ b/JetBrains.HabitatDetector/tests/HabitatInfoTest.cs @@ -180,11 +180,23 @@ public void ArchitecturePresentableTest(JetArchitecture architecture, string exp [Test] public void CurrentTest() { + Console.WriteLine("{0}: {1}", nameof(Environment) + "." + nameof(Environment.OSVersion), Environment.OSVersion); + Console.WriteLine("{0}: {1}", nameof(Environment) + "." + nameof(Environment.OSVersion) + "." + nameof(Environment.OSVersion.Platform), Environment.OSVersion.Platform); + Console.WriteLine("{0}: {1}", nameof(Environment) + "." + nameof(Environment.OSVersion) + "." + nameof(Environment.OSVersion.Version), Environment.OSVersion.Version); + Console.WriteLine("{0}: {1}", nameof(Environment) + "." + nameof(Environment.OSVersion) + "." + nameof(Environment.OSVersion.ServicePack), Environment.OSVersion.ServicePack); Console.WriteLine("{0}: {1}", nameof(HabitatInfo.OSName), HabitatInfo.OSName); Console.WriteLine("{0}: {1}", nameof(HabitatInfo.ClrImplementation), HabitatInfo.ClrImplementation); if (HabitatInfo.ClrImplementation == JetClrImplementation.Mono) + { Console.WriteLine("{0}: {1}", nameof(HabitatInfo.MonoVersion), HabitatInfo.MonoVersion?.ToString() ?? ""); + + if (HabitatInfo.MonoVersion != null) + { + Assert.AreNotEqual(0, HabitatInfo.MonoVersion.Build); + Assert.AreEqual(-1, HabitatInfo.MonoVersion.Revision); + } + } else Assert.IsNull(HabitatInfo.MonoVersion); @@ -197,6 +209,8 @@ public void CurrentTest() if (HabitatInfo.Platform == JetPlatform.Windows) { + Assert.AreEqual(PlatformID.Win32NT, Environment.OSVersion.Platform); + Console.WriteLine("{0}: {1}", nameof(HabitatInfo.WindowsBuildNumber), HabitatInfo.WindowsBuildNumber?.ToString() ?? ""); Console.WriteLine("{0}: {1}", nameof(HabitatInfo.WindowsInstallationType), HabitatInfo.WindowsInstallationType?.ToString() ?? ""); Console.WriteLine("{0}: {1}", nameof(HabitatInfo.WindowsIsUserAdministrator), HabitatInfo.WindowsIsUserAdministrator?.ToString() ?? ""); @@ -207,15 +221,36 @@ public void CurrentTest() Assert.IsNotNull(HabitatInfo.WindowsIsUserAdministrator); Assert.IsNotNull(HabitatInfo.WindowsIsElevated); Assert.IsNotNull(HabitatInfo.WindowsElevationType); + + Assert.AreEqual(Environment.OSVersion.Version.Build, checked((int)HabitatInfo.WindowsBuildNumber!)); } else { + Assert.AreNotEqual(PlatformID.Win32NT, Environment.OSVersion.Platform); + Assert.IsNull(HabitatInfo.WindowsInstallationType); Assert.IsNull(HabitatInfo.WindowsIsUserAdministrator); Assert.IsNull(HabitatInfo.WindowsIsElevated); Assert.IsNull(HabitatInfo.WindowsElevationType); } + if (HabitatInfo.Platform == JetPlatform.MacOsX) + { + Console.WriteLine("{0}: {1}", nameof(HabitatInfo.MacOSVersion), HabitatInfo.MacOSVersion?.ToString() ?? ""); + + if (HabitatInfo.MacOSVersion != null) + { + Assert.AreNotEqual(0, HabitatInfo.MacOSVersion.Build); + Assert.AreEqual(-1, HabitatInfo.MacOSVersion.Revision); + } + else + Assert.Fail(); + } + else + { + Assert.IsNull(HabitatInfo.MacOSVersion); + } + Console.WriteLine("{0}: {1}", nameof(HabitatInfo.ProcessRuntimeIdString), HabitatInfo.ProcessRuntimeIdString); Console.WriteLine("{0}: {1}", nameof(HabitatInfo.OSRuntimeIdString), HabitatInfo.OSRuntimeIdString); diff --git a/JetBrains.HabitatDetector/tests/UnitHelperTest.cs b/JetBrains.HabitatDetector/tests/UnitHelperTest.cs index 409498f..b5fe294 100644 --- a/JetBrains.HabitatDetector/tests/UnitHelperTest.cs +++ b/JetBrains.HabitatDetector/tests/UnitHelperTest.cs @@ -1,4 +1,6 @@ -using JetBrains.HabitatDetector.Impl.Unix; +using System; +using JetBrains.HabitatDetector.Impl; +using JetBrains.HabitatDetector.Impl.Unix; using NUnit.Framework; namespace JetBrains.HabitatDetector.Tests @@ -30,5 +32,15 @@ public void PlatformTest(string sysname, JetPlatform expectedPlatform, int expec [TestCase(JetPlatform.MacOsX, "x86_64", JetArchitecture.X64)] [Test] public void ConvertToArchitecture(JetPlatform platform, string machine, JetArchitecture expectedArchitecture) => Assert.AreEqual(expectedArchitecture, UnixHelper.ConvertToArchitecture(platform, machine)); + + [Test] + public void NormalizeTest() + { + Assert.AreEqual(new Version(11, 222), Helper.NormalizeVersion(new Version(11, 222))); + Assert.AreEqual(new Version(11, 222), Helper.NormalizeVersion(new Version(11, 222, 0))); + Assert.AreEqual(new Version(11, 222, 3333), Helper.NormalizeVersion(new Version(11, 222, 3333))); + Assert.AreEqual(new Version(11, 222, 3333), Helper.NormalizeVersion(new Version(11, 222, 3333, 0))); + Assert.AreEqual(new Version(11, 222, 3333), Helper.NormalizeVersion(new Version(11, 222, 3333, 44444))); + } } } \ No newline at end of file