cmake_path

於版本 3.20 新增。

此命令用於操作路徑。僅處理路徑的語法方面,不與任何底層檔案系統進行任何互動。路徑可以表示不存在的路徑,甚至是當前檔案系統或平台上不允許存在的路徑。對於與檔案系統互動的操作,請參閱 file() 命令。

注意

cmake_path 命令處理的路徑格式為建置系統的格式(即主機平台),而非目標系統。當進行交叉編譯時,如果路徑包含在主機平台上無法表示的元素(例如,當主機不是 Windows 時的磁碟機代號),則結果將不可預測。

概要

Conventions

Path Structure And Terminology

Normalization

Decomposition
  cmake_path(GET <path-var> ROOT_NAME <out-var>)
  cmake_path(GET <path-var> ROOT_DIRECTORY <out-var>)
  cmake_path(GET <path-var> ROOT_PATH <out-var>)
  cmake_path(GET <path-var> FILENAME <out-var>)
  cmake_path(GET <path-var> EXTENSION [LAST_ONLY] <out-var>)
  cmake_path(GET <path-var> STEM [LAST_ONLY] <out-var>)
  cmake_path(GET <path-var> RELATIVE_PART <out-var>)
  cmake_path(GET <path-var> PARENT_PATH <out-var>)

Query
  cmake_path(HAS_ROOT_NAME <path-var> <out-var>)
  cmake_path(HAS_ROOT_DIRECTORY <path-var> <out-var>)
  cmake_path(HAS_ROOT_PATH <path-var> <out-var>)
  cmake_path(HAS_FILENAME <path-var> <out-var>)
  cmake_path(HAS_EXTENSION <path-var> <out-var>)
  cmake_path(HAS_STEM <path-var> <out-var>)
  cmake_path(HAS_RELATIVE_PART <path-var> <out-var>)
  cmake_path(HAS_PARENT_PATH <path-var> <out-var>)
  cmake_path(IS_ABSOLUTE <path-var> <out-var>)
  cmake_path(IS_RELATIVE <path-var> <out-var>)
  cmake_path(IS_PREFIX <path-var> <input> [NORMALIZE] <out-var>)
  cmake_path(COMPARE <input1> <OP> <input2> <out-var>)

Modification
  cmake_path(SET <path-var> [NORMALIZE] <input>)
  cmake_path(APPEND <path-var> [<input>...] [OUTPUT_VARIABLE <out-var>])
  cmake_path(APPEND_STRING <path-var> [<input>...] [OUTPUT_VARIABLE <out-var>])
  cmake_path(REMOVE_FILENAME <path-var> [OUTPUT_VARIABLE <out-var>])
  cmake_path(REPLACE_FILENAME <path-var> <input> [OUTPUT_VARIABLE <out-var>])
  cmake_path(REMOVE_EXTENSION <path-var> [LAST_ONLY] [OUTPUT_VARIABLE <out-var>])
  cmake_path(REPLACE_EXTENSION <path-var> [LAST_ONLY] <input> [OUTPUT_VARIABLE <out-var>])

Generation
  cmake_path(NORMAL_PATH <path-var> [OUTPUT_VARIABLE <out-var>])
  cmake_path(RELATIVE_PATH <path-var> [BASE_DIRECTORY <input>] [OUTPUT_VARIABLE <out-var>])
  cmake_path(ABSOLUTE_PATH <path-var> [BASE_DIRECTORY <input>] [NORMALIZE] [OUTPUT_VARIABLE <out-var>])

Native Conversion
  cmake_path(NATIVE_PATH <path-var> [NORMALIZE] <out-var>)
  cmake_path(CONVERT <input> TO_CMAKE_PATH_LIST <out-var> [NORMALIZE])
  cmake_path(CONVERT <input> TO_NATIVE_PATH_LIST <out-var> [NORMALIZE])

Hashing
  cmake_path(HASH <path-var> <out-var>)

慣例

以下慣例用於此命令的文件中

<path-var>

