CMake 简介
什么是 CMake
CMake(Cross-platform Make)是一个开源的跨平台构建系统生成器。它使用简单的配置文件(CMakeLists.txt)来控制软件编译过程,并生成适用于不同平台的原生构建文件(如 Unix Makefile、Visual Studio 项目文件等)。

为什么使用 CMake
- 跨平台支持:一套配置文件可在 Windows、Linux、macOS 等平台使用
- IDE 集成:支持生成多种 IDE 项目文件
- 现代化设计:支持目标为中心的构建系统
- 强大的依赖管理:内置包查找和依赖管理功能
- 广泛采用:大量开源项目使用 CMake,已经成为 C++ 项目构建的事实标准
安装 CMake
Windows:
CMake官网下载安装包
Linux (Ubuntu/Debian):
1 2
| sudo apt update sudo apt install cmake
|
安装完成后,可通过以下命令验证
快速开始
第一个 CMake 项目
创建项目结构:
1 2 3
| my_project/ ├── CMakeLists.txt └── main.cpp
|
main.cpp:
1 2 3 4 5 6
| #include <iostream>
int main() { std::cout << "Hello, CMake!" << std::endl; return 0; }
|
CMakeLists.txt:
1 2 3 4
| cmake_minimum_required(VERSION 3.15) project(MyProject VERSION 1.0.0 LANGUAGES CXX)
add_executable(myapp main.cpp)
|
构建流程
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| mkdir build cd build
cmake ..
make
MSBuild myapp.sln /p:Configuration=Release
./myapp
> Hello, CMake!
|
基础概念
CMakeLists.txt 基本结构
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| cmake_minimum_required(VERSION 3.15)
project(ProjectName VERSION 1.0.0 DESCRIPTION "Project description" LANGUAGES CXX )
set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON)
add_executable(target_name source1.cpp source2.cpp)
target_link_libraries(target_name PRIVATE some_library)
|
项目(Project)
Project 是 CMake 构建系统的顶层容器,使用 project() 命令定义:
Project 完整语法如下:
1 2 3 4 5 6 7
| project(<PROJECT-NAME> [<language-name>...])
project(<PROJECT-NAME> [VERSION <major>[.<minor>[.<patch>[.<tweak>]]]] [DESCRIPTION <project-description-string>] [HOMEPAGE_URL <url-string>] [LANGUAGES <language-name>...])
|
<PROJECT-NAME> 项目的名称,必须唯一[VERSION <major>[.<minor>[.<patch>[.<tweak>]]]] 项目的版本号,最多包含四个部分[DESCRIPTION <project-description-string>] 项目的简要描述信息[HOMEPAGE_URL <url-string>] 项目主页链接地址[LANGUAGES <language-name>...] 显式声明项目所用的编程语言列表,如 C, CXX
示例:
1 2 3 4 5 6
| project(MyProject VERSION 1.2.3.4 DESCRIPTION "My awesome project" HOMEPAGE_URL "https://example.com" LANGUAGES CXX )
|
项目定义后,CMake 会自动设置以下变量:
PROJECT_NAME: 项目名称PROJECT_VERSION: 项目版本PROJECT_SOURCE_DIR: 项目源码根目录,包含顶级 CMakeLists.txt 文件的目录PROJECT_BINARY_DIR: 项目构建根目录,运行 cmake 命令时所在的目录
目标(Target)
目标是 CMake 的核心概念,代表构建系统的输出产物:
- 可执行文件目标:
add_executable() - 库目标:
add_library() - 自定义目标:
add_custom_target()
现代 CMake 推荐以目标为中心进行配置。
add_executable 完整语法如下:
1 2
| add_executable(<name> [WIN32] [MACOSX_BUNDLE] [EXCLUDE_FROM_ALL] [source1] [source2 ...])
|
<name> 目标的名称,必须唯一[WIN32] (可选)在 Windows 上创建 Win32 GUI 应用程序[MACOSX_BUNDLE](可选)在 macOS 上创建应用程序 bundle[EXCLUDE_FROM_ALL](可选)目标不会被默认构建,需要显式指定才会构建[source1] [source2 ...](可选)源文件列表,如果不带源文件列表,需要后续使用target_sources添加源文件
在 第一个CMake项目 中,我们创建了一个简单的可执行文件 myapp。
1
| add_executable(myapp main.cpp)
|
生成器(Generator)
生成器决定 CMake 生成何种构建文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| cmake --help
> Generators The following generators are available on this platform (* marks default): Green Hills MULTI = Generates Green Hills MULTI files (experimental, work-in-progress). * Unix Makefiles = Generates standard UNIX makefiles. Ninja = Generates build.ninja files. Ninja Multi-Config = Generates build-<Config>.ninja files. Watcom WMake = Generates Watcom WMake makefiles. CodeBlocks - Ninja = Generates CodeBlocks project files. CodeBlocks - Unix Makefiles = Generates CodeBlocks project files. CodeLite - Ninja = Generates CodeLite project files. CodeLite - Unix Makefiles = Generates CodeLite project files. Eclipse CDT4 - Ninja = Generates Eclipse CDT 4.0 project files. Eclipse CDT4 - Unix Makefiles= Generates Eclipse CDT 4.0 project files. Kate - Ninja = Generates Kate project files. Kate - Unix Makefiles = Generates Kate project files. Sublime Text 2 - Ninja = Generates Sublime Text 2 project files. Sublime Text 2 - Unix Makefiles = Generates Sublime Text 2 project files.
|
构建类型(Build Type)
CMake 支持多种构建类型:
- Debug: 包含调试信息,无优化
- Release: 优化,无调试信息
- RelWithDebInfo: 优化 + 调试信息
- MinSizeRel: 最小化大小优化
1 2 3 4
| if(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE Release) endif()
|
注意: 通常不需要在CMake中设置,而是在IDE中配置
CMake 语法基础
命令语法
CMake 命令不区分大小写,但推荐使用小写:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| command(argument1 argument2 ...)
command(TARGET target_name PROPERTY value OPTIONS opt1 opt2 )
|
变量
普通变量
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| set(MY_VAR "value") set(MY_LIST "item1" "item2" "item3")
message("Value: ${MY_VAR}")
unset(MY_VAR)
function(my_function) set(LOCAL_VAR "local") set(PARENT_VAR "parent" PARENT_SCOPE) endfunction()
|
缓存变量
1 2 3 4 5 6 7 8
| set(MY_CACHE_VAR "value" CACHE STRING "Description")
set(ENABLE_FEATURE ON CACHE BOOL "Enable feature")
set(MY_VAR "value" CACHE STRING "Description" FORCE)
|
环境变量
1 2 3 4 5
| message("PATH: $ENV{PATH}")
set(ENV{MY_VAR} "value")
|
CMAKE 内置变量
路径相关变量:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| ${PROJECT_SOURCE_DIR}
${CMAKE_SOURCE_DIR}
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_LIST_DIR}
${CMAKE_CURRENT_LIST_FILE}
${PROJECT_BINARY_DIR}
${CMAKE_BINARY_DIR}
${CMAKE_CURRENT_BINARY_DIR}
${CMAKE_INSTALL_PREFIX}
|
项目信息变量:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| ${PROJECT_NAME}
${PROJECT_VERSION} ${PROJECT_VERSION_MAJOR} ${PROJECT_VERSION_MINOR} ${PROJECT_VERSION_PATCH} ${PROJECT_VERSION_TWEAK}
${PROJECT_DESCRIPTION}
${PROJECT_HOMEPAGE_URL}
|
编译器和系统变量:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| ${CMAKE_CXX_COMPILER}
${CMAKE_C_COMPILER}
${CMAKE_CXX_COMPILER_ID}
${CMAKE_SYSTEM_NAME}
${CMAKE_SYSTEM_VERSION}
${CMAKE_SYSTEM_PROCESSOR}
|
构建配置变量:
1 2 3 4 5 6 7 8 9 10 11
| ${CMAKE_BUILD_TYPE}
${CMAKE_CXX_STANDARD}
${CMAKE_CXX_STANDARD_REQUIRED}
${CMAKE_CXX_EXTENSIONS}
|
输出目录变量:
1 2 3 4 5 6 7 8
| ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}
${CMAKE_LIBRARY_OUTPUT_DIRECTORY}
${CMAKE_ARCHIVE_OUTPUT_DIRECTORY}
|
平台检测变量:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| ${WIN32}
${UNIX}
${APPLE}
${MSVC}
${MINGW}
${CYGWIN}
|
列表(Lists)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| set(MY_LIST "a" "b" "c") set(MY_LIST "a;b;c")
list(APPEND MY_LIST "d" "e")
list(INSERT MY_LIST 0 "first")
list(REMOVE_ITEM MY_LIST "b")
list(LENGTH MY_LIST list_length)
list(GET MY_LIST 0 first_item)
list(FIND MY_LIST "c" index)
list(SORT MY_LIST)
list(REVERSE MY_LIST)
|
字符串操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| set(STR1 "Hello") set(STR2 "World") set(RESULT "${STR1} ${STR2}")
string(REPLACE "old" "new" output_var "${input_string}")
string(REGEX MATCH "pattern" output_var "${input_string}")
string(TOUPPER "${input}" output) string(TOLOWER "${input}" output)
string(LENGTH "${input}" length)
string(SUBSTRING "${input}" 0 5 output)
|
条件语句
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| if(condition) elseif(other_condition) else() endif()
if(DEFINED MY_VAR) if(EXISTS "${file_path}") if(IS_DIRECTORY "${path}") if(TARGET target_name)
if(NOT condition) if(cond1 AND cond2) if(cond1 OR cond2)
if(var EQUAL 5) if(var LESS 10) if(var GREATER 3) if(str STREQUAL "value") if(ver VERSION_GREATER "1.0")
if(WIN32) if(UNIX) if(APPLE) if(MSVC)
|
循环语句
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| foreach(item IN LISTS my_list) message("Item: ${item}") endforeach()
foreach(i RANGE 5) message("i = ${i}") endforeach()
foreach(i RANGE 1 10 2) message("i = ${i}") endforeach()
set(i 0) while(i LESS 5) message("i = ${i}") math(EXPR i "${i} + 1") endwhile()
foreach(item IN LISTS my_list) if(item STREQUAL "skip") continue() endif() if(item STREQUAL "stop") break() endif() message("${item}") endforeach()
|
函数和宏
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
| function(my_function arg1 arg2) message("arg1: ${arg1}") message("arg2: ${arg2}")
set(result "value" PARENT_SCOPE) endfunction()
my_function("hello" "world")
macro(my_macro arg) message("Macro arg: ${arg}") endmacro()
function(print_all) foreach(arg IN LISTS ARGN) message("${arg}") endforeach() endfunction()
print_all("a" "b" "c")
function(create_target) set(options OPTIONAL) set(oneValueArgs NAME TYPE) set(multiValueArgs SOURCES LIBS)
cmake_parse_arguments(ARG "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN} )
message("Name: ${ARG_NAME}") message("Sources: ${ARG_SOURCES}") endfunction()
create_target( NAME myapp TYPE EXECUTABLE SOURCES main.cpp util.cpp LIBS pthread )
|
调试技巧
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| message(STATUS "Project source dir: ${PROJECT_SOURCE_DIR}") message(STATUS "Build type: ${CMAKE_BUILD_TYPE}")
get_cmake_property(_variableNames VARIABLES) foreach(_variableName ${_variableNames}) message(STATUS "${_variableName}=${${_variableName}}") endforeach()
get_target_property(MYAPP_SOURCES myapp SOURCES) message(STATUS "myapp sources: ${MYAPP_SOURCES}")
set(CMAKE_VERBOSE_MAKEFILE ON)
|
跨平台路径处理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| set(MY_PATH "src/utils/logger.cpp") set(FULL_PATH ${PROJECT_SOURCE_DIR}/src/main.cpp)
file(TO_CMAKE_PATH "$ENV{SOME_PATH}" CONVERTED_PATH)
cmake_path(APPEND PROJECT_SOURCE_DIR "src" "main.cpp" OUTPUT_VARIABLE FULL_PATH)
get_filename_component(FILENAME ${FULL_PATH} NAME) get_filename_component(DIR ${FULL_PATH} DIRECTORY) get_filename_component(EXT ${FULL_PATH} EXT) get_filename_component(NAME_WE ${FULL_PATH} NAME_WE)
|
预编译头文件(PCH)加速编译
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| target_precompile_headers(myapp PRIVATE <iostream> <vector> <string> <memory> )
target_precompile_headers(myapp PRIVATE "${PROJECT_SOURCE_DIR}/include/pch.h" )
target_precompile_headers(mylib PUBLIC pch.h) target_precompile_headers(myapp REUSE_FROM mylib)
|
参考资源