CMake初步(3)

来源:互联网 发布:贝塔系数的算法 编辑:程序博客网 时间:2024/05/23 19:19
 
1.4 词法和语法

在开始本节的学习之前,我们先总结一下之前所了解到的CMake基本词法和命令。

CMake命令通常使用如下的格式:
  1. COMMAND( ARG1 ARG2 … )
复制代码
命令关键字之后使用括号来包含所有的参数;各个参数之间使用空格或者换行符分隔;而参数通常有以下几种形式:

变量,以${MY_VAIRABLE}的形式表达,其储存类型为字符串类型,但是可以根据具体命令的要求自动转换为布尔型、整型或者浮点类型。变量可以出 现在字符串中,也可以实现“内省”。变量有用户自定义和系统内置两种,用户自定义变量使用SET命令设置;而系统变量由系统自动赋值,例 如${PROJECT_SOURCE_DIR}。

枚举量,例如ADD_LIBRARY可以设置要生成的链接库为SHARED或者STATIC,还可以设置为MODULE(插件,可动态调用,但不作为其他工程的依赖),除此之外的赋值都是不能被识别的。

值,也就是任意的字符串内容,它可以用来指示要编译的源代码文件名,也可以表达一段文字提示信息,或者表达特定的功能。值可以使用引号进行标识,多数情况下也可以不用。

前文中我们已经了解到的命令列举如下,此外这里还简要地介绍了另一些可能在各类CMake工程中遇到的命令及其语法格式。CMake部分命令的语法歌是十分复杂,这里仅仅介绍它的某一种实现形式,建议读者阅读CMake的帮助文档以获取更多信息。

括号中为该命令的一个或多个参数项,其中使用“[…]”包含的项表示可忽略项,使用“…|…”分隔的项表示只能选择其中一项。

ADD_CUSTOM_COMMAND(
TARGET name
PRE_BUILD|PRE_LINK|POST_BUILD
COMMAND cmd1 [COMMAND cmd2 …] ):
为目标工程name添加一个或多个新的自定义的编译规则cmd1,cmd2等,执行时机可以选择编译前,链接前或者编译后。它的作用相当于Visual Studio工程的“Custom Build Step”属性。

ADD_CUSTOM_TARGET( name COMMAND cmd1 [COMMAND cmd2 …] ):
添加一个名为name的编译目标,并指定一个或多个自定义的命令cmd1,cmd2等。注意ADD_CUSTOM_COMMAND与这个命令的区别:前者 是针对一个已有的子工程进行自定义编译规则的设置;后者则是建立一个新的自定义的目标工程,例如一个专用于将已生成文件拷贝到指定文件夹的INSTALL 工程;以及与之作用截然相反的UNINSTALL工程。

ADD_DEFINITIONS( -DMACRO1 –DMACRO2 … ):
添加-D预编译宏定义,可以一次添加多个。

ADD_EXECUTABLE( name [WIN32]
source1 source2 … ):
指定一个名为name的可执行程序工程,其源文件为source1,source2等,此外还可以追加一个枚举量WIN32,表示此程序为Win32程序,使用WinMain作为程序入口。

ADD_LIBRARY( name [STATIC|SHARED|MODULE]
source1 source2 … ):
指定一个名为name的链接库工程,其源文件为source1,source2等,此外还可以指示该工程的生成结果为静态库(STATIC),动态库(SHARED)还是模块(MODULE)。

ADD_SUBDIRECTORY( dir ):
指示下一级CMake脚本所在位置位于dir子目录。

CMAKE_MINIMUM_REQUIRED( VERSION major[.minor[.patch]] ):
指示当前脚本所需的CMake版本,不能低于版本号major.minor.patch。

CONFIGURE_FILE( infile outfile ):
将文件infile复制到outfile的位置,同时执行其中变量的自动配置和更替,即,将infile中所有形同“${VAR}”和“@VAR@”的变量替换为对应的内容,并拷贝到outfile中,而这个新生成的outfile文件也可以在之后的脚本代码中得以使用。

FIND_LIBRARY( ${var}
NAMES name1 [name2 …]
PATHS path1 [path2 …]
PATH_SUFFIXES suffix1 [suffix2 …] ):
搜索一个外部的链接库文件,并将结果的全路径保存到var变量中。要搜索的链接库文件名字可能是name1,name2等;搜索路径为 path1,path2等;此外还可以指定路径的后缀词为suffix1,suffix2等。因此,系统将尝试在path1/suffix1,path1 /suffix2,path2/suffix1,path2/suffix2这些目录中搜索名为name1或name2的链接库文件,并将结果(路径和文 件名)保存到var中。

FIND_PACKAGE( name ):
在指定的模块目录中搜索一个名为Find<name>.cmake(例如,FindOSG.cmake)的CMake脚本模块文件,执行其中的内容,以图搜索到指定的外部依赖库头文件和库文件位置。

