UP | HOME

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

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)

Author: Roland Conybeare

Created: 2024-09-08 Sun 18:14

Validate