Merge output formatters and output parsers

Now only one piece of code needs to be written to both linkify output in
an output pane and create tasks for it in the issues pane.
The calling sites are also simplified. For instance, until now, build
steps had to feed their output parsers manually and then push the
created tasks up the signal stack in parallel with the actual output,
which the build manager relied upon for cross-linking the output pane
content. Afterwards, the output would get forwarded to the formatter
(and parsed for ANSI escape codes a second time). In contrast, a build
step now just forwards the process output, and task parsing as well as
output formatting is done centrally further up the stack.
Concrete user-visible improvements so far:
    - File paths in compiler/linker messages are clickable links now.
    - QtTest applications now create clickable links also when run
      as part of a build step, not just in the app output pane.

Task-number: QTCREATORBUG-22665
Change-Id: Ic9fb95b2d97f2520ab3ec653315e9219466ec08d
Reviewed-by: Christian Kandeler <christian.kandeler@qt.io>
Reviewed-by: hjk <hjk@qt.io>
Reviewed-by: Christian Stenger <christian.stenger@qt.io>
This commit is contained in:
Christian Kandeler
2020-04-16 13:53:05 +02:00
parent b7851eeb55
commit 1c6e4fbd32
115 changed files with 1124 additions and 1179 deletions

View File

@@ -93,6 +93,24 @@ QList<FormattedText> AnsiEscapeCodeHandler::parseText(const FormattedText &input
while (!strippedText.isEmpty()) { while (!strippedText.isEmpty()) {
QTC_ASSERT(m_pendingText.isEmpty(), break); QTC_ASSERT(m_pendingText.isEmpty(), break);
if (m_waitingForTerminator) {
// We ignore all escape codes taking string arguments.
QString terminator = "\x1b\\";
int terminatorPos = strippedText.indexOf(terminator);
if (terminatorPos == -1 && !m_alternateTerminator.isEmpty()) {
terminator = m_alternateTerminator;
terminatorPos = strippedText.indexOf(terminator);
}
if (terminatorPos == -1) {
m_pendingText = strippedText;
break;
}
m_waitingForTerminator = false;
m_alternateTerminator.clear();
strippedText.remove(0, terminatorPos + terminator.length());
if (strippedText.isEmpty())
break;
}
const int escapePos = strippedText.indexOf(escape.at(0)); const int escapePos = strippedText.indexOf(escape.at(0));
if (escapePos < 0) { if (escapePos < 0) {
outputData << FormattedText(strippedText, charFormat); outputData << FormattedText(strippedText, charFormat);
@@ -111,11 +129,28 @@ QList<FormattedText> AnsiEscapeCodeHandler::parseText(const FormattedText &input
break; break;
} }
if (!strippedText.startsWith(escape)) { if (!strippedText.startsWith(escape)) {
// not a control sequence switch (strippedText.at(1).toLatin1()) {
m_pendingText.clear(); case '\\': // Unexpected terminator sequence.
outputData << FormattedText(strippedText.left(1), charFormat); QTC_CHECK(false);
strippedText.remove(0, 1); Q_FALLTHROUGH();
continue; case 'N': case 'O': // Ignore unsupported single-character sequences.
strippedText.remove(0, 2);
break;
case ']':
m_alternateTerminator = QChar(7);
Q_FALLTHROUGH();
case 'P': case 'X': case '^': case '_':
strippedText.remove(0, 2);
m_waitingForTerminator = true;
break;
default:
// not a control sequence
m_pendingText.clear();
outputData << FormattedText(strippedText.left(1), charFormat);
strippedText.remove(0, 1);
continue;
}
break;
} }
m_pendingText += strippedText.midRef(0, escape.length()); m_pendingText += strippedText.midRef(0, escape.length());
strippedText.remove(0, escape.length()); strippedText.remove(0, escape.length());

View File

@@ -53,6 +53,8 @@ private:
void setFormatScope(const QTextCharFormat &charFormat); void setFormatScope(const QTextCharFormat &charFormat);
bool m_previousFormatClosed = true; bool m_previousFormatClosed = true;
bool m_waitingForTerminator = false;
QString m_alternateTerminator;
QTextCharFormat m_previousFormat; QTextCharFormat m_previousFormat;
QString m_pendingText; QString m_pendingText;
}; };

View File

@@ -23,23 +23,190 @@
** **
****************************************************************************/ ****************************************************************************/
#include "ansiescapecodehandler.h"
#include "outputformatter.h" #include "outputformatter.h"
#include "algorithm.h"
#include "ansiescapecodehandler.h"
#include "fileinprojectfinder.h"
#include "qtcassert.h" #include "qtcassert.h"
#include "synchronousprocess.h" #include "synchronousprocess.h"
#include "theme/theme.h" #include "theme/theme.h"
#include <QDir>
#include <QFileInfo>
#include <QPair> #include <QPair>
#include <QPlainTextEdit> #include <QPlainTextEdit>
#include <QPointer>
#include <QRegExp>
#include <QRegularExpressionMatch>
#include <QTextCursor> #include <QTextCursor>
#include <numeric> #include <numeric>
namespace Utils { namespace Utils {
namespace Internal { class OutputLineParser::Private
{
public:
FilePaths searchDirs;
QPointer<const OutputLineParser> redirectionDetector;
bool skipFileExistsCheck = false;
bool demoteErrorsToWarnings = false;
FileInProjectFinder *fileFinder = nullptr;
};
class OutputFormatterPrivate OutputLineParser::OutputLineParser() : d(new Private) { }
OutputLineParser::~OutputLineParser() { delete d; }
Q_GLOBAL_STATIC_WITH_ARGS(QString, linkPrefix, {"olpfile://"})
Q_GLOBAL_STATIC_WITH_ARGS(QString, linkSep, {"::"})
QString OutputLineParser::createLinkTarget(const FilePath &filePath, int line = -1, int column = -1)
{
return *linkPrefix() + filePath.toString() + *linkSep() + QString::number(line)
+ *linkSep() + QString::number(column);
}
bool OutputLineParser::isLinkTarget(const QString &target)
{
return target.startsWith(*linkPrefix());
}
void OutputLineParser::parseLinkTarget(const QString &target, FilePath &filePath, int &line,
int &column)
{
const QStringList parts = target.mid(linkPrefix()->length()).split(*linkSep());
if (parts.isEmpty())
return;
filePath = FilePath::fromString(parts.first());
line = parts.length() > 1 ? parts.at(1).toInt() : 0;
column = parts.length() > 2 ? parts.at(2).toInt() : 0;
}
// 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 OutputLineParser::setRedirectionDetector(const OutputLineParser *detector)
{
d->redirectionDetector = detector;
}
bool OutputLineParser::needsRedirection() const
{
return d->redirectionDetector && (d->redirectionDetector->hasDetectedRedirection()
|| d->redirectionDetector->needsRedirection());
}
void OutputLineParser::addSearchDir(const FilePath &dir)
{
d->searchDirs << dir;
}
void OutputLineParser::dropSearchDir(const FilePath &dir)
{
const int idx = d->searchDirs.lastIndexOf(dir);
// TODO: This apparently triggers. Find out why and either remove the assertion (if it's legit)
// or fix the culprit.
QTC_ASSERT(idx != -1, return);
d->searchDirs.removeAt(idx);
}
const FilePaths OutputLineParser::searchDirectories() const
{
return d->searchDirs;
}
void OutputLineParser::setFileFinder(FileInProjectFinder *finder)
{
d->fileFinder = finder;
}
void OutputLineParser::setDemoteErrorsToWarnings(bool demote)
{
d->demoteErrorsToWarnings = demote;
}
bool OutputLineParser::demoteErrorsToWarnings() const
{
return d->demoteErrorsToWarnings;
}
FilePath OutputLineParser::absoluteFilePath(const FilePath &filePath)
{
if (filePath.isEmpty() || filePath.toFileInfo().isAbsolute())
return filePath;
FilePaths candidates;
for (const FilePath &dir : searchDirectories()) {
const FilePath candidate = dir.pathAppended(filePath.toString());
if (candidate.exists() || d->skipFileExistsCheck)
candidates << candidate;
}
if (candidates.count() == 1)
return FilePath::fromString(QDir::cleanPath(candidates.first().toString()));
QString fp = filePath.toString();
while (fp.startsWith("../"))
fp.remove(0, 3);
bool found = false;
candidates = d->fileFinder->findFile(QUrl::fromLocalFile(fp), &found);
if (found && candidates.size() == 1)
return candidates.first();
return filePath;
}
void OutputLineParser::addLinkSpecForAbsoluteFilePath(OutputLineParser::LinkSpecs &linkSpecs,
const FilePath &filePath, int lineNo, int pos, int len)
{
if (filePath.toFileInfo().isAbsolute())
linkSpecs.append({pos, len, createLinkTarget(filePath, lineNo)});
}
void OutputLineParser::addLinkSpecForAbsoluteFilePath(OutputLineParser::LinkSpecs &linkSpecs,
const FilePath &filePath, int lineNo, const QRegExp &regex, int capIndex)
{
addLinkSpecForAbsoluteFilePath(linkSpecs, filePath, lineNo, regex.pos(capIndex),
regex.cap(capIndex).length());
}
void OutputLineParser::addLinkSpecForAbsoluteFilePath(OutputLineParser::LinkSpecs &linkSpecs,
const FilePath &filePath, int lineNo, const QRegularExpressionMatch &match,
int capIndex)
{
addLinkSpecForAbsoluteFilePath(linkSpecs, filePath, lineNo, match.capturedStart(capIndex),
match.capturedLength(capIndex));
}
void OutputLineParser::addLinkSpecForAbsoluteFilePath(OutputLineParser::LinkSpecs &linkSpecs,
const FilePath &filePath, int lineNo, const QRegularExpressionMatch &match,
const QString &capName)
{
addLinkSpecForAbsoluteFilePath(linkSpecs, filePath, lineNo, match.capturedStart(capName),
match.capturedLength(capName));
}
QString OutputLineParser::rightTrimmed(const QString &in)
{
int pos = in.length();
for (; pos > 0; --pos) {
if (!in.at(pos - 1).isSpace())
break;
}
return in.mid(0, pos);
}
#ifdef WITH_TESTS
void OutputLineParser::skipFileExistsCheck()
{
d->skipFileExistsCheck = true;
}
#endif
class OutputFormatter::Private
{ {
public: public:
QPlainTextEdit *plainTextEdit = nullptr; QPlainTextEdit *plainTextEdit = nullptr;
@@ -50,20 +217,13 @@ public:
optional<QTextCharFormat> formatOverride; optional<QTextCharFormat> formatOverride;
QList<OutputLineParser *> lineParsers; QList<OutputLineParser *> lineParsers;
OutputLineParser *nextParser = nullptr; OutputLineParser *nextParser = nullptr;
FileInProjectFinder fileFinder;
PostPrintAction postPrintAction;
bool boldFontEnabled = true; bool boldFontEnabled = true;
bool prependCarriageReturn = false; bool prependCarriageReturn = false;
}; };
} // namespace Internal OutputFormatter::OutputFormatter() : d(new Private) { }
OutputLineParser::~OutputLineParser()
{
}
OutputFormatter::OutputFormatter()
: d(new Internal::OutputFormatterPrivate)
{
}
OutputFormatter::~OutputFormatter() OutputFormatter::~OutputFormatter()
{ {
@@ -88,8 +248,44 @@ void OutputFormatter::setLineParsers(const QList<OutputLineParser *> &parsers)
{ {
flush(); flush();
qDeleteAll(d->lineParsers); qDeleteAll(d->lineParsers);
d->lineParsers = parsers; d->lineParsers.clear();
d->nextParser = nullptr; d->nextParser = nullptr;
addLineParsers(parsers);
}
void OutputFormatter::addLineParsers(const QList<OutputLineParser *> &parsers)
{
for (OutputLineParser * const p : qAsConst(parsers))
addLineParser(p);
}
void OutputFormatter::addLineParser(OutputLineParser *parser)
{
setupLineParser(parser);
d->lineParsers << parser;
}
void OutputFormatter::setupLineParser(OutputLineParser *parser)
{
parser->setFileFinder(&d->fileFinder);
connect(parser, &OutputLineParser::newSearchDir, this, &OutputFormatter::addSearchDir);
connect(parser, &OutputLineParser::searchDirExpired, this, &OutputFormatter::dropSearchDir);
}
void OutputFormatter::setFileFinder(const FileInProjectFinder &finder)
{
d->fileFinder = finder;
}
void OutputFormatter::setDemoteErrorsToWarnings(bool demote)
{
for (OutputLineParser * const p : qAsConst(d->lineParsers))
p->setDemoteErrorsToWarnings(demote);
}
void OutputFormatter::overridePostPrintAction(const PostPrintAction &postPrintAction)
{
d->postPrintAction = postPrintAction;
} }
void OutputFormatter::doAppendMessage(const QString &text, OutputFormat format) void OutputFormatter::doAppendMessage(const QString &text, OutputFormat format)
@@ -98,19 +294,33 @@ void OutputFormatter::doAppendMessage(const QString &text, OutputFormat format)
const QList<FormattedText> formattedText = parseAnsi(text, charFmt); const QList<FormattedText> formattedText = parseAnsi(text, charFmt);
const QString cleanLine = std::accumulate(formattedText.begin(), formattedText.end(), QString(), const QString cleanLine = std::accumulate(formattedText.begin(), formattedText.end(), QString(),
[](const FormattedText &t1, const FormattedText &t2) { return t1.text + t2.text; }); [](const FormattedText &t1, const FormattedText &t2) { return t1.text + t2.text; });
const OutputLineParser::Result res = handleMessage(cleanLine, format); QList<OutputLineParser *> involvedParsers;
const OutputLineParser::Result res = handleMessage(cleanLine, format, involvedParsers);
if (res.newContent) { if (res.newContent) {
append(res.newContent.value(), charFmt); append(res.newContent.value(), charFmt);
return; return;
} }
for (const FormattedText &output : linkifiedText(formattedText, res.linkSpecs)) for (const FormattedText &output : linkifiedText(formattedText, res.linkSpecs))
append(output.text, output.format); append(output.text, output.format);
for (OutputLineParser * const p : qAsConst(involvedParsers)) {
if (d->postPrintAction)
d->postPrintAction(p);
else
p->runPostPrintActions();
}
} }
OutputLineParser::Result OutputFormatter::handleMessage(const QString &text, OutputFormat format) OutputLineParser::Result OutputFormatter::handleMessage(const QString &text, OutputFormat format,
QList<OutputLineParser *> &involvedParsers)
{ {
// We only invoke the line parsers for stdout and stderr
if (format != StdOutFormat && format != StdErrFormat)
return OutputLineParser::Status::NotHandled;
const OutputLineParser * const oldNextParser = d->nextParser;
if (d->nextParser) { if (d->nextParser) {
const OutputLineParser::Result res = d->nextParser->handleLine(text, format); involvedParsers << d->nextParser;
const OutputLineParser::Result res
= d->nextParser->handleLine(text, outputTypeForParser(d->nextParser, format));
switch (res.status) { switch (res.status) {
case OutputLineParser::Status::Done: case OutputLineParser::Status::Done:
d->nextParser = nullptr; d->nextParser = nullptr;
@@ -118,18 +328,22 @@ OutputLineParser::Result OutputFormatter::handleMessage(const QString &text, Out
case OutputLineParser::Status::InProgress: case OutputLineParser::Status::InProgress:
return res; return res;
case OutputLineParser::Status::NotHandled: case OutputLineParser::Status::NotHandled:
QTC_CHECK(false); // TODO: This case will be legal after the merge
d->nextParser = nullptr; d->nextParser = nullptr;
return res; break;
} }
} }
QTC_CHECK(!d->nextParser); QTC_CHECK(!d->nextParser);
for (OutputLineParser * const parser : qAsConst(d->lineParsers)) { for (OutputLineParser * const parser : qAsConst(d->lineParsers)) {
const OutputLineParser::Result res = parser->handleLine(text, format); if (parser == oldNextParser) // We tried that one already.
continue;
const OutputLineParser::Result res
= parser->handleLine(text, outputTypeForParser(parser, format));
switch (res.status) { switch (res.status) {
case OutputLineParser::Status::Done: case OutputLineParser::Status::Done:
involvedParsers << parser;
return res; return res;
case OutputLineParser::Status::InProgress: case OutputLineParser::Status::InProgress:
involvedParsers << parser;
d->nextParser = parser; d->nextParser = parser;
return res; return res;
case OutputLineParser::Status::NotHandled: case OutputLineParser::Status::NotHandled:
@@ -139,12 +353,6 @@ OutputLineParser::Result OutputFormatter::handleMessage(const QString &text, Out
return OutputLineParser::Status::NotHandled; return OutputLineParser::Status::NotHandled;
} }
void OutputFormatter::reset()
{
for (OutputLineParser * const p : d->lineParsers)
p->reset();
}
QTextCharFormat OutputFormatter::charFormat(OutputFormat format) const QTextCharFormat OutputFormatter::charFormat(OutputFormat format) const
{ {
return d->formatOverride ? d->formatOverride.value() : d->formats[format]; return d->formatOverride ? d->formatOverride.value() : d->formats[format];
@@ -210,6 +418,8 @@ const QList<FormattedText> OutputFormatter::linkifiedText(
void OutputFormatter::append(const QString &text, const QTextCharFormat &format) void OutputFormatter::append(const QString &text, const QTextCharFormat &format)
{ {
if (!plainTextEdit())
return;
int startPos = 0; int startPos = 0;
int crPos = -1; int crPos = -1;
while ((crPos = text.indexOf('\r', startPos)) >= 0) { while ((crPos = text.indexOf('\r', startPos)) >= 0) {
@@ -237,6 +447,11 @@ void OutputFormatter::overrideTextCharFormat(const QTextCharFormat &fmt)
{ {
d->formatOverride = fmt; d->formatOverride = fmt;
} }
QList<OutputLineParser *> OutputFormatter::lineParsers() const
{
return d->lineParsers;
}
#endif // WITH_TESTS #endif // WITH_TESTS
void OutputFormatter::clearLastLine() void OutputFormatter::clearLastLine()
@@ -274,6 +489,8 @@ void OutputFormatter::flushIncompleteLine()
void OutputFormatter::dumpIncompleteLine(const QString &line, OutputFormat format) void OutputFormatter::dumpIncompleteLine(const QString &line, OutputFormat format)
{ {
if (line.isEmpty())
return;
append(line, charFormat(format)); append(line, charFormat(format));
d->incompleteLine.first.append(line); d->incompleteLine.first.append(line);
d->incompleteLine.second = format; d->incompleteLine.second = format;
@@ -281,6 +498,17 @@ void OutputFormatter::dumpIncompleteLine(const QString &line, OutputFormat forma
void OutputFormatter::handleLink(const QString &href) void OutputFormatter::handleLink(const QString &href)
{ {
// We can handle absolute file paths ourselves. Other types of references are forwarded
// to the line parsers.
if (OutputLineParser::isLinkTarget(href)) {
FilePath filePath;
int line;
int column;
OutputLineParser::parseLinkTarget(href, filePath, line, column);
QTC_ASSERT(!filePath.isEmpty(), return);
emit openInEditorRequested(filePath, line, column);
return;
}
for (OutputLineParser * const f : qAsConst(d->lineParsers)) { for (OutputLineParser * const f : qAsConst(d->lineParsers)) {
if (f->handleLink(href)) if (f->handleLink(href))
return; return;
@@ -288,11 +516,21 @@ void OutputFormatter::handleLink(const QString &href)
} }
void OutputFormatter::clear() void OutputFormatter::clear()
{
if (plainTextEdit())
plainTextEdit()->clear();
}
void OutputFormatter::reset()
{ {
d->prependCarriageReturn = false; d->prependCarriageReturn = false;
d->incompleteLine.first.clear(); d->incompleteLine.first.clear();
plainTextEdit()->clear(); d->nextParser = nullptr;
reset(); qDeleteAll(d->lineParsers);
d->lineParsers.clear();
d->fileFinder = FileInProjectFinder();
d->formatOverride.reset();
d->escapeCodeHandler = AnsiEscapeCodeHandler();
} }
void OutputFormatter::setBoldFontEnabled(bool enabled) void OutputFormatter::setBoldFontEnabled(bool enabled)
@@ -308,11 +546,44 @@ void OutputFormatter::flush()
if (!d->incompleteLine.first.isEmpty()) if (!d->incompleteLine.first.isEmpty())
flushIncompleteLine(); flushIncompleteLine();
d->escapeCodeHandler.endFormatScope(); d->escapeCodeHandler.endFormatScope();
reset(); for (OutputLineParser * const p : qAsConst(d->lineParsers))
p->flush();
if (d->nextParser)
d->nextParser->runPostPrintActions();
}
bool OutputFormatter::hasFatalErrors() const
{
return anyOf(d->lineParsers, [](const OutputLineParser *p) {
return p->hasFatalErrors();
});
}
void OutputFormatter::addSearchDir(const FilePath &dir)
{
for (OutputLineParser * const p : qAsConst(d->lineParsers))
p->addSearchDir(dir);
}
void OutputFormatter::dropSearchDir(const FilePath &dir)
{
for (OutputLineParser * const p : qAsConst(d->lineParsers))
p->dropSearchDir(dir);
}
OutputFormat OutputFormatter::outputTypeForParser(const OutputLineParser *parser,
OutputFormat type) const
{
if (type == StdOutFormat && parser->needsRedirection())
return StdErrFormat;
return type;
} }
void OutputFormatter::appendMessage(const QString &text, OutputFormat format) void OutputFormatter::appendMessage(const QString &text, OutputFormat format)
{ {
if (text.isEmpty())
return;
// If we have an existing incomplete line and its format is different from this one, // If we have an existing incomplete line and its format is different from this one,
// then we consider the two messages unrelated. We re-insert the previous incomplete line, // then we consider the two messages unrelated. We re-insert the previous incomplete line,
// possibly formatted now, and start from scratch with the new input. // possibly formatted now, and start from scratch with the new input.

View File

@@ -26,25 +26,32 @@
#pragma once #pragma once
#include "utils_global.h" #include "utils_global.h"
#include "fileutils.h"
#include "optional.h" #include "optional.h"
#include "outputformat.h" #include "outputformat.h"
#include <QObject> #include <QObject>
#include <functional>
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
class QPlainTextEdit; class QPlainTextEdit;
class QRegExp;
class QRegularExpressionMatch;
class QTextCharFormat; class QTextCharFormat;
class QTextCursor; class QTextCursor;
QT_END_NAMESPACE QT_END_NAMESPACE
namespace Utils { namespace Utils {
class FileInProjectFinder;
class FormattedText; class FormattedText;
class QTCREATOR_UTILS_EXPORT OutputLineParser class QTCREATOR_UTILS_EXPORT OutputLineParser : public QObject
{ {
Q_OBJECT
public: public:
virtual ~OutputLineParser(); OutputLineParser();
~OutputLineParser() override;
enum class Status { Done, InProgress, NotHandled }; enum class Status { Done, InProgress, NotHandled };
class LinkSpec { class LinkSpec {
@@ -65,20 +72,62 @@ public:
optional<QString> newContent; // Hard content override. Only to be used in extreme cases. optional<QString> newContent; // Hard content override. Only to be used in extreme cases.
}; };
static bool isLinkTarget(const QString &target);
static void parseLinkTarget(const QString &target, FilePath &filePath, int &line, int &column);
void addSearchDir(const Utils::FilePath &dir);
void dropSearchDir(const Utils::FilePath &dir);
const FilePaths searchDirectories() const;
void setFileFinder(Utils::FileInProjectFinder *finder);
void setDemoteErrorsToWarnings(bool demote);
bool demoteErrorsToWarnings() const;
// line contains at most one line feed character, and if it does occur, it's the last character. // line contains at most one line feed character, and if it does occur, it's the last character.
// Either way, the input is to be considered "complete" for parsing purposes. // Either way, the input is to be considered "complete" for parsing purposes.
virtual Result handleLine(const QString &line, OutputFormat format) = 0; virtual Result handleLine(const QString &line, OutputFormat format) = 0;
virtual bool handleLink(const QString &href) { Q_UNUSED(href); return false; } virtual bool handleLink(const QString &href) { Q_UNUSED(href); return false; }
virtual void reset() {} virtual bool hasFatalErrors() const { return false; }
virtual void flush() {}
virtual void runPostPrintActions() {}
void setRedirectionDetector(const OutputLineParser *detector);
bool needsRedirection() const;
virtual bool hasDetectedRedirection() const { return false; }
#ifdef WITH_TESTS
void skipFileExistsCheck();
#endif
protected:
static QString rightTrimmed(const QString &in);
Utils::FilePath absoluteFilePath(const Utils::FilePath &filePath);
static QString createLinkTarget(const FilePath &filePath, int line, int column);
void addLinkSpecForAbsoluteFilePath(LinkSpecs &linkSpecs, const FilePath &filePath,
int lineNo, int pos, int len);
void addLinkSpecForAbsoluteFilePath(LinkSpecs &linkSpecs, const FilePath &filePath,
int lineNo, const QRegExp &regex, int capIndex);
void addLinkSpecForAbsoluteFilePath(LinkSpecs &linkSpecs, const FilePath &filePath,
int lineNo, const QRegularExpressionMatch &match,
int capIndex);
void addLinkSpecForAbsoluteFilePath(LinkSpecs &linkSpecs, const FilePath &filePath,
int lineNo, const QRegularExpressionMatch &match,
const QString &capName);
signals:
void newSearchDir(const Utils::FilePath &dir);
void searchDirExpired(const Utils::FilePath &dir);
private:
class Private;
Private * const d;
}; };
namespace Internal { class OutputFormatterPrivate; }
class QTCREATOR_UTILS_EXPORT OutputFormatter : public QObject class QTCREATOR_UTILS_EXPORT OutputFormatter : public QObject
{ {
Q_OBJECT
public: public:
OutputFormatter(); OutputFormatter();
~OutputFormatter() override; ~OutputFormatter() override;
@@ -86,17 +135,33 @@ public:
QPlainTextEdit *plainTextEdit() const; QPlainTextEdit *plainTextEdit() const;
void setPlainTextEdit(QPlainTextEdit *plainText); void setPlainTextEdit(QPlainTextEdit *plainText);
// Forwards to line parsers. Add those before.
void addSearchDir(const FilePath &dir);
void dropSearchDir(const FilePath &dir);
void setLineParsers(const QList<OutputLineParser *> &parsers); // Takes ownership void setLineParsers(const QList<OutputLineParser *> &parsers); // Takes ownership
void addLineParsers(const QList<OutputLineParser *> &parsers);
void addLineParser(OutputLineParser *parser);
void setFileFinder(const FileInProjectFinder &finder);
void setDemoteErrorsToWarnings(bool demote);
using PostPrintAction = std::function<void(OutputLineParser *)>;
void overridePostPrintAction(const PostPrintAction &postPrintAction);
void appendMessage(const QString &text, OutputFormat format); void appendMessage(const QString &text, OutputFormat format);
void flush(); void flush(); // Flushes in-flight data.
void clear(); // Clears the text edit, if there is one.
void reset(); // Wipes everything except the text edit.
void handleLink(const QString &href); void handleLink(const QString &href);
void clear();
void setBoldFontEnabled(bool enabled); void setBoldFontEnabled(bool enabled);
bool hasFatalErrors() const;
#ifdef WITH_TESTS #ifdef WITH_TESTS
void overrideTextCharFormat(const QTextCharFormat &fmt); void overrideTextCharFormat(const QTextCharFormat &fmt);
QList<OutputLineParser *> lineParsers() const;
#endif #endif
#ifndef WITH_TESTS #ifndef WITH_TESTS
@@ -105,11 +170,14 @@ private:
QTextCharFormat charFormat(OutputFormat format) const; QTextCharFormat charFormat(OutputFormat format) const;
static QTextCharFormat linkFormat(const QTextCharFormat &inputFormat, const QString &href); static QTextCharFormat linkFormat(const QTextCharFormat &inputFormat, const QString &href);
signals:
void openInEditorRequested(const FilePath &filePath, int line, int column);
private: private:
void doAppendMessage(const QString &text, OutputFormat format); void doAppendMessage(const QString &text, OutputFormat format);
OutputLineParser::Result handleMessage(const QString &text, OutputFormat format); OutputLineParser::Result handleMessage(const QString &text, OutputFormat format,
void reset(); QList<OutputLineParser *> &involvedParsers);
void append(const QString &text, const QTextCharFormat &format); void append(const QString &text, const QTextCharFormat &format);
void initFormats(); void initFormats();
@@ -119,8 +187,11 @@ private:
QList<FormattedText> parseAnsi(const QString &text, const QTextCharFormat &format); QList<FormattedText> parseAnsi(const QString &text, const QTextCharFormat &format);
const QList<Utils::FormattedText> linkifiedText(const QList<FormattedText> &text, const QList<Utils::FormattedText> linkifiedText(const QList<FormattedText> &text,
const OutputLineParser::LinkSpecs &linkSpecs); const OutputLineParser::LinkSpecs &linkSpecs);
OutputFormat outputTypeForParser(const OutputLineParser *parser, OutputFormat type) const;
void setupLineParser(OutputLineParser *parser);
Internal::OutputFormatterPrivate *d; class Private;
Private * const d;
}; };

View File

@@ -178,22 +178,6 @@ bool AndroidBuildApkStep::init()
return false; return false;
} }
auto parser = new JavaParser;
parser->setProjectFileList(Utils::transform(target()->project()->files(ProjectExplorer::Project::AllFiles),
&Utils::FilePath::toString));
const QString buildKey = target()->activeBuildKey();
const ProjectNode *node = target()->project()->findNodeForBuildKey(buildKey);
QString sourceDirName;
if (node)
sourceDirName = node->data(Constants::AndroidPackageSourceDir).toString();
QFileInfo sourceDirInfo(sourceDirName);
parser->setSourceDirectory(Utils::FilePath::fromString(sourceDirInfo.canonicalFilePath()));
parser->setBuildDirectory(buildDirectory().pathAppended(Constants::ANDROID_BUILDDIRECTORY));
setOutputParser(parser);
m_openPackageLocationForRun = m_openPackageLocation; m_openPackageLocationForRun = m_openPackageLocation;
if (m_buildAAB) { if (m_buildAAB) {
@@ -218,6 +202,8 @@ bool AndroidBuildApkStep::init()
QString outputDir = buildDirectory().pathAppended(Constants::ANDROID_BUILDDIRECTORY).toString(); QString outputDir = buildDirectory().pathAppended(Constants::ANDROID_BUILDDIRECTORY).toString();
const QString buildKey = target()->activeBuildKey();
const ProjectNode *node = project()->findNodeForBuildKey(buildKey);
if (node) if (node)
m_inputFile = node->data(Constants::AndroidDeploySettingsFile).toString(); m_inputFile = node->data(Constants::AndroidDeploySettingsFile).toString();
@@ -285,6 +271,23 @@ bool AndroidBuildApkStep::init()
return true; return true;
} }
void AndroidBuildApkStep::setupOutputFormatter(OutputFormatter *formatter)
{
const auto parser = new JavaParser;
parser->setProjectFileList(Utils::transform(project()->files(ProjectExplorer::Project::AllFiles),
&Utils::FilePath::toString));
const QString buildKey = target()->activeBuildKey();
const ProjectNode *node = project()->findNodeForBuildKey(buildKey);
QString sourceDirName;
if (node)
sourceDirName = node->data(Constants::AndroidPackageSourceDir).toString();
QFileInfo sourceDirInfo(sourceDirName);
parser->setSourceDirectory(Utils::FilePath::fromString(sourceDirInfo.canonicalFilePath()));
parser->setBuildDirectory(buildDirectory().pathAppended(Constants::ANDROID_BUILDDIRECTORY));
formatter->addLineParser(parser);
AbstractProcessStep::setupOutputFormatter(formatter);
}
void AndroidBuildApkStep::showInGraphicalShell() void AndroidBuildApkStep::showInGraphicalShell()
{ {
Core::FileUtils::showInGraphicalShell(Core::ICore::mainWindow(), m_packagePath); Core::FileUtils::showInGraphicalShell(Core::ICore::mainWindow(), m_packagePath);

View File

@@ -82,6 +82,7 @@ private:
void showInGraphicalShell(); void showInGraphicalShell();
bool init() override; bool init() override;
void setupOutputFormatter(Utils::OutputFormatter *formatter) override;
ProjectExplorer::BuildStepConfigWidget *createConfigWidget() override; ProjectExplorer::BuildStepConfigWidget *createConfigWidget() override;
void processStarted() override; void processStarted() override;
void processFinished(int exitCode, QProcess::ExitStatus status) override; void processFinished(int exitCode, QProcess::ExitStatus status) override;

View File

@@ -60,6 +60,7 @@ public:
private: private:
bool init() final; bool init() final;
void setupOutputFormatter(Utils::OutputFormatter *formatter) override;
void doRun() final; void doRun() final;
QStringList m_androidDirsToClean; QStringList m_androidDirsToClean;
@@ -111,10 +112,6 @@ bool AndroidPackageInstallationStep::init()
pp->setEnvironment(env); pp->setEnvironment(env);
pp->setCommandLine(cmd); pp->setCommandLine(cmd);
setOutputParser(new GnuMakeParser());
appendOutputParsers(target()->kit()->createOutputParsers());
outputParser()->addSearchDir(pp->effectiveWorkingDirectory());
m_androidDirsToClean.clear(); m_androidDirsToClean.clear();
// don't remove gradle's cache, it takes ages to rebuild it. // don't remove gradle's cache, it takes ages to rebuild it.
m_androidDirsToClean << dirPath + "/assets"; m_androidDirsToClean << dirPath + "/assets";
@@ -123,6 +120,14 @@ bool AndroidPackageInstallationStep::init()
return AbstractProcessStep::init(); return AbstractProcessStep::init();
} }
void AndroidPackageInstallationStep::setupOutputFormatter(OutputFormatter *formatter)
{
formatter->addLineParser(new GnuMakeParser);
formatter->addLineParsers(target()->kit()->createOutputParsers());
formatter->addSearchDir(processParameters()->effectiveWorkingDirectory());
AbstractProcessStep::setupOutputFormatter(formatter);
}
void AndroidPackageInstallationStep::doRun() void AndroidPackageInstallationStep::doRun()
{ {
QString error; QString error;

View File

@@ -51,7 +51,8 @@ void JavaParser::setSourceDirectory(const Utils::FilePath &sourceDirectory)
m_sourceDirectory = sourceDirectory; m_sourceDirectory = sourceDirectory;
} }
OutputTaskParser::Status JavaParser::handleLine(const QString &line, Utils::OutputFormat type) Utils::OutputLineParser::Result JavaParser::handleLine(const QString &line,
Utils::OutputFormat type)
{ {
Q_UNUSED(type); Q_UNUSED(type);
if (m_javaRegExp.indexIn(line) == -1) if (m_javaRegExp.indexIn(line) == -1)
@@ -78,6 +79,8 @@ OutputTaskParser::Status JavaParser::handleLine(const QString &line, Utils::Outp
m_javaRegExp.cap(4).trimmed(), m_javaRegExp.cap(4).trimmed(),
absoluteFilePath(file), absoluteFilePath(file),
lineno); lineno);
emit addTask(task, 1); LinkSpecs linkSpecs;
return Status::Done; addLinkSpecForAbsoluteFilePath(linkSpecs, task.file, task.line, m_javaRegExp, 2);
scheduleTask(task, 1);
return {Status::Done, linkSpecs};
} }

View File

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

View File

@@ -145,12 +145,12 @@ bool IarParser::parseErrorOrFatalErrorDetailsMessage2(const QString &lne)
return true; return true;
} }
bool IarParser::parseWarningOrErrorOrFatalErrorDetailsMessage1(const QString &lne) OutputLineParser::Result IarParser::parseWarningOrErrorOrFatalErrorDetailsMessage1(const QString &lne)
{ {
const QRegularExpression re("^\"(.+)\",(\\d+)?\\s+(Warning|Error|Fatal error)\\[(.+)\\].+$"); const QRegularExpression re("^\"(.+)\",(\\d+)?\\s+(Warning|Error|Fatal error)\\[(.+)\\].+$");
const QRegularExpressionMatch match = re.match(lne); const QRegularExpressionMatch match = re.match(lne);
if (!match.hasMatch()) if (!match.hasMatch())
return false; return Status::NotHandled;
enum CaptureIndex { FilePathIndex = 1, LineNumberIndex, enum CaptureIndex { FilePathIndex = 1, LineNumberIndex,
MessageTypeIndex, MessageCodeIndex }; MessageTypeIndex, MessageCodeIndex };
const Utils::FilePath fileName = Utils::FilePath::fromUserInput( const Utils::FilePath fileName = Utils::FilePath::fromUserInput(
@@ -164,7 +164,10 @@ bool IarParser::parseWarningOrErrorOrFatalErrorDetailsMessage1(const QString &ln
m_expectDescription = true; m_expectDescription = true;
m_expectSnippet = false; m_expectSnippet = false;
m_expectFilePath = false; m_expectFilePath = false;
return true; LinkSpecs linkSpecs;
addLinkSpecForAbsoluteFilePath(linkSpecs, m_lastTask.file, m_lastTask.line, match,
FilePathIndex);
return {Status::InProgress, linkSpecs};
} }
bool IarParser::parseErrorInCommandLineMessage(const QString &lne) bool IarParser::parseErrorInCommandLineMessage(const QString &lne)
@@ -190,7 +193,7 @@ bool IarParser::parseErrorMessage1(const QString &lne)
return true; return true;
} }
OutputTaskParser::Status IarParser::handleLine(const QString &line, OutputFormat type) OutputLineParser::Result IarParser::handleLine(const QString &line, OutputFormat type)
{ {
const QString lne = rightTrimmed(line); const QString lne = rightTrimmed(line);
if (type == StdOutFormat) { if (type == StdOutFormat) {
@@ -208,8 +211,9 @@ OutputTaskParser::Status IarParser::handleLine(const QString &line, OutputFormat
return Status::InProgress; return Status::InProgress;
if (parseErrorOrFatalErrorDetailsMessage2(lne)) if (parseErrorOrFatalErrorDetailsMessage2(lne))
return Status::InProgress; return Status::InProgress;
if (parseWarningOrErrorOrFatalErrorDetailsMessage1(lne)) const Result res = parseWarningOrErrorOrFatalErrorDetailsMessage1(lne);
return Status::InProgress; if (res.status != Status::NotHandled)
return res;
if (m_expectFilePath) { if (m_expectFilePath) {
if (lne.endsWith(']')) { if (lne.endsWith(']')) {
@@ -256,7 +260,7 @@ void IarParser::flush()
Task t = m_lastTask; Task t = m_lastTask;
m_lastTask.clear(); m_lastTask.clear();
emit addTask(t, m_lines, 1); scheduleTask(t, m_lines, 1);
m_lines = 0; m_lines = 0;
} }

View File

@@ -48,11 +48,11 @@ private:
bool parseErrorOrFatalErrorDetailsMessage1(const QString &lne); bool parseErrorOrFatalErrorDetailsMessage1(const QString &lne);
bool parseErrorOrFatalErrorDetailsMessage2(const QString &lne); bool parseErrorOrFatalErrorDetailsMessage2(const QString &lne);
bool parseWarningOrErrorOrFatalErrorDetailsMessage1(const QString &lne); Result parseWarningOrErrorOrFatalErrorDetailsMessage1(const QString &lne);
bool parseErrorInCommandLineMessage(const QString &lne); bool parseErrorInCommandLineMessage(const QString &lne);
bool parseErrorMessage1(const QString &lne); bool parseErrorMessage1(const QString &lne);
Status handleLine(const QString &line, Utils::OutputFormat type) final; Result handleLine(const QString &line, Utils::OutputFormat type) final;
void flush() final; void flush() final;
ProjectExplorer::Task m_lastTask; ProjectExplorer::Task m_lastTask;

View File

@@ -354,7 +354,7 @@ void IarToolChain::addToEnvironment(Environment &env) const
} }
} }
QList<OutputTaskParser *> IarToolChain::createOutputParsers() const QList<Utils::OutputLineParser *> IarToolChain::createOutputParsers() const
{ {
return {new IarParser()}; return {new IarParser()};
} }

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;
QList<ProjectExplorer::OutputTaskParser *> createOutputParsers() const final; QList<Utils::OutputLineParser *> createOutputParsers() const final;
QVariantMap toMap() const final; QVariantMap toMap() const final;
bool fromMap(const QVariantMap &data) final; bool fromMap(const QVariantMap &data) final;

View File

@@ -93,12 +93,12 @@ void KeilParser::amendDescription()
// ARM compiler specific parsers. // ARM compiler specific parsers.
bool KeilParser::parseArmWarningOrErrorDetailsMessage(const QString &lne) OutputLineParser::Result KeilParser::parseArmWarningOrErrorDetailsMessage(const QString &lne)
{ {
const QRegularExpression re("^\"(.+)\", line (\\d+).*:\\s+(Warning|Error):(\\s+|.+)([#|L].+)$"); const QRegularExpression re("^\"(.+)\", line (\\d+).*:\\s+(Warning|Error):(\\s+|.+)([#|L].+)$");
const QRegularExpressionMatch match = re.match(lne); const QRegularExpressionMatch match = re.match(lne);
if (!match.hasMatch()) if (!match.hasMatch())
return false; return Status::NotHandled;
enum CaptureIndex { FilePathIndex = 1, LineNumberIndex, enum CaptureIndex { FilePathIndex = 1, LineNumberIndex,
MessageTypeIndex, MessageNoteIndex, DescriptionIndex }; MessageTypeIndex, MessageNoteIndex, DescriptionIndex };
const Utils::FilePath fileName = Utils::FilePath::fromUserInput( const Utils::FilePath fileName = Utils::FilePath::fromUserInput(
@@ -107,7 +107,10 @@ bool KeilParser::parseArmWarningOrErrorDetailsMessage(const QString &lne)
const Task::TaskType type = taskType(match.captured(MessageTypeIndex)); const Task::TaskType type = taskType(match.captured(MessageTypeIndex));
const QString descr = match.captured(DescriptionIndex); const QString descr = match.captured(DescriptionIndex);
newTask(CompileTask(type, descr, absoluteFilePath(fileName), lineno)); newTask(CompileTask(type, descr, absoluteFilePath(fileName), lineno));
return true; LinkSpecs linkSpecs;
addLinkSpecForAbsoluteFilePath(linkSpecs, m_lastTask.file, m_lastTask.line, match,
FilePathIndex);
return {Status::InProgress, linkSpecs};
} }
bool KeilParser::parseArmErrorOrFatalErorrMessage(const QString &lne) bool KeilParser::parseArmErrorOrFatalErorrMessage(const QString &lne)
@@ -125,12 +128,12 @@ bool KeilParser::parseArmErrorOrFatalErorrMessage(const QString &lne)
// MCS51 compiler specific parsers. // MCS51 compiler specific parsers.
bool KeilParser::parseMcs51WarningOrErrorDetailsMessage1(const QString &lne) OutputLineParser::Result KeilParser::parseMcs51WarningOrErrorDetailsMessage1(const QString &lne)
{ {
const QRegularExpression re("^\\*{3} (WARNING|ERROR) (\\w+) IN LINE (\\d+) OF (.+\\.\\S+): (.+)$"); const QRegularExpression re("^\\*{3} (WARNING|ERROR) (\\w+) IN LINE (\\d+) OF (.+\\.\\S+): (.+)$");
const QRegularExpressionMatch match = re.match(lne); const QRegularExpressionMatch match = re.match(lne);
if (!match.hasMatch()) if (!match.hasMatch())
return false; return Status::NotHandled;
enum CaptureIndex { MessageTypeIndex = 1, MessageCodeIndex, LineNumberIndex, enum CaptureIndex { MessageTypeIndex = 1, MessageCodeIndex, LineNumberIndex,
FilePathIndex, MessageTextIndex }; FilePathIndex, MessageTextIndex };
const Task::TaskType type = taskType(match.captured(MessageTypeIndex)); const Task::TaskType type = taskType(match.captured(MessageTypeIndex));
@@ -140,15 +143,18 @@ bool KeilParser::parseMcs51WarningOrErrorDetailsMessage1(const QString &lne)
const QString descr = QString("%1: %2").arg(match.captured(MessageCodeIndex), const QString descr = QString("%1: %2").arg(match.captured(MessageCodeIndex),
match.captured(MessageTextIndex)); match.captured(MessageTextIndex));
newTask(CompileTask(type, descr, absoluteFilePath(fileName), lineno)); newTask(CompileTask(type, descr, absoluteFilePath(fileName), lineno));
return true; LinkSpecs linkSpecs;
addLinkSpecForAbsoluteFilePath(linkSpecs, m_lastTask.file, m_lastTask.line, match,
FilePathIndex);
return {Status::InProgress, linkSpecs};
} }
bool KeilParser::parseMcs51WarningOrErrorDetailsMessage2(const QString &lne) OutputLineParser::Result KeilParser::parseMcs51WarningOrErrorDetailsMessage2(const QString &lne)
{ {
const QRegularExpression re("^\\*{3} (WARNING|ERROR) (#\\w+) IN (\\d+) \\((.+), LINE \\d+\\): (.+)$"); const QRegularExpression re("^\\*{3} (WARNING|ERROR) (#\\w+) IN (\\d+) \\((.+), LINE \\d+\\): (.+)$");
const QRegularExpressionMatch match = re.match(lne); const QRegularExpressionMatch match = re.match(lne);
if (!match.hasMatch()) if (!match.hasMatch())
return false; return Status::NotHandled;
enum CaptureIndex { MessageTypeIndex = 1, MessageCodeIndex, LineNumberIndex, enum CaptureIndex { MessageTypeIndex = 1, MessageCodeIndex, LineNumberIndex,
FilePathIndex, MessageTextIndex }; FilePathIndex, MessageTextIndex };
const Task::TaskType type = taskType(match.captured(MessageTypeIndex)); const Task::TaskType type = taskType(match.captured(MessageTypeIndex));
@@ -158,7 +164,10 @@ bool KeilParser::parseMcs51WarningOrErrorDetailsMessage2(const QString &lne)
const QString descr = QString("%1: %2").arg(match.captured(MessageCodeIndex), const QString descr = QString("%1: %2").arg(match.captured(MessageCodeIndex),
match.captured(MessageTextIndex)); match.captured(MessageTextIndex));
newTask(CompileTask(type, descr, absoluteFilePath(fileName), lineno)); newTask(CompileTask(type, descr, absoluteFilePath(fileName), lineno));
return true; LinkSpecs linkSpecs;
addLinkSpecForAbsoluteFilePath(linkSpecs, m_lastTask.file, m_lastTask.line, match,
FilePathIndex);
return {Status::InProgress, linkSpecs};
} }
bool KeilParser::parseMcs51WarningOrFatalErrorMessage(const QString &lne) bool KeilParser::parseMcs51WarningOrFatalErrorMessage(const QString &lne)
@@ -206,15 +215,17 @@ static bool hasDetailsPointer(const QString &trimmedLine)
return trimmedLine.contains('_'); return trimmedLine.contains('_');
} }
OutputTaskParser::Status KeilParser::handleLine(const QString &line, OutputFormat type) OutputLineParser::Result KeilParser::handleLine(const QString &line, OutputFormat type)
{ {
QString lne = rightTrimmed(line); QString lne = rightTrimmed(line);
if (type == StdOutFormat) { if (type == StdOutFormat) {
// Check for MSC51 compiler specific patterns. // Check for MSC51 compiler specific patterns.
const bool parsed = parseMcs51WarningOrErrorDetailsMessage1(lne) Result res = parseMcs51WarningOrErrorDetailsMessage1(lne);
|| parseMcs51WarningOrErrorDetailsMessage2(lne); if (res.status != Status::NotHandled)
if (parsed) return res;
return Status::InProgress; res = parseMcs51WarningOrErrorDetailsMessage2(lne);
if (res.status != Status::NotHandled)
return res;
if (parseMcs51WarningOrFatalErrorMessage(lne)) if (parseMcs51WarningOrFatalErrorMessage(lne))
return Status::InProgress; return Status::InProgress;
if (parseMcs51FatalErrorMessage2(lne)) if (parseMcs51FatalErrorMessage2(lne))
@@ -247,8 +258,9 @@ OutputTaskParser::Status KeilParser::handleLine(const QString &line, OutputForma
} }
// Check for ARM compiler specific patterns. // Check for ARM compiler specific patterns.
if (parseArmWarningOrErrorDetailsMessage(lne)) const Result res = parseArmWarningOrErrorDetailsMessage(lne);
return Status::InProgress; if (res.status != Status::NotHandled)
return res;
if (parseArmErrorOrFatalErorrMessage(lne)) if (parseArmErrorOrFatalErorrMessage(lne))
return Status::InProgress; return Status::InProgress;
@@ -270,7 +282,7 @@ void KeilParser::flush()
Task t = m_lastTask; Task t = m_lastTask;
m_lastTask.clear(); m_lastTask.clear();
emit addTask(t, m_lines, 1); scheduleTask(t, m_lines, 1);
m_lines = 0; m_lines = 0;
} }

View File

@@ -46,16 +46,16 @@ private:
void amendDescription(); void amendDescription();
// ARM compiler specific parsers. // ARM compiler specific parsers.
bool parseArmWarningOrErrorDetailsMessage(const QString &lne); Result parseArmWarningOrErrorDetailsMessage(const QString &lne);
bool parseArmErrorOrFatalErorrMessage(const QString &lne); bool parseArmErrorOrFatalErorrMessage(const QString &lne);
// MCS51 compiler specific parsers. // MCS51 compiler specific parsers.
bool parseMcs51WarningOrErrorDetailsMessage1(const QString &lne); Result parseMcs51WarningOrErrorDetailsMessage1(const QString &lne);
bool parseMcs51WarningOrErrorDetailsMessage2(const QString &lne); Result parseMcs51WarningOrErrorDetailsMessage2(const QString &lne);
bool parseMcs51WarningOrFatalErrorMessage(const QString &lne); bool parseMcs51WarningOrFatalErrorMessage(const QString &lne);
bool parseMcs51FatalErrorMessage2(const QString &lne); bool parseMcs51FatalErrorMessage2(const QString &lne);
Status handleLine(const QString &line, Utils::OutputFormat type) final; Result handleLine(const QString &line, Utils::OutputFormat type) final;
void flush() final; void flush() final;
ProjectExplorer::Task m_lastTask; ProjectExplorer::Task m_lastTask;

View File

@@ -506,7 +506,7 @@ void KeilToolChain::addToEnvironment(Environment &env) const
} }
} }
QList<OutputTaskParser *> KeilToolChain::createOutputParsers() const QList<OutputLineParser *> KeilToolChain::createOutputParsers() const
{ {
return {new KeilParser}; return {new KeilParser};
} }

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;
QList<ProjectExplorer::OutputTaskParser *> createOutputParsers() const final; QList<Utils::OutputLineParser *> createOutputParsers() 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,7 +87,7 @@ void SdccParser::amendDescription(const QString &desc)
++m_lines; ++m_lines;
} }
OutputTaskParser::Status SdccParser::handleLine(const QString &line, OutputFormat type) OutputLineParser::Result SdccParser::handleLine(const QString &line, OutputFormat type)
{ {
if (type == StdOutFormat) if (type == StdOutFormat)
return Status::NotHandled; return Status::NotHandled;
@@ -108,7 +108,10 @@ OutputTaskParser::Status SdccParser::handleLine(const QString &line, OutputForma
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 Status::InProgress; LinkSpecs linkSpecs;
addLinkSpecForAbsoluteFilePath(linkSpecs, m_lastTask.file, m_lastTask.line, match,
FilePathIndex);
return {Status::InProgress, linkSpecs};
} }
re.setPattern("^(.+\\.\\S+):(\\d+): (Error|error|syntax error): (.+)$"); re.setPattern("^(.+\\.\\S+):(\\d+): (Error|error|syntax error): (.+)$");
@@ -122,7 +125,10 @@ OutputTaskParser::Status SdccParser::handleLine(const QString &line, OutputForma
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 Status::InProgress; LinkSpecs linkSpecs;
addLinkSpecForAbsoluteFilePath(linkSpecs, m_lastTask.file, m_lastTask.line, match,
FilePathIndex);
return {Status::InProgress, linkSpecs};
} }
re.setPattern("^at (\\d+): (warning|error) \\d+: (.+)$"); re.setPattern("^at (\\d+): (warning|error) \\d+: (.+)$");
@@ -161,7 +167,7 @@ void SdccParser::flush()
Task t = m_lastTask; Task t = m_lastTask;
m_lastTask.clear(); m_lastTask.clear();
emit addTask(t, m_lines, 1); scheduleTask(t, m_lines, 1);
m_lines = 0; m_lines = 0;
} }

View File

@@ -45,7 +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);
Status handleLine(const QString &line, Utils::OutputFormat type) final; Result handleLine(const QString &line, Utils::OutputFormat type) final;
void flush() final; void flush() final;
ProjectExplorer::Task m_lastTask; ProjectExplorer::Task m_lastTask;

View File

@@ -307,7 +307,7 @@ void SdccToolChain::addToEnvironment(Environment &env) const
} }
} }
QList<OutputTaskParser *> SdccToolChain::createOutputParsers() const QList<Utils::OutputLineParser *> SdccToolChain::createOutputParsers() const
{ {
return {new SdccParser}; return {new SdccParser};
} }

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;
QList<ProjectExplorer::OutputTaskParser *> createOutputParsers() const final; QList<Utils::OutputLineParser *> createOutputParsers() const final;
QVariantMap toMap() const final; QVariantMap toMap() const final;
bool fromMap(const QVariantMap &data) final; bool fromMap(const QVariantMap &data) final;

View File

@@ -197,16 +197,19 @@ bool CMakeBuildStep::init()
pp->setCommandLine(cmakeCommand(rc)); pp->setCommandLine(cmakeCommand(rc));
pp->resolveAll(); pp->resolveAll();
CMakeParser *cmakeParser = new CMakeParser;
cmakeParser->setSourceDirectory(projectDirectory.toString());
setOutputParser(cmakeParser);
appendOutputParser(new GnuMakeParser);
appendOutputParsers(target()->kit()->createOutputParsers());
outputParser()->addSearchDir(pp->effectiveWorkingDirectory());
return AbstractProcessStep::init(); return AbstractProcessStep::init();
} }
void CMakeBuildStep::setupOutputFormatter(Utils::OutputFormatter *formatter)
{
CMakeParser *cmakeParser = new CMakeParser;
cmakeParser->setSourceDirectory(project()->projectDirectory().toString());
formatter->addLineParsers({cmakeParser, new GnuMakeParser});
formatter->addLineParsers(target()->kit()->createOutputParsers());
formatter->addSearchDir(processParameters()->effectiveWorkingDirectory());
AbstractProcessStep::setupOutputFormatter(formatter);
}
void CMakeBuildStep::doRun() void CMakeBuildStep::doRun()
{ {
// Make sure CMake state was written to disk before trying to build: // Make sure CMake state was written to disk before trying to build:

View File

@@ -83,6 +83,7 @@ private:
void ctor(ProjectExplorer::BuildStepList *bsl); void ctor(ProjectExplorer::BuildStepList *bsl);
bool init() override; bool init() override;
void setupOutputFormatter(Utils::OutputFormatter *formatter) override;
void doRun() override; void doRun() override;
ProjectExplorer::BuildStepConfigWidget *createConfigWidget() override; ProjectExplorer::BuildStepConfigWidget *createConfigWidget() override;

View File

@@ -54,10 +54,13 @@ CMakeParser::CMakeParser()
void CMakeParser::setSourceDirectory(const QString &sourceDir) void CMakeParser::setSourceDirectory(const QString &sourceDir)
{ {
if (m_sourceDirectory)
emit searchDirExpired(FilePath::fromString(m_sourceDirectory.value().path()));
m_sourceDirectory = QDir(sourceDir); m_sourceDirectory = QDir(sourceDir);
emit addSearchDir(FilePath::fromString(sourceDir));
} }
OutputTaskParser::Status CMakeParser::handleLine(const QString &line, OutputFormat type) OutputLineParser::Result CMakeParser::handleLine(const QString &line, OutputFormat type)
{ {
if (type != StdErrFormat) if (type != StdErrFormat)
return Status::NotHandled; return Status::NotHandled;
@@ -80,18 +83,23 @@ OutputTaskParser::Status CMakeParser::handleLine(const QString &line, OutputForm
QString path = m_sourceDirectory ? m_sourceDirectory->absoluteFilePath( QString path = m_sourceDirectory ? m_sourceDirectory->absoluteFilePath(
QDir::fromNativeSeparators(m_commonError.cap(1))) QDir::fromNativeSeparators(m_commonError.cap(1)))
: QDir::fromNativeSeparators(m_commonError.cap(1)); : QDir::fromNativeSeparators(m_commonError.cap(1));
m_lastTask = BuildSystemTask(Task::Error, m_lastTask = BuildSystemTask(Task::Error,
QString(), QString(),
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 Status::InProgress; LinkSpecs linkSpecs;
addLinkSpecForAbsoluteFilePath(linkSpecs, m_lastTask.file, m_lastTask.line,
m_commonError, 1);
return {Status::InProgress, linkSpecs};
} 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))));
LinkSpecs linkSpecs;
addLinkSpecForAbsoluteFilePath(linkSpecs, m_lastTask.file, m_lastTask.line,
m_nextSubError, 1);
m_lines = 1; m_lines = 1;
return Status::InProgress; return {Status::InProgress, linkSpecs};
} 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(' '));
@@ -118,11 +126,15 @@ OutputTaskParser::Status CMakeParser::handleLine(const QString &line, OutputForm
{ {
QRegularExpressionMatch m = m_locationLine.match(trimmedLine); QRegularExpressionMatch m = m_locationLine.match(trimmedLine);
QTC_CHECK(m.hasMatch()); QTC_CHECK(m.hasMatch());
m_lastTask.file = Utils::FilePath::fromUserInput(trimmedLine.mid(0, m.capturedStart())); m_lastTask.file = absoluteFilePath(FilePath::fromUserInput(
trimmedLine.mid(0, m.capturedStart())));
m_lastTask.line = m.captured(1).toInt(); m_lastTask.line = m.captured(1).toInt();
m_expectTripleLineErrorData = LINE_DESCRIPTION; m_expectTripleLineErrorData = LINE_DESCRIPTION;
LinkSpecs linkSpecs;
addLinkSpecForAbsoluteFilePath(linkSpecs, m_lastTask.file, m_lastTask.line, 0,
m.capturedStart());
return {Status::InProgress, linkSpecs};
} }
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('\"')))
@@ -149,7 +161,7 @@ void CMakeParser::flush()
return; return;
Task t = m_lastTask; Task t = m_lastTask;
m_lastTask.clear(); m_lastTask.clear();
emit addTask(t, m_lines, 1); scheduleTask(t, m_lines, 1);
m_lines = 0; m_lines = 0;
} }

View File

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

View File

@@ -94,17 +94,6 @@ void CMakeProcess::run(const BuildDirParameters &parameters, const QStringList &
const auto parser = new CMakeParser; const auto parser = new CMakeParser;
parser->setSourceDirectory(srcDir); parser->setSourceDirectory(srcDir);
m_parser.addLineParser(parser); m_parser.addLineParser(parser);
QDir source = QDir(srcDir);
connect(&m_parser, &IOutputParser::addTask, this,
[source](const Task &task) {
if (task.file.isEmpty() || task.file.toFileInfo().isAbsolute()) {
TaskHub::addTask(task);
} else {
Task t = task;
t.file = Utils::FilePath::fromString(source.absoluteFilePath(task.file.toString()));
TaskHub::addTask(t);
}
});
// Always use the sourceDir: If we are triggered because the build directory is getting deleted // Always use the sourceDir: If we are triggered because the build directory is getting deleted
// then we are racing against CMakeCache.txt also getting deleted. // then we are racing against CMakeCache.txt also getting deleted.
@@ -194,7 +183,7 @@ void CMakeProcess::processStandardError()
static QString rest; static QString rest;
rest = lineSplit(rest, m_process->readAllStandardError(), [this](const QString &s) { rest = lineSplit(rest, m_process->readAllStandardError(), [this](const QString &s) {
m_parser.handleStderr(s); m_parser.appendMessage(s, Utils::StdErrFormat);
Core::MessageManager::write(s); Core::MessageManager::write(s);
}); });
} }

View File

@@ -27,8 +27,7 @@
#include "builddirparameters.h" #include "builddirparameters.h"
#include <projectexplorer/ioutputparser.h> #include <utils/outputformatter.h>
#include <utils/qtcprocess.h> #include <utils/qtcprocess.h>
#include <QElapsedTimer> #include <QElapsedTimer>
@@ -72,7 +71,7 @@ private:
void checkForCancelled(); void checkForCancelled();
std::unique_ptr<Utils::QtcProcess> m_process; std::unique_ptr<Utils::QtcProcess> m_process;
ProjectExplorer::IOutputParser m_parser; Utils::OutputFormatter m_parser;
std::unique_ptr<QFutureInterface<void>> m_future; std::unique_ptr<QFutureInterface<void>> m_future;
bool m_processWasCanceled = false; bool m_processWasCanceled = false;
QTimer m_cancelTimer; QTimer m_cancelTimer;

View File

@@ -64,14 +64,6 @@ ServerModeReader::ServerModeReader()
{ {
m_cmakeParser = new CMakeParser; m_cmakeParser = new CMakeParser;
m_parser.addLineParser(m_cmakeParser); m_parser.addLineParser(m_cmakeParser);
connect(&m_parser, &IOutputParser::addTask, this, [this](const Task &t) {
Task editable(t);
if (!editable.file.isEmpty()) {
QDir srcDir(m_parameters.sourceDirectory.toString());
editable.file = FilePath::fromString(srcDir.absoluteFilePath(editable.file.toString()));
}
TaskHub::addTask(editable);
});
} }
ServerModeReader::~ServerModeReader() ServerModeReader::~ServerModeReader()
@@ -351,7 +343,7 @@ void ServerModeReader::createNewServer()
connect(m_cmakeServer.get(), &ServerMode::cmakeMessage, [this](const QString &m) { connect(m_cmakeServer.get(), &ServerMode::cmakeMessage, [this](const QString &m) {
const QStringList lines = m.split('\n'); const QStringList lines = m.split('\n');
for (const QString &l : lines) { for (const QString &l : lines) {
m_parser.handleStderr(l); m_parser.appendMessage(l, StdErrFormat);
Core::MessageManager::write(l); Core::MessageManager::write(l);
} }
}); });

View File

@@ -37,6 +37,7 @@
#include <memory> #include <memory>
namespace ProjectExplorer { class ProjectNode; } namespace ProjectExplorer { class ProjectNode; }
namespace Utils { class OutputFormatter; }
namespace CMakeProjectManager { namespace CMakeProjectManager {
@@ -187,7 +188,7 @@ private:
QList<FileGroup *> m_fileGroups; QList<FileGroup *> m_fileGroups;
CMakeParser *m_cmakeParser = nullptr; CMakeParser *m_cmakeParser = nullptr;
ProjectExplorer::IOutputParser m_parser; Utils::OutputFormatter m_parser;
#if defined(WITH_TESTS) #if defined(WITH_TESTS)
friend class CMakeProjectPlugin; friend class CMakeProjectPlugin;

View File

@@ -26,6 +26,7 @@
#include "outputwindow.h" #include "outputwindow.h"
#include "actionmanager/actionmanager.h" #include "actionmanager/actionmanager.h"
#include "editormanager/editormanager.h"
#include "coreconstants.h" #include "coreconstants.h"
#include "coreplugin.h" #include "coreplugin.h"
#include "icore.h" #include "icore.h"
@@ -138,6 +139,11 @@ OutputWindow::OutputWindow(Context context, const QString &settingsKey, QWidget
Core::ICore::settings()->setValue(d->settingsKey, fontZoom()); Core::ICore::settings()->setValue(d->settingsKey, fontZoom());
}); });
connect(outputFormatter(), &OutputFormatter::openInEditorRequested, this,
[](const Utils::FilePath &fp, int line, int column) {
EditorManager::openEditorAt(fp.toString(), line, column);
});
undoAction->setEnabled(false); undoAction->setEnabled(false);
redoAction->setEnabled(false); redoAction->setEnabled(false);
cutAction->setEnabled(false); cutAction->setEnabled(false);
@@ -528,12 +534,10 @@ private:
return Status::NotHandled; return Status::NotHandled;
} }
void reset() override { m_handling = false; }
bool m_handling = false; bool m_handling = false;
}; };
// Handles all lines starting with "B". No continuation logic // Handles all lines starting with "B". No continuation logic.
class TestFormatterB : public OutputLineParser class TestFormatterB : public OutputLineParser
{ {
private: private:
@@ -571,18 +575,17 @@ void Internal::CorePlugin::testOutputFormatter()
" A trick\n" " A trick\n"
" embedded carriage return\n" " embedded carriage return\n"
"handled by B\n"; "handled by B\n";
OutputFormatter formatter;
QPlainTextEdit textEdit;
formatter.setPlainTextEdit(&textEdit);
formatter.setLineParsers({new TestFormatterB, new TestFormatterA});
// Stress-test the implementation by providing the input in chunks, splitting at all possible // Stress-test the implementation by providing the input in chunks, splitting at all possible
// offsets. // offsets.
for (int i = 0; i < input.length(); ++i) { for (int i = 0; i < input.length(); ++i) {
formatter.appendMessage(input.left(i), NormalMessageFormat); OutputFormatter formatter;
formatter.appendMessage(input.mid(i), NormalMessageFormat); QPlainTextEdit textEdit;
formatter.setPlainTextEdit(&textEdit);
formatter.setLineParsers({new TestFormatterB, new TestFormatterA});
formatter.appendMessage(input.left(i), StdOutFormat);
formatter.appendMessage(input.mid(i), StdOutFormat);
QCOMPARE(textEdit.toPlainText(), output); QCOMPARE(textEdit.toPlainText(), output);
formatter.clear();
} }
} }
#endif // WITH_TESTS #endif // WITH_TESTS

