說到cmake
,可能最先想到的就是CmakeLists.txt
文件,但是在很多情況下,也會看到.cmake
文件。也許,你會詫異,.cmake
文件是幹什麼的,甚至會想.cmake
文件是不是cmake
的正統文件,而CmakeLists.txt
並不是。
但其實,CmakeLists.txt
纔是cmake
的正統文件,而.cmake
文件是一個模塊文件,可以被include
到CMakeLists.txt
中。
include指令
include指令一般用於語句的複用,也就是說,如果有一些語句需要在很多CMakeLists.txt文件中使用,爲避免重複編寫,可以將其寫在.cmake文件中,然後在需要的CMakeLists.txt文件中進行include操作就行了。
include指令的結構爲:
include(<file|module> [OPTIONAL] [RESULT_VARIABLE <var>]
[NO_POLICY_SCOPE])
雖然,有不少的可選參數,但是一般情況下,都是直接寫:
include(file|module)
注意,爲了使CMakeLists.txt
能夠找到該文件,需要指定文件完整路徑(絕對路徑或相對路徑),當然如果指定了CMAKE_MODULE_PATH
,就可以直接include
該目錄下的.cmake
文件了。
.cmake
文件裏面通常是什麼信息呢?
.cmake文件裏包含了一些cmake命令和一些宏/函數,當CMakeLists.txt包含該.cmake文件時,當編譯運行時,該.cmake裏的一些命令就會在該包含處得到執行,並且在包含以後的地方能夠調用該.cmake裏的一些宏和函數。
什麼是宏?什麼是函數?
MACRO宏和function函數
宏和函數的定義
先看一下關鍵字:cmake
的宏是MACRO
,函數是function
。它們的用法是:
macro(<name> [arg1 [arg2 [arg3 ...]]])
COMMAND1(ARGS ...) # 命令語句
COMMAND2(ARGS ...)
...
endmacro()
function(<name> [arg1 [arg2 [arg3 ...]]])
COMMAND1(ARGS ...) # 命令語句
COMMAND2(ARGS ...)
...
function()
定義一個名稱爲name
的宏(函數),arg1...
是傳入的參數。我們除了可以用${arg1}
來引用變量以外,系統爲我們提供了一些特殊的變量:
變量 | 說明 |
---|---|
argv# | #是一個下標,0指向第一個參數,累加 |
argv | 所有的定義時要求傳入的參數 |
argn | 定義時要求傳入的參數以外的參數 |
argc | 傳入的實際參數的個數,也就是調用函數是傳入的參數個數 |
宏和函數的區別
那麼宏和函數之間的區別是什麼呢?
其實和C/C++
裏面宏和函數之間的區別差不多,宏就是字符串替換,函數就是使用變量,在命令中途可以對改變量進行修改。
以StackOverflow
的例子來了解一下區別:
首先創建一個CMakeLists.txt
:
cmake_minimum_required(VERSION 3.0)
include(test.cmake)
在同目錄下創建文件test.cmake
:
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})
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})
運行cmake:
mkdir build && cd build
cmake ..
運行後的輸出結果是:
-- The C compiler identification is GNU 5.4.0
-- The CXX compiler identification is GNU 5.4.0
-- Check for working C compiler: /usr/bin/cc
-- Check for working C compiler: /usr/bin/cc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Detecting C compile features
-- Detecting C compile features - done
-- Check for working CXX compiler: /usr/bin/c++
-- Check for working CXX compiler: /usr/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Detecting CXX compile features
-- Detecting CXX compile features - done
=== Call macro ===
arg = ABC
# After change the value of arg.
arg = ABC
=== Call function ===
arg = ABC
# After change the value of arg.
arg = abc
-- Configuring done
-- Generating done
-- Build files have been written to: /home/yngzmiao/test/build
從這裏可以看出,宏實現的僅僅是字符串替換,宏定義的過程中是無法進行修改的,而函數卻是可以的。
蛋疼的參數
一般情況下,從上面的例子就能看出宏和函數的用法了,但很多情況下,我們自以爲的“懂了”都是假懂。比如一不小心,就會出錯。
更換test.cmake
爲下面的內容,並運行:
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)
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)
運行後的輸出結果是:
-- The C compiler identification is GNU 5.4.0
-- The CXX compiler identification is GNU 5.4.0
-- Check for working C compiler: /usr/bin/cc
-- Check for working C compiler: /usr/bin/cc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Detecting C compile features
-- Detecting C compile features - done
-- Check for working CXX compiler: /usr/bin/c++
-- Check for working CXX compiler: /usr/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Detecting CXX compile features
-- Detecting CXX compile features - done
=== Call macro ===
arg = var
# After change the value of arg.
arg = var
=== Call function ===
arg = var
# After change the value of arg.
arg = abc
-- Configuring done
-- Generating done
-- Build files have been written to: /home/yngzmiao/test/build
對比兩段程序可以看出其中的區別:無論是宏還是函數,當調用的時候如果使用的是set
出來的變量,都必須通過${}
將變量的內容傳遞進去,而不能只寫上變量名。
這是將實參傳遞給形參時的注意點,但在宏和函數的實現過程中,還有需要注意的內容。
例子:
set(var "ABC")
macro(Moo arg)
if (arg STREQUAL "ABC")
message("arg1 = ${arg}")
endif()
if (${arg} STREQUAL "ABC")
message("arg2 = ${arg}")
endif()
endmacro()
message("=== Call macro ===")
Moo(${var})
function(Foo arg)
if (arg STREQUAL "ABC")
message("arg1 = ${arg}")
endif()
if (${arg} STREQUAL "ABC")
message("arg2 = ${arg}")
endif()
endfunction()
message("=== Call function ===")
Foo(${var})
運行後的輸出結果是:
-- The C compiler identification is GNU 5.4.0
-- The CXX compiler identification is GNU 5.4.0
-- Check for working C compiler: /usr/bin/cc
-- Check for working C compiler: /usr/bin/cc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Detecting C compile features
-- Detecting C compile features - done
-- Check for working CXX compiler: /usr/bin/c++
-- Check for working CXX compiler: /usr/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Detecting CXX compile features
-- Detecting CXX compile features - done
=== Call macro ===
arg2 = ABC
=== Call function ===
arg1 = ABC
arg2 = ABC
-- Configuring done
-- Generating done
-- Build files have been written to: /home/yngzmiao/test/build
可以看出,在宏和函數的實現過程中,宏的參數由於不是傳統意義上的變量,而是字符串替換,因此需要通過${}
取出內容。而函數卻不一定需要這樣。
也就是說,對於macro
宏而言:
if(argv0) # 錯誤用法
if(${argv0}) # 正確用法
if(defined argv0) # 錯誤用法
if(defined ${argv0}) # 正確用法
也就是說,對於宏和函數的參數而言:
- 當宏和函數調用的時候,如果傳遞的是經
set
設置的變量,必須通過${}
取出內容; - 在宏的定義過程中,對變量進行的操作必須通過
${}
取出內容,而函數就沒有這個必要。
相關閱讀
CMake實戰–include命令和macro宏
cmake使用教程(八)-macro和function
CMake中include指令用法介紹