CMake and MsVS-NuGet

EDIT: This answer does not apply to C++ projects.

EDIT: As of CMake 3.15, CMake supports referencing Nuget packages with VS_PACKAGE_REFERENCES. Now, this is a much cleaner solution than the work-around proposed earlier below. To add a Nuget package reference to a CMake target, use the package name and package version separated by an _ underscore. Here is an example for BouncyCastle version 1.8.5:

set_property(TARGET MyApplication
    PROPERTY VS_PACKAGE_REFERENCES "BouncyCastle_1.8.5"
)

The documentation shows how you can add multiple Nuget packages by semicolon-delimiting ; the packages.


For CMake versions older than 3.15, here is one potential work-around:

Kudos to @Markus Mayer for the excellent answer. I ran with the suggestion, but found that using the nuget command line doesn’t hold up if you’re generating your VS project/solution files from scratch. Specifically, the nuget command line does not update the project files (.csproj), and some manual effort seems necessary to tell your project where to find the installed dependencies. The following steps outline how I worked around this for a simple project with one Nuget package dependency:

  1. Installed the Nuget package dependency using Nuget Package Manager in Visual Studio.
  2. Copied the generated packages.config file (generated in the same directory as the affected .csproj file). Placed the copy into a source directory, and renamed it as packages.config.in. Now, we can tell CMake to copy this file back to the CMake binary directory during the configure stage with configure_file. Nuget will use it to install/restore dependencies, if they are missing.
  3. Took note of where Nuget installed the DLL for this package. On my machine, this was in the CMake binary directory in a packages directory.

We can use this path to tell CMake our reference package’s location:

set_property(TARGET MyApplication PROPERTY VS_DOTNET_REFERENCE_MyReferenceLib
    ${CMAKE_BINARY_DIR}/packages/path/to/lib/MyReferenceLib.dll)

We can also see where this dependency is installed in the .csproj file to verify we got the correct path (see HintPath), and didn’t miss any other dependencies:

<Reference Include="MyReferenceLib, Version=2.5.0, Culture=neutral, PublicKeyToken=1234567891234567, processorArchitecture=MSIL">
  <HintPath>packages\path\to\lib\MyReferenceLib.dll</HintPath>
</Reference>
  1. Uninstalled the Nuget package, because now we have all the information necessary to let CMake do all the heavy lifting.

Putting it together, the CMake commands look like this:

# Find Nuget (install the latest CLI here: https://www.nuget.org/downloads).
find_program(NUGET nuget)
if(NOT NUGET)
    message(FATAL "CMake could not find the nuget command line tool. Please install it!")
else()
    # Copy the Nuget config file from source location to the CMake build directory.
    configure_file(packages.config.in packages.config COPYONLY)
    # Run Nuget using the .config file to install any missing dependencies to the build directory.
    execute_process(COMMAND 
        ${NUGET} restore packages.config -SolutionDirectory ${CMAKE_BINARY_DIR}
        WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
    )
endif()
# Provide the path to the Nuget-installed references, in case this is a fresh project build.
set_property(TARGET MyApplication PROPERTY 
    VS_DOTNET_REFERENCE_MyReferenceLib
    ${CMAKE_BINARY_DIR}/packages/path/to/lib/MyReferenceLib.dll)

While this will now also provide package paths to the VS project for fresh CMake builds, there is one caveat. If you want to upgrade the version of the Nuget-installed package you are using, you’ll have to re-do the aforementioned manual steps.

TL;DR: Here is the full CMakeLists.txt file that I tried out with Nuget-installed SQLite:

cmake_minimum_required(VERSION 3.8)

# Project name
project(MyProject LANGUAGES CSharp)

# Include CMake utilities for CSharp, for WinForm and WPF application support.
include(CSharpUtilities)

set(MyProject_SOURCES 
    Form1.cs
    Form1.Designer.cs
    Form1.resx
    Program.cs
    Properties/AssemblyInfo.cs
    Properties/Resources.Designer.cs
    Properties/Resources.resx
    Properties/Settings.Designer.cs
    Properties/Settings.settings
)

# Define the executable, including any .cs files. 
# The .resx and other Properties files are optional here, but including them makes them visible in the VS solution for easy editing. 
add_executable(MyWinFormApp ${MyProject_SOURCES})

# Set the source file properties for Windows Forms use.
csharp_set_windows_forms_properties(${MyProject_SOURCES})

# Find Nuget (install the latest CLI here: https://www.nuget.org/downloads).
find_program(NUGET nuget)
if(NOT NUGET)
    message(FATAL "CMake could not find the nuget command line tool. Please install it!")
else()
    # Copy the Nuget config file from source location to the CMake build directory.
    configure_file(packages.config.in packages.config COPYONLY)
    # Run Nuget using the .config file to installing any missing dependencies to the build directory.
    execute_process(COMMAND 
        ${NUGET} restore packages.config -SolutionDirectory ${CMAKE_BINARY_DIR}
        WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
    )
endif()

# Set the .NET Framework version for the executable.
set_property(TARGET MyWinFormApp PROPERTY VS_DOTNET_TARGET_FRAMEWORK_VERSION "v4.6.1")

# Provide the path to the Nuget-installed references, in case this is a fresh project build.
set_property(TARGET MyWinFormApp PROPERTY 
    VS_DOTNET_REFERENCE_EntityFramework 
    ${CMAKE_BINARY_DIR}/packages/EntityFramework.6.2.0/lib/net45/EntityFramework.dll)
set_property(TARGET MyWinFormApp PROPERTY 
    VS_DOTNET_REFERENCE_EntityFramework.SqlServer 
    ${CMAKE_BINARY_DIR}/packages/EntityFramework.6.2.0/lib/net45/EntityFramework.SqlServer.dll)
set_property(TARGET MyWinFormApp PROPERTY 
    VS_DOTNET_REFERENCE_System.Data.SQLite
    ${CMAKE_BINARY_DIR}/packages/System.Data.SQLite.Core.1.0.110.0/lib/net46/System.Data.SQLite.dll)
set_property(TARGET MyWinFormApp PROPERTY 
    VS_DOTNET_REFERENCE_System.Data.SQLite.EF6 
    ${CMAKE_BINARY_DIR}/packages/System.Data.SQLite.EF6.1.0.110.0/lib/net46/System.Data.SQLite.EF6.dll)
set_property(TARGET MyWinFormApp PROPERTY 
    VS_DOTNET_REFERENCE_System.Data.SQLite.Linq
    ${CMAKE_BINARY_DIR}/packages/System.Data.SQLite.Linq.1.0.110.0/lib/net46/System.Data.SQLite.Linq.dll)

# Add in the .NET reference libraries.
set_property(TARGET MyWinFormApp PROPERTY VS_DOTNET_REFERENCES
    "System"
    "System.Core"
    "System.Data"
    "System.Windows.Forms"
)

Leave a Comment