首页 最新 热门 推荐

  • 首页
  • 最新
  • 热门
  • 推荐

CMake构建工具使用教程

  • 25-02-16 17:00
  • 2877
  • 11084
blog.csdn.net

1.简介

CMake是一个开源的、跨平台的C++构建工具,通过平台和编译器无关的配置文件来声明构建目标,支持Make、ninja、MSBuild等多种底层构建工具,大多数IDE(例如CLion、Visual Studio、Visual Studio Code等)也都支持CMake。

  • 网站:https://cmake.org/
  • 官方文档:CMake Reference Documentation
  • 官方教程:CMake Tutorial

2.安装

  • 下载链接
  • 安装指引

2.1 Windows

在Windows上安装CMake有以下几种方式:

(1)下载MSI安装文件(例如cmake-3.25.2-windows-x86_64.msi)并按照指引安装。

(2)下载压缩文件(例如cmake-3.25.2-windows-x86_64.zip),解压后将其中的bin目录添加到PATH环境变量。

(3)如果安装了Visual Studio,可以通过Visual Studio Installer安装“适用于Windows的C++ CMake工具”,如下图所示。

Visual Studio Installer

2.2 Linux

在Linux上安装CMake有以下几种方式:

(1)下载压缩文件(例如cmake-3.25.2-linux-x86_64.tar.gz),解压后将其中的bin目录添加到PATH环境变量。

(2)下载安装脚本(例如cmake-3.25.2-linux-x86_64.sh)并按照指引安装。

(3)从源代码构建:下载源代码(例如cmake-3.25.2.tar.gz),解压后依次执行以下命令:

./bootstrap
make
make install
  • 1
  • 2
  • 3

2.3 macOS

下载镜像文件(例如cmake-3.25.2-macos-universal.dmg)或压缩文件(例如cmake-3.25.2-macos-universal.tar.gz),将CMake.app拷贝到/Applications目录下,运行后按照 “How to Install For Command Line Use” 菜单项中的指引安装命令行工具,如下图所示。

macOS CMake

安装成功后应该能够在命令行中执行cmake命令:

$ cmake
Usage

  cmake [options] <path-to-source>
  cmake [options] <path-to-existing-build>
  cmake [options] -S <path-to-source> -B <path-to-build>

Specify a source directory to (re-)generate a build system for it in the
current working directory.  Specify an existing build directory to
re-generate its build system.

Run 'cmake --help' for more information.
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

3.示例

下面使用CMake创建一个简单的C++ Hello World项目。

3.1 创建工作目录

首先创建项目根目录cmake-demo,并在根目录下创建一个文件CMakeLists.txt和一个子目录hello:

mkdir cmake-demo && cd cmake-demo
touch CMakeLists.txt
mkdir hello
  • 1
  • 2
  • 3

其中项目根目录cmake-demo可以在任意位置。

CMake通过名为CMakeLists.txt的文件声明构建目标和依赖关系。 根目录下的CMakeLists.txt内容如下:

cmake_minimum_required(VERSION 3.20)
project(cmake-demo)

set(CMAKE_CXX_STANDARD 14)
  • 1
  • 2
  • 3
  • 4

其中,cmake_minimum_required()命令指定该项目要求的最低CMake版本,project()命令设置项目名称,set()命令设置CMake变量的值,CMAKE_CXX_STANDARD变量指定C++标准版本。

3.2 实现hello库

在hello目录下创建hello.h和hello.cpp两个文件,内容如下:

hello.h

#pragma once

#include 

void hello(const std::string& to);
  • 1
  • 2
  • 3
  • 4
  • 5

hello.cpp

#include "hello.h"

#include 

