target_link_libraries: Allow only targets and fail on raw library name

I understand the reason why target_link_libraries accepts raw library names that aren’t known targets to CMake. However, it can often lead to surprises and wasted time. What sometimes happens is this:

  • a developer adds a target_link_libraries to use a library in one of his existing targets
  • developer starts a build job, which runs for quite a while, before failing because the include dir is wrong
  • the developer adds the missing find_package
  • everything recompiles because now the include dirs have changed

I am aware of the fact that so-called namespaced targets provide this functionality. However, there are still many targets out there that do not support this. Ideally I think we’d have a flag for target_link_libaries which makes it accept targets only. Perhaps a syntax like this:

target_link_libraries(MyExecutable TARGET_ONLY PUBLIC MyLibrary)

Right now we use a wrapper function I wrote that works around this problem by checking for targets and then calling target_link_libraries:

function(target_link_target LINK_TO)
    set(OPTIONS)
    set(SINGLE_VALUE)
    set(MULTI_VALUE PRIVATE PUBLIC INTERFACE)

    cmake_parse_arguments(TLT "${OPTIONS}" "${SINGLE_VALUE}" "${MULTI_VALUE}" ${ARGN})

    # ensure we got at least one thing to link
    if (NOT TLT_PRIVATE AND NOT TLT_PUBLIC AND NOT TLT_INTERFACE)
        message(FATAL_ERROR "No targets given for linking, specify at least one of PUBLIC, PRIVATE or INTERFACE")
    endif()

    foreach(ITEM ${TLT_PRIVATE})
        if (NOT TARGET ${ITEM})
            message(FATAL_ERROR "Linking to target ${ITEM}, which is not found")
        endif()

        target_link_libraries(${LINK_TO} PRIVATE ${ITEM})
    endforeach()

    foreach(ITEM ${TLT_PUBLIC})
        if (NOT TARGET ${ITEM})
            message(FATAL_ERROR "Linking to target ${ITEM}, which is not found")
        endif()

        target_link_libraries(${LINK_TO} PUBLIC ${ITEM})
    endforeach()

    foreach(ITEM ${TLT_INTERFACE})
        if (NOT TARGET ${ITEM})
            message(FATAL_ERROR "Linking to target ${ITEM}, which is not found")
        endif()

        target_link_libraries(${LINK_TO} INTERFACE ${ITEM})
    endforeach()
endfunction()

But it’s kind of a crude solution for a problem I believe others can also run into.

Have a look at CMAKE_LINK_LIBRARIES_ONLY_TARGETS.

1 Like