CMake语法结构说明

2023-10-22 06:28
文章标签 说明 cmake 语法结构

本文主要是介绍CMake语法结构说明,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

  • 一. 组织
    • 1. 目录
    • 2. 脚本
    • 3. 模块
  • 二. 语法
    • 1. 编码
    • 2. 源文件
    • 3. 命令调用
    • 4. 命令参数
      • (1)括号参数
      • (2)带引号的参数
      • (3)未引用的参数
    • 5. 转义序列
    • 6. 变量引用
    • 7. 注释
      • (1)括号注释
      • (2)行注释
  • 三. 控制结构
    • 1. 条件块
    • 2. 循环
    • 3. 命令定义
  • 四. 变量
  • 五. 环境变量
  • 六. 列表

一. 组织

CMake输入文件以CMake语言编写在名为CMakeLists.txt的源文件中,或以.CMake文件扩展名结尾。
项目中的CMake语言源文件被组织为:

  • 目录Directories (CMakeLists.txt),
  • 脚本Scripts (<script>.cmake)
  • 模块Modules (<module>.cmake).

1. 目录

当CMake处理项目源树时,入口点是顶级源目录中名为CMakeLists.txt的源文件。此文件可能包含整个生成规范,或者使用add_subdirectory()命令将子目录添加到生成中。命令添加的每个子目录还必须包含一个CMakeLists.txt文件作为该目录的入口点。对于处理CMakeLists.txt文件的每个源目录,CMake在构建树中生成一个相应的目录,作为默认的工作和输出目录。

2. 脚本

通过使用带有-P选项的cmake命令行工具,可以在脚本模式下处理单个<script>.cmake源文件。脚本模式只是在给定的CMake语言源文件中运行命令,而不生成构建系统。它不允许CMake命令定义生成目标或操作。

3. 模块

目录或脚本中的CMake语言代码可以使用include()命令在include上下文的范围内加载<module>.CMake源文件。项目源树也可以提供它们自己的模块,并CMAKE_MODULE_PATH变量中指定它们的位置。

二. 语法

1. 编码

CMake语言源文件可以用7位ASCII文本编写,以便在所有支持的平台上实现最大的可移植性。换行符可以编码为\n\r\n,但在读取输入文件时会转换为\n
请注意,该实现是8位干净的,因此在系统API支持这种编码的平台上,源文件可以编码为UTF-8。此外,CMake 3.2及以上版本支持在Windows上以UTF-8编码的源文件(使用UTF-16调用系统API)。此外,CMake 3.0及更高版本允许在源文件中使用领先的UTF-8字节顺序标记。

2. 源文件

CMake语言源文件由零个或多个命令调用组成,这些命令调用由换行符和可选的空格和注释分隔:

file         ::=  file_element*
file_element ::=  command_invocation line_ending |(bracket_comment|space)* line_ending
line_ending  ::=  line_comment? newline
space        ::=  <match '[ \t]+'>
newline      ::=  <match '\n'>

请注意,任何不在命令参数或括号注释内的源文件行都可以以行注释结束。

3. 命令调用

命令调用是一个名称,后面跟着用空格分隔的带括号的参数:

command_invocation  ::=  space* identifier space* '(' arguments ')'
identifier          ::=  <match '[A-Za-z_][A-Za-z0-9_]*'>
arguments           ::=  argument? separated_arguments*
separated_arguments ::=  separation+ argument? |separation* '(' arguments ')'
separation          ::=  space | line_ending

例如:

add_executable(hello world.c)

命令名不区分大小写。参数中嵌套的无引号括号必须保持平衡。每个(或)都作为未引用的实参提供给命令调用。这可以用于调用if()命令来封闭条件。例如:

if(FALSE AND (FALSE OR TRUE)) # evaluates to FALSE

4. 命令参数

命令调用中有三种类型的参数:

argument ::=  bracket_argument | quoted_argument | unquoted_argument

(1)括号参数

受Lua长括号语法启发,括号自变量将内容包含在相同长度的开头和结尾括号之间:

bracket_argument ::=  bracket_open bracket_content bracket_close
bracket_open     ::=  '[' '='* '['
bracket_content  ::=  <any text not containing a bracket_close withthe same number of '=' as the bracket_open>
bracket_close    ::=  ']' '='* ']'