void hello(const std::string& to) {
    std::cout << "Hello, " << to << "!\n";
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

这两个文件构成了一个函数库hello。

在hello目录下创建另一个CMakeLists.txt文件,在其中声明hello库:

add_library(hello hello.cpp)
target_include_directories(hello INTERFACE ${CMAKE_CURRENT_SOURCE_DIR})
  • 1
  • 2

其中,add_library()命令添加了一个函数库构建目标,名字为 “hello”、源文件为hello.cpp;target_include_directories()命令表示所有链接到hello库的构建目标都需要将当前源代码目录(即cmake-demo/hello)添加到头文件包含目录,从而可以直接#include "hello.h"。

之后在项目根目录下的CMakeLists.txt结尾添加:

add_subdirectory(hello)
  • 1

从而将子目录hello中的构建目标包含进来。

3.3 实现hello_world程序

下面创建一个hello_world程序,在main()函数中调用hello库提供的函数来打印信息。

在项目根目录下创建源文件hello_world.cpp,内容如下:

#include "hello.h"

int main() {
    hello("world");
    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

在项目根目录下的CMakeLists.txt结尾添加:

add_executable(hello_world hello_world.cpp)
target_link_libraries(hello_world hello)
  • 1
  • 2

其中,add_executable()命令添加了一个可执行程序构建目标,名字为 “hello_world”、源文件为hello_world.cpp;target_link_libraries()命令声明了构建目标之间的依赖关系:hello_world依赖hello,即hello_world程序需要链接到hello库。

3.4 构建和运行

为了构建hello_world程序,在项目根目录下执行以下命令:

$ cmake -S . -B cmake-build
-- The C compiler identification is GNU 7.5.0
-- The CXX compiler identification is GNU 7.5.0
...
-- Build files have been written to: .../cmake-demo/cmake-build

$ cmake --build cmake-build
[ 25%] Building CXX object hello/CMakeFiles/hello.dir/hello.cpp.o
[ 50%] Linking CXX static library libhello.a
[ 50%] Built target hello
[ 75%] Building CXX object CMakeFiles/hello_world.dir/hello_world.cpp.o
[100%] Linking CXX executable hello_world
[100%] Built target hello_world
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

其中,第一步是配置CMake,第二步是生成构建目标。构建完成后将在cmake-build目录下生成可执行程序hello_world,直接执行即可:

$ cmake-build/hello_world 
Hello, world!
  • 1
  • 2

完整的项目目录结构如下:

cmake-demo/
    CMakeLists.txt
    hello_world.cpp
    hello/
        CMakeLists.txt
        hello.h
        hello.cpp
    cmake-build/        # CMake自动创建
        ...
        hello_world     # 可执行程序
        hello/
            ...
            libhello.a  # hello库
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

4.CMake命令行工具

CMake命令行工具cmake的文档见cmake(1)。Linux或macOS系统也可以通过cmake --help或man cmake查看。

4.1 配置

配置(configure)是指根据CMakeLists.txt声明的构建目标(build target)和依赖关系(dependencies),针对特定的底层构建工具生成构建系统(buildsystem)(由一系列构建文件组成),所使用的底层构建工具叫做生成器(generator)。

配置命令的用法如下:

cmake [<options>] -S <path-to-source> -B <path-to-build>
  • 1

常用选项:

  • -S :指定源代码目录(source directory)。
  • -B :指定构建目录(build/binary directory),用于存放底层构建工具的构建文件(例如Makefile)和构建目标的输出(例如库文件和可执行文件)。
  • -D =:指定变量的值。可以省略中间的空格:-D=
  • -G :指定构建系统生成器。
  • -A :指定平台名称(如果生成器支持)。

其中,如果-S和-B仅指定了二者之一,则另一个默认为当前工作目录。

当构建目标或依赖关系发生变化(即CMakeLists.txt文件发生变化)时,需要重新配置。

注:在实际项目中,通常将构建目录与源代码目录区分开,从而可以方便地从Git中排除。

4.1.1 生成器

生成器是CMake使用的底层构建工具,用于指定CMake配置命令生成哪种构建文件、使用哪种编译器和链接器。例如:

生成器底层构建工具构建文件编译器和链接器
Unix MakefilesGNU MakeMakefileGCC(gcc和ld)
MinGW MakefilesMinGW MakeMakefileGCC(gcc.exe和ld.exe)
Visual StudioMSBuild.sln和.vcxprojMSVC(cl.exe和link.exe)

CMake支持不同平台上的多种生成器,详见cmake-generators(7)。可通过配置命令的-G选项指定要使用的生成器,如果未指定则使用当前平台的默认生成器。CMake在当前平台上支持的生成器和默认生成器可通过cmake --help命令查看。

例如,在Linux系统上可以使用Unix Makefiles生成器:

cmake -G "Unix Makefiles" -S . -B cmake-build
  • 1

在Windows系统上可以使用Visual Studio生成器(需要安装对应版本的Visual Studio):

cmake -G "Visual Studio 17 2022" -A x64 -B cmake-build
  • 1

4.2 构建

配置完成后,就可以根据构建系统生成构建目标。

构建命令的用法如下:

cmake --build <dir> [<options>]
  • 1

构建目标将被输出到构建目录中对应的子目录下。如果只有源代码发生变化,CMakeLists.txt文件没有变化,则只需重新构建,不需要重新配置。

常用选项:

  • --build :指定项目构建目录。
  • -j , --parallel :指定构建使用的最大并发进程数。
  • -t , --target :仅构建指定的目标及其上游依赖。可以指定多个目标,用空格分隔。如果未指定则构建所有目标。目标clean用于清除构建输出。
  • --config :对于多配置的构建工具指定使用的配置,例如Debug或Release。
  • --clean-first:首先清理构建输出(即构建目标clean)。如果要只清理构建输出,使用--target clean。

4.3 安装

构建完成后,可以将指定的构建目录中的库文件和可执行文件安装到指定位置,使用install()命令指定安装规则。

安装命令的用法如下:

cmake --install <dir> [<options>]
  • 1

常用选项:

  • --install :指定项目构建目录。
  • --prefix :覆盖默认的安装目录前缀CMAKE_INSTALL_PREFIX

4.4 运行脚本

CMake脚本文件由CMake命令组成,后缀通常为.cmake(本质上和CMakeLists.txt没有区别)。用法:

cmake [-D <var>=<value>]... -P <cmake-script-file> [<args>...]
  • 1

后面的参数将被传递给脚本,可通过变量CMAKE_ARGC和CMAKE_ARGV访问。

注:CMAKE_ARGV0是cmake可执行程序,CMAKE_ARGV1是-P,CMAKE_ARGV2是脚本文件名,CMAKE_ARGV3及之后才是传递给脚本的参数。

4.5 运行命令行工具

CMake提供了一些命令行工具,包括文件和目录操作、访问环境变量等,可以避免针对不同的操作系统编写不同的脚本命令。用法:

cmake -E <command> [<options>]
  • 1

直接运行cmake -E将列出所有命令。

常用命令:

  • cat ...:拼接文件并打印到标准输出。
  • chdir [...]:切换当前工作目录并运行命令。
  • compare_files [--ignore-eol] :比较两个文件,如果文件相同则返回0,否则返回1。--ignore-eol表示忽略换行符的差异(LF/CRLF)。
  • copy ... , copy -t ...:将文件拷贝到指定目录下,不支持通配符。
  • copy_directory ... :将目录拷贝到指定目录下。
  • echo [...]:打印参数。
  • make_directory ...:创建目录,如果需要则创建父目录。
  • rename :重命名文件或目录。
  • rm [-r] ...:删除文件或目录。'r选项用于递归删除目录及其子目录。

5.CMake命令

CMakeLists.txt文件本身也是一种语言,叫做“CMake语言”,语法说明见cmake-language(7)。

CMake语言的核心是命令(command),这些命令用于声明构建目标和依赖关系、指定编译和链接选项、设置CMake变量的值等。完整列表见cmake-commands(7)。

CMake命令的语法格式如下:

command_name(arg1 arg2 ...)
command_name(KEYWORD1 arg1 KEYWORD2 arg2 ...)
  • 1
  • 2

下面是一些常用的CMake命令(有些命令并未列出全部参数)。

5.1 脚本命令

cmake_minimum_required

指定项目要求的最低CMake版本。

cmake_minimum_required(VERSION )
  • 1

set

设置CMake变量的值。

set( ...)
  • 1

可以通过${var}的形式引用变量。如果指定了多个值,则变量的实际值是分号分隔的列表。例如:

set(srcs a.c b.c c.c)  # sets "srcs" to "a.c;b.c;c.c"
  • 1

option

定义一个布尔型选项,可以在配置命令中使用-D选项指定值为ON或OFF。

option( "" [value])
  • 1

如果未指定初始值则默认为OFF。

message

输出日志消息。

message("message text" ...)
  • 1

include

从指定的文件或模块加载并运行CMake命令。

include()
  • 1

if

条件语句,当条件为真时执行一组命令。

if()
  
elseif() # optional block, can be repeated
  
else()              # optional block
  
endif()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

其中,if()支持的表达式语法和逻辑运算符见if - Condition Syntax。例如:

if(WIN32)
  set(output_file NUL)
else()
  set(output_file /dev/null)
endif()
  • 1
  • 2
  • 3
  • 4
  • 5

while

循环语句,当条件为真时重复执行一组命令。

while()
  
endwhile()
  • 1
  • 2
  • 3

foreach

循环语句,对列表中的每个值执行一组命令。

foreach( )
  
endforeach()
  • 1
  • 2
  • 3

该命令有几种变体:

(1)遍历整数

foreach( RANGE )
foreach( RANGE   [])
  • 1
  • 2

遍历0 ~ stop或start ~ stop之间的整数,包含上界。其中,start、stop和step必须是非负整数,且stop大于等于start,step默认为1。

例如:

foreach(i RANGE 1 3)
  add_executable(prog${i} prog${i}.cpp)
endforeach()
  • 1
  • 2
  • 3

等价于

add_executable(prog1 prog1.cpp)
add_executable(prog2 prog2.cpp)
add_executable(prog3 prog3.cpp)
  • 1
  • 2
  • 3

(2)遍历列表

foreach( IN ITEMS )
  • 1

其中items是分号分隔的列表。

例如:

foreach(i IN ITEMS foo;bar;baz)
  add_executable(${i} ${i}.cpp)
endforeach()
  • 1
  • 2
  • 3

等价于

add_executable(foo foo.cpp)
add_executable(bar bar.cpp)
add_executable(baz baz.cpp)
  • 1
  • 2
  • 3

break

跳出foreach()或while()循环。

break()
  • 1

continue

继续下一次foreach()或while()循环。

continue()
  • 1

function

定义函数。

function( [ ...])
  
endfunction()
  • 1
  • 2
  • 3

该命令定义了一个名为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)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

等价于

add_executable(foo foo.cpp)
target_link_libraries(foo GUI)
add_executable(bar bar.cpp)
target_link_libraries(bar GUI)
  • 1
  • 2
  • 3
  • 4

在函数体中,除了${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)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

注:function()命令仅支持位置参数(positional parameter),使用cmake_parse_arguments()命令可以实现关键字参数(keyword parameter)。

cmake_parse_arguments

解析函数参数。

cmake_parse_arguments(    ...)
  • 1

其中,option_keywords指定所有的选项关键字(即没有值的关键字参数),one_value_keywords指定所有的单值关键字,multi_value_keywords指定所有的多值关键字,...是要被处理的参数。解析结果将被保存到各关键字对应的变量中,命名格式为_。

例如:

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()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

如果像这样调用my_install():

my_install(TARGETS foo bar DESTINATION bin OPTIONAL blub CONFIGURATIONS)
  • 1

则在函数体中调用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  # was not used
MY_INSTALL_TARGETS = "foo;bar"
MY_INSTALL_CONFIGURATIONS  # was not used
MY_INSTALL_UNPARSED_ARGUMENTS = "blub" # nothing expected after "OPTIONAL"
MY_INSTALL_KEYWORDS_MISSING_VALUES = "CONFIGURATIONS"
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

list

列表操作,详见list。

string

字符串操作,详见string。

file

文件操作,详见file。

execute_process

执行一个或多个子进程。

execute_process(
  COMMAND  []
  [COMMAND  []]...
  [WORKING_DIRECTORY ]
  [RESULT_VARIABLE ]
  [OUTPUT_VARIABLE ]
  [ERROR_VARIABLE ]
  [INPUT_FILE ]
  [OUTPUT_FILE ]
  [ERROR_FILE ]
  [COMMAND_ERROR_IS_FATAL ])
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

命令是以管道的方式并发执行的,每个命令的标准输出通过管道连接到下一个命令的标准输入。

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表示只有最后一个命令失败才算失败。

5.2 项目命令

project

设置项目名称。

project()
  • 1

该命令将设置以下变量:

  • PROJECT_NAME:项目名称
  • PROJECT_SOURCE_DIR:项目源代码目录
  • PROJECT_BINARY_DIR:项目构建目录

add_subdirectory

将指定的子目录加入构建。

add_subdirectory()
  • 1

如果dir是相对路径,则相对于当前目录。CMake执行该命令时将立即处理子目录中的CMakeLists.txt文件。

add_executable

添加可执行程序构建目标。

add_executable( ...)
  • 1

可执行文件名为 (Linux)或.exe (Windows)。

add_library

添加库构建目标。

add_library( [STATIC|SHARED] ...)
  • 1

可以指定库文件的类型:

  • STATIC:静态链接库(默认),库文件名为lib.a (Linux)或.lib (Windows)
  • SHARED:动态链接库,库文件名为lib.so (Linux)或.dll (Windows)

add_test

添加测试,详见第7节。

target_include_directories

为给定的目标添加包含目录(相当于GCC编译器的-I选项)。

target_include_directories(  ...)
  • 1

其中,PUBLIC、INTERFACE和PRIVATE关键字用于指定该选项的作用域:

  • PUBLIC:对该目标及其下游依赖均生效
  • INTERFACE:仅对该目标的下游依赖生效
  • PRIVATE:仅对该目标生效

相对路径将被解释为相对于当前源代码目录。

例如:

add_library(foo foo.cpp)
target_include_directories(foo INTERFACE ${CMAKE_CURRENT_SOURCE_DIR})
  • 1
  • 2

定义了一个函数库foo,并指定foo的下游依赖要将当前源代码目录添加到包含目录,从而可以直接包含当前目录下的头文件。

target_link_directories

为给定的目标添加库目录(相当于GCC编译器的-L选项)。

target_link_directories(  ...)
  • 1

注:一般不需要使用该命令,直接使用target_link_libraries()即可。

target_link_libraries

为给定的目标添加依赖库,即上游依赖(相当于GCC编译器的-l选项)。构建可执行文件时,依赖库将参与链接。

target_link_libraries(  ...)
target_link_libraries( ...)
  • 1
  • 2

其中,第二种形式等价于PUBLIC,即为给定的目标及其下游依赖均添加依赖库,从而依赖关系具有传递性。

每个可以是:

  • 库目标名称,由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)
  • 1
  • 2
  • 3
  • 4
  • 5

