[25oct2023] inconsistent cmake eigen package
Table of Contents
1. Setup
- library xo-kalmanfilter depends on eigen
- library xo-pykalmanfilter depends on
xo-kalmanfilter
- the project xo incorporates both codebases (along with others) into single umbrella source tree (using git submodules).
- alternatively, can build+install libraries independently (in bottom-up dependency order),
We call this last a 'vanilla build', since it follows standard practice for installing 3rd-party libraries.
In a so-called vanilla build, we use cmake's find_package()
support to acquire each dependency.
In a vanilla build, When cmake builds a library, it obtains its dependencies from their final install location.
Contrast this with the submodule build: here, when cmake build a library, it obtains its dependencies from the build tree
2. Problem
submodule build works as expected; compile flags include required
-Ipath/to/eigen/eigen3
, so c++ code like this compiles#include <Eigen/Dense>
vanilla build fails: when compiling xo-pykalmanfilter, the header path for eigen is given as
-Ipath/to/eigen
instead of-Ipath/to/eigen/eigen3
, so now need this to compile instead:#include <eigen3/Eigen/Dense>
3. Details
xo-kalmanfilter
specifies eigen dependency in the approved manner:# xo-kalmanfilter/src/kalmanfilter/CMakeLists.txt set(SELF_LIB xo_kalmanfilter) .. xo_external_target_dependency(${SELF_LIB} Eigen3 Eigen3::Eigen)
which expands as if we had written:
find_package(Eigen3 CONFIG REQUIRED) target_link_libraries(${SELF_LIB} PUBLIC Eigen3::Eigen)
This works as expected in submodule build. In submodule build, codebases
xo-kalmanfilter
andxo-pykalmanfilter
(amongst others) are incorporated into a single source tree:# xo-sm2/CMakeLists.txt set(XO_SUBMODULE_BUILD True) .. add_subdirectory(repo/xo-kalmanfilter) add_subdirectory(repo/xo-pykalmanfilter)
xo-pykalmanfilter
specifiesxo-kalmanfilter
dependency:# xo-pykalmanfilter/src/pykalmanfilter/CMakeLists.txt set(SELF_LIB pykalmanfilter) .. xo_pybind11_dependency(${SELF_LIB} xo_kalmanfilter)
which expands differently, depending on build type. In submodule build, as if we had written:
target_include_directories(${SELF_LIB} PUBLIC $<BUILD_INTERFACE:${CMAKE_SOURCE_DIR}/repo/xo_kalmanfilter/include>) target_include_directories(${SELF_LIB} PUBLIC $<BUILD_INTERFACE:${CMAKE_BINARY_DIR}/repo/xo_kalmanfilter/include>) target_link_libraries(${SELF_LIB} PUBLIC xo_kalmanfilter)
In vanilla build,
xo_pybind11_dependency()
expands differently:find_package(xo_kalmanfilter CONFIG REQUIRED) .. target_link_libraries(${SELF_LIB} PUBLIC xo_kalmanfilter)
xo-kalmanfilter
provides support for cmakefind_package()
:# xo-kalmanfilter/cmake/xo_kalmanfilterConfig.cmake.in @PACKAGE_INIT@ include(CMakeFindDependencyMacro) find_dependency(reactor) find_dependency(eigen3) include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake") check_required_components("@PROJECT_NAME@")
and generated
xo_kalmanfilterTargets.cmake
file contains:# Create imported target xo_kalmanfilter add_library(xo_kalmanfilter SHARED IMPORTED) set_target_properties(xo_kalmanfilter PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${_IMPORT_PREFIX}/include;${_IMPORT_PREFIX}/include/xo/kalmanfilter" INTERFACE_LINK_LIBRARIES "reactor;Eigen3::Eigen" )
which.. doesn't look wrong :)
Evidence points to an inconsistency in Eigen-provided cmake support, if not in cmake proper.
4. Workaround
Since the eigen dependency isn't propagating, we need to restate it in downstream
libraries, in this case xo-pykalmanfilter
:
# xo_pykalmanfilter/src/pykalmanfilter/CMakeLists.txt set(SELF_LIB pykalmanfilter) ... xo_external_target_dependency(${SELF_LIB} Eigen3 Eigen3::Eigen)