Utils: Merge TerminalProcess into TerminalImpl

Change-Id: I60a03f315e84e17877d4ecd809a994f08934bedf
Reviewed-by: Jarek Kobus <jaroslaw.kobus@qt.io>
Reviewed-by: <github-actions-qt-creator@cristianadam.eu>
This commit is contained in:
hjk
2022-03-31 15:50:28 +02:00
parent e437cf7a71
commit 6297851f29
3 changed files with 106 additions and 189 deletions

View File

@@ -212,62 +212,6 @@ public:
bool keepRawData = true;
};
class TerminalImpl : public ProcessInterface
{
public:
TerminalImpl() : m_terminal(this)
{
connect(&m_terminal, &Internal::TerminalProcess::started,
this, &ProcessInterface::started);
connect(&m_terminal, &Internal::TerminalProcess::finished,
this, &ProcessInterface::finished);
connect(&m_terminal, &Internal::TerminalProcess::errorOccurred,
this, &ProcessInterface::errorOccurred);
}
~TerminalImpl() override
{
}
QByteArray readAllStandardOutput() override { QTC_CHECK(false); return {}; }
QByteArray readAllStandardError() override { QTC_CHECK(false); return {}; }
void start() override
{
m_terminal.setProcessImpl(m_setup->m_processImpl);
m_terminal.setTerminalMode(m_setup->m_terminalMode);
m_terminal.setAbortOnMetaChars(m_setup->m_abortOnMetaChars);
m_terminal.setCommand(m_setup->m_commandLine);
m_terminal.setWorkingDirectory(m_setup->m_workingDirectory);
m_terminal.setEnvironment(m_setup->m_environment);
m_terminal.start();
}
void interrupt() override { m_terminal.interrupt(); }
void terminate() override { m_terminal.stopProcess(); }
void kill() override { m_terminal.stopProcess(); }
void close() override { m_terminal.stopProcess(); }
qint64 write(const QByteArray &) override { QTC_CHECK(false); return -1; }
QProcess::ProcessError error() const override { return m_terminal.error(); }
QProcess::ProcessState state() const override { return m_terminal.state(); }
qint64 processId() const override { return m_terminal.processId(); }
int exitCode() const override { return m_terminal.exitCode(); }
QProcess::ExitStatus exitStatus() const override { return m_terminal.exitStatus(); }
QString errorString() const override { return m_terminal.errorString(); }
void setErrorString(const QString &) override { QTC_CHECK(false); }
// intentionally no-op without an assert
bool waitForStarted(int) override { return false; }
bool waitForReadyRead(int) override { QTC_CHECK(false); return false; }
// intentionally no-op without an assert
bool waitForFinished(int) override { return false; }
void kickoffProcess() override { m_terminal.kickoffProcess(); }
qint64 applicationMainThreadID() const override { return m_terminal.applicationMainThreadID(); }
private:
Internal::TerminalProcess m_terminal;
};
class DefaultImpl : public ProcessInterface
{
public:

View File

@@ -81,43 +81,43 @@ static QString modeOption(TerminalMode m)
static QString msgCommChannelFailed(const QString &error)
{
return TerminalProcess::tr("Cannot set up communication channel: %1").arg(error);
return TerminalImpl::tr("Cannot set up communication channel: %1").arg(error);
}
static QString msgPromptToClose()
{
// Shown in a terminal which might have a different character set on Windows.
return TerminalProcess::tr("Press <RETURN> to close this window...");
return TerminalImpl::tr("Press <RETURN> to close this window...");
}
static QString msgCannotCreateTempFile(const QString &why)
{
return TerminalProcess::tr("Cannot create temporary file: %1").arg(why);
return TerminalImpl::tr("Cannot create temporary file: %1").arg(why);
}
static QString msgCannotWriteTempFile()
{
return TerminalProcess::tr("Cannot write temporary file. Disk full?");
return TerminalImpl::tr("Cannot write temporary file. Disk full?");
}
static QString msgCannotCreateTempDir(const QString & dir, const QString &why)
{
return TerminalProcess::tr("Cannot create temporary directory \"%1\": %2").arg(dir, why);
return TerminalImpl::tr("Cannot create temporary directory \"%1\": %2").arg(dir, why);
}
static QString msgUnexpectedOutput(const QByteArray &what)
{
return TerminalProcess::tr("Unexpected output from helper program (%1).").arg(QString::fromLatin1(what));
return TerminalImpl::tr("Unexpected output from helper program (%1).").arg(QString::fromLatin1(what));
}
static QString msgCannotChangeToWorkDir(const FilePath &dir, const QString &why)
{
return TerminalProcess::tr("Cannot change to working directory \"%1\": %2").arg(dir.toString(), why);
return TerminalImpl::tr("Cannot change to working directory \"%1\": %2").arg(dir.toString(), why);
}
static QString msgCannotExecute(const QString & p, const QString &why)
{
return TerminalProcess::tr("Cannot execute \"%1\": %2").arg(p, why);
return TerminalImpl::tr("Cannot execute \"%1\": %2").arg(p, why);
}
class TerminalProcessPrivate
@@ -126,19 +126,14 @@ public:
TerminalProcessPrivate(QObject *parent)
: m_process(parent) {}
TerminalMode m_terminalMode = TerminalMode::On;
FilePath m_workingDir;
Environment m_environment;
qint64 m_processId = 0;
int m_exitCode = 0;
CommandLine m_commandLine;
QProcess::ExitStatus m_appStatus = QProcess::NormalExit;
QLocalServer m_stubServer;
QLocalSocket *m_stubSocket = nullptr;
QTemporaryFile *m_tempFile = nullptr;
QProcess::ProcessError m_error = QProcess::UnknownError;
QString m_errorString;
bool m_abortOnMetaChars = true;
// Used on Unix only
QtcProcess m_process;
@@ -156,50 +151,29 @@ public:
#endif
};
TerminalProcess::TerminalProcess(QObject *parent)
: QObject(parent), d(new TerminalProcessPrivate(this))
TerminalImpl::TerminalImpl()
: d(new TerminalProcessPrivate(this))
{
connect(&d->m_stubServer, &QLocalServer::newConnection,
this, &TerminalProcess::stubConnectionAvailable);
this, &TerminalImpl::stubConnectionAvailable);
d->m_process.setProcessChannelMode(QProcess::ForwardedChannels);
}
TerminalProcess::~TerminalProcess()
TerminalImpl::~TerminalImpl()
{
stopProcess();
delete d;
}
void TerminalProcess::setProcessImpl(ProcessImpl processImpl)
{
d->m_process.setProcessImpl(processImpl);
}
void TerminalProcess::setTerminalMode(TerminalMode mode)
{
QTC_ASSERT(mode != TerminalMode::Off, return);
d->m_terminalMode = mode;
}
void TerminalProcess::setCommand(const CommandLine &command)
{
d->m_commandLine = command;
}
void TerminalProcess::setAbortOnMetaChars(bool abort)
{
d->m_abortOnMetaChars = abort;
}
qint64 TerminalProcess::applicationMainThreadID() const
qint64 TerminalImpl::applicationMainThreadID() const
{
if (HostOsInfo::isWindowsHost())
return d->m_appMainThreadId;
return -1;
}
void TerminalProcess::start()
void TerminalImpl::start()
{
if (isRunning())
return;
@@ -211,13 +185,13 @@ void TerminalProcess::start()
QString pcmd;
QString pargs;
if (d->m_terminalMode != TerminalMode::Run) { // The debugger engines already pre-process the arguments.
pcmd = d->m_commandLine.executable().toString();
pargs = d->m_commandLine.arguments();
if (m_setup->m_terminalMode != TerminalMode::Run) { // The debugger engines already pre-process the arguments.
pcmd = m_setup->m_commandLine.executable().toString();
pargs = m_setup->m_commandLine.arguments();
} else {
ProcessArgs outArgs;
ProcessArgs::prepareCommand(d->m_commandLine, &pcmd, &outArgs,
&d->m_environment, &d->m_workingDir);
ProcessArgs::prepareCommand(m_setup->m_commandLine, &pcmd, &outArgs,
&m_setup->m_environment, &m_setup->m_workingDirectory);
pargs = outArgs.toWindowsArgs();
}
@@ -227,7 +201,7 @@ void TerminalProcess::start()
return;
}
QStringList env = d->m_environment.toStringList();
QStringList env = m_setup->m_environment.toStringList();
if (!env.isEmpty()) {
d->m_tempFile = new QTemporaryFile();
if (!d->m_tempFile->open()) {
@@ -274,7 +248,7 @@ void TerminalProcess::start()
d->m_pid = new PROCESS_INFORMATION;
ZeroMemory(d->m_pid, sizeof(PROCESS_INFORMATION));
QString workDir = d->m_workingDir.toUserOutput();
QString workDir = m_setup->m_workingDirectory.toUserOutput();
if (!workDir.isEmpty() && !workDir.endsWith(QLatin1Char('\\')))
workDir.append(QLatin1Char('\\'));
@@ -330,7 +304,7 @@ void TerminalProcess::start()
};
QStringList stubArgs;
stubArgs << modeOption(d->m_terminalMode)
stubArgs << modeOption(m_setup->m_terminalMode)
<< d->m_stubServer.fullServerName()
<< workDir
<< (d->m_tempFile ? d->m_tempFile->fileName() : QString())
@@ -356,27 +330,27 @@ void TerminalProcess::start()
d->processFinishedNotifier = new QWinEventNotifier(d->m_pid->hProcess, this);
connect(d->processFinishedNotifier, &QWinEventNotifier::activated,
this, &TerminalProcess::stubExited);
this, &TerminalImpl::stubExited);
#else
ProcessArgs::SplitError perr;
ProcessArgs pargs = ProcessArgs::prepareArgs(d->m_commandLine.arguments(),
ProcessArgs pargs = ProcessArgs::prepareArgs(m_setup->m_commandLine.arguments(),
&perr,
HostOsInfo::hostOs(),
&d->m_environment,
&d->m_workingDir,
d->m_abortOnMetaChars);
&m_setup->m_environment,
&m_setup->m_workingDirectory,
m_setup->m_abortOnMetaChars);
QString pcmd;
if (perr == ProcessArgs::SplitOk) {
pcmd = d->m_commandLine.executable().toString();
pcmd = m_setup->m_commandLine.executable().toString();
} else {
if (perr != ProcessArgs::FoundMeta) {
emitError(QProcess::FailedToStart, tr("Quoting error in command."));
return;
}
if (d->m_terminalMode == TerminalMode::Debug) {
if (m_setup->m_terminalMode == TerminalMode::Debug) {
// FIXME: QTCREATORBUG-2809
emitError(QProcess::FailedToStart, tr("Debugging complex shell commands in a terminal"
" is currently not supported."));
@@ -384,8 +358,8 @@ void TerminalProcess::start()
}
pcmd = qEnvironmentVariable("SHELL", "/bin/sh");
pargs = ProcessArgs::createUnixArgs(
{"-c", (ProcessArgs::quoteArg(d->m_commandLine.executable().toString())
+ ' ' + d->m_commandLine.arguments())});
{"-c", (ProcessArgs::quoteArg(m_setup->m_commandLine.executable().toString())
+ ' ' + m_setup->m_commandLine.arguments())});
}
ProcessArgs::SplitError qerr;
@@ -393,8 +367,8 @@ void TerminalProcess::start()
const ProcessArgs terminalArgs = ProcessArgs::prepareArgs(terminal.executeArgs,
&qerr,
HostOsInfo::hostOs(),
&d->m_environment,
&d->m_workingDir);
&m_setup->m_environment,
&m_setup->m_workingDirectory);
if (qerr != ProcessArgs::SplitOk) {
emitError(QProcess::FailedToStart, qerr == ProcessArgs::BadQuoting
? tr("Quoting error in terminal command.")
@@ -408,9 +382,9 @@ void TerminalProcess::start()
return;
}
d->m_environment.unset(QLatin1String("TERM"));
m_setup->m_environment.unset(QLatin1String("TERM"));
const QStringList env = d->m_environment.toStringList();
const QStringList env = m_setup->m_environment.toStringList();
if (!env.isEmpty()) {
d->m_tempFile = new QTemporaryFile();
if (!d->m_tempFile->open()) {
@@ -434,10 +408,10 @@ void TerminalProcess::start()
QStringList allArgs = terminalArgs.toUnixArgs();
allArgs << stubPath
<< modeOption(d->m_terminalMode)
<< modeOption(m_setup->m_terminalMode)
<< d->m_stubServer.fullServerName()
<< msgPromptToClose()
<< d->m_workingDir.path()
<< m_setup->m_workingDirectory.path()
<< (d->m_tempFile ? d->m_tempFile->fileName() : QString())
<< QString::number(getpid())
<< pcmd
@@ -446,8 +420,9 @@ void TerminalProcess::start()
if (terminal.needsQuotes)
allArgs = QStringList { ProcessArgs::joinArgs(allArgs) };
d->m_process.setEnvironment(d->m_environment);
d->m_process.setEnvironment(m_setup->m_environment);
d->m_process.setCommand({FilePath::fromString(terminal.command), allArgs});
d->m_process.setProcessImpl(m_setup->m_processImpl);
d->m_process.start();
if (!d->m_process.waitForStarted()) {
const QString msg = tr("Cannot start the terminal emulator \"%1\", change the setting in the "
@@ -456,14 +431,14 @@ void TerminalProcess::start()
return;
}
d->m_stubConnectTimer = new QTimer(this);
connect(d->m_stubConnectTimer, &QTimer::timeout, this, &TerminalProcess::stopProcess);
connect(d->m_stubConnectTimer, &QTimer::timeout, this, &TerminalImpl::stopProcess);
d->m_stubConnectTimer->setSingleShot(true);
d->m_stubConnectTimer->start(10000);
#endif
}
void TerminalProcess::cleanupAfterStartFailure(const QString &errorMessage)
void TerminalImpl::cleanupAfterStartFailure(const QString &errorMessage)
{
stubServerShutdown();
emitError(QProcess::FailedToStart, errorMessage);
@@ -471,7 +446,7 @@ void TerminalProcess::cleanupAfterStartFailure(const QString &errorMessage)
d->m_tempFile = nullptr;
}
void TerminalProcess::finish(int exitCode, QProcess::ExitStatus exitStatus)
void TerminalImpl::finish(int exitCode, QProcess::ExitStatus exitStatus)
{
d->m_processId = 0;
d->m_exitCode = exitCode;
@@ -479,7 +454,7 @@ void TerminalProcess::finish(int exitCode, QProcess::ExitStatus exitStatus)
emit finished();
}
void TerminalProcess::kickoffProcess()
void TerminalImpl::kickoffProcess()
{
#ifdef Q_OS_WIN
// Not used.
@@ -491,7 +466,7 @@ void TerminalProcess::kickoffProcess()
#endif
}
void TerminalProcess::interrupt()
void TerminalImpl::interrupt()
{
#ifdef Q_OS_WIN
// Not used.
@@ -503,7 +478,7 @@ void TerminalProcess::interrupt()
#endif
}
void TerminalProcess::killProcess()
void TerminalImpl::killProcess()
{
#ifdef Q_OS_WIN
if (d->m_hInferior != NULL) {
@@ -519,7 +494,7 @@ void TerminalProcess::killProcess()
d->m_processId = 0;
}
void TerminalProcess::killStub()
void TerminalImpl::killStub()
{
#ifdef Q_OS_WIN
if (d->m_pid) {
@@ -536,7 +511,7 @@ void TerminalProcess::killStub()
#endif
}
void TerminalProcess::stopProcess()
void TerminalImpl::stopProcess()
{
killProcess();
killStub();
@@ -549,7 +524,7 @@ void TerminalProcess::stopProcess()
}
}
bool TerminalProcess::isRunning() const
bool TerminalImpl::isRunning() const
{
#ifdef Q_OS_WIN
return d->m_pid != nullptr;
@@ -559,7 +534,7 @@ bool TerminalProcess::isRunning() const
#endif
}
QProcess::ProcessState TerminalProcess::state() const
QProcess::ProcessState TerminalImpl::state() const
{
#ifdef Q_OS_WIN
return (d->m_pid != nullptr) ? QProcess::Running : QProcess::NotRunning;
@@ -569,7 +544,7 @@ QProcess::ProcessState TerminalProcess::state() const
#endif
}
QString TerminalProcess::stubServerListen()
QString TerminalImpl::stubServerListen()
{
#ifdef Q_OS_WIN
if (d->m_stubServer.listen(QString::fromLatin1("creator-%1-%2")
@@ -604,7 +579,7 @@ QString TerminalProcess::stubServerListen()
#endif
}
void TerminalProcess::stubServerShutdown()
void TerminalImpl::stubServerShutdown()
{
#ifdef Q_OS_WIN
delete d->m_stubSocket;
@@ -625,7 +600,7 @@ void TerminalProcess::stubServerShutdown()
#endif
}
void TerminalProcess::stubConnectionAvailable()
void TerminalImpl::stubConnectionAvailable()
{
if (d->m_stubConnectTimer) {
delete d->m_stubConnectTimer;
@@ -633,10 +608,10 @@ void TerminalProcess::stubConnectionAvailable()
}
d->m_stubSocket = d->m_stubServer.nextPendingConnection();
connect(d->m_stubSocket, &QIODevice::readyRead, this, &TerminalProcess::readStubOutput);
connect(d->m_stubSocket, &QIODevice::readyRead, this, &TerminalImpl::readStubOutput);
if (HostOsInfo::isAnyUnixHost())
connect(d->m_stubSocket, &QLocalSocket::disconnected, this, &TerminalProcess::stubExited);
connect(d->m_stubSocket, &QLocalSocket::disconnected, this, &TerminalImpl::stubExited);
}
static QString errorMsg(int code)
@@ -644,16 +619,18 @@ static QString errorMsg(int code)
return QString::fromLocal8Bit(strerror(code));
}
void TerminalProcess::readStubOutput()
void TerminalImpl::readStubOutput()
{
while (d->m_stubSocket->canReadLine()) {
QByteArray out = d->m_stubSocket->readLine();
#ifdef Q_OS_WIN
out.chop(2); // \r\n
if (out.startsWith("err:chdir ")) {
emitError(QProcess::FailedToStart, msgCannotChangeToWorkDir(d->m_workingDir, winErrorMessage(out.mid(10).toInt())));
emitError(QProcess::FailedToStart,
msgCannotChangeToWorkDir(m_setup->m_workingDirectory, winErrorMessage(out.mid(10).toInt())));
} else if (out.startsWith("err:exec ")) {
emitError(QProcess::FailedToStart, msgCannotExecute(d->m_commandLine.executable().toUserOutput(), winErrorMessage(out.mid(9).toInt())));
emitError(QProcess::FailedToStart,
msgCannotExecute(m_setup->m_commandLine.executable().toUserOutput(), winErrorMessage(out.mid(9).toInt())));
} else if (out.startsWith("thread ")) { // Windows only
d->m_appMainThreadId = out.mid(7).toLongLong();
} else if (out.startsWith("pid ")) {
@@ -691,9 +668,11 @@ void TerminalProcess::readStubOutput()
#else
out.chop(1); // \n
if (out.startsWith("err:chdir ")) {
emitError(QProcess::FailedToStart, msgCannotChangeToWorkDir(d->m_workingDir, errorMsg(out.mid(10).toInt())));
emitError(QProcess::FailedToStart,
msgCannotChangeToWorkDir(m_setup->m_workingDirectory, errorMsg(out.mid(10).toInt())));
} else if (out.startsWith("err:exec ")) {
emitError(QProcess::FailedToStart, msgCannotExecute(d->m_commandLine.executable().toString(), errorMsg(out.mid(9).toInt())));
emitError(QProcess::FailedToStart,
msgCannotExecute(m_setup->m_commandLine.executable().toString(), errorMsg(out.mid(9).toInt())));
} else if (out.startsWith("spid ")) {
delete d->m_tempFile;
d->m_tempFile = nullptr;
@@ -713,7 +692,7 @@ void TerminalProcess::readStubOutput()
} // while
}
void TerminalProcess::stubExited()
void TerminalImpl::stubExited()
{
// The stub exit might get noticed before we read the pid for the kill on Windows
// or the error status elsewhere.
@@ -736,7 +715,7 @@ void TerminalProcess::stubExited()
#endif
}
void TerminalProcess::cleanupInferior()
void TerminalImpl::cleanupInferior()
{
#ifdef Q_OS_WIN
delete d->inferiorFinishedNotifier;
@@ -746,7 +725,7 @@ void TerminalProcess::cleanupInferior()
#endif
}
void TerminalProcess::cleanupStub()
void TerminalImpl::cleanupStub()
{
#ifdef Q_OS_WIN
stubServerShutdown();
@@ -761,42 +740,32 @@ void TerminalProcess::cleanupStub()
#endif
}
qint64 TerminalProcess::processId() const
qint64 TerminalImpl::processId() const
{
return d->m_processId;
}
int TerminalProcess::exitCode() const
int TerminalImpl::exitCode() const
{
return d->m_exitCode;
} // This will be the signal number if exitStatus == CrashExit
QProcess::ExitStatus TerminalProcess::exitStatus() const
QProcess::ExitStatus TerminalImpl::exitStatus() const
{
return d->m_appStatus;
}
void TerminalProcess::setWorkingDirectory(const FilePath &dir)
{
d->m_workingDir = dir;
}
void TerminalProcess::setEnvironment(const Environment &env)
{
d->m_environment = env;
}
QProcess::ProcessError TerminalProcess::error() const
QProcess::ProcessError TerminalImpl::error() const
{
return d->m_error;
}
QString TerminalProcess::errorString() const
QString TerminalImpl::errorString() const
{
return d->m_errorString;
}
void TerminalProcess::emitError(QProcess::ProcessError err, const QString &errorString)
void TerminalImpl::emitError(QProcess::ProcessError err, const QString &errorString)
{
d->m_error = err;
d->m_errorString = errorString;

View File

@@ -26,6 +26,8 @@
#pragma once
#include "processenums.h"
#include "processinterface.h"
#include "qtcassert.h"
#include <QProcess>
@@ -37,46 +39,48 @@ class FilePath;
namespace Internal {
class TerminalProcess : public QObject
class TerminalImpl : public ProcessInterface
{
Q_OBJECT
public:
explicit TerminalProcess(QObject *parent);
~TerminalProcess() override;
TerminalImpl();
~TerminalImpl() final;
void setProcessImpl(ProcessImpl processImpl);
void setTerminalMode(TerminalMode mode);
void setCommand(const CommandLine &command);
void setWorkingDirectory(const FilePath &dir);
void setEnvironment(const Environment &env);
QByteArray readAllStandardOutput() final { QTC_CHECK(false); return {}; }
QByteArray readAllStandardError() final { QTC_CHECK(false); return {}; }
QProcess::ProcessError error() const;
QString errorString() const;
qint64 write(const QByteArray &) final { QTC_CHECK(false); return -1; }
void setErrorString(const QString &) final { QTC_CHECK(false); }
void start();
void stopProcess();
void terminate() final { stopProcess(); }
void kill() final { stopProcess(); }
void close() final { stopProcess(); }
// intentionally no-op without an assert
bool waitForStarted(int) final { return false; }
bool waitForReadyRead(int) final { QTC_CHECK(false); return false; }
// intentionally no-op without an assert
bool waitForFinished(int) final { return false; }
QProcess::ProcessError error() const final;
QString errorString() const final;
void start() final;
QProcess::ProcessState state() const final;
qint64 processId() const final;
int exitCode() const final;
QProcess::ExitStatus exitStatus() const final;
void kickoffProcess() final; // only debugger terminal, only non-windows
void interrupt() final; // only debugger terminal, only non-windows
qint64 applicationMainThreadID() const final; // only debugger terminal, only windows (-1 otherwise)
private:
// OK, however, impl looks a bit different (!= NotRunning vs == Running).
// Most probably changing it into (== Running) should be OK.
bool isRunning() const;
QProcess::ProcessState state() const;
qint64 processId() const;
int exitCode() const;
QProcess::ExitStatus exitStatus() const;
void setAbortOnMetaChars(bool abort); // used only in sshDeviceProcess
void kickoffProcess(); // only debugger terminal, only non-windows
void interrupt(); // only debugger terminal, only non-windows
qint64 applicationMainThreadID() const; // only debugger terminal, only windows (-1 otherwise)
signals:
void started();
void finished();
void errorOccurred(QProcess::ProcessError error);
private:
void stopProcess();
void stubConnectionAvailable();
void readStubOutput();
void stubExited();