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
结尾,则路径具有filename
。filename
实际上是路径的最后一个item-name
,因此它也可以是硬链接、符号链接或目录。filename
可以有一个扩展名。默认情况下,扩展名定义为从最左边的句点(包括句点)开始到filename
结尾的子字符串。在接受LAST_ONLY
关键字的命令中,LAST_ONLY
将解释更改为从最右边的句点开始的子字符串。以下例外情况适用于上述解释
如果
filename
中的第一个字符是句点,则忽略该句点(即,像".profile"
这样的filename
被视为没有扩展名)。如果
filename
是.
或..
,则它没有扩展名。
词干是
filename
中扩展名之前的部分。
某些命令引用 root-path
。这是 root-name
和 root-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>
变量中。
规范化¶
某些子命令支持规范化路径。用于规范化路径的算法如下
如果路径为空,则停止(空路径的规范化形式也是空路径)。
将每个
directory-separator
(可能由多个分隔符组成)替换为单个/
(/a///b --> /a/b
)。删除每个单独的句点 (
.
) 和任何紧随其后的directory-separator
(/a/./b/. --> /a/b
)。删除紧跟
directory-separator
和..
的每个item-name
(..
除外),以及任何紧随其后的directory-separator
(/a/b/../c --> a/c
)。如果存在
root-directory
,则删除任何..
和任何紧随其后的directory-separators
。根目录的父目录仍被视为根目录 (/../a --> /a
)。如果最后一个
item-name
是..
,则删除任何尾随的directory-separator
(../ --> ..
)。如果在此阶段路径为空,则添加一个
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-name
或root-directory
中至少有一个非空时,才会返回 true 结果。对于
HAS_PARENT_PATH
,根目录也被认为具有父目录,它将是其自身。除了路径仅由 filename 组成的情况外,结果为 true。
cmake_path(IS_ABSOLUTE <path-var> <out-var>)
如果 <path-var>
是绝对路径,则将 <out-var>
设置为 true。绝对路径是明确标识文件位置而无需参考其他起始位置的路径。在 Windows 上,这意味着路径必须同时具有 root-name
和 root-directory-separator
才能被视为绝对路径。在其他平台上,仅 root-directory-separator
就足够了。请注意,这意味着在 Windows 上,IS_ABSOLUTE
可以为 false,而 HAS_ROOT_DIRECTORY
可以为 true。
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>
中删除 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>])
用 <input>
替换 <path-var>
中的 filename 组件。如果 <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>])
用 <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 为 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>
的哈希值,以便对于两个比较相等的路径 p1
和 p2
(COMPARE ... EQUAL),p1
的哈希值等于 p2
的哈希值。路径始终在计算哈希值之前规范化。