Build Systems

CMake Basics

Writing Makefiles by hand for a project with hundreds of files and folders is miserable. CMake fixes this by providing a clean, high-level language to describe your project.


What CMake Does

CMake does not compile your code.

You write a file called CMakeLists.txt describing your project. You then run the cmake command. CMake reads your file, looks at your operating system, finds your installed compilers, and generates the necessary build files (like a Makefile on Linux, or a .sln on Windows).

You then use the native build tool (like make or ninja) to do the actual compilation.


The Anatomy of CMakeLists.txt

Here is a basic CMakeLists.txt for a simple project with a main file and a sensor library.

cmake
1# 1. Require a minimum CMake version
2cmake_minimum_required(VERSION 3.10)
3
4# 2. Name your project
5project(MyAwesomeApp)
6
7# 3. Create a library (a reusable chunk of code) out of sensor.c
8# "STATIC" means it will be compiled into a .a file and merged into the final app
9add_library(sensor_lib STATIC sensor.c)
10
11# 4. Tell CMake where to find the header files for the library
12target_include_directories(sensor_lib PUBLIC include/)
13
14# 5. Create the final executable out of main.c
15add_executable(my_app main.c)
16
17# 6. Link the library to the executable!
18target_link_libraries(my_app PRIVATE sensor_lib)

Why this is better than Makefiles

Notice how we didn't write a single compiler flag or terminal command! If you run this on an ESP32 toolchain, CMake knows to use xtensa-esp32-elf-gcc. If you run it on a Mac, it knows to use Apple clang. It handles all the paths, libraries, and flags automatically.


The Two-Step Build Process

Because CMake is a generator, building a CMake project is always a two-step process in the terminal:

Step 1: Configure (Generate the build files)

bash
# Create a build folder to keep our source code clean
mkdir build
cd build

# Tell CMake to read the CMakeLists.txt in the folder above (..)
cmake ..

Step 2: Build (Compile the code)

bash
# Tell CMake to trigger the underlying build system (like Make or Ninja)
cmake --build .

Desktop CMake vs ESP-IDF CMake

If you are writing a C program to run on your Mac or Windows computer, the CMakeLists.txt file above is exactly what you write. You use add_executable() and add_library() directly.

In ESP-IDF, you almost never use add_executable() or add_library().

Instead, Espressif wrote a massive wrapper around CMake called the Component System. You use ESP-IDF specific macros like idf_component_register(). The ESP-IDF build system automatically scans your folders, calls add_library() for every component under the hood, and links them all into the final firmware binary.

When you type idf.py build for an ESP32 project, idf.py is simply a Python script that runs the "Configure" and "Build" steps for you in the background!

Previous
Makefiles