2  Step 2

2.1 Exercise 1 - Creating a Library

Goal: Add and use a library

To add a library with CMake, use the add_library() command and specify the source files that make up the library.

Instead of placing all source files in a single directory, we can organize our project with one or more subdirectories. Here we create a subdirectory specifically for our library.

To this subdirectory we add another CMakeLists.txt file and source files.

In the top level CMakeLists.txt file, use the add_subdirectory() command to add the subdirectory to the build.

The library is connected to the executable target with

  • target_include_directories()
  • target_link_libraries()

Getting Started

We add a library that contains own implementation for computing square root of a number. The executable can then optionally use this library instead of the standard square root function.

The libary is put into a subdirectory MathFunctions. This directory already contains:

  • header files:
    • mysqrt.h
    • MathFunctions.h
  • their respective source files:
    • mysqrt.cxx contains custom implementation of square root function
    • MathFunctions.cxx contains a wrapper around sqrt function from msqrt.cxx in order to hide implementation details.
  • TODO: 1 - 6
    1. Creating a library target
    2. Making use of the new library target
    3. Linking the new library target to the executable target
    4. Specifying library’s header location
    5. Using the library
    6. Replacing sqrt with the wrapper function mathfunctions::sqrt

In the CMakeLists.txt file in the MathFunctions directoyr, we craete a library target called MathFunctions with add_library().

TODO 1 - Creating a Library Target

In the CMakeLists.txt in the MathFunctions directory, we create a library target called MathFunctions with add_library():

MathFunctions/CMakeLists.txt
# TODO 1: Add a library called MathFunctions with sources MathFunctions.cxx
add_library(MathFunctions MathFunctions.cxx mysqrt.cxx)

The source files of the library are passed as arguments.

TODO 2 - Making use of the new Library

To make use of the new library we add an add_subdirectory() in the top-level CMakeLists.txt:

CMakeLists.txt
add_subdirectory(MathFunctions)

TODO 3 - Linking the new Library Target to the Executable Target

We link the new library target to the executable target with target_link_libraries()

CMakeLists.txt
target_link_libraries(Tutorial PUBLIC MathFunctions)

TODO 4 - Specifying Library’s Header File Location

Modify the existing target_include_directories() to add the MathFunctions subdirectory as an include directory so that the MathFunctions.h header file can be found:

CMakeLists.txt
# TODO 4: Add MathFunctions to Tutorial's target_include_directories()
# Hint: ${PROJECT_SOURCE_DIR} is a path to the project source. AKA This folder!

# add the binary tree to the search path for include files
# so that we will find TutorialConfig.h
target_include_directories(Tutorial PUBLIC
                           "${PROJECT_BINARY_DIR}"
                           "${PROJECT_BINARY_DIR}/MathFunctions"
                           )

TODO 5 & 6- Using the Library

We use the library by including MathFunctions.h in tutorial.cxx:

tutorial.cxx
// TODO 5: Include MathFunctions.h
#include "MathFunctions/MathFunctions.h"

Replace sqrt with the wrapper function mathfunctions::sqrt:

tutorial.cxx
  // TODO 6: Replace sqrt with mathfunctions::sqrt

  // calculate square root
  // const double outputValue = sqrt(inputValue);
  const double outputValue = mathfunctions::sqrt(inputValue);

2.2 Exercise 2 - Adding an Option

In this exercise we add an option in the MathFunctions library to allow developers to select either the custom or the built-in implementation using the option() command

Goal: Add an option to build without MathFunctions

Getting Started

We will create a variable USE_MYMATH using option() in MathFunctions/CMakeLists.txt There we use that option to pass a compile time definition to the MathFunctions library.

Then, update MathFunctions.cxx to redirect compilation based on USE_MYMATH.

Lastly, we prevent mysqrt.cxx from being compiled when USE_MYMATH is on by making it its own library inside of the USE_MYMATH block of MathFunctions/CMakeLists.txt

TODOS: 7 - 14:

  1. Add an option to MathFunctions/CMakeLists.txt
  2. Make building and linking our library with mysqrt function conditional using this new option
  3. Add the corresponding changes to the source code MathFunctions/MathFunctions.cxx
  4. Including mysqrt.h if the optional varible is defined.
  5. Including cmath as well
  6. Ommitting unneccesary usage/build of mysqrt.cxx if the custom option is off.
  7. Link SqrtLibrary onto MathFunctions when the optional variable is enabled.
  8. We remove mysqrt.cxx from MathFunctions library source list because it will be pulled when SqrtLibrary is enabled.

