diff options
Diffstat (limited to 'src/plugins')
110 files changed, 690 insertions, 1127 deletions
diff --git a/src/plugins/android/androidbuildapkstep.cpp b/src/plugins/android/androidbuildapkstep.cpp index 3112d22ae59..7bb6bbc4647 100644 --- a/src/plugins/android/androidbuildapkstep.cpp +++ b/src/plugins/android/androidbuildapkstep.cpp @@ -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); diff --git a/src/plugins/android/androidbuildapkstep.h b/src/plugins/android/androidbuildapkstep.h index e60d2b7f382..ab65a2d7f55 100644 --- a/src/plugins/android/androidbuildapkstep.h +++ b/src/plugins/android/androidbuildapkstep.h @@ -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; diff --git a/src/plugins/android/androidpackageinstallationstep.cpp b/src/plugins/android/androidpackageinstallationstep.cpp index 9fd25e92967..b16a8e864e8 100644 --- a/src/plugins/android/androidpackageinstallationstep.cpp +++ b/src/plugins/android/androidpackageinstallationstep.cpp @@ -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; diff --git a/src/plugins/android/javaparser.cpp b/src/plugins/android/javaparser.cpp index 9cf34789532..04035c851d6 100644 --- a/src/plugins/android/javaparser.cpp +++ b/src/plugins/android/javaparser.cpp @@ -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}; } diff --git a/src/plugins/android/javaparser.h b/src/plugins/android/javaparser.h index 31cccbe98c4..09b19d5bdbe 100644 --- a/src/plugins/android/javaparser.h +++ b/src/plugins/android/javaparser.h @@ -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; diff --git a/src/plugins/baremetal/iarewparser.cpp b/src/plugins/baremetal/iarewparser.cpp index b30bb82f2bb..5e8b97fa628 100644 --- a/src/plugins/baremetal/iarewparser.cpp +++ b/src/plugins/baremetal/iarewparser.cpp @@ -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; } diff --git a/src/plugins/baremetal/iarewparser.h b/src/plugins/baremetal/iarewparser.h index 02ad25062dc..92b38bd26fa 100644 --- a/src/plugins/baremetal/iarewparser.h +++ b/src/plugins/baremetal/iarewparser.h @@ -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; diff --git a/src/plugins/baremetal/iarewtoolchain.cpp b/src/plugins/baremetal/iarewtoolchain.cpp index b65ebf0d9c6..1ea9d013fe6 100644 --- a/src/plugins/baremetal/iarewtoolchain.cpp +++ b/src/plugins/baremetal/iarewtoolchain.cpp @@ -354,7 +354,7 @@ void IarToolChain::addToEnvironment(Environment &env) const } } -QList<OutputTaskParser *> IarToolChain::createOutputParsers() const +QList<Utils::OutputLineParser *> IarToolChain::createOutputParsers() const { return {new IarParser()}; } diff --git a/src/plugins/baremetal/iarewtoolchain.h b/src/plugins/baremetal/iarewtoolchain.h index b1706ab2ac8..82442f1817e 100644 --- a/src/plugins/baremetal/iarewtoolchain.h +++ b/src/plugins/baremetal/iarewtoolchain.h @@ -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; diff --git a/src/plugins/baremetal/keilparser.cpp b/src/plugins/baremetal/keilparser.cpp index 094e59ab0fc..3e5032566b8 100644 --- a/src/plugins/baremetal/keilparser.cpp +++ b/src/plugins/baremetal/keilparser.cpp @@ -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; } diff --git a/src/plugins/baremetal/keilparser.h b/src/plugins/baremetal/keilparser.h index d717e744535..f8f5f8b97d4 100644 --- a/src/plugins/baremetal/keilparser.h +++ b/src/plugins/baremetal/keilparser.h @@ -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; diff --git a/src/plugins/baremetal/keiltoolchain.cpp b/src/plugins/baremetal/keiltoolchain.cpp index 922d38a4011..3eca05dcc65 100644 --- a/src/plugins/baremetal/keiltoolchain.cpp +++ b/src/plugins/baremetal/keiltoolchain.cpp @@ -506,7 +506,7 @@ void KeilToolChain::addToEnvironment(Environment &env) const } } -QList<OutputTaskParser *> KeilToolChain::createOutputParsers() const +QList<OutputLineParser *> KeilToolChain::createOutputParsers() const { return {new KeilParser}; } diff --git a/src/plugins/baremetal/keiltoolchain.h b/src/plugins/baremetal/keiltoolchain.h index 561db08bd11..3fd36d67962 100644 --- a/src/plugins/baremetal/keiltoolchain.h +++ b/src/plugins/baremetal/keiltoolchain.h @@ -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; diff --git a/src/plugins/baremetal/sdccparser.cpp b/src/plugins/baremetal/sdccparser.cpp index 7cfd363d744..9ed21234d95 100644 --- a/src/plugins/baremetal/sdccparser.cpp +++ b/src/plugins/baremetal/sdccparser.cpp @@ -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; } diff --git a/src/plugins/baremetal/sdccparser.h b/src/plugins/baremetal/sdccparser.h index a8b6f2c0fd8..0b92b702258 100644 --- a/src/plugins/baremetal/sdccparser.h +++ b/src/plugins/baremetal/sdccparser.h @@ -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; diff --git a/src/plugins/baremetal/sdcctoolchain.cpp b/src/plugins/baremetal/sdcctoolchain.cpp index 0039b32f213..36bc7945bba 100644 --- a/src/plugins/baremetal/sdcctoolchain.cpp +++ b/src/plugins/baremetal/sdcctoolchain.cpp @@ -307,7 +307,7 @@ void SdccToolChain::addToEnvironment(Environment &env) const } } -QList<OutputTaskParser *> SdccToolChain::createOutputParsers() const +QList<Utils::OutputLineParser *> SdccToolChain::createOutputParsers() const { return {new SdccParser}; } diff --git a/src/plugins/baremetal/sdcctoolchain.h b/src/plugins/baremetal/sdcctoolchain.h index 0ac575c9823..09aff331afb 100644 --- a/src/plugins/baremetal/sdcctoolchain.h +++ b/src/plugins/baremetal/sdcctoolchain.h @@ -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; diff --git a/src/plugins/cmakeprojectmanager/cmakebuildstep.cpp b/src/plugins/cmakeprojectmanager/cmakebuildstep.cpp index 0ece524c19f..aa9c3f6e6ec 100644 --- a/src/plugins/cmakeprojectmanager/cmakebuildstep.cpp +++ b/src/plugins/cmakeprojectmanager/cmakebuildstep.cpp @@ -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: diff --git a/src/plugins/cmakeprojectmanager/cmakebuildstep.h b/src/plugins/cmakeprojectmanager/cmakebuildstep.h index cd41f77f412..1e5a4e71009 100644 --- a/src/plugins/cmakeprojectmanager/cmakebuildstep.h +++ b/src/plugins/cmakeprojectmanager/cmakebuildstep.h @@ -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; diff --git a/src/plugins/cmakeprojectmanager/cmakeparser.cpp b/src/plugins/cmakeprojectmanager/cmakeparser.cpp index a2dba99b1c5..2d8aa7c0227 100644 --- a/src/plugins/cmakeprojectmanager/cmakeparser.cpp +++ b/src/plugins/cmakeprojectmanager/cmakeparser.cpp @@ -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; } diff --git a/src/plugins/cmakeprojectmanager/cmakeparser.h b/src/plugins/cmakeprojectmanager/cmakeparser.h index dfcf6d0c1b0..b0cfc6f55a7 100644 --- a/src/plugins/cmakeprojectmanager/cmakeparser.h +++ b/src/plugins/cmakeprojectmanager/cmakeparser.h @@ -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 }; diff --git a/src/plugins/cmakeprojectmanager/cmakeprocess.cpp b/src/plugins/cmakeprojectmanager/cmakeprocess.cpp index 0e6d7a52ffb..e500ac8c2ec 100644 --- a/src/plugins/cmakeprojectmanager/cmakeprocess.cpp +++ b/src/plugins/cmakeprojectmanager/cmakeprocess.cpp @@ -94,17 +94,6 @@ void CMakeProcess::run(const BuildDirParameters ¶meters, const QStringList & const auto parser = new CMakeParser; parser->setSourceDirectory(srcDir); m_parser.addLineParser(parser); - QDir source = QDir(srcDir); - connect(&m_parser, &IOutputParser::addTask, this, - [source](const Task &task) { - if (task.file.isEmpty() || task.file.toFileInfo().isAbsolute()) { - TaskHub::addTask(task); - } else { - Task t = task; - t.file = Utils::FilePath::fromString(source.absoluteFilePath(task.file.toString())); - TaskHub::addTask(t); - } - }); // Always use the sourceDir: If we are triggered because the build directory is getting deleted // then we are racing against CMakeCache.txt also getting deleted. @@ -194,7 +183,7 @@ void CMakeProcess::processStandardError() static QString rest; rest = lineSplit(rest, m_process->readAllStandardError(), [this](const QString &s) { - m_parser.handleStderr(s); + m_parser.appendMessage(s, Utils::StdErrFormat); Core::MessageManager::write(s); }); } diff --git a/src/plugins/cmakeprojectmanager/cmakeprocess.h b/src/plugins/cmakeprojectmanager/cmakeprocess.h index ed68f9f96c0..c63f54952ad 100644 --- a/src/plugins/cmakeprojectmanager/cmakeprocess.h +++ b/src/plugins/cmakeprojectmanager/cmakeprocess.h @@ -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; diff --git a/src/plugins/cmakeprojectmanager/servermodereader.cpp b/src/plugins/cmakeprojectmanager/servermodereader.cpp index 85ead8eadd3..e9f9f994261 100644 --- a/src/plugins/cmakeprojectmanager/servermodereader.cpp +++ b/src/plugins/cmakeprojectmanager/servermodereader.cpp @@ -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); } }); diff --git a/src/plugins/cmakeprojectmanager/servermodereader.h b/src/plugins/cmakeprojectmanager/servermodereader.h index a93b91e6fa9..66ab0a56be0 100644 --- a/src/plugins/cmakeprojectmanager/servermodereader.h +++ b/src/plugins/cmakeprojectmanager/servermodereader.h @@ -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; diff --git a/src/plugins/coreplugin/outputwindow.cpp b/src/plugins/coreplugin/outputwindow.cpp index 91504ff1a16..b27b91afc03 100644 --- a/src/plugins/coreplugin/outputwindow.cpp +++ b/src/plugins/coreplugin/outputwindow.cpp @@ -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 diff --git a/src/plugins/ios/iosbuildstep.cpp b/src/plugins/ios/iosbuildstep.cpp index a8503d0fc65..9fbe292299b 100644 --- a/src/plugins/ios/iosbuildstep.cpp +++ b/src/plugins/ios/iosbuildstep.cpp @@ -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()); diff --git a/src/plugins/ios/iosdsymbuildstep.cpp b/src/plugins/ios/iosdsymbuildstep.cpp index f188630bc6a..3b11d069222 100644 --- a/src/plugins/ios/iosdsymbuildstep.cpp +++ b/src/plugins/ios/iosdsymbuildstep.cpp @@ -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); diff --git a/src/plugins/ios/iosdsymbuildstep.h b/src/plugins/ios/iosdsymbuildstep.h index effc6e5f914..7e08b1a6ad2 100644 --- a/src/plugins/ios/iosdsymbuildstep.h +++ b/src/plugins/ios/iosdsymbuildstep.h @@ -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; diff --git a/src/plugins/nim/project/nimblebuildstep.cpp b/src/plugins/nim/project/nimblebuildstep.cpp index 7beebc7ae93..1d29fbb1aa4 100644 --- a/src/plugins/nim/project/nimblebuildstep.cpp +++ b/src/plugins/nim/project/nimblebuildstep.cpp @@ -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); diff --git a/src/plugins/nim/project/nimblebuildstep.h b/src/plugins/nim/project/nimblebuildstep.h index 3a3af510463..7d903e5135b 100644 --- a/src/plugins/nim/project/nimblebuildstep.h +++ b/src/plugins/nim/project/nimblebuildstep.h @@ -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; diff --git a/src/plugins/nim/project/nimcompilerbuildstep.cpp b/src/plugins/nim/project/nimcompilerbuildstep.cpp index 4950f20fbbf..9015a2002dd 100644 --- a/src/plugins/nim/project/nimcompilerbuildstep.cpp +++ b/src/plugins/nim/project/nimcompilerbuildstep.cpp @@ -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() diff --git a/src/plugins/nim/project/nimcompilerbuildstep.h b/src/plugins/nim/project/nimcompilerbuildstep.h index aca8750c06b..cd194e06ea9 100644 --- a/src/plugins/nim/project/nimcompilerbuildstep.h +++ b/src/plugins/nim/project/nimcompilerbuildstep.h @@ -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; diff --git a/src/plugins/nim/project/nimtoolchain.cpp b/src/plugins/nim/project/nimtoolchain.cpp index 337e697e4fb..1d1cf4e1a7f 100644 --- a/src/plugins/nim/project/nimtoolchain.cpp +++ b/src/plugins/nim/project/nimtoolchain.cpp @@ -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 {}; } diff --git a/src/plugins/nim/project/nimtoolchain.h b/src/plugins/nim/project/nimtoolchain.h index 7ef119af1e1..d61448e6a00 100644 --- a/src/plugins/nim/project/nimtoolchain.h +++ b/src/plugins/nim/project/nimtoolchain.h @@ -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; diff --git a/src/plugins/projectexplorer/CMakeLists.txt b/src/plugins/projectexplorer/CMakeLists.txt index 24b7dbd6553..35ed62afc67 100644 --- a/src/plugins/projectexplorer/CMakeLists.txt +++ b/src/plugins/projectexplorer/CMakeLists.txt @@ -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 diff --git a/src/plugins/projectexplorer/abstractprocessstep.cpp b/src/plugins/projectexplorer/abstractprocessstep.cpp index 479cd309f34..1a9d18a9378 100644 --- a/src/plugins/projectexplorer/abstractprocessstep.cpp +++ b/src/plugins/projectexplorer/abstractprocessstep.cpp @@ -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); } diff --git a/src/plugins/projectexplorer/abstractprocessstep.h b/src/plugins/projectexplorer/abstractprocessstep.h index f6b720eb138..9431c3a97c0 100644 --- a/src/plugins/projectexplorer/abstractprocessstep.h +++ b/src/plugins/projectexplorer/abstractprocessstep.h @@ -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); diff --git a/src/plugins/projectexplorer/ansifilterparser.cpp b/src/plugins/projectexplorer/ansifilterparser.cpp deleted file mode 100644 index a4540845ce8..00000000000 --- a/src/plugins/projectexplorer/ansifilterparser.cpp +++ /dev/null @@ -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 diff --git a/src/plugins/projectexplorer/ansifilterparser.h b/src/plugins/projectexplorer/ansifilterparser.h deleted file mode 100644 index b3ec351c357..00000000000 --- a/src/plugins/projectexplorer/ansifilterparser.h +++ /dev/null @@ -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 diff --git a/src/plugins/projectexplorer/buildmanager.cpp b/src/plugins/projectexplorer/buildmanager.cpp index 08bfa57de03..e13d12c9d8c 100644 --- a/src/plugins/projectexplorer/buildmanager.cpp +++ b/src/plugins/projectexplorer/buildmanager.cpp @@ -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; diff --git a/src/plugins/projectexplorer/buildstep.cpp b/src/plugins/projectexplorer/buildstep.cpp index 319eaf69b66..b7edd0d21a7 100644 --- a/src/plugins/projectexplorer/buildstep.cpp +++ b/src/plugins/projectexplorer/buildstep.cpp @@ -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); diff --git a/src/plugins/projectexplorer/buildstep.h b/src/plugins/projectexplorer/buildstep.h index b8dd6b67181..048b798ba48 100644 --- a/src/plugins/projectexplorer/buildstep.h +++ b/src/plugins/projectexplorer/buildstep.h @@ -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); diff --git a/src/plugins/projectexplorer/clangparser.cpp b/src/plugins/projectexplorer/clangparser.cpp index 5b6739e5448..4df1d5bff7a 100644 --- a/src/plugins/projectexplorer/clangparser.cpp +++ b/src/plugins/projectexplorer/clangparser.cpp @@ -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(); } diff --git a/src/plugins/projectexplorer/clangparser.h b/src/plugins/projectexplorer/clangparser.h index 13b2d9ece65..5b61d0cbaad 100644 --- a/src/plugins/projectexplorer/clangparser.h +++ b/src/plugins/projectexplorer/clangparser.h @@ -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; diff --git a/src/plugins/projectexplorer/compileoutputwindow.cpp b/src/plugins/projectexplorer/compileoutputwindow.cpp index 832318b4199..27c08886337 100644 --- a/src/plugins/projectexplorer/compileoutputwindow.cpp +++ b/src/plugins/projectexplorer/compileoutputwindow.cpp @@ -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; - m_taskPositions.insert(task.taskId, qMakePair(startLine, endLine)); + const int blocknumber = m_outputWindow->document()->blockCount() - offset - 1; + const int firstLine = blocknumber - linkedOutputLines - skipLines; + const int lastLine = firstLine + linkedOutputLines - 1; - 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" diff --git a/src/plugins/projectexplorer/compileoutputwindow.h b/src/plugins/projectexplorer/compileoutputwindow.h index adea3aca802..caed6e8d66c 100644 --- a/src/plugins/projectexplorer/compileoutputwindow.h +++ b/src/plugins/projectexplorer/compileoutputwindow.h @@ -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; diff --git a/src/plugins/projectexplorer/customparser.cpp b/src/plugins/projectexplorer/customparser.cpp index cff4893f760..dfd588fe201 100644 --- a/src/plugins/projectexplorer/customparser.cpp +++ b/src/plugins/projectexplorer/customparser.cpp @@ -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); } diff --git a/src/plugins/projectexplorer/customparser.h b/src/plugins/projectexplorer/customparser.h index a7e7d15d7f6..00069f117f6 100644 --- a/src/plugins/projectexplorer/customparser.h +++ b/src/plugins/projectexplorer/customparser.h @@ -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; diff --git a/src/plugins/projectexplorer/customtoolchain.cpp b/src/plugins/projectexplorer/customtoolchain.cpp index 8c95e58921a..abe682a48e5 100644 --- a/src/plugins/projectexplorer/customtoolchain.cpp +++ b/src/plugins/projectexplorer/customtoolchain.cpp @@ -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(); diff --git a/src/plugins/projectexplorer/customtoolchain.h b/src/plugins/projectexplorer/customtoolchain.h index 8274241695c..dc57fe35032 100644 --- a/src/plugins/projectexplorer/customtoolchain.h +++ b/src/plugins/projectexplorer/customtoolchain.h @@ -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); diff --git a/src/plugins/projectexplorer/gccparser.cpp b/src/plugins/projectexplorer/gccparser.cpp index a0bcf92fd6f..2e55e9178f9 100644 --- a/src/plugins/projectexplorer/gccparser.cpp +++ b/src/plugins/projectexplorer/gccparser.cpp @@ -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") diff --git a/src/plugins/projectexplorer/gccparser.h b/src/plugins/projectexplorer/gccparser.h index 46b4de42d1c..76afb536684 100644 --- a/src/plugins/projectexplorer/gccparser.h +++ b/src/plugins/projectexplorer/gccparser.h @@ -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; diff --git a/src/plugins/projectexplorer/gcctoolchain.cpp b/src/plugins/projectexplorer/gcctoolchain.cpp index 22054d6828a..81a0b015b0f 100644 --- a/src/plugins/projectexplorer/gcctoolchain.cpp +++ b/src/plugins/projectexplorer/gcctoolchain.cpp @@ -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(); } diff --git a/src/plugins/projectexplorer/gcctoolchain.h b/src/plugins/projectexplorer/gcctoolchain.h index 4e22ba05efd..72c693ea2cc 100644 --- a/src/plugins/projectexplorer/gcctoolchain.h +++ b/src/plugins/projectexplorer/gcctoolchain.h @@ -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; diff --git a/src/plugins/projectexplorer/gnumakeparser.cpp b/src/plugins/projectexplorer/gnumakeparser.cpp index 53d0db07a8a..6bb7442e0ca 100644 --- a/src/plugins/projectexplorer/gnumakeparser.cpp +++ b/src/plugins/projectexplorer/gnumakeparser.cpp @@ -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) diff --git a/src/plugins/projectexplorer/gnumakeparser.h b/src/plugins/projectexplorer/gnumakeparser.h index 5a2f247a4fd..21c77213b19 100644 --- a/src/plugins/projectexplorer/gnumakeparser.h +++ b/src/plugins/projectexplorer/gnumakeparser.h @@ -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); diff --git a/src/plugins/projectexplorer/ioutputparser.cpp b/src/plugins/projectexplorer/ioutputparser.cpp index 98f39fb4db1..72d4e28fe11 100644 --- a/src/plugins/projectexplorer/ioutputparser.cpp +++ b/src/plugins/projectexplorer/ioutputparser.cpp @@ -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 "task.h" +#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) -{ - d->searchDirs << dir; -} - -void OutputTaskParser::dropSearchDir(const Utils::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 Utils::FilePaths OutputTaskParser::searchDirectories() const -{ - 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) +const QList<OutputTaskParser::TaskInfo> OutputTaskParser::taskInfo() const { - for (OutputTaskParser * const p : qAsConst(d->lineParsers)) - p->addSearchDir(dir); + return d->scheduledTasks; } -void IOutputParser::dropSearchDir(const Utils::FilePath &dir) +void OutputTaskParser::scheduleTask(const Task &task, int outputLines, int skippedLines) { - for (OutputTaskParser * const p : qAsConst(d->lineParsers)) - p->dropSearchDir(dir); + 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); } -Utils::OutputFormat IOutputParser::outputTypeForParser(const OutputTaskParser *parser, - Utils::OutputFormat type) const +void OutputTaskParser::runPostPrintActions() { - 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 diff --git a/src/plugins/projectexplorer/ioutputparser.h b/src/plugins/projectexplorer/ioutputparser.h index efe717f1f0e..9a8ce59be1b 100644 --- a/src/plugins/projectexplorer/ioutputparser.h +++ b/src/plugins/projectexplorer/ioutputparser.h @@ -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 diff --git a/src/plugins/projectexplorer/kit.cpp b/src/plugins/projectexplorer/kit.cpp index 0cd845e5d39..e8ff3cab483 100644 --- a/src/plugins/projectexplorer/kit.cpp +++ b/src/plugins/projectexplorer/kit.cpp @@ -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; diff --git a/src/plugins/projectexplorer/kit.h b/src/plugins/projectexplorer/kit.h index 8408eab788a..871e2265682 100644 --- a/src/plugins/projectexplorer/kit.h +++ b/src/plugins/projectexplorer/kit.h @@ -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; diff --git a/src/plugins/projectexplorer/kitinformation.cpp b/src/plugins/projectexplorer/kitinformation.cpp index 590a777b143..9d67eed17a0 100644 --- a/src/plugins/projectexplorer/kitinformation.cpp +++ b/src/plugins/projectexplorer/kitinformation.cpp @@ -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)) diff --git a/src/plugins/projectexplorer/kitinformation.h b/src/plugins/projectexplorer/kitinformation.h index 6b5c77dc979..d8558b88616 100644 --- a/src/plugins/projectexplorer/kitinformation.h +++ b/src/plugins/projectexplorer/kitinformation.h @@ -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(); diff --git a/src/plugins/projectexplorer/kitmanager.cpp b/src/plugins/projectexplorer/kitmanager.cpp index 5d82fe6e284..1eab4633f54 100644 --- a/src/plugins/projectexplorer/kitmanager.cpp +++ b/src/plugins/projectexplorer/kitmanager.cpp @@ -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 {}; diff --git a/src/plugins/projectexplorer/kitmanager.h b/src/plugins/projectexplorer/kitmanager.h index 31565eec5c5..f55380c5733 100644 --- a/src/plugins/projectexplorer/kitmanager.h +++ b/src/plugins/projectexplorer/kitmanager.h @@ -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; diff --git a/src/plugins/projectexplorer/ldparser.cpp b/src/plugins/projectexplorer/ldparser.cpp index 55ef296bf82..135401dd1c4 100644 --- a/src/plugins/projectexplorer/ldparser.cpp +++ b/src/plugins/projectexplorer/ldparser.cpp @@ -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); } diff --git a/src/plugins/projectexplorer/ldparser.h b/src/plugins/projectexplorer/ldparser.h index fc3d9590759..56f94f52c70 100644 --- a/src/plugins/projectexplorer/ldparser.h +++ b/src/plugins/projectexplorer/ldparser.h @@ -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; diff --git a/src/plugins/projectexplorer/linuxiccparser.cpp b/src/plugins/projectexplorer/linuxiccparser.cpp index 812f10961ff..49bdbcdc2ad 100644 --- a/src/plugins/projectexplorer/linuxiccparser.cpp +++ b/src/plugins/projectexplorer/linuxiccparser.cpp @@ -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(); } diff --git a/src/plugins/projectexplorer/linuxiccparser.h b/src/plugins/projectexplorer/linuxiccparser.h index f5a1dd0096f..44ff094c05e 100644 --- a/src/plugins/projectexplorer/linuxiccparser.h +++ b/src/plugins/projectexplorer/linuxiccparser.h @@ -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; diff --git a/src/plugins/projectexplorer/lldparser.cpp b/src/plugins/projectexplorer/lldparser.cpp index c9384614897..aee20ea7957 100644 --- a/src/plugins/projectexplorer/lldparser.cpp +++ b/src/plugins/projectexplorer/lldparser.cpp @@ -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; } diff --git a/src/plugins/projectexplorer/lldparser.h b/src/plugins/projectexplorer/lldparser.h index d071d0264c2..f9bb4388aa9 100644 --- a/src/plugins/projectexplorer/lldparser.h +++ b/src/plugins/projectexplorer/lldparser.h @@ -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 diff --git a/src/plugins/projectexplorer/makestep.cpp b/src/plugins/projectexplorer/makestep.cpp index cd873d5ea75..ec5cafe6626 100644 --- a/src/plugins/projectexplorer/makestep.cpp +++ b/src/plugins/projectexplorer/makestep.cpp @@ -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; diff --git a/src/plugins/projectexplorer/makestep.h b/src/plugins/projectexplorer/makestep.h index bd02090bec1..decc0b0fccd 100644 --- a/src/plugins/projectexplorer/makestep.h +++ b/src/plugins/projectexplorer/makestep.h @@ -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); diff --git a/src/plugins/projectexplorer/msvcparser.cpp b/src/plugins/projectexplorer/msvcparser.cpp index 151fb52faa2..ac68bd3b679 100644 --- a/src/plugins/projectexplorer/msvcparser.cpp +++ b/src/plugins/projectexplorer/msvcparser.cpp @@ -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(); } } diff --git a/src/plugins/projectexplorer/msvcparser.h b/src/plugins/projectexplorer/msvcparser.h index 3f71263ee89..f2b8d47d97c 100644 --- a/src/plugins/projectexplorer/msvcparser.h +++ b/src/plugins/projectexplorer/msvcparser.h @@ -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; diff --git a/src/plugins/projectexplorer/msvctoolchain.cpp b/src/plugins/projectexplorer/msvctoolchain.cpp index f486a52202f..a2332739bc1 100644 --- a/src/plugins/projectexplorer/msvctoolchain.cpp +++ b/src/plugins/projectexplorer/msvctoolchain.cpp @@ -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}; } diff --git a/src/plugins/projectexplorer/msvctoolchain.h b/src/plugins/projectexplorer/msvctoolchain.h index 4d40c0a25d8..7731598cb1f 100644 --- a/src/plugins/projectexplorer/msvctoolchain.h +++ b/src/plugins/projectexplorer/msvctoolchain.h @@ -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; diff --git a/src/plugins/projectexplorer/osparser.cpp b/src/plugins/projectexplorer/osparser.cpp index 3ea1dcc1199..9a1f11a0bad 100644 --- a/src/plugins/projectexplorer/osparser.cpp +++ b/src/plugins/projectexplorer/osparser.cpp @@ -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; } } diff --git a/src/plugins/projectexplorer/osparser.h b/src/plugins/projectexplorer/osparser.h index f33551648ab..d3e97eee6e3 100644 --- a/src/plugins/projectexplorer/osparser.h +++ b/src/plugins/projectexplorer/osparser.h @@ -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; diff --git a/src/plugins/projectexplorer/outputparser_test.cpp b/src/plugins/projectexplorer/outputparser_test.cpp index 8caec11242e..8516a84de08 100644 --- a/src/plugins/projectexplorer/outputparser_test.cpp +++ b/src/plugins/projectexplorer/outputparser_test.cpp @@ -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 diff --git a/src/plugins/projectexplorer/outputparser_test.h b/src/plugins/projectexplorer/outputparser_test.h index 86dd0481343..b41b9a1f87a 100644 --- a/src/plugins/projectexplorer/outputparser_test.h +++ b/src/plugins/projectexplorer/outputparser_test.h @@ -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; }; diff --git a/src/plugins/projectexplorer/parseissuesdialog.cpp b/src/plugins/projectexplorer/parseissuesdialog.cpp index b1a38607e35..5a53574d93f 100644 --- a/src/plugins/projectexplorer/parseissuesdialog.cpp +++ b/src/plugins/projectexplorer/parseissuesdialog.cpp @@ -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"), diff --git a/src/plugins/projectexplorer/processstep.cpp b/src/plugins/projectexplorer/processstep.cpp index ea8de1cb3b5..035b816ff07 100644 --- a/src/plugins/projectexplorer/processstep.cpp +++ b/src/plugins/projectexplorer/processstep.cpp @@ -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(); diff --git a/src/plugins/projectexplorer/projectexplorer.pro b/src/plugins/projectexplorer/projectexplorer.pro index 4f9b5dd4f7a..4a8f9621989 100644 --- a/src/plugins/projectexplorer/projectexplorer.pro +++ b/src/plugins/projectexplorer/projectexplorer.pro @@ -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 \ diff --git a/src/plugins/projectexplorer/projectexplorer.qbs b/src/plugins/projectexplorer/projectexplorer.qbs index 6398533ca43..38efedd2102 100644 --- a/src/plugins/projectexplorer/projectexplorer.qbs +++ b/src/plugins/projectexplorer/projectexplorer.qbs @@ -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", diff --git a/src/plugins/projectexplorer/toolchain.h b/src/plugins/projectexplorer/toolchain.h index cc98793bded..b2dbc7ca9a4 100644 --- a/src/plugins/projectexplorer/toolchain.h +++ b/src/plugins/projectexplorer/toolchain.h @@ -47,6 +47,8 @@ #include <functional> #include <memory> +namespace Utils { class OutputLineParser; } + namespace ProjectExplorer { namespace Internal { class ToolChainPrivate; } @@ -64,7 +66,6 @@ QString languageId(Language l); } // namespace Deprecated class Abi; -class OutputTaskParser; class ToolChainConfigWidget; class ToolChainFactory; class Kit; @@ -150,7 +151,7 @@ public: Core::Id language() const; virtual Utils::FilePath compilerCommand() const = 0; - virtual QList<OutputTaskParser *> createOutputParsers() const = 0; + virtual QList<Utils::OutputLineParser *> createOutputParsers() const = 0; virtual bool operator ==(const ToolChain &) const; diff --git a/src/plugins/projectexplorer/toolchainsettingsaccessor.cpp b/src/plugins/projectexplorer/toolchainsettingsaccessor.cpp index 43c9655d836..dd6e540b1aa 100644 --- a/src/plugins/projectexplorer/toolchainsettingsaccessor.cpp +++ b/src/plugins/projectexplorer/toolchainsettingsaccessor.cpp @@ -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)) diff --git a/src/plugins/projectexplorer/xcodebuildparser.cpp b/src/plugins/projectexplorer/xcodebuildparser.cpp index 4b5d9e4c7ef..9b6486e5d5a 100644 --- a/src/plugins/projectexplorer/xcodebuildparser.cpp +++ b/src/plugins/projectexplorer/xcodebuildparser.cpp @@ -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; diff --git a/src/plugins/projectexplorer/xcodebuildparser.h b/src/plugins/projectexplorer/xcodebuildparser.h index 5c91315d74a..9342cd37854 100644 --- a/src/plugins/projectexplorer/xcodebuildparser.h +++ b/src/plugins/projectexplorer/xcodebuildparser.h @@ -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; } diff --git a/src/plugins/python/pythonrunconfiguration.cpp b/src/plugins/python/pythonrunconfiguration.cpp index 0e43cb3eea1..42dad98cb10 100644 --- a/src/plugins/python/pythonrunconfiguration.cpp +++ b/src/plugins/python/pythonrunconfiguration.cpp @@ -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; diff --git a/src/plugins/qbsprojectmanager/qbsbuildstep.cpp b/src/plugins/qbsprojectmanager/qbsbuildstep.cpp index d403ad05895..062f48e15bf 100644 --- a/src/plugins/qbsprojectmanager/qbsbuildstep.cpp +++ b/src/plugins/qbsprojectmanager/qbsbuildstep.cpp @@ -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 diff --git a/src/plugins/qbsprojectmanager/qbsbuildstep.h b/src/plugins/qbsprojectmanager/qbsbuildstep.h index f0906936b64..d257662bdb2 100644 --- a/src/plugins/qbsprojectmanager/qbsbuildstep.h +++ b/src/plugins/qbsprojectmanager/qbsbuildstep.h @@ -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; diff --git a/src/plugins/qbsprojectmanager/qbscleanstep.cpp b/src/plugins/qbsprojectmanager/qbscleanstep.cpp index 07adf568d24..44d33728960 100644 --- a/src/plugins/qbsprojectmanager/qbscleanstep.cpp +++ b/src/plugins/qbsprojectmanager/qbscleanstep.cpp @@ -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); } // -------------------------------------------------------------------- diff --git a/src/plugins/qbsprojectmanager/qbsinstallstep.cpp b/src/plugins/qbsprojectmanager/qbsinstallstep.cpp index f3e3164e1e3..9ad460e48e5 100644 --- a/src/plugins/qbsprojectmanager/qbsinstallstep.cpp +++ b/src/plugins/qbsprojectmanager/qbsinstallstep.cpp @@ -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) diff --git a/src/plugins/qmakeprojectmanager/qmakemakestep.cpp b/src/plugins/qmakeprojectmanager/qmakemakestep.cpp index 6ff538f70ad..8a105d66660 100644 --- a/src/plugins/qmakeprojectmanager/qmakemakestep.cpp +++ b/src/plugins/qmakeprojectmanager/qmakemakestep.cpp @@ -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) { diff --git a/src/plugins/qmakeprojectmanager/qmakemakestep.h b/src/plugins/qmakeprojectmanager/qmakemakestep.h index 29e5898e01c..ea4fc72b66f 100644 --- a/src/plugins/qmakeprojectmanager/qmakemakestep.h +++ b/src/plugins/qmakeprojectmanager/qmakemakestep.h @@ -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; diff --git a/src/plugins/qmakeprojectmanager/qmakeparser.cpp b/src/plugins/qmakeprojectmanager/qmakeparser.cpp index 20633f397df..64e08bf675a 100644 --- a/src/plugins/qmakeprojectmanager/qmakeparser.cpp +++ b/src/plugins/qmakeprojectmanager/qmakeparser.cpp @@ -40,7 +40,7 @@ QMakeParser::QMakeParser() : m_error(QLatin1String("^(.+):(\\d+):\\s(.+)$")) m_error.setMinimal(true); } -OutputTaskParser::Status QMakeParser::handleLine(const QString &line, OutputFormat type) +OutputLineParser::Result QMakeParser::handleLine(const QString &line, OutputFormat type) { if (type != Utils::StdErrFormat) return Status::NotHandled; @@ -49,11 +49,14 @@ OutputTaskParser::Status QMakeParser::handleLine(const QString &line, OutputForm QString fileName = m_error.cap(1); Task::TaskType type = Task::Error; const QString description = m_error.cap(3); + int fileNameOffset = m_error.pos(1); if (fileName.startsWith(QLatin1String("WARNING: "))) { type = Task::Warning; fileName = fileName.mid(9); + fileNameOffset += 9; } else if (fileName.startsWith(QLatin1String("ERROR: "))) { fileName = fileName.mid(7); + fileNameOffset += 7; } if (description.startsWith(QLatin1String("note:"), Qt::CaseInsensitive)) type = Task::Unknown; @@ -61,23 +64,25 @@ OutputTaskParser::Status QMakeParser::handleLine(const QString &line, OutputForm type = Task::Warning; else if (description.startsWith(QLatin1String("error:"), Qt::CaseInsensitive)) type = Task::Error; - emit addTask(BuildSystemTask(type, - description, - absoluteFilePath(FilePath::fromUserInput(fileName)), - m_error.cap(2).toInt() /* line */), - 1); - return Status::Done; + + BuildSystemTask t(type, description, absoluteFilePath(FilePath::fromUserInput(fileName)), + m_error.cap(2).toInt() /* line */); + LinkSpecs linkSpecs; + addLinkSpecForAbsoluteFilePath(linkSpecs, t.file, t.line, fileNameOffset, + fileName.length()); + scheduleTask(t, 1); + return {Status::Done, linkSpecs}; } if (lne.startsWith(QLatin1String("Project ERROR: ")) || lne.startsWith(QLatin1String("ERROR: "))) { const QString description = lne.mid(lne.indexOf(QLatin1Char(':')) + 2); - emit addTask(BuildSystemTask(Task::Error, description), 1); + scheduleTask(BuildSystemTask(Task::Error, description), 1); return Status::Done; } if (lne.startsWith(QLatin1String("Project WARNING: ")) || lne.startsWith(QLatin1String("WARNING: "))) { const QString description = lne.mid(lne.indexOf(QLatin1Char(':')) + 2); - emit addTask(BuildSystemTask(Task::Warning, description), 1); + scheduleTask(BuildSystemTask(Task::Warning, description), 1); return Status::Done; } return Status::NotHandled; @@ -174,7 +179,7 @@ void QmakeProjectManagerPlugin::testQmakeOutputParsers_data() << (Tasks() << BuildSystemTask(Task::Unknown, "Note: No relevant classes found. No output generated.", - FilePath::fromUserInput("/home/qtwebkithelpviewer.h"), 0)) + FilePath::fromUserInput("/home/qtwebkithelpviewer.h"), -1)) << QString(); } diff --git a/src/plugins/qmakeprojectmanager/qmakeparser.h b/src/plugins/qmakeprojectmanager/qmakeparser.h index 14c5b20d88f..210e5e576c9 100644 --- a/src/plugins/qmakeprojectmanager/qmakeparser.h +++ b/src/plugins/qmakeprojectmanager/qmakeparser.h @@ -41,7 +41,7 @@ public: QMakeParser(); private: - Status handleLine(const QString &line, Utils::OutputFormat type) override; + Result handleLine(const QString &line, Utils::OutputFormat type) override; QRegExp m_error; }; diff --git a/src/plugins/qmakeprojectmanager/qmakestep.cpp b/src/plugins/qmakeprojectmanager/qmakestep.cpp index 7d1a418249e..424e12f5437 100644 --- a/src/plugins/qmakeprojectmanager/qmakestep.cpp +++ b/src/plugins/qmakeprojectmanager/qmakestep.cpp @@ -225,8 +225,6 @@ bool QMakeStep::init() pp->setWorkingDirectory(workingDirectory); pp->setEnvironment(qmakeBc->environment()); - setOutputParser(new QMakeParser); - QmakeProFileNode *node = static_cast<QmakeProFileNode *>(qmakeBc->project()->rootProjectNode()); if (qmakeBc->subNodeBuild()) node = qmakeBc->subNodeBuild(); @@ -254,6 +252,13 @@ bool QMakeStep::init() return AbstractProcessStep::init(); } +void QMakeStep::setupOutputFormatter(OutputFormatter *formatter) +{ + formatter->addLineParser(new QMakeParser); + m_outputFormatter = formatter; + AbstractProcessStep::setupOutputFormatter(formatter); +} + void QMakeStep::doRun() { if (m_scriptTemplate) { @@ -332,7 +337,7 @@ void QMakeStep::runNextCommand() case State::IDLE: return; case State::RUN_QMAKE: - setOutputParser(new QMakeParser); + m_outputFormatter->setLineParsers({new QMakeParser}); m_nextState = (m_runMakeQmake ? State::RUN_MAKE_QMAKE_ALL : State::POST_PROCESS); startOneCommand(m_qmakeCommand); return; @@ -340,7 +345,7 @@ void QMakeStep::runNextCommand() { auto *parser = new GnuMakeParser; parser->addSearchDir(processParameters()->workingDirectory()); - setOutputParser(parser); + m_outputFormatter->setLineParsers({parser}); m_nextState = State::POST_PROCESS; startOneCommand(m_makeCommand); } diff --git a/src/plugins/qmakeprojectmanager/qmakestep.h b/src/plugins/qmakeprojectmanager/qmakestep.h index 6475757e167..05710e10836 100644 --- a/src/plugins/qmakeprojectmanager/qmakestep.h +++ b/src/plugins/qmakeprojectmanager/qmakestep.h @@ -121,6 +121,7 @@ public: QmakeBuildConfiguration *qmakeBuildConfiguration() const; QmakeBuildSystem *qmakeBuildSystem() const; bool init() override; + void setupOutputFormatter(Utils::OutputFormatter *formatter) override; void doRun() override; ProjectExplorer::BuildStepConfigWidget *createConfigWidget() override; void setForced(bool b); @@ -192,6 +193,7 @@ private: bool m_runMakeQmake = false; bool m_scriptTemplate = false; QStringList m_selectedAbis; + Utils::OutputFormatter *m_outputFormatter = nullptr; }; diff --git a/src/plugins/qtsupport/qtkitinformation.cpp b/src/plugins/qtsupport/qtkitinformation.cpp index 61f554b99d8..070f8c4860f 100644 --- a/src/plugins/qtsupport/qtkitinformation.cpp +++ b/src/plugins/qtsupport/qtkitinformation.cpp @@ -288,7 +288,7 @@ void QtKitAspect::addToEnvironment(const ProjectExplorer::Kit *k, Utils::Environ version->addToEnvironment(k, env); } -QList<OutputTaskParser *> QtKitAspect::createOutputParsers(const Kit *k) const +QList<Utils::OutputLineParser *> QtKitAspect::createOutputParsers(const Kit *k) const { if (qtVersion(k)) return {new Internal::QtTestParser, new QtParser}; diff --git a/src/plugins/qtsupport/qtkitinformation.h b/src/plugins/qtsupport/qtkitinformation.h index dacd3e6f51e..b494ec09a4a 100644 --- a/src/plugins/qtsupport/qtkitinformation.h +++ b/src/plugins/qtsupport/qtkitinformation.h @@ -54,7 +54,7 @@ public: ItemList toUserOutput(const ProjectExplorer::Kit *k) const override; void addToEnvironment(const ProjectExplorer::Kit *k, Utils::Environment &env) const override; - QList<ProjectExplorer::OutputTaskParser *> createOutputParsers(const ProjectExplorer::Kit *k) const override; + QList<Utils::OutputLineParser *> createOutputParsers(const ProjectExplorer::Kit *k) const override; void addToMacroExpander(ProjectExplorer::Kit *kit, Utils::MacroExpander *expander) const override; static Core::Id id(); diff --git a/src/plugins/qtsupport/qtoutputformatter.cpp b/src/plugins/qtsupport/qtoutputformatter.cpp index 66ebffc433e..9c1803bf3d6 100644 --- a/src/plugins/qtsupport/qtoutputformatter.cpp +++ b/src/plugins/qtsupport/qtoutputformatter.cpp @@ -79,7 +79,7 @@ public: FileInProjectFinder projectFinder; }; -class QtOutputLineParser : public QObject, public OutputLineParser +class QtOutputLineParser : public OutputLineParser { public: explicit QtOutputLineParser(Target *target); @@ -482,11 +482,11 @@ void QtSupportPlugin::testQtOutputFormatter_appendMessage() QFETCH(QTextCharFormat, inputFormat); QFETCH(QTextCharFormat, outputFormat); if (outputFormat == QTextCharFormat()) - outputFormat = formatter.charFormat(DebugFormat); + outputFormat = formatter.charFormat(StdOutFormat); if (inputFormat != QTextCharFormat()) formatter.overrideTextCharFormat(inputFormat); - formatter.appendMessage(inputText, DebugFormat); + formatter.appendMessage(inputText, StdOutFormat); formatter.flush(); QCOMPARE(edit.toPlainText(), outputText); @@ -509,7 +509,7 @@ void QtSupportPlugin::testQtOutputFormatter_appendMixedAssertAndAnsi() "file://test.cpp:123 " "Blue\n"; - formatter.appendMessage(inputText, DebugFormat); + formatter.appendMessage(inputText, StdOutFormat); QCOMPARE(edit.toPlainText(), outputText); diff --git a/src/plugins/qtsupport/qtparser.cpp b/src/plugins/qtsupport/qtparser.cpp index 1c53feb247d..b376e9911c4 100644 --- a/src/plugins/qtsupport/qtparser.cpp +++ b/src/plugins/qtsupport/qtparser.cpp @@ -28,9 +28,18 @@ #include <projectexplorer/task.h> #include <projectexplorer/projectexplorerconstants.h> -using namespace QtSupport; +#include <QFileInfo> + +#ifdef WITH_TESTS +#include "qtsupportplugin.h" +#include <projectexplorer/outputparser_test.h> +#include <QTest> +#endif + using namespace ProjectExplorer; +namespace QtSupport { + // opt. drive letter + filename: (2 brackets) #define FILE_PATTERN "^(([A-Za-z]:)?[^:]+\\.[^:]+)" @@ -43,7 +52,7 @@ QtParser::QtParser() : m_translationRegExp.setMinimal(true); } -OutputTaskParser::Status QtParser::handleLine(const QString &line, Utils::OutputFormat type) +Utils::OutputLineParser::Result QtParser::handleLine(const QString &line, Utils::OutputFormat type) { if (type != Utils::StdErrFormat) return Status::NotHandled; @@ -60,20 +69,25 @@ OutputTaskParser::Status QtParser::handleLine(const QString &line, Utils::Output type = Task::Warning; if (level.compare(QLatin1String("Note"), Qt::CaseInsensitive) == 0) type = Task::Unknown; - CompileTask task(type, m_mocRegExp.cap(5).trimmed() /* description */, - absoluteFilePath(Utils::FilePath::fromUserInput(m_mocRegExp.cap(1))), - lineno); - emit addTask(task, 1); - return Status::Done; + LinkSpecs linkSpecs; + const Utils::FilePath file + = absoluteFilePath(Utils::FilePath::fromUserInput(m_mocRegExp.cap(1))); + addLinkSpecForAbsoluteFilePath(linkSpecs, file, lineno, m_mocRegExp, 1); + CompileTask task(type, m_mocRegExp.cap(5).trimmed() /* description */, file, lineno); + scheduleTask(task, 1); + return {Status::Done, linkSpecs}; } if (m_translationRegExp.indexIn(lne) > -1) { Task::TaskType type = Task::Warning; if (m_translationRegExp.cap(1) == QLatin1String("Error")) type = Task::Error; - CompileTask task(type, m_translationRegExp.cap(2), - absoluteFilePath(Utils::FilePath::fromUserInput(m_translationRegExp.cap(3)))); - emit addTask(task, 1); - return Status::Done; + LinkSpecs linkSpecs; + const Utils::FilePath file + = absoluteFilePath(Utils::FilePath::fromUserInput(m_translationRegExp.cap(3))); + addLinkSpecForAbsoluteFilePath(linkSpecs, file, 0, m_translationRegExp, 3); + CompileTask task(type, m_translationRegExp.cap(2), file); + scheduleTask(task, 1); + return {Status::Done, linkSpecs}; } return Status::NotHandled; } @@ -81,14 +95,7 @@ OutputTaskParser::Status QtParser::handleLine(const QString &line, Utils::Output // Unit tests: #ifdef WITH_TESTS -# include <QTest> - -# include "qtsupportplugin.h" -# include <projectexplorer/projectexplorerconstants.h> -# include <projectexplorer/outputparser_test.h> - -using namespace ProjectExplorer; -using namespace QtSupport::Internal; +namespace Internal { void QtSupportPlugin::testQtOutputParser_data() { @@ -139,7 +146,7 @@ void QtSupportPlugin::testQtOutputParser_data() << QString() << QString() << (Tasks() << CompileTask(Task::Warning, QLatin1String("No relevant classes found. No output generated."), - Utils::FilePath::fromUserInput(QLatin1String("..\\untitled\\errorfile.h")), 0)) + Utils::FilePath::fromUserInput(QLatin1String("..\\untitled\\errorfile.h")), -1)) << QString(); QTest::newRow("moc warning 2") << QString::fromLatin1("c:\\code\\test.h(96): Warning: Property declaration ) has no READ accessor function. The property will be invalid.") @@ -155,7 +162,7 @@ void QtSupportPlugin::testQtOutputParser_data() << QString() << QString() << (Tasks() << CompileTask(Task::Unknown, QLatin1String("No relevant classes found. No output generated."), - Utils::FilePath::fromUserInput(QLatin1String("/home/qtwebkithelpviewer.h")), 0)) + Utils::FilePath::fromUserInput(QLatin1String("/home/qtwebkithelpviewer.h")), -1)) << QString(); QTest::newRow("ninja with moc") << QString::fromLatin1("E:/sandbox/creator/loaden/src/libs/utils/iwelcomepage.h(54): Error: Undefined interface") @@ -188,4 +195,8 @@ void QtSupportPlugin::testQtOutputParser() testbench.testParsing(input, inputChannel, tasks, childStdOutLines, childStdErrLines, outputLines); } + +} // namespace Internal #endif + +} // namespace QtSupport diff --git a/src/plugins/qtsupport/qtparser.h b/src/plugins/qtsupport/qtparser.h index 5f891a2cf31..e70661f6483 100644 --- a/src/plugins/qtsupport/qtparser.h +++ b/src/plugins/qtsupport/qtparser.h @@ -42,10 +42,10 @@ public: QtParser(); private: - Status handleLine(const QString &line, Utils::OutputFormat type) override; + Result handleLine(const QString &line, Utils::OutputFormat type) override; QRegExp m_mocRegExp; QRegExp m_translationRegExp; }; -} // namespace ProjectExplorer +} // namespace QtSupport diff --git a/src/plugins/qtsupport/qttestparser.cpp b/src/plugins/qtsupport/qttestparser.cpp index cf75303658c..1e8a572f4a6 100644 --- a/src/plugins/qtsupport/qttestparser.cpp +++ b/src/plugins/qtsupport/qttestparser.cpp @@ -47,7 +47,7 @@ using namespace Utils; namespace QtSupport { namespace Internal { -OutputTaskParser::Status QtTestParser::handleLine(const QString &line, OutputFormat type) +OutputLineParser::Result QtTestParser::handleLine(const QString &line, OutputFormat type) { if (type != StdOutFormat) return Status::NotHandled; @@ -69,11 +69,14 @@ OutputTaskParser::Status QtTestParser::handleLine(const QString &line, OutputFor QTC_CHECK(locationPattern.isValid()); const QRegularExpressionMatch match = locationPattern.match(theLine); if (match.hasMatch()) { + LinkSpecs linkSpecs; m_currentTask.file = absoluteFilePath(FilePath::fromString( QDir::fromNativeSeparators(match.captured("file")))); m_currentTask.line = match.captured("line").toInt(); + addLinkSpecForAbsoluteFilePath(linkSpecs, m_currentTask.file, m_currentTask.line, match, + "file"); emitCurrentTask(); - return Status::Done; + return {Status::Done, linkSpecs}; } m_currentTask.description.append('\n').append(theLine); return Status::InProgress; @@ -82,7 +85,7 @@ OutputTaskParser::Status QtTestParser::handleLine(const QString &line, OutputFor void QtTestParser::emitCurrentTask() { if (!m_currentTask.isNull()) { - emit addTask(m_currentTask); + scheduleTask(m_currentTask, 1); m_currentTask.clear(); } } diff --git a/src/plugins/qtsupport/qttestparser.h b/src/plugins/qtsupport/qttestparser.h index 2ed4e78bf9a..8e76eaba40b 100644 --- a/src/plugins/qtsupport/qttestparser.h +++ b/src/plugins/qtsupport/qttestparser.h @@ -35,7 +35,7 @@ class QtTestParser : public ProjectExplorer::OutputTaskParser { Q_OBJECT private: - Status handleLine(const QString &line, Utils::OutputFormat type) override; + Result handleLine(const QString &line, Utils::OutputFormat type) override; void flush() override { emitCurrentTask(); } void emitCurrentTask(); diff --git a/src/plugins/remotelinux/abstractremotelinuxdeploystep.cpp b/src/plugins/remotelinux/abstractremotelinuxdeploystep.cpp index 6a552296d10..7d24e38fb63 100644 --- a/src/plugins/remotelinux/abstractremotelinuxdeploystep.cpp +++ b/src/plugins/remotelinux/abstractremotelinuxdeploystep.cpp @@ -139,15 +139,15 @@ void AbstractRemoteLinuxDeployStep::handleProgressMessage(const QString &message void AbstractRemoteLinuxDeployStep::handleErrorMessage(const QString &message) { - emit addTask(DeploymentTask(Task::Error, message), 1); // TODO correct? emit addOutput(message, OutputFormat::ErrorMessage); + emit addTask(DeploymentTask(Task::Error, message), 1); // TODO correct? d->hasError = true; } void AbstractRemoteLinuxDeployStep::handleWarningMessage(const QString &message) { - emit addTask(DeploymentTask(Task::Warning, message), 1); // TODO correct? emit addOutput(message, OutputFormat::ErrorMessage); + emit addTask(DeploymentTask(Task::Warning, message), 1); // TODO correct? } void AbstractRemoteLinuxDeployStep::handleFinished() diff --git a/src/plugins/vcsbase/vcsoutputformatter.h b/src/plugins/vcsbase/vcsoutputformatter.h index e4ebfc6fee8..0b9486e2a91 100644 --- a/src/plugins/vcsbase/vcsoutputformatter.h +++ b/src/plugins/vcsbase/vcsoutputformatter.h @@ -31,7 +31,7 @@ QT_FORWARD_DECLARE_CLASS(QMenu) namespace VcsBase { -class VcsOutputLineParser : public QObject, public Utils::OutputLineParser +class VcsOutputLineParser : public Utils::OutputLineParser { Q_OBJECT public: diff --git a/src/plugins/winrt/winrtpackagedeploymentstep.cpp b/src/plugins/winrt/winrtpackagedeploymentstep.cpp index 32a474775bd..5c2759672a3 100644 --- a/src/plugins/winrt/winrtpackagedeploymentstep.cpp +++ b/src/plugins/winrt/winrtpackagedeploymentstep.cpp @@ -347,14 +347,14 @@ QString WinRtPackageDeploymentStep::defaultWinDeployQtArguments() const void WinRtPackageDeploymentStep::raiseError(const QString &errorMessage) { - emit addTask(DeploymentTask(Task::Error, errorMessage), 1); emit addOutput(errorMessage, BuildStep::OutputFormat::ErrorMessage); + emit addTask(DeploymentTask(Task::Error, errorMessage), 1); } void WinRtPackageDeploymentStep::raiseWarning(const QString &warningMessage) { - emit addTask(DeploymentTask(Task::Warning, warningMessage), 1); emit addOutput(warningMessage, BuildStep::OutputFormat::NormalMessage); + emit addTask(DeploymentTask(Task::Warning, warningMessage), 1); } bool WinRtPackageDeploymentStep::parseIconsAndExecutableFromManifest(QString manifestFileName, QStringList *icons, QString *executable) |