CMake

6 篇文章 0 订阅

通过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_ARGCCMAKE_ARGV<n>访问。

注:CMAKE_ARGV0是cmake可执行程序,CMAKE_ARGV1-PCMAKE_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 ~ stopstart ~ 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:实际参数的个数
ARGV0ARGV1ARGV2等:各实际参数的值
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_FATALANY表示任何一个命令失败则算失败,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>...)

其中,PUBLICINTERFACEPRIVATE关键字用于指定该选项的作用域:

  • 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变成-lfoofoo.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=definitionname。例如:

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等)的构建类型,例如DebugRelease等。
CMAKE_INSTALL_PREFIXinstall()使用的安装目录。在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调用该脚本。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

狗蛋儿l

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值