Reputation: 962
I have a two-part project. Part one is a set of C# bindings that wrap a native .dll or .so library. This produces a NugGet package, which is then consumed by the other project.
The other is a Xamarin Forms project that consumes those C# bindings. The UWP portion of my Xamarin project has no issue finding the native library inside the NuGet package. The Android project, however, cannot find it. Indeed, opening the APK reveals that the native .so file never makes it in.
I'm using NuGet's mechanism for including arch-specific libraries, by including each platform-specific .dll-or-.so in a /runtimes/platform-arch/native/
folder. My folder structure looks like this for the bindings project:
...and I can confirm that the folder structure is properly preserved in the resulting .nupkg
file, with a runtimes
folder at the root of the package, and a series of subfolders underneath it.
I generate the NuGet package by using the new built in .csproj mechanisms, without any .targets files. My csproj for the NuGet project looks like this:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<IncludeSymbols>true</IncludeSymbols>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<Version>1.0.5.1</Version>
<Company />
<Product />
</PropertyGroup>
<ItemGroup>
<Content Include="runtimes\**" PackagePath="runtimes">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<PackageReference Include="OneOf" Version="2.1.150" />
</ItemGroup>
</Project>
The second part of my project is a Xamarin Forms project, and I import the NuGet package to the .NET Standard cross-platform portion of the project.
When I build the UWP project, my platform-specific .dll
is correctly copied to the root of my .appx
file, and I can successfully P/Invoke it. (I have noticed that this only works if I build in an x64 configuration, and I am compiling on an x64 machine.) However, when building the Android project, no such thing happens with the .so
Android library.
The entire dependency chain looks something like this:
-------------NuGet Package----------
| - native_lib.so |
| - (other runtime dlls, etc) |
------------------------------------
^
|
-----Xamarin.Forms Core Project-----
| - Imports NuGet |
------------------------------------
^
|
-------Xamarin Android Project------
| - Imports Xamarin Forms Core Proj|
| - Produces APK |
------------------------------------
It does not seem to be a naming issue--attempting to rename native_lib.so
in the bindings project to libnative_lib.so
has no effect.
Am I missing a step, or does NuGet simply not understand that a Xamarin.Android project needs native libraries copied into the APK?
Source code for the NuGet project: https://github.com/pingzing/scannitsharp Source code for the Xamarin Forms project that consumes the NuGet: https://github.com/pingzing/scannit NuGet package: https://www.nuget.org/packages/ScannitSharp/1.0.5.1
Upvotes: 4
Views: 2113
Reputation: 2184
Generally speaking you need to share more info in your Q, such as your code and packaging files.
does NuGet simply not understand that a Xamarin.Android project needs native libraries copied into the APK?
Yes it does. You need to package your NuGet in a way that MSBuild includes this file in the final APK.
For Android, this is done with AndroidNativeLibrary
And a working example here https://github.com/mfkl/libvlc-nuget/blob/master/build/VideoLAN.LibVLC.Android.targets
I have noticed that this only works if I build in an x64 configuration, and I am compiling on an x64 machine.
You likely need to modify your targets file to handle the 3 possibles cases AnyCPU
, x86
and x64
. Your target should run before the actual build, with BeforeTargets="BeforeBuild"
.
You can use something like
<Content Include="@(WindowsX64IncludeFilesFullPath)">
<Link>$(WindowsX64TargetDir)\$([MSBuild]::MakeRelative($(MSBuildThisFileDirectory)..\build\x64\, %(FullPath)))</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
Check this out: https://github.com/mfkl/libvlc-nuget/blob/master/build/VideoLAN.LibVLC.Windows.targets
Upvotes: 1