CMake
CMake
概念定义
目标
目标通常指的是需要构建的最终产物,比如可执行文件、库文件等。每个目 标都有一组与之相关联的源文件、依赖关系、编译选项等。
变量与函数
${}
表示一个变量
常用自动定义变量 | 说明 |
---|---|
${PROJECT_NAME} |
代指项目名 |
${PROJECT_BINARY_DIR} |
表示项目的二进制路径,exe文件会在此路径下生成 |
${PROJECT_SOURCE_DIR} |
代指当前项目的源代码目录 注意:在子目录的CMakeList.txt中,其指向的仍是顶级目录的源代码目录 |
${CMAKE_CURRENT_SOURCE_DIR} |
当前CMakeList.txt所在目录 |
常用函数 | 说明 |
---|---|
TIMESTAMP |
获取当前的时间戳,并将其格式化为指定的格式。 |
CMake变量占位符
在头文件中,形如@name@
的为CMake变量占位符,它们将在CMake配置阶段被替换为实际的值。
软件版本号构成
- 主要部分(major):这是版本号中的第一部分,通常表示软件的主要版本或发行版。当主要部分发生变化时,通常意味着软件进行了重大的更新或改进,可能包括新的功能、用户界面更改或底层架构的改进。这种变化可能会对软件的兼容性和用户体验产生显著影响。
- 次要部分(minor):这是版本号中的第二部分,用于表示在主要版本下的次要更新或修订。次要更新通常包括性能改进、小功能添加或错误修复,这些更改通常不会破坏软件的兼容性或用户的主要工作流程。
- 修订号(patch):这是版本号中的第三部分,用于表示对软件的微小修订或错误修复。修订号的变化通常意味着软件中的一些小问题或漏洞已经被修复,而这些修复通常不会对软件的主要功能或用户体验产生显著影响。
作用域
值 | 说明 |
---|---|
PRIVATE | 包含目录仅对当前目标可见,不会传递给依赖该目标的其他目标。 |
INTERFACE | 包含目录对当前目标不可见,但会传递给依赖该目标的其他目标。这通常用于库目标,以导出其公共头文件。( 消费者需要、但生产者不需要) |
PUBLIC | 包含目录对当前目标可见,并且也会传递给依赖该目标的其他目标。这通常用于同时需要包含目录进行编译和导出其头文件的情况。 |
静态库与动态库
静态库在链接阶段会被链接到最终目标中(比如可执行程序)
缺点:同一个静态库如果被不同的程序引用,那么内存中会存在这个静态库函数的多份拷贝。
动态库在链接阶段不会被拷贝最终目标中,程序在运行阶段才会加载这个动态库。
优点:多个程序就算引用了同一个动态库,内存中也只存在一份动态库函数的拷贝。
一、基础
基本用法
1 |
|
控制流语句
if() endif()
1 |
|
foreach() endforeach()
类似Python中的for,foreach()有两种形式:
遍历列表:
1
2
3foreach(i in LISTS list_var)
...
endforeach()遍历范围:
1
2
3foreach(i RANGE start stop [step])
...
endforeach()
二、进阶
set和PROJECT_NAME
${PROJECT_NAME}
代指项目名,${}
表示一个变量
1 |
|
1 |
|
于是上面的两个代码块可以这样写:
1 |
|
项目版本号和配置头文件
project()可以设置项目的版本号,例:
1 |
|
configure_file()
1 |
|
${PROJECT_BINARY_DIR}
表示项目的二进制路径,exe文件会在此路径下生成
target_include_directories()
target_include_directories(目标名 作用域 包含目录)
为目标添加包含目录(使目标能访问目录里的文件,比如头文件)
作用域
:
值 | 说明 |
---|---|
PRIVATE | 包含目录仅对当前目标可见,不会传递给依赖该目标的其他目标。 |
INTERFACE | 包含目录对当前目标不可见,但会传递给依赖该目标的其他目标。这通常用于库目标,以导出其公共头文件。 |
PUBLIC | 包含目录对当前目标可见,并且也会传递给依赖该目标的其他目标。这通常用于同时需要包含目录进行编译和导出其头文件的情况。 |
例:
1 |
|
版本控制
创建一个用于控制版本和保存配置信息的头文件(后文将称之为配置头文件,一般与项目同名),包含以下内容(示例):
1 |
|
配置CMake时,用set(),configure_file()对该头文件重新生成。
后续运行时,可通过包含配置头文件,使用诸如
1 |
|
这样的代码打印版本号
编译时间戳
有时候我们需要知道编译时的时间戳,并在程序运行时打印出来。
string()
1 |
|
在配置信息头文件中添加内容:
1 |
|
构建完后,会变为(示例):
1 |
|
之后打印此值即可
指定C++标准
例:
1 |
|
从 GCC 6.1 开始,当不指定任何版本 C++ 标准时,默认版本是 C++ 14
添加库
基本命令
add_library()
1 |
|
add_subdirectory()
add_subdirectory
命令用于向当前项目添加一个子目录。
target_link_libraries()
target_link_libraries
命令用于为指定的目标(如可执行文件或库)添加需要链接的库。
target_link_libraries(目标 作用域 一个或多个库)
1 |
|
target_include_directories()
target_include_directories
命令用于为指定的目标(如可执行文件或库)添加包含目录。这些包含目录是编译器在查找头文件时应该搜索的路径。
1 |
|
流程示例
用
add_library(MathFuntions mysqrt.cpp)
创建一个名为MathFuntions的库,同时将mysqrt编译为库的一部分。用
add_subdirectory(MathFunctions)
将库所在的目录添加为顶级目标的子目录。用
add_executable()
为顶级目标生成exe用
target_link_libraries()
使顶级目标链接MathFunctions库用
target_include_directories
将 MathFunctions 添加为头文件目录
代码示例如下:
1
2
3
4
5
6
7
8
9
10add_subdirectory(MathFunctions)
add_executable(${PROJECT_NAME} myproject.cpp)
target_link_libraries(${PROJECT_NAME} PUBLIC MathFunctions)
target_include_directories(${PROJECT_NAME} PUBLIC
${PROJECT_BINARY_DIR}
${PROJECT_SOURCE_DIR}/MathFunctions
)
将库设置为可选项
基本命令
option()
option(选项名称 选项描述(文档) 默认值(ON/OFF))
1 |
|
list()
list()用于操作列表变量
具体操作如下:
操作 | 代码 | 说明 |
---|---|---|
创建或追加 | list(APPEND |
将一个或多个元素追加到名为 <list_var> 的列表变量中。如果列表不存在,它将被创建。 |
获取长度 | list(LENGTH |
计算名为 <list_var> 的列表的长度,并将结果存储在 <output_var> 变量中。 |
获取元素 | list(GET |
从名为 <list_var> 的列表中获取一个或多个指定索引处的元素,并将结果存储在 <output_var> 变量中。索引是从 0 开始的。 |
插入元素 | list(INSERT |
在名为 <list_var> 的列表的指定索引处插入一个或多个元素。 |
移除元素 | list(REMOVE_ITEM 或者list(REMOVE_AT |
这两个命令分别用于从名为 <list_var> 的列表中移除指定值或指定索引处的元素。 |
反转列表 | list(REVERSE |
|
排序 | list(SORT |
对名为 <list_var> 的列表进行排序 |
遍历 | 使用foreach() |
流程示例
1、修改CMakeList.txt
1 |
|
2、在顶级目标源文件中添加预命令
1 |
|
还可以添加优先选择逻辑:
1 |
|
3、修改配置头文件,添加定义:
1 |
|
添加库的使用要求
基本命令
target_compile_definitions()
该命令用于为目标添加编译时定义。这些定义在编译时会被传递给编译器,通常用于设置宏或条件编译标志。
target_compile_definitions(
1 |
|
target_compile_options()
为目标设置编译选项。这些选项会直接传递给编译器,允许你指定额外的编译标志,如优化级别、警告等级或调试信息等。
target_compile_options(
1 |
|
使用示例
在库的CMakeList.txt中加入这个:
1 |
|