Output parsers: Generalize the search directory concept

All parsers can now have search directories, not just the GnuMakeParser.
This allows us to get rid of the "task mangling", removing another
instance where the order of parsers in the chain mattered.

Task-number: QTCREATORBUG-22665
Change-Id: Id0d55522ae6800afd9f50ff36546224b0d8bb382
Reviewed-by: hjk <hjk@qt.io>
This commit is contained in:
Christian Kandeler
2020-04-07 13:49:34 +02:00
parent b22bb5a9a7
commit 7745eacc7a
34 changed files with 140 additions and 249 deletions

View File

@@ -115,7 +115,7 @@ bool AndroidPackageInstallationStep::init()
IOutputParser *parser = target()->kit()->createOutputParser(); IOutputParser *parser = target()->kit()->createOutputParser();
if (parser) if (parser)
appendOutputParser(parser); appendOutputParser(parser);
outputParser()->setWorkingDirectory(pp->effectiveWorkingDirectory()); outputParser()->addSearchDir(pp->effectiveWorkingDirectory());
m_androidDirsToClean.clear(); m_androidDirsToClean.clear();
// don't remove gradle's cache, it takes ages to rebuild it. // don't remove gradle's cache, it takes ages to rebuild it.

View File

@@ -86,7 +86,7 @@ void JavaParser::parse(const QString &line)
CompileTask task(Task::Error, CompileTask task(Task::Error,
m_javaRegExp.cap(4).trimmed(), m_javaRegExp.cap(4).trimmed(),
file /* filename */, absoluteFilePath(file),
lineno); lineno);
emit addTask(task, 1); emit addTask(task, 1);
return; return;

View File

@@ -158,7 +158,7 @@ bool IarParser::parseWarningOrErrorOrFatalErrorDetailsMessage1(const QString &ln
const int lineno = match.captured(LineNumberIndex).toInt(); const int lineno = match.captured(LineNumberIndex).toInt();
const Task::TaskType type = taskType(match.captured(MessageTypeIndex)); const Task::TaskType type = taskType(match.captured(MessageTypeIndex));
// A full description will be received later on next lines. // A full description will be received later on next lines.
newTask(CompileTask(type, {}, fileName, lineno)); newTask(CompileTask(type, {}, absoluteFilePath(fileName), lineno));
const QString firstPart = QString("[%1]: ").arg(match.captured(MessageCodeIndex)); const QString firstPart = QString("[%1]: ").arg(match.captured(MessageCodeIndex));
m_descriptionParts.append(firstPart); m_descriptionParts.append(firstPart);
m_expectDescription = true; m_expectDescription = true;

View File

@@ -106,7 +106,7 @@ bool KeilParser::parseArmWarningOrErrorDetailsMessage(const QString &lne)
const int lineno = match.captured(LineNumberIndex).toInt(); const int lineno = match.captured(LineNumberIndex).toInt();
const Task::TaskType type = taskType(match.captured(MessageTypeIndex)); const Task::TaskType type = taskType(match.captured(MessageTypeIndex));
const QString descr = match.captured(DescriptionIndex); const QString descr = match.captured(DescriptionIndex);
newTask(CompileTask(type, descr, fileName, lineno)); newTask(CompileTask(type, descr, absoluteFilePath(fileName), lineno));
return true; return true;
} }
@@ -139,7 +139,7 @@ bool KeilParser::parseMcs51WarningOrErrorDetailsMessage1(const QString &lne)
match.captured(FilePathIndex)); match.captured(FilePathIndex));
const QString descr = QString("%1: %2").arg(match.captured(MessageCodeIndex), const QString descr = QString("%1: %2").arg(match.captured(MessageCodeIndex),
match.captured(MessageTextIndex)); match.captured(MessageTextIndex));
newTask(CompileTask(type, descr, fileName, lineno)); newTask(CompileTask(type, descr, absoluteFilePath(fileName), lineno));
return true; return true;
} }
@@ -157,7 +157,7 @@ bool KeilParser::parseMcs51WarningOrErrorDetailsMessage2(const QString &lne)
match.captured(FilePathIndex)); match.captured(FilePathIndex));
const QString descr = QString("%1: %2").arg(match.captured(MessageCodeIndex), const QString descr = QString("%1: %2").arg(match.captured(MessageCodeIndex),
match.captured(MessageTextIndex)); match.captured(MessageTextIndex));
newTask(CompileTask(type, descr, fileName, lineno)); newTask(CompileTask(type, descr, absoluteFilePath(fileName), lineno));
return true; return true;
} }

View File

@@ -106,7 +106,7 @@ void SdccParser::stdError(const QString &line)
const int lineno = match.captured(LineNumberIndex).toInt(); const int lineno = match.captured(LineNumberIndex).toInt();
const Task::TaskType type = taskType(match.captured(MessageTypeIndex)); const Task::TaskType type = taskType(match.captured(MessageTypeIndex));
const QString descr = match.captured(MessageTextIndex); const QString descr = match.captured(MessageTextIndex);
newTask(CompileTask(type, descr, fileName, lineno)); newTask(CompileTask(type, descr, absoluteFilePath(fileName), lineno));
return; return;
} }
@@ -120,7 +120,7 @@ void SdccParser::stdError(const QString &line)
const int lineno = match.captured(LineNumberIndex).toInt(); const int lineno = match.captured(LineNumberIndex).toInt();
const Task::TaskType type = taskType(match.captured(MessageTypeIndex)); const Task::TaskType type = taskType(match.captured(MessageTypeIndex));
const QString descr = match.captured(MessageTextIndex); const QString descr = match.captured(MessageTextIndex);
newTask(CompileTask(type, descr, fileName, lineno)); newTask(CompileTask(type, descr, absoluteFilePath(fileName), lineno));
return; return;
} }

