diff options
author | Andre Hartmann <aha_1980@gmx.de> | 2013-08-05 21:30:41 +0300 |
---|---|---|
committer | André Hartmann <aha_1980@gmx.de> | 2013-08-08 11:50:08 +0200 |
commit | 4bc61ecac4701a0e4f4fa5c7e48986dcd687c11a (patch) | |
tree | 469d32aaa36072d9878c9669bb243e2c20b4e03c | |
parent | 9249175a4baf7dbc355fd14c1aa8dc74280eeeb8 (diff) | |
download | qt-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.cpp | 295 | ||||
-rw-r--r-- | src/plugins/projectexplorer/customparser.h | 93 | ||||
-rw-r--r-- | src/plugins/projectexplorer/customparserconfigdialog.cpp | 167 | ||||
-rw-r--r-- | src/plugins/projectexplorer/customparserconfigdialog.h | 70 | ||||
-rw-r--r-- | src/plugins/projectexplorer/customparserconfigdialog.ui | 244 | ||||
-rw-r--r-- | src/plugins/projectexplorer/customtoolchain.cpp | 59 | ||||
-rw-r--r-- | src/plugins/projectexplorer/customtoolchain.h | 12 | ||||
-rw-r--r-- | src/plugins/projectexplorer/projectexplorer.h | 3 | ||||
-rw-r--r-- | src/plugins/projectexplorer/projectexplorer.pro | 11 | ||||
-rw-r--r-- | src/plugins/projectexplorer/projectexplorer.qbs | 5 |
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>&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>&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>&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>&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&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", |