Modern CMake transitive Dependency not found

时间秒杀一切 提交于 2021-02-08 06:46:37

问题


I am currently working on implementing a ROS library into our company software stack. Because the library is based on ROS and thus uses catkin I am rewriting the library to use cmake exclusively and try to apply the modern CMake approach. The library is structured as follows:

.
|-- CMakeLists.txt
|-- LICENSE
|-- README.md
|-- grid_map_core
|   |-- CHANGELOG.rst
|   |-- CMakeLists.txt
|   |-- cmake
|   |   `-- grid_map_core-extras.cmake
|   |-- grid_map_coreConfig.cmake
|   |-- include
|   |   `-- grid_map_core
|   |       `-- iterators
|   |-- src
|   |   `-- iterators
|   `-- test

If I install the library and try to add the library in a simple test_project to the target I get an error displaying the Eigen3 dependency cannot be found:

CMake Error at CMakeLists.txt:6 (find_package):
  Found package configuration file:

    /usr/local/lib/cmake/grid_map_core/grid_map_coreConfig.cmake

  but it set grid_map_core_FOUND to FALSE so package "grid_map_core" is
  considered to be NOT FOUND.  Reason given by package:

  grid_map_core could not be found because dependency Eigen3 could not be
  found.

Unfortunately the Eigen version I have to use does not provide the Eigen3Config.cmake option and I am forced to use the cmake provided FindEigen3.cmake alternative. (I suppose compiling a newer Eigen3 version manually would be a valid alternative, nevertheless I try to completely understand the modern cmake approach which looks very promising for exactly avoiding such issues)

From all the resources online I am not quite sure how the transitive dependency is handled in this case. To my understanding the grid_map_coreConfig.cmake should forward the imported Eigen3 dependency. In the grid_map_core CMakeLists the eigen is found by the command find_package(Eigen3 3.2 REQUIRED) and the find_dependency macro just wraps this exact same command.

Resources

The main CmakeLists.txt looks as follows:

# Set cmake version
cmake_minimum_required(VERSION 3.0.2)

# Set project name
project(grid_map)

# Must use GNUInstallDirs to install libraries into correct
# locations on all platforms.
include(GNUInstallDirs)

add_compile_options(-std=c++11)


# Add subdirectories
add_subdirectory(grid_map_core)

The grid_map_core CMakeLists as follows:

# Set cmake version
cmake_minimum_required(VERSION 3.0.2)

# Set project name
project(grid_map_core)

add_compile_options(-std=c++11)

# import Eigen3
find_package(Eigen3 3.2.2 REQUIRED)

## Define Eigen addons.
include(cmake/${PROJECT_NAME}-extras.cmake)

#########
# Build #
#########

# Add the library target
add_library(${PROJECT_NAME}
  src/BufferRegion.cpp
  src/GridMap.cpp
  src/GridMapMath.cpp
  src/Polygon.cpp
  src/SubmapGeometry.cpp
  src/iterators/CircleIterator.cpp
  src/iterators/EllipseIterator.cpp
  src/iterators/GridMapIterator.cpp
  src/iterators/LineIterator.cpp
  src/iterators/PolygonIterator.cpp
  src/iterators/SlidingWindowIterator.cpp
  src/iterators/SubmapIterator.cpp
)

# set target include directories
target_include_directories(${PROJECT_NAME}
  PUBLIC
    $<INSTALL_INTERFACE:include>
    $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
    ${EIGEN3_INCLUDE_DIR}
  PRIVATE
    ${CMAKE_CURRENT_SOURCE_DIR}/src
)

# add an alias
add_library(${PROJECT_NAME}::${PROJECT_NAME} ALIAS ${PROJECT_NAME})

# set target compile options
target_compile_options(${PROJECT_NAME}
  PRIVATE
    $<$<CONFIG:Debug>:-Werror>
)

###########
# Install #
###########

# 'make install' to the right locations
install(TARGETS ${PROJECT_NAME}
  EXPORT "${PROJECT_NAME}Targets"
  ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
  LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
  INCLUDES DESTINATION include
)

# This makes the project importable from the install directory
# Put config file in per-project dir.
install(EXPORT "${PROJECT_NAME}Targets"
  FILE "${PROJECT_NAME}Targets.cmake"
  NAMESPACE "${PROJECT_NAME}::"
  DESTINATION lib/cmake/${PROJECT_NAME})

# generate config.cmake
include(CMakePackageConfigHelpers)
write_basic_package_version_file("${PROJECT_NAME}ConfigVersion.cmake"
  VERSION "${PROJECT_NAME}_VERSION"
  COMPATIBILITY SameMajorVersion
  )

# install config.cmake files
install(FILES "${PROJECT_NAME}Config.cmake"
  DESTINATION "lib/cmake/${PROJECT_NAME}")

###########
# Testing #
###########

and the grid_map_coreConfig.cmake as follows:

include(CMakeFindDependencyMacro)

find_dependency(Eigen3 REQUIRED)
include("${CMAKE_CURRENT_LIST_DIR}/grid_map_coreTargets.cmake")

and the test_project's CMakeLists.txt:

cmake_minimum_required(VERSION 3.0)
project(test_project)

set(CMAKE_MODULE_PATH /usr/share/cmake-3.0/Modules)

add_compile_options(-std=c++11)

find_package(grid_map_core REQUIRED CONFIG)

add_executable(test_project main.cpp)

target_link_libraries(test_project
  PRIVATE
    grid_map_core::grid_map_core
  )

For completeness I'm adding the FindEigen3.cmake file:

# - Try to find Eigen3 lib
#
# This module supports requiring a minimum version, e.g. you can do
#   find_package(Eigen3 3.1.2)
# to require version 3.1.2 or newer of Eigen3.
#
# Once done this will define
#
#  EIGEN3_FOUND - system has eigen lib with correct version
#  EIGEN3_INCLUDE_DIR - the eigen include directory
#  EIGEN3_VERSION - eigen version

# Copyright (c) 2006, 2007 Montel Laurent, <montel@kde.org>
# Copyright (c) 2008, 2009 Gael Guennebaud, <g.gael@free.fr>
# Copyright (c) 2009 Benoit Jacob <jacob.benoit.1@gmail.com>
# Redistribution and use is allowed according to the terms of the 2-clause BSD license.

if(NOT Eigen3_FIND_VERSION)
  if(NOT Eigen3_FIND_VERSION_MAJOR)
    set(Eigen3_FIND_VERSION_MAJOR 2)
  endif(NOT Eigen3_FIND_VERSION_MAJOR)
  if(NOT Eigen3_FIND_VERSION_MINOR)
    set(Eigen3_FIND_VERSION_MINOR 91)
  endif(NOT Eigen3_FIND_VERSION_MINOR)
  if(NOT Eigen3_FIND_VERSION_PATCH)
    set(Eigen3_FIND_VERSION_PATCH 0)
  endif(NOT Eigen3_FIND_VERSION_PATCH)

  set(Eigen3_FIND_VERSION "${Eigen3_FIND_VERSION_MAJOR}.${Eigen3_FIND_VERSION_MINOR}.${Eigen3_FIND_VERSION_PATCH}")
endif(NOT Eigen3_FIND_VERSION)

macro(_eigen3_check_version)
  file(READ "${EIGEN3_INCLUDE_DIR}/Eigen/src/Core/util/Macros.h" _eigen3_version_header)

  string(REGEX MATCH "define[ \t]+EIGEN_WORLD_VERSION[ \t]+([0-9]+)" _eigen3_world_version_match "${_eigen3_version_header}")
  set(EIGEN3_WORLD_VERSION "${CMAKE_MATCH_1}")
  string(REGEX MATCH "define[ \t]+EIGEN_MAJOR_VERSION[ \t]+([0-9]+)" _eigen3_major_version_match "${_eigen3_version_header}")
  set(EIGEN3_MAJOR_VERSION "${CMAKE_MATCH_1}")
  string(REGEX MATCH "define[ \t]+EIGEN_MINOR_VERSION[ \t]+([0-9]+)" _eigen3_minor_version_match "${_eigen3_version_header}")
  set(EIGEN3_MINOR_VERSION "${CMAKE_MATCH_1}")

  set(EIGEN3_VERSION ${EIGEN3_WORLD_VERSION}.${EIGEN3_MAJOR_VERSION}.${EIGEN3_MINOR_VERSION})
  if(${EIGEN3_VERSION} VERSION_LESS ${Eigen3_FIND_VERSION})
    set(EIGEN3_VERSION_OK FALSE)
  else(${EIGEN3_VERSION} VERSION_LESS ${Eigen3_FIND_VERSION})
    set(EIGEN3_VERSION_OK TRUE)
  endif(${EIGEN3_VERSION} VERSION_LESS ${Eigen3_FIND_VERSION})

  if(NOT EIGEN3_VERSION_OK)

    message(STATUS "Eigen3 version ${EIGEN3_VERSION} found in ${EIGEN3_INCLUDE_DIR}, "
                   "but at least version ${Eigen3_FIND_VERSION} is required")
  endif(NOT EIGEN3_VERSION_OK)
endmacro(_eigen3_check_version)

if (EIGEN3_INCLUDE_DIR)

  # in cache already
  _eigen3_check_version()
  set(EIGEN3_FOUND ${EIGEN3_VERSION_OK})

else (EIGEN3_INCLUDE_DIR)

  find_path(EIGEN3_INCLUDE_DIR NAMES signature_of_eigen3_matrix_library
      PATHS
      ${CMAKE_INSTALL_PREFIX}/include
      ${KDE4_INCLUDE_DIR}
      PATH_SUFFIXES eigen3 eigen
    )

  if(EIGEN3_INCLUDE_DIR)
    _eigen3_check_version()
  endif(EIGEN3_INCLUDE_DIR)

  include(FindPackageHandleStandardArgs)
  find_package_handle_standard_args(Eigen3 DEFAULT_MSG EIGEN3_INCLUDE_DIR EIGEN3_VERSION_OK)

  mark_as_advanced(EIGEN3_INCLUDE_DIR)

endif(EIGEN3_INCLUDE_DIR)


回答1:


The error message you got is generated by find_dependency macro, but not by find_package command, which is called internally by that macro. Because you call find_dependency with REQUIRED keyword (and this keyword is passed to inner find_package), the only possible scenario of your error is follow:

  1. Call to find_package(Eigen3 REQUIRED) interprets Eigen3 to be found.
  2. But when find_dependency checks find_package results, it interprets Eigen3 to be not found.

Rather strange, isn't it?

Actually, your FindEigen3.cmake script is one of the old ones, which sets upper case flow of "FOUND" variable, which denotes success of the script:

# EIGEN3_FOUND - system has eigen lib with correct version

but the correct name of such variable should be Eigen3_FOUND (the package name should be exactly the same as in find_package(Eigen3) call and in the name of the script FindEigen3.cmake).

Command find_package checks both spelling of the "FOUND" variable: correct one and upper-case one. Such way, when FindEigen3.cmake script sets EIGEN3_FOUND variable with intention "I have found the package", find_package understands that intention, marking the package as found.

But macro find_dependency checks only correct name of the variable, Eigen3_FOUND. Because this variable isn't set by the FindEigen3.cmake script, the package is treated as not found.

As a fast fix, you may replace in the FindEigen3.cmake script EIGEN3_FOUND with Eigen3_FOUND, and everything should work. (Well, the same replacement should be done on the user machine. Or you should ship corrected FindEigen3.cmake script with your library).



来源:https://stackoverflow.com/questions/55160869/modern-cmake-transitive-dependency-not-found

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!