Cmake命令之cmake_path

2023-08-23  本文已影响0人  Domibaba

一、命令简介

该命令用来操作路径,仅仅是语义概念上的处理,并不会在文件系统上执行任何交互。路径可以不存在,甚至是当前系统不允许的路径。如果想要了解与文件系统交互的命令,参考file()

注意:cmake_path会将路径以CMake所运行的构建系统(例如,主机平台)的格式处理,而不是目标系统。在交叉编译过程中,如果路径包含的元素在CMake运行的系统上无法表示(例如路径是C:\\,但是系统并不是Windows),那么结果不可预测。

cmake_path分为以下几个子命令:分解、查询、修改、生成、转换、哈希。

该命令有如下约定:
路径结构和术语:

一个路径的组成如下(都是可选的,但是可能存在某些限制):

root-name root-directory-separator (item-name directory-separator)\* filename

创建一个路径变量

可以使用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. 替换多个连续的/分隔符为单个/分隔符,例如//a/b///c规范化后为/a/b/c

  3. 移除在分隔符后的单个.,例如/a/./b/c/.规范化后为/a/b/c

  4. 如果item-name后面紧跟着是..,那么删除该item-name..以及跟在他们之后的分隔符/,例如/a/b/../c规范化后为/a/c

  5. 如果路径包含根路径,并且根路径后面紧跟的是..,那么移除..和跟在它后面的/,因为根目录的父路径仍是它自己。例如/../a规范化后为/a.

  6. 如果最后一个item-name..,那么移除..之后的分隔符/,例如../规范化后为..

  7. 经过上述规范化后的路径仍为空,那么规范化后的路径为.(./的规范化路径也是.).

二、子命令详解

2.1 分解

cmake_path(GET)子命令从一个路径中获取不同的部分。如果待获取的部分在路径中不存在,那么该子命令会将空字符串结果存于<out-var>中,例如只有在Windows中才存在root-name的概念,因此,如果CMake运行的系统非Windows,获取ROOT_NAME会得到一个空字符串。如果HAS_RELATIVE_PART子命令结果为false,那么获取PARENT_NAME的结果就是<path-var>,由于根路径的父路径是它自己,当HAS_RELATIVE_PART子命令结果为true时,表明<path-var>至少包含一个元素。

子命令形式为:

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

set(path "/usr/bin")

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

message("root name is: ${rootName}")
message("root directory is: ${rootDirectory}")
message("root path is: ${rootPath}")

运行结果: root name is: root directory is: / root path is: /

set(path "/usr/bin")
cmake_path(GET path FILENAME filename)
message("filename is: ${filename}")

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

运行结果: filename is: bin filename is:

set(path "name.ext1.ext2")

# 默认扩展是取最左侧的.到尾部的部分
cmake_path(GET path EXTENSION fullExt)
cmake_path(GET path STEM fullStem)
message("full extension: ${fullExt}")
message("full stem: ${fullStem}")

# 指定LAST_ONLY后,扩展取的是最右侧的.到尾部的部分
cmake_path(GET path EXTENSION LAST_ONLY lastExt)
cmake_path(GET path STEM LAST_ONLY lastStem)
message("last extension: ${lastExt}")
message("last stem: ${lastStem}")

set(dotPath "/a/.")
set(dotDotPath "/a/..")
set(someMorePath "/a/.some.more")

# filename是.的特例:扩展为空
cmake_path(GET dotPath EXTENSION dotExt)
cmake_path(GET dotPath STEM dotStem)

# filename是..的特例:扩展为空
cmake_path(GET dotDotPath EXTENSION dotDotExt)
cmake_path(GET dotDotPath STEM dotDotStem)
cmake_path(GET someMorePath EXTENSION someMoreExt)
cmake_path(GET someMorePath 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: .ext1.ext2 full stem: name last extension: .ext2 last stem: 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 "/usr/local/bin")
cmake_path(GET path RELATIVE_PART relativePath)
message("relative path: ${relativePath}")

