NuGet Packages mit nativen Libs

Ich bin vor kurzem vor dem Problem gestanden ein NuGet Paket zu erstellen, welches seine eigenen nativen Libraries mitbringt.
In meinem Fall, ging es speziell um Android Projekt, es lässt sich aber grundsätzlich auf alles anwenden.

Nachdem ich einiges an Zeit investieren musse bis es endlich geklappt hat hier eine kurze Anleitung worauf man acht geben muss:

Normalerweise macht man die Implementierung der nativen DLL fertig und legt dann im Xamarin Projekt folgende Struktur an:

/lib/[ABI]/lib___.so

Daß eine lib mit "lib" beginnen muss ist einfach ein default vom System - denn er sucht einfach nach einem lib*.sp pattern.

Das sollte im Projekt dann ca so wie im rechten Bild aussehen.

lib_struktur.png

Dann werden die Files in den Settings noch als "AndroidNativeLibrary" ausgewiesen, damit er auch weiß was das ist.

Damit is schon so einiges erlefigt, denn wenn man die .Net Library jetzt direkt im Projekt referenziert erkennt er die Libs schon und so kann man schnell entwickeln und alles "fertig" machen.

Aber wenn man ein NuGet Package daraus machen möchte hakt es denn die Libraries finden noch nicht von selbst den Weg ins Nuget Package.

Dafür braucht man dann dazu ein eigenes .nuspec file, indem dann die libs ins package aufgenommen werden.

Und damit die libs auch im Build berücksichtigt werden benötigt man ein eigenes .targets File.

Das sieht dann im .nuspec etwa so aus:

<?xml version="1.0"?>
<package >
    <metadata>
        <id>my.nuget.package</id>
        <version>$version$</version>
        <title>my.nuget.package</title>
        <authors>me</authors>
        <owners>my company</owners>
        <requireLicenseAcceptance>false</requireLicenseAcceptance>
        <description>fancy native feaures</description>
        <copyright>Copyright 2018</copyright>
        <tags>...</tags>
        <dependencies>
            <dependency id="!?!" version="1.0.0.0" />
        </dependencies>
    </metadata>
    <files>
        <file src="bin\Release\*.dll" target="lib/monoandroid" />
        <file src=" my.nuget.package.targets" target="build" />
        <file src="lib\**\*.*" target="lib" />
    </files>
</package>

Ich möchte speziell auf die sektion eingehen:

<file src="bin\Release\*.dll" target="lib/monoandroid" />

Die erste Zeile inkludiert alle kopilierten .net libs aus dem Release Verzeichnis ins Package und mit dem target lib/monoandroid wirds auch richtig referenziert.

 

<file src=" my.nuget.package.targets" target="build" />

Die 2. Zeile spezifiziert, dass ein .targets-File, dass im build verzeichnis des Nuget-Packages landen soll. 

Dieses .targets-File MUSS so heißen wie das Nuget Package!

Wenn es aber nachher im build-verzeichnis liegt und richtig heißt wird es automatisch im build berücksichtigt.

 

<file src="lib\**\*.*" target="lib" />

Die letzte Zeile beinhaltet dann schlussendlich einfach die nativen libs die im "lib"-Verzeichnis liegen und durch den **\.* Syntax werden einfach alle files in allen Unterverzeichnissen genommen (inkl. Verzeichnisstruktur).

 

Jetzt haben wir schon alles im Package file drin jetzt müssen wir nur noch das .targets-File befüllen damit es die librarys auch im Ziel Projekt als AndroidNativeLibrary mit ins APK packt.

Das schaut dann so aus:

<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
    <ItemGroup>
        <AndroidNativeLibrary Include="$(MSBuildThisFileDirectory)\..\lib\armeabi-v7a\libXXX.so">
            <Link>$(MSBuildThisFileDirectory)\..\lib\armeabi-v7a\libXXX.so</Link>
            <Abi>armeabi</Abi>
        </AndroidNativeLibrary>
        <AndroidNativeLibrary Include="$(MSBuildThisFileDirectory)\..\lib\armeabi-v7a\libXXX.so">
            <Link>$(MSBuildThisFileDirectory)\..\lib\armeabi-v7a\libXXX.so</Link>
            <Abi>armeabi-v7a</Abi>
        </AndroidNativeLibrary>
        <AndroidNativeLibrary Include="$(MSBuildThisFileDirectory)\..\lib\x86\libXXX.so">
            <Link>$(MSBuildThisFileDirectory)\..\lib\x86\libXXX.so</Link>
            <Abi>x86</Abi>
        </AndroidNativeLibrary>
    </ItemGroup>
</Project>

Wie man sieht werden die Sachen einfach wie im .csproj file ins Projekt gehängt.

Achtung: man muss jede Library je nach ABI extra einhängen - und genau auf die Schreibweisen achten.

 

Die File Struktur hier ist, bis auf das  build-Verzeichnis indem das .targets-File liegen muss, relativ frei - wo man die nativen libs hineinpackt bleibt euch überlassen - allerdings fand ich es praktisch da ich sie im Projekt ja ohnehin in der Struktur brauche.

 

 

Zu guter letzt könnt ihr das Nuget Package wie immer bauen und im Zielprojekt installieren.

Mit ein wenig Glück solltet ihr dann im APK file eure dll im "lib\[ABI]" Verzeichnis finden!

 

Hier nun noch meine Quellen:

Und mein Dank an https://twitter.com/SabotageAndi für eine schnelle Hilfe.

apk.png