cmake-language(7)

組織

CMake 輸入檔是以「CMake 語言」撰寫,並儲存在名為 CMakeLists.txt 或以 .cmake 副檔名結尾的原始檔中。

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

目錄

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

腳本

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

模組

目錄腳本中的 CMake 語言程式碼可以使用 include() 命令,在包含的內容範圍內載入 <module>.cmake 原始檔。如需 CMake 發行版中包含的模組文件,請參閱 cmake-modules(7) 手冊頁面。專案原始碼樹狀結構也可以提供自己的模組,並在 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、carriage return 或換行字元。在任何變數引用之外的 \; 會編碼成自身,但可以在未加引號的參數中使用,以編碼 ; 而不會因此分割參數值。\;變數引用內部會編碼成字面上的 ; 字元。(另請參閱政策 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() 指令會明確地設定或取消設定變數,但其他指令也有修改變數的語意。變數名稱區分大小寫,而且可以使用幾乎任何文字,但我們建議只使用由字母數字字元加上 _- 組成的名稱。

變數具有動態範圍。每次「設定」或「取消設定」變數都會在目前範圍中建立繫結。

區塊範圍

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

函式範圍

function() 指令建立的 指令定義,在被調用時,會在新的變數繫結範圍中處理已記錄的指令。變數的「設定」或「取消設定」會繫結在此範圍中,且對於目前的函式和其中的任何巢狀呼叫可見,但在函式返回後則不可見。

目錄範圍

來源樹中的每個 目錄 都有自己的變數繫結。在處理目錄的 CMakeLists.txt 檔案之前,CMake 會複製父目錄中目前定義的所有變數繫結(如果有的話),以初始化新的目錄範圍。使用 cmake -P 處理的 CMake 指令碼,會在一個「目錄」範圍中繫結變數。

不在函式呼叫中的變數「設定」或「取消設定」會繫結到目前的目錄範圍。

持久快取

CMake 會儲存一組獨立的「快取」變數,或稱為「快取條目」,其值會在專案建置樹狀結構中的多次執行之間持續存在。快取條目具有隔離的繫結範圍,只有在明確請求時才會修改,例如透過 set()unset() 指令的 CACHE 選項。

在評估 變數參考 時,CMake 會先搜尋函式呼叫堆疊(如果有的話)尋找繫結,然後回溯到目前目錄範圍中的繫結(如果有的話)。如果找到「設定」繫結,則會使用其值。如果找到「取消設定」繫結,或找不到繫結,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() 的實作,因為它們使用佔位符而非真實變數來引用引數。