CMake如何配置多目录C++项目库与程序的依赖关系
现代CMake多模块动态库依赖配置方案
核心逻辑:所有依赖关系通过目标(target)绑定,头文件路径、链接属性、编译选项随依赖自动传递,不需要为可执行文件重复配置依赖库的头文件搜索路径。
先匹配你的项目结构
你当前的目录结构是标准的多模块布局,对应配置逻辑完全通用:
项目根目录/ ├─ CMakeLists.txt # 根配置文件 ├─ libA/ │ ├─ CMakeLists.txt │ ├─ include/ # libA对外暴露的公共头文件 │ └─ src/ # libA内部源码 ├─ libB/ │ ├─ CMakeLists.txt │ ├─ include/ │ └─ src/ ├─ program1/ │ ├─ CMakeLists.txt │ ├─ include/ │ └─ src/ ├─ program2/ # 目录结构和program1完全一致 └─ program3/ # 目录结构和program1完全一致
分步配置说明
1. 根目录CMakeLists.txt
重点解决Windows下dll找不到的问题,统一输出目录,按依赖顺序添加子模块:
# CMake版本建议不低于3.16,对依赖传递、生成器表达式支持完善 cmake_minimum_required(VERSION 3.16) project(MyCppProject CXX) # 全局输出目录配置:所有dll、exe统一输出到build/bin目录,避免运行时找不到dll set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) # 新手可选配置:自动导出Windows dll符号,不用手写__declspec(dllexport/dllimport)宏 set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON) # 按依赖顺序添加子目录,被依赖的模块放在前面 add_subdirectory(libA) add_subdirectory(libB) add_subdirectory(program1) add_subdirectory(program2) add_subdirectory(program3)
2. libA子目录CMakeLists.txt
把公共头文件目录标记为PUBLIC属性,自动向下游传递:
# 自动收集src下的所有C++源码,不用手动一个个列 file(GLOB LIBA_SRC CONFIGURE_DEPENDS "src/*.cpp" "src/*.cc") # 定义动态库目标 add_library(libA SHARED ${LIBA_SRC}) # 配置头文件搜索路径 target_include_directories(libA PUBLIC # 生成器表达式,保证构建阶段路径正确,后续做安装配置也不会冲突 $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include> # 如果有仅libA内部使用的私有头文件,加在PRIVATE段,不会传递给依赖方 # PRIVATE # ${CMAKE_CURRENT_SOURCE_DIR}/src )
3. libB子目录CMakeLists.txt
直接链接依赖的libA,不需要重复配置libA的头文件路径:
file(GLOB LIBB_SRC CONFIGURE_DEPENDS "src/*.cpp" "src/*.cc") add_library(libB SHARED ${LIBB_SRC}) target_include_directories(libB PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include> ) # 链接libA,这里属性选PUBLIC:libB自己用libA,依赖libB的目标也会自动拿到libA的依赖 target_link_libraries(libB PUBLIC libA)
三个链接属性别搞混:
PRIVATE:依赖仅当前目标自己编译用,不会传递给下游PUBLIC:当前目标用,下游依赖当前目标时也会继承这个依赖INTERFACE:当前目标自己不用,仅下游使用(多用于头文件-only库)
如果libB的公共头文件没有包含任何libA的头,也可以把libA的链接属性改成PRIVATE,后续可执行程序同时链接libA和libB即可。
4. 可执行程序(program1/2/3)CMakeLists.txt
以program1为例,另外两个写法完全一致:
file(GLOB PROG1_SRC CONFIGURE_DEPENDS "src/*.cpp" "src/*.cc") add_executable(program1 ${PROG1_SRC}) # 程序自身的头文件路径设为PRIVATE即可,没有下游会依赖可执行文件 target_include_directories(program1 PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include) # 只需要链接libB,libA的头文件路径、链接配置会通过依赖自动传递过来,不需要重复配置 target_link_libraries(program1 PRIVATE libB)
避坑提示
- 不要用老版本CMake的全局命令
include_directories()、link_directories(),所有配置绑定到具体target上,避免全局污染导致的路径冲突、编译顺序问题 - Windows下运行程序提示找不到dll,优先检查dll是不是和exe在同一个目录,前面配置的统一输出目录就是为了解决这个问题,编译完成后直接到
build/bin目录下找所有生成的文件即可 - 不要手动给目标加平台相关的前缀、后缀,CMake会自动根据当前系统生成正确的文件名(Windows下是.lib/.dll,Linux下是.a/.so)
- 等流程跑通之后,可以关掉
CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS,自己写dll导出宏做符号控制,不影响现有依赖逻辑。
内容的提问来源于stack exchange,提问作者user19334409
相关产品推荐
相关产品推荐

