依赖项跟踪

rapids-cmake 最大的特性之一是它能够跟踪依赖项 (find_package, cpm),这使得项目能够轻松生成包含正确依赖项需求的 <project>-config.cmake 文件。在一个普通的 CMake 项目中,公共依赖项需要在两个地方记录:原始的 CMakeLists.txt 文件和生成的 <project>-config.cmake。这种双重事实来源增加了开发人员的负担,并增加了常见的错误来源。

rapids-cmake 旨在通过扩展现代 CMake 使用要求的概念来包含外部包,从而消除这种双重事实来源。这是通过在命令(例如 rapids_find_package()rapids_cpm_find())上使用 BUILD_EXPORT_SET(映射到 <BUILD_INTERFACE>)和 INSTALL_EXPORT_SET(映射到 <INSTALL_INTERFACE>)关键字来实现的。让我们通过一个例子来看看这些是如何结合起来,使项目依赖项跟踪变得简单的。

rapids_find_package(Threads REQUIRED
  BUILD_EXPORT_SET example-targets
  INSTALL_EXPORT_SET example-targets
  )
rapids_cpm_find(nlohmann_json 3.9.1
  BUILD_EXPORT_SET example-targets
  )

add_library(example src.cpp)
target_link_libraries(example
  PUBLIC Threads::Threads
    $<BUILD_INTERFACE:nlohmann_json::nlohmann_json>
  )
install(TARGETS example
  DESTINATION lib
  EXPORT example-targets
  )

set(doc_string [=[Provide targets for the example library.]=])
rapids_export(INSTALL example
  EXPORT_SET example-targets
  NAMESPACE example::
  DOCUMENTATION doc_string
  )
rapids_export(BUILD example
  EXPORT_SET example-targets
  NAMESPACE example::
  DOCUMENTATION doc_string
  )

跟踪 find_package

rapids_find_package(Threads REQUIRED
  BUILD_EXPORT_SET example-targets
  INSTALL_EXPORT_SET example-targets
  )

rapids_find_package(<PackageName>) 命令结合了 find_package 命令和依赖项跟踪功能。此示例记录了 Threads 包对于安装和构建配置的导出集 example-targets 都是必需的。

这意味着当调用 rapids_export() 时,生成的 example-config.cmake 文件将包含调用 find_dependency(Threads),从而消除了开发人员维护双重事实来源的需求。

rapids_find_package(<PackageName>) 命令也正确支持非必需的查找。在这些情况下,只有当下层的 find_package 命令成功时,rapids-cmake 才会记录该依赖项。

对于一些 CMake 没有 Find<Package> 模块的依赖项,项目通常需要自定义。在这些情况下,项目会有需要使用的自定义 Find<Package> 模块,并将其安装供消费者使用。Rapids-cmake 尝试通过 rapids_find_generate_module()rapids_export_package() 命令来帮助项目简化此过程。

rapids_find_generate_module() 允许项目自动生成 Find<Package> 模块,并通过 BUILD_EXPORT_SET`INSTALL_EXPORT_SET` 参数编码,指明何时生成的模块也应该被安装并添加到 CMAKE_MODULE_PATH,以便消费者可以使用它。

如果你已经编写了一个现有的 Find<Package> 模块,rapids_export_package() 简化了安装该模块并确保其成为供消费者使用的 CMAKE_MODULE_PATH 一部分的流程。

跟踪 CPM

rapids_cpm_find(nlohmann_json 3.9.1
  BUILD_EXPORT_SET example-targets
  )

rapids_cpm_find() 命令结合了 CPMFindPackage() 命令和依赖项跟踪,方式与 rapids_find_package() 非常相似。在此示例中,我们表示 nlohmann_json 仅由构建目录的 example-config 需要,而安装的 example-config 不需要。虽然这种模式很少见,但当项目存在一些消费者不需要但通过项目内部的使用要求(通过 $<BUILD_INTERFACE>)传播的依赖项时,就会出现这种情况。为什么还要使用构建目录的 config 文件呢?最常见的原因是开发人员需要在快速反馈循环中处理多个依赖的项目。在这种情况下,此工作流程避免了每次需要在依赖项目中测试更改时重新安装项目。

当与 BUILD_EXPORT_SET 一起使用时,rapids_cpm_find() 将生成一个 CPMFindPackage(<PackageName> ...) 调用;当与 INSTALL_EXPORT_SET 一起使用时,它将生成一个 find_dependency(<PackageName> ...) 调用。这背后的理论是,大多数包目前没有很好的构建 config.cmake 支持,因此最好回退到 cpm,而所有 CMake 包都应该有安装规则。如果某个 CPM 包不符合此情况,你可以转而使用 rapids_export_cpm()rapids_export_package() 来指定正确的生成命令,并放弃使用 [BUILD|INSTALL]_EXPORT_SET

生成 example-config.cmake

set(doc_string [=[Provide targets for the example library.]=])
rapids_export(INSTALL example
  EXPORT_SET example-targets
  NAMESPACE example::
  DOCUMENTATION doc_string
  )
rapids_export(BUILD example
  EXPORT_SET example-targets
  NAMESPACE example::
  DOCUMENTATION doc_string
  )

在使用 rapids-cmake 之前,如果一个项目想生成一个配置模块,他们会按照 cmake-packages 文档 中的示例,使用 install(EXPORT)export(EXPORT)write_basic_package_version_file 以及自定义的 config.cmake.in 文件。

rapids_export() 的目标是取代所有样板代码,提供一个易于使用的函数,同时嵌入由 BUILD_EXPORT_SETINSTALL_EXPORT_SET 收集的必需依赖项调用。

rapids_export() 使用 CMake 最佳实践来生成项目配置文件所需的所有组件。它负责生成正确的版本文件、查找依赖项以及创建良好 CMake 配置文件所需的所有其他样板代码。此外,由 rapids_export() 生成的文件完全独立,不依赖于 rapids-cmake