add_subdirectory() 找不到子目录的主因是路径错误或子目录缺cmakelists.txt;路径须相对当前文件、在源码树内且可读,子目录必须含cmakelists.txt,且project()定义需避免重复。

add_subdirectory() 为什么找不到子目录
常见错误是 add_subdirectory() 路径写错,或子目录里没有 CMakeLists.txt。CMake 不会自动递归查找,路径必须是相对于当前 CMakeLists.txt 的**存在且可读**的子路径。
- 路径不能用
../subproj跨出源码树根(即CMAKE_SOURCE_DIR),否则 CMake 报错add_subdirectory given source "../subproj" which is outside of the source tree - 子目录必须包含
CMakeLists.txt,哪怕只有一行project(SubProj) - 如果子项目想独立构建,它的
CMakeLists.txt里别写project()太早——在add_subdirectory()中被包含时,整个构建共用一个project()上下文,重复定义会报错
ExternalProject_Add 和 add_subdirectory 的本质区别
add_subdirectory() 是“源码内联”:子项目的 target、变量、include 目录全部注入主构建上下文;ExternalProject_Add() 是“外部构建”:它只在 configure 阶段生成构建脚本,真正编译发生在 build 阶段,且默认不导出 target 或头文件。
- 要用子项目的库,
add_subdirectory()后直接target_link_libraries(main PRIVATE SubProj::lib)即可 - 用
ExternalProject_Add()的话,得手动写find_package()或硬编码IMPORTEDtarget,还容易链接失败——因为头文件路径、库路径不会自动传播 -
ExternalProject_Add()适合下载并构建第三方(如 zlib、fmt),不适合管理你自己的子模块;add_subdirectory()才是 C++ 项目内模块化的真实做法
子项目如何导出自己的头文件和 target
子项目不是加进来了就自动能用,关键在它自己怎么写 CMakeLists.txt。默认情况下,它的 include_directories() 或 target_include_directories() 只对内部 target 有效,不对外暴露。
- 必须用
target_include_directories(SubProj PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include),其中PUBLIC表示“我用,且链接我的人也要用” - 导出 target 推荐用
add_library(SubProj INTERFACE)+target_include_directories()+target_link_libraries(),比老式add_library(SubProj STATIC)更干净 - 如果子项目有多个 target(比如
SubProj库和subproj_test测试),记得给测试 target 加EXCLUDE_FROM_ALL,不然make默认会试图构建它
Windows 下路径分隔符和生成器兼容性问题
在 Windows 上用 Ninja 或 VS 生成器时,add_subdirectory("third_party\fmt") 这种反斜杠写法可能出问题;而 ExternalProject_Add() 如果 URL 里含空格或中文,又容易触发 shell 解析异常。
立即学习“C++免费学习笔记(深入)”;
- 统一用正斜杠:
add_subdirectory(third_party/fmt),CMake 内部会自动适配 - 避免路径含空格、括号、中文——不是所有生成器(尤其 MinGW Makefiles)都处理得好
- VS 生成器下,子项目若用了
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$:Debug>"),要确保主项目也设了,否则链接时报LNK2038: mismatch detected for 'RuntimeLibrary'
最麻烦的其实是依赖传递的隐式行为:一个子项目改了 target_compile_options(),可能悄无声息地污染主项目编译选项。这种事查不到日志,只能靠 cmake --build . --verbose 看实际调用的命令行。










