1. 程式人生 > >CMake常用變數和常量環境變數查表手冊——整理

CMake常用變數和常量環境變數查表手冊——整理

一,cmake 變數引用的方式:

前面我們已經提到了,使用${}進行變數的引用。在 IF 等語句中,是直接使用變數名而不通過${}取值

二,cmake 自定義變數的方式:

主要有隱式定義和顯式定義兩種,前面舉了一個隱式定義的例子,就是 PROJECT 指令,他會隱式的定義<projectname>_BINARY_DIR 和<projectname>_SOURCE_DIR 兩個變數。
顯式定義的例子我們前面也提到了,使用 SET 指令,就可以構建一個自定義變量了。
比如:
SET(HELLO_SRC main.SOURCE_PATHc),就 PROJECT_BINARY_DIR 可以通過${HELLO_SRC}來引用這個自定義變量了.
 

三,cmake 常用變數:

1,CMAKE_BINARY_DIR
  PROJECT_BINARY_DIR
 <projectname>_BINARY_DIR
這三個變數指代的內容是一致的,如果是 in source 編譯,指得就是工程頂層目錄,如果是 out-of-source 編譯,指的是工程編譯發生的目錄。PROJECT_BINARY_DIR 跟其他指令稍有區別,現在,你可以理解為他們是一致的。
2,CMAKE_SOURCE_DIR
   PROJECT_SOURCE_DIR
   <projectname>_SOURCE_DIR
這三個變數指代的內容是一致的,不論採用何種編譯方式,都是工程頂層目錄。
也就是在 in source 編譯時,他跟 CMAKE_BINARY_DIR 等變數一致。
PROJECT_SOURCE_DIR 跟其他指令稍有區別,現在,你可以理解為他們是一致的。

3,CMAKE_CURRENT_SOURCE_DIR
指的是當前處理的 CMakeLists.txt 所在的路徑,比如上面我們提到的 src 子目錄。

4,CMAKE_CURRRENT_BINARY_DIR
如果是 in-source 編譯,它跟 CMAKE_CURRENT_SOURCE_DIR 一致,如果是 out-of-source 編譯,他指的是 target 編譯目錄。
使用我們上面提到的 ADD_SUBDIRECTORY(src bin)可以更改這個變數的值。
使用 SET(EXECUTABLE_OUTPUT_PATH <新路徑>)並不會對這個變數造成影響,它僅僅修改了最終目標檔案存放的路徑。

5,CMAKE_CURRENT_LIST_FILE

輸出呼叫這個變數的 CMakeLists.txt 的完整路徑

 

6,CMAKE_CURRENT_LIST_LINE

輸出這個變數所在的行

 

7,CMAKE_MODULE_PATH
這個變數用來定義自己的 cmake 模組所在的路徑。如果你的工程比較複雜,有可能會自己編寫一些 cmake 模組,這些 cmake 模組是隨你的工程釋出的,為了讓 cmake 在處理CMakeLists.txt 時找到這些模組,你需要通過 SET 指令,將自己的 cmake 模組路徑設定一下。
比如
SET(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake)
這時候你就可以通過 INCLUDE 指令來呼叫自己的模組了。

8,EXECUTABLE_OUTPUT_PATH 和 LIBRARY_OUTPUT_PATH
分別用來重新定義最終結果的存放目錄,前面我們已經提到了這兩個變數。

9,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 我們在上一節已經提及。

五,系統資訊

1,CMAKE_MAJOR_VERSION,CMAKE 主版本號,比如 2.4.6 中的 2
2,CMAKE_MINOR_VERSION,CMAKE 次版本號,比如 2.4.6 中的 4
3,CMAKE_PATCH_VERSION,CMAKE 補丁等級,比如 2.4.6 中的 6
4,CMAKE_SYSTEM,系統名稱,比如 Linux-2.6.22
5,CMAKE_SYSTEM_NAME,不包含版本的系統名,比如 Linux
6,CMAKE_SYSTEM_VERSION,系統版本,比如 2.6.22
7,CMAKE_SYSTEM_PROCESSOR,處理器名稱,比如 i686.
8,UNIX,在所有的類 UNIX 平臺為 TRUE,包括 OS X 和 cygwin
9,WIN32,在所有的 win32 平臺為 TRUE,包括 cygwin

