Compiling againt another version of Eigen using CMake - cmake

Am using ubuntu 16 which seems automatically linking against Eigen version 3.2.92 located at /usr/include/Eigen3. I would like to link against version 3.2.0. Thus my questions is
How could I get Eigen version 3.2.0? It is not clear from Eigen website
What I did so far is just copying /usr/include/Eigen3 from an ubuntu 14 machine, since the latter automatically comes with version 3.2.0
How to link against it using CMake?
Tried
SET (EIGEN3_INCLUDE_DIR "/home/usr/mylib/eigen/eigen3/Eigen") but without success.
For info, am using ROS (Kinetic) catkin. It happens that catkin somehow forces the development packages to linking/compiling against packages installed by default (/usr/include/..)

Other versions of Eigen are available on the website or better, from the hg repo.
How is the EIGEN3_INCLUDE_DIR used in your cmake file? For example, in one of my projects, we have set(EIGEN_INCLUDE_DIR ${SOURCE_DIR}/Common). Note that it doesn't have the "3" (it's just a variable name) and that it doesn't include the last "/Eigen" in the path.

First of all, Eigen is a header-only library, so you are not linking against it :-) Instead you want to use specific header files.
For your own packages, you can use include_directories(SYSTEM ${EIGEN3_INCLUDE_DIR}, assuming you set EIGEN3_INCLUDE_DIR correspondingly to the version you want to use. Beware that if that version differs too much (e.g. 2.x vs 3.x) with versions used by interfaces (e.g. tf library?), this may cause some issues if datatypes changed. You also need to make sure that no other directive overwrites that - best to check the parameters to g++ for that.

Related

Best practices to build vendored code with CMake

I'm trying to understand what some of the best practices are when using modern CMake (3.13+) with respect to building and including vendored or submoduled code.
Say I'm building a library MyLib. My file structure is something like this
MyLib
|-CMakeLists.txt
|-src
|-include
|-submodules
|-libgeos
In this example, I've included libgeos as a git submodule, because it's really convenient to be able to clone the project and immediately build and run tests because that dependency is present. This could also be solved by using FetchContent or something, and my question still stands; the important thing is that I do not want to rely on libgeos being installed in build environment.
Note I picked libgeos arbitrarily; I have no idea if libgeos is set up as a cmake project appropriately for this example, but this is all theoretical and I just needed some concrete library name. Please do not use the specific details of how libgeos is configured to answer this, unless libgeos is a good example of conventional cmake.
But now, there's some other project that wants to use my project, and it needs libgeos and doesn't want to depend on my project providing it.
OtherProject
|-CMakeLists.txt
|-src
|-include
|-submodules
|-libgeos
|-MyLib
|submodules
|-libgeos
When you clone OtherProject, you get two versions of libgeos, and maybe that's not great; but it's not a huge issue either. And maybe they're not the same version; say MyLib requires libgeos >= 2.0, so 2.0 is what MyLib includes, and OtherProject requires libgeos>=2.1 so OtherProject includes libgeos >= 2.1.
Now we potentially end up with some build issues. If we have the following line in OtherProject/CMakeLists.txt
add_subdirectory(submodules/libgeos)
and then again, that same line within MyLib/CMakeLists.txt, we end up with cmake errors because libgeos as a target is defined twice in the build. This can be solved a couple of ways.
Check if geos exists before adding it
if(NOT TARGET geos)
add_subdirectory(submodules/libgeos)
endif()
But this case has some issues; if that blob is in OtherProject at the top, it's fine and both projects use libgeos 2.1. But if it's in OtherProject after add_subdirectory(submodules/MyLib), then the geos 2.0 version gets added to the build, which may or may not fail loudly (Hopefully it would).
This could also be solved with find_package. Both projects include cmake/FindGeos.cmake which use that blurb above (if(NOT TARGET...)) to add geos the build and then the top project cmake files can do this
list(APPEND CMAKE_MODULE_PATH cmake)
find_package(geos 2) # (or 2.1)
then it doesn't matter what order they try to include geos, because they will both defer to FindGeos.cmake in OtherProject because it's first in the module path.
But now there's a new issue, some ThirdProject wants to use MyLib also, but ThirdProject wants to depend on libgeos which is in the system environment. It uses find_package(geos 2.1 CONFIG) to use the installed GeosConfig.cmake file, which adds geos::geos to the build and sets geos_FOUND. Suddenly, MyLib fails to build, because geos_FOUND was set, but I'm doing target_link_library(mylib PUBLIC geos).
So this could be solved by adding add_library(geos::geos ALIAS geos) in both custom FindGeos.cmake files, then it doesn't matter if geos was built from source or using the installed version, the target names are the same either way.
Now we get to my actual questions:
Lets start with
Am I crazy, no one does this, and my team is trying to use cmake all wrong?
Is there some feature of cmake that I've just completely missed that solves all these problems?
I suspect there's a good few books or presentations that cover this topic, but I just don't know where to look because there's so many; what should I be looking at? I've seen the CMake Packages page, which looks like it solves the problem when you're using all projects which are configured according to that page; but it doesn't really answer how to bridge the gap between older and newer projects.
If I'm not crazy and there's no straightforward answer or presentation that I can look at, then
What should the cmake configuration for both MyLib and libgeos look like so that these cases work?
MyLib is built alone
MyLib is built as part of a larger project which provides a different version of geos
MyLib is built as part of a larger project which depends on a different version of geos in the environment
I understand that cmake provides helpers that could be used to produce MyLibConfig.cmake if I wanted to install it in the environment. I also see that the export() function exists, which could be used to save those files in the build tree somewhere and then find them with find_package in config mode. But this feels a bit odd to me to do because it's not a multi-stage build, it's just one invocation of cmake then make.
But lets say that's the right answer and the CMake for libgeos doesn't follow it. Would it be appropriate to have FindGeos.cmake do something like this?
if(NOT geos_FOUND)
add_subdirectory(submodules/libgeos)
export(geos NAMESPACE geos)
find_package(geos CONFIG)
endif()

pkg_check_modules can't find different versions of same library

when using pkg-config in CMake through the find_package(PkgConfig) if I have multiple versions of the same library in different locations, the command stops at the first (wrong) one if I ask for a specific version, instead of searching along the other paths in the $PKG_CONFIG_PATH variable.
For instance, if my $PKG_CONFIG_PATH is /opt/lib/pkgconfig:/usr/local/lib/pkgconfig and I have Eigen 3.2.6 installed in /opt/lib/pkgconfig and Eigen 3.2.10 in /usr/local/lib/pkgconfig
then if I do:
find_package(PkgConfig)
pkg_check_modules(EIGEN eigen3=3.2.10 REQUIRED)
cmake stops with an error because it finds Eigen 3.2.6 first.
How to solve this?
Thanks.

Upgrading cmake in Yocto

I'm using Yocto and struggling to update cmake. The poky version that we are using provides cmake version 3.3.1, but one of the packages that I'm building requires cmake version 3.5 or greater.
Looking at the poky git repo, the latest provides version 3.8.2. I figured that the easiest way to upgrade cmake was to include the recipes for cmake from the current poky master branch within my own meta repo, in order to override the lesser version. So I copied the cmake directory, and expected the build to continue along...
Unfortunately, the cmake recipes that I copied in aren't working. The recipes won't even load, as they throw an error, saying "docker must contain prefix as its prefix." Printing the output of the prefix and docdir variables to the console, I see that docdir is set to "${datadir}/doc" -- the datadir variable is not expanded.
In summary, the questions that I have are:
What is the best way to upgrade cmake? Is the best way to copy in the updated recipes into my own meta repo?
Why isn't the datadir variable being expanded, and how can I fix that?
What is probably happening is that the recipe for cmake 3.8.2 (2.4) is not backward compatible with the poky version from 3.3.1 (Version 1.9). The reference is here. I guess some refactoring and important milestones happened meanwhile
The easiest way is to upgrade the whole poky folder in your ecosystem, hoping it is not breaking your other recipes.

Detect current CMake version using CMake

I am working on a project that uses CMake. The top CMakeLists.txt file contains the following line:
cmake_minimum_required(VERSION 3.7.2) # Kittens will die if you switch to an earlier version of CMake. We suggest using CMake 3.8.0.
I want to force all developers to switch to CMake 3.8.0, but for some reasons, not all developers have administration rights and are not able to switch from 3.7.2 to 3.8.0 immediately. Actually, we do not need any new features of version 3.8.0, but our policy is to use always the newest and greatest tools to prevent "porting up" problems in the future - for instance switching fast from Qt4 to Qt5 was a good decission in the past - I know switching always to the newest libraries and tools has also some drawbacks as discussed here, but we want to do it this way.
Because of this, instead of forcing everyone to use version 3.8.0, I'd like to output a warning message if CMake 3.7.2 is used. Somehow like this:
# not working - just pseudocode
if(CMAKE_VERSION == "3.7.2")
message("Please consider to switch to CMake 3.8.0")
endif()
I tried to read the VERSION variable, but this does not work. Does anyone now how this check can be achieved?
There exist a few variables for that, described here:
CMAKE_MAJOR_VERSION
major version number for CMake, e.g. the "2" in CMake 2.4.3
CMAKE_MINOR_VERSION
minor version number for CMake, e.g. the "4" in CMake 2.4.3
CMAKE_PATCH_VERSION
patch version number for CMake, e.g. the "3" in CMake 2.4.3
Also, the variable CMAKE_VERSION contains the string for the version.
In your case, you would, for instance, use the following:
if(${CMAKE_VERSION} VERSION_LESS "3.8.0")
message("Please consider to switch to CMake 3.8.0")
endif()
Other comparison operators are VERSION_EQUAL and VERSION_GREATER.

How to cross compile CMake for ARM with CMake

In short I'm trying to cross compile CMake with CMake, and I don't think I'm linking libraries correctly. What I want to do may not be possible, but I'd at least like to know why it isn't possible if that's the case.
System: The host is a Linux box with a Cavium ARM9 CPU. It's currently running version 2.6.24.4 of the Linux kernel and Debian 5.0 (Lenny). My workstation is a Core i5 running Ubuntu 12.04 LTS (Precise Pangolin).
My overall goal is to get ROS running on the Linux box. I have to compile from source rather than use apt since Debian 6.0 (Squeeze) binaries require thumb support that the Cavium does not give, and not many of the needed packages are available for Debian 5.0 (Lenny). I'd made progress installing the various libraries needed, but when I got to step 1.3.1 and tried to run CMake, I got the error
CMake 2.8 or higher is required. You are running version 2.6.0
Next I tried to download and build CMake 2.8.8 on the Linux box itself, but it was too much for the system. When that failed, I downloaded the toolchain suggested on the manufacturer's website and used the cross-compiling guide at [www.cmake.org/Wiki/CMake_Cross_Compiling] to build the CMake executables. Here is my toolchain file:
# This one is important
SET(CMAKE_SYSTEM_NAME Linux)
# Specify the cross compiler
SET(CMAKE_C_COMPILER /pathto/crosstool-linux-gcc-4.5.2-gclibc-2.9-oabi/arm-unknown-linux-gnu/bin/arm-unknown-linux-gnu-gcc)
SET(CMAKE_CXX_COMPILER /pathto/crosstool-linux-gcc-4.5.2-gclibc-2.9-oabi/arm-unknown-linux-gnu/bin/arm-unknown-linux-gnu-g++)
# Where is the target environment
SET(CMAKE_FIND_ROOT_PATH /pathto/crosstool-linux-gcc-4.5.2-gclibc-2.9-oabi/arm-unknown-linux-gnu /pathto/crosstool-linux-gcc-4.5.2-gclibc-2.9-oabi/arm-unknown-linux-gnu/arm-unknown-linux-gnu)
# Search for programs in the build host directories
SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
# For libraries and headers in the target directories
SET(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
SET(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
However, use of the binary on the Linux box gives the error
cmake: /usr/lib/libstdc++.so.6: version `GLIBCXX_3.4.14' not found (required by cmake)
Sure enough, the library is not there:
prompt# strings /usr/lib/libstdc++.so.6 | grep GLIBC
GLIBCXX_3.4
GLIBCXX_3.4.1
GLIBCXX_3.4.2
GLIBCXX_3.4.3
GLIBCXX_3.4.4
GLIBCXX_3.4.5
GLIBCXX_3.4.6
GLIBCXX_3.4.7
GLIBCXX_3.4.8
GLIBCXX_3.4.9
GLIBCXX_3.4.10
GLIBC_2.3
GLIBC_2.0
GLIBC_2.3.2
GLIBC_2.1
GLIBC_2.1.3
GLIBC_2.2
GLIBCXX_FORCE_NEW
GLIBCXX_DEBUG_MESSAGE_LENGTH
I've never cross-compiled before, but I can see one of two scenarios happening: either the binary got created with a link to a higher version of glibcxx on the host machine or the manufacturer's toolchain is more modern than their image. I don't know how to check which is happening or if something else is happening that I don't know about.
My last effort involved trying to statically cross-compile CMake to hopefully get rid of the linking error with
cmake -DCMAKE_TOOLCHAIN_FILE=../toolchain-technologic.cmake -DBUILD_SHARED_LIBS=OFF -DCMAKE_BUILD_TYPE=Release -DCMAKE_EXE_LINKER_FLAGS_RELEASE="-static" ..
I got build errors, and that binary didn't work either. I got:
FATAL: kernel too old
Segmentation fault
I'd try installing glibcxx 3.4.14 on the Linux box, but it doesn't look like it's available for this processor.
I've tried searching for CMake dependencies or system requirements and can't find anything. I've also searched on how to build CMake, but most searches turn up how to build other things with CMake rather than building CMake itself.
I do cross-compile a lot for ARM9 devices using CMake, and indeed this looks like you're not linking to the same libs you have on your target device. You shouldn't need to build CMake yourself to get this done, since it does have good support for cross-compiling since version 2.6. Just make sure you set the CMAKE_FIND_ROOT_PATH variable to a path where you have an exact copy of the root filesystem you have on your target device (with libraries and binaries pre-compiled for the target processor). That should solve your problems.
As a sidenote, I like to use crosstool-ng for building my cross-compilers. It is a really nice tool which helps you to build them from scratch, so I try to match the compiler version and glibc to the ones originally used to build the root filesystem (I usually start with a ready made root filesystem from ARMedslack, since I use Slackware for my development box and ARMedslack for my ARM targets).