定义了三个库foo、bar和baz,bar及其下游依赖均依赖foo,baz的下游依赖均依赖foo,但baz本身不依赖foo。

target_compile_options

为给定的目标添加编译选项。

target_compile_options(  ...)
  • 1

target_compile_definitions

为给定的目标添加宏定义(相当于GCC编译器的-D选项)。

target_compile_definitions(  ...)
  • 1

其中的格式为name=definition或name。例如:

target_compile_definitions(foo PUBLIC FOO)
target_compile_definitions(foo PUBLIC FOO=bar)
  • 1
  • 2

target_link_options

为给定的目标添加链接选项。

target_link_options(  ...)
  • 1

include_directories

为当前目录及子目录下的所有目标添加包含目录。

include_directories(...)
  • 1

注:优先使用target_include_directories()。

link_directories

为当前目录及子目录下的所有目标添加库目录。

link_directories(...)
  • 1

注:优先使用target_link_directories()。

link_libraries

为当前目录及子目录下的所有目标添加依赖库。

link_libraries(...)
  • 1

注:优先使用target_link_libraries()。

add_compile_options

为当前目录及子目录下的所有目标添加编译选项。

add_compile_options(
  • 1

注:优先使用target_compile_options()。

add_compile_definitions

为当前目录及子目录下的所有目标添加宏定义。

add_compile_definitions(...)
  • 1

注:优先使用target_compile_definitions()。

add_link_options

为当前目录及子目录下的所有目标添加链接选项。

add_link_options(
  • 1

注:优先使用target_link_options()。

add_custom_command

添加自定义构建规则。

add_custom_command(
  OUTPUT output...
  COMMAND command [ARGS] [args...]
  DEPENDS depends...
  [WORKING_DIRECTORY dir]
  [VERBATIM])
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

其中,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)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

命令参数可以包含重定向运算符(例如>)。

add_custom_target

添加一个没有输出的自定义目标。

add_custom_target(
  name
  [COMMAND command [args...] ...]
  [DEPENDS depends... ]
  [WORKING_DIRECTORY dir]
  [VERBATIM]
  [SOURCES src1 [src2...]])
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

install

指定安装规则。

install(TARGETS targets... DESTINATION )
install(FILES files... DESTINATION )
  • 1
  • 2

第一种形式用于安装构建目标(库文件或可执行文件),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)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