六,主要的開關選項:

1,CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS,用來控制 IF ELSE 語句的書寫方式,在
下一節語法部分會講到。
2,BUILD_SHARED_LIBS
這個開關用來控制預設的庫編譯方式,如果不進行設定,使用 ADD_LIBRARY 並沒有指定庫
型別的情況下,預設編譯生成的庫都是靜態庫。
如果 SET(BUILD_SHARED_LIBS ON)後,預設生成的為動態庫。
3,CMAKE_C_FLAGS
設定 C 編譯選項,也可以通過指令 ADD_DEFINITIONS()新增。
4,CMAKE_CXX_FLAGS
設定 C++編譯選項,也可以通過指令 ADD_DEFINITIONS()新增。
小結:
本章介紹了一些較常用的 cmake 變數,這些變數僅僅是所有 cmake 變數的很少一部分,目
前 cmake 的英文文件也是比較缺乏的,如果需要了解更多的 cmake 變數,更好的方式是閱
讀一些成功專案的 cmake 工程檔案,比如 KDE4 的程式碼。
 

八,cmake 常用指令

前面我們講到了 cmake 常用的變數,相信“cmake 即程式設計”的感覺會越來越明顯,無論如何,我們仍然可以看到 cmake 比 autotools 要簡單很多。接下來我們就要集中的看一看cmake 所提供的常用指令。在前面的章節我們已經討論了很多指令的用法,如
PROJECT,ADD_EXECUTABLE,INSTALL,ADD_SUBDIRECTORY,SUBDIRS,
INCLUDE_DIRECTORIES,LINK_DIRECTORIES,TARGET_LINK_LIBRARIES,SET 等。
本節會引入更多的 cmake 指令,為了編寫的方便,我們將按照 cmake man page 的順序來介紹各種指令,不再推薦使用的指令將不再介紹,INSTALL 系列指令在安裝部分已經做了非常詳細的說明,本節也不在提及。(你可以將本章理解成選擇性翻譯,但是會加入更多的個人理解)

一,基本指令
1,ADD_DEFINITIONS
向 C/C++編譯器新增-D 定義,比如:
ADD_DEFINITIONS(-DENABLE_DEBUG -DABC),引數之間用空格分割。

如果你的程式碼中定義了#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 ...)


3,ADD_EXECUTABLE、ADD_LIBRARY、ADD_SUBDIRECTORY 前面已經介紹過了,這裡不再羅唆。


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 <arguments to executable>]
                [OUTPUT_VARIABLE <var>]
                [RETURN_VALUE <var>])
用於在指定的目錄執行某個程式,通過 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 語句,後面的控制指令會提到。


8,FILE 指令
檔案操作指令,基本語法為:
       FILE(WRITE filename "message to write"... )
       FILE(APPEND filename "message to write"... )
       FILE(READ filename variable)
       FILE(GLOB variable [RELATIVE path] [globbing expression_r_rs]...)
       FILE(GLOB_RECURSE variable [RELATIVE path] [globbing expression_r_rs]...)
       FILE(REMOVE [directory]...)
       FILE(REMOVE_RECURSE [directory]...)
       FILE(MAKE_DIRECTORY [directory]...)
       FILE(RELATIVE_PATH variable directory file)
       FILE(TO_CMAKE_PATH path result)
       FILE(TO_NATIVE_PATH path result)
這裡的語法都比較簡單,不在展開介紹了。


9,INCLUDE 指令,用來載入 CMakeLists.txt 檔案,也用於載入預定義的 cmake 模組.
       INCLUDE(file1 [OPTIONAL])
       INCLUDE(module [OPTIONAL])
OPTIONAL 引數的作用是檔案不存在也不會產生錯誤。
你可以指定載入一個檔案,如果定義的是一個模組,那麼將在 CMAKE_MODULE_PATH 中搜索這個模組並載入。
載入的內容將在處理到 INCLUDE 語句是直接執行。
二,INSTALL 指令
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 模組的編寫。


FIND_LIBRARY 示例:
FIND_LIBRARY(libX X11 /usr/lib)
IF(NOT libX)
MESSAGE(FATAL_ERROR “libX not found”)
ENDIF(NOT libX)

 

四,控制指令:

