diff --git a/rclcpp_components/cmake/rclcpp_components_add_multiple_nodes.cmake b/rclcpp_components/cmake/rclcpp_components_add_multiple_nodes.cmake new file mode 100644 index 0000000000..a05e6ae550 --- /dev/null +++ b/rclcpp_components/cmake/rclcpp_components_add_multiple_nodes.cmake @@ -0,0 +1,39 @@ +# Copyright 2019 Open Source Robotics Foundation, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Create a target which builds a shared library and also creates a target +# which builds an executable +# usage: rclcpp_components_add_node( +# [EXCLUDE_FROM_ALL] [source2 ...]) + +# :param EXCLUDE_FROM_ALL: exclude the target from default build target +# :type EXCLUDE_FROM_ALL: boolean +# :param target: the name of the shared library target +# :type target: string +# :param sourceN: the list of source files for executable and shared library +# :type sourceN: list of strings +# +function(rclcpp_components_add_multiple_nodes target) + cmake_parse_arguments( + ARGS + "EXCLUDE_FROM_ALL" + "" + "" + ${ARGN}) + set(sourceN ${ARGS_UNPARSED_ARGUMENTS}) + add_library(${target} SHARED ${sourceN}) + string(TOUPPER ${PROJECT_NAME} PROJECT_NAME_UPPER) + target_compile_definitions(${target} PRIVATE "${PROJECT_NAME_UPPER}_BUILDING_DLL") + set_target_properties(${target} PROPERTIES EXCLUDE_FROM_ALL ${ARGS_EXCLUDE_FROM_ALL}) +endfunction() diff --git a/rclcpp_components/cmake/rclcpp_components_add_node.cmake b/rclcpp_components/cmake/rclcpp_components_add_node.cmake new file mode 100644 index 0000000000..ddbce62a5f --- /dev/null +++ b/rclcpp_components/cmake/rclcpp_components_add_node.cmake @@ -0,0 +1,59 @@ +# Copyright 2019 Open Source Robotics Foundation, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Create a target which builds a shared library and also creates a target +# which builds an executable +# usage: rclcpp_components_add_node( +# [EXCLUDE_FROM_ALL] [source2 ...]) + +# :param EXCLUDE_FROM_ALL: exclude the target from default build target +# :type EXCLUDE_FROM_ALL: boolean +# :param target: the name of the shared library target +# :type target: string +# :param sourceN: the list of source files for executable and shared library +# :type sourceN: list of strings +# +function(rclcpp_components_add_node target) + cmake_parse_arguments( + ARGS + "EXCLUDE_FROM_ALL" + "" + "" + ${ARGN}) + list(GET ARGS_UNPARSED_ARGUMENTS 0 executable_name) + list(REMOVE_AT ARGS_UNPARSED_ARGUMENTS 0) + set(sourceN ${ARGS_UNPARSED_ARGUMENTS}) + add_library(${target} SHARED ${sourceN}) + string(TOUPPER ${PROJECT_NAME} PROJECT_NAME_UPPER) + target_compile_definitions(${target} PRIVATE "${PROJECT_NAME_UPPER}_BUILDING_DLL") + set_target_properties(${target} PROPERTIES EXCLUDE_FROM_ALL ${ARGS_EXCLUDE_FROM_ALL}) + add_executable(${executable_name} ../../rclcpp/rclcpp_components/src/node_main.cpp) + set(lib ${target}) + # Needed so symbols aren't dropped if not usesd + if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + set(lib + "-Wl,--no-as-needed" + ${target} + "-Wl,--as-needed") + endif() + target_link_libraries(${executable_name} + ${lib}) + ament_target_dependencies(${executable_name} + "rclcpp" + "class_loader" + "rclcpp_components") + install(TARGETS + ${executable_name} + DESTINATION lib/${PROJECT_NAME}) +endfunction() diff --git a/rclcpp_components/cmake/rclcpp_components_register_node.cmake b/rclcpp_components/cmake/rclcpp_components_register_node.cmake new file mode 100644 index 0000000000..40b46543ee --- /dev/null +++ b/rclcpp_components/cmake/rclcpp_components_register_node.cmake @@ -0,0 +1,63 @@ +# Copyright 2019 Open Source Robotics Foundation, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# usage: rclcpp_components_register_node( +# ) +# +# Register an rclcpp component with the ament +# resource index and create an executable. +# +# :param target: the shared library target +# :type target: string +# :param ARGS: the plugin name and node name for executable +# :type ARGS: list of strings +# :param component: the component plugin name +# :type component: string +# :param node: the node's executable name +# :type node: string +# +macro(rclcpp_components_register_node target) + set(ARGS ${ARGN}) + list(GET ARGS 0 component) + list(GET ARGS 1 node) + _rclcpp_components_register_package_hook() + if(WIN32) + set(_path "bin") + else() + set(_path "lib") + endif() + set(_RCLCPP_COMPONENTS__NODES + "${_RCLCPP_COMPONENTS__NODES}${component};${_path}/$\n") + set(CLASS_NAME ${component}) + configure_file(../../rclcpp/rclcpp_components/src/node_main.cpp.in + ../../rclcpp/rclcpp_components/src/node_main_${node}.cpp @ONLY) + add_executable(${node} ../../rclcpp/rclcpp_components/src/node_main_${node}.cpp) + set(lib ${target}) + # Needed so symbols aren't dropped if not usesd + if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + set(lib + "-Wl,--no-as-needed" + ${target} + "-Wl,--as-needed") + endif() + target_link_libraries(${node} + ${lib}) + ament_target_dependencies(${node} + "rclcpp" + "class_loader" + "rclcpp_components") + install(TARGETS + ${node} + DESTINATION lib/${PROJECT_NAME}) +endmacro() diff --git a/rclcpp_components/rclcpp_components-extras.cmake b/rclcpp_components/rclcpp_components-extras.cmake index ce77ab671b..61e5acd51e 100644 --- a/rclcpp_components/rclcpp_components-extras.cmake +++ b/rclcpp_components/rclcpp_components-extras.cmake @@ -26,4 +26,6 @@ macro(_rclcpp_components_register_package_hook) endmacro() include("${rclcpp_components_DIR}/rclcpp_components_register_nodes.cmake") - +include("${rclcpp_components_DIR}/rclcpp_components_add_node.cmake") +include("${rclcpp_components_DIR}/rclcpp_components_add_multiple_nodes.cmake") +include("${rclcpp_components_DIR}/rclcpp_components_register_node.cmake") diff --git a/rclcpp_components/src/node_main.cpp b/rclcpp_components/src/node_main.cpp new file mode 100644 index 0000000000..a8ea6e7b5f --- /dev/null +++ b/rclcpp_components/src/node_main.cpp @@ -0,0 +1,68 @@ +// Copyright 2016 Open Source Robotics Foundation, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include + +#include "class_loader/class_loader.hpp" +#include "rclcpp/rclcpp.hpp" +#include "rclcpp_components/node_factory.hpp" + +#define NODE_MAIN_LOGGER_NAME "node_main" + +int main(int argc, char * argv[]) +{ + // Force flush of the stdout buffer. + setvbuf(stdout, NULL, _IONBF, BUFSIZ); + + auto args = rclcpp::init_and_remove_ros_arguments(argc, argv); + rclcpp::Logger logger = rclcpp::get_logger(NODE_MAIN_LOGGER_NAME); + rclcpp::executors::SingleThreadedExecutor exec; + rclcpp::NodeOptions options; + options.arguments(args); + std::vector loaders; + std::vector node_wrappers; + + std::vector libraries = { + // all classes from libraries linked by the linker (rather then dlopen) + // are registered under the library_path "" + "", + }; + for (auto library : libraries) { + RCLCPP_DEBUG(logger, "Loading library %s", library.c_str()); + auto loader = new class_loader::ClassLoader(library); + auto classes = loader->getAvailableClasses(); + for (auto clazz : classes) { + RCLCPP_DEBUG(logger, "Instantiate class %s", clazz.c_str()); + auto node_factory = loader->createInstance(clazz); + auto wrapper = node_factory->create_node_instance(options); + auto node = wrapper.get_node_base_interface(); + node_wrappers.push_back(wrapper); + exec.add_node(node); + } + loaders.push_back(loader); + } + + exec.spin(); + + for (auto wrapper : node_wrappers) { + exec.remove_node(wrapper.get_node_base_interface()); + } + node_wrappers.clear(); + + rclcpp::shutdown(); + + return 0; +} diff --git a/rclcpp_components/src/node_main.cpp.in b/rclcpp_components/src/node_main.cpp.in new file mode 100644 index 0000000000..0ac40e2bf6 --- /dev/null +++ b/rclcpp_components/src/node_main.cpp.in @@ -0,0 +1,73 @@ +// Copyright 2016 Open Source Robotics Foundation, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include + +#include "class_loader/class_loader.hpp" +#include "rclcpp/rclcpp.hpp" +#include "rclcpp_components/node_factory.hpp" +#include "rclcpp_components/node_factory_template.hpp" + +#define NODE_MAIN_LOGGER_NAME "node_main" + +int main(int argc, char * argv[]) +{ + // Force flush of the stdout buffer. + setvbuf(stdout, NULL, _IONBF, BUFSIZ); + + auto args = rclcpp::init_and_remove_ros_arguments(argc, argv); + rclcpp::Logger logger = rclcpp::get_logger(NODE_MAIN_LOGGER_NAME); + rclcpp::executors::SingleThreadedExecutor exec; + rclcpp::NodeOptions options; + options.arguments(args); + std::vector loaders; + std::vector node_wrappers; + std::vector libraries = { + // all classes from libraries linked by the linker (rather then dlopen) + // are registered under the library_path "" + "", + }; + std::string className = "rclcpp_components::NodeFactoryTemplate<@CLASS_NAME@>"; + for (auto library : libraries) { + RCLCPP_DEBUG(logger, "Loading library %s", library.c_str()); + auto loader = new class_loader::ClassLoader(library); + auto classes = loader->getAvailableClasses(); + for (auto clazz : classes) { + std::string name = clazz.c_str(); + if (!(name.compare(className))) { + RCLCPP_DEBUG(logger, "Instantiate class %s", clazz.c_str()); + auto node_factory = loader->createInstance(clazz); + auto wrapper = node_factory->create_node_instance(options); + auto node = wrapper.get_node_base_interface(); + node_wrappers.push_back(wrapper); + exec.add_node(node); + } + } + loaders.push_back(loader); + } + + exec.spin(); + + for (auto wrapper : node_wrappers) { + exec.remove_node(wrapper.get_node_base_interface()); + } + node_wrappers.clear(); + + rclcpp::shutdown(); + + + return 0; +}