cmake 7




# cmake 常用變量和常用環境變量

## 一,cmake 變量引用的方式:

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


## 二,cmake 自定義變量的方式:

主要有隱式定義和顯式定義兩種. 

隱式定義的例子:
PROJECT 指令會隱式的定義`<projectname>_BINARY_DIR` 和 `<projectname>_SOURCE_DIR` 兩個變量。

顯式定義的例子:
使用 SET 指令可以構建一個自定義變量
`SET(HELLO_SRC main.SOURCE_PATHc)`,可以通過`${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-ofsource
編譯,他指的是 target 編譯目錄。
使用我們上面提到的 `ADD_SUBDIRECTORY(src bin)`可以更改這個變量的值(src變成bin)。
使用 `SET(EXECUTABLE_OUTPUT_PATH <新路徑>)`並不會對這個變量造成影響,它僅僅是設置了最終的執行文件存放路徑,編譯的中間文件仍然存放在`CMAKE_CURRENT_SOURCE_DIR`

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 常用指令

### 一,基本指令

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})
```

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)
```

8,FILE 指令
文件操作指令,基本語法爲:

```
 FILE(WRITE filename "message to write"... )
 FILE(APPEND filename "message to write"... )
 FILE(READ filename variable)
 FILE(GLOB variable [RELATIVE path] [globbing
expressions]...)
 FILE(GLOB_RECURSE variable [RELATIVE path]
 [globbing expressions]...)
 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)
 # THEN section.
 COMMAND1(ARGS ...)
 COMMAND2(ARGS ...)
 ...
 ELSE(expression)
 # ELSE section.
 COMMAND1(ARGS ...)
 COMMAND2(ARGS ...)
 ...
 ENDIF(expression)
```
另外一個指令是 ELSEIF,總體把握一個原則,凡是出現 IF 的地方一定要有對應的
ENDIF.出現 ELSEIF 的地方,ENDIF 是可選的。


表達式的使用方法如下:

- IF(DEFINED variable),如果變量被定義,爲真。
- 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(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 指令,整個語句塊纔會得到真正的執行。





# 補充

# 指定編譯器選項
SET(CMAKE_CXX_FLAGS_DEBUG "-g -Wall")          
# CMAKE_C_FLAGS_DEBUG            ----  C 編譯器
# CMAKE_CXX_FLAGS_DEBUG        ----  C++ 編譯器
# -g:只是編譯器,在編譯的時候,產生調試信息。
# -Wall:生成所有警告信息。一下是具體的選項,可以單獨使用

# 指定編譯類型,debug 或者爲 release
SET(CMAKE_BUILE_TYPE RELEASE)
# debug 版會生成相關調試信息,可以使用 GDB 進行
# release不會生成調試信息。當無法進行調試時查看此處是否設置爲 debug.


add_compile_options(-std=c++11)

#當編譯一個需要第三方庫的項目時,需要知道:
#- 去哪找頭文件(.h),-I(GCC)
#    INCLUDE_DIRECTORIES()
#- 去哪找庫文件(.so/.dll/.lib/.dylib/...),-L(GCC)
#    LINK_DIRECTORIES()
#- 需要鏈接的庫文件的名字:-l(GCC)
#    LINK_LIBRARIES(庫名稱即可)

find_package(OpenCV REQUIRED)
include_directories( ${OpenCV_INCLUDE_DIRS} )
#target_link_libraries(${PROJECT_NAME} ${OpenCV_LIBS})
SET (EXTRA_LIBS ${EXTRA_LIBS} ${OpenCV_LIBS})

#find_package(Boost 1.59.0 COMPONENTS filesystem regex REQUIRED)   # 最好是加上REQUIRED,否則找不到某些庫也不會報錯,鏈接會出問題
find_package(Boost COMPONENTS filesystem regex REQUIRED)
include_directories( ${Boost_INCLUDE_DIRS} )
#target_link_libraries(${PROJECT_NAME} ${OpenCV_LIBS})
SET (EXTRA_LIBS ${EXTRA_LIBS} ${Boost_LIBRARIES})

                   

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章