##// END OF EJS Templates
Updated meson build files and force plugin unload when closing app...
jeandet -
r72:a0c89a70c83c
parent child
Show More
@@ -1,41 +1,42
1 1 #ifndef SCIQLOP_PLUGINMANAGER_H
2 2 #define SCIQLOP_PLUGINMANAGER_H
3 3
4 4 #include "CoreGlobal.h"
5 5
6 6 #include <Common/spimpl.h>
7 7
8 8 #include <QLoggingCategory>
9 9
10 10 class QDir;
11 11
12 12 Q_DECLARE_LOGGING_CATEGORY(LOG_PluginManager)
13 13
14 14 /**
15 15 * @brief The PluginManager class aims to handle the plugins loaded dynamically into SciQLop.
16 16 */
17 17 class SCIQLOP_CORE_EXPORT PluginManager {
18 18 public:
19 19 explicit PluginManager();
20 ~PluginManager();
20 21
21 22 /**
22 23 * Loads plugins into SciQlop. The loaded plugins are those located in the directory passed in
23 24 * parameter
24 25 * @param pluginDir the directory containing the plugins
25 26 */
26 27 void loadPlugins(const QDir &pluginDir);
27 28
28 29 /**
29 30 * Loads static plugins into SciQlop. SciQLOP supports statically linked plugins.
30 31 */
31 32 void loadStaticPlugins();
32 33
33 34 /// @returns the number of plugins loaded
34 35 int nbPluginsLoaded() const noexcept;
35 36
36 37 private:
37 38 struct PluginManagerPrivate;
38 39 spimpl::unique_impl_ptr<PluginManagerPrivate> impl;
39 40 };
40 41
41 42 #endif // SCIQLOP_PLUGINMANAGER_H
@@ -1,122 +1,104
1 1
2 2 catalogicpp_dep = dependency('catalogicpp', required : true, fallback:['catalogicpp','catalogicpp_dep'])
3 3 pybind11_dep = dependency('pybind11', required : true, fallback:['pybind11','pybind11_dep'])
4
4 timeseries_dep = dependency('TimeSeries', required : true, fallback:['TimeSeries','time_series_dep'])
5 5
6 6 core_moc_headers = [
7 7 './include/Common/containers.h',
8 8 './include/Common/StringUtils.h',
9 9 './include/Common/Numeric.h',
10 10 './include/Common/spimpl.h',
11 11 './include/Common/DateUtils.h',
12 12 './include/Common/MimeTypesDef.h',
13 13 './include/Common/SignalWaiter.h',
14 14 './include/Common/deprecate.h',
15 15 './include/Common/debug.h',
16 16 './include/Common/MetaTypes.h',
17 17 './include/Common/cpp_utils.h',
18 18 './include/Common/SortUtils.h',
19 './include/Data/DataSeries.h',
20 './include/Data/DataSeriesIterator.h',
21 19 './include/Data/DateTimeRangeHelper.h',
22 './include/Data/SpectrogramSeries.h',
20 './include/Data/ScalarTimeSerie.h',
23 21 './include/Data/DateTimeRange.h',
24 './include/Data/VectorSeries.h',
25 22 './include/Data/DataProviderParameters.h',
26 './include/Data/ArrayData.h',
27 './include/Data/Unit.h',
28 './include/Data/ScalarSeries.h',
23 './include/Data/VectorTimeSerie.h',
29 24 './include/Data/SqpIterator.h',
30 25 './include/Data/IDataProvider.h',
31 './include/Data/DataSeriesUtils.h',
32 './include/Data/OptionalAxis.h',
33 './include/Data/IDataSeries.h',
26 './include/Data/SpectrogramTimeSerie.h',
34 27 './include/Data/DataSeriesType.h',
35 './include/Data/ArrayDataIterator.h',
36 './include/Data/DataSeriesMergeHelper.h',
37 28 './include/CoreGlobal.h',
38 29 './include/Network/NetworkController.h',
39 30 './include/Network/Response.h',
40 31 './include/Network/Downloader.h',
41 32 './include/Settings/SqpSettingsDefs.h',
42 33 './include/Settings/ISqpSettingsBindable.h',
43 34 './include/DataSource/DataSourceController.h',
44 35 './include/DataSource/DataSourceItem.h',
45 36 './include/DataSource/DataSourceItemAction.h',
46 37 './include/DataSource/DataSourceItemMergeHelper.h',
47 38 './include/Time/TimeController.h',
48 39 './include/PluginManager/PluginManager.h',
49 40 './include/Version.h',
50 41 './include/Catalogue/CatalogueController.h',
51 42 './include/Plugin/IPlugin.h',
52 43 './include/Variable/VariableModel2.h',
53 './include/Variable/Variable.h',
54 './include/Variable/VariableCacheStrategy.h',
55 44 './include/Variable/VariableController2.h',
56 './include/Variable/VariableCacheStrategyFactory.h',
45 './include/Variable/Variable2.h',
57 46 './include/Variable/VariableSynchronizationGroup2.h',
58 './include/Variable/ProportionalCacheStrategy.h',
59 47 './include/Variable/private/VCTransaction.h',
60 './include/Variable/SingleThresholdCacheStrategy.h',
61 './include/Visualization/VisualizationController.h'
62 ]
48 './include/Visualization/VisualizationController.h']
63 49
64 50
65 51 core_moc_sources = ['src/Network/Downloader.cpp']
66 52
67 53
68 54 core_moc_files = qt5.preprocess(moc_headers : core_moc_headers, moc_sources: core_moc_sources)
69 55
70 56 core_sources = [
71 57 './src/Common/MimeTypesDef.cpp',
72 58 './src/Common/SignalWaiter.cpp',
73 59 './src/Common/DateUtils.cpp',
74 60 './src/Common/StringUtils.cpp',
75 './src/Data/VectorSeries.cpp',
76 './src/Data/ScalarSeries.cpp',
77 './src/Data/OptionalAxis.cpp',
78 './src/Data/DataSeriesIterator.cpp',
79 './src/Data/SpectrogramSeries.cpp',
80 './src/Data/ArrayDataIterator.cpp',
81 './src/Data/DataSeriesUtils.cpp',
82 61 './src/Network/Downloader.cpp',
83 62 './src/Network/NetworkController.cpp',
84 63 './src/Settings/SqpSettingsDefs.cpp',
85 64 './src/DataSource/DataSourceItemAction.cpp',
86 65 './src/DataSource/DataSourceItemMergeHelper.cpp',
87 66 './src/DataSource/DataSourceItem.cpp',
88 67 './src/DataSource/DataSourceController.cpp',
89 68 './src/Time/TimeController.cpp',
90 69 './src/PluginManager/PluginManager.cpp',
91 './src/pybind11_wrappers/CatalogWrappers.cpp',
92 #'./src/pybind11_wrappers/QtWrappers.cpp',
93 #'./src/pybind11_wrappers/CoreWrappers.cpp',
94 70 './src/Version.cpp',
95 71 './src/Catalogue/CatalogueController.cpp',
96 './src/Variable/Variable.cpp',
97 72 './src/Variable/VariableSynchronizationGroup2.cpp',
73 './src/Variable/Variable2.cpp',
98 74 './src/Variable/VariableController2.cpp',
99 75 './src/Variable/VariableModel2.cpp',
100 76 './src/Visualization/VisualizationController.cpp']
101 77
102 78
103 79 core_inc = include_directories(['include', 'include/Plugin'])
104 80
105 81 sciqlop_core_lib = library('sciqlopcore',
106 82 core_sources,
107 83 core_moc_files,
108 84 cpp_args : '-DCORE_LIB',
109 85 include_directories : core_inc,
110 dependencies : [qt5core, qt5network, catalogicpp_dep, pybind11_dep],
86 dependencies : [qt5core, qt5network, catalogicpp_dep, pybind11_dep, timeseries_dep],
111 87 install : true
112 88 )
113 89
114 90
115 91 sciqlop_core = declare_dependency(link_with : sciqlop_core_lib,
116 92 include_directories : core_inc,
117 dependencies : [qt5core, qt5network, catalogicpp_dep, pybind11_dep])
93 dependencies : [qt5core, qt5network, catalogicpp_dep, pybind11_dep, timeseries_dep])
118 94
119 95
96 [
97 './src/pybind11_wrappers/CatalogWrappers.cpp',
98 './src/pybind11_wrappers/QtWrappers.cpp',
99 './src/pybind11_wrappers/CoreWrappers.cpp'
100 ]
101
120 102
121 103 subdir('tests')
122 104
@@ -1,137 +1,154
1 1 #include <PluginManager/PluginManager.h>
2 2
3 3 #include <Plugin/IPlugin.h>
4 4
5 5 #include <QDir>
6 6 #include <QLibrary>
7 7 #include <QPluginLoader>
8 8
9 9 Q_LOGGING_CATEGORY(LOG_PluginManager, "PluginManager")
10 10
11 11 namespace {
12 12
13 13 /// Key for retrieving metadata of the plugin
14 14 const auto PLUGIN_METADATA_KEY = QStringLiteral("MetaData");
15 15
16 16 /// Key for retrieving the name of the plugin in its metadata
17 17 const auto PLUGIN_NAME_KEY = QStringLiteral("name");
18 18
19 19 /// Helper to state the plugin loading operation
20 20 struct LoadPluginState {
21 21 explicit LoadPluginState(const QString &pluginPath)
22 22 : m_PluginPath{pluginPath}, m_Valid{true}, m_ErrorMessage{}
23 23 {
24 24 }
25 25
26 26 void log() const
27 27 {
28 28 if (m_Valid) {
29 29 qCDebug(LOG_PluginManager())
30 30 << QObject::tr("File '%1' has been loaded as a plugin").arg(m_PluginPath);
31 31 }
32 32 else {
33 33 qCWarning(LOG_PluginManager())
34 34 << QObject::tr("File '%1' can't be loaded as a plugin: %2")
35 35 .arg(m_PluginPath)
36 36 .arg(m_ErrorMessage);
37 37 }
38 38 }
39 39
40 40 void setError(const QString &errorMessage)
41 41 {
42 42 m_Valid = false;
43 43 m_ErrorMessage = errorMessage;
44 44 }
45 45
46 46 QString m_PluginPath;
47 47 bool m_Valid;
48 48 QString m_ErrorMessage;
49 49 };
50 50
51 51 } // namespace
52 52
53 53 struct PluginManager::PluginManagerPrivate {
54 54 /**
55 55 * Loads a single plugin into SciQlop. The method has no effect if the plugin is malformed (e.g.
56 56 * wrong library type, missing metadata, etc.)
57 57 * @param pluginPath the path to the plugin library.
58 58 */
59 59 void loadPlugin(const QString &pluginPath)
60 60 {
61 61 qCDebug(LOG_PluginManager())
62 62 << QObject::tr("Attempting to load file '%1' as a plugin").arg(pluginPath);
63 63
64 64 LoadPluginState loadState{pluginPath};
65 65
66 66 if (QLibrary::isLibrary(pluginPath)) {
67 67 QPluginLoader pluginLoader{pluginPath};
68 68
69 69 // Retrieving the plugin name to check if it can be loaded (i.e. no plugin with the same
70 70 // name has been registered yet)
71 71 auto metadata = pluginLoader.metaData().value(PLUGIN_METADATA_KEY).toObject();
72 72 auto pluginName = metadata.value(PLUGIN_NAME_KEY).toString();
73 73
74 74 if (pluginName.isEmpty()) {
75 75 loadState.setError(QObject::tr("empty file name"));
76 76 }
77 77 else if (m_RegisteredPlugins.contains(pluginName)) {
78 78 loadState.setError(QObject::tr("name '%1' already registered").arg(pluginName));
79 79 }
80 80 else {
81 if (auto pluginInstance = qobject_cast<IPlugin *>(pluginLoader.instance())) {
82 pluginInstance->initialize();
81 if (auto plugin = pluginLoader.instance()) {
82 qobject_cast<IPlugin *>(plugin)->initialize();
83 83 m_RegisteredPlugins.insert(pluginName, pluginPath);
84 m_LoadedPlugins.insert(pluginName, plugin);
84 85 }
85 86 else {
86 87 loadState.setError(QObject::tr("the file is not a Sciqlop plugin"));
87 88 }
88 89 }
89 90 }
90 91 else {
91 92 loadState.setError(QObject::tr("the file is not a library"));
92 93 }
93 94
94 95 // Log loading result
95 96 loadState.log();
96 97 }
97 98
98 99 void loadStaticPlugins()
99 100 {
100 101 for (QObject *plugin : QPluginLoader::staticInstances()) {
101 102 qobject_cast<IPlugin *>(plugin)->initialize();
102 103 m_RegisteredPlugins.insert(plugin->metaObject()->className(), "StaticPlugin");
104 m_LoadedPlugins.insert(plugin->metaObject()->className(), plugin);
103 105 }
104 106 }
107 void clearAllPlugins()
108 {
109 for(auto plugin:m_LoadedPlugins)
110 {
111 delete plugin;
112 }
113 m_LoadedPlugins.clear();
114 m_RegisteredPlugins.clear();
115 }
105 116
106 117 /// Registered plugins (key: plugin name, value: plugin path)
107 118 QHash<QString, QString> m_RegisteredPlugins;
119 QHash<QString,QObject*> m_LoadedPlugins;
108 120 };
109 121
110 122 PluginManager::PluginManager() : impl{spimpl::make_unique_impl<PluginManagerPrivate>()}
111 123 {
112 124 }
113 125
126 PluginManager::~PluginManager()
127 {
128 impl->clearAllPlugins();
129 }
130
114 131 void PluginManager::loadPlugins(const QDir &pluginDir)
115 132 {
116 133 // Load plugins
117 134 auto pluginInfoList
118 135 = pluginDir.entryInfoList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot, QDir::Name);
119 136 for (auto entryInfo : qAsConst(pluginInfoList)) {
120 137 if (entryInfo.isDir()) {
121 138 this->loadPlugins(QDir{entryInfo.absoluteFilePath()});
122 139 }
123 140 else if (QLibrary::isLibrary(entryInfo.absoluteFilePath())) {
124 141 impl->loadPlugin(entryInfo.absoluteFilePath());
125 142 }
126 143 }
127 144 }
128 145
129 146 void PluginManager::loadStaticPlugins()
130 147 {
131 148 impl->loadStaticPlugins();
132 149 }
133 150
134 151 int PluginManager::nbPluginsLoaded() const noexcept
135 152 {
136 153 return impl->m_RegisteredPlugins.size();
137 154 }
@@ -1,135 +1,83
1 1 TestUtils = library('TestUtils', 'TestUtils/TestProviders.h', 'TestUtils/TestProviders.cpp',
2 2 dependencies : [sciqlop_core, qt5test]
3 3 )
4 4
5 5
6 6 TestUtils_dep = declare_dependency(link_with : TestUtils,
7 7 dependencies : [sciqlop_core, qt5test])
8 8
9 9
10 DataSeriesTestsUtils = library('DataSeriesTestsUtils', 'Data/DataSeriesBuilders.h', 'Data/DataSeriesBuilders.cpp',
11 'Data/DataSeriesTestsUtils.h',
12 'Data/DataSeriesTestsUtils.cpp',
13 dependencies : [sciqlop_core, qt5test]
14 )
15
16
17 DataSeriesTestsUtils_dep = declare_dependency(link_with : DataSeriesTestsUtils,
18 dependencies : [sciqlop_core, qt5test])
19
20
21 10
22 11 tests = [
23 12 {
24 13 'name':'TestStringUtils',
25 14 'sources': ['Common/TestStringUtils.cpp'],
26 15 'deps': [sciqlop_core, qt5test]
27 16 },
28 17 {
29 18 'name':'TestContainers',
30 19 'sources': ['Common/TestContainers.cpp'],
31 20 'deps': [sciqlop_core, qt5test]
32 21 },
33 22 {
34 23 'name':'TestSyncGroup',
35 24 'sources': ['Variable/TestSyncGroup.cpp'],
36 25 'deps': [sciqlop_core, qt5test]
37 26 },
38 27 {
39 28 'name':'TestDateTimeRange',
40 29 'sources': ['Data/TestDateTimeRange.cpp'],
41 30 'deps': [sciqlop_core, qt5test]
42 31 },
43 32 {
44 'name':'TestDataSeriesUtils',
45 'sources': ['Data/TestDataSeriesUtils.cpp'],
46 'deps': [sciqlop_core, qt5test]
47 },
48 {
49 'name':'TestOptionalAxis',
50 'sources': ['Data/TestOptionalAxis.cpp'],
51 'deps': [sciqlop_core, qt5test]
52 },
53 {
54 'name':'TestSpectrogramSeries',
55 'sources': [
56 'Data/TestSpectrogramSeries.cpp',
57 ],
58 'deps': [sciqlop_core, qt5test, DataSeriesTestsUtils_dep]
59 },
60 {
61 'name':'TestOneDimArrayData',
62 'sources': ['Data/TestOneDimArrayData.cpp'],
63 'deps': [sciqlop_core, qt5test]
64 },
65 {
66 'name':'TestScalarSeries',
67 'sources': [
68 'Data/TestScalarSeries.cpp',
69 ],
70 'deps': [sciqlop_core, qt5test, DataSeriesTestsUtils_dep]
71 },
72 {
73 'name':'TestTwoDimArrayData',
74 'sources': ['Data/TestTwoDimArrayData.cpp'],
75 'deps': [sciqlop_core, qt5test]
76 },
77 {
78 'name':'TestVectorSeries',
79 'sources': [
80 'Data/TestVectorSeries.cpp',
81 ],
82 'deps': [sciqlop_core, qt5test, DataSeriesTestsUtils_dep]
83 },
84 {
85 33 'name':'TestDataSourceController',
86 34 'sources': [
87 35 'DataSource/TestDataSourceController.cpp',
88 36 'DataSource/DataSourceItemBuilder.cpp'
89 37 ],
90 38 'deps': [sciqlop_core, qt5test]
91 39 },
92 40 {
93 41 'name':'TestDataSourceItem',
94 42 'sources': [
95 43 'DataSource/TestDataSourceItem.cpp',
96 44 'DataSource/DataSourceItemBuilder.cpp'
97 45 ],
98 46 'deps': [sciqlop_core, qt5test]
99 47 },
100 48 {
101 49 'name':'TestVariable',
102 50 'sources': ['Variable/TestVariable.cpp'],
103 51 'deps': [sciqlop_core, qt5test]
104 52 },
105 53 {
106 54 'name':'TestDownloader',
107 55 'sources': ['Network/TestDownloader.cpp'],
108 56 'deps': [sciqlop_core, qt5test, qt5Concurrent]
109 57 },
110 58 {
111 59 'name':'TestVariableController2',
112 60 'sources': ['Variable/TestVariableController2.cpp'],
113 61 'deps': [sciqlop_core, qt5test, qt5Concurrent, TestUtils_dep]
114 62 },
115 63 {
116 64 'name':'TestVariableController2Async',
117 65 'sources': ['Variable/TestVariableController2Async.cpp'],
118 66 'deps': [sciqlop_core, qt5test, qt5Concurrent, TestUtils_dep]
119 67 },
120 68 {
121 69 'name':'TestVariableController2WithSync',
122 70 'sources': ['Variable/TestVariableController2WithSync.cpp'],
123 71 'deps': [sciqlop_core, qt5test, qt5Concurrent, TestUtils_dep]
124 72 }
125 73 ]
126 74
127 75 foreach unit_test : tests
128 76 test_moc_files = qt5.preprocess(moc_sources : unit_test['sources'])
129 77 test_exe = executable(unit_test['name'],unit_test['sources'] , test_moc_files,
130 78 dependencies : unit_test['deps'],
131 79 cpp_args : ['-DCORE_TESTS_RESOURCES_DIR="'+meson.current_source_dir()+'/../tests-resources"']
132 80 )
133 81 test('Test-' + unit_test['name'], test_exe, args: ['-teamcity', '-o', '@0@.teamcity.txt'.format(unit_test['name'])])
134 82 endforeach
135 83
General Comments 0
You need to be logged in to leave comments. Login now