From b39b1925189416585933c017e189d04d6e4c478d Mon Sep 17 00:00:00 2001 From: Marcus Tillmanns Date: Thu, 25 Jan 2024 12:21:48 +0100 Subject: [PATCH] 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 --- src/libs/extensionsystem/CMakeLists.txt | 2 +- src/libs/extensionsystem/extensionsystem.qbs | 1 - src/libs/extensionsystem/optionsparser.cpp | 17 +- src/libs/extensionsystem/pluginmanager.cpp | 176 +++--- src/libs/extensionsystem/pluginmanager.h | 2 + src/libs/extensionsystem/pluginmanager_p.h | 8 +- src/libs/extensionsystem/pluginspec.cpp | 569 +++++++++--------- src/libs/extensionsystem/pluginspec.h | 171 ++++-- src/libs/extensionsystem/pluginspec_p.h | 107 ---- src/libs/extensionsystem/pluginview.cpp | 8 +- .../coreplugin/plugininstallwizard.cpp | 4 +- .../pluginspec/testspecs/spec_wrong2.json | 1 + .../pluginspec/testspecs/spec_wrong3.json | 1 + .../pluginspec/testspecs/spec_wrong4.json | 1 + .../pluginspec/testspecs/spec_wrong5.json | 1 + .../pluginspec/tst_pluginspec.cpp | 290 ++++----- 16 files changed, 678 insertions(+), 681 deletions(-) delete mode 100644 src/libs/extensionsystem/pluginspec_p.h diff --git a/src/libs/extensionsystem/CMakeLists.txt b/src/libs/extensionsystem/CMakeLists.txt index fb836631d93..c8e37668060 100644 --- a/src/libs/extensionsystem/CMakeLists.txt +++ b/src/libs/extensionsystem/CMakeLists.txt @@ -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 diff --git a/src/libs/extensionsystem/extensionsystem.qbs b/src/libs/extensionsystem/extensionsystem.qbs index 8748c27ba69..72fe2f4c3cf 100644 --- a/src/libs/extensionsystem/extensionsystem.qbs +++ b/src/libs/extensionsystem/extensionsystem.qbs @@ -29,7 +29,6 @@ QtcLibrary { "pluginmanager_p.h", "pluginspec.cpp", "pluginspec.h", - "pluginspec_p.h", "pluginview.cpp", "pluginview.h", ] diff --git a/src/libs/extensionsystem/optionsparser.cpp b/src/libs/extensionsystem/optionsparser.cpp index 205442488e6..dd9d03944a2 100644 --- a/src/libs/extensionsystem/optionsparser.cpp +++ b/src/libs/extensionsystem/optionsparser.cpp @@ -6,7 +6,6 @@ #include "extensionsystemtr.h" #include "pluginmanager.h" #include "pluginmanager_p.h" -#include "pluginspec_p.h" #include @@ -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); } } diff --git a/src/libs/extensionsystem/pluginmanager.cpp b/src/libs/extensionsystem/pluginmanager.cpp index fcb3062faa9..43b8f3d2fa5 100644 --- a/src/libs/extensionsystem/pluginmanager.cpp +++ b/src/libs/extensionsystem/pluginmanager.cpp @@ -8,7 +8,6 @@ #include "optionsparser.h" #include "pluginmanager_p.h" #include "pluginspec.h" -#include "pluginspec_p.h" #include @@ -35,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -327,6 +327,11 @@ void PluginManager::loadPluginsAtRuntime(const QSet &plugins) d->loadPluginsAtRuntime(plugins); } +void PluginManager::addPlugins(const QVector &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 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("d->performanceData.delayedInitialize); + bool delay = spec->delayedInitialize(); + profilingReport("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 &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 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 localDelayedInitializeQueue; for (PluginSpec *spec : queue) loadPlugin(spec, PluginSpec::Loaded); @@ -1434,12 +1434,12 @@ void PluginManagerPrivate::loadPluginsAtRuntime(const QSet &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("d->performanceData.extensionsInitialized); + &spec->performanceData().extensionsInitialized); return; } case PluginSpec::Deleted: profilingReport(">delete", spec); - spec->d->kill(); + spec->kill(); profilingReport("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("d->performanceData.load); + spec->loadLibrary(); + profilingReport("performanceData().load); break; } case PluginSpec::Initialized: { NANOTRACE_SCOPE(specName, specName + "::initialize"); profilingReport(">initializePlugin", spec); - spec->d->initializePlugin(); - profilingReport("d->performanceData.initialize); + spec->initializePlugin(); + profilingReport("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 &specs) { - qDeleteAll(pluginSpecs); - pluginSpecs.clear(); - pluginCategories.clear(); + pluginSpecs += specs; - // default - pluginCategories.insert(QString(), QVector()); - - // 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 newSpecs; + + // from the file system + for (const QString &pluginFile : pluginFiles(pluginPaths)) { + expected_str 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 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 queue = Utils::filtered(pluginSpecs, &PluginSpec::isEffectivelyEnabled); while (!queue.isEmpty()) { PluginSpec *spec = queue.takeFirst(); - queue += spec->d->enableDependenciesIndirectly(containsTestSpec(spec)); + queue += spec->enableDependenciesIndirectly(containsTestSpec(spec)); } } diff --git a/src/libs/extensionsystem/pluginmanager.h b/src/libs/extensionsystem/pluginmanager.h index a38f3c3fe7b..74237db5b34 100644 --- a/src/libs/extensionsystem/pluginmanager.h +++ b/src/libs/extensionsystem/pluginmanager.h @@ -82,6 +82,8 @@ public: static void checkForProblematicPlugins(); static PluginSpec *specForPlugin(IPlugin *plugin); + static void addPlugins(const QVector &specs); + // Settings static void setSettings(Utils::QtcSettings *settings); static Utils::QtcSettings *settings(); diff --git a/src/libs/extensionsystem/pluginmanager_p.h b/src/libs/extensionsystem/pluginmanager_p.h index 27efbf4c8f3..2404a8b49e9 100644 --- a/src/libs/extensionsystem/pluginmanager_p.h +++ b/src/libs/extensionsystem/pluginmanager_p.h @@ -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 &plugins); + void addPlugins(const QVector &specs); + void shutdown(); void setPluginPaths(const QStringList &paths); const QVector 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 &testCreator); mutable QReadWriteLock m_lock; diff --git a/src/libs/extensionsystem/pluginspec.cpp b/src/libs/extensionsystem/pluginspec.cpp index 49f9c680fe8..8740854e7e5 100644 --- a/src/libs/extensionsystem/pluginspec.cpp +++ b/src/libs/extensionsystem/pluginspec.cpp @@ -6,7 +6,6 @@ #include "extensionsystemtr.h" #include "iplugin.h" #include "pluginmanager.h" -#include "pluginspec_p.h" #include #include @@ -25,6 +24,7 @@ #include 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 loader; + std::optional 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 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 errorString; + + PluginSpec::State state; + QHash dependencySpecs; + + QJsonObject metaData; + + Utils::expected_str readMetaData(const QJsonObject &metaData); + Utils::expected_str 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 &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 &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::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 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 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 r = spec->readMetaData(spec->d->loader->metaData()); + if (!r) + return make_unexpected(r.error()); + + return spec; } -bool PluginSpecPrivate::read(const QStaticPlugin &plugin) +expected_str 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 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 PluginSpec::readMetaData(const QJsonObject &metaData) +{ + return d->readMetaData(metaData); +} +Utils::expected_str PluginSpec::reportError(const QString &error) +{ + return d->reportError(error); } /*! \internal */ -bool PluginSpecPrivate::readMetaData(const QJsonObject &pluginMetaData) +expected_str 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 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 &specs) +bool PluginSpec::resolveDependencies(const QVector &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 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 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 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(loader->instance()) - : qobject_cast(staticPlugin->instance()); + auto *pluginObject = d->loader ? qobject_cast(d->loader->instance()) + : qobject_cast(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 diff --git a/src/libs/extensionsystem/pluginspec.h b/src/libs/extensionsystem/pluginspec.h index 8db6bb44114..09b8f55618f 100644 --- a/src/libs/extensionsystem/pluginspec.h +++ b/src/libs/extensionsystem/pluginspec.h @@ -5,6 +5,10 @@ #include "extensionsystem_global.h" +#include "iplugin.h" + +#include + #include #include #include @@ -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 dependencies() const; - QJsonObject metaData() const; - const PerformanceData &performanceData() const; + PluginSpec(); + virtual ~PluginSpec(); using PluginArgumentDescriptions = QVector; - 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 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 dependencySpecs() const; - // dependency specs, valid after 'Resolved' state is reached - QHash dependencySpecs() const; - bool requiresAny(const QSet &plugins) const; + virtual bool provides(const QString &pluginName, const QString &pluginVersion) const; + virtual bool requiresAny(const QSet &plugins) const; + virtual QVector enableDependenciesIndirectly(bool enableTestDependencies); + virtual bool resolveDependencies(const QVector &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 readMetaData(const QJsonObject &metaData); + Utils::expected_str reportError(const QString &error); private: - PluginSpec(); + std::unique_ptr 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 read(const QString &filePath); + static Utils::expected_str read(const QStaticPlugin &plugin); + + Utils::expected_str readMetaData(const QJsonObject &pluginMetaData) override; + +protected: + PluginSpecImpl(); + +private: + std::unique_ptr 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 diff --git a/src/libs/extensionsystem/pluginspec_p.h b/src/libs/extensionsystem/pluginspec_p.h deleted file mode 100644 index 61832213cad..00000000000 --- a/src/libs/extensionsystem/pluginspec_p.h +++ /dev/null @@ -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 -#include -#include -#include -#include -#include -#include - -#include - -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 &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 loader; - std::optional 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 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 dependencySpecs; - PluginSpec::PluginArgumentDescriptions argumentDescriptions; - IPlugin *plugin = nullptr; - - QList 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 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 diff --git a/src/libs/extensionsystem/pluginview.cpp b/src/libs/extensionsystem/pluginview.cpp index 40738be487f..07fa11c7a9a 100644 --- a/src/libs/extensionsystem/pluginview.cpp +++ b/src/libs/extensionsystem/pluginview.cpp @@ -5,7 +5,7 @@ #include "extensionsystemtr.h" #include "pluginmanager.h" -#include "pluginspec_p.h" +#include "pluginspec.h" #include #include @@ -416,8 +416,8 @@ bool PluginView::setPluginsEnabled(const QSet &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 &plugins, bool enabl void PluginView::cancelChanges() { for (auto element : m_affectedPlugins) - element.first->d->setEnabledBySettings(element.second); + element.first->setEnabledBySettings(element.second); } } // namespace ExtensionSystem diff --git a/src/plugins/coreplugin/plugininstallwizard.cpp b/src/plugins/coreplugin/plugininstallwizard.cpp index 5b342bf6c6a..55d77496744 100644 --- a/src/plugins/coreplugin/plugininstallwizard.cpp +++ b/src/plugins/coreplugin/plugininstallwizard.cpp @@ -154,11 +154,11 @@ void checkContents(QPromise &promise, const FilePath &tempDir) if (promise.isCanceled()) return; it.next(); - PluginSpec *spec = PluginSpec::read(it.filePath()); + expected_str 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 dependencies = spec->dependencies(); + const QVector 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()) diff --git a/tests/auto/extensionsystem/pluginspec/testspecs/spec_wrong2.json b/tests/auto/extensionsystem/pluginspec/testspecs/spec_wrong2.json index 2fd9df4d5d9..63a7fc5c551 100644 --- a/tests/auto/extensionsystem/pluginspec/testspecs/spec_wrong2.json +++ b/tests/auto/extensionsystem/pluginspec/testspecs/spec_wrong2.json @@ -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." ], diff --git a/tests/auto/extensionsystem/pluginspec/testspecs/spec_wrong3.json b/tests/auto/extensionsystem/pluginspec/testspecs/spec_wrong3.json index 0b053f60140..7c43ef41d26 100644 --- a/tests/auto/extensionsystem/pluginspec/testspecs/spec_wrong3.json +++ b/tests/auto/extensionsystem/pluginspec/testspecs/spec_wrong3.json @@ -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." ], diff --git a/tests/auto/extensionsystem/pluginspec/testspecs/spec_wrong4.json b/tests/auto/extensionsystem/pluginspec/testspecs/spec_wrong4.json index c3a0d26922e..c8e1dd53d9f 100644 --- a/tests/auto/extensionsystem/pluginspec/testspecs/spec_wrong4.json +++ b/tests/auto/extensionsystem/pluginspec/testspecs/spec_wrong4.json @@ -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." ], diff --git a/tests/auto/extensionsystem/pluginspec/testspecs/spec_wrong5.json b/tests/auto/extensionsystem/pluginspec/testspecs/spec_wrong5.json index bdb095b3420..680ec4002ac 100644 --- a/tests/auto/extensionsystem/pluginspec/testspecs/spec_wrong5.json +++ b/tests/auto/extensionsystem/pluginspec/testspecs/spec_wrong5.json @@ -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." ], diff --git a/tests/auto/extensionsystem/pluginspec/tst_pluginspec.cpp b/tests/auto/extensionsystem/pluginspec/tst_pluginspec.cpp index 762142b95ee..f919ef4139d 100644 --- a/tests/auto/extensionsystem/pluginspec/tst_pluginspec.cpp +++ b/tests/auto/extensionsystem/pluginspec/tst_pluginspec.cpp @@ -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 -#include -#include #include +#include +#include #include #include @@ -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() << dep1 << dep2); + QCOMPARE(spec.dependencies(), QVector() << 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 ps = PluginSpecImpl::read( + QLatin1String(PLUGIN_DIR) + QLatin1String("/testplugin/") + + libraryName(QLatin1String("test"))); + QVERIFY(ps); + PluginSpecImpl *spec = static_cast(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 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 ps = PluginSpecImpl::read( + QLatin1String(PLUGIN_DIR) + QLatin1String("/testplugin/") + + libraryName(QLatin1String("test"))); + + QVERIFY(ps); + PluginSpecImpl *spec = static_cast(ps.value()); + QVERIFY(spec->resolveDependencies(QVector())); - 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())); - QVERIFY2(spec.loadLibrary(), qPrintable(spec.errorString)); + Utils::expected_str ps = PluginSpecImpl::read( + QLatin1String(PLUGIN_DIR) + QLatin1String("/testplugin/") + + libraryName(QLatin1String("test"))); + QVERIFY(ps); + PluginSpecImpl *spec = static_cast(ps.value()); + QVERIFY(spec->resolveDependencies(QVector())); + 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())); - QVERIFY2(spec.loadLibrary(), qPrintable(spec.errorString)); + Utils::expected_str ps = PluginSpecImpl::read( + QLatin1String(PLUGIN_DIR) + QLatin1String("/testplugin/") + + libraryName(QLatin1String("test"))); + QVERIFY(ps); + PluginSpecImpl *spec = static_cast(ps.value()); + QVERIFY(spec->resolveDependencies(QVector())); + 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); }