INTERFACE libraries linked as PRIVATE should not be in INTERFACE_LINK_LIBRARIES for STATIC linking libraries
I have a header-only INTERFACE
library and a library that can be build as STATIC
or SHARED
(depending on BUILD_SHARED_LIBS
) that links the first library as PRIVATE
(therefore the headers are only used in the .cpp
implementation files).
When built STATIC
, the exported targets for the second library, contain INTERFACE_LINK_LIBRARIES "\$<LINK_ONLY:foo>"
, but the library is not required anyway when linking because, being INTERFACE
, there is no library to link.
This becomes a problem when finding the packages, because since the libraries are in different exports, in order to find the second library, the first must be found as well, otherwise it will produce an error like this: "The following imported targets are referenced, but are missing:"
The expected behaviour is that the first library is not referenced in the second library export.
I think the description of the issue might not be very clear (sorry about that), therefore I'm attaching a minimal example (that can easily be changed to be an unit test) to reproduce the issue.
The example:
- writes some source files
- creates and exports a
foo
INTERFACE
library (containing both headers and sources) - creates and exports a
bar_shared
SHARED
library linking thefoo
library asPRIVATE
(no problems here) - creates and exports a
bar_static
STATIC
library linking thefoo
library asPRIVATE
- add tests for both
bar_shared
andbar_static
thatfoo
is not in theINTERFACE_LINK_LIBRARIES
property.
Using ar -t libbar_static.a
you can check that Foo.cpp.o
is contained in the static library, and therefore there is no need to link anything.
cmake_minimum_required(VERSION 3.5)
project(test_interface)
enable_testing()
################################################################################
## Create fake files
file(WRITE "${CMAKE_BINARY_DIR}/gen_src/foo/include/foo/Foo.h"
"#ifndef FOO_H
#define FOO_H
class Foo
{
public:
Foo();
};
#endif
")
file(WRITE "${CMAKE_BINARY_DIR}/gen_src/foo/src/Foo.cpp"
"#include <foo/Foo.h>
Foo::Foo()
{
}
")
file(WRITE "${CMAKE_BINARY_DIR}/gen_src/bar/include/bar/Bar.h"
"#ifndef BAR_H
#define BAR_H
class Bar
{
public:
Bar();
};
#endif
")
file(WRITE "${CMAKE_BINARY_DIR}/gen_src/bar/src/Bar.cpp"
"#include <bar/Bar.h>
#include <foo/Foo.h>
Bar::Bar()
{
Foo f;
}
")
################################################################################
## foo interface library
add_library(foo INTERFACE)
target_sources(foo INTERFACE $<BUILD_INTERFACE:${CMAKE_BINARY_DIR}/gen_src/foo/include/foo/Foo.h>
$<BUILD_INTERFACE:${CMAKE_BINARY_DIR}/gen_src/foo/src/Foo.cpp>
$<INSTALL_INTERFACE:include/foo/include/foo/Foo.h>
$<INSTALL_INTERFACE:include/foo/src/Foo.cpp>)
target_include_directories(foo INTERFACE $<BUILD_INTERFACE:${CMAKE_BINARY_DIR}/gen_src/foo/include>
$<INSTALL_INTERFACE:include/foo/include>)
install(TARGETS foo
EXPORT foo
COMPONENT foo)
install(DIRECTORY "${CMAKE_BINARY_DIR}/gen_src/foo"
DESTINATION include)
export(EXPORT foo
NAMESPACE test_interface::
FILE "${CMAKE_BINARY_DIR}/foo/fooTargets.cmake")
################################################################################
## bar_shared library
add_library(bar_shared SHARED "${CMAKE_BINARY_DIR}/gen_src/bar/src/Bar.cpp"
"${CMAKE_BINARY_DIR}/gen_src/bar/include/bar/Bar.h")
target_include_directories(bar_shared PUBLIC $<BUILD_INTERFACE:${CMAKE_BINARY_DIR}/gen_src/bar/include>
$<INSTALL_INTERFACE:include>)
target_link_libraries(bar_shared PRIVATE foo)
set_property(TARGET bar_shared PROPERTY PUBLIC_HEADER "${CMAKE_BINARY_DIR}/gen_src/bar/include/bar/Bar.h")
install(TARGETS bar_shared
EXPORT bar_shared
COMPONENT bar_shared
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib
PUBLIC_HEADER DESTINATION include/bar)
export(EXPORT bar_shared
NAMESPACE test_interface::
FILE "${CMAKE_BINARY_DIR}/bar_shared/bar_sharedTargets.cmake")
################################################################################
## bar_static library
add_library(bar_static STATIC "${CMAKE_BINARY_DIR}/gen_src/bar/src/Bar.cpp"
"${CMAKE_BINARY_DIR}/gen_src/bar/include/bar/Bar.h")
target_include_directories(bar_static PUBLIC $<BUILD_INTERFACE:${CMAKE_BINARY_DIR}/gen_src/bar/include>
$<INSTALL_INTERFACE:include>)
target_link_libraries(bar_static PRIVATE foo)
set_property(TARGET bar_static PROPERTY PUBLIC_HEADER "${CMAKE_BINARY_DIR}/gen_src/bar/include/bar/Bar.h")
install(TARGETS bar_static
EXPORT bar_static
COMPONENT bar_static
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib
PUBLIC_HEADER DESTINATION include/bar)
export(EXPORT bar_static
NAMESPACE test_interface::
FILE "${CMAKE_BINARY_DIR}/bar_static/bar_staticTargets.cmake")
################################################################################
## check exported targets
file(WRITE "${CMAKE_BINARY_DIR}/test_bar_shared.cmake"
"file(READ \"${CMAKE_BINARY_DIR}/bar_shared/bar_sharedTargets.cmake\" bar_sharedTargets_content)
if(\"\${bar_sharedTargets_content}\" MATCHES \"test_interface::foo\")
message(FATAL_ERROR \"bar_shared test FAILED\")
else()
message(STATUS \"bar_shared test SUCCESS\")
endif()
")
add_test(NAME test_bar_shared
COMMAND ${CMAKE_COMMAND} -P "${CMAKE_BINARY_DIR}/test_bar_shared.cmake")
file(WRITE "${CMAKE_BINARY_DIR}/test_bar_static.cmake"
"file(READ \"${CMAKE_BINARY_DIR}/bar_static/bar_staticTargets.cmake\" bar_staticTargets_content)
if(\"\${bar_staticTargets_content}\" MATCHES \"test_interface::foo\")
message(FATAL_ERROR \"bar_static test FAILED\")
else()
message(STATUS \"bar_static test SUCCESS\")
endif()
")
add_test(NAME test_bar_static
COMMAND ${CMAKE_COMMAND} -P "${CMAKE_BINARY_DIR}/test_bar_static.cmake")