Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update CMake quickstart with find_package + add CMake source build guide #4682

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions docs/_data/navigation.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ nav:
url: "/gmock_cook_book.html"
- title: "Mocking Cheat Sheet"
url: "/gmock_cheat_sheet.html"
- title: "Building from Source: CMake"
url: "/source-build-cmake.html"
- section: "References"
items:
- title: "Testing Reference"
Expand Down
84 changes: 68 additions & 16 deletions docs/quickstart-cmake.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ To complete this tutorial, you'll need:

* A compatible operating system (e.g. Linux, macOS, Windows).
* A compatible C++ compiler that supports at least C++14.
* [CMake](https://cmake.org/) and a compatible build tool for building the
project.
* [CMake](https://cmake.org/) >= 3.14 and a compatible build tool for building
the project.
* Compatible build tools include
[Make](https://www.gnu.org/software/make/),
[Ninja](https://ninja-build.org/), and others - see
Expand Down Expand Up @@ -42,10 +42,63 @@ $ mkdir my_project && cd my_project
```

Next, you'll create the `CMakeLists.txt` file and declare a dependency on
GoogleTest. There are many ways to express dependencies in the CMake ecosystem;
in this quickstart, you'll use the
[`FetchContent` CMake module](https://cmake.org/cmake/help/latest/module/FetchContent.html).
To do this, in your project directory (`my_project`), create a file named
GoogleTest. There are many ways to express dependencies in the CMake ecosystem
but the two most common methods are

1. Using the [`find_package`](https://cmake.org/cmake/help/latest/command/find_package.html)
command
2. Using the [`FetchContent`](https://cmake.org/cmake/help/latest/module/FetchContent.html)
CMake module

We will cover both methods in their [respective](#using-find_package)
[subsections](#using-FetchContent) as each has their
advantages and disadvantages.

### Using `find_package`

One very common scenario is when you would like to consume a
standalone GoogleTest installation, e.g. one
[built and installed from source](source-build-cmake.md)
locally, or one provided by a system package manager (e.g. APT, etc. on
Debian-like systems). In this case,
[`find_package`](https://cmake.org/cmake/help/latest/command/find_package.html)
is the better fit.

We can write a simple `CMakeLists.txt` as follows, using `find_package` in
[config mode](https://cmake.org/cmake/help/latest/command/find_package.html#search-modes):

```cmake
cmake_minimum_required(VERSION 3.14)
project(my_project)

# GoogleTest requires at least C++14
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

# Since CMake 3.20 CONFIG can be omitted as the FindGTest find module will
# prefer the upstream (provided by Google Test) GTestConfig.cmake if available
find_package(GTest 1.15.0 REQUIRED CONFIG)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think there is no need to differentiate; target_link_libraries should be directly followed by find_package.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The target_link_libraries call is in the "Create and run a binary" section; here I just mirror the FetchContent section.

```

A custom installation root can be specified using `GTEST_ROOT` as an environment
or CMake cache variable as mentioned in the
[`FindGTest` docs](https://cmake.org/cmake/help/latest/module/FindGTest.html#cache-variables).

### Using `FetchContent`

Another common scenario is when one wants to absolutely ensure all dependencies
use the same compile and link flags by building all of them from source with
the same settings used by the project. There may be additional requirements such
as allowing tracking of upstream changes as they flow into these dependencies'
source trees.

In this case
[`FetchContent`](https://cmake.org/cmake/help/latest/module/FetchContent.html)
is the tool of choice, allowing one to download a specific source checkout into
their project build tree and then build it as a vendored component within the
project.

So to do this, in your project directory (`my_project`), create a
`CMakeLists.txt` with the following contents:

```cmake
Expand All @@ -61,8 +114,13 @@ FetchContent_Declare(
googletest
URL https://github.com/google/googletest/archive/03597a01ee50ed33e9dfd640b249b4be3799d395.zip
)
# For Windows: Prevent overriding the parent project's compiler/linker settings
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
# Windows: Ensure C runtime linkage uses CMake defaults (shared C runtime).
# This can be omitted if you would like to use Google Test's preference of
# linking against static C runtime for static Google Test builds, shared C
# runtime for shared Google Test library builds.
if(MSVC)
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
endif()
FetchContent_MakeAvailable(googletest)
```

Expand Down Expand Up @@ -103,14 +161,8 @@ To build the code, add the following to the end of your `CMakeLists.txt` file:
```cmake
enable_testing()

add_executable(
hello_test
hello_test.cc
)
target_link_libraries(
hello_test
GTest::gtest_main
)
add_executable(hello_test hello_test.cc)
target_link_libraries(hello_test PRIVATE GTest::gtest_main)

include(GoogleTest)
gtest_discover_tests(hello_test)
Expand Down
164 changes: 164 additions & 0 deletions docs/source-build-cmake.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
# Building from Source: CMake

Sometimes, it may be desirable to produce one's own local build and install
of GoogleTest. For example, one may want to check out a particular release tag,
e.g. `v1.15.2`, and then build and install GoogleTest to one's specification.

For example, on Windows platforms, where the complication of
[different C runtimes](https://learn.microsoft.com/en-us/cpp/c-runtime-library/crt-library-features?view=msvc-170)
exists and where there can be issues if ABI-incompatible C runtimes
[are mixed](https://learn.microsoft.com/en-us/cpp/c-runtime-library/crt-library-features?view=msvc-170#what-problems-exist-if-an-application-uses-more-than-one-crt-version),
developers will often have a debug version of a dependency built, linked against
the debug shared C runtime, with the release version of said dependency built
and linked against the release shared C runtime. By default, GoogleTest uses
static C runtime for static (the default) builds and the shared C runtime for
shared builds, e.g. with `BUILD_SHARED_LIBS` set to `ON`, which is sometimes
not the desired behavior on Windows.

## Prerequisites

To build and install GoogleTest from source, one needs:

* A compatible operating system (e.g. Linux, macOS, Windows).
* A compatible C++ compiler that supports at least C++14.
* [CMake](https://cmake.org/) >= 3.14 and a compatible build tool.
* Compatible build tools include
[Make](https://www.gnu.org/software/make/),
[Ninja](https://ninja-build.org/), and others - see
[CMake Generators](https://cmake.org/cmake/help/latest/manual/cmake-generators.7.html)
for more information.
* Optionally [Git](https://git-scm.com/) if one would like to build from the
GoogleTest source tree.

See [Supported Platforms](platforms.md) for more information about platforms
compatible with GoogleTest.

If you don't already have CMake installed, see the
[CMake installation guide](https://cmake.org/install).

## Building for \*nix

To build on \*nix systems, after cloning the GoogleTest source from GitHub and
then checking out a particular commit or release tag, e.g. `v1.15.2`, usually
the following is sufficient for
[single-config generators](https://cmake.org/cmake/help/latest/manual/cmake-buildsystem.7.html#build-configurations):

```shell
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build -j
```

This will build the Release config static libraries in parallel using all
available processors, generally using Make as the underlying build tool. To use
a different single-config generator like Ninja, the configure step can be
changed to

```shell
cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release
```

One can install into their desired installation root with something like this:

```shell
cmake --install build --prefix ../googletest-1.15.2
```

For multi-config generators like
[Ninja Multi-Config](https://cmake.org/cmake/help/latest/generator/Ninja%20Multi-Config.html),
the [`--config`](https://cmake.org/cmake/help/latest/manual/cmake.1.html#cmdoption-cmake-build-config) flag needs to specified to `cmake --build`
to select the build config that will actually be built. Furthermore, if one
wants to install the debug libraries side-by-side with the release libraries,
a debug suffix should be specified for the libraries, otherwise the files will
get clobbered.

{: .callout .note}
Note: On \*nix systems the utility of installing separate debug libraries is
less because unlike Windows, which has multiple C runtimes that are often
ABI-incompatible, there is typically only one `libc` and `libstdc++` or
`libc++` runtime being linked against, so ABI incompatibility is a non-issue.
Therefore, unless necessary, prefer using a single-config generator to build
and install GoogleTest from source.

To use a multi-config generator, in particular Ninja Multi-Config, generally
the following would be desirable:

```shell
cmake -S . -B build -G "Ninja Multi-Config" -DCMAKE_DEBUG_POSTFIX=d
cmake --build build --config Debug -j
cmake --build build --config Release -j
```

The `-DCMAKE_DEBUG_POSTFIX=d` ensures that debug libraries are built with a
`d` suffix in their names, e.g. `libgtestd.a`, to prevent clobbering on
installation. Then, one can install both configs into the installation root
with something like:

```shell
cmake --install build --prefix ../googletest-1.15.2 --config Debug
cmake --install build --prefix ../googletest-1.15.2 --config Release
```

{: .callout .note}
Note: The pkg-config files that will be installed will contain linker lines that
are non suffixed, e.g. `-lgtest`, so if you are using pkg-config, your builds
will link against `libgtest.a`. But if you are building GoogleTest using CMake,
generally you should be using GoogleTest via CMake, so this is not a big issue.

## Building for Windows

On Windows, the default generator selected by CMake will be the most recent
Visual Studio generator, e.g.
[Visual Studio 17 2022](https://cmake.org/cmake/help/latest/generator/Visual%20Studio%2017%202022.html),
which is a multi-config generator. Although one can use Ninja instead, as it is
bundled with recent Visual Studio installations, due to peculiarities of the
multiple Visual Studio developer command prompts available and the inability of
Ninja to select a particular target architecture with the CMake
[`-A`](https://cmake.org/cmake/help/latest/manual/cmake.1.html#cmdoption-cmake-A)
flag like the Visual Studio generators, we consider only the standard
Visual Studio multi-config generators.

To build 64-bit binaries, after `cd`-ing into the GoogleTest repo or
source distribution, generally the following is desirable:

{: .callout .note}
Note: To build 32-bit binaries, replace `-A x64` with `-A Win32` instead.

```
cmake -S . -B build -A x64 -DCMAKE_DEBUG_POSTFIX=d -Dgtest_force_shared_crt=ON
cmake --build build --config Debug -j
cmake --build build --config Release -j
```

Like the multi-config build instructions in the
[previous section](#building-for-nix) we ensure debug libraries have a `d`
suffix with `-DCMAKE_DEBUG_POSTFIX=d` while we ensure that the static
GoogleTest libraries are linked against the shared C runtime libraries with
`-Dgtest_force_shared_crt=ON`. GoogleTest by default will have static builds
link against the static C runtime, shared builds link against the shared C
runtime, which in some cases is undesirable and goes against the CMake defaults
for the
[`MSVC_RUNTIME_LIBRARY`](https://cmake.org/cmake/help/latest/prop_tgt/MSVC_RUNTIME_LIBRARY.html)
target property.

As previously shown, we can then install both configs with something like:

```
cmake --install build --prefix ..\googletest-1.15.2 --config Debug
cmake --install build --prefix ..\googletest-1.15.2 --config Release
```

## Usage from CMake

Now that you have a separate installation of Google Test built from source, you
may consume it from a `CMakeLists.txt` with
[`find_package`](https://cmake.org/cmake/help/latest/command/find_package.html)
in the usual manner:

```cmake
find_package(GTest 1.15.0 REQUIRED CONFIG)
```

If necessary, `GTEST_ROOT` can be set in the environment or as a CMake cache
variable to help CMake locate your installation as documented in the
[`FindGTest`](https://cmake.org/cmake/help/latest/module/FindGTest.html)
documentation.