├── .gitignore ├── CHANGELOG.rst ├── CMakeLists.txt ├── CONTRIBUTING.md ├── LICENSE ├── cmake ├── blank.in ├── pluginlib_enable_plugin_testing.cmake ├── pluginlib_export_plugin_description_file.cmake └── pluginlib_package_hook.cmake ├── include └── pluginlib │ ├── class_desc.hpp │ ├── class_list_macros.hpp │ ├── class_loader.hpp │ ├── class_loader_base.hpp │ ├── class_loader_imp.hpp │ ├── exceptions.hpp │ └── impl │ └── split.hpp ├── package.xml ├── pluginlib-extras.cmake └── test ├── include ├── test_base.hpp ├── test_plugins.hpp └── visibility_control.hpp ├── test_package.xml ├── test_plugins.cpp ├── test_plugins.xml ├── unique_ptr_test.cpp └── utest.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | devel 3 | CMakeLists.txt.user 4 | -------------------------------------------------------------------------------- /CHANGELOG.rst: -------------------------------------------------------------------------------- 1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 2 | Changelog for package pluginlib 3 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 4 | 5 | 6 | 5.7.0 (2025-04-25) 7 | ------------------ 8 | 9 | 5.6.0 (2024-12-20) 10 | ------------------ 11 | * Heavily cleanup pluginlib. (`#265 `_) 12 | * Remove CODEOWNERS and mirror-rolling-to-main workflow (`#268 `_) 13 | * Contributors: Alejandro Hernández Cordero, Chris Lalancette 14 | 15 | 5.5.2 (2024-11-20) 16 | ------------------ 17 | * Fix Minor Spelling Mistakes (`#260 `_) 18 | * Contributors: David V. Lu!! 19 | 20 | 5.5.1 (2024-07-08) 21 | ------------------ 22 | * Removed deprecated method (`#256 `_) 23 | * Contributors: Alejandro Hernández Cordero 24 | 25 | 5.5.0 (2024-04-26) 26 | ------------------ 27 | 28 | 5.4.2 (2024-03-28) 29 | ------------------ 30 | * Switch from rcpputils::fs to std::filesystem (`#254 `_) 31 | * Contributors: Christophe Bedard 32 | 33 | 5.4.1 (2024-01-24) 34 | ------------------ 35 | * Remove redundant throw of a std::runtime_error (`#232 `_) 36 | * Contributors: Hunter L. Allen 37 | 38 | 5.4.0 (2023-12-26) 39 | ------------------ 40 | * Update to C++17 (`#251 `_) 41 | * Contributors: Chris Lalancette 42 | 43 | 5.3.1 (2023-11-06) 44 | ------------------ 45 | * Fix wShadow compile warning (`#250 `_) 46 | * Contributors: Steve Macenski 47 | 48 | 5.3.0 (2023-04-28) 49 | ------------------ 50 | 51 | 5.2.2 (2023-03-01) 52 | ------------------ 53 | * Update maintainers 54 | * Contributors: Audrow Nash 55 | 56 | 5.2.1 (2022-09-13) 57 | ------------------ 58 | 59 | 5.2.0 (2022-05-10) 60 | ------------------ 61 | 62 | 5.1.0 (2022-01-14) 63 | ------------------ 64 | * Install includes to include/${PROJECT_NAME} and remove ament_target_dependencies calls (`#226 `_) 65 | * Require (`#225 `_) 66 | * Move LibraryLoadExceptions down a level for more accurate error messages (`#221 `_) 67 | * Update maintainers to Chris Lalancette (`#223 `_) 68 | * extend termination condition to avoid infinite loop if package.xml is not found (`#220 `_) 69 | * Remove deprecated headers. (`#217 `_) 70 | * Contributors: Alberto Soragna, Audrow Nash, Chris Lalancette, David V. Lu!!, Shane Loretz 71 | 72 | 5.0.0 (2021-01-25) 73 | ------------------ 74 | * Use rcpputils for the filesystem implementation. (`#212 `_) 75 | * Contributors: Chris Lalancette 76 | 77 | 4.1.2 (2020-12-08) 78 | ------------------ 79 | * Check for NULL in XMLElement::Attribute 80 | * Check for NULL in XMLElement::GetText 81 | * Check for NULL in XMLNode::Value 82 | * Remove unused variable output_library (`#211 `_) 83 | * Make Chris a maintainer of pluginlib. (`#210 `_) 84 | * Add QNX C++ fs library compiler option (`#205 `_) 85 | * Contributors: Ahmed Sobhy, Chris Lalancette, Jeremie Deray, Shane Loretz 86 | 87 | 4.1.1 (2020-08-27) 88 | ------------------ 89 | * Fix cmake 3.5 compatibility (`#203 `_) 90 | * Contributors: Karsten Knese 91 | 92 | 4.1.0 (2020-08-25) 93 | ------------------ 94 | * Add function for same-package pluginlib tests (`#201 `_) 95 | * Contributors: Shane Loretz 96 | 97 | 4.0.0 (2020-08-06) 98 | ------------------ 99 | * Remove deprecated boost functions (`#199 `_) 100 | * Contributors: Shane Loretz 101 | 102 | 2.5.2 (2020-05-26) 103 | ------------------ 104 | * Link against tinyxml2 correctly (`#190 `_) 105 | * Export tinyxml2 directly from pluginlib-extras.cmake (`#192 `_) 106 | * Contributors: Karsten Knese, Sean Yen 107 | 108 | 2.5.1 (2020-05-07) 109 | ------------------ 110 | * Add missing export of stdc++fs and TinyXML2 via modern CMake (`#189 `_) 111 | * Contributors: Dirk Thomas 112 | 113 | 2.5.0 (2020-04-25) 114 | ------------------ 115 | * Export CMake targets in a addition to include directories / libraries. (`#188 `_) 116 | * Use rcpputils for library names. (`#186 `_) 117 | * Fix filesystem linking on clang9. (`#183 `_) 118 | * Contributors: Alejandro Hernández Cordero, Dirk Thomas, Emerson Knapp 119 | 120 | 2.4.1 (2019-10-23) 121 | ------------------ 122 | * Avoid build break for Visual Studio 2019 v16.3 (`#166 `_) 123 | * Contributors: Sean Yen, Steven! Ragnarök 124 | 125 | 2.4.0 (2019-09-18) 126 | ------------------ 127 | * Export tinyxml2 libraries downstream. (`#154 `_) 128 | * Contributors: Esteve Fernandez 129 | 130 | 2.3.1 (2019-05-08) 131 | ------------------ 132 | * [ros2] Cast pointers to void * when using %p (`#152 `_) 133 | * Contributors: Shane Loretz 134 | 135 | 2.3.0 (2019-04-14) 136 | ------------------ 137 | * Updated build to choose the appropriate library for experimental filesystem, based on the compiler and standard library. (`#146 `_) 138 | * Added stdc++fs as a target link library for clang compiler on linux. (`#144 `_) 139 | * Added Michael as maintainer (for build e-mails). (`#137 `_) 140 | * Contributors: Emerson Knapp, Michael Carroll, bhatsach 141 | 142 | 2.2.1 (2018-12-13) 143 | ------------------ 144 | * Removed extraneous link lines. (`#135 `_) 145 | * Collapsed testing packages back into pluginlib package. (`#134 `_) 146 | * Contributors: Michael Carroll 147 | 148 | 2.2.0 (2018-11-20) 149 | ------------------ 150 | * Reorganized tests into fixture and consumer packages. (`#130 `_) 151 | * Updated maintainer (`#129 `_) 152 | * Fixed plugin description installation to relative subdirectory (`#122 `_) 153 | * Contributors: Michael Carroll, Mikael Arguedas 154 | 155 | 2.1.1 (2018-07-17) 156 | ------------------ 157 | * export rcutils (`#120 `_) 158 | * Contributors: Karsten Knese 159 | 160 | 2.1.0 (2018-06-21) 161 | ------------------ 162 | * Fix compile error with gcc 8.1.0 (`#116 `_) 163 | * remove extra semicolon to fix pedantic warning (`#103 `_) 164 | * use new class_loader header names (`#99 `_) 165 | * adjust library search to work on windows, warn about lib prefix (`#97 `_) 166 | * vs2015 doesn't support __has_include, VS2015 and 2017 have both `_) 167 | * move pluginlib in its own folder (port 83 to ros2 branch) (`#95 `_) 168 | * Contributors: Mikael Arguedas, William Woodall, jerry73204 169 | 170 | 1.11.0 (2017-07-27) 171 | ------------------- 172 | * Switch to Tinyxml2 (`#59 `_) 173 | * do not use popen to solve catkin_path. (`#49 `_) 174 | * switch to package format 2 (`#55 `_) 175 | * remove trailing whitespaces (`#54 `_) 176 | * Contributors: Dmitry Rozhkov, Koji Terada, Mikael Arguedas 177 | 178 | 1.10.5 (2017-03-27) 179 | ------------------- 180 | * Merge pull request `#47 `_ from ros/fix_conversion 181 | fix size_t to int conversion 182 | * fix int conversion 183 | * Contributors: Mikael Arguedas 184 | 185 | 1.10.4 (2016-09-20) 186 | ------------------- 187 | * Merge pull request `#42 `_ from delftrobotics-forks/unique-ptr 188 | Add std::unique_ptr API 189 | * Add unit test for unique_ptr API. 190 | * Simplify unit tests with ASSERT_THROW. 191 | * Add ClassLoader::createUniqueInstance. 192 | * Wrap long comment on createInstance and friend. 193 | * Throw exception if plugin.xml is broken (`#41 `_) 194 | * added test case for broken xml files with missing attributes of class tag 195 | * added checks if all needed attributes of the class tag are existing 196 | * removed comment and empty line 197 | * Contributors: Maarten de Vries, Mikael Arguedas, cwecht 198 | 199 | 1.10.3 (2016-06-22) 200 | ------------------- 201 | * Merge pull request `#40 `_ from ros/fix_warnings 202 | fix deprecated warnings in unit tests 203 | * fix deprecated warnings in unit tests 204 | * removed merge messages and redundant commits 205 | * Contributors: Mikael Arguedas 206 | 207 | 1.10.2 (2016-03-14) 208 | ------------------- 209 | * Remove Boost Software License from license tag `#35 `_ 210 | * Throw an exception if ClassLoader can't be instantiated due to an invalid package name `#34 `_ 211 | * Add ":" to split function within getName. `#33 `_ 212 | * Contributors: Esteve Fernandez, Jochen Sprickerhof, Mikael Arguedas, Mike O'Driscoll 213 | 214 | 1.10.1 (2014-12-23) 215 | ------------------- 216 | * Remove GTEST_FOUND from CMakeLists.txt 217 | * Check that GTest is installed before running tests. 218 | * Moved plugin_macro_update script to scripts directory. Made plugin_macro_update rosrunnable and removed it from global PATH `#29 `_ 219 | * Contributors: Esteve Fernandez 220 | 221 | 1.10.0 (2014-05-08 14:56) 222 | ------------------------- 223 | 224 | 1.9.25 (2014-05-08 20:37) 225 | ------------------------- 226 | * Use cmake_modules to find TinyXML `#26 `_ 227 | * Check for release libraries in debug builds `#25 `_ 228 | * update refreshDeclaredClasses to force recrawl (fix `#23 `_) 229 | * Contributors: Dirk Thomas, Esteve Fernandez 230 | 231 | 1.9.24 (2014-03-11) 232 | ------------------- 233 | * Remove invalid exception when no plugins are found `#22 `_ 234 | * Update maintainer field 235 | * Contributors: Dirk Thomas, Esteve Fernandez 236 | 237 | 1.9.23 (2013-10-04) 238 | ------------------- 239 | * expose plugin paths in ClassLoader `#21 `_ 240 | * Contributors: Dirk Thomas, Mirza Shah 241 | 242 | 1.9.22 (2013-08-21) 243 | ------------------- 244 | * Fixed use of __FILE_\_ macro in deprecation warning 245 | * Added libdl to plugin_tool link args...temporary fix 246 | * Contributors: Mirza Shah 247 | 248 | 1.9.21 (2013-07-14) 249 | ------------------- 250 | * Added file hint for deprecated warnings. `#16 `_ 251 | * check for CATKIN_ENABLE_TESTING 252 | * remove mainpage.dox 253 | * Contributors: Dane Powell, Dirk Thomas, Mirza Shah 254 | 255 | 1.9.20 (2013-04-18) 256 | ------------------- 257 | * Added another unit test for managed instance case. 258 | * Fixed a regression that broke unload call. Added a unit test for this case. 259 | * Contributors: Mirza Shah 260 | 261 | 1.9.19 (2013-03-23) 262 | ------------------- 263 | * Converted ROS_DEBUG and ROS_WARN calls to ROS_DEBUG_NAMED and ROS_WARN_NAMED calls `#13 `_ 264 | * Contributors: Dave Coleman, Mirza Shah 265 | 266 | 1.9.18 (2013-01-28) 267 | ------------------- 268 | * Support for boost filesystem v2 `#11 `_ 269 | * Added more debug information 270 | * Contributors: Mario Prats, Mirza Shah 271 | 272 | 1.9.17 (2012-12-27) 273 | ------------------- 274 | * More useful debug messages 275 | * Fixed incorrect debug message in plugin description XML parsing 276 | * Contributors: Mirza Shah 277 | 278 | 1.9.16 (2012-12-21) 279 | ------------------- 280 | * Removed old file 281 | * Annotated deprecation warning with more info 282 | * Made python script global installable 283 | * Added a script to recursively update deprecated pluginlib macro 284 | * added missing license header 285 | * modified dep type of catkin 286 | * Contributors: Aaron Blasdel, Dirk Thomas, Mirza Shah 287 | 288 | 1.9.15 (2012-12-13 17:22) 289 | ------------------------- 290 | * Updated registration macros to be easier and deprecated older ones. Also cleaned up code violating standard 291 | * Added wg copyright notice 292 | * Contributors: Mirza Shah 293 | 294 | 1.9.14 (2012-12-13 15:20) 295 | ------------------------- 296 | * lookup name (i.e. magic name) is now optional. Further cleanup...alphabetized methods, broke up some. 297 | * Contributors: Mirza Shah 298 | 299 | 1.9.13 (2012-12-11) 300 | ------------------- 301 | * Made robust to plugin package having different name from the folder it came from. ```#6 `_ 357 | * Contributors: Mirza Shah 358 | 359 | 1.9.2 (2012-10-25) 360 | ------------------ 361 | * fixed deps for downstream packages 362 | * Contributors: Dirk Thomas 363 | 364 | 1.9.1 (2012-10-24 22:02) 365 | ------------------------ 366 | * fix missing and redundant deps for downstream projects 367 | * Contributors: Dirk Thomas 368 | 369 | 1.9.0 (2012-10-24 18:31) 370 | ------------------------ 371 | * renamed test target 372 | * remove obsolete files 373 | * Fixed dependency in package.xml and minor touchups 374 | * Broke up code into further files 375 | * Catkinized pluginlib and completed integration more or less with class_loader. Heavy mods to pluginlib::ClassLoader to handle constraints of Catkin as well as delegate housekeeping to class_loader::ClassLoader 376 | * Updated to utilize newly renamed class_loader (formerly plugins) library with new file names, functions, identifiers, etc 377 | * Removed explicit dependency that should have been automatically imported from dependent package in CMakeLists.txt 378 | * Fixed unhandled exception to make all unit tests pass 379 | * Removed mention of console bridge in CMakeLists.txt, plugins now probably exports 380 | * Finished mods to utilize lower level plugins library. One test still failing, will get to that soon, but basics seem to be ok 381 | * Modding pluginlib to use new plugins library. Not done, but just doing it tosync with my laptop 382 | * Removed Poco and updated CMake and manifest files to depend on lower level plugins library 383 | * Contributors: Dirk Thomas, Mirza Shah, mirzashah 384 | 385 | 1.8.6 (2012-10-09) 386 | ------------------ 387 | * added missing boost include dirs and runtime dependency 388 | * updated cmake min version to 2.8.3 389 | * Contributors: Dirk Thomas, Vincent Rabaud 390 | 391 | 1.8.5 (2012-10-01) 392 | ------------------ 393 | * add missing roslib dependency that happens in class_loader_imp.h 394 | * Contributors: Vincent Rabaud 395 | 396 | 1.8.4 (2012-09-30) 397 | ------------------ 398 | * updated to latest catkin 399 | * Contributors: Dirk Thomas 400 | 401 | 1.8.3 (2012-09-07) 402 | ------------------ 403 | * added tinyxml to project depends 404 | * Contributors: Dirk Thomas 405 | 406 | 1.8.2 (2012-09-06) 407 | ------------------ 408 | * updated pkg-config in manifest.xml 409 | * updated catkin variables 410 | * Contributors: Dirk Thomas 411 | 412 | 1.8.1 (2012-09-04) 413 | ------------------ 414 | * Missing LIBRARIES and DEPENDS specifiers from CMakeLists.txt, now added. 415 | * catkin-ized 416 | * updated api doc for load/create/unload methods 417 | * renamed new methods using shorter name for encouraged method 418 | * added cmake macro for hiding plugin symbols and respective rosbuild export 419 | * updated class loader according to updated REP 121 420 | * add auto-unload for libraries using boost shared pointer 421 | * pluginlib: added a pure-virtual base class for ClassLoader called ClassLoaderBase, which is not templated. Only one function of ClassLoader is actually templated. This allows client code to not be templated where it doesn't need to be. 422 | * patch 4 for `#4887 `_ 423 | * ignore bin 424 | * accepting patch from ticket `#4887 `_ REP 116 implementation 425 | * add explicit link against tinyxml, because users of our libraries will need to link against it 426 | * link poco_lite with tinyxml 427 | * remove namespace to be compatible with tinyxml sysdep 428 | * removing back depend on common 429 | * removing rosdep.yaml, rule is in ros/rosdep.yaml 430 | * fixed tinyxml 431 | * converting to unary stack (separated from common) 432 | * applied patch from 4923, to support boost 1.46 433 | * patch from Nick Butko osx compatibility 434 | * adding unittest melonee forgot to commit 435 | * adding pluginlib tests 436 | * patch for osx linking `#4094 `_ 437 | * Fixed exception comments 438 | * Added Ubuntu platform tags to manifest 439 | * Fixing bug where the incorrect library path was passed to dlopen from pluginlib... oops. 440 | * fix in latest for `#4013 `_ to isolate boost filesystem calls into a library 441 | * patch from Wim `#3346 `_ reviewed by Eitan and I 442 | * Adding getName and isClassAvailable function calls to the class loader 443 | * inlining to avoid multiple definitions 444 | * macro deprecation 445 | * adding warning about deprecated macro PLUGINLIB_REGISTER_CLASS 446 | * pluginlib now takes pkg/type arguments, new macro PLUGINLIB_DECLARE_CLASS 447 | * pluginlib now robust to malformed manifests 448 | * Adding more descriptive error messages when libraries fail to load 449 | * Remove use of deprecated rosbuild macros 450 | * doc review completed http://www.ros.org/wiki/pluginlib/Reviews/2009-10-06_Doc_Review 451 | * fixing documentation link 452 | * fixing `#2894 `_ 453 | * Removing ROS_ERRORS in favor of adding information to the exceptions thrown 454 | * migration part 1 455 | * Contributors: Dave Hershberger, Dirk Thomas, Ken Conley, Mirza Shah, Tully Foote, eitan, gerkey, kwc, mwise, rusu, tfoote, vpradeep, wheeler 456 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.5) 2 | project(pluginlib) 3 | 4 | # Default to C++17 5 | if(NOT CMAKE_CXX_STANDARD) 6 | set(CMAKE_CXX_STANDARD 17) 7 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 8 | endif() 9 | 10 | if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") 11 | add_compile_options(-Wall -Wextra -Wpedantic) 12 | endif() 13 | 14 | find_package(ament_cmake REQUIRED) 15 | find_package(ament_index_cpp REQUIRED) 16 | find_package(class_loader REQUIRED) 17 | find_package(rcutils REQUIRED) 18 | find_package(rcpputils REQUIRED) 19 | find_package(tinyxml2_vendor REQUIRED) 20 | find_package(TinyXML2 REQUIRED) # provided by tinyxml2 upstream, or tinyxml2_vendor 21 | 22 | add_library(${PROJECT_NAME} INTERFACE) 23 | target_include_directories(${PROJECT_NAME} INTERFACE 24 | "$" 25 | "$") 26 | target_link_libraries(${PROJECT_NAME} INTERFACE 27 | ament_index_cpp::ament_index_cpp 28 | class_loader::class_loader 29 | rcutils::rcutils 30 | rcpputils::rcpputils 31 | tinyxml2::tinyxml2) 32 | 33 | ament_export_dependencies(ament_index_cpp class_loader rcutils rcpputils tinyxml2_vendor TinyXML2) 34 | 35 | ament_export_targets(export_${PROJECT_NAME}) 36 | 37 | install( 38 | TARGETS ${PROJECT_NAME} EXPORT export_${PROJECT_NAME} 39 | ) 40 | 41 | install( 42 | DIRECTORY cmake 43 | DESTINATION share/${PROJECT_NAME} 44 | ) 45 | 46 | install(DIRECTORY include/ DESTINATION include/${PROJECT_NAME}) 47 | 48 | if(BUILD_TESTING) 49 | find_package(ament_lint_auto REQUIRED) 50 | ament_lint_auto_find_test_dependencies() 51 | 52 | find_package(ament_cmake_gtest REQUIRED) 53 | 54 | add_library(test_plugins SHARED ./test/test_plugins.cpp) 55 | target_compile_definitions(test_plugins PRIVATE "TEST_PLUGINLIB_FIXTURE_BUILDING_LIBRARY") 56 | target_include_directories(test_plugins PRIVATE 57 | "$") 58 | target_link_libraries(test_plugins ${PROJECT_NAME}) 59 | 60 | include("cmake/pluginlib_enable_plugin_testing.cmake") 61 | pluginlib_enable_plugin_testing( 62 | CMAKE_TARGET_VAR mock_install_target 63 | AMENT_PREFIX_PATH_VAR mock_install_path 64 | PACKAGE_NAME "test_pluginlib" 65 | PACKAGE_XML "test/test_package.xml" 66 | PLUGIN_CATEGORY "test_pluginlib" 67 | PLUGIN_DESCRIPTIONS "test/test_plugins.xml" 68 | PLUGIN_LIBRARIES test_plugins 69 | ) 70 | 71 | ament_add_gtest(${PROJECT_NAME}_unique_ptr_test 72 | test/unique_ptr_test.cpp 73 | APPEND_LIBRARY_DIRS "$" 74 | APPEND_ENV AMENT_PREFIX_PATH=${mock_install_path} 75 | ) 76 | if(TARGET ${PROJECT_NAME}_unique_ptr_test) 77 | target_link_libraries(${PROJECT_NAME}_unique_ptr_test ${PROJECT_NAME}) 78 | target_include_directories(${PROJECT_NAME}_unique_ptr_test PRIVATE 79 | "$") 80 | add_dependencies(${PROJECT_NAME}_unique_ptr_test "${mock_install_target}") 81 | endif() 82 | 83 | ament_add_gtest(${PROJECT_NAME}_utest 84 | test/utest.cpp 85 | APPEND_ENV AMENT_PREFIX_PATH=${mock_install_path} 86 | ) 87 | if(TARGET ${PROJECT_NAME}_utest) 88 | # Used, but not linked to test ${PROJECT_NAME}'s exports: 89 | # rcutils::rcutils 90 | target_link_libraries(${PROJECT_NAME}_utest ${PROJECT_NAME}) 91 | add_dependencies(${PROJECT_NAME}_utest "${mock_install_target}") 92 | target_include_directories(${PROJECT_NAME}_utest PRIVATE 93 | "$") 94 | endif() 95 | 96 | endif() 97 | 98 | ament_package( 99 | CONFIG_EXTRAS "pluginlib-extras.cmake" 100 | ) 101 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Any contribution that you make to this repository will 2 | be under the 3-Clause BSD License, as dictated by that 3 | [license](https://opensource.org/licenses/BSD-3-Clause). 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Redistribution and use in source and binary forms, with or without 2 | modification, are permitted provided that the following conditions are met: 3 | 4 | * Redistributions of source code must retain the above copyright 5 | notice, this list of conditions and the following disclaimer. 6 | 7 | * Redistributions in binary form must reproduce the above copyright 8 | notice, this list of conditions and the following disclaimer in the 9 | documentation and/or other materials provided with the distribution. 10 | 11 | * Neither the name of the copyright holder nor the names of its 12 | contributors may be used to endorse or promote products derived from 13 | this software without specific prior written permission. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 19 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 20 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 21 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 22 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 23 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 24 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 25 | POSSIBILITY OF SUCH DAMAGE. 26 | -------------------------------------------------------------------------------- /cmake/blank.in: -------------------------------------------------------------------------------- 1 | @FILE_CONTENT@ 2 | -------------------------------------------------------------------------------- /cmake/pluginlib_enable_plugin_testing.cmake: -------------------------------------------------------------------------------- 1 | # Copyright 2020 Open Source Robotics Foundation, Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | # Set variable for accessing template inside function scope 16 | set(PLUGINLIB_ENABLE_PLUGIN_TESTING_DIR "${CMAKE_CURRENT_LIST_DIR}") 17 | 18 | # 19 | # Enable testing plugins by mock-installing needed files to the build folder. 20 | # 21 | # Pluginlib needs a certain folder structure in the ament_index to recognize 22 | # the existence of a package and its exported plugins. 23 | # This structure must exist at test time in order for unit tests to load 24 | # plugins. 25 | # The macro `pluginlib_export_plugin_description_file()` sets up this structure 26 | # in the install space, but a package's install space is not accessible to it 27 | # when its own unit tests are run. 28 | # This function sets up the structure in the build space so that unit tests 29 | # can load plugins from the same package they reside in. 30 | # 31 | # The input to this function is everything required by pluginlib to recognize a 32 | # package and its plugins. 33 | # The output of this function is two CMake variables. 34 | # One CMake variable holds a path to be appended to the environment variable 35 | # `AMENT_PREFIX_PATH` used by the test. 36 | # Another CMake variable holds the name of a CMake target that must be 37 | # depended on to ensure the mock environment has been created before the test 38 | # runs. 39 | # 40 | # CMake macros provided by ament_cmake for creating tests have an argument 41 | # called APPEND_ENV that should be used for modifying `AMENT_PREFIX_PATH`. 42 | # `add_dependencies()` must be used to ensure the test runs after the mock 43 | # install environment has been created. 44 | # 45 | # pluginlib_enable_plugin_testing(... 46 | # CMAKE_TARGET_VAR mock_install_target 47 | # AMENT_PREFIX_PATH_VAR mock_install_path 48 | # ...) 49 | # ament_add_[some kind of test](some_test_target ... 50 | # APPEND_ENV AMENT_PREFIX_PATH="${mock_install_path}" 51 | # ...) 52 | # add_dependencies(some_test_target "${mock_install_target}") 53 | # 54 | # This function must only be called once for each unique combination of 55 | # "PACKAGE_NAME" and "PLUGIN_CATEGORY". 56 | # 57 | # :param CMAKE_TARGET_VAR: the name of the target that creates the mock install 58 | # environment. 59 | # :type CMAKE_TARGET_VAR: string 60 | # :param AMENT_PREFIX_PATH_VAR: the name of the variable that will contain the 61 | # mock installation path for tests to use after this function is run. 62 | # :type AMENT_PREFIX_PATH_VAR: string 63 | # :param PACKAGE_NAME: the name of the mock package to install. 64 | # If unspecified this defaults to "${PROJECT_NAME}" 65 | # :type PACKAGE_NAME: string 66 | # :param PACKAGE_XML: the path to a package.xml to install. 67 | # If unspecified this defaults to "${CMAKE_SOURCE_DIR}/package.xml". 68 | # :type PACKAGE_XML: string 69 | # :param PLUGIN_CATEGORY: the plugin category these plugins belong to. 70 | # :type PLUGIN_CATEGORY: string 71 | # :param PLUGIN_DESCRIPTIONS: At least one plugin description to mock install. 72 | # The paths should match the specification for relative_filename in the macro 73 | # `pluginlib_export_plugin_description_file()`. 74 | # :type PLUGIN_DESCRIPTIONS: list of strings 75 | # :param PLUGIN_LIBRARIES: At least one library target for pluginlib plugins. 76 | # There should be one target for every plugin library described in the plugin 77 | # description files. 78 | # :type PLUGIN_LIBRARIES: list of targets 79 | # 80 | # @public 81 | # 82 | function(pluginlib_enable_plugin_testing) 83 | ##### 84 | # Make sure inputs are valid 85 | ##### 86 | 87 | cmake_parse_arguments(ARG 88 | "" 89 | "CMAKE_TARGET_VAR;AMENT_PREFIX_PATH_VAR;PACKAGE_XML;PACKAGE_NAME;PLUGIN_CATEGORY" 90 | "PLUGIN_DESCRIPTIONS;PLUGIN_LIBRARIES" 91 | ${ARGN}) 92 | if(ARG_UNPARSED_ARGUMENTS) 93 | message(FATAL_ERROR "pluginlib_enable_plugin_testing() called with unused arguments: " 94 | "${ARG_UNPARSED_ARGUMENTS}") 95 | endif() 96 | 97 | # Error or set defaults for required vs optional arguments 98 | if(NOT ARG_CMAKE_TARGET_VAR) 99 | message(FATAL_ERROR "CMAKE_TARGET_VAR is a required argument") 100 | endif() 101 | if(NOT ARG_AMENT_PREFIX_PATH_VAR) 102 | message(FATAL_ERROR "AMENT_PREFIX_PATH_VAR is a required argument") 103 | endif() 104 | if(NOT ARG_PACKAGE_NAME) 105 | set(ARG_PACKAGE_NAME "${PROJECT_NAME}") 106 | endif() 107 | if(NOT ARG_PACKAGE_XML) 108 | set(ARG_PACKAGE_XML "${CMAKE_SOURCE_DIR}/package.xml") 109 | endif() 110 | if(NOT ARG_PLUGIN_CATEGORY) 111 | message(FATAL_ERROR "PLUGIN_CATEGORY is a required argument") 112 | endif() 113 | if(NOT ARG_PLUGIN_DESCRIPTIONS) 114 | message(FATAL_ERROR "At least one plugin description via PLUGIN_DESCRIPTIONS is required") 115 | endif() 116 | if(NOT ARG_PLUGIN_LIBRARIES) 117 | message(FATAL_ERROR "At least one plugin library target via PLUGIN_LIBRARIES is required") 118 | endif() 119 | 120 | set(target_name "pluginlib_enable_plugin_testing__${ARG_PLUGIN_CATEGORY}__${ARG_PACKAGE_NAME}") 121 | if(TARGET "${target_name}") 122 | message(FATAL_ERROR "pluginlib_enable_plugin_testing has already been called with " 123 | "category '${ARG_PLUGIN_CATEGORY}' and package name '${ARG_PACKAGE_NAME}'") 124 | endif() 125 | 126 | ##### 127 | # Plan out mock install space before configuring things to create it 128 | ##### 129 | 130 | # Lists of equal size 131 | # If input_files is not "", then copy that to the path in "output_files" 132 | # If input_files is "", then write string from input_content to the path in "output_files" 133 | # FOOBAR is a workaround to enable appending empty elements when the list is empty 134 | set(input_content "FOOBAR") 135 | set(input_files "FOOBAR") 136 | set(output_files "FOOBAR") 137 | 138 | # Each "PACKAGE_NAME" goes to it's own folder 139 | set(prefix "${CMAKE_CURRENT_BINARY_DIR}/pluginlib_enable_plugin_testing/install/${ARG_PACKAGE_NAME}__${ARG_PLUGIN_CATEGORY}") 140 | 141 | # Install package.xml, renaming to just `package.xml` if needed 142 | set(fake_install_dir "${prefix}/share/${ARG_PACKAGE_NAME}/") 143 | list(APPEND input_files "${ARG_PACKAGE_XML}") 144 | list(APPEND input_content "") 145 | list(APPEND output_files "${fake_install_dir}/package.xml") 146 | 147 | # Add blank file indicating existence of package in the ament index 148 | list(APPEND input_files "") 149 | list(APPEND input_content "") 150 | list(APPEND output_files "${prefix}/share/ament_index/resource_index/packages/${ARG_PACKAGE_NAME}") 151 | 152 | # Write the plugin descriptions in share/ 153 | set(ament_index_plugin_content "") 154 | foreach(plugin_description_path "${ARG_PLUGIN_DESCRIPTIONS}") 155 | list(APPEND input_files "${plugin_description_path}") 156 | list(APPEND input_content "") 157 | list(APPEND output_files "${prefix}/share/${ARG_PACKAGE_NAME}/${plugin_description_path}") 158 | 159 | set(ament_index_plugin_content "${ament_index_plugin_content}\nshare/${ARG_PACKAGE_NAME}/${plugin_description_path}") 160 | endforeach() 161 | # Get rid of leading newline 162 | string(STRIP "${ament_index_plugin_content}" ament_index_plugin_content) 163 | 164 | # Write one file that says where all of this package's plugin description files are 165 | list(APPEND input_files "") 166 | list(APPEND input_content "${ament_index_plugin_content}") 167 | list(APPEND output_files "${prefix}/share/ament_index/resource_index/${ARG_PLUGIN_CATEGORY}__pluginlib__plugin/${ARG_PACKAGE_NAME}") 168 | 169 | # Get rid of FOOBAR 170 | list(REMOVE_AT input_files 0) 171 | list(REMOVE_AT input_content 0) 172 | list(REMOVE_AT output_files 0) 173 | 174 | ##### 175 | # Create commands to generate mock install space at build time 176 | ##### 177 | 178 | list(LENGTH output_files num_output_files) 179 | # subtract 1 to avoid looping out of range 180 | math(EXPR range_stop "${num_output_files} - 1") 181 | foreach(idx RANGE ${range_stop}) 182 | list(GET input_files ${idx} input_file) 183 | list(GET input_content ${idx} content) 184 | list(GET output_files ${idx} output_file) 185 | 186 | # Make the directory at configure time because configure_file() does not make directories 187 | get_filename_component(output_dir "${output_file}" DIRECTORY) 188 | file(MAKE_DIRECTORY "${output_dir}") 189 | 190 | if(IS_ABSOLUTE "${input_file}") 191 | set(input_file_abs "${input_file}") 192 | else() 193 | set(input_file_abs "${CMAKE_CURRENT_SOURCE_DIR}/${input_file}") 194 | endif() 195 | 196 | # Copy or write file contents at build time 197 | if("${input_file}" STREQUAL "") 198 | set(FILE_CONTENT "${content}") 199 | configure_file("${PLUGINLIB_ENABLE_PLUGIN_TESTING_DIR}/blank.in" "${output_file}" @ONLY) 200 | else() 201 | configure_file("${input_file_abs}" "${output_file}" COPYONLY) 202 | endif() 203 | endforeach() 204 | 205 | # Copy library targets at build time; but make directory at configure time 206 | # because `cmake -E copy` does not make directories 207 | file(MAKE_DIRECTORY "${prefix}/lib") 208 | foreach(plugin_library "${ARG_PLUGIN_LIBRARIES}") 209 | add_custom_command(TARGET "${plugin_library}" 210 | POST_BUILD 211 | COMMAND ${CMAKE_COMMAND} -E copy $ 212 | "${prefix}/lib/" 213 | ) 214 | endforeach() 215 | 216 | add_custom_target("${target_name}" DEPENDS ${output_files}) 217 | 218 | ##### 219 | # Set output variables 220 | ##### 221 | set("${ARG_CMAKE_TARGET_VAR}" "${target_name}" PARENT_SCOPE) 222 | set("${ARG_AMENT_PREFIX_PATH_VAR}" "${prefix}" PARENT_SCOPE) 223 | endfunction() 224 | -------------------------------------------------------------------------------- /cmake/pluginlib_export_plugin_description_file.cmake: -------------------------------------------------------------------------------- 1 | # Copyright 2017 Open Source Robotics Foundation, Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | if(PLUGINLIB_EXPORT_PLUGIN_DESCRIPTION_FILE_CMAKE_GUARD) 16 | return() 17 | endif() 18 | set(PLUGINLIB_EXPORT_PLUGIN_DESCRIPTION_FILE_CMAKE_GUARD TRUE) 19 | 20 | set(__PLUGINLIB_PLUGIN_CATEGORIES "") 21 | 22 | # 23 | # Export a plugin description file provided by the current package for a given plugin category. 24 | # 25 | # In this macro "Export" implies both installing the file to 26 | # the current package's share folder as well as installing the 27 | # required ament index resources needed for the plugin description to be 28 | # discovered later using the plugin category as a filter. 29 | # 30 | # The plugin category is commonly the name of the package which uses the 31 | # plugins or some name based on that and the type of package it is, e.g. `rviz` 32 | # or `rviz_plugins` or `rviz_display_plugins`. 33 | # 34 | # The relative_filename should be relative to the CMake file that calls this 35 | # macro. 36 | # It's relative path to this file will be preserved when being installed. 37 | # For example, if that argument is "plugins/my_desc.xml", then it will be 38 | # installed to "/share//plugins/my_desc.xml" or if the 39 | # argument is just "my_desc.xml" it would be installed to 40 | # "/share//my_desc.xml". 41 | # 42 | # This macro can be called multiple times, even multiple times per 43 | # "plugin_category", but "ament_package" must be called to finish the process. 44 | # 45 | # Where the file is installed to and the use of the ament index to locate the 46 | # file at runtime should be considered an implementation detail, and other 47 | # pluginlib functions should be used in both CMake and C++/Python/etc. code to 48 | # access these files and information. 49 | # 50 | # If migrating from previous pluginlib versions, you might start with a snippet 51 | # in the package.xml's export section like this: 52 | # 53 | # 54 | # ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the description file location 55 | # ^^^^^^ referred to as the "attribute" name, is usually "plugin" 56 | # ^^^^ the package name that uses/defines the plugins described in plugin_description 57 | # 58 | # For this new way of registering plugins, the attribute is no longer 59 | # configurable, and therefore has not corresponding place in this new API. 60 | # The package name is now the "plugin_category", and can stay the same or 61 | # it can become more specific, but it is determined by the packages that will 62 | # load the plugins at runtime. 63 | # The plugin description file location should no longer include the "${prefix}" 64 | # placeholder, and instead should only include a relative path to the file in 65 | # the current package's sources. 66 | # 67 | # Based on the example above this function would be used like this: 68 | # 69 | # pluginlib_export_plugin_description_file(rviz "plugin_description.xml") 70 | # 71 | # This macro assumes the package name is PROJECT_NAME. 72 | # 73 | # :param plugin_category: category for the type of plugin described; used at 74 | # runtime to locate all description files for that kind of plugin 75 | # :type plugin_category: string 76 | # :param relative_filename: relative path to the plugin description file 77 | # :type relative_filename: string 78 | # 79 | # @public 80 | # 81 | macro(pluginlib_export_plugin_description_file plugin_category relative_filename) 82 | set(abs_filename "${CMAKE_CURRENT_SOURCE_DIR}/${relative_filename}") 83 | if(NOT EXISTS "${abs_filename}") 84 | message(FATAL_ERROR "Given plugin description file '${abs_filename}' does not exist") 85 | endif() 86 | 87 | set(relative_dir "") 88 | get_filename_component(relative_dir "${relative_filename}" DIRECTORY) 89 | install(FILES ${relative_filename} DESTINATION share/${PROJECT_NAME}/${relative_dir}) 90 | 91 | # this accumulated value is written to the ament index resource file in the 92 | # ament_package() call via the pluginlib hook 93 | set(__PLUGINLIB_CATEGORY_CONTENT__${plugin_category} 94 | "${__PLUGINLIB_CATEGORY_CONTENT__${plugin_category}}share/${PROJECT_NAME}/${relative_filename}\n") 95 | list(APPEND __PLUGINLIB_PLUGIN_CATEGORIES ${plugin_category}) # duplicates are removes on use 96 | endmacro() 97 | -------------------------------------------------------------------------------- /cmake/pluginlib_package_hook.cmake: -------------------------------------------------------------------------------- 1 | # Copyright 2017 Open Source Robotics Foundation, Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | # install the ament resource for the plugin descriptions exported by 16 | # this package 17 | list(REMOVE_DUPLICATES __PLUGINLIB_PLUGIN_CATEGORIES) 18 | foreach(plugin_category ${__PLUGINLIB_PLUGIN_CATEGORIES}) 19 | # this assumes PROJECT_NAME is the package name 20 | # note the trailing `plugin` is the invariant "attribute name" from the old 21 | # style of registering plugins through pluginlib 22 | ament_index_register_resource(${plugin_category}__pluginlib__plugin 23 | CONTENT "${__PLUGINLIB_CATEGORY_CONTENT__${plugin_category}}") 24 | endforeach() 25 | -------------------------------------------------------------------------------- /include/pluginlib/class_desc.hpp: -------------------------------------------------------------------------------- 1 | // Copyright 2008, Willow Garage, Inc. All rights reserved. 2 | // 3 | // Redistribution and use in source and binary forms, with or without 4 | // modification, are permitted provided that the following conditions are met: 5 | // 6 | // * Redistributions of source code must retain the above copyright 7 | // notice, this list of conditions and the following disclaimer. 8 | // 9 | // * Redistributions in binary form must reproduce the above copyright 10 | // notice, this list of conditions and the following disclaimer in the 11 | // documentation and/or other materials provided with the distribution. 12 | // 13 | // * Neither the name of the Willow Garage nor the names of its 14 | // contributors may be used to endorse or promote products derived from 15 | // this software without specific prior written permission. 16 | // 17 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 21 | // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | // POSSIBILITY OF SUCH DAMAGE. 28 | 29 | #ifndef PLUGINLIB__CLASS_DESC_HPP_ 30 | #define PLUGINLIB__CLASS_DESC_HPP_ 31 | 32 | #include 33 | 34 | namespace pluginlib 35 | { 36 | 37 | /// Storage for information about a given class. 38 | class ClassDesc 39 | { 40 | public: 41 | /** 42 | * \param lookup_name The lookup name of the class 43 | * \param derived_class The type of the derived class of the class 44 | * \param base_class The type of the class, corresponds to the type of the base class 45 | * \param package The package the class lives in 46 | * \param description A description for the class 47 | * \param library_name The name of the containing library for the class (not a full path!) 48 | * \param plugin_manifest_path The path to the plugin manifest file 49 | */ 50 | ClassDesc( 51 | const std::string & lookup_name, const std::string & derived_class, 52 | const std::string & base_class, const std::string & package, 53 | const std::string & description, const std::string & library_name, 54 | const std::string & plugin_manifest_path) 55 | : lookup_name_(lookup_name), 56 | derived_class_(derived_class), 57 | base_class_(base_class), 58 | package_(package), 59 | description_(description), 60 | library_name_(library_name), 61 | resolved_library_path_("UNRESOLVED"), 62 | plugin_manifest_path_(plugin_manifest_path) {} 63 | 64 | std::string lookup_name_; 65 | std::string derived_class_; 66 | std::string base_class_; 67 | std::string package_; 68 | std::string description_; 69 | std::string library_name_; 70 | std::string resolved_library_path_; // This is set by pluginlib::ClassLoader at load time. 71 | std::string plugin_manifest_path_; 72 | }; 73 | 74 | } // namespace pluginlib 75 | 76 | #endif // PLUGINLIB__CLASS_DESC_HPP_ 77 | -------------------------------------------------------------------------------- /include/pluginlib/class_list_macros.hpp: -------------------------------------------------------------------------------- 1 | // Copyright 2008, Willow Garage, Inc. All rights reserved. 2 | // 3 | // Redistribution and use in source and binary forms, with or without 4 | // modification, are permitted provided that the following conditions are met: 5 | // 6 | // * Redistributions of source code must retain the above copyright 7 | // notice, this list of conditions and the following disclaimer. 8 | // 9 | // * Redistributions in binary form must reproduce the above copyright 10 | // notice, this list of conditions and the following disclaimer in the 11 | // documentation and/or other materials provided with the distribution. 12 | // 13 | // * Neither the name of the Willow Garage nor the names of its 14 | // contributors may be used to endorse or promote products derived from 15 | // this software without specific prior written permission. 16 | // 17 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 21 | // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | // POSSIBILITY OF SUCH DAMAGE. 28 | 29 | #ifndef PLUGINLIB__CLASS_LIST_MACROS_HPP_ 30 | #define PLUGINLIB__CLASS_LIST_MACROS_HPP_ 31 | 32 | #include 33 | 34 | /// Register a class with class loader to effectively export it for plugin loading later. 35 | /** 36 | * \def PLUGINLIB_EXPORT_CLASS(class_type, base_class_type) 37 | * \param class_type The real class name with namespace qualifier (e.g. Animals::Lion) 38 | * \param base_class_type The real base class type from which class_type inherits 39 | */ 40 | #define PLUGINLIB_EXPORT_CLASS(class_type, base_class_type) \ 41 | CLASS_LOADER_REGISTER_CLASS(class_type, base_class_type) 42 | 43 | #endif // PLUGINLIB__CLASS_LIST_MACROS_HPP_ 44 | -------------------------------------------------------------------------------- /include/pluginlib/class_loader.hpp: -------------------------------------------------------------------------------- 1 | // Copyright 2009, Willow Garage, Inc. All rights reserved. 2 | // 3 | // Redistribution and use in source and binary forms, with or without 4 | // modification, are permitted provided that the following conditions are met: 5 | // 6 | // * Redistributions of source code must retain the above copyright 7 | // notice, this list of conditions and the following disclaimer. 8 | // 9 | // * Redistributions in binary form must reproduce the above copyright 10 | // notice, this list of conditions and the following disclaimer in the 11 | // documentation and/or other materials provided with the distribution. 12 | // 13 | // * Neither the name of the Willow Garage nor the names of its 14 | // contributors may be used to endorse or promote products derived from 15 | // this software without specific prior written permission. 16 | // 17 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 21 | // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | // POSSIBILITY OF SUCH DAMAGE. 28 | 29 | #ifndef PLUGINLIB__CLASS_LOADER_HPP_ 30 | #define PLUGINLIB__CLASS_LOADER_HPP_ 31 | 32 | #include 33 | #include 34 | #include 35 | #include 36 | 37 | #include "class_loader/multi_library_class_loader.hpp" 38 | #include "pluginlib/class_desc.hpp" 39 | #include "pluginlib/class_loader_base.hpp" 40 | #include "pluginlib/exceptions.hpp" 41 | #include "tinyxml2.h" // NOLINT 42 | 43 | namespace pluginlib 44 | { 45 | 46 | template 47 | using UniquePtr = class_loader::ClassLoader::UniquePtr; 48 | 49 | /// A class to help manage and load classes. 50 | template 51 | class ClassLoader : public ClassLoaderBase 52 | { 53 | public: 54 | typedef typename std::map::iterator ClassMapIterator; 55 | 56 | public: 57 | /** 58 | * \param package The package containing the base class 59 | * \param base_class The type of the base class for classes to be loaded 60 | * \param attrib_name The attribute to search for in manifext.xml files, defaults to "plugin" 61 | * \param plugin_xml_paths The list of paths of plugin.xml files, defaults to be crawled via 62 | * ros::package::getPlugins() 63 | * \throws pluginlib::ClassLoaderException if package manifest cannot be found 64 | */ 65 | ClassLoader( 66 | std::string package, 67 | std::string base_class, 68 | std::string attrib_name = std::string("plugin"), 69 | std::vector plugin_xml_paths = std::vector()); 70 | 71 | ~ClassLoader(); 72 | 73 | /// Create an instance of a desired class. 74 | /** 75 | * Implicitly calls loadLibraryForClass() to increment the library counter. 76 | * 77 | * Deleting the instance and calling unloadLibraryForClass() is automatically 78 | * handled by the shared pointer. 79 | * \param lookup_name The name of the class to load 80 | * \throws pluginlib::LibraryLoadException when the library associated with 81 | * the class cannot be loaded 82 | * \throws pluginlib::CreateClassException when the class cannot be instantiated 83 | * \return An instance of the class 84 | */ 85 | std::shared_ptr createSharedInstance(const std::string & lookup_name); 86 | 87 | /// Create an instance of a desired class. 88 | /** 89 | * Implicitly calls loadLibraryForClass() to increment the library counter. 90 | * 91 | * Deleting the instance and calling unloadLibraryForClass() is automatically 92 | * handled by the unique pointer. 93 | * 94 | * If you release the wrapped pointer you must manually call the original 95 | * deleter when you want to destroy the released pointer. 96 | * 97 | * \param lookup_name The name of the class to load. 98 | * \throws pluginlib::LibraryLoadException when the library associated with 99 | * the class cannot be loaded. 100 | * \throws pluginlib::CreateClassException when the class cannot be instantiated 101 | * \return An instance of the class 102 | */ 103 | UniquePtr createUniqueInstance(const std::string & lookup_name); 104 | 105 | /// Create an instance of a desired class. 106 | /** 107 | * Implicitly calls loadLibraryForClass() to increment the library counter. 108 | * 109 | * \attention The ownership is transferred to the caller, which is responsible 110 | * for deleting the instance and calling unloadLibraryForClass() 111 | * (in order to decrement the associated library counter and unloading it 112 | * if it is no more used). 113 | * \param lookup_name The name of the class to load 114 | * \throws pluginlib::LibraryLoadException when the library associated with 115 | * the class cannot be loaded 116 | * \throws pluginlib::CreateClassException when the class cannot be instantiated 117 | * \return An instance of the class 118 | */ 119 | T * createUnmanagedInstance(const std::string & lookup_name); 120 | 121 | /// Return a list of all available plugin manifest paths for this ClassLoader's base class type. 122 | /** 123 | * \return A vector of strings corresponding to the paths of all available plugin manifests 124 | */ 125 | std::vector getPluginXmlPaths(); 126 | 127 | /// Return a list of all available classes for this ClassLoader's base class type. 128 | /** 129 | * \return A vector of strings corresponding to the names of all available classes 130 | */ 131 | std::vector getDeclaredClasses(); 132 | 133 | /// Strip the package name off of a lookup name. 134 | /** 135 | * \param lookup_name The name of the plugin 136 | * \return The name of the plugin stripped of the package name 137 | */ 138 | virtual std::string getName(const std::string & lookup_name); 139 | 140 | /// Given the lookup name of a class, return the type of the associated base class. 141 | /** 142 | * \return The type of the associated base class 143 | */ 144 | virtual std::string getBaseClassType() const; 145 | 146 | /// Given the lookup name of a class, return the type of the derived class associated with it. 147 | /** 148 | * \param lookup_name The name of the class 149 | * \return The name of the associated derived class 150 | */ 151 | virtual std::string getClassType(const std::string & lookup_name); 152 | 153 | /// Given the lookup name of a class, return its description. 154 | /** 155 | * \param lookup_name The lookup name of the class 156 | * \return The description of the class 157 | */ 158 | virtual std::string getClassDescription(const std::string & lookup_name); 159 | 160 | /// Given the name of a class, return the path to its associated library. 161 | /** 162 | * \param lookup_name The name of the class 163 | * \return The path to the associated library 164 | */ 165 | virtual std::string getClassLibraryPath(const std::string & lookup_name); 166 | 167 | /// Given the name of a class, return name of the containing package. 168 | /** 169 | * \param lookup_name The name of the class 170 | * \return The name of the containing package 171 | */ 172 | virtual std::string getClassPackage(const std::string & lookup_name); 173 | 174 | /// Given the name of a class, return the path of the associated plugin manifest. 175 | /** 176 | * \param lookup_name The name of the class 177 | * \return The path of the associated plugin manifest 178 | */ 179 | virtual std::string getPluginManifestPath(const std::string & lookup_name); 180 | 181 | /// Return the libraries that are registered and can be loaded. 182 | /** 183 | * \return A vector of strings corresponding to the names of registered libraries 184 | */ 185 | virtual std::vector getRegisteredLibraries(); 186 | 187 | /// Check if the library for a given class is currently loaded. 188 | /** 189 | * \param lookup_name The lookup name of the class to query 190 | * \return True if the class is loaded, false otherwise 191 | */ 192 | bool isClassLoaded(const std::string & lookup_name); 193 | 194 | /// Check if the class associated with a plugin name is available to be loaded. 195 | /** 196 | * \param lookup_name The name of the plugin 197 | * \return true if the plugin is available, false otherwise 198 | */ 199 | virtual bool isClassAvailable(const std::string & lookup_name); 200 | 201 | /// Attempt to load the library containing a class with a given name. 202 | /** 203 | * The counter for the library uses (refcount) is also incremented. 204 | * 205 | * \param lookup_name The lookup name of the class to load 206 | * \throws pluginlib::LibraryLoadException if the library for the 207 | * class cannot be loaded 208 | */ 209 | virtual void loadLibraryForClass(const std::string & lookup_name); 210 | 211 | /// Refresh the list of all available classes for this ClassLoader's base class type. 212 | /** 213 | * \throws pluginlib::LibraryLoadException if package manifest cannot be found 214 | */ 215 | virtual void refreshDeclaredClasses(); 216 | 217 | /// Decrement the counter for the library containing a class with a given name. 218 | /** 219 | * Also try to unload the library, If the counter reaches zero. 220 | * 221 | * \param lookup_name The lookup name of the class to unload 222 | * \throws pluginlib::LibraryUnloadException if the library for the 223 | * class cannot be unloaded 224 | * \return The number of pending unloads until the library is removed from memory 225 | */ 226 | virtual int unloadLibraryForClass(const std::string & lookup_name); 227 | 228 | private: 229 | /// Return the paths to plugin.xml files. 230 | /** 231 | * \throws pluginlib::LibraryLoadException if package manifest cannot be found 232 | * \return A vector of paths 233 | */ 234 | std::vector getPluginXmlPaths( 235 | const std::string & package, 236 | const std::string & attrib_name); 237 | 238 | /// Return the available classes. 239 | /** 240 | * \param plugin_xml_paths The vector of paths of plugin.xml files 241 | * \throws pluginlib::LibraryLoadException if package manifest cannot be found 242 | * \return A map of class names and the corresponding descriptions 243 | */ 244 | std::map determineAvailableClasses( 245 | const std::vector & plugin_xml_paths); 246 | 247 | /// Open a package.xml file and extract the package name (i.e. contents of tag). 248 | /** 249 | * \param package_xml_path The path to the package.xml file 250 | * \return The name of the package if successful, otherwise an empty string 251 | */ 252 | std::string extractPackageNameFromPackageXML(const std::string & package_xml_path); 253 | 254 | /// Get a list of paths to try to find a library. 255 | /** 256 | * As we transition from rosbuild to Catkin build systems, plugins can be 257 | * found in the old rosbuild place (pkg_name/lib usually) or somewhere in the 258 | * Catkin build space. 259 | */ 260 | std::vector getAllLibraryPathsToTry( 261 | const std::string & library_name, 262 | const std::string & exporting_package_name); 263 | 264 | /// Return an error message for an unknown class. 265 | /** 266 | * \param lookup_name The name of the class 267 | * \return The error message 268 | */ 269 | std::string getErrorStringForUnknownClass(const std::string & lookup_name); 270 | 271 | /// Get the standard path separator for the native OS (e.g. "/" on *nix, "\" on Windows). 272 | std::string getPathSeparator(); 273 | 274 | /// Get the package name from a path to a plugin XML file. 275 | std::string getPackageFromPluginXMLFilePath(const std::string & path); 276 | 277 | /// Join two filesystem paths together utilizing appropriate path separator. 278 | std::string joinPaths(const std::string & path1, const std::string & path2); 279 | 280 | /// Parse a plugin XML file. 281 | /** 282 | * Also insert the appropriate ClassDesc entries into the passes 283 | * classes_available map. 284 | */ 285 | void processSingleXMLPluginFile( 286 | const std::string & xml_file, std::map & class_available); 288 | 289 | /// Strip all but the filename from an explicit file path. 290 | /** 291 | * \param path The path to strip 292 | * \return The basename of the path 293 | */ 294 | std::string stripAllButFileFromPath(const std::string & path); 295 | 296 | 297 | /// Helper function for unloading a shared library. 298 | /** 299 | * \param library_path The exact path to the library to unload 300 | * \return The number of pending unloads until the library is removed from memory 301 | */ 302 | int unloadClassLibraryInternal(const std::string & library_path); 303 | 304 | private: 305 | std::vector plugin_xml_paths_; 306 | // Map from library to class's descriptions described in XML. 307 | std::map classes_available_; 308 | std::string package_; 309 | std::string base_class_; 310 | std::string attrib_name_; 311 | class_loader::MultiLibraryClassLoader lowlevel_class_loader_; // The underlying classloader 312 | }; 313 | 314 | } // namespace pluginlib 315 | 316 | // Note: The implementation of the methods is in a separate file for clarity. 317 | #include "./class_loader_imp.hpp" 318 | 319 | #endif // PLUGINLIB__CLASS_LOADER_HPP_ 320 | -------------------------------------------------------------------------------- /include/pluginlib/class_loader_base.hpp: -------------------------------------------------------------------------------- 1 | // Copyright 2012, Willow Garage, Inc. All rights reserved. 2 | // 3 | // Redistribution and use in source and binary forms, with or without 4 | // modification, are permitted provided that the following conditions are met: 5 | // 6 | // * Redistributions of source code must retain the above copyright 7 | // notice, this list of conditions and the following disclaimer. 8 | // 9 | // * Redistributions in binary form must reproduce the above copyright 10 | // notice, this list of conditions and the following disclaimer in the 11 | // documentation and/or other materials provided with the distribution. 12 | // 13 | // * Neither the name of the Willow Garage nor the names of its 14 | // contributors may be used to endorse or promote products derived from 15 | // this software without specific prior written permission. 16 | // 17 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 21 | // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | // POSSIBILITY OF SUCH DAMAGE. 28 | 29 | #ifndef PLUGINLIB__CLASS_LOADER_BASE_HPP_ 30 | #define PLUGINLIB__CLASS_LOADER_BASE_HPP_ 31 | 32 | #include 33 | #include 34 | 35 | namespace pluginlib 36 | { 37 | /// Pure virtual base class of pluginlib::ClassLoader which is not templated. 38 | /** 39 | * This allows the writing of non-templated manager code 40 | * which can call all the administrative functions of ClassLoaders - 41 | * everything except createInstance() and createUnmanagedInstance(). 42 | */ 43 | class ClassLoaderBase 44 | { 45 | public: 46 | /// Empty virtual destructor. 47 | virtual ~ClassLoaderBase() {} 48 | 49 | /// Return a list of all available plugin manifest paths. 50 | /** 51 | * \return A vector of strings corresponding to the paths of all available plugin manifests 52 | */ 53 | virtual std::vector getPluginXmlPaths() = 0; 54 | 55 | /// Return a list of all available classes for this ClassLoader's base class type. 56 | /** 57 | * \return A vector of strings corresponding to the names of all available classes 58 | */ 59 | virtual std::vector getDeclaredClasses() = 0; 60 | 61 | /// Refresh the list of all available classes for this ClassLoader's base class type. 62 | /** 63 | * \throws pluginlib::LibraryLoadException if package manifest cannot be found 64 | */ 65 | virtual void refreshDeclaredClasses() = 0; 66 | 67 | /// Strip the package name off of a lookup name. 68 | /** 69 | * \param lookup_name The name of the plugin 70 | * \return The name of the plugin stripped of the package name 71 | */ 72 | virtual std::string getName(const std::string & lookup_name) = 0; 73 | 74 | /// Check if the class associated with a plugin name is available to be loaded. 75 | /** 76 | * \param lookup_name The name of the plugin 77 | * \return True if the plugin is available, false otherwise 78 | */ 79 | virtual bool isClassAvailable(const std::string & lookup_name) = 0; 80 | 81 | /// Given the lookup name of a class, return the type of the derived class associated with it. 82 | /** 83 | * \param lookup_name The name of the class 84 | * \return The name of the associated derived class 85 | */ 86 | virtual std::string getClassType(const std::string & lookup_name) = 0; 87 | 88 | /// Given the lookup name of a class, return its description. 89 | /** 90 | * \param lookup_name The lookup name of the class 91 | * \return The description of the class 92 | */ 93 | virtual std::string getClassDescription(const std::string & lookup_name) = 0; 94 | 95 | /// Given the lookup name of a class, return the type of the associated base class. 96 | /** 97 | * \return The type of the associated base class 98 | */ 99 | virtual std::string getBaseClassType() const = 0; 100 | 101 | /// Given the name of a class, return name of the containing package. 102 | /** 103 | * \param lookup_name The name of the class 104 | * \return The name of the containing package 105 | */ 106 | virtual std::string getClassPackage(const std::string & lookup_name) = 0; 107 | 108 | /// Given the name of a class, return the path of the associated plugin manifest. 109 | /** 110 | * \param lookup_name The name of the class 111 | * \return The path of the associated plugin manifest 112 | */ 113 | virtual std::string getPluginManifestPath(const std::string & lookup_name) = 0; 114 | 115 | /// Check if a given class is currently loaded. 116 | /** 117 | * \param lookup_name The lookup name of the class to query 118 | * \return True if the class is loaded, false otherwise 119 | */ 120 | virtual bool isClassLoaded(const std::string & lookup_name) = 0; 121 | 122 | /// Attempt to load a class with a given name. 123 | /** 124 | * \param lookup_name The lookup name of the class to load 125 | * \throws pluginlib::LibraryLoadException if the library for the 126 | * class cannot be loaded 127 | */ 128 | virtual void loadLibraryForClass(const std::string & lookup_name) = 0; 129 | 130 | /// Attempt to unload a class with a given name. 131 | /** 132 | * \param lookup_name The lookup name of the class to unload 133 | * \throws pluginlib::LibraryUnloadException if the library for the class 134 | * cannot be unloaded 135 | * \return The number of pending unloads until the library is removed from memory 136 | */ 137 | virtual int unloadLibraryForClass(const std::string & lookup_name) = 0; 138 | 139 | /// Return the libraries that are registered and can be loaded. 140 | /** 141 | * \return A vector of strings corresponding to the names of registered libraries 142 | */ 143 | virtual std::vector getRegisteredLibraries() = 0; 144 | 145 | /// Given the name of a class, return the path to its associated library. 146 | /** 147 | * \param lookup_name The name of the class 148 | * \return The path to the associated library 149 | */ 150 | virtual std::string getClassLibraryPath(const std::string & lookup_name) = 0; 151 | }; 152 | } // namespace pluginlib 153 | 154 | #endif // PLUGINLIB__CLASS_LOADER_BASE_HPP_ 155 | -------------------------------------------------------------------------------- /include/pluginlib/class_loader_imp.hpp: -------------------------------------------------------------------------------- 1 | // Copyright 2008, Willow Garage, Inc. All rights reserved. 2 | // 3 | // Redistribution and use in source and binary forms, with or without 4 | // modification, are permitted provided that the following conditions are met: 5 | // 6 | // * Redistributions of source code must retain the above copyright 7 | // notice, this list of conditions and the following disclaimer. 8 | // 9 | // * Redistributions in binary form must reproduce the above copyright 10 | // notice, this list of conditions and the following disclaimer in the 11 | // documentation and/or other materials provided with the distribution. 12 | // 13 | // * Neither the name of the Willow Garage nor the names of its 14 | // contributors may be used to endorse or promote products derived from 15 | // this software without specific prior written permission. 16 | // 17 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 21 | // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | // POSSIBILITY OF SUCH DAMAGE. 28 | 29 | #ifndef PLUGINLIB__CLASS_LOADER_IMP_HPP_ 30 | #define PLUGINLIB__CLASS_LOADER_IMP_HPP_ 31 | 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | 43 | #include "ament_index_cpp/get_package_prefix.hpp" 44 | #include "ament_index_cpp/get_package_share_directory.hpp" 45 | #include "ament_index_cpp/get_resource.hpp" 46 | #include "ament_index_cpp/get_resources.hpp" 47 | #include "class_loader/class_loader.hpp" 48 | #include "rcpputils/shared_library.hpp" 49 | #include "rcutils/logging_macros.h" 50 | 51 | #include "./class_loader.hpp" 52 | #include "./impl/split.hpp" 53 | 54 | #ifdef _WIN32 55 | #define CLASS_LOADER_IMPL_OS_PATHSEP ";" 56 | #else 57 | #define CLASS_LOADER_IMPL_OS_PATHSEP ":" 58 | #endif 59 | 60 | namespace pluginlib 61 | { 62 | 63 | template 64 | ClassLoader::ClassLoader( 65 | std::string package, 66 | std::string base_class, 67 | std::string attrib_name, 68 | std::vector plugin_xml_paths) 69 | : plugin_xml_paths_(plugin_xml_paths), 70 | package_(package), 71 | base_class_(base_class), 72 | attrib_name_(attrib_name), 73 | // NOTE: The parameter to the class loader enables/disables on-demand class 74 | // loading/unloading. 75 | // Leaving it off for now... libraries will be loaded immediately and won't 76 | // be unloaded until class loader is destroyed or force unload. 77 | lowlevel_class_loader_(false) 78 | /***************************************************************************/ 79 | { 80 | RCUTILS_LOG_DEBUG_NAMED("pluginlib.ClassLoader", "Creating ClassLoader, base = %s, address = %p", 81 | base_class.c_str(), static_cast(this)); 82 | try { 83 | ament_index_cpp::get_package_prefix(package_); 84 | } catch (const ament_index_cpp::PackageNotFoundError & exception) { 85 | // rethrow as class loader exception, package name is in the error message already. 86 | throw pluginlib::ClassLoaderException(exception.what()); 87 | } 88 | 89 | if (0 == plugin_xml_paths_.size()) { 90 | plugin_xml_paths_ = getPluginXmlPaths(package_, attrib_name_); 91 | } 92 | classes_available_ = determineAvailableClasses(plugin_xml_paths_); 93 | RCUTILS_LOG_DEBUG_NAMED("pluginlib.ClassLoader", 94 | "Finished constructring ClassLoader, base = %s, address = %p", 95 | base_class.c_str(), static_cast(this)); 96 | } 97 | 98 | template 99 | ClassLoader::~ClassLoader() 100 | /***************************************************************************/ 101 | { 102 | RCUTILS_LOG_DEBUG_NAMED("pluginlib.ClassLoader", 103 | "Destroying ClassLoader, base = %s, address = %p", 104 | getBaseClassType().c_str(), static_cast(this)); 105 | } 106 | 107 | template 108 | std::shared_ptr ClassLoader::createSharedInstance(const std::string & lookup_name) 109 | /***************************************************************************/ 110 | { 111 | return createUniqueInstance(lookup_name); 112 | } 113 | 114 | template 115 | UniquePtr ClassLoader::createUniqueInstance(const std::string & lookup_name) 116 | { 117 | RCUTILS_LOG_DEBUG_NAMED("pluginlib.ClassLoader", 118 | "Attempting to create managed (unique) instance for class %s.", 119 | lookup_name.c_str()); 120 | 121 | if (!isClassLoaded(lookup_name)) { 122 | loadLibraryForClass(lookup_name); 123 | } 124 | 125 | try { 126 | std::string class_type = getClassType(lookup_name); 127 | RCUTILS_LOG_DEBUG_NAMED("pluginlib.ClassLoader", "%s maps to real class type %s", 128 | lookup_name.c_str(), class_type.c_str()); 129 | 130 | UniquePtr obj = lowlevel_class_loader_.createUniqueInstance(class_type); 131 | 132 | RCUTILS_LOG_DEBUG_NAMED("pluginlib.ClassLoader", 133 | "std::unique_ptr to object of real type %s created.", 134 | class_type.c_str()); 135 | 136 | return obj; 137 | } catch (const class_loader::CreateClassException & ex) { 138 | RCUTILS_LOG_DEBUG_NAMED("pluginlib.ClassLoader", 139 | "Exception raised by low-level multi-library class loader when attempting " 140 | "to create instance of class %s.", 141 | lookup_name.c_str()); 142 | throw pluginlib::CreateClassException(ex.what()); 143 | } 144 | } 145 | 146 | template 147 | T * ClassLoader::createUnmanagedInstance(const std::string & lookup_name) 148 | /***************************************************************************/ 149 | { 150 | RCUTILS_LOG_DEBUG_NAMED("pluginlib.ClassLoader", 151 | "Attempting to create UNMANAGED instance for class %s.", 152 | lookup_name.c_str()); 153 | 154 | if (!isClassLoaded(lookup_name)) { 155 | loadLibraryForClass(lookup_name); 156 | } 157 | 158 | T * instance = 0; 159 | try { 160 | RCUTILS_LOG_DEBUG_NAMED("pluginlib.ClassLoader", 161 | "Attempting to create instance through low level multi-library class loader."); 162 | std::string class_type = getClassType(lookup_name); 163 | RCUTILS_LOG_DEBUG_NAMED("pluginlib.ClassLoader", "%s maps to real class type %s", 164 | lookup_name.c_str(), class_type.c_str()); 165 | instance = lowlevel_class_loader_.createUnmanagedInstance(class_type); 166 | RCUTILS_LOG_DEBUG_NAMED("pluginlib.ClassLoader", 167 | "Instance of type %s created.", 168 | class_type.c_str()); 169 | } catch (const class_loader::CreateClassException & ex) { 170 | RCUTILS_LOG_DEBUG_NAMED("pluginlib.ClassLoader", 171 | "Exception raised by low-level multi-library class loader when attempting " 172 | "to create UNMANAGED instance of class %s.", 173 | lookup_name.c_str()); 174 | throw pluginlib::CreateClassException(ex.what()); 175 | } 176 | return instance; 177 | } 178 | 179 | template 180 | std::vector ClassLoader::getPluginXmlPaths( 181 | const std::string & package, 182 | const std::string & attrib_name) 183 | /***************************************************************************/ 184 | { 185 | // Pull possible files from manifests of packages which depend on this package and export class 186 | std::vector paths; 187 | { 188 | // the convention is to create an ament resource which a concatenation of 189 | // the package name, "pluginlib", and the attribute being exported 190 | // __ is used as the concatenation delimiter because it cannot be in a 191 | // package name 192 | std::string resource_name = package + "__pluginlib__" + attrib_name; 193 | auto plugin_packages_with_prefixes = ament_index_cpp::get_resources(resource_name); 194 | for (const auto & package_prefix_pair : plugin_packages_with_prefixes) { 195 | // it is also convention to place the relative path to the plugin xml in 196 | // the ament resource file 197 | std::string resource_content; 198 | { 199 | using ament_index_cpp::get_resource; 200 | if (!get_resource(resource_name, package_prefix_pair.first, resource_content)) { 201 | RCUTILS_LOG_WARN_NAMED("pluginlib.ClassLoader", 202 | "unexpectedly not able to find ament resource '%s' for package '%s'", 203 | resource_name.c_str(), 204 | package_prefix_pair.first.c_str() 205 | ); 206 | continue; 207 | } 208 | } 209 | // the content may contain multiple plugin description files 210 | std::stringstream ss(resource_content); 211 | std::string line; 212 | while (std::getline(ss, line, '\n')) { 213 | if (!line.empty()) { 214 | // store the prefix for the package with a plugin and the relative path 215 | // to the plugin xml file 216 | paths.push_back(package_prefix_pair.second + "/" + line); 217 | } 218 | } 219 | } 220 | } 221 | return paths; 222 | } 223 | 224 | template 225 | std::map ClassLoader::determineAvailableClasses( 226 | const std::vector & plugin_xml_paths) 227 | /***************************************************************************/ 228 | { 229 | // mas - This method requires major refactoring... 230 | // not only is it really long and confusing but a lot of the comments do not 231 | // seem to be correct. 232 | // With time I keep correcting small things, but a good rewrite is needed. 233 | 234 | RCUTILS_LOG_DEBUG_NAMED("pluginlib.ClassLoader", "Entering determineAvailableClasses()..."); 235 | std::map classes_available; 236 | 237 | // Walk the list of all plugin XML files (variable "paths") that are exported by the build system 238 | for (std::vector::const_iterator it = plugin_xml_paths.begin(); 239 | it != plugin_xml_paths.end(); ++it) 240 | { 241 | try { 242 | processSingleXMLPluginFile(*it, classes_available); 243 | } catch (const pluginlib::InvalidXMLException & e) { 244 | RCUTILS_LOG_ERROR_NAMED("pluginlib.ClassLoader", 245 | "Skipped loading plugin with error: %s.", 246 | e.what()); 247 | } 248 | } 249 | 250 | RCUTILS_LOG_DEBUG_NAMED("pluginlib.ClassLoader", "Exiting determineAvailableClasses()..."); 251 | return classes_available; 252 | } 253 | 254 | template 255 | std::string ClassLoader::extractPackageNameFromPackageXML(const std::string & package_xml_path) 256 | /***************************************************************************/ 257 | { 258 | tinyxml2::XMLDocument document; 259 | document.LoadFile(package_xml_path.c_str()); 260 | tinyxml2::XMLElement * doc_root_node = document.FirstChildElement("package"); 261 | if (NULL == doc_root_node) { 262 | RCUTILS_LOG_ERROR_NAMED("pluginlib.ClassLoader", 263 | "Could not find a root element for package manifest at %s.", 264 | package_xml_path.c_str()); 265 | return ""; 266 | } 267 | 268 | assert(document.RootElement() == doc_root_node); 269 | 270 | tinyxml2::XMLElement * package_name_node = doc_root_node->FirstChildElement("name"); 271 | if (NULL == package_name_node) { 272 | RCUTILS_LOG_ERROR_NAMED("pluginlib.ClassLoader", 273 | "package.xml at %s does not have a tag! Cannot determine package " 274 | "which exports plugin.", 275 | package_xml_path.c_str()); 276 | return ""; 277 | } 278 | 279 | const char * package_name_node_txt = package_name_node->GetText(); 280 | if (NULL == package_name_node_txt) { 281 | RCUTILS_LOG_ERROR_NAMED("pluginlib.ClassLoader", 282 | "package.xml at %s has an invalid tag! Cannot determine package " 283 | "which exports plugin.", 284 | package_xml_path.c_str()); 285 | return ""; 286 | } 287 | 288 | return package_name_node_txt; 289 | } 290 | 291 | template 292 | std::vector ClassLoader::getAllLibraryPathsToTry( 293 | const std::string & library_name, 294 | const std::string & exporting_package_name) 295 | /***************************************************************************/ 296 | { 297 | // To determine the common prefix of the paths to try, the prefix of the 298 | // exporting package is retrieved. 299 | // To that, various library folder names are added (lib, lib64, etc...) 300 | // Additionally, "libexec" like folders are checked, using the package name 301 | // as the libexec folder name within the library name. 302 | // Finally the library name (just the file name, stripped of extra relative) 303 | // with various extensions is concatenated to the various library directories. 304 | // 305 | // For example, if the package was 'rviz' and the library_name was 306 | // 'librviz_default_plugins', these paths might be tried: 307 | // 308 | // - /lib/librviz_default_plugins.so 309 | // - /lib64/librviz_default_plugins.so 310 | // - /bin/rviz_default_plugins.dll 311 | // - /lib/rviz/librviz_default_plugins.so 312 | // - /lib64/rviz/librviz_default_plugins.so 313 | // 314 | // The extension, e.g. `.so`, might be different based on the operating 315 | // system, e.g. it might be `.dylib` on macOS or `.dll` on Windows. 316 | // Similarly, the library might have the `lib` prefix added or removed. 317 | // Also, the library name might have a `d` added if the library is built 318 | // debug, depending on the system. 319 | 320 | // TODO(wjwwood): probably should avoid "searching" and just embed the 321 | // relative path to the libraries in the ament index, since CMake knows it 322 | // at build time... 323 | 324 | const std::string path_separator = getPathSeparator(); 325 | 326 | std::vector all_paths; // result of all pairs to search 327 | 328 | std::string package_prefix = ament_index_cpp::get_package_prefix(exporting_package_name); 329 | 330 | // Setup the directories to look in. 331 | std::vector all_search_paths = { 332 | // for now just try lib and lib64 (and their respective "libexec" directories) 333 | package_prefix + path_separator + "lib", 334 | package_prefix + path_separator + "lib64", 335 | package_prefix + path_separator + "bin", // also look in bin, for dll's on Windows 336 | package_prefix + path_separator + "lib" + path_separator + exporting_package_name, 337 | package_prefix + path_separator + "lib64" + path_separator + exporting_package_name, 338 | package_prefix + path_separator + "bin" + path_separator + exporting_package_name, 339 | }; 340 | 341 | std::string stripped_library_name = stripAllButFileFromPath(library_name); 342 | 343 | std::string library_name_alternative; // either lib or without lib prefix 344 | const char * lib_prefix = "lib"; 345 | if (library_name.rfind(lib_prefix, 0) == 0) { 346 | library_name_alternative = library_name.substr(strlen(lib_prefix)); 347 | RCUTILS_LOG_WARN_NAMED("pluginlib.ClassLoader", 348 | "given plugin name '%s' should be '%s' for better portability", 349 | library_name.c_str(), 350 | library_name_alternative.c_str()); 351 | } else { 352 | library_name_alternative = lib_prefix + library_name; 353 | } 354 | std::string stripped_library_name_alternative = stripAllButFileFromPath(library_name_alternative); 355 | 356 | // Setup the relative file paths to pair with the search directories above. 357 | std::vector all_relative_library_paths = { 358 | rcpputils::get_platform_library_name(library_name), 359 | rcpputils::get_platform_library_name(library_name_alternative), 360 | rcpputils::get_platform_library_name(stripped_library_name), 361 | rcpputils::get_platform_library_name(stripped_library_name_alternative) 362 | }; 363 | std::vector all_relative_debug_library_paths = { 364 | rcpputils::get_platform_library_name(library_name, true), 365 | rcpputils::get_platform_library_name(library_name_alternative, true), 366 | rcpputils::get_platform_library_name(stripped_library_name, true), 367 | rcpputils::get_platform_library_name(stripped_library_name_alternative, true) 368 | }; 369 | 370 | for (auto && current_search_path : all_search_paths) { 371 | for (auto && current_library_path : all_relative_library_paths) { 372 | all_paths.push_back(current_search_path + path_separator + current_library_path); 373 | } 374 | for (auto && current_library_path : all_relative_debug_library_paths) { 375 | all_paths.push_back(current_search_path + path_separator + current_library_path); 376 | } 377 | } 378 | 379 | for (auto && path : all_paths) { 380 | RCUTILS_LOG_DEBUG_NAMED("pluginlib.ClassLoader", 381 | "[search path for '%s']: '%s'", 382 | library_name.c_str(), 383 | path.c_str()); 384 | } 385 | 386 | return all_paths; 387 | } 388 | 389 | template 390 | bool ClassLoader::isClassLoaded(const std::string & lookup_name) 391 | /***************************************************************************/ 392 | { 393 | return lowlevel_class_loader_.isClassAvailable(getClassType(lookup_name)); 394 | } 395 | 396 | template 397 | std::string ClassLoader::getBaseClassType() const 398 | /***************************************************************************/ 399 | { 400 | return base_class_; 401 | } 402 | 403 | template 404 | std::string ClassLoader::getClassDescription(const std::string & lookup_name) 405 | /***************************************************************************/ 406 | { 407 | ClassMapIterator it = classes_available_.find(lookup_name); 408 | if (it != classes_available_.end()) { 409 | return it->second.description_; 410 | } 411 | return ""; 412 | } 413 | 414 | template 415 | std::string ClassLoader::getClassType(const std::string & lookup_name) 416 | /***************************************************************************/ 417 | { 418 | ClassMapIterator it = classes_available_.find(lookup_name); 419 | if (it != classes_available_.end()) { 420 | return it->second.derived_class_; 421 | } 422 | return ""; 423 | } 424 | 425 | template 426 | std::string ClassLoader::getClassLibraryPath(const std::string & lookup_name) 427 | /***************************************************************************/ 428 | { 429 | if (classes_available_.find(lookup_name) == classes_available_.end()) { 430 | std::ostringstream error_msg; 431 | error_msg << "Could not find library corresponding to plugin " << lookup_name << 432 | ". Make sure the plugin description XML file has the correct name of the library."; 433 | throw pluginlib::LibraryLoadException(error_msg.str()); 434 | } 435 | ClassMapIterator it = classes_available_.find(lookup_name); 436 | std::string library_name = it->second.library_name_; 437 | RCUTILS_LOG_DEBUG_NAMED("pluginlib.ClassLoader", 438 | "Class %s maps to library %s in classes_available_.", 439 | lookup_name.c_str(), library_name.c_str()); 440 | 441 | std::vector paths_to_try = 442 | getAllLibraryPathsToTry(library_name, it->second.package_); 443 | 444 | RCUTILS_LOG_DEBUG_NAMED("pluginlib.ClassLoader", 445 | "Iterating through all possible paths where %s could be located...", 446 | library_name.c_str()); 447 | for (auto path_it = paths_to_try.begin(); path_it != paths_to_try.end(); path_it++) { 448 | RCUTILS_LOG_DEBUG_NAMED("pluginlib.ClassLoader", "Checking path %s ", path_it->c_str()); 449 | if (std::filesystem::exists(*path_it)) { 450 | RCUTILS_LOG_DEBUG_NAMED("pluginlib.ClassLoader", "Library %s found at explicit path %s.", 451 | library_name.c_str(), path_it->c_str()); 452 | return *path_it; 453 | } 454 | } 455 | std::ostringstream error_msg; 456 | error_msg << "Could not find library corresponding to plugin " << lookup_name << 457 | ". Make sure that the library '" << library_name << "' actually exists."; 458 | throw pluginlib::LibraryLoadException(error_msg.str()); 459 | } 460 | 461 | template 462 | std::string ClassLoader::getClassPackage(const std::string & lookup_name) 463 | /***************************************************************************/ 464 | { 465 | ClassMapIterator it = classes_available_.find(lookup_name); 466 | if (it != classes_available_.end()) { 467 | return it->second.package_; 468 | } 469 | return ""; 470 | } 471 | 472 | template 473 | std::vector ClassLoader::getPluginXmlPaths() 474 | /***************************************************************************/ 475 | { 476 | return plugin_xml_paths_; 477 | } 478 | 479 | template 480 | std::vector ClassLoader::getDeclaredClasses() 481 | /***************************************************************************/ 482 | { 483 | std::vector lookup_names; 484 | for (ClassMapIterator it = classes_available_.begin(); it != classes_available_.end(); ++it) { 485 | lookup_names.push_back(it->first); 486 | } 487 | 488 | return lookup_names; 489 | } 490 | 491 | template 492 | std::string ClassLoader::getErrorStringForUnknownClass(const std::string & lookup_name) 493 | /***************************************************************************/ 494 | { 495 | std::string declared_types; 496 | std::vector types = getDeclaredClasses(); 497 | for (unsigned int i = 0; i < types.size(); i++) { 498 | declared_types = declared_types + std::string(" ") + types[i]; 499 | } 500 | return "According to the loaded plugin descriptions the class " + lookup_name + 501 | " with base class type " + base_class_ + " does not exist. Declared types are " + 502 | declared_types; 503 | } 504 | 505 | template 506 | std::string ClassLoader::getName(const std::string & lookup_name) 507 | /***************************************************************************/ 508 | { 509 | // remove the package name to get the raw plugin name 510 | std::vector result = pluginlib::impl::split(lookup_name, "/|:"); 511 | return result.back(); 512 | } 513 | 514 | template 515 | std::string 516 | ClassLoader::getPackageFromPluginXMLFilePath(const std::string & plugin_xml_file_path) 517 | /***************************************************************************/ 518 | { 519 | // Note: This method takes an input a path to a plugin xml file and must determine which 520 | // package the XML file came from. This is not necessarily the same thing as the member 521 | // variable "package_". The plugin xml file can be located anywhere in the source tree for a 522 | // package 523 | 524 | // catkin and ament: 525 | // 1. Find nearest encasing package.xml 526 | // 2. Extract name of package from package.xml 527 | 528 | std::string package_name; 529 | std::filesystem::path p(plugin_xml_file_path); 530 | std::filesystem::path parent = p.parent_path(); 531 | 532 | // Figure out exactly which package the passed XML file is exported by. 533 | while (true) { 534 | if (std::filesystem::exists(parent / "package.xml")) { 535 | std::string package_file_path = (parent / "package.xml").string(); 536 | return extractPackageNameFromPackageXML(package_file_path); 537 | } 538 | 539 | // Recursive case - hop one folder up and store current parent 540 | // parent_path() returns the current path if we reached the root. 541 | p = parent; 542 | parent = parent.parent_path(); 543 | 544 | // Base case - reached root and cannot find what we're looking for 545 | if (parent.string().empty() || (p == parent)) { 546 | return ""; 547 | } 548 | } 549 | 550 | return package_name; 551 | } 552 | 553 | template 554 | std::string ClassLoader::getPathSeparator() 555 | /***************************************************************************/ 556 | { 557 | return std::string(1, std::filesystem::path::preferred_separator); 558 | } 559 | 560 | 561 | template 562 | std::string ClassLoader::getPluginManifestPath(const std::string & lookup_name) 563 | /***************************************************************************/ 564 | { 565 | ClassMapIterator it = classes_available_.find(lookup_name); 566 | if (it != classes_available_.end()) { 567 | return it->second.plugin_manifest_path_; 568 | } 569 | return ""; 570 | } 571 | 572 | 573 | template 574 | std::vector ClassLoader::getRegisteredLibraries() 575 | /***************************************************************************/ 576 | { 577 | return lowlevel_class_loader_.getRegisteredLibraries(); 578 | } 579 | 580 | template 581 | bool ClassLoader::isClassAvailable(const std::string & lookup_name) 582 | /***************************************************************************/ 583 | { 584 | return classes_available_.find(lookup_name) != classes_available_.end(); 585 | } 586 | 587 | template 588 | std::string ClassLoader::joinPaths(const std::string & path1, const std::string & path2) 589 | /***************************************************************************/ 590 | { 591 | std::filesystem::path p1(path1); 592 | return (p1 / path2).string(); 593 | } 594 | 595 | template 596 | void ClassLoader::loadLibraryForClass(const std::string & lookup_name) 597 | /***************************************************************************/ 598 | { 599 | ClassMapIterator it = classes_available_.find(lookup_name); 600 | if (it == classes_available_.end()) { 601 | RCUTILS_LOG_DEBUG_NAMED("pluginlib.ClassLoader", 602 | "Class %s has no mapping in classes_available_.", 603 | lookup_name.c_str()); 604 | throw pluginlib::LibraryLoadException(getErrorStringForUnknownClass(lookup_name)); 605 | } 606 | 607 | std::string library_path = getClassLibraryPath(lookup_name); 608 | 609 | try { 610 | lowlevel_class_loader_.loadLibrary(library_path); 611 | it->second.resolved_library_path_ = library_path; 612 | } catch (const class_loader::LibraryLoadException & ex) { 613 | std::string error_string = 614 | "Failed to load library " + library_path + ". " 615 | "Make sure that you are calling the PLUGINLIB_EXPORT_CLASS macro in the " 616 | "library code, and that names are consistent between this macro and your XML. " 617 | "Error string: " + ex.what(); 618 | throw pluginlib::LibraryLoadException(error_string); 619 | } 620 | } 621 | 622 | template 623 | void ClassLoader::processSingleXMLPluginFile( 624 | const std::string & xml_file, std::map & classes_available) 626 | /***************************************************************************/ 627 | { 628 | RCUTILS_LOG_DEBUG_NAMED("pluginlib.ClassLoader", "Processing xml file %s...", xml_file.c_str()); 629 | tinyxml2::XMLDocument document; 630 | document.LoadFile(xml_file.c_str()); 631 | tinyxml2::XMLElement * config = document.RootElement(); 632 | if (NULL == config) { 633 | throw pluginlib::InvalidXMLException( 634 | "XML Document '" + xml_file + 635 | "' has no Root Element. This likely means the XML is malformed or missing."); 636 | return; 637 | } 638 | const char * config_value = config->Value(); 639 | if (NULL == config_value) { 640 | throw pluginlib::InvalidXMLException( 641 | "XML Document '" + xml_file + 642 | "' has an invalid Root Element. This likely means the XML is malformed or missing."); 643 | return; 644 | } 645 | if (!(strcmp(config_value, "library") == 0 || 646 | strcmp(config_value, "class_libraries") == 0)) 647 | { 648 | throw pluginlib::InvalidXMLException( 649 | "The XML document '" + xml_file + "' given to add must have either \"library\" or " 650 | "\"class_libraries\" as the root tag"); 651 | return; 652 | } 653 | // Step into the filter list if necessary 654 | if (strcmp(config_value, "class_libraries") == 0) { 655 | config = config->FirstChildElement("library"); 656 | } 657 | 658 | tinyxml2::XMLElement * library = config; 659 | while (library != NULL) { 660 | const char * path = library->Attribute("path"); 661 | if (NULL == path) { 662 | RCUTILS_LOG_ERROR_NAMED("pluginlib.ClassLoader", 663 | "Attribute 'path' in 'library' tag is missing in %s.", xml_file.c_str()); 664 | continue; 665 | } 666 | std::string library_path(path); 667 | if (0 == library_path.size()) { 668 | RCUTILS_LOG_ERROR_NAMED("pluginlib.ClassLoader", 669 | "Failed to find Path Attribute in library element in %s", xml_file.c_str()); 670 | continue; 671 | } 672 | 673 | std::string package_name = getPackageFromPluginXMLFilePath(xml_file); 674 | if ("" == package_name) { 675 | RCUTILS_LOG_ERROR_NAMED("pluginlib.ClassLoader", 676 | "Could not find package manifest (neither package.xml or deprecated " 677 | "manifest.xml) at same directory level as the plugin XML file %s. " 678 | "Plugins will likely not be exported properly.\n)", 679 | xml_file.c_str()); 680 | } 681 | 682 | tinyxml2::XMLElement * class_element = library->FirstChildElement("class"); 683 | while (class_element) { 684 | std::string derived_class; 685 | if (class_element->Attribute("type") != NULL) { 686 | derived_class = std::string(class_element->Attribute("type")); 687 | } else { 688 | throw pluginlib::ClassLoaderException( 689 | "Class could not be loaded. Attribute 'type' in class tag is missing."); 690 | } 691 | 692 | std::string base_class_type; 693 | if (class_element->Attribute("base_class_type") != NULL) { 694 | base_class_type = std::string(class_element->Attribute("base_class_type")); 695 | } else { 696 | throw pluginlib::ClassLoaderException( 697 | "Class could not be loaded. Attribute 'base_class_type' in class tag is missing."); 698 | } 699 | 700 | std::string lookup_name; 701 | if (class_element->Attribute("name") != NULL) { 702 | lookup_name = class_element->Attribute("name"); 703 | RCUTILS_LOG_DEBUG_NAMED("pluginlib.ClassLoader", 704 | "XML file specifies lookup name (i.e. magic name) = %s.", 705 | lookup_name.c_str()); 706 | } else { 707 | RCUTILS_LOG_DEBUG_NAMED("pluginlib.ClassLoader", 708 | "XML file has no lookup name (i.e. magic name) for class %s, " 709 | "assuming lookup_name == real class name.", 710 | derived_class.c_str()); 711 | lookup_name = derived_class; 712 | } 713 | 714 | // make sure that this class is of the right type before registering it 715 | if (base_class_type == base_class_) { 716 | // register class here 717 | tinyxml2::XMLElement * description = class_element->FirstChildElement("description"); 718 | std::string description_str; 719 | if (description) { 720 | description_str = description->GetText() ? description->GetText() : ""; 721 | } else { 722 | description_str = "No 'description' tag for this plugin in plugin description file."; 723 | } 724 | 725 | classes_available.insert(std::pair(lookup_name, 726 | ClassDesc(lookup_name, derived_class, base_class_type, package_name, description_str, 727 | library_path, xml_file))); 728 | } 729 | 730 | // step to next class_element 731 | class_element = class_element->NextSiblingElement("class"); 732 | } 733 | library = library->NextSiblingElement("library"); 734 | } 735 | } 736 | 737 | template 738 | void ClassLoader::refreshDeclaredClasses() 739 | /***************************************************************************/ 740 | { 741 | RCUTILS_LOG_DEBUG_NAMED("pluginlib.ClassLoader", "Refreshing declared classes."); 742 | // determine classes not currently loaded for removal 743 | std::list remove_classes; 744 | for (std::map::const_iterator it = classes_available_.begin(); 745 | it != classes_available_.end(); it++) 746 | { 747 | std::string resolved_library_path = it->second.resolved_library_path_; 748 | std::vector open_libs = lowlevel_class_loader_.getRegisteredLibraries(); 749 | if (std::find(open_libs.begin(), open_libs.end(), resolved_library_path) != open_libs.end()) { 750 | remove_classes.push_back(it->first); 751 | } 752 | } 753 | 754 | while (!remove_classes.empty()) { 755 | classes_available_.erase(remove_classes.front()); 756 | remove_classes.pop_front(); 757 | } 758 | 759 | // add new classes 760 | plugin_xml_paths_ = getPluginXmlPaths(package_, attrib_name_); 761 | std::map updated_classes = determineAvailableClasses(plugin_xml_paths_); 762 | for (std::map::const_iterator it = updated_classes.begin(); 763 | it != updated_classes.end(); it++) 764 | { 765 | if (classes_available_.find(it->first) == classes_available_.end()) { 766 | classes_available_.insert(std::pair(it->first, it->second)); 767 | } 768 | } 769 | } 770 | 771 | template 772 | std::string ClassLoader::stripAllButFileFromPath(const std::string & path) 773 | /***************************************************************************/ 774 | { 775 | std::string only_file; 776 | size_t c = path.find_last_of(getPathSeparator()); 777 | if (std::string::npos == c) { 778 | return path; 779 | } else { 780 | return path.substr(c, path.size()); 781 | } 782 | } 783 | 784 | template 785 | int ClassLoader::unloadLibraryForClass(const std::string & lookup_name) 786 | /***************************************************************************/ 787 | { 788 | ClassMapIterator it = classes_available_.find(lookup_name); 789 | if (it != classes_available_.end() && it->second.resolved_library_path_ != "UNRESOLVED") { 790 | std::string library_path = it->second.resolved_library_path_; 791 | RCUTILS_LOG_DEBUG_NAMED("pluginlib.ClassLoader", 792 | "Attempting to unload library %s for class %s", 793 | library_path.c_str(), lookup_name.c_str()); 794 | return unloadClassLibraryInternal(library_path); 795 | } else { 796 | throw pluginlib::LibraryUnloadException(getErrorStringForUnknownClass(lookup_name)); 797 | } 798 | } 799 | 800 | template 801 | int ClassLoader::unloadClassLibraryInternal(const std::string & library_path) 802 | /***************************************************************************/ 803 | { 804 | return lowlevel_class_loader_.unloadLibrary(library_path); 805 | } 806 | 807 | } // namespace pluginlib 808 | 809 | #endif // PLUGINLIB__CLASS_LOADER_IMP_HPP_ 810 | -------------------------------------------------------------------------------- /include/pluginlib/exceptions.hpp: -------------------------------------------------------------------------------- 1 | // Copyright 2012, Willow Garage, Inc. All rights reserved. 2 | // 3 | // Redistribution and use in source and binary forms, with or without 4 | // modification, are permitted provided that the following conditions are met: 5 | // 6 | // * Redistributions of source code must retain the above copyright 7 | // notice, this list of conditions and the following disclaimer. 8 | // 9 | // * Redistributions in binary form must reproduce the above copyright 10 | // notice, this list of conditions and the following disclaimer in the 11 | // documentation and/or other materials provided with the distribution. 12 | // 13 | // * Neither the name of the Willow Garage nor the names of its 14 | // contributors may be used to endorse or promote products derived from 15 | // this software without specific prior written permission. 16 | // 17 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 21 | // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | // POSSIBILITY OF SUCH DAMAGE. 28 | 29 | #ifndef PLUGINLIB__EXCEPTIONS_HPP_ 30 | #define PLUGINLIB__EXCEPTIONS_HPP_ 31 | 32 | #include 33 | #include 34 | 35 | namespace pluginlib 36 | { 37 | 38 | /// A base class for all pluginlib exceptions that inherits from std::runtime_exception. 39 | /** 40 | * \class PluginlibException 41 | */ 42 | class PluginlibException : public std::runtime_error 43 | { 44 | public: 45 | explicit PluginlibException(const std::string & error_desc) 46 | : std::runtime_error(error_desc) {} 47 | }; 48 | 49 | /// Thrown when pluginlib is unable to load a plugin XML file. 50 | /** 51 | * \class InvalidXMLException 52 | */ 53 | class InvalidXMLException : public PluginlibException 54 | { 55 | public: 56 | explicit InvalidXMLException(const std::string & error_desc) 57 | : PluginlibException(error_desc) {} 58 | }; 59 | 60 | /// Thrown when pluginlib is unable to load the library associated with a given plugin. 61 | /** 62 | * \class LibraryLoadException 63 | */ 64 | class LibraryLoadException : public PluginlibException 65 | { 66 | public: 67 | explicit LibraryLoadException(const std::string & error_desc) 68 | : PluginlibException(error_desc) {} 69 | }; 70 | 71 | /// Thrown when pluginlib is unable to instantiate a class loader. 72 | /** 73 | * \class ClassLoaderException 74 | */ 75 | class ClassLoaderException : public PluginlibException 76 | { 77 | public: 78 | explicit ClassLoaderException(const std::string & error_desc) 79 | : PluginlibException(error_desc) {} 80 | }; 81 | 82 | /// Thrown when pluginlib is unable to unload the library associated with a given plugin. 83 | /** 84 | * \class LibraryUnloadException 85 | */ 86 | class LibraryUnloadException : public PluginlibException 87 | { 88 | public: 89 | explicit LibraryUnloadException(const std::string & error_desc) 90 | : PluginlibException(error_desc) {} 91 | }; 92 | 93 | /// Thrown when pluginlib is unable to create the class associated with a given plugin. 94 | /** 95 | * \class CreateClassException 96 | */ 97 | class CreateClassException : public PluginlibException 98 | { 99 | public: 100 | explicit CreateClassException(const std::string & error_desc) 101 | : PluginlibException(error_desc) {} 102 | }; 103 | 104 | } // namespace pluginlib 105 | 106 | #endif // PLUGINLIB__EXCEPTIONS_HPP_ 107 | -------------------------------------------------------------------------------- /include/pluginlib/impl/split.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017, Open Source Robotics Foundation, Inc. All rights reserved. 2 | // 3 | // Redistribution and use in source and binary forms, with or without 4 | // modification, are permitted provided that the following conditions are met: 5 | // 6 | // * Redistributions of source code must retain the above copyright 7 | // notice, this list of conditions and the following disclaimer. 8 | // 9 | // * Redistributions in binary form must reproduce the above copyright 10 | // notice, this list of conditions and the following disclaimer in the 11 | // documentation and/or other materials provided with the distribution. 12 | // 13 | // * Neither the name of the Willow Garage nor the names of its 14 | // contributors may be used to endorse or promote products derived from 15 | // this software without specific prior written permission. 16 | // 17 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 21 | // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | // POSSIBILITY OF SUCH DAMAGE. 28 | 29 | #ifndef PLUGINLIB__IMPL__SPLIT_HPP_ 30 | #define PLUGINLIB__IMPL__SPLIT_HPP_ 31 | 32 | #include 33 | #include 34 | #include 35 | 36 | namespace pluginlib 37 | { 38 | namespace impl 39 | { 40 | 41 | inline std::vector 42 | split(const std::string & input, const std::string & regex) 43 | { 44 | std::regex re(regex); 45 | // the -1 will cause this to return the stuff between the matches, see the submatch argument: 46 | // http://en.cppreference.com/w/cpp/regex/regex_token_iterator/regex_token_iterator 47 | std::sregex_token_iterator first(input.begin(), input.end(), re, -1); 48 | std::sregex_token_iterator last; 49 | return {first, last}; // vector will copy from first to last 50 | } 51 | 52 | } // namespace impl 53 | } // namespace pluginlib 54 | 55 | #endif // PLUGINLIB__IMPL__SPLIT_HPP_ 56 | -------------------------------------------------------------------------------- /package.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | pluginlib 5 | 5.7.0 6 | 7 | The pluginlib package provides tools for writing and dynamically loading plugins using the ROS build infrastructure. 8 | To work, these tools require plugin providers to register their plugins in the package.xml of their package. 9 | 10 | 11 | Chris Lalancette 12 | 13 | BSD 14 | https://github.com/ros/pluginlib/issues 15 | https://github.com/ros/pluginlib 16 | 17 | Dirk Thomas 18 | Eitan Marder-Eppstein 19 | Michael Carroll 20 | Mirza Shah 21 | Steven! Ragnarök 22 | Tully Foote 23 | 24 | ament_cmake 25 | 26 | ament_index_cpp 27 | class_loader 28 | rcutils 29 | rcpputils 30 | tinyxml2_vendor 31 | 32 | ament_cmake_gtest 33 | ament_lint_auto 34 | ament_lint_common 35 | 36 | 37 | ament_cmake 38 | 39 | 40 | -------------------------------------------------------------------------------- /pluginlib-extras.cmake: -------------------------------------------------------------------------------- 1 | # Copyright 2017 Open Source Robotics Foundation, Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | # copied from pluginlib/pluginlib-extras.cmake 16 | 17 | find_package(ament_cmake_core QUIET REQUIRED) 18 | ament_register_extension("ament_package" "pluginlib" 19 | "pluginlib_package_hook.cmake") 20 | 21 | include("${pluginlib_DIR}/pluginlib_export_plugin_description_file.cmake") 22 | include("${pluginlib_DIR}/pluginlib_enable_plugin_testing.cmake") 23 | -------------------------------------------------------------------------------- /test/include/test_base.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012, Willow Garage, Inc. All rights reserved. 2 | // 3 | // Redistribution and use in source and binary forms, with or without 4 | // modification, are permitted provided that the following conditions are met: 5 | // 6 | // * Redistributions of source code must retain the above copyright 7 | // notice, this list of conditions and the following disclaimer. 8 | // 9 | // * Redistributions in binary form must reproduce the above copyright 10 | // notice, this list of conditions and the following disclaimer in the 11 | // documentation and/or other materials provided with the distribution. 12 | // 13 | // * Neither the name of the Willow Garage nor the names of its 14 | // contributors may be used to endorse or promote products derived from 15 | // this software without specific prior written permission. 16 | // 17 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 21 | // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | // POSSIBILITY OF SUCH DAMAGE. 28 | 29 | #ifndef TEST_BASE_HPP_ 30 | #define TEST_BASE_HPP_ 31 | 32 | #include 33 | 34 | namespace test_base 35 | { 36 | class TEST_PLUGINLIB_FIXTURE_PUBLIC Fubar 37 | { 38 | public: 39 | virtual void initialize(double foo) = 0; 40 | virtual double result() = 0; 41 | virtual ~Fubar() {} 42 | 43 | protected: 44 | Fubar() {} 45 | }; 46 | } // namespace test_base 47 | #endif // TEST_BASE_HPP_ 48 | -------------------------------------------------------------------------------- /test/include/test_plugins.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012, Willow Garage, Inc. All rights reserved. 2 | // 3 | // Redistribution and use in source and binary forms, with or without 4 | // modification, are permitted provided that the following conditions are met: 5 | // 6 | // * Redistributions of source code must retain the above copyright 7 | // notice, this list of conditions and the following disclaimer. 8 | // 9 | // * Redistributions in binary form must reproduce the above copyright 10 | // notice, this list of conditions and the following disclaimer in the 11 | // documentation and/or other materials provided with the distribution. 12 | // 13 | // * Neither the name of the Willow Garage nor the names of its 14 | // contributors may be used to endorse or promote products derived from 15 | // this software without specific prior written permission. 16 | // 17 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 21 | // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | // POSSIBILITY OF SUCH DAMAGE. 28 | 29 | #ifndef TEST_PLUGINS_HPP_ 30 | #define TEST_PLUGINS_HPP_ 31 | 32 | #include 33 | 34 | #include 35 | #include 36 | 37 | namespace test_plugins 38 | { 39 | class TEST_PLUGINLIB_FIXTURE_PUBLIC Bar : public test_base::Fubar 40 | { 41 | public: 42 | Bar() {} 43 | 44 | void initialize(double foo) 45 | { 46 | foo_ = foo; 47 | } 48 | 49 | double result() 50 | { 51 | return 0.5 * foo_ * getBar(); 52 | } 53 | 54 | double getBar() 55 | { 56 | return sqrt((foo_ * foo_) - ((foo_ / 2) * (foo_ / 2))); 57 | } 58 | 59 | private: 60 | double foo_; 61 | }; 62 | 63 | class TEST_PLUGINLIB_FIXTURE_PUBLIC Foo : public test_base::Fubar 64 | { 65 | public: 66 | Foo() {} 67 | 68 | void initialize(double foo) 69 | { 70 | foo_ = foo; 71 | } 72 | 73 | double result() 74 | { 75 | return foo_ * foo_; 76 | } 77 | 78 | private: 79 | double foo_; 80 | }; 81 | } // namespace test_plugins 82 | 83 | #endif // TEST_PLUGINS_HPP_ 84 | -------------------------------------------------------------------------------- /test/include/visibility_control.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018, Open Source Robotics Foundation, Inc. All rights reserved. 2 | // 3 | // Redistribution and use in source and binary forms, with or without 4 | // modification, are permitted provided that the following conditions are met: 5 | // 6 | // * Redistributions of source code must retain the above copyright 7 | // notice, this list of conditions and the following disclaimer. 8 | // 9 | // * Redistributions in binary form must reproduce the above copyright 10 | // notice, this list of conditions and the following disclaimer in the 11 | // documentation and/or other materials provided with the distribution. 12 | // 13 | // * Neither the name of the Willow Garage nor the names of its 14 | // contributors may be used to endorse or promote products derived from 15 | // this software without specific prior written permission. 16 | // 17 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 21 | // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | // POSSIBILITY OF SUCH DAMAGE. 28 | 29 | #ifndef VISIBILITY_CONTROL_HPP_ 30 | #define VISIBILITY_CONTROL_HPP_ 31 | 32 | // This logic was borrowed (then namespaced) from the examples on the gcc wiki: 33 | // https://gcc.gnu.org/wiki/Visibility 34 | 35 | #if defined _WIN32 || defined __CYGWIN__ 36 | #ifdef __GNUC__ 37 | #define TEST_PLUGINLIB_FIXTURE_EXPORT __attribute__ ((dllexport)) 38 | #define TEST_PLUGINLIB_FIXTURE_IMPORT __attribute__ ((dllimport)) 39 | #else 40 | #define TEST_PLUGINLIB_FIXTURE_EXPORT __declspec(dllexport) 41 | #define TEST_PLUGINLIB_FIXTURE_IMPORT __declspec(dllimport) 42 | #endif 43 | #ifdef TEST_PLUGINLIB_FIXTURE_BUILDING_LIBRARY 44 | #define TEST_PLUGINLIB_FIXTURE_PUBLIC TEST_PLUGINLIB_FIXTURE_EXPORT 45 | #else 46 | #define TEST_PLUGINLIB_FIXTURE_PUBLIC TEST_PLUGINLIB_FIXTURE_IMPORT 47 | #endif 48 | #define TEST_PLUGINLIB_FIXTURE_PUBLIC_TYPE TEST_PLUGINLIB_FIXTURE_PUBLIC 49 | #define TEST_PLUGINLIB_FIXTURE_LOCAL 50 | #else 51 | #define TEST_PLUGINLIB_FIXTURE_EXPORT __attribute__ ((visibility("default"))) 52 | #define TEST_PLUGINLIB_FIXTURE_IMPORT 53 | #if __GNUC__ >= 4 54 | #define TEST_PLUGINLIB_FIXTURE_PUBLIC __attribute__ ((visibility("default"))) 55 | #define TEST_PLUGINLIB_FIXTURE_LOCAL __attribute__ ((visibility("hidden"))) 56 | #else 57 | #define TEST_PLUGINLIB_FIXTURE_PUBLIC 58 | #define TEST_PLUGINLIB_FIXTURE_LOCAL 59 | #endif 60 | #define TEST_PLUGINLIB_FIXTURE_PUBLIC_TYPE 61 | #endif 62 | 63 | #endif // VISIBILITY_CONTROL_HPP_ 64 | -------------------------------------------------------------------------------- /test/test_package.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | test_pluginlib 5 | 2.2.0 6 | 7 | This tests pluginlib. 8 | 9 | Chris Lalancette 10 | BSD 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /test/test_plugins.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012, Willow Garage, Inc. All rights reserved. 2 | // 3 | // Redistribution and use in source and binary forms, with or without 4 | // modification, are permitted provided that the following conditions are met: 5 | // 6 | // * Redistributions of source code must retain the above copyright 7 | // notice, this list of conditions and the following disclaimer. 8 | // 9 | // * Redistributions in binary form must reproduce the above copyright 10 | // notice, this list of conditions and the following disclaimer in the 11 | // documentation and/or other materials provided with the distribution. 12 | // 13 | // * Neither the name of the Willow Garage nor the names of its 14 | // contributors may be used to endorse or promote products derived from 15 | // this software without specific prior written permission. 16 | // 17 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 21 | // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | // POSSIBILITY OF SUCH DAMAGE. 28 | 29 | #include 30 | #include 31 | #include "test_plugins.hpp" 32 | 33 | PLUGINLIB_EXPORT_CLASS(test_plugins::Foo, test_base::Fubar) 34 | PLUGINLIB_EXPORT_CLASS(test_plugins::Bar, test_base::Fubar) 35 | -------------------------------------------------------------------------------- /test/test_plugins.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | This is a foo plugin. 4 | 5 | 6 | This is a bar plugin. 7 | 8 | 9 | This is a broken none plugin. 10 | 11 | 12 | -------------------------------------------------------------------------------- /test/unique_ptr_test.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012, Willow Garage, Inc. All rights reserved. 2 | // 3 | // Redistribution and use in source and binary forms, with or without 4 | // modification, are permitted provided that the following conditions are met: 5 | // 6 | // * Redistributions of source code must retain the above copyright 7 | // notice, this list of conditions and the following disclaimer. 8 | // 9 | // * Redistributions in binary form must reproduce the above copyright 10 | // notice, this list of conditions and the following disclaimer in the 11 | // documentation and/or other materials provided with the distribution. 12 | // 13 | // * Neither the name of the Willow Garage nor the names of its 14 | // contributors may be used to endorse or promote products derived from 15 | // this software without specific prior written permission. 16 | // 17 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 21 | // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | // POSSIBILITY OF SUCH DAMAGE. 28 | 29 | #include 30 | 31 | #include 32 | 33 | #include 34 | 35 | TEST(PluginlibUniquePtrTest, unknownPlugin) { 36 | pluginlib::ClassLoader test_loader("test_pluginlib", "test_base::Fubar"); 37 | ASSERT_THROW(test_loader.createUniqueInstance("test_pluginlib/foobar"), 38 | pluginlib::LibraryLoadException); 39 | } 40 | 41 | 42 | TEST(PluginlibUniquePtrTest, misspelledPlugin) { 43 | pluginlib::ClassLoader bad_test_loader("test_pluginlib", "test_base::Fuba"); 44 | ASSERT_THROW(bad_test_loader.createUniqueInstance( 45 | "test_pluginlib/foo"), pluginlib::LibraryLoadException); 46 | } 47 | 48 | TEST(PluginlibTest, brokenPlugin) { 49 | pluginlib::ClassLoader test_loader("test_pluginlib", "test_base::Fubar"); 50 | ASSERT_THROW( 51 | test_loader.createUniqueInstance("test_pluginlib/none"), pluginlib::PluginlibException); 52 | } 53 | 54 | TEST(PluginlibUniquePtrTest, workingPlugin) { 55 | pluginlib::ClassLoader test_loader("test_pluginlib", "test_base::Fubar"); 56 | 57 | try { 58 | pluginlib::UniquePtr foo = 59 | test_loader.createUniqueInstance("test_pluginlib/foo"); 60 | foo->initialize(10.0); 61 | EXPECT_EQ(100.0, foo->result()); 62 | } catch (pluginlib::PluginlibException & ex) { 63 | FAIL() << "Throwing exception: " << ex.what(); 64 | return; 65 | } catch (...) { 66 | FAIL() << "Uncaught exception"; 67 | } 68 | } 69 | 70 | TEST(PluginlibUniquePtrTest, createUniqueInstanceAndUnloadLibrary) { 71 | RCUTILS_LOG_INFO("Making the ClassLoader..."); 72 | pluginlib::ClassLoader pl("test_pluginlib", "test_base::Fubar"); 73 | 74 | RCUTILS_LOG_INFO("Instantiating plugin..."); 75 | { 76 | pluginlib::UniquePtr inst = pl.createUniqueInstance("test_pluginlib/foo"); 77 | } 78 | 79 | RCUTILS_LOG_INFO("Checking if plugin is loaded with isClassLoaded..."); 80 | if (pl.isClassLoaded("test_pluginlib/foo")) { 81 | RCUTILS_LOG_INFO("Class is loaded"); 82 | } else { 83 | FAIL() << "Library containing class should be loaded but isn't."; 84 | } 85 | 86 | RCUTILS_LOG_INFO("Trying to unload class with unloadLibraryForClass..."); 87 | try { 88 | pl.unloadLibraryForClass("test_pluginlib/foo"); 89 | } catch (pluginlib::PluginlibException & e) { 90 | FAIL() << "Could not unload library when I should be able to. " << e.what(); 91 | } 92 | RCUTILS_LOG_INFO("Done."); 93 | } 94 | 95 | // Run all the tests that were declared with TEST() 96 | int main(int argc, char ** argv) 97 | { 98 | testing::InitGoogleTest(&argc, argv); 99 | return RUN_ALL_TESTS(); 100 | } 101 | -------------------------------------------------------------------------------- /test/utest.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012, Willow Garage, Inc. All rights reserved. 2 | // 3 | // Redistribution and use in source and binary forms, with or without 4 | // modification, are permitted provided that the following conditions are met: 5 | // 6 | // * Redistributions of source code must retain the above copyright 7 | // notice, this list of conditions and the following disclaimer. 8 | // 9 | // * Redistributions in binary form must reproduce the above copyright 10 | // notice, this list of conditions and the following disclaimer in the 11 | // documentation and/or other materials provided with the distribution. 12 | // 13 | // * Neither the name of the Willow Garage nor the names of its 14 | // contributors may be used to endorse or promote products derived from 15 | // this software without specific prior written permission. 16 | // 17 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 21 | // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | // POSSIBILITY OF SUCH DAMAGE. 28 | 29 | #include 30 | 31 | #include 32 | #include // NOLINT 33 | 34 | #include "rcutils/logging_macros.h" // NOLINT 35 | 36 | #include 37 | 38 | TEST(PluginlibTest, unknownPlugin) { 39 | pluginlib::ClassLoader test_loader("test_pluginlib", "test_base::Fubar"); 40 | ASSERT_THROW( 41 | test_loader.createSharedInstance("test_pluginlib/foobar"), pluginlib::LibraryLoadException); 42 | } 43 | 44 | TEST(PluginlibTest, misspelledPlugin) { 45 | pluginlib::ClassLoader bad_test_loader("test_pluginlib", "test_base::Fuba"); 46 | ASSERT_THROW( 47 | bad_test_loader.createSharedInstance("test_pluginlib/foo"), pluginlib::LibraryLoadException); 48 | } 49 | 50 | TEST(PluginlibTest, invalidPackage) { 51 | ASSERT_THROW(pluginlib::ClassLoader("test_pluginlib_bad", 52 | "test_base::Fubar"), 53 | pluginlib::ClassLoaderException); 54 | } 55 | 56 | TEST(PluginlibTest, brokenPlugin) { 57 | pluginlib::ClassLoader test_loader("test_pluginlib", "test_base::Fubar"); 58 | ASSERT_THROW( 59 | test_loader.createSharedInstance("test_pluginlib/none"), pluginlib::PluginlibException); 60 | } 61 | 62 | TEST(PluginlibTest, workingPlugin) { 63 | pluginlib::ClassLoader test_loader("test_pluginlib", "test_base::Fubar"); 64 | 65 | try { 66 | std::shared_ptr foo = test_loader.createSharedInstance("test_pluginlib/foo"); 67 | foo->initialize(10.0); 68 | EXPECT_EQ(100.0, foo->result()); 69 | } catch (pluginlib::PluginlibException & ex) { 70 | FAIL() << "Throwing exception: " << ex.what(); 71 | return; 72 | } catch (...) { 73 | FAIL() << "Uncaught exception"; 74 | } 75 | } 76 | 77 | TEST(PluginlibTest, createUnmanagedInstanceAndUnloadLibrary) { 78 | RCUTILS_LOG_INFO("Making the ClassLoader..."); 79 | pluginlib::ClassLoader pl("test_pluginlib", "test_base::Fubar"); 80 | 81 | RCUTILS_LOG_INFO("Instantiating plugin..."); 82 | test_base::Fubar * inst = pl.createUnmanagedInstance("test_pluginlib/foo"); 83 | 84 | RCUTILS_LOG_INFO("Deleting plugin..."); 85 | delete inst; 86 | 87 | RCUTILS_LOG_INFO("Checking if plugin is loaded with isClassLoaded..."); 88 | if (pl.isClassLoaded("test_pluginlib/foo")) { 89 | RCUTILS_LOG_INFO("Class is loaded"); 90 | } else { 91 | FAIL() << "Library containing class should be loaded but isn't."; 92 | } 93 | RCUTILS_LOG_INFO("Trying to unload class with unloadLibraryForClass..."); 94 | try { 95 | pl.unloadLibraryForClass("test_pluginlib/foo"); 96 | } catch (pluginlib::PluginlibException & e) { 97 | FAIL() << "Could not unload library when I should be able to. " << e.what(); 98 | } 99 | RCUTILS_LOG_INFO("Done."); 100 | } 101 | 102 | TEST(PluginlibTest, createManagedInstanceAndUnloadLibrary) { 103 | RCUTILS_LOG_INFO("Making the ClassLoader..."); 104 | pluginlib::ClassLoader pl("test_pluginlib", "test_base::Fubar"); 105 | 106 | RCUTILS_LOG_INFO("Instantiating plugin..."); 107 | { 108 | std::shared_ptr inst = pl.createSharedInstance("test_pluginlib/foo"); 109 | } 110 | 111 | RCUTILS_LOG_INFO("Checking if plugin is loaded with isClassLoaded..."); 112 | if (pl.isClassLoaded("test_pluginlib/foo")) { 113 | RCUTILS_LOG_INFO("Class is loaded"); 114 | } else { 115 | FAIL() << "Library containing class should be loaded but isn't."; 116 | } 117 | 118 | RCUTILS_LOG_INFO("Trying to unload class with unloadLibraryForClass..."); 119 | try { 120 | pl.unloadLibraryForClass("test_pluginlib/foo"); 121 | } catch (pluginlib::PluginlibException & e) { 122 | FAIL() << "Could not unload library when I should be able to." << e.what(); 123 | } 124 | RCUTILS_LOG_INFO("Done."); 125 | } 126 | 127 | TEST(PluginlibTest, brokenXML) { 128 | try { 129 | pluginlib::ClassLoader test_loader("test_pluginlib", "test_base::Fubar", 130 | "plugin_test"); 131 | test_loader.createSharedInstance("test_pluginlib/foo"); 132 | } catch (pluginlib::PluginlibException & /*ex*/) { 133 | SUCCEED(); 134 | return; 135 | } 136 | 137 | ADD_FAILURE() << "Didn't throw exception as expected"; 138 | } 139 | 140 | // Run all the tests that were declared with TEST() 141 | int main(int argc, char ** argv) 142 | { 143 | testing::InitGoogleTest(&argc, argv); 144 | return RUN_ALL_TESTS(); 145 | } 146 | --------------------------------------------------------------------------------