Problem
I am currently writing CMakeLists.txt
files for a larger code project which consists of a hierarchy of different CMake projects which can either be built alone or monolithic all together.
Sadly, I am encountering a limitation of CMake when trying to install my targets and create export/import CMake scripts for it (using the monolithic build):
CMake Error: install(EXPORT "Advanced-Runtime" ...) includes target
"Advanced-library" which requires target "Fundamentals-library"
that is not in this export set, but in multiple other export sets:
lib/cmake/MyProj/Fundamentals-Development.cmake,
lib/cmake/MyProj/Fundamentals-Runtime.cmake.
An exported target cannot depend upon another target which is exported
multiple times. Consider consolidating the exports of the
"Fundamentals-library" target to a single export.
However, that should not really be a problem, because the Fundamentals-Development.cmake
export-set only exists for the namelink component of the Fundamentals-library
shared-library (and possibly some include files that should be installed, too).
And in fact that only seems to be a problem if I am building the Fundamentals
project together with the Advanced
project monolithic.
If I cmaked and built the Fundamentals
project before, installed its export sets and just then cmake and build the Advanced
project (which then uses find_package
to retrieve the IMPORTED
target for the Fundamentals-library
) CMake is pleased and no error occurs.
Details and Example
In order to hopefully better understand what I mean and did I am reproducing the important parts of the CMakeLists.txt
files here:
I have a project Fundamentals
which internally creates a (shared) library target Fundamentals-library
and exports it (as MyProj::Fundamentals
). I am creating two export-sets for it, Fundamentals-Runtime
and Fundamentals-Development
and the built library file will be in the Fundamentals-Runtime
export-set and its namelink symlink in the Fundamentals-Development
export-set:
...
cmake_minimum_required(VERSION 3.19...3.20)
project( Fundamentals
VERSION "1.0.0" )
add_library( ${PROJECT_NAME}-library SHARED )
add_library( ${PROJECT_NAME}-library ALIAS MyProj::${PROJECT_NAME} )
...
# Install library
install( TARGETS ${PROJECT_NAME}-library
EXPORT ${PROJECT_NAME}-Runtime
LIBRARY
DESTINATION lib
COMPONENT ${PROJECT_NAME}-Runtime
NAMELINK_SKIP
)
install( TARGETS ${PROJECT_NAME}-library
EXPORT ${PROJECT_NAME}-Development
LIBRARY
DESTINATION lib
NAMELINK_COMPONENT ${PROJECT_NAME}-Development
NAMELINK_ONLY
)
# Install export-sets
install( EXPORT ${PROJECT_NAME}-Runtime
DESTINATION lib/cmake/MyProj
FILE ${PROJECT_NAME}-Runtime.cmake
NAMESPACE MyProj::
COMPONENT ${PROJECT_NAME}-Runtime
)
install( EXPORT ${PROJECT_NAME}-Development
DESTINATION lib/cmake/MyProj
FILE ${PROJECT_NAME}-Development.cmake
NAMESPACE MyProj::
COMPONENT ${PROJECT_NAME}-Development
)
...
(Of course, I am also exporting a MyProj-Config.cmake
file which internally includes those export-set files shown above. However, for brevity and because it does not help in explaining the problem I am omitting that here.)
I can build this project and then install it just fine.
In the generated import files Fundamentals-Runtime.cmake
and we find an Fundamentals-Development.cmake
IMPORTED
target MyProj::Fundamentals
which has no dependencies (aka no property INTERFACE_LINK_LIBRARIES
).
(edit: Fundamentals-Development.cmake
seems to only contain boiler-plate code and processing returns early at the beginning of the file, because it does not contain any IMPORTED
targets.)
I also have a second project Advanced
whose CMakeLists.txt
has exactly the same install
commands, only the beginning of the file is slightly different:
cmake_minimum_required(VERSION 3.19...3.20)
project( Advanced
VERSION "2.0.0" )
# import dependency if not currently building it already.
if (NOT TARGET MyProj::Fundamentals)
find_package( MyProj REQUIRED COMPONENT Fundamentals )
endif()
add_library( ${PROJECT_NAME}-library SHARED )
add_library( ${PROJECT_NAME}-library ALIAS MyProj::${PROJECT_NAME} )
# Add (public) dependency.
target_link_libraries( ${PROJECT_NAME}-library
PUBLIC MyProj::Fundamentals )
...
# Install library and export-set as before
...
As one can see the target Advanced-library
has a dependency on the MyProj::Fundamentals
target created in project Fundamentals
.
As I already said above in the problem description, building both projects together in a monolithic build yields the error. Only building both projects separately and using the IMPORTED
target for MyProj::Fundamentals
from the Advanced
project works just fine.
When doing that I can see that the IMPORTED
target MyProj::Advanced
indeed has a property INTERFACE_LINK_LIBRARIES
with MyProj::Fundamentals
as its value. And this is what I want.
But sadly, that seems to be the problem with the monolithic build.
For example, I can workaround the error by changing the target_link_libraries
line above to the following:
target_link_libraries( ${PROJECT_NAME}-library
PUBLIC $<BUILD_INTERFACE:MyProj::Fundamentals> )
However, that results in the IMPORTED
target MyProj::Advanced
to not carry its dependency information, which is suboptimal.
Another workaround would be do not install
the namelink in a separate export-set. But that is what I want to do and that is what the NAMELINK_ONLY
and NAMELINK_SKIP
options of install(TARGETS ...)
is there for in the first place.
Questions
Does anyone have a good suggestion how to circumvent that error and still be able to build monolithic and still have the exported Advanced-library
carry its dependency information?
Or does anyone know where and how to change the implementation of CMake to not trip over this situation and prevent that error in this case?