FIND_PATH( ${var}
NAMES name1 [name2 …]
PATHS path1 [path2 …]
PATH_SUFFIXES suffix1 [suffix2 …] ):
搜索一个包含name1,name2等文件的目录,并将此路径(不包含文件名)保存到var变量中,搜索路径为path1,path2等;此外还可以指定路径的后缀词为suffix1,suffix2等。通常可以使用此命令来确认外部头文件的依赖路径。

FIND_PROGRAM( ${var}
NAMES name1 [name2 …]
PATHS path1 [path2 …]
PATH_SUFFIXES suffix1 [suffix2 …] ):
搜索一个外部的可执行程序,并将结果的全路径保存到var变量中。要搜索的程序名字可能是name1,name2等;搜索路径为path1,path2等;此外还可以指定路径的后缀词为suffix1,suffix2等。

INCLUDE( file ):
在当前文件中包含另一个CMake脚本文件的内容。

INCLUDE_DIRECTORIES( dir1 dir2 … ):
指定编译器搜索头文件的依赖路径,可以添加多个。

INSTALL( TARGETS proj1 proj2
RUNTIME DESTINATION runtime_dir
LIBRARY DESTINATION library_dir
ARCHIVE DESTINATION archive_dir):
这只是此命令的一种语法格式,安装目标工程proj1,proj2等到指定的文件夹。其中,可执行文件安装到RUNTIME DESTINATION指定的runtime_dir目录;动态链接库安装到LIBRARY DESTINATION指定的library_dir目录;静态链接库安装到ARCHIVE DESTINATION指定的archive_dir目录。如果需要安装头文件或者数据文件,则通常使用INSTALL( FILES … DESTINATION … )的形式。

LINK_DIRECTORIES( dir1 dir2 … ):
设置外部依赖库的搜索路径。

MESSAGE( [SEND_ERROR|STATUS|FATAL_ERROR] “text” … ):
在控制台或者对话框输出一行或多行调试信息文本text,枚举量用于控制信息的类型(错误,状态显示,致命错误)。

OPTION( ${var} “text” value ):
向用户提供一个可选项,提示信息为text,初始值为value,并将最终的结果传递到var变量中。在CMake-GUI中它将以配置选项的方式出现。

PROJECT( name ):
设置整个工程的名称为name。

SET( variable value
[CACHE FILEPATH|PATH|STRING|BOOL “text”] ):
定义一个用户自定义变量variable,取值为value。此外还可以使用CACHE关键字,允许用户在CMake-GUI中修改变量的值,修改方式包 括文件对话框(FILEPATH),目录对话框(PATH),编辑框(STRING)或者复选框(BOOL),并使用text作为提示信息。

SET_TARGET_PROPERTIES( name PROPERTIES prop value ):
设置名为name的工程的属性,这里主要可选的prop属性包括PROJECT_LABEL, DEBUG_POSTFIX,OUTPUT_NAME等等,value为设置值。

TARGET_LINK_LIBRARIES( name
lib1 lib2 …
[debug|optimized] lib1 lib2 … ):
指定工程name所用的依赖库,并可以使用debug和optimized关键字分别指定DEBUG与RELEASE版本所用的一个或多个依赖库。
作者: array    时间: 2009-3-15 14:37

也许您并不一定完全明白这里所说的每一句话,这也是我们之所以把词法和语法的介绍放在“Hello World”例子之后的一个原因——没错,仅仅是这些单词的罗列未免太枯燥了。那么,为什么不马上拾起我们刚刚才完成的简单脚本工程,在上面添砖加瓦一番 呢?说不定这才是您充分理解和深入学习CMake的关键呢。

是的,实践才是最好的老师。要充分理解CMake的强大之处,以及确保自己具备足够的力量去阅读OSG的CMake脚本源代码,势必还要再多做一些更为复 杂的练习才行。不过在此之前,我们还是再多了解一些CMake的常用内置变量和脚本命令,以及CMake中条件语句,循环语句和宏函数的概念用法。

包括前文介绍的PROJECT_SOURCE_DIR在内,以下内置全局变量都可以在CMake脚本中以“${…}”的形式直接加以应用,以方便脚本代码的定位和功能实现:

  • CMAKE_BUILD_TYPE:工程的编译生成的版本类型,可选项包括Debug,Release,RelWithDebInfo和MinSizeRel。
  • CMAKE_COMMAND:也就是CMake可执行文件本身的全路径,例如/usr/local/bin/cmake或者C:\Program Files\CMake 2.6\bin\cmake.exe。
  • CMAKE_DEBUG_POSTFIX:Debug版本生成目标的后缀,通常可以设置为“d”字符,例如Debug版本的OSG核心库为osgd.dll,而Release版为osg.dll。
  • CMAKE_GENERATOR:编译器名称,例如“Unix Makefiles”,“Visual Studio 7”等。
  • CMAKE_INSTALL_PREFIX:工程安装目录,所有生成和调用所需的可执行程序,库文件,头文件都会安装到该路径下,Unix/Linux下默认为/usr/local,Windows下默认为C:\Program Files。
  • CMAKE_MODULE_PATH:设置搜索CMakeModules模块(.cmake)的额外路径。
  • PROJECT_BINARY_DIR:工程生成工作所在的目录,即前文所述的“out-of-source”的目录;对于“in-source”形式的编译工作,该变量与PROJECT_SOURCE_DIR所指向的目录相同。
  • PROJECT_NAME:工程名称,即使用PROJECT命令设置的名称。
  • PROJECT_SOURCE_DIR:工程源代码文件所在的目录。
  • CYGWIN:标识当前系统是否为Cygwin。
  • MSVC:标识当前系统是否使用Microsoft Visual C。
  • UNIX:标识当前系统是否为Unix系列(包括Linux,Cygwin和Apple)。
  • WIN32:标识当前系统是否为Windows及Win64。


