CMake之CMakeLists.txt编写入门

自定义变量

主要有隐式定义和显式定义两种。 
隐式定义的一个例子是PROJECT指令,它会隐式的定义< projectname >_BINARY_DIR< projectname >_SOURCE_DIR两个变量;显式定义使用SET指令构建自定义变量,好比:SET(HELLO_SRCmain.c)就能够经过${HELLO_SRC}来引用这个自定义变量了。php

变量引用方式

使用${}进行变量的引用;在IF等语句中,是直接使用变量名而不经过${}取值。css

经常使用变量

CMAKE_BINARY_DIR 
PROJECT_BINARY_DIR 
< projectname >_BINARY_DIR 
这三个变量指代的内容是一致的,若是是in-source编译,指得就是工程顶层目录;若是是out-of-source编译,指的是工程编译发生的目录。PROJECT_BINARY_DIR跟其它指令稍有区别,目前能够认为它们是一致的。linux

CMAKE_SOURCE_DIR 
PROJECT_SOURCE_DIR 
< projectname >_SOURCE_DIR 
这三个变量指代的内容是一致的,不论采用何种编译方式,都是工程顶层目录。也就是在in-source编译时,他跟CMAKE_BINARY_DIR等变量一致。PROJECT_SOURCE_DIR跟其它指令稍有区别,目前能够认为它们是一致的。 
(out-of-source build与in-source build相对,指是否在CMakeLists.txt所在目录进行编译。)正则表达式

CMAKE_CURRENT_SOURCE_DIR 
当前处理的CMakeLists.txt所在的路径,好比上面咱们提到的src子目录。express

CMAKE_CURRRENT_BINARY_DIR 
若是是in-source编译,它跟CMAKE_CURRENT_SOURCE_DIR一致;若是是out-of-source编译,指的是target编译目录。使用ADD_SUBDIRECTORY(src bin)能够更改这个变量的值。使用SET(EXECUTABLE_OUTPUT_PATH <新路径>)并不会对这个变量形成影响,它仅仅修改了最终目标文件存放的路径。windows

CMAKE_CURRENT_LIST_FILE 
输出调用这个变量的CMakeLists.txt的完整路径bash

CMAKE_CURRENT_LIST_LINE 
输出这个变量所在的行markdown

CMAKE_MODULE_PATH 
这个变量用来定义本身的cmake模块所在的路径。若是工程比较复杂,有可能会本身编写一些cmake模块,这些cmake模块是随工程发布的,为了让cmake在处理CMakeLists.txt时找到这些模块,你须要经过SET指令将cmake模块路径设置一下。好比SET(CMAKE_MODULE_PATH,${PROJECT_SOURCE_DIR}/cmake) 
这时候就能够经过INCLUDE指令来调用本身的模块了。架构

EXECUTABLE_OUTPUT_PATH 
新定义最终结果的存放目录app

LIBRARY_OUTPUT_PATH 
新定义最终结果的存放目录

PROJECT_NAME 
返回经过PROJECT指令定义的项目名称。

cmake调用环境变量的方式

使用$ENV{NAME}指令就能够调用系统的环境变量了。好比MESSAGE(STATUS "HOME dir: $ENV{HOME}")设置环境变量的方式是SET(ENV{变量名} 值)。

  1. CMAKE_INCLUDE_CURRENT_DIR 
    自动添加CMAKE_CURRENT_BINARY_DIR和CMAKE_CURRENT_SOURCE_DIR到当前处理的CMakeLists.txt,至关于在每一个CMakeLists.txt加入:INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR})

  2. CMAKE_INCLUDE_DIRECTORIES_PROJECT_BEFORE 
    将工程提供的头文件目录始终置于系统头文件目录的前面,当定义的头文件确实跟系统发生冲突时能够提供一些帮助。

  3. CMAKE_INCLUDE_PATH和CMAKE_LIBRARY_PATH

系统信息

  • CMAKE_MAJOR_VERSION,CMAKE主版本号,好比2.4.6中的2
  • CMAKE_MINOR_VERSION,CMAKE次版本号,好比2.4.6中的4
  • CMAKE_PATCH_VERSION,CMAKE补丁等级,好比2.4.6中的6
  • CMAKE_SYSTEM,系统名称,好比Linux-2.6.22
  • CMAKE_SYSTEM_NAME,不包含版本的系统名,好比Linux
  • CMAKE_SYSTEM_VERSION,系统版本,好比2.6.22
  • CMAKE_SYSTEM_PROCESSOR,处理器名称,好比i686
  • UNIX,在全部的类Unix平台为TRUE,包括OSX和cygwin
  • WIN32,在全部的Win32平台为TRUE,包括cygwin

