CMake

CMake

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)

Reference

Email icon Github icon Youtube icon Linkedin icon

Copyright © 2025 Vakeesan All rights reserved