Example Project using GNU Style Structure
This chapter provides an overview of a project using the GNU style structure that uses CMaize as its build system.
Project Overview
ExampleGNUProject is a simple C++ project that consists of the following
components:
The
travel_cost_utilslibrary that contains some simple arithmetic calculations for use in calculating travel costs.The
travel_cost_calcexecutable that calculates travel cost for a set of predefined values using functions intravel_cost_utils.The
testdirectory that contains some tests written using the Catch2 library that test thattravel_cost_utilsworks properly.
Here is the directory structure of the project:
ExampleGNUProject
├── CMakeLists.txt
├── cmake
│ └── get_cmaize.cmake
├── include
| └── example_gnu_project
| └── travel_cost_utils
| └── travel_cost_utils.hpp
├── src
│ ├── travel_cost_utils
| | └── travel_cost_utils.cpp
│ └── travel_cost_calc
│ └── travel_cost_calc.cpp
└── test
└── travel_cost_utils
└── test_travel_cost_utils.cpp
This structure breaks the project down in the following way:
include/example_gnu_projectcontains the public headers for the projects libraries, separated into directories for each library.srccontains the source code for the project, separated into subdirectories for each executable and library.testcontains the tests for the project, broken down into subdirectories
The advantage to using this type of structure over the CMake style structure is that this structure explicitly separates public and private header files. The CMake style structure requires the user to handle the public/private-ness of header files.
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_gnu_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}/src/travel_cost_utils"
INCLUDE_DIR "${CMAKE_CURRENT_LIST_DIR}/include/example_gnu_project/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}/src/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:
Create a target name for it (
Catch2in this case)Point to its GitHub repository URL using the
URLkeywordSpecify the target we want to build in the repository using the
BUILD_TARGETkeywordSpecify the name we want to use to find this library using the
FIND_TARGETkeywordAdditionally we will pass in the
BUILD_TESTING=OFFargument so that no tests are built for the library. Passing in arguments is accomplished by using theCMAKE_ARGSkeyword.
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.