From bbe44bf42a0a408ceee2e0dcc11e972ead4c1a6e Mon Sep 17 00:00:00 2001 From: Jarek Kobus Date: Fri, 9 Jul 2021 09:09:05 +0200 Subject: [PATCH] Abstract QProcess interface Abstract part of QProcess interface into ProcessInterface pure virtual class. The current API of ProcessInterface is a part of QProcess API used currently to implement QtcProcess. Provide 2 implementations for it: QProcessImpl which uses QProcess and ProcessLauncherImpl which is currently empty implementation with assertions that no method is being called. Provide an additional switch to QtcProcess c'tor for selecting the preferred implementation, by default it selects QProcessImpl. This change doesn't influence any behavior. Change-Id: Ia5328401affe127f3d36870be8478202d4143480 Reviewed-by: Qt CI Bot Reviewed-by: hjk --- src/libs/utils/qtcprocess.cpp | 241 +++++++++++++++++++++++++++++++--- src/libs/utils/qtcprocess.h | 6 + 2 files changed, 229 insertions(+), 18 deletions(-) diff --git a/src/libs/utils/qtcprocess.cpp b/src/libs/utils/qtcprocess.cpp index 94c23921bd7..286e72a5735 100644 --- a/src/libs/utils/qtcprocess.cpp +++ b/src/libs/utils/qtcprocess.cpp @@ -94,6 +94,61 @@ public: bool keepRawData = true; }; +class ProcessInterface : public QObject +{ + Q_OBJECT +public: + ProcessInterface() : QObject() {} + + virtual QByteArray readAllStandardOutput() = 0; + virtual QByteArray readAllStandardError() = 0; + + virtual void setProcessEnvironment(const QProcessEnvironment &environment) = 0; + virtual void setWorkingDirectory(const QString &dir) = 0; + virtual void start(const QString &program, const QStringList &arguments, QIODevice::OpenMode mode) = 0; + virtual void terminate() = 0; + virtual void kill() = 0; + virtual void close() = 0; + virtual qint64 write(const QByteArray &data) = 0; + virtual void closeWriteChannel() = 0; + + virtual void setStandardInputFile(const QString &fileName) = 0; + virtual void setProcessChannelMode(QProcess::ProcessChannelMode mode) = 0; + + virtual QProcess::ProcessChannel readChannel() const = 0; // only in assert? + virtual qint64 bytesAvailable() const = 0; + virtual QString program() const = 0; + virtual QProcess::ProcessError error() const = 0; + virtual QProcess::ProcessState state() const = 0; + virtual qint64 processId() const = 0; + virtual QProcess::ExitStatus exitStatus() const = 0; + virtual QString errorString() const = 0; + virtual void setErrorString(const QString &str) = 0; + + virtual bool waitForStarted(int msecs) = 0; + virtual bool waitForReadyRead(int msecs) = 0; + virtual bool waitForFinished(int msecs) = 0; + + virtual void setLowPriority() = 0; + virtual bool lowPriority() const = 0; + virtual void setDisableUnixTerminal() = 0; + virtual void setKeepWriteChannelOpen() = 0; + virtual bool keepsWriteChannelOpen() const = 0; + +#ifdef Q_OS_WIN + virtual void setCreateProcessArgumentsModifier(QProcess::CreateProcessArgumentModifier modifier) = 0; + virtual void setNativeArguments(const QString &arguments) = 0; +#endif + +signals: + void started(); + void finished(int exitCode, QProcess::ExitStatus status); + void errorOccurred(QProcess::ProcessError error); + void stateChanged(QProcess::ProcessState state); + void readyReadStandardOutput(); + void readyReadStandardError(); +}; + class ProcessHelper : public QProcess { public: @@ -131,23 +186,169 @@ public: bool m_keepStdInOpen = false; }; +class QProcessImpl : public ProcessInterface +{ +public: + QProcessImpl() : ProcessInterface() + { + connect(&m_process, &QProcess::started, + this, &ProcessInterface::started); + connect(&m_process, QOverload::of(&QProcess::finished), + this, &ProcessInterface::finished); + connect(&m_process, &QProcess::errorOccurred, + this, &ProcessInterface::errorOccurred); + connect(&m_process, &QProcess::stateChanged, + this, &ProcessInterface::stateChanged); + connect(&m_process, &QProcess::readyReadStandardOutput, + this, &ProcessInterface::readyReadStandardOutput); + connect(&m_process, &QProcess::readyReadStandardError, + this, &ProcessInterface::readyReadStandardError); + } + + QByteArray readAllStandardOutput() override { return m_process.readAllStandardOutput(); } + QByteArray readAllStandardError() override { return m_process.readAllStandardError(); } + + void setProcessEnvironment(const QProcessEnvironment &environment) override + { m_process.setProcessEnvironment(environment); } + void setWorkingDirectory(const QString &dir) override + { m_process.setWorkingDirectory(dir); } + void start(const QString &program, const QStringList &arguments, QIODevice::OpenMode mode) override + { m_process.start(program, arguments, mode); } + void terminate() override + { m_process.terminate(); } + void kill() override + { m_process.kill(); } + void close() override + { m_process.close(); } + qint64 write(const QByteArray &data) override + { return m_process.write(data); } + void closeWriteChannel() override + { m_process.closeWriteChannel(); } + + void setStandardInputFile(const QString &fileName) override + { m_process.setStandardInputFile(fileName); } + void setProcessChannelMode(QProcess::ProcessChannelMode mode) override + { m_process.setProcessChannelMode(mode); } + + QProcess::ProcessChannel readChannel() const override + { return m_process.readChannel(); } + qint64 bytesAvailable() const override + { return m_process.bytesAvailable(); } + QString program() const override + { return m_process.program(); } + QProcess::ProcessError error() const override + { return m_process.error(); } + QProcess::ProcessState state() const override + { return m_process.state(); } + qint64 processId() const override + { return m_process.processId(); } + QProcess::ExitStatus exitStatus() const override + { return m_process.exitStatus(); } + QString errorString() const override + { return m_process.errorString(); } + void setErrorString(const QString &str) override + { m_process.setErrorString(str); } + + bool waitForStarted(int msecs) override + { return m_process.waitForStarted(msecs); } + bool waitForReadyRead(int msecs) override + { return m_process.waitForReadyRead(msecs); } + bool waitForFinished(int msecs) override + { return m_process.waitForFinished(msecs); } + + void setLowPriority() override + { m_process.m_lowPriority = true; } + bool lowPriority() const override + { return m_process.m_lowPriority; } + void setDisableUnixTerminal() override + { m_process.m_disableUnixTerminal = true; } + void setKeepWriteChannelOpen() override + { m_process.m_keepStdInOpen = true; } + bool keepsWriteChannelOpen() const override + { return m_process.m_keepStdInOpen; } + +#ifdef Q_OS_WIN + void setCreateProcessArgumentsModifier(QProcess::CreateProcessArgumentModifier modifier) override + { m_process.setCreateProcessArgumentsModifier(modifier); } + void setNativeArguments(const QString &arguments) override + { m_process.setNativeArguments(arguments); } +#endif + +private: + ProcessHelper m_process; +}; + +class ProcessLauncherImpl : public ProcessInterface +{ +public: + ProcessLauncherImpl() : ProcessInterface() {} + + QByteArray readAllStandardOutput() override { QTC_CHECK(false); return {}; } + QByteArray readAllStandardError() override { QTC_CHECK(false); return {}; } + + void setProcessEnvironment(const QProcessEnvironment &environment) override { QTC_CHECK(false); } + void setWorkingDirectory(const QString &dir) override { QTC_CHECK(false); } + void start(const QString &program, const QStringList &arguments, QIODevice::OpenMode mode) override { QTC_CHECK(false); } + void terminate() override { QTC_CHECK(false); } + void kill() override { QTC_CHECK(false); } + void close() override { QTC_CHECK(false); } + qint64 write(const QByteArray &data) override { QTC_CHECK(false); return -1; } + void closeWriteChannel() override { QTC_CHECK(false); } + + void setStandardInputFile(const QString &fileName) override { QTC_CHECK(false); } + void setProcessChannelMode(QProcess::ProcessChannelMode mode) override { QTC_CHECK(false); } + + QProcess::ProcessChannel readChannel() const override { QTC_CHECK(false); return QProcess::StandardOutput; } + qint64 bytesAvailable() const override { QTC_CHECK(false); return 0; } + QString program() const override { QTC_CHECK(false); return {}; } + QProcess::ProcessError error() const override { QTC_CHECK(false); return QProcess::UnknownError; } + QProcess::ProcessState state() const override { QTC_CHECK(false); return QProcess::NotRunning; } + qint64 processId() const override { QTC_CHECK(false); return 0; } + QProcess::ExitStatus exitStatus() const override { QTC_CHECK(false); return QProcess::NormalExit; } + QString errorString() const override { QTC_CHECK(false); return {}; } + void setErrorString(const QString &str) override { QTC_CHECK(false); } + + bool waitForStarted(int msecs) override { QTC_CHECK(false); return false; } + bool waitForReadyRead(int msecs) override { QTC_CHECK(false); return false; } + bool waitForFinished(int msecs) override { QTC_CHECK(false); return false; } + + void setLowPriority() override { QTC_CHECK(false); } + bool lowPriority() const override { QTC_CHECK(false); return false; } + void setDisableUnixTerminal() override { QTC_CHECK(false); } + void setKeepWriteChannelOpen() override { QTC_CHECK(false); } + bool keepsWriteChannelOpen() const override { QTC_CHECK(false); return false; } + +#ifdef Q_OS_WIN + void setCreateProcessArgumentsModifier(QProcess::CreateProcessArgumentModifier modifier) override + { QTC_CHECK(false); } + void setNativeArguments(const QString &arguments) override { QTC_CHECK(false); } +#endif +}; + +static ProcessInterface *newProcessInstance(QtcProcess::ProcessImpl processImpl) +{ + if (processImpl == QtcProcess::QProcessImpl) + return new QProcessImpl; + return new ProcessLauncherImpl; +} + class QtcProcessPrivate : public QObject { public: - explicit QtcProcessPrivate(QtcProcess *parent) - : q(parent), m_process(new ProcessHelper) + explicit QtcProcessPrivate(QtcProcess *parent, QtcProcess::ProcessImpl processImpl) + : q(parent), m_process(newProcessInstance(processImpl)) { - connect(m_process, &QProcess::started, + connect(m_process, &ProcessInterface::started, q, &QtcProcess::started); - connect(m_process, QOverload::of(&QProcess::finished), + connect(m_process, &ProcessInterface::finished, this, &QtcProcessPrivate::slotFinished); - connect(m_process, &QProcess::errorOccurred, + connect(m_process, &ProcessInterface::errorOccurred, this, &QtcProcessPrivate::slotError); - connect(m_process, &QProcess::stateChanged, + connect(m_process, &ProcessInterface::stateChanged, q, &QtcProcess::stateChanged); - connect(m_process, &QProcess::readyReadStandardOutput, + connect(m_process, &ProcessInterface::readyReadStandardOutput, this, &QtcProcessPrivate::handleReadyReadStandardOutput); - connect(m_process, &QProcess::readyReadStandardError, + connect(m_process, &ProcessInterface::readyReadStandardError, this, &QtcProcessPrivate::handleReadyReadStandardError); connect(&m_timer, &QTimer::timeout, this, &QtcProcessPrivate::slotTimeout); m_timer.setInterval(1000); @@ -173,7 +374,7 @@ public: } QtcProcess *q; - ProcessHelper *m_process; + ProcessInterface *m_process; CommandLine m_commandLine; FilePath m_workingDirectory; Environment m_environment; @@ -239,8 +440,8 @@ QtcProcess::Result QtcProcessPrivate::interpretExitCode(int exitCode) \sa Utils::ProcessArgs */ -QtcProcess::QtcProcess(QObject *parent) - : QObject(parent), d(new QtcProcessPrivate(this)) +QtcProcess::QtcProcess(ProcessImpl processImpl, QObject *parent) + : QObject(parent), d(new QtcProcessPrivate(this, processImpl)) { static int qProcessExitStatusMeta = qRegisterMetaType(); static int qProcessProcessErrorMeta = qRegisterMetaType(); @@ -248,6 +449,8 @@ QtcProcess::QtcProcess(QObject *parent) Q_UNUSED(qProcessProcessErrorMeta) } +QtcProcess::QtcProcess(QObject *parent) : QtcProcess(QtcProcess::QProcessImpl, parent) {} + QtcProcess::~QtcProcess() { delete d; @@ -354,12 +557,12 @@ void QtcProcess::start() if (osType == OsTypeWindows) { QString args; if (d->m_useCtrlCStub) { - if (d->m_process->m_lowPriority) + if (d->m_process->lowPriority()) ProcessArgs::addArg(&args, "-nice"); ProcessArgs::addArg(&args, QDir::toNativeSeparators(command)); command = QCoreApplication::applicationDirPath() + QLatin1String("/qtcreator_ctrlc_stub.exe"); - } else if (d->m_process->m_lowPriority) { + } else if (d->m_process->lowPriority()) { #ifdef Q_OS_WIN d->m_process->setCreateProcessArgumentsModifier( [](QProcess::CreateProcessArguments *args) { @@ -431,22 +634,22 @@ void QtcProcess::interrupt() void QtcProcess::setLowPriority() { - d->m_process->m_lowPriority = true; + d->m_process->setLowPriority(); } void QtcProcess::setDisableUnixTerminal() { - d->m_process->m_disableUnixTerminal = true; + d->m_process->setDisableUnixTerminal(); } void QtcProcess::setKeepWriteChannelOpen() { - d->m_process->m_keepStdInOpen = true; + d->m_process->setKeepWriteChannelOpen(); } bool QtcProcess::keepsWriteChannelOpen() const { - return d->m_process->m_keepStdInOpen; + return d->m_process->keepsWriteChannelOpen(); } void QtcProcess::setStandardInputFile(const QString &inputFile) @@ -1011,7 +1214,7 @@ void QtcProcess::runBlocking() if (d->m_processUserEvents) { if (!d->m_writeData.isEmpty()) { - connect(d->m_process, &QProcess::started, this, [this] { + connect(d->m_process, &ProcessInterface::started, this, [this] { write(d->m_writeData); closeWriteChannel(); }); @@ -1155,3 +1358,5 @@ void QtcProcessPrivate::slotError(QProcess::ProcessError error) } } // namespace Utils + +#include "qtcprocess.moc" diff --git a/src/libs/utils/qtcprocess.h b/src/libs/utils/qtcprocess.h index 95fe4d22563..e66e013baa7 100644 --- a/src/libs/utils/qtcprocess.h +++ b/src/libs/utils/qtcprocess.h @@ -59,6 +59,12 @@ class QTCREATOR_UTILS_EXPORT QtcProcess : public QObject Q_OBJECT public: + enum ProcessImpl { + QProcessImpl, + ProcessLauncherImpl + }; + + QtcProcess(ProcessImpl processImpl, QObject *parent = nullptr); QtcProcess(QObject *parent = nullptr); ~QtcProcess();