set(path "usr/local/bin") # 如果本身就是相对路径,那么RELATIVE_PART子命令获取到的就是它本身
cmake_path(GET path RELATIVE_PART relativePath)
message("relative path: ${relativePath}")

set(path "/")
cmake_path(GET path RELATIVE_PART relativePath)
message("relative path: \"${relativePath}\"")

运行于macOS下的结果 relative path: usr/local/bin relative path: usr/local/bin relative path: ""

set(path "/usr/local/bin")
cmake_path(GET path PARENT_PATH parentPath)
message("parent path: ${parentPath}")

set(path "usr/local/bin")
cmake_path(GET path PARENT_PATH parentPath)
message("parent path: ${parentPath}")

set(path "usr/local/bin/") # 如果filename为空,则父目录就是自身
cmake_path(GET path PARENT_PATH parentPath)
message("parent path: ${parentPath}")

set(path "/")
cmake_path(GET path PARENT_PATH parentPath)
message("parent path: ${parentPath}")

运行结果 parent path: /usr/local parent path: usr/local parent path: usr/local/bin parent path: /

2.2 查询

查询分为三大类,形式分别为HAS_XXXIS_XXXCOMPARE

1、HAS_XXX查询子命令

每一个cmake_path(GET)子命令都对应一个HAS_XXX子命令,以便判定GET子命令获取的路径信息是否存在。HAS_XXX的子命令格式如下:

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,两个特例为:

  1. 对于HAS_ROOT_PATH子命令,只有当root-nameroot-directory其中一个为非空时返回true

  2. 对于HAS_PARENT_PATH子命令,根目录的父目录是根目录自身,因此除非路径仅包含filename,否则该子命令返回true

set(path "/usr/local/bin/myfile.ext1.ext2")
cmake_path(HAS_ROOT_NAME path hrn)
cmake_path(HAS_ROOT_DIRECTORY path hrd)
cmake_path(HAS_ROOT_PATH path hrp)
cmake_path(HAS_FILENAME path hfn)
cmake_path(HAS_EXTENSION path hext)
cmake_path(HAS_STEM path hstem)
cmake_path(HAS_RELATIVE_PART path hrelative)
cmake_path(HAS_PARENT_PATH path hpp)

message("${path} has root name? --- ${hrn}")
message("${path} has root directory? --- ${hrd}")
message("${path} has root path? --- ${hrp}")
message("${path} has filename? --- ${hfn}")
message("${path} has extension? --- ${hext}")
message("${path} has stem? --- ${hstem}")
message("${path} has relative part? --- ${hrelative}")
message("${path} has parent path? --- ${hpp}")

运行结果 /usr/local/bin/myfile.ext1.ext2 has root name? --- OFF /usr/local/bin/myfile.ext1.ext2 has root directory? --- ON /usr/local/bin/myfile.ext1.ext2 has root path? --- ON /usr/local/bin/myfile.ext1.ext2 has filename? --- ON /usr/local/bin/myfile.ext1.ext2 has extension? --- ON /usr/local/bin/myfile.ext1.ext2 has stem? --- ON /usr/local/bin/myfile.ext1.ext2 has relative part? --- ON /usr/local/bin/myfile.ext1.ext2 has parent path? --- ON

2、IS_XXX查询子命令

IS_XXX查询子命令的格式如下:

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

set(path "/usr/local/bin")
cmake_path(IS_ABSOLUTE path result)
message("${path} is absolute? --- ${result}")

set(path "usr/local/bin")
cmake_path(IS_ABSOLUTE path result)
message("${path} is absolute? --- ${result}")

运行结果: /usr/local/bin is absolute? --- ON usr/local/bin is absolute? --- OFF

set(path "/usr/local/bin")
cmake_path(IS_RELATIVE path result)
message("${path} is relative? --- ${result}")

set(path "usr/local/bin")
cmake_path(IS_RELATIVE path result)
message("${path} is relative? --- ${result}")

运行结果: /usr/local/bin is relative? --- OFF usr/local/bin is relative? --- ON

