Commit acc86aee authored by hjk's avatar hjk

ProjectExplorer: Move re-runnable decision to RunWorkers

A RunControl is re-runnable if all its workers are,
a RunWorker is re-runnable if it's Stopped and unless it
says otherwise.

Also ensure SimpleTargetRunner only reportStop() once
per run and make process error message re-usable.

Change-Id: I73f5fb724d3026ceb81d5e32a3a71b4814b2bca9
Reviewed-by: Christian Stenger's avatarChristian Stenger <christian.stenger@qt.io>
parent 34dad7e3
......@@ -75,7 +75,7 @@ ClangStaticAnalyzerToolRunner::ClangStaticAnalyzerToolRunner(RunControl *runCont
setDisplayName("ClangStaticAnalyzerRunner");
runControl->setDisplayName(tr("Clang Static Analyzer"));
runControl->setIcon(ProjectExplorer::Icons::ANALYZER_START_SMALL_TOOLBAR);
runControl->setSupportsReRunning(false);
setSupportsReRunning(false);
RunConfiguration *runConfiguration = runControl->runConfiguration();
auto tool = ClangStaticAnalyzerTool::instance();
......
......@@ -116,6 +116,7 @@ void DebuggerRunTool::start()
TaskHub::clearTasks(Debugger::Constants::TASK_CATEGORY_DEBUGGER_DEBUGINFO);
TaskHub::clearTasks(Debugger::Constants::TASK_CATEGORY_DEBUGGER_RUNTIME);
setupEngine();
DebuggerEngine *engine = m_engine;
QTC_ASSERT(engine, return);
......@@ -493,6 +494,16 @@ DebuggerRunTool::DebuggerRunTool(RunControl *runControl)
m_isQmlDebugging(qmlDebugging(runControl))
{
setDisplayName("DebuggerRunTool");
runControl->setIcon(ProjectExplorer::Icons::DEBUG_START_SMALL_TOOLBAR);
runControl->setPromptToStop([](bool *optionalPrompt) {
return RunControl::showPromptToStopDialog(
DebuggerRunTool::tr("Close Debugging Session"),
DebuggerRunTool::tr("A debugging session is still in progress. "
"Terminating the session in the current"
" state can leave the target in an inconsistent state."
" Would you still like to terminate it?"),
QString(), QString(), optionalPrompt);
});
}
DebuggerRunTool::DebuggerRunTool(RunControl *runControl, const DebuggerStartParameters &sp, QString *errorMessage)
......@@ -514,30 +525,22 @@ void DebuggerRunTool::setStartParameters(const DebuggerStartParameters &sp, QStr
void DebuggerRunTool::setRunParameters(const DebuggerRunParameters &rp, QString *errorMessage)
{
Q_UNUSED(errorMessage);
m_runParameters = rp;
}
void DebuggerRunTool::setupEngine()
{
// QML and/or mixed are not prepared for it.
setSupportsReRunning(!(m_runParameters.languages & QmlLanguage));
// FIXME: Disabled due to Android. Make Android device report available ports instead.
// int portsUsed = portsUsedByDebugger();
// if (portsUsed > device()->freePorts().count()) {
// if (errorMessage)
// *errorMessage = tr("Cannot debug: Not enough free ports available.");
// reportFailure(tr("Cannot debug: Not enough free ports available."));
// return;
// }
m_runParameters = rp;
// QML and/or mixed are not prepared for it.
runControl()->setSupportsReRunning(!(rp.languages & QmlLanguage));
runControl()->setIcon(ProjectExplorer::Icons::DEBUG_START_SMALL_TOOLBAR);
runControl()->setPromptToStop([](bool *optionalPrompt) {
return RunControl::showPromptToStopDialog(
DebuggerRunTool::tr("Close Debugging Session"),
DebuggerRunTool::tr("A debugging session is still in progress. "
"Terminating the session in the current"
" state can leave the target in an inconsistent state."
" Would you still like to terminate it?"),
QString(), QString(), optionalPrompt);
});
if (Internal::fixupParameters(m_runParameters, runControl(), m_errors)) {
m_engine = createEngine(m_runParameters.cppEngineType,
m_runParameters.masterEngineType,
......@@ -545,9 +548,7 @@ void DebuggerRunTool::setRunParameters(const DebuggerRunParameters &rp, QString
m_runParameters.useTerminal,
&m_errors);
if (!m_engine) {
QString msg = m_errors.join('\n');
if (errorMessage)
*errorMessage = msg;
reportFailure(m_errors.join('\n'));
return;
}
......
......@@ -82,11 +82,14 @@ public:
int portsUsedByDebugger() const;
void appendSolibSearchPath(const QString &str);
signals:
void aboutToNotifyInferiorSetupOk();
private:
Internal::DebuggerEngine *m_engine = nullptr; // Master engine
void setupEngine();
QPointer<Internal::DebuggerEngine> m_engine; // Master engine
Internal::DebuggerRunParameters m_runParameters;
QStringList m_errors;
bool m_isDying = false;
......
......@@ -263,36 +263,6 @@ QString GdbEngine::failedToStartMessage()
return tr("The gdb process failed to start.");
}
QString GdbEngine::errorMessage(QProcess::ProcessError error)
{
switch (error) {
case QProcess::FailedToStart:
return failedToStartMessage() + ' ' + tr("Either the "
"invoked program \"%1\" is missing, or you may have insufficient "
"permissions to invoke the program.\n%2")
.arg(runParameters().debugger.executable, m_gdbProc.errorString());
case QProcess::Crashed:
if (isDying())
return tr("The gdb process crashed some time after starting "
"successfully.");
else
return tr("The gdb process was ended forcefully");
case QProcess::Timedout:
return tr("The last waitFor...() function timed out. "
"The state of QProcess is unchanged, and you can try calling "
"waitFor...() again.");
case QProcess::WriteError:
return tr("An error occurred when attempting to write "
"to the gdb process. For example, the process may not be running, "
"or it may have closed its input channel.");
case QProcess::ReadError:
return tr("An error occurred when attempting to read from "
"the gdb process. For example, the process may not be running.");
default:
return tr("An unknown error in the gdb process occurred.");
}
}
#if 0
static void dump(const char *first, const char *middle, const QString & to)
{
......@@ -3880,7 +3850,7 @@ void GdbEngine::startGdb(const QStringList &args)
if (!QFileInfo(wd).isDir())
msg = failedToStartMessage() + ' ' + tr("The working directory \"%1\" is not usable.").arg(wd);
else
msg = errorMessage(QProcess::FailedToStart);
msg = RunWorker::userMessageForProcessError(QProcess::FailedToStart, rp.debugger.executable);
handleAdapterStartFailed(msg);
return;
}
......@@ -4064,7 +4034,11 @@ void GdbEngine::reloadDebuggingHelpers()
void GdbEngine::handleGdbError(QProcess::ProcessError error)
{
const QString msg = errorMessage(error);
QString program = runParameters().debugger.executable;
QString msg = RunWorker::userMessageForProcessError(error, program);
QString errorString = m_gdbProc.errorString();
if (!errorString.isEmpty())
msg += '\n' + errorString;
showMessage("HANDLE GDB ERROR: " + msg);
// Show a message box for asynchronously reported issues.
switch (error) {
......
......@@ -374,7 +374,6 @@ protected:
//
// Convenience Functions
//
QString errorMessage(QProcess::ProcessError error);
void showExecutionError(const QString &message);
QString failedToStartMessage();
......
......@@ -515,7 +515,7 @@ void AppOutputPane::reRunRunControl()
handleOldOutput(tab.window);
tab.window->scrollToBottom();
tab.runControl->initiateStart();
tab.runControl->initiateReStart();
}
void AppOutputPane::attachToRunControl()
......
......@@ -615,6 +615,7 @@ public:
int startWatchdogTimerId = -1;
int stopWatchdogInterval = 0; // 5000;
int stopWatchdogTimerId = -1;
bool supportsReRunning = true;
};
enum class RunControlState
......@@ -671,6 +672,7 @@ public:
void debugMessage(const QString &msg);
void initiateStart();
void initiateReStart();
void continueStart();
void initiateStop();
void continueStop();
......@@ -682,6 +684,7 @@ public:
void showError(const QString &msg);
static bool isAllowedTransition(RunControlState from, RunControlState to);
bool supportsReRunning() const;
RunControl *q;
QString displayName;
......@@ -699,7 +702,6 @@ public:
Utils::ProcessHandle applicationProcessHandle;
RunControlState state = RunControlState::Initialized;
bool supportsReRunning = true;
QList<QPointer<RunWorker>> m_workers;
......@@ -751,6 +753,12 @@ void RunControl::initiateStart()
d->initiateStart();
}
void RunControl::initiateReStart()
{
emit aboutToStart();
d->initiateReStart();
}
void RunControl::initiateStop()
{
d->initiateStop();
......@@ -808,6 +816,22 @@ void RunControlPrivate::initiateStart()
continueStart();
}
void RunControlPrivate::initiateReStart()
{
checkState(RunControlState::Stopped);
// Re-set worked on re-runs.
for (RunWorker *worker : m_workers) {
if (worker->d->state == RunWorkerState::Done)
worker->d->state = RunWorkerState::Initialized;
}
setState(RunControlState::Starting);
debugMessage("Queue: ReStarting");
continueStart();
}
void RunControlPrivate::continueStart()
{
checkState(RunControlState::Starting);
......@@ -870,6 +894,7 @@ void RunControlPrivate::continueStop()
{
debugMessage("Continue Stopping");
checkState(RunControlState::Stopping);
bool allDone = true;
for (RunWorker *worker : m_workers) {
if (worker) {
const QString &workerId = worker->d->id;
......@@ -881,15 +906,18 @@ void RunControlPrivate::continueStop()
break;
case RunWorkerState::Stopping:
debugMessage(" " + workerId + " was already Stopping. Keeping it that way");
allDone = false;
break;
case RunWorkerState::Starting:
worker->d->state = RunWorkerState::Stopping;
debugMessage(" " + workerId + " was Starting, queuing stop");
allDone = false;
QTimer::singleShot(0, worker, &RunWorker::initiateStop);
return; // Sic.
case RunWorkerState::Running:
debugMessage(" " + workerId + " was Running, queuing stop");
worker->d->state = RunWorkerState::Stopping;
allDone = false;
QTimer::singleShot(0, worker, &RunWorker::initiateStop);
return; // Sic.
case RunWorkerState::Done:
......@@ -903,8 +931,10 @@ void RunControlPrivate::continueStop()
debugMessage("Found unknown deleted worker");
}
}
if (allDone) {
debugMessage("All workers stopped. Set runControl to Stopped");
setState(RunControlState::Stopped);
}
}
void RunControlPrivate::onWorkerStarted(RunWorker *worker)
......@@ -1077,12 +1107,18 @@ void RunControl::setPromptToStop(const std::function<bool (bool *)> &promptToSto
bool RunControl::supportsReRunning() const
{
return d->supportsReRunning;
return d->supportsReRunning();
}
void RunControl::setSupportsReRunning(bool reRunningSupported)
bool RunControlPrivate::supportsReRunning() const
{
d->supportsReRunning = reRunningSupported;
for (RunWorker *worker : m_workers) {
if (!worker->d->supportsReRunning)
return false;
if (worker->d->state != RunWorkerState::Done)
return false;
}
return true;
}
bool RunControl::isRunning() const
......@@ -1185,7 +1221,6 @@ void RunControlPrivate::setState(RunControlState newState)
foreach (auto worker, m_workers)
if (worker)
worker->onFinished();
//state = RunControlState::Initialized; // Reset for potential re-running.
emit q->finished();
break;
default:
......@@ -1261,6 +1296,7 @@ SimpleTargetRunner::SimpleTargetRunner(RunControl *runControl)
void SimpleTargetRunner::start()
{
m_stopReported = false;
m_launcher.disconnect(this);
QString msg = RunControl::tr("Starting %1...").arg(m_runnable.displayName());
......@@ -1358,14 +1394,22 @@ void SimpleTargetRunner::onProcessFinished(int exitCode, QProcess::ExitStatus st
else
msg = tr("%2 exited with code %1").arg(exitCode);
appendMessage(msg.arg(m_runnable.displayName()), Utils::NormalMessageFormat);
if (!m_stopReported) {
m_stopReported = true;
reportStopped();
}
}
void SimpleTargetRunner::onProcessError(QProcess::ProcessError)
void SimpleTargetRunner::onProcessError(QProcess::ProcessError error)
{
QString msg = tr("%1 finished.");
appendMessage(msg.arg(m_runnable.displayName()), Utils::NormalMessageFormat);
if (error == QProcess::Timedout)
return; // No actual change on the process side.
QString msg = userMessageForProcessError(error, m_runnable.displayName());
appendMessage(msg, Utils::NormalMessageFormat);
if (!m_stopReported) {
m_stopReported = true;
reportStopped();
}
}
void SimpleTargetRunner::setRunnable(const Runnable &runnable)
......@@ -1519,6 +1563,49 @@ QVariant RunWorker::recordedData(const QString &channel) const
return d->data[channel];
}
void RunWorker::setSupportsReRunning(bool reRunningSupported)
{
d->supportsReRunning = reRunningSupported;
}
bool RunWorker::supportsReRunning() const
{
return d->supportsReRunning;
}
QString RunWorker::userMessageForProcessError(QProcess::ProcessError error, const QString &program)
{
QString failedToStart = tr("The process failed to start.");
QString msg = tr("An unknown error in the process occurred.");
switch (error) {
case QProcess::FailedToStart:
msg = failedToStart + ' ' + tr("Either the "
"invoked program \"%1\" is missing, or you may have insufficient "
"permissions to invoke the program.").arg(program);
break;
case QProcess::Crashed:
msg = tr("The process was ended forcefully");
break;
case QProcess::Timedout:
// "The last waitFor...() function timed out. "
// "The state of QProcess is unchanged, and you can try calling "
// "waitFor...() again."
return QString(); // sic!
case QProcess::WriteError:
msg = tr("An error occurred when attempting to write "
"to the process. For example, the process may not be running, "
"or it may have closed its input channel.");
break;
case QProcess::ReadError:
msg = tr("An error occurred when attempting to read from "
"the process. For example, the process may not be running.");
break;
case QProcess::UnknownError:
break;
}
return msg;
}
void RunWorker::start()
{
reportStarted();
......
......@@ -359,6 +359,10 @@ public:
void reportStopped();
void reportFailure(const QString &msg = QString());
void setSupportsReRunning(bool reRunningSupported);
bool supportsReRunning() const;
static QString userMessageForProcessError(QProcess::ProcessError, const QString &programName);
signals:
void dataReported(int channel, const QVariant &data);
......@@ -393,13 +397,13 @@ public:
~RunControl() override;
void initiateStart();
void initiateReStart();
void initiateStop();
bool promptToStop(bool *optionalPrompt = nullptr) const;
void setPromptToStop(const std::function<bool(bool *)> &promptToStop);
virtual bool supportsReRunning() const;
void setSupportsReRunning(bool reRunningSupported);
bool supportsReRunning() const;
virtual QString displayName() const;
void setDisplayName(const QString &displayName);
......@@ -512,6 +516,7 @@ private:
ApplicationLauncher m_launcher;
Runnable m_runnable;
bool m_stopReported = false;
};
} // namespace ProjectExplorer
......@@ -88,7 +88,7 @@ QmlProfilerRunner::QmlProfilerRunner(RunControl *runControl)
{
setDisplayName("QmlProfilerRunner");
runControl->setIcon(ProjectExplorer::Icons::ANALYZER_START_SMALL_TOOLBAR);
runControl->setSupportsReRunning(false);
setSupportsReRunning(false);
// Only wait 4 seconds for the 'Waiting for connection' on application output, then just try to connect
// (application output might be redirected / blocked)
......
......@@ -55,7 +55,7 @@ ValgrindToolRunner::ValgrindToolRunner(RunControl *runControl)
: RunWorker(runControl)
{
runControl->setIcon(ProjectExplorer::Icons::ANALYZER_START_SMALL_TOOLBAR);
runControl->setSupportsReRunning(false);
setSupportsReRunning(false);
if (IRunConfigurationAspect *aspect = runControl->runConfiguration()->extraAspect(ANALYZER_VALGRIND_SETTINGS))
m_settings = qobject_cast<ValgrindBaseSettings *>(aspect->currentSettings());
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment