CMake and MsVS-NuGet

c++, cmake, nuget-package, visual-studio-2012

Solution

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:

- Installed the Nuget package dependency using Nuget Package Manager in Visual Studio.

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

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

- 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"
)

Problem

I'm currently developing a desktop application, using the C++ REST SDK (codename Casablanca), Qt5 and some other libraries. For the project setup, I use CMake. How to get CMake to install NuGet Packages? I now have to install it each time manually, if I rerun CMake, which isn't really an option.

Original source

Related problems