build/DependencyHelper.cs (114 lines of code) (raw):
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using Microsoft.Extensions.DependencyModel;
using Newtonsoft.Json.Linq;
namespace Build
{
public static class DependencyHelper
{
private static readonly Lazy<Dictionary<string, string[]>> _ridGraph = new Lazy<Dictionary<string, string[]>>(BuildRuntimesGraph);
private static string _runtimeIdentifier;
public const string DefaultWindowsRID = "win10";
public const string DefaultOSXRID = "osx.10.12";
public const string DefaultLinuxRID = "linux";
private static Dictionary<string, string[]> BuildRuntimesGraph()
{
var ridGraph = new Dictionary<string, string[]>();
string runtimesJson = GetRuntimesGraphJson();
var runtimes = (JObject)JObject.Parse(runtimesJson)["runtimes"];
foreach (var runtime in runtimes)
{
string[] imports = ((JObject)runtime.Value)["#import"]
?.Values<string>()
.ToArray();
ridGraph.Add(runtime.Key, imports);
}
return ridGraph;
}
private static string GetDefaultPlatformRid()
{
// This logic follows what the .NET Core host does in: https://github.com/dotnet/core-setup/blob/master/src/corehost/common/pal.h
// When running on a platform that is not supported in RID fallback graph (because it was unknown
// at the time the SharedFX in question was built), we need to use a reasonable fallback RID to allow
// consuming the native assets.
//
// For Windows and OSX, we will maintain the last highest RID-Platform we are known to support for them as the
// degree of compat across their respective releases is usually high.
//
// We cannot maintain the same (compat) invariant for linux and thus, we will fallback to using lowest RID-Plaform.
string rid = null;
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
rid = DefaultWindowsRID;
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
{
rid = DefaultOSXRID;
}
else
{
rid = DefaultLinuxRID;
}
return rid;
}
private static string GetRuntimesGraphJson()
{
return GetResourceFileContents("runtimes.json");
}
private static string GetResourceFileContents(string fileName)
{
var assembly = typeof(Helper).Assembly;
using (Stream resource = assembly.GetManifestResourceStream("Build.runtimes.json")) // ($"{assembly.GetName().Name}.{fileName}"))
using (var reader = new StreamReader(resource))
{
return reader.ReadToEnd();
}
}
/// <summary>
/// Gets the default runtime fallback RIDs for a given RID.
/// The graph used to build the fallback list is static and
/// useful in self-contained scenarios, where this information
/// is not available at runtime
/// </summary>
/// <param name="rid">The runtime identifier to lookup.</param>
/// <returns>The runtime fallbacks for the provided identifier.</returns>
public static RuntimeFallbacks GetDefaultRuntimeFallbacks(string rid)
{
var ridGraph = _ridGraph.Value;
var runtimeFallbacks = new RuntimeFallbacks(rid);
var fallbacks = new List<string>();
if (!ridGraph.ContainsKey(rid))
{
rid = GetDefaultPlatformRid();
fallbacks.Add(rid);
}
var queue = new Queue<string>(ridGraph[rid]);
while (queue.Count > 0)
{
var currentRid = queue.Dequeue();
if (fallbacks.Contains(currentRid))
{
continue;
}
fallbacks.Add(currentRid);
foreach (var fallbackRid in ridGraph[currentRid])
{
if (!fallbacks.Contains(fallbackRid, StringComparer.OrdinalIgnoreCase))
{
queue.Enqueue(fallbackRid);
}
}
}
runtimeFallbacks.Fallbacks = fallbacks.AsReadOnly();
return runtimeFallbacks;
}
public static List<string> GetRuntimeFallbacks()
{
string currentRuntimeIdentifier = GetRuntimeIdentifier();
return GetRuntimeFallbacks(currentRuntimeIdentifier);
}
public static List<string> GetRuntimeFallbacks(string rid)
{
if (rid == null)
{
throw new ArgumentNullException(nameof(rid));
}
RuntimeFallbacks fallbacks = DependencyContext.Default
.RuntimeGraph
.FirstOrDefault(f => string.Equals(f.Runtime, rid, StringComparison.OrdinalIgnoreCase))
?? GetDefaultRuntimeFallbacks(rid)
?? new RuntimeFallbacks("any");
var rids = new List<string> { fallbacks.Runtime };
rids.AddRange(fallbacks.Fallbacks);
return rids;
}
private static string GetRuntimeIdentifier() => _runtimeIdentifier ??= AppContext.GetData("RUNTIME_IDENTIFIER") as string ?? "unknown";
}
}