-
Notifications
You must be signed in to change notification settings - Fork 269
Guide for packaging C# library using P/Invoke to per-architecture and/or per-platform C++ native DLLs #8623
Description
I am trying to make a NuGet package deploying a C# .NET Standard 2.0 library which does P/Invoke calls into a platform-dependent C++ library, which must therefore also be deployed, but is obviously architecture-dependent (x86, x64, ARM, ARM64), as well as platform-dependent (Desktop (Win32) vs. UWP).
I read most documentations on docs.microsoft.com, issues on this GitHub and others, SO issues, etc. and it is still very unclear how to do this. Information is sparse, sometimes contradictory, and the lack of details on some concepts like TFMs makes the task nearly impossible. This whole thing could really use some detailed documenting and samples.
In no particular order:
Target frameworks
-
https://docs.microsoft.com/en-us/nuget/reference/target-frameworks has a long list of supported frameworks, but
nativeis not included, as reported in NuGet/docs.microsoft.com-nuget#1480. However https://devblogs.microsoft.com/nuget/native-support/ clearly states that:When targeting native projects, a new target framework name is now recognized: native.
-
This page lists
netcoreas a framework with versions like5.0. But .NET Core is just releasing its 3.0 this week. So clearly there is no relation between the two, but there is not a word on it. -
This page also casually mentions the TFM
win10:win10 (not supported by Windows 10 Platform)
There is no explanation on what
win10is (is this Desktop, as opposed to UWP?) nor whywin10would not be supported on Windows 10 despite the name clearly saying otherwise.
Package structure
-
https://docs.microsoft.com/en-us/nuget/create-packages/creating-a-package goes into details about the NuGet package structure, but does not mention anything about native DLLs.
-
https://devblogs.microsoft.com/nuget/native-support/ says that the
build/folder should be used to put the native DLLs, but https://docs.microsoft.com/en-us/nuget/create-packages/creating-a-package says it is only for.targetsand.propsfiles. -
https://devblogs.microsoft.com/nuget/native-support/ says that the
build/folder respects target frameworks, but does that mean thatbuild/native/x86/my.dllvs.build/native/x64/my.dllworks? Or should architecture-specific native DLLs be put inruntimes/? -
Does NuGet support Desktop (Win32) and UWP inside the same package? I could not find any documentation about that. I tried using
runtimes\win10-x86\native\my.dllfor the Desktop variant, andruntimes\win10-x86\lib\uap10.0\my.dllfor the UWP variant of the native DLL, but I get an error error APPX1101: Payload contains two or more files with the same destination path. Why is that? Those are different frameworks, why is NuGet trying to import both files into a UWP project?
P/Invoke
It seems many people have problem with deploying architecture-specific native DLLs. A quick search on nuget.org shows that packages like Microsoft.Net.Native.Compiler have many "runtime" variants starting with e.g. a runtime.win10-x64. prefix, but it doesn't seem there is documentation about this approach.
https://github.com/Mizux/dotnet-native attempts to provide an example using the undocumented runtime.json used by CoreFX, but looking at the example it seems that for each native DLL variant, a specific .NET Standard 2.0 wrapper assembly is needed, instead of using a single one with multiple native DLLs. This sounds very cumbersome, is that the only option?
Related to that, if it is possible to use a single .NET Standard 2.0 assembly, then how to deploy the correct native DLL? With a .NET Core 3.0 sample app, it seems that currently NuGet copies the entire runtimes/ folder inside the bin/ folder of the app, instead of only the appropriate native DLL. This results in multiple copies, and of course the wrong DLL path which prevents DllImport from finding the native DLL.
Other issues
There are many other logged issues that seem partially related:
-
csproj: document how to properly pack platform-specific native assemblies #6645 mentions that
runtimes/{rid}/native does not work with netfx
but there is no context about where that comes from. And it suggests putting native DLLs in
lib/which is reserved for assemblies, so doesn't seem to be a correct solution. -
Absolutely need example of how to include native libraries #6648 closed as duplicate, although the context is not clear (what kind of app / platform is this about?)
-
Support or Documentation of Creating of Packages with Native Applications - PhantomJS #3931 seems to be somewhat related, but uses
project.json(?) -
NuGet for UWP apps cannot call C++ libraries #2350 asks about the P/Invoke and packaging issue, but was closed without answer.
-
Feature Request: Allow architecture-specific binaries without requiring client projects to use RuntimeIdentifier(s) #6846 touches on the deploy problem when consuming the package
-
Improvement: Handle platform specific assemblies (x86, x64 etc) with msbuild conditionals #8573, Issues using nuget packages with C# libraries compiled for specific architecture. #8435, UAP - platform specific binaries not picked up from runtimes #1221, (UWP) Platform Dependent UWP Library fails due to Compile-Time Reference Assembly Missing #5606, ... I didn't read all of them, there are too many.
-
Several issues mention https://stackoverflow.com/questions/49162127/how-to-include-native-assets-in-nuget-on-multiple-platforms-only-64-bit but this seems to be only a subset of the issues, it is not clear how this scales to multiple architectures AND multiple platforms at the same time. It also seems to suggest multiple assemblies are needed.
-
The road of the
AssemblyLoadContextseems to be a runtime solution to a packaging problem, and really not a path I want to get onto.