File indexing completed on 2024-05-19 05:42:14
0001 // ct_lvtplg_pluginmanager.cpp -*-C++-*- 0002 0003 /* 0004 // Copyright 2023 Codethink Ltd <codethink@codethink.co.uk> 0005 // SPDX-License-Identifier: Apache-2.0 0006 // 0007 // Licensed under the Apache License, Version 2.0 (the "License"); 0008 // you may not use this file except in compliance with the License. 0009 // You may obtain a copy of the License at 0010 // 0011 // http://www.apache.org/licenses/LICENSE-2.0 0012 // 0013 // Unless required by applicable law or agreed to in writing, software 0014 // distributed under the License is distributed on an "AS IS" BASIS, 0015 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 0016 // See the License for the specific language governing permissions and 0017 // limitations under the License. 0018 */ 0019 0020 #include <ct_lvtplg_basicpluginhandlers.h> 0021 #include <ct_lvtplg_basicpluginhooks.h> 0022 #include <ct_lvtplg_pluginmanager.h> 0023 #ifdef ENABLE_PYTHON_PLUGINS 0024 #include <ct_lvtplg_pythonlibrarydispatcher.h> 0025 #endif 0026 #include <ct_lvtplg_sharedlibrarydispatcher.h> 0027 0028 #include <KPluginMetaData> 0029 #include <QCoreApplication> 0030 0031 namespace { 0032 template<typename HookFunctionType, typename HandlerType> 0033 void callSingleHook(const std::string& hookName, 0034 HandlerType&& handler, 0035 Codethink::lvtplg::AbstractLibraryDispatcher *library) 0036 { 0037 if (!library->isEnabled()) { 0038 return; 0039 } 0040 0041 auto resolveContext = library->resolve(hookName); 0042 if (resolveContext->hook) { 0043 reinterpret_cast<HookFunctionType>(resolveContext->hook)(&handler); 0044 } 0045 } 0046 0047 template<typename HookFunctionType, typename HandlerType> 0048 void callAllHooks( 0049 const std::string& hookName, 0050 HandlerType&& handler, 0051 std::unordered_map<std::string, std::unique_ptr<Codethink::lvtplg::AbstractLibraryDispatcher>>& libraries) 0052 { 0053 QDebug dbg(QtDebugMsg); 0054 for (auto const& [_, pluginLib] : libraries) { 0055 if (!pluginLib->isEnabled()) { 0056 continue; 0057 } 0058 0059 callSingleHook<HookFunctionType, HandlerType>(hookName, std::forward<HandlerType>(handler), pluginLib.get()); 0060 } 0061 } 0062 0063 template<typename DispatcherType> 0064 void tryInstallPlugin( 0065 QString const& pluginDir, 0066 std::unordered_map<std::string, std::unique_ptr<Codethink::lvtplg::AbstractLibraryDispatcher>>& libraries) 0067 { 0068 // TODO: This should check if the plugin is already loaded before attempting 0069 // to load it (because of knewstuff). 0070 QDebug dbg(QtDebugMsg); 0071 0072 dbg << "(" << DispatcherType::name << "): Trying to install plugin " << pluginDir << "... "; 0073 if (!DispatcherType::isValidPlugin(pluginDir)) { 0074 dbg << "Invalid plugin."; 0075 return; 0076 } 0077 0078 auto lib = DispatcherType::loadSinglePlugin(pluginDir); 0079 if (!lib) { 0080 dbg << "Could not load *valid* plugin: " << pluginDir; 0081 return; 0082 } 0083 0084 auto id = lib->pluginId(); 0085 libraries[id] = std::move(lib); 0086 dbg << "Plugin successfully loaded!"; 0087 } 0088 0089 template<typename DispatcherType> 0090 void tryReloadPlugin( 0091 QString const& pluginDir, 0092 std::unordered_map<std::string, std::unique_ptr<Codethink::lvtplg::AbstractLibraryDispatcher>>& libraries) 0093 { 0094 QDebug dbg(QtDebugMsg); 0095 0096 dbg << "(" << DispatcherType::name << "): Trying to reload plugin " << pluginDir << "... "; 0097 const bool res = DispatcherType::reloadSinglePlugin(pluginDir); 0098 if (!res) { 0099 dbg << "Could not load *valid* plugin: " << pluginDir; 0100 return; 0101 } 0102 } 0103 0104 void loadSinglePlugin( 0105 const QString& pluginDir, 0106 std::unordered_map<std::string, std::unique_ptr<Codethink::lvtplg::AbstractLibraryDispatcher>>& libraries) 0107 { 0108 tryInstallPlugin<Codethink::lvtplg::SharedLibraryDispatcher>(pluginDir, libraries); 0109 #ifdef ENABLE_PYTHON_PLUGINS 0110 tryInstallPlugin<Codethink::lvtplg::PythonLibraryDispatcher>(pluginDir, libraries); 0111 #endif 0112 } 0113 0114 } // namespace 0115 namespace Codethink::lvtplg { 0116 0117 void PluginManager::loadPlugins(const QList<QString>& searchPaths) 0118 { 0119 for (const auto& pluginsStr : searchPaths) { 0120 QDir pluginsPath(pluginsStr); 0121 0122 qDebug() << "Loading plugins from " << pluginsPath.path() << "..."; 0123 if (!pluginsPath.exists()) { 0124 qDebug() << pluginsPath.path() << "does not exists, skipping plugin search for it."; 0125 continue; 0126 } 0127 if (!pluginsPath.isReadable()) { 0128 qDebug() << pluginsPath.path() << "exists but it's not readable, skipping plugin search for it."; 0129 continue; 0130 } 0131 0132 pluginsPath.setFilter(QDir::Dirs | QDir::NoDotAndDotDot); 0133 pluginsPath.setSorting(QDir::Name); 0134 const auto entryInfoList = pluginsPath.entryInfoList(); 0135 qDebug() << entryInfoList.size() << "is the size of the number of files here"; 0136 for (auto const& fileInfo : qAsConst(entryInfoList)) { 0137 qDebug() << "\tTesting fileInfo" << fileInfo; 0138 const auto pluginDir = fileInfo.absoluteFilePath(); 0139 loadSinglePlugin(pluginDir, libraries); 0140 } 0141 } 0142 0143 qDebug() << "Loaded plugins:"; 0144 for (auto const& [_, p] : this->libraries) { 0145 qDebug() << "+ " << QString::fromStdString(p->fileName()); 0146 } 0147 } 0148 0149 void PluginManager::reloadPlugin(const QString& pluginFolder) 0150 { 0151 for (auto const& [_, p] : this->libraries) { 0152 const QString loadedLibrary = QString::fromStdString(p->fileName()); 0153 0154 if (loadedLibrary.startsWith(pluginFolder)) { 0155 removePlugin(pluginFolder); 0156 loadSinglePlugin(pluginFolder, libraries); 0157 return; 0158 } 0159 } 0160 0161 QFileInfo pluginPath(pluginFolder); 0162 if (!pluginPath.exists()) { 0163 return; 0164 } 0165 0166 tryInstallPlugin<SharedLibraryDispatcher>(pluginPath.absoluteFilePath(), libraries); 0167 #ifdef ENABLE_PYTHON_PLUGINS 0168 tryInstallPlugin<PythonLibraryDispatcher>(pluginPath.absoluteFilePath(), libraries); 0169 #endif 0170 } 0171 0172 void PluginManager::removePlugin(const QString& pluginFolder) 0173 { 0174 for (auto const& [key, p] : this->libraries) { 0175 const QString loadedLibrary = QString::fromStdString(p->fileName()); 0176 0177 if (loadedLibrary.startsWith(pluginFolder)) { 0178 auto handler = PluginSetupHandler{std::bind_front(&PluginManager::registerPluginData, this), 0179 std::bind_front(&PluginManager::getPluginData, this), 0180 std::bind_front(&PluginManager::unregisterPluginData, this)}; 0181 0182 callSingleHook<hookTeardownPlugin_f>("hookTeardownPlugin", handler, p.get()); 0183 p->unload(); 0184 this->libraries.erase(key); 0185 return; 0186 } 0187 } 0188 } 0189 0190 std::vector<std::string> PluginManager::getPluginsMetadataFilePaths() const 0191 { 0192 auto metadataFilePaths = std::vector<std::string>{}; 0193 for (auto const& [_, lib] : this->libraries) { 0194 metadataFilePaths.push_back(lib->metadataFilePath()); 0195 } 0196 return metadataFilePaths; 0197 } 0198 0199 std::optional<std::reference_wrapper<AbstractLibraryDispatcher>> 0200 PluginManager::getPluginById(std::string const& id) const 0201 { 0202 if (!this->libraries.contains(id)) { 0203 return std::nullopt; 0204 } 0205 return *this->libraries.at(id); 0206 } 0207 0208 void PluginManager::callHooksSetupPlugin() 0209 { 0210 callAllHooks<hookSetupPlugin_f>("hookSetupPlugin", 0211 PluginSetupHandler{std::bind_front(&PluginManager::registerPluginData, this), 0212 std::bind_front(&PluginManager::getPluginData, this), 0213 std::bind_front(&PluginManager::unregisterPluginData, this)}, 0214 libraries); 0215 } 0216 0217 void PluginManager::callHooksTeardownPlugin() 0218 { 0219 callAllHooks<hookTeardownPlugin_f>("hookTeardownPlugin", 0220 PluginSetupHandler{std::bind_front(&PluginManager::registerPluginData, this), 0221 std::bind_front(&PluginManager::getPluginData, this), 0222 std::bind_front(&PluginManager::unregisterPluginData, this)}, 0223 libraries); 0224 } 0225 0226 void PluginManager::callHooksContextMenu(getAllEntitiesInCurrentView_f const& getAllEntitiesInCurrentView, 0227 getEntityByQualifiedName_f const& getEntityByQualifiedName, 0228 getEdgeByQualifiedName_f const& getEdgeByQualifiedName, 0229 registerContextMenu_f const& registerContextMenu) 0230 { 0231 callAllHooks<hookGraphicsViewContextMenu_f>( 0232 "hookGraphicsViewContextMenu", 0233 PluginContextMenuHandler{std::bind_front(&PluginManager::getPluginData, this), 0234 getAllEntitiesInCurrentView, 0235 getEntityByQualifiedName, 0236 registerContextMenu, 0237 getEdgeByQualifiedName}, 0238 libraries); 0239 } 0240 0241 void PluginManager::callHooksSetupDockWidget(createPluginDock_f const& createPluginDock) 0242 { 0243 callAllHooks<hookSetupDockWidget_f>( 0244 "hookSetupDockWidget", 0245 PluginSetupDockWidgetHandler{std::bind_front(&PluginManager::getPluginData, this), createPluginDock}, 0246 libraries); 0247 } 0248 0249 void PluginManager::callHooksSetupEntityReport(getEntity_f const& getEntity, addReport_f const& addReport) 0250 { 0251 callAllHooks<hookSetupEntityReport_f>( 0252 "hookSetupEntityReport", 0253 PluginEntityReportHandler{std::bind_front(&PluginManager::getPluginData, this), getEntity, addReport}, 0254 libraries); 0255 } 0256 0257 void PluginManager::callHooksPhysicalParserOnHeaderFound(getSourceFile_f const& getSourceFile, 0258 getIncludedFile_f const& getIncludedFile, 0259 getLineNo_f const& getLineNo) 0260 { 0261 callAllHooks<hookPhysicalParserOnHeaderFound_f>( 0262 "hookPhysicalParserOnHeaderFound", 0263 PluginPhysicalParserOnHeaderFoundHandler{std::bind_front(&PluginManager::getPluginData, this), 0264 getSourceFile, 0265 getIncludedFile, 0266 getLineNo}, 0267 libraries); 0268 } 0269 0270 void PluginManager::callHooksPluginLogicalParserOnCppCommentFoundHandler(getFilename_f const& getFilename, 0271 getBriefText_f const& getBriefText, 0272 getStartLine_f const& getStartLine, 0273 getEndLine_f const& getEndLine) 0274 { 0275 callAllHooks<hookLogicalParserOnCppCommentFound_f>( 0276 "hookLogicalParserOnCppCommentFound", 0277 PluginLogicalParserOnCppCommentFoundHandler{std::bind_front(&PluginManager::getPluginData, this), 0278 getFilename, 0279 getBriefText, 0280 getStartLine, 0281 getEndLine}, 0282 libraries); 0283 } 0284 0285 void PluginManager::callHooksOnParseCompleted(runQueryOnDatabase_f const& runQueryOnDatabase) 0286 { 0287 callAllHooks<hookOnParseCompleted_f>( 0288 "hookOnParseCompleted", 0289 PluginParseCompletedHandler{std::bind_front(&PluginManager::getPluginData, this), runQueryOnDatabase}, 0290 libraries); 0291 } 0292 0293 void PluginManager::callHooksActiveSceneChanged(getSceneName_f const& getSceneName) 0294 { 0295 callAllHooks<hookActiveSceneChanged_f>( 0296 "hookActiveSceneChanged", 0297 PluginActiveSceneChangedHandler{std::bind_front(&PluginManager::getPluginData, this), getSceneName}, 0298 libraries); 0299 } 0300 0301 void PluginManager::callHooksGraphChanged(graphChanged_getSceneName_f const& getSceneName, 0302 graphChanged_getVisibleEntities_f const& getVisibleEntities, 0303 graphChanged_getEdgeByQualifiedName_f const& getEdgeByQualifiedName, 0304 graphChanged_getProjectData_f const& getProjectData) 0305 { 0306 callAllHooks<hookGraphChanged_f>("hookGraphChanged", 0307 PluginGraphChangedHandler{std::bind_front(&PluginManager::getPluginData, this), 0308 getSceneName, 0309 getVisibleEntities, 0310 getEdgeByQualifiedName, 0311 getProjectData}, 0312 libraries); 0313 } 0314 0315 void PluginManager::registerPluginData(std::string const& id, void *data) 0316 { 0317 this->pluginData[id] = data; 0318 } 0319 0320 void PluginManager::unregisterPluginData(std::string const& id) 0321 { 0322 this->pluginData.erase(id); 0323 } 0324 0325 void *PluginManager::getPluginData(std::string const& id) const 0326 { 0327 try { 0328 return this->pluginData.at(id); 0329 } catch (std::out_of_range const&) { 0330 return nullptr; 0331 } 0332 } 0333 0334 void PluginManager::registerPluginQObject(std::string const& id, QObject *object) 0335 { 0336 this->pluginQObjects[id] = object; 0337 } 0338 0339 QObject *PluginManager::getPluginQObject(std::string const& id) const 0340 { 0341 return this->pluginQObjects.at(id); 0342 } 0343 0344 } // namespace Codethink::lvtplg