【CMake】cmake中的include指令(.cmake文件/MACRO宏/function函數)

說到cmake,可能最先想到的就是CmakeLists.txt文件,但是在很多情況下,也會看到.cmake文件。也許,你會詫異,.cmake文件是幹什麼的,甚至會想.cmake文件是不是cmake的正統文件,而CmakeLists.txt並不是。

但其實,CmakeLists.txt纔是cmake的正統文件,而.cmake文件是一個模塊文件,可以被includeCMakeLists.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指令用法介紹

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