forked from qt-creator/qt-creator
Plugin install: Check if archive contains a usable plugin
Checks if there is a library file which is a Qt Creator plugin that is compatible with the version of Qt Creator that is running. Change-Id: Ic5284e3803c45b8e2ef0d30afccb1680fabf43f3 Reviewed-by: Alessandro Portale <alessandro.portale@qt.io>
This commit is contained in:
@@ -1559,11 +1559,9 @@ void PluginManagerPrivate::readPluginPaths()
|
|||||||
pluginCategories.insert(QString(), QVector<PluginSpec *>());
|
pluginCategories.insert(QString(), QVector<PluginSpec *>());
|
||||||
|
|
||||||
for (const QString &pluginFile : pluginFiles(pluginPaths)) {
|
for (const QString &pluginFile : pluginFiles(pluginPaths)) {
|
||||||
auto *spec = new PluginSpec;
|
PluginSpec *spec = PluginSpec::read(pluginFile);
|
||||||
if (!spec->d->read(pluginFile)) { // not a Qt Creator plugin
|
if (!spec) // not a Qt Creator plugin
|
||||||
delete spec;
|
|
||||||
continue;
|
continue;
|
||||||
}
|
|
||||||
|
|
||||||
// defaultDisabledPlugins and defaultEnabledPlugins from install settings
|
// defaultDisabledPlugins and defaultEnabledPlugins from install settings
|
||||||
// is used to override the defaults read from the plugin spec
|
// is used to override the defaults read from the plugin spec
|
||||||
|
@@ -547,6 +547,16 @@ void PluginSpec::setEnabledBySettings(bool value)
|
|||||||
d->setEnabledBySettings(value);
|
d->setEnabledBySettings(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PluginSpec *PluginSpec::read(const QString &filePath)
|
||||||
|
{
|
||||||
|
auto spec = new PluginSpec;
|
||||||
|
if (!spec->d->read(filePath)) { // not a Qt Creator plugin
|
||||||
|
delete spec;
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return spec;
|
||||||
|
}
|
||||||
|
|
||||||
//==========PluginSpecPrivate==================
|
//==========PluginSpecPrivate==================
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
@@ -133,6 +133,8 @@ public:
|
|||||||
|
|
||||||
void setEnabledBySettings(bool value);
|
void setEnabledBySettings(bool value);
|
||||||
|
|
||||||
|
static PluginSpec *read(const QString &filePath);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
PluginSpec();
|
PluginSpec();
|
||||||
|
|
||||||
|
@@ -25,13 +25,18 @@
|
|||||||
|
|
||||||
#include "plugininstallwizard.h"
|
#include "plugininstallwizard.h"
|
||||||
|
|
||||||
|
#include "coreplugin.h"
|
||||||
#include "icore.h"
|
#include "icore.h"
|
||||||
|
|
||||||
|
#include <extensionsystem/pluginspec.h>
|
||||||
|
|
||||||
#include <utils/archive.h>
|
#include <utils/archive.h>
|
||||||
#include <utils/fileutils.h>
|
#include <utils/fileutils.h>
|
||||||
#include <utils/hostosinfo.h>
|
#include <utils/hostosinfo.h>
|
||||||
#include <utils/infolabel.h>
|
#include <utils/infolabel.h>
|
||||||
#include <utils/pathchooser.h>
|
#include <utils/pathchooser.h>
|
||||||
|
#include <utils/qtcassert.h>
|
||||||
|
#include <utils/runextensions.h>
|
||||||
#include <utils/temporarydirectory.h>
|
#include <utils/temporarydirectory.h>
|
||||||
#include <utils/wizard.h>
|
#include <utils/wizard.h>
|
||||||
#include <utils/wizardpage.h>
|
#include <utils/wizardpage.h>
|
||||||
@@ -40,6 +45,7 @@
|
|||||||
|
|
||||||
#include <QButtonGroup>
|
#include <QButtonGroup>
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
|
#include <QDirIterator>
|
||||||
#include <QFileInfo>
|
#include <QFileInfo>
|
||||||
#include <QLabel>
|
#include <QLabel>
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
@@ -50,6 +56,7 @@
|
|||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
|
using namespace ExtensionSystem;
|
||||||
using namespace Utils;
|
using namespace Utils;
|
||||||
|
|
||||||
struct Data
|
struct Data
|
||||||
@@ -59,6 +66,15 @@ struct Data
|
|||||||
bool installIntoApplication;
|
bool installIntoApplication;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static QStringList libraryNameFilter()
|
||||||
|
{
|
||||||
|
if (HostOsInfo().isWindowsHost())
|
||||||
|
return {"*.dll"};
|
||||||
|
if (HostOsInfo().isLinuxHost())
|
||||||
|
return {"*.so"};
|
||||||
|
return {"*.dylib"};
|
||||||
|
}
|
||||||
|
|
||||||
static bool hasLibSuffix(const FilePath &path)
|
static bool hasLibSuffix(const FilePath &path)
|
||||||
{
|
{
|
||||||
return (HostOsInfo().isWindowsHost() && path.endsWith(".dll"))
|
return (HostOsInfo().isWindowsHost() && path.endsWith(".dll"))
|
||||||
@@ -146,6 +162,12 @@ public:
|
|||||||
class CheckArchivePage : public WizardPage
|
class CheckArchivePage : public WizardPage
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
struct ArchiveIssue
|
||||||
|
{
|
||||||
|
QString message;
|
||||||
|
InfoLabel::InfoType type;
|
||||||
|
};
|
||||||
|
|
||||||
CheckArchivePage(Data *data, QWidget *parent)
|
CheckArchivePage(Data *data, QWidget *parent)
|
||||||
: WizardPage(parent)
|
: WizardPage(parent)
|
||||||
, m_data(data)
|
, m_data(data)
|
||||||
@@ -155,6 +177,8 @@ public:
|
|||||||
setLayout(vlayout);
|
setLayout(vlayout);
|
||||||
|
|
||||||
m_label = new InfoLabel;
|
m_label = new InfoLabel;
|
||||||
|
m_label->setElideMode(Qt::ElideNone);
|
||||||
|
m_label->setWordWrap(true);
|
||||||
m_cancelButton = new QPushButton(PluginInstallWizard::tr("Cancel"));
|
m_cancelButton = new QPushButton(PluginInstallWizard::tr("Cancel"));
|
||||||
m_output = new QTextEdit;
|
m_output = new QTextEdit;
|
||||||
m_output->setReadOnly(true);
|
m_output->setReadOnly(true);
|
||||||
@@ -187,18 +211,17 @@ public:
|
|||||||
if (!m_archive) {
|
if (!m_archive) {
|
||||||
m_label->setType(InfoLabel::Error);
|
m_label->setType(InfoLabel::Error);
|
||||||
m_label->setText(PluginInstallWizard::tr("The file is not an archive."));
|
m_label->setText(PluginInstallWizard::tr("The file is not an archive."));
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
QObject::connect(m_archive, &Archive::outputReceived, this, [this](const QString &output) {
|
QObject::connect(m_archive, &Archive::outputReceived, this, [this](const QString &output) {
|
||||||
m_output->append(output);
|
m_output->append(output);
|
||||||
});
|
});
|
||||||
QObject::connect(m_archive, &Archive::finished, this, [this](bool success) {
|
QObject::connect(m_archive, &Archive::finished, this, [this](bool success) {
|
||||||
m_cancelButton->setVisible(false);
|
m_archive = nullptr; // we don't own it
|
||||||
m_isComplete = success;
|
m_cancelButton->disconnect();
|
||||||
if (success) {
|
if (!success) { // unarchiving failed
|
||||||
m_label->setType(InfoLabel::Ok);
|
m_cancelButton->setVisible(false);
|
||||||
m_label->setText(PluginInstallWizard::tr("Archive is ok."));
|
|
||||||
} else {
|
|
||||||
if (m_canceled) {
|
if (m_canceled) {
|
||||||
m_label->setType(InfoLabel::Information);
|
m_label->setType(InfoLabel::Information);
|
||||||
m_label->setText(PluginInstallWizard::tr("Canceled."));
|
m_label->setText(PluginInstallWizard::tr("Canceled."));
|
||||||
@@ -207,9 +230,31 @@ public:
|
|||||||
m_label->setText(
|
m_label->setText(
|
||||||
PluginInstallWizard::tr("There was an error while unarchiving."));
|
PluginInstallWizard::tr("There was an error while unarchiving."));
|
||||||
}
|
}
|
||||||
|
} else { // unarchiving was successful, run a check
|
||||||
|
m_archiveCheck = Utils::runAsync(
|
||||||
|
[this](QFutureInterface<ArchiveIssue> &fi) { return checkContents(fi); });
|
||||||
|
Utils::onFinished(m_archiveCheck, this, [this](const QFuture<ArchiveIssue> &f) {
|
||||||
|
m_cancelButton->setVisible(false);
|
||||||
|
m_cancelButton->disconnect();
|
||||||
|
const bool ok = f.resultCount() == 0 && !f.isCanceled();
|
||||||
|
if (f.isCanceled()) {
|
||||||
|
m_label->setType(InfoLabel::Information);
|
||||||
|
m_label->setText(PluginInstallWizard::tr("Canceled."));
|
||||||
|
} else if (ok) {
|
||||||
|
m_label->setType(InfoLabel::Ok);
|
||||||
|
m_label->setText(PluginInstallWizard::tr("Archive is OK."));
|
||||||
|
} else {
|
||||||
|
const ArchiveIssue issue = f.result();
|
||||||
|
m_label->setType(issue.type);
|
||||||
|
m_label->setText(issue.message);
|
||||||
|
}
|
||||||
|
m_isComplete = ok;
|
||||||
|
emit completeChanged();
|
||||||
|
});
|
||||||
|
QObject::connect(m_cancelButton, &QPushButton::clicked, this, [this] {
|
||||||
|
m_archiveCheck.cancel();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
m_archive = nullptr; // we don't own it
|
|
||||||
emit completeChanged();
|
|
||||||
});
|
});
|
||||||
QObject::connect(m_cancelButton, &QPushButton::clicked, m_archive, [this] {
|
QObject::connect(m_cancelButton, &QPushButton::clicked, m_archive, [this] {
|
||||||
m_canceled = true;
|
m_canceled = true;
|
||||||
@@ -217,15 +262,60 @@ public:
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Async. Result is set if any issue was found.
|
||||||
|
void checkContents(QFutureInterface<ArchiveIssue> &fi)
|
||||||
|
{
|
||||||
|
QTC_ASSERT(m_tempDir.get(), return );
|
||||||
|
|
||||||
|
PluginSpec *coreplugin = CorePlugin::instance()->pluginSpec();
|
||||||
|
|
||||||
|
// look for plugin
|
||||||
|
QDirIterator it(m_tempDir->path(), libraryNameFilter(), QDir::Files | QDir::NoSymLinks);
|
||||||
|
while (it.hasNext()) {
|
||||||
|
if (fi.isCanceled())
|
||||||
|
return;
|
||||||
|
it.next();
|
||||||
|
PluginSpec *spec = PluginSpec::read(it.filePath());
|
||||||
|
if (spec) {
|
||||||
|
// Is a Qt Creator plugin. Let's see if we find a Core dependency and check the
|
||||||
|
// version
|
||||||
|
const QVector<PluginDependency> dependencies = spec->dependencies();
|
||||||
|
const auto found = std::find_if(dependencies.constBegin(),
|
||||||
|
dependencies.constEnd(),
|
||||||
|
[coreplugin](const PluginDependency &d) {
|
||||||
|
return d.name == coreplugin->name();
|
||||||
|
});
|
||||||
|
if (found != dependencies.constEnd()) {
|
||||||
|
if (!coreplugin->provides(found->name, found->version)) {
|
||||||
|
fi.reportResult({PluginInstallWizard::tr(
|
||||||
|
"Plugin requires an incompatible version of %1 (%2).")
|
||||||
|
.arg(Constants::IDE_DISPLAY_NAME)
|
||||||
|
.arg(found->version),
|
||||||
|
InfoLabel::Error});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return; // successful / no error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fi.reportResult({PluginInstallWizard::tr("Did not find %1 plugin in toplevel directory.")
|
||||||
|
.arg(Constants::IDE_DISPLAY_NAME),
|
||||||
|
InfoLabel::Error});
|
||||||
|
}
|
||||||
|
|
||||||
void cleanupPage()
|
void cleanupPage()
|
||||||
{
|
{
|
||||||
// back button pressed
|
// back button pressed
|
||||||
|
m_cancelButton->disconnect();
|
||||||
if (m_archive) {
|
if (m_archive) {
|
||||||
m_cancelButton->disconnect();
|
|
||||||
m_archive->disconnect();
|
m_archive->disconnect();
|
||||||
m_archive->cancel();
|
m_archive->cancel();
|
||||||
m_archive = nullptr; // we don't own it
|
m_archive = nullptr; // we don't own it
|
||||||
}
|
}
|
||||||
|
if (m_archiveCheck.isRunning()) {
|
||||||
|
m_archiveCheck.cancel();
|
||||||
|
m_archiveCheck.waitForFinished();
|
||||||
|
}
|
||||||
m_tempDir.reset();
|
m_tempDir.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -233,6 +323,7 @@ public:
|
|||||||
|
|
||||||
std::unique_ptr<TemporaryDirectory> m_tempDir;
|
std::unique_ptr<TemporaryDirectory> m_tempDir;
|
||||||
Archive *m_archive = nullptr;
|
Archive *m_archive = nullptr;
|
||||||
|
QFuture<ArchiveIssue> m_archiveCheck;
|
||||||
InfoLabel *m_label = nullptr;
|
InfoLabel *m_label = nullptr;
|
||||||
QPushButton *m_cancelButton = nullptr;
|
QPushButton *m_cancelButton = nullptr;
|
||||||
QTextEdit *m_output = nullptr;
|
QTextEdit *m_output = nullptr;
|
||||||
|
Reference in New Issue
Block a user