Shared library dependency is not forwarded by a static library target when privately linked

42 views Asked by At

I have a complex CMake project where an executable links to a shared library. The shared library has a dependency on a static library. The static library in turn depends on other shared libraries.

A minimal self-containing project can be found here: https://github.com/rweickelt/stackoverflow/tree/cmake-transitive-dependencies

I repeat the CMake file here:

set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_PROCESSOR arm)
set(CMAKE_SYSTEM_VERSION 1)

set(CMAKE_C_COMPILER   "arm-linux-gnueabihf-gcc")
set(CMAKE_CXX_COMPILER "arm-linux-gnueabihf-g++")

cmake_minimum_required(VERSION 3.21)
project(SharedLibProblem C CXX)

# Manually setting output paths to simulate original project structure
# where all target sources live in various sub directories added by
# add_subdirectory()

add_library(TransitiveLib SHARED transitive.cpp)
set_target_properties(TransitiveLib PROPERTIES
    CXX_VISIBILITY_PRESET hidden
    LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/TransitiveLib"
    RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/TransitiveLib"
    INSTALL_RPATH "\$ORIGIN/../lib"
)
target_compile_definitions(TransitiveLib PRIVATE BUILDING_LIBRARY)

add_library(StaticLib STATIC static.cpp)
set_target_properties(StaticLib PROPERTIES
    ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/StaticLib"
)
target_link_libraries(StaticLib PUBLIC TransitiveLib)

add_library(SharedLib SHARED shared.cpp)
set_target_properties(SharedLib PROPERTIES
    CXX_VISIBILITY_PRESET hidden
    LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/SharedLib"
    RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/SharedLib"
    INSTALL_RPATH "\$ORIGIN/../lib"
)
target_compile_definitions(SharedLib PRIVATE BUILDING_LIBRARY)

# StaticLib shall not leak any defines or headers to SharedLib.
# Thus it's private. But its link dependencies must be
# forwarded regardless.
target_link_libraries(SharedLib PRIVATE StaticLib)

add_executable(TheExecutable executable.cpp)
target_link_libraries(TheExecutable PRIVATE SharedLib)
set_target_properties(TheExecutable PROPERTIES
    CXX_VISIBILITY_PRESET hidden
    RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/TheExecutable"
    INSTALL_RPATH "\$ORIGIN/../lib"
)

Linking works when using the native x64 gcc on Linux. It also works with MSVC. But when building the project with the Raspbian GCC 10.2.0 toolchain, the final linking step fails:

[8/8] Linking CXX executable TheExecutable/TheExecutable
FAILED: TheExecutable/TheExecutable
: && /opt/cross-pi-gcc-10.2.0-2/bin/arm-linux-gnueabihf-g++   CMakeFiles/TheExecutable.dir/executable.cpp.o -o TheExecutable/TheExecutable  -Wl,-rpath,/mnt/c/Users/RichardWeickelt/projects/build/build-shared-lib-problem/SharedLib  SharedLib/libSharedLib.so && :
/opt/cross-pi-gcc-10.2.0-2/bin/../lib/gcc/arm-linux-gnueabihf/10.2.0/../../../../arm-linux-gnueabihf/bin/ld: warning: libTransitiveLib.so, needed by SharedLib/libSharedLib.so, not found (try using -rpath or -rpath-link)
/opt/cross-pi-gcc-10.2.0-2/bin/../lib/gcc/arm-linux-gnueabihf/10.2.0/../../../../arm-linux-gnueabihf/bin/ld: SharedLib/libSharedLib.so: undefined reference to `transitive_library_function()'
collect2: error: ld returned 1 exit status
ninja: build stopped: subcommand failed.

I suspect that the cross-build fails because Raspbian GCC is configured with sysroot. Maybe that changes library search behavior. I have tried various things, including explicitly setting -rpath-link for all shared libraries.

I am tempted to simply set ARCHIVE_OUTPUT_DIRECTORY LIBRARY_OUTPUT_DIRECTORY and RUNTIME_OUTPUT_DIRECTORY for every library target to ${CMAKE_BINARY_DIR}/lib so that all dependent shared libraries in the build folder are always found.

But what's the canonical way to solve that problem? Linking a static library privately while keeping all shared library dependencies seems not unusual to me.

0

There are 0 answers