From e202e4b8e0c891c362c29905e2f310ff9ee02279 Mon Sep 17 00:00:00 2001 From: Daniel Teske Date: Mon, 24 Oct 2011 13:10:38 +0000 Subject: [PATCH] Fix various crashes Task-Number: QTCREATORBUG-6365 Change-Id: I19a200e3c811eef83d591f6eacca3e48eb0fba8f Reviewed-by: Tobias Hunger --- src/plugins/projectexplorer/buildmanager.cpp | 77 ++++++++++++++----- src/plugins/projectexplorer/buildmanager.h | 6 +- .../buildsettingspropertiespage.cpp | 25 ++++++ src/plugins/projectexplorer/buildstep.cpp | 10 +++ src/plugins/projectexplorer/buildstep.h | 2 + src/plugins/projectexplorer/project.cpp | 11 ++- src/plugins/projectexplorer/project.h | 2 +- .../projectexplorer/projectexplorer.cpp | 14 ++++ .../runsettingspropertiespage.cpp | 32 ++++++-- src/plugins/projectexplorer/target.cpp | 22 +++++- src/plugins/projectexplorer/target.h | 4 +- .../projectexplorer/targetsettingspanel.cpp | 37 +++++++-- 12 files changed, 196 insertions(+), 46 deletions(-) diff --git a/src/plugins/projectexplorer/buildmanager.cpp b/src/plugins/projectexplorer/buildmanager.cpp index c66c3d0f7e3..ecf2c054390 100644 --- a/src/plugins/projectexplorer/buildmanager.cpp +++ b/src/plugins/projectexplorer/buildmanager.cpp @@ -89,6 +89,8 @@ struct BuildManagerPrivate { QString m_currentConfiguration; // used to decide if we are building a project to decide when to emit buildStateChanged(Project *) QHash m_activeBuildSteps; + QHash m_activeBuildStepsPerTarget; + QHash m_activeBuildStepsPerProjectConfiguration; Project *m_previousBuildStepProject; // is set to true while canceling, so that nextBuildStep knows that the BuildStep finished because of canceling bool m_canceling; @@ -221,7 +223,7 @@ void BuildManager::cancel() QTimer::singleShot(0, this, SLOT(emitCancelMessage())); disconnectOutput(d->m_currentBuildStep); - decrementActiveBuildSteps(d->m_currentBuildStep->buildConfiguration()->target()->project()); + decrementActiveBuildSteps(d->m_currentBuildStep); d->m_progressFutureInterface->setProgressValueAndText(d->m_progress*100, tr("Build canceled")); //TODO NBS fix in qtconcurrent clearBuildQueue(); @@ -256,7 +258,7 @@ void BuildManager::emitCancelMessage() void BuildManager::clearBuildQueue() { foreach (BuildStep *bs, d->m_buildQueue) { - decrementActiveBuildSteps(bs->buildConfiguration()->target()->project()); + decrementActiveBuildSteps(bs); disconnectOutput(bs); } @@ -382,7 +384,7 @@ void BuildManager::nextBuildQueue() disconnectOutput(d->m_currentBuildStep); ++d->m_progress; d->m_progressFutureInterface->setProgressValueAndText(d->m_progress*100, msgProgress(d->m_progress, d->m_maxProgress)); - decrementActiveBuildSteps(d->m_currentBuildStep->buildConfiguration()->target()->project()); + decrementActiveBuildSteps(d->m_currentBuildStep); bool result = d->m_watcher.result(); if (!result) { @@ -491,7 +493,7 @@ bool BuildManager::buildQueueAppend(QList steps) for (i = 0; i < count; ++i) { ++d->m_maxProgress; d->m_buildQueue.append(steps.at(i)); - incrementActiveBuildSteps(steps.at(i)->buildConfiguration()->target()->project()); + incrementActiveBuildSteps(steps.at(i)); } return true; } @@ -531,14 +533,29 @@ void BuildManager::appendStep(BuildStep *step) startBuildQueue(); } +template +int count(const QHash &hash, T *key) +{ + typename QHash::const_iterator it = hash.find(key); + typename QHash::const_iterator end = hash.end(); + if (it != end) + return *it; + return 0; +} + bool BuildManager::isBuilding(Project *pro) { - QHash::iterator it = d->m_activeBuildSteps.find(pro); - QHash::iterator end = d->m_activeBuildSteps.end(); - if (it == end || *it == 0) - return false; - else - return true; + return count(d->m_activeBuildSteps, pro) > 0; +} + +bool BuildManager::isBuilding(Target *t) +{ + return count(d->m_activeBuildStepsPerTarget, t) > 0; +} + +bool BuildManager::isBuilding(ProjectConfiguration *p) +{ + return count(d->m_activeBuildStepsPerProjectConfiguration, p) > 0; } bool BuildManager::isBuilding(BuildStep *step) @@ -546,33 +563,51 @@ bool BuildManager::isBuilding(BuildStep *step) return (d->m_currentBuildStep == step) || d->m_buildQueue.contains(step); } -void BuildManager::incrementActiveBuildSteps(Project *pro) +template bool increment(QHash &hash, T *key) { - QHash::iterator it = d->m_activeBuildSteps.find(pro); - QHash::iterator end = d->m_activeBuildSteps.end(); + typename QHash::iterator it = hash.find(key); + typename QHash::iterator end = hash.end(); if (it == end) { - d->m_activeBuildSteps.insert(pro, 1); - emit buildStateChanged(pro); + hash.insert(key, 1); + return true; } else if (*it == 0) { ++*it; - emit buildStateChanged(pro); + return true; } else { ++*it; } + return false; } -void BuildManager::decrementActiveBuildSteps(Project *pro) +template bool decrement(QHash &hash, T *key) { - QHash::iterator it = d->m_activeBuildSteps.find(pro); - QHash::iterator end = d->m_activeBuildSteps.end(); + typename QHash::iterator it = hash.find(key); + typename QHash::iterator end = hash.end(); if (it == end) { - Q_ASSERT(false && "BuildManager d->m_activeBuildSteps says project is not building, but apparently a build step was still in the queue."); + // Can't happen } else if (*it == 1) { --*it; - emit buildStateChanged(pro); + return true; } else { --*it; } + return false; +} + +void BuildManager::incrementActiveBuildSteps(BuildStep *bs) +{ + increment(d->m_activeBuildStepsPerProjectConfiguration, bs->projectConfiguration()); + increment(d->m_activeBuildStepsPerTarget, bs->target()); + if (increment(d->m_activeBuildSteps, bs->project())) + emit buildStateChanged(bs->project()); +} + +void BuildManager::decrementActiveBuildSteps(BuildStep *bs) +{ + decrement(d->m_activeBuildStepsPerProjectConfiguration, bs->projectConfiguration()); + decrement(d->m_activeBuildStepsPerTarget, bs->target()); + if (decrement(d->m_activeBuildSteps, bs->project())) + emit buildStateChanged(bs->project()); } void BuildManager::disconnectOutput(BuildStep *bs) diff --git a/src/plugins/projectexplorer/buildmanager.h b/src/plugins/projectexplorer/buildmanager.h index 03b01f72be1..753fed0f836 100644 --- a/src/plugins/projectexplorer/buildmanager.h +++ b/src/plugins/projectexplorer/buildmanager.h @@ -63,6 +63,8 @@ public: bool buildLists(QList bsls); bool buildList(BuildStepList *bsl); bool isBuilding(Project *p); + bool isBuilding(Target *t); + bool isBuilding(ProjectConfiguration *p); bool isBuilding(BuildStep *step); // Append any build step to the list of build steps (currently only used to add the QMakeStep) @@ -102,8 +104,8 @@ private: void nextStep(); void clearBuildQueue(); bool buildQueueAppend(QList steps); - void incrementActiveBuildSteps(Project *pro); - void decrementActiveBuildSteps(Project *pro); + void incrementActiveBuildSteps(BuildStep *bs); + void decrementActiveBuildSteps(BuildStep *bs); void disconnectOutput(BuildStep *bs); BuildManagerPrivate *d; diff --git a/src/plugins/projectexplorer/buildsettingspropertiespage.cpp b/src/plugins/projectexplorer/buildsettingspropertiespage.cpp index 74db6361c54..917033174f8 100644 --- a/src/plugins/projectexplorer/buildsettingspropertiespage.cpp +++ b/src/plugins/projectexplorer/buildsettingspropertiespage.cpp @@ -42,6 +42,8 @@ #include #include #include +#include +#include #include #include @@ -370,6 +372,29 @@ void BuildSettingsWidget::deleteConfiguration(BuildConfiguration *deleteConfigur m_target->buildConfigurations().size() <= 1) return; + ProjectExplorer::BuildManager *bm = ProjectExplorerPlugin::instance()->buildManager(); + if (bm->isBuilding(deleteConfiguration)) { + QMessageBox box; + QPushButton *closeAnyway = box.addButton(tr("Cancel Build && Remove Build Configuration"), QMessageBox::AcceptRole); + QPushButton *cancelClose = box.addButton(tr("Do Not Remove"), QMessageBox::RejectRole); + box.setDefaultButton(cancelClose); + box.setWindowTitle(tr("Remove Build Configuration %1?").arg(deleteConfiguration->displayName())); + box.setText(tr("The build configuration %1 is currently being built.").arg(deleteConfiguration->displayName())); + box.setInformativeText(tr("Do you want to cancel the build process and remove the Build Configuration anyway?")); + box.exec(); + if (box.clickedButton() != closeAnyway) + return; + bm->cancel(); + } else { + QMessageBox msgBox(QMessageBox::Question, tr("Remove Build Configuration?"), + tr("Do you really want to delete build configuration %1?").arg(deleteConfiguration->displayName()), + QMessageBox::Yes|QMessageBox::No, this); + msgBox.setDefaultButton(QMessageBox::No); + msgBox.setEscapeButton(QMessageBox::No); + if (msgBox.exec() == QMessageBox::No) + return; + } + m_target->removeBuildConfiguration(deleteConfiguration); updateBuildSettings(); diff --git a/src/plugins/projectexplorer/buildstep.cpp b/src/plugins/projectexplorer/buildstep.cpp index 2a5ddaa961a..9fe11189a16 100644 --- a/src/plugins/projectexplorer/buildstep.cpp +++ b/src/plugins/projectexplorer/buildstep.cpp @@ -156,11 +156,21 @@ DeployConfiguration *BuildStep::deployConfiguration() const return dc; } +ProjectConfiguration *BuildStep::projectConfiguration() const +{ + return static_cast(parent()->parent()); +} + Target *BuildStep::target() const { return qobject_cast(parent()->parent()->parent()); } +Project *BuildStep::project() const +{ + return target()->project(); +} + bool BuildStep::immutable() const { return false; diff --git a/src/plugins/projectexplorer/buildstep.h b/src/plugins/projectexplorer/buildstep.h index a9b98a7ee26..b43ef005101 100644 --- a/src/plugins/projectexplorer/buildstep.h +++ b/src/plugins/projectexplorer/buildstep.h @@ -72,7 +72,9 @@ public: BuildConfiguration *buildConfiguration() const; DeployConfiguration *deployConfiguration() const; + ProjectConfiguration *projectConfiguration() const; Target *target() const; + Project *project() const; enum OutputFormat { NormalOutput, ErrorOutput, MessageOutput, ErrorMessageOutput }; enum OutputNewlineSetting { DoAppendNewline, DontAppendNewline }; diff --git a/src/plugins/projectexplorer/project.cpp b/src/plugins/projectexplorer/project.cpp index 76773817960..28f42d6cdc0 100644 --- a/src/plugins/projectexplorer/project.cpp +++ b/src/plugins/projectexplorer/project.cpp @@ -43,6 +43,7 @@ #include #include #include +#include #include #include @@ -166,9 +167,15 @@ void Project::addTarget(Target *t) setActiveTarget(t); } -void Project::removeTarget(Target *target) +bool Project::removeTarget(Target *target) { - QTC_ASSERT(target && d->m_targets.contains(target), return); + if (!target || !d->m_targets.contains(target)) + return false; + + ProjectExplorer::BuildManager *bm = + ProjectExplorer::ProjectExplorerPlugin::instance()->buildManager(); + if (bm->isBuilding(target)) + return false; emit aboutToRemoveTarget(target); diff --git a/src/plugins/projectexplorer/project.h b/src/plugins/projectexplorer/project.h index cf3d67f354f..b87d96f6f10 100644 --- a/src/plugins/projectexplorer/project.h +++ b/src/plugins/projectexplorer/project.h @@ -83,7 +83,7 @@ public: // Target: void addTarget(Target *target); - void removeTarget(Target *target); + bool removeTarget(Target *target); QList targets() const; // Note: activeTarget can be 0 (if no targets are defined). diff --git a/src/plugins/projectexplorer/projectexplorer.cpp b/src/plugins/projectexplorer/projectexplorer.cpp index 9a2979f2849..1773509e235 100644 --- a/src/plugins/projectexplorer/projectexplorer.cpp +++ b/src/plugins/projectexplorer/projectexplorer.cpp @@ -1031,6 +1031,20 @@ void ProjectExplorerPlugin::unloadProject() if (debug) qDebug() << "ProjectExplorerPlugin::unloadProject"; + if (buildManager()->isBuilding(d->m_currentProject)) { + QMessageBox box; + QPushButton *closeAnyway = box.addButton(tr("Cancel Build && Unload"), QMessageBox::AcceptRole); + QPushButton *cancelClose = box.addButton(tr("Do Not Unload"), QMessageBox::RejectRole); + box.setDefaultButton(cancelClose); + box.setWindowTitle(tr("Unload Project %1?").arg(d->m_currentProject->displayName())); + box.setText(tr("The project %1 is currently being built.").arg(d->m_currentProject->displayName())); + box.setInformativeText(tr("Do you want to cancel the build process and unload the project anyway?")); + box.exec(); + if (box.clickedButton() != closeAnyway) + return; + buildManager()->cancel(); + } + Core::IFile *fi = d->m_currentProject->file(); if (!fi || fi->fileName().isEmpty()) //nothing to save? diff --git a/src/plugins/projectexplorer/runsettingspropertiespage.cpp b/src/plugins/projectexplorer/runsettingspropertiespage.cpp index e1016ca4604..e0497ca72e7 100644 --- a/src/plugins/projectexplorer/runsettingspropertiespage.cpp +++ b/src/plugins/projectexplorer/runsettingspropertiespage.cpp @@ -42,6 +42,8 @@ #include #include +#include +#include #include #include @@ -389,15 +391,31 @@ void RunSettingsWidget::addDeployConfiguration() void RunSettingsWidget::removeDeployConfiguration() { DeployConfiguration *dc = m_target->activeDeployConfiguration(); - QMessageBox msgBox(QMessageBox::Question, tr("Remove Deploy Configuration?"), - tr("Do you really want to delete deploy configuration %1?").arg(dc->displayName()), - QMessageBox::Yes|QMessageBox::No, this); - msgBox.setDefaultButton(QMessageBox::No); - msgBox.setEscapeButton(QMessageBox::No); - if (msgBox.exec() == QMessageBox::No) - return; + ProjectExplorer::BuildManager *bm = ProjectExplorerPlugin::instance()->buildManager(); + if (bm->isBuilding(dc)) { + QMessageBox box; + QPushButton *closeAnyway = box.addButton(tr("Cancel Build && Remove Deploy Configuration"), QMessageBox::AcceptRole); + QPushButton *cancelClose = box.addButton(tr("Do Not Remove"), QMessageBox::RejectRole); + box.setDefaultButton(cancelClose); + box.setWindowTitle(tr("Remove Deploy Configuration %1?").arg(dc->displayName())); + box.setText(tr("The deploy configuration %1 is currently being built.").arg(dc->displayName())); + box.setInformativeText(tr("Do you want to cancel the build process and remove the Deploy Configuration anyway?")); + box.exec(); + if (box.clickedButton() != closeAnyway) + return; + bm->cancel(); + } else { + QMessageBox msgBox(QMessageBox::Question, tr("Remove Deploy Configuration?"), + tr("Do you really want to delete deploy configuration %1?").arg(dc->displayName()), + QMessageBox::Yes|QMessageBox::No, this); + msgBox.setDefaultButton(QMessageBox::No); + msgBox.setEscapeButton(QMessageBox::No); + if (msgBox.exec() == QMessageBox::No) + return; + } m_target->removeDeployConfiguration(dc); + m_removeDeployToolButton->setEnabled(m_target->deployConfigurations().size() > 1); } diff --git a/src/plugins/projectexplorer/target.cpp b/src/plugins/projectexplorer/target.cpp index ec5c57b39d8..54f194606fa 100644 --- a/src/plugins/projectexplorer/target.cpp +++ b/src/plugins/projectexplorer/target.cpp @@ -42,6 +42,8 @@ #include #include +#include +#include #include #include @@ -174,11 +176,16 @@ void Target::addBuildConfiguration(BuildConfiguration *configuration) setActiveBuildConfiguration(configuration); } -void Target::removeBuildConfiguration(BuildConfiguration *configuration) +bool Target::removeBuildConfiguration(BuildConfiguration *configuration) { //todo: this might be error prone if (!d->m_buildConfigurations.contains(configuration)) - return; + return false; + + ProjectExplorer::BuildManager *bm = + ProjectExplorer::ProjectExplorerPlugin::instance()->buildManager(); + if (bm->isBuilding(configuration)) + return false; d->m_buildConfigurations.removeOne(configuration); @@ -192,6 +199,7 @@ void Target::removeBuildConfiguration(BuildConfiguration *configuration) } delete configuration; + return true; } QList Target::buildConfigurations() const @@ -242,11 +250,16 @@ void Target::addDeployConfiguration(DeployConfiguration *dc) Q_ASSERT(activeDeployConfiguration()); } -void Target::removeDeployConfiguration(DeployConfiguration *dc) +bool Target::removeDeployConfiguration(DeployConfiguration *dc) { //todo: this might be error prone if (!d->m_deployConfigurations.contains(dc)) - return; + return false; + + ProjectExplorer::BuildManager *bm = + ProjectExplorer::ProjectExplorerPlugin::instance()->buildManager(); + if (bm->isBuilding(dc)) + return false; d->m_deployConfigurations.removeOne(dc); @@ -260,6 +273,7 @@ void Target::removeDeployConfiguration(DeployConfiguration *dc) } delete dc; + return true; } QList Target::deployConfigurations() const diff --git a/src/plugins/projectexplorer/target.h b/src/plugins/projectexplorer/target.h index cb224985b8c..03b286e1179 100644 --- a/src/plugins/projectexplorer/target.h +++ b/src/plugins/projectexplorer/target.h @@ -67,7 +67,7 @@ public: // Build configuration void addBuildConfiguration(BuildConfiguration *configuration); - void removeBuildConfiguration(BuildConfiguration *configuration); + bool removeBuildConfiguration(BuildConfiguration *configuration); QList buildConfigurations() const; virtual BuildConfiguration *activeBuildConfiguration() const; @@ -77,7 +77,7 @@ public: // DeployConfiguration void addDeployConfiguration(DeployConfiguration *dc); - void removeDeployConfiguration(DeployConfiguration *dc); + bool removeDeployConfiguration(DeployConfiguration *dc); QList deployConfigurations() const; virtual DeployConfiguration *activeDeployConfiguration() const; diff --git a/src/plugins/projectexplorer/targetsettingspanel.cpp b/src/plugins/projectexplorer/targetsettingspanel.cpp index 3a68ffa1c26..d7e2fc63e6e 100644 --- a/src/plugins/projectexplorer/targetsettingspanel.cpp +++ b/src/plugins/projectexplorer/targetsettingspanel.cpp @@ -40,6 +40,8 @@ #include "targetsettingswidget.h" #include +#include +#include #include #include @@ -47,6 +49,7 @@ #include #include #include +#include using namespace ProjectExplorer; using namespace ProjectExplorer::Internal; @@ -230,13 +233,33 @@ void TargetSettingsPanelWidget::removeTarget() { int index = m_selector->currentIndex(); Target *t = m_targets.at(index); - int ret = QMessageBox::warning(this, tr("Qt Creator"), - tr("Do you really want to remove the\n" - "\"%1\" target?").arg(t->displayName()), - QMessageBox::Yes | QMessageBox::No, - QMessageBox::No); - if (ret == QMessageBox::Yes) - m_project->removeTarget(t); + + ProjectExplorer::BuildManager *bm = ProjectExplorerPlugin::instance()->buildManager(); + if (bm->isBuilding(t)) { + QMessageBox box; + QPushButton *closeAnyway = box.addButton(tr("Cancel Build && Remove Target"), QMessageBox::AcceptRole); + QPushButton *cancelClose = box.addButton(tr("Do Not Remove"), QMessageBox::RejectRole); + box.setDefaultButton(cancelClose); + box.setWindowTitle(tr("Remove Target %1?").arg(t->displayName())); + box.setText(tr("The target %1 is currently being built.").arg(t->displayName())); + box.setInformativeText(tr("Do you want to cancel the build process and remove the Target anyway?")); + box.exec(); + if (box.clickedButton() != closeAnyway) + return; + bm->cancel(); + } else { + // We don't show the generic message box on removing the target, if we showed the still building one + int ret = QMessageBox::warning(this, tr("Qt Creator"), + tr("Do you really want to remove the\n" + "\"%1\" target?").arg(t->displayName()), + QMessageBox::Yes | QMessageBox::No, + QMessageBox::No); + if (ret != QMessageBox::Yes) + return; + } + + m_project->removeTarget(t); + } void TargetSettingsPanelWidget::targetAdded(ProjectExplorer::Target *target)