Cmake and Msvs-Nuget

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

Including nuget packages in VS2019 C++ cross platform program

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

Note, this solution only works for C# or hybrid C#/C++ projects. As mentioned here, Microsoft doesn't support PackageReference for pure C++ projects.


Prior to CMake 3.15, CMake has no built-in commands for Nuget support, so you will have to use the nuget command line utilities to include Nuget dependencies using CMake.

You can use CMake's find_program() to locate the nuget command line utility (once installed), coupled with add_custom_command() or execute_process() to execute nuget commands from CMake. The answers to this question discuss in more detail, but it could essentially look something 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()

This assumes you have an existing packages.config file listing the nuget dependencies for your project.

To tie dependencies to a specific target, you (unfortunately) have to use the full path to where nuget placed the assembly/library.

For .NET nuget packages this would look like this:

# Provide the path to the Nuget-installed references.
set_property(TARGET MyTarget PROPERTY
VS_DOTNET_REFERENCE_MyReferenceLib
${CMAKE_BINARY_DIR}/packages/path/to/nuget/lib/MyReferenceLib.dll
)

For C++-flavored nuget packages, it could look like this:

add_library(MyLibrary PUBLIC
MySource.cpp
MyClass1.cpp
...
)

# Provide the path to the Nuget-installed libraries.
target_link_libraries(MyLibrary PUBLIC
${CMAKE_BINARY_DIR}/packages/path/to/nuget/lib/MyCppLib.dll
)

As an aside, CMake does support the creation of Nuget packages with CPack. Here is the documentation for the CPack Nuget generator.

Nuget package from CMake

CMake 3.15 and above supports referencing Nuget packages with VS_PACKAGE_REFERENCES. 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 older CMake versions, you could try the proposed work-around suggested here.

CMake CSharp reference nuget packges

EDIT: As of CMake 3.15, CMake supports referencing Nuget packages with VS_PACKAGE_REFERENCES. This is a cleaner solution than restoring the Nuget packages manually, and hard-coding the package paths in CMake. The VS_PACKAGE_REFERENCES target property now handles all of that overhead for you.

To add a Nuget package reference to a CMake target, use the package name and package version separated by an underscore _, like this:

set_property(TARGET ${PROJECT_NAME}
PROPERTY VS_PACKAGE_REFERENCES "ExcelDna.Integration_0.34.6"
)

You can grab any version number in a range, with *, and append multiple packages using a semicolon:

set_property(TARGET ${PROJECT_NAME}
PROPERTY VS_PACKAGE_REFERENCES "ExcelDna.Integration_0.34.*;ExcelDna.AddIn_1.0.0"
)

You can use VS_DOTNET_REFERENCE_<YourLibrary> to get CMake to find your Nuget package references. Try this:

set_property(TARGET ${PROJECT_NAME} PROPERTY 
VS_DOTNET_REFERENCE_ExcelDna.Integration
${CMAKE_BINARY_DIR}/packages/ExcelDna.Integration.0.34.6/lib/ExcelDna.Integration.dll
)

Note, the full DLL name must be appended to the VS_DOTNET_REFERENCE_ directive to create the full variable. However, I have not seen a version number independent way to load the Nuget packages, and I've had to manually edit my CMake files to include these packages. You can check out this answer for a more detailed explanation.

CMake's VS_PACKAGE_REFERENCES not adding a reference to VS2017 project

Hmm, apparantly, C++ projects are not supported by PackageReference according to learn.microsoft.com

ASP.NET apps targeting the full .NET Framework include only limited
support for PackageReference. C++ and JavaScript project types are
unsupported.

This makes the whole VS_PACKAGE_REFERENCES option from CMake inapplicable for C++ projects.

How to use NuGet to install Boost

I make a test according to your description and it installed boost correctly.
Sample Image

In your code the error shows that “Project 'Default' is not found”.

Please check this drop down box “Defalut project” in Package Manager Console, this error may occur if the project you selected has already been deleted or unload.
Sample Image

CMake generated MSVC project cannot find symbols even if lib files are correctly generated for their correlated dlls

The line in Root/Sentry.Demo/CMakeLists.txt:

include_directories(SENTRY.Engine/src/engine SENTRY.Core/src/module)

appears to be incorrect. It uses relative paths, so I don't believe these are valid paths in your project:

Root/Sentry.Demo/SENTRY.Engine/src/engine
Root/Sentry.Demo/SENTRY.Core/src/module

Prefer to use absolute paths wherever possible, through use of the CMAKE_SOURCE_DIR variable. This variable provides the path to the top-level source directory. So try something like this instead:

include_directories(
${CMAKE_SOURCE_DIR}/SENTRY.Engine/src/engine
${CMAKE_SOURCE_DIR}/SENTRY.Core/src/module
)

I took another look at your repo, and perhaps more importantly, you must have the full definition of Engine template functions in the header file, not the source file.

So move these function definitions to the header file, within your Engine class definition:

template<typename T_rep, typename T_ratio>
void Engine<T_rep, T_ratio>::Init()
{
for (auto& module_ : Modules)
{
module_->Init();
}
}

template<typename T_rep, typename T_ratio>
void Engine<T_rep, T_ratio>::Run()
{
RunUpdateLoop = true;
auto TPStart = std::chrono::steady_clock::now();
auto TPEnd = TPStart;

while (RunUpdateLoop)
{
auto deltaT = TPEnd - TPStart;
TPStart = std::chrono::steady_clock::now();

for (auto& module_ : Modules)
{
module_->Run((deltaT));
}

TPEnd = std::chrono::steady_clock::now();
}
}

template<typename T_rep, typename T_ratio>
void Engine<T_rep, T_ratio>::RegisterModule(Module<T_rep, T_ratio>* ToRegister)
{
Modules.push_back(ToRegister);
}

This should help get you on the right track.



Related Topics



Leave a reply



Submit