Learning CMake 1: a really quick introduction to CMake
Tags: cmake, cpp, buildsystems
Published: 2019-10-02 17:14:23

---
title: Learning CMake 1: a really quick introduction to CMake
published: true
description: A different approach to learn CMake.
tags: cmake, cpp, buildsystems, opensource
cover_image: https://rcdesign.com/wp-content/uploads/2017/11/how-long-build-professional-website.jpg
series: "Learning CMake"
---

# A really quick introduction to CMake

We could say that [CMake](https://cmake.org/) is a framework to build/test/package software. It is not a build system: it is a build system generator. With CMake, we will write how to build our software in a high-level programming language and we will generate a native build system (like *Unix Makefiles* or *Visual Studio Solutions*). It's open source and extensively used by different companies and projects.

There is a lot of information on the Internet about what CMake is and its origins. For that reason, it doesn't make sense to cover that on this post. Also, I will not explain anything about the CMake language syntax: it is really easy to follow and you can search on Google or other search engine if you have any questions.

CMake is a really powerful tool but, if you're not using it properly, it can be a nightmare. Remember:

![With great power comes great responsibility](https://media1.tenor.com/images/4c3b395bb7e3b40b780ac97f287b6ab3/tenor.gif?itemid=4050585)

You can download CMake from its [official website](https://cmake.org/).

There is a really good book called "Mastering CMake". You should read it if you need to write a complex system. This book is related to the version 3.1 (when this post was written, the latest version is 3.15 but the most of the concepts are still valid).

![Mastering CMake](https://i.gr-assets.com/images/S/compressed.photo.goodreads.com/books/1434945327i/25770849._UY630_SR1200,630_.jpg)


# The basics

As part of the installation, you will receive:
* `cmake`: the CMake runtime (to generate native build systems and other magics)
* `cpack`: the tool to package software
* `ctest`: the tool to test software

Regarding the way to build software using CMake, can be simplified as:
1. Generate the build system
2. Build the source code

## The `cmake` command-line
If you run `cmake --help`, you will see all the possible parameters to provide to CMake. Don't worry: we'll learn how to use them in the future. First, I want to show you something that is really interesting: `cmake` has a set of commands that are totally cross-platform and can be used independently of the operating
system where you are running CMake. To get all these commands, just run `cmake -E`:

```sh
Usage: ./cmake -E <command> [arguments...]
Available commands:
  capabilities              - Report capabilities built into cmake in JSON format
  chdir dir cmd [args...]   - run command in a given directory
  compare_files [--ignore-eol] file1 file2
                              - check if file1 is same as file2
  copy <file>... destination  - copy files to destination (either file or directory)
  copy_directory <dir>... destination   - copy content of <dir>... directories to 'destination' directory
  copy_if_different <file>... destination  - copy files if it has changed
  echo [<string>...]        - displays arguments as text
  echo_append [<string>...] - displays arguments as text but no new line
  env [--unset=NAME]... [NAME=VALUE]... COMMAND [ARG]...
                            - run command in a modified environment
  environment               - display the current environment
  make_directory <dir>...   - create parent and <dir> directories
  md5sum <file>...          - create MD5 checksum of files
  sha1sum <file>...         - create SHA1 checksum of files
  sha224sum <file>...       - create SHA224 checksum of files
  sha256sum <file>...       - create SHA256 checksum of files
  sha384sum <file>...       - create SHA384 checksum of files
  sha512sum <file>...       - create SHA512 checksum of files
  remove [-f] <file>...     - remove the file(s), use -f to force it
  remove_directory <dir>... - remove directories and their contents
  rename oldname newname    - rename a file or directory (on one volume)
  server                    - start cmake in server mode
  sleep <number>...         - sleep for given number of seconds
  tar [cxt][vf][zjJ] file.tar [file/dir1 file/dir2 ...]
                            - create or extract a tar or zip archive
  time command [args...]    - run command and display elapsed time
  touch <file>...           - touch a <file>.
  touch_nocreate <file>...  - touch a <file> but do not create it.
  create_symlink old new    - create a symbolic link new -> old
```

Why are these commands interesting? For instance: usually, I work with Linux. When I have to work with a Windows machine, I don't know some of the commands to do some regular tasks like removing a directory recursively. I can use the command from CMake (if CMake is installed in the machine ¯\\_(ツ)_/¯):

```sh
cmake -E remove_directory foo_folder
```

These commands are really useful to maintain our build system totally platform independent.

## Let's build something

![](https://media1.giphy.com/media/Rm1p7xp3Odl2o/giphy.gif)

Ok, it is the time to build a "Hello World" example. You'll be able to find a lot of them on the Internet, but I will try to explain some extra concepts.

Let's imagine we need to build this C application:

```c
#include <stdio.h>
int main()
{
   printf("Hello, people!\n");
   return 0;
}
```

We will need to create a file called `CMakeLists.txt` in the same folder where the source code is placed. This folder is called **source directory**. The content of the `CMakeLists.txt` file will be:

```cmake
# Set the minimun CMake version required by the project
cmake_minimum_required(VERSION 3.5)

# Set a name for the project
project(example)

# Create an executable with the source code file
add_executable(my_app
    "${CMAKE_CURRENT_SOURCE_DIR}/myapp.c"
)
```

The folder structure will be:

```sh
.
├── CMakeLists.txt
└── myapp.c
```

### Generating the build system
Now, we are going to generate the build system in order to build our source code. First, we are going to create the folder where the build will be done. This folder is called **binary directory**. You can create it running `mkdir ../build`. The folder structure will be:

```sh
.
├── build
└── src
    ├── CMakeLists.txt
    └── myapp.c
```

It is a good idea to have different source and build directories. This situation is called **outsource build**. Doing this separation, we can build the source code without modifying the source directory (what will make easier to deal with control-version softwares and other tools). To make possible this, we should reference the input and output files relative to the `CMAKE_SOURCE_DIR`, `CMAKE_CURRENT_SOURCE_DIR`, `CMAKE_BINARY_DIR` and `CMAKE_CURRENT_BINARY_DIR` CMake variables (these variables are automatically populated). Also, it is a good idea to add quotes to the paths to avoid issues with spaces in the path to the files and the CMake syntax.

Now, you have to move to the binary directory (`cd ../build`) and then we will be able to generate the build system running CMake:

```sh
➜ iblancasa: cmake ../src
-- The C compiler identification is GNU 8.3.1
-- The CXX compiler identification is GNU 8.3.1
-- Check for working C compiler: /usr/bin/cc
-- Check for working C compiler: /usr/bin/cc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Detecting C compile features
-- Detecting C compile features - done
-- 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: /home/israel/cmake/build
```

#### CMake generators
The generator is the native build system to use build the source code.

By default, CMake will generate build files using the most common generator for your host platform (e.g., Makefiles on Unix-like systems and Visual Studio Solutions on Windows). You will need to specify the `-G` option to CMake to generate the build system with the desired generator. [All the valid values are described described in the CMake documentation CMake Generators Section](https://cmake.org/cmake/help/latest/manual/cmake-generators.7.html).

There are two kind of generators:
* Single-configuration generators: like Unix Makefiles. The build system is generated for just one configuration (Debug-Static, for instance). If you want to build the source code with a different configuration, you will need to create a new binary directory and call CMake again with the new configuration.
* Multiconfiguration generators: like Visual Studio Solutions. The build system allows to build with more than one configuration (Release-Static and Debug-Static, for instance). When the build is done, it is needed to specify what configuration you want to build.

**Important:** pay special attention to the Visual Studio generators. You need to specify the platform with the `-A` parameter. For instance, to generate the build system for Visual Studio 2019:

```sh
cmake ../build -G "Visual Studio 16 2019" -A x64
```

Also, it is important to talk about `CMAKE_BUILD_TYPE` and `BUILD_SHARED_LIBS`:
* ``-DCMAKE_BUILD_TYPE`` (just for single configuration generators): specifies the build mode. Valid values are `Release` and `Debug`. See the [CMake documentation for more details (Optional)](https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html)
* ``-DBUILD_SHARED_LIBS``: specifies the link mode. Valid values are ``ON`` for dynamic linking and ``OFF`` for static linking. [See CMake documentation for more details.
(Optional)](https://cmake.org/cmake/help/latest/variable/BUILD_SHARED_LIBS.html)

So, if you want to generate a build system for a Debug/Dynamic configuration, we need to call CMake as follows:

```sh
cmake -DCMAKE_BUILD_TYPE=Debug -DBUILD_SHARED_LIBS=ON ..
```


### Building the source code
Depending on the CMake generator used, you can build the source code running directly the native tool. For instance, if you generated an Unix Makefile build system, you will be able to build just running `make`.

Another option, is to use the `cmake` command line. For single configuration projects, you can build running `cmake --build .`:

```sh
➜ iblancasa: cmake --build .
Scanning dependencies of target my_app
[ 50%] Building C object CMakeFiles/my_app.dir/myapp.c.o
[100%] Linking C executable my_app
[100%] Built target my_app
```

If the used generator was a multiconfiguration project, you will need to specify the configuration to build:

```sh
➜ iblancasa: cmake --build . --config Release
```

And the application will be available in the binary directory (the location may be different depending on the generator used):

```sh
➜ iblancasa: ./my_app
Hello, people!
```

We can also specify what target do we want to build. This can be performed with the `--target` parameter.

```sh
➜ iblancasa: cmake --build . --target my_app
```


If you are using Unix Makefiles, you can list all the targets with `make help`.

```sh
The following are some of the valid targets for this Makefile:
... all (the default if no target is provided)
... clean
... depend
... rebuild_cache
... my_app
... edit_cache
... myapp.o
... myapp.i
... myapp.s
```

That target `my_app` was created by CMake using the `add_executable` builtin method.

# Recap
What did we learn?
* What is CMake
    * Its command-line
* How to build a "Hello World" application
    * CMake generators
    * Build using different configurations