Using CMake FetchContent to Pull GoogleTest into Your Build
Learn how CMake's FetchContent module downloads and builds dependencies like GoogleTest directly in your project, ensuring reproducible builds without external package managers.
02 Jul 2025, 00:33 UTC

Problem: Manual Dependency Setup Is Fragile
When a CMake project relies on external libraries, developers often copy source tarballs, maintain a local mirror, or ask users to install packages via system managers. These approaches introduce version drift, break reproducibility, and add steps to the onboarding checklist.
Thesis: FetchContent Gives Reproducible, In‑Tree Dependencies
CMake’s FetchContent module downloads, configures, and builds a dependency as part of the main project’s build tree. By declaring an exact commit or tag, you get the same source on every machine, and CMake’s dependency tracking rebuilds only when the declared version changes.
How FetchContent Works
The workflow consists of two steps:
FetchContent_Declare(NAME GIT_REPOSITORY URL GIT_TAG tag-or-commit)– records where to get the source.FetchContent_MakeAvailable(NAME)– triggers the checkout, runs the dependency’sCMakeLists.txt, and makes its targets available.
All artifacts land under the build directory in a _deps subfolder, keeping the source tree clean.
Worked Example: Adding GoogleTest
Create a fresh directory myapp and place the following CMakeLists.txt inside:
cmake_minimum_required(VERSION 3.14)
project(MyApp LANGUAGES CXX)
include(FetchContent)
FetchContent_Declare(
googletest
GIT_REPOSITORY https://github.com/google/googletest.git
GIT_TAG release-1.14.0 # exact tag for reproducibility
)
# Make the target available; this also configures and builds googletest
FetchContent_MakeAvailable(googletest)
add_executable(my_test test_main.cpp)
target_link_libraries(my_test PRIVATE GTest::GTest)
# Enable CTest integration
include(GoogleTest)
gtest_discover_tests(my_test)
Next, add a minimal test source file test_main.cpp:
#include <gtest/gtest.h>
TEST(SampleTest, BasicAssertion) {
EXPECT_EQ(2 * 2, 4);
}
From a terminal with write permission to myapp and internet access, run:
# Configure and build
cmake -S . -B build
cmake --build build
CMake will:
- Clone googletest into
build/_deps/googletest-src. - Configure and build it in
build/_deps/googletest-build. - Link
GTest::GTesttomy_test.
After the build finishes, you can run the test:
cd build
ctest --output-on-failure
You should see the test pass, confirming that FetchContent pulled and built the dependency correctly.
Trade‑off and Limitation
The primary limitation is network access during the configuration step. If you need offline builds, you must pre‑populate the _deps cache or mirror the repository locally. Additionally, if a dependency’s CMakeLists.txt assumes it is installed in a system location (e.g., uses find_package with hard‑coded paths), you may need to apply a patch or set FETCHCONTENT_SOURCE_DIR_NAME to point to a compatible fork.
To verify that version pinning works, change the GIT_TAG value to a different commit, re‑run cmake -S . -B build, and observe that CMake re‑fetches only googletest (the _deps folder is updated) while leaving your own source untouched.
Actionable Closing
Start using FetchContent for any third‑party code that ships a CMakeLists.txt. Pin an exact tag or commit, commit the updated CMakeLists.txt to your repository, and let CI runners handle the fetch automatically. When you need to work offline, copy the _deps folder from a previous build into the build tree before configuring, or set up a local mirror and point GIT_REPOSITORY to it. This gives you reproducible builds without sacrificing the simplicity of a single‑command configure step.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.