Skip to content

Wiki

Top-level CMake and common compilation options

About 18 min read

Existing English translations were reused. 28 current source blocks remain in Simplified Chinese, without triggering paid translation.

本节先讲两个文件:

  1. Top-level CMakeLists.txt
  2. cmake/ProjectOptions.cmake

这两个文件决定了整个工程的基本身份和公共编译规则。最后再介绍 Windows 安装时使用的 cmake/WindowsRuntime.cmake。

1.Top level CMakeLists.txt

The top-level files of this template are as follows:

cmake_minimum_required(VERSION 3.22)

project(cmake_template VERSION 1.0.0 LANGUAGES C CXX)

include(GNUInstallDirs)
include(CTest)

option(BUILD_SHARED_LIBS "Build shared project libraries" ON)

include("${CMAKE_CURRENT_LIST_DIR}/cmake/ProjectOptions.cmake")

add_subdirectory(src)

include("${CMAKE_CURRENT_LIST_DIR}/cmake/WindowsRuntime.cmake")

if(BUILD_TESTING)
  add_test(NAME eigen_example COMMAND ${PROJECT_NAME})
  set_tests_properties(eigen_example PROPERTIES
    PASS_REGULAR_EXPRESSION "Determinant = -2"
  )
endif()

Top-level CMakeLists.txt principle: only perform overall control, do not pile up specific business logic and third-party dependencies.

2.cmake_minimum_required

cmake_minimum_required(VERSION 3.22)

Function:

  1. Specify the minimum CMake version required by the project.
  2. Tell CMake to use the policy behavior corresponding to that version.
  3. Too old a CMake will report an error directly and will not continue configuring.

What can be filled in:

cmake_minimum_required(VERSION 3.20)
cmake_minimum_required(VERSION 3.22)
cmake_minimum_required(VERSION 3.25)
cmake_minimum_required(VERSION 3.28)

Selection recommendations:

写法 说明
3.10 太老,很多现代 CMake 写法不方便
3.16 Ubuntu 20.04 常见版本,兼容性好
3.22 本模板使用,兼容 Ubuntu 22.04 的 CMake 3.22.1,搭配预设格式版本 3
3.25 使用预设格式版本 6 时需要,本模板当前没有使用其新增功能
3.28 Ubuntu 24.04 常见版本,系统软件源提供 CMake 3.28.3
4.2 Ubuntu 26.04 常见版本,系统软件源提供 CMake 4.2.3

在 Ubuntu 24.04、26.04 上使用较新的 CMake 时,工程仍可保留 VERSION 3.22 的最低要求。只有实际使用了更高版本才支持的功能时,才需要提高最低版本。

最低版本应根据实际使用的功能和验证环境确定。本模板在 CMake 3.22.1 下已验证配置、构建、安装与运行,无需提高到 3.25。调整完整工程的最低要求时,还应同步检查 CMakePresets.json 的格式版本和 cmakeMinimumRequired。

3.project

project(cmake_template VERSION 1.0.0 LANGUAGES C CXX)

Function:

  1. Set the project name.
  2. Set the project version.
  3. Declare which languages the project uses.
  4. Initialize a set of CMake variables.

3.1.Project Name

project(cmake_template)

The project name affects these variables:

Variable Value
PROJECT_NAME cmake_template
CMAKE_PROJECT_NAME Top-level project name, usually also cmake_template

This template is used in src/CMakeLists.txt:

add_executable(${PROJECT_NAME}
  ${CMAKE_CURRENT_SOURCE_DIR}/main.cpp
)

So the final executable file name is:

cmake_template

If the project name is changed to:

project(robot_app VERSION 1.0.0 LANGUAGES C CXX)

The final executable file will become:

robot_app

3.2.VERSION

VERSION 1.0.0

Project version number.

What can be filled in:

VERSION 0.1.0
VERSION 1.0.0
VERSION 2.3.4

CMake generates these variables:

Variable Example value
PROJECT_VERSION 1.0.0
PROJECT_VERSION_MAJOR 1
PROJECT_VERSION_MINOR 0
PROJECT_VERSION_PATCH 0

