forked from qt-creator/qt-creator
ProjectExplorer: Prevent target switch on project that's to be removed
Switching targets starts up a lot of machinery that is undesired for a project that's going away. The same goes for switching build configurations on a target that is being removed. Fixes: QTCREATORBUG-25655 Change-Id: I0cb6e395cca8f89bfeb70fcdf571bbcb64f94247 Reviewed-by: hjk <hjk@qt.io> Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
This commit is contained in:
@@ -39,6 +39,7 @@
|
|||||||
#include <extensionsystem/pluginmanager.h>
|
#include <extensionsystem/pluginmanager.h>
|
||||||
|
|
||||||
#include <projectexplorer/abi.h>
|
#include <projectexplorer/abi.h>
|
||||||
|
#include <projectexplorer/buildinfo.h>
|
||||||
#include <projectexplorer/buildsteplist.h>
|
#include <projectexplorer/buildsteplist.h>
|
||||||
#include <projectexplorer/buildsystem.h>
|
#include <projectexplorer/buildsystem.h>
|
||||||
#include <projectexplorer/customexecutablerunconfiguration.h>
|
#include <projectexplorer/customexecutablerunconfiguration.h>
|
||||||
@@ -647,6 +648,25 @@ ProjectExplorer::DeploymentKnowledge GenericProject::deploymentKnowledge() const
|
|||||||
return DeploymentKnowledge::Approximative;
|
return DeploymentKnowledge::Approximative;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GenericProject::configureAsExampleProject(ProjectExplorer::Kit *kit)
|
||||||
|
{
|
||||||
|
QList<BuildInfo> infoList;
|
||||||
|
const QList<Kit *> kits(kit != nullptr ? QList<Kit *>({kit}) : KitManager::kits());
|
||||||
|
for (Kit *k : kits) {
|
||||||
|
if (auto factory = BuildConfigurationFactory::find(k, projectFilePath())) {
|
||||||
|
for (int i = 0; i < 5; ++i) {
|
||||||
|
BuildInfo buildInfo;
|
||||||
|
buildInfo.displayName = tr("Build %1").arg(i + 1);
|
||||||
|
buildInfo.factory = factory;
|
||||||
|
buildInfo.kitId = kit->id();
|
||||||
|
buildInfo.buildDirectory = projectFilePath();
|
||||||
|
infoList << buildInfo;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setup(infoList);
|
||||||
|
}
|
||||||
|
|
||||||
bool GenericProjectFile::reload(QString *errorString, IDocument::ReloadFlag flag, IDocument::ChangeType type)
|
bool GenericProjectFile::reload(QString *errorString, IDocument::ReloadFlag flag, IDocument::ChangeType type)
|
||||||
{
|
{
|
||||||
Q_UNUSED(errorString)
|
Q_UNUSED(errorString)
|
||||||
|
@@ -43,6 +43,7 @@ public:
|
|||||||
private:
|
private:
|
||||||
RestoreResult fromMap(const QVariantMap &map, QString *errorMessage) final;
|
RestoreResult fromMap(const QVariantMap &map, QString *errorMessage) final;
|
||||||
ProjectExplorer::DeploymentKnowledge deploymentKnowledge() const final;
|
ProjectExplorer::DeploymentKnowledge deploymentKnowledge() const final;
|
||||||
|
void configureAsExampleProject(ProjectExplorer::Kit *kit) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Internal
|
} // namespace Internal
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
add_qtc_plugin(ProjectExplorer
|
add_qtc_plugin(ProjectExplorer
|
||||||
DEPENDS QtcSsh Qt5::Qml
|
DEPENDS QtcSsh Qt5::Qml
|
||||||
PLUGIN_DEPENDS Core TextEditor
|
PLUGIN_DEPENDS Core TextEditor
|
||||||
|
PLUGIN_TEST_DEPENDS GenericProjectManager
|
||||||
SOURCES
|
SOURCES
|
||||||
abi.cpp abi.h
|
abi.cpp abi.h
|
||||||
abiwidget.cpp abiwidget.h
|
abiwidget.cpp abiwidget.h
|
||||||
@@ -214,6 +215,15 @@ extend_qtc_plugin(ProjectExplorer
|
|||||||
jsonwizard/jsonwizard_test.cpp
|
jsonwizard/jsonwizard_test.cpp
|
||||||
outputparser_test.cpp outputparser_test.h
|
outputparser_test.cpp outputparser_test.h
|
||||||
)
|
)
|
||||||
|
|
||||||
|
file(GLOB_RECURSE test_resources RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} testdata/*)
|
||||||
|
qt_add_resources(ProjectExplorer "testdata"
|
||||||
|
CONDITION WITH_TESTS
|
||||||
|
PREFIX "/projectexplorer"
|
||||||
|
BASE "."
|
||||||
|
FILES ${test_resources}
|
||||||
|
)
|
||||||
|
|
||||||
qtc_plugin_enabled(_projectexplorer_enabled ProjectExplorer)
|
qtc_plugin_enabled(_projectexplorer_enabled ProjectExplorer)
|
||||||
if (WITH_TESTS AND _projectexplorer_enabled)
|
if (WITH_TESTS AND _projectexplorer_enabled)
|
||||||
set_source_files_properties(jsonwizard/jsonwizard_test.cpp
|
set_source_files_properties(jsonwizard/jsonwizard_test.cpp
|
||||||
|
@@ -187,6 +187,7 @@ public:
|
|||||||
bool m_hasMakeInstallEquivalent = false;
|
bool m_hasMakeInstallEquivalent = false;
|
||||||
bool m_needsBuildConfigurations = true;
|
bool m_needsBuildConfigurations = true;
|
||||||
bool m_needsDeployConfigurations = true;
|
bool m_needsDeployConfigurations = true;
|
||||||
|
bool m_shuttingDown = false;
|
||||||
|
|
||||||
std::function<BuildSystem *(Target *)> m_buildSystemCreator;
|
std::function<BuildSystem *(Target *)> m_buildSystemCreator;
|
||||||
|
|
||||||
@@ -246,6 +247,16 @@ Utils::Id Project::id() const
|
|||||||
return d->m_id;
|
return d->m_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Project::markAsShuttingDown()
|
||||||
|
{
|
||||||
|
d->m_shuttingDown = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Project::isShuttingDown() const
|
||||||
|
{
|
||||||
|
return d->m_shuttingDown;
|
||||||
|
}
|
||||||
|
|
||||||
QString Project::mimeType() const
|
QString Project::mimeType() const
|
||||||
{
|
{
|
||||||
return d->m_document->mimeType();
|
return d->m_document->mimeType();
|
||||||
@@ -311,6 +322,7 @@ bool Project::removeTarget(Target *target)
|
|||||||
if (BuildManager::isBuilding(target))
|
if (BuildManager::isBuilding(target))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
target->markAsShuttingDown();
|
||||||
emit aboutToRemoveTarget(target);
|
emit aboutToRemoveTarget(target);
|
||||||
auto keep = Utils::take(d->m_targets, target);
|
auto keep = Utils::take(d->m_targets, target);
|
||||||
if (target == d->m_activeTarget) {
|
if (target == d->m_activeTarget) {
|
||||||
@@ -1065,10 +1077,14 @@ QStringList Project::availableQmlPreviewTranslations(QString *errorMessage)
|
|||||||
|
|
||||||
} // namespace ProjectExplorer
|
} // namespace ProjectExplorer
|
||||||
|
|
||||||
|
#include <coreplugin/editormanager/editormanager.h>
|
||||||
#include <utils/hostosinfo.h>
|
#include <utils/hostosinfo.h>
|
||||||
|
#include <utils/temporarydirectory.h>
|
||||||
|
|
||||||
#include <QTest>
|
#include <QEventLoop>
|
||||||
#include <QSignalSpy>
|
#include <QSignalSpy>
|
||||||
|
#include <QTest>
|
||||||
|
#include <QTimer>
|
||||||
|
|
||||||
namespace ProjectExplorer {
|
namespace ProjectExplorer {
|
||||||
|
|
||||||
@@ -1265,6 +1281,60 @@ void ProjectExplorerPlugin::testProject_projectTree()
|
|||||||
QVERIFY(!project.rootProjectNode());
|
QVERIFY(!project.rootProjectNode());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ProjectExplorerPlugin::testProject_multipleBuildConfigs()
|
||||||
|
{
|
||||||
|
// Find suitable kit.
|
||||||
|
Kit * const kit = Utils::findOr(KitManager::kits(), nullptr, [](const Kit *k) {
|
||||||
|
return k->isValid();
|
||||||
|
});
|
||||||
|
if (!kit)
|
||||||
|
QSKIP("The test requires at least one valid kit.");
|
||||||
|
|
||||||
|
// Copy project from qrc file and set it up.
|
||||||
|
using namespace Utils;
|
||||||
|
QTemporaryDir * const tempDir = TemporaryDirectory::masterTemporaryDirectory();
|
||||||
|
QVERIFY(tempDir->isValid());
|
||||||
|
QString error;
|
||||||
|
const FilePath projectDir = FilePath::fromString(tempDir->path() + "/generic-project");
|
||||||
|
FileUtils::copyRecursively(FilePath::fromString(":/projectexplorer/testdata/generic-project"),
|
||||||
|
projectDir, &error);
|
||||||
|
QVERIFY2(error.isEmpty(), qPrintable(error));
|
||||||
|
const QFileInfoList files = QDir(projectDir.toString()).entryInfoList(QDir::Files | QDir::Dirs);
|
||||||
|
for (const QFileInfo &f : files)
|
||||||
|
QFile(f.absoluteFilePath()).setPermissions(f.permissions() | QFile::WriteUser);
|
||||||
|
const auto theProject = openProject(projectDir.pathAppended("generic-project.creator")
|
||||||
|
.toString());
|
||||||
|
QVERIFY2(theProject, qPrintable(theProject.errorMessage()));
|
||||||
|
theProject.project()->configureAsExampleProject(kit);
|
||||||
|
QCOMPARE(theProject.project()->targets().size(), 1);
|
||||||
|
Target * const target = theProject.project()->activeTarget();
|
||||||
|
QVERIFY(target);
|
||||||
|
QCOMPARE(target->buildConfigurations().size(), 6);
|
||||||
|
SessionManager::setActiveBuildConfiguration(target, target->buildConfigurations().at(1),
|
||||||
|
SetActive::Cascade);
|
||||||
|
BuildSystem * const bs = theProject.project()->activeTarget()->buildSystem();
|
||||||
|
QVERIFY(bs);
|
||||||
|
QCOMPARE(bs, target->activeBuildConfiguration()->buildSystem());
|
||||||
|
if (bs->isWaitingForParse() || bs->isParsing()) {
|
||||||
|
QEventLoop loop;
|
||||||
|
QTimer t;
|
||||||
|
t.setSingleShot(true);
|
||||||
|
connect(&t, &QTimer::timeout, &loop, &QEventLoop::quit);
|
||||||
|
connect(bs, &BuildSystem::parsingFinished, &loop, &QEventLoop::quit);
|
||||||
|
t.start(10000);
|
||||||
|
QVERIFY(loop.exec());
|
||||||
|
QVERIFY(t.isActive());
|
||||||
|
}
|
||||||
|
QVERIFY(!bs->isWaitingForParse() && !bs->isParsing());
|
||||||
|
|
||||||
|
QCOMPARE(SessionManager::startupProject(), theProject.project());
|
||||||
|
QCOMPARE(ProjectTree::currentProject(), theProject.project());
|
||||||
|
QVERIFY(Core::EditorManager::openEditor(projectDir.pathAppended("main.cpp").toString()));
|
||||||
|
QVERIFY(ProjectTree::currentNode());
|
||||||
|
ProjectTree::instance()->expandAll();
|
||||||
|
SessionManager::closeAllProjects(); // QTCREATORBUG-25655
|
||||||
|
}
|
||||||
|
|
||||||
#endif // WITH_TESTS
|
#endif // WITH_TESTS
|
||||||
|
|
||||||
} // namespace ProjectExplorer
|
} // namespace ProjectExplorer
|
||||||
|
@@ -80,6 +80,9 @@ public:
|
|||||||
QString displayName() const;
|
QString displayName() const;
|
||||||
Utils::Id id() const;
|
Utils::Id id() const;
|
||||||
|
|
||||||
|
void markAsShuttingDown();
|
||||||
|
bool isShuttingDown() const;
|
||||||
|
|
||||||
QString mimeType() const;
|
QString mimeType() const;
|
||||||
bool canBuildProducts() const;
|
bool canBuildProducts() const;
|
||||||
|
|
||||||
|
@@ -273,6 +273,7 @@ private slots:
|
|||||||
void testProject_parsingSuccess();
|
void testProject_parsingSuccess();
|
||||||
void testProject_parsingFail();
|
void testProject_parsingFail();
|
||||||
void testProject_projectTree();
|
void testProject_projectTree();
|
||||||
|
void testProject_multipleBuildConfigs();
|
||||||
|
|
||||||
void testSessionSwitch();
|
void testSessionSwitch();
|
||||||
#endif // WITH_TESTS
|
#endif // WITH_TESTS
|
||||||
|
@@ -342,6 +342,10 @@ equals(TEST, 1) {
|
|||||||
outputparser_test.cpp
|
outputparser_test.cpp
|
||||||
HEADERS += \
|
HEADERS += \
|
||||||
outputparser_test.h
|
outputparser_test.h
|
||||||
|
test_files.files = $$files(testdata/*, true)
|
||||||
|
test_files.base = $$PWD
|
||||||
|
test_files.prefix = /projectexplorer
|
||||||
|
RESOURCES += test_files
|
||||||
}
|
}
|
||||||
|
|
||||||
journald {
|
journald {
|
||||||
|
@@ -16,6 +16,8 @@ Project {
|
|||||||
Depends { name: "libclang"; required: false }
|
Depends { name: "libclang"; required: false }
|
||||||
Depends { name: "clang_defines" }
|
Depends { name: "clang_defines" }
|
||||||
|
|
||||||
|
pluginTestDepends: ["GenericProjectManager"]
|
||||||
|
|
||||||
Group {
|
Group {
|
||||||
name: "General"
|
name: "General"
|
||||||
files: [
|
files: [
|
||||||
@@ -252,6 +254,15 @@ Project {
|
|||||||
files: ["outputparser_test.h", "outputparser_test.cpp"]
|
files: ["outputparser_test.h", "outputparser_test.cpp"]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Group {
|
||||||
|
name: "Test resources"
|
||||||
|
condition: qtc.testsEnabled
|
||||||
|
files: ["testdata/**"]
|
||||||
|
fileTags: ["qt.core.resource_data"]
|
||||||
|
Qt.core.resourcePrefix: "/projectexplorer"
|
||||||
|
Qt.core.resourceSourceBase: path
|
||||||
|
}
|
||||||
|
|
||||||
Export {
|
Export {
|
||||||
Depends { name: "Qt.network" }
|
Depends { name: "Qt.network" }
|
||||||
}
|
}
|
||||||
|
@@ -8,3 +8,5 @@ QTC_PLUGIN_DEPENDS += \
|
|||||||
coreplugin \
|
coreplugin \
|
||||||
texteditor
|
texteditor
|
||||||
QT *= network
|
QT *= network
|
||||||
|
QTC_TEST_DEPENDS += \
|
||||||
|
genericprojectmanager
|
||||||
|
@@ -286,6 +286,9 @@ void SessionManager::setActiveTarget(Project *project, Target *target, SetActive
|
|||||||
{
|
{
|
||||||
QTC_ASSERT(project, return);
|
QTC_ASSERT(project, return);
|
||||||
|
|
||||||
|
if (project->isShuttingDown())
|
||||||
|
return;
|
||||||
|
|
||||||
project->setActiveTarget(target);
|
project->setActiveTarget(target);
|
||||||
|
|
||||||
if (!target) // never cascade setting no target
|
if (!target) // never cascade setting no target
|
||||||
@@ -307,6 +310,11 @@ void SessionManager::setActiveTarget(Project *project, Target *target, SetActive
|
|||||||
void SessionManager::setActiveBuildConfiguration(Target *target, BuildConfiguration *bc, SetActive cascade)
|
void SessionManager::setActiveBuildConfiguration(Target *target, BuildConfiguration *bc, SetActive cascade)
|
||||||
{
|
{
|
||||||
QTC_ASSERT(target, return);
|
QTC_ASSERT(target, return);
|
||||||
|
QTC_ASSERT(target->project(), return);
|
||||||
|
|
||||||
|
if (target->project()->isShuttingDown() || target->isShuttingDown())
|
||||||
|
return;
|
||||||
|
|
||||||
target->setActiveBuildConfiguration(bc);
|
target->setActiveBuildConfiguration(bc);
|
||||||
|
|
||||||
if (!bc)
|
if (!bc)
|
||||||
@@ -335,6 +343,11 @@ void SessionManager::setActiveBuildConfiguration(Target *target, BuildConfigurat
|
|||||||
void SessionManager::setActiveDeployConfiguration(Target *target, DeployConfiguration *dc, SetActive cascade)
|
void SessionManager::setActiveDeployConfiguration(Target *target, DeployConfiguration *dc, SetActive cascade)
|
||||||
{
|
{
|
||||||
QTC_ASSERT(target, return);
|
QTC_ASSERT(target, return);
|
||||||
|
QTC_ASSERT(target->project(), return);
|
||||||
|
|
||||||
|
if (target->project()->isShuttingDown() || target->isShuttingDown())
|
||||||
|
return;
|
||||||
|
|
||||||
target->setActiveDeployConfiguration(dc);
|
target->setActiveDeployConfiguration(dc);
|
||||||
|
|
||||||
if (!dc)
|
if (!dc)
|
||||||
@@ -724,6 +737,7 @@ void SessionManager::removeProjects(const QList<Project *> &remove)
|
|||||||
// Delete projects
|
// Delete projects
|
||||||
for (Project *pro : remove) {
|
for (Project *pro : remove) {
|
||||||
pro->saveSettings();
|
pro->saveSettings();
|
||||||
|
pro->markAsShuttingDown();
|
||||||
|
|
||||||
// Remove the project node:
|
// Remove the project node:
|
||||||
d->m_projects.removeOne(pro);
|
d->m_projects.removeOne(pro);
|
||||||
|
@@ -122,6 +122,8 @@ public:
|
|||||||
ProjectConfigurationModel m_buildConfigurationModel;
|
ProjectConfigurationModel m_buildConfigurationModel;
|
||||||
ProjectConfigurationModel m_deployConfigurationModel;
|
ProjectConfigurationModel m_deployConfigurationModel;
|
||||||
ProjectConfigurationModel m_runConfigurationModel;
|
ProjectConfigurationModel m_runConfigurationModel;
|
||||||
|
|
||||||
|
bool m_shuttingDown = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@@ -232,6 +234,16 @@ bool Target::isActive() const
|
|||||||
return project()->activeTarget() == this;
|
return project()->activeTarget() == this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Target::markAsShuttingDown()
|
||||||
|
{
|
||||||
|
d->m_shuttingDown = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Target::isShuttingDown() const
|
||||||
|
{
|
||||||
|
return d->m_shuttingDown;
|
||||||
|
}
|
||||||
|
|
||||||
Project *Target::project() const
|
Project *Target::project() const
|
||||||
{
|
{
|
||||||
return static_cast<Project *>(parent());
|
return static_cast<Project *>(parent());
|
||||||
@@ -511,6 +523,9 @@ RunConfiguration *Target::activeRunConfiguration() const
|
|||||||
|
|
||||||
void Target::setActiveRunConfiguration(RunConfiguration *rc)
|
void Target::setActiveRunConfiguration(RunConfiguration *rc)
|
||||||
{
|
{
|
||||||
|
if (isShuttingDown())
|
||||||
|
return;
|
||||||
|
|
||||||
if ((!rc && d->m_runConfigurations.isEmpty()) ||
|
if ((!rc && d->m_runConfigurations.isEmpty()) ||
|
||||||
(rc && d->m_runConfigurations.contains(rc) &&
|
(rc && d->m_runConfigurations.contains(rc) &&
|
||||||
rc != d->m_activeRunConfiguration)) {
|
rc != d->m_activeRunConfiguration)) {
|
||||||
|
@@ -63,6 +63,9 @@ public:
|
|||||||
|
|
||||||
bool isActive() const;
|
bool isActive() const;
|
||||||
|
|
||||||
|
void markAsShuttingDown();
|
||||||
|
bool isShuttingDown() const;
|
||||||
|
|
||||||
Project *project() const;
|
Project *project() const;
|
||||||
Kit *kit() const;
|
Kit *kit() const;
|
||||||
BuildSystem *buildSystem() const;
|
BuildSystem *buildSystem() const;
|
||||||
|
0
src/plugins/projectexplorer/testdata/generic-project/generic-project.cflags
vendored
Normal file
0
src/plugins/projectexplorer/testdata/generic-project/generic-project.cflags
vendored
Normal file
0
src/plugins/projectexplorer/testdata/generic-project/generic-project.config
vendored
Normal file
0
src/plugins/projectexplorer/testdata/generic-project/generic-project.config
vendored
Normal file
1
src/plugins/projectexplorer/testdata/generic-project/generic-project.creator
vendored
Normal file
1
src/plugins/projectexplorer/testdata/generic-project/generic-project.creator
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
[General]
|
0
src/plugins/projectexplorer/testdata/generic-project/generic-project.cxxflags
vendored
Normal file
0
src/plugins/projectexplorer/testdata/generic-project/generic-project.cxxflags
vendored
Normal file
1
src/plugins/projectexplorer/testdata/generic-project/generic-project.files
vendored
Normal file
1
src/plugins/projectexplorer/testdata/generic-project/generic-project.files
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
main.cpp
|
0
src/plugins/projectexplorer/testdata/generic-project/generic-project.includes
vendored
Normal file
0
src/plugins/projectexplorer/testdata/generic-project/generic-project.includes
vendored
Normal file
1
src/plugins/projectexplorer/testdata/generic-project/main.cpp
vendored
Normal file
1
src/plugins/projectexplorer/testdata/generic-project/main.cpp
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
int main() {}
|
Reference in New Issue
Block a user