CMake--經常使用指令

1 . ADD_DEFINITIONS

向 C/C++ 編譯器添加 -D 定義,好比 在CMakeList.txt文件中添加:express

ADD_DEFINITIONS(-DENABLE_DEBUG -DABC) #參數之間用空格分割。

代碼中有:windows

#ifdef ENABLE_DEBUG 
    ... //代碼段生效
#endif

若是要添加其餘的編譯器開關,能夠經過 CMAKE_C_FLAGS 變量和 CMAKE_CXX_FLAGS 變量設置。less


2.ADD_DEPENDENCIES

定義 target 依賴的其餘 target ,確保在編譯本 target 以前,其餘的 target 已經被構建。ide

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 中 .oop

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 來執行測試了。ui


5. AUX_SOURCE_DIRECTORY

基本語法是:spa

AUX_SOURCE_DIRECTORY(dir VARIABLE)

做用是發現一個目錄下全部的源代碼文件並將列表存儲在一個變量中,這個指令臨時被用來自動構建源文件列表。由於目前 cmake 還不能自動發現新添加的源文件。
好比code

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)

在 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] [globbingexpressions]...)
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

INCLUDE 指令,用來載入 CMakeLists.txt 文件,也用於載入預約義的 cmake 模塊 .

INCLUDE(file1 [OPTIONAL])
INCLUDE(module [OPTIONAL])

OPTIONAL 參數的做用是文件不存在也不會產生錯誤。
你能夠指定載入一個文件,若是定義的是一個模塊,那麼將在 CMAKE_MODULE_PATH 中搜索這個模塊並載入。
載入的內容將在處理到 INCLUDE 語句是直接執行。


10.INSTALL

INSTALL 系列指令已經在前面的章節有很是詳細的說明,這裏不在贅述,可參考前面的安裝部分。


11.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 .cmake 模塊,你也能夠本身定義 Find 模塊,經過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)

沒有找到libX被付爲libX-NOTFOUND


12.IF 指令

IF(expression)
COMMAND1(ARGS ...)
COMMAND2(ARGS ...)
...
ELSE(expression)
# ELSE section.
COMMAND1(ARGS ...)
COMMAND2(ARGS ...)
...
ENDIF(expression)

另一個指令是 ELSEIF

IF(expression)
# THEN section.
COMMAND1(ARGS ...)
COMMAND2(ARGS ...)
ELSEIF(expression)
# THEN section.
COMMAND1(ARGS ...)
COMMAND2(ARGS ...)
ELSEIF(expression)
# THEN section.
COMMAND1(ARGS ...)
COMMAND2(ARGS ...)
ENDIF(expression)

整體把握一個原則,凡是出現 IF 的地方必定要有對應的ENDIF。出現 ELSEIF 的地方, ENDIF 是可選的。

IF判斷表達式中經常使用的指令:

命令名 變量說明
NOT True if the expression is not true
AND True if both expressions would be considered true individually
OR True if either expression would be considered true individually
COMMAND True if the given name is a command, macro or function that can be invoked
POLICY True if the given name is an existing policy
TARGET True if the given name is an existing logical target name such as those created by the add_executable(), add_library(), or add_custom_target() commands}
EXISTS True if the named file or directory exists. Behavior is well-defined only for full paths
IS_DIRECTORY True if the given name is a directory. Behavior is well-defined only for full paths
IS_SYMLINK True if the given name is a symbolic link. Behavior is well-defined only for full paths
IS_ABSOLUTE True if the given path is an absolute path
MATCHES if(<variable\|string> MATCHES regex) True if the given string or variable’s value matches the given regular expression
LESS True if the given string or variable’s value is a valid number and less than that on the right
GREATER True if the given string or variable’s value is a valid number and greater than that on the right
EQUAL True if the given string or variable’s value is a valid number and equal to that on the right
STRLESS True if the given string or variable’s value is lexicographically less than the string or variable on the right
STRGREATER True if the given string or variable’s value is lexicographically greater than the string or variable on the right
STREQUAL True if the given string or variable’s value is lexicographically equal to the string or variable on the right
VERSION_LESS Component-wise integer version number comparison (version format is major[.minor[.patch[.tweak]]]
VERSION_EQUAL Component-wise integer version number comparison (version format is major[.minor[.patch[.tweak]]])
VERSION_GREATER Component-wise integer version number comparison (version format is major[.minor[.patch[.tweak]]])
DEFINED True if the given variable is defined. It does not matter if the variable is true or false just if it has been

一個小栗子,用來判斷平臺差別:

IF(WIN32)
MESSAGE(STATUS 「This is windows.」)
# 做一些 Windows 相關的操做
ELSE(WIN32)
MESSAGE(STATUS 「This is not windows」)
# 做一些非 Windows 相關的操做
ENDIF(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)

13.WHILE

WHILE 指令的語法是:

WHILE(condition)
COMMAND1(ARGS ...)
COMMAND2(ARGS ...)
...
ENDWHILE(condition)

其真假判斷條件能夠參考 IF 指令。


14.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
注:整個FOREACH遇到 ENDFOREACH 指令,整個語句塊纔會獲得真正的執行。
***

15.MACRO

宏定義以下:

macro(<name> [arg1 [arg2 [arg3 ...]]])
  COMMAND1(ARGS ...)
  COMMAND2(ARGS ...)
  ...
endmacro(<name>)
  • \(<name>\)爲函數名字
  • arg一、arg2...爲函數參數

舉個栗子:

set(var "ABC")

macro(Moo arg)
  message("arg = ${arg}")
  set(arg "abc")
  message("# After change the value of arg.")
  message("arg = ${arg}")
endmacro()
message("=== Call macro ===")
Moo(${var})

#輸出以下:
=== Call macro ===
arg = ABC
# After change the value of arg.
arg = ABC

這裏的宏是作了字符串的替換


16.FUNCTION

1)函數定義以下:

