CMake Tips 'n Tricks
Best Practices
In the top level CMakeLists.txt, it is a good practice to use include(...)
statements. These statements directly embed the included file in the location where the include
statement is made, allowing for better modularity and readability of the CMakeLists.txt file. For clarity, a cmake folder is typically used to house important included CMake files. Important files that should be included are
- cmake/Testing.cmake - Has build directives for the testing library.
- cmake/CodeCoverage.cmake - Has build directives for making code coverage reports during testing (often included in Testing.cmake).
- cmake/Cpack.cmake - Has build directives for packing the library into a DEB archive.
CMake Variables
See the Ubuntu Manpages for a list of variables provided by CMake. A useful subset of these variables is provided at the bottom of this article.
To manipulate variables used at build time (either custom or CMake-provided), use cmake -D<argument>=<value> ..
and access them using ${<argument>}
.
For example, if your CMakeLists.txt contains the following code.
message("CUSTOM_VARIABLE is ${CUSTOM_VARIABLE}")
message("CUSTOM_VARIABLE is ${CUSTOM_VARIABLE}")
Then at the command line use,
$ cmake -DCUSTOM_VARIABLE=qwerty ..
CUSTOM_VARIABLE is qwerty
...
$ cmake -DCUSTOM_VARIABLE=qwerty ..
CUSTOM_VARIABLE is qwerty
...
CMake Commands
configure_file
: At build time, copy a file to another location and modify it's contents.
- This can be done with a config.h.in files, for example, to auto populate a configuration header given project name and version in the CMakeLists.txt.
@ONLY
restricts variable replacement to references of the form@VAR@
, otherwise all variables in the form${<argument>}
are also replaced.
configure_file(config.yaml.in ${CMAKE_CURRENT_BINARY_DIR}/config.yaml @ONLY)
configure_file(config.yaml.in ${CMAKE_CURRENT_BINARY_DIR}/config.yaml @ONLY)
install
: Defines the installation rules for a target set of files, directories, libraries, etc.
Getting Clangd to work in Vim
Ensure you have the following snippets in your CMakeLists.txt.
# Exports the compile_commands.json file
set(CMAKE_EXPORT_COMPILE_COMMANDS
ON
CACHE INTERNAL "")
if(CMAKE_EXPORT_COMPILE_COMMANDS)
set(CMAKE_CXX_STANDARD_INCLUDE_DIRECTORIES
${CMAKE_CXX_IMPLICIT_INCLUDE_DIRECTORIES})
endif()
...
set_target_properties(${CMAKE_PROJECT_NAME} PROPERTIES CXX_STANDARD 17)
set_target_properties(${CMAKE_PROJECT_NAME} PROPERTIES ENABLE_EXPORTS 1)
# Exports the compile_commands.json file
set(CMAKE_EXPORT_COMPILE_COMMANDS
ON
CACHE INTERNAL "")
if(CMAKE_EXPORT_COMPILE_COMMANDS)
set(CMAKE_CXX_STANDARD_INCLUDE_DIRECTORIES
${CMAKE_CXX_IMPLICIT_INCLUDE_DIRECTORIES})
endif()
...
set_target_properties(${CMAKE_PROJECT_NAME} PROPERTIES CXX_STANDARD 17)
set_target_properties(${CMAKE_PROJECT_NAME} PROPERTIES ENABLE_EXPORTS 1)
Useful Variables
- `CMAKE_CURRENT_SOURCE_DIR=/path/to/project-root
- `CMAKE_CURRENT_BINARY_DIR=/path/to/project-root/build
CMAKE_PROJECT_NAME=project-root
CMAKE_PROJECT_VERSION=0.1.2
Available with GNUInstallDirsCMAKE_INSTALL_BINDIR=bin
CMAKE_INSTALL_SYSCONFDIR=etc
CMAKE_INSTALL_LIBDIR=lib
CMAKE_INSTALL_INCLUDEDIR=include
Additional important variables can be found here.
CMake learning
When using the find method you can filter the permission denied actor by following instructions at: https://unix.stackexchange.com/questions/42841/how-to-skip-permission-denied-errors-when-running-find-in-linux
See the beginner tutorials at: https://www.youtube.com/watch?v=abuCXC3t6eQ&list=PLK6MXr8gasrGmIiSuVQXpfFuE1uPT615s&index=4
OFFICIAL CMAKE tutorial: https://cmake.org/cmake/help/latest/guide/tutorial/index.html
(get the tutorial source code from the cmake git repo)
Step 1: Starting point
The most basic project is a simple executable that is built from source code files. For simple projects, a three line CMakeLists.txt is all that is required.
Always start with the cmake_minimum_required(VERSION 3.xx), then you can set a project name, configure files (see Step 1), and most importantly, addexecutable(Name-of-exec-file exec-file.cxx)
You can set variables in the cmake scripting language using the set() function.
To build and test
- Create a build directory where the build files and executables will be installed.
Mkdir build && cd build (for example)
- Run the cmake command on the directory containing the source files
Cmake ../src (or something similar)
- Run the cmake --build command specifying the build directory
Cmake --build .
Step 2: Adding a library
When adding a library you can use the add_library or add_subdirectory functions, check cmake documentation for more information.
Step 3: Adding usage requirements for library
Usage requirements allow better control over library/executable’s link and include line. Primary commands
target_compile_definitions()
target_compile_options()
target_include_directories()
target_link_libraries()
…
find_package() has two modes…
one is MODULE mode, which searches for a Find[package].cmake
The other is CONFIG mode which searches for [package]Config.cmake
EXTRAS
To run cmake you can pass the path to the src directory or to an already configured binary directory.
Some of the most common things in cmake is making libraries and executables.
Ninja build is a small build system that focuses on speed and is designed to be built into (embedded in) other build systems.
Compiler systems…
I know of gcc, g++, and now llvm, but I’m not sure of their differences and when it is better to employ which one.