set(path "/usr/local/bin")
cmake_path(IS_PREFIX path "/usr/local/bin/myfile" result)
message("if ${path} is prefix of /usr/local/bin/myfile? --- ${result}")
cmake_path(IS_PREFIX path "/usr/local" result)
message("if ${path} is prefix of /usr/local? --- ${result}")

set(path "/usr/../tmp/dir")
cmake_path(IS_PREFIX path "/tmp/dir/myfile" NORMALIZE result)
message("if ${path} is prefix of /tmp/dir/myfil? --- ${result}")

运行结果: if /usr/local/bin is prefix of /usr/local/bin/myfile? --- ON if /usr/local/bin is prefix of /usr/local? --- OFF if /usr/../tmp/dir is prefix of /tmp/dir/myfil? --- ON

3、COMPARE查询子命令
cmake_path(COMPARE "/usr/local/bin" EQUAL "/usr/local/bin" cmpResult)
message("CMP result: ${cmpResult}")
cmake_path(COMPARE "/usr/local/bin/" NOT_EQUAL "/usr/local/bin" cmpResult)
message("CMP result: ${cmpResult}")

运行结果: CMP result: ON CMP result: ON

2.3 修改

修改包含设置、追加、移除、替换等子命令操作,具体格式如下:

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

1、设置子命令
set(path "/usr/local/bin")
cmake_path(SET result ${path}) # 如果此处不是用${path}而是path,那么result的结果是"path"
message("${path} set to ${result}")
set(path "/usr/../local////bin/.")
cmake_path(SET result NORMALIZE ${path})
message("${path} normalized set to ${result}")

运行结果: /usr/local/bin set to /usr/local/bin /usr/../local////bin/. normalized set to /local/bin/

2、追加子命令
  1. 如果当前的<input>是绝对路径,则使用<input>替换<path-var>,拼接过程结束。

  2. <input>存在ROOT_NAME并且与<path-var>ROOT_NAME不相同,那么使用<input>替换<path-var>,拼接过程结束。

  3. 如果<input>ROOT_DIRECTORY存在,那么移除<path-var>任意的ROOT_DIRECTORY和整个相对路径。

  4. 如果<input>不存在ROOT_DIRECTORY<path-var>存在filename,或<path-var>是非绝对路径且不存在ROOT_DIRECTORY,那么将/拼接到<path-var>

  5. <input>拼接到<path-var>,忽略ROOT_NAME

cmake_path(APPEND result "/usr/local/bin" "test/myfile" "myfile2")
message("path result is : ${result}")
cmake_path(APPEND result1 "usr/local" "/tmp/myfile")
message("path result1 is : ${result1}")
cmake_path(APPEND result2 "usr/local/" "mydir1" "mydir2")
message("path result2 is : ${result2}")

运行结果: path result is : /usr/local/bin/test/myfile/myfile2 path result1 is : /tmp/myfile path result2 is : usr/local/mydir1/mydir2

set(rs "")
cmake_path(APPEND_STRING rs "/usr/" "local" "bin")
message("APPEND_STRING: ${rs}")

运行结果: APPEND_STRING: /usr/localbin

set(path "/usr/local/bin/myfile")
cmake_path(REMOVE_FILENAME path)
message("For ${path} remove filename is: ${path}")
cmake_path(HAS_FILENAME path hfn)
message("For ${path} has filename? --- ${hfn}")

set(path "/usr/local/bin/myfile")
cmake_path(REMOVE_FILENAME path OUTPUT_VARIABLE output) # 有OUTPUT_VARIABLE,原始路径path不会被修改
message("For ${path} remove filename is: ${output}, orignal path is ${path}")
cmake_path(HAS_FILENAME path hfn)
message("For ${path} has filename? --- ${hfn}")

运行结果: For /usr/local/bin/ remove filename is: /usr/local/bin/ For /usr/local/bin/ has filename? --- OFF For /usr/local/bin/myfile remove filename is: /usr/local/bin/, orignal path is /usr/local/bin/myfile For /usr/local/bin/myfile has filename? --- ON

  1. 使用cmake_path(HAS_FILENAME path has_filename)判断是否存在filename

  2. 第一步的has_filename如果为真,那么依次执行cmake_path(REMOVE_FILENAME path)cmake_path(APPEND path input)

