Skip to main content

C++ vcpkg Manifest Mode + CMake

 If you've ever tried to install a C++ library and felt like you were assembling furniture without instructions, this article is for you. We're going to talk about vcpkg manifest mode and how it works with CMake, and I'm going to explain it like you're five years old (in a good way — no judgment here).

First, Let's Talk About the Problem

In most programming languages, adding a library is easy. Python has pip install requests. JavaScript has npm install express. You type one command, and boom, the library shows up in your project.

C++ never really had that. For decades, if you wanted to use a library like fmt or nlohmann/json, you had to:

  1. Download the source code yourself
  2. Figure out how to compile it
  3. Tell your compiler where to find the headers
  4. Tell your linker where to find the compiled binaries
  5. Cry a little

vcpkg is Microsoft's answer to this mess. It's a package manager for C++ — like pip or npm, but for C++ libraries. And manifest mode is the modern, recommended way to use it.

Classic Mode vs. Manifest Mode (The "Dummy" Explanation)

Think of vcpkg like a grocery store.

  • Classic mode is like walking into the store and physically putting items into a shared pantry in your house. Every project on your computer shares that one pantry. If Project A needs an old version of a can of beans and Project B needs a new version, you've got a problem — they're fighting over the same can.

  • Manifest mode is like writing a shopping list (a file called vcpkg.json) and keeping a separate fridge for each project. Every project gets its own ingredients, at exactly the versions it asked for. Nothing gets mixed up. Nothing conflicts.

Manifest mode is the modern way, and it's what almost everyone should use today. So that's what we're covering.

Step 1: Get vcpkg Onto Your Machine

You only need to do this once, ever (not once per project).

# Clone vcpkg from GitHub
git clone https://github.com/microsoft/vcpkg.git

# Go into the folder
cd vcpkg

# Run the bootstrap script (this builds the vcpkg tool itself)
./bootstrap-vcpkg.sh      # macOS/Linux
# or
.\bootstrap-vcpkg.bat     # Windows

That's it. You now have a vcpkg executable sitting in that folder. Think of it as installing the "grocery store app" on your phone. You haven't bought anything yet — you've just installed the tool that lets you shop.

Step 2: Create Your Project Folder

Let's make a simple example project.

my-cool-app/
├── CMakeLists.txt
├── vcpkg.json
└── src/
    └── main.cpp

Nothing fancy. A CMake file, a manifest file, and some source code.

Step 3: Write the Shopping List — vcpkg.json

This is the heart of manifest mode. This file tells vcpkg exactly what libraries your project needs.

{
  "name": "my-cool-app",
  "version": "1.0.0",
  "dependencies": [
    "fmt",
    "nlohmann-json"
  ]
}

Let's break this down like you're new here:

  • "name" — the name of your project. Lowercase, no spaces.
  • "version" — your project's version number.
  • "dependencies" — a plain list of library names you want. Here we're grabbing fmt (a popular text-formatting library) and nlohmann-json (a very popular JSON library).

That's genuinely it. You don't specify where to download them from or how to build them. vcpkg already knows, because it maintains a giant catalog of thousands of libraries.

Step 4: Write Your CMakeLists.txt

CMake is the tool that actually compiles your project. Here's a minimal setup that plays nicely with vcpkg.

