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>
This commit is contained in:
Christian Kandeler
2020-04-08 17:45:39 +02:00
parent fa517bd72a
commit 45ba9fcd53
78 changed files with 807 additions and 931 deletions

View File

@@ -112,9 +112,7 @@ bool AndroidPackageInstallationStep::init()
pp->setCommandLine(cmd); pp->setCommandLine(cmd);
setOutputParser(new GnuMakeParser()); setOutputParser(new GnuMakeParser());
IOutputParser *parser = target()->kit()->createOutputParser(); appendOutputParsers(target()->kit()->createOutputParsers());
if (parser)
appendOutputParser(parser);
outputParser()->addSearchDir(pp->effectiveWorkingDirectory()); outputParser()->addSearchDir(pp->effectiveWorkingDirectory());
m_androidDirsToClean.clear(); m_androidDirsToClean.clear();

View File

@@ -51,15 +51,12 @@ void JavaParser::setSourceDirectory(const Utils::FilePath &sourceDirectory)
m_sourceDirectory = 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); Q_UNUSED(type);
IOutputParser::handleLine(line, type); if (m_javaRegExp.indexIn(line) == -1)
} return Status::NotHandled;
void JavaParser::parse(const QString &line)
{
if (m_javaRegExp.indexIn(line) > -1) {
bool ok; bool ok;
int lineno = m_javaRegExp.cap(3).toInt(&ok); int lineno = m_javaRegExp.cap(3).toInt(&ok);
if (!ok) if (!ok)
@@ -69,7 +66,6 @@ void JavaParser::parse(const QString &line)
Utils::FilePath relativePath = file.relativeChildPath(m_buildDirectory); Utils::FilePath relativePath = file.relativeChildPath(m_buildDirectory);
file = m_sourceDirectory.pathAppended(relativePath.toString()); file = m_sourceDirectory.pathAppended(relativePath.toString());
} }
if (file.toFileInfo().isRelative()) { if (file.toFileInfo().isRelative()) {
for (int i = 0; i < m_fileList.size(); i++) for (int i = 0; i < m_fileList.size(); i++)
if (m_fileList[i].endsWith(file.toString())) { if (m_fileList[i].endsWith(file.toString())) {
@@ -83,7 +79,5 @@ void JavaParser::parse(const QString &line)
absoluteFilePath(file), absoluteFilePath(file),
lineno); lineno);
emit addTask(task, 1); emit addTask(task, 1);
return; return Status::Done;
}
} }

View File

@@ -45,8 +45,7 @@ public:
void setSourceDirectory(const Utils::FilePath &sourceDirectory); void setSourceDirectory(const Utils::FilePath &sourceDirectory);
private: private:
void handleLine(const QString &line, Utils::OutputFormat type) override; Status doHandleLine(const QString &line, Utils::OutputFormat type) override;
void parse(const QString &line);
QRegExp m_javaRegExp; QRegExp m_javaRegExp;
QStringList m_fileList; QStringList m_fileList;

View File

