Skip to main content
Reproducible dependencies
"Modern CMake" course developed by Fergus Cooper and the Oxford Research Software Engineering group

"Modern CMake" course developed by Fergus Cooper and the Oxford Research Software Engineering group

Creative Commons License

Reproducible dependencies

Beyond find_package

In the earlier Finding dependencies section, we used find_package to locate libraries that are already installed on the system. That is a common situation on a well-managed machine, but it puts the burden on whoever builds the project to install the right libraries first, at the right versions.
For builds that are reproducible anywhere, it helps to have the build itself acquire its dependencies. There are two broad approaches: fetching sources with CMake, or using a dedicated package manager.

FetchContent

CMake's built-in FetchContent module downloads a dependency's source and adds it to your build, so its targets are available just as if they were part of your project:
include(FetchContent) FetchContent_Declare( fmt GIT_REPOSITORY https://github.com/fmtlib/fmt.git GIT_TAG 12.2.0 ) FetchContent_MakeAvailable(fmt) target_link_libraries(main_executable PRIVATE fmt::fmt)
FetchContent_MakeAvailable downloads the sources (the first time) and calls add_subdirectory on them, defining the fmt::fmt target we then link against.
Here we are fetching {fmt}, a formatting library, and once it is linked we can use it in place of stream output:
#include <fmt/format.h> fmt::print("Hypergeometric probability: {:.4f}\n", hypergeometricPmf(10'000, 4'270, 300, 128));
Always pin GIT_TAG to a specific tag or commit, not a branch like main. Pinning is what makes the build reproducible: everyone gets the exact same version of the dependency, and it will not change under you.

Fetch, or find if already installed

Downloading and building a dependency every time is wasteful if it is already installed. Since CMake 3.24, FetchContent_Declare accepts FIND_PACKAGE_ARGS, which tells FetchContent_MakeAvailable to try find_package first and only fall back to downloading:
FetchContent_Declare( fmt GIT_REPOSITORY https://github.com/fmtlib/fmt.git GIT_TAG 12.2.0 FIND_PACKAGE_ARGS CONFIG ) FetchContent_MakeAvailable(fmt)
This gives you the best of both: fast builds where the library is installed, and a self-contained build where it is not.

Package managers (for information only)

The rest of this section is background reading, and out of scope for this course. Package managers are worth knowing about, and you will meet them on real projects, so we describe what they look like and where they fit. But there is nothing to install, no exercise depends on them, and every checkpoint in this course uses FetchContent instead. Follow the links below if you want to go further on your own.
For larger projects, a dedicated C++ package manager can cache and reuse pre-built binaries and manage many dependencies at once. Two are widely used with CMake.

vcpkg

vcpkg is maintained by Microsoft. Its getting started guide and CMake integration docs are the place to start if you want to try it.
Declare your dependencies in a vcpkg.json manifest at the project root:
{ "name": "intro-cmake-course", "version": "1.0.0", "dependencies": ["fmt", "eigen3", "boost-program-options"] }
Then point CMake at vcpkg's toolchain file at configure time, and it installs and exposes the dependencies for find_package:
cmake -S . -B build_dir \ -DCMAKE_TOOLCHAIN_FILE=/path/to/vcpkg/scripts/buildsystems/vcpkg.cmake
Setting the toolchain file is a good thing to record in a preset so nobody has to remember the path.

Conan

Conan (version 2) is maintained by JFrog. Its tutorial and CMake integration docs cover the same ground for Conan.
Conan uses a conanfile.txt (or conanfile.py) to list dependencies:
[requires] fmt/12.2.0 [generators] CMakeDeps CMakeToolchain
You install the dependencies and generate CMake integration in one step, then configure using the toolchain and presets Conan wrote:
conan install . --build=missing cmake --preset conan-release

Which should I use?

  • FetchContent needs no extra tools and keeps everything in-tree, which is ideal for small projects and for pulling in a few small header-only or source-only libraries.
  • A package manager (vcpkg or Conan) is usually a better fit once you have many dependencies, want cached binaries, or need to share a locked set of versions across a team. Which of the two to pick tends to come down to what your dependencies, your collaborators, and your CI already use.
For this course, the answer is FetchContent: it is what the checkpoints use, and it needs nothing beyond CMake itself.

Fetch and use {fmt}

Starting from Checkpoint 5, fetch {fmt} version 12.2.0 with FetchContent, link it to main_executable, and replace some of the std::cout output in main.cpp with fmt::print.
Unlike Eigen and Boost, you should not need to install anything first: the build acquires fmt for you.