CMake

Introduction

C++ Standard

1

Data source: www.jetbrains.com

CMake2

  • Cross-platform, compiler-independent
  • Generates configuration for other build systems
  • Open-Source

Vocabulary

Example

File: main.cpp

#include <add.h>

int main() {
  return add(2, 3);
}

Header File

File: add.h

#pragma once

int add(const int x, const int y);

Translation Unit

File: add.cpp

#include <add.h>

int add(const int x, const int y) {
  return x + y;
}

Interface

  • Describes parameters and return value
  • Is checked at callsite (main.cpp) by the compiler
  • Promises an implementation

Implementation

  • Specifies what function does
  • Not checked at callsite (main.cpp) by the compiler
  • Only one implementation allowed

Quiz

How does the build system know which header belongs to which translation unit?

Build Process

  • Compiler
  • Linker

Compile Step

cd src && mkdir build && cd build
g++ -c ../add.cpp -o add.o
g++ -c ../main.cpp -o main.o

Compiler’s View

File: main.cpp

int add(const int x, const int y);

int main() {
  return add(2, 3);
}

Compile Step Characteristics

  • Optimizes code, slow
  • Checks external interfaces
  • Does not check external implementations
  • Parallelizable (multiple translation units at once)

Linker’s View

  • File: main.o
    • Provides int main()
    • Needs int add(int, int)
  • File add.o
    • Provides int add(int, int)

Run the executable

./example
echo $?

Rebuild can become cumbersome

Change main.cpp:

#include <add.h>
#include <iostream>

int main() {
  std::cout << add(1, 2) << std::endl;
  return 0;
}

Rebuild:

# add.o: not changed / already built
g++ -c ../main.cpp -o main.o
g++ add.o main.o -o example

Special Case

Sometimes callsite needs definition.

  • Inline function
inline int add(const int x, const int y) { return x + y; }
  • Template
template <class T>
T add(const T x, const T y) { return x + y; }

Libraries

  • Provide interfaces for compiler
  • Provide implementation for linker (.a, .so, …)

Summary

  • Building is system dependent
  • External libraries may require flags
  • Managing flags and parameters becomes infeasible

Modern CMake

Goals

  • Targets, targets, targets
  • Declarative
  • Avoid global variables

Example

cmake_minimum_required(VERSION 3.16 FATAL_ERROR)
project(addition VERSION 0.1.0 LANGUAGES C CXX)

add_library(addition addition/add.cpp)
target_include_directories(addition PUBLIC addition/include)

add_executable(example main.cpp)
target_link_libraries(example PRIVATE addition)

Targets

  • addition (library)
  • example (executable)
add_library(addition addition/add.cpp)
target_include_directories(addition PUBLIC addition/include)

add_executable(example main.cpp)
target_link_libraries(example PRIVATE addition)

Quiz

What if addition depends on a library XYZ?

Solution

  • Link XYZ to addition.
target_link_libraries(addition PUBLIC XYZ)
  • Target example automatically depends on XYZ.

Visibility

Target User
PRIVATE x
PUBLIC x x
INTERFACE x

Library Types

BUILD_SHARED_LIBS defines default.

add_library(addition STATIC addition/add.cpp)
add_library(addition SHARED addition/add.cpp)

Localize Settings

Library knows paths and flags.

target_link_libraries(example PRIVATE addition)

Using External Library

Library knows paths and flags.

find_package(Eigen3 3.3.7 REQUIRED)

target_link_libraries(example PRIVATE Eigen3::Eigen)

Find Package Modes

  • CONFIG mode: library maintainer provides settings
  • MODULE mode: a provided program tries to detect settings
  • MANUAL mode: 🙁

Find Package Modes

  • Eigen3: CONFIG mode 😎
find_package(Eigen3 3.3.7 CONFIG REQUIRED)
  • HDF5: MODULE mode 🙂
find_package(HDF5 1.10 MODULE REQUIRED)

Find Package Modes

  • PETSc: MANUAL mode 🙁

    • Write find module
    • PkgConfig may help

Global Variables

Use targets, not global variables.

  • set(CMAKE_CXX_FLAGS "-std=c++11") 😭
  • target_compile_features(addition PUBLIC cxx_std_11) 😎

Calling CMake

Platform-independent builds.

Configure:

cmake -DCMAKE_BUILD_TYPE=Debug -S src -B build -G Ninja
# -DCMAKE_BUILD_TYPE=...  Build type
# -S                      Source directory
# -B                      Build directory
# -G                      Build system generator

Build:

cmake --build build
# --build                 Build directory

Build types

Default: Don’t pass any compiler options
Debug Enable debug compiler flags
Release Enable compiler optimization flags
RelWithDebInfo Include debug information for profilers
MinSizeRel Release, optimizing size instead of performance

More info: Manual

Beyond Building

  • Testing
  • Package libraries
  • Generate installers

  1. https://www.jetbrains.com/lp/devecosystem-2021/cpp/↩︎

  2. https://en.wikipedia.org/wiki/CMake↩︎