主要的开关选项

  1. CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS 
    用来控制IF ELSE语句的书写方式。

  2. BUILD_SHARED_LIBS 
    这个开关用来控制默认的库编译方式。若是不进行设置,使用ADD_LIBRARY并无指定库类型的状况下,默认编译生成的库都是静态库;若是SET(BUILD_SHARED_LIBSON)后,默认生成的为动态库。

  3. CMAKE_C_FLAGS 
    设置C编译选项,也能够经过指令ADD_DEFINITIONS()添加。

  4. MAKE_CXX_FLAGS 
    设置C++编译选项,也能够经过指令ADD_DEFINITIONS()添加。

cMake经常使用指令

这里引入更多的cmake指令,为了编写的方便,将按照cmakeman page 的顺序介绍各类指令,再也不推荐使用的指令将再也不介绍。

基本指令

PROJECT(HELLO) 
指定项目名称,生成的VC项目的名称,使用${HELLO_SOURCE_DIR}表示项目根目录。

INCLUDE_DIRECTORIES 
指定头文件的搜索路径,至关于指定gcc的-I参数 
INCLUDE_DIRECTORIES(${HELLO_SOURCE_DIR}/Hello) #增长Hello为include目录

TARGET_LINK_LIBRARIES 
添加连接库,相同于指定-l参数 
TARGET_LINK_LIBRARIES(demoHello) #将可执行文件与Hello链接成最终文件demo

LINK_DIRECTORIES 
动态连接库或静态连接库的搜索路径,至关于gcc的-L参数 
LINK_DIRECTORIES(${HELLO_BINARY_DIR}/Hello)#增长Hello为link目录

ADD_DEFINITIONS 
向C/C++编译器添加-D定义,好比: 
ADD_DEFINITIONS(-DENABLE_DEBUG-DABC) 
参数之间用空格分割。若是代码中定义了:

#ifdef ENABLE_DEBUG #endif
  • 1
  • 2
  • 3

这个代码块就会生效。若是要添加其余的编译器开关,能够经过CMAKE_C_FLAGS变量和CMAKE_CXX_FLAGS变量设置。

ADD_DEPENDENCIES* 
定义target依赖的其它target,确保在编译本target以前,其它的target已经被构建。ADD_DEPENDENCIES(target-name depend-target1 depend-target2 ...)

ADD_EXECUTABLE 
ADD_EXECUTABLE(helloDemo demo.cxx demo_b.cxx) 
指定编译,好像也能够添加.o文件,将cxx编译成可执行文件

ADD_LIBRARY 
ADD_LIBRARY(Hellohello.cxx) #将hello.cxx编译成静态库如libHello.a

ADD_SUBDIRECTORY 
ADD_SUBDIRECTORY(Hello) #包含子目录

ADD_TEST 
ENABLE_TESTING 
ENABLE_TESTING指令用来控制Makefile是否构建test目标,涉及工程全部目录。语法很简单,没有任何参数,ENABLE_TESTING()通常放在工程的主CMakeLists.txt中。 
ADD_TEST指令的语法是:ADD_TEST(testnameExename arg1 arg2 …) 
testname是自定义的test名称,Exename能够是构建的目标文件也能够是外部脚本等等,后面链接传递给可执行文件的参数。

若是没有在同一个CMakeLists.txt中打开ENABLE_TESTING()指令,任何ADD_TEST都是无效的。好比前面的Helloworld例子,能够在工程主CMakeLists.txt中添加

ADD_TEST(mytest ${PROJECT_BINARY_DIR}/bin/main) ENABLE_TESTING
  • 1
  • 2

生成Makefile后,就能够运行make test来执行测试了。

AUX_SOURCE_DIRECTORY 
基本语法是:AUX_SOURCE_DIRECTORY(dir VARIABLE),做用是发现一个目录下全部的源代码文件并将列表存储在一个变量中,这个指令临时被用来自动构建源文件列表,由于目前cmake还不能自动发现新添加的源文件。好比:

AUX_SOURCE_DIRECTORY(. SRC_LIST) ADD_EXECUTABLE(main ${SRC_LIST})
  • 1
  • 2

能够经过后面提到的FOR EACH指令来处理这个LIST。

