Recently started looking at publishing our C# libraries to nuget.org. This first post covers my experience with the actual packaging. In a later post I’ll do the signing and publishing.
In the Beginning
Subor.NNanomsg.NETStandard (our fork of NNanomsg- a C# library for nanomsg) was particularly frustrating because it involves a native library nanomsg.dll
.
Google un-earthed several promising resources:
- Stackoverflow #1 and #2
- MS docs on using nuget to create native packages
- Blog by former SignalR/ASP.NET guy
But I struggled making a package that worked; nanomsg.dll
wasn’t in the output directory.
General Tips
-
The
.targets
file mentioned in several places seems to no longer be necessary (VS 15.7.4 and nuget.exe 4.7.0) unless your package will be consumed by c++ projects. -
Managed libraries need to get packaged in
/lib/TFM/
. See Supporting multiple .NET framework versions and about Target Framework Monikers (TFM) -
Native libraries need to get packaged in
/runtimes/RID/native/
. Runtime ID Catalog not only has a complete list of all Runtime IDs (RIDs), but also gives a pretty detailed explanation. - Familiarize yourself with nuget.exe
nuget pack -Properties Configuration=Release
will build a release nupkg (default is debug) for the.csproj
in the current directory. The output assembly will automatically be included.- Package version can be set by adding
-Version X.Y.Z
- The NuGetPackageExplorer gives a nice visual look at your nupkg and its meta-data. Just make sure to File->Close otherwise nuget.exe and Visual Studio will have problems accessing your nupkg.
Nuspec
nuget.exe spec
will generate a template nuspec from csproj in current directory- A nuspec file with the same name as the csproj will automatically get included when using nuget.exe from the command line. For example,
NNanomsg.NETStandard.csproj
andNNanomsg.NETStandard.nuspec
. - Replacement tokens usable in nuspec
- Using a nuspec together with a csproj is clumsy
- There’s only replacement tokens for some meta-data values
- Omitting a tag from the nuspec results in the value being undefined- even if it is set in the csproj
- Enabling generation of NuGet package on build (placed in output folder) doesn’t use the nuspec:
-
When using nuget.exe, a
<files>
section of the nuspec seems to be the only way to include/runtimes/
files.<Content>
in csproj places things in acontent/
subfolder (nuget.exe ignores<ContentTargetFolders>
),<None>
doesn’t include them at all. - You can directly pack the nuspec (e.g.
nuget pack NNanomsg.NETStandard.nuspec
)- If you see the following warning:
WARNING: NU5100: The assembly 'bin\Release\netstandard2.0\NNanomsg.NETStandard.dll' is not inside the 'lib' folder and hence it won't be added as a reference when the package is installed into a project. Move it into the 'lib' folder if it needs to be referenced.
You likely need the following in your nuspec:
<files> <file src="bin\$configuration$\netstandard2.0\*.dll" target="lib\netstandard2.0\"/> <file src="runtimes\**" target="runtimes/"/> </files>
- If you get the following error:
Attempting to build package from 'NNanomsg.NETStandard.nuspec'. Value cannot be null or an empty string. Parameter name: value
The nuspec contains a replacement token (i.e.
$xxx$
) that needs to be replaced with an actual value
- If you see the following warning:
NuGet as MSBuild targets
Starting NuGet 4.0, all package-related metadata can be stored in the csproj.
If your project doesn’t reference any external packages you might need to force the PackageReference format by adding to your csproj:
<PropertyGroup>
<RestoreProjectStyle>PackageReference</RestoreProjectStyle>
</PropertyGroup>
Not sure if it matters, but I use PackageReference by default (Tools->Options->NuGet Package Manager):
I include the native binaries with:
<ItemGroup>
<Content Include="runtimes\**" PackagePath="runtimes" Visible="false" />
</ItemGroup>
And then right-click the project->Pack to generate the nupkg. Or in a VS Developer Command Prompt:
msbuild /t:pack
Consuming nupkg
-
In Visual Studio, Tools->Options->NuGet Package Manager->Package Sources to add a local directory as source for nuget packages:
-
Visual Studio Package Manager (View->Other Windows->Package Manager Console) makes it easy to reload a local nupkg. Make sure Default project is set to the correct project and run:
Install-Package D:\dev\NNanomsg\NNanomsg.NETStandard\NNanomsg.NETStandard.0.5.2.nupkg
. -
Installing the package doesn’t cause the native binaries to get copied, you have to build the project.
-
Assuming your nupkg contains 32-bit and 64-bit native libraries,
runtimes/win-x86/native/nanomsg.dll
andruntimes/win-x64/native/nanomsg.dll
, respectively. If Platform target (project Properties->Build) of the consuming project isAny CPU
neither native assembly will be copied to the output directory. You must select eitherx86
orx64
:
Conclusions
- Least frustrating to pick either nuspec or VS project for package definition- don’t use both
- Nuspec has diminished VS integration, but simpler command line use- just need nuget.exe
- Using VS project for package definition has best VS integration, but then stuck dealing with complexities of msbuild
Next I’ll go over signing our package and pushing it to nuget.org.