summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndre Hartmann <aha_1980@gmx.de>2013-08-05 21:30:41 +0300
committerAndré Hartmann <aha_1980@gmx.de>2013-08-08 11:50:08 +0200
commit4bc61ecac4701a0e4f4fa5c7e48986dcd687c11a (patch)
tree469d32aaa36072d9878c9669bb243e2c20b4e03c
parent9249175a4baf7dbc355fd14c1aa8dc74280eeeb8 (diff)
downloadqt-creator-4bc61ecac4701a0e4f4fa5c7e48986dcd687c11a.tar.gz
Custom Error Parser
Allow setting the following items from outside: * capture regular expression, * file name, line number and message capture position and * whether to parse stdout, stderr or both The parser functions can be unit-tested by running (Debug build of Qt Creator needed): qtcreator -test ProjectExplorer,testCustomOutputParsers The data is passed to the custom parser in CustomToolChain::outputParser(). The parser information is stored in toolchains.xml together with the custom toolchain. A configuration widget is provided to set up and test the regular expression against a sample error message. Change-Id: I6191df3c44432943e0aeb16c48d8e79d35845d2e Reviewed-by: Orgad Shaneh <orgads@gmail.com> Reviewed-by: Tobias Hunger <tobias.hunger@digia.com>
-rw-r--r--src/plugins/projectexplorer/customparser.cpp295
-rw-r--r--src/plugins/projectexplorer/customparser.h93
-rw-r--r--src/plugins/projectexplorer/customparserconfigdialog.cpp167
-rw-r--r--src/plugins/projectexplorer/customparserconfigdialog.h70
-rw-r--r--src/plugins/projectexplorer/customparserconfigdialog.ui244
-rw-r--r--src/plugins/projectexplorer/customtoolchain.cpp59
-rw-r--r--src/plugins/projectexplorer/customtoolchain.h12
-rw-r--r--src/plugins/projectexplorer/projectexplorer.h3
-rw-r--r--src/plugins/projectexplorer/projectexplorer.pro11
-rw-r--r--src/plugins/projectexplorer/projectexplorer.qbs5
10 files changed, 952 insertions, 7 deletions
diff --git a/src/plugins/projectexplorer/customparser.cpp b/src/plugins/projectexplorer/customparser.cpp
new file mode 100644
index 0000000000..15aebda487
--- /dev/null
+++ b/src/plugins/projectexplorer/customparser.cpp
@@ -0,0 +1,295 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Andre Hartmann.
+** Contact: aha_1980@gmx.de
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "customparser.h"
+#include "task.h"
+#include "projectexplorerconstants.h"
+
+#include <utils/qtcassert.h>
+
+#include <QString>
+
+using namespace ProjectExplorer;
+
+CustomParserSettings::CustomParserSettings() :
+ fileNameCap(1),
+ lineNumberCap(2),
+ messageCap(3)
+{ }
+
+bool CustomParserSettings::operator !=(const CustomParserSettings &other) const
+{
+ if (errorPattern == other.errorPattern)
+ return false;
+ if (fileNameCap == other.fileNameCap)
+ return false;
+ if (lineNumberCap == other.lineNumberCap)
+ return false;
+ if (messageCap == other.messageCap)
+ return false;
+
+ return true;
+}
+
+CustomParser::CustomParser(const CustomParserSettings &settings) :
+ m_parserChannels(ParseBothChannels)
+{
+ setObjectName(QLatin1String("CustomParser"));
+
+ setSettings(settings);
+}
+
+CustomParser::~CustomParser()
+{
+}
+
+void CustomParser::setErrorPattern(const QString &errorPattern)
+{
+ m_errorRegExp.setPattern(errorPattern);
+ m_errorRegExp.setMinimal(true);
+ QTC_CHECK(m_errorRegExp.isValid());
+}
+
+QString CustomParser::errorPattern() const
+{
+ return m_errorRegExp.pattern();
+}
+
+int CustomParser::lineNumberCap() const
+{
+ return m_lineNumberCap;
+}
+
+void CustomParser::setLineNumberCap(int lineNumberCap)
+{
+ m_lineNumberCap = lineNumberCap;
+}
+
+int CustomParser::fileNameCap() const
+{
+ return m_fileNameCap;
+}
+
+void CustomParser::setFileNameCap(int fileNameCap)
+{
+ m_fileNameCap = fileNameCap;
+}
+
+int CustomParser::messageCap() const
+{
+ return m_messageCap;
+}
+
+void CustomParser::setMessageCap(int messageCap)
+{
+ m_messageCap = messageCap;
+}
+
+void CustomParser::stdError(const QString &line)
+{
+ if (m_parserChannels & ParseStdErrChannel)
+ if (parseLine(line))
+ return;
+
+ IOutputParser::stdError(line);
+}
+
+void CustomParser::stdOutput(const QString &line)
+{
+ if (m_parserChannels & ParseStdOutChannel)
+ if (parseLine(line))
+ return;
+
+ IOutputParser::stdOutput(line);
+}
+
+void CustomParser::setSettings(const CustomParserSettings &settings)
+{
+ setErrorPattern(settings.errorPattern);
+ setFileNameCap(settings.fileNameCap);
+ setLineNumberCap(settings.lineNumberCap);
+ setMessageCap(settings.messageCap);
+}
+
+bool CustomParser::parseLine(const QString &rawLine)
+{
+ if (m_errorRegExp.isEmpty())
+ return false;
+
+ if (m_errorRegExp.indexIn(rawLine.trimmed()) == -1)
+ return false;
+
+ const Utils::FileName fileName =
+ Utils::FileName::fromUserInput(m_errorRegExp.cap(m_fileNameCap));
+ const int lineNumber = m_errorRegExp.cap(m_lineNumberCap).toInt();
+ const QString message = m_errorRegExp.cap(m_messageCap);
+
+ emit addTask(Task(Task::Error, message, fileName, lineNumber, Constants::TASK_CATEGORY_COMPILE));
+ return true;
+}
+
+// Unit tests:
+
+#ifdef WITH_TESTS
+# include <QTest>
+
+# include "projectexplorer.h"
+# include "metatypedeclarations.h"
+# include "outputparser_test.h"
+
+using namespace Utils;
+
+void ProjectExplorerPlugin::testCustomOutputParsers_data()
+{
+ QTest::addColumn<QString>("input");
+ QTest::addColumn<OutputParserTester::Channel>("inputChannel");
+ QTest::addColumn<QString>("pattern");
+ QTest::addColumn<int>("fileNameCap");
+ QTest::addColumn<int>("lineNumberCap");
+ QTest::addColumn<int>("messageCap");
+ QTest::addColumn<QString>("childStdOutLines");
+ QTest::addColumn<QString>("childStdErrLines");
+ QTest::addColumn<QList<ProjectExplorer::Task> >("tasks");
+ QTest::addColumn<QString>("outputLines");
+
+ const Core::Id categoryCompile = Core::Id(Constants::TASK_CATEGORY_COMPILE);
+ const QString simplePattern = QLatin1String("^([a-z]+\\.[a-z]+):(\\d+): error: ([^\\s].+)$");
+ const Utils::FileName fileName = Utils::FileName::fromUserInput(QLatin1String("main.c"));
+
+ QTest::newRow("empty pattern")
+ << QString::fromLatin1("Sometext")
+ << OutputParserTester::STDOUT
+ << QString::fromLatin1("")
+ << 1 << 2 << 3
+ << QString::fromLatin1("Sometext\n") << QString()
+ << QList<ProjectExplorer::Task>()
+ << QString();
+
+ QTest::newRow("pass-through stdout")
+ << QString::fromLatin1("Sometext")
+ << OutputParserTester::STDOUT
+ << simplePattern
+ << 1 << 2 << 3
+ << QString::fromLatin1("Sometext\n") << QString()
+ << QList<ProjectExplorer::Task>()
+ << QString();
+
+ QTest::newRow("pass-through stderr")
+ << QString::fromLatin1("Sometext")
+ << OutputParserTester::STDERR
+ << simplePattern
+ << 1 << 2 << 3
+ << QString() << QString::fromLatin1("Sometext\n")
+ << QList<ProjectExplorer::Task>()
+ << QString();
+
+ const QString simpleError = QLatin1String("main.c:9: error: `sfasdf' undeclared (first use this function)");
+ const QString message = QLatin1String("`sfasdf' undeclared (first use this function)");
+
+ QTest::newRow("simple error")
+ << simpleError
+ << OutputParserTester::STDERR
+ << simplePattern
+ << 1 << 2 << 3
+ << QString() << QString()
+ << (QList<ProjectExplorer::Task>()
+ << Task(Task::Error, message, fileName, 9, categoryCompile)
+ )
+ << QString();
+
+ const QString simpleError2 = QLatin1String("Error: main.c:19: `sfasdf' undeclared (first use this function)");
+ const QString simplePattern2 = QLatin1String("^Error: ([a-z]+\\.[a-z]+):(\\d+): ([^\\s].+)$");
+ const int lineNumber2 = 19;
+
+ QTest::newRow("another simple error on stderr")
+ << simpleError2
+ << OutputParserTester::STDERR
+ << simplePattern2
+ << 1 << 2 << 3
+ << QString() << QString()
+ << (QList<ProjectExplorer::Task>()
+ << Task(Task::Error, message, fileName, lineNumber2, categoryCompile)
+ )
+ << QString();
+
+ QTest::newRow("another simple error on stdout")
+ << simpleError2
+ << OutputParserTester::STDOUT
+ << simplePattern2
+ << 1 << 2 << 3
+ << QString() << QString()
+ << (QList<ProjectExplorer::Task>()
+ << Task(Task::Error, message, fileName, lineNumber2, categoryCompile)
+ )
+ << QString();
+
+ const QString unitTestError = QLatin1String("../LedDriver/LedDriverTest.c:63: FAIL: Expected 0x0080 Was 0xffff");
+ const FileName unitTestFileName = FileName::fromUserInput(QLatin1String("../LedDriver/LedDriverTest.c"));
+ const QString unitTestMessage = QLatin1String("Expected 0x0080 Was 0xffff");
+ const QString unitTestPattern = QLatin1String("^([^:]+):(\\d+): FAIL: ([^\\s].+)$");
+ const int unitTestLineNumber = 63;
+
+ QTest::newRow("unit test error")
+ << unitTestError
+ << OutputParserTester::STDOUT
+ << unitTestPattern
+ << 1 << 2 << 3
+ << QString() << QString()
+ << (QList<ProjectExplorer::Task>()
+ << Task(Task::Error, unitTestMessage, unitTestFileName, unitTestLineNumber, categoryCompile)
+ )
+ << QString();
+}
+
+void ProjectExplorerPlugin::testCustomOutputParsers()
+{
+ QFETCH(QString, input);
+ QFETCH(OutputParserTester::Channel, inputChannel);
+ QFETCH(QString, pattern);
+ QFETCH(int, fileNameCap);
+ QFETCH(int, lineNumberCap);
+ QFETCH(int, messageCap);
+ QFETCH(QString, childStdOutLines);
+ QFETCH(QString, childStdErrLines);
+ QFETCH(QList<Task>, tasks);
+ QFETCH(QString, outputLines);
+
+ CustomParser *parser = new CustomParser;
+ parser->setErrorPattern(pattern);
+ parser->setFileNameCap(fileNameCap);
+ parser->setLineNumberCap(lineNumberCap);
+ parser->setMessageCap(messageCap);
+
+ OutputParserTester testbench;
+ testbench.appendOutputParser(parser);
+ testbench.testParsing(input, inputChannel,
+ tasks, childStdOutLines, childStdErrLines,
+ outputLines);
+}
+#endif
diff --git a/src/plugins/projectexplorer/customparser.h b/src/plugins/projectexplorer/customparser.h
new file mode 100644
index 0000000000..0aece8a8e6
--- /dev/null
+++ b/src/plugins/projectexplorer/customparser.h
@@ -0,0 +1,93 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Andre Hartmann.
+** Contact: aha_1980@gmx.de
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef CUSTOMPARSER_H
+#define CUSTOMPARSER_H
+
+#include "ioutputparser.h"
+
+#include <projectexplorer/task.h>
+
+#include <QRegExp>
+
+namespace ProjectExplorer {
+
+class CustomParserSettings
+{
+public:
+ CustomParserSettings();
+
+ bool operator !=(const CustomParserSettings &other) const;
+
+ QString errorPattern;
+ int fileNameCap;
+ int lineNumberCap;
+ int messageCap;
+};
+
+class CustomParser : public ProjectExplorer::IOutputParser
+{
+public:
+ enum CustomParserChannels {
+ ParseNoChannel = 0,
+ ParseStdErrChannel = 1,
+ ParseStdOutChannel = 2,
+ ParseBothChannels = 3
+ };
+
+ CustomParser(const CustomParserSettings &settings = CustomParserSettings());
+ ~CustomParser();
+ void stdError(const QString &line);
+ void stdOutput(const QString &line);
+
+ void setSettings(const CustomParserSettings &settings);
+
+ void setErrorPattern(const QString &errorPattern);
+ QString errorPattern() const;
+ int fileNameCap() const;
+ void setFileNameCap(int fileNameCap);
+ int lineNumberCap() const;
+ void setLineNumberCap(int lineNumberCap);
+ int messageCap() const;
+ void setMessageCap(int messageCap);
+
+private:
+ bool parseLine(const QString &rawLine);
+
+ QRegExp m_errorRegExp;
+ int m_fileNameCap;
+ int m_lineNumberCap;
+ int m_messageCap;
+
+ CustomParserChannels m_parserChannels;
+};
+
+} // namespace ProjectExplorer
+
+#endif // CUSTOMPARSER_H
diff --git a/src/plugins/projectexplorer/customparserconfigdialog.cpp b/src/plugins/projectexplorer/customparserconfigdialog.cpp
new file mode 100644
index 0000000000..c236abe1a5
--- /dev/null
+++ b/src/plugins/projectexplorer/customparserconfigdialog.cpp
@@ -0,0 +1,167 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Andre Hartmann.
+** Contact: aha_1980@gmx.de
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "customparserconfigdialog.h"
+#include "ui_customparserconfigdialog.h"
+
+#include <QPushButton>
+#include <QRegExp>
+
+CustomParserConfigDialog::CustomParserConfigDialog(QDialog *parent) :
+ QDialog(parent),
+ ui(new Ui::CustomParserConfigDialog)
+{
+ ui->setupUi(this);
+
+ connect(ui->errorPattern, SIGNAL(textChanged(QString)), this, SLOT(changed()));
+ connect(ui->errorMessage, SIGNAL(textChanged(QString)), this, SLOT(changed()));
+ connect(ui->fileNameCap, SIGNAL(valueChanged(int)), this, SLOT(changed()));
+ connect(ui->lineNumberCap, SIGNAL(valueChanged(int)), this, SLOT(changed()));
+ connect(ui->messageCap, SIGNAL(valueChanged(int)), this, SLOT(changed()));
+
+ changed();
+ m_dirty = false;
+}
+
+CustomParserConfigDialog::~CustomParserConfigDialog()
+{
+ delete ui;
+}
+
+void CustomParserConfigDialog::setExampleSettings()
+{
+ setErrorPattern(QLatin1String("#error (.*):(\\d+): (.*)$"));
+ setFileNameCap(1);
+ setLineNumberCap(2);
+ setMessageCap(3);
+ ui->errorMessage->setText(QLatin1String("#error /home/user/src/test.c:891: Unknown identifier `test`"));
+}
+
+void CustomParserConfigDialog::setSettings(const ProjectExplorer::CustomParserSettings &settings)
+{
+ if (settings.errorPattern.isEmpty()) {
+ setExampleSettings();
+ return;
+ }
+
+ setErrorPattern(settings.errorPattern);
+ setFileNameCap(settings.fileNameCap);
+ setLineNumberCap(settings.lineNumberCap);
+ setMessageCap(settings.messageCap);
+}
+
+ProjectExplorer::CustomParserSettings CustomParserConfigDialog::settings() const
+{
+ ProjectExplorer::CustomParserSettings result;
+ result.errorPattern = errorPattern();
+ result.fileNameCap = fileNameCap();
+ result.lineNumberCap = lineNumberCap();
+ result.messageCap = messageCap();
+ return result;
+}
+
+void CustomParserConfigDialog::setErrorPattern(const QString &errorPattern)
+{
+ ui->errorPattern->setText(errorPattern);
+}
+
+QString CustomParserConfigDialog::errorPattern() const
+{
+ return ui->errorPattern->text();
+}
+
+void CustomParserConfigDialog::setFileNameCap(int fileNameCap)
+{
+ ui->fileNameCap->setValue(fileNameCap);
+}
+
+int CustomParserConfigDialog::fileNameCap() const
+{
+ return ui->fileNameCap->value();
+}
+
+void CustomParserConfigDialog::setLineNumberCap(int lineNumberCap)
+{
+ ui->lineNumberCap->setValue(lineNumberCap);
+}
+
+int CustomParserConfigDialog::lineNumberCap() const
+{
+ return ui->lineNumberCap->value();
+}
+
+void CustomParserConfigDialog::setMessageCap(int messageCap)
+{
+ ui->messageCap->setValue(messageCap);
+}
+
+int CustomParserConfigDialog::messageCap() const
+{
+ return ui->messageCap->value();
+}
+
+bool CustomParserConfigDialog::isDirty() const
+{
+ return m_dirty;
+}
+
+void CustomParserConfigDialog::changed()
+{
+ QRegExp rx;
+ rx.setPattern(ui->errorPattern->text());
+ rx.setMinimal(true);
+
+ QPalette palette;
+ palette.setColor(QPalette::Text, rx.isValid() ? Qt::black : Qt::red);
+ ui->errorPattern->setPalette(palette);
+ ui->errorPattern->setToolTip(rx.isValid() ? QString() : rx.errorString());
+
+ int pos = rx.indexIn(ui->errorMessage->text());
+ if (rx.isEmpty() || !rx.isValid() || pos < 0) {
+ QString error = QLatin1String("<font color=\"red\">") + tr("Not applicable: ");
+ if (rx.isEmpty())
+ error += tr("Pattern is empty.");
+ else if (!rx.isValid())
+ error += rx.errorString();
+ else
+ error += tr("Pattern does not match the error message.");
+
+ ui->fileNameTest->setText(error);
+ ui->lineNumberTest->setText(error);
+ ui->messageTest->setText(error);
+ ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
+ return;
+ }
+
+ ui->fileNameTest->setText(rx.cap(ui->fileNameCap->value()));
+ ui->lineNumberTest->setText(rx.cap(ui->lineNumberCap->value()));
+ ui->messageTest->setText(rx.cap(ui->messageCap->value()));
+ ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(true);
+ m_dirty = true;
+}
diff --git a/src/plugins/projectexplorer/customparserconfigdialog.h b/src/plugins/projectexplorer/customparserconfigdialog.h
new file mode 100644
index 0000000000..1513b9a873
--- /dev/null
+++ b/src/plugins/projectexplorer/customparserconfigdialog.h
@@ -0,0 +1,70 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Andre Hartmann.
+** Contact: aha_1980@gmx.de
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef CUSTOMPARSERCONFIGDIALOG_H
+#define CUSTOMPARSERCONFIGDIALOG_H
+
+#include "customparser.h"
+
+#include <QDialog>
+
+namespace Ui {
+class CustomParserConfigDialog;
+}
+
+class CustomParserConfigDialog : public QDialog
+{
+ Q_OBJECT
+
+public:
+ explicit CustomParserConfigDialog(QDialog *parent = 0);
+ ~CustomParserConfigDialog();
+
+ void setExampleSettings();
+ void setSettings(const ProjectExplorer::CustomParserSettings &settings);
+ ProjectExplorer::CustomParserSettings settings() const;
+ void setErrorPattern(const QString &errorPattern);
+ QString errorPattern() const;
+ void setFileNameCap(int fileNameCap);
+ int fileNameCap() const;
+ void setLineNumberCap(int lineNumberCap);
+ int lineNumberCap() const;
+ void setMessageCap(int messageCap);
+ int messageCap() const;
+ bool isDirty() const;
+
+private slots:
+ void changed();
+
+private:
+ Ui::CustomParserConfigDialog *ui;
+ bool m_dirty;
+};
+
+#endif // CUSTOMPARSERCONFIGDIALOG_H
diff --git a/src/plugins/projectexplorer/customparserconfigdialog.ui b/src/plugins/projectexplorer/customparserconfigdialog.ui
new file mode 100644
index 0000000000..09e37db5b5
--- /dev/null
+++ b/src/plugins/projectexplorer/customparserconfigdialog.ui
@@ -0,0 +1,244 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>CustomParserConfigDialog</class>
+ <widget class="QDialog" name="CustomParserConfigDialog">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>470</width>
+ <height>368</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Custom Parser</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_3">
+ <item>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <widget class="QLabel" name="label_4">
+ <property name="text">
+ <string>&amp;Error message capture pattern:</string>
+ </property>
+ <property name="buddy">
+ <cstring>errorPattern</cstring>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLineEdit" name="errorPattern">
+ <property name="text">
+ <string>#error (.*):(\d+): (.*)$</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="capturePositionsGroup">
+ <property name="title">
+ <string>Capture Positions</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout">
+ <item row="0" column="0">
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>&amp;File name:</string>
+ </property>
+ <property name="buddy">
+ <cstring>fileNameCap</cstring>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QLabel" name="label_2">
+ <property name="text">
+ <string>&amp;Line number:</string>
+ </property>
+ <property name="buddy">
+ <cstring>lineNumberCap</cstring>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="2">
+ <widget class="QLabel" name="label_3">
+ <property name="text">
+ <string>&amp;Message:</string>
+ </property>
+ <property name="buddy">
+ <cstring>messageCap</cstring>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QSpinBox" name="fileNameCap">
+ <property name="maximum">
+ <number>9</number>
+ </property>
+ <property name="value">
+ <number>1</number>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QSpinBox" name="lineNumberCap">
+ <property name="maximum">
+ <number>9</number>
+ </property>
+ <property name="value">
+ <number>2</number>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="2">
+ <widget class="QSpinBox" name="messageCap">
+ <property name="maximum">
+ <number>9</number>
+ </property>
+ <property name="value">
+ <number>3</number>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="testGroup">
+ <property name="title">
+ <string>Test</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_2">
+ <item>
+ <widget class="QLabel" name="label_5">
+ <property name="text">
+ <string>E&amp;rror message:</string>
+ </property>
+ <property name="buddy">
+ <cstring>errorMessage</cstring>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLineEdit" name="errorMessage">
+ <property name="text">
+ <string>#error /home/user/src/test.c:891: Unknown identifier `test`</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <layout class="QFormLayout" name="formLayout">
+ <item row="0" column="0">
+ <widget class="QLabel" name="label_6">
+ <property name="text">
+ <string>File name:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QLabel" name="fileNameTest">
+ <property name="text">
+ <string>TextLabel</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="label_7">
+ <property name="text">
+ <string>Line number:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QLabel" name="lineNumberTest">
+ <property name="text">
+ <string>TextLabel</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QLabel" name="label_8">
+ <property name="text">
+ <string>Message:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <widget class="QLabel" name="messageTest">
+ <property name="text">
+ <string>TextLabel</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <spacer name="verticalSpacer">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QDialogButtonBox" name="buttonBox">
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <tabstops>
+ <tabstop>errorPattern</tabstop>
+ <tabstop>fileNameCap</tabstop>
+ <tabstop>lineNumberCap</tabstop>
+ <tabstop>messageCap</tabstop>
+ <tabstop>errorMessage</tabstop>
+ <tabstop>buttonBox</tabstop>
+ </tabstops>
+ <resources/>
+ <connections>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>accepted()</signal>
+ <receiver>CustomParserConfigDialog</receiver>
+ <slot>accept()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>320</x>
+ <y>341</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>386</x>
+ <y>289</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>rejected()</signal>
+ <receiver>CustomParserConfigDialog</receiver>
+ <slot>reject()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>277</x>
+ <y>350</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>421</x>
+ <y>255</y>
+ </hint>
+ </hints>
+ </connection>
+ </connections>
+</ui>
diff --git a/src/plugins/projectexplorer/customtoolchain.cpp b/src/plugins/projectexplorer/customtoolchain.cpp
index 8e529987dc..cb35e3b8f5 100644
--- a/src/plugins/projectexplorer/customtoolchain.cpp
+++ b/src/plugins/projectexplorer/customtoolchain.cpp
@@ -33,6 +33,8 @@
#include "clangparser.h"
#include "linuxiccparser.h"
#include "msvcparser.h"
+#include "customparser.h"
+#include "customparserconfigdialog.h"
#include "projectexplorerconstants.h"
#include "toolchainmanager.h"
@@ -44,7 +46,9 @@
#include <QFormLayout>
#include <QPlainTextEdit>
#include <QLineEdit>
+#include <QHBoxLayout>
#include <QComboBox>
+#include <QPushButton>
using namespace Utils;
@@ -63,6 +67,10 @@ static const char headerPathsKeyC[] = "ProjectExplorer.CustomToolChain.HeaderPat
static const char cxx11FlagsKeyC[] = "ProjectExplorer.CustomToolChain.Cxx11Flags";
static const char mkspecsKeyC[] = "ProjectExplorer.CustomToolChain.Mkspecs";
static const char outputParserKeyC[] = "ProjectExplorer.CustomToolChain.OutputParser";
+static const char errorPatternKeyC[] = "ProjectExplorer.CustomToolChain.ErrorPattern";
+static const char lineNumberCapKeyC[] = "ProjectExplorer.CustomToolChain.LineNumberCap";
+static const char fileNameCapKeyC[] = "ProjectExplorer.CustomToolChain.FileNameCap";
+static const char messageCapKeyC[] = "ProjectExplorer.CustomToolChain.MessageCap";
// --------------------------------------------------------------------------
// CustomToolChain
@@ -204,6 +212,7 @@ IOutputParser *CustomToolChain::outputParser() const
#if defined(QT_OS_WIN)
case Msvc: return new MsvcParser;
#endif
+ case Custom: return new CustomParser(m_customParserSettings);
default: return 0;
}
}
@@ -294,6 +303,10 @@ QVariantMap CustomToolChain::toMap() const
data.insert(QLatin1String(cxx11FlagsKeyC), m_cxx11Flags);
data.insert(QLatin1String(mkspecsKeyC), mkspecs());
data.insert(QLatin1String(outputParserKeyC), m_outputParser);
+ data.insert(QLatin1String(errorPatternKeyC), m_customParserSettings.errorPattern);
+ data.insert(QLatin1String(fileNameCapKeyC), m_customParserSettings.fileNameCap);
+ data.insert(QLatin1String(lineNumberCapKeyC), m_customParserSettings.lineNumberCap);
+ data.insert(QLatin1String(messageCapKeyC), m_customParserSettings.messageCap);
return data;
}
@@ -311,6 +324,10 @@ bool CustomToolChain::fromMap(const QVariantMap &data)
m_cxx11Flags = data.value(QLatin1String(cxx11FlagsKeyC)).toStringList();
setMkspecs(data.value(QLatin1String(mkspecsKeyC)).toString());
m_outputParser = (OutputParser)data.value(QLatin1String(outputParserKeyC)).toInt();
+ m_customParserSettings.errorPattern = data.value(QLatin1String(errorPatternKeyC)).toString();
+ m_customParserSettings.fileNameCap = data.value(QLatin1String(fileNameCapKeyC)).toInt();
+ m_customParserSettings.lineNumberCap = data.value(QLatin1String(lineNumberCapKeyC)).toInt();
+ m_customParserSettings.messageCap = data.value(QLatin1String(messageCapKeyC)).toInt();
QTC_ASSERT(m_outputParser >= Gcc && m_outputParser < OutputParserCount, return false);
return true;
@@ -339,6 +356,16 @@ void CustomToolChain::setOutputParserType(CustomToolChain::OutputParser parser)
m_outputParser = parser;
}
+CustomParserSettings CustomToolChain::customParserSettings() const
+{
+ return m_customParserSettings;
+}
+
+void CustomToolChain::setCustomParserSettings(const CustomParserSettings &settings)
+{
+ m_customParserSettings = settings;
+}
+
QString CustomToolChain::parserName(CustomToolChain::OutputParser parser)
{
switch (parser) {
@@ -348,6 +375,7 @@ QString CustomToolChain::parserName(CustomToolChain::OutputParser parser)
#if defined(Q_OS_WIN)
case Msvc: return tr("MSVC");
#endif
+ case Custom: return tr("Custom");
default: return QString();
}
}
@@ -460,13 +488,17 @@ CustomToolChainConfigWidget::CustomToolChainConfigWidget(CustomToolChain *tc) :
m_headerDetails(new TextEditDetailsWidget(m_headerPaths)),
m_cxx11Flags(new QLineEdit),
m_mkspecs(new QLineEdit),
- m_errorParserComboBox(new QComboBox)
+ m_errorParserComboBox(new QComboBox),
+ m_customParserSettingsButton(new QPushButton(tr("Custom Parser Settings...")))
{
Q_ASSERT(tc);
for (int i = 0; i < CustomToolChain::OutputParserCount; ++i)
m_errorParserComboBox->addItem(CustomToolChain::parserName((CustomToolChain::OutputParser)i));
+ QWidget *parserLayoutWidget = new QWidget;
+ QHBoxLayout *parserLayout = new QHBoxLayout(parserLayoutWidget);
+ parserLayout->setContentsMargins(0, 0, 0, 0);
m_predefinedMacros->setTabChangesFocus(true);
m_predefinedMacros->setToolTip(tr("Each line defines a macro. Format is MACRO[=VALUE]"));
m_headerPaths->setTabChangesFocus(true);
@@ -482,7 +514,9 @@ CustomToolChainConfigWidget::CustomToolChainConfigWidget(CustomToolChain *tc) :
m_mainLayout->addRow(tr("&Header paths:"), m_headerDetails);
m_mainLayout->addRow(tr("C++11 &flags:"), m_cxx11Flags);
m_mainLayout->addRow(tr("&Qt mkspecs:"), m_mkspecs);
- m_mainLayout->addRow(tr("&Error Parser:"), m_errorParserComboBox);
+ parserLayout->addWidget(m_errorParserComboBox);
+ parserLayout->addWidget(m_customParserSettingsButton);
+ m_mainLayout->addRow(tr("&Error parser:"), parserLayoutWidget);
addErrorLabel();
setFromToolchain();
@@ -494,6 +528,8 @@ CustomToolChainConfigWidget::CustomToolChainConfigWidget(CustomToolChain *tc) :
connect(m_headerPaths, SIGNAL(textChanged()), this, SLOT(updateSummaries()));
connect(m_errorParserComboBox, SIGNAL(currentIndexChanged(int)),
this, SLOT(errorParserChanged(int)));
+ connect(m_customParserSettingsButton, SIGNAL(clicked()),
+ this, SLOT(openCustomParserSettingsDialog()));
errorParserChanged(m_errorParserComboBox->currentIndex());
}
@@ -507,10 +543,22 @@ void CustomToolChainConfigWidget::updateSummaries()
void CustomToolChainConfigWidget::errorParserChanged(int index)
{
- Q_UNUSED(index);
+ m_customParserSettingsButton->setEnabled(index == m_errorParserComboBox->count() - 1);
emit dirty();
}
+void CustomToolChainConfigWidget::openCustomParserSettingsDialog()
+{
+ CustomParserConfigDialog dialog;
+ dialog.setSettings(m_customParserSettings);
+
+ if (dialog.exec() == QDialog::Accepted) {
+ m_customParserSettings = dialog.settings();
+ if (dialog.isDirty())
+ emit dirty();
+ }
+}
+
void CustomToolChainConfigWidget::applyImpl()
{
if (toolChain()->isAutoDetected())
@@ -528,6 +576,7 @@ void CustomToolChainConfigWidget::applyImpl()
tc->setMkspecs(m_mkspecs->text());
tc->setDisplayName(displayName); // reset display name
tc->setOutputParserType((CustomToolChain::OutputParser)m_errorParserComboBox->currentIndex());
+ tc->setCustomParserSettings(m_customParserSettings);
}
void CustomToolChainConfigWidget::setFromToolchain()
@@ -543,6 +592,7 @@ void CustomToolChainConfigWidget::setFromToolchain()
m_cxx11Flags->setText(tc->cxx11Flags().join(QLatin1String(",")));
m_mkspecs->setText(tc->mkspecs());
m_errorParserComboBox->setCurrentIndex(tc->outputParserType());
+ m_customParserSettings = tc->customParserSettings();
blockSignals(blocked);
}
@@ -557,7 +607,8 @@ bool CustomToolChainConfigWidget::isDirtyImpl() const
|| m_headerDetails->entries() != tc->headerPathsList()
|| m_cxx11Flags->text().split(QLatin1Char(',')) != tc->cxx11Flags()
|| m_mkspecs->text() != tc->mkspecs()
- || m_errorParserComboBox->currentIndex() == tc->outputParserType();
+ || m_errorParserComboBox->currentIndex() == tc->outputParserType()
+ || m_customParserSettings != tc->customParserSettings();
}
void CustomToolChainConfigWidget::makeReadOnlyImpl()
diff --git a/src/plugins/projectexplorer/customtoolchain.h b/src/plugins/projectexplorer/customtoolchain.h
index cbb9429421..8686fef164 100644
--- a/src/plugins/projectexplorer/customtoolchain.h
+++ b/src/plugins/projectexplorer/customtoolchain.h
@@ -33,6 +33,7 @@
#include "projectexplorer_export.h"
#include "abi.h"
+#include "customparser.h"
#include "headerpath.h"
#include "toolchain.h"
#include "toolchainconfigwidget.h"
@@ -43,6 +44,7 @@ QT_BEGIN_NAMESPACE
class QPlainTextEdit;
class QTextEdit;
class QComboBox;
+class QPushButton;
QT_END_NAMESPACE
namespace Utils { class PathChooser; }
@@ -69,6 +71,7 @@ public:
#if defined(Q_OS_WIN)
Msvc = 3,
#endif
+ Custom,
OutputParserCount
};
@@ -114,7 +117,10 @@ public:
OutputParser outputParserType() const;
void setOutputParserType(OutputParser parser);
+ CustomParserSettings customParserSettings() const;
+ void setCustomParserSettings(const CustomParserSettings &settings);
static QString parserName(OutputParser parser);
+
protected:
CustomToolChain(const QString &id, bool autodetect);
CustomToolChain(const CustomToolChain &);
@@ -132,6 +138,8 @@ private:
QList<Utils::FileName> m_mkspecs;
OutputParser m_outputParser;
+ CustomParserSettings m_customParserSettings;
+
friend class Internal::CustomToolChainFactory;
friend class ToolChainFactory;
};
@@ -176,6 +184,7 @@ public:
private slots:
void updateSummaries();
void errorParserChanged(int index);
+ void openCustomParserSettingsDialog();
protected:
void applyImpl();
@@ -195,6 +204,9 @@ protected:
QLineEdit *m_cxx11Flags;
QLineEdit *m_mkspecs;
QComboBox *m_errorParserComboBox;
+ QPushButton *m_customParserSettingsButton;
+
+ CustomParserSettings m_customParserSettings;
};
} // namespace Internal
diff --git a/src/plugins/projectexplorer/projectexplorer.h b/src/plugins/projectexplorer/projectexplorer.h
index 7afb133016..d64702070c 100644
--- a/src/plugins/projectexplorer/projectexplorer.h
+++ b/src/plugins/projectexplorer/projectexplorer.h
@@ -246,6 +246,9 @@ private slots:
void testGccOutputParsers_data();
void testGccOutputParsers();
+ void testCustomOutputParsers_data();
+ void testCustomOutputParsers();
+
void testClangOutputParser_data();
void testClangOutputParser();
diff --git a/src/plugins/projectexplorer/projectexplorer.pro b/src/plugins/projectexplorer/projectexplorer.pro
index f778848240..d1a17621b2 100644
--- a/src/plugins/projectexplorer/projectexplorer.pro
+++ b/src/plugins/projectexplorer/projectexplorer.pro
@@ -130,7 +130,9 @@ HEADERS += projectexplorer.h \
deploymentdata.h \
buildtargetinfo.h \
customtoolchain.h \
- projectmacroexpander.h
+ projectmacroexpander.h \
+ customparser.h \
+ customparserconfigdialog.h
SOURCES += projectexplorer.cpp \
abi.cpp \
@@ -246,7 +248,9 @@ SOURCES += projectexplorer.cpp \
devicesupport/desktopdeviceconfigurationwidget.cpp \
deployablefile.cpp \
customtoolchain.cpp \
- projectmacroexpander.cpp
+ projectmacroexpander.cpp \
+ customparser.cpp \
+ customparserconfigdialog.cpp
FORMS += processstep.ui \
editorsettingspropertiespage.ui \
@@ -260,7 +264,8 @@ FORMS += processstep.ui \
devicesupport/devicefactoryselectiondialog.ui \
devicesupport/devicesettingswidget.ui \
devicesupport/devicetestdialog.ui \
- devicesupport/desktopdeviceconfigurationwidget.ui
+ devicesupport/desktopdeviceconfigurationwidget.ui \
+ customparserconfigdialog.ui
WINSOURCES += \
windebuginterface.cpp \
diff --git a/src/plugins/projectexplorer/projectexplorer.qbs b/src/plugins/projectexplorer/projectexplorer.qbs
index e70197598a..eeea81b108 100644
--- a/src/plugins/projectexplorer/projectexplorer.qbs
+++ b/src/plugins/projectexplorer/projectexplorer.qbs
@@ -74,6 +74,11 @@ QtcPlugin {
"currentprojectfilter.h",
"currentprojectfind.cpp",
"currentprojectfind.h",
+ "customparser.cpp",
+ "customparser.h",
+ "customparserconfigdialog.cpp",
+ "customparserconfigdialog.h",
+ "customparserconfigdialog.ui",
"customtoolchain.cpp",
"customtoolchain.h",
"dependenciespanel.cpp",