You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.29 18:03:15