forked from qt-creator/qt-creator
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:
@@ -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);
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user