CMake脚本编写简单入门示例



原文地址:https://blog.csdn.net/onion_autotest/article/details/7222954

CMake是一个跨平台的编译配置工具,CMake运行时,有两种主要模式:执行脚本模式,和执行编译模式 。本文主要介绍CMake执行脚本模式,以及如何编写CMake脚本,并给出简单的示例。至于CMake时如何安装、配置等内容,网上到处都有资料可查,本文在此不做介绍。

一、CMake版“Hello World”

先来看一段示例:新建一个文本文件,"cmake.txt";在文件中输入内容:
  1. message("Hello world")  
message("Hello world")

保存文件,打开控制台,切换到cmake.txt所在的目录,输入以下命令:
  1. cmake -P cmake.txt  
cmake -P cmake.txt

回车后,可以看到控制台上打印出了"Hello World"字符。

好了,示例到此结束,我们来讲解一下。
首先,需要传递参数"-P" 才能切换到脚本模式下运行;
其次,CMake对于"-P"后面传入的文件名没有限制,可以为.txt,也可以为其他后缀,如".cmake",甚至没有后缀也可以。
不过建议是采用以".cmake"作为脚本文件的后缀,便于识别区分和管理。
然后,这里使用了CMake脚本中一个最基本、最常用的方法:"message()",
关于这个方法的详细使用可以参照CMake官方文档中的使用说明,
或者在控制台下输入"cmake --help-command message"查看帮助。

二、CMake脚本语法特性概览


1、注释。

CMake中使用"#"表示注释该行代码。


2、内置命令。

与其他语言编程语言不同的是,CMake脚本的语法中没有赋值操作,无论是赋值,还是比较、判断操作,都是通过内置命令来完成的,例如"set(),math()等"。并且CMake中的内置命令不区分大小写。
所有的内置命令调用形式为:
  1. command(arg1 arg2 arg3 ... argn)  
command(arg1 arg2 arg3 ... argn)

每个参数均以空格,或者分号分割。注:不建议使用分号分割参数

3、变量。

CMake中的变量无需声明,并且没有类型概念,这一点类似于python;
变量可以认为都是全局的,哪怕在一个宏中定义的变量,也可以在宏的外面被访问到;
所有的变量都是一个列表变量,下文在举例时会详细说明这一点;
CMake对于变量是大小写敏感的。

4、变量的引用。

在CMake中,有两种引用方式:对于变量值的引用,和直接引用这个变量本身,使用方式分别是:
${varName} 和 varName


三、CMake脚本中常见方法的使用及示例


1、赋值

CMake中所有的赋值操作都是通过这个命令完成的:
  1. set(variable value1 value2 value3 ... valueN)。  
set(variable value1 value2 value3 ... valueN)。

调用这个命令后,variable变量将是一个列表,其中包含值"value1,value2,...valueN"。具体使用范例见下:
  1. set(myVar "This is a test print!")  
  2. message("${myVar}")  
set(myVar "This is a test print!")
message("${myVar}")

此时,控制台中将打印出"This is a test print!"。


这里需要注意的是,message函数中的变量参数,可以写在引号中,并且带引号和不带引号时,有一点差异,下面再看一例:
  1. set(myvar "a" "b")  
  2. message("${myvar}")  
  3. message(${myvar})  
set(myvar "a" "b")
message("${myvar}")
message(${myvar})

此时,控制台中将分别打印出"a;b"和"ab"。这是因为,不带引号时,${myvar}是一个列表,包含了两个值,而message中相当于接收到了两个参数"a"、"b",因此输出"ab"。而带有引号时,引号中的内容整体将作为一个参数存在。


另一个比较特别的地方是,给变量赋值时,可以不用使用引号,例如:set(myvar a "b" c d)。


2、循环

CMake中的循环有两种:foreach()...endforeach()和while()...endwhile()。
while循环没什么特别的,会使用判断就会使用while循环,所以这里只讲一下foreach循环。
首先来看下面的例子:
  1. set(mylist "a" "b" c "d")  
  2. foreach(_var ${mylist})  
  3.     message("当前变量是:${_var}")  
  4. endforeach()  
set(mylist "a" "b" c "d")
foreach(_var ${mylist})
	message("当前变量是:${_var}")
endforeach()

以上是foreach最基本、最常见的用法,还有一种比较实用的方法是:foreach(loop_var RANGE start stop [step]) 。使用示例见下:
  1. set(result 0)  
  2. foreach(_var RANGE 0 100)  
  3.     math(EXPR result "${result}+${_var}")  
  4. endforeach()  
  5. message("from 0 plus to 100 is:${result}")  
