CMake:编写 CMakeLists 文件

文章目录

  • 编辑 CMakeLists 文件
  • CMake 语言
  • 注释
  • 变量
  • 变量范围
  • 命令
  • 基本命令
  • 流控制
  • 条件语句
  • 循环构造
  • 过程定义
  • 正则表达式
  • 高级命令

本章将介绍编写有效 CMakeList 的基础知识 文件。它将涵盖基本命令和问题 您将需要处理大多数项目。虽然 CMake 可以处理极其复杂的问题 项目,对于大多数项目,您会发现本章的内容会告诉您 你所有你需要知道的。CMake 由写入的 CMakeLists.txt 文件驱动 对于软件项目。CMakeLists 文件确定所有内容 要向用户显示的选项,以及要编译到哪些源文件的选项。在 除了讨论如何编写 CMakeLists 文件外,本章 还将介绍如何使它们健壮和可维护。

编辑 CMakeLists 文件

CMakeLists 文件几乎可以在任何文本编辑器中编辑。一些 编辑器(如 Notepad++)带有 CMake 语法突出显示和 内置缩进支持。对于 Emacs 或 Vim 等编辑器,CMake 包括缩进和语法突出显示模式。这些可以找到 在源代码分发的目录中,或从 CMake 下载页面。Auxiliary

在任何受支持的生成器(Makefile、Visual Studio 等)中,如果 您编辑 CMakeLists 文件并重新生成,有一些规则会自动 调用 CMake 来更新生成的文件(例如 Makefile 或项目 files)。这有助于确保您生成的文件是 始终与您的 CMakeLists 文件同步。

CMake 语言

CMake 语言由注释、命令和变量组成。

注释

注释从行的开头并一直延伸到行的末尾。有关详细信息,请参阅手册。#

变量

CMakeLists 文件使用变量的方式与任何编程语言非常相似。CMake的 变量名称区分大小写,只能包含字母数字 字符和下划线。

CMake 自动定义了许多有用的变量,它们是 在手册中讨论过。 这些变量以 开头。避免使用此命名约定(并且, 理想情况下,为特定于项目的变量建立自己的)。CMAKE_

所有 CMake 变量都以字符串形式在内部存储,尽管它们可能 有时被解释为其他类型。

使用该命令设置变量值。在最简单的形式中, 第一个参数是变量的名称和 其余参数是值。打包了多个值参数 放入以分号分隔的列表中,并存储在 变量作为字符串。例如:

set(Foo "")      # 1 quoted arg -> value is ""
set(Foo a)       # 1 unquoted arg -> value is "a"
set(Foo "a b c") # 1 quoted arg -> value is "a b c"
set(Foo a b c)   # 3 unquoted args -> value is "a;b;c"

可以使用语法在命令参数中引用变量,其中变量名称是变量。如果命名变量 未定义,则引用将替换为空字符串; 否则,它将被变量的值替换。更换是 在扩展未带引号的参数之前执行,因此可变 包含分号的值被拆分为零个或多个参数 原始未带引号的参数的位置。例如:${VAR}VAR

set(Foo a b c)    # 3 unquoted args -> value is "a;b;c"
command(${Foo})   # unquoted arg replaced by a;b;c# and expands to three arguments
command("${Foo}") # quoted arg value is "a;b;c"
set(Foo "")       # 1 quoted arg -> value is empty string
command(${Foo})   # unquoted arg replaced by empty string# and expands to zero arguments
command("${Foo}") # quoted arg value is empty string

系统环境变量和 Windows 注册表值可以是 直接在 CMake 中访问。要访问系统环境变量, 使用语法 .CMake 还可以引用注册表 许多命令中的条目使用以下形式的语法,其中路径 是从注册表树和注册表项生成的。$ENV{VAR}[HKEY_CURRENT_USER\Software\path1\path2;key]

变量范围

