diff --git a/src/plugins/baremetal/baremetal.pro b/src/plugins/baremetal/baremetal.pro index 922ff50fe43..ac49b0041fc 100644 --- a/src/plugins/baremetal/baremetal.pro +++ b/src/plugins/baremetal/baremetal.pro @@ -25,6 +25,7 @@ SOURCES += baremetalplugin.cpp \ sdcctoolchain.cpp \ iarewparser.cpp \ keilparser.cpp \ + sdccparser.cpp \ HEADERS += baremetalplugin.h \ baremetalconstants.h \ @@ -49,6 +50,7 @@ HEADERS += baremetalplugin.h \ sdcctoolchain.h \ iarewparser.h \ keilparser.h \ + sdccparser.h \ RESOURCES += \ baremetal.qrc diff --git a/src/plugins/baremetal/baremetal.qbs b/src/plugins/baremetal/baremetal.qbs index 5c7694a8e8a..018c2dc23b7 100644 --- a/src/plugins/baremetal/baremetal.qbs +++ b/src/plugins/baremetal/baremetal.qbs @@ -37,5 +37,6 @@ QtcPlugin { "sdcctoolchain.cpp", "sdcctoolchain.h", "iarewparser.cpp", "iarewparser.h", "keilparser.cpp", "keilparser.h", + "sdccparser.cpp", "sdccparser.h", ] } diff --git a/src/plugins/baremetal/baremetalplugin.h b/src/plugins/baremetal/baremetalplugin.h index 151c63338e7..58e0763a796 100644 --- a/src/plugins/baremetal/baremetalplugin.h +++ b/src/plugins/baremetal/baremetalplugin.h @@ -49,6 +49,8 @@ private slots: void testIarOutputParsers(); void testKeilOutputParsers_data(); void testKeilOutputParsers(); + void testSdccOutputParsers_data(); + void testSdccOutputParsers(); #endif // WITH_TESTS }; diff --git a/src/plugins/baremetal/sdccparser.cpp b/src/plugins/baremetal/sdccparser.cpp new file mode 100644 index 00000000000..7f11896d425 --- /dev/null +++ b/src/plugins/baremetal/sdccparser.cpp @@ -0,0 +1,335 @@ +/**************************************************************************** +** +** Copyright (C) 2019 Denis Shienkov +** 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 "sdccparser.h" + +#include +#include + +#include +#include + +#include + +using namespace ProjectExplorer; + +namespace BareMetal { +namespace Internal { + +static Task::TaskType taskType(const QString &msgType) +{ + if (msgType == "warning" || msgType == "Warning") { + return Task::TaskType::Warning; + } else if (msgType == "error" || msgType == "Error" + || msgType == "syntax error") { + return Task::TaskType::Error; + } + return Task::TaskType::Unknown; +} + +SdccParser::SdccParser() +{ + setObjectName("SdccParser"); +} + +Core::Id SdccParser::id() +{ + return "BareMetal.OutputParser.Sdcc"; +} + +void SdccParser::newTask(const Task &task) +{ + doFlush(); + m_lastTask = task; + m_lines = 1; +} + +void SdccParser::amendDescription(const QString &desc) +{ + const int start = m_lastTask.description.count() + 1; + m_lastTask.description.append(QLatin1Char('\n')); + m_lastTask.description.append(desc); + + QTextLayout::FormatRange fr; + fr.start = start; + fr.length = m_lastTask.description.count() + 1; + fr.format.setFont(TextEditor::TextEditorSettings::fontSettings().font()); + fr.format.setFontStyleHint(QFont::Monospace); + m_lastTask.formats.append(fr); + + ++m_lines; +} + +void SdccParser::stdError(const QString &line) +{ + IOutputParser::stdError(line); + + const QString lne = rightTrimmed(line); + + QRegularExpression re; + QRegularExpressionMatch match; + + re.setPattern("^(.+\\.\\S+):(\\d+): (warning|error) (\\d+): (.+)$"); + match = re.match(lne); + if (match.hasMatch()) { + enum CaptureIndex { FilePathIndex = 1, LineNumberIndex, + MessageTypeIndex, MessageCodeIndex, MessageTextIndex }; + const Utils::FileName fileName = Utils::FileName::fromUserInput( + match.captured(FilePathIndex)); + const int lineno = match.captured(LineNumberIndex).toInt(); + const Task::TaskType type = taskType(match.captured(MessageTypeIndex)); + const QString descr = match.captured(MessageTextIndex); + const Task task(type, descr, fileName, lineno, Constants::TASK_CATEGORY_COMPILE); + newTask(task); + return; + } + + re.setPattern("^(.+\\.\\S+):(\\d+): (syntax error): (.+)$"); + match = re.match(lne); + if (match.hasMatch()) { + enum CaptureIndex { FilePathIndex = 1, LineNumberIndex, + MessageTypeIndex, MessageTextIndex }; + const Utils::FileName fileName = Utils::FileName::fromUserInput( + match.captured(FilePathIndex)); + const int lineno = match.captured(LineNumberIndex).toInt(); + const Task::TaskType type = taskType(match.captured(MessageTypeIndex)); + const QString descr = match.captured(MessageTextIndex); + const Task task(type, descr, fileName, lineno, Constants::TASK_CATEGORY_COMPILE); + newTask(task); + return; + } + + re.setPattern("^at (\\d+): (error) \\d+: (.+)$"); + match = re.match(lne); + if (match.hasMatch()) { + enum CaptureIndex { MessageCodeIndex = 1, MessageTypeIndex, MessageTextIndex }; + const Task::TaskType type = taskType(match.captured(MessageTypeIndex)); + const QString descr = match.captured(MessageTextIndex); + const Task task(type, descr, {}, -1, Constants::TASK_CATEGORY_COMPILE); + newTask(task); + return; + } + + re.setPattern("^\\?ASlink-(Warning|Error)-(.+)$"); + match = re.match(lne); + if (match.hasMatch()) { + enum CaptureIndex { MessageTypeIndex = 1, MessageTextIndex }; + const Task::TaskType type = taskType(match.captured(MessageTypeIndex)); + const QString descr = match.captured(MessageTextIndex); + const Task task(type, descr, {}, -1, Constants::TASK_CATEGORY_COMPILE); + newTask(task); + return; + } + + if (!m_lastTask.isNull()) { + amendDescription(lne); + return; + } + + doFlush(); +} + +void SdccParser::stdOutput(const QString &line) +{ + IOutputParser::stdOutput(line); +} + +void SdccParser::doFlush() +{ + if (m_lastTask.isNull()) + return; + + Task t = m_lastTask; + m_lastTask.clear(); + emit addTask(t, m_lines, 1); + m_lines = 0; +} + +} // namespace Internal +} // namespace BareMetal + +// Unit tests: + +#ifdef WITH_TESTS +#include "baremetalplugin.h" +#include +#include + +namespace BareMetal { +namespace Internal { + +void BareMetalPlugin::testSdccOutputParsers_data() +{ + QTest::addColumn("input"); + QTest::addColumn("inputChannel"); + QTest::addColumn("childStdOutLines"); + QTest::addColumn("childStdErrLines"); + QTest::addColumn >("tasks"); + QTest::addColumn("outputLines"); + + QTest::newRow("pass-through stdout") + << "Sometext" << OutputParserTester::STDOUT + << "Sometext\n" << QString() + << QList() + << QString(); + QTest::newRow("pass-through stderr") + << "Sometext" << OutputParserTester::STDERR + << QString() << "Sometext\n" + << QList() + << QString(); + + const Core::Id categoryCompile = Constants::TASK_CATEGORY_COMPILE; + + // Compiler messages. + + QTest::newRow("Compiler single line warning") + << QString::fromLatin1("c:\\foo\\main.c:63: warning 123: Some warning") + << OutputParserTester::STDERR + << QString() + << QString::fromLatin1("c:\\foo\\main.c:63: warning 123: Some warning\n") + << (QList() << Task(Task::Warning, + QLatin1String("Some warning"), + Utils::FileName::fromUserInput(QLatin1String("c:\\foo\\main.c")), + 63, + categoryCompile)) + << QString(); + + QTest::newRow("Compiler multi line warning") + << QString::fromLatin1("c:\\foo\\main.c:63: warning 123: Some warning\n" + "details #1\n" + " details #2") + << OutputParserTester::STDERR + << QString() + << QString::fromLatin1("c:\\foo\\main.c:63: warning 123: Some warning\n" + "details #1\n" + " details #2\n") + << (QList() << Task(Task::Warning, + QLatin1String("Some warning\n" + "details #1\n" + " details #2"), + Utils::FileName::fromUserInput(QLatin1String("c:\\foo\\main.c")), + 63, + categoryCompile)) + << QString(); + + QTest::newRow("Compiler single line error") + << QString::fromLatin1("c:\\foo\\main.c:63: error 123: Some error") + << OutputParserTester::STDERR + << QString() + << QString::fromLatin1("c:\\foo\\main.c:63: error 123: Some error\n") + << (QList() << Task(Task::Error, + QLatin1String("Some error"), + Utils::FileName::fromUserInput(QLatin1String("c:\\foo\\main.c")), + 63, + categoryCompile)) + << QString(); + + QTest::newRow("Compiler multi line error") + << QString::fromLatin1("c:\\foo\\main.c:63: error 123: Some error\n" + "details #1\n" + " details #2") + << OutputParserTester::STDERR + << QString() + << QString::fromLatin1("c:\\foo\\main.c:63: error 123: Some error\n" + "details #1\n" + " details #2\n") + << (QList() << Task(Task::Error, + QLatin1String("Some error\n" + "details #1\n" + " details #2"), + Utils::FileName::fromUserInput(QLatin1String("c:\\foo\\main.c")), + 63, + categoryCompile)) + << QString(); + + QTest::newRow("Compiler syntax error") + << QString::fromLatin1("c:\\foo\\main.c:63: syntax error: Some error") + << OutputParserTester::STDERR + << QString() + << QString::fromLatin1("c:\\foo\\main.c:63: syntax error: Some error\n") + << (QList() << Task(Task::Error, + QLatin1String("Some error"), + Utils::FileName::fromUserInput(QLatin1String("c:\\foo\\main.c")), + 63, + categoryCompile)) + << QString(); + + QTest::newRow("Compiler bad option error") + << QString::fromLatin1("at 1: error 123: Some error") + << OutputParserTester::STDERR + << QString() + << QString::fromLatin1("at 1: error 123: Some error\n") + << (QList() << Task(Task::Error, + QLatin1String("Some error"), + Utils::FileName(), + -1, + categoryCompile)) + << QString(); + + QTest::newRow("Linker warning") + << QString::fromLatin1("?ASlink-Warning-Couldn't find library 'foo.lib'") + << OutputParserTester::STDERR + << QString() + << QString::fromLatin1("?ASlink-Warning-Couldn't find library 'foo.lib'\n") + << (QList() << Task(Task::Warning, + QLatin1String("Couldn't find library 'foo.lib'"), + Utils::FileName(), + -1, + categoryCompile)) + << QString(); + + QTest::newRow("Linker error") + << QString::fromLatin1("?ASlink-Error- : \"foo.rel\"") + << OutputParserTester::STDERR + << QString() + << QString::fromLatin1("?ASlink-Error- : \"foo.rel\"\n") + << (QList() << Task(Task::Error, + QLatin1String(" : \"foo.rel\""), + Utils::FileName(), + -1, + categoryCompile)) + << QString(); +} + +void BareMetalPlugin::testSdccOutputParsers() +{ + OutputParserTester testbench; + testbench.appendOutputParser(new SdccParser); + QFETCH(QString, input); + QFETCH(OutputParserTester::Channel, inputChannel); + QFETCH(QList, tasks); + QFETCH(QString, childStdOutLines); + QFETCH(QString, childStdErrLines); + QFETCH(QString, outputLines); + + testbench.testParsing(input, inputChannel, + tasks, childStdOutLines, childStdErrLines, + outputLines); +} + +} // namespace Internal +} // namespace BareMetal + +#endif // WITH_TESTS diff --git a/src/plugins/baremetal/sdccparser.h b/src/plugins/baremetal/sdccparser.h new file mode 100644 index 00000000000..7bbdc307d8b --- /dev/null +++ b/src/plugins/baremetal/sdccparser.h @@ -0,0 +1,57 @@ +/**************************************************************************** +** +** Copyright (C) 2019 Denis Shienkov +** 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 +#include + +namespace BareMetal { +namespace Internal { + +// SdccParser + +class SdccParser final : public ProjectExplorer::IOutputParser +{ + Q_OBJECT + +public: + SdccParser(); + static Core::Id id(); + +private: + void newTask(const ProjectExplorer::Task &task); + void amendDescription(const QString &desc); + + void stdError(const QString &line) override; + void stdOutput(const QString &line) override; + void doFlush() override; + + ProjectExplorer::Task m_lastTask; + int m_lines = 0; +}; + +} // namespace Internal +} // namespace BareMetal diff --git a/src/plugins/baremetal/sdcctoolchain.cpp b/src/plugins/baremetal/sdcctoolchain.cpp index 7551e1744a4..a78e8a98f39 100644 --- a/src/plugins/baremetal/sdcctoolchain.cpp +++ b/src/plugins/baremetal/sdcctoolchain.cpp @@ -24,6 +24,7 @@ ****************************************************************************/ #include "baremetalconstants.h" +#include "sdccparser.h" #include "sdcctoolchain.h" #include @@ -327,7 +328,7 @@ void SdccToolChain::addToEnvironment(Environment &env) const IOutputParser *SdccToolChain::outputParser() const { - return nullptr; + return new SdccParser; } QVariantMap SdccToolChain::toMap() const