diff options
author | Christian Kandeler <christian.kandeler@qt.io> | 2020-04-08 17:45:39 +0200 |
---|---|---|
committer | Christian Kandeler <christian.kandeler@qt.io> | 2020-04-09 14:49:32 +0000 |
commit | 45ba9fcd535e4cfd5f057149b1ca4bb4dfed5bdb (patch) | |
tree | 3e3246ccf3d971e69004182007bd1b726b88b8bf /src/plugins/projectexplorer | |
parent | fa517bd72aa21ea82072af27ce98030c4ff028f2 (diff) |
Output parsers: Replace the chaining approach
Use "flat" aggregation instead.
This is another step towards the formatter/parser merger.
Along the way, also fix some some subclasses (mostly in BareMetal) that
erroneously forwarded handled output to other parsers.
Task-number: QTCREATORBUG-22665
Change-Id: I12947349ca663d2e6bbfc99efd069d69e2b54969
Reviewed-by: hjk <hjk@qt.io>
Diffstat (limited to 'src/plugins/projectexplorer')
43 files changed, 519 insertions, 541 deletions
diff --git a/src/plugins/projectexplorer/abstractprocessstep.cpp b/src/plugins/projectexplorer/abstractprocessstep.cpp index fce8f47818..3a2ed067bf 100644 --- a/src/plugins/projectexplorer/abstractprocessstep.cpp +++ b/src/plugins/projectexplorer/abstractprocessstep.cpp @@ -106,11 +106,10 @@ public: AbstractProcessStep *q; std::unique_ptr<Utils::QtcProcess> m_process; - std::unique_ptr<IOutputParser> m_outputParserChain; + IOutputParser m_outputParser; ProcessParameters m_param; Utils::FileInProjectFinder m_fileFinder; bool m_ignoreReturnValue = false; - bool m_skipFlush = false; bool m_lowPriority = false; std::unique_ptr<QTextDecoder> stdoutStream; std::unique_ptr<QTextDecoder> stderrStream; @@ -120,6 +119,8 @@ AbstractProcessStep::AbstractProcessStep(BuildStepList *bsl, Core::Id id) : BuildStep(bsl, id), d(new Private(this)) { + connect(&d->m_outputParser, &IOutputParser::addTask, + this, &AbstractProcessStep::taskAdded); } AbstractProcessStep::~AbstractProcessStep() @@ -130,16 +131,10 @@ AbstractProcessStep::~AbstractProcessStep() /*! Deletes all existing output parsers and starts a new chain with the given parser. - - Derived classes need to call this function. */ - void AbstractProcessStep::setOutputParser(IOutputParser *parser) { - parser->addFilter(&Internal::filterAnsiEscapeCodes); - d->m_outputParserChain.reset(parser); - connect(d->m_outputParserChain.get(), &IOutputParser::addTask, - this, &AbstractProcessStep::taskAdded); + d->m_outputParser.setLineParsers({parser}); } /*! @@ -149,14 +144,18 @@ void AbstractProcessStep::appendOutputParser(IOutputParser *parser) { if (!parser) return; + d->m_outputParser.addLineParser(parser); +} - QTC_ASSERT(d->m_outputParserChain, return); - d->m_outputParserChain->appendOutputParser(parser); +void AbstractProcessStep::appendOutputParsers(const QList<IOutputParser *> &parsers) +{ + for (IOutputParser * const p : parsers) + appendOutputParser(p); } IOutputParser *AbstractProcessStep::outputParser() const { - return d->m_outputParserChain.get(); + return &d->m_outputParser; } void AbstractProcessStep::emitFaultyConfigurationMessage() @@ -189,6 +188,7 @@ void AbstractProcessStep::setIgnoreReturnValue(bool b) bool AbstractProcessStep::init() { + d->m_outputParser.addFilter(&Internal::filterAnsiEscapeCodes); d->m_fileFinder.setProjectDirectory(project()->projectDirectory()); d->m_fileFinder.setProjectFiles(project()->files(Project::AllFiles)); return !d->m_process; @@ -244,7 +244,7 @@ void AbstractProcessStep::doRun() if (!d->m_process->waitForStarted()) { processStartupFailed(); d->m_process.reset(); - d->m_outputParserChain.reset(); + d->m_outputParser.clear(); finish(false); return; } @@ -272,7 +272,7 @@ void AbstractProcessStep::cleanUp(QProcess *process) processFinished(process->exitCode(), process->exitStatus()); const bool returnValue = processSucceeded(process->exitCode(), process->exitStatus()) || d->m_ignoreReturnValue; - d->m_outputParserChain.reset(); + d->m_outputParser.clear(); d->m_process.reset(); // Report result @@ -302,8 +302,8 @@ void AbstractProcessStep::processStarted() void AbstractProcessStep::processFinished(int exitCode, QProcess::ExitStatus status) { - if (d->m_outputParserChain) - d->m_outputParserChain->flush(); + d->m_outputParser.flush(); + d->m_outputParser.clear(); QString command = QDir::toNativeSeparators(d->m_param.effectiveCommand().toString()); if (status == QProcess::NormalExit && exitCode == 0) { @@ -338,7 +338,7 @@ void AbstractProcessStep::processStartupFailed() bool AbstractProcessStep::processSucceeded(int exitCode, QProcess::ExitStatus status) { - if (outputParser() && outputParser()->hasFatalErrors()) + if (outputParser()->hasFatalErrors()) return false; return exitCode == 0 && status == QProcess::NormalExit; @@ -359,8 +359,7 @@ void AbstractProcessStep::processReadyReadStdOutput() void AbstractProcessStep::stdOutput(const QString &output) { - if (d->m_outputParserChain) - d->m_outputParserChain->handleStdout(output); + d->m_outputParser.handleStdout(output); emit addOutput(output, BuildStep::OutputFormat::Stdout, BuildStep::DontAppendNewline); } @@ -379,8 +378,7 @@ void AbstractProcessStep::processReadyReadStdError() void AbstractProcessStep::stdError(const QString &output) { - if (d->m_outputParserChain) - d->m_outputParserChain->handleStderr(output); + d->m_outputParser.handleStderr(output); emit addOutput(output, BuildStep::OutputFormat::Stderr, BuildStep::DontAppendNewline); } @@ -396,13 +394,6 @@ void AbstractProcessStep::taskAdded(const Task &task, int linkedOutputLines, int if (d->m_ignoreReturnValue) return; - // flush out any pending tasks before proceeding: - if (!d->m_skipFlush && d->m_outputParserChain) { - d->m_skipFlush = true; - d->m_outputParserChain->flushTasks(); - d->m_skipFlush = false; - } - Task editable(task); QString filePath = task.file.toString(); if (!filePath.isEmpty() && !filePath.startsWith('<') && !QDir::isAbsolutePath(filePath)) { diff --git a/src/plugins/projectexplorer/abstractprocessstep.h b/src/plugins/projectexplorer/abstractprocessstep.h index 12c1fdecbf..2ad1c1609f 100644 --- a/src/plugins/projectexplorer/abstractprocessstep.h +++ b/src/plugins/projectexplorer/abstractprocessstep.h @@ -48,6 +48,7 @@ public: void setOutputParser(IOutputParser *parser); void appendOutputParser(IOutputParser *parser); + void appendOutputParsers(const QList<IOutputParser *> &parsers); IOutputParser *outputParser() const; void emitFaultyConfigurationMessage(); diff --git a/src/plugins/projectexplorer/clangparser.cpp b/src/plugins/projectexplorer/clangparser.cpp index 4e0bb63fdd..c2deba71ae 100644 --- a/src/plugins/projectexplorer/clangparser.cpp +++ b/src/plugins/projectexplorer/clangparser.cpp @@ -25,6 +25,7 @@ #include "clangparser.h" #include "ldparser.h" +#include "lldparser.h" #include "projectexplorerconstants.h" using namespace ProjectExplorer; @@ -54,25 +55,28 @@ ClangParser::ClangParser() : setObjectName(QLatin1String("ClangParser")); } -void ClangParser::handleLine(const QString &line, OutputFormat type) +QList<IOutputParser *> ClangParser::clangParserSuite() { - if (type != StdErrFormat) { - IOutputParser::handleLine(line, type); - return; - } + return {new ClangParser, new Internal::LldParser, new LdParser}; +} + +IOutputParser::Status ClangParser::doHandleLine(const QString &line, OutputFormat type) +{ + if (type != StdErrFormat) + return Status::NotHandled; const QString lne = rightTrimmed(line); QRegularExpressionMatch match = m_summaryRegExp.match(lne); if (match.hasMatch()) { doFlush(); m_expectSnippet = false; - return; + return Status::Done; } match = m_commandRegExp.match(lne); if (match.hasMatch()) { m_expectSnippet = true; newTask(CompileTask(taskType(match.captured(3)), match.captured(4))); - return; + return Status::InProgress; } match = m_inLineRegExp.match(lne); @@ -82,7 +86,7 @@ void ClangParser::handleLine(const QString &line, OutputFormat type) lne.trimmed(), absoluteFilePath(FilePath::fromUserInput(match.captured(2))), match.captured(3).toInt() /* line */)); - return; + return Status::InProgress; } match = m_messageRegExp.match(lne); @@ -96,22 +100,22 @@ void ClangParser::handleLine(const QString &line, OutputFormat type) match.captured(8), absoluteFilePath(FilePath::fromUserInput(match.captured(1))), lineNo)); - return; + return Status::InProgress; } match = m_codesignRegExp.match(lne); if (match.hasMatch()) { m_expectSnippet = true; newTask(CompileTask(Task::Error, match.captured(1))); - return; + return Status::InProgress; } if (m_expectSnippet) { amendDescription(lne, true); - return; + return Status::InProgress; } - IOutputParser::handleLine(line, StdErrFormat); + return Status::NotHandled; } Core::Id ClangParser::id() @@ -259,7 +263,7 @@ void ProjectExplorerPlugin::testClangOutputParser_data() void ProjectExplorerPlugin::testClangOutputParser() { OutputParserTester testbench; - testbench.appendOutputParser(new ClangParser); + testbench.setLineParsers(ClangParser::clangParserSuite()); QFETCH(QString, input); QFETCH(OutputParserTester::Channel, inputChannel); QFETCH(Tasks, tasks); diff --git a/src/plugins/projectexplorer/clangparser.h b/src/plugins/projectexplorer/clangparser.h index d6ed0e2eaf..b6ef7eb2a0 100644 --- a/src/plugins/projectexplorer/clangparser.h +++ b/src/plugins/projectexplorer/clangparser.h @@ -39,10 +39,12 @@ class PROJECTEXPLORER_EXPORT ClangParser : public ProjectExplorer::GccParser public: ClangParser(); + static QList<IOutputParser *> clangParserSuite(); + static Core::Id id(); private: - void handleLine(const QString &line, Utils::OutputFormat type) override; + Status doHandleLine(const QString &line, Utils::OutputFormat type) override; QRegularExpression m_commandRegExp; QRegularExpression m_inLineRegExp; diff --git a/src/plugins/projectexplorer/customparser.cpp b/src/plugins/projectexplorer/customparser.cpp index 8ee8c900de..514a6bd12a 100644 --- a/src/plugins/projectexplorer/customparser.cpp +++ b/src/plugins/projectexplorer/customparser.cpp @@ -129,14 +129,14 @@ Core::Id CustomParser::id() return Core::Id("ProjectExplorer.OutputParser.Custom"); } -void CustomParser::handleLine(const QString &line, OutputFormat type) +IOutputParser::Status CustomParser::doHandleLine(const QString &line, OutputFormat type) { const CustomParserExpression::CustomParserChannel channel = type == StdErrFormat ? CustomParserExpression::ParseStdErrChannel : CustomParserExpression::ParseStdOutChannel; if (parseLine(line, channel)) - return; - IOutputParser::handleLine(line, type); + return Status::Done; + return Status::NotHandled; } bool CustomParser::hasMatch(const QString &line, CustomParserExpression::CustomParserChannel channel, @@ -467,7 +467,7 @@ void ProjectExplorerPlugin::testCustomOutputParsers() parser->skipFileExistsCheck(); OutputParserTester testbench; - testbench.appendOutputParser(parser); + testbench.addLineParser(parser); testbench.testParsing(input, inputChannel, tasks, childStdOutLines, childStdErrLines, outputLines); diff --git a/src/plugins/projectexplorer/customparser.h b/src/plugins/projectexplorer/customparser.h index 0df842b04b..db1f0b4c8e 100644 --- a/src/plugins/projectexplorer/customparser.h +++ b/src/plugins/projectexplorer/customparser.h @@ -91,7 +91,7 @@ public: static Core::Id id(); private: - void handleLine(const QString &line, Utils::OutputFormat type) override; + Status doHandleLine(const QString &line, Utils::OutputFormat type) override; bool hasMatch(const QString &line, CustomParserExpression::CustomParserChannel channel, const CustomParserExpression &expression, Task::TaskType taskType); diff --git a/src/plugins/projectexplorer/customtoolchain.cpp b/src/plugins/projectexplorer/customtoolchain.cpp index e24e859862..7c8de01d34 100644 --- a/src/plugins/projectexplorer/customtoolchain.cpp +++ b/src/plugins/projectexplorer/customtoolchain.cpp @@ -196,19 +196,19 @@ QStringList CustomToolChain::suggestedMkspecList() const return m_mkspecs; } -IOutputParser *CustomToolChain::outputParser() const +QList<IOutputParser *> CustomToolChain::outputParsers() const { if (m_outputParserId == GccParser::id()) - return new GccParser; + return GccParser::gccParserSuite(); if (m_outputParserId == ClangParser::id()) - return new ClangParser; + return ClangParser::clangParserSuite(); if (m_outputParserId == LinuxIccParser::id()) - return new LinuxIccParser; + return LinuxIccParser::iccParserSuite(); if (m_outputParserId == MsvcParser::id()) - return new MsvcParser; + return {new MsvcParser}; if (m_outputParserId == CustomParser::id()) - return new CustomParser(m_customParserSettings); - return nullptr; + return {new CustomParser(m_customParserSettings)}; + return {}; } QStringList CustomToolChain::headerPathsList() const diff --git a/src/plugins/projectexplorer/customtoolchain.h b/src/plugins/projectexplorer/customtoolchain.h index 074a09e0f5..d554d44f4f 100644 --- a/src/plugins/projectexplorer/customtoolchain.h +++ b/src/plugins/projectexplorer/customtoolchain.h @@ -84,7 +84,7 @@ public: const Utils::Environment &env) const override; void addToEnvironment(Utils::Environment &env) const override; QStringList suggestedMkspecList() const override; - IOutputParser *outputParser() const override; + QList<IOutputParser *> outputParsers() const override; QStringList headerPathsList() const; void setHeaderPaths(const QStringList &list); diff --git a/src/plugins/projectexplorer/gccparser.cpp b/src/plugins/projectexplorer/gccparser.cpp index a96e0b2cf2..22fc040ed0 100644 --- a/src/plugins/projectexplorer/gccparser.cpp +++ b/src/plugins/projectexplorer/gccparser.cpp @@ -59,27 +59,75 @@ GccParser::GccParser() // optional .exe postfix m_regExpGccNames.setPattern(QLatin1String(COMMAND_PATTERN)); QTC_CHECK(m_regExpGccNames.isValid()); +} + +Core::Id GccParser::id() +{ + return Core::Id("ProjectExplorer.OutputParser.Gcc"); +} + +QList<IOutputParser *> GccParser::gccParserSuite() +{ + return {new GccParser, new Internal::LldParser, new LdParser}; +} + +void GccParser::newTask(const Task &task) +{ + doFlush(); + m_currentTask = task; + m_lines = 1; +} + +void GccParser::doFlush() +{ + if (m_currentTask.isNull()) + return; + Task t = m_currentTask; + m_currentTask.clear(); + emit addTask(t, m_lines, 1); + m_lines = 0; +} - appendOutputParser(new Internal::LldParser); - appendOutputParser(new LdParser); +void GccParser::amendDescription(const QString &desc, bool monospaced) +{ + if (m_currentTask.isNull()) + return; + int start = m_currentTask.description.count() + 1; + m_currentTask.description.append(QLatin1Char('\n')); + m_currentTask.description.append(desc); + if (monospaced) { + QTextLayout::FormatRange fr; + fr.start = start; + fr.length = desc.count() + 1; + fr.format.setFont(TextEditor::TextEditorSettings::fontSettings().font()); + fr.format.setFontStyleHint(QFont::Monospace); + m_currentTask.formats.append(fr); + } + ++m_lines; + return; } -void GccParser::stdError(const QString &line) +IOutputParser::Status GccParser::doHandleLine(const QString &line, OutputFormat type) { - QString lne = rightTrimmed(line); + if (type == StdOutFormat) { + // TODO: The "flush on channel switch" logic could possibly also done centrally. + // But see MSVC with the stdout/stderr switches because of jom + doFlush(); + return Status::NotHandled; + } + + const QString lne = rightTrimmed(line); // Blacklist some lines to not handle them: if (lne.startsWith(QLatin1String("TeamBuilder ")) || lne.startsWith(QLatin1String("distcc["))) { - IOutputParser::handleLine(line, StdErrFormat); - return; + return Status::NotHandled; } // Handle misc issues: - if (lne.startsWith(QLatin1String("ERROR:")) || - lne == QLatin1String("* cpp failed")) { + if (lne.startsWith(QLatin1String("ERROR:")) || lne == QLatin1String("* cpp failed")) { newTask(CompileTask(Task::Error, lne /* description */)); - return; + return Status::InProgress; } QRegularExpressionMatch match = m_regExpGccNames.match(lne); @@ -93,7 +141,7 @@ void GccParser::stdError(const QString &line) description = description.mid(7); } newTask(CompileTask(type, description)); - return; + return Status::InProgress; } match = m_regExp.match(lne); @@ -114,7 +162,7 @@ void GccParser::stdError(const QString &line) description = match.captured(5) + description; newTask(CompileTask(type, description, absoluteFilePath(filename), lineno)); - return; + return Status::InProgress; } match = m_regExpIncluded.match(lne); @@ -123,69 +171,14 @@ void GccParser::stdError(const QString &line) lne.trimmed() /* description */, absoluteFilePath(Utils::FilePath::fromUserInput(match.captured(1))), match.captured(3).toInt() /* linenumber */)); - return; + return Status::InProgress; } else if (lne.startsWith(' ') && !m_currentTask.isNull()) { amendDescription(lne, true); - return; + return Status::InProgress; } doFlush(); - IOutputParser::handleLine(line, StdErrFormat); -} - -void GccParser::stdOutput(const QString &line) -{ - doFlush(); - IOutputParser::handleLine(line, StdOutFormat); -} - -Core::Id GccParser::id() -{ - return Core::Id("ProjectExplorer.OutputParser.Gcc"); -} - -void GccParser::newTask(const Task &task) -{ - doFlush(); - m_currentTask = task; - m_lines = 1; -} - -void GccParser::doFlush() -{ - if (m_currentTask.isNull()) - return; - Task t = m_currentTask; - m_currentTask.clear(); - emit addTask(t, m_lines, 1); - m_lines = 0; -} - -void GccParser::amendDescription(const QString &desc, bool monospaced) -{ - if (m_currentTask.isNull()) - return; - int start = m_currentTask.description.count() + 1; - m_currentTask.description.append(QLatin1Char('\n')); - m_currentTask.description.append(desc); - if (monospaced) { - QTextLayout::FormatRange fr; - fr.start = start; - fr.length = desc.count() + 1; - fr.format.setFont(TextEditor::TextEditorSettings::fontSettings().font()); - fr.format.setFontStyleHint(QFont::Monospace); - m_currentTask.formats.append(fr); - } - ++m_lines; - return; -} - -void GccParser::handleLine(const QString &line, OutputFormat type) -{ - if (type == StdOutFormat) - stdOutput(line); - else - stdError(line); + return Status::NotHandled; } // Unit tests: @@ -1130,7 +1123,7 @@ void ProjectExplorerPlugin::testGccOutputParsers_data() void ProjectExplorerPlugin::testGccOutputParsers() { OutputParserTester testbench; - testbench.appendOutputParser(new GccParser); + testbench.setLineParsers(GccParser::gccParserSuite()); QFETCH(QString, input); QFETCH(OutputParserTester::Channel, inputChannel); QFETCH(Tasks, tasks); diff --git a/src/plugins/projectexplorer/gccparser.h b/src/plugins/projectexplorer/gccparser.h index 6d795c6750..e2010a7cf3 100644 --- a/src/plugins/projectexplorer/gccparser.h +++ b/src/plugins/projectexplorer/gccparser.h @@ -42,6 +42,8 @@ public: static Core::Id id(); + static QList<IOutputParser *> gccParserSuite(); + protected: void newTask(const Task &task); void doFlush() override; @@ -49,10 +51,7 @@ protected: void amendDescription(const QString &desc, bool monospaced); private: - void handleLine(const QString &line, Utils::OutputFormat type) override; - - void stdError(const QString &line); - void stdOutput(const QString &line); + Status doHandleLine(const QString &line, Utils::OutputFormat type) override; QRegularExpression m_regExp; QRegularExpression m_regExpIncluded; diff --git a/src/plugins/projectexplorer/gcctoolchain.cpp b/src/plugins/projectexplorer/gcctoolchain.cpp index 4b2d04e6e0..cb4f8ce216 100644 --- a/src/plugins/projectexplorer/gcctoolchain.cpp +++ b/src/plugins/projectexplorer/gcctoolchain.cpp @@ -731,9 +731,9 @@ FilePath GccToolChain::makeCommand(const Environment &environment) const return tmp.isEmpty() ? FilePath::fromString("make") : tmp; } -IOutputParser *GccToolChain::outputParser() const +QList<IOutputParser *> GccToolChain::outputParsers() const { - return new GccParser; + return GccParser::gccParserSuite(); } void GccToolChain::resetToolChain(const FilePath &path) @@ -1628,9 +1628,9 @@ LanguageExtensions ClangToolChain::defaultLanguageExtensions() const return LanguageExtension::Gnu; } -IOutputParser *ClangToolChain::outputParser() const +QList<IOutputParser *> ClangToolChain::outputParsers() const { - return new ClangParser; + return ClangParser::clangParserSuite(); } // -------------------------------------------------------------------------- @@ -1898,9 +1898,9 @@ LanguageExtensions LinuxIccToolChain::languageExtensions(const QStringList &cxxf return extensions; } -IOutputParser *LinuxIccToolChain::outputParser() const +QList<IOutputParser *> LinuxIccToolChain::outputParsers() const { - return new LinuxIccParser; + return LinuxIccParser::iccParserSuite(); } QStringList LinuxIccToolChain::suggestedMkspecList() const diff --git a/src/plugins/projectexplorer/gcctoolchain.h b/src/plugins/projectexplorer/gcctoolchain.h index a0761aa1fe..60d68d66a2 100644 --- a/src/plugins/projectexplorer/gcctoolchain.h +++ b/src/plugins/projectexplorer/gcctoolchain.h @@ -94,7 +94,7 @@ public: void addToEnvironment(Utils::Environment &env) const override; Utils::FilePath makeCommand(const Utils::Environment &environment) const override; QStringList suggestedMkspecList() const override; - IOutputParser *outputParser() const override; + QList<IOutputParser *> outputParsers() const override; QVariantMap toMap() const override; bool fromMap(const QVariantMap &data) override; @@ -226,7 +226,7 @@ public: Utils::LanguageExtensions languageExtensions(const QStringList &cxxflags) const override; Utils::WarningFlags warningFlags(const QStringList &cflags) const override; - IOutputParser *outputParser() const override; + QList<IOutputParser *> outputParsers() const override; QStringList suggestedMkspecList() const override; void addToEnvironment(Utils::Environment &env) const override; @@ -286,7 +286,7 @@ class PROJECTEXPLORER_EXPORT LinuxIccToolChain : public GccToolChain public: Utils::LanguageExtensions languageExtensions(const QStringList &cxxflags) const override; - IOutputParser *outputParser() const override; + QList<IOutputParser *> outputParsers() const override; QStringList suggestedMkspecList() const override; diff --git a/src/plugins/projectexplorer/gnumakeparser.cpp b/src/plugins/projectexplorer/gnumakeparser.cpp index 2d8769d8db..f9d681348d 100644 --- a/src/plugins/projectexplorer/gnumakeparser.cpp +++ b/src/plugins/projectexplorer/gnumakeparser.cpp @@ -56,35 +56,6 @@ GnuMakeParser::GnuMakeParser() QTC_CHECK(m_errorInMakefile.isValid()); } -void GnuMakeParser::handleLine(const QString &line, OutputFormat type) -{ - if (type == StdOutFormat) - stdOutput(line); - else - stdError(line); -} - -bool GnuMakeParser::hasFatalErrors() const -{ - return (m_fatalErrorCount > 0) || IOutputParser::hasFatalErrors(); -} - -void GnuMakeParser::stdOutput(const QString &line) -{ - const QString lne = rightTrimmed(line); - - QRegularExpressionMatch match = m_makeDir.match(lne); - if (match.hasMatch()) { - if (match.captured(6) == QLatin1String("Leaving")) - dropSearchDir(FilePath::fromString(match.captured(7))); - else - addSearchDir(FilePath::fromString(match.captured(7))); - return; - } - - IOutputParser::handleLine(line, StdOutFormat); -} - class Result { public: Result() = default; @@ -125,13 +96,29 @@ static Result parseDescription(const QString &description) return result; } -void GnuMakeParser::stdError(const QString &line) +void GnuMakeParser::emitTask(const ProjectExplorer::Task &task) { - const QString lne = rightTrimmed(line); + if (task.type == Task::Error) // Assume that all make errors will be follow up errors. + m_suppressIssues = true; + emit addTask(task, 1, 0); +} +IOutputParser::Status GnuMakeParser::doHandleLine(const QString &line, OutputFormat type) +{ + const QString lne = rightTrimmed(line); + if (type == StdOutFormat) { + QRegularExpressionMatch match = m_makeDir.match(lne); + if (match.hasMatch()) { + if (match.captured(6) == QLatin1String("Leaving")) + emit searchDirOut(FilePath::fromString(match.captured(7))); + else + emit searchDirIn(FilePath::fromString(match.captured(7))); + return Status::Done; + } + return Status::NotHandled; + } QRegularExpressionMatch match = m_errorInMakefile.match(lne); if (match.hasMatch()) { - flush(); Result res = parseDescription(match.captured(5)); if (res.isFatal) ++m_fatalErrorCount; @@ -140,27 +127,24 @@ void GnuMakeParser::stdError(const QString &line) absoluteFilePath(FilePath::fromUserInput(match.captured(1))), match.captured(4).toInt() /* line */)); } - return; + return Status::Done; } match = m_makeLine.match(lne); if (match.hasMatch()) { - flush(); Result res = parseDescription(match.captured(6)); if (res.isFatal) ++m_fatalErrorCount; if (!m_suppressIssues) emitTask(BuildSystemTask(res.type, res.description)); - return; + return Status::Done; } - IOutputParser::handleLine(line, StdErrFormat); + return Status::NotHandled; } -void GnuMakeParser::emitTask(const ProjectExplorer::Task &task) +bool GnuMakeParser::hasFatalErrors() const { - if (task.type == Task::Error) // Assume that all make errors will be follow up errors. - m_suppressIssues = true; - emit addTask(task, 1, 0); + return (m_fatalErrorCount > 0) || IOutputParser::hasFatalErrors(); } } // ProjectExplorer @@ -371,7 +355,7 @@ void ProjectExplorerPlugin::testGnuMakeParserParsing() connect(&testbench, &OutputParserTester::aboutToDeleteParser, tester, &GnuMakeParserTester::parserIsAboutToBeDeleted); - testbench.appendOutputParser(childParser); + testbench.addLineParser(childParser); QFETCH(QStringList, extraSearchDirs); QFETCH(QString, input); QFETCH(OutputParserTester::Channel, inputChannel); @@ -381,11 +365,11 @@ void ProjectExplorerPlugin::testGnuMakeParserParsing() QFETCH(QString, outputLines); QFETCH(QStringList, additionalSearchDirs); - FilePaths searchDirs = childParser->searchDirectories(); + FilePaths searchDirs = testbench.searchDirectories(); // add extra directories: foreach (const QString &dir, extraSearchDirs) - childParser->addSearchDir(FilePath::fromString(dir)); + testbench.addSearchDir(FilePath::fromString(dir)); testbench.testParsing(input, inputChannel, tasks, childStdOutLines, childStdErrLines, @@ -418,7 +402,7 @@ void ProjectExplorerPlugin::testGnuMakeParserTaskMangling() OutputParserTester testbench; auto *childParser = new GnuMakeParser; - testbench.appendOutputParser(childParser); + testbench.addLineParser(childParser); childParser->addSearchDir(FilePath::fromString(fi.absolutePath())); testbench.testParsing( fi.fileName() + ":360: *** missing separator (did you mean TAB instead of 8 spaces?). Stop.", diff --git a/src/plugins/projectexplorer/gnumakeparser.h b/src/plugins/projectexplorer/gnumakeparser.h index 3557158594..c514f865b6 100644 --- a/src/plugins/projectexplorer/gnumakeparser.h +++ b/src/plugins/projectexplorer/gnumakeparser.h @@ -40,12 +40,9 @@ public: explicit GnuMakeParser(); private: - void handleLine(const QString &line, Utils::OutputFormat type) override; + Status doHandleLine(const QString &line, Utils::OutputFormat type) override; bool hasFatalErrors() const override; - void stdOutput(const QString &line); - void stdError(const QString &line); - void emitTask(const ProjectExplorer::Task &task); QRegularExpression m_makeDir; diff --git a/src/plugins/projectexplorer/ioutputparser.cpp b/src/plugins/projectexplorer/ioutputparser.cpp index bd9590ef52..72cb25b811 100644 --- a/src/plugins/projectexplorer/ioutputparser.cpp +++ b/src/plugins/projectexplorer/ioutputparser.cpp @@ -26,10 +26,12 @@ #include "ioutputparser.h" #include "task.h" +#include <utils/algorithm.h> #include <utils/synchronousprocess.h> #include <QDir> #include <QFileInfo> +#include <QPointer> /*! \class ProjectExplorer::IOutputParser @@ -41,21 +43,7 @@ */ /*! - \fn void ProjectExplorer::IOutputParser::appendOutputParser(IOutputParser *parser) - - Appends a subparser to this parser, of which IOutputParser will take - ownership. -*/ - -/*! - \fn IOutputParser *ProjectExplorer::IOutputParser::childParser() const - - Returns the head of this parser's output parser children. IOutputParser - keeps ownership. -*/ - -/*! - \fn void ProjectExplorer::IOutputParser::handleLine(const QString &line, Utils::OutputFormat type) + \fn ProjectExplorer::IOutputParser::Status ProjectExplorer::IOutputParser::doHandleLine(const QString &line, Utils::OutputFormat type) Called once for each line of standard output or standard error to parse. */ @@ -67,28 +55,12 @@ */ /*! - \fn void ProjectExplorer::IOutputParser::addOutput(const QString &string, ProjectExplorer::BuildStep::OutputFormat format) - - Should be emitted whenever some additional information should be added to the - output. - - \note This is additional information. There is no need to add each line. -*/ - -/*! \fn void ProjectExplorer::IOutputParser::addTask(const ProjectExplorer::Task &task) Should be emitted for each task seen in the output. */ /*! - \fn void ProjectExplorer::IOutputParser::taskAdded(const ProjectExplorer::Task &task) - - Subparsers have their addTask signal connected to this slot. - This function can be overwritten to change the task. -*/ - -/*! \fn void ProjectExplorer::IOutputParser::doFlush() Instructs a parser to flush its state. @@ -121,14 +93,14 @@ public: break; const QString line = pendingData.left(eolPos + 1); pendingData.remove(0, eolPos + 1); - parser->handleLine(parser->filteredLine(line), type); + parser->handleLine(line, type); } } void flush() { if (!pendingData.isEmpty()) { - parser->handleLine(parser->filteredLine(pendingData), type); + parser->handleLine(pendingData, type); pendingData.clear(); } } @@ -146,11 +118,13 @@ public: stderrState(parser, Utils::StdErrFormat) {} - IOutputParser *childParser = nullptr; + QList<IOutputParser *> lineParsers; + IOutputParser *nextParser = nullptr; QList<Filter> filters; Utils::FilePaths searchDirs; OutputChannelState stdoutState; OutputChannelState stderrState; + QPointer<const IOutputParser> redirectionDetector; bool skipFileExistsCheck = false; }; @@ -160,7 +134,7 @@ IOutputParser::IOutputParser() : d(new IOutputParserPrivate(this)) IOutputParser::~IOutputParser() { - delete d->childParser; + clear(); delete d; } @@ -174,73 +148,115 @@ void IOutputParser::handleStderr(const QString &data) d->stderrState.handleData(data); } -void IOutputParser::appendOutputParser(IOutputParser *parser) +IOutputParser::Status IOutputParser::doHandleLine(const QString &line, Utils::OutputFormat type) { - if (!parser) - return; - if (d->childParser) { - d->childParser->appendOutputParser(parser); - return; + Q_UNUSED(line); + Q_UNUSED(type); + return Status::NotHandled; +} + +void IOutputParser::doFlush() { } + +void IOutputParser::handleLine(const QString &line, Utils::OutputFormat type) +{ + const QString cleanLine = filteredLine(line); + if (d->nextParser) { + switch (d->nextParser->doHandleLine(cleanLine, outputTypeForParser(d->nextParser, type))) { + case Status::Done: + d->nextParser = nullptr; + return; + case Status::InProgress: + return; + case Status::NotHandled: + d->nextParser = nullptr; + break; + } } + QTC_CHECK(!d->nextParser); + for (IOutputParser * const lineParser : d->lineParsers) { + switch (lineParser->doHandleLine(cleanLine, outputTypeForParser(lineParser, type))) { + case Status::Done: + return; + case Status::InProgress: + d->nextParser = lineParser; + return; + case Status::NotHandled: + break; + } + } +} - d->childParser = parser; - connect(parser, &IOutputParser::addTask, this, &IOutputParser::addTask); +QString IOutputParser::filteredLine(const QString &line) const +{ + QString l = line; + for (const IOutputParser::Filter &f : qAsConst(d->filters)) + l = f(l); + return l; } -IOutputParser *IOutputParser::childParser() const +void IOutputParser::connectLineParser(IOutputParser *parser) { - return d->childParser; + connect(parser, &IOutputParser::addTask, this, &IOutputParser::addTask); + connect(parser, &IOutputParser::searchDirIn, this, &IOutputParser::addSearchDir); + connect(parser, &IOutputParser::searchDirOut, this, &IOutputParser::dropSearchDir); } -void IOutputParser::setChildParser(IOutputParser *parser) +bool IOutputParser::hasFatalErrors() const { - if (d->childParser != parser) - delete d->childParser; - d->childParser = parser; - if (parser) - connect(parser, &IOutputParser::addTask, this, &IOutputParser::addTask); + return Utils::anyOf(d->lineParsers, [](const IOutputParser *p) { return p->hasFatalErrors(); }); } -void IOutputParser::handleLine(const QString &line, Utils::OutputFormat type) +void IOutputParser::flush() { - if (d->childParser) - d->childParser->handleLine(line, type); + d->stdoutState.flush(); + d->stderrState.flush(); + doFlush(); + for (IOutputParser * const p : qAsConst(d->lineParsers)) + p->doFlush(); } -void IOutputParser::skipFileExistsCheck() +void IOutputParser::clear() { - d->skipFileExistsCheck = true; + d->nextParser = nullptr; + d->redirectionDetector.clear(); + d->filters.clear(); + d->searchDirs.clear(); + qDeleteAll(d->lineParsers); + d->lineParsers.clear(); + d->stdoutState.pendingData.clear(); + d->stderrState.pendingData.clear(); } -void IOutputParser::doFlush() { } +void IOutputParser::addLineParser(IOutputParser *parser) +{ + connectLineParser(parser); + d->lineParsers << parser; +} -QString IOutputParser::filteredLine(const QString &line) const +void IOutputParser::addLineParsers(const QList<IOutputParser *> &parsers) { - QString l = line; - for (const IOutputParser::Filter &f : qAsConst(d->filters)) - l = f(l); - return l; + for (IOutputParser * const p : qAsConst(parsers)) + addLineParser(p); } -bool IOutputParser::hasFatalErrors() const +void IOutputParser::setLineParsers(const QList<IOutputParser *> &parsers) { - return d->childParser && d->childParser->hasFatalErrors(); + qDeleteAll(d->lineParsers); + d->lineParsers.clear(); + addLineParsers(parsers); } -void IOutputParser::flush() +#ifdef WITH_TESTS +QList<IOutputParser *> IOutputParser::lineParsers() const { - flushTasks(); - d->stdoutState.flush(); - d->stderrState.flush(); - flushTasks(); + return d->lineParsers; } -void IOutputParser::flushTasks() +void IOutputParser::skipFileExistsCheck() { - doFlush(); - if (d->childParser) - d->childParser->flushTasks(); + d->skipFileExistsCheck = true; } +#endif // WITH_TESTS QString IOutputParser::rightTrimmed(const QString &in) { @@ -260,8 +276,8 @@ void IOutputParser::addFilter(const Filter &filter) void IOutputParser::addSearchDir(const Utils::FilePath &dir) { d->searchDirs << dir; - if (d->childParser) - d->childParser->addSearchDir(dir); + for (IOutputParser * const p : qAsConst(d->lineParsers)) + p->addSearchDir(dir); } void IOutputParser::dropSearchDir(const Utils::FilePath &dir) @@ -269,8 +285,8 @@ void IOutputParser::dropSearchDir(const Utils::FilePath &dir) const int idx = d->searchDirs.lastIndexOf(dir); QTC_ASSERT(idx != -1, return); d->searchDirs.removeAt(idx); - if (d->childParser) - d->childParser->dropSearchDir(dir); + for (IOutputParser * const p : qAsConst(d->lineParsers)) + p->dropSearchDir(dir); } const Utils::FilePaths IOutputParser::searchDirectories() const @@ -293,4 +309,27 @@ Utils::FilePath IOutputParser::absoluteFilePath(const Utils::FilePath &filePath) return filePath; } +// The redirection mechanism is needed for broken build tools (e.g. xcodebuild) that get invoked +// indirectly as part of the build process and redirect their child processes' stderr output +// to stdout. A parser might be able to detect this condition and inform interested +// other parsers that they need to interpret stdout data as stderr. +void IOutputParser::setRedirectionDetector(const IOutputParser *detector) +{ + d->redirectionDetector = detector; +} + +bool IOutputParser::needsRedirection() const +{ + return d->redirectionDetector && (d->redirectionDetector->hasDetectedRedirection() + || d->redirectionDetector->needsRedirection()); +} + +Utils::OutputFormat IOutputParser::outputTypeForParser(const IOutputParser *parser, + Utils::OutputFormat type) const +{ + if (type == Utils::StdOutFormat && parser->needsRedirection()) + return Utils::StdErrFormat; + return type; +} + } // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/ioutputparser.h b/src/plugins/projectexplorer/ioutputparser.h index e200f7eef0..3018b58796 100644 --- a/src/plugins/projectexplorer/ioutputparser.h +++ b/src/plugins/projectexplorer/ioutputparser.h @@ -47,11 +47,6 @@ public: void handleStdout(const QString &data); void handleStderr(const QString &data); - void appendOutputParser(IOutputParser *parser); - - IOutputParser *childParser() const; - void setChildParser(IOutputParser *parser); - virtual bool hasFatalErrors() const; using Filter = std::function<QString(const QString &)>; @@ -60,25 +55,44 @@ public: void addSearchDir(const Utils::FilePath &dir); void dropSearchDir(const Utils::FilePath &dir); const Utils::FilePaths searchDirectories() const; - void skipFileExistsCheck(); // For testing only - void flush(); // flush pending tasks & output - void flushTasks(); // flush pending tasks only + void flush(); + void clear(); + + void addLineParser(IOutputParser *parser); + void addLineParsers(const QList<IOutputParser *> &parsers); + void setLineParsers(const QList<IOutputParser *> &parsers); + +#ifdef WITH_TESTS + QList<IOutputParser *> lineParsers() const; + void skipFileExistsCheck(); +#endif + + void setRedirectionDetector(const IOutputParser *detector); static QString rightTrimmed(const QString &in); signals: + void searchDirIn(const Utils::FilePath &dir); + void searchDirOut(const Utils::FilePath &dir); void addTask(const ProjectExplorer::Task &task, int linkedOutputLines = 0, int skipLines = 0); protected: - virtual void handleLine(const QString &line, Utils::OutputFormat type); + enum class Status { Done, InProgress, NotHandled }; Utils::FilePath absoluteFilePath(const Utils::FilePath &filePath); private: + virtual Status doHandleLine(const QString &line, Utils::OutputFormat type); virtual void doFlush(); + virtual bool hasDetectedRedirection() const { return false; } + void handleLine(const QString &line, Utils::OutputFormat type); QString filteredLine(const QString &line) const; + void connectLineParser(IOutputParser *parser); + bool needsRedirection() const; + Utils::OutputFormat outputTypeForParser(const IOutputParser *parser, + Utils::OutputFormat type) const; class OutputChannelState; class IOutputParserPrivate; diff --git a/src/plugins/projectexplorer/kit.cpp b/src/plugins/projectexplorer/kit.cpp index 75f52ec1c8..52b13dc38d 100644 --- a/src/plugins/projectexplorer/kit.cpp +++ b/src/plugins/projectexplorer/kit.cpp @@ -559,12 +559,12 @@ void Kit::addToEnvironment(Environment &env) const aspect->addToEnvironment(this, env); } -IOutputParser *Kit::createOutputParser() const +QList<IOutputParser *> Kit::createOutputParsers() const { - auto first = new OsParser; + QList<IOutputParser *> parsers{new OsParser}; for (KitAspect *aspect : KitManager::kitAspects()) - first->appendOutputParser(aspect->createOutputParser(this)); - return first; + parsers << aspect->createOutputParsers(this); + return parsers; } QString Kit::toHtml(const Tasks &additional, const QString &extraText) const diff --git a/src/plugins/projectexplorer/kit.h b/src/plugins/projectexplorer/kit.h index fe76b750dc..831a450827 100644 --- a/src/plugins/projectexplorer/kit.h +++ b/src/plugins/projectexplorer/kit.h @@ -116,7 +116,7 @@ public: bool isEqual(const Kit *other) const; void addToEnvironment(Utils::Environment &env) const; - IOutputParser *createOutputParser() const; + QList<IOutputParser *> createOutputParsers() const; QString toHtml(const Tasks &additional = Tasks(), const QString &extraText = QString()) const; Kit *clone(bool keepName = false) const; diff --git a/src/plugins/projectexplorer/kitinformation.cpp b/src/plugins/projectexplorer/kitinformation.cpp index d21ededfd7..ef0520e169 100644 --- a/src/plugins/projectexplorer/kitinformation.cpp +++ b/src/plugins/projectexplorer/kitinformation.cpp @@ -557,14 +557,13 @@ void ToolChainKitAspect::addToMacroExpander(Kit *kit, Utils::MacroExpander *expa }); } - -IOutputParser *ToolChainKitAspect::createOutputParser(const Kit *k) const +QList<IOutputParser *> ToolChainKitAspect::createOutputParsers(const Kit *k) const { for (const Core::Id langId : {Constants::CXX_LANGUAGE_ID, Constants::C_LANGUAGE_ID}) { if (const ToolChain * const tc = toolChain(k, langId)) - return tc->outputParser(); + return tc->outputParsers(); } - return nullptr; + return {}; } QSet<Core::Id> ToolChainKitAspect::availableFeatures(const Kit *k) const diff --git a/src/plugins/projectexplorer/kitinformation.h b/src/plugins/projectexplorer/kitinformation.h index e4179f2a61..4eb3417195 100644 --- a/src/plugins/projectexplorer/kitinformation.h +++ b/src/plugins/projectexplorer/kitinformation.h @@ -84,7 +84,7 @@ public: void addToEnvironment(const Kit *k, Utils::Environment &env) const override; void addToMacroExpander(Kit *kit, Utils::MacroExpander *expander) const override; - IOutputParser *createOutputParser(const Kit *k) const override; + QList<IOutputParser *> createOutputParsers(const Kit *k) const override; QSet<Core::Id> availableFeatures(const Kit *k) const override; static Core::Id id(); diff --git a/src/plugins/projectexplorer/kitmanager.cpp b/src/plugins/projectexplorer/kitmanager.cpp index f989b955cc..72b727847b 100644 --- a/src/plugins/projectexplorer/kitmanager.cpp +++ b/src/plugins/projectexplorer/kitmanager.cpp @@ -677,10 +677,10 @@ void KitAspect::addToEnvironment(const Kit *k, Environment &env) const Q_UNUSED(env) } -IOutputParser *KitAspect::createOutputParser(const Kit *k) const +QList<IOutputParser *> KitAspect::createOutputParsers(const Kit *k) const { Q_UNUSED(k) - return nullptr; + return {}; } QString KitAspect::displayNamePostfix(const Kit *k) const diff --git a/src/plugins/projectexplorer/kitmanager.h b/src/plugins/projectexplorer/kitmanager.h index 21228f44c2..d70c2f42b5 100644 --- a/src/plugins/projectexplorer/kitmanager.h +++ b/src/plugins/projectexplorer/kitmanager.h @@ -91,7 +91,7 @@ public: virtual KitAspectWidget *createConfigWidget(Kit *) const = 0; virtual void addToEnvironment(const Kit *k, Utils::Environment &env) const; - virtual IOutputParser *createOutputParser(const Kit *k) const; + virtual QList<IOutputParser *> createOutputParsers(const Kit *k) const; virtual QString displayNamePostfix(const Kit *k) const; diff --git a/src/plugins/projectexplorer/ldparser.cpp b/src/plugins/projectexplorer/ldparser.cpp index 4892ae90d5..f99a02d354 100644 --- a/src/plugins/projectexplorer/ldparser.cpp +++ b/src/plugins/projectexplorer/ldparser.cpp @@ -54,27 +54,27 @@ LdParser::LdParser() QTC_CHECK(m_regExpGccNames.isValid()); } -void LdParser::handleLine(const QString &line, Utils::OutputFormat type) +IOutputParser::Status LdParser::doHandleLine(const QString &line, Utils::OutputFormat type) { - if (type != Utils::StdErrFormat) { - IOutputParser::handleLine(line, type); - return; - } + if (type != Utils::StdErrFormat) + return Status::NotHandled; + QString lne = rightTrimmed(line); - if (!lne.isEmpty() && !lne.at(0).isSpace() && !m_incompleteTask.isNull()) - flush(); + if (!lne.isEmpty() && !lne.at(0).isSpace() && !m_incompleteTask.isNull()) { + doFlush(); + return Status::NotHandled; + } if (lne.startsWith(QLatin1String("TeamBuilder ")) || lne.startsWith(QLatin1String("distcc[")) || lne.contains(QLatin1String("ar: creating "))) { - IOutputParser::handleLine(line, Utils::StdErrFormat); - return; + return Status::NotHandled; } // ld on macOS if (lne.startsWith("Undefined symbols for architecture") && lne.endsWith(":")) { m_incompleteTask = CompileTask(Task::Error, lne); - return; + return Status::InProgress; } if (!m_incompleteTask.isNull() && lne.startsWith(" ")) { m_incompleteTask.description.append('\n').append(lne); @@ -82,19 +82,19 @@ void LdParser::handleLine(const QString &line, Utils::OutputFormat type) const QRegularExpressionMatch match = locRegExp.match(lne); if (match.hasMatch()) m_incompleteTask.setFile(Utils::FilePath::fromString(match.captured("file"))); - return; + return Status::InProgress; } if (lne.startsWith("collect2:") || lne.startsWith("collect2.exe:")) { emit addTask(CompileTask(Task::Error, lne /* description */), 1); - return; + return Status::Done; } QRegularExpressionMatch match = m_ranlib.match(lne); if (match.hasMatch()) { QString description = match.captured(2); emit addTask(CompileTask(Task::Warning, description), 1); - return; + return Status::Done; } match = m_regExpGccNames.match(lne); @@ -108,7 +108,7 @@ void LdParser::handleLine(const QString &line, Utils::OutputFormat type) description = description.mid(7); } emit addTask(CompileTask(type, description), 1); - return; + return Status::Done; } match = m_regExpLinker.match(lne); @@ -138,10 +138,10 @@ void LdParser::handleLine(const QString &line, Utils::OutputFormat type) description = description.mid(9); } emit addTask(CompileTask(type, description, absoluteFilePath(filename), lineno), 1); - return; + return Status::Done; } - IOutputParser::handleLine(line, Utils::StdErrFormat); + return Status::NotHandled; } void LdParser::doFlush() diff --git a/src/plugins/projectexplorer/ldparser.h b/src/plugins/projectexplorer/ldparser.h index a2b48a9c20..e07931636a 100644 --- a/src/plugins/projectexplorer/ldparser.h +++ b/src/plugins/projectexplorer/ldparser.h @@ -39,7 +39,7 @@ class LdParser : public ProjectExplorer::IOutputParser public: LdParser(); private: - void handleLine(const QString &line, Utils::OutputFormat type) override; + Status doHandleLine(const QString &line, Utils::OutputFormat type) override; void doFlush() override; QRegularExpression m_ranlib; diff --git a/src/plugins/projectexplorer/linuxiccparser.cpp b/src/plugins/projectexplorer/linuxiccparser.cpp index 4d25e7f31e..a8ba996ea5 100644 --- a/src/plugins/projectexplorer/linuxiccparser.cpp +++ b/src/plugins/projectexplorer/linuxiccparser.cpp @@ -63,21 +63,15 @@ LinuxIccParser::LinuxIccParser() : m_pchInfoLine.setPattern(QLatin1String("^\".*\": (creating|using) precompiled header file \".*\"\n$")); m_pchInfoLine.setMinimal(true); QTC_CHECK(m_pchInfoLine.isValid()); - - appendOutputParser(new Internal::LldParser); - appendOutputParser(new LdParser); } -void LinuxIccParser::handleLine(const QString &line, OutputFormat type) +IOutputParser::Status LinuxIccParser::doHandleLine(const QString &line, OutputFormat type) { - if (type != Utils::StdErrFormat) { - IOutputParser::handleLine(line, type); - return; - } - if (m_pchInfoLine.indexIn(line) != -1) { - // totally ignore this line - return; - } + if (type != Utils::StdErrFormat) + return Status::NotHandled; + + if (m_pchInfoLine.indexIn(line) != -1) + return Status::Done; // totally ignore this line if (m_expectFirstLine && m_firstLine.indexIn(line) != -1) { // Clear out old task @@ -94,7 +88,9 @@ void LinuxIccParser::handleLine(const QString &line, OutputFormat type) m_lines = 1; m_expectFirstLine = false; - } else if (!m_expectFirstLine && m_caretLine.indexIn(line) != -1) { + return Status::InProgress; + } + if (!m_expectFirstLine && m_caretLine.indexIn(line) != -1) { // Format the last line as code QTextLayout::FormatRange fr; fr.start = m_temporary.description.lastIndexOf(QLatin1Char('\n')) + 1; @@ -107,20 +103,25 @@ void LinuxIccParser::handleLine(const QString &line, OutputFormat type) fr2.length = 1; fr2.format.setFontWeight(QFont::Bold); m_temporary.formats.append(fr2); - } else if (!m_expectFirstLine && line.trimmed().isEmpty()) { // last Line + return Status::InProgress; + } + if (!m_expectFirstLine && line.trimmed().isEmpty()) { // last Line m_expectFirstLine = true; emit addTask(m_temporary, m_lines); m_temporary = Task(); - } else if (!m_expectFirstLine && m_continuationLines.indexIn(line) != -1) { + return Status::Done; + } + if (!m_expectFirstLine && m_continuationLines.indexIn(line) != -1) { m_temporary.description.append(QLatin1Char('\n')); m_indent = 0; while (m_indent < line.length() && line.at(m_indent).isSpace()) m_indent++; m_temporary.description.append(m_continuationLines.cap(1).trimmed()); ++m_lines; - } else { - IOutputParser::handleLine(line, StdErrFormat); + return Status::InProgress; } + QTC_CHECK(m_temporary.isNull()); + return Status::NotHandled; } Core::Id LinuxIccParser::id() @@ -128,6 +129,11 @@ Core::Id LinuxIccParser::id() return Core::Id("ProjectExplorer.OutputParser.Icc"); } +QList<IOutputParser *> LinuxIccParser::iccParserSuite() +{ + return {new LinuxIccParser, new Internal::LldParser, new LdParser}; +} + void LinuxIccParser::doFlush() { if (m_temporary.isNull()) @@ -238,7 +244,7 @@ void ProjectExplorerPlugin::testLinuxIccOutputParsers_data() void ProjectExplorerPlugin::testLinuxIccOutputParsers() { OutputParserTester testbench; - testbench.appendOutputParser(new LinuxIccParser); + testbench.setLineParsers(LinuxIccParser::iccParserSuite()); QFETCH(QString, input); QFETCH(OutputParserTester::Channel, inputChannel); QFETCH(Tasks, tasks); diff --git a/src/plugins/projectexplorer/linuxiccparser.h b/src/plugins/projectexplorer/linuxiccparser.h index 54cd09605d..03dc8ad431 100644 --- a/src/plugins/projectexplorer/linuxiccparser.h +++ b/src/plugins/projectexplorer/linuxiccparser.h @@ -41,8 +41,10 @@ public: static Core::Id id(); + static QList<IOutputParser *> iccParserSuite(); + private: - void handleLine(const QString &line, Utils::OutputFormat type) override; + Status doHandleLine(const QString &line, Utils::OutputFormat type) override; void doFlush() override; QRegExp m_firstLine; diff --git a/src/plugins/projectexplorer/lldparser.cpp b/src/plugins/projectexplorer/lldparser.cpp index 233bbfdc05..87097c685a 100644 --- a/src/plugins/projectexplorer/lldparser.cpp +++ b/src/plugins/projectexplorer/lldparser.cpp @@ -35,16 +35,15 @@ namespace ProjectExplorer { namespace Internal { -void LldParser::handleLine(const QString &line, Utils::OutputFormat type) +IOutputParser::Status LldParser::doHandleLine(const QString &line, Utils::OutputFormat type) { - if (type != Utils::StdErrFormat) { - IOutputParser::handleLine(line, type); - return; - } + if (type != Utils::StdErrFormat) + return Status::NotHandled; + const QString trimmedLine = rightTrimmed(line); if (trimmedLine.contains("error:") && trimmedLine.contains("lld")) { emit addTask(CompileTask(Task::Error, trimmedLine)); - return; + return Status::Done; } static const QStringList prefixes{">>> referenced by ", ">>> defined at ", ">>> "}; for (const QString &prefix : prefixes) { @@ -70,9 +69,9 @@ void LldParser::handleLine(const QString &line, Utils::OutputFormat type) trimmedLine.mid(filePathOffset, filePathLen).trimmed()); emit addTask(CompileTask(Task::Unknown, trimmedLine.mid(4).trimmed(), absoluteFilePath(file), lineNo)); - return; + return Status::Done; } - IOutputParser::handleLine(line, Utils::StdErrFormat); + return Status::NotHandled; } } // namespace Internal diff --git a/src/plugins/projectexplorer/lldparser.h b/src/plugins/projectexplorer/lldparser.h index a2054e1107..4f254d8a17 100644 --- a/src/plugins/projectexplorer/lldparser.h +++ b/src/plugins/projectexplorer/lldparser.h @@ -32,7 +32,7 @@ namespace Internal { class LldParser : public IOutputParser { - void handleLine(const QString &line, Utils::OutputFormat type) override; + Status doHandleLine(const QString &line, Utils::OutputFormat type) override; }; } // namespace Internal diff --git a/src/plugins/projectexplorer/makestep.cpp b/src/plugins/projectexplorer/makestep.cpp index 8a067cc1e1..cd873d5ea7 100644 --- a/src/plugins/projectexplorer/makestep.cpp +++ b/src/plugins/projectexplorer/makestep.cpp @@ -103,9 +103,7 @@ bool MakeStep::init() setIgnoreReturnValue(isClean()); setOutputParser(new GnuMakeParser()); - IOutputParser *parser = target()->kit()->createOutputParser(); - if (parser) - appendOutputParser(parser); + appendOutputParsers(target()->kit()->createOutputParsers()); outputParser()->addSearchDir(pp->effectiveWorkingDirectory()); return AbstractProcessStep::init(); diff --git a/src/plugins/projectexplorer/msvcparser.cpp b/src/plugins/projectexplorer/msvcparser.cpp index 6ef6bf792e..d368dcec0f 100644 --- a/src/plugins/projectexplorer/msvcparser.cpp +++ b/src/plugins/projectexplorer/msvcparser.cpp @@ -100,79 +100,71 @@ MsvcParser::MsvcParser() QTC_CHECK(m_additionalInfoRegExp.isValid()); } -void MsvcParser::stdOutput(const QString &line) +Core::Id MsvcParser::id() { - QRegularExpressionMatch match = m_additionalInfoRegExp.match(line); - if (line.startsWith(" ") && !match.hasMatch()) { - if (m_lastTask.isNull()) - return; + return Core::Id("ProjectExplorer.OutputParser.Msvc"); +} - m_lastTask.description.append('\n'); - m_lastTask.description.append(line.mid(8)); - // trim trailing spaces: - int i = 0; - for (i = m_lastTask.description.length() - 1; i >= 0; --i) { - if (!m_lastTask.description.at(i).isSpace()) - break; - } - m_lastTask.description.truncate(i + 1); - - if (m_lastTask.formats.isEmpty()) { - QTextLayout::FormatRange fr; - fr.start = m_lastTask.description.indexOf('\n') + 1; - fr.length = m_lastTask.description.length() - fr.start; - fr.format.setFontItalic(true); - m_lastTask.formats.append(fr); - } else { - m_lastTask.formats[0].length = m_lastTask.description.length() - m_lastTask.formats[0].start; +IOutputParser::Status MsvcParser::doHandleLine(const QString &line, OutputFormat type) +{ + if (type == OutputFormat::StdOutFormat) { + QRegularExpressionMatch match = m_additionalInfoRegExp.match(line); + if (line.startsWith(" ") && !match.hasMatch()) { + if (m_lastTask.isNull()) + return Status::NotHandled; + + m_lastTask.description.append('\n'); + m_lastTask.description.append(line.mid(8)); + // trim trailing spaces: + int i = 0; + for (i = m_lastTask.description.length() - 1; i >= 0; --i) { + if (!m_lastTask.description.at(i).isSpace()) + break; + } + m_lastTask.description.truncate(i + 1); + + if (m_lastTask.formats.isEmpty()) { + QTextLayout::FormatRange fr; + fr.start = m_lastTask.description.indexOf('\n') + 1; + fr.length = m_lastTask.description.length() - fr.start; + fr.format.setFontItalic(true); + m_lastTask.formats.append(fr); + } else { + m_lastTask.formats[0].length = m_lastTask.description.length() + - m_lastTask.formats[0].start; + } + ++m_lines; + return Status::InProgress; } - ++m_lines; - return; - } - if (processCompileLine(line)) - return; - if (handleNmakeJomMessage(line, &m_lastTask)) { - m_lines = 1; - return; - } - if (match.hasMatch()) { - QString description = match.captured(1) - + match.captured(4).trimmed(); - if (!match.captured(1).isEmpty()) - description.chop(1); // Remove trailing quote - m_lastTask = CompileTask(Task::Unknown, description, - absoluteFilePath(FilePath::fromUserInput(match.captured(2))), - match.captured(3).toInt() /* linenumber */); - m_lines = 1; - return; + if (processCompileLine(line)) + return Status::InProgress; + if (handleNmakeJomMessage(line, &m_lastTask)) { + m_lines = 1; + return Status::InProgress; + } + if (match.hasMatch()) { + QString description = match.captured(1) + + match.captured(4).trimmed(); + if (!match.captured(1).isEmpty()) + description.chop(1); // Remove trailing quote + m_lastTask = CompileTask(Task::Unknown, description, + absoluteFilePath(FilePath::fromUserInput(match.captured(2))), + match.captured(3).toInt() /* linenumber */); + m_lines = 1; + return Status::InProgress; + } + return Status::NotHandled; } - IOutputParser::handleLine(line, StdOutFormat); -} -void MsvcParser::stdError(const QString &line) -{ if (processCompileLine(line)) - return; + return Status::InProgress; // Jom outputs errors to stderr if (handleNmakeJomMessage(line, &m_lastTask)) { m_lines = 1; - return; + return Status::InProgress; } - IOutputParser::handleLine(line, StdErrFormat); -} - -Core::Id MsvcParser::id() -{ - return Core::Id("ProjectExplorer.OutputParser.Msvc"); -} - -void MsvcParser::handleLine(const QString &line, OutputFormat type) -{ - if (type == OutputFormat::StdOutFormat) - stdOutput(line); - else - stdError(line); + return Status::NotHandled; } bool MsvcParser::processCompileLine(const QString &line) @@ -220,24 +212,6 @@ ClangClParser::ClangClParser() QTC_CHECK(m_compileRegExp.isValid()); } -void ClangClParser::handleLine(const QString &line, OutputFormat type) -{ - if (type == StdOutFormat) - stdOutput(line); - else - stdError(line); -} - -void ClangClParser::stdOutput(const QString &line) -{ - if (handleNmakeJomMessage(line, &m_lastTask)) { - m_linkedLines = 1; - doFlush(); - return; - } - IOutputParser::handleLine(line, StdOutFormat); -} - // Check for a code marker '~~~~ ^ ~~~~~~~~~~~~' underlining above code. static inline bool isClangCodeMarker(const QString &trimmedLine) { @@ -246,51 +220,59 @@ static inline bool isClangCodeMarker(const QString &trimmedLine) [] (QChar c) { return c != ' ' && c != '^' && c != '~'; }); } -void ClangClParser::stdError(const QString &lineIn) +IOutputParser::Status ClangClParser::doHandleLine(const QString &line, OutputFormat type) { - const QString line = IOutputParser::rightTrimmed(lineIn); // Strip \r\n. + if (type == StdOutFormat) { + if (handleNmakeJomMessage(line, &m_lastTask)) { + m_linkedLines = 1; + doFlush(); + return Status::Done; + } + return Status::NotHandled; + } + const QString lne = IOutputParser::rightTrimmed(line); // Strip \n. - if (handleNmakeJomMessage(line, &m_lastTask)) { + if (handleNmakeJomMessage(lne, &m_lastTask)) { m_linkedLines = 1; doFlush(); - return; + return Status::Done; } // Finish a sequence of warnings/errors: "2 warnings generated." - if (!line.isEmpty() && line.at(0).isDigit() && line.endsWith("generated.")) { + if (!lne.isEmpty() && lne.at(0).isDigit() && lne.endsWith("generated.")) { doFlush(); - return; + return Status::Done; } // Start a new error message by a sequence of "In file included from " which is to be skipped. - if (line.startsWith("In file included from ")) { + if (lne.startsWith("In file included from ")) { doFlush(); - return; + return Status::Done; } - QRegularExpressionMatch match = m_compileRegExp.match(line); + QRegularExpressionMatch match = m_compileRegExp.match(lne); if (match.hasMatch()) { doFlush(); const QPair<FilePath, int> position = parseFileName(match.captured(1)); m_lastTask = CompileTask(taskType(match.captured(2)), match.captured(3).trimmed(), absoluteFilePath(position.first), position.second); m_linkedLines = 1; - return; + return Status::InProgress; } if (!m_lastTask.isNull()) { - const QString trimmed = line.trimmed(); + const QString trimmed = lne.trimmed(); if (isClangCodeMarker(trimmed)) { doFlush(); - return; + return Status::Done; } m_lastTask.description.append('\n'); m_lastTask.description.append(trimmed); ++m_linkedLines; - return; + return Status::InProgress; } - IOutputParser::handleLine(lineIn, StdErrFormat); + return Status::NotHandled; } void ClangClParser::doFlush() @@ -566,7 +548,7 @@ void ProjectExplorerPlugin::testMsvcOutputParsers_data() void ProjectExplorerPlugin::testMsvcOutputParsers() { OutputParserTester testbench; - testbench.appendOutputParser(new MsvcParser); + testbench.addLineParser(new MsvcParser); QFETCH(QString, input); QFETCH(OutputParserTester::Channel, inputChannel); QFETCH(Tasks, tasks); @@ -645,7 +627,7 @@ void ProjectExplorerPlugin::testClangClOutputParsers_data() void ProjectExplorerPlugin::testClangClOutputParsers() { OutputParserTester testbench; - testbench.appendOutputParser(new ClangClParser); + testbench.addLineParser(new ClangClParser); QFETCH(QString, input); QFETCH(OutputParserTester::Channel, inputChannel); QFETCH(Tasks, tasks); diff --git a/src/plugins/projectexplorer/msvcparser.h b/src/plugins/projectexplorer/msvcparser.h index bff487f9fb..fbeb6f1ae6 100644 --- a/src/plugins/projectexplorer/msvcparser.h +++ b/src/plugins/projectexplorer/msvcparser.h @@ -43,11 +43,9 @@ public: static Core::Id id(); private: - void handleLine(const QString &line, Utils::OutputFormat type) override; + Status doHandleLine(const QString &line, Utils::OutputFormat type) override; void doFlush() override; - void stdOutput(const QString &line); - void stdError(const QString &line); bool processCompileLine(const QString &line); QRegularExpression m_compileRegExp; @@ -65,12 +63,9 @@ public: ClangClParser(); private: - void handleLine(const QString &line, Utils::OutputFormat type) override; + Status doHandleLine(const QString &line, Utils::OutputFormat type) override; void doFlush() override; - void stdOutput(const QString &line); - void stdError(const QString &line); - const QRegularExpression m_compileRegExp; Task m_lastTask; int m_linkedLines = 0; diff --git a/src/plugins/projectexplorer/msvctoolchain.cpp b/src/plugins/projectexplorer/msvctoolchain.cpp index 53da86420e..9969d4f035 100644 --- a/src/plugins/projectexplorer/msvctoolchain.cpp +++ b/src/plugins/projectexplorer/msvctoolchain.cpp @@ -1169,9 +1169,9 @@ void MsvcToolChain::rescanForCompiler() }); } -IOutputParser *MsvcToolChain::outputParser() const +QList<IOutputParser *> MsvcToolChain::outputParsers() const { - return new MsvcParser; + return {new MsvcParser}; } void MsvcToolChain::setupVarsBat(const Abi &abi, const QString &varsBat, const QString &varsBatArg) @@ -1656,9 +1656,9 @@ QStringList ClangClToolChain::suggestedMkspecList() const return {mkspec, "win32-clang-msvc"}; } -IOutputParser *ClangClToolChain::outputParser() const +QList<IOutputParser *> ClangClToolChain::outputParsers() const { - return new ClangClParser; + return {new ClangClParser}; } static inline QString llvmDirKey() diff --git a/src/plugins/projectexplorer/msvctoolchain.h b/src/plugins/projectexplorer/msvctoolchain.h index 7eba087a19..e6be1608a1 100644 --- a/src/plugins/projectexplorer/msvctoolchain.h +++ b/src/plugins/projectexplorer/msvctoolchain.h @@ -89,7 +89,7 @@ public: Utils::FilePath makeCommand(const Utils::Environment &environment) const override; Utils::FilePath compilerCommand() const override; - IOutputParser *outputParser() const override; + QList<IOutputParser *> outputParsers() const override; QString varsBatArg() const { return m_varsBatArg; } QString varsBat() const { return m_vcvarsBat; } @@ -174,7 +174,7 @@ public: QStringList suggestedMkspecList() const override; void addToEnvironment(Utils::Environment &env) const override; Utils::FilePath compilerCommand() const override; - IOutputParser *outputParser() const override; + QList<IOutputParser *> outputParsers() const override; QVariantMap toMap() const override; bool fromMap(const QVariantMap &data) override; std::unique_ptr<ToolChainConfigWidget> createConfigurationWidget() override; diff --git a/src/plugins/projectexplorer/osparser.cpp b/src/plugins/projectexplorer/osparser.cpp index c543aac393..3c1a097446 100644 --- a/src/plugins/projectexplorer/osparser.cpp +++ b/src/plugins/projectexplorer/osparser.cpp @@ -36,39 +36,30 @@ OsParser::OsParser() setObjectName(QLatin1String("OsParser")); } -void OsParser::handleLine(const QString &line, Utils::OutputFormat type) -{ - if (type == Utils::StdOutFormat) - stdOutput(line); - else - stdError(line); -} - -void OsParser::stdError(const QString &line) +IOutputParser::Status OsParser::doHandleLine(const QString &line, Utils::OutputFormat type) { + if (type == Utils::StdOutFormat) { + if (Utils::HostOsInfo::isWindowsHost()) { + const QString trimmed = line.trimmed(); + if (trimmed == QLatin1String("The process cannot access the file because it is " + "being used by another process.")) { + emit addTask(CompileTask(Task::Error, tr( + "The process cannot access the file because it is being used " + "by another process.\n" + "Please close all running instances of your application before " + "starting a build."))); + m_hasFatalError = true; + return Status::Done; + } + } + return Status::NotHandled; + } if (Utils::HostOsInfo::isLinuxHost()) { const QString trimmed = line.trimmed(); - if (trimmed.contains(QLatin1String(": error while loading shared libraries:"))) + if (trimmed.contains(QLatin1String(": error while loading shared libraries:"))) { emit addTask(CompileTask(Task::Error, trimmed)); - } - IOutputParser::handleLine(line, Utils::StdErrFormat); -} - -void OsParser::stdOutput(const QString &line) -{ - if (Utils::HostOsInfo::isWindowsHost()) { - const QString trimmed = line.trimmed(); - if (trimmed == QLatin1String("The process cannot access the file because it is being used by another process.")) { - emit addTask(CompileTask(Task::Error, tr( - "The process cannot access the file because it is being used by another process.\n" - "Please close all running instances of your application before starting a build."))); - m_hasFatalError = true; + return Status::Done; } } - IOutputParser::handleLine(line, Utils::StdOutFormat); -} - -bool OsParser::hasFatalErrors() const -{ - return m_hasFatalError || IOutputParser::hasFatalErrors(); + return Status::NotHandled; } diff --git a/src/plugins/projectexplorer/osparser.h b/src/plugins/projectexplorer/osparser.h index fa56268c65..fd0e9dea20 100644 --- a/src/plugins/projectexplorer/osparser.h +++ b/src/plugins/projectexplorer/osparser.h @@ -41,11 +41,8 @@ public: OsParser(); private: - void handleLine(const QString &line, Utils::OutputFormat type) override; - bool hasFatalErrors() const override; - - void stdError(const QString &line); - void stdOutput(const QString &line); + Status doHandleLine(const QString &line, Utils::OutputFormat type) override; + bool hasFatalErrors() const override { return m_hasFatalError; } bool m_hasFatalError = false; }; diff --git a/src/plugins/projectexplorer/outputparser_test.cpp b/src/plugins/projectexplorer/outputparser_test.cpp index 8a2bebf09d..5ae0c46aed 100644 --- a/src/plugins/projectexplorer/outputparser_test.cpp +++ b/src/plugins/projectexplorer/outputparser_test.cpp @@ -53,12 +53,11 @@ void OutputParserTester::testParsing(const QString &lines, const QString &childStdErrLines, const QString &outputLines) { - if (!m_terminator) { - m_terminator = new TestTerminator(this); - appendOutputParser(m_terminator); - } + const auto terminator = new TestTerminator(this); + if (!lineParsers().isEmpty()) + terminator->setRedirectionDetector(lineParsers().last()); + addLineParser(terminator); reset(); - Q_ASSERT(childParser()); if (inputChannel == STDOUT) handleStdout(lines + '\n'); @@ -68,7 +67,7 @@ void OutputParserTester::testParsing(const QString &lines, // delete the parser(s) to test emit aboutToDeleteParser(); - setChildParser(nullptr); + setLineParsers({}); QCOMPARE(m_receivedOutput, outputLines); QCOMPARE(m_receivedStdErrChildLine, childStdErrLines); @@ -103,13 +102,14 @@ TestTerminator::TestTerminator(OutputParserTester *t) : m_tester(t) { } -void TestTerminator::handleLine(const QString &line, Utils::OutputFormat type) +IOutputParser::Status TestTerminator::doHandleLine(const QString &line, Utils::OutputFormat type) { - QVERIFY(line.endsWith('\n')); + QTC_CHECK(line.endsWith('\n')); if (type == Utils::StdOutFormat) m_tester->m_receivedStdOutChildLine.append(line); else m_tester->m_receivedStdErrChildLine.append(line); + return Status::Done; } } // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/outputparser_test.h b/src/plugins/projectexplorer/outputparser_test.h index 3d749f422a..1a1fff1e65 100644 --- a/src/plugins/projectexplorer/outputparser_test.h +++ b/src/plugins/projectexplorer/outputparser_test.h @@ -69,7 +69,6 @@ private: QString m_receivedStdOutChildLine; Tasks m_receivedTasks; QString m_receivedOutput; - TestTerminator *m_terminator = nullptr; friend class TestTerminator; }; @@ -82,7 +81,7 @@ public: TestTerminator(OutputParserTester *t); private: - void handleLine(const QString &line, Utils::OutputFormat type) override; + Status doHandleLine(const QString &line, Utils::OutputFormat type) override; OutputParserTester *m_tester = nullptr; }; diff --git a/src/plugins/projectexplorer/parseissuesdialog.cpp b/src/plugins/projectexplorer/parseissuesdialog.cpp index e61b4adc1c..7696950ddb 100644 --- a/src/plugins/projectexplorer/parseissuesdialog.cpp +++ b/src/plugins/projectexplorer/parseissuesdialog.cpp @@ -151,12 +151,14 @@ static void parse(QFutureInterface<void> &future, const QString &output, void ParseIssuesDialog::accept() { - std::unique_ptr<IOutputParser> parser(d->kitChooser.currentKit()->createOutputParser()); - if (!parser) { + const QList<IOutputParser *> lineParsers = d->kitChooser.currentKit()->createOutputParsers(); + if (lineParsers.isEmpty()) { QMessageBox::critical(this, tr("Cannot Parse"), tr("Cannot parse: The chosen kit does " "not provide an output parser.")); return; } + std::unique_ptr<IOutputParser> parser(new IOutputParser); + parser->setLineParsers(lineParsers); if (d->clearTasksCheckBox.isChecked()) TaskHub::clearTasks(); connect(parser.get(), &IOutputParser::addTask, [](const Task &t) { TaskHub::addTask(t); }); diff --git a/src/plugins/projectexplorer/processstep.cpp b/src/plugins/projectexplorer/processstep.cpp index 02501150b5..ea8de1cb3b 100644 --- a/src/plugins/projectexplorer/processstep.cpp +++ b/src/plugins/projectexplorer/processstep.cpp @@ -100,7 +100,7 @@ ProcessStep::ProcessStep(BuildStepList *bsl, Core::Id id) bool ProcessStep::init() { setupProcessParameters(processParameters()); - setOutputParser(target()->kit()->createOutputParser()); + appendOutputParsers(target()->kit()->createOutputParsers()); return AbstractProcessStep::init(); } diff --git a/src/plugins/projectexplorer/toolchain.h b/src/plugins/projectexplorer/toolchain.h index d1a90a2104..9e8b6c087c 100644 --- a/src/plugins/projectexplorer/toolchain.h +++ b/src/plugins/projectexplorer/toolchain.h @@ -150,7 +150,7 @@ public: Core::Id language() const; virtual Utils::FilePath compilerCommand() const = 0; - virtual IOutputParser *outputParser() const = 0; + virtual QList<IOutputParser *> outputParsers() const = 0; virtual bool operator ==(const ToolChain &) const; diff --git a/src/plugins/projectexplorer/toolchainsettingsaccessor.cpp b/src/plugins/projectexplorer/toolchainsettingsaccessor.cpp index 78fafb5ea7..6e15b14191 100644 --- a/src/plugins/projectexplorer/toolchainsettingsaccessor.cpp +++ b/src/plugins/projectexplorer/toolchainsettingsaccessor.cpp @@ -324,7 +324,7 @@ public: void addToEnvironment(Environment &env) const override { Q_UNUSED(env) } FilePath makeCommand(const Environment &) const override { return FilePath::fromString("make"); } FilePath compilerCommand() const override { return Utils::FilePath::fromString("/tmp/test/gcc"); } - IOutputParser *outputParser() const override { return nullptr; } + QList<IOutputParser *> outputParsers() const override { return {}; } std::unique_ptr<ToolChainConfigWidget> createConfigurationWidget() override { return nullptr; } bool operator ==(const ToolChain &other) const override { if (!ToolChain::operator==(other)) diff --git a/src/plugins/projectexplorer/xcodebuildparser.cpp b/src/plugins/projectexplorer/xcodebuildparser.cpp index ea5d2c9b04..12dcac6f5a 100644 --- a/src/plugins/projectexplorer/xcodebuildparser.cpp +++ b/src/plugins/projectexplorer/xcodebuildparser.cpp @@ -52,61 +52,47 @@ XcodebuildParser::XcodebuildParser() QTC_CHECK(m_buildRe.isValid()); } -void XcodebuildParser::handleLine(const QString &line, OutputFormat type) -{ - if (type == StdOutFormat) - stdOutput(line); - else - stdError(line); -} - -bool XcodebuildParser::hasFatalErrors() const -{ - return (m_fatalErrorCount > 0) || IOutputParser::hasFatalErrors(); -} - -void XcodebuildParser::stdOutput(const QString &line) +IOutputParser::Status XcodebuildParser::doHandleLine(const QString &line, OutputFormat type) { const QString lne = rightTrimmed(line); - if (m_buildRe.indexIn(lne) > -1) { - m_xcodeBuildParserState = InXcodebuild; - m_lastTarget = m_buildRe.cap(2); - m_lastProject = m_buildRe.cap(3); - return; - } - if (m_xcodeBuildParserState == InXcodebuild || m_xcodeBuildParserState == UnknownXcodebuildState) { - if (m_successRe.indexIn(lne) > -1) { - m_xcodeBuildParserState = OutsideXcodebuild; - return; + if (type == StdOutFormat) { + if (m_buildRe.indexIn(lne) > -1) { + m_xcodeBuildParserState = InXcodebuild; + m_lastTarget = m_buildRe.cap(2); + m_lastProject = m_buildRe.cap(3); + return Status::Done; } - if (lne.endsWith(QLatin1String(signatureChangeEndsWithPattern))) { - CompileTask task(Task::Warning, - tr("Replacing signature"), - absoluteFilePath(FilePath::fromString( - lne.left(lne.size() - QLatin1String(signatureChangeEndsWithPattern).size())))); - emit addTask(task, 1); - return; + if (m_xcodeBuildParserState == InXcodebuild + || m_xcodeBuildParserState == UnknownXcodebuildState) { + if (m_successRe.indexIn(lne) > -1) { + m_xcodeBuildParserState = OutsideXcodebuild; + return Status::Done; + } + if (lne.endsWith(QLatin1String(signatureChangeEndsWithPattern))) { + CompileTask task(Task::Warning, + tr("Replacing signature"), + absoluteFilePath(FilePath::fromString( + lne.left(lne.size() - QLatin1String(signatureChangeEndsWithPattern).size())))); + emit addTask(task, 1); + return Status::Done; + } } - IOutputParser::handleLine(line, StdErrFormat); // ?? - } else { - IOutputParser::handleLine(line, StdOutFormat); + return Status::NotHandled; } -} - -void XcodebuildParser::stdError(const QString &line) -{ - const QString lne = rightTrimmed(line); if (m_failureRe.indexIn(lne) > -1) { ++m_fatalErrorCount; m_xcodeBuildParserState = UnknownXcodebuildState; // unfortunately the m_lastTarget, m_lastProject might not be in sync emit addTask(CompileTask(Task::Error, tr("Xcodebuild failed."))); - return; - } - if (m_xcodeBuildParserState == OutsideXcodebuild) { // also forward if UnknownXcodebuildState ? - IOutputParser::handleLine(line, StdErrFormat); - return; } + if (m_xcodeBuildParserState == OutsideXcodebuild) + return Status::NotHandled; + return Status::Done; +} + +bool XcodebuildParser::hasDetectedRedirection() const +{ + return m_xcodeBuildParserState != OutsideXcodebuild; } } // namespace ProjectExplorer @@ -268,7 +254,7 @@ void ProjectExplorerPlugin::testXcodebuildParserParsing() connect(&testbench, &OutputParserTester::aboutToDeleteParser, tester, &XcodebuildParserTester::onAboutToDeleteParser); - testbench.appendOutputParser(childParser); + testbench.addLineParser(childParser); QFETCH(ProjectExplorer::XcodebuildParser::XcodebuildStatus, initialStatus); QFETCH(QString, input); QFETCH(OutputParserTester::Channel, inputChannel); diff --git a/src/plugins/projectexplorer/xcodebuildparser.h b/src/plugins/projectexplorer/xcodebuildparser.h index 59ee84152b..c2fce0e4a7 100644 --- a/src/plugins/projectexplorer/xcodebuildparser.h +++ b/src/plugins/projectexplorer/xcodebuildparser.h @@ -47,11 +47,9 @@ public: XcodebuildParser(); private: - void handleLine(const QString &line, Utils::OutputFormat type) override; - bool hasFatalErrors() const override; - - void stdOutput(const QString &line); - void stdError(const QString &line); + Status doHandleLine(const QString &line, Utils::OutputFormat type) override; + bool hasDetectedRedirection() const override; + bool hasFatalErrors() const override { return m_fatalErrorCount > 0; } int m_fatalErrorCount = 0; QRegExp m_failureRe; |