CMake 中的变量的范围与大多数变量略有不同 语言。当您设置变量时,它对当前可见 CMakeLists 文件或函数以及任何子目录的 CMakeLists 文件, 调用的任何函数或宏,以及调用的任何文件 使用命令包含。当新的子目录 被处理(或调用函数),创建一个新的变量作用域,并且 使用调用中所有变量的当前值进行初始化 范围。在子作用域中创建的任何新变量或所做的更改 对现有变量,则不会影响父作用域。考虑 以下示例:

function(foo)message(${test}) # test is 1 hereset(test 2)message(${test}) # test is 2 here, but only in this scope
endfunction()set(test 1)
foo()
message(${test}) # test will still be 1 here

在某些情况下,您可能希望函数或子目录将 变量。CMake 有一种方法可以返回一个 值,并且可以通过将该选项与命令一起使用来完成。我们可以修改 前面的示例,以便函数更改 test 的值 在其父级的作用域中,如下所示:PARENT_SCOPEfoo

function(foo)message(${test}) # test is 1 hereset(test 2 PARENT_SCOPE)message(${test}) # test still 1 in this scope
endfunction()set(test 1)
foo()
message(${test}) # test will now be 2 here

CMake 中的变量是按照命令执行的顺序定义的。

请看以下示例:

# FOO is undefinedset(FOO 1)
# FOO is now set to 1set(FOO 0)
# FOO is now set to 0

要了解变量的作用域,请考虑以下示例:

set(foo 1)# process the dir1 subdirectory
add_subdirectory(dir1)# include and process the commands in file1.cmake
include(file1.cmake)set(bar 2)
# process the dir2 subdirectory
add_subdirectory(dir2)# include and process the commands in file2.cmake
include(file2.cmake)

在此示例中,由于变量在 开始时,它将在处理 dir1 和 dir2 时定义。在 对比度,仅在处理 dir2 时定义。同样,在处理 file1.cmake 和 file2.cmake,而只会在处理时定义 file2.cmake。foobarfoobar

命令

命令由命令名称、左括号、空格组成 分隔的参数和右括号。每个命令的计算结果均在 它在 CMakeLists 文件中的显示顺序。有关完整列表,请参阅手册 CMake 命令。

CMake 不再对命令名称区分大小写,因此在看到 的地方,可以改用 或。它被认为是 使用小写命令的最佳做法。所有空格(空格、换行符、 tabs) 被忽略,除非分隔参数。因此,命令可能会跨越 多行,只要命令名称和左括号打开即可 同一行。commandCOMMANDCommand

CMake 命令参数是空格分隔的,并且区分大小写。命令 参数可以带引号,也可以不带引号。带引号的参数开始和结束 在双引号 (“) 中,并且始终只表示一个参数。任何双人间 值中包含的引号必须使用反斜杠进行转义。考虑 使用括号参数 对于需要转义的参数,请参阅手册。未加引号的论点 以双引号以外的任何字符开头(后面的双引号是 literal),并自动扩展为零个或多个参数 在值内用分号分隔。例如:

command("")          # 1 quoted argument
command("a b c")     # 1 quoted argument
command("a;b;c")     # 1 quoted argument
command("a" "b" "c") # 3 quoted arguments
command(a b c)       # 3 unquoted arguments
command(a;b;c)       # 1 unquoted argument expands to 3

基本命令

正如我们之前所看到的,和 命令 显式设置或取消设置变量。、 和 命令提供字符串和列表的基本操作。

和 命令是主要的 用于定义要构建的可执行文件和库的命令,以及 哪些源文件包含它们。对于 Visual Studio 项目, 源文件将像往常一样显示在 IDE 中,但任何头文件都会显示 项目使用不会。要显示头文件,只需 将它们添加到可执行文件或库的源文件列表中; 这可以对所有发电机完成。任何不使用 直接的头文件(例如基于 Makefile 的生成器)将 只需忽略它们。

流控制

CMake 语言提供了三个流控制构造来帮助组织 您的 CMakeLists 文件,并使其保持可维护性。

