ProjectExplorer: Generalize the concept of custom output parsers

They can now be created independently of any toolchains, and we expose
them in the build and run configurations, so that users can easily get
tasks for output that comes from custom tools or is otherwise specific
to the user's environment.

Fixes: QTCREATORBUG-23993
Change-Id: I405753b9b68508ffe5deb4fcac08d6b213c7554d
Reviewed-by: André Hartmann <aha_1980@gmx.de>
Reviewed-by: hjk <hjk@qt.io>
This commit is contained in:
Christian Kandeler
2020-05-06 12:51:08 +02:00
parent c7973c7d35
commit c15e09e0fe
19 changed files with 619 additions and 114 deletions

View File

@@ -35,6 +35,7 @@ add_qtc_plugin(ProjectExplorer
customexecutablerunconfiguration.cpp customexecutablerunconfiguration.h
customparser.cpp customparser.h
customparserconfigdialog.cpp customparserconfigdialog.h customparserconfigdialog.ui
customparserssettingspage.cpp customparserssettingspage.h
customtoolchain.cpp customtoolchain.h
customwizard/customwizard.cpp customwizard/customwizard.h
customwizard/customwizardpage.cpp customwizard/customwizardpage.h

View File

@@ -30,6 +30,7 @@
#include "buildsteplist.h"
#include "buildstepspage.h"
#include "buildsystem.h"
#include "customparser.h"
#include "environmentwidget.h"
#include "kit.h"
#include "kitinformation.h"
@@ -65,13 +66,14 @@ const char BUILD_STEP_LIST_COUNT[] = "ProjectExplorer.BuildConfiguration.BuildSt
const char BUILD_STEP_LIST_PREFIX[] = "ProjectExplorer.BuildConfiguration.BuildStepList.";
const char CLEAR_SYSTEM_ENVIRONMENT_KEY[] = "ProjectExplorer.BuildConfiguration.ClearSystemEnvironment";
const char USER_ENVIRONMENT_CHANGES_KEY[] = "ProjectExplorer.BuildConfiguration.UserEnvironmentChanges";
const char CUSTOM_PARSERS_KEY[] = "ProjectExplorer.BuildConfiguration.CustomParsers";
namespace ProjectExplorer {
namespace Internal {
class BuildEnvironmentWidget : public NamedWidget
{
Q_DECLARE_TR_FUNCTIONS(ProjectExplorer::BuildEnvironmentWidget)
Q_DECLARE_TR_FUNCTIONS(ProjectExplorer::Internal::BuildEnvironmentWidget)
public:
explicit BuildEnvironmentWidget(BuildConfiguration *bc)
@@ -107,6 +109,25 @@ public:
}
};
class CustomParsersBuildWidget : public NamedWidget
{
Q_DECLARE_TR_FUNCTIONS(ProjectExplorer::Internal::CustomParsersBuildWidget)
public:
CustomParsersBuildWidget(BuildConfiguration *bc) : NamedWidget(tr("Custom Output Parsers"))
{
const auto selectionWidget = new CustomParsersSelectionWidget(this);
const auto layout = new QVBoxLayout(this);
layout->setContentsMargins(0, 0, 0, 0);
layout->addWidget(selectionWidget);
connect(selectionWidget, &CustomParsersSelectionWidget::selectionChanged,
[selectionWidget, bc] {
bc->setCustomParsers(selectionWidget->selectedParsers());
});
selectionWidget->setSelectedParsers(bc->customParsers());
}
};
class BuildConfigurationPrivate
{
@@ -128,6 +149,7 @@ public:
QList<Core::Id> m_initialBuildSteps;
QList<Core::Id> m_initialCleanSteps;
Utils::MacroExpander m_macroExpander;
QList<Core::Id> m_customParsers;
// FIXME: Remove.
BuildConfiguration::BuildType m_initialBuildType = BuildConfiguration::Unknown;
@@ -294,7 +316,10 @@ NamedWidget *BuildConfiguration::createConfigWidget()
QList<NamedWidget *> BuildConfiguration::createSubConfigWidgets()
{
return {new Internal::BuildEnvironmentWidget(this)};
return {
new Internal::BuildEnvironmentWidget(this),
new Internal::CustomParsersBuildWidget(this)
};
}
BuildSystem *BuildConfiguration::buildSystem() const
@@ -334,6 +359,8 @@ QVariantMap BuildConfiguration::toMap() const
map.insert(QLatin1String(BUILD_STEP_LIST_PREFIX) + QString::number(0), d->m_buildSteps.toMap());
map.insert(QLatin1String(BUILD_STEP_LIST_PREFIX) + QString::number(1), d->m_cleanSteps.toMap());
map.insert(CUSTOM_PARSERS_KEY, transform(d->m_customParsers,&Core::Id::toSetting));
return map;
}
@@ -366,6 +393,8 @@ bool BuildConfiguration::fromMap(const QVariantMap &map)
}
}
d->m_customParsers = transform(map.value(CUSTOM_PARSERS_KEY).toList(), &Core::Id::fromSetting);
return ProjectConfiguration::fromMap(map);
}
@@ -449,6 +478,16 @@ void BuildConfiguration::addToEnvironment(Environment &env) const
Q_UNUSED(env)
}
const QList<Core::Id> BuildConfiguration::customParsers() const
{
return d->m_customParsers;
}
void BuildConfiguration::setCustomParsers(const QList<Core::Id> &parsers)
{
d->m_customParsers = parsers;
}
bool BuildConfiguration::useSystemEnvironment() const
{
return !d->m_clearSystemEnvironment;

View File

@@ -79,6 +79,9 @@ public:
virtual void addToEnvironment(Utils::Environment &env) const;
const QList<Core::Id> customParsers() const;
void setCustomParsers(const QList<Core::Id> &parsers);
BuildStepList *buildSteps() const;
BuildStepList *cleanSteps() const;

View File

@@ -27,9 +27,11 @@
#include "buildconfiguration.h"
#include "buildsteplist.h"
#include "customparser.h"
#include "deployconfiguration.h"
#include "kitinformation.h"
#include "project.h"
#include "projectexplorer.h"
#include "projectexplorerconstants.h"
#include "target.h"
@@ -263,6 +265,10 @@ QString BuildStep::fallbackWorkingDirectory() const
void BuildStep::setupOutputFormatter(OutputFormatter *formatter)
{
for (const Core::Id id : buildConfiguration()->customParsers()) {
if (Internal::CustomParser * const parser = Internal::CustomParser::createFromId(id))
formatter->addLineParser(parser);
}
Utils::FileInProjectFinder fileFinder;
fileFinder.setProjectDirectory(project()->projectDirectory());
fileFinder.setProjectFiles(project()->files(Project::AllFiles));

View File

@@ -24,16 +24,41 @@
****************************************************************************/
#include "customparser.h"
#include "task.h"
#include "projectexplorerconstants.h"
#include "buildmanager.h"
#include "projectexplorerconstants.h"
#include "task.h"
#include <coreplugin/icore.h>
#include <utils/qtcassert.h>
#include <QCheckBox>
#include <QLabel>
#include <QPair>
#include <QString>
#include <QVBoxLayout>
#ifdef WITH_TESTS
# include <QTest>
# include "projectexplorer.h"
# include "outputparser_test.h"
#endif
using namespace Utils;
using namespace ProjectExplorer;
const char idKey[] = "Id";
const char nameKey[] = "Name";
const char errorKey[] = "Error";
const char warningKey[] = "Warning";
const char patternKey[] = "Pattern";
const char lineNumberCapKey[] = "LineNumberCap";
const char fileNameCapKey[] = "FileNameCap";
const char messageCapKey[] = "MessageCap";
const char channelKey[] = "Channel";
const char exampleKey[] = "Example";
namespace ProjectExplorer {
namespace Internal {
bool CustomParserExpression::operator ==(const CustomParserExpression &other) const
{
@@ -47,7 +72,7 @@ QString CustomParserExpression::pattern() const
return m_regExp.pattern();
}
void ProjectExplorer::CustomParserExpression::setPattern(const QString &pattern)
void CustomParserExpression::setPattern(const QString &pattern)
{
m_regExp.setPattern(pattern);
QTC_CHECK(m_regExp.isValid());
@@ -86,6 +111,31 @@ void CustomParserExpression::setMessageCap(int messageCap)
m_messageCap = messageCap;
}
QVariantMap CustomParserExpression::toMap() const
{
QVariantMap map;
map.insert(patternKey, pattern());
map.insert(messageCapKey, messageCap());
map.insert(fileNameCapKey, fileNameCap());
map.insert(lineNumberCapKey, lineNumberCap());
map.insert(exampleKey, example());
map.insert(channelKey, channel());
return map;
}
void CustomParserExpression::fromMap(const QVariantMap &map)
{
setPattern(map.value(patternKey).toString());
setMessageCap(map.value(messageCapKey).toInt());
setFileNameCap(map.value(fileNameCapKey).toInt());
setLineNumberCap(map.value(lineNumberCapKey).toInt());
setExample(map.value(exampleKey).toString());
int channel = map.value(channelKey).toInt();
if (channel == ParseNoChannel || channel > ParseBothChannels)
channel = ParseStdErrChannel;
setChannel(static_cast<CustomParserChannel>(channel));
}
int CustomParserExpression::lineNumberCap() const
{
return m_lineNumberCap;
@@ -108,7 +158,26 @@ void CustomParserExpression::setFileNameCap(int fileNameCap)
bool CustomParserSettings::operator ==(const CustomParserSettings &other) const
{
return error == other.error && warning == other.warning;
return id == other.id && displayName == other.displayName
&& error == other.error && warning == other.warning;
}
QVariantMap CustomParserSettings::toMap() const
{
QVariantMap map;
map.insert(idKey, id.toSetting());
map.insert(nameKey, displayName);
map.insert(errorKey, error.toMap());
map.insert(warningKey, warning.toMap());
return map;
}
void CustomParserSettings::fromMap(const QVariantMap &map)
{
id = Core::Id::fromSetting(map.value(idKey));
displayName = map.value(nameKey).toString();
error.fromMap(map.value(errorKey).toMap());
warning.fromMap(map.value(warningKey).toMap());
}
CustomParser::CustomParser(const CustomParserSettings &settings)
@@ -124,6 +193,15 @@ void CustomParser::setSettings(const CustomParserSettings &settings)
m_warning = settings.warning;
}
CustomParser *CustomParser::createFromId(Core::Id id)
{
const Internal::CustomParserSettings parser = findOrDefault(ProjectExplorerPlugin::customParsers(),
[id](const Internal::CustomParserSettings &p) { return p.id == id; });
if (parser.id.isValid())
return new CustomParser(parser);
return nullptr;
}
Core::Id CustomParser::id()
{
return Core::Id("ProjectExplorer.OutputParser.Custom");
@@ -162,7 +240,7 @@ OutputLineParser::Result CustomParser::hasMatch(
addLinkSpecForAbsoluteFilePath(linkSpecs, fileName, lineNumber, match,
expression.fileNameCap());
scheduleTask(CompileTask(taskType, message, fileName, lineNumber), 1);
return Status::Done;
return {Status::Done, linkSpecs};
}
OutputLineParser::Result CustomParser::parseLine(
@@ -177,13 +255,133 @@ OutputLineParser::Result CustomParser::parseLine(
return hasMatch(line, channel, m_warning, Task::Warning);
}
namespace {
class SelectionWidget : public QWidget
{
Q_OBJECT
public:
SelectionWidget(QWidget *parent = nullptr) : QWidget(parent)
{
const auto layout = new QVBoxLayout(this);
const auto explanatoryLabel = new QLabel(tr(
"Custom output parsers scan command line output for user-provided error patterns<br>"
"in order to create entries in the issues pane.<br>"
"The parsers can be configured <a href=\"dummy\">here</a>."));
layout->addWidget(explanatoryLabel);
connect(explanatoryLabel, &QLabel::linkActivated, [] {
Core::ICore::showOptionsDialog(Constants::CUSTOM_PARSERS_SETTINGS_PAGE_ID);
});
updateUi();
connect(ProjectExplorerPlugin::instance(), &ProjectExplorerPlugin::customParsersChanged,
this, &SelectionWidget::updateUi);
}
void setSelectedParsers(const QList<Core::Id> &parsers)
{
for (const auto &p : qAsConst(parserCheckBoxes))
p.first->setChecked(parsers.contains(p.second));
emit selectionChanged();
}
QList<Core::Id> selectedParsers() const
{
QList<Core::Id> parsers;
for (const auto &p : qAsConst(parserCheckBoxes)) {
if (p.first->isChecked())
parsers << p.second;
}
return parsers;
}
signals:
void selectionChanged();
private:
void updateUi()
{
const auto layout = qobject_cast<QVBoxLayout *>(this->layout());
QTC_ASSERT(layout, return);
const QList<Core::Id> parsers = selectedParsers();
for (const auto &p : qAsConst(parserCheckBoxes))
delete p.first;
parserCheckBoxes.clear();
for (const CustomParserSettings &s : ProjectExplorerPlugin::customParsers()) {
const auto checkBox = new QCheckBox(s.displayName, this);
connect(checkBox, &QCheckBox::stateChanged,
this, &SelectionWidget::selectionChanged);
parserCheckBoxes << qMakePair(checkBox, s.id);
layout->addWidget(checkBox);
}
setSelectedParsers(parsers);
}
QList<QPair<QCheckBox *, Core::Id>> parserCheckBoxes;
};
} // anonymous namespace
CustomParsersSelectionWidget::CustomParsersSelectionWidget(QWidget *parent) : DetailsWidget(parent)
{
const auto widget = new SelectionWidget(this);
connect(widget, &SelectionWidget::selectionChanged, [this] {
updateSummary();
emit selectionChanged();
});
setWidget(widget);
updateSummary();
}
void CustomParsersSelectionWidget::setSelectedParsers(const QList<Core::Id> &parsers)
{
qobject_cast<SelectionWidget *>(widget())->setSelectedParsers(parsers);
}
QList<Core::Id> CustomParsersSelectionWidget::selectedParsers() const
{
return qobject_cast<SelectionWidget *>(widget())->selectedParsers();
}
void CustomParsersSelectionWidget::updateSummary()
{
const QList<Core::Id> parsers
= qobject_cast<SelectionWidget *>(widget())->selectedParsers();
if (parsers.isEmpty())
setSummaryText(tr("There are no custom parsers active"));
else
setSummaryText(tr("There are %n custom parsers active", nullptr, parsers.count()));
}
CustomParsersAspect::CustomParsersAspect(Target *target)
{
Q_UNUSED(target)
setId("CustomOutputParsers");
setSettingsKey("CustomOutputParsers");
setDisplayName(tr("Custom Output Parsers"));
setConfigWidgetCreator([this] {
const auto widget = new CustomParsersSelectionWidget;
widget->setSelectedParsers(m_parsers);
connect(widget, &CustomParsersSelectionWidget::selectionChanged,
this, [this, widget] { m_parsers = widget->selectedParsers(); });
return widget;
});
}
void CustomParsersAspect::fromMap(const QVariantMap &map)
{
m_parsers = transform(map.value(settingsKey()).toList(), &Core::Id::fromSetting);
}
void CustomParsersAspect::toMap(QVariantMap &map) const
{
map.insert(settingsKey(), transform(m_parsers, &Core::Id::toSetting));
}
} // namespace Internal
// Unit tests:
#ifdef WITH_TESTS
# include <QTest>
# include "projectexplorer.h"
# include "outputparser_test.h"
using namespace Internal;
void ProjectExplorerPlugin::testCustomOutputParsers_data()
{
@@ -478,4 +676,9 @@ void ProjectExplorerPlugin::testCustomOutputParsers()
tasks, childStdOutLines, childStdErrLines,
outputLines);
}
} // namespace ProjectExplorer
#endif
#include <customparser.moc>

View File

@@ -26,12 +26,18 @@
#pragma once
#include "ioutputparser.h"
#include "projectconfiguration.h"
#include <projectexplorer/task.h>
#include <utils/detailswidget.h>
#include <QRegularExpression>
#include <QVariantMap>
namespace ProjectExplorer {
class Target;
namespace Internal {
class CustomParserExpression
{
@@ -62,6 +68,9 @@ public:
int messageCap() const;
void setMessageCap(int messageCap);
QVariantMap toMap() const;
void fromMap(const QVariantMap &map);
private:
QRegularExpression m_regExp;
CustomParserExpression::CustomParserChannel m_channel = ParseBothChannels;
@@ -77,6 +86,11 @@ public:
bool operator ==(const CustomParserSettings &other) const;
bool operator !=(const CustomParserSettings &other) const { return !operator==(other); }
QVariantMap toMap() const;
void fromMap(const QVariantMap &map);
Core::Id id;
QString displayName;
CustomParserExpression error;
CustomParserExpression warning;
};
@@ -88,6 +102,7 @@ public:
void setSettings(const CustomParserSettings &settings);
static CustomParser *createFromId(Core::Id id);
static Core::Id id();
private:
@@ -101,6 +116,39 @@ private:
CustomParserExpression m_warning;
};
class CustomParsersSelectionWidget : public Utils::DetailsWidget
{
Q_OBJECT
public:
CustomParsersSelectionWidget(QWidget *parent = nullptr);
void setSelectedParsers(const QList<Core::Id> &parsers);
QList<Core::Id> selectedParsers() const;
signals:
void selectionChanged();
private:
void updateSummary();
};
class CustomParsersAspect : public ProjectConfigurationAspect
{
Q_OBJECT
public:
CustomParsersAspect(Target *target);
void setParsers(const QList<Core::Id> &parsers) { m_parsers = parsers; }
const QList<Core::Id> parsers() const { return m_parsers; }
private:
void fromMap(const QVariantMap &map) override;
void toMap(QVariantMap &map) const override;
QList<Core::Id> m_parsers;
};
} // namespace Internal
} // namespace ProjectExplorer
Q_DECLARE_METATYPE(ProjectExplorer::CustomParserExpression::CustomParserChannel);
Q_DECLARE_METATYPE(ProjectExplorer::Internal::CustomParserExpression::CustomParserChannel);

View File

@@ -35,7 +35,7 @@
namespace ProjectExplorer {
namespace Internal {
CustomParserConfigDialog::CustomParserConfigDialog(QDialog *parent) :
CustomParserConfigDialog::CustomParserConfigDialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::CustomParserConfigDialog)
{

View File

@@ -43,7 +43,7 @@ class CustomParserConfigDialog : public QDialog
Q_OBJECT
public:
explicit CustomParserConfigDialog(QDialog *parent = nullptr);
explicit CustomParserConfigDialog(QWidget *parent = nullptr);
~CustomParserConfigDialog() override;
void setExampleSettings();

View File

@@ -0,0 +1,144 @@
/****************************************************************************
**
** Copyright (C) 2020 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#include "customparserssettingspage.h"
#include "customparser.h"
#include "customparserconfigdialog.h"
#include "projectexplorer.h"
#include "projectexplorerconstants.h"
#include <utils/algorithm.h>
#include <utils/qtcassert.h>
#include <QHBoxLayout>
#include <QList>
#include <QListWidget>
#include <QPushButton>
#include <QUuid>
#include <QVBoxLayout>
namespace ProjectExplorer {
namespace Internal {
class CustomParsersSettingsWidget final : public Core::IOptionsPageWidget
{
Q_DECLARE_TR_FUNCTIONS(ProjectExplorer::Internal::CustomParsersSettingsPage)
public:
CustomParsersSettingsWidget()
{
m_customParsers = ProjectExplorerPlugin::customParsers();
resetListView();
const auto mainLayout = new QVBoxLayout(this);
const auto widgetLayout = new QHBoxLayout;
mainLayout->addLayout(widgetLayout);
widgetLayout->addWidget(&m_parserListView);
const auto buttonLayout = new QVBoxLayout;
widgetLayout->addLayout(buttonLayout);
const auto addButton = new QPushButton(tr("Add..."));
const auto removeButton = new QPushButton(tr("Remove"));
const auto editButton = new QPushButton("Edit...");
buttonLayout->addWidget(addButton);
buttonLayout->addWidget(removeButton);
buttonLayout->addWidget(editButton);
buttonLayout->addStretch(1);
connect(addButton, &QPushButton::clicked, [this] {
CustomParserConfigDialog dlg(this);
dlg.setSettings(CustomParserSettings());
if (dlg.exec() != QDialog::Accepted)
return;
CustomParserSettings newParser = dlg.settings();
newParser.id = Core::Id::fromString(QUuid::createUuid().toString());
newParser.displayName = tr("New Parser");
m_customParsers << newParser;
resetListView();
});
connect(removeButton, &QPushButton::clicked, [this] {
const QList<QListWidgetItem *> sel = m_parserListView.selectedItems();
QTC_ASSERT(sel.size() == 1, return);
m_customParsers.removeAt(m_parserListView.row(sel.first()));
delete sel.first();
});
connect(editButton, &QPushButton::clicked, [this] {
const QList<QListWidgetItem *> sel = m_parserListView.selectedItems();
QTC_ASSERT(sel.size() == 1, return);
CustomParserSettings &s = m_customParsers[m_parserListView.row(sel.first())];
CustomParserConfigDialog dlg(this);
dlg.setSettings(s);
if (dlg.exec() != QDialog::Accepted)
return;
s.error = dlg.settings().error;
s.warning = dlg.settings().warning;
});
connect(&m_parserListView, &QListWidget::itemChanged, [this](QListWidgetItem *item) {
m_customParsers[m_parserListView.row(item)].displayName = item->text();
resetListView();
});
const auto updateButtons = [this, removeButton, editButton] {
const bool enable = !m_parserListView.selectedItems().isEmpty();
removeButton->setEnabled(enable);
editButton->setEnabled(enable);
};
updateButtons();
connect(m_parserListView.selectionModel(), &QItemSelectionModel::selectionChanged,
updateButtons);
}
private:
void apply() override { ProjectExplorerPlugin::setCustomParsers(m_customParsers); }
void resetListView()
{
m_parserListView.clear();
Utils::sort(m_customParsers,
[](const CustomParserSettings &s1, const CustomParserSettings &s2) {
return s1.displayName < s2.displayName;
});
for (const CustomParserSettings &s : qAsConst(m_customParsers)) {
const auto item = new QListWidgetItem(s.displayName);
item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable);
m_parserListView.addItem(item);
}
}
QListWidget m_parserListView;
QList<CustomParserSettings> m_customParsers;
};
CustomParsersSettingsPage::CustomParsersSettingsPage()
{
setId(Constants::CUSTOM_PARSERS_SETTINGS_PAGE_ID);
setDisplayName(CustomParsersSettingsWidget::tr("Custom Output Parsers"));
setCategory(Constants::BUILD_AND_RUN_SETTINGS_CATEGORY);
setWidgetCreator([] { return new CustomParsersSettingsWidget; });
}
} // namespace Internal
} // namespace ProjectExplorer

View File

@@ -0,0 +1,40 @@
/****************************************************************************
**
** Copyright (C) 2020 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#pragma once
#include <coreplugin/dialogs/ioptionspage.h>
namespace ProjectExplorer {
namespace Internal {
class CustomParsersSettingsPage final : public Core::IOptionsPage
{
public:
CustomParsersSettingsPage();
};
} // namespace Internal
} // namespace ProjectExplorer

View File

@@ -30,7 +30,7 @@
#include "linuxiccparser.h"
#include "msvcparser.h"
#include "customparser.h"
#include "customparserconfigdialog.h"
#include "projectexplorer.h"
#include "projectexplorerconstants.h"
#include "projectmacro.h"
#include "toolchainmanager.h"
@@ -41,12 +41,12 @@
#include <utils/pathchooser.h>
#include <utils/qtcassert.h>
#include <QFormLayout>
#include <QPlainTextEdit>
#include <QLineEdit>
#include <QHBoxLayout>
#include <QComboBox>
#include <QPushButton>
#include <QFormLayout>
#include <QHBoxLayout>
#include <QLineEdit>
#include <QPlainTextEdit>
#include <QUuid>
using namespace Utils;
@@ -64,18 +64,6 @@ 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 errorLineNumberCapKeyC[] = "ProjectExplorer.CustomToolChain.ErrorLineNumberCap";
static const char errorFileNameCapKeyC[] = "ProjectExplorer.CustomToolChain.ErrorFileNameCap";
static const char errorMessageCapKeyC[] = "ProjectExplorer.CustomToolChain.ErrorMessageCap";
static const char errorChannelKeyC[] = "ProjectExplorer.CustomToolChain.ErrorChannel";
static const char errorExampleKeyC[] = "ProjectExplorer.CustomToolChain.ErrorExample";
static const char warningPatternKeyC[] = "ProjectExplorer.CustomToolChain.WarningPattern";
static const char warningLineNumberCapKeyC[] = "ProjectExplorer.CustomToolChain.WarningLineNumberCap";
static const char warningFileNameCapKeyC[] = "ProjectExplorer.CustomToolChain.WarningFileNameCap";
static const char warningMessageCapKeyC[] = "ProjectExplorer.CustomToolChain.WarningMessageCap";
static const char warningChannelKeyC[] = "ProjectExplorer.CustomToolChain.WarningChannel";
static const char warningExampleKeyC[] = "ProjectExplorer.CustomToolChain.WarningExample";
// --------------------------------------------------------------------------
// CustomToolChain
@@ -88,6 +76,14 @@ CustomToolChain::CustomToolChain() :
setTypeDisplayName(tr("Custom"));
}
Internal::CustomParserSettings CustomToolChain::customParserSettings() const
{
return findOrDefault(ProjectExplorerPlugin::customParsers(),
[this](const Internal::CustomParserSettings &s) {
return s.id == outputParserId();
});
}
Abi CustomToolChain::targetAbi() const
{
return m_targetAbi;
@@ -206,8 +202,7 @@ QList<Utils::OutputLineParser *> CustomToolChain::createOutputParsers() const
return LinuxIccParser::iccParserSuite();
if (m_outputParserId == MsvcParser::id())
return {new MsvcParser};
if (m_outputParserId == CustomParser::id())
return {new CustomParser(m_customParserSettings)};
return {new Internal::CustomParser(customParserSettings())};
return {};
}
@@ -293,18 +288,6 @@ QVariantMap CustomToolChain::toMap() const
data.insert(QLatin1String(cxx11FlagsKeyC), m_cxx11Flags);
data.insert(QLatin1String(mkspecsKeyC), mkspecs());
data.insert(QLatin1String(outputParserKeyC), m_outputParserId.toSetting());
data.insert(QLatin1String(errorPatternKeyC), m_customParserSettings.error.pattern());
data.insert(QLatin1String(errorFileNameCapKeyC), m_customParserSettings.error.fileNameCap());
data.insert(QLatin1String(errorLineNumberCapKeyC), m_customParserSettings.error.lineNumberCap());
data.insert(QLatin1String(errorMessageCapKeyC), m_customParserSettings.error.messageCap());
data.insert(QLatin1String(errorChannelKeyC), m_customParserSettings.error.channel());
data.insert(QLatin1String(errorExampleKeyC), m_customParserSettings.error.example());
data.insert(QLatin1String(warningPatternKeyC), m_customParserSettings.warning.pattern());
data.insert(QLatin1String(warningFileNameCapKeyC), m_customParserSettings.warning.fileNameCap());
data.insert(QLatin1String(warningLineNumberCapKeyC), m_customParserSettings.warning.lineNumberCap());
data.insert(QLatin1String(warningMessageCapKeyC), m_customParserSettings.warning.messageCap());
data.insert(QLatin1String(warningChannelKeyC), m_customParserSettings.warning.channel());
data.insert(QLatin1String(warningExampleKeyC), m_customParserSettings.warning.example());
return data;
}
@@ -323,20 +306,48 @@ bool CustomToolChain::fromMap(const QVariantMap &data)
m_cxx11Flags = data.value(QLatin1String(cxx11FlagsKeyC)).toStringList();
setMkspecs(data.value(QLatin1String(mkspecsKeyC)).toString());
setOutputParserId(Core::Id::fromSetting(data.value(QLatin1String(outputParserKeyC))));
m_customParserSettings.error.setPattern(data.value(QLatin1String(errorPatternKeyC)).toString());
m_customParserSettings.error.setFileNameCap(data.value(QLatin1String(errorFileNameCapKeyC)).toInt());
m_customParserSettings.error.setLineNumberCap(data.value(QLatin1String(errorLineNumberCapKeyC)).toInt());
m_customParserSettings.error.setMessageCap(data.value(QLatin1String(errorMessageCapKeyC)).toInt());
m_customParserSettings.error.setChannel(
static_cast<CustomParserExpression::CustomParserChannel>(data.value(QLatin1String(errorChannelKeyC)).toInt()));
m_customParserSettings.error.setExample(data.value(QLatin1String(errorExampleKeyC)).toString());
m_customParserSettings.warning.setPattern(data.value(QLatin1String(warningPatternKeyC)).toString());
m_customParserSettings.warning.setFileNameCap(data.value(QLatin1String(warningFileNameCapKeyC)).toInt());
m_customParserSettings.warning.setLineNumberCap(data.value(QLatin1String(warningLineNumberCapKeyC)).toInt());
m_customParserSettings.warning.setMessageCap(data.value(QLatin1String(warningMessageCapKeyC)).toInt());
m_customParserSettings.warning.setChannel(
static_cast<CustomParserExpression::CustomParserChannel>(data.value(QLatin1String(warningChannelKeyC)).toInt()));
m_customParserSettings.warning.setExample(data.value(QLatin1String(warningExampleKeyC)).toString());
// Restore Pre-4.13 settings.
if (outputParserId() == Internal::CustomParser::id()) {
Internal::CustomParserSettings customParserSettings;
customParserSettings.error.setPattern(
data.value("ProjectExplorer.CustomToolChain.ErrorPattern").toString());
customParserSettings.error.setFileNameCap(
data.value("ProjectExplorer.CustomToolChain.ErrorLineNumberCap").toInt());
customParserSettings.error.setLineNumberCap(
data.value("ProjectExplorer.CustomToolChain.ErrorFileNameCap").toInt());
customParserSettings.error.setMessageCap(
data.value("ProjectExplorer.CustomToolChain.ErrorMessageCap").toInt());
customParserSettings.error.setChannel(
static_cast<Internal::CustomParserExpression::CustomParserChannel>(
data.value("ProjectExplorer.CustomToolChain.ErrorChannel").toInt()));
customParserSettings.error.setExample(
data.value("ProjectExplorer.CustomToolChain.ErrorExample").toString());
customParserSettings.warning.setPattern(
data.value("ProjectExplorer.CustomToolChain.WarningPattern").toString());
customParserSettings.warning.setFileNameCap(
data.value("ProjectExplorer.CustomToolChain.WarningLineNumberCap").toInt());
customParserSettings.warning.setLineNumberCap(
data.value("ProjectExplorer.CustomToolChain.WarningFileNameCap").toInt());
customParserSettings.warning.setMessageCap(
data.value("ProjectExplorer.CustomToolChain.WarningMessageCap").toInt());
customParserSettings.warning.setChannel(
static_cast<Internal::CustomParserExpression::CustomParserChannel>(
data.value("ProjectExplorer.CustomToolChain.WarningChannel").toInt()));
customParserSettings.warning.setExample(
data.value("ProjectExplorer.CustomToolChain.WarningExample").toString());
if (!customParserSettings.error.pattern().isEmpty()
|| !customParserSettings.error.pattern().isEmpty()) {
// Found custom parser in old settings, move to new place.
customParserSettings.id = Core::Id::fromString(QUuid::createUuid().toString());
setOutputParserId(customParserSettings.id);
customParserSettings.displayName = tr("Parser for toolchain %1").arg(displayName());
QList<Internal::CustomParserSettings> settings
= ProjectExplorerPlugin::customParsers();
settings << customParserSettings;
ProjectExplorerPlugin::setCustomParsers(settings);
}
}
return true;
}
@@ -367,19 +378,6 @@ void CustomToolChain::setOutputParserId(Core::Id parserId)
toolChainUpdated();
}
CustomParserSettings CustomToolChain::customParserSettings() const
{
return m_customParserSettings;
}
void CustomToolChain::setCustomParserSettings(const CustomParserSettings &settings)
{
if (m_customParserSettings == settings)
return;
m_customParserSettings = settings;
toolChainUpdated();
}
QList<CustomToolChain::Parser> CustomToolChain::parsers()
{
QList<CustomToolChain::Parser> result;
@@ -387,8 +385,6 @@ QList<CustomToolChain::Parser> CustomToolChain::parsers()
result.append({ClangParser::id(), tr("Clang")});
result.append({LinuxIccParser::id(), tr("ICC")});
result.append({MsvcParser::id(), tr("MSVC")});
result.append({CustomParser::id(), tr("Custom")});
return result;
}
@@ -472,14 +468,15 @@ CustomToolChainConfigWidget::CustomToolChainConfigWidget(CustomToolChain *tc) :
m_headerDetails(new TextEditDetailsWidget(m_headerPaths)),
m_cxx11Flags(new QLineEdit),
m_mkspecs(new QLineEdit),
m_errorParserComboBox(new QComboBox),
m_customParserSettingsButton(new QPushButton(tr("Custom Parser Settings...")))
m_errorParserComboBox(new QComboBox)
{
Q_ASSERT(tc);
const QList<CustomToolChain::Parser> parsers = CustomToolChain::parsers();
for (const auto &parser : parsers)
m_errorParserComboBox->addItem(parser.displayName, parser.parserId.toString());
for (const Internal::CustomParserSettings &s : ProjectExplorerPlugin::customParsers())
m_errorParserComboBox->addItem(s.displayName, s.id.toString());
auto parserLayoutWidget = new QWidget;
auto parserLayout = new QHBoxLayout(parserLayoutWidget);
@@ -503,7 +500,6 @@ CustomToolChainConfigWidget::CustomToolChainConfigWidget(CustomToolChain *tc) :
m_mainLayout->addRow(tr("C++11 &flags:"), m_cxx11Flags);
m_mainLayout->addRow(tr("&Qt mkspecs:"), m_mkspecs);
parserLayout->addWidget(m_errorParserComboBox);
parserLayout->addWidget(m_customParserSettingsButton);
m_mainLayout->addRow(tr("&Error parser:"), parserLayoutWidget);
addErrorLabel();
@@ -522,8 +518,6 @@ CustomToolChainConfigWidget::CustomToolChainConfigWidget(CustomToolChain *tc) :
connect(m_mkspecs, &QLineEdit::textChanged, this, &ToolChainConfigWidget::dirty);
connect(m_errorParserComboBox, QOverload<int>::of(&QComboBox::currentIndexChanged),
this, &CustomToolChainConfigWidget::errorParserChanged);
connect(m_customParserSettingsButton, &QAbstractButton::clicked,
this, &CustomToolChainConfigWidget::openCustomParserSettingsDialog);
errorParserChanged();
}
@@ -538,23 +532,9 @@ void CustomToolChainConfigWidget::updateSummaries()
void CustomToolChainConfigWidget::errorParserChanged(int )
{
const auto currentId = Core::Id::fromSetting(m_errorParserComboBox->currentData());
m_customParserSettingsButton->setEnabled(currentId == CustomParser::id());
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())
@@ -577,7 +557,6 @@ void CustomToolChainConfigWidget::applyImpl()
tc->setMkspecs(m_mkspecs->text());
tc->setDisplayName(displayName); // reset display name
tc->setOutputParserId(Core::Id::fromSetting(m_errorParserComboBox->currentData()));
tc->setCustomParserSettings(m_customParserSettings);
setFromToolchain(); // Refresh with actual data from the toolchain. This shows what e.g. the
// macro parser did with the input.
@@ -600,7 +579,6 @@ void CustomToolChainConfigWidget::setFromToolchain()
m_mkspecs->setText(tc->mkspecs());
int index = m_errorParserComboBox->findData(tc->outputParserId().toSetting());
m_errorParserComboBox->setCurrentIndex(index);
m_customParserSettings = tc->customParserSettings();
}
bool CustomToolChainConfigWidget::isDirtyImpl() const
@@ -614,8 +592,7 @@ bool CustomToolChainConfigWidget::isDirtyImpl() const
|| m_headerDetails->entries() != tc->headerPathsList()
|| m_cxx11Flags->text().split(QLatin1Char(',')) != tc->cxx11Flags()
|| m_mkspecs->text() != tc->mkspecs()
|| Core::Id::fromSetting(m_errorParserComboBox->currentData()) == tc->outputParserId()
|| m_customParserSettings != tc->customParserSettings();
|| Core::Id::fromSetting(m_errorParserComboBox->currentData()) == tc->outputParserId();
}
void CustomToolChainConfigWidget::makeReadOnlyImpl()

View File

@@ -39,7 +39,6 @@ QT_BEGIN_NAMESPACE
class QPlainTextEdit;
class QTextEdit;
class QComboBox;
class QPushButton;
QT_END_NAMESPACE
namespace Utils { class PathChooser; }
@@ -108,13 +107,13 @@ public:
Core::Id outputParserId() const;
void setOutputParserId(Core::Id parserId);
CustomParserSettings customParserSettings() const;
void setCustomParserSettings(const CustomParserSettings &settings);
static QList<CustomToolChain::Parser> parsers();
private:
CustomToolChain();
Internal::CustomParserSettings customParserSettings() const;
Utils::FilePath m_compilerCommand;
Utils::FilePath m_makeCommand;
@@ -125,7 +124,6 @@ private:
QStringList m_mkspecs;
Core::Id m_outputParserId;
CustomParserSettings m_customParserSettings;
friend class Internal::CustomToolChainFactory;
friend class ToolChainFactory;
@@ -155,7 +153,6 @@ public:
private:
void updateSummaries();
void errorParserChanged(int index = -1);
void openCustomParserSettingsDialog();
protected:
void applyImpl() override;
@@ -175,9 +172,6 @@ protected:
QLineEdit *m_cxx11Flags;
QLineEdit *m_mkspecs;
QComboBox *m_errorParserComboBox;
QPushButton *m_customParserSettingsButton;
CustomParserSettings m_customParserSettings;
};
} // namespace Internal

View File

@@ -33,6 +33,7 @@
#include "compileoutputwindow.h"
#include "configtaskhandler.h"
#include "customexecutablerunconfiguration.h"
#include "customparserssettingspage.h"
#include "customwizard/customwizard.h"
#include "deployablefile.h"
#include "deployconfiguration.h"
@@ -283,6 +284,9 @@ const char SEPARATE_DEBUG_INFO_SETTINGS_KEY[] = "ProjectExplorer/Settings/Separa
const char QML_DEBUGGING_SETTINGS_KEY[] = "ProjectExplorer/Settings/QmlDebugging";
const char QT_QUICK_COMPILER_SETTINGS_KEY[] = "ProjectExplorer/Settings/QtQuickCompiler";
const char CUSTOM_PARSER_COUNT_KEY[] = "ProjectExplorer/Settings/CustomParserCount";
const char CUSTOM_PARSER_PREFIX_KEY[] = "ProjectExplorer/Settings/CustomParser";
} // namespace Constants
@@ -551,6 +555,7 @@ public:
MiniProjectTargetSelector * m_targetSelector;
ProjectExplorerSettings m_projectExplorerSettings;
BuildPropertiesSettings m_buildPropertiesSettings;
QList<Internal::CustomParserSettings> m_customParsers;
bool m_shouldHaveRunConfiguration = false;
bool m_shuttingDown = false;
Core::Id m_runMode = Constants::NO_RUN_MODE;
@@ -631,6 +636,7 @@ public:
CompileOutputSettingsPage m_compileOutputSettingsPage;
DeviceSettingsPage m_deviceSettingsPage;
SshSettingsPage m_sshSettingsPage;
CustomParsersSettingsPage m_customParsersSettingsPage;
ProjectTreeWidgetFactory m_projectTreeFactory;
FolderNavigationWidgetFactory m_folderNavigationWidgetFactory;
@@ -817,6 +823,8 @@ bool ProjectExplorerPlugin::initialize(const QStringList &arguments, QString *er
});
ProjectPanelFactory::registerFactory(panelFactory);
RunConfiguration::registerAspect<CustomParsersAspect>();
// context menus
ActionContainer *msessionContextMenu =
ActionManager::createMenu(Constants::M_SESSIONCONTEXT);
@@ -1547,6 +1555,14 @@ bool ProjectExplorerPlugin::initialize(const QStringList &arguments, QString *er
dd->m_buildPropertiesSettings.qtQuickCompiler
= loadTriStateValue(Constants::QT_QUICK_COMPILER_SETTINGS_KEY);
const int customParserCount = s->value(Constants::CUSTOM_PARSER_COUNT_KEY).toInt();
for (int i = 0; i < customParserCount; ++i) {
CustomParserSettings settings;
settings.fromMap(s->value(Constants::CUSTOM_PARSER_PREFIX_KEY
+ QString::number(i)).toMap());
dd->m_customParsers << settings;
}
auto buildManager = new BuildManager(this, dd->m_cancelBuildAction);
connect(buildManager, &BuildManager::buildStateChanged,
dd, &ProjectExplorerPluginPrivate::updateActions);
@@ -2181,6 +2197,12 @@ void ProjectExplorerPluginPrivate::savePersistentSettings()
dd->m_buildPropertiesSettings.qmlDebugging.toVariant());
s->setValue(Constants::QT_QUICK_COMPILER_SETTINGS_KEY,
dd->m_buildPropertiesSettings.qtQuickCompiler.toVariant());
s->setValue(Constants::CUSTOM_PARSER_COUNT_KEY, dd->m_customParsers.count());
for (int i = 0; i < dd->m_customParsers.count(); ++i) {
s->setValue(Constants::CUSTOM_PARSER_PREFIX_KEY + QString::number(i),
dd->m_customParsers.at(i).toMap());
}
}
void ProjectExplorerPlugin::openProjectWelcomePage(const QString &fileName)
@@ -3869,6 +3891,19 @@ void ProjectExplorerPlugin::showQtSettings()
dd->m_buildPropertiesSettings.showQtSettings = true;
}
void ProjectExplorerPlugin::setCustomParsers(const QList<CustomParserSettings> &settings)
{
if (dd->m_customParsers != settings) {
dd->m_customParsers = settings;
emit m_instance->customParsersChanged();
}
}
const QList<CustomParserSettings> ProjectExplorerPlugin::customParsers()
{
return dd->m_customParsers;
}
QStringList ProjectExplorerPlugin::projectFilePatterns()
{
QStringList patterns;

View File

@@ -59,6 +59,7 @@ class FileNode;
namespace Internal {
class AppOutputSettings;
class CustomParserSettings;
class MiniProjectTargetSelector;
class ProjectExplorerSettings;
}
@@ -141,6 +142,9 @@ public:
static const BuildPropertiesSettings &buildPropertiesSettings();
static void showQtSettings();
static void setCustomParsers(const QList<Internal::CustomParserSettings> &settings);
static const QList<Internal::CustomParserSettings> customParsers();
static void startRunControl(RunControl *runControl);
static void showOutputPaneForRunControl(RunControl *runControl);
@@ -192,6 +196,7 @@ signals:
void recentProjectsChanged();
void settingsChanged();
void customParsersChanged();
void runActionsUpdated();

View File

@@ -19,6 +19,7 @@ HEADERS += projectexplorer.h \
buildtargettype.h \
clangparser.h \
configtaskhandler.h \
customparserssettingspage.h \
desktoprunconfiguration.h \
environmentaspect.h \
environmentaspectwidget.h \
@@ -176,6 +177,7 @@ SOURCES += projectexplorer.cpp \
buildpropertiessettingspage.cpp \
buildsystem.cpp \
clangparser.cpp \
customparserssettingspage.cpp \
configtaskhandler.cpp \
desktoprunconfiguration.cpp \
environmentaspect.cpp \

View File

@@ -54,6 +54,7 @@ Project {
"customexecutablerunconfiguration.cpp", "customexecutablerunconfiguration.h",
"customparser.cpp", "customparser.h",
"customparserconfigdialog.cpp", "customparserconfigdialog.h", "customparserconfigdialog.ui",
"customparserssettingspage.cpp", "customparserssettingspage.h",
"customtoolchain.cpp", "customtoolchain.h",
"dependenciespanel.cpp", "dependenciespanel.h",
"deployablefile.cpp", "deployablefile.h",

View File

@@ -110,6 +110,7 @@ const char KITS_SETTINGS_PAGE_ID[] = "D.ProjectExplorer.KitsOptions";
const char SSH_SETTINGS_PAGE_ID[] = "F.ProjectExplorer.SshOptions";
const char TOOLCHAIN_SETTINGS_PAGE_ID[] = "M.ProjectExplorer.ToolChainOptions";
const char DEBUGGER_SETTINGS_PAGE_ID[] = "N.ProjectExplorer.DebuggerOptions";
const char CUSTOM_PARSERS_SETTINGS_PAGE_ID[] = "X.ProjectExplorer.CustomParsersSettingsPage";
// Build and Run settings category
const char BUILD_AND_RUN_SETTINGS_CATEGORY[] = "K.BuildAndRun";

View File

@@ -136,8 +136,6 @@ public:
bool isConfigured() const { return checkForIssues().isEmpty(); }
virtual Tasks checkForIssues() const { return {}; }
Utils::OutputFormatter *createOutputFormatter() const;
using CommandLineGetter = std::function<Utils::CommandLine()>;
void setCommandLineGetter(const CommandLineGetter &cmdGetter);
Utils::CommandLine commandLine() const;

View File

@@ -26,16 +26,17 @@
#include "runcontrol.h"
#include "devicesupport/desktopdevice.h"
#include "project.h"
#include "target.h"
#include "toolchain.h"
#include "abi.h"
#include "buildconfiguration.h"
#include "customparser.h"
#include "environmentaspect.h"
#include "kitinformation.h"
#include "project.h"
#include "projectexplorer.h"
#include "runconfigurationaspects.h"
#include "session.h"
#include "kitinformation.h"
#include "target.h"
#include "toolchain.h"
#include <utils/algorithm.h>
#include <utils/checkablemessagebox.h>
@@ -825,7 +826,14 @@ void RunControlPrivate::showError(const QString &msg)
QList<Utils::OutputLineParser *> RunControl::createOutputParsers() const
{
return OutputFormatterFactory::createFormatters(target());
QList<Utils::OutputLineParser *> parsers = OutputFormatterFactory::createFormatters(target());
if (const auto customParsersAspect = runConfiguration()->aspect<CustomParsersAspect>()) {
for (const Core::Id id : customParsersAspect->parsers()) {
if (CustomParser * const parser = CustomParser::createFromId(id))
parsers << parser;
}
}
return parsers;
}
Core::Id RunControl::runMode() const