The build system is critical to a large project, and can itself be seen as a sub-project within the larger project. cmake is a common build tool in the C++ ecosystem. Here is a brief introduction to the usage of cmake.

The simplest example

cmake_minimum_required(VERSION 3.15)

# set the project name
project(Tutorial)

# add the executable
add_executable(Tutorial tutorial.cpp)

Common commands

Take the following complex CMakeLists.txt file as an example:

cmake_minimum_required(VERSION 3.15)

# set the project name
project(Tutorial)

set(SRC_LIST tutorial.cpp)

option(USE_MYMATH "Use tutorial provided math implementation" ON)

# add the executable
add_executable(${PROJECT_NAME} ${SRC_LIST})

# add the library
add_library(MathFunctions MathFunctions/mysqrt.cpp)

configure_file(TutorialConfig.h.in TutorialConfig.h)

target_include_directories(${PROJECT_NAME} PUBLIC
                           ${PROJECT_BINARY_DIR}
                           )

target_link_libraries(${PROJECT_NAME} PUBLIC MathFunctions)
  • cmake_minimum_required(VERSION 3.15) Required minimum version of cmake
  • project(Tutorial) Set the name of the project
  • set(SRC_LIST tutorial.cpp) Set variables
  • option(USE_MYMATH “Use tutorial provided math implementation” ON) Set build options
  • add_executable(${PROJECT_NAME} ${SRC_LIST}) Add an executable as a build target
  • add_library(MathFunctions MathFunctions/mysqrt.cpp) Add a library as a build target
  • configure_file(TutorialConfig.h.in TutorialConfig.h) Generate a header file containing configuration information based on the template by replacing the contents of the template
  • target_include_directories target_include_directories(${PROJECT_NAME} PUBLIC ${PROJECT_BINARY_DIR} ) Add include directories to the build target
  • target_link_libraries(${PROJECT_NAME} PUBLIC MathFunctions) Link static libraries for the build target.

Set variables

list(APPEND EXTRA_LIBS MathFunctions)
set(SRC_LIST a.cpp b.cpp c.cpp)
string(TIMESTAMP COMPILE_TIME %Y%m%d-%H%M%S)
unset(SRC_LIST)

Common variables

${PROJECT_NAME}

${PROJECT_SOURCE_DIR}

${PROJECT_BINARY_DIR}

${CMAKE_ROOT}

${CMAKE_CURRENT_BINARY_DIR}

${CMAKE_CURRENT_SOURCE_DIR}

Control compiler behavior

set (CMAKE_CXX_STANDARD 23)
set (CMAKE_CXX_EXTENSIONS OFF)
set (CMAKE_CXX_STANDARD_REQUIRED ON)

build switch

``option`’’ defines the build switch, as in the following example:

option(USE_MYMATH "Use tutorial provided math implementation" ON)
if(USE_MYMATH)
  add_subdirectory(MathFunctions)
  list(APPEND EXTRA_LIBS MathFunctions)
  list(APPEND EXTRA_INCLUDES ${PROJECT_SOURCE_DIR}/MathFunctions)
endif()

Information from cmake to source code

In this example, define several variables for version and build time in the CMakeLists.txt file, and then generate the header file via configure_file.

CMakeLists.txt

set (Tutorial_VERSION_MAJOR 1)
set (tutorial_VERSION_MINOR 0)
set (tutorial_VERSION_PATCH 12)
string(TIMESTAMP COMPILE_TIME %Y%m%d-%H%M%S)

configure_file(TutorialConfig.h.in TutorialConfig.h)

TutorialConfig.h.in

// the configured options and settings for Tutorial
#define Tutorial_VERSION_MAJOR @PROJECT_VERSION_MAJOR@
#define Tutorial_VERSION_MINOR @PROJECT_VERSION_MINOR@
#define Tutorial_VERSION_PATCH @PROJECT_VERSION_PATCH@

#define TIMESTAMP @COMPILE_TIME@

Importing options

Assume that USE_SSL is an option defined by cmake, option(USE_SSL "Use SSL" ON). You can define and get the value of option in config.h.in like this.

``cmake #cmakedefine01 USE_SSL




## Macro

cmake's macro can be very powerful.

For example, this one is useful for adding source files to variables.

```cmake
macro(add_glob cur_list)
    file(GLOB __tmp CONFIGURE_DEPENDS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} ${ARGN})
    list(APPEND ${cur_list} ${__tmp})
endmacro()

macro(add_headers_and_sources prefix common_path)
    add_glob(${prefix}_headers ${CMAKE_CURRENT_SOURCE_DIR} ${common_path}/*.h)
    add_glob(${prefix}_sources ${common_path}/*.cpp ${common_path}/*.c ${common_path}/*.h)
endmacro()

macro(add_headers_only prefix common_path)
    add_glob(${prefix}_headers ${CMAKE_CURRENT_SOURCE_DIR} ${common_path}/*.h)
endmacro()

This one, for example, extends add_executable to create a more powerful add_executable.

macro (clickhouse_add_executable target)
    # invoke built-in add_executable
    # explicitly acquire and interpose malloc symbols by clickhouse_malloc
    # if GLIBC_COMPATIBILITY is ON and ENABLE_THINLTO is on than provide memcpy symbol explicitly to neutrialize thinlto's libcall generation.
    if (ARCH_AMD64 AND GLIBC_COMPATIBILITY AND ENABLE_THINLTO)
        add_executable (${ARGV} $<TARGET_OBJECTS:clickhouse_malloc> $<TARGET_OBJECTS:memcpy>)
    else ()
        add_executable (${ARGV} $<TARGET_OBJECTS:clickhouse_malloc>)
    endif ()

    get_target_property (type ${target} TYPE)
    if (${type} STREQUAL EXECUTABLE)
        # disabled for TSAN and gcc since libtsan.a provides overrides too
        if (TARGET clickhouse_new_delete)
            # operator::new/delete for executables (MemoryTracker stuff)
            target_link_libraries (${target} PRIVATE clickhouse_new_delete)
        endif()

        # In case of static jemalloc, because zone_register() is located in zone.c and
        # is never used outside (it is declared as constructor) it is omitted
        # by the linker, and so jemalloc will not be registered as system
        # allocator under osx [1], and clickhouse will SIGSEGV.
        #
        # [1]: https://github.com/jemalloc/jemalloc/issues/708
        #
        # About symbol name.
        # - _zone_register not zone_register due to Mach-O binary format.
        # - _je_zone_register due to JEMALLOC_PRIVATE_NAMESPACE=je_ under OS X.
        # - but jemalloc-cmake does not run private_namespace.sh
        # so symbol name should be _zone_register
        if (ENABLE_JEMALLOC AND OS_DARWIN)
            set_property(TARGET ${target} APPEND PROPERTY LINK_OPTIONS -u_zone_register)
        endif()
    endif()
endmacro()

build directory

build/
    CMakeCache.txt
    CMakeFiles/
    cmake_install.cmake
    Makefile
    Tutorial.exe
    TutorialConfig.h
    MathFunctions/

Makefile is the build file generated by cmake from the top-level CMakeLists.txt, through which the whole project can be compiled.

Tutorial.exe is the generated executable file through which the program is run.

MathFunction is the directory of the library.