Description
cc: @tannergooding @richlander @jkotas
This is yet another issue regarding how to best author native library nuget packages and define, build, test, publish deploy applications that consume these. I have tried hard to wrap my head about this by reading many issues and studying existing packages. I have a particular need that is similar to TorchSharp
with massive native libraries that not only need to be split into fragments but also where if possible it would be best only to "download" the runtime identifier (RID) specific packages needed for local development. (But on windows that local development often means BOTH x86 and x64 in our case).
Below I wrote a walk-through I did of using ClangSharp
(in excessive detail for reference) and the many questions that it raised for me compared to how I am used to working with this (based on our own way of authoring native library packages that are explicitly copied to sub-directories (x64
, x86
) alongside exe
and with those directories then added at runtime based on the process arch/os/system to dll directories i.e. via AddDllDirectory
. Having something "custom" is a maintenance issue of course, but also an on-boarding issue. Using documented best practices would be best, but as far as I can tell there are none?
In any case, at the end of the walk-through I encounter the problem that when specifying multiple RIDs i.e.
<RuntimeIdentifiers>win-x64;win-x86</RuntimeIdentifiers>
then the runtime.json
trick does not appear to work when running unit tests from inside Visual Studio. I have to explicitly add the RID specific nuget packages anyway, so I then wonder how exactly is one supposed to author nuget packages to be able to support running multiple RIDs (in this case solely interested in win-x86 and win-x64 for now) with full support for it as usual in VS and other tools? We need to be able to debug and run from VS?
And how do you switch which RID you run with when F5 running in VS?
Should I simply accept that the runtime.json
way is too flawed and explicitly reference all needed nuget packages? Would this then avoid the need to specify RIDs? Which also has issues with "forcing" self-contained (we don't want that), in fact we'd like to simply be able to deploy/copy-paste build output as something like:
App.exe
win-x64\
// win-x64 specific native libraries
win-x86\
// win-x86 specific native libraries
where the app is not RID specific (framework-dependent of course). And this should work on both win-x86/winx64. This is what we have now and what works. Our developers are used to this. But it's based on native library nuget packages that explicitly copy their native library contents to those folders and of course referencing all those RID specific ones. I had hoped perhaps one could avoid the RID specific referencing, but that does not seem to work "smoothly". Which I'd guess then means the whole runtime.json
is not the way to go.
Secondly, I think I read somewhere (can't find or remember where) that for .NET 8 it is considered to force a specific RID on build? I can see given my experience below why one might consider doing that, but that would then raise other issues such as losing what used to be a core tenant (IMHO) of .NET which is that a build output (not publish) is RID agnostic. Would that be lost then?
All in all, to solve these issues I have to author my own little tool for packaging the native libraries, consider all the issues around consumption, testing etc. And after going through all this I am still left with feeling rather lost 😅 I still don't know exactly what is the best solution here. And the packages I am creating are intended to be published for the public, e.g. so I can publish the revived CNTK packages I've made on nuget.org for example.
On top of this we still want to support publishing RID specific applications, but then we don't want native libraries embedded in single file, there is an option for that which is great, but then we want those dlls in sub-folder, not directly next to the exe
, which means we have to hack around that in MSBuild and then face issues with mixed-mode assemblies etc. Yes, we also have those which also makes things very interesting.
ML/AI isn't going away. For each new CUDA or whatever release the native libraries double in size (minimum!). Easy authoring and consumption of those would be great, but I am sure also won't be solved in the immediate future, I need to know what to do now?
The walk-through will come as the next comment.
Links
- "[Feature] Increase the package size limit on NuGet.org from 250 MB"
[Feature] Increase the package size limit on NuGet.org from 250 MB NuGet/NuGetGallery#9473
This features a discussion on how to split a nuget package and links. - TorchSharp - package uses primary/fragment trick
https://www.nuget.org/packages/libtorch-cuda-11.7-win-x64/ - SciSharp.TensorFlow.Redist - adopts same trick
https://www.nuget.org/packages/SciSharp.TensorFlow.Redist-Linux-GPU#dependencies-body-tab - "NuGet 3: The Runtime ID Graph" - discusses
runtime.json
https://natemcmaster.com/blog/2016/05/19/nuget3-rid-graph/ - "Improve handling of native packages (Support RID specific dependencies)" - shows how libclang uses this trick
Improve handling of native packages (Support RID specific dependencies) NuGet/Home#10571
https://www.nuget.org/packages/libclang
https://www.nuget.org/packages/libclang.runtime.win-x64/ - "MSBuild inline tasks with RoslynCodeTaskFactory" - need to join file fragments
https://learn.microsoft.com/en-us/visualstudio/msbuild/msbuild-roslyncodetaskfactory?view=vs-2022 - "Shipping a cross-platform MSBuild task in a NuGet package"
https://natemcmaster.com/blog/2017/07/05/msbuild-task-in-nuget/ - "Architecture-specific folders like runtimes//native/ outside of NuGet packages [nativeinterop]"
Architecture-specific folders likeruntimes/<rid>/native/
outside of NuGet packages [nativeinterop] #24708 - "Should runtime. packages be listed in NuGet.org?"
Shouldruntime.
packages be listed in NuGet.org? core#7568 - "Create a nuget package"
Create a nuget package vincenzoml/SimpleITK-dotnet-quickstart#1 - "Add a way to list native assets that a project will load from the app directory (list them in deps.json)"
Add a way to list native assets that a project will load from the app directory (list them in deps.json) #11373 - "Guide for packaging C# library using P/Invoke to per-architecture and/or per-platform C++ native DLLs"
Guide for packaging C# library using P/Invoke to per-architecture and/or per-platform C++ native DLLs NuGet/Home#8623 - Mizux
dotnet-native
template git repository
https://github.com/Mizux/dotnet-native