How to consume usage requirements of a target without exporting LINK_ONLY

Hi,

I have the following sample project.

cmake_minimum_required(VERSION 3.21)
project(proj LANGUAGES CXX)

include(GNUInstallDirs)

set(source_file "${CMAKE_CURRENT_BINARY_DIR}/lib.cpp")
file(WRITE "${source_file}" "int foo() {return 0;}")
file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/utils")

add_library(Core SHARED "${source_file}")
target_include_directories(Core PUBLIC "$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/utils>")
target_compile_definitions(Core PUBLIC "FEATURE1=ON")
target_compile_features(Core PUBLIC "cxx_std_23")
target_compile_options(Core PUBLIC "-Wall")

add_library(Gui STATIC "${source_file}")


# (1)
#target_link_libraries(Gui PRIVATE Core)

# This causes the ToolkitTargets.cmake file to contain
#set_target_properties(Toolkit::Gui PROPERTIES
  #INTERFACE_LINK_LIBRARIES "\$<LINK_ONLY:Core>"
#)


# (2)
#target_link_libraries(Gui PRIVATE $<LINK_ONLY:Core>)

# This causes the ToolkitTargets.cmake file to contain
#set_target_properties(Toolkit::Gui PROPERTIES
  #INTERFACE_LINK_LIBRARIES "\$<LINK_ONLY:\$<LINK_ONLY:Core>>"
#)


#(3) This achieves the desired effect, but is verbose and might not cover everything?
target_include_directories(Gui PRIVATE "$<TARGET_PROPERTY:Core,INTERFACE_INCLUDE_DIRECTORIES>")
target_compile_definitions(Gui PRIVATE "$<TARGET_PROPERTY:Core,INTERFACE_COMPILE_DEFINITIONS>")
target_compile_features(Gui PRIVATE "$<TARGET_PROPERTY:Core,INTERFACE_COMPILE_FEATURES>")
target_compile_options(Gui PRIVATE "$<TARGET_PROPERTY:Core,INTERFACE_COMPILE_OPTIONS>")

install(TARGETS Core Gui
    EXPORT ToolkitTargets
    BUNDLE  DESTINATION .
    RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
    LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
)
install(EXPORT ToolkitTargets
    NAMESPACE Toolkit::
    DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/Toolkit
)
$ cmake .. -DCMAKE_INSTALL_PREFIX=$PWD/installed -GNinja && ninja install

I would like to consume the usage requirements of the Core shared lib when building the Gui static lib, but not export the $<LINK_ONLY:Core> requirement in the installed ToolkitTargets.cmake file for the Gui static lib, so that user projects that consume Gui don’t need to link to the Core target.

By consuming the usage requirements, I mean make sure Core’s compile definitions, compile features, include paths, etc are used when building Gui’s sources.

The explicit link requirement is not needed, because Core will be dynamically loaded using dlopen() by Gui’s sources at an appropriate time.

I think I can achieve that using approach (3) as described in the project comment.

(1) and (2) don’t work because of $<LINK_ONLY:Core> being automatically added to Gui’s INTERFACE_LINK_LIBRARIES.

I don’t like (3) because i’m not sure if it covers everything, and because in the future CMake might introduce new types of usage requirements.

Ideally I’d use something like (1), perhaps with a new generator expression like

target_link_libraries(Gui PRIVATE $<CONSUME_COMPILE_USAGE_REQUIREMENTS:Core>)

Does something like that exist already? Is there a better way of achieving what I want?

Thank you.

Does the $<BUILD_LOCAL_INTERFACE:...> generator expression give you what you’re looking for? It is available with CMake 3.26 and later.

It seems so, but with a caveat.

When I use

target_link_libraries(Gui PRIVATE $<BUILD_LOCAL_INTERFACE:Core>)

The exported file contains the following:

# Create imported target Toolkit::Gui
add_library(Toolkit::Gui STATIC IMPORTED)

set_target_properties(Toolkit::Gui PROPERTIES
  INTERFACE_LINK_LIBRARIES "\$<LINK_ONLY:>"
)

I tried linking to this target in a separate project, and the "\$<LINK_ONLY:> didn’t seem to break anything, and the project built successfully, but it’s not unreasonable to think that a future CMake version might error out due to the empty value passed to $<LINK_ONLY:>.

I suppose this might be an oversight when $<BUILD_LOCAL_INTERFACE:> was implemented.

Filed https://gitlab.kitware.com/cmake/cmake/-/issues/26181