forked from qt-creator/qt-creator
ExtensionSystem: Refactor PluginSpec
Splits the functionality between plugin type specific and general. Allows Plugins to be loaded after the first pass, e.g. for Lua scripted plugins. Change-Id: If2712817a672c49d554fdc308250cb06ca7eb3f8 Reviewed-by: Eike Ziller <eike.ziller@qt.io>
This commit is contained in:
@@ -11,7 +11,7 @@ add_qtc_library(ExtensionSystem
|
||||
pluginerroroverview.cpp pluginerroroverview.h
|
||||
pluginerrorview.cpp pluginerrorview.h
|
||||
pluginmanager.cpp pluginmanager.h pluginmanager_p.h
|
||||
pluginspec.cpp pluginspec.h pluginspec_p.h
|
||||
pluginspec.cpp pluginspec.h
|
||||
pluginview.cpp pluginview.h
|
||||
EXPLICIT_MOC
|
||||
pluginmanager.h
|
||||
|
@@ -29,7 +29,6 @@ QtcLibrary {
|
||||
"pluginmanager_p.h",
|
||||
"pluginspec.cpp",
|
||||
"pluginspec.h",
|
||||
"pluginspec_p.h",
|
||||
"pluginview.cpp",
|
||||
"pluginview.h",
|
||||
]
|
||||
|
@@ -6,7 +6,6 @@
|
||||
#include "extensionsystemtr.h"
|
||||
#include "pluginmanager.h"
|
||||
#include "pluginmanager_p.h"
|
||||
#include "pluginspec_p.h"
|
||||
|
||||
#include <utils/algorithm.h>
|
||||
|
||||
@@ -119,7 +118,7 @@ bool OptionsParser::checkForTestOptions()
|
||||
} else {
|
||||
m_pmPrivate->testSpecs.emplace_back(spec, args);
|
||||
}
|
||||
} else {
|
||||
} else {
|
||||
if (m_errorString)
|
||||
*m_errorString = Tr::tr("The plugin \"%1\" does not exist.").arg(pluginName);
|
||||
m_hasError = true;
|
||||
@@ -177,7 +176,7 @@ bool OptionsParser::checkForLoadOption()
|
||||
if (nextToken(RequiredToken)) {
|
||||
if (m_currentArg == QLatin1String("all")) {
|
||||
for (PluginSpec *spec : std::as_const(m_pmPrivate->pluginSpecs))
|
||||
spec->d->setForceEnabled(true);
|
||||
spec->setForceEnabled(true);
|
||||
m_isDependencyRefreshNeeded = true;
|
||||
} else {
|
||||
PluginSpec *spec = m_pmPrivate->pluginByName(m_currentArg);
|
||||
@@ -186,7 +185,7 @@ bool OptionsParser::checkForLoadOption()
|
||||
*m_errorString = Tr::tr("The plugin \"%1\" does not exist.").arg(m_currentArg);
|
||||
m_hasError = true;
|
||||
} else {
|
||||
spec->d->setForceEnabled(true);
|
||||
spec->setForceEnabled(true);
|
||||
m_isDependencyRefreshNeeded = true;
|
||||
}
|
||||
}
|
||||
@@ -202,7 +201,7 @@ bool OptionsParser::checkForNoLoadOption()
|
||||
if (nextToken(RequiredToken)) {
|
||||
if (m_currentArg == QLatin1String("all")) {
|
||||
for (PluginSpec *spec : std::as_const(m_pmPrivate->pluginSpecs))
|
||||
spec->d->setForceDisabled(true);
|
||||
spec->setForceDisabled(true);
|
||||
m_isDependencyRefreshNeeded = true;
|
||||
} else {
|
||||
PluginSpec *spec = m_pmPrivate->pluginByName(m_currentArg);
|
||||
@@ -211,10 +210,10 @@ bool OptionsParser::checkForNoLoadOption()
|
||||
*m_errorString = Tr::tr("The plugin \"%1\" does not exist.").arg(m_currentArg);
|
||||
m_hasError = true;
|
||||
} else {
|
||||
spec->d->setForceDisabled(true);
|
||||
spec->setForceDisabled(true);
|
||||
// recursively disable all plugins that require this plugin
|
||||
for (PluginSpec *dependantSpec : PluginManager::pluginsRequiringPlugin(spec))
|
||||
dependantSpec->d->setForceDisabled(true);
|
||||
dependantSpec->setForceDisabled(true);
|
||||
m_isDependencyRefreshNeeded = true;
|
||||
}
|
||||
}
|
||||
@@ -292,10 +291,10 @@ bool OptionsParser::checkForUnknownOption()
|
||||
void OptionsParser::forceDisableAllPluginsExceptTestedAndForceEnabled()
|
||||
{
|
||||
for (const PluginManagerPrivate::TestSpec &testSpec : m_pmPrivate->testSpecs)
|
||||
testSpec.pluginSpec->d->setForceEnabled(true);
|
||||
testSpec.pluginSpec->setForceEnabled(true);
|
||||
for (PluginSpec *spec : std::as_const(m_pmPrivate->pluginSpecs)) {
|
||||
if (!spec->isForceEnabled() && !spec->isRequired())
|
||||
spec->d->setForceDisabled(true);
|
||||
spec->setForceDisabled(true);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -8,7 +8,6 @@
|
||||
#include "optionsparser.h"
|
||||
#include "pluginmanager_p.h"
|
||||
#include "pluginspec.h"
|
||||
#include "pluginspec_p.h"
|
||||
|
||||
#include <nanotrace/nanotrace.h>
|
||||
|
||||
@@ -35,6 +34,7 @@
|
||||
#include <QLibraryInfo>
|
||||
#include <QMessageBox>
|
||||
#include <QMetaProperty>
|
||||
#include <QPluginLoader>
|
||||
#include <QPushButton>
|
||||
#include <QScopeGuard>
|
||||
#include <QSysInfo>
|
||||
@@ -327,6 +327,11 @@ void PluginManager::loadPluginsAtRuntime(const QSet<PluginSpec *> &plugins)
|
||||
d->loadPluginsAtRuntime(plugins);
|
||||
}
|
||||
|
||||
void PluginManager::addPlugins(const QVector<PluginSpec *> &specs)
|
||||
{
|
||||
d->addPlugins(specs);
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns \c true if any plugin has errors even though it is enabled.
|
||||
Most useful to call after loadPlugins().
|
||||
@@ -903,14 +908,6 @@ QVector<PluginSpec *> PluginManager::loadQueue()
|
||||
|
||||
//============PluginManagerPrivate===========
|
||||
|
||||
/*!
|
||||
\internal
|
||||
*/
|
||||
PluginSpec *PluginManagerPrivate::createSpec()
|
||||
{
|
||||
return new PluginSpec();
|
||||
}
|
||||
|
||||
/*!
|
||||
\internal
|
||||
*/
|
||||
@@ -935,14 +932,6 @@ void PluginManagerPrivate::setGlobalSettings(QtcSettings *s)
|
||||
globalSettings->setParent(this);
|
||||
}
|
||||
|
||||
/*!
|
||||
\internal
|
||||
*/
|
||||
PluginSpecPrivate *PluginManagerPrivate::privateSpec(PluginSpec *spec)
|
||||
{
|
||||
return spec->d;
|
||||
}
|
||||
|
||||
void PluginManagerPrivate::startDelayedInitialize()
|
||||
{
|
||||
Utils::setMimeStartupPhase(MimeStartupPhase::PluginsDelayedInitializing);
|
||||
@@ -954,8 +943,8 @@ void PluginManagerPrivate::startDelayedInitialize()
|
||||
delayedInitializeQueue.pop();
|
||||
NANOTRACE_SCOPE(specName, specName + "::delayedInitialized");
|
||||
profilingReport(">delayedInitialize", spec);
|
||||
bool delay = spec->d->delayedInitialize();
|
||||
profilingReport("<delayedInitialize", spec, &spec->d->performanceData.delayedInitialize);
|
||||
bool delay = spec->delayedInitialize();
|
||||
profilingReport("<delayedInitialize", spec, &spec->performanceData().delayedInitialize);
|
||||
if (delay) // give UI a bit of breathing space, but prevent user interaction
|
||||
QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
|
||||
}
|
||||
@@ -1068,10 +1057,8 @@ void PluginManagerPrivate::checkForDuplicatePlugins()
|
||||
if (spec->isEffectivelyEnabled() && other->isEffectivelyEnabled()) {
|
||||
const QString error = Tr::tr(
|
||||
"Multiple versions of the same plugin have been found.");
|
||||
spec->d->hasError = true;
|
||||
spec->d->errorString = error;
|
||||
other->d->hasError = true;
|
||||
other->d->errorString = error;
|
||||
spec->setError(error);
|
||||
other->setError(error);
|
||||
}
|
||||
} else {
|
||||
seen.insert(spec->name(), spec);
|
||||
@@ -1403,7 +1390,7 @@ void PluginManagerPrivate::loadPlugins()
|
||||
delayedInitializeQueue.push(spec);
|
||||
} else {
|
||||
// Plugin initialization failed, so cleanup after it
|
||||
spec->d->kill();
|
||||
spec->kill();
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -1421,10 +1408,23 @@ void PluginManagerPrivate::loadPlugins()
|
||||
void PluginManagerPrivate::loadPluginsAtRuntime(const QSet<PluginSpec *> &plugins)
|
||||
{
|
||||
QTC_CHECK(allOf(plugins, [](PluginSpec *spec) { return spec->isSoftLoadable(); }));
|
||||
// load the plugins ordered by dependency
|
||||
|
||||
// load the plugins and their dependencies (if possible) ordered by dependency
|
||||
const QList<PluginSpec *> queue = filtered(loadQueue(), [&plugins](PluginSpec *spec) {
|
||||
return plugins.contains(spec);
|
||||
// Is the current plugin already running, or not soft loadable?
|
||||
if (spec->state() == PluginSpec::State::Running || !spec->isSoftLoadable())
|
||||
return false;
|
||||
|
||||
// Is the current plugin in the list of plugins to load?
|
||||
if (plugins.contains(spec))
|
||||
return true;
|
||||
|
||||
// Is the current plugin a dependency of any of the plugins we want to load?
|
||||
return plugins.contains(spec) || Utils::anyOf(plugins, [spec](PluginSpec *other) {
|
||||
return other->requiresAny({spec});
|
||||
});
|
||||
});
|
||||
|
||||
std::queue<PluginSpec *> localDelayedInitializeQueue;
|
||||
for (PluginSpec *spec : queue)
|
||||
loadPlugin(spec, PluginSpec::Loaded);
|
||||
@@ -1434,12 +1434,12 @@ void PluginManagerPrivate::loadPluginsAtRuntime(const QSet<PluginSpec *> &plugin
|
||||
[this](PluginSpec *spec) { loadPlugin(spec, PluginSpec::Running); });
|
||||
Utils::reverseForeach(queue, [](PluginSpec *spec) {
|
||||
if (spec->state() == PluginSpec::Running) {
|
||||
const bool delay = spec->d->delayedInitialize();
|
||||
const bool delay = spec->delayedInitialize();
|
||||
if (delay)
|
||||
QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
|
||||
} else {
|
||||
// Plugin initialization failed, so cleanup after it
|
||||
spec->d->kill();
|
||||
spec->kill();
|
||||
}
|
||||
});
|
||||
emit q->pluginsChanged();
|
||||
@@ -1494,17 +1494,17 @@ bool PluginManagerPrivate::loadQueue(PluginSpec *spec,
|
||||
return true;
|
||||
// check for circular dependencies
|
||||
if (circularityCheckQueue.contains(spec)) {
|
||||
spec->d->hasError = true;
|
||||
spec->d->errorString = Tr::tr("Circular dependency detected:");
|
||||
spec->d->errorString += QLatin1Char('\n');
|
||||
QString errorString = Tr::tr("Circular dependency detected:");
|
||||
errorString += QLatin1Char('\n');
|
||||
int index = circularityCheckQueue.indexOf(spec);
|
||||
for (int i = index; i < circularityCheckQueue.size(); ++i) {
|
||||
const PluginSpec *depSpec = circularityCheckQueue.at(i);
|
||||
spec->d->errorString.append(Tr::tr("%1 (%2) depends on")
|
||||
.arg(depSpec->name(), depSpec->version()));
|
||||
spec->d->errorString += QLatin1Char('\n');
|
||||
errorString.append(
|
||||
Tr::tr("%1 (%2) depends on").arg(depSpec->name(), depSpec->version()));
|
||||
errorString += QLatin1Char('\n');
|
||||
}
|
||||
spec->d->errorString.append(Tr::tr("%1 (%2)").arg(spec->name(), spec->version()));
|
||||
errorString.append(Tr::tr("%1 (%2)").arg(spec->name(), spec->version()));
|
||||
spec->setError(errorString);
|
||||
return false;
|
||||
}
|
||||
circularityCheckQueue.append(spec);
|
||||
@@ -1523,10 +1523,9 @@ bool PluginManagerPrivate::loadQueue(PluginSpec *spec,
|
||||
continue;
|
||||
PluginSpec *depSpec = it.value();
|
||||
if (!loadQueue(depSpec, queue, circularityCheckQueue)) {
|
||||
spec->d->hasError = true;
|
||||
spec->d->errorString =
|
||||
spec->setError(
|
||||
Tr::tr("Cannot load plugin because dependency failed to load: %1 (%2)\nReason: %3")
|
||||
.arg(depSpec->name(), depSpec->version(), depSpec->errorString());
|
||||
.arg(depSpec->name(), depSpec->version(), depSpec->errorString()));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1619,9 +1618,9 @@ void PluginManagerPrivate::checkForProblematicPlugins()
|
||||
dialog.addButton(Tr::tr("Continue"), QMessageBox::RejectRole);
|
||||
dialog.exec();
|
||||
if (dialog.clickedButton() == disableButton) {
|
||||
spec->d->setForceDisabled(true);
|
||||
spec->setForceDisabled(true);
|
||||
for (PluginSpec *other : dependents)
|
||||
other->d->setForceDisabled(true);
|
||||
other->setForceDisabled(true);
|
||||
enableDependenciesIndirectly();
|
||||
}
|
||||
}
|
||||
@@ -1664,15 +1663,15 @@ void PluginManagerPrivate::loadPlugin(PluginSpec *spec, PluginSpec::State destSt
|
||||
case PluginSpec::Running: {
|
||||
NANOTRACE_SCOPE(specName, specName + "::extensionsInitialized");
|
||||
profilingReport(">initializeExtensions", spec);
|
||||
spec->d->initializeExtensions();
|
||||
spec->initializeExtensions();
|
||||
profilingReport("<initializeExtensions",
|
||||
spec,
|
||||
&spec->d->performanceData.extensionsInitialized);
|
||||
&spec->performanceData().extensionsInitialized);
|
||||
return;
|
||||
}
|
||||
case PluginSpec::Deleted:
|
||||
profilingReport(">delete", spec);
|
||||
spec->d->kill();
|
||||
spec->kill();
|
||||
profilingReport("<delete", spec);
|
||||
return;
|
||||
default:
|
||||
@@ -1686,10 +1685,10 @@ void PluginManagerPrivate::loadPlugin(PluginSpec *spec, PluginSpec::State destSt
|
||||
continue;
|
||||
PluginSpec *depSpec = it.value();
|
||||
if (depSpec->state() != destState) {
|
||||
spec->d->hasError = true;
|
||||
spec->d->errorString =
|
||||
Tr::tr("Cannot load plugin because dependency failed to load: %1(%2)\nReason: %3")
|
||||
.arg(depSpec->name(), depSpec->version(), depSpec->errorString());
|
||||
spec->setError(
|
||||
Tr::tr(
|
||||
"Cannot load plugin because dependency failed to load: %1(%2)\nReason: %3")
|
||||
.arg(depSpec->name(), depSpec->version(), depSpec->errorString()));
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -1698,20 +1697,20 @@ void PluginManagerPrivate::loadPlugin(PluginSpec *spec, PluginSpec::State destSt
|
||||
case PluginSpec::Loaded: {
|
||||
NANOTRACE_SCOPE(specName, specName + "::load");
|
||||
profilingReport(">loadLibrary", spec);
|
||||
spec->d->loadLibrary();
|
||||
profilingReport("<loadLibrary", spec, &spec->d->performanceData.load);
|
||||
spec->loadLibrary();
|
||||
profilingReport("<loadLibrary", spec, &spec->performanceData().load);
|
||||
break;
|
||||
}
|
||||
case PluginSpec::Initialized: {
|
||||
NANOTRACE_SCOPE(specName, specName + "::initialize");
|
||||
profilingReport(">initializePlugin", spec);
|
||||
spec->d->initializePlugin();
|
||||
profilingReport("<initializePlugin", spec, &spec->d->performanceData.initialize);
|
||||
spec->initializePlugin();
|
||||
profilingReport("<initializePlugin", spec, &spec->performanceData().initialize);
|
||||
break;
|
||||
}
|
||||
case PluginSpec::Stopped:
|
||||
profilingReport(">stop", spec);
|
||||
if (spec->d->stop() == IPlugin::AsynchronousShutdown) {
|
||||
if (spec->stop() == IPlugin::AsynchronousShutdown) {
|
||||
asynchronousPlugins << spec;
|
||||
connect(spec->plugin(), &IPlugin::asynchronousShutdownFinished, this, [this, spec] {
|
||||
asynchronousPlugins.remove(spec);
|
||||
@@ -1753,45 +1752,24 @@ static const QStringList pluginFiles(const QStringList &pluginPaths)
|
||||
return pluginFiles;
|
||||
}
|
||||
|
||||
/*!
|
||||
\internal
|
||||
*/
|
||||
void PluginManagerPrivate::readPluginPaths()
|
||||
void PluginManagerPrivate::addPlugins(const QVector<PluginSpec *> &specs)
|
||||
{
|
||||
qDeleteAll(pluginSpecs);
|
||||
pluginSpecs.clear();
|
||||
pluginCategories.clear();
|
||||
pluginSpecs += specs;
|
||||
|
||||
// default
|
||||
pluginCategories.insert(QString(), QVector<PluginSpec *>());
|
||||
|
||||
// from the file system
|
||||
for (const QString &pluginFile : pluginFiles(pluginPaths)) {
|
||||
PluginSpec *spec = PluginSpec::read(pluginFile);
|
||||
if (spec) // Qt Creator plugin
|
||||
pluginSpecs.append(spec);
|
||||
}
|
||||
// static
|
||||
for (const QStaticPlugin &plugin : QPluginLoader::staticPlugins()) {
|
||||
PluginSpec *spec = PluginSpec::read(plugin);
|
||||
if (spec) // Qt Creator plugin
|
||||
pluginSpecs.append(spec);
|
||||
}
|
||||
|
||||
for (PluginSpec *spec : pluginSpecs) {
|
||||
for (PluginSpec *spec : specs) {
|
||||
// defaultDisabledPlugins and defaultEnabledPlugins from install settings
|
||||
// is used to override the defaults read from the plugin spec
|
||||
if (spec->isEnabledByDefault() && defaultDisabledPlugins.contains(spec->name())) {
|
||||
spec->d->setEnabledByDefault(false);
|
||||
spec->d->setEnabledBySettings(false);
|
||||
spec->setEnabledByDefault(false);
|
||||
spec->setEnabledBySettings(false);
|
||||
} else if (!spec->isEnabledByDefault() && defaultEnabledPlugins.contains(spec->name())) {
|
||||
spec->d->setEnabledByDefault(true);
|
||||
spec->d->setEnabledBySettings(true);
|
||||
spec->setEnabledByDefault(true);
|
||||
spec->setEnabledBySettings(true);
|
||||
}
|
||||
if (!spec->isEnabledByDefault() && forceEnabledPlugins.contains(spec->name()))
|
||||
spec->d->setEnabledBySettings(true);
|
||||
spec->setEnabledBySettings(true);
|
||||
if (spec->isEnabledByDefault() && disabledPlugins.contains(spec->name()))
|
||||
spec->d->setEnabledBySettings(false);
|
||||
spec->setEnabledBySettings(false);
|
||||
|
||||
pluginCategories[spec->category()].append(spec);
|
||||
}
|
||||
@@ -1803,21 +1781,49 @@ void PluginManagerPrivate::readPluginPaths()
|
||||
emit q->pluginsChanged();
|
||||
}
|
||||
|
||||
/*!
|
||||
\internal
|
||||
*/
|
||||
void PluginManagerPrivate::readPluginPaths()
|
||||
{
|
||||
QVector<PluginSpec *> newSpecs;
|
||||
|
||||
// from the file system
|
||||
for (const QString &pluginFile : pluginFiles(pluginPaths)) {
|
||||
expected_str<PluginSpec *> spec = PluginSpecImpl::read(pluginFile);
|
||||
if (!spec) {
|
||||
qCWarning(pluginLog).noquote()
|
||||
<< QString("Ignoring plugin \"%1\" because: %2").arg(pluginFile).arg(spec.error());
|
||||
continue;
|
||||
}
|
||||
newSpecs.append(*spec);
|
||||
}
|
||||
|
||||
// static
|
||||
for (const QStaticPlugin &plugin : QPluginLoader::staticPlugins()) {
|
||||
expected_str<PluginSpec *> spec = PluginSpecImpl::read(plugin);
|
||||
QTC_ASSERT_EXPECTED(spec, continue);
|
||||
newSpecs.append(*spec);
|
||||
}
|
||||
|
||||
addPlugins(newSpecs);
|
||||
}
|
||||
|
||||
void PluginManagerPrivate::resolveDependencies()
|
||||
{
|
||||
for (PluginSpec *spec : std::as_const(pluginSpecs))
|
||||
spec->d->resolveDependencies(pluginSpecs);
|
||||
spec->resolveDependencies(pluginSpecs);
|
||||
}
|
||||
|
||||
void PluginManagerPrivate::enableDependenciesIndirectly()
|
||||
{
|
||||
for (PluginSpec *spec : std::as_const(pluginSpecs))
|
||||
spec->d->enabledIndirectly = false;
|
||||
spec->setEnabledIndirectly(false);
|
||||
// cannot use reverse loadQueue here, because test dependencies can introduce circles
|
||||
QVector<PluginSpec *> queue = Utils::filtered(pluginSpecs, &PluginSpec::isEffectivelyEnabled);
|
||||
while (!queue.isEmpty()) {
|
||||
PluginSpec *spec = queue.takeFirst();
|
||||
queue += spec->d->enableDependenciesIndirectly(containsTestSpec(spec));
|
||||
queue += spec->enableDependenciesIndirectly(containsTestSpec(spec));
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -82,6 +82,8 @@ public:
|
||||
static void checkForProblematicPlugins();
|
||||
static PluginSpec *specForPlugin(IPlugin *plugin);
|
||||
|
||||
static void addPlugins(const QVector<PluginSpec *> &specs);
|
||||
|
||||
// Settings
|
||||
static void setSettings(Utils::QtcSettings *settings);
|
||||
static Utils::QtcSettings *settings();
|
||||
|
@@ -36,8 +36,6 @@ class PluginManager;
|
||||
|
||||
namespace Internal {
|
||||
|
||||
class PluginSpecPrivate;
|
||||
|
||||
class EXTENSIONSYSTEM_TEST_EXPORT PluginManagerPrivate : public QObject
|
||||
{
|
||||
public:
|
||||
@@ -52,6 +50,8 @@ public:
|
||||
void checkForProblematicPlugins();
|
||||
void loadPlugins();
|
||||
void loadPluginsAtRuntime(const QSet<PluginSpec *> &plugins);
|
||||
void addPlugins(const QVector<PluginSpec *> &specs);
|
||||
|
||||
void shutdown();
|
||||
void setPluginPaths(const QStringList &paths);
|
||||
const QVector<ExtensionSystem::PluginSpec *> loadQueue();
|
||||
@@ -119,10 +119,6 @@ public:
|
||||
PluginSpec *pluginForOption(const QString &option, bool *requiresArgument) const;
|
||||
PluginSpec *pluginByName(const QString &name) const;
|
||||
|
||||
// used by tests
|
||||
static PluginSpec *createSpec();
|
||||
static PluginSpecPrivate *privateSpec(PluginSpec *spec);
|
||||
|
||||
static void addTestCreator(IPlugin *plugin, const std::function<QObject *()> &testCreator);
|
||||
|
||||
mutable QReadWriteLock m_lock;
|
||||
|
@@ -6,7 +6,6 @@
|
||||
#include "extensionsystemtr.h"
|
||||
#include "iplugin.h"
|
||||
#include "pluginmanager.h"
|
||||
#include "pluginspec_p.h"
|
||||
|
||||
#include <utils/algorithm.h>
|
||||
#include <utils/hostosinfo.h>
|
||||
@@ -25,6 +24,7 @@
|
||||
#include <QPluginLoader>
|
||||
|
||||
using namespace ExtensionSystem::Internal;
|
||||
using namespace Utils;
|
||||
|
||||
namespace ExtensionSystem {
|
||||
|
||||
@@ -168,22 +168,78 @@ QString PluginDependency::toString() const
|
||||
return name + " (" + version + typeString(type) + ")";
|
||||
}
|
||||
|
||||
/*!
|
||||
\internal
|
||||
*/
|
||||
PluginSpec::PluginSpec()
|
||||
: d(new PluginSpecPrivate(this))
|
||||
namespace Internal {
|
||||
class PluginSpecImplPrivate
|
||||
{
|
||||
}
|
||||
public:
|
||||
std::optional<QPluginLoader> loader;
|
||||
std::optional<QStaticPlugin> staticPlugin;
|
||||
|
||||
IPlugin *plugin;
|
||||
};
|
||||
|
||||
class PluginSpecPrivate
|
||||
{
|
||||
public:
|
||||
ExtensionSystem::PerformanceData performanceData;
|
||||
|
||||
QString name;
|
||||
QString version;
|
||||
QString compatVersion;
|
||||
QString vendor;
|
||||
QString category;
|
||||
QString description;
|
||||
QString longDescription;
|
||||
QString url;
|
||||
QString license;
|
||||
QString revision;
|
||||
QString copyright;
|
||||
QStringList arguments;
|
||||
QRegularExpression platformSpecification;
|
||||
QVector<ExtensionSystem::PluginDependency> dependencies;
|
||||
|
||||
PluginSpecImpl::PluginArgumentDescriptions argumentDescriptions;
|
||||
QString location;
|
||||
QString filePath;
|
||||
|
||||
bool experimental{false};
|
||||
bool deprecated{false};
|
||||
bool required{false};
|
||||
|
||||
bool enabledByDefault{false};
|
||||
bool enabledBySettings{true};
|
||||
bool enabledIndirectly{false};
|
||||
bool forceEnabled{false};
|
||||
bool forceDisabled{false};
|
||||
bool softLoadable{false};
|
||||
|
||||
std::optional<QString> errorString;
|
||||
|
||||
PluginSpec::State state;
|
||||
QHash<PluginDependency, PluginSpec *> dependencySpecs;
|
||||
|
||||
QJsonObject metaData;
|
||||
|
||||
Utils::expected_str<void> readMetaData(const QJsonObject &metaData);
|
||||
Utils::expected_str<void> reportError(const QString &error)
|
||||
{
|
||||
errorString = error;
|
||||
return {};
|
||||
};
|
||||
};
|
||||
} // namespace Internal
|
||||
|
||||
/*!
|
||||
\internal
|
||||
*/
|
||||
PluginSpec::~PluginSpec()
|
||||
{
|
||||
delete d;
|
||||
d = nullptr;
|
||||
}
|
||||
PluginSpecImpl::PluginSpecImpl()
|
||||
: d(new PluginSpecImplPrivate)
|
||||
{}
|
||||
|
||||
/*!
|
||||
\internal
|
||||
*/
|
||||
PluginSpecImpl::~PluginSpecImpl() = default;
|
||||
|
||||
/*!
|
||||
Returns the plugin name. This is valid after the PluginSpec::Read state is
|
||||
@@ -278,10 +334,7 @@ QString PluginSpec::category() const
|
||||
|
||||
QString PluginSpec::revision() const
|
||||
{
|
||||
const QJsonValue revision = metaData().value("Revision");
|
||||
if (revision.isString())
|
||||
return revision.toString();
|
||||
return QString();
|
||||
return d->revision;
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -419,7 +472,7 @@ QJsonObject PluginSpec::metaData() const
|
||||
return d->metaData;
|
||||
}
|
||||
|
||||
const PerformanceData &PluginSpec::performanceData() const
|
||||
PerformanceData &PluginSpec::performanceData() const
|
||||
{
|
||||
return d->performanceData;
|
||||
}
|
||||
@@ -491,7 +544,7 @@ PluginSpec::State PluginSpec::state() const
|
||||
*/
|
||||
bool PluginSpec::hasError() const
|
||||
{
|
||||
return d->hasError;
|
||||
return d->errorString.has_value();
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -500,7 +553,7 @@ bool PluginSpec::hasError() const
|
||||
*/
|
||||
QString PluginSpec::errorString() const
|
||||
{
|
||||
return d->errorString;
|
||||
return d->errorString.value_or(QString());
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -509,9 +562,13 @@ QString PluginSpec::errorString() const
|
||||
|
||||
\sa PluginSpec::dependencies()
|
||||
*/
|
||||
bool PluginSpec::provides(const QString &pluginName, const QString &version) const
|
||||
bool PluginSpec::provides(const QString &pluginName, const QString &pluginVersion) const
|
||||
{
|
||||
return d->provides(pluginName, version);
|
||||
if (QString::compare(pluginName, name(), Qt::CaseInsensitive) != 0)
|
||||
return false;
|
||||
|
||||
return (versionCompare(version(), pluginVersion) >= 0)
|
||||
&& (versionCompare(compatVersion(), pluginVersion) <= 0);
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -519,7 +576,7 @@ bool PluginSpec::provides(const QString &pluginName, const QString &version) con
|
||||
already been successfully loaded. That is, the PluginSpec::Loaded state
|
||||
is reached.
|
||||
*/
|
||||
IPlugin *PluginSpec::plugin() const
|
||||
IPlugin *PluginSpecImpl::plugin() const
|
||||
{
|
||||
return d->plugin;
|
||||
}
|
||||
@@ -548,6 +605,11 @@ bool PluginSpec::requiresAny(const QSet<PluginSpec *> &plugins) const
|
||||
return false;
|
||||
}
|
||||
|
||||
void PluginSpec::setEnabledByDefault(bool value)
|
||||
{
|
||||
d->enabledByDefault = value;
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets whether the plugin should be loaded at startup to \a value.
|
||||
|
||||
@@ -555,27 +617,40 @@ bool PluginSpec::requiresAny(const QSet<PluginSpec *> &plugins) const
|
||||
*/
|
||||
void PluginSpec::setEnabledBySettings(bool value)
|
||||
{
|
||||
d->setEnabledBySettings(value);
|
||||
d->enabledBySettings = value;
|
||||
}
|
||||
void PluginSpec::setEnabledIndirectly(bool value)
|
||||
{
|
||||
d->enabledIndirectly = value;
|
||||
}
|
||||
void PluginSpec::setForceDisabled(bool value)
|
||||
{
|
||||
d->forceDisabled = value;
|
||||
}
|
||||
void PluginSpec::setForceEnabled(bool value)
|
||||
{
|
||||
d->forceEnabled = value;
|
||||
}
|
||||
|
||||
PluginSpec *PluginSpec::read(const QString &filePath)
|
||||
// returns the plugins that it actually indirectly enabled
|
||||
QVector<PluginSpec *> PluginSpec::enableDependenciesIndirectly(bool enableTestDependencies)
|
||||
{
|
||||
auto spec = new PluginSpec;
|
||||
if (!spec->d->read(filePath)) { // not a Qt Creator plugin
|
||||
delete spec;
|
||||
return nullptr;
|
||||
}
|
||||
return spec;
|
||||
}
|
||||
if (!isEffectivelyEnabled()) // plugin not enabled, nothing to do
|
||||
return {};
|
||||
|
||||
PluginSpec *PluginSpec::read(const QStaticPlugin &plugin)
|
||||
{
|
||||
auto spec = new PluginSpec;
|
||||
if (!spec->d->read(plugin)) { // not a Qt Creator plugin
|
||||
delete spec;
|
||||
return nullptr;
|
||||
QVector<PluginSpec *> enabled;
|
||||
for (auto it = d->dependencySpecs.cbegin(), end = d->dependencySpecs.cend(); it != end; ++it) {
|
||||
if (it.key().type != PluginDependency::Required
|
||||
&& (!enableTestDependencies || it.key().type != PluginDependency::Test))
|
||||
continue;
|
||||
|
||||
PluginSpec *dependencySpec = it.value();
|
||||
if (!dependencySpec->isEffectivelyEnabled()) {
|
||||
dependencySpec->setEnabledIndirectly(true);
|
||||
enabled << dependencySpec;
|
||||
}
|
||||
}
|
||||
return spec;
|
||||
return enabled;
|
||||
}
|
||||
|
||||
//==========PluginSpecPrivate==================
|
||||
@@ -610,112 +685,45 @@ namespace {
|
||||
const char ARGUMENT_PARAMETER[] = "Parameter";
|
||||
const char ARGUMENT_DESCRIPTION[] = "Description";
|
||||
}
|
||||
/*!
|
||||
\internal
|
||||
*/
|
||||
PluginSpecPrivate::PluginSpecPrivate(PluginSpec *spec)
|
||||
: q(spec)
|
||||
{}
|
||||
|
||||
void PluginSpecPrivate::reset()
|
||||
{
|
||||
name.clear();
|
||||
version.clear();
|
||||
compatVersion.clear();
|
||||
vendor.clear();
|
||||
copyright.clear();
|
||||
license.clear();
|
||||
description.clear();
|
||||
longDescription.clear();
|
||||
url.clear();
|
||||
category.clear();
|
||||
location.clear();
|
||||
filePath.clear();
|
||||
state = PluginSpec::Invalid;
|
||||
hasError = false;
|
||||
errorString.clear();
|
||||
dependencies.clear();
|
||||
metaData = QJsonObject();
|
||||
loader.reset();
|
||||
staticPlugin.reset();
|
||||
}
|
||||
|
||||
/*!
|
||||
\internal
|
||||
Returns false if the file does not represent a Qt Creator plugin.
|
||||
*/
|
||||
bool PluginSpecPrivate::read(const QString &fileName)
|
||||
expected_str<PluginSpec *> PluginSpecImpl::read(const QString &fileName)
|
||||
{
|
||||
qCDebug(pluginLog) << "\nReading meta data of" << fileName;
|
||||
reset();
|
||||
auto spec = new PluginSpecImpl;
|
||||
|
||||
QFileInfo fileInfo(fileName);
|
||||
location = fileInfo.absolutePath();
|
||||
filePath = fileInfo.absoluteFilePath();
|
||||
loader.emplace();
|
||||
spec->setLocation(fileInfo.absolutePath());
|
||||
spec->setFilePath(fileInfo.absoluteFilePath());
|
||||
spec->d->loader.emplace();
|
||||
|
||||
if (Utils::HostOsInfo::isMacHost())
|
||||
loader->setLoadHints(QLibrary::ExportExternalSymbolsHint);
|
||||
loader->setFileName(filePath);
|
||||
if (loader->fileName().isEmpty()) {
|
||||
qCDebug(pluginLog) << "Cannot open file";
|
||||
return false;
|
||||
}
|
||||
spec->d->loader->setLoadHints(QLibrary::ExportExternalSymbolsHint);
|
||||
|
||||
if (!readMetaData(loader->metaData()))
|
||||
return false;
|
||||
spec->d->loader->setFileName(fileInfo.absoluteFilePath());
|
||||
if (spec->d->loader->fileName().isEmpty())
|
||||
return make_unexpected(::ExtensionSystem::Tr::tr("Cannot open file"));
|
||||
|
||||
state = PluginSpec::Read;
|
||||
return true;
|
||||
expected_str<void> r = spec->readMetaData(spec->d->loader->metaData());
|
||||
if (!r)
|
||||
return make_unexpected(r.error());
|
||||
|
||||
return spec;
|
||||
}
|
||||
|
||||
bool PluginSpecPrivate::read(const QStaticPlugin &plugin)
|
||||
expected_str<PluginSpec *> PluginSpecImpl::read(const QStaticPlugin &plugin)
|
||||
{
|
||||
auto spec = new PluginSpecImpl;
|
||||
|
||||
qCDebug(pluginLog) << "\nReading meta data of static plugin";
|
||||
reset();
|
||||
staticPlugin = plugin;
|
||||
if (!readMetaData(plugin.metaData()))
|
||||
return false;
|
||||
spec->d->staticPlugin = plugin;
|
||||
expected_str<void> r = spec->readMetaData(plugin.metaData());
|
||||
if (!r)
|
||||
return make_unexpected(r.error());
|
||||
|
||||
state = PluginSpec::Read;
|
||||
return true;
|
||||
}
|
||||
|
||||
void PluginSpecPrivate::setEnabledBySettings(bool value)
|
||||
{
|
||||
enabledBySettings = value;
|
||||
}
|
||||
|
||||
void PluginSpecPrivate::setEnabledByDefault(bool value)
|
||||
{
|
||||
enabledByDefault = value;
|
||||
}
|
||||
|
||||
void PluginSpecPrivate::setForceEnabled(bool value)
|
||||
{
|
||||
forceEnabled = value;
|
||||
if (value)
|
||||
forceDisabled = false;
|
||||
}
|
||||
|
||||
void PluginSpecPrivate::setForceDisabled(bool value)
|
||||
{
|
||||
if (value)
|
||||
forceEnabled = false;
|
||||
forceDisabled = value;
|
||||
}
|
||||
|
||||
void PluginSpecPrivate::setSoftLoadable(bool value)
|
||||
{
|
||||
softLoadable = value;
|
||||
}
|
||||
|
||||
/*!
|
||||
\internal
|
||||
*/
|
||||
bool PluginSpecPrivate::reportError(const QString &err)
|
||||
{
|
||||
errorString = err;
|
||||
hasError = true;
|
||||
return true;
|
||||
return spec;
|
||||
}
|
||||
|
||||
static inline QString msgValueMissing(const char *key)
|
||||
@@ -725,58 +733,67 @@ static inline QString msgValueMissing(const char *key)
|
||||
|
||||
static inline QString msgValueIsNotAString(const char *key)
|
||||
{
|
||||
return Tr::tr("Value for key \"%1\" is not a string")
|
||||
.arg(QLatin1String(key));
|
||||
return Tr::tr("Value for key \"%1\" is not a string").arg(QLatin1String(key));
|
||||
}
|
||||
|
||||
static inline QString msgValueIsNotABool(const char *key)
|
||||
{
|
||||
return Tr::tr("Value for key \"%1\" is not a bool")
|
||||
.arg(QLatin1String(key));
|
||||
return Tr::tr("Value for key \"%1\" is not a bool").arg(QLatin1String(key));
|
||||
}
|
||||
|
||||
static inline QString msgValueIsNotAObjectArray(const char *key)
|
||||
{
|
||||
return Tr::tr("Value for key \"%1\" is not an array of objects")
|
||||
.arg(QLatin1String(key));
|
||||
return Tr::tr("Value for key \"%1\" is not an array of objects").arg(QLatin1String(key));
|
||||
}
|
||||
|
||||
static inline QString msgValueIsNotAMultilineString(const char *key)
|
||||
{
|
||||
return Tr::tr("Value for key \"%1\" is not a string and not an array of strings")
|
||||
.arg(QLatin1String(key));
|
||||
.arg(QLatin1String(key));
|
||||
}
|
||||
|
||||
static inline QString msgInvalidFormat(const char *key, const QString &content)
|
||||
{
|
||||
return Tr::tr("Value \"%2\" for key \"%1\" has invalid format")
|
||||
.arg(QLatin1String(key), content);
|
||||
return Tr::tr("Value \"%2\" for key \"%1\" has invalid format").arg(QLatin1String(key), content);
|
||||
}
|
||||
|
||||
Utils::expected_str<void> PluginSpec::readMetaData(const QJsonObject &metaData)
|
||||
{
|
||||
return d->readMetaData(metaData);
|
||||
}
|
||||
Utils::expected_str<void> PluginSpec::reportError(const QString &error)
|
||||
{
|
||||
return d->reportError(error);
|
||||
}
|
||||
|
||||
/*!
|
||||
\internal
|
||||
*/
|
||||
bool PluginSpecPrivate::readMetaData(const QJsonObject &pluginMetaData)
|
||||
expected_str<void> PluginSpecImpl::readMetaData(const QJsonObject &pluginMetaData)
|
||||
{
|
||||
qCDebug(pluginLog) << "MetaData:" << QJsonDocument(pluginMetaData).toJson();
|
||||
qCDebug(pluginLog).noquote() << "MetaData:" << QJsonDocument(pluginMetaData).toJson();
|
||||
QJsonValue value;
|
||||
value = pluginMetaData.value(QLatin1String("IID"));
|
||||
if (!value.isString()) {
|
||||
qCDebug(pluginLog) << "Not a plugin (no string IID found)";
|
||||
return false;
|
||||
}
|
||||
if (value.toString() != PluginManager::pluginIID()) {
|
||||
qCDebug(pluginLog) << "Plugin ignored (IID does not match)";
|
||||
return false;
|
||||
}
|
||||
if (!value.isString())
|
||||
return make_unexpected(::ExtensionSystem::Tr::tr("No IID found"));
|
||||
|
||||
if (value.toString() != PluginManager::pluginIID())
|
||||
return make_unexpected(::ExtensionSystem::Tr::tr("Expected IID \"%1\", but found \"%2\"")
|
||||
.arg(PluginManager::pluginIID())
|
||||
.arg(value.toString()));
|
||||
|
||||
value = pluginMetaData.value(QLatin1String(PLUGIN_METADATA));
|
||||
if (!value.isObject()) {
|
||||
if (!value.isObject())
|
||||
return reportError(::ExtensionSystem::Tr::tr("Plugin meta data not found"));
|
||||
}
|
||||
metaData = value.toObject();
|
||||
|
||||
value = metaData.value(QLatin1String(PLUGIN_NAME));
|
||||
return PluginSpec::readMetaData(value.toObject());
|
||||
}
|
||||
|
||||
Utils::expected_str<void> PluginSpecPrivate::readMetaData(const QJsonObject &data)
|
||||
{
|
||||
metaData = data;
|
||||
|
||||
QJsonValue value = metaData.value(QLatin1String(PLUGIN_NAME));
|
||||
if (value.isUndefined())
|
||||
return reportError(msgValueMissing(PLUGIN_NAME));
|
||||
if (!value.isString())
|
||||
@@ -789,14 +806,14 @@ bool PluginSpecPrivate::readMetaData(const QJsonObject &pluginMetaData)
|
||||
if (!value.isString())
|
||||
return reportError(msgValueIsNotAString(PLUGIN_VERSION));
|
||||
version = value.toString();
|
||||
if (!isValidVersion(version))
|
||||
if (!PluginSpec::isValidVersion(version))
|
||||
return reportError(msgInvalidFormat(PLUGIN_VERSION, version));
|
||||
|
||||
value = metaData.value(QLatin1String(PLUGIN_COMPATVERSION));
|
||||
if (!value.isUndefined() && !value.isString())
|
||||
return reportError(msgValueIsNotAString(PLUGIN_COMPATVERSION));
|
||||
compatVersion = value.toString(version);
|
||||
if (!value.isUndefined() && !isValidVersion(compatVersion))
|
||||
if (!value.isUndefined() && !PluginSpec::isValidVersion(compatVersion))
|
||||
return reportError(msgInvalidFormat(PLUGIN_COMPATVERSION, compatVersion));
|
||||
|
||||
value = metaData.value(QLatin1String(PLUGIN_REQUIRED));
|
||||
@@ -844,11 +861,11 @@ bool PluginSpecPrivate::readMetaData(const QJsonObject &pluginMetaData)
|
||||
copyright = value.toString();
|
||||
|
||||
value = metaData.value(QLatin1String(DESCRIPTION));
|
||||
if (!value.isUndefined() && !Utils::readMultiLineString(value, &description))
|
||||
if (!value.isUndefined() && !readMultiLineString(value, &description))
|
||||
return reportError(msgValueIsNotAString(DESCRIPTION));
|
||||
|
||||
value = metaData.value(QLatin1String(LONGDESCRIPTION));
|
||||
if (!value.isUndefined() && !Utils::readMultiLineString(value, &longDescription))
|
||||
if (!value.isUndefined() && !readMultiLineString(value, &longDescription))
|
||||
return reportError(msgValueIsNotAString(LONGDESCRIPTION));
|
||||
|
||||
value = metaData.value(QLatin1String(URL));
|
||||
@@ -862,9 +879,14 @@ bool PluginSpecPrivate::readMetaData(const QJsonObject &pluginMetaData)
|
||||
category = value.toString();
|
||||
|
||||
value = metaData.value(QLatin1String(LICENSE));
|
||||
if (!value.isUndefined() && !Utils::readMultiLineString(value, &license))
|
||||
if (!value.isUndefined() && !readMultiLineString(value, &license))
|
||||
return reportError(msgValueIsNotAMultilineString(LICENSE));
|
||||
|
||||
value = metaData.value("Revision");
|
||||
if (!value.isUndefined() && !value.isString())
|
||||
return reportError(msgValueIsNotAString("Revision"));
|
||||
revision = value.toString();
|
||||
|
||||
value = metaData.value(QLatin1String(PLATFORM));
|
||||
if (!value.isUndefined() && !value.isString())
|
||||
return reportError(msgValueIsNotAString(PLATFORM));
|
||||
@@ -906,7 +928,7 @@ bool PluginSpecPrivate::readMetaData(const QJsonObject &pluginMetaData)
|
||||
.arg(msgValueIsNotAString(DEPENDENCY_VERSION)));
|
||||
}
|
||||
dep.version = value.toString();
|
||||
if (!isValidVersion(dep.version)) {
|
||||
if (!PluginSpec::isValidVersion(dep.version)) {
|
||||
return reportError(
|
||||
::ExtensionSystem::Tr::tr("Dependency: %1")
|
||||
.arg(msgInvalidFormat(DEPENDENCY_VERSION, dep.version)));
|
||||
@@ -987,23 +1009,15 @@ bool PluginSpecPrivate::readMetaData(const QJsonObject &pluginMetaData)
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
state = PluginSpecImpl::Read;
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
/*!
|
||||
\internal
|
||||
*/
|
||||
bool PluginSpecPrivate::provides(const QString &pluginName, const QString &pluginVersion) const
|
||||
{
|
||||
if (QString::compare(pluginName, name, Qt::CaseInsensitive) != 0)
|
||||
return false;
|
||||
return (versionCompare(version, pluginVersion) >= 0) && (versionCompare(compatVersion, pluginVersion) <= 0);
|
||||
}
|
||||
|
||||
/*!
|
||||
\internal
|
||||
*/
|
||||
const QRegularExpression &PluginSpecPrivate::versionRegExp()
|
||||
static const QRegularExpression &versionRegExp()
|
||||
{
|
||||
static const QRegularExpression reg("^([0-9]+)(?:[.]([0-9]+))?(?:[.]([0-9]+))?(?:_([0-9]+))?$");
|
||||
return reg;
|
||||
@@ -1011,7 +1025,7 @@ const QRegularExpression &PluginSpecPrivate::versionRegExp()
|
||||
/*!
|
||||
\internal
|
||||
*/
|
||||
bool PluginSpecPrivate::isValidVersion(const QString &version)
|
||||
bool PluginSpec::isValidVersion(const QString &version)
|
||||
{
|
||||
return versionRegExp().match(version).hasMatch();
|
||||
}
|
||||
@@ -1019,7 +1033,7 @@ bool PluginSpecPrivate::isValidVersion(const QString &version)
|
||||
/*!
|
||||
\internal
|
||||
*/
|
||||
int PluginSpecPrivate::versionCompare(const QString &version1, const QString &version2)
|
||||
int PluginSpec::versionCompare(const QString &version1, const QString &version2)
|
||||
{
|
||||
const QRegularExpressionMatch match1 = versionRegExp().match(version1);
|
||||
const QRegularExpressionMatch match2 = versionRegExp().match(version2);
|
||||
@@ -1041,196 +1055,205 @@ int PluginSpecPrivate::versionCompare(const QString &version1, const QString &ve
|
||||
/*!
|
||||
\internal
|
||||
*/
|
||||
bool PluginSpecPrivate::resolveDependencies(const QVector<PluginSpec *> &specs)
|
||||
bool PluginSpec::resolveDependencies(const QVector<PluginSpec *> &specs)
|
||||
{
|
||||
if (hasError)
|
||||
if (hasError())
|
||||
return false;
|
||||
if (state == PluginSpec::Resolved)
|
||||
state = PluginSpec::Read; // Go back, so we just re-resolve the dependencies.
|
||||
if (state != PluginSpec::Read) {
|
||||
errorString = ::ExtensionSystem::Tr::tr("Resolving dependencies failed because state != Read");
|
||||
hasError = true;
|
||||
if (state() > PluginSpecImpl::Resolved)
|
||||
return true; // We are resolved already.
|
||||
if (state() == PluginSpecImpl::Resolved)
|
||||
setState(PluginSpecImpl::Read); // Go back, so we just re-resolve the dependencies.
|
||||
if (state() != PluginSpecImpl::Read) {
|
||||
setError(::ExtensionSystem::Tr::tr("Resolving dependencies failed because state != Read"));
|
||||
return false;
|
||||
}
|
||||
|
||||
QHash<PluginDependency, PluginSpec *> resolvedDependencies;
|
||||
for (const PluginDependency &dependency : std::as_const(dependencies)) {
|
||||
PluginSpec * const found = Utils::findOrDefault(specs, [&dependency](PluginSpec *spec) {
|
||||
for (const PluginDependency &dependency : d->dependencies) {
|
||||
PluginSpec *const found = findOrDefault(specs, [&dependency](PluginSpec *spec) {
|
||||
return spec->provides(dependency.name, dependency.version);
|
||||
});
|
||||
if (!found) {
|
||||
if (dependency.type == PluginDependency::Required) {
|
||||
hasError = true;
|
||||
if (!errorString.isEmpty())
|
||||
errorString.append(QLatin1Char('\n'));
|
||||
errorString.append(::ExtensionSystem::Tr::tr("Could not resolve dependency '%1(%2)'")
|
||||
.arg(dependency.name, dependency.version));
|
||||
const QString error = ::ExtensionSystem::Tr::tr(
|
||||
"Could not resolve dependency '%1(%2)'")
|
||||
.arg(dependency.name, dependency.version);
|
||||
if (hasError())
|
||||
setError(errorString() + '\n' + error);
|
||||
else
|
||||
setError(error);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
resolvedDependencies.insert(dependency, found);
|
||||
}
|
||||
if (hasError)
|
||||
if (hasError())
|
||||
return false;
|
||||
|
||||
dependencySpecs = resolvedDependencies;
|
||||
d->dependencySpecs = resolvedDependencies;
|
||||
|
||||
state = PluginSpec::Resolved;
|
||||
d->state = PluginSpecImpl::Resolved;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// returns the plugins that it actually indirectly enabled
|
||||
QVector<PluginSpec *> PluginSpecPrivate::enableDependenciesIndirectly(bool enableTestDependencies)
|
||||
PluginSpec::PluginSpec()
|
||||
: d(new PluginSpecPrivate())
|
||||
{}
|
||||
|
||||
PluginSpec::~PluginSpec() = default;
|
||||
|
||||
void PluginSpec::setState(State state)
|
||||
{
|
||||
if (!q->isEffectivelyEnabled()) // plugin not enabled, nothing to do
|
||||
return {};
|
||||
QVector<PluginSpec *> enabled;
|
||||
for (auto it = dependencySpecs.cbegin(), end = dependencySpecs.cend(); it != end; ++it) {
|
||||
if (it.key().type != PluginDependency::Required
|
||||
&& (!enableTestDependencies || it.key().type != PluginDependency::Test))
|
||||
continue;
|
||||
PluginSpec *dependencySpec = it.value();
|
||||
if (!dependencySpec->isEffectivelyEnabled()) {
|
||||
dependencySpec->d->enabledIndirectly = true;
|
||||
enabled << dependencySpec;
|
||||
}
|
||||
}
|
||||
return enabled;
|
||||
d->state = state;
|
||||
}
|
||||
|
||||
void PluginSpec::setLocation(const QString &location)
|
||||
{
|
||||
d->location = location;
|
||||
}
|
||||
|
||||
void PluginSpec::setFilePath(const QString &filePath)
|
||||
{
|
||||
d->filePath = filePath;
|
||||
}
|
||||
|
||||
void PluginSpec::setError(const QString &errorString)
|
||||
{
|
||||
d->errorString = errorString;
|
||||
}
|
||||
|
||||
/*!
|
||||
\internal
|
||||
*/
|
||||
bool PluginSpecPrivate::loadLibrary()
|
||||
bool PluginSpecImpl::loadLibrary()
|
||||
{
|
||||
if (hasError)
|
||||
if (hasError())
|
||||
return false;
|
||||
if (state != PluginSpec::Resolved) {
|
||||
if (state == PluginSpec::Loaded)
|
||||
|
||||
if (state() != PluginSpecImpl::Resolved) {
|
||||
if (state() == PluginSpecImpl::Loaded)
|
||||
return true;
|
||||
errorString =
|
||||
::ExtensionSystem::Tr::tr("Loading the library failed because state != Resolved");
|
||||
hasError = true;
|
||||
setError(::ExtensionSystem::Tr::tr("Loading the library failed because state != Resolved"));
|
||||
return false;
|
||||
}
|
||||
if (loader && !loader->load()) {
|
||||
hasError = true;
|
||||
errorString = QDir::toNativeSeparators(filePath) + QString::fromLatin1(": ")
|
||||
+ loader->errorString();
|
||||
if (d->loader && !d->loader->load()) {
|
||||
setError(QDir::toNativeSeparators(filePath()) + QString::fromLatin1(": ")
|
||||
+ d->loader->errorString());
|
||||
return false;
|
||||
}
|
||||
auto *pluginObject = loader ? qobject_cast<IPlugin *>(loader->instance())
|
||||
: qobject_cast<IPlugin *>(staticPlugin->instance());
|
||||
auto *pluginObject = d->loader ? qobject_cast<IPlugin *>(d->loader->instance())
|
||||
: qobject_cast<IPlugin *>(d->staticPlugin->instance());
|
||||
if (!pluginObject) {
|
||||
hasError = true;
|
||||
errorString =
|
||||
::ExtensionSystem::Tr::tr("Plugin is not valid (does not derive from IPlugin)");
|
||||
if (loader)
|
||||
loader->unload();
|
||||
setError(::ExtensionSystem::Tr::tr("Plugin is not valid (does not derive from IPlugin)"));
|
||||
if (d->loader)
|
||||
d->loader->unload();
|
||||
return false;
|
||||
}
|
||||
state = PluginSpec::Loaded;
|
||||
plugin = pluginObject;
|
||||
setState(PluginSpecImpl::Loaded);
|
||||
d->plugin = pluginObject;
|
||||
return true;
|
||||
}
|
||||
|
||||
/*!
|
||||
\internal
|
||||
*/
|
||||
bool PluginSpecPrivate::initializePlugin()
|
||||
bool PluginSpecImpl::initializePlugin()
|
||||
{
|
||||
if (hasError)
|
||||
if (hasError())
|
||||
return false;
|
||||
if (state != PluginSpec::Loaded) {
|
||||
if (state == PluginSpec::Initialized)
|
||||
|
||||
if (state() != PluginSpecImpl::Loaded) {
|
||||
if (state() == PluginSpecImpl::Initialized)
|
||||
return true;
|
||||
errorString = ::ExtensionSystem::Tr::tr(
|
||||
"Initializing the plugin failed because state != Loaded");
|
||||
hasError = true;
|
||||
setError(
|
||||
::ExtensionSystem::Tr::tr("Initializing the plugin failed because state != Loaded"));
|
||||
return false;
|
||||
}
|
||||
if (!plugin) {
|
||||
errorString = ::ExtensionSystem::Tr::tr(
|
||||
"Internal error: have no plugin instance to initialize");
|
||||
hasError = true;
|
||||
if (!d->plugin) {
|
||||
setError(
|
||||
::ExtensionSystem::Tr::tr("Internal error: have no plugin instance to initialize"));
|
||||
return false;
|
||||
}
|
||||
QString err;
|
||||
if (!plugin->initialize(arguments, &err)) {
|
||||
errorString = ::ExtensionSystem::Tr::tr("Plugin initialization failed: %1").arg(err);
|
||||
hasError = true;
|
||||
if (!d->plugin->initialize(arguments(), &err)) {
|
||||
setError(::ExtensionSystem::Tr::tr("Plugin initialization failed: %1").arg(err));
|
||||
return false;
|
||||
}
|
||||
state = PluginSpec::Initialized;
|
||||
setState(PluginSpecImpl::Initialized);
|
||||
return true;
|
||||
}
|
||||
|
||||
/*!
|
||||
\internal
|
||||
*/
|
||||
bool PluginSpecPrivate::initializeExtensions()
|
||||
bool PluginSpecImpl::initializeExtensions()
|
||||
{
|
||||
if (hasError)
|
||||
if (hasError())
|
||||
return false;
|
||||
if (state != PluginSpec::Initialized) {
|
||||
if (state == PluginSpec::Running)
|
||||
|
||||
if (state() != PluginSpecImpl::Initialized) {
|
||||
if (state() == PluginSpecImpl::Running)
|
||||
return true;
|
||||
errorString = ::ExtensionSystem::Tr::tr(
|
||||
"Cannot perform extensionsInitialized because state != Initialized");
|
||||
hasError = true;
|
||||
setError(::ExtensionSystem::Tr::tr(
|
||||
"Cannot perform extensionsInitialized because state != Initialized"));
|
||||
return false;
|
||||
}
|
||||
if (!plugin) {
|
||||
errorString = ::ExtensionSystem::Tr::tr(
|
||||
"Internal error: have no plugin instance to perform extensionsInitialized");
|
||||
hasError = true;
|
||||
if (!d->plugin) {
|
||||
setError(::ExtensionSystem::Tr::tr(
|
||||
"Internal error: have no plugin instance to perform extensionsInitialized"));
|
||||
return false;
|
||||
}
|
||||
plugin->extensionsInitialized();
|
||||
state = PluginSpec::Running;
|
||||
d->plugin->extensionsInitialized();
|
||||
setState(PluginSpecImpl::Running);
|
||||
return true;
|
||||
}
|
||||
|
||||
/*!
|
||||
\internal
|
||||
*/
|
||||
bool PluginSpecPrivate::delayedInitialize()
|
||||
bool PluginSpecImpl::delayedInitialize()
|
||||
{
|
||||
if (hasError)
|
||||
if (hasError())
|
||||
return true;
|
||||
|
||||
if (state() != PluginSpecImpl::Running)
|
||||
return false;
|
||||
if (state != PluginSpec::Running)
|
||||
return false;
|
||||
if (!plugin) {
|
||||
errorString = ::ExtensionSystem::Tr::tr(
|
||||
"Internal error: have no plugin instance to perform delayedInitialize");
|
||||
hasError = true;
|
||||
if (!d->plugin) {
|
||||
setError(::ExtensionSystem::Tr::tr(
|
||||
"Internal error: have no plugin instance to perform delayedInitialize"));
|
||||
return false;
|
||||
}
|
||||
const bool res = plugin->delayedInitialize();
|
||||
const bool res = d->plugin->delayedInitialize();
|
||||
return res;
|
||||
}
|
||||
|
||||
/*!
|
||||
\internal
|
||||
*/
|
||||
IPlugin::ShutdownFlag PluginSpecPrivate::stop()
|
||||
IPlugin::ShutdownFlag PluginSpecImpl::stop()
|
||||
{
|
||||
if (!plugin)
|
||||
if (hasError())
|
||||
return IPlugin::ShutdownFlag::SynchronousShutdown;
|
||||
|
||||
if (!d->plugin)
|
||||
return IPlugin::SynchronousShutdown;
|
||||
state = PluginSpec::Stopped;
|
||||
return plugin->aboutToShutdown();
|
||||
setState(PluginSpecImpl::Stopped);
|
||||
return d->plugin->aboutToShutdown();
|
||||
}
|
||||
|
||||
/*!
|
||||
\internal
|
||||
*/
|
||||
void PluginSpecPrivate::kill()
|
||||
void PluginSpecImpl::kill()
|
||||
{
|
||||
if (!plugin)
|
||||
if (hasError())
|
||||
return;
|
||||
delete plugin;
|
||||
plugin = nullptr;
|
||||
state = PluginSpec::Deleted;
|
||||
}
|
||||
|
||||
} // ExtensionSystem
|
||||
if (!d->plugin)
|
||||
return;
|
||||
delete d->plugin;
|
||||
d->plugin = nullptr;
|
||||
setState(PluginSpecImpl::Deleted);
|
||||
}
|
||||
} // namespace ExtensionSystem
|
||||
|
@@ -5,6 +5,10 @@
|
||||
|
||||
#include "extensionsystem_global.h"
|
||||
|
||||
#include "iplugin.h"
|
||||
|
||||
#include <utils/expected.h>
|
||||
|
||||
#include <QHash>
|
||||
#include <QStaticPlugin>
|
||||
#include <QString>
|
||||
@@ -14,17 +18,19 @@ QT_BEGIN_NAMESPACE
|
||||
class QRegularExpression;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
class tst_PluginSpec;
|
||||
|
||||
namespace ExtensionSystem {
|
||||
|
||||
namespace Internal {
|
||||
|
||||
class OptionsParser;
|
||||
class PluginSpecPrivate;
|
||||
class PluginSpecImplPrivate;
|
||||
class PluginManagerPrivate;
|
||||
class PluginSpecPrivate;
|
||||
|
||||
} // Internal
|
||||
|
||||
class IPlugin;
|
||||
class PluginView;
|
||||
|
||||
struct EXTENSIONSYSTEM_EXPORT PluginDependency
|
||||
@@ -37,8 +43,6 @@ struct EXTENSIONSYSTEM_EXPORT PluginDependency
|
||||
|
||||
PluginDependency() : type(Required) {}
|
||||
|
||||
friend size_t qHash(const PluginDependency &value);
|
||||
|
||||
QString name;
|
||||
QString version;
|
||||
Type type;
|
||||
@@ -46,6 +50,8 @@ struct EXTENSIONSYSTEM_EXPORT PluginDependency
|
||||
QString toString() const;
|
||||
};
|
||||
|
||||
size_t EXTENSIONSYSTEM_EXPORT qHash(const PluginDependency &value);
|
||||
|
||||
struct EXTENSIONSYSTEM_EXPORT PluginArgumentDescription
|
||||
{
|
||||
QString name;
|
||||
@@ -73,77 +79,126 @@ struct EXTENSIONSYSTEM_EXPORT PerformanceData
|
||||
|
||||
class EXTENSIONSYSTEM_EXPORT PluginSpec
|
||||
{
|
||||
friend class ::tst_PluginSpec;
|
||||
friend class Internal::PluginManagerPrivate;
|
||||
friend class Internal::OptionsParser;
|
||||
|
||||
public:
|
||||
enum State { Invalid, Read, Resolved, Loaded, Initialized, Running, Stopped, Deleted};
|
||||
|
||||
~PluginSpec();
|
||||
|
||||
// information from the xml file, valid after 'Read' state is reached
|
||||
QString name() const;
|
||||
QString version() const;
|
||||
QString compatVersion() const;
|
||||
QString vendor() const;
|
||||
QString copyright() const;
|
||||
QString license() const;
|
||||
QString description() const;
|
||||
QString longDescription() const;
|
||||
QString url() const;
|
||||
QString category() const;
|
||||
QString revision() const;
|
||||
QRegularExpression platformSpecification() const;
|
||||
bool isAvailableForHostPlatform() const;
|
||||
bool isRequired() const;
|
||||
bool isExperimental() const;
|
||||
bool isDeprecated() const;
|
||||
bool isEnabledByDefault() const;
|
||||
bool isEnabledBySettings() const;
|
||||
bool isEffectivelyEnabled() const;
|
||||
bool isEnabledIndirectly() const;
|
||||
bool isForceEnabled() const;
|
||||
bool isForceDisabled() const;
|
||||
bool isSoftLoadable() const;
|
||||
QVector<PluginDependency> dependencies() const;
|
||||
QJsonObject metaData() const;
|
||||
const PerformanceData &performanceData() const;
|
||||
PluginSpec();
|
||||
virtual ~PluginSpec();
|
||||
|
||||
using PluginArgumentDescriptions = QVector<PluginArgumentDescription>;
|
||||
PluginArgumentDescriptions argumentDescriptions() const;
|
||||
enum State { Invalid, Read, Resolved, Loaded, Initialized, Running, Stopped, Deleted};
|
||||
|
||||
// other information, valid after 'Read' state is reached
|
||||
QString location() const;
|
||||
QString filePath() const;
|
||||
// information read from the plugin, valid after 'Read' state is reached
|
||||
virtual QString name() const;
|
||||
virtual QString version() const;
|
||||
virtual QString compatVersion() const;
|
||||
virtual QString vendor() const;
|
||||
virtual QString copyright() const;
|
||||
virtual QString license() const;
|
||||
virtual QString description() const;
|
||||
virtual QString longDescription() const;
|
||||
virtual QString url() const;
|
||||
virtual QString category() const;
|
||||
virtual QString revision() const;
|
||||
virtual QRegularExpression platformSpecification() const;
|
||||
|
||||
QStringList arguments() const;
|
||||
void setArguments(const QStringList &arguments);
|
||||
void addArgument(const QString &argument);
|
||||
virtual bool isAvailableForHostPlatform() const;
|
||||
virtual bool isRequired() const;
|
||||
virtual bool isExperimental() const;
|
||||
virtual bool isDeprecated() const;
|
||||
virtual bool isEnabledByDefault() const;
|
||||
virtual bool isEnabledBySettings() const;
|
||||
virtual bool isEffectivelyEnabled() const;
|
||||
virtual bool isEnabledIndirectly() const;
|
||||
virtual bool isForceEnabled() const;
|
||||
virtual bool isForceDisabled() const;
|
||||
virtual bool isSoftLoadable() const;
|
||||
|
||||
bool provides(const QString &pluginName, const QString &version) const;
|
||||
virtual QVector<PluginDependency> dependencies() const;
|
||||
virtual QJsonObject metaData() const;
|
||||
virtual PerformanceData &performanceData() const;
|
||||
virtual PluginArgumentDescriptions argumentDescriptions() const;
|
||||
virtual QString location() const;
|
||||
virtual QString filePath() const;
|
||||
virtual QStringList arguments() const;
|
||||
virtual void setArguments(const QStringList &arguments);
|
||||
virtual void addArgument(const QString &argument);
|
||||
virtual QHash<PluginDependency, PluginSpec *> dependencySpecs() const;
|
||||
|
||||
// dependency specs, valid after 'Resolved' state is reached
|
||||
QHash<PluginDependency, PluginSpec *> dependencySpecs() const;
|
||||
bool requiresAny(const QSet<PluginSpec *> &plugins) const;
|
||||
virtual bool provides(const QString &pluginName, const QString &pluginVersion) const;
|
||||
virtual bool requiresAny(const QSet<PluginSpec *> &plugins) const;
|
||||
virtual QVector<PluginSpec *> enableDependenciesIndirectly(bool enableTestDependencies);
|
||||
virtual bool resolveDependencies(const QVector<PluginSpec *> &pluginSpecs);
|
||||
|
||||
// linked plugin instance, valid after 'Loaded' state is reached
|
||||
IPlugin *plugin() const;
|
||||
virtual IPlugin *plugin() const = 0;
|
||||
virtual State state() const;
|
||||
virtual bool hasError() const;
|
||||
virtual QString errorString() const;
|
||||
|
||||
// state
|
||||
State state() const;
|
||||
bool hasError() const;
|
||||
QString errorString() const;
|
||||
static bool isValidVersion(const QString &version);
|
||||
static int versionCompare(const QString &version1, const QString &version2);
|
||||
|
||||
void setEnabledBySettings(bool value);
|
||||
virtual void setEnabledBySettings(bool value);
|
||||
|
||||
static PluginSpec *read(const QString &filePath);
|
||||
static PluginSpec *read(const QStaticPlugin &plugin);
|
||||
protected:
|
||||
virtual void setEnabledByDefault(bool value);
|
||||
virtual void setEnabledIndirectly(bool value);
|
||||
virtual void setForceDisabled(bool value);
|
||||
virtual void setForceEnabled(bool value);
|
||||
|
||||
virtual bool loadLibrary() = 0;
|
||||
virtual bool initializePlugin() = 0;
|
||||
virtual bool initializeExtensions() = 0;
|
||||
virtual bool delayedInitialize() = 0;
|
||||
virtual IPlugin::ShutdownFlag stop() = 0;
|
||||
virtual void kill() = 0;
|
||||
|
||||
virtual void setError(const QString &errorString);
|
||||
|
||||
protected:
|
||||
virtual void setState(State state);
|
||||
|
||||
virtual void setLocation(const QString &location);
|
||||
virtual void setFilePath(const QString &filePath);
|
||||
virtual Utils::expected_str<void> readMetaData(const QJsonObject &metaData);
|
||||
Utils::expected_str<void> reportError(const QString &error);
|
||||
|
||||
private:
|
||||
PluginSpec();
|
||||
std::unique_ptr<Internal::PluginSpecPrivate> d;
|
||||
};
|
||||
|
||||
Internal::PluginSpecPrivate *d;
|
||||
class EXTENSIONSYSTEM_TEST_EXPORT PluginSpecImpl : public PluginSpec
|
||||
{
|
||||
public:
|
||||
~PluginSpecImpl() override;
|
||||
|
||||
// linked plugin instance, valid after 'Loaded' state is reached
|
||||
IPlugin *plugin() const override;
|
||||
|
||||
bool loadLibrary() override;
|
||||
bool initializePlugin() override;
|
||||
bool initializeExtensions() override;
|
||||
bool delayedInitialize() override;
|
||||
IPlugin::ShutdownFlag stop() override;
|
||||
void kill() override;
|
||||
|
||||
static Utils::expected_str<PluginSpec *> read(const QString &filePath);
|
||||
static Utils::expected_str<PluginSpec *> read(const QStaticPlugin &plugin);
|
||||
|
||||
Utils::expected_str<void> readMetaData(const QJsonObject &pluginMetaData) override;
|
||||
|
||||
protected:
|
||||
PluginSpecImpl();
|
||||
|
||||
private:
|
||||
std::unique_ptr<Internal::PluginSpecImplPrivate> d;
|
||||
friend class PluginView;
|
||||
friend class Internal::OptionsParser;
|
||||
friend class Internal::PluginManagerPrivate;
|
||||
friend class Internal::PluginSpecPrivate;
|
||||
friend class Internal::PluginSpecImplPrivate;
|
||||
friend class ::tst_PluginSpec;
|
||||
};
|
||||
|
||||
} // namespace ExtensionSystem
|
||||
|
@@ -1,107 +0,0 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "pluginspec.h"
|
||||
#include "iplugin.h"
|
||||
|
||||
#include <QJsonObject>
|
||||
#include <QObject>
|
||||
#include <QPluginLoader>
|
||||
#include <QRegularExpression>
|
||||
#include <QStringList>
|
||||
#include <QVector>
|
||||
#include <QXmlStreamReader>
|
||||
|
||||
#include <optional>
|
||||
|
||||
namespace ExtensionSystem {
|
||||
|
||||
class IPlugin;
|
||||
|
||||
namespace Internal {
|
||||
|
||||
class EXTENSIONSYSTEM_TEST_EXPORT PluginSpecPrivate : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
PluginSpecPrivate(PluginSpec *spec);
|
||||
|
||||
void reset();
|
||||
bool read(const QString &fileName);
|
||||
bool read(const QStaticPlugin &plugin);
|
||||
bool provides(const QString &pluginName, const QString &version) const;
|
||||
bool resolveDependencies(const QVector<PluginSpec *> &specs);
|
||||
bool loadLibrary();
|
||||
bool initializePlugin();
|
||||
bool initializeExtensions();
|
||||
bool delayedInitialize();
|
||||
IPlugin::ShutdownFlag stop();
|
||||
void kill();
|
||||
|
||||
void setEnabledBySettings(bool value);
|
||||
void setEnabledByDefault(bool value);
|
||||
void setForceEnabled(bool value);
|
||||
void setForceDisabled(bool value);
|
||||
void setSoftLoadable(bool value);
|
||||
|
||||
std::optional<QPluginLoader> loader;
|
||||
std::optional<QStaticPlugin> staticPlugin;
|
||||
|
||||
QString name;
|
||||
QString version;
|
||||
QString compatVersion;
|
||||
bool required = false;
|
||||
bool experimental = false;
|
||||
bool enabledByDefault = true;
|
||||
bool deprecated = false;
|
||||
QString vendor;
|
||||
QString copyright;
|
||||
QString license;
|
||||
QString description;
|
||||
QString longDescription;
|
||||
QString url;
|
||||
QString category;
|
||||
QRegularExpression platformSpecification;
|
||||
QVector<PluginDependency> dependencies;
|
||||
QJsonObject metaData;
|
||||
bool enabledBySettings = true;
|
||||
bool enabledIndirectly = false;
|
||||
bool forceEnabled = false;
|
||||
bool forceDisabled = false;
|
||||
bool softLoadable = false;
|
||||
|
||||
QString location;
|
||||
QString filePath;
|
||||
QStringList arguments;
|
||||
|
||||
QHash<PluginDependency, PluginSpec *> dependencySpecs;
|
||||
PluginSpec::PluginArgumentDescriptions argumentDescriptions;
|
||||
IPlugin *plugin = nullptr;
|
||||
|
||||
QList<TestCreator> registeredPluginTests;
|
||||
|
||||
PluginSpec::State state = PluginSpec::Invalid;
|
||||
bool hasError = false;
|
||||
QString errorString;
|
||||
|
||||
PerformanceData performanceData;
|
||||
|
||||
static bool isValidVersion(const QString &version);
|
||||
static int versionCompare(const QString &version1, const QString &version2);
|
||||
|
||||
QVector<PluginSpec *> enableDependenciesIndirectly(bool enableTestDependencies = false);
|
||||
|
||||
bool readMetaData(const QJsonObject &pluginMetaData);
|
||||
|
||||
private:
|
||||
PluginSpec *q;
|
||||
|
||||
bool reportError(const QString &err);
|
||||
static const QRegularExpression &versionRegExp();
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace ExtensionSystem
|
@@ -5,7 +5,7 @@
|
||||
|
||||
#include "extensionsystemtr.h"
|
||||
#include "pluginmanager.h"
|
||||
#include "pluginspec_p.h"
|
||||
#include "pluginspec.h"
|
||||
|
||||
#include <utils/algorithm.h>
|
||||
#include <utils/categorysortfiltermodel.h>
|
||||
@@ -416,8 +416,8 @@ bool PluginView::setPluginsEnabled(const QSet<PluginSpec *> &plugins, bool enabl
|
||||
});
|
||||
QTC_ASSERT(item, continue);
|
||||
if (m_affectedPlugins.find(spec) == m_affectedPlugins.end())
|
||||
m_affectedPlugins[spec] = spec->d->enabledBySettings;
|
||||
spec->d->setEnabledBySettings(enable);
|
||||
m_affectedPlugins[spec] = spec->isEnabledBySettings();
|
||||
spec->setEnabledBySettings(enable);
|
||||
item->updateColumn(LoadedColumn);
|
||||
item->parent()->updateColumn(LoadedColumn);
|
||||
}
|
||||
@@ -428,7 +428,7 @@ bool PluginView::setPluginsEnabled(const QSet<PluginSpec *> &plugins, bool enabl
|
||||
void PluginView::cancelChanges()
|
||||
{
|
||||
for (auto element : m_affectedPlugins)
|
||||
element.first->d->setEnabledBySettings(element.second);
|
||||
element.first->setEnabledBySettings(element.second);
|
||||
}
|
||||
|
||||
} // namespace ExtensionSystem
|
||||
|
@@ -154,11 +154,11 @@ void checkContents(QPromise<ArchiveIssue> &promise, const FilePath &tempDir)
|
||||
if (promise.isCanceled())
|
||||
return;
|
||||
it.next();
|
||||
PluginSpec *spec = PluginSpec::read(it.filePath());
|
||||
expected_str<PluginSpec *> spec = PluginSpecImpl::read(it.filePath());
|
||||
if (spec) {
|
||||
// Is a Qt Creator plugin. Let's see if we find a Core dependency and check the
|
||||
// version
|
||||
const QVector<PluginDependency> dependencies = spec->dependencies();
|
||||
const QVector<PluginDependency> dependencies = (*spec)->dependencies();
|
||||
const auto found = std::find_if(dependencies.constBegin(), dependencies.constEnd(),
|
||||
[coreplugin](const PluginDependency &d) { return d.name == coreplugin->name(); });
|
||||
if (found == dependencies.constEnd())
|
||||
|
@@ -11,6 +11,7 @@
|
||||
"end of terms"
|
||||
],
|
||||
"Description" : [
|
||||
"This spec is broken because no name is set.",
|
||||
"This plugin is just a test.",
|
||||
" it demonstrates the great use of the plugin spec."
|
||||
],
|
||||
|
@@ -11,6 +11,7 @@
|
||||
"end of terms"
|
||||
],
|
||||
"Description" : [
|
||||
"This spec is wrong because no version is set.",
|
||||
"This plugin is just a test.",
|
||||
" it demonstrates the great use of the plugin spec."
|
||||
],
|
||||
|
@@ -12,6 +12,7 @@
|
||||
"end of terms"
|
||||
],
|
||||
"Description" : [
|
||||
"This spec is wrong because the first dependency has no name.",
|
||||
"This plugin is just a test.",
|
||||
" it demonstrates the great use of the plugin spec."
|
||||
],
|
||||
|
@@ -12,6 +12,7 @@
|
||||
"end of terms"
|
||||
],
|
||||
"Description" : [
|
||||
"This spec is wrong because the first dependencies version is invalid.",
|
||||
"This plugin is just a test.",
|
||||
" it demonstrates the great use of the plugin spec."
|
||||
],
|
||||
|
@@ -1,10 +1,9 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
#include <extensionsystem/pluginspec.h>
|
||||
#include <extensionsystem/pluginspec_p.h>
|
||||
#include <extensionsystem/pluginmanager_p.h>
|
||||
#include <extensionsystem/pluginmanager.h>
|
||||
#include <extensionsystem/pluginmanager_p.h>
|
||||
#include <extensionsystem/pluginspec.h>
|
||||
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
@@ -85,109 +84,110 @@ void tst_PluginSpec::cleanupTestCase()
|
||||
|
||||
void tst_PluginSpec::read()
|
||||
{
|
||||
Internal::PluginSpecPrivate spec(0);
|
||||
QCOMPARE(spec.state, PluginSpec::Invalid);
|
||||
PluginSpecImpl spec;
|
||||
QCOMPARE(spec.state(), PluginSpec::Invalid);
|
||||
QVERIFY(spec.readMetaData(metaData("testspecs/spec1.json")));
|
||||
QVERIFY(!spec.hasError);
|
||||
QVERIFY(spec.errorString.isEmpty());
|
||||
QCOMPARE(spec.name, QString("test"));
|
||||
QCOMPARE(spec.version, QString("1.0.1"));
|
||||
QCOMPARE(spec.compatVersion, QString("1.0.0"));
|
||||
QCOMPARE(spec.required, false);
|
||||
QCOMPARE(spec.experimental, false);
|
||||
QCOMPARE(spec.enabledBySettings, true);
|
||||
QCOMPARE(spec.vendor, QString("The Qt Company Ltd"));
|
||||
QCOMPARE(spec.copyright, QString("(C) 2015 The Qt Company Ltd"));
|
||||
QCOMPARE(spec.license, QString("This is a default license bla\nblubbblubb\nend of terms"));
|
||||
QCOMPARE(spec.description, QString("This plugin is just a test."));
|
||||
QVERIFY(!spec.hasError());
|
||||
QVERIFY(spec.errorString().isEmpty());
|
||||
QCOMPARE(spec.name(), QString("test"));
|
||||
QCOMPARE(spec.version(), QString("1.0.1"));
|
||||
QCOMPARE(spec.compatVersion(), QString("1.0.0"));
|
||||
QCOMPARE(spec.isRequired(), false);
|
||||
QCOMPARE(spec.isExperimental(), false);
|
||||
QCOMPARE(spec.isEnabledBySettings(), true);
|
||||
QCOMPARE(spec.vendor(), QString("The Qt Company Ltd"));
|
||||
QCOMPARE(spec.copyright(), QString("(C) 2015 The Qt Company Ltd"));
|
||||
QCOMPARE(spec.license(), QString("This is a default license bla\nblubbblubb\nend of terms"));
|
||||
QCOMPARE(spec.description(), QString("This plugin is just a test."));
|
||||
QCOMPARE(
|
||||
spec.longDescription,
|
||||
spec.longDescription(),
|
||||
QString(
|
||||
"This plugin is just a test.\n it demonstrates the great use of the plugin spec."));
|
||||
QCOMPARE(spec.url, QString("http://www.qt.io"));
|
||||
QCOMPARE(spec.url(), QString("http://www.qt.io"));
|
||||
PluginDependency dep1;
|
||||
dep1.name = QString("SomeOtherPlugin");
|
||||
dep1.version = QString("2.3.0_2");
|
||||
PluginDependency dep2;
|
||||
dep2.name = QString("EvenOther");
|
||||
dep2.version = QString("1.0.0");
|
||||
QCOMPARE(spec.dependencies, QVector<PluginDependency>() << dep1 << dep2);
|
||||
QCOMPARE(spec.dependencies(), QVector<PluginDependency>() << dep1 << dep2);
|
||||
|
||||
// test missing compatVersion behavior
|
||||
// and 'required' attribute
|
||||
QVERIFY(spec.readMetaData(metaData("testspecs/spec2.json")));
|
||||
QCOMPARE(spec.version, QString("3.1.4_10"));
|
||||
QCOMPARE(spec.compatVersion, QString("3.1.4_10"));
|
||||
QCOMPARE(spec.required, true);
|
||||
QCOMPARE(spec.version(), QString("3.1.4_10"));
|
||||
QCOMPARE(spec.compatVersion(), QString("3.1.4_10"));
|
||||
QCOMPARE(spec.isRequired(), true);
|
||||
}
|
||||
|
||||
void tst_PluginSpec::readError()
|
||||
{
|
||||
Internal::PluginSpecPrivate spec(0);
|
||||
QCOMPARE(spec.state, PluginSpec::Invalid);
|
||||
PluginSpecImpl spec;
|
||||
QCOMPARE(spec.state(), PluginSpec::Invalid);
|
||||
QVERIFY(!spec.readMetaData(metaData("non-existing-file.json")));
|
||||
QCOMPARE(spec.state, PluginSpec::Invalid);
|
||||
QVERIFY(!spec.hasError);
|
||||
QVERIFY(spec.errorString.isEmpty());
|
||||
QCOMPARE(spec.state(), PluginSpec::Invalid);
|
||||
QVERIFY(!spec.hasError());
|
||||
QVERIFY(spec.errorString().isEmpty());
|
||||
QVERIFY(spec.readMetaData(metaData("testspecs/spec_wrong2.json")));
|
||||
QCOMPARE(spec.state, PluginSpec::Invalid);
|
||||
QVERIFY(spec.hasError);
|
||||
QVERIFY(!spec.errorString.isEmpty());
|
||||
QCOMPARE(spec.state(), PluginSpec::Invalid);
|
||||
QVERIFY(spec.hasError());
|
||||
QVERIFY(!spec.errorString().isEmpty());
|
||||
QVERIFY(spec.readMetaData(metaData("testspecs/spec_wrong3.json")));
|
||||
QCOMPARE(spec.state, PluginSpec::Invalid);
|
||||
QVERIFY(spec.hasError);
|
||||
QVERIFY(!spec.errorString.isEmpty());
|
||||
QCOMPARE(spec.state(), PluginSpec::Invalid);
|
||||
QVERIFY(spec.hasError());
|
||||
QVERIFY(!spec.errorString().isEmpty());
|
||||
QVERIFY(spec.readMetaData(metaData("testspecs/spec_wrong4.json")));
|
||||
QCOMPARE(spec.state, PluginSpec::Invalid);
|
||||
QVERIFY(spec.hasError);
|
||||
QVERIFY(!spec.errorString.isEmpty());
|
||||
QCOMPARE(spec.state(), PluginSpec::Invalid);
|
||||
QVERIFY(spec.hasError());
|
||||
QVERIFY(!spec.errorString().isEmpty());
|
||||
QVERIFY(spec.readMetaData(metaData("testspecs/spec_wrong5.json")));
|
||||
QCOMPARE(spec.state, PluginSpec::Invalid);
|
||||
QVERIFY(spec.hasError);
|
||||
QVERIFY(!spec.errorString.isEmpty());
|
||||
QCOMPARE(spec.state(), PluginSpec::Invalid);
|
||||
QVERIFY(spec.hasError());
|
||||
QVERIFY(!spec.errorString().isEmpty());
|
||||
}
|
||||
|
||||
void tst_PluginSpec::isValidVersion()
|
||||
{
|
||||
QVERIFY(Internal::PluginSpecPrivate::isValidVersion("2"));
|
||||
QVERIFY(Internal::PluginSpecPrivate::isValidVersion("53"));
|
||||
QVERIFY(Internal::PluginSpecPrivate::isValidVersion("52_1"));
|
||||
QVERIFY(Internal::PluginSpecPrivate::isValidVersion("3.12"));
|
||||
QVERIFY(Internal::PluginSpecPrivate::isValidVersion("31.1_12"));
|
||||
QVERIFY(Internal::PluginSpecPrivate::isValidVersion("31.1.0"));
|
||||
QVERIFY(Internal::PluginSpecPrivate::isValidVersion("1.0.2_1"));
|
||||
QVERIFY(PluginSpec::isValidVersion("2"));
|
||||
QVERIFY(PluginSpec::isValidVersion("53"));
|
||||
QVERIFY(PluginSpec::isValidVersion("52_1"));
|
||||
QVERIFY(PluginSpec::isValidVersion("3.12"));
|
||||
QVERIFY(PluginSpec::isValidVersion("31.1_12"));
|
||||
QVERIFY(PluginSpec::isValidVersion("31.1.0"));
|
||||
QVERIFY(PluginSpec::isValidVersion("1.0.2_1"));
|
||||
|
||||
QVERIFY(!Internal::PluginSpecPrivate::isValidVersion(""));
|
||||
QVERIFY(!Internal::PluginSpecPrivate::isValidVersion("1..0"));
|
||||
QVERIFY(!Internal::PluginSpecPrivate::isValidVersion("1.0_"));
|
||||
QVERIFY(!Internal::PluginSpecPrivate::isValidVersion("1.0.0.0"));
|
||||
QVERIFY(!PluginSpec::isValidVersion(""));
|
||||
QVERIFY(!PluginSpec::isValidVersion("1..0"));
|
||||
QVERIFY(!PluginSpec::isValidVersion("1.0_"));
|
||||
QVERIFY(!PluginSpec::isValidVersion("1.0.0.0"));
|
||||
}
|
||||
|
||||
void tst_PluginSpec::versionCompare()
|
||||
{
|
||||
QVERIFY(Internal::PluginSpecPrivate::versionCompare("3", "3") == 0);
|
||||
QVERIFY(Internal::PluginSpecPrivate::versionCompare("3.0.0", "3") == 0);
|
||||
QVERIFY(Internal::PluginSpecPrivate::versionCompare("3.0", "3") == 0);
|
||||
QVERIFY(Internal::PluginSpecPrivate::versionCompare("3.0.0_1", "3_1") == 0);
|
||||
QVERIFY(Internal::PluginSpecPrivate::versionCompare("3.0_21", "3_21") == 0);
|
||||
QVERIFY(PluginSpec::versionCompare("3", "3") == 0);
|
||||
QVERIFY(PluginSpec::versionCompare("3.0.0", "3") == 0);
|
||||
QVERIFY(PluginSpec::versionCompare("3.0", "3") == 0);
|
||||
QVERIFY(PluginSpec::versionCompare("3.0.0_1", "3_1") == 0);
|
||||
QVERIFY(PluginSpec::versionCompare("3.0_21", "3_21") == 0);
|
||||
|
||||
QVERIFY(Internal::PluginSpecPrivate::versionCompare("3", "1") > 0);
|
||||
QVERIFY(Internal::PluginSpecPrivate::versionCompare("3", "1.0_12") > 0);
|
||||
QVERIFY(Internal::PluginSpecPrivate::versionCompare("3_1", "3") > 0);
|
||||
QVERIFY(Internal::PluginSpecPrivate::versionCompare("3.1.0_23", "3.1") > 0);
|
||||
QVERIFY(Internal::PluginSpecPrivate::versionCompare("3.1_23", "3.1_12") > 0);
|
||||
QVERIFY(PluginSpec::versionCompare("3", "1") > 0);
|
||||
QVERIFY(PluginSpec::versionCompare("3", "1.0_12") > 0);
|
||||
QVERIFY(PluginSpec::versionCompare("3_1", "3") > 0);
|
||||
QVERIFY(PluginSpec::versionCompare("3.1.0_23", "3.1") > 0);
|
||||
QVERIFY(PluginSpec::versionCompare("3.1_23", "3.1_12") > 0);
|
||||
|
||||
QVERIFY(Internal::PluginSpecPrivate::versionCompare("1", "3") < 0);
|
||||
QVERIFY(Internal::PluginSpecPrivate::versionCompare("1.0_12", "3") < 0);
|
||||
QVERIFY(Internal::PluginSpecPrivate::versionCompare("3", "3_1") < 0);
|
||||
QVERIFY(Internal::PluginSpecPrivate::versionCompare("3.1", "3.1.0_23") < 0);
|
||||
QVERIFY(Internal::PluginSpecPrivate::versionCompare("3.1_12", "3.1_23") < 0);
|
||||
QVERIFY(PluginSpec::versionCompare("1", "3") < 0);
|
||||
QVERIFY(PluginSpec::versionCompare("1.0_12", "3") < 0);
|
||||
QVERIFY(PluginSpec::versionCompare("3", "3_1") < 0);
|
||||
QVERIFY(PluginSpec::versionCompare("3.1", "3.1.0_23") < 0);
|
||||
QVERIFY(PluginSpec::versionCompare("3.1_12", "3.1_23") < 0);
|
||||
}
|
||||
|
||||
void tst_PluginSpec::provides()
|
||||
{
|
||||
Internal::PluginSpecPrivate spec(0);
|
||||
PluginSpecImpl spec;
|
||||
QVERIFY(spec.readMetaData(metaData("testspecs/simplespec.json")));
|
||||
|
||||
QVERIFY(!spec.provides("SomeOtherPlugin", "2.2.3_9"));
|
||||
QVERIFY(!spec.provides("MyPlugin", "2.2.3_10"));
|
||||
QVERIFY(!spec.provides("MyPlugin", "2.2.4"));
|
||||
@@ -211,112 +211,132 @@ void tst_PluginSpec::provides()
|
||||
|
||||
void tst_PluginSpec::experimental()
|
||||
{
|
||||
Internal::PluginSpecPrivate spec(0);
|
||||
PluginSpecImpl spec;
|
||||
QVERIFY(spec.readMetaData(metaData("testspecs/simplespec_experimental.json")));
|
||||
QCOMPARE(spec.experimental, true);
|
||||
QCOMPARE(spec.enabledBySettings, false);
|
||||
|
||||
QCOMPARE(spec.isExperimental(), true);
|
||||
QCOMPARE(spec.isEnabledBySettings(), false);
|
||||
}
|
||||
|
||||
void tst_PluginSpec::locationAndPath()
|
||||
{
|
||||
Internal::PluginSpecPrivate spec(0);
|
||||
QVERIFY(spec.read(QLatin1String(PLUGIN_DIR) + QLatin1String("/testplugin/") + libraryName(QLatin1String("test"))));
|
||||
QCOMPARE(spec.location, QString(QLatin1String(PLUGIN_DIR) + QLatin1String("/testplugin")));
|
||||
QCOMPARE(spec.filePath, QString(QLatin1String(PLUGIN_DIR) + QLatin1String("/testplugin/") + libraryName(QLatin1String("test"))));
|
||||
Utils::expected_str<PluginSpec *> ps = PluginSpecImpl::read(
|
||||
QLatin1String(PLUGIN_DIR) + QLatin1String("/testplugin/")
|
||||
+ libraryName(QLatin1String("test")));
|
||||
QVERIFY(ps);
|
||||
PluginSpecImpl *spec = static_cast<PluginSpecImpl *>(ps.value());
|
||||
QCOMPARE(spec->location(), QString(QLatin1String(PLUGIN_DIR) + QLatin1String("/testplugin")));
|
||||
QCOMPARE(spec->filePath(),
|
||||
QString(QLatin1String(PLUGIN_DIR) + QLatin1String("/testplugin/")
|
||||
+ libraryName(QLatin1String("test"))));
|
||||
}
|
||||
|
||||
void tst_PluginSpec::resolveDependencies()
|
||||
{
|
||||
QVector<PluginSpec *> specs;
|
||||
PluginSpec *spec1 = Internal::PluginManagerPrivate::createSpec();
|
||||
PluginSpec *spec1 = new PluginSpecImpl();
|
||||
specs.append(spec1);
|
||||
Internal::PluginSpecPrivate *spec1Priv = Internal::PluginManagerPrivate::privateSpec(spec1);
|
||||
spec1Priv->readMetaData(metaData("testdependencies/spec1.json"));
|
||||
spec1Priv->state = PluginSpec::Read; // fake read state for plugin resolving
|
||||
spec1->readMetaData(metaData("testdependencies/spec1.json"));
|
||||
spec1->setState(PluginSpec::Read); // fake read state for plugin resolving
|
||||
|
||||
PluginSpec *spec2 = Internal::PluginManagerPrivate::createSpec();
|
||||
PluginSpec *spec2 = new PluginSpecImpl();
|
||||
specs.append(spec2);
|
||||
Internal::PluginSpecPrivate *spec2Priv = Internal::PluginManagerPrivate::privateSpec(spec2);
|
||||
spec2Priv->readMetaData(metaData("testdependencies/spec2.json"));
|
||||
spec2Priv->state = PluginSpec::Read; // fake read state for plugin resolving
|
||||
spec2->readMetaData(metaData("testdependencies/spec2.json"));
|
||||
spec2->setState(PluginSpec::Read); // fake read state for plugin resolving
|
||||
|
||||
PluginSpec *spec3 = Internal::PluginManagerPrivate::createSpec();
|
||||
PluginSpec *spec3 = new PluginSpecImpl();
|
||||
specs.append(spec3);
|
||||
Internal::PluginSpecPrivate *spec3Priv = Internal::PluginManagerPrivate::privateSpec(spec3);
|
||||
spec3Priv->readMetaData(metaData("testdependencies/spec3.json"));
|
||||
spec3Priv->state = PluginSpec::Read; // fake read state for plugin resolving
|
||||
spec3->readMetaData(metaData("testdependencies/spec3.json"));
|
||||
spec3->setState(PluginSpec::Read); // fake read state for plugin resolving
|
||||
|
||||
PluginSpec *spec4 = Internal::PluginManagerPrivate::createSpec();
|
||||
PluginSpec *spec4 = new PluginSpecImpl();
|
||||
specs.append(spec4);
|
||||
Internal::PluginSpecPrivate *spec4Priv = Internal::PluginManagerPrivate::privateSpec(spec4);
|
||||
spec4Priv->readMetaData(metaData("testdependencies/spec4.json"));
|
||||
spec4Priv->state = PluginSpec::Read; // fake read state for plugin resolving
|
||||
spec4->readMetaData(metaData("testdependencies/spec4.json"));
|
||||
spec4->setState(PluginSpec::Read); // fake read state for plugin resolving
|
||||
|
||||
PluginSpec *spec5 = Internal::PluginManagerPrivate::createSpec();
|
||||
PluginSpec *spec5 = new PluginSpecImpl();
|
||||
specs.append(spec5);
|
||||
Internal::PluginSpecPrivate *spec5Priv = Internal::PluginManagerPrivate::privateSpec(spec5);
|
||||
spec5Priv->readMetaData(metaData("testdependencies/spec5.json"));
|
||||
spec5Priv->state = PluginSpec::Read; // fake read state for plugin resolving
|
||||
spec5->readMetaData(metaData("testdependencies/spec5.json"));
|
||||
spec5->setState(PluginSpec::Read); // fake read state for plugin resolving
|
||||
|
||||
QVERIFY(spec1Priv->resolveDependencies(specs));
|
||||
QCOMPARE(spec1Priv->dependencySpecs.size(), 2);
|
||||
QVERIFY(!spec1Priv->dependencySpecs.key(spec2).name.isEmpty());
|
||||
QVERIFY(!spec1Priv->dependencySpecs.key(spec3).name.isEmpty());
|
||||
QCOMPARE(spec1Priv->state, PluginSpec::Resolved);
|
||||
QVERIFY(!spec4Priv->resolveDependencies(specs));
|
||||
QVERIFY(spec4Priv->hasError);
|
||||
QCOMPARE(spec4Priv->state, PluginSpec::Read);
|
||||
QVERIFY(spec1->resolveDependencies(specs));
|
||||
QCOMPARE(spec1->dependencySpecs().size(), 2);
|
||||
QVERIFY(!spec1->dependencySpecs().key(spec2).name.isEmpty());
|
||||
QVERIFY(!spec1->dependencySpecs().key(spec3).name.isEmpty());
|
||||
QCOMPARE(spec1->state(), PluginSpec::Resolved);
|
||||
QVERIFY(!spec4->resolveDependencies(specs));
|
||||
QVERIFY(spec4->hasError());
|
||||
QCOMPARE(spec4->state(), PluginSpec::Read);
|
||||
}
|
||||
|
||||
void tst_PluginSpec::loadLibrary()
|
||||
{
|
||||
PluginSpec *ps = Internal::PluginManagerPrivate::createSpec();
|
||||
Internal::PluginSpecPrivate *spec = Internal::PluginManagerPrivate::privateSpec(ps);
|
||||
QVERIFY(spec->read(QLatin1String(PLUGIN_DIR) + QLatin1String("/testplugin/") + libraryName(QLatin1String("test"))));
|
||||
Utils::expected_str<PluginSpec *> ps = PluginSpecImpl::read(
|
||||
QLatin1String(PLUGIN_DIR) + QLatin1String("/testplugin/")
|
||||
+ libraryName(QLatin1String("test")));
|
||||
|
||||
QVERIFY(ps);
|
||||
PluginSpecImpl *spec = static_cast<PluginSpecImpl *>(ps.value());
|
||||
|
||||
QVERIFY(spec->resolveDependencies(QVector<PluginSpec *>()));
|
||||
QVERIFY2(spec->loadLibrary(), qPrintable(spec->errorString));
|
||||
QVERIFY(spec->plugin != 0);
|
||||
QVERIFY(QLatin1String(spec->plugin->metaObject()->className()) == QLatin1String("MyPlugin::MyPluginImpl"));
|
||||
QCOMPARE(spec->state, PluginSpec::Loaded);
|
||||
QVERIFY(!spec->hasError);
|
||||
QCOMPARE(spec->plugin, ps->plugin());
|
||||
delete ps;
|
||||
QVERIFY2(spec->loadLibrary(), qPrintable(spec->errorString()));
|
||||
QVERIFY(spec->plugin() != 0);
|
||||
QVERIFY(QLatin1String(spec->plugin()->metaObject()->className())
|
||||
== QLatin1String("MyPlugin::MyPluginImpl"));
|
||||
QCOMPARE(spec->state(), PluginSpec::Loaded);
|
||||
QVERIFY(!spec->hasError());
|
||||
|
||||
delete *ps;
|
||||
}
|
||||
|
||||
void tst_PluginSpec::initializePlugin()
|
||||
{
|
||||
Internal::PluginSpecPrivate spec(0);
|
||||
QVERIFY(spec.read(QLatin1String(PLUGIN_DIR) + QLatin1String("/testplugin/") + libraryName(QLatin1String("test"))));
|
||||
QVERIFY(spec.resolveDependencies(QVector<PluginSpec *>()));
|
||||
QVERIFY2(spec.loadLibrary(), qPrintable(spec.errorString));
|
||||
Utils::expected_str<PluginSpec *> ps = PluginSpecImpl::read(
|
||||
QLatin1String(PLUGIN_DIR) + QLatin1String("/testplugin/")
|
||||
+ libraryName(QLatin1String("test")));
|
||||
QVERIFY(ps);
|
||||
PluginSpecImpl *spec = static_cast<PluginSpecImpl *>(ps.value());
|
||||
QVERIFY(spec->resolveDependencies(QVector<PluginSpec *>()));
|
||||
QVERIFY2(spec->loadLibrary(), qPrintable(spec->errorString()));
|
||||
bool isInitialized;
|
||||
QMetaObject::invokeMethod(spec.plugin, "isInitialized",
|
||||
Qt::DirectConnection, Q_RETURN_ARG(bool, isInitialized));
|
||||
QMetaObject::invokeMethod(spec->plugin(),
|
||||
"isInitialized",
|
||||
Qt::DirectConnection,
|
||||
Q_RETURN_ARG(bool, isInitialized));
|
||||
QVERIFY(!isInitialized);
|
||||
QVERIFY(spec.initializePlugin());
|
||||
QCOMPARE(spec.state, PluginSpec::Initialized);
|
||||
QVERIFY(!spec.hasError);
|
||||
QMetaObject::invokeMethod(spec.plugin, "isInitialized",
|
||||
Qt::DirectConnection, Q_RETURN_ARG(bool, isInitialized));
|
||||
QVERIFY(spec->initializePlugin());
|
||||
QCOMPARE(spec->state(), PluginSpec::Initialized);
|
||||
QVERIFY(!spec->hasError());
|
||||
QMetaObject::invokeMethod(spec->plugin(),
|
||||
"isInitialized",
|
||||
Qt::DirectConnection,
|
||||
Q_RETURN_ARG(bool, isInitialized));
|
||||
QVERIFY(isInitialized);
|
||||
}
|
||||
|
||||
void tst_PluginSpec::initializeExtensions()
|
||||
{
|
||||
Internal::PluginSpecPrivate spec(0);
|
||||
QVERIFY(spec.read(QLatin1String(PLUGIN_DIR) + QLatin1String("/testplugin/") + libraryName(QLatin1String("test"))));
|
||||
QVERIFY(spec.resolveDependencies(QVector<PluginSpec *>()));
|
||||
QVERIFY2(spec.loadLibrary(), qPrintable(spec.errorString));
|
||||
Utils::expected_str<PluginSpec *> ps = PluginSpecImpl::read(
|
||||
QLatin1String(PLUGIN_DIR) + QLatin1String("/testplugin/")
|
||||
+ libraryName(QLatin1String("test")));
|
||||
QVERIFY(ps);
|
||||
PluginSpecImpl *spec = static_cast<PluginSpecImpl *>(ps.value());
|
||||
QVERIFY(spec->resolveDependencies(QVector<PluginSpec *>()));
|
||||
QVERIFY2(spec->loadLibrary(), qPrintable(spec->errorString()));
|
||||
bool isExtensionsInitialized;
|
||||
QVERIFY(spec.initializePlugin());
|
||||
QMetaObject::invokeMethod(spec.plugin, "isExtensionsInitialized",
|
||||
Qt::DirectConnection, Q_RETURN_ARG(bool, isExtensionsInitialized));
|
||||
QVERIFY(spec->initializePlugin());
|
||||
QMetaObject::invokeMethod(spec->plugin(),
|
||||
"isExtensionsInitialized",
|
||||
Qt::DirectConnection,
|
||||
Q_RETURN_ARG(bool, isExtensionsInitialized));
|
||||
QVERIFY(!isExtensionsInitialized);
|
||||
QVERIFY(spec.initializeExtensions());
|
||||
QCOMPARE(spec.state, PluginSpec::Running);
|
||||
QVERIFY(!spec.hasError);
|
||||
QMetaObject::invokeMethod(spec.plugin, "isExtensionsInitialized",
|
||||
Qt::DirectConnection, Q_RETURN_ARG(bool, isExtensionsInitialized));
|
||||
QVERIFY(spec->initializeExtensions());
|
||||
QCOMPARE(spec->state(), PluginSpec::Running);
|
||||
QVERIFY(!spec->hasError());
|
||||
QMetaObject::invokeMethod(spec->plugin(),
|
||||
"isExtensionsInitialized",
|
||||
Qt::DirectConnection,
|
||||
Q_RETURN_ARG(bool, isExtensionsInitialized));
|
||||
QVERIFY(isExtensionsInitialized);
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user