File indexing completed on 2024-05-05 04:39:27

0001 /*
0002     SPDX-FileCopyrightText: 2020 Friedrich W. H. Kossebau <kossebau@kde.org>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #include "compileanalyzejob.h"
0008 
0009 // lib
0010 #include <debug.h>
0011 // KF
0012 #include <KLocalizedString>
0013 // Qt
0014 #include <QTemporaryFile>
0015 
0016 namespace KDevelop
0017 {
0018 
0019 QString CompileAnalyzeJob::spaceEscapedString(const QString& s)
0020 {
0021     return QString(s).replace(QLatin1Char(' '), QLatin1String("\\ "));
0022 }
0023 
0024 CompileAnalyzeJob::CompileAnalyzeJob(QObject* parent)
0025     : OutputExecuteJob(parent)
0026 {
0027     setCapabilities(KJob::Killable);
0028     setStandardToolView(IOutputView::TestView);
0029     setBehaviours(IOutputView::AutoScroll);
0030     setProperties(JobProperties(DisplayStdout | DisplayStderr | PostProcessOutput));
0031 }
0032 
0033 CompileAnalyzeJob::~CompileAnalyzeJob()
0034 {
0035     doKill();
0036 
0037     if (!m_makeFilePath.isEmpty()) {
0038         QFile::remove(m_makeFilePath);
0039     }
0040 }
0041 
0042 void CompileAnalyzeJob::setParallelJobCount(int parallelJobCount)
0043 {
0044     m_parallelJobCount = parallelJobCount;
0045 }
0046 
0047 void CompileAnalyzeJob::setBuildDirectoryRoot(const QString& buildDir)
0048 {
0049     m_buildDir = buildDir;
0050 }
0051 
0052 void CompileAnalyzeJob::setCommand(const QString& command, bool verboseOutput)
0053 {
0054     m_command = command;
0055     m_verboseOutput = verboseOutput;
0056 }
0057 
0058 void CompileAnalyzeJob::setToolDisplayName(const QString& toolDisplayName)
0059 {
0060     m_toolDisplayName = toolDisplayName;
0061 
0062     m_fileStartedRegex  = QRegularExpression(m_toolDisplayName + QLatin1String(" check started  for (.+)$"));
0063     m_fileFinishedRegex = QRegularExpression(m_toolDisplayName + QLatin1String(" check finished for (.+)$"));
0064 }
0065 
0066 void CompileAnalyzeJob::setSources(const QStringList& sources)
0067 {
0068     m_sources = sources;
0069 }
0070 
0071 void CompileAnalyzeJob::generateMakefile()
0072 {
0073     QTemporaryFile makefile(m_buildDir + QLatin1String("/kdevcompileanalyzerXXXXXX.makefile"));
0074     makefile.setAutoRemove(false);
0075     makefile.open();
0076     m_makeFilePath = makefile.fileName();
0077 
0078     QTextStream scriptStream(&makefile);
0079 
0080     scriptStream << QStringLiteral("SOURCES =");
0081     for (const auto& source : qAsConst(m_sources)) {
0082         scriptStream << QLatin1String(" \\\n\t") << spaceEscapedString(source);
0083     }
0084     scriptStream << QLatin1Char('\n');
0085 
0086     scriptStream << QLatin1String("COMMAND = ");
0087     if (!m_verboseOutput) {
0088         scriptStream << QLatin1Char('@');
0089     }
0090     scriptStream << m_command << QLatin1Char('\n');
0091 
0092     scriptStream << QLatin1String(".PHONY: all $(SOURCES)\n");
0093     scriptStream << QLatin1String("all: $(SOURCES)\n");
0094     scriptStream << QLatin1String("$(SOURCES):\n");
0095 
0096     scriptStream << QLatin1String("\t@echo '") << m_toolDisplayName << QLatin1String(" check started  for $@'\n");
0097     // Wrap filename ($@) with quotas to handle "whitespaced" file names.
0098     scriptStream << QLatin1String("\t$(COMMAND) \"$@\"\n");
0099     scriptStream << QLatin1String("\t@echo '") << m_toolDisplayName << QLatin1String(" check finished for $@'\n");
0100 
0101     makefile.close();
0102 }
0103 
0104 void CompileAnalyzeJob::start()
0105 {
0106     // TODO: check success of creation
0107     generateMakefile();
0108 
0109     *this << QStringList{
0110         QStringLiteral("make"),
0111         QStringLiteral("-j"),
0112         QString::number(m_parallelJobCount),
0113         QStringLiteral("-k"), // keep-going
0114         QStringLiteral("-f"),
0115         m_makeFilePath,
0116     };
0117 
0118     qCDebug(KDEV_COMPILEANALYZER) << "executing:" << commandLine().join(QLatin1Char(' '));
0119 
0120     m_finishedCount = 0;
0121     m_totalCount = m_sources.size();
0122 
0123     setPercent(0);
0124 
0125     KDevelop::OutputExecuteJob::start();
0126 }
0127 
0128 void CompileAnalyzeJob::parseProgress(const QStringList& lines)
0129 {
0130     for (const auto& line : lines) {
0131         const auto startedMatch = m_fileStartedRegex.match(line);
0132         if (startedMatch.hasMatch()) {
0133             emit infoMessage(this, startedMatch.captured(1));
0134             continue;
0135         }
0136 
0137         const auto finishedMatch = m_fileFinishedRegex.match(line);
0138         if (finishedMatch.hasMatch()) {
0139             ++m_finishedCount;
0140             setPercent(static_cast<double>(m_finishedCount)/m_totalCount * 100);
0141             continue;
0142         }
0143     }
0144 }
0145 
0146 void CompileAnalyzeJob::postProcessStdout(const QStringList& lines)
0147 {
0148     parseProgress(lines);
0149 
0150     KDevelop::OutputExecuteJob::postProcessStdout(lines);
0151 }
0152 
0153 void CompileAnalyzeJob::childProcessExited(int exitCode, QProcess::ExitStatus exitStatus)
0154 {
0155     qCDebug(KDEV_COMPILEANALYZER) << "Process Finished, exitCode" << exitCode << "process exit status" << exitStatus;
0156 
0157     setPercent(100);
0158 
0159     KDevelop::OutputExecuteJob::childProcessExited(exitCode, exitStatus);
0160 }
0161 
0162 }
0163 
0164 #include "moc_compileanalyzejob.cpp"