cmake 是一个跨平台编译工具,它面向各种平台提供适配的编译系统配置文件,进而调用这些编译系统完成编译工作。cmake 进入3.x 版本,指令大量更新,一些老的指令开始被新的指令集替代,并加入了一些更加高效的指令/参数。本文归纳了cmake 3.x 版本的常用指令,方便使用时备查。
关于cmake工具的简单介绍和VS Code cmake Tools 环境配置,可以参考我的另一篇博客。本文参考资料:【公开课】现代CMake高级教程 - Bilibili 小彭老师,CMake Reference Documentation
ps: 本文是大全类文档,适合备查,完整学习
cmake
强烈建议跟随官方tutorial,会有非常好的效果
典型的cmake 项目构建流程如下:
配置阶段 Configure:根据编写的CmakeLists.txt
文件,以及选择的编译系统,生成该系统的构建规则文件。(例如,对make
生成Makefile
,对MSVC
生成sln
(可以在VS中打开))
cmake -B build
build
目录,而不需要事先创建并进入-D
设置缓存变量,格式-D
,如:
CMAKE_C_COMPILTER
, CMAKE_CXX_COMPILER
等CMAKE_INSTALL_PREFIX
CMAKE_BUILD_TYPE
-Dvar:type=value
,可以将option
设定为OFF
,即表示不启用(会覆盖CMakeLists中的默认选项)-G
指定生成器(Generator,即构建系统),可以通过--help
查看支持的列表
-A
指定架构(For MSVC build system)-T
指定工具链,例如使用ClangCL
: -T ClangCL,host=x64
(For MSVC build system)Ninja
作为生成器,效率较高编译阶段 Build:根据生成的构建规则,调用构建系统进行构建,这一步真正输出项目目标(可执行文件、共享库等)
cmake --build build
构建完成后,可以在build
目录下找到输出结果(MSVC并不是直接放在build
下,而是在构建模式对应目录下(Debug
, Release
),这与其他不同)
项目配置变量是控制项目构建,以及包含项目信息的关键变量。这些变量可以在命令行配置(缓存变量),也可以在CMakeLists内部修改。
一般有四种项目构建模式:
-O0 -g
-O3 -DNDEBUG
MinSizeRel
最小体积发布 :生成项目文件小,性能优化不完全 -Os -DNDEBUG
RelWithDebInfo
带调试信息发布 -O2 -g -DNDEBUG
可以通过在CMakeLists中增加默认选项脚本的方法修改默认选项为Release
if (NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Release)
endif()
使用命令project
初始化项目信息
project( [...])
project(
[VERSION [.[.[.]]]]
[DESCRIPTION ]
[HOMEPAGE_URL ]
[LANGUAGES ...])
C CXX
C CXX ASM FORTRAN CUDA OBJC OBJCXX ISPC
VERSION
字段:设置项目版本号,会自动配置相关变量该命令将初始化名为project_name
的项目,并给相关变量赋值:
PROJECT_NAME
: 项目名称CMAKE_PROJECT_NAME
:根项目名称PROJECT_SOURCE_DIR
:项目源码路径,即初始化project的CMakeLists.txt
所在路径PROJECT_BINARY_DIR
:项目输出路径,通常是./build
路径CMAKE_CURRENT_SOURCE_DIR
:当前源码路径CMAKE_CURRENT_BINARY_DIR
:当前输出路径,即当前CMakeLists.txt
所在路径,子模块中指子模块路径一般通过如下指令设置标准(推荐放在project
指令前,会在project语言启用时检测)
set(CMAKE_C_STANDARD 11)
set(CMAKE_C_STANDARD_REQUIRED ON)
set(CMAKE_C_EXTENSIONS OFF)
CMAKE_C_STANDARD_REQUIRED
在启用语言时检查编译器是否支持该标准CMAKE_C_EXTENSIONS
是否启用GNU拓展语言特性(对跨平台有影响)构建目标主要有两种类型:
使用add_executable
命令添加可执行文件目标:
add_executable( [WIN32] [MACOSX_BUNDLE]
[EXCLUDE_FROM_ALL]
[source1] [source2 ...])
name
的可执行文件目标,源文件来自source
${var_name}
,目录的所有文件可以使用 aux_source_directory
添加source
可以省略(>3.11)
,并在后面以target_sources
的形式给出add_library( [STATIC | SHARED | MODULE]
[EXCLUDE_FROM_ALL]
[
STATIC/SHARED
指定生成静态库/动态库,默认静态另外还有对象库object library
add_library( OBJECT [
对象库不输出实际的库文件,而是可以直接被加入到其他构建目标中,如:
add_library(... $ ...)
add_executable(... $ ...)
或者使用target_link_libraries()
链接到对象库(CMAKE >3.12)
注意:动态库不能直接引用静态库,因为动态库开启PIC,而静态库没有
set_property(TARGET target_name PROPERTY POSITION_INDEPENDENT_CODE ON)
另外,Windows平台如果要使用动态库,则需要添加宏
声明前添加
#ifdef _MSC_VER
__declspce(dllimport)
#endif
定义前加
#ifdef _MSC_VER
__declspce(dllexport)
#endif
set_target_properties(target_name PROPERTIES
property_name value
...)
使用set_target_properties
设置编译目标属性
这些属性包括(详细信息参考cmake-properties(7) — CMake 3.25.1 Documentation):
*_OUTPUT_DIRECTORY
使用target_link_libraries()
链接第三方库
target_link_libraries( ... - ... ...)
其中,item可以是:
add_library( IMPORTED)
引入)优化方案(对使用cmake的三方库,限Linux):
find_package(LIBRARY_NAME REQUIRED)
target_link_libraries( [PUBLIC] LIBRARY_NAME::library_target_name)
find_package
会在/usr/lib/cmake
下增加.cmake
配置文件,cmake会自动搜索并链接相应的库。使用target_compile_definitions
来配置编译时宏定义,这些定义可以在C/C++的宏中被使用
target_compile_definitions(target PUBLIC def)
install(TARGETS ... [destination])
以上命令用于安装对象到destination
子命令还可以是:
FILES
安装文件,如头文件等DIRECTORY
目录使用enable_testing
以启用ctest,ctest会自动执行命令,并匹配输出,自动化完成测试
以下示例增加一个ctest,并设置匹配的输出结果
add_test(NAME Usage COMMAND Tutorial)
set_tests_properties(Usage
PROPERTIES PASS_REGULAR_EXPRESSION "Usage:.*number")
如果程序输出:Usage:[any]number
,就算正确通过
还可以将测试集写为函数,增强代码复用性
function (do_test target arg result)
add_test(NAME Comp${arg} COMMAND ${target} ${arg})
set_tests_properties(Comp${arg}
PROPERTIES PASS_REGULAR_EXPRESSION "${arg} is ${result}")
endfunction()
function (do_math_test target)
do_test(${target} 4 2)
do_test(${target} -25 "(-nan|nan|0)")
endfunction()
do_math_test(Tutorial)
使用set
命令来设置变量:
set( ... [PARENT_SCOPE])
"a;b"
(以分号分隔,加引号)等效通常情况下,CMakeLists文件结构如下所示:
cmake_minimum_required(VERSION 3.0)
project(project_name)
...
cmake_minimum_required
指定了该项目生成需要的最小版本project()
指定项目名称,该命令以下部分都是该项目的配置aux_source_directory( )
此命令将添加source_dir
下的所有匹配文件到variable
中。(匹配文件根据项目语言决定)
file
是cmake中的文件操作指令,可以完成复制、创建等一系列工作,也可以用于查找文件:
file(GLOB CONFIGURE_DEPENDS )
CONFIGURE_DEPENDS
可以在文件更新时自动更新cmake*.cpp *.h
等configure_file
可以完成文件配置,按照模板文件生成目标文件
configure_file(Config.h.in ${PROJECT_SOURCE_DIR}/Config.h)
该命令会将模板文件Config.h.in
中内容替换为实际配置内容,并将头文件写入source
目录下的Config.h
中,这样,可以完成对文件内容的修改,如启用选项,编辑#define
常量,或其他宏补全效果。
常用模板格式如下:
#define FOO_STR "@FOO_STR@"
#cmakedefine FOO_ENABLE
该命令会将@
号包含的部分转换为cmake 执行环境中变量,使用cmakedefine
,如果cmake环境中存在变量,则会将其修改为#define FOO_ENABLE
即,如果在CMakeLists中设置
set(FOO_STR "Hello world")
option(FOO_ENABLE True)
则模板文件中的定义将被输出为:
#define FOO_STR "Hello world"
#define FOO_ENABLE
使用message
命令输出字符串
message([] "message text" ...)
其中模式可以为下列等:
STATUS
状态信息,前面带--
WARNING
警告信息,黄色警告FATAL_ERROR
致命错误INFO
默认,前面不带任何,白色,输出到stderr
使用list
命令来构建,操作列表
Reading
list(LENGTH )
list(GET [ ...] )
list(JOIN )
list(SUBLIST )
Search
list(FIND )
Modification
list(APPEND [...])
list(FILTER {INCLUDE | EXCLUDE} REGEX )
list(INSERT [...])
list(POP_BACK [...])
list(POP_FRONT [...])
list(PREPEND [...])
list(REMOVE_ITEM ...)
list(REMOVE_AT ...)
list(REMOVE_DUPLICATES )
list(TRANSFORM [...])
Ordering
list(REVERSE )
list(SORT [...])
详见list — CMake 3.25.1 Documentation,[下一小节](#选项 option)会有应用选项的列表操作
使用option
来生成选项,这些选项**会在ccmake
**或cmake-gui
中被显示
option(VAR_NAME [Description] [default_value])
option
中的默认选项会被命令行中的-D
选项覆盖例如,
option(USE_MYMATH "Enable MyMath Library" True)
在cmake-gui
中,会显示如下,并可以被配置
一般项目情形下,需要使用option
来控制某个子功能是否启动,可以使用以下命令(接上例USE_MYMATH
):
if (USE_MYMATH)
add_subdirectory(MathFunctions)
list(APPEND EXTRA_LIBS MathFunctions)
list(APPEND EXTRA_INCLUDES MathFunctions)
endif()
如果选项中启用USE_MYMATH
,才会进行库引用
使用生成器表达式可以简化指令
$<$:statement>
只有在变量=值时,才会表现为statement
,否则,生成器表达式的值为空
默认变量传播规则:parent->child
如果要child->parent,则增加选项PARENT_SCOPE
在外部引用项目时,需要满足一些特定条件,这些条件称为usage requirements
只要项目开发者编写Usage Requirements,使用者就可以直接通过link完成寻找包和链接功能,而不需要另外的配置(添加include等),是现代cmake的主要构成。
同时,这些引用也是会传递的,不需要进行额外配置(但要保证配置正确)
在配置项目时,一般有三种配置选项PRIVATE|PUBLIC|INTERFACE
,其中,PRIVATE
仅限于项目自身编译时使用,INTERFACE
是Usage Requirements,它要求所有使用该库的项目添加该动作。PUBLIC
则是两者兼有。
例如,
add_library(MathFunctions mysqrt.cxx)
target_include_directories(MathFunctions INTERFACE ${CMAKE_CURRENT_SOURCE_DIR})
以上指令会使所有引用该库(MathFunctions
)的项目自动引用该项目的头文件