Современная CMake транзитивная зависимость не найдена - PullRequest
0 голосов
/ 14 марта 2019

В настоящее время я работаю над внедрением библиотеки ROS в программный стек нашей компании.Поскольку библиотека основана на ROS и, таким образом, использует catkin, я переписываю библиотеку для использования исключительно cmake и пытаюсь применить современный подход CMake .Библиотека структурирована следующим образом:

.
|-- 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

Если я устанавливаю библиотеку и пытаюсь добавить библиотеку в виде простого test_project к цели, я получаю сообщение об ошибке, показывающее, что зависимость Eigen3 не найдена:

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.

К сожалению, версия Eigen, которую я должен использовать, не предоставляет опцию Eigen3Config.cmake, и я вынужден использовать альтернативу cmake FindEigen3.cmake.(Я полагаю, что компиляция более новой версии Eigen3 вручную была бы верной альтернативой, тем не менее, я пытаюсь полностью понять современный подход cmake, который выглядит очень многообещающим для точного избежания таких проблем)

Из всех ресурсов онлайн я не совсемуверен, как транзитивная зависимость обрабатывается в этом случае.Насколько я понимаю, grid_map_coreConfig.cmake должен переслать импортированную зависимость Eigen3.В grid_map_core CMakeLists собственный объект определяется командой find_package(Eigen3 3.2 REQUIRED), а макрос find_dependency просто оборачивает эту точно такую ​​же команду.

Resources

Основной файл CmakeLists.txt выглядит следующим образом:

# 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)

CMakeLists grid_map_core выглядит следующим образом:

# 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 #
###########

и grid_map_coreConfig.cmake следующим образом:

include(CMakeFindDependencyMacro)

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

и CMakeLists test_project.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
  )

Для полноты я добавляю файл FindEigen3.cmake:

# - 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 Ответ

0 голосов
/ 18 марта 2019

Полученное сообщение об ошибке генерируется макросом find_dependency, но не командой find_package, которая вызывается внутренне этим макросом. Поскольку вы вызываете find_dependency с ключевым словом REQUIRED (а это ключевое слово передается на внутренний find_package), единственно возможный сценарий вашей ошибки следующий:

  1. Вызов find_package(Eigen3 REQUIRED) интерпретирует Eigen3 как найдено .
  2. Но когда find_dependency проверяет find_package результаты, он интерпретирует Eigen3 как not found .

Довольно странно, не правда ли?

На самом деле, ваш FindEigen3.cmake скрипт является одним из старых, который устанавливает верхний регистр поток переменной «FOUND», которая обозначает успех скрипта:

# EIGEN3_FOUND - система имеет собственную библиотеку с правильной версией

но правильное имя такой переменной должно быть Eigen3_FOUND (имя пакета должно быть точно таким же, как при вызове find_package(Eigen3) и в имени скрипта FindEigen3.cmake).

Команда find_package проверяет как написание переменной «НАЙДЕНО»: правильную и заглавную. Таким образом, когда сценарий FindEigen3.cmake устанавливает переменную EIGEN3_FOUND с намерением «Я нашел пакет», find_package понимает это намерение, помечая пакет как found .

Но макрос find_dependency проверяет только правильное имя переменной, Eigen3_FOUND. Поскольку эта переменная не устанавливается скриптом FindEigen3.cmake, пакет обрабатывается как not found .

В качестве быстрого исправления вы можете заменить в FindEigen3.cmake сценарии EIGEN3_FOUND на Eigen3_FOUND, и все должно работать. (Ну, такая же замена должна быть сделана на компьютере пользователя. Или вы должны отправить исправленный скрипт FindEigen3.cmake в вашу библиотеку).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...