CMake Integration
Part 1: Reference
Integration Points
Because we use CMake to build ApprovalTests.cpp, we also provide integration points for our users.
CMake target
Approval Tests’ CMake build exports an interface target
ApprovalTests::ApprovalTests
. Linking against it will add the proper
include path and all necessary capabilities to the resulting binary.
This target is provided when ApprovalTests.cpp is used as a
subdirectory. Assuming that ApprovalTests.cpp has been cloned to
lib/ApprovalTests.cpp
:
add_subdirectory(lib/ApprovalTests.cpp)
target_link_libraries(tests ApprovalTests::ApprovalTests)
CMake project options
ApprovalTests.cpp’s CMake project also provides some options for other projects that consume it.
The file CMake/ApprovalTestsOptions.cmake defines these options.
The options provided are:
Which targets are built
Note that these are always enabled if this is the top-level project.
APPROVAL_TESTS_BUILD_TESTING
When
ON
, the self-tests are run. Defaults toOFF
.
APPROVAL_TESTS_BUILD_EXAMPLES
When
ON
, the examples are run. Defaults toOFF
.
APPROVAL_TESTS_BUILD_DOCS
When
ON
, documentation files are generated, if Doxygen, Sphinx and mdsnippets are installed. Defaults toOFF
.These are not part of the
ALL
target.
Which third_party libraries are made available
Note that these are always included if this is the top-level project.
APPROVAL_TESTS_BUILD_THIRD_PARTY_CATCH2
When
ON
, this project’s copy of the Catch2 test framework is included. Defaults toOFF
.
APPROVAL_TESTS_BUILD_THIRD_PARTY_DOCTEST
When
ON
, this project’s copy of the doctest test framework is included. Defaults toOFF
.
APPROVAL_TESTS_BUILD_THIRD_PARTY_UT
When
ON
, this project’s copy of the Boost.UT test framework is included. Defaults toOFF
.
Control the behaviour of our builds
APPROVAL_TESTS_ENABLE_CODE_COVERAGE
When
ON
, Approval Test’s own tests are run with code coverage enabled. This uses Lars Bilke’s CodeCoverage.cmake. Defaults toOFF
.
CMake commands support
add_subdirectory()
See Use own ApprovalTests.cpp and Catch2 clones below, for an example using add_subdirectory().
Use case: This is typically for you have your own copy of the Approval Tests project directory that you want to re-use.
FetchContent
See Make CMake clone ApprovalTests.cpp and Catch2 below, for an example using the FetchContent module.
The examples below use
FetchContent_MakeAvailable()
, which requires CMake 3.14 or above.If you only have CMake 3.11 or above, see FetchContent (CMake 3.11+) for how to use
FetchContent_Populate()
.Use case: This is typically for when you want CMake to download a specific version of Approval Tests for you, behind the scenes.
ExternalProject
With CMake before 3.11, see the ExternalProject module. The
FetchContent
examples below should help get started withExternalProject
.Use case: This is typically for when you want CMake to download a specific version of Approval Tests for you, behind the scenes, and you are using an older version of CMake.
find_package()
- not supportedThere is not yet support for find_package().
Single header or CMake Integration?
There are two main options for incorporating Approval Tests in to your project:
Download the single-header file from the Releases page and, typically, add the header to your source code.
Obtain a copy of the entire ApprovalTests.cpp repository and incorporate it in to your CMake build scripts.
Options for obtaining the repository typically include:
cloning it
forking it
including it as a sub-repository
having a build tool, such as CMake, download it for you automatically as part of your builds
CMake Integration Benefits
We recommend using the CMake integration route, which has several user benefits over the single header:
Automatic prevention of most of the scenarios listed in Troubleshooting Misconfigured Build.
Source-code compatibility with the single header download:
For convenience, we provide a wrapper header file ApprovalTests.hpp, which can be used to access all the features of this library, without having to know which features are provided by which header files.
Flexibility in how many of the Approval Tests include files you include:
There is also the option to include just the headers in ApprovalTests/ that you use.
This may slightly improve build speeds.
It may occasionally provide workarounds to bugs.
Part 2: Optional Explanatory Examples
Scenarios when using Approval Tests
Context
Example Project
Suppose you are writing some tests that use ApprovalTests.cpp with the Catch2 test framework.
Your top-level CMakeLists.txt
file might look something like this:
cmake_minimum_required(VERSION 3.14 FATAL_ERROR)
# This version is needed for FetchContent_Declare & FetchContent_MakeAvailable
project(fetch_content_approvaltests)
enable_testing()
add_subdirectory(dependencies)
add_subdirectory(tests)
(See snippet source)
The important thing to note, for following the examples below, is the
add_subdirectory(dependencies)
line. It makes CMake load a file
dependencies/CMakeLists.txt
.
Each example below shows a dependencies/CMakeLists.txt
, for the
corresponding scenario. All other code is identical between the example
directories.
Here is this example project.
Example Tests
Your tests/CMakeLists.txt
file might look something like this:
add_executable(tests
main.cpp
tests.cpp
)
target_link_libraries(tests ApprovalTests::ApprovalTests Catch2::Catch2)
target_compile_features(tests PUBLIC cxx_std_11)
set_target_properties(tests PROPERTIES CXX_EXTENSIONS OFF)
add_test(
NAME tests
COMMAND tests)
(See snippet source)
This says that the libraries ApprovalTests::ApprovalTests
and
Catch2::Catch2
are required by the tests
executable.
Here is this example project’s test directory.
Dependencies
How might you enable CMake to provide those libraries? In other words,
what are the options for the contents of
dependencies/CMakeLists.txt
?
The next few sections describe some options.
Make CMake clone ApprovalTests.cpp and Catch2
Note: The files in this section can be viewed and downloaded from fetch_content_approvaltests_catch2.
The following is for when you just want ApprovalTests.cpp and Catch2 to be downloaded as part of your project’s build. You don’t particularly want to see their source code, although you’re happy if your debugger steps in to them.
It also needs CMake 3.14 or above.
We use this dependencies/CMakeLists.txt
file:
# Needs CMake 3.14 or above
include(FetchContent)
# -------------------------------------------------------------------
# ApprovalTests.cpp
FetchContent_Declare(ApprovalTests
GIT_REPOSITORY https://github.com/approvals/ApprovalTests.cpp.git
GIT_TAG master)
FetchContent_MakeAvailable(ApprovalTests)
# -------------------------------------------------------------------
# Catch2
FetchContent_Declare(Catch2
GIT_REPOSITORY https://github.com/catchorg/Catch2.git
GIT_TAG v2.11.1)
FetchContent_MakeAvailable(Catch2)
(See snippet source)
Note the GIT_TAG
values: This tells CMake which revision of
dependencies to use. The value can be a tag or a git commit ID. Here we
use master
, to always test our integrations with the latest Approval
Tests code. However, it is generally recommended to pin your
dependencies to specific versions, and test behaviour before updating to
newer versions.
After CMake has generated the build files, the directory structure would
look something like this, where the cmake-build-debug
directory is
the build space, and the cmake-build-debug/_deps
contains the
downloaded and built ApprovalTests.cpp and Catch2 repositories:
fetch_content_approvaltests_catch2/
.git/
cmake-build-debug/
_deps/
approvaltests-build/
approvaltests-src/
approvaltests-subbuild/
catch2-build
catch2-src
catch2-subbuild
...
CMakeLists.txt
dependencies/
CMakeLists.txt
tests/
...
Make CMake clone ApprovalTests.cpp
Note: The files in this section can be viewed and downloaded from fetch_content_approvaltests.
The only difference between the previous example and this one is that here we use the Catch2 header that is in the ApprovalTests.cpp project.
We use this dependencies/CMakeLists.txt
file:
# Needs CMake 3.14 or above
include(FetchContent)
# -------------------------------------------------------------------
# ApprovalTests.cpp
FetchContent_Declare(ApprovalTests
GIT_REPOSITORY https://github.com/approvals/ApprovalTests.cpp.git
GIT_TAG master)
# Tell the ApprovalTests CMake files that we want to use its copy of Catch2:
set(APPROVAL_TESTS_BUILD_THIRD_PARTY_CATCH2 ON CACHE BOOL "")
FetchContent_MakeAvailable(ApprovalTests)
(See snippet source)
We have set APPROVAL_TESTS_BUILD_THIRD_PARTY_CATCH2
to ON
, so
that CMake will use the copy of Catch2 that is included in the
ApprovalTests.cpp repository.
There are also options to enable use of ApprovalTests.cpp’s copies of all other supported test frameworks except GoogleTest, including:
APPROVAL_TESTS_BUILD_THIRD_PARTY_DOCTEST
APPROVAL_TESTS_BUILD_THIRD_PARTY_UT
Use own ApprovalTests.cpp and Catch2 clones
Note: The files in this section can be viewed and downloaded from add_subdirectory_approvaltests_catch2.
Here, instead of getting CMake to download ApprovalTests.cpp and Catch2, we have got our own clones or forks of them, which we want to use with our own tests.
In this example, the directory structure looks like this:
ApprovalTests.cpp/
.git/
CMakeLists.txt
...
Catch2/
.git/
CMakeLists.txt
...
add_subdirectory_approvaltests_catch2/
.git/
CMakeLists.txt
dependencies/
CMakeLists.txt
tests/
We use this dependencies/CMakeLists.txt
file:
# -------------------------------------------------------------------
# ApprovalTests.cpp
add_subdirectory(
../../ApprovalTests.cpp
${CMAKE_CURRENT_BINARY_DIR}/approvaltests.cpp_build
)
# -------------------------------------------------------------------
# Catch2
set(CATCH_BUILD_TESTING OFF CACHE BOOL "")
add_subdirectory(
../../Catch2
${CMAKE_CURRENT_BINARY_DIR}/catch2_build
)
(See snippet source)
Here we use add_subdirectory()
. This works with older versions of
CMake, unlike the FetchContent
examples above.
The above was tested with CMake 3.8.
If your directory layout differed from the above, you would change the
relative paths in the add_subdirectory()
lines.
Using other supported test frameworks
To save space and repetition, the examples above only show the Catch2 test framework.
The same principles apply when using all the other test frameworks supported by ApprovalTests.cpp.
Scenarios when developing ApprovalTests.cpp
Developing ApprovalTests.cpp with test framework sources
Note: The files in this section can be viewed and downloaded from dev_approvals.
For Approval Tests project maintainers, it is useful to be able to edit and debug both this project and the test frameworks that it depends upon. It helps to be able to see the source of these frameworks, rather than just the single-header releases that are copied in to the third_party directory here.
This also allows us to checkout different commits of any of these projects.
Here we want to enable and run all the ApprovalTests.cpp tests, unlike the cases above, where we only want to run the tests of the project that is being developed.
Consider this directory structure, where the repositories for all these
projects are checked out side-by-side, and there is an extra directory
dev_approvals/
that will contain just a CMakeLists.txt
file, to
set up a project containing all the other directories:
ApprovalTests.cpp/
.git/
CMakeLists.txt
...
Catch2/
.git/
CMakeLists.txt
...
doctest/
.git/
CMakeLists.txt
...
googletest/
.git/
CMakeLists.txt
...
ut/
.git/
CMakeLists.txt
...
dev_approvals/
CMakeLists.txt
The file dev_approvals/CMakeLists.txt
creates a kind of “super
build”: one project for developing ApprovalTests.cpp and all the
projects it depends on:
cmake_minimum_required(VERSION 3.8 FATAL_ERROR)
project(dev_approvals)
enable_testing()
set(CMAKE_VERBOSE_MAKEFILE off)
# Prevent ctest creating cluttering up CLion with nearly 30 CTest targets
# (Continuous, ContinuousBuild etc) when it does:
# include(CTest)
# This hack taken from https://stackoverflow.com/a/57240389/104370
set_property(GLOBAL PROPERTY CTEST_TARGETS_ADDED 1) # hack to prevent CTest added targets
# -------------------------------------------------------------------
# boost
# This will be used by find_package() in ApprovalTests.cpp/tests/Boost_Tests
# If there is a local boost directory, use tat.
# Otherwise, require the user to have installed boost (as is done in CI builds)
if (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/../boost)
set(BOOST_ROOT ${CMAKE_CURRENT_SOURCE_DIR}/../boost)
endif()
# -------------------------------------------------------------------
# Catch2
set(CATCH_BUILD_TESTING OFF CACHE BOOL "")
add_subdirectory(
../Catch2
${CMAKE_CURRENT_BINARY_DIR}/catch2_build
)
# -------------------------------------------------------------------
# CppUTest
# Prevent CppUTest's own tests from being built
set(TESTS OFF CACHE BOOL "")
# Prevent build of CppUTest from generating thousands of lines of
# -Wc++98-compat and -Wc++98-compat-pedantic warnings:
set(C++11 ON CACHE BOOL "Compile with C++11 support")
add_subdirectory(
../cpputest
${CMAKE_CURRENT_BINARY_DIR}/cpputest_build
)
# -------------------------------------------------------------------
# doctest
add_subdirectory(
../doctest
${CMAKE_CURRENT_BINARY_DIR}/doctest_build
)
# -------------------------------------------------------------------
# filesystem
set(CATCH_BUILD_TESTING OFF CACHE BOOL "")
add_subdirectory(
../filesystem
${CMAKE_CURRENT_BINARY_DIR}/filesystem_build
)
# -------------------------------------------------------------------
# fmt
set(CATCH_BUILD_TESTING OFF CACHE BOOL "")
add_subdirectory(
../fmt
${CMAKE_CURRENT_BINARY_DIR}/fmt_build
)
# -------------------------------------------------------------------
# GoogleTest
# Prevent GoogleTest from overriding our compiler/linker options
# when building with Visual Studio
set(gtest_force_shared_crt ON CACHE BOOL "" )
add_subdirectory(
../googletest
${CMAKE_CURRENT_BINARY_DIR}/googletest_build
)
# -------------------------------------------------------------------
# Boost.ut
set(BUILD_BENCHMARKS OFF CACHE BOOL "")
set(BUILD_EXAMPLES OFF CACHE BOOL "")
set(BUILD_TESTS OFF CACHE BOOL "")
add_subdirectory(
../ut
${CMAKE_CURRENT_BINARY_DIR}/ut_build
)
if(TARGET Boost::ut)
add_library(boost.ut ALIAS ut)
endif()
if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")
# Turn off some checks off for boost.ut
target_compile_options(ut INTERFACE
-Wno-c99-extensions # Needed for Boost.ut, at least in v1.1.6
-Wno-documentation-unknown-command # unknown command tag name \userguide
-Wno-weak-vtables
-Wno-comma # See https://github.com/boost-ext/ut/issues/398
)
endif()
# -------------------------------------------------------------------
# ApprovalTests.cpp
set(APPROVAL_TESTS_BUILD_TESTING ON CACHE BOOL "")
set(APPROVAL_TESTS_BUILD_EXAMPLES ON CACHE BOOL "")
add_subdirectory(
../ApprovalTests.cpp
${CMAKE_CURRENT_BINARY_DIR}/approvaltests.cpp_build
)
(See snippet source)