From c15e09e0fe880c58ad39d5d8d0a1baeb84fc7e3b Mon Sep 17 00:00:00 2001 From: Christian Kandeler Date: Wed, 6 May 2020 12:51:08 +0200 Subject: [PATCH] ProjectExplorer: Generalize the concept of custom output parsers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 Reviewed-by: hjk --- src/plugins/projectexplorer/CMakeLists.txt | 1 + .../projectexplorer/buildconfiguration.cpp | 43 +++- .../projectexplorer/buildconfiguration.h | 3 + src/plugins/projectexplorer/buildstep.cpp | 6 + src/plugins/projectexplorer/customparser.cpp | 223 +++++++++++++++++- src/plugins/projectexplorer/customparser.h | 50 +++- .../customparserconfigdialog.cpp | 2 +- .../customparserconfigdialog.h | 2 +- .../customparserssettingspage.cpp | 144 +++++++++++ .../customparserssettingspage.h | 40 ++++ .../projectexplorer/customtoolchain.cpp | 145 +++++------- src/plugins/projectexplorer/customtoolchain.h | 10 +- .../projectexplorer/projectexplorer.cpp | 35 +++ src/plugins/projectexplorer/projectexplorer.h | 5 + .../projectexplorer/projectexplorer.pro | 2 + .../projectexplorer/projectexplorer.qbs | 1 + .../projectexplorerconstants.h | 1 + .../projectexplorer/runconfiguration.h | 2 - src/plugins/projectexplorer/runcontrol.cpp | 18 +- 19 files changed, 619 insertions(+), 114 deletions(-) create mode 100644 src/plugins/projectexplorer/customparserssettingspage.cpp create mode 100644 src/plugins/projectexplorer/customparserssettingspage.h diff --git a/src/plugins/projectexplorer/CMakeLists.txt b/src/plugins/projectexplorer/CMakeLists.txt index 35ed62afc67..81559e66f8f 100644 --- a/src/plugins/projectexplorer/CMakeLists.txt +++ b/src/plugins/projectexplorer/CMakeLists.txt @@ -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 diff --git a/src/plugins/projectexplorer/buildconfiguration.cpp b/src/plugins/projectexplorer/buildconfiguration.cpp index 09a7f822a97..de8ac4dbe49 100644 --- a/src/plugins/projectexplorer/buildconfiguration.cpp +++ b/src/plugins/projectexplorer/buildconfiguration.cpp @@ -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 m_initialBuildSteps; QList m_initialCleanSteps; Utils::MacroExpander m_macroExpander; + QList m_customParsers; // FIXME: Remove. BuildConfiguration::BuildType m_initialBuildType = BuildConfiguration::Unknown; @@ -294,7 +316,10 @@ NamedWidget *BuildConfiguration::createConfigWidget() QList 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 BuildConfiguration::customParsers() const +{ + return d->m_customParsers; +} + +void BuildConfiguration::setCustomParsers(const QList &parsers) +{ + d->m_customParsers = parsers; +} + bool BuildConfiguration::useSystemEnvironment() const { return !d->m_clearSystemEnvironment; diff --git a/src/plugins/projectexplorer/buildconfiguration.h b/src/plugins/projectexplorer/buildconfiguration.h index 2641ea35974..5e7bf1d03ac 100644 --- a/src/plugins/projectexplorer/buildconfiguration.h +++ b/src/plugins/projectexplorer/buildconfiguration.h @@ -79,6 +79,9 @@ public: virtual void addToEnvironment(Utils::Environment &env) const; + const QList customParsers() const; + void setCustomParsers(const QList &parsers); + BuildStepList *buildSteps() const; BuildStepList *cleanSteps() const; diff --git a/src/plugins/projectexplorer/buildstep.cpp b/src/plugins/projectexplorer/buildstep.cpp index ca64125d3a3..4a56d805c46 100644 --- a/src/plugins/projectexplorer/buildstep.cpp +++ b/src/plugins/projectexplorer/buildstep.cpp @@ -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)); diff --git a/src/plugins/projectexplorer/customparser.cpp b/src/plugins/projectexplorer/customparser.cpp index dfd588fe201..7d6770c9962 100644 --- a/src/plugins/projectexplorer/customparser.cpp +++ b/src/plugins/projectexplorer/customparser.cpp @@ -24,16 +24,41 @@ ****************************************************************************/ #include "customparser.h" -#include "task.h" -#include "projectexplorerconstants.h" -#include "buildmanager.h" +#include "projectexplorerconstants.h" +#include "task.h" + +#include #include +#include +#include +#include #include +#include + +#ifdef WITH_TESTS +# include + +# 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(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
" + "in order to create entries in the issues pane.
" + "The parsers can be configured here.")); + 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 &parsers) + { + for (const auto &p : qAsConst(parserCheckBoxes)) + p.first->setChecked(parsers.contains(p.second)); + emit selectionChanged(); + } + + QList selectedParsers() const + { + QList 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(this->layout()); + QTC_ASSERT(layout, return); + const QList 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> 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 &parsers) +{ + qobject_cast(widget())->setSelectedParsers(parsers); +} + +QList CustomParsersSelectionWidget::selectedParsers() const +{ + return qobject_cast(widget())->selectedParsers(); +} + +void CustomParsersSelectionWidget::updateSummary() +{ + const QList parsers + = qobject_cast(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 -# 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 diff --git a/src/plugins/projectexplorer/customparser.h b/src/plugins/projectexplorer/customparser.h index 00069f117f6..48a88096d2e 100644 --- a/src/plugins/projectexplorer/customparser.h +++ b/src/plugins/projectexplorer/customparser.h @@ -26,12 +26,18 @@ #pragma once #include "ioutputparser.h" +#include "projectconfiguration.h" #include +#include #include +#include 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 &parsers); + QList selectedParsers() const; + +signals: + void selectionChanged(); + +private: + void updateSummary(); +}; + +class CustomParsersAspect : public ProjectConfigurationAspect +{ + Q_OBJECT +public: + CustomParsersAspect(Target *target); + + void setParsers(const QList &parsers) { m_parsers = parsers; } + const QList parsers() const { return m_parsers; } + +private: + void fromMap(const QVariantMap &map) override; + void toMap(QVariantMap &map) const override; + + QList m_parsers; +}; + +} // namespace Internal } // namespace ProjectExplorer -Q_DECLARE_METATYPE(ProjectExplorer::CustomParserExpression::CustomParserChannel); +Q_DECLARE_METATYPE(ProjectExplorer::Internal::CustomParserExpression::CustomParserChannel); diff --git a/src/plugins/projectexplorer/customparserconfigdialog.cpp b/src/plugins/projectexplorer/customparserconfigdialog.cpp index 7e6bb28ee26..5584ae480af 100644 --- a/src/plugins/projectexplorer/customparserconfigdialog.cpp +++ b/src/plugins/projectexplorer/customparserconfigdialog.cpp @@ -35,7 +35,7 @@ namespace ProjectExplorer { namespace Internal { -CustomParserConfigDialog::CustomParserConfigDialog(QDialog *parent) : +CustomParserConfigDialog::CustomParserConfigDialog(QWidget *parent) : QDialog(parent), ui(new Ui::CustomParserConfigDialog) { diff --git a/src/plugins/projectexplorer/customparserconfigdialog.h b/src/plugins/projectexplorer/customparserconfigdialog.h index c34dbfbf4e1..fc787a72b62 100644 --- a/src/plugins/projectexplorer/customparserconfigdialog.h +++ b/src/plugins/projectexplorer/customparserconfigdialog.h @@ -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(); diff --git a/src/plugins/projectexplorer/customparserssettingspage.cpp b/src/plugins/projectexplorer/customparserssettingspage.cpp new file mode 100644 index 00000000000..78163555dbf --- /dev/null +++ b/src/plugins/projectexplorer/customparserssettingspage.cpp @@ -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 +#include + +#include +#include +#include +#include +#include +#include + +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 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 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 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 diff --git a/src/plugins/projectexplorer/customparserssettingspage.h b/src/plugins/projectexplorer/customparserssettingspage.h new file mode 100644 index 00000000000..7790d947bc4 --- /dev/null +++ b/src/plugins/projectexplorer/customparserssettingspage.h @@ -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 + +namespace ProjectExplorer { +namespace Internal { + +class CustomParsersSettingsPage final : public Core::IOptionsPage +{ +public: + CustomParsersSettingsPage(); +}; + +} // namespace Internal +} // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/customtoolchain.cpp b/src/plugins/projectexplorer/customtoolchain.cpp index abe682a48e5..2bada426582 100644 --- a/src/plugins/projectexplorer/customtoolchain.cpp +++ b/src/plugins/projectexplorer/customtoolchain.cpp @@ -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 #include -#include -#include -#include -#include #include -#include +#include +#include +#include +#include +#include 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 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(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(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( + 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( + 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 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::parsers() { QList result; @@ -387,8 +385,6 @@ QList 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 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::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() diff --git a/src/plugins/projectexplorer/customtoolchain.h b/src/plugins/projectexplorer/customtoolchain.h index dc57fe35032..382cb3ebca4 100644 --- a/src/plugins/projectexplorer/customtoolchain.h +++ b/src/plugins/projectexplorer/customtoolchain.h @@ -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 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 diff --git a/src/plugins/projectexplorer/projectexplorer.cpp b/src/plugins/projectexplorer/projectexplorer.cpp index 3648e0c1a4d..5ece973a057 100644 --- a/src/plugins/projectexplorer/projectexplorer.cpp +++ b/src/plugins/projectexplorer/projectexplorer.cpp @@ -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 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(); + // 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 &settings) +{ + if (dd->m_customParsers != settings) { + dd->m_customParsers = settings; + emit m_instance->customParsersChanged(); + } +} + +const QList ProjectExplorerPlugin::customParsers() +{ + return dd->m_customParsers; +} + QStringList ProjectExplorerPlugin::projectFilePatterns() { QStringList patterns; diff --git a/src/plugins/projectexplorer/projectexplorer.h b/src/plugins/projectexplorer/projectexplorer.h index d14177e96e3..5f95965eb39 100644 --- a/src/plugins/projectexplorer/projectexplorer.h +++ b/src/plugins/projectexplorer/projectexplorer.h @@ -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 &settings); + static const QList customParsers(); + static void startRunControl(RunControl *runControl); static void showOutputPaneForRunControl(RunControl *runControl); @@ -192,6 +196,7 @@ signals: void recentProjectsChanged(); void settingsChanged(); + void customParsersChanged(); void runActionsUpdated(); diff --git a/src/plugins/projectexplorer/projectexplorer.pro b/src/plugins/projectexplorer/projectexplorer.pro index 4a8f9621989..d9f7cbf55be 100644 --- a/src/plugins/projectexplorer/projectexplorer.pro +++ b/src/plugins/projectexplorer/projectexplorer.pro @@ -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 \ diff --git a/src/plugins/projectexplorer/projectexplorer.qbs b/src/plugins/projectexplorer/projectexplorer.qbs index 38efedd2102..d6429ca9d86 100644 --- a/src/plugins/projectexplorer/projectexplorer.qbs +++ b/src/plugins/projectexplorer/projectexplorer.qbs @@ -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", diff --git a/src/plugins/projectexplorer/projectexplorerconstants.h b/src/plugins/projectexplorer/projectexplorerconstants.h index 2fc05770560..08e82106109 100644 --- a/src/plugins/projectexplorer/projectexplorerconstants.h +++ b/src/plugins/projectexplorer/projectexplorerconstants.h @@ -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"; diff --git a/src/plugins/projectexplorer/runconfiguration.h b/src/plugins/projectexplorer/runconfiguration.h index d71f87ce75f..e896968c106 100644 --- a/src/plugins/projectexplorer/runconfiguration.h +++ b/src/plugins/projectexplorer/runconfiguration.h @@ -136,8 +136,6 @@ public: bool isConfigured() const { return checkForIssues().isEmpty(); } virtual Tasks checkForIssues() const { return {}; } - Utils::OutputFormatter *createOutputFormatter() const; - using CommandLineGetter = std::function; void setCommandLineGetter(const CommandLineGetter &cmdGetter); Utils::CommandLine commandLine() const; diff --git a/src/plugins/projectexplorer/runcontrol.cpp b/src/plugins/projectexplorer/runcontrol.cpp index 2c07d016db7..29b98c945c6 100644 --- a/src/plugins/projectexplorer/runcontrol.cpp +++ b/src/plugins/projectexplorer/runcontrol.cpp @@ -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 #include @@ -825,7 +826,14 @@ void RunControlPrivate::showError(const QString &msg) QList RunControl::createOutputParsers() const { - return OutputFormatterFactory::createFormatters(target()); + QList parsers = OutputFormatterFactory::createFormatters(target()); + if (const auto customParsersAspect = runConfiguration()->aspect()) { + for (const Core::Id id : customParsersAspect->parsers()) { + if (CustomParser * const parser = CustomParser::createFromId(id)) + parsers << parser; + } + } + return parsers; } Core::Id RunControl::runMode() const