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) :
BuildStep(bsl, id), m_timer(0), m_futureInterface(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) :
BuildStep(bsl, bs), m_timer(0), m_futureInterface(0),
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)
{
if (m_outputParserChain)
m_outputParserChain->flush();
QString command = QDir::toNativeSeparators(m_param.effectiveCommand());
if (status == QProcess::NormalExit && exitCode == 0) {
emit addOutput(tr("The process \"%1\" exited normally.").arg(command),
@@ -367,6 +370,13 @@ void AbstractProcessStep::taskAdded(const ProjectExplorer::Task &task)
if (m_ignoreReturnValue)
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);
QString filePath = task.file.toString();
if (!filePath.isEmpty() && !QDir::isAbsolutePath(filePath)) {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -108,6 +108,17 @@
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 {
IOutputParser::IOutputParser() : m_parser(0)
@@ -180,6 +191,9 @@ void IOutputParser::taskAdded(const ProjectExplorer::Task &task)
emit addTask(task);
}
void IOutputParser::doFlush()
{ }
bool IOutputParser::hasFatalErrors() const
{
return false || (m_parser && m_parser->hasFatalErrors());
@@ -191,6 +205,13 @@ void IOutputParser::setWorkingDirectory(const QString &workingDirectory)
m_parser->setWorkingDirectory(workingDirectory);
}
void IOutputParser::flush()
{
doFlush();
if (m_parser)
m_parser->flush();
}
QString IOutputParser::rightTrimmed(const QString &in)
{
int pos = in.length();

View File

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

View File

@@ -58,12 +58,6 @@ LinuxIccParser::LinuxIccParser()
appendOutputParser(new LdParser);
}
LinuxIccParser::~LinuxIccParser()
{
if (!m_temporary.isNull())
addTask(m_temporary);
}
void LinuxIccParser::stdError(const QString &line)
{
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
# include <QTest>
# include "projectexplorer.h"

View File

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

View File

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

View File

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

View File

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