cmake_minimum_required(VERSION 3.20)
project(my_cool_app LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

# Find the libraries vcpkg installed for us
find_package(fmt CONFIG REQUIRED)
find_package(nlohmann_json CONFIG REQUIRED)

add_executable(my_cool_app src/main.cpp)

# Link the libraries to our executable
target_link_libraries(my_cool_app
    PRIVATE
        fmt::fmt
        nlohmann_json::nlohmann_json
)

What's happening here, in plain English:

  • find_package(fmt CONFIG REQUIRED) — "Hey CMake, go find the fmt library that vcpkg installed, and fail loudly if you can't find it."
  • target_link_libraries(...) — "Attach these libraries to my program so it actually knows how to use them at compile time."

You are not manually specifying include paths or .lib/.so file locations anywhere. vcpkg + CMake handle all of that plumbing behind the scenes. This is the magic of manifest mode.

Step 5: Write Some Actual Code

Just so we have something to compile — src/main.cpp:

#include <fmt/core.h>
#include <nlohmann/json.hpp>

int main() {
    // Using fmt to print nicely formatted text
    fmt::print("Hello, {}! You are {} years old.\n", "World", 25);

    // Using nlohmann/json to build a small JSON object
    nlohmann::json data = {
        {"name", "Ada"},
        {"language", "C++"},
        {"awesome", true}
    };

    fmt::print("JSON output: {}\n", data.dump(2));

    return 0;
}

Nothing exotic — just proof that both libraries actually work.

Step 6: The Magic Command That Ties It All Together

This is the part that trips people up, so pay attention. When you run CMake, you need to tell it about vcpkg using something called a toolchain file. Think of the toolchain file as a translator that tells CMake "hey, before you go looking for libraries, check vcpkg's fridge first."

cmake -B build -S . \
  -DCMAKE_TOOLCHAIN_FILE=/path/to/vcpkg/scripts/buildsystems/vcpkg.cmake

Replace /path/to/vcpkg with wherever you cloned vcpkg in Step 1.

When you run this command, here's what actually happens behind the scenes:

  1. CMake starts up and sees the toolchain file.
  2. The toolchain file quietly tells vcpkg to look at your vcpkg.json.
  3. vcpkg sees you need fmt and nlohmann-json.
  4. vcpkg automatically downloads and builds those libraries (just for this project, in a local hidden folder called vcpkg_installed/).
  5. CMake then finds those freshly built libraries and links them into your app.

You didn't have to manually download anything. You just described what you wanted, and the tools handled the "how."

Then you actually build the project:

cmake --build build

And run it:

./build/my_cool_app

You should see:

Hello, World! You are 25 years old.
JSON output: {
  "awesome": true,
  "language": "C++",
  "name": "Ada"
}

A Nice Shortcut: Skip the Long Command

Typing that long -DCMAKE_TOOLCHAIN_FILE=... path every time gets old fast. You can set an environment variable once instead:

export VCPKG_ROOT=/path/to/vcpkg
export PATH=$VCPKG_ROOT:$PATH

Then, in your CMakeLists.txt, you can even skip passing the flag manually if your CMake setup detects VCPKG_ROOT (many modern IDEs like Visual Studio and CLion do this automatically). Or you can use a CMakePresets.json file, which is the "grown-up" way of never typing that flag again:

{
  "version": 6,
  "configurePresets": [
    {
      "name": "default",
      "generator": "Ninja",
      "binaryDir": "${sourceDir}/build",
      "toolchainFile": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake"
    }
  ]
}

Now you can just run:

cmake --preset default
cmake --build build

Much cleaner.

Pinning Versions (So Things Don't Break Randomly)

By default, vcpkg.json grabs whatever's currently in vcpkg's catalog. If you want reproducible builds — meaning your teammate's computer installs the exact same library versions as yours — you add a vcpkg-configuration.json file that locks vcpkg to a specific snapshot in time (called a "baseline"):

{
  "default-registry": {
    "kind": "git",
    "repository": "https://github.com/microsoft/vcpkg",
    "baseline": "a1b2c3d4e5f6..."
  }
}

You get that baseline hash by running git rev-parse HEAD inside your vcpkg folder. This just means "always use the library versions as they existed on this exact date," so nobody's build randomly changes six months from now.

Quick Recap (For Skimmers)

Step What You Do What It's Like
1 Clone & bootstrap vcpkg once Installing the grocery store app
2 Write vcpkg.json Writing your shopping list
3 Write CMakeLists.txt with find_package Telling your kitchen where ingredients go
4 Run CMake with the toolchain file Sending your list to the store, groceries appear
5 Build & run Cooking the meal

Popular posts from this blog

How to Check if Someone is Connected to Your Machine in Linux

Picture this: you glance at your system monitor and notice your CPU is humming along even though you're not running anything demanding. Or maybe your internet feels sluggish for no obvious reason. A small, uneasy thought creeps in — is someone else on my machine right now? For Linux users, this isn't something you have to wonder about. Linux ships with a powerful set of built-in tools that let you see exactly who's connected, who's logged in, and what your network is doing at any given moment. You don't need to be a security expert to use them — you just need to know where to look. This guide walks you through the practical, no-nonsense steps to check for unauthorized connections on your Linux system, with real commands you can run right now. Why Monitoring Network Connections Matters Every device on a network — including your own Linux machine — communicates using an IP address. When another device or user connects to your system, that connection shows up as a trac...

How to Set Up a Linux Web Server and Host an HTML Page Easily

Setting up a web server on Linux means spending a fair amount of time in the terminal — Linux leans heavily on the command line rather than clicking through menus, so you'll be typing out instructions more often than not.  If you're new to this, it can feel a little intimidating at first, but the good news is you don't need to become a Linux wizard overnight. A handful of core commands will get you surprisingly far. A few you'll lean on constantly: cd — move between directories ls — see what's in the current directory mkdir — create a new folder nano or vim — edit files right there in the terminal sudo — run something with administrator privileges Get comfortable with these and you'll be able to navigate around, tweak configuration files, and install software without much trouble. You don't need to memorize everything — you just need to be confident enough to follow along with clear instructions, which is exactly what this guide aims to give you....

Linux Network Troubleshooting

If you've spent any time as a sysadmin — or honestly, just as someone who's had to fix their own home network at 11pm — you know that connectivity issues are one of the most common headaches out there. The good news is that a handful of core tools and a methodical approach can take you from "why isn't this working" to a root cause pretty quickly.  This guide walks through the essentials: configuring interfaces, managing routes, and diagnosing problems when things go sideways. Configuring Network Interfaces Your network interfaces are the actual bridge between your machine and the outside world, so getting them configured correctly is step one for any kind of reliable connectivity. Doing It Manually ifconfig is the old-school, tried-and-true tool for this on Unix-like systems. To see everything currently configured, run: ifconfig -a If you need to manually set up a specific interface — assigning an IP, a netmask, and bringing it online — it looks like this: ifconf...