Alex Pander
Alex Pander

Reputation: 155

CMake Package Support - Includes and Libraries not found

I am currently developing a software package, for which I'd like to provide the cmake package support (so users can find it with find_package(...)). The Problem is, the package is found but FOO_INCLUDE_DIR and FOO_LIBRARIES is empty.

Within my package I have several modules, each with a CMakeLists file which installs the respective library and headers with:

install(TARGETS ${LIBRARY_NAME} EXPORT FooTargets
    RUNTIME       DESTINATION ${Foo_RUNTIME_INSTALL_DIR}
    LIBRARY       DESTINATION ${Foo_LIBRARY_INSTALL_DIR}
    ARCHIVE       DESTINATION ${Foo_ARCHIVE_INSTALL_DIR}
    FRAMEWORK     DESTINATION ${Foo_FRAMEWORK_INSTALL_DIR})

# Headers
install(
        DIRECTORY include/${LIBRARY_NAME}
        DESTINATION include/${PROJECT_NAME}
        FILES_MATCHING
            PATTERN "*.h"
            PATTERN "*.hpp"
)

The headers for the library are included with target_include_directories like this:

target_include_directories(${LIBRARY_NAME} PUBLIC
        $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include> # for headers when building
        $<INSTALL_INTERFACE:${Foo_INC_INSTALL_DIR}> # for client in install mode
)

I checked the folders and all libraries and headers are correctly installed. In my toplevel CMakeLists I export my targets with:

install(
        EXPORT FooTargets
        DESTINATION ${Foo_CMAKE_CONFIG_INSTALL_DIR}
        FILE FooConfig.cmake
)

The config is where I assume it to be (usr/local/lib/cmake/Foo). So everything seems to be correct. When I look into my FooConfig.cmake it says:

# Create imported target realm_densifier_base
add_library(FooLib1 SHARED IMPORTED)

set_target_properties(FooLib1 PROPERTIES
  INTERFACE_INCLUDE_DIRECTORIES "/usr/local/include/Foo"
  INTERFACE_LINK_LIBRARIES "...several libraries..."
)

...which is absolutely correct and exactly what I expected. What part of the puzzle is missing? Is INTERFACE_INCLUDE_DIRECTORIES and INTERFACE_LINK_LIBRARIES not the correct flag to be set?

Thanks for the help and best regards,

Alex

Edit:

@Guillaume Racicot already cleared most things up, I only knew the "non target" way of adding headers to my project, that was with include_directories(Foo_INCLUDE_DIRS). However, in the target-world linking against my library Foo was enough. Another thing was that I messed up some directories in the target_include_directories(...) command, so directories were wrong and therefore could not be found in my other project. Thanks for the help!

Upvotes: 1

Views: 1118

Answers (1)

Guillaume Racicot
Guillaume Racicot

Reputation: 41760

Why would FOO_INCLUDE_DIR or FOO_LIBRARIES be set? This may be how old find modules worked, but not how config files work. Even newer find modules expose targets instead of directory variables.

When generating an XYZConfig.cmake file, informations about targets will be exported, not informations on the directory.

With such exportation:

install(
    EXPORT FooTargets
    NAMESPACE Foo::
    DESTINATION ${Foo_CMAKE_CONFIG_INSTALL_DIR}
    FILE FooConfig.cmake
)

You would expect users of the package to use it like so:

find_package(Foo REQUIRED)

#         or PUBLIC ------v
target_link_libraries(bar PRIVATE Foo::FooLib1)

If your package has multiple targets in the export set, then you can link to both or only one

target_link_libraries(bar PRIVATE Foo::FooLib1 Foo::FooLib2)
target_link_libraries(baz PUBLIC  Foo::FooLib2) # link to lib2 only

When you link to an exported target like Foo::FooLib1, its public interface will be transitively transmitted to its user. In the example above, bar will inherit properties from the linked target.

So the INTERFACE_INCLUDE_DIRECTORIES of Foo::FooLib1 and Foo::FooLib2 will be appended to bar's INCLUDE_DIRECTORIES. Same for LINK_LIBRARIES.

For baz, not only its INCLUDE_DIRECTORIES will contain Foo::FooLib2 entries, but also its own INTERFACE_INCLUDE_DIRECTORIES will transitively transmit the usage requirements of Foo::FooLib2

Upvotes: 2

Related Questions