问题
I have a package called MYLIBS consisting of two libraries, lib1 and lib2, which I want to export through the configuration 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 configuration 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 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)
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, i.e. lib1 first and then lib2, otherwise I get an error when reading the configuration file by FindPackage()
.
I am 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 configuration 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 am I supposed to use that? Would it be of any help?
回答1:
To begin with, you can remove the add_dependencies
line. See target_link_libraries and add_dependencies.
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 build system 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 in 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 configuration 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 configuration 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 configuration 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 configuration 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.
来源:https://stackoverflow.com/questions/36103012/cmake-dependency-management-in-a-multi-library-package-export