Problem with linking on static libraries

Hello,

I have a simple library project, which consumes an other library, provided as a CMake installation configuration (throught a ZIP and a call to FetchContent).

The question is at the end.

Here is the files structure :

└── DepTest
    ├── CMakeLists.txt
    ├── include
    │   └── DepTest
    │       └── a.h
    ├── src
    │   ├── CMakeLists.txt
    │   ├── a.cpp
    └── test
        ├── CMakeLists.txt
        └── test.cpp

Root CMakeLists.txt :

cmake_minimum_required(VERSION 3.17.2)

project(DepTest VERSION 1.0.0 LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

add_subdirectory(src)

add_subdirectory(test)

test CMakeLists.txt :

add_executable(DepTestTest test.cpp)

target_link_libraries(DepTestTest PRIVATE DepTest)

and the big part, the src CMakeLists.txt :

set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTORCC ON)

find_package(Qt5 REQUIRED
             COMPONENTS Core)

if(POLICY CMP0135)
	cmake_policy(SET CMP0135 NEW)
endif()

include(FetchContent)
FetchContent_Declare(OtherLib
    URL           <path_to_otherLib>
    HTTP_USERNAME $ENV{NEXUS_USERNAME}
    HTTP_PASSWORD $ENV{NEXUS_PASSWORD})
FetchContent_MakeAvailable(OtherLib)

set(OtherLib_SOURCE_DIR)
FetchContent_GetProperties(OtherLib
	SOURCE_DIR OtherLib_SOURCE_DIR)

add_library(DepTest STATIC)

target_include_directories(${PROJECT_NAME}
    PRIVATE
        ${OtherLib_SOURCE_DIR}/include)

# Problem !!!
target_link_directories(${PROJECT_NAME}
    PUBLIC
        ${OtherLib_SOURCE_DIR}/lib)

target_link_libraries(DepTest
    PRIVATE
        Qt5::Core
        OtherLib$<$<CONFIG:Debug>:d>)

set(PUBLIC_SOURCE_LIST
    ${PROJECT_SOURCE_DIR}/include/DepTest/a.h)

set(PRIVATE_SOURCE_LIST
    a.cpp)

target_sources(DepTest
	PRIVATE
		${PUBLIC_SOURCE_LIST}
		${PRIVATE_SOURCE_LIST})

source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} PREFIX Sources FILES ${PRIVATE_SOURCE_LIST})
source_group(TREE ${PROJECT_SOURCE_DIR}/include/DepTest PREFIX Interface FILES ${PUBLIC_SOURCE_LIST})

# CMake will install by default the files in C:/Program Files/DepTest
# (see in CMake documentation CMAKE_INSTALL_PREFIX), but we don't want
# to pollute C:/Program Files, so we'll put the files somewhere in the
# project folders.
# All installation files will be rooted in CMAKE_INSTALL_PREFIX.
if(DEFINED CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
    message(
        STATUS
        "CMAKE_INSTALL_PREFIX is not set\n"
        "   Default value: ${CMAKE_INSTALL_PREFIX}\n"
        "   Will set it to ${CMAKE_SOURCE_DIR}/install"
    )
    set(CMAKE_INSTALL_PREFIX
        "${CMAKE_SOURCE_DIR}/install"
        CACHE PATH "Where the library will be installed to" FORCE
    )
else()
    message(
        STATUS
        "CMAKE_INSTALL_PREFIX was already set\n"
        "   Current value: ${CMAKE_INSTALL_PREFIX}"
    )
endif()

# This module sets several installation directories compliant with GNU standard.
# They can also be used for Windows.
include(GNUInstallDirs)
set(CMAKE_INSTALL_INCLUDEDIR "${CMAKE_INSTALL_PREFIX}/include")

target_include_directories(DepTest
    PUBLIC
        $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>
        $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
	PRIVATE
        ${CMAKE_CURRENT_LIST_DIR})

# To install the public header files, we cannot just set the PUBLIC_HEADER property on these
# files,and expect that the install() command will do correctly the job by using the parameter
# PUBLIC_HEADER DESTINATION: the file structure will not be respected.
# So instead we iterate through public headers and install them "manually".
# Note : May be obsolete since the introduction of File sets (see target_sources())
foreach(header ${PUBLIC_SOURCE_LIST})
    file(RELATIVE_PATH header_file_path "${PROJECT_SOURCE_DIR}" "${header}")
    get_filename_component(header_directory_path "${header_file_path}" DIRECTORY)
    install(FILES ${header}
            DESTINATION "${CMAKE_INSTALL_PREFIX}/${header_directory_path}")
endforeach()

# We need to keep the original name, before postfix appending, in order to manage the PDB file later.
set(ORIGINAL_PROJECT_NAME ${PROJECT_NAME})

# Add a suffix on ibrary binary for debug version
set_target_properties(${PROJECT_NAME} PROPERTIES DEBUG_POSTFIX "d")

install(TARGETS ${PROJECT_NAME}
    EXPORT "${PROJECT_NAME}Targets"
    # These get default values from GNUInstallDirs, no need to set them
    # RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} # bin
    # LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} # lib
    # ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} # lib
    # except for public headers, as we want them to be inside a library folder
    # PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME} # include/DepTest
    INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} # include/DepTest
)

