当前位置: 首页 > news >正文

CMake进阶: 路径处理指令join_paths和cmake_path

目录

1.join_paths

1.1.简介

1.2.函数参数解析

1.3.核心逻辑分步拆解

1.4.常用示例

1.5.注意事项

2.cmake_path

2.1.简介

2.2.常用子命令与实战示例

2.3.与旧方法的对比(优势)

2.4.注意事项

2.5.常见使用场景总结

相关链接


1.join_paths

1.1.简介

JoinPaths.cmake:

function(join_paths joined_path first_path_segment)set(temp_path "${first_path_segment}")foreach(current_segment IN LISTS ARGN)if(NOT ("${current_segment}" STREQUAL ""))if(IS_ABSOLUTE "${current_segment}")set(temp_path "${current_segment}")else()set(temp_path "${temp_path}/${current_segment}")endif()endif()endforeach()set(${joined_path} "${temp_path}" PARENT_SCOPE)
endfunction()

         join_paths 是一个自定义路径拼接工具,用于将多个路径片段(如 ab/cd)拼接成一个完整的路径,同时处理绝对路径覆盖空片段过滤逻辑,确保跨平台路径格式的兼容性(CMake 会自动处理 Windows 的 \ 和 Unix 的 /)。

        输入多个路径片段(如 first_path_segment + 可变参数 ARGN),输出拼接后的完整路径,核心规则:

  1. 从第一个路径片段开始初始化拼接结果;
  2. 后续片段若为绝对路径,则直接覆盖之前的拼接结果(绝对路径优先级最高);
  3. 后续片段若为相对路径,则追加到当前拼接结果后(用 / 连接);
  4. 自动过滤空片段(避免生成 a//b 这类无效路径)。

1.2.函数参数解析

参数名类型作用
joined_path输出参数存储最终拼接结果的变量名(需通过 PARENT_SCOPE 传递到函数外部)。
first_path_segment输入参数第一个路径片段(拼接的起始基础,不能为空,否则后续相对路径会出错)。
ARGN可变输入参数后续的路径片段(可多个,如 bc/d/e 等)。

1.3.核心逻辑分步拆解

CMake基础:宏(macro)和函数(function)

函数内部执行流程可分为 4 步,以下结合代码逐行解释:

1.初始化拼接结果

set(temp_path "${first_path_segment}")
  • 用第一个路径片段 first_path_segment 初始化临时变量 temp_path(后续拼接基于此变量)。
  • 例:若 first_path_segment = "src", 则 temp_path = "src"

2.遍历后续路径片段(ARGN

foreach(current_segment IN LISTS ARGN)
  • ARGN 是 CMake 内置变量,代表函数定义中未显式声明的所有后续参数(如调用 join_paths(result "a" "b" "c") 时,ARGN = ["b", "c"])。
  • 循环处理每个后续片段 current_segment

3.过滤空片段 + 处理绝对 / 相对路径

if(NOT ("${current_segment}" STREQUAL ""))  # 1. 过滤空片段if(IS_ABSOLUTE "${current_segment}")    # 2. 若为绝对路径:覆盖之前结果set(temp_path "${current_segment}")else()                                  # 3. 若为相对路径:追加到当前结果set(temp_path "${temp_path}/${current_segment}")endif()
endif()
  • 过滤空片段:若片段为空(如调用时传了 ""),直接跳过,避免生成 a//b 这类含多余分隔符的路径。
  • 绝对路径覆盖:若当前片段是绝对路径(如 /usr/local 或 C:/Program Files),则直接将 temp_path 重置为该绝对路径(因为绝对路径不需要依赖之前的相对路径)。
  • 相对路径追加:若为相对路径,用 / 连接到当前 temp_path 后(CMake 会自动将 / 转换为当前平台的路径分隔符,如 Windows 下的 \)。

4.输出拼接结果到父作用域

set(${joined_path} "${temp_path}" PARENT_SCOPE)
  • PARENT_SCOPE 是关键:CMake 函数内部的变量默认是局部变量,添加此关键字可将 temp_path 的值赋值给外部传入的 joined_path 变量(让函数外部能使用拼接结果)。

1.4.常用示例

通过不同调用场景,理解函数的实际效果(跨平台兼容,以下示例结果在 Windows 下会自动将 / 转为 \):

示例 1:基本相对路径拼接

# 调用:拼接 "src"、"ui"、"dialogs"
join_paths(RESULT "src" "ui" "dialogs")
message(STATUS "拼接结果:${RESULT}")  # 输出:src/ui/dialogs(Windows 下为 src\ui\dialogs)

示例 2:包含绝对路径(覆盖逻辑)

# 调用:第一个片段是相对路径,第三个片段是绝对路径
join_paths(RESULT "src" "temp" "/usr/local/include")
message(STATUS "拼接结果:${RESULT}")  # 输出:/usr/local/include(绝对路径覆盖之前的 "src/temp")

示例 3:包含空片段(自动过滤)

# 调用:第二个片段是空字符串,会被跳过
join_paths(RESULT "build" "" "Debug" "bin")
message(STATUS "拼接结果:${RESULT}")  # 输出:build/Debug/bin(空片段未影响)

示例 4:Windows 绝对路径拼接

# 调用:第一个片段是 Windows 绝对路径,后续追加相对路径
join_paths(RESULT "C:/Qt" "5.15.2" "msvc2019_64" "bin")
message(STATUS "拼接结果:${RESULT}")  # 输出:C:/Qt/5.15.2/msvc2019_64/bin(Windows 下为 C:\Qt\5.15.2\msvc2019_64\bin)

1.5.注意事项

1.第一个片段不能为空:若 first_path_segment 是空字符串,后续相对路径会从根目录开始(如 join_paths(RESULT "" "a" "b") 会生成 /a/b,Windows 下为 \a\b),可能不符合预期,建议第一个片段至少为非空路径(如 . 代表当前目录)。

2.跨平台兼容性:函数中用 / 连接路径,但 CMake 会自动根据当前平台转换为对应的分隔符(Windows 用 \,Unix 用 /),无需手动处理。

3.输出参数需传变量名
调用时第一个参数必须是变量名(如 RESULT),不能直接传值(如 join_paths("src/ui" "src" "ui") 是错误的,会导致结果无法输出)。

4.CMake 版本兼容
函数依赖 IS_ABSOLUTE(CMake 3.0+ 支持)和 foreach(IN LISTS)(CMake 3.0+ 支持),适用于绝大多数现代 CMake 环境(3.0+)。

2.cmake_path

2.1.简介

        在 CMake 3.20+ 中,cmake_path 是一个专门用于路径处理的现代化命令,核心作用是统一解决路径拼接、解析、转换、判断等场景,替代了早期 CMake 中需要自定义函数(如 join_paths)或依赖 file() 命令的繁琐操作。它原生支持跨平台路径格式(自动处理 Windows 的 \ 和 Unix 的 /),且语法清晰、功能覆盖全面。

        相比早期路径处理方式(如自定义 join_pathsfile(TO_CMAKE_PATH)),cmake_path 的优势的在于:

  1. 功能模块化:将路径操作拆分为 JOIN(拼接)、GET_FILENAME_COMPONENT(解析)、ABSOLUTE_PATH(绝对化)等子命令,逻辑清晰;
  2. 跨平台兼容:自动适配不同系统的路径分隔符,无需手动替换 \ 或 /
  3. 内置容错:自动处理路径中的 .(当前目录)、..(上级目录),避免无效路径;
  4. 支持变量直接操作:可直接读取 / 修改路径变量,无需中间临时变量。

2.2.常用子命令与实战示例

cmake_path 的语法格式为:

cmake_path(<子命令> [参数1] [参数2] ... OUTPUT <输出变量>)

(部分子命令无需 OUTPUT,直接操作变量)

以下是项目中最常用的 6 类子命令:

1.路径拼接:JOIN

将多个路径片段拼接为完整路径,自动处理分隔符和绝对路径覆盖(类似自定义 join_paths,但更智能)。

语法cmake_path(JOIN <片段1> <片段2> ... [OUTPUT <结果变量>])

示例:

# 1. 基本拼接:相对路径片段
cmake_path(JOIN "src" "ui" "dialogs" OUTPUT UI_DIR)
message(STATUS "UI 目录:${UI_DIR}")  # 输出:src/ui/dialogs(Windows 下为 src\ui\dialogs)# 2. 包含绝对路径:后续绝对路径会覆盖之前的拼接结果
cmake_path(JOIN "build" "temp" "/usr/local/include" OUTPUT INC_DIR)
message(STATUS "头文件目录:${INC_DIR}")  # 输出:/usr/local/include(绝对路径覆盖)# 3. 包含 . 和 ..:自动简化路径
cmake_path(JOIN "src" ".." "bin" OUTPUT BIN_DIR)
message(STATUS "二进制目录:${BIN_DIR}")  # 输出:bin(src/.. 简化为当前目录)

2.路径解析:GET_FILENAME_COMPONENT

从完整路径中提取指定部分(如文件名、目录名、扩展名),替代早期的 file(GET_FILENAME_COMPONENT)

语法cmake_path(GET_FILENAME_COMPONENT <路径变量> <提取类型> OUTPUT <结果变量>)

常见 提取类型

  • DIRECTORY:提取路径中的目录部分;
  • NAME:提取文件名(含扩展名);
  • NAME_WE:提取文件名(不含扩展名);
  • EXTENSION:提取文件扩展名;
  • STEM:同 NAME_WE,提取文件名(不含扩展名)。

示例:

# 定义一个完整路径
set(FILE_PATH "/home/user/project/src/main.cpp")# 1. 提取目录部分
cmake_path(GET_FILENAME_COMPONENT FILE_DIR "${FILE_PATH}" DIRECTORY OUTPUT DIR_RESULT)
message(STATUS "文件目录:${DIR_RESULT}")  # 输出:/home/user/project/src# 2. 提取完整文件名(含扩展名)
cmake_path(GET_FILENAME_COMPONENT FILE_NAME "${FILE_PATH}" NAME OUTPUT NAME_RESULT)
message(STATUS "完整文件名:${NAME_RESULT}")  # 输出:main.cpp# 3. 提取文件名(不含扩展名)
cmake_path(GET_FILENAME_COMPONENT FILE_NAME_WE "${FILE_PATH}" NAME_WE OUTPUT NAME_WE_RESULT)
message(STATUS "文件名(无扩展名):${NAME_WE_RESULT}")  # 输出:main# 4. 提取扩展名
cmake_path(GET_FILENAME_COMPONENT FILE_EXT "${FILE_PATH}" EXTENSION OUTPUT EXT_RESULT)
message(STATUS "文件扩展名:${EXT_RESULT}")  # 输出:cpp

3.路径绝对化:ABSOLUTE_PATH

将相对路径转换为绝对路径,基于指定的 “基准目录”(默认基准目录为当前构建目录 CMAKE_CURRENT_BINARY_DIR)。

语法cmake_path(ABSOLUTE_PATH <路径变量> [BASE_DIRECTORY <基准目录>] OUTPUT <结果变量>)

示例:

# 相对路径
set(RELATIVE_PATH "src/ui")# 1. 基于当前构建目录(默认)转换为绝对路径
cmake_path(ABSOLUTE_PATH "${RELATIVE_PATH}" OUTPUT ABS_PATH1)
message(STATUS "绝对路径1:${ABS_PATH1}")  # 输出:/home/user/project/build/src/ui(Unix)# 2. 基于源码目录(CMAKE_CURRENT_SOURCE_DIR)转换为绝对路径
cmake_path(ABSOLUTE_PATH "${RELATIVE_PATH}" BASE_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" OUTPUT ABS_PATH2)
message(STATUS "绝对路径2:${ABS_PATH2}")  # 输出:/home/user/project/src/ui(Unix)

4.路径规范化:NORMAL_PATH

简化路径中的 .(当前目录)、..(上级目录)和重复的分隔符,生成规范的路径格式。

语法cmake_path(NORMAL_PATH <路径变量> [OUTPUT <结果变量>])

示例:

# 含冗余部分的路径
set(MESSY_PATH "/home/user/../project/src/./ui//dialogs")# 规范化路径
cmake_path(NORMAL_PATH "${MESSY_PATH}" OUTPUT NORMALIZED_PATH)
message(STATUS "规范化路径:${NORMALIZED_PATH}")  # 输出:/home/project/src/ui/dialogs

5.路径判断:EXISTS/IS_ABSOLUTE/IS_DIRECTORY/IS_FILE

判断路径是否存在、是否为绝对路径、是否为目录 / 文件,结果存储为布尔变量(TRUE/FALSE)。

语法cmake_path(<判断类型> <路径变量> OUTPUT <结果变量>)

常见 判断类型

  • EXISTS:路径是否存在(文件或目录);
  • IS_ABSOLUTE:路径是否为绝对路径;
  • IS_DIRECTORY:路径是否为目录(且存在);
  • IS_FILE:路径是否为文件(且存在)。

示例:

set(TEST_PATH "/home/user/project/src/main.cpp")
set(REL_PATH "src/ui")# 1. 判断是否为绝对路径
cmake_path(IS_ABSOLUTE "${TEST_PATH}" OUTPUT IS_ABS)
message(STATUS "${TEST_PATH} 是否为绝对路径:${IS_ABS}")  # 输出:TRUE# 2. 判断路径是否存在
cmake_path(EXISTS "${TEST_PATH}" OUTPUT PATH_EXISTS)
message(STATUS "${TEST_PATH} 是否存在:${PATH_EXISTS}")  # 输出:TRUE(若文件存在)# 3. 判断是否为目录
cmake_path(IS_DIRECTORY "${REL_PATH}" OUTPUT IS_DIR)
message(STATUS "${REL_PATH} 是否为目录:${IS_DIR}")  # 输出:TRUE(若目录存在)

6.路径追加:APPEND

在已有路径变量后追加片段(类似 JOIN,但直接修改原变量,无需输出新变量)。

语法cmake_path(APPEND <路径变量> <片段1> <片段2> ...)

示例:

# 初始路径
set(BIN_DIR "build/bin")# 追加片段(Debug 目录)
cmake_path(APPEND BIN_DIR "Debug")
message(STATUS "追加后路径:${BIN_DIR}")  # 输出:build/bin/Debug(Windows 下为 build\bin\Debug)# 继续追加片段(exe 目录)
cmake_path(APPEND BIN_DIR "exe")
message(STATUS "再次追加后路径:${BIN_DIR}")  # 输出:build/bin/Debug/exe

2.3.与旧方法的对比(优势)

以 “路径拼接” 为例,对比 cmake_path(JOIN) 与早期自定义 join_paths 函数:

特性cmake_path(JOIN)(CMake 3.20+)自定义 join_paths 函数
跨平台分隔符自动适配(\//需依赖 CMake 自动转换,或手动处理
路径简化(.``..自动简化(如 src/../bin → bin需额外添加简化逻辑,否则生成无效路径
语法简洁性一行命令,直接输出变量需定义函数,调用时需传递输出变量名
错误处理内置容错(如空片段自动忽略)需手动添加空片段过滤逻辑

2.4.注意事项

1.CMake 版本要求cmake_path 是 CMake 3.20 及以上版本引入的,若项目需兼容旧版本(如 3.19 及以下),需改用 file() 命令或自定义函数(如 join_paths);

2.路径变量引用:所有路径参数需用 ${} 引用变量(如 ${FILE_PATH}),直接传字符串(如 "src/ui")也支持;

3.输出变量覆盖OUTPUT 指定的变量若已存在,会被覆盖,需注意变量名冲突;

4.Windows 特殊路径:支持 Windows 下的盘符路径(如 C:/Program Files),自动保留盘符信息。

2.5.常见使用场景总结

场景需求推荐子命令示例代码片段
拼接源码 / 构建目录路径JOINcmake_path(JOIN "${CMAKE_SOURCE_DIR}" "src" OUTPUT SRC_DIR)
提取库文件的目录GET_FILENAME_COMPONENTcmake_path(GET_FILENAME_COMPONENT "${LIB_FILE}" DIRECTORY OUTPUT LIB_DIR)
检查配置文件是否存在EXISTScmake_path(EXISTS "${CONFIG_FILE}" OUTPUT CONFIG_EXISTS)
将相对路径转为绝对路径ABSOLUTE_PATHcmake_path(ABSOLUTE_PATH "${REL_PATH}" BASE_DIRECTORY "${CMAKE_SOURCE_DIR}" OUTPUT ABS_PATH)
简化用户输入的冗余路径NORMAL_PATHcmake_path(NORMAL_PATH "${USER_PATH}" OUTPUT NORMAL_PATH)

cmake_path 是 CMake 为路径处理设计的官方标准化工具,覆盖了路径拼接、解析、转换、判断的全场景,解决了早期自定义函数的繁琐和兼容性问题。在 CMake 3.20+ 的项目中,推荐优先使用 cmake_path 替代旧的路径处理方式,提升代码的可读性和跨平台稳定性。

相关链接

  • CMake 官网 https://cmake.org/
  • CMake 官方文档:https://cmake.org/cmake/help/latest/guide/tutorial/index.html
  • CMake 源码:https://github.com/Kitware/CMake
  • CMake 源码:https://gitlab.kitware.com/cmake/
  • 中文版基础介绍: https://www.hahack.com/codes/cmake/
  • wiki: https://gitlab.kitware.com/cmake/community/-/wikis/Home
  • Modern CMake 简体中文版:  https://modern-cmake-cn.github.io/Modern-CMake-zh_CN/

文章转载自:

http://85oEn99T.rnzgf.cn
http://OSpTkJnd.rnzgf.cn
http://3mKr5Yyv.rnzgf.cn
http://oPOkHjyz.rnzgf.cn
http://Uha7GaY0.rnzgf.cn
http://YvRFSLjU.rnzgf.cn
http://osdxHPzO.rnzgf.cn
http://Bkngv21J.rnzgf.cn
http://bcThWUVI.rnzgf.cn
http://w8uuI6Iw.rnzgf.cn
http://1ZwuhAFq.rnzgf.cn
http://9L8B2vie.rnzgf.cn
http://z9DojWZv.rnzgf.cn
http://OPB4GNWG.rnzgf.cn
http://cv2o3aBm.rnzgf.cn
http://YuxOTIum.rnzgf.cn
http://1kLjhdCy.rnzgf.cn
http://HhlkP7kU.rnzgf.cn
http://vS7qtkW4.rnzgf.cn
http://H1KYx0j3.rnzgf.cn
http://qvHwkIMg.rnzgf.cn
http://t7yuiTj5.rnzgf.cn
http://xfOOtud8.rnzgf.cn
http://hYgdSb5X.rnzgf.cn
http://NQcbbFZ9.rnzgf.cn
http://HWUnj9TW.rnzgf.cn
http://JI2s1e2b.rnzgf.cn
http://PpZFA1fO.rnzgf.cn
http://Svv7jNhm.rnzgf.cn
http://iRRAgaTR.rnzgf.cn
http://www.dtcms.com/a/388351.html

相关文章:

  • 算法简略速记手册
  • C语言(长期更新)第17讲内存函数
  • 【CSP-S】 基础知识与编程环境
  • Python HTTPS 教程 如何发送 HTTPS 请求、解决证书错误、实现抓包与网络调试全攻略
  • 【Cesium 开发实战教程】第五篇:空间分析实战:缓冲区、可视域与工程测量
  • 告别塑料感!10分钟学会基础材质调节
  • CSS Modules 和 CSS-in-JS比较
  • threejs(三)模型对象、材质
  • (自用)vscode正则表达式(正则表达式语法大全)vocode正则化(注意正则化和正则表达式不是一个概念)
  • Node.js:重新定义全栈开发的JavaScript运行时
  • @PropertySource 注解学习笔记
  • 安徽Ecovadis认证辅导怎么做呢?
  • 【完整源码+数据集+部署教程】太阳能面板缺陷分割系统: yolov8-seg-C2f-REPVGGOREPA
  • 什么是直播美颜SDK?人脸识别与实时渲染的技术解析
  • RabbitMQ-MQTT即时通讯详解
  • AI辅助论文写作:如何成为真正的“AI Native学者”?
  • Frida 实战:Android JNI 数组 (jobjectArray) 操作全流程解析
  • 腾讯正式发布全新一代智能驾驶地图9.0
  • 鸿蒙应用开发之装饰器大总结 —— 从语法糖到全场景跨语言运行时的全景视角
  • 论文阅读:EMNLP 2024 Humans or LLMs as the Judge? A Study on Judgement Bias
  • 4-1〔O҉S҉C҉P҉ ◈ 研记〕❘ WEB应用攻击▸目录遍历漏洞-A
  • 买期货卖认购期权策略
  • 使用 VB.NET 进行仪器编程
  • C# DataGridView中DataGridViewCheckBoxColumn不能界面上勾选的原因
  • FT5206GE1屏幕驱动 适配STM32F1 型号SLC07009A(记录第一次完全独自编写触摸板驱动)
  • PETRV1在NuScenes数据集上的推理及可视化详解
  • 函数后的 `const` 关键字
  • Dify 从入门到精通(第 85/100 篇):Dify 的多模态模型扩展性(高级篇)
  • Flutter-[2]第一个应用
  • Jenkins + SonarQube 从原理到实战六:Jenkins 和 SonarQube 的项目落地实践