CMake::CMakeLists.txt基本語法及常用

CMakeLists.txt基本語法及常用


常用變量:

變量名 含義
PROJECT_NAME project命令中寫的項目名
CMAKE_VERSION 當前使用CMake的版本
CMAKE_SOURCE_DIR 工程頂層目錄,即入口CMakeLists文件所在路徑
PROJECT_SOURCE_DIR CMAKE_SOURCE_DIR
CMAKE_BINARY_DIR 工程編譯發生的目錄,即執行cmake命令進行項目配置的目錄,一般爲build
PROJECT_BINARY_DIR CMAKE_BINARY_DIR
CMAKE_CURRENT_SOURCE_DIR 當前處理的CMakeLists.txt所在的路徑
CMAKE_CURRRENT_BINARY_DIR 當前處理的CMakeLists.txt中生成目標文件所在編譯目錄
CMAKE_CURRENT_LIST_FILE 輸出調用這個變量的CMakeLists.txt文件的完整路徑
CMAKE_CURRENT_LIST_DIR 當前處理的CMakeLists.txt文件所在目錄的路徑
CMAKE_INSTALL_PREFIX 指定make install命令執行時包安裝路徑
CMAKE_MODULE_PATH find_package命令搜索包路徑之一,默認爲空

0 . Set

使用set來設置變量

(1)使用語法 ${VariableName} 來訪問名字爲 VariableName 的變量的值(變量名區分大小寫)。需要注意的是,即使在字符串中也可以使用 ${VariableName} 來訪問變量的值: 

set(VAR a b c)

#輸出 VAR = a;b;c

message("VAR = ${VAR}")

(2)設置 CMAKE_C_FLAGS 變量和 CMAKE_CXX_FLAGS 變量等

變量名 含義
CMAKE_BUILD_TYPE 編譯選項,Release或者Debug,如set(CMAKE_BUILD_TYPE "Release")
CMAKE_CXX_FLAGS 編譯標誌,設置C++11編譯,set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
CMAKE_CXX_STANDARD 也可以設置C++11編譯,set(CMAKE_CXX_STANDARD 11)
   

1 . ADD_DEFINITIONS

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

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

(1)add_executable 命令

命令語法:

add_executable(<name> [WIN32] [MACOSX_BUNDLE][EXCLUDE_FROM_ALL] source1 source2 … sourceN)

命令簡述:用於指定從一組源文件 source1 source2 … sourceN 編譯出一個可執行文件且命名爲 name

使用範例:

add_executable(Main ${DIR_SRCS})

(2)add_library 命令

命令語法:

add_library([STATIC | SHARED | MODULE] [EXCLUDE_FROM_ALL] source1source2 … sourceN)

命令簡述:用於指定從一組源文件 source1 source2 … sourceN 編譯出一個庫文件且命名爲 name

使用範例:

add_library(Lib ${DIR_SRCS})

(3) add_subdirectory 命令

命令語法:

add_subdirectory(source_dir [binary_dir] [EXCLUDE_FROM_ALL])

命令簡述:用於添加一個需要進行構建的子目錄

使用範例:

add_subdirectory(Lib)

4. ADD_TEST 與 ENABLE_TESTING

ENABLE_TESTING
指令用來控制 Makefile 是否構建 test 目標,涉及工程所有目錄。語法很簡單,沒有任何參數, ENABLE_TESTING() ,一般情況這個指令放在工程的主CMakeLists.txt 中 .

(1)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.8 FATAL_ERROR)

如果 cmake 版本小與 2.8 ,則出現嚴重錯誤,整個過程中止。


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)

(1)WRITE

WRITE 將一則信息寫入文件’filename’中,如果該文件存在,它會覆蓋它,如果不存在,它會創建該文件

(2)APPEND

APPEND 如同WRITE,區別在於它將信息內容追加到文件末尾。

(3)READ

READ 會讀取文件的內容並將其存入到變量中。它會在給定的偏移量處開始讀取最多numBytes個字節。如果指定了HEX參數,二進制數據將會被轉換成十進制表示形式並存儲到變量中。

(4)GLOB 遍歷

1)目錄的遍歷

# GLOB 用於產生一個文件(目錄)路徑列表並保存在variable 中

# 文件路徑列表中的每個文件的文件名都能匹配globbing expressions(非正則表達式,但是類似)

