forked from qt-creator/qt-creator
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:
@@ -93,6 +93,24 @@ QList<FormattedText> AnsiEscapeCodeHandler::parseText(const FormattedText &input
|
||||
|
||||
while (!strippedText.isEmpty()) {
|
||||
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));
|
||||
if (escapePos < 0) {
|
||||
outputData << FormattedText(strippedText, charFormat);
|
||||
@@ -111,11 +129,28 @@ QList<FormattedText> AnsiEscapeCodeHandler::parseText(const FormattedText &input
|
||||
break;
|
||||
}
|
||||
if (!strippedText.startsWith(escape)) {
|
||||
// not a control sequence
|
||||
m_pendingText.clear();
|
||||
outputData << FormattedText(strippedText.left(1), charFormat);
|
||||
strippedText.remove(0, 1);
|
||||
continue;
|
||||
switch (strippedText.at(1).toLatin1()) {
|
||||
case '\\': // Unexpected terminator sequence.
|
||||
QTC_CHECK(false);
|
||||
Q_FALLTHROUGH();
|
||||
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());
|
||||
strippedText.remove(0, escape.length());
|
||||
|
@@ -53,6 +53,8 @@ private:
|
||||
void setFormatScope(const QTextCharFormat &charFormat);
|
||||
|
||||
bool m_previousFormatClosed = true;
|
||||
bool m_waitingForTerminator = false;
|
||||
QString m_alternateTerminator;
|
||||
QTextCharFormat m_previousFormat;
|
||||
QString m_pendingText;
|
||||
};
|
||||
|
@@ -23,23 +23,190 @@
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "ansiescapecodehandler.h"
|
||||
#include "outputformatter.h"
|
||||
|
||||
#include "algorithm.h"
|
||||
#include "ansiescapecodehandler.h"
|
||||
#include "fileinprojectfinder.h"
|
||||
#include "qtcassert.h"
|
||||
#include "synchronousprocess.h"
|
||||
#include "theme/theme.h"
|
||||
|
||||
#include <QDir>
|
||||
#include <QFileInfo>
|
||||
#include <QPair>
|
||||
#include <QPlainTextEdit>
|
||||
#include <QPointer>
|
||||
#include <QRegExp>
|
||||
#include <QRegularExpressionMatch>
|
||||
#include <QTextCursor>
|
||||
|
||||
#include <numeric>
|
||||
|
||||
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 ®ex, 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:
|
||||
QPlainTextEdit *plainTextEdit = nullptr;
|
||||
@@ -50,20 +217,13 @@ public:
|
||||
optional<QTextCharFormat> formatOverride;
|
||||
QList<OutputLineParser *> lineParsers;
|
||||
OutputLineParser *nextParser = nullptr;
|
||||
FileInProjectFinder fileFinder;
|
||||
PostPrintAction postPrintAction;
|
||||
bool boldFontEnabled = true;
|
||||
bool prependCarriageReturn = false;
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
|
||||
OutputLineParser::~OutputLineParser()
|
||||
{
|
||||
}
|
||||
|
||||
OutputFormatter::OutputFormatter()
|
||||
: d(new Internal::OutputFormatterPrivate)
|
||||
{
|
||||
}
|
||||
OutputFormatter::OutputFormatter() : d(new Private) { }
|
||||
|
||||
OutputFormatter::~OutputFormatter()
|
||||
{
|
||||
@@ -88,8 +248,44 @@ void OutputFormatter::setLineParsers(const QList<OutputLineParser *> &parsers)
|
||||
{
|
||||
flush();
|
||||
qDeleteAll(d->lineParsers);
|
||||
d->lineParsers = parsers;
|
||||
d->lineParsers.clear();
|
||||
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)
|
||||
@@ -98,19 +294,33 @@ void OutputFormatter::doAppendMessage(const QString &text, OutputFormat format)
|
||||
const QList<FormattedText> formattedText = parseAnsi(text, charFmt);
|
||||
const QString cleanLine = std::accumulate(formattedText.begin(), formattedText.end(), QString(),
|
||||
[](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) {
|
||||
append(res.newContent.value(), charFmt);
|
||||
return;
|
||||
}
|
||||
for (const FormattedText &output : linkifiedText(formattedText, res.linkSpecs))
|
||||
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) {
|
||||
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) {
|
||||
case OutputLineParser::Status::Done:
|
||||
d->nextParser = nullptr;
|
||||
@@ -118,18 +328,22 @@ OutputLineParser::Result OutputFormatter::handleMessage(const QString &text, Out
|
||||
case OutputLineParser::Status::InProgress:
|
||||
return res;
|
||||
case OutputLineParser::Status::NotHandled:
|
||||
QTC_CHECK(false); // TODO: This case will be legal after the merge
|
||||
d->nextParser = nullptr;
|
||||
return res;
|
||||
break;
|
||||
}
|
||||
}
|
||||
QTC_CHECK(!d->nextParser);
|
||||
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) {
|
||||
case OutputLineParser::Status::Done:
|
||||
involvedParsers << parser;
|
||||
return res;
|
||||
case OutputLineParser::Status::InProgress:
|
||||
involvedParsers << parser;
|
||||
d->nextParser = parser;
|
||||
return res;
|
||||
case OutputLineParser::Status::NotHandled:
|
||||
@@ -139,12 +353,6 @@ OutputLineParser::Result OutputFormatter::handleMessage(const QString &text, Out
|
||||
return OutputLineParser::Status::NotHandled;
|
||||
}
|
||||
|
||||
void OutputFormatter::reset()
|
||||
{
|
||||
for (OutputLineParser * const p : d->lineParsers)
|
||||
p->reset();
|
||||
}
|
||||
|
||||
QTextCharFormat OutputFormatter::charFormat(OutputFormat format) const
|
||||
{
|
||||
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)
|
||||
{
|
||||
if (!plainTextEdit())
|
||||
return;
|
||||
int startPos = 0;
|
||||
int crPos = -1;
|
||||
while ((crPos = text.indexOf('\r', startPos)) >= 0) {
|
||||
@@ -237,6 +447,11 @@ void OutputFormatter::overrideTextCharFormat(const QTextCharFormat &fmt)
|
||||
{
|
||||
d->formatOverride = fmt;
|
||||
}
|
||||
|
||||
QList<OutputLineParser *> OutputFormatter::lineParsers() const
|
||||
{
|
||||
return d->lineParsers;
|
||||
}
|
||||
#endif // WITH_TESTS
|
||||
|
||||
void OutputFormatter::clearLastLine()
|
||||
@@ -274,6 +489,8 @@ void OutputFormatter::flushIncompleteLine()
|
||||
|
||||
void OutputFormatter::dumpIncompleteLine(const QString &line, OutputFormat format)
|
||||
{
|
||||
if (line.isEmpty())
|
||||
return;
|
||||
append(line, charFormat(format));
|
||||
d->incompleteLine.first.append(line);
|
||||
d->incompleteLine.second = format;
|
||||
@@ -281,6 +498,17 @@ void OutputFormatter::dumpIncompleteLine(const QString &line, OutputFormat forma
|
||||
|
||||
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)) {
|
||||
if (f->handleLink(href))
|
||||
return;
|
||||
@@ -288,11 +516,21 @@ void OutputFormatter::handleLink(const QString &href)
|
||||
}
|
||||
|
||||
void OutputFormatter::clear()
|
||||
{
|
||||
if (plainTextEdit())
|
||||
plainTextEdit()->clear();
|
||||
}
|
||||
|
||||
void OutputFormatter::reset()
|
||||
{
|
||||
d->prependCarriageReturn = false;
|
||||
d->incompleteLine.first.clear();
|
||||
plainTextEdit()->clear();
|
||||
reset();
|
||||
d->nextParser = nullptr;
|
||||
qDeleteAll(d->lineParsers);
|
||||
d->lineParsers.clear();
|
||||
d->fileFinder = FileInProjectFinder();
|
||||
d->formatOverride.reset();
|
||||
d->escapeCodeHandler = AnsiEscapeCodeHandler();
|
||||
}
|
||||
|
||||
void OutputFormatter::setBoldFontEnabled(bool enabled)
|
||||
@@ -308,11 +546,44 @@ void OutputFormatter::flush()
|
||||
if (!d->incompleteLine.first.isEmpty())
|
||||
flushIncompleteLine();
|
||||
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)
|
||||
{
|
||||
if (text.isEmpty())
|
||||
return;
|
||||
|
||||
// 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,
|
||||
// possibly formatted now, and start from scratch with the new input.
|
||||
|
@@ -26,25 +26,32 @@
|
||||
#pragma once
|
||||
|
||||
#include "utils_global.h"
|
||||
#include "fileutils.h"
|
||||
#include "optional.h"
|
||||
#include "outputformat.h"
|
||||
|
||||
#include <QObject>
|
||||
|
||||
#include <functional>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QPlainTextEdit;
|
||||
class QRegExp;
|
||||
class QRegularExpressionMatch;
|
||||
class QTextCharFormat;
|
||||
class QTextCursor;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
namespace Utils {
|
||||
|
||||
class FileInProjectFinder;
|
||||
class FormattedText;
|
||||
|
||||
class QTCREATOR_UTILS_EXPORT OutputLineParser
|
||||
class QTCREATOR_UTILS_EXPORT OutputLineParser : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
virtual ~OutputLineParser();
|
||||
OutputLineParser();
|
||||
~OutputLineParser() override;
|
||||
|
||||
enum class Status { Done, InProgress, NotHandled };
|
||||
class LinkSpec {
|
||||
@@ -65,20 +72,62 @@ public:
|
||||
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.
|
||||
// Either way, the input is to be considered "complete" for parsing purposes.
|
||||
virtual Result handleLine(const QString &line, OutputFormat format) = 0;
|
||||
|
||||
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 ®ex, 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
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
OutputFormatter();
|
||||
~OutputFormatter() override;
|
||||
@@ -86,17 +135,33 @@ public:
|
||||
QPlainTextEdit *plainTextEdit() const;
|
||||
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 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 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 clear();
|
||||
void setBoldFontEnabled(bool enabled);
|
||||
|
||||
bool hasFatalErrors() const;
|
||||
|
||||
#ifdef WITH_TESTS
|
||||
void overrideTextCharFormat(const QTextCharFormat &fmt);
|
||||
QList<OutputLineParser *> lineParsers() const;
|
||||
#endif
|
||||
|
||||
#ifndef WITH_TESTS
|
||||
@@ -105,11 +170,14 @@ private:
|
||||
QTextCharFormat charFormat(OutputFormat format) const;
|
||||
static QTextCharFormat linkFormat(const QTextCharFormat &inputFormat, const QString &href);
|
||||
|
||||
signals:
|
||||
void openInEditorRequested(const FilePath &filePath, int line, int column);
|
||||
|
||||
private:
|
||||
void doAppendMessage(const QString &text, OutputFormat format);
|
||||
|
||||
OutputLineParser::Result handleMessage(const QString &text, OutputFormat format);
|
||||
void reset();
|
||||
OutputLineParser::Result handleMessage(const QString &text, OutputFormat format,
|
||||
QList<OutputLineParser *> &involvedParsers);
|
||||
|
||||
void append(const QString &text, const QTextCharFormat &format);
|
||||
void initFormats();
|
||||
@@ -119,8 +187,11 @@ private:
|
||||
QList<FormattedText> parseAnsi(const QString &text, const QTextCharFormat &format);
|
||||
const QList<Utils::FormattedText> linkifiedText(const QList<FormattedText> &text,
|
||||
const OutputLineParser::LinkSpecs &linkSpecs);
|
||||
OutputFormat outputTypeForParser(const OutputLineParser *parser, OutputFormat type) const;
|
||||
void setupLineParser(OutputLineParser *parser);
|
||||
|
||||
Internal::OutputFormatterPrivate *d;
|
||||
class Private;
|
||||
Private * const d;
|
||||
};
|
||||
|
||||
|
||||
|
@@ -178,22 +178,6 @@ bool AndroidBuildApkStep::init()
|
||||
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;
|
||||
|
||||
if (m_buildAAB) {
|
||||
@@ -218,6 +202,8 @@ bool AndroidBuildApkStep::init()
|
||||
|
||||
QString outputDir = buildDirectory().pathAppended(Constants::ANDROID_BUILDDIRECTORY).toString();
|
||||
|
||||
const QString buildKey = target()->activeBuildKey();
|
||||
const ProjectNode *node = project()->findNodeForBuildKey(buildKey);
|
||||
if (node)
|
||||
m_inputFile = node->data(Constants::AndroidDeploySettingsFile).toString();
|
||||
|
||||
@@ -285,6 +271,23 @@ bool AndroidBuildApkStep::init()
|
||||
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()
|
||||
{
|
||||
Core::FileUtils::showInGraphicalShell(Core::ICore::mainWindow(), m_packagePath);
|
||||
|
@@ -82,6 +82,7 @@ private:
|
||||
void showInGraphicalShell();
|
||||
|
||||
bool init() override;
|
||||
void setupOutputFormatter(Utils::OutputFormatter *formatter) override;
|
||||
ProjectExplorer::BuildStepConfigWidget *createConfigWidget() override;
|
||||
void processStarted() override;
|
||||
void processFinished(int exitCode, QProcess::ExitStatus status) override;
|
||||
|
@@ -60,6 +60,7 @@ public:
|
||||
|
||||
private:
|
||||
bool init() final;
|
||||
void setupOutputFormatter(Utils::OutputFormatter *formatter) override;
|
||||
void doRun() final;
|
||||
|
||||
QStringList m_androidDirsToClean;
|
||||
@@ -111,10 +112,6 @@ bool AndroidPackageInstallationStep::init()
|
||||
pp->setEnvironment(env);
|
||||
pp->setCommandLine(cmd);
|
||||
|
||||
setOutputParser(new GnuMakeParser());
|
||||
appendOutputParsers(target()->kit()->createOutputParsers());
|
||||
outputParser()->addSearchDir(pp->effectiveWorkingDirectory());
|
||||
|
||||
m_androidDirsToClean.clear();
|
||||
// don't remove gradle's cache, it takes ages to rebuild it.
|
||||
m_androidDirsToClean << dirPath + "/assets";
|
||||
@@ -123,6 +120,14 @@ bool AndroidPackageInstallationStep::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()
|
||||
{
|
||||
QString error;
|
||||
|
@@ -51,7 +51,8 @@ void JavaParser::setSourceDirectory(const Utils::FilePath &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);
|
||||
if (m_javaRegExp.indexIn(line) == -1)
|
||||
@@ -78,6 +79,8 @@ OutputTaskParser::Status JavaParser::handleLine(const QString &line, Utils::Outp
|
||||
m_javaRegExp.cap(4).trimmed(),
|
||||
absoluteFilePath(file),
|
||||
lineno);
|
||||
emit addTask(task, 1);
|
||||
return Status::Done;
|
||||
LinkSpecs linkSpecs;
|
||||
addLinkSpecForAbsoluteFilePath(linkSpecs, task.file, task.line, m_javaRegExp, 2);
|
||||
scheduleTask(task, 1);
|
||||
return {Status::Done, linkSpecs};
|
||||
}
|
||||
|
@@ -45,7 +45,7 @@ public:
|
||||
void setSourceDirectory(const Utils::FilePath &sourceDirectory);
|
||||
|
||||
private:
|
||||
Status handleLine(const QString &line, Utils::OutputFormat type) override;
|
||||
Result handleLine(const QString &line, Utils::OutputFormat type) override;
|
||||
|
||||
QRegExp m_javaRegExp;
|
||||
QStringList m_fileList;
|
||||
|
@@ -145,12 +145,12 @@ bool IarParser::parseErrorOrFatalErrorDetailsMessage2(const QString &lne)
|
||||
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 QRegularExpressionMatch match = re.match(lne);
|
||||
if (!match.hasMatch())
|
||||
return false;
|
||||
return Status::NotHandled;
|
||||
enum CaptureIndex { FilePathIndex = 1, LineNumberIndex,
|
||||
MessageTypeIndex, MessageCodeIndex };
|
||||
const Utils::FilePath fileName = Utils::FilePath::fromUserInput(
|
||||
@@ -164,7 +164,10 @@ bool IarParser::parseWarningOrErrorOrFatalErrorDetailsMessage1(const QString &ln
|
||||
m_expectDescription = true;
|
||||
m_expectSnippet = 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)
|
||||
@@ -190,7 +193,7 @@ bool IarParser::parseErrorMessage1(const QString &lne)
|
||||
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);
|
||||
if (type == StdOutFormat) {
|
||||
@@ -208,8 +211,9 @@ OutputTaskParser::Status IarParser::handleLine(const QString &line, OutputFormat
|
||||
return Status::InProgress;
|
||||
if (parseErrorOrFatalErrorDetailsMessage2(lne))
|
||||
return Status::InProgress;
|
||||
if (parseWarningOrErrorOrFatalErrorDetailsMessage1(lne))
|
||||
return Status::InProgress;
|
||||
const Result res = parseWarningOrErrorOrFatalErrorDetailsMessage1(lne);
|
||||
if (res.status != Status::NotHandled)
|
||||
return res;
|
||||
|
||||
if (m_expectFilePath) {
|
||||
if (lne.endsWith(']')) {
|
||||
@@ -256,7 +260,7 @@ void IarParser::flush()
|
||||
|
||||
Task t = m_lastTask;
|
||||
m_lastTask.clear();
|
||||
emit addTask(t, m_lines, 1);
|
||||
scheduleTask(t, m_lines, 1);
|
||||
m_lines = 0;
|
||||
}
|
||||
|
||||
|
@@ -48,11 +48,11 @@ private:
|
||||
|
||||
bool parseErrorOrFatalErrorDetailsMessage1(const QString &lne);
|
||||
bool parseErrorOrFatalErrorDetailsMessage2(const QString &lne);
|
||||
bool parseWarningOrErrorOrFatalErrorDetailsMessage1(const QString &lne);
|
||||
Result parseWarningOrErrorOrFatalErrorDetailsMessage1(const QString &lne);
|
||||
bool parseErrorInCommandLineMessage(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;
|
||||
|
||||
ProjectExplorer::Task m_lastTask;
|
||||
|
@@ -354,7 +354,7 @@ void IarToolChain::addToEnvironment(Environment &env) const
|
||||
}
|
||||
}
|
||||
|
||||
QList<OutputTaskParser *> IarToolChain::createOutputParsers() const
|
||||
QList<Utils::OutputLineParser *> IarToolChain::createOutputParsers() const
|
||||
{
|
||||
return {new IarParser()};
|
||||
}
|
||||
|
@@ -68,7 +68,7 @@ public:
|
||||
const Utils::FilePath &,
|
||||
const 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;
|
||||
bool fromMap(const QVariantMap &data) final;
|
||||
|
@@ -93,12 +93,12 @@ void KeilParser::amendDescription()
|
||||
|
||||
// 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 QRegularExpressionMatch match = re.match(lne);
|
||||
if (!match.hasMatch())
|
||||
return false;
|
||||
return Status::NotHandled;
|
||||
enum CaptureIndex { FilePathIndex = 1, LineNumberIndex,
|
||||
MessageTypeIndex, MessageNoteIndex, DescriptionIndex };
|
||||
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 QString descr = match.captured(DescriptionIndex);
|
||||
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)
|
||||
@@ -125,12 +128,12 @@ bool KeilParser::parseArmErrorOrFatalErorrMessage(const QString &lne)
|
||||
|
||||
// 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 QRegularExpressionMatch match = re.match(lne);
|
||||
if (!match.hasMatch())
|
||||
return false;
|
||||
return Status::NotHandled;
|
||||
enum CaptureIndex { MessageTypeIndex = 1, MessageCodeIndex, LineNumberIndex,
|
||||
FilePathIndex, MessageTextIndex };
|
||||
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),
|
||||
match.captured(MessageTextIndex));
|
||||
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 QRegularExpressionMatch match = re.match(lne);
|
||||
if (!match.hasMatch())
|
||||
return false;
|
||||
return Status::NotHandled;
|
||||
enum CaptureIndex { MessageTypeIndex = 1, MessageCodeIndex, LineNumberIndex,
|
||||
FilePathIndex, MessageTextIndex };
|
||||
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),
|
||||
match.captured(MessageTextIndex));
|
||||
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)
|
||||
@@ -206,15 +215,17 @@ static bool hasDetailsPointer(const QString &trimmedLine)
|
||||
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);
|
||||
if (type == StdOutFormat) {
|
||||
// Check for MSC51 compiler specific patterns.
|
||||
const bool parsed = parseMcs51WarningOrErrorDetailsMessage1(lne)
|
||||
|| parseMcs51WarningOrErrorDetailsMessage2(lne);
|
||||
if (parsed)
|
||||
return Status::InProgress;
|
||||
Result res = parseMcs51WarningOrErrorDetailsMessage1(lne);
|
||||
if (res.status != Status::NotHandled)
|
||||
return res;
|
||||
res = parseMcs51WarningOrErrorDetailsMessage2(lne);
|
||||
if (res.status != Status::NotHandled)
|
||||
return res;
|
||||
if (parseMcs51WarningOrFatalErrorMessage(lne))
|
||||
return Status::InProgress;
|
||||
if (parseMcs51FatalErrorMessage2(lne))
|
||||
@@ -247,8 +258,9 @@ OutputTaskParser::Status KeilParser::handleLine(const QString &line, OutputForma
|
||||
}
|
||||
|
||||
// Check for ARM compiler specific patterns.
|
||||
if (parseArmWarningOrErrorDetailsMessage(lne))
|
||||
return Status::InProgress;
|
||||
const Result res = parseArmWarningOrErrorDetailsMessage(lne);
|
||||
if (res.status != Status::NotHandled)
|
||||
return res;
|
||||
if (parseArmErrorOrFatalErorrMessage(lne))
|
||||
return Status::InProgress;
|
||||
|
||||
@@ -270,7 +282,7 @@ void KeilParser::flush()
|
||||
|
||||
Task t = m_lastTask;
|
||||
m_lastTask.clear();
|
||||
emit addTask(t, m_lines, 1);
|
||||
scheduleTask(t, m_lines, 1);
|
||||
m_lines = 0;
|
||||
}
|
||||
|
||||
|
@@ -46,16 +46,16 @@ private:
|
||||
void amendDescription();
|
||||
|
||||
// ARM compiler specific parsers.
|
||||
bool parseArmWarningOrErrorDetailsMessage(const QString &lne);
|
||||
Result parseArmWarningOrErrorDetailsMessage(const QString &lne);
|
||||
bool parseArmErrorOrFatalErorrMessage(const QString &lne);
|
||||
|
||||
// MCS51 compiler specific parsers.
|
||||
bool parseMcs51WarningOrErrorDetailsMessage1(const QString &lne);
|
||||
bool parseMcs51WarningOrErrorDetailsMessage2(const QString &lne);
|
||||
Result parseMcs51WarningOrErrorDetailsMessage1(const QString &lne);
|
||||
Result parseMcs51WarningOrErrorDetailsMessage2(const QString &lne);
|
||||
bool parseMcs51WarningOrFatalErrorMessage(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;
|
||||
|
||||
ProjectExplorer::Task m_lastTask;
|
||||
|
@@ -506,7 +506,7 @@ void KeilToolChain::addToEnvironment(Environment &env) const
|
||||
}
|
||||
}
|
||||
|
||||
QList<OutputTaskParser *> KeilToolChain::createOutputParsers() const
|
||||
QList<OutputLineParser *> KeilToolChain::createOutputParsers() const
|
||||
{
|
||||
return {new KeilParser};
|
||||
}
|
||||
|
@@ -69,7 +69,7 @@ public:
|
||||
const Utils::FilePath &,
|
||||
const 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;
|
||||
bool fromMap(const QVariantMap &data) final;
|
||||
|
@@ -87,7 +87,7 @@ void SdccParser::amendDescription(const QString &desc)
|
||||
++m_lines;
|
||||
}
|
||||
|
||||
OutputTaskParser::Status SdccParser::handleLine(const QString &line, OutputFormat type)
|
||||
OutputLineParser::Result SdccParser::handleLine(const QString &line, OutputFormat type)
|
||||
{
|
||||
if (type == StdOutFormat)
|
||||
return Status::NotHandled;
|
||||
@@ -108,7 +108,10 @@ OutputTaskParser::Status SdccParser::handleLine(const QString &line, OutputForma
|
||||
const Task::TaskType type = taskType(match.captured(MessageTypeIndex));
|
||||
const QString descr = match.captured(MessageTextIndex);
|
||||
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): (.+)$");
|
||||
@@ -122,7 +125,10 @@ OutputTaskParser::Status SdccParser::handleLine(const QString &line, OutputForma
|
||||
const Task::TaskType type = taskType(match.captured(MessageTypeIndex));
|
||||
const QString descr = match.captured(MessageTextIndex);
|
||||
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+: (.+)$");
|
||||
@@ -161,7 +167,7 @@ void SdccParser::flush()
|
||||
|
||||
Task t = m_lastTask;
|
||||
m_lastTask.clear();
|
||||
emit addTask(t, m_lines, 1);
|
||||
scheduleTask(t, m_lines, 1);
|
||||
m_lines = 0;
|
||||
}
|
||||
|
||||
|
@@ -45,7 +45,7 @@ private:
|
||||
void newTask(const ProjectExplorer::Task &task);
|
||||
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;
|
||||
|
||||
ProjectExplorer::Task m_lastTask;
|
||||
|
@@ -307,7 +307,7 @@ void SdccToolChain::addToEnvironment(Environment &env) const
|
||||
}
|
||||
}
|
||||
|
||||
QList<OutputTaskParser *> SdccToolChain::createOutputParsers() const
|
||||
QList<Utils::OutputLineParser *> SdccToolChain::createOutputParsers() const
|
||||
{
|
||||
return {new SdccParser};
|
||||
}
|
||||
|
@@ -69,7 +69,7 @@ public:
|
||||
const Utils::FilePath &,
|
||||
const 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;
|
||||
bool fromMap(const QVariantMap &data) final;
|
||||
|
@@ -197,16 +197,19 @@ bool CMakeBuildStep::init()
|
||||
pp->setCommandLine(cmakeCommand(rc));
|
||||
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();
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
// Make sure CMake state was written to disk before trying to build:
|
||||
|
@@ -83,6 +83,7 @@ private:
|
||||
void ctor(ProjectExplorer::BuildStepList *bsl);
|
||||
|
||||
bool init() override;
|
||||
void setupOutputFormatter(Utils::OutputFormatter *formatter) override;
|
||||
void doRun() override;
|
||||
ProjectExplorer::BuildStepConfigWidget *createConfigWidget() override;
|
||||
|
||||
|
@@ -54,10 +54,13 @@ CMakeParser::CMakeParser()
|
||||
|
||||
void CMakeParser::setSourceDirectory(const QString &sourceDir)
|
||||
{
|
||||
if (m_sourceDirectory)
|
||||
emit searchDirExpired(FilePath::fromString(m_sourceDirectory.value().path()));
|
||||
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)
|
||||
return Status::NotHandled;
|
||||
@@ -80,18 +83,23 @@ OutputTaskParser::Status CMakeParser::handleLine(const QString &line, OutputForm
|
||||
QString path = m_sourceDirectory ? m_sourceDirectory->absoluteFilePath(
|
||||
QDir::fromNativeSeparators(m_commonError.cap(1)))
|
||||
: QDir::fromNativeSeparators(m_commonError.cap(1));
|
||||
|
||||
m_lastTask = BuildSystemTask(Task::Error,
|
||||
QString(),
|
||||
absoluteFilePath(FilePath::fromUserInput(path)),
|
||||
m_commonError.cap(2).toInt());
|
||||
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) {
|
||||
m_lastTask = BuildSystemTask(Task::Error, QString(),
|
||||
absoluteFilePath(FilePath::fromUserInput(m_nextSubError.cap(1))));
|
||||
LinkSpecs linkSpecs;
|
||||
addLinkSpecForAbsoluteFilePath(linkSpecs, m_lastTask.file, m_lastTask.line,
|
||||
m_nextSubError, 1);
|
||||
m_lines = 1;
|
||||
return Status::InProgress;
|
||||
return {Status::InProgress, linkSpecs};
|
||||
} else if (trimmedLine.startsWith(QLatin1String(" ")) && !m_lastTask.isNull()) {
|
||||
if (!m_lastTask.description.isEmpty())
|
||||
m_lastTask.description.append(QLatin1Char(' '));
|
||||
@@ -118,11 +126,15 @@ OutputTaskParser::Status CMakeParser::handleLine(const QString &line, OutputForm
|
||||
{
|
||||
QRegularExpressionMatch m = m_locationLine.match(trimmedLine);
|
||||
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_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:
|
||||
m_lastTask.description = trimmedLine;
|
||||
if (trimmedLine.endsWith(QLatin1Char('\"')))
|
||||
@@ -149,7 +161,7 @@ void CMakeParser::flush()
|
||||
return;
|
||||
Task t = m_lastTask;
|
||||
m_lastTask.clear();
|
||||
emit addTask(t, m_lines, 1);
|
||||
scheduleTask(t, m_lines, 1);
|
||||
m_lines = 0;
|
||||
}
|
||||
|
||||
|
@@ -47,7 +47,7 @@ public:
|
||||
void setSourceDirectory(const QString &sourceDir);
|
||||
|
||||
private:
|
||||
Status handleLine(const QString &line, Utils::OutputFormat type) override;
|
||||
Result handleLine(const QString &line, Utils::OutputFormat type) override;
|
||||
void flush() override;
|
||||
|
||||
enum TripleLineError { NONE, LINE_LOCATION, LINE_DESCRIPTION, LINE_DESCRIPTION2 };
|
||||
|
@@ -94,17 +94,6 @@ void CMakeProcess::run(const BuildDirParameters ¶meters, const QStringList &
|
||||
const auto parser = new CMakeParser;
|
||||
parser->setSourceDirectory(srcDir);
|
||||
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
|
||||
// then we are racing against CMakeCache.txt also getting deleted.
|
||||
@@ -194,7 +183,7 @@ void CMakeProcess::processStandardError()
|
||||
|
||||
static QString rest;
|
||||
rest = lineSplit(rest, m_process->readAllStandardError(), [this](const QString &s) {
|
||||
m_parser.handleStderr(s);
|
||||
m_parser.appendMessage(s, Utils::StdErrFormat);
|
||||
Core::MessageManager::write(s);
|
||||
});
|
||||
}
|
||||
|
@@ -27,8 +27,7 @@
|
||||
|
||||
#include "builddirparameters.h"
|
||||
|
||||
#include <projectexplorer/ioutputparser.h>
|
||||
|
||||
#include <utils/outputformatter.h>
|
||||
#include <utils/qtcprocess.h>
|
||||
|
||||
#include <QElapsedTimer>
|
||||
@@ -72,7 +71,7 @@ private:
|
||||
void checkForCancelled();
|
||||
|
||||
std::unique_ptr<Utils::QtcProcess> m_process;
|
||||
ProjectExplorer::IOutputParser m_parser;
|
||||
Utils::OutputFormatter m_parser;
|
||||
std::unique_ptr<QFutureInterface<void>> m_future;
|
||||
bool m_processWasCanceled = false;
|
||||
QTimer m_cancelTimer;
|
||||
|
@@ -64,14 +64,6 @@ ServerModeReader::ServerModeReader()
|
||||
{
|
||||
m_cmakeParser = new 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()
|
||||
@@ -351,7 +343,7 @@ void ServerModeReader::createNewServer()
|
||||
connect(m_cmakeServer.get(), &ServerMode::cmakeMessage, [this](const QString &m) {
|
||||
const QStringList lines = m.split('\n');
|
||||
for (const QString &l : lines) {
|
||||
m_parser.handleStderr(l);
|
||||
m_parser.appendMessage(l, StdErrFormat);
|
||||
Core::MessageManager::write(l);
|
||||
}
|
||||
});
|
||||
|
@@ -37,6 +37,7 @@
|
||||
#include <memory>
|
||||
|
||||
namespace ProjectExplorer { class ProjectNode; }
|
||||
namespace Utils { class OutputFormatter; }
|
||||
|
||||
namespace CMakeProjectManager {
|
||||
|
||||
@@ -187,7 +188,7 @@ private:
|
||||
QList<FileGroup *> m_fileGroups;
|
||||
|
||||
CMakeParser *m_cmakeParser = nullptr;
|
||||
ProjectExplorer::IOutputParser m_parser;
|
||||
Utils::OutputFormatter m_parser;
|
||||
|
||||
#if defined(WITH_TESTS)
|
||||
friend class CMakeProjectPlugin;
|
||||
|
@@ -26,6 +26,7 @@
|
||||
#include "outputwindow.h"
|
||||
|
||||
#include "actionmanager/actionmanager.h"
|
||||
#include "editormanager/editormanager.h"
|
||||
#include "coreconstants.h"
|
||||
#include "coreplugin.h"
|
||||
#include "icore.h"
|
||||
@@ -138,6 +139,11 @@ OutputWindow::OutputWindow(Context context, const QString &settingsKey, QWidget
|
||||
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);
|
||||
redoAction->setEnabled(false);
|
||||
cutAction->setEnabled(false);
|
||||
@@ -528,12 +534,10 @@ private:
|
||||
return Status::NotHandled;
|
||||
}
|
||||
|
||||
void reset() override { 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
|
||||
{
|
||||
private:
|
||||
@@ -571,18 +575,17 @@ void Internal::CorePlugin::testOutputFormatter()
|
||||
" A trick\n"
|
||||
" embedded carriage return\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
|
||||
// offsets.
|
||||
for (int i = 0; i < input.length(); ++i) {
|
||||
formatter.appendMessage(input.left(i), NormalMessageFormat);
|
||||
formatter.appendMessage(input.mid(i), NormalMessageFormat);
|
||||
OutputFormatter formatter;
|
||||
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);
|
||||
formatter.clear();
|
||||
}
|
||||
}
|
||||
#endif // WITH_TESTS
|
||||
|
@@ -82,6 +82,7 @@ public:
|
||||
Utils::FilePath buildCommand() const;
|
||||
|
||||
bool init() final;
|
||||
void setupOutputFormatter(Utils::OutputFormatter *formatter);
|
||||
void doRun() final;
|
||||
bool fromMap(const QVariantMap &map) final;
|
||||
QVariantMap toMap() const final;
|
||||
@@ -222,13 +223,17 @@ bool IosBuildStep::init()
|
||||
// That is mostly so that rebuild works on an already clean project
|
||||
setIgnoreReturnValue(m_clean);
|
||||
|
||||
setOutputParser(new GnuMakeParser());
|
||||
appendOutputParsers(target()->kit()->createOutputParsers());
|
||||
outputParser()->addSearchDir(pp->effectiveWorkingDirectory());
|
||||
|
||||
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 map(AbstractProcessStep::toMap());
|
||||
|
@@ -80,9 +80,6 @@ bool IosDsymBuildStep::init()
|
||||
// That is mostly so that rebuild works on an already clean project
|
||||
setIgnoreReturnValue(m_clean);
|
||||
|
||||
appendOutputParsers(target()->kit()->createOutputParsers());
|
||||
outputParser()->addSearchDir(pp->effectiveWorkingDirectory());
|
||||
|
||||
return AbstractProcessStep::init();
|
||||
}
|
||||
|
||||
@@ -189,6 +186,13 @@ void IosDsymBuildStep::doRun()
|
||||
AbstractProcessStep::doRun();
|
||||
}
|
||||
|
||||
void IosDsymBuildStep::setupOutputFormatter(OutputFormatter *formatter)
|
||||
{
|
||||
formatter->setLineParsers(target()->kit()->createOutputParsers());
|
||||
formatter->addSearchDir(processParameters()->effectiveWorkingDirectory());
|
||||
AbstractProcessStep::setupOutputFormatter(formatter);
|
||||
}
|
||||
|
||||
BuildStepConfigWidget *IosDsymBuildStep::createConfigWidget()
|
||||
{
|
||||
return new IosDsymBuildStepConfigWidget(this);
|
||||
|
@@ -56,6 +56,7 @@ public:
|
||||
private:
|
||||
bool init() override;
|
||||
void doRun() override;
|
||||
void setupOutputFormatter(Utils::OutputFormatter *formatter) override;
|
||||
QVariantMap toMap() const override;
|
||||
bool fromMap(const QVariantMap &map) override;
|
||||
|
||||
|
@@ -45,7 +45,7 @@ namespace {
|
||||
|
||||
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();
|
||||
static QRegularExpression regex("(.+.nim)\\((\\d+), (\\d+)\\) (.+)",
|
||||
@@ -74,9 +74,12 @@ class NimParser : public OutputTaskParser
|
||||
else
|
||||
return Status::NotHandled;
|
||||
|
||||
emit addTask(CompileTask(type, message, absoluteFilePath(FilePath::fromUserInput(filename)),
|
||||
lineNumber));
|
||||
return Status::Done;
|
||||
const CompileTask t(type, message, absoluteFilePath(FilePath::fromUserInput(filename)),
|
||||
lineNumber);
|
||||
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()
|
||||
{
|
||||
auto parser = new NimParser();
|
||||
parser->addSearchDir(project()->projectDirectory());
|
||||
setOutputParser(parser);
|
||||
|
||||
ProcessParameters* params = processParameters();
|
||||
params->setEnvironment(buildEnvironment());
|
||||
params->setMacroExpander(macroExpander());
|
||||
@@ -107,6 +106,14 @@ bool NimbleBuildStep::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()
|
||||
{
|
||||
return new NimbleBuildStepWidget(this);
|
||||
|
@@ -37,7 +37,7 @@ public:
|
||||
NimbleBuildStep(ProjectExplorer::BuildStepList *parentList, Core::Id id);
|
||||
|
||||
bool init() override;
|
||||
|
||||
void setupOutputFormatter(Utils::OutputFormatter *formatter) override;
|
||||
ProjectExplorer::BuildStepConfigWidget *createConfigWidget() override;
|
||||
|
||||
QString arguments() const;
|
||||
|
@@ -47,7 +47,7 @@ namespace Nim {
|
||||
|
||||
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();
|
||||
static QRegularExpression regex("(.+.nim)\\((\\d+), (\\d+)\\) (.+)",
|
||||
@@ -76,9 +76,12 @@ class NimParser : public ProjectExplorer::OutputTaskParser
|
||||
else
|
||||
return Status::NotHandled;
|
||||
|
||||
emit addTask(CompileTask(type, message, absoluteFilePath(FilePath::fromUserInput(filename)),
|
||||
lineNumber));
|
||||
return Status::Done;
|
||||
const CompileTask t(type, message, absoluteFilePath(FilePath::fromUserInput(filename)),
|
||||
lineNumber);
|
||||
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();
|
||||
}
|
||||
|
||||
bool NimCompilerBuildStep::init()
|
||||
void NimCompilerBuildStep::setupOutputFormatter(OutputFormatter *formatter)
|
||||
{
|
||||
setOutputParser(new NimParser());
|
||||
appendOutputParsers(target()->kit()->createOutputParsers());
|
||||
outputParser()->addSearchDir(processParameters()->effectiveWorkingDirectory());
|
||||
return AbstractProcessStep::init();
|
||||
formatter->addLineParser(new NimParser);
|
||||
formatter->addLineParsers(target()->kit()->createOutputParsers());
|
||||
formatter->addSearchDir(processParameters()->effectiveWorkingDirectory());
|
||||
AbstractProcessStep::setupOutputFormatter(formatter);
|
||||
}
|
||||
|
||||
BuildStepConfigWidget *NimCompilerBuildStep::createConfigWidget()
|
||||
|
@@ -41,7 +41,7 @@ public:
|
||||
|
||||
NimCompilerBuildStep(ProjectExplorer::BuildStepList *parentList, Core::Id id);
|
||||
|
||||
bool init() override;
|
||||
void setupOutputFormatter(Utils::OutputFormatter *formatter) override;
|
||||
ProjectExplorer::BuildStepConfigWidget *createConfigWidget() override;
|
||||
|
||||
bool fromMap(const QVariantMap &map) override;
|
||||
|
@@ -120,7 +120,7 @@ void NimToolChain::setCompilerCommand(const FilePath &compilerCommand)
|
||||
parseVersion(compilerCommand, m_version);
|
||||
}
|
||||
|
||||
QList<OutputTaskParser *> NimToolChain::createOutputParsers() const
|
||||
QList<Utils::OutputLineParser *> NimToolChain::createOutputParsers() const
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
@@ -56,7 +56,7 @@ public:
|
||||
Utils::FilePath compilerCommand() const final;
|
||||
QString compilerVersion() const;
|
||||
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;
|
||||
|
||||
QVariantMap toMap() const final;
|
||||
|
@@ -8,7 +8,6 @@ add_qtc_plugin(ProjectExplorer
|
||||
addrunconfigdialog.cpp addrunconfigdialog.h
|
||||
allprojectsfilter.cpp allprojectsfilter.h
|
||||
allprojectsfind.cpp allprojectsfind.h
|
||||
ansifilterparser.cpp ansifilterparser.h
|
||||
applicationlauncher.cpp applicationlauncher.h
|
||||
appoutputpane.cpp appoutputpane.h
|
||||
baseprojectwizarddialog.cpp baseprojectwizarddialog.h
|
||||
|
@@ -24,7 +24,6 @@
|
||||
****************************************************************************/
|
||||
|
||||
#include "abstractprocessstep.h"
|
||||
#include "ansifilterparser.h"
|
||||
#include "buildconfiguration.h"
|
||||
#include "buildstep.h"
|
||||
#include "ioutputparser.h"
|
||||
@@ -37,8 +36,8 @@
|
||||
|
||||
#include <coreplugin/reaper.h>
|
||||
|
||||
#include <utils/fileinprojectfinder.h>
|
||||
#include <utils/fileutils.h>
|
||||
#include <utils/outputformatter.h>
|
||||
#include <utils/qtcassert.h>
|
||||
#include <utils/qtcprocess.h>
|
||||
|
||||
@@ -106,27 +105,18 @@ public:
|
||||
|
||||
AbstractProcessStep *q;
|
||||
std::unique_ptr<Utils::QtcProcess> m_process;
|
||||
IOutputParser m_outputParser;
|
||||
ProcessParameters m_param;
|
||||
bool m_ignoreReturnValue = false;
|
||||
bool m_lowPriority = false;
|
||||
std::unique_ptr<QTextDecoder> stdoutStream;
|
||||
std::unique_ptr<QTextDecoder> stderrStream;
|
||||
OutputFormatter *outputFormatter = nullptr;
|
||||
};
|
||||
|
||||
AbstractProcessStep::AbstractProcessStep(BuildStepList *bsl, Core::Id id) :
|
||||
BuildStep(bsl, id),
|
||||
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()
|
||||
@@ -134,36 +124,6 @@ AbstractProcessStep::~AbstractProcessStep()
|
||||
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()
|
||||
{
|
||||
emit addOutput(tr("Configuration is faulty. Check the Issues view for details."),
|
||||
@@ -194,14 +154,16 @@ void AbstractProcessStep::setIgnoreReturnValue(bool b)
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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
|
||||
YourBuildStep::run().
|
||||
@@ -257,7 +219,6 @@ void AbstractProcessStep::doRun()
|
||||
if (!d->m_process->waitForStarted()) {
|
||||
processStartupFailed();
|
||||
d->m_process.reset();
|
||||
d->m_outputParser.clear();
|
||||
finish(false);
|
||||
return;
|
||||
}
|
||||
@@ -285,7 +246,6 @@ void AbstractProcessStep::cleanUp(QProcess *process)
|
||||
processFinished(process->exitCode(), process->exitStatus());
|
||||
const bool returnValue = processSucceeded(process->exitCode(), process->exitStatus()) || d->m_ignoreReturnValue;
|
||||
|
||||
d->m_outputParser.clear();
|
||||
d->m_process.reset();
|
||||
|
||||
// Report result
|
||||
@@ -315,9 +275,6 @@ void AbstractProcessStep::processStarted()
|
||||
|
||||
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());
|
||||
if (status == QProcess::NormalExit && exitCode == 0) {
|
||||
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)
|
||||
{
|
||||
if (outputParser()->hasFatalErrors())
|
||||
if (d->outputFormatter->hasFatalErrors())
|
||||
return false;
|
||||
|
||||
return exitCode == 0 && status == QProcess::NormalExit;
|
||||
@@ -372,7 +329,6 @@ void AbstractProcessStep::processReadyReadStdOutput()
|
||||
|
||||
void AbstractProcessStep::stdOutput(const QString &output)
|
||||
{
|
||||
d->m_outputParser.handleStdout(output);
|
||||
emit addOutput(output, BuildStep::OutputFormat::Stdout, BuildStep::DontAppendNewline);
|
||||
}
|
||||
|
||||
@@ -391,7 +347,6 @@ void AbstractProcessStep::processReadyReadStdError()
|
||||
|
||||
void AbstractProcessStep::stdError(const QString &output)
|
||||
{
|
||||
d->m_outputParser.handleStderr(output);
|
||||
emit addOutput(output, BuildStep::OutputFormat::Stderr, BuildStep::DontAppendNewline);
|
||||
}
|
||||
|
||||
|
@@ -31,8 +31,6 @@
|
||||
|
||||
namespace Utils { class FilePath; }
|
||||
namespace ProjectExplorer {
|
||||
|
||||
class IOutputParser;
|
||||
class OutputTaskParser;
|
||||
class ProcessParameters;
|
||||
|
||||
@@ -47,17 +45,13 @@ public:
|
||||
bool ignoreReturnValue();
|
||||
void setIgnoreReturnValue(bool b);
|
||||
|
||||
void setOutputParser(OutputTaskParser *parser);
|
||||
void appendOutputParser(OutputTaskParser *parser);
|
||||
void appendOutputParsers(const QList<OutputTaskParser *> &parsers);
|
||||
IOutputParser *outputParser() const;
|
||||
|
||||
void emitFaultyConfigurationMessage();
|
||||
|
||||
protected:
|
||||
AbstractProcessStep(BuildStepList *bsl, Core::Id id);
|
||||
~AbstractProcessStep() override;
|
||||
bool init() override;
|
||||
void setupOutputFormatter(Utils::OutputFormatter *formatter) override;
|
||||
void doRun() override;
|
||||
void setLowPriority();
|
||||
virtual void finish(bool success);
|
||||
|
@@ -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
|
@@ -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
|
@@ -46,6 +46,7 @@
|
||||
#include <coreplugin/progressmanager/futureprogress.h>
|
||||
#include <coreplugin/progressmanager/progressmanager.h>
|
||||
#include <extensionsystem/pluginmanager.h>
|
||||
#include <utils/outputformatter.h>
|
||||
#include <utils/runextensions.h>
|
||||
#include <utils/stringutils.h>
|
||||
|
||||
@@ -680,6 +681,7 @@ void BuildManager::nextStep()
|
||||
}
|
||||
|
||||
static const auto finishedHandler = [](bool success) {
|
||||
d->m_outputWindow->outputFormatter()->flush();
|
||||
d->m_lastStepSucceeded = success;
|
||||
disconnect(d->m_currentBuildStep, nullptr, instance(), nullptr);
|
||||
BuildManager::nextBuildQueue();
|
||||
@@ -688,6 +690,8 @@ void BuildManager::nextStep()
|
||||
Qt::QueuedConnection);
|
||||
connect(d->m_currentBuildStep, &BuildStep::progress,
|
||||
instance(), &BuildManager::progressChanged);
|
||||
d->m_outputWindow->outputFormatter()->reset();
|
||||
d->m_currentBuildStep->setupOutputFormatter(d->m_outputWindow->outputFormatter());
|
||||
d->m_currentBuildStep->run();
|
||||
} else {
|
||||
d->m_running = false;
|
||||
|
@@ -36,6 +36,8 @@
|
||||
#include <coreplugin/variablechooser.h>
|
||||
|
||||
#include <utils/algorithm.h>
|
||||
#include <utils/fileinprojectfinder.h>
|
||||
#include <utils/outputformatter.h>
|
||||
#include <utils/qtcassert.h>
|
||||
#include <utils/runextensions.h>
|
||||
|
||||
@@ -254,6 +256,14 @@ QString BuildStep::fallbackWorkingDirectory() const
|
||||
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)
|
||||
{
|
||||
fi.reportResult(success);
|
||||
|
@@ -44,6 +44,7 @@ namespace Utils {
|
||||
class Environment;
|
||||
class FilePath;
|
||||
class MacroExpander;
|
||||
class OutputFormatter;
|
||||
} // Utils
|
||||
|
||||
namespace ProjectExplorer {
|
||||
@@ -91,6 +92,8 @@ public:
|
||||
Utils::MacroExpander *macroExpander() const;
|
||||
QString fallbackWorkingDirectory() const;
|
||||
|
||||
virtual void setupOutputFormatter(Utils::OutputFormatter *formatter);
|
||||
|
||||
enum class OutputFormat {
|
||||
Stdout, Stderr, // These are for forwarded output from external tools
|
||||
NormalMessage, ErrorMessage // These are for messages from Creator itself
|
||||
@@ -117,8 +120,8 @@ public:
|
||||
|
||||
signals:
|
||||
/// Adds a \p task to the Issues pane.
|
||||
/// Do note that for linking compile output with tasks, you should first emit the task
|
||||
/// and then emit the output. \p linkedOutput lines will be linked. And the last \p skipLines will
|
||||
/// Do note that for linking compile output with tasks, you should first emit the output
|
||||
/// and then emit the task. \p linkedOutput lines will be linked. And the last \p skipLines will
|
||||
/// be skipped.
|
||||
void addTask(const ProjectExplorer::Task &task, int linkedOutputLines = 0, int skipLines = 0);
|
||||
|
||||
|
@@ -55,12 +55,12 @@ ClangParser::ClangParser() :
|
||||
setObjectName(QLatin1String("ClangParser"));
|
||||
}
|
||||
|
||||
QList<OutputTaskParser *> ClangParser::clangParserSuite()
|
||||
QList<OutputLineParser *> ClangParser::clangParserSuite()
|
||||
{
|
||||
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)
|
||||
return Status::NotHandled;
|
||||
@@ -82,11 +82,12 @@ OutputTaskParser::Status ClangParser::handleLine(const QString &line, OutputForm
|
||||
match = m_inLineRegExp.match(lne);
|
||||
if (match.hasMatch()) {
|
||||
m_expectSnippet = true;
|
||||
newTask(CompileTask(Task::Unknown,
|
||||
lne.trimmed(),
|
||||
absoluteFilePath(FilePath::fromUserInput(match.captured(2))),
|
||||
match.captured(3).toInt() /* line */));
|
||||
return Status::InProgress;
|
||||
const FilePath filePath = absoluteFilePath(FilePath::fromUserInput(match.captured(2)));
|
||||
const int lineNo = match.captured(3).toInt();
|
||||
LinkSpecs linkSpecs;
|
||||
addLinkSpecForAbsoluteFilePath(linkSpecs, filePath, lineNo, match, 2);
|
||||
newTask(CompileTask(Task::Unknown, lne.trimmed(), filePath, lineNo));
|
||||
return {Status::InProgress, linkSpecs};
|
||||
}
|
||||
|
||||
match = m_messageRegExp.match(lne);
|
||||
@@ -96,10 +97,10 @@ OutputTaskParser::Status ClangParser::handleLine(const QString &line, OutputForm
|
||||
int lineNo = match.captured(4).toInt(&ok);
|
||||
if (!ok)
|
||||
lineNo = match.captured(5).toInt(&ok);
|
||||
newTask(CompileTask(taskType(match.captured(7)),
|
||||
match.captured(8),
|
||||
absoluteFilePath(FilePath::fromUserInput(match.captured(1))),
|
||||
lineNo));
|
||||
const FilePath filePath = absoluteFilePath(FilePath::fromUserInput(match.captured(1)));
|
||||
LinkSpecs linkSpecs;
|
||||
addLinkSpecForAbsoluteFilePath(linkSpecs, filePath, lineNo, match, 1);
|
||||
newTask(CompileTask(taskType(match.captured(7)), match.captured(8), filePath, lineNo));
|
||||
return Status::InProgress;
|
||||
}
|
||||
|
||||
@@ -255,8 +256,7 @@ void ProjectExplorerPlugin::testClangOutputParser_data()
|
||||
<< (Tasks()
|
||||
<< CompileTask(Task::Unknown,
|
||||
"Note: No relevant classes found. No output generated.",
|
||||
FilePath::fromUserInput("/home/qtwebkithelpviewer.h"),
|
||||
0))
|
||||
FilePath::fromUserInput("/home/qtwebkithelpviewer.h")))
|
||||
<< QString();
|
||||
}
|
||||
|
||||
|
@@ -39,12 +39,12 @@ class PROJECTEXPLORER_EXPORT ClangParser : public ProjectExplorer::GccParser
|
||||
public:
|
||||
ClangParser();
|
||||
|
||||
static QList<OutputTaskParser *> clangParserSuite();
|
||||
static QList<Utils::OutputLineParser *> clangParserSuite();
|
||||
|
||||
static Core::Id id();
|
||||
|
||||
private:
|
||||
Status handleLine(const QString &line, Utils::OutputFormat type) override;
|
||||
Result handleLine(const QString &line, Utils::OutputFormat type) override;
|
||||
|
||||
QRegularExpression m_commandRegExp;
|
||||
QRegularExpression m_inLineRegExp;
|
||||
|
@@ -24,12 +24,14 @@
|
||||
****************************************************************************/
|
||||
|
||||
#include "compileoutputwindow.h"
|
||||
|
||||
#include "buildmanager.h"
|
||||
#include "showoutputtaskhandler.h"
|
||||
#include "task.h"
|
||||
#include "ioutputparser.h"
|
||||
#include "projectexplorer.h"
|
||||
#include "projectexplorericons.h"
|
||||
#include "projectexplorersettings.h"
|
||||
#include "showoutputtaskhandler.h"
|
||||
#include "task.h"
|
||||
#include "taskhub.h"
|
||||
|
||||
#include <coreplugin/outputwindow.h>
|
||||
@@ -40,7 +42,8 @@
|
||||
#include <texteditor/texteditorsettings.h>
|
||||
#include <texteditor/fontsettings.h>
|
||||
#include <texteditor/behaviorsettings.h>
|
||||
#include <utils/outputformat.h>
|
||||
#include <utils/algorithm.h>
|
||||
#include <utils/outputformatter.h>
|
||||
#include <utils/proxyaction.h>
|
||||
#include <utils/theme/theme.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 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) :
|
||||
m_cancelBuildButton(new QToolButton),
|
||||
m_settingsButton(new QToolButton)
|
||||
{
|
||||
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->setWindowIcon(Icons::WINDOW.icon());
|
||||
m_outputWindow->setReadOnly(true);
|
||||
m_outputWindow->setUndoRedoEnabled(false);
|
||||
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,
|
||||
// otherwise the highlight for searching is too light
|
||||
QPalette p = m_outputWindow->palette();
|
||||
@@ -254,7 +212,6 @@ void CompileOutputWindow::appendText(const QString &text, BuildStep::OutputForma
|
||||
void CompileOutputWindow::clearContents()
|
||||
{
|
||||
m_outputWindow->clear();
|
||||
m_outputWindow->clearTasks();
|
||||
m_taskPositions.clear();
|
||||
}
|
||||
|
||||
@@ -287,22 +244,17 @@ bool CompileOutputWindow::canNavigate() const
|
||||
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)
|
||||
return;
|
||||
const int charNumber = m_outputWindow->document()->characterCount();
|
||||
if (charNumber > m_outputWindow->maxCharCount())
|
||||
return;
|
||||
|
||||
const int blocknumber = m_outputWindow->document()->blockCount();
|
||||
const int startLine = blocknumber - linkedOutputLines + 1 - skipLines;
|
||||
const int endLine = blocknumber - skipLines;
|
||||
const int blocknumber = m_outputWindow->document()->blockCount() - offset - 1;
|
||||
const int firstLine = blocknumber - linkedOutputLines - skipLines;
|
||||
const int lastLine = firstLine + linkedOutputLines - 1;
|
||||
|
||||
m_taskPositions.insert(task.taskId, qMakePair(startLine, endLine));
|
||||
|
||||
for (int i = startLine; i <= endLine; ++i)
|
||||
m_outputWindow->addTask(task, i);
|
||||
m_taskPositions.insert(task.taskId, qMakePair(firstLine, lastLine));
|
||||
}
|
||||
|
||||
bool CompileOutputWindow::knowsPositionOf(const Task &task)
|
||||
@@ -340,6 +292,11 @@ void CompileOutputWindow::setSettings(const CompileOutputSettings &settings)
|
||||
updateFromSettings();
|
||||
}
|
||||
|
||||
Utils::OutputFormatter *CompileOutputWindow::outputFormatter() const
|
||||
{
|
||||
return m_outputWindow->outputFormatter();
|
||||
}
|
||||
|
||||
void CompileOutputWindow::updateFilter()
|
||||
{
|
||||
m_outputWindow->updateFilterProperties(filterText(), filterCaseSensitivity(),
|
||||
@@ -415,5 +372,3 @@ CompileOutputSettingsPage::CompileOutputSettingsPage()
|
||||
|
||||
} // Internal
|
||||
} // ProjectExplorer
|
||||
|
||||
#include "compileoutputwindow.moc"
|
||||
|
@@ -37,6 +37,9 @@ QT_BEGIN_NAMESPACE
|
||||
class QToolButton;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
namespace Core { class OutputWindow; }
|
||||
namespace Utils { class OutputFormatter; }
|
||||
|
||||
namespace ProjectExplorer {
|
||||
class Task;
|
||||
|
||||
@@ -70,7 +73,7 @@ public:
|
||||
|
||||
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);
|
||||
void showPositionOf(const Task &task);
|
||||
|
||||
@@ -79,6 +82,8 @@ public:
|
||||
const CompileOutputSettings &settings() const { return m_settings; }
|
||||
void setSettings(const CompileOutputSettings &settings);
|
||||
|
||||
Utils::OutputFormatter *outputFormatter() const;
|
||||
|
||||
private:
|
||||
void updateFilter() override;
|
||||
|
||||
@@ -86,7 +91,7 @@ private:
|
||||
void storeSettings() const;
|
||||
void updateFromSettings();
|
||||
|
||||
CompileOutputTextEdit *m_outputWindow;
|
||||
Core::OutputWindow *m_outputWindow;
|
||||
QHash<unsigned int, QPair<int, int>> m_taskPositions;
|
||||
ShowOutputTaskHandler *m_handler;
|
||||
QToolButton *m_cancelBuildButton;
|
||||
|
@@ -129,45 +129,51 @@ Core::Id CustomParser::id()
|
||||
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
|
||||
? CustomParserExpression::ParseStdErrChannel
|
||||
: CustomParserExpression::ParseStdOutChannel;
|
||||
if (parseLine(line, channel))
|
||||
return Status::Done;
|
||||
return Status::NotHandled;
|
||||
return parseLine(line, channel);
|
||||
}
|
||||
|
||||
bool CustomParser::hasMatch(const QString &line, CustomParserExpression::CustomParserChannel channel,
|
||||
const CustomParserExpression &expression, Task::TaskType taskType)
|
||||
OutputLineParser::Result CustomParser::hasMatch(
|
||||
const QString &line,
|
||||
CustomParserExpression::CustomParserChannel channel,
|
||||
const CustomParserExpression &expression,
|
||||
Task::TaskType taskType
|
||||
)
|
||||
{
|
||||
if (!(channel & expression.channel()))
|
||||
return false;
|
||||
return Status::NotHandled;
|
||||
|
||||
if (expression.pattern().isEmpty())
|
||||
return false;
|
||||
return Status::NotHandled;
|
||||
|
||||
const QRegularExpressionMatch match = expression.match(line);
|
||||
if (!match.hasMatch())
|
||||
return false;
|
||||
return Status::NotHandled;
|
||||
|
||||
const FilePath fileName = absoluteFilePath(FilePath::fromString(
|
||||
match.captured(expression.fileNameCap())));
|
||||
const int lineNumber = match.captured(expression.lineNumberCap()).toInt();
|
||||
const QString message = match.captured(expression.messageCap());
|
||||
|
||||
emit addTask(CompileTask(taskType, message, fileName, lineNumber), 1);
|
||||
return true;
|
||||
LinkSpecs linkSpecs;
|
||||
addLinkSpecForAbsoluteFilePath(linkSpecs, fileName, lineNumber, match,
|
||||
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();
|
||||
|
||||
if (hasMatch(line, channel, m_error, Task::Error))
|
||||
return true;
|
||||
|
||||
const Result res = hasMatch(line, channel, m_error, Task::Error);
|
||||
if (res.status != Status::NotHandled)
|
||||
return res;
|
||||
return hasMatch(line, channel, m_warning, Task::Warning);
|
||||
}
|
||||
|
||||
|
@@ -91,11 +91,11 @@ public:
|
||||
static Core::Id id();
|
||||
|
||||
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,
|
||||
const CustomParserExpression &expression, Task::TaskType taskType);
|
||||
bool parseLine(const QString &rawLine, CustomParserExpression::CustomParserChannel channel);
|
||||
Result hasMatch(const QString &line, CustomParserExpression::CustomParserChannel channel,
|
||||
const CustomParserExpression &expression, Task::TaskType taskType);
|
||||
Result parseLine(const QString &rawLine, CustomParserExpression::CustomParserChannel channel);
|
||||
|
||||
CustomParserExpression m_error;
|
||||
CustomParserExpression m_warning;
|
||||
|
@@ -196,7 +196,7 @@ QStringList CustomToolChain::suggestedMkspecList() const
|
||||
return m_mkspecs;
|
||||
}
|
||||
|
||||
QList<OutputTaskParser *> CustomToolChain::createOutputParsers() const
|
||||
QList<Utils::OutputLineParser *> CustomToolChain::createOutputParsers() const
|
||||
{
|
||||
if (m_outputParserId == GccParser::id())
|
||||
return GccParser::gccParserSuite();
|
||||
|
@@ -84,7 +84,7 @@ public:
|
||||
const Utils::Environment &env) const override;
|
||||
void addToEnvironment(Utils::Environment &env) const override;
|
||||
QStringList suggestedMkspecList() const override;
|
||||
QList<OutputTaskParser *> createOutputParsers() const override;
|
||||
QList<Utils::OutputLineParser *> createOutputParsers() const override;
|
||||
QStringList headerPathsList() const;
|
||||
void setHeaderPaths(const QStringList &list);
|
||||
|
||||
|
@@ -66,7 +66,7 @@ Core::Id GccParser::id()
|
||||
return Core::Id("ProjectExplorer.OutputParser.Gcc");
|
||||
}
|
||||
|
||||
QList<OutputTaskParser *> GccParser::gccParserSuite()
|
||||
QList<OutputLineParser *> GccParser::gccParserSuite()
|
||||
{
|
||||
return {new GccParser, new Internal::LldParser, new LdParser};
|
||||
}
|
||||
@@ -84,7 +84,7 @@ void GccParser::flush()
|
||||
return;
|
||||
Task t = m_currentTask;
|
||||
m_currentTask.clear();
|
||||
emit addTask(t, m_lines, 1);
|
||||
scheduleTask(t, m_lines, 1);
|
||||
m_lines = 0;
|
||||
}
|
||||
|
||||
@@ -107,11 +107,9 @@ void GccParser::amendDescription(const QString &desc, bool monospaced)
|
||||
return;
|
||||
}
|
||||
|
||||
OutputTaskParser::Status GccParser::handleLine(const QString &line, OutputFormat type)
|
||||
OutputLineParser::Result GccParser::handleLine(const QString &line, OutputFormat type)
|
||||
{
|
||||
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();
|
||||
return Status::NotHandled;
|
||||
}
|
||||
@@ -146,7 +144,6 @@ OutputTaskParser::Status GccParser::handleLine(const QString &line, OutputFormat
|
||||
|
||||
match = m_regExp.match(lne);
|
||||
if (match.hasMatch()) {
|
||||
Utils::FilePath filename = Utils::FilePath::fromUserInput(match.captured(1));
|
||||
int lineno = match.captured(3).toInt();
|
||||
Task::TaskType type = Task::Unknown;
|
||||
QString description = match.captured(8);
|
||||
@@ -161,17 +158,21 @@ OutputTaskParser::Status GccParser::handleLine(const QString &line, OutputFormat
|
||||
if (match.captured(5).startsWith(QLatin1Char('#')))
|
||||
description = match.captured(5) + description;
|
||||
|
||||
newTask(CompileTask(type, description, absoluteFilePath(filename), lineno));
|
||||
return Status::InProgress;
|
||||
const FilePath filePath = absoluteFilePath(FilePath::fromUserInput(match.captured(1)));
|
||||
LinkSpecs linkSpecs;
|
||||
addLinkSpecForAbsoluteFilePath(linkSpecs, filePath, lineno, match, 1);
|
||||
newTask(CompileTask(type, description, filePath, lineno));
|
||||
return {Status::InProgress, linkSpecs};
|
||||
}
|
||||
|
||||
match = m_regExpIncluded.match(lne);
|
||||
if (match.hasMatch()) {
|
||||
newTask(CompileTask(Task::Unknown,
|
||||
lne.trimmed() /* description */,
|
||||
absoluteFilePath(Utils::FilePath::fromUserInput(match.captured(1))),
|
||||
match.captured(3).toInt() /* linenumber */));
|
||||
return Status::InProgress;
|
||||
const FilePath filePath = absoluteFilePath(FilePath::fromUserInput(match.captured(1)));
|
||||
const int lineNo = match.captured(3).toInt();
|
||||
LinkSpecs linkSpecs;
|
||||
addLinkSpecForAbsoluteFilePath(linkSpecs, filePath, lineNo, match, 1);
|
||||
newTask(CompileTask(Task::Unknown, lne.trimmed() /* description */, filePath, lineNo));
|
||||
return {Status::InProgress, linkSpecs};
|
||||
} else if (lne.startsWith(' ') && !m_currentTask.isNull()) {
|
||||
amendDescription(lne, true);
|
||||
return Status::InProgress;
|
||||
@@ -681,8 +682,7 @@ void ProjectExplorerPlugin::testGccOutputParsers_data()
|
||||
<< (Tasks()
|
||||
<< CompileTask(Task::Unknown,
|
||||
"In file included from <command-line>:0:0:",
|
||||
FilePath::fromUserInput("<command-line>"),
|
||||
0)
|
||||
FilePath::fromUserInput("<command-line>"))
|
||||
<< CompileTask(Task::Warning,
|
||||
"\"STUPID_DEFINE\" redefined",
|
||||
FilePath::fromUserInput("./mw.h"),
|
||||
@@ -1009,8 +1009,7 @@ void ProjectExplorerPlugin::testGccOutputParsers_data()
|
||||
<< (Tasks()
|
||||
<< CompileTask(Task::Unknown,
|
||||
"Note: No relevant classes found. No output generated.",
|
||||
FilePath::fromUserInput("/home/qtwebkithelpviewer.h"),
|
||||
0))
|
||||
FilePath::fromUserInput("/home/qtwebkithelpviewer.h")))
|
||||
<< QString();
|
||||
|
||||
QTest::newRow("GCC 9 output")
|
||||
|
@@ -42,7 +42,7 @@ public:
|
||||
|
||||
static Core::Id id();
|
||||
|
||||
static QList<OutputTaskParser *> gccParserSuite();
|
||||
static QList<OutputLineParser *> gccParserSuite();
|
||||
|
||||
protected:
|
||||
void newTask(const Task &task);
|
||||
@@ -51,7 +51,7 @@ protected:
|
||||
void amendDescription(const QString &desc, bool monospaced);
|
||||
|
||||
private:
|
||||
Status handleLine(const QString &line, Utils::OutputFormat type) override;
|
||||
Result handleLine(const QString &line, Utils::OutputFormat type) override;
|
||||
|
||||
QRegularExpression m_regExp;
|
||||
QRegularExpression m_regExpIncluded;
|
||||
|
@@ -731,7 +731,7 @@ FilePath GccToolChain::makeCommand(const Environment &environment) const
|
||||
return tmp.isEmpty() ? FilePath::fromString("make") : tmp;
|
||||
}
|
||||
|
||||
QList<OutputTaskParser *> GccToolChain::createOutputParsers() const
|
||||
QList<OutputLineParser *> GccToolChain::createOutputParsers() const
|
||||
{
|
||||
return GccParser::gccParserSuite();
|
||||
}
|
||||
@@ -1628,7 +1628,7 @@ LanguageExtensions ClangToolChain::defaultLanguageExtensions() const
|
||||
return LanguageExtension::Gnu;
|
||||
}
|
||||
|
||||
QList<OutputTaskParser *> ClangToolChain::createOutputParsers() const
|
||||
QList<OutputLineParser *> ClangToolChain::createOutputParsers() const
|
||||
{
|
||||
return ClangParser::clangParserSuite();
|
||||
}
|
||||
@@ -1898,7 +1898,7 @@ LanguageExtensions LinuxIccToolChain::languageExtensions(const QStringList &cxxf
|
||||
return extensions;
|
||||
}
|
||||
|
||||
QList<OutputTaskParser *> LinuxIccToolChain::createOutputParsers() const
|
||||
QList<OutputLineParser *> LinuxIccToolChain::createOutputParsers() const
|
||||
{
|
||||
return LinuxIccParser::iccParserSuite();
|
||||
}
|
||||
|
@@ -94,7 +94,7 @@ public:
|
||||
void addToEnvironment(Utils::Environment &env) const override;
|
||||
Utils::FilePath makeCommand(const Utils::Environment &environment) const override;
|
||||
QStringList suggestedMkspecList() const override;
|
||||
QList<OutputTaskParser *> createOutputParsers() const override;
|
||||
QList<Utils::OutputLineParser *> createOutputParsers() const override;
|
||||
|
||||
QVariantMap toMap() const override;
|
||||
bool fromMap(const QVariantMap &data) override;
|
||||
@@ -226,7 +226,7 @@ public:
|
||||
Utils::LanguageExtensions languageExtensions(const QStringList &cxxflags) const override;
|
||||
Utils::WarningFlags warningFlags(const QStringList &cflags) const override;
|
||||
|
||||
QList<OutputTaskParser *> createOutputParsers() const override;
|
||||
QList<Utils::OutputLineParser *> createOutputParsers() const override;
|
||||
|
||||
QStringList suggestedMkspecList() const override;
|
||||
void addToEnvironment(Utils::Environment &env) const override;
|
||||
@@ -286,7 +286,7 @@ class PROJECTEXPLORER_EXPORT LinuxIccToolChain : public GccToolChain
|
||||
|
||||
public:
|
||||
Utils::LanguageExtensions languageExtensions(const QStringList &cxxflags) const override;
|
||||
QList<OutputTaskParser *> createOutputParsers() const override;
|
||||
QList<Utils::OutputLineParser *> createOutputParsers() const override;
|
||||
|
||||
QStringList suggestedMkspecList() const override;
|
||||
|
||||
|
@@ -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.
|
||||
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);
|
||||
if (type == StdOutFormat) {
|
||||
@@ -119,19 +119,21 @@ OutputTaskParser::Status GnuMakeParser::handleLine(const QString &line, OutputFo
|
||||
}
|
||||
QRegularExpressionMatch match = m_errorInMakefile.match(lne);
|
||||
if (match.hasMatch()) {
|
||||
Result res = parseDescription(match.captured(5));
|
||||
ProjectExplorer::Result res = parseDescription(match.captured(5));
|
||||
if (res.isFatal)
|
||||
++m_fatalErrorCount;
|
||||
LinkSpecs linkSpecs;
|
||||
if (!m_suppressIssues) {
|
||||
emitTask(BuildSystemTask(res.type, res.description,
|
||||
absoluteFilePath(FilePath::fromUserInput(match.captured(1))),
|
||||
match.captured(4).toInt() /* line */));
|
||||
const FilePath file = absoluteFilePath(FilePath::fromUserInput(match.captured(1)));
|
||||
const int lineNo = match.captured(4).toInt();
|
||||
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);
|
||||
if (match.hasMatch()) {
|
||||
Result res = parseDescription(match.captured(6));
|
||||
ProjectExplorer::Result res = parseDescription(match.captured(6));
|
||||
if (res.isFatal)
|
||||
++m_fatalErrorCount;
|
||||
if (!m_suppressIssues)
|
||||
|
@@ -40,7 +40,7 @@ public:
|
||||
explicit GnuMakeParser();
|
||||
|
||||
private:
|
||||
Status handleLine(const QString &line, Utils::OutputFormat type) override;
|
||||
Result handleLine(const QString &line, Utils::OutputFormat type) override;
|
||||
bool hasFatalErrors() const override;
|
||||
|
||||
void emitTask(const ProjectExplorer::Task &task);
|
||||
|
@@ -24,15 +24,9 @@
|
||||
****************************************************************************/
|
||||
|
||||
#include "ioutputparser.h"
|
||||
|
||||
#include "task.h"
|
||||
|
||||
#include <utils/algorithm.h>
|
||||
#include <utils/fileinprojectfinder.h>
|
||||
#include <utils/synchronousprocess.h>
|
||||
|
||||
#include <QDir>
|
||||
#include <QFileInfo>
|
||||
#include <QPointer>
|
||||
#include "taskhub.h"
|
||||
|
||||
|
||||
/*!
|
||||
@@ -76,296 +70,32 @@ namespace ProjectExplorer {
|
||||
class OutputTaskParser::Private
|
||||
{
|
||||
public:
|
||||
Utils::FilePaths searchDirs;
|
||||
Utils::FileInProjectFinder *fileFinder = nullptr;
|
||||
QPointer<const OutputTaskParser> redirectionDetector;
|
||||
bool skipFileExistsCheck = false;
|
||||
QList<TaskInfo> scheduledTasks;
|
||||
};
|
||||
|
||||
OutputTaskParser::OutputTaskParser() : d(new Private)
|
||||
{
|
||||
}
|
||||
OutputTaskParser::OutputTaskParser() : d(new Private) { }
|
||||
|
||||
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);
|
||||
|
||||
// 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);
|
||||
TaskInfo ts(task, outputLines, skippedLines);
|
||||
if (ts.task.type == Task::Error && demoteErrorsToWarnings())
|
||||
ts.task.type = Task::Warning;
|
||||
d->scheduledTasks << ts;
|
||||
QTC_CHECK(d->scheduledTasks.size() <= 2);
|
||||
}
|
||||
|
||||
const Utils::FilePaths OutputTaskParser::searchDirectories() const
|
||||
void OutputTaskParser::runPostPrintActions()
|
||||
{
|
||||
return d->searchDirs;
|
||||
}
|
||||
|
||||
// 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;
|
||||
for (const TaskInfo &t : qAsConst(d->scheduledTasks))
|
||||
TaskHub::addTask(t.task);
|
||||
d->scheduledTasks.clear();
|
||||
}
|
||||
|
||||
} // namespace ProjectExplorer
|
||||
|
@@ -28,103 +28,38 @@
|
||||
#include "projectexplorer_export.h"
|
||||
#include "buildstep.h"
|
||||
|
||||
#include <utils/fileutils.h>
|
||||
#include <utils/outputformat.h>
|
||||
#include <utils/outputformatter.h>
|
||||
|
||||
#include <functional>
|
||||
|
||||
namespace Utils { class FileInProjectFinder; }
|
||||
|
||||
namespace ProjectExplorer {
|
||||
class Task;
|
||||
|
||||
class PROJECTEXPLORER_EXPORT OutputTaskParser : public QObject
|
||||
class PROJECTEXPLORER_EXPORT OutputTaskParser : public Utils::OutputLineParser
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
OutputTaskParser();
|
||||
~OutputTaskParser() override;
|
||||
|
||||
void addSearchDir(const Utils::FilePath &dir);
|
||||
void dropSearchDir(const Utils::FilePath &dir);
|
||||
const Utils::FilePaths searchDirectories() const;
|
||||
|
||||
enum class Status { Done, InProgress, NotHandled };
|
||||
virtual Status handleLine(const QString &line, Utils::OutputFormat type) = 0;
|
||||
|
||||
virtual bool hasFatalErrors() const { return false; }
|
||||
virtual void flush() {}
|
||||
|
||||
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);
|
||||
class TaskInfo
|
||||
{
|
||||
public:
|
||||
TaskInfo(const Task &t, int l, int s) : task(t), linkedLines(l), skippedLines(s) {}
|
||||
Task task;
|
||||
int linkedLines = 0;
|
||||
int skippedLines = 0;
|
||||
};
|
||||
const QList<TaskInfo> taskInfo() const;
|
||||
|
||||
protected:
|
||||
static QString rightTrimmed(const QString &in);
|
||||
Utils::FilePath absoluteFilePath(const Utils::FilePath &filePath);
|
||||
void scheduleTask(const Task &task, int outputLines, int skippedLines = 0);
|
||||
|
||||
private:
|
||||
void runPostPrintActions() override;
|
||||
|
||||
class Private;
|
||||
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
|
||||
|
@@ -559,9 +559,9 @@ void Kit::addToEnvironment(Environment &env) const
|
||||
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())
|
||||
parsers << aspect->createOutputParsers(this);
|
||||
return parsers;
|
||||
|
@@ -38,10 +38,10 @@
|
||||
namespace Utils {
|
||||
class Environment;
|
||||
class MacroExpander;
|
||||
class OutputLineParser;
|
||||
} // namespace Utils
|
||||
|
||||
namespace ProjectExplorer {
|
||||
class OutputTaskParser;
|
||||
|
||||
namespace Internal {
|
||||
class KitManagerPrivate;
|
||||
@@ -116,7 +116,7 @@ public:
|
||||
bool isEqual(const Kit *other) 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;
|
||||
Kit *clone(bool keepName = false) const;
|
||||
|
@@ -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}) {
|
||||
if (const ToolChain * const tc = toolChain(k, langId))
|
||||
|
@@ -85,7 +85,7 @@ public:
|
||||
|
||||
void addToEnvironment(const Kit *k, Utils::Environment &env) 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;
|
||||
|
||||
static Core::Id id();
|
||||
|
@@ -677,7 +677,7 @@ void KitAspect::addToEnvironment(const Kit *k, Environment &env) const
|
||||
Q_UNUSED(env)
|
||||
}
|
||||
|
||||
QList<OutputTaskParser *> KitAspect::createOutputParsers(const Kit *k) const
|
||||
QList<OutputLineParser *> KitAspect::createOutputParsers(const Kit *k) const
|
||||
{
|
||||
Q_UNUSED(k)
|
||||
return {};
|
||||
|
@@ -42,11 +42,11 @@ namespace Utils {
|
||||
class Environment;
|
||||
class FilePath;
|
||||
class MacroExpander;
|
||||
class OutputLineParser;
|
||||
} // namespace Utils
|
||||
|
||||
namespace ProjectExplorer {
|
||||
class Task;
|
||||
class OutputTaskParser;
|
||||
class KitAspectWidget;
|
||||
class KitManager;
|
||||
|
||||
@@ -91,7 +91,7 @@ public:
|
||||
virtual KitAspectWidget *createConfigWidget(Kit *) const = 0;
|
||||
|
||||
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;
|
||||
|
||||
|
@@ -54,7 +54,7 @@ LdParser::LdParser()
|
||||
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)
|
||||
return Status::NotHandled;
|
||||
@@ -80,20 +80,24 @@ OutputTaskParser::Status LdParser::handleLine(const QString &line, Utils::Output
|
||||
m_incompleteTask.description.append('\n').append(lne);
|
||||
static const QRegularExpression locRegExp(" (?<symbol>\\S+) in (?<file>\\S+)");
|
||||
const QRegularExpressionMatch match = locRegExp.match(lne);
|
||||
if (match.hasMatch())
|
||||
m_incompleteTask.setFile(Utils::FilePath::fromString(match.captured("file")));
|
||||
return Status::InProgress;
|
||||
LinkSpecs linkSpecs;
|
||||
if (match.hasMatch()) {
|
||||
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:")) {
|
||||
emit addTask(CompileTask(Task::Error, lne /* description */), 1);
|
||||
scheduleTask(CompileTask(Task::Error, lne /* description */), 1);
|
||||
return Status::Done;
|
||||
}
|
||||
|
||||
QRegularExpressionMatch match = m_ranlib.match(lne);
|
||||
if (match.hasMatch()) {
|
||||
QString description = match.captured(2);
|
||||
emit addTask(CompileTask(Task::Warning, description), 1);
|
||||
scheduleTask(CompileTask(Task::Warning, description), 1);
|
||||
return Status::Done;
|
||||
}
|
||||
|
||||
@@ -107,7 +111,7 @@ OutputTaskParser::Status LdParser::handleLine(const QString &line, Utils::Output
|
||||
} else if (description.startsWith(QLatin1String("fatal: "))) {
|
||||
description = description.mid(7);
|
||||
}
|
||||
emit addTask(CompileTask(type, description), 1);
|
||||
scheduleTask(CompileTask(type, description), 1);
|
||||
return Status::Done;
|
||||
}
|
||||
|
||||
@@ -117,12 +121,15 @@ OutputTaskParser::Status LdParser::handleLine(const QString &line, Utils::Output
|
||||
int lineno = match.captured(7).toInt(&ok);
|
||||
if (!ok)
|
||||
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);
|
||||
if (!sourceFileName.isEmpty()
|
||||
&& !sourceFileName.startsWith(QLatin1String("(.text"))
|
||||
&& !sourceFileName.startsWith(QLatin1String("(.data"))) {
|
||||
filename = Utils::FilePath::fromUserInput(sourceFileName);
|
||||
filename = absoluteFilePath(Utils::FilePath::fromUserInput(sourceFileName));
|
||||
capIndex = 4;
|
||||
}
|
||||
QString description = match.captured(8).trimmed();
|
||||
Task::TaskType type = Task::Error;
|
||||
@@ -137,8 +144,10 @@ OutputTaskParser::Status LdParser::handleLine(const QString &line, Utils::Output
|
||||
type = Task::Warning;
|
||||
description = description.mid(9);
|
||||
}
|
||||
emit addTask(CompileTask(type, description, absoluteFilePath(filename), lineno), 1);
|
||||
return Status::Done;
|
||||
LinkSpecs linkSpecs;
|
||||
addLinkSpecForAbsoluteFilePath(linkSpecs, filename, lineno, match, capIndex);
|
||||
scheduleTask(CompileTask(type, description, filename, lineno), 1);
|
||||
return {Status::Done, linkSpecs};
|
||||
}
|
||||
|
||||
return Status::NotHandled;
|
||||
@@ -150,5 +159,5 @@ void LdParser::flush()
|
||||
return;
|
||||
const Task t = m_incompleteTask;
|
||||
m_incompleteTask.clear();
|
||||
emit addTask(t);
|
||||
scheduleTask(t, 1);
|
||||
}
|
||||
|
@@ -39,7 +39,7 @@ class LdParser : public ProjectExplorer::OutputTaskParser
|
||||
public:
|
||||
LdParser();
|
||||
private:
|
||||
Status handleLine(const QString &line, Utils::OutputFormat type) override;
|
||||
Result handleLine(const QString &line, Utils::OutputFormat type) override;
|
||||
void flush() override;
|
||||
|
||||
QRegularExpression m_ranlib;
|
||||
|
@@ -65,7 +65,7 @@ LinuxIccParser::LinuxIccParser() :
|
||||
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)
|
||||
return Status::NotHandled;
|
||||
@@ -81,10 +81,11 @@ OutputTaskParser::Status LinuxIccParser::handleLine(const QString &line, OutputF
|
||||
type = Task::Error;
|
||||
else if (category == QLatin1String("warning"))
|
||||
type = Task::Warning;
|
||||
m_temporary = CompileTask(type,
|
||||
m_firstLine.cap(6).trimmed(),
|
||||
absoluteFilePath(Utils::FilePath::fromUserInput(m_firstLine.cap(1))),
|
||||
m_firstLine.cap(2).toInt());
|
||||
const FilePath filePath = absoluteFilePath(FilePath::fromUserInput(m_firstLine.cap(1)));
|
||||
const int lineNo = m_firstLine.cap(2).toInt();
|
||||
LinkSpecs linkSpecs;
|
||||
addLinkSpecForAbsoluteFilePath(linkSpecs, filePath, lineNo, m_firstLine, 1);
|
||||
m_temporary = CompileTask(type, m_firstLine.cap(6).trimmed(), filePath, lineNo);
|
||||
|
||||
m_lines = 1;
|
||||
m_expectFirstLine = false;
|
||||
@@ -107,7 +108,7 @@ OutputTaskParser::Status LinuxIccParser::handleLine(const QString &line, OutputF
|
||||
}
|
||||
if (!m_expectFirstLine && line.trimmed().isEmpty()) { // last Line
|
||||
m_expectFirstLine = true;
|
||||
emit addTask(m_temporary, m_lines);
|
||||
scheduleTask(m_temporary, m_lines);
|
||||
m_temporary = Task();
|
||||
return Status::Done;
|
||||
}
|
||||
@@ -129,7 +130,7 @@ Core::Id LinuxIccParser::id()
|
||||
return Core::Id("ProjectExplorer.OutputParser.Icc");
|
||||
}
|
||||
|
||||
QList<OutputTaskParser *> LinuxIccParser::iccParserSuite()
|
||||
QList<OutputLineParser *> LinuxIccParser::iccParserSuite()
|
||||
{
|
||||
return {new LinuxIccParser, new Internal::LldParser, new LdParser};
|
||||
}
|
||||
@@ -140,7 +141,7 @@ void LinuxIccParser::flush()
|
||||
return;
|
||||
Task t = m_temporary;
|
||||
m_temporary.clear();
|
||||
emit addTask(t, m_lines, 1);
|
||||
scheduleTask(t, m_lines, 1);
|
||||
}
|
||||
|
||||
#ifdef WITH_TESTS
|
||||
@@ -237,7 +238,7 @@ void ProjectExplorerPlugin::testLinuxIccOutputParsers_data()
|
||||
<< (Tasks()
|
||||
<< CompileTask(Task::Unknown,
|
||||
"Note: No relevant classes found. No output generated.",
|
||||
FilePath::fromUserInput("/home/qtwebkithelpviewer.h"), 0))
|
||||
FilePath::fromUserInput("/home/qtwebkithelpviewer.h"), -1))
|
||||
<< QString();
|
||||
}
|
||||
|
||||
|
@@ -41,10 +41,10 @@ public:
|
||||
|
||||
static Core::Id id();
|
||||
|
||||
static QList<OutputTaskParser *> iccParserSuite();
|
||||
static QList<Utils::OutputLineParser *> iccParserSuite();
|
||||
|
||||
private:
|
||||
Status handleLine(const QString &line, Utils::OutputFormat type) override;
|
||||
Result handleLine(const QString &line, Utils::OutputFormat type) override;
|
||||
void flush() override;
|
||||
|
||||
QRegExp m_firstLine;
|
||||
|
@@ -35,14 +35,14 @@
|
||||
namespace ProjectExplorer {
|
||||
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)
|
||||
return Status::NotHandled;
|
||||
|
||||
const QString trimmedLine = rightTrimmed(line);
|
||||
if (trimmedLine.contains("error:") && trimmedLine.contains("lld")) {
|
||||
emit addTask(CompileTask(Task::Error, trimmedLine));
|
||||
scheduleTask(CompileTask(Task::Error, trimmedLine), 1);
|
||||
return Status::Done;
|
||||
}
|
||||
static const QStringList prefixes{">>> referenced by ", ">>> defined at ", ">>> "};
|
||||
@@ -65,11 +65,13 @@ OutputTaskParser::Status LldParser::handleLine(const QString &line, Utils::Outpu
|
||||
else
|
||||
filePathOffset = prefix.length();
|
||||
const int filePathLen = locOffset == -1 ? -1 : locOffset - filePathOffset;
|
||||
const auto file = Utils::FilePath::fromUserInput(
|
||||
trimmedLine.mid(filePathOffset, filePathLen).trimmed());
|
||||
emit addTask(CompileTask(Task::Unknown, trimmedLine.mid(4).trimmed(),
|
||||
absoluteFilePath(file), lineNo));
|
||||
return Status::Done;
|
||||
const auto file = absoluteFilePath(Utils::FilePath::fromUserInput(
|
||||
trimmedLine.mid(filePathOffset, filePathLen).trimmed()));
|
||||
LinkSpecs linkSpecs;
|
||||
addLinkSpecForAbsoluteFilePath(linkSpecs, file, lineNo, filePathOffset, filePathLen);
|
||||
scheduleTask(CompileTask(Task::Unknown, trimmedLine.mid(4).trimmed(),
|
||||
file, lineNo), 1);
|
||||
return {Status::Done, linkSpecs};
|
||||
}
|
||||
return Status::NotHandled;
|
||||
}
|
||||
|
@@ -32,7 +32,7 @@ namespace Internal {
|
||||
|
||||
class LldParser : public OutputTaskParser
|
||||
{
|
||||
Status handleLine(const QString &line, Utils::OutputFormat type) override;
|
||||
Result handleLine(const QString &line, Utils::OutputFormat type) override;
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
|
@@ -102,13 +102,17 @@ bool MakeStep::init()
|
||||
// That is mostly so that rebuild works on an already clean project
|
||||
setIgnoreReturnValue(isClean());
|
||||
|
||||
setOutputParser(new GnuMakeParser());
|
||||
appendOutputParsers(target()->kit()->createOutputParsers());
|
||||
outputParser()->addSearchDir(pp->effectiveWorkingDirectory());
|
||||
|
||||
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)
|
||||
{
|
||||
m_clean = clean;
|
||||
|
@@ -54,6 +54,7 @@ public:
|
||||
void setAvailableBuildTargets(const QStringList &buildTargets);
|
||||
|
||||
bool init() override;
|
||||
void setupOutputFormatter(Utils::OutputFormatter *formatter) override;
|
||||
ProjectExplorer::BuildStepConfigWidget *createConfigWidget() override;
|
||||
bool buildsTarget(const QString &target) const;
|
||||
void setBuildTarget(const QString &target, bool on);
|
||||
|
@@ -105,7 +105,7 @@ Core::Id MsvcParser::id()
|
||||
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) {
|
||||
QRegularExpressionMatch match = m_additionalInfoRegExp.match(line);
|
||||
@@ -137,8 +137,9 @@ OutputTaskParser::Status MsvcParser::handleLine(const QString &line, OutputForma
|
||||
return Status::InProgress;
|
||||
}
|
||||
|
||||
if (processCompileLine(line))
|
||||
return Status::InProgress;
|
||||
const Result res = processCompileLine(line);
|
||||
if (res.status != Status::NotHandled)
|
||||
return res;
|
||||
if (handleNmakeJomMessage(line, &m_lastTask)) {
|
||||
m_lines = 1;
|
||||
return Status::InProgress;
|
||||
@@ -148,17 +149,20 @@ OutputTaskParser::Status MsvcParser::handleLine(const QString &line, OutputForma
|
||||
+ match.captured(4).trimmed();
|
||||
if (!match.captured(1).isEmpty())
|
||||
description.chop(1); // Remove trailing quote
|
||||
m_lastTask = CompileTask(Task::Unknown, description,
|
||||
absoluteFilePath(FilePath::fromUserInput(match.captured(2))),
|
||||
match.captured(3).toInt() /* linenumber */);
|
||||
const FilePath filePath = absoluteFilePath(FilePath::fromUserInput(match.captured(2)));
|
||||
const int lineNo = match.captured(3).toInt();
|
||||
LinkSpecs linkSpecs;
|
||||
addLinkSpecForAbsoluteFilePath(linkSpecs, filePath, lineNo, match, 2);
|
||||
m_lastTask = CompileTask(Task::Unknown, description, filePath, lineNo);
|
||||
m_lines = 1;
|
||||
return Status::InProgress;
|
||||
return {Status::InProgress, linkSpecs};
|
||||
}
|
||||
return Status::NotHandled;
|
||||
}
|
||||
|
||||
if (processCompileLine(line))
|
||||
return Status::InProgress;
|
||||
const Result res = processCompileLine(line);
|
||||
if (res.status != Status::NotHandled)
|
||||
return res;
|
||||
// Jom outputs errors to stderr
|
||||
if (handleNmakeJomMessage(line, &m_lastTask)) {
|
||||
m_lines = 1;
|
||||
@@ -167,20 +171,23 @@ OutputTaskParser::Status MsvcParser::handleLine(const QString &line, OutputForma
|
||||
return Status::NotHandled;
|
||||
}
|
||||
|
||||
bool MsvcParser::processCompileLine(const QString &line)
|
||||
MsvcParser::Result MsvcParser::processCompileLine(const QString &line)
|
||||
{
|
||||
flush();
|
||||
|
||||
QRegularExpressionMatch match = m_compileRegExp.match(line);
|
||||
if (match.hasMatch()) {
|
||||
QPair<FilePath, int> position = parseFileName(match.captured(1));
|
||||
const FilePath filePath = absoluteFilePath(position.first);
|
||||
m_lastTask = CompileTask(taskType(match.captured(2)),
|
||||
match.captured(3) + match.captured(4).trimmed(), // description
|
||||
absoluteFilePath(position.first), position.second);
|
||||
filePath, position.second);
|
||||
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()
|
||||
@@ -190,7 +197,7 @@ void MsvcParser::flush()
|
||||
|
||||
Task t = m_lastTask;
|
||||
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 != '~'; });
|
||||
}
|
||||
|
||||
OutputTaskParser::Status ClangClParser::handleLine(const QString &line, OutputFormat type)
|
||||
OutputLineParser::Result ClangClParser::handleLine(const QString &line, OutputFormat type)
|
||||
{
|
||||
if (type == StdOutFormat) {
|
||||
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(),
|
||||
absoluteFilePath(position.first), position.second);
|
||||
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()) {
|
||||
@@ -278,7 +287,7 @@ OutputTaskParser::Status ClangClParser::handleLine(const QString &line, OutputFo
|
||||
void ClangClParser::flush()
|
||||
{
|
||||
if (!m_lastTask.isNull()) {
|
||||
emit addTask(m_lastTask, m_linkedLines, 1);
|
||||
scheduleTask(m_lastTask, m_linkedLines, 1);
|
||||
m_lastTask.clear();
|
||||
}
|
||||
}
|
||||
|
@@ -43,10 +43,10 @@ public:
|
||||
static Core::Id id();
|
||||
|
||||
private:
|
||||
Status handleLine(const QString &line, Utils::OutputFormat type) override;
|
||||
Result handleLine(const QString &line, Utils::OutputFormat type) override;
|
||||
void flush() override;
|
||||
|
||||
bool processCompileLine(const QString &line);
|
||||
Result processCompileLine(const QString &line);
|
||||
|
||||
QRegularExpression m_compileRegExp;
|
||||
QRegularExpression m_additionalInfoRegExp;
|
||||
@@ -63,7 +63,7 @@ public:
|
||||
ClangClParser();
|
||||
|
||||
private:
|
||||
Status handleLine(const QString &line, Utils::OutputFormat type) override;
|
||||
Result handleLine(const QString &line, Utils::OutputFormat type) override;
|
||||
void flush() override;
|
||||
|
||||
const QRegularExpression m_compileRegExp;
|
||||
|
@@ -1169,7 +1169,7 @@ void MsvcToolChain::rescanForCompiler()
|
||||
});
|
||||
}
|
||||
|
||||
QList<OutputTaskParser *> MsvcToolChain::createOutputParsers() const
|
||||
QList<OutputLineParser *> MsvcToolChain::createOutputParsers() const
|
||||
{
|
||||
return {new MsvcParser};
|
||||
}
|
||||
@@ -1656,7 +1656,7 @@ QStringList ClangClToolChain::suggestedMkspecList() const
|
||||
return {mkspec, "win32-clang-msvc"};
|
||||
}
|
||||
|
||||
QList<OutputTaskParser *> ClangClToolChain::createOutputParsers() const
|
||||
QList<OutputLineParser *> ClangClToolChain::createOutputParsers() const
|
||||
{
|
||||
return {new ClangClParser};
|
||||
}
|
||||
|
@@ -89,7 +89,7 @@ public:
|
||||
|
||||
Utils::FilePath makeCommand(const Utils::Environment &environment) 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 varsBat() const { return m_vcvarsBat; }
|
||||
@@ -174,7 +174,7 @@ public:
|
||||
QStringList suggestedMkspecList() const override;
|
||||
void addToEnvironment(Utils::Environment &env) const override;
|
||||
Utils::FilePath compilerCommand() const override;
|
||||
QList<OutputTaskParser *> createOutputParsers() const override;
|
||||
QList<Utils::OutputLineParser *> createOutputParsers() const override;
|
||||
QVariantMap toMap() const override;
|
||||
bool fromMap(const QVariantMap &data) override;
|
||||
std::unique_ptr<ToolChainConfigWidget> createConfigurationWidget() override;
|
||||
|
@@ -36,18 +36,18 @@ OsParser::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 (Utils::HostOsInfo::isWindowsHost()) {
|
||||
const QString trimmed = line.trimmed();
|
||||
if (trimmed == QLatin1String("The process cannot access the file because it is "
|
||||
"being used by another process.")) {
|
||||
emit addTask(CompileTask(Task::Error, tr(
|
||||
scheduleTask(CompileTask(Task::Error, tr(
|
||||
"The process cannot access the file because it is being used "
|
||||
"by another process.\n"
|
||||
"Please close all running instances of your application before "
|
||||
"starting a build.")));
|
||||
"starting a build.")), 1);
|
||||
m_hasFatalError = true;
|
||||
return Status::Done;
|
||||
}
|
||||
@@ -57,7 +57,7 @@ OutputTaskParser::Status OsParser::handleLine(const QString &line, Utils::Output
|
||||
if (Utils::HostOsInfo::isLinuxHost()) {
|
||||
const QString trimmed = line.trimmed();
|
||||
if (trimmed.contains(QLatin1String(": error while loading shared libraries:"))) {
|
||||
emit addTask(CompileTask(Task::Error, trimmed));
|
||||
scheduleTask(CompileTask(Task::Error, trimmed), 1);
|
||||
return Status::Done;
|
||||
}
|
||||
}
|
||||
|
@@ -41,7 +41,7 @@ public:
|
||||
OsParser();
|
||||
|
||||
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 m_hasFatalError = false;
|
||||
|
@@ -24,7 +24,9 @@
|
||||
****************************************************************************/
|
||||
|
||||
#include "outputparser_test.h"
|
||||
#include "projectexplorer.h"
|
||||
#include "task.h"
|
||||
#include "taskhub.h"
|
||||
|
||||
#if defined(WITH_TESTS)
|
||||
|
||||
@@ -41,11 +43,16 @@ static inline QByteArray msgFileComparisonFail(const Utils::FilePath &f1, const
|
||||
// test functions:
|
||||
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);
|
||||
});
|
||||
}
|
||||
|
||||
OutputParserTester::~OutputParserTester()
|
||||
{
|
||||
TaskHub::instance()->disconnect(this);
|
||||
}
|
||||
|
||||
void OutputParserTester::testParsing(const QString &lines,
|
||||
Channel inputChannel,
|
||||
Tasks tasks,
|
||||
@@ -60,9 +67,9 @@ void OutputParserTester::testParsing(const QString &lines,
|
||||
reset();
|
||||
|
||||
if (inputChannel == STDOUT)
|
||||
handleStdout(lines + '\n');
|
||||
appendMessage(lines + '\n', Utils::StdOutFormat);
|
||||
else
|
||||
handleStderr(lines + '\n');
|
||||
appendMessage(lines + '\n', Utils::StdErrFormat);
|
||||
flush();
|
||||
|
||||
// delete the parser(s) to test
|
||||
@@ -102,7 +109,7 @@ TestTerminator::TestTerminator(OutputParserTester *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'));
|
||||
if (type == Utils::StdOutFormat)
|
||||
@@ -112,6 +119,64 @@ OutputTaskParser::Status TestTerminator::handleLine(const QString &line, Utils::
|
||||
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
|
||||
|
||||
#endif
|
||||
|
@@ -36,7 +36,7 @@ namespace ProjectExplorer {
|
||||
|
||||
class TestTerminator;
|
||||
|
||||
class PROJECTEXPLORER_EXPORT OutputParserTester : public IOutputParser
|
||||
class PROJECTEXPLORER_EXPORT OutputParserTester : public Utils::OutputFormatter
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
@@ -47,6 +47,7 @@ public:
|
||||
};
|
||||
|
||||
OutputParserTester();
|
||||
~OutputParserTester();
|
||||
|
||||
// test functions:
|
||||
void testParsing(const QString &lines, Channel inputChannel,
|
||||
@@ -81,7 +82,7 @@ public:
|
||||
TestTerminator(OutputParserTester *t);
|
||||
|
||||
private:
|
||||
Status handleLine(const QString &line, Utils::OutputFormat type) override;
|
||||
Result handleLine(const QString &line, Utils::OutputFormat type) override;
|
||||
|
||||
OutputParserTester *m_tester = nullptr;
|
||||
};
|
||||
|
@@ -136,13 +136,13 @@ ParseIssuesDialog::~ParseIssuesDialog()
|
||||
}
|
||||
|
||||
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');
|
||||
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) {
|
||||
(parser.get()->*parserFunc)(line + '\n');
|
||||
parser->appendMessage(line + '\n', format);
|
||||
future.setProgressValue(future.progressValue() + 1);
|
||||
if (future.isCanceled())
|
||||
return;
|
||||
@@ -151,17 +151,17 @@ static void parse(QFutureInterface<void> &future, const QString &output,
|
||||
|
||||
void ParseIssuesDialog::accept()
|
||||
{
|
||||
const QList<OutputTaskParser *> lineParsers = d->kitChooser.currentKit()->createOutputParsers();
|
||||
const QList<Utils::OutputLineParser *> lineParsers =
|
||||
d->kitChooser.currentKit()->createOutputParsers();
|
||||
if (lineParsers.isEmpty()) {
|
||||
QMessageBox::critical(this, tr("Cannot Parse"), tr("Cannot parse: The chosen kit does "
|
||||
"not provide an output parser."));
|
||||
return;
|
||||
}
|
||||
std::unique_ptr<IOutputParser> parser(new IOutputParser);
|
||||
std::unique_ptr<Utils::OutputFormatter> parser(new Utils::OutputFormatter);
|
||||
parser->setLineParsers(lineParsers);
|
||||
if (d->clearTasksCheckBox.isChecked())
|
||||
TaskHub::clearTasks();
|
||||
connect(parser.get(), &IOutputParser::addTask, [](const Task &t) { TaskHub::addTask(t); });
|
||||
const QFuture<void> f = Utils::runAsync(&parse, d->compileOutputEdit.toPlainText(),
|
||||
std::move(parser), d->stderrCheckBox.isChecked());
|
||||
Core::ProgressManager::addTask(f, tr("Parsing build output"),
|
||||
|
@@ -35,6 +35,7 @@
|
||||
#include "target.h"
|
||||
|
||||
#include <utils/fileutils.h>
|
||||
#include <utils/outputformatter.h>
|
||||
|
||||
using namespace Utils;
|
||||
|
||||
@@ -53,6 +54,7 @@ public:
|
||||
ProcessStep(BuildStepList *bsl, Core::Id id);
|
||||
|
||||
bool init() final;
|
||||
void setupOutputFormatter(Utils::OutputFormatter *formatter);
|
||||
void setupProcessParameters(ProcessParameters *pp);
|
||||
|
||||
BaseStringAspect *m_command;
|
||||
@@ -100,10 +102,15 @@ ProcessStep::ProcessStep(BuildStepList *bsl, Core::Id id)
|
||||
bool ProcessStep::init()
|
||||
{
|
||||
setupProcessParameters(processParameters());
|
||||
appendOutputParsers(target()->kit()->createOutputParsers());
|
||||
return AbstractProcessStep::init();
|
||||
}
|
||||
|
||||
void ProcessStep::setupOutputFormatter(OutputFormatter *formatter)
|
||||
{
|
||||
formatter->addLineParsers(target()->kit()->createOutputParsers());
|
||||
AbstractProcessStep::setupOutputFormatter(formatter);
|
||||
}
|
||||
|
||||
void ProcessStep::setupProcessParameters(ProcessParameters *pp)
|
||||
{
|
||||
QString workingDirectory = m_workingDirectory->value();
|
||||
|
@@ -11,7 +11,6 @@ HEADERS += projectexplorer.h \
|
||||
abi.h \
|
||||
abiwidget.h \
|
||||
addrunconfigdialog.h \
|
||||
ansifilterparser.h \
|
||||
buildaspects.h \
|
||||
buildinfo.h \
|
||||
buildpropertiessettings.h \
|
||||
@@ -172,7 +171,6 @@ SOURCES += projectexplorer.cpp \
|
||||
abi.cpp \
|
||||
abiwidget.cpp \
|
||||
addrunconfigdialog.cpp \
|
||||
ansifilterparser.cpp \
|
||||
buildaspects.cpp \
|
||||
buildinfo.cpp \
|
||||
buildpropertiessettingspage.cpp \
|
||||
|
@@ -27,7 +27,6 @@ Project {
|
||||
"addrunconfigdialog.cpp", "addrunconfigdialog.h",
|
||||
"allprojectsfilter.cpp", "allprojectsfilter.h",
|
||||
"allprojectsfind.cpp", "allprojectsfind.h",
|
||||
"ansifilterparser.cpp", "ansifilterparser.h",
|
||||
"applicationlauncher.cpp", "applicationlauncher.h",
|
||||
"appoutputpane.cpp", "appoutputpane.h",
|
||||
"baseprojectwizarddialog.cpp", "baseprojectwizarddialog.h",
|
||||
|
@@ -47,6 +47,8 @@
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
|
||||
namespace Utils { class OutputLineParser; }
|
||||
|
||||
namespace ProjectExplorer {
|
||||
|
||||
namespace Internal { class ToolChainPrivate; }
|
||||
@@ -64,7 +66,6 @@ QString languageId(Language l);
|
||||
} // namespace Deprecated
|
||||
|
||||
class Abi;
|
||||
class OutputTaskParser;
|
||||
class ToolChainConfigWidget;
|
||||
class ToolChainFactory;
|
||||
class Kit;
|
||||
@@ -150,7 +151,7 @@ public:
|
||||
Core::Id language() const;
|
||||
|
||||
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;
|
||||
|
||||
|
@@ -324,7 +324,7 @@ public:
|
||||
void addToEnvironment(Environment &env) const override { Q_UNUSED(env) }
|
||||
FilePath makeCommand(const Environment &) const override { return FilePath::fromString("make"); }
|
||||
FilePath compilerCommand() const override { return Utils::FilePath::fromString("/tmp/test/gcc"); }
|
||||
QList<OutputTaskParser *> createOutputParsers() const override { return {}; }
|
||||
QList<OutputLineParser *> createOutputParsers() const override { return {}; }
|
||||
std::unique_ptr<ToolChainConfigWidget> createConfigurationWidget() override { return nullptr; }
|
||||
bool operator ==(const ToolChain &other) const override {
|
||||
if (!ToolChain::operator==(other))
|
||||
|
@@ -52,7 +52,7 @@ XcodebuildParser::XcodebuildParser()
|
||||
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);
|
||||
if (type == StdOutFormat) {
|
||||
@@ -69,12 +69,16 @@ OutputTaskParser::Status XcodebuildParser::handleLine(const QString &line, Outpu
|
||||
return Status::Done;
|
||||
}
|
||||
if (lne.endsWith(QLatin1String(signatureChangeEndsWithPattern))) {
|
||||
const int filePathEndPos = lne.size()
|
||||
- QLatin1String(signatureChangeEndsWithPattern).size();
|
||||
CompileTask task(Task::Warning,
|
||||
tr("Replacing signature"),
|
||||
absoluteFilePath(FilePath::fromString(
|
||||
lne.left(lne.size() - QLatin1String(signatureChangeEndsWithPattern).size()))));
|
||||
emit addTask(task, 1);
|
||||
return Status::Done;
|
||||
lne.left(filePathEndPos))));
|
||||
LinkSpecs linkSpecs;
|
||||
addLinkSpecForAbsoluteFilePath(linkSpecs, task.file, task.line, 0, filePathEndPos);
|
||||
scheduleTask(task, 1);
|
||||
return {Status::Done, linkSpecs};
|
||||
}
|
||||
}
|
||||
return Status::NotHandled;
|
||||
@@ -83,7 +87,7 @@ OutputTaskParser::Status XcodebuildParser::handleLine(const QString &line, Outpu
|
||||
++m_fatalErrorCount;
|
||||
m_xcodeBuildParserState = UnknownXcodebuildState;
|
||||
// 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)
|
||||
return Status::NotHandled;
|
||||
|
@@ -47,7 +47,7 @@ public:
|
||||
XcodebuildParser();
|
||||
|
||||
private:
|
||||
Status handleLine(const QString &line, Utils::OutputFormat type) override;
|
||||
Result handleLine(const QString &line, Utils::OutputFormat type) override;
|
||||
bool hasDetectedRedirection() const override;
|
||||
bool hasFatalErrors() const override { return m_fatalErrorCount > 0; }
|
||||
|
||||
|
@@ -126,12 +126,6 @@ private:
|
||||
return true;
|
||||
}
|
||||
|
||||
void reset() override
|
||||
{
|
||||
m_inTraceBack = false;
|
||||
m_tasks.clear();
|
||||
}
|
||||
|
||||
const QRegularExpression filePattern;
|
||||
QList<Task> m_tasks;
|
||||
bool m_inTraceBack;
|
||||
|
@@ -34,12 +34,12 @@
|
||||
#include <coreplugin/icore.h>
|
||||
#include <coreplugin/variablechooser.h>
|
||||
#include <projectexplorer/buildsteplist.h>
|
||||
#include <projectexplorer/ioutputparser.h>
|
||||
#include <projectexplorer/kit.h>
|
||||
#include <projectexplorer/projectexplorerconstants.h>
|
||||
#include <projectexplorer/target.h>
|
||||
#include <qtsupport/qtversionmanager.h>
|
||||
#include <utils/macroexpander.h>
|
||||
#include <utils/outputformatter.h>
|
||||
#include <utils/pathchooser.h>
|
||||
#include <utils/qtcassert.h>
|
||||
#include <utils/qtcprocess.h>
|
||||
@@ -155,7 +155,6 @@ QbsBuildStep::~QbsBuildStep()
|
||||
doCancel();
|
||||
if (m_session)
|
||||
m_session->disconnect(this);
|
||||
delete m_parser;
|
||||
}
|
||||
|
||||
bool QbsBuildStep::init()
|
||||
@@ -168,11 +167,6 @@ bool QbsBuildStep::init()
|
||||
if (!bc)
|
||||
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_activeFileTags = bc->activeFileTags();
|
||||
m_products = bc->products();
|
||||
@@ -180,6 +174,12 @@ bool QbsBuildStep::init()
|
||||
return true;
|
||||
}
|
||||
|
||||
void QbsBuildStep::setupOutputFormatter(OutputFormatter *formatter)
|
||||
{
|
||||
formatter->addLineParsers(target()->kit()->createOutputParsers());
|
||||
BuildStep::setupOutputFormatter(formatter);
|
||||
}
|
||||
|
||||
void QbsBuildStep::doRun()
|
||||
{
|
||||
// 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,
|
||||
bool success)
|
||||
{
|
||||
Q_UNUSED(workingDir);
|
||||
const bool hasOutput = !stdOut.isEmpty() || !stdErr.isEmpty();
|
||||
if (success && !hasOutput)
|
||||
return;
|
||||
|
||||
if (m_parser)
|
||||
m_parser->addSearchDir(workingDir);
|
||||
emit addOutput(executable.toUserOutput() + ' ' + QtcProcess::joinArgs(arguments),
|
||||
OutputFormat::Stdout);
|
||||
for (const QString &line : stdErr) {
|
||||
if (m_parser)
|
||||
m_parser->handleStderr(line + '\n');
|
||||
for (const QString &line : stdErr)
|
||||
emit addOutput(line, OutputFormat::Stderr);
|
||||
}
|
||||
for (const QString &line : stdOut) {
|
||||
if (m_parser)
|
||||
m_parser->handleStdout(line + '\n');
|
||||
for (const QString &line : 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,
|
||||
const QString &file, int line)
|
||||
{
|
||||
emit addTask(CompileTask(type, message, FilePath::fromString(file), line), 1);
|
||||
emit addOutput(message, OutputFormat::Stdout);
|
||||
emit addTask(CompileTask(type, message, FilePath::fromString(file), line), 1);
|
||||
}
|
||||
|
||||
QString QbsBuildStep::buildVariant() const
|
||||
|
@@ -30,7 +30,6 @@
|
||||
#include <projectexplorer/buildstep.h>
|
||||
#include <projectexplorer/task.h>
|
||||
|
||||
namespace ProjectExplorer { class IOutputParser; }
|
||||
namespace Utils { class FancyLineEdit; }
|
||||
|
||||
namespace QbsProjectManager {
|
||||
@@ -81,6 +80,7 @@ signals:
|
||||
|
||||
private:
|
||||
bool init() override;
|
||||
void setupOutputFormatter(Utils::OutputFormatter *formatter) override;
|
||||
void doRun() override;
|
||||
void doCancel() override;
|
||||
ProjectExplorer::BuildStepConfigWidget *createConfigWidget() override;
|
||||
@@ -134,7 +134,6 @@ private:
|
||||
QString m_currentTask;
|
||||
int m_maxProgress;
|
||||
bool m_lastWasSuccess;
|
||||
ProjectExplorer::IOutputParser *m_parser = nullptr;
|
||||
bool m_parsingProject = false;
|
||||
bool m_parsingAfterBuild = false;
|
||||
|
||||
|
@@ -160,8 +160,8 @@ void QbsCleanStep::handleProgress(int value)
|
||||
|
||||
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 addTask(CompileTask(type, message, Utils::FilePath::fromString(file), line), 1);
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
@@ -202,9 +202,8 @@ void QbsInstallStep::handleProgress(int value)
|
||||
void QbsInstallStep::createTaskAndOutput(Task::TaskType type, const QString &message,
|
||||
const Utils::FilePath &file, int line)
|
||||
{
|
||||
const CompileTask task(type, message, file, line);
|
||||
emit addTask(task, 1);
|
||||
emit addOutput(message, OutputFormat::Stdout);
|
||||
emit addTask(CompileTask(type, message, file, line), 1);
|
||||
}
|
||||
|
||||
void QbsInstallStep::setRemoveFirst(bool rf)
|
||||
|
@@ -164,25 +164,6 @@ bool QmakeMakeStep::init()
|
||||
pp->setCommandLine(makeCmd);
|
||||
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());
|
||||
QTC_ASSERT(rootNode, return false);
|
||||
m_scriptTarget = rootNode->projectType() == ProjectType::ScriptTemplate;
|
||||
@@ -199,6 +180,30 @@ bool QmakeMakeStep::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()
|
||||
{
|
||||
if (m_scriptTarget || m_ignoredNonTopLevelBuild) {
|
||||
|
@@ -52,6 +52,7 @@ public:
|
||||
private:
|
||||
void finish(bool success) override;
|
||||
bool init() override;
|
||||
void setupOutputFormatter(Utils::OutputFormatter *formatter) override;
|
||||
void doRun() override;
|
||||
QStringList displayArguments() const override;
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user