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:

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 and NNanomsg.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 a content/ 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

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 and runtimes/win-x64/native/nanomsg.dll, respectively. If Platform target (project Properties->Build) of the consuming project is Any CPU neither native assembly will be copied to the output directory. You must select either x86 or x64:

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.