Cmake:依赖管理在多库包导出 [英] Cmake: dependency management in a multi-library package export

查看:533
本文介绍了Cmake:依赖管理在多库包导出的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个名为 MYLIBS 的包,其中包含两个库 lib1 lib2 ,我想通过配置文件导出。项目结构如下:

 ├──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 c $ c>和 target_include / link(),因为它没有一个。



,我有:

  @ PACKAGE_INIT @ 
## PROJECT_LIBRARIES是在软件包构建过程中填写的。在这种情况下:lib1,lib2
set(@ PROJECT_NAME @ _LIBRARIES @ PROJECT_LIBRARIES @)

##客户端项目使用的公共变量:
#PROJECT_NAME_INCLUDE_DIRS全部包含路径
#PROJECT_NAME_LIBRARIES是所有库的名称

unset(@ PROJECT_NAME @ _INCLUDE_DIRS)
foreach(INCLUDE_DIR $ {INCLUDE_DIRS})
set_and_check PROJECT_NAME @ _INCLUDE_DIR $ {INCLUDE_DIR})
清单(APPEND @ PROJECT_NAME @ _INCLUDE_DIRS $ {@ PROJECT_NAME @ _INCLUDE_DIR})
endforeach()

## PACKAGE_PACKAGE_DIRNAME_include程序包构建
foreach(lib $ {@ PROJECT_NAME @ _LIBRARIES})
list(APPEND INCLUDE_DIRS @PACKAGE_PACKAGE_DIRNAME_include @ / $ {lib})
endforeach(lib)
$ b b#查看此包中导出目标的信息
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() ed是否有一种方法告诉cmake关于配置文件中的依赖性或 lib2 ,因为我们知道依赖关系的导出文件在系统上的位置?我可以看到target_link_libraries()有一个PUBLIC选项。我应该如何使用它?

解决方案

首先,您可以删除 add_dependencies 行。请参见 http://stackoverflow.com/a/27305021/2428389



其次,您有

  target_include_directories(lib2 PRIVATE / path / to / lib1)

但不应该需要。相反,删除它,并将其添加到 lib1

  target_include_directories lib1 PUBLIC / path / to / lib1)

这些只是清理。



您没有发布错误,并且您的帖子中缺少很多其他重要信息,因此我会猜测。



猜测,错误是沿着

行的:

 引用导入目标,但缺少:lib2 

您导出 lib1 <两个独立的导出集 - lib1Exports 中的 。将它们放在一个导出集中将解决问题,并且是最简单的方法,至少在两个目标示例中。



猜测<强>你知道,你不是这样做,因为你的构建系统的规模大于两个目标。但是,这直接导致您的问题 - 这意味着您必须管理导出集之间的顺序依赖。



这是独立目标之间的依赖关系。 导出集是具有独立依赖图的不同单元。 CMake不帮助你管理它。你必须管理导出集之间的依赖关系。问题是,你目前没有管理或表示那些依赖。



target_link_libraries(PUBLIC)对您没有帮助。请在这里阅读: https: //cmake.org/cmake/help/v3.4/manual/cmake-buildsystem.7.html#transitive-usage-requirements



如果您想象一个类似于预处理器文件,你可能会看到你的选项。想想 lib2_private.h ,它不会 #include lib1_private.h alllibs.h 需要以正确的顺序 include 。因为 _private 头是私有的,因为客户端将总是包括 alllibs.h ,这将工作。在这种方法中,您在一个地方管理总依赖关系树。



另一种方法是创建 lib2_internal.h 其中包含

  #includelib1_private.h
#includelib2_private.h

lib1_internal.h ,其中包含

  #includelib1_private.h


$ b b

在这种方法中,您需要管理靠近它们的依赖关系的依赖关系,因此您可以有多个地方指定总依赖关系树的子集。 alllibs.h 可以使用

  #includelib1_internal.h 
#includelib2_internal.h

  #includelib2_internal.h
#includelib1_internal.h

并且顺序无所谓。



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



你没有提供多少信息,但我猜测您正在填充多个

 列表(APPEND MYPROJ_EXPORT_SETS fooExports)

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



