<<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
命令中使用的關鍵字,例如:
- 一元關鍵字:
NOT
,TARGET
,EXISTS
(檔案),DEFINED
, 等等 - 二元關鍵字:
STREQUAL
,AND
,OR
,MATCHES
(正規表示式),VERSION_LESS
,VERSION_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
中。