# For some build configurations, we have also to copy "by hand" the PDB files, because it is not done by CMake.
# CMake provides the expression generator $<TARGET_PDB_FILE:tgt>, but it concerns the PDB built by linker.
# As static libraries don't link, we need the PDB geenrated during the build. But CMake doesn't provide
# expression generator for this file (an issue has been opened : https://gitlab.kitware.com/cmake/cmake/-/issues/16932).
# We have so to "manually" localize the PDB file, and copy it during the installation for the relevant builds.
if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.19.0")
	message(WARNING ">>>> Use <CONFIG:Debug,RelWithDebInfo> instead boolean test in if() test inside install command")
endif()
install(CODE "
	if ($<CONFIG:Debug> OR $<CONFIG:RelWithDebInfo>)
		file(INSTALL \"${CMAKE_CURRENT_BINARY_DIR}/${ORIGINAL_PROJECT_NAME}.dir/$<CONFIG>/${ORIGINAL_PROJECT_NAME}.pdb\"
			 DESTINATION \"\${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}\")
	endif()
	")

# Problem !!!
install(EXPORT "${PROJECT_NAME}Targets"
    FILE "${PROJECT_NAME}Targets.cmake"
    NAMESPACE ${namespace}::  # This namespace will have to be used by the client when it will search the package
    DESTINATION cmake)

include(CMakePackageConfigHelpers)

# Generate the version file for the config file
write_basic_package_version_file(
    "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake"
    COMPATIBILITY AnyNewerVersion
)

# Create config file
configure_package_config_file(${CMAKE_CURRENT_SOURCE_DIR}/Config.cmake.in
    "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake"
    INSTALL_DESTINATION cmake
)
# Install config files
install(FILES
    "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake"
    "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake"
    DESTINATION cmake
)

In the big CMakeLists.txt, please search for the 2 occurrences of “# Problem !!!”.
These two instructions causes some troubles. Here what I get when I run CMake -G:

CMake Error in src/CMakeLists.txt:
  Target "DepTest" INTERFACE_LINK_DIRECTORIES property contains path:

    "E:/CMake_sandbox/DepTest/build/_deps/OtherLib-src/lib"

  which is prefixed in the build directory.


CMake Error in src/CMakeLists.txt:
  Target "DepTest" INTERFACE_LINK_DIRECTORIES property contains path:

    "E:/CMake_sandbox/DepTest/build/_deps/OtherLib-src/lib"

  which is prefixed in the build directory.Target "DepTest"
  INTERFACE_LINK_DIRECTORIES property contains path:

    "E:/CMake_sandbox/DepTest/build/_deps/OtherLib-src/lib"

  which is prefixed in the source directory.

I found this related post :

But I don’t really understand what’s happening, and what I should do.

OK, I resolved the problem by adding to target_link_directories() the classic pair BUILD_INTERFACE/INSTALL_INTERFACE.

I think understanding their purpose, but not using them should only case a problem when using the package.
I don’t understand why I got the error message above. And this one isn’t clear at all.