CMake学习总结
Cmake主要功能:
- 根据编译器产生相应编译器的源码编译环境
- 根据相应编译器编译程序
- 编译执行程序可安装到任何一指定路径
Cmake主要特点
- 开放源代码,使用类似BSD许可发布。
- 跨平台,并可生成native编译配置文件,在Linux/Unix平台,生成makefile,在苹果平台,可以生成xcode,在Windows平台,可以生成MSVC平台,可以生成MSVC的工程文件。
- 能够管理大型项目。
- 简化编译构建过程和编译过程。Cmake的工具链非常简单:cmake+make
- 高效率,主要因为Cmake的工具链中没有libtool。
- 可扩展,可以为cmake编写特定功能的模块,扩充cmake功能。
Cmake常用技巧
- 需要看make构建的详细过程,可以使用make VERBOSE=1或者VERBOSE=1 make来进行构建。
- Cmake的构建文件为CMakeLists.txt。
CMake基本指令
PROJECT指令
PROJECT指令语法为:PROJECT(projectname [CXX][C][Java]。
主要是指定工程名称和工程支持的语言,默认支持所有语言。
该指令隐式定义了两个cmake变量:_BINARY_DIR和_SOURCE_DIR,若采用内部编译,则这两个变量都是工程所在路径。同时Cmake系统也预定义了PROJECT_BINARY_DIR和PROJECT_SOURCE_DIR变量。
为统一起见,建议以后直接使用PROJECT_BINARY_DIR,PROJECT_SOURCE_DIR,即使修改了工程名称,也不会影响这两个变量。如果使用了_SOURCE_DIR,修改工程名称后,需要同时修改这些变量。SET指令
SET指令语法为:SET(VAR [VALUE] [CACHE TYPE DOCSTRING [FORCE])。
SET指令用来显示定义变量。MESSAGE指令
MESSAGE指令的语法为:MESSAGE([SEND_ERROR | STATUS | FATAL_ERROR] “message to display” …)。
这个指令用于向终端输出用户定义的信息,包含了三种类型:SEND_ERROR,产生错误,生成过程被跳过。SATUS,输出前缀为——的信息。FATAL_ERROR,立即终止所有camke过程。ADD_EXECUTABLE指令
ADD_EXECUTABLE(exe$(SRC_LIST))定义这个工程会生成一个文件名为exe的可执行文件,相关源文件是SRC_LIST中定义的源文件列表。
ADD_SUBDIRECTORY指令
ADD_SUBDIRECTORY指令语法为:ADD_SUBDIEECTORY(source_dir [binary_dir][EXCLUDE_FROM_ALL])。
这个指令用于向当前工程添加存放源文件的子目录,并可以指定中间二进制和目标二进制存放的位置。
EXCLUDE_FROM_ALL参数的含义是将这个目录从编译过程排除,如工程example,可能就需要工程构建完成后,再进入example目录单独进行构建。(也可以通过以来解决此类问题)SUBDIRS指令
SUBDIRS指令用法为:SUBDIRS(dir1 dir2 …),此指令不推荐使用。
它可以一次添加多个子目录,并且,即使外部编译,子目录体系仍然会被保存。ADD_LIBARAY指令
ADD_LIBRARY(libname [SHARED|STATIC|MODULE] [EXECLUDE_FROM_ALL],其中,SHARED:动态库,STATIC:静态库,MODULE为在使用dyld的系统有效,如果不支持dyld,则被当作SHARED对待。EXCLUDE_FROM_ALL参数的意思是这个库不会默认构建,除非有其他组件依赖或者手工构建。
SET_TARGET_PROPERTIES指令
SET_TARGET_PROPERTIES基本语法:SET_TARGET_PROPERTIES(target1 target2 … PROPRERTIES prop1 value1 prop2 values …)
这条指令可以用来设置输出的名称,对于动态库,还可以用来指定动态库版本和API版本。
SET_TARGET_PROOERTIES(hello PROPERTIES VERSION 1.2 SVOERSION 1)VERSION指代动态库版本,SOVERSION指代API版本。INCLUDE_DIRECTORIES指令
INCLUDE_DIRECTORIES指令语法为:INCLUDE_DIRECTORIES([AFTER|BEFORE] [SYSTEM] dir1 dir2 …)
该指令用于向工程中添加多个特定的头文件搜索路径,路径指令用空格分割,如果路径中包含了空格,可以使用双引号将它括起来,默认的行为是追加到当前的头文件搜索路径的后面。
可以通过两种方式进行控制搜索路径添加的方式:
1)CMAKE_INCLUDE_DIRECTIONS_BEFORE,通过SET这个cmake变量为on,可以将添加的头文件的搜索路径放在已有路径的前面。
2)通过AFTER或者BEFORE参数,也可以控制追加还是置前。TARGET_LINK_LIBRARIES指令
TARGET_LINK_LIBRARIES语法为:TARGET_LINK_LIBRARIES(target library1<debug | optimized> library2 ...) 这个指令可以用来为target添加需要链接的共享库。
特殊环境变量
特殊环境变量CMAKE_INCLUDE_PATH和CMAKE_LIBRARY_PATH。这两个变量是环境变量但不是cmake变量。这两个变量主要用来解决autotools工程中-extra-include-dir等参数的支持。即如果头文件没有放在常规路径(/usr/include,/usr/local/include等),则可以通过这些变量进行弥补。也可以使用export CMAKE_INCLUDE_PATH=/usr/include/…
FIND_PATH用来指定路径中搜索文件名。如果不使用FIND_PATH,CMAKE_INCLUDE_PATH变量的设置是没有作用。
Cmake基本语法规则
- 变量使用${}方式取值,但是在IF控制语句中是直接使用变量名
- 指令(参数1 参数2 …)参数使用括弧括起,参数之间使用空格或分号分开。
- 指令是大小写无关的,参数和变量是大小写相关的,推荐全部使用大写指令。
- 可以使用make clean对构建结果进行清理。
Cmake内部编译和外部编译
- Cmake内部编译,它会生成一些无法自动删除的中间文件,故建议使用外部编译。
- 外部编译的好处为,对原有工程没有任何影响,所有动作全部发生在编译目录。外部编译的过程如下:
- 首先,先清除目录中原工程文件和CMakeLists.txt之外的所有中间文件,最关键的是CMakeCache.txt。
- 构建build目录
- 进入build目录,运行cmake <工程的全部路径>,查看build目录,就能够生成编译需要的Makefile和其他中间文件。
-运行make构建工程,就会在当前目录(build目录)中获得目标文件。
程序安装方法
- 从代码编译后直接make install安装。
打包时指定目录安装。安装指令具体如下:
INSTALL指令用法为: INSTALL指令用于定义安装规则,安装的内容包括目标二进制、动态库、静态库以及文件、目录、脚本等。 INSTALL指令包含了各种安装类型。 INSTALL(TARGETS targets ... [[ARCHIVE][LIBRARY][RUNTIME][DESTINATION <dir>][PERMISSIONS permissions...][CONFIGURATIONS][Debug][Release]...]][COMPONENT <component>][OPTIONAL]] 目标类型有三种:ARCHIVE特指静态库,LIBARAY特指动态库,RUNTIME:特指可执行目标二进制。DESTINATION:安装路径。
Cmake常用变量
- CMAKE_BINARY_DIR PROJECT_BINARY_DIR _BINARY_DIR:这三个变量指代的内容是一致的,如果是in source编译,指的就是工程顶层目录,如果时out-of-source编译,指的是工程编译发生的目录。
- CMAKE_SOURCE_DIR PROJECT_SOURCE_DIR _SOURCE_DIR:这三个变量指代的内容是一致的,不论采用何种编译方式,都是工程顶层目录。
- CMAKE_CURRENT_SOURCE_DIR:指的是当前处理的CMakeLists.txt所在的路径。
- CMAKE_CURRENT_BINARY_DIR:如果是in-source编译,它跟CMAKE_CURRENT_SOURCE_DIR一致,如果是our-of-source编译,它指的的target编译目录。使用ADD_SUBDIRECTORY(src bin)可以改变这个变量值。使用SET(EXECUTABLE_OUTPUT_PATH <新路径>)并不会对这个变量造成影响,它只是修改了最终目标文件存放的路径。
- CMAKE_CURRENT_LIST_FILE:输出调用这个变量的CMakeLists.txt的完整路径。
- CMAKE_CURRENT_LIST_LINE:输出这个变量所在的行
- CMAKE_MODULE_PATH:这个变量用来定义自己的cmake模块所在路径。如果工程比较复杂,可能需自己编写cmake模块,这些cmake模块随工程发布,为让cmake在处理CMakeLists.txt时,需要通过SET指令,将自己的cmake模块路径设置一下。
- EXECUTABLE_OUTPUT_PATH和LIBRARY_OUTPUT_PATH分别用来重新定义最终结果存放目录。
- PROJECT_NAME:返回通过PROJECT指令定义的项目名称。
Cmake调用环境变量方式
- 使用$ENV{NAME}指令就可以调用系统的环境变量。
- 设置环境变量的方式:SET(ENV{变量名} 值)
Cmake系统变量信息
- CMAKE_MAJOR_VERSION,CMAKE主版本号
- CMAKE_MINOR_VERSION,CMAKE次版本号
- CMAKE_PATCH_VERSION,CMAKE补丁等级
- CMAKE_SYSTEM,系统名称
- CMAKE_SYSTEM_NAME:不包含版本的系统名
- CMAKE_SYSTEM_VERSION:系统版本
- CMAKE_SYSTEM_PROCESSOR:处理器名称
- UNIX,在所有的类UNIX平台为TRUE,包括OS X和cygwin
- WIN32,在所有的win32平台为TRUE,包括cygwin
Cmake主要开关选项
- CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS,用来控制IF ELSE语句的书写方式
- BULID_SHARED_LIBS,用来控制默认的库编译方式,如果不进行设置,使用ADD_LIBRARY并没有设置库类型的情况下,默认编译生成的库都是静态库。如果SET(BUILD_SHARED_LIB\ ON)后,默认生成为动态库。
- CMAKE_C_FLAGS设置C编译选项,也可以通过指令ADD_DEFINITIONS()添加。
- CMKAE_CXX_FLAGS,设置C++编译选项,可以通过指令ADD_DEFINITIONS()添加。
Cmake常用指令
ADD_DEFINITIONS向C/C++编译器添加-D定义,比如:ADD_DEFINITIONS(-DENABLE_DEBUG_DABC),参数之间用空格分割。
ADD_DEPENDENCIES定义target依赖的其他target,确保在编译target之前,其他target已经被构建。
ADD_EXECUTABLE、ADD_LIBRARY、ADD_SUBDIRECTORY。
ADD_TEST与ENABLE_TESTING指令。ENABLE_TESTING指令迎来控制Makefile是否构建test目标,涉及工程所有目录。语法很简单,没有任何参数,一般情况下,这个指令放在工程的主CMakeList.txt中。
ADD_TEST指令的语法:ADD_TEST(testname Exename arg1 arg2 …)testname是自定义的test名称,Exename可以是构建的目标文件也可以是外部脚本等等。后面连接传递给可执行文件的参数。如果没有在任何同一个CMakeLists.txt中打开ENABLE_TESTING()指令,任何ADD_TEST都是无效的。
AUX_SOURCE_DIRECTORY基本语法是:AUX_SOURCE_DIRECTORY(dir VAEIABLE)作用是发现一个目录下所有源代码文件并将列表存储在一个变量中,这个指令被用来自动构建源文件列表。目前,cmake还不能自动发现新添加的源文件。
CMAKE_MINIMUM_REQUIRED语法为CMAKE_MINIMUM_REQUIRED(VERSION versionNumber [FATAL_ERROR])如果Cmake版本低于某一个值,则出现严重错误,整个过程终止。
EXEC_PROGRAM:在CMakeLists.txt处理过程中执行命令,并不会在生成Makefile中执行。具体语法为:EXEC_PROGRAME(Executable [directory in which to run][ARGS][OUTPUT_VARIABLE][RETURN_VALUE])用于在指定的目录运行某个程序,通过ARGS添加参数,如果要获取输出和返回值,可通过OUTPUT_VARIABLE和RETURN_VALUE分别定义两个变量,这个指令可以在CMakeLists.txt处理过程中支持任何命令,如根据系统情况去修改代码文件等。
FILE指令:文件操作指令,基本语法为:
- FILE(WRITE filename “message to write” …)
- FILE(APPEND filename “message to write” …)
- FILE (READ filename variable)
- FILE(GLOB variable [RELATIVE path][globbing expression]…)
- FILE(REMOVE [directory]…)
- FILE(REMOVE_RECURESE [directory]…)
- FILE(MAKE_DIRECTORY [directory]…)
- FILE(RELATIVE_PATH variable directory file)
- FILE(TO_CMAKE_PATH path result)
- FILE(TO_NATIVE_PATH path result)
INCLUDE指令,用来载入CMakeLists.txt文件,也用于载入预定义的cmake模块。INCLUDE(file1 [OPTIONAL]);INCLUDE(module [OPTIONAL])。OPTIONAL参数的作用是文件不存在也不会产生错误。可以指定载入一个文件,如果定义的是一个模块,那么将在CMAKE_MODULE_PATH中搜索这个模块并载入。载入的内容将在处理到INCLUDE语句时直接执行。
FIND_指令,主要包括以下:
- FIND_FILE(name1 path2 path2 …)VAR变量代表找到的文件全路径,包含文件名
- FIND_LIBRARY(name1 path1 path2 …)VAR变量表示找到的库全路径,包括库文件名
- FIND_PATH(name1 path1 path2 …)VAR变量代表包含这个文件的路径。
- FIND_PROGRAM(name1 path1 path2 …)VAR变量代表包含这个程序的全路径。
控制指令
IF指令,基本语法如下:
IF(expression)# THEN section.COMMOND1(ARGS ...) COMMAND2(ARGS ...) ... ELSE(expression)# ELSE section.COMMAND1(ARGS ...)COMMAND2(ARGS ...)... ENDIF(expression)
另一个指令是ELSEIF,总体把握一个原则,范式出现IF的地方,一定要有ENDIF,出现ELSEIF地方,ENDIF是可选的。表达式的使用方法如下:
IF(var),如果变量不是空,0,N,NO,OFF,FALSE,NOTFOUND或_NOTFOUND时,表达式为真。 IF(NOT var),与上面相反 IF(var1 AND var2),当两个变量都为真时为真。 IF(var1 OR var2),当两个变量其中一个为真时为真。 IF(COMMAND cmd),当给定的cmd确实是命令并可以调用为真。 IF(EXISTS dir)或者IF(EXISTS file),当目录名或者文件名存在时为真。 IF(file1 IS_NEWER_THAN file2),当file1比file2,或者file1/file2其中一个不存在时为真,文件名需使用完整路径。 IF(IS_DIRECTORY dirname),当dirname是目录时,为真。 IF(variable MATCHES regex)IF(string MATCHES regex)当给定的变量或者字符串能够匹配正则表达式regex时为真。
WHILE指令语法如下:
WHILE(condition)COMMAND1(ARGS ...)COMMAND2(ARGS ...)...ENDWHILE(condition)其真假判断条件可以参考IF指令。
FOREACH指令语法如下:
FOREACH(loop_var arg1 arg2 ...)COMMAND1(ARGS ...)COMMOND2(ARGS ...)...ENDFOREACH(loop_var) FOREACH(loop_var RANGE total)ENDFOREACH(loop_var)从0到total以1位步进。 FOREACH(loop_var RANGE start stop [step])ENDFOREACH(loop_var),从start开始到stop结束,以step为步进。 FOREACH指令遇到ENDFOREACH指令,整个语句块才会得到真正的执行。