forked from qt-creator/qt-creator
Get rid of OutputProxy in ShellCommand
Before we have used the OutputProxyFactory, which was called in the non-gui thread. The factory, when run, created the connection between the thread and outside world (e.g. VersionControl output window). Instead of setting the factory we provide a set of virtual functions called directly from non-gui threads. We also provide overrides for them in VcsCommand class. Their implementation safely redirects the calls directly to the VcsOutputWindow through the QMetaObject::invokeMethod() with auto connection as a default. Task-number: QTCREATORBUG-25744 Change-Id: I09f2da278003b71095e953a51499a5513cb8f03f Reviewed-by: Eike Ziller <eike.ziller@qt.io>
This commit is contained in:
@@ -85,7 +85,6 @@ public:
|
|||||||
|
|
||||||
~ShellCommandPrivate() { delete m_progressParser; }
|
~ShellCommandPrivate() { delete m_progressParser; }
|
||||||
|
|
||||||
std::function<OutputProxy *()> m_proxyFactory = []() { return new OutputProxy; };
|
|
||||||
QString m_displayName;
|
QString m_displayName;
|
||||||
const QString m_defaultWorkingDirectory;
|
const QString m_defaultWorkingDirectory;
|
||||||
const Environment m_environment;
|
const Environment m_environment;
|
||||||
@@ -318,33 +317,30 @@ void ShellCommand::runCommand(SynchronousProcess &proc,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
QSharedPointer<OutputProxy> proxy(d->m_proxyFactory());
|
|
||||||
|
|
||||||
if (!(d->m_flags & SuppressCommandLogging))
|
if (!(d->m_flags & SuppressCommandLogging))
|
||||||
emit proxy->appendCommand(dir, command);
|
appendCommand(dir, command);
|
||||||
|
|
||||||
proc.setCommand(command);
|
proc.setCommand(command);
|
||||||
if ((d->m_flags & FullySynchronously)
|
if ((d->m_flags & FullySynchronously)
|
||||||
|| (!(d->m_flags & NoFullySync)
|
|| (!(d->m_flags & NoFullySync)
|
||||||
&& QThread::currentThread() == QCoreApplication::instance()->thread())) {
|
&& QThread::currentThread() == QCoreApplication::instance()->thread())) {
|
||||||
runFullySynchronous(proc, proxy, dir);
|
runFullySynchronous(proc, dir);
|
||||||
} else {
|
} else {
|
||||||
runSynchronous(proc, proxy, dir);
|
runSynchronous(proc, dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!d->m_aborted) {
|
if (!d->m_aborted) {
|
||||||
// Success/Fail message in appropriate window?
|
// Success/Fail message in appropriate window?
|
||||||
if (proc.result() == QtcProcess::FinishedWithSuccess) {
|
if (proc.result() == QtcProcess::FinishedWithSuccess) {
|
||||||
if (d->m_flags & ShowSuccessMessage)
|
if (d->m_flags & ShowSuccessMessage)
|
||||||
emit proxy->appendMessage(proc.exitMessage());
|
appendMessage(proc.exitMessage());
|
||||||
} else if (!(d->m_flags & SuppressFailMessage)) {
|
} else if (!(d->m_flags & SuppressFailMessage)) {
|
||||||
emit proxy->appendError(proc.exitMessage());
|
appendError(proc.exitMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ShellCommand::runFullySynchronous(SynchronousProcess &process,
|
void ShellCommand::runFullySynchronous(SynchronousProcess &process,
|
||||||
QSharedPointer<OutputProxy> proxy,
|
|
||||||
const QString &workingDirectory)
|
const QString &workingDirectory)
|
||||||
{
|
{
|
||||||
// Set up process
|
// Set up process
|
||||||
@@ -364,20 +360,19 @@ void ShellCommand::runFullySynchronous(SynchronousProcess &process,
|
|||||||
if (!d->m_aborted) {
|
if (!d->m_aborted) {
|
||||||
const QString stdErr = process.stdErr();
|
const QString stdErr = process.stdErr();
|
||||||
if (!stdErr.isEmpty() && !(d->m_flags & SuppressStdErr))
|
if (!stdErr.isEmpty() && !(d->m_flags & SuppressStdErr))
|
||||||
emit proxy->append(stdErr);
|
append(stdErr);
|
||||||
|
|
||||||
const QString stdOut = process.stdOut();
|
const QString stdOut = process.stdOut();
|
||||||
if (!stdOut.isEmpty() && d->m_flags & ShowStdOut) {
|
if (!stdOut.isEmpty() && d->m_flags & ShowStdOut) {
|
||||||
if (d->m_flags & SilentOutput)
|
if (d->m_flags & SilentOutput)
|
||||||
emit proxy->appendSilently(stdOut);
|
appendSilently(stdOut);
|
||||||
else
|
else
|
||||||
emit proxy->append(stdOut);
|
append(stdOut);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ShellCommand::runSynchronous(SynchronousProcess &process,
|
void ShellCommand::runSynchronous(SynchronousProcess &process,
|
||||||
QSharedPointer<OutputProxy> proxy,
|
|
||||||
const QString &workingDirectory)
|
const QString &workingDirectory)
|
||||||
{
|
{
|
||||||
connect(this, &ShellCommand::terminate, &process, &SynchronousProcess::stopProcess);
|
connect(this, &ShellCommand::terminate, &process, &SynchronousProcess::stopProcess);
|
||||||
@@ -393,11 +388,11 @@ void ShellCommand::runSynchronous(SynchronousProcess &process,
|
|||||||
if (d->m_flags & MergeOutputChannels) {
|
if (d->m_flags & MergeOutputChannels) {
|
||||||
process.setProcessChannelMode(QProcess::MergedChannels);
|
process.setProcessChannelMode(QProcess::MergedChannels);
|
||||||
} else if (d->m_progressiveOutput || !(d->m_flags & SuppressStdErr)) {
|
} else if (d->m_progressiveOutput || !(d->m_flags & SuppressStdErr)) {
|
||||||
process.setStdErrCallback([this, proxy](const QString &text) {
|
process.setStdErrCallback([this](const QString &text) {
|
||||||
if (d->m_progressParser)
|
if (d->m_progressParser)
|
||||||
d->m_progressParser->parseProgress(text);
|
d->m_progressParser->parseProgress(text);
|
||||||
if (!(d->m_flags & SuppressStdErr))
|
if (!(d->m_flags & SuppressStdErr))
|
||||||
emit proxy->appendError(text);
|
appendError(text);
|
||||||
if (d->m_progressiveOutput)
|
if (d->m_progressiveOutput)
|
||||||
emit stdErrText(text);
|
emit stdErrText(text);
|
||||||
});
|
});
|
||||||
@@ -405,11 +400,11 @@ void ShellCommand::runSynchronous(SynchronousProcess &process,
|
|||||||
|
|
||||||
// connect stdout to the output window if desired
|
// connect stdout to the output window if desired
|
||||||
if (d->m_progressParser || d->m_progressiveOutput || (d->m_flags & ShowStdOut)) {
|
if (d->m_progressParser || d->m_progressiveOutput || (d->m_flags & ShowStdOut)) {
|
||||||
process.setStdOutCallback([this, proxy](const QString &text) {
|
process.setStdOutCallback([this](const QString &text) {
|
||||||
if (d->m_progressParser)
|
if (d->m_progressParser)
|
||||||
d->m_progressParser->parseProgress(text);
|
d->m_progressParser->parseProgress(text);
|
||||||
if (d->m_flags & ShowStdOut)
|
if (d->m_flags & ShowStdOut)
|
||||||
emit proxy->append(text);
|
append(text);
|
||||||
if (d->m_progressiveOutput) {
|
if (d->m_progressiveOutput) {
|
||||||
emit stdOutText(text);
|
emit stdOutText(text);
|
||||||
d->m_hadOutput = true;
|
d->m_hadOutput = true;
|
||||||
@@ -463,11 +458,6 @@ void ShellCommand::setProgressiveOutput(bool progressive)
|
|||||||
d->m_progressiveOutput = progressive;
|
d->m_progressiveOutput = progressive;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ShellCommand::setOutputProxyFactory(const std::function<OutputProxy *()> &factory)
|
|
||||||
{
|
|
||||||
d->m_proxyFactory = factory;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ShellCommand::setDisableUnixTerminal()
|
void ShellCommand::setDisableUnixTerminal()
|
||||||
{
|
{
|
||||||
d->m_disableUnixTerminal = true;
|
d->m_disableUnixTerminal = true;
|
||||||
|
@@ -29,8 +29,6 @@
|
|||||||
|
|
||||||
#include "qtcprocess.h"
|
#include "qtcprocess.h"
|
||||||
|
|
||||||
#include <functional>
|
|
||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
QT_BEGIN_NAMESPACE
|
||||||
class QMutex;
|
class QMutex;
|
||||||
class QVariant;
|
class QVariant;
|
||||||
@@ -62,22 +60,6 @@ private:
|
|||||||
friend class ShellCommand;
|
friend class ShellCommand;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Users of this class can either be in the GUI thread or in other threads.
|
|
||||||
// Use Qt::AutoConnection to always append in the GUI thread (directly or queued)
|
|
||||||
class QTCREATOR_UTILS_EXPORT OutputProxy : public QObject
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
friend class ShellCommand;
|
|
||||||
|
|
||||||
signals:
|
|
||||||
void append(const QString &text);
|
|
||||||
void appendSilently(const QString &text);
|
|
||||||
void appendError(const QString &text);
|
|
||||||
void appendCommand(const QString &workingDirectory, const Utils::CommandLine &command);
|
|
||||||
void appendMessage(const QString &text);
|
|
||||||
};
|
|
||||||
|
|
||||||
class QTCREATOR_UTILS_EXPORT ShellCommand : public QObject
|
class QTCREATOR_UTILS_EXPORT ShellCommand : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
@@ -136,7 +118,6 @@ public:
|
|||||||
bool hasProgressParser() const;
|
bool hasProgressParser() const;
|
||||||
void setProgressiveOutput(bool progressive);
|
void setProgressiveOutput(bool progressive);
|
||||||
|
|
||||||
void setOutputProxyFactory(const std::function<OutputProxy *()> &factory);
|
|
||||||
void setDisableUnixTerminal();
|
void setDisableUnixTerminal();
|
||||||
|
|
||||||
// This is called once per job in a thread.
|
// This is called once per job in a thread.
|
||||||
@@ -162,16 +143,24 @@ protected:
|
|||||||
int timeoutS() const;
|
int timeoutS() const;
|
||||||
QString workDirectory(const QString &wd) const;
|
QString workDirectory(const QString &wd) const;
|
||||||
|
|
||||||
|
// Below methods are called directly from other threads
|
||||||
|
virtual void append(const QString &text) { Q_UNUSED(text) }
|
||||||
|
virtual void appendSilently(const QString &text) { Q_UNUSED(text) }
|
||||||
|
virtual void appendError(const QString &text) { Q_UNUSED(text) }
|
||||||
|
virtual void appendCommand(const QString &workingDirectory, const Utils::CommandLine &command) {
|
||||||
|
Q_UNUSED(workingDirectory)
|
||||||
|
Q_UNUSED(command)
|
||||||
|
}
|
||||||
|
virtual void appendMessage(const QString &text) { Q_UNUSED(text) }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void run(QFutureInterface<void> &future);
|
void run(QFutureInterface<void> &future);
|
||||||
|
|
||||||
// Run without a event loop in fully blocking mode. No signals will be delivered.
|
// Run without a event loop in fully blocking mode. No signals will be delivered.
|
||||||
void runFullySynchronous(SynchronousProcess &proc,
|
void runFullySynchronous(SynchronousProcess &proc,
|
||||||
QSharedPointer<OutputProxy> proxy,
|
|
||||||
const QString &workingDirectory);
|
const QString &workingDirectory);
|
||||||
// Run with an event loop. Signals will be delivered.
|
// Run with an event loop. Signals will be delivered.
|
||||||
void runSynchronous(SynchronousProcess &proc,
|
void runSynchronous(SynchronousProcess &proc,
|
||||||
QSharedPointer<OutputProxy> proxy,
|
|
||||||
const QString &workingDirectory);
|
const QString &workingDirectory);
|
||||||
|
|
||||||
class Internal::ShellCommandPrivate *const d;
|
class Internal::ShellCommandPrivate *const d;
|
||||||
|
@@ -42,23 +42,8 @@ VcsCommand::VcsCommand(const QString &workingDirectory, const Environment &envir
|
|||||||
{
|
{
|
||||||
VcsOutputWindow::setRepository(workingDirectory);
|
VcsOutputWindow::setRepository(workingDirectory);
|
||||||
setDisableUnixTerminal();
|
setDisableUnixTerminal();
|
||||||
setOutputProxyFactory([this] {
|
m_outputWindow = VcsOutputWindow::instance();
|
||||||
auto proxy = new OutputProxy;
|
|
||||||
VcsOutputWindow *outputWindow = VcsOutputWindow::instance();
|
|
||||||
|
|
||||||
connect(proxy, &OutputProxy::append,
|
|
||||||
outputWindow, [](const QString &txt) { VcsOutputWindow::append(txt); });
|
|
||||||
connect(proxy, &OutputProxy::appendSilently,
|
|
||||||
outputWindow, &VcsOutputWindow::appendSilently);
|
|
||||||
connect(proxy, &OutputProxy::appendError,
|
|
||||||
outputWindow, &VcsOutputWindow::appendError);
|
|
||||||
connect(proxy, &OutputProxy::appendCommand,
|
|
||||||
outputWindow, &VcsOutputWindow::appendCommand);
|
|
||||||
connect(proxy, &OutputProxy::appendMessage,
|
|
||||||
outputWindow, &VcsOutputWindow::appendMessage);
|
|
||||||
|
|
||||||
return proxy;
|
|
||||||
});
|
|
||||||
connect(this, &VcsCommand::started, this, [this] {
|
connect(this, &VcsCommand::started, this, [this] {
|
||||||
if (flags() & ExpectRepoChanges)
|
if (flags() & ExpectRepoChanges)
|
||||||
Utils::GlobalFileChangeBlocker::instance()->forceBlocked(true);
|
Utils::GlobalFileChangeBlocker::instance()->forceBlocked(true);
|
||||||
@@ -84,6 +69,41 @@ void VcsCommand::runCommand(SynchronousProcess &proc,
|
|||||||
emitRepositoryChanged(workingDirectory);
|
emitRepositoryChanged(workingDirectory);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void VcsCommand::append(const QString &text)
|
||||||
|
{
|
||||||
|
QMetaObject::invokeMethod(m_outputWindow, [this, text] {
|
||||||
|
m_outputWindow->append(text);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void VcsCommand::appendSilently(const QString &text)
|
||||||
|
{
|
||||||
|
QMetaObject::invokeMethod(m_outputWindow, [this, text] {
|
||||||
|
m_outputWindow->appendSilently(text);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void VcsCommand::appendError(const QString &text)
|
||||||
|
{
|
||||||
|
QMetaObject::invokeMethod(m_outputWindow, [this, text] {
|
||||||
|
m_outputWindow->appendError(text);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void VcsCommand::appendCommand(const QString &workingDirectory, const Utils::CommandLine &command)
|
||||||
|
{
|
||||||
|
QMetaObject::invokeMethod(m_outputWindow, [this, workingDirectory, command] {
|
||||||
|
m_outputWindow->appendCommand(workingDirectory, command);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void VcsCommand::appendMessage(const QString &text)
|
||||||
|
{
|
||||||
|
QMetaObject::invokeMethod(m_outputWindow, [this, text] {
|
||||||
|
m_outputWindow->appendMessage(text);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
void VcsCommand::emitRepositoryChanged(const QString &workingDirectory)
|
void VcsCommand::emitRepositoryChanged(const QString &workingDirectory)
|
||||||
{
|
{
|
||||||
if (m_preventRepositoryChanged || !(flags() & VcsCommand::ExpectRepoChanges))
|
if (m_preventRepositoryChanged || !(flags() & VcsCommand::ExpectRepoChanges))
|
||||||
|
@@ -31,6 +31,8 @@
|
|||||||
|
|
||||||
namespace VcsBase {
|
namespace VcsBase {
|
||||||
|
|
||||||
|
class VcsOutputWindow;
|
||||||
|
|
||||||
class VCSBASE_EXPORT VcsCommand : public Core::ShellCommand
|
class VCSBASE_EXPORT VcsCommand : public Core::ShellCommand
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
@@ -49,12 +51,20 @@ public:
|
|||||||
const Utils::CommandLine &command,
|
const Utils::CommandLine &command,
|
||||||
const QString &workDirectory = {}) override;
|
const QString &workDirectory = {}) override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void append(const QString &text) override;
|
||||||
|
void appendSilently(const QString &text) override;
|
||||||
|
void appendError(const QString &text) override;
|
||||||
|
void appendCommand(const QString &workingDirectory, const Utils::CommandLine &command) override;
|
||||||
|
void appendMessage(const QString &text) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void emitRepositoryChanged(const QString &workingDirectory);
|
void emitRepositoryChanged(const QString &workingDirectory);
|
||||||
|
|
||||||
void coreAboutToClose() override;
|
void coreAboutToClose() override;
|
||||||
|
|
||||||
bool m_preventRepositoryChanged;
|
bool m_preventRepositoryChanged;
|
||||||
|
VcsOutputWindow *m_outputWindow = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace VcsBase
|
} // namespace VcsBase
|
||||||
|
Reference in New Issue
Block a user