ApplicationLauncher: Use QtcProcess in terminal mode instead of ConsoleProcess

Change-Id: Ie1f8385fe151d65acc584456cb2328aca12b15cc
Reviewed-by: hjk <hjk@qt.io>
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: <github-actions-qt-creator@cristianadam.eu>
This commit is contained in:
Jarek Kobus
2022-01-24 16:01:03 +01:00
parent e28d2a88b2
commit 18fdad280a

View File

@@ -31,7 +31,6 @@
#include <coreplugin/icore.h> #include <coreplugin/icore.h>
#include <utils/consoleprocess.h>
#include <utils/fileutils.h> #include <utils/fileutils.h>
#include <utils/qtcassert.h> #include <utils/qtcassert.h>
#include <utils/qtcprocess.h> #include <utils/qtcprocess.h>
@@ -53,7 +52,7 @@
Encapsulates processes running in a console or as GUI processes, Encapsulates processes running in a console or as GUI processes,
captures debug output of GUI processes on Windows (outputDebugString()). captures debug output of GUI processes on Windows (outputDebugString()).
\sa Utils::ConsoleProcess \sa Utils::QtcProcess
*/ */
using namespace Utils; using namespace Utils;
@@ -76,15 +75,14 @@ public:
// Local // Local
void handleProcessStarted(); void handleProcessStarted();
void localGuiProcessError(); void localProcessError(QProcess::ProcessError error);
void localConsoleProcessError();
void readLocalStandardOutput(); void readLocalStandardOutput();
void readLocalStandardError(); void readLocalStandardError();
void cannotRetrieveLocalDebugOutput(); void cannotRetrieveLocalDebugOutput();
void checkLocalDebugOutput(qint64 pid, const QString &message); void checkLocalDebugOutput(qint64 pid, const QString &message);
void localProcessDone(int, QProcess::ExitStatus); void localProcessDone(int, QProcess::ExitStatus);
qint64 applicationPID() const; qint64 applicationPID() const;
bool isRunning() const; bool isLocalRunning() const;
// Remote // Remote
void doReportError(const QString &message, void doReportError(const QString &message,
@@ -102,9 +100,9 @@ public:
bool m_runAsRoot = false; bool m_runAsRoot = false;
// Local // Local
QtcProcess m_guiProcess; QtcProcess *m_localProcess = nullptr;
ConsoleProcess m_consoleProcess;
bool m_useTerminal = false; bool m_useTerminal = false;
QProcess::ProcessChannelMode m_processChannelMode;
// Keep track whether we need to emit a finished signal // Keep track whether we need to emit a finished signal
bool m_processRunning = false; bool m_processRunning = false;
@@ -125,38 +123,17 @@ public:
} // Internal } // Internal
ApplicationLauncherPrivate::ApplicationLauncherPrivate(ApplicationLauncher *parent) static QProcess::ProcessChannelMode defaultProcessChannelMode()
: q(parent), m_outputCodec(QTextCodec::codecForLocale())
{ {
if (ProjectExplorerPlugin::appOutputSettings().mergeChannels) { return ProjectExplorerPlugin::appOutputSettings().mergeChannels
m_guiProcess.setProcessChannelMode(QProcess::MergedChannels); ? QProcess::MergedChannels : QProcess::SeparateChannels;
} else { }
m_guiProcess.setProcessChannelMode(QProcess::SeparateChannels);
connect(&m_guiProcess, &QtcProcess::readyReadStandardError,
this, &ApplicationLauncherPrivate::readLocalStandardError);
}
connect(&m_guiProcess, &QtcProcess::readyReadStandardOutput,
this, &ApplicationLauncherPrivate::readLocalStandardOutput);
connect(&m_guiProcess, &QtcProcess::errorOccurred,
this, &ApplicationLauncherPrivate::localGuiProcessError);
connect(&m_guiProcess, &QtcProcess::finished, this, [this] {
localProcessDone(m_guiProcess.exitCode(), m_guiProcess.exitStatus());
});
connect(&m_guiProcess, &QtcProcess::started,
this, &ApplicationLauncherPrivate::handleProcessStarted);
connect(&m_guiProcess, &QtcProcess::errorOccurred,
q, &ApplicationLauncher::error);
connect(&m_consoleProcess, &ConsoleProcess::started,
this, &ApplicationLauncherPrivate::handleProcessStarted);
connect(&m_consoleProcess, &ConsoleProcess::errorOccurred,
this, &ApplicationLauncherPrivate::localConsoleProcessError);
connect(&m_consoleProcess, &ConsoleProcess::finished, this, [this] {
localProcessDone(m_consoleProcess.exitCode(), m_consoleProcess.exitStatus());
});
connect(&m_consoleProcess, &ConsoleProcess::errorOccurred,
q, &ApplicationLauncher::error);
ApplicationLauncherPrivate::ApplicationLauncherPrivate(ApplicationLauncher *parent)
: q(parent)
, m_processChannelMode(defaultProcessChannelMode())
, m_outputCodec(QTextCodec::codecForLocale())
{
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
connect(WinDebugInterface::instance(), &WinDebugInterface::cannotRetrieveDebugOutput, connect(WinDebugInterface::instance(), &WinDebugInterface::cannotRetrieveDebugOutput,
this, &ApplicationLauncherPrivate::cannotRetrieveLocalDebugOutput); this, &ApplicationLauncherPrivate::cannotRetrieveLocalDebugOutput);
@@ -167,13 +144,14 @@ ApplicationLauncherPrivate::ApplicationLauncherPrivate(ApplicationLauncher *pare
ApplicationLauncher::ApplicationLauncher(QObject *parent) : QObject(parent), ApplicationLauncher::ApplicationLauncher(QObject *parent) : QObject(parent),
d(std::make_unique<ApplicationLauncherPrivate>(this)) d(std::make_unique<ApplicationLauncherPrivate>(this))
{ } {
}
ApplicationLauncher::~ApplicationLauncher() = default; ApplicationLauncher::~ApplicationLauncher() = default;
void ApplicationLauncher::setProcessChannelMode(QProcess::ProcessChannelMode mode) void ApplicationLauncher::setProcessChannelMode(QProcess::ProcessChannelMode mode)
{ {
d->m_guiProcess.setProcessChannelMode(mode); d->m_processChannelMode = mode;
} }
void ApplicationLauncher::setUseTerminal(bool on) void ApplicationLauncher::setUseTerminal(bool on)
@@ -194,18 +172,11 @@ void ApplicationLauncher::stop()
void ApplicationLauncherPrivate::stop() void ApplicationLauncherPrivate::stop()
{ {
if (m_isLocal) { if (m_isLocal) {
if (!isRunning()) if (!isLocalRunning())
return; return;
if (m_useTerminal) { QTC_ASSERT(m_localProcess, return);
m_consoleProcess.stopProcess(); m_localProcess->stopProcess();
localProcessDone(0, QProcess::CrashExit); localProcessDone(0, QProcess::CrashExit);
} else {
m_guiProcess.terminate();
if (!m_guiProcess.waitForFinished(1000)) { // This is blocking, so be fast.
m_guiProcess.kill();
m_guiProcess.waitForFinished();
}
}
} else { } else {
if (m_stopRequested) if (m_stopRequested)
return; return;
@@ -225,7 +196,7 @@ void ApplicationLauncherPrivate::stop()
bool ApplicationLauncher::isRunning() const bool ApplicationLauncher::isRunning() const
{ {
return d->isRunning(); return d->isLocalRunning();
} }
bool ApplicationLauncher::isLocal() const bool ApplicationLauncher::isLocal() const
@@ -233,11 +204,11 @@ bool ApplicationLauncher::isLocal() const
return d->m_isLocal; return d->m_isLocal;
} }
bool ApplicationLauncherPrivate::isRunning() const bool ApplicationLauncherPrivate::isLocalRunning() const
{ {
if (m_useTerminal) if (!m_localProcess)
return m_consoleProcess.isRunning(); return false;
return m_guiProcess.state() != QProcess::NotRunning; return m_localProcess->state() != QProcess::NotRunning;
} }
ProcessHandle ApplicationLauncher::applicationPID() const ProcessHandle ApplicationLauncher::applicationPID() const
@@ -247,73 +218,71 @@ ProcessHandle ApplicationLauncher::applicationPID() const
qint64 ApplicationLauncherPrivate::applicationPID() const qint64 ApplicationLauncherPrivate::applicationPID() const
{ {
if (!isRunning()) if (!isLocalRunning())
return 0; return 0;
if (m_useTerminal) return m_localProcess->processId();
return m_consoleProcess.processId();
return m_guiProcess.processId();
} }
QString ApplicationLauncher::errorString() const QString ApplicationLauncher::errorString() const
{ {
if (d->m_isLocal) if (d->m_isLocal)
return d->m_useTerminal ? d->m_consoleProcess.errorString() : d->m_guiProcess.errorString(); return d->m_localProcess ? d->m_localProcess->errorString() : QString();
return d->m_remoteErrorString; return d->m_remoteErrorString;
} }
QProcess::ProcessError ApplicationLauncher::processError() const QProcess::ProcessError ApplicationLauncher::processError() const
{ {
if (d->m_isLocal) if (d->m_isLocal)
return d->m_useTerminal ? d->m_consoleProcess.error() : d->m_guiProcess.error(); return d->m_localProcess ? d->m_localProcess->error() : QProcess::UnknownError;
return d->m_remoteError; return d->m_remoteError;
} }
void ApplicationLauncherPrivate::localGuiProcessError() void ApplicationLauncherPrivate::localProcessError(QProcess::ProcessError error)
{ {
QString error; // TODO: why below handlings are different?
QProcess::ExitStatus status = QProcess::NormalExit; if (m_useTerminal) {
switch (m_guiProcess.error()) { emit q->appendMessage(m_localProcess->errorString(), ErrorMessageFormat);
case QProcess::FailedToStart: if (m_processRunning && m_localProcess->processId() == 0) {
error = ApplicationLauncher::tr("Failed to start program. Path or permissions wrong?"); m_processRunning = false;
break; emit q->processExited(-1, QProcess::NormalExit);
case QProcess::Crashed: }
status = QProcess::CrashExit; } else {
break; QString error;
default: QProcess::ExitStatus status = QProcess::NormalExit;
error = ApplicationLauncher::tr("Some error has occurred while running the program."); switch (m_localProcess->error()) {
} case QProcess::FailedToStart:
if (!error.isEmpty()) error = ApplicationLauncher::tr("Failed to start program. Path or permissions wrong?");
emit q->appendMessage(error, ErrorMessageFormat); break;
if (m_processRunning && !isRunning()) { case QProcess::Crashed:
m_processRunning = false; status = QProcess::CrashExit;
emit q->processExited(-1, status); break;
} default:
} error = ApplicationLauncher::tr("Some error has occurred while running the program.");
}
void ApplicationLauncherPrivate::localConsoleProcessError() if (!error.isEmpty())
{ emit q->appendMessage(error, ErrorMessageFormat);
emit q->appendMessage(m_consoleProcess.errorString(), ErrorMessageFormat); if (m_processRunning && !isLocalRunning()) {
if (m_processRunning && m_consoleProcess.processId() == 0) { m_processRunning = false;
m_processRunning = false; emit q->processExited(-1, status);
emit q->processExited(-1, QProcess::NormalExit); }
} }
emit q->error(error);
} }
void ApplicationLauncherPrivate::readLocalStandardOutput() void ApplicationLauncherPrivate::readLocalStandardOutput()
{ {
QByteArray data = m_guiProcess.readAllStandardOutput(); const QByteArray data = m_localProcess->readAllStandardOutput();
QString msg = m_outputCodec->toUnicode( const QString msg = m_outputCodec->toUnicode(
data.constData(), data.length(), &m_outputCodecState); data.constData(), data.length(), &m_outputCodecState);
emit q->appendMessage(msg, StdOutFormat, false); emit q->appendMessage(msg, StdOutFormat, false);
} }
void ApplicationLauncherPrivate::readLocalStandardError() void ApplicationLauncherPrivate::readLocalStandardError()
{ {
QByteArray data = m_guiProcess.readAllStandardError(); const QByteArray data = m_localProcess->readAllStandardError();
QString msg = m_outputCodec->toUnicode( const QString msg = m_outputCodec->toUnicode(
data.constData(), data.length(), &m_errorCodecState); data.constData(), data.length(), &m_errorCodecState);
emit q->appendMessage(msg, StdErrFormat, false); emit q->appendMessage(msg, StdErrFormat, false);
} }
@@ -365,17 +334,39 @@ void ApplicationLauncherPrivate::start(const Runnable &runnable, const IDevice::
m_isLocal = local; m_isLocal = local;
if (m_isLocal) { if (m_isLocal) {
QTC_ASSERT(!m_localProcess, return);
const QtcProcess::TerminalMode terminalMode = m_useTerminal
? QtcProcess::TerminalOn : QtcProcess::TerminalOff;
m_localProcess = new QtcProcess(terminalMode, this);
m_localProcess->setProcessChannelMode(m_processChannelMode);
if (m_processChannelMode == QProcess::SeparateChannels) {
connect(m_localProcess, &QtcProcess::readyReadStandardError,
this, &ApplicationLauncherPrivate::readLocalStandardError);
}
if (!m_useTerminal) {
connect(m_localProcess, &QtcProcess::readyReadStandardOutput,
this, &ApplicationLauncherPrivate::readLocalStandardOutput);
}
connect(m_localProcess, &QtcProcess::started,
this, &ApplicationLauncherPrivate::handleProcessStarted);
connect(m_localProcess, &QtcProcess::finished, this, [this] {
localProcessDone(m_localProcess->exitCode(), m_localProcess->exitStatus());
});
connect(m_localProcess, &QtcProcess::errorOccurred,
this, &ApplicationLauncherPrivate::localProcessError);
// Work around QTBUG-17529 (QtDeclarative fails with 'File name case mismatch' ...) // Work around QTBUG-17529 (QtDeclarative fails with 'File name case mismatch' ...)
const FilePath fixedPath = runnable.workingDirectory.normalizedPathName(); const FilePath fixedPath = runnable.workingDirectory.normalizedPathName();
m_guiProcess.setWorkingDirectory(fixedPath); m_localProcess->setWorkingDirectory(fixedPath);
m_consoleProcess.setWorkingDirectory(fixedPath);
Environment env = runnable.environment; Environment env = runnable.environment;
if (m_runAsRoot) if (m_runAsRoot)
RunControl::provideAskPassEntry(env); RunControl::provideAskPassEntry(env);
m_guiProcess.setEnvironment(env); m_localProcess->setEnvironment(env);
m_consoleProcess.setEnvironment(env);
m_processRunning = true; m_processRunning = true;
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
@@ -396,14 +387,10 @@ void ApplicationLauncherPrivate::start(const Runnable &runnable, const IDevice::
wrapped.addCommandLineAsArgs(cmdLine); wrapped.addCommandLineAsArgs(cmdLine);
cmdLine = wrapped; cmdLine = wrapped;
} }
// TODO: QtcProcess::setRunAsRoot() doens't work as expected currently
if (m_useTerminal) { // m_localProcess->setRunAsRoot(m_runAsRoot);
m_consoleProcess.setCommand(cmdLine); m_localProcess->setCommand(cmdLine);
m_consoleProcess.start(); m_localProcess->start();
} else {
m_guiProcess.setCommand(cmdLine);
m_guiProcess.start();
}
} else { } else {
QTC_ASSERT(m_state == Inactive, return); QTC_ASSERT(m_state == Inactive, return);