Skip to content

CMake FAQ

1. Why use CMake

It’s important to emphasize that CMake does not build application binaries directly. CMake is a “meta-build system”. Instead of manually configuring a makefile or the ninja build files, CMake shall do that for us. We simply need to tell CMake what we want to build and, to varying degrees, how we want it built.

A simplified order of operations might go something like:

  1. Configure your project via the CMakeLists.txt files
  2. Generate the build files/operations with CMake → cmake .. -GNinja
  3. Compile the project with your choice of toolchain (ninja, make, etc.)
  4. Run, flash, and or debug your application to your heart's content

2. What is the top-level CMakeLists.txt?

CMakeList.txt is a file that holds all the cmake scripts that operate on your system. Normally, this will be located in the root of your repository or project although you may find exceptions for non-executable projects.

In theory, you could have a single CMakelists.txt file that defines everything, but this is akin to putting everything in a single main.c file. This is the first file processed when you run ``cmake ..” and will usually dictate what kind of build you're using and what artifacts shall be generated during compilation.

3. Why multiple CMakeLists.txt and *.cmake files?

We touch on this again later in the lab, but utilizing multiple CMakelists.txt files to define library targets, executables, or include dependencies is good practice as far as organization is concerned. A few general benefits include:

  • Modularity
  • Separation of concerns
  • Isolation for libraries and custom targets

Intuition behind managing/exposing libraries from top-level CMakelists.txt \ From a practical standpoint, the top-level CmakeLists.txt file will do a large part of managing and exposing the dependencies of your project. Particularly for dependencies that multiple project parts require, we can include the respective CMakelists.txt or *.cmake file down to the lowest relevant level or directory. Once inherited by the top-level CMakeLists.txt file, it can be consumed and linked to other targets in the project.

4. How to build Cmake Projects (PLEASE READ!)

When you’re ready to generate the build scripts for your project, you must inform cmake where your build output directory is located. Through the command line, this can be done in two ways:

  1. Navigate to your build directory and then type cmake .. -G <insert generator>

Or

  1. From the root of your project, type cmake -s . -B build/ -G <insert generator>

Alternatively, your text editor or IDE may have their own interfaces for building CMake projects. See here for a more comprehensive list of IDEs/text editors with CMake integration.

5. What directory to build CMake from

As alluded to above, always build your cmake projects from your build directory or through the command line; inform cmake where the build directory is located via the -B argument.

Remember, we don’t want to build directly in our source directory. This intertwines “artifacts” from the build process with source files, making it challenging to remove stale artifacts and commit only source files.

6. When to rebuild CMake

Generally, whenever you make a change to any CMakeLists.txt file or andy *.cmake file, you should rebuild your project with the cmake command.

7. When to clear the CMake cache (CMakeCache.txt)

“If you significantly change your computer, either by changing the operating system or switching to a different compiler, you will need to delete the cache file (and probably all of your binary tree’s object files, libraries, and executables).”

One additional case to consider is when you add (or remove) a toolchain file. The toolchain path is usually saved in the cache and would probably enable cross compilation which could be a nuisance if you want to run a test build or another configuration.

8. How to clear the CMake cache (CMakeCache.txt)

There are two ways to go delete the CMake cache manually:

  1. Either through the command line or files interface, manually delete the CMakeCache.txt file and then regenerate the the project through one of the methods “How to build Cmake Projects”

  2. From CMake 3.24 version onward, you can add the –fresh command line option which will rebuild the entire project tree including the cache each time.

Assuming you run cmake form the build directory:

cmake .. --fresh -B. -GNinja

Or if you invoke from the project root:

cmake -S. --fresh -B. -GNinja

NOTE: You need to specify the build directory and generator again when you use either of these options.

9. How to compile a program

The exact command changes based on what generator used (Ninja, makefiles,...) For our lab, from the build directory type the following command: ninja

  • Yes, that's all it takes. Don’t ask questions, just compile!

10. How to delete the compiled build artifacts

Deleting compiled artifacts depends on the generator (Ninja, makefiles,...) used. Our lab uses Ninja. From the build directory type ninja cleanto delete all compiled artifacts.

11. How to execute a program

Once we’ve built and compiled our project the only thing left to do is run whatever executable was produced (assuming it runs on your host system). From your terminal/command-line navigate to where the executable was produced which in our case will be somewhere within the build folder. Once you have navigated to type “./<program name>” It should look something like:

cd build
./helloworld

12. Distinguishing between regular unit test frameworks and CTest

The official CMake documentation has an extremely detailed analysis of why CTest is utilized and how it facilitates testing. From a high level view though, CTest offers a few advantages over just running the test framework standalone:

  • Greater test isolation
  • Easier to handle larger numbers of tests
  • Individual test case configuration

It might help to think of CTest in the sense that it is a wrapper around the tests frameworks and tests we write. It invokes each test or the collection of tests in controlled environments and records the output from the respective tests.

Also give this a read.