左括号写为[后接零或更多=后接[。相应的右括号写为]后接相同数量的=后接]。支架不嵌套。可以始终为打开和关闭括号选择唯一的长度,以包含其他长度的关闭括号。
括号参数内容由位于左括号和右括号之间的所有文本组成,除了左括号后面的一行换行符(如果有的话)将被忽略。不执行对所附内容(如转义序列或变量引用)的评估。括号参数总是作为一个参数提供给命令调用。例如:

message([=[
This is the first line in a bracket argument with bracket length 1.
No \-escape sequences or ${variable} references are evaluated.
This is always one argument even though it contains a ; character.
The text does not end on a closing bracket of length 0 like ]].
It does end in a closing bracket of length 1.
]=])

(2)带引号的参数

带引号的参数包含位于开头和结尾双引号字符之间的内容:

quoted_argument     ::=  '"' quoted_element* '"'
quoted_element      ::=  <any character except '\' or '"'> |escape_sequence |quoted_continuation
quoted_continuation ::=  '\' newline

带引号的参数内容包括位于左引号和右引号之间的所有文本。转义序列和变量引用都会进行求值。引用的参数总是作为一个参数提供给命令调用。例如:

message("This is a quoted argument containing multiple lines.
This is always one argument even though it contains a ; character.
Both \\-escape sequences and ${variable} references are evaluated.
The text does not end on an escaped double-quote like \".
It does end in an unescaped double quote.
")

任何以奇数反斜杠结尾的行上的最后一个\都被视为行的延续,并与紧随其后的换行符一起被忽略。例如:

message("\
This is the first line of a quoted argument. \
In fact it is the only line but since it is long \
the source code uses line continuation.\
")

(3)未引用的参数

未引用的参数不包含在任何引用语法中。它不能包含任何空格、(,)、#、“或\,除非用反斜杠转义:

unquoted_argument ::=  unquoted_element+ | unquoted_legacy
unquoted_element  ::=  <any character except whitespace or one of '()#"\'> |escape_sequence
unquoted_legacy   ::=  <see note in text>

未引用的参数内容由允许字符或转义字符的连续块中的所有文本组成。转义序列和变量引用都会进行求值。结果值的划分方式与列表划分为元素的方式相同。每个非空元素都作为参数提供给命令调用。因此,一个未引用的参数可以作为零个或多个参数提供给命令调用。例如:

foreach(argNoSpaceEscaped\ SpaceThis;Divides;Into;Five;ArgumentsEscaped\;Semicolon)message("${arg}")
endforeach()

5. 转义序列

转义序列是一个\后面跟一个字符:

escape_sequence  ::=  escape_identity | escape_encoded | escape_semicolon
escape_identity  ::=  '\' <match '[^A-Za-z0-9;]'>
escape_encoded   ::=  '\t' | '\r' | '\n'
escape_semicolon ::=  '\;'

后面跟着一个非字母数字字符的\只是对文字字符进行编码,而不将其解释为语法。\t、\r或分别对制表符、回车符或换行符进行编码。A \:在任何变量引用之外对其自身进行编码,但可以在未引用的参数中用于对;而不除以其上的参数值。A\:内部变量引用对文本进行编码;

6. 变量引用

变量引用的形式为${<variable>},在带引号的参数或未带引号的自变量中进行求值。变量引用由指定变量或缓存项的值替换,或者如果两者都未设置,则由空字符串替换。变量引用可以嵌套并由内而外进行评估,例如${outer_${inner_variable}_variable}

文字变量引用可以由字母数字字符、字符/_.±、,和转义序列。嵌套引用可用于评估任何名称的变量。

  • Variables部分记录了变量名称的范围以及如何设置它们的值。
  • 环境变量引用的形式为$ENV{<variable>}
  • 缓存变量引用的形式为$cache{<variable>},并由指定ca的值替换
  • if()命令有一个特殊的条件语法,允许使用缩写形式<variable>而不是${<variable>}的变量引用。但是,环境变量总是需要被引用为$ENV{<variable>}

7. 注释

注释以#字符开头,该字符不在括号参数、带引号的参数内,也不作为未带引号参数的一部分以\转义。有两种类型的注释:

  • 括号注释
  • 行注释

(1)括号注释

#后面紧跟着一个括号,形成一个括号注释,由整个括号外壳组成:

bracket_comment ::=  '#' bracket_argument

例如:

#[[This is a bracket comment. It runs until the close bracket.]]
message("First Argument\n" #[[Bracket Comment]] "Second Argument")

(2)行注释

#后面没有紧跟括号,形成一个行注释,该注释一直持续到行的末尾:

line_comment ::=  '#' <any text not starting in a bracket_openand not containing a newline>

例如:

# This is a line comment.
message("First Argument\n" # This is a line comment :)"Second Argument") # This is a line comment.

三. 控制结构

1. 条件块

if(),eleif(),else(),endif(),命令定义要有条件执行的代码块。

2. 循环

foreach(),endforeach(),while(),endwhile(),命令分隔要在循环中执行的代码块。在这样的块中,break()命令可以用于提前终止循环,而continue(),命令则可以用于立即开始下一次迭代。

3. 命令定义

macro(),endmark(),function(),endfunction(),命令定义了要记录的代码块,以便以后作为命令调用。

四. 变量

变量是CMake语言中的基本存储单元。它们的值总是字符串类型的,尽管有些命令可能会将字符串解释为其他类型的值。set()unset()命令显式地设置或取消设置变量,但其他命令也具有修改变量的语义。变量名称区分大小写,几乎可以由任何文本组成,但我们建议使用仅由字母数字字符加上_-组成的名称。
变量具有动态范围。每个变量setunset在当前作用域中创建一个绑定:

  • 块范围:block()命令可以为变量绑定创建一个新的作用域。
  • 功能范围:function()命令创建的命令定义创建的命令在被调用时处理新变量绑定范围中记录的命令。变量setunset在此范围内绑定,并且对于当前函数及其内的任何嵌套调用都可见,但在函数返回后不可见。
  • 目录作用域:源树中的每个目录都有自己的变量绑定。在处理目录的CMakeLists.txt文件之前,CMake复制当前在父目录中定义的所有变量绑定(如果有的话),以初始化新的目录作用域。当使用CMake -P处理时,CMake脚本将变量绑定到一个“目录”范围中。
    不在函数调用内的变量setunset绑定到当前目录作用域。
  • 永久缓存:CMake存储一组单独的“缓存”变量或“缓存条目”,其值在项目构建树中的多个运行中保持不变。缓存条目有一个单独的绑定作用域,该绑定作用域仅通过显式请求进行修改,例如通过set()unset()命令的Cache选项进行修改。

在评估变量引用时,CMake首先在函数调用堆栈(如果有)中搜索绑定,然后返回到当前目录作用域中的绑定(如果有的话)。如果找到set绑定,则使用其值。如果找到“未设置”的绑定,或者没有找到绑定,CMake将搜索缓存条目。如果找到缓存条目,则使用其值。否则,变量引用的计算结果为空字符串。$CACHE{}VAR}语法可用于直接查找缓存条目。

五. 环境变量

环境变量与普通变量一样,有以下区别:

  • 范围:环境变量在CMake过程中具有全局作用域。它们从不缓存。
  • 参考文献:变量引用的形式为$ENV{<Variable>},使用ENV运算符。
  • 初始化:CMake环境变量的初始值是调用进程的初始值。可以使用set()unset()命令更改值。这些命令只影响正在运行的CMake进程,而不会影响整个系统环境。更改后的值不会写回调用进程,后续的构建或测试进程也看不到这些值。

六. 列表

尽管CMake中的所有值都存储为字符串,但在某些上下文中,例如在评估未引用的参数时,字符串可能会被视为列表。在这样的上下文中,字符串通过在上进行拆分而被划分为列表元素;不在[]字符数不相等且前面不紧跟\的字符。序列\:不划分值,但替换为:在得到的元素中。
元素列表通过连接由:分隔的元素来表示为字符串;例如,set()命令将多个值作为列表存储到目标变量中:

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

列表用于简单的用例,如源文件列表,不应用于复杂的数据处理任务。大多数构造列表的命令都不会转义:列表元素中的字符,从而使嵌套列表变平:

set(x a "b;c") # sets "x" to "a;b;c", not "a;b\;c"

通常,列表不支持包含以下内容的元素:字符。为避免出现问题,请考虑以下建议:

  • 许多CMake命令、变量和属性的接口都接受分号分隔的列表。避免将包含分号元素的列表传递给这些接口,除非它们记录了直接支持或某种转义或编码分号的方式。

  • 构造列表时,用未使用的占位符替换:在元素中。然后替换:用于处理列表元素时的占位符。例如,以下代码使用|代替:字符:

    set(mylist a "b|c")
    foreach(entry IN LISTS mylist)string(REPLACE "|" ";" entry "${entry}")# use "${entry}" normally
    endforeach()
    
  • 在生成器表达式列表中,使用$<SEMICON>生成器表达式。

  • 在命令调用中,尽可能使用带引号的参数语法。被调用的命令将接收保留分号的参数内容。未引用的参数将以分号分隔。

  • function()实现中,避免使用ARGVARGN,因为它们不区分值中的分号和分隔值的分号。相反,更喜欢使用命名的位置参数以及ARGCARGV#变量。使用cmake_parse_arguments()解析参数时,首选其parse_ARGV签名,该签名使用ARGV#变量。

这篇关于CMake语法结构说明的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/259676

相关文章

Tomcat版本与Java版本的关系及说明

《Tomcat版本与Java版本的关系及说明》:本文主要介绍Tomcat版本与Java版本的关系及说明,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录Tomcat版本与Java版本的关系Tomcat历史版本对应的Java版本Tomcat支持哪些版本的pythonJ

Nginx指令add_header和proxy_set_header的区别及说明

《Nginx指令add_header和proxy_set_header的区别及说明》:本文主要介绍Nginx指令add_header和proxy_set_header的区别及说明,具有很好的参考价... 目录Nginx指令add_header和proxy_set_header区别如何理解反向代理?proxy

JAVA SE包装类和泛型详细介绍及说明方法

《JAVASE包装类和泛型详细介绍及说明方法》:本文主要介绍JAVASE包装类和泛型的相关资料,包括基本数据类型与包装类的对应关系,以及装箱和拆箱的概念,并重点讲解了自动装箱和自动拆箱的机制,文... 目录1. 包装类1.1 基本数据类型和对应的包装类1.2 装箱和拆箱1.3 自动装箱和自动拆箱2. 泛型2

MySQL常见的存储引擎和区别说明

《MySQL常见的存储引擎和区别说明》MySQL支持多种存储引擎,如InnoDB、MyISAM、MEMORY、Archive、CSV和Blackhole,每种引擎有其特点和适用场景,选择存储引擎时需根... 目录mysql常见的存储引擎和区别说明1. InnoDB2. MyISAM3. MEMORY4. A

MyBatis的配置对象Configuration作用及说明

《MyBatis的配置对象Configuration作用及说明》MyBatis的Configuration对象是MyBatis的核心配置对象,它包含了MyBatis运行时所需的几乎所有配置信息,这个对... 目录MyBATis配置对象Configuration作用Configuration 对象的主要作用C

在Dockerfile中copy和add的区别及说明

《在Dockerfile中copy和add的区别及说明》COPY和ADD都是Dockerfile中用于文件复制的命令,但COPY仅用于本地文件或目录的复制,不支持自动解压缩;而ADD除了复制本地文件或... 目录在dockerfile中,copy 和 add有什么区别?COPY 命令ADD 命令总结在Doc

解读Pandas和Polars的区别及说明

《解读Pandas和Polars的区别及说明》Pandas和Polars是Python中用于数据处理的两个库,Pandas适用于中小规模数据的快速原型开发和复杂数据操作,而Polars则专注于高效数据... 目录Pandas vs Polars 对比表使用场景对比Pandas 的使用场景Polars 的使用

Spring Boot Actuator使用说明

《SpringBootActuator使用说明》SpringBootActuator是一个用于监控和管理SpringBoot应用程序的强大工具,通过引入依赖并配置,可以启用默认的监控接口,... 目录项目里引入下面这个依赖使用场景总结说明:本文介绍Spring Boot Actuator的使用,关于Spri

Linux中shell解析脚本的通配符、元字符、转义符说明

《Linux中shell解析脚本的通配符、元字符、转义符说明》:本文主要介绍shell通配符、元字符、转义符以及shell解析脚本的过程,通配符用于路径扩展,元字符用于多命令分割,转义符用于将特殊... 目录一、linux shell通配符(wildcard)二、shell元字符(特殊字符 Meta)三、s

java脚本使用不同版本jdk的说明介绍

《java脚本使用不同版本jdk的说明介绍》本文介绍了在Java中执行JavaScript脚本的几种方式,包括使用ScriptEngine、Nashorn和GraalVM,ScriptEngine适用... 目录Java脚本使用不同版本jdk的说明1.使用ScriptEngine执行javascript2.