注释
行注释
块注释
基础设置
最低版本设置
1
| cmake_minimum_required(VERSION 3.15)
|
说明:
- 指定 CMake 的最低版本要求
- 一般现代项目建议使用
3.15 或更高版本
- 若项目依赖较新的特性,可适当提高版本要求
定义工程名称
1 2 3 4 5
| project(<PROJECT-NAME> [VERSION <major>[.<minor>[.<patch>[.<tweak>]]]] [DESCRIPTION <project-description-string>] [HOMEPAGE_URL <url-string>] [LANGUAGES <language-name>...])
|
示例:
1
| project(MyApp VERSION 1.0.0 LANGUAGES CXX)
|
说明:
VERSION:工程版本
DESCRIPTION:工程描述
HOMEPAGE_URL:项目主页
LANGUAGES:指定使用的语言,如 C、CXX
设置C++标准
全局设置
1 2 3
| set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF)
|
说明:
CMAKE_CXX_STANDARD:设置默认 C++ 标准
CMAKE_CXX_STANDARD_REQUIRED ON:强制要求该标准
CMAKE_CXX_EXTENSIONS OFF:关闭编译器扩展,尽量使用标准 C++
针对单个目标设置(推荐)
1 2
| add_executable(MyApp main.cpp) target_compile_features(MyApp PRIVATE cxx_std_17)
|
说明:
target_compile_features 是现代 CMake 推荐的目标级写法
- 该命令必须在目标创建后调用
- 同样适用于库目标
生成可执行程序
1
| add_executable(<可执行程序名> <源文件>)
|
示例:
1 2 3 4 5
| add_executable(MyApp src/main.cpp src/app.cpp src/util.cpp )
|
说明:
- 目标名通常也是默认输出文件名
- 建议明确列出源文件
- 不建议初学阶段使用通配符自动收集源码
执行与构建
配置工程
说明:
-S .:当前目录是源码目录
-B build:将构建文件放到 build 目录
- 如果
build 目录不存在,CMake 会自动创建
编译工程
说明:
- 不需要进入
build 目录
- CMake 会自动调用底层构建工具(如
make、ninja、MSBuild)
指定 Debug / Release
单配置生成器
适用于:
1 2
| cmake -S . -B build -DCMAKE_BUILD_TYPE=Debug cmake --build build
|
1 2
| cmake -S . -B build -DCMAKE_BUILD_TYPE=Release cmake --build build
|
多配置生成器
适用于:
- Visual Studio
- Xcode
- Ninja Multi-Config
1 2 3
| cmake -S . -B build cmake --build build --config Debug cmake --build build --config Release
|
变量
定义变量
1
| set(VAR [VALUE] [CACHE TYPE DOCSTRING [FORCE]])
|
示例:
1 2
| set(MY_NAME "hello") message(STATUS "${MY_NAME}")
|
说明:
VAR:变量名
VALUE:变量值
CACHE:设置缓存变量,通常可在整个工程中访问
变量的基本特点
- CMake 中变量本质上按字符串处理
- 列表本质上是用分号分隔的字符串
- 变量可以重复赋值,后一次赋值会覆盖前面的值
- 取变量值使用
${VAR}
示例:
1 2 3
| set(MY_VAR hello) set(MY_VAR world) message(STATUS "${MY_VAR}")
|
常用预定义变量
| 变量 |
含义 |
PROJECT_SOURCE_DIR |
当前项目根目录 |
PROJECT_BINARY_DIR |
当前项目构建目录 |
CMAKE_SOURCE_DIR |
顶层源码目录 |
CMAKE_BINARY_DIR |
顶层构建目录 |
CMAKE_CURRENT_SOURCE_DIR |
当前处理的 CMakeLists.txt 所在源码目录 |
CMAKE_CURRENT_BINARY_DIR |
当前处理的 CMakeLists.txt 对应的构建目录 |
PROJECT_NAME |
当前项目名称 |
目标与属性
现代 CMake 更推荐围绕“目标(target)”进行设置,而不是大量使用全局变量。
常见目标包括:
- 可执行程序:
add_executable(...)
- 静态库 / 动态库:
add_library(...)
设置输出路径
可执行文件输出路径
1 2 3
| set_target_properties(MyApp PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin" )
|
按配置设置输出路径
1 2 3 4
| set_target_properties(MyApp PROPERTIES RUNTIME_OUTPUT_DIRECTORY_DEBUG "${CMAKE_BINARY_DIR}/debug/bin" RUNTIME_OUTPUT_DIRECTORY_RELEASE "${CMAKE_BINARY_DIR}/release/bin" )
|
说明:
RUNTIME_OUTPUT_DIRECTORY:可执行文件输出目录
- 推荐输出到构建目录,不建议输出到源码目录
添加头文件目录
1 2 3 4
| target_include_directories(MyApp PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include ${CMAKE_CURRENT_SOURCE_DIR}/other_include )
|
PRIVATE:仅当前目标可见
PUBLIC:当前目标和依赖此目标的目标都可见
INTERFACE:仅依赖此目标的目标可见,当前目标不可见(用于纯头文件库)
添加宏定义
1
| target_compile_definitions(MyApp PRIVATE DEBUG)
|
等价于给当前目标添加:
按配置添加宏定义
1 2 3 4
| target_compile_definitions(MyApp PRIVATE $<$<CONFIG:Debug>:DEBUG;_DEBUG> $<$<CONFIG:Release>:NDEBUG> )
|
添加编译选项
1 2 3 4
| target_compile_options(MyApp PRIVATE -Wall -Wextra )
|
说明:
- 用于设置编译器警告、优化等选项
- 推荐使用目标级命令,而不是全局
add_compile_options
库
生成静态库
1
| add_library(<库名> STATIC <源文件>)
|
示例:
1 2 3
| add_library(MyLib STATIC src/lib.cpp )
|
生成动态库
1
| add_library(<库名> SHARED <源文件>)
|
示例:
1 2 3
| add_library(MyLib SHARED src/lib.cpp )
|
说明:
STATIC:静态库
SHARED:动态库 / 共享库
- 库文件实际命名由平台决定,不同系统格式不同
- 在 CMake 中链接库时通常直接使用目标名即可
指定库输出路径
1 2 3 4 5
| set_target_properties(MyLib PROPERTIES ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib" LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib" RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin" )
|
说明:
ARCHIVE_OUTPUT_DIRECTORY:静态库输出目录
LIBRARY_OUTPUT_DIRECTORY:动态库输出目录(非 Windows)
RUNTIME_OUTPUT_DIRECTORY:可执行文件和 Windows 下 DLL 输出目录
链接库
链接项目内构建的库
1
| target_link_libraries(MyApp PRIVATE Mylib)
|
链接外部库:优先使用 find_package
1 2
| find_package(SomeLib REQUIRED) target_link_libraries(MyApp PRIVATE SomeLib::SomeLib)
|
手动查找库文件
1 2
| find_library(CALC_LIB calc HINTS ${CMAKE_CURRENT_SOURCE_DIR}/lib) target_link_libraries(MyApp PRIVATE ${CALC_LIB})
|
指定库搜索路径(不推荐作为首选方案)
1 2
| target_link_directories(MyApp PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/lib) target_link_libraries(MyApp PRIVATE calc)
|
说明:
find_package
find_library
target_link_directories
target_link_directories 只是告诉链接器去哪里找库,不如直接链接目标或完整路径可靠
动态库和静态库的区别
- 静态库:在链接阶段被打包进可执行文件
- 动态库:程序运行时加载
CMake 中链接语法相同:
1
| target_link_libraries(MyApp PRIVATE MyLib)
|
但使用动态库时,需要确保运行时能找到该库:
- Linux:可通过
LD_LIBRARY_PATH
- macOS:可通过
DYLD_LIBRARY_PATH
- Windows:DLL 通常需要位于可执行文件同目录,或在
PATH 中
第三方库
优先使用 find_package
对于系统已安装或提供 CMake 配置文件的库,优先使用:
1 2
| find_package(SomeLib REQUIRED) target_link_libraries(MyApp PRIVATE SomeLib::SomeLib)
|
这是现代 CMake 最推荐的方式。
使用 find_library 查找库文件
如果没有可用的 find_package,可以手动查找:
1 2
| find_library(MY_LIB some_lib HINTS /path/to/lib) target_link_libraries(MyApp PRIVATE ${MY_LIB})
|
不知道库名时如何排查
方法 1:查官方文档
看库的官方文档是否有类似写法:
1 2
| find_package(xxx REQUIRED) target_link_libraries(myapp PRIVATE xxx::xxx)
|
方法 2:查看安装目录中的 *Config.cmake
常见文件名:
1 2 3
| xxxConfig.cmake xxx-config.cmake XxxConfig.cmake
|
例如:
1 2 3
| /lib/cmake/fmt/fmt-config.cmake /lib/cmake/spdlog/spdlogConfig.cmake /lib/cmake/OpenCV/OpenCVConfig.cmake
|
如果存在,通常表示支持:
Linux 下可用以下命令查找:
1 2
| find /usr /usr/local -name "*Config.cmake" | grep -i fmt find /usr /usr/local -iname "*fmt*config.cmake"
|
方法 3:查看 CMake 报错提示
例如:
1
| find_package(fmt REQUIRED)
|
若报错:
1 2 3 4
| Could not find a package configuration file provided by "fmt" with any of the following names: fmtConfig.cmake fmt-config.cmake
|
通常说明:
find_package(fmt) 这个包名大概率是对的
- CMake 正在查找
fmtConfig.cmake 或 fmt-config.cmake
- 问题多半是没有找到对应路径,而不是包名写错
使用FetchContent获取第三方依赖
对于需要源码构建的第三方库,可以使用 FetchContent。
示例:使用 GoogleTest
1 2 3 4 5 6 7 8 9 10 11
| include(FetchContent)
FetchContent_Declare( googletest GIT_REPOSITORY https://github.com/google/googletest.git GIT_TAG v1.14.0 )
FetchContent_MakeAvailable(googletest)
target_link_libraries(MyTest PRIVATE GTest::gtest_main)
|
说明:
FetchContent 会在配置阶段拉取并引入外部工程
- 适合测试依赖、小型依赖或需要固定版本的场景
线程库
如果程序使用线程库,推荐写法:
1 2 3
| set(THREADS_PREFER_PTHREAD_FLAG ON) find_package(Threads REQUIRED) target_link_libraries(MyApp PRIVATE Threads::Threads)
|
说明:
- 这是跨平台的推荐方式
- 不要手动写死
pthread
日志输出
常见关键字:
STATUS
WARNING
AUTHOR_WARNING
SEND_ERROR
FATAL_ERROR
示例:
1 2
| message(STATUS "Configuring project...") message(WARNING "This is a warning")
|
列表与字符串操作
追加列表
1
| list(APPEND SOURCES src/newfile.cpp src/another.cpp)
|
移除列表元素
1
| list(REMOVE_ITEM SOURCES src/main.cpp)
|
其他常用操作
1 2
| list(LENGTH SOURCES len) list(GET SOURCES 0 first)
|
使用set拼接
1
| set(SOURCES ${SOURCES} src/newfile.cpp)
|
说明:
- 这种方式可用,但通常不如
list(APPEND ...) 清晰
条件判断与选项
if 条件判断
1 2 3 4 5
| if(WIN32) message(STATUS "Windows") elseif(UNIX) message(STATUS "Unix-like") endif()
|
常见条件:
option 选项开关
1
| option(BUILD_TESTS "Build tests" ON)
|
使用示例:
1 2 3
| if(BUILD_TESTS) add_subdirectory(tests) endif()
|
说明:
- 常用于启用或禁用某些模块
- 配置时可通过命令行覆盖:
1
| cmake -S . -B build -DBUILD_TESTS=OFF
|
目录嵌套与作用域
大型项目通常会在多个模块目录中分别放置 CMakeLists.txt,再由顶层统一组织。
添加子目录
1
| add_subdirectory(<source_dir> [binary_dir] [EXCLUDE_FROM_ALL])
|
说明:
source_dir:子目录路径,里面通常包含 CMakeLists.txt
binary_dir:对应的构建目录,一般不需要手动指定
EXCLUDE_FROM_ALL:该子目录中的目标不会默认加入父工程的 ALL 目标中,需要显式构建
示例:
1 2
| add_subdirectory(lib) add_subdirectory(app)
|
变量作用域
在 add_subdirectory() 形成的目录层级中:
- 父目录设置的普通变量,子目录可以读取
- 子目录中设置或修改的普通变量,默认不会影响父目录
- 如需向父目录回传变量,可使用:
1
| set(MY_VAR value PARENT_SCOPE)
|
CACHE 变量通常整个工程可见,但应谨慎使用
- 现代 CMake 更推荐通过目标属性来传递依赖,而不是大量共享变量
安装与测试
安装 install
安装可执行文件
1
| install(TARGETS MyApp DESTINATION bin)
|
安装头文件
1
| install(FILES include/myheader.h DESTINATION include)
|
说明:
install() 用于定义安装规则
- 安装时通常执行:
也可以指定安装目录:
1
| cmake --install build --prefix ./install
|
测试 enable_testing
1 2 3 4
| enable_testing()
add_executable(MyTest test.cpp) add_test(NAME my_test COMMAND MyTest)
|
说明:
enable_testing():启用测试支持
add_test():注册测试
执行测试:
一个最小完整示例
项目结构:
1 2 3 4 5 6
| MyApp/ ├── CMakeLists.txt ├── include/ │ └── hello.h └── src/ └── main.cpp
|
顶层 CMakeLists.txt:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| cmake_minimum_required(VERSION 3.15) project(MyApp VERSION 1.0.0 LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF)
add_executable(MyApp src/main.cpp )
target_include_directories(MyApp PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include )
|
构建命令:
1 2
| cmake -S . -B build cmake --build build
|
若为单配置生成器,构建 Release:
1 2
| cmake -S . -B build -DCMAKE_BUILD_TYPE=Release cmake --build build
|
附:推荐的现代 CMake 编写习惯
- 优先使用目标级命令:
target_include_directories
target_link_libraries
target_compile_definitions
target_compile_options
- 优先使用:
find_package
find_library
尽量少用:
构建产物尽量放在构建目录中,不要污染源码目录
尽量显式列出源文件,避免一开始就依赖通配符
优先通过“目标之间的依赖关系”组织工程,而不是通过大量全局变量共享配置