diff --git a/src/libs/utils/qtcprocess.cpp b/src/libs/utils/qtcprocess.cpp index e2f51bde0da..4bdad3bb518 100644 --- a/src/libs/utils/qtcprocess.cpp +++ b/src/libs/utils/qtcprocess.cpp @@ -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: diff --git a/src/libs/utils/terminalprocess.cpp b/src/libs/utils/terminalprocess.cpp index 4ca45fe9fb8..8869c11895e 100644 --- a/src/libs/utils/terminalprocess.cpp +++ b/src/libs/utils/terminalprocess.cpp @@ -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 to close this window..."); + return TerminalImpl::tr("Press 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; diff --git a/src/libs/utils/terminalprocess_p.h b/src/libs/utils/terminalprocess_p.h index 8befc4aae67..5e8341a11c2 100644 --- a/src/libs/utils/terminalprocess_p.h +++ b/src/libs/utils/terminalprocess_p.h @@ -26,6 +26,8 @@ #pragma once #include "processenums.h" +#include "processinterface.h" +#include "qtcassert.h" #include @@ -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();