set(result 0)
foreach(_var RANGE 0 100)
	math(EXPR result "${result}+${_var}")
endforeach()
message("from 0 plus to 100 is:${result}")

这里是演示了计算从0一直加到100的结果。注:math()是数学计算函数,具体使用方法可以参考官方帮助文档


好了,foreach循环的基本用法就是这些,其实在循环中,有一个方法是会经常用到的,它是:list()。
关于这个方法的几种常见用法见下示例:
  1. # list(LENGTH <list> <output variable>)  
  2. set(mylist a b c d )  
  3. list(LENGTH mylist _length)  
  4. message("列表中元素的个数为:${_length}")  
# list(LENGTH <list> <output variable>)
set(mylist a b c d )
list(LENGTH mylist _length)
message("列表中元素的个数为:${_length}")

  1. # 稍微综合一点的示例:  
  2. # 生成一个列表a,再将这个列表反序,得到列表b,然后将列表a、b中相应位置的元素合并  
  3. set(strList "a" "b" "c" "d")  
  4. set(reverList ${strList})  
  5. list(REVERSE reverList)  
  6. message("${reverList}")  
  7.   
  8. foreach(_var ${reverList})    
  9.     list(FIND strList ${_var} temp)  
  10.     list(GET reverList ${temp} reverItem)  
  11.     list(APPEND  result "${_var}${reverItem}")    
  12.     message("当前元素序号 :${temp}")  
  13. endforeach()  
  14. message("${result}")  
# 稍微综合一点的示例:
# 生成一个列表a,再将这个列表反序,得到列表b,然后将列表a、b中相应位置的元素合并
set(strList "a" "b" "c" "d")
set(reverList ${strList})
list(REVERSE reverList)
message("${reverList}")

foreach(_var ${reverList})	
	list(FIND strList ${_var} temp)
	list(GET reverList ${temp} reverItem)
	list(APPEND  result "${_var}${reverItem}")	
	message("当前元素序号 :${temp}")
endforeach()
message("${result}")


3、判断

CMake中,判断的用法如下:
  1. if(expression)  
  2.     # then section.  
  3.     COMMAND1(ARGS ...)  
  4.     COMMAND2(ARGS ...)  
  5.     ...  
  6.   elseif(expression2)  
  7.     # elseif section.  
  8.     COMMAND1(ARGS ...)  
  9.     COMMAND2(ARGS ...)  
  10.     ...  
  11.   else(expression)  
  12.     # else section.  
  13.     COMMAND1(ARGS ...)  
  14.     COMMAND2(ARGS ...)  
  15.     ...  
  16.   endif(expression)  
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)

相比于其他“正常的编程语言”,CMake中的判断可以说是比较变态的。
首先看看官方文档中的说明:对于表达式"if(<constant>)",True if the constant is 1, ON, YES, TRUE, Y, or a non-zero number. False if the constant is 0, OFF, NO, FALSE, N, IGNORE, "", or ends in the suffix '-NOTFOUND'. 
就是说,CMake中if语句中对于条件真假的判断,不完全是以变量的真假为标准的,有时还和变量名有关。
总之,使用if 时,要特别注意这一点。


下面来看一下 if 的具体使用示例:
  1. if(WIN32)  
  2.     message("this operation platform is windows")  
  3. elseif(UNIX)  
  4.     message("this operation platform is Linux")  
  5. endif()  
if(WIN32)
	message("this operation platform is windows")
elseif(UNIX)
	message("this operation platform is Linux")
endif()

其中,"WIN32"和"UNIX"均为CMake中的常量,具体用法可以参考官方文档。

4、宏、函数

同大多数脚本语言一样,CMake中也有宏和函数的概念,关键字分别为"macro"和"function",具体用法如下:
  1. macro(<name> [arg1 [arg2 [arg3 ...]]])  
  2.     COMMAND1(ARGS ...)  
  3.     COMMAND2(ARGS ...)  
  4.     ...  
  5. endmacro(<name>)  
  6. function(<name> [arg1 [arg2 [arg3 ...]]])  
  7.     COMMAND1(ARGS ...)  
  8.     COMMAND2(ARGS ...)  
  9.     ...  
  10. endfunction(<name>)  
macro(<name> [arg1 [arg2 [arg3 ...]]])
    COMMAND1(ARGS ...)
    COMMAND2(ARGS ...)
    ...
endmacro(<name>)
function(<name> [arg1 [arg2 [arg3 ...]]])
    COMMAND1(ARGS ...)
    COMMAND2(ARGS ...)
    ...
endfunction(<name>)


