OutputParser: Allow to flush pending tasks.

Add a way to flush out tasks from the outputparsers. This is necessary
to make parsers that keep state more robust.

Flush parsers (once) before adding any new task. This keeps the sequence
intact.

Flush all parsers once the parsing is done to make sure there is no task
queued somewhere.

Task-number: QTCREATORBUG-9195
Change-Id: Icd37df1f470cb73123ad286d6900ad1047a1d512
Reviewed-by: Tobias Hunger <tobias.hunger@digia.com>
This commit is contained in:
Tobias Hunger
2013-05-03 16:08:00 +02:00
parent 14763118a5
commit 8bbad43019
13 changed files with 93 additions and 69 deletions

View File

@@ -88,7 +88,7 @@ using namespace ProjectExplorer;
AbstractProcessStep::AbstractProcessStep(BuildStepList *bsl, const Core::Id id) : AbstractProcessStep::AbstractProcessStep(BuildStepList *bsl, const Core::Id id) :
BuildStep(bsl, id), m_timer(0), m_futureInterface(0), BuildStep(bsl, id), m_timer(0), m_futureInterface(0),
m_ignoreReturnValue(false), m_process(0), m_ignoreReturnValue(false), m_process(0),
m_outputParserChain(0) m_outputParserChain(0), m_skipFlush(false)
{ {
} }
@@ -96,7 +96,7 @@ AbstractProcessStep::AbstractProcessStep(BuildStepList *bsl,
AbstractProcessStep *bs) : AbstractProcessStep *bs) :
BuildStep(bsl, bs), m_timer(0), m_futureInterface(0), BuildStep(bsl, bs), m_timer(0), m_futureInterface(0),
m_ignoreReturnValue(bs->m_ignoreReturnValue), m_ignoreReturnValue(bs->m_ignoreReturnValue),
m_process(0), m_outputParserChain(0) m_process(0), m_outputParserChain(0), m_skipFlush(false)
{ {
} }
@@ -261,6 +261,9 @@ void AbstractProcessStep::processStarted()
void AbstractProcessStep::processFinished(int exitCode, QProcess::ExitStatus status) void AbstractProcessStep::processFinished(int exitCode, QProcess::ExitStatus status)
{ {
if (m_outputParserChain)
m_outputParserChain->flush();
QString command = QDir::toNativeSeparators(m_param.effectiveCommand()); QString command = QDir::toNativeSeparators(m_param.effectiveCommand());
if (status == QProcess::NormalExit && exitCode == 0) { if (status == QProcess::NormalExit && exitCode == 0) {
emit addOutput(tr("The process \"%1\" exited normally.").arg(command), emit addOutput(tr("The process \"%1\" exited normally.").arg(command),
@@ -367,6 +370,13 @@ void AbstractProcessStep::taskAdded(const ProjectExplorer::Task &task)
if (m_ignoreReturnValue) if (m_ignoreReturnValue)
return; return;
// flush out any pending tasks before proceeding:
if (!m_skipFlush && m_outputParserChain) {
m_skipFlush = true;
m_outputParserChain->flush();
m_skipFlush = false;
}
Task editable(task); Task editable(task);
QString filePath = task.file.toString(); QString filePath = task.file.toString();
if (!filePath.isEmpty() && !QDir::isAbsolutePath(filePath)) { if (!filePath.isEmpty() && !QDir::isAbsolutePath(filePath)) {

View File

@@ -101,6 +101,7 @@ private:
QEventLoop *m_eventLoop; QEventLoop *m_eventLoop;
ProjectExplorer::IOutputParser *m_outputParserChain; ProjectExplorer::IOutputParser *m_outputParserChain;
bool m_killProcess; bool m_killProcess;
bool m_skipFlush;
}; };
} // namespace ProjectExplorer } // namespace ProjectExplorer

View File

@@ -52,7 +52,7 @@ void ClangParser::stdError(const QString &line)
{ {
const QString lne = rightTrimmed(line); const QString lne = rightTrimmed(line);
if (m_summaryRegExp.indexIn(lne) > -1) { if (m_summaryRegExp.indexIn(lne) > -1) {
emitTask(); doFlush();
m_expectSnippet = false; m_expectSnippet = false;
return; return;
} }
@@ -74,11 +74,11 @@ void ClangParser::stdError(const QString &line)
if (m_inLineRegExp.indexIn(lne) > -1) { if (m_inLineRegExp.indexIn(lne) > -1) {
m_expectSnippet = true; m_expectSnippet = true;
newTask(Task::Unknown, newTask(Task(Task::Unknown,
lne.trimmed(), lne.trimmed(),
Utils::FileName::fromUserInput(m_inLineRegExp.cap(2)), /* filename */ Utils::FileName::fromUserInput(m_inLineRegExp.cap(2)), /* filename */
m_inLineRegExp.cap(3).toInt(), /* line */ m_inLineRegExp.cap(3).toInt(), /* line */
Core::Id(Constants::TASK_CATEGORY_COMPILE)); Core::Id(Constants::TASK_CATEGORY_COMPILE)));
return; return;
} }

View File

@@ -60,11 +60,6 @@ GccParser::GccParser()
appendOutputParser(new LdParser); appendOutputParser(new LdParser);
} }
GccParser::~GccParser()
{
emitTask();
}
void GccParser::stdError(const QString &line) void GccParser::stdError(const QString &line)
{ {
QString lne = rightTrimmed(line); QString lne = rightTrimmed(line);
@@ -79,11 +74,11 @@ void GccParser::stdError(const QString &line)
// Handle misc issues: // Handle misc issues:
if (lne.startsWith(QLatin1String("ERROR:")) || if (lne.startsWith(QLatin1String("ERROR:")) ||
lne == QLatin1String("* cpp failed")) { lne == QLatin1String("* cpp failed")) {
newTask(Task::Error, newTask(Task(Task::Error,
lne /* description */, lne /* description */,
Utils::FileName() /* filename */, Utils::FileName() /* filename */,
-1 /* linenumber */, -1 /* linenumber */,
Core::Id(Constants::TASK_CATEGORY_COMPILE)); Core::Id(Constants::TASK_CATEGORY_COMPILE)));
return; return;
} else if (m_regExpGccNames.indexIn(lne) > -1) { } else if (m_regExpGccNames.indexIn(lne) > -1) {
QString description = lne.mid(m_regExpGccNames.matchedLength()); QString description = lne.mid(m_regExpGccNames.matchedLength());
@@ -121,44 +116,40 @@ void GccParser::stdError(const QString &line)
newTask(task); newTask(task);
return; return;
} else if (m_regExpIncluded.indexIn(lne) > -1) { } else if (m_regExpIncluded.indexIn(lne) > -1) {
newTask(Task::Unknown, newTask(Task(Task::Unknown,
lne.trimmed() /* description */, lne.trimmed() /* description */,
Utils::FileName::fromUserInput(m_regExpIncluded.cap(1)) /* filename */, Utils::FileName::fromUserInput(m_regExpIncluded.cap(1)) /* filename */,
m_regExpIncluded.cap(3).toInt() /* linenumber */, m_regExpIncluded.cap(3).toInt() /* linenumber */,
Core::Id(Constants::TASK_CATEGORY_COMPILE)); Core::Id(Constants::TASK_CATEGORY_COMPILE)));
return; return;
} else if (lne.startsWith(QLatin1Char(' '))) { } else if (lne.startsWith(QLatin1Char(' '))) {
amendDescription(lne, true); amendDescription(lne, true);
return; return;
} }
emitTask(); doFlush();
IOutputParser::stdError(line); IOutputParser::stdError(line);
} }
void GccParser::stdOutput(const QString &line) void GccParser::stdOutput(const QString &line)
{ {
emitTask(); doFlush();
IOutputParser::stdOutput(line); IOutputParser::stdOutput(line);
} }
void GccParser::newTask(const Task &task) void GccParser::newTask(const Task &task)
{ {
emitTask(); doFlush();
m_currentTask = task; m_currentTask = task;
} }
void GccParser::newTask(Task::TaskType type_, const QString &description_, void GccParser::doFlush()
const Utils::FileName &file_, int line_, const Core::Id &category_)
{ {
newTask(Task(type_, description_, file_, line_, category_)); if (m_currentTask.isNull())
} return;
Task t = m_currentTask;
void GccParser::emitTask() m_currentTask.clear();
{ emit addTask(t);
if (!m_currentTask.isNull())
emit addTask(m_currentTask);
m_currentTask = Task();
} }
void GccParser::amendDescription(const QString &desc, bool monospaced) void GccParser::amendDescription(const QString &desc, bool monospaced)

View File

@@ -44,15 +44,13 @@ class GccParser : public ProjectExplorer::IOutputParser
public: public:
GccParser(); GccParser();
~GccParser();
void stdError(const QString &line); void stdError(const QString &line);
void stdOutput(const QString &line); void stdOutput(const QString &line);
protected: protected:
void newTask(const Task &task); void newTask(const Task &task);
void newTask(Task::TaskType type_, const QString &description_, void doFlush();
const Utils::FileName &file_, int line_, const Core::Id &category_);
void emitTask();
void amendDescription(const QString &desc, bool monospaced); void amendDescription(const QString &desc, bool monospaced);

View File

@@ -90,7 +90,7 @@ void GnuMakeParser::stdError(const QString &line)
++m_fatalErrorCount; ++m_fatalErrorCount;
if (!m_suppressIssues) { if (!m_suppressIssues) {
m_suppressIssues = true; m_suppressIssues = true;
addTask(Task(Task::Error, emit addTask(Task(Task::Error,
m_makefileError.cap(3), m_makefileError.cap(3),
Utils::FileName::fromUserInput(m_makefileError.cap(1)), Utils::FileName::fromUserInput(m_makefileError.cap(1)),
m_makefileError.cap(2).toInt(), m_makefileError.cap(2).toInt(),
@@ -110,7 +110,7 @@ void GnuMakeParser::stdError(const QString &line)
type = Task::Warning; type = Task::Warning;
} }
addTask(Task(type, description, emit addTask(Task(type, description,
Utils::FileName() /* filename */, Utils::FileName() /* filename */,
-1, /* line */ -1, /* line */
Core::Id(Constants::TASK_CATEGORY_BUILDSYSTEM))); Core::Id(Constants::TASK_CATEGORY_BUILDSYSTEM)));

View File

@@ -108,6 +108,17 @@
This method can be overwritten to change the task. This method can be overwritten to change the task.
*/ */
/*!
\fn void ProjectExplorer::IOutputParser::doFlush()
\brief This method is called whenever a parser is supposed to flush his state.
Parsers may have state (e.g. because they need to aggregate several lines into one task). This
method is called when this state needs to be flushed out to be visible.
doFlush() is called by flush(). flush() is called on childparsers whenever a new task is added.
It is also called once when all input has been parsed.
*/
namespace ProjectExplorer { namespace ProjectExplorer {
IOutputParser::IOutputParser() : m_parser(0) IOutputParser::IOutputParser() : m_parser(0)
@@ -180,6 +191,9 @@ void IOutputParser::taskAdded(const ProjectExplorer::Task &task)
emit addTask(task); emit addTask(task);
} }
void IOutputParser::doFlush()
{ }
bool IOutputParser::hasFatalErrors() const bool IOutputParser::hasFatalErrors() const
{ {
return false || (m_parser && m_parser->hasFatalErrors()); return false || (m_parser && m_parser->hasFatalErrors());
@@ -191,6 +205,13 @@ void IOutputParser::setWorkingDirectory(const QString &workingDirectory)
m_parser->setWorkingDirectory(workingDirectory); m_parser->setWorkingDirectory(workingDirectory);
} }
void IOutputParser::flush()
{
doFlush();
if (m_parser)
m_parser->flush();
}
QString IOutputParser::rightTrimmed(const QString &in) QString IOutputParser::rightTrimmed(const QString &in)
{ {
int pos = in.length(); int pos = in.length();

View File

@@ -44,7 +44,7 @@ class PROJECTEXPLORER_EXPORT IOutputParser : public QObject
Q_OBJECT Q_OBJECT
public: public:
IOutputParser(); IOutputParser();
virtual ~IOutputParser(); ~IOutputParser();
virtual void appendOutputParser(IOutputParser *parser); virtual void appendOutputParser(IOutputParser *parser);
@@ -60,6 +60,8 @@ public:
// For GnuMakeParser // For GnuMakeParser
virtual void setWorkingDirectory(const QString &workingDirectory); virtual void setWorkingDirectory(const QString &workingDirectory);
void flush(); // flush out pending tasks
static QString rightTrimmed(const QString &in); static QString rightTrimmed(const QString &in);
signals: signals:
@@ -71,6 +73,8 @@ public slots:
virtual void taskAdded(const ProjectExplorer::Task &task); virtual void taskAdded(const ProjectExplorer::Task &task);
private: private:
virtual void doFlush();
IOutputParser *m_parser; IOutputParser *m_parser;
}; };

View File

@@ -58,12 +58,6 @@ LinuxIccParser::LinuxIccParser()
appendOutputParser(new LdParser); appendOutputParser(new LdParser);
} }
LinuxIccParser::~LinuxIccParser()
{
if (!m_temporary.isNull())
addTask(m_temporary);
}
void LinuxIccParser::stdError(const QString &line) void LinuxIccParser::stdError(const QString &line)
{ {
if (m_expectFirstLine && m_firstLine.indexIn(line) != -1) { if (m_expectFirstLine && m_firstLine.indexIn(line) != -1) {
@@ -107,6 +101,15 @@ void LinuxIccParser::stdError(const QString &line)
} }
} }
void LinuxIccParser::doFlush()
{
if (m_temporary.isNull())
return;
Task t = m_temporary;
m_temporary.clear();
emit addTask(t);
}
#ifdef WITH_TESTS #ifdef WITH_TESTS
# include <QTest> # include <QTest>
# include "projectexplorer.h" # include "projectexplorer.h"

View File

@@ -43,11 +43,12 @@ class LinuxIccParser : public ProjectExplorer::IOutputParser
public: public:
LinuxIccParser(); LinuxIccParser();
~LinuxIccParser();
void stdError(const QString &line); void stdError(const QString &line);
private: private:
void doFlush();
QRegExp m_firstLine; QRegExp m_firstLine;
QRegExp m_continuationLines; QRegExp m_continuationLines;
QRegExp m_caretLine; QRegExp m_caretLine;

View File

@@ -69,11 +69,6 @@ MsvcParser::MsvcParser()
m_additionalInfoRegExp.setMinimal(true); m_additionalInfoRegExp.setMinimal(true);
} }
MsvcParser::~MsvcParser()
{
sendQueuedTask();
}
void MsvcParser::stdOutput(const QString &line) void MsvcParser::stdOutput(const QString &line)
{ {
int infoPos = m_additionalInfoRegExp.indexIn(line); int infoPos = m_additionalInfoRegExp.indexIn(line);
@@ -141,7 +136,7 @@ void MsvcParser::stdError(const QString &line)
bool MsvcParser::processCompileLine(const QString &line) bool MsvcParser::processCompileLine(const QString &line)
{ {
sendQueuedTask(); doFlush();
if (m_compileRegExp.indexIn(line) > -1) { if (m_compileRegExp.indexIn(line) > -1) {
QPair<Utils::FileName, int> position = parseFileName( m_compileRegExp.cap(1)); QPair<Utils::FileName, int> position = parseFileName( m_compileRegExp.cap(1));
@@ -159,13 +154,14 @@ bool MsvcParser::processCompileLine(const QString &line)
return false; return false;
} }
void MsvcParser::sendQueuedTask() void MsvcParser::doFlush()
{ {
if (m_lastTask.isNull()) if (m_lastTask.isNull())
return; return;
addTask(m_lastTask); Task t = m_lastTask;
m_lastTask = Task(); m_lastTask.clear();
emit addTask(t);
} }
// Unit tests: // Unit tests:

View File

@@ -44,14 +44,12 @@ class MsvcParser : public ProjectExplorer::IOutputParser
public: public:
MsvcParser(); MsvcParser();
~MsvcParser();
void stdOutput(const QString &line); void stdOutput(const QString &line);
void stdError(const QString &line); void stdError(const QString &line);
void flush();
private: private:
void sendQueuedTask(); void doFlush();
bool processCompileLine(const QString &line); bool processCompileLine(const QString &line);
QRegExp m_compileRegExp; QRegExp m_compileRegExp;

View File

@@ -58,6 +58,7 @@ void OutputParserTester::testParsing(const QString &lines,
else else
childParser()->stdError(input + QLatin1Char('\n')); childParser()->stdError(input + QLatin1Char('\n'));
} }
childParser()->flush();
// first disconnect ourselves from the end of the parser chain again // first disconnect ourselves from the end of the parser chain again
IOutputParser * parser = this; IOutputParser * parser = this;