How to cleanly handle dependencies in ament?
If I build a library to be used by other ROS 2 developers, what is the way to make it easy to use for downstream packages?
In modern CMake one answer should be exporting targets. This would then allow downstream packages to easily use that target via e.g. find_package(some_dependency REQUIRED)
and target_link_libraries(new_target [PUBLIC | PRIVATE | INTERFACE] some_dependency::some_dependency)
and if the author of some_dependency
has done things right, that's all I have to do as the author of my_target
.
Ament_cmake provides help for the exporting mechanism using install(TARGETS my_dependency EXPORT some_dependency ...)
and ament_export_interface(some_dependency)
(which does the heavy lifting if I'm right). Now if some_dependency
itself depends on some_other_dependency
, it seems that this dependency must have been added to ament_export_dependencies()
for the target some_dependency
even if it is not used in any header and could be linked privately. Otherwise I would have to find_package(some_other_dependency)
in the build of new_target
, which is wrong. This might be an artifact of the problem that it seems impossible to use target_link_libraries
together with keywords PUBLIC | PRIVATE | INTERFACE
as they are not used internally in ament (at least that's what CMake tells me).
However, there is also ament_target_dependencies()
and ament_export_dependencies()
and they seem to provide another way of handling dependencies: If I have a ROS 2 package where the export target (if it exists) does not have the same name as the target (then, for some reason, it seems I have to use target_link_libraries
, see below for an example), I can just use ament_target_dependencies(new_target some_dependency)
. If the dependency is used in a header and therefore needs to be linked publicly/by interface, I additionally have to add ament_export_dependency(some_dependency)
(in contrast to how I have to use it with exported targets, see above).
What's the preferred way of doing things and how to do this way right?
Some real world examples:
- Currently, rosbag2 relies entirely on the second mechanism. See https://github.com/ros2/rosbag2/blob/... for an example (no
target_link_libraries
, no EXPORT, etc.) - RViz relies on a mix of using targets and not (see for instance https://github.com/ros2/rviz/blob/42e... , but the CMakeLists should be cleaned up...).
- When trying to clean this up I found: For the
resource_retriever
, I have to usetarget_link_libraries
, forament_index_cpp
, I can also useament_target_dependencies
. The only difference between the two is that the name of the EXPORT target inament_index_cpp
is different from the target, while it is the same forresource_retriever
(see https://github.com/ros/resource_retri... vs https://github.com/ament/ament_index/... )