View File

@@ -82,6 +82,7 @@ public:
Utils::FilePath buildCommand() const; Utils::FilePath buildCommand() const;
bool init() final; bool init() final;
void setupOutputFormatter(Utils::OutputFormatter *formatter);
void doRun() final; void doRun() final;
bool fromMap(const QVariantMap &map) final; bool fromMap(const QVariantMap &map) final;
QVariantMap toMap() const final; QVariantMap toMap() const final;
@@ -222,13 +223,17 @@ bool IosBuildStep::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(new GnuMakeParser());
appendOutputParsers(target()->kit()->createOutputParsers());
outputParser()->addSearchDir(pp->effectiveWorkingDirectory());
return AbstractProcessStep::init(); return AbstractProcessStep::init();
} }
void IosBuildStep::setupOutputFormatter(OutputFormatter *formatter)
{
formatter->addLineParser(new GnuMakeParser);
formatter->addLineParsers(target()->kit()->createOutputParsers());
formatter->addSearchDir(processParameters()->effectiveWorkingDirectory());
AbstractProcessStep::setupOutputFormatter(formatter);
}
QVariantMap IosBuildStep::toMap() const QVariantMap IosBuildStep::toMap() const
{ {
QVariantMap map(AbstractProcessStep::toMap()); QVariantMap map(AbstractProcessStep::toMap());

View File

@@ -80,9 +80,6 @@ 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);
appendOutputParsers(target()->kit()->createOutputParsers());
outputParser()->addSearchDir(pp->effectiveWorkingDirectory());
return AbstractProcessStep::init(); return AbstractProcessStep::init();
} }
@@ -189,6 +186,13 @@ void IosDsymBuildStep::doRun()
AbstractProcessStep::doRun(); AbstractProcessStep::doRun();
} }
void IosDsymBuildStep::setupOutputFormatter(OutputFormatter *formatter)
{
formatter->setLineParsers(target()->kit()->createOutputParsers());
formatter->addSearchDir(processParameters()->effectiveWorkingDirectory());
AbstractProcessStep::setupOutputFormatter(formatter);
}
BuildStepConfigWidget *IosDsymBuildStep::createConfigWidget() BuildStepConfigWidget *IosDsymBuildStep::createConfigWidget()
{ {
return new IosDsymBuildStepConfigWidget(this); return new IosDsymBuildStepConfigWidget(this);

View File

@@ -56,6 +56,7 @@ public:
private: private:
bool init() override; bool init() override;
void doRun() override; void doRun() override;
void setupOutputFormatter(Utils::OutputFormatter *formatter) override;
QVariantMap toMap() const override; QVariantMap toMap() const override;
bool fromMap(const QVariantMap &map) override; bool fromMap(const QVariantMap &map) override;

View File

@@ -45,7 +45,7 @@ namespace {
class NimParser : public OutputTaskParser class NimParser : public OutputTaskParser
{ {
Status handleLine(const QString &lne, Utils::OutputFormat) override Result handleLine(const QString &lne, Utils::OutputFormat) override
{ {
const QString line = lne.trimmed(); const QString line = lne.trimmed();
static QRegularExpression regex("(.+.nim)\\((\\d+), (\\d+)\\) (.+)", static QRegularExpression regex("(.+.nim)\\((\\d+), (\\d+)\\) (.+)",
@@ -74,9 +74,12 @@ class NimParser : public OutputTaskParser
else else
return Status::NotHandled; return Status::NotHandled;
emit addTask(CompileTask(type, message, absoluteFilePath(FilePath::fromUserInput(filename)), const CompileTask t(type, message, absoluteFilePath(FilePath::fromUserInput(filename)),
lineNumber)); lineNumber);
return Status::Done; LinkSpecs linkSpecs;
addLinkSpecForAbsoluteFilePath(linkSpecs, t.file, t.line, match, 1);
scheduleTask(t, 1);
return {Status::Done, linkSpecs};
} }
}; };
@@ -95,10 +98,6 @@ NimbleBuildStep::NimbleBuildStep(BuildStepList *parentList, Core::Id id)
bool NimbleBuildStep::init() bool NimbleBuildStep::init()
{ {
auto parser = new NimParser();
parser->addSearchDir(project()->projectDirectory());
setOutputParser(parser);
ProcessParameters* params = processParameters(); ProcessParameters* params = processParameters();
params->setEnvironment(buildEnvironment()); params->setEnvironment(buildEnvironment());
params->setMacroExpander(macroExpander()); params->setMacroExpander(macroExpander());
@@ -107,6 +106,14 @@ bool NimbleBuildStep::init()
return AbstractProcessStep::init(); return AbstractProcessStep::init();
} }
void NimbleBuildStep::setupOutputFormatter(OutputFormatter *formatter)
{
const auto parser = new NimParser();
parser->addSearchDir(project()->projectDirectory());
formatter->addLineParser(parser);
AbstractProcessStep::setupOutputFormatter(formatter);
}
BuildStepConfigWidget *NimbleBuildStep::createConfigWidget() BuildStepConfigWidget *NimbleBuildStep::createConfigWidget()
{ {
return new NimbleBuildStepWidget(this); return new NimbleBuildStepWidget(this);

View File

@@ -37,7 +37,7 @@ public:
NimbleBuildStep(ProjectExplorer::BuildStepList *parentList, Core::Id id); NimbleBuildStep(ProjectExplorer::BuildStepList *parentList, Core::Id id);
bool init() override; bool init() override;
void setupOutputFormatter(Utils::OutputFormatter *formatter) override;
ProjectExplorer::BuildStepConfigWidget *createConfigWidget() override; ProjectExplorer::BuildStepConfigWidget *createConfigWidget() override;
QString arguments() const; QString arguments() const;

View File

@@ -47,7 +47,7 @@ namespace Nim {
class NimParser : public ProjectExplorer::OutputTaskParser class NimParser : public ProjectExplorer::OutputTaskParser
{ {
Status handleLine(const QString &lne, Utils::OutputFormat) override Result handleLine(const QString &lne, Utils::OutputFormat) override
{ {
const QString line = lne.trimmed(); const QString line = lne.trimmed();
static QRegularExpression regex("(.+.nim)\\((\\d+), (\\d+)\\) (.+)", static QRegularExpression regex("(.+.nim)\\((\\d+), (\\d+)\\) (.+)",
@@ -76,9 +76,12 @@ class NimParser : public ProjectExplorer::OutputTaskParser
else else
return Status::NotHandled; return Status::NotHandled;
emit addTask(CompileTask(type, message, absoluteFilePath(FilePath::fromUserInput(filename)), const CompileTask t(type, message, absoluteFilePath(FilePath::fromUserInput(filename)),
lineNumber)); lineNumber);
return Status::Done; LinkSpecs linkSpecs;
addLinkSpecForAbsoluteFilePath(linkSpecs, t.file, t.line, match, 1);
scheduleTask(t, 1);
return {Status::Done, linkSpecs};
} }
}; };
@@ -100,12 +103,12 @@ NimCompilerBuildStep::NimCompilerBuildStep(BuildStepList *parentList, Core::Id i
updateProcessParameters(); updateProcessParameters();
} }
bool NimCompilerBuildStep::init() void NimCompilerBuildStep::setupOutputFormatter(OutputFormatter *formatter)
{ {
setOutputParser(new NimParser()); formatter->addLineParser(new NimParser);
appendOutputParsers(target()->kit()->createOutputParsers()); formatter->addLineParsers(target()->kit()->createOutputParsers());
outputParser()->addSearchDir(processParameters()->effectiveWorkingDirectory()); formatter->addSearchDir(processParameters()->effectiveWorkingDirectory());
return AbstractProcessStep::init(); AbstractProcessStep::setupOutputFormatter(formatter);
} }
BuildStepConfigWidget *NimCompilerBuildStep::createConfigWidget() BuildStepConfigWidget *NimCompilerBuildStep::createConfigWidget()

View File

@@ -41,7 +41,7 @@ public:
NimCompilerBuildStep(ProjectExplorer::BuildStepList *parentList, Core::Id id); NimCompilerBuildStep(ProjectExplorer::BuildStepList *parentList, Core::Id id);
bool init() override; void setupOutputFormatter(Utils::OutputFormatter *formatter) override;
ProjectExplorer::BuildStepConfigWidget *createConfigWidget() override; ProjectExplorer::BuildStepConfigWidget *createConfigWidget() override;
bool fromMap(const QVariantMap &map) override; bool fromMap(const QVariantMap &map) override;

View File

@@ -120,7 +120,7 @@ void NimToolChain::setCompilerCommand(const FilePath &compilerCommand)
parseVersion(compilerCommand, m_version); parseVersion(compilerCommand, m_version);
} }
QList<OutputTaskParser *> NimToolChain::createOutputParsers() const QList<Utils::OutputLineParser *> NimToolChain::createOutputParsers() const
{ {
return {}; return {};
} }

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);
QList<ProjectExplorer::OutputTaskParser *> createOutputParsers() const final; QList<Utils::OutputLineParser *> createOutputParsers() 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

@@ -8,7 +8,6 @@ add_qtc_plugin(ProjectExplorer
addrunconfigdialog.cpp addrunconfigdialog.h addrunconfigdialog.cpp addrunconfigdialog.h
allprojectsfilter.cpp allprojectsfilter.h allprojectsfilter.cpp allprojectsfilter.h
allprojectsfind.cpp allprojectsfind.h allprojectsfind.cpp allprojectsfind.h
ansifilterparser.cpp ansifilterparser.h
applicationlauncher.cpp applicationlauncher.h applicationlauncher.cpp applicationlauncher.h
appoutputpane.cpp appoutputpane.h appoutputpane.cpp appoutputpane.h
baseprojectwizarddialog.cpp baseprojectwizarddialog.h baseprojectwizarddialog.cpp baseprojectwizarddialog.h

View File

@@ -24,7 +24,6 @@
****************************************************************************/ ****************************************************************************/
#include "abstractprocessstep.h" #include "abstractprocessstep.h"
#include "ansifilterparser.h"
#include "buildconfiguration.h" #include "buildconfiguration.h"
#include "buildstep.h" #include "buildstep.h"
#include "ioutputparser.h" #include "ioutputparser.h"
@@ -37,8 +36,8 @@
#include <coreplugin/reaper.h> #include <coreplugin/reaper.h>
#include <utils/fileinprojectfinder.h>
#include <utils/fileutils.h> #include <utils/fileutils.h>
#include <utils/outputformatter.h>
#include <utils/qtcassert.h> #include <utils/qtcassert.h>
#include <utils/qtcprocess.h> #include <utils/qtcprocess.h>
@@ -106,27 +105,18 @@ public:
AbstractProcessStep *q; AbstractProcessStep *q;
std::unique_ptr<Utils::QtcProcess> m_process; std::unique_ptr<Utils::QtcProcess> m_process;
IOutputParser m_outputParser;
ProcessParameters m_param; ProcessParameters m_param;
bool m_ignoreReturnValue = false; bool m_ignoreReturnValue = 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;
OutputFormatter *outputFormatter = nullptr;
}; };
AbstractProcessStep::AbstractProcessStep(BuildStepList *bsl, Core::Id id) : 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,
[this](const Task &task, int linkedLines, int skipLines) {
// Do not bother to report issues if we do not care about the results of
// the buildstep anyway:
// TODO: Does that make sense? The user might still want to know that
// something failed, even if it wasn't fatal...
if (!d->m_ignoreReturnValue)
emit addTask(task, linkedLines, skipLines);
});
} }
AbstractProcessStep::~AbstractProcessStep() AbstractProcessStep::~AbstractProcessStep()
@@ -134,36 +124,6 @@ AbstractProcessStep::~AbstractProcessStep()
delete d; delete d;
} }
/*!
Deletes all existing output parsers and starts a new chain with the
given parser.
*/
void AbstractProcessStep::setOutputParser(OutputTaskParser *parser)
{
d->m_outputParser.setLineParsers({parser});
}
/*!
Appends the given output parser to the existing chain of parsers.
*/
void AbstractProcessStep::appendOutputParser(OutputTaskParser *parser)
{
if (!parser)
return;
d->m_outputParser.addLineParser(parser);
}
void AbstractProcessStep::appendOutputParsers(const QList<OutputTaskParser *> &parsers)
{
for (OutputTaskParser * const p : parsers)
appendOutputParser(p);
}
IOutputParser *AbstractProcessStep::outputParser() const
{
return &d->m_outputParser;
}
void AbstractProcessStep::emitFaultyConfigurationMessage() void AbstractProcessStep::emitFaultyConfigurationMessage()
{ {
emit addOutput(tr("Configuration is faulty. Check the Issues view for details."), emit addOutput(tr("Configuration is faulty. Check the Issues view for details."),
@@ -194,14 +154,16 @@ void AbstractProcessStep::setIgnoreReturnValue(bool b)
bool AbstractProcessStep::init() bool AbstractProcessStep::init()
{ {
Utils::FileInProjectFinder fileFinder;
fileFinder.setProjectDirectory(project()->projectDirectory());
fileFinder.setProjectFiles(project()->files(Project::AllFiles));
d->m_outputParser.addFilter(&Internal::filterAnsiEscapeCodes);
d->m_outputParser.setFileFinder(fileFinder);
return !d->m_process; return !d->m_process;
} }
void AbstractProcessStep::setupOutputFormatter(OutputFormatter *formatter)
{
formatter->setDemoteErrorsToWarnings(d->m_ignoreReturnValue);
d->outputFormatter = formatter;
BuildStep::setupOutputFormatter(formatter);
}
/*! /*!
Reimplemented from BuildStep::init(). You need to call this from Reimplemented from BuildStep::init(). You need to call this from
YourBuildStep::run(). YourBuildStep::run().
@@ -257,7 +219,6 @@ 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_outputParser.clear();
finish(false); finish(false);
return; return;
} }
@@ -285,7 +246,6 @@ 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_outputParser.clear();
d->m_process.reset(); d->m_process.reset();
// Report result // Report result
@@ -315,9 +275,6 @@ void AbstractProcessStep::processStarted()
void AbstractProcessStep::processFinished(int exitCode, QProcess::ExitStatus status) void AbstractProcessStep::processFinished(int exitCode, QProcess::ExitStatus status)
{ {
d->m_outputParser.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) {
emit addOutput(tr("The process \"%1\" exited normally.").arg(command), emit addOutput(tr("The process \"%1\" exited normally.").arg(command),
@@ -351,7 +308,7 @@ void AbstractProcessStep::processStartupFailed()
bool AbstractProcessStep::processSucceeded(int exitCode, QProcess::ExitStatus status) bool AbstractProcessStep::processSucceeded(int exitCode, QProcess::ExitStatus status)
{ {
if (outputParser()->hasFatalErrors()) if (d->outputFormatter->hasFatalErrors())
return false; return false;
return exitCode == 0 && status == QProcess::NormalExit; return exitCode == 0 && status == QProcess::NormalExit;
@@ -372,7 +329,6 @@ void AbstractProcessStep::processReadyReadStdOutput()
void AbstractProcessStep::stdOutput(const QString &output) void AbstractProcessStep::stdOutput(const QString &output)
{ {
d->m_outputParser.handleStdout(output);
emit addOutput(output, BuildStep::OutputFormat::Stdout, BuildStep::DontAppendNewline); emit addOutput(output, BuildStep::OutputFormat::Stdout, BuildStep::DontAppendNewline);
} }
@@ -391,7 +347,6 @@ void AbstractProcessStep::processReadyReadStdError()
void AbstractProcessStep::stdError(const QString &output) void AbstractProcessStep::stdError(const QString &output)
{ {
d->m_outputParser.handleStderr(output);
emit addOutput(output, BuildStep::OutputFormat::Stderr, BuildStep::DontAppendNewline); emit addOutput(output, BuildStep::OutputFormat::Stderr, BuildStep::DontAppendNewline);
} }

View File

@@ -31,8 +31,6 @@
namespace Utils { class FilePath; } namespace Utils { class FilePath; }
namespace ProjectExplorer { namespace ProjectExplorer {
class IOutputParser;
class OutputTaskParser; class OutputTaskParser;
class ProcessParameters; class ProcessParameters;
@@ -47,17 +45,13 @@ public:
bool ignoreReturnValue(); bool ignoreReturnValue();
void setIgnoreReturnValue(bool b); void setIgnoreReturnValue(bool b);
void setOutputParser(OutputTaskParser *parser);
void appendOutputParser(OutputTaskParser *parser);
void appendOutputParsers(const QList<OutputTaskParser *> &parsers);
IOutputParser *outputParser() const;
void emitFaultyConfigurationMessage(); void emitFaultyConfigurationMessage();
protected: protected:
AbstractProcessStep(BuildStepList *bsl, Core::Id id); AbstractProcessStep(BuildStepList *bsl, Core::Id id);
~AbstractProcessStep() override; ~AbstractProcessStep() override;
bool init() override; bool init() override;
void setupOutputFormatter(Utils::OutputFormatter *formatter) override;
void doRun() override; void doRun() override;
void setLowPriority(); void setLowPriority();
virtual void finish(bool success); virtual void finish(bool success);

View File

@@ -1,164 +0,0 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#include "ansifilterparser.h"
#ifdef WITH_TESTS
#include "projectexplorer.h"
#include "outputparser_test.h"
#include "task.h"
#include <QTest>
#endif // WITH_TESTS
namespace ProjectExplorer {
namespace Internal {
enum AnsiState {
PLAIN,
ANSI_START,
ANSI_CSI,
ANSI_SEQUENCE,
ANSI_WAITING_FOR_ST,
ANSI_ST_STARTED
};
QString filterAnsiEscapeCodes(const QString &line)
{
QString result;
result.reserve(line.count());
static AnsiState state = PLAIN;
foreach (const QChar c, line) {
unsigned int val = c.unicode();
switch (state) {
case PLAIN:
if (val == 27) // 'ESC'
state = ANSI_START;
else if (val == 155) // equivalent to 'ESC'-'['
state = ANSI_CSI;
else
result.append(c);
break;
case ANSI_START:
if (val == 91) // [
state = ANSI_CSI;
else if (val == 80 || val == 93 || val == 94 || val == 95) // 'P', ']', '^' and '_'
state = ANSI_WAITING_FOR_ST;
else if (val >= 64 && val <= 95)
state = PLAIN;
else
state = ANSI_SEQUENCE;
break;
case ANSI_CSI:
if (val >= 64 && val <= 126) // Anything between '@' and '~'
state = PLAIN;
break;
case ANSI_SEQUENCE:
if (val >= 64 && val <= 95) // Anything between '@' and '_'
state = PLAIN;
break;
case ANSI_WAITING_FOR_ST:
if (val == 7) // 'BEL'
state = PLAIN;
if (val == 27) // 'ESC'
state = ANSI_ST_STARTED;
break;
case ANSI_ST_STARTED:
if (val == 92) // '\'
state = PLAIN;
else
state = ANSI_WAITING_FOR_ST;
break;
}
}
return result;
}
} // namespace Internal
#ifdef WITH_TESTS
void ProjectExplorerPlugin::testAnsiFilterOutputParser_data()
{
QTest::addColumn<QString>("input");
QTest::addColumn<OutputParserTester::Channel>("inputChannel");
QTest::addColumn<QString>("childStdOutLines");
QTest::addColumn<QString>("childStdErrLines");
QTest::addColumn<QString>("outputLines");
QTest::newRow("pass-through stdout")
<< QString::fromLatin1("Sometext") << OutputParserTester::STDOUT
<< QString::fromLatin1("Sometext\n") << QString();
QTest::newRow("pass-through stderr")
<< QString::fromLatin1("Sometext") << OutputParserTester::STDERR
<< QString() << QString::fromLatin1("Sometext\n");
QString input = QString::fromLatin1("te") + QChar(27) + QString::fromLatin1("Nst");
QTest::newRow("ANSI: ESC-N")
<< input << OutputParserTester::STDOUT
<< QString::fromLatin1("test\n") << QString();
input = QString::fromLatin1("te") + QChar(27) + QLatin1String("^ignored") + QChar(27) + QLatin1String("\\st");
QTest::newRow("ANSI: ESC-^ignoredESC-\\")
<< input << OutputParserTester::STDOUT
<< QString::fromLatin1("test\n") << QString();
input = QString::fromLatin1("te") + QChar(27) + QLatin1String("]0;ignored") + QChar(7) + QLatin1String("st");
QTest::newRow("ANSI: window title change")
<< input << OutputParserTester::STDOUT
<< QString::fromLatin1("test\n") << QString();
input = QString::fromLatin1("te") + QChar(27) + QLatin1String("[Ast");
QTest::newRow("ANSI: cursor up")
<< input << OutputParserTester::STDOUT
<< QString::fromLatin1("test\n") << QString();
input = QString::fromLatin1("te") + QChar(27) + QLatin1String("[2Ast");
QTest::newRow("ANSI: cursor up (with int parameter)")
<< input << OutputParserTester::STDOUT
<< QString::fromLatin1("test\n") << QString();
input = QString::fromLatin1("te") + QChar(27) + QLatin1String("[2;3Hst");
QTest::newRow("ANSI: position cursor")
<< input << OutputParserTester::STDOUT
<< QString::fromLatin1("test\n") << QString();
input = QString::fromLatin1("te") + QChar(27) + QLatin1String("[31;1mst");
QTest::newRow("ANSI: bold red")
<< input << OutputParserTester::STDOUT
<< QString::fromLatin1("test\n") << QString();
}
void ProjectExplorerPlugin::testAnsiFilterOutputParser()
{
OutputParserTester testbench;
testbench.addFilter(&Internal::filterAnsiEscapeCodes);
QFETCH(QString, input);
QFETCH(OutputParserTester::Channel, inputChannel);
QFETCH(QString, childStdOutLines);
QFETCH(QString, childStdErrLines);
testbench.testParsing(input, inputChannel,
Tasks(), childStdOutLines, childStdErrLines,
QString());
}
#endif
} // namespace ProjectExplorer

View File

@@ -1,38 +0,0 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#pragma once
#include "ioutputparser.h"
#include "projectexplorer_export.h"
namespace ProjectExplorer {
namespace Internal {
QString filterAnsiEscapeCodes(const QString &line);
} // namespace Internal
} // namespace ProjectExplorer

View File

@@ -46,6 +46,7 @@
#include <coreplugin/progressmanager/futureprogress.h> #include <coreplugin/progressmanager/futureprogress.h>
#include <coreplugin/progressmanager/progressmanager.h> #include <coreplugin/progressmanager/progressmanager.h>
#include <extensionsystem/pluginmanager.h> #include <extensionsystem/pluginmanager.h>
#include <utils/outputformatter.h>
#include <utils/runextensions.h> #include <utils/runextensions.h>
#include <utils/stringutils.h> #include <utils/stringutils.h>
@@ -680,6 +681,7 @@ void BuildManager::nextStep()
} }
static const auto finishedHandler = [](bool success) { static const auto finishedHandler = [](bool success) {
d->m_outputWindow->outputFormatter()->flush();
d->m_lastStepSucceeded = success; d->m_lastStepSucceeded = success;
disconnect(d->m_currentBuildStep, nullptr, instance(), nullptr); disconnect(d->m_currentBuildStep, nullptr, instance(), nullptr);
BuildManager::nextBuildQueue(); BuildManager::nextBuildQueue();
@@ -688,6 +690,8 @@ void BuildManager::nextStep()
Qt::QueuedConnection); Qt::QueuedConnection);
connect(d->m_currentBuildStep, &BuildStep::progress, connect(d->m_currentBuildStep, &BuildStep::progress,
instance(), &BuildManager::progressChanged); instance(), &BuildManager::progressChanged);
d->m_outputWindow->outputFormatter()->reset();
d->m_currentBuildStep->setupOutputFormatter(d->m_outputWindow->outputFormatter());
d->m_currentBuildStep->run(); d->m_currentBuildStep->run();
} else { } else {
d->m_running = false; d->m_running = false;

View File

@@ -36,6 +36,8 @@
#include <coreplugin/variablechooser.h> #include <coreplugin/variablechooser.h>
#include <utils/algorithm.h> #include <utils/algorithm.h>
#include <utils/fileinprojectfinder.h>
#include <utils/outputformatter.h>
#include <utils/qtcassert.h> #include <utils/qtcassert.h>
#include <utils/runextensions.h> #include <utils/runextensions.h>
@@ -254,6 +256,14 @@ QString BuildStep::fallbackWorkingDirectory() const
return {Constants::DEFAULT_WORKING_DIR_ALTERNATE}; return {Constants::DEFAULT_WORKING_DIR_ALTERNATE};
} }
void BuildStep::setupOutputFormatter(OutputFormatter *formatter)
{
Utils::FileInProjectFinder fileFinder;
fileFinder.setProjectDirectory(project()->projectDirectory());
fileFinder.setProjectFiles(project()->files(Project::AllFiles));
formatter->setFileFinder(fileFinder);
}
void BuildStep::reportRunResult(QFutureInterface<bool> &fi, bool success) void BuildStep::reportRunResult(QFutureInterface<bool> &fi, bool success)
{ {
fi.reportResult(success); fi.reportResult(success);

View File

@@ -44,6 +44,7 @@ namespace Utils {
class Environment; class Environment;
class FilePath; class FilePath;
class MacroExpander; class MacroExpander;
class OutputFormatter;
} // Utils } // Utils
namespace ProjectExplorer { namespace ProjectExplorer {
@@ -91,6 +92,8 @@ public:
Utils::MacroExpander *macroExpander() const; Utils::MacroExpander *macroExpander() const;
QString fallbackWorkingDirectory() const; QString fallbackWorkingDirectory() const;
virtual void setupOutputFormatter(Utils::OutputFormatter *formatter);
enum class OutputFormat { enum class OutputFormat {
Stdout, Stderr, // These are for forwarded output from external tools Stdout, Stderr, // These are for forwarded output from external tools
NormalMessage, ErrorMessage // These are for messages from Creator itself NormalMessage, ErrorMessage // These are for messages from Creator itself
@@ -117,8 +120,8 @@ public:
signals: signals:
/// Adds a \p task to the Issues pane. /// Adds a \p task to the Issues pane.
/// Do note that for linking compile output with tasks, you should first emit the task /// Do note that for linking compile output with tasks, you should first emit the output
/// and then emit the output. \p linkedOutput lines will be linked. And the last \p skipLines will /// and then emit the task. \p linkedOutput lines will be linked. And the last \p skipLines will
/// be skipped. /// be skipped.
void addTask(const ProjectExplorer::Task &task, int linkedOutputLines = 0, int skipLines = 0); void addTask(const ProjectExplorer::Task &task, int linkedOutputLines = 0, int skipLines = 0);

View File

@@ -55,12 +55,12 @@ ClangParser::ClangParser() :
setObjectName(QLatin1String("ClangParser")); setObjectName(QLatin1String("ClangParser"));
} }
QList<OutputTaskParser *> ClangParser::clangParserSuite() QList<OutputLineParser *> ClangParser::clangParserSuite()
{ {
return {new ClangParser, new Internal::LldParser, new LdParser}; return {new ClangParser, new Internal::LldParser, new LdParser};
} }
OutputTaskParser::Status ClangParser::handleLine(const QString &line, OutputFormat type) OutputLineParser::Result ClangParser::handleLine(const QString &line, OutputFormat type)
{ {
if (type != StdErrFormat) if (type != StdErrFormat)
return Status::NotHandled; return Status::NotHandled;
@@ -82,11 +82,12 @@ OutputTaskParser::Status ClangParser::handleLine(const QString &line, OutputForm
match = m_inLineRegExp.match(lne); match = m_inLineRegExp.match(lne);
if (match.hasMatch()) { if (match.hasMatch()) {
m_expectSnippet = true; m_expectSnippet = true;
newTask(CompileTask(Task::Unknown, const FilePath filePath = absoluteFilePath(FilePath::fromUserInput(match.captured(2)));
lne.trimmed(), const int lineNo = match.captured(3).toInt();
absoluteFilePath(FilePath::fromUserInput(match.captured(2))), LinkSpecs linkSpecs;
match.captured(3).toInt() /* line */)); addLinkSpecForAbsoluteFilePath(linkSpecs, filePath, lineNo, match, 2);
return Status::InProgress; newTask(CompileTask(Task::Unknown, lne.trimmed(), filePath, lineNo));
return {Status::InProgress, linkSpecs};
} }
match = m_messageRegExp.match(lne); match = m_messageRegExp.match(lne);
@@ -96,10 +97,10 @@ OutputTaskParser::Status ClangParser::handleLine(const QString &line, OutputForm
int lineNo = match.captured(4).toInt(&ok); int lineNo = match.captured(4).toInt(&ok);
if (!ok) if (!ok)
lineNo = match.captured(5).toInt(&ok); lineNo = match.captured(5).toInt(&ok);
newTask(CompileTask(taskType(match.captured(7)), const FilePath filePath = absoluteFilePath(FilePath::fromUserInput(match.captured(1)));
match.captured(8), LinkSpecs linkSpecs;
absoluteFilePath(FilePath::fromUserInput(match.captured(1))), addLinkSpecForAbsoluteFilePath(linkSpecs, filePath, lineNo, match, 1);
lineNo)); newTask(CompileTask(taskType(match.captured(7)), match.captured(8), filePath, lineNo));
return Status::InProgress; return Status::InProgress;
} }
@@ -255,8 +256,7 @@ void ProjectExplorerPlugin::testClangOutputParser_data()
<< (Tasks() << (Tasks()
<< CompileTask(Task::Unknown, << CompileTask(Task::Unknown,
"Note: No relevant classes found. No output generated.", "Note: No relevant classes found. No output generated.",
FilePath::fromUserInput("/home/qtwebkithelpviewer.h"), FilePath::fromUserInput("/home/qtwebkithelpviewer.h")))
0))
<< QString(); << QString();
} }

