概述
1 CMake语法
CMakeLists文件是由注释、命令以及空白符三种语法组成。注释是由符号#开始直到一行结束。命令是由命令名称、括号以及由空白符分隔的参数组成。除了用于分隔空命令参数的空白符,其他的空白符都会被忽略。双引号中的内容只会被看作是一个参数。反斜杠可以用来对字符进行转义。
2 基础命令
CMakeLists文件的第一个命令是 project 命令,用来指定项目的名称以及指定项目的语言。它的语法如下:
project (projectname [CXX] [C] [Java] [NONE])
如果没有指定项目语言则CMake默认支持C和C++。如果使用了NONE参数,则CMake将不会包含任何的语言支持。如果指定了项目语言为C++,则C语言支持也会被加载。
每出现一个 project 命令,CMake都会创建一个IDE的项目文件,这个项目文件将会包含CMakeLists文件中的所有目标文件以及使用 add_subdirectory 命令包含的子目录。如果在add_subdirectory 命令中使用了 EXCLUDE_FROM_ALL选项,则子目录不会出现在顶层的Makefile文件或IDE的工程文件中。
set 命令是使用最多的命令,用来定义或修改变量和列表的值与 set 命令相对的是 remove 和 separate_arguments 命令。remove 命令用来从变量列表中移除一个值,separate_arguments 命令用来根据空白符将一个变量分割为一个变量列表。
add_executable 和 add_library 用来定义要生成的库和可执行文件,以及库和可执行文件包含的源文件。
3 流程控制
CMake提供了三种流程控制结构:
- 条件语句(例如:if)
- 循环结构(例如:foreach 和 while)
- 过程定义(例如:macro 和 function)
条件语句
条件语句在许多方面和其他语言的的if语句一样,具体如下:
if (FOO)
#do something here
else (FOO)
#do something here
endif (FOO)
else 和 endif 分句中的条件用来提供额外的错误检查,它们必须和 if 的条件完全相同。else 和 endif 分句中的条件也可以省略,具体如下:
if (FOO)
#do something here
else ()
#do something here
endif ()
CMake 也支持用 elseif 来进行多个条件测试。例如
if (MSVC80)
# do something here
elseif (MSVC90)
# do something here
elseif (APPLE)
# do something here
endif()
CMake 中的 if 命令只有有限的几个操作可以使用,具体如下:
if (variable)
# 当 variable 不为 空值,0,FALSE,OFF 或者 NOTFOUND 时为真
if (NOT variable)
# 当 variable 为 空值,0,FALSE,OFF 或者 NOTFOUND 时为真
if (variable1 AND variable2)
# 当 variable1, variable1 同时不为 空值,0,FALSE,OFF 或者 NOTFOUND 时为真
if (variable1 OR variable2)
# 当 variable1, variable1 有一个不为 空值,0,FALSE,OFF 或者 NOTFOUND 时为真
if (COMMAND command-name)
# 当 command-name 是可调用的命令时为真
if (DEFINED variable)
# 当 variable 已经被设置了值时为真
if (EXISTS file-name)
if (EXISTS directory-name)
# 当指定的文件或者目录时为真
if (IS_DIRECTORY name)
if (IS_ABSOLUTE name)
# 当 name 是目录或者是绝对路径是为真
if (name1 IS_NEWER_THAN name2)
# 当 name1 文件的修改时间比 name2 文件的修改时间要新时为真
if (variable MATCHES regex)
if (string MATCHES regex)
# 当给定的变量或者字符串与给定的正则表达式相匹配时为真
选项 EQUAL, LESS 和 GREATER 可用来进行数值的比较, STRLESS, STREQUAL 和 STRGREATER 可用于进行字典序的比较, VERSION_LESS, VERSION_EQUAL 和 VERSION_GREATER 能用于 major[.minor[.patch[.tweak]]] 形式的版本比较,这些选项也可以进行组合得到更强的判断条件。
CMake 定义了条件语句的执行顺序:括号里的语句会首先被执行,然后是执行 EXISTS, COMMAND, DEFINED 以及类型的前置运算符,接着是执行 EQUAL. LESS, GREATER, STREQUAL, STRLESS, STRGREATER 和 MATCHES 运算符, NOT 运算符会接着被评估, 最后是执行 AND 和 OR 运算。同一级别的运算符则是按照从左到右的顺序执行,只有所有的表达式被执行了才会评估最终结果。CMake 会将 ON, 1, YES, TRUE, Y 看作是真值,将 OFF, 0, NO, FALSE, N, NOTFOUND, *-NOTFOUND, IGNORER 看作是假值。
循环结构
foreach 用来重复执行列表中的一组命令,foreach 中的第一个参数是变量的名字用来在每次循环中取不同的值。foreach 循环可以进行嵌套,并且循环变量比其他变量有更高的展开优先级,因此在 foreach 的循环体里可以使用循环变量构建变量名。
foreach (foo list)
# do something here
endforeach (foo)
while 命令根据一个条件变量来提供循环功能。while 命令中的条件变量和之前的 if 命令是一致的。
while (foo)
# do something here
endwhile ()
过程定义
macro 和 function 命令能够重复执行 CMakeLists 文件中比较分散的功能。在一个 macro 或者 function 定义后,就可以被任何在它定义之后处理的 CMakeLists 文件中使用。在macro 和 function 中有一些预定义的标准参数,ARGV0, ARGV1 分别表示 macro 和 function 传入的第一个参数和第二个参数。ARGV 是 macro 和 function 所有参数的一个列表,ARGN 是表示传入 macro 和 function 的在正式定义参数之后传入的不定形式的参数的列表。
CMake 中的 function 与 C 或者是 C++ 中的函数很像,可以传递参数到 function 中当作变量使用。function 中,一些例如 ARGC, ARGV, ARGN, ARGV0, ARGV1 的标准变量已经被定义。在 function 中是一个新的变量作用域, 与使用 add_subdirectory 命令进入子目录产生的新变量作用域一样,所有在调用 function 时已定义的变量在 function 中仍然被定义,但是对变量的修改以及定义新的变量只在这个 function 中起作用, 函数返回后这些变量就被移除了。定义 function 时,第一个参数是 function 的名称,剩余的其他参数都会传递到 function 中当变量使用。
function (name argv1 argv2)
# do something here
set (${argv1} "val" PARENT_SCOPE)
endfunction ()
在 function 中,使用带 PARENT_SCOPE 选项的 set 命令可以修改父作用域的中变量的值。
macro 的定义和调用方式与 function 一样,但与 function 最主要的不同是 macro 不会产生新的变量作用域。 macro 中的参数不是当作变量,而是当作在执行前进行替换的字符串。这与 C 和 C++ 中, 函数与宏定义的区别是一样的。macro 的第一个参数是创建的 macro 的名称,剩余的其他参数是 macro 的中的参数。
macro (name argv1 argv2)
# do something here
endmacro (name)
macro 也支持使用参数列表来进行宏替换,参数可以使用 ARGC, ARGV0, ARGV1 等来进行引用, 其含义与 function 中的一样。
4 正则表达式
正则表达式是用来进行字符匹配的字符串,可以由标准的数字字母以及正则表达式元字符组成。正则表达式的元字符有:
^ 匹配一行或者字符串的开头
$ 匹配一行或者字符串的结尾
. 匹配除新行以为的任何字符
[] 匹配方括号内的任何字符
[^] 匹配不在方括号内的任何字符
[-] 匹配横杠两端字符范围内的任何字符
* 匹配正在处理的模式零次或多次
+ 匹配正在处理的模式一次或多次
? 匹配正在处理的模式零次或一次
() 保存一个匹配的表达式并用于后面的替换操作
(|) 匹配竖线左边或者右边
5 检查CMake的版本
CMake 随着新版本的发布会引入一些新的特性,当你需要使用当前版本 CMake 包含而之前的版本不包含的的命令时,可以有几种方法进行处理。一种方案是使用 if 命令来检查新的命令是不是存在:
# 测试命令是否存在
if (COMMAND new_command)
# use the new_command do something here
endif (COMMAND new_command)
如果需要更多的信息,可以使用 CMAKE_VERSION 变量来获取实际的 CMake 版本进行处理:
# 查找更新版本的CMake
if (${CMAKE_VERSION} VERSION_GREATER 2.8.0)
# do something special here
endif ()
在编写 CMakeLists 文件时可能会不想支持一些旧版本的CMake,同在 CMakeLists 文件的最顶点放置如下命令可以做到:
cmake_minimum_required (VERSION 2.2)
如果 CMake 版本比上述命令指定的要低时,则会产生一条错误的信息来指示项目需要的最低版本的 CMake。
6 使用模块
CMake 通过使用模块的方式来支持代码重用。模块是一段在一个文件中的 CMake 命令, 它们可以通过 include 命令包含进其他的 CMakeLists 文件中。一个模块的位置可以通过绝对路径来指定,也可以让 CMake 自己查找。CMake 会在 CMAKE_MODULE_PATH 指定的文件里查找模块。如果没有找到 CMake 就会在自己的模块子目录下查找。模块可以主要分为以下的三类:
(1) Find Modlues
这些模块决定了软件元素的位置,如头文件和库。CMake 包含很多的 Find Modlues,Find Modlues 用来定位软件元素的的位置,如果没有找到,它们会提供一个缓存记录使得用户可以设置它们的需要的值。
find_path 命令用来定位头文件的位置,命令的第一个参数是用来存储结果的变量名称,第二个参数是需要查找的头文件名称,剩余的参数是用来寻找头文件的路径:
find_path (VARIABLE_NAME includefile path1 path2)
当头文件没有找到时,VARIABLE_NAME 变量会被设置为 VARIABLE_NAME-NOTFOUND。
find_library 命令用来寻找真正的库文件,这个命令执行了额外的检查来寻找一个合适的的库名称,如在 linux 系统中会在名称前添加 lib,在结尾添加 .so。find_library 命令的调用方式与 find_path 命令类似:
find_path (VARIABLE_NAME libraryfile path1 path2)
在 CMake 中寻找模块的基本文件结构如下:
find_path (MODULE_INCLUDE_DIR header.h path1 path2)
find_library (MODULE_LIBRARY library path1 path2)
if(MODULE_LIBRARY)
if(MODULE_INCLUDE_DIR)
set (MODULE_FOUND "YES")
endif()
endif()
(2) System Introspection Modules
这些模块提供了目标平台或编译器的相关信息,如 float 的长度,支持 ANSI C++ 流等。它们的模块名称通常有 Test 或者 Check 的前缀。为了产生正确的编译结果,大多数的 System Introspection Modules 参与到代码的编译过程中,它们的源代码通常有和模块一样的名称,并以 .c 或 .cxx 后缀结尾。
(3) Utility Modlues
Utility Modlues 模块提供了额外的功能,如支持一个 CMake 工程依赖另一个的的情形。CMake 包含了一些 Utility Modlues 用来帮助 CMake 的使用。CMakeExportBuildSetting 和 CMakeImportBuildSetting 提供了工具用来区分两个用相同的编译器和关键标志进行编译的 C++ 项目。CMakePrintSystemInformation 模块用来打印 CMake 的关键设置来帮助调试。
最后
以上就是精明毛巾为你收集整理的CMake 基本语法(Mastering CMake 笔记)的全部内容,希望文章能够帮你解决CMake 基本语法(Mastering CMake 笔记)所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复