Background
- Upstream pkg (called rclcpp_sample) provides a library-a.
- Downstream pkg (called rclcpp_child_sample) provides an executable that depends on library-a.
- Both are
ament-package
s (i.e. written withinament
framework), to be built withcolcon
.
To avoid a persistent linker error (very similar to robotics.stackexchange.com#23995 but I wouldn't say the same), the solution I came up with was to target_link_libraries
the target that the upstream pkg exports.
target_link_libraries(${app_derived} rclcpp_sample::rclcpp_sample)
Questions
Q1. Why
Common error I got was:ament_target_dependencies
can't link to a resource in anotherament
package?# colcon build --packages-select rclcpp_sample rclcpp_child_sample Starting >>> rclcpp_sample Finished <<< rclcpp_sample [1.46s] Starting >>> rclcpp_child_sample --- stderr: rclcpp_child_sample /usr/bin/ld: CMakeFiles/app_derived.dir/src/exec.cpp.o: in function `void __gnu_cxx::new_allocator<rclcpp_sample::DerivedModule>::construct<rclcpp_sample::DerivedModule, char const (&) [9]>(rclcpp_sample::DerivedModule*, char const (&) [9])': exec.cpp:(.text._ZN9__gnu_cxx13new_allocatorIN13rclcpp_sample13DerivedModuleEE9constructIS2_JRA9_KcEEEvPT_DpOT0_[_ZN9__gnu_cxx13new_allocatorIN13rclcpp_sample13DerivedModuleEE9constructIS2_JRA9_KcEEEvPT_DpOT0_]+0x79): undefined reference to `rclcpp_sample::DerivedModule::DerivedModule(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >)' collect2: error: ld returned 1 exit status make[2]: *** [CMakeFiles/app_derived.dir/build.make:132: app_derived] Error 1 make[1]: *** [CMakeFiles/Makefile2:78: CMakeFiles/app_derived.dir/all] Error 2 make: *** [Makefile:141: all] Error 2 --- Failed <<< rclcpp_child_sample [2.67s, exited with code 2]
Q2. Why the exported macro is duplicating the element as
rclcpp_sample::rclcpp_sample
?rclcpp_sample
is the namespace I use but the duplication of it is never defined anywhere in the upstream.
More background
Full code of 2 packages can be seen in prooving_grounds!2 if that's quicker.
Upstream pkg
CMakeLists.txtcmake_minimum_required(VERSION 3.8)
project(rclcpp_sample)
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
add_compile_options(-Wall -Wextra -Wpedantic)
endif()
# BEGIN: From ROS2 tutorial https://docs.ros.org/en/galactic/Tutorials/Beginner-Client-Libraries/Writing-A-Simple-Cpp-Publisher-And-Subscriber.html
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()
# END From ROS2 tutorial
set(THIS_PACKAGE_INCLUDE_DEPENDS
ament_cmake
rclcpp
)
foreach(Dependency IN ITEMS ${THIS_PACKAGE_INCLUDE_DEPENDS})
find_package(${Dependency} REQUIRED)
endforeach()
add_library(${PROJECT_NAME} SHARED
src/impl.cpp
)
target_include_directories(
${PROJECT_NAME}
PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>)
#target_link_libraries(${PROJECT_NAME} ${THIS_PACKAGE_INCLUDE_DEPENDS})
ament_target_dependencies(${PROJECT_NAME} ${THIS_PACKAGE_INCLUDE_DEPENDS})
ament_export_include_directories(include)
ament_export_targets(${PROJECT_NAME}Targets HAS_LIBRARY_TARGET)
install(DIRECTORY include/
DESTINATION include
)
install(
TARGETS ${PROJECT_NAME}
EXPORT ${PROJECT_NAME}Targets
INCLUDES DESTINATION include
RUNTIME DESTINATION bin
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib
)
ament_package()
base.hpp (defines the parent of another class)
#ifndef RCLCPP_SAMPLE__BASE_HPP_
#define RCLCPP_SAMPLE__BASE_HPP_
#include <ament_index_cpp/get_package_share_directory.hpp>
#include <unistd.h>
#include <rclcpp/rclcpp.hpp>
#include <string>
namespace rclcpp_sample
{
class BaseModules : public rclcpp::Node
{
public:
BaseModules(const std::string& nodename);
virtual ~BaseModules();
virtual bool func_to_be_overridden();
};
} // namespace rclcpp_sample
#endif //RCLCPP_SAMPLE__BASE_HPP_
child1.hpp
#ifndef RCLCPP_SAMPLE__CHILD1_HPP_
#define RCLCPP_SAMPLE__CHILD1_HPP_
#include <string>
#include <rclcpp_sample/base.hpp>
namespace rclcpp_sample
{
class DerivedModule : public BaseModules
{
public:
explicit DerivedModule(const std::string& nodename);
virtual ~DerivedModule();
virtual bool func_to_be_overridden();
};
} // namespace rclcpp_sample
#endif // RCLCPP_SAMPLE__CHILD1_HPP_
impl.cpp
#include <rclcpp_sample/base.hpp>
#include <rclcpp_sample/child1.hpp>
using namespace rclcpp_sample;
BaseModules::BaseModules(const std::string& nodename) :
rclcpp::Node(nodename)
{
RCLCPP_INFO(this->get_logger(), "Initialization.");
}
BaseModules::~BaseModules()
{
RCLCPP_INFO(this->get_logger(), "Deconstruction.");
}
bool BaseModules::func_to_be_overridden()
{
return false;
}
DerivedModule::DerivedModule(const std::string& nodename) :
BaseModules(nodename)
{
RCLCPP_INFO(get_logger(), "Construtor of DerivedModule class.");
}
DerivedModule::~DerivedModule()
{
}
bool DerivedModule::func_to_be_overridden()
{
return true;
}
Downstream pkg
CMakeLists.txtcmake_minimum_required(VERSION 3.8)
project(rclcpp_child_sample)
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
add_compile_options(-Wall -Wextra -Wpedantic)
endif()
set(THIS_PACKAGE_INCLUDE_DEPENDS
ament_cmake
rclcpp
rclcpp_sample
)
foreach(dep IN ITEMS ${THIS_PACKAGE_INCLUDE_DEPENDS})
find_package(${dep} REQUIRED)
endforeach()
set(app_derived app_derived)
add_executable(${app_derived} src/exec.cpp)
target_link_libraries(${app_derived} rclcpp_sample::rclcpp_sample)
ament_target_dependencies(${app_derived} ${THIS_PACKAGE_INCLUDE_DEPENDS})
install(TARGETS ${app_derived} RUNTIME DESTINATION lib/${PROJECT_NAME})
ament_package()
exec.cpp
#include <rclcpp/rclcpp.hpp>
#include <string>
#include <rclcpp_sample/base.hpp>
#include <rclcpp_sample/child1.hpp>
int main(int argc, char *argv[])
{
rclcpp::init(argc, argv);
auto app = std::make_shared<rclcpp_sample::DerivedModule>("app_execo");
rclcpp::executors::MultiThreadedExecutor executor;
executor.add_node(app);
std::thread([&executor]() { executor.spin(); }).detach();
RCLCPP_INFO(rclcpp::get_logger("exec_rclcpp_sample"),
"func_to_be_overridden: %d", app->func_to_be_overridden());
rclcpp::shutdown();
}