ExtensionSystem: Allow opt-in plugin loading without restart

Change-Id: I682e51d047c43ff5bf4647ef7e015222707f3204
Reviewed-by: Marcus Tillmanns <marcus.tillmanns@qt.io>
Reviewed-by: Eike Ziller <eike.ziller@qt.io>
This commit is contained in:
hjk
2023-06-20 16:06:50 +02:00
committed by Marcus Tillmanns
parent 845337436c
commit 32914fe66b
9 changed files with 63 additions and 19 deletions

View File

@@ -322,6 +322,13 @@ void PluginManager::loadPlugins()
d->loadPlugins();
}
void PluginManager::loadPlugin(PluginSpec *spec)
{
d->loadPlugin(spec, PluginSpec::Loaded);
d->loadPlugin(spec, PluginSpec::Initialized);
d->loadPlugin(spec, PluginSpec::Running);
}
/*!
Returns \c true if any plugin has errors even though it is enabled.
Most useful to call after loadPlugins().
@@ -1616,6 +1623,7 @@ void PluginManagerPrivate::loadPlugin(PluginSpec *spec, PluginSpec::State destSt
break;
}
// check if dependencies have loaded without error
if (!spec->isSoftLoadable()) {
const QHash<PluginDependency, PluginSpec *> deps = spec->dependencySpecs();
for (auto it = deps.cbegin(), end = deps.cend(); it != end; ++it) {
if (it.key().type != PluginDependency::Required)
@@ -1629,6 +1637,7 @@ void PluginManagerPrivate::loadPlugin(PluginSpec *spec, PluginSpec::State destSt
return;
}
}
}
switch (destState) {
case PluginSpec::Loaded: {
NANOTRACE_SCOPE(specName, specName + "::load");

View File

@@ -68,6 +68,7 @@ public:
// Plugin operations
static QVector<PluginSpec *> loadQueue();
static void loadPlugins();
static void loadPlugin(PluginSpec *);
static QStringList pluginPaths();
static void setPluginPaths(const QStringList &paths);
static QString pluginIID();

View File

@@ -386,6 +386,11 @@ bool PluginSpec::isForceDisabled() const
return d->forceDisabled;
}
bool PluginSpec::isSoftLoadable() const
{
return d->softLoadable;
}
/*!
The plugin dependencies. This is valid after the PluginSpec::Read state is reached.
*/
@@ -570,6 +575,7 @@ namespace {
const char PLUGIN_REQUIRED[] = "Required";
const char PLUGIN_EXPERIMENTAL[] = "Experimental";
const char PLUGIN_DISABLED_BY_DEFAULT[] = "DisabledByDefault";
const char PLUGIN_SOFTLOADABLE[] = "SoftLoadable";
const char VENDOR[] = "Vendor";
const char COPYRIGHT[] = "Copyright";
const char LICENSE[] = "License";
@@ -683,6 +689,11 @@ void PluginSpecPrivate::setForceDisabled(bool value)
forceDisabled = value;
}
void PluginSpecPrivate::setSoftLoadable(bool value)
{
softLoadable = value;
}
/*!
\internal
*/
@@ -796,6 +807,12 @@ bool PluginSpecPrivate::readMetaData(const QJsonObject &pluginMetaData)
enabledByDefault = false;
enabledBySettings = enabledByDefault;
value = metaData.value(QLatin1String(PLUGIN_SOFTLOADABLE));
if (!value.isUndefined() && !value.isBool())
return reportError(msgValueIsNotABool(PLUGIN_SOFTLOADABLE));
softLoadable = value.toBool(false);
qCDebug(pluginLog) << "softLoadable =" << softLoadable;
value = metaData.value(QLatin1String(VENDOR));
if (!value.isUndefined() && !value.isString())
return reportError(msgValueIsNotAString(VENDOR));

View File

@@ -100,6 +100,7 @@ public:
bool isEnabledIndirectly() const;
bool isForceEnabled() const;
bool isForceDisabled() const;
bool isSoftLoadable() const;
QVector<PluginDependency> dependencies() const;
QJsonObject metaData() const;
const PerformanceData &performanceData() const;

View File

