Choosing Between FetchContent and ExternalProject for CMake Dependencies
Learn when to use FetchContent versus ExternalProject in CMake to manage dependencies. Compare configuration-time and build-time dependency management with implementation examples.
29 May 2026, 17:01 UTC

The Dependency Integration Dilemma
When adding external libraries to a C++ project, you must decide whether the dependency should be part of your project's internal build graph or treated as a separate binary entity. Choosing the wrong method often leads to "target not found" errors during configuration or prohibitively slow build times as the project grows.
The core decision rests on when the dependency is acquired and how its targets are exposed to your main application. In CMake (3.11+), this typically means choosing between FetchContent and ExternalProject.
Comparison of Integration Strategies
| Feature | FetchContent | ExternalProject |
|---|---|---|
| Execution Phase | Configure time (during cmake ..) |
Build time (during make or ninja) |
| Target Visibility | Native (use target_link_libraries) |
External (requires manual path mapping) |
| Compiler Flags | Inherits parent project settings | Isolated; requires explicit passing |
| Config Speed | Slower (downloads/populates early) | Faster (defers to build phase) |
| Best Use Case | Small-to-medium source libs, header-only | Massive frameworks (LLVM, Boost, Qt) |
Evaluating Trade-offs
FetchContent: The Integrated Approach
FetchContent downloads the source and immediately calls add_subdirectory() on the dependency. This means the dependency's targets (e.g., nlohmann_json::nlohmann_json) are available globally in your project. It is the most ergonomic choice for modern CMake development because it eliminates the need to manage absolute paths to include directories or library files.
Risk: If you have 20 dependencies, your configuration step (the time it takes to run the CMake command) will increase significantly, as CMake must verify the state of every external repository before generating the build files.
ExternalProject: The Isolated Approach
ExternalProject treats the dependency as a completely separate build process. It downloads, configures, and builds the library only when you actually start the build. Because this happens after the main project's configuration phase, the main project has no knowledge of the targets created by the external project.
Risk: You must manually define IMPORTED targets or hardcode paths to the resulting .lib or .a files. If the external project changes its output directory, your main project will fail to link.
Implementation Example: FetchContent
For most modern libraries that provide a CMakeLists.txt, FetchContent is the recommended starting point. This example demonstrates integrating a dependency so that it can be linked by name.
# Run these commands in your project root
# cmake -B build
# cmake --build build
cmake_minimum_required(VERSION 3.14)
project(DependencyExample)
include(FetchContent)
# 1. Declare the dependency source and version
FetchContent_Declare(
googletest
GIT_REPOSITORY https://github.com/google/googletest.git
GIT_TAG v1.14.0
)
# 2. Populate and make targets available
# This effectively calls add_subdirectory() internally
FetchContent_MakeAvailable(googletest)
add_executable(my_test main.cpp)
# 3. Link using the target name defined by the dependency
# No absolute paths required
target_link_libraries(my_test PRIVATE gtest_main)
Verification and Validation
To verify that FetchContent is working correctly, check the build directory after configuration. You should see a _deps folder containing the source code of the dependency. If the build succeeds, it confirms that the target gtest_main was visible to the linker during the build phase.
Rollback and Cleanup
Because FetchContent modifies the build tree, the only way to "roll back" a version change or remove a dependency is to delete the build/ directory (specifically the _deps folder) and re-run the configuration step. This ensures no stale source files from previous versions remain.
Decision Summary
- Use FetchContent if: The library is relatively small, you want to use the same compiler flags across the whole project, and you prefer using
target_link_libraries. - Use ExternalProject if: The dependency takes 10+ minutes to compile, has a complex build system that conflicts with yours, or is a massive project that should only be built once and cached.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.