ProjectExplorer: Do not auto-remove customized run configurations

Until now, it could easily happen that a user's carefully fine-tuned run
configurations disappeared just because of e.g. a temporarily broken
build system file or simply a switch to a different build configuration.
As this is clearly not acceptable, we now make sure customized run
configurations are not thrown away even when there is currently no
matching factory for them.

Fixes: QTCREATORBUG-23163
Fixes: QTCREATORBUG-28273
Change-Id: Ic011a650d15a2897108df986c7e9fe410852e926
Reviewed-by: <github-actions-qt-creator@cristianadam.eu>
Reviewed-by: hjk <hjk@qt.io>
This commit is contained in:
Christian Kandeler
2023-06-27 16:26:56 +02:00
parent 1cbd417906
commit b1876052bd
8 changed files with 69 additions and 10 deletions

View File

@@ -37,7 +37,7 @@ bool DisplayName::usesDefaultValue() const
void DisplayName::toMap(QVariantMap &map, const QString &key) const void DisplayName::toMap(QVariantMap &map, const QString &key) const
{ {
if (!usesDefaultValue()) if (m_forceSerialization || !usesDefaultValue())
map.insert(key, m_value); map.insert(key, m_value);
} }

View File

@@ -22,6 +22,7 @@ public:
QString value() const; QString value() const;
QString defaultValue() const { return m_defaultValue; } QString defaultValue() const { return m_defaultValue; }
bool usesDefaultValue() const; bool usesDefaultValue() const;
void forceSerialization() { m_forceSerialization = true; }
void toMap(QVariantMap &map, const QString &key) const; void toMap(QVariantMap &map, const QString &key) const;
void fromMap(const QVariantMap &map, const QString &key); void fromMap(const QVariantMap &map, const QString &key);
@@ -29,6 +30,7 @@ public:
private: private:
QString m_value; QString m_value;
QString m_defaultValue; QString m_defaultValue;
bool m_forceSerialization = false;
}; };
bool QTCREATOR_UTILS_EXPORT operator==(const DisplayName &dn1, const DisplayName &dn2); bool QTCREATOR_UTILS_EXPORT operator==(const DisplayName &dn1, const DisplayName &dn2);

View File

@@ -38,6 +38,7 @@ public:
bool usesDefaultDisplayName() const { return m_displayName.usesDefaultValue(); } bool usesDefaultDisplayName() const { return m_displayName.usesDefaultValue(); }
void setDisplayName(const QString &name); void setDisplayName(const QString &name);
void setDefaultDisplayName(const QString &name); void setDefaultDisplayName(const QString &name);
void forceDisplayNameSerialization() { m_displayName.forceSerialization(); }
void setToolTip(const QString &text); void setToolTip(const QString &text);
QString toolTip() const; QString toolTip() const;

View File

