1. 项目与环境设置
cmake_minimum_required
cmake_minimum_required 是 CMake 中的一个命令,用于指定项目所需的最低 CMake 版本。它通常位于 CMakeLists.txt 文件的开头部分。
语法
cmake_minimum_required(VERSION
VERSION
示例
cmake_minimum_required(VERSION 3.10)
表示该项目需要 CMake 3.10 或更高版本才能构建。
cmake_minimum_required(VERSION 3.10...3.15)
表示该项目需要 CMake 版本在 3.10 到 3.15 之间。
作用
版本兼容性检查:确保用户使用的 CMake 版本满足项目需求。策略设置:CMake 会根据指定的版本调整其行为(如策略设置),以确保向后兼容性。
注意事项
通常应放在 CMakeLists.txt 文件的最前面,在其他命令之前。如果不指定,CMake 可能会使用默认行为,但可能导致不可预期的结果。
project
project 是 CMake 中的一个命令,用于定义项目的基本信息,包括项目名称、版本、描述以及使用的编程语言等。它是 CMakeLists.txt 文件中的基本指令之一,通常在文件的开头部分使用。
语法
project(
[VERSION
[DESCRIPTION
[HOMEPAGE_URL
[LANGUAGES
参数说明
示例
project(MyProject
VERSION 1.0.0
DESCRIPTION "A simple CMake project"
HOMEPAGE_URL "https://example.com"
LANGUAGES C CXX)
作用
设置项目名称:CMake 会将该名称存储在变量 PROJECT_NAME 中。设置版本信息:版本信息会存储在变量 PROJECT_VERSION、PROJECT_VERSION_MAJOR 等中。启用语言支持:通过 LANGUAGES 参数指定项目使用的编程语言,CMake 会检查相应的编译器是否可用。定义相关变量:如 PROJECT_SOURCE_DIR(项目源码目录)、PROJECT_BINARY_DIR(项目构建目录)等。
注意事项
project 命令通常放在 CMakeLists.txt 文件的开头,但在 cmake_minimum_required 之后。如果未指定 LANGUAGES,CMake 会默认启用 C 和 CXX(C++)。如果项目中不需要某些语言,应明确指定以避免不必要的检查。
set
在CMake中,set命令用于定义或修改变量的值。变量可以存储字符串、列表或路径等信息,并在后续的CMake脚本中使用。set的基本语法如下:
set(
参数说明:
示例:
设置普通变量:
set(MY_VAR "Hello, CMake!") # 定义变量MY_VAR
设置列表变量:
set(SOURCE_FILES main.cpp utils.cpp lib.cpp) # SOURCE_FILES为列表
设置缓存变量(持久化):
set(CMAKE_CXX_STANDARD 14 CACHE STRING "C++ standard version") # 设置C++标准为14
强制覆盖缓存变量:
set(ENABLE_TESTING ON CACHE BOOL "Enable tests" FORCE) # 强制开启测试选项
注意事项:
变量名区分大小写,通常约定全大写(如CMAKE_CXX_STANDARD)。未指定CACHE的变量默认为普通变量,作用域限于当前目录及子目录。通过CACHE定义的变量可在命令行或GUI工具(如ccmake)中修改。
message
message 是 CMake 中的一个命令,用于在构建过程中输出调试信息或错误提示。它可以帮助开发者在 CMake 配置或构建阶段打印相关信息,便于调试和跟踪问题。
语法
message([
参数说明
STATUS:输出一般状态信息(通常以 -- 前缀显示)。WARNING:输出警告信息(黄色高亮)。AUTHOR_WARNING:输出作者警告(类似 WARNING,但可能被忽略)。SEND_ERROR:输出错误信息(红色高亮),但不会终止配置过程。FATAL_ERROR:输出致命错误信息(红色高亮),并终止配置过程。DEPRECATION:输出弃用警告(如果启用了 CMAKE_ERROR_DEPRECATED 或 CMAKE_WARN_DEPRECATED)。VERBOSE、DEBUG、TRACE:输出更详细的调试信息(需配合 CMake 日志级别使用)。
"message text":要输出的消息内容,可以是字符串或变量。
示例
输出普通信息:
message("This is a simple message.")
输出状态信息:
message(STATUS "This is a status message.")
输出警告信息:
message(WARNING "This is a warning message.")
输出错误信息并终止配置:
message(FATAL_ERROR "This is a fatal error message.")
结合变量输出:
set(MY_VAR "Hello, CMake!")
message("The value of MY_VAR is: ${MY_VAR}")
注意事项
如果不指定
2. 文件与目录操作
add_subdirectory
add_subdirectory 是 CMake 中的一个命令,用于将指定的子目录添加到当前项目的构建系统中。这个命令通常用于模块化项目结构,允许将不同的功能模块或组件组织在不同的子目录中。
语法
add_subdirectory(source_dir [binary_dir] [EXCLUDE_FROM_ALL])
参数说明
source_dir
指定包含 CMakeLists.txt 的子目录路径。路径可以是相对路径(相对于当前 CMakeLists.txt 文件)或绝对路径。
binary_dir(可选)
指定子目录的构建输出路径。如果未指定,CMake 默认使用 source_dir 的同名目录(在构建目录中)。
EXCLUDE_FROM_ALL(可选)
如果指定此选项,子目录中的目标(如可执行文件或库)默认不会包含在父目录的 ALL 目标中。这意味着这些目标不会在默认构建时被编译,除非显式指定。
功能
加载子目录的 CMakeLists.txt
CMake 会解析子目录中的 CMakeLists.txt 文件,并将其内容纳入当前项目的构建系统。
作用域隔离
子目录中的变量和选项通常不会影响父目录,除非显式传递(例如通过 set 或 PARENT_SCOPE)。
构建输出管理
通过 binary_dir 可以控制子目录的构建产物存放位置,便于组织复杂的项目结构。
示例
# 添加子目录 src,构建输出到 build/src
add_subdirectory(src)
# 添加子目录 tests,并排除默认构建
add_subdirectory(tests EXCLUDE_FROM_ALL)
注意事项
子目录必须包含有效的 CMakeLists.txt 文件,否则会报错。路径中的空格或特殊字符可能需要转义处理。多次调用 add_subdirectory 添加同一目录会导致重复定义错误。
aux_source_directory
aux_source_directory 是 CMake 中的一个命令,用于自动收集指定目录下的所有源文件。这个命令会扫描指定目录,并将所有符合条件的源文件(如 .c、.cpp 等)的路径存储到一个变量中,方便后续使用。
语法
aux_source_directory(
示例
# 收集当前目录下的所有源文件,并存储到变量 SOURCES 中
aux_source_directory(. SOURCES)
# 使用收集到的源文件编译可执行文件
add_executable(my_app ${SOURCES})
注意事项
文件类型:aux_source_directory 默认会识别常见的源文件扩展名(如 .c、.cpp、.cc 等),但不会识别头文件(如 .h、.hpp)。递归扫描:该命令不会递归扫描子目录,仅扫描指定的目录。手动指定文件:对于复杂的项目,建议手动指定源文件,以避免意外包含不需要的文件。
适用场景
适用于小型项目或快速原型开发,可以简化源文件的收集。不推荐用于大型或复杂的项目,因为可能会包含不需要的文件。
file
file 是 CMake 中的一个命令,用于执行与文件相关的操作。它支持多种子命令,可以用于文件的复制、查找、读写等操作。以下是一些常用的 file 子命令:
1. COPY
用于复制文件或目录到指定位置。
file(COPY
示例:
file(COPY myfile.txt DESTINATION ${CMAKE_CURRENT_BINARY_DIR})
2. GLOB
用于匹配指定模式的文件列表。
file(GLOB
示例:
file(GLOB SOURCES "src/*.cpp")
3. WRITE
用于将内容写入文件。
file(WRITE
示例:
file(WRITE output.txt "Hello, CMake!")
4. READ
用于读取文件内容。
file(READ
示例:
file(READ input.txt CONTENT)
5. REMOVE 和 REMOVE_RECURSE
用于删除文件或目录。
file(REMOVE
file(REMOVE_RECURSE
REMOVE:删除文件。REMOVE_RECURSE:递归删除目录及其内容。
示例:
file(REMOVE oldfile.txt)
file(REMOVE_RECURSE olddir)
6. MAKE_DIRECTORY
用于创建目录。
file(MAKE_DIRECTORY
示例:
file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/mydir)
7. RENAME
用于重命名文件或目录。
file(RENAME
示例:
file(RENAME oldname.txt newname.txt)
8. TOUCH
用于创建空文件或更新文件的时间戳。
file(TOUCH
示例:
file(TOUCH newfile.txt)
9. GENERATE
用于生成文件。
file(GENERATE OUTPUT
OUTPUT:生成的文件路径。CONTENT:文件内容。
示例:
file(GENERATE OUTPUT config.h CONTENT "#define VERSION 1.0")
注意事项
路径可以是绝对路径或相对路径(相对于 CMAKE_CURRENT_SOURCE_DIR)。某些操作(如 GLOB)在配置阶段执行,而不是构建阶段。
3. 构建目标管理
add_executable
add_executable 是 CMake 中的一个命令,用于创建一个可执行文件。它的基本语法如下:
add_executable(
示例
add_executable(my_app main.cpp helper.cpp)
这个命令会生成一个名为 my_app 的可执行文件,使用 main.cpp 和 helper.cpp 作为源文件。
注意事项
源文件路径:如果源文件不在当前 CMakeLists.txt 所在的目录中,需要指定相对或绝对路径。目标名称唯一性:
add_library
add_library 是 CMake 中的一个命令,用于创建库文件(静态库、共享库或模块库)。它的基本语法如下:
add_library(
参数说明
指定库的名称,生成的库文件会根据平台自动添加前缀和后缀(如 lib 和 .a、.so 等)。
库类型(可选)
STATIC:创建静态库(.a 或 .lib)。SHARED:创建共享库(.so 或 .dll)。MODULE:创建模块库(通常用于插件,不链接到其他目标,但可以动态加载)。
如果未指定类型,默认由 BUILD_SHARED_LIBS 变量决定(默认为 STATIC)。
EXCLUDE_FROM_ALL(可选)
如果指定,该库不会在默认构建目标(如 make all)中构建,需要显式构建。
源文件(source1, source2, …)
列出构建库所需的源文件(如 .cpp, .c)。可以是绝对路径或相对路径(相对于 CMakeLists.txt)。
示例
创建静态库
add_library(my_static_lib STATIC src/file1.cpp src/file2.cpp)
创建共享库
add_library(my_shared_lib SHARED src/file1.cpp src/file2.cpp)
不指定类型(依赖 BUILD_SHARED_LIBS)
set(BUILD_SHARED_LIBS ON) # 默认生成 SHARED 库
add_library(my_lib src/file1.cpp)
排除默认构建
add_library(optional_lib EXCLUDE_FROM_ALL STATIC src/optional.cpp)
注意事项
库名称在项目中必须唯一。源文件路径建议使用 target_sources 动态管理,而非硬编码在 add_library 中。
target_link_libraries
target_link_libraries 是 CMake 中的一个命令,用于将库文件链接到指定的目标(target)。这个目标通常是通过 add_executable 或 add_library 命令生成的可执行文件或库。
基本语法
target_link_libraries(
PRIVATE:库仅链接到目标本身,不会传递给依赖该目标的其他目标。PUBLIC:库不仅链接到目标本身,还会传递给依赖该目标的其他目标。INTERFACE:库不会链接到目标本身,但会传递给依赖该目标的其他目标。
示例
链接系统库
add_executable(my_app main.cpp)
target_link_libraries(my_app pthread)
这里将 pthread 库链接到 my_app 可执行文件。
使用可见性关键字
add_library(my_lib STATIC my_lib.cpp)
target_link_libraries(my_lib PRIVATE some_private_lib)
target_link_libraries(my_lib PUBLIC some_public_lib)
some_private_lib 仅用于 my_lib 的构建。some_public_lib 不仅用于 my_lib,还会传递给链接 my_lib 的其他目标。
链接其他 CMake 目标
add_library(helper_lib STATIC helper.cpp)
add_executable(my_app main.cpp)
target_link_libraries(my_app PRIVATE helper_lib)
这里将 helper_lib 静态库链接到 my_app 可执行文件。
注意事项
如果未指定 PRIVATE、PUBLIC 或 INTERFACE,默认行为可能因 CMake 版本而异,建议显式指定。可以一次链接多个库,例如:target_link_libraries(my_app PUBLIC lib1 lib2)
target_link_libraries 是 CMake 中管理依赖关系的重要命令,确保构建时正确链接所需的库文件。
target_include_directories
target_include_directories 是 CMake 中的一个命令,用于为特定的目标(如可执行文件或库)指定头文件的搜索路径。
语法
target_include_directories(
[
参数说明
PRIVATE:仅用于构建目标本身。PUBLIC:用于构建目标本身和依赖该目标的其他目标。INTERFACE:仅用于依赖该目标的其他目标。
示例
add_executable(my_app main.cpp)
target_include_directories(my_app PRIVATE include)
注意事项
路径可以是绝对路径或相对路径(相对于当前 CMakeLists.txt 文件)。多次调用 target_include_directories 会追加目录,而不是覆盖。使用 PUBLIC 或 INTERFACE 时,依赖该目标的其他目标会自动继承这些包含目录。
target_compile_options
target_compile_options 是 CMake 中的一个命令,用于为特定的目标(如可执行文件或库)添加编译选项。这些选项可以是编译器特定的标志,例如警告选项、优化选项等。
语法
target_compile_options(
[
PRIVATE:仅用于当前目标的编译。PUBLIC:用于当前目标的编译,并且会传递给依赖该目标的其他目标。INTERFACE:仅传递给依赖该目标的其他目标,不用于当前目标的编译。
items1..., items2...:要添加的编译选项列表。
示例
add_executable(my_app main.cpp)
# 添加私有编译选项(仅用于 my_app)
target_compile_options(my_app PRIVATE -Wall -Wextra)
# 添加公共编译选项(用于 my_app 并传递给依赖它的目标)
target_compile_options(my_lib PUBLIC -O2)
注意事项
选项的格式取决于所使用的编译器(如 GCC、Clang、MSVC)。使用 PRIVATE、PUBLIC 或 INTERFACE 时需根据需求选择合适的可见性。可以通过 BEFORE 控制选项的插入顺序,这在某些情况下可能影响编译行为。
target_compile_definitions
target_compile_definitions 是 CMake 中的一个命令,用于向特定的目标(如可执行文件或库)添加预处理器定义。这些定义会在编译时传递给编译器,通常用于条件编译或配置代码的不同行为。
语法
target_compile_definitions(
[
PRIVATE:仅适用于当前目标的编译。PUBLIC:适用于当前目标的编译,并且会传递给依赖该目标的其他目标。INTERFACE:仅传递给依赖该目标的其他目标,不用于当前目标的编译。
[items...]:预处理器定义的列表,可以是 -D 开头的定义(如 -DDEBUG),或直接写定义(如 DEBUG=1)。
示例
添加私有定义(仅当前目标使用):
target_compile_definitions(my_target PRIVATE DEBUG)
等效于在编译时传递 -DDEBUG。
添加公有定义(当前目标和依赖目标使用):
target_compile_definitions(my_target PUBLIC USE_FEATURE_X)
添加接口定义(仅依赖目标使用):
target_compile_definitions(my_target INTERFACE ENABLE_LOGGING)
带值的定义:
target_compile_definitions(my_target PRIVATE "VERSION=1.0")
等效于 -DVERSION=1.0。
注意事项
定义可以是简单的标识符(如 DEBUG)或带值的定义(如 VERSION=1.0)。如果定义中包含空格或特殊字符,建议用引号包裹。该命令仅适用于通过 add_executable() 或 add_library() 创建的目标。
4. 依赖管理
find_package
find_package 是 CMake 中的一个命令,用于查找并导入外部库(如 Boost、OpenCV 等)。它的主要功能是定位系统上已安装的第三方库,并设置相关变量,以便在 CMake 项目中使用这些库。
基本语法
find_package(
[REQUIRED] [[COMPONENTS] [components...]]
[OPTIONAL_COMPONENTS components...]
[NO_POLICY_SCOPE])
参数说明
工作原理
查找包:
CMake 会搜索
CMAKE_PREFIX_PATH 指定的路径。系统默认路径(如 /usr/local、/usr)。
设置变量:
成功找到包后,CMake 会设置以下变量:
使用包:
通过 target_link_libraries 将库链接到目标。
示例
# 查找 OpenCV,要求最低版本 3.0,且必须找到
find_package(OpenCV 3.0 REQUIRED)
if(OpenCV_FOUND)
# 添加 OpenCV 头文件路径
include_directories(${OpenCV_INCLUDE_DIRS})
# 将 OpenCV 库链接到目标
target_link_libraries(my_target ${OpenCV_LIBRARIES})
endif()
注意事项
如果包未找到且未指定 REQUIRED,CMake 不会报错,但
find_library
find_library 是 CMake 提供的一个命令,用于在系统中查找指定的库文件。它会搜索常见的库文件路径,并返回找到的库文件的完整路径。这个命令通常用于定位第三方库的位置。
基本语法:
find_library( name [path1 path2 ...])
:用于存储找到的库文件路径的变量名。name:要查找的库的名称(不包含前缀 lib 和后缀,如 .so 或 .a)。[path1 path2 ...]:可选参数,指定额外的搜索路径。
示例:
find_library(LOG_LIB log)
如果找到 liblog.so 或 liblog.a,路径会存储在变量 LOG_LIB 中。
常见选项:
NO_DEFAULT_PATH:禁止搜索默认路径。PATHS:指定额外的搜索路径。
find_path
find_path 是 CMake 提供的一个命令,用于在系统中查找指定的头文件路径。它会搜索常见的头文件路径,并返回找到的头文件所在的目录路径。这个命令通常用于定位第三方库的头文件位置。
基本语法:
find_path( name [path1 path2 ...])
:用于存储找到的头文件目录路径的变量名。name:要查找的头文件名(如 stdio.h)。[path1 path2 ...]:可选参数,指定额外的搜索路径。
示例:
find_path(ZLIB_INCLUDE_DIR zlib.h)
如果找到 zlib.h,其所在目录路径会存储在变量 ZLIB_INCLUDE_DIR 中。
常见选项:
NO_DEFAULT_PATH:禁止搜索默认路径。PATHS:指定额外的搜索路径。
使用场景
find_library 和 find_path 通常结合使用,用于定位第三方库的头文件和库文件路径。例如:
find_path(OPENSSL_INCLUDE_DIR openssl/ssl.h)
find_library(OPENSSL_LIBRARY ssl)
if (OPENSSL_INCLUDE_DIR AND OPENSSL_LIBRARY)
include_directories(${OPENSSL_INCLUDE_DIR})
target_link_libraries(my_target ${OPENSSL_LIBRARY})
endif()
include_directories
include_directories 是 CMake 中的一个命令,用于向构建系统添加头文件的搜索路径。这个命令会全局地影响当前 CMakeLists.txt 及其所有子目录中的所有目标(如可执行文件和库)。
基本语法
include_directories([AFTER|BEFORE] [SYSTEM] dir1 [dir2 ...])
参数说明
AFTER|BEFORE:可选参数,控制新路径是追加到现有路径之后(AFTER,默认行为)还是插入到现有路径之前(BEFORE)。SYSTEM:可选参数,将目录标记为系统头文件目录(编译器可能会抑制这些目录中的警告)。dir1 [dir2 ...]:要添加的头文件搜索路径(可以是绝对路径或相对路径)。
特点
全局性:会影响当前目录及所有子目录中的所有目标。不推荐使用:现代 CMake 更推荐使用 target_include_directories(),因为它可以精确控制每个目标的头文件搜索路径,避免污染全局作用域。路径顺序:默认新路径追加到现有路径列表末尾,但可以通过 BEFORE 调整。
示例
include_directories(${PROJECT_SOURCE_DIR}/include)
注意事项
如果路径中包含空格,需要用引号包裹(如 "path/with spaces")。相对路径会被解释为相对于当前 CMakeLists.txt 所在目录。
link_directories
link_directories 是 CMake 中的一个命令,用于在构建过程中指定链接器搜索库文件的目录路径。它会全局添加库文件搜索路径,影响当前目录及所有子目录中的目标。
基本语法
link_directories(directory1 directory2 ...)
主要特点
全局作用:设置的路径会影响当前 CMakeLists.txt 及其所有子目录中的目标。链接器搜索路径:这些目录会被添加到链接器的库文件搜索路径中(如 -L 选项在 GCC 中)。不推荐使用:CMake 官方文档建议优先使用 target_link_directories() 或直接指定库的完整路径。
使用示例
link_directories(/path/to/libs /another/path)
add_executable(my_app main.cpp)
target_link_libraries(my_app my_library) # 链接器会在上述路径中查找 my_library
注意事项
现代 CMake 推荐使用 target_link_directories() 替代,因为它可以精确控制每个目标的链接路径。直接使用绝对路径指定库文件通常是最可靠的做法。过度使用可能导致构建系统难以维护,特别是大型项目中。
5. 属性与配置
set_property
set_property 是 CMake 中的一个命令,用于设置目标(targets)、源文件(source files)、目录(directories)或其他 CMake 实体的属性。属性可以用于控制构建过程中的各种行为,例如编译选项、链接选项、依赖关系等。
基本语法
set_property(
PROPERTY
参数说明
GLOBAL:全局属性。DIRECTORY:目录属性。TARGET:目标属性(如可执行文件或库)。SOURCE:源文件属性。TEST:测试属性。CACHE:缓存变量属性。
PROPERTY
[ …]:属性的值,可以是单个值或多个值(取决于属性类型)。
示例
设置目标属性:
add_executable(my_app main.cpp)
set_property(TARGET my_app PROPERTY CXX_STANDARD 11)
这里将 my_app 目标的 C++ 标准设置为 C++11。
设置源文件属性:
set_property(SOURCE main.cpp PROPERTY COMPILE_DEFINITIONS "DEBUG=1")
这里为 main.cpp 源文件设置了一个编译定义 DEBUG=1。
设置全局属性:
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
这里设置了一个全局属性 USE_FOLDERS,用于启用 IDE 中的文件夹组织功能。
注意事项
属性的名称和值取决于具体的 CMake 版本和生成器(如 Makefile、Visual Studio 等)。某些属性是只读的,不能通过 set_property 修改。可以使用 get_property 命令获取属性的当前值。
set_property 是 CMake 中非常灵活的命令,可以用于精细控制构建过程的各个方面。
set_target_properties
set_target_properties 是 CMake 中用于设置目标(target)属性的命令。目标可以是可执行文件(add_executable 创建)、静态库(add_library 创建,类型为 STATIC)或动态库(add_library 创建,类型为 SHARED)。
语法
set_target_properties(target1 target2 ...
PROPERTIES prop1 value1
prop2 value2 ...)
参数说明
target1 target2 …:需要设置属性的目标名称。PROPERTIES:关键字,表示后面是属性名和属性值的列表。prop1 value1, prop2 value2 …:要设置的属性名和对应的值。
常用属性
OUTPUT_NAME:指定目标输出文件的名称(不包含扩展名)。
set_target_properties(my_lib PROPERTIES OUTPUT_NAME "renamed_lib")
生成的文件可能是 renamed_lib.a(静态库)或 renamed_lib.so(动态库)。
VERSION:设置目标的版本号(通常用于动态库)。
set_target_properties(my_lib PROPERTIES VERSION "1.2.3")
生成的文件可能是 my_lib.so.1.2.3(Linux)或 my_lib.1.2.3.dylib(macOS)。
SOVERSION:设置目标的符号版本号(通常用于动态库的兼容性)。
set_target_properties(my_lib PROPERTIES SOVERSION "1")
生成的文件可能是 my_lib.so.1(符号链接到 my_lib.so.1.2.3)。
CXX_STANDARD:设置目标的 C++ 标准(如 C++11、C++14 等)。
set_target_properties(my_target PROPERTIES CXX_STANDARD 11)
COMPILE_FLAGS:设置目标的编译选项。
set_target_properties(my_target PROPERTIES COMPILE_FLAGS "-Wall -Wextra")
示例
add_library(my_shared_lib SHARED src.cpp)
set_target_properties(my_shared_lib
PROPERTIES
OUTPUT_NAME "custom_name"
VERSION "1.0.0"
SOVERSION "1")
生成的文件可能是:
custom_name.so.1.0.0(实际库文件)custom_name.so.1(符号链接到 custom_name.so.1.0.0)custom_name.so(符号链接到 custom_name.so.1)
option
在 CMake 中,option 是一个命令,用于定义一个可以由用户在配置阶段(cmake 命令运行时)切换的布尔选项。它的语法如下:
option(
示例
option(BUILD_TESTS "Build the test suite" ON)
定义了一个名为 BUILD_TESTS 的选项,默认值为 ON。用户可以通过命令行覆盖默认值:cmake -DBUILD_TESTS=OFF ..
用途
控制项目的可选功能(如测试、文档生成)。在 if() 语句中检查选项状态:if(BUILD_TESTS)
add_subdirectory(tests)
endif()
注意
option 仅支持布尔值(ON/OFF)。与 set(... CACHE BOOL "...") 类似,但 option 更简洁。
6. 安装与导出
install
install 是 CMake 中的一个命令,用于定义项目安装规则。它指定了在构建完成后,如何将生成的文件(如可执行文件、库、头文件等)安装到目标系统中。
基本语法
install(TARGETS
[EXPORT
[RUNTIME|LIBRARY|ARCHIVE|FRAMEWORK|BUNDLE|PRIVATE_HEADER|PUBLIC_HEADER|RESOURCE]
DESTINATION
[PERMISSIONS
[CONFIGURATIONS [Debug|Release|...]]
[COMPONENT
[OPTIONAL] [NAMELINK_COMPONENT
[NAMELINK_ONLY|NAMELINK_SKIP]
[...])
install(FILES
DESTINATION
[PERMISSIONS
[CONFIGURATIONS [Debug|Release|...]]
[COMPONENT
[RENAME
install(DIRECTORY
DESTINATION
[FILE_PERMISSIONS
[DIRECTORY_PERMISSIONS
[USE_SOURCE_PERMISSIONS] [OPTIONAL]
[CONFIGURATIONS [Debug|Release|...]]
[COMPONENT
[FILES_MATCHING]
[[PATTERN
[EXCLUDE] [PERMISSIONS
主要用途
安装目标文件(如可执行文件、库):
install(TARGETS my_executable
RUNTIME DESTINATION bin)
TARGETS 指定要安装的目标(如可执行文件或库)。RUNTIME 表示可执行文件,LIBRARY 表示共享库,ARCHIVE 表示静态库。DESTINATION 指定安装目录(如 bin、lib、include)。
安装文件(如头文件、配置文件):
install(FILES my_header.h
DESTINATION include)
FILES 指定要安装的单个文件。DESTINATION 指定目标目录。
安装目录(递归安装目录内容):
install(DIRECTORY include/
DESTINATION include)
DIRECTORY 指定要安装的目录。可以使用 PATTERN 或 REGEX 过滤文件。
常用选项
PERMISSIONS:设置文件权限(如 OWNER_READ、GROUP_WRITE)。CONFIGURATIONS:指定仅在特定构建配置(如 Debug 或 Release)下安装。COMPONENT:将安装内容分组到组件中(用于分步安装)。
示例
# 安装可执行文件到 bin 目录
install(TARGETS my_app
RUNTIME DESTINATION bin)
# 安装静态库到 lib 目录
install(TARGETS my_lib
ARCHIVE DESTINATION lib)
# 安装头文件到 include 目录
install(FILES my_header.h
DESTINATION include)
# 安装整个目录(递归)
install(DIRECTORY docs/
DESTINATION share/doc)
export
export 是 CMake 中的一个命令,用于将当前项目的配置导出,以便其他项目可以方便地引用和使用。它通常用于库项目的构建,使得其他项目可以通过 find_package() 命令找到并使用该库。
基本语法
export(EXPORT
[NAMESPACE
[FILE
[CXX_MODULES_DIRECTORY
参数说明
EXPORT
指定要导出的目标集合的名称,这个名称通常是通过 install(TARGETS ... EXPORT ...) 命令定义的。
NAMESPACE
为导出的目标添加命名空间前缀,避免命名冲突。例如,如果命名空间是 MyLib::,那么目标 mylib 会被导出为 MyLib::mylib。
FILE
指定导出的配置文件名称。如果不指定,默认会生成一个
CXX_MODULES_DIRECTORY
(可选)指定 C++ 模块文件的导出目录(CMake 3.28+ 支持)。
使用场景
导出库目标供其他项目使用
在库项目中,通过 export 命令生成配置文件,其他项目可以通过 find_package() 直接引用该库。
与 install(EXPORT) 结合使用
通常在库的安装脚本中,export 会和 install(EXPORT) 一起使用,确保库的安装和导出配置同时完成。
示例
# 定义一个库目标
add_library(mylib STATIC mylib.cpp)
# 安装目标并指定导出名称
install(TARGETS mylib
EXPORT MyLibTargets
ARCHIVE DESTINATION lib)
# 导出目标配置到 MyLibTargets.cmake 文件
export(EXPORT MyLibTargets
FILE "${CMAKE_CURRENT_BINARY_DIR}/MyLibTargets.cmake"
NAMESPACE MyLib::)
其他项目可以通过以下方式引用:
find_package(MyLib REQUIRED)
target_link_libraries(myapp PRIVATE MyLib::mylib)
注意事项
export 通常用于开发阶段的临时导出,而 install(EXPORT) 用于正式安装时的导出。导出的文件需要与项目的版本和配置(如 Debug/Release)匹配。
configure_file
configure_file 是 CMake 中的一个命令,用于生成配置文件。它通常用于将模板文件中的变量替换为 CMake 中的值,从而生成最终的配置文件。
基本语法
configure_file(
[COPYONLY] [ESCAPE_QUOTES] [@ONLY]
[NEWLINE_STYLE [UNIX|DOS|WIN32|LF|CRLF]])
参数说明
: 输入的模板文件路径(通常是 .in 文件)。
使用示例
假设有一个模板文件 config.h.in,内容如下:
#define VERSION "@PROJECT_VERSION@"
#define INSTALL_PATH "@CMAKE_INSTALL_PREFIX@"
在 CMakeLists.txt 中使用 configure_file:
project(MyProject VERSION 1.0.0)
configure_file(config.h.in config.h)
生成的 config.h 文件内容为:
#define VERSION "1.0.0"
#define INSTALL_PATH "/usr/local"
常见用途
生成版本信息文件:将项目版本号写入头文件。配置安装路径:根据 CMAKE_INSTALL_PREFIX 生成路径配置文件。平台相关配置:根据不同的编译环境生成不同的配置。
注意事项
输入文件中的变量必须已在 CMake 中定义,否则会被替换为空字符串。使用 @ONLY 可以避免与脚本语言(如 shell)的变量冲突。
7. 测试与文档
enable_testing
enable_testing() 是 CMake 中的一个命令,用于在当前目录及其子目录中启用测试功能。它通常与 add_test() 命令一起使用,用于定义和运行项目的测试用例。
功能
启用 CMake 的测试功能,使得后续可以使用 add_test() 命令添加测试用例。必须在调用 add_test() 之前调用 enable_testing(),否则 add_test() 不会生效。
语法
enable_testing()
使用示例
# 启用测试功能
enable_testing()
# 添加一个测试用例
add_test(NAME my_test COMMAND my_test_executable)
注意事项
enable_testing() 只需要在 CMakeLists.txt 文件中调用一次,通常在项目的根目录或测试目录中调用。如果没有调用 enable_testing(),即使定义了 add_test(),也不会生成任何测试目标。启用测试后,可以通过 ctest 命令运行测试。
add_test
add_test 是 CMake 中的一个命令,用于向项目添加测试用例。它通常与 CTest(CMake 的测试工具)结合使用,用于定义和运行项目的测试。
基本语法
add_test(NAME
[CONFIGURATIONS
[WORKING_DIRECTORY
[COMMAND_EXPAND_LISTS])
参数说明
NAME:指定测试的名称,用于标识测试用例。COMMAND:指定运行测试的可执行文件及其参数。CONFIGURATIONS(可选):指定测试仅在特定的构建配置(如 Debug、Release)下运行。WORKING_DIRECTORY(可选):设置测试运行的工作目录。COMMAND_EXPAND_LISTS(可选):如果命令参数中包含生成器表达式($<...>),则展开列表。
示例
add_executable(MyTest test.cpp)
add_test(NAME MyTest COMMAND MyTest)
注意事项
add_test 通常与 enable_testing() 一起使用,后者用于启用测试功能。测试用例需要在 CMakeLists.txt 的适当位置定义,通常在项目构建完成后运行。使用 ctest 命令可以运行所有定义的测试用例。
运行测试
在构建目录下执行:
ctest
add_test 是 CMake 测试框架的基础,用于集成单元测试或其他验证步骤到构建流程中。
add_custom_target
add_custom_target 是 CMake 中的一个命令,用于在构建系统中创建一个自定义目标。自定义目标通常用于执行一些不需要编译的操作,例如生成文档、运行测试、打包发布等。
基本语法
add_custom_target(Name [ALL] [command1 [args1...]]
[COMMAND command2 [args2...] ...]
[DEPENDS depend depend depend ... ]
[BYPRODUCTS [files...]]
[WORKING_DIRECTORY dir]
[COMMENT comment]
[VERBATIM] [SOURCES src1 [src2...]])
参数说明
Name: 自定义目标的名称。ALL: 如果指定了 ALL,则该目标会被默认构建(即在执行 make 或 cmake --build 时会自动构建)。COMMAND: 指定要执行的命令。可以指定多个命令,它们会按顺序执行。DEPENDS: 指定该目标依赖的其他目标或文件。如果依赖项发生变化,该目标会重新构建。BYPRODUCTS: 指定该目标生成的副产品文件。这些文件可能会被其他目标依赖。WORKING_DIRECTORY: 指定命令执行的工作目录。COMMENT: 在执行目标时显示的注释信息。VERBATIM: 确保命令参数不会被转义或修改。SOURCES: 指定与该目标关联的源文件(通常用于 IDE 中的显示)。
示例
add_custom_target(docs
COMMAND doxygen Doxyfile
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
COMMENT "Generating API documentation with Doxygen"
)
在这个示例中,创建了一个名为 docs 的自定义目标,用于运行 Doxygen 生成文档。执行 make docs 时会触发该目标。
注意事项
自定义目标不会自动构建,除非指定了 ALL 或显式调用(如 make docs)。如果目标依赖其他文件或目标(通过 DEPENDS),则这些依赖项会被检查是否需要重新构建。自定义目标通常用于非编译任务,例如生成文档、运行测试或打包。
8. 条件逻辑与循环
if/else/endif
在 CMake 中,if/else/endif 用于条件判断,可以根据不同的条件执行不同的 CMake 命令或设置不同的变量。以下是基本语法和常见用法:
基本语法
if(condition)
# 条件为真时执行的命令
else()
# 条件为假时执行的命令
endif()
常见条件类型
变量检查
检查变量是否为真:if(VARIABLE_NAME)
检查变量是否等于某个值:if(VARIABLE_NAME STREQUAL "value")
逻辑运算
与运算(AND):if(condition1 AND condition2)
或运算(OR):if(condition1 OR condition2)
非运算(NOT):if(NOT condition)
文件或路径检查
检查文件是否存在:if(EXISTS "path/to/file")
检查是否为目录:if(IS_DIRECTORY "path/to/dir")
平台检查
检查操作系统类型:if(WIN32) # Windows
if(UNIX) # Unix-like (Linux, macOS)
if(APPLE) # macOS
示例
set(MY_VAR "ON")
if(MY_VAR STREQUAL "ON")
message(STATUS "MY_VAR is ON")
else()
message(STATUS "MY_VAR is OFF")
endif()
if(WIN32)
message(STATUS "Running on Windows")
elseif(APPLE)
message(STATUS "Running on macOS")
else()
message(STATUS "Running on Linux or other Unix-like system")
endif()
注意事项
if 和 endif 必须成对出现。条件表达式中的变量名不需要 ${} 符号(直接使用变量名)。字符串比较时区分大小写,建议使用 STREQUAL 或 STRCASEEQUAL(不区分大小写)。
foreach
foreach 是 CMake 中的一个控制流命令,用于遍历列表中的元素,并对每个元素执行相同的操作。它通常用于批量处理多个文件或目标,例如编译多个源文件或链接多个库。
基本语法
foreach(loop_var item1 item2 ... itemN)
# 循环体,可以使用 ${loop_var} 引用当前元素
endforeach()
参数说明
loop_var:循环变量,每次迭代时会赋值为当前列表元素。item1 item2 … itemN:要遍历的列表元素,可以是变量或直接列出的值。
示例
遍历直接列出的值
以下示例打印列表中的每个元素:
foreach(name Alice Bob Charlie)
message("Hello, ${name}!")
endforeach()
输出:
Hello, Alice!
Hello, Bob!
Hello, Charlie!
遍历变量中的列表
使用 set 定义列表变量,并通过 foreach 遍历:
set(SOURCE_FILES main.cpp utils.cpp helper.cpp)
foreach(file ${SOURCE_FILES})
message("Processing file: ${file}")
endforeach()
结合范围生成列表
使用 RANGE 关键字生成数字序列并遍历:
foreach(i RANGE 1 3)
message("Index: ${i}")
endforeach()
输出:
Index: 1
Index: 2
Index: 3
高级用法
遍历目录中的文件
结合 file(GLOB) 获取文件列表并处理:
file(GLOB SOURCES "src/*.cpp")
foreach(src ${SOURCES})
message("Found source: ${src}")
endforeach()
嵌套循环
支持多层嵌套遍历:
foreach(dir include src)
foreach(file ${dir}/*.h ${dir}/*.cpp)
message("File: ${file}")
endforeach()
endforeach()
注意事项
循环变量(如 loop_var)的作用域仅限于 foreach 和 endforeach 之间。若需提前终止循环,可使用 break() 或 continue() 控制流程。
function/macro
在 CMake 中,function 和 macro 用于定义可重用的代码块,类似于其他编程语言中的函数或宏。它们的主要区别在于作用域和参数传递方式。
function
使用 function(name [arg1 [arg2 ...]]) 定义函数。函数有自己的作用域,变量在函数内部定义不会影响外部作用域。参数按值传递,函数内部对参数的修改不会影响外部变量。使用 return() 可以提前退出函数。
示例:
function(my_function arg1 arg2)
message("arg1: ${arg1}, arg2: ${arg2}")
set(arg1 "new_value") # 修改不会影响外部
endfunction()
my_function("hello" "world")
macro
使用 macro(name [arg1 [arg2 ...]]) 定义宏。宏没有独立作用域,相当于直接将代码插入调用位置。参数按名称传递(类似文本替换),宏内部对参数的修改会影响外部变量。不能使用 return() 退出宏。
示例:
macro(my_macro arg1 arg2)
message("arg1: ${arg1}, arg2: ${arg2}")
set(arg1 "new_value") # 修改会影响外部
endmacro()
my_macro("hello" "world")
选择建议
优先使用 function,除非需要宏的文本替换特性。宏适合简单代码替换,函数适合复杂逻辑。
9. 高级特性
include
include 是 CMake 中的一个命令,用于在当前的 CMake 脚本中包含其他 CMake 脚本文件。它通常用于引入外部的工具链文件、模块或其他 CMake 脚本,以便复用代码或配置。
基本语法
include(
示例
包含一个工具链文件:
include(toolchain.cmake)
包含一个模块(CMake 会在模块路径中查找):
include(FindPython)
使用 OPTIONAL 和 RESULT_VARIABLE:
include(optional_file.cmake OPTIONAL RESULT_VARIABLE included_file)
if (NOT included_file)
message(WARNING "Optional file not found!")
endif()
注意事项
include 不会引入新的变量作用域,被包含文件中的变量会影响当前作用域。如果文件路径包含空格或特殊字符,需要用引号括起来。