@@ -190,67 +190,56 @@ bool IarParser::parseErrorMessage1(const QString &lne)
return true; return true;
} }
void IarParser::handleLine(const QString &line, OutputFormat type) IOutputParser::Status IarParser::doHandleLine(const QString &line, OutputFormat type)
{ {
if (type == StdOutFormat) const QString lne = rightTrimmed(line);
stdOutput(line); if (type == StdOutFormat) {
else // The call sequence has the meaning!
stdError(line); const bool leastOneParsed = parseErrorInCommandLineMessage(lne)
|| parseErrorMessage1(lne);
if (!leastOneParsed) {
doFlush();
return Status::NotHandled;
}
return Status::InProgress;
} }
void IarParser::stdError(const QString &line)
{
IOutputParser::handleLine(line, StdErrFormat);
const QString lne = rightTrimmed(line);
if (parseErrorOrFatalErrorDetailsMessage1(lne)) if (parseErrorOrFatalErrorDetailsMessage1(lne))
return; return Status::InProgress;
if (parseErrorOrFatalErrorDetailsMessage2(lne)) if (parseErrorOrFatalErrorDetailsMessage2(lne))
return; return Status::InProgress;
if (parseWarningOrErrorOrFatalErrorDetailsMessage1(lne)) if (parseWarningOrErrorOrFatalErrorDetailsMessage1(lne))
return; return Status::InProgress;
if (lne.isEmpty()) { if (m_expectFilePath) {
//
} else if (!lne.startsWith(' ')) {
return;
} else if (m_expectFilePath) {
if (lne.endsWith(']')) { if (lne.endsWith(']')) {
const QString lastPart = lne.left(lne.size() - 1); const QString lastPart = lne.left(lne.size() - 1);
m_filePathParts.push_back(lastPart); m_filePathParts.push_back(lastPart);
doFlush();
return Status::Done;
} else { } else {
m_filePathParts.push_back(lne); 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.")) { if (!lne.endsWith("Fatal error detected, aborting.")) {
m_snippets.push_back(lne); m_snippets.push_back(lne);
return; return Status::InProgress;
} }
} else if (m_expectDescription) { } else if (m_expectDescription) {
if (!lne.startsWith(" ")) { if (!lne.startsWith(" ")) {
m_descriptionParts.push_back(lne.trimmed()); m_descriptionParts.push_back(lne.trimmed());
return; return Status::InProgress;
} }
} }
if (!m_lastTask.isNull()) {
doFlush(); doFlush();
return Status::Done;
} }
void IarParser::stdOutput(const QString &line) return Status::NotHandled;
{
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;
doFlush();
} }
void IarParser::doFlush() void IarParser::doFlush()
@@ -308,7 +297,7 @@ void BareMetalPlugin::testIarOutputParsers_data()
QTest::newRow("Error in command line") QTest::newRow("Error in command line")
<< QString::fromLatin1("Error in command line: Some error") << QString::fromLatin1("Error in command line: Some error")
<< OutputParserTester::STDOUT << OutputParserTester::STDOUT
<< QString::fromLatin1("Error in command line: Some error\n") << QString()
<< QString() << QString()
<< (Tasks() << CompileTask(Task::Error, << (Tasks() << CompileTask(Task::Error,
"Error in command line: Some error")) "Error in command line: Some error"))
@@ -317,7 +306,7 @@ void BareMetalPlugin::testIarOutputParsers_data()
QTest::newRow("Linker error") QTest::newRow("Linker error")
<< QString::fromLatin1("Error[e46]: Some error") << QString::fromLatin1("Error[e46]: Some error")
<< OutputParserTester::STDOUT << OutputParserTester::STDOUT
<< QString::fromLatin1("Error[e46]: Some error\n") << QString()
<< QString() << QString()
<< (Tasks() << CompileTask(Task::Error, << (Tasks() << CompileTask(Task::Error,
"[e46]: Some error")) "[e46]: Some error"))
@@ -329,8 +318,7 @@ void BareMetalPlugin::testIarOutputParsers_data()
" Some warning \"foo\" bar") " Some warning \"foo\" bar")
<< OutputParserTester::STDERR << OutputParserTester::STDERR
<< QString() << QString()
<< QString::fromLatin1("\"c:\\foo\\main.c\",63 Warning[Pe223]:\n" << QString()
" Some warning \"foo\" bar\n")
<< (Tasks() << CompileTask(Task::Warning, << (Tasks() << CompileTask(Task::Warning,
"[Pe223]: Some warning \"foo\" bar", "[Pe223]: Some warning \"foo\" bar",
Utils::FilePath::fromUserInput("c:\\foo\\main.c"), Utils::FilePath::fromUserInput("c:\\foo\\main.c"),
@@ -344,10 +332,7 @@ void BareMetalPlugin::testIarOutputParsers_data()
" Some warning") " Some warning")
<< OutputParserTester::STDERR << OutputParserTester::STDERR
<< QString() << QString()
<< QString::fromLatin1(" some_detail;\n" << QString()
" ^\n"
"\"c:\\foo\\main.c\",63 Warning[Pe223]:\n"
" Some warning\n")
<< (Tasks() << CompileTask(Task::Warning, << (Tasks() << CompileTask(Task::Warning,
"[Pe223]: Some warning\n" "[Pe223]: Some warning\n"
" some_detail;\n" " some_detail;\n"
@@ -362,9 +347,7 @@ void BareMetalPlugin::testIarOutputParsers_data()
" , split") " , split")
<< OutputParserTester::STDERR << OutputParserTester::STDERR
<< QString() << QString()
<< QString::fromLatin1("\"c:\\foo\\main.c\",63 Warning[Pe223]:\n" << QString()
" Some warning\n"
" , split\n")
<< (Tasks() << CompileTask(Task::Warning, << (Tasks() << CompileTask(Task::Warning,
"[Pe223]: Some warning, split", "[Pe223]: Some warning, split",
FilePath::fromUserInput("c:\\foo\\main.c"), FilePath::fromUserInput("c:\\foo\\main.c"),
@@ -376,8 +359,7 @@ void BareMetalPlugin::testIarOutputParsers_data()
" Some error") " Some error")
<< OutputParserTester::STDERR << OutputParserTester::STDERR
<< QString() << QString()
<< QString::fromLatin1("\"c:\\foo\\main.c\",63 Error[Pe223]:\n" << QString()
" Some error\n")
<< (Tasks() << CompileTask(Task::Error, << (Tasks() << CompileTask(Task::Error,
"[Pe223]: Some error", "[Pe223]: Some error",
FilePath::fromUserInput("c:\\foo\\main.c"), FilePath::fromUserInput("c:\\foo\\main.c"),
@@ -391,10 +373,7 @@ void BareMetalPlugin::testIarOutputParsers_data()
" Some error") " Some error")
<< OutputParserTester::STDERR << OutputParserTester::STDERR
<< QString() << QString()
<< QString::fromLatin1(" some_detail;\n" << QString()
" ^\n"
"\"c:\\foo\\main.c\",63 Error[Pe223]:\n"
" Some error\n")
<< (Tasks() << CompileTask(Task::Error, << (Tasks() << CompileTask(Task::Error,
"[Pe223]: Some error\n" "[Pe223]: Some error\n"
" some_detail;\n" " some_detail;\n"
@@ -409,9 +388,7 @@ void BareMetalPlugin::testIarOutputParsers_data()
" , split") " , split")
<< OutputParserTester::STDERR << OutputParserTester::STDERR
<< QString() << QString()
<< QString::fromLatin1("\"c:\\foo\\main.c\",63 Error[Pe223]:\n" << QString()
" Some error\n"
" , split\n")
<< (Tasks() << CompileTask(Task::Error, << (Tasks() << CompileTask(Task::Error,
"[Pe223]: Some error, split", "[Pe223]: Some error, split",
FilePath::fromUserInput("c:\\foo\\main.c"), FilePath::fromUserInput("c:\\foo\\main.c"),
@@ -425,10 +402,7 @@ void BareMetalPlugin::testIarOutputParsers_data()
"]") "]")
<< OutputParserTester::STDERR << OutputParserTester::STDERR
<< QString() << QString()
<< QString::fromLatin1("Error[Li005]: Some error \"foo\" [referenced from c:\\fo\n" << QString()
" o\\bar\\mai\n"
" n.c.o\n"
"]\n")
<< (Tasks() << CompileTask(Task::Error, << (Tasks() << CompileTask(Task::Error,
"[Li005]: Some error \"foo\"", "[Li005]: Some error \"foo\"",
FilePath::fromUserInput("c:\\foo\\bar\\main.c.o"))) FilePath::fromUserInput("c:\\foo\\bar\\main.c.o")))
@@ -441,10 +415,7 @@ void BareMetalPlugin::testIarOutputParsers_data()
"Fatal error detected, aborting.") "Fatal error detected, aborting.")
<< OutputParserTester::STDERR << OutputParserTester::STDERR
<< QString() << QString()
<< QString::fromLatin1("Fatal error[Su011]: Some error:\n" << QString()
" c:\\foo.c\n"
" c:\\bar.c\n"
"Fatal error detected, aborting.\n")
<< (Tasks() << CompileTask(Task::Error, << (Tasks() << CompileTask(Task::Error,
"[Su011]: Some error:\n" "[Su011]: Some error:\n"
" c:\\foo.c\n" " c:\\foo.c\n"
@@ -455,7 +426,7 @@ void BareMetalPlugin::testIarOutputParsers_data()
<< QString::fromLatin1("At end of source Error[Pe040]: Some error \";\"") << QString::fromLatin1("At end of source Error[Pe040]: Some error \";\"")
<< OutputParserTester::STDERR << OutputParserTester::STDERR
<< QString() << QString()
<< QString::fromLatin1("At end of source Error[Pe040]: Some error \";\"\n") << QString()
<< (Tasks() << CompileTask(Task::Error, << (Tasks() << CompileTask(Task::Error,
"[Pe040]: Some error \";\"")) "[Pe040]: Some error \";\""))
<< QString(); << QString();
@@ -464,7 +435,7 @@ void BareMetalPlugin::testIarOutputParsers_data()
void BareMetalPlugin::testIarOutputParsers() void BareMetalPlugin::testIarOutputParsers()
{ {
OutputParserTester testbench; OutputParserTester testbench;
testbench.appendOutputParser(new IarParser); testbench.addLineParser(new IarParser);
QFETCH(QString, input); QFETCH(QString, input);
QFETCH(OutputParserTester::Channel, inputChannel); QFETCH(OutputParserTester::Channel, inputChannel);
QFETCH(Tasks, tasks); QFETCH(Tasks, tasks);

View File

@@ -52,9 +52,7 @@ private:
bool parseErrorInCommandLineMessage(const QString &lne); bool parseErrorInCommandLineMessage(const QString &lne);
bool parseErrorMessage1(const QString &lne); bool parseErrorMessage1(const QString &lne);
void handleLine(const QString &line, Utils::OutputFormat type) final; Status doHandleLine(const QString &line, Utils::OutputFormat type) final;
void stdError(const QString &line);
void stdOutput(const QString &line);
void doFlush() final; void doFlush() final;
ProjectExplorer::Task m_lastTask; ProjectExplorer::Task m_lastTask;

View File

@@ -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 QVariantMap IarToolChain::toMap() const

View File

@@ -68,7 +68,7 @@ public:
const Utils::FilePath &, const Utils::FilePath &,
const Utils::Environment &env) const final; const Utils::Environment &env) const final;
void addToEnvironment(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; QVariantMap toMap() const final;
bool fromMap(const QVariantMap &data) final; bool fromMap(const QVariantMap &data) final;

View File

@@ -190,34 +190,6 @@ bool KeilParser::parseMcs51FatalErrorMessage2(const QString &lne)
return true; 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) static bool hasDetailsEntry(const QString &trimmedLine)
{ {
const QRegularExpression re("^([0-9A-F]{4})"); const QRegularExpression re("^([0-9A-F]{4})");
@@ -234,21 +206,19 @@ static bool hasDetailsPointer(const QString &trimmedLine)
return trimmedLine.contains('_'); 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); QString lne = rightTrimmed(line);
if (type == StdOutFormat) {
// Check for MSC51 compiler specific patterns. // Check for MSC51 compiler specific patterns.
const bool parsed = parseMcs51WarningOrErrorDetailsMessage1(lne) const bool parsed = parseMcs51WarningOrErrorDetailsMessage1(lne)
|| parseMcs51WarningOrErrorDetailsMessage2(lne); || parseMcs51WarningOrErrorDetailsMessage2(lne);
if (!parsed) { if (parsed)
return Status::InProgress;
if (parseMcs51WarningOrFatalErrorMessage(lne)) if (parseMcs51WarningOrFatalErrorMessage(lne))
return; return Status::InProgress;
if (parseMcs51FatalErrorMessage2(lne)) if (parseMcs51FatalErrorMessage2(lne))
return; return Status::InProgress;
}
if (m_lastTask.isNull()) { if (m_lastTask.isNull()) {
// Check for details, which are comes on a previous // Check for details, which are comes on a previous
@@ -262,18 +232,33 @@ void KeilParser::stdOutput(const QString &line)
if (hasDetailsEntry(lne) || hasDetailsPointer(lne)) { if (hasDetailsEntry(lne) || hasDetailsPointer(lne)) {
lne.replace(0, 4, " "); lne.replace(0, 4, " ");
m_snippets.push_back(lne); m_snippets.push_back(lne);
return; return Status::InProgress;
} }
} else { } else {
// Check for details, which are comes on a next // Check for details, which are comes on a next
// lines, after the message. // lines, after the message.
if (lne.startsWith(' ')) { if (lne.startsWith(' ')) {
m_snippets.push_back(lne); m_snippets.push_back(lne);
return; return Status::InProgress;
} }
} }
doFlush();
return Status::NotHandled;
}
// 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(); doFlush();
return Status::NotHandled;
} }
void KeilParser::doFlush() void KeilParser::doFlush()
@@ -328,7 +313,7 @@ void BareMetalPlugin::testKeilOutputParsers_data()
<< QString::fromLatin1("\"c:\\foo\\main.c\", line 63: Warning: #1234: Some warning") << QString::fromLatin1("\"c:\\foo\\main.c\", line 63: Warning: #1234: Some warning")
<< OutputParserTester::STDERR << OutputParserTester::STDERR
<< QString() << QString()
<< QString::fromLatin1("\"c:\\foo\\main.c\", line 63: Warning: #1234: Some warning\n") << QString()
<< (Tasks() << CompileTask(Task::Warning, << (Tasks() << CompileTask(Task::Warning,
"#1234: Some warning", "#1234: Some warning",
FilePath::fromUserInput("c:\\foo\\main.c"), FilePath::fromUserInput("c:\\foo\\main.c"),
@@ -341,9 +326,7 @@ void BareMetalPlugin::testKeilOutputParsers_data()
" ^") " ^")
<< OutputParserTester::STDERR << OutputParserTester::STDERR
<< QString() << QString()
<< QString::fromLatin1("\"c:\\foo\\main.c\", line 63: Warning: #1234: Some warning\n" << QString()
" int f;\n"
" ^\n")
<< (Tasks() << CompileTask(Task::Warning, << (Tasks() << CompileTask(Task::Warning,
"#1234: Some warning\n" "#1234: Some warning\n"
" int f;\n" " int f;\n"
@@ -356,7 +339,7 @@ void BareMetalPlugin::testKeilOutputParsers_data()
<< QString::fromLatin1("\"c:\\foo\\main.c\", line 63: Error: #1234: Some error") << QString::fromLatin1("\"c:\\foo\\main.c\", line 63: Error: #1234: Some error")
<< OutputParserTester::STDERR << OutputParserTester::STDERR
<< QString() << QString()
<< QString::fromLatin1("\"c:\\foo\\main.c\", line 63: Error: #1234: Some error\n") << QString()
<< (Tasks() << CompileTask(Task::Error, << (Tasks() << CompileTask(Task::Error,
"#1234: Some error", "#1234: Some error",
FilePath::fromUserInput("c:\\foo\\main.c"), 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") << QString::fromLatin1("\"flash.sct\", line 51 (column 20): Error: L1234: Some error")
<< OutputParserTester::STDERR << OutputParserTester::STDERR
<< QString() << QString()
<< QString::fromLatin1("\"flash.sct\", line 51 (column 20): Error: L1234: Some error\n") << QString()
<< (Tasks() << CompileTask(Task::Error, << (Tasks() << CompileTask(Task::Error,
"L1234: Some error", "L1234: Some error",
FilePath::fromUserInput("flash.sct"), FilePath::fromUserInput("flash.sct"),
@@ -380,9 +363,7 @@ void BareMetalPlugin::testKeilOutputParsers_data()
" ^") " ^")
<< OutputParserTester::STDERR << OutputParserTester::STDERR
<< QString() << QString()
<< QString::fromLatin1("\"c:\\foo\\main.c\", line 63: Error: #1234: Some error\n" << QString()
" int f;\n"
" ^\n")
<< (Tasks() << CompileTask(Task::Error, << (Tasks() << CompileTask(Task::Error,
"#1234: Some error\n" "#1234: Some error\n"
" int f;\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") << QString::fromLatin1("\"c:\\foo\\main.c\", line 71: Error: At end of source: #40: Some error")
<< OutputParserTester::STDERR << OutputParserTester::STDERR
<< QString() << QString()
<< QString::fromLatin1("\"c:\\foo\\main.c\", line 71: Error: At end of source: #40: Some error\n") << QString()
<< (Tasks() << CompileTask(Task::Error, << (Tasks() << CompileTask(Task::Error,
"#40: Some error", "#40: Some error",
FilePath::fromUserInput("c:\\foo\\main.c"), FilePath::fromUserInput("c:\\foo\\main.c"),
@@ -406,7 +387,7 @@ void BareMetalPlugin::testKeilOutputParsers_data()
<< QString::fromLatin1("Error: L6226E: Some error.") << QString::fromLatin1("Error: L6226E: Some error.")
<< OutputParserTester::STDERR << OutputParserTester::STDERR
<< QString() << QString()
<< QString::fromLatin1("Error: L6226E: Some error.\n") << QString()
<< (Tasks() << CompileTask(Task::Error, << (Tasks() << CompileTask(Task::Error,
"L6226E: Some error.")) "L6226E: Some error."))
<< QString(); << QString();
@@ -417,7 +398,7 @@ void BareMetalPlugin::testKeilOutputParsers_data()
QTest::newRow("MCS51: Assembler simple warning") QTest::newRow("MCS51: Assembler simple warning")
<< QString::fromLatin1("*** WARNING #A9 IN 15 (c:\\foo\\dscr.a51, LINE 15): Some warning") << QString::fromLatin1("*** WARNING #A9 IN 15 (c:\\foo\\dscr.a51, LINE 15): Some warning")
<< OutputParserTester::STDOUT << OutputParserTester::STDOUT
<< QString::fromLatin1("*** WARNING #A9 IN 15 (c:\\foo\\dscr.a51, LINE 15): Some warning\n") << QString()
<< QString() << QString()
<< (Tasks() << CompileTask(Task::Warning, << (Tasks() << CompileTask(Task::Warning,
"#A9: Some warning", "#A9: Some warning",
@@ -428,7 +409,7 @@ void BareMetalPlugin::testKeilOutputParsers_data()
QTest::newRow("MCS51: Assembler simple error") QTest::newRow("MCS51: Assembler simple error")
<< QString::fromLatin1("*** ERROR #A9 IN 15 (c:\\foo\\dscr.a51, LINE 15): Some error") << QString::fromLatin1("*** ERROR #A9 IN 15 (c:\\foo\\dscr.a51, LINE 15): Some error")
<< OutputParserTester::STDOUT << OutputParserTester::STDOUT
<< QString::fromLatin1("*** ERROR #A9 IN 15 (c:\\foo\\dscr.a51, LINE 15): Some error\n") << QString()
<< QString() << QString()
<< (Tasks() << CompileTask(Task::Error, << (Tasks() << CompileTask(Task::Error,
"#A9: Some error", "#A9: Some error",
@@ -441,9 +422,7 @@ void BareMetalPlugin::testKeilOutputParsers_data()
" Some detail 1\n" " Some detail 1\n"
" Some detail N") " Some detail N")
<< OutputParserTester::STDOUT << OutputParserTester::STDOUT
<< QString::fromLatin1("A51 FATAL ERROR -\n" << QString()
" Some detail 1\n"
" Some detail N\n")
<< QString() << QString()
<< (Tasks() << CompileTask(Task::Error, << (Tasks() << CompileTask(Task::Error,
"Assembler fatal error\n" "Assembler fatal error\n"
@@ -456,9 +435,7 @@ void BareMetalPlugin::testKeilOutputParsers_data()
"*** ___^\n" "*** ___^\n"
"*** ERROR #A45 IN 28 (d:\\foo.a51, LINE 28): Some error") "*** ERROR #A45 IN 28 (d:\\foo.a51, LINE 28): Some error")
<< OutputParserTester::STDOUT << OutputParserTester::STDOUT
<< QString::fromLatin1("01AF Some detail\n" << QString()
"*** ___^\n"
"*** ERROR #A45 IN 28 (d:\\foo.a51, LINE 28): Some error\n")
<< QString() << QString()
<< (Tasks() << CompileTask(Task::Error, << (Tasks() << CompileTask(Task::Error,
"#A45: Some error\n" "#A45: Some error\n"
@@ -472,7 +449,7 @@ void BareMetalPlugin::testKeilOutputParsers_data()
QTest::newRow("MCS51: Compiler simple warning") QTest::newRow("MCS51: Compiler simple warning")
<< QString::fromLatin1("*** WARNING C123 IN LINE 13 OF c:\\foo.c: Some warning") << QString::fromLatin1("*** WARNING C123 IN LINE 13 OF c:\\foo.c: Some warning")
<< OutputParserTester::STDOUT << OutputParserTester::STDOUT
<< QString::fromLatin1("*** WARNING C123 IN LINE 13 OF c:\\foo.c: Some warning\n") << QString()
<< QString() << QString()
<< (Tasks() << CompileTask(Task::Warning, << (Tasks() << CompileTask(Task::Warning,
"C123: Some warning", "C123: Some warning",
@@ -483,7 +460,7 @@ void BareMetalPlugin::testKeilOutputParsers_data()
QTest::newRow("MCS51: Compiler extended warning") QTest::newRow("MCS51: Compiler extended warning")
<< QString::fromLatin1("*** WARNING C123 IN LINE 13 OF c:\\foo.c: Some warning : 'extended text'") << QString::fromLatin1("*** WARNING C123 IN LINE 13 OF c:\\foo.c: Some warning : 'extended text'")
<< OutputParserTester::STDOUT << OutputParserTester::STDOUT
<< QString::fromLatin1("*** WARNING C123 IN LINE 13 OF c:\\foo.c: Some warning : 'extended text'\n") << QString()
<< QString() << QString()
<< (Tasks() << CompileTask(Task::Warning, << (Tasks() << CompileTask(Task::Warning,
"C123: Some warning : 'extended text'", "C123: Some warning : 'extended text'",
@@ -494,7 +471,7 @@ void BareMetalPlugin::testKeilOutputParsers_data()
QTest::newRow("MCS51: Compiler simple error") QTest::newRow("MCS51: Compiler simple error")
<< QString::fromLatin1("*** ERROR C123 IN LINE 13 OF c:\\foo.c: Some error") << QString::fromLatin1("*** ERROR C123 IN LINE 13 OF c:\\foo.c: Some error")
<< OutputParserTester::STDOUT << OutputParserTester::STDOUT
<< QString::fromLatin1("*** ERROR C123 IN LINE 13 OF c:\\foo.c: Some error\n") << QString()
<< QString() << QString()
<< (Tasks() << CompileTask(Task::Error, << (Tasks() << CompileTask(Task::Error,
"C123: Some error", "C123: Some error",
@@ -505,7 +482,7 @@ void BareMetalPlugin::testKeilOutputParsers_data()
QTest::newRow("MCS51: Compiler extended error") QTest::newRow("MCS51: Compiler extended error")
<< QString::fromLatin1("*** ERROR C123 IN LINE 13 OF c:\\foo.c: Some error : 'extended text'") << QString::fromLatin1("*** ERROR C123 IN LINE 13 OF c:\\foo.c: Some error : 'extended text'")
<< OutputParserTester::STDOUT << OutputParserTester::STDOUT
<< QString::fromLatin1("*** ERROR C123 IN LINE 13 OF c:\\foo.c: Some error : 'extended text'\n") << QString()
<< QString() << QString()
<< (Tasks() << CompileTask(Task::Error, << (Tasks() << CompileTask(Task::Error,
"C123: Some error : 'extended text'", "C123: Some error : 'extended text'",
@@ -518,9 +495,7 @@ void BareMetalPlugin::testKeilOutputParsers_data()
" Some detail 1\n" " Some detail 1\n"
" Some detail N") " Some detail N")
<< OutputParserTester::STDOUT << OutputParserTester::STDOUT
<< QString::fromLatin1("C51 FATAL-ERROR -\n" << QString()
" Some detail 1\n"
" Some detail N\n")
<< QString() << QString()
<< (Tasks() << CompileTask(Task::Error, << (Tasks() << CompileTask(Task::Error,
"Compiler fatal error\n" "Compiler fatal error\n"
@@ -533,8 +508,7 @@ void BareMetalPlugin::testKeilOutputParsers_data()
<< QString::fromLatin1("*** WARNING L16: Some warning\n" << QString::fromLatin1("*** WARNING L16: Some warning\n"
" Some detail 1") " Some detail 1")
<< OutputParserTester::STDOUT << OutputParserTester::STDOUT
<< QString::fromLatin1("*** WARNING L16: Some warning\n" << QString()
" Some detail 1\n")
<< QString() << QString()
<< (Tasks() << CompileTask(Task::Warning, << (Tasks() << CompileTask(Task::Warning,
"L16: Some warning\n" "L16: Some warning\n"
@@ -544,7 +518,7 @@ void BareMetalPlugin::testKeilOutputParsers_data()
QTest::newRow("MCS51: Linker simple fatal error") QTest::newRow("MCS51: Linker simple fatal error")
<< QString::fromLatin1("*** FATAL ERROR L456: Some error") << QString::fromLatin1("*** FATAL ERROR L456: Some error")
<< OutputParserTester::STDOUT << OutputParserTester::STDOUT
<< QString::fromLatin1("*** FATAL ERROR L456: Some error\n") << QString()
<< QString() << QString()
<< (Tasks() << CompileTask(Task::Error, << (Tasks() << CompileTask(Task::Error,
"L456: Some error")) "L456: Some error"))
@@ -555,9 +529,7 @@ void BareMetalPlugin::testKeilOutputParsers_data()
" Some detail 1\n" " Some detail 1\n"
" Some detail N") " Some detail N")
<< OutputParserTester::STDOUT << OutputParserTester::STDOUT
<< QString::fromLatin1("*** FATAL ERROR L456: Some error\n" << QString()
" Some detail 1\n"
" Some detail N\n")
<< QString() << QString()
<< (Tasks() << CompileTask(Task::Error, << (Tasks() << CompileTask(Task::Error,
"L456: Some error\n" "L456: Some error\n"
@@ -569,7 +541,7 @@ void BareMetalPlugin::testKeilOutputParsers_data()
void BareMetalPlugin::testKeilOutputParsers() void BareMetalPlugin::testKeilOutputParsers()
{ {
OutputParserTester testbench; OutputParserTester testbench;
testbench.appendOutputParser(new KeilParser); testbench.addLineParser(new KeilParser);
QFETCH(QString, input); QFETCH(QString, input);
QFETCH(OutputParserTester::Channel, inputChannel); QFETCH(OutputParserTester::Channel, inputChannel);
QFETCH(Tasks, tasks); QFETCH(Tasks, tasks);

View File

@@ -55,9 +55,7 @@ private:
bool parseMcs51WarningOrFatalErrorMessage(const QString &lne); bool parseMcs51WarningOrFatalErrorMessage(const QString &lne);
bool parseMcs51FatalErrorMessage2(const QString &lne); bool parseMcs51FatalErrorMessage2(const QString &lne);
void handleLine(const QString &line, Utils::OutputFormat type) final; Status doHandleLine(const QString &line, Utils::OutputFormat type) final;
void stdError(const QString &line);
void stdOutput(const QString &line);
void doFlush() final; void doFlush() final;
ProjectExplorer::Task m_lastTask; ProjectExplorer::Task m_lastTask;

View File

@@ -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 QVariantMap KeilToolChain::toMap() const

View File

@@ -69,7 +69,7 @@ public:
const Utils::FilePath &, const Utils::FilePath &,
const Utils::Environment &env) const final; const Utils::Environment &env) const final;
void addToEnvironment(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; QVariantMap toMap() const final;
bool fromMap(const QVariantMap &data) final; bool fromMap(const QVariantMap &data) final;

View File

@@ -87,17 +87,10 @@ void SdccParser::amendDescription(const QString &desc)
++m_lines; ++m_lines;
} }
void SdccParser::handleLine(const QString &line, OutputFormat type) IOutputParser::Status SdccParser::doHandleLine(const QString &line, OutputFormat type)
{ {
if (type == StdOutFormat) if (type == StdOutFormat)
stdOutput(line); return Status::NotHandled;
else
stdError(line);
}
void SdccParser::stdError(const QString &line)
{
IOutputParser::handleLine(line, StdErrFormat);
const QString lne = rightTrimmed(line); const QString lne = rightTrimmed(line);
@@ -115,7 +108,7 @@ void SdccParser::stdError(const QString &line)
const Task::TaskType type = taskType(match.captured(MessageTypeIndex)); const Task::TaskType type = taskType(match.captured(MessageTypeIndex));
const QString descr = match.captured(MessageTextIndex); const QString descr = match.captured(MessageTextIndex);
newTask(CompileTask(type, descr, absoluteFilePath(fileName), lineno)); newTask(CompileTask(type, descr, absoluteFilePath(fileName), lineno));
return; return Status::InProgress;
} }
re.setPattern("^(.+\\.\\S+):(\\d+): (Error|error|syntax error): (.+)$"); 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 Task::TaskType type = taskType(match.captured(MessageTypeIndex));
const QString descr = match.captured(MessageTextIndex); const QString descr = match.captured(MessageTextIndex);
newTask(CompileTask(type, descr, absoluteFilePath(fileName), lineno)); newTask(CompileTask(type, descr, absoluteFilePath(fileName), lineno));
return; return Status::InProgress;
} }
re.setPattern("^at (\\d+): (warning|error) \\d+: (.+)$"); 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 Task::TaskType type = taskType(match.captured(MessageTypeIndex));
const QString descr = match.captured(MessageTextIndex); const QString descr = match.captured(MessageTextIndex);
newTask(CompileTask(type, descr)); newTask(CompileTask(type, descr));
return; return Status::InProgress;
} }
re.setPattern("^\\?ASlink-(Warning|Error)-(.+)$"); re.setPattern("^\\?ASlink-(Warning|Error)-(.+)$");
@@ -149,20 +142,16 @@ void SdccParser::stdError(const QString &line)
const Task::TaskType type = taskType(match.captured(MessageTypeIndex)); const Task::TaskType type = taskType(match.captured(MessageTypeIndex));
const QString descr = match.captured(MessageTextIndex); const QString descr = match.captured(MessageTextIndex);
newTask(CompileTask(type, descr)); newTask(CompileTask(type, descr));
return; return Status::InProgress;
} }
if (!m_lastTask.isNull()) { if (!m_lastTask.isNull()) {
amendDescription(lne); amendDescription(lne);
return; return Status::InProgress;
} }
doFlush(); doFlush();
} return Status::NotHandled;
void SdccParser::stdOutput(const QString &line)
{
IOutputParser::handleLine(line, StdOutFormat);
} }
void SdccParser::doFlush() void SdccParser::doFlush()
@@ -215,7 +204,7 @@ void BareMetalPlugin::testSdccOutputParsers_data()
<< QString::fromLatin1("c:\\foo\\main.c:63: Error: Some error") << QString::fromLatin1("c:\\foo\\main.c:63: Error: Some error")
<< OutputParserTester::STDERR << OutputParserTester::STDERR
<< QString() << QString()
<< QString::fromLatin1("c:\\foo\\main.c:63: Error: Some error\n") << QString()
<< (Tasks() << CompileTask(Task::Error, << (Tasks() << CompileTask(Task::Error,
"Some error", "Some error",
FilePath::fromUserInput("c:\\foo\\main.c"), 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") << QString::fromLatin1("c:\\foo\\main.c:63: warning 123: Some warning")
<< OutputParserTester::STDERR << OutputParserTester::STDERR
<< QString() << QString()
<< QString::fromLatin1("c:\\foo\\main.c:63: warning 123: Some warning\n") << QString()
<< (Tasks() << CompileTask(Task::Warning, << (Tasks() << CompileTask(Task::Warning,
"Some warning", "Some warning",
FilePath::fromUserInput("c:\\foo\\main.c"), FilePath::fromUserInput("c:\\foo\\main.c"),
@@ -239,9 +228,7 @@ void BareMetalPlugin::testSdccOutputParsers_data()
" details #2") " details #2")
<< OutputParserTester::STDERR << OutputParserTester::STDERR
<< QString() << QString()
<< QString::fromLatin1("c:\\foo\\main.c:63: warning 123: Some warning\n" << QString()
"details #1\n"
" details #2\n")
<< (Tasks() << CompileTask(Task::Warning, << (Tasks() << CompileTask(Task::Warning,
"Some warning\n" "Some warning\n"
"details #1\n" "details #1\n"
@@ -254,7 +241,7 @@ void BareMetalPlugin::testSdccOutputParsers_data()
<< QString::fromLatin1("c:\\foo\\main.c:63: error: Some error") << QString::fromLatin1("c:\\foo\\main.c:63: error: Some error")
<< OutputParserTester::STDERR << OutputParserTester::STDERR
<< QString() << QString()
<< QString::fromLatin1("c:\\foo\\main.c:63: error: Some error\n") << QString()
<< (Tasks() << CompileTask(Task::Error, << (Tasks() << CompileTask(Task::Error,
"Some error", "Some error",
FilePath::fromUserInput("c:\\foo\\main.c"), 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") << QString::fromLatin1("c:\\foo\\main.c:63: error 123: Some error")
<< OutputParserTester::STDERR << OutputParserTester::STDERR
<< QString() << QString()
<< QString::fromLatin1("c:\\foo\\main.c:63: error 123: Some error\n") << QString()
<< (Tasks() << CompileTask(Task::Error, << (Tasks() << CompileTask(Task::Error,
"Some error", "Some error",
FilePath::fromUserInput("c:\\foo\\main.c"), FilePath::fromUserInput("c:\\foo\\main.c"),
@@ -278,9 +265,7 @@ void BareMetalPlugin::testSdccOutputParsers_data()
" details #2") " details #2")
<< OutputParserTester::STDERR << OutputParserTester::STDERR
<< QString() << QString()
<< QString::fromLatin1("c:\\foo\\main.c:63: error 123: Some error\n" << QString()
"details #1\n"
" details #2\n")
<< (Tasks() << CompileTask(Task::Error, << (Tasks() << CompileTask(Task::Error,
"Some error\n" "Some error\n"
"details #1\n" "details #1\n"
@@ -293,7 +278,7 @@ void BareMetalPlugin::testSdccOutputParsers_data()
<< QString::fromLatin1("c:\\foo\\main.c:63: syntax error: Some error") << QString::fromLatin1("c:\\foo\\main.c:63: syntax error: Some error")
<< OutputParserTester::STDERR << OutputParserTester::STDERR
<< QString() << QString()
<< QString::fromLatin1("c:\\foo\\main.c:63: syntax error: Some error\n") << QString()
<< (Tasks() << CompileTask(Task::Error, << (Tasks() << CompileTask(Task::Error,
"Some error", "Some error",
FilePath::fromUserInput("c:\\foo\\main.c"), FilePath::fromUserInput("c:\\foo\\main.c"),
@@ -304,7 +289,7 @@ void BareMetalPlugin::testSdccOutputParsers_data()
<< QString::fromLatin1("at 1: error 123: Some error") << QString::fromLatin1("at 1: error 123: Some error")
<< OutputParserTester::STDERR << OutputParserTester::STDERR
<< QString() << QString()
<< QString::fromLatin1("at 1: error 123: Some error\n") << QString()
<< (Tasks() << CompileTask(Task::Error, << (Tasks() << CompileTask(Task::Error,
"Some error")) "Some error"))
<< QString(); << QString();
@@ -313,7 +298,7 @@ void BareMetalPlugin::testSdccOutputParsers_data()
<< QString::fromLatin1("at 1: warning 123: Some warning") << QString::fromLatin1("at 1: warning 123: Some warning")
<< OutputParserTester::STDERR << OutputParserTester::STDERR
<< QString() << QString()
<< QString::fromLatin1("at 1: warning 123: Some warning\n") << QString()
<< (Tasks() << CompileTask(Task::Warning, << (Tasks() << CompileTask(Task::Warning,
"Some warning")) "Some warning"))
<< QString(); << QString();
@@ -322,7 +307,7 @@ void BareMetalPlugin::testSdccOutputParsers_data()
<< QString::fromLatin1("?ASlink-Warning-Couldn't find library 'foo.lib'") << QString::fromLatin1("?ASlink-Warning-Couldn't find library 'foo.lib'")
<< OutputParserTester::STDERR << OutputParserTester::STDERR
<< QString() << QString()
<< QString::fromLatin1("?ASlink-Warning-Couldn't find library 'foo.lib'\n") << QString()
<< (Tasks() << CompileTask(Task::Warning, << (Tasks() << CompileTask(Task::Warning,
"Couldn't find library 'foo.lib'")) "Couldn't find library 'foo.lib'"))
<< QString(); << QString();
@@ -331,7 +316,7 @@ void BareMetalPlugin::testSdccOutputParsers_data()
<< QString::fromLatin1("?ASlink-Error-<cannot open> : \"foo.rel\"") << QString::fromLatin1("?ASlink-Error-<cannot open> : \"foo.rel\"")
<< OutputParserTester::STDERR << OutputParserTester::STDERR
<< QString() << QString()
<< QString::fromLatin1("?ASlink-Error-<cannot open> : \"foo.rel\"\n") << QString()
<< (Tasks() << CompileTask(Task::Error, << (Tasks() << CompileTask(Task::Error,
"<cannot open> : \"foo.rel\"")) "<cannot open> : \"foo.rel\""))
<< QString(); << QString();
@@ -340,7 +325,7 @@ void BareMetalPlugin::testSdccOutputParsers_data()
void BareMetalPlugin::testSdccOutputParsers() void BareMetalPlugin::testSdccOutputParsers()
{ {
OutputParserTester testbench; OutputParserTester testbench;
testbench.appendOutputParser(new SdccParser); testbench.addLineParser(new SdccParser);
QFETCH(QString, input); QFETCH(QString, input);
QFETCH(OutputParserTester::Channel, inputChannel); QFETCH(OutputParserTester::Channel, inputChannel);
QFETCH(Tasks, tasks); QFETCH(Tasks, tasks);

View File

@@ -45,9 +45,7 @@ private:
void newTask(const ProjectExplorer::Task &task); void newTask(const ProjectExplorer::Task &task);
void amendDescription(const QString &desc); void amendDescription(const QString &desc);
void handleLine(const QString &line, Utils::OutputFormat type) final; Status doHandleLine(const QString &line, Utils::OutputFormat type) final;
void stdError(const QString &line);
void stdOutput(const QString &line);
void doFlush() final; void doFlush() final;
ProjectExplorer::Task m_lastTask; ProjectExplorer::Task m_lastTask;

View File

@@ -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 QVariantMap SdccToolChain::toMap() const

View File

@@ -69,7 +69,7 @@ public:
const Utils::FilePath &, const Utils::FilePath &,
const Utils::Environment &env) const final; const Utils::Environment &env) const final;
void addToEnvironment(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; QVariantMap toMap() const final;
bool fromMap(const QVariantMap &data) final; bool fromMap(const QVariantMap &data) final;

View File

@@ -214,9 +214,7 @@ bool CMakeBuildStep::init()
cmakeParser->setSourceDirectory(projectDirectory.toString()); cmakeParser->setSourceDirectory(projectDirectory.toString());
setOutputParser(cmakeParser); setOutputParser(cmakeParser);
appendOutputParser(new GnuMakeParser); appendOutputParser(new GnuMakeParser);
IOutputParser *parser = target()->kit()->createOutputParser(); appendOutputParsers(target()->kit()->createOutputParsers());
if (parser)
appendOutputParser(parser);
outputParser()->addSearchDir(pp->effectiveWorkingDirectory()); outputParser()->addSearchDir(pp->effectiveWorkingDirectory());
return AbstractProcessStep::init(); return AbstractProcessStep::init();

View File

@@ -57,22 +57,21 @@ void CMakeParser::setSourceDirectory(const QString &sourceDir)
m_sourceDirectory = QDir(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) { if (type != StdErrFormat)
IOutputParser::handleLine(line, type); return Status::NotHandled;
return;
}
QString trimmedLine = rightTrimmed(line); QString trimmedLine = rightTrimmed(line);
switch (m_expectTripleLineErrorData) { switch (m_expectTripleLineErrorData) {
case NONE: case NONE:
if (trimmedLine.isEmpty() && !m_lastTask.isNull()) { if (trimmedLine.isEmpty() && !m_lastTask.isNull()) {
if (m_skippedFirstEmptyLine) if (m_skippedFirstEmptyLine) {
doFlush(); doFlush();
else return Status::InProgress;
}
m_skippedFirstEmptyLine = true; m_skippedFirstEmptyLine = true;
return; return Status::InProgress;
} }
if (m_skippedFirstEmptyLine) if (m_skippedFirstEmptyLine)
m_skippedFirstEmptyLine = false; m_skippedFirstEmptyLine = false;
@@ -87,35 +86,34 @@ void CMakeParser::handleLine(const QString &line, OutputFormat type)
absoluteFilePath(FilePath::fromUserInput(path)), absoluteFilePath(FilePath::fromUserInput(path)),
m_commonError.cap(2).toInt()); m_commonError.cap(2).toInt());
m_lines = 1; m_lines = 1;
return; return Status::InProgress;
} else if (m_nextSubError.indexIn(trimmedLine) != -1) { } else if (m_nextSubError.indexIn(trimmedLine) != -1) {
m_lastTask = BuildSystemTask(Task::Error, QString(), m_lastTask = BuildSystemTask(Task::Error, QString(),
absoluteFilePath(FilePath::fromUserInput(m_nextSubError.cap(1)))); absoluteFilePath(FilePath::fromUserInput(m_nextSubError.cap(1))));
m_lines = 1; m_lines = 1;
return; return Status::InProgress;
} else if (trimmedLine.startsWith(QLatin1String(" ")) && !m_lastTask.isNull()) { } else if (trimmedLine.startsWith(QLatin1String(" ")) && !m_lastTask.isNull()) {
if (!m_lastTask.description.isEmpty()) if (!m_lastTask.description.isEmpty())
m_lastTask.description.append(QLatin1Char(' ')); m_lastTask.description.append(QLatin1Char(' '));
m_lastTask.description.append(trimmedLine.trimmed()); m_lastTask.description.append(trimmedLine.trimmed());
++m_lines; ++m_lines;
return; return Status::InProgress;
} else if (trimmedLine.endsWith(QLatin1String("in cmake code at"))) { } else if (trimmedLine.endsWith(QLatin1String("in cmake code at"))) {
m_expectTripleLineErrorData = LINE_LOCATION; m_expectTripleLineErrorData = LINE_LOCATION;
doFlush(); doFlush();
const Task::TaskType type = const Task::TaskType type =
trimmedLine.contains(QLatin1String("Error")) ? Task::Error : Task::Warning; trimmedLine.contains(QLatin1String("Error")) ? Task::Error : Task::Warning;
m_lastTask = BuildSystemTask(type, QString()); m_lastTask = BuildSystemTask(type, QString());
return; return Status::InProgress;
} else if (trimmedLine.startsWith("CMake Error: ")) { } else if (trimmedLine.startsWith("CMake Error: ")) {
m_lastTask = BuildSystemTask(Task::Error, trimmedLine.mid(13)); m_lastTask = BuildSystemTask(Task::Error, trimmedLine.mid(13));
m_lines = 1; m_lines = 1;
return; return Status::InProgress;
} else if (trimmedLine.startsWith("-- ") || trimmedLine.startsWith(" * ")) { } else if (trimmedLine.startsWith("-- ") || trimmedLine.startsWith(" * ")) {
// Do not pass on lines starting with "-- " or "* ". Those are typical CMake output // Do not pass on lines starting with "-- " or "* ". Those are typical CMake output
return; return Status::InProgress;
} }
IOutputParser::handleLine(line, StdErrFormat); return Status::NotHandled;
return;
case LINE_LOCATION: case LINE_LOCATION:
{ {
QRegularExpressionMatch m = m_locationLine.match(trimmedLine); 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_lastTask.line = m.captured(1).toInt();
m_expectTripleLineErrorData = LINE_DESCRIPTION; m_expectTripleLineErrorData = LINE_DESCRIPTION;
} }
return; return Status::InProgress;
case LINE_DESCRIPTION: case LINE_DESCRIPTION:
m_lastTask.description = trimmedLine; m_lastTask.description = trimmedLine;
if (trimmedLine.endsWith(QLatin1Char('\"'))) if (trimmedLine.endsWith(QLatin1Char('\"')))
@@ -132,14 +130,15 @@ void CMakeParser::handleLine(const QString &line, OutputFormat type)
else { else {
m_expectTripleLineErrorData = NONE; m_expectTripleLineErrorData = NONE;
doFlush(); doFlush();
return Status::Done;
} }
return; return Status::InProgress;
case LINE_DESCRIPTION2: case LINE_DESCRIPTION2:
m_lastTask.description.append(QLatin1Char('\n')); m_lastTask.description.append(QLatin1Char('\n'));
m_lastTask.description.append(trimmedLine); m_lastTask.description.append(trimmedLine);
m_expectTripleLineErrorData = NONE; m_expectTripleLineErrorData = NONE;
doFlush(); doFlush();
return; return Status::Done;
} }
} }
@@ -301,7 +300,7 @@ void Internal::CMakeProjectPlugin::testCMakeParser_data()
void Internal::CMakeProjectPlugin::testCMakeParser() void Internal::CMakeProjectPlugin::testCMakeParser()
{ {
OutputParserTester testbench; OutputParserTester testbench;
testbench.appendOutputParser(new CMakeParser); testbench.addLineParser(new CMakeParser);
QFETCH(QString, input); QFETCH(QString, input);
QFETCH(OutputParserTester::Channel, inputChannel); QFETCH(OutputParserTester::Channel, inputChannel);
QFETCH(Tasks, tasks); QFETCH(Tasks, tasks);

View File

@@ -45,7 +45,7 @@ public:
void setSourceDirectory(const QString &sourceDir); void setSourceDirectory(const QString &sourceDir);
private: private:
void handleLine(const QString &line, Utils::OutputFormat type) override; Status doHandleLine(const QString &line, Utils::OutputFormat type) override;
void doFlush() override; void doFlush() override;
enum TripleLineError { NONE, LINE_LOCATION, LINE_DESCRIPTION, LINE_DESCRIPTION2 }; enum TripleLineError { NONE, LINE_LOCATION, LINE_DESCRIPTION, LINE_DESCRIPTION2 };

View File

@@ -223,9 +223,7 @@ bool IosBuildStep::init()
setIgnoreReturnValue(m_clean); setIgnoreReturnValue(m_clean);
setOutputParser(new GnuMakeParser()); setOutputParser(new GnuMakeParser());
IOutputParser *parser = target()->kit()->createOutputParser(); appendOutputParsers(target()->kit()->createOutputParsers());
if (parser)
appendOutputParser(parser);
outputParser()->addSearchDir(pp->effectiveWorkingDirectory()); outputParser()->addSearchDir(pp->effectiveWorkingDirectory());
return AbstractProcessStep::init(); return AbstractProcessStep::init();

View File

@@ -80,8 +80,7 @@ bool IosDsymBuildStep::init()
// That is mostly so that rebuild works on an already clean project // That is mostly so that rebuild works on an already clean project
setIgnoreReturnValue(m_clean); setIgnoreReturnValue(m_clean);
setOutputParser(target()->kit()->createOutputParser()); appendOutputParsers(target()->kit()->createOutputParsers());
if (outputParser())
outputParser()->addSearchDir(pp->effectiveWorkingDirectory()); outputParser()->addSearchDir(pp->effectiveWorkingDirectory());
return AbstractProcessStep::init(); return AbstractProcessStep::init();

View File

@@ -45,15 +45,9 @@ namespace {
class NimParser : public IOutputParser class NimParser : public IOutputParser
{ {
private: Status doHandleLine(const QString &lne, Utils::OutputFormat) override
void handleLine(const QString &line, Utils::OutputFormat type) override
{
parseLine(line.trimmed());
IOutputParser::handleLine(line, type);
}
void parseLine(const QString &line)
{ {
const QString line = lne.trimmed();
static QRegularExpression regex("(.+.nim)\\((\\d+), (\\d+)\\) (.+)", static QRegularExpression regex("(.+.nim)\\((\\d+), (\\d+)\\) (.+)",
QRegularExpression::OptimizeOnFirstUsageOption); QRegularExpression::OptimizeOnFirstUsageOption);
static QRegularExpression warning("(Warning):(.*)", static QRegularExpression warning("(Warning):(.*)",
@@ -63,13 +57,13 @@ private:
QRegularExpressionMatch match = regex.match(line); QRegularExpressionMatch match = regex.match(line);
if (!match.hasMatch()) if (!match.hasMatch())
return; return Status::NotHandled;
const QString filename = match.captured(1); const QString filename = match.captured(1);
bool lineOk = false; bool lineOk = false;
const int lineNumber = match.captured(2).toInt(&lineOk); const int lineNumber = match.captured(2).toInt(&lineOk);
const QString message = match.captured(4); const QString message = match.captured(4);
if (!lineOk) if (!lineOk)
return; return Status::NotHandled;
Task::TaskType type = Task::Unknown; Task::TaskType type = Task::Unknown;
@@ -78,10 +72,11 @@ private:
else if (error.match(message).hasMatch()) else if (error.match(message).hasMatch())
type = Task::Error; type = Task::Error;
else else
return; return Status::NotHandled;
emit addTask(CompileTask(type, message, absoluteFilePath(FilePath::fromUserInput(filename)), emit addTask(CompileTask(type, message, absoluteFilePath(FilePath::fromUserInput(filename)),
lineNumber)); lineNumber));
return Status::Done;
} }
}; };

View File

@@ -47,14 +47,9 @@ namespace Nim {
class NimParser : public ProjectExplorer::IOutputParser class NimParser : public ProjectExplorer::IOutputParser
{ {
void handleLine(const QString &line, Utils::OutputFormat type) override Status doHandleLine(const QString &lne, Utils::OutputFormat) override
{
parseLine(line.trimmed());
IOutputParser::handleLine(line, type);
}
void parseLine(const QString &line)
{ {
const QString line = lne.trimmed();
static QRegularExpression regex("(.+.nim)\\((\\d+), (\\d+)\\) (.+)", static QRegularExpression regex("(.+.nim)\\((\\d+), (\\d+)\\) (.+)",
QRegularExpression::OptimizeOnFirstUsageOption); QRegularExpression::OptimizeOnFirstUsageOption);
static QRegularExpression warning("(Warning):(.*)", static QRegularExpression warning("(Warning):(.*)",
@@ -64,13 +59,13 @@ class NimParser : public ProjectExplorer::IOutputParser
QRegularExpressionMatch match = regex.match(line); QRegularExpressionMatch match = regex.match(line);
if (!match.hasMatch()) if (!match.hasMatch())
return; return Status::NotHandled;
const QString filename = match.captured(1); const QString filename = match.captured(1);
bool lineOk = false; bool lineOk = false;
const int lineNumber = match.captured(2).toInt(&lineOk); const int lineNumber = match.captured(2).toInt(&lineOk);
const QString message = match.captured(4); const QString message = match.captured(4);
if (!lineOk) if (!lineOk)
return; return Status::NotHandled;
Task::TaskType type = Task::Unknown; Task::TaskType type = Task::Unknown;
@@ -79,10 +74,11 @@ class NimParser : public ProjectExplorer::IOutputParser
else if (error.match(message).hasMatch()) else if (error.match(message).hasMatch())
type = Task::Error; type = Task::Error;
else else
return; return Status::NotHandled;
emit addTask(CompileTask(type, message, absoluteFilePath(FilePath::fromUserInput(filename)), emit addTask(CompileTask(type, message, absoluteFilePath(FilePath::fromUserInput(filename)),
lineNumber)); lineNumber));
return Status::Done;
} }
}; };
@@ -107,8 +103,7 @@ NimCompilerBuildStep::NimCompilerBuildStep(BuildStepList *parentList, Core::Id i
bool NimCompilerBuildStep::init() bool NimCompilerBuildStep::init()
{ {
setOutputParser(new NimParser()); setOutputParser(new NimParser());
if (IOutputParser *parser = target()->kit()->createOutputParser()) appendOutputParsers(target()->kit()->createOutputParsers());
appendOutputParser(parser);
outputParser()->addSearchDir(processParameters()->effectiveWorkingDirectory()); outputParser()->addSearchDir(processParameters()->effectiveWorkingDirectory());
return AbstractProcessStep::init(); return AbstractProcessStep::init();
} }
@@ -308,7 +303,7 @@ void NimPlugin::testNimParser_data()
QTest::newRow("Parse error string") QTest::newRow("Parse error string")
<< QString::fromLatin1("main.nim(23, 1) Error: undeclared identifier: 'x'") << QString::fromLatin1("main.nim(23, 1) Error: undeclared identifier: 'x'")
<< OutputParserTester::STDERR << OutputParserTester::STDERR
<< QString("") << QString("main.nim(23, 1) Error: undeclared identifier: 'x'\n") << QString() << QString()
<< Tasks({CompileTask(Task::Error, << Tasks({CompileTask(Task::Error,
"Error: undeclared identifier: 'x'", "Error: undeclared identifier: 'x'",
FilePath::fromUserInput("main.nim"), 23)}) FilePath::fromUserInput("main.nim"), 23)})
@@ -317,7 +312,7 @@ void NimPlugin::testNimParser_data()
QTest::newRow("Parse warning string") QTest::newRow("Parse warning string")
<< QString::fromLatin1("lib/pure/parseopt.nim(56, 34) Warning: quoteIfContainsWhite is deprecated [Deprecated]") << QString::fromLatin1("lib/pure/parseopt.nim(56, 34) Warning: quoteIfContainsWhite is deprecated [Deprecated]")
<< OutputParserTester::STDERR << OutputParserTester::STDERR
<< QString("") << QString("lib/pure/parseopt.nim(56, 34) Warning: quoteIfContainsWhite is deprecated [Deprecated]\n") << QString() << QString()
<< Tasks({CompileTask(Task::Warning, << Tasks({CompileTask(Task::Warning,
"Warning: quoteIfContainsWhite is deprecated [Deprecated]", "Warning: quoteIfContainsWhite is deprecated [Deprecated]",
FilePath::fromUserInput("lib/pure/parseopt.nim"), 56)}) FilePath::fromUserInput("lib/pure/parseopt.nim"), 56)})
@@ -327,7 +322,7 @@ void NimPlugin::testNimParser_data()
void NimPlugin::testNimParser() void NimPlugin::testNimParser()
{ {
OutputParserTester testbench; OutputParserTester testbench;
testbench.appendOutputParser(new NimParser); testbench.addLineParser(new NimParser);
QFETCH(QString, input); QFETCH(QString, input);
QFETCH(OutputParserTester::Channel, inputChannel); QFETCH(OutputParserTester::Channel, inputChannel);
QFETCH(Tasks, tasks); QFETCH(Tasks, tasks);

View File

@@ -120,9 +120,9 @@ void NimToolChain::setCompilerCommand(const FilePath &compilerCommand)
parseVersion(compilerCommand, m_version); parseVersion(compilerCommand, m_version);
} }
IOutputParser *NimToolChain::outputParser() const QList<IOutputParser *> NimToolChain::outputParsers() const
{ {
return nullptr; return {};
} }
std::unique_ptr<ProjectExplorer::ToolChainConfigWidget> NimToolChain::createConfigurationWidget() std::unique_ptr<ProjectExplorer::ToolChainConfigWidget> NimToolChain::createConfigurationWidget()

View File

@@ -56,7 +56,7 @@ public:
Utils::FilePath compilerCommand() const final; Utils::FilePath compilerCommand() const final;
QString compilerVersion() const; QString compilerVersion() const;
void setCompilerCommand(const Utils::FilePath &compilerCommand); void setCompilerCommand(const Utils::FilePath &compilerCommand);
ProjectExplorer::IOutputParser *outputParser() const final; QList<ProjectExplorer::IOutputParser *> outputParsers() const final;
std::unique_ptr<ProjectExplorer::ToolChainConfigWidget> createConfigurationWidget() final; std::unique_ptr<ProjectExplorer::ToolChainConfigWidget> createConfigurationWidget() final;
QVariantMap toMap() const final; QVariantMap toMap() const final;

View File

@@ -106,11 +106,10 @@ public:
AbstractProcessStep *q; AbstractProcessStep *q;
std::unique_ptr<Utils::QtcProcess> m_process; std::unique_ptr<Utils::QtcProcess> m_process;
std::unique_ptr<IOutputParser> m_outputParserChain; IOutputParser m_outputParser;
ProcessParameters m_param; ProcessParameters m_param;
Utils::FileInProjectFinder m_fileFinder; Utils::FileInProjectFinder m_fileFinder;
bool m_ignoreReturnValue = false; bool m_ignoreReturnValue = false;
bool m_skipFlush = false;
bool m_lowPriority = false; bool m_lowPriority = false;
std::unique_ptr<QTextDecoder> stdoutStream; std::unique_ptr<QTextDecoder> stdoutStream;
std::unique_ptr<QTextDecoder> stderrStream; std::unique_ptr<QTextDecoder> stderrStream;
@@ -120,6 +119,8 @@ AbstractProcessStep::AbstractProcessStep(BuildStepList *bsl, Core::Id id) :
BuildStep(bsl, id), BuildStep(bsl, id),
d(new Private(this)) d(new Private(this))
{ {
connect(&d->m_outputParser, &IOutputParser::addTask,
this, &AbstractProcessStep::taskAdded);
} }
AbstractProcessStep::~AbstractProcessStep() AbstractProcessStep::~AbstractProcessStep()
@@ -130,16 +131,10 @@ AbstractProcessStep::~AbstractProcessStep()
/*! /*!
Deletes all existing output parsers and starts a new chain with the Deletes all existing output parsers and starts a new chain with the
given parser. given parser.
Derived classes need to call this function.
*/ */
void AbstractProcessStep::setOutputParser(IOutputParser *parser) void AbstractProcessStep::setOutputParser(IOutputParser *parser)
{ {
parser->addFilter(&Internal::filterAnsiEscapeCodes); d->m_outputParser.setLineParsers({parser});
d->m_outputParserChain.reset(parser);
connect(d->m_outputParserChain.get(), &IOutputParser::addTask,
this, &AbstractProcessStep::taskAdded);
} }
/*! /*!
@@ -149,14 +144,18 @@ void AbstractProcessStep::appendOutputParser(IOutputParser *parser)
{ {
if (!parser) if (!parser)
return; return;
d->m_outputParser.addLineParser(parser);
}
QTC_ASSERT(d->m_outputParserChain, return); void AbstractProcessStep::appendOutputParsers(const QList<IOutputParser *> &parsers)
d->m_outputParserChain->appendOutputParser(parser); {
for (IOutputParser * const p : parsers)
appendOutputParser(p);
} }
IOutputParser *AbstractProcessStep::outputParser() const IOutputParser *AbstractProcessStep::outputParser() const
{ {
return d->m_outputParserChain.get(); return &d->m_outputParser;
} }
void AbstractProcessStep::emitFaultyConfigurationMessage() void AbstractProcessStep::emitFaultyConfigurationMessage()
@@ -189,6 +188,7 @@ void AbstractProcessStep::setIgnoreReturnValue(bool b)
bool AbstractProcessStep::init() bool AbstractProcessStep::init()
{ {
d->m_outputParser.addFilter(&Internal::filterAnsiEscapeCodes);
d->m_fileFinder.setProjectDirectory(project()->projectDirectory()); d->m_fileFinder.setProjectDirectory(project()->projectDirectory());
d->m_fileFinder.setProjectFiles(project()->files(Project::AllFiles)); d->m_fileFinder.setProjectFiles(project()->files(Project::AllFiles));
return !d->m_process; return !d->m_process;
@@ -244,7 +244,7 @@ void AbstractProcessStep::doRun()
if (!d->m_process->waitForStarted()) { if (!d->m_process->waitForStarted()) {
processStartupFailed(); processStartupFailed();
d->m_process.reset(); d->m_process.reset();
d->m_outputParserChain.reset(); d->m_outputParser.clear();
finish(false); finish(false);
return; return;
} }
@@ -272,7 +272,7 @@ void AbstractProcessStep::cleanUp(QProcess *process)
processFinished(process->exitCode(), process->exitStatus()); processFinished(process->exitCode(), process->exitStatus());
const bool returnValue = processSucceeded(process->exitCode(), process->exitStatus()) || d->m_ignoreReturnValue; const bool returnValue = processSucceeded(process->exitCode(), process->exitStatus()) || d->m_ignoreReturnValue;
d->m_outputParserChain.reset(); d->m_outputParser.clear();
d->m_process.reset(); d->m_process.reset();
// Report result // Report result
@@ -302,8 +302,8 @@ void AbstractProcessStep::processStarted()
void AbstractProcessStep::processFinished(int exitCode, QProcess::ExitStatus status) void AbstractProcessStep::processFinished(int exitCode, QProcess::ExitStatus status)
{ {
if (d->m_outputParserChain) d->m_outputParser.flush();
d->m_outputParserChain->flush(); d->m_outputParser.clear();
QString command = QDir::toNativeSeparators(d->m_param.effectiveCommand().toString()); QString command = QDir::toNativeSeparators(d->m_param.effectiveCommand().toString());
if (status == QProcess::NormalExit && exitCode == 0) { if (status == QProcess::NormalExit && exitCode == 0) {
@@ -338,7 +338,7 @@ void AbstractProcessStep::processStartupFailed()
bool AbstractProcessStep::processSucceeded(int exitCode, QProcess::ExitStatus status) bool AbstractProcessStep::processSucceeded(int exitCode, QProcess::ExitStatus status)
{ {
if (outputParser() && outputParser()->hasFatalErrors()) if (outputParser()->hasFatalErrors())
return false; return false;
return exitCode == 0 && status == QProcess::NormalExit; return exitCode == 0 && status == QProcess::NormalExit;
@@ -359,8 +359,7 @@ void AbstractProcessStep::processReadyReadStdOutput()
void AbstractProcessStep::stdOutput(const QString &output) void AbstractProcessStep::stdOutput(const QString &output)
{ {
if (d->m_outputParserChain) d->m_outputParser.handleStdout(output);
d->m_outputParserChain->handleStdout(output);
emit addOutput(output, BuildStep::OutputFormat::Stdout, BuildStep::DontAppendNewline); emit addOutput(output, BuildStep::OutputFormat::Stdout, BuildStep::DontAppendNewline);
} }
@@ -379,8 +378,7 @@ void AbstractProcessStep::processReadyReadStdError()
void AbstractProcessStep::stdError(const QString &output) void AbstractProcessStep::stdError(const QString &output)
{ {
if (d->m_outputParserChain) d->m_outputParser.handleStderr(output);
d->m_outputParserChain->handleStderr(output);
emit addOutput(output, BuildStep::OutputFormat::Stderr, BuildStep::DontAppendNewline); 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) if (d->m_ignoreReturnValue)
return; 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); Task editable(task);
QString filePath = task.file.toString(); QString filePath = task.file.toString();
if (!filePath.isEmpty() && !filePath.startsWith('<') && !QDir::isAbsolutePath(filePath)) { if (!filePath.isEmpty() && !filePath.startsWith('<') && !QDir::isAbsolutePath(filePath)) {

View File

@@ -48,6 +48,7 @@ public:
void setOutputParser(IOutputParser *parser); void setOutputParser(IOutputParser *parser);
void appendOutputParser(IOutputParser *parser); void appendOutputParser(IOutputParser *parser);
void appendOutputParsers(const QList<IOutputParser *> &parsers);
IOutputParser *outputParser() const; IOutputParser *outputParser() const;
void emitFaultyConfigurationMessage(); void emitFaultyConfigurationMessage();

View File

@@ -25,6 +25,7 @@
#include "clangparser.h" #include "clangparser.h"
#include "ldparser.h" #include "ldparser.h"
#include "lldparser.h"
#include "projectexplorerconstants.h" #include "projectexplorerconstants.h"
using namespace ProjectExplorer; using namespace ProjectExplorer;
@@ -54,25 +55,28 @@ ClangParser::ClangParser() :
setObjectName(QLatin1String("ClangParser")); setObjectName(QLatin1String("ClangParser"));
} }
void ClangParser::handleLine(const QString &line, OutputFormat type) QList<IOutputParser *> ClangParser::clangParserSuite()
{ {
if (type != StdErrFormat) { return {new ClangParser, new Internal::LldParser, new LdParser};
IOutputParser::handleLine(line, type);
return;
} }
IOutputParser::Status ClangParser::doHandleLine(const QString &line, OutputFormat type)
{
if (type != StdErrFormat)
return Status::NotHandled;
const QString lne = rightTrimmed(line); const QString lne = rightTrimmed(line);
QRegularExpressionMatch match = m_summaryRegExp.match(lne); QRegularExpressionMatch match = m_summaryRegExp.match(lne);
if (match.hasMatch()) { if (match.hasMatch()) {
doFlush(); doFlush();
m_expectSnippet = false; m_expectSnippet = false;
return; return Status::Done;
} }
match = m_commandRegExp.match(lne); match = m_commandRegExp.match(lne);
if (match.hasMatch()) { if (match.hasMatch()) {
m_expectSnippet = true; m_expectSnippet = true;
newTask(CompileTask(taskType(match.captured(3)), match.captured(4))); newTask(CompileTask(taskType(match.captured(3)), match.captured(4)));
return; return Status::InProgress;
} }
match = m_inLineRegExp.match(lne); match = m_inLineRegExp.match(lne);
@@ -82,7 +86,7 @@ void ClangParser::handleLine(const QString &line, OutputFormat type)
lne.trimmed(), lne.trimmed(),
absoluteFilePath(FilePath::fromUserInput(match.captured(2))), absoluteFilePath(FilePath::fromUserInput(match.captured(2))),
match.captured(3).toInt() /* line */)); match.captured(3).toInt() /* line */));
return; return Status::InProgress;
} }
match = m_messageRegExp.match(lne); match = m_messageRegExp.match(lne);
@@ -96,22 +100,22 @@ void ClangParser::handleLine(const QString &line, OutputFormat type)
match.captured(8), match.captured(8),
absoluteFilePath(FilePath::fromUserInput(match.captured(1))), absoluteFilePath(FilePath::fromUserInput(match.captured(1))),
lineNo)); lineNo));
return; return Status::InProgress;
} }
match = m_codesignRegExp.match(lne); match = m_codesignRegExp.match(lne);
if (match.hasMatch()) { if (match.hasMatch()) {
m_expectSnippet = true; m_expectSnippet = true;
newTask(CompileTask(Task::Error, match.captured(1))); newTask(CompileTask(Task::Error, match.captured(1)));
return; return Status::InProgress;
} }
if (m_expectSnippet) { if (m_expectSnippet) {
amendDescription(lne, true); amendDescription(lne, true);
return; return Status::InProgress;
} }
IOutputParser::handleLine(line, StdErrFormat); return Status::NotHandled;
} }
Core::Id ClangParser::id() Core::Id ClangParser::id()
@@ -259,7 +263,7 @@ void ProjectExplorerPlugin::testClangOutputParser_data()
void ProjectExplorerPlugin::testClangOutputParser() void ProjectExplorerPlugin::testClangOutputParser()
{ {
OutputParserTester testbench; OutputParserTester testbench;
testbench.appendOutputParser(new ClangParser); testbench.setLineParsers(ClangParser::clangParserSuite());
QFETCH(QString, input); QFETCH(QString, input);
QFETCH(OutputParserTester::Channel, inputChannel); QFETCH(OutputParserTester::Channel, inputChannel);
QFETCH(Tasks, tasks); QFETCH(Tasks, tasks);

View File

@@ -39,10 +39,12 @@ class PROJECTEXPLORER_EXPORT ClangParser : public ProjectExplorer::GccParser
public: public:
ClangParser(); ClangParser();
static QList<IOutputParser *> clangParserSuite();
static Core::Id id(); static Core::Id id();
private: private:
void handleLine(const QString &line, Utils::OutputFormat type) override; Status doHandleLine(const QString &line, Utils::OutputFormat type) override;
QRegularExpression m_commandRegExp; QRegularExpression m_commandRegExp;
QRegularExpression m_inLineRegExp; QRegularExpression m_inLineRegExp;

View File

@@ -129,14 +129,14 @@ Core::Id CustomParser::id()
return Core::Id("ProjectExplorer.OutputParser.Custom"); 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 const CustomParserExpression::CustomParserChannel channel = type == StdErrFormat
? CustomParserExpression::ParseStdErrChannel ? CustomParserExpression::ParseStdErrChannel
: CustomParserExpression::ParseStdOutChannel; : CustomParserExpression::ParseStdOutChannel;
if (parseLine(line, channel)) if (parseLine(line, channel))
return; return Status::Done;
IOutputParser::handleLine(line, type); return Status::NotHandled;
} }
bool CustomParser::hasMatch(const QString &line, CustomParserExpression::CustomParserChannel channel, bool CustomParser::hasMatch(const QString &line, CustomParserExpression::CustomParserChannel channel,
@@ -467,7 +467,7 @@ void ProjectExplorerPlugin::testCustomOutputParsers()
parser->skipFileExistsCheck(); parser->skipFileExistsCheck();
OutputParserTester testbench; OutputParserTester testbench;
testbench.appendOutputParser(parser); testbench.addLineParser(parser);
testbench.testParsing(input, inputChannel, testbench.testParsing(input, inputChannel,
tasks, childStdOutLines, childStdErrLines, tasks, childStdOutLines, childStdErrLines,
outputLines); outputLines);

View File

@@ -91,7 +91,7 @@ public:
static Core::Id id(); static Core::Id id();
private: 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, bool hasMatch(const QString &line, CustomParserExpression::CustomParserChannel channel,
const CustomParserExpression &expression, Task::TaskType taskType); const CustomParserExpression &expression, Task::TaskType taskType);

View File

@@ -196,19 +196,19 @@ QStringList CustomToolChain::suggestedMkspecList() const
return m_mkspecs; return m_mkspecs;
} }
IOutputParser *CustomToolChain::outputParser() const QList<IOutputParser *> CustomToolChain::outputParsers() const
{ {
if (m_outputParserId == GccParser::id()) if (m_outputParserId == GccParser::id())
return new GccParser; return GccParser::gccParserSuite();
if (m_outputParserId == ClangParser::id()) if (m_outputParserId == ClangParser::id())
return new ClangParser; return ClangParser::clangParserSuite();
if (m_outputParserId == LinuxIccParser::id()) if (m_outputParserId == LinuxIccParser::id())
return new LinuxIccParser; return LinuxIccParser::iccParserSuite();
if (m_outputParserId == MsvcParser::id()) if (m_outputParserId == MsvcParser::id())
return new MsvcParser; return {new MsvcParser};
if (m_outputParserId == CustomParser::id()) if (m_outputParserId == CustomParser::id())
return new CustomParser(m_customParserSettings); return {new CustomParser(m_customParserSettings)};
return nullptr; return {};
} }
QStringList CustomToolChain::headerPathsList() const QStringList CustomToolChain::headerPathsList() const

View File

@@ -84,7 +84,7 @@ public:
const Utils::Environment &env) const override; const Utils::Environment &env) const override;
void addToEnvironment(Utils::Environment &env) const override; void addToEnvironment(Utils::Environment &env) const override;
QStringList suggestedMkspecList() const override; QStringList suggestedMkspecList() const override;
IOutputParser *outputParser() const override; QList<IOutputParser *> outputParsers() const override;
QStringList headerPathsList() const; QStringList headerPathsList() const;
void setHeaderPaths(const QStringList &list); void setHeaderPaths(const QStringList &list);

View File

@@ -59,84 +59,6 @@ GccParser::GccParser()
// optional .exe postfix // optional .exe postfix
m_regExpGccNames.setPattern(QLatin1String(COMMAND_PATTERN)); m_regExpGccNames.setPattern(QLatin1String(COMMAND_PATTERN));
QTC_CHECK(m_regExpGccNames.isValid()); QTC_CHECK(m_regExpGccNames.isValid());
appendOutputParser(new Internal::LldParser);
appendOutputParser(new LdParser);
}
void GccParser::stdError(const QString &line)
{
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;
}
// Handle misc issues:
if (lne.startsWith(QLatin1String("ERROR:")) ||
lne == QLatin1String("* cpp failed")) {
newTask(CompileTask(Task::Error, lne /* description */));
return;
}
QRegularExpressionMatch match = m_regExpGccNames.match(lne);
if (match.hasMatch()) {
QString description = lne.mid(match.capturedLength());
Task::TaskType type = Task::Error;
if (description.startsWith(QLatin1String("warning: "))) {
type = Task::Warning;
description = description.mid(9);
} else if (description.startsWith(QLatin1String("fatal: "))) {
description = description.mid(7);
}
newTask(CompileTask(type, description));
return;
}
match = m_regExp.match(lne);
if (match.hasMatch()) {
Utils::FilePath filename = Utils::FilePath::fromUserInput(match.captured(1));
int lineno = match.captured(3).toInt();
Task::TaskType type = Task::Unknown;
QString description = match.captured(8);
if (match.captured(7) == QLatin1String("warning"))
type = Task::Warning;
else if (match.captured(7) == QLatin1String("error") ||
description.startsWith(QLatin1String("undefined reference to")) ||
description.startsWith(QLatin1String("multiple definition of")))
type = Task::Error;
// Prepend "#warning" or "#error" if that triggered the match on (warning|error)
// We want those to show how the warning was triggered
if (match.captured(5).startsWith(QLatin1Char('#')))
description = match.captured(5) + description;
newTask(CompileTask(type, description, absoluteFilePath(filename), lineno));
return;
}
match = m_regExpIncluded.match(lne);
if (match.hasMatch()) {
newTask(CompileTask(Task::Unknown,
lne.trimmed() /* description */,
absoluteFilePath(Utils::FilePath::fromUserInput(match.captured(1))),
match.captured(3).toInt() /* linenumber */));
return;
} else if (lne.startsWith(' ') && !m_currentTask.isNull()) {
amendDescription(lne, true);
return;
}
doFlush();
IOutputParser::handleLine(line, StdErrFormat);
}
void GccParser::stdOutput(const QString &line)
{
doFlush();
IOutputParser::handleLine(line, StdOutFormat);
} }
Core::Id GccParser::id() Core::Id GccParser::id()
@@ -144,6 +66,11 @@ Core::Id GccParser::id()
return Core::Id("ProjectExplorer.OutputParser.Gcc"); return Core::Id("ProjectExplorer.OutputParser.Gcc");
} }
QList<IOutputParser *> GccParser::gccParserSuite()
{
return {new GccParser, new Internal::LldParser, new LdParser};
}
void GccParser::newTask(const Task &task) void GccParser::newTask(const Task &task)
{ {
doFlush(); doFlush();
@@ -180,12 +107,78 @@ void GccParser::amendDescription(const QString &desc, bool monospaced)
return; return;
} }
void GccParser::handleLine(const QString &line, OutputFormat type) IOutputParser::Status GccParser::doHandleLine(const QString &line, OutputFormat type)
{ {
if (type == StdOutFormat) if (type == StdOutFormat) {
stdOutput(line); // TODO: The "flush on channel switch" logic could possibly also done centrally.
else // But see MSVC with the stdout/stderr switches because of jom
stdError(line); 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["))) {
return Status::NotHandled;
}
// Handle misc issues:
if (lne.startsWith(QLatin1String("ERROR:")) || lne == QLatin1String("* cpp failed")) {
newTask(CompileTask(Task::Error, lne /* description */));
return Status::InProgress;
}
QRegularExpressionMatch match = m_regExpGccNames.match(lne);
if (match.hasMatch()) {
QString description = lne.mid(match.capturedLength());
Task::TaskType type = Task::Error;
if (description.startsWith(QLatin1String("warning: "))) {
type = Task::Warning;
description = description.mid(9);
} else if (description.startsWith(QLatin1String("fatal: "))) {
description = description.mid(7);
}
newTask(CompileTask(type, description));
return Status::InProgress;
}
match = m_regExp.match(lne);
if (match.hasMatch()) {
Utils::FilePath filename = Utils::FilePath::fromUserInput(match.captured(1));
int lineno = match.captured(3).toInt();
Task::TaskType type = Task::Unknown;
QString description = match.captured(8);
if (match.captured(7) == QLatin1String("warning"))
type = Task::Warning;
else if (match.captured(7) == QLatin1String("error") ||
description.startsWith(QLatin1String("undefined reference to")) ||
description.startsWith(QLatin1String("multiple definition of")))
type = Task::Error;
// Prepend "#warning" or "#error" if that triggered the match on (warning|error)
// We want those to show how the warning was triggered
if (match.captured(5).startsWith(QLatin1Char('#')))
description = match.captured(5) + description;
newTask(CompileTask(type, description, absoluteFilePath(filename), lineno));
return Status::InProgress;
}
match = m_regExpIncluded.match(lne);
if (match.hasMatch()) {
newTask(CompileTask(Task::Unknown,
lne.trimmed() /* description */,
absoluteFilePath(Utils::FilePath::fromUserInput(match.captured(1))),
match.captured(3).toInt() /* linenumber */));
return Status::InProgress;
} else if (lne.startsWith(' ') && !m_currentTask.isNull()) {
amendDescription(lne, true);
return Status::InProgress;
}
doFlush();
return Status::NotHandled;
} }
// Unit tests: // Unit tests:
@@ -1130,7 +1123,7 @@ void ProjectExplorerPlugin::testGccOutputParsers_data()
void ProjectExplorerPlugin::testGccOutputParsers() void ProjectExplorerPlugin::testGccOutputParsers()
{ {
OutputParserTester testbench; OutputParserTester testbench;
testbench.appendOutputParser(new GccParser); testbench.setLineParsers(GccParser::gccParserSuite());
QFETCH(QString, input); QFETCH(QString, input);
QFETCH(OutputParserTester::Channel, inputChannel); QFETCH(OutputParserTester::Channel, inputChannel);
QFETCH(Tasks, tasks); QFETCH(Tasks, tasks);

View File

@@ -42,6 +42,8 @@ public:
static Core::Id id(); static Core::Id id();
static QList<IOutputParser *> gccParserSuite();
protected: protected:
void newTask(const Task &task); void newTask(const Task &task);
void doFlush() override; void doFlush() override;
@@ -49,10 +51,7 @@ protected:
void amendDescription(const QString &desc, bool monospaced); void amendDescription(const QString &desc, bool monospaced);
private: private:
void handleLine(const QString &line, Utils::OutputFormat type) override; Status doHandleLine(const QString &line, Utils::OutputFormat type) override;
void stdError(const QString &line);
void stdOutput(const QString &line);
QRegularExpression m_regExp; QRegularExpression m_regExp;
QRegularExpression m_regExpIncluded; QRegularExpression m_regExpIncluded;

View File

@@ -731,9 +731,9 @@ FilePath GccToolChain::makeCommand(const Environment &environment) const
return tmp.isEmpty() ? FilePath::fromString("make") : tmp; 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) void GccToolChain::resetToolChain(const FilePath &path)
@@ -1628,9 +1628,9 @@ LanguageExtensions ClangToolChain::defaultLanguageExtensions() const
return LanguageExtension::Gnu; 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; return extensions;
} }
IOutputParser *LinuxIccToolChain::outputParser() const QList<IOutputParser *> LinuxIccToolChain::outputParsers() const
{ {
return new LinuxIccParser; return LinuxIccParser::iccParserSuite();
} }
QStringList LinuxIccToolChain::suggestedMkspecList() const QStringList LinuxIccToolChain::suggestedMkspecList() const

View File

@@ -94,7 +94,7 @@ public:
void addToEnvironment(Utils::Environment &env) const override; void addToEnvironment(Utils::Environment &env) const override;
Utils::FilePath makeCommand(const Utils::Environment &environment) const override; Utils::FilePath makeCommand(const Utils::Environment &environment) const override;
QStringList suggestedMkspecList() const override; QStringList suggestedMkspecList() const override;
IOutputParser *outputParser() const override; QList<IOutputParser *> outputParsers() const override;
QVariantMap toMap() const override; QVariantMap toMap() const override;
bool fromMap(const QVariantMap &data) override; bool fromMap(const QVariantMap &data) override;
@@ -226,7 +226,7 @@ public:
Utils::LanguageExtensions languageExtensions(const QStringList &cxxflags) const override; Utils::LanguageExtensions languageExtensions(const QStringList &cxxflags) const override;
Utils::WarningFlags warningFlags(const QStringList &cflags) const override; Utils::WarningFlags warningFlags(const QStringList &cflags) const override;
IOutputParser *outputParser() const override; QList<IOutputParser *> outputParsers() const override;
QStringList suggestedMkspecList() const override; QStringList suggestedMkspecList() const override;
void addToEnvironment(Utils::Environment &env) const override; void addToEnvironment(Utils::Environment &env) const override;
@@ -286,7 +286,7 @@ class PROJECTEXPLORER_EXPORT LinuxIccToolChain : public GccToolChain
public: public:
Utils::LanguageExtensions languageExtensions(const QStringList &cxxflags) const override; Utils::LanguageExtensions languageExtensions(const QStringList &cxxflags) const override;
IOutputParser *outputParser() const override; QList<IOutputParser *> outputParsers() const override;
QStringList suggestedMkspecList() const override; QStringList suggestedMkspecList() const override;

View File

@@ -56,35 +56,6 @@ GnuMakeParser::GnuMakeParser()
QTC_CHECK(m_errorInMakefile.isValid()); 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 { class Result {
public: public:
Result() = default; Result() = default;
@@ -125,13 +96,29 @@ static Result parseDescription(const QString &description)
return result; return result;
} }
void GnuMakeParser::stdError(const QString &line) void GnuMakeParser::emitTask(const ProjectExplorer::Task &task)
{
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); 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); QRegularExpressionMatch match = m_errorInMakefile.match(lne);
if (match.hasMatch()) { if (match.hasMatch()) {
flush();
Result res = parseDescription(match.captured(5)); Result res = parseDescription(match.captured(5));
if (res.isFatal) if (res.isFatal)
++m_fatalErrorCount; ++m_fatalErrorCount;
@@ -140,27 +127,24 @@ void GnuMakeParser::stdError(const QString &line)
absoluteFilePath(FilePath::fromUserInput(match.captured(1))), absoluteFilePath(FilePath::fromUserInput(match.captured(1))),
match.captured(4).toInt() /* line */)); match.captured(4).toInt() /* line */));
} }
return; return Status::Done;
} }
match = m_makeLine.match(lne); match = m_makeLine.match(lne);
if (match.hasMatch()) { if (match.hasMatch()) {
flush();
Result res = parseDescription(match.captured(6)); Result res = parseDescription(match.captured(6));
if (res.isFatal) if (res.isFatal)
++m_fatalErrorCount; ++m_fatalErrorCount;
if (!m_suppressIssues) if (!m_suppressIssues)
emitTask(BuildSystemTask(res.type, res.description)); 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. return (m_fatalErrorCount > 0) || IOutputParser::hasFatalErrors();
m_suppressIssues = true;
emit addTask(task, 1, 0);
} }
} // ProjectExplorer } // ProjectExplorer
@@ -371,7 +355,7 @@ void ProjectExplorerPlugin::testGnuMakeParserParsing()
connect(&testbench, &OutputParserTester::aboutToDeleteParser, connect(&testbench, &OutputParserTester::aboutToDeleteParser,
tester, &GnuMakeParserTester::parserIsAboutToBeDeleted); tester, &GnuMakeParserTester::parserIsAboutToBeDeleted);
testbench.appendOutputParser(childParser); testbench.addLineParser(childParser);
QFETCH(QStringList, extraSearchDirs); QFETCH(QStringList, extraSearchDirs);
QFETCH(QString, input); QFETCH(QString, input);
QFETCH(OutputParserTester::Channel, inputChannel); QFETCH(OutputParserTester::Channel, inputChannel);
@@ -381,11 +365,11 @@ void ProjectExplorerPlugin::testGnuMakeParserParsing()
QFETCH(QString, outputLines); QFETCH(QString, outputLines);
QFETCH(QStringList, additionalSearchDirs); QFETCH(QStringList, additionalSearchDirs);
FilePaths searchDirs = childParser->searchDirectories(); FilePaths searchDirs = testbench.searchDirectories();
// add extra directories: // add extra directories:
foreach (const QString &dir, extraSearchDirs) foreach (const QString &dir, extraSearchDirs)
childParser->addSearchDir(FilePath::fromString(dir)); testbench.addSearchDir(FilePath::fromString(dir));
testbench.testParsing(input, inputChannel, testbench.testParsing(input, inputChannel,
tasks, childStdOutLines, childStdErrLines, tasks, childStdOutLines, childStdErrLines,
@@ -418,7 +402,7 @@ void ProjectExplorerPlugin::testGnuMakeParserTaskMangling()
OutputParserTester testbench; OutputParserTester testbench;
auto *childParser = new GnuMakeParser; auto *childParser = new GnuMakeParser;
testbench.appendOutputParser(childParser); testbench.addLineParser(childParser);
childParser->addSearchDir(FilePath::fromString(fi.absolutePath())); childParser->addSearchDir(FilePath::fromString(fi.absolutePath()));
testbench.testParsing( testbench.testParsing(
fi.fileName() + ":360: *** missing separator (did you mean TAB instead of 8 spaces?). Stop.", fi.fileName() + ":360: *** missing separator (did you mean TAB instead of 8 spaces?). Stop.",

View File

@@ -40,12 +40,9 @@ public:
explicit GnuMakeParser(); explicit GnuMakeParser();
private: private:
void handleLine(const QString &line, Utils::OutputFormat type) override; Status doHandleLine(const QString &line, Utils::OutputFormat type) override;
bool hasFatalErrors() const override; bool hasFatalErrors() const override;
void stdOutput(const QString &line);
void stdError(const QString &line);
void emitTask(const ProjectExplorer::Task &task); void emitTask(const ProjectExplorer::Task &task);
QRegularExpression m_makeDir; QRegularExpression m_makeDir;

View File

@@ -26,10 +26,12 @@
#include "ioutputparser.h" #include "ioutputparser.h"
#include "task.h" #include "task.h"
#include <utils/algorithm.h>
#include <utils/synchronousprocess.h> #include <utils/synchronousprocess.h>
#include <QDir> #include <QDir>
#include <QFileInfo> #include <QFileInfo>
#include <QPointer>
/*! /*!
\class ProjectExplorer::IOutputParser \class ProjectExplorer::IOutputParser
@@ -41,21 +43,7 @@
*/ */
/*! /*!
\fn void ProjectExplorer::IOutputParser::appendOutputParser(IOutputParser *parser) \fn ProjectExplorer::IOutputParser::Status ProjectExplorer::IOutputParser::doHandleLine(const QString &line, Utils::OutputFormat type)
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)
Called once for each line of standard output or standard error to parse. Called once for each line of standard output or standard error to parse.
*/ */
@@ -66,28 +54,12 @@
This is mainly a Symbian specific quirk. This is mainly a Symbian specific quirk.
*/ */
/*!
\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) \fn void ProjectExplorer::IOutputParser::addTask(const ProjectExplorer::Task &task)
Should be emitted for each task seen in the output. 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() \fn void ProjectExplorer::IOutputParser::doFlush()
@@ -121,14 +93,14 @@ public:
break; break;
const QString line = pendingData.left(eolPos + 1); const QString line = pendingData.left(eolPos + 1);
pendingData.remove(0, eolPos + 1); pendingData.remove(0, eolPos + 1);
parser->handleLine(parser->filteredLine(line), type); parser->handleLine(line, type);
} }
} }
void flush() void flush()
{ {
if (!pendingData.isEmpty()) { if (!pendingData.isEmpty()) {
parser->handleLine(parser->filteredLine(pendingData), type); parser->handleLine(pendingData, type);
pendingData.clear(); pendingData.clear();
} }
} }
@@ -146,11 +118,13 @@ public:
stderrState(parser, Utils::StdErrFormat) stderrState(parser, Utils::StdErrFormat)
{} {}
IOutputParser *childParser = nullptr; QList<IOutputParser *> lineParsers;
IOutputParser *nextParser = nullptr;
QList<Filter> filters; QList<Filter> filters;
Utils::FilePaths searchDirs; Utils::FilePaths searchDirs;
OutputChannelState stdoutState; OutputChannelState stdoutState;
OutputChannelState stderrState; OutputChannelState stderrState;
QPointer<const IOutputParser> redirectionDetector;
bool skipFileExistsCheck = false; bool skipFileExistsCheck = false;
}; };
@@ -160,7 +134,7 @@ IOutputParser::IOutputParser() : d(new IOutputParserPrivate(this))
IOutputParser::~IOutputParser() IOutputParser::~IOutputParser()
{ {
delete d->childParser; clear();
delete d; delete d;
} }
@@ -174,46 +148,44 @@ void IOutputParser::handleStderr(const QString &data)
d->stderrState.handleData(data); d->stderrState.handleData(data);
} }
void IOutputParser::appendOutputParser(IOutputParser *parser) IOutputParser::Status IOutputParser::doHandleLine(const QString &line, Utils::OutputFormat type)
{ {
if (!parser) Q_UNUSED(line);
return; Q_UNUSED(type);
if (d->childParser) { return Status::NotHandled;
d->childParser->appendOutputParser(parser);
return;
}
d->childParser = parser;
connect(parser, &IOutputParser::addTask, this, &IOutputParser::addTask);
}
IOutputParser *IOutputParser::childParser() const
{
return d->childParser;
}
void IOutputParser::setChildParser(IOutputParser *parser)
{
if (d->childParser != parser)
delete d->childParser;
d->childParser = parser;
if (parser)
connect(parser, &IOutputParser::addTask, this, &IOutputParser::addTask);
}
void IOutputParser::handleLine(const QString &line, Utils::OutputFormat type)
{
if (d->childParser)
d->childParser->handleLine(line, type);
}
void IOutputParser::skipFileExistsCheck()
{
d->skipFileExistsCheck = true;
} }
void IOutputParser::doFlush() { } 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;
}
}
}
QString IOutputParser::filteredLine(const QString &line) const QString IOutputParser::filteredLine(const QString &line) const
{ {
QString l = line; QString l = line;
@@ -222,26 +194,70 @@ QString IOutputParser::filteredLine(const QString &line) const
return l; return l;
} }
void IOutputParser::connectLineParser(IOutputParser *parser)
{
connect(parser, &IOutputParser::addTask, this, &IOutputParser::addTask);
connect(parser, &IOutputParser::searchDirIn, this, &IOutputParser::addSearchDir);
connect(parser, &IOutputParser::searchDirOut, this, &IOutputParser::dropSearchDir);
}
bool IOutputParser::hasFatalErrors() const bool IOutputParser::hasFatalErrors() const
{ {
return d->childParser && d->childParser->hasFatalErrors(); return Utils::anyOf(d->lineParsers, [](const IOutputParser *p) { return p->hasFatalErrors(); });
} }
void IOutputParser::flush() void IOutputParser::flush()
{ {
flushTasks();
d->stdoutState.flush(); d->stdoutState.flush();
d->stderrState.flush(); d->stderrState.flush();
flushTasks(); doFlush();
for (IOutputParser * const p : qAsConst(d->lineParsers))
p->doFlush();
} }
void IOutputParser::flushTasks() void IOutputParser::clear()
{ {
doFlush(); d->nextParser = nullptr;
if (d->childParser) d->redirectionDetector.clear();
d->childParser->flushTasks(); d->filters.clear();
d->searchDirs.clear();
qDeleteAll(d->lineParsers);
d->lineParsers.clear();
d->stdoutState.pendingData.clear();
d->stderrState.pendingData.clear();
} }
void IOutputParser::addLineParser(IOutputParser *parser)
{
connectLineParser(parser);
d->lineParsers << parser;
}
void IOutputParser::addLineParsers(const QList<IOutputParser *> &parsers)
{
for (IOutputParser * const p : qAsConst(parsers))
addLineParser(p);
}
void IOutputParser::setLineParsers(const QList<IOutputParser *> &parsers)
{
qDeleteAll(d->lineParsers);
d->lineParsers.clear();
addLineParsers(parsers);
}
#ifdef WITH_TESTS
QList<IOutputParser *> IOutputParser::lineParsers() const
{
return d->lineParsers;
}
void IOutputParser::skipFileExistsCheck()
{
d->skipFileExistsCheck = true;
}
#endif // WITH_TESTS
QString IOutputParser::rightTrimmed(const QString &in) QString IOutputParser::rightTrimmed(const QString &in)
{ {
int pos = in.length(); int pos = in.length();
@@ -260,8 +276,8 @@ void IOutputParser::addFilter(const Filter &filter)
void IOutputParser::addSearchDir(const Utils::FilePath &dir) void IOutputParser::addSearchDir(const Utils::FilePath &dir)
{ {
d->searchDirs << dir; d->searchDirs << dir;
if (d->childParser) for (IOutputParser * const p : qAsConst(d->lineParsers))
d->childParser->addSearchDir(dir); p->addSearchDir(dir);
} }
void IOutputParser::dropSearchDir(const Utils::FilePath &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); const int idx = d->searchDirs.lastIndexOf(dir);
QTC_ASSERT(idx != -1, return); QTC_ASSERT(idx != -1, return);
d->searchDirs.removeAt(idx); d->searchDirs.removeAt(idx);
if (d->childParser) for (IOutputParser * const p : qAsConst(d->lineParsers))
d->childParser->dropSearchDir(dir); p->dropSearchDir(dir);
} }
const Utils::FilePaths IOutputParser::searchDirectories() const const Utils::FilePaths IOutputParser::searchDirectories() const
@@ -293,4 +309,27 @@ Utils::FilePath IOutputParser::absoluteFilePath(const Utils::FilePath &filePath)
return 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 } // namespace ProjectExplorer

View File

@@ -47,11 +47,6 @@ public:
void handleStdout(const QString &data); void handleStdout(const QString &data);
void handleStderr(const QString &data); void handleStderr(const QString &data);
void appendOutputParser(IOutputParser *parser);
IOutputParser *childParser() const;
void setChildParser(IOutputParser *parser);
virtual bool hasFatalErrors() const; virtual bool hasFatalErrors() const;
using Filter = std::function<QString(const QString &)>; using Filter = std::function<QString(const QString &)>;
@@ -60,25 +55,44 @@ public:
void addSearchDir(const Utils::FilePath &dir); void addSearchDir(const Utils::FilePath &dir);
void dropSearchDir(const Utils::FilePath &dir); void dropSearchDir(const Utils::FilePath &dir);
const Utils::FilePaths searchDirectories() const; const Utils::FilePaths searchDirectories() const;
void skipFileExistsCheck(); // For testing only
void flush(); // flush pending tasks & output void flush();
void flushTasks(); // flush pending tasks only 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); static QString rightTrimmed(const QString &in);
signals: 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); void addTask(const ProjectExplorer::Task &task, int linkedOutputLines = 0, int skipLines = 0);
protected: protected:
virtual void handleLine(const QString &line, Utils::OutputFormat type); enum class Status { Done, InProgress, NotHandled };
Utils::FilePath absoluteFilePath(const Utils::FilePath &filePath); Utils::FilePath absoluteFilePath(const Utils::FilePath &filePath);
private: private:
virtual Status doHandleLine(const QString &line, Utils::OutputFormat type);
virtual void doFlush(); virtual void doFlush();
virtual bool hasDetectedRedirection() const { return false; }
void handleLine(const QString &line, Utils::OutputFormat type);
QString filteredLine(const QString &line) const; 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 OutputChannelState;
class IOutputParserPrivate; class IOutputParserPrivate;

View File

@@ -559,12 +559,12 @@ void Kit::addToEnvironment(Environment &env) const
aspect->addToEnvironment(this, env); 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()) for (KitAspect *aspect : KitManager::kitAspects())
first->appendOutputParser(aspect->createOutputParser(this)); parsers << aspect->createOutputParsers(this);
return first; return parsers;
} }
QString Kit::toHtml(const Tasks &additional, const QString &extraText) const QString Kit::toHtml(const Tasks &additional, const QString &extraText) const

View File

@@ -116,7 +116,7 @@ public:
bool isEqual(const Kit *other) const; bool isEqual(const Kit *other) const;
void addToEnvironment(Utils::Environment &env) 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; QString toHtml(const Tasks &additional = Tasks(), const QString &extraText = QString()) const;
Kit *clone(bool keepName = false) const; Kit *clone(bool keepName = false) const;

View File

@@ -557,14 +557,13 @@ void ToolChainKitAspect::addToMacroExpander(Kit *kit, Utils::MacroExpander *expa
}); });
} }
QList<IOutputParser *> ToolChainKitAspect::createOutputParsers(const Kit *k) const
IOutputParser *ToolChainKitAspect::createOutputParser(const Kit *k) const
{ {
for (const Core::Id langId : {Constants::CXX_LANGUAGE_ID, Constants::C_LANGUAGE_ID}) { for (const Core::Id langId : {Constants::CXX_LANGUAGE_ID, Constants::C_LANGUAGE_ID}) {
if (const ToolChain * const tc = toolChain(k, langId)) 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 QSet<Core::Id> ToolChainKitAspect::availableFeatures(const Kit *k) const

View File

@@ -84,7 +84,7 @@ public:
void addToEnvironment(const Kit *k, Utils::Environment &env) const override; void addToEnvironment(const Kit *k, Utils::Environment &env) const override;
void addToMacroExpander(Kit *kit, Utils::MacroExpander *expander) 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; QSet<Core::Id> availableFeatures(const Kit *k) const override;
static Core::Id id(); static Core::Id id();

View File

@@ -677,10 +677,10 @@ void KitAspect::addToEnvironment(const Kit *k, Environment &env) const
Q_UNUSED(env) Q_UNUSED(env)
} }
IOutputParser *KitAspect::createOutputParser(const Kit *k) const QList<IOutputParser *> KitAspect::createOutputParsers(const Kit *k) const
{ {
Q_UNUSED(k) Q_UNUSED(k)
return nullptr; return {};
} }
QString KitAspect::displayNamePostfix(const Kit *k) const QString KitAspect::displayNamePostfix(const Kit *k) const

View File

@@ -91,7 +91,7 @@ public:
virtual KitAspectWidget *createConfigWidget(Kit *) const = 0; virtual KitAspectWidget *createConfigWidget(Kit *) const = 0;
virtual void addToEnvironment(const Kit *k, Utils::Environment &env) const; 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; virtual QString displayNamePostfix(const Kit *k) const;

View File

@@ -54,27 +54,27 @@ LdParser::LdParser()
QTC_CHECK(m_regExpGccNames.isValid()); 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) { if (type != Utils::StdErrFormat)
IOutputParser::handleLine(line, type); return Status::NotHandled;
return;
}
QString lne = rightTrimmed(line); QString lne = rightTrimmed(line);
if (!lne.isEmpty() && !lne.at(0).isSpace() && !m_incompleteTask.isNull()) if (!lne.isEmpty() && !lne.at(0).isSpace() && !m_incompleteTask.isNull()) {
flush(); doFlush();
return Status::NotHandled;
}
if (lne.startsWith(QLatin1String("TeamBuilder ")) if (lne.startsWith(QLatin1String("TeamBuilder "))
|| lne.startsWith(QLatin1String("distcc[")) || lne.startsWith(QLatin1String("distcc["))
|| lne.contains(QLatin1String("ar: creating "))) { || lne.contains(QLatin1String("ar: creating "))) {
IOutputParser::handleLine(line, Utils::StdErrFormat); return Status::NotHandled;
return;
} }
// ld on macOS // ld on macOS
if (lne.startsWith("Undefined symbols for architecture") && lne.endsWith(":")) { if (lne.startsWith("Undefined symbols for architecture") && lne.endsWith(":")) {
m_incompleteTask = CompileTask(Task::Error, lne); m_incompleteTask = CompileTask(Task::Error, lne);
return; return Status::InProgress;
} }
if (!m_incompleteTask.isNull() && lne.startsWith(" ")) { if (!m_incompleteTask.isNull() && lne.startsWith(" ")) {
m_incompleteTask.description.append('\n').append(lne); 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); const QRegularExpressionMatch match = locRegExp.match(lne);
if (match.hasMatch()) if (match.hasMatch())
m_incompleteTask.setFile(Utils::FilePath::fromString(match.captured("file"))); m_incompleteTask.setFile(Utils::FilePath::fromString(match.captured("file")));
return; return Status::InProgress;
} }
if (lne.startsWith("collect2:") || lne.startsWith("collect2.exe:")) { if (lne.startsWith("collect2:") || lne.startsWith("collect2.exe:")) {
emit addTask(CompileTask(Task::Error, lne /* description */), 1); emit addTask(CompileTask(Task::Error, lne /* description */), 1);
return; return Status::Done;
} }
QRegularExpressionMatch match = m_ranlib.match(lne); QRegularExpressionMatch match = m_ranlib.match(lne);
if (match.hasMatch()) { if (match.hasMatch()) {
QString description = match.captured(2); QString description = match.captured(2);
emit addTask(CompileTask(Task::Warning, description), 1); emit addTask(CompileTask(Task::Warning, description), 1);
return; return Status::Done;
} }
match = m_regExpGccNames.match(lne); match = m_regExpGccNames.match(lne);
@@ -108,7 +108,7 @@ void LdParser::handleLine(const QString &line, Utils::OutputFormat type)
description = description.mid(7); description = description.mid(7);
} }
emit addTask(CompileTask(type, description), 1); emit addTask(CompileTask(type, description), 1);
return; return Status::Done;
} }
match = m_regExpLinker.match(lne); match = m_regExpLinker.match(lne);
@@ -138,10 +138,10 @@ void LdParser::handleLine(const QString &line, Utils::OutputFormat type)
description = description.mid(9); description = description.mid(9);
} }
emit addTask(CompileTask(type, description, absoluteFilePath(filename), lineno), 1); emit addTask(CompileTask(type, description, absoluteFilePath(filename), lineno), 1);
return; return Status::Done;
} }
IOutputParser::handleLine(line, Utils::StdErrFormat); return Status::NotHandled;
} }
void LdParser::doFlush() void LdParser::doFlush()

View File

@@ -39,7 +39,7 @@ class LdParser : public ProjectExplorer::IOutputParser
public: public:
LdParser(); LdParser();
private: private:
void handleLine(const QString &line, Utils::OutputFormat type) override; Status doHandleLine(const QString &line, Utils::OutputFormat type) override;
void doFlush() override; void doFlush() override;
QRegularExpression m_ranlib; QRegularExpression m_ranlib;

View File

@@ -63,21 +63,15 @@ LinuxIccParser::LinuxIccParser() :
m_pchInfoLine.setPattern(QLatin1String("^\".*\": (creating|using) precompiled header file \".*\"\n$")); m_pchInfoLine.setPattern(QLatin1String("^\".*\": (creating|using) precompiled header file \".*\"\n$"));
m_pchInfoLine.setMinimal(true); m_pchInfoLine.setMinimal(true);
QTC_CHECK(m_pchInfoLine.isValid()); 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) { if (type != Utils::StdErrFormat)
IOutputParser::handleLine(line, type); return Status::NotHandled;
return;
} if (m_pchInfoLine.indexIn(line) != -1)
if (m_pchInfoLine.indexIn(line) != -1) { return Status::Done; // totally ignore this line
// totally ignore this line
return;
}
if (m_expectFirstLine && m_firstLine.indexIn(line) != -1) { if (m_expectFirstLine && m_firstLine.indexIn(line) != -1) {
// Clear out old task // Clear out old task
@@ -94,7 +88,9 @@ void LinuxIccParser::handleLine(const QString &line, OutputFormat type)
m_lines = 1; m_lines = 1;
m_expectFirstLine = false; 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 // Format the last line as code
QTextLayout::FormatRange fr; QTextLayout::FormatRange fr;
fr.start = m_temporary.description.lastIndexOf(QLatin1Char('\n')) + 1; 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.length = 1;
fr2.format.setFontWeight(QFont::Bold); fr2.format.setFontWeight(QFont::Bold);
m_temporary.formats.append(fr2); 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; m_expectFirstLine = true;
emit addTask(m_temporary, m_lines); emit addTask(m_temporary, m_lines);
m_temporary = Task(); 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_temporary.description.append(QLatin1Char('\n'));
m_indent = 0; m_indent = 0;
while (m_indent < line.length() && line.at(m_indent).isSpace()) while (m_indent < line.length() && line.at(m_indent).isSpace())
m_indent++; m_indent++;
m_temporary.description.append(m_continuationLines.cap(1).trimmed()); m_temporary.description.append(m_continuationLines.cap(1).trimmed());
++m_lines; ++m_lines;
} else { return Status::InProgress;
IOutputParser::handleLine(line, StdErrFormat);
} }
QTC_CHECK(m_temporary.isNull());
return Status::NotHandled;
} }
Core::Id LinuxIccParser::id() Core::Id LinuxIccParser::id()
@@ -128,6 +129,11 @@ Core::Id LinuxIccParser::id()
return Core::Id("ProjectExplorer.OutputParser.Icc"); return Core::Id("ProjectExplorer.OutputParser.Icc");
} }
QList<IOutputParser *> LinuxIccParser::iccParserSuite()
{
return {new LinuxIccParser, new Internal::LldParser, new LdParser};
}
void LinuxIccParser::doFlush() void LinuxIccParser::doFlush()
{ {
if (m_temporary.isNull()) if (m_temporary.isNull())
@@ -238,7 +244,7 @@ void ProjectExplorerPlugin::testLinuxIccOutputParsers_data()
void ProjectExplorerPlugin::testLinuxIccOutputParsers() void ProjectExplorerPlugin::testLinuxIccOutputParsers()
{ {
OutputParserTester testbench; OutputParserTester testbench;
testbench.appendOutputParser(new LinuxIccParser); testbench.setLineParsers(LinuxIccParser::iccParserSuite());
QFETCH(QString, input); QFETCH(QString, input);
QFETCH(OutputParserTester::Channel, inputChannel); QFETCH(OutputParserTester::Channel, inputChannel);
QFETCH(Tasks, tasks); QFETCH(Tasks, tasks);

View File

@@ -41,8 +41,10 @@ public:
static Core::Id id(); static Core::Id id();
static QList<IOutputParser *> iccParserSuite();
private: private:
void handleLine(const QString &line, Utils::OutputFormat type) override; Status doHandleLine(const QString &line, Utils::OutputFormat type) override;
void doFlush() override; void doFlush() override;
QRegExp m_firstLine; QRegExp m_firstLine;

View File

@@ -35,16 +35,15 @@
namespace ProjectExplorer { namespace ProjectExplorer {
namespace Internal { 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) { if (type != Utils::StdErrFormat)
IOutputParser::handleLine(line, type); return Status::NotHandled;
return;
}
const QString trimmedLine = rightTrimmed(line); const QString trimmedLine = rightTrimmed(line);
if (trimmedLine.contains("error:") && trimmedLine.contains("lld")) { if (trimmedLine.contains("error:") && trimmedLine.contains("lld")) {
emit addTask(CompileTask(Task::Error, trimmedLine)); emit addTask(CompileTask(Task::Error, trimmedLine));
return; return Status::Done;
} }
static const QStringList prefixes{">>> referenced by ", ">>> defined at ", ">>> "}; static const QStringList prefixes{">>> referenced by ", ">>> defined at ", ">>> "};
for (const QString &prefix : prefixes) { for (const QString &prefix : prefixes) {
@@ -70,9 +69,9 @@ void LldParser::handleLine(const QString &line, Utils::OutputFormat type)
trimmedLine.mid(filePathOffset, filePathLen).trimmed()); trimmedLine.mid(filePathOffset, filePathLen).trimmed());
emit addTask(CompileTask(Task::Unknown, trimmedLine.mid(4).trimmed(), emit addTask(CompileTask(Task::Unknown, trimmedLine.mid(4).trimmed(),
absoluteFilePath(file), lineNo)); absoluteFilePath(file), lineNo));
return; return Status::Done;
} }
IOutputParser::handleLine(line, Utils::StdErrFormat); return Status::NotHandled;
} }
} // namespace Internal } // namespace Internal

View File

@@ -32,7 +32,7 @@ namespace Internal {
class LldParser : public IOutputParser class LldParser : public IOutputParser
{ {
void handleLine(const QString &line, Utils::OutputFormat type) override; Status doHandleLine(const QString &line, Utils::OutputFormat type) override;
}; };
} // namespace Internal } // namespace Internal

View File

@@ -103,9 +103,7 @@ bool MakeStep::init()
setIgnoreReturnValue(isClean()); setIgnoreReturnValue(isClean());
setOutputParser(new GnuMakeParser()); setOutputParser(new GnuMakeParser());
IOutputParser *parser = target()->kit()->createOutputParser(); appendOutputParsers(target()->kit()->createOutputParsers());
if (parser)
appendOutputParser(parser);
outputParser()->addSearchDir(pp->effectiveWorkingDirectory()); outputParser()->addSearchDir(pp->effectiveWorkingDirectory());
return AbstractProcessStep::init(); return AbstractProcessStep::init();

View File

@@ -100,12 +100,18 @@ MsvcParser::MsvcParser()
QTC_CHECK(m_additionalInfoRegExp.isValid()); QTC_CHECK(m_additionalInfoRegExp.isValid());
} }
void MsvcParser::stdOutput(const QString &line) Core::Id MsvcParser::id()
{ {
return Core::Id("ProjectExplorer.OutputParser.Msvc");
}
IOutputParser::Status MsvcParser::doHandleLine(const QString &line, OutputFormat type)
{
if (type == OutputFormat::StdOutFormat) {
QRegularExpressionMatch match = m_additionalInfoRegExp.match(line); QRegularExpressionMatch match = m_additionalInfoRegExp.match(line);
if (line.startsWith(" ") && !match.hasMatch()) { if (line.startsWith(" ") && !match.hasMatch()) {
if (m_lastTask.isNull()) if (m_lastTask.isNull())
return; return Status::NotHandled;
m_lastTask.description.append('\n'); m_lastTask.description.append('\n');
m_lastTask.description.append(line.mid(8)); m_lastTask.description.append(line.mid(8));
@@ -124,17 +130,18 @@ void MsvcParser::stdOutput(const QString &line)
fr.format.setFontItalic(true); fr.format.setFontItalic(true);
m_lastTask.formats.append(fr); m_lastTask.formats.append(fr);
} else { } else {
m_lastTask.formats[0].length = m_lastTask.description.length() - m_lastTask.formats[0].start; m_lastTask.formats[0].length = m_lastTask.description.length()
- m_lastTask.formats[0].start;
} }
++m_lines; ++m_lines;
return; return Status::InProgress;
} }
if (processCompileLine(line)) if (processCompileLine(line))
return; return Status::InProgress;
if (handleNmakeJomMessage(line, &m_lastTask)) { if (handleNmakeJomMessage(line, &m_lastTask)) {
m_lines = 1; m_lines = 1;
return; return Status::InProgress;
} }
if (match.hasMatch()) { if (match.hasMatch()) {
QString description = match.captured(1) QString description = match.captured(1)
@@ -145,34 +152,19 @@ void MsvcParser::stdOutput(const QString &line)
absoluteFilePath(FilePath::fromUserInput(match.captured(2))), absoluteFilePath(FilePath::fromUserInput(match.captured(2))),
match.captured(3).toInt() /* linenumber */); match.captured(3).toInt() /* linenumber */);
m_lines = 1; m_lines = 1;
return; return Status::InProgress;
} }
IOutputParser::handleLine(line, StdOutFormat); return Status::NotHandled;
} }
void MsvcParser::stdError(const QString &line)
{
if (processCompileLine(line)) if (processCompileLine(line))
return; return Status::InProgress;
// Jom outputs errors to stderr // Jom outputs errors to stderr
if (handleNmakeJomMessage(line, &m_lastTask)) { if (handleNmakeJomMessage(line, &m_lastTask)) {
m_lines = 1; m_lines = 1;
return; return Status::InProgress;
} }
IOutputParser::handleLine(line, StdErrFormat); return Status::NotHandled;
}
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);
} }
bool MsvcParser::processCompileLine(const QString &line) bool MsvcParser::processCompileLine(const QString &line)
@@ -220,24 +212,6 @@ ClangClParser::ClangClParser()
QTC_CHECK(m_compileRegExp.isValid()); 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. // Check for a code marker '~~~~ ^ ~~~~~~~~~~~~' underlining above code.
static inline bool isClangCodeMarker(const QString &trimmedLine) static inline bool isClangCodeMarker(const QString &trimmedLine)
{ {
@@ -246,51 +220,59 @@ static inline bool isClangCodeMarker(const QString &trimmedLine)
[] (QChar c) { return c != ' ' && c != '^' && c != '~'; }); [] (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)) { if (handleNmakeJomMessage(line, &m_lastTask)) {
m_linkedLines = 1; m_linkedLines = 1;
doFlush(); doFlush();
return; return Status::Done;
}
return Status::NotHandled;
}
const QString lne = IOutputParser::rightTrimmed(line); // Strip \n.
if (handleNmakeJomMessage(lne, &m_lastTask)) {
m_linkedLines = 1;
doFlush();
return Status::Done;
} }
// Finish a sequence of warnings/errors: "2 warnings generated." // 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(); doFlush();
return; return Status::Done;
} }
// Start a new error message by a sequence of "In file included from " which is to be skipped. // 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(); doFlush();
return; return Status::Done;
} }
QRegularExpressionMatch match = m_compileRegExp.match(line); QRegularExpressionMatch match = m_compileRegExp.match(lne);
if (match.hasMatch()) { if (match.hasMatch()) {
doFlush(); doFlush();
const QPair<FilePath, int> position = parseFileName(match.captured(1)); const QPair<FilePath, int> position = parseFileName(match.captured(1));
m_lastTask = CompileTask(taskType(match.captured(2)), match.captured(3).trimmed(), m_lastTask = CompileTask(taskType(match.captured(2)), match.captured(3).trimmed(),
absoluteFilePath(position.first), position.second); absoluteFilePath(position.first), position.second);
m_linkedLines = 1; m_linkedLines = 1;
return; return Status::InProgress;
} }
if (!m_lastTask.isNull()) { if (!m_lastTask.isNull()) {
const QString trimmed = line.trimmed(); const QString trimmed = lne.trimmed();
if (isClangCodeMarker(trimmed)) { if (isClangCodeMarker(trimmed)) {
doFlush(); doFlush();
return; return Status::Done;
} }
m_lastTask.description.append('\n'); m_lastTask.description.append('\n');
m_lastTask.description.append(trimmed); m_lastTask.description.append(trimmed);
++m_linkedLines; ++m_linkedLines;
return; return Status::InProgress;
} }
IOutputParser::handleLine(lineIn, StdErrFormat); return Status::NotHandled;
} }
void ClangClParser::doFlush() void ClangClParser::doFlush()
@@ -566,7 +548,7 @@ void ProjectExplorerPlugin::testMsvcOutputParsers_data()
void ProjectExplorerPlugin::testMsvcOutputParsers() void ProjectExplorerPlugin::testMsvcOutputParsers()
{ {
OutputParserTester testbench; OutputParserTester testbench;
testbench.appendOutputParser(new MsvcParser); testbench.addLineParser(new MsvcParser);
QFETCH(QString, input); QFETCH(QString, input);
QFETCH(OutputParserTester::Channel, inputChannel); QFETCH(OutputParserTester::Channel, inputChannel);
QFETCH(Tasks, tasks); QFETCH(Tasks, tasks);
@@ -645,7 +627,7 @@ void ProjectExplorerPlugin::testClangClOutputParsers_data()
void ProjectExplorerPlugin::testClangClOutputParsers() void ProjectExplorerPlugin::testClangClOutputParsers()
{ {
OutputParserTester testbench; OutputParserTester testbench;
testbench.appendOutputParser(new ClangClParser); testbench.addLineParser(new ClangClParser);
QFETCH(QString, input); QFETCH(QString, input);
QFETCH(OutputParserTester::Channel, inputChannel); QFETCH(OutputParserTester::Channel, inputChannel);
QFETCH(Tasks, tasks); QFETCH(Tasks, tasks);

View File

@@ -43,11 +43,9 @@ public:
static Core::Id id(); static Core::Id id();
private: private:
void handleLine(const QString &line, Utils::OutputFormat type) override; Status doHandleLine(const QString &line, Utils::OutputFormat type) override;
void doFlush() override; void doFlush() override;
void stdOutput(const QString &line);
void stdError(const QString &line);
bool processCompileLine(const QString &line); bool processCompileLine(const QString &line);
QRegularExpression m_compileRegExp; QRegularExpression m_compileRegExp;
@@ -65,12 +63,9 @@ public:
ClangClParser(); ClangClParser();
private: private:
void handleLine(const QString &line, Utils::OutputFormat type) override; Status doHandleLine(const QString &line, Utils::OutputFormat type) override;
void doFlush() override; void doFlush() override;
void stdOutput(const QString &line);
void stdError(const QString &line);
const QRegularExpression m_compileRegExp; const QRegularExpression m_compileRegExp;
Task m_lastTask; Task m_lastTask;
int m_linkedLines = 0; int m_linkedLines = 0;

View File

@@ -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) 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"}; return {mkspec, "win32-clang-msvc"};
} }
IOutputParser *ClangClToolChain::outputParser() const QList<IOutputParser *> ClangClToolChain::outputParsers() const
{ {
return new ClangClParser; return {new ClangClParser};
} }
static inline QString llvmDirKey() static inline QString llvmDirKey()

View File

@@ -89,7 +89,7 @@ public:
Utils::FilePath makeCommand(const Utils::Environment &environment) const override; Utils::FilePath makeCommand(const Utils::Environment &environment) const override;
Utils::FilePath compilerCommand() const override; Utils::FilePath compilerCommand() const override;
IOutputParser *outputParser() const override; QList<IOutputParser *> outputParsers() const override;
QString varsBatArg() const { return m_varsBatArg; } QString varsBatArg() const { return m_varsBatArg; }
QString varsBat() const { return m_vcvarsBat; } QString varsBat() const { return m_vcvarsBat; }
@@ -174,7 +174,7 @@ public:
QStringList suggestedMkspecList() const override; QStringList suggestedMkspecList() const override;
void addToEnvironment(Utils::Environment &env) const override; void addToEnvironment(Utils::Environment &env) const override;
Utils::FilePath compilerCommand() const override; Utils::FilePath compilerCommand() const override;
IOutputParser *outputParser() const override; QList<IOutputParser *> outputParsers() const override;
QVariantMap toMap() const override; QVariantMap toMap() const override;
bool fromMap(const QVariantMap &data) override; bool fromMap(const QVariantMap &data) override;
std::unique_ptr<ToolChainConfigWidget> createConfigurationWidget() override; std::unique_ptr<ToolChainConfigWidget> createConfigurationWidget() override;

View File

@@ -36,39 +36,30 @@ OsParser::OsParser()
setObjectName(QLatin1String("OsParser")); setObjectName(QLatin1String("OsParser"));
} }
void OsParser::handleLine(const QString &line, Utils::OutputFormat type) IOutputParser::Status OsParser::doHandleLine(const QString &line, Utils::OutputFormat type)
{
if (type == Utils::StdOutFormat)
stdOutput(line);
else
stdError(line);
}
void OsParser::stdError(const QString &line)
{
if (Utils::HostOsInfo::isLinuxHost()) {
const QString trimmed = line.trimmed();
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 (type == Utils::StdOutFormat) {
if (Utils::HostOsInfo::isWindowsHost()) { if (Utils::HostOsInfo::isWindowsHost()) {
const QString trimmed = line.trimmed(); const QString trimmed = line.trimmed();
if (trimmed == QLatin1String("The process cannot access the file because it is being used by another process.")) { if (trimmed == QLatin1String("The process cannot access the file because it is "
"being used by another process.")) {
emit addTask(CompileTask(Task::Error, tr( emit addTask(CompileTask(Task::Error, tr(
"The process cannot access the file because it is being used by another process.\n" "The process cannot access the file because it is being used "
"Please close all running instances of your application before starting a build."))); "by another process.\n"
"Please close all running instances of your application before "
"starting a build.")));
m_hasFatalError = true; m_hasFatalError = true;
return Status::Done;
} }
} }
IOutputParser::handleLine(line, Utils::StdOutFormat); return Status::NotHandled;
} }
if (Utils::HostOsInfo::isLinuxHost()) {
bool OsParser::hasFatalErrors() const const QString trimmed = line.trimmed();
{ if (trimmed.contains(QLatin1String(": error while loading shared libraries:"))) {
return m_hasFatalError || IOutputParser::hasFatalErrors(); emit addTask(CompileTask(Task::Error, trimmed));
return Status::Done;
}
}
return Status::NotHandled;
} }

View File

@@ -41,11 +41,8 @@ public:
OsParser(); OsParser();
private: private:
void handleLine(const QString &line, Utils::OutputFormat type) override; Status doHandleLine(const QString &line, Utils::OutputFormat type) override;
bool hasFatalErrors() const override; bool hasFatalErrors() const override { return m_hasFatalError; }
void stdError(const QString &line);
void stdOutput(const QString &line);
bool m_hasFatalError = false; bool m_hasFatalError = false;
}; };

View File

@@ -53,12 +53,11 @@ void OutputParserTester::testParsing(const QString &lines,
const QString &childStdErrLines, const QString &childStdErrLines,
const QString &outputLines) const QString &outputLines)
{ {
if (!m_terminator) { const auto terminator = new TestTerminator(this);
m_terminator = new TestTerminator(this); if (!lineParsers().isEmpty())
appendOutputParser(m_terminator); terminator->setRedirectionDetector(lineParsers().last());
} addLineParser(terminator);
reset(); reset();
Q_ASSERT(childParser());
if (inputChannel == STDOUT) if (inputChannel == STDOUT)
handleStdout(lines + '\n'); handleStdout(lines + '\n');
@@ -68,7 +67,7 @@ void OutputParserTester::testParsing(const QString &lines,
// delete the parser(s) to test // delete the parser(s) to test
emit aboutToDeleteParser(); emit aboutToDeleteParser();
setChildParser(nullptr); setLineParsers({});
QCOMPARE(m_receivedOutput, outputLines); QCOMPARE(m_receivedOutput, outputLines);
QCOMPARE(m_receivedStdErrChildLine, childStdErrLines); QCOMPARE(m_receivedStdErrChildLine, childStdErrLines);
@@ -103,13 +102,14 @@ TestTerminator::TestTerminator(OutputParserTester *t) :
m_tester(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) if (type == Utils::StdOutFormat)
m_tester->m_receivedStdOutChildLine.append(line); m_tester->m_receivedStdOutChildLine.append(line);
else else
m_tester->m_receivedStdErrChildLine.append(line); m_tester->m_receivedStdErrChildLine.append(line);
return Status::Done;
} }
} // namespace ProjectExplorer } // namespace ProjectExplorer

View File

@@ -69,7 +69,6 @@ private:
QString m_receivedStdOutChildLine; QString m_receivedStdOutChildLine;
Tasks m_receivedTasks; Tasks m_receivedTasks;
QString m_receivedOutput; QString m_receivedOutput;
TestTerminator *m_terminator = nullptr;
friend class TestTerminator; friend class TestTerminator;
}; };
@@ -82,7 +81,7 @@ public:
TestTerminator(OutputParserTester *t); TestTerminator(OutputParserTester *t);
private: private:
void handleLine(const QString &line, Utils::OutputFormat type) override; Status doHandleLine(const QString &line, Utils::OutputFormat type) override;
OutputParserTester *m_tester = nullptr; OutputParserTester *m_tester = nullptr;
}; };

View File

@@ -151,12 +151,14 @@ static void parse(QFutureInterface<void> &future, const QString &output,
void ParseIssuesDialog::accept() void ParseIssuesDialog::accept()
{ {
std::unique_ptr<IOutputParser> parser(d->kitChooser.currentKit()->createOutputParser()); const QList<IOutputParser *> lineParsers = d->kitChooser.currentKit()->createOutputParsers();
if (!parser) { if (lineParsers.isEmpty()) {
QMessageBox::critical(this, tr("Cannot Parse"), tr("Cannot parse: The chosen kit does " QMessageBox::critical(this, tr("Cannot Parse"), tr("Cannot parse: The chosen kit does "
"not provide an output parser.")); "not provide an output parser."));
return; return;
} }
std::unique_ptr<IOutputParser> parser(new IOutputParser);
parser->setLineParsers(lineParsers);
if (d->clearTasksCheckBox.isChecked()) if (d->clearTasksCheckBox.isChecked())
TaskHub::clearTasks(); TaskHub::clearTasks();
connect(parser.get(), &IOutputParser::addTask, [](const Task &t) { TaskHub::addTask(t); }); connect(parser.get(), &IOutputParser::addTask, [](const Task &t) { TaskHub::addTask(t); });

View File

@@ -100,7 +100,7 @@ ProcessStep::ProcessStep(BuildStepList *bsl, Core::Id id)
bool ProcessStep::init() bool ProcessStep::init()
{ {
setupProcessParameters(processParameters()); setupProcessParameters(processParameters());
setOutputParser(target()->kit()->createOutputParser()); appendOutputParsers(target()->kit()->createOutputParsers());
return AbstractProcessStep::init(); return AbstractProcessStep::init();
} }

View File

@@ -150,7 +150,7 @@ public:
Core::Id language() const; Core::Id language() const;
virtual Utils::FilePath compilerCommand() const = 0; virtual Utils::FilePath compilerCommand() const = 0;
virtual IOutputParser *outputParser() const = 0; virtual QList<IOutputParser *> outputParsers() const = 0;
virtual bool operator ==(const ToolChain &) const; virtual bool operator ==(const ToolChain &) const;

View File

@@ -324,7 +324,7 @@ public:
void addToEnvironment(Environment &env) const override { Q_UNUSED(env) } void addToEnvironment(Environment &env) const override { Q_UNUSED(env) }
FilePath makeCommand(const Environment &) const override { return FilePath::fromString("make"); } FilePath makeCommand(const Environment &) const override { return FilePath::fromString("make"); }
FilePath compilerCommand() const override { return Utils::FilePath::fromString("/tmp/test/gcc"); } 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; } std::unique_ptr<ToolChainConfigWidget> createConfigurationWidget() override { return nullptr; }
bool operator ==(const ToolChain &other) const override { bool operator ==(const ToolChain &other) const override {
if (!ToolChain::operator==(other)) if (!ToolChain::operator==(other))

View File

@@ -52,32 +52,21 @@ XcodebuildParser::XcodebuildParser()
QTC_CHECK(m_buildRe.isValid()); QTC_CHECK(m_buildRe.isValid());
} }
void XcodebuildParser::handleLine(const QString &line, OutputFormat type) IOutputParser::Status XcodebuildParser::doHandleLine(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)
{ {
const QString lne = rightTrimmed(line); const QString lne = rightTrimmed(line);
if (type == StdOutFormat) {
if (m_buildRe.indexIn(lne) > -1) { if (m_buildRe.indexIn(lne) > -1) {
m_xcodeBuildParserState = InXcodebuild; m_xcodeBuildParserState = InXcodebuild;
m_lastTarget = m_buildRe.cap(2); m_lastTarget = m_buildRe.cap(2);
m_lastProject = m_buildRe.cap(3); m_lastProject = m_buildRe.cap(3);
return; return Status::Done;
} }
if (m_xcodeBuildParserState == InXcodebuild || m_xcodeBuildParserState == UnknownXcodebuildState) { if (m_xcodeBuildParserState == InXcodebuild
|| m_xcodeBuildParserState == UnknownXcodebuildState) {
if (m_successRe.indexIn(lne) > -1) { if (m_successRe.indexIn(lne) > -1) {
m_xcodeBuildParserState = OutsideXcodebuild; m_xcodeBuildParserState = OutsideXcodebuild;
return; return Status::Done;
} }
if (lne.endsWith(QLatin1String(signatureChangeEndsWithPattern))) { if (lne.endsWith(QLatin1String(signatureChangeEndsWithPattern))) {
CompileTask task(Task::Warning, CompileTask task(Task::Warning,
@@ -85,28 +74,25 @@ void XcodebuildParser::stdOutput(const QString &line)
absoluteFilePath(FilePath::fromString( absoluteFilePath(FilePath::fromString(
lne.left(lne.size() - QLatin1String(signatureChangeEndsWithPattern).size())))); lne.left(lne.size() - QLatin1String(signatureChangeEndsWithPattern).size()))));
emit addTask(task, 1); emit addTask(task, 1);
return; 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) { if (m_failureRe.indexIn(lne) > -1) {
++m_fatalErrorCount; ++m_fatalErrorCount;
m_xcodeBuildParserState = UnknownXcodebuildState; m_xcodeBuildParserState = UnknownXcodebuildState;
// unfortunately the m_lastTarget, m_lastProject might not be in sync // unfortunately the m_lastTarget, m_lastProject might not be in sync
emit addTask(CompileTask(Task::Error, tr("Xcodebuild failed."))); emit addTask(CompileTask(Task::Error, tr("Xcodebuild failed.")));
return;
} }
if (m_xcodeBuildParserState == OutsideXcodebuild) { // also forward if UnknownXcodebuildState ? if (m_xcodeBuildParserState == OutsideXcodebuild)
IOutputParser::handleLine(line, StdErrFormat); return Status::NotHandled;
return; return Status::Done;
} }
bool XcodebuildParser::hasDetectedRedirection() const
{
return m_xcodeBuildParserState != OutsideXcodebuild;
} }
} // namespace ProjectExplorer } // namespace ProjectExplorer
@@ -268,7 +254,7 @@ void ProjectExplorerPlugin::testXcodebuildParserParsing()
connect(&testbench, &OutputParserTester::aboutToDeleteParser, connect(&testbench, &OutputParserTester::aboutToDeleteParser,
tester, &XcodebuildParserTester::onAboutToDeleteParser); tester, &XcodebuildParserTester::onAboutToDeleteParser);
testbench.appendOutputParser(childParser); testbench.addLineParser(childParser);
QFETCH(ProjectExplorer::XcodebuildParser::XcodebuildStatus, initialStatus); QFETCH(ProjectExplorer::XcodebuildParser::XcodebuildStatus, initialStatus);
QFETCH(QString, input); QFETCH(QString, input);
QFETCH(OutputParserTester::Channel, inputChannel); QFETCH(OutputParserTester::Channel, inputChannel);

View File

@@ -47,11 +47,9 @@ public:
XcodebuildParser(); XcodebuildParser();
private: private:
void handleLine(const QString &line, Utils::OutputFormat type) override; Status doHandleLine(const QString &line, Utils::OutputFormat type) override;
bool hasFatalErrors() const override; bool hasDetectedRedirection() const override;
bool hasFatalErrors() const override { return m_fatalErrorCount > 0; }
void stdOutput(const QString &line);
void stdError(const QString &line);
int m_fatalErrorCount = 0; int m_fatalErrorCount = 0;
QRegExp m_failureRe; QRegExp m_failureRe;

View File

@@ -169,8 +169,8 @@ bool QbsBuildStep::init()
return false; return false;
delete m_parser; delete m_parser;
m_parser = target()->kit()->createOutputParser(); m_parser = new IOutputParser;
if (m_parser) m_parser->setLineParsers(target()->kit()->createOutputParsers());
connect(m_parser, &ProjectExplorer::IOutputParser::addTask, this, &QbsBuildStep::addTask); connect(m_parser, &ProjectExplorer::IOutputParser::addTask, this, &QbsBuildStep::addTask);
m_changedFiles = bc->changedFiles(); m_changedFiles = bc->changedFiles();

View File

@@ -166,14 +166,22 @@ bool QmakeMakeStep::init()
setOutputParser(new ProjectExplorer::GnuMakeParser()); setOutputParser(new ProjectExplorer::GnuMakeParser());
ToolChain *tc = ToolChainKitAspect::cxxToolChain(target()->kit()); ToolChain *tc = ToolChainKitAspect::cxxToolChain(target()->kit());
if (tc && tc->targetAbi().os() == Abi::DarwinOS) IOutputParser *xcodeBuildParser = nullptr;
appendOutputParser(new XcodebuildParser); if (tc && tc->targetAbi().os() == Abi::DarwinOS) {
IOutputParser *parser = target()->kit()->createOutputParser(); xcodeBuildParser = new XcodebuildParser;
if (parser) appendOutputParser(xcodeBuildParser);
appendOutputParser(parser); }
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()); 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()); auto rootNode = dynamic_cast<QmakeProFileNode *>(project()->rootProjectNode());
QTC_ASSERT(rootNode, return false); QTC_ASSERT(rootNode, return false);

View File

@@ -40,12 +40,10 @@ QMakeParser::QMakeParser() : m_error(QLatin1String("^(.+):(\\d+):\\s(.+)$"))
m_error.setMinimal(true); 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) { if (type != Utils::StdErrFormat)
IOutputParser::handleLine(line, type); return Status::NotHandled;
return;
}
QString lne = rightTrimmed(line); QString lne = rightTrimmed(line);
if (m_error.indexIn(lne) > -1) { if (m_error.indexIn(lne) > -1) {
QString fileName = m_error.cap(1); QString fileName = m_error.cap(1);
@@ -68,21 +66,21 @@ void QMakeParser::handleLine(const QString &line, OutputFormat type)
absoluteFilePath(FilePath::fromUserInput(fileName)), absoluteFilePath(FilePath::fromUserInput(fileName)),
m_error.cap(2).toInt() /* line */), m_error.cap(2).toInt() /* line */),
1); 1);
return; return Status::Done;
} }
if (lne.startsWith(QLatin1String("Project ERROR: ")) if (lne.startsWith(QLatin1String("Project ERROR: "))
|| lne.startsWith(QLatin1String("ERROR: "))) { || lne.startsWith(QLatin1String("ERROR: "))) {
const QString description = lne.mid(lne.indexOf(QLatin1Char(':')) + 2); const QString description = lne.mid(lne.indexOf(QLatin1Char(':')) + 2);
emit addTask(BuildSystemTask(Task::Error, description), 1); emit addTask(BuildSystemTask(Task::Error, description), 1);
return; return Status::Done;
} }
if (lne.startsWith(QLatin1String("Project WARNING: ")) if (lne.startsWith(QLatin1String("Project WARNING: "))
|| lne.startsWith(QLatin1String("WARNING: "))) { || lne.startsWith(QLatin1String("WARNING: "))) {
const QString description = lne.mid(lne.indexOf(QLatin1Char(':')) + 2); const QString description = lne.mid(lne.indexOf(QLatin1Char(':')) + 2);
emit addTask(BuildSystemTask(Task::Warning, description), 1); emit addTask(BuildSystemTask(Task::Warning, description), 1);
return; return Status::Done;
} }
IOutputParser::handleLine(line, StdErrFormat); return Status::NotHandled;
} }
} // QmakeProjectManager } // QmakeProjectManager
@@ -183,7 +181,7 @@ void QmakeProjectManagerPlugin::testQmakeOutputParsers_data()
void QmakeProjectManagerPlugin::testQmakeOutputParsers() void QmakeProjectManagerPlugin::testQmakeOutputParsers()
{ {
OutputParserTester testbench; OutputParserTester testbench;
testbench.appendOutputParser(new QMakeParser); testbench.addLineParser(new QMakeParser);
QFETCH(QString, input); QFETCH(QString, input);
QFETCH(OutputParserTester::Channel, inputChannel); QFETCH(OutputParserTester::Channel, inputChannel);
QFETCH(Tasks, tasks); QFETCH(Tasks, tasks);

View File

@@ -41,7 +41,7 @@ public:
QMakeParser(); QMakeParser();
private: private:
void handleLine(const QString &line, Utils::OutputFormat type) override; Status doHandleLine(const QString &line, Utils::OutputFormat type) override;
QRegExp m_error; QRegExp m_error;
}; };

View File

@@ -288,14 +288,11 @@ void QtKitAspect::addToEnvironment(const ProjectExplorer::Kit *k, Utils::Environ
version->addToEnvironment(k, env); version->addToEnvironment(k, env);
} }
ProjectExplorer::IOutputParser *QtKitAspect::createOutputParser(const ProjectExplorer::Kit *k) const QList<IOutputParser *> QtKitAspect::createOutputParsers(const Kit *k) const
{ {
if (qtVersion(k)) { if (qtVersion(k))
const auto parser = new Internal::QtTestParser; return {new Internal::QtTestParser, new QtParser};
parser->appendOutputParser(new QtParser); return {};
return parser;
}
return nullptr;
} }
class QtMacroSubProvider class QtMacroSubProvider

View File

@@ -54,7 +54,7 @@ public:
ItemList toUserOutput(const ProjectExplorer::Kit *k) const override; ItemList toUserOutput(const ProjectExplorer::Kit *k) const override;
void addToEnvironment(const ProjectExplorer::Kit *k, Utils::Environment &env) 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; void addToMacroExpander(ProjectExplorer::Kit *kit, Utils::MacroExpander *expander) const override;
static Core::Id id(); static Core::Id id();

View File

@@ -43,12 +43,11 @@ QtParser::QtParser() :
m_translationRegExp.setMinimal(true); 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) { if (type != Utils::StdErrFormat)
IOutputParser::handleLine(line, type); return Status::NotHandled;
return;
}
QString lne = rightTrimmed(line); QString lne = rightTrimmed(line);
if (m_mocRegExp.indexIn(lne) > -1) { if (m_mocRegExp.indexIn(lne) > -1) {
bool ok; bool ok;
@@ -65,7 +64,7 @@ void QtParser::handleLine(const QString &line, Utils::OutputFormat type)
absoluteFilePath(Utils::FilePath::fromUserInput(m_mocRegExp.cap(1))), absoluteFilePath(Utils::FilePath::fromUserInput(m_mocRegExp.cap(1))),
lineno); lineno);
emit addTask(task, 1); emit addTask(task, 1);
return; return Status::Done;
} }
if (m_translationRegExp.indexIn(lne) > -1) { if (m_translationRegExp.indexIn(lne) > -1) {
Task::TaskType type = Task::Warning; 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), CompileTask task(type, m_translationRegExp.cap(2),
absoluteFilePath(Utils::FilePath::fromUserInput(m_translationRegExp.cap(3)))); absoluteFilePath(Utils::FilePath::fromUserInput(m_translationRegExp.cap(3))));
emit addTask(task, 1); emit addTask(task, 1);
return; return Status::Done;
} }
IOutputParser::handleLine(line, Utils::StdErrFormat); return Status::NotHandled;
} }
// Unit tests: // Unit tests:
@@ -179,7 +178,7 @@ void QtSupportPlugin::testQtOutputParser_data()
void QtSupportPlugin::testQtOutputParser() void QtSupportPlugin::testQtOutputParser()
{ {
OutputParserTester testbench; OutputParserTester testbench;
testbench.appendOutputParser(new QtParser); testbench.addLineParser(new QtParser);
QFETCH(QString, input); QFETCH(QString, input);
QFETCH(OutputParserTester::Channel, inputChannel); QFETCH(OutputParserTester::Channel, inputChannel);
QFETCH(Tasks, tasks); QFETCH(Tasks, tasks);

View File

@@ -42,7 +42,7 @@ public:
QtParser(); QtParser();
private: private:
void handleLine(const QString &line, Utils::OutputFormat type) override; Status doHandleLine(const QString &line, Utils::OutputFormat type) override;
QRegExp m_mocRegExp; QRegExp m_mocRegExp;
QRegExp m_translationRegExp; QRegExp m_translationRegExp;

View File

@@ -47,12 +47,11 @@ using namespace Utils;
namespace QtSupport { namespace QtSupport {
namespace Internal { namespace Internal {
void QtTestParser::handleLine(const QString &line, OutputFormat type) IOutputParser::Status QtTestParser::doHandleLine(const QString &line, OutputFormat type)
{ {
if (type != StdOutFormat) { if (type != StdOutFormat)
IOutputParser::handleLine(line, type); return Status::NotHandled;
return;
}
const QString theLine = rightTrimmed(line); const QString theLine = rightTrimmed(line);
static const QRegularExpression triggerPattern("^(?:XPASS|FAIL!) : .+$"); static const QRegularExpression triggerPattern("^(?:XPASS|FAIL!) : .+$");
QTC_CHECK(triggerPattern.isValid()); QTC_CHECK(triggerPattern.isValid());
@@ -60,12 +59,10 @@ void QtTestParser::handleLine(const QString &line, OutputFormat type)
emitCurrentTask(); emitCurrentTask();
m_currentTask = Task(Task::Error, theLine, FilePath(), -1, m_currentTask = Task(Task::Error, theLine, FilePath(), -1,
Constants::TASK_CATEGORY_AUTOTEST); Constants::TASK_CATEGORY_AUTOTEST);
return; return Status::InProgress;
}
if (m_currentTask.isNull()) {
IOutputParser::handleLine(line, StdOutFormat);
return;
} }
if (m_currentTask.isNull())
return Status::NotHandled;
static const QRegularExpression locationPattern(HostOsInfo::isWindowsHost() static const QRegularExpression locationPattern(HostOsInfo::isWindowsHost()
? QString(QT_TEST_FAIL_WIN_REGEXP) ? QString(QT_TEST_FAIL_WIN_REGEXP)
: QString(QT_TEST_FAIL_UNIX_REGEXP)); : QString(QT_TEST_FAIL_UNIX_REGEXP));
@@ -76,9 +73,10 @@ void QtTestParser::handleLine(const QString &line, OutputFormat type)
QDir::fromNativeSeparators(match.captured("file")))); QDir::fromNativeSeparators(match.captured("file"))));
m_currentTask.line = match.captured("line").toInt(); m_currentTask.line = match.captured("line").toInt();
emitCurrentTask(); emitCurrentTask();
return; return Status::Done;
} }
m_currentTask.description.append('\n').append(theLine); m_currentTask.description.append('\n').append(theLine);
return Status::InProgress;
} }
void QtTestParser::emitCurrentTask() void QtTestParser::emitCurrentTask()
@@ -93,7 +91,7 @@ void QtTestParser::emitCurrentTask()
void QtSupportPlugin::testQtTestOutputParser() void QtSupportPlugin::testQtTestOutputParser()
{ {
OutputParserTester testbench; OutputParserTester testbench;
testbench.appendOutputParser(new QtTestParser); testbench.addLineParser(new QtTestParser);
const QString input = "random output\n" const QString input = "random output\n"
"PASS : MyTest::someTest()\n" "PASS : MyTest::someTest()\n"
"XPASS : MyTest::someTest()\n" "XPASS : MyTest::someTest()\n"

View File

@@ -35,7 +35,7 @@ class QtTestParser : public ProjectExplorer::IOutputParser
{ {
Q_OBJECT Q_OBJECT
private: private:
void handleLine(const QString &line, Utils::OutputFormat type) override; Status doHandleLine(const QString &line, Utils::OutputFormat type) override;
void doFlush() override { emitCurrentTask(); } void doFlush() override { emitCurrentTask(); }
void emitCurrentTask(); void emitCurrentTask();

View File

@@ -54,19 +54,20 @@ CompilerOutputProcessor::~CompilerOutputProcessor()
void CompilerOutputProcessor::start() void CompilerOutputProcessor::start()
{ {
ProjectExplorer::OsParser parser; ProjectExplorer::IOutputParser parser;
parser.appendOutputParser(new QmakeProjectManager::QMakeParser); parser.addLineParser(new ProjectExplorer::OsParser);
parser.appendOutputParser(new ProjectExplorer::GnuMakeParser); parser.addLineParser(new QmakeProjectManager::QMakeParser);
parser.appendOutputParser(new QtSupport::QtParser); parser.addLineParser(new ProjectExplorer::GnuMakeParser);
parser.addLineParser(new QtSupport::QtParser);
switch (m_compilerType) { switch (m_compilerType) {
case CompilerTypeGcc: case CompilerTypeGcc:
parser.appendOutputParser(new ProjectExplorer::GccParser); parser.addLineParsers(ProjectExplorer::GccParser::gccParserSuite());
break; break;
case CompilerTypeClang: case CompilerTypeClang:
parser.appendOutputParser(new ProjectExplorer::ClangParser); parser.addLineParsers(ProjectExplorer::ClangParser::clangParserSuite());
break; break;
case CompilerTypeMsvc: case CompilerTypeMsvc:
parser.appendOutputParser(new ProjectExplorer::MsvcParser); parser.addLineParser(new ProjectExplorer::MsvcParser);
break; break;
} }