# [ROS2] rclcpp linking error and correct way to handle dependencies

Hi, I'm trying to migrate my ROS 1 libraries to ROS 2 and have some trouble with setting up the packages.

I read the ament CMake documentation and this thread, but I'm still not sure what the recommended way to deal with dependencies is. It would be great if someone could give feedback on the CMake files, e.g. if there are missing or superfluous lines or something should be done differently.

At the moment, colcon build fails with the following error:

Starting >>> bridge
--- stderr: bridge
collect2: error: ld returned 1 exit status


CMakeLists.txt of package bridge:

cmake_minimum_required(VERSION 3.5)
project(bridge)
find_package(ament_cmake REQUIRED)
find_package(rclcpp REQUIRED)
find_package(rl2_com REQUIRED)
find_package(bridge_support REQUIRED)

ament_target_dependencies(bridge rclcpp rl2_com bridge_support)

install(TARGETS
bridge
DESTINATION lib/${PROJECT_NAME}/ ) ament_package()  CMakeLists.txt of package bridge_support (header-only library): cmake_minimum_required(VERSION 3.5) project(bridge_support) find_package(ament_cmake REQUIRED) install( DIRECTORY include/ DESTINATION include ) ament_export_include_directories(include) ament_package()  CMakeLists.txt of package rl2_com: cmake_minimum_required(VERSION 3.5) project(rl2_com) find_package(ament_cmake REQUIRED) find_package(rl2_logging REQUIRED) include_directories(include) add_library(rl2_com src/com.cpp) target_link_libraries(rl2_com rl2_logging::rl2_logging) ament_target_dependencies(rl2_com rl2_logging) ament_export_dependencies(rl2_logging) ament_export_interfaces(export_rl2_com HAS_LIBRARY_TARGET) install( DIRECTORY include/ DESTINATION include ) install( TARGETS rl2_com EXPORT export_rl2_com LIBRARY DESTINATION lib ARCHIVE DESTINATION lib RUNTIME DESTINATION bin INCLUDES DESTINATION include ) ament_package()  CMakeLists.txt of package rl2_logging: cmake_minimum_required(VERSION 3.5) project(rl2_logging) find_package(ament_cmake REQUIRED) find_package(rclcpp REQUIRED) include_directories(include) add_library(rl2_logging src/log.cpp) target_link_libraries(rl2_logging rclcpp) ament_target_dependencies(rl2_logging rclcpp) ament_export_dependencies(rclcpp) ament_export_interfaces(export_rl2_logging HAS_LIBRARY_TARGET) install( DIRECTORY include/ DESTINATION include ) install( TARGETS rl2_logging EXPORT export_rl2_logging LIBRARY DESTINATION lib ARCHIVE DESTINATION lib RUNTIME DESTINATION bin INCLUDES DESTINATION include ) ament_package()  The package.xml files contain <depend>XXXXXX</depend> directives for the required packages. Please note: For better readability, I omitted the following lines in each CMakeLists.txt: # Default to C99 if(NOT CMAKE_C_STANDARD) set(CMAKE_C_STANDARD 99) endif() # Default to C++17 if(NOT CMAKE_CXX_STANDARD) set(CMAKE_CXX_STANDARD 17) endif() if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") add_compile_options(-Wall -Wextra -Wpedantic) endif()  edit retag close merge delete ## 1 Answer Sort by » oldest newest most voted (I should preface this by saying that I stick with more of a vanilla CMake approach instead of using the ament CMake macros.) I think the main problem is that rclcpp isn't a valid library name for target_link_libraries. You'll have to use ${rclcpp_LIBRARIES} instead, which gets evaluated to the names of the libraries exported by the rclcpp package.

You might also run into issues finding the correct headers for each package. As written, include_directories(include) just finds the headers in the current package but not the headers in the packages you get through find_package(). I think that the preferred "modern CMake" way to do this is to use target_include directories for each library and executable. This also lets you use generator expressions to find the correct include directory in different build and install configurations.

As a side note, the header files for your package should be in a sub-directory of the include directory with a name matching the package name. For example, include/rl2_logging/some_header.h.

For your rl2_logging example, these tweaks would look like this:

cmake_minimum_required(VERSION 3.5)
project(rl2_logging)
find_package(ament_cmake REQUIRED)
find_package(rclcpp REQUIRED)

target_link_libraries(rl2_logging ${rclcpp_LIBRARIES}) target_include_directories(rl2_logging PUBLIC "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>" "$<INSTALL_INTERFACE:include>"
${rclcpp_INCLUDE_DIRS} ) ament_target_dependencies(rl2_logging rclcpp) ament_export_dependencies(rclcpp) ament_export_interfaces(export_rl2_logging HAS_LIBRARY_TARGET) install( DIRECTORY include/${PROJECT_NAME}/
DESTINATION include/\${PROJECT_NAME}/
)

install(
TARGETS rl2_logging
EXPORT export_rl2_logging
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib
RUNTIME DESTINATION bin
INCLUDES DESTINATION include
)

ament_package()

more

Thanks! That did the trick.

I see that this approach allows finer control over what is linked against what (compared to catkin), but I can imagine that it's more difficult to understand for researchers who are not very familiar with CMake.

( 2019-10-16 02:20:56 -0500 )edit