В настоящее время я работаю над внедрением библиотеки ROS в программный стек нашей компании. Поскольку библиотека основана на ROS и, следовательно, использует сережку, я переписываю библиотеку для использования исключительно 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
просто обертывает эту же самую команду.
Ресурсы
Основной 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)
Grid_map_core CMakeLists выглядит следующим образом:
# 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.cm сделать следующим образом:
include(CMakeFindDependencyMacro)
find_dependency(Eigen3 REQUIRED)
include("${CMAKE_CURRENT_LIST_DIR}/grid_map_coreTargets.cmake")
и CMakeLists.txt test_project
:
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, <[email protected]>
# Copyright (c) 2008, 2009 Gael Guennebaud, <[email protected]>
# Copyright (c) 2009 Benoit Jacob <[email protected]>
# 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)
find_dependency()
call не может найти вашFindEigen3.cmake
скрипт. Где находится этот скрипт? - person Tsyvarev   schedule 14.03.2019FindEigen3.cmake
является частью CMake, но пусть будет так. Макросfind_dependency
вызываетfind_package()
внутри, и последний должен вывести полезное сообщение в случае сбоя. Но я не вижу этого сообщения в вашем выводе. Если в вашем тестовом проекте используется ключевое слово QUITE при поискеgrid_map
, удалите это ключевое слово. - person Tsyvarev   schedule 14.03.2019FindXxx.cmake
скрипты, которые задают результат через переменнуюXXX_FOUND
вместоXxx_FOUND
. В то время как командаfind_package
может проверять оба написания переменной,find_dependency
проверяет толькоXxx_FOUND
один. Мне любопытно, устанавливает ли ваш скриптFindEigen3.cmake
переменнуюEIGEN3_FOUND
. (Более подозрительно: еслиfind_package()
вызывается с ключевым словом REQUIRED и терпит неудачу, то он должен немедленно завершить работу CMake. Но ваш вывод послеfind_package
вызова. ) - person Tsyvarev   schedule 14.03.2019FindEigen3.cmake
является частью cmake, но я считаю, что это была часть Eigen версии 3.2.2. до того, как они начали делать это «правильно» и выпускатьEigen3Config.cmake
. Я не на офисном ПК, поэтому я не могу слишком много исследовать сам. Тем не менее, поскольку библиотека собирается, файл находится как минимум в одном проекте, и остается вопрос, почему его нельзя найти в тестовом проекте. Я предоставлюtest_project
CMakeLists.txt
как можно скорее, но на самом деле просто найти библиотекуgrid_map
и связать ее. - person Beat Scherrer   schedule 14.03.2019find_package(Eigen3 REQUIRED)
считает, что переменнаяEIGEN3_FOUND
имеет значение TRUE, и интерпретирует Eigen3 как найденное, поэтому сборка вашей библиотеки прошла успешно. Такой же (или похожий) вызов выполняется при поиске в вашей библиотеке, и сноваfind_package(Eigen3 REQUIRED)
интерпретирует Eigen3 как найденный. Но после возвратаfind_dependency(Eigen3 REQUIRED)
проверяет переменнуюEigen3_FOUND
(на случай!). Поскольку эта переменная не установлена,find_dependency
интерпретирует Eigen3 как не найденный. - person Tsyvarev   schedule 14.03.2019find_package(Eigen3 REQUIRED)
вместоfind_dependency()
, когда снова вернусь на рабочую станцию. Очень расстраивает то, что для cmake так мало шаблонов и ресурсов, в отличие от C++. - person Beat Scherrer   schedule 14.03.2019target_link_libraries(PUBLIC Eigen3)
, поскольку eigen является только заголовком. Таким образом, использованиеfind_package()
вgrid_mapConfig.cmake
работает. При использованииfind_dependency()
возникает та же исходная ошибка. - person Beat Scherrer   schedule 18.03.2019FindEigen3.cmake
, который вы используете. Хотя бы ссылку на него добавь. Поскольку Eigen3 предназначен только для заголовка, почему вы когда-либоfind_dependency
для него? Включают ли ваши общедоступные заголовки (которые доступны для пользователя вашей библиотеки) заголовки Eigen3? - person Tsyvarev   schedule 18.03.2019find_package(Foo 1.0)
target_link_library(target PUBLIC/PRIVATE Foo::Foo)
, даже если библиотека является только заголовком. Но посколькуFindEigen3.cmake
не поддерживает эту парадигму, мне нужно только добавить собственные заголовки в мои общедоступные заголовки, и я могу забыть о зависимости. Итак, возможно, новейшая версия Eigen поддерживает эту современную парадигму cmake, которая мне очень нравится и которую я хотел адаптировать в этом случае (где она явно не работает таким образом). - person Beat Scherrer   schedule 18.03.2019find_dependency
требуется, чтобы библиотека существовала на компьютере пользователя. Это абсолютно необходимо для обычных (разделяемых) библиотек, потому что без них ваша библиотека вообще бы не работала. Но если ваша библиотека использует какой-то заголовок только внутренне (в своей реализации, но не в общедоступном интерфейсе), нет смысла требовать, чтобы эта библиотека только заголовка была установлена на компьютере пользователя. Все заголовки из него уже скомпилированы в вашу библиотеку. - person Tsyvarev   schedule 18.03.2019GridMap
, поэтому она является «зависимостью интерфейса» и должна существовать на компьютере пользователя, поэтому установка зависимости от eigen была бы естественной (даже если это только заголовок). - person Beat Scherrer   schedule 18.03.2019