Description
As a follow up of old closed issues:
- ament_export_dependency_with_components #282
- no way to ament_export a package with its components #199
Problem
Let's consider the following example:
- Package A creates libA, which depends on PCL
common
andio
components, and on Boostthread
- Package B creates binB, which depends on libA
On Package A, we would have something like:
- package.xml
<depend>libpcl-common</depend>
<depend>libpcl-io</depend>
<depend>libboost-thread</depend>
- CMakeLists.txt
find_package(PCL REQUIRED COMPONENTS common io)
find_package(Boost REQUIRED COMPONENTS thread)
add_library(libA
# ...
)
# include/link found PCL and Boost components
ament_target_dependencies(libA PCL Boost)
# ament_export_targets()
# install(TARGETS libA...)
# ...
# export components for downstream?
ament_export_dependencies(PCL Boost)
ament_package()
On package B, we would have:
- package.xml:
<depend>packageA</depend>
- CMakeLists.txt:
find_package(packageA REQUIRED)
add_executable(binB
# ...
)
# link libA target to binB
ament_target_dependencies(binB packageA)
ament_package()
As reported in other issues, this does not work. Essentially, what ament_export_dependencies(PCL Boost)
does is to add a find_package(PCL)
and find_package(Boost)
in downstream packages. find_package(PCL)
includes all PCL components, so "it will work", even though it will also bloat downstream packages with useless PCL components. On the other hand, find_package(Boost)
does not include the thread
component, and the downstream package will be broken.
The alternative is not to use ament_export_dependencies()
in packageA and instead fetch PCL and Boost components manually downstream, in such case package B would look like this:
- package.xml:
<depend>packageA</depend>
<depend>libpcl-common</depend>
<depend>libpcl-io</depend>
<depend>libboost-thread</depend>
- CMakeLists.txt:
find_package(packageA REQUIRED)
find_package(PCL REQUIRED COMPONENTS common io)
find_package(Boost REQUIRED COMPONENTS thread)
add_executable(binB
# ...
)
# link libA target to binB
ament_target_dependencies(binB
packageA
PCL
Boost
)
ament_package()
Although this works and does exactly what is required, it makes no sense for downstream packages to have to fetch upstream dependency themselves.
Solution
The solution is to let the user export components. Previous issues have suggested a new ament_export_dependency_with_components()
macro, but I think the functionality can be added to the existing ament_export_dependencies()
macro.
For example:
ament_export_dependencies(PCL COMPONENTS common io)
ament_export_dependencies(Boost COMPONENTS thread)
Example implementation
Current implementation accepts syntax ament_export_dependencies(depA depB depC...)
. The macro call appends all these dependencies to _AMENT_CMAKE_EXPORT_DEPENDENCIES
(e.g. "prevDepX;depA;depB;depC"
). Later, when the package B calls find_package(PackageA)
, find_package()
is called on each listed dependency.
There could be a new syntax ament_export_dependencies(depA COMPONENTS compA compB...)
. When COMPONENTS
keyword is used, the dependency components would be packed and appended to _AMENT_CMAKE_EXPORT_DEPENDENCIES
using a special separator character (e.g. :
). For example: prevDepX;depA:compA:compB
. Then, when someone calls find_package(PackageA)
, the list of components would be unpacked into a find_package(depA QUIET REQUIRED COMPONENTS compA compB)
.
Note that when the COMPONENTS
keyword is used, only 1 dependency would be allowed per call. The 2 syntaxes would remain valid:
# legacy export
ament_export_dependencies(depA depB depC)
# new component export
ament_export_dependencies(PCL COMPONENTS common io)
ament_export_dependencies(Boost COMPONENTS thread)