cmake使用教程(七)-流程和循环

13,248 阅读5分钟

【cmake系列使用教程】

cmake使用教程(一)-起步

cmake使用教程(二)-添加库

cmake使用教程(三)-安装、测试、系统自检

cmake使用教程(四)-文件生成器

cmake使用教程(五)-cpack生成安装包

cmake使用教程(六)-蛋疼的语法

cmake使用教程(七)-流程和循环

cmake使用教程(八)-macro和function

这个系列的文章翻译自官方cmake教程:cmake tutorial

示例程序地址:github.com/rangaofei/t…

不会仅仅停留在官方教程。本人作为一个安卓开发者,实在是没有linux c程序开发经验,望大佬们海涵。教程是在macos下完成,大部分linux我也测试过,有特殊说明的我会标注出来。本教程基于cmake-3.10.2,同时认为你已经安装好cmake。

流程判断

cmake中的流程判断相对简单,与c语言接近。

形式如下:

if(expression)
  # then section.
  COMMAND1(ARGS ...)
  COMMAND2(ARGS ...)
  #...
elseif(expression2)
  # elseif section.
  COMMAND1(ARGS ...)
  COMMAND2(ARGS ...)
  #...
else(expression)
  # else section.
  COMMAND1(ARGS ...)
  COMMAND2(ARGS ...)
  #...
endif(expression)

此处有一点注意:else和endif中的表达式是可以省略的,看到现在大部分网上的教程有必须加SET(CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS ON)这句才能省略表达式,一脸懵逼,关于那个变量,我也查不到api,包括废弃的,所以我都不写这句。

if表达式可以用长表达式,优先级顺序如下:

> EXISTS, COMMAND, DEFINED 
> EQUAL, LESS, LESS_EQUAL, GREATER, GREATER_EQUAL, STREQUAL, STRLESS, STRLESS_EQUAL, STRGREATER, STRGREATER_EQUAL, VERSION_EQUAL, VERSION_LESS, VERSION_LESS_EQUAL, VERSION_GREATER, VERSION_GREATER_EQUAL, MATCHES
> NOT,AND,OR

别看下面这张表很长,其实没什么内容:

表达式 true false 说明
<constant> 1, ON, YES, TRUE, Y,或者是非0数字 0, OFF, NO, FALSE, N, IGNORE, NOTFOUND,空字符串,或者带-NOTFOUND后缀 布尔判断值大小写不敏感
<variable|string> 已经定义且不是false的变量 未定义或者是false的变量 变量就是字符串
<NOT expression> expression为false expression为true
AND 两个条件全部成立 至少有一个为假
COMAND command-name 已经定义的command,macro或者function 未定义
POLICY policy-id policy存在 policy不存在 形式为CMP
TARGET target-name 已经用add_executable(), add_library(), or add_custom_target()定义过的target 未定义
TEST test-name add_test()创建过的测试名称 未创建
EXISTS path-to-file-or-directory 文件或者路径存在 文件或者路径不存在 此处是全路径
file1 IS_NEWER_THAN file2 file1的时间戳大于file2的时间戳
其中一个文件不存在
两个文件时间戳相同
其他情况 文件路径必须是全路径
IS_DIRECTORY path-to-directory 给定的变量是文件夹 不是文件夹 全路径
IS_SYMLINK file-name 变量是链接 不是 全路径
IS_ABSOLUTE path 是绝对路径 不是
<variable|string> MATCHES regex 正则表达式匹配成功 匹配失败
<variable|string> LESS <variable|string> 给定的变量是数字并且左边小于右边 左边大于右边 用于比较数字的大小
LESS:小于
GREATER:大于
EQUAL:等于
GREATER_EQUAL:大于等于
LESS_EQUAL:小于等于
<variable|string> STRLESS <variable|string> 按字典顺序左边小于右边 左边大于右边 用于比较字符串
LESS:小于
STRGREATER:大于
STREQUAL:等于
STRLESS_EQUAL:小于等于
STRGREATER_EQUAL:大于等于
<variable|string> VERSION_LESS <variable|string> 左边的版本号小于右边的版本号 大于 用于版本号的比较
LESS:小于
VERSION_GREATER:大于
VERSION_EQUAL:等于
VERSION_LESS_EQUAL:小于等于
VERSION_GREATER_EQUAL:大于等于
<variable string> IN_LIST 右边的item中有左边 没有
DEFINED 已定义变量 未定义变量
(expr1) AND (expr2 OR (expr3)) 1为真且2或者3至少有一个为真 其他情况

