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

CMake:如何让目录B的配置依赖目录A的构建或查找结果

CMake 外部项目构建结果的条件配置问题

问题概述

我的场景本质是CMake可选依赖问题,但因使用ExternalProject_Add而更复杂:

  • 外部项目目录“A”的CMakeLists.txt逻辑:
    • 先执行find_package(A-PKG ...),若A-PKG_FOUND为真,提取路径并定义相关变量;
    • 若未找到,通过ExternalProject_Add(A-PRJ ...)下载、构建并安装该项目,再根据构建结果定义变量。
  • 需求:目录“B”需根据“A”的查找或构建结果调整配置——具体是根据“A”的构建产出,设置target_compile_definitions和target_include_directories。

核心矛盾

CMake的依赖仅控制构建顺序,不影响配置顺序:所有配置步骤均在构建前完成,导致“B”配置时“A”尚未生成产物,等“A”构建完成后,“B”的配置已固化,无法适配新的产物信息。

痛点需求

不想让“B”强制依赖“A”的成功构建/查找,希望实现无侵入逻辑:若“A”构建失败或未被包含,无需手动修改“B”(及“C”“D”等依赖项)的配置,“B”仍能正常构建,仅调整构建方式。

已尝试无效方案

  • 为ExternalProject_Add(A-PRJ ...)设置BUILD_BYPRODUCTS、STEP_TARGETS为build/install;添加add_custom_target(A-TARG ALL DEPENDS A-PRJ-build ...);多种依赖设置均仅能控制构建顺序,无法让“B”的配置依赖“A”的构建。
  • 设置A_FOUND缓存变量,但该变量在“A”配置阶段就被赋值,此时实际产物尚未生成,导致“B”尝试包含不存在的头文件而构建失败;若给“B”添加硬依赖“A”的构建,虽能保证头文件存在,但违反了“B”无需依赖“A”成功的需求。

临时 workaround

目前只能在首次配置并构建后,修改“B”的CMakeLists.txt触发重配置,再重新构建,效率低下。

可行解决方案

方案1:通过configure_file生成动态配置文件

  1. 在“A”的CMakeLists.txt末尾,统一生成包含配置信息的CMake脚本,记录A_FOUND、A_INCLUDE_DIRS、A_DEFINITIONS等变量:
    if(A-PKG_FOUND)
      set(A_FOUND ON)
      set(A_INCLUDE_DIRS "${A-PKG_INCLUDE_DIRS}")
      set(A_DEFINITIONS "${A-PKG_DEFINITIONS}")
    else()
      set(A_INSTALL_DIR "${CMAKE_CURRENT_BINARY_DIR}/A-install")
      set(A_FOUND ON)
      set(A_INCLUDE_DIRS "${A_INSTALL_DIR}/include")
      set(A_DEFINITIONS "-DUSE_BUILT_A=1")
    endif()
    
    configure_file(
      "${CMAKE_CURRENT_SOURCE_DIR}/A_config.cmake.in"
      "${CMAKE_CURRENT_BINARY_DIR}/A_config.cmake"
      @ONLY
    )
    
  2. 在“B”的CMakeLists.txt中,先加载初始配置,再添加自定义命令依赖“A”的安装目标,确保配置文件更新后再应用到“B”的目标:
    # 初始加载A的配置(可能为空)
    if(EXISTS "${A_BINARY_DIR}/A_config.cmake")
      include("${A_BINARY_DIR}/A_config.cmake")
    else()
      set(A_FOUND OFF)
    endif()
    
    # 生成B的配置头文件模板
    configure_file(
      "${CMAKE_CURRENT_SOURCE_DIR}/B_config.h.in"
      "${CMAKE_CURRENT_BINARY_DIR}/B_config.h.tmp"
      @ONLY
    )
    
    # 自定义命令:等待A构建完成后同步配置
    add_custom_command(
      OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/B_config.h"
      COMMAND ${CMAKE_COMMAND} -E copy_if_different
        "${CMAKE_CURRENT_BINARY_DIR}/B_config.h.tmp"
        "${CMAKE_CURRENT_BINARY_DIR}/B_config.h"
      DEPENDS "${A_BINARY_DIR}/A_config.cmake" A-PRJ-install
    )
    
    # 绑定配置文件到B的目标
    add_executable(B main.cpp)
    target_include_directories(B PRIVATE "${CMAKE_CURRENT_BINARY_DIR}")
    if(A_FOUND)
      target_include_directories(B PRIVATE "${A_INCLUDE_DIRS}")
      target_compile_definitions(B PRIVATE "${A_DEFINITIONS}")
    endif()
    add_dependencies(B "${CMAKE_CURRENT_BINARY_DIR}/B_config.h")
    

方案2:在“A”的安装阶段触发全局重配置

在“A”的ExternalProject_Add的INSTALL_COMMAND中添加触发CMake重配置的命令,让“A”构建完成后自动重新运行配置阶段,此时“B”就能获取到最新的产物信息:

ExternalProject_Add(A-PRJ
  # 其他参数(如SOURCE_DIR、BINARY_DIR等)...
  INSTALL_COMMAND ${CMAKE_MAKE_PROGRAM} install
    COMMAND ${CMAKE_COMMAND} "${CMAKE_SOURCE_DIR}"
    WORKING_DIRECTORY "${CMAKE_BINARY_DIR}"
)

注意:该方式会增加配置阶段的运行次数,可能延长总构建时间,但能确保配置的准确性。

方案3:通过自定义脚本延迟查找“A”的产物

如果“A”安装后会生成CMake包配置文件(如A-PKGConfig.cmake),可以通过自定义脚本在“A”安装完成后再执行find_package,将结果写入文件供“B”读取:

  1. 编写find_A.cmake脚本:
    set(A_INSTALL_DIR "${CMAKE_ARGV3}")
    find_package(A-PKG PATHS "${A_INSTALL_DIR}" NO_DEFAULT_PATH)
    if(A-PKG_FOUND)
      file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/A_FOUND.txt" "ON")
      file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/A_INCLUDES.txt" "${A-PKG_INCLUDE_DIRS}")
      file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/A_DEFS.txt" "${A-PKG_DEFINITIONS}")
    else()
      file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/A_FOUND.txt" "OFF")
    endif()
    
  2. 在“B”的CMakeLists.txt中使用:
    set(A_INSTALL_DIR "${CMAKE_CURRENT_BINARY_DIR}/../A/A-install")
    add_custom_target(WaitForA
      COMMAND ${CMAKE_COMMAND} -P "${CMAKE_CURRENT_SOURCE_DIR}/find_A.cmake" "${A_INSTALL_DIR}"
      DEPENDS A-PRJ-install
    )
    
    add_executable(B main.cpp)
    add_dependencies(B WaitForA)
    # 读取文件内容并应用到目标配置
    target_compile_definitions(B PRIVATE $<$<FILE_EXISTS:${CMAKE_CURRENT_BINARY_DIR}/A_DEFS.txt>:$(shell cat ${CMAKE_CURRENT_BINARY_DIR}/A_DEFS.txt)>)
    target_include_directories(B PRIVATE $<$<FILE_EXISTS:${CMAKE_CURRENT_BINARY_DIR}/A_INCLUDES.txt>:$(shell cat ${CMAKE_CURRENT_BINARY_DIR}/A_INCLUDES.txt)>)
    

内容的提问来源于stack exchange,提问作者Theodore Hall

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 11:54:56