通过CMake编译文件
创建一个CMakeLists.txt,并在文件中加入以下代码:
cmake_minimum_required(VERSION 3.0) //构建项目所需的最低CMake版本。
project(SortMake) //指定当前项目的名称
aux_source_directory(src SRC_SUB)//src对应目录里搜索源文件,保存在文件列表SRC_SUB里
aux_source_directory(. SRC_CUR) //在当前目录进行同样操作
add_executable(sort ${SRC_SUB} ${SRC_CUR})//用什么源文件生成可执行程序,此处生成sort
include_directories(include) //头文件的位置 头文件目录与目录之间加空格可以添加多个目录
CMake命令行工具
配置
配置(configure)是指根据CMakeLists.txt声明的构建目标(build target)和依赖关系(dependencies),针对特定的底层构建工具生成构建系统(buildsystem)(由一系列构建文件组成),所使用的底层构建工具叫做生成器(generator)。
配置命令的用法如下:
cmake [<options>] -S <path-to-source> -B <path-to-build>
常用选项:
-S <path-to-source>
:指定源代码目录(source directory)。
-B <path-to-build>
:指定构建目录(build/binary directory),用于存放底层构建工具的构建文件(例如Makefile)和构建目标的输出(例如库文件和可执行文件)。
-D <var>=<value>
:指定变量的值。可以省略中间的空格:-D<var>=<value>
-G <generator-name>
:指定构建系统生成器。
-A <platform-name>
:指定平台名称(如果生成器支持)。
其中,如果-S
和-B
仅指定了二者之一,则另一个默认为当前工作目录。
当构建目标或依赖关系发生变化(即CMakeLists.txt文件发生变化)时,需要重新配置。
注:在实际项目中,通常将构建目录与源代码目录区分开,从而可以方便地从Git中排除。
CMake在当前平台上支持的生成器和默认生成器可通过cmake --help
命令查看。
例如,在Linux系统上可以使用Unix Makefiles生成器:
cmake -G "Unix Makefiles" -S . -B cmake-build
在Windows系统上可以使用Visual Studio生成器(需要安装对应版本的Visual Studio):
cmake -G "Visual Studio 17 2022" -A x64 -B cmake-build
构建
配置完成后,就可以根据构建系统生成构建目标。
构建命令的用法如下:
cmake --build <dir> [<options>]
构建目标将被输出到构建目录中对应的子目录下。如果只有源代码发生变化,CMakeLists.txt文件没有变化,则只需重新构建,不需要重新配置。
常用选项:
--build <dir>
:指定项目构建目录。
-j <jobs>, --parallel <jobs>
:指定构建使用的最大并发进程数。
-t <name>, --target <name>
:仅构建指定的目标及其上游依赖。可以指定多个目标,用空格分隔。如果未指定则构建所有目标。目标clean用于清除构建输出。
--config <cfg>
:对于多配置的构建工具指定使用的配置,例如Debug或Release。
--clean-first
:首先清理构建输出(即构建目标clean
)。如果要只清理构建输出,使用--target clean
。
安装
构建完成后,可以将指定的构建目录中的库文件和可执行文件安装到指定位置,使用install()命令指定安装规则。
安装命令的用法如下:
cmake --install <dir> [<options>]
常用选项:
--install <dir>
:指定项目构建目录。
--prefix <dir>
:覆盖默认的安装目录前缀CMAKE_INSTALL_PREFIX
运行脚本
CMake脚本文件由CMake命令组成,后缀通常为.cmake(本质上和CMakeLists.txt没有区别)。用法:
cmake [-D <var>=<value>]... -P <cmake-script-file> [<args>...]
后面的参数将被传递给脚本,可通过变量CMAKE_ARGC
和CMAKE_ARGV<n>
访问。
注:CMAKE_ARGV0
是cmake可执行程序,CMAKE_ARGV1
是-P
,CMAKE_ARGV2
是脚本文件名,CMAKE_ARGV3
及之后才是传递给脚本的参数。
运行命令行工具
CMake提供了一些命令行工具,包括文件和目录操作、访问环境变量等,可以避免针对不同的操作系统编写不同的脚本命令。用法:
cmake -E <command> [<options>]
直接运行cmake -E
将列出所有命令。
常用命令:
cat <files>...
:拼接文件并打印到标准输出。
chdir <dir> <cmd> [<args>...]
:切换当前工作目录并运行命令。
compare_files [--ignore-eol] <file1> <file2>
:比较两个文件,如果文件相同则返回0,否则返回1。--ignore-eol
表示忽略换行符的差异(LF/CRLF)。
copy <file>... <dst>, copy -t <dst> <file>...
:将文件拷贝到指定目录下,不支持通配符。
copy_directory <dir>... <dst>
:将目录拷贝到指定目录下。
echo [<args>...]
:打印参数。
make_directory <dir>...
:创建目录,如果需要则创建父目录。
rename <oldname> <newname>
:重命名文件或目录。
rm [-r] <file|dir>...
:删除文件或目录。'r
选项用于递归删除目录及其子目录。
CMake命令
脚本命令
指定项目要求的最低CMake版本。
cmake_minimum_required(VERSION <min>)
设置CMake变量的值。
set(<variable> <value>...)
可以通过${var}
的形式引用变量。如果指定了多个值,则变量的实际值是分号分隔的列表。例如:
set(srcs a.c b.c c.c) # sets "srcs" to "a.c;b.c;c.c"
定义一个布尔型选项,可以在配置命令中使用-D选项指定值为ON或OFF。
option(<variable> "<help_text>" [value])
如果未指定初始值则默认为OFF
。
输出日志消息。
message("message text" ...)
从指定的文件或模块加载并运行CMake命令。
include(<file|module>)
条件语句,当条件为真时执行一组命令。
if(<condition>)
<commands>
elseif(<condition>) # optional block, can be repeated
<commands>
else() # optional block
<commands>
endif()
例如:
if(WIN32)
set(output_file NUL)
else()
set(output_file /dev/null)
endif()
循环语句,当条件为真时重复执行一组命令。
while(<condition>)
<commands>
endwhile()
循环语句,对列表中的每个值执行一组命令。
foreach(<loop_var> <items>)
<commands>
endforeach()
该命令有几种变体:
(1)遍历整数
foreach(<loop_var> RANGE <stop>)
foreach(<loop_var> RANGE <start> <stop> [<step>])
遍历0 ~ stop
或start ~ stop
之间的整数,包含上界。其中,start、stop和step
必须是非负整数,且stop
大于等于start,step
默认为1。
例如:
foreach(i RANGE 1 3)
add_executable(prog${i} prog${i}.cpp)
endforeach()
等价于
add_executable(prog1 prog1.cpp)
add_executable(prog2 prog2.cpp)
add_executable(prog3 prog3.cpp)
(2)遍历列表
foreach(<loop_var> IN ITEMS <items>)
其中items
是分号分隔的列表。
例如:
foreach(i IN ITEMS foo;bar;baz)
add_executable(${i} ${i}.cpp)
endforeach()
等价于
add_executable(foo foo.cpp)
add_executable(bar bar.cpp)
add_executable(baz baz.cpp)
跳出foreach()或while()循环。
break()
继续下一次foreach()或while()循环。
continue()
定义函数。
function(<name> [<arg1> ...])
<commands>
endfunction()
该命令定义了一个名为name
的函数,可以接受参数,在函数体中可以用${arg1}
引用参数arg1
。函数体中的命令只有在函数被调用时才会执行。
例如:
function(add_gui_executable name source)
add_executable(${name} ${source})
target_link_libraries(${name} GUI)
endfunction()
add_gui_executable(foo foo.cpp)
add_gui_executable(bar bar.cpp)
等价于
add_executable(foo foo.cpp)
target_link_libraries(foo GUI)
add_executable(bar bar.cpp)
target_link_libraries(bar GUI)
在函数体中,除了${arg1}
等形式参数,还可以使用以下变量:
ARGC
:实际参数的个数
ARGV0
、ARGV1
、ARGV2
等:各实际参数的值
ARGV
:所有参数的列表
ARGN
:最后一个期望的参数之后所有参数的列表
这些变量可用于创建带有可选参数的函数。例如,上面定义的函数add_gui_executable()
只能接受单个源文件参数source
。要接受多个源文件可修改为:
function(add_gui_executable name)
add_executable(${name} ${GUI_TYPE} ${ARGN})
target_link_libraries(${name} GUI)
endfunction()
add_gui_executable(foo foo.cpp bar.cpp)
注:function()
命令仅支持位置参数(positional parameter)
,使用cmake_parse_arguments()
命令可以实现关键字参数(keyword parameter)
。
解析函数参数。
cmake_parse_arguments(<prefix> <option_keywords> <one_value_keywords> <multi_value_keywords> <args>...)
其中,option_keywords
指定所有的选项关键字(即没有值的关键字参数)
one_value_keywords
指定所有的单值关键字
multi_value_keywords
指定所有的多值关键字
<args>...
是要被处理的参数。解析结果将被保存到各关键字对应的变量中,命名格式为<prefix>_<keyword>
。
例如:
function(my_install)
set(option_keywords OPTIONAL FAST)
set(one_value_keywords DESTINATION RENAME)
set(multi_value_keywords TARGETS CONFIGURATIONS)
cmake_parse_arguments(MY_INSTALL "${option_keywords}" "${one_value_keywords}" "${multi_value_keywords}" ${ARGN})
# ...
endfunction()
如果像这样调用my_install()
:
my_install(TARGETS foo bar DESTINATION bin OPTIONAL blub CONFIGURATIONS)
则在函数体中调用cmake_parse_arguments()
后将定义以下变量:
MY_INSTALL_OPTIONAL = TRUE
MY_INSTALL_FAST = FALSE # was not used in call to my_install
MY_INSTALL_DESTINATION = "bin"
MY_INSTALL_RENAME <UNDEFINED> # was not used
MY_INSTALL_TARGETS = "foo;bar"
MY_INSTALL_CONFIGURATIONS <UNDEFINED> # was not used
MY_INSTALL_UNPARSED_ARGUMENTS = "blub" # nothing expected after "OPTIONAL"
MY_INSTALL_KEYWORDS_MISSING_VALUES = "CONFIGURATIONS"
执行一个或多个子进程。
execute_process(
COMMAND <cmd1> [<args>]
[COMMAND <cmd2> [<aegs>]]...
[WORKING_DIRECTORY <directory>]
[RESULT_VARIABLE <variable>]
[OUTPUT_VARIABLE <variable>]
[ERROR_VARIABLE <variable>]
[INPUT_FILE <file>]
[OUTPUT_FILE <file>]
[ERROR_FILE <file>]
[COMMAND_ERROR_IS_FATAL <ANY|LAST>])
命令是以管道的方式并发执行的,每个命令的标准输出通过管道连接到下一个命令的标准输入。
execute_process()
是在CMake 配置时运行指定的命令,使用add_custom_command()
创建在构建时运行的自定义命令。
选项:
COMMAND
:子进程命令行。重定向运算符(例如>)被当作普通参数,使用INPUT*
、OUTPUT*
和ERROR*
选项重定向stdin、stdout和stderr。
WORKING_DIRECTORY
:执行命令的工作目录。
RESULT_VARIABLE
:指定的变量将被设置为最后一个子进程的结果(返回码或错误信息)。
OUTPUT_VARIABLE
, ERROR_VARIABLE
:指定的变量将被分别设置为标准输出和标准错误的内容。
INPUT_FILE
:将第一个子进程的标准输入重定向到指定的文件。
OUTPUT_FILE
:将最后一个子进程的标准输出重定向到指定的文件。
ERROR_FILE
:将所有子进程的标准错误重定向到指定的文件。
COMMAND_ERROR_IS_FATAL
:ANY
表示任何一个命令失败则算失败,LAST
表示只有最后一个命令失败才算失败。
项目命令
设置项目名称。
project(<name>)
该命令将设置以下变量:
PROJECT_NAME
:项目名称
PROJECT_SOURCE_DIR
:项目源代码目录
PROJECT_BINARY_DIR
:项目构建目录
将指定的子目录加入构建。
add_subdirectory(<dir>)
如果dir
是相对路径,则相对于当前目录。CMake执行该命令时将立即处理子目录中的CMakeLists.txt文件。
添加可执行程序构建目标。
add_executable(<name> <source>...)
可执行文件名为<name>
(Linux)或<name>.exe
(Windows)。
添加库构建目标。
add_library(<name> [STATIC|SHARED] <source>...)
可以指定库文件的类型:
STATIC
:静态链接库(默认),库文件名为lib<name>.a
(Linux)或<name>.lib
(Windows)
SHARED
:动态链接库,库文件名为lib<name>.so
(Linux)或<name>.dll
(Windows)
为给定的目标添加包含目录(相当于GCC编译器的-I选项)。
target_include_directories(<target> <PUBLIC|INTERFACE|PRIVATE> <dir>...)
其中,PUBLIC
、INTERFACE
和PRIVATE
关键字用于指定该选项的作用域:
PUBLIC
:对该目标及其下游依赖均生效INTERFACE
:仅对该目标的下游依赖生效PRIVATE
:仅对该目标生效
相对路径将被解释为相对于当前源代码目录。
例如:
add_library(foo foo.cpp)
target_include_directories(foo INTERFACE ${CMAKE_CURRENT_SOURCE_DIR})
定义了一个函数库foo,并指定foo的下游依赖要将当前源代码目录添加到包含目录,从而可以直接包含当前目录下的头文件。
为给定的目标添加库目录(相当于GCC编译器的-L
选项)。
target_link_directories(<target> <PUBLIC|INTERFACE|PRIVATE> <dir>...)
注:一般不需要使用该命令,直接使用target_link_libraries()
即可。
为给定的目标添加依赖库,即上游依赖(相当于GCC编译器的-l
选项)。构建可执行文件时,依赖库将参与链接。
target_link_libraries(<target> <PUBLIC|INTERFACE|PRIVATE> <item>...)
target_link_libraries(<target> <item>...)
其中,第二种形式等价于PUBLIC
,即为给定的目标及其下游依赖均添加依赖库,从而依赖关系具有传递性。
每个<item>
可以是:
库目标名称,由add_library()
创建
库文件完整路径
库文件名称(例如foo
变成-lfoo
或foo.lib
)
链接选项,以-
开头,但-l
和-framework
除外。
例如:
add_library(foo foo.cpp)
add_library(bar bar.cpp)
add_library(baz baz.cpp)
target_link_libraries(bar foo)
target_link_libraries(baz INTERFACE foo)
定义了三个库foo、bar和baz,bar及其下游依赖均依赖foo,baz的下游依赖均依赖foo,但baz本身不依赖foo。
为给定的目标添加编译选项。
target_compile_options(<target> <PUBLIC|INTERFACE|PRIVATE> <item>...)
为给定的目标添加宏定义(相当于GCC编译器的-D选项)。
target_compile_definitions(<target> <PUBLIC|INTERFACE|PRIVATE> <item>...)
其中<item>
的格式为name=definition
或name
。例如:
target_compile_definitions(foo PUBLIC FOO)
target_compile_definitions(foo PUBLIC FOO=bar)
为给定的目标添加链接选项。
target_link_options(<target> <PUBLIC|INTERFACE|PRIVATE> <item>...)
为当前目录及子目录下的所有目标添加包含目录。
include_directories(<dir>...)
注:优先使用target_include_directories()
。
为当前目录及子目录下的所有目标添加库目录。
link_directories(<dir>...)
注:优先使用target_link_directories()
。
为当前目录及子目录下的所有目标添加依赖库。
link_libraries(<item>...)
注:优先使用target_link_libraries()。
为当前目录及子目录下的所有目标添加编译选项。
add_compile_options(…)
1
注:优先使用target_compile_options()
。
add_compile_definitions
为当前目录及子目录下的所有目标添加宏定义。
add_compile_definitions(<definition>...)
注:优先使用target_compile_definitions()
。
add_link_options
为当前目录及子目录下的所有目标添加链接选项。
add_link_options(<option>...)
注:优先使用target_link_options()
。
add_custom_command
添加自定义构建规则。
add_custom_command(
OUTPUT output...
COMMAND command [ARGS] [args...]
DEPENDS depends...
[WORKING_DIRECTORY dir]
[VERBATIM])
其中,depends
可以是构建目标或文件名
VERBATIM
选项保证命令的参数被正确转义。
如果command
是一个可执行文件目标,将会被自动替换为构建生成的可执行文件路径。
例如:
add_custom_command(
OUTPUT out.c
COMMAND someTool -i ${CMAKE_CURRENT_SOURCE_DIR}/in.txt
-o out.c
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/in.txt
VERBATIM)
add_library(myLib out.c)
命令参数可以包含重定向运算符(例如>
)。
添加一个没有输出的自定义目标。
add_custom_target(
name
[COMMAND command [args...] ...]
[DEPENDS depends... ]
[WORKING_DIRECTORY dir]
[VERBATIM]
[SOURCES src1 [src2...]])
指定安装规则。
install(TARGETS targets... DESTINATION <dir>)
install(FILES files... DESTINATION <dir>)
第一种形式用于安装构建目标(库文件或可执行文件),dir
可以是绝对路径或相对路径,相对路径将被解释为相对于CMAKE_INSTALL_PREFIX
变量的值。
第二种形式用于安装文件,如果文件名是相对路径,则相对于当前源代码目录。
例如:
add_executable(foo foo.cpp)
add_library(bar bar.cpp)
install(TARGETS foo DESTINATION bin)
install(TARGETS bar DESTINATION lib)
install(FILES bar.h DESTINATION include)
在执行安装命令cmake --install
时分别将可执行文件foo、库文件libbar.a和头文件bar.h安装到CMAKE_INSTALL_PREFIX
下的bin、lib和include目录。
5.3 生成器表达式
cmake-generator-expressions(7)
CMake内置变量
CMake提供了很多内置变量,可以通过set()
命令或-D
选项指定。下面是一些常用的变量:
路径相关变量
CMAKE_COMMAND
:cmake命令的完整路径CMAKE_GENERATOR
:构建项目使用的生成器CMAKE_SOURCE_DIR
:顶层源代码目录CMAKE_BINARY_DIR
:顶层构建目录CMAKE_CURRENT_SOURCE_DIR
:当前源代码目录CMAKE_CURRENT_BINARY_DIR
:当前构建目录PROJECT_NAME
:最近调用project()
命令的项目名称PROJECT_SOURCE_DIR
:最近调用project()
命令的项目源代码目录PROJECT_BINARY_DIR
:最近调用project()
命令的项目构建目录
系统相关变量
LINUX
:如果目标系统是Linux则设置为TRUE
WIN32
:如果目标系统是Windows则设置为TRUE
APPLE
:如果目标系统是macOS则设置为TRUE
语言相关变量
CMAKE_C_STANDARD
:默认C标准版本,可选的值为90、99、11、17、23等CMAKE_CXX_STANDARD
:默认C++标准版本,可选的值为98、11、14、17、20、23、26等CMAKE_<LANG>_COMPILER
:指定语言的编译器完整路径,<LANG>
可以是C、CXX等CMAKE_<LANG>_FLAGS
:指定语言的编译选项
构建/安装相关变量
CMAKE_BUILD_TYPE
:指定单配置生成器(例如Makefile、Ninja等)的构建类型,例如Debug
、Release
等。
CMAKE_INSTALL_PREFIX
:install()
使用的安装目录。在UNIX上默认为 “/usr/local”。
在Windows上默认为 “C:\Program Files/${PROJECT_NAME}”。
测试
CMake通过CTest模块提供了测试支持。
首先在项目根目录下的CMakeLists.txt中调用enable_testing()
命令,之后可以在任意的CMakeLists.txt中通过add_test()命令添加测试。
添加测试
add_test()
命令的用法如下:
add_test(
NAME <name>
COMMAND <command> [<arg>...]
[WORKING_DIRECTORY <dir>])
其中,command
指定测试命令,如果是一个可执行文件目标,将会被自动替换为构建生成的可执行文件路径。如果命令的返回码为0则认为测试通过,否则测试失败。
注:由于CTest并不是在shell中执行测试命令,因此无法使用标准输入/输出重定向,command
中的<
和>
将被当作普通参数。如果需要重定向测试命令的标准输入/输出,有两种方法:
使用bash -c
,例如add_test(NAME my_test COMMAND sh -c "foo < in.txt > out.txt")
,但这种方法不是平台独立的,在Windows上需要使用cmd /c
。
在一个cmake脚本中调用execute_process()
执行真正的测试命令,并在add_test()
中通过cmake -P调用该脚本。