set(path "/usr/local/bin/myfile")
cmake_path(REPLACE_FILENAME path "replacefile" OUTPUT_VARIABLE output)
message("After replace ${path}, result is: ${output}")

# 等效的命令如下
set(path "/usr/local/bin/myfile")
cmake_path(HAS_FILENAME path has_filename)
if(has_filename)
    cmake_path(REMOVE_FILENAME path OUTPUT_VARIABLE output)
    cmake_path(APPEND output "replacefile")
    message("After replace ${path} use combine commands, result is: ${output}")
endif()

运行结果: After replace /usr/local/bin/myfile, result is: /usr/local/bin/replacefile After replace /usr/local/bin/myfile use combine commands, result is: /usr/local/bin/replacefile

set(path "/usr/local/bin/myfile.ext1.ext2")
cmake_path(REMOVE_EXTENSION path OUTPUT_VARIABLE output)
message("remove extersion of ${path} is ${output}")
cmake_path(REMOVE_EXTENSION path LAST_ONLY OUTPUT_VARIABLE output)
message("remove LAST_ONLY extersion of ${path} is ${output}")

运行结果: remove extersion of /usr/local/bin/myfile.ext1.ext2 is /usr/local/bin/myfile remove LAST_ONLY extersion of /usr/local/bin/myfile.ext1.ext2 is /usr/local/bin/myfile.ext1

set(path "/usr/local/bin/myfile.ext1.ext2")
cmake_path(REPLACE_EXTENSION path "dat1.dat2" OUTPUT_VARIABLE output)
message("use dat1.dat2 replace extersion of ${path} is ${output}")
cmake_path(REPLACE_EXTENSION path LAST_ONLY "dat1.dat2" OUTPUT_VARIABLE output)
message("user dat1.dat2 replace LAST_ONLY extersion of ${path} is ${output}")

运行结果: use dat1.dat2 replace extersion of /usr/local/bin/myfile.ext1.ext2 is /usr/local/bin/myfile.dat1.dat2 user dat1.dat2 replace LAST_ONLY extersion of /usr/local/bin/myfile.ext1.ext2 is /usr/local/bin/myfile.ext1.dat1.dat2

2.4 生成路径

分为规范化路径、产生相对路径、产生绝对路径三种。

set(path "/usr/local/bin/myfile")
cmake_path(RELATIVE_PATH path BASE_DIRECTORY "/usr/local" OUTPUT_VARIABLE output)
message("the relative path ${path} for base /usr/local is ${output}")

cmake_path(RELATIVE_PATH path OUTPUT_VARIABLE output) # 未指定基路径,默认基路径为CMAKE_CURRENT_SOURCE_DIR变量存放的路径
message("the relative path ${path} for base ${CMAKE_CURRENT_SOURCE_DIR} is ${output}")

运行结果 the relative path /usr/local/bin/myfile for base /usr/local is bin/myfile the relative path /usr/local/bin/myfile for base /Users/XX1/XX2/XX3/XX4/XX5 is ../../../../../../usr/local/bin/myfile

set(path "/usr/local/bin/myfile") # 原路径是绝对路径,结果不变
cmake_path(ABSOLUTE_PATH path BASE_DIRECTORY "/usr/local" OUTPUT_VARIABLE output)
message("the absolute path ${path} for base /usr/local is ${output}")
cmake_path(ABSOLUTE_PATH path OUTPUT_VARIABLE output)
message("the absolute path ${path} for base ${CMAKE_CURRENT_SOURCE_DIR} is ${output}")

set(path "mydir/myfile") # 原路径是相对路径,拼接
cmake_path(ABSOLUTE_PATH path BASE_DIRECTORY "/usr/local" OUTPUT_VARIABLE output)
message("the absolute path ${path} for base /usr/local is ${output}")
cmake_path(ABSOLUTE_PATH path OUTPUT_VARIABLE output)
message("the absolute path ${path} for base ${CMAKE_CURRENT_SOURCE_DIR} is ${output}")