条件语句(例如 )

循环结构(例如和 )

程序定义(例如和 )

条件语句

首先,我们将考虑命令。在许多方面,CMake 中的命令就像任何 其他语言。它计算其表达式并使用它来执行代码 在其正文中或子句中的代码(可选)。为 例:

if(FOO)# do something here
else()# do something else
endif()

CMake 还支持帮助按顺序测试多个 条件。例如:

if(MSVC80)# do something here
elseif(MSVC90)# do something else
elseif(APPLE)# do something else
endif()

该命令记录了它可以测试的许多条件。

循环构造

和 命令允许您处理 按顺序发生的重复性任务。命令中断 在正常情况下,它之前会退出 or 循环 结束。

该命令使您能够执行组 的 CMake 命令在列表的成员上重复执行。考虑 以下示例改编自 VTK

foreach(tfileTestAnisotropicDiffusion2DTestButterworthLowPassTestButterworthHighPassTestCityBlockDistanceTestConvolve)add_test(${tfile}-image ${VTK_EXECUTABLE}${VTK_SOURCE_DIR}/Tests/rtImageTest.tcl${VTK_SOURCE_DIR}/Tests/${tfile}.tcl-D ${VTK_DATA_ROOT}-V Baseline/Imaging/${tfile}.png-A ${VTK_SOURCE_DIR}/Wrapping/Tcl)
endforeach()

该命令的第一个参数是 变量,每次迭代时将采用不同的值 循环;其余参数是 圈。在此示例中,循环的主体只是一个 CMake 命令,.在正文中,每个 循环变量(在本例中)被引用的时间将 替换为列表中的当前值。在第一个 迭代时,出现的次数将被替换为 。在下一次迭代中,将替换为 。循环 将继续循环,直到处理完所有参数。tfile t f i l e T e s t A n i s o t r o p i c D i f f u s i o n 2 D {tfile}TestAnisotropicDiffusion2D tfileTestAnisotropicDiffusion2D{tfile}TestButterworthLowPass

值得一提的是,循环可以嵌套,并且 循环变量在任何其他变量之前被替换 扩张。这意味着在循环的主体中,您可以 使用 loop 变量构造变量名称。在下面的代码中, 循环变量被展开,然后与 连接。然后,将新的变量名称展开并测试为 看看它是否匹配。tfile_TEST_RESULTFAILED

if(${${tfile}_TEST_RESULT} MATCHES FAILED)message("Test ${tfile} failed.")
endif()

该命令提供基于测试条件的循环。这 命令中测试表达式的格式与 如前所述,它用于命令。考虑 以下示例,由 CTest 使用。请注意,CTest 会在内部更新 的值。CTEST_ELAPSED_TIME

#####################################################
# run paraview and ctest test dashboards for 6 hours
#
while(${CTEST_ELAPSED_TIME} LESS 36000)set(START_TIME ${CTEST_ELAPSED_TIME})ctest_run_script("dash1_ParaView_vs71continuous.cmake")ctest_run_script("dash1_cmake_vs71continuous.cmake")
endwhile()

过程定义

和 命令支持重复性任务 可能分散在您的 CMakeLists 文件中。一次宏或 函数,它可以通过处理后处理的任何 CMakeLists 文件 它的定义。

CMake 中的函数非常类似于 C 或 C++ 中的函数。您可以 将参数传递到其中,它们成为 功能。同样,一些标准变量如 、 、 和 、 等 定义。函数调用具有动态作用域。在一个函数中,你 在新的变量作用域中;这就像你如何掉进一个 子目录,并且位于新的 变量范围。函数时定义的所有变量 被调用保持定义,但对变量或新 变量仅存在于函数中。当函数返回时, 这些变量将消失。更简单地说:当你调用 函数,推送一个新的变量范围;当它返回时, 弹出变量范围。ARGCARGVARGNARGV0ARGV1

