adesso Blog

Für ein Projekt entschloss ich mich, die libgit2-Bibliothek für die portable C#-Implementierung zu nutzen. Beim Hinzufügen des NuGet-Pakets in der aktuellen Version 0.26.2 (vom 11. Dezember 2019) trat ein Problem während der Laufzeit auf: Die freigegebene Bibliothek konnte nicht geladen werden.

In diesem Blog-Beitrag erfahrt ihr mehr über diesen Fehler. Ich erkläre die einzelnen Schritte, wie ihr dieses Problem beheben und mit eurem Projekt fortfahren könnt.

Ich erstelle zuerst ein MVP (minimal funktionsfähiges Produkt), um das Problem zu replizieren und es ohne weitere Projektabhängigkeiten zu untersuchen. Dann befolge ich den Prozess zur Fehlerbehebung und zur Suche nach einer Lösung.

Das Ganze führe ich auf Majaro x64 with .Net 6 aus.

Den Fehler im MVP replizieren

Meine Beobachtung war, dass jeder Aufruf der Bibliothek einen Fehler verursachte. Daher konnte das Problem recht einfach repliziert werden: Ich musste nur das NuGet-Paket einbinden und irgendetwas aus der Bibliothek aufrufen.

	
	// Get the directory where the executable is run.
		string currentDirectory = Directory.GetCurrentDirectory();
		// Call the library to check whether the provided location is a valid
		// git repository.
		bool directoryIsARepository = LibGit2Sharp.Repository.IsValid(currentDirectory);
		// Set the exit code to non zero to indicate an error or the uncaught
		// exception will print the details.
		return directoryIsARepository ? 0 : 1;
	

Nachdem der Fehler auf meinem Rechner nachgestellt wurde, habe ich ein neues Repository erstellt und ein Minimum an Code hinzugefügt, der zum Reproduzieren des Fehlers erforderlich ist. Das Repository wurde unter sr.ht hochgeladen.

	
	$ git log --oneline -n1
		e8f4902 Add simple check for the library
		~/Projects/poc_libgit2sharp main $ make run
		dotnet build ./poc_libgit2sharp/poc_libgit2sharp.csproj
		Microsoft (R) Build Engine version 17.0.0+c9eb9dd64 for .NET
		Copyright (C) Microsoft Corporation. All rights reserved.
		  Determining projects to restore...
		  All projects are up-to-date for restore.
		  poc_libgit2sharp -> /home/vince/Projects/poc_libgit2sharp/poc_libgit2sharp/bin/Debug/net6.0/poc_libgit2sharp.dll
		Build succeeded.
		    0 Warning(s)
		    0 Error(s)
		Time Elapsed 00:00:01.33
		dotnet run --project ./poc_libgit2sharp/poc_libgit2sharp.csproj
		Current directory is '/home/vince/Projects/poc_libgit2sharp'
		Checking whether the directory contains a valid git repository.
		Should return true as the program is run from a repository.
		Unhandled exception. System.TypeInitializationException: The type initializer for 'LibGit2Sharp.Core.NativeMethods' threw an exception.
		---> System.DllNotFoundException: Unable to load shared library 'git2-106a5f2' or one of its dependencies. In order to help diagnose loading problems, consider setting the LD_DEBUG environment variable: libgit2-106a5f2: cannot open shared object file: No such file or directory
		at LibGit2Sharp.Core.NativeMethods.git_libgit2_init()
		at LibGit2Sharp.Core.NativeMethods.InitializeNativeLibrary()
		at LibGit2Sharp.Core.NativeMethods..cctor()
		--- End of inner exception stack trace ---
		at LibGit2Sharp.Core.NativeMethods.git_repository_open_ext(git_repository*& repository, FilePath path, RepositoryOpenFlags flags, FilePath ceilingDirs)
		at LibGit2Sharp.Core.Proxy.git_repository_open_ext(String path, RepositoryOpenFlags flags, String ceilingDirs)
		at LibGit2Sharp.Repository.IsValid(String path)
		at Program.<Main>$(String[] args) in /home/vince/Projects/poc_libgit2sharp/poc_libgit2sharp/Program.cs:line 7
		make: *** [Makefile:18: run] Error 134
	

