How to Configure GoogleTest with CMake for Both Static and Shared Library Builds

Set the CMake variable BUILD_SHARED_LIBS to ON to compile GoogleTest as a shared library, or leave it OFF (the default) for static linking; on Windows, you must also set gtest_force_shared_crt to ON when building shared libraries to avoid C runtime conflicts.

The google/googletest repository provides a flexible CMake build system that supports both static and dynamic library outputs from the same source tree. Configuring GoogleTest with CMake for both static and shared library builds requires understanding the interaction between standard CMake variables and GoogleTest-specific options defined in the root CMakeLists.txt.

Key CMake Configuration Variables

The build behavior is controlled through a set of cache variables defined in the top-level CMakeLists.txt. The primary toggle is BUILD_SHARED_LIBS, a standard CMake option that determines whether the gtest and gmock targets are built as position-independent shared objects or static archives.

When BUILD_SHARED_LIBS is ON, the CMake scripts automatically apply the WINDOWS_EXPORT_ALL_SYMBOLS target property on Windows platforms and generate the gtest_export.h header to handle symbol visibility. For static builds, the export macros resolve to empty definitions, and the library compiles as a standard archive.

Additional critical options include:

  • gtest_force_shared_crt – Forces the use of the DLL version of the Microsoft C runtime on Windows. This is mandatory when building shared libraries to prevent multiply-defined symbol errors.
  • gtest_disable_pthreads – Disables pthread linking on platforms where it is unavailable or undesired.
  • gtest_build_samples and gtest_build_tests – Control whether the example binaries and GoogleTest's own unit tests are compiled.

Building GoogleTest as a Static Library

By default, BUILD_SHARED_LIBS is OFF, causing CMake to generate static libraries (libgtest.a or gtest.lib). In this mode, the INTERFACE_INCLUDE_DIRECTORIES property is set to automatically expose googletest/include and googlemock/include to downstream targets.


# CMakeLists.txt - Static GoogleTest configuration

cmake_minimum_required(VERSION 3.14)
project(MyTests)

# Add GoogleTest as a subdirectory

add_subdirectory(googletest)

# Link against static targets

add_executable(my_test_suite test_main.cpp)
target_link_libraries(my_test_suite PRIVATE gtest gtest_main)

In googletest/CMakeLists.txt, the gtest and gtest_main targets are defined with archive outputs. The build system handles the distinction between the core library (gtest) and the runner that includes a default main() function (gtest_main).

Building GoogleTest as a Shared Library

To produce a dynamic shared library (.so, .dylib, or .dll), you must explicitly set BUILD_SHARED_LIBS to ON before adding the GoogleTest subdirectory. On Windows, you must additionally enable gtest_force_shared_crt to ensure the C runtime is linked dynamically.


# CMakeLists.txt - Shared GoogleTest configuration

cmake_minimum_required(VERSION 3.14)
project(MyTestsShared)

# Request shared library build

set(BUILD_SHARED_LIBS ON)
set(gtest_force_shared_crt ON)  # Required on Windows

add_subdirectory(googletest)

add_executable(my_shared_test test_main.cpp)
target_link_libraries(my_shared_test PRIVATE gtest gtest_main)

When configured this way, googletest/CMakeLists.txt applies platform-specific logic to export symbols. On Windows, this generates gtest_export.h in the build directory, which is included by the public headers in googletest/include/gtest/.

Installing GoogleTest for Reuse

To install the built libraries and headers for consumption by other projects via find_package(), use the install targets defined in the CMake scripts.


# Installation configuration (run from within GoogleTest source)

include(GNUInstallDirs)

install(
  TARGETS gtest gtest_main
  EXPORT GTestTargets
  ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
  LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
  RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
)

install(
  DIRECTORY include/
  DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
)

install(
  EXPORT GTestTargets
  FILE GTestConfig.cmake
  NAMESPACE GTest::
  DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/GTest
)

This exports the targets with the GTest:: namespace, allowing downstream projects to link against GTest::gtest and GTest::gtest_main regardless of whether they were built statically or shared.

Summary

  • Static builds (BUILD_SHARED_LIBS=OFF) are the default and produce libgtest.a (or .lib) with empty export macros.
  • Shared builds (BUILD_SHARED_LIBS=ON) require WINDOWS_EXPORT_ALL_SYMBOLS on Windows and generate gtest_export.h for proper symbol visibility.
  • Windows shared builds must set gtest_force_shared_crt to ON to link the DLL C runtime correctly.
  • The targets gtest, gtest_main, gmock, and gmock_main are defined in googletest/CMakeLists.txt and googlemock/CMakeLists.txt respectively, handling include paths automatically via INTERFACE_INCLUDE_DIRECTORIES.

Frequently Asked Questions

Can I mix static and shared GoogleTest libraries in the same project?

No, you cannot link both static and shared variants of GoogleTest into the same executable because this causes ODR (One Definition Rule) violations and duplicate symbol errors. Choose one linkage model per project or use separate CMake build directories with different BUILD_SHARED_LIBS values.

Why is gtest_force_shared_crt required on Windows for shared builds?

When building GoogleTest as a DLL on Windows, all components must link against the same C runtime library. Setting gtest_force_shared_crt to ON forces the /MD (multithreaded DLL) compiler flag, ensuring that the GoogleTest DLL and your application share the same heap and runtime state, preventing memory corruption and link errors.

How do downstream projects find GoogleTest headers after installation?

The CMake scripts in googletest/CMakeLists.txt set INTERFACE_INCLUDE_DIRECTORIES on the gtest target to point to both the source include/ directory and the generated build directory. When installed, the GTestConfig.cmake file preserves these paths, so target_link_libraries(your_target PRIVATE GTest::gtest) automatically adds the correct include directories without manual target_include_directories calls.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →