ProcessInterface: Add done() signal

And get rid of errorOccurred() and finished() signals.
Get rid of resultData() accessor, as this data is passed
with done() signal.

Task-number: QTCREATORBUG-27358
Change-Id: I677bbd174cceea6d8f5a989f961222c417992b60
Reviewed-by: hjk <hjk@qt.io>
Reviewed-by: <github-actions-qt-creator@cristianadam.eu>
This commit is contained in:
Jarek Kobus
2022-04-11 11:28:39 +02:00
parent 00acccfd3d
commit 4ca336762e
10 changed files with 210 additions and 385 deletions

View File

@@ -74,12 +74,12 @@ void StartProcessPacket::doSerialize(QDataStream &stream) const
void StartProcessPacket::doDeserialize(QDataStream &stream) void StartProcessPacket::doDeserialize(QDataStream &stream)
{ {
int pm; int processModeInt;
stream >> command stream >> command
>> arguments >> arguments
>> workingDir >> workingDir
>> env >> env
>> pm >> processModeInt
>> writeData >> writeData
>> standardInputFile >> standardInputFile
>> belowNormalPriority >> belowNormalPriority
@@ -87,7 +87,7 @@ void StartProcessPacket::doDeserialize(QDataStream &stream)
>> lowPriority >> lowPriority
>> unixTerminalDisabled >> unixTerminalDisabled
>> useCtrlCStub; >> useCtrlCStub;
processMode = Utils::ProcessMode(pm); processMode = Utils::ProcessMode(processModeInt);
} }
@@ -119,9 +119,9 @@ void StopProcessPacket::doSerialize(QDataStream &stream) const
void StopProcessPacket::doDeserialize(QDataStream &stream) void StopProcessPacket::doDeserialize(QDataStream &stream)
{ {
int sig; int signalTypeInt;
stream >> sig; stream >> signalTypeInt;
signalType = SignalType(sig); signalType = SignalType(signalTypeInt);
} }
void WritePacket::doSerialize(QDataStream &stream) const void WritePacket::doSerialize(QDataStream &stream) const
@@ -134,25 +134,6 @@ void WritePacket::doDeserialize(QDataStream &stream)
stream >> inputData; stream >> inputData;
} }
ProcessErrorPacket::ProcessErrorPacket(quintptr token)
: LauncherPacket(LauncherPacketType::ProcessError, token)
{
}
void ProcessErrorPacket::doSerialize(QDataStream &stream) const
{
stream << static_cast<quint8>(error) << errorString;
}
void ProcessErrorPacket::doDeserialize(QDataStream &stream)
{
quint8 e;
stream >> e;
error = static_cast<QProcess::ProcessError>(e);
stream >> errorString;
}
void ReadyReadPacket::doSerialize(QDataStream &stream) const void ReadyReadPacket::doSerialize(QDataStream &stream) const
{ {
stream << standardChannel; stream << standardChannel;
@@ -164,27 +145,32 @@ void ReadyReadPacket::doDeserialize(QDataStream &stream)
} }
ProcessFinishedPacket::ProcessFinishedPacket(quintptr token) ProcessDonePacket::ProcessDonePacket(quintptr token)
: LauncherPacket(LauncherPacketType::ProcessFinished, token) : LauncherPacket(LauncherPacketType::ProcessDone, token)
{ {
} }
void ProcessFinishedPacket::doSerialize(QDataStream &stream) const void ProcessDonePacket::doSerialize(QDataStream &stream) const
{ {
stream << errorString << stdOut << stdErr stream << exitCode
<< static_cast<quint8>(exitStatus) << static_cast<quint8>(error) << int(exitStatus)
<< exitCode; << int(error)
<< errorString
<< stdOut
<< stdErr;
} }
void ProcessFinishedPacket::doDeserialize(QDataStream &stream) void ProcessDonePacket::doDeserialize(QDataStream &stream)
{ {
stream >> errorString >> stdOut >> stdErr; int exitStatusInt, errorInt;
quint8 val; stream >> exitCode
stream >> val; >> exitStatusInt
exitStatus = static_cast<QProcess::ExitStatus>(val); >> errorInt
stream >> val; >> errorString
error = static_cast<QProcess::ProcessError>(val); >> stdOut
stream >> exitCode; >> stdErr;
exitStatus = QProcess::ExitStatus(exitStatusInt);
error = QProcess::ProcessError(errorInt);
} }
ShutdownPacket::ShutdownPacket() : LauncherPacket(LauncherPacketType::Shutdown, 0) { } ShutdownPacket::ShutdownPacket() : LauncherPacket(LauncherPacketType::Shutdown, 0) { }

View File

