13

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

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)(只要通过 CLI 设置指向已安装的 CMake 包配置文件的Foo的正确目录路径)。

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

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

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

到目前为止一切都很好,但是等等......我必须再次明确设置每个依赖项名称及其版本?在声明依赖项时,我已经这样做了CMakeLists.txt!如何使用该方法创建可重用的通用包配置文件?目前我无法确定该问题的任何解决方案,除了CMakeLists.txtPackageConfig.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。

4

1 回答 1

2

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

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

@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 版本中,使用未记录的内部属性_CMAKE_<PKG>_TRANSITIVE_DEPENDENCY(就像我在这里所做的那样)过滤它可能会在没有警告或策略的情况下中断。它还依赖于包调用find_dependency而不是find_package.
  2. 通过 Find 模块(而不是配置文件)加载的包可能没有版本集。损坏的配置文件(即没有文件的配置PackageConfigVersion.cmake文件)也可能缺少版本信息。

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

于 2021-08-18T20:16:35.700 回答