始終為變數的名稱。對於期望 <path-var> 作為輸入的命令,該變數必須存在,並且應包含單一路徑。

<input>

字串文字,可能包含路徑、路徑片段,或根據命令具有特殊分隔符的多個路徑。請參閱每個命令的說明,以了解其如何被解讀。

<input>...

零或多個字串文字參數。

<out-var>

變數的名稱,命令的結果將寫入其中。

路徑結構與術語

路徑具有以下結構(所有組件都是可選的,但有一些限制)

root-name root-directory-separator (item-name directory-separator)* filename
根名稱 (root-name)

在具有多個根目錄的檔案系統上識別根目錄(例如 "C:""//myserver")。它是可選的。

根目錄分隔符 (root-directory-separator)

如果存在,則表示此路徑為絕對路徑的目錄分隔符。如果缺少,並且 root-name 以外的第一個元素是 item-name,則路徑是相對路徑。

項目名稱 (item-name)

非目錄分隔符的一系列字元。此名稱可以識別檔案、硬連結、符號連結或目錄。會識別兩種特殊情況

  • 由單個點字元 . 組成的項目名稱是一個目錄名稱,指代當前目錄。

  • 由兩個點字元 .. 組成的項目名稱是一個目錄名稱,指代父目錄。

上面顯示的 (...)* 模式表示可以有零個或多個項目名稱,多個項目由 directory-separator 分隔。()* 字元不是路徑的一部分。

目錄分隔符 (directory-separator)

唯一可識別的目錄分隔符是正斜線字元 /。如果重複此字元,則將其視為單個目錄分隔符。換句話說,/usr///////lib/usr/lib 相同。

檔名 (filename)

如果路徑不以 directory-separator 結尾,則它具有 filenamefilename 實際上是路徑的最後一個 item-name,因此它也可以是硬連結、符號連結或目錄。

filename 可以具有*副檔名*。預設情況下,副檔名定義為從最左邊的句點(包括句點)開始到 filename 結尾的子字串。在接受 LAST_ONLY 關鍵字的命令中,LAST_ONLY 會將解釋更改為從最右邊的句點開始的子字串。

以下例外情況適用於上述解釋

  • 如果 filename 中的第一個字元是句點,則忽略該句點(即,像 ".profile" 這樣的 filename 被視為沒有副檔名)。

  • 如果 filename...,則它沒有副檔名。

*詞幹*是 filename 中副檔名之前的部分。

某些命令會參考 root-path。這是 root-nameroot-directory-separator 的串聯,其中任何一個或兩個都可以為空。relative-part 指的是刪除所有 root-path 的完整路徑。

建立路徑變數

雖然可以使用普通的 set() 命令小心地建立路徑,但建議改用 cmake_path(SET),因為它會在需要時自動將路徑轉換為所需的格式。cmake_path(APPEND) 子命令可能是另一個合適的替代方案,其中需要透過聯結片段來建構路徑。以下範例比較了三種建構相同路徑的方法

set(path1 "${CMAKE_CURRENT_SOURCE_DIR}/data")

cmake_path(SET path2 "${CMAKE_CURRENT_SOURCE_DIR}/data")

cmake_path(APPEND path3 "${CMAKE_CURRENT_SOURCE_DIR}" "data")

修改產生子命令可以就地儲存結果,也可以儲存在以 OUTPUT_VARIABLE 關鍵字命名的單獨變數中。所有其他子命令都將結果儲存在強制性的 <out-var> 變數中。

正規化

某些子命令支援*正規化*路徑。用於正規化路徑的演算法如下

  1. 如果路徑為空,則停止(空路徑的正規化形式也是空路徑)。

  2. 將每個 directory-separator(可能由多個分隔符組成)替換為單個 //a///b  --> /a/b)。

  3. 移除每個單獨的句點 (.) 和任何緊接其後的 directory-separator/a/./b/. --> /a/b)。

  4. 移除每個緊接其後跟著 directory-separator..item-name.. 除外),以及任何緊接其後的 directory-separator/a/b/../c --> a/c)。

  5. 如果存在 root-directory,則移除任何 .. 和任何緊接其後的 directory-separators。根目錄的父目錄仍被視為根目錄(/../a --> /a)。

  6. 如果最後一個 item-name..,則移除任何尾隨的 directory-separator../ --> ..)。

  7. 如果此階段路徑為空,則加入一個 dot./ 的正規形式是 .)。

