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.

26 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.

    Liked by 1 person

  3. Tobias

    Hello,

    when I try to install my package via Nuget Console (or NuGet solution manager) I receive the following error:
    “Could not install package ‘SamplePackage 1.0.0’. 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.”

    When I add a packages.config with the package config:

    everything works like a charm.

    Any idea why it is not working when I try to install via Nuget Console (or manager)? Do you need any further infos?

    Cheers Tobias

    Like

    1. CJohnson

      You cannot use the nuget package manager to install your native package. You have to edit the msbuild files manually, using a text editor. I made this abundantly clear in my article.

      Liked by 1 person

  4. Tobias

    Hello,

    when I try to install my package via Nuget Console (or NuGet solution manager) I receive the following error:
    “Could not install package ‘SamplePackage 1.0.0’. 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.”

    When I add a packages.config with the package config (package id=”SamplePackage” version=”1.0.0″) everything works like a charm.

    Any idea why it is not working when I try to install via Nuget Console (or manager)? Do you need any further infos?

    Cheers Tobias

    Like

  5. 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

  6. Chetan G

    Excellent and very useful.
    I have project that contains 300++ subproject under it also many native library want to create nuget pacakage.
    Any pointers on it.Using Vs2017
    Eg.
    1. Xyz project with .sln contains all 300+ listed and sln.props file currently holding lib path used for build.
    Under Xyz added Abc project having its own
    .Vcxproj project files.

    Like

    1. Chetan G

      Thanks for quick reply.
      Many files can be packaged under nuget however if that pakage need to be used for mutliple projects (300+) so need to update all .vcxprjct files to use this nuget packages.
      Is there any way I can do it using main project sln.props file?
      Which is used by all projects ?

      Like

  7. Chetan G

    Thanks for quick reply.
    Many files can be packaged under nuget however if that pakage need to be used for mutliple projects (300+) so need to update all .vcxprjct files to use this nuget packages.
    Is there any way I can do it using main project sln.props file?
    Which is used by all projects ?

    Like

    1. CJohnson

      You should have already set up your projects to use one master .props file already. If you do that it that is an easy matter to modify that one master .props file to include the nuget project.

      Like

  8. You can actually have the added and packages.config created/updated automatically. You need the name of the .props and .targets files to match the ID, e.g. Google.Filament.props, and the file(s) placed under the build folder.

    Like

  9. Chris

    ” you should be able to do a nuget restore and build your native project”

    The “do a nuget restore” part is kind of the issue, here. If you don’t do nuget restore on the solution prior to loading it, then Visual Sutdio won’t find your .props file and any projects that reference it will fail to load. I’d like to do this, but I’m sure if I tell my co-workers, “lol yeah, sorry about that, you have to do nuget-restore on the solution before you load it whenever you have a clean checkout” I’ll never hear the end of it, even if it is something that they don’t have to do very frequently.

    Like

    1. Chris

      OK, I’ve figured this out. I think some other people commenting might have mentioned some of these things, but I’ll summarize. This worked for me with Visual Studio 2019.

      * Make sure you have named your .props and .targets files the same as the package ID for your nuget package. (In this example that would be Google.Filament.props). I’ll call my package ‘blah’.

      * Create your .nuspec file, setting ‘build\native’ (not just ‘native’) as the root. E.g.

      The nuspec file for Microsoft’s cpprestsdk project (look on github) is a good reference for this.

      * DO NOT EDIT THE .vcxproj FILES. DO NOT CREATE ANY packages.config FILES. Instead, create a nuget.config file in the same directory as your solution that has the ‘enabled’ and ‘automatic’ keys set to “True” for the packageRestore tag.

      * Install the packages for the appropriate projects with Visual Studio (right-click solution, Manage NuGet packages for solution…) After the packages are installed, Visual Studio will add appropriate directives to the .vcxproj files that reference the .props and .targets files from your NuGet package, while allowing the projects to load if they don’t exist.

      * Build the project. Before the build starts, Visual Studio should automatically restore NuGet packages, the props/targets files will now exist, and everything should go smoothly. No need to manually do ‘nuget restore’ or anything like that before you can build.

      Like

  10. Greg Granato

    I have a question about this section of the .props:

    mdd
    md

    Is the assumption that x86 and x64 link the same library?

    I have this situation – the question marks are where I’m stumped and I can’t find doc on LibraryType:

    mdd
    md
    ???
    ???
    ???
    ???

    Please advise.

    Like

  11. Greg Granato

    I have a question about a section in the .props file above. I want to provide for a package that supports libraries for Win32, x64 and ARMhf (via cross compile using VisualGDB). In the .props file there is a PropertyGroup / LibraryType which I’m guessing I would need to expand. I can’t find doc on LibraryType anywhere. Please advise.

    Like

  12. Greg Granato

    It occurred to me that LibaryType (md|mdd) means select the runtime library type md being multi-threaded dll and mdd being multi-threaded debug dll.

    Like

  13. MikeM

    Thanks, this is very valuable!!!

    One question: how do I deal with different versions in platforms (e.g. x64 etc), toolsets (v120, v141), build configurations (Release, Debug) etc.? Do I need to create different NuGet packages for each or can I have one package that contains all? And if I can, how do I control which assemblies are consumed by each alternative? Thanks!!!

    Like

    1. CJohnson

      You should be able to put all of them into a single nuget package. Remember a nuget package is simply a zip file by another name.

      Like

    2. Steven Ford

      To do this; we have the following in our nuget package’s .targets file:

      BorsukCore.lib;DirectXTK.lib;DirectXTKAudioWin8.lib;%(AdditionalDependencies)
      $(MSBuildThisFileDirectory)lib\$(Platform)\Debug;%(AdditionalLibraryDirectories)

      BorsukCore.lib;DirectXTK.lib;DirectXTKAudioWin8.lib;%(AdditionalDependencies)
      $(MSBuildThisFileDirectory)lib\$(Platform)\Release;%(AdditionalLibraryDirectories)

      So as you can see, based off the configuration / platform / toolset combination, we add different built version of the libaries etc.

      If you want to support having multiple platforms (we don’t), then you’d just add further entries in the above etc.

      Like

    3. Steven Ford

      Arg! the original reply commented out all of the XML, so here’s attempt #2

      To support multiple platforms / configurations etc., look at your targets file. You’ll see there’s a series of ‘ItemDefinitionGroup’ nodes which can have an optional ‘Condition’ attribute, e.g.

      Condition=”‘$(Configuration)’ == ‘Debug’ AND ‘$(PlatformToolset)’ == ‘v141’ AND ‘$(Platform)’ == ‘x64′”

      In our nuget package (where we link to the actual libraries and not just the header files), we have multiple entries, one for the debug variant and one for the release variant, each of which points to a different set of library files to link to.

      Obviously, you can use the usual variables in your items, e.g. for ‘AdditionalLibraryDirectories’ we have – $(MSBuildThisFileDirectory)lib\$(Platform)\Release;%(AdditionalLibraryDirectories)

      Like

Leave a comment