-
Notifications
You must be signed in to change notification settings - Fork 0
CMake
Sample file/folder structure
|-- projectBuild/ # "Out of source build"
\-- project/
|-- CMakeLists.txt
|-- include/
| \-- MyClass.hpp
\-- src/
-- MyClass.cpp
-- mainapp.cpp
CMakeLists.txt:
cmake_minimum_required(VERSION 3.8) # Set min required CMake version and store it in the variable `VERSION`
# Better: Define ranges with min and max supported version
cmake_minimum_required(VERSION 3.8...3.19)
project(projectName LANGUAGES CXX) # Give the project a name (="target") and define the language
# Optional: Get more flexibility by adding a version number
project(projectName VERSION 1.0 LANGUAGES CXX)
target_compile_features(myTarget PUBLIC cxx_std_17) # Set cpp standard (cxx_std_11, cxx_std_14, and cxx_std_17, ...)
# CMake >=3.8; cleaner than using set(CMAKE_CXX_STANDARD 17)
# set_target_properties(myTarget PROPERTIES CXX_EXTENSIONS OFF) # Optional
# Add find_packages here:
# ...
# Split your CMake as your project grows in multiple subfolders containing other CMakeLists.txt
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/subfolder/)
# Libraries
add_library(libTargetName STATIC src/someLib.cpp inc/someLib.hpp) # Add lib as statically binded and add its source and inlcude files
# Used to (automatically by CMake) manage any dependencies (and order) and required build options for each target (otherwise you would have to do it yourself)
# Later it makes often sense to save files in a variable like this:
set(INCLUDES_MYLIB
${CMAKE_CURRENT_SOURCE_DIR}/src/ # The folder suffices
)
set(SOURCES_MYLIB
${CMAKE_CURRENT_SOURCE_DIR}/src/someCpp.cpp # Each file needs to be stated or use globbing
${CMAKE_CURRENT_SOURCE_DIR}/src/anotherCpp.cpp
)
add_library(anotherLibTarget SHARED ${SOURCES_MYLIB} ${INCLUDES_MYLIB})
# ${INCLUDES} is optional but helps IDEs
target_include_directories(libTargetName PUBLIC inc/) # Make headers visible to the project
target_compile_features(libTargetName PUBLIC cxx_std_11) # Optional: Specify options for compiling the library
target_include_directories(anotherLibTarget PUBLIC INCLUDES_MYLIB)
add_executable(execName nameOfMainMain.cpp)
target_link_libraries(execName PUBLIC libTargetName) # Link the library to your project/executable -> Chain the targets
# Automatically adds include paths of the library to the target project
target_link_libraries(execName PUBLIC anotherLibTarget)
Execute cmake (command line):
# In the simplest cast
cmake path/to/source
# Generator and compiler specified
cmake -G"my generator" -DCMAKE_C_COMPILER=gcc-4.2 -DCMAKE_CXX_COMPILER=g++-4.2 path/to/source/
It is recommended not to put spaces after -D, -G, ... See here why.
Show available generators are shown with:
cmake --help
From the documentation:
If a non-full path value is supplied then CMake will resolve the full path of the compiler.
Meaning -DCMAKE_C_COMPILER=gcc-4.2
or -DCMAKE_C_COMPILER=/path/to/gcc-4.2
will do.
project
name. -> The following variables get set with this name:
PROJECT_NAME
-
CMAKE_PROJECT_NAME
: only reachable in the top levelCMakeLists.txt
- Scope can be
PUBLIC
,PRIVATE
,INTERFACE
You can use set()
or file(GLOB SOURCES "src/*.cpp")
. file
allows also recursive search with GLOB_RECURSE
(instead of GLOB
).
set(SOURCES src/mainapp.cpp src/Student.cpp)
# OR
# Use globbing to add source files additions which are aded to the variable SOURCES
file(GLOB SOURCES "src/*.cpp")
Set a name for the executable (add_executable
): This is the name of the binary (executable) being created.
add_executable(execName ${SOURCES})
# TODO: how would it look like without the variable ${SOURCES}? -> add it explicitely
For a library instead it would be:
add_library(libTargetName SHARED ${SOURCES})
Options are:
SHARED
STATIC
MODULE
TODO: explain the differences (maybe also regarding licences) between those options
Add parts of code as library
set(INCLUDES_MYLIB
# Include directories containing the header files
${CMAKE_CURRENT_SOURCE_DIR}/src/
)
set(SOURCES_MYLIB
# Only source files have to be specified on a file basis
${CMAKE_CURRENT_SOURCE_DIR}/src/MyClass.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/AnotherClass.cpp
)
add_library(myLibName STATIC ${SOURCES_MYLIB} ${INCLUDES_MYLIB})
target_include_directories(myLibName PUBLIC ${INCLUDES_MYLIB})
target_link_libraries(myLibName PUBLIC someLib)
target_link_libraries(myLibName PUBLIC mainexec::anotherLocalLib)
# You can set aliases too
add_library(mainexec::mylib ALIAS myLibName)
You can nest CMakeLists.txt
. In order to do that add a add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/subfolder/)
which contains another CMakeLists.txt
etc.
- Directories (
CMakeLists.txt
) - Scripts (
<script>.cmake
) - Modules (
<module>.cmake
)
Type | Description |
---|---|
BOOL |
Bool (values: ON (1) / OFF (0) |
PATH |
Path to a directory |
FILEPATH |
Path to a file |
STRING |
String |
INTERNAL |
Hide variable in GUI or with cmake -L ; implies STRING |
STATIC |
Value managed by CMake do not change |
UNINITIALIZED |
Type not yet specified |
Evaluates to 0
if (all case insensitive):
- String is empty
-
0
,FALSE
,OFF
,N
,NO
,IGNORE
, or ends withNOTFOUND
Lists just strings with semicolon delimiters.
# Some list myVar with elements a, b and c
set(myVar a b c)
# equals
set(myVar "a;b;c")
# but
set(myVar "a b c")
# gives the string: "a b c"
For manipulation of lists use the list()
command.
See here.
set(var1 OFF)
set(var2 "var1")
if(${var2}) # Evaluates to OFF
CMake offers a variable cache. Those variables are set before the script runs and stored in CMakeCache.txt
. This is usally useful to set variables by the user.
# Sets VAR_NAME to `INIT_VALUE` if and only if VAR_NAME is unset so far
set(VAR_NAME "INIT_VALUE" CACHE STRING "Description")
# Shorthand for type BOOL
option(VAR_NAME "Description" [<value>])
# <value> defaults to OFF if not present
# Equivalent to:
set(VAR_NAME "OFF" CACHE BOOL "Description")
set(VAR_NAME "VALUE" CACHE INTERNAL "DEFAULT VALUE")
# Set default value and description/docstring
set(MY_DROPDOWN_VAR "Default" CACHE STRING "Description")
# Set options
set_property(CACHE MY_DROPDOWN_VAR PROPERTY STRINGS Default LearnCMake GoHome)
Doing so presents a drop-down menu in the CMake GUI.
# Read
$ENV{<env-var-name>}
# Test if defined
if(DEFINED ENV{<env-var-name>})
# Set (only affects the value within CMake!)
set(ENV{<variable>} [<value>])
# If <value> is an empty string or not present the variable is cleared
# Definition
function(<fctn_name> [<arg1> ...])
some_command()
endfunction()
# Call
a_func() # Name is case INsensitive
# or
cmake_language(CALL a_func)
Watch a variable for change.
variable_watch(myVar)
All predefined variable by CMake: https://cmake.org/cmake/help/latest/manual/cmake-variables.7.html
All predefined properties by CMake: https://cmake.org/cmake/help/latest/manual/cmake-properties.7.html
Prints all, properties/variables... see: https://cmake.org/cmake/help/latest/module/CMakePrintHelpers.html
Generates a lot of output!!
cmake --trace <...>
My take-away from reading some established modern practices:
Type | Convention |
---|---|
VARIABLE | UPPER_CASE_SNAKE_CASE |
function | lower_case_snake_case; prefer functions over macros |
CMakeFiles.cmake | CamelCase |
Use empty end commands for:
endforeach()
endif()
endfunction()
endmacro()
endwhile()
Also: Use empty else() commands.
# DO:
if(SOME_VAR)
some_command()
else()
some_command()
endif()
# NOT:
if(BAD_EXAMPLE)
some_command()
endif(BAD_EXAMPLE)
> tree
\-- project/
|-- CMakeLists.txt
|-- include/
| \-- MyClass.hpp
\-- src/
-- MyClass.cpp
-- mainapp.cpp
> mkdir projectBuild
> cd projectBuild
> cmake ../project
> make
- Always quote string variables (
"${MY_STRING_VAR}"
) - Never quote other types (
${MY_NON_STRING_VAR}
)
- Kiware CMake Wiki - pretty known
- Modern CMake - very modern and easy walk-through for beginners
- Effective Modern CMake
- Minimal CMake Example - actually not so minimal as the title suggests but qualifies as a good cheat sheet for the most common things
- Quick CMake tutorial - very short and light-weight CMake introduction partly using CLion menus
- Introduction to CMake by Example - nice introduction; really step-by-step with hands-on character
This work is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License *.
Code (snippets) are licensed under a MIT License *.
* Unless stated otherwise