Cmake:依赖管理在多库包导出 [英] Cmake: dependency management in a multi-library package export
问题描述
我有一个名为 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()
调用。
表示导出集依赖性的选项是:
- 在Config文件中管理它们 - 用硬编码的有序列表替换循环
-
- 添加更多变量以表示导出集的依赖关系,无论您在何处填充
MYPROJ_EXPORT_SETS
变量,并将Config文件中的循环替换为更复杂的 - 与(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:
- Manage them in the Config file - replace the loop with a hardcoded ordered list
- 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. - 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屋!