View File

@@ -217,7 +217,7 @@ bool CMakeBuildStep::init()
IOutputParser *parser = target()->kit()->createOutputParser(); IOutputParser *parser = target()->kit()->createOutputParser();
if (parser) if (parser)
appendOutputParser(parser); appendOutputParser(parser);
outputParser()->setWorkingDirectory(pp->effectiveWorkingDirectory()); outputParser()->addSearchDir(pp->effectiveWorkingDirectory());
return AbstractProcessStep::init(); return AbstractProcessStep::init();
} }

View File

@@ -80,13 +80,13 @@ void CMakeParser::stdError(const QString &line)
m_lastTask = BuildSystemTask(Task::Error, m_lastTask = BuildSystemTask(Task::Error,
QString(), QString(),
FilePath::fromUserInput(path), absoluteFilePath(FilePath::fromUserInput(path)),
m_commonError.cap(2).toInt()); m_commonError.cap(2).toInt());
m_lines = 1; m_lines = 1;
return; return;
} else if (m_nextSubError.indexIn(trimmedLine) != -1) { } else if (m_nextSubError.indexIn(trimmedLine) != -1) {
m_lastTask = BuildSystemTask(Task::Error, QString(), m_lastTask = BuildSystemTask(Task::Error, QString(),
FilePath::fromUserInput(m_nextSubError.cap(1))); absoluteFilePath(FilePath::fromUserInput(m_nextSubError.cap(1))));
m_lines = 1; m_lines = 1;
return; return;
} else if (trimmedLine.startsWith(QLatin1String(" ")) && !m_lastTask.isNull()) { } else if (trimmedLine.startsWith(QLatin1String(" ")) && !m_lastTask.isNull()) {

View File

@@ -226,7 +226,7 @@ bool IosBuildStep::init()
IOutputParser *parser = target()->kit()->createOutputParser(); IOutputParser *parser = target()->kit()->createOutputParser();
if (parser) if (parser)
appendOutputParser(parser); appendOutputParser(parser);
outputParser()->setWorkingDirectory(pp->effectiveWorkingDirectory()); outputParser()->addSearchDir(pp->effectiveWorkingDirectory());
return AbstractProcessStep::init(); return AbstractProcessStep::init();
} }

View File

@@ -82,7 +82,7 @@ bool IosDsymBuildStep::init()
setOutputParser(target()->kit()->createOutputParser()); setOutputParser(target()->kit()->createOutputParser());
if (outputParser()) if (outputParser())
outputParser()->setWorkingDirectory(pp->effectiveWorkingDirectory()); outputParser()->addSearchDir(pp->effectiveWorkingDirectory());
return AbstractProcessStep::init(); return AbstractProcessStep::init();
} }

View File

@@ -87,7 +87,8 @@ private:
else else
return; return;
emit addTask(CompileTask(type, message, FilePath::fromUserInput(filename), lineNumber)); emit addTask(CompileTask(type, message, absoluteFilePath(FilePath::fromUserInput(filename)),
lineNumber));
} }
}; };
@@ -107,7 +108,7 @@ NimbleBuildStep::NimbleBuildStep(BuildStepList *parentList, Core::Id id)
bool NimbleBuildStep::init() bool NimbleBuildStep::init()
{ {
auto parser = new NimParser(); auto parser = new NimParser();
parser->setWorkingDirectory(project()->projectDirectory()); parser->addSearchDir(project()->projectDirectory());
setOutputParser(parser); setOutputParser(parser);
ProcessParameters* params = processParameters(); ProcessParameters* params = processParameters();

View File

@@ -89,7 +89,8 @@ private:
else else
return; return;
emit addTask(CompileTask(type, message, FilePath::fromUserInput(filename), lineNumber)); emit addTask(CompileTask(type, message, absoluteFilePath(FilePath::fromUserInput(filename)),
lineNumber));
} }
}; };
@@ -116,7 +117,7 @@ bool NimCompilerBuildStep::init()
setOutputParser(new NimParser()); setOutputParser(new NimParser());
if (IOutputParser *parser = target()->kit()->createOutputParser()) if (IOutputParser *parser = target()->kit()->createOutputParser())
appendOutputParser(parser); appendOutputParser(parser);
outputParser()->setWorkingDirectory(processParameters()->effectiveWorkingDirectory()); outputParser()->addSearchDir(processParameters()->effectiveWorkingDirectory());
return AbstractProcessStep::init(); return AbstractProcessStep::init();
} }

View File

@@ -76,7 +76,7 @@ void ClangParser::stdError(const QString &line)
m_expectSnippet = true; m_expectSnippet = true;
newTask(CompileTask(Task::Unknown, newTask(CompileTask(Task::Unknown,
lne.trimmed(), lne.trimmed(),
FilePath::fromUserInput(match.captured(2)), /* filename */ absoluteFilePath(FilePath::fromUserInput(match.captured(2))),
match.captured(3).toInt() /* line */)); match.captured(3).toInt() /* line */));
return; return;
} }
@@ -90,7 +90,7 @@ void ClangParser::stdError(const QString &line)
lineNo = match.captured(5).toInt(&ok); lineNo = match.captured(5).toInt(&ok);
newTask(CompileTask(taskType(match.captured(7)), newTask(CompileTask(taskType(match.captured(7)),
match.captured(8), match.captured(8),
FilePath::fromUserInput(match.captured(1)), /* filename */ absoluteFilePath(FilePath::fromUserInput(match.captured(1))),
lineNo)); lineNo));
return; return;
} }

