CMake is an open-source, cross-platform build system generator. It uses configuration files (one or more CMakeLists.txt) to produce build system files. Once you have these build systems files, you can launch them directly to compile and link your project.
This article provides a practical introduction to CMake: a simple project with a simple configuration file and the key concepts you need to get productive quickly.
A simple CMake project
Let’s first create a simple project with the following structure:
simple_project/
├── CMakeLists.txt
├── lib/
│ ├── hello_lib.c
│ └── hello_lib.h
└── src/
└── main.cWhere the main.c is:
#include "hello_lib.h"
int main()
{
hello_world();
return 0;
}hello_lib.c:
#include <stdio.h>
#include "hello_lib.h"
void hello_world(void)
{
printf("Hello world!\n");
}hello_lib.h:
#ifndef HELLO_LIB_H
#define HELLO_LIB_H
void hello_world(void);
#endif // HELLO_LIB_HAnd the CMakeLists.txt is:
cmake_minimum_required(VERSION 3.20.0)
project(
my_project
LANGUAGES C
)
add_library(hello_lib
STATIC
lib/hello_lib.c
)
target_include_directories(
hello_lib
PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/lib
)
add_executable(
${PROJECT_NAME}
src/main.c
)
target_link_libraries(
${PROJECT_NAME}
PRIVATE
hello_lib
)Let’s analyse the structure of the CMakeLists.txt:
- cmake_minimum_required: we tell CMake we need at least version 3.20.0.
- project: this command sets the project name (my_project) and the language used. If we want, we can also add the version and the description.
- add_library: we create a static library named hello_lib specifying the source location. The STATIC keyword means that this library will be compiled into the final binary instead of being dynamically loaded at runtime.
- target_include_directories: we tell CMake where to find header files for hello_lib. The keyword PUBLIC means that not only does hello_lib use this directory, but any targets linking to hello_lib will also inherit this include directory.
${CMAKE_CURRENT_SOURCE_DIR} is a variable that points to the current directory (where this CMakeLists.txt is located). - add_executable: we create an executable target using the project’s name (my_project) giving also the location of the executable’s source files (in this case main.c). If we want, we can use a different name for the executable (in this case the name of the executable should be the same used in the target_link_libraries below).
- target_link_libraries: we link our library hello_lib to the my_project executable. The keyword PRIVATE means that only my_project can use hello_lib.
Building the project
Configuration
To generate the build system, from simple_project folder, we use the cmake command with the -S and -B options to specify the source directory and build directory:
cmake -S . -B buildIf you want to use a different generator, you can specify it with the -G option. For example, to use Ninja, we could run the following command:
cmake -S . -B build -G NinjaCompilation
Once the generation is completed, we can build the project:
cmake --build buildWhere the build is the build folder chosen previously.
Run the executable
Once the compilation is done, run the executable:
./build/my_projectThat should print:
Hello world!Resources
Some useful resources to learn more about CMake:
- CMake Official documentation: the official CMake Reference documentation
- Modern CMake Book: a free online book about modern CMake