Skip to content

Commit 2504c52

Browse files
A-Ovchinnikov-mxtmdsbordingAArnottqmfrederik
authored
Support Windows ARM64 (#13)
* Cherry-pick some commits from `master` branch * Delete Travis, AppVeyor and Azure Pipelines CI * Enable loading arm64 libgit2 * Use .NET 5.0 for tests Co-authored-by: Tom Deseyn <tom.deseyn@gmail.com> Co-authored-by: Brandon Ording <bording@gmail.com> Co-authored-by: Andrew Arnott <andrewarnott@gmail.com> Co-authored-by: Frederik Carlier <frederik.carlier@quamotion.mobi> Co-authored-by: Edward Thomson <ethomson@edwardthomson.com>
1 parent 461d714 commit 2504c52

17 files changed

+197
-375
lines changed

.travis.yml

Lines changed: 0 additions & 35 deletions
This file was deleted.

LibGit2Sharp.Tests/CommitFixture.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1058,8 +1058,8 @@ public void CanPrettifyAMessage()
10581058
}
10591059

10601060
private readonly string signedCommit =
1061-
"tree 6b79e22d69bf46e289df0345a14ca059dfc9bdf6\n" +
1062-
"parent 34734e478d6cf50c27c9d69026d93974d052c454\n" +
1061+
"tree 4b825dc642cb6eb9a060e54bf8d69288fbee4904\n" +
1062+
"parent 8496071c1b46c854b31185ea97743be6a8774479\n" +
10631063
"author Ben Burkert <ben@benburkert.com> 1358451456 -0800\n" +
10641064
"committer Ben Burkert <ben@benburkert.com> 1358451456 -0800\n" +
10651065
"gpgsig -----BEGIN PGP SIGNATURE-----\n" +
@@ -1102,8 +1102,8 @@ public void CanPrettifyAMessage()
11021102
"-----END PGP SIGNATURE-----";
11031103