@@ -5,9 +5,10 @@
#include "buildconfiguration.h" #include "buildconfiguration.h"
#include "deployconfiguration.h" #include "deployconfiguration.h"
#include "projectconfiguration.h"
#include "projectexplorertr.h"
#include "runconfiguration.h" #include "runconfiguration.h"
#include "target.h" #include "target.h"
#include "projectconfiguration.h"
#include <utils/algorithm.h> #include <utils/algorithm.h>
#include <utils/stringutils.h> #include <utils/stringutils.h>
@@ -30,6 +31,9 @@ static bool isOrderedBefore(const ProjectConfiguration *a, const ProjectConfigur
ProjectConfigurationModel::ProjectConfigurationModel(Target *target) : ProjectConfigurationModel::ProjectConfigurationModel(Target *target) :
m_target(target) m_target(target)
{ {
connect(target, &Target::runConfigurationsUpdated, this, [this] {
emit dataChanged(index(0, 0), index(rowCount(), 0));
});
} }
int ProjectConfigurationModel::rowCount(const QModelIndex &parent) const int ProjectConfigurationModel::rowCount(const QModelIndex &parent) const
@@ -87,13 +91,17 @@ void ProjectConfigurationModel::displayNameChanged(ProjectConfiguration *pc)
QVariant ProjectConfigurationModel::data(const QModelIndex &index, int role) const QVariant ProjectConfigurationModel::data(const QModelIndex &index, int role) const
{ {
if (role == Qt::DisplayRole) { if (index.row() >= m_projectConfigurations.size())
const int row = index.row(); return {};
if (row < m_projectConfigurations.size()) ProjectConfiguration * const config = m_projectConfigurations.at(index.row());
return m_projectConfigurations.at(row)->expandedDisplayName();
}
return QVariant(); if (role == Qt::DisplayRole) {
QString displayName = config->expandedDisplayName();
if (const auto rc = qobject_cast<RunConfiguration *>(config); rc && !rc->hasCreator())
displayName += QString(" [%1]").arg(Tr::tr("unavailable"));
return displayName;
}
return {};
} }
ProjectConfiguration *ProjectConfigurationModel::projectConfigurationAt(int i) const ProjectConfiguration *ProjectConfigurationModel::projectConfigurationAt(int i) const

View File

@@ -45,6 +45,7 @@ using namespace ProjectExplorer::Internal;
namespace ProjectExplorer { namespace ProjectExplorer {
const char BUILD_KEY[] = "ProjectExplorer.RunConfiguration.BuildKey"; const char BUILD_KEY[] = "ProjectExplorer.RunConfiguration.BuildKey";
const char CUSTOMIZED_KEY[] = "ProjectExplorer.RunConfiguration.Customized";
/////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////
// //
@@ -162,6 +163,7 @@ RunConfiguration::RunConfiguration(Target *target, Utils::Id id)
: ProjectConfiguration(target, id) : ProjectConfiguration(target, id)
{ {
QTC_CHECK(target && target == this->target()); QTC_CHECK(target && target == this->target());
forceDisplayNameSerialization();
connect(target, &Target::parsingFinished, this, &RunConfiguration::update); connect(target, &Target::parsingFinished, this, &RunConfiguration::update);
m_expander.setDisplayName(Tr::tr("Run Settings")); m_expander.setDisplayName(Tr::tr("Run Settings"));
@@ -239,6 +241,34 @@ bool RunConfiguration::isConfigured() const
return !Utils::anyOf(checkForIssues(), [](const Task &t) { return t.type == Task::Error; }); return !Utils::anyOf(checkForIssues(), [](const Task &t) { return t.type == Task::Error; });
} }
bool RunConfiguration::isCustomized() const
{
if (m_customized)
return true;
QVariantMap state = toMapSimple();
// TODO: Why do we save this at all? It's a computed value.
state.remove("RunConfiguration.WorkingDirectory.default");
return state != m_pristineState;
}
bool RunConfiguration::hasCreator() const
{
return Utils::contains(RunConfigurationFactory::creatorsForTarget(target()),
[this](const RunConfigurationCreationInfo &info) {
return info.factory->runConfigurationId() == id() && info.buildKey == buildKey();
});
}
void RunConfiguration::setPristineState()
{
if (!m_customized) {
m_pristineState = toMapSimple();
m_pristineState.remove("RunConfiguration.WorkingDirectory.default");
}
}
void RunConfiguration::addAspectFactory(const AspectFactory &aspectFactory) void RunConfiguration::addAspectFactory(const AspectFactory &aspectFactory)
{ {
theAspectFactories.push_back(aspectFactory); theAspectFactories.push_back(aspectFactory);
@@ -277,8 +307,14 @@ Task RunConfiguration::createConfigurationIssue(const QString &description) cons
QVariantMap RunConfiguration::toMap() const QVariantMap RunConfiguration::toMap() const
{ {
QVariantMap map = ProjectConfiguration::toMap(); QVariantMap map = toMapSimple();
map.insert(CUSTOMIZED_KEY, isCustomized());
return map;
}
QVariantMap RunConfiguration::toMapSimple() const
{
QVariantMap map = ProjectConfiguration::toMap();
map.insert(BUILD_KEY, m_buildKey); map.insert(BUILD_KEY, m_buildKey);
// FIXME: Remove this id mangling, e.g. by using a separate entry for the build key. // FIXME: Remove this id mangling, e.g. by using a separate entry for the build key.
@@ -344,6 +380,7 @@ bool RunConfiguration::fromMap(const QVariantMap &map)
if (!ProjectConfiguration::fromMap(map)) if (!ProjectConfiguration::fromMap(map))
return false; return false;
m_customized = m_customized || map.value(CUSTOMIZED_KEY, false).toBool();
m_buildKey = map.value(BUILD_KEY).toString(); m_buildKey = map.value(BUILD_KEY).toString();
if (m_buildKey.isEmpty()) { if (m_buildKey.isEmpty()) {
@@ -584,6 +621,7 @@ RunConfiguration *RunConfigurationCreationInfo::create(Target *target) const
rc->m_buildKey = buildKey; rc->m_buildKey = buildKey;
rc->update(); rc->update();
rc->setDisplayName(displayName); rc->setDisplayName(displayName);
rc->setPristineState();
return rc; return rc;
} }
@@ -597,6 +635,7 @@ RunConfiguration *RunConfigurationFactory::restore(Target *parent, const QVarian
RunConfiguration *rc = factory->create(parent); RunConfiguration *rc = factory->create(parent);
if (rc->fromMap(map)) { if (rc->fromMap(map)) {
rc->update(); rc->update();
rc->setPristineState();
return rc; return rc;
} }
delete rc; delete rc;

View File

@@ -111,7 +111,10 @@ public:
QWidget *createConfigurationWidget(); QWidget *createConfigurationWidget();
bool isConfigured() const; bool isConfigured() const;
bool isCustomized() const;
bool hasCreator() const;
virtual Tasks checkForIssues() const { return {}; } virtual Tasks checkForIssues() const { return {}; }
void setPristineState();
using CommandLineGetter = std::function<Utils::CommandLine()>; using CommandLineGetter = std::function<Utils::CommandLine()>;
void setCommandLineGetter(const CommandLineGetter &cmdGetter); void setCommandLineGetter(const CommandLineGetter &cmdGetter);
@@ -170,6 +173,7 @@ private:
// Any additional data should be handled by aspects. // Any additional data should be handled by aspects.
bool fromMap(const QVariantMap &map) final; bool fromMap(const QVariantMap &map) final;
QVariantMap toMap() const final; QVariantMap toMap() const final;
QVariantMap toMapSimple() const;
static void addAspectFactory(const AspectFactory &aspectFactory); static void addAspectFactory(const AspectFactory &aspectFactory);
@@ -182,6 +186,8 @@ private:
RunnableModifier m_runnableModifier; RunnableModifier m_runnableModifier;
Updater m_updater; Updater m_updater;
Utils::MacroExpander m_expander; Utils::MacroExpander m_expander;
QVariantMap m_pristineState;
bool m_customized = false;
}; };
class RunConfigurationCreationInfo class RunConfigurationCreationInfo

View File

@@ -706,7 +706,7 @@ void Target::updateDefaultRunConfigurations()
present = true; present = true;
} }
} }
if (!present) if (!present && !rc->isCustomized())
toRemove.append(rc); toRemove.append(rc);
} }
configuredCount -= toRemove.count(); configuredCount -= toRemove.count();
@@ -797,6 +797,8 @@ void Target::updateDefaultRunConfigurations()
// Remove the RCs that are no longer needed: // Remove the RCs that are no longer needed:
for (RunConfiguration *rc : std::as_const(removalList)) for (RunConfiguration *rc : std::as_const(removalList))
removeRunConfiguration(rc); removeRunConfiguration(rc);
emit runConfigurationsUpdated();
} }
QVariant Target::namedSettings(const QString &name) const QVariant Target::namedSettings(const QString &name) const

View File

@@ -131,6 +131,7 @@ signals:
void removedRunConfiguration(ProjectExplorer::RunConfiguration *rc); void removedRunConfiguration(ProjectExplorer::RunConfiguration *rc);
void addedRunConfiguration(ProjectExplorer::RunConfiguration *rc); void addedRunConfiguration(ProjectExplorer::RunConfiguration *rc);
void activeRunConfigurationChanged(ProjectExplorer::RunConfiguration *rc); void activeRunConfigurationChanged(ProjectExplorer::RunConfiguration *rc);
void runConfigurationsUpdated();
void removedBuildConfiguration(ProjectExplorer::BuildConfiguration *bc); void removedBuildConfiguration(ProjectExplorer::BuildConfiguration *bc);
void addedBuildConfiguration(ProjectExplorer::BuildConfiguration *bc); void addedBuildConfiguration(ProjectExplorer::BuildConfiguration *bc);