该命令定义一个新函数。第一个参数 是要定义的函数的名称;所有其他参数都是 函数的形式参数。

function(DetermineTime _time)# pass the result up to whatever invoked thisset(${_time} "1:23:45" PARENT_SCOPE)
endfunction()# now use the function we just defined
DetermineTime(current_time)if(DEFINED current_time)message(STATUS "The time is now: ${current_time}")
endif()

请注意,在此示例中,用于传递 返回变量。调用该命令的值为 ,该值为 。最后,该命令使用该选项在 调用方的作用域,而不是本地作用域。_time_timecurrent_timePARENT_SCOPE

宏的定义和调用方式与函数相同。这 主要区别在于宏不会推送和弹出新变量 scope,并且宏的参数不被视为变量 但作为在执行前替换的字符串。这很像 宏和 C 或 C++ 中的函数之间的差异。第一个 argument 是要创建的宏的名称;所有其他参数 是宏的正式参数。

# define a simple macro
macro(assert TEST COMMENT)if(NOT ${TEST})message("Assertion failed: ${COMMENT}")endif()
endmacro()# use the macro
find_library(FOO_LIB foo /usr/local/lib)
assert(${FOO_LIB} "Unable to find library foo")

上面的简单示例创建了一个名为 的宏。宏 被定义为两个参数;第一个是要测试的值和 第二个是如果测试失败,要打印出来的注释。的主体 macro 是带有命令的简单命令 里面。当命令 发现。只需使用其名称即可调用该宏,就好像它是 命令。在上面的例子中,如果找不到,那么 将显示消息,指示错误情况。assertFOO_LIB

该命令还支持定义采用变量的宏 参数列表。如果要定义一个宏,这将很有用 具有可选参数或多个签名。变量参数可以 改用 和 、 等引用 的形式参数。 表示第一个参数 宏观; 表示下一个,依此类推。您还可以 混合使用形式参数和变量参数,如 下面的例子。ARGCARGV0ARGV1ARGV0ARGV1

# define a macro that takes at least two arguments
# (the formal arguments) plus an optional third argument
macro(assert TEST COMMENT)if(NOT ${TEST})message("Assertion failed: ${COMMENT}")# if called with three arguments then also write the# message to a file specified as the third argumentif(${ARGC} MATCHES 3)file(APPEND ${ARGV2} "Assertion failed: ${COMMENT}")endif()endif()
endmacro()# use the macro
find_library(FOO_LIB foo /usr/local/lib)
assert(${FOO_LIB} "Unable to find library foo")

在此示例中,两个必需的参数是 和 。这些必需的参数可以按名称引用,如 在此示例中,它们通过引用 和 .如果要将参数作为列表进行处理,请使用 and 变量。 (与 、 等相反)是宏的所有参数的列表,而 是形式化后所有参数的列表 参数。在宏中,您可以使用以下命令 迭代或根据需要迭代。TEST COMMENT ARGV0 ARGV1 ARGV ARGN ARGV ARGV0 ARGV1 ARGN ARGV ARGN

该命令从函数、目录或文件返回。注意 与函数不同,宏是就地扩展的,因此不能 句柄 。

正则表达式

一些 CMake 命令(如 和 )利用了 正则表达式,也可以将正则表达式作为正则表达式 论点。在最简单的形式中,正则表达式是 用于搜索完全匹配字符的字符。然而,许多 要找到的确切序列是未知的,或者只有 字符串的开头或结尾是必需的。由于有几个 用于指定正则表达式的不同约定,CMake 的 标准在命令文档中进行了描述。这 description 基于Texas Instruments的开源正则表达式类 ,CMake 使用它来解析正则表达式。

高级命令

有一些命令可能非常有用,但不是 通常用于编写 CMakeLists 文件。本节将讨论 其中一些命令以及它们何时有用。