11041104
private readonly string signedData =
1105-
"tree 6b79e22d69bf46e289df0345a14ca059dfc9bdf6\n" +
1106-
"parent 34734e478d6cf50c27c9d69026d93974d052c454\n" +
1105+
"tree 4b825dc642cb6eb9a060e54bf8d69288fbee4904\n" +
1106+
"parent 8496071c1b46c854b31185ea97743be6a8774479\n" +
11071107
"author Ben Burkert <ben@benburkert.com> 1358451456 -0800\n" +
11081108
"committer Ben Burkert <ben@benburkert.com> 1358451456 -0800\n" +
11091109
"\n" +
@@ -1155,7 +1155,7 @@ public void CanCreateACommitString()
11551155
[Fact]
11561156
public void CanCreateASignedCommit()
11571157
{
1158-
string repoPath = InitNewRepository();
1158+
string repoPath = SandboxStandardTestRepo();
11591159
using (var repo = new Repository(repoPath))
11601160
{
11611161
var odb = repo.ObjectDatabase;

LibGit2Sharp.Tests/GlobalSettingsFixture.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,25 +22,25 @@ public void CanGetMinimumCompiledInFeatures()
2222
public void CanRetrieveValidVersionString()
2323
{
2424
// Version string format is:
25-
// Major.Minor.Patch[-previewTag]+g{LibGit2Sharp_abbrev_hash}.libgit2-{libgit2_abbrev_hash} (x86|x64 - features)
25+
// Major.Minor.Patch[-previewTag]+{LibGit2Sharp_abbrev_hash}.libgit2-{libgit2_abbrev_hash} (x86|x64|arm64 - features)
2626
// Example output:
27-
// "0.25.0-preview.52+g871d13a67f.libgit2-15e1193 (x86 - Threads, Https)"
27+
// "0.25.0-preview.52+871d13a67f.libgit2-15e1193 (x86 - Threads, Https)"
2828

2929
string versionInfo = GlobalSettings.Version.ToString();
3030

3131
// The GlobalSettings.Version returned string should contain :
3232
// version: '0.25.0[-previewTag]' LibGit2Sharp version number.
3333
// git2SharpHash: '871d13a67f' LibGit2Sharp hash.
34-
// arch: 'x86' or 'x64' libgit2 target.
34+
// arch: 'x86', 'x64' or 'arm64' libgit2 target.
3535
// git2Features: 'Threads, Ssh' libgit2 features compiled with.
36-
string regex = @"^(?<version>\d+\.\d+\.\d+(-[\w\-\.]+)?\+(g(?<git2SharpHash>[a-f0-9]{10})\.)?libgit2-[a-f0-9]{7}) \((?<arch>\w+) - (?<git2Features>(?:\w*(?:, )*\w+)*)\)$";
36+
string regex = @"^(?<version>\d+\.\d+\.\d+(-[\w\-\.]+)?\+((?<git2SharpHash>[a-f0-9]{10})\.)?libgit2-[a-f0-9]{7}) \((?<arch>\w+) - (?<git2Features>(?:\w*(?:, )*\w+)*)\)$";
3737

3838
Assert.NotNull(versionInfo);
3939

4040
Match regexResult = Regex.Match(versionInfo, regex);
4141

4242
Assert.True(regexResult.Success, "The following version string format is enforced:" +
43-
"Major.Minor.Patch[-previewTag]+g{LibGit2Sharp_abbrev_hash}.libgit2-{libgit2_abbrev_hash} (x86|x64 - features). " +
43+
"Major.Minor.Patch[-previewTag]+{LibGit2Sharp_abbrev_hash}.libgit2-{libgit2_abbrev_hash} (x86|x64|arm64 - features). " +
4444
"But found \"" + versionInfo + "\" instead.");
4545
}
4646

Lines changed: 14 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,30 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
4-
<TargetFrameworks>net46;netcoreapp2.0</TargetFrameworks>
4+
<TargetFrameworks>net472;net5.0</TargetFrameworks>
55
</PropertyGroup>
66

77
<ItemGroup>
88
<ProjectReference Include="..\LibGit2Sharp\LibGit2Sharp.csproj" />
9-
<ProjectReference Include="..\NativeLibraryLoadTestApp\x86\NativeLibraryLoadTestApp.x86.csproj" Condition="'$(TargetFramework)' == 'net46'" ReferenceOutputAssembly="false" OutputItemType="TestAppExe" />
10-
<ProjectReference Include="..\NativeLibraryLoadTestApp\x64\NativeLibraryLoadTestApp.x64.csproj" Condition="'$(TargetFramework)' == 'net46'" ReferenceOutputAssembly="false" OutputItemType="TestAppExe" />
9+
<ProjectReference Include="..\NativeLibraryLoadTestApp\x86\NativeLibraryLoadTestApp.x86.csproj" Condition="'$(TargetFramework)' != 'net5.0'" ReferenceOutputAssembly="false" OutputItemType="TestAppExe" />
10+
<ProjectReference Include="..\NativeLibraryLoadTestApp\x64\NativeLibraryLoadTestApp.x64.csproj" Condition="'$(TargetFramework)' != 'net5.0'" ReferenceOutputAssembly="false" OutputItemType="TestAppExe" />
1111
</ItemGroup>
1212

1313
<ItemGroup>
14-
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.8.0" />
15-
<PackageReference Include="Moq" Version="4.9.0" />
16-
<PackageReference Include="xunit" Version="2.4.0" />
17-
<PackageReference Include="xunit.runner.console" Version="2.4.0" />
18-
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.0" />
19-
<PackageReference Include="xunit.skippablefact" Version="1.3.6" />
14+
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.4.0" />
15+
<PackageReference Include="Moq" Version="4.10.1" />
16+
<PackageReference Include="System.ValueTuple" Version="4.5.0" />
17+
<PackageReference Include="xunit" Version="2.4.1" />
18+
<PackageReference Include="xunit.runner.console" Version="2.4.1" />
19+
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.1" />
20+
<PackageReference Include="xunit.skippablefact" Version="1.3.12" />
2021
</ItemGroup>
2122

2223
<ItemGroup>
2324
<Compile Include="..\LibGit2Sharp\Core\Platform.cs" Link="TestHelpers\Platform.cs" />
24-
<Compile Remove="desktop\**" Condition=" '$(TargetFramework)' != 'net46' " />
25-
</ItemGroup>
26-
27-
<ItemGroup>
28-
<Content Include="Resources\**\*.*">
29-
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
30-
</Content>
25+
<Compile Remove="desktop\**" Condition="'$(TargetFramework)' == 'net5.0'" />
26+
<Content Include="Resources\**\*.*" CopyToOutputDirectory="PreserveNewest" />
27+
<None Update="xunit.runner.json" CopyToOutputDirectory="PreserveNewest" />
3128
</ItemGroup>
3229

3330
<Target Name="CopyTestAppExes" AfterTargets="ResolveProjectReferences">
@@ -43,4 +40,4 @@
4340

4441
<Import Project="..\Targets\GenerateNativeDllName.targets" />
4542

46-
</Project>
43+
</Project>

LibGit2Sharp.Tests/xunit.runner.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"$schema": "https://xunit.github.io/schema/current/xunit.runner.schema.json",
3+
"shadowCopy": false
4+
}

LibGit2Sharp/Core/NativeMethods.cs

Lines changed: 126 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using System.IO;
3+
using System.Reflection;
34
using System.Runtime.CompilerServices;
45
using System.Runtime.ConstrainedExecution;
56
using System.Runtime.InteropServices;
@@ -29,32 +30,144 @@ static NativeMethods()
2930
{
3031
if (Platform.IsRunningOnNetFramework() || Platform.IsRunningOnNetCore())
3132
{
32-
string nativeLibraryDir = GlobalSettings.GetAndLockNativeLibraryPath();
33-
if (nativeLibraryDir != null)
33+
// Use .NET Core 3.0+ NativeLibrary when available.
34+
if (!TryUseNativeLibrary())
3435
{
35-
string nativeLibraryPath = Path.Combine(nativeLibraryDir, libgit2 + Platform.GetNativeLibraryExtension());
36+
// NativeLibrary is not available, fall back.
3637

38+
// Use GlobalSettings.NativeLibraryPath when set.
3739
// Try to load the .dll from the path explicitly.
3840
// If this call succeeds further DllImports will find the library loaded and not attempt to load it again.
3941
// If it fails the next DllImport will load the library from safe directories.
40-
#if NETFRAMEWORK
41-
if (Platform.OperatingSystem == OperatingSystemType.Windows)
42-
#else
43-
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
44-
#endif
42+
string nativeLibraryPath = GetGlobalSettingsNativeLibraryPath();
43+
if (nativeLibraryPath != null)
4544
{
46-
LoadWindowsLibrary(nativeLibraryPath);
47-
}
48-
else
49-
{
50-
LoadUnixLibrary(nativeLibraryPath, RTLD_NOW);
45+
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
46+
{
47+
LoadWindowsLibrary(nativeLibraryPath);
48+
}
49+
else
50+
{
51+
LoadUnixLibrary(nativeLibraryPath, RTLD_NOW);
52+
}
5153
}
5254
}
5355
}
5456

5557
InitializeNativeLibrary();
5658
}
5759

60+
private static string GetGlobalSettingsNativeLibraryPath()
61+
{
62+
string nativeLibraryDir = GlobalSettings.GetAndLockNativeLibraryPath();
63+
if (nativeLibraryDir == null)
64+
{
65+
return null;
66+
}
67+
return Path.Combine(nativeLibraryDir, libgit2 + Platform.GetNativeLibraryExtension());
68+
}
69+
70+
private delegate bool TryLoadLibraryByNameDelegate(string libraryName, Assembly assembly, DllImportSearchPath? searchPath, out IntPtr handle);
71+
private delegate bool TryLoadLibraryByPathDelegate(string libraryPath, out IntPtr handle);
72+
73+
static TryLoadLibraryByNameDelegate _tryLoadLibraryByName;
74+
static TryLoadLibraryByPathDelegate _tryLoadLibraryByPath;
75+
76+
static bool TryLoadLibrary(string libraryName, Assembly assembly, DllImportSearchPath? searchPath, out IntPtr handle)
77+
{
78+
if (_tryLoadLibraryByName == null)
79+
{
80+
throw new NotSupportedException();
81+
}
82+
return _tryLoadLibraryByName(libraryName, assembly, searchPath, out handle);
83+
}
84+
85+
static bool TryLoadLibrary(string libraryPath, out IntPtr handle)
86+
{
87+
if (_tryLoadLibraryByPath == null)
88+
{
89+
throw new NotSupportedException();
90+
}
91+
return _tryLoadLibraryByPath(libraryPath, out handle);
92+
}
93+
94+
private static bool TryUseNativeLibrary()
95+
{
96+
// NativeLibrary is available in .NET Core 3.0+.
97+
// We use reflection to use NativeLibrary so this library can target 'netstandard2.0'.
98+
99+
Type dllImportResolverType = Type.GetType("System.Runtime.InteropServices.DllImportResolver, System.Runtime.InteropServices", throwOnError: false);
100+
Type nativeLibraryType = Type.GetType("System.Runtime.InteropServices.NativeLibrary, System.Runtime.InteropServices", throwOnError: false);
101+
var tryLoadLibraryByName = (TryLoadLibraryByNameDelegate)nativeLibraryType?.GetMethod("TryLoad",
102+
new Type[] { typeof(string), typeof(Assembly), typeof(DllImportSearchPath?), typeof(IntPtr).MakeByRefType() })?.CreateDelegate(typeof(TryLoadLibraryByNameDelegate));
103+
var tryLoadLibraryByPath = (TryLoadLibraryByPathDelegate)nativeLibraryType?.GetMethod("TryLoad",
104+
new Type[] { typeof(string), typeof(IntPtr).MakeByRefType() })?.CreateDelegate(typeof(TryLoadLibraryByPathDelegate));
105+
MethodInfo setDllImportResolver = nativeLibraryType?.GetMethod("SetDllImportResolver", new Type[] { typeof(Assembly), dllImportResolverType});
106+
107+
if (dllImportResolverType == null ||
108+
nativeLibraryType == null ||
109+
tryLoadLibraryByName == null ||
110+
tryLoadLibraryByPath == null ||
111+
setDllImportResolver == null)
112+
{
113+
return false;
114+
}
115+
116+
_tryLoadLibraryByPath = tryLoadLibraryByPath;
117+
_tryLoadLibraryByName = tryLoadLibraryByName;
118+
119+
// NativeMethods.SetDllImportResolver(typeof(NativeMethods).Assembly, ResolveDll);
120+
object resolveDelegate = typeof(NativeMethods).GetMethod(nameof(ResolveDll), BindingFlags.NonPublic | BindingFlags.Static).CreateDelegate(dllImportResolverType);
121+
setDllImportResolver.Invoke(null, new object[] { typeof(NativeMethods).Assembly, resolveDelegate });
122+
123+
return true;
124+
}
125+
126+
private static IntPtr ResolveDll(string libraryName, Assembly assembly, DllImportSearchPath? searchPath)
127+
{
128+
IntPtr handle = IntPtr.Zero;
129+
if (libraryName == libgit2)
130+
{
131+
// Use GlobalSettings.NativeLibraryPath when set.
132+
string nativeLibraryPath = GetGlobalSettingsNativeLibraryPath();
133+
if (nativeLibraryPath != null &&
134+
TryLoadLibrary(nativeLibraryPath, out handle))
135+
{
136+
return handle;
137+
}
138+
139+
// Use Default DllImport resolution.
140+
if (TryLoadLibrary(libraryName, assembly, searchPath, out handle))
141+
{
142+
return handle;
143+
}
144+
145+
// We cary a number of .so files for Linux which are linked against various
146+
// libc/OpenSSL libraries. Try them out.
147+
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
148+
{
149+
// The libraries are located at 'runtimes/<rid>/native/lib{libraryName}.so'
150+
// The <rid> ends with the processor architecture. e.g. fedora-x64.
151+
string assemblyDirectory = Path.GetDirectoryName(typeof(NativeMethods).Assembly.Location);
152+
string processorArchitecture = RuntimeInformation.ProcessArchitecture.ToString().ToLowerInvariant();
153+
string runtimesDirectory = Path.Combine(assemblyDirectory, "runtimes");
154+
155+
if (Directory.Exists(runtimesDirectory))
156+
{
157+
foreach (var runtimeFolder in Directory.GetDirectories(runtimesDirectory, $"*-{processorArchitecture}"))
158+
{
159+
string libPath = Path.Combine(runtimeFolder, "native", $"lib{libraryName}.so");
160+
if (TryLoadLibrary(libPath, out handle))
161+
{
162+
return handle;
163+
}
164+
}
165+
}
166+
}
167+
}
168+
return handle;
169+
}
170+
58171
public const int RTLD_NOW = 0x002;
59172

60173
[DllImport("libdl", EntryPoint = "dlopen")]

0 commit comments

Comments
 (0)