function(<name> [arg1 [arg2 [arg3 ...]]])
  COMMAND1(ARGS ...)
  COMMAND2(ARGS ...)
  ...
endfunction(<name>)
  • \(<name>\)爲函數名字
  • arg一、arg2...爲函數參數

舉個栗子:

set(var "ABC")
function(Foo arg)
  message("arg = ${arg}")
  set(arg "abc")
  message("# After change the value of arg.")
  message("arg = ${arg}")
endfunction()
message("=== Call function ===")
Foo(${var})

#輸出爲:
=== Call function ===
arg = ABC
# After change the value of arg.
arg = abc

上面的栗子和c語言的值傳函數比較像

2)實現相似引用傳參

set(var "abc")                      # 定義一個變量var,初值爲abc

function(f1 arg)
    set(${arg} "ABC" PARENT_SCOPE)  # ${arg} == var, 因而至關於set(var "ABC" PARENT_SCOPE)
endfunction()

message("before calling f1, var = ${var}")
f1(var)                                     # 若是寫成了f1(${var})會怎樣?
message("after calling f1, var = ${var}")

須要注意的兩點:

  • 函數調用處用變量的名字var,而不是它的值${var}
  • 在函數內部,set的時候,要加上做用域PARENT_SCOPE.

3)隱含參數

name 變量說明
ARGC 函數實參的個數
ARGV 全部實參列表
ARGN 全部額外實參列表, 即ARGV去掉函數聲明時顯示指定的實參,剩餘的實參
ARGV0 函數第1個實參
ARGV1 函數第2個實參
ARGV2 函數第3個實參
依次類推 依次類推

使用上面表格裏的幾個隱含參數,經過下面這個例子能夠更好的說明上面兩種傳遞參數的方式,函數內部發生了什麼。

function(print_list arg)
    message("======= args count : ${ARGC} ======= ")    # 實際實參個數

    message("======= all args ======= ")                # 打印全部參數
    foreach(v IN LISTS ARGV)
        message(${v})
    endforeach()


    message("======= all extra args ======= ")          # 打印全部額外參數
    foreach(v IN LISTS ARGN)
        message(${v})
    endforeach()

    message("======= print content of ARGV0 ======= ")  # 打印第一個參數裏的全部內容
    foreach(v IN LISTS ARGV0)
        message(${v})
    endforeach()
endfunction()

set(arg hello world) 

message("------------ calling with qutoes -----------")     # 使用引號來調用
print_list("${arg}")

message("------------ calling without qutoes -----------")  # 不使用引號調用
print_list(${arg})


輸出爲:
------------ calling with qutoes -----------
======= args count : 1 ======= 
======= all args ======= 
hello
world
======= all extra args ======= 
======= print content of ARGV0 ======= 
hello
world
------------ calling without qutoes -----------
======= args count : 2 ======= 
======= all args ======= 
hello
world
======= all extra args ======= 
world
======= print content of ARGV0 ======= 
hello

從兩個輸出結果裏能夠看到:

1.使用引號包裹參數時
參數個數:1, 即hello world
額外參數個數: 0
打印第一個參數的內容 = 要打印的列表內容

2.不使用引號包裹參數時
參數個數:2, 分別是 hello 和 world
額外參數個數: 1, world
打印第一個參數的內容 = hello

在不使用括號包裹的狀況下,由於函數只須要一個參數,列表裏除了第一個元素的其它元素被當作額外的參數傳給函數了,當我打印第一個參數的時候,就僅僅把列表的第一個元素打印出來了。

經過這個例子能夠看到,在不使用括號來包裹列表類型的參數做爲函數實參時,列表參數內部的空格(或者分號)會使得這個列表的內容被當作多個參數傳遞給函數。 此外,CMake裏的函數支持遞歸調用。

相關文章
相關標籤/搜索