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

相關文章
相關標籤/搜索