使用FetchContent_Declare引入共享库时DLL未输出到父项目目录如何解决
你的Repo A(project_a)的顶层CMakeLists.txt(也就是标注为CMakeLists.txt : 2的文件)硬编码了全局输出目录:
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/bin-etc") set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/lib") set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/bin")
当它通过FetchContent被作为子项目引入到Repo B中构建时,CMAKE_CURRENT_SOURCE_DIR指向的是拉取后的子项目源码路径./output/_deps/project_a-src,因此DLL会生成到该路径下的bin目录,不会继承Repo B的输出路径配置。
优先修改Repo A的顶层CMakeLists.txt,只有当Repo A作为顶层独立项目构建时,才设置专属的输出目录等全局配置,如果是作为依赖被其他项目引入,就沿用父项目的全局配置:
# 修改CMakeLists.txt : 2的全局配置部分 cmake_minimum_required(VERSION 3.16) set_property(GLOBAL PROPERTY USE_FOLDERS ON) set(CMAKE_SYSTEM_VERSION 10.0.19041.0) # 新增判断:仅当前项目为顶层项目时设置输出目录 if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME) set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/bin-etc") set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/lib") set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/bin") # 类似C++标准、全局编译选项这类配置也建议放在这个判断里,避免污染父项目配置 set(CMAKE_CXX_STANDARD 23) set(CMAKE_CXX_STANDARD_REQUIRED ON) endif() project(project_a LANGUAGES CXX) add_subdirectory(src)
修改后重新构建Repo B,project_a的DLL会自动生成到Repo B设置的./bin目录下,无需额外配置。
如果没有权限修改Repo A的CMake配置,可以在Repo B调用FetchContent_MakeAvailable之前,手动覆盖project_a的输出目录配置:
# 修改CMakeLists.txt : 3的FetchContent部分 include(FetchContent) FetchContent_Declare(project_a GIT_REPOSITORY <REPO LINK> GIT_TAG master) # 新增:覆盖子项目的输出目录为当前父项目的bin目录 set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/../bin" CACHE PATH "" FORCE) set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/../lib" CACHE PATH "" FORCE) set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/../bin-etc" CACHE PATH "" FORCE) FetchContent_MakeAvailable(project_a)
注意构建完成后如果有其他子项目需要单独配置输出目录,记得改回对应值。
这类模块化设计是否需要更换实现方案?
FetchContent本身是CMake原生支持的、非常适合中小项目模块化管理的方案,相比git submodule不需要用户手动初始化子模块,相比vcpkg/conan这类包管理器没有额外的学习和配置成本。只要所有子模块的CMake都遵循「仅顶层项目设置全局配置」的规范,完全可以长期使用。如果是大型项目依赖非常复杂,再考虑切换到vcpkg/conan这类包管理方案。手动添加copy命令是否是合适的解决方案?
是下策,不到万不得已不建议使用:手动拷贝需要兼容多配置生成器(比如Visual Studio会自动生成Debug/Release子目录)的路径,还需要处理增量构建时的拷贝触发逻辑,维护成本很高。只有当你既不能修改子项目代码,也不方便覆盖子项目配置时,才考虑用add_custom_command加拷贝逻辑作为临时解决方案。
内容的提问来源于stack exchange,提问作者Marcin Poloczek

