C
C#•7mo ago
MiKom

Publishing application with native libraries

Hey. I'm trying to create an application that depends on some native shared libraries (written in C++). I created a NativeLibraries project that contains the required shared libraries as Content elements in .csproj:
<Content Condition="$(OS)=='Unix'" Include="../path/to/x64/library.so" LinkBase="runtimes/linux-x64/native">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Condition="$(OS)=='Unix'" Include="../path/to/arm/library.so" LinkBase="runtimes/linux-arm/native">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Condition="$(OS)=='Unix'" Include="../path/to/x64/library.so" LinkBase="runtimes/linux-x64/native">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Condition="$(OS)=='Unix'" Include="../path/to/arm/library.so" LinkBase="runtimes/linux-arm/native">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
Then my main application depends on this NativeLibraries project. When I build it, everything is fine, the native libraries are nicely placed in runtimes/* folder in the build directory of the main application. Problem is that the libraries are not copied to the output directory directly for rid-specific publishing (dotnet publish -r linux-arm) but are still present in runtimes/linux-arm/native folder. Unlike for example SkiaSharp (that I also use). The native libraries of SkiaSharp are correctly copied directly to the publish output directory. SkiaSharp doesn't seem to be particularly clever here:
<ItemGroup>
<!-- glibc -->
<PackageFile Include="..\..\output\native\linux\x64\libHarfBuzzSharp*" PackagePath="runtimes\linux-x64\native\%(Filename)%(Extension)" />
<PackageFile Include="..\..\output\native\linux\x86\libHarfBuzzSharp*" PackagePath="runtimes\linux-x86\native\%(Filename)%(Extension)" />
<ItemGroup>
<!-- glibc -->
<PackageFile Include="..\..\output\native\linux\x64\libHarfBuzzSharp*" PackagePath="runtimes\linux-x64\native\%(Filename)%(Extension)" />
<PackageFile Include="..\..\output\native\linux\x86\libHarfBuzzSharp*" PackagePath="runtimes\linux-x86\native\%(Filename)%(Extension)" />
And the PackageFile is expanded as follows:
<ItemGroup>
<None Include="@(PackageFile)" Link="%(PackagePath)" Pack="True" />
</ItemGroup>
<ItemGroup>
<None Include="@(PackageFile)" Link="%(PackagePath)" Pack="True" />
</ItemGroup>
Which is similar to my Content element. I tried adding this Pack as well but it didn't help. Probably, SkiaSharp is doing something else that I don't see here. What may it be? Microsoft docs mostly talk about packaging native libraries as Nuget packages which is not the case for me. Thanks
5 Replies
MiKom
MiKom•7mo ago
My MSBuild-foo wasn't good enough to figure out what more is SkiaSharp doing 😦
Dongle
Dongle•7mo ago
Pack only works in NuGet AFAIK You would probably want CopyToOutputDirectory
MiKom
MiKom•7mo ago
It's there in my Content entry. And it is copied, but on publish, the libraries aren't moved from runtimes/[rid]/native to the output directory directly. You mean that I need CopyToOutputDirectory in another place?
Dongle
Dongle•7mo ago
Contents aren't directly copied So put a CopyToOutputDirectory to them
MiKom
MiKom•7mo ago
I got them already in the project that defines them:
<Content Condition="$(OS)=='Unix'" Include="../path/to/x64/library.so" LinkBase="runtimes/linux-x64/native">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Condition="$(OS)=='Unix'" Include="../path/to/x64/library.so" LinkBase="runtimes/linux-x64/native">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
Do you mean that I also need to have the copying in the consumer, the end-application that's getting packaged? Possibly SkiaSharp is doing some Nuget-specific magic that I don't know about I hoped that publish does some automatic copying based on runtimeid folders in runtimes/ but apparently not It seems that this magic is related to the contents of obj/project.assets.json. If something is runtimeTargets there, then MSBuild knows to copy it to proper place. But it seems to be done like that only for Nuget packages :/