View File

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

View File

@@ -24,12 +24,14 @@
****************************************************************************/ ****************************************************************************/
#include "compileoutputwindow.h" #include "compileoutputwindow.h"
#include "buildmanager.h" #include "buildmanager.h"
#include "showoutputtaskhandler.h" #include "ioutputparser.h"
#include "task.h"
#include "projectexplorer.h" #include "projectexplorer.h"
#include "projectexplorericons.h" #include "projectexplorericons.h"
#include "projectexplorersettings.h" #include "projectexplorersettings.h"
#include "showoutputtaskhandler.h"
#include "task.h"
#include "taskhub.h" #include "taskhub.h"
#include <coreplugin/outputwindow.h> #include <coreplugin/outputwindow.h>
@@ -40,7 +42,8 @@
#include <texteditor/texteditorsettings.h> #include <texteditor/texteditorsettings.h>
#include <texteditor/fontsettings.h> #include <texteditor/fontsettings.h>
#include <texteditor/behaviorsettings.h> #include <texteditor/behaviorsettings.h>
#include <utils/outputformat.h> #include <utils/algorithm.h>
#include <utils/outputformatter.h>
#include <utils/proxyaction.h> #include <utils/proxyaction.h>
#include <utils/theme/theme.h> #include <utils/theme/theme.h>
#include <utils/utilsicons.h> #include <utils/utilsicons.h>
@@ -67,74 +70,29 @@ const char WRAP_OUTPUT_KEY[] = "ProjectExplorer/Settings/WrapBuildOutput";
const char MAX_LINES_KEY[] = "ProjectExplorer/Settings/MaxBuildOutputLines"; const char MAX_LINES_KEY[] = "ProjectExplorer/Settings/MaxBuildOutputLines";
const char OPTIONS_PAGE_ID[] = "C.ProjectExplorer.CompileOutputOptions"; const char OPTIONS_PAGE_ID[] = "C.ProjectExplorer.CompileOutputOptions";
class CompileOutputTextEdit : public Core::OutputWindow
{
Q_OBJECT
public:
CompileOutputTextEdit(const Core::Context &context) : Core::OutputWindow(context, SETTINGS_KEY)
{
setMouseTracking(true);
}
void addTask(const Task &task, int blocknumber)
{
m_taskids.insert(blocknumber, task.taskId);
}
void clearTasks()
{
m_taskids.clear();
}
protected:
void mouseMoveEvent(QMouseEvent *ev) override
{
const int line = cursorForPosition(ev->pos()).block().blockNumber();
if (m_taskids.contains(line) && m_mousePressButton == Qt::NoButton)
viewport()->setCursor(Qt::PointingHandCursor);
else
viewport()->setCursor(Qt::IBeamCursor);
QPlainTextEdit::mouseMoveEvent(ev);
}
void mousePressEvent(QMouseEvent *ev) override
{
m_mousePressPosition = ev->pos();
m_mousePressButton = ev->button();
QPlainTextEdit::mousePressEvent(ev);
}
void mouseReleaseEvent(QMouseEvent *ev) override
{
if ((m_mousePressPosition - ev->pos()).manhattanLength() < 4
&& m_mousePressButton == Qt::LeftButton) {
int line = cursorForPosition(ev->pos()).block().blockNumber();
if (unsigned taskid = m_taskids.value(line, 0))
TaskHub::showTaskInEditor(taskid);
}
m_mousePressButton = Qt::NoButton;
QPlainTextEdit::mouseReleaseEvent(ev);
}
private:
QHash<int, unsigned int> m_taskids; //Map blocknumber to taskId
QPoint m_mousePressPosition;
Qt::MouseButton m_mousePressButton = Qt::NoButton;
};
CompileOutputWindow::CompileOutputWindow(QAction *cancelBuildAction) : CompileOutputWindow::CompileOutputWindow(QAction *cancelBuildAction) :
m_cancelBuildButton(new QToolButton), m_cancelBuildButton(new QToolButton),
m_settingsButton(new QToolButton) m_settingsButton(new QToolButton)
{ {
Core::Context context(C_COMPILE_OUTPUT); Core::Context context(C_COMPILE_OUTPUT);
m_outputWindow = new CompileOutputTextEdit(context); m_outputWindow = new Core::OutputWindow(context, SETTINGS_KEY);
m_outputWindow->setWindowTitle(displayName()); m_outputWindow->setWindowTitle(displayName());
m_outputWindow->setWindowIcon(Icons::WINDOW.icon()); m_outputWindow->setWindowIcon(Icons::WINDOW.icon());
m_outputWindow->setReadOnly(true); m_outputWindow->setReadOnly(true);
m_outputWindow->setUndoRedoEnabled(false); m_outputWindow->setUndoRedoEnabled(false);
m_outputWindow->setMaxCharCount(Core::Constants::DEFAULT_MAX_CHAR_COUNT); m_outputWindow->setMaxCharCount(Core::Constants::DEFAULT_MAX_CHAR_COUNT);
outputFormatter()->overridePostPrintAction([this](Utils::OutputLineParser *parser) {
if (const auto taskParser = qobject_cast<OutputTaskParser *>(parser)) {
int offset = 0;
Utils::reverseForeach(taskParser->taskInfo(), [this, &offset](const OutputTaskParser::TaskInfo &ti) {
registerPositionOf(ti.task, ti.linkedLines, ti.skippedLines, offset);
offset += ti.linkedLines;
});
}
parser->runPostPrintActions();
});
// Let selected text be colored as if the text edit was editable, // Let selected text be colored as if the text edit was editable,
// otherwise the highlight for searching is too light // otherwise the highlight for searching is too light
QPalette p = m_outputWindow->palette(); QPalette p = m_outputWindow->palette();
@@ -254,7 +212,6 @@ void CompileOutputWindow::appendText(const QString &text, BuildStep::OutputForma
void CompileOutputWindow::clearContents() void CompileOutputWindow::clearContents()
{ {
m_outputWindow->clear(); m_outputWindow->clear();
m_outputWindow->clearTasks();
m_taskPositions.clear(); m_taskPositions.clear();
} }
@@ -287,22 +244,17 @@ bool CompileOutputWindow::canNavigate() const
return false; return false;
} }
void CompileOutputWindow::registerPositionOf(const Task &task, int linkedOutputLines, int skipLines) void CompileOutputWindow::registerPositionOf(const Task &task, int linkedOutputLines, int skipLines,
int offset)
{ {
if (linkedOutputLines <= 0) if (linkedOutputLines <= 0)
return; return;
const int charNumber = m_outputWindow->document()->characterCount();
if (charNumber > m_outputWindow->maxCharCount())
return;
const int blocknumber = m_outputWindow->document()->blockCount(); const int blocknumber = m_outputWindow->document()->blockCount() - offset - 1;
const int startLine = blocknumber - linkedOutputLines + 1 - skipLines; const int firstLine = blocknumber - linkedOutputLines - skipLines;
const int endLine = blocknumber - skipLines; const int lastLine = firstLine + linkedOutputLines - 1;
m_taskPositions.insert(task.taskId, qMakePair(startLine, endLine)); m_taskPositions.insert(task.taskId, qMakePair(firstLine, lastLine));
for (int i = startLine; i <= endLine; ++i)
m_outputWindow->addTask(task, i);
} }
bool CompileOutputWindow::knowsPositionOf(const Task &task) bool CompileOutputWindow::knowsPositionOf(const Task &task)
@@ -340,6 +292,11 @@ void CompileOutputWindow::setSettings(const CompileOutputSettings &settings)
updateFromSettings(); updateFromSettings();
} }
Utils::OutputFormatter *CompileOutputWindow::outputFormatter() const
{
return m_outputWindow->outputFormatter();
}
void CompileOutputWindow::updateFilter() void CompileOutputWindow::updateFilter()
{ {
m_outputWindow->updateFilterProperties(filterText(), filterCaseSensitivity(), m_outputWindow->updateFilterProperties(filterText(), filterCaseSensitivity(),
@@ -415,5 +372,3 @@ CompileOutputSettingsPage::CompileOutputSettingsPage()
} // Internal } // Internal
} // ProjectExplorer } // ProjectExplorer
#include "compileoutputwindow.moc"

View File

@@ -37,6 +37,9 @@ QT_BEGIN_NAMESPACE
class QToolButton; class QToolButton;
QT_END_NAMESPACE QT_END_NAMESPACE
namespace Core { class OutputWindow; }
namespace Utils { class OutputFormatter; }
namespace ProjectExplorer { namespace ProjectExplorer {
class Task; class Task;
@@ -70,7 +73,7 @@ public:
void appendText(const QString &text, BuildStep::OutputFormat format); void appendText(const QString &text, BuildStep::OutputFormat format);
void registerPositionOf(const Task &task, int linkedOutputLines, int skipLines); void registerPositionOf(const Task &task, int linkedOutputLines, int skipLines, int offset = 0);
bool knowsPositionOf(const Task &task); bool knowsPositionOf(const Task &task);
void showPositionOf(const Task &task); void showPositionOf(const Task &task);
@@ -79,6 +82,8 @@ public:
const CompileOutputSettings &settings() const { return m_settings; } const CompileOutputSettings &settings() const { return m_settings; }
void setSettings(const CompileOutputSettings &settings); void setSettings(const CompileOutputSettings &settings);
Utils::OutputFormatter *outputFormatter() const;
private: private:
void updateFilter() override; void updateFilter() override;
@@ -86,7 +91,7 @@ private:
void storeSettings() const; void storeSettings() const;
void updateFromSettings(); void updateFromSettings();
CompileOutputTextEdit *m_outputWindow; Core::OutputWindow *m_outputWindow;
QHash<unsigned int, QPair<int, int>> m_taskPositions; QHash<unsigned int, QPair<int, int>> m_taskPositions;
ShowOutputTaskHandler *m_handler; ShowOutputTaskHandler *m_handler;
QToolButton *m_cancelBuildButton; QToolButton *m_cancelBuildButton;

View File

@@ -129,45 +129,51 @@ Core::Id CustomParser::id()
return Core::Id("ProjectExplorer.OutputParser.Custom"); return Core::Id("ProjectExplorer.OutputParser.Custom");
} }
OutputTaskParser::Status CustomParser::handleLine(const QString &line, OutputFormat type) OutputLineParser::Result CustomParser::handleLine(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)) return parseLine(line, channel);
return Status::Done;
return Status::NotHandled;
} }
bool CustomParser::hasMatch(const QString &line, CustomParserExpression::CustomParserChannel channel, OutputLineParser::Result CustomParser::hasMatch(
const CustomParserExpression &expression, Task::TaskType taskType) const QString &line,
CustomParserExpression::CustomParserChannel channel,
const CustomParserExpression &expression,
Task::TaskType taskType
)
{ {
if (!(channel & expression.channel())) if (!(channel & expression.channel()))
return false; return Status::NotHandled;
if (expression.pattern().isEmpty()) if (expression.pattern().isEmpty())
return false; return Status::NotHandled;
const QRegularExpressionMatch match = expression.match(line); const QRegularExpressionMatch match = expression.match(line);
if (!match.hasMatch()) if (!match.hasMatch())
return false; return Status::NotHandled;
const FilePath fileName = absoluteFilePath(FilePath::fromString( const FilePath fileName = absoluteFilePath(FilePath::fromString(
match.captured(expression.fileNameCap()))); match.captured(expression.fileNameCap())));
const int lineNumber = match.captured(expression.lineNumberCap()).toInt(); const int lineNumber = match.captured(expression.lineNumberCap()).toInt();
const QString message = match.captured(expression.messageCap()); const QString message = match.captured(expression.messageCap());
LinkSpecs linkSpecs;
emit addTask(CompileTask(taskType, message, fileName, lineNumber), 1); addLinkSpecForAbsoluteFilePath(linkSpecs, fileName, lineNumber, match,
return true; expression.fileNameCap());
scheduleTask(CompileTask(taskType, message, fileName, lineNumber), 1);
return Status::Done;
} }
bool CustomParser::parseLine(const QString &rawLine, CustomParserExpression::CustomParserChannel channel) OutputLineParser::Result CustomParser::parseLine(
const QString &rawLine,
CustomParserExpression::CustomParserChannel channel
)
{ {
const QString line = rawLine.trimmed(); const QString line = rawLine.trimmed();
const Result res = hasMatch(line, channel, m_error, Task::Error);
if (hasMatch(line, channel, m_error, Task::Error)) if (res.status != Status::NotHandled)
return true; return res;
return hasMatch(line, channel, m_warning, Task::Warning); return hasMatch(line, channel, m_warning, Task::Warning);
} }

View File

@@ -91,11 +91,11 @@ public:
static Core::Id id(); static Core::Id id();
private: private:
Status handleLine(const QString &line, Utils::OutputFormat type) override; Result handleLine(const QString &line, Utils::OutputFormat type) override;
bool hasMatch(const QString &line, CustomParserExpression::CustomParserChannel channel, Result hasMatch(const QString &line, CustomParserExpression::CustomParserChannel channel,
const CustomParserExpression &expression, Task::TaskType taskType); const CustomParserExpression &expression, Task::TaskType taskType);
bool parseLine(const QString &rawLine, CustomParserExpression::CustomParserChannel channel); Result parseLine(const QString &rawLine, CustomParserExpression::CustomParserChannel channel);
CustomParserExpression m_error; CustomParserExpression m_error;
CustomParserExpression m_warning; CustomParserExpression m_warning;

View File

@@ -196,7 +196,7 @@ QStringList CustomToolChain::suggestedMkspecList() const
return m_mkspecs; return m_mkspecs;
} }
QList<OutputTaskParser *> CustomToolChain::createOutputParsers() const QList<Utils::OutputLineParser *> CustomToolChain::createOutputParsers() const
{ {
if (m_outputParserId == GccParser::id()) if (m_outputParserId == GccParser::id())
return GccParser::gccParserSuite(); return GccParser::gccParserSuite();

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;
QList<OutputTaskParser *> createOutputParsers() const override; QList<Utils::OutputLineParser *> createOutputParsers() const override;
QStringList headerPathsList() const; QStringList headerPathsList() const;
void setHeaderPaths(const QStringList &list); void setHeaderPaths(const QStringList &list);

View File

@@ -66,7 +66,7 @@ Core::Id GccParser::id()
return Core::Id("ProjectExplorer.OutputParser.Gcc"); return Core::Id("ProjectExplorer.OutputParser.Gcc");
} }
QList<OutputTaskParser *> GccParser::gccParserSuite() QList<OutputLineParser *> GccParser::gccParserSuite()
{ {
return {new GccParser, new Internal::LldParser, new LdParser}; return {new GccParser, new Internal::LldParser, new LdParser};
} }
@@ -84,7 +84,7 @@ void GccParser::flush()
return; return;
Task t = m_currentTask; Task t = m_currentTask;
m_currentTask.clear(); m_currentTask.clear();
emit addTask(t, m_lines, 1); scheduleTask(t, m_lines, 1);
m_lines = 0; m_lines = 0;
} }
@@ -107,11 +107,9 @@ void GccParser::amendDescription(const QString &desc, bool monospaced)
return; return;
} }
OutputTaskParser::Status GccParser::handleLine(const QString &line, OutputFormat type) OutputLineParser::Result GccParser::handleLine(const QString &line, OutputFormat type)
{ {
if (type == StdOutFormat) { if (type == StdOutFormat) {
// TODO: The "flush on channel switch" logic could possibly also done centrally.
// But see MSVC with the stdout/stderr switches because of jom
flush(); flush();
return Status::NotHandled; return Status::NotHandled;
} }
@@ -146,7 +144,6 @@ OutputTaskParser::Status GccParser::handleLine(const QString &line, OutputFormat
match = m_regExp.match(lne); match = m_regExp.match(lne);
if (match.hasMatch()) { if (match.hasMatch()) {
Utils::FilePath filename = Utils::FilePath::fromUserInput(match.captured(1));
int lineno = match.captured(3).toInt(); int lineno = match.captured(3).toInt();
Task::TaskType type = Task::Unknown; Task::TaskType type = Task::Unknown;
QString description = match.captured(8); QString description = match.captured(8);
@@ -161,17 +158,21 @@ OutputTaskParser::Status GccParser::handleLine(const QString &line, OutputFormat
if (match.captured(5).startsWith(QLatin1Char('#'))) if (match.captured(5).startsWith(QLatin1Char('#')))
description = match.captured(5) + description; description = match.captured(5) + description;
newTask(CompileTask(type, description, absoluteFilePath(filename), lineno)); const FilePath filePath = absoluteFilePath(FilePath::fromUserInput(match.captured(1)));
return Status::InProgress; LinkSpecs linkSpecs;
addLinkSpecForAbsoluteFilePath(linkSpecs, filePath, lineno, match, 1);
newTask(CompileTask(type, description, filePath, lineno));
return {Status::InProgress, linkSpecs};
} }
match = m_regExpIncluded.match(lne); match = m_regExpIncluded.match(lne);
if (match.hasMatch()) { if (match.hasMatch()) {
newTask(CompileTask(Task::Unknown, const FilePath filePath = absoluteFilePath(FilePath::fromUserInput(match.captured(1)));
lne.trimmed() /* description */, const int lineNo = match.captured(3).toInt();
absoluteFilePath(Utils::FilePath::fromUserInput(match.captured(1))), LinkSpecs linkSpecs;
match.captured(3).toInt() /* linenumber */)); addLinkSpecForAbsoluteFilePath(linkSpecs, filePath, lineNo, match, 1);
return Status::InProgress; newTask(CompileTask(Task::Unknown, lne.trimmed() /* description */, filePath, lineNo));
return {Status::InProgress, linkSpecs};
} else if (lne.startsWith(' ') && !m_currentTask.isNull()) { } else if (lne.startsWith(' ') && !m_currentTask.isNull()) {
amendDescription(lne, true); amendDescription(lne, true);
return Status::InProgress; return Status::InProgress;
@@ -681,8 +682,7 @@ void ProjectExplorerPlugin::testGccOutputParsers_data()
<< (Tasks() << (Tasks()
<< CompileTask(Task::Unknown, << CompileTask(Task::Unknown,
"In file included from <command-line>:0:0:", "In file included from <command-line>:0:0:",
FilePath::fromUserInput("<command-line>"), FilePath::fromUserInput("<command-line>"))
0)
<< CompileTask(Task::Warning, << CompileTask(Task::Warning,
"\"STUPID_DEFINE\" redefined", "\"STUPID_DEFINE\" redefined",
FilePath::fromUserInput("./mw.h"), FilePath::fromUserInput("./mw.h"),
@@ -1009,8 +1009,7 @@ void ProjectExplorerPlugin::testGccOutputParsers_data()
<< (Tasks() << (Tasks()
<< CompileTask(Task::Unknown, << CompileTask(Task::Unknown,
"Note: No relevant classes found. No output generated.", "Note: No relevant classes found. No output generated.",
FilePath::fromUserInput("/home/qtwebkithelpviewer.h"), FilePath::fromUserInput("/home/qtwebkithelpviewer.h")))
0))
<< QString(); << QString();
QTest::newRow("GCC 9 output") QTest::newRow("GCC 9 output")

View File

@@ -42,7 +42,7 @@ public:
static Core::Id id(); static Core::Id id();
static QList<OutputTaskParser *> gccParserSuite(); static QList<OutputLineParser *> gccParserSuite();
protected: protected:
void newTask(const Task &task); void newTask(const Task &task);
@@ -51,7 +51,7 @@ protected:
void amendDescription(const QString &desc, bool monospaced); void amendDescription(const QString &desc, bool monospaced);
private: private:
Status handleLine(const QString &line, Utils::OutputFormat type) override; Result handleLine(const QString &line, Utils::OutputFormat type) override;
QRegularExpression m_regExp; QRegularExpression m_regExp;
QRegularExpression m_regExpIncluded; QRegularExpression m_regExpIncluded;

View File

@@ -731,7 +731,7 @@ FilePath GccToolChain::makeCommand(const Environment &environment) const
return tmp.isEmpty() ? FilePath::fromString("make") : tmp; return tmp.isEmpty() ? FilePath::fromString("make") : tmp;
} }
QList<OutputTaskParser *> GccToolChain::createOutputParsers() const QList<OutputLineParser *> GccToolChain::createOutputParsers() const
{ {
return GccParser::gccParserSuite(); return GccParser::gccParserSuite();
} }
@@ -1628,7 +1628,7 @@ LanguageExtensions ClangToolChain::defaultLanguageExtensions() const
return LanguageExtension::Gnu; return LanguageExtension::Gnu;
} }
QList<OutputTaskParser *> ClangToolChain::createOutputParsers() const QList<OutputLineParser *> ClangToolChain::createOutputParsers() const
{ {
return ClangParser::clangParserSuite(); return ClangParser::clangParserSuite();
} }
@@ -1898,7 +1898,7 @@ LanguageExtensions LinuxIccToolChain::languageExtensions(const QStringList &cxxf
return extensions; return extensions;
} }
QList<OutputTaskParser *> LinuxIccToolChain::createOutputParsers() const QList<OutputLineParser *> LinuxIccToolChain::createOutputParsers() const
{ {
return LinuxIccParser::iccParserSuite(); return LinuxIccParser::iccParserSuite();
} }

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;
QList<OutputTaskParser *> createOutputParsers() const override; QList<Utils::OutputLineParser *> createOutputParsers() 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;
QList<OutputTaskParser *> createOutputParsers() const override; QList<Utils::OutputLineParser *> createOutputParsers() 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;
QList<OutputTaskParser *> createOutputParsers() const override; QList<Utils::OutputLineParser *> createOutputParsers() const override;
QStringList suggestedMkspecList() const override; QStringList suggestedMkspecList() const override;

View File

@@ -100,10 +100,10 @@ void GnuMakeParser::emitTask(const ProjectExplorer::Task &task)
{ {
if (task.type == Task::Error) // Assume that all make errors will be follow up errors. if (task.type == Task::Error) // Assume that all make errors will be follow up errors.
m_suppressIssues = true; m_suppressIssues = true;
emit addTask(task, 1, 0); scheduleTask(task, 1, 0);
} }
OutputTaskParser::Status GnuMakeParser::handleLine(const QString &line, OutputFormat type) OutputLineParser::Result GnuMakeParser::handleLine(const QString &line, OutputFormat type)
{ {
const QString lne = rightTrimmed(line); const QString lne = rightTrimmed(line);
if (type == StdOutFormat) { if (type == StdOutFormat) {
@@ -119,19 +119,21 @@ OutputTaskParser::Status GnuMakeParser::handleLine(const QString &line, OutputFo
} }
QRegularExpressionMatch match = m_errorInMakefile.match(lne); QRegularExpressionMatch match = m_errorInMakefile.match(lne);
if (match.hasMatch()) { if (match.hasMatch()) {
Result res = parseDescription(match.captured(5)); ProjectExplorer::Result res = parseDescription(match.captured(5));
if (res.isFatal) if (res.isFatal)
++m_fatalErrorCount; ++m_fatalErrorCount;
LinkSpecs linkSpecs;
if (!m_suppressIssues) { if (!m_suppressIssues) {
emitTask(BuildSystemTask(res.type, res.description, const FilePath file = absoluteFilePath(FilePath::fromUserInput(match.captured(1)));
absoluteFilePath(FilePath::fromUserInput(match.captured(1))), const int lineNo = match.captured(4).toInt();
match.captured(4).toInt() /* line */)); addLinkSpecForAbsoluteFilePath(linkSpecs, file, lineNo, match, 1);
emitTask(BuildSystemTask(res.type, res.description, file, lineNo));
} }
return Status::Done; return {Status::Done, linkSpecs};
} }
match = m_makeLine.match(lne); match = m_makeLine.match(lne);
if (match.hasMatch()) { if (match.hasMatch()) {
Result res = parseDescription(match.captured(6)); ProjectExplorer::Result res = parseDescription(match.captured(6));
if (res.isFatal) if (res.isFatal)
++m_fatalErrorCount; ++m_fatalErrorCount;
if (!m_suppressIssues) if (!m_suppressIssues)

View File

@@ -40,7 +40,7 @@ public:
explicit GnuMakeParser(); explicit GnuMakeParser();
private: private:
Status handleLine(const QString &line, Utils::OutputFormat type) override; Result handleLine(const QString &line, Utils::OutputFormat type) override;
bool hasFatalErrors() const override; bool hasFatalErrors() const override;
void emitTask(const ProjectExplorer::Task &task); void emitTask(const ProjectExplorer::Task &task);

View File

@@ -24,15 +24,9 @@
****************************************************************************/ ****************************************************************************/
#include "ioutputparser.h" #include "ioutputparser.h"
#include "task.h" #include "task.h"
#include "taskhub.h"
#include <utils/algorithm.h>
#include <utils/fileinprojectfinder.h>
#include <utils/synchronousprocess.h>
#include <QDir>
#include <QFileInfo>
#include <QPointer>
/*! /*!
@@ -76,296 +70,32 @@ namespace ProjectExplorer {
class OutputTaskParser::Private class OutputTaskParser::Private
{ {
public: public:
Utils::FilePaths searchDirs; QList<TaskInfo> scheduledTasks;
Utils::FileInProjectFinder *fileFinder = nullptr;
QPointer<const OutputTaskParser> redirectionDetector;
bool skipFileExistsCheck = false;
}; };
OutputTaskParser::OutputTaskParser() : d(new Private) OutputTaskParser::OutputTaskParser() : d(new Private) { }
{
}
OutputTaskParser::~OutputTaskParser() { delete d; } OutputTaskParser::~OutputTaskParser() { delete d; }
void OutputTaskParser::addSearchDir(const Utils::FilePath &dir) const QList<OutputTaskParser::TaskInfo> OutputTaskParser::taskInfo() const
{ {
d->searchDirs << dir; return d->scheduledTasks;
} }
void OutputTaskParser::dropSearchDir(const Utils::FilePath &dir) void OutputTaskParser::scheduleTask(const Task &task, int outputLines, int skippedLines)
{ {
const int idx = d->searchDirs.lastIndexOf(dir); TaskInfo ts(task, outputLines, skippedLines);
if (ts.task.type == Task::Error && demoteErrorsToWarnings())
// TODO: This apparently triggers. Find out why and either remove the assertion (if it's legit) ts.task.type = Task::Warning;
// or fix the culprit. d->scheduledTasks << ts;
QTC_ASSERT(idx != -1, return); QTC_CHECK(d->scheduledTasks.size() <= 2);
d->searchDirs.removeAt(idx);
} }
const Utils::FilePaths OutputTaskParser::searchDirectories() const void OutputTaskParser::runPostPrintActions()
{ {
return d->searchDirs; for (const TaskInfo &t : qAsConst(d->scheduledTasks))
} TaskHub::addTask(t.task);
d->scheduledTasks.clear();
// 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 OutputTaskParser::setRedirectionDetector(const OutputTaskParser *detector)
{
d->redirectionDetector = detector;
}
bool OutputTaskParser::needsRedirection() const
{
return d->redirectionDetector && (d->redirectionDetector->hasDetectedRedirection()
|| d->redirectionDetector->needsRedirection());
}
void OutputTaskParser::setFileFinder(Utils::FileInProjectFinder *finder)
{
d->fileFinder = finder;
}
Utils::FilePath OutputTaskParser::absoluteFilePath(const Utils::FilePath &filePath)
{
if (filePath.isEmpty() || filePath.toFileInfo().isAbsolute())
return filePath;
Utils::FilePaths candidates;
for (const Utils::FilePath &dir : searchDirectories()) {
const Utils::FilePath candidate = dir.pathAppended(filePath.toString());
if (candidate.exists() || d->skipFileExistsCheck)
candidates << candidate;
}
if (candidates.count() == 1)
return Utils::FilePath::fromString(QDir::cleanPath(candidates.first().toString()));
QString fp = filePath.toString();
while (fp.startsWith("../"))
fp.remove(0, 3);
bool found = false;
candidates = d->fileFinder->findFile(QUrl::fromLocalFile(fp), &found);
if (found && candidates.size() == 1)
return candidates.first();
return filePath;
}
QString OutputTaskParser::rightTrimmed(const QString &in)
{
int pos = in.length();
for (; pos > 0; --pos) {
if (!in.at(pos - 1).isSpace())
break;
}
return in.mid(0, pos);
}
#ifdef WITH_TESTS
void OutputTaskParser::skipFileExistsCheck()
{
d->skipFileExistsCheck = true;
}
#endif
class IOutputParser::OutputChannelState
{
public:
using LineHandler = void (IOutputParser::*)(const QString &line);
OutputChannelState(IOutputParser *parser, Utils::OutputFormat type)
: parser(parser), type(type) {}
void handleData(const QString &newData)
{
pendingData += newData;
pendingData = Utils::SynchronousProcess::normalizeNewlines(pendingData);
while (true) {
const int eolPos = pendingData.indexOf('\n');
if (eolPos == -1)
break;
const QString line = pendingData.left(eolPos + 1);
pendingData.remove(0, eolPos + 1);
parser->handleLine(line, type);
}
}
void flush()
{
if (!pendingData.isEmpty()) {
parser->handleLine(pendingData, type);
pendingData.clear();
}
}
IOutputParser * const parser;
const Utils::OutputFormat type;
QString pendingData;
};
class IOutputParser::IOutputParserPrivate
{
public:
IOutputParserPrivate(IOutputParser *parser)
: stdoutState(parser, Utils::StdOutFormat),
stderrState(parser, Utils::StdErrFormat)
{}
QList<OutputTaskParser *> lineParsers;
OutputTaskParser *nextParser = nullptr;
QList<Filter> filters;
Utils::FileInProjectFinder fileFinder;
OutputChannelState stdoutState;
OutputChannelState stderrState;
};
IOutputParser::IOutputParser() : d(new IOutputParserPrivate(this))
{
}
IOutputParser::~IOutputParser()
{
clear();
delete d;
}
void IOutputParser::handleStdout(const QString &data)
{
d->stdoutState.handleData(data);
}
void IOutputParser::handleStderr(const QString &data)
{
d->stderrState.handleData(data);
}
void IOutputParser::handleLine(const QString &line, Utils::OutputFormat type)
{
const QString cleanLine = filteredLine(line);
if (d->nextParser) {
switch (d->nextParser->handleLine(cleanLine, outputTypeForParser(d->nextParser, type))) {
case OutputTaskParser::Status::Done:
d->nextParser = nullptr;
return;
case OutputTaskParser::Status::InProgress:
return;
case OutputTaskParser::Status::NotHandled:
d->nextParser = nullptr;
break;
}
}
QTC_CHECK(!d->nextParser);
for (OutputTaskParser * const lineParser : d->lineParsers) {
switch (lineParser->handleLine(cleanLine, outputTypeForParser(lineParser, type))) {
case OutputTaskParser::Status::Done:
return;
case OutputTaskParser::Status::InProgress:
d->nextParser = lineParser;
return;
case OutputTaskParser::Status::NotHandled:
break;
}
}
}
QString IOutputParser::filteredLine(const QString &line) const
{
QString l = line;
for (const IOutputParser::Filter &f : qAsConst(d->filters))
l = f(l);
return l;
}
void IOutputParser::setupLineParser(OutputTaskParser *parser)
{
parser->setFileFinder(&d->fileFinder);
connect(parser, &OutputTaskParser::addTask, this, &IOutputParser::addTask);
connect(parser, &OutputTaskParser::newSearchDir, this, &IOutputParser::addSearchDir);
connect(parser, &OutputTaskParser::searchDirExpired, this, &IOutputParser::dropSearchDir);
}
bool IOutputParser::hasFatalErrors() const
{
return Utils::anyOf(d->lineParsers, [](const OutputTaskParser *p) {
return p->hasFatalErrors();
});
}
void IOutputParser::flush()
{
d->stdoutState.flush();
d->stderrState.flush();
for (OutputTaskParser * const p : qAsConst(d->lineParsers))
p->flush();
}
void IOutputParser::clear()
{
d->nextParser = nullptr;
d->filters.clear();
qDeleteAll(d->lineParsers);
d->lineParsers.clear();
d->stdoutState.pendingData.clear();
d->stderrState.pendingData.clear();
d->fileFinder = Utils::FileInProjectFinder();
}
void IOutputParser::addLineParser(OutputTaskParser *parser)
{
setupLineParser(parser);
d->lineParsers << parser;
}
void IOutputParser::addLineParsers(const QList<OutputTaskParser *> &parsers)
{
for (OutputTaskParser * const p : qAsConst(parsers))
addLineParser(p);
}
void IOutputParser::setLineParsers(const QList<OutputTaskParser *> &parsers)
{
qDeleteAll(d->lineParsers);
d->lineParsers.clear();
addLineParsers(parsers);
}
void IOutputParser::setFileFinder(const Utils::FileInProjectFinder &finder)
{
d->fileFinder = finder;
}
#ifdef WITH_TESTS
QList<OutputTaskParser *> IOutputParser::lineParsers() const
{
return d->lineParsers;
}
#endif // WITH_TESTS
void IOutputParser::addFilter(const Filter &filter)
{
d->filters << filter;
}
void IOutputParser::addSearchDir(const Utils::FilePath &dir)
{
for (OutputTaskParser * const p : qAsConst(d->lineParsers))
p->addSearchDir(dir);
}
void IOutputParser::dropSearchDir(const Utils::FilePath &dir)
{
for (OutputTaskParser * const p : qAsConst(d->lineParsers))
p->dropSearchDir(dir);
}
Utils::OutputFormat IOutputParser::outputTypeForParser(const OutputTaskParser *parser,
Utils::OutputFormat type) const
{
if (type == Utils::StdOutFormat && parser->needsRedirection())
return Utils::StdErrFormat;
return type;
} }
} // namespace ProjectExplorer } // namespace ProjectExplorer

View File

@@ -28,103 +28,38 @@
#include "projectexplorer_export.h" #include "projectexplorer_export.h"
#include "buildstep.h" #include "buildstep.h"
#include <utils/fileutils.h> #include <utils/outputformatter.h>
#include <utils/outputformat.h>
#include <functional> #include <functional>
namespace Utils { class FileInProjectFinder; }
namespace ProjectExplorer { namespace ProjectExplorer {
class Task; class Task;
class PROJECTEXPLORER_EXPORT OutputTaskParser : public QObject class PROJECTEXPLORER_EXPORT OutputTaskParser : public Utils::OutputLineParser
{ {
Q_OBJECT Q_OBJECT
public: public:
OutputTaskParser(); OutputTaskParser();
~OutputTaskParser() override; ~OutputTaskParser() override;
void addSearchDir(const Utils::FilePath &dir); class TaskInfo
void dropSearchDir(const Utils::FilePath &dir); {
const Utils::FilePaths searchDirectories() const; public:
TaskInfo(const Task &t, int l, int s) : task(t), linkedLines(l), skippedLines(s) {}
enum class Status { Done, InProgress, NotHandled }; Task task;
virtual Status handleLine(const QString &line, Utils::OutputFormat type) = 0; int linkedLines = 0;
int skippedLines = 0;
virtual bool hasFatalErrors() const { return false; } };
virtual void flush() {} const QList<TaskInfo> taskInfo() const;
void setRedirectionDetector(const OutputTaskParser *detector);
bool needsRedirection() const;
virtual bool hasDetectedRedirection() const { return false; }
void setFileFinder(Utils::FileInProjectFinder *finder);
#ifdef WITH_TESTS
void skipFileExistsCheck();
#endif
signals:
void newSearchDir(const Utils::FilePath &dir);
void searchDirExpired(const Utils::FilePath &dir);
void addTask(const ProjectExplorer::Task &task, int linkedOutputLines = 0, int skipLines = 0);
protected: protected:
static QString rightTrimmed(const QString &in); void scheduleTask(const Task &task, int outputLines, int skippedLines = 0);
Utils::FilePath absoluteFilePath(const Utils::FilePath &filePath);
private: private:
void runPostPrintActions() override;
class Private; class Private;
Private * const d; Private * const d;
}; };
// Documentation inside.
class PROJECTEXPLORER_EXPORT IOutputParser : public QObject
{
Q_OBJECT
public:
IOutputParser();
~IOutputParser() override;
void handleStdout(const QString &data);
void handleStderr(const QString &data);
bool hasFatalErrors() const;
using Filter = std::function<QString(const QString &)>;
void addFilter(const Filter &filter);
// Forwards to line parsers. Add those before.
void addSearchDir(const Utils::FilePath &dir);
void dropSearchDir(const Utils::FilePath &dir);
void flush();
void clear();
void addLineParser(OutputTaskParser *parser);
void addLineParsers(const QList<OutputTaskParser *> &parsers);
void setLineParsers(const QList<OutputTaskParser *> &parsers);
void setFileFinder(const Utils::FileInProjectFinder &finder);
#ifdef WITH_TESTS
QList<OutputTaskParser *> lineParsers() const;
#endif
signals:
void addTask(const ProjectExplorer::Task &task, int linkedOutputLines = 0, int skipLines = 0);
private:
void handleLine(const QString &line, Utils::OutputFormat type);
QString filteredLine(const QString &line) const;
void setupLineParser(OutputTaskParser *parser);
Utils::OutputFormat outputTypeForParser(const OutputTaskParser *parser,
Utils::OutputFormat type) const;
class OutputChannelState;
class IOutputParserPrivate;
IOutputParserPrivate * const d;
};
} // namespace ProjectExplorer } // namespace ProjectExplorer

View File

@@ -559,9 +559,9 @@ void Kit::addToEnvironment(Environment &env) const
aspect->addToEnvironment(this, env); aspect->addToEnvironment(this, env);
} }
QList<OutputTaskParser *> Kit::createOutputParsers() const QList<OutputLineParser *> Kit::createOutputParsers() const
{ {
QList<OutputTaskParser *> parsers{new OsParser}; QList<OutputLineParser *> parsers{new OsParser};
for (KitAspect *aspect : KitManager::kitAspects()) for (KitAspect *aspect : KitManager::kitAspects())
parsers << aspect->createOutputParsers(this); parsers << aspect->createOutputParsers(this);
return parsers; return parsers;

View File

@@ -38,10 +38,10 @@
namespace Utils { namespace Utils {
class Environment; class Environment;
class MacroExpander; class MacroExpander;
class OutputLineParser;
} // namespace Utils } // namespace Utils
namespace ProjectExplorer { namespace ProjectExplorer {
class OutputTaskParser;
namespace Internal { namespace Internal {
class KitManagerPrivate; class KitManagerPrivate;
@@ -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;
QList<OutputTaskParser *> createOutputParsers() const; QList<Utils::OutputLineParser *> 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,7 +557,7 @@ void ToolChainKitAspect::addToMacroExpander(Kit *kit, Utils::MacroExpander *expa
}); });
} }
QList<OutputTaskParser *> ToolChainKitAspect::createOutputParsers(const Kit *k) const QList<Utils::OutputLineParser *> ToolChainKitAspect::createOutputParsers(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))

View File

@@ -85,7 +85,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;
QList<OutputTaskParser *> createOutputParsers(const Kit *k) const override; QList<Utils::OutputLineParser *> 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,7 +677,7 @@ void KitAspect::addToEnvironment(const Kit *k, Environment &env) const
Q_UNUSED(env) Q_UNUSED(env)
} }
QList<OutputTaskParser *> KitAspect::createOutputParsers(const Kit *k) const QList<OutputLineParser *> KitAspect::createOutputParsers(const Kit *k) const
{ {
Q_UNUSED(k) Q_UNUSED(k)
return {}; return {};

View File

@@ -42,11 +42,11 @@ namespace Utils {
class Environment; class Environment;
class FilePath; class FilePath;
class MacroExpander; class MacroExpander;
class OutputLineParser;
} // namespace Utils } // namespace Utils
namespace ProjectExplorer { namespace ProjectExplorer {
class Task; class Task;
class OutputTaskParser;
class KitAspectWidget; class KitAspectWidget;
class KitManager; class KitManager;
@@ -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 QList<OutputTaskParser *> createOutputParsers(const Kit *k) const; virtual QList<Utils::OutputLineParser *> createOutputParsers(const Kit *k) const;
virtual QString displayNamePostfix(const Kit *k) const; virtual QString displayNamePostfix(const Kit *k) const;

View File

@@ -54,7 +54,7 @@ LdParser::LdParser()
QTC_CHECK(m_regExpGccNames.isValid()); QTC_CHECK(m_regExpGccNames.isValid());
} }
OutputTaskParser::Status LdParser::handleLine(const QString &line, Utils::OutputFormat type) Utils::OutputLineParser::Result LdParser::handleLine(const QString &line, Utils::OutputFormat type)
{ {
if (type != Utils::StdErrFormat) if (type != Utils::StdErrFormat)
return Status::NotHandled; return Status::NotHandled;
@@ -80,20 +80,24 @@ OutputTaskParser::Status LdParser::handleLine(const QString &line, Utils::Output
m_incompleteTask.description.append('\n').append(lne); m_incompleteTask.description.append('\n').append(lne);
static const QRegularExpression locRegExp(" (?<symbol>\\S+) in (?<file>\\S+)"); static const QRegularExpression locRegExp(" (?<symbol>\\S+) in (?<file>\\S+)");
const QRegularExpressionMatch match = locRegExp.match(lne); const QRegularExpressionMatch match = locRegExp.match(lne);
if (match.hasMatch()) LinkSpecs linkSpecs;
m_incompleteTask.setFile(Utils::FilePath::fromString(match.captured("file"))); if (match.hasMatch()) {
return Status::InProgress; m_incompleteTask.setFile(absoluteFilePath(Utils::FilePath::fromString(
match.captured("file"))));
addLinkSpecForAbsoluteFilePath(linkSpecs, m_incompleteTask.file, 0, match, "file");
}
return {Status::InProgress, linkSpecs};
} }
if (lne.startsWith("collect2:") || lne.startsWith("collect2.exe:")) { if (lne.startsWith("collect2:") || lne.startsWith("collect2.exe:")) {
emit addTask(CompileTask(Task::Error, lne /* description */), 1); scheduleTask(CompileTask(Task::Error, lne /* description */), 1);
return Status::Done; 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); scheduleTask(CompileTask(Task::Warning, description), 1);
return Status::Done; return Status::Done;
} }
@@ -107,7 +111,7 @@ OutputTaskParser::Status LdParser::handleLine(const QString &line, Utils::Output
} else if (description.startsWith(QLatin1String("fatal: "))) { } else if (description.startsWith(QLatin1String("fatal: "))) {
description = description.mid(7); description = description.mid(7);
} }
emit addTask(CompileTask(type, description), 1); scheduleTask(CompileTask(type, description), 1);
return Status::Done; return Status::Done;
} }
@@ -117,12 +121,15 @@ OutputTaskParser::Status LdParser::handleLine(const QString &line, Utils::Output
int lineno = match.captured(7).toInt(&ok); int lineno = match.captured(7).toInt(&ok);
if (!ok) if (!ok)
lineno = -1; lineno = -1;
Utils::FilePath filename = Utils::FilePath::fromUserInput(match.captured(1)); Utils::FilePath filename
= absoluteFilePath(Utils::FilePath::fromUserInput(match.captured(1)));
int capIndex = 1;
const QString sourceFileName = match.captured(4); const QString sourceFileName = match.captured(4);
if (!sourceFileName.isEmpty() if (!sourceFileName.isEmpty()
&& !sourceFileName.startsWith(QLatin1String("(.text")) && !sourceFileName.startsWith(QLatin1String("(.text"))
&& !sourceFileName.startsWith(QLatin1String("(.data"))) { && !sourceFileName.startsWith(QLatin1String("(.data"))) {
filename = Utils::FilePath::fromUserInput(sourceFileName); filename = absoluteFilePath(Utils::FilePath::fromUserInput(sourceFileName));
capIndex = 4;
} }
QString description = match.captured(8).trimmed(); QString description = match.captured(8).trimmed();
Task::TaskType type = Task::Error; Task::TaskType type = Task::Error;
@@ -137,8 +144,10 @@ OutputTaskParser::Status LdParser::handleLine(const QString &line, Utils::Output
type = Task::Warning; type = Task::Warning;
description = description.mid(9); description = description.mid(9);
} }
emit addTask(CompileTask(type, description, absoluteFilePath(filename), lineno), 1); LinkSpecs linkSpecs;
return Status::Done; addLinkSpecForAbsoluteFilePath(linkSpecs, filename, lineno, match, capIndex);
scheduleTask(CompileTask(type, description, filename, lineno), 1);
return {Status::Done, linkSpecs};
} }
return Status::NotHandled; return Status::NotHandled;
@@ -150,5 +159,5 @@ void LdParser::flush()
return; return;
const Task t = m_incompleteTask; const Task t = m_incompleteTask;
m_incompleteTask.clear(); m_incompleteTask.clear();
emit addTask(t); scheduleTask(t, 1);
} }

