Commit 131c7a1c authored by Tobias Hunger's avatar Tobias Hunger

ProjectExplorer: Introduce base class for enabled/disabled project configuration

... and use this as a base for all RunConfigurations.

Clean out code in the individual run configurations dealing with their
enabled/disabled state.

Change-Id: Icc2ea136b056f7aea7ce96480b4402459d7ac0ce
Reviewed-by: Tim Jenssen's avatarTim Jenssen <tim.jenssen@qt.io>
parent c01ddc46
......@@ -40,7 +40,6 @@ public:
BareMetalCustomRunConfiguration(ProjectExplorer::Target *parent,
BareMetalCustomRunConfiguration *source);
bool isEnabled() const override { return true; }
bool isConfigured() const override;
ConfigurationState ensureConfigured(QString *errorMessage) override;
QWidget *createConfigurationWidget() override;
......
......@@ -75,17 +75,6 @@ void BareMetalRunConfiguration::init()
this, &BareMetalRunConfiguration::handleBuildSystemDataUpdated); // Handles device changes, etc.
}
bool BareMetalRunConfiguration::isEnabled() const
{
m_disabledReason.clear(); // FIXME: Check this makes sense.
return true;
}
QString BareMetalRunConfiguration::disabledReason() const
{
return m_disabledReason;
}
QWidget *BareMetalRunConfiguration::createConfigurationWidget()
{
return new BareMetalRunConfigurationWidget(this);
......@@ -167,11 +156,6 @@ QString BareMetalRunConfiguration::buildSystemTarget() const
return (bst == targets.list.constEnd()) ? QString() : bst->targetName;
}
void BareMetalRunConfiguration::setDisabledReason(const QString &reason) const
{
m_disabledReason = reason;
}
void BareMetalRunConfiguration::handleBuildSystemDataUpdated()
{
emit targetInformationChanged();
......
......@@ -44,8 +44,6 @@ public:
explicit BareMetalRunConfiguration(ProjectExplorer::Target *parent, Core::Id id,
const QString &projectFilePath);
bool isEnabled() const override;
QString disabledReason() const override;
QWidget *createConfigurationWidget() override;
Utils::OutputFormatter *createOutputFormatter() const override;
......@@ -70,14 +68,12 @@ protected:
BareMetalRunConfiguration(ProjectExplorer::Target *parent, BareMetalRunConfiguration *source);
bool fromMap(const QVariantMap &map) override;
QString defaultDisplayName();
void setDisabledReason(const QString &reason) const;
private:
void handleBuildSystemDataUpdated();
void init();
QString m_projectFilePath;
mutable QString m_disabledReason;
QString m_workingDirectory;
};
......
......@@ -152,7 +152,7 @@ void CMakeProject::updateProjectData(CMakeBuildConfiguration *bc)
}
updateApplicationAndDeploymentTargets();
updateTargetRunConfigurations(t);
t->updateDefaultRunConfigurations();
createGeneratedCodeModelSupport();
......@@ -446,35 +446,6 @@ QStringList CMakeProject::filesGeneratedFrom(const QString &sourceFile) const
}
}
void CMakeProject::updateTargetRunConfigurations(Target *t)
{
// *Update* existing runconfigurations (no need to update new ones!):
QHash<QString, const CMakeBuildTarget *> buildTargetHash;
const QList<CMakeBuildTarget> buildTargetList = buildTargets();
foreach (const CMakeBuildTarget &bt, buildTargetList) {
if (bt.targetType != ExecutableType || bt.executable.isEmpty())
continue;
buildTargetHash.insert(bt.title, &bt);
}
foreach (RunConfiguration *rc, t->runConfigurations()) {
auto cmakeRc = qobject_cast<CMakeRunConfiguration *>(rc);
if (!cmakeRc)
continue;
auto btIt = buildTargetHash.constFind(cmakeRc->title());
cmakeRc->setEnabled(btIt != buildTargetHash.constEnd());
if (btIt != buildTargetHash.constEnd()) {
cmakeRc->setExecutable(btIt.value()->executable.toString());
cmakeRc->setBaseWorkingDirectory(btIt.value()->workingDirectory);
}
}
// create new and remove obsolete RCs using the factories
t->updateDefaultRunConfigurations();
}
void CMakeProject::updateApplicationAndDeploymentTargets()
{
Target *t = activeTarget();
......
......@@ -122,7 +122,6 @@ private:
void createGeneratedCodeModelSupport();
QStringList filesGeneratedFrom(const QString &sourceFile) const final;
void updateTargetRunConfigurations(ProjectExplorer::Target *t);
void updateApplicationAndDeploymentTargets();
ProjectExplorer::Target *m_connectedTarget = nullptr;
......
......@@ -82,8 +82,7 @@ CMakeRunConfiguration::CMakeRunConfiguration(Target *parent, CMakeRunConfigurati
RunConfiguration(parent, source),
m_buildSystemTarget(source->m_buildSystemTarget),
m_executable(source->m_executable),
m_title(source->m_title),
m_enabled(source->m_enabled)
m_title(source->m_title)
{
ctor();
}
......@@ -144,38 +143,31 @@ QString CMakeRunConfiguration::defaultDisplayName() const
{
if (m_title.isEmpty())
return tr("Run CMake kit");
QString result = m_title;
if (!m_enabled) {
result += QLatin1Char(' ');
result += tr("(disabled)");
}
return result;
return m_title;
}
QWidget *CMakeRunConfiguration::createConfigurationWidget()
void CMakeRunConfiguration::updateEnabledState()
{
return new CMakeRunConfigurationWidget(this);
auto cp = qobject_cast<CMakeProject *>(target()->project());
if (!cp->hasBuildTarget(m_buildSystemTarget))
setEnabled(false);
else
RunConfiguration::updateEnabledState();
}
void CMakeRunConfiguration::setEnabled(bool b)
{
if (m_enabled == b)
return;
m_enabled = b;
emit enabledChanged();
setDefaultDisplayName(defaultDisplayName());
}
bool CMakeRunConfiguration::isEnabled() const
QWidget *CMakeRunConfiguration::createConfigurationWidget()
{
return m_enabled;
return new CMakeRunConfigurationWidget(this);
}
QString CMakeRunConfiguration::disabledReason() const
{
if (!m_enabled)
return tr("The executable is not built by the current build configuration");
return QString();
auto cp = qobject_cast<CMakeProject *>(target()->project());
QTC_ASSERT(cp, return QString());
if (cp->hasParsingData() && !cp->hasBuildTarget(m_buildSystemTarget))
return tr("The project no longer builds the target associated with this run configuration.");
return RunConfiguration::disabledReason();
}
static void updateExecutable(CMakeRunConfiguration *rc, Utils::FancyLineEdit *fle)
......
......@@ -51,9 +51,6 @@ public:
QVariantMap toMap() const override;
void setEnabled(bool b);
bool isEnabled() const override;
QString disabledReason() const override;
QString buildSystemTarget() const final { return m_buildSystemTarget; }
......@@ -63,6 +60,8 @@ protected:
bool fromMap(const QVariantMap &map) override;
QString defaultDisplayName() const;
void updateEnabledState() final;
private:
QString baseWorkingDirectory() const;
void ctor();
......@@ -70,7 +69,6 @@ private:
const QString m_buildSystemTarget;
QString m_executable;
QString m_title;
bool m_enabled = true;
};
class CMakeRunConfigurationWidget : public QWidget
......
......@@ -111,48 +111,16 @@ IosRunConfiguration::IosRunConfiguration(Target *parent, IosRunConfiguration *so
void IosRunConfiguration::init()
{
QmakeProject *project = static_cast<QmakeProject *>(target()->project());
m_parseSuccess = project->validParse(m_profilePath);
m_parseInProgress = project->parseInProgress(m_profilePath);
m_lastIsEnabled = isEnabled();
m_lastDisabledReason = disabledReason();
updateDisplayNames();
connect(DeviceManager::instance(), &DeviceManager::updated,
this, &IosRunConfiguration::deviceChanges);
connect(KitManager::instance(), &KitManager::kitsChanged,
this, &IosRunConfiguration::deviceChanges);
connect(project, &QmakeProject::proFileUpdated,
this, &IosRunConfiguration::proFileUpdated);
}
void IosRunConfiguration::enabledCheck()
{
bool newIsEnabled = isEnabled();
QString newDisabledReason = disabledReason();
if (newDisabledReason != m_lastDisabledReason || newIsEnabled != m_lastIsEnabled) {
m_lastDisabledReason = newDisabledReason;
m_lastIsEnabled = newIsEnabled;
emit enabledChanged();
}
}
void IosRunConfiguration::deviceChanges() {
updateDisplayNames();
enabledCheck();
}
void IosRunConfiguration::proFileUpdated(QmakeProFile *pro, bool success,
bool parseInProgress)
{
if (m_profilePath != pro->filePath())
return;
m_parseSuccess = success;
m_parseInProgress = parseInProgress;
if (success && !parseInProgress) {
updateDisplayNames();
emit localExecutableChanged();
}
enabledCheck();
updateEnabledState();
}
QWidget *IosRunConfiguration::createConfigurationWidget()
......@@ -182,6 +150,21 @@ void IosRunConfiguration::updateDisplayNames()
setDisplayName(tr("Run %1 on %2").arg(applicationName()).arg(devName));
}
void IosRunConfiguration::updateEnabledState()
{
Core::Id devType = DeviceTypeKitInformation::deviceTypeId(target()->kit());
if (devType != Constants::IOS_DEVICE_TYPE && devType != Constants::IOS_SIMULATOR_TYPE) {
setEnabled(false);
return;
}
IDevice::ConstPtr dev = DeviceKitInformation::device(target()->kit());
if (dev.isNull() || dev->deviceState() != IDevice::DeviceReadyToUse) {
setEnabled(false);
return;
}
return RunConfiguration::updateEnabledState();
}
IosDeployStep *IosRunConfiguration::deployStep() const
{
DeployConfiguration *config = target()->activeDeployConfiguration();
......@@ -287,26 +270,8 @@ QString IosRunConfiguration::buildSystemTarget() const
return static_cast<QmakeProject *>(target()->project())->mapProFilePathToTarget(m_profilePath);
}
bool IosRunConfiguration::isEnabled() const
{
if (m_parseInProgress || !m_parseSuccess)
return false;
Core::Id devType = DeviceTypeKitInformation::deviceTypeId(target()->kit());
if (devType != Constants::IOS_DEVICE_TYPE && devType != Constants::IOS_SIMULATOR_TYPE)
return false;
IDevice::ConstPtr dev = DeviceKitInformation::device(target()->kit());
if (dev.isNull() || dev->deviceState() != IDevice::DeviceReadyToUse)
return false;
return RunConfiguration::isEnabled();
}
QString IosRunConfiguration::disabledReason() const
{
if (m_parseInProgress)
return tr("The .pro file \"%1\" is currently being parsed.").arg(m_profilePath.fileName());
if (!m_parseSuccess)
return static_cast<QmakeProject *>(target()->project())
->disabledReasonForRunConfiguration(m_profilePath);
Core::Id devType = DeviceTypeKitInformation::deviceTypeId(target()->kit());
if (devType != Constants::IOS_DEVICE_TYPE && devType != Constants::IOS_SIMULATOR_TYPE)
return tr("Kit has incorrect device type for running on iOS devices.");
......
......@@ -58,7 +58,6 @@ public:
QString applicationName() const;
Utils::FileName bundleDirectory() const;
Utils::FileName localExecutable() const;
bool isEnabled() const override;
QString disabledReason() const override;
IosDeviceType deviceType() const;
void setDeviceType(const IosDeviceType &deviceType);
......@@ -75,18 +74,13 @@ signals:
void localExecutableChanged();
private:
void proFileUpdated(QmakeProjectManager::QmakeProFile *pro, bool success, bool parseInProgress);
void deviceChanges();
void init();
void enabledCheck();
friend class IosRunConfigurationWidget;
void updateDisplayNames();
void updateEnabledState() final;
Utils::FileName m_profilePath;
QString m_lastDisabledReason;
bool m_lastIsEnabled;
bool m_parseInProgress;
bool m_parseSuccess;
IosDeviceType m_deviceType;
};
......
......@@ -123,3 +123,26 @@ QString ProjectExplorer::displayNameFromMap(const QVariantMap &map)
{
return map.value(QLatin1String(DISPLAY_NAME_KEY), QString()).toString();
}
bool StatefulProjectConfiguration::isEnabled() const
{
return m_isEnabled;
}
StatefulProjectConfiguration::StatefulProjectConfiguration(QObject *parent, Core::Id id) :
ProjectConfiguration(parent, id)
{ }
StatefulProjectConfiguration::StatefulProjectConfiguration(QObject *parent,
const StatefulProjectConfiguration *source) :
ProjectConfiguration(parent, source),
m_isEnabled(source->m_isEnabled)
{ }
void StatefulProjectConfiguration::setEnabled(bool enabled)
{
if (enabled == m_isEnabled)
return;
m_isEnabled = enabled;
emit enabledChanged();
}
......@@ -81,6 +81,30 @@ private:
Utils::MacroExpander m_macroExpander;
};
class PROJECTEXPLORER_EXPORT StatefulProjectConfiguration : public ProjectConfiguration
{
Q_OBJECT
public:
StatefulProjectConfiguration() = default;
bool isEnabled() const;
virtual QString disabledReason() const = 0;
signals:
void enabledChanged();
protected:
StatefulProjectConfiguration(QObject *parent, Core::Id id);
StatefulProjectConfiguration(QObject *parent, const StatefulProjectConfiguration *source);
void setEnabled(bool enabled);
private:
bool m_isEnabled;
};
// helper functions:
PROJECTEXPLORER_EXPORT Core::Id idFromMap(const QVariantMap &map);
PROJECTEXPLORER_EXPORT QString displayNameFromMap(const QVariantMap &map);
......
......@@ -172,21 +172,24 @@ void IRunConfigurationAspect::resetProjectToGlobalSettings()
A run configuration specifies how a target should be run, while a runner
does the actual running.
All RunControls and the target hold a shared pointer to the run
configuration. That is, the lifetime of the run configuration might exceed
the life of the target.
The user might still have a RunControl running (or output tab of that RunControl open)
and yet unloaded the target.
Also, a run configuration might be already removed from the list of run
configurations
for a target, but still be runnable via the output tab.
The target owns the RunConfiguraitons and a RunControl will need to copy all
necessary data as the RunControl may continue to exist after the RunConfiguration
has been destroyed.
A RunConfiguration disables itself when the project is parsing or has no parsing
data available. The disabledReason() method can be used to get a user-facing string
describing why the RunConfiguration considers itself unfit for use.
Override updateEnabledState() to change the enabled state handling. Override
disabledReasons() to provide better/more descriptions to the user.
Connect signals that may change enabled state of your RunConfiguration to updateEnabledState.
*/
static std::vector<RunConfiguration::AspectFactory> theAspectFactories;
RunConfiguration::RunConfiguration(Target *target, Core::Id id) :
ProjectConfiguration(target, id)
StatefulProjectConfiguration(target, id)
{
Q_ASSERT(target);
ctor();
......@@ -196,7 +199,7 @@ RunConfiguration::RunConfiguration(Target *target, Core::Id id) :
}
RunConfiguration::RunConfiguration(Target *target, RunConfiguration *source) :
ProjectConfiguration(target, source)
StatefulProjectConfiguration(target, source)
{
Q_ASSERT(target);
ctor();
......@@ -212,6 +215,22 @@ RunConfiguration::~RunConfiguration()
qDeleteAll(m_aspects);
}
QString RunConfiguration::disabledReason() const
{
if (target()->project()->isParsing())
return tr("The Project is currently being parsed.");
if (!target()->project()->hasParsingData())
return tr("The project could not be fully parsed.");
return QString();
}
void RunConfiguration::updateEnabledState()
{
Project *p = target()->project();
setEnabled(!p->isParsing() && p->hasParsingData());
}
void RunConfiguration::addAspectFactory(const AspectFactory &aspectFactory)
{
theAspectFactories.push_back(aspectFactory);
......@@ -225,6 +244,17 @@ void RunConfiguration::addExtraAspect(IRunConfigurationAspect *aspect)
void RunConfiguration::ctor()
{
connect(target()->project(), &Project::parsingStarted,
this, [this]() { updateEnabledState(); });
connect(target()->project(), &Project::parsingFinished,
this, [this]() { updateEnabledState(); });
connect(target(), &Target::addedRunConfiguration,
this, [this](const RunConfiguration *rc) {
if (rc == this)
updateEnabledState();
});
connect(this, &RunConfiguration::enabledChanged,
this, &RunConfiguration::requestRunActionsUpdate);
......@@ -259,20 +289,6 @@ RunConfiguration *RunConfiguration::startupRunConfiguration()
return nullptr;
}
/*!
Checks whether a run configuration is enabled.
*/
bool RunConfiguration::isEnabled() const
{
return true;
}
QString RunConfiguration::disabledReason() const
{
return QString();
}
bool RunConfiguration::isConfigured() const
{
return true;
......@@ -287,7 +303,6 @@ RunConfiguration::ConfigurationState RunConfiguration::ensureConfigured(QString
return UnConfigured;
}
BuildConfiguration *RunConfiguration::activeBuildConfiguration() const
{
if (!target())
......
......@@ -199,15 +199,15 @@ private:
};
// Documentation inside.
class PROJECTEXPLORER_EXPORT RunConfiguration : public ProjectConfiguration
class PROJECTEXPLORER_EXPORT RunConfiguration : public StatefulProjectConfiguration
{
Q_OBJECT
public:
~RunConfiguration() override;
virtual bool isEnabled() const;
virtual QString disabledReason() const;
QString disabledReason() const override;
virtual QWidget *createConfigurationWidget() = 0;
virtual bool isConfigured() const;
......@@ -252,7 +252,6 @@ public:
}
signals:
void enabledChanged();
void requestRunActionsUpdate();
void configurationFinished();
......@@ -263,6 +262,8 @@ protected:
/// convenience function to get current build configuration.
BuildConfiguration *activeBuildConfiguration() const;
virtual void updateEnabledState();
private:
void ctor();
......
......@@ -156,8 +156,6 @@ public:
QWidget *createConfigurationWidget() override;
QVariantMap toMap() const override;
bool fromMap(const QVariantMap &map) override;
bool isEnabled() const override { return m_enabled; }
QString disabledReason() const override;
Runnable runnable() const override;
bool supportsDebugger() const { return true; }
......@@ -165,7 +163,6 @@ public:
QString arguments() const;
QString interpreter() const { return m_interpreter; }
void setInterpreter(const QString &interpreter) { m_interpreter = interpreter; }
void setEnabled(bool b);
private:
friend class PythonRunConfigurationFactory;
......@@ -174,15 +171,13 @@ private:
QString m_interpreter;
QString m_mainScript;
bool m_enabled;
};
////////////////////////////////////////////////////////////////
PythonRunConfiguration::PythonRunConfiguration(Target *parent, Core::Id id) :
RunConfiguration(parent, id),
m_mainScript(scriptFromId(id)),
m_enabled(true)
m_mainScript(scriptFromId(id))
{
Environment sysEnv = Environment::systemEnvironment();
const QString exec = sysEnv.searchInPath("python").toString();
......@@ -197,8 +192,7 @@ PythonRunConfiguration::PythonRunConfiguration(Target *parent, Core::Id id) :
PythonRunConfiguration::PythonRunConfiguration(Target *parent, PythonRunConfiguration *source) :
RunConfiguration(parent, source),
m_interpreter(source->interpreter()),
m_mainScript(source->m_mainScript),
m_enabled(source->m_enabled)
m_mainScript(source->m_mainScript)
{
setDefaultDisplayName(defaultDisplayName());
}
......@@ -220,10 +214,7 @@ bool PythonRunConfiguration::fromMap(const QVariantMap &map)
QString PythonRunConfiguration::defaultDisplayName() const
{
QString result = tr("Run %1").arg(m_mainScript);
if (!m_enabled)
result += ' ' + tr("(disabled)");
return result;
return tr("Run %1").arg(m_mainScript);
}
QWidget *PythonRunConfiguration::createConfigurationWidget()
......@@ -231,22 +222,6 @@ QWidget *PythonRunConfiguration::createConfigurationWidget()
return new PythonRunConfigurationWidget(this);
}
void PythonRunConfiguration::setEnabled(bool b)
{
if (m_enabled == b)
return;
m_enabled = b;
emit enabledChanged();
setDefaultDisplayName(defaultDisplayName());
}
QString PythonRunConfiguration::disabledReason() const
{
if (!m_enabled)
return tr("The script is currently disabled.");
return QString();
}
Runnable PythonRunConfiguration::runnable() const
{
StandardRunnable r;
......
......@@ -44,11 +44,11 @@
#include <utils/detailswidget.h>
#include <utils/stringutils.h>
#include <utils/persistentsettings.h>
#include <utils/utilsicons.h>
#include <qtsupport/qtoutputformatter.h>
#include <qtsupport/qtsupportconstants.h>
#include <qtsupport/qtkitinformation.h>
#include <utils/hostosinfo.h>
#include <utils/utilsicons.h>
#include "api/runenvironment.h"
......@@ -140,34 +140,15 @@ QbsRunConfiguration::QbsRunConfiguration(Target *parent, QbsRunConfiguration *so
ctor();
}
bool QbsRunConfiguration::isEnabled() const
{
QbsProject *project = static_cast<QbsProject *>(target()->project());
return !project->isParsing() && project->hasParseResult();
}
QString QbsRunConfiguration::disabledReason() const
{
QbsProject *project = static_cast<QbsProject *>(target()->project());
if (project->isParsing())
return tr("The .qbs files are currently being parsed.");
if (!project->hasParseResult())
return tr("Parsing of .qbs files has failed.");
return QString();
}
void QbsRunConfiguration::ctor()
{
setDefaultDisplayName(defaultDisplayName());
QbsProject *project = static_cast<QbsProject *>(target()->project());
connect(project, &Project::parsingStarted, this, &RunConfiguration::enabledChanged);
connect(project, &Project::parsingFinished, this, [this](bool success) {
auto terminalAspect = extraAspect<TerminalAspect>();
if (success && !terminalAspect->isUserSet())
terminalAspect->setUseTerminal(isConsoleApplication());
emit enabledChanged();
});
connect(BuildManager::instance(), &BuildManager::buildStateChanged, this,
[this, project](Project *p) {
......
......@@ -64,8 +64,6 @@ class QbsRunConfiguration : public ProjectExplorer::RunConfiguration
public:
QbsRunConfiguration(ProjectExplorer::Target *parent, Core::Id id);
bool isEnabled() const override;
QString disabledReason() const override;
QWidget *createConfigurationWidget() override;
ProjectExplorer::Runnable runnable() const override;
......
......@@ -58,26 +58,20 @@ QmakeAndroidRunConfiguration::QmakeAndroidRunConfiguration(Target *parent, Core:
: AndroidRunConfiguration(parent, id)
, m_proFilePath(path)
{
QmakeProject *project = static_cast<QmakeProject *>(parent->project());
m_parseSuccess = project->validParse(m_proFilePath);
m_parseInProgress = project->parseInProgress(m_proFilePath);
init();
ctor();
}
QmakeAndroidRunConfiguration::QmakeAndroidRunConfiguration(Target *parent, QmakeAndroidRunConfiguration *source)
: AndroidRunConfiguration(parent, source)
, m_proFilePath(source->m_proFilePath)
, m_parseSuccess(source->m_parseSuccess)
, m_parseInProgress(source->m_parseInProgress)
{
init();
ctor();
}
void QmakeAndroidRunConfiguration::init()
void QmakeAndroidRunConfiguration::ctor()
{
setDefaultDisplayName(defaultDisplayName());
connect(qmakeProject(), &QmakeProject::proFileUpdated,
this, &QmakeAndroidRunConfiguration::proFileUpdated);