C++ Module 15 – Build systems, testing and tooling
vcpkg and Conan: C++ dependency management
Add {fmt} 12.2 to a CMake project with vcpkg manifest mode and with Conan 2, compare the two, and see what pins versions and licences before you ship.
What you will learn
- Add a third-party library with vcpkg manifest mode and a baseline
- Add the same library with Conan 2, a profile and the presets it generates
- Record exact dependency versions so every build resolves the same ones
- Check the licence of every dependency before shipping
Before you start
What you’ll learn · Before you start
What you will learn
- Add a third-party library with vcpkg manifest mode and a baseline
- Add the same library with Conan 2, a profile and the presets it generates
- Record exact dependency versions so every build resolves the same ones
- Check the licence of every dependency before shipping
Before you start
On this page
The scoreboard program of this lesson needs one third-party library, fmt 12.2.0, the formatting library, and C++ has
no package manager: A tool that fetches the libraries a project depends on at chosen versions, builds them or downloads them built, and tells the build where they are. vcpkg and Conan are package managers for C and C++. built into the language or its compilers to fetch it. So the
project must get fmt’s headers, build it with settings that match its own, and tell CMake where it is. vcpkg,
Microsoft’s open-source package manager, and Conan, an open-source project maintained by JFrog, both do that job,
and both are MIT-licensed. vcpkg reads a vcpkg.json manifest, builds every dependency from source with its own
scripts and hands the results to CMake through a toolchain file: A CMake script read before the project's own files, given with CMAKE_TOOLCHAIN_FILE or a preset's toolchainFile. vcpkg's and Conan's toolchain files tell CMake where the installed packages are.. Conan reads a
conanfile, downloads a prebuilt binary from ConanCenter when one matches your compiler and settings or builds from
source, and writes CMake files and presets.
With either one, the project’s CMakeLists.txt stays ordinary: it says find_package(fmt) and nothing about where
the library came from. This lesson adds fmt 12.2.0 to the same small project with vcpkg 2026.07.29 and with
Conan 2.33.0.
Two package managers, one unchanged CMakeLists.txt
The two tools do the same job in different ways, and the table compares them row by row:
| vcpkg | Conan 2 | |
|---|---|---|
| You write | vcpkg.json, the manifest |
conanfile.txt, or conanfile.py for logic |
| Where versions come from | A baseline: one commit of the vcpkg registry fixes every port’s version | Each requirement names a version, such as fmt/12.2.0, or a range |
| Binaries | Built from source on your machine, then kept in a local binary cache | Prebuilt on ConanCenter for common settings, otherwise --build=missing builds them |
| Describing the build | Triplets such as arm64-osx |
Profiles: compiler, its version, C++ standard, build type |
| Joining CMake | The toolchain file scripts/buildsystems/vcpkg.cmake |
Generated CMakeDeps and CMakeToolchain files and a conan-release preset |
| Catalogue | 2,858 ports in the registry at release 2026.07.29 | About 2,000 recipes on ConanCenter |
The diagram follows both routes from a list of dependencies to the same find_package call:
Two package managers, one unchanged CMakeLists.txt
Text description of the diagram
The diagram is one column: a group of boxes for vcpkg at the top, a group for Conan at the bottom, and between them one box, the project's CMakeLists.txt with find_package(fmt) and target_link_libraries, which both groups lead to.
- vcpkg: vcpkg.json lists the dependencies and a baseline. Configuring with cmake --preset vcpkg loads vcpkg's toolchain file, which builds each port from source and installs it into the build folder's vcpkg_installed folder, where find_package finds it.
- Conan: conanfile.txt lists the requirements and the generators. conan install downloads a prebuilt binary when one matches the profile, or builds from source with --build=missing, and writes the fmt config files and a conan-release preset that CMake then uses.
Neither route changes the CMake code of the project itself.
The project is a scoreboard printed with fmt. Its CMake code and its two dependency files sit side by side:
Text (CMakeLists.txt) · scores/CMakeLists.txt
cmake_minimum_required(VERSION 3.28...4.4)
project(scores LANGUAGES CXX)
set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_CXX_SCAN_FOR_MODULES OFF)
# Nothing here names a package manager: vcpkg or Conan only makes find_package() succeed.
find_package(fmt 12 CONFIG REQUIRED)
add_executable(scores main.cpp)
target_compile_features(scores PRIVATE cxx_std_23)
target_link_libraries(scores PRIVATE fmt::fmt) C++ · scores/main.cpp
// A small scoreboard printed with {fmt}: aligned columns, a joined list and the version of the library.
#include <string>
#include <utility>
#include <vector>
#include <fmt/format.h>
#include <fmt/ranges.h>
int main() {
const std::vector<std::pair<std::string, int>> scores{{"Asha", 92}, {"Ravi", 87}, {"Meera", 78}};
std::vector<int> marks;
for (const auto& [name, mark] : scores) {
fmt::print("{:<8}{:>4}\n", name, mark);
marks.push_back(mark);
}
fmt::print("marks: {}\n", fmt::join(marks, ", "));
fmt::print("built with {{fmt}} {}.{}.{}\n", FMT_VERSION / 10000, FMT_VERSION / 100 % 100, FMT_VERSION % 100);
} JSON (vcpkg.json) · scores/vcpkg.json
{
"name": "scores",
"version": "1.0.0",
"dependencies": ["fmt"],
"builtin-baseline": "9e593bb18ea69cc5095e012465dcd675a822ed0d"
} Text (conanfile.txt) · scores/conanfile.txt
[requires]
fmt/12.2.0
[generators]
CMakeDeps
CMakeToolchain
[layout]
cmake_layout JSON (CMakePresets.json) · scores/CMakePresets.json
{
"version": 6,
"configurePresets": [
{
"name": "vcpkg",
"displayName": "Release build with dependencies from vcpkg (needs VCPKG_ROOT)",
"generator": "Ninja",
"binaryDir": "${sourceDir}/build/vcpkg",
"toolchainFile": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake",
"cacheVariables": { "CMAKE_BUILD_TYPE": "Release" }
}
],
"buildPresets": [{ "name": "vcpkg", "configurePreset": "vcpkg" }]
} Checkpoint
Check: vcpkg and Conan side by side
What else decides how dependable your dependencies are
- A vcpkg baseline is one commit of the vcpkg registry, and every port gets the version recorded in that commit.
version>=andoverridesinvcpkg.jsonask for at least a version, or for exactly one.- A Conan version range such as
fmt/[>=11 <13]resolves to the newest matching version the remote has. - A lockfile (
conan.lock) records the exact versions and recipe revisions one build resolved. - Binary caching keeps built packages and reuses them whenever the same inputs come back.
- Licence data is what each package declares: vcpkg prints it after installing, Conan’s
graph infoshows it. - Vendoring copies a small header-only library into your repository at a pinned version.
Each of these answers the same question from a different side: will next month’s build use the same code as today’s, and are you allowed to ship it?
JSON Validator Check a vcpkg.json or CMakePresets.json for a missing comma before vcpkg or CMake rejects it.vcpkg builds fmt from source at its baseline’s version
from session import run
# This session needs the network: it clones the vcpkg registry at its 2026.07.29 release, downloads the vcpkg tool,
# and vcpkg downloads and builds {fmt} 12.2.0.
run("""
git -c advice.detachedHead=false clone --quiet --depth 1 --branch 2026.07.29 https://github.com/microsoft/vcpkg.git
""", keep=r"^\[exit")
run("""
./vcpkg/bootstrap-vcpkg.sh -disableMetrics
export VCPKG_ROOT=$PWD/vcpkg
cd scores
""")
run("""
cmake --preset vcpkg
""", keep=r"^-- Running vcpkg install$|^The following packages|^ \S+:arm64-osx|^ \* |^Additional packages|^Installing \d|^Downloading https|^Packages installed in this|^MIT$|^-- Build files")
run("""
cmake --build --preset vcpkg
./build/vcpkg/scores
ls build/vcpkg/vcpkg_installed/arm64-osx/share/fmt
""") Output
$ git -c advice.detachedHead=false clone --quiet --depth 1 --branch 2026.07.29 https://github.com/microsoft/vcpkg.git
[1 line not shown]
$ ./vcpkg/bootstrap-vcpkg.sh -disableMetrics
Downloading vcpkg-macos...
vcpkg package management program version 2026-07-27-98d7cb0cf1f4686a3e43aa5672b6230c1d56bce8
See LICENSE.txt for license information.
$ export VCPKG_ROOT=$PWD/vcpkg
$ cd scores
$ cmake --preset vcpkg
-- Running vcpkg install
The following packages will be built and installed:
fmt:arm64-osx@12.2.0#1
* vcpkg-cmake:arm64-osx@2024-04-23
* vcpkg-cmake-config:arm64-osx@2026-07-21
Additional packages (*) will be modified to complete this operation.
Installing 1/3 vcpkg-cmake-config:arm64-osx@2026-07-21...
Installing 2/3 vcpkg-cmake:arm64-osx@2024-04-23...
Installing 3/3 fmt:arm64-osx@12.2.0#1...
Downloading https://github.com/fmtlib/fmt/commit/588b3a0f8f6a8bcf2a959cae882d5b2703e86737.patch?full_index=1 -> fmt-backport-4813.patch
Downloading https://github.com/fmtlib/fmt/archive/12.2.0.tar.gz -> fmtlib-fmt-12.2.0.tar.gz
Packages installed in this vcpkg installation declare the following licenses:
MIT
-- Build files have been written to: build/vcpkg
[62 lines not shown]
$ cmake --build --preset vcpkg
[1/2] Building CXX object CMakeFiles/scores.dir/main.cpp.o
[2/2] Linking CXX executable scores
$ ./build/vcpkg/scores
Asha 92
Ravi 87
Meera 78
marks: 92, 87, 78
built with {fmt} 12.2.0
$ ls build/vcpkg/vcpkg_installed/arm64-osx/share/fmt
copyright
fmt-config-version.cmake
fmt-config.cmake
fmt-targets-debug.cmake
fmt-targets-release.cmake
fmt-targets.cmake
usage
vcpkg_abi_info.txt
vcpkg-spdx-resources.json
vcpkg.spdx.json
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 vcpkg_session.py
Follow that run one stage at a time, from the cloned registry to the scoreboard it printed:
The drawing shows the whole scene; its 7 steps are listed below.
Every step as text
- The registry.
git clone --depth 1 --branch 2026.07.29fetched the vcpkg registry at that release: its scripts and a port, a build recipe, for each library. - The tool.
bootstrap-vcpkg.shdownloaded the vcpkg program that matches the registry, andVCPKG_ROOTthen pointed at the clone. - The preset. The
vcpkgpreset namesscripts/buildsystems/vcpkg.cmakeunderVCPKG_ROOTas its toolchain file, so configuring runs vcpkg before CMake looks for any package. - The manifest. vcpkg read
vcpkg.json: one dependency,fmt, and abuiltin-baseline. The baseline gave fmt 12.2.0, revision 1 of its port, and two helper ports came with it. - Built from source. It downloaded fmt's source archive and a patch, built them for the triplet
arm64-osx, and listed the licences the installed packages declare: MIT. - Found like any library. CMake then found fmt in
build/vcpkg/vcpkg_installed, through thefind_package(fmt 12 CONFIG REQUIRED)call that knows nothing about vcpkg. - The scoreboard. The build compiled
main.cppand linkedscores, which printed three names, the marks joined by commas andbuilt with {fmt} 12.2.0.
The registry was cloned at its 2026.07.29 release, and bootstrap-vcpkg.sh downloaded the matching vcpkg program.
The vcpkg preset sets vcpkg’s toolchain file, so cmake --preset vcpkg ran vcpkg install before configuring:
vcpkg read the manifest, chose fmt 12.2.0, revision 1 of its port, from the baseline, downloaded the source and a
patch, built it, and listed the licences the installed packages declare, here only MIT. CMake then found fmt like any
installed library. Each package also gets a copyright file and an SPDX description, vcpkg.spdx.json, in its
share folder.
Key idea
vcpkg turns one committed baseline into one version of every port, builds each from source, and joins CMake through
its toolchain file, so the project’s own CMake code only calls find_package.
Checkpoint
Check: fmt through vcpkg and a baseline
Conan needs a profile that matches the project
from session import run
# This session needs the network: pip installs Conan 2.33.0 from PyPI, and Conan downloads the {fmt} 12.2.0 recipe and
# sources from ConanCenter and builds them.
run("""
python3 -m venv conan-env
conan-env/bin/pip install --quiet --disable-pip-version-check conan==2.33.0
export PATH=$PWD/conan-env/bin:$PATH
conan --version
conan profile detect
cd scores
""")
run("""
conan install . --build=missing -s compiler.cppstd=23 -c tools.cmake.cmaketoolchain:generator=Ninja
""", keep=r"^======== |^fmt/12\.2\.0: (?:Downloaded|Building|Package '|Created)|^Generators folder: build|^CMakeToolchain: Preset|^Install finished")
run("""
cmake --preset conan-release
cmake --build --preset conan-release
./build/Release/scores
""", keep=r"^(?!-- (?:The|Detecting|Check|Configuring|Generating|Conan:|Using Conan))") Output
$ python3 -m venv conan-env
$ conan-env/bin/pip install --quiet --disable-pip-version-check conan==2.33.0
$ export PATH=$PWD/conan-env/bin:$PATH
$ conan --version
Conan version 2.33.0
$ conan profile detect
detect_api: Found apple-clang 21.0
detect_api: apple-clang>=13, using the major as version
Detected profile:
WARN: This profile is a guess of your environment, please check it.
WARN: Defaulted to cppstd='gnu17' for apple-clang.
WARN: The output of this command is not guaranteed to be stable and can change in future Conan versions.
WARN: Use your own profile files for stability.
Saving detected profile to ~/.conan2/profiles/default
[settings]
arch=armv8
build_type=Release
compiler=apple-clang
compiler.cppstd=gnu17
compiler.libcxx=libc++
compiler.version=21
os=Macos
$ cd scores
$ conan install . --build=missing -s compiler.cppstd=23 -c tools.cmake.cmaketoolchain:generator=Ninja
======== Input profiles ========
======== Computing dependency graph ========
fmt/12.2.0: Downloaded recipe revision b445244e6d0151a425e229248cd2bed8
======== Computing necessary packages ========
======== Installing packages ========
fmt/12.2.0: Package '80f381038c73ab8bba10c44388057ab48634313d' built
======== Finalizing install (deploy, generators) ========
Generators folder: build/Release/generators
CMakeToolchain: Preset 'conan-release' added to CMakePresets.json.
Install finished successfully
[165 lines not shown]
$ cmake --preset conan-release
-- Conan toolchain: Defining libcxx as C++ flags: -stdlib=libc++
-- Conan toolchain: C++ Standard 23 with extensions OFF
-- Build files have been written to: build/Release
[11 lines not shown]
$ cmake --build --preset conan-release
[1/2] Building CXX object CMakeFiles/scores.dir/main.cpp.o
[2/2] Linking CXX executable scores
$ ./build/Release/scores
Asha 92
Ravi 87
Meera 78
marks: 92, 87, 78
built with {fmt} 12.2.0
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 conan_session.py
The Conan run, command by command, with what each one printed or wrote:
The drawing shows the whole scene; its 6 steps are listed below.
Every step as text
- Conan itself. Conan is a Python program: the session made a virtual environment and installed exactly version 2.33.0 into it with pip.
- A guessed profile.
conan profile detectfound Apple clang 21 and wrote a default profile withcompiler.cppstd=gnu17, with a warning that the profile is a guess to check. - Settings that match.
conan installwas given-s compiler.cppstd=23, so the binary it provides matches a C++23 project, and Ninja as the generator for the toolchain it writes. - No binary to download. Conan downloaded the fmt/12.2.0 recipe, found no prebuilt binary for these settings on ConanCenter, and
--build=missingbuilt one from source. - Generated files. The generators
CMakeDepsandCMakeToolchainwrote fmt's CMake files intobuild/Release/generatorsand added theconan-releasepreset. - The same scoreboard. CMake configured with C++ standard 23, extensions off, found fmt with the same
find_packagecall as before, andscoresprintedbuilt with {fmt} 12.2.0again.
conan profile detect found Apple clang 21 and guessed compiler.cppstd=gnu17, with a warning to check the guess;
the install overrode it with -s compiler.cppstd=23 to match the project. No prebuilt binary matched those settings,
so --build=missing built fmt from the recipe, and the generators wrote the conan-release preset that CMake used
next. Conan’s own suggestion is worth taking: keep your own profile files under version control instead of relying
on detection, so every machine builds with the same settings.
The configure step of that preset confirmed the settings in its first lines, Conan toolchain: Defining libcxx as C++ flags: -stdlib=libc++ and Conan toolchain: C++ Standard 23 with extensions OFF. Everything Conan generated sits in
build/Release/generators, inside the build folder, and the scoreboard printed the same lines as the vcpkg build.
Checkpoint
Check: fmt through Conan and a profile
Check yourself
Ten questions on vcpkg, Conan and how the two compare, asked one at a time with the idea you know least first. Every choice explains itself as soon as you pick it.
Practice that adapts to you
Practise this lesson
10 questions about 6 ideas of this lesson. Each answer is explained at once, and what you get right is kept in this browser.
Versions that stay pinned: baselines, lockfiles and a shallow-clone failure, reproduced Pro: opens with a Pro, Premium or Ultimate pass
A build is reproducible only if every dependency resolves to the same version and the same recipe every time. Both tools can promise that, and both have a common way of breaking the promise, run here against the real registry and ConanCenter.
In this part:
- What a vcpkg baseline decides, and what it needs
- What a Conan lockfile holds
- The mechanisms side by side
3 runnable examples (JSON (vcpkg.json), Python) · about 4 minutes
Opening this part with your pass…
This part opens with any MySmartCoPilot pass.
- Pro pass · 1 week₹79$2.99
- 30 days₹179$5.99
- 1 year₹1,699$45
One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.
Passes can’t be bought right now, so this part can’t be opened at the moment. The free part above stands on its own. If you have a pass, enter your licence key.
It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).
Licences before you ship: what each package declares, and a default that changes it Pro: opens with a Pro, Premium or Ultimate pass
Every dependency comes with a licence, and the obligations range from keeping a notice to publishing your own source code. The package managers report what each package declares; what you must do with it depends on how you ship.
In this part:
- What the package managers report
- What each licence asks of the program you ship
- A default that turns a library into one you cannot ship
2 runnable examples (Python, Text) · about 4 minutes
Opening this part with your pass…Pro part. It opens with the same pass as the part above.
vcpkg, Conan, FetchContent or vendoring: the JSON exercise and a decision table Pro: opens with a Pro, Premium or Ultimate pass
Not every dependency needs a package manager, and not every team needs the same one. The exercise below adds a header-only library with Conan; the table then compares the four ways a C++ project usually gets its dependencies.
In this part:
- Exercise: a JSON round trip with nlohmann/json
- Choosing the route
- Your own libraries as packages
2 runnable examples (C++, Python, Text (CMakeLists.txt), Text (conanfile.txt)) · about 6 minutes
Opening this part with your pass…Pro part. It opens with the same pass as the part above.
Interview questions
-
Fresher · Warm-up
Why is adding a third-party library harder in C++ than in a language with one package manager, and what do vcpkg and Conan do about it?
C++ has no package manager built into the language or its compilers, and a compiled library must match the program it joins: the same compiler family and standard library, compatible settings such as the C++ standard and build type, and the same processor. So a library cannot simply be downloaded as one file; it has to be built, or chosen from binaries, for your exact configuration, and CMake must be told where it is. vcpkg builds each dependency from source with its own scripts, at the versions of one registry commit, and connects to CMake through a toolchain file. Conan describes your configuration in a profile, downloads a matching prebuilt binary or builds one, and generates the CMake files. With either, the project's CMake code only calls find_package.
-
Mid to senior
A release build pulled a newer version of a dependency than the one QA tested, and a bug appeared in production. Design the dependency policy that makes every build reproducible, with vcpkg or with Conan.
-
Senior to staff
Before a release, the legal team asks which licences your app's 40 C++ dependencies carry and what you owe for each. How do you answer quickly and reliably, and what would make you stop the release?
-
Senior to staff
A team of five ships a desktop app on Windows, macOS and Linux with about 15 dependencies. CI takes 25 minutes, mostly building those dependencies from source on every run. Which dependency approach do you choose, and why?
Key takeaways
- vcpkg and Conan both make
find_packagesucceed; the project’s CMake code does not change between them. - vcpkg builds each port from source at the versions of one baseline commit; Conan resolves each requirement and uses a prebuilt binary when one matches the profile.
- Detected Conan profiles are guesses: set
compiler.cppstdand keep your own profiles in version control. - vcpkg printed the licences it installed, and Conan’s
graph infoshows each recipe’s licence field.
Terms in this lesson
- baseline (vcpkg)
- One commit of the vcpkg registry, named by builtin-baseline in vcpkg.json. Every port the project uses gets the version recorded in that commit.
- lockfile
- A file that records the exact versions, and for Conan the recipe revisions, that one build resolved, such as conan.lock, so that later builds resolve the same ones.
- package manager
- A tool that fetches the libraries a project depends on at chosen versions, builds them or downloads them built, and tells the build where they are. vcpkg and Conan are package managers for C and C++.
- profile (Conan)
- A file of settings that describes a build to Conan: the compiler, its version, the C++ standard and the build type, among others. Conan provides binaries that match it.
- toolchain file
- A CMake script read before the project's own files, given with CMAKE_TOOLCHAIN_FILE or a preset's toolchainFile. vcpkg's and Conan's toolchain files tell CMake where the installed packages are.
- vendoring
- Copying a library's source into your own repository at a pinned version, instead of fetching it with a package manager.
References
- vcpkg overview (Microsoft)
- vcpkg: Versioning concepts (baselines, version constraints, overrides) (Microsoft)
- vcpkg release 2026.07.29 (total port count) (Microsoft)
- ConanCenter (recipes and references) (JFrog (Conan project))
- Conan 2 documentation (JFrog (Conan project))
- Conan 2: Lockfiles (JFrog (Conan project))
- Conan 2.33.0 release (JFrog (Conan project))
- conan-center-index: the ffmpeg recipe (default options) (JFrog (Conan project))
- FetchContent (Kitware)
- cmake-toolchains(7) (Kitware)
- FFmpeg License and Legal Considerations (FFmpeg project)
- FFmpeg configure script: licensing options (--enable-gpl, --enable-nonfree) (FFmpeg project)
- The MIT License (Open Source Initiative)
- The zlib License (Open Source Initiative)
- Apache License, Version 2.0 (The Apache Software Foundation)
- Boost Software License 1.0 (Boost)
- GNU Lesser General Public License, version 2.1 (Free Software Foundation)
- GNU General Public License, version 2 (Free Software Foundation)
Related tools
Report a problem with this lesson
Kept only in this browser. Your Learn progress