上一篇学习笔记,笔者简单浏览了CMake官网提供的教程,但感觉并不系统,而且对很多指令并没有进行解释,所以只写了一半就放弃了……而且笔者英语也不好,索性还是直接在国内网站上找别人的总结,然后自己综合学一下吧……
参考地址: http://www.cnblogs.com/ph829/p/4759124.html http://blog.csdn.net/bytxl/article/details/50635016 http://blog.csdn.net/wzzfeitian/article/details/40963457/
1、每一个需要进行cmake操作的目录下面,都必须存在文件CMakeLists.txt 。 2、cmake指令不区分大小写。本文为了醒目,笔者把cmake指令都作大写处理。 3、变量使用${}方式取值,但是在 IF 控制语句中是直接使用变量名; 4、指令(参数 1 参数 2…),参数使用括弧括起,参数之间使用空格或分号分开;
该语句一般都可以放置在CMakeLists.txt的开头,用于说明CMake最低版本要求。 这行命令是可选的,我们可以不写这句话,但在有些情况下,如果CMakeLists.txt文件中使用了一些高版本cmake特有的一些命令的时候,就需要加上这样一行,提醒用户升级到该版本之后再执行cmake。
cmake_minimum_required (VERSION 2.6)
上述例程指cmake的最低版本至少为2.6。
格式:
PROJECT(name)
该指令一般置于CMakeLists.txt的开头,定义了工程的名称。但项目最终编译生成的可执行文件并不一定是这个项目名称,而是由另一条命令确定的,稍候我们再介绍。 执行了该条指令之后,将会自动创建两个变量:
project(CRNode)
执行了上一条指令,即定义了一个项目名称CRNode,相应的会生成两个变量:CRNode_BINARY_DIR, CRNode_SOURCE_DIR。 cmake中预定义了两个变量:PROJECT_BINARY_DIR与PROJECT_SOURCE_DIR。 在这个例子中: PROJECT_BINARY_DIR = CRNode_BINARY_DIR PROJECT_SOURCE_DIR = CRNode_SOURCE_DIR 笔者强烈推荐直接使用PROJECT_BINARY_DIR与PROJECT_SOURCE_DIR,这样及时项目名称发生了变化,也不会影响CMakeLists.txt文件。 关于上面两个变量是否相同的问题,涉及到编译方法是内部编译还是外部编译。如果是内部编译,则上面两个变量相同;如果是外部编译,则两个变量不同。此处对内部编译与外部编译做出介绍:
假设此时已经完成了CMakeLists.txt的编写,在CMakeLists.txt所在目录下,有两种执行cmake的方法:
cmake ./
make
以及:
mkdir build
cd ./build
cmake ../
make
第一种方法是内部构建,第二种方法是外部构建。上述两种方法中,最大不同在于cmake与make的工作路径不同。 内部构建方法中,cmake生成的中间文件和可执行文件都会存放在项目目录中;外部构建方法中,中间文件与可执行文件都存放在build目录中。 笔者强烈建议使用外部构建方法。优点显而易见:最大限度的保持了代码目录的整洁,生成、编译与安装是不同于项目目录的其他目录中,在外部构建方法下,PROJECT_SOURCE_DIR指向目录与内部构建相同,为CMakeLists.txt所在根目录;而PROJECT_BINARY_DIR不同,它指向CMakeLists.txt所在根目录下的build目录。
格式:
SET(VAR [VALUE] [CACHE TYPEDOCSTRING [FORCE]])
例:
SET(CMAKE_INSTALL_PREFIX /usr/local)
该例程中,我们显式的将CMAKE_INSTALL_PREFIX的值定义为/usr/local,如此在外部构建情况下执行make install命令时,make会将生成的可执行文件拷贝到/usr/local/bin目录下。 当然,可执行文件的安装路径CMAKE_INSTALL_PREFIX也可以在执行cmake命令的时候指定,cmake参数如下:
cmake -DCMAKE_INSTALL_PREFIX=/usr ..
如果cmake参数和CMakeLists.txt文件中都不指定该值的话,则该值为默认的/usr/local。
格式:
ADD_SUBDIRECTORY(source_dir [binary_dir] [EXCLUDE_FROM_ALL])
这个指令用于向当前工程添加存放源文件的子目录,并可以指定中间二进制和目标二进制存放的位置。EXCLUDE_FROM_ALL 参数的含义是将这个目录从编译过程中排除。比如,工程有时候存在example,可能就需要工程构建完成后,再进入example目录单独进行构建。
格式:
INCLUDE_DIRECTORIES([AFTER|BEFORE] [SYSTEM] dir1 dir2 ...)
向工程添加多个特定的头文件搜索路径,路径之间用空格分隔。类似于gcc中的编译参数-l
,即指定编译过程中编译器搜索头文件的路径。当项目需要的头文件不在系统默认的搜索路径时,则指定该路径。
AFTER/BEFORE参数,控制追加或置前。默认情况下,追加当前头文件搜索路径的后面。
注:如果路径包含空格,可以使用双引号将它括起来。
例:
INCLUDE_DIRECTORIES(/usr/include/thrift)
格式:
ADD_EXECUTABLE(exename srcname)
该命令给出源文件名称,并指出需要编译出的可执行文件名。
例1:
ADD_EXECUTABLE(hello ${SRC_LIST})
上述例程说明SRC_LIST变量中的源文件,需要编译出名为hello的可执行文件。
例2:
SET(SRC_LIST main.cc
rpc/CRNode.cpp
rpc/Schd_types.cpp
task/TaskExecutor.cpp
task/TaskMoniter.cpp
util/Const.cpp
util/Globals.cc
)
ADD_EXECUTABLE(CRNode ${SRC_LIST})
该例程中,定义了该工程会生成一个名为CRNode的可执行文件,所依赖的源文件是变量SRC_LIST定义的源文件列表。 注:如果前文PROJECT()指令中定义的项目名称也定义为CRNode,也没有什么问题,两者之间没有任何关系。
格式:
ADD_LIBRARY(libname [SHARED|STATIC|MODULE] [EXCLUDE_FROM_ALL] source1 source2 ... sourceN)
例:
ADD_LIBRARY(hello SHARED ${LIBHELLO_SRC})
EXECUTABLE_OUTPUT_PATH为生成可执行文件路径,LIBRARY_OUTPUT_PATH为生成库文件路径。 我们可以通过SET指令对其进行设置最终的目标二进制的位置,即最终生成的工程可执行文件与最终的共享库,而不包含编译生成的中间文件。 命令如下:
SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)
SET(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib)
注:指令ADD_EXECUTABLE, ADD_LIBRARY出现的位置,如果需要改变目标存放路径,就在该处添加上述定义。
格式:
LINK_DIRECTORIES(directory1 directory2 ...)
该指令用于添加外部库的搜索路径。
格式:
TARGET_LINK_LIBRARIES(target library1 <debug | optimized> library2 ..)
指定链接目标文件时需要链接的外部库,效果类似于gcc编译参数-L
,解决外部库依赖的问题。
向终端输出用户定义的信息或变量值; 格式:
MESSAGE([SEND_ERROR | STATUS | FATAL_ERROR] “message to display” …)
设置目标的某些属性,改变它们构建的方式。 格式:
SET_TARGET_PROPERTIES(target1 target2 ...
PROPERTIES prop1 value1 prop2 value2 ...)
该指令为一个目标设置属性,语法是列出所有用户想要变更的文件,然后提供想要设置的值。用户可以使用任何想用的属性与对应的值,并在随后的代码中调用GET_TARGET_PROPERTY命令取出属性的值。
影响目标输出文件的属性PROPERTIES
详述如下:
PREFIX覆盖了默认的目标名前缀(如lib); SUFFIX覆盖了默认的目标名后缀(如.so)。
与PREFIX, SUFFIX是等价的属性,但针对的是DLL导入库(即共享库目标)。
构建目标时,OUTPUT_NAME用来设置目标的真实名称。
为一个目标的链接阶段添加额外标志。 LINK_FLAGS_< CONFIG >将为配置< CONFIG >添加链接标志,如DEBUG, RELEASE, MINSIZEREL, RELWITHDEBINFO。
设置附加的编译器标志,在构建目标内的源文件时被用到。
改变链接可执行文件或共享库的工具。默认值是设置与库中文件相匹配的语言。 CXX与C是该属性的公共值。
VERSION指定构建的版本号,SOVERSION指定构建的API版本号。 构建或安装时,如果平台支持符号链接,且链接器支持so名称,那么恰当的符号链接将会被创建。如果只指定两者中的一个,缺失的另一个假定为具有相同版本号。 例1:
SET_TARGET_PROPERTIES(hello_static PROPERTIES OUTPUT_NAME "hello")
例2:
SET_TARGET_PROPERTIES(hello PROPERTEIES VERSION 1.2 SOVERSION 1)
该指令用于控制版本,VERSION指代动态库版本,SOVERSION指代API版本。
查找某个路径下的所有源文件,并将源文件列表存储到一个变量中。 格式:
AUX_SOURCE_DIRECTORY(< dir > < variable >)
例:
AUX_SOURCE_DIRECTORY(. SRC_LIST)
该指令将当前目录下的文件列表全部存入变量SRC_LIST中。
INSTALL命令可以按照对象的不同分为三种类型:目标文件、非目标文件、目录;
格式:
INSTALL(TARGETS targets...
[[ARCHIVE|LIBRARY|RUNTIME]
[DESTINATION < dir >]
[PERMISSIONS permissions...]
[CONFIGURATIONS
[Debug|Release|...]]
[COMPONENT < component >]
[OPTIONAL]
] [...])
${CMAKE_INSTALL_PREFIX}/< dir >
其余参数待笔者使用到再进行补充吧……
.sh脚本文件,即为典型的非目标文件的可执行程序。 格式:
INSTALL(PROGRAMS files... DESTINATION < dir >
[PERMISSIONS permissions...]
[CONFIGURATIONS [Debug|Release|...]]
[COMPONENT < component >]
[RENAME < name >] [OPTIONAL])
使用方法基本和上述目标文件指令的INSTALL相同,唯一别的不同是,安装非目标文件之后的权限为OWNER_EXECUTE, GOUP_EXECUTE, WORLD_EXECUTE,即755权限目录的安装。
格式:
INSTALL(DIRECTORY dirs... DESTINATION < dir >
[FILE_PERMISSIONS permissions...]
[DIRECTORY_PERMISSIONS permissions...]
[USE_SOURCE_PERMISSIONS]
[CONFIGURATIONS [Debug|Release|...]]
[COMPONENT < component >]
[[PATTERN < pattern > | REGEX < regex >]
[EXCLUDE] [PERMISSIONS permissions...]] [...])
例:
INSTALL(DIRECTORY icons scripts/ DESTINATION share/myproj
PATTERN "CVS" EXCLUDE
PATTERN "scripts/*" PERMISSIONS OWNER_EXECUTE OWNER_WRITE OWNER_READ GROUP_EXECUTE GROUP_READ)
该指令的执行结果是:
IF…ELSE…语法格式基本类似与C语言,大致如下:
IF (expression)
COMMAND1(ARGS ...)
COMMAND2(ARGS ...)
...
ELSE (expression)
COMMAND1(ARGS ...)
COMMAND2(ARGS ...)
...
ENDIF (expression)
其中,一定要有ENDIF与IF对应。
IF ("hello" MATCHES "ell")
MESSAGE("true")
ENDIF ("hello" MATCHES "ell")
例1: 判断平台差异:
IF(WIN32)
MESSAGE(STATUS "This is windows.")
ELSE(WIN32)
MESSAGE(STATUS "This is not windows.")
ENDIF(WIN32)
上述代码可以控制不同平台进行不同控制。
注:也许ELSE(WIN32)之类的语句阅读起来很不舒服,这时候可以加上语句:
SET(CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS ON)
这时候上述结构就可以写成:
IF(WIN32)
ELSE()
ENDIF()
例2: 配合ELSEIF使用,不同平台上的控制:
IF(WIN32)
#do something related to WIN32
ELSEIF(UNIX)
#do something related to UNIX
ELSEIF(APPLE)
#do something related to APPLE
ENDIF (WIN32)
语法结构如下:
WHILE(condition)
COMMAND1(ARGS ...)
COMMAND2(ARGS ...)
...
ENDWHILE(condition)
真假判断条件可以参考IF指令。
FOREACH有三种使用形式的语法,且每个FOREACH都需要一个ENDFOREACH()与之匹配。
FOREACH(loop_var arg1 arg2 ...)
COMMAND1(ARGS ...)
COMMAND2(ARGS ...)
...
ENDFOREACH(loop_var)
例:
AUX_SOURCE_DIRECTORY(. SRC_LIST)
FOREACH(F ${SRC_LIST})
MESSAGE(${F})
ENDFOREACH(F)
该例程中,现将当前路径下的所有源文件列表赋值给变量SRC_LIST,然后遍历SRC_LIST中的文件,并持续输出信息,信息内容是当前路径下所有源文件的名称。
FOREACH(loop_var RANGE total)
COMMAND1(ARGS ...)
COMMAND2(ARGS ...)
...
ENDFOREACH(loop_var)
例:
FOREACH(VAR RANGE 10)
MESSAGE(${VAR})
ENDFOREACH(VAR)
该例程从0到total(此处为10),以1为步进。此处输出为:012345678910
FOREACH(loop_var RANGE start stop [step])
COMMAND1(ARGS ...)
COMMAND2(ARGS ...)
...
ENDFOREACH(loop_var)
从start开始,到stop结束,以step为步进。 例:
FOREACH(A RANGE 5 15 3)
MESSAGE(${A})
ENDFOREACH(A)
此处输出为581114
差不多就这样吧…… 笔者总结了三篇文章中的cmake语法,现在也需要试着自己写cmake试一试了…… 下一次再写cmake学习笔记,应该就是把以前自己的项目用cmake配置一下试试了。 cmake学习结束之后,就是时候开始研究cmake生成的Makefile的语法了。