前面我们已经提到了,使用${}进行变量的引用。在 IF 等语句中,是直接使用变量名而不通过${}取值
主要有隐式定义和显式定义两种。
隐式定义的例子: PROJECT 指令,会隐式的定义
输出调用这个变量的 CMakeLists.txt 的完整路径
输出这个变量所在的行
返回通过 PROJECT 指令定义的项目名称。
生成 debug 版和 release 版的程序。
可以的取值是 Debug Release RelWithDebInfo 和 MinSizeRel。当这个变量值为 Debug 的时候,CMake 会使用变量CMAKE_CXX_FLAGS_DEBUG 和 CMAKE_C_FLAGS_DEBUG 中的字符串作为编译选项生成 Makefile,当这个变量值为 Release 的时候,工程会使用变量CMAKE_CXX_FLAGS_RELEASE 和 CMAKE_C_FLAGS_RELEASE 选项生成 Makefile。
现假设项目中只有一个文件 main.cpp ,下面是一个可以选择生成 debug 版和 release 版的程序的 CMakeList.txt :
1 PROJECT(main)
2 CMAKE_MINIMUM_REQUIRED(VERSION 2.6)
3 SET(CMAKE_SOURCE_DIR .)
4
5 SET(CMAKE_CXX_FLAGS_DEBUG "$ENV{CXXFLAGS} -O0 -Wall -g -ggdb")
6 SET(CMAKE_CXX_FLAGS_RELEASE "$ENV{CXXFLAGS} -O3 -Wall")
7
8 AUX_SOURCE_DIRECTORY(. DIR_SRCS)
9 ADD_EXECUTABLE(main ${DIR_SRCS})
第 5 和 6 行设置了两个变量 CMAKE_CXX_FLAGS_DEBUG 和
CMAKE_CXX_FLAGS_RELEASE, 这两个变量是分别用于 debug 和 release 的编译选项。
编辑 CMakeList.txt 后需要执行 ccmake 命令生成 Makefile 。在进入项目的根目录,输入
"ccmake ." 进入一个图形化界面,如下图所示:
图 5. ccmake 的界面
按照界面中的提示进行操作,按 "c" 进行 configure ,这时界面中显示出了配置变量
CMAKE_BUILD_TYPE 的条目。如下图所示:
图 6. 执行了 configure 以后 ccmake 的界面
下面我们首先生成 Debug 版的 Makefile :将变量 CMAKE_BUILD_TYPE 设置为
Debug ,按 "c" 进行 configure ,按 "g" 生成 Makefile 并退出。这时执行命令
find * | xargs grep "O0" 后结果如下:
CMakeFiles/main.dir/flags.make:CXX_FLAGS = -O0 -Wall -g -ggdb CMakeFiles/main.dir/link.txt:/usr/bin/c++ -O0 -Wall -g -ggdb CMakeFiles/main.dir/main.cpp.o -o main -rdynamic CMakeLists.txt:SET(CMAKE_CXX_FLAGS_DEBUG "$ENV{CXXFLAGS} -O0 -Wall -g -ggdb")
这个结果说明生成的 Makefile 中使用了变量 CMAKE_CXX_FLAGS_DEBUG 作为编译时的
参数。
下面我们将生成 Release 版的 Makefile :再次执行命令 "ccmake ." 将变量
CMAKE_BUILD_TYPE 设置为 Release ,生成 Makefile 并退出。执行命令
find * | xargs grep "O0" 后结果如下:
CMakeLists.txt:SET(CMAKE_CXX_FLAGS_DEBUG "$ENV{CXXFLAGS} -O0 -Wall -g -ggdb")
而执行命令 find * | xargs grep "O3" 后结果如下:
CMakeCache.txt:CMAKE_CXX_FLAGS_RELEASE:STRING=-O3 -DNDEBUG CMakeCache.txt:CMAKE_C_FLAGS_RELEASE:STRING=-O3 -DNDEBUG CMakeFiles/main.dir/flags.make:CXX_FLAGS = -O3 -Wall CMakeFiles/main.dir/link.txt:/usr/bin/c++ -O3 -Wall CMakeFiles/main.dir/main.cpp.o -o main -rdynamic CMakeLists.txt:SET(CMAKE_CXX_FLAGS_RELEASE "$ENV{CXXFLAGS} -O3 -Wall")
这两个结果说明生成的 Makefile 中使用了变量 CMAKE_CXX_FLAGS_RELEASE 作为编译
时的参数。
3,CMAKE_INCLUDE_PATH 和 CMAKE_LIBRARY_PATH 我们在上一节已经提及。
1,CMAKE_MAJOR_VERSION,CMAKE 主版本号,比如 2.4.6 中的 2
2,CMAKE_MINOR_VERSION,CMAKE 次版本号,比如 2.4.6 中的 49,WIN32,在所有的 win32 平台为 TRUE,包括 cygwin
1,CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS
用来控制 IF ELSE 语句的书写方式,在下一节语法部分会讲到。
2,BUILD_SHARED_LIBS读一些成功项目的 cmake 工程文件,比如 KDE4 的代码。
如果你的代码中定义了#ifdef ENABLE_DEBUG #endif,这个代码块就会生效。
如果要添加其他的编译器开关,可以通过 CMAKE_C_FLAGS 变量和 CMAKE_CXX_FLAGS 变量设置。
2,ADD_DEPENDENCIES
定义 target 依赖的其他 target,确保在编译本 target 之前,其他的 target 已经被构建。
ADD_DEPENDENCIES(target-name depend-target1
depend-target2 ...)
让一个顶层目标依赖于其他的顶层目标。一个顶层目标是由命令ADD_EXECUTABLE,ADD_LIBRARY,或者ADD_CUSTOM_TARGET产生的目标。为这些命令的输出引入依赖性可以保证某个目标在其他的目标之前被构建。查看ADD_CUSTOM_TARGET和ADD_CUSTOM_COMMAND命令的DEPENDS选项,可以了解如何根据自定义规则引入文件级的依赖性。查看SET_SOURCE_FILES_PROPERTIES命令的OBJECT_DEPENDS选项,可以了解如何为目标文件引入文件级的依赖性。
3,ADD_EXECUTABLE、ADD_LIBRARY、ADD_SUBDIRECTORY
ADD_EXECUTABLE(可执行文件名 生成该可执行文件的源文件)
说明源文件需要编译出的可执行文件名
例:
ADD_EXECUTABLE(hello ${SRC_LIST})
说明SRC_LIST变量中的源文件需要编译出名为hello的可执行文件
ADD_LIBRARY(libname [SHARED|STATIC|MODULE] [EXCLUDE_FROM_ALL] source1 source2 ... sourceN)
生成动态静态库
ADD_LIBRARY(hello SHARED ${LIBHELLO_SRC})
ADD_SUBDIRECTORY(src_dir [binary_dir] [EXCLUDE_FROM_ALL])
向当前工程添加存放源文件的子目录,并可以指定中间二进制和目标二进制的存放位置
EXCLUDE_FROM_ALL含义:将这个目录从编译过程中排除
4,ADD_TEST 与 ENABLE_TESTING 指令。
ENABLE_TESTING 指令用来控制 Makefile 是否构建 test 目标,涉及工程所有目录。语法很简单,没有任何参数,ENABLE_TESTING(),一般情况这个指令放在工程的主CMakeLists.txt 中.
ADD_TEST 指令的语法是:
ADD_TEST(testname Exename arg1 arg2 ...)
testname 是自定义的 test 名称,Exename 可以是构建的目标文件也可以是外部脚本等等。后面连接传递给可执行文件的参数。如果没有在同一个 CMakeLists.txt 中打开ENABLE_TESTING()指令,任何 ADD_TEST 都是无效的。
比如我们前面的 Helloworld 例子,可以在工程主 CMakeLists.txt 中添加
ADD_TEST(mytest ${PROJECT_BINARY_DIR}/bin/main)
ENABLE_TESTING()
生成 Makefile 后,就可以运行 make test 来执行测试了。
5,AUX_SOURCE_DIRECTORY
基本语法是:
AUX_SOURCE_DIRECTORY(dir VARIABLE)
作用是发现一个目录下所有的源代码文件并将列表存储在一个变量中,这个指令临时被用来自动构建源文件列表。因为目前 cmake 还不能自动发现新添加的源文件。
比如
AUX_SOURCE_DIRECTORY(. SRC_LIST)
ADD_EXECUTABLE(main ${SRC_LIST})
你也可以通过后面提到的 FOREACH 指令来处理这个 LIST
6,CMAKE_MINIMUM_REQUIRED
其语法为 CMAKE_MINIMUM_REQUIRED(VERSION versionNumber [FATAL_ERROR])
比如 CMAKE_MINIMUM_REQUIRED(VERSION 2.5 FATAL_ERROR)
如果 cmake 版本小与 2.5,则出现严重错误,整个过程中止。
7,EXEC_PROGRAM
在 CMakeLists.txt 处理过程中执行命令,并不会在生成的 Makefile 中执行。
具体语法为:
EXEC_PROGRAM(Executable [directory in which to run]
[ARGS
[OUTPUT_VARIABLE ]
[RETURN_VALUE ])
用于在指定的目录运行某个程序,通过 ARGS 添加参数,如果要获取输出和返回值,可通过OUTPUT_VARIABLE 和 RETURN_VALUE 分别定义两个变量.
这个指令可以帮助你在 CMakeLists.txt 处理过程中支持任何命令,比如根据系统情况去修改代码文件等等。
举个简单的例子,我们要在 src 目录执行 ls 命令,并把结果和返回值存下来。
可以直接在 src/CMakeLists.txt 中添加:
EXEC_PROGRAM(ls ARGS "*.c" OUTPUT_VARIABLE LS_OUTPUT RETURN_VALUE
LS_RVALUE)
IF(not LS_RVALUE)
MESSAGE(STATUS "ls result: " ${LS_OUTPUT})
ENDIF(not LS_RVALUE)
在 cmake 生成 Makefile 的过程中,就会执行 ls 命令,如果返回 0,则说明成功执行,那么就输出 ls *.c 的结果。关于 IF 语句,后面的控制指令会提到。
file(WRITE filename "message to write"... ) file(APPEND filename "message to write"... ) file(READ filename variable [LIMIT numBytes] [OFFSET offset] [HEX]) file(STRINGS filename variable [LIMIT_COUNT num] [LIMIT_INPUT numBytes] [LIMIT_OUTPUT numBytes] [LENGTH_MINIMUM numBytes] [LENGTH_MAXIMUM numBytes] [NEWLINE_CONSUME] [REGEX regex] [NO_HEX_CONVERSION]) file(GLOB variable [RELATIVE path] [globbing expressions]...) file(GLOB_RECURSE variable [RELATIVE path] [FOLLOW_SYMLINKS] [globbing expressions]...) file(RENAMEfile(REMOVE [file1 ...]) file(REMOVE_RECURSE [file1 ...]) file(MAKE_DIRECTORY [directory1 directory2 ...]) file(RELATIVE_PATH variable directory file) file(TO_CMAKE_PATH path result) file(TO_NATIVE_PATH path result) file(DOWNLOAD url file [TIMEOUT timeout] [STATUS status] [LOG log] [EXPECTED_MD5 sum] [SHOW_PROGRESS]) )
WRITE选项将会写一条消息到名为filename的文件中。如果文件已经存在,该命令会覆盖已有的文件;如果文件不存在,它将创建该文件。
APPEND选项和WRITE选项一样,将会写一条消息到名为filename的文件中,只是该消息会附加到文件末尾。
READ选项将会读一个文件中的内容并将其存储在变量里。读文件的位置从offset开始,最多读numBytes个字节。如果指定了HEX参数,二进制代码将会转换为十六进制表达方式,并存储在变量里。
STRINGS将会从一个文件中将一个ASCII字符串的list解析出来,然后存储在variable变量中。文件中的二进制数据会被忽略。回车换行符会被忽略。它也可以用在Intel的Hex和Motorola的S-记录文件;读取它们时,它们会被自动转换为二进制格式。可以使用NO_HEX_CONVERSION选项禁止这项功能。LIMIT_COUNT选项设定了返回的字符串的最大数量。LIMIT_INPUT设置了从输入文件中读取的最大字节数。LIMIT_OUTPUT设置了在输出变量中存储的最大字节数。LENGTH_MINIMUM设置了要返回的字符串的最小长度;小于该长度的字符串会被忽略。LENGTH_MAXIMUM设置了返回字符串的最大长度;更长的字符串会被分割成不长于最大长度的字符串。NEWLINE_CONSUME选项允许新行被包含到字符串中,而不是终止它们。REGEX选项指定了一个待返回的字符串必须满足的正则表达式。典型的使用方式是:
file(STRINGS myfile.txt myfile)
该命令在变量myfile中存储了一个list,该list中每个项是输入文件中的一行文本。
GLOB选项将会为所有匹配查询表达式的文件生成一个文件list,并将该list存储进变量variable里。文件名查询表达式与正则表达式类似,只不过更加简单。如果为一个表达式指定了RELATIVE标志,返回的结果将会是相对于给定路径的相对路径。文件名查询表达式的例子有:
*.cxx - 匹配所有扩展名为cxx的文件。 *.vt? - 匹配所有扩展名是vta,...,vtz的文件。 f[3-5].txt - 匹配文件f3.txt, f4.txt, f5.txt。
GLOB_RECURSE选项将会生成一个类似于通常的GLOB选项的list,只是它会寻访所有那些匹配目录的子路径并同时匹配查询表达式的文件。作为符号链接的子路径只有在给定FOLLOW_SYMLINKS选项或者cmake策略CMP0009被设置为NEW时,才会被寻访到。参见cmake --help-policy CMP0009 查询跟多有用的信息。
使用递归查询的例子有:
/dir/*.py - 匹配所有在/dir及其子目录下的python文件。
MAKE_DIRECTORY选项将会创建指定的目录,如果它们的父目录不存在时,同样也会创建。(类似于mkdir命令——译注)
RENAME选项对同一个文件系统下的一个文件或目录重命名。(类似于mv命令——译注)
REMOVE选项将会删除指定的文件,包括在子路径下的文件。(类似于rm命令——译注)
REMOVE_RECURSE选项会删除给定的文件以及目录,包括非空目录。(类似于rm -r 命令——译注)
RELATIVE_PATH选项会确定从direcroty参数到指定文件的相对路径。
TO_CMAKE_PATH选项会把path转换为一个以unix的 / 开头的cmake风格的路径。输入可以是一个单一的路径,也可以是一个系统路径,比如"$ENV{PATH}"。注意,在调用TO_CMAKE_PATH的ENV周围的双引号只能有一个参数(Note the double quotes around the ENV call TO_CMAKE_PATH only takes one argument. 原文如此。quotes和后面的takes让人后纠结,这句话翻译可能有误。欢迎指正——译注)。
TO_NATIVE_PATH选项与TO_CMAKE_PATH选项很相似,但是它会把cmake风格的路径转换为本地路径风格:windows下用\,而unix下用/。
DOWNLOAD 将给定的URL下载到指定的文件中。如果指定了LOG var选项,下载日志将会被输出到var中。如果指定了STATUS var选项,下载操作的状态会被输出到var中。该状态返回值是一个长度为2的list。list的第一个元素是操作的数字返回值,第二个返回值是错误的字符串值。错误信息如果是数字0,操作中没有发生错误。如果指定了TIMEOUT time选项,在time秒之后,操作会超时退出;time应该是整数。如果指定了EXPECTED_MD5 sum选项,下载操作会认证下载的文件的实际MD5和是否与期望值匹配。如果不匹配,操作将返回一个错误。如果指定了SHOW_PROGRESS选项,进度信息会以状态信息的形式被打印出来,直到操作完成。
file命令还提供了COPY和INSTALL两种格式:
file(files... DESTINATION [FILE_PERMISSIONS permissions...] [DIRECTORY_PERMISSIONS permissions...] [NO_SOURCE_PERMISSIONS] [USE_SOURCE_PERMISSIONS] [FILES_MATCHING] [[PATTERN| REGEX [EXCLUDE] [PERMISSIONS permissions...]] [...])]
COPY版本把文件、目录以及符号连接拷贝到一个目标文件夹。相对输入路径的评估是基于当前的源代码目录进行的,相对目标路径的评估是基于当前的构建目录进行的。复制过程将保留输入文件的时间戳;并且如果目标路径处存在同名同时间戳的文件,复制命令会把它优化掉。赋值过程将保留输入文件的访问权限,除非显式指定权限或指定NO_SOURCE_PERMISSIONS选项(默认是USE_SOURCE_PERMISSIONS)。参见install(DIRECTORY)命令中关于权限(permissions),PATTERN,REGEX和EXCLUDE选项的文档。
INSTALL版本与COPY版本只有十分微小的差别:它会打印状态信息,并且默认使用NO_SOURCE_PERMISSIONS选项。install命令生成的安装脚本使用这个版本(它会使用一些没有在文档中涉及的内部使用的选项。)
9,INCLUDE 指令,用来载入 CMakeLists.txt 文件,也用于载入预定义的 cmake 模块.
INCLUDE(file1 [OPTIONAL])
INCLUDE(module [OPTIONAL])
OPTIONAL 参数的作用是文件不存在也不会产生错误。
你可以指定载入一个文件,如果定义的是一个模块,那么将在 CMAKE_MODULE_PATH 中搜索这个模块并载入。
载入的内容将在处理到 INCLUDE 语句时直接执行。
https://cmake.org/cmake/help/v3.0/command/include.html
指明头文件查找的路径,
原型如下
:
该命令在参数
path*
指示的
目录中查找文件
name1
并将查找到的路径保存在变量
VAR
中。
用法同FIND_PATH,表明库查找路径。
VAR 变量代表包含这个程序的全路径。
用来调用预定义在 CMAKE_MODULE_PATH 下的 Find
CMake 会到变量 CMAKE_MODULE_PATH 指示的目录中查找文件 Findname.cmake 并执行。这条命令执行后,
QUIET 参数:
对应于Find
MESSAGE(STATUS "Found NAME: ${NAME_LIBRARY}")
指定了QUIET就不输出指令。
REQUIRED 参数
其含义是指是否是工程必须的,如果使用了这个参数,说明是必须的,如果找不到,则工程不能编译。对应于Find
示例:
FIND_PACKAGE( libdb_cxx REQUIRED)
CMake 会到变量 CMAKE_MODULE_PATH 指示的目录中查找文件 Findlibdb_cxx.cmake 并执行。这条命令执行后,
FIND_PACKAGE 的使用方法和 Find 模块的编写参见下面:编写属于自己的 FindHello 模块。
FIND_LIBRARY 示例:
FIND_LIBRARY(libX X11 /usr/lib)
IF(NOT libX)
MESSAGE(FATAL_ERROR “libX not found”)
ENDIF(NOT libX)
掌握了以上的各种控制指令,你应该完全可以通过 cmake 管理复杂的程序了,下一节,我
们将介绍一个比较复杂的例子,通过他来演示本章的一些指令,并介绍模块的概念。
本章我们将着重介绍系统预定义的 Find 模块的使用以及自己编写 Find 模块,系统中提供了其他各种模块,一般情况需要使用 INCLUDE 指令显式的调用,FIND_PACKAGE 指令是一个特例,可以直接调用预定义的模块.
其实使用纯粹依靠 cmake 本身提供的基本指令来管理工程是一件非常复杂的事情,所以,cmake 设计成了可扩展的架构,可以通过编写一些通用的模块来扩展 cmake.
在本章,我们准备首先介绍一下 cmake 提供的 FindCURL 模块的使用。然后,基于我们前面的 libhello 共享库,编写一个 FindHello.cmake 模块.
如果
将
我们再来看一个复杂的例子,通过
SET(mySources viewer.c)
SET(optionalSources)
SET(optionalLibs)
FIND_PACKAGE(JPEG)
IF(JPEG_FOUND)
SET(optionalSources ${optionalSources} jpegview.c)
INCLUDE_DIRECTORIES( ${JPEG_INCLUDE_DIR} )
SET(optionalLibs ${optionalLibs} ${JPEG_LIBRARIES} )
ADD_DEFINITIONS(-DENABLE_JPEG_SUPPORT)
ENDIF(JPEG_FOUND)
IF(PNG_FOUND)
SET(optionalSources ${optionalSources} pngview.c)
INCLUDE_DIRECTORIES( ${PNG_INCLUDE_DIR} )
SET(optionalLibs ${optionalLibs} ${PNG_LIBRARIES} )
ADD_DEFINITIONS(-DENABLE_PNG_SUPPORT)
ENDIF(PNG_FOUND)
ADD_EXECUTABLE(viewer ${mySources} ${optionalSources} )
TARGET_LINK_LIBRARIES(viewer ${optionalLibs}
通过判断系统是否提供了 JPEG 库来决定程序是否支持 JPEG 功能。
我们在此前的 t3 实例中,演示了构建动态库、静态库的过程并进行了安装。
接下来,我们在 t6 示例中演示如何自定义 FindHELLO 模块并使用这个模块构建工程:
请在建立/backup/cmake/中建立 t6 目录,并在其中建立 cmake 目录用于存放我们自己定义的 FindHELLO.cmake 模块,同时建立 src 目录,用于存放我们的源文件。
REQUIRED 参数,其含义是指这个共享库是否是工程必须的,如果使用了这个参数,说明这个链接库是必备库,如果找不到这个链接库,则工程不能编译。对应于
FindHELLO.cmake 模块中的 HELLO_FIND_REQUIRED 变量。
同样,我们在上面的模块中定义了 HELLO_FOUND,
HELLO_INCLUDE_DIR,HELLO_LIBRARY 变量供开发者在 FIND_PACKAGE 指令中使用。
OK,下面建立 src/main.c,内容为:
#include
int main()
{
HelloFunc();
return 0;
}
建立 src/CMakeLists.txt 文件,内容如下:
FIND_PACKAGE(HELLO)
IF(HELLO_FOUND)
ADD_EXECUTABLE(hello main.c)
INCLUDE_DIRECTORIES(${HELLO_INCLUDE_DIR})
TARGET_LINK_LIBRARIES(hello ${HELLO_LIBRARY})
ENDIF(HELLO_FOUND)
为了能够让工程找到 FindHELLO.cmake 模块(存放在工程中的 cmake 目录)我们在主工程文件 CMakeLists.txt 中加入:
SET(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake)
这时候再次运行 cmake ..
我们得到如下输出:
CMake Error: Could not find hello library.
因为找不到 libhello.x,所以,整个 Makefile 生成过程被出错中止。
小结:
在本节中,我们学习了如何使用系统提供的 Find
Find
后面的章节,我们会逐渐学习更多的 cmake 模块使用方法以及用 cmake 来管理 GTK 和 QT4工程。
http://blog.csdn.net/gubenpeiyuan/article/details/8667279