cmake_path

在版本 3.20 中添加。

此命令用于路径操作。只处理路径的语法方面,不与任何底层文件系统进行任何交互。该路径可能表示一个不存在的路径,甚至是在当前文件系统或平台上不允许存在的路径。对于确实与文件系统交互的操作,请参阅 file() 命令。

注意

The 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,因此它也可以是硬链接、符号链接或目录。

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

以下异常适用于上述解释

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

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

stem 是扩展名之前 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. 删除每个 item-name(除 .. 外),该名称紧随其后的是一个 directory-separator 和一个 ..,以及任何紧随其后的 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 至少有一个非空时,才会返回真值。

  • 对于 HAS_PARENT_PATH,根目录也被认为有父目录,其父目录就是自身。除了路径仅包含一个 文件名 的情况,其他情况结果都为真。

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

如果 <path-var> 为绝对路径,则将 <out-var> 设置为真。绝对路径是指不依赖额外起始位置就能唯一标识文件位置的路径。在 Windows 上,这意味着路径必须同时具有 root-nameroot-directory-separator 才能被视为绝对路径。在其他平台上,只需要 root-directory-separator 即可。请注意,这意味着在 Windows 上,IS_ABSOLUTE 可能为假,而 HAS_ROOT_DIRECTORY 可能为真。

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

此命令将 IS_ABSOLUTE 的反值存储在 <out-var> 中。

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> 返回假。

例如

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> 中的 文件名 部分替换为 <input>。如果 <path-var> 没有文件名部分(即 HAS_FILENAME 返回假),则路径保持不变。该操作等效于以下操作

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 为真),则它相对于 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> 的哈希值,使得对于两个比较相等(COMPARE ... EQUAL)的路径 p1p2p1 的哈希值等于 p2 的哈希值。在计算哈希值之前,路径总是规范化的。