forked from qt-creator/qt-creator
Utils: Merge {synchronous,qtc}process.{h,cpp} file pairs
Mechanical to prepare merging the actual classes. Adapting #includes. Change-Id: I77a2c28129287778bc870c30cb890cd26bc2e62b Reviewed-by: Christian Stenger <christian.stenger@qt.io>
This commit is contained in:
@@ -55,8 +55,8 @@
|
||||
#include <utils/hostosinfo.h>
|
||||
#include <utils/mimetypes/mimedatabase.h>
|
||||
#include <utils/qtcassert.h>
|
||||
#include <utils/qtcprocess.h>
|
||||
#include <utils/qtcsettings.h>
|
||||
#include <utils/synchronousprocess.h>
|
||||
|
||||
#ifdef WITH_TESTS
|
||||
#include <utils/hostosinfo.h>
|
||||
|
||||
@@ -151,7 +151,6 @@ add_qtc_library(Utils
|
||||
stringutils.cpp stringutils.h
|
||||
styledbar.cpp styledbar.h
|
||||
stylehelper.cpp stylehelper.h
|
||||
synchronousprocess.cpp synchronousprocess.h
|
||||
templateengine.cpp templateengine.h
|
||||
temporarydirectory.cpp temporarydirectory.h
|
||||
temporaryfile.cpp temporaryfile.h
|
||||
|
||||
@@ -25,7 +25,6 @@
|
||||
|
||||
#include "buildablehelperlibrary.h"
|
||||
#include "hostosinfo.h"
|
||||
#include "synchronousprocess.h"
|
||||
#include "qtcprocess.h"
|
||||
|
||||
#include <QDateTime>
|
||||
|
||||
@@ -26,11 +26,10 @@
|
||||
#include "pathchooser.h"
|
||||
|
||||
#include "environment.h"
|
||||
#include "qtcassert.h"
|
||||
#include "macroexpander.h"
|
||||
|
||||
#include "synchronousprocess.h"
|
||||
#include "hostosinfo.h"
|
||||
#include "macroexpander.h"
|
||||
#include "qtcassert.h"
|
||||
#include "qtcprocess.h"
|
||||
#include "theme/theme.h"
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
@@ -24,17 +24,22 @@
|
||||
****************************************************************************/
|
||||
|
||||
#include "qtcprocess.h"
|
||||
#include "stringutils.h"
|
||||
|
||||
#include <utils/qtcassert.h>
|
||||
#include <utils/hostosinfo.h>
|
||||
#include "stringutils.h"
|
||||
#include "executeondestruction.h"
|
||||
#include "hostosinfo.h"
|
||||
#include "qtcassert.h"
|
||||
#include "qtcprocess.h"
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QDir>
|
||||
#include <QDebug>
|
||||
#include <QDir>
|
||||
#include <QLoggingCategory>
|
||||
#include <QRegularExpression>
|
||||
#include <QStack>
|
||||
#include <QTextCodec>
|
||||
#include <QThread>
|
||||
#include <QTimer>
|
||||
|
||||
#ifdef QT_GUI_LIB
|
||||
// qmlpuppet does not use that.
|
||||
@@ -42,6 +47,10 @@
|
||||
#include <QMessageBox>
|
||||
#endif
|
||||
|
||||
#include <algorithm>
|
||||
#include <limits.h>
|
||||
#include <memory>
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
#define CALLBACK WINAPI
|
||||
#include <qt_windows.h>
|
||||
@@ -1845,4 +1854,575 @@ QString QtcProcess::locateBinary(const QString &binary)
|
||||
return locateBinary(QString::fromLocal8Bit(path), binary);
|
||||
}
|
||||
|
||||
|
||||
/*!
|
||||
\class Utils::SynchronousProcess
|
||||
|
||||
\brief The SynchronousProcess class runs a synchronous process in its own
|
||||
event loop that blocks only user input events. Thus, it allows for the GUI to
|
||||
repaint and append output to log windows.
|
||||
|
||||
The stdOut(), stdErr() signals are emitted unbuffered as the process
|
||||
writes them.
|
||||
|
||||
The stdOutBuffered(), stdErrBuffered() signals are emitted with complete
|
||||
lines based on the '\\n' marker if they are enabled using
|
||||
stdOutBufferedSignalsEnabled()/setStdErrBufferedSignalsEnabled().
|
||||
They would typically be used for log windows.
|
||||
|
||||
There is a timeout handling that takes effect after the last data have been
|
||||
read from stdout/stdin (as opposed to waitForFinished(), which measures time
|
||||
since it was invoked). It is thus also suitable for slow processes that
|
||||
continuously output data (like version system operations).
|
||||
|
||||
The property timeOutMessageBoxEnabled influences whether a message box is
|
||||
shown asking the user if they want to kill the process on timeout (default: false).
|
||||
|
||||
There are also static utility functions for dealing with fully synchronous
|
||||
processes, like reading the output with correct timeout handling.
|
||||
|
||||
Caution: This class should NOT be used if there is a chance that the process
|
||||
triggers opening dialog boxes (for example, by file watchers triggering),
|
||||
as this will cause event loop problems.
|
||||
*/
|
||||
|
||||
enum { debug = 0 };
|
||||
enum { syncDebug = 0 };
|
||||
|
||||
enum { defaultMaxHangTimerCount = 10 };
|
||||
|
||||
static Q_LOGGING_CATEGORY(processLog, "qtc.utils.synchronousprocess", QtWarningMsg);
|
||||
|
||||
// ----------- SynchronousProcessResponse
|
||||
void SynchronousProcessResponse::clear()
|
||||
{
|
||||
result = StartFailed;
|
||||
exitCode = -1;
|
||||
rawStdOut.clear();
|
||||
rawStdErr.clear();
|
||||
}
|
||||
|
||||
QString SynchronousProcessResponse::exitMessage(const QString &binary, int timeoutS) const
|
||||
{
|
||||
switch (result) {
|
||||
case Finished:
|
||||
return SynchronousProcess::tr("The command \"%1\" finished successfully.").arg(QDir::toNativeSeparators(binary));
|
||||
case FinishedError:
|
||||
return SynchronousProcess::tr("The command \"%1\" terminated with exit code %2.").arg(QDir::toNativeSeparators(binary)).arg(exitCode);
|
||||
case TerminatedAbnormally:
|
||||
return SynchronousProcess::tr("The command \"%1\" terminated abnormally.").arg(QDir::toNativeSeparators(binary));
|
||||
case StartFailed:
|
||||
return SynchronousProcess::tr("The command \"%1\" could not be started.").arg(QDir::toNativeSeparators(binary));
|
||||
case Hang:
|
||||
return SynchronousProcess::tr("The command \"%1\" did not respond within the timeout limit (%2 s).")
|
||||
.arg(QDir::toNativeSeparators(binary)).arg(timeoutS);
|
||||
}
|
||||
return QString();
|
||||
}
|
||||
|
||||
QByteArray SynchronousProcessResponse::allRawOutput() const
|
||||
{
|
||||
if (!rawStdOut.isEmpty() && !rawStdErr.isEmpty()) {
|
||||
QByteArray result = rawStdOut;
|
||||
if (!result.endsWith('\n'))
|
||||
result += '\n';
|
||||
result += rawStdErr;
|
||||
return result;
|
||||
}
|
||||
return !rawStdOut.isEmpty() ? rawStdOut : rawStdErr;
|
||||
}
|
||||
|
||||
QString SynchronousProcessResponse::allOutput() const
|
||||
{
|
||||
const QString out = stdOut();
|
||||
const QString err = stdErr();
|
||||
|
||||
if (!out.isEmpty() && !err.isEmpty()) {
|
||||
QString result = out;
|
||||
if (!result.endsWith('\n'))
|
||||
result += '\n';
|
||||
result += err;
|
||||
return result;
|
||||
}
|
||||
return !out.isEmpty() ? out : err;
|
||||
}
|
||||
|
||||
QString SynchronousProcessResponse::stdOut() const
|
||||
{
|
||||
return QtcProcess::normalizeNewlines(codec->toUnicode(rawStdOut));
|
||||
}
|
||||
|
||||
QString SynchronousProcessResponse::stdErr() const
|
||||
{
|
||||
return QtcProcess::normalizeNewlines(codec->toUnicode(rawStdErr));
|
||||
}
|
||||
|
||||
QTCREATOR_UTILS_EXPORT QDebug operator<<(QDebug str, const SynchronousProcessResponse& r)
|
||||
{
|
||||
QDebug nsp = str.nospace();
|
||||
nsp << "SynchronousProcessResponse: result=" << r.result << " ex=" << r.exitCode << '\n'
|
||||
<< r.rawStdOut.size() << " bytes stdout, stderr=" << r.rawStdErr << '\n';
|
||||
return str;
|
||||
}
|
||||
|
||||
SynchronousProcessResponse::Result defaultExitCodeInterpreter(int code)
|
||||
{
|
||||
return code ? SynchronousProcessResponse::FinishedError
|
||||
: SynchronousProcessResponse::Finished;
|
||||
}
|
||||
|
||||
// Data for one channel buffer (stderr/stdout)
|
||||
class ChannelBuffer : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
void clearForRun();
|
||||
|
||||
QString linesRead();
|
||||
void append(const QByteArray &text, bool emitSignals);
|
||||
|
||||
QByteArray rawData;
|
||||
QString incompleteLineBuffer; // lines not yet signaled
|
||||
QTextCodec *codec = nullptr; // Not owner
|
||||
std::unique_ptr<QTextCodec::ConverterState> codecState;
|
||||
int rawDataPos = 0;
|
||||
bool bufferedSignalsEnabled = false;
|
||||
bool firstBuffer = true;
|
||||
|
||||
signals:
|
||||
void outputBuffered(const QString &text, bool firstTime);
|
||||
};
|
||||
|
||||
void ChannelBuffer::clearForRun()
|
||||
{
|
||||
firstBuffer = true;
|
||||
rawDataPos = 0;
|
||||
rawData.clear();
|
||||
codecState.reset(new QTextCodec::ConverterState);
|
||||
incompleteLineBuffer.clear();
|
||||
}
|
||||
|
||||
/* Check for complete lines read from the device and return them, moving the
|
||||
* buffer position. */
|
||||
QString ChannelBuffer::linesRead()
|
||||
{
|
||||
// Convert and append the new input to the buffer of incomplete lines
|
||||
const char *start = rawData.constData() + rawDataPos;
|
||||
const int len = rawData.size() - rawDataPos;
|
||||
incompleteLineBuffer.append(codec->toUnicode(start, len, codecState.get()));
|
||||
rawDataPos = rawData.size();
|
||||
|
||||
// Any completed lines in the incompleteLineBuffer?
|
||||
const int lastLineIndex = qMax(incompleteLineBuffer.lastIndexOf('\n'),
|
||||
incompleteLineBuffer.lastIndexOf('\r'));
|
||||
if (lastLineIndex == -1)
|
||||
return QString();
|
||||
|
||||
// Get completed lines and remove them from the incompleteLinesBuffer:
|
||||
const QString lines = QtcProcess::normalizeNewlines(incompleteLineBuffer.left(lastLineIndex + 1));
|
||||
incompleteLineBuffer = incompleteLineBuffer.mid(lastLineIndex + 1);
|
||||
|
||||
return lines;
|
||||
}
|
||||
|
||||
void ChannelBuffer::append(const QByteArray &text, bool emitSignals)
|
||||
{
|
||||
if (text.isEmpty())
|
||||
return;
|
||||
rawData += text;
|
||||
if (!emitSignals)
|
||||
return;
|
||||
|
||||
// Buffered. Emit complete lines?
|
||||
if (bufferedSignalsEnabled) {
|
||||
const QString lines = linesRead();
|
||||
if (!lines.isEmpty()) {
|
||||
emit outputBuffered(lines, firstBuffer);
|
||||
firstBuffer = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ----------- SynchronousProcessPrivate
|
||||
class SynchronousProcessPrivate {
|
||||
public:
|
||||
void clearForRun();
|
||||
|
||||
QTextCodec *m_codec = QTextCodec::codecForLocale();
|
||||
QtcProcess m_process;
|
||||
QTimer m_timer;
|
||||
QEventLoop m_eventLoop;
|
||||
SynchronousProcessResponse m_result;
|
||||
FilePath m_binary;
|
||||
ChannelBuffer m_stdOut;
|
||||
ChannelBuffer m_stdErr;
|
||||
ExitCodeInterpreter m_exitCodeInterpreter = defaultExitCodeInterpreter;
|
||||
|
||||
int m_hangTimerCount = 0;
|
||||
int m_maxHangTimerCount = defaultMaxHangTimerCount;
|
||||
bool m_startFailure = false;
|
||||
bool m_timeOutMessageBoxEnabled = false;
|
||||
bool m_waitingForUser = false;
|
||||
};
|
||||
|
||||
void SynchronousProcessPrivate::clearForRun()
|
||||
{
|
||||
m_hangTimerCount = 0;
|
||||
m_stdOut.clearForRun();
|
||||
m_stdOut.codec = m_codec;
|
||||
m_stdErr.clearForRun();
|
||||
m_stdErr.codec = m_codec;
|
||||
m_result.clear();
|
||||
m_result.codec = m_codec;
|
||||
m_startFailure = false;
|
||||
m_binary = {};
|
||||
}
|
||||
|
||||
// ----------- SynchronousProcess
|
||||
SynchronousProcess::SynchronousProcess() :
|
||||
d(new SynchronousProcessPrivate)
|
||||
{
|
||||
d->m_timer.setInterval(1000);
|
||||
connect(&d->m_timer, &QTimer::timeout, this, &SynchronousProcess::slotTimeout);
|
||||
connect(&d->m_process, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished),
|
||||
this, &SynchronousProcess::finished);
|
||||
connect(&d->m_process, &QProcess::errorOccurred, this, &SynchronousProcess::error);
|
||||
connect(&d->m_process, &QProcess::readyReadStandardOutput,
|
||||
this, [this]() {
|
||||
d->m_hangTimerCount = 0;
|
||||
processStdOut(true);
|
||||
});
|
||||
connect(&d->m_process, &QProcess::readyReadStandardError,
|
||||
this, [this]() {
|
||||
d->m_hangTimerCount = 0;
|
||||
processStdErr(true);
|
||||
});
|
||||
connect(&d->m_stdOut, &ChannelBuffer::outputBuffered, this, &SynchronousProcess::stdOutBuffered);
|
||||
connect(&d->m_stdErr, &ChannelBuffer::outputBuffered, this, &SynchronousProcess::stdErrBuffered);
|
||||
}
|
||||
|
||||
SynchronousProcess::~SynchronousProcess()
|
||||
{
|
||||
disconnect(&d->m_timer, nullptr, this, nullptr);
|
||||
disconnect(&d->m_process, nullptr, this, nullptr);
|
||||
delete d;
|
||||
}
|
||||
|
||||
void SynchronousProcess::setTimeoutS(int timeoutS)
|
||||
{
|
||||
if (timeoutS > 0)
|
||||
d->m_maxHangTimerCount = qMax(2, timeoutS);
|
||||
else
|
||||
d->m_maxHangTimerCount = INT_MAX / 1000;
|
||||
}
|
||||
|
||||
int SynchronousProcess::timeoutS() const
|
||||
{
|
||||
return d->m_maxHangTimerCount == (INT_MAX / 1000) ? -1 : d->m_maxHangTimerCount;
|
||||
}
|
||||
|
||||
void SynchronousProcess::setCodec(QTextCodec *c)
|
||||
{
|
||||
QTC_ASSERT(c, return);
|
||||
d->m_codec = c;
|
||||
}
|
||||
|
||||
QTextCodec *SynchronousProcess::codec() const
|
||||
{
|
||||
return d->m_codec;
|
||||
}
|
||||
|
||||
bool SynchronousProcess::stdOutBufferedSignalsEnabled() const
|
||||
{
|
||||
return d->m_stdOut.bufferedSignalsEnabled;
|
||||
}
|
||||
|
||||
void SynchronousProcess::setStdOutBufferedSignalsEnabled(bool v)
|
||||
{
|
||||
d->m_stdOut.bufferedSignalsEnabled = v;
|
||||
}
|
||||
|
||||
bool SynchronousProcess::stdErrBufferedSignalsEnabled() const
|
||||
{
|
||||
return d->m_stdErr.bufferedSignalsEnabled;
|
||||
}
|
||||
|
||||
void SynchronousProcess::setStdErrBufferedSignalsEnabled(bool v)
|
||||
{
|
||||
d->m_stdErr.bufferedSignalsEnabled = v;
|
||||
}
|
||||
|
||||
Environment SynchronousProcess::environment() const
|
||||
{
|
||||
return d->m_process.environment();
|
||||
}
|
||||
|
||||
bool SynchronousProcess::timeOutMessageBoxEnabled() const
|
||||
{
|
||||
return d->m_timeOutMessageBoxEnabled;
|
||||
}
|
||||
|
||||
void SynchronousProcess::setTimeOutMessageBoxEnabled(bool v)
|
||||
{
|
||||
d->m_timeOutMessageBoxEnabled = v;
|
||||
}
|
||||
|
||||
void SynchronousProcess::setEnvironment(const Environment &e)
|
||||
{
|
||||
d->m_process.setEnvironment(Environment(e));
|
||||
}
|
||||
|
||||
void SynchronousProcess::setDisableUnixTerminal()
|
||||
{
|
||||
d->m_process.setDisableUnixTerminal();
|
||||
}
|
||||
|
||||
void SynchronousProcess::setExitCodeInterpreter(const ExitCodeInterpreter &interpreter)
|
||||
{
|
||||
QTC_ASSERT(interpreter, return);
|
||||
d->m_exitCodeInterpreter = interpreter;
|
||||
}
|
||||
|
||||
ExitCodeInterpreter SynchronousProcess::exitCodeInterpreter() const
|
||||
{
|
||||
return d->m_exitCodeInterpreter;
|
||||
}
|
||||
|
||||
void SynchronousProcess::setWorkingDirectory(const QString &workingDirectory)
|
||||
{
|
||||
d->m_process.setWorkingDirectory(workingDirectory);
|
||||
}
|
||||
|
||||
QString SynchronousProcess::workingDirectory() const
|
||||
{
|
||||
return d->m_process.workingDirectory();
|
||||
}
|
||||
|
||||
QProcess::ProcessChannelMode SynchronousProcess::processChannelMode () const
|
||||
{
|
||||
return d->m_process.processChannelMode();
|
||||
}
|
||||
|
||||
void SynchronousProcess::setProcessChannelMode(QProcess::ProcessChannelMode m)
|
||||
{
|
||||
d->m_process.setProcessChannelMode(m);
|
||||
}
|
||||
|
||||
#ifdef QT_GUI_LIB
|
||||
static bool isGuiThread()
|
||||
{
|
||||
return QThread::currentThread() == QCoreApplication::instance()->thread();
|
||||
}
|
||||
#endif
|
||||
|
||||
SynchronousProcessResponse SynchronousProcess::run(const CommandLine &cmd,
|
||||
const QByteArray &writeData)
|
||||
{
|
||||
// FIXME: Implement properly
|
||||
if (cmd.executable().needsDevice()) {
|
||||
QtcProcess proc;
|
||||
proc.setEnvironment(Environment(d->m_process.environment()));
|
||||
proc.setWorkingDirectory(d->m_process.workingDirectory());
|
||||
proc.setCommand(cmd);
|
||||
|
||||
// writeData ?
|
||||
proc.start();
|
||||
|
||||
proc.waitForFinished();
|
||||
|
||||
SynchronousProcessResponse res;
|
||||
res.result = SynchronousProcessResponse::Finished;
|
||||
res.exitCode = proc.exitCode();
|
||||
res.rawStdOut = proc.readAllStandardOutput();
|
||||
res.rawStdErr = proc.readAllStandardError();
|
||||
return res;
|
||||
};
|
||||
|
||||
qCDebug(processLog).noquote() << "Starting:" << cmd.toUserOutput();
|
||||
ExecuteOnDestruction logResult([this] {
|
||||
qCDebug(processLog) << d->m_result;
|
||||
});
|
||||
|
||||
d->clearForRun();
|
||||
|
||||
d->m_binary = cmd.executable();
|
||||
// using QProcess::start() and passing program, args and OpenMode results in a different
|
||||
// quoting of arguments than using QProcess::setArguments() beforehand and calling start()
|
||||
// only with the OpenMode
|
||||
d->m_process.setCommand(cmd);
|
||||
if (!writeData.isEmpty()) {
|
||||
connect(&d->m_process, &QProcess::started, this, [this, writeData] {
|
||||
d->m_process.write(writeData);
|
||||
d->m_process.closeWriteChannel();
|
||||
});
|
||||
}
|
||||
d->m_process.setOpenMode(writeData.isEmpty() ? QIODevice::ReadOnly : QIODevice::ReadWrite);
|
||||
d->m_process.start();
|
||||
|
||||
// On Windows, start failure is triggered immediately if the
|
||||
// executable cannot be found in the path. Do not start the
|
||||
// event loop in that case.
|
||||
if (!d->m_startFailure) {
|
||||
d->m_timer.start();
|
||||
#ifdef QT_GUI_LIB
|
||||
if (isGuiThread())
|
||||
QApplication::setOverrideCursor(Qt::WaitCursor);
|
||||
#endif
|
||||
d->m_eventLoop.exec(QEventLoop::ExcludeUserInputEvents);
|
||||
processStdOut(false);
|
||||
processStdErr(false);
|
||||
|
||||
d->m_result.rawStdOut = d->m_stdOut.rawData;
|
||||
d->m_result.rawStdErr = d->m_stdErr.rawData;
|
||||
|
||||
d->m_timer.stop();
|
||||
#ifdef QT_GUI_LIB
|
||||
if (isGuiThread())
|
||||
QApplication::restoreOverrideCursor();
|
||||
#endif
|
||||
}
|
||||
|
||||
return d->m_result;
|
||||
}
|
||||
|
||||
SynchronousProcessResponse SynchronousProcess::runBlocking(const CommandLine &cmd)
|
||||
{
|
||||
// FIXME: Implement properly
|
||||
if (cmd.executable().needsDevice()) {
|
||||
QtcProcess proc;
|
||||
proc.setEnvironment(Environment(d->m_process.environment()));
|
||||
proc.setWorkingDirectory(d->m_process.workingDirectory());
|
||||
proc.setCommand(cmd);
|
||||
|
||||
// writeData ?
|
||||
proc.start();
|
||||
|
||||
proc.waitForFinished();
|
||||
|
||||
SynchronousProcessResponse res;
|
||||
res.result = SynchronousProcessResponse::Finished;
|
||||
res.exitCode = proc.exitCode();
|
||||
res.rawStdOut = proc.readAllStandardOutput();
|
||||
res.rawStdErr = proc.readAllStandardError();
|
||||
return res;
|
||||
};
|
||||
|
||||
qCDebug(processLog).noquote() << "Starting blocking:" << cmd.toUserOutput();
|
||||
ExecuteOnDestruction logResult([this] {
|
||||
qCDebug(processLog) << d->m_result;
|
||||
});
|
||||
|
||||
d->clearForRun();
|
||||
|
||||
d->m_binary = cmd.executable();
|
||||
d->m_process.setOpenMode(QIODevice::ReadOnly);
|
||||
d->m_process.setCommand(cmd);
|
||||
d->m_process.start();
|
||||
if (!d->m_process.waitForStarted(d->m_maxHangTimerCount * 1000)) {
|
||||
d->m_result.result = SynchronousProcessResponse::StartFailed;
|
||||
return d->m_result;
|
||||
}
|
||||
d->m_process.closeWriteChannel();
|
||||
if (!d->m_process.waitForFinished(d->m_maxHangTimerCount * 1000)) {
|
||||
d->m_result.result = SynchronousProcessResponse::Hang;
|
||||
d->m_process.terminate();
|
||||
if (!d->m_process.waitForFinished(1000)) {
|
||||
d->m_process.kill();
|
||||
d->m_process.waitForFinished(1000);
|
||||
}
|
||||
}
|
||||
|
||||
if (d->m_process.state() != QProcess::NotRunning)
|
||||
return d->m_result;
|
||||
|
||||
d->m_result.exitCode = d->m_process.exitCode();
|
||||
if (d->m_result.result == SynchronousProcessResponse::StartFailed) {
|
||||
if (d->m_process.exitStatus() != QProcess::NormalExit)
|
||||
d->m_result.result = SynchronousProcessResponse::TerminatedAbnormally;
|
||||
else
|
||||
d->m_result.result = (exitCodeInterpreter())(d->m_result.exitCode);
|
||||
}
|
||||
processStdOut(false);
|
||||
processStdErr(false);
|
||||
|
||||
d->m_result.rawStdOut = d->m_stdOut.rawData;
|
||||
d->m_result.rawStdErr = d->m_stdErr.rawData;
|
||||
|
||||
return d->m_result;
|
||||
}
|
||||
|
||||
bool SynchronousProcess::terminate()
|
||||
{
|
||||
return d->m_process.stopProcess();
|
||||
}
|
||||
|
||||
void SynchronousProcess::slotTimeout()
|
||||
{
|
||||
if (!d->m_waitingForUser && (++d->m_hangTimerCount > d->m_maxHangTimerCount)) {
|
||||
if (debug)
|
||||
qDebug() << Q_FUNC_INFO << "HANG detected, killing";
|
||||
d->m_waitingForUser = true;
|
||||
const bool terminate = !d->m_timeOutMessageBoxEnabled || askToKill(d->m_binary.toString());
|
||||
d->m_waitingForUser = false;
|
||||
if (terminate) {
|
||||
d->m_process.stopProcess();
|
||||
d->m_result.result = SynchronousProcessResponse::Hang;
|
||||
} else {
|
||||
d->m_hangTimerCount = 0;
|
||||
}
|
||||
} else {
|
||||
if (debug)
|
||||
qDebug() << Q_FUNC_INFO << d->m_hangTimerCount;
|
||||
}
|
||||
}
|
||||
|
||||
void SynchronousProcess::finished(int exitCode, QProcess::ExitStatus e)
|
||||
{
|
||||
if (debug)
|
||||
qDebug() << Q_FUNC_INFO << exitCode << e;
|
||||
d->m_hangTimerCount = 0;
|
||||
|
||||
switch (e) {
|
||||
case QProcess::NormalExit:
|
||||
d->m_result.result = d->m_exitCodeInterpreter(exitCode);
|
||||
d->m_result.exitCode = exitCode;
|
||||
break;
|
||||
case QProcess::CrashExit:
|
||||
// Was hang detected before and killed?
|
||||
if (d->m_result.result != SynchronousProcessResponse::Hang)
|
||||
d->m_result.result = SynchronousProcessResponse::TerminatedAbnormally;
|
||||
d->m_result.exitCode = -1;
|
||||
break;
|
||||
}
|
||||
d->m_eventLoop.quit();
|
||||
}
|
||||
|
||||
void SynchronousProcess::error(QProcess::ProcessError e)
|
||||
{
|
||||
d->m_hangTimerCount = 0;
|
||||
if (debug)
|
||||
qDebug() << Q_FUNC_INFO << e;
|
||||
// Was hang detected before and killed?
|
||||
if (d->m_result.result != SynchronousProcessResponse::Hang)
|
||||
d->m_result.result = SynchronousProcessResponse::StartFailed;
|
||||
d->m_startFailure = true;
|
||||
d->m_eventLoop.quit();
|
||||
}
|
||||
|
||||
void SynchronousProcess::processStdOut(bool emitSignals)
|
||||
{
|
||||
// Handle binary data
|
||||
d->m_stdOut.append(d->m_process.readAllStandardOutput(), emitSignals);
|
||||
}
|
||||
|
||||
void SynchronousProcess::processStdErr(bool emitSignals)
|
||||
{
|
||||
// Handle binary data
|
||||
d->m_stdErr.append(d->m_process.readAllStandardError(), emitSignals);
|
||||
}
|
||||
|
||||
} // namespace Utils
|
||||
|
||||
#include "qtcprocess.moc"
|
||||
|
||||
|
||||
@@ -25,14 +25,61 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "utils_global.h"
|
||||
|
||||
#include "environment.h"
|
||||
|
||||
#include <QProcess>
|
||||
#include <QTextCodec>
|
||||
|
||||
#include <functional>
|
||||
|
||||
QT_FORWARD_DECLARE_CLASS(QDebug)
|
||||
|
||||
namespace Utils {
|
||||
|
||||
class AbstractMacroExpander;
|
||||
|
||||
namespace Internal { class QtcProcessPrivate; }
|
||||
class SynchronousProcessPrivate;
|
||||
class CommandLine;
|
||||
class Environment;
|
||||
|
||||
/* Result of SynchronousProcess execution */
|
||||
class QTCREATOR_UTILS_EXPORT SynchronousProcessResponse
|
||||
{
|
||||
public:
|
||||
enum Result {
|
||||
// Finished with return code 0
|
||||
Finished,
|
||||
// Finished with return code != 0
|
||||
FinishedError,
|
||||
// Process terminated abnormally (kill)
|
||||
TerminatedAbnormally,
|
||||
// Executable could not be started
|
||||
StartFailed,
|
||||
// Hang, no output after time out
|
||||
Hang };
|
||||
|
||||
void clear();
|
||||
|
||||
// Helper to format an exit message.
|
||||
QString exitMessage(const QString &binary, int timeoutS) const;
|
||||
|
||||
QByteArray allRawOutput() const;
|
||||
QString allOutput() const;
|
||||
|
||||
QString stdOut() const;
|
||||
QString stdErr() const;
|
||||
|
||||
Result result = StartFailed;
|
||||
int exitCode = -1;
|
||||
|
||||
QByteArray rawStdOut;
|
||||
QByteArray rawStdErr;
|
||||
QTextCodec *codec = QTextCodec::codecForLocale();
|
||||
};
|
||||
|
||||
|
||||
class QTCREATOR_UTILS_EXPORT QtcProcess : public QProcess
|
||||
{
|
||||
@@ -178,4 +225,70 @@ private:
|
||||
QProcessEnvironment processEnvironment() const = delete;
|
||||
};
|
||||
|
||||
QTCREATOR_UTILS_EXPORT QDebug operator<<(QDebug str, const SynchronousProcessResponse &);
|
||||
|
||||
using ExitCodeInterpreter = std::function<SynchronousProcessResponse::Result(int /*exitCode*/)>;
|
||||
QTCREATOR_UTILS_EXPORT SynchronousProcessResponse::Result defaultExitCodeInterpreter(int code);
|
||||
|
||||
class QTCREATOR_UTILS_EXPORT SynchronousProcess : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
SynchronousProcess();
|
||||
~SynchronousProcess() override;
|
||||
|
||||
/* Timeout for hanging processes (triggers after no more output
|
||||
* occurs on stderr/stdout). */
|
||||
void setTimeoutS(int timeoutS);
|
||||
int timeoutS() const;
|
||||
|
||||
void setCodec(QTextCodec *c);
|
||||
QTextCodec *codec() const;
|
||||
|
||||
QProcess::ProcessChannelMode processChannelMode () const;
|
||||
void setProcessChannelMode(QProcess::ProcessChannelMode m);
|
||||
|
||||
bool stdOutBufferedSignalsEnabled() const;
|
||||
void setStdOutBufferedSignalsEnabled(bool);
|
||||
|
||||
bool stdErrBufferedSignalsEnabled() const;
|
||||
void setStdErrBufferedSignalsEnabled(bool);
|
||||
|
||||
bool timeOutMessageBoxEnabled() const;
|
||||
void setTimeOutMessageBoxEnabled(bool);
|
||||
|
||||
Environment environment() const;
|
||||
void setEnvironment(const Environment &);
|
||||
|
||||
void setWorkingDirectory(const QString &workingDirectory);
|
||||
QString workingDirectory() const;
|
||||
|
||||
// Unix: Do not give the child process a terminal for input prompting.
|
||||
void setDisableUnixTerminal();
|
||||
|
||||
void setExitCodeInterpreter(const ExitCodeInterpreter &interpreter);
|
||||
ExitCodeInterpreter exitCodeInterpreter() const;
|
||||
|
||||
// Starts a nested event loop and runs the command
|
||||
SynchronousProcessResponse run(const CommandLine &cmd, const QByteArray &writeData = {});
|
||||
// Starts the command blocking the UI fully
|
||||
SynchronousProcessResponse runBlocking(const CommandLine &cmd);
|
||||
|
||||
signals:
|
||||
void stdOutBuffered(const QString &lines, bool firstTime);
|
||||
void stdErrBuffered(const QString &lines, bool firstTime);
|
||||
|
||||
public slots:
|
||||
bool terminate();
|
||||
|
||||
private:
|
||||
void slotTimeout();
|
||||
void finished(int exitCode, QProcess::ExitStatus e);
|
||||
void error(QProcess::ProcessError);
|
||||
void processStdOut(bool emitSignals);
|
||||
void processStdErr(bool emitSignals);
|
||||
|
||||
SynchronousProcessPrivate *d;
|
||||
};
|
||||
|
||||
} // namespace Utils
|
||||
|
||||
@@ -27,16 +27,13 @@
|
||||
|
||||
#include "utils_global.h"
|
||||
|
||||
#include "synchronousprocess.h"
|
||||
|
||||
#include <QObject>
|
||||
#include "qtcprocess.h"
|
||||
|
||||
#include <functional>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QMutex;
|
||||
class QVariant;
|
||||
class QProcessEnvironment;
|
||||
template <typename T>
|
||||
class QFutureInterface;
|
||||
template <typename T>
|
||||
@@ -45,7 +42,6 @@ QT_END_NAMESPACE
|
||||
|
||||
namespace Utils {
|
||||
|
||||
class CommandLine;
|
||||
namespace Internal { class ShellCommandPrivate; }
|
||||
|
||||
class QTCREATOR_UTILS_EXPORT ProgressParser
|
||||
|
||||
@@ -1,591 +0,0 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2016 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "synchronousprocess.h"
|
||||
#include "executeondestruction.h"
|
||||
#include "hostosinfo.h"
|
||||
#include "qtcassert.h"
|
||||
#include "qtcprocess.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QDir>
|
||||
#include <QLoggingCategory>
|
||||
#include <QMessageBox>
|
||||
#include <QTextCodec>
|
||||
#include <QThread>
|
||||
#include <QTimer>
|
||||
|
||||
#include <QApplication>
|
||||
|
||||
#include <algorithm>
|
||||
#include <limits.h>
|
||||
#include <memory>
|
||||
|
||||
/*!
|
||||
\class Utils::SynchronousProcess
|
||||
|
||||
\brief The SynchronousProcess class runs a synchronous process in its own
|
||||
event loop that blocks only user input events. Thus, it allows for the GUI to
|
||||
repaint and append output to log windows.
|
||||
|
||||
The stdOut(), stdErr() signals are emitted unbuffered as the process
|
||||
writes them.
|
||||
|
||||
The stdOutBuffered(), stdErrBuffered() signals are emitted with complete
|
||||
lines based on the '\\n' marker if they are enabled using
|
||||
stdOutBufferedSignalsEnabled()/setStdErrBufferedSignalsEnabled().
|
||||
They would typically be used for log windows.
|
||||
|
||||
There is a timeout handling that takes effect after the last data have been
|
||||
read from stdout/stdin (as opposed to waitForFinished(), which measures time
|
||||
since it was invoked). It is thus also suitable for slow processes that continously
|
||||
output data (like version system operations).
|
||||
|
||||
The property timeOutMessageBoxEnabled influences whether a message box is
|
||||
shown asking the user if they want to kill the process on timeout (default: false).
|
||||
|
||||
There are also static utility functions for dealing with fully synchronous
|
||||
processes, like reading the output with correct timeout handling.
|
||||
|
||||
Caution: This class should NOT be used if there is a chance that the process
|
||||
triggers opening dialog boxes (for example, by file watchers triggering),
|
||||
as this will cause event loop problems.
|
||||
*/
|
||||
|
||||
enum { debug = 0 };
|
||||
enum { syncDebug = 0 };
|
||||
|
||||
enum { defaultMaxHangTimerCount = 10 };
|
||||
|
||||
namespace Utils {
|
||||
|
||||
static Q_LOGGING_CATEGORY(processLog, "qtc.utils.synchronousprocess", QtWarningMsg);
|
||||
|
||||
// ----------- SynchronousProcessResponse
|
||||
void SynchronousProcessResponse::clear()
|
||||
{
|
||||
result = StartFailed;
|
||||
exitCode = -1;
|
||||
rawStdOut.clear();
|
||||
rawStdErr.clear();
|
||||
}
|
||||
|
||||
QString SynchronousProcessResponse::exitMessage(const QString &binary, int timeoutS) const
|
||||
{
|
||||
switch (result) {
|
||||
case Finished:
|
||||
return SynchronousProcess::tr("The command \"%1\" finished successfully.").arg(QDir::toNativeSeparators(binary));
|
||||
case FinishedError:
|
||||
return SynchronousProcess::tr("The command \"%1\" terminated with exit code %2.").arg(QDir::toNativeSeparators(binary)).arg(exitCode);
|
||||
case TerminatedAbnormally:
|
||||
return SynchronousProcess::tr("The command \"%1\" terminated abnormally.").arg(QDir::toNativeSeparators(binary));
|
||||
case StartFailed:
|
||||
return SynchronousProcess::tr("The command \"%1\" could not be started.").arg(QDir::toNativeSeparators(binary));
|
||||
case Hang:
|
||||
return SynchronousProcess::tr("The command \"%1\" did not respond within the timeout limit (%2 s).")
|
||||
.arg(QDir::toNativeSeparators(binary)).arg(timeoutS);
|
||||
}
|
||||
return QString();
|
||||
}
|
||||
|
||||
QByteArray SynchronousProcessResponse::allRawOutput() const
|
||||
{
|
||||
if (!rawStdOut.isEmpty() && !rawStdErr.isEmpty()) {
|
||||
QByteArray result = rawStdOut;
|
||||
if (!result.endsWith('\n'))
|
||||
result += '\n';
|
||||
result += rawStdErr;
|
||||
return result;
|
||||
}
|
||||
return !rawStdOut.isEmpty() ? rawStdOut : rawStdErr;
|
||||
}
|
||||
|
||||
QString SynchronousProcessResponse::allOutput() const
|
||||
{
|
||||
const QString out = stdOut();
|
||||
const QString err = stdErr();
|
||||
|
||||
if (!out.isEmpty() && !err.isEmpty()) {
|
||||
QString result = out;
|
||||
if (!result.endsWith('\n'))
|
||||
result += '\n';
|
||||
result += err;
|
||||
return result;
|
||||
}
|
||||
return !out.isEmpty() ? out : err;
|
||||
}
|
||||
|
||||
QString SynchronousProcessResponse::stdOut() const
|
||||
{
|
||||
return QtcProcess::normalizeNewlines(codec->toUnicode(rawStdOut));
|
||||
}
|
||||
|
||||
QString SynchronousProcessResponse::stdErr() const
|
||||
{
|
||||
return QtcProcess::normalizeNewlines(codec->toUnicode(rawStdErr));
|
||||
}
|
||||
|
||||
QTCREATOR_UTILS_EXPORT QDebug operator<<(QDebug str, const SynchronousProcessResponse& r)
|
||||
{
|
||||
QDebug nsp = str.nospace();
|
||||
nsp << "SynchronousProcessResponse: result=" << r.result << " ex=" << r.exitCode << '\n'
|
||||
<< r.rawStdOut.size() << " bytes stdout, stderr=" << r.rawStdErr << '\n';
|
||||
return str;
|
||||
}
|
||||
|
||||
SynchronousProcessResponse::Result defaultExitCodeInterpreter(int code)
|
||||
{
|
||||
return code ? SynchronousProcessResponse::FinishedError
|
||||
: SynchronousProcessResponse::Finished;
|
||||
}
|
||||
|
||||
// Data for one channel buffer (stderr/stdout)
|
||||
class ChannelBuffer : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
void clearForRun();
|
||||
|
||||
QString linesRead();
|
||||
void append(const QByteArray &text, bool emitSignals);
|
||||
|
||||
QByteArray rawData;
|
||||
QString incompleteLineBuffer; // lines not yet signaled
|
||||
QTextCodec *codec = nullptr; // Not owner
|
||||
std::unique_ptr<QTextCodec::ConverterState> codecState;
|
||||
int rawDataPos = 0;
|
||||
bool bufferedSignalsEnabled = false;
|
||||
bool firstBuffer = true;
|
||||
|
||||
signals:
|
||||
void outputBuffered(const QString &text, bool firstTime);
|
||||
};
|
||||
|
||||
void ChannelBuffer::clearForRun()
|
||||
{
|
||||
firstBuffer = true;
|
||||
rawDataPos = 0;
|
||||
rawData.clear();
|
||||
codecState.reset(new QTextCodec::ConverterState);
|
||||
incompleteLineBuffer.clear();
|
||||
}
|
||||
|
||||
/* Check for complete lines read from the device and return them, moving the
|
||||
* buffer position. */
|
||||
QString ChannelBuffer::linesRead()
|
||||
{
|
||||
// Convert and append the new input to the buffer of incomplete lines
|
||||
const char *start = rawData.constData() + rawDataPos;
|
||||
const int len = rawData.size() - rawDataPos;
|
||||
incompleteLineBuffer.append(codec->toUnicode(start, len, codecState.get()));
|
||||
rawDataPos = rawData.size();
|
||||
|
||||
// Any completed lines in the incompleteLineBuffer?
|
||||
const int lastLineIndex = qMax(incompleteLineBuffer.lastIndexOf('\n'),
|
||||
incompleteLineBuffer.lastIndexOf('\r'));
|
||||
if (lastLineIndex == -1)
|
||||
return QString();
|
||||
|
||||
// Get completed lines and remove them from the incompleteLinesBuffer:
|
||||
const QString lines = QtcProcess::normalizeNewlines(incompleteLineBuffer.left(lastLineIndex + 1));
|
||||
incompleteLineBuffer = incompleteLineBuffer.mid(lastLineIndex + 1);
|
||||
|
||||
return lines;
|
||||
}
|
||||
|
||||
void ChannelBuffer::append(const QByteArray &text, bool emitSignals)
|
||||
{
|
||||
if (text.isEmpty())
|
||||
return;
|
||||
rawData += text;
|
||||
if (!emitSignals)
|
||||
return;
|
||||
|
||||
// Buffered. Emit complete lines?
|
||||
if (bufferedSignalsEnabled) {
|
||||
const QString lines = linesRead();
|
||||
if (!lines.isEmpty()) {
|
||||
emit outputBuffered(lines, firstBuffer);
|
||||
firstBuffer = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ----------- SynchronousProcessPrivate
|
||||
class SynchronousProcessPrivate {
|
||||
public:
|
||||
void clearForRun();
|
||||
|
||||
QTextCodec *m_codec = QTextCodec::codecForLocale();
|
||||
QtcProcess m_process;
|
||||
QTimer m_timer;
|
||||
QEventLoop m_eventLoop;
|
||||
SynchronousProcessResponse m_result;
|
||||
FilePath m_binary;
|
||||
ChannelBuffer m_stdOut;
|
||||
ChannelBuffer m_stdErr;
|
||||
ExitCodeInterpreter m_exitCodeInterpreter = defaultExitCodeInterpreter;
|
||||
|
||||
int m_hangTimerCount = 0;
|
||||
int m_maxHangTimerCount = defaultMaxHangTimerCount;
|
||||
bool m_startFailure = false;
|
||||
bool m_timeOutMessageBoxEnabled = false;
|
||||
bool m_waitingForUser = false;
|
||||
};
|
||||
|
||||
void SynchronousProcessPrivate::clearForRun()
|
||||
{
|
||||
m_hangTimerCount = 0;
|
||||
m_stdOut.clearForRun();
|
||||
m_stdOut.codec = m_codec;
|
||||
m_stdErr.clearForRun();
|
||||
m_stdErr.codec = m_codec;
|
||||
m_result.clear();
|
||||
m_result.codec = m_codec;
|
||||
m_startFailure = false;
|
||||
m_binary = {};
|
||||
}
|
||||
|
||||
// ----------- SynchronousProcess
|
||||
SynchronousProcess::SynchronousProcess() :
|
||||
d(new SynchronousProcessPrivate)
|
||||
{
|
||||
d->m_timer.setInterval(1000);
|
||||
connect(&d->m_timer, &QTimer::timeout, this, &SynchronousProcess::slotTimeout);
|
||||
connect(&d->m_process, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished),
|
||||
this, &SynchronousProcess::finished);
|
||||
connect(&d->m_process, &QProcess::errorOccurred, this, &SynchronousProcess::error);
|
||||
connect(&d->m_process, &QProcess::readyReadStandardOutput,
|
||||
this, [this]() {
|
||||
d->m_hangTimerCount = 0;
|
||||
processStdOut(true);
|
||||
});
|
||||
connect(&d->m_process, &QProcess::readyReadStandardError,
|
||||
this, [this]() {
|
||||
d->m_hangTimerCount = 0;
|
||||
processStdErr(true);
|
||||
});
|
||||
connect(&d->m_stdOut, &ChannelBuffer::outputBuffered, this, &SynchronousProcess::stdOutBuffered);
|
||||
connect(&d->m_stdErr, &ChannelBuffer::outputBuffered, this, &SynchronousProcess::stdErrBuffered);
|
||||
}
|
||||
|
||||
SynchronousProcess::~SynchronousProcess()
|
||||
{
|
||||
disconnect(&d->m_timer, nullptr, this, nullptr);
|
||||
disconnect(&d->m_process, nullptr, this, nullptr);
|
||||
delete d;
|
||||
}
|
||||
|
||||
void SynchronousProcess::setTimeoutS(int timeoutS)
|
||||
{
|
||||
if (timeoutS > 0)
|
||||
d->m_maxHangTimerCount = qMax(2, timeoutS);
|
||||
else
|
||||
d->m_maxHangTimerCount = INT_MAX / 1000;
|
||||
}
|
||||
|
||||
int SynchronousProcess::timeoutS() const
|
||||
{
|
||||
return d->m_maxHangTimerCount == (INT_MAX / 1000) ? -1 : d->m_maxHangTimerCount;
|
||||
}
|
||||
|
||||
void SynchronousProcess::setCodec(QTextCodec *c)
|
||||
{
|
||||
QTC_ASSERT(c, return);
|
||||
d->m_codec = c;
|
||||
}
|
||||
|
||||
QTextCodec *SynchronousProcess::codec() const
|
||||
{
|
||||
return d->m_codec;
|
||||
}
|
||||
|
||||
bool SynchronousProcess::stdOutBufferedSignalsEnabled() const
|
||||
{
|
||||
return d->m_stdOut.bufferedSignalsEnabled;
|
||||
}
|
||||
|
||||
void SynchronousProcess::setStdOutBufferedSignalsEnabled(bool v)
|
||||
{
|
||||
d->m_stdOut.bufferedSignalsEnabled = v;
|
||||
}
|
||||
|
||||
bool SynchronousProcess::stdErrBufferedSignalsEnabled() const
|
||||
{
|
||||
return d->m_stdErr.bufferedSignalsEnabled;
|
||||
}
|
||||
|
||||
void SynchronousProcess::setStdErrBufferedSignalsEnabled(bool v)
|
||||
{
|
||||
d->m_stdErr.bufferedSignalsEnabled = v;
|
||||
}
|
||||
|
||||
Environment SynchronousProcess::environment() const
|
||||
{
|
||||
return d->m_process.environment();
|
||||
}
|
||||
|
||||
bool SynchronousProcess::timeOutMessageBoxEnabled() const
|
||||
{
|
||||
return d->m_timeOutMessageBoxEnabled;
|
||||
}
|
||||
|
||||
void SynchronousProcess::setTimeOutMessageBoxEnabled(bool v)
|
||||
{
|
||||
d->m_timeOutMessageBoxEnabled = v;
|
||||
}
|
||||
|
||||
void SynchronousProcess::setEnvironment(const Environment &e)
|
||||
{
|
||||
d->m_process.setEnvironment(Environment(e));
|
||||
}
|
||||
|
||||
void SynchronousProcess::setDisableUnixTerminal()
|
||||
{
|
||||
d->m_process.setDisableUnixTerminal();
|
||||
}
|
||||
|
||||
void SynchronousProcess::setExitCodeInterpreter(const ExitCodeInterpreter &interpreter)
|
||||
{
|
||||
QTC_ASSERT(interpreter, return);
|
||||
d->m_exitCodeInterpreter = interpreter;
|
||||
}
|
||||
|
||||
ExitCodeInterpreter SynchronousProcess::exitCodeInterpreter() const
|
||||
{
|
||||
return d->m_exitCodeInterpreter;
|
||||
}
|
||||
|
||||
void SynchronousProcess::setWorkingDirectory(const QString &workingDirectory)
|
||||
{
|
||||
d->m_process.setWorkingDirectory(workingDirectory);
|
||||
}
|
||||
|
||||
QString SynchronousProcess::workingDirectory() const
|
||||
{
|
||||
return d->m_process.workingDirectory();
|
||||
}
|
||||
|
||||
QProcess::ProcessChannelMode SynchronousProcess::processChannelMode () const
|
||||
{
|
||||
return d->m_process.processChannelMode();
|
||||
}
|
||||
|
||||
void SynchronousProcess::setProcessChannelMode(QProcess::ProcessChannelMode m)
|
||||
{
|
||||
d->m_process.setProcessChannelMode(m);
|
||||
}
|
||||
|
||||
static bool isGuiThread()
|
||||
{
|
||||
return QThread::currentThread() == QCoreApplication::instance()->thread();
|
||||
}
|
||||
|
||||
SynchronousProcessResponse SynchronousProcess::run(const CommandLine &cmd,
|
||||
const QByteArray &writeData)
|
||||
{
|
||||
qCDebug(processLog).noquote() << "Starting:" << cmd.toUserOutput();
|
||||
ExecuteOnDestruction logResult([this] {
|
||||
qCDebug(processLog) << d->m_result;
|
||||
});
|
||||
|
||||
d->clearForRun();
|
||||
|
||||
d->m_binary = cmd.executable();
|
||||
// using QProcess::start() and passing program, args and OpenMode results in a different
|
||||
// quoting of arguments than using QProcess::setArguments() beforehand and calling start()
|
||||
// only with the OpenMode
|
||||
d->m_process.setCommand(cmd);
|
||||
if (!writeData.isEmpty()) {
|
||||
connect(&d->m_process, &QProcess::started, this, [this, writeData] {
|
||||
d->m_process.write(writeData);
|
||||
d->m_process.closeWriteChannel();
|
||||
});
|
||||
}
|
||||
d->m_process.setOpenMode(writeData.isEmpty() ? QIODevice::ReadOnly : QIODevice::ReadWrite);
|
||||
d->m_process.start();
|
||||
|
||||
// On Windows, start failure is triggered immediately if the
|
||||
// executable cannot be found in the path. Do not start the
|
||||
// event loop in that case.
|
||||
if (!d->m_startFailure) {
|
||||
d->m_timer.start();
|
||||
if (isGuiThread())
|
||||
QApplication::setOverrideCursor(Qt::WaitCursor);
|
||||
d->m_eventLoop.exec(QEventLoop::ExcludeUserInputEvents);
|
||||
processStdOut(false);
|
||||
processStdErr(false);
|
||||
|
||||
d->m_result.rawStdOut = d->m_stdOut.rawData;
|
||||
d->m_result.rawStdErr = d->m_stdErr.rawData;
|
||||
|
||||
d->m_timer.stop();
|
||||
if (isGuiThread())
|
||||
QApplication::restoreOverrideCursor();
|
||||
}
|
||||
|
||||
return d->m_result;
|
||||
}
|
||||
|
||||
SynchronousProcessResponse SynchronousProcess::runBlocking(const CommandLine &cmd)
|
||||
{
|
||||
qCDebug(processLog).noquote() << "Starting blocking:" << cmd.toUserOutput();
|
||||
ExecuteOnDestruction logResult([this] {
|
||||
qCDebug(processLog) << d->m_result;
|
||||
});
|
||||
|
||||
d->clearForRun();
|
||||
|
||||
d->m_binary = cmd.executable();
|
||||
d->m_process.setOpenMode(QIODevice::ReadOnly);
|
||||
d->m_process.setCommand(cmd);
|
||||
d->m_process.start();
|
||||
if (!d->m_process.waitForStarted(d->m_maxHangTimerCount * 1000)) {
|
||||
d->m_result.result = SynchronousProcessResponse::StartFailed;
|
||||
return d->m_result;
|
||||
}
|
||||
d->m_process.closeWriteChannel();
|
||||
if (!d->m_process.waitForFinished(d->m_maxHangTimerCount * 1000)) {
|
||||
d->m_result.result = SynchronousProcessResponse::Hang;
|
||||
d->m_process.terminate();
|
||||
if (!d->m_process.waitForFinished(1000)) {
|
||||
d->m_process.kill();
|
||||
d->m_process.waitForFinished(1000);
|
||||
}
|
||||
}
|
||||
|
||||
if (d->m_process.state() != QProcess::NotRunning)
|
||||
return d->m_result;
|
||||
|
||||
d->m_result.exitCode = d->m_process.exitCode();
|
||||
if (d->m_result.result == SynchronousProcessResponse::StartFailed) {
|
||||
if (d->m_process.exitStatus() != QProcess::NormalExit)
|
||||
d->m_result.result = SynchronousProcessResponse::TerminatedAbnormally;
|
||||
else
|
||||
d->m_result.result = (exitCodeInterpreter())(d->m_result.exitCode);
|
||||
}
|
||||
processStdOut(false);
|
||||
processStdErr(false);
|
||||
|
||||
d->m_result.rawStdOut = d->m_stdOut.rawData;
|
||||
d->m_result.rawStdErr = d->m_stdErr.rawData;
|
||||
|
||||
return d->m_result;
|
||||
}
|
||||
|
||||
bool SynchronousProcess::terminate()
|
||||
{
|
||||
return d->m_process.stopProcess();
|
||||
}
|
||||
|
||||
static inline bool askToKill(const QString &binary = QString())
|
||||
{
|
||||
if (!isGuiThread())
|
||||
return true;
|
||||
const QString title = SynchronousProcess::tr("Process not Responding");
|
||||
QString msg = binary.isEmpty() ?
|
||||
SynchronousProcess::tr("The process is not responding.") :
|
||||
SynchronousProcess::tr("The process \"%1\" is not responding.").arg(QDir::toNativeSeparators(binary));
|
||||
msg += QLatin1Char(' ');
|
||||
msg += SynchronousProcess::tr("Would you like to terminate it?");
|
||||
// Restore the cursor that is set to wait while running.
|
||||
const bool hasOverrideCursor = QApplication::overrideCursor() != nullptr;
|
||||
if (hasOverrideCursor)
|
||||
QApplication::restoreOverrideCursor();
|
||||
QMessageBox::StandardButton answer = QMessageBox::question(nullptr, title, msg, QMessageBox::Yes|QMessageBox::No);
|
||||
if (hasOverrideCursor)
|
||||
QApplication::setOverrideCursor(Qt::WaitCursor);
|
||||
return answer == QMessageBox::Yes;
|
||||
}
|
||||
|
||||
void SynchronousProcess::slotTimeout()
|
||||
{
|
||||
if (!d->m_waitingForUser && (++d->m_hangTimerCount > d->m_maxHangTimerCount)) {
|
||||
if (debug)
|
||||
qDebug() << Q_FUNC_INFO << "HANG detected, killing";
|
||||
d->m_waitingForUser = true;
|
||||
const bool terminate = !d->m_timeOutMessageBoxEnabled || askToKill(d->m_binary.toString());
|
||||
d->m_waitingForUser = false;
|
||||
if (terminate) {
|
||||
d->m_process.stopProcess();
|
||||
d->m_result.result = SynchronousProcessResponse::Hang;
|
||||
} else {
|
||||
d->m_hangTimerCount = 0;
|
||||
}
|
||||
} else {
|
||||
if (debug)
|
||||
qDebug() << Q_FUNC_INFO << d->m_hangTimerCount;
|
||||
}
|
||||
}
|
||||
|
||||
void SynchronousProcess::finished(int exitCode, QProcess::ExitStatus e)
|
||||
{
|
||||
if (debug)
|
||||
qDebug() << Q_FUNC_INFO << exitCode << e;
|
||||
d->m_hangTimerCount = 0;
|
||||
|
||||
switch (e) {
|
||||
case QProcess::NormalExit:
|
||||
d->m_result.result = d->m_exitCodeInterpreter(exitCode);
|
||||
d->m_result.exitCode = exitCode;
|
||||
break;
|
||||
case QProcess::CrashExit:
|
||||
// Was hang detected before and killed?
|
||||
if (d->m_result.result != SynchronousProcessResponse::Hang)
|
||||
d->m_result.result = SynchronousProcessResponse::TerminatedAbnormally;
|
||||
d->m_result.exitCode = -1;
|
||||
break;
|
||||
}
|
||||
d->m_eventLoop.quit();
|
||||
}
|
||||
|
||||
void SynchronousProcess::error(QProcess::ProcessError e)
|
||||
{
|
||||
d->m_hangTimerCount = 0;
|
||||
if (debug)
|
||||
qDebug() << Q_FUNC_INFO << e;
|
||||
// Was hang detected before and killed?
|
||||
if (d->m_result.result != SynchronousProcessResponse::Hang)
|
||||
d->m_result.result = SynchronousProcessResponse::StartFailed;
|
||||
d->m_startFailure = true;
|
||||
d->m_eventLoop.quit();
|
||||
}
|
||||
|
||||
void SynchronousProcess::processStdOut(bool emitSignals)
|
||||
{
|
||||
// Handle binary data
|
||||
d->m_stdOut.append(d->m_process.readAllStandardOutput(), emitSignals);
|
||||
}
|
||||
|
||||
void SynchronousProcess::processStdErr(bool emitSignals)
|
||||
{
|
||||
// Handle binary data
|
||||
d->m_stdErr.append(d->m_process.readAllStandardError(), emitSignals);
|
||||
}
|
||||
|
||||
} // namespace Utils
|
||||
|
||||
#include "synchronousprocess.moc"
|
||||
@@ -1,144 +0,0 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2016 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "utils_global.h"
|
||||
|
||||
#include <QProcess>
|
||||
#include <QTextCodec>
|
||||
|
||||
#include <functional>
|
||||
|
||||
QT_FORWARD_DECLARE_CLASS(QDebug)
|
||||
|
||||
namespace Utils {
|
||||
|
||||
class SynchronousProcessPrivate;
|
||||
class CommandLine;
|
||||
class Environment;
|
||||
|
||||
/* Result of SynchronousProcess execution */
|
||||
class QTCREATOR_UTILS_EXPORT SynchronousProcessResponse
|
||||
{
|
||||
public:
|
||||
enum Result {
|
||||
// Finished with return code 0
|
||||
Finished,
|
||||
// Finished with return code != 0
|
||||
FinishedError,
|
||||
// Process terminated abnormally (kill)
|
||||
TerminatedAbnormally,
|
||||
// Executable could not be started
|
||||
StartFailed,
|
||||
// Hang, no output after time out
|
||||
Hang };
|
||||
|
||||
void clear();
|
||||
|
||||
// Helper to format an exit message.
|
||||
QString exitMessage(const QString &binary, int timeoutS) const;
|
||||
|
||||
QByteArray allRawOutput() const;
|
||||
QString allOutput() const;
|
||||
|
||||
QString stdOut() const;
|
||||
QString stdErr() const;
|
||||
|
||||
Result result = StartFailed;
|
||||
int exitCode = -1;
|
||||
|
||||
QByteArray rawStdOut;
|
||||
QByteArray rawStdErr;
|
||||
QTextCodec *codec = QTextCodec::codecForLocale();
|
||||
};
|
||||
|
||||
QTCREATOR_UTILS_EXPORT QDebug operator<<(QDebug str, const SynchronousProcessResponse &);
|
||||
|
||||
using ExitCodeInterpreter = std::function<SynchronousProcessResponse::Result(int /*exitCode*/)>;
|
||||
QTCREATOR_UTILS_EXPORT SynchronousProcessResponse::Result defaultExitCodeInterpreter(int code);
|
||||
|
||||
class QTCREATOR_UTILS_EXPORT SynchronousProcess : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
SynchronousProcess();
|
||||
~SynchronousProcess() override;
|
||||
|
||||
/* Timeout for hanging processes (triggers after no more output
|
||||
* occurs on stderr/stdout). */
|
||||
void setTimeoutS(int timeoutS);
|
||||
int timeoutS() const;
|
||||
|
||||
void setCodec(QTextCodec *c);
|
||||
QTextCodec *codec() const;
|
||||
|
||||
QProcess::ProcessChannelMode processChannelMode () const;
|
||||
void setProcessChannelMode(QProcess::ProcessChannelMode m);
|
||||
|
||||
bool stdOutBufferedSignalsEnabled() const;
|
||||
void setStdOutBufferedSignalsEnabled(bool);
|
||||
|
||||
bool stdErrBufferedSignalsEnabled() const;
|
||||
void setStdErrBufferedSignalsEnabled(bool);
|
||||
|
||||
bool timeOutMessageBoxEnabled() const;
|
||||
void setTimeOutMessageBoxEnabled(bool);
|
||||
|
||||
Environment environment() const;
|
||||
void setEnvironment(const Environment &);
|
||||
|
||||
void setWorkingDirectory(const QString &workingDirectory);
|
||||
QString workingDirectory() const;
|
||||
|
||||
// Unix: Do not give the child process a terminal for input prompting.
|
||||
void setDisableUnixTerminal();
|
||||
|
||||
void setExitCodeInterpreter(const ExitCodeInterpreter &interpreter);
|
||||
ExitCodeInterpreter exitCodeInterpreter() const;
|
||||
|
||||
// Starts a nested event loop and runs the command
|
||||
SynchronousProcessResponse run(const CommandLine &cmd, const QByteArray &writeData = {});
|
||||
// Starts the command blocking the UI fully
|
||||
SynchronousProcessResponse runBlocking(const CommandLine &cmd);
|
||||
|
||||
signals:
|
||||
void stdOutBuffered(const QString &lines, bool firstTime);
|
||||
void stdErrBuffered(const QString &lines, bool firstTime);
|
||||
|
||||
public slots:
|
||||
bool terminate();
|
||||
|
||||
private:
|
||||
void slotTimeout();
|
||||
void finished(int exitCode, QProcess::ExitStatus e);
|
||||
void error(QProcess::ProcessError);
|
||||
void processStdOut(bool emitSignals);
|
||||
void processStdErr(bool emitSignals);
|
||||
|
||||
SynchronousProcessPrivate *d;
|
||||
};
|
||||
|
||||
} // namespace Utils
|
||||
@@ -60,7 +60,6 @@ SOURCES += \
|
||||
$$PWD/classnamevalidatinglineedit.cpp \
|
||||
$$PWD/fancylineedit.cpp \
|
||||
$$PWD/qtcolorbutton.cpp \
|
||||
$$PWD/synchronousprocess.cpp \
|
||||
$$PWD/savefile.cpp \
|
||||
$$PWD/fileutils.cpp \
|
||||
$$PWD/textfileformat.cpp \
|
||||
@@ -188,7 +187,6 @@ HEADERS += \
|
||||
$$PWD/fancylineedit.h \
|
||||
$$PWD/qtcolorbutton.h \
|
||||
$$PWD/consoleprocess.h \
|
||||
$$PWD/synchronousprocess.h \
|
||||
$$PWD/savefile.h \
|
||||
$$PWD/fileutils.h \
|
||||
$$PWD/textfileformat.h \
|
||||
|
||||
@@ -263,8 +263,6 @@ Project {
|
||||
"styledbar.h",
|
||||
"stylehelper.cpp",
|
||||
"stylehelper.h",
|
||||
"synchronousprocess.cpp",
|
||||
"synchronousprocess.h",
|
||||
"templateengine.cpp",
|
||||
"templateengine.h",
|
||||
"temporarydirectory.cpp",
|
||||
|
||||
@@ -22,16 +22,19 @@
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "androidavdmanager.h"
|
||||
|
||||
#include "avdmanageroutputparser.h"
|
||||
|
||||
#include <coreplugin/icore.h>
|
||||
|
||||
#include <projectexplorer/projectexplorerconstants.h>
|
||||
|
||||
#include <utils/algorithm.h>
|
||||
#include <utils/qtcassert.h>
|
||||
#include <utils/qtcprocess.h>
|
||||
#include <utils/runextensions.h>
|
||||
#include <utils/synchronousprocess.h>
|
||||
|
||||
#include <QApplication>
|
||||
#include <QFileInfo>
|
||||
|
||||
@@ -59,7 +59,6 @@
|
||||
#include <utils/infolabel.h>
|
||||
#include <utils/pathchooser.h>
|
||||
#include <utils/qtcprocess.h>
|
||||
#include <utils/synchronousprocess.h>
|
||||
|
||||
#include <QCheckBox>
|
||||
#include <QComboBox>
|
||||
|
||||
@@ -57,9 +57,9 @@
|
||||
#include <utils/hostosinfo.h>
|
||||
#include <utils/persistentsettings.h>
|
||||
#include <utils/qtcassert.h>
|
||||
#include <utils/qtcprocess.h>
|
||||
#include <utils/runextensions.h>
|
||||
#include <utils/stringutils.h>
|
||||
#include <utils/synchronousprocess.h>
|
||||
|
||||
#include <QApplication>
|
||||
#include <QDirIterator>
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
#include "androidconfigurations.h"
|
||||
#include "ui_androidcreatekeystorecertificate.h"
|
||||
|
||||
#include <utils/synchronousprocess.h>
|
||||
#include <utils/qtcprocess.h>
|
||||
|
||||
#include <QFileDialog>
|
||||
#include <QRegularExpression>
|
||||
|
||||
@@ -55,7 +55,6 @@
|
||||
#include <utils/layoutbuilder.h>
|
||||
#include <utils/qtcassert.h>
|
||||
#include <utils/qtcprocess.h>
|
||||
#include <utils/synchronousprocess.h>
|
||||
|
||||
#include <QCheckBox>
|
||||
#include <QFileDialog>
|
||||
|
||||
@@ -53,8 +53,8 @@
|
||||
|
||||
#include <utils/algorithm.h>
|
||||
#include <utils/qtcassert.h>
|
||||
#include <utils/qtcprocess.h>
|
||||
#include <utils/stringutils.h>
|
||||
#include <utils/synchronousprocess.h>
|
||||
|
||||
#include <QApplication>
|
||||
#include <QDir>
|
||||
|
||||
@@ -47,7 +47,6 @@
|
||||
#include <utils/qtcprocess.h>
|
||||
#include <utils/runextensions.h>
|
||||
#include <utils/stringutils.h>
|
||||
#include <utils/synchronousprocess.h>
|
||||
#include <utils/temporaryfile.h>
|
||||
#include <utils/url.h>
|
||||
|
||||
|
||||
@@ -34,7 +34,6 @@
|
||||
#include <utils/qtcprocess.h>
|
||||
#include <utils/runextensions.h>
|
||||
#include <utils/stringutils.h>
|
||||
#include <utils/synchronousprocess.h>
|
||||
|
||||
#include <QFutureWatcher>
|
||||
#include <QLoggingCategory>
|
||||
|
||||
@@ -45,7 +45,6 @@
|
||||
#include <utils/pathchooser.h>
|
||||
#include <utils/qtcassert.h>
|
||||
#include <utils/qtcprocess.h>
|
||||
#include <utils/synchronousprocess.h>
|
||||
#include <utils/utilsicons.h>
|
||||
|
||||
#include <QDesktopServices>
|
||||
|
||||
@@ -38,7 +38,6 @@
|
||||
#include <utils/pathchooser.h>
|
||||
#include <utils/qtcassert.h>
|
||||
#include <utils/qtcprocess.h>
|
||||
#include <utils/synchronousprocess.h>
|
||||
|
||||
#include <QDebug>
|
||||
#include <QDir>
|
||||
|
||||
@@ -38,7 +38,6 @@
|
||||
#include <utils/pathchooser.h>
|
||||
#include <utils/qtcassert.h>
|
||||
#include <utils/qtcprocess.h>
|
||||
#include <utils/synchronousprocess.h>
|
||||
|
||||
#include <QDebug>
|
||||
#include <QDir>
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
#include <utils/environment.h>
|
||||
#include <utils/pathchooser.h>
|
||||
#include <utils/qtcassert.h>
|
||||
#include <utils/synchronousprocess.h>
|
||||
#include <utils/qtcprocess.h>
|
||||
|
||||
#include <QDebug>
|
||||
#include <QDir>
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
|
||||
#include <utils/runextensions.h>
|
||||
#include <utils/stringutils.h>
|
||||
#include <utils/synchronousprocess.h>
|
||||
#include <utils/qtcprocess.h>
|
||||
|
||||
#include <QDateTime>
|
||||
#include <QFile>
|
||||
|
||||
@@ -53,8 +53,8 @@
|
||||
#include <utils/fileutils.h>
|
||||
#include <utils/mimetypes/mimedatabase.h>
|
||||
#include <utils/qtcassert.h>
|
||||
#include <utils/qtcprocess.h>
|
||||
#include <utils/runextensions.h>
|
||||
#include <utils/synchronousprocess.h>
|
||||
#include <utils/temporarydirectory.h>
|
||||
#include <utils/textutils.h>
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
|
||||
#include <coreplugin/icore.h>
|
||||
|
||||
#include <utils/synchronousprocess.h>
|
||||
#include <utils/qtcprocess.h>
|
||||
|
||||
#include <QDateTime>
|
||||
#include <QFile>
|
||||
|
||||
@@ -34,8 +34,8 @@
|
||||
#include <cpptools/compileroptionsbuilder.h>
|
||||
#include <cpptools/cpptoolsreuse.h>
|
||||
|
||||
#include <utils/synchronousprocess.h>
|
||||
#include <utils/qtcassert.h>
|
||||
#include <utils/qtcprocess.h>
|
||||
|
||||
#include <QDebug>
|
||||
#include <QDir>
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
|
||||
#include <utils/environment.h>
|
||||
#include <utils/qtcassert.h>
|
||||
#include <utils/synchronousprocess.h>
|
||||
#include <utils/qtcprocess.h>
|
||||
#include <utils/temporaryfile.h>
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
@@ -35,11 +35,10 @@
|
||||
#include <cpptools/cpptoolsreuse.h>
|
||||
#include <projectexplorer/projectexplorerconstants.h>
|
||||
|
||||
#include <utils/qtcprocess.h>
|
||||
#include <utils/checkablemessagebox.h>
|
||||
#include <utils/environment.h>
|
||||
#include <utils/hostosinfo.h>
|
||||
#include <utils/synchronousprocess.h>
|
||||
#include <utils/qtcprocess.h>
|
||||
|
||||
#include <QFileInfo>
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
#include <coreplugin/messagemanager.h>
|
||||
|
||||
#include <utils/environment.h>
|
||||
#include <utils/synchronousprocess.h>
|
||||
#include <utils/qtcprocess.h>
|
||||
|
||||
#include <QDir>
|
||||
#include <QFileInfo>
|
||||
|
||||
@@ -60,9 +60,9 @@
|
||||
#include <utils/infobar.h>
|
||||
#include <utils/parameteraction.h>
|
||||
#include <utils/qtcassert.h>
|
||||
#include <utils/qtcprocess.h>
|
||||
#include <utils/runextensions.h>
|
||||
#include <utils/stringutils.h>
|
||||
#include <utils/synchronousprocess.h>
|
||||
#include <utils/temporarydirectory.h>
|
||||
|
||||
#include <vcsbase/basevcseditorfactory.h>
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
#include <utils/fileutils.h>
|
||||
#include <utils/id.h>
|
||||
#include <utils/optional.h>
|
||||
#include <utils/synchronousprocess.h>
|
||||
#include <utils/qtcprocess.h>
|
||||
|
||||
QT_FORWARD_DECLARE_CLASS(QProcess)
|
||||
|
||||
|
||||
@@ -36,8 +36,8 @@
|
||||
#include <utils/infolabel.h>
|
||||
#include <utils/pathchooser.h>
|
||||
#include <utils/qtcassert.h>
|
||||
#include <utils/qtcprocess.h>
|
||||
#include <utils/runextensions.h>
|
||||
#include <utils/synchronousprocess.h>
|
||||
#include <utils/temporarydirectory.h>
|
||||
#include <utils/wizard.h>
|
||||
#include <utils/wizardpage.h>
|
||||
|
||||
@@ -42,9 +42,9 @@
|
||||
|
||||
#include <texteditor/textdocument.h>
|
||||
|
||||
#include <utils/synchronousprocess.h>
|
||||
#include <utils/parameteraction.h>
|
||||
#include <utils/qtcassert.h>
|
||||
#include <utils/qtcprocess.h>
|
||||
#include <utils/stringutils.h>
|
||||
|
||||
#include <coreplugin/icore.h>
|
||||
|
||||
@@ -35,8 +35,8 @@
|
||||
#include <utils/hostosinfo.h>
|
||||
#include <utils/macroexpander.h>
|
||||
#include <utils/qtcassert.h>
|
||||
#include <utils/qtcprocess.h>
|
||||
#include <utils/stringutils.h>
|
||||
#include <utils/synchronousprocess.h>
|
||||
#include <utils/utilsicons.h>
|
||||
#include <utils/winutils.h>
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
#include <utils/pathchooser.h>
|
||||
#include <utils/persistentsettings.h>
|
||||
#include <utils/qtcassert.h>
|
||||
#include <utils/synchronousprocess.h>
|
||||
#include <utils/qtcprocess.h>
|
||||
#include <utils/treemodel.h>
|
||||
#include <utils/winutils.h>
|
||||
|
||||
|
||||
@@ -64,7 +64,6 @@
|
||||
#include <utils/qtcassert.h>
|
||||
#include <utils/qtcprocess.h>
|
||||
#include <utils/stringutils.h>
|
||||
#include <utils/synchronousprocess.h>
|
||||
#include <utils/temporaryfile.h>
|
||||
|
||||
#include <QDirIterator>
|
||||
|
||||
@@ -31,8 +31,9 @@
|
||||
|
||||
#include <coreplugin/icore.h>
|
||||
#include <coreplugin/shellcommand.h>
|
||||
|
||||
#include <utils/hostosinfo.h>
|
||||
#include <utils/synchronousprocess.h>
|
||||
#include <utils/qtcprocess.h>
|
||||
|
||||
#include <QFile>
|
||||
#include <QJsonDocument>
|
||||
|
||||
@@ -50,7 +50,6 @@
|
||||
#include <utils/qtcassert.h>
|
||||
#include <utils/qtcprocess.h>
|
||||
#include <utils/stringutils.h>
|
||||
#include <utils/synchronousprocess.h>
|
||||
#include <utils/temporaryfile.h>
|
||||
#include <utils/theme/theme.h>
|
||||
|
||||
|
||||
@@ -39,8 +39,8 @@
|
||||
#include <utils/filesearch.h>
|
||||
#include <utils/fileutils.h>
|
||||
#include <utils/qtcassert.h>
|
||||
#include <utils/qtcprocess.h>
|
||||
#include <utils/runextensions.h>
|
||||
#include <utils/synchronousprocess.h>
|
||||
#include <utils/textfileformat.h>
|
||||
|
||||
#include <QCheckBox>
|
||||
|
||||
@@ -31,10 +31,7 @@
|
||||
#include "iosprobe.h"
|
||||
|
||||
#include <coreplugin/icore.h>
|
||||
#include <utils/algorithm.h>
|
||||
#include <utils/synchronousprocess.h>
|
||||
#include <utils/qtcassert.h>
|
||||
#include <utils/synchronousprocess.h>
|
||||
|
||||
#include <projectexplorer/kitmanager.h>
|
||||
#include <projectexplorer/kitinformation.h>
|
||||
#include <projectexplorer/devicesupport/devicemanager.h>
|
||||
@@ -42,14 +39,20 @@
|
||||
#include <projectexplorer/toolchain.h>
|
||||
#include <projectexplorer/gcctoolchain.h>
|
||||
#include <projectexplorer/projectexplorerconstants.h>
|
||||
|
||||
#include <debugger/debuggeritemmanager.h>
|
||||
#include <debugger/debuggeritem.h>
|
||||
#include <debugger/debuggerkitinformation.h>
|
||||
|
||||
#include <qtsupport/baseqtversion.h>
|
||||
#include <qtsupport/qtkitinformation.h>
|
||||
#include <qtsupport/qtversionmanager.h>
|
||||
#include <qtsupport/qtversionfactory.h>
|
||||
|
||||
#include <utils/algorithm.h>
|
||||
#include <utils/qtcassert.h>
|
||||
#include <utils/qtcprocess.h>
|
||||
|
||||
#include <QDir>
|
||||
#include <QDomDocument>
|
||||
#include <QFileInfo>
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
#include "iosprobe.h"
|
||||
|
||||
#include <utils/algorithm.h>
|
||||
#include <utils/synchronousprocess.h>
|
||||
#include <utils/qtcprocess.h>
|
||||
|
||||
#include <QDir>
|
||||
#include <QFileInfo>
|
||||
|
||||
@@ -29,12 +29,14 @@
|
||||
#include "iossimulator.h"
|
||||
#include "simulatorcontrol.h"
|
||||
|
||||
#include "debugger/debuggerconstants.h"
|
||||
#include <coreplugin/icore.h>
|
||||
#include <utils/qtcassert.h>
|
||||
|
||||
#include <debugger/debuggerconstants.h>
|
||||
|
||||
#include <utils/fileutils.h>
|
||||
#include <utils/qtcassert.h>
|
||||
#include <utils/qtcprocess.h>
|
||||
#include <utils/runextensions.h>
|
||||
#include <utils/synchronousprocess.h>
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QDir>
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
#include <utils/algorithm.h>
|
||||
#include <utils/runextensions.h>
|
||||
#include <utils/qtcassert.h>
|
||||
#include <utils/synchronousprocess.h>
|
||||
#include <utils/qtcprocess.h>
|
||||
|
||||
#ifdef Q_OS_MAC
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
|
||||
@@ -34,6 +34,8 @@
|
||||
#include <coreplugin/icore.h>
|
||||
#include <coreplugin/idocument.h>
|
||||
#include <coreplugin/messagemanager.h>
|
||||
#include <coreplugin/progressmanager/progressmanager.h>
|
||||
|
||||
#include <languageserverprotocol/completion.h>
|
||||
#include <languageserverprotocol/diagnostics.h>
|
||||
#include <languageserverprotocol/languagefeatures.h>
|
||||
@@ -41,8 +43,10 @@
|
||||
#include <languageserverprotocol/servercapabilities.h>
|
||||
#include <languageserverprotocol/workspace.h>
|
||||
#include <languageserverprotocol/progresssupport.h>
|
||||
|
||||
#include <projectexplorer/project.h>
|
||||
#include <projectexplorer/session.h>
|
||||
|
||||
#include <texteditor/codeassist/documentcontentcompletion.h>
|
||||
#include <texteditor/codeassist/iassistprocessor.h>
|
||||
#include <texteditor/ioutlinewidget.h>
|
||||
@@ -52,10 +56,10 @@
|
||||
#include <texteditor/texteditor.h>
|
||||
#include <texteditor/texteditoractionhandler.h>
|
||||
#include <texteditor/texteditorsettings.h>
|
||||
|
||||
#include <utils/mimetypes/mimedatabase.h>
|
||||
#include <utils/qtcprocess.h>
|
||||
#include <utils/synchronousprocess.h>
|
||||
#include <coreplugin/progressmanager/progressmanager.h>
|
||||
|
||||
|
||||
#include <QDebug>
|
||||
#include <QLoggingCategory>
|
||||
|
||||
@@ -35,10 +35,11 @@
|
||||
#include <vcsbase/vcsbaseeditor.h>
|
||||
#include <vcsbase/vcsbaseeditorconfig.h>
|
||||
#include <vcsbase/vcsbasediffeditorcontroller.h>
|
||||
#include <utils/synchronousprocess.h>
|
||||
|
||||
#include <utils/fileutils.h>
|
||||
#include <utils/hostosinfo.h>
|
||||
#include <utils/qtcassert.h>
|
||||
#include <utils/qtcprocess.h>
|
||||
|
||||
#include <QDateTime>
|
||||
#include <QDir>
|
||||
|
||||
@@ -41,11 +41,13 @@
|
||||
#include <coreplugin/icore.h>
|
||||
#include <coreplugin/messagemanager.h>
|
||||
#include <coreplugin/locator/commandlocator.h>
|
||||
|
||||
#include <texteditor/textdocument.h>
|
||||
|
||||
#include <utils/fileutils.h>
|
||||
#include <utils/parameteraction.h>
|
||||
#include <utils/qtcassert.h>
|
||||
#include <utils/synchronousprocess.h>
|
||||
#include <utils/qtcprocess.h>
|
||||
#include <utils/temporarydirectory.h>
|
||||
|
||||
#include <vcsbase/basevcseditorfactory.h>
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
|
||||
#include <utils/fileutils.h>
|
||||
#include <utils/hostosinfo.h>
|
||||
#include <utils/synchronousprocess.h>
|
||||
#include <utils/qtcprocess.h>
|
||||
#include <utils/temporarydirectory.h>
|
||||
|
||||
#include <QFileInfo>
|
||||
|
||||
@@ -37,11 +37,9 @@
|
||||
#include <utils/algorithm.h>
|
||||
#include <utils/environment.h>
|
||||
#include <utils/hostosinfo.h>
|
||||
#include <utils/synchronousprocess.h>
|
||||
#include <utils/pathchooser.h>
|
||||
#include <utils/qtcassert.h>
|
||||
#include <utils/qtcprocess.h>
|
||||
#include <utils/synchronousprocess.h>
|
||||
|
||||
#include <QBuffer>
|
||||
#include <QComboBox>
|
||||
|
||||
@@ -40,7 +40,6 @@
|
||||
#include <utils/optional.h>
|
||||
#include <utils/qtcassert.h>
|
||||
#include <utils/qtcprocess.h>
|
||||
#include <utils/synchronousprocess.h>
|
||||
#include <utils/runextensions.h>
|
||||
#include <utils/temporarydirectory.h>
|
||||
#include <utils/pathchooser.h>
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
#include "pythonconstants.h"
|
||||
|
||||
#include <coreplugin/dialogs/ioptionspage.h>
|
||||
#include "coreplugin/icore.h"
|
||||
#include <coreplugin/icore.h>
|
||||
|
||||
#include <utils/algorithm.h>
|
||||
#include <utils/qtcassert.h>
|
||||
@@ -37,7 +37,7 @@
|
||||
#include <utils/listmodel.h>
|
||||
#include <utils/layoutbuilder.h>
|
||||
#include <utils/pathchooser.h>
|
||||
#include <utils/synchronousprocess.h>
|
||||
#include <utils/qtcprocess.h>
|
||||
#include <utils/treemodel.h>
|
||||
|
||||
#include <QDir>
|
||||
|
||||
@@ -48,7 +48,6 @@
|
||||
#include <utils/qtcassert.h>
|
||||
#include <utils/qtcprocess.h>
|
||||
#include <utils/runextensions.h>
|
||||
#include <utils/synchronousprocess.h>
|
||||
|
||||
#include <QDir>
|
||||
#include <QFutureWatcher>
|
||||
|
||||
@@ -28,20 +28,22 @@
|
||||
#include "subversionplugin.h"
|
||||
#include "subversionsettings.h"
|
||||
|
||||
#include <coreplugin/editormanager/editormanager.h>
|
||||
|
||||
#include <vcsbase/vcscommand.h>
|
||||
#include <vcsbase/vcsbaseconstants.h>
|
||||
#include <vcsbase/vcsbasediffeditorcontroller.h>
|
||||
#include <vcsbase/vcsbaseeditor.h>
|
||||
#include <vcsbase/vcsbaseeditorconfig.h>
|
||||
#include <vcsbase/vcsbaseplugin.h>
|
||||
#include <utils/qtcassert.h>
|
||||
#include <utils/synchronousprocess.h>
|
||||
|
||||
#include <diffeditor/diffeditorcontroller.h>
|
||||
#include <diffeditor/diffutils.h>
|
||||
#include <coreplugin/editormanager/editormanager.h>
|
||||
|
||||
#include <utils/algorithm.h>
|
||||
#include <utils/hostosinfo.h>
|
||||
#include <utils/qtcassert.h>
|
||||
#include <utils/qtcprocess.h>
|
||||
|
||||
#include <QDir>
|
||||
#include <QFileInfo>
|
||||
|
||||
@@ -57,8 +57,8 @@
|
||||
#include <utils/hostosinfo.h>
|
||||
#include <utils/parameteraction.h>
|
||||
#include <utils/qtcassert.h>
|
||||
#include <utils/qtcprocess.h>
|
||||
#include <utils/stringutils.h>
|
||||
#include <utils/synchronousprocess.h>
|
||||
|
||||
#include <QDebug>
|
||||
#include <QDir>
|
||||
|
||||
@@ -32,11 +32,11 @@
|
||||
#include <coreplugin/messagemanager.h>
|
||||
|
||||
#include <utils/differ.h>
|
||||
#include <utils/qtcassert.h>
|
||||
#include <utils/qtcprocess.h>
|
||||
#include <utils/runextensions.h>
|
||||
#include <utils/synchronousprocess.h>
|
||||
#include <utils/temporarydirectory.h>
|
||||
#include <utils/textutils.h>
|
||||
#include <utils/qtcassert.h>
|
||||
|
||||
#include <QFileInfo>
|
||||
#include <QFutureWatcher>
|
||||
|
||||
@@ -31,8 +31,8 @@
|
||||
|
||||
#include <utils/fileutils.h>
|
||||
#include <utils/hostosinfo.h>
|
||||
#include <utils/qtcprocess.h>
|
||||
#include <utils/stringutils.h>
|
||||
#include <utils/synchronousprocess.h>
|
||||
|
||||
#include <QSettings>
|
||||
#include <QLatin1String>
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
|
||||
#include <utils/qtcassert.h>
|
||||
#include <utils/qtcprocess.h>
|
||||
#include <utils/synchronousprocess.h>
|
||||
|
||||
#include <vcsbase/vcsbaseeditor.h>
|
||||
#include <vcsbase/vcsoutputwindow.h>
|
||||
#include <vcsbase/vcsbaseplugin.h>
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
|
||||
#include <utils/fileutils.h>
|
||||
#include <utils/id.h>
|
||||
#include <utils/synchronousprocess.h>
|
||||
#include <utils/qtcprocess.h>
|
||||
|
||||
#include <QObject>
|
||||
#include <QStringList>
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
#include <projectexplorer/project.h>
|
||||
#include <projectexplorer/session.h>
|
||||
#include <utils/qtcassert.h>
|
||||
#include <utils/synchronousprocess.h>
|
||||
#include <utils/qtcprocess.h>
|
||||
|
||||
#include <QDebug>
|
||||
#include <QDir>
|
||||
|
||||
@@ -32,12 +32,6 @@
|
||||
#include <utils/environment.h>
|
||||
#include <utils/qtcprocess.h>
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
#ifdef Q_CC_MSVC
|
||||
#include <utils/synchronousprocess.h>
|
||||
#endif // Q_CC_MSVC
|
||||
#endif // Q_OS_WIN
|
||||
|
||||
#include <QtTest>
|
||||
#include <math.h>
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
#include "mainwindow.h"
|
||||
|
||||
#include <utils/fileutils.h>
|
||||
#include <utils/synchronousprocess.h>
|
||||
#include <utils/qtcprocess.h>
|
||||
|
||||
#include <QPlainTextEdit>
|
||||
#include <QApplication>
|
||||
|
||||
Reference in New Issue
Block a user