CMAKE_MINIMUM_REQUIRED 
语法为CMAKE_MINIMUM_REQUIRED(VERSION versionNumber [FATAL_ERROR]), 
好比:CMAKE_MINIMUM_REQUIRED(VERSION 2.5 FATAL_ERROR) 
若是cmake版本小与2.5,则出现严重错误,整个过程当中止。

EXEC_PROGRAM 
在CMakeLists.txt处理过程当中执行命令,并不会在生成的Makefile中执行。具体语法为:

EXEC_PROGRAM(Executable [directory in which to run] [ARGS <arguments to executable>] [OUTPUT_VARIABLE <var>] [RETURN_VALUE <var>])
  • 1

用于在指定的目录运行某个程序,经过ARGS添加参数,若是要获取输出和返回值,可经过OUTPUT_VARIABLERETURN_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)
  • 1
  • 2
  • 3
  • 4

在cmake生成Makefile过程当中,就会执行ls命令,若是返回0,则说明成功执行,那么就输出ls *.c的结果。关于IF语句,后面的控制指令会提到。

FILE指令 
文件操做指令,基本语法为:

FILE(WRITEfilename "message to write"... ) FILE(APPENDfilename "message to write"... ) FILE(READfilename variable) FILE(GLOBvariable [RELATIVE path] [globbing expression_r_rs]...) FILE(GLOB_RECURSEvariable [RELATIVE path] [globbing expression_r_rs]...) FILE(REMOVE[directory]...) FILE(REMOVE_RECURSE[directory]...) FILE(MAKE_DIRECTORY[directory]...) FILE(RELATIVE_PATHvariable directory file) FILE(TO_CMAKE_PATHpath result) FILE(TO_NATIVE_PATHpath result)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

INCLUDE指令 
用来载入CMakeLists.txt文件,也用于载入预约义的cmake模块。

INCLUDE(file1[OPTIONAL]) INCLUDE(module[OPTIONAL])
  • 1
  • 2

OPTIONAL参数的做用是文件不存在也不会产生错误,能够指定载入一个文件,若是定义的是一个模块,那么将在CMAKE_MODULE_PATH中搜索这个模块并载入,载入的内容将在处理到INCLUDE语句是直接执行。

INSTALL指令

FIND_指令 
FIND_系列指令主要包含一下指令:

FIND_FILE(<VAR>name1 path1 path2 …) VAR变量表明找到的文件全路径,包含文件名 FIND_LIBRARY(<VAR>name1 path1 path2 …) VAR变量表示找到的库全路径,包含库文件名 FIND_PATH(<VAR>name1 path1 path2 …) VAR变量表明包含这个文件的路径 FIND_PROGRAM(<VAR>name1 path1 path2 …) VAR变量表明包含这个程序的全路径 FIND_PACKAGE(<name>[major.minor] [QUIET] [NO_MODULE] [[REQUIRED|COMPONENTS][componets...]]) 用来调用预约义在CMAKE_MODULE_PATH下的Find<name>.cmake模块,也能够本身定义Find<name>模块,经过SET(CMAKE_MODULE_PATH dir)将其放入工程的某个目录中供工程使用,后面会详细介绍FIND_PACKAGE的使用方法和Find模块的编写。
  • 1
  • 2
  • 3
  • 4
  • 5

FIND_LIBRARY示例:

FIND_LIBRARY(libXX11 /usr/lib) IF(NOT libX) MESSAGE(FATAL_ERROR "libX not found") ENDIF(NOT libX)
  • 1
  • 2
  • 3
  • 4

控制指令

IF指令,基本语法为:

IF(expression_r_r) #THEN section. COMMAND1(ARGS…) COMMAND2(ARGS…) … ELSE(expression_r_r) #ELSE section. COMMAND1(ARGS…) COMMAND2(ARGS…) … ENDIF(expression_r_r)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

另一个指令是ELSEIF,整体把握一个原则,凡是出现IF的地方必定要有对应的ENDIF,出现ELSEIF的地方,ENDIF是可选的。表达式的使用方法以下:

IF(var) 若是变量不是:空, 0, N, NO, OFF, FALSE, NOTFOUND 或 <var>_NOTFOUND时,表达式为真。 IF(NOT var), 与上述条件相反。 IF(var1AND var2), 当两个变量都为真是为真。 IF(var1OR var2), 当两个变量其中一个为真时为真。 IF(COMMANDcmd), 当给定的cmd确实是命令并能够调用是为真。 IF(EXISTS dir) or IF(EXISTS file), 当目录名或者文件名存在时为真。 IF(file1IS_NEWER_THAN file2), 当file1比file2新,或者file1/file2其中有一个不存在时为真文件名请使用完整路径。 IF(IS_DIRECTORY dirname), 当dirname是目录时为真。 IF(variableMATCHES regex)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

