##// END OF EJS Templates
Merge branch 'feature/InitPlugin' into develop
Alexandre Leroux -
r72:538f97c38e97 merge
parent child
Show More
@@ -0,0 +1,34
1 #ifndef SCIQLOP_PLUGINMANAGER_H
2 #define SCIQLOP_PLUGINMANAGER_H
3
4 #include <Common/spimpl.h>
5
6 #include <QLoggingCategory>
7
8 class QDir;
9
10 Q_DECLARE_LOGGING_CATEGORY(LOG_PluginManager)
11
12 /**
13 * @brief The PluginManager class aims to handle the plugins loaded dynamically into SciQLop.
14 */
15 class PluginManager {
16 public:
17 explicit PluginManager();
18
19 /**
20 * Loads plugins into SciQlop. The loaded plugins are those located in the directory passed in
21 * parameter
22 * @param pluginDir the directory containing the plugins
23 */
24 void loadPlugins(const QDir &pluginDir);
25
26 /// @returns the number of plugins loaded
27 int nbPluginsLoaded() const noexcept;
28
29 private:
30 class PluginManagerPrivate;
31 spimpl::unique_impl_ptr<PluginManagerPrivate> impl;
32 };
33
34 #endif // SCIQLOP_PLUGINMANAGER_H
@@ -0,0 +1,118
1 #include <Plugin/PluginManager.h>
2
3 #include <Plugin/IPlugin.h>
4
5 #include <QDir>
6 #include <QLibrary>
7 #include <QPluginLoader>
8
9 Q_LOGGING_CATEGORY(LOG_PluginManager, "PluginManager")
10
11 namespace {
12
13 /// Key for retrieving metadata of the plugin
14 const auto PLUGIN_METADATA_KEY = QStringLiteral("MetaData");
15
16 /// Key for retrieving the name of the plugin in its metadata
17 const auto PLUGIN_NAME_KEY = QStringLiteral("name");
18
19 /// Helper to state the plugin loading operation
20 struct LoadPluginState {
21 explicit LoadPluginState(const QString &pluginPath)
22 : m_PluginPath{pluginPath}, m_Valid{true}, m_ErrorMessage{}
23 {
24 }
25
26 void log() const
27 {
28 if (m_Valid) {
29 qCDebug(LOG_PluginManager())
30 << QObject::tr("File '%1' has been loaded as a plugin").arg(m_PluginPath);
31 }
32 else {
33 qCWarning(LOG_PluginManager())
34 << QObject::tr("File '%1' can't be loaded as a plugin: %2")
35 .arg(m_PluginPath)
36 .arg(m_ErrorMessage);
37 }
38 }
39
40 void setError(const QString &errorMessage)
41 {
42 m_Valid = false;
43 m_ErrorMessage = errorMessage;
44 }
45
46 QString m_PluginPath;
47 bool m_Valid;
48 QString m_ErrorMessage;
49 };
50
51 } // namespace
52
53 struct PluginManager::PluginManagerPrivate {
54 /**
55 * Loads a single plugin into SciQlop. The method has no effect if the plugin is malformed (e.g.
56 * wrong library type, missing metadata, etc.)
57 * @param pluginPath the path to the plugin library.
58 */
59 void loadPlugin(const QString &pluginPath)
60 {
61 qCDebug(LOG_PluginManager())
62 << QObject::tr("Attempting to load file '%1' as a plugin").arg(pluginPath);
63
64 LoadPluginState loadState{pluginPath};
65
66 if (QLibrary::isLibrary(pluginPath)) {
67 QPluginLoader pluginLoader{pluginPath};
68
69 // Retrieving the plugin name to check if it can be loaded (i.e. no plugin with the same
70 // name has been registered yet)
71 auto metadata = pluginLoader.metaData().value(PLUGIN_METADATA_KEY).toObject();
72 auto pluginName = metadata.value(PLUGIN_NAME_KEY).toString();
73
74 if (pluginName.isEmpty()) {
75 loadState.setError(QObject::tr("empty file name"));
76 }
77 else if (m_RegisteredPlugins.contains(pluginName)) {
78 loadState.setError(QObject::tr("name '%1' already registered").arg(pluginName));
79 }
80 else {
81 if (auto pluginInstance = qobject_cast<IPlugin *>(pluginLoader.instance())) {
82 pluginInstance->initialize();
83 m_RegisteredPlugins.insert(pluginName, pluginPath);
84 }
85 else {
86 loadState.setError(QObject::tr("the file is not a Sciqlop plugin"));
87 }
88 }
89 }
90 else {
91 loadState.setError(QObject::tr("the file is not a library"));
92 }
93
94 // Log loading result
95 loadState.log();
96 }
97
98 /// Registered plugins (key: plugin name, value: plugin path)
99 QHash<QString, QString> m_RegisteredPlugins;
100 };
101
102 PluginManager::PluginManager() : impl{spimpl::make_unique_impl<PluginManagerPrivate>()}
103 {
104 }
105
106 void PluginManager::loadPlugins(const QDir &pluginDir)
107 {
108 // Load plugins
109 auto pluginInfoList = pluginDir.entryInfoList(QDir::Files, QDir::Name);
110 for (auto pluginInfo : qAsConst(pluginInfoList)) {
111 impl->loadPlugin(pluginInfo.absoluteFilePath());
112 }
113 }
114
115 int PluginManager::nbPluginsLoaded() const noexcept
116 {
117 return impl->m_RegisteredPlugins.size();
118 }
1 NO CONTENT: new file 100644, binary diff hidden
NO CONTENT: new file 100644, binary diff hidden
1 NO CONTENT: new file 100644, binary diff hidden
NO CONTENT: new file 100644, binary diff hidden
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
1 NO CONTENT: new file 100644, binary diff hidden
NO CONTENT: new file 100644, binary diff hidden
1 NO CONTENT: new file 100644, binary diff hidden
NO CONTENT: new file 100644, binary diff hidden
1 NO CONTENT: new file 100644, binary diff hidden
NO CONTENT: new file 100644, binary diff hidden
1 NO CONTENT: new file 100644, binary diff hidden
NO CONTENT: new file 100644, binary diff hidden
1 NO CONTENT: new file 100644, binary diff hidden
NO CONTENT: new file 100644, binary diff hidden
@@ -0,0 +1,81
1 #include <Plugin/PluginManager.h>
2
3 #include <QObject>
4 #include <QtTest>
5
6 #include <QtDebug>
7
8 namespace {
9
10 /// Path for the tests
11 const auto TESTS_RESOURCES_PATH = QFileInfo{
12 QString{SCQCORE_TESTS_RESOURCES_PATH},
13 "Plugin/TestPluginManager"}.absoluteFilePath();
14
15 QString pluginDirPath(const QString &pluginDirName)
16 {
17 return QFileInfo{TESTS_RESOURCES_PATH, pluginDirName}.absoluteFilePath();
18 }
19
20 } // namespace
21
22 class TestPluginManager : public QObject {
23 Q_OBJECT
24 private slots:
25 /// Defines data for plugin loading
26 /// @sa testLoadPlugin()
27 void testLoadPlugin_data();
28
29 /// Tests plugin loading
30 void testLoadPlugin();
31 };
32
33 void TestPluginManager::testLoadPlugin_data()
34 {
35 // ////////////// //
36 // Test structure //
37 // ////////////// //
38
39 // Name of directory containing the plugins
40 QTest::addColumn<QString>("pluginDirName");
41 // Number of loaded plugins expected
42 QTest::addColumn<int>("nbPluginsLoaded");
43
44 // ////////// //
45 // Test cases //
46 // ////////// //
47
48 QTest::newRow("Valid plugin") << QStringLiteral("Test_ValidPlugin") << 1;
49
50 // Two different plugins
51 QTest::newRow("Valid plugins") << QStringLiteral("Test_ValidPlugins") << 2;
52
53 // Two plugins with the same name: we expect that only one is loaded
54 QTest::newRow("Duplicated plugins") << QStringLiteral("Test_DuplicatedPlugins") << 1;
55
56 QTest::newRow("Invalid plugin (not a DLL)") << QStringLiteral("Test_InvalidFileType") << 0;
57 QTest::newRow("Invalid plugin (not a SciQlop DLL)")
58 << QStringLiteral("Test_NotSciqlopDll") << 0;
59 QTest::newRow("Invalid plugin (missing metadata)")
60 << QStringLiteral("Test_MissingPluginMetadata") << 0;
61 }
62
63 void TestPluginManager::testLoadPlugin()
64 {
65 QFETCH(QString, pluginDirName);
66 QFETCH(int, nbPluginsLoaded);
67
68 // Generates plugin dir
69 auto pluginDir = QDir{pluginDirPath(pluginDirName)};
70 QVERIFY(pluginDir.exists());
71
72 // Load plugins
73 PluginManager pluginManager{};
74 pluginManager.loadPlugins(pluginDir);
75
76 // Check the number of plugins loaded
77 QCOMPARE(pluginManager.nbPluginsLoaded(), nbPluginsLoaded);
78 }
79
80 QTEST_MAIN(TestPluginManager)
81 #include "TestPluginManager.moc"
@@ -0,0 +1,45
1 ## plugin - CMakeLists.txt
2 STRING(TOLOWER ${CMAKE_PROJECT_NAME} LIBRARY_PREFFIX)
3 SET(SQPPLUGIN_LIBRARY_NAME "${LIBRARY_PREFFIX}_plugin${DEBUG_SUFFIX}")
4 SET(INCLUDES_DIR "${CMAKE_CURRENT_SOURCE_DIR}/include")
5
6 # Include plugin directory
7 INCLUDE_DIRECTORIES("${INCLUDES_DIR}")
8
9 #
10 # Find Qt modules
11 #
12 SCIQLOP_FIND_QT(Core)
13
14 #
15 # Compile the library
16 #
17 FILE (GLOB_RECURSE MODULE_SOURCES
18 ${INCLUDES_DIR}/*.h)
19
20 ADD_LIBRARY(${SQPPLUGIN_LIBRARY_NAME} ${MODULE_SOURCES})
21
22 # Add the files to the list of files to be analyzed
23 LIST(APPEND CHECKSTYLE_INPUT_FILES ${MODULE_SOURCES})
24 SCIQLOP_SET_TO_PARENT_SCOPE(CHECKSTYLE_INPUT_FILES)
25 # Vera++ exclusion files
26 #LIST(APPEND CHECKSTYLE_EXCLUSION_FILES ${CMAKE_CURRENT_SOURCE_DIR}/path/to/exclusionFiles.tcl)
27 SCIQLOP_SET_TO_PARENT_SCOPE(CHECKSTYLE_EXCLUSION_FILES)
28
29 #
30 # Set the files that must be formatted by clang-format.
31 #
32 LIST (APPEND FORMATTING_INPUT_FILES ${MODULE_SOURCES})
33 SCIQLOP_SET_TO_PARENT_SCOPE(FORMATTING_INPUT_FILES)
34
35 #
36 # Set the directories that doxygen must browse to generate the
37 # documentation.
38 #
39 # Source directories:
40 LIST (APPEND DOXYGEN_INPUT_DIRS "${CMAKE_CURRENT_SOURCE_DIR}/docs")
41 LIST (APPEND DOXYGEN_INPUT_DIRS "${CMAKE_CURRENT_SOURCE_DIR}/include")
42 SCIQLOP_SET_TO_PARENT_SCOPE(DOXYGEN_INPUT_DIRS)
43 # Source directories to exclude from the documentation generation
44 #LIST (APPEND DOXYGEN_EXCLUDE_PATTERNS "${CMAKE_CURRENT_SOURCE_DIR}/path/to/subdir/*")
45 SCIQLOP_SET_TO_PARENT_SCOPE(DOXYGEN_EXCLUDE_PATTERNS)
@@ -0,0 +1,12
1 # - Try to find sciqlop-plugin
2 # Once done this will define
3 # SCIQLOP-PLUGIN_FOUND - System has sciqlop-plugin
4 # SCIQLOP-PLUGIN_INCLUDE_DIR - The sciqlop-plugin include directories
5
6 if(SCIQLOP-PLUGIN_FOUND)
7 return()
8 endif(SCIQLOP-PLUGIN_FOUND)
9
10 set(SCIQLOP-PLUGIN_INCLUDE_DIR ${sciqlop-plugin_DIR}/../include)
11
12 set(SCIQLOP-PLUGIN_FOUND TRUE)
@@ -0,0 +1,20
1 #ifndef SCIQLOP_IPLUGIN_H
2 #define SCIQLOP_IPLUGIN_H
3
4 #include <QString>
5 #include <QtPlugin>
6
7 /**
8 * @brief Interface for a plugin
9 */
10 class IPlugin {
11 public:
12 virtual ~IPlugin() = default;
13
14 /// Initializes the plugin
15 virtual void initialize() = 0;
16 };
17
18 Q_DECLARE_INTERFACE(IPlugin, "sciqlop.plugin.IPlugin")
19
20 #endif // SCIQLOP_IPLUGIN_H
@@ -21,6 +21,10 endif(BUILD_TESTS)
21 #
21 #
22 # Compile the diffents modules
22 # Compile the diffents modules
23 #
23 #
24 set(sciqlop-plugin_DIR "${CMAKE_SOURCE_DIR}/plugin/cmake")
25 set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${sciqlop-plugin_DIR}")
26 ADD_SUBDIRECTORY("${CMAKE_SOURCE_DIR}/plugin")
27
24 set(sciqlop-core_DIR "${CMAKE_SOURCE_DIR}/core/cmake")
28 set(sciqlop-core_DIR "${CMAKE_SOURCE_DIR}/core/cmake")
25 set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${sciqlop-core_DIR}")
29 set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${sciqlop-core_DIR}")
26 ADD_SUBDIRECTORY("${CMAKE_SOURCE_DIR}/core")
30 ADD_SUBDIRECTORY("${CMAKE_SOURCE_DIR}/core")
@@ -16,7 +16,11 CONFIGURE_FILE("${CMAKE_CURRENT_SOURCE_DIR}/resources/Version.h.in"
16 "${INCLUDES_DIR}/Version.h")
16 "${INCLUDES_DIR}/Version.h")
17 CONFIGURE_FILE("${CMAKE_CURRENT_SOURCE_DIR}/resources/Version.cpp.in"
17 CONFIGURE_FILE("${CMAKE_CURRENT_SOURCE_DIR}/resources/Version.cpp.in"
18 "${SOURCES_DIR}/Version.cpp")
18 "${SOURCES_DIR}/Version.cpp")
19
19
20 # Find dependent modules
21 find_package(sciqlop-plugin)
22 INCLUDE_DIRECTORIES(${SCIQLOP-PLUGIN_INCLUDE_DIR})
23
20 #
24 #
21 # Find Qt modules
25 # Find Qt modules
22 #
26 #
@@ -69,6 +73,10 IF(BUILD_TESTS)
69 FILE (GLOB_RECURSE TESTS_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/Test*.h)
73 FILE (GLOB_RECURSE TESTS_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/Test*.h)
70 SET( TEST_LIBRARIES ${SQPCORE_LIBRARY_NAME})
74 SET( TEST_LIBRARIES ${SQPCORE_LIBRARY_NAME})
71
75
76 # Set tests resources path
77 SET(RESOURCES_PATH ${CMAKE_CURRENT_SOURCE_DIR}/tests-resources/)
78 add_definitions(-DSCQCORE_TESTS_RESOURCES_PATH="${RESOURCES_PATH}")
79
72 SET(TARGETS_COV)
80 SET(TARGETS_COV)
73 FOREACH( testFile ${TESTS_SOURCES} )
81 FOREACH( testFile ${TESTS_SOURCES} )
74 GET_FILENAME_COMPONENT( testDirectory ${testFile} DIRECTORY )
82 GET_FILENAME_COMPONENT( testDirectory ${testFile} DIRECTORY )
General Comments 0
You need to be logged in to leave comments. Login now