天天看點

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)

最終得到的輸出是:

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工程。