目录
[2.最常用的 5 种解析模式](#2.最常用的 5 种解析模式)
2.3.NAME_WE (Name Without Extension)
3.1.多级路径向上推导(写XXXConfig.cmake必用)
[3.3.提取文件名 + 后缀分离](#3.3.提取文件名 + 后缀分离)
3.4.配合CMAKE_CURRENT_LIST_DIR拼接路径
5.完整可复用:自定义XXXConfig.cmake中的路径推导模板
1.简介
get_filename_component是 CMake 内置核心命令 (不是变量),也是实现「第三方库路径自动推导」的核心,专门用于解析 / 提取文件路径、目录路径、文件名、后缀名等路径相关信息 ,是实现跨平台路径处理、编写通用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.DIRECTORY 和 PATH 完全等价
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()