分解

GET 子命令的以下形式分別從路徑中檢索不同的元件或元件組。請參閱 路徑結構與術語,以了解每個路徑元件的含義。

cmake_path(GET <path-var> ROOT_NAME <out-var>)
cmake_path(GET <path-var> ROOT_DIRECTORY <out-var>)
cmake_path(GET <path-var> ROOT_PATH <out-var>)
cmake_path(GET <path-var> FILENAME <out-var>)
cmake_path(GET <path-var> EXTENSION [LAST_ONLY] <out-var>)
cmake_path(GET <path-var> STEM [LAST_ONLY] <out-var>)
cmake_path(GET <path-var> RELATIVE_PART <out-var>)
cmake_path(GET <path-var> PARENT_PATH <out-var>)

如果請求的元件未出現在路徑中,則會在 <out-var> 中儲存空字串。例如,只有 Windows 系統具有 root-name 的概念,因此當主機是非 Windows 時,ROOT_NAME 子命令將始終返回空字串。

對於 PARENT_PATH,如果 HAS_RELATIVE_PART 子命令返回 false,則結果是 <path-var> 的副本。請注意,這表示根目錄被視為具有父目錄,而該父目錄是它本身。如果 HAS_RELATIVE_PART 返回 true,則結果本質上是 <path-var> 減少一個元素。

根目錄範例

set(path "c:/a")

cmake_path(GET path ROOT_NAME rootName)
cmake_path(GET path ROOT_DIRECTORY rootDir)
cmake_path(GET path ROOT_PATH rootPath)

message("Root name is \"${rootName}\"")
message("Root directory is \"${rootDir}\"")
message("Root path is \"${rootPath}\"")
Root name is "c:"
Root directory is "/"
Root path is "c:/"

檔名範例

set(path "/a/b")
cmake_path(GET path FILENAME filename)
message("First filename is \"${filename}\"")

# Trailing slash means filename is empty
set(path "/a/b/")
cmake_path(GET path FILENAME filename)
message("Second filename is \"${filename}\"")
First filename is "b"
Second filename is ""

副檔名和詞幹範例

set(path "name.ext1.ext2")

cmake_path(GET path EXTENSION fullExt)
cmake_path(GET path STEM fullStem)
message("Full extension is \"${fullExt}\"")
message("Full stem is \"${fullStem}\"")

# Effect of LAST_ONLY
cmake_path(GET path EXTENSION LAST_ONLY lastExt)
cmake_path(GET path STEM LAST_ONLY lastStem)
message("Last extension is \"${lastExt}\"")
message("Last stem is \"${lastStem}\"")

# Special cases
set(dotPath "/a/.")
set(dotDotPath "/a/..")
set(someMorePath "/a/.some.more")
cmake_path(GET dotPath EXTENSION dotExt)
cmake_path(GET dotPath STEM dotStem)
cmake_path(GET dotDotPath EXTENSION dotDotExt)
cmake_path(GET dotDotPath STEM dotDotStem)
cmake_path(GET dotMorePath EXTENSION someMoreExt)
cmake_path(GET dotMorePath STEM someMoreStem)
message("Dot extension is \"${dotExt}\"")
message("Dot stem is \"${dotStem}\"")
message("Dot-dot extension is \"${dotDotExt}\"")
message("Dot-dot stem is \"${dotDotStem}\"")
message(".some.more extension is \"${someMoreExt}\"")
message(".some.more stem is \"${someMoreStem}\"")
Full extension is ".ext1.ext2"
Full stem is "name"
Last extension is ".ext2"
Last stem is "name.ext1"
Dot extension is ""
Dot stem is "."
Dot-dot extension is ""
Dot-dot stem is ".."
.some.more extension is ".more"
.some.more stem is ".some"

