Example:
D:\Dev\Open3D\ycdev-cpp\realsense\CMakeLists.txt
http://www.elpauer.org/stuff/learning_cmake.pdf Learning Cmake
- cmake --help-command-list //Get a list command used in Cmake
- Cmake --debug-output ../src //See what camke really does
- Cmake -- trace ../src //Lots of output or --trace-expand(trace and expanded variables)
Cmake variable are case sensitive, while commands are case insensitive
message(STATUS foo) //Message is command. STATUS is key word, it is case sensitive
MESSAGE(status foo)
Output:
foo
Statusfoo
Set(foo 42)
MESSAGE(STATUS ${foo})
MESSAGE(STATUS %{Foo})
Output:
-- 42
-- (nothing)
Have this tree:
- Myapp
- Src
- Build
- Cd myapp/build
- Cmake ../src //It will generate VS project and solution in Build folder
Very simple executable:
- cmake_minimum_required(VERSION 3.17) //My installation Cmake 3.17
- PROJECT( helloworld)
- SET (hello_SRCS hello.cpp)
- ADD_EXECUTABLE(hello ${hello_SRCS}) //VS project use hello.proj
Very simple library:
- PROJECT( mylibrary) //Solution use this name
- SET (mylib_SRCS library.cpp)
- ADD_LIBRARY(my SHARED ${mylib_SRCS}) //If without SHARED, generate a static library
The Cmake cache stores values which are not usually changed. Edit cache use CMakeSetup(Windows)
SET (var1 13)
- If "var1" already existed in the cache, it is shadowed by this value
- This call does not overwite "var1" value in the cache, it existed
SET(var2 17 ... CACHE ...) //Reuse the cache
- "var2" already in cache => keep cache
- "var2" not yet un cache, var2 is set to 17
SET(var3 23 ... CACHE FORCE) //Unconditional set and overwrite cache
- "var3" always takes this value, whether it was already in the cache or not
Flow control
- IF(expression)
...
ELSEIF(expression2) //the expression2 or expression in else and endif is optional. Add them is good style
...
ELSE(expression)
...
ENDIF(expression)
If(FOO) //FOO is true if it is 1, ON, YES, TRUE, Y
EndIf(FOO)
if(var) if(NOT var) if(var AND var) if(var OR var) if(DEFINED var) if(EXISTS filename) if(EXISTS dirname)
if(n1 IS_NEWER_THAN n2) if(var MATCHES regex) if(1 LESS 3) if(FOO STRLESS BAR)
- Process a list:
FOREACH(loop_var)
...
ENDFOREACH(loop_var)
- WHILE(condition)
...
ENDWHILE(condition)
Managing Debug and Release builds
- SET(CMAKE_BUILD_TYPE Debug)
- As any other variable, it can be set from the command line:
Cmake -DCMAKE_BUILD_TYPE=Release ../src
- Specify debug and release targets and 3rdparty libs:
TARGET_LINK_LIBRARIES(wakeup RELASEE ${wakeup_SRCS})
TARGET_LINK_LIBRARIES(wakeup DEBUG ${wakeup_SRCS})
Directories
- Libraries built in your project (even if in a different CmakeLists.txt) is automatic (in rare occasions: ADD_DEPENDENCIES)
- If the 3rd party library or .h is in a “standard” directory (PATH and/or LD_LIBRARY_PATH) is automatic
- If in a nonstandard dir:
- Headers: use INCLUDE_DIRECTORIES
- Libraries: use FIND_LIBRARY and link with the result of it (try to avoid LINK_DIRECTORIES)
Make install
- INSTALL(TARGETS clock wakeup RUNTIME DESTINATION bin LIBRARY DESTINATION lib)
- Would install in /usr/local/bin and /usr/local/lib(Unix) or %PROGRAMFILES%\projectname(Windows)
Macros
- MACRO( <name> [arg1 [arg2 [arg3 ...]]] )
COMMAND1(ARGS ...)
COMMAND2(ARGS ...)
...
ENDMACRO( <name> )
- They perform text substitution, just like #define does in C
Message
- MESSAGE( [SEND_ERROR | STATUS | FATAL_ERROR] "message to display" ... )
set(SRC adder.c main.c)
if(NOT DEFINED SRC)
message (FATAL_ERROR "No sources defined“)
endif ()
foreach (file ${SRC}) //Printing all source files
message(${file})
endforeach ()
Variables
Use this snippet to list all variables and their values:
- get_cmake_property( P VARIABLES )
foreach( VAR in ${P} )
message( STATUS " ${VAR}=${${VAR}}" )
endforeach()
Properties
- A very short and simple way to think about it is that properties are variables scoped to a target. For example:
add_executable(foo foo.cpp)
set_target_properties(foo PROPERTIES
CXX_STANDARD 14
CXX_EXTENSIONS OFF)
# Build foo with c++11 for some reason
add_executable(foo11 foo.cpp)
set_target_properties(foo11 PROPERTIES
CXX_STANDARD 11
CXX_EXTENSIONS OFF)
Cmake default variables
- CMAKE_SOURCE_DIR //refer to the folder where the top-level CMakeLists.txt
- PROJECT_SOURCE_DIR //refers to the folder of the CMakeLists.txt containing the most recent project() command(local)
Example:
project(Outer)
add_subdirectory(Inner)
and Inner's:
project(Inner)
CMAKE_SOURCE_DIR is Outer's source dir. For Outer, PROJECT_SOURCE_DIR is the same
For Inner, PROJECT_SOURCE_DIR is the subdirectory containing its CMakeLists.txt
Subconfigs
- Add_subdirectory(subfolder)
- Subconfig creates its own executable/library which is used by toplevel config
- Only describes source and header files and toplevel adds them to its build process
Find_package
Include
- include(<file|module> [OPTIONAL] [RESULT_VARIABLE <VAR>] [NO_POLICY_SCOPE])
Load and run CMake code from the file given.If OPTIONAL is present, then no error is raised if the file does not exist. If RESULT_VARIABLE is given the variable will be set to the full filename which has been included or NOTFOUND if it failed. If a module is specified instead of a file, the file with name <modulename>.cmake is searched first in CMAKE_MODULE_PATH, then in the CMake module directory.
- CMake 'module' is simply a file that can be used with the find_package directive. That is, when you run find_package(Module), it searches for a file in the MODULE_PATH that is named FindModule.cmake.
If you include a file without the extension, it too will search through your MODULE_PATH for that file.cmake. Example:
+ root/
+ CMakeLists.txt
+ cmake/
| + FindMatlab.cmake
| + TestInline.cmake
| + stdint.cmake
+ src/
+ include/
In CMakeLists.txt:
set (CMAKE_MODULE_PATH "${CMAKE_MODULE_PATH};${CMAKE_CURRENT_SOURCE_DIR}/cmake")
find_package (Matlab) # runs FindMatlab.cmake
include(TestInline) # defines a macro:
test_inline (CONFIG_C_INLINE)
include(stdint) # simply executes flat CMake code
- CMAKE_MODULE_PATH
list of directories specifying a search path for CMake modules to be loaded by the include() or find_package() commands before checking the default modules that come with CMake. By default it is empty, it is intended to be set by the project.
To append your module path to CMAKE_MODULE_PATH
LIST(APPEND CMAKE_MODULE_PATH ${YourPath})
Or if you prefer your modules to be used first
LIST(INSERT CMAKE_MODULE_PATH 0 ${Yourpath})
- Cmake has Predefined Modules
Example BZIP2: Predefined „find“-modules search for the libraries and define variables
# BZIP2_FOUND - system has BZip2
# BZIP2_INCLUDE_DIR - the BZip2 include directory
# BZIP2_LIBRARIES - Link these to use BZip2
# BZIP2_NEED_PREFIX - this is set if the functions are prefixed with
BZ2_find_package (BZip2)
include_directories(${BZIP_INCLUDE_DIRS})
target_link_libraries (cmakeexample ${BZIP2_LIBRARIES})
- Find_library
Example Looking for the TCL Library
find_library (TCL_LIBRARY NAMES tcl tcl84 tcl83 tcl82 tcl80 PATHS /usr/lib /usr/local/lib)
if (TCL_LIBRARY)
target_link_library(fooexe ${TCL_LIBRARY})
endif ()
cmake --graphviz=graph/test.dot . //Run it in build tree, such as under D:\Dev\Open3D\Open3D\build>
//Install GraphViz it has dot command
dot -Tpdf graph1.dot -o graph1.pdf
From <https://github.com/rakhimov/cppdep/wiki/How-to-view-or-work-with-Graphviz-Dot-files>
# Convert dot to png via graphviz
dot -Tpng filename.dot -o filename.png
# Convert dot to svg via graphviz
dot -Tsvg filename.dot -o filename.svg