以简单的求和函数为例,我们来看宏的一个示例:
  1. macro(sum outvar)  
  2.     set(_args ${ARGN})  
  3.     set(result 0)  
  4.       
  5.     foreach(_var ${_args})  
  6.         math(EXPR result "${result}+${_var}")  
  7.     endforeach()  
  8.       
  9.     set(${outvar} ${result})  
  10. endmacro()  
  11.   
  12.   
  13. sum(addResult 1 2 3 4 5)  
  14. message("Result is :${addResult}")   
macro(sum outvar)
	set(_args ${ARGN})
	set(result 0)
	
	foreach(_var ${_args})
		math(EXPR result "${result}+${_var}")
	endforeach()
	
	set(${outvar} ${result})
endmacro()


sum(addResult 1 2 3 4 5)
message("Result is :${addResult}") 


上面是一段求和函数,我们来解读一下代码:
"${ARGN}"是CMake中的一个变量,指代宏中传入的多余参数。因为我们这个宏sum中只定义了一个参数"outvar",其余需要求和的数字都是不定形式传入的,所以需要先将多余的参数传入一个单独的变量中。当然,在这个示例中,第一行代码显得多余,因为似乎没必要将额外参数单独放在一个变量中,但是建议这么做。


对上面这个宏再进一步加强:如果我们想限制这个宏中传入的参数数目(尽管在这个宏中实际上是不必要的),那么可以将宏改写一下:
  1. macro(sum outvar)  
  2.     set(_args ${ARGN})  
  3.     list(LENGTH _args argLength)  
  4.     if(NOT argLength LESS 4) # 限制不能超过4个数字  
  5.         message(FATAL_ERROR "to much args!")  
  6.     endif()  
  7.     set(result 0)  
  8.       
  9.     foreach(_var ${ARGN})  
  10.         math(EXPR result "${result}+${_var}")  
  11.     endforeach()  
  12.       
  13.     set(${outvar} ${result})  
  14. endmacro()  
  15.   
  16.   
  17. sum(addResult 1 2 3 4 5)  
  18. message("Result is :${addResult}")   
macro(sum outvar)
	set(_args ${ARGN})
	list(LENGTH _args argLength)
	if(NOT argLength LESS 4) # 限制不能超过4个数字
		message(FATAL_ERROR "to much args!")
	endif()
	set(result 0)
	
	foreach(_var ${ARGN})
		math(EXPR result "${result}+${_var}")
	endforeach()
	
	set(${outvar} ${result})
endmacro()


sum(addResult 1 2 3 4 5)
message("Result is :${addResult}") 

而CMake中的函数("function")与宏唯一的区别就在于,函数不能像宏那样将计算结果传出来(也不是完全不能,只是复杂一些),并且函数中的变量是局部的,而宏中的变量在外面也可以被访问到,请看下例:
  1. macro(macroTest)  
  2.     set(test1 "aaa")  
  3. endmacro()  
  4.   
  5.   
  6. function(funTest)  
  7.     set(test2 "bbb")  
  8. endfunction()  
  9.   
  10.   
  11. macroTest()  
  12. message("${test1}")  
  13.   
  14.   
  15. funTest()  
  16. message("${test2}")  
macro(macroTest)
	set(test1 "aaa")
endmacro()


function(funTest)
	set(test2 "bbb")
endfunction()


macroTest()
message("${test1}")


funTest()
message("${test2}")

运行这段代码后,只会打印出一条信息"aaa",由此可以看到宏与函数的区别。

5、综合示例

最后我们来通过一个稍微复杂综合一点的宏来结束本文。
下面的这个宏是找出指定数值范围内全部素数,并输出。
  1. macro(GetPrime output maxNum)  
  2.     set(extArg ${ARGN})  
  3.     if(extArg)  
  4.         message(SEND_ERROR "To much args!")  
  5.     endif()  
  6.       
  7.     # 没有判断传入的变量是否为数字类型  
  8.     set(result)  
  9.     foreach(_var RANGE 2 ${maxNum})  
  10.         set(isPrime 1)  
  11.         math(EXPR upplimit ${_var}-1)         
  12.         foreach(_subVar RANGE 2 ${upplimit})  
  13.             math(EXPR _temp "${_var}%${_subVar}")  
  14.             if(_temp EQUAL 0)  
  15.                 set(isPrime 0)  
  16.                 break()  
  17.             endif()  
  18.         endforeach()  
  19.           
  20.         if(isPrime)  
  21.             list(APPEND result ${_var})  
  22.         endif()  
  23.     endforeach()      
  24.     set(output ${result})  
  25. endmacro()  
  26.   
  27.   
  28. GetPrime(output 100)  
  29. message("${output}")  

猜你喜欢

转载自blog.csdn.net/zp704393004/article/details/80244283