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>
This commit is contained in:
Andre Hartmann
2013-08-05 21:30:41 +03:00
committed by André Hartmann
parent 9249175a4b
commit 4bc61ecac4
10 changed files with 952 additions and 7 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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>

View File

@@ -33,6 +33,8 @@
#include "clangparser.h" #include "clangparser.h"
#include "linuxiccparser.h" #include "linuxiccparser.h"
#include "msvcparser.h" #include "msvcparser.h"
#include "customparser.h"
#include "customparserconfigdialog.h"
#include "projectexplorerconstants.h" #include "projectexplorerconstants.h"
#include "toolchainmanager.h" #include "toolchainmanager.h"
@@ -44,7 +46,9 @@
#include <QFormLayout> #include <QFormLayout>
#include <QPlainTextEdit> #include <QPlainTextEdit>
#include <QLineEdit> #include <QLineEdit>
#include <QHBoxLayout>
#include <QComboBox> #include <QComboBox>
#include <QPushButton>
using namespace Utils; using namespace Utils;
@@ -63,6 +67,10 @@ static const char headerPathsKeyC[] = "ProjectExplorer.CustomToolChain.HeaderPat
static const char cxx11FlagsKeyC[] = "ProjectExplorer.CustomToolChain.Cxx11Flags"; static const char cxx11FlagsKeyC[] = "ProjectExplorer.CustomToolChain.Cxx11Flags";
static const char mkspecsKeyC[] = "ProjectExplorer.CustomToolChain.Mkspecs"; static const char mkspecsKeyC[] = "ProjectExplorer.CustomToolChain.Mkspecs";
static const char outputParserKeyC[] = "ProjectExplorer.CustomToolChain.OutputParser"; 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 // CustomToolChain
@@ -204,6 +212,7 @@ IOutputParser *CustomToolChain::outputParser() const
#if defined(QT_OS_WIN) #if defined(QT_OS_WIN)
case Msvc: return new MsvcParser; case Msvc: return new MsvcParser;
#endif #endif
case Custom: return new CustomParser(m_customParserSettings);
default: return 0; default: return 0;
} }
} }
@@ -294,6 +303,10 @@ QVariantMap CustomToolChain::toMap() const
data.insert(QLatin1String(cxx11FlagsKeyC), m_cxx11Flags); data.insert(QLatin1String(cxx11FlagsKeyC), m_cxx11Flags);
data.insert(QLatin1String(mkspecsKeyC), mkspecs()); data.insert(QLatin1String(mkspecsKeyC), mkspecs());
data.insert(QLatin1String(outputParserKeyC), m_outputParser); 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; return data;
} }
@@ -311,6 +324,10 @@ bool CustomToolChain::fromMap(const QVariantMap &data)
m_cxx11Flags = data.value(QLatin1String(cxx11FlagsKeyC)).toStringList(); m_cxx11Flags = data.value(QLatin1String(cxx11FlagsKeyC)).toStringList();
setMkspecs(data.value(QLatin1String(mkspecsKeyC)).toString()); setMkspecs(data.value(QLatin1String(mkspecsKeyC)).toString());
m_outputParser = (OutputParser)data.value(QLatin1String(outputParserKeyC)).toInt(); 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); QTC_ASSERT(m_outputParser >= Gcc && m_outputParser < OutputParserCount, return false);
return true; return true;
@@ -339,6 +356,16 @@ void CustomToolChain::setOutputParserType(CustomToolChain::OutputParser parser)
m_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) QString CustomToolChain::parserName(CustomToolChain::OutputParser parser)
{ {
switch (parser) { switch (parser) {
@@ -348,6 +375,7 @@ QString CustomToolChain::parserName(CustomToolChain::OutputParser parser)
#if defined(Q_OS_WIN) #if defined(Q_OS_WIN)
case Msvc: return tr("MSVC"); case Msvc: return tr("MSVC");
#endif #endif
case Custom: return tr("Custom");
default: return QString(); default: return QString();
} }
} }
@@ -460,13 +488,17 @@ CustomToolChainConfigWidget::CustomToolChainConfigWidget(CustomToolChain *tc) :
m_headerDetails(new TextEditDetailsWidget(m_headerPaths)), m_headerDetails(new TextEditDetailsWidget(m_headerPaths)),
m_cxx11Flags(new QLineEdit), m_cxx11Flags(new QLineEdit),
m_mkspecs(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); Q_ASSERT(tc);
for (int i = 0; i < CustomToolChain::OutputParserCount; ++i) for (int i = 0; i < CustomToolChain::OutputParserCount; ++i)
m_errorParserComboBox->addItem(CustomToolChain::parserName((CustomToolChain::OutputParser)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->setTabChangesFocus(true);
m_predefinedMacros->setToolTip(tr("Each line defines a macro. Format is MACRO[=VALUE]")); m_predefinedMacros->setToolTip(tr("Each line defines a macro. Format is MACRO[=VALUE]"));
m_headerPaths->setTabChangesFocus(true); m_headerPaths->setTabChangesFocus(true);
@@ -482,7 +514,9 @@ CustomToolChainConfigWidget::CustomToolChainConfigWidget(CustomToolChain *tc) :
m_mainLayout->addRow(tr("&Header paths:"), m_headerDetails); m_mainLayout->addRow(tr("&Header paths:"), m_headerDetails);
m_mainLayout->addRow(tr("C++11 &flags:"), m_cxx11Flags); m_mainLayout->addRow(tr("C++11 &flags:"), m_cxx11Flags);
m_mainLayout->addRow(tr("&Qt mkspecs:"), m_mkspecs); 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(); addErrorLabel();
setFromToolchain(); setFromToolchain();
@@ -494,6 +528,8 @@ CustomToolChainConfigWidget::CustomToolChainConfigWidget(CustomToolChain *tc) :
connect(m_headerPaths, SIGNAL(textChanged()), this, SLOT(updateSummaries())); connect(m_headerPaths, SIGNAL(textChanged()), this, SLOT(updateSummaries()));
connect(m_errorParserComboBox, SIGNAL(currentIndexChanged(int)), connect(m_errorParserComboBox, SIGNAL(currentIndexChanged(int)),
this, SLOT(errorParserChanged(int))); this, SLOT(errorParserChanged(int)));
connect(m_customParserSettingsButton, SIGNAL(clicked()),
this, SLOT(openCustomParserSettingsDialog()));
errorParserChanged(m_errorParserComboBox->currentIndex()); errorParserChanged(m_errorParserComboBox->currentIndex());
} }
@@ -507,10 +543,22 @@ void CustomToolChainConfigWidget::updateSummaries()
void CustomToolChainConfigWidget::errorParserChanged(int index) void CustomToolChainConfigWidget::errorParserChanged(int index)
{ {
Q_UNUSED(index); m_customParserSettingsButton->setEnabled(index == m_errorParserComboBox->count() - 1);
emit dirty(); 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() void CustomToolChainConfigWidget::applyImpl()
{ {
if (toolChain()->isAutoDetected()) if (toolChain()->isAutoDetected())
@@ -528,6 +576,7 @@ void CustomToolChainConfigWidget::applyImpl()
tc->setMkspecs(m_mkspecs->text()); tc->setMkspecs(m_mkspecs->text());
tc->setDisplayName(displayName); // reset display name tc->setDisplayName(displayName); // reset display name
tc->setOutputParserType((CustomToolChain::OutputParser)m_errorParserComboBox->currentIndex()); tc->setOutputParserType((CustomToolChain::OutputParser)m_errorParserComboBox->currentIndex());
tc->setCustomParserSettings(m_customParserSettings);
} }
void CustomToolChainConfigWidget::setFromToolchain() void CustomToolChainConfigWidget::setFromToolchain()
@@ -543,6 +592,7 @@ void CustomToolChainConfigWidget::setFromToolchain()
m_cxx11Flags->setText(tc->cxx11Flags().join(QLatin1String(","))); m_cxx11Flags->setText(tc->cxx11Flags().join(QLatin1String(",")));
m_mkspecs->setText(tc->mkspecs()); m_mkspecs->setText(tc->mkspecs());
m_errorParserComboBox->setCurrentIndex(tc->outputParserType()); m_errorParserComboBox->setCurrentIndex(tc->outputParserType());
m_customParserSettings = tc->customParserSettings();
blockSignals(blocked); blockSignals(blocked);
} }
@@ -557,7 +607,8 @@ bool CustomToolChainConfigWidget::isDirtyImpl() const
|| m_headerDetails->entries() != tc->headerPathsList() || m_headerDetails->entries() != tc->headerPathsList()
|| m_cxx11Flags->text().split(QLatin1Char(',')) != tc->cxx11Flags() || m_cxx11Flags->text().split(QLatin1Char(',')) != tc->cxx11Flags()
|| m_mkspecs->text() != tc->mkspecs() || m_mkspecs->text() != tc->mkspecs()
|| m_errorParserComboBox->currentIndex() == tc->outputParserType(); || m_errorParserComboBox->currentIndex() == tc->outputParserType()
|| m_customParserSettings != tc->customParserSettings();
} }
void CustomToolChainConfigWidget::makeReadOnlyImpl() void CustomToolChainConfigWidget::makeReadOnlyImpl()

