修改输出目录后CMake FetchContent拉取依赖及工具链报错问题
多仓库CMake项目按工具链划分输出目录的解决方案
问题背景
配置多仓库CMake项目时遇到以下场景:
- Project A为图形应用,依赖作为设备控制器/硬件抽象层(HAL)的Project B,预期通过
FetchContent拉取Project B源码及工具链文件后,使用该工具链完成配置。 - 命令行直接添加
--toolchain参数执行时流程正常,但使用CMakePresets或将输出目录设为带工具链名称的路径(如${sourceDir}/out/ProjectB_SDK)时,会报错找不到工具链文件——此时Project B尚未被克隆,工具链文件确实不存在。 - 输出目录设为简单的
${sourceDir}/out/时,无论用预设还是命令行都能正常工作,但需求是按工具链名称划分输出目录,生成对应不同工具链的项目。
问题根源
CMake在启动配置时,会优先检查预设或命令行中指定的CMAKE_TOOLCHAIN_FILE是否存在,而FetchContent_MakeAvailable是在CMakeLists.txt执行过程中才会拉取依赖。当输出目录包含工具链名称时,CMake没有缓存可复用,必须从头开始,导致工具链检查发生在Project B拉取之前,触发文件不存在的错误。
解决方案
方法一:调整CMake流程,先拉取依赖再加载工具链
核心思路是先完成Project B的拉取,再设置工具链文件并重新触发配置,绕过CMake启动时的工具链检查:
- 修改Project A顶层CMakeLists.txt
cmake_minimum_required(VERSION 3.23.0 FATAL_ERROR) # 第一步:优先拉取Project B,此时不加载工具链 include(FetchContent) set(FETCHCONTENT_QUIET FALSE) FetchContent_Declare( ProjectB GIT_PROGRESS TRUE GIT_REPOSITORY git@ssh_git_link:link/to.git GIT_TAG origin/development_branch SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/ProjectB BINARY_DIR ${CMAKE_BINARY_DIR}/ProjectB ) FetchContent_MakeAvailable(ProjectB) # 第二步:设置工具链文件并检查是否已加载 set(TOOLCHAIN_PATH "${CMAKE_CURRENT_SOURCE_DIR}/ProjectB/ProjectB_SDK-toolchain.cmake") set(CMAKE_TOOLCHAIN_FILE "${TOOLCHAIN_PATH}" CACHE PATH "Toolchain file from ProjectB" FORCE) if(NOT CMAKE_TOOLCHAIN_FILE_LOADED) # 加载工具链文件 include(${CMAKE_TOOLCHAIN_FILE}) set(CMAKE_TOOLCHAIN_FILE_LOADED TRUE CACHE INTERNAL "Flag to indicate toolchain loaded") # 重新触发CMake配置,确保工具链生效 configure_file(${CMAKE_CURRENT_LIST_FILE} ${CMAKE_CURRENT_BINARY_DIR}/CMakeLists.txt COPYONLY) execute_process( COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" -S ${CMAKE_CURRENT_BINARY_DIR} -B ${CMAKE_BINARY_DIR} RESULT_VARIABLE ret_code ) if(NOT ret_code EQUAL 0) message(FATAL_ERROR "Failed to reconfigure with toolchain file") endif() return() endif() # 后续正常配置流程 set(CMAKE_CONFIGURATION_TYPES "Debug" "Release" CACHE STRING "" FORCE) project(ColadaE2G)
- 修改CMakePresets.json
移除预设中的toolchainFile配置,改为由CMakeLists.txt自动加载:
{ "version": 4, "configurePresets": [ { "name": "ProjectB_SDK", "displayName": "ProjectB_SDK", "binaryDir": "${sourceDir}/out/ProjectB_SDK", "generator": "Visual Studio 9 2008" } ], "buildPresets": [ { "name": "ProjectB_SDK-Debug", "configuration": "Debug", "configurePreset": "ProjectB_SDK" }, { "name": "ProjectB_SDK-Release", "configuration": "Release", "configurePreset": "ProjectB_SDK" } ] }
方法二:使用ExternalProject替代FetchContent(更灵活的依赖管理)
如果FetchContent的流程限制过多,可以改用ExternalProject先完整拉取Project B,再加载工具链:
cmake_minimum_required(VERSION 3.23.0 FATAL_ERROR) include(ExternalProject) # 仅拉取Project B源码,跳过构建/安装步骤 ExternalProject_Add( ProjectB GIT_REPOSITORY git@ssh_git_link:link/to.git GIT_TAG origin/development_branch SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/ProjectB BINARY_DIR ${CMAKE_BINARY_DIR}/ProjectB CONFIGURE_COMMAND "" BUILD_COMMAND "" INSTALL_COMMAND "" TEST_COMMAND "" ) # 获取Project B源码目录,加载工具链文件 ExternalProject_Get_Property(ProjectB SOURCE_DIR) set(CMAKE_TOOLCHAIN_FILE "${SOURCE_DIR}/ProjectB_SDK-toolchain.cmake" CACHE PATH "Toolchain file from ProjectB" FORCE) include(${CMAKE_TOOLCHAIN_FILE}) # 后续正常配置 set(CMAKE_CONFIGURATION_TYPES "Debug" "Release" CACHE STRING "" FORCE) project(ColadaE2G) # 绑定依赖关系 add_dependencies(ColadaE2G ProjectB)
验证方案
执行以下命令测试配置:
cmake --preset ProjectB_SDK cmake --build --preset ProjectB_SDK-Debug
内容的提问来源于stack exchange,提问作者Daniel Beliavskij
相关产品推荐
相关产品推荐

