<<Modern CMake>> 翻譯 2.2 CMake 程式設計

資料管理樂園發表於2019-07-26

<<Modern CMake>> 翻譯 2.2 CMake 程式設計

流程控制

CMake有一個 if 語句, 經年累月之後,現在它已經相當複雜。 您可以在 if 語句中使用全大寫字母書寫一系列關鍵字,並且您通常可以直接通過名稱(if語句在歷史上出現早於變數擴充套件)或使用 ${} 語法來引用變數。 下面是 if 語句的示例:

if(variable)
    # If variable is `ON`, `YES`, `TRUE`, `Y`, or non zero number
else()
    # If variable is `0`, `OFF`, `NO`, `FALSE`, `N`, `IGNORE`, `NOTFOUND`, `""`, or ends in `-NOTFOUND`
endif()
# If variable does not expand to one of the above, CMake will expand it then try again

如果您明確地進行變數擴充套件(例如 ${variable},由於擴充套件的潛在擴充套件),這可能會有點混亂。 在CMake 3.1+ 中新增了一個策略(CMP0054),使得被引號引起來的變數擴充套件不被遞迴擴充套件。 因此,只要 CMake 的最低版本是 3.1+,您就可以:

if("${variable}")
    # True if variable is not false-like
else()
    # Note that undefined variables would be `""` thus false
endif()

這裡還有一些可以在 if 命令中使用的關鍵字,例如:

  • 一元關鍵字: NOTTARGETEXISTS (檔案), DEFINED, 等等
  • 二元關鍵字: STREQUALANDORMATCHES (正規表示式), VERSION_LESSVERSION_LESS_EQUAL(CMake 3.7+), 等等
  • 括號可以用於分組

生成器表示式 (Generator-expressions)

Generator-expressions 非常強大,但也有點奇怪和專業。 大多數 CMake 命令在配置時執行,包括上面看到的 if 語句。 但是如果你需要在構建時甚至安裝時間執行邏輯呢? 為了這個目的 CMake 新增了生成器表示式(Generator-expressions)。1 它們在目標屬性中進行計算和執行。

最簡單的生成器表示式是資訊表示式,具有這樣 $<KEYWORD> 的形式; 他們評估(計算和執行)一條與當前配置相關的資訊。 另一種形式是 $<KEYWORD:value>,KEYWORD 關鍵字控制評估,value 是要評估的專案(這裡也允許使用資訊表示式關鍵字)。 如果 KEYWORD 是一個計算結果為 0 或 1 的生成器表示式或變數,value 則替換為 0 或 1。 您可以巢狀生成器表示式,並且可以使用變數來使巢狀變數可讀。 某些表示式允許使用逗號分隔的多個值。2

例如,如果你想要設定僅在 DEBUG 配置下生效的編譯標誌,則可以這樣寫:

target_compile_options(MyTarget PRIVATE "$<$<CONFIG:Debug>:--my-flag>")

這是一種比使用專用 *_DEBUG 變數更新,更好的方式,並且可以推廣到生成器表示式支援的所有情況。 請注意,您永遠不應該使用配置時的值作為當前配置,因為 IDE 之類的多配置生成器並沒有“當前”配置,僅在構建時通過生成器表示式和自定義的 *_<CONFIG> 變數起作用。

生成器表示式的其他常見用法:

  • 將專案限制為僅限某種語言(例如 CXX),以避免它與 CUDA 等混在一起,或者根據目標語言將其包裝以使其不同。
  • 訪問配置相關屬性,例如目標檔案位置。
  • 為構建和安裝目錄提供不同的位置。

最後一個很常見。幾乎每個支援安裝的軟體包都會看到類似的內容:

 target_include_directories(
     MyTarget
     PUBLIC
     $<BUILD_INTERFACE:"${CMAKE_CURRENT_SOURCE_DIR}/include">
     $<INSTALL_INTERFACE:include>
     )
1. 它們就像在構建/安裝時評估它們一樣,但實際上它們是針對每個構建配置分別進行評估的。 ↩
2. CMake 文件將表示式拆分為資訊,邏輯和輸出。 ↩

巨集和函式

你可以方便的定義你自己的 CMake 函式 function 和巨集 macro 。 函式和巨集之間的唯一區別是作用範圍:巨集沒有作用範圍。 因此,如果您在函式中設定了變數並希望它在外部可見,那麼您將需要用 PARENT_SCOPE。 由於您必須在每個函式中明確用 PARENT_SCOPE 設定您希望外部世界可見的變數,巢狀函式有點兒煩。 但是,函式不會像巨集一樣 “洩漏” 變數。 在以下示例中,我將使用函式。

一個簡單使用函式的例子如下:

function(SIMPLE REQUIRED_ARG)
    message(STATUS "Simple arguments: ${REQUIRED_ARG}, followed by ${ARGV}")
    set(${REQUIRED_ARG} "From SIMPLE" PARENT_SCOPE)
endfunction()

simple(This)
message("Output: ${This}")

如果你想用位置引數,它們需要明確列出,並且所有其他引數都被放在 ARGN 中(ARGV 中儲存有所有引數,甚至那些你已經列出的引數)。 您只能通過設定變數來解決 CMake 中函式沒有返回值的問題。 在上面的示例中,您可以顯式指定要設定的變數名稱。

引數

你已經在 CMake 函式的大部分使用中看到,CMake 有一個命名變數系統。 您可以將它與 cmake_parse_arguments 函式一起使用。 如果要支援 3.5 以下的 CMake 版本,您還需要包含 CMakeParseArguments 模組,該模組在成為內建命令之前就已存在。

以下是如何使用它的示例:

function(COMPLEX)
cmake_parse_arguments(
    COMPLEX_PREFIX
    "SINGLE;ANOTHER"
    "ONE_VALUE;ALSO_ONE_VALUE"
    "MULTI_VALUES"
    ${ARGN}
)

endfunction()

complex(SINGLE ONE_VALUE value MULTI_VALUES some other values)

在此呼叫 cmake_parse_arguments 函式後,在 complex 函式內部,給我們產生了這些變數:

COMPLEX_PREFIX_SINGLE = TRUE
COMPLEX_PREFIX_ANOTHER = FALSE
COMPLEX_PREFIX_ONE_VALUE = "value"
COMPLEX_PREFIX_ALSO_ONE_VALUE = <UNDEFINED>
COMPLEX_PREFIX_MULTI_VALUES = "some;other;values"

如果你檢視官方頁面,你會看到一個稍微不同的使用 set 的方法避免在列表中明確寫分號的方法; 隨意使用您最喜歡的結構。 您也可以將它與上面列出的位置引數混合使用; 任何剩餘的引數(包括可選的位置引數)都會儲存在 COMPLEX_PREFIX_UNPARSED_ARGUMENTS 中。

相關文章