首先,考虑创建 两个目标之间的依赖关系。CMake 自动创建依赖项 当它可以确定目标时。例如,CMake 将 自动为依赖于 库目标。该命令通常为 用于指定目标之间的目标间依赖关系,其中至少一个 的 目标 是自定义目标(请参阅添加自定义命令部分)。

该命令还涉及 依赖。此命令控制正则表达式,即 用于跟踪源代码依赖关系。默认情况下,CMake 将 跟踪源文件(包括系统文件)的所有依赖关系 如。如果使用命令指定正则表达式,则该正则表达式将 用于限制要处理的包含文件。例如;如果 您的软件项目的 include 文件都以前缀 foo 开头 (例如,等),您可以指定一个常规 表达式,将依赖项检查限制为 项目的文件。stdio.hfooMain.c fooStruct.h^foo.*$

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/736421.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Rust 语言的 println! 宏的格式占位符

一、占位符 println! 宏的占位符主要基于 Rust 的格式化语法,它支持多种占位符格式来插入和格式化不同的值。除了 {} 和 {:?} 之外,还有其他几种形式: 基本占位符 {}:用于插入任何实现了 Display trait 的类型的值。 调试占位符…

PaddlePaddle框架安装

提示:可在python环境中进行安装,避免环境污染,创建命令conda create -n xxx_name python3.9,激活conda activate xxx_name 第一步:查看计算机平台版本 在窗口输入查看命令,查看CUDA的版本 nvidia-smi 二、根据以下条件…

webpack和vite的区别

