Skip to content

Commit 82b716d

Browse files
committed
Add NativeLibraryPathResolver
1 parent a6e374d commit 82b716d

File tree

3 files changed

+2283
-52
lines changed

3 files changed

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

0 commit comments

Comments
 (0)