TODO 7 - Adding an Option

We add an option to MathFunctions/CMakeLists.txt. This will be displayed in the cmake-gui and ccmake with a default value of ON.

MathFunctions/CMakeLists.txt
# TODO 7: Create a variable USE_MYMATH using option and set default to ON
option(USE_MYMATH "Use custom math implementation" ON)

TODO 8 - Make Building and Linking the Library Conditional

Make building and linking our library with mysqrt function conditional using this new option.

Create an if() statement which checks the value of USE_MATH. Inside the if() put the target_compile_definitions() command with the compile definition USE_MYMATH:

MathFunctions/CMakeLists.txt
# TODO 8: If USE_MYMATH is ON, use target_compile_definitions to pass
# USE_MYMATH as a precompiled definition to our source files
if(USE_MYMATH)
    target_compile_definitions(MathFunctions PRIVATE "USE_MYMATH")
endif()

Now when USE_MYMATH is ON, the compile definition USE_MYMATH will be set. We can then use this compile definitnion to enable or disable sections of our source code.

TODO 9 - Adding the Changes to the Source Code

We add the corresponding changes to the source code. In MathFunctions.cxx we use USE_MYMATH to control which square root function is used:

MathFunctions/MathFunctions.cxx
  // TODO 9: If USE_MYMATH is defined, use detail::mysqrt.
  // Otherwise, use std::sqrt.
  #ifdef USE_MYMATH
    return detail::mysqrt(x);
  #else
    return std::sqrt(x);
  #endif

TODO 10 - Including mysqrt.h Conditionally

Next, we need to include mysqrt.h if USE_MYMATH is defined.

MathFunctions/MathFunctions.cxx
// TODO 10: Wrap the mysqrt include in a precompiled ifdef based on USE_MYMATH
#ifdef USE_MYMATH
  #include "mysqrt.h"
#endif

TODO 11 - Including cmath

Now since we use std::sqrt() (see TODO 9), we must include cmath:

MathFunctions/MathFunctions.cxx
// TODO 11: include cmath
#include <cmath>

TODO 12 & 13 - Omitting Compilation of mysqrt.cxx if Option is off

At this piont, even if USE_MYMATH is off, mysqrt.cxx would not be used but still compiled because MathFunctions target has mysqrt.cxx listed under sources.

We can fix this in various ways:

  1. use target_sources() to add mysqrt.cxx rom within the USE_MYMATH block.
  2. create an additional library within the USE_MYMATH block which is responsible for compiling mysqrt.cxx.

We will go with the second option.

First we create an additional library from within USE_MYMATH called SqrtLibrary that has sources mysqrt.cxx:

MathFunctions/CMakeLists.txt
if(USE_MYMATH)
    target_compile_definitions(MathFunctions PRIVATE "USE_MYMATH")
    # TODO 12: When USE_MYMATH is ON, add a library for SqrtLibrary with
    # source mysqrt.cxx
    add_library(SqrtLibrary STATIC
                mysqrt.cxx)
endif()

Next, we link SqrtLibrary onto MathFunctions when USE_MYMATH is enabled:

MathFunctions/CMakeLists.txt
if(USE_MYMATH)
    target_compile_definitions(MathFunctions PRIVATE "USE_MYMATH")
    # TODO 12: When USE_MYMATH is ON, add a library for SqrtLibrary with
    # source mysqrt.cxx
    add_library(SqrtLibrary STATIC
                mysqrt.cxx)
    # TODO 13: When USE_MYMATH is ON, link SqrtLibrary to the MathFunctions Library
    target_link_libraries(MathFunctions PRIVATE SqrtLibrary)
endif()

TODO 14 - Removing mysqrt.cxx from Library Source

Finally, we can remove mysqrt.cxx from our MathFunctions library source list because it will be pulled when SqrtLibrary is included.

MathFunctions/CMakeLists.txt
add_library(MathFunctions MathFunctions.cxx)

With these changes, the mysqrt function is now completely optional to whoever is building and using MathFunctions library. Users can toggle USE_MYMATH to this end.

Building & Running

We can manually configure CMake to use the variable providing an option from the command line:

cmake ../ -DUSE_MYMATH=OFF #or ON

Alternatively we can use cmake-gui or ccmake:

ccmake ../

and set the automatically detected USE_MYMATH variable via the user interface. Afterwards build from within the build directory:

cmake --build .