IF(string MATCHES regex) 当给定的变量或者字符串可以匹配正则表达式regex时为真。好比:

IF("hello" MATCHES "hello") MESSAGE("true") ENDIF("hello" MATCHES "hello")
  • 1
  • 2
  • 3
IF(variable LESS number) IF(string LESS number) IF(variable GREATER number) IF(string GREATER number) IF(variable EQUAL number) IF(string EQUAL number)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

数字比较表达式

IF(variable STRLESS string) IF(string STRLESS string) IF(variable STRGREATER string) IF(string STRGREATER string) IF(variable STREQUAL string) IF(string STREQUAL string)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

按照字母序的排列进行比较。

IF(DEFINED variable),若是变量被定义,为真。 
一个小例子,用来判断平台差别:

IF(WIN32) MESSAGE(STATUS“This is windows.”) #做一些Windows相关的操做 ELSE(WIN32) MESSAGE(STATUS“This is not windows”) #做一些非Windows相关的操做 ENDIF(WIN32)
  • 1
  • 2
  • 3
  • 4
  • 5

上述代码用来控制在不一样的平台进行不一样的控制,可是阅读起来却并非那么舒服, ELSE(WIN32)之类的语句很容易引发歧义。 
这就用到了咱们在 经常使用变量 一节提到的CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS开关。能够SET(CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTSON),这时候就能够写成:

IF(WIN32) ELSE() ENDIF()
  • 1
  • 2
  • 3
  • 4
  • 5

若是配合ELSEIF使用,可能的写法是这样:

IF(WIN32) #dosomething related to WIN32 ELSEIF(UNIX) #dosomething related to UNIX ELSEIF(APPLE) #dosomething related to APPLE ENDIF(WIN32)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

WHILE指令

WHILE指令的语法是:

WHILE(condition) COMMAND1(ARGS…) COMMAND2(ARGS…) … ENDWHILE(condition)
  • 1
  • 2
  • 3
  • 4
  • 5

其真假判断条件能够参考IF指令。

FOREACH指令

FOREACH指令的使用方法有三种形式: 
(1)列表

FOREACH(loop_vararg1 arg2 …) COMMAND1(ARGS…) COMMAND2(ARGS…) … ENDFOREACH(loop_var)
  • 1
  • 2
  • 3
  • 4
  • 5

像咱们前面使用的AUX_SOURCE_DIRECTORY的例子

AUX_SOURCE_DIRECTORY(.SRC_LIST) FOREACH(F ${SRC_LIST}) MESSAGE(${F}) ENDFOREACH(F)
  • 1
  • 2
  • 3
  • 4

(2)范围

FOREACH(loop_var RANGE total) ENDFOREACH(loop_var)
  • 1
  • 2
  • 3

从0到total以1为步进,举例以下:

FOREACH(VARRANGE 10) MESSAGE(${VAR}) ENDFOREACH(VAR)
  • 1
  • 2
  • 3

最终获得的输出是:

0
1
2
3
4
5
6
7
8
9
10
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

(3)范围和步进

FOREACH(loop_var RANGE start stop [step]) ENDFOREACH(loop_var)
  • 1
  • 2
  • 3

从start开始到stop结束,以step为步进。举例以下:

FOREACH(A RANGE 5 15 3) MESSAGE(${A}) ENDFOREACH(A)
  • 1
  • 2
  • 3

最终获得的结果是:

5
8
11
14
  • 1
  • 2
  • 3
  • 4

这个指令须要注意的是,直到遇到ENDFOREACH指令,整个语句块才会获得真正的执行。

复杂的例子:模块的使用和自定义模块

这里将着重介绍系统预约义的Find模块的使用以及本身编写Find模块,系统中提供了其余各类模块,通常状况须要使用INCLUDE指令显式的调用,FIND_PACKAGE指令是一个特例,能够直接调用预约义的模块。

其实纯粹依靠cmake自己提供的基本指令来管理工程是一件很是复杂的事情,因此cmake设计成了可扩展的架构,能够经过编写一些通用的模块来扩展cmake。

首先介绍一下cmake提供的FindCURL模块的使用,而后基于前面的libhello共享库,编写一个FindHello.cmake模块。 
(一)使用FindCURL模块 
在/backup/cmake目录创建t5目录,用于存放CURL的例子。 
创建src目录,并创建src/main.c,内容以下:

