Getting started with Raspberry Pico and CMake
Tags: raspberrypi, pico, cmake
Published: 2021-05-31 05:02:17


When you work with the Raspberry Pi Pico C/C++ SDK, you also need to understand the CMake build system that is used. In my first projects, I was happy to copy and paste the example files and tweak them. Yet, when developing my libraries, new features were required. First, I wanted to have different types of build, like example and test. The example build should compile all examples, and link them with the Pico SDK and my library. The test build should compile the library, link to the unit testing framework, and provide an executable that runs all test files with injected mock files.

Turns out that this is quite a challenge! I could not find a good documentation of CMake essentials from the perspective of a new to C, new to Pico SDK developer. This article explains all you need to know about basic CMake, and after reading you should feel comfortable with configuring, compiling and building your projects. This article is structured into essential "how to" questions, so you can easily skip to the part that you like to do.

_This article originally appeared at [my blog](https://admantium.com/blog/pico06_basic_cmake/)_.

## What is CMake about?

CMake is the entry tool to configuring, compiling and building your project. Considering these steps, CMake has a distinguished role:

- Configuring: CMake is the tool to configure your project. You call it from the top-level directory with `cmake -B build -S .`, where `build` is the output directory, and `./` the directory with the _root level cmake file_.
- Compiling: You will build your project using the `make` command, and this command uses _Makefiles_ that were generated by CMake.
- Building: You will also build executable files that link to your library code and/or external libraries. This is also done with the `make` command, and the _Makefiles_ will include all the defined build options of your CMake configuration.

What appears confusing at first, but becomes clearer with experience, is that you need to place a file called `CMakeLists.txt` in your project root directory and in all subdirectories that contain source code.

Depending on where you define them, these files have different roles, which I separate as follows:

- `main config`: The top-level directory includes the root CMake file. This file needs to set essential config options, and it will list all additional directories that will be included when you execute CMake.
- `library config`: Directories in which you just build a library, you will put a config file that includes the `add_library` directive. During the compilation process, you will product library files, which are typically named `lib.a`.
- `executable config`: In directories in which you have executable code, you will put a config file that contains the directive `add executable`. During building, you will have one and only one C file with a `main()` method, and the result will be named `file.out`

In the remainder of this article, I will show a concise example for each type. If you want to see a complete example, check out my [pico-dht11-lib](https://github.com:admantium-sg/pico-dht11-lib) project on Github.

## Main CMake Config File

The main config file needs to contain the following:

```cpp
cmake_minimum_required(VERSION 3.12)

include($ENV{PICO_SDK_PATH}/pico_sdk_init.cmake)
pico_sdk_init()

project(pico-shift-register)

add_subdirectory(./src)
add_subdirectory(./examples)
```

Essential commands are:

- `cmake_minimum_version` A flag that controls the compatibility of your CMake files with a specific version
- `project` The name of this CMake file, its used throughout the build chain
- `add_subdirectory` List any other directories that contain a `CMakeLists.txt` file

Specific for the Raspberry Pico is the `include` statement to load the Pico SDK, and the custom CMake function `pico_sdk_init`. It is imperative that you place this at the top of the root config!

## Library CMake Config File

If you just need to compile code and build a library, use this template:

```cpp
file(GLOB FILES *.c *.h)

add_library(pico-shift-register ${FILES})

target_link_libraries(pico-shift-register pico_stdlib)

target_include_directories(pico-shift-register PUBLIC ../include/)
```

In this file, we see the following statements:

- `file`: Collect all files that need to be compiled. You can use a `GLOB` function as shown here, or explicitly mention the specific files
- `add_library`: With this declaration, you express the intent to build a library. The first argument, here its `pico-shift-register`, is the name of the library, the second argument are the files that will be compiled to create your library.
- `target_link_libraries` If you link with other libraries, list them here
- `target_include_directories` Libraries need to publish their header files so that you can import them in source code. This statement expresses where to find the files - typically in an `include` directory of your projects.

## Executable CMake Config File

When building an executable, use this:

```cpp
add_executable(8_led_blink 8_led_blink.c)

target_link_libraries(8_led_blink pico_stdlib pico-shift-register)

pico_add_extra_outputs(8_led_blink)
```

Here, the meaning of these declarations is:

- `add_executable` Defines the intent to create an executable file (.bin, .uf2 etc.), the first argument is the name, the second argument are the source files.
- `target_link_libraries` As before, the names of all additional libraries that you want to link with your executable.

The statement `pico_add_extra_outputs` is again a custom CMake function from the Pico SDK, it will produce additional files for the executable.

## Build Commands

With all your CMake files in place - which is the root directory, and in each lib subfolder or folder with executables - now execute the following commands from the root directory:

```bash
# Configure
cmake -B build -S .

# Build the library / produces libpico-shift-register.a.
make -C build/src

# Build the examples / produces 8_led_blink.elf, .uf2 etc.
make -C build/examples
```

## Conclusion

This article showed you the essentials of CMake when working with the Raspberry Pico. I explained the essential CMake file setup as well as the differences between compiling libraries and building executables. Then, I showed and explained an example for the three types of CMake files: main, library, and executable. Putting it all together, you can then execute the build commands from the root-directory to build your library or examples. In the next post, we will extend this knowledge with more advanced CMake configurations.