How to use modern CMake best practices in ROS 1?
Hi,
I am trying to improve my CMakeLists.txt by using modern CMake best practices (i.e. using target_include_directories
instead of include_directories
) for a ROS 1 package. I'd like to create a package that works in the devel and install workspace, passes roslint successfully, and of course, can be used with the find_package
command just like any other ROS package.
Can someone provide a toy example to start from that satisfies the requirements above? I've been trying this so far, but I can't get past roslint and I fail to understand what I'm doing wrong.
cmake_minimum_required(VERSION 3.5)
project(my_ros_project)
add_compile_options(
-std=c++14
)
find_package(catkin REQUIRED COMPONENTS
another_ros_package
rosdoc_lite
roslint
)
# A system package
find_package(Eigen3 REQUIRED)
catkin_package(
INCLUDE_DIRS
include
LIBRARIES
${PROJECT_NAME}
CATKIN_DEPENDS
another_ros_package
DEPENDS
EIGEN3
)
# This package
target_include_directories(${PROJECT_NAME} PUBLIC
"$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>"
"$<INSTALL_INTERFACE:include>")
# ROS
target_include_directories(${PROJECT_NAME} SYSTEM PUBLIC ${catkin_INCLUDE_DIRS})
target_link_libraries( ${PROJECT_NAME} PUBLIC ${catkin_LIBRARIES})
add_dependencies(${PROJECT_NAME} ${catkin_EXPORTED_TARGETS})
# Eigen
target_include_directories(${PROJECT_NAME} SYSTEM PUBLIC
"${EIGEN3_INCLUDE_DIR}")
install(
TARGETS ${PROJECT_NAME}
ARCHIVE DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
LIBRARY DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
)
install(
DIRECTORY include/${PROJECT_NAME}/
DESTINATION ${CATKIN_PACKAGE_INCLUDE_DESTINATION}
)
I get the following linter errors and I don't understand what is the issue.
First error:
error: include path 'include' is exported but not used for the build
* You have listed an include path in the INCLUDE_DIRS stanza of
* the catkin_package() command, but that path is not mentioned in
* any include_directories() call.
* You can ignore this problem with --ignore unused_include_path
I can simply remove the include from INCLUDE_DIRS to make that linter error disappear, but that seems wrong.
Second error:
CMakeLists.txt(39): error: target_include_directories() exports external PUBLIC path '${catkin_INCLUDE_DIRS}'
* You have exported an include path as part of your interface for
* dependent targets, but that path does not belong to your
* package. You probably meant to use the PRIVATE scope if you
* merely need that include path for your build. If you actually
* wanted to export the path to your dependees, you hould be aware
* that target properties will be exported as fixed strings,
* meaning your installed package will break if that location
* changes for any reason. A more robust way would be to use
* target_link_libraries(PUBLIC) with the proper imported target
* instead.
* You can ignore this problem with --ignore external_interface_path
While using modern CMake is certainly a good idea, I'm wondering whether it makes sense in this specific context. Catkin was mostly written when there was only "old CMake", meaning it does a couple of things behind-the-scenes which CMake these days does itself (processing and gathering all the include and link libraries, exporting them, passing them on to dependees, etc).
Managing the difference between
devel
andinstall
spaces is also partly taken care of for you among other things (via the extras-files fi).Using
target_link_libraries(..)
andtarget_include_directories(..)
withPUBLIC
andPRIVATE
is of course nice, but Catkin will not export your CMake targets, nor will CMake (as you don't have an EXPORT), so that information doesn't get to downstream consumers (unless something has recently changed).Mixing modern CMake with this is possible, but seems 'unnecessary' and makes your
CMakeLists.txt
harder to read, while the gains seem minimal.The primary motivation for me to use modern CMake is the greater isolation and the fact we get relocatable packages. Building with
catkin_tools
orcatkin_make_isolated
also results in isolated builds. Relocatable packages are sort-of achieved by Catkin already (although you'll find lots of hard-coded absolute paths everywhere), and using modern CMake doesn't solve the remaining problems.Again: I'm not saying you shouldn't do this -- after all, that's your decision. I just wanted to take a step back and evaluate the pros-cons.
re:
roslint
: do you meancatkin_lint
? Because the errors you show are from the latter I believe.From the looks of it,
catkin_lint
is just confused by your use of "non standard" things liketarget_include_directories(..)
, and the second error seems to reinforce what I wrote in my first comment: mixing Catkin-CMake and modern CMake is non-trivial, especially when using CMake targets (andINTERFACE
libraries, etc).I guess that's what I thought, but I still wondered if there was a better way of packaging ROS 1 packages than using old CMake. Thanks for the feedback.
Well realistically, what would you gain by using modern CMake? In this specific context I mean.
ROS 1 packages primarily use Catkin, and as long as you stay "inside" that environment, things "just work" (and if they don't, there are probably known work-arounds).
Catkin is in maintenance mode, so I don't believe it will see any major improvements which could make it more amenable to modern CMake any more.
On the ROS 2 side things are different, and Ament is much more recent, and there's work to slowly migrate away from customisations and rely on standard CMake as much as possible. It's not going to go fast though.
I'm not trying to gold plate the situation btw. I'm mostly curious as to your motivations. If your motivation was "I'd like to see what's possible", that would be a perfectly fine one of course.
I think what I would gain is to be able to reduce the scope of what is included per target instead of everything inside the project (i.e. target vs directory scope) and specify PRIVATE/PUBLIC/INTERFACE keywords.
If you're ok with ignoring
catkin_lint
warnings/errors about this, then that should be perfectly possible.Catkin will not interfere with any of that.