cmake笔记
记录一个CMake
速查表。
运行 cmake
:
1 | cmake -B build -DCMAKE_BUILD_TYPE=Release |
常用函数
函数名 | 例子 | 用途 |
---|---|---|
set | set(CMAKE_CXX_STANDARD 11) | 为变量赋值 |
option | option(USE_VULKAN_UTILS "use custom vulkan utilities" ON) | 选项(和set功能相同) |
include | include(Ctest) | 包含cmake文件 |
cmake_minimum_required | cmake_minimum_required(VERSION 3.10) | cmake版本指定 |
project | project(VulkanToy VERSION 1.0) | 项目名称,可以包括版本 |
add_executable | add_executable(VulkanToy main.cpp) | 添加一个要生成可执行文件的目标 |
add_library | add_library(VulkanUtils utils.cpp) | 添加一个要生成库的目标 |
add_subdirectory | add_subdirectory(VulkanUtils) | 添加子目录 |
find_package | find_package(Vulkan REQUIRED) | 查找包 |
aux_source_directory | aux_source_directory(. sources) | 自动搜集有需要的后缀名的文件放入 sources 变量 |
target_include_directories | target_include_directories(test PRIVATE VulkanUtils) | 包含子库 |
target_link_libraries | target_link_libraries(VulkanToy utilTest) | 链接子库目录 |
target_add_definitions | target_add_definitions(VulkanToy PUBLIC MY_MACRO=1) | 添加一个宏定义 |
target_compile_options | target_compile_options(VulkanToy PUBLIC -fopenmp) | 添加编译器命令行选项 |
target_sources | target_sources(VulkanToy PUBLIC test.cpp other.cpp) | 添加要编译的源文件 |
include_directories | include_directories(/opt/cuda/include) | 添加头文件搜索目录 |
link_directories | link_directories(/opt/cuda) | 添加库文件的搜索路径 |
add_definitions | add_definitions(MY_MACRO=1) | 添加一个宏定义 |
add_compile_options | add_compile_options(-fopenmp) | 添加编译器命令行选项 |
configure_file | configure_file(VulkanToyConfig.h.in VulkanToyConfig.h) | 配置文件 |
install | install(TARGETS VulkanToy DESTINATION bin) | 安装 |
enable_testing | enable_testing() | 启动测试功能 |
add_test | add_test(NAME VulkanToyRuns COMMAND VulkanToy) | 添加一个测试 |
set_tests_properties | set_tests_properties(VulkanToyRuns PROPERTIES PASS_REGULAR_EXPRESSION ".*") | 设置测试的属性(比如结果是否满足条件 |
add_custom_command | add_custom_command(TARGET${target_name} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy ${PROJECT_SOURCE_DIR}/vert.spv $TARGET_FILE_DIR:${target_name}(TARGET_FILE_DIR:${target_name})) | 自定义命令 |
部分关键字
关键字 | 例子 | 用途 |
---|---|---|
GLOB | file(GLOB sources CONFIGURE_DEPENDS .cpp .h) | 将文件夹下的对应扩展名的所有文件都放入 sources 中 |
GLOB_RECURSE | file(GLOB_RECURSE sources CONFIGURE_DEPENDS .cpp .h) | 自动包含所有子文件夹下的文件 |
CONFIGURE_DEPENDS | file(GLOB sources CONFIGURE_DEPENDS .cpp .h) | 当添加新文件时,自动更新变量 |
部分说明
指定C++标准
1 | set(CMAKE_CXX_STANDARD 11) |
设置构建的类型
有四种构建的类型:
Debug
调试模式,完全不优化,生成调试信息,方便调试程序Release
发布模式,优化程度最高,性能最佳,但是编译比Debug
慢MinSizeRel
最小体积发布,生成的文件比Release
更小,不完全优化,减少二进制体积RelWithDebInfo
带调试信息发布,生成的文件比Release
更大,因为带有调试的符号信息
可以检查是否设置了构建类型,没有就可以设置成默认的:
1
2
3
4if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Release)
endif()
在代码中可以使用NDEBUG
也就是"NO
DEBUG"来判断是否是非调试的构建类型,比如用 1
2
3
std::cerr << phyDevice.getProperties().deviceName << std::endl;Debug
的时候输出调试信息。 ###
添加子目录
子目录中包含自己的
CMakeLists.txt
。添加子目录后子目录下的
CMakeLists.txt
会被执行,整个项目编译时,这个子目录中的目标也会被编译(比如生成
.lib
)。但是不加其他语句(target_link_libraries
)的话这个目标与当前的目标没有任何联系。反过来,如果只是链接第三方的库不需要用
add_subdirectory
,需要
target_link_libraries
。
1 | add_subdirectory(VulkanUtils) |
包含子库
将某个(子)目录加入当前项目,使得在编译时,指定的目录下的头文件能够被访问到。
第一个参数是目标,第二个是作用域关键字,第三个是目录的标识符。如果要包含项目目录中的子目录,第三个参数可以是
"${PROJECT_SOURCE_DIR}/目录名称"
。但是包含项目目录中的子目录可以有更好的做法:使用
add_library
。
第三个参数可以使用相对路径,相对的当前路径时
CMakeLists.txt
所在的路径。比如
CMakeLists.txt
所在的文件夹中有一个子文件夹
VulkanUtils
,如果 test
目标想包含
VulkanUtils
里的头文件,就可以直接写:
1 | target_include_directories(test PRIVATE VulkanUtils) |
在vs中,这个语句会影响对应目标的属性页的附加包含目录。
链接子库目录
1 | target_link_libraries(VulkanToy PUBLIC "${PROJECT_BINARY_DIR}") |
在vs中,这个语句会影响对应目标的属性页的附加依赖项。
作用域关键字
当存在多个共享库而且存在依赖关系时,作用域关键字可以用于指定依赖关系。
链接的依赖关系
这里以两个库 foo
,bar
,目标
app
为例,两个库有对应名字的头文件和源文件。假设
app
的 CMakeLists.txt
中有以下语句:
1 | target_link_libraries(app bar) |
这里的 PUBLIC
可以换成另外两个关键字,含义如下:
- PRIVATE: bar.h没有包含foo.h,只有bar.cpp包含了foo.h。此时,app没有包含foo.h,因此不能使用foo中定义的符号。换句话说,app只知道bar的存在,完全不知道foo的存在。
- INTERFACE: bar.h中包含了foo.h,但是bar.cpp并没有用到foo定义的符号。此时,app包含了foo.h,可以引用foo的符号。换句话说,bar只是作为一个接口、界面,把foo传递给了app。
- PUBLIC:
等于PRIVATE加INTERFACE,bar.h包含了foo.h,且bar.cpp引用了foo的符号。此时,app包含了foo.h,可以引用foo的符号。
对于
target_link_libraries(app bar)
,使用不同关键字并不会影响编译。
对于编译来说,关键字会影响编译时是否加入某些库(比如
.lib
)。
包含的依赖关系
一般 target_include_directories
和
target_link_libraries
会使用相同的关键字,含义也是一样的。但是
target_link_libraries
可以省略关键字,target_include_directories
不可以。
总结来说,如果有 foo->bar->app
这样的依赖关系,如果
bar.cpp
使用了 foo.hpp
里的符号,且
app.cpp
里使用了 foo.hpp
里的符号,那么就只能用
PUBLIC
,如果只有前者,可以用
PRIVATE
,如果只有后者,可以用
INTERFACE``。记住,INTERFACE
意味着库的使用者需要,但库的设计者不需要。
测试中发现,如果只有后者,bar.hpp
中并不能
#include "foo.hpp"
(应该是因为如果可以,那么
foo.cpp
也可以使用 foo.hpp
的符号,就不满足
INTERFACE
的要求了),只有 app.cpp
可以。
配置文件
编写一个扩展名为 .h.in
的文件(本文例子中是
VulkanToyConfig.h.in
),里面写这样的内容,两个宏的前缀是项目名:
1 |
CMakeLists.txt
中加入下面这行:
1 | configure_file(VulkanToyConfig.h.in VulkanToyConfig.h) |
这行代码会将第一个参数复制到第二个参数这个文件中,这个文件
VulkanToyConfig.h
不存在时,cmake会在运行的所在目录生成一个,比如我是在
Build
目录下。他可以被包含到当前路径下的源文件里,这样就可以使用定义的宏等。
选项
一个例子是可以决定是否包含某些子目录,为真时加入该子目录(作为一个库,该目录下要有自己的
CMakeLists.txt
,会被独立构建)。这个选项会在生成项目时出现,如图:
1 | option(USE_VULKAN_UTILS "use custom vulkan utilities" ON) |
list
可以修改变量中的内容,在这里用来保存需要额外链接的库
在配置文件 .h.in
(本文例子中是
VulkanToyConfig.h.in
)加入下面的内容。
1 |
安装
在文件尾添加:
1 | install(TARGETS VulkanToy DESTINATION bin) |
TARGETS指定安装的目标以及安装的形式,FILES会把项目中指定的文件拷贝到目标位置。如果有子目标的话,子目标里的
CMakeLists.txt
里也需要添加类似的内容,但是
TARGETS
的第三个参数要根据子目标的需求来写(换成
lib
)。
加入安装语句后,使用install时默认在本机安装软件的目录下,比如
C:\Program Files (x86)
。因此有时需要管理员权限。可以用
--prefix
指定安装位置。
1 | cmake --install . --config Debug |
也可以用下面的命令来安装,可以指定debug或者release。但是不能用
--prefix
。
1 | cmake --build . --target install --config Debug |
测试
1 | enable_testing() |
在 Build
目录运行命令 ctest
,如果出现
Test not available without configuration. (Missing "-C <config>"?)
这样的错误,就可以用
-C
指定配置:
1 | cmake .. |
自定义命令
add_custom_command
有两种,一种用于生成输出文件,一种在生成目标文件时自动执行。
参考
【公开课】现代CMake高级教程(持续更新中) 非常推荐观看。 cmake:添加自定义操作 CMake应用:生成器表达式