View File

@@ -145,13 +145,6 @@ Core::Id CustomParser::id()
return Core::Id("ProjectExplorer.OutputParser.Custom"); return Core::Id("ProjectExplorer.OutputParser.Custom");
} }
FilePath CustomParser::absoluteFilePath(const QString &filePath) const
{
if (workingDirectory().isEmpty())
return FilePath::fromUserInput(filePath);
return workingDirectory().resolvePath(filePath);
}
bool CustomParser::hasMatch(const QString &line, CustomParserExpression::CustomParserChannel channel, bool CustomParser::hasMatch(const QString &line, CustomParserExpression::CustomParserChannel channel,
const CustomParserExpression &expression, Task::TaskType taskType) const CustomParserExpression &expression, Task::TaskType taskType)
{ {
@@ -165,7 +158,8 @@ bool CustomParser::hasMatch(const QString &line, CustomParserExpression::CustomP
if (!match.hasMatch()) if (!match.hasMatch())
return false; return false;
const FilePath fileName = absoluteFilePath(match.captured(expression.fileNameCap())); const FilePath fileName = absoluteFilePath(FilePath::fromString(
match.captured(expression.fileNameCap())));
const int lineNumber = match.captured(expression.lineNumberCap()).toInt(); const int lineNumber = match.captured(expression.lineNumberCap()).toInt();
const QString message = match.captured(expression.messageCap()); const QString message = match.captured(expression.messageCap());
@@ -475,7 +469,8 @@ void ProjectExplorerPlugin::testCustomOutputParsers()
CustomParser *parser = new CustomParser; CustomParser *parser = new CustomParser;
parser->setSettings(settings); parser->setSettings(settings);
parser->setWorkingDirectory(FilePath::fromString(workDir)); parser->addSearchDir(FilePath::fromString(workDir));
parser->skipFileExistsCheck();
OutputParserTester testbench; OutputParserTester testbench;
testbench.appendOutputParser(parser); testbench.appendOutputParser(parser);

View File

@@ -94,7 +94,6 @@ public:
static Core::Id id(); static Core::Id id();
private: private:
Utils::FilePath absoluteFilePath(const QString &filePath) const;
bool hasMatch(const QString &line, CustomParserExpression::CustomParserChannel channel, bool hasMatch(const QString &line, CustomParserExpression::CustomParserChannel channel,
const CustomParserExpression &expression, Task::TaskType taskType); const CustomParserExpression &expression, Task::TaskType taskType);
bool parseLine(const QString &rawLine, CustomParserExpression::CustomParserChannel channel); bool parseLine(const QString &rawLine, CustomParserExpression::CustomParserChannel channel);

View File

@@ -113,7 +113,7 @@ void GccParser::stdError(const QString &line)
if (match.captured(5).startsWith(QLatin1Char('#'))) if (match.captured(5).startsWith(QLatin1Char('#')))
description = match.captured(5) + description; description = match.captured(5) + description;
newTask(CompileTask(type, description, filename, lineno)); newTask(CompileTask(type, description, absoluteFilePath(filename), lineno));
return; return;
} }
@@ -121,7 +121,7 @@ void GccParser::stdError(const QString &line)
if (match.hasMatch()) { if (match.hasMatch()) {
newTask(CompileTask(Task::Unknown, newTask(CompileTask(Task::Unknown,
lne.trimmed() /* description */, lne.trimmed() /* description */,
Utils::FilePath::fromUserInput(match.captured(1)) /* filename */, absoluteFilePath(Utils::FilePath::fromUserInput(match.captured(1))),
match.captured(3).toInt() /* linenumber */)); match.captured(3).toInt() /* linenumber */));
return; return;
} else if (lne.startsWith(' ') && !m_currentTask.isNull()) { } else if (lne.startsWith(' ') && !m_currentTask.isNull()) {

View File

@@ -29,7 +29,7 @@
#include "task.h" #include "task.h"
#include <utils/qtcassert.h> #include <utils/qtcassert.h>
#include <utils/temporarydirectory.h> #include <utils/temporaryfile.h>
#include <QDir> #include <QDir>
#include <QFile> #include <QFile>
@@ -68,9 +68,9 @@ void GnuMakeParser::stdOutput(const QString &line)
QRegularExpressionMatch match = m_makeDir.match(lne); QRegularExpressionMatch match = m_makeDir.match(lne);
if (match.hasMatch()) { if (match.hasMatch()) {
if (match.captured(6) == QLatin1String("Leaving")) if (match.captured(6) == QLatin1String("Leaving"))
removeDirectory(match.captured(7)); dropSearchDir(FilePath::fromString(match.captured(7)));
else else
addDirectory(match.captured(7)); addSearchDir(FilePath::fromString(match.captured(7)));
return; return;
} }
@@ -128,10 +128,9 @@ void GnuMakeParser::stdError(const QString &line)
if (res.isFatal) if (res.isFatal)
++m_fatalErrorCount; ++m_fatalErrorCount;
if (!m_suppressIssues) { if (!m_suppressIssues) {
taskAdded(BuildSystemTask(res.type, res.description, emitTask(BuildSystemTask(res.type, res.description,
FilePath::fromUserInput(match.captured(1)) /* filename */, absoluteFilePath(FilePath::fromUserInput(match.captured(1))),
match.captured(4).toInt() /* line */), match.captured(4).toInt() /* line */));
1, 0);
} }
return; return;
} }
@@ -142,61 +141,18 @@ void GnuMakeParser::stdError(const QString &line)
if (res.isFatal) if (res.isFatal)
++m_fatalErrorCount; ++m_fatalErrorCount;
if (!m_suppressIssues) if (!m_suppressIssues)
taskAdded(BuildSystemTask(res.type, res.description), 1, 0); emitTask(BuildSystemTask(res.type, res.description));
return; return;
} }
IOutputParser::stdError(line); IOutputParser::stdError(line);
} }
void GnuMakeParser::addDirectory(const QString &dir) void GnuMakeParser::emitTask(const ProjectExplorer::Task &task)
{ {
if (dir.isEmpty()) if (task.type == Task::Error) // Assume that all make errors will be follow up errors.
return;
m_directories.append(dir);
}
void GnuMakeParser::removeDirectory(const QString &dir)
{
m_directories.removeOne(dir);
}
void GnuMakeParser::taskAdded(const Task &task, int linkedLines, int skippedLines)
{
Task editable(task);
if (task.type == Task::Error) {
// assume that all make errors will be follow up errors:
m_suppressIssues = true; m_suppressIssues = true;
} emit addTask(task, 1, 0);
QString filePath(task.file.toString());
if (!filePath.isEmpty() && !QDir::isAbsolutePath(filePath)) {
QFileInfoList possibleFiles;
foreach (const QString &dir, searchDirectories()) {
QFileInfo candidate(dir + QLatin1Char('/') + filePath);
if (candidate.exists()
&& !possibleFiles.contains(candidate)) {
possibleFiles << candidate;
}
}
if (possibleFiles.size() == 1)
editable.file = Utils::FilePath::fromFileInfo(possibleFiles.first());
// Let the Makestep apply additional heuristics (based on
// files in ther project) if we cannot uniquely
// identify the file!
}
IOutputParser::taskAdded(editable, linkedLines, skippedLines);
}
QStringList GnuMakeParser::searchDirectories() const
{
QStringList dirs = m_directories;
if (!workingDirectory().isEmpty())
dirs << workingDirectory().toString();
return dirs;
} }
} // ProjectExplorer } // ProjectExplorer
@@ -417,117 +373,52 @@ void ProjectExplorerPlugin::testGnuMakeParserParsing()
QFETCH(QString, outputLines); QFETCH(QString, outputLines);
QFETCH(QStringList, additionalSearchDirs); QFETCH(QStringList, additionalSearchDirs);
QStringList searchDirs = childParser->searchDirectories(); FilePaths searchDirs = childParser->searchDirectories();
// add extra directories: // add extra directories:
foreach (const QString &dir, extraSearchDirs) foreach (const QString &dir, extraSearchDirs)
childParser->addDirectory(dir); childParser->addSearchDir(FilePath::fromString(dir));
testbench.testParsing(input, inputChannel, testbench.testParsing(input, inputChannel,
tasks, childStdOutLines, childStdErrLines, tasks, childStdOutLines, childStdErrLines,
outputLines); outputLines);
// make sure we still have all the original dirs // make sure we still have all the original dirs
QStringList newSearchDirs = tester->directories; FilePaths newSearchDirs = tester->directories;
foreach (const QString &dir, searchDirs) { foreach (const FilePath &dir, searchDirs) {
QVERIFY(newSearchDirs.contains(dir)); QVERIFY(newSearchDirs.contains(dir));
newSearchDirs.removeOne(dir); newSearchDirs.removeOne(dir);
} }
// make sure we have all additional dirs: // make sure we have all additional dirs:
foreach (const QString &dir, additionalSearchDirs) { foreach (const QString &dir, additionalSearchDirs) {
QVERIFY(newSearchDirs.contains(dir)); const FilePath fp = FilePath::fromString(dir);
newSearchDirs.removeOne(dir); QVERIFY(newSearchDirs.contains(fp));
newSearchDirs.removeOne(fp);
} }
// make sure we have no extra cruft: // make sure we have no extra cruft:
QVERIFY(newSearchDirs.isEmpty()); QVERIFY(newSearchDirs.isEmpty());
delete tester; delete tester;
} }
void ProjectExplorerPlugin::testGnuMakeParserTaskMangling_data()
{
QTest::addColumn<QStringList>("files");
QTest::addColumn<QStringList>("searchDirectories");
QTest::addColumn<Task>("inputTask");
QTest::addColumn<Task>("outputTask");
QTest::newRow("no filename")
<< QStringList()
<< QStringList()
<< Task(CompileTask(Task::Error,
"no filename, no mangling"))
<< Task(CompileTask(Task::Error,
"no filename, no mangling"));
QTest::newRow("no mangling")
<< QStringList()
<< QStringList()
<< Task(CompileTask(Task::Error,
"unknown filename, no mangling",
FilePath::fromUserInput("some/path/unknown.cpp")))
<< Task(CompileTask(Task::Error,
"unknown filename, no mangling",
FilePath::fromUserInput("some/path/unknown.cpp")));
QTest::newRow("find file")
<< QStringList("test/file.cpp")
<< QStringList("test")
<< Task(CompileTask(Task::Error,
"mangling",
FilePath::fromUserInput("file.cpp"),
10))
<< Task(CompileTask(Task::Error,
"mangling",
FilePath::fromUserInput("$TMPDIR/test/file.cpp"),
10));
}
void ProjectExplorerPlugin::testGnuMakeParserTaskMangling() void ProjectExplorerPlugin::testGnuMakeParserTaskMangling()
{ {
TemporaryFile theMakeFile("Makefile.XXXXXX");
QVERIFY2(theMakeFile.open(), qPrintable(theMakeFile.errorString()));
QFileInfo fi(theMakeFile);
QVERIFY2(fi.fileName().startsWith("Makefile"), qPrintable(theMakeFile.fileName()));
OutputParserTester testbench; OutputParserTester testbench;
auto *childParser = new GnuMakeParser; auto *childParser = new GnuMakeParser;
testbench.appendOutputParser(childParser); testbench.appendOutputParser(childParser);
childParser->addSearchDir(FilePath::fromString(fi.absolutePath()));
QFETCH(QStringList, files); testbench.testParsing(
QFETCH(QStringList, searchDirectories); fi.fileName() + ":360: *** missing separator (did you mean TAB instead of 8 spaces?). Stop.",
QFETCH(Task, inputTask); OutputParserTester::STDERR,
QFETCH(Task, outputTask); {BuildSystemTask(Task::Error,
"missing separator (did you mean TAB instead of 8 spaces?). Stop.",
// setup files: FilePath::fromString(theMakeFile.fileName()), 360)},
const QString tempdir QString(), QString(), QString());
= Utils::TemporaryDirectory::masterDirectoryPath() + '/' + QUuid::createUuid().toString() + '/';
QDir filedir(tempdir);
foreach (const QString &file, files) {
Q_ASSERT(!file.startsWith('/'));
Q_ASSERT(!file.contains("../"));
filedir.mkpath(file.left(file.lastIndexOf('/')));
QFile tempfile(tempdir + file);
if (!tempfile.open(QIODevice::WriteOnly))
continue;
tempfile.write("Delete me again!");
tempfile.close();
}
// setup search dirs:
foreach (const QString &dir, searchDirectories) {
Q_ASSERT(!dir.startsWith(QLatin1Char('/')));
Q_ASSERT(!dir.contains(QLatin1String("../")));
childParser->addDirectory(tempdir + dir);
}
// fix up output task file:
QString filePath = outputTask.file.toString();
if (filePath.startsWith(QLatin1String("$TMPDIR/")))
outputTask.file = Utils::FilePath::fromString(filePath.replace(QLatin1String("$TMPDIR/"), tempdir));
// test mangling:
testbench.testTaskMangling(inputTask, outputTask);
// clean up:
foreach (const QString &file, files)
filedir.rmpath(tempdir + file);
} }
} // ProjectExplorer } // ProjectExplorer

View File

@@ -42,23 +42,16 @@ public:
void stdOutput(const QString &line) override; void stdOutput(const QString &line) override;
void stdError(const QString &line) override; void stdError(const QString &line) override;
QStringList searchDirectories() const;
bool hasFatalErrors() const override; bool hasFatalErrors() const override;
void taskAdded(const ProjectExplorer::Task &task, int linkedLines, int skippedLines) override;
private: private:
void addDirectory(const QString &dir); void emitTask(const ProjectExplorer::Task &task);
void removeDirectory(const QString &dir);
QRegularExpression m_makeDir; QRegularExpression m_makeDir;
QRegularExpression m_makeLine; QRegularExpression m_makeLine;
QRegularExpression m_threeStarError; QRegularExpression m_threeStarError;
QRegularExpression m_errorInMakefile; QRegularExpression m_errorInMakefile;
QStringList m_directories;
bool m_suppressIssues = false; bool m_suppressIssues = false;
int m_fatalErrorCount = 0; int m_fatalErrorCount = 0;
@@ -77,7 +70,7 @@ public:
explicit GnuMakeParserTester(GnuMakeParser *parser, QObject *parent = nullptr); explicit GnuMakeParserTester(GnuMakeParser *parser, QObject *parent = nullptr);
void parserIsAboutToBeDeleted(); void parserIsAboutToBeDeleted();
QStringList directories; Utils::FilePaths directories;
GnuMakeParser *parser; GnuMakeParser *parser;
}; };
#endif #endif