1,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)
另外一個指令是 ELSEIF,總體把握一個原則,凡是出現 IF 的地方一定要有對應的ENDIF.出現 ELSEIF 的地方,ENDIF 是可選的。
表示式的使用方法如下:
IF(var),如果變數不是:空,0,N, NO, OFF, FALSE, NOTFOUND 或<var>_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 時為真。比如:
IF("hello" MATCHES "ell")
MESSAGE("true")
ENDIF("hello" MATCHES "ell")
IF(variable LESS number)
IF(string LESS number)
IF(variable GREATER number)
IF(string GREATER number)
IF(variable EQUAL number)
IF(string EQUAL number)
數字比較表示式
IF(variable STRLESS string)
IF(string STRLESS string)
IF(variable STRGREATER string)
IF(string STRGREATER string)
IF(variable STREQUAL string)
IF(string STREQUAL string)
按照字母序的排列進行比較.
IF(DEFINED variable),如果變數被定義,為真。
一個小例子,用來判斷平臺差異:
IF(WIN32)
    MESSAGE(STATUS “This is windows.”)
    #作一些 Windows 相關的操作
ELSE(WIN32)
    MESSAGE(STATUS “This is not windows”)
    #作一些非 Windows 相關的操作
ENDIF(WIN32)
上述程式碼用來控制在不同的平臺進行不同的控制,但是,閱讀起來卻並不是那麼舒服,
ELSE(WIN32)之類的語句很容易引起歧義。
這就用到了我們在“常用變數”一節提到的 CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS 開關。
可以 SET(CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS ON)
這時候就可以寫成:
IF(WIN32)
ELSE()
ENDIF()
如果配合 ELSEIF 使用,可能的寫法是這樣:
IF(WIN32)
#do something related to WIN32
ELSEIF(UNIX)
#do something related to UNIX
ELSEIF(APPLE)
#do something related to APPLE
ENDIF(WIN32)
2,WHILE
WHILE 指令的語法是:
        WHILE(condition)
          COMMAND1(ARGS ...)
          COMMAND2(ARGS ...)
          ...
        ENDWHILE(condition)
其真假判斷條件可以參考 IF 指令。
3,FOREACH
FOREACH 指令的使用方法有三種形式:
1,列表
        FOREACH(loop_var arg1 arg2 ...)
          COMMAND1(ARGS ...)
          COMMAND2(ARGS ...)
          ...
        ENDFOREACH(loop_var)
像我們前面使用的 AUX_SOURCE_DIRECTORY 的例子
AUX_SOURCE_DIRECTORY(. SRC_LIST)
FOREACH(F ${SRC_LIST})
    MESSAGE(${F})
ENDFOREACH(F)
2,範圍
FOREACH(loop_var RANGE total)
ENDFOREACH(loop_var)
從 0 到 total 以1為步進
舉例如下:
FOREACH(VAR RANGE 10)
MESSAGE(${VAR})
ENDFOREACH(VAR)
最終得到的輸出是:
0
1
2
3
4
5
6
7
8
9
10
3,範圍和步進
FOREACH(loop_var RANGE start stop [step])
ENDFOREACH(loop_var)
從 start 開始到 stop 結束,以 step 為步進,
舉例如下
FOREACH(A RANGE 5 15 3)
MESSAGE(${A})
ENDFOREACH(A)
最終得到的結果是:
5
8
11
14
這個指令需要注意的是,知道遇到 ENDFOREACH 指令,整個語句塊才會得到真正的執行。
小結:
本小節基本涵蓋了常用的 cmake 指令,包括基本指令、查詢指令、安裝指令以及控制語句等,特別需要注意的是,在控制語句條件中使用變數,不能用${}引用,而是直接應用變數名。

 

掌握了以上的各種控制指令,你應該完全可以通過 cmake 管理複雜的程式了,下一節,我
們將介紹一個比較複雜的例子,通過他來演示本章的一些指令,並介紹模組的概念。


九,複雜的例子:模組的使用和自定義模組
你現在還會覺得 cmake 簡單嗎?


