Continues migration with DDS, Common
This commit is contained in:
1 parent
9b15cee841
commit
fa48db111e
668 files changed
+111841
-2
No files matched your search
+449
@@ -0,0 +1,449 @@
|
||||
|
||||
|
||||
|
||||
#include "lts_plugin_loader.h"
|
||||
|
||||
#include <NGLLogger/NGLLoggerBootstrap.h>
|
||||
#include <LtsAppDescriptor/lts_app_descriptor.h>
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QDirIterator>
|
||||
#include <QFileInfo>
|
||||
#include <QJsonObject>
|
||||
#include <QPluginLoader>
|
||||
#include <QMutableListIterator>
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
|
||||
namespace LtsPluginManager
|
||||
{
|
||||
|
||||
struct LtsPluginLoaderHandler
|
||||
{
|
||||
LtsPluginLoader ltsPluginLoader;
|
||||
static NGL::INGLLogger &logger() { return NGL::NGLLoggerBootstrap::getNGLRootLogger().getNGLLogger("LtsPluginLoader"); }
|
||||
static void cleanupPluginLoader();
|
||||
};
|
||||
Q_GLOBAL_STATIC(LtsPluginLoaderHandler, s_ltsPluginLoader)
|
||||
|
||||
void LtsPluginLoaderHandler::cleanupPluginLoader()
|
||||
{
|
||||
if (s_ltsPluginLoader.exists())
|
||||
s_ltsPluginLoader->ltsPluginLoader.unloadAllPlugins();
|
||||
}
|
||||
|
||||
|
||||
LtsPluginLoader *LtsPluginLoader::getInstance()
|
||||
{
|
||||
if (!s_ltsPluginLoader.exists())
|
||||
qAddPostRoutine(&LtsPluginLoaderHandler::cleanupPluginLoader);
|
||||
return &s_ltsPluginLoader->ltsPluginLoader;
|
||||
}
|
||||
|
||||
|
||||
LtsPluginLoader::~LtsPluginLoader()
|
||||
{
|
||||
unloadAllPlugins();
|
||||
}
|
||||
|
||||
void LtsPluginLoader::unloadAllPlugins()
|
||||
{
|
||||
for (auto &pi : qAsConst(m_loadedPlugins))
|
||||
{
|
||||
QPluginLoader *pluginLoader = pi.pluginLoader;
|
||||
|
||||
delete pluginLoader->instance();
|
||||
|
||||
if (!pluginLoader->unload())
|
||||
qWarning() << "~LtsPluginLoader: could not unload" << pi.pluginName << pluginLoader->errorString();
|
||||
|
||||
delete pluginLoader;
|
||||
}
|
||||
m_loadedPlugins.clear();
|
||||
}
|
||||
|
||||
|
||||
void LtsPluginLoader::checkPlugins(const QVector<LtsAppDescriptor::AppDescription> &appVector,
|
||||
QStringList &errors )
|
||||
{
|
||||
NGL::INGLLogger &logger = LtsPluginLoaderHandler::logger();
|
||||
|
||||
if (appVector.isEmpty())
|
||||
{
|
||||
errors += QStringLiteral("No Apps available");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
QVector<PluginInstance> dataPlugins;
|
||||
QVector<PluginInstance> guiPlugins;
|
||||
auto addDataPluginFn = [&](const PluginInstance &pi)
|
||||
{
|
||||
dataPlugins.append(pi);
|
||||
return true;
|
||||
};
|
||||
auto addGuiPluginFn = [&](const PluginInstance &pi)
|
||||
{
|
||||
guiPlugins.append(pi);
|
||||
return true;
|
||||
};
|
||||
|
||||
if (!loadPlugins(PluginType::DATA, addDataPluginFn))
|
||||
{
|
||||
errors += QStringLiteral("failed to load data plugins");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!loadPlugins(PluginType::GUI, addGuiPluginFn))
|
||||
{
|
||||
errors += QStringLiteral("failed to load gui plugins");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
auto checkDuplicateIidsFn = [&](const QVector<PluginInstance> &plugins, const QString &pluginType)
|
||||
{
|
||||
typedef QMap<QString , QString > IidMap;
|
||||
IidMap iids;
|
||||
|
||||
for (auto i3 = plugins.constBegin(); i3 != plugins.constEnd(); ++i3)
|
||||
{
|
||||
const PluginInstance &pin = *i3;
|
||||
auto iidFound = iids.constFind(pin.pluginName);
|
||||
|
||||
if (iidFound != iids.constEnd())
|
||||
errors += QStringLiteral("Iid duplicate for %1 plugin '%2'").arg(pluginType, pin.pluginName);
|
||||
else
|
||||
iids.insert(pin.pluginName, pin.fullFileName);
|
||||
}
|
||||
};
|
||||
|
||||
checkDuplicateIidsFn(dataPlugins, QStringLiteral("data"));
|
||||
checkDuplicateIidsFn(guiPlugins, QStringLiteral("gui"));
|
||||
|
||||
|
||||
|
||||
QVector<PluginInstance> plugins(dataPlugins + guiPlugins);
|
||||
|
||||
|
||||
|
||||
for (auto i1 = appVector.constBegin(); i1 != appVector.constEnd(); ++i1)
|
||||
{
|
||||
const LtsAppDescriptor::AppDescription &app = *i1;
|
||||
|
||||
|
||||
|
||||
const QVector<LtsAppDescriptor::PluginInfo> &piVector = app.pluginInfos;
|
||||
|
||||
for (auto i2 = piVector.constBegin(); i2 != piVector.constEnd(); i2++)
|
||||
{
|
||||
const LtsAppDescriptor::PluginInfo &pi = *i2;
|
||||
bool found = false;
|
||||
|
||||
for (auto i3 = plugins.constBegin(); i3 != plugins.constEnd(); ++i3)
|
||||
{
|
||||
const PluginInstance &pin = *i3;
|
||||
|
||||
if (pi.iid == pin.pluginName)
|
||||
{
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found)
|
||||
{
|
||||
ERROR_LOG(logger) << "Plugin with iid '" << pi.iid << "' was requested but was not loaded!";
|
||||
errors += QStringLiteral("App '%1' requires plugin '%2' that could not be found.").arg(app.appName, pi.iid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
bool LtsPluginLoader::loadPlugins(PluginType requestedPluginType,
|
||||
const std::function<bool(const PluginInstance &)> &loadPlugin,
|
||||
const QStringList &requiredPlugins,
|
||||
const QString &specialTopicTypeFilter,
|
||||
const QStringList &optionalPlugins)
|
||||
{
|
||||
NGL::INGLLogger &logger = LtsPluginLoaderHandler::logger();
|
||||
Q_ASSERT(requestedPluginType != PluginType::UNKNOWN);
|
||||
|
||||
QStringList requiredPluginsList = requiredPlugins;
|
||||
QStringList optionalPluginsList = optionalPlugins;
|
||||
bool checkPluginsLists = (!requiredPluginsList.isEmpty()) || (!optionalPluginsList.isEmpty());
|
||||
|
||||
if (checkPluginsLists && !m_loadedPlugins.isEmpty())
|
||||
{
|
||||
{
|
||||
QMutableListIterator<QString> i1(requiredPluginsList);
|
||||
|
||||
while (i1.hasNext())
|
||||
{
|
||||
const QString pluginName = i1.next().toLower();
|
||||
|
||||
QMap<PluginKey, PluginInstance>::iterator i2 = m_loadedPlugins.find(PluginKey(requestedPluginType, pluginName));
|
||||
|
||||
if (i2 != m_loadedPlugins.end())
|
||||
{
|
||||
DEBUG_LOG(logger) << "Plugin " << pluginName << " already loaded, using that instance";
|
||||
|
||||
if (loadPlugin)
|
||||
loadPlugin(i2.value());
|
||||
|
||||
i1.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
{
|
||||
QMutableListIterator<QString> i1(optionalPluginsList);
|
||||
|
||||
while (i1.hasNext())
|
||||
{
|
||||
const QString pluginName = i1.next().toLower();
|
||||
|
||||
QMap<PluginKey, PluginInstance>::iterator i2 = m_loadedPlugins.find(PluginKey(requestedPluginType, pluginName));
|
||||
|
||||
if (i2 != m_loadedPlugins.end())
|
||||
{
|
||||
DEBUG_LOG(logger) << "Plugin " << pluginName << " already loaded, using that instance";
|
||||
|
||||
if (loadPlugin)
|
||||
loadPlugin(i2.value());
|
||||
|
||||
i1.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
QFileInfo libPathInfo(QCoreApplication::applicationDirPath() + QStringLiteral("/../lib/"));
|
||||
QString libPath(libPathInfo.absolutePath());
|
||||
if (!libPathInfo.exists())
|
||||
{
|
||||
|
||||
|
||||
libPathInfo.setFile(QCoreApplication::applicationFilePath());
|
||||
libPath = libPathInfo.absolutePath();
|
||||
}
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
const QStringList nameFilter = QStringList(QStringLiteral("*Plugin*.dll"));
|
||||
#else
|
||||
const QStringList nameFilter = QStringList(QStringLiteral("*Plugin*.so"));
|
||||
#endif
|
||||
|
||||
static const QStringList pluginTypes({QStringLiteral("data"),
|
||||
QStringLiteral("gui"),
|
||||
QStringLiteral("topic_type"),
|
||||
QStringLiteral("project"),
|
||||
QStringLiteral("style"),
|
||||
QStringLiteral("special")});
|
||||
|
||||
QDirIterator pluginDir(libPath, nameFilter, QDir::Files);
|
||||
while (pluginDir.hasNext())
|
||||
{
|
||||
pluginDir.next();
|
||||
const QString filename = pluginDir.fileName();
|
||||
if (filename.contains(QStringLiteral("PluginInterface"), Qt::CaseSensitive))
|
||||
continue;
|
||||
|
||||
const QString absoluteFilePath = pluginDir.filePath();
|
||||
|
||||
DEBUG_LOG(logger) << "File Path: " << absoluteFilePath;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
QScopedPointer<QPluginLoader> pluginLoader(new QPluginLoader);
|
||||
pluginLoader->setFileName(absoluteFilePath);
|
||||
|
||||
const QJsonObject jObj = pluginLoader->metaData();
|
||||
QJsonObject::const_iterator iid = jObj.find(QStringLiteral("IID"));
|
||||
|
||||
if (iid == jObj.end())
|
||||
{
|
||||
DEBUG_LOG(logger) << "Plugin '" << filename << "' has no IID !";
|
||||
continue;
|
||||
}
|
||||
|
||||
const QString jsonIid = iid.value().toString().toLower();
|
||||
const QVector<QStringRef> iidParameters = jsonIid.splitRef(QLatin1Char(':'));
|
||||
|
||||
DEBUG_LOG(logger) << "Plugin " << absoluteFilePath << " provides iid '" << jsonIid;
|
||||
|
||||
if (iidParameters.count() != 3)
|
||||
{
|
||||
WARNING_LOG(logger) << "Invalid iid "<<jsonIid<<" for plugin " << filename;
|
||||
continue;
|
||||
}
|
||||
|
||||
bool ok = false;
|
||||
int pluginLoaderVersion = iidParameters.at(1).toInt(&ok);
|
||||
|
||||
if (!ok || pluginLoaderVersion != PLUGIN_LOADER_VERSION)
|
||||
{
|
||||
WARNING_LOG(logger) << "Invalid plugin loader version" << pluginLoaderVersion << "for plugin:" << filename;
|
||||
continue;
|
||||
}
|
||||
|
||||
PluginType pluginType = PluginType::UNKNOWN;
|
||||
|
||||
const QString pluginName = iidParameters.at(2).toString();
|
||||
const QString pluginTypeString = iidParameters.at(0).toString();
|
||||
switch (pluginTypes.indexOf(pluginTypeString))
|
||||
{
|
||||
case 0:
|
||||
pluginType = PluginType::DATA;
|
||||
break;
|
||||
case 1:
|
||||
pluginType = PluginType::GUI;
|
||||
break;
|
||||
case 2:
|
||||
pluginType = PluginType::TOPIC_TYPE;
|
||||
break;
|
||||
case 3:
|
||||
pluginType = PluginType::PROJECT;
|
||||
break;
|
||||
case 4:
|
||||
pluginType = PluginType::STYLE;
|
||||
break;
|
||||
case 5:
|
||||
pluginType = PluginType::SPECIAL;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (pluginType != requestedPluginType)
|
||||
{
|
||||
DEBUG_LOG(logger) << "Plugin provides not the requested type " << (int)requestedPluginType << " is: " << (int)pluginType;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (pluginType == PluginType::SPECIAL && !specialTopicTypeFilter.isEmpty())
|
||||
{
|
||||
if (!pluginName.startsWith(specialTopicTypeFilter))
|
||||
{
|
||||
DEBUG_LOG(logger) << "Special plugin, but filter does not match: " << pluginName << " - " << specialTopicTypeFilter;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (checkPluginsLists)
|
||||
{
|
||||
bool found = false;
|
||||
|
||||
{
|
||||
QMutableListIterator<QString> i (requiredPluginsList);
|
||||
|
||||
while (i.hasNext())
|
||||
{
|
||||
const QString requiredPlugin = i.next().toLower();
|
||||
|
||||
if (pluginName == requiredPlugin)
|
||||
{
|
||||
found = true;
|
||||
i.remove();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!found)
|
||||
{
|
||||
for (auto i = optionalPluginsList.constBegin(); i != optionalPluginsList.constEnd(); i++)
|
||||
{
|
||||
const QString optionalPlugin = (*i).toLower();
|
||||
|
||||
if (pluginName == optionalPlugin)
|
||||
{
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!found)
|
||||
{
|
||||
DEBUG_LOG(logger) << "Plugin " << pluginName << " was not requested";
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
if (loadPlugin && !m_loadedPlugins.isEmpty())
|
||||
{
|
||||
QMap<PluginKey, PluginInstance>::iterator i = m_loadedPlugins.find(PluginKey(requestedPluginType, pluginName));
|
||||
|
||||
if (i != m_loadedPlugins.end())
|
||||
{
|
||||
DEBUG_LOG(logger) << "Plugin " << pluginName << " already loaded, using that instance ("<<i.value().plugin->metaObject()->className()<<")";
|
||||
|
||||
loadPlugin(i.value());
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
QObject *plugin = pluginLoader->instance();
|
||||
|
||||
if (!plugin)
|
||||
{
|
||||
ERROR_LOG(logger) << "Failed to load " << filename << " - " << pluginLoader->errorString();
|
||||
continue;
|
||||
}
|
||||
|
||||
PluginInstance pi;
|
||||
pi.pluginName = pluginName;
|
||||
pi.fileName = filename;
|
||||
pi.fullFileName = absoluteFilePath;
|
||||
pi.plugin = plugin;
|
||||
pi.pluginLoader = pluginLoader.data();
|
||||
|
||||
if (loadPlugin && !loadPlugin(pi))
|
||||
{
|
||||
DEBUG_LOG(logger) << "Plugin " << pluginName << " unloaded";
|
||||
delete pluginLoader->instance();
|
||||
pluginLoader->unload();
|
||||
continue;
|
||||
}
|
||||
pi.pluginLoader = pluginLoader.take();
|
||||
|
||||
|
||||
m_loadedPlugins.insert(PluginKey(requestedPluginType, pluginName), pi);
|
||||
|
||||
|
||||
|
||||
|
||||
DEBUG_LOG(logger) << "Loaded " << jsonIid << " - File: " << filename;
|
||||
}
|
||||
|
||||
|
||||
if (checkPluginsLists && !requiredPluginsList.isEmpty())
|
||||
{
|
||||
WARNING_LOG(logger) << "These plugins could not be found for Plugin Type " << (int)requestedPluginType << ": " << requiredPluginsList.join(QStringLiteral(", "));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in new issue
Block a user