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:
Marcus Tillmanns
2024-01-25 12:21:48 +01:00
parent 1af555ad09
commit b39b192518
16 changed files with 678 additions and 681 deletions

View File

@@ -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

View File

@@ -29,7 +29,6 @@ QtcLibrary {
"pluginmanager_p.h",
"pluginspec.cpp",
"pluginspec.h",
"pluginspec_p.h",
"pluginview.cpp",
"pluginview.h",
]

View File

@@ -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);
}
}

View File

@@ -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));
}
}

View File

@@ -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();

View File

@@ -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;

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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())

View File

@@ -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."
],

View File

@@ -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."
],

View File

@@ -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."
],

View File

@@ -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."
],

View File

@@ -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);
}