File indexing completed on 2024-05-05 16:45:13
0001 /* 0002 SPDX-FileCopyrightText: 2011 Mathieu Lornac <mathieu.lornac@gmail.com> 0003 SPDX-FileCopyrightText: 2011 Damien Coppel <damien.coppel@gmail.com> 0004 SPDX-FileCopyrightText: 2011 Lionel Duc <lionel.data@gmail.com> 0005 SPDX-FileCopyrightText: 2011 Sebastien Rannou <mxs@sbrk.org> 0006 SPDX-FileCopyrightText: 2011 Lucas Sarie <lucas.sarie@gmail.com> 0007 SPDX-FileCopyrightText: 2006-2008 Hamish Rodda <rodda@kde.org> 0008 SPDX-FileCopyrightText: 2002 Harald Fernengel <harry@kdevelop.org> 0009 SPDX-FileCopyrightText: 2013 Christoph Thielecke <crissi99@gmx.de> 0010 SPDX-FileCopyrightText: 2016-2017 Anton Anikin <anton.anikin@htower.ru> 0011 0012 SPDX-License-Identifier: GPL-2.0-or-later 0013 */ 0014 0015 #include "job.h" 0016 0017 #include "debug.h" 0018 #include "parser.h" 0019 #include "utils.h" 0020 0021 #include <interfaces/icore.h> 0022 #include <interfaces/iuicontroller.h> 0023 #include <sublime/message.h> 0024 #include <shell/problem.h> 0025 // KF 0026 #include <KLocalizedString> 0027 // Qt 0028 #include <QApplication> 0029 #include <QElapsedTimer> 0030 #include <QRegularExpression> 0031 0032 namespace cppcheck 0033 { 0034 0035 Job::Job(const Parameters& params, QObject* parent) 0036 : KDevelop::OutputExecuteJob(parent) 0037 , m_timer(new QElapsedTimer) 0038 , m_parser(new CppcheckParser) 0039 , m_showXmlOutput(params.showXmlOutput) 0040 , m_projectRootPath(params.projectRootPath()) 0041 { 0042 setJobName(i18n("Cppcheck Analysis (%1)", prettyPathName(params.checkPath))); 0043 0044 setCapabilities(KJob::Killable); 0045 setStandardToolView(KDevelop::IOutputView::TestView); 0046 setBehaviours(KDevelop::IOutputView::AutoScroll); 0047 0048 setProperties(KDevelop::OutputExecuteJob::JobProperty::DisplayStdout); 0049 setProperties(KDevelop::OutputExecuteJob::JobProperty::DisplayStderr); 0050 setProperties(KDevelop::OutputExecuteJob::JobProperty::PostProcessOutput); 0051 0052 *this << params.commandLine(); 0053 qCDebug(KDEV_CPPCHECK) << "checking path" << params.checkPath; 0054 } 0055 0056 Job::~Job() 0057 { 0058 doKill(); 0059 } 0060 0061 void Job::postProcessStdout(const QStringList& lines) 0062 { 0063 static const auto fileNameRegex = QRegularExpression(QStringLiteral("Checking ([^:]*)\\.{3}")); 0064 static const auto percentRegex = QRegularExpression(QStringLiteral("(\\d+)% done")); 0065 0066 QRegularExpressionMatch match; 0067 0068 for (const QString& line : lines) { 0069 match = fileNameRegex.match(line); 0070 if (match.hasMatch()) { 0071 emit infoMessage(this, match.captured(1)); 0072 continue; 0073 } 0074 0075 match = percentRegex.match(line); 0076 if (match.hasMatch()) { 0077 setPercent(match.capturedRef(1).toULong()); 0078 continue; 0079 } 0080 } 0081 0082 m_standardOutput << lines; 0083 0084 if (status() == KDevelop::OutputExecuteJob::JobStatus::JobRunning) { 0085 KDevelop::OutputExecuteJob::postProcessStdout(lines); 0086 } 0087 } 0088 0089 void Job::postProcessStderr(const QStringList& lines) 0090 { 0091 static const auto xmlStartRegex = QRegularExpression(QStringLiteral("\\s*<")); 0092 0093 for (const QString & line : lines) { 0094 // unfortunately sometime cppcheck send non-XML messages to stderr. 0095 // For example, if we pass '-I /missing_include_dir' to the argument list, 0096 // then stderr output will contains such line (tested on cppcheck 1.72): 0097 // 0098 // (information) Couldn't find path given by -I '/missing_include_dir' 0099 // 0100 // Therefore we must 'move' such messages to m_standardOutput. 0101 0102 if (line.indexOf(xmlStartRegex) != -1) { // the line contains XML 0103 m_xmlOutput << line; 0104 0105 m_parser->addData(line); 0106 0107 m_problems = m_parser->parse(); 0108 emitProblems(); 0109 } 0110 else { 0111 KDevelop::IProblem::Ptr problem(new KDevelop::DetectedProblem(i18n("Cppcheck"))); 0112 0113 problem->setSeverity(KDevelop::IProblem::Error); 0114 problem->setDescription(line); 0115 problem->setExplanation(QStringLiteral("Check your cppcheck settings")); 0116 0117 m_problems = {problem}; 0118 emitProblems(); 0119 0120 if (m_showXmlOutput) { 0121 m_standardOutput << line; 0122 } else { 0123 postProcessStdout({line}); 0124 } 0125 } 0126 } 0127 0128 if (status() == KDevelop::OutputExecuteJob::JobStatus::JobRunning && m_showXmlOutput) { 0129 KDevelop::OutputExecuteJob::postProcessStderr(lines); 0130 } 0131 } 0132 0133 void Job::start() 0134 { 0135 m_standardOutput.clear(); 0136 m_xmlOutput.clear(); 0137 0138 qCDebug(KDEV_CPPCHECK) << "executing:" << commandLine().join(QLatin1Char(' ')); 0139 0140 m_timer->restart(); 0141 KDevelop::OutputExecuteJob::start(); 0142 } 0143 0144 void Job::childProcessError(QProcess::ProcessError e) 0145 { 0146 QString messageText; 0147 0148 switch (e) { 0149 case QProcess::FailedToStart: 0150 messageText = i18n("Failed to start Cppcheck from \"%1\".", commandLine()[0]); 0151 break; 0152 0153 case QProcess::Crashed: 0154 if (status() != KDevelop::OutputExecuteJob::JobStatus::JobCanceled) { 0155 messageText = i18n("Cppcheck crashed."); 0156 } 0157 break; 0158 0159 case QProcess::Timedout: 0160 messageText = i18n("Cppcheck process timed out."); 0161 break; 0162 0163 case QProcess::WriteError: 0164 messageText = i18n("Write to Cppcheck process failed."); 0165 break; 0166 0167 case QProcess::ReadError: 0168 messageText = i18n("Read from Cppcheck process failed."); 0169 break; 0170 0171 case QProcess::UnknownError: 0172 // current cppcheck errors will be displayed in the output view 0173 // don't notify the user 0174 break; 0175 } 0176 0177 if (!messageText.isEmpty()) { 0178 auto* message = new Sublime::Message(messageText, Sublime::Message::Error); 0179 KDevelop::ICore::self()->uiController()->postMessage(message); 0180 } 0181 0182 KDevelop::OutputExecuteJob::childProcessError(e); 0183 } 0184 0185 void Job::childProcessExited(int exitCode, QProcess::ExitStatus exitStatus) 0186 { 0187 qCDebug(KDEV_CPPCHECK) << "Process Finished, exitCode" << exitCode << "process exit status" << exitStatus; 0188 0189 postProcessStdout({QStringLiteral("Elapsed time: %1 s.").arg(m_timer->elapsed()/1000.0)}); 0190 0191 if (exitCode != 0) { 0192 qCDebug(KDEV_CPPCHECK) << "cppcheck failed, standard output: "; 0193 qCDebug(KDEV_CPPCHECK) << m_standardOutput.join(QLatin1Char('\n')); 0194 qCDebug(KDEV_CPPCHECK) << "cppcheck failed, XML output: "; 0195 qCDebug(KDEV_CPPCHECK) << m_xmlOutput.join(QLatin1Char('\n')); 0196 } 0197 0198 KDevelop::OutputExecuteJob::childProcessExited(exitCode, exitStatus); 0199 } 0200 0201 void Job::emitProblems() 0202 { 0203 if (!m_problems.isEmpty()) { 0204 emit problemsDetected(m_problems); 0205 } 0206 } 0207 0208 } 0209 0210 #include "moc_job.cpp"