How do I package a portable .NET library in a modern general-purpose way? Let\'s assume I have a single AnyCPU assembly that I wish to make available to any .NET platform th
There seems to have been a lot of churn about the best way to make portable .NET framework libraries/NuGet packages over the past several months. If you can wait a bit, you'll want to read up about the .NET Standard. Immo Landwerth wrote a detailed blog post introducing the .NET Standard in September of 2016. Support for .NET Standard 2.0 in .NET Core is expected to arrive in early 2017, in the same timeframe as Visual Studio 2017, which is currently in Release Candidate stage. The .NET Standard 2.0 will be implemented by .NET Framework, .NET Core, and Xamarin. It will also include compatibility shims for .NET Framework binaries, which should make adoption easier.
Update:
Oren Novotny has a very informative blog post on this subject: Multi-targeting the world: a single project to rule them all. He discusses .NET Standard and how to multi-target with Visual Studio 2017. The blog post was from before the RTM of Visual Studio 2017, but it's very helpful.
This answer builds upon the principles used to package libraries targeting the .NET Framework. Read the linked answer first to better understand the following.
To publish the portable .NET library, you will need to create a NuGet package with the following structure:
\---lib
\---dotnet
MyPortableLibrary.dll
MyPortableLibrary.pdb
MyPortableLibrary.XML
All three files will come from your project's build output directory under the Release build configuration.
The dotnet
directory in the structure above has a special meaning - it indicates to NuGet that the files in the directory are to be used on any platform that all your package's dependencies are compatible with. Therefore, your package is automatically usable on any .NET platform that supports all your dependencies (e.g. .NET Core).
The crucial next step is to determine the list of dependencies. Due to a package management issue it is not possible to simply declare a dependency on .NET Core itself (.NET Core is the API surface shared by all the .NET platforms). Instead, you must manually determine each .NET Core component dependency and add it to the nuspec file.
The dependency detection process for .NET Core packages consists of two steps:
Visual Studio does not provide the information you need. Instead, you need to build your library and inspect the resulting DLL file. The following PowerShell script will display the references of a .NET assembly:
Get-ChildItem MyPortableLibrary.dll | % { [Reflection.Assembly]::LoadFile($_.FullName).GetReferencedAssemblies() | % { $_.Name + ".dll" } }
The output of this command will be a list of assembly names, for example:
System.Runtime.dll
System.Resources.ResourceManager.dll
System.Numerics.Vectors.dll
Once you have obtained the list, open the project.lock.json file in your project directory. This file contains information about all NuGet packages used by your project. You will find, among other data, various blocks of JSON such as the following:
"System.Numerics.Vectors/4.1.0": {
"dependencies": {
"System.Globalization": "[4.0.10, )",
"System.Resources.ResourceManager": "[4.0.0, )",
"System.Runtime": "[4.0.20, )",
"System.Runtime.Extensions": "[4.0.10, )"
},
"frameworkAssemblies": [
"mscorlib",
"System.Numerics"
],
"compile": {
"ref/net46/System.Numerics.Vectors.dll": {}
},
"runtime": {
"lib/net46/System.Numerics.Vectors.dll": {}
}
},
This block of JSON indicates that the assembly files listed under "compile" are provided by the package listed in the top level value (System.Numerics.Vectors version 4.1.0). Use this information to map every referenced assembly to a NuGet package. Note that while the package and assembly names are often the same, this is not always the case!
For any NuGet packages that are not part of .NET Core, you can skip the above process as you already know the exact package you have a dependency on. The dependency detection logic described here is only required because you cannot declare a dependency directly on .NET Core (the Microsoft.NETCore package) due to the issue linked above.
Now simply list all the dependencies in your nuspec file, based on the following example:
<?xml version="1.0"?>
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata minClientVersion="3.2">
<id>Example.MyPortableLibrary</id>
<version>1.0.0</version>
<authors>Firstname Lastname</authors>
<description>Example of a portable library with NuGet package dependencies.</description>
<dependencies>
<dependency id="System.Numerics.Vectors" version="4.1.0" />
<dependency id="System.Resources.ResourceManager" version="4.0.0" />
<dependency id="System.Runtime" version="4.0.20" />
</dependencies>
</metadata>
<files>
<file src="..\bin\Release\MyPortableLibrary.*" target="lib\dotnet" />
</files>
</package>
That's it! The resulting package is usable on any compatible .NET platform, such as .NET Framework 4.6 or Universal Windows Platform. Remember to build your solution using the Release configuration before creating the NuGet package.
A sample library and the relevant packaging files are available on GitHub. The solution corresponding to this answer is PortableLibrary.
Refer to Lucian Wischik's blog for a deeper dive into the logic that operates on such NuGet packages.