Learn CMake
In brief, CMake is all about targets and properties
CMake Project Structure
A typical CMake project can be regarded to has three Tree
:
Source Tree:
project_root
├── CMakeLists.txt
├── simple_example.cpp
├── simple_lib.cpp
└── simple_lib.hpp
Build Tree:
project_root
├── CMakeLists.txt
├── simple_example.cpp
├── simple_lib.cpp
├── simple_lib.hpp
└── build
└── CMakeCache.txt
Install Tree:
This tree is located in the CMAKE_INSTALL_PREFIX
, of which default value is platform-dependent. By default, it is set to /usr/local
on Unix-like systems (Linux, macOS) and C:/Program Files/<Project Name>
on Windows..
To change it, you can pass -DCMAKE_INSTALL_PREFIX
argument during CMake configuration
step, like this:
cmake -B build -S . -DCMAKE_INSTALL_PREFIX=/my/custom/installation/path
Alternatively, you can change it by passing --prefix
(it can be relative path) argument during CMake install
step, like this:
cmake --install build --prefix "/my/custom/installation/path"
It's recommended to use a default install layout as GNUInstallDirs
.
A install tree will look like as below if you'd like all things to be installed inside the project via cmake --install build --prefix "./install
.
project_root
├── CMakeLists.txt
├── simple_example.cpp
├── simple_lib.cpp
├── simple_lib.hpp
├── build
│ └── CMakeCache.txt
└── install
├── bin
│ └── executables
├── sbin
│ └── sysadmin executables
├── lib
│ ├── compiled libraries (*.so (unix) or *.dll (windows))
│ └── library archive files (*.lib (windows))
├── libexec
│ └── executables not directly invoked by user
├── include
│ └── header files
└── doc
└── documentation
How CMake Works
A typical workflow of CMake includes Configure
, Build
and Install
steps, combined with the above mentioned Trees
concepts.
Configure
step will generate a sort of configuration files, the most important ones among them areCMakeCache.txt
,cmake_install.cmake
andMakefile
if usingMake
as building system. With these generated configuration files, the later stepsBuild
andInstall
will run according to them.Build
step will generate the build binary directory.Install
step will generate the install binary directory.
How to make your package be found by others by find_package()
package configuration files: find_package
RPATH in CMake
CMake Variables
There are some useful and important CMake variables that will be introduced here:
CMAKE_PREFIX_PATH
CMAKE_IGNORE_PATH
clang
FAQ
Find out clang
include search path
❯ clang -x c -v -E /dev/null
...
#include "..." search starts here:
#include <...> search starts here:
/opt/homebrew/Cellar/llvm/17.0.1/lib/clang/17/include
/Library/Developer/CommandLineTools/SDKs/MacOSX14.sdk/usr/include
/Library/Developer/CommandLineTools/SDKs/MacOSX14.sdk/System/Library/Frameworks (framework directory)
End of search list.
# 1 "/dev/null"
# 1 "<built-in>" 1
# 1 "<built-in>" 3
# 420 "<built-in>" 3
# 1 "<command line>" 1
# 1 "<built-in>" 2
# 1 "/dev/null" 2
Add include search path to clang
Use environment variables C_INCLUDE_PATH
for c
and CPLUS_INCLUDE_PATH
for c++
.
clang
:
❯ C_INCLUDE_PATH=/opt/homebrew/include clang -x c -v -E /dev/null
...
#include "..." search starts here:
#include <...> search starts here:
/usr/local/include
/opt/homebrew/include
/Library/Developer/CommandLineTools/usr/lib/clang/15.0.0/include
/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include
/Library/Developer/CommandLineTools/usr/include
/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/Frameworks (framework directory)
clang++
:
❯ CPLUS_INCLUDE_PATH=/opt/homebrew/include clang -x c++ -v -E /dev/null
...
#include "..." search starts here:
#include <...> search starts here:
/usr/local/include
/opt/homebrew/include
/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/c++/v1
/Library/Developer/CommandLineTools/usr/lib/clang/15.0.0/include
/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include
/Library/Developer/CommandLineTools/usr/include
/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/Frameworks (framework directory)
Use -I
flag,
❯ clang -x c -I/opt/homebrew/include -v -E /dev/null
...
#include "..." search starts here:
#include <...> search starts here:
/opt/homebrew/include
/usr/local/include
/Library/Developer/CommandLineTools/usr/lib/clang/15.0.0/include
/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include
/Library/Developer/CommandLineTools/usr/include
/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/Frameworks (framework directory)
Find out clang
library search paths
❯ clang -Xlinker -v
...
Library search paths:
/usr/local/lib
Framework search paths:
ld: Undefined symbols:
_main, referenced from:
<initial-undefines>
clang: error: linker command failed with exit code 1 (use -v to see invocation)
Add library search path to clang
Use environment variables LIBRARY_PATH
,
❯ LIBRARY_PATH=$LIBRARY_PATH:/usr/lib clang -Xlinker -v
...
Library search paths:
.
/usr/lib
/usr/local/lib
Framework search paths:
ld: Undefined symbols:
_main, referenced from:
<initial-undefines>
clang: error: linker command failed with exit code 1 (use -v to see invocation)
Use -L
flag:
❯ clang -L/opt/homebrew/lib -Xlinker -v
https://langui.sh/2015/07/24/osx-clang-include-lib-search-paths/
What is the difference? clang++ | clang -std=c++11
CMake FAQ
Add library search path to CMake
globally in project
set(CMAKE_LIBRARY_PATH ${CMAKE_LIBRARY_PATH} /opt/local/lib)
LINK_DIRECTORIES(/opt/local/lib)
Resources
CMake hands-on workshop — CMake Workshop