表示导出集依赖性的选项是:


  1. 在Config文件中管理它们 - 用硬编码的有序列表替换循环

  2. 添加更多变量以表示导出集的依赖关系,无论您在何处填充 MYPROJ_EXPORT_SETS 变量,并将Config文件中的循环替换为更复杂的

  3. 与(2)相同,但生成中间文件,而不关心Config文件中的包含顺序。

(1)可能是最有意义的,但是你可能还要退后一步,更加仔细地考虑你在这里创建的抽象/包装器。 p>

I have a package called MYLIBS consisting of two libraries lib1 and lib2 which I want to export through the config file for the package. The project structure is as follows:

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

in lib2 I have:

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
)

The same as lib1 except that lib1 does not have the add_dependencies() and target_include/link() as it does not have one.

In my config file template, I have:

@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 info 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)

so I go through the export files for libraries one by one and include them. The problem is that I have to do that in the right order, ie. lib1 first and then lib2, otherwise I get an error when reading the config file by FindPackage(). Not really sure how the transitive dependencies would work tbh. since these libraries are include()ed from the same export file, is there a way of telling cmake about the dependencies in the config file or in the export file of lib2 considering that we know where the export files for the dependencies are going to be on the system? I can see target_link_libraries() has a PUBLIC option. How I am supposed to use that? would it be of any help?

解决方案

To begin with, you can remove the add_dependencies line. See http://stackoverflow.com/a/27305021/2428389

Second, you have

target_include_directories(lib2 PRIVATE /path/to/lib1)

but that should not be needed. Instead, remove it, and add this to lib1:

target_include_directories(lib1 PUBLIC /path/to/lib1)

Those are just clean-ups though.

You didn't post the error, and there is lots of other important information missing in your post, so I do some guessing.

I guess the error is something along the lines of

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

You export lib1 and lib2 in two separate 'export sets' - lib1Exports and lib2Exports. Putting them in one 'export set' would solve the problem and be the easiest way forward, at least in the two-target example.

I guess you know that and you are not doing it because the scale of your buildsystem is bigger than two targets. However, that leads directly to your problem - it means you must manage the order dependencies between 'export sets'.

This is independent of dependencies between targets. An 'export set' is a different 'unit' with an independent dependency graph. CMake doesn't help you to manage it. You have to manage the dependencies between 'export sets'. The problem is that you are not currently managing or expressing those dependencies. See below for your options regarding expressing those dependencies.

target_link_libraries(PUBLIC) does not help you. Read about it here: https://cmake.org/cmake/help/v3.4/manual/cmake-buildsystem.7.html#transitive-usage-requirements

If you think of an analogy to preprocessor files, you might see your options. Think of lib2_private.h which does not #include lib1_private.h. A alllibs.h will need to include those two in the correct order. Because the _private headers are private, and because clients will always include alllibs.h instead, that will work. In this approach, you manage the total dependency tree in one place.

An alternative approach would be to create lib2_internal.h which contains

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

and lib1_internal.h which contains

#include "lib1_private.h"

in this approach, you manage dependencies close to their dependers, so you would have multiple places which specify subsets of the total dependency tree. The alllibs.h could use

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

or

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

and the order would not matter.

Your config file with the loop is alllibs.h - it is the only file clients include. Can you manage the order entirely there? Yes, if you can manage the order within the @PROJECT_NAME@_LIBRARIES variable. By the way, you should call that @PROJECT_NAME@_EXPORT_SETS probably. If you don't see why, have another look at what I said above about it being a different 'unit'.

You didn't give much information, but I guess you are populating that with multiple

list(APPEND MYPROJ_EXPORT_SETS fooExports)

calls, perhaps in some macro. So the order is not easily maintainable, as it would be as a single set() call.

So, your options to express 'export set' dependencies are:

  1. Manage them in the Config file - replace the loop with a hardcoded ordered list
  2. Add more variables to express dependencies of export sets wherever you populate the MYPROJ_EXPORT_SETS variable, and replace the loop in your Config file with something more complex that takes those dependencies into account.
  3. Same as (2), but generate intermediate files and don't care about the include order within the Config file.

(1) probably makes most sense, but you might also have to step back and think harder about the abstractions/wrappers you're creating which led you here.

这篇关于Cmake:依赖管理在多库包导出的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