forked from qt-creator/qt-creator
Utils: Add async variant of DataFromProcess
... and make use of it in PathChooser. Change-Id: I0e81afec2caf38f488a8ab98b55016535c187fc2 Reviewed-by: <github-actions-qt-creator@cristianadam.eu> Reviewed-by: hjk <hjk@qt.io> Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
This commit is contained in:
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
#include <memory>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
@@ -32,6 +33,7 @@ public:
|
|||||||
public:
|
public:
|
||||||
using OutputParser = std::function<std::optional<Data>(const QString &)>;
|
using OutputParser = std::function<std::optional<Data>(const QString &)>;
|
||||||
using ErrorHandler = std::function<void(const Process &)>;
|
using ErrorHandler = std::function<void(const Process &)>;
|
||||||
|
using Callback = std::function<void(const std::optional<Data> &)>;
|
||||||
|
|
||||||
Parameters(const CommandLine &cmdLine, const OutputParser &parser)
|
Parameters(const CommandLine &cmdLine, const OutputParser &parser)
|
||||||
: commandLine(cmdLine)
|
: commandLine(cmdLine)
|
||||||
@@ -43,25 +45,50 @@ public:
|
|||||||
std::chrono::seconds timeout = std::chrono::seconds(10);
|
std::chrono::seconds timeout = std::chrono::seconds(10);
|
||||||
OutputParser parser;
|
OutputParser parser;
|
||||||
ErrorHandler errorHandler;
|
ErrorHandler errorHandler;
|
||||||
|
Callback callback;
|
||||||
QList<ProcessResult> allowedResults{ProcessResult::FinishedWithSuccess};
|
QList<ProcessResult> allowedResults{ProcessResult::FinishedWithSuccess};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Use the first variant whenever possible.
|
||||||
|
static void provideData(const Parameters ¶ms);
|
||||||
static std::optional<Data> getData(const Parameters ¶ms);
|
static std::optional<Data> getData(const Parameters ¶ms);
|
||||||
|
|
||||||
// TODO: async variant.
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
using Key = std::tuple<FilePath, QStringList, QString>;
|
using Key = std::tuple<FilePath, QStringList, QString>;
|
||||||
using Value = std::pair<std::optional<Data>, QDateTime>;
|
using Value = std::pair<std::optional<Data>, QDateTime>;
|
||||||
|
|
||||||
|
static std::optional<Data> getOrProvideData(const Parameters ¶ms);
|
||||||
|
static std::optional<Data> handleProcessFinished(const Parameters ¶ms,
|
||||||
|
const QDateTime &exeTimestamp,
|
||||||
|
const Key &cacheKey,
|
||||||
|
const std::shared_ptr<Process> &process);
|
||||||
|
|
||||||
static inline QHash<Key, Value> m_cache;
|
static inline QHash<Key, Value> m_cache;
|
||||||
static inline QMutex m_cacheMutex;
|
static inline QMutex m_cacheMutex;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template<typename Data>
|
||||||
|
inline void DataFromProcess<Data>::provideData(const Parameters ¶ms)
|
||||||
|
{
|
||||||
|
QTC_ASSERT(params.callback, return);
|
||||||
|
getOrProvideData(params);
|
||||||
|
}
|
||||||
|
|
||||||
template<typename Data>
|
template<typename Data>
|
||||||
inline std::optional<Data> DataFromProcess<Data>::getData(const Parameters ¶ms)
|
inline std::optional<Data> DataFromProcess<Data>::getData(const Parameters ¶ms)
|
||||||
{
|
{
|
||||||
if (params.commandLine.executable().isEmpty())
|
QTC_ASSERT(!params.callback, return {});
|
||||||
|
return getOrProvideData(params);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Data>
|
||||||
|
inline std::optional<Data> DataFromProcess<Data>::getOrProvideData(const Parameters ¶ms)
|
||||||
|
{
|
||||||
|
if (params.commandLine.executable().isEmpty()) {
|
||||||
|
if (params.callback)
|
||||||
|
params.callback({});
|
||||||
return {};
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
const auto key = std::make_tuple(params.commandLine.executable(),
|
const auto key = std::make_tuple(params.commandLine.executable(),
|
||||||
params.environment.toStringList(),
|
params.environment.toStringList(),
|
||||||
@@ -74,21 +101,47 @@ inline std::optional<Data> DataFromProcess<Data>::getData(const Parameters ¶
|
|||||||
return it.value().first;
|
return it.value().first;
|
||||||
}
|
}
|
||||||
|
|
||||||
Process outputRetriever;
|
const auto outputRetriever = std::make_shared<Process>();
|
||||||
outputRetriever.setCommand(params.commandLine);
|
outputRetriever->setCommand(params.commandLine);
|
||||||
outputRetriever.runBlocking(params.timeout);
|
if (params.callback) {
|
||||||
|
QObject::connect(outputRetriever.get(),
|
||||||
// Do not store into cache: The next call might succeed.
|
&Process::done,
|
||||||
if (outputRetriever.result() == ProcessResult::Canceled)
|
[params, exeTimestamp, key, outputRetriever] {
|
||||||
|
handleProcessFinished(params, exeTimestamp, key, outputRetriever);
|
||||||
|
});
|
||||||
|
outputRetriever->start();
|
||||||
return {};
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
outputRetriever->runBlocking(params.timeout);
|
||||||
|
return handleProcessFinished(params, exeTimestamp, key, outputRetriever);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Data>
|
||||||
|
inline std::optional<Data> DataFromProcess<Data>::handleProcessFinished(
|
||||||
|
const Parameters ¶ms,
|
||||||
|
const QDateTime &exeTimestamp,
|
||||||
|
const Key &cacheKey,
|
||||||
|
const std::shared_ptr<Process> &process)
|
||||||
|
{
|
||||||
|
// Do not store into cache: The next call might succeed.
|
||||||
|
if (process->result() == ProcessResult::Canceled) {
|
||||||
|
if (params.callback)
|
||||||
|
params.callback({});
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
std::optional<Data> data;
|
std::optional<Data> data;
|
||||||
if (params.allowedResults.contains(outputRetriever.result()))
|
if (params.allowedResults.contains(process->result()))
|
||||||
data = params.parser(outputRetriever.cleanedStdOut());
|
data = params.parser(process->cleanedStdOut());
|
||||||
else if (params.errorHandler)
|
else if (params.errorHandler)
|
||||||
params.errorHandler(outputRetriever);
|
params.errorHandler(*process);
|
||||||
QMutexLocker<QMutex> cacheLocker(&m_cacheMutex);
|
QMutexLocker<QMutex> cacheLocker(&m_cacheMutex);
|
||||||
m_cache.insert(key, std::make_pair(data, exeTimestamp));
|
m_cache.insert(cacheKey, std::make_pair(data, exeTimestamp));
|
||||||
|
if (params.callback) {
|
||||||
|
params.callback(data);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
#include <QFileDialog>
|
#include <QFileDialog>
|
||||||
#include <QFuture>
|
#include <QFuture>
|
||||||
#include <QGuiApplication>
|
#include <QGuiApplication>
|
||||||
|
#include <QHelpEvent>
|
||||||
#include <QHBoxLayout>
|
#include <QHBoxLayout>
|
||||||
#include <QMenu>
|
#include <QMenu>
|
||||||
#include <QPushButton>
|
#include <QPushButton>
|
||||||
@@ -85,8 +86,6 @@ public:
|
|||||||
QStringList arguments() const { return m_arguments; }
|
QStringList arguments() const { return m_arguments; }
|
||||||
void setArguments(const QStringList &arguments) { m_arguments = arguments; }
|
void setArguments(const QStringList &arguments) { m_arguments = arguments; }
|
||||||
|
|
||||||
static QString toolVersion(const CommandLine &cmd);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Extension point for concatenating existing tooltips.
|
// Extension point for concatenating existing tooltips.
|
||||||
virtual QString defaultToolTip() const { return QString(); }
|
virtual QString defaultToolTip() const { return QString(); }
|
||||||
@@ -108,35 +107,41 @@ bool BinaryVersionToolTipEventFilter::eventFilter(QObject *o, QEvent *e)
|
|||||||
QTC_ASSERT(le, return false);
|
QTC_ASSERT(le, return false);
|
||||||
|
|
||||||
const QString binary = le->text();
|
const QString binary = le->text();
|
||||||
if (!binary.isEmpty()) {
|
DataFromProcess<QString>::Parameters params(CommandLine(FilePath::fromString(
|
||||||
const QString version = BinaryVersionToolTipEventFilter::toolVersion(
|
QDir::cleanPath(binary)),
|
||||||
CommandLine(FilePath::fromString(QDir::cleanPath(binary)), m_arguments));
|
m_arguments),
|
||||||
if (!version.isEmpty()) {
|
[](const QString &output) { return output; });
|
||||||
|
params.callback = [binary, self = QPointer(this),
|
||||||
|
le = QPointer(le)](const std::optional<QString> &version) {
|
||||||
|
if (!self || !le)
|
||||||
|
return;
|
||||||
|
if (!version || version->isEmpty())
|
||||||
|
return;
|
||||||
|
if (binary != le->text())
|
||||||
|
return;
|
||||||
|
|
||||||
// Concatenate tooltips.
|
// Concatenate tooltips.
|
||||||
QString tooltip = "<html><head/><body>";
|
QString tooltip = "<html><head/><body>";
|
||||||
const QString defaultValue = defaultToolTip();
|
const QString defaultValue = self->defaultToolTip();
|
||||||
if (!defaultValue.isEmpty()) {
|
if (!defaultValue.isEmpty()) {
|
||||||
tooltip += "<p>";
|
tooltip += "<p>";
|
||||||
tooltip += defaultValue;
|
tooltip += defaultValue;
|
||||||
tooltip += "</p>";
|
tooltip += "</p>";
|
||||||
}
|
}
|
||||||
tooltip += "<pre>";
|
tooltip += "<pre>";
|
||||||
tooltip += version;
|
tooltip += *version;
|
||||||
tooltip += "</pre><body></html>";
|
tooltip += "</pre><body></html>";
|
||||||
le->setToolTip(tooltip);
|
le->setToolTip(tooltip);
|
||||||
|
if (QRect(le->mapToGlobal(QPoint(0, 0)), le->size()).contains(QCursor::pos())) {
|
||||||
|
QCoreApplication::postEvent(le,
|
||||||
|
new QHelpEvent(QEvent::ToolTip,
|
||||||
|
le->mapFromGlobal(QCursor::pos()),
|
||||||
|
QCursor::pos()));
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
return false;
|
DataFromProcess<QString>::provideData(params);
|
||||||
}
|
|
||||||
|
|
||||||
QString BinaryVersionToolTipEventFilter::toolVersion(const CommandLine &cmd)
|
return false;
|
||||||
{
|
|
||||||
DataFromProcess<QString>::Parameters params(cmd, [](const QString &output) { return output; });
|
|
||||||
using namespace std::chrono_literals;
|
|
||||||
params.timeout = 1s;
|
|
||||||
if (const auto version = DataFromProcess<QString>::getData(params))
|
|
||||||
return *version;
|
|
||||||
return {};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extends BinaryVersionToolTipEventFilter to prepend the existing pathchooser
|
// Extends BinaryVersionToolTipEventFilter to prepend the existing pathchooser
|
||||||
@@ -722,11 +727,6 @@ FancyLineEdit *PathChooser::lineEdit() const
|
|||||||
return d->m_lineEdit;
|
return d->m_lineEdit;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString PathChooser::toolVersion(const CommandLine &cmd)
|
|
||||||
{
|
|
||||||
return BinaryVersionToolTipEventFilter::toolVersion(cmd);
|
|
||||||
}
|
|
||||||
|
|
||||||
void PathChooser::installLineEditVersionToolTip(QLineEdit *le, const QStringList &arguments)
|
void PathChooser::installLineEditVersionToolTip(QLineEdit *le, const QStringList &arguments)
|
||||||
{
|
{
|
||||||
auto ef = new BinaryVersionToolTipEventFilter(le);
|
auto ef = new BinaryVersionToolTipEventFilter(le);
|
||||||
|
|||||||
@@ -99,8 +99,6 @@ public:
|
|||||||
QStringList commandVersionArguments() const;
|
QStringList commandVersionArguments() const;
|
||||||
void setCommandVersionArguments(const QStringList &arguments);
|
void setCommandVersionArguments(const QStringList &arguments);
|
||||||
|
|
||||||
// Utility to run a tool and return its stdout.
|
|
||||||
static QString toolVersion(const CommandLine &cmd);
|
|
||||||
// Install a tooltip on lineedits used for binaries showing the version.
|
// Install a tooltip on lineedits used for binaries showing the version.
|
||||||
static void installLineEditVersionToolTip(QLineEdit *le, const QStringList &arguments);
|
static void installLineEditVersionToolTip(QLineEdit *le, const QStringList &arguments);
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
#include "gerritplugin.h"
|
#include "gerritplugin.h"
|
||||||
|
|
||||||
#include <utils/commandline.h>
|
#include <utils/commandline.h>
|
||||||
|
#include <utils/datafromprocess.h>
|
||||||
#include <utils/environment.h>
|
#include <utils/environment.h>
|
||||||
#include <utils/hostosinfo.h>
|
#include <utils/hostosinfo.h>
|
||||||
#include <utils/pathchooser.h>
|
#include <utils/pathchooser.h>
|
||||||
@@ -70,8 +71,12 @@ void GerritParameters::setPortFlagBySshType()
|
|||||||
{
|
{
|
||||||
bool isPlink = false;
|
bool isPlink = false;
|
||||||
if (!ssh.isEmpty()) {
|
if (!ssh.isEmpty()) {
|
||||||
const QString version = PathChooser::toolVersion({ssh, {"-V"}});
|
DataFromProcess<QString>::Parameters params({ssh, {"-V"}},
|
||||||
isPlink = version.contains("plink", Qt::CaseInsensitive);
|
[](const QString &output) { return output; });
|
||||||
|
using namespace std::chrono_literals;
|
||||||
|
params.timeout = 1s;
|
||||||
|
if (const auto version = DataFromProcess<QString>::getData(params))
|
||||||
|
isPlink = version->contains("plink", Qt::CaseInsensitive);
|
||||||
}
|
}
|
||||||
portFlag = QLatin1String(isPlink ? "-P" : defaultPortFlag);
|
portFlag = QLatin1String(isPlink ? "-P" : defaultPortFlag);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user