#include<curl/curl.h> #include<stdio.h> #include<stdlib.h> #include<unistd.h> FILE*fp; intwrite_data(void *ptr, size_t size, size_t nmemb, void *stream) { int written = fwrite(ptr, size, nmemb, (FILE *)fp); return written; } int main() { const char *path = “/tmp/curl-test”; const char *mode = “w”; fp = fopen(path,mode); curl_global_init(CURL_GLOBAL_ALL);  CURL coderes;  CURL *curl = curl_easy_init(); curl_easy_setopt(curl, CURLOPT_URL, “http://www.linux-ren.org”); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data); curl_easy_setopt(curl, CURLOPT_VERBOSE, 1); res = curl_easy_perform(curl); curl_easy_cleanup(curl); }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

这段代码的做用是经过curl取回www.linux-ren.org的首页并写入/tmp/curl-test文件中

创建主工程文件CmakeLists.txt,以下:

PROJECT(CURLTEST) ADD_SUBDIRECTORY(src)
  • 1
  • 2

创建src/CmakeLists.txt

ADD_EXECUTABLE(curltest main.c)
  • 1

如今天然是没办法编译的,咱们须要添加curl的头文件路径和库文件。

方法1: 
直接经过INCLUDE_DIRECTORIESTARGET_LINK_LIBRARIES指令添加:

咱们能够直接在src/CMakeLists.txt中添加:

INCLUDE_DIRECTORIES(/usr/include) TARGET_LINK_LIBRARIES(curltestcurl)
  • 1
  • 2

而后创建build目录进行外部构建便可。 
如今要探讨的是使用cmake提供的FindCURL模块。

方法2: 
使用FindCURL模块。向src/CMakeLists.txt中添加:

FIND_PACKAGE(CURL) IF(CURL_FOUND) INCLUDE_DIRECTORIES(${CURL_INCLUDE_DIR}) TARGET_LINK_LIBRARIES(curltest${CURL_LIBRARY}) ELSE(CURL_FOUND) MESSAGE(FATAL_ERROR”CURL library not found”) ENDIF(CURL_FOUND)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

对于系统预约义的Find<name>.cmake模块,使用方法通常如上例所示,每个模块都会定义如下几个变量:

<name>_FOUND <name>_INCLUDE_DIR or <name>_INCLUDES <name>_LIBRARY or <name>_LIBRARIES
  • 1
  • 2
  • 3

能够经过<name>_FOUND来判断模块是否被找到,若是没有找到,按照工程的须要关闭某些特性、给出提醒或者停止编译,上面的例子就是报出致命错误并终止构建。 
若是<name>_FOUND为真,则将<name>_INCLUDE_DIR加入INCLUDE_DIRECTORIES,将<name>_LIBRARY加入TARGET_LINK_LIBRARIES中。

咱们再来看一个复杂的例子,经过<name>_FOUND来控制工程特性:

SET(mySourcesviewer.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}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

经过判断系统是否提供了JPEG库来决定程序是否支持JPEG功能。

(二)编写属于本身的FindHello模块 
接下来在t6示例中演示如何自定义FindHELLO模块并使用这个模块构建工程。在/backup/cmake/中创建t6目录,并在其中创建cmake目录用于存放咱们本身定义的FindHELLO.cmake模块,同时创建src目录,用于存放咱们的源文件。

1.定义cmake/FindHELLO.cmake模块

FIND_PATH(HELLO_INCLUDE_DIR hello.h /usr/include/hello /usr/local/include/hello) FIND_LIBRARY(HELLO_LIBRARY NAMES hello PATH /usr/lib /usr/local/lib) IF(HELLO_INCLUDE_DIR AND HELLO_LIBRARY) SET(HELLO_FOUNDTRUE) ENDIF(HELLO_INCLUDE_DIR AND HELLO_LIBRARY) IF(HELLO_FOUND) IF(NOT HELLO_FIND_QUIETLY) MESSAGE(STATUS"Found Hello: ${HELLO_LIBRARY}") ENDIF(NOT HELLO_FIND_QUIETLY) ELSE(HELLO_FOUND) IF(HELLO_FIND_REQUIRED) MESSAGE(FATAL_ERROR"Could not find hello library") ENDIF(HELLO_FIND_REQUIRED) ENDIF(HELLO_FOUND)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

