Skip to main content

Install and use packages with CMake in Visual Studio

This tutorial shows you how to create a C++ "Hello World" program that uses the fmt library with CMake, kmpkg and Visual Studio. You'll install dependencies, configure, build, and run a simple application.

Prerequisites

1 - Set up kmpkg

  1. Clone the repository

    The first step is to clone the kmpkg repository from GitHub. The repository contains scripts to acquire the kmpkg executable and a registry of curated open-source libraries maintained by the kmpkg community. To do this, run:

    git clone https://github.com/kumose/kmpkg.git

    The kmpkg curated registry is a set of over 2,000 open-source libraries. These libraries have been validated by kmpkg's continuous integration pipelines to work together. While the kmpkg repository does not contain the source code for these libraries, it holds recipes and metadata to build and install them in your system.

  2. Run the bootstrap script

    Now that you have cloned the kmpkg repository, navigate to the kmpkg directory and execute the bootstrap script:

cd kmpkg && bootstrap-kmpkg.bat

The bootstrap script performs prerequisite checks and downloads the kmpkg executable.

That's it! kmpkg is set up and ready to use.

2 - Set up the Visual Studio project

  1. Create the Visual Studio project

    • Create a new project in Visual Studio using the "CMake Project" template
  • Name your project "helloworld"
  • Check the box for "Place solution and project in the same directory."
  • Click the "Create" button
  1. Configure the KMPKG_ROOT environment variable.

Setting environment variables in this manner only affects the current terminal session. To make these changes permanent across all sessions, set them through the Windows System Environment Variables panel.

Open the built-in Developer PowerShell window in Visual Studio.

Run the following commands:

$env:KMPKG_ROOT="C:\path\to\kmpkg"
$env:PATH="$env:KMPKG_ROOT;$env:PATH"

Setting KMPKG_ROOT helps Visual Studio locate your kmpkg instance. Adding it to PATH ensures you can run kmpkg commands directly from the shell.

  1. Generate a manifest file and add dependencies.

    Run the following command to create a kmpkg manifest file (kmpkg.json):

    kmpkg new --application

    The kmpkg new command adds a kmpkg.json file and a kmpkg-configuration.json file in the project's directory.

    Add the fmt package as a dependency:

    kmpkg add port fmt

    Your kmpkg.json should now contain:

    {
    "dependencies": [
    "fmt"
    ]
    }

    This is your manifest file. kmpkg reads the manifest file to learn what dependencies to install and integrates with CMake to provide the dependencies required by your project.

    The generated kmpkg-configuration.json file introduces a baseline that places minimum version constraints on the project's dependencies. Modifying this file is beyond the scope of this tutorial. While not applicable in this tutorial, it's a good practice to keep the kmpkg-configuration.json file under source control to ensure version consistency across different development environments.

3 - Set up the project files

  1. Modify the helloworld.cpp file.

    Replace the content of helloworld.cpp with the following code:

#include <fmt/core.h>

int main()
{
fmt::print("Hello World!\n");
return 0;
}

This source file includes the <fmt/core.h> header which is part of the fmt library. The main() function calls fmt::print() to output the "Hello World!" message to the console.

  1. Configure the CMakePresets.json file.

    CMake can automatically link libraries installed by kmpkg when CMAKE_TOOLCHAIN_FILE is set to use kmpkg's custom toolchain. This can be acomplished using CMake presets files.

    Modify CMakePresets.json to match the content below:

CMakePresets.json
    {
"version": 2,
"configurePresets": [
{
"name": "kmpkg",
"generator": "Ninja",
"binaryDir": "${sourceDir}/build",
"cacheVariables": {
"CMAKE_TOOLCHAIN_FILE": "$env{KMPKG_ROOT}/scripts/buildsystems/kmpkg.cmake"
}
}
]
}

Create CMakeUserPresets.json with the following content:

CMakeUserPresets.json
{
"version": 2,
"configurePresets": [
{
"name": "default",
"inherits": "kmpkg",
"environment": {
"KMPKG_ROOT": "<path to kmpkg>"
}
}
]
}

The CMakePresets.json file contains a single preset named "kmpkg", which sets the CMAKE_TOOLCHAIN_FILE variable. The CMakeUserPresets.json file sets the KMPKG_ROOT environment variable to point to the absolute path containing your local installation of kmpkg. It is recommended to not check CMakeUserPresets.json into version control systems.

  1. Edit the CMakeLists.txt file.

    Replace the contents of the CMakeLists.txt file with the following code:

CMakeLists.txt
cmake_minimum_required(VERSION 3.10)

project(HelloWorld)

find_package(fmt CONFIG REQUIRED)

add_executable(HelloWorld helloworld.cpp)

target_link_libraries(HelloWorld PRIVATE fmt::fmt)

Now, let's break down what each line in the CMakeLists.txt file does:

  • cmake_minimum_required(VERSION 3.10): Specifies that the minimum version of CMake required to build the project is 3.10. If the version of CMake installed on your system is lower than this, the build fails.
  • project(HelloWorld): Sets the name of the project to "HelloWorld."
  • find_package(fmt CONFIG REQUIRED): Looks for the fmt library using its CMake configuration file. The REQUIRED keyword ensures that an error is generated if the package is not found.
  • add_executable(HelloWorld helloworld.cpp): Adds an executable target named "HelloWorld," built from the source file helloworld.cpp.
  • target_link_libraries(HelloWorld PRIVATE fmt::fmt): Specifies that the HelloWorld executable should link against the fmt library. The PRIVATE keyword indicates that fmt is only needed for building HelloWorld and should not propagate to other dependent projects.

4 - Build and run the project

  1. Build the project.

    Build the project using the Build > Build All option from the top menu.

  2. Run the application.

    Finally, run the executable:

You should see the output:

Next steps

To learn more about kmpkg.json, see our reference documentation: