Subdirectories and the CMake language
Checkpoint 1
Now look at the next checkpoint in the
checkpoint_1 subdirectory, which looks like this:CMakeLists.txt src/ CMakeLists.txt functionality.cpp functionality.hpp main.cpp
Our project has grown! In addition to the code in
main.cpp, some new
functionality was added to new source files functionality.cpp and
functionality.hpp.This code is now contained in a specific directory
src/, inside the project
directory.Compartmentalising build logic
In the top-level
CMakeLists.txt, we use the add_subdirectory cmake function
to add the src directory to the project:add_subdirectory(src)
This causes CMake to process the
CMakeLists.txt file in the directory src.# src/CMakeLists.txt add_executable(main_executable functionality.cpp functionality.hpp main.cpp )
Variables defined in the upper scope are available in the lower scope, but not
the other way around.
Using subdirectories enables clear structure and modularity, and keeps the
top-level
CMakeLists.txt clean and tidy.Programming CMake
Lets have a look at some of the features of the CMake language.
Like many languages you can create variables, which you can do by simply
assigning a value via the
set function:set( my_var "Hello, World!" )
This creates a variable called
my_var and assigns it the string "Hello, World!".Variables can hold lists:
set( src_files main.cpp functionality.cpp functionality.hpp )
Variables can be dereferenced using
${}:set(another_list ${src_files})
Here the value of
another_list is set to the value of src_files.You can even store a variable name or part of a variable name in another variable. For example:
set(var files) # var = "files" set(yet_another_list ${src_${var}})
Storing a list of sources in a variable like this works, but in modern CMake it
is usually clearer to list the sources directly in
add_executable or
add_library.Also avoid using
file(GLOB ...) to collect sources automatically:
CMake cannot tell when you add or remove a file, so the build can silently go
stale.Adding new files to the project
Look through the files in Checkpoint 1.
Add a new pair of hpp/cpp files that defines a new function.
- Call it from the main executable
- Add the files to
src/CMakeLists.txt - Configure, compile and run: check that your new function has been executed
Print information with message()
You often need to print information during the configuration step. This can be
done with the
message() command:set(name "Jane Doe") message(STATUS "Hello ${name}")
-- The CXX compiler identification is GNU 13.3.0 ... -- Hello Jane Doe -- Configuring done -- Generating done
Options for message()
message(STATUS "A simple message")
STATUS can be replaced by e.g. WARNING, SEND_ERROR, FATAL_ERROR
depending on the situation.message(SEND_ERROR "An error occurred but configure step continues")
CMake Error at CMakeLists.txt:2 (message): An error occurred but configure step continues -- Configuring incomplete, errors occurred!