相對部分範例

set(path "c:/a/b")
cmake_path(GET path RELATIVE_PART result)
message("Relative part is \"${result}\"")

set(path "c/d")
cmake_path(GET path RELATIVE_PART result)
message("Relative part is \"${result}\"")

set(path "/")
cmake_path(GET path RELATIVE_PART result)
message("Relative part is \"${result}\"")
Relative part is "a/b"
Relative part is "c/d"
Relative part is ""

路徑遍歷範例

set(path "c:/a/b")
cmake_path(GET path PARENT_PATH result)
message("Parent path is \"${result}\"")

set(path "c:/")
cmake_path(GET path PARENT_PATH result)
message("Parent path is \"${result}\"")
Parent path is "c:/a"
Parent path is "c:/"

查詢

每個 GET 子命令都有一個對應的 HAS_... 子命令,可用於發現是否存在特定的路徑元件。請參閱 路徑結構與術語,以了解每個路徑元件的含義。

cmake_path(HAS_ROOT_NAME <path-var> <out-var>)
cmake_path(HAS_ROOT_DIRECTORY <path-var> <out-var>)
cmake_path(HAS_ROOT_PATH <path-var> <out-var>)
cmake_path(HAS_FILENAME <path-var> <out-var>)
cmake_path(HAS_EXTENSION <path-var> <out-var>)
cmake_path(HAS_STEM <path-var> <out-var>)
cmake_path(HAS_RELATIVE_PART <path-var> <out-var>)
cmake_path(HAS_PARENT_PATH <path-var> <out-var>)

上述每個操作都遵循可預測的模式,如果路徑具有相關的組件,則將 <out-var> 設定為 true,否則為 false。請注意以下特殊情況:

  • 對於 HAS_ROOT_PATH,只有當 root-nameroot-directory 至少有一個非空時,才會返回 true 的結果。

  • 對於 HAS_PARENT_PATH,根目錄也被視為具有父目錄,即它自己。除非路徑僅由 檔案名稱 組成,否則結果為 true。

cmake_path(IS_ABSOLUTE <path-var> <out-var>)

如果 <path-var> 是絕對路徑,則將 <out-var> 設定為 true。絕對路徑是指明確識別檔案位置的路徑,而無需參考額外的起始位置。在 Windows 上,這表示路徑必須同時具有 root-nameroot-directory-separator 才能被視為絕對路徑。在其他平台上,僅需要 root-directory-separator 就足夠了。請注意,這表示在 Windows 上,IS_ABSOLUTE 可以為 false,而 HAS_ROOT_DIRECTORY 可以為 true。

cmake_path(IS_RELATIVE <path-var> <out-var>)

這將在 <out-var> 中儲存與 IS_ABSOLUTE 相反的值。

cmake_path(IS_PREFIX <path-var> <input> [NORMALIZE] <out-var>)

檢查 <path-var> 是否為 <input> 的前綴。

當指定 NORMALIZE 選項時,<path-var><input> 會在檢查之前進行正規化

set(path "/a/b/c")
cmake_path(IS_PREFIX path "/a/b/c/d" result) # result = true
cmake_path(IS_PREFIX path "/a/b" result)     # result = false
cmake_path(IS_PREFIX path "/x/y/z" result)   # result = false

set(path "/a/b")
cmake_path(IS_PREFIX path "/a/c/../b" NORMALIZE result)   # result = true
cmake_path(COMPARE <input1> EQUAL <input2> <out-var>)
cmake_path(COMPARE <input1> NOT_EQUAL <input2> <out-var>)

比較以字串常值提供的兩個路徑的詞彙表示。除了將多個連續的目錄分隔符號有效地摺疊成單個分隔符號之外,不會對任何路徑執行正規化。根據以下虛擬碼邏輯確定相等性:

if(NOT <input1>.root_name() STREQUAL <input2>.root_name())
  return FALSE

if(<input1>.has_root_directory() XOR <input2>.has_root_directory())
  return FALSE

