CMake(全)
CMake 系统学习指南
第一部分:CMake 基础概念
1.1 什么是 CMake?
CMake 是一个跨平台的构建系统生成器。它不直接构建项目,而是生成平台特定的构建文件:
- Linux/Unix: 生成 Makefile
- Windows: 生成 Visual Studio 项目文件
- macOS: 生成 Xcode 项目文件
1.2 为什么使用 CMake?
- 跨平台: 一份 CMakeLists.txt 可以在不同平台生成构建文件
- 依赖管理: 自动查找库、头文件路径
- 现代 C++ 支持: 支持 C++11/14/17/20 标准
- CUDA 支持: 原生支持 CUDA 项目构建
- IDE 集成: CLion、VSCode、Visual Studio 都支持
1.3 CMake 构建流程
CMakeLists.txt → CMake 配置 → 生成构建文件 → 编译链接 → 可执行文件
(源文件) (cmake) (Makefile) (make) (./app)
两个阶段:
- 配置阶段:
cmake ..- 读取 CMakeLists.txt,生成构建文件 - 构建阶段:
make- 编译链接生成目标
第二部分:CMake 基础语法
2.1 最简单的 CMakeLists.txt
# 指定 CMake 最低版本
cmake_minimum_required(VERSION 3.10)
# 项目名称
project(MyProject)
# 生成可执行文件
add_executable(myapp main.cpp)
2.2 常用命令详解
2.2.1 cmake_minimum_required
cmake_minimum_required(VERSION 3.10)
cmake_minimum_required(VERSION 3.10...3.20) # 支持 3.10 到 3.20
作用: 指定运行此 CMakeLists.txt 所需的最低 CMake 版本。
2.2.2 project
project(MyProject) # 最简形式
project(MyProject VERSION 1.0.0) # 指定版本
project(MyProject LANGUAGES CXX) # 指定语言
project(MyProject
VERSION 1.0.0
LANGUAGES CXX CUDA # 支持 C++ 和 CUDA
DESCRIPTION "A CUDA learning project"
)
project() 会设置以下变量:
PROJECT_NAME: 项目名称PROJECT_VERSION: 项目版本PROJECT_SOURCE_DIR: 项目源码目录PROJECT_BINARY_DIR: 项目构建目录
2.2.3 add_executable
# 基本用法
add_executable(myapp main.cpp)
# 多个源文件
add_executable(myapp
main.cpp
utils.cpp
helper.cpp
)
# 使用变量
set(SOURCES main.cpp utils.cpp helper.cpp)
add_executable(myapp ${SOURCES})
2.2.4 add_library
# 静态库 (libmylib.a 或 mylib.lib)
add_library(mylib STATIC lib.cpp)
# 动态库 (libmylib.so 或 mylib.dll)
add_library(mylib SHARED lib.cpp)
# 默认 (通常为静态库)
add_library(mylib lib.cpp)
2.2.5 target_link_libraries
# 链接库到可执行文件
add_executable(myapp main.cpp)
add_library(mylib lib.cpp)
target_link_libraries(myapp mylib)
# 链接系统库
target_link_libraries(myapp pthread)
target_link_libraries(myapp cuda)
第三部分:CMake 变量
3.1 变量定义和使用
# 定义变量
set(MY_VAR "Hello")
set(SOURCES main.cpp utils.cpp)
# 使用变量
message(STATUS "MY_VAR = ${MY_VAR}")
add_executable(myapp ${SOURCES})
# 追加变量
set(SOURCES ${SOURCES} helper.cpp)
list(APPEND SOURCES extra.cpp) # 推荐
3.2 CMake 内置变量
# 项目相关
PROJECT_NAME # 项目名称
PROJECT_SOURCE_DIR # 项目源码目录
PROJECT_BINARY_DIR # 项目构建目录
# 构建类型
CMAKE_BUILD_TYPE # Debug, Release, RelWithDebInfo, MinSizeRel
# 编译器相关
CMAKE_CXX_COMPILER # C++ 编译器路径
CMAKE_CUDA_COMPILER # CUDA 编译器路径
# 标准相关
CMAKE_CXX_STANDARD # C++ 标准版本 (11, 14, 17, 20)
CMAKE_CXX_STANDARD_REQUIRED # 强制使用指定标准 (ON/OFF)
CMAKE_CXX_EXTENSIONS # 启用编译器扩展 (ON/OFF)
# 安装路径
CMAKE_INSTALL_PREFIX # 安装前缀 (/usr/local)
3.3 设置 C++ 标准
# 方法 1: 使用变量
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
# 方法 2: 使用 target (推荐)
add_executable(myapp main.cpp)
set_target_properties(myapp PROPERTIES
CXX_STANDARD 17
CXX_STANDARD_REQUIRED ON
CXX_EXTENSIONS OFF
)
第四部分:构建目标详解
4.1 可执行文件
add_executable(myapp main.cpp)
# 设置输出目录
set_target_properties(myapp PROPERTIES
RUNTIME_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/bin
)
# 设置输出名称
set_target_properties(myapp PROPERTIES
OUTPUT_NAME "my_application"
)
4.2 库文件
# 创建库
add_library(mylib STATIC lib.cpp)
# 设置库输出目录
set_target_properties(mylib PROPERTIES
LIBRARY_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/lib
ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/lib
)
# 创建头文件库 (header-only library)
add_library(mylib INTERFACE)
target_include_directories(mylib INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/include)
4.3 链接库
# 创建库
add_library(mathlib STATIC math.cpp)
# 创建可执行文件并链接库
add_executable(calculator main.cpp)
target_link_libraries(calculator mathlib)
# 链接可见性
target_link_libraries(calculator
PUBLIC mathlib # 传递给依赖此目标的其他目标
PRIVATE threadlib # 仅用于此目标
INTERFACE apilib # 仅用于依赖此目标的其他目标
)
第五部分:条件判断和循环
5.1 条件判断
# if-else-endif
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
message(STATUS "Debug 模式")
add_definitions(-DDEBUG)
else()
message(STATUS "Release 模式")
endif()
# 检查变量
if(ENABLE_CUDA)
enable_language(CUDA)
endif()
# 检查操作系统
if(WIN32)
message(STATUS "Windows 平台")
elseif(UNIX)
message(STATUS "Unix/Linux 平台")
elseif(APPLE)
message(STATUS "macOS 平台")
endif()
# 逻辑运算
if(ENABLE_CUDA AND CUDA_FOUND)
message(STATUS "CUDA 已启用且找到")
endif()
if(USE_OPENMP OR USE_TBB)
message(STATUS "并行库已启用")
endif()
5.2 循环
# foreach 循环
set(SOURCES a.cpp b.cpp c.cpp)
foreach(SRC ${SOURCES})
message(STATUS "源文件: ${SRC}")
endforeach()
# 数字循环
foreach(i RANGE 1 5)
message(STATUS "数字: ${i}")
endforeach()
# while 循环
set(COUNT 0)
while(COUNT LESS 5)
message(STATUS "计数: ${COUNT}")
math(EXPR COUNT "${COUNT} + 1")
endwhile()
第六部分:函数和宏
6.1 函数
# 定义函数
function(add_my_executable NAME)
message(STATUS "创建可执行文件: ${NAME}")
add_executable(${NAME} ${ARGN}) # ARGN 包含额外参数
endfunction()
# 使用函数
add_my_executable(myapp main.cpp utils.cpp)
6.2 宏
# 定义宏
macro(print_var VAR_NAME)
message(STATUS "${VAR_NAME} = ${${VAR_NAME}}")
endmacro()
# 使用宏
set(MY_VALUE 42)
print_var(MY_VALUE) # 输出: MY_VALUE = 42
6.3 函数 vs 宏
区别:
- 函数: 有自己的作用域,内部变量不影响外部
- 宏: 在调用者的作用域中展开,内部变量会影响外部
function(test_func)
set(VAR "function") # 不影响外部
endfunction()
macro(test_macro)
set(VAR "macro") # 会影响外部
endmacro()
set(VAR "original")
test_func()
message(STATUS "After function: ${VAR}") # original
test_macro()
message(STATUS "After macro: ${VAR}") # macro
第七部分:查找包和依赖管理
7.1 find_package
# 查找包
find_package(CUDA REQUIRED)
find_package(OpenCV REQUIRED)
find_package(Boost 1.70 REQUIRED COMPONENTS filesystem system)
# 检查是否找到
if(CUDA_FOUND)
message(STATUS "CUDA 版本: ${CUDA_VERSION}")
message(STATUS "CUDA 编译器: ${CUDA_COMPILER}")
endif()
# 使用找到的包
target_link_libraries(myapp ${CUDA_LIBRARIES})
target_include_directories(myapp ${CUDA_INCLUDE_DIRS})
7.2 find_library 和 find_path
# 查找库文件
find_library(MATH_LIB m) # 查找 libm.so 或 libm.a
if(MATH_LIB)
target_link_libraries(myapp ${MATH_LIB})
endif()
# 查找头文件路径
find_path(MATH_INCLUDE_DIR math.h)
if(MATH_INCLUDE_DIR)
target_include_directories(myapp ${MATH_INCLUDE_DIR})
endif()
7.3 包配置模式
# 使用 Config 模式(推荐)
find_package(CUDA REQUIRED)
# 创建导入目标
add_executable(myapp main.cpp)
target_link_libraries(myapp CUDA::cudart) # 现代 CMake 方式
第八部分:CMake 与 CUDA 集成
8.1 启用 CUDA 支持
cmake_minimum_required(VERSION 3.10)
project(MyCUDAProject LANGUAGES CXX CUDA)
# 或者动态启用
project(MyProject LANGUAGES CXX)
enable_language(CUDA)
8.2 CUDA 源文件编译
# 方法 1: 直接添加 .cu 文件
add_executable(myapp main.cpp kernel.cu)
# 方法 2: 使用变量分离
set(CPP_SOURCES main.cpp utils.cpp)
set(CUDA_SOURCES kernel.cu helper.cu)
add_executable(myapp ${CPP_SOURCES} ${CUDA_SOURCES})
8.3 CUDA 编译选项
# 设置 CUDA 架构
set(CMAKE_CUDA_ARCHITECTURES 75 86) # SM 75 (Turing), SM 86 (Ampere)
# 或者针对特定目标设置
set_target_properties(myapp PROPERTIES
CUDA_ARCHITECTURES "75;86"
)
# 设置 CUDA 标准
set(CMAKE_CUDA_STANDARD 17)
set(CMAKE_CUDA_STANDARD_REQUIRED ON)
# CUDA 编译器标志
set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -O3")
set(CMAKE_CUDA_FLAGS_DEBUG "-G -g") # Debug 模式
set(CMAKE_CUDA_FLAGS_RELEASE "-O3") # Release 模式
8.4 CUDA 库链接
# 链接 CUDA 运行时库
target_link_libraries(myapp CUDA::cudart)
# 链接 CUDA 库
target_link_libraries(myapp CUDA::cublas CUDA::cufft)
# 链接 cuDNN(需要先找到)
find_package(CUDNN REQUIRED)
target_link_libraries(myapp CUDA::cudnn)
8.5 分离 CUDA 和 C++ 代码
# 创建 CUDA 库
add_library(cudakernel STATIC kernel.cu)
target_include_directories(cudakernel PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(cudakernel CUDA::cudart)
# 创建 C++ 可执行文件
add_executable(myapp main.cpp)
target_link_libraries(myapp cudakernel)
第九部分:现代 CMake 最佳实践
9.1 使用 target_* 命令
传统方式(不推荐):
include_directories(/usr/include)
link_directories(/usr/lib)
add_definitions(-DDEBUG)
现代方式(推荐):
target_include_directories(myapp PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include)
target_link_directories(myapp PRIVATE /usr/lib)
target_compile_definitions(myapp PRIVATE DEBUG)
9.2 可见性修饰符
target_include_directories(myapp
PUBLIC # 对当前目标和依赖者都可见
${CMAKE_CURRENT_SOURCE_DIR}/include
PRIVATE # 仅对当前目标可见
${CMAKE_CURRENT_SOURCE_DIR}/src
INTERFACE # 仅对依赖者可见
${CMAKE_CURRENT_SOURCE_DIR}/api
)
9.3 Generator Expressions
# 根据构建类型设置不同选项
target_compile_options(myapp PRIVATE
$<$<CONFIG:Debug>:-g -O0>
$<$<CONFIG:Release>:-O3>
)
# 根据编译器设置选项
target_compile_options(myapp PRIVATE
$<$<CXX_COMPILER_ID:GNU>:-Wall>
$<$<CXX_COMPILER_ID:Clang>:-Weverything>
)
# 平台特定设置
target_compile_definitions(myapp PRIVATE
$<$<PLATFORM_ID:Windows>:WINDOWS_BUILD>
$<$<PLATFORM_ID:Linux>:LINUX_BUILD>
)
9.4 属性设置
# 设置目标属性
set_target_properties(myapp PROPERTIES
CXX_STANDARD 17
CXX_STANDARD_REQUIRED ON
CUDA_STANDARD 17
CUDA_STANDARD_REQUIRED ON
CUDA_ARCHITECTURES "75;86"
OUTPUT_NAME "my_application"
RUNTIME_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/bin
)
# 获取属性
get_target_property(OUTPUT_NAME myapp OUTPUT_NAME)
message(STATUS "输出名称: ${OUTPUT_NAME}")
第十部分:实际项目示例
10.1 你的 CUDA 项目改进版
cmake_minimum_required(VERSION 3.18)
project(HelloWorld
VERSION 1.0.0
LANGUAGES CXX CUDA
DESCRIPTION "CUDA Hello World 示例"
)
# 设置 C++ 和 CUDA 标准
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CUDA_STANDARD 17)
set(CMAKE_CUDA_STANDARD_REQUIRED ON)
# 设置 CUDA 架构(根据你的 GPU 选择)
# SM 75: Turing (RTX 20xx, GTX 16xx)
# SM 80: Ampere (RTX 30xx, A100)
# SM 86: Ampere (RTX 30xx mobile)
# SM 89: Ada Lovelace (RTX 40xx)
set(CMAKE_CUDA_ARCHITECTURES 75 86)
# 编译选项
if(MSVC)
add_compile_options(/W4)
else()
add_compile_options(-Wall -Wextra -Wpedantic)
endif()
# 创建可执行文件
add_executable(hello_world hello_world.cu)
# 链接 CUDA 运行时
target_link_libraries(hello_world CUDA::cudart)
# 设置输出目录
set_target_properties(hello_world PROPERTIES
RUNTIME_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/bin
)
# 打印配置信息
message(STATUS "项目名称: ${PROJECT_NAME}")
message(STATUS "项目版本: ${PROJECT_VERSION}")
message(STATUS "C++ 标准: ${CMAKE_CXX_STANDARD}")
message(STATUS "CUDA 标准: ${CMAKE_CUDA_STANDARD}")
message(STATUS "CUDA 架构: ${CMAKE_CUDA_ARCHITECTURES}")
10.2 多文件 CUDA 项目
cmake_minimum_required(VERSION 3.18)
project(MyCUDAApp LANGUAGES CXX CUDA)
# 标准
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CUDA_STANDARD 17)
set(CMAKE_CUDA_ARCHITECTURES 75 86)
# 源文件
set(CPP_SOURCES
main.cpp
utils.cpp
)
set(CUDA_SOURCES
kernel.cu
helper.cu
)
# 创建 CUDA 库
add_library(cudakernel STATIC ${CUDA_SOURCES})
target_include_directories(cudakernel PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include)
target_link_libraries(cudakernel PUBLIC CUDA::cudart)
# 创建可执行文件
add_executable(myapp ${CPP_SOURCES})
target_link_libraries(myapp PRIVATE cudakernel)
# 输出目录
set_target_properties(myapp PROPERTIES
RUNTIME_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/bin
)
第十一部分:常用命令速查
11.1 文件操作
# 读取文件
file(READ "config.txt" CONFIG_CONTENT)
# 写入文件
file(WRITE "output.txt" "Hello, CMake!")
# 查找文件
file(GLOB SOURCES "src/*.cpp")
file(GLOB_RECURSE SOURCES "src/**/*.cpp") # 递归查找
# 创建目录
file(MAKE_DIRECTORY ${PROJECT_BINARY_DIR}/output)
# 复制文件
file(COPY ${PROJECT_SOURCE_DIR}/data DESTINATION ${PROJECT_BINARY_DIR})
11.2 字符串操作
# 字符串拼接
set(FULL_NAME "${FIRST_NAME} ${LAST_NAME}")
# 字符串替换
string(REPLACE "old" "new" NEW_STRING ${OLD_STRING})
# 字符串长度
string(LENGTH ${MY_STRING} STRING_LENGTH)
# 子字符串
string(SUBSTRING ${MY_STRING} 0 5 SUB_STR)
# 转换为大写/小写
string(TOUPPER ${MY_STRING} UPPER_STRING)
string(TOLOWER ${MY_STRING} LOWER_STRING)
11.3 列表操作
# 创建列表
set(MY_LIST a b c d)
# 追加元素
list(APPEND MY_LIST e f)
# 插入元素
list(INSERT MY_LIST 2 x) # 在索引 2 处插入 x
# 删除元素
list(REMOVE_ITEM MY_LIST b)
list(REMOVE_AT MY_LIST 0) # 删除索引 0 的元素
# 列表长度
list(LENGTH MY_LIST LIST_LENGTH)
# 获取元素
list(GET MY_LIST 0 FIRST_ITEM)
# 连接列表
string(JOIN " " JOINED_STRING ${MY_LIST})
11.4 消息输出
# 不同级别的消息
message(STATUS "普通信息")
message(WARNING "警告信息")
message(SEND_ERROR "错误信息,继续处理")
message(FATAL_ERROR "致命错误,停止处理")
# 调试输出
message(STATUS "变量值: ${MY_VAR}")
message(STATUS "列表: ${MY_LIST}")
第十二部分:构建和安装
12.1 构建类型
# 设置默认构建类型
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Release CACHE STRING "Build type" FORCE)
endif()
# 不同构建类型的编译选项
set(CMAKE_CXX_FLAGS_DEBUG "-g -O0")
set(CMAKE_CXX_FLAGS_RELEASE "-O3 -DNDEBUG")
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O2 -g -DNDEBUG")
set(CMAKE_CXX_FLAGS_MINSIZEREL "-Os -DNDEBUG")
12.2 安装规则
# 安装可执行文件
install(TARGETS myapp
RUNTIME DESTINATION bin
)
# 安装库文件
install(TARGETS mylib
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib
)
# 安装头文件
install(FILES ${PROJECT_SOURCE_DIR}/include/mylib.h
DESTINATION include
)
# 安装整个目录
install(DIRECTORY ${PROJECT_SOURCE_DIR}/include/
DESTINATION include
)
12.3 自定义目标
# 添加自定义命令
add_custom_command(
OUTPUT generated.h
COMMAND ${PYTHON_EXECUTABLE} ${PROJECT_SOURCE_DIR}/generate.py
DEPENDS generate.py
COMMENT "生成头文件"
)
# 添加自定义目标
add_custom_target(generate
COMMAND ${PYTHON_EXECUTABLE} ${PROJECT_SOURCE_DIR}/generate.py
COMMENT "运行生成脚本"
)
# 添加依赖
add_dependencies(myapp generate)
第十三部分:调试和故障排除
13.1 查看变量
# 打印所有缓存变量
get_cmake_property(_variableNames VARIABLES)
foreach(_variableName ${_variableNames})
message(STATUS "${_variableName}=${${_variableName}}")
endforeach()
# 打印特定变量
message(STATUS "CMAKE_CXX_COMPILER = ${CMAKE_CXX_COMPILER}")
message(STATUS "CMAKE_CUDA_COMPILER = ${CMAKE_CUDA_COMPILER}")
13.2 查看目标属性
# 获取目标属性
get_target_property(INCLUDE_DIRS myapp INCLUDE_DIRECTORIES)
message(STATUS "包含目录: ${INCLUDE_DIRS}")
get_target_property(LINK_LIBS myapp LINK_LIBRARIES)
message(STATUS "链接库: ${LINK_LIBS}")
13.3 调试构建过程
# 详细输出
cmake .. -DCMAKE_VERBOSE_MAKEFILE=ON
# 或者使用 make
make VERBOSE=1
# 查看编译命令
ninja -v # 如果使用 Ninja
第十四部分:进阶主题
14.1 CMake 模块
# 包含模块
include(CheckCXXCompilerFlag)
# 检查编译器标志
check_cxx_compiler_flag("-std=c++17" COMPILER_SUPPORTS_CXX17)
if(COMPILER_SUPPORTS_CXX17)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++17")
endif()
# 自定义模块
list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake)
include(MyCustomModule)
14.2 测试支持
enable_testing()
add_executable(test_myapp test.cpp)
target_link_libraries(test_myapp mylib)
add_test(NAME MyTest COMMAND test_myapp)
# 设置测试属性
set_tests_properties(MyTest PROPERTIES
TIMEOUT 10
LABELS "unit"
)
14.3 打包
# 设置 CPack
set(CPACK_PACKAGE_NAME "MyApp")
set(CPACK_PACKAGE_VERSION "1.0.0")
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "My Application")
# 包含 CPack
include(CPack)
第十五部分:学习路径建议
15.1 初级阶段
- ✅ 理解 CMake 基本概念和构建流程
- ✅ 学会编写简单的 CMakeLists.txt
- ✅ 掌握 add_executable、add_library、target_link_libraries
- ✅ 理解变量和基本语法
15.2 中级阶段
- ⬜ 学习 find_package 和依赖管理
- ⬜ 掌握条件判断和循环
- ⬜ 理解现代 CMake 的 target_* 命令
- ⬜ 学会使用 Generator Expressions
15.3 高级阶段
- ⬜ 编写自定义 CMake 模块
- ⬜ 理解 CMake 内部机制
- ⬜ 掌握跨平台构建技巧
- ⬜ 学习 CMake 测试和打包
附录:常用资源
官方文档
- CMake 官方文档: https://cmake.org/documentation/
- CMake 教程: https://cmake.org/cmake/help/latest/guide/tutorial/
学习资源
- Modern CMake: https://cliutils.gitlab.io/modern-cmake/
- CMake 最佳实践: https://github.com/cpp-best-practices/cppbestpractices
CUDA 相关
- CMake CUDA 支持: https://cmake.org/cmake/help/latest/manual/cmake-language.7.html#cuda
- NVIDIA CMake 示例: https://github.com/NVIDIA/cuda-samples
练习建议
- 从简单开始: 先创建一个最简单的 CMake 项目
- 逐步增加复杂度: 添加库、多文件、CUDA 支持
- 实践你的项目: 改进现有的 CUDA 项目 CMake 配置
- 阅读优秀项目: 查看开源项目的 CMakeLists.txt

浙公网安备 33010602011771号