Skip to content

Commit

Permalink
Enhancements to RID fallback logic to handle unknown RIDs
Browse files Browse the repository at this point in the history
  • Loading branch information
fabiocav committed Oct 18, 2018
1 parent f06136b commit fc0c087
Show file tree
Hide file tree
Showing 4 changed files with 111 additions and 42 deletions.
109 changes: 85 additions & 24 deletions src/WebJobs.Script/Description/DotNet/DependencyHelper.cs
Expand Up @@ -5,8 +5,10 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using Microsoft.Extensions.DependencyModel;
using Newtonsoft.Json.Linq;
using static Microsoft.DotNet.PlatformAbstractions.RuntimeEnvironment;

namespace Microsoft.Azure.WebJobs.Script.Description
{
Expand All @@ -32,32 +34,34 @@ public static class DependencyHelper
return ridGraph;
}

public static IEnumerable<string> GetFallbacks(Dictionary<string, string[]> ridGraph, string rid)
private static string GetDefaultPlatformRid()
{
var fallbacks = new HashSet<string>();
var queue = new Queue<string>(ridGraph[rid]);

while (queue.Count > 0)
// 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))
{
var currentRid = queue.Dequeue();

if (fallbacks.Contains(currentRid))
{
continue;
}

fallbacks.Add(currentRid);

foreach (var fallbackRid in ridGraph[currentRid])
{
if (!fallbacks.Contains(fallbackRid))
{
queue.Enqueue(fallbackRid);
}
}
rid = DotNetConstants.DefaultWindowsRID;
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
{
rid = DotNetConstants.DefaultOSXRID;
}
else
{
rid = DotNetConstants.DefaultLinuxRID;
}

return fallbacks;
return rid;
}

private static string GetRuntimeAssembliesJson()
Expand Down Expand Up @@ -100,9 +104,66 @@ private static string GetResourceFileContents(string fileName)
/// <returns>The runtime fallbacks for the provided identifier.</returns>
public static RuntimeFallbacks GetDefaultRuntimeFallbacks(string rid)
{
IEnumerable<string> fallbacks = GetFallbacks(_ridGraph.Value, 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");

return new RuntimeFallbacks(rid, fallbacks);
var rids = new List<string> { fallbacks.Runtime };
rids.AddRange(fallbacks.Fallbacks);
return rids;
}
}
}
4 changes: 4 additions & 0 deletions src/WebJobs.Script/Description/DotNet/DotNetConstants.cs
Expand Up @@ -19,6 +19,10 @@ public static class DotNetConstants
public const string InvalidEntryPointNameCompilationCode = "AF007";
public const string AsyncVoidCode = "AF008";

public const string DefaultWindowsRID = "win10";
public const string DefaultOSXRID = "osx.10.12";
public const string DefaultLinuxRID = "linux";

public static string[] FrameworkReferences = new[]
{
"Microsoft.CSharp",
Expand Down
Expand Up @@ -11,8 +11,6 @@
using System.Runtime.Loader;
using Microsoft.Azure.WebJobs.Host;
using Microsoft.Azure.WebJobs.Script.Config;
using Microsoft.Extensions.DependencyModel;
using static Microsoft.DotNet.PlatformAbstractions.RuntimeEnvironment;
using ResolutionPolicyEvaluator = System.Func<System.Reflection.AssemblyName, System.Reflection.Assembly, bool>;

namespace Microsoft.Azure.WebJobs.Script.Description
Expand Down Expand Up @@ -226,28 +224,13 @@ private string GetRuntimeAssetPath(string assetFileName, bool isNativeAsset)
string ridSubFolder = isNativeAsset ? "native" : string.Empty;
string runtimesPath = Path.Combine(basePath, "runtimes");

List<string> rids = GetRuntimeFallbacks();
List<string> rids = DependencyHelper.GetRuntimeFallbacks();

return rids.Select(r => Path.Combine(runtimesPath, r, ridSubFolder, assetFileName))
.Union(_probingPaths)
.FirstOrDefault(p => File.Exists(p));
}

private static List<string> GetRuntimeFallbacks()
{
string currentRuntimeIdentifier = GetRuntimeIdentifier();

RuntimeFallbacks fallbacks = DependencyContext.Default
.RuntimeGraph
.FirstOrDefault(f => string.Equals(f.Runtime, currentRuntimeIdentifier, StringComparison.OrdinalIgnoreCase))
?? DependencyHelper.GetDefaultRuntimeFallbacks(currentRuntimeIdentifier)
?? new RuntimeFallbacks("any");

var rids = new List<string> { fallbacks.Runtime };
rids.AddRange(fallbacks.Fallbacks);
return rids;
}

internal string GetUnmanagedLibraryFileName(string unmanagedLibraryName)
{
// We need to properly resolve the native library in different platforms:
Expand Down
Expand Up @@ -27,5 +27,26 @@ public void GetDefaultRuntimeFallbacks_MatchesCurrentRuntimeFallbacks()
Assert.True(match, $"Mismatched fallbacks for RID '{fallback.Runtime}'");
}
}

[Theory]
[InlineData("win11-x86")]
public void GetRuntimeFallbacks_WithUnkonwnRid_DefaultsToPlatformRid(string rid)
{
List<string> rids = DependencyHelper.GetRuntimeFallbacks(rid);

// Ensure the "unknown" RID is still in the list
Assert.Equal(rid, rids.First());

// Ensure our fallback list matches our default RID fallback
var defaultRidFallback = DependencyHelper.GetDefaultRuntimeFallbacks(DotNetConstants.DefaultWindowsRID);
var defaultRidGraph = new List<string> { defaultRidFallback.Runtime };
defaultRidGraph.AddRange(defaultRidFallback.Fallbacks);

bool match = defaultRidGraph
.Zip(rids.Skip(1), (s1, s2) => string.Equals(s1, s2, StringComparison.Ordinal))
.All(r => r);

Assert.True(match, $"Mismatched fallbacks for unknown RID");
}
}
}

0 comments on commit fc0c087

Please sign in to comment.