View File

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

View File

@@ -65,7 +65,7 @@ LinuxIccParser::LinuxIccParser() :
QTC_CHECK(m_pchInfoLine.isValid()); QTC_CHECK(m_pchInfoLine.isValid());
} }
OutputTaskParser::Status LinuxIccParser::handleLine(const QString &line, OutputFormat type) OutputLineParser::Result LinuxIccParser::handleLine(const QString &line, OutputFormat type)
{ {
if (type != Utils::StdErrFormat) if (type != Utils::StdErrFormat)
return Status::NotHandled; return Status::NotHandled;
@@ -81,10 +81,11 @@ OutputTaskParser::Status LinuxIccParser::handleLine(const QString &line, OutputF
type = Task::Error; type = Task::Error;
else if (category == QLatin1String("warning")) else if (category == QLatin1String("warning"))
type = Task::Warning; type = Task::Warning;
m_temporary = CompileTask(type, const FilePath filePath = absoluteFilePath(FilePath::fromUserInput(m_firstLine.cap(1)));
m_firstLine.cap(6).trimmed(), const int lineNo = m_firstLine.cap(2).toInt();
absoluteFilePath(Utils::FilePath::fromUserInput(m_firstLine.cap(1))), LinkSpecs linkSpecs;
m_firstLine.cap(2).toInt()); addLinkSpecForAbsoluteFilePath(linkSpecs, filePath, lineNo, m_firstLine, 1);
m_temporary = CompileTask(type, m_firstLine.cap(6).trimmed(), filePath, lineNo);
m_lines = 1; m_lines = 1;
m_expectFirstLine = false; m_expectFirstLine = false;
@@ -107,7 +108,7 @@ OutputTaskParser::Status LinuxIccParser::handleLine(const QString &line, OutputF
} }
if (!m_expectFirstLine && line.trimmed().isEmpty()) { // last Line if (!m_expectFirstLine && line.trimmed().isEmpty()) { // last Line
m_expectFirstLine = true; m_expectFirstLine = true;
emit addTask(m_temporary, m_lines); scheduleTask(m_temporary, m_lines);
m_temporary = Task(); m_temporary = Task();
return Status::Done; return Status::Done;
} }
@@ -129,7 +130,7 @@ Core::Id LinuxIccParser::id()
return Core::Id("ProjectExplorer.OutputParser.Icc"); return Core::Id("ProjectExplorer.OutputParser.Icc");
} }
QList<OutputTaskParser *> LinuxIccParser::iccParserSuite() QList<OutputLineParser *> LinuxIccParser::iccParserSuite()
{ {
return {new LinuxIccParser, new Internal::LldParser, new LdParser}; return {new LinuxIccParser, new Internal::LldParser, new LdParser};
} }
@@ -140,7 +141,7 @@ void LinuxIccParser::flush()
return; return;
Task t = m_temporary; Task t = m_temporary;
m_temporary.clear(); m_temporary.clear();
emit addTask(t, m_lines, 1); scheduleTask(t, m_lines, 1);
} }
#ifdef WITH_TESTS #ifdef WITH_TESTS
@@ -237,7 +238,7 @@ void ProjectExplorerPlugin::testLinuxIccOutputParsers_data()
<< (Tasks() << (Tasks()
<< CompileTask(Task::Unknown, << CompileTask(Task::Unknown,
"Note: No relevant classes found. No output generated.", "Note: No relevant classes found. No output generated.",
FilePath::fromUserInput("/home/qtwebkithelpviewer.h"), 0)) FilePath::fromUserInput("/home/qtwebkithelpviewer.h"), -1))
<< QString(); << QString();
} }

