CMake进阶:核心命令get_filename_component 完全详解

目录

1.简介

[2.最常用的 5 种解析模式](#2.最常用的 5 种解析模式)

2.1.DIRECTORY

2.2.NAME

2.3.NAME_WE (Name Without Extension)

2.4.EXT

2.5.ROOT

3.使用场景

3.1.多级路径向上推导(写XXXConfig.cmake必用)

3.2.规范化路径(去除./../,转为绝对路径)

[3.3.提取文件名 + 后缀分离](#3.3.提取文件名 + 后缀分离)

3.4.配合CMAKE_CURRENT_LIST_DIR拼接路径

4.注意事项

5.完整可复用:自定义XXXConfig.cmake中的路径推导模板


1.简介

CMake进阶:vcpkg中OpenSSLConfig.cmake详解

get_filename_componentCMake 内置核心命令 (不是变量),也是实现「第三方库路径自动推导」的核心,专门用于解析 / 提取文件路径、目录路径、文件名、后缀名等路径相关信息 ,是实现跨平台路径处理、编写通用XXXConfig.cmake/CMakeLists.txt的必备命令,没有之一。

语法:

get_filename_component(<输出变量名> <待解析的路径> <解析模式> [ABSOLUTE] [REALPATH])

参数说明:

  • <输出变量名>:解析后的结果,存入这个变量中供后续使用;
  • <待解析的路径>:可以是文件路径目录路径,支持相对路径 / 绝对路径;
  • <解析模式>:核心!指定要解析的内容,大小写不敏感,有 7 种常用模式,重点记前 5 个高频的
  • [ABSOLUTE]:可选但几乎必加 !将解析后的相对路径转为绝对路径,CMake 路径处理推荐一律用绝对路径,避免各种路径错误;
  • [REALPATH]:可选,解析出真实路径(会解析软链接 / 快捷方式、去除路径中的.//../),跨平台兼容性更强。

2.最常用的 5 种解析模式

2.1.DIRECTORY

提取传入路径 对应的上级目录路径文件 / 目录路径都能解析

  • 如果传入的是「文件路径」→ 提取该文件所在的目录
  • 如果传入的是「目录路径」→ 提取该目录的上级目录
cpp 复制代码
以目录 D:/project/thirdparty/xxx/lib/cmake/xxx/XXXConfig.cmake 为例:

# 示例:传入 配置文件的绝对路径 (${CMAKE_CURRENT_LIST_FILE}就是当前脚本自身路径)
get_filename_component(CONFIG_DIR ${CMAKE_CURRENT_LIST_FILE} DIRECTORY)
# 结果:CONFIG_DIR = D:/project/thirdparty/xxx/lib/cmake/xxx

# 简写PATH 和 DIRECTORY 完全等价,推荐写DIRECTORY语义更清晰
get_filename_component(CONFIG_DIR ${CMAKE_CURRENT_LIST_FILE} PATH)

2.2.NAME

提取传入路径的「文件名」(包含后缀),不管传入的是文件还是目录

cpp 复制代码
get_filename_component(FILE_NAME ${CMAKE_CURRENT_LIST_FILE} NAME)
# 结果:FILE_NAME = XXXConfig.cmake

# 如果传入目录路径
get_filename_component(DIR_NAME "D:/project/thirdparty/xxx/lib" NAME)
# 结果:DIR_NAME = lib

2.3.NAME_WE (Name Without Extension)

提取文件名不含后缀,仅对文件路径有效,目录路径用这个无意义

cpp 复制代码
get_filename_component(FILE_NAME_NO_EXT ${CMAKE_CURRENT_LIST_FILE} NAME_WE)
# 结果:FILE_NAME_NO_EXT = XXXConfig

2.4.EXT

提取文件的后缀名 (带.),仅对文件路径有效

cpp 复制代码
get_filename_component(FILE_EXT ${CMAKE_CURRENT_LIST_FILE} EXT)
# 结果:FILE_EXT = .cmake

2.5.ROOT

提取路径的根目录 ,比如 Windows 的D:/,Linux/macOS 的/

cpp 复制代码
get_filename_component(ROOT_DIR ${CMAKE_CURRENT_LIST_FILE} ROOT)
# Windows结果:ROOT_DIR = D:/
# Linux/macOS结果:ROOT_DIR = /

3.使用场景

3.1.多级路径向上推导 (写XXXConfig.cmake必用)

在自定义XXXConfig.cmake中,通过「配置文件自身路径」自动向上推导整个库的根目录、头文件目录、库文件目录、bin 目录无需硬编码任何路径 ,实现「库放在任意位置都能被 CMake 自动识别」,也是所有官方XXXConfig.cmake的标准写法。

你的XXXConfig.cmake的标准存放路径:

cpp 复制代码
thirdparty/xxx/            # 库的根目录(我们最终要推导的)
├── include/               # 头文件目录(需要推导)
├── lib/                   # 库文件目录(需要推导)
│   └── cmake/
│       └── xxx/           # 库名目录
│           └── XXXConfig.cmake  # 当前脚本自身
└── bin/                   # DLL/SO动态库目录(需要推导)

核心目标 :在XXXConfig.cmake中,从脚本自身路径出发,推导出上面所有目录,完全无硬编码

cpp 复制代码
# 1. 获取当前XXXConfig.cmake脚本自身的绝对路径(CMAKE_CURRENT_LIST_FILE是脚本自身路径)
# 路径:thirdparty/xxx/lib/cmake/xxx/XXXConfig.cmake
get_filename_component(_XXX_CONFIG_DIR "${CMAKE_CURRENT_LIST_FILE}" DIRECTORY ABSOLUTE)

# 2. 向上1级 → cmake/xxx 目录
# 路径:thirdparty/xxx/lib/cmake/xxx
get_filename_component(_XXX_CMAKE_SUB_DIR "${_XXX_CONFIG_DIR}" DIRECTORY ABSOLUTE)

# 3. 向上1级 → cmake 目录
# 路径:thirdparty/xxx/lib/cmake
get_filename_component(_XXX_CMAKE_DIR "${_XXX_CMAKE_SUB_DIR}" DIRECTORY ABSOLUTE)

# 4. 向上1级 → lib 目录
# 路径:thirdparty/xxx/lib
get_filename_component(_XXX_LIB_DIR "${_XXX_CMAKE_DIR}" DIRECTORY ABSOLUTE)

# 5. 向上1级 → 库的根目录【最终核心】
# 路径:thirdparty/xxx
get_filename_component(_XXX_ROOT_DIR "${_XXX_LIB_DIR}" DIRECTORY ABSOLUTE)

# ✅ 最终:通过根目录,拼接出所有需要的目录,固定写法,永不失效
set(_XXX_INCLUDE_DIR "${_XXX_ROOT_DIR}/include") # 头文件目录
set(_XXX_BIN_DIR     "${_XXX_ROOT_DIR}/bin")     # 动态库运行目录
set(_XXX_LIB_DIR     "${_XXX_ROOT_DIR}/lib")     # 静态/导入库目录

3.2.规范化路径(去除./../,转为绝对路径)

有时候路径中会有./../,导致路径不规范,用get_filename_component+ABSOLUTE+REALPATH可以一键规范化:

cpp 复制代码
# 原始路径有../,不规范
set(RAW_PATH "./thirdparty/../xxx/lib")
# 规范化为绝对路径,去除../
get_filename_component(NORMAL_PATH ${RAW_PATH} DIRECTORY ABSOLUTE REALPATH)

3.3.提取文件名 + 后缀分离

处理配置文件、源码文件时常用:

cpp 复制代码
set(FILE_PATH "D:/project/src/main.cpp")
get_filename_component(FILE_NAME ${FILE_PATH} NAME)       # main.cpp
get_filename_component(FILE_NAME_WE ${FILE_PATH} NAME_WE) # main
get_filename_component(FILE_EXT ${FILE_PATH} EXT)         # .cpp

3.4.配合CMAKE_CURRENT_LIST_DIR拼接路径

和之前CMAKE_CURRENT_LIST_DIR完美配合,实现跨平台路径拼接:

cpp 复制代码
# 拼接当前脚本同级的config目录
get_filename_component(CONFIG_DIR "${CMAKE_CURRENT_LIST_DIR}/config" DIRECTORY ABSOLUTE)

4.注意事项

1.DIRECTORYPATH 完全等价

CMake 官方定义,这两个模式是同一个意思,推荐写DIRECTORY,语义更清晰,一眼看出是「提取目录」。

2.强烈建议永远加上 ABSOLUTE 参数

CMake 中路径分「相对路径」和「绝对路径」,相对路径容易因为CMAKE_CURRENT_SOURCE_DIR/CMAKE_CURRENT_BINARY_DIR的变化导致路径错误,所有路径处理都用绝对路径,是 CMake 的最佳实践。

3.路径分隔符自动跨平台兼容

get_filename_component会自动适配 Windows 的\和 Linux/macOS 的/,你在代码中写/即可,CMake 会自动转换为对应系统的分隔符,无需手动处理。

4.变量名建议加下划线前缀(私有变量)

推导路径时的临时变量(比如_XXX_CONFIG_DIR_XXX_ROOT_DIR),建议加下划线前缀,表示这是脚本内的私有变量,避免和外部用户定义的变量重名冲突,这是 CMake 官方的编码规范。

5.多级推导的顺序不能乱

向上推导目录时,必须从脚本自身路径开始,逐级向上,不能跳过层级,否则会推导错误。

5.完整可复用:自定义XXXConfig.cmake中的路径推导模板

cpp 复制代码
# ==============================================================================
# XXXConfig.cmake 通用路径推导模板 (替换XXX为你的库名)
# 配置文件路径:xxx/lib/cmake/xxx/XXXConfig.cmake
# ==============================================================================
# 1. 从脚本自身路径,逐级向上推导库根目录
get_filename_component(_XXX_CONFIG_DIR "${CMAKE_CURRENT_LIST_FILE}" DIRECTORY ABSOLUTE)
get_filename_component(_XXX_LIB_CMAKE_DIR "${_XXX_CONFIG_DIR}" DIRECTORY ABSOLUTE)
get_filename_component(_XXX_LIB_DIR "${_XXX_LIB_CMAKE_DIR}" DIRECTORY ABSOLUTE)
get_filename_component(_XXX_ROOT_DIR "${_XXX_LIB_DIR}" DIRECTORY ABSOLUTE)

# 2. 定义库的核心目录(固定写法)
set(XXX_INCLUDE_DIRS "${_XXX_ROOT_DIR}/include" CACHE PATH "XXX头文件目录")
set(XXX_LIBRARY_DIRS "${_XXX_ROOT_DIR}/lib" CACHE PATH "XXX库文件目录")
set(XXX_BIN_DIR      "${_XXX_ROOT_DIR}/bin" CACHE PATH "XXX动态库运行目录")
set(XXX_ROOT_DIR     "${_XXX_ROOT_DIR}" CACHE PATH "XXX库根目录")

# 3. 验证目录存在性(报错提示,可选但推荐)
if(NOT EXISTS ${XXX_INCLUDE_DIRS})
    message(FATAL_ERROR "XXX头文件目录不存在: ${XXX_INCLUDE_DIRS}")
endif()
if(NOT EXISTS ${XXX_LIBRARY_DIRS})
    message(FATAL_ERROR "XXX库文件目录不存在: ${XXX_LIBRARY_DIRS}")
endif()
相关推荐
Blossom.11810 小时前
工业级扩散模型优化实战:从Stable Diffusion到LCM的毫秒级生成
开发语言·人工智能·python·深度学习·机器学习·stable diffusion·transformer
嘿嘿潶黑黑10 小时前
关于QButtonGroup 在Qt5和Qt6之间的差异
开发语言·qt
代码游侠10 小时前
应用——Linux FrameBuffer图形显示与多线程消息系统项目
linux·运维·服务器·开发语言·前端·算法
hqwest10 小时前
码上通QT实战09--监控页面01-区域划分
开发语言·qt·layout·qss·qt 布局
mrcrack10 小时前
洛谷 B3656 【模板】双端队列 1 方案1+离线处理+一维数组+偏移量 方案2+stl list
c++·list
我命由我1234510 小时前
Kotlin 面向对象 - 装箱与拆箱
android·java·开发语言·kotlin·android studio·android jetpack·android-studio
大、男人10 小时前
python之Callable
开发语言·python
lingzhilab10 小时前
零知IDE——基于STMF103RBT6结合PAJ7620U2手势控制192位WS2812 RGB立方体矩阵
c++·stm32·矩阵