View File

@@ -28,6 +28,9 @@
#include <utils/synchronousprocess.h> #include <utils/synchronousprocess.h>
#include <QDir>
#include <QFileInfo>
/*! /*!
\class ProjectExplorer::IOutputParser \class ProjectExplorer::IOutputParser
@@ -162,9 +165,10 @@ public:
IOutputParser *childParser = nullptr; IOutputParser *childParser = nullptr;
QList<Filter> filters; QList<Filter> filters;
Utils::FilePath workingDir; Utils::FilePaths searchDirs;
OutputChannelState stdoutState; OutputChannelState stdoutState;
OutputChannelState stderrState; OutputChannelState stderrState;
bool skipFileExistsCheck = false;
}; };
IOutputParser::IOutputParser() : d(new IOutputParserPrivate(this)) IOutputParser::IOutputParser() : d(new IOutputParserPrivate(this))
@@ -197,7 +201,7 @@ void IOutputParser::appendOutputParser(IOutputParser *parser)
} }
d->childParser = parser; d->childParser = parser;
connect(parser, &IOutputParser::addTask, this, &IOutputParser::taskAdded); connect(parser, &IOutputParser::addTask, this, &IOutputParser::addTask);
} }
IOutputParser *IOutputParser::childParser() const IOutputParser *IOutputParser::childParser() const
@@ -211,7 +215,7 @@ void IOutputParser::setChildParser(IOutputParser *parser)
delete d->childParser; delete d->childParser;
d->childParser = parser; d->childParser = parser;
if (parser) if (parser)
connect(parser, &IOutputParser::addTask, this, &IOutputParser::taskAdded); connect(parser, &IOutputParser::addTask, this, &IOutputParser::addTask);
} }
void IOutputParser::stdOutput(const QString &line) void IOutputParser::stdOutput(const QString &line)
@@ -226,28 +230,18 @@ void IOutputParser::stdError(const QString &line)
d->childParser->stdError(line); d->childParser->stdError(line);
} }
Utils::FilePath IOutputParser::workingDirectory() const { return d->workingDir; } void IOutputParser::skipFileExistsCheck()
void IOutputParser::taskAdded(const Task &task, int linkedOutputLines, int skipLines)
{ {
emit addTask(task, linkedOutputLines, skipLines); d->skipFileExistsCheck = true;
} }
void IOutputParser::doFlush() void IOutputParser::doFlush() { }
{ }
bool IOutputParser::hasFatalErrors() const bool IOutputParser::hasFatalErrors() const
{ {
return d->childParser && d->childParser->hasFatalErrors(); return d->childParser && d->childParser->hasFatalErrors();
} }
void IOutputParser::setWorkingDirectory(const Utils::FilePath &fn)
{
d->workingDir = fn;
if (d->childParser)
d->childParser->setWorkingDirectory(fn);
}
void IOutputParser::flush() void IOutputParser::flush()
{ {
flushTasks(); flushTasks();
@@ -278,4 +272,40 @@ void IOutputParser::addFilter(const Filter &filter)
d->filters << filter; d->filters << filter;
} }
void IOutputParser::addSearchDir(const Utils::FilePath &dir)
{
d->searchDirs << dir;
if (d->childParser)
d->childParser->addSearchDir(dir);
}
void IOutputParser::dropSearchDir(const Utils::FilePath &dir)
{
const int idx = d->searchDirs.lastIndexOf(dir);
QTC_ASSERT(idx != -1, return);
d->searchDirs.removeAt(idx);
if (d->childParser)
d->childParser->dropSearchDir(dir);
}
const Utils::FilePaths IOutputParser::searchDirectories() const
{
return d->searchDirs;
}
Utils::FilePath IOutputParser::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()));
return filePath;
}
} // namespace ProjectExplorer } // namespace ProjectExplorer

View File

@@ -56,15 +56,16 @@ public:
using Filter = std::function<QString(const QString &)>; using Filter = std::function<QString(const QString &)>;
void addFilter(const Filter &filter); void addFilter(const Filter &filter);
void setWorkingDirectory(const Utils::FilePath &fn); void addSearchDir(const Utils::FilePath &dir);
void dropSearchDir(const Utils::FilePath &dir);
const Utils::FilePaths searchDirectories() const;
void skipFileExistsCheck(); // For testing only
void flush(); // flush pending tasks & output void flush(); // flush pending tasks & output
void flushTasks(); // flush pending tasks only void flushTasks(); // flush pending tasks only
static QString rightTrimmed(const QString &in); static QString rightTrimmed(const QString &in);
virtual void taskAdded(const ProjectExplorer::Task &task, int linkedOutputLines = 0, int skipLines = 0);
signals: signals:
void addTask(const ProjectExplorer::Task &task, int linkedOutputLines = 0, int skipLines = 0); void addTask(const ProjectExplorer::Task &task, int linkedOutputLines = 0, int skipLines = 0);
@@ -72,7 +73,7 @@ protected:
virtual void stdOutput(const QString &line); virtual void stdOutput(const QString &line);
virtual void stdError(const QString &line); virtual void stdError(const QString &line);
Utils::FilePath workingDirectory() const; Utils::FilePath absoluteFilePath(const Utils::FilePath &filePath);
private: private:
virtual void doFlush(); virtual void doFlush();

View File

@@ -133,7 +133,7 @@ void LdParser::stdError(const QString &line)
type = Task::Warning; type = Task::Warning;
description = description.mid(9); description = description.mid(9);
} }
emit addTask(CompileTask(type, description, filename, lineno), 1); emit addTask(CompileTask(type, description, absoluteFilePath(filename), lineno), 1);
return; return;
} }

View File

@@ -85,7 +85,7 @@ void LinuxIccParser::stdError(const QString &line)
type = Task::Warning; type = Task::Warning;
m_temporary = CompileTask(type, m_temporary = CompileTask(type,
m_firstLine.cap(6).trimmed(), m_firstLine.cap(6).trimmed(),
Utils::FilePath::fromUserInput(m_firstLine.cap(1)), absoluteFilePath(Utils::FilePath::fromUserInput(m_firstLine.cap(1))),
m_firstLine.cap(2).toInt()); m_firstLine.cap(2).toInt());
m_lines = 1; m_lines = 1;

View File

@@ -64,7 +64,8 @@ void LldParser::stdError(const QString &line)
const int filePathLen = locOffset == -1 ? -1 : locOffset - filePathOffset; const int filePathLen = locOffset == -1 ? -1 : locOffset - filePathOffset;
const auto file = Utils::FilePath::fromUserInput( const auto file = Utils::FilePath::fromUserInput(
trimmedLine.mid(filePathOffset, filePathLen).trimmed()); trimmedLine.mid(filePathOffset, filePathLen).trimmed());
emit addTask(CompileTask(Task::Unknown, trimmedLine.mid(4).trimmed(), file, lineNo)); emit addTask(CompileTask(Task::Unknown, trimmedLine.mid(4).trimmed(),
absoluteFilePath(file), lineNo));
return; return;
} }
IOutputParser::stdError(line); IOutputParser::stdError(line);

View File

@@ -106,7 +106,7 @@ bool MakeStep::init()
IOutputParser *parser = target()->kit()->createOutputParser(); IOutputParser *parser = target()->kit()->createOutputParser();
if (parser) if (parser)
appendOutputParser(parser); appendOutputParser(parser);
outputParser()->setWorkingDirectory(pp->effectiveWorkingDirectory()); outputParser()->addSearchDir(pp->effectiveWorkingDirectory());
return AbstractProcessStep::init(); return AbstractProcessStep::init();
} }

View File

@@ -142,7 +142,7 @@ void MsvcParser::stdOutput(const QString &line)
if (!match.captured(1).isEmpty()) if (!match.captured(1).isEmpty())
description.chop(1); // Remove trailing quote description.chop(1); // Remove trailing quote
m_lastTask = CompileTask(Task::Unknown, description, m_lastTask = CompileTask(Task::Unknown, description,
FilePath::fromUserInput(match.captured(2)), /* fileName */ absoluteFilePath(FilePath::fromUserInput(match.captured(2))),
match.captured(3).toInt() /* linenumber */); match.captured(3).toInt() /* linenumber */);
m_lines = 1; m_lines = 1;
return; return;
@@ -176,7 +176,7 @@ bool MsvcParser::processCompileLine(const QString &line)
QPair<FilePath, int> position = parseFileName(match.captured(1)); QPair<FilePath, int> position = parseFileName(match.captured(1));
m_lastTask = CompileTask(taskType(match.captured(2)), m_lastTask = CompileTask(taskType(match.captured(2)),
match.captured(3) + match.captured(4).trimmed(), // description match.captured(3) + match.captured(4).trimmed(), // description
position.first, position.second); absoluteFilePath(position.first), position.second);
m_lines = 1; m_lines = 1;
return true; return true;
} }
@@ -257,7 +257,7 @@ void ClangClParser::stdError(const QString &lineIn)
doFlush(); doFlush();
const QPair<FilePath, int> position = parseFileName(match.captured(1)); const QPair<FilePath, int> position = parseFileName(match.captured(1));
m_lastTask = CompileTask(taskType(match.captured(2)), match.captured(3).trimmed(), m_lastTask = CompileTask(taskType(match.captured(2)), match.captured(3).trimmed(),
position.first, position.second); absoluteFilePath(position.first), position.second);
m_linkedLines = 1; m_linkedLines = 1;
return; return;
} }