View File

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

View File

@@ -35,14 +35,14 @@
namespace ProjectExplorer { namespace ProjectExplorer {
namespace Internal { namespace Internal {
OutputTaskParser::Status LldParser::handleLine(const QString &line, Utils::OutputFormat type) Utils::OutputLineParser::Result LldParser::handleLine(const QString &line, Utils::OutputFormat type)
{ {
if (type != Utils::StdErrFormat) if (type != Utils::StdErrFormat)
return Status::NotHandled; return Status::NotHandled;
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)); scheduleTask(CompileTask(Task::Error, trimmedLine), 1);
return Status::Done; return Status::Done;
} }
static const QStringList prefixes{">>> referenced by ", ">>> defined at ", ">>> "}; static const QStringList prefixes{">>> referenced by ", ">>> defined at ", ">>> "};
@@ -65,11 +65,13 @@ OutputTaskParser::Status LldParser::handleLine(const QString &line, Utils::Outpu
else else
filePathOffset = prefix.length(); filePathOffset = prefix.length();
const int filePathLen = locOffset == -1 ? -1 : locOffset - filePathOffset; const int filePathLen = locOffset == -1 ? -1 : locOffset - filePathOffset;
const auto file = Utils::FilePath::fromUserInput( const auto file = absoluteFilePath(Utils::FilePath::fromUserInput(
trimmedLine.mid(filePathOffset, filePathLen).trimmed()); trimmedLine.mid(filePathOffset, filePathLen).trimmed()));
emit addTask(CompileTask(Task::Unknown, trimmedLine.mid(4).trimmed(), LinkSpecs linkSpecs;
absoluteFilePath(file), lineNo)); addLinkSpecForAbsoluteFilePath(linkSpecs, file, lineNo, filePathOffset, filePathLen);
return Status::Done; scheduleTask(CompileTask(Task::Unknown, trimmedLine.mid(4).trimmed(),
file, lineNo), 1);
return {Status::Done, linkSpecs};
} }
return Status::NotHandled; return Status::NotHandled;
} }

