From b01c549fa0ee4eb1668fb8caec71154ca6eafddc Mon Sep 17 00:00:00 2001 From: Tomas Matousek Date: Tue, 10 Apr 2018 18:50:28 -0700 Subject: [PATCH 01/14] Avoid reading and writing global state when loading native library --- LibGit2Sharp/Core/NativeMethods.cs | 50 ++++++++++++++++++++---------- LibGit2Sharp/Core/Platform.cs | 12 +++++++ LibGit2Sharp/GlobalSettings.cs | 38 +++++++++++++++-------- 3 files changed, 70 insertions(+), 30 deletions(-) diff --git a/LibGit2Sharp/Core/NativeMethods.cs b/LibGit2Sharp/Core/NativeMethods.cs index ddcb0cdd1..bc1ae8a15 100644 --- a/LibGit2Sharp/Core/NativeMethods.cs +++ b/LibGit2Sharp/Core/NativeMethods.cs @@ -1,10 +1,15 @@ using System; -using System.Globalization; using System.IO; using System.Runtime.CompilerServices; +using System.Runtime.ConstrainedExecution; using System.Runtime.InteropServices; using LibGit2Sharp.Core.Handles; +// Restrict the set of directories where the native library is loaded from to safe directories. +[assembly: DefaultDllImportSearchPaths(DllImportSearchPath.AssemblyDirectory | DllImportSearchPath.ApplicationDirectory | DllImportSearchPath.SafeDirectories)] + +#pragma warning disable IDE1006 // Naming Styles + // ReSharper disable InconsistentNaming namespace LibGit2Sharp.Core { @@ -17,41 +22,52 @@ internal static class NativeMethods // This will handle initialization and shutdown of the underlying // native library. #pragma warning disable 0414 - private static readonly NativeShutdownObject shutdownObject; - #pragma warning restore 0414 + private static NativeShutdownObject shutdownObject; +#pragma warning restore 0414 static NativeMethods() { - if (Platform.OperatingSystem == OperatingSystemType.Windows) + if (Platform.IsRunningOnNetFramework()) { - string nativeLibraryPath = GlobalSettings.GetAndLockNativeLibraryPath(); - - string path = Path.Combine(nativeLibraryPath, Platform.ProcessorArchitecture); + string dllPath = Path.Combine(GlobalSettings.GetAndLockNativeLibraryPath(), libgit2 + ".dll"); - const string pathEnvVariable = "PATH"; - Environment.SetEnvironmentVariable(pathEnvVariable, - String.Format(CultureInfo.InvariantCulture, "{0}{1}{2}", path, Path.PathSeparator, Environment.GetEnvironmentVariable(pathEnvVariable))); + // Try to load the .dll from the path explicitly. + // If this call succeeds further DllImports will find the library loaded and not attempt to load it again. + // If it fails the next DllImport will load the library from safe directories. + LoadWindowsLibrary(dllPath); } - LoadNativeLibrary(); - shutdownObject = new NativeShutdownObject(); + InitializeNativeLibrary(); } + [DllImport("kernel32", EntryPoint = "LoadLibrary")] + private static extern IntPtr LoadWindowsLibrary(string path); + // Avoid inlining this method because otherwise mono's JITter may try // to load the library _before_ we've configured the path. [MethodImpl(MethodImplOptions.NoInlining)] - private static void LoadNativeLibrary() + private static void InitializeNativeLibrary() { - // Configure the OpenSSL locking on the true initialization - // of the library. - if (git_libgit2_init() == 1) + int initCounter; + try + { + } + finally // avoid thread aborts + { + // Initialization can be called multiple times as long as there is a corresponding shutdown to each initialization. + initCounter = git_libgit2_init(); + shutdownObject = new NativeShutdownObject(); + } + + // Configure the OpenSSL locking on the first initialization of the library in the current process. + if (initCounter == 1) { git_openssl_set_locking(); } } // Shutdown the native library in a finalizer. - private sealed class NativeShutdownObject + private sealed class NativeShutdownObject : CriticalFinalizerObject { ~NativeShutdownObject() { diff --git a/LibGit2Sharp/Core/Platform.cs b/LibGit2Sharp/Core/Platform.cs index a07d5172e..8fdbb9b62 100644 --- a/LibGit2Sharp/Core/Platform.cs +++ b/LibGit2Sharp/Core/Platform.cs @@ -36,5 +36,17 @@ public static OperatingSystemType OperatingSystem throw new InvalidOperationException(); } } + + /// + /// Returns true if the runtime is Mono. + /// + public static bool IsRunningOnMono() + => Type.GetType("Mono.Runtime") != null; + + /// + /// Returns true if the runtime is .NET Framework. + /// + public static bool IsRunningOnNetFramework() + => typeof(object).Assembly.GetName().Name == "mscorlib" && !IsRunningOnMono(); } } diff --git a/LibGit2Sharp/GlobalSettings.cs b/LibGit2Sharp/GlobalSettings.cs index c953a7b0e..eaffbedab 100644 --- a/LibGit2Sharp/GlobalSettings.cs +++ b/LibGit2Sharp/GlobalSettings.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.IO; using System.Reflection; +using System.Runtime.InteropServices; using LibGit2Sharp.Core; namespace LibGit2Sharp @@ -13,6 +14,7 @@ public static class GlobalSettings { private static readonly Lazy version = new Lazy(Version.Build); private static readonly Dictionary registeredFilters; + private static readonly bool nativeLibraryPathAllowed; private static LogConfiguration logConfiguration = LogConfiguration.None; @@ -21,7 +23,9 @@ public static class GlobalSettings static GlobalSettings() { - if (Platform.OperatingSystem == OperatingSystemType.Windows) + nativeLibraryPathAllowed = Platform.IsRunningOnNetFramework(); + + if (nativeLibraryPathAllowed) { /* Assembly.CodeBase is not actually a correctly formatted * URI. It's merely prefixed with `file:///` and has its @@ -148,23 +152,24 @@ public static LogConfiguration LogConfiguration } /// - /// Sets a hint path for searching for native binaries: when - /// specified, native binaries will first be searched in a - /// subdirectory of the given path corresponding to the operating - /// system and architecture (eg, "x86" or "x64") before falling - /// back to the default path ("lib\win32\x86" or "lib\win32\x64" - /// next to the application). + /// Sets a path for loading native binaries on .NET Framework. + /// When specified, native .dll will first be searched in a + /// subdirectory of the given path corresponding to the + /// architecture ("x86" or "x64") before falling + /// back to searching the default load directories + /// (, + /// and + /// ). /// /// This must be set before any other calls to the library, - /// and is not available on Unix platforms: see your dynamic - /// library loader's documentation for details. + /// and is not available on other platforms than .NET Framework. /// /// public static string NativeLibraryPath { get { - if (Platform.OperatingSystem != OperatingSystemType.Windows) + if (!nativeLibraryPathAllowed) { throw new LibGit2SharpException("Querying the native hint path is only supported on Windows platforms"); } @@ -174,7 +179,7 @@ public static string NativeLibraryPath set { - if (Platform.OperatingSystem != OperatingSystemType.Windows) + if (!nativeLibraryPathAllowed) { throw new LibGit2SharpException("Setting the native hint path is only supported on Windows platforms"); } @@ -184,14 +189,21 @@ public static string NativeLibraryPath throw new LibGit2SharpException("You cannot set the native library path after it has been loaded"); } - nativeLibraryPath = value; + try + { + nativeLibraryPath = Path.GetFullPath(value); + } + catch (Exception e) + { + throw new LibGit2SharpException(e.Message); + } } } internal static string GetAndLockNativeLibraryPath() { nativeLibraryPathLocked = true; - return nativeLibraryPath; + return Path.Combine(nativeLibraryPath, Platform.ProcessorArchitecture); } /// From 0e2665c1de6d55de8f4fef80c07663776985e2c4 Mon Sep 17 00:00:00 2001 From: Tomas Matousek Date: Wed, 11 Apr 2018 18:07:24 -0700 Subject: [PATCH 02/14] Add test. Sign all assemblies. Add InternalsVisibleTo to the product assembly, so that tests can access internal APIs. --- Directory.Build.props | 2 + .../LibGit2Sharp.TestApp.csproj | 13 ++++++ LibGit2Sharp.TestApp/TestApp.cs | 46 +++++++++++++++++++ LibGit2Sharp.Tests/GlobalSettingsFixture.cs | 35 +++++++++++++- LibGit2Sharp.Tests/LibGit2Sharp.Tests.csproj | 4 +- LibGit2Sharp.Tests/SetErrorFixture.cs | 2 +- .../TestHelpers/ConditionalFactAttribute.cs | 36 +++++++++++++++ .../TestHelpers/ProcessHelper.cs | 36 +++++++++++++++ LibGit2Sharp.sln | 6 +++ LibGit2Sharp/LibGit2Sharp.csproj | 4 +- LibGit2Sharp/Properties/AssemblyInfo.cs | 3 ++ 11 files changed, 179 insertions(+), 8 deletions(-) create mode 100644 LibGit2Sharp.TestApp/LibGit2Sharp.TestApp.csproj create mode 100644 LibGit2Sharp.TestApp/TestApp.cs create mode 100644 LibGit2Sharp.Tests/TestHelpers/ConditionalFactAttribute.cs create mode 100644 LibGit2Sharp.Tests/TestHelpers/ProcessHelper.cs diff --git a/Directory.Build.props b/Directory.Build.props index fb2ca9ca9..26d936e15 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -5,6 +5,8 @@ $(MSBuildThisFileDirectory)bin\$(MSBuildProjectName)\$(Configuration)\ $(MSBuildThisFileDirectory)obj\$(MSBuildProjectName)\ $(DefineConstants);$(ExtraDefine) + true + ..\libgit2sharp.snk diff --git a/LibGit2Sharp.TestApp/LibGit2Sharp.TestApp.csproj b/LibGit2Sharp.TestApp/LibGit2Sharp.TestApp.csproj new file mode 100644 index 000000000..a802df9ae --- /dev/null +++ b/LibGit2Sharp.TestApp/LibGit2Sharp.TestApp.csproj @@ -0,0 +1,13 @@ + + + + Exe + net461 + AnyCPU + + + + + + + diff --git a/LibGit2Sharp.TestApp/TestApp.cs b/LibGit2Sharp.TestApp/TestApp.cs new file mode 100644 index 000000000..234169a75 --- /dev/null +++ b/LibGit2Sharp.TestApp/TestApp.cs @@ -0,0 +1,46 @@ +using System; +using System.IO; +using System.Runtime.InteropServices; +using System.Text; + +namespace LibGit2Sharp.Tests +{ + public class TestApp + { + [DllImport("kernel32")] + private static extern IntPtr GetModuleHandle(string path); + + [DllImport("kernel32")] + private static extern int GetModuleFileName(IntPtr handle, [Out]StringBuilder path, int size); + + static int Main(string[] args) + { + if (args.Length < 1 || args.Length > 2) + { + Console.Error.WriteLine("Usage: "); + return -1; + } + + var moduleName = args[0]; + var loadFromDirectory = args[1]; + var expectedPath = Path.Combine(loadFromDirectory, (IntPtr.Size == 4) ? "x86" : "x64", moduleName + ".dll"); + + GlobalSettings.NativeLibraryPath = loadFromDirectory; + var isValid = Repository.IsValid(Path.GetTempPath()); + + var capacity = ushort.MaxValue; + var moduleHandle = GetModuleHandle(moduleName); + var buffer = new StringBuilder(capacity); + int actualLength = GetModuleFileName(moduleHandle, buffer, capacity); + var actualPath = buffer.ToString(0, actualLength); + + if (expectedPath != actualPath) + { + Console.WriteLine(actualPath); + return 1; + } + + return 0; + } + } +} diff --git a/LibGit2Sharp.Tests/GlobalSettingsFixture.cs b/LibGit2Sharp.Tests/GlobalSettingsFixture.cs index 76b2c2ad3..9df459ddf 100644 --- a/LibGit2Sharp.Tests/GlobalSettingsFixture.cs +++ b/LibGit2Sharp.Tests/GlobalSettingsFixture.cs @@ -1,4 +1,7 @@ -using System.Text.RegularExpressions; +using System; +using System.IO; +using System.Text.RegularExpressions; +using LibGit2Sharp.Core; using LibGit2Sharp.Tests.TestHelpers; using Xunit; @@ -49,5 +52,35 @@ public void TryingToResetNativeLibraryPathAfterLoadedThrows() Assert.Throws(() => { GlobalSettings.NativeLibraryPath = "C:/Foo"; }); } + + [ConditionalFact(typeof(NetFramework))] + public void LoadFromSpecifiedPath() + { +#if NET461 + var nativeDllFileName = NativeDllName.Name + ".dll"; + + var testAppExe = typeof(TestApp).Assembly.Location; + var tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + var platformDir = Path.Combine(tempDir, "plat"); + + try + { + Directory.CreateDirectory(Path.Combine(platformDir, "x86")); + Directory.CreateDirectory(Path.Combine(platformDir, "x64")); + + File.Copy(Path.Combine(GlobalSettings.NativeLibraryPath, "x86", nativeDllFileName), Path.Combine(platformDir, "x86", nativeDllFileName)); + File.Copy(Path.Combine(GlobalSettings.NativeLibraryPath, "x64", nativeDllFileName), Path.Combine(platformDir, "x64", nativeDllFileName)); + + var (output, exitCode) = ProcessHelper.RunProcess(testAppExe, arguments: $@"{NativeDllName.Name} ""{platformDir}""", workingDirectory: tempDir); + + Assert.Empty(output); + Assert.Equal(0, exitCode); + } + finally + { + DirectoryHelper.DeleteDirectory(tempDir); + } +#endif + } } } diff --git a/LibGit2Sharp.Tests/LibGit2Sharp.Tests.csproj b/LibGit2Sharp.Tests/LibGit2Sharp.Tests.csproj index 29cc60403..7b0c17e19 100644 --- a/LibGit2Sharp.Tests/LibGit2Sharp.Tests.csproj +++ b/LibGit2Sharp.Tests/LibGit2Sharp.Tests.csproj @@ -2,11 +2,11 @@ net461;netcoreapp2.0 - $(DefineConstants);DESKTOP + @@ -19,8 +19,6 @@ - - diff --git a/LibGit2Sharp.Tests/SetErrorFixture.cs b/LibGit2Sharp.Tests/SetErrorFixture.cs index 31b7513a2..609e0f77c 100644 --- a/LibGit2Sharp.Tests/SetErrorFixture.cs +++ b/LibGit2Sharp.Tests/SetErrorFixture.cs @@ -49,7 +49,7 @@ public void FormatAggregateException() Exception exceptionToThrow = new AggregateException(aggregateExceptionMessage, new Exception(innerExceptionMessage), new Exception(innerExceptionMessage2)); StringBuilder sb = new StringBuilder(); -#if DESKTOP +#if NET461 sb.AppendLine(aggregateExceptionMessage); #else sb.AppendLine($"{aggregateExceptionMessage} ({innerExceptionMessage}) ({innerExceptionMessage2})"); diff --git a/LibGit2Sharp.Tests/TestHelpers/ConditionalFactAttribute.cs b/LibGit2Sharp.Tests/TestHelpers/ConditionalFactAttribute.cs new file mode 100644 index 000000000..2e97ecaaf --- /dev/null +++ b/LibGit2Sharp.Tests/TestHelpers/ConditionalFactAttribute.cs @@ -0,0 +1,36 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using LibGit2Sharp.Core; +using Xunit; + +namespace LibGit2Sharp.Tests +{ + public class ConditionalFactAttribute : FactAttribute + { + public ConditionalFactAttribute(params Type[] skipConditions) + { + foreach (var skipCondition in skipConditions) + { + ExecutionCondition condition = (ExecutionCondition)Activator.CreateInstance(skipCondition); + if (condition.ShouldSkip) + { + Skip = condition.SkipReason; + break; + } + } + } + } + + public abstract class ExecutionCondition + { + public abstract bool ShouldSkip { get; } + public abstract string SkipReason { get; } + } + + public class NetFramework : ExecutionCondition + { + public override bool ShouldSkip => !Platform.IsRunningOnNetFramework(); + public override string SkipReason => ".NET Framework only test"; + } +} diff --git a/LibGit2Sharp.Tests/TestHelpers/ProcessHelper.cs b/LibGit2Sharp.Tests/TestHelpers/ProcessHelper.cs new file mode 100644 index 000000000..7c2855528 --- /dev/null +++ b/LibGit2Sharp.Tests/TestHelpers/ProcessHelper.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Text; + +namespace LibGit2Sharp.Tests +{ + public static class ProcessHelper + { + public static (string, int) RunProcess(string fileName, string arguments, string workingDirectory = null) + { + var process = new Process + { + StartInfo = new ProcessStartInfo(fileName, arguments) + { + RedirectStandardError = true, + RedirectStandardOutput = true, + CreateNoWindow = true, + UseShellExecute = false, + WorkingDirectory = workingDirectory ?? string.Empty + } + }; + + var output = new StringBuilder(); + + process.OutputDataReceived += (_, e) => output.AppendLine(e.Data); + process.ErrorDataReceived += (_, e) => output.AppendLine(e.Data); + + process.Start(); + + process.WaitForExit(); + + return (output.ToString(), process.ExitCode); + } + } +} diff --git a/LibGit2Sharp.sln b/LibGit2Sharp.sln index fb8a7101b..b9ecd1f45 100644 --- a/LibGit2Sharp.sln +++ b/LibGit2Sharp.sln @@ -15,6 +15,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution version.json = version.json EndProjectSection EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LibGit2Sharp.TestApp", "LibGit2Sharp.TestApp\LibGit2Sharp.TestApp.csproj", "{86453D2C-4953-4DF4-B12A-ADE579608BAA}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -29,6 +31,10 @@ Global {286E63EB-04DD-4ADE-88D6-041B57800761}.Debug|Any CPU.Build.0 = Debug|Any CPU {286E63EB-04DD-4ADE-88D6-041B57800761}.Release|Any CPU.ActiveCfg = Release|Any CPU {286E63EB-04DD-4ADE-88D6-041B57800761}.Release|Any CPU.Build.0 = Release|Any CPU + {86453D2C-4953-4DF4-B12A-ADE579608BAA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {86453D2C-4953-4DF4-B12A-ADE579608BAA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {86453D2C-4953-4DF4-B12A-ADE579608BAA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {86453D2C-4953-4DF4-B12A-ADE579608BAA}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/LibGit2Sharp/LibGit2Sharp.csproj b/LibGit2Sharp/LibGit2Sharp.csproj index fc15f0e2f..f161ddddb 100644 --- a/LibGit2Sharp/LibGit2Sharp.csproj +++ b/LibGit2Sharp/LibGit2Sharp.csproj @@ -9,9 +9,7 @@ libgit2 git https://github.com/libgit2/libgit2sharp/ LibGit2Sharp contributors - $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb - true - ..\libgit2sharp.snk + $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb diff --git a/LibGit2Sharp/Properties/AssemblyInfo.cs b/LibGit2Sharp/Properties/AssemblyInfo.cs index ffa977d1d..ccbbf7b5f 100644 --- a/LibGit2Sharp/Properties/AssemblyInfo.cs +++ b/LibGit2Sharp/Properties/AssemblyInfo.cs @@ -1,4 +1,5 @@ using System; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; // General Information about an assembly is controlled through the following @@ -16,3 +17,5 @@ // The following GUID is for the ID of the typelib if this project is exposed to COM [assembly: Guid("c6f71967-5be1-49f5-b48e-861bff498ea3")] + +[assembly: InternalsVisibleTo("LibGit2Sharp.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010013172CAC3D61EF825164EF8443ED2F97316D0C2A4A65D3B2F6E5C9175C6C589D6A0EAE803E3E7FC0DA9E6672B1DE036CF74E1D33E21DD83E1145E3A454F92E52107495082DCCD1D9F521592F79F41DF26ED727059F8A4E5D3C23ECC525306831A15F1E56B693FDE112137E973B599A13209A5B63E05EE00886DE594E70A993B5")] From 8a10035c2cbd5ad635386c8675c2489f624f882d Mon Sep 17 00:00:00 2001 From: Tomas Matousek Date: Wed, 11 Apr 2018 19:06:25 -0700 Subject: [PATCH 03/14] Make test types internal --- LibGit2Sharp.Tests/TestHelpers/ConditionalFactAttribute.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/LibGit2Sharp.Tests/TestHelpers/ConditionalFactAttribute.cs b/LibGit2Sharp.Tests/TestHelpers/ConditionalFactAttribute.cs index 2e97ecaaf..cc052a4b8 100644 --- a/LibGit2Sharp.Tests/TestHelpers/ConditionalFactAttribute.cs +++ b/LibGit2Sharp.Tests/TestHelpers/ConditionalFactAttribute.cs @@ -6,7 +6,7 @@ namespace LibGit2Sharp.Tests { - public class ConditionalFactAttribute : FactAttribute + internal class ConditionalFactAttribute : FactAttribute { public ConditionalFactAttribute(params Type[] skipConditions) { @@ -22,13 +22,13 @@ public ConditionalFactAttribute(params Type[] skipConditions) } } - public abstract class ExecutionCondition + internal abstract class ExecutionCondition { public abstract bool ShouldSkip { get; } public abstract string SkipReason { get; } } - public class NetFramework : ExecutionCondition + internal class NetFramework : ExecutionCondition { public override bool ShouldSkip => !Platform.IsRunningOnNetFramework(); public override string SkipReason => ".NET Framework only test"; From 748e5968ab804244acafe127cfe8a05fc89233f0 Mon Sep 17 00:00:00 2001 From: Tomas Matousek Date: Thu, 12 Apr 2018 15:43:39 -0700 Subject: [PATCH 04/14] PR feedback and test both x64 and x86 platforms --- Directory.Build.props | 2 +- .../LibGit2Sharp.TestApp.x64.csproj} | 8 +++-- .../x86/LibGit2Sharp.TestApp.x86.csproj | 17 +++++++++ LibGit2Sharp.Tests/GlobalSettingsFixture.cs | 21 ++++++----- LibGit2Sharp.Tests/LibGit2Sharp.Tests.csproj | 20 +++++++++-- LibGit2Sharp.Tests/SetErrorFixture.cs | 2 +- .../TestHelpers/ConditionalFactAttribute.cs | 36 ------------------- LibGit2Sharp.sln | 8 ++++- 8 files changed, 59 insertions(+), 55 deletions(-) rename LibGit2Sharp.TestApp/{LibGit2Sharp.TestApp.csproj => x64/LibGit2Sharp.TestApp.x64.csproj} (52%) create mode 100644 LibGit2Sharp.TestApp/x86/LibGit2Sharp.TestApp.x86.csproj delete mode 100644 LibGit2Sharp.Tests/TestHelpers/ConditionalFactAttribute.cs diff --git a/Directory.Build.props b/Directory.Build.props index 26d936e15..5402df162 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -6,7 +6,7 @@ $(MSBuildThisFileDirectory)obj\$(MSBuildProjectName)\ $(DefineConstants);$(ExtraDefine) true - ..\libgit2sharp.snk + $(MSBuildThisFileDirectory)libgit2sharp.snk diff --git a/LibGit2Sharp.TestApp/LibGit2Sharp.TestApp.csproj b/LibGit2Sharp.TestApp/x64/LibGit2Sharp.TestApp.x64.csproj similarity index 52% rename from LibGit2Sharp.TestApp/LibGit2Sharp.TestApp.csproj rename to LibGit2Sharp.TestApp/x64/LibGit2Sharp.TestApp.x64.csproj index a802df9ae..a3c313a59 100644 --- a/LibGit2Sharp.TestApp/LibGit2Sharp.TestApp.csproj +++ b/LibGit2Sharp.TestApp/x64/LibGit2Sharp.TestApp.x64.csproj @@ -3,11 +3,15 @@ Exe net461 - AnyCPU + x64 - + + + + + diff --git a/LibGit2Sharp.TestApp/x86/LibGit2Sharp.TestApp.x86.csproj b/LibGit2Sharp.TestApp/x86/LibGit2Sharp.TestApp.x86.csproj new file mode 100644 index 000000000..daaf8f51f --- /dev/null +++ b/LibGit2Sharp.TestApp/x86/LibGit2Sharp.TestApp.x86.csproj @@ -0,0 +1,17 @@ + + + + Exe + net461 + x86 + + + + + + + + + + + diff --git a/LibGit2Sharp.Tests/GlobalSettingsFixture.cs b/LibGit2Sharp.Tests/GlobalSettingsFixture.cs index 9df459ddf..2bd350eb3 100644 --- a/LibGit2Sharp.Tests/GlobalSettingsFixture.cs +++ b/LibGit2Sharp.Tests/GlobalSettingsFixture.cs @@ -53,23 +53,23 @@ public void TryingToResetNativeLibraryPathAfterLoadedThrows() Assert.Throws(() => { GlobalSettings.NativeLibraryPath = "C:/Foo"; }); } - [ConditionalFact(typeof(NetFramework))] - public void LoadFromSpecifiedPath() + [SkippableTheory] + [InlineData(new object[] { "x86" })] + [InlineData(new object[] { "x64" })] + public void LoadFromSpecifiedPath(string platform) { -#if NET461 - var nativeDllFileName = NativeDllName.Name + ".dll"; + Skip.IfNot(Platform.IsRunningOnNetFramework(), ".NET Framework only test."); - var testAppExe = typeof(TestApp).Assembly.Location; + var nativeDllFileName = NativeDllName.Name + ".dll"; + var testDir = Path.GetDirectoryName(typeof(GlobalSettingsFixture).Assembly.Location); + var testAppExe = Path.Combine(testDir, $"LibGit2Sharp.TestApp.{platform}.exe"); var tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); var platformDir = Path.Combine(tempDir, "plat"); try { - Directory.CreateDirectory(Path.Combine(platformDir, "x86")); - Directory.CreateDirectory(Path.Combine(platformDir, "x64")); - - File.Copy(Path.Combine(GlobalSettings.NativeLibraryPath, "x86", nativeDllFileName), Path.Combine(platformDir, "x86", nativeDllFileName)); - File.Copy(Path.Combine(GlobalSettings.NativeLibraryPath, "x64", nativeDllFileName), Path.Combine(platformDir, "x64", nativeDllFileName)); + Directory.CreateDirectory(Path.Combine(platformDir, platform)); + File.Copy(Path.Combine(GlobalSettings.NativeLibraryPath, platform, nativeDllFileName), Path.Combine(platformDir, platform, nativeDllFileName)); var (output, exitCode) = ProcessHelper.RunProcess(testAppExe, arguments: $@"{NativeDllName.Name} ""{platformDir}""", workingDirectory: tempDir); @@ -80,7 +80,6 @@ public void LoadFromSpecifiedPath() { DirectoryHelper.DeleteDirectory(tempDir); } -#endif } } } diff --git a/LibGit2Sharp.Tests/LibGit2Sharp.Tests.csproj b/LibGit2Sharp.Tests/LibGit2Sharp.Tests.csproj index 7b0c17e19..5e5d610a6 100644 --- a/LibGit2Sharp.Tests/LibGit2Sharp.Tests.csproj +++ b/LibGit2Sharp.Tests/LibGit2Sharp.Tests.csproj @@ -2,11 +2,13 @@ net461;netcoreapp2.0 + $(DefineConstants);DESKTOP - + + @@ -18,8 +20,20 @@ - - + + + <_TestAppFile Include="@(TestAppExe->'%(RootDir)%(Directory)%(Filename).exe')" /> + <_TestAppFile Include="@(TestAppExe->'%(RootDir)%(Directory)%(Filename).exe.config')" /> + <_TestAppFile Include="@(TestAppExe->'%(RootDir)%(Directory)%(Filename).pdb')" /> + + + + + + + + + diff --git a/LibGit2Sharp.Tests/SetErrorFixture.cs b/LibGit2Sharp.Tests/SetErrorFixture.cs index 609e0f77c..31b7513a2 100644 --- a/LibGit2Sharp.Tests/SetErrorFixture.cs +++ b/LibGit2Sharp.Tests/SetErrorFixture.cs @@ -49,7 +49,7 @@ public void FormatAggregateException() Exception exceptionToThrow = new AggregateException(aggregateExceptionMessage, new Exception(innerExceptionMessage), new Exception(innerExceptionMessage2)); StringBuilder sb = new StringBuilder(); -#if NET461 +#if DESKTOP sb.AppendLine(aggregateExceptionMessage); #else sb.AppendLine($"{aggregateExceptionMessage} ({innerExceptionMessage}) ({innerExceptionMessage2})"); diff --git a/LibGit2Sharp.Tests/TestHelpers/ConditionalFactAttribute.cs b/LibGit2Sharp.Tests/TestHelpers/ConditionalFactAttribute.cs deleted file mode 100644 index cc052a4b8..000000000 --- a/LibGit2Sharp.Tests/TestHelpers/ConditionalFactAttribute.cs +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using LibGit2Sharp.Core; -using Xunit; - -namespace LibGit2Sharp.Tests -{ - internal class ConditionalFactAttribute : FactAttribute - { - public ConditionalFactAttribute(params Type[] skipConditions) - { - foreach (var skipCondition in skipConditions) - { - ExecutionCondition condition = (ExecutionCondition)Activator.CreateInstance(skipCondition); - if (condition.ShouldSkip) - { - Skip = condition.SkipReason; - break; - } - } - } - } - - internal abstract class ExecutionCondition - { - public abstract bool ShouldSkip { get; } - public abstract string SkipReason { get; } - } - - internal class NetFramework : ExecutionCondition - { - public override bool ShouldSkip => !Platform.IsRunningOnNetFramework(); - public override string SkipReason => ".NET Framework only test"; - } -} diff --git a/LibGit2Sharp.sln b/LibGit2Sharp.sln index b9ecd1f45..ed3dd1b56 100644 --- a/LibGit2Sharp.sln +++ b/LibGit2Sharp.sln @@ -15,7 +15,9 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution version.json = version.json EndProjectSection EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LibGit2Sharp.TestApp", "LibGit2Sharp.TestApp\LibGit2Sharp.TestApp.csproj", "{86453D2C-4953-4DF4-B12A-ADE579608BAA}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LibGit2Sharp.TestApp.x86", "LibGit2Sharp.TestApp\x86\LibGit2Sharp.TestApp.x86.csproj", "{86453D2C-4953-4DF4-B12A-ADE579608BAA}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LibGit2Sharp.TestApp.x64", "LibGit2Sharp.TestApp\x64\LibGit2Sharp.TestApp.x64.csproj", "{5C55175D-6A1F-4C51-B791-BF7DD00124EE}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -35,6 +37,10 @@ Global {86453D2C-4953-4DF4-B12A-ADE579608BAA}.Debug|Any CPU.Build.0 = Debug|Any CPU {86453D2C-4953-4DF4-B12A-ADE579608BAA}.Release|Any CPU.ActiveCfg = Release|Any CPU {86453D2C-4953-4DF4-B12A-ADE579608BAA}.Release|Any CPU.Build.0 = Release|Any CPU + {5C55175D-6A1F-4C51-B791-BF7DD00124EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5C55175D-6A1F-4C51-B791-BF7DD00124EE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5C55175D-6A1F-4C51-B791-BF7DD00124EE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5C55175D-6A1F-4C51-B791-BF7DD00124EE}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE From 671d0589465a92389f7cb1d84bee2231c15f5445 Mon Sep 17 00:00:00 2001 From: Tomas Matousek Date: Thu, 12 Apr 2018 15:44:38 -0700 Subject: [PATCH 05/14] Rename platform to architecture --- LibGit2Sharp.Tests/GlobalSettingsFixture.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/LibGit2Sharp.Tests/GlobalSettingsFixture.cs b/LibGit2Sharp.Tests/GlobalSettingsFixture.cs index 2bd350eb3..65b897f7f 100644 --- a/LibGit2Sharp.Tests/GlobalSettingsFixture.cs +++ b/LibGit2Sharp.Tests/GlobalSettingsFixture.cs @@ -54,22 +54,22 @@ public void TryingToResetNativeLibraryPathAfterLoadedThrows() } [SkippableTheory] - [InlineData(new object[] { "x86" })] - [InlineData(new object[] { "x64" })] - public void LoadFromSpecifiedPath(string platform) + [InlineData("x86")] + [InlineData("x64")] + public void LoadFromSpecifiedPath(string architecture) { Skip.IfNot(Platform.IsRunningOnNetFramework(), ".NET Framework only test."); var nativeDllFileName = NativeDllName.Name + ".dll"; var testDir = Path.GetDirectoryName(typeof(GlobalSettingsFixture).Assembly.Location); - var testAppExe = Path.Combine(testDir, $"LibGit2Sharp.TestApp.{platform}.exe"); + var testAppExe = Path.Combine(testDir, $"LibGit2Sharp.TestApp.{architecture}.exe"); var tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); var platformDir = Path.Combine(tempDir, "plat"); try { - Directory.CreateDirectory(Path.Combine(platformDir, platform)); - File.Copy(Path.Combine(GlobalSettings.NativeLibraryPath, platform, nativeDllFileName), Path.Combine(platformDir, platform, nativeDllFileName)); + Directory.CreateDirectory(Path.Combine(platformDir, architecture)); + File.Copy(Path.Combine(GlobalSettings.NativeLibraryPath, architecture, nativeDllFileName), Path.Combine(platformDir, architecture, nativeDllFileName)); var (output, exitCode) = ProcessHelper.RunProcess(testAppExe, arguments: $@"{NativeDllName.Name} ""{platformDir}""", workingDirectory: tempDir); From 2c1e96d323b36ea48359c14a99d516342bd1726a Mon Sep 17 00:00:00 2001 From: Tomas Matousek Date: Fri, 13 Apr 2018 19:16:25 -0700 Subject: [PATCH 06/14] Do not create packages for test app projects --- LibGit2Sharp.TestApp/Directory.Build.props | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 LibGit2Sharp.TestApp/Directory.Build.props diff --git a/LibGit2Sharp.TestApp/Directory.Build.props b/LibGit2Sharp.TestApp/Directory.Build.props new file mode 100644 index 000000000..c55b35c72 --- /dev/null +++ b/LibGit2Sharp.TestApp/Directory.Build.props @@ -0,0 +1,7 @@ + + + + false + + + From fc7b5b34daa0ade8f04163e2ed140f95fb080aa9 Mon Sep 17 00:00:00 2001 From: Tomas Matousek Date: Fri, 13 Apr 2018 19:17:59 -0700 Subject: [PATCH 07/14] Search for native library in runtimes dir and allow setting NativeLibraryPath on .NET Core. --- LibGit2Sharp/Core/NativeMethods.cs | 18 +++++-- LibGit2Sharp/Core/Platform.cs | 46 +++++++++++++++- LibGit2Sharp/GlobalSettings.cs | 85 +++++++++++++++++++----------- 3 files changed, 115 insertions(+), 34 deletions(-) diff --git a/LibGit2Sharp/Core/NativeMethods.cs b/LibGit2Sharp/Core/NativeMethods.cs index bc1ae8a15..4c54388b7 100644 --- a/LibGit2Sharp/Core/NativeMethods.cs +++ b/LibGit2Sharp/Core/NativeMethods.cs @@ -27,19 +27,31 @@ internal static class NativeMethods static NativeMethods() { - if (Platform.IsRunningOnNetFramework()) + if (Platform.IsRunningOnNetFramework() || Platform.IsRunningOnNetCore()) { - string dllPath = Path.Combine(GlobalSettings.GetAndLockNativeLibraryPath(), libgit2 + ".dll"); + string dllPath = Path.Combine(GlobalSettings.GetAndLockNativeLibraryPath(), libgit2 + Platform.GetNativeLibraryExtension()); // Try to load the .dll from the path explicitly. // If this call succeeds further DllImports will find the library loaded and not attempt to load it again. // If it fails the next DllImport will load the library from safe directories. - LoadWindowsLibrary(dllPath); + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + LoadWindowsLibrary(dllPath); + } + else + { + LoadUnixLibrary(dllPath, RTLD_NOW); + } } InitializeNativeLibrary(); } + public const int RTLD_NOW = 0x002; + + [DllImport("libdl", EntryPoint = "dlopen")] + private static extern IntPtr LoadUnixLibrary(string path, int flags); + [DllImport("kernel32", EntryPoint = "LoadLibrary")] private static extern IntPtr LoadWindowsLibrary(string path); diff --git a/LibGit2Sharp/Core/Platform.cs b/LibGit2Sharp/Core/Platform.cs index 8fdbb9b62..53475bec0 100644 --- a/LibGit2Sharp/Core/Platform.cs +++ b/LibGit2Sharp/Core/Platform.cs @@ -33,10 +33,48 @@ public static OperatingSystemType OperatingSystem return OperatingSystemType.MacOSX; } - throw new InvalidOperationException(); + throw new PlatformNotSupportedException(); } } + /// + /// Determines the RID to use when loading libgit2 native library. + /// This method only supports RIDs that are currently used by LibGit2Sharp.NativeBinaries. + /// + public static string GetNativeLibraryRuntimeId() + { + switch (OperatingSystem) + { + case OperatingSystemType.MacOSX: + return "osx"; + + case OperatingSystemType.Unix: + return "linux-" + ProcessorArchitecture; + + case OperatingSystemType.Windows: + return "win7-" + ProcessorArchitecture; + } + + throw new PlatformNotSupportedException(); + } + + public static string GetNativeLibraryExtension() + { + switch (OperatingSystem) + { + case OperatingSystemType.MacOSX: + return ".dylib"; + + case OperatingSystemType.Unix: + return ".so"; + + case OperatingSystemType.Windows: + return ".dll"; + } + + throw new PlatformNotSupportedException(); + } + /// /// Returns true if the runtime is Mono. /// @@ -48,5 +86,11 @@ public static bool IsRunningOnMono() /// public static bool IsRunningOnNetFramework() => typeof(object).Assembly.GetName().Name == "mscorlib" && !IsRunningOnMono(); + + /// + /// Returns true if the runtime is .NET Core. + /// + public static bool IsRunningOnNetCore() + => typeof(object).Assembly.GetName().Name != "mscorlib"; } } diff --git a/LibGit2Sharp/GlobalSettings.cs b/LibGit2Sharp/GlobalSettings.cs index eaffbedab..5378a6829 100644 --- a/LibGit2Sharp/GlobalSettings.cs +++ b/LibGit2Sharp/GlobalSettings.cs @@ -23,36 +23,57 @@ public static class GlobalSettings static GlobalSettings() { - nativeLibraryPathAllowed = Platform.IsRunningOnNetFramework(); + bool netFX = Platform.IsRunningOnNetFramework(); + bool netCore = Platform.IsRunningOnNetCore(); + + nativeLibraryPathAllowed = netFX || netCore; if (nativeLibraryPathAllowed) { - /* Assembly.CodeBase is not actually a correctly formatted - * URI. It's merely prefixed with `file:///` and has its - * backslashes flipped. This is superior to EscapedCodeBase, - * which does not correctly escape things, and ambiguates a - * space (%20) with a literal `%20` in the path. Sigh. - */ - var managedPath = Assembly.GetExecutingAssembly().CodeBase; - if (managedPath == null) - { - managedPath = Assembly.GetExecutingAssembly().Location; - } - else if (managedPath.StartsWith("file:///")) + string assemblyDirectory = GetExecutingAssemblyDirectory(); + + if (netFX) { - managedPath = managedPath.Substring(8).Replace('/', '\\'); + // For .NET Framework apps the dependencies are deployed to lib/win32/{architecture} directory + nativeLibraryPath = Path.Combine(assemblyDirectory, "lib", "win32"); } - else if (managedPath.StartsWith("file://")) + else { - managedPath = @"\\" + managedPath.Substring(7).Replace('/', '\\'); + // .NET Core apps that depend on native libraries load them directly from paths specified + // in .deps.json file of that app and the native library loader just works. + // However, .NET Core doesn't support .deps.json for plugins yet (such as msbuild tasks). + // To address that shortcoming we assume that the plugin deploys the native binaries to runtimes\{rid}\native + // directories and search there. + nativeLibraryPath = Path.Combine(assemblyDirectory, "runtimes", Platform.GetNativeLibraryRuntimeId(), "native"); } + } - managedPath = Path.GetDirectoryName(managedPath); + registeredFilters = new Dictionary(); + } - nativeLibraryPath = Path.Combine(managedPath, "lib", "win32"); + private static string GetExecutingAssemblyDirectory() + { + // Assembly.CodeBase is not actually a correctly formatted + // URI. It's merely prefixed with `file:///` and has its + // backslashes flipped. This is superior to EscapedCodeBase, + // which does not correctly escape things, and ambiguates a + // space (%20) with a literal `%20` in the path. Sigh. + var managedPath = Assembly.GetExecutingAssembly().CodeBase; + if (managedPath == null) + { + managedPath = Assembly.GetExecutingAssembly().Location; + } + else if (managedPath.StartsWith("file:///")) + { + managedPath = managedPath.Substring(8).Replace('/', '\\'); + } + else if (managedPath.StartsWith("file://")) + { + managedPath = @"\\" + managedPath.Substring(7).Replace('/', '\\'); } - registeredFilters = new Dictionary(); + managedPath = Path.GetDirectoryName(managedPath); + return managedPath; } /// @@ -152,17 +173,18 @@ public static LogConfiguration LogConfiguration } /// - /// Sets a path for loading native binaries on .NET Framework. - /// When specified, native .dll will first be searched in a - /// subdirectory of the given path corresponding to the - /// architecture ("x86" or "x64") before falling - /// back to searching the default load directories - /// (, + /// Sets a path for loading native binaries on .NET Framework or .NET Core. + /// When specified, native library will first be searched under the given path. + /// On .NET Framework a subdirectory corresponding to the architecture ("x86" or "x64") is appended, + /// otherwise the native library is expected to be found in the directory as specified. + /// + /// If the library is not found it will be searched in standard search paths: + /// , /// and - /// ). + /// . /// /// This must be set before any other calls to the library, - /// and is not available on other platforms than .NET Framework. + /// and is not available on other platforms than .NET Framework and .NET Core. /// /// public static string NativeLibraryPath @@ -171,7 +193,7 @@ public static string NativeLibraryPath { if (!nativeLibraryPathAllowed) { - throw new LibGit2SharpException("Querying the native hint path is only supported on Windows platforms"); + throw new LibGit2SharpException("Querying the native hint path is only supported on .NET Framework and .NET Core platforms"); } return nativeLibraryPath; @@ -181,7 +203,7 @@ public static string NativeLibraryPath { if (!nativeLibraryPathAllowed) { - throw new LibGit2SharpException("Setting the native hint path is only supported on Windows platforms"); + throw new LibGit2SharpException("Setting the native hint path is only supported on .NET Framework and .NET Core platforms"); } if (nativeLibraryPathLocked) @@ -203,7 +225,10 @@ public static string NativeLibraryPath internal static string GetAndLockNativeLibraryPath() { nativeLibraryPathLocked = true; - return Path.Combine(nativeLibraryPath, Platform.ProcessorArchitecture); + + return Platform.IsRunningOnNetFramework() ? + Path.Combine(nativeLibraryPath, Platform.ProcessorArchitecture) : + nativeLibraryPath; } /// From fe71caa5b87adb09184f9233a2f94b99f36ebce2 Mon Sep 17 00:00:00 2001 From: Tomas Matousek Date: Mon, 16 Apr 2018 09:59:30 -0700 Subject: [PATCH 08/14] Avoid IVT --- Directory.Build.props | 2 -- LibGit2Sharp.Tests/LibGit2Sharp.Tests.csproj | 12 +++++++----- LibGit2Sharp/LibGit2Sharp.csproj | 4 +++- LibGit2Sharp/Properties/AssemblyInfo.cs | 3 --- 4 files changed, 10 insertions(+), 11 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index 5402df162..fb2ca9ca9 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -5,8 +5,6 @@ $(MSBuildThisFileDirectory)bin\$(MSBuildProjectName)\$(Configuration)\ $(MSBuildThisFileDirectory)obj\$(MSBuildProjectName)\ $(DefineConstants);$(ExtraDefine) - true - $(MSBuildThisFileDirectory)libgit2sharp.snk diff --git a/LibGit2Sharp.Tests/LibGit2Sharp.Tests.csproj b/LibGit2Sharp.Tests/LibGit2Sharp.Tests.csproj index 5e5d610a6..5eacddd31 100644 --- a/LibGit2Sharp.Tests/LibGit2Sharp.Tests.csproj +++ b/LibGit2Sharp.Tests/LibGit2Sharp.Tests.csproj @@ -20,20 +20,22 @@ + + + + + + <_TestAppFile Include="@(TestAppExe->'%(RootDir)%(Directory)%(Filename).exe')" /> <_TestAppFile Include="@(TestAppExe->'%(RootDir)%(Directory)%(Filename).exe.config')" /> <_TestAppFile Include="@(TestAppExe->'%(RootDir)%(Directory)%(Filename).pdb')" /> - + - - - - diff --git a/LibGit2Sharp/LibGit2Sharp.csproj b/LibGit2Sharp/LibGit2Sharp.csproj index f161ddddb..fc15f0e2f 100644 --- a/LibGit2Sharp/LibGit2Sharp.csproj +++ b/LibGit2Sharp/LibGit2Sharp.csproj @@ -9,7 +9,9 @@ libgit2 git https://github.com/libgit2/libgit2sharp/ LibGit2Sharp contributors - $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb + $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb + true + ..\libgit2sharp.snk diff --git a/LibGit2Sharp/Properties/AssemblyInfo.cs b/LibGit2Sharp/Properties/AssemblyInfo.cs index ccbbf7b5f..ffa977d1d 100644 --- a/LibGit2Sharp/Properties/AssemblyInfo.cs +++ b/LibGit2Sharp/Properties/AssemblyInfo.cs @@ -1,5 +1,4 @@ using System; -using System.Runtime.CompilerServices; using System.Runtime.InteropServices; // General Information about an assembly is controlled through the following @@ -17,5 +16,3 @@ // The following GUID is for the ID of the typelib if this project is exposed to COM [assembly: Guid("c6f71967-5be1-49f5-b48e-861bff498ea3")] - -[assembly: InternalsVisibleTo("LibGit2Sharp.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010013172CAC3D61EF825164EF8443ED2F97316D0C2A4A65D3B2F6E5C9175C6C589D6A0EAE803E3E7FC0DA9E6672B1DE036CF74E1D33E21DD83E1145E3A454F92E52107495082DCCD1D9F521592F79F41DF26ED727059F8A4E5D3C23ECC525306831A15F1E56B693FDE112137E973B599A13209A5B63E05EE00886DE594E70A993B5")] From 9aadf0bcae8b73107cb91c219a64d2378ccb3ec1 Mon Sep 17 00:00:00 2001 From: Tomas Matousek Date: Mon, 16 Apr 2018 10:13:47 -0700 Subject: [PATCH 09/14] Generate NativeDllName in tests --- LibGit2Sharp.Tests/LibGit2Sharp.Tests.csproj | 2 ++ LibGit2Sharp/LibGit2Sharp.csproj | 3 +- .../CodeGenerator.targets | 24 -------------- Targets/GenerateNativeDllName.targets | 33 +++++++++++++++++++ 4 files changed, 37 insertions(+), 25 deletions(-) rename {LibGit2Sharp => Targets}/CodeGenerator.targets (75%) create mode 100644 Targets/GenerateNativeDllName.targets diff --git a/LibGit2Sharp.Tests/LibGit2Sharp.Tests.csproj b/LibGit2Sharp.Tests/LibGit2Sharp.Tests.csproj index 5eacddd31..d1d11ef31 100644 --- a/LibGit2Sharp.Tests/LibGit2Sharp.Tests.csproj +++ b/LibGit2Sharp.Tests/LibGit2Sharp.Tests.csproj @@ -37,5 +37,7 @@ + + diff --git a/LibGit2Sharp/LibGit2Sharp.csproj b/LibGit2Sharp/LibGit2Sharp.csproj index fc15f0e2f..9b910685e 100644 --- a/LibGit2Sharp/LibGit2Sharp.csproj +++ b/LibGit2Sharp/LibGit2Sharp.csproj @@ -38,7 +38,8 @@ - + + diff --git a/LibGit2Sharp/CodeGenerator.targets b/Targets/CodeGenerator.targets similarity index 75% rename from LibGit2Sharp/CodeGenerator.targets rename to Targets/CodeGenerator.targets index a317d9261..ae73a4725 100644 --- a/LibGit2Sharp/CodeGenerator.targets +++ b/Targets/CodeGenerator.targets @@ -6,36 +6,12 @@ - $(IntermediateOutputPath)NativeDllName.g.cs $(IntermediateOutputPath)UniqueIdentifier.g.cs $(IntermediateOutputPath)AssemblyCommitIds.g.cs - - - - namespace LibGit2Sharp.Core - { - internal static class NativeDllName - { - public const string Name = "$(libgit2_filename)"%3b - } - } - - - - - - - - - - - - - diff --git a/Targets/GenerateNativeDllName.targets b/Targets/GenerateNativeDllName.targets new file mode 100644 index 000000000..244b707b4 --- /dev/null +++ b/Targets/GenerateNativeDllName.targets @@ -0,0 +1,33 @@ + + + + $(MSBuildAllProjects);$(MSBuildThisFileFullPath) + + + + + $(IntermediateOutputPath)NativeDllName.g.cs + + + + + + + namespace LibGit2Sharp.Core + { + internal static class NativeDllName + { + public const string Name = "$(libgit2_filename)"%3b + } + } + + + + + + + + + + + From 896eb24e14045e6b0af5110dea20c2f9e2892734 Mon Sep 17 00:00:00 2001 From: Tomas Matousek Date: Mon, 16 Apr 2018 10:19:32 -0700 Subject: [PATCH 10/14] Rename TestApp to NativeLibraryLoadTestApp --- LibGit2Sharp.Tests/GlobalSettingsFixture.cs | 2 +- LibGit2Sharp.Tests/LibGit2Sharp.Tests.csproj | 4 ++-- LibGit2Sharp.sln | 4 ++-- .../Directory.Build.props | 0 {LibGit2Sharp.TestApp => NativeLibraryLoadTestApp}/TestApp.cs | 0 .../x64/NativeLibraryLoadTestApp.x64.csproj | 0 .../x86/NativeLibraryLoadTestApp.x86.csproj | 0 7 files changed, 5 insertions(+), 5 deletions(-) rename {LibGit2Sharp.TestApp => NativeLibraryLoadTestApp}/Directory.Build.props (100%) rename {LibGit2Sharp.TestApp => NativeLibraryLoadTestApp}/TestApp.cs (100%) rename LibGit2Sharp.TestApp/x64/LibGit2Sharp.TestApp.x64.csproj => NativeLibraryLoadTestApp/x64/NativeLibraryLoadTestApp.x64.csproj (100%) rename LibGit2Sharp.TestApp/x86/LibGit2Sharp.TestApp.x86.csproj => NativeLibraryLoadTestApp/x86/NativeLibraryLoadTestApp.x86.csproj (100%) diff --git a/LibGit2Sharp.Tests/GlobalSettingsFixture.cs b/LibGit2Sharp.Tests/GlobalSettingsFixture.cs index 65b897f7f..381d13d65 100644 --- a/LibGit2Sharp.Tests/GlobalSettingsFixture.cs +++ b/LibGit2Sharp.Tests/GlobalSettingsFixture.cs @@ -62,7 +62,7 @@ public void LoadFromSpecifiedPath(string architecture) var nativeDllFileName = NativeDllName.Name + ".dll"; var testDir = Path.GetDirectoryName(typeof(GlobalSettingsFixture).Assembly.Location); - var testAppExe = Path.Combine(testDir, $"LibGit2Sharp.TestApp.{architecture}.exe"); + var testAppExe = Path.Combine(testDir, $"NativeLibraryLoadTestApp.{architecture}.exe"); var tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); var platformDir = Path.Combine(tempDir, "plat"); diff --git a/LibGit2Sharp.Tests/LibGit2Sharp.Tests.csproj b/LibGit2Sharp.Tests/LibGit2Sharp.Tests.csproj index d1d11ef31..4a705d51b 100644 --- a/LibGit2Sharp.Tests/LibGit2Sharp.Tests.csproj +++ b/LibGit2Sharp.Tests/LibGit2Sharp.Tests.csproj @@ -7,8 +7,8 @@ - - + + diff --git a/LibGit2Sharp.sln b/LibGit2Sharp.sln index ed3dd1b56..1bcc15392 100644 --- a/LibGit2Sharp.sln +++ b/LibGit2Sharp.sln @@ -15,9 +15,9 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution version.json = version.json EndProjectSection EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LibGit2Sharp.TestApp.x86", "LibGit2Sharp.TestApp\x86\LibGit2Sharp.TestApp.x86.csproj", "{86453D2C-4953-4DF4-B12A-ADE579608BAA}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NativeLibraryLoadTestApp.x86", "NativeLibraryLoadTestApp\x86\NativeLibraryLoadTestApp.x86.csproj", "{86453D2C-4953-4DF4-B12A-ADE579608BAA}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LibGit2Sharp.TestApp.x64", "LibGit2Sharp.TestApp\x64\LibGit2Sharp.TestApp.x64.csproj", "{5C55175D-6A1F-4C51-B791-BF7DD00124EE}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NativeLibraryLoadTestApp.x64", "NativeLibraryLoadTestApp\x64\NativeLibraryLoadTestApp.x64.csproj", "{5C55175D-6A1F-4C51-B791-BF7DD00124EE}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution diff --git a/LibGit2Sharp.TestApp/Directory.Build.props b/NativeLibraryLoadTestApp/Directory.Build.props similarity index 100% rename from LibGit2Sharp.TestApp/Directory.Build.props rename to NativeLibraryLoadTestApp/Directory.Build.props diff --git a/LibGit2Sharp.TestApp/TestApp.cs b/NativeLibraryLoadTestApp/TestApp.cs similarity index 100% rename from LibGit2Sharp.TestApp/TestApp.cs rename to NativeLibraryLoadTestApp/TestApp.cs diff --git a/LibGit2Sharp.TestApp/x64/LibGit2Sharp.TestApp.x64.csproj b/NativeLibraryLoadTestApp/x64/NativeLibraryLoadTestApp.x64.csproj similarity index 100% rename from LibGit2Sharp.TestApp/x64/LibGit2Sharp.TestApp.x64.csproj rename to NativeLibraryLoadTestApp/x64/NativeLibraryLoadTestApp.x64.csproj diff --git a/LibGit2Sharp.TestApp/x86/LibGit2Sharp.TestApp.x86.csproj b/NativeLibraryLoadTestApp/x86/NativeLibraryLoadTestApp.x86.csproj similarity index 100% rename from LibGit2Sharp.TestApp/x86/LibGit2Sharp.TestApp.x86.csproj rename to NativeLibraryLoadTestApp/x86/NativeLibraryLoadTestApp.x86.csproj From 53e1feafc3b0017c8cd9e49ac8b95882a4826b31 Mon Sep 17 00:00:00 2001 From: Tomas Matousek Date: Mon, 16 Apr 2018 10:33:26 -0700 Subject: [PATCH 11/14] Fix path --- LibGit2Sharp.Tests/LibGit2Sharp.Tests.csproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/LibGit2Sharp.Tests/LibGit2Sharp.Tests.csproj b/LibGit2Sharp.Tests/LibGit2Sharp.Tests.csproj index 4a705d51b..ae58206ec 100644 --- a/LibGit2Sharp.Tests/LibGit2Sharp.Tests.csproj +++ b/LibGit2Sharp.Tests/LibGit2Sharp.Tests.csproj @@ -7,8 +7,8 @@ - - + + From e618156c150d0bf2c318f5e6fa30b468f63ee45f Mon Sep 17 00:00:00 2001 From: Tomas Matousek Date: Mon, 16 Apr 2018 10:34:01 -0700 Subject: [PATCH 12/14] Do not set default value for NativeLibararyPath on .NET Core --- LibGit2Sharp/Core/NativeMethods.cs | 26 ++++++++++++--------- LibGit2Sharp/Core/Platform.cs | 21 ----------------- LibGit2Sharp/GlobalSettings.cs | 37 ++++++++++++------------------ 3 files changed, 30 insertions(+), 54 deletions(-) diff --git a/LibGit2Sharp/Core/NativeMethods.cs b/LibGit2Sharp/Core/NativeMethods.cs index 4c54388b7..b5aa8097e 100644 --- a/LibGit2Sharp/Core/NativeMethods.cs +++ b/LibGit2Sharp/Core/NativeMethods.cs @@ -29,18 +29,22 @@ static NativeMethods() { if (Platform.IsRunningOnNetFramework() || Platform.IsRunningOnNetCore()) { - string dllPath = Path.Combine(GlobalSettings.GetAndLockNativeLibraryPath(), libgit2 + Platform.GetNativeLibraryExtension()); - - // Try to load the .dll from the path explicitly. - // If this call succeeds further DllImports will find the library loaded and not attempt to load it again. - // If it fails the next DllImport will load the library from safe directories. - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - LoadWindowsLibrary(dllPath); - } - else + string nativeLibraryDir = GlobalSettings.GetAndLockNativeLibraryPath(); + if (nativeLibraryDir != null) { - LoadUnixLibrary(dllPath, RTLD_NOW); + string nativeLibraryPath = Path.Combine(nativeLibraryDir, libgit2 + Platform.GetNativeLibraryExtension()); + + // Try to load the .dll from the path explicitly. + // If this call succeeds further DllImports will find the library loaded and not attempt to load it again. + // If it fails the next DllImport will load the library from safe directories. + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + LoadWindowsLibrary(nativeLibraryPath); + } + else + { + LoadUnixLibrary(nativeLibraryPath, RTLD_NOW); + } } } diff --git a/LibGit2Sharp/Core/Platform.cs b/LibGit2Sharp/Core/Platform.cs index 53475bec0..e8d536475 100644 --- a/LibGit2Sharp/Core/Platform.cs +++ b/LibGit2Sharp/Core/Platform.cs @@ -37,27 +37,6 @@ public static OperatingSystemType OperatingSystem } } - /// - /// Determines the RID to use when loading libgit2 native library. - /// This method only supports RIDs that are currently used by LibGit2Sharp.NativeBinaries. - /// - public static string GetNativeLibraryRuntimeId() - { - switch (OperatingSystem) - { - case OperatingSystemType.MacOSX: - return "osx"; - - case OperatingSystemType.Unix: - return "linux-" + ProcessorArchitecture; - - case OperatingSystemType.Windows: - return "win7-" + ProcessorArchitecture; - } - - throw new PlatformNotSupportedException(); - } - public static string GetNativeLibraryExtension() { switch (OperatingSystem) diff --git a/LibGit2Sharp/GlobalSettings.cs b/LibGit2Sharp/GlobalSettings.cs index 5378a6829..f71646e76 100644 --- a/LibGit2Sharp/GlobalSettings.cs +++ b/LibGit2Sharp/GlobalSettings.cs @@ -20,6 +20,7 @@ public static class GlobalSettings private static string nativeLibraryPath; private static bool nativeLibraryPathLocked; + private static string nativeLibraryDefaultPath; static GlobalSettings() { @@ -28,24 +29,14 @@ static GlobalSettings() nativeLibraryPathAllowed = netFX || netCore; - if (nativeLibraryPathAllowed) + if (netFX) { - string assemblyDirectory = GetExecutingAssemblyDirectory(); - - if (netFX) - { - // For .NET Framework apps the dependencies are deployed to lib/win32/{architecture} directory - nativeLibraryPath = Path.Combine(assemblyDirectory, "lib", "win32"); - } - else - { - // .NET Core apps that depend on native libraries load them directly from paths specified - // in .deps.json file of that app and the native library loader just works. - // However, .NET Core doesn't support .deps.json for plugins yet (such as msbuild tasks). - // To address that shortcoming we assume that the plugin deploys the native binaries to runtimes\{rid}\native - // directories and search there. - nativeLibraryPath = Path.Combine(assemblyDirectory, "runtimes", Platform.GetNativeLibraryRuntimeId(), "native"); - } + // For .NET Framework apps the dependencies are deployed to lib/win32/{architecture} directory + nativeLibraryDefaultPath = Path.Combine(GetExecutingAssemblyDirectory(), "lib", "win32"); + } + else + { + nativeLibraryDefaultPath = null; } registeredFilters = new Dictionary(); @@ -186,6 +177,10 @@ public static LogConfiguration LogConfiguration /// This must be set before any other calls to the library, /// and is not available on other platforms than .NET Framework and .NET Core. /// + /// + /// If not specified on .NET Framework it defaults to lib/win32 subdirectory + /// of the directory where this assembly is loaded from. + /// /// public static string NativeLibraryPath { @@ -196,7 +191,7 @@ public static string NativeLibraryPath throw new LibGit2SharpException("Querying the native hint path is only supported on .NET Framework and .NET Core platforms"); } - return nativeLibraryPath; + return nativeLibraryPath ?? nativeLibraryDefaultPath; } set @@ -225,10 +220,8 @@ public static string NativeLibraryPath internal static string GetAndLockNativeLibraryPath() { nativeLibraryPathLocked = true; - - return Platform.IsRunningOnNetFramework() ? - Path.Combine(nativeLibraryPath, Platform.ProcessorArchitecture) : - nativeLibraryPath; + string result = nativeLibraryPath ?? nativeLibraryDefaultPath; + return Platform.IsRunningOnNetFramework() ? Path.Combine(result, Platform.ProcessorArchitecture) : result; } /// From d9496ba00be1bb3669626a0552ee5da0319a7f72 Mon Sep 17 00:00:00 2001 From: Brandon Ording Date: Mon, 16 Apr 2018 20:35:06 -0400 Subject: [PATCH 13/14] Clean up trailing whitespace --- LibGit2Sharp.Tests/LibGit2Sharp.Tests.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LibGit2Sharp.Tests/LibGit2Sharp.Tests.csproj b/LibGit2Sharp.Tests/LibGit2Sharp.Tests.csproj index ae58206ec..6837e02eb 100644 --- a/LibGit2Sharp.Tests/LibGit2Sharp.Tests.csproj +++ b/LibGit2Sharp.Tests/LibGit2Sharp.Tests.csproj @@ -37,7 +37,7 @@ - + From 497f91f181cf13c5ac41c63bf1d5052e21bd8f99 Mon Sep 17 00:00:00 2001 From: Brandon Ording Date: Mon, 16 Apr 2018 20:36:04 -0400 Subject: [PATCH 14/14] Add code generation targets to Solution Items --- LibGit2Sharp.sln | 2 ++ 1 file changed, 2 insertions(+) diff --git a/LibGit2Sharp.sln b/LibGit2Sharp.sln index 1bcc15392..921aee2d4 100644 --- a/LibGit2Sharp.sln +++ b/LibGit2Sharp.sln @@ -9,8 +9,10 @@ EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{0CA739FD-DA4D-4F64-9834-DA14A3ECD04B}" ProjectSection(SolutionItems) = preProject .gitignore = .gitignore + Targets\CodeGenerator.targets = Targets\CodeGenerator.targets Directory.Build.props = Directory.Build.props Directory.Build.targets = Directory.Build.targets + Targets\GenerateNativeDllName.targets = Targets\GenerateNativeDllName.targets nuget.config = nuget.config version.json = version.json EndProjectSection