路径

在 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> 作为输入的命令,该变量必须存在并且它应该包含单个路径。

<输入>

一个字符串文字,它可能包含一个路径、路径片段或多个带有特殊分隔符的路径,具体取决于命令。请参阅每个命令的描述以了解其解释方式。

<输入>...

零个或多个字符串文字参数。

<输出变量>

将写入命令结果的变量的名称。

路径结构和术语

路径具有以下结构(所有组件都是可选的,有一些限制):

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

标识具有多个根的文件系统上的根(例如 "C:""//myserver")。它是可选的。

根目录分隔符

目录分隔符,如果存在,则表示此路径是绝对路径。如果它丢失并且除 root-name 之外的第一个元素是 item-name,那么路径是相对的。

项目名称

不是目录分隔符的字符序列。此名称可以标识文件、硬链接、符号链接或目录。识别出两种特殊情况:

  • 由单个点字符 . 组成的项目名称是指代当前目录的目录名称。

  • 由两个点字符``..`` 组成的项目名称是指代父目录的目录名称。

上面显示的 (...)* 模式表示可以有零个或多个项目名称,多个项目由 directory-separator 分隔。 ()* 字符不是路径的一部分。

目录分隔符

唯一可识别的目录分隔符是正斜杠字符“/”。如果此字符重复,则将其视为单个目录分隔符。换句话说,/usr///////lib 与``/usr/lib`` 相同。

文件名

如果路径不以“目录分隔符”结尾,则它具有“文件名”。 filename 实际上是路径的最后一个 item-name,因此它也可以是硬链接、符号链接或目录。

文件名 可以有*扩展名*。默认情况下,扩展名定义为从最左边的句点(包括句点)开始到“文件名”结尾的子字符串。在接受“LAST_ONLY”关键字的命令中,“LAST_ONLY”将解释更改为从最右边的句点开始的子字符串。

以下例外情况适用于上述解释:

  • 如果 filename 中的第一个字符是句点,则忽略该句点(即像 ".profile"filename 被视为没有扩展名)。

  • 如果 filename...,则它没有扩展名。

stem 是扩展名之前的“文件名”部分。

一些命令引用 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")

ModificationGeneration 子命令可以就地存储结果,或者存储在以 OUTPUT_VARIABLE 关键字命名的单独变量中。所有其他子命令将结果存储在强制性的“<out-var>”变量中。

正常化

一些子命令支持*规范化*路径。用于规范化路径的算法如下:

  1. 如果路径为空,则停止(空路径的规范化形式也是空路径)。

  2. 将每个可能由多个分隔符组成的“目录分隔符”替换为单个“/”(“/a///b --> /a/b”)。

  3. 删除每个单独的句点 (.) 和紧跟在``目录分隔符`` 之后的所有句点 (/a/./b/. --> /a/b)。

  4. 删除紧跟在``目录分隔符``和``..``之后的每个``项目名称``(..``除外),以及紧随其后的任何``目录-分隔符``(/a/b/../c --> a/c``)。

  5. 如果有一个 root-directory,删除任何 .. 和紧随其后的任何 directory-separators。根目录的父目录仍被视为根目录 (/../a --> /a)。

  6. 如果最后一个 item-name..,删除任何尾随的 directory-separator (../ --> ..)。

  7. 如果这个阶段路径是空的,添加一个``点``(./``的正常形式是.``)。

分解

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

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

如果 <path-var> 是绝对的,则将 <out-var> 设置为 true。绝对路径是一种在不引用其他起始位置的情况下明确标识文件位置的路径。在 Windows 上,这意味着路径必须同时具有 root-nameroot-directory-separator 才能被视为绝对路径。在其他平台上,只需一个 root-directory-separator 就足够了。请注意,这意味着在 Windows 上,IS_ABSOLUTE 可以为假,而``HAS_ROOT_DIRECTORY`` 可以为真。

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> 在检查之前被标准化 <Normalization>`。

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 选项时,转换后的路径是 normalized

例如:

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>])

使用“/”作为“目录分隔符”将所有“<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 返回)。删除后,任何尾随的“目录分隔符”(如果存在)将保持不变。

如果未给出 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 \"${result}\"")

输出::

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> 中删除 :ref:`extension <EXTENSION_DEF>`(如果有的话)。

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

<input> 替换 extension。其效果等同于以下内容:

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 为真),则相对于 BASE_DIRECTORY 选项指定的给定基目录进行评估。如果未指定 BASE_DIRECTORY,则默认基目录将为 CMAKE_CURRENT_SOURCE_DIR

当指定 NORMALIZE 选项时,路径在路径计算之后是 normalized

因为 cmake_path() 不访问文件系统,所以符号链接没有解析,任何前导波浪号都没有展开。要计算解析了符号链接并扩展前导波浪号的真实路径,请改用 file(REAL_PATH) 命令。

本地转换

本节命令中,*native*指的是宿主平台,交叉编译时不是目标平台。

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

将 cmake 风格的 <path-var> 转换为带有特定平台斜杠的本机路径(Windows 主机上的```` 和其他地方的``/``)。

当指定 NORMALIZE 选项时,路径在转换之前是 normalized

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

将本机“<input>”路径转换为带有正斜杠(“/”)的 cmake 样式路径。在 Windows 主机上,长文件名标记被考虑在内。输入可以是单个路径或系统搜索路径,如 $ENV{PATH}。搜索路径将转换为由 ; 字符分隔的 cmake 样式列表(在非 Windows 平台上,这实际上意味着 : 分隔符被替换为 ;)。转换的结果存储在 <out-var> 变量中。

当指定 NORMALIZE 选项时,路径在转换之前是 normalized

备注

与大多数其他``cmake_path()`` 子命令不同,CONVERT 子命令将文字字符串作为输入,而不是变量名。

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

将 cmake 风格的``<input>`` 路径转换为带有特定于平台的斜杠的本机路径(Windows 主机上的```` 和其他地方的``/)。输入可以是单个路径或 cmake 样式的列表。列表将转换为本机搜索路径(;`` - 在 Windows 上分隔,: - 在其他平台上分隔)。转换的结果存储在 <out-var> 变量中。

当指定 NORMALIZE 选项时,路径在转换之前是 normalized

备注

与大多数其他``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`` 的哈希值。在计算散列之前,路径总是 normalized