From a5952658b240e1d2761b943ed7514e4589b986fe Mon Sep 17 00:00:00 2001 From: Daniel Teske Date: Thu, 17 Sep 2015 15:24:32 +0200 Subject: [PATCH] Add an option "Stop before build" for windows users On windows, users frequently get compile errors because the application binaries/libraries are locked while the application is still running. A good solution would require actually knowing which builds output files that are used by the currently running applications, and would distinguish between locally running applications and remote applications. This solution in this patch is far simpler, it offers a option to stop all runcontrols or stop all runcontrols from the project, that the user wants to build. Those options don't take into account whether stopping those applications is actually needed and is global. Change-Id: I75b0b6c450898bb29c1e4f9c18c3199aeed696fe Task-number: QTCREATORBUG-13188 Reviewed-by: Leena Miettinen Reviewed-by: Tobias Hunger --- src/plugins/projectexplorer/appoutputpane.cpp | 7 ++ src/plugins/projectexplorer/appoutputpane.h | 2 + .../projectexplorer/projectexplorer.cpp | 49 ++++++++++ .../projectexplorer/projectexplorer.pro | 6 +- .../projectexplorer/projectexplorer.qbs | 1 + .../projectexplorer/projectexplorersettings.h | 8 +- .../projectexplorersettingspage.cpp | 2 + .../projectexplorersettingspage.ui | 45 ++++++++- .../projectexplorer/runconfiguration.cpp | 9 ++ .../projectexplorer/runconfiguration.h | 2 + .../projectexplorer/waitforstopdialog.cpp | 93 +++++++++++++++++++ .../projectexplorer/waitforstopdialog.h | 66 +++++++++++++ 12 files changed, 285 insertions(+), 5 deletions(-) create mode 100644 src/plugins/projectexplorer/waitforstopdialog.cpp create mode 100644 src/plugins/projectexplorer/waitforstopdialog.h diff --git a/src/plugins/projectexplorer/appoutputpane.cpp b/src/plugins/projectexplorer/appoutputpane.cpp index 7ce0a65c10d..a79c9b19ae4 100644 --- a/src/plugins/projectexplorer/appoutputpane.cpp +++ b/src/plugins/projectexplorer/appoutputpane.cpp @@ -535,6 +535,13 @@ bool AppOutputPane::closeTabs(CloseTabMode mode) return allClosed; } +QList AppOutputPane::allRunControls() const +{ + return Utils::transform(m_runControlTabs,[](const RunControlTab &tab) { + return tab.runControl; + }); +} + bool AppOutputPane::closeTab(int index) { return closeTab(index, CloseTabWithPrompt); diff --git a/src/plugins/projectexplorer/appoutputpane.h b/src/plugins/projectexplorer/appoutputpane.h index 824edf6d0aa..101bd9c7b55 100644 --- a/src/plugins/projectexplorer/appoutputpane.h +++ b/src/plugins/projectexplorer/appoutputpane.h @@ -92,6 +92,8 @@ public: bool aboutToClose() const; bool closeTabs(CloseTabMode mode); + QList allRunControls() const; + signals: void allRunControlsFinished(); void runControlStarted(ProjectExplorer::RunControl *rc); diff --git a/src/plugins/projectexplorer/projectexplorer.cpp b/src/plugins/projectexplorer/projectexplorer.cpp index a02a5bcf95b..266d00e4ad0 100644 --- a/src/plugins/projectexplorer/projectexplorer.cpp +++ b/src/plugins/projectexplorer/projectexplorer.cpp @@ -92,6 +92,7 @@ #include "devicesupport/devicesettingspage.h" #include "targetsettingspanel.h" #include "projectpanelfactory.h" +#include "waitforstopdialog.h" #ifdef Q_OS_WIN # include "windebuginterface.h" @@ -1160,6 +1161,13 @@ bool ProjectExplorerPlugin::initialize(const QStringList &arguments, QString *er QUuid(s->value(QLatin1String("ProjectExplorer/Settings/EnvironmentId")).toByteArray()); if (dd->m_projectExplorerSettings.environmentId.isNull()) dd->m_projectExplorerSettings.environmentId = QUuid::createUuid(); + int tmp = s->value(QLatin1String("ProjectExplorer/Settings/StopBeforeBuild"), + Utils::HostOsInfo::isWindowsHost() ? 1 : 0).toInt(); + dd->m_projectExplorerSettings.stopBeforeBuild = ProjectExplorerSettings::StopBeforeBuild(tmp); + if (tmp < 0 || tmp > ProjectExplorerSettings::StopAll) + tmp = Utils::HostOsInfo::isWindowsHost() ? 1 : 0; + + dd->m_projectExplorerSettings.stopBeforeBuild = ProjectExplorerSettings::StopBeforeBuild(tmp); connect(dd->m_sessionManagerAction, &QAction::triggered, dd, &ProjectExplorerPluginPrivate::showSessionManager); @@ -1646,6 +1654,7 @@ void ProjectExplorerPluginPrivate::savePersistentSettings() s->setValue(QLatin1String("ProjectExplorer/Settings/PromptToStopRunControl"), dd->m_projectExplorerSettings.prompToStopRunControl); s->setValue(QLatin1String("ProjectExplorer/Settings/MaxAppOutputLines"), dd->m_projectExplorerSettings.maxAppOutputLines); s->setValue(QLatin1String("ProjectExplorer/Settings/EnvironmentId"), dd->m_projectExplorerSettings.environmentId.toByteArray()); + s->setValue(QLatin1String("ProjectExplorer/Settings/StopBeforeBuild"), dd->m_projectExplorerSettings.stopBeforeBuild); } void ProjectExplorerPlugin::openProjectWelcomePage(const QString &fileName) @@ -2293,6 +2302,46 @@ int ProjectExplorerPluginPrivate::queue(QList projects, QList ste if (!m_instance->saveModifiedFiles()) return -1; + if (m_projectExplorerSettings.stopBeforeBuild != ProjectExplorerSettings::StopNone) { + QList toStop; + foreach (RunControl *rc, m_outputPane->allRunControls()) { + if (rc->isRunning() + && (m_projectExplorerSettings.stopBeforeBuild == ProjectExplorerSettings::StopAll + || projects.contains(rc->project()))) + toStop << rc; + } + + if (!toStop.isEmpty()) { + bool stopThem = true; + if (m_projectExplorerSettings.prompToStopRunControl) { + QStringList names = Utils::transform(toStop, &RunControl::displayName); + if (QMessageBox::question(ICore::mainWindow(), tr("Stop Applications"), + tr("Stop these applications before building?") + + QLatin1String("\n\n") + + names.join(QLatin1Char('\n'))) + == QMessageBox::No) { + stopThem = false; + } + } + + QList asyncStop; + if (stopThem) { + foreach (RunControl *rc, toStop) { + if (rc->stop() == RunControl::AsynchronousStop) + asyncStop << rc; + } + } + + if (!asyncStop.isEmpty()) { + WaitForStopDialog dialog(asyncStop); + dialog.exec(); + + if (dialog.canceled()) + return -1; + } + } + } + QList stepLists; QStringList names; QStringList preambleMessage; diff --git a/src/plugins/projectexplorer/projectexplorer.pro b/src/plugins/projectexplorer/projectexplorer.pro index d80732f7d5d..9ce399bbb7f 100644 --- a/src/plugins/projectexplorer/projectexplorer.pro +++ b/src/plugins/projectexplorer/projectexplorer.pro @@ -153,7 +153,8 @@ HEADERS += projectexplorer.h \ projectwelcomepage.h \ projectpanelfactory.h \ projecttree.h \ - expanddata.h + expanddata.h \ + waitforstopdialog.h SOURCES += projectexplorer.cpp \ abi.cpp \ @@ -293,7 +294,8 @@ SOURCES += projectexplorer.cpp \ projectwelcomepage.cpp \ projectpanelfactory.cpp \ projecttree.cpp \ - expanddata.cpp + expanddata.cpp \ + waitforstopdialog.cpp FORMS += processstep.ui \ editorsettingspropertiespage.ui \ diff --git a/src/plugins/projectexplorer/projectexplorer.qbs b/src/plugins/projectexplorer/projectexplorer.qbs index 21f53556f0d..4cba524ee7b 100644 --- a/src/plugins/projectexplorer/projectexplorer.qbs +++ b/src/plugins/projectexplorer/projectexplorer.qbs @@ -153,6 +153,7 @@ QtcPlugin { "toolchainoptionspage.cpp", "toolchainoptionspage.h", "unconfiguredprojectpanel.cpp", "unconfiguredprojectpanel.h", "vcsannotatetaskhandler.cpp", "vcsannotatetaskhandler.h", + "waitforstopdialog.cpp", "waitforstopdialog.h", "xcodebuildparser.cpp", "xcodebuildparser.h" ] } diff --git a/src/plugins/projectexplorer/projectexplorersettings.h b/src/plugins/projectexplorer/projectexplorersettings.h index 7caa541bc22..ee27265b32d 100644 --- a/src/plugins/projectexplorer/projectexplorersettings.h +++ b/src/plugins/projectexplorer/projectexplorersettings.h @@ -39,6 +39,8 @@ namespace Internal { class ProjectExplorerSettings { public: + enum StopBeforeBuild { StopNone = 0, StopSameProject = 1, StopAll = 2 }; + ProjectExplorerSettings() : buildBeforeDeploy(true), deployBeforeRun(true), saveBeforeBuild(false), showCompilerOutput(false), @@ -46,7 +48,7 @@ public: cleanOldAppOutput(false), mergeStdErrAndStdOut(false), wrapAppOutput(true), useJom(true), autorestoreLastSession(false), prompToStopRunControl(false), - maxAppOutputLines(100000) + maxAppOutputLines(100000), stopBeforeBuild(StopBeforeBuild::StopNone) { } bool buildBeforeDeploy; @@ -62,6 +64,7 @@ public: bool autorestoreLastSession; // This option is set in the Session Manager! bool prompToStopRunControl; int maxAppOutputLines; + StopBeforeBuild stopBeforeBuild; // Add a UUid which is used to identify the development environment. // This is used to warn the user when he is trying to open a .user file that was created @@ -84,7 +87,8 @@ inline bool operator==(const ProjectExplorerSettings &p1, const ProjectExplorerS && p1.autorestoreLastSession == p2.autorestoreLastSession && p1.prompToStopRunControl == p2.prompToStopRunControl && p1.maxAppOutputLines == p2.maxAppOutputLines - && p1.environmentId == p2.environmentId; + && p1.environmentId == p2.environmentId + && p1.stopBeforeBuild == p2.stopBeforeBuild; } } // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/projectexplorersettingspage.cpp b/src/plugins/projectexplorer/projectexplorersettingspage.cpp index e652f01c7f0..3b56da928ed 100644 --- a/src/plugins/projectexplorer/projectexplorersettingspage.cpp +++ b/src/plugins/projectexplorer/projectexplorersettingspage.cpp @@ -83,6 +83,7 @@ ProjectExplorerSettings ProjectExplorerSettingsWidget::settings() const pes.prompToStopRunControl = m_ui.promptToStopRunControlCheckBox->isChecked(); pes.maxAppOutputLines = m_ui.maxAppOutputBox->value(); pes.environmentId = m_environmentId; + pes.stopBeforeBuild = ProjectExplorerSettings::StopBeforeBuild(m_ui.stopBeforeBuildComboBox->currentIndex()); return pes; } @@ -101,6 +102,7 @@ void ProjectExplorerSettingsWidget::setSettings(const ProjectExplorerSettings & m_ui.promptToStopRunControlCheckBox->setChecked(pes.prompToStopRunControl); m_ui.maxAppOutputBox->setValue(pes.maxAppOutputLines); m_environmentId = pes.environmentId; + m_ui.stopBeforeBuildComboBox->setCurrentIndex(pes.stopBeforeBuild); } QString ProjectExplorerSettingsWidget::projectsDirectory() const diff --git a/src/plugins/projectexplorer/projectexplorersettingspage.ui b/src/plugins/projectexplorer/projectexplorersettingspage.ui index 0c3d4259d39..91908fa4cb1 100644 --- a/src/plugins/projectexplorer/projectexplorersettingspage.ui +++ b/src/plugins/projectexplorer/projectexplorersettingspage.ui @@ -183,6 +183,49 @@ + + + + + Stop applications before building: + + + + + + + + None + + + + + Same Project + + + + + All + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + 0 @@ -215,7 +258,7 @@ - + 12 diff --git a/src/plugins/projectexplorer/runconfiguration.cpp b/src/plugins/projectexplorer/runconfiguration.cpp index 8c3fdf8254c..abb506f70d3 100644 --- a/src/plugins/projectexplorer/runconfiguration.cpp +++ b/src/plugins/projectexplorer/runconfiguration.cpp @@ -521,7 +521,11 @@ RunControl::RunControl(RunConfiguration *runConfiguration, Core::Id mode) if (runConfiguration) { m_displayName = runConfiguration->displayName(); m_outputFormatter = runConfiguration->createOutputFormatter(); + + if (runConfiguration->target()) + m_project = m_runConfiguration->target()->project(); } + // We need to ensure that there's always a OutputFormatter if (!m_outputFormatter) m_outputFormatter = new Utils::OutputFormatter(); @@ -559,6 +563,11 @@ RunConfiguration *RunControl::runConfiguration() const return m_runConfiguration.data(); } +Project *RunControl::project() const +{ + return m_project.data(); +} + ProcessHandle RunControl::applicationProcessHandle() const { return m_applicationProcessHandle; diff --git a/src/plugins/projectexplorer/runconfiguration.h b/src/plugins/projectexplorer/runconfiguration.h index ff511a84a79..3e5f18a0ee9 100644 --- a/src/plugins/projectexplorer/runconfiguration.h +++ b/src/plugins/projectexplorer/runconfiguration.h @@ -301,6 +301,7 @@ public: Abi abi() const; RunConfiguration *runConfiguration() const; + Project *project() const; bool sameRunConfiguration(const RunControl *other) const; Utils::OutputFormatter *outputFormatter(); @@ -331,6 +332,7 @@ private: Core::Id m_runMode; QString m_icon; const QPointer m_runConfiguration; + QPointer m_project; Utils::OutputFormatter *m_outputFormatter; // A handle to the actual application process. diff --git a/src/plugins/projectexplorer/waitforstopdialog.cpp b/src/plugins/projectexplorer/waitforstopdialog.cpp new file mode 100644 index 00000000000..8f6529c6363 --- /dev/null +++ b/src/plugins/projectexplorer/waitforstopdialog.cpp @@ -0,0 +1,93 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://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 http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#include "waitforstopdialog.h" + +#include + +#include +#include +#include +#include + +using namespace ProjectExplorer; +using namespace ProjectExplorer::Internal; + +WaitForStopDialog::WaitForStopDialog(QList runControls) + : m_runControls(runControls) +{ + setWindowTitle(tr("Waiting for Applications to Stop")); + + QVBoxLayout *layout = new QVBoxLayout(); + setLayout(layout); + + m_progressLabel = new QLabel; + layout->addWidget(m_progressLabel); + + QPushButton *cancelButton = new QPushButton(tr("Cancel")); + connect(cancelButton, &QPushButton::clicked, + this, &QDialog::close); + layout->addWidget(cancelButton); + + updateProgressText(); + + foreach (RunControl *rc, runControls) + connect(rc, &RunControl::finished, this, &WaitForStopDialog::runControlFinished); + + m_timer.start(); +} + +bool WaitForStopDialog::canceled() +{ + return !m_runControls.isEmpty(); +} + +void WaitForStopDialog::updateProgressText() +{ + QString text = tr("Waiting for applications to stop.") + QLatin1String("\n\n"); + QStringList names = Utils::transform(m_runControls, &RunControl::displayName); + text += names.join(QLatin1Char('\n')); + m_progressLabel->setText(text); +} + +void WaitForStopDialog::runControlFinished() +{ + RunControl *rc = qobject_cast(sender()); + m_runControls.removeOne(rc); + + if (m_runControls.isEmpty()) { + if (m_timer.elapsed() < 1000) + QTimer::singleShot(1000 - m_timer.elapsed(), this, &QDialog::close); + else + QDialog::close(); + } else { + updateProgressText(); + } +} diff --git a/src/plugins/projectexplorer/waitforstopdialog.h b/src/plugins/projectexplorer/waitforstopdialog.h new file mode 100644 index 00000000000..42530caccc7 --- /dev/null +++ b/src/plugins/projectexplorer/waitforstopdialog.h @@ -0,0 +1,66 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://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 http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#ifndef WAITFORSTOPDIALOG_H +#define WAITFORSTOPDIALOG_H + +#include +#include +#include + +#include "runconfiguration.h" + +QT_BEGIN_NAMESPACE +class QLabel; +QT_END_NAMESPACE + +namespace ProjectExplorer { +namespace Internal { + +class WaitForStopDialog : public QDialog +{ +public: + WaitForStopDialog(QList runControls); + + bool canceled(); +private: + void updateProgressText(); + void runControlFinished(); + + QList m_runControls; + QLabel *m_progressLabel; + QTime m_timer; +}; + +} // namespace Internal +} // namespace ProjectExplorer + +#endif // WAITFORSTOPDIALOG_H +