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.cmakeIMPORTED 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?