cmake-language(7)

組織

CMake 輸入檔以 "CMake 語言" 撰寫,原始檔名為 CMakeLists.txt 或以 .cmake 副檔名結尾。

專案中的 CMake 語言原始檔組織成

目錄

當 CMake 處理專案原始碼樹狀結構時,入口點是頂層原始碼目錄中名為 CMakeLists.txt 的原始檔。此檔案可能包含完整的建置規範,或使用 add_subdirectory() 命令將子目錄新增至建置。命令新增的每個子目錄也必須包含一個 CMakeLists.txt 檔案作為該目錄的入口點。對於每個處理其 CMakeLists.txt 檔案的原始碼目錄,CMake 會在建置樹狀結構中產生一個對應的目錄,作為預設的工作和輸出目錄。

腳本

可以使用 cmake(1) 命令列工具和 -P 選項,以腳本模式處理個別的 <script>.cmake 原始檔。腳本模式僅執行給定的 CMake 語言原始檔中的命令,而不產生建置系統。它不允許定義建置目標或動作的 CMake 命令。

模組

目錄腳本 中的 CMake 語言程式碼可以使用 include() 命令,在包含上下文的範圍內載入 <module>.cmake 原始檔。請參閱 cmake-modules(7) 手冊頁,以取得 CMake 發行版中包含的模組文件。專案原始碼樹狀結構也可以提供自己的模組,並在 CMAKE_MODULE_PATH 變數中指定其位置。

語法

編碼

CMake 語言原始檔可以使用 7 位元 ASCII 文字撰寫,以在所有支援的平台上達到最大的可攜性。換行符號可以編碼為 \n\r\n,但在讀取輸入檔時會轉換為 \n

請注意,實作是 8 位元乾淨的,因此在具有支援此編碼的系統 API 的平台上,原始檔可以編碼為 UTF-8。此外,CMake 3.2 及更高版本支援 Windows 上以 UTF-8 編碼的原始檔 (使用 UTF-16 呼叫系統 API)。此外,CMake 3.0 及更高版本允許原始檔中具有前導 UTF-8 位元組順序記號

原始檔

CMake 語言原始檔由零或多個 命令調用 組成,以換行符號分隔,並可選擇性地使用空格和 註解

file         ::=  file_element*
file_element ::=  command_invocation line_ending |
                  (bracket_comment|space)* line_ending
line_ending  ::=  line_comment? newline
space        ::=  <match '[ \t]+'>
newline      ::=  <match '\n'>

請注意,不在 命令參數方括號註解 內的任何原始檔行都可以以 行註解 結尾。

命令調用

命令調用是一個名稱,後跟以括號括住並以空白分隔的參數

command_invocation  ::=  space* identifier space* '(' arguments ')'
identifier          ::=  <match '[A-Za-z_][A-Za-z0-9_]*'>
arguments           ::=  argument? separated_arguments*
separated_arguments ::=  separation+ argument? |
                         separation* '(' arguments ')'
separation          ::=  space | line_ending

例如

add_executable(hello world.c)

命令名稱不區分大小寫。參數中巢狀的無引號括號必須平衡。每個 () 都會作為常值 無引號參數 提供給命令調用。這可以用於呼叫 if() 命令以括住條件。例如

if(FALSE AND (FALSE OR TRUE)) # evaluates to FALSE

注意

3.0 之前的 CMake 版本要求命令名稱識別碼至少為 2 個字元。

2.8.12 之前的 CMake 版本會靜默接受緊跟在 引號參數 之後且未以任何空白分隔的 無引號參數引號參數。為了相容性,CMake 2.8.12 及更高版本接受此類程式碼,但會產生警告。

命令參數

命令調用 中有三種類型的參數

argument ::=  bracket_argument | quoted_argument | unquoted_argument

方括號參數

方括號參數,靈感來自 Lua 長方括號語法,將內容括在相同長度的開頭和結尾 "方括號" 之間

bracket_argument ::=  bracket_open bracket_content bracket_close
bracket_open     ::=  '[' '='* '['
bracket_content  ::=  <any text not containing a bracket_close with
                       the same number of '=' as the bracket_open>
bracket_close    ::=  ']' '='* ']'