针对上面的模块让咱们再来回顾一下FIND_PACKAGE指令:

FIND_PACKAGE(<name>[major.minor] [QUIET] [NO_MODULE] [[REQUIRED|COMPONENTS][componets...]])
  • 1

前面的CURL例子中咱们使用了最简单的FIND_PACKAGE指令,其实它可使用多种参数:

QUIET参数,对应与咱们编写的FindHELLO中的HELLO_FIND_QUIETLY,若是不指定这个参数,就会执行:

MESSAGE(STATUS"Found Hello: ${HELLO_LIBRARY}")
  • 1

REQUIRED参数,其含义是指这个共享库是不是工程必须的,若是使用了这个参数,说明这个连接库是必备库,若是找不到这个连接库,则工程不能编译。对应于FindHELLO.cmake模块中的HELLO_FIND_REQUIRED变量。 
一样,咱们在上面的模块中定义了HELLO_FOUND,HELLO_INCLUDE_DIRHELLO_LIBRARY变量供开发者在FIND_PACKAGE指令中使用。

下面创建src/main.c,内容为:

#include<hello.h> int main() { HelloFunc(); return 0; }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

创建src/CMakeLists.txt文件,内容以下:

FIND_PACKAGE(HELLO) IF(HELLO_FOUND) ADD_EXECUTABLE(hellomain.c) INCLUDE_DIRECTORIES(${HELLO_INCLUDE_DIR}) TARGET_LINK_LIBRARIES(hello${HELLO_LIBRARY}) ENDIF(HELLO_FOUND)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

为了可以让工程找到 FindHELLO.cmake 模块(存放在工程中的cmake目录)咱们在主工程文件 CMakeLists.txt 中加入:

SET(CMAKE_MODULE_PATH${PROJECT_SOURCE_DIR}/cmake)
  • 1

(三)使用自定义的FindHELLO模块构建工程 
仍然采用外部编译的方式,创建build目录,进入目录运行:

cmake ..
  • 1

咱们能够从输出中看到:

FoundHello: /usr/lib/libhello.so
  • 1

若是咱们把上面的FIND_PACKAGE(HELLO)修改成FIND_PACKAGE(HELLO QUIET),

不会看到上面的输出。接下来就可使用make命令构建工程,运行:

./src/hello
  • 1

能够获得输出

HelloWorld
  • 1

说明工程成功构建。

(四)若是没有找到hellolibrary呢? 
咱们能够尝试将/usr/lib/libhello.x移动到/tmp目录,这样按照FindHELLO模块的定义,找不到hellolibrary了,咱们再来看一下构建结果:

cmake ..
  • 1

仍然能够成功进行构建,可是这时候是没有办法编译的。

修改FIND_PACKAGE(HELLO)FIND_PACKAGE(HELLO REQUIRED),将hellolibrary定义为工程必须的共享库。 
这时候再次运行

cmake ..
  • 1

咱们获得以下输出:

CMakeError: Could not find hello library.
  • 1

由于找不到libhello.x,因此,整个Makefile生成过程被出错停止。

一些问题

1.怎样区分debug、release版本 
创建debug/release两目录,分别在其中执行cmake -D CMAKE_BUILD_TYPE=Debug(或Release),须要编译不一样版本时进入不一样目录执行make便可:

Debug版会使用参数-g; Release版使用-O3–DNDEBUG
  • 1
  • 2

另外一种设置方法——例如DEBUG版设置编译参数DDEBUG

IF(DEBUG_mode) add_definitions(-DDEBUG) ENDIF()
  • 1
  • 2
  • 3

在执行cmake时增长参数便可,例如cmake -D DEBUG_mode=ON

2.怎样设置条件编译 
例如debug版设置编译选项DEBUG,而且更改不该改变CMakelist.txt 
使用option command,eg:

option(DEBUG_mode"ON for debug or OFF for release" ON) IF(DEBUG_mode) add_definitions(-DDEBUG) ENDIF()
  • 1
  • 2
  • 3
  • 4

使其生效的方法:首先cmake生成makefile,而后make edit_cache编辑编译选项;Linux下会打开一个文本框,能够更改,改完后再make生成目标文件——emacs不支持make edit_cache

局限:这种方法不能直接设置生成的makefile,而是必须使用命令在make前设置参数;对于debug、release版本,至关于须要两个目录,分别先cmake一次,而后分别makeedit_cache一次;

指望的效果:在执行cmake时直接经过参数指定一个开关项,生成相应的makefile

相关文章
相关标签/搜索