File indexing completed on 2024-04-21 04:36:02

0001 /* This file is part of KDevelop
0002 
0003    Copyright 2016 Anton Anikin <anton.anikin@htower.ru>
0004 
0005    This program is free software; you can redistribute it and/or
0006    modify it under the terms of the GNU General Public
0007    License as published by the Free Software Foundation; either
0008    version 2 of the License, or (at your option) any later version.
0009 
0010    This program is distributed in the hope that it will be useful,
0011    but WITHOUT ANY WARRANTY; without even the implied warranty of
0012    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
0013    General Public License for more details.
0014 
0015    You should have received a copy of the GNU General Public License
0016    along with this program; see the file COPYING.  If not, write to
0017    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0018    Boston, MA 02110-1301, USA.
0019 */
0020 
0021 #include "job.h"
0022 
0023 #include "debug.h"
0024 #include "rules.h"
0025 #include "utils.h"
0026 
0027 #include <klocalizedstring.h>
0028 #include <kmessagebox.h>
0029 #include <shell/problem.h>
0030 #include <language/editor/documentrange.h>
0031 
0032 #include <QApplication>
0033 #include <QElapsedTimer>
0034 #include <QRegularExpression>
0035 
0036 namespace verapp
0037 {
0038 
0039 Job::Job(const Parameters &params)
0040     : OutputExecuteJob(nullptr)
0041     , m_timer(new QElapsedTimer)
0042 {
0043     setJobName(i18n("Vera++ Analysis (%1)", prettyPathName(params.checkPath)));
0044 
0045     setCapabilities(KJob::Killable);
0046     setStandardToolView(KDevelop::IOutputView::TestView);
0047     setBehaviours(KDevelop::IOutputView::AutoScroll);
0048 
0049     setProperties(OutputExecuteJob::JobProperty::DisplayStdout);
0050     setProperties(OutputExecuteJob::JobProperty::DisplayStderr);
0051     setProperties(OutputExecuteJob::JobProperty::PostProcessOutput);
0052 
0053 #if defined(_WIN32) || defined(_WIN64)
0054     *this << "cmd";
0055 #else
0056     *this << "sh";
0057 #endif
0058 
0059     *this << params.buildRunScript();
0060 
0061     qCDebug(KDEV_VERAPP) << "checking path" << params.checkPath;
0062 }
0063 
0064 Job::~Job()
0065 {
0066 }
0067 
0068 void Job::postProcessStdout(const QStringList& lines)
0069 {
0070     static const auto errorRegex    = QRegularExpression("(.+):(\\d+):\\s*([A-Z]\\d{3}):\\s*(.+)$");
0071     static const auto fileNameRegex = QRegularExpression("Checking ([^:]*)\\.{3}");
0072     static const auto percentRegex  = QRegularExpression("(\\d+)% done");
0073 
0074     QVector<KDevelop::IProblem::Ptr> problems;
0075     QRegularExpressionMatch match;
0076 
0077     for (const QString & line : lines) {
0078         match = errorRegex.match(line);
0079         if (match.hasMatch()) {
0080             KDevelop::IProblem::Ptr problem(new KDevelop::DetectedProblem(i18n("Vera++")));
0081 
0082             problem->setSeverity(KDevelop::IProblem::Warning);
0083             problem->setDescription(match.captured(3) + ": " + match.captured(4));
0084             problem->setExplanation(rules::explanation(match.captured(3)));
0085 
0086             KDevelop::DocumentRange range;
0087             range.document = KDevelop::IndexedString(match.captured(1));
0088             range.setBothLines(match.captured(2).toInt() - 1);
0089             range.setBothColumns(0);
0090             problem->setFinalLocation(range);
0091             problem->setFinalLocationMode(KDevelop::IProblem::TrimmedLine);
0092 
0093             problems.append(problem);
0094             continue;
0095         }
0096 
0097         match = fileNameRegex.match(line);
0098         if (match.hasMatch()) {
0099             emit infoMessage(this, match.captured(1));
0100             continue;
0101         }
0102 
0103         match = percentRegex.match(line);
0104         if (match.hasMatch()) {
0105             setPercent(match.captured(1).toULong());
0106             continue;
0107         }
0108     }
0109 
0110     if (problems.size()) {
0111         emit problemsDetected(problems);
0112     }
0113 
0114     m_standardOutput << lines;
0115 
0116     if (status() == KDevelop::OutputExecuteJob::JobStatus::JobRunning) {
0117         OutputExecuteJob::postProcessStdout(lines);
0118     }
0119 }
0120 
0121 void Job::postProcessStderr(const QStringList& lines)
0122 {
0123     m_stderrOutput << lines;
0124 
0125     if (status() == KDevelop::OutputExecuteJob::JobStatus::JobRunning) {
0126         OutputExecuteJob::postProcessStderr(lines);
0127     }
0128 }
0129 
0130 void Job::start()
0131 {
0132     m_standardOutput.clear();
0133     m_stderrOutput.clear();
0134 
0135     qCDebug(KDEV_VERAPP) << "executing:" << commandLine().join(' ');
0136 
0137     m_timer->restart();
0138     OutputExecuteJob::start();
0139 }
0140 
0141 void Job::childProcessError(QProcess::ProcessError e)
0142 {
0143     QString message;
0144 
0145     switch (e) {
0146     case QProcess::FailedToStart:
0147         message = i18n("Failed to start Vera++ from \"%1\".", commandLine()[0]);
0148         break;
0149 
0150     case QProcess::Crashed:
0151         if (status() != KDevelop::OutputExecuteJob::JobStatus::JobCanceled) {
0152             message = i18n("Vera++ crashed.");
0153         }
0154         break;
0155 
0156     case QProcess::Timedout:
0157         message = i18n("Vera++ process timed out.");
0158         break;
0159 
0160     case QProcess::WriteError:
0161         message = i18n("Write to Vera++ process failed.");
0162         break;
0163 
0164     case QProcess::ReadError:
0165         message = i18n("Read from Vera++ process failed.");
0166         break;
0167 
0168     case QProcess::UnknownError:
0169         // current vera++ errors will be displayed in the output view
0170         // don't notify the user
0171         break;
0172     }
0173 
0174     if (!message.isEmpty()) {
0175         KMessageBox::error(qApp->activeWindow(), message, i18n("Vera++ Error"));
0176     }
0177 
0178     KDevelop::OutputExecuteJob::childProcessError(e);
0179 }
0180 
0181 void Job::childProcessExited(int exitCode, QProcess::ExitStatus exitStatus)
0182 {
0183     qCDebug(KDEV_VERAPP) << "Process Finished, exitCode" << exitCode << "process exit status" << exitStatus;
0184 
0185     postProcessStdout({QString("Elapsed time: %1 s.").arg(m_timer->elapsed()/1000.0)});
0186 
0187     if (exitCode != 0) {
0188         qCDebug(KDEV_VERAPP) << "vera++ failed";
0189         qCDebug(KDEV_VERAPP) << "stdout output: ";
0190         qCDebug(KDEV_VERAPP) << m_standardOutput.join('\n');
0191         qCDebug(KDEV_VERAPP) << "stderr output: ";
0192         qCDebug(KDEV_VERAPP) << m_stderrOutput.join('\n');
0193     }
0194 
0195     KDevelop::OutputExecuteJob::childProcessExited(exitCode, exitStatus);
0196 }
0197 
0198 }