CMake
Wed Oct 29 2025 00:00:00 GMT+0000 (Coordinated Universal Time)
Let’s learn CMake together!
BASICS
Minimum version
cmake_minimum_required(VERSION 3.15)
after cmake 3.12, it supports range of versions.
cmake_minimum_required(VERSION 3.15...4.0)
Setting a project
project(Myproject VERSION 1.0
DESCRIPTION "my first project"
LANGUAGES C CXX
)
all the keyword arguments are optional.
Making exectables
add_exectable(one two.cpp three.h)
one: name of the executable file and the cmake target.
Making library
add_library(one STATIC two.cpp three.h)
type: STATIC, SHARED, MODULE
if it is not defined, BUILD_SHARED_LIBS choose between STATIC AND SHARED
Targets
target_include_directories(one PUBLIC include)
PUBLIC, PRIVATE, INTERFACE
Example
cmake_minimum_required(VERSION 3.15...4.0)
project(Calculator LANGUAGES CXX)
add_library(calclib STATIC src/calclib.cpp include/calc/lib.hpp)
target_include_directories(calclib PUBLIC include)
target_compile_features(calclib PUBLIC cxx_std_11)
add_executable(calc apps/calc.cpp)
target_link_libraries(calc PUBLIC calclib)
Variables and caches
Local variables
set(MY_VARIABLE "values")
all caps
access them using ${}
list of values set(MY_LIST "one" "two" )
paths are accessed with double quots "${MY_PATH}"
Set variables from commandline
set(MY_CACHED_VARAIBLE "value" CACHE STRING "Description")
not overrides unless set(MY_CACHED_VARAIBLE "value" CACHE STRING "" FORCE).
to avoid showing when cmake -L .. is called the variable mark_as_advanced(MY_CACHED_VARAIBLE) as second line of command or else
set(MY_CACHED_VARAIBLE "value" CACHE INTERNAL "").
BOOL as same with option(MY_OPTION "This is settable from commandline" OFF)
Properties
set_property(TARGET TargetName
POPERTY CXX_STANDARD 11)
set_target_properties(TargetName PROPERTIES
CXX_STANDARD 11)
likewise get_property(ResultVariable TARGET TargetName PROPERTY CXX_STANDARD )
Control flow
if(variable)
#
else()
#
endif()
genrator-expressions
unlike most CMake commands happen at configuration stage, generator expression used in build and install time.
for an example, put compile time flag only for DEBUG configuration
target_compile_options(MyTarget PRIVATE "$<$<CONFIG:Debug>:--my-flag>")
Macros and Functions
simple function
function(SIMPLE REQUIRED_ARG)
message(STATUS "simple arguments: ${REQUIRED_ARG}, followed by ${ARGN}")
set(${REQUIRED_ARG} "From SIMPLE" PARENT_SCOPE)
endfunction()
simple(This Foo Bar)
message("Output: ${This}")
output will be
-- Simple arguments: This, followed by Foo;Bar
Output: From SIMPLE
Communicate with code
in version.h.in
#prgma once
#define MY_VERSION_MAJOR @PROJECT_VERSION_MAJOR@
#define MY_VERSION "@PROJECT_VERSION@"
in cmake lines:
configure_file(
"${PROJECT_SOURCE_DIR}/include/my/version.h.in"
"${PROJECT_SOURCE_DIR}/include/my/version.h"
)
likewise can read them from files:
set(VERSION_REGX "#define MY_VERSION[ \t]\"(.+)\"")
#read the file containg the version
file(STRINGS "${CMAKE_CURRENT_SOURCE_DIR}/include/my/version.hpp"
VERSION_STRING REGEX ${VERSION_REGEX})
#take just version
string(REGEX REPLACE ${VERSION_REGEX} "\\1" VERSION_STRING "${VERSION_STRING}")
#get project version automatically
project(my LANGUAGES CXX VERSION ${VERSION_STRING})
project structure
- project
- .gitignore
- README.md
- LICENSE.md
- CMakeLists.txt
- cmake
- FindSomeLib.cmake
- include
- project
- lib.hpp
- src
- CMakeLists.txt
- lib.cpp
- apps
- CMakeLists.txt
- app.cpp
- tests
- CMakeLists.txt
- testlib.cpp
- docs
- CMakeLists.txt
- extern
- googletest
- scripts
- helper.py
Apparent things:
no CMakeLists.txt inside include,
while add_subdirectory to add folders containing a CMakeLists.txt file
use set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH})
Compare with available project(openAMP)
here is the link “https://github.com/OpenAMP/open-amp.git” project have the cmake directory with some importatnt point to note.
linker
let’s say you have linker.ld.in
MEMORY
{
RAM (rwx) : ORIGIN = @RAM_ORIGIN@, LENGTH = @RAM_LENGTH@
FLASH (rx) : ORIGIN = @FLASH_ORIGIN@, LENGTH = @FLASH_LENGTH@
}
SECTIONS
{
/* typical sections here */
}
and in cmake you may have
# Memory configuration values
set(RAM_ORIGIN 0x20000000)
set(RAM_LENGTH 64K)
set(FLASH_ORIGIN 0x08000000)
set(FLASH_LENGTH 512K)
configure_file(
${CMAKE_CURRENT_SOURCE_DIR}/linker.ld.in
${CMAKE_CURRENT_BINARY_DIR}/linker.ld
@ONLY
)
which means Every @SOMETHING@ in .in file will be replaced with ${SOMETHING} from CMake.
The result linker.ld is placed in the binary directory.
in /cmake/platforms/zynqmp_r5_generic.cmake
set (CMAKE_SYSTEM_PROCESSOR "arm" CACHE STRING "")
set (MACHINE "zynqmp_r5" CACHE STRING "")
set (CROSS_PREFIX "armr5-none-eabi-" CACHE STRING "")
# Xilinx SDK version earlier than 2017.2 use mfloat-abi=soft by default to generate libxil
set (CMAKE_C_FLAGS "-mfloat-abi=hard -mfpu=vfpv3-d16 -mcpu=cortex-r5" CACHE STRING "")
include (cross_generic_gcc)
whatif in a generic platforms?
set (CMAKE_SYSTEM_NAME "Generic" CACHE STRING "")
set (CMAKE_C_COMPILER "${CROSS_PREFIX}gcc")
set (CMAKE_CXX_COMPILER "${CROSS_PREFIX}g++")
# _exit is in the BSP rather than in libgcc, leaving this out
# causes errors in try_compile on ARM generic.
set (CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY)
set (CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER CACHE STRING "")
set (CMAKE_FIND_ROOT_PATH_MODE_LIBRARY NEVER CACHE STRING "")
set (CMAKE_FIND_ROOT_PATH_MODE_INCLUDE NEVER CACHE STRING "")
at the same time for linux in zynqmp
set (CMAKE_SYSTEM_PROCESSOR "arm64")
set (CROSS_PREFIX "aarch64-linux-gnu-")
set (MACHINE "zynqmp" CACHE STRING "")
include (cross_linux_gcc)
for other linux platforms
set (CMAKE_SYSTEM_NAME "Linux")
set (CMAKE_C_COMPILER "${CROSS_PREFIX}gcc")
set (CMAKE_CXX_COMPILER "${CROSS_PREFIX}g++")
set (CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set (CMAKE_FIND_ROOT_PATH_MODE_LIBRARY NEVER)
set (CMAKE_FIND_ROOT_PATH_MODE_INCLUDE NEVER)
we can think of it as a toolchain.cmake file.
inside the modules they have FindLibmetal.cmake
# FindLibmetal
# --------
#
# Find Libmetal
#
# Find the native Libmetal includes and library this module defines
#
# ::
#
# LIBMETAL_INCLUDE_DIR, where to find metal/sysfs.h, etc.
# LIBSYSFS_LIB_DIR, where to find libmetal library.
# FIX ME, CMAKE_FIND_ROOT_PATH doesn't work
# even use the following
# set (CMAKE_FIND_ROOT_PATH_MODE_LIBRARY BOTH)
# set (CMAKE_FIND_ROOT_PATH_MODE_INCLUDE BOTH)
# set (CMAKE_FIND_ROOT_PATH_MODE_PROGRAM BOTH)
find_path(LIBMETAL_INCLUDE_DIR NAMES metal/sys.h PATHS ${CMAKE_FIND_ROOT_PATH})
find_library(LIBMETAL_LIB NAMES metal PATHS ${CMAKE_FIND_ROOT_PATH})
get_filename_component(LIBMETAL_LIB_DIR ${LIBMETAL_LIB} DIRECTORY)
# handle the QUIETLY and REQUIRED arguments and set HUGETLBFS_FOUND to TRUE if
# all listed variables are TRUE
include (FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS (Libmetal DEFAULT_MSG LIBMETAL_LIB LIBMETAL_INCLUDE_DIR)
if (LIBMETAL_FOUND)
set (LIBMETAL_LIBS ${LIBMETAL_LIB})
endif (LIBMETAL_FOUND)
mark_as_advanced (LIBMETAL_LIB LIBMETAL_INCLUDE_DIR LIBMETAL_LIB_DIR)