1/* 2 This file is part of Corrade. 3 4 Copyright © 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 5 2017, 2018, 2019, 2020 Vladimír Vondruš <mosra@centrum.cz> 6 7 Permission is hereby granted, free of charge, to any person obtaining a 8 copy of this software and associated documentation files (the "Software"), 9 to deal in the Software without restriction, including without limitation 10 the rights to use, copy, modify, merge, publish, distribute, sublicense, 11 and/or sell copies of the Software, and to permit persons to whom the 12 Software is furnished to do so, subject to the following conditions: 13 14 The above copyright notice and this permission notice shall be included 15 in all copies or substantial portions of the Software. 16 17 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 20 THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 22 FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 DEALINGS IN THE SOFTWARE. 24*/ 25 26namespace Corrade { 27/** @page plugin-management Plugin management 28@brief Developing, loading and using plugins. 29 30@tableofcontents 31@m_footernavigation 32 33The @ref PluginManager::Manager class provides hierarchical plugin management 34functions. Main features: 35 36- Plugin manager version and plugin interface version checks to avoid 37 unexpected behavior 38- Both static and dynamic plugin support 39- Plugin dependecies and aliases 40- Usage checks, the manager doesn't allow plugin unload if there are any 41 active plugin instances 42 43This tutorial will give you a brief introduction into how plugins are defined, 44compiled and managed. 45 46@section plugin-management-interface Plugin interface 47 48Plugin interface is a class with virtual methods, defining the way how to work 49with a particular plugin. 50 51Every plugin interface has to be derived from @ref PluginManager::AbstractPlugin 52and reimplement the 53@ref PluginManager::AbstractPlugin::AbstractPlugin(PluginManager::AbstractManager&, const std::string&) 54constructor. This is needed for instance use checks, as described above. Plugin 55classes derived from that interface need to reimplement the constructor too, of 56course. 57 58Additionaly, an interface string should be provided by overriding the 59@ref PluginManager::AbstractPlugin::pluginInterface() function in order to have 60an additional safety check for interface compatibility. The macro takes a 61string which uniquely names that particular interface. A good practice is to 62use "Java package name"-style syntax because this makes the name as unique as 63possible. The interface name should also contain a version identifier to make 64sure the plugin will not be loaded with incompatible interface version. If this 65function is not overriden, the default implementation returns @cpp "" @ce and 66effectively makes the interface compatibility check a no-op. 67 68To make lives of the users easier, we can define a list of paths where the 69plugins will be searched for using 70@ref PluginManager::AbstractPlugin::pluginSearchPaths() "pluginSearchPaths()". 71The paths can be either absolute (for example hardcoding a system-wide 72installation path) or relative (relative to the executable file). In this case 73the plugins will be right next to the executable, so just a single entry 74with @cpp "" @ce will do. If we wouldn't specify the search paths, the user 75would need to pass a plugin search path to the plugin manager constructor. 76 77@dontinclude pluginmanager/AbstractAnimal.h 78@skip class AbstractAnimal 79@until }; 80@until }; 81 82@section plugin-management-plugin Plugin definition 83 84Every plugin is represented by a class derived from a particular plugin 85interface. The plugin class is then registered as a static or a dynamic plugin. 86Every plugin also needs to have an associated metadata file, which contains 87information about plugin dependencies and optionally also plugin-specific data. Full 88specification of plugin metadata file syntax can be found in the 89@ref PluginManager::PluginMetadata class documentation. 90 91First we define one static plugin, which will be included in the application 92out-of-the-box: 93 94@dontinclude pluginmanager/Canary.cpp 95@skip class Canary 96@until }; 97 98After defining the plugin we have to register it with the 99@ref CORRADE_PLUGIN_REGISTER() macro. The first argument is plugin name (which 100will be used when instancing the plugin), second argument is name of the plugin 101class and third is the name of used plugin interface. 102 103@code{.cpp} 104CORRADE_PLUGIN_REGISTER(Canary, Canary, 105 "cz.mosra.corrade.Examples.AbstractAnimal/1.0") 106@endcode 107 108And a corresponding configuration file, `Canary.conf`: 109 110@include pluginmanager/Canary.conf 111 112Then we define one dynamic plugin. Note that the macro for registering dynamic 113plugin is the same, the only difference will be in `CMakeLists.txt`, as you 114will see below. This way you can decide at compile time which plugins will be 115dynamic, which will be static, or, for example, which will be compiled directly 116into the library/application, so they can be used directly without the plugin 117manager. 118 119@dontinclude pluginmanager/Dog.cpp 120@skip class Dog 121@until }; 122 123@code{.cpp} 124CORRADE_PLUGIN_REGISTER(Dog, Dog, 125 "cz.mosra.corrade.Examples.AbstractAnimal/1.0") 126@endcode 127 128And a corresponding configuration file, `Dog.conf`: 129 130@include pluginmanager/Dog.conf 131 132@section plugin-management-compilation Plugin compilation 133 134Requiring the Corrade package using @cmake find_package() @ce will define two 135useful macros for plugin compilation: 136 137@dontinclude pluginmanager/CMakeLists.txt 138@skip find_package 139@until corrade_add_static_plugin 140 141The @ref corrade-cmake-add-plugin "corrade_add_plugin()" macro takes plugin 142name as first argument, second argument is a directory where to install the 143plugin files, third argument is name of configuration file and after that comes 144one or more source files. We use the build directory for storing the plugins to 145avoid the need for installation. 146 147@note Note that on Windows a plugin DLL can't have unresolved references and 148 thus needs to be explicitly linked to all its dependencies, even if they 149 would be later at runtime correctly supplied by the plugin manager. 150 151The @ref corrade-cmake-add-static-plugin "corrade_add_static_plugin()" macro is 152similar to the above, except that it creates a static plugin instead of a 153dynamic one. 154 155@section plugin-management-management Plugin management 156 157Now it's time to initialize @ref PluginManager::Manager and make use of the 158plugins. @ref PluginManager::Manager is a templated class and that means it 159will load and make available only plugins with the interface specified as 160template. 161 162In order to make the plugin manager find the static plugins, we have to import 163them with the @ref CORRADE_PLUGIN_IMPORT() macro (for example at the beginning 164of the @cpp main() @ce function). It takes a plugin name as an argument. 165 166@note To make users of your plugins happier, you can for example provide a 167 "static plugin import file" that calls the above macro together with a 168 @ref CORRADE_AUTOMATIC_INITIALIZER(), so all the users have to do is to 169 @cpp #include @ce it. For even more convenience, you can tell CMake to 170 attach this file automatically every time given static plugin is linked 171 to a target using the @cmake target_sources() @ce command. 172 173This example application will load plugin specified as command-line argument 174and then displays brief info about a given animal. For convenient argument 175parsing and usage documentation we used @ref Utility::Arguments. 176 177@dontinclude pluginmanager/main.cpp 178@skip int main 179@until } 180@until } 181@until } 182@until } 183@until } 184@until } 185@until } 186@until } 187 188Compile the application with a simple CMake @cmake add_executable() @ce 189command and don't forget to link in all the static plugins compiled above: 190 191@dontinclude pluginmanager/CMakeLists.txt 192@skip add_executable 193@until target_link_libraries 194 195After a successful compilation we can run the application with plugin name 196as an argument: 197 198@code{.shell-session} 199$ ./PluginTest --help 200Usage: 201 ./PluginTest [-h|--help] [--] plugin 202 203Displays info about given animal. 204 205Arguments: 206 plugin animal plugin name 207 -h, --help display this help message and exit 208 209$ ./PluginTest Canary 210Using plugin 'I'm allergic to canaries!' 211 212Name: Achoo 213Leg count: 2 214Has tail: yes 215 216$ ./PluginTest Dog 217Using plugin 'A simple dog plugin' 218 219Name: Doug 220Leg count: 4 221Has tail: yes 222@endcode 223 224The full file content is linked below. Full source code is also available in 225the [GitHub repository](https://github.com/mosra/corrade/tree/master/src/examples/pluginmanager). 226 227- @ref pluginmanager/AbstractAnimal.h "AbstractAnimal.h" 228- @ref pluginmanager/Canary.cpp "Canary.cpp" 229- @ref pluginmanager/Canary.conf "Canary.conf" 230- @ref pluginmanager/Dog.cpp "Dog.cpp" 231- @ref pluginmanager/Dog.conf "Dog.conf" 232- @ref pluginmanager/main.cpp "main.cpp" 233- @ref pluginmanager/CMakeLists.txt "CMakeLists.txt" 234 235@example pluginmanager/AbstractAnimal.h @m_examplenavigation{plugin-management,pluginmanager/} @m_footernavigation 236@example pluginmanager/Canary.cpp @m_examplenavigation{plugin-management,pluginmanager/} @m_footernavigation 237@example pluginmanager/Canary.conf @m_examplenavigation{plugin-management,pluginmanager/} @m_footernavigation 238@example pluginmanager/Dog.cpp @m_examplenavigation{plugin-management,pluginmanager/} @m_footernavigation 239@example pluginmanager/Dog.conf @m_examplenavigation{plugin-management,pluginmanager/} @m_footernavigation 240@example pluginmanager/main.cpp @m_examplenavigation{plugin-management,pluginmanager/} @m_footernavigation 241@example pluginmanager/CMakeLists.txt @m_examplenavigation{plugin-management,pluginmanager/} @m_footernavigation 242 243 */ 244} 245