运行结果: the absolute path /usr/local/bin/myfile for base /usr/local is /usr/local/bin/myfile the absolute path /usr/local/bin/myfile for base /Users/XX1/XX2/XX3/XX4/XX5 is /usr/local/bin/myfile

the absolute path mydir/myfile for base /usr/local is /usr/local/mydir/myfile the absolute path mydir/myfile for base /Users/XX1/XX2/XX3/XX4/XX5 is /Users/XX1/XX2/XX3/XX4/XX5/mydir/myfile

2,5 原生转换

指的是转换成CMake所在的构建平台路径规范,因此这里的原生指交叉编译的构建平台,而不是目标平台。例如程序在windows下构建,在嵌入式设备上跑,那么原生指的是Windows平台。

set(path "/usr/local/bin/myfile")
cmake_path(NATIVE_PATH path output)
message("the native path of ${path} is: ${output}")

运行结果(macOS): the native path of /usr/local/bin/myfile is: /usr/local/bin/myfile

运行结果(Windows): the native path of /usr/local/bin/myfile is: \usr\local\bin\myfile

set(path "/usr/local/bin/myfile" "/tmp/mydir") # cmake的set命令以`;`分隔多个值,${path}=/usr/local/bin/myfile;/tmp/mydir
cmake_path(CONVERT "${path}" TO_CMAKE_PATH_LIST output) # 注意要使用"${path}"而不是${path}变量
message("1: the native path ${path} to cmake-style: ${output}")

set(path "/usr/local/bin/myfile:/tmp/mydir") # 模拟以:分隔多个值,${path}="/usr/local/bin/myfile:/tmp/mydir"
cmake_path(CONVERT "${path}" TO_CMAKE_PATH_LIST output) # ${path}变量已经是一个字符串字面量,因此此处也可以用${path}
message("2: the native path ${path} to cmake-style: ${output}")

运行结果(macOS)(注意;和:的区别): 1: the native path /usr/local/bin/myfile;/tmp/mydir to cmake-style: /usr/local/bin/myfile;/tmp/mydir 2: the native path /usr/local/bin/myfile:/tmp/mydir to cmake-style: /usr/local/bin/myfile;/tmp/mydir

运行结果(Windows)(注意;和:的区别): 1: the native path /usr/local/bin/myfile;/tmp/mydir to cmake-style: /usr/local/bin/myfile:/tmp/mydir 2: the native path /usr/local/bin/myfile:/tmp/mydir to cmake-style: /usr/local/bin/myfile:/tmp/mydir

set(path "/usr/local/bin/myfile" "/tmp/mydir") # cmake的set命令以`;`分隔多个值,${path}=/usr/local/bin/myfile;/tmp/mydir
cmake_path(CONVERT "${path}" TO_NATIVE_PATH_LIST output) # 注意要使用"${path}"而不是${path}变量
message("the cmake-style path ${path} to native path: ${output}")

运行结果(macOS)(注意;和:的区别): the cmake-style path /usr/local/bin/myfile;/tmp/mydir to native path: /usr/local/bin/myfile:/tmp/mydir

运行结果(Windows)(注意;和:的区别): the cmake-style path /usr/local/bin/myfile;/tmp/mydir to native path: \usr\local\bin\myfile;\tmp\mydir

2.6 哈希

set(path "/usr/local/bin/myfile")
cmake_path(HASH path hash_path)
cmake_path(APPEND path2 "/usr/local" "bin/myfile")
cmake_path(HASH path2 hash_path2)
message("path1:${path}, path1 hash:${hash_path}")
message("path2:${path2}, path2 hash:${hash_path2}")

运行结果: path1:/usr/local/bin/myfile, path1 hash:5a17be211871fbe path2:/usr/local/bin/myfile, path2 hash:5a17be211871fbe


附录:参考文档

  1. https://cmake.org/cmake/help/latest/command/cmake_path.html
上一篇下一篇

猜你喜欢

热点阅读