webpack和vite都是现代化web构建工具,但他们在构建速度、开发体验、构建结果等方面有所不同 1、构建速度:vite的速度一般快于webpack (1)传统的构建工具例如webpack在开发过程中会对整个应用或者大部分应用进行打包,之后浏览器才能加载处理之后的包(好比webpack打包先从入口文…

C语言---单身狗问题

1.单身狗初阶 这个题目就是数组里面有一串数字,都是成对存在的,只有一个数字只出现了一次,请你找出来 (1)异或是满足交换律的,两个相同的数字异或之后是0; (2)让0和每个…

如何利用数据采集工具,解决医疗数据采集痛点?

在当今信息发达的时代,医疗行业也面临着日益增长的数据量和越来越复杂的管理和挑战。医院是医疗服务的核心机构,需要处理大量病人信息、医疗记录、医疗影像等,从而确保病人最佳的治疗。 但传统的医院数据信息往往存在诸多问题,如…

CVPR 2022 Oral | Bailando: 基于编舞记忆和Actor-Critic GPT的3D舞蹈生成

目录 测试结果: 02 提出的方法 测试结果: 预测有3个步骤,速度比较慢 02 提出的方法 1. 针对舞蹈序列的VQ-VAE和编舞记忆 与之前的方法不同,我们不学习从音频特征到 3D 关键点序列的连续域的直接映射。相反,我们先让…

Oracle Linux 8.9 安装 Python 3.11.8 和 Miniconda

Oracle Linux 8.9 安装 Python 3.11.8 和 Miniconda 1. 安装 Python 3.11.82. 安装 Miniconda 1. 安装 Python 3.11.8 Update system, sudo dnf update -yInstall Library, sudo dnf install curl gcc openssl-devel bzip2-devel libffi-devel zlib-devel wget make git -yI…

Spring Boot 本地部署 JSP

自己是Spring Boot 的初学者,开始看教程的时候发现基本上都是部署的 JSP,但是按照教程一步步走下来始终无法成功,一直都是 404: 查阅各种资料后,总结出一套 Spring Boot 支持 JSP 的流程: 添加依赖 在pom.xml中添加…

Java算法之动态规划

Java算法之动态规划 前言 ​ 最近这一段时间一直在刷算法题,基本上一有时间就会做一两道,这两天做了几道动态规划的问题,动态规划之前一直是我比较头疼的一个问题,感觉好复杂,一遇到这样的问题就想跳过,昨…

jupyterlab 设置

字体 pip install jupyterlab-fontsFira Code Regular 主题 pip install theme-darcula更改jupyter lab默认启动路径 https://blog.csdn.net/monster_MF/article/details/119248111 (1)找到jupyter_lab_config所在路径 winR,输入cmd&am…

NIN网络中的网络

是什么 intro LeNet→AlexNet→VGG→NiN→GoogLeNet→ResNetLeNet→AlexNet→VGG 卷积层模块充分抽取空间特征全连接层输出分类结果AlexNet & VGG 改进在于把两个模块加宽 、加深(加宽指增加通道数,那加深呢?(层数增加叭 Ni…

qemu快速入门

前提: 我们做嵌入式软件的时候,往往可能会缺少嵌入式的硬件,那我们希望提前开始准备代码的话,就需要qemu这个开源软件,它可以模拟各种型号的芯片 。那么我们可以提前在这个模拟器上面去开发代码、验证、调试。 正片开始…

跨境电商新篇章:独立站如何携手海外网红营销,实现品牌飞跃

随着品牌出海的火热,独立站成为越来越多企业的选择。然而,在激烈的市场竞争中,如何提高独立站的知名度,成为企业亟需解决的问题之一。在这个背景下,海外网红营销崭露头角,成为一种备受关注的新型推广策略。…

Covalent(CQT)降低数据可用性成本,加快 Layer2 在 Web3 领域的扩张

Covalent Network(CQT)通过其统一 API,正在为 EVM Layer2 生态系统提供支持,减少了开发者需要导航多个数据供应商的需求,通过解决多链环境中的碎片化挑战,极大地提高了他们的效率。 通过其统一 API 支持 2…

蓝桥杯嵌入式2018年第九届省赛主观题解析

1 题目 2 解析 /* USER CODE BEGIN Header */ /********************************************************************************* file : main.c* brief : Main program body********************************************************************…

3.6研究代码(2)

指的是微电网运行参数。 在MATLAB中,randi([0,1],1,48) 会生成一个包含1*48个0或1的随机整数数组。这意味着数组中的每个元素都将是0或1。 MATLAB帮助中心:均匀分布的伪随机整数 - MATLAB randi - MathWorks 中国https://ww2.mathworks.cn/help/matlab/r…

可调恒定电流稳压器NSI50150ADT4G车规级LED驱动器 提供专业的汽车级照明解决方案

NSI50150ADT4G产品概述: NSI50150ADT4G可调恒定电流稳压器 (CCR) ,是一款简单、经济和耐用的器件,适用于为 LED 中的调节电流提供成本高效的方案(与恒定电流二极管 CCD 类似)。该 (CCR) 基于自偏置晶体管 (SBT) 技术&…

MybatisPlus分页失效不起作用问题剖析

【问题描述】 在使用MybatisPlus的selectPage时发现分页不起作用&#xff0c;每次返回的都是全部的数据&#xff0c;同时getPages()和getTotal()返回的都是0。 【相关代码】 mybatisPlus的版本&#xff1a; <dependency><groupId>com.baomidou</groupId>&…

官宣正式成为 PostgreSQL Contributor,Richard 有何秘诀?

作为世界上最受欢迎的开源数据库之一&#xff0c;PostgreSQL 国际社区于3月3日正式公布了新加入的 PostgreSQL Contributor 名单&#xff0c;以认可为 PostgreSQL 开源项目做出实质性、长期贡献的人员。本次公布的名单中包括 3 名 Contributor 和 6 名 Major Contributor。 拓…

移动App开发常见的三种模式:原生应用、H5移动应用、混合模式应用

引言 在移动应用市场的迅猛发展中&#xff0c;移动App开发正日益成为技术创新和用户体验提升的焦点。对于开发者而言&#xff0c;选择适合自己项目的开发模式成为至关重要的决策。本文将探究移动App开发的三种常见模式&#xff1a;原生应用、H5移动应用和混合模式应用。这三种…