Using CMake

Programming Workshop 2 (CSCI 1061U)

Faisal Qureshi

Faculty of Science, Ontario Tech University

http://vclab.science.uoit.ca


What is CMake

CMake is a cross-platform family of tools for building, testing, and maintaining large software systems. This tutorial is designed to serve as a starting point for using CMake to build c/c++ programs. You can find more information about CMake at https://cmake.org.

*The following tutorial borrows heavily from the official CMake guide available here.

Example 1

Source files are available here.

Task

Lets consider a simple program that comprises three files.

We want to create an executable Example1.

Recipe

Step 1 (Write a CMakeList.txt)

We will begin by creating a CMakeLists.txt that will include the script needed to complete the above task. Lines that start with `#** are comments.

CMakeLists.txt

cmake_minimum_required(VERSION 3.10)

# project name
project(Example1)

add_executable(Example1 main.cpp util.cpp)

Step 2 (Generate platform-specific build system)

At this point the directory structure looks as follows:

[... using-cmake] $ tree example1
example1
└── src
    ├── CMakeLists.txt
    ├── main.cpp
    ├── util.cpp
    └── util.h

Now let’s create a sub-folder build. We will use this sub-folder to setup the build environment using the cmake command.

[... using-cmake] $ mkdir build
[... using-cmake] $ tree example1
example1
├── build
└── src
    ├── CMakeLists.txt
    ├── main.cpp
    ├── util.cpp
    └── util.h

Note we have kept the build outside of the the src folder. This is intentional. Once we are done with cmake, the build folder will consists of platform-specific build instructions. These instructions are only valid for this machine.

[... using-cmake] $ cd example1/build
[... using-cmake/example1/build] $ cmake ../src

Note that we issued the cmake command in build folder, and we passed it the path of the src folder, which is ../src. Recall that file CMakeLists.txt alongwith the source files sit in the src folder.

The cmake command will create the platform-specific build environment. I am running these programs on unix-like system. Consequently, cmake creates a Makefile.

[... using-cmake/example1/build] $ tree ../../example1
../../example1
├── build
│   ├── CMakeCache.txt
│   ├── CMakeFiles
│   │   ├── 3.23.1
│   │   │   ├── CMakeCCompiler.cmake
│   │   │   ├── CMakeCXXCompiler.cmake
│   │   │   ├── CMakeDetermineCompilerABI_C.bin
│   │   │   ├── CMakeDetermineCompilerABI_CXX.bin
│   │   │   ├── CMakeSystem.cmake
│   │   │   ├── CompilerIdC
│   │   │   │   ├── CMakeCCompilerId.c
│   │   │   │   ├── CMakeCCompilerId.o
│   │   │   │   └── tmp
│   │   │   └── CompilerIdCXX
│   │   │       ├── CMakeCXXCompilerId.cpp
│   │   │       ├── CMakeCXXCompilerId.o
│   │   │       └── tmp
│   │   ├── CMakeDirectoryInformation.cmake
│   │   ├── CMakeError.log
│   │   ├── CMakeOutput.log
│   │   ├── CMakeTmp
│   │   ├── Makefile.cmake
│   │   ├── Makefile2
│   │   ├── TargetDirectories.txt
│   │   ├── cmake.check_cache
│   │   ├── prog.dir
│   │   │   ├── DependInfo.cmake
│   │   │   ├── build.make
│   │   │   ├── cmake_clean.cmake
│   │   │   ├── compiler_depend.make
│   │   │   ├── compiler_depend.ts
│   │   │   ├── depend.make
│   │   │   ├── flags.make
│   │   │   ├── link.txt
│   │   │   └── progress.make
│   │   └── progress.marks
│   ├── Makefile
│   └── cmake_install.cmake
└── src
    ├── CMakeLists.txt
    ├── main.cpp
    ├── util.cpp
    └── util.h

cmake vs ccmake

In this example, we have used cmake to generated the platform specific build environment. cmake is smart enough to get the usual items (needed to setup the build environment) correctly, including include paths, library paths, compilers, etc. Sometimes, however, you wish to override these defaults. You can do so by using ccmake command instead. It will present a gui like interface to allow you to change the various values, e.g., compilers, etc.

Supported platform-specific build environemnt

CMake supports a number of platform-specific build environments. What that means is given a single source folder with a CMakeLists.txt file, CMake is able to generate makefiles for unix-like systems, visual studio project files for windows, and XCode project files for OSX. Check out CMake documentation for an up-to-date list of supported packages.

Step 3 (Build)

Luckily, you don’t have to worry too much about contents on the build folder. Let’s use make to build the executable.

[... using-cmake/example1/build] $ make
[ 33%] Building CXX object CMakeFiles/prog.dir/main.cpp.o
[ 66%] Building CXX object CMakeFiles/prog.dir/util.cpp.o
[100%] Linking CXX executable prog
[100%] Built target prog
[... using-cmake/example1/build] $ ./prog
sum = 83
smallest = 34
largest = 1

Example 2

Source files are available here.

Task

Lets consider a simple program that comprises three files.

We want to create an executable Example2. However, this time we want to set the version number at build time, plus we want to use the C++11 standard.

Recipe

Step 1

Let’s use the following CMakeLists.txt file

CMakeLists.txt

cmake_minimum_required(VERSION 3.10)

# project name
project(Example2 VERSION 1.0)

add_executable(Example2 main.cpp util.cpp)

# This is a header file will pass the version number to 
# the program.
configure_file(Example2_Config.h.in Example2_Config.h)

# Example2_Config.h header will will be created in the 
# build directory (and not the source directory).  Note that
# the build directory is not included in the include-files-paths
# by default.  Therefore, we include the build directory path
# in the include-files-paths.
target_include_directories(Example2 PUBLIC ${PROJECT_BINARY_DIR})

# specify the c++ standard
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED True)

We will also create a Example2_Config.h.in file to push the version information in the `CMakeLists.txt** file to the program.

