File indexing completed on 2025-03-09 03:56:12

0001 /* ============================================================
0002  *
0003  * This file is a part of digiKam project
0004  * https://www.digikam.org
0005  *
0006  * Date        : 2015-04-13
0007  * Description : Generic process launcher with a capture of console output
0008  *
0009  * SPDX-FileCopyrightText: 2011-2024 by Gilles Caulier <caulier dot gilles at gmail dot com>
0010  *
0011  * SPDX-License-Identifier: GPL-2.0-or-later
0012  *
0013  * ============================================================ */
0014 
0015 #include "processlauncher.h"
0016 
0017 // Qt includes
0018 
0019 #include <QByteArray>
0020 #include <QProcess>
0021 #include <QSharedPointer>
0022 #include <QElapsedTimer>
0023 #include <QProcessEnvironment>
0024 
0025 // Local includes
0026 
0027 #include "digikam_globals.h"
0028 #include "digikam_debug.h"
0029 
0030 namespace Digikam
0031 {
0032 
0033 class Q_DECL_HIDDEN ProcessLauncher::Private
0034 {
0035 public:
0036 
0037     Private() = default;
0038 
0039 public:
0040 
0041     QSharedPointer<QProcess> process       = nullptr;
0042     QStringList              args;
0043     QString                  prog;
0044     QString                  dir;
0045     bool                     consoleTraces = true;
0046     bool                     successFlag   = false;
0047     int                      exitCode      = 0;
0048     int                      timeOut       = 30000;           ///< in milli-seconds;
0049     qint64                   elapsed       = 0;
0050     QString                  output;
0051 };
0052 
0053 ProcessLauncher::ProcessLauncher(QObject* const parent)
0054     : QThread(parent),
0055       d      (new Private)
0056 {
0057 }
0058 
0059 ProcessLauncher::~ProcessLauncher()
0060 {
0061     if (!d->process.isNull())
0062     {
0063         d->process->kill();
0064     }
0065 
0066     delete d;
0067 }
0068 
0069 void ProcessLauncher::setProgram(const QString& prog)
0070 {
0071     d->prog = prog;
0072 }
0073 
0074 void ProcessLauncher::setArguments(const QStringList& args)
0075 {
0076     d->args = args;
0077 }
0078 
0079 void ProcessLauncher::setWorkingDirectory(const QString& dir)
0080 {
0081     d->dir = dir;
0082 }
0083 
0084 void ProcessLauncher::setTimeOut(int msecs)
0085 {
0086     d->timeOut = msecs;
0087 }
0088 
0089 int ProcessLauncher::exitCode() const
0090 {
0091     return d->exitCode;
0092 }
0093 
0094 QString ProcessLauncher::output() const
0095 {
0096     return d->output;
0097 }
0098 
0099 bool ProcessLauncher::success() const
0100 {
0101     return d->successFlag;
0102 }
0103 
0104 qint64 ProcessLauncher::elapsedTime() const
0105 {
0106     return d->elapsed;
0107 }
0108 
0109 void ProcessLauncher::setConsoleTraces(bool b)
0110 {
0111     d->consoleTraces = b;
0112 }
0113 
0114 void ProcessLauncher::startProcess()
0115 {
0116     start();
0117 }
0118 
0119 void ProcessLauncher::run()
0120 {
0121     QString     prog;
0122     QStringList args;
0123 
0124     d->process.reset(new QProcess());
0125     d->process->setProcessChannelMode(QProcess::MergedChannels);
0126     d->process->setWorkingDirectory(d->dir);
0127 
0128     QProcessEnvironment env = adjustedEnvironmentForAppImage();
0129     d->process->setProcessEnvironment(env);
0130 
0131     connect(d->process.data(), SIGNAL(readyRead()),
0132             this, SLOT(slotReadyRead()));
0133 
0134     QElapsedTimer etimer;
0135     etimer.start();
0136 
0137 #ifdef Q_OS_WIN
0138 
0139     prog = QLatin1String("cmd");
0140     args = QStringList() << QLatin1String("/c") << d->prog << d->args;
0141 
0142 #else   // Linux and MacOS
0143 
0144     prog = d->prog;
0145     args = d->args;
0146 
0147 #endif
0148 
0149     qCInfo(DIGIKAM_GENERAL_LOG) << "=== Starting process:" << prog << args;
0150 
0151 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
0152 
0153     d->process->start(prog, args);
0154 
0155 #else
0156 
0157     d->process->setProgram(prog);
0158     d->process->setArguments(args);
0159     d->process->start();
0160 
0161 #endif
0162 
0163     if (d->process->waitForStarted(d->timeOut))
0164     {
0165         d->successFlag = d->process->waitForFinished(-1) && (d->process->exitStatus() == QProcess::NormalExit);
0166         d->elapsed     = etimer.elapsed();
0167         d->exitCode    = d->process->exitCode();
0168 
0169         msleep(500);  // Wait a little bit to flush the event loop.
0170 
0171         qCInfo(DIGIKAM_GENERAL_LOG) << "=== Process execution is complete!";
0172         qCInfo(DIGIKAM_GENERAL_LOG) << "> Process exit code        :" << d->exitCode;
0173         qCInfo(DIGIKAM_GENERAL_LOG) << "> Process elasped time (ms):" << d->elapsed;
0174     }
0175     else
0176     {
0177         qCWarning(DIGIKAM_GENERAL_LOG) << "=== Process execution failed!";
0178     }
0179 
0180     Q_EMIT signalComplete(d->successFlag, d->exitCode);
0181 }
0182 
0183 void ProcessLauncher::slotReadyRead()
0184 {
0185     QByteArray data = d->process->readAll();
0186 
0187     if (!data.isEmpty())
0188     {
0189         QString txt = QString::fromLocal8Bit(data.data(), data.size());
0190         d->output.append(txt);
0191 
0192         if (d->consoleTraces)
0193         {
0194             Q_FOREACH (const QString& str, txt.split(QLatin1Char('\n')))
0195             {
0196                 if (!str.isEmpty())
0197                 {
0198                     qCDebug(DIGIKAM_GENERAL_LOG)
0199 
0200 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
0201 
0202                        .noquote()
0203 
0204 #endif
0205 
0206                        << str;
0207                 }
0208             }
0209         }
0210     }
0211 }
0212 
0213 } // namespace Digikam
0214 
0215 #include "moc_processlauncher.cpp"