不必担心这里介绍的命令和变量太多,也不必担心它们会很快消失在您的脑海深处。下一节我们将尝试创建一个稍微复杂一些的工程VersionMe,并争取将上文涉及到的大部分命令和内置变量派上用场,以求在实战中让您领略到CMake的强大魅力。

不过在结束枯燥的本章之前,我们还需要介绍一下CMake中重要的条件语句语法,循环语句语法和宏函数。它们分别相当于C程序中的 if…else,while/for以及函数的作用,并且条件和循环语句都可以嵌套工作。毫无疑问,它们在脚本语言的流程控制过程中必然不可或缺。

CMake中的条件语句基本格式为:
  1. IF( expression )
  2. ELSE( expression )
  3. ENDIF( expression )
复制代码
或者,
  1. IF( expression1 )
  2. ELSEIF( expression2 )
  3. ELSE()
  4. ENDIF()
复制代码
这 里的expression是判断条件,和C/C++类似,CMake的条件也存在“与/或/非”以及“等于/大于/小于”等几种操作符,分别用AND /OR/NOT以及EQUAL/LESS/GREATER来表示。当判断条件为真,执行IF后的命令段,否则继续判断并执行相应条件对应的命令段,或者不 执行任何操作。例如:
  1. IF ( ${number} GREATER 4 )
  2. ENDIF( ${number}GREATER 4 )
复制代码
表示判断变量number是否大于4,进而执行对应的语句段。此时用户定义的字符串变量会被自动转换为整型变量以便进行判断。
此外形同这样的判断语句也是十分常见的:
  1. IF ( NOT ${variable} )
  2. ENDIF( NOT ${variable} )
复制代码
如果变量variable的值为空,0,N,NO,OFF,FALSE,NOTFOUND这几种之一的话,则认为此变量表示“假”,即此处的“NOT ${variable}”为真。

CMake中的循环语句基本格式为:
  1. FOREACH( var arg1 arg2 … )
  2. ENDFOREACH( var )
复制代码
这里设置一个循环的局部变量var,每次将其赋为arg1,arg2等变量(或者变量数组)中的一个值,并执行循环中的命令段。例如:

FOREACH( var ${OPENGL_LIBRARIES} ),它表示将局部变量var每次设置为变量OPENGL_LIBRARIES中的一个值。后者的内容可能为“opengl32.lib;glu32.lib”的形式。

CMake自动将分号分隔的字符串认为是数组,因此会自动从该变量中择取var的取值。

另一种表达循环语句的语法格式为:
  1. WHILE ( expression )
  2. ENDWHILE( expression )
复制代码
这里的expression和IF语句判断字段中的含义相同。

CMake中的宏函数可以理解为C语言的函数,它改变代码执行跳转的流程并简化了脚本程序的开发,其基本格式为:
  1. MACRO( funcname [arg1 [arg2 …]] )
  2. ENDMACRO( funcname )
复制代码
和函数的编写要求一样,CMake的宏函数必须指定一个函数名funcname,以及零个或多个输入参数arg1,arg2等。需要调用宏函数的时候,只要直接使用funcname(arg1 arg2)的形式就可以了,例如:
  1. MACRO( MY_FUNC arg1 arg2 )
  2. ENDMACRO( MY_FUNC )
复制代码
在主程序中,需要调用此宏函数时,只需执行形同下面的语句:
  1. MYFUNC( param1 param2 )
复制代码
就可以将实际参数param1和param2传入宏函数体。

此外,宏函数体内可以使用内置变量${ARGC},${ARGV}和${ARGN}来表达传入参数的属性:${ARGC}保存了传入参数的个 数;${ARGV}保存一个传入参数组成的数组,可以供FOREACH语句使用;${ARGN}则比较特殊,它保存了“显式参数”之外的所有“隐式参数” 所组成的数组。对于上面的例句来说,arg1,arg2就是显式参数,而如果用户在调用MY_FUNC时采用下面的形式:
  1. MYFUNC( param1 param2 other1 other2 … )
复制代码
那么other1,other2等就是隐式参数,可以用${ARGN}来获取它们的数组。这对于CMake而言是完全合法的,并且可以因此定义不定参数项的宏函数,从而大大增强了脚本程序的灵活性。

转自:http://bbs.osgchina.org/viewthread.php?action=printable&tid=1229
原创粉丝点击