Wiki
Top-level CMake and common compilation options
Existing English translations were reused. 28 current source blocks remain in Simplified Chinese, without triggering paid translation.
本节先讲两个文件:
- Top-level
CMakeLists.txt 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:
- Specify the minimum CMake version required by the project.
- Tell CMake to use the policy behavior corresponding to that version.
- 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:
- Set the project name.
- Set the project version.
- Declare which languages the project uses.
- 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:
lib1_src_libthe file itself needsproject_options.- link
lib1_src_libtargets will also getproject_options. project_warningsis only used when compilinglib1_src_libitself, and does not force downstream code to enable the same warning.
20.cmake/WindowsRuntime.cmake
这个文件负责安装 MinGW 工具链中找到的常用 GCC 运行库。开发时先保证工具链的 bin/ 位于 PATH 中;验证安装结果时,按模板的安装步骤执行,再整体移动安装目录检查程序能否运行。
日常练习保留这个文件即可。新增第三方库后,还要按依赖自身的安装要求检查运行环境。Linux 继续使用系统运行库和本模板的 RPATH 配置。