在执行安装命令cmake --install时分别将可执行文件foo、库文件libbar.a和头文件bar.h安装到CMAKE_INSTALL_PREFIX下的bin、lib和include目录。

5.3 生成器表达式

cmake-generator-expressions(7)

6.CMake内置变量

CMake提供了很多内置变量,可以通过set()命令或-D选项指定。下面是一些常用的变量,完整列表见cmake-variables(7)。

6.1 路径相关变量

  • 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()命令的项目构建目录

6.2 系统相关变量

  • LINUX:如果目标系统是Linux则设置为TRUE
  • WIN32:如果目标系统是Windows则设置为TRUE
  • APPLE:如果目标系统是macOS则设置为TRUE

6.3 语言相关变量

  • CMAKE_C_STANDARD:默认C标准版本,可选的值为90、99、11、17、23等
  • CMAKE_CXX_STANDARD:默认C++标准版本,可选的值为98、11、14、17、20、23、26等
  • CMAKE__COMPILER:指定语言的编译器完整路径,可以是C、CXX等
  • CMAKE__FLAGS:指定语言的编译选项

6.4 构建/安装相关变量

  • CMAKE_BUILD_TYPE:指定单配置生成器(例如Makefile、Ninja等)的构建类型,例如Debug、Release等,详见Build Configurations。
  • CMAKE_INSTALL_PREFIX:install()使用的安装目录。在UNIX上默认为 “/usr/local”,在Windows上默认为 “C:\Program Files/${PROJECT_NAME}”。