開頭方括號寫為 [,後跟零或多個 =,再後跟 [。對應的結尾方括號寫為 ],後跟相同數量的 =,再後跟 ]。方括號不會巢狀。始終可以為開頭和結尾方括號選擇唯一的長度,以包含其他長度的結尾方括號。

方括號參數內容由開頭和結尾方括號之間的所有文字組成,但緊接在開頭方括號之後的一個換行符號 (如果有的話) 會被忽略。不會對括住的內容執行評估,例如 跳脫序列變數參考。方括號參數始終作為一個參數提供給命令調用。

例如

message([=[
This is the first line in a bracket argument with bracket length 1.
No \-escape sequences or ${variable} references are evaluated.
This is always one argument even though it contains a ; character.
The text does not end on a closing bracket of length 0 like ]].
It does end in a closing bracket of length 1.
]=])

注意

3.0 之前的 CMake 版本不支援方括號參數。它們將開頭方括號解釋為 無引號參數 的開始。

引號參數

引號參數將內容括在開頭和結尾雙引號字元之間

quoted_argument     ::=  '"' quoted_element* '"'
quoted_element      ::=  <any character except '\' or '"'> |
                         escape_sequence |
                         quoted_continuation
quoted_continuation ::=  '\' newline

引號參數內容由開頭和結尾引號之間的所有文字組成。跳脫序列變數參考 都會被評估。引號參數始終作為一個參數提供給命令調用。

例如

