How to make a Nuget package for C++

Making a nuget package for managed code is really straightforward, since it is so extensively documented on Microsoft’s various websites.

But if you want to make a nuget package that contains native code like libraries (*.lib), headers (*.h) you are almost out of luck! Microsoft will give you about 3 minuscule paragraphs full of cryptic junk for documentation!

https://docs.microsoft.com/en-us/nuget/guides/native-packages

The first key to understand how to put C++ stuff into a nuget package is to understand that a nuget package (*.nupkg) is really just a zip file that has been renamed. Therefore you should just be able to stick anything in there that you like.

The second key is that we will just be using Nuget to download and unzip the nuget package for us. After that we are on our own.

The third key is that none of the Visual Studio versions will offer any aid at all in hooking up the nuget package to the project that needs it. It is up to you to break open your text editor and modify your visual studio project files (*.vcxproj etc.).

Steps

Here is a high-level summary of what needs to be done, and will be explained in detail:

  1. Gather or stage your native library files into a folder of your choosing.
  2. Create a *.nuspec file in that folder.
  3. Edit the *.nuspec file to include the files you want to include in your package.
  4. Create a *.props file
  5. Call nuget pack to create the package.
  6. Push the nuget package to a feed somewhere.
  7. Create a packages.config file.
  8. Edit the visual studio project file to point to where the restored nuget package is.

NOTE: In this document I will be using the Google Filament renderer library as my demonstration example, since I had to do that here at work recently.

Stage the native files