View File

@@ -33,6 +33,7 @@
#include "projectexplorer_export.h" #include "projectexplorer_export.h"
#include "abi.h" #include "abi.h"
#include "customparser.h"
#include "headerpath.h" #include "headerpath.h"
#include "toolchain.h" #include "toolchain.h"
#include "toolchainconfigwidget.h" #include "toolchainconfigwidget.h"
@@ -43,6 +44,7 @@ QT_BEGIN_NAMESPACE
class QPlainTextEdit; class QPlainTextEdit;
class QTextEdit; class QTextEdit;
class QComboBox; class QComboBox;
class QPushButton;
QT_END_NAMESPACE QT_END_NAMESPACE
namespace Utils { class PathChooser; } namespace Utils { class PathChooser; }
@@ -69,6 +71,7 @@ public:
#if defined(Q_OS_WIN) #if defined(Q_OS_WIN)
Msvc = 3, Msvc = 3,
#endif #endif
Custom,
OutputParserCount OutputParserCount
}; };
@@ -114,7 +117,10 @@ public:
OutputParser outputParserType() const; OutputParser outputParserType() const;
void setOutputParserType(OutputParser parser); void setOutputParserType(OutputParser parser);
CustomParserSettings customParserSettings() const;
void setCustomParserSettings(const CustomParserSettings &settings);
static QString parserName(OutputParser parser); static QString parserName(OutputParser parser);
protected: protected:
CustomToolChain(const QString &id, bool autodetect); CustomToolChain(const QString &id, bool autodetect);
CustomToolChain(const CustomToolChain &); CustomToolChain(const CustomToolChain &);
@@ -132,6 +138,8 @@ private:
QList<Utils::FileName> m_mkspecs; QList<Utils::FileName> m_mkspecs;
OutputParser m_outputParser; OutputParser m_outputParser;
CustomParserSettings m_customParserSettings;
friend class Internal::CustomToolChainFactory; friend class Internal::CustomToolChainFactory;
friend class ToolChainFactory; friend class ToolChainFactory;
}; };
@@ -176,6 +184,7 @@ public:
private slots: private slots:
void updateSummaries(); void updateSummaries();
void errorParserChanged(int index); void errorParserChanged(int index);
void openCustomParserSettingsDialog();
protected: protected:
void applyImpl(); void applyImpl();
@@ -195,6 +204,9 @@ protected:
QLineEdit *m_cxx11Flags; QLineEdit *m_cxx11Flags;
QLineEdit *m_mkspecs; QLineEdit *m_mkspecs;
QComboBox *m_errorParserComboBox; QComboBox *m_errorParserComboBox;
QPushButton *m_customParserSettingsButton;
CustomParserSettings m_customParserSettings;
}; };
} // namespace Internal } // namespace Internal

