在ExternalProject_Add中如何处理硬依赖?

编程语言 2026-07-09

我的主项目中有两个外部项目(X和 Y)。除了主项目本身依赖这两个之外,Y依赖X

在构建阶段发生的失败(在我的案例中 make)来自Y 内部的 find_program(),它需要一个X 正在生成的二进制文件。对该调用的结果被存储在一个变量中,如果在项目的配置阶段没有找到该二进制文件,该变量将被设为“空”,从而导致致命错误。

我尝试使用 DEPENDS 属性来自 ExternalProject_Add()

ExternalProject_Add(X
  ...
)

ExternalProject_Add(Y
  DEPENDS X
  ...
)

但我仍然收到由 find_program() 的结果触发的错误信息。

由于对 find_program() 的调用不能切换/开关,试图通过 CMAKE_ARGS 使用传给CMake的参数来传递存放二进制路径的变量也不起作用。

如果我修改Y 的 CMakeLists.txt(例如通过应用补丁)我可以修复,但我在想,如果出于某些原因无法/不希望改变依赖关系,那么如何解决这个问题呢?

cmake_minimum_required(VERSION 3.22)
project(TEST)


# Global settings
set(GLOBAL_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)
set(CMAKE_INSTALL_PREFIX ${PROJECT_BINARY_DIR}/install)
set(DEPS_DIR ${CMAKE_SOURCE_DIR}/deps)

# Sets global output directory for single configuration (GCC)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${GLOBAL_OUTPUT_PATH})
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${GLOBAL_OUTPUT_PATH})
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${GLOBAL_OUTPUT_PATH})

# Sets global output directory for sub-configurations (msvc, mingw)
foreach(OUTPUTCONFIG ${CMAKE_CONFIGURATION_TYPES})
    string(TOUPPER ${OUTPUTCONFIG} OUTPUTCONFIG)
    set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_${OUTPUTCONFIG} ${GLOBAL_OUTPUT_PATH})
    set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_${OUTPUTCONFIG} ${GLOBAL_OUTPUT_PATH})
    set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_${OUTPUTCONFIG} ${GLOBAL_OUTPUT_PATH})
endforeach(OUTPUTCONFIG CMAKE_CONFIGURATION_TYPES)

include(ExternalProject)
ExternalProject_Add(sqlite3
  GIT_REPOSITORY "https://github.com/sqlite/sqlite.git"
  GIT_TAG "master"

  UPDATE_COMMAND ""
  PATCH_COMMAND ""

  SOURCE_DIR "${DEPS_DIR}/sqlite"
  CONFIGURE_COMMAND ${DEPS_DIR}/sqlite/configure --all --prefix=${CMAKE_BINARY_DIR} CFLAGS="-Os" CXXFLAGS="-Os" SQLITE_THREADSAFE=2 SQLITE_OMIT_COMPLETE=1 SQLITE_OMIT_CHECK=1 SQLITE_OMIT_DEPRECATED=1 BUILD_SHARED_LIBS=0 --disable-debug
  BUILD_COMMAND "${MAKE}"

  TEST_COMMAND ""
)

ExternalProject_Add(proj
  DEPENDS sqlite3

  GIT_REPOSITORY "https://github.com/OSGeo/PROJ.git"
  GIT_TAG "master"

  UPDATE_COMMAND ""
  PATCH_COMMAND ""

  SOURCE_DIR "${DEPS_DIR}/proj"
  CMAKE_ARGS "-DBUILD_SHARED_LIBS=0 -DBUILD_TESTING=0 -DBUILD_APPS=0 -DSQLite3_LIBRARY_DIR=${CMAKE_LIBRARY_OUTPUT_DIRECTORY} -DEXE_SQLITE3=${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/sqlite3"
  BUILD_COMMAND "${MAKE}"
)

解决方案

在翻阅CMake的文档时,我发现 find_program() 会受到 [CMAKE_PROGRAM_PATH] 的影响(以及另外两个变量)。解决办法是将 DEPENDESCMAKE_PROGRAM_PATH 组合起来。

正如在我的问题中所提到的 DEPENDS,它确保外部项目相关阶段(配置、构建、测试、安装)的执行顺序。这确保X(SQLite3)可以生成Y 需要的二进制文件(此处为 sqlite3),从而生成所需的 proj.db

通过在Y 的配置阶段将由X 生成的二进制路径传递给Y,使用 CMAKE_PROGRAM_PATHfind_program() 调用将在常见位置进行查找(Unix/Linux与 Windows不同),以及 CMAKE_PROGRAM_PATH 提供的位置(请注意,可以添加多条路径,但分隔符不同——在Unix/Linux上是 :,在Windows上是 ;)。

ExternalProject_Add(proj
  DEPENDS sqlite3 # <----- !!!IMPORTANT!!!

  GIT_REPOSITORY "https://github.com/OSGeo/PROJ.git"
  GIT_TAG "master"
  GIT_PROGRESS 1

  UPDATE_COMMAND ""
  PATCH_COMMAND ""

  SOURCE_DIR "${DEPS_DIR}/proj"
  CMAKE_CACHE_ARGS
    -DCMAKE_PROGRAM_PATH:PATH=${CMAKE_RUNTIME_OUTPUT_DIRECTORY} # <----- !!!IMPORTANT!!!
  CMAKE_ARGS 
    -DCMAKE_INSTALL_PREFIX=${PROJECT_BINARY_DIR}
    -DBUILD_SHARED_LIBS=0
    -DBUILD_TESTING=0
    -DBUILD_APPS=0

  ...
)

首先下载、配置并构建X 项目。接下来处理Y 项目。在配置阶段,由于分配给 CMAKE_PROGRAM_PATH 的值,find_program() 找到了来自X 的二进制文件,Y的 CMake配置也生成了成功构建阶段所需的所有文件。对于PROJ,我还必须运行安装步骤,这意味着我必须覆盖 CMAKE_INSTALL_PREFIX,因为默认需要管理员权限,且在Unix/Linux上需要访问到 /usr,而且我也不想真的安装任何东西。

我的CMake确实很混乱,因此不要照着原样照抄,但它确实展示了如何处理这样一个对顺序和中间输出有要求的硬依赖。请注意,这只处理 find_program() 出问题的情况。对于 find_package() 等,有不同的做法。此外,这里第二个项目使用的是CMake。我不知道它在Autotools等环境下会如何工作。

站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章