org-howto/articles/2023/10/inconsistent-eigen-package.org
Roland Conybeare 3b66752c5c
All checks were successful
Deploy / publish (push) Successful in 14s
bugfix: fix broken forgejo links
2026-06-18 08:32:16 -04:00

5.2 KiB

[2023] inconsistent eigen package

#

#

#

#

#

#

TL;DR

Eigen cmake support seems to be inconsistent

Setup

  • library xo-kalmanfilter depends on eigen
  • library xo-pykalmanfilter depends on xo-kalmanfilter
  • the project xo-umbrella2 incorporates both codebases (along with others) into a single source tree (using git subrepo).
  • 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

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>

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 and xo-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 specifies xo-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 cmake find_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.

Workaround

It's sufficient to restate the eigen dependency in xo-pykalmanfilter:

  # xo_pykalmanfilter/src/pykalmanfilter/CMakeLists.txt
  set(SELF_LIB pykalmanfilter)
  ...
  xo_external_target_dependency(${SELF_LIB} Eigen3 Eigen3::Eigen)