Merge output formatters and output parsers

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

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

View File

@@ -93,6 +93,24 @@ QList<FormattedText> AnsiEscapeCodeHandler::parseText(const FormattedText &input
while (!strippedText.isEmpty()) {
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());

View File

@@ -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;
};

View File

@@ -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 &regex, int capIndex)
{
addLinkSpecForAbsoluteFilePath(linkSpecs, filePath, lineNo, regex.pos(capIndex),
regex.cap(capIndex).length());
}
void OutputLineParser::addLinkSpecForAbsoluteFilePath(OutputLineParser::LinkSpecs &linkSpecs,
const FilePath &filePath, int lineNo, const QRegularExpressionMatch &match,
int capIndex)
{
addLinkSpecForAbsoluteFilePath(linkSpecs, filePath, lineNo, match.capturedStart(capIndex),
match.capturedLength(capIndex));
}
void OutputLineParser::addLinkSpecForAbsoluteFilePath(OutputLineParser::LinkSpecs &linkSpecs,
const FilePath &filePath, int lineNo, const QRegularExpressionMatch &match,
const QString &capName)
{
addLinkSpecForAbsoluteFilePath(linkSpecs, filePath, lineNo, match.capturedStart(capName),
match.capturedLength(capName));
}
QString OutputLineParser::rightTrimmed(const QString &in)
{
int pos = in.length();
for (; pos > 0; --pos) {
if (!in.at(pos - 1).isSpace())
break;
}
return in.mid(0, pos);
}
#ifdef WITH_TESTS
void OutputLineParser::skipFileExistsCheck()
{
d->skipFileExistsCheck = true;
}
#endif
class OutputFormatter::Private
{
public:
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.

View File

@@ -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 &regex, int capIndex);
void addLinkSpecForAbsoluteFilePath(LinkSpecs &linkSpecs, const FilePath &filePath,
int lineNo, const QRegularExpressionMatch &match,
int capIndex);
void addLinkSpecForAbsoluteFilePath(LinkSpecs &linkSpecs, const FilePath &filePath,
int lineNo, const QRegularExpressionMatch &match,
const QString &capName);
signals:
void newSearchDir(const Utils::FilePath &dir);
void searchDirExpired(const Utils::FilePath &dir);
private:
class Private;
Private * const d;
};
namespace Internal { class OutputFormatterPrivate; }
class QTCREATOR_UTILS_EXPORT OutputFormatter : public QObject
{
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;
};

View File

@@ -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);

View File

@@ -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;

View File

@@ -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;

View File

@@ -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};
}

View File

@@ -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;

View File

@@ -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;
}

View File

@@ -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;

View File

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

View File

@@ -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;

View File

@@ -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;
}

View File

@@ -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;

View File

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

View File

@@ -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;

View File

@@ -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;
}

View File

@@ -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;

View File

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

View File

@@ -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;

View File

@@ -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:

View File

@@ -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;

View File

@@ -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;
}

View File

@@ -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 };

View File

@@ -94,17 +94,6 @@ void CMakeProcess::run(const BuildDirParameters &parameters, 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);
});
}

View File

@@ -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;

View File

@@ -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);
}
});

View File

@@ -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;

View File

@@ -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

View File

@@ -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());

View File

@@ -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);

View File

@@ -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;

View File

@@ -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);

View File

@@ -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;

View File

@@ -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()

View File

@@ -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;

View File

@@ -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 {};
}

View File

@@ -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;

View File

@@ -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

View File

@@ -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);
}

View File

@@ -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);

View File

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

View File

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

View File

@@ -46,6 +46,7 @@
#include <coreplugin/progressmanager/futureprogress.h>
#include <coreplugin/progressmanager/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;

View File

@@ -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);

View File

@@ -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);

View File

@@ -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();
}

View File

@@ -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;

View File

@@ -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"

View File

@@ -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;

View File

@@ -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);
}

View File

@@ -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;

View File

@@ -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();

View File

@@ -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);

View File

@@ -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")

View File

@@ -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;

View File

@@ -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();
}

View File

@@ -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;

View File

@@ -100,10 +100,10 @@ void GnuMakeParser::emitTask(const ProjectExplorer::Task &task)
{
if (task.type == Task::Error) // Assume that all make errors will be follow up errors.
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)

View File

@@ -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);

View File

@@ -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

View File

@@ -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

View File

@@ -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;

View File

@@ -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;

View File

@@ -557,7 +557,7 @@ void ToolChainKitAspect::addToMacroExpander(Kit *kit, Utils::MacroExpander *expa
});
}
QList<OutputTaskParser *> ToolChainKitAspect::createOutputParsers(const Kit *k) const
QList<Utils::OutputLineParser *> ToolChainKitAspect::createOutputParsers(const Kit *k) const
{
for (const Core::Id langId : {Constants::CXX_LANGUAGE_ID, Constants::C_LANGUAGE_ID}) {
if (const ToolChain * const tc = toolChain(k, langId))

View File

@@ -85,7 +85,7 @@ public:
void addToEnvironment(const Kit *k, Utils::Environment &env) const override;
void 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();

View File

@@ -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 {};

View File

@@ -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;

View File

@@ -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);
}

View File

@@ -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;

View File

@@ -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();
}

View File

@@ -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;

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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;

View File

@@ -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);

View File

@@ -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();
}
}

View File

@@ -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;

View File

@@ -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};
}

View File

@@ -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;

View File

@@ -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;
}
}

View File

@@ -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;

View File

@@ -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

View File

@@ -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;
};

View File

@@ -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"),

View File

@@ -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();

View File

@@ -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 \

View File

@@ -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",

View File

@@ -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;

View File

@@ -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))

View File

@@ -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;

View File

@@ -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; }

View File

@@ -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;

View File

@@ -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

View File

@@ -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;

View File

@@ -160,8 +160,8 @@ void QbsCleanStep::handleProgress(int value)
void QbsCleanStep::createTaskAndOutput(ProjectExplorer::Task::TaskType type, const QString &message, const QString &file, int line)
{
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);
}
// --------------------------------------------------------------------

View File

@@ -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)

View File

@@ -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) {

View File

@@ -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