This should be easy, copy all the files you need for your native library to a a convenient folder. For example:

  • bin/*
  • docs/*
  • include/*
  • lib/*
  • README.md

Create the *.nuspec file

I like to put this *.nuspec file inside of the directory that has the code I’m packaging up. That makes it easier and simplifies the paths we will put inside the *.nuspec file. For my example, mine is filament.nuspec:

  • bin/*
  • docs/*
  • include/*
  • lib/*
  • README.md
  • filament.nuspec

Edit the *.nuspec file

A nuspec files is an xml file and hence must follow the syntax requirements as documented on microsoft’s website:

https://docs.microsoft.com/en-us/nuget/reference/nuspec

I chose to have the package contents follow the same layout as how Google gives them to me. Thus my nuspec package looks like this:

<?xml version="1.0"?>
<package >
	<metadata>
		<id>Google.Filament</id>
		<version>2019.08.08</version>
		<description>Google Filament Renderer</description>
		<tags>Native, native</tags>
	</metadata>
	
	<files>
		<file src="lib\**\*.*"       target="native\lib"     />
		<file src="include\**\*.*"   target="native\include" />
		<file src="docs\*"           target="native\docs"    />
		<file src="bin\*"            target="native\bin"     />
		<file src="README.md"        target="native"      />
		<file src="filament.props"   target="native" />
	</files>
</package>

The most important part here is the <tags> element that contains the text ‘native’. To be doubly sure I got it right, I added it twice, the first time with a capital ‘N’. This is useful when the package is hosted in nuget.org, you can do a search for native packages by using the search term: tag:native

Each <file> xml element has two attributes: a ‘src’ attribute and a ‘target’ attribute. Source is where it gets it’s files from, and Target is where the files will be placed when the nuget package is restored or unzipped. What you specify here is completely up to you. As you can see in my example above, I have also created a filament.props file. I will be using this later to make it simpler for any project to consume this nuget package.

Also I added a root folder called ‘native’. It worked with it, I haven’t tested it without a root folder. But you can give this root folder any arbitrary name you want.

Create a filament.props file

This will aid us later on in consuming the nuget package from another C++ project. Here we will create an MSBuild file (NOT a .vcxproj file) that will describe where the include files and library files are. This step requires a good understanding of the MSBuild xml syntax and especially a good knowledge of C++ project file (i.e. *.vcxproj) syntax. If you don’t know that, just copy and paste my code here:

<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" 
        ToolsVersion="15.0">
  <PropertyGroup>
    <LibraryType Condition="'$(Configuration)'=='Debug'">mdd</LibraryType>
    <LibraryType Condition="'$(Configuration)'=='Release'">md</LibraryType>
  </PropertyGroup>
  <ItemGroup>
  <FilamentLibs Include="$(MSBuildThisFileDirectory)\lib\x86_64\$(LibraryType)\*.lib" />
  </ItemGroup>
  <PropertyGroup>
    <!-- Expland the items to a property -->
    <FilamentLibraries>@(FilamentLibs)</FilamentLibraries>
    </PropertyGroup>
    <ItemDefinitionGroup>
    <ClCompile>	<AdditionalIncludeDirectories>$(MSBuildThisFileDirectory)\include</AdditionalIncludeDirectories>
    </ClCompile>
    <Link>
      <AdditionalDependencies>$(FilamentLibraries);%(AdditionalDependencies) 
      </AdditionalDependencies>
    </Link>
  </ItemDefinitionGroup>
</Project>

This specifies all the library files using a wildcard pattern (*.lib) and it points to where the include directory is too. It uses the special reserved msbuild property $(MSBuildThisFileDirectory) which helps anchor the paths that are being used inside of this file.

Call Nuget Pack

Now comes the fun part, to create the nuget package. I run a simple batch script like this:

NuGet.exe pack filament.nuspec -OutputDirectory builds\nuget\x86_64

Thereupon I can look in my builds\nuget\x86_64 directory and see a nuget package named Google.Filament.2019.8.8.nupkg

Call Nuget Push

Now that the package is created, it’s time to push it up to a nuget feed. Which nuget feed you push up to is up to you, and is none of my business. But nuget.org is the defacto source. But if you have a private feed you use for your company that works too. I run a simple batch script like this:

NuGet.exe push builds\nuget\x86_64\*.nupkg -Source https://<some nuget url> -Apikey <some api key>

Create a packages.config file

This part is super easy. Create a packages.config file in the same directory as the visual studio project file (*.vcxproj) that will be consuming the nuget package.

Place the following xml snippet into the file:

<?xml version="1.0" encoding="utf-8"?>
<packages>
  <package id="Google.Filament" version="2019.8.8" />
</packages>

Notice how the version matches what was put inside the *.nuspec file.

Edit the visual studio project file

This step involves a text editor (I prefer Notepad++ or Visual Studio Code). This step is also markedly different from what we would use if we were using visual studio to find a managed .net nuget package for a managed .net project.

But since this is a native project, we don’t get the big boy tools, and have to settle for the hand-me-downs from Microsoft.

First we will open the *.vcxproj file that will be consuming this project, and will simply add the following line of xml code at the end of the file:

<Import Project="<Your package directory>\Google.Filament.2019.8.8\native\filament.props" />

Where your packages go, is up to you when you do a nuget restore. So where-ever that is, you will have to change the snippet <Your package directory> to point to where-ever it was that nuget unpacked the files. This can be a relative or an absolute path, so it’s up to you. But in the end, msbuild needs to be able to find that *.props file, otherwise it won’t load in visual studio.

Summary

And that is it. The package is done, and you should be able to do a nuget restore and build your native project. Many of the steps were the same as creating a managed .NET nuget package. With the exception of the manual editing of the native project files.

If you are looking for more examples of nuget packages holding native projects, do a search on nuget.org, and find a project. Browse to the page for the nuget package and find the link to the ‘project’ code page for it. Usually on github.com. Click on the link to open the project in Github and then hunt around for a *.nuspec file.

5 thoughts on “How to make a Nuget package for C++

  1. Thank you for sharing this information. Indeed Microsoft docs do not through light on situations different than the standard ones.
    My case is similar to what you describe but with a swap.
    I have a DLL made in C/C++ that I am distributing via NuGet
    https://www.nuget.org/packages/libSoftMeter/
    and I wish to be consumed by any type of project (as it is a standard DLL).
    I did the nuget package via NuGet Package Explorer but when trying to consume it via a C# I get errors like

    Warning NU1701 Package ‘libSoftMeter 1.2.1’ was restored using ‘.NETFramework,Version=v4.6.1, .NETFramework,Version=v4.6.2,
    .NETFramework,Version=v4.7, .NETFramework,Version=v4.7.1, .NETFramework,Version=v4.7.2, .NETFramework,Version=v4.8’
    instead of the project target framework
    ‘.NETCoreApp,Version=v3.0’.
    This package may not be fully compatible with your project.

    Any pointers on what to look for, or if this attempt is doomed?

    Like

  2. Thank you for a very helpful article, it helped us a lot.

    However, one comment, if you name your .props (or .targets) file to be the same as the name of the package, i.e. Google.Filament.props rather than filament.props, then VS will automatically update your C++ project file if you add the nuget reference.

    Like

  3. In your nuspec file I suffered from amissing comma error. This:

    Native native

    should be:

    Native, native

    I got an error when pushing it the package.

    You are trying to install this package into a project that targets ‘native,Version=v0.0’, but the package does not contain any assembly references or content files that are compatible with that framework. For more information, contact the package author.

    Like

Leave a Reply to CJohnson Cancel reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s