Zuerst sollten wir uns die zurückgegebene Fehlermeldung genauer anschauen, um das Grundproblem zu verstehen. Erst danach ergreifen wir weitere Maßnahmen und analysieren Code des Open-Source-Repositorys libgit2.

	
	Unhandled exception. System.TypeInitializationException:
			The type initializer for 'LibGit2Sharp.Core.NativeMethods' threw an exception.
		---> System.DllNotFoundException:
			Unable to load shared library 'git2-106a5f2' or one of its dependencies.
			In order to help diagnose loading problems, consider setting the LD_DEBUG environment variable:
			libgit2-106a5f2: cannot open shared object file: No such file or directory
	

Wir wissen, dass die Bibliothek ein Wrapper um die C-Implementierung von libgit2 ist, um sie in der verwalteten Welt von .NET nutzen zu können. Mit diesem Hintergrundwissen können wir also annehmen, dass es bei der Referenz zu git2-106a5f2 von System.DllNotFoundException um die native Bibliothek geht. Im nächsten Schritt wird ermittelt, von wo aus die Bibliothek geladen wird und wo der Fehler auftritt. Mit diesen Informationen kann man das Problem besser nachvollziehen.

Die Untersuchung mit Debug-Optionen

Die Fehlermeldung liefert uns alle Informationen, die wir für das weitere Vorgehen benötigen. Sie gibt an, dass LD_DEBUG genutzt wird. Sehen wir uns also die Ausgabe mit dieser Umgebungsvariablen an (Ausgabe stark gekürzt):

	
	~/Projects/poc_libgit2sharp main $ LD_DEBUG=libs make run
		dotnet run --project ./poc_libgit2sharp/poc_libgit2sharp.csproj
		   1022222:	initialize program: /home/vince/Projects/poc_libgit2sharp/poc_libgit2sharp/bin/Debug/net6.0/poc_libgit2sharp
		Current directory is '/home/vince/Projects/poc_libgit2sharp'
		Checking whether the directory contains a valid git repository.
		Should return true as the program is run from a repository.
		   1022222:	find library=git2-106a5f2.so [0]; searching
		   1022222:	 search cache=/etc/ld.so.cache
		   1022222:	 search path=/usr/lib/tls:/usr/lib		(system search path)
		   1022222:	  trying file=/usr/lib/tls/git2-106a5f2.so
		   1022222:	  trying file=/usr/lib/git2-106a5f2.so
		   1022222:
		   1022222:	[...]
		   1022222:	find library=libgit2-106a5f2.so [0]; searching
		   1022222:	 search cache=/etc/ld.so.cache
		   1022222:	 search path=/usr/lib/tls:/usr/lib		(system search path)
		   1022222:	  trying file=/usr/lib/tls/libgit2-106a5f2.so
		   1022222:	  trying file=/usr/lib/libgit2-106a5f2.so
		   1022222:
		   1022222:	[...]
		   1022222:	find library=git2-106a5f2 [0]; searching
		   1022222:	 search cache=/etc/ld.so.cache
		   1022222:	 search path=/usr/lib/tls:/usr/lib		(system search path)
		   1022222:	  trying file=/usr/lib/tls/git2-106a5f2
		   1022222:	  trying file=/usr/lib/git2-106a5f2
		   1022222:
		   1022222:	find library=libgit2-106a5f2 [0]; searching
		   1022222:	 search cache=/etc/ld.so.cache
		   1022222:	 search path=/usr/lib/tls:/usr/lib		(system search path)
		   1022222:	  trying file=/usr/lib/tls/libgit2-106a5f2
		   1022222:	  trying file=/usr/lib/libgit2-106a5f2
		   1022222:
		Unhandled exception.
		System.TypeInitializationException: The type initializer for 'LibGit2Sharp.Core.NativeMethods' threw an exception.
		 ---> System.DllNotFoundException: Unable to load shared library 'git2-106a5f2' or one of its dependencies.
		      [...]
		make: *** [Makefile:18: run] Error 134
	