Return FALSE if a relative portion of <input1> is not lexicographically
equal to the relative portion of <input2>. This comparison is performed path
component-wise. If all of the components compare equal, then return TRUE.

注意

與大多數其他 cmake_path() 子命令不同,COMPARE 子命令將字串常值作為輸入,而不是變數的名稱。

修改

cmake_path(SET <path-var> [NORMALIZE] <input>)

<input> 路徑指定給 <path-var>。如果 <input> 是原生路徑,則會將其轉換為使用正斜線 (/) 的 cmake 樣式路徑。在 Windows 上,會考慮長檔名標記。

當指定 NORMALIZE 選項時,路徑在轉換後會被正規化

例如

set(native_path "c:\\a\\b/..\\c")
cmake_path(SET path "${native_path}")
message("CMake path is \"${path}\"")

cmake_path(SET path NORMALIZE "${native_path}")
message("Normalized CMake path is \"${path}\"")

輸出

CMake path is "c:/a/b/../c"
Normalized CMake path is "c:/a/c"
cmake_path(APPEND <path-var> [<input>...] [OUTPUT_VARIABLE <out-var>])

使用 / 作為 directory-separator,將所有 <input> 引數附加到 <path-var>。根據 <input>,可能會捨棄 <path-var> 先前的内容。對於每個 <input> 引數,會應用以下演算法(虛擬碼):

# <path> is the contents of <path-var>

if(<input>.is_absolute() OR
   (<input>.has_root_name() AND
    NOT <input>.root_name() STREQUAL <path>.root_name()))
  replace <path> with <input>
  return()
endif()

if(<input>.has_root_directory())
  remove any root-directory and the entire relative path from <path>
elseif(<path>.has_filename() OR
       (NOT <path-var>.has_root_directory() OR <path>.is_absolute()))
  append directory-separator to <path>
endif()

append <input> omitting any root-name to <path>
cmake_path(APPEND_STRING <path-var> [<input>...] [OUTPUT_VARIABLE <out-var>])

將所有 <input> 引數附加到 <path-var>,而不新增任何 directory-separator

cmake_path(REMOVE_FILENAME <path-var> [OUTPUT_VARIABLE <out-var>])

<path-var> 中移除 檔案名稱 組件(由 GET ... FILENAME 返回)。移除後,任何尾部的 directory-separator(如果存在)都會保留。

如果未指定 OUTPUT_VARIABLE,則在此函式返回後,HAS_FILENAME 會針對 <path-var> 返回 false。

例如

set(path "/a/b")
cmake_path(REMOVE_FILENAME path)
message("First path is \"${path}\"")

# filename is now already empty, the following removes nothing
cmake_path(REMOVE_FILENAME path)
message("Second path is \"${path}\"")

輸出

First path is "/a/"
Second path is "/a/"
cmake_path(REPLACE_FILENAME <path-var> <input> [OUTPUT_VARIABLE <out-var>])

使用 <input> 替換 <path-var> 中的 檔案名稱 組件。如果 <path-var> 沒有檔案名稱組件(即 HAS_FILENAME 返回 false),則路徑不會變更。此操作等效於以下操作:

cmake_path(HAS_FILENAME path has_filename)
if(has_filename)
  cmake_path(REMOVE_FILENAME path)
  cmake_path(APPEND path input);
endif()
cmake_path(REMOVE_EXTENSION <path-var> [LAST_ONLY]
                                       [OUTPUT_VARIABLE <out-var>])

<path-var> 中移除 副檔名(如果有的話)。

cmake_path(REPLACE_EXTENSION <path-var> [LAST_ONLY] <input>
                             [OUTPUT_VARIABLE <out-var>])

使用 <input> 替換 副檔名。其效果等同於以下操作:

cmake_path(REMOVE_EXTENSION path)
if(NOT "input" MATCHES "^\\.")
  cmake_path(APPEND_STRING path ".")
endif()
cmake_path(APPEND_STRING path "input")

生成

