summaryrefslogtreecommitdiff
path: root/src/plugins/baremetal/sdccparser.cpp
diff options
context:
space:
mode:
authorDenis Shienkov <denis.shienkov@gmail.com>2019-05-07 14:25:21 +0300
committerDenis Shienkov <denis.shienkov@gmail.com>2019-05-08 09:22:10 +0000
commit6c87ddcea19fd5b2e02ace9a8a43b958c3e52c3c (patch)
tree34f5d14484c8c863d44821e12c456d1dc8d28f8b /src/plugins/baremetal/sdccparser.cpp
parentd0bb7ee72173c4d48e90d993c0b87f6ef8adfe4b (diff)
downloadqt-creator-6c87ddcea19fd5b2e02ace9a8a43b958c3e52c3c.tar.gz
bare-metal: Add command line parser for SDCC toolchain
This commit implements a parser which handles an often encountered compiler warnings and errors; a missed warnings and errors can be added later. Change-Id: Ie5cdd1193dae3ebed64d4f78316ae4175bdb0b22 Reviewed-by: Christian Kandeler <christian.kandeler@qt.io>
Diffstat (limited to 'src/plugins/baremetal/sdccparser.cpp')
-rw-r--r--src/plugins/baremetal/sdccparser.cpp335
1 files changed, 335 insertions, 0 deletions
diff --git a/src/plugins/baremetal/sdccparser.cpp b/src/plugins/baremetal/sdccparser.cpp
new file mode 100644
index 0000000000..7f11896d42
--- /dev/null
+++ b/src/plugins/baremetal/sdccparser.cpp
@@ -0,0 +1,335 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 Denis Shienkov <denis.shienkov@gmail.com>
+** 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 <projectexplorer/projectexplorerconstants.h>
+#include <projectexplorer/task.h>
+
+#include <texteditor/fontsettings.h>
+#include <texteditor/texteditorsettings.h>
+
+#include <QRegularExpression>
+
+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 <projectexplorer/outputparser_test.h>
+#include <QTest>
+
+namespace BareMetal {
+namespace Internal {
+
+void BareMetalPlugin::testSdccOutputParsers_data()
+{
+ QTest::addColumn<QString>("input");
+ QTest::addColumn<OutputParserTester::Channel>("inputChannel");
+ QTest::addColumn<QString>("childStdOutLines");
+ QTest::addColumn<QString>("childStdErrLines");
+ QTest::addColumn<QList<ProjectExplorer::Task> >("tasks");
+ QTest::addColumn<QString>("outputLines");
+
+ QTest::newRow("pass-through stdout")
+ << "Sometext" << OutputParserTester::STDOUT
+ << "Sometext\n" << QString()
+ << QList<Task>()
+ << QString();
+ QTest::newRow("pass-through stderr")
+ << "Sometext" << OutputParserTester::STDERR
+ << QString() << "Sometext\n"
+ << QList<Task>()
+ << 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(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(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(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(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(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(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(Task::Warning,
+ QLatin1String("Couldn't find library 'foo.lib'"),
+ Utils::FileName(),
+ -1,
+ categoryCompile))
+ << QString();
+
+ QTest::newRow("Linker error")
+ << QString::fromLatin1("?ASlink-Error-<cannot open> : \"foo.rel\"")
+ << OutputParserTester::STDERR
+ << QString()
+ << QString::fromLatin1("?ASlink-Error-<cannot open> : \"foo.rel\"\n")
+ << (QList<Task>() << Task(Task::Error,
+ QLatin1String("<cannot open> : \"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<Task>, 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