Search code examples
c++cmakespdlog

Accessing an external project with add-subdirectory results in CMake Error related to export set


I have a project A that depends on spdlog. Here is the structure:

|--- dir A
...|---src
......|---CMakeLists.txt
...|---include
...|---CMakeLists.txt
|---external/3rd_party/spdlog

I am trying to access spdlog in project A by adding a subdirectory. Here is how my A/CMakeLists.txt looks like:

cmake_minimum_required(VERSION 3.9.3 FATAL_ERROR)
project(GLOBAL CXX)
add_subdirectory(../external/3rd_party/spdlog   ${CMAKE_BINARY_DIR}/spdlog  EXCLUDE_FROM_ALL)
add_subdirectory(src)

Here is how my A/src/CMakeLists.txt looks like:

cmake_minimum_required(VERSION 3.9.3 FATAL_ERROR)
project(Alib CXX)

if(NOT TARGET spdlog)
   # Stand-alone build
   find_package(spdlog_header_only REQUIRED)
endif()
add_librray(A A.cpp)
target_link_libraries(A PUBLIC spdlog_header_only)

install(TARGETS A
   EXPORT ATargets
   LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
   ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
)
install(EXPORT ATargets
    NAMESPACE A::
    FILE ATargets.cmake
    DESTINATION ${INSTALL_CONFIGDIR})

install(FILES AConfig.cmake DESTINATION ${INSTALL_CONFIGDIR})

When I try to build this, I get the following error:

CMake Error: install(EXPORT "ATargets" ...) includes target "A" which requires target "spdlog_header_only" that is not in the export set.

Please can you suggest me how to fix it?

For some reason I need to maintain the same directory structure I have shown above. Here is a related question but does not have an answer : here


Solution

  • Meaining of the error

    Since you use PUBLIC keyword when link your library (A) with spdlog_header_only, CMake expects that this linking is also needed for users of your library. So, when you create config file for your library (with install(EXPORT)), CMake adds linking with spdlog_header_only target into the config file too. Like

    # [ATargets.cmake]
    target_link_libraries(A::A PUBLIC spdlog_header_only)
    

    Linking with a target implies existence of this target, but you do not install spdlog_header_only target. Because of that, created config file for your library won't work. This is what CMake tells you in the error message.

    Simple fix

    The simplest fix would be using PRIVATE linking with spdlog_header_only target, so that linking won't be part of the config file. Beware, in that case a user of your (installed) library won't get access to the header files for spdlog. (But a user could obtain these headers by other means.)

    Hard fix

    But if you want a user of your library to have access to spdlog headers (or, worse, the public headers of your library use #include for headers from spdlog), then you cannot drop PUBLIC linking. In that case you need to install spdlog_header_only target too. E.g. by enabling SPDLOG_INSTALL option

    set(SPDLOG_INSTALL ON)
    

    before

    add_subdirectory(../external/3rd_party/spdlog   ${CMAKE_BINARY_DIR}/spdlog  EXCLUDE_FROM_ALL)
    

    (Note, that aside from enabling SPDLOG_INSTALL option, several other adjustments needs to be done for make the config file for your library to work.)