本章我們將著重介紹系統預定義的 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;
int write_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);
CURLcode res;
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);
}
這段程式碼的作用是通過 curl 取回 www.linux-ren.org 的首頁並寫入/tmp/curl-test檔案中。
建立主工程檔案 CMakeLists.txt
PROJECT(CURLTEST)
ADD_SUBDIRECTORY(src)
建立 src/CMakeLists.txt
ADD_EXECUTABLE(curltest main.c)
現在自然是沒辦法編譯的,我們需要新增 curl 的標頭檔案路徑和庫檔案。
方法 1:
直接通過 INCLUDE_DIRECTORIES 和 TARGET_LINK_LIBRARIES 指令新增:
我們可以直接在 src/CMakeLists.txt 中新增:
INCLUDE_DIRECTORIES(/usr/include)
TARGET_LINK_LIBRARIES(curltest curl)
然後建立 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)
對於系統預定義的 Find<name>.cmake 模組,使用方法一般如上例所示:
每一個模組都會定義以下幾個變數
    <name>_FOUND
  •
    <name>_INCLUDE_DIR or <name>_INCLUDES
  •
    <name>_LIBRARY or <name>_LIBRARIES
  •
你可以通過<name>_FOUND 來判斷模組是否被找到,如果沒有找到,按照工程的需要關閉某些特性、給出提醒或者中止編譯,上面的例子就是報出致命錯誤並終止構建。


如果<name>_FOUND 為真,則將<name>_INCLUDE_DIR 加入 INCLUDE_DIRECTORIES,
將<name>_LIBRARY 加入 TARGET_LINK_LIBRARIES 中。
我們再來看一個複雜的例子,通過<name>_FOUND 來控制工程特性:
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 功能。


二,編寫屬於自己的 FindHello 模組。
我們在此前的 t3 例項中,演示了構建動態庫、靜態庫的過程並進行了安裝。
接下來,我們在 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_FOUND TRUE)
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)
針對上面的模組讓我們再來回顧一下 FIND_PACKAGE 指令:
       FIND_PACKAGE(<name> [major.minor] [QUIET] [NO_MODULE]
                 [[REQUIRED|COMPONENTS] [componets...]])
前面的 CURL 例子中我們使用了最簡單的 FIND_PACKAGE 指令,其實他可以使用多種引數,
QUIET 引數,對應與我們編寫的 FindHELLO 中的 HELLO_FIND_QUIETLY,如果不指定這個引數,就會執行:
MESSAGE(STATUS "Found Hello: ${HELLO_LIBRARY}")


REQUIRED 引數,其含義是指這個共享庫是否是工程必須的,如果使用了這個引數,說明這個連結庫是必備庫,如果找不到這個連結庫,則工程不能編譯。對應於
FindHELLO.cmake 模組中的 HELLO_FIND_REQUIRED 變數。
同樣,我們在上面的模組中定義了 HELLO_FOUND,
HELLO_INCLUDE_DIR,HELLO_LIBRARY 變數供開發者在 FIND_PACKAGE 指令中使用。
OK,下面建立 src/main.c,內容為:
#include <hello.h>
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)


三,使用自定義的 FindHELLO 模組構建工程
仍然採用外部編譯的方式,建立 build 目錄,進入目錄執行:
cmake ..
我們可以從輸出中看到:
Found Hello: /usr/lib/libhello.so
如果我們把上面的 FIND_PACKAGE(HELLO)修改為 FIND_PACKAGE(HELLO QUIET),則不會看到上面的輸出。
接下來就可以使用 make 命令構建工程,執行:
./src/hello 可以得到輸出
Hello World。
說明工程成功構建。
四,如果沒有找到 hello library 呢?
我們可以嘗試將/usr/lib/libhello.x 移動到/tmp 目錄,這樣,按照 FindHELLO 模組的定義,就找不到 hello library 了,我們再來看一下構建結果:
cmake ..
仍然可以成功進行構建,但是這時候是沒有辦法編譯的。
修改 FIND_PACKAGE(HELLO)為 FIND_PACKAGE(HELLO REQUIRED),將 hello library 定義為工程必須的共享庫。


這時候再次執行 cmake ..
我們得到如下輸出:
CMake Error: Could not find hello library.
因為找不到 libhello.x,所以,整個 Makefile 生成過程被出錯中止。
小結:
在本節中,我們學習瞭如何使用系統提供的 Find<NAME>模組並學習了自己編寫
Find<NAME>模組以及如何在工程中使用這些模組。
後面的章節,我們會逐漸學習更多的 cmake 模組使用方法以及用 cmake 來管理 GTK 和 QT4工程。