├── .gitignore ├── LICENSE ├── README.md ├── code ├── CMakeLists.txt ├── cmake │ ├── CodeCoverage.cmake │ └── CppPluginFrameworkConfig.cmake ├── inc │ └── CppPluginFramework │ │ ├── AbstractPlugin.hpp │ │ ├── IPlugin.hpp │ │ ├── IPluginFactory.hpp │ │ ├── LoggingCategories.hpp │ │ ├── Plugin.hpp │ │ ├── PluginConfig.hpp │ │ ├── PluginFactoryTemplate.hpp │ │ ├── PluginInstanceConfig.hpp │ │ ├── PluginManager.hpp │ │ ├── PluginManagerConfig.hpp │ │ ├── Validation.hpp │ │ └── VersionInfo.hpp ├── src │ ├── AbstractPlugin.cpp │ ├── LoggingCategories.cpp │ ├── Plugin.cpp │ ├── PluginConfig.cpp │ ├── PluginInstanceConfig.cpp │ ├── PluginManager.cpp │ ├── PluginManagerConfig.cpp │ ├── Validation.cpp │ └── VersionInfo.cpp └── tests │ ├── CMakeLists.txt │ ├── integration │ ├── CMakeLists.txt │ ├── Plugin │ │ ├── CMakeLists.txt │ │ └── testPlugin.cpp │ ├── PluginManager │ │ ├── CMakeLists.txt │ │ ├── TestData.qrc │ │ ├── TestData │ │ │ ├── AppConfig.json │ │ │ ├── AppConfigWithInvalidStartupOrder.json │ │ │ └── InvalidConfigWithUnsupportedDependency.json │ │ └── testPluginManager.cpp │ └── TestPlugins │ │ ├── CMakeLists.txt │ │ ├── ITestPlugin1.hpp │ │ ├── ITestPlugin2.hpp │ │ ├── TestPlugin1.cpp │ │ ├── TestPlugin1.hpp │ │ ├── TestPlugin2.cpp │ │ └── TestPlugin2.hpp │ └── unit │ ├── CMakeLists.txt │ ├── PluginConfig │ ├── CMakeLists.txt │ └── testPluginConfig.cpp │ ├── PluginInstanceConfig │ ├── CMakeLists.txt │ └── testPluginInstanceConfig.cpp │ ├── PluginManagerConfig │ ├── CMakeLists.txt │ ├── TestData.qrc │ ├── TestData │ │ ├── dummyPlugin1 │ │ └── dummyPlugin2 │ └── testPluginManagerConfig.cpp │ ├── Validation │ ├── CMakeLists.txt │ ├── TestData.qrc │ ├── TestData │ │ ├── file │ │ ├── file.ext │ │ ├── file.txt │ │ └── somePath │ │ │ ├── file │ │ │ ├── file.ext │ │ │ └── file.txt │ └── testValidation.cpp │ └── VersionInfo │ ├── CMakeLists.txt │ └── testVersionInfo.cpp └── docs ├── Design.md ├── Diagrams ├── ClassDiagrams.plantuml ├── ClassDiagrams │ ├── Plugin.svg │ ├── PluginFactory.svg │ ├── PluginManager.svg │ └── PluginManagerConfig.svg ├── FlowCharts.plantuml └── FlowCharts │ ├── ShutdownWorkflow.svg │ └── StartupWorkflow.svg └── Requirements.md /.gitignore: -------------------------------------------------------------------------------- 1 | # C++ objects and libs 2 | 3 | *.slo 4 | *.lo 5 | *.o 6 | *.a 7 | *.la 8 | *.lai 9 | *.so 10 | *.dll 11 | *.dylib 12 | 13 | # Qt-es 14 | 15 | /.qmake.cache 16 | /.qmake.stash 17 | *.pro.user 18 | *.pro.user.* 19 | *.qbs.user 20 | *.qbs.user.* 21 | *.moc 22 | moc_*.cpp 23 | moc_*.h 24 | qrc_*.cpp 25 | ui_*.h 26 | Makefile* 27 | *build-* 28 | 29 | # QtCreator 30 | 31 | *.autosave 32 | 33 | # QtCtreator Qml 34 | *.qmlproject.user 35 | *.qmlproject.user.* 36 | 37 | # QtCtreator CMake 38 | CMakeLists.txt.user* 39 | 40 | # Visual Studio Code 41 | .vscode 42 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | 9 | This version of the GNU Lesser General Public License incorporates 10 | the terms and conditions of version 3 of the GNU General Public 11 | License, supplemented by the additional permissions listed below. 12 | 13 | 0. Additional Definitions. 14 | 15 | As used herein, "this License" refers to version 3 of the GNU Lesser 16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 17 | General Public License. 18 | 19 | "The Library" refers to a covered work governed by this License, 20 | other than an Application or a Combined Work as defined below. 21 | 22 | An "Application" is any work that makes use of an interface provided 23 | by the Library, but which is not otherwise based on the Library. 24 | Defining a subclass of a class defined by the Library is deemed a mode 25 | of using an interface provided by the Library. 26 | 27 | A "Combined Work" is a work produced by combining or linking an 28 | Application with the Library. The particular version of the Library 29 | with which the Combined Work was made is also called the "Linked 30 | Version". 31 | 32 | The "Minimal Corresponding Source" for a Combined Work means the 33 | Corresponding Source for the Combined Work, excluding any source code 34 | for portions of the Combined Work that, considered in isolation, are 35 | based on the Application, and not on the Linked Version. 36 | 37 | The "Corresponding Application Code" for a Combined Work means the 38 | object code and/or source code for the Application, including any data 39 | and utility programs needed for reproducing the Combined Work from the 40 | Application, but excluding the System Libraries of the Combined Work. 41 | 42 | 1. Exception to Section 3 of the GNU GPL. 43 | 44 | You may convey a covered work under sections 3 and 4 of this License 45 | without being bound by section 3 of the GNU GPL. 46 | 47 | 2. Conveying Modified Versions. 48 | 49 | If you modify a copy of the Library, and, in your modifications, a 50 | facility refers to a function or data to be supplied by an Application 51 | that uses the facility (other than as an argument passed when the 52 | facility is invoked), then you may convey a copy of the modified 53 | version: 54 | 55 | a) under this License, provided that you make a good faith effort to 56 | ensure that, in the event an Application does not supply the 57 | function or data, the facility still operates, and performs 58 | whatever part of its purpose remains meaningful, or 59 | 60 | b) under the GNU GPL, with none of the additional permissions of 61 | this License applicable to that copy. 62 | 63 | 3. Object Code Incorporating Material from Library Header Files. 64 | 65 | The object code form of an Application may incorporate material from 66 | a header file that is part of the Library. You may convey such object 67 | code under terms of your choice, provided that, if the incorporated 68 | material is not limited to numerical parameters, data structure 69 | layouts and accessors, or small macros, inline functions and templates 70 | (ten or fewer lines in length), you do both of the following: 71 | 72 | a) Give prominent notice with each copy of the object code that the 73 | Library is used in it and that the Library and its use are 74 | covered by this License. 75 | 76 | b) Accompany the object code with a copy of the GNU GPL and this license 77 | document. 78 | 79 | 4. Combined Works. 80 | 81 | You may convey a Combined Work under terms of your choice that, 82 | taken together, effectively do not restrict modification of the 83 | portions of the Library contained in the Combined Work and reverse 84 | engineering for debugging such modifications, if you also do each of 85 | the following: 86 | 87 | a) Give prominent notice with each copy of the Combined Work that 88 | the Library is used in it and that the Library and its use are 89 | covered by this License. 90 | 91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 92 | document. 93 | 94 | c) For a Combined Work that displays copyright notices during 95 | execution, include the copyright notice for the Library among 96 | these notices, as well as a reference directing the user to the 97 | copies of the GNU GPL and this license document. 98 | 99 | d) Do one of the following: 100 | 101 | 0) Convey the Minimal Corresponding Source under the terms of this 102 | License, and the Corresponding Application Code in a form 103 | suitable for, and under terms that permit, the user to 104 | recombine or relink the Application with a modified version of 105 | the Linked Version to produce a modified Combined Work, in the 106 | manner specified by section 6 of the GNU GPL for conveying 107 | Corresponding Source. 108 | 109 | 1) Use a suitable shared library mechanism for linking with the 110 | Library. A suitable mechanism is one that (a) uses at run time 111 | a copy of the Library already present on the user's computer 112 | system, and (b) will operate properly with a modified version 113 | of the Library that is interface-compatible with the Linked 114 | Version. 115 | 116 | e) Provide Installation Information, but only if you would otherwise 117 | be required to provide such information under section 6 of the 118 | GNU GPL, and only to the extent that such information is 119 | necessary to install and execute a modified version of the 120 | Combined Work produced by recombining or relinking the 121 | Application with a modified version of the Linked Version. (If 122 | you use option 4d0, the Installation Information must accompany 123 | the Minimal Corresponding Source and Corresponding Application 124 | Code. If you use option 4d1, you must provide the Installation 125 | Information in the manner specified by section 6 of the GNU GPL 126 | for conveying Corresponding Source.) 127 | 128 | 5. Combined Libraries. 129 | 130 | You may place library facilities that are a work based on the 131 | Library side by side in a single library together with other library 132 | facilities that are not Applications and are not covered by this 133 | License, and convey such a combined library under terms of your 134 | choice, if you do both of the following: 135 | 136 | a) Accompany the combined library with a copy of the same work based 137 | on the Library, uncombined with any other library facilities, 138 | conveyed under the terms of this License. 139 | 140 | b) Give prominent notice with the combined library that part of it 141 | is a work based on the Library, and explaining where to find the 142 | accompanying uncombined form of the same work. 143 | 144 | 6. Revised Versions of the GNU Lesser General Public License. 145 | 146 | The Free Software Foundation may publish revised and/or new versions 147 | of the GNU Lesser General Public License from time to time. Such new 148 | versions will be similar in spirit to the present version, but may 149 | differ in detail to address new problems or concerns. 150 | 151 | Each version is given a distinguishing version number. If the 152 | Library as you received it specifies that a certain numbered version 153 | of the GNU Lesser General Public License "or any later version" 154 | applies to it, you have the option of following the terms and 155 | conditions either of that published version or of any later version 156 | published by the Free Software Foundation. If the Library as you 157 | received it does not specify a version number of the GNU Lesser 158 | General Public License, you may choose any version of the GNU Lesser 159 | General Public License ever published by the Free Software Foundation. 160 | 161 | If the Library as you received it specifies that a proxy can decide 162 | whether future versions of the GNU Lesser General Public License shall 163 | apply, that proxy's public statement of acceptance of any version is 164 | permanent authorization for you to choose that version for the 165 | Library. 166 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # C++ Plugin Framework 2 | 3 | The purpose of this plugin framework is to simplify creation and loading of C++ plugins. 4 | 5 | The framework can be used to create cross-platform plugins, since it is using only Qt and STL libraries. 6 | 7 | It is recommended to build the C++ plugin framework, the plugins and the application with the same development environment to avoid the problem of ABI incompatibility between compilers and used libraries. 8 | 9 | 10 | ## Requirements 11 | 12 | Requirements document can be found [here](docs/Requirements.md). 13 | 14 | 15 | ## Design 16 | 17 | Design document can be found [here](docs/Design.md). 18 | 19 | 20 | ## Build and deployment 21 | 22 | For building the following requirements are needed: 23 | 24 | * CMake v3.5 or later 25 | * C++14 compliant compiler 26 | * Qt v5.9 or later (only Core and Test modules) 27 | * [Cedar Framework](https://github.com/djurodrljaca/CedarFramework) 28 | * [C++ Config Framework](https://github.com/djurodrljaca/CppConfigFramework) 29 | 30 | If the library needs to be deployed to a custom location you need to set the `CMAKE_INSTALL_PREFIX` variable or if you need to use a custom Qt installation, *CedarFramework*, or *CppConfigFramework* is not deployed to one of the standard locations you need to set the `CMAKE_PREFIX_PATH` variable. 31 | 32 | Example: 33 | 34 | ``` 35 | $ cmake -DCMAKE_INSTALL_PREFIX=path/to/install/dir -DCMAKE_PREFIX_PATH="path/to/qt/dir;path/to/CedarFramework/dir;path/to/CppConfigFramework/dir" path/to/source/dir 36 | ``` 37 | 38 | Then build and deploy the library: 39 | 40 | ``` 41 | $ cmake --build . --target install 42 | ``` 43 | 44 | 45 | ## Usage 46 | 47 | ### CMake Integration 48 | 49 | To use this module in a CMake project you need to have a built and deployed *CedarFramework* and *CppConfigFramework* dependencies. Then build and deploy *CppPluginFramework* and add this to your project's CMakeLists.txt: 50 | 51 | ```CMake 52 | find_package(CppPluginFramework REQUIRED) 53 | ``` 54 | 55 | And link it to your target: 56 | 57 | ```CMake 58 | target_link_libraries(target_name PUBLIC CppPluginFramework::CppPluginFramework) 59 | ``` 60 | 61 | 62 | ### Creating an interface for the plugin 63 | 64 | When creating a plugin it is recommended to split the actual plugin and the interfaces that will be implemented by it to separate targets so that other plugins can depend just on the interfaces (header files) instead of to the plugins. 65 | 66 | ```CMake 67 | # File: CMakeLists.txt 68 | 69 | add_library(ExampleInterface INTERFACE) 70 | 71 | target_link_libraries(ExampleInterface INTERFACE ${LIBS}) 72 | 73 | target_include_directories(ExampleInterface INTERFACE 74 | $ 75 | $ 76 | ) 77 | 78 | # Create dummy target so that IDEs show the header files 79 | add_custom_target(ExampleInterface_IDE SOURCES 80 | inc/IExample.hpp 81 | ) 82 | ``` 83 | 84 | ```C++ 85 | // File: IExample.hpp 86 | 87 | class IExample 88 | { 89 | public: 90 | virtual ~IExample() = default; 91 | virtual bool exampleMethod() const = 0; 92 | }; 93 | ``` 94 | 95 | 96 | ### Creating a plugin 97 | 98 | When the interface targets are available then the plugin can just add them as the target dependencies. 99 | 100 | ```CMake 101 | # File: CMakeLists.txt 102 | 103 | add_library(ExamplePlugin SHARED ${PARAM_SOURCES}) 104 | 105 | target_link_libraries(ExamplePlugin PUBLIC 106 | CppPluginFramework::CppPluginFramework 107 | ExampleInterface 108 | ) 109 | 110 | # Needed only if custom prefix and/or suffix are needed for the plugin library 111 | set_target_properties(ExamplePlugin PROPERTIES 112 | PREFIX "" 113 | SUFFIX ".plugin" 114 | ) 115 | ``` 116 | 117 | ```C++ 118 | // File: ExamplePlugin.hpp 119 | 120 | class ExamplePlugin : public CppPluginFramework::AbstractPlugin, public IExample 121 | { 122 | public: 123 | ExamplePlugin(const QString &name); 124 | ~ExamplePlugin() override = default; 125 | 126 | bool loadConfig(const CppConfigFramework::ConfigObjectNode &config) override; 127 | bool injectDependency(CppPluginFramework::IPlugin *plugin) override; 128 | void ejectDependencies() override; 129 | 130 | bool exampleMethod() const override; 131 | }; 132 | 133 | class Q_DECL_EXPORT ExamplePluginFactory : 134 | public QObject, 135 | public CppPluginFramework::PluginFactoryTemplate 136 | { 137 | Q_OBJECT 138 | Q_PLUGIN_METADATA(IID "CppPluginFramework::IPluginFactory") 139 | Q_INTERFACES(CppPluginFramework::IPluginFactory) 140 | 141 | public: 142 | ~ExamplePluginFactory() override = default; 143 | }; 144 | ``` 145 | 146 | ```C++ 147 | // File: ExamplePlugin.cpp 148 | 149 | ExamplePlugin::ExamplePlugin(const QString &name) 150 | : CppPluginFramework::AbstractPlugin(name, 151 | CppPluginFramework::VersionInfo(1, 0, 0), 152 | "Example plugin", 153 | { "IExample" }), 154 | IExample() 155 | { 156 | } 157 | 158 | bool ExamplePlugin::loadConfig(const CppConfigFramework::ConfigObjectNode &config) 159 | { 160 | return true; 161 | } 162 | 163 | bool ExamplePlugin::injectDependency(CppPluginFramework::IPlugin *plugin) 164 | { 165 | return false; 166 | } 167 | 168 | void ExamplePlugin::ejectDependencies() 169 | { 170 | } 171 | 172 | bool ExamplePlugin::exampleMethod() const 173 | { 174 | return true; 175 | } 176 | ``` 177 | 178 | 179 | ### Using a plugin in an application 180 | 181 | To be able to use a plugin you only need access to the *CppPluginFramework* (and its dependencies) and the plugin's interfaces. 182 | 183 | The load the plugins you need to prepare a configuration and load it with the *PluginManager* class. 184 | 185 | ```json 186 | { 187 | "config": 188 | { 189 | "plugins": 190 | { 191 | "example": 192 | { 193 | "$file_path": "${PluginPath}/ExamplePlugin.plugin", 194 | "version": "1.0.0", 195 | "instances": 196 | { 197 | "example": 198 | { 199 | "name": "example_instance" 200 | }, 201 | } 202 | } 203 | } 204 | } 205 | } 206 | ``` 207 | 208 | ```C++ 209 | ConfigReader configReader; 210 | EnvironmentVariables environmentVariables; 211 | 212 | auto config = configReader.read("path/to/config/file", 213 | QDir(QCoreApplication::applicationDirPath()), 214 | ConfigNodePath::ROOT_PATH, 215 | ConfigNodePath::ROOT_PATH, 216 | {}, 217 | &environmentVariables); 218 | 219 | PluginManagerConfig pluginManagerConfig; 220 | pluginManagerConfig.loadConfig(*config); 221 | 222 | PluginManager pluginManager; 223 | pluginManager.load(pluginManagerConfig); 224 | pluginManager.start(); 225 | 226 | auto example = pluginManager.pluginInstance("example_instance") 227 | bool result = example->exampleMethod(); 228 | 229 | pluginManager.stop(); 230 | pluginManager.unload(); 231 | ``` 232 | -------------------------------------------------------------------------------- /code/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This file is part of C++ Plugin Framework. 2 | # 3 | # C++ Plugin Framework is free software: you can redistribute it and/or modify it under the terms 4 | # of the GNU Lesser General Public License as published by the Free Software Foundation, either 5 | # version 3 of the License, or (at your option) any later version. 6 | # 7 | # C++ Plugin Framework is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 8 | # without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU Lesser General Public License for more details. 10 | # 11 | # You should have received a copy of the GNU Lesser General Public License along with C++ Plugin 12 | # Framework. If not, see . 13 | 14 | cmake_minimum_required(VERSION 3.5.0) 15 | 16 | project(CppPluginFramework VERSION 1.0.0) 17 | 18 | set(CMAKE_AUTOMOC ON) 19 | 20 | find_package(CppConfigFramework REQUIRED) 21 | 22 | # -------------------------------------------------------------------------------------------------- 23 | # Code Coverage 24 | # -------------------------------------------------------------------------------------------------- 25 | option(CppPluginFramework_CodeCoverage "C++ Plugin Framework Code Coverage" OFF) 26 | 27 | if (CppPluginFramework_CodeCoverage MATCHES ON) 28 | include(cmake/CodeCoverage.cmake) 29 | APPEND_COVERAGE_COMPILER_FLAGS() 30 | 31 | set(excludes_cmake_prefix_path) 32 | foreach(item ${CMAKE_PREFIX_PATH}) 33 | get_filename_component(item ${item} ABSOLUTE) 34 | list(APPEND excludes_cmake_prefix_path "'${item}/*'") 35 | endforeach() 36 | 37 | set(CppPluginFramework_CodeCoverage_EXCLUDES 38 | "'/opt/*'" 39 | "'/usr/*'" 40 | ${excludes_cmake_prefix_path} 41 | "'${CMAKE_SOURCE_DIR}/tests/*'" 42 | "'${CMAKE_BINARY_DIR}/*'" 43 | ) 44 | endif() 45 | 46 | # -------------------------------------------------------------------------------------------------- 47 | # CppPluginFramework library 48 | # -------------------------------------------------------------------------------------------------- 49 | add_library(CppPluginFramework SHARED 50 | inc/CppPluginFramework/AbstractPlugin.hpp 51 | inc/CppPluginFramework/IPlugin.hpp 52 | inc/CppPluginFramework/IPluginFactory.hpp 53 | inc/CppPluginFramework/LoggingCategories.hpp 54 | inc/CppPluginFramework/Plugin.hpp 55 | inc/CppPluginFramework/PluginConfig.hpp 56 | inc/CppPluginFramework/PluginFactoryTemplate.hpp 57 | inc/CppPluginFramework/PluginInstanceConfig.hpp 58 | inc/CppPluginFramework/PluginManager.hpp 59 | inc/CppPluginFramework/PluginManagerConfig.hpp 60 | inc/CppPluginFramework/Validation.hpp 61 | inc/CppPluginFramework/VersionInfo.hpp 62 | 63 | src/AbstractPlugin.cpp 64 | src/LoggingCategories.cpp 65 | src/Plugin.cpp 66 | src/PluginConfig.cpp 67 | src/PluginInstanceConfig.cpp 68 | src/PluginManager.cpp 69 | src/PluginManagerConfig.cpp 70 | src/Validation.cpp 71 | src/VersionInfo.cpp 72 | ) 73 | 74 | set_target_properties(CppPluginFramework PROPERTIES 75 | VERSION ${PROJECT_VERSION} 76 | SOVERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}") 77 | 78 | include(GenerateExportHeader) 79 | generate_export_header(CppPluginFramework 80 | EXPORT_FILE_NAME exports/CppPluginFramework/CppPluginFrameworkExport.hpp) 81 | 82 | target_include_directories(CppPluginFramework PUBLIC 83 | $ 84 | $ 85 | $ 86 | ) 87 | 88 | target_link_libraries(CppPluginFramework PUBLIC 89 | CppConfigFramework::CppConfigFramework 90 | ) 91 | 92 | set_target_properties(CppPluginFramework PROPERTIES 93 | CXX_STANDARD 14 94 | CXX_STANDARD_REQUIRED YES 95 | CXX_EXTENSIONS NO 96 | ) 97 | 98 | # -------------------------------------------------------------------------------------------------- 99 | # Package 100 | # -------------------------------------------------------------------------------------------------- 101 | add_library(CppPluginFramework::CppPluginFramework ALIAS CppPluginFramework) 102 | 103 | install(TARGETS CppPluginFramework EXPORT CppPluginFrameworkTargets DESTINATION 104 | LIBRARY DESTINATION lib 105 | ARCHIVE DESTINATION lib 106 | RUNTIME DESTINATION bin 107 | INCLUDES DESTINATION include 108 | ) 109 | 110 | install( 111 | DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/inc/CppPluginFramework 112 | DESTINATION include 113 | COMPONENT Devel 114 | ) 115 | 116 | install( 117 | DIRECTORY ${PROJECT_BINARY_DIR}/exports/CppPluginFramework 118 | DESTINATION include 119 | COMPONENT Devel 120 | ) 121 | 122 | include(CMakePackageConfigHelpers) 123 | write_basic_package_version_file( 124 | "${CMAKE_CURRENT_BINARY_DIR}/CppPluginFramework/CppPluginFrameworkConfigVersion.cmake" 125 | VERSION ${CppPluginFramework_VERSION} 126 | COMPATIBILITY AnyNewerVersion 127 | ) 128 | 129 | export(EXPORT CppPluginFrameworkTargets 130 | FILE "${CMAKE_CURRENT_BINARY_DIR}/CppPluginFramework/CppPluginFrameworkTargets.cmake" 131 | NAMESPACE CppPluginFramework:: 132 | ) 133 | 134 | configure_file(cmake/CppPluginFrameworkConfig.cmake 135 | "${CMAKE_CURRENT_BINARY_DIR}/CppPluginFramework/CppPluginFrameworkConfig.cmake" 136 | COPYONLY 137 | ) 138 | 139 | set(ConfigPackageLocation lib/cmake/CppPluginFramework) 140 | 141 | install( 142 | EXPORT CppPluginFrameworkTargets 143 | FILE CppPluginFrameworkTargets.cmake 144 | NAMESPACE CppPluginFramework:: 145 | DESTINATION ${ConfigPackageLocation} 146 | ) 147 | 148 | install( 149 | FILES 150 | cmake/CppPluginFrameworkConfig.cmake 151 | "${CMAKE_CURRENT_BINARY_DIR}/CppPluginFramework/CppPluginFrameworkConfigVersion.cmake" 152 | DESTINATION ${ConfigPackageLocation} 153 | COMPONENT Devel 154 | ) 155 | 156 | # -------------------------------------------------------------------------------------------------- 157 | # Tests 158 | # -------------------------------------------------------------------------------------------------- 159 | enable_testing() 160 | add_subdirectory(tests) 161 | -------------------------------------------------------------------------------- /code/cmake/CodeCoverage.cmake: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2012 - 2017, Lars Bilke 2 | # All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, with or without modification, 5 | # are permitted provided that the following conditions are met: 6 | # 7 | # 1. Redistributions of source code must retain the above copyright notice, this 8 | # list of conditions and the following disclaimer. 9 | # 10 | # 2. Redistributions in binary form must reproduce the above copyright notice, 11 | # this list of conditions and the following disclaimer in the documentation 12 | # and/or other materials provided with the distribution. 13 | # 14 | # 3. Neither the name of the copyright holder nor the names of its contributors 15 | # may be used to endorse or promote products derived from this software without 16 | # specific prior written permission. 17 | # 18 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 22 | # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 | # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 25 | # ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | # 29 | # CHANGES: 30 | # 31 | # 2012-01-31, Lars Bilke 32 | # - Enable Code Coverage 33 | # 34 | # 2013-09-17, Joakim Söderberg 35 | # - Added support for Clang. 36 | # - Some additional usage instructions. 37 | # 38 | # 2016-02-03, Lars Bilke 39 | # - Refactored functions to use named parameters 40 | # 41 | # 2017-06-02, Lars Bilke 42 | # - Merged with modified version from github.com/ufz/ogs 43 | # 44 | # 45 | # USAGE: 46 | # 47 | # 1. Copy this file into your cmake modules path. 48 | # 49 | # 2. Add the following line to your CMakeLists.txt: 50 | # include(CodeCoverage) 51 | # 52 | # 3. Append necessary compiler flags: 53 | # APPEND_COVERAGE_COMPILER_FLAGS() 54 | # 55 | # 4. If you need to exclude additional directories from the report, specify them 56 | # using the COVERAGE_EXCLUDES variable before calling SETUP_TARGET_FOR_COVERAGE. 57 | # Example: 58 | # set(COVERAGE_EXCLUDES 'dir1/*' 'dir2/*') 59 | # 60 | # 5. Use the functions described below to create a custom make target which 61 | # runs your test executable and produces a code coverage report. 62 | # 63 | # 6. Build a Debug build: 64 | # cmake -DCMAKE_BUILD_TYPE=Debug .. 65 | # make 66 | # make my_coverage_target 67 | # 68 | 69 | include(CMakeParseArguments) 70 | 71 | # Check prereqs 72 | find_program( GCOV_PATH gcov ) 73 | find_program( LCOV_PATH lcov ) 74 | find_program( GENHTML_PATH genhtml ) 75 | find_program( GCOVR_PATH gcovr PATHS ${CMAKE_SOURCE_DIR}/scripts/test) 76 | find_program( SIMPLE_PYTHON_EXECUTABLE python ) 77 | 78 | if(NOT GCOV_PATH) 79 | message(FATAL_ERROR "gcov not found! Aborting...") 80 | endif() # NOT GCOV_PATH 81 | 82 | if("${CMAKE_CXX_COMPILER_ID}" MATCHES "(Apple)?[Cc]lang") 83 | if("${CMAKE_CXX_COMPILER_VERSION}" VERSION_LESS 3) 84 | message(FATAL_ERROR "Clang version must be 3.0.0 or greater! Aborting...") 85 | endif() 86 | elseif(NOT CMAKE_COMPILER_IS_GNUCXX) 87 | message(FATAL_ERROR "Compiler is not GNU gcc! Aborting...") 88 | endif() 89 | 90 | set(COVERAGE_COMPILER_FLAGS "-g -O0 --coverage -fprofile-arcs -ftest-coverage" 91 | CACHE INTERNAL "") 92 | 93 | set(CMAKE_CXX_FLAGS_COVERAGE 94 | ${COVERAGE_COMPILER_FLAGS} 95 | CACHE STRING "Flags used by the C++ compiler during coverage builds." 96 | FORCE ) 97 | set(CMAKE_C_FLAGS_COVERAGE 98 | ${COVERAGE_COMPILER_FLAGS} 99 | CACHE STRING "Flags used by the C compiler during coverage builds." 100 | FORCE ) 101 | set(CMAKE_EXE_LINKER_FLAGS_COVERAGE 102 | "" 103 | CACHE STRING "Flags used for linking binaries during coverage builds." 104 | FORCE ) 105 | set(CMAKE_SHARED_LINKER_FLAGS_COVERAGE 106 | "" 107 | CACHE STRING "Flags used by the shared libraries linker during coverage builds." 108 | FORCE ) 109 | mark_as_advanced( 110 | CMAKE_CXX_FLAGS_COVERAGE 111 | CMAKE_C_FLAGS_COVERAGE 112 | CMAKE_EXE_LINKER_FLAGS_COVERAGE 113 | CMAKE_SHARED_LINKER_FLAGS_COVERAGE ) 114 | 115 | if(NOT CMAKE_BUILD_TYPE STREQUAL "Debug") 116 | message(WARNING "Code coverage results with an optimised (non-Debug) build may be misleading") 117 | endif() # NOT CMAKE_BUILD_TYPE STREQUAL "Debug" 118 | 119 | if(CMAKE_C_COMPILER_ID STREQUAL "GNU") 120 | link_libraries(gcov) 121 | else() 122 | set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --coverage") 123 | endif() 124 | 125 | # Defines a target for running and collection code coverage information 126 | # Builds dependencies, runs the given executable and outputs reports. 127 | # NOTE! The executable should always have a ZERO as exit code otherwise 128 | # the coverage generation will not complete. 129 | # 130 | # SETUP_TARGET_FOR_COVERAGE( 131 | # NAME testrunner_coverage # New target name 132 | # EXECUTABLE testrunner -j ${PROCESSOR_COUNT} # Executable in PROJECT_BINARY_DIR 133 | # DEPENDENCIES testrunner # Dependencies to build first 134 | # PROJECT_PATH /project/path # Project path 135 | # ) 136 | function(SETUP_TARGET_FOR_COVERAGE) 137 | 138 | set(options NONE) 139 | set(oneValueArgs NAME PROJECT_PATH) 140 | set(multiValueArgs EXECUTABLE EXECUTABLE_ARGS DEPENDENCIES EXCLUDES) 141 | cmake_parse_arguments(Coverage "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) 142 | 143 | if(NOT LCOV_PATH) 144 | message(FATAL_ERROR "lcov not found! Aborting...") 145 | endif() # NOT LCOV_PATH 146 | 147 | if(NOT GENHTML_PATH) 148 | message(FATAL_ERROR "genhtml not found! Aborting...") 149 | endif() # NOT GENHTML_PATH 150 | 151 | # Setup target 152 | if (Coverage_PROJECT_PATH) 153 | set(GENHTML_PREFIX --prefix ${Coverage_PROJECT_PATH}) 154 | endif() 155 | 156 | add_custom_target(${Coverage_NAME} 157 | 158 | # Cleanup lcov 159 | COMMAND ${LCOV_PATH} --directory . --zerocounters 160 | # Create baseline to make sure untouched files show up in the report 161 | COMMAND ${LCOV_PATH} -c -i -d . -o ${Coverage_NAME}.base 162 | 163 | # Run tests 164 | COMMAND ${Coverage_EXECUTABLE} 165 | 166 | # Capturing lcov counters and generating report 167 | COMMAND ${LCOV_PATH} --directory . --capture --output-file ${Coverage_NAME}.info 168 | 169 | # add baseline counters 170 | COMMAND ${LCOV_PATH} -a ${Coverage_NAME}.base -a ${Coverage_NAME}.info --output-file ${Coverage_NAME}.total 171 | COMMAND ${LCOV_PATH} --remove ${Coverage_NAME}.total ${Coverage_EXCLUDES} --output-file ${PROJECT_BINARY_DIR}/${Coverage_NAME}.info.cleaned 172 | COMMAND ${GENHTML_PATH} -o ${Coverage_NAME} ${PROJECT_BINARY_DIR}/${Coverage_NAME}.info.cleaned ${GENHTML_PREFIX} 173 | COMMAND ${CMAKE_COMMAND} -E remove ${Coverage_NAME}.base ${Coverage_NAME}.info ${Coverage_NAME}.total ${PROJECT_BINARY_DIR}/${Coverage_NAME}.info.cleaned 174 | 175 | WORKING_DIRECTORY ${PROJECT_BINARY_DIR} 176 | DEPENDS ${Coverage_DEPENDENCIES} 177 | COMMENT "Resetting code coverage counters to zero.\nProcessing code coverage counters and generating report." 178 | ) 179 | 180 | # Show info where to find the report 181 | add_custom_command(TARGET ${Coverage_NAME} POST_BUILD 182 | COMMAND ; 183 | COMMENT "Open ./${Coverage_NAME}/index.html in your browser to view the coverage report." 184 | ) 185 | 186 | endfunction() # SETUP_TARGET_FOR_COVERAGE 187 | 188 | # Defines a target for running and collection code coverage information 189 | # Builds dependencies, runs the given executable and outputs reports. 190 | # NOTE! The executable should always have a ZERO as exit code otherwise 191 | # the coverage generation will not complete. 192 | # 193 | # SETUP_TARGET_FOR_COVERAGE_COBERTURA( 194 | # NAME ctest_coverage # New target name 195 | # EXECUTABLE ctest -j ${PROCESSOR_COUNT} # Executable in PROJECT_BINARY_DIR 196 | # DEPENDENCIES executable_target # Dependencies to build first 197 | # ) 198 | function(SETUP_TARGET_FOR_COVERAGE_COBERTURA) 199 | 200 | set(options NONE) 201 | set(oneValueArgs NAME) 202 | set(multiValueArgs EXECUTABLE EXECUTABLE_ARGS DEPENDENCIES) 203 | cmake_parse_arguments(Coverage "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) 204 | 205 | if(NOT SIMPLE_PYTHON_EXECUTABLE) 206 | message(FATAL_ERROR "python not found! Aborting...") 207 | endif() # NOT SIMPLE_PYTHON_EXECUTABLE 208 | 209 | if(NOT GCOVR_PATH) 210 | message(FATAL_ERROR "gcovr not found! Aborting...") 211 | endif() # NOT GCOVR_PATH 212 | 213 | # Combine excludes to several -e arguments 214 | set(COBERTURA_EXCLUDES "") 215 | foreach(EXCLUDE ${COVERAGE_EXCLUDES}) 216 | set(COBERTURA_EXCLUDES "-e ${EXCLUDE} ${COBERTURA_EXCLUDES}") 217 | endforeach() 218 | 219 | add_custom_target(${Coverage_NAME} 220 | 221 | # Run tests 222 | ${Coverage_EXECUTABLE} 223 | 224 | # Running gcovr 225 | COMMAND ${GCOVR_PATH} -x -r ${CMAKE_SOURCE_DIR} ${COBERTURA_EXCLUDES} 226 | -o ${Coverage_NAME}.xml 227 | WORKING_DIRECTORY ${PROJECT_BINARY_DIR} 228 | DEPENDS ${Coverage_DEPENDENCIES} 229 | COMMENT "Running gcovr to produce Cobertura code coverage report." 230 | ) 231 | 232 | # Show info where to find the report 233 | add_custom_command(TARGET ${Coverage_NAME} POST_BUILD 234 | COMMAND ; 235 | COMMENT "Cobertura code coverage report saved in ${Coverage_NAME}.xml." 236 | ) 237 | 238 | endfunction() # SETUP_TARGET_FOR_COVERAGE_COBERTURA 239 | 240 | function(APPEND_COVERAGE_COMPILER_FLAGS) 241 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${COVERAGE_COMPILER_FLAGS}" PARENT_SCOPE) 242 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${COVERAGE_COMPILER_FLAGS}" PARENT_SCOPE) 243 | message(STATUS "Appending code coverage compiler flags: ${COVERAGE_COMPILER_FLAGS}") 244 | endfunction() # APPEND_COVERAGE_COMPILER_FLAGS 245 | -------------------------------------------------------------------------------- /code/cmake/CppPluginFrameworkConfig.cmake: -------------------------------------------------------------------------------- 1 | include(CMakeFindDependencyMacro) 2 | find_dependency(CppConfigFramework) 3 | 4 | include("${CMAKE_CURRENT_LIST_DIR}/CppPluginFrameworkTargets.cmake") 5 | -------------------------------------------------------------------------------- /code/inc/CppPluginFramework/AbstractPlugin.hpp: -------------------------------------------------------------------------------- 1 | /* This file is part of C++ Plugin Framework. 2 | * 3 | * C++ Plugin Framework is free software: you can redistribute it and/or modify it under the terms 4 | * of the GNU Lesser General Public License as published by the Free Software Foundation, either 5 | * version 3 of the License, or (at your option) any later version. 6 | * 7 | * C++ Plugin Framework is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 8 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | * GNU Lesser General Public License for more details. 10 | * 11 | * You should have received a copy of the GNU Lesser General Public License along with C++ Plugin 12 | * Framework. If not, see . 13 | */ 14 | 15 | /*! 16 | * \file 17 | * 18 | * Contains a convenience base class for creating plugins 19 | */ 20 | 21 | #pragma once 22 | 23 | // C++ Plugin Framework includes 24 | #include 25 | 26 | // Qt includes 27 | #include 28 | 29 | // System includes 30 | 31 | // Forward declarations 32 | 33 | // Macros 34 | 35 | // ------------------------------------------------------------------------------------------------- 36 | 37 | namespace CppPluginFramework 38 | { 39 | 40 | /*! 41 | * This is a convenience base class for creating plugins. It provides a common implementation a part 42 | * of the plugin interface. 43 | * 44 | * Derived classes still need to implement the following: 45 | * - Loading of a configuration 46 | * - Injection and ejection of dependencies 47 | * 48 | * Derived classes can also hook to events: 49 | * - Starting plugin 50 | * - Stopping plugin 51 | */ 52 | class CPPPLUGINFRAMEWORK_EXPORT AbstractPlugin : public IPlugin 53 | { 54 | public: 55 | /*! 56 | * Constructor 57 | * 58 | * \param name Plugin instance name 59 | * \param version Plugin version 60 | */ 61 | AbstractPlugin(const QString &name, 62 | const VersionInfo &version, 63 | const QString &description, 64 | const QSet &exportedInterfaces); 65 | 66 | //! Destructor 67 | ~AbstractPlugin() override = default; 68 | 69 | //! \copydoc CppPluginFramework::IPlugin::name() 70 | QString name() const override final; 71 | 72 | //! \copydoc CppPluginFramework::IPlugin::version() 73 | VersionInfo version() const override final; 74 | 75 | //! \copydoc CppPluginFramework::IPlugin::description() 76 | QString description() const override final; 77 | 78 | //! \copydoc CppPluginFramework::IPlugin::isInterfaceExported() 79 | bool isInterfaceExported(const QString &interface) const override final; 80 | 81 | //! \copydoc CppPluginFramework::IPlugin::exportedInterfaces() 82 | QSet exportedInterfaces() const override final; 83 | 84 | //! \copydoc CppPluginFramework::IPlugin::isStarted() 85 | bool isStarted() const override final; 86 | 87 | //! \copydoc CppPluginFramework::IPlugin::start() 88 | bool start() override final; 89 | 90 | //! \copydoc CppPluginFramework::IPlugin::stop() 91 | void stop() override final; 92 | 93 | private: 94 | /*! 95 | * Executes the startup procedure 96 | * 97 | * \retval true Success 98 | * \retval false Failure 99 | * 100 | * This method is called inside start() method if the plugin is not started yet. Default 101 | * implementation doesn't do anything. This method needs to be overridden if anything needs 102 | * to be done during startup. 103 | */ 104 | virtual bool onStart(); 105 | 106 | /*! 107 | * Executes the shutdown procedure 108 | * 109 | * This method is called inside stop() method if the plugin is not started yet. Default 110 | * implementation doesn't do anything. This method needs to be overridden if anything needs 111 | * to be done during startup. 112 | */ 113 | virtual void onStop(); 114 | 115 | private: 116 | //! Enables thread-safe access to the plugin data 117 | mutable QMutex m_mutex; 118 | 119 | //! Holds the name of the plugin instance 120 | QString m_name; 121 | 122 | //! Holds the version of the plugin 123 | VersionInfo m_version; 124 | 125 | //! Holds the description of the plugin 126 | QString m_description; 127 | 128 | //! Holds the list of exported interfaces of the plugin 129 | QSet m_exportedInterfaces; 130 | 131 | //! Holds the "started" flag 132 | bool m_started; 133 | }; 134 | 135 | } 136 | -------------------------------------------------------------------------------- /code/inc/CppPluginFramework/IPlugin.hpp: -------------------------------------------------------------------------------- 1 | /* This file is part of C++ Plugin Framework. 2 | * 3 | * C++ Plugin Framework is free software: you can redistribute it and/or modify it under the terms 4 | * of the GNU Lesser General Public License as published by the Free Software Foundation, either 5 | * version 3 of the License, or (at your option) any later version. 6 | * 7 | * C++ Plugin Framework is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 8 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | * GNU Lesser General Public License for more details. 10 | * 11 | * You should have received a copy of the GNU Lesser General Public License along with C++ Plugin 12 | * Framework. If not, see . 13 | */ 14 | 15 | /*! 16 | * \file 17 | * 18 | * Contains a base class for all plugins 19 | */ 20 | 21 | #pragma once 22 | 23 | // C++ Plugin Framework includes 24 | #include 25 | 26 | // C++ Config Framework includes 27 | #include 28 | 29 | // Qt includes 30 | 31 | // System includes 32 | 33 | // Forward declarations 34 | 35 | // Macros 36 | 37 | // ------------------------------------------------------------------------------------------------- 38 | 39 | namespace CppPluginFramework 40 | { 41 | 42 | //! This is a base class for all plugins 43 | class CPPPLUGINFRAMEWORK_EXPORT IPlugin 44 | { 45 | public: 46 | //! Destructor 47 | virtual ~IPlugin() = default; 48 | 49 | /*! 50 | * Returns plugin's name 51 | * 52 | * \return Plugin's name 53 | */ 54 | virtual QString name() const = 0; 55 | 56 | /*! 57 | * Returns plugin's version 58 | * 59 | * \return Plugin's version 60 | */ 61 | virtual VersionInfo version() const = 0; 62 | 63 | /*! 64 | * Returns plugin's description 65 | * 66 | * \return Plugin's description 67 | */ 68 | virtual QString description() const = 0; 69 | 70 | /*! 71 | * Checks if the specified interface is exported by this plugin 72 | * 73 | * \param interface Name of the interface to check 74 | * 75 | * \retval true The specified interface is exported 76 | * \retval false The specified interface is not exported 77 | */ 78 | virtual bool isInterfaceExported(const QString &interface) const = 0; 79 | 80 | /*! 81 | * Returns a list of all exported interfaces 82 | * 83 | * \return List of all exported interfaces 84 | */ 85 | virtual QSet exportedInterfaces() const = 0; 86 | 87 | /*! 88 | * Loads the specified config 89 | * 90 | * \param config Config node 91 | * 92 | * \retval true Success 93 | * \retval false Failure 94 | */ 95 | virtual bool loadConfig(const CppConfigFramework::ConfigObjectNode &config) = 0; 96 | 97 | /*! 98 | * Injects all dependencies (interfaces) that can be taken out of the specified plugin instance 99 | * 100 | * \param plugin Plugin 101 | * 102 | * \retval true Success 103 | * \retval false Failure 104 | */ 105 | virtual bool injectDependency(IPlugin *plugin) = 0; 106 | 107 | //! Ejects all injected dependencies (interfaces) 108 | virtual void ejectDependencies() = 0; 109 | 110 | /*! 111 | * Checks if plugin is started 112 | * 113 | * \retval true Plugin is started 114 | * \retval false Plugin is not started 115 | */ 116 | virtual bool isStarted() const = 0; 117 | 118 | /*! 119 | * Starts the plugin 120 | * 121 | * \retval true Plugin was started 122 | * \retval false Plugin was not started 123 | */ 124 | virtual bool start() = 0; 125 | 126 | //! Stops the plugin 127 | virtual void stop() = 0; 128 | 129 | /*! 130 | * Convenience method for casting this plugin to the specified interface 131 | * 132 | * \return Pointer to the specified interface or a null pointer if this plugin does not 133 | * implement the specified interface 134 | */ 135 | template 136 | T* interface() 137 | { 138 | return dynamic_cast(this); 139 | } 140 | }; 141 | 142 | } // namespace CppPluginFramework 143 | -------------------------------------------------------------------------------- /code/inc/CppPluginFramework/IPluginFactory.hpp: -------------------------------------------------------------------------------- 1 | /* This file is part of C++ Plugin Framework. 2 | * 3 | * C++ Plugin Framework is free software: you can redistribute it and/or modify it under the terms 4 | * of the GNU Lesser General Public License as published by the Free Software Foundation, either 5 | * version 3 of the License, or (at your option) any later version. 6 | * 7 | * C++ Plugin Framework is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 8 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | * GNU Lesser General Public License for more details. 10 | * 11 | * You should have received a copy of the GNU Lesser General Public License along with C++ Plugin 12 | * Framework. If not, see . 13 | */ 14 | 15 | /*! 16 | * \file 17 | * 18 | * Contains a base class for a factory that shall be able to create instances of a plugin 19 | */ 20 | 21 | #pragma once 22 | 23 | // C++ Plugin Framework includes 24 | #include 25 | 26 | // Qt includes 27 | 28 | // System includes 29 | 30 | // Forward declarations 31 | 32 | // Macros 33 | 34 | // ------------------------------------------------------------------------------------------------- 35 | 36 | namespace CppPluginFramework 37 | { 38 | 39 | //! This is a base class for a factory that shall be able to create instances of a plugin 40 | class CPPPLUGINFRAMEWORK_EXPORT IPluginFactory 41 | { 42 | public: 43 | //! Destructor 44 | virtual ~IPluginFactory() = default; 45 | 46 | /*! 47 | * Creates a plugin instance 48 | * 49 | * \param instanceName Name of the plugin instance 50 | * 51 | * \return Plugin instance or nullptr if the instance could not be created 52 | */ 53 | virtual std::unique_ptr createInstance(const QString &instanceName) const = 0; 54 | }; 55 | 56 | } // namespace CppPluginFramework 57 | 58 | //! Maps the class name to its string equivalent 59 | Q_DECLARE_INTERFACE(CppPluginFramework::IPluginFactory, "CppPluginFramework::IPluginFactory") 60 | -------------------------------------------------------------------------------- /code/inc/CppPluginFramework/LoggingCategories.hpp: -------------------------------------------------------------------------------- 1 | /* This file is part of C++ Plugin Framework. 2 | * 3 | * C++ Plugin Framework is free software: you can redistribute it and/or modify it under the terms 4 | * of the GNU Lesser General Public License as published by the Free Software Foundation, either 5 | * version 3 of the License, or (at your option) any later version. 6 | * 7 | * C++ Plugin Framework is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 8 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | * GNU Lesser General Public License for more details. 10 | * 11 | * You should have received a copy of the GNU Lesser General Public License along with C++ Plugin 12 | * Framework. If not, see . 13 | */ 14 | 15 | /*! 16 | * \file 17 | * 18 | * Contains logging categories 19 | */ 20 | 21 | #pragma once 22 | 23 | // C++ Plugin Framework includes 24 | #include 25 | 26 | // Qt includes 27 | #include 28 | 29 | // System includes 30 | 31 | // Forward declarations 32 | 33 | // Macros 34 | 35 | // ------------------------------------------------------------------------------------------------- 36 | 37 | namespace CppPluginFramework 38 | { 39 | 40 | namespace LoggingCategory 41 | { 42 | 43 | //! Logging category for plugin config 44 | CPPPLUGINFRAMEWORK_EXPORT extern const QLoggingCategory Config; 45 | 46 | //! Logging category for Plugin 47 | CPPPLUGINFRAMEWORK_EXPORT extern const QLoggingCategory Plugin; 48 | 49 | //! Logging category for PluginManager 50 | CPPPLUGINFRAMEWORK_EXPORT extern const QLoggingCategory PluginManager; 51 | 52 | } // namespace LoggingCategory 53 | 54 | } // namespace CppPluginFramework 55 | -------------------------------------------------------------------------------- /code/inc/CppPluginFramework/Plugin.hpp: -------------------------------------------------------------------------------- 1 | /* This file is part of C++ Plugin Framework. 2 | * 3 | * C++ Plugin Framework is free software: you can redistribute it and/or modify it under the terms 4 | * of the GNU Lesser General Public License as published by the Free Software Foundation, either 5 | * version 3 of the License, or (at your option) any later version. 6 | * 7 | * C++ Plugin Framework is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 8 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | * GNU Lesser General Public License for more details. 10 | * 11 | * You should have received a copy of the GNU Lesser General Public License along with C++ Plugin 12 | * Framework. If not, see . 13 | */ 14 | 15 | /*! 16 | * \file 17 | * 18 | * Contains a class that is able to load a plugin from a library 19 | */ 20 | 21 | #pragma once 22 | 23 | // C++ Plugin Framework includes 24 | #include 25 | #include 26 | 27 | // Qt includes 28 | 29 | // System includes 30 | 31 | // Forward declarations 32 | 33 | // Macros 34 | 35 | // ------------------------------------------------------------------------------------------------- 36 | 37 | namespace CppPluginFramework 38 | { 39 | 40 | //! This class enables loading of plugin instances from a library 41 | class CPPPLUGINFRAMEWORK_EXPORT Plugin 42 | { 43 | public: 44 | /*! 45 | * Loads plugin instances from the specified library 46 | * 47 | * \param pluginConfig Plugin config 48 | * 49 | * \return Loaded plugin instances or an empty vector if loading failed 50 | */ 51 | static std::vector> loadInstances(const PluginConfig &pluginConfig); 52 | 53 | private: 54 | //! Construction of this class is disabled 55 | Plugin() = delete; 56 | 57 | /*! 58 | * Loads the plugin instance from the specified library and configures it 59 | * 60 | * \param instanceConfig Plugin instance config 61 | * 62 | * \return Loaded plugin instance or nullptr if loading failed 63 | */ 64 | static std::unique_ptr loadInstance(const IPluginFactory &pluginFactory, 65 | const PluginInstanceConfig &instanceConfig); 66 | 67 | /*! 68 | * Checks if the version matches the plugin config's version requirements 69 | * 70 | * \param pluginVersion Plugin version 71 | * \param pluginConfig Plugin config 72 | * 73 | * \retval true Success 74 | * \retval false Failure 75 | */ 76 | static bool checkVersion(const VersionInfo &pluginVersion, const PluginConfig &pluginConfig); 77 | }; 78 | 79 | } // namespace CppPluginFramework 80 | -------------------------------------------------------------------------------- /code/inc/CppPluginFramework/PluginConfig.hpp: -------------------------------------------------------------------------------- 1 | /* This file is part of C++ Plugin Framework. 2 | * 3 | * C++ Plugin Framework is free software: you can redistribute it and/or modify it under the terms 4 | * of the GNU Lesser General Public License as published by the Free Software Foundation, either 5 | * version 3 of the License, or (at your option) any later version. 6 | * 7 | * C++ Plugin Framework is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 8 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | * GNU Lesser General Public License for more details. 10 | * 11 | * You should have received a copy of the GNU Lesser General Public License along with C++ Plugin 12 | * Framework. If not, see . 13 | */ 14 | 15 | /*! 16 | * \file 17 | * 18 | * Contains a config class for a Plugin 19 | */ 20 | 21 | #pragma once 22 | 23 | // C++ Plugin Framework includes 24 | #include 25 | #include 26 | 27 | // Qt includes 28 | 29 | // System includes 30 | 31 | // Forward declarations 32 | 33 | // Macros 34 | 35 | // ------------------------------------------------------------------------------------------------- 36 | 37 | namespace CppPluginFramework 38 | { 39 | 40 | //! Config class for a Plugin 41 | class CPPPLUGINFRAMEWORK_EXPORT PluginConfig : public CppConfigFramework::ConfigItem 42 | { 43 | public: 44 | //! Constructor 45 | PluginConfig() = default; 46 | 47 | /*! 48 | * Constructor 49 | * 50 | * \param filePath Path to the plugin's library 51 | * \param version Required plugin version 52 | * \param instanceConfigs List of plugin's instance configs 53 | */ 54 | PluginConfig(const QString &filePath, 55 | const VersionInfo &version, 56 | const QList &instanceConfigs); 57 | 58 | /*! 59 | * Constructor 60 | * 61 | * \param filePath Path to the plugin's library 62 | * \param minVersion Minimum required plugin version 63 | * \param maxVersion Maximum required plugin version 64 | * \param instanceConfigs List of plugin's instance configs 65 | */ 66 | PluginConfig(const QString &filePath, 67 | const VersionInfo &minVersion, 68 | const VersionInfo &maxVersion, 69 | const QList &instanceConfigs); 70 | 71 | /*! 72 | * Copy constructor 73 | * 74 | * \param other Instance to copy 75 | */ 76 | PluginConfig(const PluginConfig &other) = default; 77 | 78 | /*! 79 | * Move constructor 80 | * 81 | * \param other Instance to move 82 | */ 83 | PluginConfig(PluginConfig &&other) noexcept = default; 84 | 85 | //! Destructor 86 | ~PluginConfig() override = default; 87 | 88 | /*! 89 | * Copy assignment operator 90 | * 91 | * \param other Instance to copy assign 92 | * 93 | * \return Reference to this instance after the assignment is made 94 | */ 95 | PluginConfig &operator=(const PluginConfig &other) = default; 96 | 97 | /*! 98 | * Move assignment operator 99 | * 100 | * \param other Instance to move assign 101 | * 102 | * \return Reference to this instance after the assignment is made 103 | */ 104 | PluginConfig &operator=(PluginConfig &&other) noexcept = default; 105 | 106 | /*! 107 | * Checks if plugin config is valid 108 | * 109 | * \retval true Plugin config is valid 110 | * \retval false Plugin config is not valid 111 | */ 112 | bool isValid() const; 113 | 114 | /*! 115 | * Checks if a specific version is required 116 | * 117 | * \retval true A specific version is required 118 | * \retval false A version range is allowed 119 | */ 120 | bool isExactVersion() const; 121 | 122 | /*! 123 | * Checks if a version range is allowed 124 | * 125 | * \retval true A version range is allowed 126 | * \retval false A specific version is required 127 | */ 128 | bool isVersionRange() const; 129 | 130 | /*! 131 | * Returns file path to the plugin's library 132 | * 133 | * \return File path to the plugin's library 134 | */ 135 | QString filePath() const; 136 | 137 | /*! 138 | * Sets file path to the plugin's library 139 | * 140 | * \param filePath File path to the plugin's library 141 | */ 142 | void setFilePath(const QString &filePath); 143 | 144 | /*! 145 | * Returns plugin's required version 146 | * 147 | * \return Plugin's required version 148 | */ 149 | VersionInfo version() const; 150 | 151 | /*! 152 | * Sets plugin's required version 153 | * 154 | * \param version Plugin's required version 155 | */ 156 | void setVersion(const VersionInfo &version); 157 | 158 | /*! 159 | * Returns plugin's required minimum version 160 | * 161 | * \return Plugin's required minimum version 162 | */ 163 | VersionInfo minVersion() const; 164 | 165 | /*! 166 | * Sets plugin's required minimum version 167 | * 168 | * \param minVersion Plugin's required minimum version 169 | */ 170 | void setMinVersion(const VersionInfo &minVersion); 171 | 172 | /*! 173 | * Returns plugin's required maximum version 174 | * 175 | * \return Plugin's required maximum version 176 | */ 177 | VersionInfo maxVersion() const; 178 | 179 | /*! 180 | * Sets plugin's required maximum version 181 | * 182 | * \param minVersion Plugin's required maximum version 183 | */ 184 | void setMaxVersion(const VersionInfo &maxVersion); 185 | 186 | /*! 187 | * Returns list of plugin's instance configs 188 | * 189 | * \return List of plugin's instance configs 190 | */ 191 | const QList &instanceConfigs() const; 192 | 193 | /*! 194 | * Sets list of plugin's instance configs 195 | * 196 | * \param instanceConfigs List of plugin's instance configs 197 | */ 198 | void setInstanceConfigs(const QList &instanceConfigs); 199 | 200 | private: 201 | //! \copydoc CppConfigFramework::ConfigItem::loadConfigParameters() 202 | bool loadConfigParameters(const CppConfigFramework::ConfigObjectNode &config) override; 203 | 204 | //! \copydoc CppConfigFramework::ConfigItem::storeConfigParameters() 205 | bool storeConfigParameters(CppConfigFramework::ConfigObjectNode *config) override; 206 | 207 | //! \copydoc CppConfigFramework::ConfigItem::validateConfig() 208 | QString validateConfig() const override; 209 | 210 | private: 211 | //! Holds the path to the plugin's library 212 | QString m_filePath; 213 | 214 | //! Holds the plugin's required version 215 | VersionInfo m_version; 216 | 217 | //! Holds the plugin's minimum required version 218 | VersionInfo m_minVersion; 219 | 220 | //! Holds the plugin's maximum required version 221 | VersionInfo m_maxVersion; 222 | 223 | //! Holds the list of plugin's instance configs 224 | QList m_instanceConfigs; 225 | }; 226 | 227 | } // namespace CppPluginFramework 228 | 229 | // ------------------------------------------------------------------------------------------------- 230 | 231 | /*! 232 | * Global "equal to" operator for CppPluginFramework::PluginConfig 233 | * 234 | * \param left Plugin config 235 | * \param right Plugin config 236 | * 237 | * \retval true Plugin configs are equal 238 | * \retval false Plugin configs are not equal 239 | */ 240 | CPPPLUGINFRAMEWORK_EXPORT bool operator==(const CppPluginFramework::PluginConfig &left, 241 | const CppPluginFramework::PluginConfig &right); 242 | 243 | // ------------------------------------------------------------------------------------------------- 244 | 245 | /*! 246 | * Global "not equal to" operator for CppPluginFramework::PluginConfig 247 | * 248 | * \param left Plugin config 249 | * \param right Plugin config 250 | * 251 | * \retval true Plugin configs are not equal 252 | * \retval false Plugin configs are equal 253 | */ 254 | CPPPLUGINFRAMEWORK_EXPORT bool operator!=(const CppPluginFramework::PluginConfig &left, 255 | const CppPluginFramework::PluginConfig &right); 256 | -------------------------------------------------------------------------------- /code/inc/CppPluginFramework/PluginFactoryTemplate.hpp: -------------------------------------------------------------------------------- 1 | /* This file is part of C++ Plugin Framework. 2 | * 3 | * C++ Plugin Framework is free software: you can redistribute it and/or modify it under the terms 4 | * of the GNU Lesser General Public License as published by the Free Software Foundation, either 5 | * version 3 of the License, or (at your option) any later version. 6 | * 7 | * C++ Plugin Framework is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 8 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | * GNU Lesser General Public License for more details. 10 | * 11 | * You should have received a copy of the GNU Lesser General Public License along with C++ Plugin 12 | * Framework. If not, see . 13 | */ 14 | 15 | /*! 16 | * \file 17 | * 18 | * Contains a template class for a factory that shall be able to create instances of a plugin 19 | */ 20 | 21 | #pragma once 22 | 23 | // C++ Plugin Framework includes 24 | #include 25 | 26 | // Qt includes 27 | #include 28 | 29 | // System includes 30 | 31 | // Forward declarations 32 | 33 | // Macros 34 | 35 | // ------------------------------------------------------------------------------------------------- 36 | 37 | namespace CppPluginFramework 38 | { 39 | 40 | /*! 41 | * This is a template class for a factory that shall be able to create instances of a plugin 42 | * 43 | * \tparam T Plugin class 44 | * 45 | * To create a plugin the following code needs to be put into the header file, preferably directly 46 | * after the declaration of the plugin class: 47 | * 48 | * \code{.cpp} 49 | * 50 | * class Q_DECL_EXPORT PluginFactory : 51 | * public QObject, 52 | * public CppPluginFramework::PluginFactoryTemplate 53 | * { 54 | * Q_OBJECT 55 | * Q_PLUGIN_METADATA(IID "CppPluginFramework::IPluginFactory") 56 | * Q_INTERFACES(CppPluginFramework::IPluginFactory) 57 | * 58 | * public: 59 | * ~PluginFactory() override = default; 60 | * }; 61 | * 62 | * \endcode 63 | * 64 | * \note The PLUGIN_CLASS placeholders need to be replaced with the plugin class name 65 | */ 66 | template::value, bool> = true> 67 | class PluginFactoryTemplate : public IPluginFactory 68 | { 69 | public: 70 | //! Destructor 71 | ~PluginFactoryTemplate() override = default; 72 | 73 | //! \copydoc IPluginFactory::createPluginInstance() 74 | std::unique_ptr createInstance(const QString &instanceName) const override 75 | { 76 | return std::make_unique(instanceName); 77 | } 78 | }; 79 | 80 | } // namespace CppPluginFramework 81 | -------------------------------------------------------------------------------- /code/inc/CppPluginFramework/PluginInstanceConfig.hpp: -------------------------------------------------------------------------------- 1 | /* This file is part of C++ Plugin Framework. 2 | * 3 | * C++ Plugin Framework is free software: you can redistribute it and/or modify it under the terms 4 | * of the GNU Lesser General Public License as published by the Free Software Foundation, either 5 | * version 3 of the License, or (at your option) any later version. 6 | * 7 | * C++ Plugin Framework is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 8 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | * GNU Lesser General Public License for more details. 10 | * 11 | * You should have received a copy of the GNU Lesser General Public License along with C++ Plugin 12 | * Framework. If not, see . 13 | */ 14 | 15 | /*! 16 | * \file 17 | * 18 | * Contains a config class for a Plugin Instance 19 | */ 20 | 21 | #pragma once 22 | 23 | // C++ Plugin Framework includes 24 | #include 25 | 26 | // C++ Config Framework includes 27 | #include 28 | 29 | // Qt includes 30 | 31 | // System includes 32 | 33 | // Forward declarations 34 | 35 | // Macros 36 | 37 | // ------------------------------------------------------------------------------------------------- 38 | 39 | namespace CppPluginFramework 40 | { 41 | 42 | //! Config class for a Plugin Instance 43 | class CPPPLUGINFRAMEWORK_EXPORT PluginInstanceConfig : public CppConfigFramework::ConfigItem 44 | { 45 | public: 46 | //! Constructor 47 | PluginInstanceConfig() = default; 48 | 49 | /*! 50 | * Constructor 51 | * 52 | * \param name Name of the plugin instance 53 | * \param config Plugin instance's config 54 | * \param dependencies List of plugin's dependencies 55 | */ 56 | PluginInstanceConfig(const QString &name, 57 | const CppConfigFramework::ConfigObjectNode &config = {}, 58 | const QSet &dependencies = {}); 59 | 60 | /*! 61 | * Copy constructor 62 | * 63 | * \param other Instance to copy 64 | */ 65 | PluginInstanceConfig(const PluginInstanceConfig &other); 66 | 67 | /*! 68 | * Move constructor 69 | * 70 | * \param other Instance to move 71 | */ 72 | PluginInstanceConfig(PluginInstanceConfig &&other) noexcept = default; 73 | 74 | //! Destructor 75 | ~PluginInstanceConfig() override = default; 76 | 77 | /*! 78 | * Copy assignment operator 79 | * 80 | * \param other Instance to copy assign 81 | * 82 | * \return Reference to this instance after the assignment is made 83 | */ 84 | PluginInstanceConfig &operator=(const PluginInstanceConfig &other); 85 | 86 | /*! 87 | * Move assignment operator 88 | * 89 | * \param other Instance to move assign 90 | * 91 | * \return Reference to this instance after the assignment is made 92 | */ 93 | PluginInstanceConfig &operator=(PluginInstanceConfig &&other) noexcept = default; 94 | 95 | /*! 96 | * Checks if plugin instance config is valid 97 | * 98 | * \retval true Plugin instance config is valid 99 | * \retval false Plugin instance config is not valid 100 | */ 101 | bool isValid() const; 102 | 103 | /*! 104 | * Returns plugin instance's name 105 | * 106 | * \return Plugin instance's name 107 | */ 108 | QString name() const; 109 | 110 | /*! 111 | * Sets plugin instance's name 112 | * 113 | * \param name Plugin instance's name 114 | */ 115 | void setName(const QString &name); 116 | 117 | /*! 118 | * Returns the plugin instance's config 119 | * 120 | * \return Plugin instance's config 121 | */ 122 | const CppConfigFramework::ConfigObjectNode &config() const; 123 | 124 | /*! 125 | * Sets the plugin instance's config 126 | * 127 | * \param config Plugin instance's config 128 | */ 129 | void setConfig(const CppConfigFramework::ConfigObjectNode &config); 130 | 131 | /*! 132 | * Returns list of plugin instance's dependencies 133 | * 134 | * \return List of plugin instance's dependencies 135 | */ 136 | QSet dependencies() const; 137 | 138 | /*! 139 | * Sets list of plugin instance's dependencies 140 | * 141 | * \param dependencies List of plugin instance's dependencies 142 | */ 143 | void setDependencies(const QSet &dependencies); 144 | 145 | private: 146 | //! \copydoc CppConfigFramework::ConfigItem::loadConfigParameters() 147 | bool loadConfigParameters(const CppConfigFramework::ConfigObjectNode &config) override; 148 | 149 | //! \copydoc CppConfigFramework::ConfigItem::storeConfigParameters() 150 | bool storeConfigParameters(CppConfigFramework::ConfigObjectNode *config) override; 151 | 152 | //! \copydoc CppConfigFramework::ConfigItem::validateConfig() 153 | QString validateConfig() const override; 154 | 155 | private: 156 | //! Holds the plugin instance's name 157 | QString m_name; 158 | 159 | //! Holds the (optional) plugin instance's config node 160 | CppConfigFramework::ConfigObjectNode m_config; 161 | 162 | //! Holds the list of plugin instance's dependencies 163 | QSet m_dependencies; 164 | }; 165 | 166 | } // namespace CppPluginFramework 167 | 168 | // ------------------------------------------------------------------------------------------------- 169 | 170 | /*! 171 | * Global "equal to" operator for CppPluginFramework::PluginInstanceConfig 172 | * 173 | * \param left Instance config 174 | * \param right Instance config 175 | * 176 | * \retval true Instance configs are equal 177 | * \retval false Instance configs are not equal 178 | */ 179 | CPPPLUGINFRAMEWORK_EXPORT bool operator==(const CppPluginFramework::PluginInstanceConfig &left, 180 | const CppPluginFramework::PluginInstanceConfig &right); 181 | 182 | // ------------------------------------------------------------------------------------------------- 183 | 184 | /*! 185 | * Global "not equal to" operator for CppPluginFramework::PluginInstanceConfig 186 | * 187 | * \param left Instance config 188 | * \param right Instance config 189 | * 190 | * \retval true Instance configs are not equal 191 | * \retval false Instance configs are equal 192 | */ 193 | CPPPLUGINFRAMEWORK_EXPORT bool operator!=(const CppPluginFramework::PluginInstanceConfig &left, 194 | const CppPluginFramework::PluginInstanceConfig &right); 195 | -------------------------------------------------------------------------------- /code/inc/CppPluginFramework/PluginManager.hpp: -------------------------------------------------------------------------------- 1 | /* This file is part of C++ Plugin Framework. 2 | * 3 | * C++ Plugin Framework is free software: you can redistribute it and/or modify it under the terms 4 | * of the GNU Lesser General Public License as published by the Free Software Foundation, either 5 | * version 3 of the License, or (at your option) any later version. 6 | * 7 | * C++ Plugin Framework is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 8 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | * GNU Lesser General Public License for more details. 10 | * 11 | * You should have received a copy of the GNU Lesser General Public License along with C++ Plugin 12 | * Framework. If not, see . 13 | */ 14 | 15 | /*! 16 | * \file 17 | * 18 | * Contains a plugin management class 19 | */ 20 | 21 | #pragma once 22 | 23 | // C++ Plugin Framework includes 24 | #include 25 | #include 26 | 27 | // Qt includes 28 | 29 | // System includes 30 | 31 | // Forward declarations 32 | 33 | // Macros 34 | 35 | // ------------------------------------------------------------------------------------------------- 36 | 37 | namespace CppPluginFramework 38 | { 39 | 40 | //! This class manages plugins 41 | class CPPPLUGINFRAMEWORK_EXPORT PluginManager 42 | { 43 | public: 44 | //! Destructor 45 | ~PluginManager(); 46 | 47 | /*! 48 | * Loads all plugin instances specified in the config 49 | * 50 | * \param pluginManagerConfig Plugin manager configs 51 | * 52 | * \retval true Success 53 | * \retval false Failure 54 | * 55 | * After each plugin is loaded all of its instances get created and configured. When all the 56 | * plugins are loaded the dependencies of each plugin instance are injected into it. 57 | */ 58 | bool load(const PluginManagerConfig &pluginManagerConfig); 59 | 60 | /*! 61 | * Unloads all loaded plugin instances 62 | * 63 | * \retval true Success 64 | * \retval false Failure 65 | */ 66 | bool unload(); 67 | 68 | /*! 69 | * Starts all loaded plugin instances 70 | * 71 | * \retval true Success 72 | * \retval false Failure 73 | */ 74 | bool start(); 75 | 76 | //! Stops all loaded plugin instances 77 | void stop(); 78 | 79 | /*! 80 | * Checks if a plugin instance with the specified name was loaded 81 | * 82 | * \param instanceName Plugin instance name 83 | * 84 | * \retval true Plugin was loaded 85 | * \retval false Plugin was not loaded 86 | */ 87 | bool hasPluginInstance(const QString &instanceName) const; 88 | 89 | /*! 90 | * Gets the specified plugin instance 91 | * 92 | * \param instanceName Plugin instance name 93 | * 94 | * \return Plugin instance or nullptr if there is no plugin instance with that name 95 | */ 96 | IPlugin *pluginInstance(const QString &instanceName); 97 | 98 | /*! 99 | * Gets names of all loaded plugin instances 100 | * 101 | * \return Names of all loaded plugin instances 102 | */ 103 | QStringList pluginInstanceNames() const; 104 | 105 | private: 106 | /*! 107 | * Injects dependencies to all specified plugins 108 | * 109 | * \param pluginConfigs List of plugin configs 110 | * 111 | * \retval true All dependencies were injected 112 | * \retval false Injection of at least one dependency failed 113 | */ 114 | bool injectAllDependencies(const QList &pluginConfigs); 115 | 116 | /*! 117 | * Injects dependencies to the specified instance 118 | * 119 | * \param instanceName Name of the plugin instance to inject dependencies to 120 | * \param dependencies Names of the needed dependencies to inject 121 | * 122 | * \retval true Success 123 | * \retval false Failure 124 | */ 125 | bool injectDependencies(const QString &instanceName, const QSet &dependencies); 126 | 127 | /*! 128 | * Ejects all injected dependencies 129 | * 130 | * \retval true Success 131 | * \retval false Failure (at least one plugin instance was still running) 132 | */ 133 | bool ejectDependencies(); 134 | 135 | private: 136 | //! Holds all of the loaded plugins 137 | std::map> m_pluginInstances; 138 | 139 | //! Holds the order in which the plugin instances will be started 140 | QStringList m_pluginStartupOrder; 141 | }; 142 | 143 | } // namespace CppPluginFramework 144 | -------------------------------------------------------------------------------- /code/inc/CppPluginFramework/PluginManagerConfig.hpp: -------------------------------------------------------------------------------- 1 | /* This file is part of C++ Plugin Framework. 2 | * 3 | * C++ Plugin Framework is free software: you can redistribute it and/or modify it under the terms 4 | * of the GNU Lesser General Public License as published by the Free Software Foundation, either 5 | * version 3 of the License, or (at your option) any later version. 6 | * 7 | * C++ Plugin Framework is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 8 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | * GNU Lesser General Public License for more details. 10 | * 11 | * You should have received a copy of the GNU Lesser General Public License along with C++ Plugin 12 | * Framework. If not, see . 13 | */ 14 | 15 | /*! 16 | * \file 17 | * 18 | * Contains a config class for the Plugin Manager 19 | */ 20 | 21 | #pragma once 22 | 23 | // C++ Plugin Framework includes 24 | #include 25 | 26 | // Qt includes 27 | 28 | // System includes 29 | 30 | // Forward declarations 31 | 32 | // Macros 33 | 34 | // ------------------------------------------------------------------------------------------------- 35 | 36 | namespace CppPluginFramework 37 | { 38 | 39 | //! Config class for the Plugin Manager 40 | class CPPPLUGINFRAMEWORK_EXPORT PluginManagerConfig : public CppConfigFramework::ConfigItem 41 | { 42 | public: 43 | //! Constructor 44 | PluginManagerConfig() = default; 45 | 46 | /*! 47 | * Copy constructor 48 | * 49 | * \param other Instance to copy 50 | */ 51 | PluginManagerConfig(const PluginManagerConfig &other) = default; 52 | 53 | /*! 54 | * Move constructor 55 | * 56 | * \param other Instance to move 57 | */ 58 | PluginManagerConfig(PluginManagerConfig &&other) noexcept = default; 59 | 60 | //! Destructor 61 | ~PluginManagerConfig() override = default; 62 | 63 | /*! 64 | * Copy assignment operator 65 | * 66 | * \param other Instance to copy assign 67 | * 68 | * \return Reference to this instance after the assignment is made 69 | */ 70 | PluginManagerConfig &operator=(const PluginManagerConfig &other) = default; 71 | 72 | /*! 73 | * Move assignment operator 74 | * 75 | * \param other Instance to move assign 76 | * 77 | * \return Reference to this instance after the assignment is made 78 | */ 79 | PluginManagerConfig &operator=(PluginManagerConfig &&other) noexcept = default; 80 | 81 | /*! 82 | * Checks if plugin config is valid 83 | * 84 | * \retval true Plugin config is valid 85 | * \retval false Plugin config is not valid 86 | */ 87 | bool isValid() const; 88 | 89 | /*! 90 | * Gets plugin configs that were read from the config file 91 | * 92 | * \return Plugin configs 93 | */ 94 | const QList &pluginConfigs() const; 95 | 96 | /*! 97 | * Sets list of plugin configs 98 | * 99 | * \param pluginConfigs List of plugin configs 100 | */ 101 | void setPluginConfigs(const QList &pluginConfigs); 102 | 103 | /*! 104 | * Gets priorities for starting plugin instances 105 | * 106 | * \return Plugin configs 107 | */ 108 | const QStringList &pluginStartupPriorities() const; 109 | 110 | /*! 111 | * Sets priorities for starting plugin instances 112 | * 113 | * \param startupPriorities Priorities for starting plugin instances 114 | */ 115 | void setPluginStartupPriorities(const QStringList &startupPriorities); 116 | 117 | private: 118 | //! \copydoc CppConfigFramework::ConfigItem::loadConfigParameters() 119 | bool loadConfigParameters(const CppConfigFramework::ConfigObjectNode &config) override; 120 | 121 | //! \copydoc CppConfigFramework::ConfigItem::storeConfigParameters() 122 | bool storeConfigParameters(CppConfigFramework::ConfigObjectNode *config) override; 123 | 124 | //! \copydoc CppConfigFramework::ConfigItem::validateConfig() 125 | QString validateConfig() const override; 126 | 127 | private: 128 | //! Holds the plugin configs 129 | QList m_pluginConfigs; 130 | 131 | //! Holds the optional order in which the plugin instances need to be started; all unreferenced 132 | //! plugin instances will be started in no particular order 133 | QStringList m_pluginStartupPriorities; 134 | }; 135 | 136 | } // namespace CppPluginFramework 137 | 138 | // ------------------------------------------------------------------------------------------------- 139 | 140 | /*! 141 | * Global "equal to" operator for CppPluginFramework::PluginManagerConfig 142 | * 143 | * \param left Plugin config 144 | * \param right Plugin config 145 | * 146 | * \retval true Plugin configs are equal 147 | * \retval false Plugin configs are not equal 148 | */ 149 | CPPPLUGINFRAMEWORK_EXPORT bool operator==(const CppPluginFramework::PluginManagerConfig &left, 150 | const CppPluginFramework::PluginManagerConfig &right); 151 | 152 | // ------------------------------------------------------------------------------------------------- 153 | 154 | /*! 155 | * Global "not equal to" operator for CppPluginFramework::PluginManagerConfig 156 | * 157 | * \param left Plugin config 158 | * \param right Plugin config 159 | * 160 | * \retval true Plugin configs are not equal 161 | * \retval false Plugin configs are equal 162 | */ 163 | CPPPLUGINFRAMEWORK_EXPORT bool operator!=(const CppPluginFramework::PluginManagerConfig &left, 164 | const CppPluginFramework::PluginManagerConfig &right); 165 | -------------------------------------------------------------------------------- /code/inc/CppPluginFramework/Validation.hpp: -------------------------------------------------------------------------------- 1 | /* This file is part of C++ Plugin Framework. 2 | * 3 | * C++ Plugin Framework is free software: you can redistribute it and/or modify it under the terms 4 | * of the GNU Lesser General Public License as published by the Free Software Foundation, either 5 | * version 3 of the License, or (at your option) any later version. 6 | * 7 | * C++ Plugin Framework is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 8 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | * GNU Lesser General Public License for more details. 10 | * 11 | * You should have received a copy of the GNU Lesser General Public License along with C++ Plugin 12 | * Framework. If not, see . 13 | */ 14 | 15 | /*! 16 | * \file 17 | * 18 | * Contains validation methods 19 | */ 20 | 21 | #pragma once 22 | 23 | // C++ Plugin Framework includes 24 | #include 25 | 26 | // Qt includes 27 | #include 28 | 29 | // System includes 30 | 31 | // Forward declarations 32 | 33 | // Macros 34 | 35 | // ------------------------------------------------------------------------------------------------- 36 | 37 | namespace CppPluginFramework 38 | { 39 | 40 | namespace Validation 41 | { 42 | 43 | /*! 44 | * Validates plugin's instance name 45 | * 46 | * \param name Plugin's instance name 47 | * 48 | * \retval true Plugin's instance name is valid 49 | * \retval false Plugin's instance name is not valid 50 | */ 51 | CPPPLUGINFRAMEWORK_EXPORT bool validatePluginInstanceName(const QString &name); 52 | 53 | /*! 54 | * Validates plugin's interface name 55 | * 56 | * \param name Plugin's interface name 57 | * 58 | * \retval true Plugin's interface name is valid 59 | * \retval false Plugin's interface name is not valid 60 | */ 61 | CPPPLUGINFRAMEWORK_EXPORT bool validateInterfaceName(const QString &name); 62 | 63 | /*! 64 | * Validates plugin's exported interfaces 65 | * 66 | * \param exportedInterfaces Plugin's exported interface names 67 | * 68 | * \retval true Plugin's exported interfaces are valid 69 | * \retval false Plugin's exported interfaces are not valid 70 | * 71 | * This method checks if at least one interface is exported and if the interface names are valid. 72 | */ 73 | CPPPLUGINFRAMEWORK_EXPORT bool validateExportedInterfaces(const QSet &exportedInterfaces); 74 | 75 | /*! 76 | * Validates the environment variable name 77 | * 78 | * \param name Environment variable name 79 | * 80 | * \retval true Valid 81 | * \retval false Invalid 82 | */ 83 | CPPPLUGINFRAMEWORK_EXPORT bool validateEnvironmentVariableName(const QString &name); 84 | 85 | /*! 86 | * Validates file path 87 | * 88 | * \param filePath File path to validate 89 | * 90 | * \retval true Valid 91 | * \retval false Invalid 92 | */ 93 | CPPPLUGINFRAMEWORK_EXPORT bool validateFilePath(const QString &filePath); 94 | 95 | } // namespace Validation 96 | 97 | } // namespace CppPluginFramework 98 | -------------------------------------------------------------------------------- /code/inc/CppPluginFramework/VersionInfo.hpp: -------------------------------------------------------------------------------- 1 | /* This file is part of C++ Plugin Framework. 2 | * 3 | * C++ Plugin Framework is free software: you can redistribute it and/or modify it under the terms 4 | * of the GNU Lesser General Public License as published by the Free Software Foundation, either 5 | * version 3 of the License, or (at your option) any later version. 6 | * 7 | * C++ Plugin Framework is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 8 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | * GNU Lesser General Public License for more details. 10 | * 11 | * You should have received a copy of the GNU Lesser General Public License along with C++ Plugin 12 | * Framework. If not, see . 13 | */ 14 | 15 | /*! 16 | * \file 17 | * 18 | * Contains a class that holds version information 19 | */ 20 | 21 | #pragma once 22 | 23 | // C++ Plugin Framework includes 24 | #include 25 | 26 | // C++ Config Framework includes 27 | 28 | // Cedar Framework includes 29 | #include 30 | 31 | // Qt includes 32 | 33 | // System includes 34 | 35 | // Forward declarations 36 | 37 | // Macros 38 | 39 | // ------------------------------------------------------------------------------------------------- 40 | 41 | namespace CppPluginFramework 42 | { 43 | 44 | /*! 45 | * This class holds version information 46 | */ 47 | class CPPPLUGINFRAMEWORK_EXPORT VersionInfo 48 | { 49 | public: 50 | //! Constructor 51 | VersionInfo(); 52 | 53 | /*! 54 | * Constructor 55 | * 56 | * \param major Major version number 57 | * \param minor Minor version number 58 | * \param patch Patch version number 59 | * \param dev Optional development version string 60 | */ 61 | VersionInfo(const int major, const int minor, const int patch, const QString &dev = {}); 62 | 63 | /*! 64 | * Constructor 65 | * 66 | * \param version String representation of the version 67 | */ 68 | VersionInfo(const QString &version); 69 | 70 | /*! 71 | * Checks if version information is null (default constructed value) 72 | * 73 | * \retval true Null 74 | * \retval false Not null 75 | */ 76 | bool isNull() const; 77 | 78 | /*! 79 | * Checks if version information is valid 80 | * 81 | * \retval true Valid 82 | * \retval false Invalid 83 | */ 84 | bool isValid() const; 85 | 86 | /*! 87 | * Returns major version number 88 | * 89 | * \return Major version number 90 | */ 91 | int major() const; 92 | 93 | /*! 94 | * Sets major version number 95 | * 96 | * \param major Major version number 97 | */ 98 | void setMajor(const int &major); 99 | 100 | /*! 101 | * Returns minor version number 102 | * 103 | * \return Minor version number 104 | */ 105 | int minor() const; 106 | 107 | /*! 108 | * Sets minor version number 109 | * 110 | * \param minor Minor version number 111 | */ 112 | void setMinor(const int &minor); 113 | 114 | /*! 115 | * Returns patch version number 116 | * 117 | * \return Patch version number 118 | */ 119 | int patch() const; 120 | 121 | /*! 122 | * Sets patch version number 123 | * 124 | * \param patch Patch version number 125 | */ 126 | void setPatch(const int &patch); 127 | 128 | /*! 129 | * Returns development version string 130 | * 131 | * \return Development version string 132 | */ 133 | QString dev() const; 134 | 135 | /*! 136 | * Sets development version string 137 | * 138 | * \param dev Development version string 139 | */ 140 | void setDev(const QString &dev); 141 | 142 | /*! 143 | * Generates a string representation of the version 144 | * 145 | * \return String representation of the version 146 | */ 147 | QString toString() const; 148 | 149 | /*! 150 | * Checks if version range is valid 151 | * 152 | * \param minVersion Minimum version 153 | * \param maxVersion Maximum version 154 | * 155 | * \retval true Valid 156 | * \retval false Invalid 157 | */ 158 | static bool isRangeValid(const VersionInfo &minVersion, const VersionInfo &maxVersion); 159 | 160 | /*! 161 | * Checks if specified version is in version range 162 | * 163 | * \param version Version to check 164 | * \param minVersion Minimum version 165 | * \param maxVersion Maximum version 166 | * 167 | * \retval true Version is in range 168 | * \retval false Version is in not range 169 | */ 170 | static bool isVersionInRange(const VersionInfo &version, 171 | const VersionInfo &minVersion, 172 | const VersionInfo &maxVersion); 173 | 174 | private: 175 | //! Major version number 176 | int m_major; 177 | 178 | //! Minor version number 179 | int m_minor; 180 | 181 | //! Patch version number 182 | int m_patch; 183 | 184 | //! Development version number 185 | QString m_dev; 186 | }; 187 | 188 | } // namespace CppPluginFramework 189 | 190 | // ------------------------------------------------------------------------------------------------- 191 | 192 | /*! 193 | * Global "equal to" operator for CppPluginFramework::VersionInfo 194 | * 195 | * \param left Version 196 | * \param right Version 197 | * 198 | * \retval true Versions are equal 199 | * \retval false Versions are not equal 200 | * 201 | * \note Result of the comparison is valid only if both versions are valid! 202 | */ 203 | CPPPLUGINFRAMEWORK_EXPORT bool operator==(const CppPluginFramework::VersionInfo &left, 204 | const CppPluginFramework::VersionInfo &right); 205 | 206 | // ------------------------------------------------------------------------------------------------- 207 | 208 | /*! 209 | * Global "not equal to" operator for CppPluginFramework::VersionInfo 210 | * 211 | * \param left Version 212 | * \param right Version 213 | * 214 | * \retval true Versions are not equal 215 | * \retval false Versions are equal 216 | * 217 | * \note Result of the comparison is valid only if both versions are valid! 218 | */ 219 | CPPPLUGINFRAMEWORK_EXPORT bool operator!=(const CppPluginFramework::VersionInfo &left, 220 | const CppPluginFramework::VersionInfo &right); 221 | 222 | // ------------------------------------------------------------------------------------------------- 223 | 224 | /*! 225 | * Global "less than" operator for CppPluginFramework::VersionInfo 226 | * 227 | * \param left Version 228 | * \param right Version 229 | * 230 | * \retval true Version "left" is smaller than version "right" 231 | * \retval false Version "left" is not smaller than version "right" 232 | * 233 | * \note Result of the comparison is valid only if both versions are valid! 234 | */ 235 | CPPPLUGINFRAMEWORK_EXPORT bool operator<(const CppPluginFramework::VersionInfo &left, 236 | const CppPluginFramework::VersionInfo &right); 237 | 238 | // ------------------------------------------------------------------------------------------------- 239 | 240 | /*! 241 | * Global "less than or equal to" operator for CppPluginFramework::VersionInfo 242 | * 243 | * \param left Version 244 | * \param right Version 245 | * 246 | * \retval true Version "left" is less than or equal to version "right" 247 | * \retval false Version "left" is not less than or equal to version "right" 248 | * 249 | * \note Result of the comparison is valid only if both versions are valid! 250 | */ 251 | CPPPLUGINFRAMEWORK_EXPORT bool operator<=(const CppPluginFramework::VersionInfo &left, 252 | const CppPluginFramework::VersionInfo &right); 253 | 254 | // ------------------------------------------------------------------------------------------------- 255 | 256 | /*! 257 | * Global "greater than" operator for CppPluginFramework::VersionInfo 258 | * 259 | * \param left Version 260 | * \param right Version 261 | * 262 | * \retval true Version "left" is greater than version "right" 263 | * \retval false Version "left" is not greater than version "right" 264 | * 265 | * \note Result of the comparison is valid only if both versions are valid! 266 | */ 267 | CPPPLUGINFRAMEWORK_EXPORT bool operator>(const CppPluginFramework::VersionInfo &left, 268 | const CppPluginFramework::VersionInfo &right); 269 | 270 | // ------------------------------------------------------------------------------------------------- 271 | 272 | /*! 273 | * Global "greater than or equal to" operator for CppPluginFramework::VersionInfo 274 | * 275 | * \param left Version 276 | * \param right Version 277 | * 278 | * \retval true Version "left" is greater than or equal to version "right" 279 | * \retval false Version "left" is not greater than or equal to version "right" 280 | * 281 | * \note Result of the comparison is valid only if both versions are valid! 282 | */ 283 | CPPPLUGINFRAMEWORK_EXPORT bool operator>=(const CppPluginFramework::VersionInfo &left, 284 | const CppPluginFramework::VersionInfo &right); 285 | 286 | // ------------------------------------------------------------------------------------------------- 287 | 288 | namespace CedarFramework 289 | { 290 | 291 | //! \copydoc CedarFramework::serialize() 292 | template<> 293 | CPPPLUGINFRAMEWORK_EXPORT bool deserialize(const QJsonValue &json, 294 | CppPluginFramework::VersionInfo *value); 295 | 296 | } 297 | -------------------------------------------------------------------------------- /code/src/AbstractPlugin.cpp: -------------------------------------------------------------------------------- 1 | /* This file is part of C++ Plugin Framework. 2 | * 3 | * C++ Plugin Framework is free software: you can redistribute it and/or modify it under the terms 4 | * of the GNU Lesser General Public License as published by the Free Software Foundation, either 5 | * version 3 of the License, or (at your option) any later version. 6 | * 7 | * C++ Plugin Framework is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 8 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | * GNU Lesser General Public License for more details. 10 | * 11 | * You should have received a copy of the GNU Lesser General Public License along with C++ Plugin 12 | * Framework. If not, see . 13 | */ 14 | 15 | /*! 16 | * \file 17 | * 18 | * Contains a convenience base class for creating plugins 19 | */ 20 | 21 | // Own header 22 | #include 23 | 24 | // C++ Plugin Framework includes 25 | 26 | // Qt includes 27 | 28 | // System includes 29 | 30 | // Forward declarations 31 | 32 | // Macros 33 | 34 | // ------------------------------------------------------------------------------------------------- 35 | 36 | namespace CppPluginFramework 37 | { 38 | 39 | AbstractPlugin::AbstractPlugin(const QString &name, 40 | const VersionInfo &version, 41 | const QString &description, 42 | const QSet &exportedInterfaces) 43 | : m_name(name), 44 | m_version(version), 45 | m_description(description), 46 | m_exportedInterfaces(exportedInterfaces), 47 | m_started(false) 48 | { 49 | } 50 | 51 | // ------------------------------------------------------------------------------------------------- 52 | 53 | QString AbstractPlugin::name() const 54 | { 55 | QMutexLocker locker(&m_mutex); 56 | return m_name; 57 | } 58 | 59 | // ------------------------------------------------------------------------------------------------- 60 | 61 | QString AbstractPlugin::description() const 62 | { 63 | QMutexLocker locker(&m_mutex); 64 | 65 | return m_description; 66 | } 67 | 68 | // ------------------------------------------------------------------------------------------------- 69 | 70 | VersionInfo AbstractPlugin::version() const 71 | { 72 | QMutexLocker locker(&m_mutex); 73 | return m_version; 74 | } 75 | 76 | // ------------------------------------------------------------------------------------------------- 77 | 78 | bool AbstractPlugin::isInterfaceExported(const QString &interface) const 79 | { 80 | QMutexLocker locker(&m_mutex); 81 | return m_exportedInterfaces.contains(interface); 82 | } 83 | 84 | // ------------------------------------------------------------------------------------------------- 85 | 86 | QSet AbstractPlugin::exportedInterfaces() const 87 | { 88 | QMutexLocker locker(&m_mutex); 89 | return m_exportedInterfaces; 90 | } 91 | 92 | // ------------------------------------------------------------------------------------------------- 93 | 94 | bool AbstractPlugin::isStarted() const 95 | { 96 | QMutexLocker locker(&m_mutex); 97 | return m_started; 98 | } 99 | 100 | // ------------------------------------------------------------------------------------------------- 101 | 102 | bool AbstractPlugin::start() 103 | { 104 | // Check if plugin is already started 105 | if (isStarted()) 106 | { 107 | // Error, plugin is already started 108 | return false; 109 | } 110 | 111 | // Start the plugin 112 | const bool success = onStart(); 113 | 114 | QMutexLocker locker(&m_mutex); 115 | m_started = success; 116 | return success; 117 | } 118 | 119 | // ------------------------------------------------------------------------------------------------- 120 | 121 | void AbstractPlugin::stop() 122 | { 123 | // Check if plugin is actually started 124 | if (!isStarted()) 125 | { 126 | // Plugin is already stopped 127 | return; 128 | } 129 | 130 | // Stop the plugin 131 | onStop(); 132 | 133 | QMutexLocker locker(&m_mutex); 134 | m_started = false; 135 | } 136 | 137 | // ------------------------------------------------------------------------------------------------- 138 | 139 | bool AbstractPlugin::onStart() 140 | { 141 | return true; 142 | } 143 | 144 | // ------------------------------------------------------------------------------------------------- 145 | 146 | void AbstractPlugin::onStop() 147 | { 148 | } 149 | 150 | } // namespace CppPluginFramework 151 | -------------------------------------------------------------------------------- /code/src/LoggingCategories.cpp: -------------------------------------------------------------------------------- 1 | /* This file is part of C++ Plugin Framework. 2 | * 3 | * C++ Plugin Framework is free software: you can redistribute it and/or modify it under the terms 4 | * of the GNU Lesser General Public License as published by the Free Software Foundation, either 5 | * version 3 of the License, or (at your option) any later version. 6 | * 7 | * C++ Plugin Framework is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 8 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | * GNU Lesser General Public License for more details. 10 | * 11 | * You should have received a copy of the GNU Lesser General Public License along with C++ Plugin 12 | * Framework. If not, see . 13 | */ 14 | 15 | /*! 16 | * \file 17 | * 18 | * Contains logging categories 19 | */ 20 | 21 | // Own header 22 | #include 23 | 24 | // C++ Plugin Framework includes 25 | 26 | // Qt includes 27 | 28 | // System includes 29 | 30 | // Forward declarations 31 | 32 | // Macros 33 | 34 | // ------------------------------------------------------------------------------------------------- 35 | 36 | namespace CppPluginFramework 37 | { 38 | 39 | namespace LoggingCategory 40 | { 41 | 42 | const QLoggingCategory Config("CppPluginFramework.Config"); 43 | const QLoggingCategory Plugin("CppPluginFramework.Plugin"); 44 | const QLoggingCategory PluginManager("CppPluginFramework.PluginManager"); 45 | 46 | } // namespace LoggingCategory 47 | 48 | } // namespace CppPluginFramework 49 | -------------------------------------------------------------------------------- /code/src/Plugin.cpp: -------------------------------------------------------------------------------- 1 | /* This file is part of C++ Plugin Framework. 2 | * 3 | * C++ Plugin Framework is free software: you can redistribute it and/or modify it under the terms 4 | * of the GNU Lesser General Public License as published by the Free Software Foundation, either 5 | * version 3 of the License, or (at your option) any later version. 6 | * 7 | * C++ Plugin Framework is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 8 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | * GNU Lesser General Public License for more details. 10 | * 11 | * You should have received a copy of the GNU Lesser General Public License along with C++ Plugin 12 | * Framework. If not, see . 13 | */ 14 | 15 | /*! 16 | * \file 17 | * 18 | * Contains a class that is able to load a plugin from a library 19 | */ 20 | 21 | // Own header 22 | #include 23 | 24 | // C++ Plugin Framework includes 25 | #include 26 | #include 27 | #include 28 | 29 | // Qt includes 30 | #include 31 | #include 32 | 33 | // System includes 34 | 35 | // Forward declarations 36 | 37 | // Macros 38 | 39 | // ------------------------------------------------------------------------------------------------- 40 | 41 | namespace CppPluginFramework 42 | { 43 | 44 | std::vector> Plugin::loadInstances(const PluginConfig &pluginConfig) 45 | { 46 | // Check plugin config 47 | if (!pluginConfig.isValid()) 48 | { 49 | qCWarning(CppPluginFramework::LoggingCategory::Plugin) << "Plugin config is not valid!"; 50 | return {}; 51 | } 52 | 53 | // Load plugin from the library and extract the plugin factory interface from it 54 | QPluginLoader loader(pluginConfig.filePath()); 55 | auto *loaderInstance = loader.instance(); 56 | 57 | if (loaderInstance == nullptr) 58 | { 59 | qCWarning(CppPluginFramework::LoggingCategory::Plugin) 60 | << "Failed to load plugin:" << loader.fileName(); 61 | return {}; 62 | } 63 | 64 | auto *pluginFactory = qobject_cast(loaderInstance); 65 | 66 | if (pluginFactory == nullptr) 67 | { 68 | qCWarning(CppPluginFramework::LoggingCategory::Plugin) 69 | << QString("Loaded plugin [%1] does not implement the plugin factory interface!") 70 | .arg(loader.fileName()); 71 | return {}; 72 | } 73 | 74 | // Create plugin instances 75 | std::vector> instances; 76 | 77 | for (const PluginInstanceConfig &instanceConfig : pluginConfig.instanceConfigs()) 78 | { 79 | // Create plugin instance 80 | auto instance = loadInstance(*pluginFactory, instanceConfig); 81 | 82 | if (!instance) 83 | { 84 | qCWarning(CppPluginFramework::LoggingCategory::Plugin) 85 | << QString("Failed to load the plugin instance [%1] from the plugin [%2]!") 86 | .arg(instanceConfig.name(), loader.fileName()); 87 | return {}; 88 | } 89 | 90 | // Check plugin's version 91 | if (!checkVersion(instance->version(), pluginConfig)) 92 | { 93 | qCWarning(CppPluginFramework::LoggingCategory::Plugin) 94 | << QString("Plugin instance [%1] from the plugin [%2] has an unsupported " 95 | "version!").arg(instanceConfig.name(), loader.fileName()); 96 | return {}; 97 | } 98 | 99 | // Add the instance to the container of loaded instances 100 | instances.push_back(std::move(instance)); 101 | } 102 | 103 | return instances; 104 | } 105 | 106 | // ------------------------------------------------------------------------------------------------- 107 | 108 | std::unique_ptr Plugin::loadInstance(const IPluginFactory &pluginFactory, 109 | const PluginInstanceConfig &instanceConfig) 110 | { 111 | // Create plugin instance 112 | auto instance = pluginFactory.createInstance(instanceConfig.name()); 113 | 114 | if (!instance) 115 | { 116 | qCWarning(CppPluginFramework::LoggingCategory::Plugin) 117 | << "Failed to create the plugin instance!"; 118 | return {}; 119 | } 120 | 121 | // Configure the plugin instance 122 | if (!instance->loadConfig(instanceConfig.config())) 123 | { 124 | qCWarning(CppPluginFramework::LoggingCategory::Plugin) 125 | << "Failed to load the plugin instance's configuration!"; 126 | return {}; 127 | } 128 | 129 | return instance; 130 | } 131 | 132 | // ------------------------------------------------------------------------------------------------- 133 | 134 | bool Plugin::checkVersion(const VersionInfo &pluginVersion, const PluginConfig &pluginConfig) 135 | { 136 | if (pluginConfig.isExactVersion()) 137 | { 138 | // Plugin's version must match the exact version 139 | if (pluginVersion != pluginConfig.version()) 140 | { 141 | qCWarning(CppPluginFramework::LoggingCategory::Plugin) 142 | << QString("Loaded plugin's version [%1] does not match the expected version " 143 | "[%2]!").arg(pluginVersion.toString(), 144 | pluginConfig.version().toString()); 145 | return false; 146 | } 147 | } 148 | else 149 | { 150 | // Plugin's version must be within the version range 151 | if (!VersionInfo::isVersionInRange(pluginVersion, 152 | pluginConfig.minVersion(), 153 | pluginConfig.maxVersion())) 154 | { 155 | qCWarning(CppPluginFramework::LoggingCategory::Plugin) 156 | << QString("Loaded plugin's version [%1] does not match the expected version " 157 | "range: min=[%2], max=[%3]!") 158 | .arg(pluginVersion.toString(), 159 | pluginConfig.minVersion().toString(), 160 | pluginConfig.maxVersion().toString()); 161 | return false; 162 | } 163 | } 164 | 165 | return true; 166 | } 167 | 168 | } // namespace CppPluginFramework 169 | -------------------------------------------------------------------------------- /code/src/PluginConfig.cpp: -------------------------------------------------------------------------------- 1 | /* This file is part of C++ Plugin Framework. 2 | * 3 | * C++ Plugin Framework is free software: you can redistribute it and/or modify it under the terms 4 | * of the GNU Lesser General Public License as published by the Free Software Foundation, either 5 | * version 3 of the License, or (at your option) any later version. 6 | * 7 | * C++ Plugin Framework is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 8 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | * GNU Lesser General Public License for more details. 10 | * 11 | * You should have received a copy of the GNU Lesser General Public License along with C++ Plugin 12 | * Framework. If not, see . 13 | */ 14 | 15 | /*! 16 | * \file 17 | * 18 | * Contains a config class for a Plugin 19 | */ 20 | 21 | // Own header 22 | #include 23 | 24 | // C++ Plugin Framework includes 25 | #include 26 | #include 27 | 28 | // Qt includes 29 | #include 30 | 31 | // System includes 32 | 33 | // Forward declarations 34 | 35 | // Macros 36 | 37 | // ------------------------------------------------------------------------------------------------- 38 | 39 | namespace CppPluginFramework 40 | { 41 | 42 | PluginConfig::PluginConfig(const QString &filePath, 43 | const VersionInfo &version, 44 | const QList &instanceConfigs) 45 | : m_filePath(filePath), 46 | m_version(version), 47 | m_instanceConfigs(instanceConfigs) 48 | { 49 | } 50 | 51 | // ------------------------------------------------------------------------------------------------- 52 | 53 | PluginConfig::PluginConfig(const QString &filePath, 54 | const VersionInfo &minVersion, 55 | const VersionInfo &maxVersion, 56 | const QList &instanceConfigs) 57 | : m_filePath(filePath), 58 | m_minVersion(minVersion), 59 | m_maxVersion(maxVersion), 60 | m_instanceConfigs(instanceConfigs) 61 | { 62 | } 63 | 64 | // ------------------------------------------------------------------------------------------------- 65 | 66 | bool PluginConfig::isValid() const 67 | { 68 | return validateConfig().isEmpty(); 69 | } 70 | 71 | // ------------------------------------------------------------------------------------------------- 72 | 73 | bool PluginConfig::isExactVersion() const 74 | { 75 | return ((!m_version.isNull()) && m_minVersion.isNull() && m_maxVersion.isNull()); 76 | } 77 | 78 | // ------------------------------------------------------------------------------------------------- 79 | 80 | bool PluginConfig::isVersionRange() const 81 | { 82 | return (m_version.isNull() && (!m_minVersion.isNull()) && (!m_maxVersion.isNull())); 83 | } 84 | 85 | // ------------------------------------------------------------------------------------------------- 86 | 87 | QString PluginConfig::filePath() const 88 | { 89 | return m_filePath; 90 | } 91 | 92 | // ------------------------------------------------------------------------------------------------- 93 | 94 | void PluginConfig::setFilePath(const QString &filePath) 95 | { 96 | m_filePath = filePath; 97 | } 98 | 99 | // ------------------------------------------------------------------------------------------------- 100 | 101 | VersionInfo PluginConfig::version() const 102 | { 103 | return m_version; 104 | } 105 | 106 | // ------------------------------------------------------------------------------------------------- 107 | 108 | void PluginConfig::setVersion(const VersionInfo &version) 109 | { 110 | m_version = version; 111 | } 112 | 113 | // ------------------------------------------------------------------------------------------------- 114 | 115 | VersionInfo PluginConfig::minVersion() const 116 | { 117 | return m_minVersion; 118 | } 119 | 120 | // ------------------------------------------------------------------------------------------------- 121 | 122 | void PluginConfig::setMinVersion(const VersionInfo &minVersion) 123 | { 124 | m_minVersion = minVersion; 125 | } 126 | 127 | // ------------------------------------------------------------------------------------------------- 128 | 129 | VersionInfo PluginConfig::maxVersion() const 130 | { 131 | return m_maxVersion; 132 | } 133 | 134 | // ------------------------------------------------------------------------------------------------- 135 | 136 | void PluginConfig::setMaxVersion(const VersionInfo &maxVersion) 137 | { 138 | m_maxVersion = maxVersion; 139 | } 140 | 141 | // ------------------------------------------------------------------------------------------------- 142 | 143 | const QList &PluginConfig::instanceConfigs() const 144 | { 145 | return m_instanceConfigs; 146 | } 147 | 148 | // ------------------------------------------------------------------------------------------------- 149 | 150 | void PluginConfig::setInstanceConfigs(const QList &instanceConfigs) 151 | { 152 | m_instanceConfigs = instanceConfigs; 153 | } 154 | 155 | // ------------------------------------------------------------------------------------------------- 156 | 157 | bool PluginConfig::loadConfigParameters(const CppConfigFramework::ConfigObjectNode &config) 158 | { 159 | // Load file path 160 | if (!loadRequiredConfigParameter(&m_filePath, QStringLiteral("file_path"), config)) 161 | { 162 | qCWarning(CppPluginFramework::LoggingCategory::Config) 163 | << "Failed to load plugin's file path!"; 164 | return false; 165 | } 166 | 167 | // Load version 168 | m_version = VersionInfo(); 169 | 170 | if (!loadOptionalConfigParameter(&m_version, QStringLiteral("version"), config)) 171 | { 172 | qCWarning(CppPluginFramework::LoggingCategory::Config) 173 | << "Failed to load plugin's version!"; 174 | return false; 175 | } 176 | 177 | // Load min version 178 | m_minVersion = VersionInfo(); 179 | 180 | if (!loadOptionalConfigParameter(&m_minVersion, QStringLiteral("min_version"), config)) 181 | { 182 | qCWarning(CppPluginFramework::LoggingCategory::Config) 183 | << "Failed to load plugin's min version!"; 184 | return false; 185 | } 186 | 187 | // Load max version 188 | m_maxVersion = VersionInfo(); 189 | 190 | if (!loadOptionalConfigParameter(&m_maxVersion, QStringLiteral("max_version"), config)) 191 | { 192 | qCWarning(CppPluginFramework::LoggingCategory::Config) 193 | << "Failed to load plugin's max version"; 194 | return false; 195 | } 196 | 197 | // Load instance configs 198 | if (!loadRequiredConfigContainer(&m_instanceConfigs, QStringLiteral("instances"), config)) 199 | { 200 | qCWarning(CppPluginFramework::LoggingCategory::Config) 201 | << "Failed to load plugin's instances!"; 202 | return false; 203 | } 204 | 205 | return true; 206 | } 207 | 208 | // ------------------------------------------------------------------------------------------------- 209 | 210 | bool PluginConfig::storeConfigParameters(CppConfigFramework::ConfigObjectNode *config) 211 | { 212 | // Storing config parameters is currently not supported 213 | Q_UNUSED(config) 214 | return false; 215 | } 216 | 217 | // ------------------------------------------------------------------------------------------------- 218 | 219 | QString PluginConfig::validateConfig() const 220 | { 221 | // Check file path 222 | if (!Validation::validateFilePath(m_filePath)) 223 | { 224 | return QStringLiteral("File path is not valid: ") % m_filePath; 225 | } 226 | 227 | // Check version info 228 | if (isExactVersion()) 229 | { 230 | if (!m_version.isValid()) 231 | { 232 | return QStringLiteral("Version is not valid"); 233 | } 234 | } 235 | else if (isVersionRange()) 236 | { 237 | if (!VersionInfo::isRangeValid(m_minVersion, m_maxVersion)) 238 | { 239 | return QStringLiteral("Version range is not valid"); 240 | } 241 | } 242 | else 243 | { 244 | return QStringLiteral("Either just the version parameter needs to be set or " 245 | "both min and max version parameters!"); 246 | } 247 | 248 | // At least one plugin instance is required 249 | if (m_instanceConfigs.isEmpty()) 250 | { 251 | return QStringLiteral("Plugin config does not define any plugin instances"); 252 | } 253 | 254 | // Check individual instances are valid and if this plugin config contains duplicate instances 255 | // (instances with the same name) 256 | QStringList instanceNames; 257 | 258 | for (const auto &instanceConfig : m_instanceConfigs) 259 | { 260 | if (!instanceConfig.isValid()) 261 | { 262 | return QStringLiteral("Plugin instance config is not valid: ") % instanceConfig.name(); 263 | } 264 | 265 | if (instanceNames.contains(instanceConfig.name())) 266 | { 267 | return QStringLiteral("Duplicate plugin instance name: ") % instanceConfig.name(); 268 | } 269 | 270 | instanceNames.append(instanceConfig.name()); 271 | } 272 | 273 | return QString(); 274 | } 275 | 276 | } // namespace CppPluginFramework 277 | 278 | // ------------------------------------------------------------------------------------------------- 279 | 280 | bool operator==(const CppPluginFramework::PluginConfig &left, 281 | const CppPluginFramework::PluginConfig &right) 282 | { 283 | if ((left.filePath() != right.filePath()) || 284 | (left.version() != right.version()) || 285 | (left.minVersion() != right.minVersion()) || 286 | (left.maxVersion() != right.maxVersion())) 287 | { 288 | return false; 289 | } 290 | 291 | if (left.instanceConfigs().size() != right.instanceConfigs().size()) 292 | { 293 | return false; 294 | } 295 | 296 | for (const auto &item : left.instanceConfigs()) 297 | { 298 | if (!right.instanceConfigs().contains(item)) 299 | { 300 | return false; 301 | } 302 | } 303 | 304 | return true; 305 | } 306 | 307 | // ------------------------------------------------------------------------------------------------- 308 | 309 | bool operator!=(const CppPluginFramework::PluginConfig &left, 310 | const CppPluginFramework::PluginConfig &right) 311 | { 312 | return !(left == right); 313 | } 314 | -------------------------------------------------------------------------------- /code/src/PluginInstanceConfig.cpp: -------------------------------------------------------------------------------- 1 | /* This file is part of C++ Plugin Framework. 2 | * 3 | * C++ Plugin Framework is free software: you can redistribute it and/or modify it under the terms 4 | * of the GNU Lesser General Public License as published by the Free Software Foundation, either 5 | * version 3 of the License, or (at your option) any later version. 6 | * 7 | * C++ Plugin Framework is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 8 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | * GNU Lesser General Public License for more details. 10 | * 11 | * You should have received a copy of the GNU Lesser General Public License along with C++ Plugin 12 | * Framework. If not, see . 13 | */ 14 | 15 | /*! 16 | * \file 17 | * 18 | * Contains a config class for a Plugin Instance 19 | */ 20 | 21 | // Own header 22 | #include 23 | 24 | // C++ Plugin Framework includes 25 | #include 26 | #include 27 | 28 | // Qt includes 29 | #include 30 | 31 | // System includes 32 | 33 | // Forward declarations 34 | 35 | // Macros 36 | 37 | // ------------------------------------------------------------------------------------------------- 38 | 39 | namespace CppPluginFramework 40 | { 41 | 42 | PluginInstanceConfig::PluginInstanceConfig(const QString &name, 43 | const CppConfigFramework::ConfigObjectNode &config, 44 | const QSet &dependencies) 45 | : m_name(name), 46 | m_config(std::move(config.clone()->toObject())), 47 | m_dependencies(dependencies) 48 | { 49 | } 50 | 51 | // ------------------------------------------------------------------------------------------------- 52 | 53 | PluginInstanceConfig::PluginInstanceConfig(const PluginInstanceConfig &other) 54 | : m_name(other.m_name), 55 | m_config(std::move(other.m_config.clone()->toObject())), 56 | m_dependencies(other.m_dependencies) 57 | { 58 | } 59 | 60 | // ------------------------------------------------------------------------------------------------- 61 | 62 | PluginInstanceConfig &PluginInstanceConfig::operator=(const PluginInstanceConfig &other) 63 | { 64 | if (this == &other) 65 | { 66 | return *this; 67 | } 68 | 69 | m_name = other.m_name; 70 | m_config = std::move(other.m_config.clone()->toObject()); 71 | m_dependencies = other.m_dependencies; 72 | return *this; 73 | } 74 | 75 | // ------------------------------------------------------------------------------------------------- 76 | 77 | bool PluginInstanceConfig::isValid() const 78 | { 79 | return validateConfig().isEmpty(); 80 | } 81 | 82 | // ------------------------------------------------------------------------------------------------- 83 | 84 | QString PluginInstanceConfig::name() const 85 | { 86 | return m_name; 87 | } 88 | 89 | // ------------------------------------------------------------------------------------------------- 90 | 91 | void PluginInstanceConfig::setName(const QString &name) 92 | { 93 | m_name = name; 94 | } 95 | 96 | // ------------------------------------------------------------------------------------------------- 97 | 98 | const CppConfigFramework::ConfigObjectNode &PluginInstanceConfig::config() const 99 | { 100 | return m_config; 101 | } 102 | 103 | // ------------------------------------------------------------------------------------------------- 104 | 105 | void PluginInstanceConfig::setConfig(const CppConfigFramework::ConfigObjectNode &config) 106 | { 107 | m_config = std::move(config.clone()->toObject()); 108 | } 109 | 110 | // ------------------------------------------------------------------------------------------------- 111 | 112 | QSet PluginInstanceConfig::dependencies() const 113 | { 114 | return m_dependencies; 115 | } 116 | 117 | // ------------------------------------------------------------------------------------------------- 118 | 119 | void PluginInstanceConfig::setDependencies(const QSet &dependencies) 120 | { 121 | m_dependencies = dependencies; 122 | } 123 | 124 | // ------------------------------------------------------------------------------------------------- 125 | 126 | bool PluginInstanceConfig::loadConfigParameters(const CppConfigFramework::ConfigObjectNode &config) 127 | { 128 | // Load name 129 | if (!loadRequiredConfigParameter(&m_name, QStringLiteral("name"), config)) 130 | { 131 | qCWarning(CppPluginFramework::LoggingCategory::Config) 132 | << "Failed to load plugin instance's name!"; 133 | return false; 134 | } 135 | 136 | // Load config 137 | const auto *configMember = config.member(QStringLiteral("config")); 138 | 139 | if (configMember == nullptr) 140 | { 141 | m_config = CppConfigFramework::ConfigObjectNode(); 142 | } 143 | else 144 | { 145 | if (!configMember->isObject()) 146 | { 147 | qCWarning(CppPluginFramework::LoggingCategory::Config) 148 | << "Plugin instance's config is not an Object! Type:" 149 | << CppConfigFramework::ConfigNode::typeToString(configMember->type()); 150 | return false; 151 | } 152 | 153 | m_config = std::move(configMember->clone()->toObject()); 154 | } 155 | 156 | // Load dependencies 157 | m_dependencies.clear(); 158 | 159 | if (!loadOptionalConfigParameter(&m_dependencies, QStringLiteral("dependencies"), config)) 160 | { 161 | qCWarning(CppPluginFramework::LoggingCategory::Config) 162 | << "Failed to load plugin instance's dependencies!"; 163 | return false; 164 | } 165 | 166 | return true; 167 | } 168 | 169 | // ------------------------------------------------------------------------------------------------- 170 | 171 | bool PluginInstanceConfig::storeConfigParameters(CppConfigFramework::ConfigObjectNode *config) 172 | { 173 | // Storing config parameters is currently not supported 174 | Q_UNUSED(config) 175 | return false; 176 | } 177 | 178 | // ------------------------------------------------------------------------------------------------- 179 | 180 | QString PluginInstanceConfig::validateConfig() const 181 | { 182 | // Check name 183 | if (!Validation::validatePluginInstanceName(m_name)) 184 | { 185 | return QStringLiteral("Name is not valid: ") % m_name; 186 | } 187 | 188 | // Config is optional 189 | 190 | // Check (optional) dependencies 191 | if (!m_dependencies.isEmpty()) 192 | { 193 | // Check individual dependency if it is referencing a valid name 194 | for (const QString &dependency : m_dependencies) 195 | { 196 | if (dependency == m_name) 197 | { 198 | return QStringLiteral("Dependency name is the same as the plugin instance name: ") % 199 | dependency; 200 | } 201 | 202 | if (!Validation::validatePluginInstanceName(dependency)) 203 | { 204 | return QStringLiteral("Dependency's name is not valid: ") % dependency; 205 | } 206 | } 207 | } 208 | 209 | return QString(); 210 | } 211 | 212 | } // namespace CppPluginFramework 213 | 214 | // ------------------------------------------------------------------------------------------------- 215 | 216 | bool operator==(const CppPluginFramework::PluginInstanceConfig &left, 217 | const CppPluginFramework::PluginInstanceConfig &right) 218 | { 219 | return ((left.name() == right.name()) && 220 | (left.config() == right.config()) && 221 | (left.dependencies() == right.dependencies())); 222 | } 223 | 224 | // ------------------------------------------------------------------------------------------------- 225 | 226 | bool operator!=(const CppPluginFramework::PluginInstanceConfig &left, 227 | const CppPluginFramework::PluginInstanceConfig &right) 228 | { 229 | return !(left == right); 230 | } 231 | -------------------------------------------------------------------------------- /code/src/PluginManagerConfig.cpp: -------------------------------------------------------------------------------- 1 | /* This file is part of C++ Plugin Framework. 2 | * 3 | * C++ Plugin Framework is free software: you can redistribute it and/or modify it under the terms 4 | * of the GNU Lesser General Public License as published by the Free Software Foundation, either 5 | * version 3 of the License, or (at your option) any later version. 6 | * 7 | * C++ Plugin Framework is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 8 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | * GNU Lesser General Public License for more details. 10 | * 11 | * You should have received a copy of the GNU Lesser General Public License along with C++ Plugin 12 | * Framework. If not, see . 13 | */ 14 | 15 | /*! 16 | * \file 17 | * 18 | * Contains a config class for the Plugin Manager 19 | */ 20 | 21 | // Own header 22 | #include 23 | 24 | // C++ Plugin Framework includes 25 | #include 26 | 27 | // Qt includes 28 | #include 29 | 30 | // System includes 31 | 32 | // Forward declarations 33 | 34 | // Macros 35 | 36 | // ------------------------------------------------------------------------------------------------- 37 | 38 | namespace CppPluginFramework 39 | { 40 | 41 | bool PluginManagerConfig::isValid() const 42 | { 43 | return validateConfig().isEmpty(); 44 | } 45 | 46 | // ------------------------------------------------------------------------------------------------- 47 | 48 | const QList &PluginManagerConfig::pluginConfigs() const 49 | { 50 | return m_pluginConfigs; 51 | } 52 | 53 | // ------------------------------------------------------------------------------------------------- 54 | 55 | void PluginManagerConfig::setPluginConfigs(const QList &pluginConfigs) 56 | { 57 | m_pluginConfigs = pluginConfigs; 58 | } 59 | 60 | // ------------------------------------------------------------------------------------------------- 61 | 62 | const QStringList &PluginManagerConfig::pluginStartupPriorities() const 63 | { 64 | return m_pluginStartupPriorities; 65 | } 66 | 67 | // ------------------------------------------------------------------------------------------------- 68 | 69 | void PluginManagerConfig::setPluginStartupPriorities(const QStringList &startupPriorities) 70 | { 71 | m_pluginStartupPriorities = startupPriorities; 72 | } 73 | 74 | // ------------------------------------------------------------------------------------------------- 75 | 76 | bool PluginManagerConfig::loadConfigParameters(const CppConfigFramework::ConfigObjectNode &config) 77 | { 78 | // Load plugin configs 79 | if (!loadRequiredConfigContainer(&m_pluginConfigs, QStringLiteral("plugins"), config)) 80 | { 81 | qCWarning(CppPluginFramework::LoggingCategory::Config) 82 | << "Failed to load plugin configurations!"; 83 | return false; 84 | } 85 | 86 | // Load plugin startup priorities 87 | m_pluginStartupPriorities.clear(); 88 | 89 | if (!loadOptionalConfigParameter(&m_pluginStartupPriorities, 90 | QStringLiteral("plugin_startup_priorities"), 91 | config)) 92 | { 93 | qCWarning(CppPluginFramework::LoggingCategory::Config) 94 | << "Failed to load plugin's startup priorities!"; 95 | return false; 96 | } 97 | 98 | return true; 99 | } 100 | 101 | // ------------------------------------------------------------------------------------------------- 102 | 103 | bool PluginManagerConfig::storeConfigParameters(CppConfigFramework::ConfigObjectNode *config) 104 | { 105 | // Storing config parameters is currently not supported 106 | Q_UNUSED(config) 107 | return false; 108 | } 109 | 110 | // ------------------------------------------------------------------------------------------------- 111 | 112 | QString PluginManagerConfig::validateConfig() const 113 | { 114 | // Check individual plugins are valid and extract all instance names 115 | QSet plugins; 116 | QSet instanceNames; 117 | QSet dependencies; 118 | 119 | for (const auto &pluginConfig : m_pluginConfigs) 120 | { 121 | // Check individual plugins are valid 122 | if (!pluginConfig.isValid()) 123 | { 124 | return QStringLiteral("Plugin config is not valid: ") % pluginConfig.filePath(); 125 | } 126 | 127 | // Check for duplicate plugins 128 | if (plugins.contains(pluginConfig.filePath())) 129 | { 130 | return QStringLiteral("Duplicated plugin: [%1]") % pluginConfig.filePath(); 131 | } 132 | 133 | plugins.insert(pluginConfig.filePath()); 134 | 135 | // Check for plugin instances of this plugin 136 | for (const auto &instanceConfig : pluginConfig.instanceConfigs()) 137 | { 138 | // Check for duplicate instance name 139 | if (instanceNames.contains(instanceConfig.name())) 140 | { 141 | return QString("Plugin [%1] has an instance with a duplicated name [%2]!") 142 | .arg(pluginConfig.filePath(), 143 | instanceConfig.name()); 144 | } 145 | 146 | instanceNames.insert(instanceConfig.name()); 147 | 148 | // Keep track of all dependencies 149 | dependencies.unite(instanceConfig.dependencies()); 150 | } 151 | } 152 | 153 | // Check if dependencies reference actual plugin instances 154 | for (const QString &dependency : dependencies) 155 | { 156 | if (!instanceNames.contains(dependency)) 157 | { 158 | return QString("Dependency [%1] does not reference an actual plugin instance!") 159 | .arg(dependency); 160 | } 161 | } 162 | 163 | // Check if the startup priorities reference actual plugin instances 164 | for (const QString &instanceName : m_pluginStartupPriorities) 165 | { 166 | if (!instanceNames.contains(instanceName)) 167 | { 168 | return QString("Plugin instance [%1] referenced in the startup priorities does not" 169 | "reference an actual plugin instance!") 170 | .arg(instanceName); 171 | } 172 | 173 | if (m_pluginStartupPriorities.count(instanceName) != 1) 174 | { 175 | return QString("Duplicate plugin instance [%1] in the startup priorities!") 176 | .arg(instanceName); 177 | } 178 | } 179 | 180 | return QString(); 181 | } 182 | 183 | } // namespace CppPluginFramework 184 | 185 | // ------------------------------------------------------------------------------------------------- 186 | 187 | bool operator==(const CppPluginFramework::PluginManagerConfig &left, 188 | const CppPluginFramework::PluginManagerConfig &right) 189 | { 190 | return ((left.pluginConfigs() == right.pluginConfigs()) && 191 | (left.pluginStartupPriorities() == right.pluginStartupPriorities())); 192 | } 193 | 194 | // ------------------------------------------------------------------------------------------------- 195 | 196 | bool operator!=(const CppPluginFramework::PluginManagerConfig &left, 197 | const CppPluginFramework::PluginManagerConfig &right) 198 | { 199 | return !(left == right); 200 | } 201 | -------------------------------------------------------------------------------- /code/src/Validation.cpp: -------------------------------------------------------------------------------- 1 | /* This file is part of C++ Plugin Framework. 2 | * 3 | * C++ Plugin Framework is free software: you can redistribute it and/or modify it under the terms 4 | * of the GNU Lesser General Public License as published by the Free Software Foundation, either 5 | * version 3 of the License, or (at your option) any later version. 6 | * 7 | * C++ Plugin Framework is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 8 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | * GNU Lesser General Public License for more details. 10 | * 11 | * You should have received a copy of the GNU Lesser General Public License along with C++ Plugin 12 | * Framework. If not, see . 13 | */ 14 | 15 | /*! 16 | * \file 17 | * 18 | * Contains validation methods 19 | */ 20 | 21 | // Own header 22 | #include 23 | 24 | // C++ Plugin Framework includes 25 | #include 26 | 27 | // Qt includes 28 | #include 29 | #include 30 | 31 | // System includes 32 | 33 | // Forward declarations 34 | 35 | // Macros 36 | 37 | // ------------------------------------------------------------------------------------------------- 38 | 39 | namespace CppPluginFramework 40 | { 41 | 42 | namespace Validation 43 | { 44 | 45 | bool validatePluginInstanceName(const QString &name) 46 | { 47 | QRegularExpression regex("^[a-zA-Z][a-zA-Z0-9\\._-]*$"); 48 | auto match = regex.match(name); 49 | 50 | return match.hasMatch(); 51 | } 52 | 53 | // ------------------------------------------------------------------------------------------------- 54 | 55 | bool validateInterfaceName(const QString &name) 56 | { 57 | QRegularExpression regex("^[a-zA-Z][a-zA-Z0-9]*(::[a-zA-Z][a-zA-Z0-9]*)*$"); 58 | auto match = regex.match(name); 59 | 60 | return match.hasMatch(); 61 | } 62 | 63 | // ------------------------------------------------------------------------------------------------- 64 | 65 | bool validateExportedInterfaces(const QSet &exportedInterfaces) 66 | { 67 | if (exportedInterfaces.isEmpty()) 68 | { 69 | return false; 70 | } 71 | 72 | for (const QString &item : exportedInterfaces) 73 | { 74 | if (!validateInterfaceName(item)) 75 | { 76 | return false; 77 | } 78 | } 79 | 80 | return true; 81 | } 82 | 83 | // ------------------------------------------------------------------------------------------------- 84 | 85 | bool validateEnvironmentVariableName(const QString &name) 86 | { 87 | QRegularExpression regex("^[a-zA-Z_][a-zA-Z0-9_]*$"); 88 | auto match = regex.match(name); 89 | 90 | return match.hasMatch(); 91 | } 92 | 93 | // ------------------------------------------------------------------------------------------------- 94 | 95 | bool validateFilePath(const QString &filePath) 96 | { 97 | return QFileInfo(filePath).isFile(); 98 | } 99 | 100 | } // namespace Validation 101 | 102 | } // namespace CppPluginFramework 103 | -------------------------------------------------------------------------------- /code/tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This file is part of C++ Plugin Framework. 2 | # 3 | # C++ Plugin Framework is free software: you can redistribute it and/or modify it under the terms 4 | # of the GNU Lesser General Public License as published by the Free Software Foundation, either 5 | # version 3 of the License, or (at your option) any later version. 6 | # 7 | # C++ Plugin Framework is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 8 | # without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU Lesser General Public License for more details. 10 | # 11 | # You should have received a copy of the GNU Lesser General Public License along with C++ Plugin 12 | # Framework. If not, see . 13 | 14 | set(CMAKE_AUTOMOC ON) 15 | set(CMAKE_AUTORCC ON) 16 | 17 | find_package(Qt5 COMPONENTS Test REQUIRED) 18 | 19 | # -------------------------------------------------------------------------------------------------- 20 | # Custom (meta) targets 21 | # -------------------------------------------------------------------------------------------------- 22 | add_custom_target(all_tests) 23 | 24 | # -------------------------------------------------------------------------------------------------- 25 | # Helper methods 26 | # -------------------------------------------------------------------------------------------------- 27 | function(CppPluginFramework_AddTest) 28 | # Function parameters 29 | set(options) # Boolean parameters 30 | set(oneValueParams # Parameters with one value 31 | TEST_NAME 32 | ) 33 | set(multiValueParams # Parameters with multiple values 34 | ADDITIONAL_SOURCES 35 | ADDITIONAL_HEADERS 36 | ADDITIONAL_LIBS 37 | LABELS 38 | ) 39 | 40 | cmake_parse_arguments(PARAM "${options}" "${oneValueParams}" "${multiValueParams}" ${ARGN}) 41 | 42 | # Create test executable 43 | add_executable(${PARAM_TEST_NAME} 44 | ${PARAM_TEST_NAME}.cpp 45 | ${PARAM_ADDITIONAL_SOURCES} 46 | ${PARAM_ADDITIONAL_HEADERS} 47 | ) 48 | 49 | target_include_directories(${PARAM_TEST_NAME} PUBLIC 50 | ${CMAKE_CURRENT_BINARY_DIR} 51 | ) 52 | 53 | target_link_libraries(${PARAM_TEST_NAME} 54 | PUBLIC CppPluginFramework 55 | PUBLIC Qt5::Test 56 | PUBLIC ${PARAM_ADDITIONAL_LIBS} 57 | ) 58 | 59 | add_test(${PARAM_TEST_NAME} ${PARAM_TEST_NAME}) 60 | 61 | # Add test to target "all_tests" 62 | add_dependencies(all_tests ${PARAM_TEST_NAME}) 63 | 64 | # Set test labels 65 | if (PARAM_LABELS) 66 | set_tests_properties(${PARAM_TEST_NAME} PROPERTIES 67 | LABELS "CPPPLUGINFRAMEWORK;${PARAM_LABELS}" 68 | ) 69 | endif() 70 | endfunction() 71 | 72 | # -------------------------------------------------------------------------------------------------- 73 | # Tests 74 | # -------------------------------------------------------------------------------------------------- 75 | add_subdirectory(unit) 76 | add_subdirectory(integration) 77 | 78 | # -------------------------------------------------------------------------------------------------- 79 | # Code Coverage 80 | # -------------------------------------------------------------------------------------------------- 81 | if (CppPluginFramework_CodeCoverage MATCHES ON) 82 | # Prepare targets for code coverage 83 | SETUP_TARGET_FOR_COVERAGE( 84 | NAME ctest_coverage_all_tests 85 | EXECUTABLE ctest 86 | DEPENDENCIES all_tests 87 | PROJECT_PATH ${PROJECT_SOURCE_DIR} 88 | EXCLUDES ${CppPluginFramework_CodeCoverage_EXCLUDES} 89 | ) 90 | endif() 91 | -------------------------------------------------------------------------------- /code/tests/integration/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This file is part of C++ Plugin Framework. 2 | # 3 | # C++ Plugin Framework is free software: you can redistribute it and/or modify it under the terms 4 | # of the GNU Lesser General Public License as published by the Free Software Foundation, either 5 | # version 3 of the License, or (at your option) any later version. 6 | # 7 | # C++ Plugin Framework is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 8 | # without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU Lesser General Public License for more details. 10 | # 11 | # You should have received a copy of the GNU Lesser General Public License along with C++ Plugin 12 | # Framework. If not, see . 13 | 14 | # -------------------------------------------------------------------------------------------------- 15 | # Custom (meta) targets 16 | # -------------------------------------------------------------------------------------------------- 17 | add_custom_target(all_integration_tests) 18 | 19 | # -------------------------------------------------------------------------------------------------- 20 | # Helper methods 21 | # -------------------------------------------------------------------------------------------------- 22 | function(CppPluginFramework_AddIntegrationTest) 23 | # Function parameters 24 | set(options) # Boolean parameters 25 | set(oneValueParams # Parameters with one value 26 | TEST_NAME 27 | ) 28 | set(multiValueParams) # Parameters with multiple values 29 | 30 | cmake_parse_arguments(PARAM "${options}" "${oneValueParams}" "${multiValueParams}" ${ARGN}) 31 | 32 | # Add test 33 | CppPluginFramework_AddTest(${ARGN} LABELS CPPPLUGINFRAMEWORK_INTEGRATION_TESTS) 34 | 35 | # Add test to target "all_integration_tests" 36 | add_dependencies(all_integration_tests ${PARAM_TEST_NAME}) 37 | endfunction() 38 | 39 | # -------------------------------------------------------------------------------------------------- 40 | # Test plugins 41 | # -------------------------------------------------------------------------------------------------- 42 | add_subdirectory(TestPlugins) 43 | 44 | # -------------------------------------------------------------------------------------------------- 45 | # Integration tests 46 | # -------------------------------------------------------------------------------------------------- 47 | add_subdirectory(Plugin) 48 | add_subdirectory(PluginManager) 49 | 50 | # -------------------------------------------------------------------------------------------------- 51 | # Code Coverage 52 | # -------------------------------------------------------------------------------------------------- 53 | if (CppPluginFramework_CodeCoverage MATCHES ON) 54 | # Prepare targets for code coverage 55 | SETUP_TARGET_FOR_COVERAGE( 56 | NAME ctest_coverage_all_integration_tests 57 | EXECUTABLE ctest -L CPPPLUGINFRAMEWORK_INTEGRATION_TESTS 58 | DEPENDENCIES all_integration_tests 59 | PROJECT_PATH ${PROJECT_SOURCE_DIR} 60 | EXCLUDES ${CppPluginFramework_CodeCoverage_EXCLUDES} 61 | ) 62 | endif() 63 | -------------------------------------------------------------------------------- /code/tests/integration/Plugin/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This file is part of C++ Plugin Framework. 2 | # 3 | # C++ Plugin Framework is free software: you can redistribute it and/or modify it under the terms 4 | # of the GNU Lesser General Public License as published by the Free Software Foundation, either 5 | # version 3 of the License, or (at your option) any later version. 6 | # 7 | # C++ Plugin Framework is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 8 | # without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU Lesser General Public License for more details. 10 | # 11 | # You should have received a copy of the GNU Lesser General Public License along with C++ Plugin 12 | # Framework. If not, see . 13 | 14 | CppPluginFramework_AddIntegrationTest(TEST_NAME testPlugin) 15 | -------------------------------------------------------------------------------- /code/tests/integration/Plugin/testPlugin.cpp: -------------------------------------------------------------------------------- 1 | /* This file is part of C++ Plugin Framework. 2 | * 3 | * C++ Plugin Framework is free software: you can redistribute it and/or modify it under the terms 4 | * of the GNU Lesser General Public License as published by the Free Software Foundation, either 5 | * version 3 of the License, or (at your option) any later version. 6 | * 7 | * C++ Plugin Framework is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 8 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | * GNU Lesser General Public License for more details. 10 | * 11 | * You should have received a copy of the GNU Lesser General Public License along with C++ Plugin 12 | * Framework. If not, see . 13 | */ 14 | 15 | /*! 16 | * \file 17 | * 18 | * Contains unit tests for Plugin class 19 | */ 20 | 21 | // C++ Plugin Framework includes 22 | #include 23 | #include 24 | 25 | // C++ Config Framework includes 26 | #include 27 | 28 | // Qt includes 29 | #include 30 | #include 31 | 32 | // System includes 33 | 34 | // Forward declarations 35 | 36 | // Macros 37 | 38 | // Test class declaration -------------------------------------------------------------------------- 39 | 40 | using namespace CppConfigFramework; 41 | using namespace CppPluginFramework; 42 | 43 | Q_DECLARE_METATYPE(PluginConfig) 44 | Q_DECLARE_METATYPE(VersionInfo) 45 | 46 | class TestPlugin : public QObject 47 | { 48 | Q_OBJECT 49 | 50 | private slots: 51 | // Functions executed by QtTest before and after test suite 52 | void initTestCase(); 53 | void cleanupTestCase(); 54 | 55 | // Functions executed by QtTest before and after each test 56 | void init(); 57 | void cleanup(); 58 | 59 | // Test functions 60 | void testLoadPlugin(); 61 | void testLoadPlugin_data(); 62 | }; 63 | 64 | // Test Case init/cleanup methods ------------------------------------------------------------------ 65 | 66 | void TestPlugin::initTestCase() 67 | { 68 | } 69 | 70 | void TestPlugin::cleanupTestCase() 71 | { 72 | } 73 | 74 | // Test init/cleanup methods ----------------------------------------------------------------------- 75 | 76 | void TestPlugin::init() 77 | { 78 | } 79 | 80 | void TestPlugin::cleanup() 81 | { 82 | } 83 | 84 | // Test: loading of plugins ------------------------------------------------------------------------ 85 | 86 | void TestPlugin::testLoadPlugin() 87 | { 88 | QFETCH(PluginConfig, config); 89 | QFETCH(QStringList, instanceNames); 90 | QFETCH(VersionInfo, version); 91 | QFETCH(QString, description); 92 | QFETCH(QStringList, exportedInterfaces); 93 | QFETCH(bool, result); 94 | 95 | // Load plugin 96 | auto instances = Plugin::loadInstances(config); 97 | QCOMPARE(instances.size(), static_cast(instanceNames.size())); 98 | 99 | // Check plugin 100 | if (result) 101 | { 102 | QStringList checkedInstances; 103 | 104 | for (const auto &instance : instances) 105 | { 106 | QVERIFY(instance); 107 | 108 | QVERIFY(instanceNames.contains(instance->name())); 109 | QVERIFY(!checkedInstances.contains(instance->name())); 110 | checkedInstances.append(instance->name()); 111 | 112 | QCOMPARE(instance->version(), version); 113 | QCOMPARE(instance->description(), description); 114 | #if QT_VERSION < QT_VERSION_CHECK(5, 14, 0) 115 | QCOMPARE(instance->exportedInterfaces(), QSet::fromList(exportedInterfaces)); 116 | #else 117 | QCOMPARE(instance->exportedInterfaces(), QSet(exportedInterfaces.begin(), 118 | exportedInterfaces.end())); 119 | #endif 120 | } 121 | 122 | QCOMPARE(checkedInstances.size(), instanceNames.size()); 123 | #if QT_VERSION < QT_VERSION_CHECK(5, 14, 0) 124 | QCOMPARE(QSet::fromList(checkedInstances), QSet::fromList(instanceNames)); 125 | #else 126 | QCOMPARE(QSet(checkedInstances.begin(), checkedInstances.end()), 127 | QSet(instanceNames.begin(), instanceNames.end())); 128 | #endif 129 | } 130 | } 131 | 132 | void TestPlugin::testLoadPlugin_data() 133 | { 134 | QTest::addColumn("config"); 135 | QTest::addColumn("instanceNames"); 136 | QTest::addColumn("version"); 137 | QTest::addColumn("description"); 138 | QTest::addColumn("exportedInterfaces"); 139 | QTest::addColumn("result"); 140 | 141 | QDir testPluginsDir(QCoreApplication::applicationDirPath()); 142 | testPluginsDir.cd("../TestPlugins"); 143 | 144 | // Loading of test plugin 1 145 | { 146 | ConfigObjectNode instance1Config 147 | { 148 | { "value", ConfigValueNode("value1") } 149 | }; 150 | 151 | ConfigObjectNode instance2Config 152 | { 153 | { "value", ConfigValueNode("value2") } 154 | }; 155 | 156 | QList instanceConfigs; 157 | instanceConfigs << PluginInstanceConfig("instance1", instance1Config) 158 | << PluginInstanceConfig("instance2", instance2Config); 159 | 160 | PluginConfig config(testPluginsDir.filePath("TestPlugin1.plugin"), 161 | VersionInfo(1, 0, 0), 162 | instanceConfigs); 163 | 164 | QTest::newRow("valid: test plugin 1") 165 | << config 166 | << QStringList { "instance1", "instance2" } 167 | << VersionInfo(1, 0, 0) 168 | << QString("test plugin 1") 169 | << QStringList { "CppPluginFramework::TestPlugins::ITestPlugin1" } 170 | << true; 171 | } 172 | 173 | // Loading of test plugin 2 174 | { 175 | ConfigObjectNode instance3Config 176 | { 177 | { "delimiter", ConfigValueNode(";") } 178 | }; 179 | 180 | QList instanceConfigs; 181 | instanceConfigs << PluginInstanceConfig("instance3", 182 | instance3Config, 183 | QSet { "instance1", "instance2"}); 184 | 185 | PluginConfig config(testPluginsDir.filePath("TestPlugin2.plugin"), 186 | VersionInfo(1, 0, 0), 187 | VersionInfo(1, 0, 1), 188 | instanceConfigs); 189 | 190 | QTest::newRow("valid: test plugin 2") 191 | << config 192 | << QStringList { "instance3" } 193 | << VersionInfo(1, 0, 0) 194 | << QString("test plugin 2") 195 | << QStringList { "CppPluginFramework::TestPlugins::ITestPlugin2" } 196 | << true; 197 | } 198 | 199 | // Loading of plugin with invalid config 200 | { 201 | ConfigObjectNode instance1Config 202 | { 203 | { "invalid", ConfigValueNode("x") } 204 | }; 205 | 206 | QList instanceConfigs; 207 | instanceConfigs << PluginInstanceConfig("instance1", instance1Config); 208 | 209 | PluginConfig config(testPluginsDir.filePath("TestPlugin1.plugin"), 210 | VersionInfo(1, 0, 0), 211 | instanceConfigs); 212 | 213 | QTest::newRow("invalid: config") << config 214 | << QStringList() 215 | << VersionInfo(1, 0, 0) 216 | << QString() 217 | << QStringList() 218 | << false; 219 | } 220 | 221 | // Loading of plugin with invalid plugin config 222 | { 223 | QTest::newRow("invalid: plugin config") << PluginConfig() 224 | << QStringList() 225 | << VersionInfo() 226 | << QString() 227 | << QStringList() 228 | << false; 229 | } 230 | 231 | // Loading of plugin with invalid version 232 | { 233 | ConfigObjectNode instance1Config 234 | { 235 | { "value", ConfigValueNode("value1") } 236 | }; 237 | 238 | QList instanceConfigs; 239 | instanceConfigs << PluginInstanceConfig("instance1", instance1Config); 240 | 241 | PluginConfig config(testPluginsDir.filePath("TestPlugin1.plugin"), 242 | VersionInfo(1, 0, 1), 243 | instanceConfigs); 244 | 245 | QTest::newRow("invalid: version 1") << config 246 | << QStringList() 247 | << VersionInfo() 248 | << QString() 249 | << QStringList() 250 | << false; 251 | 252 | config = PluginConfig(testPluginsDir.filePath("TestPlugin1.plugin"), 253 | VersionInfo(1, 0, 1), 254 | VersionInfo(1, 0, 2), 255 | instanceConfigs); 256 | 257 | QTest::newRow("invalid: version 2") << config 258 | << QStringList() 259 | << VersionInfo() 260 | << QString() 261 | << QStringList() 262 | << false; 263 | } 264 | } 265 | 266 | // Main function ----------------------------------------------------------------------------------- 267 | 268 | QTEST_MAIN(TestPlugin) 269 | #include "testPlugin.moc" 270 | -------------------------------------------------------------------------------- /code/tests/integration/PluginManager/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This file is part of C++ Plugin Framework. 2 | # 3 | # C++ Plugin Framework is free software: you can redistribute it and/or modify it under the terms 4 | # of the GNU Lesser General Public License as published by the Free Software Foundation, either 5 | # version 3 of the License, or (at your option) any later version. 6 | # 7 | # C++ Plugin Framework is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 8 | # without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU Lesser General Public License for more details. 10 | # 11 | # You should have received a copy of the GNU Lesser General Public License along with C++ Plugin 12 | # Framework. If not, see . 13 | 14 | CppPluginFramework_AddIntegrationTest(TEST_NAME testPluginManager ADDITIONAL_SOURCES TestData.qrc) 15 | -------------------------------------------------------------------------------- /code/tests/integration/PluginManager/TestData.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | TestData/AppConfig.json 4 | TestData/InvalidConfigWithUnsupportedDependency.json 5 | TestData/AppConfigWithInvalidStartupOrder.json 6 | 7 | 8 | -------------------------------------------------------------------------------- /code/tests/integration/PluginManager/TestData/AppConfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "environment_variables": 3 | { 4 | "TestPluginsPath": "../TestPlugins" 5 | }, 6 | 7 | "config": 8 | { 9 | "plugin_startup_priorities": 10 | [ 11 | "instance1", 12 | "instance2" 13 | ], 14 | 15 | "plugins": 16 | { 17 | "test_plugin1": 18 | { 19 | "$file_path": "${TestPluginsPath}/TestPlugin1.plugin", 20 | "version": "1.0.0", 21 | "comment": "test plugin which just returns the configured value", 22 | "instances": 23 | { 24 | "instance1": 25 | { 26 | "name": "instance1", 27 | "config": 28 | { 29 | "value": "value1" 30 | } 31 | }, 32 | 33 | "instance2": 34 | { 35 | "name": "instance2", 36 | "config": 37 | { 38 | "value": "value2" 39 | } 40 | } 41 | } 42 | }, 43 | 44 | "test_plugin2": 45 | { 46 | "$file_path": "${TestPluginsPath}/TestPlugin2.plugin", 47 | "min_version": "1.0.0", 48 | "max_version": "1.0.1", 49 | "comment": "test plugin which just joins the values it gets from its dependencies", 50 | "instances": 51 | { 52 | "instance3": 53 | { 54 | "name": "instance3", 55 | "config": 56 | { 57 | "delimiter": ";" 58 | }, 59 | "dependencies": 60 | [ 61 | "instance1", 62 | "instance2" 63 | ] 64 | } 65 | } 66 | } 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /code/tests/integration/PluginManager/TestData/AppConfigWithInvalidStartupOrder.json: -------------------------------------------------------------------------------- 1 | { 2 | "includes": 3 | [ 4 | { 5 | "file_path": "AppConfig.json" 6 | } 7 | ], 8 | 9 | "config": 10 | { 11 | "plugin_startup_priorities": 12 | [ 13 | "instance3" 14 | ] 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /code/tests/integration/PluginManager/TestData/InvalidConfigWithUnsupportedDependency.json: -------------------------------------------------------------------------------- 1 | { 2 | "environment_variables": 3 | { 4 | "TestPluginsPath": "../TestPlugins" 5 | }, 6 | 7 | "config": 8 | { 9 | "plugins": 10 | { 11 | "test_plugin1": 12 | { 13 | "$file_path": "${TestPluginsPath}/TestPlugin1.plugin", 14 | "version": "1.0.0", 15 | "instances": 16 | { 17 | "instance1": 18 | { 19 | "name": "instance1", 20 | "config": 21 | { 22 | "value": "value1" 23 | } 24 | }, 25 | 26 | "instance2": 27 | { 28 | "name": "instance2", 29 | "config": 30 | { 31 | "value": "value2" 32 | }, 33 | "dependencies": 34 | [ 35 | "instance1" 36 | ] 37 | } 38 | } 39 | }, 40 | 41 | "test_plugin2": 42 | { 43 | "$file_path": "${TestPluginsPath}/TestPlugin2.plugin", 44 | "min_version": "1.0.0", 45 | "max_version": "1.0.1", 46 | "instances": 47 | { 48 | "instance3": 49 | { 50 | "name": "instance3", 51 | "config": 52 | { 53 | "delimiter": ";" 54 | }, 55 | "dependencies": 56 | [ 57 | "instance1", 58 | "instance2" 59 | ] 60 | } 61 | } 62 | } 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /code/tests/integration/PluginManager/testPluginManager.cpp: -------------------------------------------------------------------------------- 1 | /* This file is part of C++ Plugin Framework. 2 | * 3 | * C++ Plugin Framework is free software: you can redistribute it and/or modify it under the terms 4 | * of the GNU Lesser General Public License as published by the Free Software Foundation, either 5 | * version 3 of the License, or (at your option) any later version. 6 | * 7 | * C++ Plugin Framework is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 8 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | * GNU Lesser General Public License for more details. 10 | * 11 | * You should have received a copy of the GNU Lesser General Public License along with C++ Plugin 12 | * Framework. If not, see . 13 | */ 14 | 15 | /*! 16 | * \file 17 | * 18 | * Contains unit tests for PluginManager class 19 | */ 20 | 21 | // C++ Plugin Framework includes 22 | #include 23 | #include 24 | #include "../TestPlugins/ITestPlugin1.hpp" 25 | #include "../TestPlugins/ITestPlugin2.hpp" 26 | 27 | // C++ Config Framework includes 28 | #include 29 | 30 | // Qt includes 31 | #include 32 | #include 33 | 34 | // System includes 35 | 36 | // Forward declarations 37 | 38 | // Macros 39 | 40 | // Test class declaration -------------------------------------------------------------------------- 41 | 42 | using namespace CppConfigFramework; 43 | using namespace CppPluginFramework; 44 | 45 | class TestPluginManager : public QObject 46 | { 47 | Q_OBJECT 48 | 49 | private slots: 50 | // Functions executed by QtTest before and after test suite 51 | void initTestCase(); 52 | void cleanupTestCase(); 53 | 54 | // Functions executed by QtTest before and after each test 55 | void init(); 56 | void cleanup(); 57 | 58 | // Test functions 59 | void testLoad(); 60 | void testLoadAfterStart(); 61 | void testLoadPluginsWithInvalidConfig(); 62 | void testLoadPluginsWithUnsupportedDependency(); 63 | void testLoadPluginsWithInvalidStartupOrder(); 64 | }; 65 | 66 | // Test Case init/cleanup methods ------------------------------------------------------------------ 67 | 68 | void TestPluginManager::initTestCase() 69 | { 70 | } 71 | 72 | void TestPluginManager::cleanupTestCase() 73 | { 74 | } 75 | 76 | // Test init/cleanup methods ----------------------------------------------------------------------- 77 | 78 | void TestPluginManager::init() 79 | { 80 | } 81 | 82 | void TestPluginManager::cleanup() 83 | { 84 | } 85 | 86 | // Test: loading of plugins ------------------------------------------------------------------------ 87 | 88 | void TestPluginManager::testLoad() 89 | { 90 | // First load the config 91 | ConfigReader configReader; 92 | EnvironmentVariables environmentVariables; 93 | 94 | auto config = configReader.read(":/TestData/AppConfig.json", 95 | QDir(QCoreApplication::applicationDirPath()), 96 | ConfigNodePath::ROOT_PATH, 97 | ConfigNodePath::ROOT_PATH, 98 | {}, 99 | &environmentVariables); 100 | QVERIFY(config); 101 | 102 | PluginManagerConfig pluginManagerConfig; 103 | QVERIFY(pluginManagerConfig.loadConfig(*config)); 104 | 105 | // Load plugins 106 | PluginManager pluginManager; 107 | QVERIFY(pluginManager.load(pluginManagerConfig)); 108 | 109 | // Check all instances 110 | const QStringList instanceNames = pluginManager.pluginInstanceNames(); 111 | QCOMPARE(instanceNames.count(), 3); 112 | 113 | QVERIFY(instanceNames.contains("instance1")); 114 | QVERIFY(instanceNames.contains("instance2")); 115 | QVERIFY(instanceNames.contains("instance3")); 116 | 117 | QVERIFY(pluginManager.hasPluginInstance("instance1")); 118 | QVERIFY(pluginManager.hasPluginInstance("instance2")); 119 | QVERIFY(pluginManager.hasPluginInstance("instance3")); 120 | 121 | auto instance1 = pluginManager.pluginInstance("instance1"); 122 | auto instance2 = pluginManager.pluginInstance("instance2"); 123 | auto instance3 = pluginManager.pluginInstance("instance3"); 124 | 125 | QVERIFY(instance1 != nullptr); 126 | QVERIFY(instance2 != nullptr); 127 | QVERIFY(instance3 != nullptr); 128 | 129 | // Start plugins 130 | QVERIFY(pluginManager.start()); 131 | 132 | // Check if all instances return expected values 133 | QCOMPARE(instance1->interface()->value(), QStringLiteral("value1")); 134 | QCOMPARE(instance2->interface()->value(), QStringLiteral("value2")); 135 | QCOMPARE(instance3->interface()->joinedValues(), 136 | QStringLiteral("value1;value2")); 137 | 138 | // Stop plugins 139 | pluginManager.stop(); 140 | 141 | // Unload plugins 142 | QVERIFY(pluginManager.unload()); 143 | } 144 | 145 | // Test: loading of plugins after the plugins were already started --------------------------------- 146 | 147 | void TestPluginManager::testLoadAfterStart() 148 | { 149 | // First load the config 150 | ConfigReader configReader; 151 | EnvironmentVariables environmentVariables; 152 | 153 | auto config = configReader.read(":/TestData/AppConfig.json", 154 | QDir(QCoreApplication::applicationDirPath()), 155 | ConfigNodePath::ROOT_PATH, 156 | ConfigNodePath::ROOT_PATH, 157 | std::vector(), 158 | &environmentVariables); 159 | QVERIFY(config); 160 | 161 | PluginManagerConfig pluginManagerConfig; 162 | QVERIFY(pluginManagerConfig.loadConfig(*config)); 163 | 164 | // Load plugins 165 | PluginManager pluginManager; 166 | QVERIFY(pluginManager.load(pluginManagerConfig)); 167 | 168 | // Start plugins 169 | QVERIFY(pluginManager.start()); 170 | 171 | // Load plugins again (must fail) 172 | QVERIFY(!pluginManager.load(pluginManagerConfig)); 173 | 174 | // Stop plugins 175 | pluginManager.stop(); 176 | 177 | // Unload plugins 178 | QVERIFY(pluginManager.unload()); 179 | } 180 | 181 | // Test: loading of plugins with invalid config ---------------------------------------------------- 182 | 183 | void TestPluginManager::testLoadPluginsWithInvalidConfig() 184 | { 185 | PluginManagerConfig pluginManagerConfig; 186 | pluginManagerConfig.setPluginConfigs({ PluginConfig() }); 187 | 188 | // Load plugins with invalid config 189 | PluginManager pluginManager; 190 | QVERIFY(!pluginManager.load(pluginManagerConfig)); 191 | } 192 | 193 | // Test: loading of plugins with an unsupported dependency ----------------------------------------- 194 | 195 | void TestPluginManager::testLoadPluginsWithUnsupportedDependency() 196 | { 197 | // First load the config 198 | ConfigReader configReader; 199 | EnvironmentVariables environmentVariables; 200 | 201 | auto config = configReader.read(":/TestData/InvalidConfigWithUnsupportedDependency.json", 202 | QDir(QCoreApplication::applicationDirPath()), 203 | ConfigNodePath::ROOT_PATH, 204 | ConfigNodePath::ROOT_PATH, 205 | std::vector(), 206 | &environmentVariables); 207 | QVERIFY(config); 208 | 209 | PluginManagerConfig pluginManagerConfig; 210 | QVERIFY(pluginManagerConfig.loadConfig(*config)); 211 | 212 | PluginManager pluginManager; 213 | QVERIFY(!pluginManager.load(pluginManagerConfig)); 214 | } 215 | 216 | // Test: loading of plugins with invalid startup order --------------------------------------------- 217 | 218 | void TestPluginManager::testLoadPluginsWithInvalidStartupOrder() 219 | { 220 | // First load the config 221 | ConfigReader configReader; 222 | EnvironmentVariables environmentVariables; 223 | 224 | auto config = configReader.read(":/TestData/AppConfigWithInvalidStartupOrder.json", 225 | QDir(QCoreApplication::applicationDirPath()), 226 | ConfigNodePath::ROOT_PATH, 227 | ConfigNodePath::ROOT_PATH, 228 | std::vector(), 229 | &environmentVariables); 230 | QVERIFY(config); 231 | 232 | PluginManagerConfig pluginManagerConfig; 233 | QVERIFY(pluginManagerConfig.loadConfig(*config)); 234 | 235 | // Load plugins 236 | PluginManager pluginManager; 237 | QVERIFY(pluginManager.load(pluginManagerConfig)); 238 | 239 | // Start plugins (must fail) 240 | QVERIFY(!pluginManager.start()); 241 | } 242 | 243 | // Main function ----------------------------------------------------------------------------------- 244 | 245 | QTEST_MAIN(TestPluginManager) 246 | #include "testPluginManager.moc" 247 | -------------------------------------------------------------------------------- /code/tests/integration/TestPlugins/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This file is part of C++ Plugin Framework. 2 | # 3 | # C++ Plugin Framework is free software: you can redistribute it and/or modify it under the terms 4 | # of the GNU Lesser General Public License as published by the Free Software Foundation, either 5 | # version 3 of the License, or (at your option) any later version. 6 | # 7 | # C++ Plugin Framework is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 8 | # without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU Lesser General Public License for more details. 10 | # 11 | # You should have received a copy of the GNU Lesser General Public License along with C++ Plugin 12 | # Framework. If not, see . 13 | 14 | # -------------------------------------------------------------------------------------------------- 15 | # Test Plugin 1 16 | # -------------------------------------------------------------------------------------------------- 17 | add_library(TestPlugin1 SHARED 18 | ITestPlugin1.hpp 19 | TestPlugin1.hpp 20 | TestPlugin1.cpp 21 | ) 22 | 23 | target_include_directories(TestPlugin1 24 | PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} 25 | ) 26 | 27 | target_link_libraries(TestPlugin1 28 | PUBLIC CppPluginFramework 29 | PUBLIC Qt5::Core 30 | ) 31 | 32 | set_target_properties(TestPlugin1 PROPERTIES 33 | PREFIX "" 34 | SUFFIX ".plugin" 35 | ) 36 | 37 | # -------------------------------------------------------------------------------------------------- 38 | # Test Plugin 2 39 | # -------------------------------------------------------------------------------------------------- 40 | add_library(TestPlugin2 SHARED 41 | ITestPlugin2.hpp 42 | TestPlugin2.hpp 43 | TestPlugin2.cpp 44 | ) 45 | 46 | target_include_directories(TestPlugin2 47 | PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} 48 | ) 49 | 50 | target_link_libraries(TestPlugin2 51 | PUBLIC CppPluginFramework 52 | PUBLIC Qt5::Core 53 | ) 54 | 55 | set_target_properties(TestPlugin2 PROPERTIES 56 | PREFIX "" 57 | SUFFIX ".plugin" 58 | ) 59 | -------------------------------------------------------------------------------- /code/tests/integration/TestPlugins/ITestPlugin1.hpp: -------------------------------------------------------------------------------- 1 | /* This file is part of C++ Plugin Framework. 2 | * 3 | * C++ Plugin Framework is free software: you can redistribute it and/or modify it under the terms 4 | * of the GNU Lesser General Public License as published by the Free Software Foundation, either 5 | * version 3 of the License, or (at your option) any later version. 6 | * 7 | * C++ Plugin Framework is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 8 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | * GNU Lesser General Public License for more details. 10 | * 11 | * You should have received a copy of the GNU Lesser General Public License along with C++ Plugin 12 | * Framework. If not, see . 13 | */ 14 | 15 | /*! 16 | * \file 17 | * 18 | * Contains the interface for the "test plugin 1" 19 | */ 20 | 21 | #pragma once 22 | 23 | // Qt includes 24 | #include 25 | 26 | // System includes 27 | 28 | // Forward declarations 29 | 30 | // Macros 31 | 32 | // ------------------------------------------------------------------------------------------------- 33 | 34 | namespace CppPluginFramework 35 | { 36 | namespace TestPlugins 37 | { 38 | 39 | class ITestPlugin1 40 | { 41 | public: 42 | virtual ~ITestPlugin1() = default; 43 | virtual QString value() const = 0; 44 | }; 45 | 46 | } // namespace TestPlugins 47 | } // namespace CppPluginFramework 48 | -------------------------------------------------------------------------------- /code/tests/integration/TestPlugins/ITestPlugin2.hpp: -------------------------------------------------------------------------------- 1 | /* This file is part of C++ Plugin Framework. 2 | * 3 | * C++ Plugin Framework is free software: you can redistribute it and/or modify it under the terms 4 | * of the GNU Lesser General Public License as published by the Free Software Foundation, either 5 | * version 3 of the License, or (at your option) any later version. 6 | * 7 | * C++ Plugin Framework is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 8 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | * GNU Lesser General Public License for more details. 10 | * 11 | * You should have received a copy of the GNU Lesser General Public License along with C++ Plugin 12 | * Framework. If not, see . 13 | */ 14 | 15 | /*! 16 | * \file 17 | * 18 | * Contains the interface for the "test plugin 2" 19 | */ 20 | 21 | #pragma once 22 | 23 | // Qt includes 24 | #include 25 | 26 | // System includes 27 | 28 | // Forward declarations 29 | 30 | // Macros 31 | 32 | // ------------------------------------------------------------------------------------------------- 33 | 34 | namespace CppPluginFramework 35 | { 36 | namespace TestPlugins 37 | { 38 | 39 | class ITestPlugin2 40 | { 41 | public: 42 | virtual ~ITestPlugin2() = default; 43 | virtual QString joinedValues() const = 0; 44 | }; 45 | 46 | } // namespace TestPlugins 47 | } // namespace CppPluginFramework 48 | -------------------------------------------------------------------------------- /code/tests/integration/TestPlugins/TestPlugin1.cpp: -------------------------------------------------------------------------------- 1 | /* This file is part of C++ Plugin Framework. 2 | * 3 | * C++ Plugin Framework is free software: you can redistribute it and/or modify it under the terms 4 | * of the GNU Lesser General Public License as published by the Free Software Foundation, either 5 | * version 3 of the License, or (at your option) any later version. 6 | * 7 | * C++ Plugin Framework is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 8 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | * GNU Lesser General Public License for more details. 10 | * 11 | * You should have received a copy of the GNU Lesser General Public License along with C++ Plugin 12 | * Framework. If not, see . 13 | */ 14 | 15 | /*! 16 | * \file 17 | * 18 | * Contains the "test plugin 1" 19 | */ 20 | 21 | // C++ Plugin Framework includes 22 | #include "TestPlugin1.hpp" 23 | 24 | // C++ Config Framework includes 25 | #include 26 | 27 | // Cedar Framework includes 28 | #include 29 | 30 | // Qt includes 31 | 32 | // System includes 33 | 34 | // Forward declarations 35 | 36 | // Macros 37 | 38 | // ------------------------------------------------------------------------------------------------- 39 | 40 | namespace CppPluginFramework 41 | { 42 | namespace TestPlugins 43 | { 44 | 45 | // ------------------------------------------------------------------------------------------------- 46 | 47 | static const VersionInfo s_version("1.0.0"); 48 | static const QString s_description("test plugin 1"); 49 | static const QSet s_exportedInterfaces 50 | { 51 | "CppPluginFramework::TestPlugins::ITestPlugin1" 52 | }; 53 | 54 | // ------------------------------------------------------------------------------------------------- 55 | 56 | TestPlugin1::TestPlugin1(const QString &name) 57 | : CppPluginFramework::AbstractPlugin(name, s_version, s_description, s_exportedInterfaces) 58 | { 59 | } 60 | 61 | // ------------------------------------------------------------------------------------------------- 62 | 63 | bool TestPlugin1::loadConfig(const CppConfigFramework::ConfigObjectNode &config) 64 | { 65 | const auto jsonValue = CppConfigFramework::ConfigWriter::convertToJsonValue(config); 66 | 67 | if (!jsonValue.isObject()) 68 | { 69 | return false; 70 | } 71 | 72 | return CedarFramework::deserializeNode(jsonValue, "value", &m_configuredValue); 73 | } 74 | 75 | // ------------------------------------------------------------------------------------------------- 76 | 77 | bool TestPlugin1::injectDependency(IPlugin *plugin) 78 | { 79 | // This plugin doesn't support dependencies! 80 | Q_UNUSED(plugin) 81 | return false; 82 | } 83 | 84 | // ------------------------------------------------------------------------------------------------- 85 | 86 | void TestPlugin1::ejectDependencies() 87 | { 88 | } 89 | 90 | // ------------------------------------------------------------------------------------------------- 91 | 92 | QString TestPlugin1::value() const 93 | { 94 | return m_configuredValue; 95 | } 96 | 97 | } // namespace TestPlugins 98 | } // namespace CppPluginFramework 99 | -------------------------------------------------------------------------------- /code/tests/integration/TestPlugins/TestPlugin1.hpp: -------------------------------------------------------------------------------- 1 | /* This file is part of C++ Plugin Framework. 2 | * 3 | * C++ Plugin Framework is free software: you can redistribute it and/or modify it under the terms 4 | * of the GNU Lesser General Public License as published by the Free Software Foundation, either 5 | * version 3 of the License, or (at your option) any later version. 6 | * 7 | * C++ Plugin Framework is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 8 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | * GNU Lesser General Public License for more details. 10 | * 11 | * You should have received a copy of the GNU Lesser General Public License along with C++ Plugin 12 | * Framework. If not, see . 13 | */ 14 | 15 | /*! 16 | * \file 17 | * 18 | * Contains the "test plugin 1" 19 | */ 20 | 21 | #pragma once 22 | 23 | // C++ Plugin Framework includes 24 | #include 25 | #include 26 | #include 27 | 28 | // Qt includes 29 | 30 | // System includes 31 | 32 | // Forward declarations 33 | 34 | // Macros 35 | 36 | // ------------------------------------------------------------------------------------------------- 37 | 38 | namespace CppPluginFramework 39 | { 40 | namespace TestPlugins 41 | { 42 | 43 | class TestPlugin1 : public CppPluginFramework::AbstractPlugin, public ITestPlugin1 44 | { 45 | public: 46 | TestPlugin1(const QString &name); 47 | ~TestPlugin1() = default; 48 | 49 | bool loadConfig(const CppConfigFramework::ConfigObjectNode &config) override; 50 | bool injectDependency(IPlugin *plugin) override; 51 | void ejectDependencies() override; 52 | 53 | virtual QString value() const override; 54 | 55 | private: 56 | QString m_configuredValue; 57 | }; 58 | 59 | // ------------------------------------------------------------------------------------------------- 60 | 61 | class Q_DECL_EXPORT PluginFactory : public QObject, public PluginFactoryTemplate 62 | { 63 | Q_OBJECT 64 | Q_PLUGIN_METADATA(IID "CppPluginFramework::IPluginFactory") 65 | Q_INTERFACES(CppPluginFramework::IPluginFactory) 66 | 67 | public: 68 | //! Destructor 69 | ~PluginFactory() override = default; 70 | }; 71 | 72 | } // namespace TestPlugins 73 | } // namespace CppPluginFramework 74 | -------------------------------------------------------------------------------- /code/tests/integration/TestPlugins/TestPlugin2.cpp: -------------------------------------------------------------------------------- 1 | /* This file is part of C++ Plugin Framework. 2 | * 3 | * C++ Plugin Framework is free software: you can redistribute it and/or modify it under the terms 4 | * of the GNU Lesser General Public License as published by the Free Software Foundation, either 5 | * version 3 of the License, or (at your option) any later version. 6 | * 7 | * C++ Plugin Framework is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 8 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | * GNU Lesser General Public License for more details. 10 | * 11 | * You should have received a copy of the GNU Lesser General Public License along with C++ Plugin 12 | * Framework. If not, see . 13 | */ 14 | 15 | /*! 16 | * \file 17 | * 18 | * Contains the "test plugin 2" 19 | */ 20 | 21 | // C++ Plugin Framework includes 22 | #include "TestPlugin2.hpp" 23 | 24 | // C++ Config Framework includes 25 | #include 26 | 27 | // Cedar Framework includes 28 | #include 29 | 30 | // Qt includes 31 | 32 | // System includes 33 | 34 | // Forward declarations 35 | 36 | // Macros 37 | 38 | // ------------------------------------------------------------------------------------------------- 39 | 40 | namespace CppPluginFramework 41 | { 42 | namespace TestPlugins 43 | { 44 | 45 | // ------------------------------------------------------------------------------------------------- 46 | 47 | static const VersionInfo s_version("1.0.0"); 48 | static const QString s_description("test plugin 2"); 49 | static const QSet s_exportedInterfaces 50 | { 51 | "CppPluginFramework::TestPlugins::ITestPlugin2" 52 | }; 53 | 54 | // ------------------------------------------------------------------------------------------------- 55 | 56 | TestPlugin2::TestPlugin2(const QString &name) 57 | : CppPluginFramework::AbstractPlugin(name, s_version, s_description, s_exportedInterfaces) 58 | { 59 | } 60 | 61 | // ------------------------------------------------------------------------------------------------- 62 | 63 | bool TestPlugin2::loadConfig(const CppConfigFramework::ConfigObjectNode &config) 64 | { 65 | const auto jsonValue = CppConfigFramework::ConfigWriter::convertToJsonValue(config); 66 | 67 | if (!jsonValue.isObject()) 68 | { 69 | return false; 70 | } 71 | 72 | return CedarFramework::deserializeNode(jsonValue, "delimiter", &m_configuredDelimiter); 73 | } 74 | 75 | // ------------------------------------------------------------------------------------------------- 76 | 77 | bool TestPlugin2::injectDependency(IPlugin *plugin) 78 | { 79 | if (isStarted()) 80 | { 81 | return false; 82 | } 83 | 84 | if (!plugin->isInterfaceExported("CppPluginFramework::TestPlugins::ITestPlugin1")) 85 | { 86 | return false; 87 | } 88 | 89 | auto *interface = plugin->interface(); 90 | 91 | if (interface == nullptr) 92 | { 93 | return false; 94 | } 95 | 96 | m_dependencies.append(plugin); 97 | return true; 98 | } 99 | 100 | // ------------------------------------------------------------------------------------------------- 101 | 102 | void TestPlugin2::ejectDependencies() 103 | { 104 | if (!isStarted()) 105 | { 106 | m_dependencies.clear(); 107 | } 108 | } 109 | 110 | // ------------------------------------------------------------------------------------------------- 111 | 112 | QString TestPlugin2::joinedValues() const 113 | { 114 | QStringList values; 115 | 116 | for (auto *dependency : m_dependencies) 117 | { 118 | auto *interface = dependency->interface(); 119 | values.append(interface->value()); 120 | } 121 | 122 | values.sort(); 123 | 124 | return values.join(m_configuredDelimiter); 125 | } 126 | 127 | // ------------------------------------------------------------------------------------------------- 128 | 129 | bool TestPlugin2::onStart() 130 | { 131 | if (m_dependencies.isEmpty()) 132 | { 133 | return false; 134 | } 135 | 136 | for (auto *dependency : qAsConst(m_dependencies)) 137 | { 138 | if (!dependency->isStarted()) 139 | { 140 | return false; 141 | } 142 | } 143 | 144 | return true; 145 | } 146 | 147 | } // namespace TestPlugins 148 | } // namespace CppPluginFramework 149 | -------------------------------------------------------------------------------- /code/tests/integration/TestPlugins/TestPlugin2.hpp: -------------------------------------------------------------------------------- 1 | /* This file is part of C++ Plugin Framework. 2 | * 3 | * C++ Plugin Framework is free software: you can redistribute it and/or modify it under the terms 4 | * of the GNU Lesser General Public License as published by the Free Software Foundation, either 5 | * version 3 of the License, or (at your option) any later version. 6 | * 7 | * C++ Plugin Framework is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 8 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | * GNU Lesser General Public License for more details. 10 | * 11 | * You should have received a copy of the GNU Lesser General Public License along with C++ Plugin 12 | * Framework. If not, see . 13 | */ 14 | 15 | /*! 16 | * \file 17 | * 18 | * Contains the "test plugin 2" 19 | */ 20 | 21 | #pragma once 22 | 23 | // C++ Plugin Framework includes 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | // Qt includes 30 | 31 | // System includes 32 | 33 | // Forward declarations 34 | 35 | // Macros 36 | 37 | // ------------------------------------------------------------------------------------------------- 38 | 39 | namespace CppPluginFramework 40 | { 41 | namespace TestPlugins 42 | { 43 | 44 | class TestPlugin2 : public CppPluginFramework::AbstractPlugin, public ITestPlugin2 45 | { 46 | public: 47 | TestPlugin2(const QString &name); 48 | virtual ~TestPlugin2() = default; 49 | 50 | virtual bool loadConfig(const CppConfigFramework::ConfigObjectNode &config) override; 51 | virtual bool injectDependency(IPlugin *plugin) override; 52 | virtual void ejectDependencies() override; 53 | 54 | virtual QString joinedValues() const override; 55 | 56 | private: 57 | bool onStart() override; 58 | 59 | private: 60 | QString m_configuredDelimiter; 61 | QList m_dependencies; 62 | }; 63 | 64 | // ------------------------------------------------------------------------------------------------- 65 | 66 | class Q_DECL_EXPORT PluginFactory : public QObject, public PluginFactoryTemplate 67 | { 68 | Q_OBJECT 69 | Q_PLUGIN_METADATA(IID "CppPluginFramework::IPluginFactory") 70 | Q_INTERFACES(CppPluginFramework::IPluginFactory) 71 | 72 | public: 73 | //! Destructor 74 | ~PluginFactory() override = default; 75 | }; 76 | 77 | } // namespace TestPlugins 78 | } // namespace CppPluginFramework 79 | -------------------------------------------------------------------------------- /code/tests/unit/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This file is part of C++ Plugin Framework. 2 | # 3 | # C++ Plugin Framework is free software: you can redistribute it and/or modify it under the terms 4 | # of the GNU Lesser General Public License as published by the Free Software Foundation, either 5 | # version 3 of the License, or (at your option) any later version. 6 | # 7 | # C++ Plugin Framework is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 8 | # without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU Lesser General Public License for more details. 10 | # 11 | # You should have received a copy of the GNU Lesser General Public License along with C++ Plugin 12 | # Framework. If not, see . 13 | 14 | # -------------------------------------------------------------------------------------------------- 15 | # Custom (meta) targets 16 | # -------------------------------------------------------------------------------------------------- 17 | add_custom_target(all_unit_tests) 18 | 19 | # -------------------------------------------------------------------------------------------------- 20 | # Helper methods 21 | # -------------------------------------------------------------------------------------------------- 22 | function(CppPluginFramework_AddUnitTest) 23 | # Function parameters 24 | set(options) # Boolean parameters 25 | set(oneValueParams # Parameters with one value 26 | TEST_NAME 27 | ) 28 | set(multiValueParams) # Parameters with multiple values 29 | 30 | cmake_parse_arguments(PARAM "${options}" "${oneValueParams}" "${multiValueParams}" ${ARGN}) 31 | 32 | # Add test 33 | CppPluginFramework_AddTest(${ARGN} LABELS CPPPLUGINFRAMEWORK_UNIT_TESTS) 34 | 35 | # Add test to target "all_unit_tests" 36 | add_dependencies(all_unit_tests ${PARAM_TEST_NAME}) 37 | endfunction() 38 | 39 | # -------------------------------------------------------------------------------------------------- 40 | # Unit tests 41 | # -------------------------------------------------------------------------------------------------- 42 | add_subdirectory(PluginConfig) 43 | add_subdirectory(PluginInstanceConfig) 44 | add_subdirectory(PluginManagerConfig) 45 | add_subdirectory(Validation) 46 | add_subdirectory(VersionInfo) 47 | 48 | # -------------------------------------------------------------------------------------------------- 49 | # Code Coverage 50 | # -------------------------------------------------------------------------------------------------- 51 | if (CppPluginFramework_CodeCoverage MATCHES ON) 52 | # Prepare targets for code coverage 53 | SETUP_TARGET_FOR_COVERAGE( 54 | NAME ctest_coverage_all_unit_tests 55 | EXECUTABLE ctest -L CPPPLUGINFRAMEWORK_UNIT_TESTS 56 | DEPENDENCIES all_unit_tests 57 | PROJECT_PATH ${PROJECT_SOURCE_DIR} 58 | EXCLUDES ${CppPluginFramework_CodeCoverage_EXCLUDES} 59 | ) 60 | endif() 61 | -------------------------------------------------------------------------------- /code/tests/unit/PluginConfig/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This file is part of C++ Plugin Framework. 2 | # 3 | # C++ Plugin Framework is free software: you can redistribute it and/or modify it under the terms 4 | # of the GNU Lesser General Public License as published by the Free Software Foundation, either 5 | # version 3 of the License, or (at your option) any later version. 6 | # 7 | # C++ Plugin Framework is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 8 | # without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU Lesser General Public License for more details. 10 | # 11 | # You should have received a copy of the GNU Lesser General Public License along with C++ Plugin 12 | # Framework. If not, see . 13 | 14 | CppPluginFramework_AddUnitTest(TEST_NAME testPluginConfig) 15 | -------------------------------------------------------------------------------- /code/tests/unit/PluginInstanceConfig/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This file is part of C++ Plugin Framework. 2 | # 3 | # C++ Plugin Framework is free software: you can redistribute it and/or modify it under the terms 4 | # of the GNU Lesser General Public License as published by the Free Software Foundation, either 5 | # version 3 of the License, or (at your option) any later version. 6 | # 7 | # C++ Plugin Framework is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 8 | # without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU Lesser General Public License for more details. 10 | # 11 | # You should have received a copy of the GNU Lesser General Public License along with C++ Plugin 12 | # Framework. If not, see . 13 | 14 | CppPluginFramework_AddUnitTest(TEST_NAME testPluginInstanceConfig) 15 | -------------------------------------------------------------------------------- /code/tests/unit/PluginManagerConfig/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This file is part of C++ Plugin Framework. 2 | # 3 | # C++ Plugin Framework is free software: you can redistribute it and/or modify it under the terms 4 | # of the GNU Lesser General Public License as published by the Free Software Foundation, either 5 | # version 3 of the License, or (at your option) any later version. 6 | # 7 | # C++ Plugin Framework is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 8 | # without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU Lesser General Public License for more details. 10 | # 11 | # You should have received a copy of the GNU Lesser General Public License along with C++ Plugin 12 | # Framework. If not, see . 13 | 14 | CppPluginFramework_AddUnitTest(TEST_NAME testPluginManagerConfig ADDITIONAL_SOURCES TestData.qrc) 15 | -------------------------------------------------------------------------------- /code/tests/unit/PluginManagerConfig/TestData.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | TestData/dummyPlugin1 4 | TestData/dummyPlugin2 5 | 6 | 7 | -------------------------------------------------------------------------------- /code/tests/unit/PluginManagerConfig/TestData/dummyPlugin1: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /code/tests/unit/PluginManagerConfig/TestData/dummyPlugin2: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /code/tests/unit/Validation/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This file is part of C++ Plugin Framework. 2 | # 3 | # C++ Plugin Framework is free software: you can redistribute it and/or modify it under the terms 4 | # of the GNU Lesser General Public License as published by the Free Software Foundation, either 5 | # version 3 of the License, or (at your option) any later version. 6 | # 7 | # C++ Plugin Framework is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 8 | # without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU Lesser General Public License for more details. 10 | # 11 | # You should have received a copy of the GNU Lesser General Public License along with C++ Plugin 12 | # Framework. If not, see . 13 | 14 | CppPluginFramework_AddUnitTest(TEST_NAME testValidation ADDITIONAL_SOURCES TestData.qrc) 15 | -------------------------------------------------------------------------------- /code/tests/unit/Validation/TestData.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | TestData/file 4 | TestData/file.ext 5 | TestData/file.txt 6 | TestData/somePath/file 7 | TestData/somePath/file.ext 8 | TestData/somePath/file.txt 9 | 10 | 11 | -------------------------------------------------------------------------------- /code/tests/unit/Validation/TestData/file: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /code/tests/unit/Validation/TestData/file.ext: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /code/tests/unit/Validation/TestData/file.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /code/tests/unit/Validation/TestData/somePath/file: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /code/tests/unit/Validation/TestData/somePath/file.ext: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /code/tests/unit/Validation/TestData/somePath/file.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /code/tests/unit/VersionInfo/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This file is part of C++ Plugin Framework. 2 | # 3 | # C++ Plugin Framework is free software: you can redistribute it and/or modify it under the terms 4 | # of the GNU Lesser General Public License as published by the Free Software Foundation, either 5 | # version 3 of the License, or (at your option) any later version. 6 | # 7 | # C++ Plugin Framework is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 8 | # without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU Lesser General Public License for more details. 10 | # 11 | # You should have received a copy of the GNU Lesser General Public License along with C++ Plugin 12 | # Framework. If not, see . 13 | 14 | CppPluginFramework_AddUnitTest(TEST_NAME testVersionInfo) 15 | -------------------------------------------------------------------------------- /docs/Design.md: -------------------------------------------------------------------------------- 1 | # Design 2 | 3 | The plugin framework has two main uses: 4 | 5 | * Creating plugins 6 | * Plugin management 7 | 8 | 9 | ## Plugins 10 | 11 | Each plugin shall provide the following metadata: 12 | 13 | * Instance Name 14 | * Version 15 | * Description 16 | * Exported Interfaces 17 | 18 | Each plugin shall also provide some management features: 19 | 20 | * Configuration of the plugin 21 | * Injection and ejection of its dependencies 22 | * Stating and stopping of the plugin 23 | 24 | The main purpose of the plugin is to export an interface to a specific functionality. Each exposed interface can be accessed by using the `interface()` method. 25 | 26 | ![Class diagram for the Plugin class](Diagrams/ClassDiagrams/Plugin.svg "Class diagram for the Plugin class") 27 | 28 | 29 | ### Plugin Implementation 30 | 31 | All plugins shall inherit from the `IPlugin` interface. 32 | 33 | For most plugins it should be sufficient to use the `AbstractPlugin` base class. This class shall provide implementation of the *boilerplate* part of a plugin. The plugins that shall use it shall only need to implement the following: 34 | 35 | * Loading the plugin's configuration (optional feature) 36 | * Management of plugin's dependencies (optional feature) 37 | * Handling of plugin startup and shutdown procedures (for some plugins the default implementation should be good enough) 38 | * Exported interfaces (one or more) 39 | 40 | For more advanced uses it shall be necessary to use the `IPlugin` interface directly. 41 | 42 | 43 | ### Dynamically Linked Library 44 | 45 | A dynamically linked library shall be created for each plugin. This library shall provide a plugin factory with which individual plugin instances can be created. 46 | 47 | ![Class diagram for the PluginFactory class](Diagrams/ClassDiagrams/PluginFactory.svg "Class diagram for the PluginFactory class") 48 | 49 | 50 | ## Plugin Management 51 | 52 | Applications that want to use plugins need to use `PluginManager` class. Its purpose is to: 53 | 54 | * Load the plugins 55 | * Create and configure plugin instances 56 | * Provide access to the plugin instances 57 | * Inject dependencies into plugin instances 58 | * Start the plugins 59 | * Stop the plugins 60 | * Eject dependencies from plugin instances 61 | * Unload the plugins 62 | 63 | ![Class diagram for the PluginManagement class](Diagrams/ClassDiagrams/PluginManager.svg "Class diagram for the PluginManagement class") 64 | 65 | 66 | ### Configuration 67 | 68 | The configuration must be represented in *CppConfigFramework* format. 69 | 70 | ![Class diagram for the PluginManagerConfig class](Diagrams/ClassDiagrams/PluginManagerConfig.svg "Class diagram for the PluginManagerConfig class") 71 | 72 | Plugin management configuration shall be represented in a three level hierarchy: plugin manager, plugins, and plugin instances. 73 | 74 | The plugin manager shall provide the following information: 75 | 76 | * Plugin configurations 77 | * Plugin startup priorities (optional) 78 | 79 | Each plugin shall provide the following information: 80 | 81 | * Plugin file path 82 | * Version requirements (exact version or version range) 83 | * List of plugin instances (at least one) 84 | 85 | Each plugin instance needs to provide the following information: 86 | 87 | * Plugin instance name 88 | * Configuration (optional) 89 | * List of plugin dependencies (optional) 90 | 91 | 92 | ### Plugin Startup Workflow 93 | 94 | The application shall first load the configuration (from a *CppConfigFramework* file or equivalent *JSON Object*) and then the configured plugins shall be loaded with the plugin manager. Finally the application shall start the plugins. 95 | 96 | The plugins shall be started in the same order as defined in the configuration. First all the plugins from the *plugin startup priorities* shall be started (in the defined order) and then all the others (in no particular order). 97 | 98 | ![Plugin startup workflow](Diagrams/FlowCharts/StartupWorkflow.svg "Plugin startup workflow") 99 | 100 | 101 | ### Plugin Shutdown Workflow 102 | 103 | When the application no longer needs the plugins it shall first stop the plugins and then unload them. 104 | 105 | ![Plugin shutdown workflow](Diagrams/FlowCharts/ShutdownWorkflow.svg "Plugin shutdown workflow") 106 | -------------------------------------------------------------------------------- /docs/Diagrams/ClassDiagrams.plantuml: -------------------------------------------------------------------------------- 1 | @startuml Plugin 2 | IPlugin <|-- AbstractPlugin 3 | AbstractPlugin <|-- ExamplePlugin 4 | ExampleInterfaceA <|-- ExamplePlugin 5 | ExampleInterfaceB <|-- ExamplePlugin 6 | 7 | abstract class IPlugin { 8 | {abstract} +name() 9 | {abstract} +version() 10 | {abstract} +description() 11 | {abstract} +isInterfaceExported(interface) 12 | {abstract} +exportedInterfaces() 13 | {abstract} +loadConfig(config) 14 | {abstract} +injectDependency(plugin) 15 | {abstract} +ejectDependencies() 16 | {abstract} +isStarted() 17 | {abstract} +start() 18 | {abstract} +stop() 19 | +interface() 20 | } 21 | 22 | abstract class AbstractPlugin { 23 | +name() 24 | +version() 25 | +description() 26 | +isInterfaceExported(interface) 27 | +exportedInterfaces() 28 | +loadConfig(config) 29 | +injectDependency(plugin) 30 | +ejectDependencies() 31 | +isStarted() 32 | +start() 33 | +stop() 34 | +interface() 35 | {abstract} -onStart() 36 | {abstract} -onStop() 37 | } 38 | 39 | class ExampleInterfaceA { 40 | {abstract} +exampleMethodA() 41 | } 42 | 43 | class ExampleInterfaceB { 44 | {abstract} +exampleMethodB() 45 | } 46 | 47 | class ExamplePlugin { 48 | +injectDependency() 49 | +ejectDependencies() 50 | +exampleMethodA() 51 | +exampleMethodB() 52 | -onStart() 53 | -onStop() 54 | } 55 | @enduml 56 | 57 | @startuml PluginFactory 58 | IPluginFactory <|-- PluginFactory 59 | 60 | abstract class IPluginFactory { 61 | {abstract} +createInstance(instanceName) 62 | } 63 | 64 | class PluginFactory { 65 | +createInstance(instanceName) 66 | } 67 | @enduml 68 | 69 | @startuml PluginManager 70 | PluginManager -- Plugin 71 | 72 | class PluginManager { 73 | +load() 74 | +unload() 75 | +start() 76 | +stop() 77 | +pluginInstance(instanceName) 78 | +pluginInstanceNames() 79 | } 80 | 81 | class Plugin { 82 | +loadInstances(pluginConfig) 83 | -loadInstance(pluginFactory, instanceConfig) 84 | -checkVersion(pluginVersion, pluginConfig) 85 | } 86 | 87 | @startuml PluginManagerConfig 88 | PluginManagerConfig o-- PluginConfig 89 | PluginConfig o-- PluginInstanceConfig 90 | 91 | class PluginManagerConfig { 92 | +isValid() 93 | +pluginConfigs() 94 | +setPluginConfigs(pluginConfigs) 95 | +pluginStartupPriorities() 96 | +setPluginStartupPriorities(startupPriorities) 97 | } 98 | 99 | class PluginConfig { 100 | +isValid() 101 | +isExactVersion() 102 | +isVersionRange() 103 | +filePath() 104 | +setFilePath(filePath) 105 | +version() 106 | +setVersion(version) 107 | +minVersion() 108 | +setMinVersion(minVersion) 109 | +maxVersion() 110 | +setMaxVersion(maxVersion) 111 | +instanceConfigs() 112 | +setInstanceConfigs(instanceConfigs) 113 | } 114 | 115 | class PluginInstanceConfig { 116 | +isValid() 117 | +name() 118 | +setName(name) 119 | +config() 120 | +setConfig(config) 121 | +dependencies() 122 | +setDependencies(dependencies) 123 | } 124 | @enduml 125 | -------------------------------------------------------------------------------- /docs/Diagrams/ClassDiagrams/PluginFactory.svg: -------------------------------------------------------------------------------- 1 | IPluginFactorycreateInstance(instanceName)PluginFactorycreateInstance(instanceName) -------------------------------------------------------------------------------- /docs/Diagrams/ClassDiagrams/PluginManager.svg: -------------------------------------------------------------------------------- 1 | PluginManagerload()unload()start()stop()pluginInstance(instanceName)pluginInstanceNames()PluginloadInstances(pluginConfig)loadInstance(pluginFactory, instanceConfig)checkVersion(pluginVersion, pluginConfig) -------------------------------------------------------------------------------- /docs/Diagrams/FlowCharts.plantuml: -------------------------------------------------------------------------------- 1 | @startuml StartupWorkflow 2 | start 3 | 4 | :Load plugin manager configuration; 5 | -> Iterate over plugin configurations; 6 | 7 | repeat 8 | :Load plugin library; 9 | :Get plugin factory; 10 | -> Iterate over plugin instance configurations; 11 | 12 | repeat 13 | :Create plugin instance; 14 | :Configure plugin instance; 15 | :Check plugin instance version; 16 | :Store plugin instance; 17 | repeat while (All plugin instances loaded?) is (No) 18 | -> Yes; 19 | repeat while (All plugins loaded?) is (No) 20 | -> Yes; 21 | :Inject all dependencies; 22 | :Prepare startup order; 23 | :Start all plugin instances; 24 | stop 25 | 26 | @enduml 27 | 28 | @startuml ShutdownWorkflow 29 | start 30 | 31 | :Stop all plugin instances; 32 | :Eject all dependencies; 33 | :Destroy plugin instances; 34 | 35 | stop 36 | 37 | @enduml 38 | -------------------------------------------------------------------------------- /docs/Diagrams/FlowCharts/ShutdownWorkflow.svg: -------------------------------------------------------------------------------- 1 | Stop all plugin instancesEject all dependenciesDestroy plugin instances -------------------------------------------------------------------------------- /docs/Requirements.md: -------------------------------------------------------------------------------- 1 | # Requirements 2 | 3 | 4 | ## [R1] Plugin metadata 5 | 6 | Each plugin shall expose the following metadata: 7 | 8 | * Instance Name 9 | * Version 10 | * Description 11 | * Exported Interfaces 12 | 13 | For *Instance Name* see [[R2] Plugin instance name](#r2-plugin-instance-name). 14 | 15 | For *Version* see [[R3] Plugin version](#r3-plugin-version). 16 | 17 | *Description* is an optional text that describes the purpose of the plugin and/or any other information about the plugin. 18 | 19 | For *Exported Interfaces* see [[R4] Exported interfaces](#r4-exported-interfaces). 20 | 21 | 22 | ## [R2] Plugin instance name 23 | 24 | Represents the name of plugin instance. Allowed characters are: 25 | 26 | * All letters of the English alphabet (A-Z, a-z) 27 | * Numbers (0-9) 28 | * Period character (U+002E) 29 | * Hyphen character (U+002D) 30 | * Underscore character (U+005F) 31 | 32 | The plugin instance name shall always start with a letter. 33 | 34 | 35 | ## [R3] Plugin version 36 | 37 | Each plugin shall have a version which shall be in one of the following formats: 38 | 39 | * `MAJOR.MINOR.PATCH` 40 | * `MAJOR.MINOR.PATCH-DEV` 41 | 42 | 43 | ### Major version 44 | 45 | `MAJOR` part of the version shall be a non-negative integer. This part of the version shall be incremented every time when there is a significant change in the architecture and/or design of the plugin. 46 | 47 | 48 | ### Minor version 49 | 50 | `MINOR` part of the version shall be a non-negative integer. This part of the version shall be incremented every time when API, ABI, or behavior of the plugin is changed. 51 | 52 | 53 | ### Patch version 54 | 55 | `PATCH` part of the version shall be a non-negative integer. This part of the version shall be incremented every time when a new release is made and the API, ABI, and (intended) behavior of the plugin is not changed. 56 | 57 | This is generally meant for bug fix releases. 58 | 59 | 60 | ### Development version 61 | 62 | `DEV` optional part of the version shall be a string that can be used for example to specify the type of and number of the release (`alpha3`, `beta1`, `RC5`, `bugfix-123`, `IntegrationTest-20180102-1` etc.). All characters are allowed. 63 | 64 | 65 | ## [R4] Exported interfaces 66 | 67 | Each plugin shall export at least one interface. A plugin shall make it possible for its users to query if a specific interface is exported by the plugin. 68 | 69 | 70 | ## [R5] Plugin dependencies 71 | 72 | A plugin shall optionally have dependencies to other plugins. The dependent plugins shall be injected to the plugin before it is started. A dependent plugin shall only be ejected from a plugin when both plugins are stopped. 73 | 74 | 75 | ## [R6] Plugin instance 76 | 77 | A plugin shall enable creation of multiple instances of the same plugin, but only if they have different names! 78 | 79 | 80 | ## [R7] Configuration 81 | 82 | Loading of plugins shall done according to its configuration which shall be stored in *CppConfigFramework* format. 83 | 84 | The configuration shall consist of two parts: 85 | 86 | * Plugin configurations 87 | * Plugin startup priorities 88 | 89 | 90 | ### Plugin configuration 91 | 92 | This part of the configuration shall be used to define which plugins need to be loaded and which instances of these plugins need to be created. It shall contain the following information for each plugin: 93 | 94 | * Plugin file path 95 | * Version requirements 96 | * Comment 97 | * Plugin instances: 98 | 99 | * Plugin instance name 100 | * Configuration 101 | * Comment (optional, it shall not be processed) 102 | * Plugin dependencies 103 | 104 | *Plugin file path* shall point to plugin file (dynamic library). It shall be either an absolute or relative path and it shall be possible to reference environment variables. 105 | 106 | *Version requirements* shall specify either a specific version or a version range of the plugin which needs to be loaded. For more details about versioning see [[R3] Plugin version](#r3-plugin-version). 107 | 108 | *Comment* is optional and it shall not be processed. It can for example be used to explain the purpose of this plugin. 109 | 110 | In case a version range is defined then the following formula shall be used: 111 | 112 | ``` 113 | min_version ≤ version < max_version 114 | ``` 115 | 116 | 117 | #### Plugin instances 118 | 119 | A plugin shall configure at least one plugin instance to be created. 120 | 121 | *Plugin instance name* shall define the name of the plugin instance. See [[R2] Plugin instance name](#r2-plugin-instance-name). 122 | 123 | *Configuration* shall be optional for a plugin instance and it shall be stored in *CppConfigFramework* format. 124 | 125 | *Comment* is optional and it shall not be processed. It can for example be used to explain the purpose of this plugin instance. 126 | 127 | A plugin instance shall optionally define its dependencies to other plugins. *Plugin instance dependencies* shall contain a list of references (plugin instance names) to another plugin instances. 128 | 129 | 130 | ### Plugin startup priorities 131 | 132 | This is an optional part of the configuration and it shall be used to define the order in which the referenced plugins (by plugin instance names) need to be started. 133 | 134 | All unreferenced plugins shall be started after these plugins in no particular order. 135 | 136 | 137 | ## [R8] Plugin Startup 138 | 139 | The following startup procedure shall be implemented: 140 | 141 | 1. To be able to start the plugins they shall first have to be loaded 142 | 2. For each plugin all of their plugin instances shall be created and configured 143 | 3. Only when all the plugin instances are created it shall be possible to inject the configured dependencies into the plugin instances 144 | 4. At this point the plugins can all be started in the configured startup order 145 | 5. The application shall now be able to use the plugins 146 | 147 | 148 | ## [R9] Plugin Shutdown 149 | 150 | The following shutdown procedure shall be implemented: 151 | 152 | 1. Plugin instances shall be stopped in the reverse order as they were started 153 | 2. All dependencies shall be ejected 154 | 3. All plugin instances shall be destroyed 155 | --------------------------------------------------------------------------------