# 如果指定了 RELATIVE 路徑,那麼返回的文件路徑列表中的路徑爲相對於 RELATIVE 的路徑
file(GLOB variable [RELATIVE path][globbing expressions]...)

2) 獲取當前目錄下的所有的文件(目錄)的路徑並保存到 ALL_FILE_PATH 變量中

file(GLOB ALL_FILE_PATH ./*)

3)獲取當前目錄下的 .h 文件的文件名並保存到ALL_H_FILE 變量中

# 這裏的變量CMAKE_CURRENT_LIST_DIR 表示正在處理的 CMakeLists.txt 文件的所在的目錄的絕對路徑(2.8.3 以及以後版本才支持)

file(GLOB ALL_H_FILE RELATIVE${CMAKE_CURRENT_LIST_DIR} ${CMAKE_CURRENT_LIST_DIR}/*.h)

(5)REMOVE

REMOVE 會刪除指定的文件以及子目錄下的文件

(6)REMOVE_RECURSE

REMOVE_RECURSE 會刪除指定的文件及子目錄,包括非空目錄

(7)MAKE_DIRECTORY

MAKE_DIRECTORY在指定目錄處創建子目錄,如果它們的父目錄不存在,也會創建它們的父目錄

(8)RELATIVE_PATH

RELATIVE_PAT推斷出指定文件相對於特定目錄的路徑

...


9 .INCLUDE

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

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

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

應用舉例:

include(./common.cmake) # 指定包含文件的全路徑

set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake) # 設置include的搜索路徑
include(def) # 在搜索路徑中搜索def.cmake文件

10.INSTALL(引自:Install用法

install方法的基礎用法如下

install(TARGETS MyLib
        EXPORT MyLibTargets 
        LIBRARY DESTINATION lib  # 動態庫安裝路徑
        ARCHIVE DESTINATION lib  # 靜態庫安裝路徑
        RUNTIME DESTINATION bin  # 可執行文件安裝路徑
        PUBLIC_HEADER DESTINATION include  # 頭文件安裝路徑
        )

LIBRARY, ARCHIVE, RUNTIME, PUBLIC_HEADER是可選的,可以根據需要進行選擇。

DESTINATION後面的路徑可以自行制定,根目錄默認爲CMAKE_INSTALL_PREFIX,可以試用set方法進行指定,

如果使用默認值的話,

  • Unix系統的默認值爲 /usr/local,
  • Windows的默認值爲 c:/Program Files/${PROJECT_NAME}
  • 比如linux系統下若LIBRARY的安裝路徑指定爲lib,即爲/usr/local/lib。所以要安裝mymath mymathapp我們可以這樣寫
# 將庫文件,可執行文件,頭文件安裝到指定目錄
install(TARGETS mymath mymathapp
        EXPORT MyMathTargets
        LIBRARY DESTINATION lib
        ARCHIVE DESTINATION lib
        RUNTIME DESTINATION bin
        PUBLIC_HEADER DESTINATION include
        )

他人如果使用我們編寫的函數庫,安裝完成後,希望可以通過find_package方法進行引用,這時我們需要怎麼做呢。

1. 首先我們需要生成一個MyMathConfigVersion.cmake的文件來聲明版本信息

# 寫入庫的版本信息
include(CMakePackageConfigHelpers)
write_basic_package_version_file(
        MyMathConfigVersion.cmake
        VERSION ${PACKAGE_VERSION}
        COMPATIBILITY AnyNewerVersion  # 表示該函數庫向下兼容
        )

其中PACKAGE_VERSION便是我們在CMakeLists.txt開頭project(Installation VERSION 1.0)中聲明的版本號

2. 第二步我們將前面EXPORT MyMathTargets的信息寫入到MyLibTargets.cmake文件中, 該文件存放目錄爲${CMAKE_INSTALL_PREFIX}/lib/cmake/MyMath

install(EXPORT MyMathTargets
        FILE MyLibTargets.cmake
        NAMESPACE MyMath::
        DESTINATION lib/cmake/MyLib
        )

3. 最後我們在源代碼目錄新建一個MyMathConfig.cmake.in文件,用於獲取配置過程中的變量,並尋找項目依賴包。如果不依賴外部項目的話,可以直接include MyMathTargets.cmake文件

include(CMakeFindDependencyMacro)

# 如果想要獲取Config階段的變量,可以使用這個
# set(my-config-var @my-config-var@)

# 如果你的項目需要依賴其他的庫,可以使用下面語句,用法與find_package相同
# find_dependency(MYDEP REQUIRED)

# Any extra setup

# Add the targets file
include("${CMAKE_CURRENT_LIST_DIR}/MyMathTargets.cmake")

4. 最後在CMakeLists.txt文件中,配置生成MyMathTargets.cmake文件,並一同安裝到${CMAKE_INSTALL_PREFIX}/lib/cmake/MyMath目錄中。

configure_file(MyMathConfig.cmake.in MyMathConfig.cmake @ONLY)
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/MyMathConfig.cmake"
                "${CMAKE_CURRENT_BINARY_DIR}/MyMathConfigVersion.cmake"
        DESTINATION lib/cmake/MyMath
        )

5. 最後我們在其他項目中,就可以使用

find_package(MyMath 1.0)
target_linked_library(otherapp MyMath::mymath)

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...]])
Find_Package()用法

如果程序中使用了外部庫,事先並不知道它的頭文件和鏈接庫的位置,就要給出頭文件和鏈接庫的查找方法,並將他們鏈接到程序中。

FIND_PACKAGE(<name> [major.minor] [QUIET] [NO_MODULE] 
[[REQUIRED|COMPONENTS] [componets...]])

1) find_package()的查找路徑

find_package()命令首先會在模塊路徑中尋找 一個事先編譯好的Find.cmake文件,而且一般官方給出了很多,不需要自己編寫這是查找庫的一個典型方式。
具體查找路徑依次爲CMake:

模塊模式

  • ${CMAKE_MODULE_PATH}中的所有目錄。
  • 模塊目錄 /share/cmake-x.y/Modules/ 這稱爲。

配置模式

  • ~/.cmake/packages/或/usr/local/share/中的各個包目錄中查找,尋找<庫名字的大寫>Config.cmake 或者 <庫名字的小寫>-config.cmake (比如庫Opencv,它會查找/usr/local/share/OpenCV中的OpenCVConfig.cmake或opencv-config.cmake)。

2) *.cmake文件定義變量

不管使用哪一種模式,只要找到.cmake,.cmake裏面都會定義下面這些變量:

<NAME>_FOUND
<NAME>_INCLUDE_DIRS or <NAME>_INCLUDES
<NAME>_LIBRARIES or <NAME>_LIBRARIES or <NAME>_LIBS
<NAME>_DEFINITIONS

注意大部分包的這些變量中的包名是全大寫的,如 LIBFOO_FOUND ,有些包則使用包的實際大小寫,如 LibFoo_FOUND

3)添加頭文件與鏈接庫文件

如果找到這個包,則可以通過在工程的頂層目錄中的CMakeLists.txt 文件添加 include_directories(_INCLUDE_DIRS) 來包含庫的頭文件,添加target_link_libraries(源文件 _LIBRARIES)命令將源文件與庫文件鏈接起來。

4) 鏈接OpenCV的例子

創建t4目錄添加cmake目錄與main.cpp與CMakeList.txt文件

創建cmake目錄添加FindOpenCV.cmake文件。

CMakeList.txt

cmake_minimum_required(VERSION 2.8)
PROJECT (HELLO)
SET(SRC_LIST main.cpp)

INCLUDE_DIRECTORIES(cmake)

SET(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake)
#在${CMAKE_MODULE_PATH}中添加包含FindOpenCV.cmake目錄

FIND_PACKAGE(OpenCV)
#獲取OPENCV_FOUND OPENCV_INCLUDE_DIR OPENCV_LIBRARIES

INCLUDE_DIRECTORIES(${OPENCV_INCLUDE_DIR})

ADD_EXECUTABLE(hello ${SRC_LIST})
TARGET_LINK_LIBRARIES(hello ${OPENCV_LIBRARIES})

 


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 指令的使用方法有三種形式:

AUX_SOURCE_DIRECTORY(. SRC_LIST)
FOREACH(F ${SRC_LIST})
MESSAGE(${F})
ENDFOREACH(F)

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><name>爲函數名字
  • arg1、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><name>爲函數名字
  • arg1、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裏的函數支持遞歸調用。



參考:

https://blog.csdn.net/qq_35503971/article/details/102754613

https://www.cnblogs.com/fnlingnzb-learner/p/7202222.html

https://blog.csdn.net/zhanghm1995/article/details/80902807

https://www.cnblogs.com/narjaja/p/9533181.html

https://www.cnblogs.com/never--more/p/6921837.html

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