@@ -45,11 +45,10 @@ enum class LauncherPacketType {
WriteIntoProcess, WriteIntoProcess,
StopProcess, StopProcess,
// launcher -> client packets: // launcher -> client packets:
ProcessError,
ProcessStarted, ProcessStarted,
ReadyReadStandardOutput, ReadyReadStandardOutput,
ReadyReadStandardError, ReadyReadStandardError,
ProcessFinished ProcessDone
}; };
class PacketParser class PacketParser
@@ -176,19 +175,6 @@ private:
void doDeserialize(QDataStream &stream) override; void doDeserialize(QDataStream &stream) override;
}; };
class ProcessErrorPacket : public LauncherPacket
{
public:
ProcessErrorPacket(quintptr token);
QProcess::ProcessError error = QProcess::UnknownError;
QString errorString;
private:
void doSerialize(QDataStream &stream) const override;
void doDeserialize(QDataStream &stream) override;
};
class ReadyReadPacket : public LauncherPacket class ReadyReadPacket : public LauncherPacket
{ {
public: public:
@@ -216,17 +202,18 @@ public:
: ReadyReadPacket(LauncherPacketType::ReadyReadStandardError, token) { } : ReadyReadPacket(LauncherPacketType::ReadyReadStandardError, token) { }
}; };
class ProcessFinishedPacket : public LauncherPacket class ProcessDonePacket : public LauncherPacket
{ {
public: public:
ProcessFinishedPacket(quintptr token); ProcessDonePacket(quintptr token);
QString errorString;
QByteArray stdOut; QByteArray stdOut;
QByteArray stdErr; QByteArray stdErr;
int exitCode = 0;
QProcess::ExitStatus exitStatus = QProcess::NormalExit; QProcess::ExitStatus exitStatus = QProcess::NormalExit;
QProcess::ProcessError error = QProcess::UnknownError; QProcess::ProcessError error = QProcess::UnknownError;
int exitCode = 0; QString errorString;
private: private:
void doSerialize(QDataStream &stream) const override; void doSerialize(QDataStream &stream) const override;

View File

@@ -49,20 +49,6 @@ private:
const CallerHandle::SignalType m_signalType; const CallerHandle::SignalType m_signalType;
}; };
class ErrorSignal : public LauncherSignal
{
public:
ErrorSignal(QProcess::ProcessError error, const QString &errorString)
: LauncherSignal(CallerHandle::SignalType::Error)
, m_error(error)
, m_errorString(errorString) {}
QProcess::ProcessError error() const { return m_error; }
QString errorString() const { return m_errorString; }
private:
const QProcess::ProcessError m_error;
const QString m_errorString;
};
class StartedSignal : public LauncherSignal class StartedSignal : public LauncherSignal
{ {
public: public:
@@ -92,19 +78,15 @@ private:
QByteArray m_stdErr; QByteArray m_stdErr;
}; };
class FinishedSignal : public LauncherSignal class DoneSignal : public LauncherSignal
{ {
public: public:
FinishedSignal(QProcess::ExitStatus exitStatus, DoneSignal(const ProcessResultData &resultData)
int exitCode) : LauncherSignal(CallerHandle::SignalType::Done)
: LauncherSignal(CallerHandle::SignalType::Finished) , m_resultData(resultData) {}
, m_exitStatus(exitStatus) ProcessResultData resultData() const { return m_resultData; }
, m_exitCode(exitCode) {}
QProcess::ExitStatus exitStatus() const { return m_exitStatus; }
int exitCode() const { return m_exitCode; }
private: private:
const QProcess::ExitStatus m_exitStatus; const ProcessResultData m_resultData;
const int m_exitCode;
}; };
CallerHandle::~CallerHandle() CallerHandle::~CallerHandle()
@@ -124,38 +106,28 @@ bool CallerHandle::waitForReadyRead(int msces)
bool CallerHandle::waitForFinished(int msecs) bool CallerHandle::waitForFinished(int msecs)
{ {
return waitForSignal(msecs, SignalType::Finished); return waitForSignal(msecs, SignalType::Done);
} }
QList<CallerHandle::SignalType> CallerHandle::flush() void CallerHandle::flush()
{ {
return flushFor(SignalType::NoSignal); flushFor(SignalType::NoSignal);
} }
QList<CallerHandle::SignalType> CallerHandle::flushFor(SignalType signalType) bool CallerHandle::flushFor(SignalType signalType)
{ {
QTC_ASSERT(isCalledFromCallersThread(), return {}); QTC_ASSERT(isCalledFromCallersThread(), return {});
QList<LauncherSignal *> oldSignals; QList<LauncherSignal *> oldSignals;
QList<SignalType> flushedSignals; QList<SignalType> flushedSignals;
{ {
// 1. If signalType is no signal - flush all
// 2. Flush all if we have any error
// 3. If we are flushing for Finished or ReadyRead, flush all, too
// 4. If we are flushing for Started, flush Started only
// In short: only when we are flushing for Started, flush started only signal,
// otherwise flush always all. (note: we can't flush for Error, since we don't have
// waitForError() method).
QMutexLocker locker(&m_mutex); QMutexLocker locker(&m_mutex);
const QList<SignalType> storedSignals = const QList<SignalType> storedSignals =
Utils::transform(qAsConst(m_signals), [](const LauncherSignal *launcherSignal) { Utils::transform(qAsConst(m_signals), [](const LauncherSignal *launcherSignal) {
return launcherSignal->signalType(); return launcherSignal->signalType();
}); });
const bool flushAll = (signalType != SignalType::Started) // If we are flushing for Started, flush Started only
|| storedSignals.contains(SignalType::Error); const bool flushAll = (signalType != SignalType::Started);
if (flushAll) { if (flushAll) {
oldSignals = m_signals; oldSignals = m_signals;
m_signals = {}; m_signals = {};
@@ -169,27 +141,28 @@ QList<CallerHandle::SignalType> CallerHandle::flushFor(SignalType signalType)
} }
} }
} }
bool signalMatched = false;
for (const LauncherSignal *storedSignal : qAsConst(oldSignals)) { for (const LauncherSignal *storedSignal : qAsConst(oldSignals)) {
const SignalType storedSignalType = storedSignal->signalType(); const SignalType storedSignalType = storedSignal->signalType();
if (storedSignalType == signalType)
signalMatched = true;
switch (storedSignalType) { switch (storedSignalType) {
case SignalType::NoSignal: case SignalType::NoSignal:
break; break;
case SignalType::Error:
handleError(static_cast<const ErrorSignal *>(storedSignal));
break;
case SignalType::Started: case SignalType::Started:
handleStarted(static_cast<const StartedSignal *>(storedSignal)); handleStarted(static_cast<const StartedSignal *>(storedSignal));
break; break;
case SignalType::ReadyRead: case SignalType::ReadyRead:
handleReadyRead(static_cast<const ReadyReadSignal *>(storedSignal)); handleReadyRead(static_cast<const ReadyReadSignal *>(storedSignal));
break; break;
case SignalType::Finished: case SignalType::Done:
handleFinished(static_cast<const FinishedSignal *>(storedSignal)); signalMatched = true;
handleDone(static_cast<const DoneSignal *>(storedSignal));
break; break;
} }
delete storedSignal; delete storedSignal;
} }
return flushedSignals; return signalMatched;
} }
// Called from caller's thread exclusively. // Called from caller's thread exclusively.
@@ -200,18 +173,6 @@ bool CallerHandle::shouldFlush() const
return !m_signals.isEmpty(); return !m_signals.isEmpty();
} }
void CallerHandle::handleError(const ErrorSignal *launcherSignal)
{
QTC_ASSERT(isCalledFromCallersThread(), return);
m_processState = QProcess::NotRunning;
m_result.m_error = launcherSignal->error();
if (!launcherSignal->errorString().isEmpty())
m_result.m_errorString = launcherSignal->errorString();
if (m_result.m_error == QProcess::FailedToStart)
m_result.m_exitCode = 255; // This code is being returned by QProcess when FailedToStart error occurred
emit errorOccurred(m_result.m_error);
}
void CallerHandle::handleStarted(const StartedSignal *launcherSignal) void CallerHandle::handleStarted(const StartedSignal *launcherSignal)
{ {
QTC_ASSERT(isCalledFromCallersThread(), return); QTC_ASSERT(isCalledFromCallersThread(), return);
@@ -248,13 +209,11 @@ void CallerHandle::handleReadyRead(const ReadyReadSignal *launcherSignal)
} }
} }
void CallerHandle::handleFinished(const FinishedSignal *launcherSignal) void CallerHandle::handleDone(const DoneSignal *launcherSignal)
{ {
QTC_ASSERT(isCalledFromCallersThread(), return); QTC_ASSERT(isCalledFromCallersThread(), return);
m_processState = QProcess::NotRunning; m_processState = QProcess::NotRunning;
m_result.m_exitStatus = launcherSignal->exitStatus(); emit done(launcherSignal->resultData());
m_result.m_exitCode = launcherSignal->exitCode();
emit finished();
} }
// Called from launcher's thread exclusively. // Called from launcher's thread exclusively.
@@ -276,19 +235,9 @@ void CallerHandle::appendSignal(LauncherSignal *newSignal)
if (!m_signals.isEmpty()) { if (!m_signals.isEmpty()) {
LauncherSignal *lastSignal = m_signals.last(); LauncherSignal *lastSignal = m_signals.last();
QTC_ASSERT(lastSignal->signalType() != SignalType::Finished, QTC_ASSERT(lastSignal->signalType() != SignalType::Done,
qWarning() << "Buffering new signal for process" << m_command qWarning() << "Buffering new signal for process" << m_command
<< "while the last finished() signal wasn't flushed yet."); << "while the last done() signal wasn't flushed yet.");
if (lastSignal->signalType() == SignalType::Error) {
ErrorSignal *lastError = static_cast<ErrorSignal *>(lastSignal);
QTC_ASSERT(lastError->error() != QProcess::FailedToStart,
qWarning() << "Buffering new signal for process" << m_command
<< "while the last FailedToStart error signal wasn't flushed yet.");
QTC_ASSERT(newSignal->signalType() == SignalType::Finished,
qWarning() << "Buffering non finished signal for process" << m_command
<< "while the last buffered signal was an error.");
}
// Merge ReadyRead signals into one. // Merge ReadyRead signals into one.
if (lastSignal->signalType() == SignalType::ReadyRead if (lastSignal->signalType() == SignalType::ReadyRead
@@ -329,13 +278,13 @@ void CallerHandle::sendStopPacket(StopProcessPacket::SignalType signalType)
} }
m_processState.store(QProcess::NotRunning); m_processState.store(QProcess::NotRunning);
m_result.m_errorString = QCoreApplication::translate("Utils::LauncherHandle", const QString errorString = QCoreApplication::translate("Utils::LauncherHandle",
"Process was canceled before it was started."); "Process was canceled before it was started.");
m_result.m_error = QProcess::FailedToStart; const ProcessResultData result = { 0, QProcess::NormalExit, QProcess::FailedToStart,
emit errorOccurred(m_result.m_error); errorString };
emit done(result);
} }
void CallerHandle::terminate() void CallerHandle::terminate()
{ {
QTC_ASSERT(isCalledFromCallersThread(), return); QTC_ASSERT(isCalledFromCallersThread(), return);
@@ -366,24 +315,15 @@ qint64 CallerHandle::processId() const
return m_processId; return m_processId;
} }
ProcessResultData CallerHandle::resultData() const
{
QTC_ASSERT(isCalledFromCallersThread(), return {});
return m_result;
}
void CallerHandle::setErrorString(const QString &str)
{
QTC_ASSERT(isCalledFromCallersThread(), return);
m_result.m_errorString = str;
}
void CallerHandle::start(const QString &program, const QStringList &arguments) void CallerHandle::start(const QString &program, const QStringList &arguments)
{ {
QTC_ASSERT(isCalledFromCallersThread(), return); QTC_ASSERT(isCalledFromCallersThread(), return);
if (!m_launcherHandle || m_launcherHandle->isSocketError()) { if (!m_launcherHandle || m_launcherHandle->isSocketError()) {
m_result.m_error = QProcess::FailedToStart; const QString errorString = QCoreApplication::translate("Utils::LauncherHandle",
emit errorOccurred(m_result.m_error); "Process launcher socket error.");
const ProcessResultData result = { 0, QProcess::NormalExit, QProcess::FailedToStart,
errorString };
emit done(result);
return; return;
} }
@@ -492,7 +432,7 @@ bool CallerHandle::canWaitFor(SignalType newSignal) const
case SignalType::Started: case SignalType::Started:
return m_processState == QProcess::Starting; return m_processState == QProcess::Starting;
case SignalType::ReadyRead: case SignalType::ReadyRead:
case SignalType::Finished: case SignalType::Done:
return m_processState != QProcess::NotRunning; return m_processState != QProcess::NotRunning;
default: default:
break; break;
@@ -524,16 +464,10 @@ bool LauncherHandle::waitForSignal(int msecs, CallerHandle::SignalType newSignal
break; break;
if (!doWaitForSignal(deadline, newSignal)) if (!doWaitForSignal(deadline, newSignal))
break; break;
const QList<CallerHandle::SignalType> flushedSignals = m_callerHandle->flushFor(newSignal); // Matching (or Done) signal was flushed
const bool errorOccurred = flushedSignals.contains(CallerHandle::SignalType::Error); if (m_callerHandle->flushFor(newSignal))
if (errorOccurred)
return true; // apparently QProcess behaves like this in case of error
const bool newSignalFlushed = flushedSignals.contains(newSignal);
if (newSignalFlushed) // so we don't continue waiting
return true; return true;
const bool finishedSignalFlushed = flushedSignals.contains(CallerHandle::SignalType::Finished); // Otherwise continue awaiting (e.g. when ReadyRead came while waitForFinished())
if (finishedSignalFlushed)
return true; // finish has appeared but we were waiting for other signal
} }
return false; return false;
} }
@@ -557,30 +491,6 @@ bool LauncherHandle::doWaitForSignal(QDeadlineTimer deadline, CallerHandle::Sign
return ret; return ret;
} }
// Called from launcher's thread exclusively. Call me with mutex locked.
void LauncherHandle::wakeUpIfWaitingFor(CallerHandle::SignalType newSignal)
{
QTC_ASSERT(isCalledFromLaunchersThread(), return);
// TODO: should we always wake up in case m_waitingFor != NoSignal?
// The matching signal came
const bool signalMatched = (m_waitingFor == newSignal);
// E.g. if we are waiting for ReadyRead and we got Finished or Error signal instead -> wake it, too.
const bool finishedOrErrorWhileWaiting =
(m_waitingFor != CallerHandle::SignalType::NoSignal)
&& ((newSignal == CallerHandle::SignalType::Finished) || (newSignal == CallerHandle::SignalType::Error));
// Wake up, flush and continue waiting.
// E.g. when being in waitingForFinished() state and Started or ReadyRead signal came.
const bool continueWaitingAfterFlushing =
((m_waitingFor == CallerHandle::SignalType::Finished) && (newSignal != CallerHandle::SignalType::Finished))
|| ((m_waitingFor == CallerHandle::SignalType::ReadyRead) && (newSignal == CallerHandle::SignalType::Started));
const bool shouldWake = signalMatched
|| finishedOrErrorWhileWaiting
|| continueWaitingAfterFlushing;
if (shouldWake)
m_waitCondition.wakeOne();
}
// Called from launcher's thread exclusively. Call me with mutex locked. // Called from launcher's thread exclusively. Call me with mutex locked.
void LauncherHandle::flushCaller() void LauncherHandle::flushCaller()
{ {
@@ -588,6 +498,8 @@ void LauncherHandle::flushCaller()
if (!m_callerHandle) if (!m_callerHandle)
return; return;
m_waitCondition.wakeOne();
// call in callers thread // call in callers thread
QMetaObject::invokeMethod(m_callerHandle, &CallerHandle::flush); QMetaObject::invokeMethod(m_callerHandle, &CallerHandle::flush);
} }
@@ -596,9 +508,6 @@ void LauncherHandle::handlePacket(LauncherPacketType type, const QByteArray &pay
{ {
QTC_ASSERT(isCalledFromLaunchersThread(), return); QTC_ASSERT(isCalledFromLaunchersThread(), return);
switch (type) { switch (type) {
case LauncherPacketType::ProcessError:
handleErrorPacket(payload);
break;
case LauncherPacketType::ProcessStarted: case LauncherPacketType::ProcessStarted:
handleStartedPacket(payload); handleStartedPacket(payload);
break; break;
@@ -608,32 +517,18 @@ void LauncherHandle::handlePacket(LauncherPacketType type, const QByteArray &pay
case LauncherPacketType::ReadyReadStandardError: case LauncherPacketType::ReadyReadStandardError:
handleReadyReadStandardError(payload); handleReadyReadStandardError(payload);
break; break;
case LauncherPacketType::ProcessFinished: case LauncherPacketType::ProcessDone:
handleFinishedPacket(payload); handleDonePacket(payload);
break; break;
default: default:
QTC_ASSERT(false, break); QTC_ASSERT(false, break);
} }
} }
void LauncherHandle::handleErrorPacket(const QByteArray &packetData)
{
QTC_ASSERT(isCalledFromLaunchersThread(), return);
QMutexLocker locker(&m_mutex);
wakeUpIfWaitingFor(CallerHandle::SignalType::Error);
if (!m_callerHandle)
return;
const auto packet = LauncherPacket::extractPacket<ProcessErrorPacket>(m_token, packetData);
m_callerHandle->appendSignal(new ErrorSignal(packet.error, packet.errorString));
flushCaller();
}
void LauncherHandle::handleStartedPacket(const QByteArray &packetData) void LauncherHandle::handleStartedPacket(const QByteArray &packetData)
{ {
QTC_ASSERT(isCalledFromLaunchersThread(), return); QTC_ASSERT(isCalledFromLaunchersThread(), return);
QMutexLocker locker(&m_mutex); QMutexLocker locker(&m_mutex);
wakeUpIfWaitingFor(CallerHandle::SignalType::Started);
if (!m_callerHandle) if (!m_callerHandle)
return; return;
@@ -646,7 +541,6 @@ void LauncherHandle::handleReadyReadStandardOutput(const QByteArray &packetData)
{ {
QTC_ASSERT(isCalledFromLaunchersThread(), return); QTC_ASSERT(isCalledFromLaunchersThread(), return);
QMutexLocker locker(&m_mutex); QMutexLocker locker(&m_mutex);
wakeUpIfWaitingFor(CallerHandle::SignalType::ReadyRead);
if (!m_callerHandle) if (!m_callerHandle)
return; return;
@@ -662,7 +556,6 @@ void LauncherHandle::handleReadyReadStandardError(const QByteArray &packetData)
{ {
QTC_ASSERT(isCalledFromLaunchersThread(), return); QTC_ASSERT(isCalledFromLaunchersThread(), return);
QMutexLocker locker(&m_mutex); QMutexLocker locker(&m_mutex);
wakeUpIfWaitingFor(CallerHandle::SignalType::ReadyRead);
if (!m_callerHandle) if (!m_callerHandle)
return; return;
@@ -674,27 +567,22 @@ void LauncherHandle::handleReadyReadStandardError(const QByteArray &packetData)
flushCaller(); flushCaller();
} }
void LauncherHandle::handleFinishedPacket(const QByteArray &packetData) void LauncherHandle::handleDonePacket(const QByteArray &packetData)
{ {
QTC_ASSERT(isCalledFromLaunchersThread(), return); QTC_ASSERT(isCalledFromLaunchersThread(), return);
QMutexLocker locker(&m_mutex); QMutexLocker locker(&m_mutex);
wakeUpIfWaitingFor(CallerHandle::SignalType::Finished);
if (!m_callerHandle) if (!m_callerHandle)
return; return;
const auto packet = LauncherPacket::extractPacket<ProcessFinishedPacket>(m_token, packetData); const auto packet = LauncherPacket::extractPacket<ProcessDonePacket>(m_token, packetData);
const QByteArray stdOut = packet.stdOut; const QByteArray stdOut = packet.stdOut;
const QByteArray stdErr = packet.stdErr; const QByteArray stdErr = packet.stdErr;
const QProcess::ProcessError error = packet.error; const ProcessResultData result = { packet.exitCode, packet.exitStatus,
const QString errorString = packet.errorString; packet.error, packet.errorString };
// We assume that if error is UnknownError, everything went fine.
// By default QProcess returns "Unknown error" for errorString()
if (error != QProcess::UnknownError)
m_callerHandle->appendSignal(new ErrorSignal(error, errorString));
if (!stdOut.isEmpty() || !stdErr.isEmpty()) if (!stdOut.isEmpty() || !stdErr.isEmpty())
m_callerHandle->appendSignal(new ReadyReadSignal(stdOut, stdErr)); m_callerHandle->appendSignal(new ReadyReadSignal(stdOut, stdErr));
m_callerHandle->appendSignal(new FinishedSignal(packet.exitStatus, packet.exitCode)); m_callerHandle->appendSignal(new DoneSignal(result));
flushCaller(); flushCaller();
} }
@@ -712,13 +600,15 @@ void LauncherHandle::handleSocketError(const QString &message)
QTC_ASSERT(isCalledFromLaunchersThread(), return); QTC_ASSERT(isCalledFromLaunchersThread(), return);
m_socketError = true; // TODO: ??? m_socketError = true; // TODO: ???
QMutexLocker locker(&m_mutex); QMutexLocker locker(&m_mutex);
wakeUpIfWaitingFor(CallerHandle::SignalType::Error);
if (!m_callerHandle) if (!m_callerHandle)
return; return;
// TODO: FailedToStart may be wrong in case process has already started
const QString errorString = QCoreApplication::translate("Utils::QtcProcess", const QString errorString = QCoreApplication::translate("Utils::QtcProcess",
"Internal socket error: %1").arg(message); "Internal socket error: %1").arg(message);
m_callerHandle->appendSignal(new ErrorSignal(QProcess::FailedToStart, errorString)); const ProcessResultData result = { 0, QProcess::NormalExit, QProcess::FailedToStart,
errorString };
m_callerHandle->appendSignal(new DoneSignal(result));
flushCaller(); flushCaller();
} }
@@ -874,11 +764,10 @@ void LauncherSocket::handleSocketDataAvailable()
LauncherHandle *handle = handleForToken(m_packetParser.token()); LauncherHandle *handle = handleForToken(m_packetParser.token());
if (handle) { if (handle) {
switch (m_packetParser.type()) { switch (m_packetParser.type()) {
case LauncherPacketType::ProcessError:
case LauncherPacketType::ProcessStarted: case LauncherPacketType::ProcessStarted:
case LauncherPacketType::ReadyReadStandardOutput: case LauncherPacketType::ReadyReadStandardOutput:
case LauncherPacketType::ReadyReadStandardError: case LauncherPacketType::ReadyReadStandardError:
case LauncherPacketType::ProcessFinished: case LauncherPacketType::ProcessDone:
handle->handlePacket(m_packetParser.type(), m_packetParser.packetData()); handle->handlePacket(m_packetParser.type(), m_packetParser.packetData());
break; break;
default: default:

View File

@@ -51,10 +51,9 @@ namespace Internal {
class LauncherInterfacePrivate; class LauncherInterfacePrivate;
class LauncherHandle; class LauncherHandle;
class LauncherSignal; class LauncherSignal;
class ErrorSignal;
class StartedSignal; class StartedSignal;
class ReadyReadSignal; class ReadyReadSignal;
class FinishedSignal; class DoneSignal;
// All the methods and data fields in this class are called / accessed from the caller's thread. // All the methods and data fields in this class are called / accessed from the caller's thread.
// Exceptions are explicitly marked. // Exceptions are explicitly marked.
@@ -64,10 +63,9 @@ class CallerHandle : public QObject
public: public:
enum class SignalType { enum class SignalType {
NoSignal, NoSignal,
Error,
Started, Started,
ReadyRead, ReadyRead,
Finished Done
}; };
Q_ENUM(SignalType) Q_ENUM(SignalType)
CallerHandle(QObject *parent, quintptr token) CallerHandle(QObject *parent, quintptr token)
@@ -82,8 +80,8 @@ public:
bool waitForFinished(int msecs); bool waitForFinished(int msecs);
// Returns the list of flushed signals. // Returns the list of flushed signals.
QList<SignalType> flush(); void flush();
QList<SignalType> flushFor(SignalType signalType); bool flushFor(SignalType signalType);
bool shouldFlush() const; bool shouldFlush() const;
// Called from launcher's thread exclusively. // Called from launcher's thread exclusively.
void appendSignal(LauncherSignal *launcherSignal); void appendSignal(LauncherSignal *launcherSignal);
@@ -99,9 +97,6 @@ public:
QByteArray readAllStandardError(); QByteArray readAllStandardError();
qint64 processId() const; qint64 processId() const;
ProcessResultData resultData() const;
void setErrorString(const QString &str);
void start(const QString &program, const QStringList &arguments); void start(const QString &program, const QStringList &arguments);
// Called from caller's or launcher's thread. // Called from caller's or launcher's thread.
@@ -116,9 +111,8 @@ public:
void setProcessSetupData(const ProcessSetupData::Ptr &setup); void setProcessSetupData(const ProcessSetupData::Ptr &setup);
signals: signals:
void errorOccurred(QProcess::ProcessError error);
void started(); void started();
void finished(); void done(const Utils::ProcessResultData &resultData);
void readyReadStandardOutput(); void readyReadStandardOutput();
void readyReadStandardError(); void readyReadStandardError();
@@ -142,10 +136,9 @@ private:
return tmp; return tmp;
} }
void handleError(const ErrorSignal *launcherSignal);
void handleStarted(const StartedSignal *launcherSignal); void handleStarted(const StartedSignal *launcherSignal);
void handleReadyRead(const ReadyReadSignal *launcherSignal); void handleReadyRead(const ReadyReadSignal *launcherSignal);
void handleFinished(const FinishedSignal *launcherSignal); void handleDone(const DoneSignal *launcherSignal);
// Lives in launcher's thread. Modified from caller's thread. // Lives in launcher's thread. Modified from caller's thread.
LauncherHandle *m_launcherHandle = nullptr; LauncherHandle *m_launcherHandle = nullptr;
@@ -162,7 +155,6 @@ private:
int m_processId = 0; int m_processId = 0;
QByteArray m_stdout; QByteArray m_stdout;
QByteArray m_stderr; QByteArray m_stderr;
ProcessResultData m_result;
QString m_command; QString m_command;
QStringList m_arguments; QStringList m_arguments;
@@ -195,17 +187,13 @@ public:
private: private:
// Called from caller's thread exclusively. // Called from caller's thread exclusively.
bool doWaitForSignal(QDeadlineTimer deadline, CallerHandle::SignalType newSignal); bool doWaitForSignal(QDeadlineTimer deadline, CallerHandle::SignalType newSignal);
// Called from launcher's thread exclusively. Call me with mutex locked.
void wakeUpIfWaitingFor(CallerHandle::SignalType newSignal);
// Called from launcher's thread exclusively. Call me with mutex locked. // Called from launcher's thread exclusively. Call me with mutex locked.
void flushCaller(); void flushCaller();
// Called from launcher's thread exclusively. // Called from launcher's thread exclusively.
void handleErrorPacket(const QByteArray &packetData);
void handleStartedPacket(const QByteArray &packetData); void handleStartedPacket(const QByteArray &packetData);
void handleReadyReadStandardOutput(const QByteArray &packetData); void handleReadyReadStandardOutput(const QByteArray &packetData);
void handleReadyReadStandardError(const QByteArray &packetData); void handleReadyReadStandardError(const QByteArray &packetData);
void handleFinishedPacket(const QByteArray &packetData); void handleDonePacket(const QByteArray &packetData);
// Called from caller's or launcher's thread. // Called from caller's or launcher's thread.
bool isCalledFromLaunchersThread() const; bool isCalledFromLaunchersThread() const;

View File

@@ -93,8 +93,6 @@ public:
virtual qint64 processId() const = 0; virtual qint64 processId() const = 0;
virtual QProcess::ProcessState state() const = 0; virtual QProcess::ProcessState state() const = 0;
virtual ProcessResultData resultData() const = 0;
virtual bool waitForStarted(int msecs) = 0; virtual bool waitForStarted(int msecs) = 0;
virtual bool waitForReadyRead(int msecs) = 0; virtual bool waitForReadyRead(int msecs) = 0;
virtual bool waitForFinished(int msecs) = 0; virtual bool waitForFinished(int msecs) = 0;
@@ -104,8 +102,7 @@ public:
signals: signals:
void started(); void started();
void finished(); void done(const Utils::ProcessResultData &resultData);
void errorOccurred(QProcess::ProcessError error);
void readyReadStandardOutput(); void readyReadStandardOutput();
void readyReadStandardError(); void readyReadStandardError();
@@ -126,8 +123,7 @@ public:
{ {
m_target->setParent(this); m_target->setParent(this);
connect(m_target, &ProcessInterface::started, this, &ProcessInterface::started); connect(m_target, &ProcessInterface::started, this, &ProcessInterface::started);
connect(m_target, &ProcessInterface::finished, this, &ProcessInterface::finished); connect(m_target, &ProcessInterface::done, this, &ProcessInterface::done);
connect(m_target, &ProcessInterface::errorOccurred, this, &ProcessInterface::errorOccurred);
connect(m_target, &ProcessInterface::readyReadStandardOutput, connect(m_target, &ProcessInterface::readyReadStandardOutput,
this, &ProcessInterface::readyReadStandardOutput); this, &ProcessInterface::readyReadStandardOutput);
connect(m_target, &ProcessInterface::readyReadStandardError, connect(m_target, &ProcessInterface::readyReadStandardError,
@@ -147,8 +143,6 @@ public:
qint64 processId() const override { return m_target->processId(); } qint64 processId() const override { return m_target->processId(); }
QProcess::ProcessState state() const override { return m_target->state(); } QProcess::ProcessState state() const override { return m_target->state(); }
ProcessResultData resultData() const override { return m_target->resultData(); };
bool waitForStarted(int msecs) override { return m_target->waitForStarted(msecs); } bool waitForStarted(int msecs) override { return m_target->waitForStarted(msecs); }
bool waitForReadyRead(int msecs) override { return m_target->waitForReadyRead(msecs); } bool waitForReadyRead(int msecs) override { return m_target->waitForReadyRead(msecs); }
bool waitForFinished(int msecs) override { return m_target->waitForFinished(msecs); } bool waitForFinished(int msecs) override { return m_target->waitForFinished(msecs); }

View File

@@ -221,7 +221,6 @@ protected:
void defaultStart(); void defaultStart();
private: private:
virtual void setErrorString(const QString &str) = 0;
virtual void doDefaultStart(const QString &program, const QStringList &arguments) = 0; virtual void doDefaultStart(const QString &program, const QStringList &arguments) = 0;
bool dissolveCommand(QString *program, QStringList *arguments); bool dissolveCommand(QString *program, QStringList *arguments);
bool ensureProgramExists(const QString &program); bool ensureProgramExists(const QString &program);
@@ -290,9 +289,9 @@ bool DefaultImpl::dissolveCommand(QString *program, QStringList *arguments)
*arguments = QStringList(); *arguments = QStringList();
} else { } else {
if (!success) { if (!success) {
setErrorString(tr("Error in command line.")); const ProcessResultData result = { 0, QProcess::NormalExit, QProcess::FailedToStart,
// TODO: in fact it's WrongArgumentsFailure tr("Error in command line.") };
emit errorOccurred(QProcess::FailedToStart); emit done(result);
return false; return false;
} }
*arguments = processArgs.toUnixArgs(); *arguments = processArgs.toUnixArgs();
@@ -319,9 +318,11 @@ bool DefaultImpl::ensureProgramExists(const QString &program)
if (programFilePath.exists() && programFilePath.isExecutableFile()) if (programFilePath.exists() && programFilePath.isExecutableFile())
return true; return true;
setErrorString(QLatin1String("The program \"%1\" does not exist or is not executable.") const QString errorString = tr("The program \"%1\" does not exist or is not executable.")
.arg(program)); .arg(program);
emit errorOccurred(QProcess::FailedToStart); const ProcessResultData result = { 0, QProcess::NormalExit, QProcess::FailedToStart,
errorString };
emit done(result);
return false; return false;
} }
@@ -333,9 +334,9 @@ public:
connect(m_process, &QProcess::started, connect(m_process, &QProcess::started,
this, &QProcessImpl::handleStarted); this, &QProcessImpl::handleStarted);
connect(m_process, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished), connect(m_process, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished),
this, &ProcessInterface::finished); this, &QProcessImpl::handleFinished);
connect(m_process, &QProcess::errorOccurred, connect(m_process, &QProcess::errorOccurred,
this, &ProcessInterface::errorOccurred); this, &QProcessImpl::handleError);
connect(m_process, &QProcess::readyReadStandardOutput, connect(m_process, &QProcess::readyReadStandardOutput,
this, &ProcessInterface::readyReadStandardOutput); this, &ProcessInterface::readyReadStandardOutput);
connect(m_process, &QProcess::readyReadStandardError, connect(m_process, &QProcess::readyReadStandardError,
@@ -352,12 +353,6 @@ public:
void close() final { m_process->close(); } void close() final { m_process->close(); }
qint64 write(const QByteArray &data) final { return m_process->write(data); } qint64 write(const QByteArray &data) final { return m_process->write(data); }
ProcessResultData resultData() const final {
return { m_process->exitCode(), m_process->exitStatus(),
m_process->error(), m_process->errorString() };
};
void setErrorString(const QString &str) final { m_process->setErrorString(str); }
QProcess::ProcessState state() const final { return m_process->state(); } QProcess::ProcessState state() const final { return m_process->state(); }
qint64 processId() const final { return m_process->processId(); } qint64 processId() const final { return m_process->processId(); }
@@ -392,6 +387,23 @@ private:
m_process->processStartHandler()->handleProcessStarted(); m_process->processStartHandler()->handleProcessStarted();
emit started(); emit started();
} }
void handleError(QProcess::ProcessError error)
{
if (error != QProcess::FailedToStart)
return;
const ProcessResultData result = { m_process->exitCode(), m_process->exitStatus(),
error, m_process->errorString() };
emit done(result);
}
void handleFinished(int exitCode, QProcess::ExitStatus exitStatus)
{
const ProcessResultData result = { exitCode, exitStatus,
m_process->error(), m_process->errorString() };
emit done(result);
}
ProcessHelper *m_process; ProcessHelper *m_process;
}; };
@@ -409,12 +421,10 @@ public:
{ {
m_handle = LauncherInterface::registerHandle(this, token()); m_handle = LauncherInterface::registerHandle(this, token());
m_handle->setProcessSetupData(m_setup); m_handle->setProcessSetupData(m_setup);
connect(m_handle, &CallerHandle::errorOccurred,
this, &ProcessInterface::errorOccurred);
connect(m_handle, &CallerHandle::started, connect(m_handle, &CallerHandle::started,
this, &ProcessInterface::started); this, &ProcessInterface::started);
connect(m_handle, &CallerHandle::finished, connect(m_handle, &CallerHandle::done,
this, &ProcessInterface::finished); this, &ProcessInterface::done);
connect(m_handle, &CallerHandle::readyReadStandardOutput, connect(m_handle, &CallerHandle::readyReadStandardOutput,
this, &ProcessInterface::readyReadStandardOutput); this, &ProcessInterface::readyReadStandardOutput);
connect(m_handle, &CallerHandle::readyReadStandardError, connect(m_handle, &CallerHandle::readyReadStandardError,
@@ -440,9 +450,6 @@ public:
void close() final { m_handle->kill(); } // TODO: is it more like terminate or kill? void close() final { m_handle->kill(); } // TODO: is it more like terminate or kill?
qint64 write(const QByteArray &data) final { return m_handle->write(data); } qint64 write(const QByteArray &data) final { return m_handle->write(data); }
ProcessResultData resultData() const final { return m_handle->resultData(); };
void setErrorString(const QString &str) final { m_handle->setErrorString(str); }
QProcess::ProcessState state() const final { return m_handle->state(); } QProcess::ProcessState state() const final { return m_handle->state(); }
qint64 processId() const final { return m_handle->processId(); } qint64 processId() const final { return m_handle->processId(); }
@@ -473,12 +480,6 @@ static ProcessImpl defaultProcessImpl()
class QtcProcessPrivate : public QObject class QtcProcessPrivate : public QObject
{ {
public: public:
enum StartFailure {
NoFailure,
WrongCommandFailure,
OtherFailure
};
explicit QtcProcessPrivate(QtcProcess *parent) explicit QtcProcessPrivate(QtcProcess *parent)
: QObject(parent) : QObject(parent)
, q(parent) , q(parent)
@@ -503,10 +504,8 @@ public:
connect(m_process.get(), &ProcessInterface::started, connect(m_process.get(), &ProcessInterface::started,
this, &QtcProcessPrivate::emitStarted); this, &QtcProcessPrivate::emitStarted);
connect(m_process.get(), &ProcessInterface::finished, connect(m_process.get(), &ProcessInterface::done,
this, &QtcProcessPrivate::slotFinished); this, &QtcProcessPrivate::handleDone);
connect(m_process.get(), &ProcessInterface::errorOccurred,
this, &QtcProcessPrivate::handleError);
connect(m_process.get(), &ProcessInterface::readyReadStandardOutput, connect(m_process.get(), &ProcessInterface::readyReadStandardOutput,
this, &QtcProcessPrivate::handleReadyReadStandardOutput); this, &QtcProcessPrivate::handleReadyReadStandardOutput);
connect(m_process.get(), &ProcessInterface::readyReadStandardError, connect(m_process.get(), &ProcessInterface::readyReadStandardError,
@@ -559,9 +558,8 @@ public:
ProcessSetupData m_setup; ProcessSetupData m_setup;
void slotTimeout(); void slotTimeout();
void slotFinished(); void handleDone(const ProcessResultData &data);
void handleFinished(int exitCode, QProcess::ExitStatus status); void handleError();
void handleError(QProcess::ProcessError error);
void clearForRun(); void clearForRun();
void emitStarted(); void emitStarted();
@@ -572,6 +570,8 @@ public:
ProcessResult interpretExitCode(int exitCode); ProcessResult interpretExitCode(int exitCode);
ProcessResultData m_resultData;
QTextCodec *m_codec = QTextCodec::codecForLocale(); QTextCodec *m_codec = QTextCodec::codecForLocale();
QEventLoop *m_eventLoop = nullptr; QEventLoop *m_eventLoop = nullptr;
ProcessResult m_result = ProcessResult::StartFailed; ProcessResult m_result = ProcessResult::StartFailed;
@@ -581,7 +581,6 @@ public:
int m_hangTimerCount = 0; int m_hangTimerCount = 0;
int m_maxHangTimerCount = defaultMaxHangTimerCount; int m_maxHangTimerCount = defaultMaxHangTimerCount;
StartFailure m_startFailure = NoFailure;
bool m_timeOutMessageBoxEnabled = false; bool m_timeOutMessageBoxEnabled = false;
bool m_waitingForUser = false; bool m_waitingForUser = false;
@@ -605,7 +604,7 @@ void QtcProcessPrivate::clearForRun()
m_stdErr.clearForRun(); m_stdErr.clearForRun();
m_stdErr.codec = m_codec; m_stdErr.codec = m_codec;
m_result = ProcessResult::StartFailed; m_result = ProcessResult::StartFailed;
m_startFailure = NoFailure; m_resultData = {};
} }
ProcessResult QtcProcessPrivate::interpretExitCode(int exitCode) ProcessResult QtcProcessPrivate::interpretExitCode(int exitCode)
@@ -1028,12 +1027,7 @@ void QtcProcess::setResult(const ProcessResult &result)
ProcessResultData QtcProcess::resultData() const ProcessResultData QtcProcess::resultData() const
{ {
const ProcessResultData result = d->m_process ? d->m_process->resultData() return d->m_resultData;
: ProcessResultData();
// This code (255) is being returned by QProcess when FailedToStart error occurred
if (d->m_startFailure == QtcProcessPrivate::WrongCommandFailure)
return { 255, result.m_exitStatus, QProcess::FailedToStart, result.m_errorString };
return result;
} }
int QtcProcess::exitCode() const int QtcProcess::exitCode() const
@@ -1478,10 +1472,10 @@ void QtcProcess::runBlocking(EventLoopMode eventLoopMode)
setProperty(QTC_PROCESS_BLOCKING_TYPE, QVariant()); setProperty(QTC_PROCESS_BLOCKING_TYPE, QVariant());
} }
if (eventLoopMode == EventLoopMode::On) { if (eventLoopMode == EventLoopMode::On) {
// On Windows, start failure is triggered immediately if the // Start failure is triggered immediately if the executable cannot be found in the path.
// executable cannot be found in the path. Do not start the // In this case the process is left in NotRunning state.
// event loop in that case. // Do not start the event loop in that case.
if (d->m_startFailure == QtcProcessPrivate::NoFailure) { if (state() == QProcess::Starting) {
QTimer timer(this); QTimer timer(this);
connect(&timer, &QTimer::timeout, d, &QtcProcessPrivate::slotTimeout); connect(&timer, &QTimer::timeout, d, &QtcProcessPrivate::slotTimeout);
timer.setInterval(1000); timer.setInterval(1000);
@@ -1573,21 +1567,28 @@ void QtcProcessPrivate::slotTimeout()
} }
} }
void QtcProcessPrivate::slotFinished() void QtcProcessPrivate::handleDone(const ProcessResultData &data)
{ {
handleFinished(m_process->resultData().m_exitCode, m_process->resultData().m_exitStatus); m_resultData = data;
emitFinished();
} // This code (255) is being returned by QProcess when FailedToStart error occurred
if (m_resultData.m_error == QProcess::FailedToStart)
m_resultData.m_exitCode = 0xFF;
// HACK: See QIODevice::errorString() implementation.
if (m_resultData.m_error == QProcess::UnknownError)
m_resultData.m_errorString.clear();
if (m_resultData.m_error != QProcess::UnknownError)
handleError();
void QtcProcessPrivate::handleFinished(int exitCode, QProcess::ExitStatus status)
{
if (debug) if (debug)
qDebug() << Q_FUNC_INFO << exitCode << status; qDebug() << Q_FUNC_INFO << m_resultData.m_exitCode << m_resultData.m_exitStatus;
m_hangTimerCount = 0; m_hangTimerCount = 0;
switch (status) { switch (m_resultData.m_exitStatus) {
case QProcess::NormalExit: case QProcess::NormalExit:
m_result = interpretExitCode(exitCode); m_result = interpretExitCode(m_resultData.m_exitCode);
break; break;
case QProcess::CrashExit: case QProcess::CrashExit:
// Was hang detected before and killed? // Was hang detected before and killed?
@@ -1600,21 +1601,25 @@ void QtcProcessPrivate::handleFinished(int exitCode, QProcess::ExitStatus status
m_stdOut.handleRest(); m_stdOut.handleRest();
m_stdErr.handleRest(); m_stdErr.handleRest();
if (m_resultData.m_error != QProcess::FailedToStart)
emitFinished();
emit q->done();
} }
void QtcProcessPrivate::handleError(QProcess::ProcessError error) void QtcProcessPrivate::handleError()
{ {
m_hangTimerCount = 0; m_hangTimerCount = 0;
if (debug) if (debug)
qDebug() << Q_FUNC_INFO << error; qDebug() << Q_FUNC_INFO << m_resultData.m_error;
// Was hang detected before and killed? // Was hang detected before and killed?
if (m_result != ProcessResult::Hang) if (m_result != ProcessResult::Hang)
m_result = ProcessResult::StartFailed; m_result = ProcessResult::StartFailed;
m_startFailure = (error == QProcess::FailedToStart) ? WrongCommandFailure : OtherFailure;
if (m_eventLoop) if (m_eventLoop)
m_eventLoop->quit(); m_eventLoop->quit();
emitErrorOccurred(error); emitErrorOccurred(m_resultData.m_error);
} }
void QtcProcessPrivate::emitStarted() void QtcProcessPrivate::emitStarted()
@@ -1627,15 +1632,12 @@ void QtcProcessPrivate::emitFinished()
{ {
CALL_STACK_GUARD(); CALL_STACK_GUARD();
q->emitFinished(); q->emitFinished();
emit q->done();
} }
void QtcProcessPrivate::emitErrorOccurred(QProcess::ProcessError error) void QtcProcessPrivate::emitErrorOccurred(QProcess::ProcessError error)
{ {
CALL_STACK_GUARD(); CALL_STACK_GUARD();
emit q->errorOccurred(error); emit q->errorOccurred(error);
if (error == QProcess::FailedToStart)
emit q->done();
} }
void QtcProcessPrivate::emitReadyReadStandardOutput() void QtcProcessPrivate::emitReadyReadStandardOutput()

View File

@@ -442,14 +442,6 @@ void TerminalImpl::cleanupAfterStartFailure(const QString &errorMessage)
d->m_tempFile = nullptr; d->m_tempFile = nullptr;
} }
void TerminalImpl::finish(int exitCode, QProcess::ExitStatus exitStatus)
{
d->m_processId = 0;
d->m_result.m_exitCode = exitCode;
d->m_result.m_exitStatus = exitStatus;
emit finished();
}
void TerminalImpl::kickoffProcess() void TerminalImpl::kickoffProcess()
{ {
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
@@ -652,7 +644,7 @@ void TerminalImpl::readStubOutput()
emitError(QProcess::UnknownError, tr("Cannot obtain exit status from inferior: %1") emitError(QProcess::UnknownError, tr("Cannot obtain exit status from inferior: %1")
.arg(winErrorMessage(GetLastError()))); .arg(winErrorMessage(GetLastError())));
cleanupInferior(); cleanupInferior();
finish(chldStatus, QProcess::NormalExit); emitFinished(chldStatus, QProcess::NormalExit);
}); });
emit started(); emit started();
@@ -676,9 +668,9 @@ void TerminalImpl::readStubOutput()
d->m_processId = out.mid(4).toInt(); d->m_processId = out.mid(4).toInt();
emit started(); emit started();
} else if (out.startsWith("exit ")) { } else if (out.startsWith("exit ")) {
finish(out.mid(5).toInt(), QProcess::NormalExit); emitFinished(out.mid(5).toInt(), QProcess::NormalExit);
} else if (out.startsWith("crash ")) { } else if (out.startsWith("crash ")) {
finish(out.mid(6).toInt(), QProcess::CrashExit); emitFinished(out.mid(6).toInt(), QProcess::CrashExit);
} else { } else {
emitError(QProcess::UnknownError, msgUnexpectedOutput(out)); emitError(QProcess::UnknownError, msgUnexpectedOutput(out));
d->m_process.terminate(); d->m_process.terminate();
@@ -700,14 +692,14 @@ void TerminalImpl::stubExited()
if (d->m_hInferior != NULL) { if (d->m_hInferior != NULL) {
TerminateProcess(d->m_hInferior, (unsigned)-1); TerminateProcess(d->m_hInferior, (unsigned)-1);
cleanupInferior(); cleanupInferior();
finish(-1, QProcess::CrashExit); emitFinished(-1, QProcess::CrashExit);
} }
#else #else
stubServerShutdown(); stubServerShutdown();
delete d->m_tempFile; delete d->m_tempFile;
d->m_tempFile = nullptr; d->m_tempFile = nullptr;
if (d->m_processId) if (d->m_processId)
finish(-1, QProcess::CrashExit); emitFinished(-1, QProcess::CrashExit);
#endif #endif
} }
@@ -741,17 +733,22 @@ qint64 TerminalImpl::processId() const
return d->m_processId; return d->m_processId;
} }
ProcessResultData TerminalImpl::resultData() const void TerminalImpl::emitError(QProcess::ProcessError error, const QString &errorString)
{ {
return d->m_result; d->m_result.m_error = error;
d->m_result.m_errorString = errorString;
if (error == QProcess::FailedToStart)
emit done(d->m_result);
} }
void TerminalImpl::emitError(QProcess::ProcessError err, const QString &errorString) void TerminalImpl::emitFinished(int exitCode, QProcess::ExitStatus exitStatus)
{ {
d->m_result.m_error = err; d->m_processId = 0;
d->m_result.m_errorString = errorString; d->m_result.m_exitCode = exitCode;
emit errorOccurred(err); d->m_result.m_exitStatus = exitStatus;
emit done(d->m_result);
} }
} // Internal } // Internal
} // Utils } // Utils

View File

@@ -64,7 +64,6 @@ public:
QProcess::ProcessState state() const final; QProcess::ProcessState state() const final;
qint64 processId() const final; qint64 processId() const final;
ProcessResultData resultData() const final;
void kickoffProcess() final; // only debugger terminal, only non-windows void kickoffProcess() final; // only debugger terminal, only non-windows
void interrupt() final; // only debugger terminal, only non-windows void interrupt() final; // only debugger terminal, only non-windows
@@ -80,10 +79,10 @@ private:
void readStubOutput(); void readStubOutput();
void stubExited(); void stubExited();
void cleanupAfterStartFailure(const QString &errorMessage); void cleanupAfterStartFailure(const QString &errorMessage);
void finish(int exitCode, QProcess::ExitStatus exitStatus);
void killProcess(); void killProcess();
void killStub(); void killStub();
void emitError(QProcess::ProcessError err, const QString &errorString); void emitError(QProcess::ProcessError error, const QString &errorString);
void emitFinished(int exitCode, QProcess::ExitStatus exitStatus);
QString stubServerListen(); QString stubServerListen();
void stubServerShutdown(); void stubServerShutdown();
void cleanupStub(); void cleanupStub();

View File

@@ -133,61 +133,49 @@ void LauncherSocketHandler::handleSocketClosed()
qApp->quit(); qApp->quit();
} }
void LauncherSocketHandler::handleProcessError() void LauncherSocketHandler::handleProcessError(Process *process)
{ {
Process * proc = senderProcess();
// In case of FailedToStart we won't receive finished signal, so we send the error // In case of FailedToStart we won't receive finished signal, so we send the error
// packet and remove the process here and now. For all other errors we should expect // packet and remove the process here and now. For all other errors we should expect
// corresponding finished signal to appear, so we will send the error data together with // corresponding finished signal to appear, so we will send the error data together with
// the finished packet later on. // the finished packet later on.
if (proc->error() != QProcess::FailedToStart) if (process->error() == QProcess::FailedToStart)
return; handleProcessFinished(process);
ProcessErrorPacket packet(proc->token());
packet.error = proc->error();
packet.errorString = proc->errorString();
sendPacket(packet);
removeProcess(proc->token());
} }
void LauncherSocketHandler::handleProcessStarted() void LauncherSocketHandler::handleProcessStarted(Process *process)
{ {
Process *proc = senderProcess(); ProcessStartedPacket packet(process->token());
ProcessStartedPacket packet(proc->token()); packet.processId = process->processId();
packet.processId = proc->processId(); process->processStartHandler()->handleProcessStarted();
proc->processStartHandler()->handleProcessStarted();
sendPacket(packet); sendPacket(packet);
} }
void LauncherSocketHandler::handleReadyReadStandardOutput() void LauncherSocketHandler::handleReadyReadStandardOutput(Process *process)
{ {
Process * proc = senderProcess(); ReadyReadStandardOutputPacket packet(process->token());
ReadyReadStandardOutputPacket packet(proc->token()); packet.standardChannel = process->readAllStandardOutput();
packet.standardChannel = proc->readAllStandardOutput();
sendPacket(packet); sendPacket(packet);
} }
void LauncherSocketHandler::handleReadyReadStandardError() void LauncherSocketHandler::handleReadyReadStandardError(Process *process)
{ {
Process * proc = senderProcess(); ReadyReadStandardErrorPacket packet(process->token());
ReadyReadStandardErrorPacket packet(proc->token()); packet.standardChannel = process->readAllStandardError();
packet.standardChannel = proc->readAllStandardError();
sendPacket(packet); sendPacket(packet);
} }
void LauncherSocketHandler::handleProcessFinished() void LauncherSocketHandler::handleProcessFinished(Process *process)
{ {
Process * proc = senderProcess(); ProcessDonePacket packet(process->token());
ProcessFinishedPacket packet(proc->token()); packet.exitCode = process->exitCode();
packet.error = proc->error(); packet.exitStatus = process->exitStatus();
packet.errorString = proc->errorString(); packet.error = process->error();
packet.exitCode = proc->exitCode(); packet.errorString = process->errorString();
packet.exitStatus = proc->exitStatus(); packet.stdErr = process->readAllStandardError();
packet.stdErr = proc->readAllStandardError(); packet.stdOut = process->readAllStandardOutput();
packet.stdOut = proc->readAllStandardOutput();
sendPacket(packet); sendPacket(packet);
removeProcess(proc->token()); removeProcess(process->token());
} }
void LauncherSocketHandler::handleStartPacket() void LauncherSocketHandler::handleStartPacket()
@@ -263,7 +251,7 @@ void LauncherSocketHandler::handleStopPacket()
} else { } else {
// We got the client request to stop the starting / running process. // We got the client request to stop the starting / running process.
// We report process exit to the client. // We report process exit to the client.
ProcessFinishedPacket packet(process->token()); ProcessDonePacket packet(process->token());
packet.error = QProcess::Crashed; packet.error = QProcess::Crashed;
packet.exitCode = -1; packet.exitCode = -1;
packet.exitStatus = QProcess::CrashExit; packet.exitStatus = QProcess::CrashExit;
@@ -289,14 +277,14 @@ void LauncherSocketHandler::sendPacket(const LauncherPacket &packet)
Process *LauncherSocketHandler::setupProcess(quintptr token) Process *LauncherSocketHandler::setupProcess(quintptr token)
{ {
const auto p = new Process(token, this); const auto p = new Process(token, this);
connect(p, &QProcess::errorOccurred, this, &LauncherSocketHandler::handleProcessError); connect(p, &QProcess::started, this, [this, p] { handleProcessStarted(p); });
connect(p, &QProcess::started, this, &LauncherSocketHandler::handleProcessStarted); connect(p, &QProcess::errorOccurred, this, [this, p] { handleProcessError(p); });
connect(p, &QProcess::readyReadStandardOutput,
this, &LauncherSocketHandler::handleReadyReadStandardOutput);
connect(p, &QProcess::readyReadStandardError,
this, &LauncherSocketHandler::handleReadyReadStandardError);
connect(p, static_cast<void (QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished), connect(p, static_cast<void (QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished),
this, &LauncherSocketHandler::handleProcessFinished); this, [this, p] { handleProcessFinished(p); });
connect(p, &QProcess::readyReadStandardOutput,
this, [this, p] { handleReadyReadStandardOutput(p); });
connect(p, &QProcess::readyReadStandardError,
this, [this, p] { handleReadyReadStandardError(p); });
return p; return p;
} }
@@ -311,11 +299,6 @@ void LauncherSocketHandler::removeProcess(quintptr token)
ProcessReaper::reap(process); ProcessReaper::reap(process);
} }
Process *LauncherSocketHandler::senderProcess() const
{
return static_cast<Process *>(sender());
}
} // namespace Internal } // namespace Internal
} // namespace Utils } // namespace Utils

View File

@@ -52,11 +52,12 @@ private:
void handleSocketData(); void handleSocketData();
void handleSocketError(); void handleSocketError();
void handleSocketClosed(); void handleSocketClosed();
void handleProcessError();
void handleProcessStarted(); void handleProcessStarted(Process *process);
void handleReadyReadStandardOutput(); void handleProcessError(Process *process);
void handleReadyReadStandardError(); void handleProcessFinished(Process *process);
void handleProcessFinished(); void handleReadyReadStandardOutput(Process *process);
void handleReadyReadStandardError(Process *process);
void handleStartPacket(); void handleStartPacket();
void handleWritePacket(); void handleWritePacket();
@@ -67,7 +68,6 @@ private:
Process *setupProcess(quintptr token); Process *setupProcess(quintptr token);
void removeProcess(quintptr token); void removeProcess(quintptr token);
Process *senderProcess() const;
const QString m_serverPath; const QString m_serverPath;
QLocalSocket * const m_socket; QLocalSocket * const m_socket;