Wird der Parameter LD_DEBUG auf all oder files festgelegt, erhalten wir ähnliche Ergebnisse:

	
	   1022654:	file=/home/vince/Projects/poc_libgit2sharp/poc_libgit2sharp/bin/Debug/net6.0/runtimes/linux-x64/native/git2-106a5f2.so [0];  dynamically loaded by /usr/share/dotnet/shared/Microsoft.NETCore.App/6.0.2/libcoreclr.so [0]
		   1022654:
		   1022654:	file=/usr/share/dotnet/shared/Microsoft.NETCore.App/6.0.2/git2-106a5f2.so [0];  dynamically loaded by /usr/share/dotnet/shared/Microsoft.NETCore.App/6.0.2/libcoreclr.so [0]
		   1022654:
		   1022654:	file=/home/vince/Projects/poc_libgit2sharp/poc_libgit2sharp/bin/Debug/net6.0/git2-106a5f2.so [0];  dynamically loaded by /usr/share/dotnet/shared/Microsoft.NETCore.App/6.0.2/libcoreclr.so [0]
		   1022654:
		   1022654:	file=git2-106a5f2.so [0];  dynamically loaded by /usr/share/dotnet/shared/Microsoft.NETCore.App/6.0.2/libcoreclr.so [0]
		   [...]
		   1022654:	file=/home/vince/Projects/poc_libgit2sharp/poc_libgit2sharp/bin/Debug/net6.0/runtimes/linux-x64/native/libgit2-106a5f2.so [0];  dynamically loaded by /usr/share/dotnet/shared/Microsoft.NETCore.App/6.0.2/libcoreclr.so [0]
		   1022654:	file=/home/vince/Projects/poc_libgit2sharp/poc_libgit2sharp/bin/Debug/net6.0/runtimes/linux-x64/native/libgit2-106a5f2.so [0];  generating link map
		   1022654:	  dynamic: 0x00007fda6ad24d90  base: 0x00007fda6aa00000   size: 0x000000000032a5d0
		   1022654:	    entry: 0x00007fda6aa166f0  phdr: 0x00007fda6aa00040  phnum:                  7
	

Die Bibliotheksdateien ./bin/Debug/net6.0/runtimes/linux-x64/native/libgit2-106a5f2.so sind vorhanden und sie werden korrekt importiert. Aber die referenzierte Datei /usr/share/dotnet/shared/Microsoft.NETCore.App/6.0.2/git2-106a5f2.so ist unter diesem globalen Speicherort nicht zu finden.

Wenn keine spezifische Version auf die gemeinsam genutzte Objektdatei angewendet wird, sollte die Datei meiner Meinung nach vom System stammen. Wir prüfen den Paket-Manager und installieren die aktuelle Version der zugrunde liegenden Bibliothek libgit2 auf dem System.

	
	$ sudo pacman -S libgit2
	

Aber sogar nach der Installation und Aktualisierung tritt der Fehler weiterhin auf. Wie bereits vermutet, gibt es keinen Zusammenhang mit der globalen Installation der Bibliothek. Sie sollte mit dem NuGet-Paket ausgeliefert werden.

Der nächste Schritt ist also, die Probleme auf GitHub zu untersuchen und eine mögliche Lösung zu finden.

Die GitHub-Fehler auf mögliche Lösungen hin untersuchen

Vor der Analyse der Installationsscripts oder des Bibliothek-Codes untersuchen wir die Fehler auf GitHub.Einige Probleme deuten darauf hin, dass die korrekte Abhängigkeit des Pakets verwendet werden soll. Das ist LibGit2Sharp.NativeBinaries in Version 2.0.306. Das war bereits der Fall, als die Datei sobj/project.assets.json überprüft wurde. Mehrere Probleme weisen allerdings darauf hin, dass es Probleme mit .Net 5 gibt, die in der Preview-Version 0.27.0des NuGet-Pakets LibGit2Sharp behoben werden sollten.

Die Preview-Version der Bibliothek installieren

Aktualisieren wir jetzt die Paketreferenz, um zu prüfen, ob die Preview-Version das Problem für die .Net-6-Anwendung behebt.

	
	  <ItemGroup>
		    <PackageReference Include="LibGit2Sharp" Version="0.27.0-preview-0182" />
		  </ItemGroup>
	
	
	~/Projects/poc_libgit2sharp main $ make run
		[...]
		dotnet run --project ./poc_libgit2sharp/poc_libgit2sharp.csproj
		Current directory is '/home/vince/Projects/poc_libgit2sharp'
		Checking whether the directory contains a valid git repository.
		Should return true as the program is run from a repository.
		This message indicates that no exception was thrown and the library works as expected.
		The directory '/home/vince/Projects/poc_libgit2sharp' is a git repository 'True'
	

Perfekt! Es wird kein Ausnahmefehler von der Bibliothek zurückgegeben und das erwartete Ergebnis ist korrekt. Das Arbeitsverzeichnis ist tatsächlich ein git repository.

