diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/StartupHookProvider.cs b/src/coreclr/src/System.Private.CoreLib/src/System/StartupHookProvider.cs index cfe7daca7ad939..39bbc1ab9a1914 100644 --- a/src/coreclr/src/System.Private.CoreLib/src/System/StartupHookProvider.cs +++ b/src/coreclr/src/System.Private.CoreLib/src/System/StartupHookProvider.cs @@ -28,7 +28,7 @@ private static void ProcessStartupHooks() // 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; diff --git a/src/installer/corehost/cli/hostpolicy/coreclr.cpp b/src/installer/corehost/cli/hostpolicy/coreclr.cpp index 234353aa4c47e7..b329a72d85886a 100644 --- a/src/installer/corehost/cli/hostpolicy/coreclr.cpp +++ b/src/installer/corehost/cli/hostpolicy/coreclr.cpp @@ -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(common_property::Last), "Invalid property count"); diff --git a/src/installer/corehost/cli/hostpolicy/coreclr.h b/src/installer/corehost/cli/hostpolicy/coreclr.h index d2ccd5da5510d5..a06e81fe2e346f 100644 --- a/src/installer/corehost/cli/hostpolicy/coreclr.h +++ b/src/installer/corehost/cli/hostpolicy/coreclr.h @@ -67,6 +67,7 @@ enum class common_property StartUpHooks, AppPaths, AppNIPaths, + RuntimeIdentifier, // Sentinel value - new values should be defined above Last diff --git a/src/installer/corehost/cli/hostpolicy/hostpolicy_context.cpp b/src/installer/corehost/cli/hostpolicy/hostpolicy_context.cpp index 17bfe5d3e99aae..bb4a965126d962 100644 --- a/src/installer/corehost/cli/hostpolicy/hostpolicy_context.cpp +++ b/src/installer/corehost/cli/hostpolicy/hostpolicy_context.cpp @@ -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()); diff --git a/src/libraries/System.Private.CoreLib/src/System/AppContext.cs b/src/libraries/System.Private.CoreLib/src/System/AppContext.cs index c79e8ee9f96f0a..90735669641622 100644 --- a/src/libraries/System.Private.CoreLib/src/System/AppContext.cs +++ b/src/libraries/System.Private.CoreLib/src/System/AppContext.cs @@ -22,7 +22,7 @@ public static partial class AppContext 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 => diff --git a/src/libraries/System.Private.CoreLib/src/System/Environment.cs b/src/libraries/System.Private.CoreLib/src/System/Environment.cs index 62571701d28c3c..a71c68131c5403 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Environment.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Environment.cs @@ -144,7 +144,7 @@ public static Version Version { // 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()?.InformationalVersion; ReadOnlySpan versionSpan = versionString.AsSpan(); diff --git a/src/libraries/System.Private.CoreLib/src/System/Resources/ResourceManager.Uap.cs b/src/libraries/System.Private.CoreLib/src/System/Resources/ResourceManager.Uap.cs index 2fa9e0dc6e1340..2a8d7373208105 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Resources/ResourceManager.Uap.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Resources/ResourceManager.Uap.cs @@ -88,7 +88,7 @@ private static bool ShouldUseUapResourceManagement(Assembly resourcesAssembly) 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; diff --git a/src/libraries/System.Runtime.InteropServices.RuntimeInformation/ref/System.Runtime.InteropServices.RuntimeInformation.cs b/src/libraries/System.Runtime.InteropServices.RuntimeInformation/ref/System.Runtime.InteropServices.RuntimeInformation.cs index 412075fad881de..f0357797e8adec 100644 --- a/src/libraries/System.Runtime.InteropServices.RuntimeInformation/ref/System.Runtime.InteropServices.RuntimeInformation.cs +++ b/src/libraries/System.Runtime.InteropServices.RuntimeInformation/ref/System.Runtime.InteropServices.RuntimeInformation.cs @@ -32,6 +32,7 @@ public enum Architecture } 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; } } diff --git a/src/libraries/System.Runtime.InteropServices.RuntimeInformation/src/System/Runtime/InteropServices/RuntimeInformation/RuntimeInformation.cs b/src/libraries/System.Runtime.InteropServices.RuntimeInformation/src/System/Runtime/InteropServices/RuntimeInformation/RuntimeInformation.cs index 8d61597264a2a3..8e50ce0d1d91cb 100644 --- a/src/libraries/System.Runtime.InteropServices.RuntimeInformation/src/System/Runtime/InteropServices/RuntimeInformation/RuntimeInformation.cs +++ b/src/libraries/System.Runtime.InteropServices.RuntimeInformation/src/System/Runtime/InteropServices/RuntimeInformation/RuntimeInformation.cs @@ -10,6 +10,7 @@ public static partial class RuntimeInformation { private const string FrameworkName = ".NET Core"; private static string? s_frameworkDescription; + private static string? s_runtimeIdentifier; public static string FrameworkDescription { @@ -17,7 +18,7 @@ public static string FrameworkDescription { 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 @@ public static string FrameworkDescription return s_frameworkDescription; } } + + /// + /// Returns an opaque string that identifies the platform on which an app is running. + /// + /// + /// 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. + /// + public static string RuntimeIdentifier => + s_runtimeIdentifier ??= AppContext.GetData("RUNTIME_IDENTIFIER") as string ?? "unknown"; } } diff --git a/src/libraries/System.Runtime.InteropServices.RuntimeInformation/tests/DescriptionNameTests.cs b/src/libraries/System.Runtime.InteropServices.RuntimeInformation/tests/DescriptionNameTests.cs index 73f692898dbcfd..379bff8436dfde 100644 --- a/src/libraries/System.Runtime.InteropServices.RuntimeInformation/tests/DescriptionNameTests.cs +++ b/src/libraries/System.Runtime.InteropServices.RuntimeInformation/tests/DescriptionNameTests.cs @@ -22,7 +22,8 @@ public void DumpRuntimeInformationToConsole() 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 index 00000000000000..ac7a0b7ad9e0fe --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices.RuntimeInformation/tests/RuntimeIdentifierTests.cs @@ -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); + } + } +} diff --git a/src/libraries/System.Runtime.InteropServices.RuntimeInformation/tests/System.Runtime.InteropServices.RuntimeInformation.Tests.csproj b/src/libraries/System.Runtime.InteropServices.RuntimeInformation/tests/System.Runtime.InteropServices.RuntimeInformation.Tests.csproj index b5c466a5617fdf..d2db26f29416ee 100644 --- a/src/libraries/System.Runtime.InteropServices.RuntimeInformation/tests/System.Runtime.InteropServices.RuntimeInformation.Tests.csproj +++ b/src/libraries/System.Runtime.InteropServices.RuntimeInformation/tests/System.Runtime.InteropServices.RuntimeInformation.Tests.csproj @@ -1,10 +1,12 @@ + true $(NetCoreAppCurrent)-Windows_NT;$(NetCoreAppCurrent)-Unix + Common\Interop\Linux\Interop.cgroups.cs