Learning Cmake
Table of Contents
add_subdirectory
add_subdirectory tells CMake to enter another folder, read its CMakeLists.txt, and treat everything defined there as part of the same build.
1add_subdirectory(tests)When CMake processes this line, it immediately steps into the tests/ directory, loads tests/CMakeLists.txt, and executes it just as if its contents were written in the main file.
Any targets created inside that subdirectory, such as executables or libraries defined with add_executable or add_library, become part of the same overall project. These targets can be built with make, linked against other targets, and discovered and run by ctest.
The big picture in modern CMake is that everything revolves around targets. A target represents a concrete buildable unit, such as an executable or a library, and CMake manages dependencies and relationships between these targets automatically.
target_include_directories
target_include_directories controls where the compiler looks for header files when it encounters an #include directive.
For example, when the compiler sees an include like the following, it needs to know which directories to search in order to find the corresponding header files.
1#include "order_book.hpp"
2#include <boost/asio.hpp>The compiler first checks the directory of the current source file. If the header is not found there, it then searches through the include directories provided by target_include_directories.
The general syntax of this command is shown below. The visibility keyword determines how the include directories are applied to the target and any targets that depend on it.
1target_include_directories(<target>
2 PRIVATE | PUBLIC | INTERFACE
3 <dir1> <dir2> ...
4)Using PRIVATE means the include directories are only used when compiling this target. PUBLIC applies the directories to this target and anything that links against it, while INTERFACE applies them only to dependent targets.
In the example below, the Boost include directory is added so the compiler can find Boost headers such as boost/asio.hpp .
1set(BOOST_INCLUDEDIR "/usr/local/opt/boost/include")
2
3add_executable(
4 mini_trader
5 src/main.cpp
6 src/order_book.cpp
7 src/tcp_server.cpp
8)
9
10# Important: all source files must be listed so they are compiled and linked
11target_include_directories(
12 mini_trader
13 PRIVATE ${Boost_INCLUDE_DIRS}
14)target_link_libraries
target_link_libraries tells the linker which external libraries your program depends on. While header files are needed during compilation, libraries are required at link time to resolve symbols for functions and objects your code uses.
For example, if your code uses Boost, POSIX threads, or Google Test, the compiler may successfully build your source files, but the final executable will fail to link unless those libraries are explicitly linked.
The general syntax is shown below. Similar to include directories, the visibility keyword controls whether the linked libraries are used only by this target or also propagated to targets that depend on it.
1target_link_libraries(<target>
2 PRIVATE | PUBLIC | INTERFACE
3 lib1 lib2 ...
4)Using PRIVATE means the libraries are linked only when building this target. PUBLIC links them for this target and also exposes them to dependent targets, while INTERFACE applies them only to dependents.
In the example below, the executable is linked against Boost libraries and the system threading library. This ensures that all Boost and threading symbols used in the code are correctly resolved when producing the final binary.
1set(BOOST_LIBRARYDIR "/usr/local/opt/boost/lib")
2
3add_executable(
4 mini_trader
5 src/main.cpp
6 src/order_book.cpp
7 src/tcp_server.cpp
8)
9
10# All required libraries must be linked to produce the final executable
11target_link_libraries(
12 mini_trader
13 PRIVATE ${Boost_LIBRARIES} Threads::Threads
14)