Example Project using CMake Style Structure

This chapter provides an overview of a project structured in a style that is common to CMake projects that uses CMaize as its build system.

Project Overview

ExampleCMakeProject is a simple C++ project that consists of the following components:

  • The travel_cost_utils library that contains some simple arithmetic calculations for use in calculating travel costs.

  • The travel_cost_calc executable that calculates travel cost for a set of predefined values using functions in travel_cost_utils.

  • The test directory that contains some tests written using the Catch2 library that test that travel_cost_utils works properly.

Here is the file structure of the project:

ExampleCMakeProject
├── CMakeLists.txt
├── cmake
│   └── get_cmaize.cmake
├── travel_cost_utils
|   ├── travel_cost_utils.cpp
|   └── travel_cost_utils.hpp
├── travel_cost_calc
|   └── travel_cost_calc.cpp
└── test
    └── test_travel_cost_utils.cpp

This structure is commonly found in CMake projects. A subdirectory is created for each executable and library. A subdirectory is also created the project’s tests.

Creating the CMakeLists.txt File

Here we will cover the contents of the CMakeLists.txt file at the root of the project.

The Basics

First we must start with the basics. We need to declare the minimum required version of CMake, the name and version of the project, the language of the project, and the C++ standard of the project.

# Declare minimum version required
cmake_minimum_required(VERSION 3.13.4)

# Declare project name and version
project(example_cmake_project VERSION 1.0)

# Set the project language
set(LANGUAGE CXX)

# Set C++ standard
set(CMAKE_CXX_STANDARD 14)

Including CMaize

We also need to include CMaize. This is described more in Automatically Downloading and Including CMaize.

# Include CMaize automatically
include("${PROJECT_SOURCE_DIR}/cmake/get_cmaize.cmake")

Adding the TravelCostUtils Library

Now we will add our travel_cost_utils library. We need to specify the target name, its source directory, and its include directory:

# Add our travel_cost_utils library
cpp_add_library(
    travel_cost_utils
    SOURCE_DIR "${CMAKE_CURRENT_LIST_DIR}/travel_cost_utils"
    INCLUDE_DIR "${CMAKE_CURRENT_LIST_DIR}/travel_cost_utils"
)

Adding the TravelCostCalc Executable

Here we add our travel_cost_calc executable. We simply need to specify the target name, its source directory, and its dependencies:

# Add the travel_cost_calc executable
cpp_add_executable(
    travel_cost_calc
    SOURCE_DIR "${CMAKE_CURRENT_LIST_DIR}/travel_cost_calc"
    DEPENDS travel_cost_utils
)

Adding Tests

Now we will add our tests. First, we’ll create an if block so that our tests are only built if the BUILD_TESTING argument is set to ON (or some other “truthy” value):

if("${BUILD_TESTING}")

    # Stuff for building tests will go here

endif()

Next we will add a target for finding or building the Catch2 unit testing framework. We need to do the following:

  1. Create a target name for it (Catch2 in this case)

  2. Point to its GitHub repository URL using the URL keyword

  3. Specify the target we want to build in the repository using the BUILD_TARGET keyword

  4. Specify the name we want to use to find this library using the FIND_TARGET keyword

  5. Additionally we will pass in the BUILD_TESTING=OFF argument so that no tests are built for the library. Passing in arguments is accomplished by using the CMAKE_ARGS keyword.

if("${BUILD_TESTING}")

    # Get the Catch2 unit testing framework
    cpp_find_or_build_dependency(
        Catch2
        URL github.com/catchorg/Catch2
        BUILD_TARGET Catch2
        FIND_TARGET Catch2::Catch2
        CMAKE_ARGS BUILD_TESTING=OFF
    )

endif()

Finally, to add our tests, we just need to specify the source directory and the tests dependencies (which in this case are our TravelCostUtils library and the Catch2 testing framework). We will put our tests and their dependencies within an if block so that they are only built if users set the BUILD_TESTING argument to ON (or some other “truthy” value).

# Build tests if build testing is enabled
if("${BUILD_TESTING}")

    # Get the Catch2 unit testing framework
    cpp_find_or_build_dependency(
        Catch2
        URL github.com/catchorg/Catch2
        BUILD_TARGET Catch2
        FIND_TARGET Catch2::Catch2
        CMAKE_ARGS BUILD_TESTING=OFF
    )

    # Add the tests
    cpp_add_tests(
        travel_cost_utils_test
        SOURCE_DIR "${CMAKE_CURRENT_LIST_DIR}/test"
        DEPENDS travel_cost_utils Catch2::Catch2
    )

endif()

Note

Here we use Catch2::Catch2 to find the Catch2 test framework as this is the name of the target exported by Catch2.

Building the Project

The project can be built by running the following commands at the root of the project:

# Create the build system
cmake -B build

# Build the project
cmake --build build

Running the Executable and Tests

We can run our executable and our tests with the following commands:

# Run the executable
./build/travel_cost_calc

# Run the tests
./build/travel_cost_utils_test

Final Project Code

This final example project can be viewed and downloaded here.