Example2_Config.h.in

#define Example2_VERSION_MAJOR @Example2_VERSION_MAJOR@
#define Example2_VERSION_MINOR @Example2_VERSION_MINOR@

Step 2

Create a build folder and issue cmake command in this folder as seen below.

[... using-cmake/example2/src] $ mkdir ../build
[... using-cmake/example2/src] $ cd ../build
[... using-cmake/example2/build] $ cmake

Step 3

I am using unix-like environment, so I will issue make command in the build folder to create the executable.

[... using-cmake/example2/build] $ make
[... using-cmake/example2/build] $ ./Example2
version = 1.0
sum = 83
smallest = 34
largest = 1

Now change the version information in the CMakeLists.txt file and redo steps 2 and 3. See if your program spits out the newer version.

Example 3

This is taken from CMake tutorial at https://cmake.org/cmake/help/latest/guide/tutorial/Adding%20a%20Library.html.

The source code used is available here.

Task

  1. Create a library.
  2. Create a program that uses the functions available in this library.
  3. Control whether or not this library is used via CMake.

Recipe

We assume the following folder structure:

[... using-cmake/example3] $ tree src
src
├── CMakeLists.txt (1)
├── Example3_Config.h.in
├── mylib
│   ├── CMakeLists.txt (2)
│   ├── mylib.cpp
│   └── mylib.h
└── prog
    ├── CMakeLists.txt (3)
    └── main.cpp

The build is controlled by three CMakeLists.txt files.

CMakeLists.txt (1)

# This CMakeLists.txt file lives in the main folder

cmake_minimum_required(VERSION 3.10)

project(Example3)

# This defines a switch that we can pass to cmake at invocation time.
# mylib library will only built if this switch is ON
option(USE_MYLIB "Use mylib functions" ON)

# We will use Example3_Config.h to let our program know if USE_MYLIB 
# is defined.  This way the program can be built to use mylib when
# it is available.
configure_file(Example3_Config.h.in Example3_Config.h)

if(USE_MYLIB)
# Includes subfolder that contains the library
add_subdirectory(mylib)
endif()

# Includes subfolder that contains the program files that will use the library
add_subdirectory(prog**

CMakeLists.txt (2)

# This CMakeLists.txt file lives in the subfolder containing the program files

cmake_minimum_required(VERSION 3.10)

add_executable(Example3 main.cpp)

if(USE_MYLIB)
target_link_libraries(Example3 mylib)
target_include_directories(Example3 PUBLIC "${PROJECT_SOURCE_DIR}/mylib")
endif()

# We need this since Example3_Config.h is copied into the built folder
target_include_directories(Example3 PUBLIC "${PROJECT_BINARY_DIR}**)

CMakeLists.txt (3)

# This CMakeLists.txt file lives in subfolder containing the library

cmake_minimum_required(VERSION 3.10)

add_library(mylib mylib.cpp)

CMake options

You can control the USE_MYLIB cmake option via command line by using

[... using-cmake/example3/build] $ cmake -DUSE_MYLIB=OFF

Alternately, you can invoke ccmake and it will provide an ncurses-like gui where you can turn on or off the various options. Try it out.

[... using-cmake/example3/build] $ ccmake

References