CMake include目录释义及多源码目录递归包含方法问询
一、“当前源目录”的明确
include_directories中提到的当前源目录,指的是执行该指令的CMakeLists.txt文件所在的目录,对应CMake内置变量CMAKE_CURRENT_SOURCE_DIR的值,和正在编译的源文件所在目录完全无关。
举个直观例子:
假设目录结构:
proj/ ├─ CMakeLists.txt └─ src/ ├─ CMakeLists.txt └─ core/ └─ main.cpp
如果在proj/src/CMakeLists.txt中执行include_directories(./inc),这里的相对路径./inc会被解析为proj/src/inc,不会因为main.cpp位于src/core目录而改变解析结果。
二、跨目录头文件引用的优雅实现
针对你提到的目录结构(A目录含主CMakeLists.txt及头文件,下属B、C子目录各有源码和头文件),推荐以下几种实现方式:
1. 优先使用现代CMake的目标级包含配置
现代CMake不推荐全局的include_directories,更建议用target_include_directories为每个编译目标(库/可执行文件)精准设置包含路径,避免全局污染,还能自动传递依赖路径。
示例配置:
# A目录的CMakeLists.txt cmake_minimum_required(VERSION 3.10) project(MyProject) # 定义B、C子目录的库目标 add_library(B STATIC B/*.cpp) add_library(C STATIC C/*.cpp) # 定义主程序目标 add_executable(main main.cpp) # 给B目标添加包含路径:A根目录(引用A的头文件)、C目录(引用C的头文件) target_include_directories(B PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/C ) # 给C目标添加包含路径:A根目录、B目录 target_include_directories(C PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/B ) # 给主程序添加包含路径,并链接B、C库 target_include_directories(main PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} ) target_link_libraries(main PRIVATE B C)
其中PUBLIC关键字的作用是:如果其他目标链接了B,B的PUBLIC包含目录会自动传递给该目标,无需重复配置。
2. 模块化拆分CMakeLists.txt(更整洁的结构)
可以在B、C子目录各自编写CMakeLists.txt,再通过add_subdirectory引入主配置,让项目结构更清晰:
# A目录的CMakeLists.txt cmake_minimum_required(VERSION 3.10) project(MyProject) add_subdirectory(B) add_subdirectory(C) add_executable(main main.cpp) target_link_libraries(main PRIVATE B C) target_include_directories(main PUBLIC ${PROJECT_SOURCE_DIR})
# B目录的CMakeLists.txt add_library(B STATIC *.cpp) target_include_directories(B PUBLIC ${PROJECT_SOURCE_DIR} PUBLIC ${PROJECT_SOURCE_DIR}/C )
# C目录的CMakeLists.txt add_library(C STATIC *.cpp) target_include_directories(C PUBLIC ${PROJECT_SOURCE_DIR} PUBLIC ${PROJECT_SOURCE_DIR}/B )
PROJECT_SOURCE_DIR是项目根目录(即A目录)的路径,比CMAKE_CURRENT_SOURCE_DIR更稳定,适合跨目录引用。
3. 递归添加所有子目录头文件(类似Xcode/VS的递归查找)
如果确实需要一次性递归添加所有子目录作为包含路径,可以用以下方式实现,但不推荐长期使用(新增文件需重新运行CMake,易引发命名冲突):
# 递归查找所有头文件 file(GLOB_RECURSE ALL_HEADERS *.h *.hpp) # 提取所有头文件所在目录 set(INCLUDE_DIRS "") foreach(HEADER ${ALL_HEADERS}) get_filename_component(DIR ${HEADER} DIRECTORY) list(APPEND INCLUDE_DIRS ${DIR}) endforeach() # 去重后添加到全局包含目录 list(REMOVE_DUPLICATES INCLUDE_DIRS) include_directories(${INCLUDE_DIRS})
4. 结构化头文件引用规范
在源码中建议使用相对项目根目录的路径引用头文件,比如B目录的源码中:
- 引用A目录的头文件:
#include "a_header.h"(因为A目录已被设为包含路径) - 引用C目录的头文件:
#include "C/c_header.h"
这种方式结构清晰,目录变动时无需大量修改源码路径。
内容的提问来源于stack exchange,提问作者Joymaker

