forked from qt-creator/qt-creator
ExtensionSystem: Allow updating and removing plugins
Change-Id: Ie81e697c97fbfe0dc66346f42d467e1a697606c3 Reviewed-by: Eike Ziller <eike.ziller@qt.io>
This commit is contained in:
@@ -839,11 +839,17 @@ int main(int argc, char **argv)
|
|||||||
// Make sure we honor the system's proxy settings
|
// Make sure we honor the system's proxy settings
|
||||||
QNetworkProxyFactory::setUseSystemConfiguration(true);
|
QNetworkProxyFactory::setUseSystemConfiguration(true);
|
||||||
|
|
||||||
|
// We need to install plugins before we scan for them.
|
||||||
|
PluginManager::installPluginsAfterRestart();
|
||||||
|
|
||||||
// Load
|
// Load
|
||||||
const QStringList pluginPaths = installPluginPaths + options.customPluginPaths;
|
const QStringList pluginPaths = installPluginPaths + options.customPluginPaths;
|
||||||
PluginManager::setPluginPaths(
|
PluginManager::setPluginPaths(
|
||||||
getPluginPaths() + Utils::transform(pluginPaths, &FilePath::fromUserInput));
|
getPluginPaths() + Utils::transform(pluginPaths, &FilePath::fromUserInput));
|
||||||
|
|
||||||
|
// We need to remove plugins once we have scanned for them.
|
||||||
|
PluginManager::removePluginsAfterRestart();
|
||||||
|
|
||||||
QMap<QString, QString> foundAppOptions;
|
QMap<QString, QString> foundAppOptions;
|
||||||
if (pluginArguments.size() > 1) {
|
if (pluginArguments.size() > 1) {
|
||||||
QMap<QString, bool> appOptions;
|
QMap<QString, bool> appOptions;
|
||||||
|
@@ -347,6 +347,27 @@ void PluginManager::addPlugins(const PluginSpecs &specs)
|
|||||||
d->addPlugins(specs);
|
d->addPlugins(specs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PluginManager::removePluginsAfterRestart()
|
||||||
|
{
|
||||||
|
d->removePluginsAfterRestart();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PluginManager::installPluginsAfterRestart()
|
||||||
|
{
|
||||||
|
d->installPluginsAfterRestart();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PluginManager::removePluginOnRestart(const QString &id)
|
||||||
|
{
|
||||||
|
d->removePluginOnNextRestart(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PluginManager::installPluginOnRestart(
|
||||||
|
const Utils::FilePath &source, const Utils::FilePath &destination)
|
||||||
|
{
|
||||||
|
d->installPluginOnNextRestart(source, destination);
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
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().
|
||||||
@@ -1689,6 +1710,11 @@ PluginSpec *PluginManager::specForPlugin(IPlugin *plugin)
|
|||||||
return findOrDefault(d->pluginSpecs, equal(&PluginSpec::plugin, plugin));
|
return findOrDefault(d->pluginSpecs, equal(&PluginSpec::plugin, plugin));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool PluginManager::takePluginIdForRemoval(const QString &id)
|
||||||
|
{
|
||||||
|
return d->m_pluginsToRemove.remove(id);
|
||||||
|
}
|
||||||
|
|
||||||
static QString pluginListString(const QSet<PluginSpec *> &plugins)
|
static QString pluginListString(const QSet<PluginSpec *> &plugins)
|
||||||
{
|
{
|
||||||
QStringList names = Utils::transform<QList>(plugins, &PluginSpec::name);
|
QStringList names = Utils::transform<QList>(plugins, &PluginSpec::name);
|
||||||
@@ -1899,6 +1925,36 @@ static const FilePaths pluginFiles(const FilePaths &pluginPaths)
|
|||||||
return pluginFiles;
|
return pluginFiles;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool PluginManagerPrivate::removePlugin(const QString &pluginId)
|
||||||
|
{
|
||||||
|
PluginSpec *existingSpec
|
||||||
|
= Utils::findOrDefault(pluginSpecs, Utils::equal(&PluginSpec::id, pluginId));
|
||||||
|
|
||||||
|
if (existingSpec) {
|
||||||
|
QTC_ASSERT(existingSpec->state() == PluginSpec::State::Resolved, return false);
|
||||||
|
|
||||||
|
const Result removeResult = existingSpec->removePluginFiles();
|
||||||
|
if (!removeResult) {
|
||||||
|
qCWarning(pluginLog) << "Failed to remove plugin files for" << pluginId << ":"
|
||||||
|
<< removeResult.error();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (QList<PluginSpec *> &category : pluginCategories) {
|
||||||
|
category.removeOne(existingSpec);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pluginSpecs.removeOne(existingSpec)) {
|
||||||
|
delete existingSpec;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is used by other plugin managers like Lua that is not loaded yet.
|
||||||
|
m_pluginsToRemove << pluginId;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void PluginManagerPrivate::addPlugins(const PluginSpecs &specs)
|
void PluginManagerPrivate::addPlugins(const PluginSpecs &specs)
|
||||||
{
|
{
|
||||||
pluginSpecs += specs;
|
pluginSpecs += specs;
|
||||||
@@ -1928,6 +1984,108 @@ void PluginManagerPrivate::addPlugins(const PluginSpecs &specs)
|
|||||||
emit q->pluginsChanged();
|
emit q->pluginsChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const char PLUGINS_TO_INSTALL_KEY[] = "PluginsToInstall";
|
||||||
|
static const char PLUGINS_TO_REMOVE_KEY[] = "PluginsToRemove";
|
||||||
|
|
||||||
|
void PluginManagerPrivate::removePluginOnNextRestart(const QString &pluginId)
|
||||||
|
{
|
||||||
|
settings->setValue(
|
||||||
|
PLUGINS_TO_REMOVE_KEY, settings->value(PLUGINS_TO_REMOVE_KEY).toStringList() << pluginId);
|
||||||
|
settings->sync();
|
||||||
|
}
|
||||||
|
|
||||||
|
static QList<QPair<FilePath, FilePath>> readPluginInstallList(QtcSettings *settings)
|
||||||
|
{
|
||||||
|
int size = settings->beginReadArray(PLUGINS_TO_INSTALL_KEY);
|
||||||
|
|
||||||
|
QList<QPair<FilePath, FilePath>> installList;
|
||||||
|
for (int i = 0; i < size; ++i) {
|
||||||
|
settings->setArrayIndex(i);
|
||||||
|
installList.append(
|
||||||
|
{FilePath::fromVariant(settings->value("src")),
|
||||||
|
FilePath::fromVariant(settings->value("dest"))});
|
||||||
|
}
|
||||||
|
settings->endArray();
|
||||||
|
return installList;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PluginManagerPrivate::installPluginOnNextRestart(
|
||||||
|
const Utils::FilePath &src, const Utils::FilePath &dest)
|
||||||
|
{
|
||||||
|
const QList<QPair<FilePath, FilePath>> list = readPluginInstallList(settings)
|
||||||
|
<< qMakePair(src, dest);
|
||||||
|
|
||||||
|
settings->beginWriteArray(PLUGINS_TO_INSTALL_KEY);
|
||||||
|
for (int i = 0; i < list.size(); ++i) {
|
||||||
|
settings->setArrayIndex(i);
|
||||||
|
settings->setValue("src", list.at(i).first.toVariant());
|
||||||
|
settings->setValue("dest", list.at(i).second.toVariant());
|
||||||
|
}
|
||||||
|
settings->endArray();
|
||||||
|
|
||||||
|
settings->sync();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PluginManagerPrivate::removePluginsAfterRestart()
|
||||||
|
{
|
||||||
|
const QStringList removeList = settings->value(PLUGINS_TO_REMOVE_KEY).toStringList();
|
||||||
|
for (const QString &pluginId : removeList)
|
||||||
|
removePlugin(pluginId);
|
||||||
|
|
||||||
|
settings->remove(PLUGINS_TO_REMOVE_KEY);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PluginManagerPrivate::installPluginsAfterRestart()
|
||||||
|
{
|
||||||
|
QTC_CHECK(pluginSpecs.isEmpty());
|
||||||
|
|
||||||
|
QList<QPair<FilePath, FilePath>> installList = readPluginInstallList(settings);
|
||||||
|
const Utils::FilePaths pluginPaths = PluginManager::pluginPaths();
|
||||||
|
|
||||||
|
for (const auto &[src, dest] : installList) {
|
||||||
|
if (!src.exists()) {
|
||||||
|
qCWarning(pluginLog()) << "Cannot install source " << src << ", it does not exist";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dest.isDir()) {
|
||||||
|
if (auto result = dest.removeRecursively(); !result) {
|
||||||
|
qCWarning(pluginLog()) << "Failed to remove" << dest << ":" << result.error();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
} else if (dest.isFile()) {
|
||||||
|
if (const Result result = dest.removeFile(); !result) {
|
||||||
|
qCWarning(pluginLog()) << "Failed to remove" << dest << ":" << result.error();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (src.isFile()) {
|
||||||
|
if (!dest.createDir()) {
|
||||||
|
qCWarning(pluginLog()) << "Cannot install file" << src << "to" << dest
|
||||||
|
<< "because the destination directory cannot be created";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Utils::Result result = src.isDir() ? src.copyRecursively(dest)
|
||||||
|
: src.copyFile(dest / src.fileName());
|
||||||
|
|
||||||
|
if (!result) {
|
||||||
|
qCWarning(pluginLog())
|
||||||
|
<< "Failed to install" << src << "to" << dest << ":" << result.error();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = src.isDir() ? src.removeRecursively() : src.removeFile();
|
||||||
|
if (!result)
|
||||||
|
qCWarning(pluginLog())
|
||||||
|
<< "Failed to remove the source file in" << src << ":" << result.error();
|
||||||
|
}
|
||||||
|
|
||||||
|
settings->remove(PLUGINS_TO_INSTALL_KEY);
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
\internal
|
\internal
|
||||||
*/
|
*/
|
||||||
@@ -1954,6 +2112,13 @@ void PluginManagerPrivate::readPluginPaths()
|
|||||||
newSpecs.append(spec->release());
|
newSpecs.append(spec->release());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
newSpecs = Utils::filtered(newSpecs, [this](PluginSpec *spec) {
|
||||||
|
return pluginById(spec->id()) == nullptr;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (newSpecs.empty())
|
||||||
|
return;
|
||||||
|
|
||||||
addPlugins(newSpecs);
|
addPlugins(newSpecs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -80,9 +80,19 @@ public:
|
|||||||
static const QSet<PluginSpec *> pluginsRequiredByPlugin(PluginSpec *spec);
|
static const QSet<PluginSpec *> pluginsRequiredByPlugin(PluginSpec *spec);
|
||||||
static void checkForProblematicPlugins();
|
static void checkForProblematicPlugins();
|
||||||
static PluginSpec *specForPlugin(IPlugin *plugin);
|
static PluginSpec *specForPlugin(IPlugin *plugin);
|
||||||
|
static bool takePluginIdForRemoval(const QString &id);
|
||||||
|
|
||||||
static void addPlugins(const QVector<PluginSpec *> &specs);
|
static void addPlugins(const QVector<PluginSpec *> &specs);
|
||||||
|
|
||||||
|
static void reInstallPlugins();
|
||||||
|
|
||||||
|
static void removePluginOnRestart(const QString &id);
|
||||||
|
static void installPluginOnRestart(
|
||||||
|
const Utils::FilePath &source, const Utils::FilePath &destination);
|
||||||
|
|
||||||
|
static void removePluginsAfterRestart();
|
||||||
|
static void installPluginsAfterRestart();
|
||||||
|
|
||||||
// UI
|
// UI
|
||||||
static std::optional<QSet<PluginSpec *>> askForEnablingPlugins(
|
static std::optional<QSet<PluginSpec *>> askForEnablingPlugins(
|
||||||
QWidget *dialogParent, const QSet<PluginSpec *> &plugins, bool enable);
|
QWidget *dialogParent, const QSet<PluginSpec *> &plugins, bool enable);
|
||||||
|
@@ -52,6 +52,8 @@ public:
|
|||||||
void loadPluginsAtRuntime(const QSet<PluginSpec *> &plugins);
|
void loadPluginsAtRuntime(const QSet<PluginSpec *> &plugins);
|
||||||
void addPlugins(const QVector<PluginSpec *> &specs);
|
void addPlugins(const QVector<PluginSpec *> &specs);
|
||||||
|
|
||||||
|
bool removePlugin(const QString &pluginId);
|
||||||
|
|
||||||
void shutdown();
|
void shutdown();
|
||||||
void setPluginPaths(const Utils::FilePaths &paths);
|
void setPluginPaths(const Utils::FilePaths &paths);
|
||||||
const QVector<ExtensionSystem::PluginSpec *> loadQueue();
|
const QVector<ExtensionSystem::PluginSpec *> loadQueue();
|
||||||
@@ -70,6 +72,13 @@ public:
|
|||||||
|
|
||||||
bool acceptTermsAndConditions(PluginSpec *spec);
|
bool acceptTermsAndConditions(PluginSpec *spec);
|
||||||
void setAcceptTermsAndConditionsCallback(const std::function<bool(PluginSpec *)> &callback);
|
void setAcceptTermsAndConditionsCallback(const std::function<bool(PluginSpec *)> &callback);
|
||||||
|
void readPluginPaths();
|
||||||
|
|
||||||
|
void removePluginsAfterRestart();
|
||||||
|
void installPluginsAfterRestart();
|
||||||
|
|
||||||
|
void removePluginOnNextRestart(const QString &pluginId);
|
||||||
|
void installPluginOnNextRestart(const Utils::FilePath &src, const Utils::FilePath &dest);
|
||||||
|
|
||||||
class TestSpec {
|
class TestSpec {
|
||||||
public:
|
public:
|
||||||
@@ -143,12 +152,13 @@ public:
|
|||||||
|
|
||||||
PluginManager::ProcessData m_creatorProcessData;
|
PluginManager::ProcessData m_creatorProcessData;
|
||||||
|
|
||||||
|
QSet<QString> m_pluginsToRemove;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
PluginManager *q;
|
PluginManager *q;
|
||||||
|
|
||||||
void startDelayedInitialize();
|
void startDelayedInitialize();
|
||||||
|
|
||||||
void readPluginPaths();
|
|
||||||
bool loadQueue(PluginSpec *spec,
|
bool loadQueue(PluginSpec *spec,
|
||||||
QVector<ExtensionSystem::PluginSpec *> &queue,
|
QVector<ExtensionSystem::PluginSpec *> &queue,
|
||||||
QVector<ExtensionSystem::PluginSpec *> &circularityCheckQueue);
|
QVector<ExtensionSystem::PluginSpec *> &circularityCheckQueue);
|
||||||
|
@@ -1453,4 +1453,33 @@ QList<PluginSpec *> pluginSpecsFromArchive(const Utils::FilePath &path)
|
|||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Utils::Result PluginSpec::removePluginFiles() const
|
||||||
|
{
|
||||||
|
if (isSystemPlugin())
|
||||||
|
return Result::Error(Tr::tr("Cannot remove system plugins."));
|
||||||
|
|
||||||
|
// Try to figure out where we are ...
|
||||||
|
const Utils::FilePaths pluginPaths = PluginManager::pluginPaths();
|
||||||
|
|
||||||
|
for (const FilePath &pluginPath : pluginPaths) {
|
||||||
|
if (location().isChildOf(pluginPath)) {
|
||||||
|
const FilePath rootFolder = location().relativeChildPath(pluginPath);
|
||||||
|
if (rootFolder.isEmpty())
|
||||||
|
return Result::Error(Tr::tr("Could not determine root folder."));
|
||||||
|
|
||||||
|
const FilePath pathToDelete = pluginPath
|
||||||
|
/ rootFolder.pathComponents().first().toString();
|
||||||
|
return pathToDelete.removeRecursively();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return filePath().removeFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PluginSpec::isSystemPlugin() const
|
||||||
|
{
|
||||||
|
return !filePath().isChildOf(appInfo().userPluginsRoot)
|
||||||
|
&& !filePath().isChildOf(appInfo().userLuaPlugins);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace ExtensionSystem
|
} // namespace ExtensionSystem
|
||||||
|
@@ -161,6 +161,9 @@ public:
|
|||||||
|
|
||||||
virtual Utils::FilePath installLocation(bool inUserFolder) const = 0;
|
virtual Utils::FilePath installLocation(bool inUserFolder) const = 0;
|
||||||
|
|
||||||
|
virtual Utils::Result removePluginFiles() const;
|
||||||
|
virtual bool isSystemPlugin() const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void setEnabledByDefault(bool value);
|
virtual void setEnabledByDefault(bool value);
|
||||||
virtual void setEnabledIndirectly(bool value);
|
virtual void setEnabledIndirectly(bool value);
|
||||||
|
@@ -51,6 +51,7 @@ struct Data
|
|||||||
bool installIntoApplication = false;
|
bool installIntoApplication = false;
|
||||||
std::unique_ptr<PluginSpec> pluginSpec = nullptr;
|
std::unique_ptr<PluginSpec> pluginSpec = nullptr;
|
||||||
bool loadImmediately = false;
|
bool loadImmediately = false;
|
||||||
|
bool prepareForUpdate = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
static bool hasLibSuffix(const FilePath &path)
|
static bool hasLibSuffix(const FilePath &path)
|
||||||
@@ -127,13 +128,20 @@ public:
|
|||||||
|
|
||||||
using CheckResult = expected_str<PluginSpec *>;
|
using CheckResult = expected_str<PluginSpec *>;
|
||||||
|
|
||||||
static Result checkPlugin(PluginSpec *spec)
|
static Result checkPlugin(PluginSpec *spec, bool update)
|
||||||
{
|
{
|
||||||
if (Utils::anyOf(PluginManager::plugins(), [spec](PluginSpec *other) {
|
const bool pluginAlreadyExists
|
||||||
|
= Utils::anyOf(PluginManager::plugins(), [spec](PluginSpec *other) {
|
||||||
return other->id() == spec->id();
|
return other->id() == spec->id();
|
||||||
}))
|
});
|
||||||
|
|
||||||
|
if (!update && pluginAlreadyExists) {
|
||||||
return Result::Error(
|
return Result::Error(
|
||||||
Tr::tr("A plugin with ID \"%1\" is already installed.").arg(spec->id()));
|
Tr::tr("A plugin with ID \"%1\" is already installed.").arg(spec->id()));
|
||||||
|
} else if (update && !pluginAlreadyExists) {
|
||||||
|
return Result::Error(Tr::tr("No plugin with ID \"%1\" is installed.").arg(spec->id()));
|
||||||
|
}
|
||||||
|
|
||||||
if (!spec->resolveDependencies(PluginManager::plugins())) {
|
if (!spec->resolveDependencies(PluginManager::plugins())) {
|
||||||
return Result::Error(
|
return Result::Error(
|
||||||
Tr::tr("Plugin failed to resolve dependencies:") + " " + spec->errorString());
|
Tr::tr("Plugin failed to resolve dependencies:") + " " + spec->errorString());
|
||||||
@@ -142,18 +150,18 @@ static Result checkPlugin(PluginSpec *spec)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static expected_str<std::unique_ptr<PluginSpec>> checkPlugin(
|
static expected_str<std::unique_ptr<PluginSpec>> checkPlugin(
|
||||||
expected_str<std::unique_ptr<PluginSpec>> spec)
|
expected_str<std::unique_ptr<PluginSpec>> spec, bool update)
|
||||||
{
|
{
|
||||||
if (!spec)
|
if (!spec)
|
||||||
return spec;
|
return spec;
|
||||||
const Result ok = checkPlugin(spec->get());
|
const Result ok = checkPlugin(spec->get(), update);
|
||||||
if (ok)
|
if (ok)
|
||||||
return spec;
|
return spec;
|
||||||
return Utils::make_unexpected(ok.error());
|
return Utils::make_unexpected(ok.error());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Async. Result is set if any issue was found.
|
// Async. Result is set if any issue was found.
|
||||||
void checkContents(QPromise<CheckResult> &promise, const FilePath &tempDir)
|
void checkContents(QPromise<CheckResult> &promise, const FilePath &tempDir, bool update)
|
||||||
{
|
{
|
||||||
QList<PluginSpec *> plugins = pluginSpecsFromArchive(tempDir);
|
QList<PluginSpec *> plugins = pluginSpecsFromArchive(tempDir);
|
||||||
if (plugins.isEmpty()) {
|
if (plugins.isEmpty()) {
|
||||||
@@ -167,7 +175,7 @@ void checkContents(QPromise<CheckResult> &promise, const FilePath &tempDir)
|
|||||||
}
|
}
|
||||||
|
|
||||||
PluginSpec *plugin = plugins.front();
|
PluginSpec *plugin = plugins.front();
|
||||||
const Result ok = checkPlugin(plugin);
|
const Result ok = checkPlugin(plugin, update);
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
promise.addResult(Utils::make_unexpected(ok.error()));
|
promise.addResult(Utils::make_unexpected(ok.error()));
|
||||||
delete plugin;
|
delete plugin;
|
||||||
@@ -214,8 +222,8 @@ public:
|
|||||||
emit completeChanged();
|
emit completeChanged();
|
||||||
if (hasLibSuffix(m_data->sourcePath)) {
|
if (hasLibSuffix(m_data->sourcePath)) {
|
||||||
m_cancelButton->setVisible(false);
|
m_cancelButton->setVisible(false);
|
||||||
expected_str<std::unique_ptr<PluginSpec>> spec = checkPlugin(
|
expected_str<std::unique_ptr<PluginSpec>> spec
|
||||||
readCppPluginSpec(m_data->sourcePath));
|
= checkPlugin(readCppPluginSpec(m_data->sourcePath), m_data->prepareForUpdate);
|
||||||
if (!spec) {
|
if (!spec) {
|
||||||
m_label->setType(InfoLabel::Error);
|
m_label->setType(InfoLabel::Error);
|
||||||
m_label->setText(spec.error());
|
m_label->setText(spec.error());
|
||||||
@@ -228,8 +236,18 @@ public:
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_tempDir = std::make_unique<TemporaryDirectory>("plugininstall");
|
FilePath tmpDirBase;
|
||||||
m_data->extractedPath = m_tempDir->path();
|
if (m_data->prepareForUpdate)
|
||||||
|
tmpDirBase = appInfo().userResources / "install-prep-area";
|
||||||
|
else
|
||||||
|
tmpDirBase = TemporaryDirectory::masterDirectoryFilePath();
|
||||||
|
|
||||||
|
tmpDirBase.ensureWritableDir();
|
||||||
|
|
||||||
|
m_tempDir = std::make_unique<QTemporaryDir>((tmpDirBase / "plugininstall").path());
|
||||||
|
m_tempDir->setAutoRemove(false);
|
||||||
|
|
||||||
|
m_data->extractedPath = FilePath::fromString(m_tempDir->path());
|
||||||
m_label->setText(Tr::tr("Checking archive..."));
|
m_label->setText(Tr::tr("Checking archive..."));
|
||||||
m_label->setType(InfoLabel::None);
|
m_label->setType(InfoLabel::None);
|
||||||
|
|
||||||
@@ -244,7 +262,7 @@ public:
|
|||||||
|
|
||||||
const auto onUnarchiverSetup = [this, sourceAndCommand](Unarchiver &unarchiver) {
|
const auto onUnarchiverSetup = [this, sourceAndCommand](Unarchiver &unarchiver) {
|
||||||
unarchiver.setSourceAndCommand(*sourceAndCommand);
|
unarchiver.setSourceAndCommand(*sourceAndCommand);
|
||||||
unarchiver.setDestDir(m_tempDir->path());
|
unarchiver.setDestDir(m_data->extractedPath);
|
||||||
connect(&unarchiver, &Unarchiver::outputReceived, this, [this](const QString &output) {
|
connect(&unarchiver, &Unarchiver::outputReceived, this, [this](const QString &output) {
|
||||||
m_output->append(output);
|
m_output->append(output);
|
||||||
});
|
});
|
||||||
@@ -255,10 +273,11 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
const auto onCheckerSetup = [this](Async<CheckResult> &async) {
|
const auto onCheckerSetup = [this](Async<CheckResult> &async) {
|
||||||
if (!m_tempDir)
|
if (!m_data->extractedPath.exists())
|
||||||
return SetupResult::StopWithError;
|
return SetupResult::StopWithError;
|
||||||
|
|
||||||
async.setConcurrentCallData(checkContents, m_tempDir->path());
|
async.setConcurrentCallData(
|
||||||
|
checkContents, m_data->extractedPath, m_data->prepareForUpdate);
|
||||||
return SetupResult::Continue;
|
return SetupResult::Continue;
|
||||||
};
|
};
|
||||||
const auto onCheckerDone = [this](const Async<CheckResult> &async) {
|
const auto onCheckerDone = [this](const Async<CheckResult> &async) {
|
||||||
@@ -289,8 +308,11 @@ public:
|
|||||||
{
|
{
|
||||||
// back button pressed
|
// back button pressed
|
||||||
m_taskTreeRunner.reset();
|
m_taskTreeRunner.reset();
|
||||||
|
if (m_tempDir) {
|
||||||
|
m_tempDir->remove();
|
||||||
m_tempDir.reset();
|
m_tempDir.reset();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool isComplete() const final { return m_isComplete; }
|
bool isComplete() const final { return m_isComplete; }
|
||||||
|
|
||||||
@@ -301,7 +323,7 @@ public:
|
|||||||
return WizardPage::nextId();
|
return WizardPage::nextId();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<TemporaryDirectory> m_tempDir;
|
std::unique_ptr<QTemporaryDir> m_tempDir;
|
||||||
TaskTreeRunner m_taskTreeRunner;
|
TaskTreeRunner m_taskTreeRunner;
|
||||||
InfoLabel *m_label = nullptr;
|
InfoLabel *m_label = nullptr;
|
||||||
QPushButton *m_cancelButton = nullptr;
|
QPushButton *m_cancelButton = nullptr;
|
||||||
@@ -489,7 +511,13 @@ static bool copyPluginFile(const FilePath &src, const FilePath &dest)
|
|||||||
return false;
|
return false;
|
||||||
destFile.removeFile();
|
destFile.removeFile();
|
||||||
}
|
}
|
||||||
dest.parentDir().ensureWritableDir();
|
if (!destFile.parentDir().ensureWritableDir()) {
|
||||||
|
QMessageBox::warning(
|
||||||
|
ICore::dialogParent(),
|
||||||
|
Tr::tr("Failed to Write File"),
|
||||||
|
Tr::tr("Failed to create directory \"%1\".").arg(dest.toUserOutput()));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
if (!src.copyFile(destFile)) {
|
if (!src.copyFile(destFile)) {
|
||||||
QMessageBox::warning(ICore::dialogParent(),
|
QMessageBox::warning(ICore::dialogParent(),
|
||||||
Tr::tr("Failed to Write File"),
|
Tr::tr("Failed to Write File"),
|
||||||
@@ -500,12 +528,18 @@ static bool copyPluginFile(const FilePath &src, const FilePath &dest)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
InstallResult executePluginInstallWizard(const FilePath &archive)
|
QString extensionId(PluginSpec *spec)
|
||||||
|
{
|
||||||
|
return spec->vendorId() + "." + spec->id();
|
||||||
|
}
|
||||||
|
|
||||||
|
InstallResult executePluginInstallWizard(const FilePath &archive, bool prepareForUpdate)
|
||||||
{
|
{
|
||||||
Wizard wizard;
|
Wizard wizard;
|
||||||
wizard.setWindowTitle(Tr::tr("Install Plugin"));
|
wizard.setWindowTitle(Tr::tr("Install Plugin"));
|
||||||
|
|
||||||
Data data;
|
Data data;
|
||||||
|
data.prepareForUpdate = prepareForUpdate;
|
||||||
|
|
||||||
if (archive.isEmpty()) {
|
if (archive.isEmpty()) {
|
||||||
auto filePage = new SourcePage(&data, &wizard);
|
auto filePage = new SourcePage(&data, &wizard);
|
||||||
@@ -526,10 +560,23 @@ InstallResult executePluginInstallWizard(const FilePath &archive)
|
|||||||
auto summaryPage = new SummaryPage(&data, &wizard);
|
auto summaryPage = new SummaryPage(&data, &wizard);
|
||||||
wizard.addPage(summaryPage);
|
wizard.addPage(summaryPage);
|
||||||
|
|
||||||
auto install = [&wizard, &data]() {
|
auto install = [&wizard, &data, prepareForUpdate]() {
|
||||||
if (wizard.exec()) {
|
if (wizard.exec()) {
|
||||||
const FilePath installPath = data.pluginSpec->installLocation(
|
const FilePath installPath = data.pluginSpec->installLocation(
|
||||||
!data.installIntoApplication);
|
!data.installIntoApplication)
|
||||||
|
/ extensionId(data.pluginSpec.get());
|
||||||
|
|
||||||
|
if (prepareForUpdate) {
|
||||||
|
if (hasLibSuffix(data.sourcePath)) {
|
||||||
|
ExtensionSystem::PluginManager::installPluginOnRestart(
|
||||||
|
data.pluginSpec->filePath(), installPath);
|
||||||
|
} else {
|
||||||
|
ExtensionSystem::PluginManager::installPluginOnRestart(
|
||||||
|
data.extractedPath, installPath);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if (hasLibSuffix(data.sourcePath)) {
|
if (hasLibSuffix(data.sourcePath)) {
|
||||||
if (!copyPluginFile(data.sourcePath, installPath))
|
if (!copyPluginFile(data.sourcePath, installPath))
|
||||||
return false;
|
return false;
|
||||||
@@ -568,6 +615,9 @@ InstallResult executePluginInstallWizard(const FilePath &archive)
|
|||||||
// so we can safely set them as accepted here.
|
// so we can safely set them as accepted here.
|
||||||
PluginManager::instance()->setTermsAndConditionsAccepted(data.pluginSpec.get());
|
PluginManager::instance()->setTermsAndConditionsAccepted(data.pluginSpec.get());
|
||||||
|
|
||||||
|
if (prepareForUpdate)
|
||||||
|
return InstallResult::NeedsRestart;
|
||||||
|
|
||||||
auto spec = data.pluginSpec.release();
|
auto spec = data.pluginSpec.release();
|
||||||
PluginManager::addPlugins({spec});
|
PluginManager::addPlugins({spec});
|
||||||
|
|
||||||
|
@@ -17,6 +17,7 @@ enum class InstallResult {
|
|||||||
NeedsRestart,
|
NeedsRestart,
|
||||||
};
|
};
|
||||||
|
|
||||||
CORE_EXPORT InstallResult executePluginInstallWizard(const Utils::FilePath &archive = {});
|
CORE_EXPORT InstallResult
|
||||||
|
executePluginInstallWizard(const Utils::FilePath &archive = {}, bool prepareForUpdate = false);
|
||||||
|
|
||||||
} // namespace Core
|
} // namespace Core
|
||||||
|
@@ -25,6 +25,7 @@
|
|||||||
#include <solutions/tasking/tasktreerunner.h>
|
#include <solutions/tasking/tasktreerunner.h>
|
||||||
|
|
||||||
#include <utils/algorithm.h>
|
#include <utils/algorithm.h>
|
||||||
|
#include <utils/appinfo.h>
|
||||||
#include <utils/fileutils.h>
|
#include <utils/fileutils.h>
|
||||||
#include <utils/hostosinfo.h>
|
#include <utils/hostosinfo.h>
|
||||||
#include <utils/icon.h>
|
#include <utils/icon.h>
|
||||||
@@ -87,6 +88,21 @@ static QWidget *toScrollableColumn(QWidget *widget)
|
|||||||
return scrollArea;
|
return scrollArea;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const char kRestartSetting[] = "RestartAfterPluginEnabledChanged";
|
||||||
|
|
||||||
|
static void requestRestart()
|
||||||
|
{
|
||||||
|
if (ICore::infoBar()->canInfoBeAdded(kRestartSetting)) {
|
||||||
|
Utils::InfoBarEntry
|
||||||
|
info(kRestartSetting, Core::Tr::tr("Plugin changes will take effect after restart."));
|
||||||
|
info.addCustomButton(Tr::tr("Restart Now"), [] {
|
||||||
|
ICore::infoBar()->removeInfo(kRestartSetting);
|
||||||
|
QTimer::singleShot(0, ICore::instance(), &ICore::restart);
|
||||||
|
});
|
||||||
|
ICore::infoBar()->addInfo(info);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class CollapsingWidget : public QWidget
|
class CollapsingWidget : public QWidget
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -153,6 +169,24 @@ public:
|
|||||||
installButton = new Button(Tr::tr("Install..."), Button::LargePrimary);
|
installButton = new Button(Tr::tr("Install..."), Button::LargePrimary);
|
||||||
installButton->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Preferred);
|
installButton->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Preferred);
|
||||||
installButton->hide();
|
installButton->hide();
|
||||||
|
connect(
|
||||||
|
installButton,
|
||||||
|
&QAbstractButton::pressed,
|
||||||
|
this,
|
||||||
|
&HeadingWidget::pluginInstallationRequested);
|
||||||
|
|
||||||
|
removeButton = new Button(Tr::tr("Remove ..."), Button::SmallSecondary);
|
||||||
|
removeButton->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Preferred);
|
||||||
|
removeButton->hide();
|
||||||
|
connect(removeButton, &QAbstractButton::pressed, this, [this]() {
|
||||||
|
ExtensionSystem::PluginManager::removePluginOnRestart(m_currentPluginId);
|
||||||
|
requestRestart();
|
||||||
|
});
|
||||||
|
|
||||||
|
updateButton = new Button(Tr::tr("Update ..."), Button::LargePrimary);
|
||||||
|
updateButton->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Preferred);
|
||||||
|
updateButton->hide();
|
||||||
|
connect(updateButton, &QAbstractButton::pressed, this, &HeadingWidget::pluginUpdateRequested);
|
||||||
|
|
||||||
using namespace Layouting;
|
using namespace Layouting;
|
||||||
Row {
|
Row {
|
||||||
@@ -181,6 +215,8 @@ public:
|
|||||||
},
|
},
|
||||||
Column {
|
Column {
|
||||||
installButton,
|
installButton,
|
||||||
|
updateButton,
|
||||||
|
removeButton,
|
||||||
st,
|
st,
|
||||||
},
|
},
|
||||||
noMargin, spacing(SpacingTokens::ExPaddingGapL),
|
noMargin, spacing(SpacingTokens::ExPaddingGapL),
|
||||||
@@ -189,8 +225,6 @@ public:
|
|||||||
setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Maximum);
|
setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Maximum);
|
||||||
m_dlCountItems->setVisible(false);
|
m_dlCountItems->setVisible(false);
|
||||||
|
|
||||||
connect(installButton, &QAbstractButton::pressed,
|
|
||||||
this, &HeadingWidget::pluginInstallationRequested);
|
|
||||||
connect(m_vendor, &QAbstractButton::pressed, this, [this]() {
|
connect(m_vendor, &QAbstractButton::pressed, this, [this]() {
|
||||||
emit vendorClicked(m_currentVendor);
|
emit vendorClicked(m_currentVendor);
|
||||||
});
|
});
|
||||||
@@ -203,6 +237,8 @@ public:
|
|||||||
if (!current.isValid())
|
if (!current.isValid())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
m_currentPluginId = current.data(RoleId).toString();
|
||||||
|
|
||||||
m_icon->setPixmap(itemIcon(current, SizeBig));
|
m_icon->setPixmap(itemIcon(current, SizeBig));
|
||||||
|
|
||||||
const QString name = current.data(RoleName).toString();
|
const QString name = current.data(RoleName).toString();
|
||||||
@@ -220,17 +256,27 @@ public:
|
|||||||
const QString description = current.data(RoleDescriptionShort).toString();
|
const QString description = current.data(RoleDescriptionShort).toString();
|
||||||
m_details->setText(description);
|
m_details->setText(description);
|
||||||
|
|
||||||
|
ExtensionSystem::PluginSpec *pluginSpec = pluginSpecForId(current.data(RoleId).toString());
|
||||||
|
|
||||||
const ItemType itemType = current.data(RoleItemType).value<ItemType>();
|
const ItemType itemType = current.data(RoleItemType).value<ItemType>();
|
||||||
const bool isPack = itemType == ItemTypePack;
|
const bool isPack = itemType == ItemTypePack;
|
||||||
const bool isRemotePlugin = !(isPack || pluginSpecForId(current.data(RoleId).toString()));
|
const bool isRemotePlugin = !(isPack || pluginSpec);
|
||||||
const QString downloadUrl = current.data(RoleDownloadUrl).toString();
|
const QString downloadUrl = current.data(RoleDownloadUrl).toString();
|
||||||
|
removeButton->setVisible(!isRemotePlugin && pluginSpec && !pluginSpec->isSystemPlugin());
|
||||||
installButton->setVisible(isRemotePlugin && !downloadUrl.isEmpty());
|
installButton->setVisible(isRemotePlugin && !downloadUrl.isEmpty());
|
||||||
if (installButton->isVisible())
|
if (installButton->isVisible())
|
||||||
installButton->setToolTip(downloadUrl);
|
installButton->setToolTip(downloadUrl);
|
||||||
|
|
||||||
|
updateButton->setVisible(
|
||||||
|
pluginSpec
|
||||||
|
&& ExtensionSystem::PluginSpec::versionCompare(
|
||||||
|
pluginSpec->version(), current.data(RoleVersion).toString())
|
||||||
|
< 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void pluginInstallationRequested();
|
void pluginInstallationRequested();
|
||||||
|
void pluginUpdateRequested();
|
||||||
void vendorClicked(const QString &vendor);
|
void vendorClicked(const QString &vendor);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -243,24 +289,12 @@ private:
|
|||||||
QWidget *m_dlCountItems;
|
QWidget *m_dlCountItems;
|
||||||
QLabel *m_details;
|
QLabel *m_details;
|
||||||
QAbstractButton *installButton;
|
QAbstractButton *installButton;
|
||||||
|
QAbstractButton *removeButton;
|
||||||
|
QAbstractButton *updateButton;
|
||||||
QString m_currentVendor;
|
QString m_currentVendor;
|
||||||
|
QString m_currentPluginId;
|
||||||
};
|
};
|
||||||
|
|
||||||
const char kRestartSetting[] = "RestartAfterPluginEnabledChanged";
|
|
||||||
|
|
||||||
static void requestRestart()
|
|
||||||
{
|
|
||||||
if (ICore::infoBar()->canInfoBeAdded(kRestartSetting)) {
|
|
||||||
Utils::InfoBarEntry
|
|
||||||
info(kRestartSetting, Core::Tr::tr("Plugin changes will take effect after restart."));
|
|
||||||
info.addCustomButton(Tr::tr("Restart Now"), [] {
|
|
||||||
ICore::infoBar()->removeInfo(kRestartSetting);
|
|
||||||
QTimer::singleShot(0, ICore::instance(), &ICore::restart);
|
|
||||||
});
|
|
||||||
ICore::infoBar()->addInfo(info);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class PluginStatusWidget : public QWidget
|
class PluginStatusWidget : public QWidget
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -394,7 +428,7 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
void updateView(const QModelIndex ¤t);
|
void updateView(const QModelIndex ¤t);
|
||||||
void fetchAndInstallPlugin(const QUrl &url, const QString &id);
|
void fetchAndInstallPlugin(const QUrl &url, const QString &id, bool update);
|
||||||
|
|
||||||
QString m_currentItemName;
|
QString m_currentItemName;
|
||||||
ExtensionsModel *m_extensionModel;
|
ExtensionsModel *m_extensionModel;
|
||||||
@@ -562,7 +596,10 @@ ExtensionManagerWidget::ExtensionManagerWidget()
|
|||||||
m_secondaryDescriptionWidget->setWidth(secondaryDescriptionWidth);
|
m_secondaryDescriptionWidget->setWidth(secondaryDescriptionWidth);
|
||||||
});
|
});
|
||||||
connect(m_headingWidget, &HeadingWidget::pluginInstallationRequested, this, [this]() {
|
connect(m_headingWidget, &HeadingWidget::pluginInstallationRequested, this, [this]() {
|
||||||
fetchAndInstallPlugin(QUrl::fromUserInput(m_currentDownloadUrl), m_currentId);
|
fetchAndInstallPlugin(QUrl::fromUserInput(m_currentDownloadUrl), m_currentId, false);
|
||||||
|
});
|
||||||
|
connect(m_headingWidget, &HeadingWidget::pluginUpdateRequested, this, [this]() {
|
||||||
|
fetchAndInstallPlugin(QUrl::fromUserInput(m_currentDownloadUrl), m_currentId, true);
|
||||||
});
|
});
|
||||||
connect(m_tags, &TagList::tagSelected, m_extensionBrowser, &ExtensionsBrowser::setFilter);
|
connect(m_tags, &TagList::tagSelected, m_extensionBrowser, &ExtensionsBrowser::setFilter);
|
||||||
connect(m_headingWidget, &HeadingWidget::vendorClicked,
|
connect(m_headingWidget, &HeadingWidget::vendorClicked,
|
||||||
@@ -649,7 +686,7 @@ void ExtensionManagerWidget::updateView(const QModelIndex ¤t)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExtensionManagerWidget::fetchAndInstallPlugin(const QUrl &url, const QString &id)
|
void ExtensionManagerWidget::fetchAndInstallPlugin(const QUrl &url, const QString &id, bool update)
|
||||||
{
|
{
|
||||||
using namespace Tasking;
|
using namespace Tasking;
|
||||||
|
|
||||||
@@ -720,7 +757,7 @@ void ExtensionManagerWidget::fetchAndInstallPlugin(const QUrl &url, const QStrin
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const auto onPluginInstallation = [storage]() {
|
const auto onPluginInstallation = [storage, update]() {
|
||||||
if (storage->packageData.isEmpty())
|
if (storage->packageData.isEmpty())
|
||||||
return false;
|
return false;
|
||||||
const FilePath source = FilePath::fromUrl(storage->url);
|
const FilePath source = FilePath::fromUrl(storage->url);
|
||||||
@@ -730,7 +767,7 @@ void ExtensionManagerWidget::fetchAndInstallPlugin(const QUrl &url, const QStrin
|
|||||||
|
|
||||||
saver.write(storage->packageData);
|
saver.write(storage->packageData);
|
||||||
if (saver.finalize(ICore::dialogParent())) {
|
if (saver.finalize(ICore::dialogParent())) {
|
||||||
auto result = executePluginInstallWizard(saver.filePath());
|
auto result = executePluginInstallWizard(saver.filePath(), update);
|
||||||
switch (result) {
|
switch (result) {
|
||||||
case InstallResult::Success:
|
case InstallResult::Success:
|
||||||
return true;
|
return true;
|
||||||
|
@@ -48,7 +48,7 @@ public:
|
|||||||
|
|
||||||
void ExtensionsModelPrivate::addUnlistedLocalPlugins()
|
void ExtensionsModelPrivate::addUnlistedLocalPlugins()
|
||||||
{
|
{
|
||||||
QStringList responseExtensions;
|
QSet<QString> responseExtensions;
|
||||||
for (const QJsonValueConstRef &responseItem : qAsConst(responseItems))
|
for (const QJsonValueConstRef &responseItem : qAsConst(responseItems))
|
||||||
responseExtensions << responseItem.toObject().value("id").toString();
|
responseExtensions << responseItem.toObject().value("id").toString();
|
||||||
|
|
||||||
@@ -124,6 +124,8 @@ QVariant ExtensionsModelPrivate::dataFromRemotePlugin(const QJsonObject &json, i
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case RoleVersion:
|
||||||
|
return metaData.value("Version");
|
||||||
case RoleItemType:
|
case RoleItemType:
|
||||||
return ItemTypeExtension;
|
return ItemTypeExtension;
|
||||||
case RoleDescriptionLong: {
|
case RoleDescriptionLong: {
|
||||||
|
@@ -416,10 +416,26 @@ public:
|
|||||||
FilePaths folders = path.dirEntries(FileFilter({}, QDir::Dirs | QDir::NoDotAndDotDot));
|
FilePaths folders = path.dirEntries(FileFilter({}, QDir::Dirs | QDir::NoDotAndDotDot));
|
||||||
|
|
||||||
for (const FilePath &folder : folders) {
|
for (const FilePath &folder : folders) {
|
||||||
const FilePath script = folder / (folder.baseName() + ".lua");
|
FilePath script = folder / (folder.baseName() + ".lua");
|
||||||
if (!script.exists())
|
if (!script.exists()) {
|
||||||
|
FilePaths contents = folder.dirEntries(QDir::Dirs | QDir::NoDotAndDotDot);
|
||||||
|
if (contents.empty())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
for (const FilePath &subfolder : contents) {
|
||||||
|
script = subfolder / (subfolder.baseName() + ".lua");
|
||||||
|
if (!script.exists()) {
|
||||||
|
script.clear();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (script.isEmpty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
const expected_str<LuaPluginSpec *> result = loadPlugin(script);
|
const expected_str<LuaPluginSpec *> result = loadPlugin(script);
|
||||||
|
|
||||||
if (!result) {
|
if (!result) {
|
||||||
@@ -430,6 +446,19 @@ public:
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (PluginManager::takePluginIdForRemoval((*result)->id())) {
|
||||||
|
auto removeResult = (*result)->removePluginFiles();
|
||||||
|
if (!removeResult) {
|
||||||
|
qWarning() << "Failed to remove plugin files" << script << ":"
|
||||||
|
<< removeResult.error();
|
||||||
|
MessageManager::writeFlashing(
|
||||||
|
Tr::tr("Failed to remove plugin files of %1: %2")
|
||||||
|
.arg((*result)->id())
|
||||||
|
.arg(removeResult.error()));
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
plugins.insert(*result);
|
plugins.insert(*result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user