View File

@@ -39,6 +39,13 @@ static inline QByteArray msgFileComparisonFail(const Utils::FilePath &f1, const
} }
// test functions: // test functions:
OutputParserTester::OutputParserTester()
{
connect(this, &IOutputParser::addTask, this, [this](const Task &t) {
m_receivedTasks.append(t);
});
}
void OutputParserTester::testParsing(const QString &lines, void OutputParserTester::testParsing(const QString &lines,
Channel inputChannel, Channel inputChannel,
Tasks tasks, Tasks tasks,
@@ -79,38 +86,11 @@ void OutputParserTester::testParsing(const QString &lines,
} }
} }
void OutputParserTester::testTaskMangling(const Task &input,
const Task &output)
{
reset();
childParser()->taskAdded(input);
QVERIFY(m_receivedOutput.isNull());
QVERIFY(m_receivedStdErrChildLine.isNull());
QVERIFY(m_receivedStdOutChildLine.isNull());
QVERIFY(m_receivedTasks.size() == 1);
if (m_receivedTasks.size() == 1) {
QCOMPARE(m_receivedTasks.at(0).category, output.category);
QCOMPARE(m_receivedTasks.at(0).description, output.description);
QVERIFY2(m_receivedTasks.at(0).file == output.file,
msgFileComparisonFail(m_receivedTasks.at(0).file, output.file));
QCOMPARE(m_receivedTasks.at(0).line, output.line);
QCOMPARE(m_receivedTasks.at(0).type, output.type);
}
}
void OutputParserTester::setDebugEnabled(bool debug) void OutputParserTester::setDebugEnabled(bool debug)
{ {
m_debug = debug; m_debug = debug;
} }
void OutputParserTester::taskAdded(const Task &task, int linkedLines, int skipLines)
{
Q_UNUSED(linkedLines)
Q_UNUSED(skipLines)
m_receivedTasks.append(task);
}
void OutputParserTester::reset() void OutputParserTester::reset()
{ {
m_receivedStdErrChildLine.clear(); m_receivedStdErrChildLine.clear();

View File

@@ -46,14 +46,14 @@ public:
STDERR STDERR
}; };
OutputParserTester();
// test functions: // test functions:
void testParsing(const QString &lines, Channel inputChannel, void testParsing(const QString &lines, Channel inputChannel,
Tasks tasks, Tasks tasks,
const QString &childStdOutLines, const QString &childStdOutLines,
const QString &childStdErrLines, const QString &childStdErrLines,
const QString &outputLines); const QString &outputLines);
void testTaskMangling(const Task &input,
const Task &output);
void setDebugEnabled(bool); void setDebugEnabled(bool);
@@ -61,8 +61,6 @@ signals:
void aboutToDeleteParser(); void aboutToDeleteParser();
private: private:
void taskAdded(const ProjectExplorer::Task &task, int linkedLines, int skipLines) override;
void reset(); void reset();
bool m_debug = false; bool m_debug = false;

