From bb2d53a63aee69f712aab1488555cbb1a664098d Mon Sep 17 00:00:00 2001 From: Gerhard Hoffmann Date: Wed, 28 Feb 2024 16:48:12 +0100 Subject: [PATCH] add qt-process-handling --- process/command.cpp | 124 ++++++++++++++++++++++++++++++++++++++++++++ process/command.h | 35 +++++++++++++ 2 files changed, 159 insertions(+) create mode 100644 process/command.cpp create mode 100644 process/command.h diff --git a/process/command.cpp b/process/command.cpp new file mode 100644 index 0000000..c85c8ac --- /dev/null +++ b/process/command.cpp @@ -0,0 +1,124 @@ +#include "command.h" + +#include +#include +#include +#include +#include + +Command::Command(QString const &command, int start_timeout, int finish_timeout) + : m_command(command.trimmed()) + , m_commandResult("") + , m_waitForStartTimeout(start_timeout) + , m_waitForFinishTimeout(finish_timeout) + , m_exitCode(-1) { +} + +QString Command::getCommandResult() const { + return m_commandResult; +} + +void Command::readyReadStandardOutput() { + QProcess *p = (QProcess *)sender(); + m_commandResult += p->readAllStandardOutput(); + // qCritical() << m_commandResult; +} + +void Command::readyReadStandardError() { + QProcess *p = (QProcess *)sender(); + QByteArray buf = p->readAllStandardError(); + qCritical() << buf; +} + +void Command::finished(int /*exitCode*/, QProcess::ExitStatus /*exitStatus*/) { + QProcess *p = (QProcess *)sender(); + // read all remaining data sent to the process, just in case + QString d = p->readAllStandardOutput(); + if (!d.isEmpty()) { + m_commandResult += d; + } + disconnect(p, SIGNAL(finished(int,QProcess::ExitStatus)), this, SLOT(readyReadStandardOutput())); + disconnect(p, SIGNAL(finished(int,QProcess::ExitStatus)), this, SLOT(readyReadStandardError())); +} + +bool Command::execute(QString workingDirectory, QStringList args) { + + if (!QDir::setCurrent(workingDirectory)) { + qCritical() << "SET WORKING_DIRECTORY" << workingDirectory + << "FAILED FOR" << m_command; + return false; + } + + QScopedPointer p(new QProcess(this)); + p->setWorkingDirectory(workingDirectory); + p->setProcessChannelMode(QProcess::MergedChannels); + + connect(&(*p), SIGNAL(readyReadStandardOutput()), this, SLOT(readyReadStandardOutput())); + connect(&(*p), SIGNAL(readyReadStandardError()), this, SLOT(readyReadStandardError())); + + if (!args.isEmpty()) { + qDebug() << "START COMMAND" << m_command << "WITH ARGS" << args + << "IN" << p->workingDirectory(); + p->start(m_command, args); + } else { + qDebug() << "START COMMAND" << m_command + << "IN" << p->workingDirectory(); + p->start(m_command); + } + + qint64 const start = QDateTime::currentDateTime().toMSecsSinceEpoch(); + + if (p->waitForStarted(m_waitForStartTimeout)) { + qDebug() << "PROCESS" << m_command << "STARTED IN" << p->workingDirectory(); + if (p->state() == QProcess::ProcessState::Running) { + qDebug() << "PROCESS" << m_command << "RUNNING IN" << p->workingDirectory(); + // wait forever for git/opkg-commands to finish + int wait = m_waitForFinishTimeout; + if (m_command.trimmed().startsWith("git", Qt::CaseInsensitive) || + m_command.trimmed().startsWith("opkg", Qt::CaseInsensitive)) { + wait = -1; + } + bool const no_timeout = p->waitForFinished(wait); + if (no_timeout) { + qDebug() << "PROCESS" << m_command << "FINISHED IN" << p->workingDirectory(); + if (p->exitStatus() == QProcess::NormalExit) { + if ((m_exitCode = p->exitCode()) == 0) { + qint64 const end = QDateTime::currentDateTime().toMSecsSinceEpoch(); + qDebug() << "EXECUTED" << m_command + << QString("(runtime %1ms)").arg(end-start) + << "with code" << m_exitCode + << "IN" << p->workingDirectory(); + return true; + } else { + qint64 const end = QDateTime::currentDateTime().toMSecsSinceEpoch(); + qCritical() << "EXECUTED" << m_command + << QString("(runtime %1ms)").arg(end-start) + << "with code" << m_exitCode + << "IN" << p->workingDirectory(); + } + } else { + qint64 const end = QDateTime::currentDateTime().toMSecsSinceEpoch(); + qCritical() << "PROCESS" << m_command << "CRASHED with code" + << p->exitCode() + << QString("(after %1ms)").arg(end-start) + << "IN" << p->workingDirectory(); + } + } else { + qint64 const end = QDateTime::currentDateTime().toMSecsSinceEpoch(); + qCritical() << "PROCESS" << m_command + << "DID NOT FINISH WITH" << wait + << "MS IN" << p->workingDirectory() + << QString("(runtime %1ms)").arg(end-start); + } + } else { + qCritical() << "WRONG PROCESS STATE" << p->state() + << "IN" << p->workingDirectory(); + } + } else { + qint64 const end = QDateTime::currentDateTime().toMSecsSinceEpoch(); + qCritical() << "PROCESS" << m_command << "TIMEOUT AT START" + << QString("(runtime %1ms)").arg(end-start) + << "IN" << p->workingDirectory(); + } + return false; +} diff --git a/process/command.h b/process/command.h new file mode 100644 index 0000000..bc796ee --- /dev/null +++ b/process/command.h @@ -0,0 +1,35 @@ +#ifndef COMMAND_H_INCLUDED +#define COMMAND_H_INCLUDED +#endif // COMMAND_H_INCLUDED + +#include +#include +#include +#include +#include + + +class Command : public QObject { + Q_OBJECT + + QString m_command; + QString m_commandResult; + int m_waitForStartTimeout; + int m_waitForFinishTimeout; + int m_exitCode; +public: + explicit Command(QString const &command, + int start_timeout = 100000, + int finish_timeout = 100000); + + QString getCommandResult() const; + QString command() const { return m_command; } + + bool execute(QString workingDirectory, QStringList args = QStringList()); + int exitCode() const { return m_exitCode; } + +private slots: + void readyReadStandardOutput(); + void readyReadStandardError(); + void finished(int exitCode, QProcess::ExitStatus exitStatus); +};