View File

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

View File

@@ -102,13 +102,17 @@ bool MakeStep::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(isClean()); setIgnoreReturnValue(isClean());
setOutputParser(new GnuMakeParser());
appendOutputParsers(target()->kit()->createOutputParsers());
outputParser()->addSearchDir(pp->effectiveWorkingDirectory());
return AbstractProcessStep::init(); return AbstractProcessStep::init();
} }
void MakeStep::setupOutputFormatter(OutputFormatter *formatter)
{
formatter->addLineParser(new GnuMakeParser());
formatter->addLineParsers(target()->kit()->createOutputParsers());
formatter->addSearchDir(processParameters()->effectiveWorkingDirectory());
AbstractProcessStep::setupOutputFormatter(formatter);
}
void MakeStep::setClean(bool clean) void MakeStep::setClean(bool clean)
{ {
m_clean = clean; m_clean = clean;

View File

@@ -54,6 +54,7 @@ public:
void setAvailableBuildTargets(const QStringList &buildTargets); void setAvailableBuildTargets(const QStringList &buildTargets);
bool init() override; bool init() override;
void setupOutputFormatter(Utils::OutputFormatter *formatter) override;
ProjectExplorer::BuildStepConfigWidget *createConfigWidget() override; ProjectExplorer::BuildStepConfigWidget *createConfigWidget() override;
bool buildsTarget(const QString &target) const; bool buildsTarget(const QString &target) const;
void setBuildTarget(const QString &target, bool on); void setBuildTarget(const QString &target, bool on);

View File

@@ -105,7 +105,7 @@ Core::Id MsvcParser::id()
return Core::Id("ProjectExplorer.OutputParser.Msvc"); return Core::Id("ProjectExplorer.OutputParser.Msvc");
} }
OutputTaskParser::Status MsvcParser::handleLine(const QString &line, OutputFormat type) OutputLineParser::Result MsvcParser::handleLine(const QString &line, OutputFormat type)
{ {
if (type == OutputFormat::StdOutFormat) { if (type == OutputFormat::StdOutFormat) {
QRegularExpressionMatch match = m_additionalInfoRegExp.match(line); QRegularExpressionMatch match = m_additionalInfoRegExp.match(line);
@@ -137,8 +137,9 @@ OutputTaskParser::Status MsvcParser::handleLine(const QString &line, OutputForma
return Status::InProgress; return Status::InProgress;
} }
if (processCompileLine(line)) const Result res = processCompileLine(line);
return Status::InProgress; if (res.status != Status::NotHandled)
return res;
if (handleNmakeJomMessage(line, &m_lastTask)) { if (handleNmakeJomMessage(line, &m_lastTask)) {
m_lines = 1; m_lines = 1;
return Status::InProgress; return Status::InProgress;
@@ -148,17 +149,20 @@ OutputTaskParser::Status MsvcParser::handleLine(const QString &line, OutputForma
+ match.captured(4).trimmed(); + match.captured(4).trimmed();
if (!match.captured(1).isEmpty()) if (!match.captured(1).isEmpty())
description.chop(1); // Remove trailing quote description.chop(1); // Remove trailing quote
m_lastTask = CompileTask(Task::Unknown, description, const FilePath filePath = absoluteFilePath(FilePath::fromUserInput(match.captured(2)));
absoluteFilePath(FilePath::fromUserInput(match.captured(2))), const int lineNo = match.captured(3).toInt();
match.captured(3).toInt() /* linenumber */); LinkSpecs linkSpecs;
addLinkSpecForAbsoluteFilePath(linkSpecs, filePath, lineNo, match, 2);
m_lastTask = CompileTask(Task::Unknown, description, filePath, lineNo);
m_lines = 1; m_lines = 1;
return Status::InProgress; return {Status::InProgress, linkSpecs};
} }
return Status::NotHandled; return Status::NotHandled;
} }
if (processCompileLine(line)) const Result res = processCompileLine(line);
return Status::InProgress; if (res.status != Status::NotHandled)
return res;
// 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;
@@ -167,20 +171,23 @@ OutputTaskParser::Status MsvcParser::handleLine(const QString &line, OutputForma
return Status::NotHandled; return Status::NotHandled;
} }
bool MsvcParser::processCompileLine(const QString &line) MsvcParser::Result MsvcParser::processCompileLine(const QString &line)
{ {
flush(); flush();
QRegularExpressionMatch match = m_compileRegExp.match(line); QRegularExpressionMatch match = m_compileRegExp.match(line);
if (match.hasMatch()) { if (match.hasMatch()) {
QPair<FilePath, int> position = parseFileName(match.captured(1)); QPair<FilePath, int> position = parseFileName(match.captured(1));
const FilePath filePath = absoluteFilePath(position.first);
m_lastTask = CompileTask(taskType(match.captured(2)), m_lastTask = CompileTask(taskType(match.captured(2)),
match.captured(3) + match.captured(4).trimmed(), // description match.captured(3) + match.captured(4).trimmed(), // description
absoluteFilePath(position.first), position.second); filePath, position.second);
m_lines = 1; m_lines = 1;
return true; LinkSpecs linkSpecs;
addLinkSpecForAbsoluteFilePath(linkSpecs, m_lastTask.file, m_lastTask.line, match, 1);
return {Status::InProgress, linkSpecs};
} }
return false; return Status::NotHandled;
} }
void MsvcParser::flush() void MsvcParser::flush()
@@ -190,7 +197,7 @@ void MsvcParser::flush()
Task t = m_lastTask; Task t = m_lastTask;
m_lastTask.clear(); m_lastTask.clear();
emit addTask(t, m_lines, 1); scheduleTask(t, m_lines, 1);
} }
// -------------------------------------------------------------------------- // --------------------------------------------------------------------------
@@ -220,7 +227,7 @@ static inline bool isClangCodeMarker(const QString &trimmedLine)
[] (QChar c) { return c != ' ' && c != '^' && c != '~'; }); [] (QChar c) { return c != ' ' && c != '^' && c != '~'; });
} }
OutputTaskParser::Status ClangClParser::handleLine(const QString &line, OutputFormat type) OutputLineParser::Result ClangClParser::handleLine(const QString &line, OutputFormat type)
{ {
if (type == StdOutFormat) { if (type == StdOutFormat) {
if (handleNmakeJomMessage(line, &m_lastTask)) { if (handleNmakeJomMessage(line, &m_lastTask)) {
@@ -257,7 +264,9 @@ OutputTaskParser::Status ClangClParser::handleLine(const QString &line, OutputFo
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 Status::InProgress; LinkSpecs linkSpecs;
addLinkSpecForAbsoluteFilePath(linkSpecs, m_lastTask.file, m_lastTask.line, match, 1);
return {Status::InProgress, linkSpecs};
} }
if (!m_lastTask.isNull()) { if (!m_lastTask.isNull()) {
@@ -278,7 +287,7 @@ OutputTaskParser::Status ClangClParser::handleLine(const QString &line, OutputFo
void ClangClParser::flush() void ClangClParser::flush()
{ {
if (!m_lastTask.isNull()) { if (!m_lastTask.isNull()) {
emit addTask(m_lastTask, m_linkedLines, 1); scheduleTask(m_lastTask, m_linkedLines, 1);
m_lastTask.clear(); m_lastTask.clear();
} }
} }

View File

@@ -43,10 +43,10 @@ public:
static Core::Id id(); static Core::Id id();
private: private:
Status handleLine(const QString &line, Utils::OutputFormat type) override; Result handleLine(const QString &line, Utils::OutputFormat type) override;
void flush() override; void flush() override;
bool processCompileLine(const QString &line); Result processCompileLine(const QString &line);
QRegularExpression m_compileRegExp; QRegularExpression m_compileRegExp;
QRegularExpression m_additionalInfoRegExp; QRegularExpression m_additionalInfoRegExp;
@@ -63,7 +63,7 @@ public:
ClangClParser(); ClangClParser();
private: private:
Status handleLine(const QString &line, Utils::OutputFormat type) override; Result handleLine(const QString &line, Utils::OutputFormat type) override;
void flush() override; void flush() override;
const QRegularExpression m_compileRegExp; const QRegularExpression m_compileRegExp;

View File

@@ -1169,7 +1169,7 @@ void MsvcToolChain::rescanForCompiler()
}); });
} }
QList<OutputTaskParser *> MsvcToolChain::createOutputParsers() const QList<OutputLineParser *> MsvcToolChain::createOutputParsers() const
{ {
return {new MsvcParser}; return {new MsvcParser};
} }
@@ -1656,7 +1656,7 @@ QStringList ClangClToolChain::suggestedMkspecList() const
return {mkspec, "win32-clang-msvc"}; return {mkspec, "win32-clang-msvc"};
} }
QList<OutputTaskParser *> ClangClToolChain::createOutputParsers() const QList<OutputLineParser *> ClangClToolChain::createOutputParsers() const
{ {
return {new ClangClParser}; return {new ClangClParser};
} }

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;
QList<OutputTaskParser *> createOutputParsers() const override; QList<Utils::OutputLineParser *> createOutputParsers() 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;
QList<OutputTaskParser *> createOutputParsers() const override; QList<Utils::OutputLineParser *> createOutputParsers() 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,18 +36,18 @@ OsParser::OsParser()
setObjectName(QLatin1String("OsParser")); setObjectName(QLatin1String("OsParser"));
} }
OutputTaskParser::Status OsParser::handleLine(const QString &line, Utils::OutputFormat type) Utils::OutputLineParser::Result OsParser::handleLine(const QString &line, Utils::OutputFormat type)
{ {
if (type == Utils::StdOutFormat) { 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 " if (trimmed == QLatin1String("The process cannot access the file because it is "
"being used by another process.")) { "being used by another process.")) {
emit addTask(CompileTask(Task::Error, tr( scheduleTask(CompileTask(Task::Error, tr(
"The process cannot access the file because it is being used " "The process cannot access the file because it is being used "
"by another process.\n" "by another process.\n"
"Please close all running instances of your application before " "Please close all running instances of your application before "
"starting a build."))); "starting a build.")), 1);
m_hasFatalError = true; m_hasFatalError = true;
return Status::Done; return Status::Done;
} }
@@ -57,7 +57,7 @@ OutputTaskParser::Status OsParser::handleLine(const QString &line, Utils::Output
if (Utils::HostOsInfo::isLinuxHost()) { if (Utils::HostOsInfo::isLinuxHost()) {
const QString trimmed = line.trimmed(); const QString trimmed = line.trimmed();
if (trimmed.contains(QLatin1String(": error while loading shared libraries:"))) { if (trimmed.contains(QLatin1String(": error while loading shared libraries:"))) {
emit addTask(CompileTask(Task::Error, trimmed)); scheduleTask(CompileTask(Task::Error, trimmed), 1);
return Status::Done; return Status::Done;
} }
} }

View File

@@ -41,7 +41,7 @@ public:
OsParser(); OsParser();
private: private:
Status handleLine(const QString &line, Utils::OutputFormat type) override; Result handleLine(const QString &line, Utils::OutputFormat type) override;
bool hasFatalErrors() const override { return m_hasFatalError; } bool hasFatalErrors() const override { return m_hasFatalError; }
bool m_hasFatalError = false; bool m_hasFatalError = false;

View File

@@ -24,7 +24,9 @@
****************************************************************************/ ****************************************************************************/
#include "outputparser_test.h" #include "outputparser_test.h"
#include "projectexplorer.h"
#include "task.h" #include "task.h"
#include "taskhub.h"
#if defined(WITH_TESTS) #if defined(WITH_TESTS)
@@ -41,11 +43,16 @@ static inline QByteArray msgFileComparisonFail(const Utils::FilePath &f1, const
// test functions: // test functions:
OutputParserTester::OutputParserTester() OutputParserTester::OutputParserTester()
{ {
connect(this, &IOutputParser::addTask, this, [this](const Task &t) { connect(TaskHub::instance(), &TaskHub::taskAdded, this, [this](const Task &t) {
m_receivedTasks.append(t); m_receivedTasks.append(t);
}); });
} }
OutputParserTester::~OutputParserTester()
{
TaskHub::instance()->disconnect(this);
}
void OutputParserTester::testParsing(const QString &lines, void OutputParserTester::testParsing(const QString &lines,
Channel inputChannel, Channel inputChannel,
Tasks tasks, Tasks tasks,
@@ -60,9 +67,9 @@ void OutputParserTester::testParsing(const QString &lines,
reset(); reset();
if (inputChannel == STDOUT) if (inputChannel == STDOUT)
handleStdout(lines + '\n'); appendMessage(lines + '\n', Utils::StdOutFormat);
else else
handleStderr(lines + '\n'); appendMessage(lines + '\n', Utils::StdErrFormat);
flush(); flush();
// delete the parser(s) to test // delete the parser(s) to test
@@ -102,7 +109,7 @@ TestTerminator::TestTerminator(OutputParserTester *t) :
m_tester(t) m_tester(t)
{ } { }
OutputTaskParser::Status TestTerminator::handleLine(const QString &line, Utils::OutputFormat type) Utils::OutputLineParser::Result TestTerminator::handleLine(const QString &line, Utils::OutputFormat type)
{ {
QTC_CHECK(line.endsWith('\n')); QTC_CHECK(line.endsWith('\n'));
if (type == Utils::StdOutFormat) if (type == Utils::StdOutFormat)
@@ -112,6 +119,64 @@ OutputTaskParser::Status TestTerminator::handleLine(const QString &line, Utils::
return Status::Done; return Status::Done;
} }
void ProjectExplorerPlugin::testAnsiFilterOutputParser_data()
{
QTest::addColumn<QString>("input");
QTest::addColumn<OutputParserTester::Channel>("inputChannel");
QTest::addColumn<QString>("childStdOutLines");
QTest::addColumn<QString>("childStdErrLines");
QTest::addColumn<QString>("outputLines");
QTest::newRow("pass-through stdout")
<< QString::fromLatin1("Sometext") << OutputParserTester::STDOUT
<< QString::fromLatin1("Sometext\n") << QString();
QTest::newRow("pass-through stderr")
<< QString::fromLatin1("Sometext") << OutputParserTester::STDERR
<< QString() << QString::fromLatin1("Sometext\n");
QString input = QString::fromLatin1("te") + QChar(27) + QString::fromLatin1("Nst");
QTest::newRow("ANSI: ESC-N")
<< input << OutputParserTester::STDOUT
<< QString::fromLatin1("test\n") << QString();
input = QString::fromLatin1("te") + QChar(27) + QLatin1String("^ignored") + QChar(27) + QLatin1String("\\st");
QTest::newRow("ANSI: ESC-^ignoredESC-\\")
<< input << OutputParserTester::STDOUT
<< QString::fromLatin1("test\n") << QString();
input = QString::fromLatin1("te") + QChar(27) + QLatin1String("]0;ignored") + QChar(7) + QLatin1String("st");
QTest::newRow("ANSI: window title change")
<< input << OutputParserTester::STDOUT
<< QString::fromLatin1("test\n") << QString();
input = QString::fromLatin1("te") + QChar(27) + QLatin1String("[Ast");
QTest::newRow("ANSI: cursor up")
<< input << OutputParserTester::STDOUT
<< QString::fromLatin1("test\n") << QString();
input = QString::fromLatin1("te") + QChar(27) + QLatin1String("[2Ast");
QTest::newRow("ANSI: cursor up (with int parameter)")
<< input << OutputParserTester::STDOUT
<< QString::fromLatin1("test\n") << QString();
input = QString::fromLatin1("te") + QChar(27) + QLatin1String("[2;3Hst");
QTest::newRow("ANSI: position cursor")
<< input << OutputParserTester::STDOUT
<< QString::fromLatin1("test\n") << QString();
input = QString::fromLatin1("te") + QChar(27) + QLatin1String("[31;1mst");
QTest::newRow("ANSI: bold red")
<< input << OutputParserTester::STDOUT
<< QString::fromLatin1("test\n") << QString();
}
void ProjectExplorerPlugin::testAnsiFilterOutputParser()
{
OutputParserTester testbench;
QFETCH(QString, input);
QFETCH(OutputParserTester::Channel, inputChannel);
QFETCH(QString, childStdOutLines);
QFETCH(QString, childStdErrLines);
testbench.testParsing(input, inputChannel,
Tasks(), childStdOutLines, childStdErrLines,
QString());
}
} // namespace ProjectExplorer } // namespace ProjectExplorer
#endif #endif

View File

@@ -36,7 +36,7 @@ namespace ProjectExplorer {
class TestTerminator; class TestTerminator;
class PROJECTEXPLORER_EXPORT OutputParserTester : public IOutputParser class PROJECTEXPLORER_EXPORT OutputParserTester : public Utils::OutputFormatter
{ {
Q_OBJECT Q_OBJECT
@@ -47,6 +47,7 @@ public:
}; };
OutputParserTester(); OutputParserTester();
~OutputParserTester();
// test functions: // test functions:
void testParsing(const QString &lines, Channel inputChannel, void testParsing(const QString &lines, Channel inputChannel,
@@ -81,7 +82,7 @@ public:
TestTerminator(OutputParserTester *t); TestTerminator(OutputParserTester *t);
private: private:
Status handleLine(const QString &line, Utils::OutputFormat type) override; Result handleLine(const QString &line, Utils::OutputFormat type) override;
OutputParserTester *m_tester = nullptr; OutputParserTester *m_tester = nullptr;
}; };

View File

@@ -136,13 +136,13 @@ ParseIssuesDialog::~ParseIssuesDialog()
} }
static void parse(QFutureInterface<void> &future, const QString &output, static void parse(QFutureInterface<void> &future, const QString &output,
const std::unique_ptr<IOutputParser> &parser, bool isStderr) const std::unique_ptr<Utils::OutputFormatter> &parser, bool isStderr)
{ {
const QStringList lines = output.split('\n'); const QStringList lines = output.split('\n');
future.setProgressRange(0, lines.count()); future.setProgressRange(0, lines.count());
const auto parserFunc = isStderr ? &IOutputParser::handleStderr : &IOutputParser::handleStdout; const Utils::OutputFormat format = isStderr ? Utils::StdErrFormat : Utils::StdOutFormat;
for (const QString &line : lines) { for (const QString &line : lines) {
(parser.get()->*parserFunc)(line + '\n'); parser->appendMessage(line + '\n', format);
future.setProgressValue(future.progressValue() + 1); future.setProgressValue(future.progressValue() + 1);
if (future.isCanceled()) if (future.isCanceled())
return; return;
@@ -151,17 +151,17 @@ static void parse(QFutureInterface<void> &future, const QString &output,
void ParseIssuesDialog::accept() void ParseIssuesDialog::accept()
{ {
const QList<OutputTaskParser *> lineParsers = d->kitChooser.currentKit()->createOutputParsers(); const QList<Utils::OutputLineParser *> lineParsers =
d->kitChooser.currentKit()->createOutputParsers();
if (lineParsers.isEmpty()) { 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); std::unique_ptr<Utils::OutputFormatter> parser(new Utils::OutputFormatter);
parser->setLineParsers(lineParsers); 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); });
const QFuture<void> f = Utils::runAsync(&parse, d->compileOutputEdit.toPlainText(), const QFuture<void> f = Utils::runAsync(&parse, d->compileOutputEdit.toPlainText(),
std::move(parser), d->stderrCheckBox.isChecked()); std::move(parser), d->stderrCheckBox.isChecked());
Core::ProgressManager::addTask(f, tr("Parsing build output"), Core::ProgressManager::addTask(f, tr("Parsing build output"),

View File

@@ -35,6 +35,7 @@
#include "target.h" #include "target.h"
#include <utils/fileutils.h> #include <utils/fileutils.h>
#include <utils/outputformatter.h>
using namespace Utils; using namespace Utils;
@@ -53,6 +54,7 @@ public:
ProcessStep(BuildStepList *bsl, Core::Id id); ProcessStep(BuildStepList *bsl, Core::Id id);
bool init() final; bool init() final;
void setupOutputFormatter(Utils::OutputFormatter *formatter);
void setupProcessParameters(ProcessParameters *pp); void setupProcessParameters(ProcessParameters *pp);
BaseStringAspect *m_command; BaseStringAspect *m_command;
@@ -100,10 +102,15 @@ ProcessStep::ProcessStep(BuildStepList *bsl, Core::Id id)
bool ProcessStep::init() bool ProcessStep::init()
{ {
setupProcessParameters(processParameters()); setupProcessParameters(processParameters());
appendOutputParsers(target()->kit()->createOutputParsers());
return AbstractProcessStep::init(); return AbstractProcessStep::init();
} }
void ProcessStep::setupOutputFormatter(OutputFormatter *formatter)
{
formatter->addLineParsers(target()->kit()->createOutputParsers());
AbstractProcessStep::setupOutputFormatter(formatter);
}
void ProcessStep::setupProcessParameters(ProcessParameters *pp) void ProcessStep::setupProcessParameters(ProcessParameters *pp)
{ {
QString workingDirectory = m_workingDirectory->value(); QString workingDirectory = m_workingDirectory->value();

View File

@@ -11,7 +11,6 @@ HEADERS += projectexplorer.h \
abi.h \ abi.h \
abiwidget.h \ abiwidget.h \
addrunconfigdialog.h \ addrunconfigdialog.h \
ansifilterparser.h \
buildaspects.h \ buildaspects.h \
buildinfo.h \ buildinfo.h \
buildpropertiessettings.h \ buildpropertiessettings.h \
@@ -172,7 +171,6 @@ SOURCES += projectexplorer.cpp \
abi.cpp \ abi.cpp \
abiwidget.cpp \ abiwidget.cpp \
addrunconfigdialog.cpp \ addrunconfigdialog.cpp \
ansifilterparser.cpp \
buildaspects.cpp \ buildaspects.cpp \
buildinfo.cpp \ buildinfo.cpp \
buildpropertiessettingspage.cpp \ buildpropertiessettingspage.cpp \

View File

@@ -27,7 +27,6 @@ Project {
"addrunconfigdialog.cpp", "addrunconfigdialog.h", "addrunconfigdialog.cpp", "addrunconfigdialog.h",
"allprojectsfilter.cpp", "allprojectsfilter.h", "allprojectsfilter.cpp", "allprojectsfilter.h",
"allprojectsfind.cpp", "allprojectsfind.h", "allprojectsfind.cpp", "allprojectsfind.h",
"ansifilterparser.cpp", "ansifilterparser.h",
"applicationlauncher.cpp", "applicationlauncher.h", "applicationlauncher.cpp", "applicationlauncher.h",
"appoutputpane.cpp", "appoutputpane.h", "appoutputpane.cpp", "appoutputpane.h",
"baseprojectwizarddialog.cpp", "baseprojectwizarddialog.h", "baseprojectwizarddialog.cpp", "baseprojectwizarddialog.h",

View File

@@ -47,6 +47,8 @@
#include <functional> #include <functional>
#include <memory> #include <memory>
namespace Utils { class OutputLineParser; }
namespace ProjectExplorer { namespace ProjectExplorer {
namespace Internal { class ToolChainPrivate; } namespace Internal { class ToolChainPrivate; }
@@ -64,7 +66,6 @@ QString languageId(Language l);
} // namespace Deprecated } // namespace Deprecated
class Abi; class Abi;
class OutputTaskParser;
class ToolChainConfigWidget; class ToolChainConfigWidget;
class ToolChainFactory; class ToolChainFactory;
class Kit; class Kit;
@@ -150,7 +151,7 @@ public:
Core::Id language() const; Core::Id language() const;
virtual Utils::FilePath compilerCommand() const = 0; virtual Utils::FilePath compilerCommand() const = 0;
virtual QList<OutputTaskParser *> createOutputParsers() const = 0; virtual QList<Utils::OutputLineParser *> createOutputParsers() 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"); }
QList<OutputTaskParser *> createOutputParsers() const override { return {}; } QList<OutputLineParser *> createOutputParsers() 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,7 +52,7 @@ XcodebuildParser::XcodebuildParser()
QTC_CHECK(m_buildRe.isValid()); QTC_CHECK(m_buildRe.isValid());
} }
OutputTaskParser::Status XcodebuildParser::handleLine(const QString &line, OutputFormat type) OutputLineParser::Result XcodebuildParser::handleLine(const QString &line, OutputFormat type)
{ {
const QString lne = rightTrimmed(line); const QString lne = rightTrimmed(line);
if (type == StdOutFormat) { if (type == StdOutFormat) {
@@ -69,12 +69,16 @@ OutputTaskParser::Status XcodebuildParser::handleLine(const QString &line, Outpu
return Status::Done; return Status::Done;
} }
if (lne.endsWith(QLatin1String(signatureChangeEndsWithPattern))) { if (lne.endsWith(QLatin1String(signatureChangeEndsWithPattern))) {
const int filePathEndPos = lne.size()
- QLatin1String(signatureChangeEndsWithPattern).size();
CompileTask task(Task::Warning, CompileTask task(Task::Warning,
tr("Replacing signature"), tr("Replacing signature"),
absoluteFilePath(FilePath::fromString( absoluteFilePath(FilePath::fromString(
lne.left(lne.size() - QLatin1String(signatureChangeEndsWithPattern).size())))); lne.left(filePathEndPos))));
emit addTask(task, 1); LinkSpecs linkSpecs;
return Status::Done; addLinkSpecForAbsoluteFilePath(linkSpecs, task.file, task.line, 0, filePathEndPos);
scheduleTask(task, 1);
return {Status::Done, linkSpecs};
} }
} }
return Status::NotHandled; return Status::NotHandled;
@@ -83,7 +87,7 @@ OutputTaskParser::Status XcodebuildParser::handleLine(const QString &line, Outpu
++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."))); scheduleTask(CompileTask(Task::Error, tr("Xcodebuild failed.")), 1);
} }
if (m_xcodeBuildParserState == OutsideXcodebuild) if (m_xcodeBuildParserState == OutsideXcodebuild)
return Status::NotHandled; return Status::NotHandled;

View File

@@ -47,7 +47,7 @@ public:
XcodebuildParser(); XcodebuildParser();
private: private:
Status handleLine(const QString &line, Utils::OutputFormat type) override; Result handleLine(const QString &line, Utils::OutputFormat type) override;
bool hasDetectedRedirection() const override; bool hasDetectedRedirection() const override;
bool hasFatalErrors() const override { return m_fatalErrorCount > 0; } bool hasFatalErrors() const override { return m_fatalErrorCount > 0; }

View File

@@ -126,12 +126,6 @@ private:
return true; return true;
} }
void reset() override
{
m_inTraceBack = false;
m_tasks.clear();
}
const QRegularExpression filePattern; const QRegularExpression filePattern;
QList<Task> m_tasks; QList<Task> m_tasks;
bool m_inTraceBack; bool m_inTraceBack;

View File

@@ -34,12 +34,12 @@
#include <coreplugin/icore.h> #include <coreplugin/icore.h>
#include <coreplugin/variablechooser.h> #include <coreplugin/variablechooser.h>
#include <projectexplorer/buildsteplist.h> #include <projectexplorer/buildsteplist.h>
#include <projectexplorer/ioutputparser.h>
#include <projectexplorer/kit.h> #include <projectexplorer/kit.h>
#include <projectexplorer/projectexplorerconstants.h> #include <projectexplorer/projectexplorerconstants.h>
#include <projectexplorer/target.h> #include <projectexplorer/target.h>
#include <qtsupport/qtversionmanager.h> #include <qtsupport/qtversionmanager.h>
#include <utils/macroexpander.h> #include <utils/macroexpander.h>
#include <utils/outputformatter.h>
#include <utils/pathchooser.h> #include <utils/pathchooser.h>
#include <utils/qtcassert.h> #include <utils/qtcassert.h>
#include <utils/qtcprocess.h> #include <utils/qtcprocess.h>
@@ -155,7 +155,6 @@ QbsBuildStep::~QbsBuildStep()
doCancel(); doCancel();
if (m_session) if (m_session)
m_session->disconnect(this); m_session->disconnect(this);
delete m_parser;
} }
bool QbsBuildStep::init() bool QbsBuildStep::init()
@@ -168,11 +167,6 @@ bool QbsBuildStep::init()
if (!bc) if (!bc)
return false; return false;
delete m_parser;
m_parser = new IOutputParser;
m_parser->setLineParsers(target()->kit()->createOutputParsers());
connect(m_parser, &ProjectExplorer::IOutputParser::addTask, this, &QbsBuildStep::addTask);
m_changedFiles = bc->changedFiles(); m_changedFiles = bc->changedFiles();
m_activeFileTags = bc->activeFileTags(); m_activeFileTags = bc->activeFileTags();
m_products = bc->products(); m_products = bc->products();
@@ -180,6 +174,12 @@ bool QbsBuildStep::init()
return true; return true;
} }
void QbsBuildStep::setupOutputFormatter(OutputFormatter *formatter)
{
formatter->addLineParsers(target()->kit()->createOutputParsers());
BuildStep::setupOutputFormatter(formatter);
}
void QbsBuildStep::doRun() void QbsBuildStep::doRun()
{ {
// We need a pre-build parsing step in order not to lose project file changes done // We need a pre-build parsing step in order not to lose project file changes done
@@ -371,35 +371,24 @@ void QbsBuildStep::handleProcessResult(
const QStringList &stdErr, const QStringList &stdErr,
bool success) bool success)
{ {
Q_UNUSED(workingDir);
const bool hasOutput = !stdOut.isEmpty() || !stdErr.isEmpty(); const bool hasOutput = !stdOut.isEmpty() || !stdErr.isEmpty();
if (success && !hasOutput) if (success && !hasOutput)
return; return;
if (m_parser)
m_parser->addSearchDir(workingDir);
emit addOutput(executable.toUserOutput() + ' ' + QtcProcess::joinArgs(arguments), emit addOutput(executable.toUserOutput() + ' ' + QtcProcess::joinArgs(arguments),
OutputFormat::Stdout); OutputFormat::Stdout);
for (const QString &line : stdErr) { for (const QString &line : stdErr)
if (m_parser)
m_parser->handleStderr(line + '\n');
emit addOutput(line, OutputFormat::Stderr); emit addOutput(line, OutputFormat::Stderr);
} for (const QString &line : stdOut)
for (const QString &line : stdOut) {
if (m_parser)
m_parser->handleStdout(line + '\n');
emit addOutput(line, OutputFormat::Stdout); emit addOutput(line, OutputFormat::Stdout);
}
if (m_parser) {
m_parser->flush();
m_parser->dropSearchDir(workingDir);
}
} }
void QbsBuildStep::createTaskAndOutput(ProjectExplorer::Task::TaskType type, const QString &message, void QbsBuildStep::createTaskAndOutput(ProjectExplorer::Task::TaskType type, const QString &message,
const QString &file, int line) const QString &file, int line)
{ {
emit addTask(CompileTask(type, message, FilePath::fromString(file), line), 1);
emit addOutput(message, OutputFormat::Stdout); emit addOutput(message, OutputFormat::Stdout);
emit addTask(CompileTask(type, message, FilePath::fromString(file), line), 1);
} }
QString QbsBuildStep::buildVariant() const QString QbsBuildStep::buildVariant() const

View File

@@ -30,7 +30,6 @@
#include <projectexplorer/buildstep.h> #include <projectexplorer/buildstep.h>
#include <projectexplorer/task.h> #include <projectexplorer/task.h>
namespace ProjectExplorer { class IOutputParser; }
namespace Utils { class FancyLineEdit; } namespace Utils { class FancyLineEdit; }
namespace QbsProjectManager { namespace QbsProjectManager {
@@ -81,6 +80,7 @@ signals:
private: private:
bool init() override; bool init() override;
void setupOutputFormatter(Utils::OutputFormatter *formatter) override;
void doRun() override; void doRun() override;
void doCancel() override; void doCancel() override;
ProjectExplorer::BuildStepConfigWidget *createConfigWidget() override; ProjectExplorer::BuildStepConfigWidget *createConfigWidget() override;
@@ -134,7 +134,6 @@ private:
QString m_currentTask; QString m_currentTask;
int m_maxProgress; int m_maxProgress;
bool m_lastWasSuccess; bool m_lastWasSuccess;
ProjectExplorer::IOutputParser *m_parser = nullptr;
bool m_parsingProject = false; bool m_parsingProject = false;
bool m_parsingAfterBuild = false; bool m_parsingAfterBuild = false;

View File

@@ -160,8 +160,8 @@ void QbsCleanStep::handleProgress(int value)
void QbsCleanStep::createTaskAndOutput(ProjectExplorer::Task::TaskType type, const QString &message, const QString &file, int line) void QbsCleanStep::createTaskAndOutput(ProjectExplorer::Task::TaskType type, const QString &message, const QString &file, int line)
{ {
emit addTask(CompileTask(type, message, Utils::FilePath::fromString(file), line), 1);
emit addOutput(message, OutputFormat::Stdout); emit addOutput(message, OutputFormat::Stdout);
emit addTask(CompileTask(type, message, Utils::FilePath::fromString(file), line), 1);
} }
// -------------------------------------------------------------------- // --------------------------------------------------------------------

View File

@@ -202,9 +202,8 @@ void QbsInstallStep::handleProgress(int value)
void QbsInstallStep::createTaskAndOutput(Task::TaskType type, const QString &message, void QbsInstallStep::createTaskAndOutput(Task::TaskType type, const QString &message,
const Utils::FilePath &file, int line) const Utils::FilePath &file, int line)
{ {
const CompileTask task(type, message, file, line);
emit addTask(task, 1);
emit addOutput(message, OutputFormat::Stdout); emit addOutput(message, OutputFormat::Stdout);
emit addTask(CompileTask(type, message, file, line), 1);
} }
void QbsInstallStep::setRemoveFirst(bool rf) void QbsInstallStep::setRemoveFirst(bool rf)

View File

@@ -164,25 +164,6 @@ bool QmakeMakeStep::init()
pp->setCommandLine(makeCmd); pp->setCommandLine(makeCmd);
pp->resolveAll(); pp->resolveAll();
setOutputParser(new ProjectExplorer::GnuMakeParser());
ToolChain *tc = ToolChainKitAspect::cxxToolChain(target()->kit());
OutputTaskParser *xcodeBuildParser = nullptr;
if (tc && tc->targetAbi().os() == Abi::DarwinOS) {
xcodeBuildParser = new XcodebuildParser;
appendOutputParser(xcodeBuildParser);
}
QList<OutputTaskParser *> 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 (OutputTaskParser * const p : qAsConst(additionalParsers))
p->setRedirectionDetector(xcodeBuildParser);
}
appendOutputParsers(additionalParsers);
outputParser()->addSearchDir(pp->effectiveWorkingDirectory());
auto rootNode = dynamic_cast<QmakeProFileNode *>(project()->rootProjectNode()); auto rootNode = dynamic_cast<QmakeProFileNode *>(project()->rootProjectNode());
QTC_ASSERT(rootNode, return false); QTC_ASSERT(rootNode, return false);
m_scriptTarget = rootNode->projectType() == ProjectType::ScriptTemplate; m_scriptTarget = rootNode->projectType() == ProjectType::ScriptTemplate;
@@ -199,6 +180,30 @@ bool QmakeMakeStep::init()
return AbstractProcessStep::init(); return AbstractProcessStep::init();
} }
void QmakeMakeStep::setupOutputFormatter(Utils::OutputFormatter *formatter)
{
formatter->addLineParser(new ProjectExplorer::GnuMakeParser());
ToolChain *tc = ToolChainKitAspect::cxxToolChain(target()->kit());
OutputTaskParser *xcodeBuildParser = nullptr;
if (tc && tc->targetAbi().os() == Abi::DarwinOS) {
xcodeBuildParser = new XcodebuildParser;
formatter->addLineParser(xcodeBuildParser);
}
QList<Utils::OutputLineParser *> 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 (Utils::OutputLineParser * const p : qAsConst(additionalParsers))
p->setRedirectionDetector(xcodeBuildParser);
}
formatter->addLineParsers(additionalParsers);
formatter->addSearchDir(processParameters()->effectiveWorkingDirectory());
AbstractProcessStep::setupOutputFormatter(formatter);
}
void QmakeMakeStep::doRun() void QmakeMakeStep::doRun()
{ {
if (m_scriptTarget || m_ignoredNonTopLevelBuild) { if (m_scriptTarget || m_ignoredNonTopLevelBuild) {

View File

@@ -52,6 +52,7 @@ public:
private: private:
void finish(bool success) override; void finish(bool success) override;
bool init() override; bool init() override;
void setupOutputFormatter(Utils::OutputFormatter *formatter) override;
void doRun() override; void doRun() override;
QStringList displayArguments() const override; QStringList displayArguments() const override;

Some files were not shown because too many files have changed in this diff Show More