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,根目錄也被認為具有父目錄,它將是其自身。除非路徑僅由 filename 組成,否則結果為 true。

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

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

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> 中移除 filename 組件(由 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>])

<path-var> 中的 filename 組件替換為 <input>。如果 <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> 中移除 extension(如果有的話)。

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

extension 替換為 <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> 的雜湊值,以便對於兩個比較相等的路徑 p1p2 (COMPARE ... EQUAL),p1 的雜湊值等於 p2 的雜湊值。路徑始終在計算雜湊之前進行 正規化