message("This is a quoted argument containing multiple lines.
This is always one argument even though it contains a ; character.
Both \\-escape sequences and ${variable} references are evaluated.
The text does not end on an escaped double-quote like \".
It does end in an unescaped double quote.
")

任何以奇數個反斜線結尾的行上的最後一個 \ 都會被視為行繼續符號,並與緊隨其後的換行符號一起忽略。例如

message("\
This is the first line of a quoted argument. \
In fact it is the only line but since it is long \
the source code uses line continuation.\
")

注意

3.0 之前的 CMake 版本不支援使用 \ 繼續。它們會報告引號參數中包含以奇數個 \ 字元結尾的行的錯誤。

無引號參數

無引號參數未以任何引號語法括住。除了以反斜線跳脫之外,它可能不包含任何空白、()#"\

unquoted_argument ::=  unquoted_element+ | unquoted_legacy
unquoted_element  ::=  <any character except whitespace or one of '()#"\'> |
                       escape_sequence
unquoted_legacy   ::=  <see note in text>

無引號參數內容由允許或跳脫字元的連續區塊中的所有文字組成。跳脫序列變數參考 都會被評估。結果值以 列表 分割成元素的方式分割。每個非空元素都作為一個參數提供給命令調用。因此,無引號參數可以作為零個或多個參數提供給命令調用。

例如

foreach(arg
    NoSpace
    Escaped\ Space
    This;Divides;Into;Five;Arguments
    Escaped\;Semicolon
    )
  message("${arg}")
endforeach()

注意

為了支援舊版 CMake 程式碼,無引號參數也可能包含雙引號字串 ("...",可能括住水平空白) 和 make 樣式變數參考 ($(MAKEVAR))。

未跳脫的雙引號必須平衡,不得出現在無引號參數的開頭,並被視為內容的一部分。例如,無引號參數 -Da="b c"-Da=$(v)a" "b"c"d 都會按字面意義解釋。它們可以改為寫為引號參數 "-Da=\"b c\"""-Da=$(v)""a\" \"b\"c\"d"

Make 樣式參考按字面意義視為內容的一部分,並且不會進行變數展開。它們被視為單個參數的一部分 (而不是作為單獨的 $(MAKEVAR) 參數)。

上面的 "unquoted_legacy" 產生式代表此類參數。我們不建議在新程式碼中使用舊版無引號參數。而是使用 引號參數方括號參數 來表示內容。

跳脫序列

跳脫序列是一個 \,後跟一個字元

escape_sequence  ::=  escape_identity | escape_encoded | escape_semicolon
escape_identity  ::=  '\' <match '[^A-Za-z0-9;]'>
escape_encoded   ::=  '\t' | '\r' | '\n'
escape_semicolon ::=  '\;'

後跟非字母數字字元的 \ 只會編碼常值字元,而不會將其解釋為語法。\t\r\n 分別編碼 Tab 字元、歸位字元或換行字元。任何 變數參考 外部的 \; 會編碼自身,但可以在 無引號參數 中使用,以編碼 ;,而不會在其上分割參數值。變數參考 內的 \; 會編碼常值 ; 字元。(另請參閱政策 CMP0053 文件以了解歷史考量。)

變數參考

變數參考的格式為 ${<variable>},並在 引號參數無引號參數 內評估。變數參考會由指定變數或快取項目的值取代,如果兩者都未設定,則由空字串取代。變數參考可以巢狀,並且從內到外評估,例如 ${outer_${inner_variable}_variable}

常值變數參考可以由字母數字字元、字元 /_.+-跳脫序列 組成。巢狀參考可以用於評估任何名稱的變數。另請參閱政策 CMP0053 文件以了解歷史考量以及 $ 在技術上也允許但不建議使用的原因。

變數 區段說明了變數名稱的範圍以及如何設定其值。

環境變數參考的格式為 $ENV{<variable>}。請參閱 環境變數 區段以取得更多資訊。

快取變數參考的格式為 $CACHE{<variable>},並由指定快取項目的值取代,而不會檢查是否有名稱相同的普通變數。如果快取項目不存在,則會由空字串取代。請參閱 CACHE 以取得更多資訊。

if() 命令具有特殊的條件語法,允許使用簡短格式 <variable> 而不是 ${<variable>} 的變數參考。但是,環境變數始終需要參考為 $ENV{<variable>}

註解

註解以 # 字元開頭,該字元不在 方括號參數引號參數 內,或未在 無引號參數 中以 \ 跳脫。註解有兩種類型:方括號註解行註解

方括號註解

緊接在 bracket_open 之後的 # 會形成方括號註解,該註解由整個方括號括住部分組成

bracket_comment ::=  '#' bracket_argument

例如

#[[This is a bracket comment.
It runs until the close bracket.]]
message("First Argument\n" #[[Bracket Comment]] "Second Argument")

注意

3.0 之前的 CMake 版本不支援方括號註解。它們將開頭 # 解釋為 行註解 的開始。

行註解

未緊接在 bracket_open 之後的 # 會形成行註解,該註解會執行到行尾

line_comment ::=  '#' <any text not starting in a bracket_open
                       and not containing a newline>

例如

# This is a line comment.
message("First Argument\n" # This is a line comment :)
        "Second Argument") # This is a line comment.

控制結構

條件區塊

if()/elseif()/else()/endif() 命令分隔要有條件執行的程式碼區塊。

迴圈

foreach()/endforeach()while()/endwhile() 命令分隔要在迴圈中執行的程式碼區塊。在此類區塊內,可以使用 break() 命令提早終止迴圈,而可以使用 continue() 命令立即開始下一次迭代。

命令定義

macro()/endmacro()function()/endfunction() 命令分隔要記錄以供稍後作為命令調用的程式碼區塊。

變數

變數是 CMake 語言中的基本儲存單位。它們的值始終為字串類型,儘管某些命令可能會將字串解釋為其他類型的值。set()unset() 命令明確設定或取消設定變數,但其他命令也具有修改變數的語意。變數名稱區分大小寫,並且可以由幾乎任何文字組成,但我們建議堅持僅由字母數字字元加上 _- 組成的名稱。

變數具有動態範圍。每個 "set" 或 "unset" 變數都會在目前範圍中建立繫結

區塊範圍

block() 命令可以為變數繫結建立新的範圍。

函式範圍

function() 命令建立的 命令定義 會建立命令,這些命令在調用時,會在新的變數繫結範圍中處理記錄的命令。"set" 或 "unset" 變數會在此範圍內繫結,並且對於目前的函式及其中的任何巢狀呼叫都是可見的,但在函式傳回後則不可見。

目錄範圍

原始碼樹狀結構中的每個 目錄 都有自己的變數繫結。在處理目錄的 CMakeLists.txt 檔案之前,CMake 會複製目前在父目錄中定義的所有變數繫結 (如果有的話),以初始化新的目錄範圍。當使用 cmake -P 處理時,CMake 腳本 會在一個 "目錄" 範圍中繫結變數。

不在函式呼叫內的 "set" 或 "unset" 變數會繫結到目前的目錄範圍。

持久性快取

CMake 儲存一組單獨的 "快取" 變數,或 "快取項目",其值在專案建置樹狀結構中的多次執行中保持不變。快取項目具有隔離的繫結範圍,僅透過明確請求修改,例如透過 set()unset() 命令的 CACHE 選項。

在評估 變數參考 時,CMake 首先搜尋函式呼叫堆疊 (如果有的話) 以尋找繫結,然後回退到目前目錄範圍中的繫結 (如果有的話)。如果找到 "set" 繫結,則會使用其值。如果找到 "unset" 繫結,或未找到繫結,則 CMake 會搜尋快取項目。如果找到快取項目,則會使用其值。否則,變數參考會評估為空字串。$CACHE{VAR} 語法可用於執行直接快取項目查找。

cmake-variables(7) 手冊說明了 CMake 提供的許多變數,或專案程式碼設定時對 CMake 具有意義的變數。

注意

CMake 保留以下識別碼

  • CMAKE_ (大寫、小寫或混合大小寫) 開頭,或

  • _CMAKE_ (大寫、小寫或混合大小寫) 開頭,或

  • _ 後跟任何 CMake 命令 的名稱開頭。

環境變數

環境變數與普通 變數 類似,但有以下差異

範圍

環境變數在 CMake 處理程序中具有全域範圍。它們永遠不會被快取。

參考

變數參考 的格式為 $ENV{<variable>},使用 ENV 運算子。

初始化

CMake 環境變數的初始值是呼叫處理程序的值。可以使用 set()unset() 命令來變更值。這些命令僅影響正在執行的 CMake 處理程序,而不影響整個系統環境。變更的值不會寫回呼叫處理程序,並且後續的建置或測試處理程序看不到它們。

請參閱 cmake -E env 命令列工具,以在修改後的環境中執行命令。

檢查

請參閱 cmake -E environment 命令列工具,以顯示所有目前的環境變數。

cmake-env-variables(7) 手冊說明了對 CMake 具有特殊意義的環境變數。

列表

雖然 CMake 中的所有值都儲存為字串,但在某些情況下,例如在評估 無引號參數 期間,字串可以被視為列表。在此類情況下,字串會透過在 ; 字元上分割來分割成列表元素,這些字元不遵循不等數量的 [] 字元,並且不會緊接在 \ 之後。序列 \; 不會分割值,而是在結果元素中由 ; 取代。

元素列表表示為字串,方法是串連以 ; 分隔的元素。例如,set() 命令將多個值儲存到目的地變數中作為列表

set(srcs a.c b.c c.c) # sets "srcs" to "a.c;b.c;c.c"

列表旨在用於簡單的用例,例如原始檔列表,不應用於複雜的資料處理任務。大多數建構列表的命令不會跳脫列表元素中的 ; 字元,因此會展平巢狀列表

set(x a "b;c") # sets "x" to "a;b;c", not "a;b\;c"

一般來說,列表不支援包含 ; 字元的元素。為了避免問題,請考慮以下建議

  • 許多 CMake 命令、變數和屬性的介面接受以分號分隔的列表。除非它們記錄直接支援或某種跳脫或編碼分號的方式,否則請避免將包含分號的元素的列表傳遞到這些介面。

  • 在建構列表時,在元素中將 ; 替換為其他未使用的佔位符。然後在處理列表元素時,將佔位符替換為 ;。例如,以下程式碼使用 | 代替 ; 字元

    set(mylist a "b|c")
    foreach(entry IN LISTS mylist)
      string(REPLACE "|" ";" entry "${entry}")
      # use "${entry}" normally
    endforeach()
    

    ExternalProject 模組的 LIST_SEPARATOR 選項是使用此方法建構的介面範例。

  • 產生器運算式 列表中,請使用 $<SEMICOLON> 產生器運算式。

  • 在命令呼叫中,請盡可能使用 引號參數 語法。被呼叫的命令將接收帶有保留分號的參數內容。無引號參數 將在分號上分割。

  • function() 的實作中,請避免使用 ARGVARGN,因為它們無法區分數值中的分號和分隔數值的分號。請改為偏好使用具名的位置引數以及 ARGCARGV# 變數。當使用 cmake_parse_arguments() 來解析引數時,請偏好使用其 PARSE_ARGV 簽名,它會使用 ARGV# 變數。

    請注意,此方法不適用於 macro() 的實作,因為它們使用佔位符 (placeholders) 而非真實變數來引用引數。