7.测试

CMake通过CTest模块提供了测试支持。

首先在项目根目录下的CMakeLists.txt中调用enable_testing()命令,之后可以在任意的CMakeLists.txt中通过add_test()命令添加测试。

7.1 添加测试

add_test()命令的用法如下:

add_test(
  NAME 
  COMMAND  [...]
  [WORKING_DIRECTORY ])
  • 1
  • 2
  • 3
  • 4

其中,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调用该脚本。

参考:

  • How to use redirection in cmake add_test
  • Capturing/processing output of the ADD_TEST command

7.2 运行测试

用于运行测试的命令行工具是ctest,文档见ctest(1)。

添加测试并配置、构建完成后,在构建目录下直接执行ctest命令即可。

注:CTest本身不提供任何断言或比较功能,如何执行测试完全由测试命令决定。

7.3 GoogleTest

GoogleTest是一个常用的C++测试框架,CMake通过GoogleTest模块提供了对GoogleTest的支持。

示例见GoogleTest使用教程。

8.模块

CMake自带一些提供额外功能的模块(module),例如前面提到的CTest和GoogleTest,可以通过include()命令加载。完整列表见cmake-modules(7)。

8.1 FetchContent

FetchContent模块提供了在配置时自动加载外部项目的功能,主要命令是FetchContent_Declare()和FetchContent_MakeAvailable()。