cmake_path(NORMAL_PATH <path-var> [OUTPUT_VARIABLE <out-var>])

根據正規化中描述的步驟,正規化 <path-var>

cmake_path(RELATIVE_PATH <path-var> [BASE_DIRECTORY <input>]
                                    [OUTPUT_VARIABLE <out-var>])

修改 <path-var>,使其相對於 BASE_DIRECTORY 引數。如果未指定 BASE_DIRECTORY,則預設基本目錄將為 CMAKE_CURRENT_SOURCE_DIR

作為參考,用於計算相對路徑的演算法與 C++ std::filesystem::path::lexically_relative 使用的演算法相同。

cmake_path(ABSOLUTE_PATH <path-var> [BASE_DIRECTORY <input>] [NORMALIZE]
                                    [OUTPUT_VARIABLE <out-var>])

如果 <path-var> 是相對路徑(IS_RELATIVE 為 true),則會相對於由 BASE_DIRECTORY 選項指定的給定基本目錄進行評估。如果未指定 BASE_DIRECTORY,則預設基本目錄將為 CMAKE_CURRENT_SOURCE_DIR

當指定 NORMALIZE 選項時,路徑會在路徑計算後被正規化

由於 cmake_path() 不會存取檔案系統,因此不會解析符號連結,並且不會展開任何前導波浪號。若要計算已解析符號連結並展開前導波浪號的實際路徑,請改用 file(REAL_PATH) 命令。

原生轉換

對於本節中的命令,原生 指的是主機平台,而不是跨編譯時的目標平台。

cmake_path(NATIVE_PATH <path-var> [NORMALIZE] <out-var>)

將 cmake 樣式的 <path-var> 轉換為具有平台特定斜線的原生路徑(在 Windows 主機上為 \,在其他位置為 /)。

當指定 NORMALIZE 選項時,路徑會在轉換之前被正規化

cmake_path(CONVERT <input> TO_CMAKE_PATH_LIST <out-var> [NORMALIZE])

將原生 <input> 路徑轉換為具有正斜線 (/) 的 cmake 樣式路徑。在 Windows 主機上,會考慮長檔名標記。輸入可以是單一路徑或系統搜尋路徑,如 $ENV{PATH}。搜尋路徑將轉換為由 ; 字元分隔的 cmake 樣式清單(在非 Windows 平台上,這基本上表示 : 分隔符號會取代為 ;)。轉換的結果會儲存在 <out-var> 變數中。

當指定 NORMALIZE 選項時,路徑會在轉換之前被正規化

注意

與大多數其他 cmake_path() 子命令不同,CONVERT 子命令將字串常值作為輸入,而不是變數的名稱。

cmake_path(CONVERT <input> TO_NATIVE_PATH_LIST <out-var> [NORMALIZE])

將 cmake 樣式的 <input> 路徑轉換為具有平台特定斜線的原生路徑(在 Windows 主機上為 \,在其他位置為 /)。輸入可以是單一路徑或 cmake 樣式清單。清單將轉換為原生搜尋路徑(在 Windows 上以 ; 分隔,在其他平台上以 : 分隔)。轉換的結果會儲存在 <out-var> 變數中。

當指定 NORMALIZE 選項時,路徑會在轉換之前被正規化

注意

與大多數其他 cmake_path() 子命令不同,CONVERT 子命令將字串常值作為輸入,而不是變數的名稱。

例如

set(paths "/a/b/c" "/x/y/z")
cmake_path(CONVERT "${paths}" TO_NATIVE_PATH_LIST native_paths)
message("Native path list is \"${native_paths}\"")

在 Windows 上的輸出

Native path list is "\a\b\c;\x\y\z"

在所有其他平台上的輸出

Native path list is "/a/b/c:/x/y/z"

雜湊

cmake_path(HASH <path-var> <out-var>)

計算 <path-var> 的雜湊值,以便對於兩個比較相等的路徑 p1p2COMPARE ... EQUAL),p1 的雜湊值等於 p2 的雜湊值。路徑始終會在計算雜湊之前進行正規化