CMake 包配置文件中的泛型"transitive behavior" (find_dependency)

Generic "transitive behavior" in CMake Package Configuration File (find_dependency)

本文关键字:find dependency behavior transitive 配置文件 泛型 CMake      更新时间:2023-10-16

我正在尝试增强我的CMake约定配置。 目前框架。我的每个C++组件(即 CMake 项目)都是通过该框架构建的,并且该框架已经能够使用configure_package_config_file()命令创建 CMake 包配置文件。

框架使用以下(最小)模板文件PackageConfig.cmake.in(v1)。

@PACKAGE_INIT@
include("${CMAKE_CURRENT_LIST_DIR}/@targets_export_name@.cmake")
check_required_components("@PROJECT_NAME@")

如果使用该方法构建和安装组件Foo,则一切正常 由另一个组件Bar使用find_package(<package> CONFIG)命令(只要正确的目录路径指向 已安装的Foo的 CMake 软件包配置文件是通过 CLI 设置的)。

但是(当然)如果A本身有一个或多个依赖项,就会出现问题。使用当前的方法,B必须find_package()A本身。这意味着传递依赖项当前不会报告给需要依赖项的组件。显然这不是我想要的 实现。

经过一些谷歌搜索,我了解了find_dependency()命令,该命令是为了解决提到的问题而创建的:

它被设计用于包配置文件 (<package>Config.cmake)。 find_dependency转发传递给原始find_package()调用的QUIETREQUIRED的正确参数。指定的任何其他参数都将转发到find_package()

到目前为止一切顺利,但是等等...我必须再次显式设置每个依赖项名称及其版本?在CMakeLists.txt中声明依赖项时,我已经这样做了!如何创建可重用的通用包 使用这种方法的配置文件?我目前无法确定该问题的任何解决方案,除了在CMakeLists.txt中明确列出所有依赖项(及其版本)并将该列表传递给PackageConfig.cmake.in

示例:未经测试的PackageConfig.cmake.in(v2):

@PACKAGE_INIT@
include(CMakeFindDependencyMacro)
# TODO(wolters): How-to implement this with a generic approach? Would the
# following work? Does a better solution do the problem exist?
#
# 1. Add the following to `CMakeLists.txt`:
#    list(APPEND target_dependencies "Baz 1.2.3")
#    list(APPEND target_dependencies "Example 0.9.0")
# 2. "Pass" the variable `target_dependencies` to the
#    `configure_package_config_file` command.
# 3. Add the following code to the CMake package config file.
foreach(dependency "@target_dependencies@")
find_dependency(${dependency})
endforeach()
include("${CMAKE_CURRENT_LIST_DIR}/@targets_export_name@.cmake")
check_required_components("@PROJECT_NAME@")

虽然这感觉很奇怪,而且我(还)不知道它是否有效(但它应该, 理论上)。

所以我的问题是:我如何才能普遍地实现传递行为 CMake 包配置文件。

最后但并非最不重要的一点是:我使用的是最新的稳定 CMake 版本 3.9.4。

这太长了,无法发表评论,所以我发布作为答案...

作为第一次切割,您可以在名为PACKAGE_DEPENDENCIES的变量中调用configure_package_config_file之前计算已加载包的列表。首先像这样调整模板:

@PACKAGE_INIT@
include(CMakeFindDependencyMacro)
@PACKAGE_DEPENDENCIES@
include("${CMAKE_CURRENT_LIST_DIR}/@targets_export_name@.cmake")
check_required_components("@PROJECT_NAME@")

然后,要计算PACKAGE_DEPENDENCIES,您可以使用如下所示的代码段:

set(PACKAGE_DEPENDENCIES "")
get_property(packages GLOBAL PROPERTY PACKAGES_FOUND)
foreach (pkg IN LISTS packages)
get_property(is_transitive GLOBAL PROPERTY _CMAKE_${pkg}_TRANSITIVE_DEPENDENCY)
if (is_transitive)
continue()
elseif (${pkg}_VERSION)
string(APPEND PACKAGE_DEPENDENCIES "find_dependency(${pkg} ${${pkg}_VERSION})n")
else ()
string(APPEND PACKAGE_DEPENDENCIES "find_dependency(${pkg})n")
endif()
endforeach ()

然后,您就可以致电configure_package_config_file了。

但是,这种方法有很多方面很脆弱:

  1. 全局PACKAGES_FOUND属性包括所有包,甚至是通过find_dependency递归找到的包,这很可能不是您想要的。使用未记录的内部属性_CMAKE_<PKG>_TRANSITIVE_DEPENDENCY(如我在此处所做的那样)对其进行过滤可能会在较新的 CMake 版本中中断,而不会发出警告或策略。它还依赖于调用find_dependency而不是find_package的包。
  2. 通过查找模块(而不是配置文件)加载的包可能没有版本集。损坏的配置文件(即没有PackageConfigVersion.cmake文件的配置文件)也可能缺少版本信息。

(1) 的一种解决方法可能是仅将此行为应用于使用约定重于配置框架的包(例如,通过设置特殊的全局属性)。与(2)相似的故事。