|
1 | 1 | using System;
|
2 | 2 | using System.IO;
|
| 3 | +using System.Reflection; |
3 | 4 | using System.Runtime.CompilerServices;
|
4 | 5 | using System.Runtime.ConstrainedExecution;
|
5 | 6 | using System.Runtime.InteropServices;
|
@@ -29,32 +30,146 @@ static NativeMethods()
|
29 | 30 | {
|
30 | 31 | if (Platform.IsRunningOnNetFramework() || Platform.IsRunningOnNetCore())
|
31 | 32 | {
|
32 |
| - string nativeLibraryDir = GlobalSettings.GetAndLockNativeLibraryPath(); |
33 |
| - if (nativeLibraryDir != null) |
| 33 | + // Use .NET Core 3.0+ NativeLibrary when available. |
| 34 | + if (!TryUseNativeLibrary()) |
34 | 35 | {
|
35 |
| - string nativeLibraryPath = Path.Combine(nativeLibraryDir, libgit2 + Platform.GetNativeLibraryExtension()); |
| 36 | + // NativeLibrary is not available, fall back. |
36 | 37 |
|
| 38 | + // Use GlobalSettings.NativeLibraryPath when set. |
37 | 39 | // Try to load the .dll from the path explicitly.
|
38 | 40 | // If this call succeeds further DllImports will find the library loaded and not attempt to load it again.
|
39 | 41 | // If it fails the next DllImport will load the library from safe directories.
|
| 42 | + string nativeLibraryPath = GetGlobalSettingsNativeLibraryPath(); |
| 43 | + if (nativeLibraryPath != null) |
| 44 | + { |
40 | 45 | #if NETFRAMEWORK
|
41 |
| - if (Platform.OperatingSystem == OperatingSystemType.Windows) |
| 46 | + if (Platform.OperatingSystem == OperatingSystemType.Windows) |
42 | 47 | #else
|
43 |
| - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) |
| 48 | + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) |
44 | 49 | #endif
|
45 |
| - { |
46 |
| - LoadWindowsLibrary(nativeLibraryPath); |
47 |
| - } |
48 |
| - else |
49 |
| - { |
50 |
| - LoadUnixLibrary(nativeLibraryPath, RTLD_NOW); |
| 50 | + { |
| 51 | + LoadWindowsLibrary(nativeLibraryPath); |
| 52 | + } |
| 53 | + else |
| 54 | + { |
| 55 | + LoadUnixLibrary(nativeLibraryPath, RTLD_NOW); |
| 56 | + } |
51 | 57 | }
|
52 | 58 | }
|
53 | 59 | }
|
54 | 60 |
|
55 | 61 | InitializeNativeLibrary();
|
56 | 62 | }
|
57 | 63 |
|
| 64 | + private static string GetGlobalSettingsNativeLibraryPath() |
| 65 | + { |
| 66 | + string nativeLibraryDir = GlobalSettings.GetAndLockNativeLibraryPath(); |
| 67 | + if (nativeLibraryDir == null) |
| 68 | + { |
| 69 | + return null; |
| 70 | + } |
| 71 | + return Path.Combine(nativeLibraryDir, libgit2 + Platform.GetNativeLibraryExtension()); |
| 72 | + } |
| 73 | + |
| 74 | + private delegate bool TryLoadLibraryByNameDelegate(string libraryName, Assembly assembly, DllImportSearchPath? searchPath, out IntPtr handle); |
| 75 | + private delegate bool TryLoadLibraryByPathDelegate(string libraryPath, out IntPtr handle); |
| 76 | + |
| 77 | + static TryLoadLibraryByNameDelegate _tryLoadLibraryByName; |
| 78 | + static TryLoadLibraryByPathDelegate _tryLoadLibraryByPath; |
| 79 | + |
| 80 | + static bool TryLoadLibrary(string libraryName, Assembly assembly, DllImportSearchPath? searchPath, out IntPtr handle) |
| 81 | + { |
| 82 | + if (_tryLoadLibraryByName == null) |
| 83 | + { |
| 84 | + throw new NotSupportedException(); |
| 85 | + } |
| 86 | + return _tryLoadLibraryByName(libraryName, assembly, searchPath, out handle); |
| 87 | + } |
| 88 | + |
| 89 | + static bool TryLoadLibrary(string libraryPath, out IntPtr handle) |
| 90 | + { |
| 91 | + if (_tryLoadLibraryByPath == null) |
| 92 | + { |
| 93 | + throw new NotSupportedException(); |
| 94 | + } |
| 95 | + return _tryLoadLibraryByPath(libraryPath, out handle); |
| 96 | + } |
| 97 | + |
| 98 | + private static bool TryUseNativeLibrary() |
| 99 | + { |
| 100 | + // NativeLibrary is available in .NET Core 3.0+. |
| 101 | + // We use reflection to use NativeLibrary so this library can target 'netstandard2.0'. |
| 102 | + |
| 103 | + Type dllImportResolverType = Type.GetType("System.Runtime.InteropServices.DllImportResolver, System.Runtime.InteropServices", throwOnError: false); |
| 104 | + Type nativeLibraryType = Type.GetType("System.Runtime.InteropServices.NativeLibrary, System.Runtime.InteropServices", throwOnError: false); |
| 105 | + var tryLoadLibraryByName = (TryLoadLibraryByNameDelegate)nativeLibraryType?.GetMethod("TryLoad", |
| 106 | + new Type[] { typeof(string), typeof(Assembly), typeof(DllImportSearchPath?), typeof(IntPtr).MakeByRefType() })?.CreateDelegate(typeof(TryLoadLibraryByNameDelegate)); |
| 107 | + var tryLoadLibraryByPath = (TryLoadLibraryByPathDelegate)nativeLibraryType?.GetMethod("TryLoad", |
| 108 | + new Type[] { typeof(string), typeof(IntPtr).MakeByRefType() })?.CreateDelegate(typeof(TryLoadLibraryByPathDelegate)); |
| 109 | + MethodInfo setDllImportResolver = nativeLibraryType?.GetMethod("SetDllImportResolver", new Type[] { typeof(Assembly), dllImportResolverType}); |
| 110 | + |
| 111 | + if (dllImportResolverType == null || |
| 112 | + nativeLibraryType == null || |
| 113 | + tryLoadLibraryByName == null || |
| 114 | + tryLoadLibraryByPath == null || |
| 115 | + setDllImportResolver == null) |
| 116 | + { |
| 117 | + return false; |
| 118 | + } |
| 119 | + |
| 120 | + _tryLoadLibraryByPath = tryLoadLibraryByPath; |
| 121 | + _tryLoadLibraryByName = tryLoadLibraryByName; |
| 122 | + |
| 123 | + // NativeMethods.SetDllImportResolver(typeof(NativeMethods).Assembly, ResolveDll); |
| 124 | + object resolveDelegate = typeof(NativeMethods).GetMethod(nameof(ResolveDll), BindingFlags.NonPublic | BindingFlags.Static).CreateDelegate(dllImportResolverType); |
| 125 | + setDllImportResolver.Invoke(null, new object[] { typeof(NativeMethods).Assembly, resolveDelegate }); |
| 126 | + |
| 127 | + return true; |
| 128 | + } |
| 129 | + |
| 130 | + private static IntPtr ResolveDll(string libraryName, Assembly assembly, DllImportSearchPath? searchPath) |
| 131 | + { |
| 132 | + IntPtr handle = IntPtr.Zero; |
| 133 | + if (libraryName == libgit2) |
| 134 | + { |
| 135 | + // Use GlobalSettings.NativeLibraryPath when set. |
| 136 | + string nativeLibraryPath = GetGlobalSettingsNativeLibraryPath(); |
| 137 | + if (nativeLibraryPath != null && |
| 138 | + TryLoadLibrary(nativeLibraryPath, out handle)) |
| 139 | + { |
| 140 | + return handle; |
| 141 | + } |
| 142 | + |
| 143 | + // Use Default DllImport resolution. |
| 144 | + if (TryLoadLibrary(libraryName, assembly, searchPath, out handle)) |
| 145 | + { |
| 146 | + return handle; |
| 147 | + } |
| 148 | + |
| 149 | +#if NETFRAMEWORK |
| 150 | +#else |
| 151 | + // We cary a number of .so files for Linux which are linked against various |
| 152 | + // libc/OpenSSL libraries. Try them out. |
| 153 | + if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) |
| 154 | + { |
| 155 | + // The libraries are located at 'runtimes/<rid>/native/lib{libraryName}.so' |
| 156 | + // The <rid> ends with the processor architecture. e.g. fedora-x64. |
| 157 | + string assemblyDirectory = Path.GetDirectoryName(typeof(NativeMethods).Assembly.Location); |
| 158 | + string processorArchitecture = RuntimeInformation.ProcessArchitecture.ToString().ToLowerInvariant(); |
| 159 | + foreach (var runtimeFolder in Directory.GetDirectories(Path.Combine(assemblyDirectory, "runtimes"), $"*-{processorArchitecture}")) |
| 160 | + { |
| 161 | + string libPath = Path.Combine(runtimeFolder, "native", $"lib{libraryName}.so"); |
| 162 | + if (TryLoadLibrary(libPath, out handle)) |
| 163 | + { |
| 164 | + return handle; |
| 165 | + } |
| 166 | + } |
| 167 | + } |
| 168 | +#endif |
| 169 | + } |
| 170 | + return handle; |
| 171 | + } |
| 172 | + |
58 | 173 | public const int RTLD_NOW = 0x002;
|
59 | 174 |
|
60 | 175 | [DllImport("libdl", EntryPoint = "dlopen")]
|
|
0 commit comments