Skip to content

Commit 59bfde3

Browse files
committed
Add NativeLibraryPathResolver
1 parent a6e374d commit 59bfde3

File tree

3 files changed

+2284
-52
lines changed

3 files changed

+2284
-52
lines changed

LibGit2Sharp/GlobalSettings.cs

Lines changed: 3 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ public static class GlobalSettings
1414
{
1515
private static readonly Lazy<Version> version = new Lazy<Version>(Version.Build);
1616
private static readonly Dictionary<Filter, FilterRegistration> registeredFilters;
17-
private static readonly bool nativeLibraryPathAllowed;
1817

1918
private static LogConfiguration logConfiguration = LogConfiguration.None;
2019

@@ -24,49 +23,11 @@ public static class GlobalSettings
2423

2524
static GlobalSettings()
2625
{
27-
bool netFX = Platform.IsRunningOnNetFramework();
28-
bool netCore = Platform.IsRunningOnNetCore();
29-
30-
nativeLibraryPathAllowed = netFX || netCore;
31-
32-
if (netFX)
33-
{
34-
// For .NET Framework apps the dependencies are deployed to lib/win32/{architecture} directory
35-
nativeLibraryDefaultPath = Path.Combine(GetExecutingAssemblyDirectory(), "lib", "win32");
36-
}
37-
else
38-
{
39-
nativeLibraryDefaultPath = null;
40-
}
26+
nativeLibraryDefaultPath = NativeLibraryPathResolver.GetNativeLibraryDefaultPath();
4127

4228
registeredFilters = new Dictionary<Filter, FilterRegistration>();
4329
}
4430

45-
private static string GetExecutingAssemblyDirectory()
46-
{
47-
// Assembly.CodeBase is not actually a correctly formatted
48-
// URI. It's merely prefixed with `file:///` and has its
49-
// backslashes flipped. This is superior to EscapedCodeBase,
50-
// which does not correctly escape things, and ambiguates a
51-
// space (%20) with a literal `%20` in the path. Sigh.
52-
var managedPath = Assembly.GetExecutingAssembly().CodeBase;
53-
if (managedPath == null)
54-
{
55-
managedPath = Assembly.GetExecutingAssembly().Location;
56-
}
57-
else if (managedPath.StartsWith("file:///"))
58-
{
59-
managedPath = managedPath.Substring(8).Replace('/', '\\');
60-
}
61-
else if (managedPath.StartsWith("file://"))
62-
{
63-
managedPath = @"\\" + managedPath.Substring(7).Replace('/', '\\');
64-
}
65-
66-
managedPath = Path.GetDirectoryName(managedPath);
67-
return managedPath;
68-
}
69-
7031
/// <summary>
7132
/// Returns information related to the current LibGit2Sharp
7233
/// library.
@@ -186,21 +147,11 @@ public static string NativeLibraryPath
186147
{
187148
get
188149
{
189-
if (!nativeLibraryPathAllowed)
190-
{
191-
throw new LibGit2SharpException("Querying the native hint path is only supported on .NET Framework and .NET Core platforms");
192-
}
193-
194150
return nativeLibraryPath ?? nativeLibraryDefaultPath;
195151
}
196152

197153
set
198154
{
199-
if (!nativeLibraryPathAllowed)
200-
{
201-
throw new LibGit2SharpException("Setting the native hint path is only supported on .NET Framework and .NET Core platforms");
202-
}
203-
204155
if (nativeLibraryPathLocked)
205156
{
206157
throw new LibGit2SharpException("You cannot set the native library path after it has been loaded");
@@ -220,8 +171,8 @@ public static string NativeLibraryPath
220171
internal static string GetAndLockNativeLibraryPath()
221172
{
222173
nativeLibraryPathLocked = true;
223-
string result = nativeLibraryPath ?? nativeLibraryDefaultPath;
224-
return Platform.IsRunningOnNetFramework() ? Path.Combine(result, Platform.ProcessorArchitecture) : result;
174+
175+
return nativeLibraryPath ?? nativeLibraryDefaultPath;
225176
}
226177

227178
/// <summary>
Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
using System.Collections.Generic;
2+
using System.IO;
3+
using System.Linq;
4+
using System.Reflection;
5+
using Microsoft.DotNet.PlatformAbstractions;
6+
using SimpleJson;
7+
8+
namespace LibGit2Sharp
9+
{
10+
static class NativeLibraryPathResolver
11+
{
12+
static readonly HashSet<string> nativeLibraries = new HashSet<string> { "debian.9-x64", "fedora-x64", "linux-x64", "osx", "rhel-x64", "win-x64", "win-x86" };
13+
14+
public static string GetNativeLibraryDefaultPath()
15+
{
16+
var runtimeIdentifier = RuntimeEnvironment.GetRuntimeIdentifier();
17+
var graph = GetRuntimeGraph();
18+
19+
var compatibleRuntimeIdentifiers = graph.GetCompatibleRuntimeIdentifiers(runtimeIdentifier);
20+
21+
return GetNativeLibraryPath(compatibleRuntimeIdentifiers);
22+
}
23+
24+
private static Graph GetRuntimeGraph()
25+
{
26+
var assembly = Assembly.GetExecutingAssembly();
27+
var resourceName = "runtime.json";
28+
29+
var rids = new List<Runtime>();
30+
31+
using (var stream = assembly.GetManifestResourceStream(resourceName))
32+
using (var reader = new StreamReader(stream))
33+
{
34+
var result = reader.ReadToEnd();
35+
var json = (JsonObject)SimpleJson.SimpleJson.DeserializeObject(result);
36+
37+
var runtimes = (JsonObject)json["runtimes"];
38+
39+
foreach (var runtime in runtimes)
40+
{
41+
var imports = (JsonArray)((JsonObject)runtime.Value)["#import"];
42+
43+
var importedRuntimeIdentifiers = new List<string>();
44+
45+
foreach (var import in imports)
46+
{
47+
importedRuntimeIdentifiers.Add((string)import);
48+
}
49+
50+
rids.Add(new Runtime(runtime.Key, importedRuntimeIdentifiers));
51+
}
52+
}
53+
54+
return new Graph(rids);
55+
}
56+
57+
private static string GetNativeLibraryPath(List<string> runtimeIdentifiers)
58+
{
59+
foreach (var runtimeIdentifier in runtimeIdentifiers)
60+
{
61+
if (nativeLibraries.Contains(runtimeIdentifier))
62+
{
63+
return Path.Combine(GetExecutingAssemblyDirectory(), "runtimes", runtimeIdentifier, "native");
64+
}
65+
}
66+
67+
return null;
68+
}
69+
70+
private static string GetExecutingAssemblyDirectory()
71+
{
72+
// Assembly.CodeBase is not actually a correctly formatted
73+
// URI. It's merely prefixed with `file:///` and has its
74+
// backslashes flipped. This is superior to EscapedCodeBase,
75+
// which does not correctly escape things, and ambiguates a
76+
// space (%20) with a literal `%20` in the path. Sigh.
77+
var managedPath = Assembly.GetExecutingAssembly().CodeBase;
78+
79+
if (managedPath == null)
80+
{
81+
managedPath = Assembly.GetExecutingAssembly().Location;
82+
}
83+
else if (managedPath.StartsWith("file:///"))
84+
{
85+
managedPath = managedPath.Substring(8).Replace('/', '\\');
86+
}
87+
else if (managedPath.StartsWith("file://"))
88+
{
89+
managedPath = @"\\" + managedPath.Substring(7).Replace('/', '\\');
90+
}
91+
92+
managedPath = Path.GetDirectoryName(managedPath);
93+
94+
return managedPath;
95+
}
96+
97+
class Runtime
98+
{
99+
public string RuntimeIdentifier { get; }
100+
101+
public List<string> ImportedRuntimeIdentifiers { get; }
102+
103+
public Runtime(string runtimeIdentifier, List<string> importedRuntimeIdentifiers)
104+
{
105+
RuntimeIdentifier = runtimeIdentifier;
106+
ImportedRuntimeIdentifiers = importedRuntimeIdentifiers;
107+
}
108+
}
109+
110+
class Graph
111+
{
112+
readonly Dictionary<string, Runtime> runtimes;
113+
114+
public Graph(List<Runtime> list)
115+
{
116+
runtimes = list.ToDictionary(r => r.RuntimeIdentifier);
117+
}
118+
119+
public List<string> GetCompatibleRuntimeIdentifiers(string runtimeIdentifier)
120+
{
121+
var result = new List<string>();
122+
123+
if (runtimes.TryGetValue(runtimeIdentifier, out var initialRuntime))
124+
{
125+
var queue = new Queue<Runtime>();
126+
var hash = new HashSet<string>();
127+
128+
hash.Add(runtimeIdentifier);
129+
queue.Enqueue(initialRuntime);
130+
131+
while (queue.Count > 0)
132+
{
133+
var runtime = queue.Dequeue();
134+
result.Add(runtime.RuntimeIdentifier);
135+
136+
foreach (var item in runtime.ImportedRuntimeIdentifiers)
137+
{
138+
if (hash.Add(item))
139+
{
140+
queue.Enqueue(runtimes[item]);
141+
}
142+
}
143+
}
144+
}
145+
else
146+
{
147+
result.Add(runtimeIdentifier);
148+
}
149+
150+
return result;
151+
}
152+
}
153+
}
154+
}

0 commit comments

Comments
 (0)