Skip to main content

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.

  1. Configure step will generate a sort of configuration files, the most important ones among them are CMakeCache.txt, cmake_install.cmake and Makefile if using Make as building system. With these generated configuration files, the later steps Build and Install will run according to them.
  2. Build step will generate the build binary directory.
  3. 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

Title

RPATH in CMake

1

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

  1. set(CMAKE_LIBRARY_PATH ${CMAKE_LIBRARY_PATH} /opt/local/lib)
  2. LINK_DIRECTORIES(/opt/local/lib)

Resources

CMake hands-on workshop — CMake Workshop

Footnotes

  1. RPATH handling from official cmake