View File

@@ -229,7 +229,6 @@ private slots:
void testGnuMakeParserParsing_data(); void testGnuMakeParserParsing_data();
void testGnuMakeParserParsing(); void testGnuMakeParserParsing();
void testGnuMakeParserTaskMangling_data();
void testGnuMakeParserTaskMangling(); void testGnuMakeParserTaskMangling();
void testXcodebuildParserParsing_data(); void testXcodebuildParserParsing_data();

View File

@@ -74,8 +74,8 @@ void XcodebuildParser::stdOutput(const QString &line)
if (lne.endsWith(QLatin1String(signatureChangeEndsWithPattern))) { if (lne.endsWith(QLatin1String(signatureChangeEndsWithPattern))) {
CompileTask task(Task::Warning, CompileTask task(Task::Warning,
tr("Replacing signature"), tr("Replacing signature"),
FilePath::fromString( absoluteFilePath(FilePath::fromString(
lne.left(lne.size() - QLatin1String(signatureChangeEndsWithPattern).size()))); lne.left(lne.size() - QLatin1String(signatureChangeEndsWithPattern).size()))));
emit addTask(task, 1); emit addTask(task, 1);
return; return;
} }

View File

@@ -376,7 +376,7 @@ void QbsBuildStep::handleProcessResult(
return; return;
if (m_parser) if (m_parser)
m_parser->setWorkingDirectory(workingDir); m_parser->addSearchDir(workingDir);
emit addOutput(executable.toUserOutput() + ' ' + QtcProcess::joinArgs(arguments), emit addOutput(executable.toUserOutput() + ' ' + QtcProcess::joinArgs(arguments),
OutputFormat::Stdout); OutputFormat::Stdout);
for (const QString &line : stdErr) { for (const QString &line : stdErr) {
@@ -389,8 +389,10 @@ void QbsBuildStep::handleProcessResult(
m_parser->handleStdout(line + '\n'); m_parser->handleStdout(line + '\n');
emit addOutput(line, OutputFormat::Stdout); emit addOutput(line, OutputFormat::Stdout);
} }
if (m_parser) if (m_parser) {
m_parser->flush(); m_parser->flush();
m_parser->dropSearchDir(workingDir);
}
} }
void QbsBuildStep::createTaskAndOutput(ProjectExplorer::Task::TaskType type, const QString &message, void QbsBuildStep::createTaskAndOutput(ProjectExplorer::Task::TaskType type, const QString &message,

View File

@@ -171,7 +171,7 @@ bool QmakeMakeStep::init()
IOutputParser *parser = target()->kit()->createOutputParser(); IOutputParser *parser = target()->kit()->createOutputParser();
if (parser) if (parser)
appendOutputParser(parser); appendOutputParser(parser);
outputParser()->setWorkingDirectory(pp->effectiveWorkingDirectory()); outputParser()->addSearchDir(pp->effectiveWorkingDirectory());
appendOutputParser(new QMakeParser); // make may cause qmake to be run, add last to make sure appendOutputParser(new QMakeParser); // make may cause qmake to be run, add last to make sure
// it has a low priority. // it has a low priority.

View File

@@ -61,7 +61,7 @@ void QMakeParser::stdError(const QString &line)
type = Task::Error; type = Task::Error;
emit addTask(BuildSystemTask(type, emit addTask(BuildSystemTask(type,
description, description,
FilePath::fromUserInput(fileName), absoluteFilePath(FilePath::fromUserInput(fileName)),
m_error.cap(2).toInt() /* line */), m_error.cap(2).toInt() /* line */),
1); 1);
return; return;

View File

@@ -338,7 +338,7 @@ void QMakeStep::runNextCommand()
case State::RUN_MAKE_QMAKE_ALL: case State::RUN_MAKE_QMAKE_ALL:
{ {
auto *parser = new GnuMakeParser; auto *parser = new GnuMakeParser;
parser->setWorkingDirectory(processParameters()->workingDirectory()); parser->addSearchDir(processParameters()->workingDirectory());
setOutputParser(parser); setOutputParser(parser);
m_nextState = State::POST_PROCESS; m_nextState = State::POST_PROCESS;
startOneCommand(m_makeCommand); startOneCommand(m_makeCommand);

View File

@@ -58,7 +58,7 @@ void QtParser::stdError(const QString &line)
if (level.compare(QLatin1String("Note"), Qt::CaseInsensitive) == 0) if (level.compare(QLatin1String("Note"), Qt::CaseInsensitive) == 0)
type = Task::Unknown; type = Task::Unknown;
CompileTask task(type, m_mocRegExp.cap(5).trimmed() /* description */, CompileTask task(type, m_mocRegExp.cap(5).trimmed() /* description */,
Utils::FilePath::fromUserInput(m_mocRegExp.cap(1)) /* filename */, absoluteFilePath(Utils::FilePath::fromUserInput(m_mocRegExp.cap(1))),
lineno); lineno);
emit addTask(task, 1); emit addTask(task, 1);
return; return;
@@ -68,7 +68,7 @@ void QtParser::stdError(const QString &line)
if (m_translationRegExp.cap(1) == QLatin1String("Error")) if (m_translationRegExp.cap(1) == QLatin1String("Error"))
type = Task::Error; type = Task::Error;
CompileTask task(type, m_translationRegExp.cap(2), CompileTask task(type, m_translationRegExp.cap(2),
Utils::FilePath::fromUserInput(m_translationRegExp.cap(3))); absoluteFilePath(Utils::FilePath::fromUserInput(m_translationRegExp.cap(3))));
emit addTask(task, 1); emit addTask(task, 1);
return; return;
} }

View File

@@ -68,8 +68,8 @@ void QtTestParser::stdOutput(const QString &line)
QTC_CHECK(locationPattern.isValid()); QTC_CHECK(locationPattern.isValid());
const QRegularExpressionMatch match = locationPattern.match(theLine); const QRegularExpressionMatch match = locationPattern.match(theLine);
if (match.hasMatch()) { if (match.hasMatch()) {
m_currentTask.file = FilePath::fromString( m_currentTask.file = absoluteFilePath(FilePath::fromString(
QDir::fromNativeSeparators(match.captured("file"))); QDir::fromNativeSeparators(match.captured("file"))));
m_currentTask.line = match.captured("line").toInt(); m_currentTask.line = match.captured("line").toInt();
emitCurrentTask(); emitCurrentTask();
return; return;