Eine Referenz auf eine Preview-Version ist allerdings immer unangenehm und sollte vermieden werden. Von Git selbst wissen wir, dass wir die Quellen direkt einbinden und sie in unser Projekt integrieren können. Aber was heißt das für unser Projekt? Das wollen wir in diesem MVP mit beiden Optionen herausfinden.

Erstellung aus der Quelle

Im letzten Schritt versuchen wir nun, das Projekt selbst zu erstellen und das Ergebnis in unser Projekt einzubinden. Mit dieser Option kann der master-Zweig des Repositorys direkt verknüpft und entsprechend aktualisiert werden. So erhalten wir die aktuellen Updates und müssen auf kein Update des NuGet-Pakets warten.

	
	$ git submodule add https://github.com/libgit2/libgit2sharp.git libgit2sharp
		[...]
		$ ls -l
		-rw-r--r--  1 vince vince  349 Apr 21 20:23 .gitignore
		-rw-r--r--  1 vince vince   99 Apr 21 22:16 .gitmodules
		[...]
		drwxr-xr-x 9 vince vince  4096 Apr 21 22:24 libgit2sharp
		-rw-r--r-- 1 vince vince   432 Apr 21 20:54 Makefile
		drwxr-xr-x 4 vince vince  4096 Apr 21 20:21 poc_libgit2sharp
		$ cat .gitmodules
		[submodule "libgit2sharp"]
			path = libgit2sharp
			url = https://github.com/libgit2/libgit2sharp.git
	

Jetzt kann endlich die Projektreferenz aktualisiert werden.

	
	$ git diff d85adbb~..d85adbb
		diff --git a/poc_libgit2sharp/poc_libgit2sharp.csproj b/poc_libgit2sharp/poc_libgit2sharp.csproj
		index 4329b2b..f3f8c62 100644
		--- a/poc_libgit2sharp/poc_libgit2sharp.csproj
		+++ b/poc_libgit2sharp/poc_libgit2sharp.csproj
		@@ -8,7 +8,7 @@
		   </PropertyGroup>
		   <ItemGroup>
		-    <PackageReference Include="LibGit2Sharp" Version="0.27.0-preview-0182" />
		+    <ProjectReference Include="..\libgit2sharp\LibGit2Sharp\LibGit2Sharp.csproj" />
		   </ItemGroup>
		 </Project
	

Das Submodul funktioniert wie erwartet, so wie bei der Preview-Version des NuGet-Pakets. Beim Submodul treten ähnliche Nachteile auf wie bei der Verwendung eines Paket-Managers: Die Referenz muss manuell aktualisiert werden, wenn eine neue Version veröffentlicht wird.

Wenn das Submodul verwendet wird, kann der master-Zweig direkt ausgecheckt und auf die neueste Entwicklungsversion geprüft werden. Ganz wichtig: Wenn ein stabiles Release von 0.27.0 des Pakets veröffentlicht wird, muss diese Option neu bewertet werden.

Fazit

Um alle Änderungen anzuzeigen, die seit der letzten veröffentlichten Version vorgenommen wurden, können wir den Unterschied auf GitHub v0.26..master direkt auschecken.

Die Hilfe zu Submodulen ist umfangreich. Sie kann unter man 1 git-submodule und man 7 gitsubmodules abgerufen werden.

Bei meinem Projekt habe ich mich für den Submodul-Workaround entschieden, weil dieser einige Vorteile hat:

  • Durch das regelmäßige Erstellen kann ich das Submodul auf Aktualisierungen hin überwachen.
  • Ich bleibe bei Änderungen an der Bibliothek auf dem Laufenden.
  • Ich kann den Entwicklerinnen und Entwicklern der Bibliothek Feedback geben, da ich mit einer Version arbeite, die neue Features und Fehlerbehebungen enthält.
  • Ich kann auf verschiedenen Zweigen verschiedene Versionen der Bibliothek testen.
Bild Vincent Scherb

Autor Vincent Scherb

Vincent Scherb ist Softwareentwickler bei adesso in Nürnberg. Er arbeitet hauptsächlich als Backend-Entwickler in der Fertigungsindustrie mit Microsoft-Technologien.

Kategorie:

Softwareentwicklung

Schlagwörter:

NuGet

Diese Seite speichern. Diese Seite entfernen.