NuGet Integration with CMake

Am excited to get this template running, since with this template, we’ll be able to appreciate the support that CMake provides for NuGet package manager.

The objectives of this demo are the following:

  1. Demonstrate how to automatically install or restore NuGet packages hosted on NuGet.org using CMake
  2. These NuGet packages would later be used by the template project. Hence, demonstrate how to locate these NuGet packages on disk
  3. Learn a bit about CMake modules, as we would be requiring to create one

More on NuGet

NuGet is Microsoft-supported way of sharing code. NuGet.org lists gallery that is used for publicly hosting NuGet packages that could be consumed by other users

The NuGet Package of Interest

I’ll more than happy to demonstrate installing or restoring NuGet Package named ‘Pvs.SoftTools.SystemC.1.0.0’. This package provides the SystemC include headers and compiled libraries. This is a C++ library. The user shall be able to use SystemC features once SystemC NuGet package is successfully integration into the build environment using CMake instructions.

Link to NuGet Package : My First Experimental SystemC NuGet Package

NOTE: Detailing on creation of NuGet packages is not the goal of this project, and hence, not within the current scope.

Few Final Strides Before We Dive In …

The current directory structure looks like this

08_using_NuGet
|
|..... CMakeLists.txt
|
cmake_modules
|
|..... Find<package_name>.cmake

Once the demo template is executed, one would find new software packages getting installed. And the newer directory structure would look like this

08_using_NuGet
|
|..... CMakeLists.txt
|..... packages.config
|
cmake_modules
|
|..... Find<package_name>.cmake
|
pvs_nuget_packages

Application Source

The demo template uses nuget sources as shown below.

#inclue "systemc.h"

int sc_main(int sc_argc, char * sc_argv[])
{
  std::cout << "\nRunning this SystemC demo example by using Pvs.SoftTools.SystemC NuGet package" << std::endl;
  return 0;
}

As one could notice, I’m using the systemc.h include header file and the function sc_main as prescribed by the SystemC library.

Until and unless, these include headers are not available to the source and SystemC library is not available to link against, compiling the above code snippet just like a normal single source file would report in compilation error(s).

But, SystemC is an external package, so how does the user download the package ?

Installing / Restoring NuGet Package “Pvs.SoftTools.SystemC.1.0.0”

User is requested to install the NuGet CLI tool for this activity. NuGet reads the list of packages to be downloaded and installed by reading packages.config file.

The basic template for making entries into packages.config is as shown below.

<?xml version="1.0" encoding="utf-8"?>
<packages>
  <package id="Pvs.SoftTools.SystemC" version="0.1.0" />
</packages>

With the default settings of the NuGet CLI that is installed, one could make use of nuget-restore or nuget-install command with the above packages.config as the input to download the packages from NuGet.org to the local disk.

CMake provides a command find_program to query if the NuGet CLI is installed.

find_program(NUGET nuget)

if (NOT NUGET)
  message(FATAL_ERROR "<Report Error Message Here>")
endif()

In order to accomplish the objective of this template, first we have to make the demo CMakeLists.txt file to point to the directory pvs_nuget_packages that would contain the installed packages.

set(
  PVS_NUGET_PACKAGES
  ${PROJECT_SOURCE_DIR}/../pvs_nuget_packages
)

file(MAKE_DIRECTORY ${PVS_NUGET_PACKAGES})

The above commands instruct CMake to create an empty directory pvs_nuget_packages if it doesn’t exist.

If the NuGet CLI is FOUND, one needs to be aware of the following:

  1. NuGet CLI uses packages.config file in order to identify packages to be restored or installed and need to be placed alongside the * (.sln)*. For this, one may make use of CMake custom command `configure_file`
  2. Restoring or installing the NuGet packages into the directory specified. For this, user may use add_custom_target command in order to prepare the command to be run on the command line. Also, we need to make the solution file dependent on this nuget command execution. It is only then that the building of the target (executable) will invoke the custom target to be executed and running the custom target, restores the NuGet package onto the local disk at the specified location.

The following listfile rules do the above mentioned.

add_exectuble(${PROJECT_NAME} ${<list_of_sources>})

if (NUGET)
  # Copies the 'packages.config' file to the directory listing the solution
  configure_file(
    packages.config
    ${CMAKE_SOURCE_DIR}/build/packages.config COPYONLY
  )
  # Adds a custom target 'nuget-restore'
  add_custom_target(
    nuget-restore
    COMMAND NUGET restore ${PROJECT_SOURCE_DIR}/build/${PROJECT_NAME}.sln
    -PackagesDirectory ${PVS_NUGET_PACKAGES}
  )
endif()

add_dependencies(
  ${PROJECT_NAME}
  nuget-restore
)

Since, the demo performs out-of-source build, the packages.config needs to be copied into build directory. That is what is being performed by configure_file command.

Next, nuget-restore is the custom target that is to be created and later, solution bulid is make dependent on this custom target. add_custom_target and add_dependencies are demonstrating that behavior.

It’s Game Time …

Run the executable and one must see the following strings getting printed on the console.

        SystemC 2.3.2-Accellera --- Sep 15 2018 11:52:36
        Copyright (c) 1996-2017 by all Contributors,
        ALL RIGHTS RESERVED

Running this SystemC demo example by using Pvs.SoftTools.SystemC NuGet package

Yay !! :)