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 | |
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')
77 files changed, 777 insertions, 902 deletions
diff --git a/src/plugins/android/androidpackageinstallationstep.cpp b/src/plugins/android/androidpackageinstallationstep.cpp index 3c61a32915..9fd25e9296 100644 --- a/src/plugins/android/androidpackageinstallationstep.cpp +++ b/src/plugins/android/androidpackageinstallationstep.cpp @@ -112,9 +112,7 @@ bool AndroidPackageInstallationStep::init() pp->setCommandLine(cmd); setOutputParser(new GnuMakeParser()); - IOutputParser *parser = target()->kit()->createOutputParser(); - if (parser) - appendOutputParser(parser); + appendOutputParsers(target()->kit()->createOutputParsers()); outputParser()->addSearchDir(pp->effectiveWorkingDirectory()); m_androidDirsToClean.clear(); diff --git a/src/plugins/android/javaparser.cpp b/src/plugins/android/javaparser.cpp index 1f45263675..8be5f08b82 100644 --- a/src/plugins/android/javaparser.cpp +++ b/src/plugins/android/javaparser.cpp @@ -51,39 +51,33 @@ void JavaParser::setSourceDirectory(const Utils::FilePath &sourceDirectory) m_sourceDirectory = sourceDirectory; } -void JavaParser::handleLine(const QString &line, Utils::OutputFormat type) +IOutputParser::Status JavaParser::doHandleLine(const QString &line, Utils::OutputFormat type) { - parse(line); - IOutputParser::handleLine(line, type); -} - -void JavaParser::parse(const QString &line) -{ - if (m_javaRegExp.indexIn(line) > -1) { - bool ok; - int lineno = m_javaRegExp.cap(3).toInt(&ok); - if (!ok) - lineno = -1; - Utils::FilePath file = Utils::FilePath::fromUserInput(m_javaRegExp.cap(2)); - if (file.isChildOf(m_buildDirectory)) { - Utils::FilePath relativePath = file.relativeChildPath(m_buildDirectory); - file = m_sourceDirectory.pathAppended(relativePath.toString()); - } + Q_UNUSED(type); + if (m_javaRegExp.indexIn(line) == -1) + return Status::NotHandled; - if (file.toFileInfo().isRelative()) { - for (int i = 0; i < m_fileList.size(); i++) - if (m_fileList[i].endsWith(file.toString())) { - file = Utils::FilePath::fromString(m_fileList[i]); - break; - } - } - - CompileTask task(Task::Error, - m_javaRegExp.cap(4).trimmed(), - absoluteFilePath(file), - lineno); - emit addTask(task, 1); - return; + bool ok; + int lineno = m_javaRegExp.cap(3).toInt(&ok); + if (!ok) + lineno = -1; + Utils::FilePath file = Utils::FilePath::fromUserInput(m_javaRegExp.cap(2)); + if (file.isChildOf(m_buildDirectory)) { + Utils::FilePath relativePath = file.relativeChildPath(m_buildDirectory); + file = m_sourceDirectory.pathAppended(relativePath.toString()); + } + if (file.toFileInfo().isRelative()) { + for (int i = 0; i < m_fileList.size(); i++) + if (m_fileList[i].endsWith(file.toString())) { + file = Utils::FilePath::fromString(m_fileList[i]); + break; + } } + CompileTask task(Task::Error, + m_javaRegExp.cap(4).trimmed(), + absoluteFilePath(file), + lineno); + emit addTask(task, 1); + return Status::Done; } diff --git a/src/plugins/android/javaparser.h b/src/plugins/android/javaparser.h index ad4ebdc8c5..160006636c 100644 --- a/src/plugins/android/javaparser.h +++ b/src/plugins/android/javaparser.h @@ -45,8 +45,7 @@ public: void setSourceDirectory(const Utils::FilePath &sourceDirectory); private: - void handleLine(const QString &line, Utils::OutputFormat type) override; - void parse(const QString &line); + Status doHandleLine(const QString &line, Utils::OutputFormat type) override; QRegExp m_javaRegExp; QStringList m_fileList; diff --git a/src/plugins/baremetal/iarewparser.cpp b/src/plugins/baremetal/iarewparser.cpp index 39a8a112f1..f3e357544d 100644 --- a/src/plugins/baremetal/iarewparser.cpp +++ b/src/plugins/baremetal/iarewparser.cpp @@ -190,67 +190,56 @@ bool IarParser::parseErrorMessage1(const QString &lne) return true; } -void IarParser::handleLine(const QString &line, OutputFormat type) +IOutputParser::Status IarParser::doHandleLine(const QString &line, OutputFormat type) { - if (type == StdOutFormat) - stdOutput(line); - else - stdError(line); -} - -void IarParser::stdError(const QString &line) -{ - IOutputParser::handleLine(line, StdErrFormat); - const QString lne = rightTrimmed(line); + if (type == StdOutFormat) { + // The call sequence has the meaning! + const bool leastOneParsed = parseErrorInCommandLineMessage(lne) + || parseErrorMessage1(lne); + if (!leastOneParsed) { + doFlush(); + return Status::NotHandled; + } + return Status::InProgress; + } if (parseErrorOrFatalErrorDetailsMessage1(lne)) - return; + return Status::InProgress; if (parseErrorOrFatalErrorDetailsMessage2(lne)) - return; + return Status::InProgress; if (parseWarningOrErrorOrFatalErrorDetailsMessage1(lne)) - return; + return Status::InProgress; - if (lne.isEmpty()) { - // - } else if (!lne.startsWith(' ')) { - return; - } else if (m_expectFilePath) { + if (m_expectFilePath) { if (lne.endsWith(']')) { const QString lastPart = lne.left(lne.size() - 1); m_filePathParts.push_back(lastPart); + doFlush(); + return Status::Done; } else { m_filePathParts.push_back(lne); - return; + return Status::InProgress; } - } else if (m_expectSnippet) { + } + if (m_expectSnippet && lne.startsWith(' ')) { if (!lne.endsWith("Fatal error detected, aborting.")) { m_snippets.push_back(lne); - return; + return Status::InProgress; } } else if (m_expectDescription) { if (!lne.startsWith(" ")) { m_descriptionParts.push_back(lne.trimmed()); - return; + return Status::InProgress; } } - doFlush(); -} - -void IarParser::stdOutput(const QString &line) -{ - IOutputParser::handleLine(line, StdOutFormat); - - const QString lne = rightTrimmed(line); - - // The call sequence has the meaning! - const bool leastOneParsed = parseErrorInCommandLineMessage(lne) - || parseErrorMessage1(lne); - if (!leastOneParsed) - return; + if (!m_lastTask.isNull()) { + doFlush(); + return Status::Done; + } - doFlush(); + return Status::NotHandled; } void IarParser::doFlush() @@ -308,7 +297,7 @@ void BareMetalPlugin::testIarOutputParsers_data() QTest::newRow("Error in command line") << QString::fromLatin1("Error in command line: Some error") << OutputParserTester::STDOUT - << QString::fromLatin1("Error in command line: Some error\n") + << QString() << QString() << (Tasks() << CompileTask(Task::Error, "Error in command line: Some error")) @@ -317,7 +306,7 @@ void BareMetalPlugin::testIarOutputParsers_data() QTest::newRow("Linker error") << QString::fromLatin1("Error[e46]: Some error") << OutputParserTester::STDOUT - << QString::fromLatin1("Error[e46]: Some error\n") + << QString() << QString() << (Tasks() << CompileTask(Task::Error, "[e46]: Some error")) @@ -329,8 +318,7 @@ void BareMetalPlugin::testIarOutputParsers_data() " Some warning \"foo\" bar") << OutputParserTester::STDERR << QString() - << QString::fromLatin1("\"c:\\foo\\main.c\",63 Warning[Pe223]:\n" - " Some warning \"foo\" bar\n") + << QString() << (Tasks() << CompileTask(Task::Warning, "[Pe223]: Some warning \"foo\" bar", Utils::FilePath::fromUserInput("c:\\foo\\main.c"), @@ -344,10 +332,7 @@ void BareMetalPlugin::testIarOutputParsers_data() " Some warning") << OutputParserTester::STDERR << QString() - << QString::fromLatin1(" some_detail;\n" - " ^\n" - "\"c:\\foo\\main.c\",63 Warning[Pe223]:\n" - " Some warning\n") + << QString() << (Tasks() << CompileTask(Task::Warning, "[Pe223]: Some warning\n" " some_detail;\n" @@ -362,9 +347,7 @@ void BareMetalPlugin::testIarOutputParsers_data() " , split") << OutputParserTester::STDERR << QString() - << QString::fromLatin1("\"c:\\foo\\main.c\",63 Warning[Pe223]:\n" - " Some warning\n" - " , split\n") + << QString() << (Tasks() << CompileTask(Task::Warning, "[Pe223]: Some warning, split", FilePath::fromUserInput("c:\\foo\\main.c"), @@ -376,8 +359,7 @@ void BareMetalPlugin::testIarOutputParsers_data() " Some error") << OutputParserTester::STDERR << QString() - << QString::fromLatin1("\"c:\\foo\\main.c\",63 Error[Pe223]:\n" - " Some error\n") + << QString() << (Tasks() << CompileTask(Task::Error, "[Pe223]: Some error", FilePath::fromUserInput("c:\\foo\\main.c"), @@ -391,10 +373,7 @@ void BareMetalPlugin::testIarOutputParsers_data() " Some error") << OutputParserTester::STDERR << QString() - << QString::fromLatin1(" some_detail;\n" - " ^\n" - "\"c:\\foo\\main.c\",63 Error[Pe223]:\n" - " Some error\n") + << QString() << (Tasks() << CompileTask(Task::Error, "[Pe223]: Some error\n" " some_detail;\n" @@ -409,9 +388,7 @@ void BareMetalPlugin::testIarOutputParsers_data() " , split") << OutputParserTester::STDERR << QString() - << QString::fromLatin1("\"c:\\foo\\main.c\",63 Error[Pe223]:\n" - " Some error\n" - " , split\n") + << QString() << (Tasks() << CompileTask(Task::Error, "[Pe223]: Some error, split", FilePath::fromUserInput("c:\\foo\\main.c"), @@ -425,10 +402,7 @@ void BareMetalPlugin::testIarOutputParsers_data() "]") << OutputParserTester::STDERR << QString() - << QString::fromLatin1("Error[Li005]: Some error \"foo\" [referenced from c:\\fo\n" - " o\\bar\\mai\n" - " n.c.o\n" - "]\n") + << QString() << (Tasks() << CompileTask(Task::Error, "[Li005]: Some error \"foo\"", FilePath::fromUserInput("c:\\foo\\bar\\main.c.o"))) @@ -441,10 +415,7 @@ void BareMetalPlugin::testIarOutputParsers_data() "Fatal error detected, aborting.") << OutputParserTester::STDERR << QString() - << QString::fromLatin1("Fatal error[Su011]: Some error:\n" - " c:\\foo.c\n" - " c:\\bar.c\n" - "Fatal error detected, aborting.\n") + << QString() << (Tasks() << CompileTask(Task::Error, "[Su011]: Some error:\n" " c:\\foo.c\n" @@ -455,7 +426,7 @@ void BareMetalPlugin::testIarOutputParsers_data() << QString::fromLatin1("At end of source Error[Pe040]: Some error \";\"") << OutputParserTester::STDERR << QString() - << QString::fromLatin1("At end of source Error[Pe040]: Some error \";\"\n") + << QString() << (Tasks() << CompileTask(Task::Error, "[Pe040]: Some error \";\"")) << QString(); @@ -464,7 +435,7 @@ void BareMetalPlugin::testIarOutputParsers_data() void BareMetalPlugin::testIarOutputParsers() { OutputParserTester testbench; - testbench.appendOutputParser(new IarParser); + testbench.addLineParser(new IarParser); QFETCH(QString, input); QFETCH(OutputParserTester::Channel, inputChannel); QFETCH(Tasks, tasks); diff --git a/src/plugins/baremetal/iarewparser.h b/src/plugins/baremetal/iarewparser.h index 0b066a5f33..546bb400fc 100644 --- a/src/plugins/baremetal/iarewparser.h +++ b/src/plugins/baremetal/iarewparser.h @@ -52,9 +52,7 @@ private: bool parseErrorInCommandLineMessage(const QString &lne); bool parseErrorMessage1(const QString &lne); - void handleLine(const QString &line, Utils::OutputFormat type) final; - void stdError(const QString &line); - void stdOutput(const QString &line); + Status doHandleLine(const QString &line, Utils::OutputFormat type) final; void doFlush() final; ProjectExplorer::Task m_lastTask; diff --git a/src/plugins/baremetal/iarewtoolchain.cpp b/src/plugins/baremetal/iarewtoolchain.cpp index a67ba48eda..34afc316ef 100644 --- a/src/plugins/baremetal/iarewtoolchain.cpp +++ b/src/plugins/baremetal/iarewtoolchain.cpp @@ -354,9 +354,9 @@ void IarToolChain::addToEnvironment(Environment &env) const } } -IOutputParser *IarToolChain::outputParser() const +QList<IOutputParser *> IarToolChain::outputParsers() const { - return new IarParser; + return {new IarParser()}; } QVariantMap IarToolChain::toMap() const diff --git a/src/plugins/baremetal/iarewtoolchain.h b/src/plugins/baremetal/iarewtoolchain.h index 59a67e02ef..932e8555a7 100644 --- a/src/plugins/baremetal/iarewtoolchain.h +++ b/src/plugins/baremetal/iarewtoolchain.h @@ -68,7 +68,7 @@ public: const Utils::FilePath &, const Utils::Environment &env) const final; void addToEnvironment(Utils::Environment &env) const final; - ProjectExplorer::IOutputParser *outputParser() const final; + QList<ProjectExplorer::IOutputParser *> outputParsers() const final; QVariantMap toMap() const final; bool fromMap(const QVariantMap &data) final; diff --git a/src/plugins/baremetal/keilparser.cpp b/src/plugins/baremetal/keilparser.cpp index acd6491f6e..f02ff16bde 100644 --- a/src/plugins/baremetal/keilparser.cpp +++ b/src/plugins/baremetal/keilparser.cpp @@ -190,34 +190,6 @@ bool KeilParser::parseMcs51FatalErrorMessage2(const QString &lne) return true; } -void KeilParser::handleLine(const QString &line, OutputFormat type) -{ - if (type == StdOutFormat) - stdOutput(line); - else - stdError(line); -} - -void KeilParser::stdError(const QString &line) -{ - IOutputParser::handleLine(line, StdErrFormat); - - const QString lne = rightTrimmed(line); - - // Check for ARM compiler specific patterns. - if (parseArmWarningOrErrorDetailsMessage(lne)) - return; - if (parseArmErrorOrFatalErorrMessage(lne)) - return; - - if (lne.startsWith(' ')) { - m_snippets.push_back(lne); - return; - } - - doFlush(); -} - static bool hasDetailsEntry(const QString &trimmedLine) { const QRegularExpression re("^([0-9A-F]{4})"); @@ -234,46 +206,59 @@ static bool hasDetailsPointer(const QString &trimmedLine) return trimmedLine.contains('_'); } -void KeilParser::stdOutput(const QString &line) +IOutputParser::Status KeilParser::doHandleLine(const QString &line, OutputFormat type) { - IOutputParser::handleLine(line, StdOutFormat); - QString lne = rightTrimmed(line); - - // Check for MSC51 compiler specific patterns. - const bool parsed = parseMcs51WarningOrErrorDetailsMessage1(lne) - || parseMcs51WarningOrErrorDetailsMessage2(lne); - if (!parsed) { + if (type == StdOutFormat) { + // Check for MSC51 compiler specific patterns. + const bool parsed = parseMcs51WarningOrErrorDetailsMessage1(lne) + || parseMcs51WarningOrErrorDetailsMessage2(lne); + if (parsed) + return Status::InProgress; if (parseMcs51WarningOrFatalErrorMessage(lne)) - return; + return Status::InProgress; if (parseMcs51FatalErrorMessage2(lne)) - return; + return Status::InProgress; + + if (m_lastTask.isNull()) { + // Check for details, which are comes on a previous + // lines, before the message. + + // This code handles the details in a form like: + // 0000 24 ljmp usb_stub_isr ; (00) Setup data available. + // *** _____________________________________^ + // 003C 54 ljmp usb_stub_isr ; (3C) EP8 in/out. + // *** _____________________________________^ + if (hasDetailsEntry(lne) || hasDetailsPointer(lne)) { + lne.replace(0, 4, " "); + m_snippets.push_back(lne); + return Status::InProgress; + } + } else { + // Check for details, which are comes on a next + // lines, after the message. + if (lne.startsWith(' ')) { + m_snippets.push_back(lne); + return Status::InProgress; + } + } + doFlush(); + return Status::NotHandled; } - if (m_lastTask.isNull()) { - // Check for details, which are comes on a previous - // lines, before the message. - - // This code handles the details in a form like: - // 0000 24 ljmp usb_stub_isr ; (00) Setup data available. - // *** _____________________________________^ - // 003C 54 ljmp usb_stub_isr ; (3C) EP8 in/out. - // *** _____________________________________^ - if (hasDetailsEntry(lne) || hasDetailsPointer(lne)) { - lne.replace(0, 4, " "); - m_snippets.push_back(lne); - return; - } - } else { - // Check for details, which are comes on a next - // lines, after the message. - if (lne.startsWith(' ')) { - m_snippets.push_back(lne); - return; - } + // Check for ARM compiler specific patterns. + if (parseArmWarningOrErrorDetailsMessage(lne)) + return Status::InProgress; + if (parseArmErrorOrFatalErorrMessage(lne)) + return Status::InProgress; + + if (lne.startsWith(' ')) { + m_snippets.push_back(lne); + return Status::InProgress; } doFlush(); + return Status::NotHandled; } void KeilParser::doFlush() @@ -328,7 +313,7 @@ void BareMetalPlugin::testKeilOutputParsers_data() << QString::fromLatin1("\"c:\\foo\\main.c\", line 63: Warning: #1234: Some warning") << OutputParserTester::STDERR << QString() - << QString::fromLatin1("\"c:\\foo\\main.c\", line 63: Warning: #1234: Some warning\n") + << QString() << (Tasks() << CompileTask(Task::Warning, "#1234: Some warning", FilePath::fromUserInput("c:\\foo\\main.c"), @@ -341,9 +326,7 @@ void BareMetalPlugin::testKeilOutputParsers_data() " ^") << OutputParserTester::STDERR << QString() - << QString::fromLatin1("\"c:\\foo\\main.c\", line 63: Warning: #1234: Some warning\n" - " int f;\n" - " ^\n") + << QString() << (Tasks() << CompileTask(Task::Warning, "#1234: Some warning\n" " int f;\n" @@ -356,7 +339,7 @@ void BareMetalPlugin::testKeilOutputParsers_data() << QString::fromLatin1("\"c:\\foo\\main.c\", line 63: Error: #1234: Some error") << OutputParserTester::STDERR << QString() - << QString::fromLatin1("\"c:\\foo\\main.c\", line 63: Error: #1234: Some error\n") + << QString() << (Tasks() << CompileTask(Task::Error, "#1234: Some error", FilePath::fromUserInput("c:\\foo\\main.c"), @@ -367,7 +350,7 @@ void BareMetalPlugin::testKeilOutputParsers_data() << QString::fromLatin1("\"flash.sct\", line 51 (column 20): Error: L1234: Some error") << OutputParserTester::STDERR << QString() - << QString::fromLatin1("\"flash.sct\", line 51 (column 20): Error: L1234: Some error\n") + << QString() << (Tasks() << CompileTask(Task::Error, "L1234: Some error", FilePath::fromUserInput("flash.sct"), @@ -380,9 +363,7 @@ void BareMetalPlugin::testKeilOutputParsers_data() " ^") << OutputParserTester::STDERR << QString() - << QString::fromLatin1("\"c:\\foo\\main.c\", line 63: Error: #1234: Some error\n" - " int f;\n" - " ^\n") + << QString() << (Tasks() << CompileTask(Task::Error, "#1234: Some error\n" " int f;\n" @@ -395,7 +376,7 @@ void BareMetalPlugin::testKeilOutputParsers_data() << QString::fromLatin1("\"c:\\foo\\main.c\", line 71: Error: At end of source: #40: Some error") << OutputParserTester::STDERR << QString() - << QString::fromLatin1("\"c:\\foo\\main.c\", line 71: Error: At end of source: #40: Some error\n") + << QString() << (Tasks() << CompileTask(Task::Error, "#40: Some error", FilePath::fromUserInput("c:\\foo\\main.c"), @@ -406,7 +387,7 @@ void BareMetalPlugin::testKeilOutputParsers_data() << QString::fromLatin1("Error: L6226E: Some error.") << OutputParserTester::STDERR << QString() - << QString::fromLatin1("Error: L6226E: Some error.\n") + << QString() << (Tasks() << CompileTask(Task::Error, "L6226E: Some error.")) << QString(); @@ -417,7 +398,7 @@ void BareMetalPlugin::testKeilOutputParsers_data() QTest::newRow("MCS51: Assembler simple warning") << QString::fromLatin1("*** WARNING #A9 IN 15 (c:\\foo\\dscr.a51, LINE 15): Some warning") << OutputParserTester::STDOUT - << QString::fromLatin1("*** WARNING #A9 IN 15 (c:\\foo\\dscr.a51, LINE 15): Some warning\n") + << QString() << QString() << (Tasks() << CompileTask(Task::Warning, "#A9: Some warning", @@ -428,7 +409,7 @@ void BareMetalPlugin::testKeilOutputParsers_data() QTest::newRow("MCS51: Assembler simple error") << QString::fromLatin1("*** ERROR #A9 IN 15 (c:\\foo\\dscr.a51, LINE 15): Some error") << OutputParserTester::STDOUT - << QString::fromLatin1("*** ERROR #A9 IN 15 (c:\\foo\\dscr.a51, LINE 15): Some error\n") + << QString() << QString() << (Tasks() << CompileTask(Task::Error, "#A9: Some error", @@ -441,9 +422,7 @@ void BareMetalPlugin::testKeilOutputParsers_data() " Some detail 1\n" " Some detail N") << OutputParserTester::STDOUT - << QString::fromLatin1("A51 FATAL ERROR -\n" - " Some detail 1\n" - " Some detail N\n") + << QString() << QString() << (Tasks() << CompileTask(Task::Error, "Assembler fatal error\n" @@ -456,9 +435,7 @@ void BareMetalPlugin::testKeilOutputParsers_data() "*** ___^\n" "*** ERROR #A45 IN 28 (d:\\foo.a51, LINE 28): Some error") << OutputParserTester::STDOUT - << QString::fromLatin1("01AF Some detail\n" - "*** ___^\n" - "*** ERROR #A45 IN 28 (d:\\foo.a51, LINE 28): Some error\n") + << QString() << QString() << (Tasks() << CompileTask(Task::Error, "#A45: Some error\n" @@ -472,7 +449,7 @@ void BareMetalPlugin::testKeilOutputParsers_data() QTest::newRow("MCS51: Compiler simple warning") << QString::fromLatin1("*** WARNING C123 IN LINE 13 OF c:\\foo.c: Some warning") << OutputParserTester::STDOUT - << QString::fromLatin1("*** WARNING C123 IN LINE 13 OF c:\\foo.c: Some warning\n") + << QString() << QString() << (Tasks() << CompileTask(Task::Warning, "C123: Some warning", @@ -483,7 +460,7 @@ void BareMetalPlugin::testKeilOutputParsers_data() QTest::newRow("MCS51: Compiler extended warning") << QString::fromLatin1("*** WARNING C123 IN LINE 13 OF c:\\foo.c: Some warning : 'extended text'") << OutputParserTester::STDOUT - << QString::fromLatin1("*** WARNING C123 IN LINE 13 OF c:\\foo.c: Some warning : 'extended text'\n") + << QString() << QString() << (Tasks() << CompileTask(Task::Warning, "C123: Some warning : 'extended text'", @@ -494,7 +471,7 @@ void BareMetalPlugin::testKeilOutputParsers_data() QTest::newRow("MCS51: Compiler simple error") << QString::fromLatin1("*** ERROR C123 IN LINE 13 OF c:\\foo.c: Some error") << OutputParserTester::STDOUT - << QString::fromLatin1("*** ERROR C123 IN LINE 13 OF c:\\foo.c: Some error\n") + << QString() << QString() << (Tasks() << CompileTask(Task::Error, "C123: Some error", @@ -505,7 +482,7 @@ void BareMetalPlugin::testKeilOutputParsers_data() QTest::newRow("MCS51: Compiler extended error") << QString::fromLatin1("*** ERROR C123 IN LINE 13 OF c:\\foo.c: Some error : 'extended text'") << OutputParserTester::STDOUT - << QString::fromLatin1("*** ERROR C123 IN LINE 13 OF c:\\foo.c: Some error : 'extended text'\n") + << QString() << QString() << (Tasks() << CompileTask(Task::Error, "C123: Some error : 'extended text'", @@ -518,9 +495,7 @@ void BareMetalPlugin::testKeilOutputParsers_data() " Some detail 1\n" " Some detail N") << OutputParserTester::STDOUT - << QString::fromLatin1("C51 FATAL-ERROR -\n" - " Some detail 1\n" - " Some detail N\n") + << QString() << QString() << (Tasks() << CompileTask(Task::Error, "Compiler fatal error\n" @@ -533,8 +508,7 @@ void BareMetalPlugin::testKeilOutputParsers_data() << QString::fromLatin1("*** WARNING L16: Some warning\n" " Some detail 1") << OutputParserTester::STDOUT - << QString::fromLatin1("*** WARNING L16: Some warning\n" - " Some detail 1\n") + << QString() << QString() << (Tasks() << CompileTask(Task::Warning, "L16: Some warning\n" @@ -544,7 +518,7 @@ void BareMetalPlugin::testKeilOutputParsers_data() QTest::newRow("MCS51: Linker simple fatal error") << QString::fromLatin1("*** FATAL ERROR L456: Some error") << OutputParserTester::STDOUT - << QString::fromLatin1("*** FATAL ERROR L456: Some error\n") + << QString() << QString() << (Tasks() << CompileTask(Task::Error, "L456: Some error")) @@ -555,9 +529,7 @@ void BareMetalPlugin::testKeilOutputParsers_data() " Some detail 1\n" " Some detail N") << OutputParserTester::STDOUT - << QString::fromLatin1("*** FATAL ERROR L456: Some error\n" - " Some detail 1\n" - " Some detail N\n") + << QString() << QString() << (Tasks() << CompileTask(Task::Error, "L456: Some error\n" @@ -569,7 +541,7 @@ void BareMetalPlugin::testKeilOutputParsers_data() void BareMetalPlugin::testKeilOutputParsers() { OutputParserTester testbench; - testbench.appendOutputParser(new KeilParser); + testbench.addLineParser(new KeilParser); QFETCH(QString, input); QFETCH(OutputParserTester::Channel, inputChannel); QFETCH(Tasks, tasks); diff --git a/src/plugins/baremetal/keilparser.h b/src/plugins/baremetal/keilparser.h index d2bcba9f85..3fff04bb09 100644 --- a/src/plugins/baremetal/keilparser.h +++ b/src/plugins/baremetal/keilparser.h @@ -55,9 +55,7 @@ private: bool parseMcs51WarningOrFatalErrorMessage(const QString &lne); bool parseMcs51FatalErrorMessage2(const QString &lne); - void handleLine(const QString &line, Utils::OutputFormat type) final; - void stdError(const QString &line); - void stdOutput(const QString &line); + Status doHandleLine(const QString &line, Utils::OutputFormat type) final; void doFlush() final; ProjectExplorer::Task m_lastTask; diff --git a/src/plugins/baremetal/keiltoolchain.cpp b/src/plugins/baremetal/keiltoolchain.cpp index fd05815cd5..96ee6ec0f8 100644 --- a/src/plugins/baremetal/keiltoolchain.cpp +++ b/src/plugins/baremetal/keiltoolchain.cpp @@ -374,9 +374,9 @@ void KeilToolChain::addToEnvironment(Environment &env) const } } -IOutputParser *KeilToolChain::outputParser() const +QList<IOutputParser *> KeilToolChain::outputParsers() const { - return new KeilParser; + return {new KeilParser}; } QVariantMap KeilToolChain::toMap() const diff --git a/src/plugins/baremetal/keiltoolchain.h b/src/plugins/baremetal/keiltoolchain.h index 341e28a1b3..7326973037 100644 --- a/src/plugins/baremetal/keiltoolchain.h +++ b/src/plugins/baremetal/keiltoolchain.h @@ -69,7 +69,7 @@ public: const Utils::FilePath &, const Utils::Environment &env) const final; void addToEnvironment(Utils::Environment &env) const final; - ProjectExplorer::IOutputParser *outputParser() const final; + QList<ProjectExplorer::IOutputParser *> outputParsers() const final; QVariantMap toMap() const final; bool fromMap(const QVariantMap &data) final; diff --git a/src/plugins/baremetal/sdccparser.cpp b/src/plugins/baremetal/sdccparser.cpp index 445758c49c..fdf46c3349 100644 --- a/src/plugins/baremetal/sdccparser.cpp +++ b/src/plugins/baremetal/sdccparser.cpp @@ -87,17 +87,10 @@ void SdccParser::amendDescription(const QString &desc) ++m_lines; } -void SdccParser::handleLine(const QString &line, OutputFormat type) +IOutputParser::Status SdccParser::doHandleLine(const QString &line, OutputFormat type) { if (type == StdOutFormat) - stdOutput(line); - else - stdError(line); -} - -void SdccParser::stdError(const QString &line) -{ - IOutputParser::handleLine(line, StdErrFormat); + return Status::NotHandled; const QString lne = rightTrimmed(line); @@ -115,7 +108,7 @@ void SdccParser::stdError(const QString &line) const Task::TaskType type = taskType(match.captured(MessageTypeIndex)); const QString descr = match.captured(MessageTextIndex); newTask(CompileTask(type, descr, absoluteFilePath(fileName), lineno)); - return; + return Status::InProgress; } re.setPattern("^(.+\\.\\S+):(\\d+): (Error|error|syntax error): (.+)$"); @@ -129,7 +122,7 @@ void SdccParser::stdError(const QString &line) const Task::TaskType type = taskType(match.captured(MessageTypeIndex)); const QString descr = match.captured(MessageTextIndex); newTask(CompileTask(type, descr, absoluteFilePath(fileName), lineno)); - return; + return Status::InProgress; } re.setPattern("^at (\\d+): (warning|error) \\d+: (.+)$"); @@ -139,7 +132,7 @@ void SdccParser::stdError(const QString &line) const Task::TaskType type = taskType(match.captured(MessageTypeIndex)); const QString descr = match.captured(MessageTextIndex); newTask(CompileTask(type, descr)); - return; + return Status::InProgress; } re.setPattern("^\\?ASlink-(Warning|Error)-(.+)$"); @@ -149,20 +142,16 @@ void SdccParser::stdError(const QString &line) const Task::TaskType type = taskType(match.captured(MessageTypeIndex)); const QString descr = match.captured(MessageTextIndex); newTask(CompileTask(type, descr)); - return; + return Status::InProgress; } if (!m_lastTask.isNull()) { amendDescription(lne); - return; + return Status::InProgress; } doFlush(); -} - -void SdccParser::stdOutput(const QString &line) -{ - IOutputParser::handleLine(line, StdOutFormat); + return Status::NotHandled; } void SdccParser::doFlush() @@ -215,7 +204,7 @@ void BareMetalPlugin::testSdccOutputParsers_data() << QString::fromLatin1("c:\\foo\\main.c:63: Error: Some error") << OutputParserTester::STDERR << QString() - << QString::fromLatin1("c:\\foo\\main.c:63: Error: Some error\n") + << QString() << (Tasks() << CompileTask(Task::Error, "Some error", FilePath::fromUserInput("c:\\foo\\main.c"), @@ -226,7 +215,7 @@ void BareMetalPlugin::testSdccOutputParsers_data() << QString::fromLatin1("c:\\foo\\main.c:63: warning 123: Some warning") << OutputParserTester::STDERR << QString() - << QString::fromLatin1("c:\\foo\\main.c:63: warning 123: Some warning\n") + << QString() << (Tasks() << CompileTask(Task::Warning, "Some warning", FilePath::fromUserInput("c:\\foo\\main.c"), @@ -239,9 +228,7 @@ void BareMetalPlugin::testSdccOutputParsers_data() " details #2") << OutputParserTester::STDERR << QString() - << QString::fromLatin1("c:\\foo\\main.c:63: warning 123: Some warning\n" - "details #1\n" - " details #2\n") + << QString() << (Tasks() << CompileTask(Task::Warning, "Some warning\n" "details #1\n" @@ -254,7 +241,7 @@ void BareMetalPlugin::testSdccOutputParsers_data() << QString::fromLatin1("c:\\foo\\main.c:63: error: Some error") << OutputParserTester::STDERR << QString() - << QString::fromLatin1("c:\\foo\\main.c:63: error: Some error\n") + << QString() << (Tasks() << CompileTask(Task::Error, "Some error", FilePath::fromUserInput("c:\\foo\\main.c"), @@ -265,7 +252,7 @@ void BareMetalPlugin::testSdccOutputParsers_data() << QString::fromLatin1("c:\\foo\\main.c:63: error 123: Some error") << OutputParserTester::STDERR << QString() - << QString::fromLatin1("c:\\foo\\main.c:63: error 123: Some error\n") + << QString() << (Tasks() << CompileTask(Task::Error, "Some error", FilePath::fromUserInput("c:\\foo\\main.c"), @@ -278,9 +265,7 @@ void BareMetalPlugin::testSdccOutputParsers_data() " details #2") << OutputParserTester::STDERR << QString() - << QString::fromLatin1("c:\\foo\\main.c:63: error 123: Some error\n" - "details #1\n" - " details #2\n") + << QString() << (Tasks() << CompileTask(Task::Error, "Some error\n" "details #1\n" @@ -293,7 +278,7 @@ void BareMetalPlugin::testSdccOutputParsers_data() << QString::fromLatin1("c:\\foo\\main.c:63: syntax error: Some error") << OutputParserTester::STDERR << QString() - << QString::fromLatin1("c:\\foo\\main.c:63: syntax error: Some error\n") + << QString() << (Tasks() << CompileTask(Task::Error, "Some error", FilePath::fromUserInput("c:\\foo\\main.c"), @@ -304,7 +289,7 @@ void BareMetalPlugin::testSdccOutputParsers_data() << QString::fromLatin1("at 1: error 123: Some error") << OutputParserTester::STDERR << QString() - << QString::fromLatin1("at 1: error 123: Some error\n") + << QString() << (Tasks() << CompileTask(Task::Error, "Some error")) << QString(); @@ -313,7 +298,7 @@ void BareMetalPlugin::testSdccOutputParsers_data() << QString::fromLatin1("at 1: warning 123: Some warning") << OutputParserTester::STDERR << QString() - << QString::fromLatin1("at 1: warning 123: Some warning\n") + << QString() << (Tasks() << CompileTask(Task::Warning, "Some warning")) << QString(); @@ -322,7 +307,7 @@ void BareMetalPlugin::testSdccOutputParsers_data() << QString::fromLatin1("?ASlink-Warning-Couldn't find library 'foo.lib'") << OutputParserTester::STDERR << QString() - << QString::fromLatin1("?ASlink-Warning-Couldn't find library 'foo.lib'\n") + << QString() << (Tasks() << CompileTask(Task::Warning, "Couldn't find library 'foo.lib'")) << QString(); @@ -331,7 +316,7 @@ void BareMetalPlugin::testSdccOutputParsers_data() << QString::fromLatin1("?ASlink-Error-<cannot open> : \"foo.rel\"") << OutputParserTester::STDERR << QString() - << QString::fromLatin1("?ASlink-Error-<cannot open> : \"foo.rel\"\n") + << QString() << (Tasks() << CompileTask(Task::Error, "<cannot open> : \"foo.rel\"")) << QString(); @@ -340,7 +325,7 @@ void BareMetalPlugin::testSdccOutputParsers_data() void BareMetalPlugin::testSdccOutputParsers() { OutputParserTester testbench; - testbench.appendOutputParser(new SdccParser); + testbench.addLineParser(new SdccParser); QFETCH(QString, input); QFETCH(OutputParserTester::Channel, inputChannel); QFETCH(Tasks, tasks); diff --git a/src/plugins/baremetal/sdccparser.h b/src/plugins/baremetal/sdccparser.h index 2ce08e0d97..f96b00a3b5 100644 --- a/src/plugins/baremetal/sdccparser.h +++ b/src/plugins/baremetal/sdccparser.h @@ -45,9 +45,7 @@ private: void newTask(const ProjectExplorer::Task &task); void amendDescription(const QString &desc); - void handleLine(const QString &line, Utils::OutputFormat type) final; - void stdError(const QString &line); - void stdOutput(const QString &line); + Status doHandleLine(const QString &line, Utils::OutputFormat type) final; void doFlush() final; ProjectExplorer::Task m_lastTask; diff --git a/src/plugins/baremetal/sdcctoolchain.cpp b/src/plugins/baremetal/sdcctoolchain.cpp index 84dd4134c7..fcdf639e89 100644 --- a/src/plugins/baremetal/sdcctoolchain.cpp +++ b/src/plugins/baremetal/sdcctoolchain.cpp @@ -307,9 +307,9 @@ void SdccToolChain::addToEnvironment(Environment &env) const } } -IOutputParser *SdccToolChain::outputParser() const +QList<IOutputParser *> SdccToolChain::outputParsers() const { - return new SdccParser; + return {new SdccParser}; } QVariantMap SdccToolChain::toMap() const diff --git a/src/plugins/baremetal/sdcctoolchain.h b/src/plugins/baremetal/sdcctoolchain.h index aff4be1226..01e3abbe54 100644 --- a/src/plugins/baremetal/sdcctoolchain.h +++ b/src/plugins/baremetal/sdcctoolchain.h @@ -69,7 +69,7 @@ public: const Utils::FilePath &, const Utils::Environment &env) const final; void addToEnvironment(Utils::Environment &env) const final; - ProjectExplorer::IOutputParser *outputParser() const final; + QList<ProjectExplorer::IOutputParser *> outputParsers() const final; QVariantMap toMap() const final; bool fromMap(const QVariantMap &data) final; diff --git a/src/plugins/cmakeprojectmanager/cmakebuildstep.cpp b/src/plugins/cmakeprojectmanager/cmakebuildstep.cpp index 519433e894..22b114518b 100644 --- a/src/plugins/cmakeprojectmanager/cmakebuildstep.cpp +++ b/src/plugins/cmakeprojectmanager/cmakebuildstep.cpp @@ -214,9 +214,7 @@ bool CMakeBuildStep::init() cmakeParser->setSourceDirectory(projectDirectory.toString()); setOutputParser(cmakeParser); appendOutputParser(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/cmakeprojectmanager/cmakeparser.cpp b/src/plugins/cmakeprojectmanager/cmakeparser.cpp index 69283d421b..86d369cd60 100644 --- a/src/plugins/cmakeprojectmanager/cmakeparser.cpp +++ b/src/plugins/cmakeprojectmanager/cmakeparser.cpp @@ -57,22 +57,21 @@ void CMakeParser::setSourceDirectory(const QString &sourceDir) m_sourceDirectory = QDir(sourceDir); } -void CMakeParser::handleLine(const QString &line, OutputFormat type) +IOutputParser::Status CMakeParser::doHandleLine(const QString &line, OutputFormat type) { - if (type != StdErrFormat) { - IOutputParser::handleLine(line, type); - return; - } + if (type != StdErrFormat) + return Status::NotHandled; QString trimmedLine = rightTrimmed(line); switch (m_expectTripleLineErrorData) { case NONE: if (trimmedLine.isEmpty() && !m_lastTask.isNull()) { - if (m_skippedFirstEmptyLine) + if (m_skippedFirstEmptyLine) { doFlush(); - else - m_skippedFirstEmptyLine = true; - return; + return Status::InProgress; + } + m_skippedFirstEmptyLine = true; + return Status::InProgress; } if (m_skippedFirstEmptyLine) m_skippedFirstEmptyLine = false; @@ -87,35 +86,34 @@ void CMakeParser::handleLine(const QString &line, OutputFormat type) absoluteFilePath(FilePath::fromUserInput(path)), m_commonError.cap(2).toInt()); m_lines = 1; - return; + return Status::InProgress; } else if (m_nextSubError.indexIn(trimmedLine) != -1) { m_lastTask = BuildSystemTask(Task::Error, QString(), absoluteFilePath(FilePath::fromUserInput(m_nextSubError.cap(1)))); m_lines = 1; - return; + return Status::InProgress; } else if (trimmedLine.startsWith(QLatin1String(" ")) && !m_lastTask.isNull()) { if (!m_lastTask.description.isEmpty()) m_lastTask.description.append(QLatin1Char(' ')); m_lastTask.description.append(trimmedLine.trimmed()); ++m_lines; - return; + return Status::InProgress; } else if (trimmedLine.endsWith(QLatin1String("in cmake code at"))) { m_expectTripleLineErrorData = LINE_LOCATION; doFlush(); const Task::TaskType type = trimmedLine.contains(QLatin1String("Error")) ? Task::Error : Task::Warning; m_lastTask = BuildSystemTask(type, QString()); - return; + return Status::InProgress; } else if (trimmedLine.startsWith("CMake Error: ")) { m_lastTask = BuildSystemTask(Task::Error, trimmedLine.mid(13)); m_lines = 1; - return; + return Status::InProgress; } else if (trimmedLine.startsWith("-- ") || trimmedLine.startsWith(" * ")) { // Do not pass on lines starting with "-- " or "* ". Those are typical CMake output - return; + return Status::InProgress; } - IOutputParser::handleLine(line, StdErrFormat); - return; + return Status::NotHandled; case LINE_LOCATION: { QRegularExpressionMatch m = m_locationLine.match(trimmedLine); @@ -124,7 +122,7 @@ void CMakeParser::handleLine(const QString &line, OutputFormat type) m_lastTask.line = m.captured(1).toInt(); m_expectTripleLineErrorData = LINE_DESCRIPTION; } - return; + return Status::InProgress; case LINE_DESCRIPTION: m_lastTask.description = trimmedLine; if (trimmedLine.endsWith(QLatin1Char('\"'))) @@ -132,14 +130,15 @@ void CMakeParser::handleLine(const QString &line, OutputFormat type) else { m_expectTripleLineErrorData = NONE; doFlush(); + return Status::Done; } - return; + return Status::InProgress; case LINE_DESCRIPTION2: m_lastTask.description.append(QLatin1Char('\n')); m_lastTask.description.append(trimmedLine); m_expectTripleLineErrorData = NONE; doFlush(); - return; + return Status::Done; } } @@ -301,7 +300,7 @@ void Internal::CMakeProjectPlugin::testCMakeParser_data() void Internal::CMakeProjectPlugin::testCMakeParser() { OutputParserTester testbench; - testbench.appendOutputParser(new CMakeParser); + testbench.addLineParser(new CMakeParser); QFETCH(QString, input); QFETCH(OutputParserTester::Channel, inputChannel); QFETCH(Tasks, tasks); diff --git a/src/plugins/cmakeprojectmanager/cmakeparser.h b/src/plugins/cmakeprojectmanager/cmakeparser.h index a8fc2298d4..4b17b5a62f 100644 --- a/src/plugins/cmakeprojectmanager/cmakeparser.h +++ b/src/plugins/cmakeprojectmanager/cmakeparser.h @@ -45,7 +45,7 @@ public: void setSourceDirectory(const QString &sourceDir); private: - void handleLine(const QString &line, Utils::OutputFormat type) override; + Status doHandleLine(const QString &line, Utils::OutputFormat type) override; void doFlush() override; enum TripleLineError { NONE, LINE_LOCATION, LINE_DESCRIPTION, LINE_DESCRIPTION2 }; diff --git a/src/plugins/ios/iosbuildstep.cpp b/src/plugins/ios/iosbuildstep.cpp index 6566134398..a8503d0fc6 100644 --- a/src/plugins/ios/iosbuildstep.cpp +++ b/src/plugins/ios/iosbuildstep.cpp @@ -223,9 +223,7 @@ bool IosBuildStep::init() setIgnoreReturnValue(m_clean); 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/ios/iosdsymbuildstep.cpp b/src/plugins/ios/iosdsymbuildstep.cpp index 937811bcd3..f188630bc6 100644 --- a/src/plugins/ios/iosdsymbuildstep.cpp +++ b/src/plugins/ios/iosdsymbuildstep.cpp @@ -80,9 +80,8 @@ bool IosDsymBuildStep::init() // That is mostly so that rebuild works on an already clean project setIgnoreReturnValue(m_clean); - setOutputParser(target()->kit()->createOutputParser()); - if (outputParser()) - outputParser()->addSearchDir(pp->effectiveWorkingDirectory()); + appendOutputParsers(target()->kit()->createOutputParsers()); + outputParser()->addSearchDir(pp->effectiveWorkingDirectory()); return AbstractProcessStep::init(); } diff --git a/src/plugins/nim/project/nimblebuildstep.cpp b/src/plugins/nim/project/nimblebuildstep.cpp index eb7a31f07a..36efa46313 100644 --- a/src/plugins/nim/project/nimblebuildstep.cpp +++ b/src/plugins/nim/project/nimblebuildstep.cpp @@ -45,15 +45,9 @@ namespace { class NimParser : public IOutputParser { -private: - void handleLine(const QString &line, Utils::OutputFormat type) override - { - parseLine(line.trimmed()); - IOutputParser::handleLine(line, type); - } - - void parseLine(const QString &line) + Status doHandleLine(const QString &lne, Utils::OutputFormat) override { + const QString line = lne.trimmed(); static QRegularExpression regex("(.+.nim)\\((\\d+), (\\d+)\\) (.+)", QRegularExpression::OptimizeOnFirstUsageOption); static QRegularExpression warning("(Warning):(.*)", @@ -63,13 +57,13 @@ private: QRegularExpressionMatch match = regex.match(line); if (!match.hasMatch()) - return; + return Status::NotHandled; const QString filename = match.captured(1); bool lineOk = false; const int lineNumber = match.captured(2).toInt(&lineOk); const QString message = match.captured(4); if (!lineOk) - return; + return Status::NotHandled; Task::TaskType type = Task::Unknown; @@ -78,10 +72,11 @@ private: else if (error.match(message).hasMatch()) type = Task::Error; else - return; + return Status::NotHandled; emit addTask(CompileTask(type, message, absoluteFilePath(FilePath::fromUserInput(filename)), lineNumber)); + return Status::Done; } }; diff --git a/src/plugins/nim/project/nimcompilerbuildstep.cpp b/src/plugins/nim/project/nimcompilerbuildstep.cpp index 071bc85cb2..8bdf39d576 100644 --- a/src/plugins/nim/project/nimcompilerbuildstep.cpp +++ b/src/plugins/nim/project/nimcompilerbuildstep.cpp @@ -47,14 +47,9 @@ namespace Nim { class NimParser : public ProjectExplorer::IOutputParser { - void handleLine(const QString &line, Utils::OutputFormat type) override - { - parseLine(line.trimmed()); - IOutputParser::handleLine(line, type); - } - - void parseLine(const QString &line) + Status doHandleLine(const QString &lne, Utils::OutputFormat) override { + const QString line = lne.trimmed(); static QRegularExpression regex("(.+.nim)\\((\\d+), (\\d+)\\) (.+)", QRegularExpression::OptimizeOnFirstUsageOption); static QRegularExpression warning("(Warning):(.*)", @@ -64,13 +59,13 @@ class NimParser : public ProjectExplorer::IOutputParser QRegularExpressionMatch match = regex.match(line); if (!match.hasMatch()) - return; + return Status::NotHandled; const QString filename = match.captured(1); bool lineOk = false; const int lineNumber = match.captured(2).toInt(&lineOk); const QString message = match.captured(4); if (!lineOk) - return; + return Status::NotHandled; Task::TaskType type = Task::Unknown; @@ -79,10 +74,11 @@ class NimParser : public ProjectExplorer::IOutputParser else if (error.match(message).hasMatch()) type = Task::Error; else - return; + return Status::NotHandled; emit addTask(CompileTask(type, message, absoluteFilePath(FilePath::fromUserInput(filename)), lineNumber)); + return Status::Done; } }; @@ -107,8 +103,7 @@ NimCompilerBuildStep::NimCompilerBuildStep(BuildStepList *parentList, Core::Id i bool NimCompilerBuildStep::init() { setOutputParser(new NimParser()); - if (IOutputParser *parser = target()->kit()->createOutputParser()) - appendOutputParser(parser); + appendOutputParsers(target()->kit()->createOutputParsers()); outputParser()->addSearchDir(processParameters()->effectiveWorkingDirectory()); return AbstractProcessStep::init(); } @@ -308,7 +303,7 @@ void NimPlugin::testNimParser_data() QTest::newRow("Parse error string") << QString::fromLatin1("main.nim(23, 1) Error: undeclared identifier: 'x'") << OutputParserTester::STDERR - << QString("") << QString("main.nim(23, 1) Error: undeclared identifier: 'x'\n") + << QString() << QString() << Tasks({CompileTask(Task::Error, "Error: undeclared identifier: 'x'", FilePath::fromUserInput("main.nim"), 23)}) @@ -317,7 +312,7 @@ void NimPlugin::testNimParser_data() QTest::newRow("Parse warning string") << QString::fromLatin1("lib/pure/parseopt.nim(56, 34) Warning: quoteIfContainsWhite is deprecated [Deprecated]") << OutputParserTester::STDERR - << QString("") << QString("lib/pure/parseopt.nim(56, 34) Warning: quoteIfContainsWhite is deprecated [Deprecated]\n") + << QString() << QString() << Tasks({CompileTask(Task::Warning, "Warning: quoteIfContainsWhite is deprecated [Deprecated]", FilePath::fromUserInput("lib/pure/parseopt.nim"), 56)}) @@ -327,7 +322,7 @@ void NimPlugin::testNimParser_data() void NimPlugin::testNimParser() { OutputParserTester testbench; - testbench.appendOutputParser(new NimParser); + testbench.addLineParser(new NimParser); QFETCH(QString, input); QFETCH(OutputParserTester::Channel, inputChannel); QFETCH(Tasks, tasks); diff --git a/src/plugins/nim/project/nimtoolchain.cpp b/src/plugins/nim/project/nimtoolchain.cpp index 031557c889..7aed9dacf6 100644 --- a/src/plugins/nim/project/nimtoolchain.cpp +++ b/src/plugins/nim/project/nimtoolchain.cpp @@ -120,9 +120,9 @@ void NimToolChain::setCompilerCommand(const FilePath &compilerCommand) parseVersion(compilerCommand, m_version); } -IOutputParser *NimToolChain::outputParser() const +QList<IOutputParser *> NimToolChain::outputParsers() const { - return nullptr; + return {}; } std::unique_ptr<ProjectExplorer::ToolChainConfigWidget> NimToolChain::createConfigurationWidget() diff --git a/src/plugins/nim/project/nimtoolchain.h b/src/plugins/nim/project/nimtoolchain.h index a140f1444f..df21587776 100644 --- a/src/plugins/nim/project/nimtoolchain.h +++ b/src/plugins/nim/project/nimtoolchain.h @@ -56,7 +56,7 @@ public: Utils::FilePath compilerCommand() const final; QString compilerVersion() const; void setCompilerCommand(const Utils::FilePath &compilerCommand); - ProjectExplorer::IOutputParser *outputParser() const final; + QList<ProjectExplorer::IOutputParser *> outputParsers() const final; std::unique_ptr<ProjectExplorer::ToolChainConfigWidget> createConfigurationWidget() final; QVariantMap toMap() const final; 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; diff --git a/src/plugins/qbsprojectmanager/qbsbuildstep.cpp b/src/plugins/qbsprojectmanager/qbsbuildstep.cpp index 43034fd87d..a8ee28540c 100644 --- a/src/plugins/qbsprojectmanager/qbsbuildstep.cpp +++ b/src/plugins/qbsprojectmanager/qbsbuildstep.cpp @@ -169,9 +169,9 @@ bool QbsBuildStep::init() return false; delete m_parser; - m_parser = target()->kit()->createOutputParser(); - if (m_parser) - connect(m_parser, &ProjectExplorer::IOutputParser::addTask, this, &QbsBuildStep::addTask); + m_parser = new IOutputParser; + m_parser->setLineParsers(target()->kit()->createOutputParsers()); + connect(m_parser, &ProjectExplorer::IOutputParser::addTask, this, &QbsBuildStep::addTask); m_changedFiles = bc->changedFiles(); m_activeFileTags = bc->activeFileTags(); diff --git a/src/plugins/qmakeprojectmanager/qmakemakestep.cpp b/src/plugins/qmakeprojectmanager/qmakemakestep.cpp index f3c85b388c..d5540b7060 100644 --- a/src/plugins/qmakeprojectmanager/qmakemakestep.cpp +++ b/src/plugins/qmakeprojectmanager/qmakemakestep.cpp @@ -166,14 +166,22 @@ bool QmakeMakeStep::init() setOutputParser(new ProjectExplorer::GnuMakeParser()); ToolChain *tc = ToolChainKitAspect::cxxToolChain(target()->kit()); - if (tc && tc->targetAbi().os() == Abi::DarwinOS) - appendOutputParser(new XcodebuildParser); - IOutputParser *parser = target()->kit()->createOutputParser(); - if (parser) - appendOutputParser(parser); + IOutputParser *xcodeBuildParser = nullptr; + if (tc && tc->targetAbi().os() == Abi::DarwinOS) { + xcodeBuildParser = new XcodebuildParser; + appendOutputParser(xcodeBuildParser); + } + QList<IOutputParser *> additionalParsers = target()->kit()->createOutputParsers(); + + // make may cause qmake to be run, add last to make sure // it has a low priority. + additionalParsers << new QMakeParser; + + if (xcodeBuildParser) { + for (IOutputParser * const p : qAsConst(additionalParsers)) + p->setRedirectionDetector(xcodeBuildParser); + } + appendOutputParsers(additionalParsers); outputParser()->addSearchDir(pp->effectiveWorkingDirectory()); - appendOutputParser(new QMakeParser); // make may cause qmake to be run, add last to make sure - // it has a low priority. auto rootNode = dynamic_cast<QmakeProFileNode *>(project()->rootProjectNode()); QTC_ASSERT(rootNode, return false); diff --git a/src/plugins/qmakeprojectmanager/qmakeparser.cpp b/src/plugins/qmakeprojectmanager/qmakeparser.cpp index 3952e591ec..86a0ffc67b 100644 --- a/src/plugins/qmakeprojectmanager/qmakeparser.cpp +++ b/src/plugins/qmakeprojectmanager/qmakeparser.cpp @@ -40,12 +40,10 @@ QMakeParser::QMakeParser() : m_error(QLatin1String("^(.+):(\\d+):\\s(.+)$")) m_error.setMinimal(true); } -void QMakeParser::handleLine(const QString &line, OutputFormat type) +IOutputParser::Status QMakeParser::doHandleLine(const QString &line, OutputFormat type) { - if (type != Utils::StdErrFormat) { - IOutputParser::handleLine(line, type); - return; - } + if (type != Utils::StdErrFormat) + return Status::NotHandled; QString lne = rightTrimmed(line); if (m_error.indexIn(lne) > -1) { QString fileName = m_error.cap(1); @@ -68,21 +66,21 @@ void QMakeParser::handleLine(const QString &line, OutputFormat type) absoluteFilePath(FilePath::fromUserInput(fileName)), m_error.cap(2).toInt() /* line */), 1); - return; + return Status::Done; } if (lne.startsWith(QLatin1String("Project ERROR: ")) || lne.startsWith(QLatin1String("ERROR: "))) { const QString description = lne.mid(lne.indexOf(QLatin1Char(':')) + 2); emit addTask(BuildSystemTask(Task::Error, description), 1); - return; + return Status::Done; } if (lne.startsWith(QLatin1String("Project WARNING: ")) || lne.startsWith(QLatin1String("WARNING: "))) { const QString description = lne.mid(lne.indexOf(QLatin1Char(':')) + 2); emit addTask(BuildSystemTask(Task::Warning, description), 1); - return; + return Status::Done; } - IOutputParser::handleLine(line, StdErrFormat); + return Status::NotHandled; } } // QmakeProjectManager @@ -183,7 +181,7 @@ void QmakeProjectManagerPlugin::testQmakeOutputParsers_data() void QmakeProjectManagerPlugin::testQmakeOutputParsers() { OutputParserTester testbench; - testbench.appendOutputParser(new QMakeParser); + testbench.addLineParser(new QMakeParser); QFETCH(QString, input); QFETCH(OutputParserTester::Channel, inputChannel); QFETCH(Tasks, tasks); diff --git a/src/plugins/qmakeprojectmanager/qmakeparser.h b/src/plugins/qmakeprojectmanager/qmakeparser.h index 11e0529651..822700efd0 100644 --- a/src/plugins/qmakeprojectmanager/qmakeparser.h +++ b/src/plugins/qmakeprojectmanager/qmakeparser.h @@ -41,7 +41,7 @@ public: QMakeParser(); private: - void handleLine(const QString &line, Utils::OutputFormat type) override; + Status doHandleLine(const QString &line, Utils::OutputFormat type) override; QRegExp m_error; }; diff --git a/src/plugins/qtsupport/qtkitinformation.cpp b/src/plugins/qtsupport/qtkitinformation.cpp index 0ff13d07ec..5243699afd 100644 --- a/src/plugins/qtsupport/qtkitinformation.cpp +++ b/src/plugins/qtsupport/qtkitinformation.cpp @@ -288,14 +288,11 @@ void QtKitAspect::addToEnvironment(const ProjectExplorer::Kit *k, Utils::Environ version->addToEnvironment(k, env); } -ProjectExplorer::IOutputParser *QtKitAspect::createOutputParser(const ProjectExplorer::Kit *k) const +QList<IOutputParser *> QtKitAspect::createOutputParsers(const Kit *k) const { - if (qtVersion(k)) { - const auto parser = new Internal::QtTestParser; - parser->appendOutputParser(new QtParser); - return parser; - } - return nullptr; + if (qtVersion(k)) + return {new Internal::QtTestParser, new QtParser}; + return {}; } class QtMacroSubProvider diff --git a/src/plugins/qtsupport/qtkitinformation.h b/src/plugins/qtsupport/qtkitinformation.h index d155b8ea5f..e63c75c61e 100644 --- a/src/plugins/qtsupport/qtkitinformation.h +++ b/src/plugins/qtsupport/qtkitinformation.h @@ -54,7 +54,7 @@ public: ItemList toUserOutput(const ProjectExplorer::Kit *k) const override; void addToEnvironment(const ProjectExplorer::Kit *k, Utils::Environment &env) const override; - ProjectExplorer::IOutputParser *createOutputParser(const ProjectExplorer::Kit *k) const override; + QList<ProjectExplorer::IOutputParser *> createOutputParsers(const ProjectExplorer::Kit *k) const override; void addToMacroExpander(ProjectExplorer::Kit *kit, Utils::MacroExpander *expander) const override; static Core::Id id(); diff --git a/src/plugins/qtsupport/qtparser.cpp b/src/plugins/qtsupport/qtparser.cpp index edeff3d178..c3fb0d5710 100644 --- a/src/plugins/qtsupport/qtparser.cpp +++ b/src/plugins/qtsupport/qtparser.cpp @@ -43,12 +43,11 @@ QtParser::QtParser() : m_translationRegExp.setMinimal(true); } -void QtParser::handleLine(const QString &line, Utils::OutputFormat type) +IOutputParser::Status QtParser::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 (m_mocRegExp.indexIn(lne) > -1) { bool ok; @@ -65,7 +64,7 @@ void QtParser::handleLine(const QString &line, Utils::OutputFormat type) absoluteFilePath(Utils::FilePath::fromUserInput(m_mocRegExp.cap(1))), lineno); emit addTask(task, 1); - return; + return Status::Done; } if (m_translationRegExp.indexIn(lne) > -1) { Task::TaskType type = Task::Warning; @@ -74,9 +73,9 @@ void QtParser::handleLine(const QString &line, Utils::OutputFormat type) CompileTask task(type, m_translationRegExp.cap(2), absoluteFilePath(Utils::FilePath::fromUserInput(m_translationRegExp.cap(3)))); emit addTask(task, 1); - return; + return Status::Done; } - IOutputParser::handleLine(line, Utils::StdErrFormat); + return Status::NotHandled; } // Unit tests: @@ -179,7 +178,7 @@ void QtSupportPlugin::testQtOutputParser_data() void QtSupportPlugin::testQtOutputParser() { OutputParserTester testbench; - testbench.appendOutputParser(new QtParser); + testbench.addLineParser(new QtParser); QFETCH(QString, input); QFETCH(OutputParserTester::Channel, inputChannel); QFETCH(Tasks, tasks); diff --git a/src/plugins/qtsupport/qtparser.h b/src/plugins/qtsupport/qtparser.h index 62e6fe6622..8fda649bbc 100644 --- a/src/plugins/qtsupport/qtparser.h +++ b/src/plugins/qtsupport/qtparser.h @@ -42,7 +42,7 @@ public: QtParser(); private: - void handleLine(const QString &line, Utils::OutputFormat type) override; + Status doHandleLine(const QString &line, Utils::OutputFormat type) override; QRegExp m_mocRegExp; QRegExp m_translationRegExp; diff --git a/src/plugins/qtsupport/qttestparser.cpp b/src/plugins/qtsupport/qttestparser.cpp index 9c84ee46c0..be3c2920e0 100644 --- a/src/plugins/qtsupport/qttestparser.cpp +++ b/src/plugins/qtsupport/qttestparser.cpp @@ -47,12 +47,11 @@ using namespace Utils; namespace QtSupport { namespace Internal { -void QtTestParser::handleLine(const QString &line, OutputFormat type) +IOutputParser::Status QtTestParser::doHandleLine(const QString &line, OutputFormat type) { - if (type != StdOutFormat) { - IOutputParser::handleLine(line, type); - return; - } + if (type != StdOutFormat) + return Status::NotHandled; + const QString theLine = rightTrimmed(line); static const QRegularExpression triggerPattern("^(?:XPASS|FAIL!) : .+$"); QTC_CHECK(triggerPattern.isValid()); @@ -60,12 +59,10 @@ void QtTestParser::handleLine(const QString &line, OutputFormat type) emitCurrentTask(); m_currentTask = Task(Task::Error, theLine, FilePath(), -1, Constants::TASK_CATEGORY_AUTOTEST); - return; - } - if (m_currentTask.isNull()) { - IOutputParser::handleLine(line, StdOutFormat); - return; + return Status::InProgress; } + if (m_currentTask.isNull()) + return Status::NotHandled; static const QRegularExpression locationPattern(HostOsInfo::isWindowsHost() ? QString(QT_TEST_FAIL_WIN_REGEXP) : QString(QT_TEST_FAIL_UNIX_REGEXP)); @@ -76,9 +73,10 @@ void QtTestParser::handleLine(const QString &line, OutputFormat type) QDir::fromNativeSeparators(match.captured("file")))); m_currentTask.line = match.captured("line").toInt(); emitCurrentTask(); - return; + return Status::Done; } m_currentTask.description.append('\n').append(theLine); + return Status::InProgress; } void QtTestParser::emitCurrentTask() @@ -93,7 +91,7 @@ void QtTestParser::emitCurrentTask() void QtSupportPlugin::testQtTestOutputParser() { OutputParserTester testbench; - testbench.appendOutputParser(new QtTestParser); + testbench.addLineParser(new QtTestParser); const QString input = "random output\n" "PASS : MyTest::someTest()\n" "XPASS : MyTest::someTest()\n" diff --git a/src/plugins/qtsupport/qttestparser.h b/src/plugins/qtsupport/qttestparser.h index f99faccd25..d32d7a40f4 100644 --- a/src/plugins/qtsupport/qttestparser.h +++ b/src/plugins/qtsupport/qttestparser.h @@ -35,7 +35,7 @@ class QtTestParser : public ProjectExplorer::IOutputParser { Q_OBJECT private: - void handleLine(const QString &line, Utils::OutputFormat type) override; + Status doHandleLine(const QString &line, Utils::OutputFormat type) override; void doFlush() override { emitCurrentTask(); } void emitCurrentTask(); |