CMake:多库包导出中的依赖项管理

CMake: dependency management in a multi-library package export

本文关键字:依赖 管理 包导出 CMake      更新时间:2023-10-16

我有一个名为MYLIBS的包,由两个库lib1lib2组成,我想通过包的配置文件导出它们。项目结构如下:

├── Lib1
│   ├── CMakeLists.txt
│   ├── lib1-class.cpp
│   └── lib1-class.h
├── lib2
│   └── CMakeLists.txt
│   ├── lib2-class.cpp
│   ├── lib2-class.h
├── cmake
│   └── LIBSConfig.cmake.in
├── CMakeLists.txt

lib2 中,我有:

add_library(lib2
        STATIC
        ${SOURCE_FILES}
        )
target_include_directories(lib2 PRIVATE /path/to/lib1)
target_link_libraries(lib2 PUBLIC lib1)
add_dependencies(lib2 lib1)
install(
        TARGETS
        lib2
        DESTINATION
        lib/MYLIBS/lib2
        EXPORT
        lib2Exports
)
install(
        EXPORT
        lib2Exports
        DESTINATION
        lib/MYLIBS/lib2
)
与 lib1

相同,只是 lib1 没有add_dependencies()target_include/link()因为它没有。

在我的配置文件模板中,我有:

@PACKAGE_INIT@
## PROJECT_LIBRARIES is filled-in during the package build. in this case : lib1,lib2
set(@PROJECT_NAME@_LIBRARIES @PROJECT_LIBRARIES@)
## The public variables to be used by the client project:
#PROJECT_NAME_INCLUDE_DIRS is all the include paths
#PROJECT_NAME_LIBRARIES is the name of all the libraries
unset(@PROJECT_NAME@_INCLUDE_DIRS)
foreach(INCLUDE_DIR ${INCLUDE_DIRS})
    set_and_check(@PROJECT_NAME@_INCLUDE_DIR ${INCLUDE_DIR})
    list(APPEND @PROJECT_NAME@_INCLUDE_DIRS ${@PROJECT_NAME@_INCLUDE_DIR})
endforeach()
## PACKAGE_PACKAGE_DIRNAME_include is filled-in during the package build
foreach(lib ${@PROJECT_NAME@_LIBRARIES})
    list(APPEND INCLUDE_DIRS @PACKAGE_PACKAGE_DIRNAME_include@/${lib})
endforeach(lib)
# Looks up the information about the exported targets in this package
foreach(lib ${@PROJECT_NAME@_LIBRARIES})
    if(NOT TARGET ${lib})
        include(@PACKAGE_PACKAGE_DIRNAME_lib@/${lib}/${lib}Exports.cmake)
    endif()
endforeach(lib)

因此,我逐个浏览库的导出文件并包含它们。问题是我必须以正确的顺序执行此操作,即首先是lib1,然后是lib2,否则我在读取配置文件时出现错误 FindPackage().

我不太确定传递依赖项将如何工作 tbh。由于这些库是从同一个导出文件中include()的,考虑到我们知道依赖项的导出文件将在系统上的位置,有没有办法告诉 CMake 配置文件或 lib2 导出文件中的依赖项?

我可以看到target_link_libraries()有一个公共选项。我应该如何使用它?会有什么帮助吗?

首先,您可以删除add_dependencies行。请参阅target_link_libraries和add_dependencies

第二,你有

target_include_directories(lib2 PRIVATE /path/to/lib1)

但这不应该是必需的。相反,请将其删除,并将其添加到lib1

target_include_directories(lib1 PUBLIC /path/to/lib1)

不过,这些只是清理。

您没有发布错误,并且您的帖子中缺少许多其他重要信息,所以我做了一些猜测。

我想错误类似于

The following imported targets are referenced, but are missing: lib2

您将lib1lib2导出到两个单独的"导出集"中 - lib1Exportslib2Exports 中。将它们放在一个"导出集"中可以解决问题,并且是最简单的方法,至少在双目标示例中是这样。

我想你知道这一点,你没有这样做,因为你的构建系统的规模大于两个目标。但是,这直接导致了您的问题 - 这意味着您必须管理"导出集"之间的订单依赖关系。

这与目标之间的依赖关系无关。"导出集"是具有独立依赖关系图的不同"单元"。CMake 不能帮助你管理它。您必须管理"导出集"之间的依赖关系。问题是您当前没有管理或表达这些依赖项。有关表达这些依赖项的选项,请参阅下文。

target_link_libraries(PUBLIC)对你没有帮助。在可传递使用要求中阅读有关它的信息。

如果您想与预处理器文件进行类比,您可能会看到您的选项。想想lib2_private.h#include lib1_private.h.alllibs.h需要按正确的顺序include这两者。由于_private标头是私有的,并且客户端将始终包含alllibs.h,因此这将起作用。在此方法中,您可以在一个位置管理总依赖项树。

另一种方法是创建包含以下内容的lib2_internal.h

#include "lib1_private.h"
#include "lib2_private.h" 

以及包含以下内容的lib1_internal.h

#include "lib1_private.h"

在此方法中,您可以管理靠近其依赖项的依赖项,因此您将有多个位置指定总依赖项树的子集。alllibs.h可以使用

#include "lib1_internal.h"
#include "lib2_internal.h" 

#include "lib2_internal.h"
#include "lib1_internal.h" 

顺序无关紧要。

带有循环的配置文件是alllibs.h - 它是客户端包含的唯一文件。你能完全在那里管理订单吗?是的,如果您可以在@PROJECT_NAME@_LIBRARIES变量中管理订单。顺便说一句,您可能应该将其称为@PROJECT_NAME@_EXPORT_SETS。如果你不明白为什么,再看看我上面所说的它是一个不同的"单位"。

没有提供太多信息,但我想您正在用多个填充它

list(APPEND MYPROJ_EXPORT_SETS fooExports)

调用,也许在某个宏中。因此,该顺序不容易维护,因为它将作为单个set()调用。

因此,表示"导出集"依赖项的选项是:

  1. 在配置文件中管理它们 - 将循环替换为硬编码的有序列表
  2. 在填充MYPROJ_EXPORT_SETS变量的位置添加更多变量以表示导出集的依赖项,并将配置文件中的循环替换为考虑这些依赖项的更复杂的内容。
  3. 与 (2) 相同,但生成中间文件,并且不关心配置文件中的包含顺序。

(1)可能是最有意义的,但你可能也必须退后一步,更努力地思考你正在创建的抽象/包装器,这些抽象/包装器将你带到了这里。