cmake函數function和宏定義macro在某種程度上來說是一樣的,都是創建一段有名字的代碼稍后可以調用,還可以傳參數。
他們的定義如下:
macro定義:
macro(<name> [arg1 [arg2 [arg3 ...]]])
...
endmacro([name])
function定義:
function(<name> [arg1 [arg2 [arg3 ...]]])
...
endfunction([name])
函數和宏的默認內部變量
變量 | 說明 |
---|---|
ARGV# | ARGV0為第一個參數,ARGV1為第二個參數,依次類推 |
ARGV | 定義宏(函數)時參數為2個,實際傳了4個,則ARGV代表實際傳入的兩個 |
ARGN | 定義宏(函數)時參數為2個,實際傳了4個,則ARGN代表剩下的兩個 |
ARGC | 實際傳入的參數的個數 |
從定義上看他們貌似一模一樣,宏和函數確實差不多,宏跟C語言中的宏概念不一樣,不過還是有一點區別
相同點
調用方式一模一樣,都是name(arg1....)形式調用,都是要先聲明在調用,實際傳入參數個數可以大於定義的參數個數
使用示例:
在sample9創建macro_function.cmake,內容如下:
# 定義函數 Function(myfunction ag1 ag2 ag3) message(STATUS "function ag is " ${ag1}) message(STATUS "function ag is " ${ag2}) message(STATUS "function ag is " ${ag3}) endfunction(myfunction) # 定義宏 macro(mymacro ag1 ag2 ag3) message(STATUS "macro ag is " ${ag1}) message(STATUS "macro ag is " ${ag2}) message(STATUS "macro ag is " ${ag3}) endmacro(mymacro) # 調用函數 myfunction(1 2 3 4 5) message(STATUS "\n") # 調用宏 mymacro(1 2 3 4 5)
輸出如下:
-- function ag is 1
-- function ag is 2
-- function ag is 3
--
-- macro ag is 1
-- macro ag is 2
-- macro ag is 3
不同點
宏的ARGN、ARGV等內部變量不能直接在if語句和foreach(..IN LISTS..)語句中使用。其它一樣
示例如下:
macro(_bar) message(STATUS "this is in macro bar " ${ARGN}) set(aa ${ARGV0}) if(ARGV0) message(STATUS "this is in a 1") endif() if(aa) message(STATUS "this is in a 2 " ${aa}) endif() set(list_var ${ARGN}) foreach(arg IN LISTS ARGN) message(STATUS "this is in macro 2 ${arg}") endforeach() foreach(arg ${ARGN}) message(STATUS "this is in macro 3 ${arg}") endforeach() foreach(arg IN LISTS list_var) message(STATUS "this is in macro 4 ${arg}") endforeach() endmacro(_bar) message(STATUS "\n") _bar(a b c) function(_func) message(STATUS "this is in func __func " ${ARGN}) set(aa ${ARGV0}) if(ARGV0) message(STATUS "this is in a 1 " ${ARGV0}) endif() if(aa) message(STATUS "this is in a 2 " ${aa}) endif() set(list_var ${ARGN}) foreach(arg IN LISTS ARGN) message(STATUS "this is in __func 2 ${arg}") endforeach() foreach(arg IN LISTS list_var) message(STATUS "this is in __func 3 ${arg}") endforeach() endfunction(_func) message(STATUS "\n") _func(d e f)
macro(_bar) message(STATUS "this is in macro bar " ${ARGN}) set(aa ${ARGV0}) if(ARGV0) message(STATUS "this is in a 1") endif() if(aa) message(STATUS "this is in a 2 " ${aa}) endif() set(list_var ${ARGN}) foreach(arg IN LISTS ARGN) message(STATUS "this is in macro 2 ${arg}") endforeach() foreach(arg ${ARGN}) message(STATUS "this is in macro 3 ${arg}") endforeach() foreach(arg IN LISTS list_var) message(STATUS "this is in macro 4 ${arg}") endforeach() endmacro(_bar) message(STATUS "\n") _bar(a b c) function(_func) message(STATUS "this is in func __func " ${ARGN}) set(aa ${ARGV0}) if(ARGV0) message(STATUS "this is in a 1 " ${ARGV0}) endif() if(aa) message(STATUS "this is in a 2 " ${aa}) endif() set(list_var ${ARGN}) foreach(arg IN LISTS ARGN) message(STATUS "this is in __func 2 ${arg}") endforeach() foreach(arg IN LISTS list_var) message(STATUS "this is in __func 3 ${arg}") endforeach() endfunction(_func) message(STATUS "\n") _func(d e f)
輸出:
-- this is in macro bar abc -- this is in a 2 a -- this is in macro 3 a -- this is in macro 3 b -- this is in macro 3 c -- this is in macro 4 a -- this is in macro 4 b -- this is in macro 4 c -- -- this is in func __func def -- this is in a 1 d -- this is in a 2 d -- this is in __func 2 d -- this is in __func 2 e -- this is in __func 2 f -- this is in __func 3 d -- this is in __func 3 e -- this is in __func 3 f
模塊
cmake能夠識別CMakeLists.txt文件和xxx.cmake結尾的文件,模塊就是以xxx.cmake結尾的文件,可以理解為,將一些通用的函數功能封裝到到一個指定的文件中,然后通過include(xxx.cmake)方式引用,這樣可以達到代碼復用的目的。模塊既可以被CMakeLists.txt引用,也可以被其它模塊引用
cmake系統本身內置了一些預定義的模塊可以供我們使用,比如FindCURL模塊,它的使用方式是通過FIND_PACKAGE指令來完成的,請看如下例子:
創建sample10,建立src/main.cpp文件,內容如下:
#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 = "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.baidu.com"); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data); curl_easy_setopt(curl, CURLOPT_VERBOSE, 1); res = curl_easy_perform(curl); curl_easy_cleanup(curl); return 0; }
這段代碼的作用是通過curl [取回www.baidu.com]的首頁並寫入當前目錄下的curl-test 文件中。
創建工程根目錄的CMakeLists.txt文件,內容如下:
# CMake 最低版本號要求 cmake_minimum_required (VERSION 2.8) if(POLICY CMP0042) cmake_policy(SET CMP0042 NEW) # CMake 3.0+ (2.8.12): MacOS "@rpath" in target's install name endif() # 項目工程名 project (sample10) message(STATUS "root This is BINARY dir " ${PROJECT_BINARY_DIR}) message(STATUS "root This is SOURCE dir " ${PROJECT_SOURCE_DIR}) # 添加子目錄 ADD_SUBDIRECTORY(src)
創建src/CMakeLists.txt,內容如下:
# 打印信息 message(STATUS "src This is BINARY dir " ${PROJECT_BINARY_DIR}) message(STATUS "src This is SOURCE dir " ${PROJECT_SOURCE_DIR}) set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin) # 定義工程根目錄; CMAKE_SOURCE_DIR為內建變量,表示工程根目錄的CMakeLists.txt文件路徑 SET(ROOT_DIR ${CMAKE_SOURCE_DIR}) # 構建可執行程序 ADD_EXECUTABLE(sample10 main.cpp) # 查找指定的庫 FIND_PACKAGE(CURL) IF(CURL_FOUND) MESSAGE(STATUS ”CURL library ${CURL_INCLUDE_DIR}”) MESSAGE(STATUS ”CURL library ${CURL_LIBRARY}”) INCLUDE_DIRECTORIES(${CURL_INCLUDE_DIR}) TARGET_LINK_LIBRARIES(sample10 ${CURL_LIBRARY}) ELSE(CURL_FOUND) MESSAGE(FATAL_ERROR ”CURL library not found”) ENDIF(CURL_FOUND)
在build文件夾內構建工程 cmake .. 然后執行 make,執行bin/sample10,就模擬了一次curl操作,將百度的首頁內容寫入到curl-text文件中了
cmake中關於內置Find.cmake模塊的使用語法:
對於系統預定義的Find.cmake 模塊,使用方法一般如上例所示:每一個模塊都會定義以下幾個變量
- _FOUND
- _INCLUDE_DIR or _INCLUDES
- _LIBRARY or _LIBRARIES
比如上面是要查找CURL庫的頭文件以及庫文件路徑,對應的變量就是CURL_FOUND、CURL_INCLUDE_DIR、CURL_INCLUDE_DIR
通過_FOUND 來判斷模塊是否找到,如果_FOUND 為真,則將_INCLUDE_DIR 加入INCLUDE_DIRECTORIES,將_LIBRARY 加入TARGET_LINK_LIBRARIES 中。
看如下一段代碼,通過_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) ADD_EXECUTABLE(viewer ${mySources} ${optionalSources} ) TARGET_LINK_LIBRARIES(viewer ${optionalLibs}
通過判斷系統是否提供了JPEG 庫來決定程序是否支持JPEG 功能。