Later, if you create installation packages, export CMake packages, or generate version header files, these variables will be very useful.

3.3.LANGUAGES

LANGUAGES C CXX

Declares which languages are enabled for the project.

Common values:

Value Description
C C language
CXX C++
CUDA CUDA
ASM Assembly
Fortran Fortran

If the project only has C++, you can also write:

project(cmake_template VERSION 1.0.0 LANGUAGES CXX)

This template retains C CXX to allow both src/*.c and src/*.cpp to participate in construction.

4.include(GNUInstallDirs)

include(GNUInstallDirs)

Purpose: Load GNU-style installation directory variables.

Common variables:

Variable Common Value Purpose
CMAKE_INSTALL_BINDIR bin Executable file
CMAKE_INSTALL_LIBDIR lib or lib64 Dynamic library, static library
CMAKE_INSTALL_INCLUDEDIR include Header file
CMAKE_INSTALL_DATADIR share Data file
CMAKE_INSTALL_DOCDIR share/doc/项目名 Documentation

Why not write it by hand:

install(TARGETS app DESTINATION bin)
install(TARGETS lib DESTINATION lib)

Because different Linux distributions may use lib or lib64. After using GNUInstallDirs, CMake will set it according to system conventions.

Usage example in the template:

install(TARGETS ${PROJECT_NAME}
  RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
)

Library installation:

install(TARGETS ${PREFIX}_src_lib
  LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
  ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
  RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
)

Header file installation:

install(DIRECTORY "${CMAKE_CURRENT_LIST_DIR}/inc/"
  DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
)

5.选择库类型和启用测试

顶层先提供库类型选项:

option(BUILD_SHARED_LIBS "Build shared project libraries" ON)

option 创建一个可以在配置时选择的开关。这里默认开启,Linux 预设也明确设置为 ON;Windows 预设设置为 OFF。后面的库模块不写死库类型,就可以使用这项设置。

顶层还加载:

include(CTest)

CTest 使工程可以用 ctest 执行测试,并提供 BUILD_TESTING 选项。顶层的 eigen_example 测试会运行当前程序,再检查原始 Eigen 示例的输出。它不增加业务源码。

add_subdirectory(src) 后加载的 WindowsRuntime.cmake 用于安装常用 GCC 运行库,后面单独解释。

6.include("${CMAKE_CURRENT_LIST_DIR}/cmake/ProjectOptions.cmake")

include("${CMAKE_CURRENT_LIST_DIR}/cmake/ProjectOptions.cmake")

Purpose: Read another CMake file.

6.1.include(...)

include will execute the specified CMake script in the current scope.

You can write:

include(GNUInstallDirs)
include(cmake/ProjectOptions.cmake)
include("${CMAKE_CURRENT_LIST_DIR}/cmake/ProjectOptions.cmake")

Difference:

Syntax Description
include(GNUInstallDirs) Load a CMake built-in module
include(cmake/ProjectOptions.cmake) Relative path syntax
include("${CMAKE_CURRENT_LIST_DIR}/cmake/ProjectOptions.cmake") More robust, not affected by the current working directory

6.2.CMAKE_CURRENT_LIST_DIR

Indicates the directory where the CMake file currently being processed is located.

At the top level, in CMakeLists.txt, it is the project root directory.

Therefore:

"${CMAKE_CURRENT_LIST_DIR}/cmake/ProjectOptions.cmake"

Indicates:

项目根目录/cmake/ProjectOptions.cmake

7.add_subdirectory(src)

add_subdirectory(src)

Function: Enter the src subdirectory and continue reading src/CMakeLists.txt.

What can be filled in:

add_subdirectory(src)
add_subdirectory(test)
add_subdirectory(third_party/some_lib)

Common notation:

add_subdirectory(<源码目录>)

You can also specify the build directory:

add_subdirectory(src src_build)

But it is generally not needed.

Write only at the top level:

add_subdirectory(src)

表示具体的可执行文件、库、依赖,都交给 src 下面的 CMake 文件管理。

8.cmake/ProjectOptions.cmake

The content of this template is as follows:

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

set(CMAKE_C_STANDARD 11)
set(CMAKE_C_STANDARD_REQUIRED ON)
set(CMAKE_C_EXTENSIONS OFF)

set(CMAKE_EXPORT_COMPILE_COMMANDS ON)

# Keep the Windows executable in a predictable location.
if(WIN32)
  set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin")
endif()

add_library(project_options INTERFACE)
target_compile_features(project_options INTERFACE cxx_std_20)

add_library(project_warnings INTERFACE)
target_compile_options(project_warnings INTERFACE
  "$<$<COMPILE_LANG_AND_ID:C,GNU,Clang>:-Wall;-Wextra;-Wpedantic>"
  "$<$<COMPILE_LANG_AND_ID:CXX,GNU,Clang>:-Wall;-Wextra;-Wpedantic>"
)

This file only contains project common options, not third-party dependencies such as Eigen, OpenCV, etc.

9.CMAKE_CXX_STANDARD

set(CMAKE_CXX_STANDARD 20)

Set the default C++ standard.

Common values:

Value Standard
11 C++11
14 C++14
17 C++17
20 C++20
23 C++23

本模板默认使用 C++20:

set(CMAKE_CXX_STANDARD 20)

如果想用 C++23:

set(CMAKE_CXX_STANDARD 23)
target_compile_features(project_options INTERFACE cxx_std_23)

Note that it is best to change both places together.

CMake 版本和 C++ 标准版本需要分别判断。CMake 3.22 已能识别 cxx_std_20,但这表示选择相应的编译器标准模式,不代表编译器和标准库已经实现全部 C++20 功能;具体说明见 CMake 编译特性文档。模板当前的 Eigen 示例已使用 GCC 11.4 验证,后续程序用到的新语法和标准库功能仍需对应的编译器支持。

10.CMAKE_CXX_STANDARD_REQUIRED

set(CMAKE_CXX_STANDARD_REQUIRED ON)

Purpose: Requires the compiler to support the specified C++ standard.

What can be filled in:

Value Description
ON The specified standard must be used; if unsupported, report an error
OFF If unsupported, it may fall back to a lower standard

模板建议写 ON,使不支持所选标准模式的编译器明确报错,避免静默退回旧标准。

11.CMAKE_CXX_EXTENSIONS

set(CMAKE_CXX_EXTENSIONS OFF)

Function: Controls whether to use the compiler extension standard.

What can be filled in:

值 编译选项倾向 说明
ON gnu++20 允许 GNU 扩展
OFF c++20 使用所选标准的非 GNU 扩展模式

表中以模板默认的 C++20 为例,具体参数拼写由 CMake 根据编译器选择。Linux-only 项目也建议先写 OFF,这样代码更接近标准 C++。

12.C standard-related options

set(CMAKE_C_STANDARD 11)
set(CMAKE_C_STANDARD_REQUIRED ON)
set(CMAKE_C_EXTENSIONS OFF)

The meaning is similar to the corresponding option C++.

Common C standards:

Value Standard
99 C99
11 C11
17 C17
23 C23

If the project does not have a .c file, you can also enable only C++:

project(cmake_template VERSION 1.0.0 LANGUAGES CXX)

Then delete the C standard-related settings.

13.CMAKE_EXPORT_COMPILE_COMMANDS

set(CMAKE_EXPORT_COMPILE_COMMANDS ON)

Function: Generate compile_commands.json.

You can enter:

Value Description
ON Generate
OFF Do not generate

This option is also written in CMakePresets.json:

"CMAKE_EXPORT_COMPILE_COMMANDS": "ON"

Writing both places does not conflict. The advantage of preset is that both the IDE and the command line can clearly see this configuration; the advantage of ProjectOptions.cmake is that it is enabled by default even without using preset.

14.Windows 的运行文件输出目录

if(WIN32)
  set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin")
endif()

WIN32 表示当前构建目标是 Windows,也包含 64 位 Windows。这里在创建各 target 之前,统一指定运行文件的输出位置。

Windows 主程序会进入 build/windows-debug/bin/。两个项目库按预设选择静态链接;Linux 不进入这个输出目录分支,保留原有构建目录布局。

15.add_library(project_options INTERFACE)

add_library(project_options INTERFACE)

Create an interface target.

INTERFACEThe target has no source code and will not generate .a or .so. It is only used to carry "usage requirements" such as compile features, macro definitions, include paths, link dependencies, and so on.

add_library Common types:

Type Has source code Generates artifacts Purpose
STATIC Yes .a Static library
SHARED Yes .so Dynamic library
MODULE Yes Plug-in dynamic module
OBJECT Yes .o Collection Reuse object files
INTERFACE No No Transitive configuration

Use this template:

project_options

Save C++ standard requirements.

16.target_compile_features

target_compile_features(project_options INTERFACE cxx_std_20)

作用:声明使用这个 target 的目标至少需要 C++20 标准模式。

What can be filled in:

Notation Explanation
cxx_std_11 At least C++11
cxx_std_14 At least C++14
cxx_std_17 At least C++17
cxx_std_20 At least C++20
cxx_std_23 At least C++23

为什么这里又写了一遍 C++20?

set(CMAKE_CXX_STANDARD 20)

是当前目录及其子目录中后续创建目标的默认值。

target_compile_features(project_options INTERFACE cxx_std_20)

are target-level requirements. Modern CMake more recommends using target-level requirements because they can propagate along with target link relationships.

17.add_library(project_warnings INTERFACE)

add_library(project_warnings INTERFACE)

Create an interface target specifically for saving warning options.

Thus each target can choose whether to link:

target_link_libraries(my_target
  PRIVATE
    project_warnings
)

18.target_compile_options

target_compile_options(project_warnings INTERFACE
  "$<$<COMPILE_LANG_AND_ID:C,GNU,Clang>:-Wall;-Wextra;-Wpedantic>"
  "$<$<COMPILE_LANG_AND_ID:CXX,GNU,Clang>:-Wall;-Wextra;-Wpedantic>"
)

Purpose: Add compile options to the target.

This template option:

Option Effect
-Wall Enable a set of common warnings
-Wextra Enable more warnings
-Wpedantic Remind of non-conforming writing styles

外面的 $<...> 是生成器表达式。第一行只在用 GNU 或 Clang 编译 C 源文件时添加这些参数,第二行对应 C++ 源文件。Windows 的 MinGW-w64 GCC 同样属于 GNU,因此两种平台都能使用这一组警告选项。表达式用引号括起来,让其中用分号分隔的选项作为一个整体交给 CMake 处理。参见 CMake 生成器表达式说明。

Common appendable options:

Options Description
-Wconversion Warn when type conversion may lose information
-Wshadow Warn when variable names are shadowed
-Werror Treat warnings as errors

It is not recommended to add -Werror to the template by default. This is because different compilers and different third-party header files may produce different warnings, and beginners can easily get stuck.

19.PUBLIC、PRIVATE、INTERFACE's intuition

The following will appear extensively in the CMake file:

PUBLIC
PRIVATE
INTERFACE

They control "whether the attribute is passed to the consumer".

Keyword Use it yourself Share with others
PRIVATE Yes No
PUBLIC Yes Yes
INTERFACE No Yes

For example:

target_link_libraries(lib1_src_lib
  PUBLIC
    project_options
  PRIVATE
    project_warnings
)

Meaning:

  1. lib1_src_lib the file itself needs project_options.
  2. link lib1_src_lib targets will also get project_options.
  3. project_warnings is only used when compiling lib1_src_lib itself, and does not force downstream code to enable the same warning.

20.cmake/WindowsRuntime.cmake

这个文件负责安装 MinGW 工具链中找到的常用 GCC 运行库。开发时先保证工具链的 bin/ 位于 PATH 中;验证安装结果时,按模板的安装步骤执行,再整体移动安装目录检查程序能否运行。

日常练习保留这个文件即可。新增第三方库后,还要按依赖自身的安装要求检查运行环境。Linux 继续使用系统运行库和本模板的 RPATH 配置。