Introduction to CMake

For writing cross-platform applications

At some point in our C++ journey, we meet tools that improve our project's maintainability. Configuring build systems help us relieve the overhead of writing multiple makefiles for different platforms. This ensures that we can be able to build and deploy software across multiple platforms.

It's been only 3 months since I first integrated CMake into my C++ projects. And since then, I'm able to write my program without worrying about different compilers, and Operating Systems it will run on. It allowed me to focus more on the work. Indeed, it has added value both to my development experience and my projects. So I'm starting this series to document the things I learned from Dominik Berner, and Mustafa Kemal Gilor's (2022) book: CMake Best Practices.

In this article, I will briefly cover CMake and take you through an overview of its build process. And on the next part, we will look at CMake as a scripting language.

What is CMake?

  • CMake is an open-source project that serves as a tool for building, testing, packaging, and distributing cross-platform software
  • CMake is a scripting language written in C++
  • CMake is a de facto industry standard for building C++ projects
  • CMake is divided into 3 command-line tools:
    • cmake: for generating compiler-independent build instruction
    • ctest: for detecting and running tests
    • cpack: for packing the software project into convenient installers

Benefits of using CMake

  • Provides an interface to configure a compiler-independent build
  • Separate the source code directory and the compiler outputs: enable building multiple builds from the same source tree
  • Supports cross-compilation
  • Enables CI/CD automation for testing and building the project*
  • Integrates well with package managers such as Conan
  • Improves the project's maintainability by providing a single point of definitions from build configuration, testing, and packaging specifications

Installing CMake

  1. Download CMake from: cmake.org/download
    1. It is available as a pre-compiled binary or as a source code
  2. Extract it into a folder and open the command line in that directory
  3. Run the command ./configure make
  4. To install CMake, run the command make install
  5. To check if the installation is successful, run cmake --version. It should print out the version of CMake, like this:
C:\Users\[username]\[path]>cmake --version
cmake version 3.22.1

CMake suite is maintained and supported by Kitware (kitware.com/cmake).

Hello, World! in CMake

Before writing your C++ project, it is good practice to test out the build configuration first. This section briefly walks you through the minimal CMake configuration for hello, world! in C++.

  1. Execute mkdir src to create the following directory.
    └───src
    
  2. Write the main.cpp inside the src directory:

    #include <iostream>
    
    auto main() -> int {
       std::cout << "hello, world! \n";    
       return 0;
    }
    
  3. Create CMakeLists.txt and add the following commands:
    cmake_minimum_required(VERSION 3.10)
    # set project name, version, description, and language specification 
    project(
       "HelloWorld"
       VERSION 1.0
       DESCRIPTION "Hello World in CMake"
       LANGUAGES CXX
    )
    # tells CMake to build an executable
    add_executable(${PROJECT_NAME})
    # tells CMake which location to look for the sources of the executable
    target_sources(${PROJECT_NAME} 
       PRIVATE src/main.cpp
    )
    
  4. Go to the project directory and execute cmake -S . -B build to make the build directory.
    $ cmake -S . -B build
    -- The CXX compiler identification is GNU 10.3.0
    -- Check for working CXX compiler: /usr/bin/c++
    -- Check for working CXX compiler: /usr/bin/c++ -- works
    -- Detecting CXX compiler ABI info
    -- Detecting CXX compiler ABI info - done
    -- Detecting CXX compile features
    -- Detecting CXX compile features - done
    -- Configuring done
    -- Generating done
    -- Build files have been written to: [ProjectFolder]/build
    
  5. Go to the build directory and run make to build the project.
    $ cd build && make
    Scanning dependencies of target HelloWorld
    [ 50%] Building CXX object CMakeFiles/HelloWorld.dir/src/main.cpp.o
    [100%] Linking CXX executable H
    
  6. Execute the binary.

The Build Process

CMake build process is mainly broken down into two steps: configure and build.

  1. Configure (triggered by the command cmake -S . -B build): CMake searches for any usable toolchain available and decides which configuration it outputs; The standard output is Unix Makefiles but in the event where Microsoft Visual Studio Compiler (MSVC) is detected *.sln file will be created instead.
    • During this process, CMakeLists.txt is parsed and executed to configure the build files relative to the toolchains, architecture, and dependencies.
    • CMake writes build files based on the generators; it can be changed using cmake . -G [generator] command.
  2. Build (triggered by the command make): CMake will execute the build files to compile and link binaries, run tests, and pack artifacts.
    • The build directory will contain the generated binaries, artifacts, build instructions, and cache
    • Inside the build directory, CMakeCache.txt file will contain all the detected configurations

References

  1. D. Berner & M.K. Gilor (2022). CMake Best Practices. Packt Publishing. ISBN 9781803239729.
  2. CMake (2022). CMake Tutorial. cmake.org/cmake/help/latest/guide/tutorial/..
  3. Wikipedia Contributors (2022). CMake. en.wikipedia.org/wiki/CMake
  4. Cmake (2022). Why CMake? cmake.org/cmake/help/book/mastering-cmake/c..