示例:

  • GoogleTest: GoogleTest使用教程 3.2节
  • FLTK: 《C++程序设计原理与实践》笔记 第12章 一个显示模型 12.8.2节
  • gRPC: gRPC入门教程 2.1.3节
注:本文转载自blog.csdn.net的zzy979的文章"https://blog.csdn.net/zzy979481894/article/details/129109513"。版权归原作者所有,此博客不拥有其著作权,亦不承担相应法律责任。如有侵权,请联系我们删除。
复制链接
复制链接
相关推荐
发表评论
登录后才能发表评论和回复 注册

/ 登录

评论记录:

未查询到任何数据!
回复评论:

分类栏目

后端 (14832) 前端 (14280) 移动开发 (3760) 编程语言 (3851) Java (3904) Python (3298) 人工智能 (10119) AIGC (2810) 大数据 (3499) 数据库 (3945) 数据结构与算法 (3757) 音视频 (2669) 云原生 (3145) 云平台 (2965) 前沿技术 (2993) 开源 (2160) 小程序 (2860) 运维 (2533) 服务器 (2698) 操作系统 (2325) 硬件开发 (2492) 嵌入式 (2955) 微软技术 (2769) 软件工程 (2056) 测试 (2865) 网络空间安全 (2948) 网络与通信 (2797) 用户体验设计 (2592) 学习和成长 (2593) 搜索 (2744) 开发工具 (7108) 游戏 (2829) HarmonyOS (2935) 区块链 (2782) 数学 (3112) 3C硬件 (2759) 资讯 (2909) Android (4709) iOS (1850) 代码人生 (3043) 阅读 (2841)

热门文章

101
推荐
关于我们 隐私政策 免责声明 联系我们
Copyright © 2020-2024 蚁人论坛 (iYenn.com) All Rights Reserved.
Scroll to Top