View File

@@ -246,6 +246,9 @@ private slots:
void testGccOutputParsers_data(); void testGccOutputParsers_data();
void testGccOutputParsers(); void testGccOutputParsers();
void testCustomOutputParsers_data();
void testCustomOutputParsers();
void testClangOutputParser_data(); void testClangOutputParser_data();
void testClangOutputParser(); void testClangOutputParser();

View File

@@ -130,7 +130,9 @@ HEADERS += projectexplorer.h \
deploymentdata.h \ deploymentdata.h \
buildtargetinfo.h \ buildtargetinfo.h \
customtoolchain.h \ customtoolchain.h \
projectmacroexpander.h projectmacroexpander.h \
customparser.h \
customparserconfigdialog.h
SOURCES += projectexplorer.cpp \ SOURCES += projectexplorer.cpp \
abi.cpp \ abi.cpp \
@@ -246,7 +248,9 @@ SOURCES += projectexplorer.cpp \
devicesupport/desktopdeviceconfigurationwidget.cpp \ devicesupport/desktopdeviceconfigurationwidget.cpp \
deployablefile.cpp \ deployablefile.cpp \
customtoolchain.cpp \ customtoolchain.cpp \
projectmacroexpander.cpp projectmacroexpander.cpp \
customparser.cpp \
customparserconfigdialog.cpp
FORMS += processstep.ui \ FORMS += processstep.ui \
editorsettingspropertiespage.ui \ editorsettingspropertiespage.ui \
@@ -260,7 +264,8 @@ FORMS += processstep.ui \
devicesupport/devicefactoryselectiondialog.ui \ devicesupport/devicefactoryselectiondialog.ui \
devicesupport/devicesettingswidget.ui \ devicesupport/devicesettingswidget.ui \
devicesupport/devicetestdialog.ui \ devicesupport/devicetestdialog.ui \
devicesupport/desktopdeviceconfigurationwidget.ui devicesupport/desktopdeviceconfigurationwidget.ui \
customparserconfigdialog.ui
WINSOURCES += \ WINSOURCES += \
windebuginterface.cpp \ windebuginterface.cpp \

View File

@@ -74,6 +74,11 @@ QtcPlugin {
"currentprojectfilter.h", "currentprojectfilter.h",
"currentprojectfind.cpp", "currentprojectfind.cpp",
"currentprojectfind.h", "currentprojectfind.h",
"customparser.cpp",
"customparser.h",
"customparserconfigdialog.cpp",
"customparserconfigdialog.h",
"customparserconfigdialog.ui",
"customtoolchain.cpp", "customtoolchain.cpp",
"customtoolchain.h", "customtoolchain.h",
"dependenciespanel.cpp", "dependenciespanel.cpp",