在if条件表达式中,是不必用${var}来取变量的值的,系统会自动转换。例如设置两个变量,然后比较各种取值的情况:

set(var1 OFF)
set(var2 "var1")

if(var2)实际是判断var1是否为false; if(${var2})相当于if(var1),实际是判断OFF;

foreach循环

1. 第一种形式

foreach(loop_var arg1 arg2 ...)
  COMMAND1(ARGS ...)
  COMMAND2(ARGS ...)
  ...
endforeach(loop_var)

此处注意endforeach(loop_var)的变量最好不要省略,因为foreach循环是依靠变量来跳出循环的。

foreach和匹配endforeach之间的所有命令都会被系统记录而不被调用。 一旦找到了了endforeach,则会执行原来记录的命令。在循环的每次迭代之前,${loop_var}将被设置为具有列表中当前值的变量。

foreach(i 0 1 2 3)
    message(STATUS "current is ${i}")
endforeach(i)
    message(STATUS "end")
endforeach(i)

一个简单的循环,但是多了一个endforeach。看一下结果

➜  StepTest git:(master) ✗ cmake -P foreach.cmake
-- current is 0
-- current is 1
-- current is 2
-- current is 3
-- end
CMake Error at foreach.cmake:5 (endforeach):
  endforeach An ENDFOREACH command was found outside of a proper FOREACH
  ENDFOREACH structure.  Or its arguments did not match the opening FOREACH
  command.

报错了。没有匹配的foreach。

2. 第二种形式

foreach(loop_var RANGE total)

从0开始直到total结束(包含total)

foreach(i RANGE 3)
    message(STATUS "current is ${i}")
endforeach(i)

范围将会是0-3,查看一下结果:

➜  StepTest git:(master) ✗ cmake -P foreach.cmake
-- current is 0
-- current is 1
-- current is 2
-- current is 3

3. 第三种形式

foreach(loop_var RANGE start stop [step])

从start开始直到stop结束之间的值,可以设置步进值step。

foreach(i RANGE 0 3 1)
message(STATUS "current is ${i}")
endforeach(i)

输出结果和上面的一样.

注意一点:最后的结果不会大于stop值,步进值是浮点数时会被转为整形

4. 第四种形式

foreach(loop_var IN [LISTS [list1 [...]]]
                    [ITEMS [item1 [...]]])

也比较简单,多了LIST关键字来循环list。不多讲。

while循环

while(condition)
  COMMAND1(ARGS ...)
  COMMAND2(ARGS ...)
  ...
endwhile(condition)

注意endwhile中的条件最好不要省略。这个条件和if中的表达式是一样的规则。 循环形式和foreach循环类似,直到碰到endwhile才开始执行每一条指令。

在while和foreach循环中,取变量的值请用${var}。break和continue的用法基本与c一样,放心使用。

在实际项目中,经常使用option来和if搭配。 option使用比较简单:

option(<option_variable> "help string describing option"
       [initial value])

initial value只能使用ON或者OFF,假如未设定,默认为false。 cmake_dependent_option是cmake内置的一个module,用来生成依赖其他option的option,这个相当蛋疼。

看一个简单的例子:

include(${CMAKE_ROOT}/Modules/CMakeDependentOption.cmake)
option(USE_CURL "use libcurl" ON)
option(USE_MATH "use libm" ON)
cmake_dependent_option(DEPENT_USE_CURL "this is dependent on USE_CURL" ON "USE_CURL;NOT USE_MATH" OFF)
if(DEPENT_USE_CURL)
    message(STATUS "using lib curl")
else()
    message(STATUS "not using lib curl")
endif()

第一行包含了我们需要的依赖模块。

第二行第三行定义了两个optionUSE_CURL,USE_MATH全为ON

第四行定义了一个optionDEPENT_USE_CURL,后边紧跟的是它的说明 this is dependent on USE_CURL,再后边相当于一个三元判断式,假如USE_CURL;NOT USE_MATH为真时,取前边的值,否则取后边的值。

5-9行是一个if语句,用来输出我们想要的结果。

输出结果:

➜  StepTest git:(master) ✗ cmake -P optionc.cmake
-- not using lib curl