forked from qt-creator/qt-creator
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:
@@ -322,6 +322,13 @@ void PluginManager::loadPlugins()
|
|||||||
d->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.
|
Returns \c true if any plugin has errors even though it is enabled.
|
||||||
Most useful to call after loadPlugins().
|
Most useful to call after loadPlugins().
|
||||||
@@ -1616,17 +1623,19 @@ void PluginManagerPrivate::loadPlugin(PluginSpec *spec, PluginSpec::State destSt
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
// check if dependencies have loaded without error
|
// check if dependencies have loaded without error
|
||||||
const QHash<PluginDependency, PluginSpec *> deps = spec->dependencySpecs();
|
if (!spec->isSoftLoadable()) {
|
||||||
for (auto it = deps.cbegin(), end = deps.cend(); it != end; ++it) {
|
const QHash<PluginDependency, PluginSpec *> deps = spec->dependencySpecs();
|
||||||
if (it.key().type != PluginDependency::Required)
|
for (auto it = deps.cbegin(), end = deps.cend(); it != end; ++it) {
|
||||||
continue;
|
if (it.key().type != PluginDependency::Required)
|
||||||
PluginSpec *depSpec = it.value();
|
continue;
|
||||||
if (depSpec->state() != destState) {
|
PluginSpec *depSpec = it.value();
|
||||||
spec->d->hasError = true;
|
if (depSpec->state() != destState) {
|
||||||
spec->d->errorString =
|
spec->d->hasError = true;
|
||||||
Tr::tr("Cannot load plugin because dependency failed to load: %1(%2)\nReason: %3")
|
spec->d->errorString =
|
||||||
.arg(depSpec->name(), depSpec->version(), depSpec->errorString());
|
Tr::tr("Cannot load plugin because dependency failed to load: %1(%2)\nReason: %3")
|
||||||
return;
|
.arg(depSpec->name(), depSpec->version(), depSpec->errorString());
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
switch (destState) {
|
switch (destState) {
|
||||||
|
|||||||
@@ -68,6 +68,7 @@ public:
|
|||||||
// Plugin operations
|
// Plugin operations
|
||||||
static QVector<PluginSpec *> loadQueue();
|
static QVector<PluginSpec *> loadQueue();
|
||||||
static void loadPlugins();
|
static void loadPlugins();
|
||||||
|
static void loadPlugin(PluginSpec *);
|
||||||
static QStringList pluginPaths();
|
static QStringList pluginPaths();
|
||||||
static void setPluginPaths(const QStringList &paths);
|
static void setPluginPaths(const QStringList &paths);
|
||||||
static QString pluginIID();
|
static QString pluginIID();
|
||||||
|
|||||||
@@ -386,6 +386,11 @@ bool PluginSpec::isForceDisabled() const
|
|||||||
return d->forceDisabled;
|
return d->forceDisabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool PluginSpec::isSoftLoadable() const
|
||||||
|
{
|
||||||
|
return d->softLoadable;
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
The plugin dependencies. This is valid after the PluginSpec::Read state is reached.
|
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_REQUIRED[] = "Required";
|
||||||
const char PLUGIN_EXPERIMENTAL[] = "Experimental";
|
const char PLUGIN_EXPERIMENTAL[] = "Experimental";
|
||||||
const char PLUGIN_DISABLED_BY_DEFAULT[] = "DisabledByDefault";
|
const char PLUGIN_DISABLED_BY_DEFAULT[] = "DisabledByDefault";
|
||||||
|
const char PLUGIN_SOFTLOADABLE[] = "SoftLoadable";
|
||||||
const char VENDOR[] = "Vendor";
|
const char VENDOR[] = "Vendor";
|
||||||
const char COPYRIGHT[] = "Copyright";
|
const char COPYRIGHT[] = "Copyright";
|
||||||
const char LICENSE[] = "License";
|
const char LICENSE[] = "License";
|
||||||
@@ -683,6 +689,11 @@ void PluginSpecPrivate::setForceDisabled(bool value)
|
|||||||
forceDisabled = value;
|
forceDisabled = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PluginSpecPrivate::setSoftLoadable(bool value)
|
||||||
|
{
|
||||||
|
softLoadable = value;
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
\internal
|
\internal
|
||||||
*/
|
*/
|
||||||
@@ -796,6 +807,12 @@ bool PluginSpecPrivate::readMetaData(const QJsonObject &pluginMetaData)
|
|||||||
enabledByDefault = false;
|
enabledByDefault = false;
|
||||||
enabledBySettings = enabledByDefault;
|
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));
|
value = metaData.value(QLatin1String(VENDOR));
|
||||||
if (!value.isUndefined() && !value.isString())
|
if (!value.isUndefined() && !value.isString())
|
||||||
return reportError(msgValueIsNotAString(VENDOR));
|
return reportError(msgValueIsNotAString(VENDOR));
|
||||||
|
|||||||
@@ -100,6 +100,7 @@ public:
|
|||||||
bool isEnabledIndirectly() const;
|
bool isEnabledIndirectly() const;
|
||||||
bool isForceEnabled() const;
|
bool isForceEnabled() const;
|
||||||
bool isForceDisabled() const;
|
bool isForceDisabled() const;
|
||||||
|
bool isSoftLoadable() const;
|
||||||
QVector<PluginDependency> dependencies() const;
|
QVector<PluginDependency> dependencies() const;
|
||||||
QJsonObject metaData() const;
|
QJsonObject metaData() const;
|
||||||
const PerformanceData &performanceData() const;
|
const PerformanceData &performanceData() const;
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ public:
|
|||||||
void setEnabledByDefault(bool value);
|
void setEnabledByDefault(bool value);
|
||||||
void setForceEnabled(bool value);
|
void setForceEnabled(bool value);
|
||||||
void setForceDisabled(bool value);
|
void setForceDisabled(bool value);
|
||||||
|
void setSoftLoadable(bool value);
|
||||||
|
|
||||||
std::optional<QPluginLoader> loader;
|
std::optional<QPluginLoader> loader;
|
||||||
std::optional<QStaticPlugin> staticPlugin;
|
std::optional<QStaticPlugin> staticPlugin;
|
||||||
@@ -69,6 +70,7 @@ public:
|
|||||||
bool enabledIndirectly = false;
|
bool enabledIndirectly = false;
|
||||||
bool forceEnabled = false;
|
bool forceEnabled = false;
|
||||||
bool forceDisabled = false;
|
bool forceDisabled = false;
|
||||||
|
bool softLoadable = false;
|
||||||
|
|
||||||
QString location;
|
QString location;
|
||||||
QString filePath;
|
QString filePath;
|
||||||
|
|||||||
@@ -237,9 +237,7 @@ public:
|
|||||||
if (column == LoadedColumn && role == Qt::CheckStateRole) {
|
if (column == LoadedColumn && role == Qt::CheckStateRole) {
|
||||||
const QVector<PluginSpec *> affectedPlugins
|
const QVector<PluginSpec *> affectedPlugins
|
||||||
= Utils::filtered(m_plugins, [](PluginSpec *spec) { return !spec->isRequired(); });
|
= Utils::filtered(m_plugins, [](PluginSpec *spec) { return !spec->isRequired(); });
|
||||||
if (m_view->setPluginsEnabled(Utils::transform<QSet>(affectedPlugins,
|
if (m_view->setPluginsEnabled(toSet(affectedPlugins), data.toBool())) {
|
||||||
[](PluginSpec *s) { return s; }),
|
|
||||||
data.toBool())) {
|
|
||||||
update();
|
update();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -419,8 +417,8 @@ bool PluginView::setPluginsEnabled(const QSet<PluginSpec *> &plugins, bool enabl
|
|||||||
spec->d->setEnabledBySettings(enable);
|
spec->d->setEnabledBySettings(enable);
|
||||||
item->updateColumn(LoadedColumn);
|
item->updateColumn(LoadedColumn);
|
||||||
item->parent()->updateColumn(LoadedColumn);
|
item->parent()->updateColumn(LoadedColumn);
|
||||||
emit pluginSettingsChanged(spec);
|
|
||||||
}
|
}
|
||||||
|
emit pluginsChanged(affectedPlugins, enable);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
#include <utils/treemodel.h>
|
#include <utils/treemodel.h>
|
||||||
|
|
||||||
|
#include <QSet>
|
||||||
#include <QWidget>
|
#include <QWidget>
|
||||||
|
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
@@ -40,7 +41,7 @@ public:
|
|||||||
signals:
|
signals:
|
||||||
void currentPluginChanged(ExtensionSystem::PluginSpec *spec);
|
void currentPluginChanged(ExtensionSystem::PluginSpec *spec);
|
||||||
void pluginActivated(ExtensionSystem::PluginSpec *spec);
|
void pluginActivated(ExtensionSystem::PluginSpec *spec);
|
||||||
void pluginSettingsChanged(ExtensionSystem::PluginSpec *spec);
|
void pluginsChanged(const QSet<ExtensionSystem::PluginSpec *> &spec, bool enabled);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
PluginSpec *pluginForIndex(const QModelIndex &index) const;
|
PluginSpec *pluginForIndex(const QModelIndex &index) const;
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
#include <extensionsystem/pluginspec.h>
|
#include <extensionsystem/pluginspec.h>
|
||||||
#include <extensionsystem/pluginview.h>
|
#include <extensionsystem/pluginview.h>
|
||||||
|
|
||||||
|
#include <utils/algorithm.h>
|
||||||
#include <utils/fancylineedit.h>
|
#include <utils/fancylineedit.h>
|
||||||
#include <utils/layoutbuilder.h>
|
#include <utils/layoutbuilder.h>
|
||||||
|
|
||||||
@@ -21,6 +22,7 @@
|
|||||||
#include <QDialogButtonBox>
|
#include <QDialogButtonBox>
|
||||||
#include <QPushButton>
|
#include <QPushButton>
|
||||||
|
|
||||||
|
using namespace ExtensionSystem;
|
||||||
using namespace Utils;
|
using namespace Utils;
|
||||||
|
|
||||||
namespace Core {
|
namespace Core {
|
||||||
@@ -59,8 +61,16 @@ PluginDialog::PluginDialog(QWidget *parent)
|
|||||||
this, &PluginDialog::updateButtons);
|
this, &PluginDialog::updateButtons);
|
||||||
connect(m_view, &ExtensionSystem::PluginView::pluginActivated,
|
connect(m_view, &ExtensionSystem::PluginView::pluginActivated,
|
||||||
this, &PluginDialog::openDetails);
|
this, &PluginDialog::openDetails);
|
||||||
connect(m_view, &ExtensionSystem::PluginView::pluginSettingsChanged, this, [this] {
|
connect(m_view, &ExtensionSystem::PluginView::pluginsChanged,
|
||||||
m_isRestartRequired = true;
|
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,
|
connect(m_detailsButton, &QAbstractButton::clicked, this,
|
||||||
[this] { openDetails(m_view->currentPlugin()); });
|
[this] { openDetails(m_view->currentPlugin()); });
|
||||||
@@ -75,7 +85,11 @@ PluginDialog::PluginDialog(QWidget *parent)
|
|||||||
|
|
||||||
void PluginDialog::closeDialog()
|
void PluginDialog::closeDialog()
|
||||||
{
|
{
|
||||||
ExtensionSystem::PluginManager::writeSettings();
|
PluginManager::writeSettings();
|
||||||
|
|
||||||
|
for (PluginSpec *plugin : m_softLoad)
|
||||||
|
PluginManager::loadPlugin(plugin);
|
||||||
|
|
||||||
if (m_isRestartRequired) {
|
if (m_isRestartRequired) {
|
||||||
RestartDialog restartDialog(ICore::dialogParent(),
|
RestartDialog restartDialog(ICore::dialogParent(),
|
||||||
Tr::tr("Plugin changes will take effect after restart."));
|
Tr::tr("Plugin changes will take effect after restart."));
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ private:
|
|||||||
QPushButton *m_errorDetailsButton;
|
QPushButton *m_errorDetailsButton;
|
||||||
QPushButton *m_installButton;
|
QPushButton *m_installButton;
|
||||||
bool m_isRestartRequired = false;
|
bool m_isRestartRequired = false;
|
||||||
|
QSet<ExtensionSystem::PluginSpec *> m_softLoad;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Internal
|
} // namespace Internal
|
||||||
|
|||||||
Reference in New Issue
Block a user