Add RuntimeInformation.RuntimeIdentifier (#34206)
authorEric Erhardt <eric.erhardt@microsoft.com>
Sat, 28 Mar 2020 05:47:13 +0000 (00:47 -0500)
committerGitHub <noreply@github.com>
Sat, 28 Mar 2020 05:47:13 +0000 (22:47 -0700)
* Add RuntimeInformation.RuntimeIdentifier

This value returns the Runtime Identifier (RID) of the current machine.

Contributes to #26780

* Include XML doc comments for the new property.

* Consistently check for AppContext strings.

12 files changed:
src/coreclr/src/System.Private.CoreLib/src/System/StartupHookProvider.cs
src/installer/corehost/cli/hostpolicy/coreclr.cpp
src/installer/corehost/cli/hostpolicy/coreclr.h
src/installer/corehost/cli/hostpolicy/hostpolicy_context.cpp
src/libraries/System.Private.CoreLib/src/System/AppContext.cs
src/libraries/System.Private.CoreLib/src/System/Environment.cs
src/libraries/System.Private.CoreLib/src/System/Resources/ResourceManager.Uap.cs
src/libraries/System.Runtime.InteropServices.RuntimeInformation/ref/System.Runtime.InteropServices.RuntimeInformation.cs
src/libraries/System.Runtime.InteropServices.RuntimeInformation/src/System/Runtime/InteropServices/RuntimeInformation/RuntimeInformation.cs
src/libraries/System.Runtime.InteropServices.RuntimeInformation/tests/DescriptionNameTests.cs
src/libraries/System.Runtime.InteropServices.RuntimeInformation/tests/RuntimeIdentifierTests.cs [new file with mode: 0644]
src/libraries/System.Runtime.InteropServices.RuntimeInformation/tests/System.Runtime.InteropServices.RuntimeInformation.Tests.csproj

index cfe7daca7ad939abbffef7bbddd5eeb5e3566ba7..39bbc1ab9a1914dd2c58039974bfbd895ca748e6 100644 (file)
@@ -28,7 +28,7 @@ namespace System
             // Initialize tracing before any user code can be called.
             System.Diagnostics.Tracing.RuntimeEventSource.Initialize();
 
-            string? startupHooksVariable = (string?)AppContext.GetData("STARTUP_HOOKS");
+            string? startupHooksVariable = AppContext.GetData("STARTUP_HOOKS") as string;
             if (startupHooksVariable == null)
             {
                 return;
index 234353aa4c47e7e70d87e7f6937af9bfa89feb86..b329a72d85886aab5cc6179490d733fbb8c5071a 100644 (file)
@@ -203,7 +203,8 @@ namespace
         _X("JIT_PATH"),
         _X("STARTUP_HOOKS"),
         _X("APP_PATHS"),
-        _X("APP_NI_PATHS")
+        _X("APP_NI_PATHS"),
+        _X("RUNTIME_IDENTIFIER")
     };
 
     static_assert((sizeof(PropertyNameMapping) / sizeof(*PropertyNameMapping)) == static_cast<size_t>(common_property::Last), "Invalid property count");
index d2ccd5da5510d56961d8cc5dba8d4d03207d1036..a06e81fe2e346f9ebd8c2a998cf62523e81dda08 100644 (file)
@@ -67,6 +67,7 @@ enum class common_property
     StartUpHooks,
     AppPaths,
     AppNIPaths,
+    RuntimeIdentifier,
 
     // Sentinel value - new values should be defined above
     Last
index 17bfe5d3e99aae91fd5ab81bc8c644e6fad8f397..bb4a965126d962d4852511821273b177b125a4a3 100644 (file)
@@ -145,6 +145,7 @@ int hostpolicy_context_t::initialize(hostpolicy_init_t &hostpolicy_init, const a
     coreclr_properties.add(common_property::FxDepsFile, fx_deps_str.c_str());
     coreclr_properties.add(common_property::ProbingDirectories, resolver.get_lookup_probe_directories().c_str());
     coreclr_properties.add(common_property::FxProductVersion, clr_library_version.c_str());
+    coreclr_properties.add(common_property::RuntimeIdentifier, get_current_runtime_id(true /*use_fallback*/).c_str());
 
     if (!clrjit_path.empty())
         coreclr_properties.add(common_property::JitPath, clrjit_path.c_str());
index c79e8ee9f96f0a9dd0b3ff8e0877a688a61a63f8..907356696416225de16bdb04693c478a06b67119 100644 (file)
@@ -22,7 +22,7 @@ namespace System
         public static string BaseDirectory =>
             // The value of APP_CONTEXT_BASE_DIRECTORY key has to be a string and it is not allowed to be any other type.
             // Otherwise the caller will get invalid cast exception
-            (string?)GetData("APP_CONTEXT_BASE_DIRECTORY") ??
+            GetData("APP_CONTEXT_BASE_DIRECTORY") as string ??
             (s_defaultBaseDirectory ??= GetBaseDirectoryCore());
 
         public static string? TargetFrameworkName =>
index 62571701d28c3ca14c7848ba6fc8e161ffa8a7c1..a71c68131c5403be508e015a6c4d53c7b1e828e3 100644 (file)
@@ -144,7 +144,7 @@ namespace System
             {
                 // FX_PRODUCT_VERSION is expected to be set by the host
                 // Use AssemblyInformationalVersionAttribute as fallback if the exact product version is not specified by the host
-                string? versionString = (string?)AppContext.GetData("FX_PRODUCT_VERSION") ??
+                string? versionString = AppContext.GetData("FX_PRODUCT_VERSION") as string ??
                     typeof(object).Assembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>()?.InformationalVersion;
 
                 ReadOnlySpan<char> versionSpan = versionString.AsSpan();
index 2fa9e0dc6e1340861cbb6f7748030163832bcfce..2a8d7373208105d9bd139bf82c3806be45cb9f20 100644 (file)
@@ -88,7 +88,7 @@ namespace System.Resources
                 return false;
 
             // Check to see if the assembly is under PLATFORM_RESOURCE_ROOTS. If it is, then we should use satellite assembly lookup for it.
-            string? platformResourceRoots = (string?)AppContext.GetData("PLATFORM_RESOURCE_ROOTS");
+            string? platformResourceRoots = AppContext.GetData("PLATFORM_RESOURCE_ROOTS") as string;
             if (!string.IsNullOrEmpty(platformResourceRoots))
             {
                 string resourceAssemblyPath = resourcesAssembly.Location;
index 412075fad881de17bf481f178c08918bf619a526..f0357797e8adec305d8d192a4d56e143e06f49ef 100644 (file)
@@ -32,6 +32,7 @@ namespace System.Runtime.InteropServices
     }
     public static partial class RuntimeInformation
     {
+        public static string RuntimeIdentifier { get { throw null; } }
         public static string FrameworkDescription { get { throw null; } }
         public static System.Runtime.InteropServices.Architecture OSArchitecture { get { throw null; } }
         public static string OSDescription { get { throw null; } }
index 8d61597264a2a396f6684e31dd018a59798a1f14..8e50ce0d1d91cb68949cf0ac58a7981b3fe8ff6b 100644 (file)
@@ -10,6 +10,7 @@ namespace System.Runtime.InteropServices
     {
         private const string FrameworkName = ".NET Core";
         private static string? s_frameworkDescription;
+        private static string? s_runtimeIdentifier;
 
         public static string FrameworkDescription
         {
@@ -17,7 +18,7 @@ namespace System.Runtime.InteropServices
             {
                 if (s_frameworkDescription == null)
                 {
-                    string? versionString = (string?)AppContext.GetData("FX_PRODUCT_VERSION");
+                    string? versionString = AppContext.GetData("FX_PRODUCT_VERSION") as string;
 
                     if (versionString == null)
                     {
@@ -41,5 +42,18 @@ namespace System.Runtime.InteropServices
                 return s_frameworkDescription;
             }
         }
+
+        /// <summary>
+        /// Returns an opaque string that identifies the platform on which an app is running.
+        /// </summary>
+        /// <remarks>
+        /// The property returns a string that identifies the operating system, typically including version,
+        /// and processor architecture of the currently executing process.
+        /// Since this string is opaque, it is not recommended to parse the string into its constituent parts.
+        ///
+        /// For more information, see https://docs.microsoft.com/dotnet/core/rid-catalog.
+        /// </remarks>
+        public static string RuntimeIdentifier =>
+            s_runtimeIdentifier ??= AppContext.GetData("RUNTIME_IDENTIFIER") as string ?? "unknown";
     }
 }
index 73f692898dbcfd28e46fa1c962eb6820cf6c7062..379bff8436dfde89381a60eb12c12188d7ef7b52 100644 (file)
@@ -22,7 +22,8 @@ namespace System.Runtime.InteropServices.RuntimeInformationTests
             string osd = RuntimeInformation.OSDescription.Trim();
             string osv = Environment.OSVersion.ToString();
             string osa = RuntimeInformation.OSArchitecture.ToString();
-            Console.WriteLine($"### OS: Distro={dvs} Description={osd} Version={osv} Arch={osa}");
+            string rid = RuntimeInformation.RuntimeIdentifier;
+            Console.WriteLine($"### OS: Distro={dvs} Description={osd} Version={osv} Arch={osa} Rid={rid}");
 
             string lcr = PlatformDetection.LibcRelease;
             string lcv = PlatformDetection.LibcVersion;
diff --git a/src/libraries/System.Runtime.InteropServices.RuntimeInformation/tests/RuntimeIdentifierTests.cs b/src/libraries/System.Runtime.InteropServices.RuntimeInformation/tests/RuntimeIdentifierTests.cs
new file mode 100644 (file)
index 0000000..ac7a0b7
--- /dev/null
@@ -0,0 +1,98 @@
+// 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.
+
+using System.IO;
+using System.Linq;
+using Microsoft.DotNet.RemoteExecutor;
+using Xunit;
+
+namespace System.Runtime.InteropServices.RuntimeInformationTests
+{
+    public class RuntimeIdentifierTests
+    {
+        [Fact]
+        [ActiveIssue("https://github.com/dotnet/runtime/issues/26780")] // need a new testhost
+        public void VerifyOSRid()
+        {
+            Assert.NotNull(RuntimeInformation.RuntimeIdentifier);
+            Assert.Same(RuntimeInformation.RuntimeIdentifier, RuntimeInformation.RuntimeIdentifier);
+            Assert.EndsWith(RuntimeInformation.ProcessArchitecture.ToString(), RuntimeInformation.RuntimeIdentifier, StringComparison.OrdinalIgnoreCase);
+        }
+
+        [Fact]
+        [ActiveIssue("https://github.com/dotnet/runtime/issues/26780")] // need a new testhost
+        public void VerifyEnvironmentVariable()
+        {
+            RemoteInvokeOptions options = new RemoteInvokeOptions();
+            options.StartInfo.EnvironmentVariables.Add("DOTNET_RUNTIME_ID", "overridenFromEnv-rid");
+
+            RemoteExecutor.Invoke(() =>
+            {
+                Assert.Equal("overridenFromEnv-rid", RuntimeInformation.RuntimeIdentifier);
+            }, options).Dispose();
+        }
+
+        [Fact]
+        public void VerifyAppContextVariable()
+        {
+            RemoteExecutor.Invoke(() =>
+            {
+                AppDomain.CurrentDomain.SetData("RUNTIME_IDENTIFIER", "overriden-rid");
+
+                Assert.Equal("overriden-rid", RuntimeInformation.RuntimeIdentifier);
+            }).Dispose();
+        }
+
+        [Fact]
+        public void VerifyAppContextVariableUnknown()
+        {
+            RemoteExecutor.Invoke(() =>
+            {
+                AppDomain.CurrentDomain.SetData("RUNTIME_IDENTIFIER", null);
+
+                Assert.Equal("unknown", RuntimeInformation.RuntimeIdentifier);
+            }).Dispose();
+
+            RemoteExecutor.Invoke(() =>
+            {
+                AppDomain.CurrentDomain.SetData("RUNTIME_IDENTIFIER", new object());
+
+                Assert.Equal("unknown", RuntimeInformation.RuntimeIdentifier);
+            }).Dispose();
+        }
+
+        [Fact, PlatformSpecific(TestPlatforms.Windows)]
+        [ActiveIssue("https://github.com/dotnet/runtime/issues/26780")] // need a new testhost
+        public void VerifyWindowsRid()
+        {
+            Assert.StartsWith("win", RuntimeInformation.RuntimeIdentifier, StringComparison.OrdinalIgnoreCase);
+        }
+
+        [Fact, PlatformSpecific(TestPlatforms.Linux)]
+        [ActiveIssue("https://github.com/dotnet/runtime/issues/26780")] // need a new testhost
+        public void VerifyLinuxRid()
+        {
+            string expectedOSName = File.ReadAllLines("/etc/os-release")
+                .First(line => line.StartsWith("ID=", StringComparison.OrdinalIgnoreCase))
+                .Substring("ID=".Length)
+                .Trim();
+
+            Assert.StartsWith(expectedOSName, RuntimeInformation.RuntimeIdentifier, StringComparison.OrdinalIgnoreCase);
+        }
+
+        [Fact, PlatformSpecific(TestPlatforms.FreeBSD)]
+        [ActiveIssue("https://github.com/dotnet/runtime/issues/26780")] // need a new testhost
+        public void VerifyFreeBSDRid()
+        {
+            Assert.StartsWith("freebsd", RuntimeInformation.RuntimeIdentifier, StringComparison.OrdinalIgnoreCase);
+        }
+
+        [Fact, PlatformSpecific(TestPlatforms.OSX)]
+        [ActiveIssue("https://github.com/dotnet/runtime/issues/26780")] // need a new testhost
+        public void VerifyOSXRid()
+        {
+            Assert.StartsWith("osx", RuntimeInformation.RuntimeIdentifier, StringComparison.OrdinalIgnoreCase);
+        }
+    }
+}
index b5c466a5617fdfb77224a3a1c2362f7cb725f49c..d2db26f29416eecf3235a4837f461ef7b9b3e8eb 100644 (file)
@@ -1,10 +1,12 @@
 <Project Sdk="Microsoft.NET.Sdk">
   <PropertyGroup>
+    <IncludeRemoteExecutor>true</IncludeRemoteExecutor>
     <TargetFrameworks>$(NetCoreAppCurrent)-Windows_NT;$(NetCoreAppCurrent)-Unix</TargetFrameworks>
   </PropertyGroup>
   <ItemGroup>
     <Compile Include="CheckArchitectureTests.cs" />
     <Compile Include="CheckPlatformTests.cs" />
+    <Compile Include="RuntimeIdentifierTests.cs" />
     <Compile Include="DescriptionNameTests.cs" />
     <Compile Include="$(CommonPath)Interop\Linux\cgroups\Interop.cgroups.cs">
       <Link>Common\Interop\Linux\Interop.cgroups.cs</Link>