find_package, Config mode: won't find library

We equipped a library package with a *Config.cmake file, following step 11 of the CMake tutorial. Yet our downstream software fails to find the library.

Our package is called “formfactor” [mlz / libformfactor · GitLab]. It provides a shared library (libformfactor) and some header files.

Our downstream code uses

find_package(formfactor REQUIRED CONFIG)
message(STATUS "formfactor: found=${formfactor_FOUND}, include_dirs=${formfactor_INCLUDE_DIR}, "
    "lib=${formfactor_LIBRARY}, version=${formfactor_VERSION}")

to search for the library. Alas, it prints

formfactor: found=1, include_dirs=/usr/local/include, lib=, version=0.1

That is, it does not find the library, yet fails to raise a fatal error though we said “REQUIRED”.

Package “formfactor” contains all the following:

CMakeLists.txt:

cmake_minimum_required(VERSION 3.6 FATAL_ERROR)
project(formfactor VERSION 0.1.1 LANGUAGES CXX)

if(NOT DEFINED BUILD_SHARED_LIBS)
    option(BUILD_SHARED_LIBS "Build as shared library" ON)
endif()
option(WERROR "Treat warnings as errors" OFF)

add_subdirectory(ff)

install(EXPORT formfactorTargets
  FILE formfactorTargets.cmake
  DESTINATION cmake
)

include(CMakePackageConfigHelpers)
configure_package_config_file(${CMAKE_CURRENT_SOURCE_DIR}/Config.cmake.in
    "${CMAKE_CURRENT_BINARY_DIR}/formfactorConfig.cmake"
    INSTALL_DESTINATION cmake
    NO_SET_AND_CHECK_MACRO
    NO_CHECK_REQUIRED_COMPONENTS_MACRO
    )

write_basic_package_version_file(
    "${CMAKE_CURRENT_BINARY_DIR}/formfactorConfigVersion.cmake"
    VERSION "${formfactor_VERSION_MAJOR}.${formfactor_VERSION_MINOR}"
    COMPATIBILITY AnyNewerVersion
    )

install(FILES
    ${PROJECT_BINARY_DIR}/formfactorConfig.cmake
    ${PROJECT_BINARY_DIR}/formfactorConfigVersion.cmake
    DESTINATION cmake)

export(EXPORT formfactorTargets
  FILE ${CMAKE_CURRENT_BINARY_DIR}/formfactorTargets.cmake
)

Config.cmake.in:


set(formfactor_INCLUDE_DIR @CMAKE_INSTALL_PREFIX@/include)
include(${CMAKE_CURRENT_LIST_DIR}/formfactorTargets.cmake)

ff/CMakeLists.txt:

set(lib formfactor)
set(${lib}_LIBRARY ${lib} PARENT_SCOPE)

file(GLOB src_files *.cpp)
set(api_files Polyhedron.h PolyhedralTopology.h PolyhedralComponents.h)

add_library(${lib} ${src_files})

target_include_directories(${lib}
    PUBLIC
    $<BUILD_INTERFACE:${CMAKE_SOURCE_DIR}>
    $<INSTALL_INTERFACE:include>
    )

set_target_properties(
    ${lib} PROPERTIES
    OUTPUT_NAME ${lib}
    VERSION ${formfactor_VERSION}
    SOVERSION ${formfactor_VERSION_MAJOR})

install(
    TARGETS ${lib}
    EXPORT formfactorTargets
    LIBRARY DESTINATION lib
    RUNTIME DESTINATION lib
    ARCHIVE DESTINATION lib)

install(
    FILES ${api_files}
    DESTINATION include/ff)

Anything wrong? Anything missing? Why else doesn’t it work?

To me it looks like INSTALL_DESTINATION of formfactorConfig.cmake (“lib\cmake”) differs from the one used by the other files (“cmake”). “lib\cmake” is a good place to install all those files.

changed top-level CMakeLists.txt; now INSTALL_DESTINATION cmake instead of ... lib/cmake; still the same error.

The autogenerated **build/formFactorTargets.cmake ** contains

add_library(formfactor SHARED IMPORTED)

set_target_properties(formfactor PROPERTIES
  INTERFACE_INCLUDE_DIRECTORIES "/G/lib/ff"
)

# Import target "formfactor" for configuration ""
set_property(TARGET formfactor APPEND PROPERTY IMPORTED_CONFIGURATIONS NOCONFIG)
set_target_properties(formfactor PROPERTIES
  IMPORTED_LOCATION_NOCONFIG "/G/lib/ff/build/ff/libformfactor.so.0.1.1"
  IMPORTED_SONAME_NOCONFIG "libformfactor.so.0"
  )

How is this information supposed to be transmitted to the downstream call of find_package and to the variable formfactor_LIBRARY?

Variables are set manually. I see there is a setting for the INCLUDE_DIR variable in your Config.cmake.in, but nothing sets the _LIBRARY variable. I’d say you don’t need either and you should instead just be using the formfactor target directly instead. The properties you see being set are “usage requirements” and CMake knows how to use them to make the target “useful” to those linking to it.

Problem solved at c++ - <Package>Config.cmake for a library package - Stack Overflow. In retrospect, @ben.boeckel also had given the right answer; I just wasn’t prepared to catch the key point.

In short: Nothing is wrong with the CMake tutorial and how I applied it to the “formfactor” package. My mistake was to overlook its consequences for downstream.

In “modern” CMake Config style, there are no variables formfactor_INCLUDE_DIR and formfactor_LIBRARY. Instead, we just have a target formfactor. In downstream, we refer to it per

target_link_libraries(downstream_target PUBLIC formfactor)

Note that formfactor is not enclosed in ${...}: a target is not a variable.

No more need to mention “formfactor” in a target_include_directories command; this is implied by the above target_link_libraries command.