forked from qt-creator/qt-creator
PluginInstallWizard: Reuse TaskTree
Change-Id: I0e139627a9d4c3401bcf7727ef1e51b2de858b77 Reviewed-by: <github-actions-qt-creator@cristianadam.eu> Reviewed-by: Eike Ziller <eike.ziller@qt.io>
This commit is contained in:
@@ -28,7 +28,6 @@
|
||||
#include <QButtonGroup>
|
||||
#include <QDir>
|
||||
#include <QDirIterator>
|
||||
#include <QFileInfo>
|
||||
#include <QLabel>
|
||||
#include <QMessageBox>
|
||||
#include <QPushButton>
|
||||
@@ -38,6 +37,7 @@
|
||||
#include <memory>
|
||||
|
||||
using namespace ExtensionSystem;
|
||||
using namespace Tasking;
|
||||
using namespace Utils;
|
||||
|
||||
struct Data
|
||||
@@ -117,12 +117,11 @@ public:
|
||||
if (hasLibSuffix(path))
|
||||
return true;
|
||||
|
||||
QString error;
|
||||
if (!Archive::supportsFile(path, &error)) {
|
||||
m_info->setText(error);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
const auto sourceAndCommand = Unarchiver::sourceAndCommand(path);
|
||||
if (!sourceAndCommand)
|
||||
m_info->setText(sourceAndCommand.error());
|
||||
|
||||
return bool(sourceAndCommand);
|
||||
}
|
||||
|
||||
int nextId() const final
|
||||
@@ -136,15 +135,48 @@ public:
|
||||
Data *m_data = nullptr;
|
||||
};
|
||||
|
||||
struct ArchiveIssue
|
||||
{
|
||||
QString message;
|
||||
InfoLabel::InfoType type;
|
||||
};
|
||||
|
||||
// Async. Result is set if any issue was found.
|
||||
void checkContents(QPromise<ArchiveIssue> &promise, const FilePath &tempDir)
|
||||
{
|
||||
PluginSpec *coreplugin = PluginManager::specForPlugin(CorePlugin::instance());
|
||||
|
||||
// look for plugin
|
||||
QDirIterator it(tempDir.path(), libraryNameFilter(), QDir::Files | QDir::NoSymLinks,
|
||||
QDirIterator::Subdirectories);
|
||||
while (it.hasNext()) {
|
||||
if (promise.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())
|
||||
return;
|
||||
if (coreplugin->provides(found->name, found->version))
|
||||
return;
|
||||
promise.addResult(ArchiveIssue{
|
||||
Tr::tr("Plugin requires an incompatible version of %1 (%2).")
|
||||
.arg(Constants::IDE_DISPLAY_NAME, found->version), InfoLabel::Error});
|
||||
return; // successful / no error
|
||||
}
|
||||
}
|
||||
promise.addResult(ArchiveIssue{Tr::tr("Did not find %1 plugin.")
|
||||
.arg(Constants::IDE_DISPLAY_NAME), InfoLabel::Error});
|
||||
}
|
||||
|
||||
class CheckArchivePage : public WizardPage
|
||||
{
|
||||
public:
|
||||
struct ArchiveIssue
|
||||
{
|
||||
QString message;
|
||||
InfoLabel::InfoType type;
|
||||
};
|
||||
|
||||
CheckArchivePage(Data *data, QWidget *parent)
|
||||
: WizardPage(parent)
|
||||
, m_data(data)
|
||||
@@ -155,6 +187,12 @@ public:
|
||||
m_label->setElideMode(Qt::ElideNone);
|
||||
m_label->setWordWrap(true);
|
||||
m_cancelButton = new QPushButton(Tr::tr("Cancel"));
|
||||
connect(m_cancelButton, &QPushButton::clicked, this, [this] {
|
||||
m_taskTree.reset();
|
||||
m_cancelButton->setVisible(false);
|
||||
m_label->setType(InfoLabel::Information);
|
||||
m_label->setText(Tr::tr("Canceled."));
|
||||
});
|
||||
m_output = new QTextEdit;
|
||||
m_output->setReadOnly(true);
|
||||
|
||||
@@ -169,142 +207,87 @@ public:
|
||||
{
|
||||
m_isComplete = false;
|
||||
emit completeChanged();
|
||||
m_canceled = false;
|
||||
|
||||
m_tempDir = std::make_unique<TemporaryDirectory>("plugininstall");
|
||||
m_data->extractedPath = m_tempDir->path();
|
||||
m_label->setText(Tr::tr("Checking archive..."));
|
||||
m_label->setType(InfoLabel::None);
|
||||
|
||||
m_cancelButton->setVisible(true);
|
||||
m_output->clear();
|
||||
|
||||
m_archive.reset(new Archive(m_data->sourcePath, m_tempDir->path()));
|
||||
if (!m_archive->isValid()) {
|
||||
const auto sourceAndCommand = Unarchiver::sourceAndCommand(m_data->sourcePath);
|
||||
if (!sourceAndCommand) {
|
||||
m_label->setType(InfoLabel::Error);
|
||||
m_label->setText(Tr::tr("The file is not an archive."));
|
||||
m_label->setText(sourceAndCommand.error());
|
||||
return;
|
||||
}
|
||||
QObject::connect(m_archive.get(), &Archive::outputReceived, this,
|
||||
[this](const QString &output) {
|
||||
m_output->append(output);
|
||||
});
|
||||
QObject::connect(m_archive.get(), &Archive::finished, this, [this](bool success) {
|
||||
m_archive.release()->deleteLater();
|
||||
handleFinished(success);
|
||||
});
|
||||
QObject::connect(m_cancelButton, &QPushButton::clicked, this, [this] {
|
||||
m_canceled = true;
|
||||
m_archive.reset();
|
||||
handleFinished(false);
|
||||
});
|
||||
m_archive->unarchive();
|
||||
}
|
||||
|
||||
void handleFinished(bool success)
|
||||
{
|
||||
m_cancelButton->disconnect();
|
||||
if (!success) { // unarchiving failed
|
||||
m_cancelButton->setVisible(false);
|
||||
if (m_canceled) {
|
||||
m_label->setType(InfoLabel::Information);
|
||||
m_label->setText(Tr::tr("Canceled."));
|
||||
const auto onUnarchiverSetup = [this, sourceAndCommand](Unarchiver &unarchiver) {
|
||||
unarchiver.setSourceAndCommand(*sourceAndCommand);
|
||||
unarchiver.setDestDir(m_tempDir->path());
|
||||
connect(&unarchiver, &Unarchiver::outputReceived, this, [this](const QString &output) {
|
||||
m_output->append(output);
|
||||
});
|
||||
};
|
||||
const auto onUnarchiverError = [this](const Unarchiver &) {
|
||||
m_label->setType(InfoLabel::Error);
|
||||
m_label->setText(Tr::tr("There was an error while unarchiving."));
|
||||
};
|
||||
|
||||
const auto onCheckerSetup = [this](Async<ArchiveIssue> &async) {
|
||||
if (!m_tempDir)
|
||||
return TaskAction::StopWithError;
|
||||
|
||||
async.setConcurrentCallData(checkContents, m_tempDir->path());
|
||||
async.setFutureSynchronizer(PluginManager::futureSynchronizer());
|
||||
return TaskAction::Continue;
|
||||
};
|
||||
const auto onCheckerDone = [this](const Async<ArchiveIssue> &async) {
|
||||
m_isComplete = !async.isResultAvailable();
|
||||
if (m_isComplete) {
|
||||
m_label->setType(InfoLabel::Ok);
|
||||
m_label->setText(Tr::tr("Archive is OK."));
|
||||
} else {
|
||||
m_label->setType(InfoLabel::Error);
|
||||
m_label->setText(Tr::tr("There was an error while unarchiving."));
|
||||
const ArchiveIssue issue = async.result();
|
||||
m_label->setType(issue.type);
|
||||
m_label->setText(issue.message);
|
||||
}
|
||||
} else { // unarchiving was successful, run a check
|
||||
m_archiveCheck = Utils::asyncRun([this](QPromise<ArchiveIssue> &promise)
|
||||
{ return checkContents(promise); });
|
||||
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(Tr::tr("Canceled."));
|
||||
} else if (ok) {
|
||||
m_label->setType(InfoLabel::Ok);
|
||||
m_label->setText(Tr::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();
|
||||
});
|
||||
}
|
||||
}
|
||||
emit completeChanged();
|
||||
};
|
||||
|
||||
// Async. Result is set if any issue was found.
|
||||
void checkContents(QPromise<ArchiveIssue> &promise)
|
||||
{
|
||||
QTC_ASSERT(m_tempDir.get(), return );
|
||||
const Group root {
|
||||
UnarchiverTask(onUnarchiverSetup, {}, onUnarchiverError),
|
||||
AsyncTask<ArchiveIssue>(onCheckerSetup, onCheckerDone)
|
||||
};
|
||||
m_taskTree.reset(new TaskTree(root));
|
||||
|
||||
PluginSpec *coreplugin = PluginManager::specForPlugin(CorePlugin::instance());
|
||||
const auto onEnd = [this] {
|
||||
m_cancelButton->setVisible(false);
|
||||
m_taskTree.release()->deleteLater();
|
||||
};
|
||||
connect(m_taskTree.get(), &TaskTree::done, this, onEnd);
|
||||
connect(m_taskTree.get(), &TaskTree::errorOccurred, this, onEnd);
|
||||
|
||||
// look for plugin
|
||||
QDirIterator it(m_tempDir->path().path(),
|
||||
libraryNameFilter(),
|
||||
QDir::Files | QDir::NoSymLinks,
|
||||
QDirIterator::Subdirectories);
|
||||
while (it.hasNext()) {
|
||||
if (promise.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)) {
|
||||
promise.addResult(ArchiveIssue{
|
||||
Tr::tr("Plugin requires an incompatible version of %1 (%2).")
|
||||
.arg(Constants::IDE_DISPLAY_NAME).arg(found->version),
|
||||
InfoLabel::Error});
|
||||
return;
|
||||
}
|
||||
}
|
||||
return; // successful / no error
|
||||
}
|
||||
}
|
||||
promise.addResult(ArchiveIssue{Tr::tr("Did not find %1 plugin.")
|
||||
.arg(Constants::IDE_DISPLAY_NAME), InfoLabel::Error});
|
||||
m_cancelButton->setVisible(true);
|
||||
m_taskTree->start();
|
||||
}
|
||||
|
||||
void cleanupPage() final
|
||||
{
|
||||
// back button pressed
|
||||
m_cancelButton->disconnect();
|
||||
m_archive.reset();
|
||||
if (m_archiveCheck.isRunning()) {
|
||||
m_archiveCheck.cancel();
|
||||
m_archiveCheck.waitForFinished();
|
||||
}
|
||||
m_taskTree.reset();
|
||||
m_tempDir.reset();
|
||||
}
|
||||
|
||||
bool isComplete() const final { return m_isComplete; }
|
||||
|
||||
std::unique_ptr<TemporaryDirectory> m_tempDir;
|
||||
std::unique_ptr<Archive> m_archive;
|
||||
QFuture<ArchiveIssue> m_archiveCheck;
|
||||
std::unique_ptr<TaskTree> m_taskTree;
|
||||
InfoLabel *m_label = nullptr;
|
||||
QPushButton *m_cancelButton = nullptr;
|
||||
QTextEdit *m_output = nullptr;
|
||||
Data *m_data = nullptr;
|
||||
bool m_isComplete = false;
|
||||
bool m_canceled = false;
|
||||
};
|
||||
|
||||
class InstallLocationPage : public WizardPage
|
||||
|
||||
Reference in New Issue
Block a user