@@ -45,6 +45,7 @@ public:
void setEnabledByDefault(bool value);
void setForceEnabled(bool value);
void setForceDisabled(bool value);
void setSoftLoadable(bool value);
std::optional<QPluginLoader> loader;
std::optional<QStaticPlugin> staticPlugin;
@@ -69,6 +70,7 @@ public:
bool enabledIndirectly = false;
bool forceEnabled = false;
bool forceDisabled = false;
bool softLoadable = false;
QString location;
QString filePath;

View File

@@ -237,9 +237,7 @@ public:
if (column == LoadedColumn && role == Qt::CheckStateRole) {
const QVector<PluginSpec *> affectedPlugins
= Utils::filtered(m_plugins, [](PluginSpec *spec) { return !spec->isRequired(); });
if (m_view->setPluginsEnabled(Utils::transform<QSet>(affectedPlugins,
[](PluginSpec *s) { return s; }),
data.toBool())) {
if (m_view->setPluginsEnabled(toSet(affectedPlugins), data.toBool())) {
update();
return true;
}
@@ -419,8 +417,8 @@ bool PluginView::setPluginsEnabled(const QSet<PluginSpec *> &plugins, bool enabl
spec->d->setEnabledBySettings(enable);
item->updateColumn(LoadedColumn);
item->parent()->updateColumn(LoadedColumn);
emit pluginSettingsChanged(spec);
}
emit pluginsChanged(affectedPlugins, enable);
return true;
}

View File

@@ -7,6 +7,7 @@
#include <utils/treemodel.h>
#include <QSet>
#include <QWidget>
#include <unordered_map>
@@ -40,7 +41,7 @@ public:
signals:
void currentPluginChanged(ExtensionSystem::PluginSpec *spec);
void pluginActivated(ExtensionSystem::PluginSpec *spec);
void pluginSettingsChanged(ExtensionSystem::PluginSpec *spec);
void pluginsChanged(const QSet<ExtensionSystem::PluginSpec *> &spec, bool enabled);
private:
PluginSpec *pluginForIndex(const QModelIndex &index) const;

View File

@@ -14,6 +14,7 @@
#include <extensionsystem/pluginspec.h>
#include <extensionsystem/pluginview.h>
#include <utils/algorithm.h>
#include <utils/fancylineedit.h>
#include <utils/layoutbuilder.h>
@@ -21,6 +22,7 @@
#include <QDialogButtonBox>
#include <QPushButton>
using namespace ExtensionSystem;
using namespace Utils;
namespace Core {
@@ -59,8 +61,16 @@ PluginDialog::PluginDialog(QWidget *parent)
this, &PluginDialog::updateButtons);
connect(m_view, &ExtensionSystem::PluginView::pluginActivated,
this, &PluginDialog::openDetails);
connect(m_view, &ExtensionSystem::PluginView::pluginSettingsChanged, this, [this] {
connect(m_view, &ExtensionSystem::PluginView::pluginsChanged,
this, [this](const QSet<PluginSpec *> &plugins, bool enable) {
for (PluginSpec *plugin : plugins) {
if (enable && plugin->isSoftLoadable()) {
m_softLoad.insert(plugin);
} else {
m_softLoad.remove(plugin); // In case it was added, harmless otherwise.
m_isRestartRequired = true;
}
}
});
connect(m_detailsButton, &QAbstractButton::clicked, this,
[this] { openDetails(m_view->currentPlugin()); });
@@ -75,7 +85,11 @@ PluginDialog::PluginDialog(QWidget *parent)
void PluginDialog::closeDialog()
{
ExtensionSystem::PluginManager::writeSettings();
PluginManager::writeSettings();
for (PluginSpec *plugin : m_softLoad)
PluginManager::loadPlugin(plugin);
if (m_isRestartRequired) {
RestartDialog restartDialog(ICore::dialogParent(),
Tr::tr("Plugin changes will take effect after restart."));

View File

@@ -38,6 +38,7 @@ private:
QPushButton *m_errorDetailsButton;
QPushButton *m_installButton;
bool m_isRestartRequired = false;
QSet<ExtensionSystem::PluginSpec *> m_softLoad;
};
} // namespace Internal