From a6e6a53e82a06b0c78c20e44643f7bdc050862df Mon Sep 17 00:00:00 2001 From: Christian Kandeler Date: Tue, 4 Feb 2020 14:59:08 +0100 Subject: [PATCH] QbsProjectManager: Build up the project tree in a dedicated thread ... and only switch the root node in the UI thread. Creating the project tree can take some time for larger projects; no need to block the UI for that. Task-number: QTCREATORBUG-18533 Change-Id: I093dc9fb8f3454011a3c64bcc0f785e8b7753b4e Reviewed-by: hjk --- .../qbsprojectmanager/qbsnodetreebuilder.cpp | 26 +++---- .../qbsprojectmanager/qbsnodetreebuilder.h | 15 +++- src/plugins/qbsprojectmanager/qbsproject.cpp | 75 ++++++++++++------- src/plugins/qbsprojectmanager/qbsproject.h | 11 +-- 4 files changed, 81 insertions(+), 46 deletions(-) diff --git a/src/plugins/qbsprojectmanager/qbsnodetreebuilder.cpp b/src/plugins/qbsprojectmanager/qbsnodetreebuilder.cpp index bcf52a96f1f..18a994d3844 100644 --- a/src/plugins/qbsprojectmanager/qbsnodetreebuilder.cpp +++ b/src/plugins/qbsprojectmanager/qbsnodetreebuilder.cpp @@ -194,32 +194,32 @@ static QStringList unreferencedBuildSystemFiles(const QJsonObject &project) return unreferenced; } -std::unique_ptr QbsNodeTreeBuilder::buildTree(const QbsBuildSystem *buildSystem) +QbsProjectNode *QbsNodeTreeBuilder::buildTree(const QString &projectName, + const Utils::FilePath &projectFile, + const Utils::FilePath &projectDir, + const QJsonObject &projectData) { - const Project * const project = buildSystem->project(); - auto root = std::make_unique(buildSystem->projectData()); + auto root = std::make_unique(projectData); // If we have no project information at all (i.e. it could not be properly parsed), // create the main project file node "manually". - if (buildSystem->projectData().isEmpty()) { - auto fileNode = std::make_unique(project->projectFilePath(), FileType::Project); + if (projectData.isEmpty()) { + auto fileNode = std::make_unique(projectFile, FileType::Project); root->addNode(std::move(fileNode)); } else { setupProjectNode(root.get()); } if (root->displayName().isEmpty()) - root->setDisplayName(project->displayName()); + root->setDisplayName(projectName); if (root->displayName().isEmpty()) - root->setDisplayName(project->projectFilePath().toFileInfo().completeBaseName()); + root->setDisplayName(projectFile.toFileInfo().completeBaseName()); - auto buildSystemFiles = std::make_unique(project->projectDirectory()); + auto buildSystemFiles = std::make_unique(projectDir); buildSystemFiles->setDisplayName(QCoreApplication::translate("QbsProjectNode", "Qbs files")); - const FilePath projectDir = project->projectDirectory(); - const FilePath buildDir = FilePath::fromString(buildSystem->projectData() - .value("build-directory").toString()); - const QStringList files = unreferencedBuildSystemFiles(buildSystem->projectData()); + const FilePath buildDir = FilePath::fromString(projectData.value("build-directory").toString()); + const QStringList files = unreferencedBuildSystemFiles(projectData); for (const QString &f : files) { const FilePath filePath = FilePath::fromString(f); if (filePath.isChildOf(projectDir)) { @@ -230,7 +230,7 @@ std::unique_ptr QbsNodeTreeBuilder::buildTree(const QbsBuildSyst } buildSystemFiles->compress(); root->addNode(std::move(buildSystemFiles)); - return root; + return root.release(); } } // namespace Internal diff --git a/src/plugins/qbsprojectmanager/qbsnodetreebuilder.h b/src/plugins/qbsprojectmanager/qbsnodetreebuilder.h index 3c2cd450dc3..7bbff704ac8 100644 --- a/src/plugins/qbsprojectmanager/qbsnodetreebuilder.h +++ b/src/plugins/qbsprojectmanager/qbsnodetreebuilder.h @@ -25,18 +25,29 @@ #pragma once +#include + #include +QT_BEGIN_NAMESPACE +class QJsonObject; +class QString; +QT_END_NAMESPACE + +namespace Utils { class FilePath; } + namespace QbsProjectManager { namespace Internal { -class QbsBuildSystem; class QbsProjectNode; class QbsNodeTreeBuilder { public: - static std::unique_ptr buildTree(const QbsBuildSystem *buildSystem); + static QbsProjectNode *buildTree(const QString &projectName, + const Utils::FilePath &projectFile, + const Utils::FilePath &projectDir, + const QJsonObject &projectData); }; } // namespace Internal diff --git a/src/plugins/qbsprojectmanager/qbsproject.cpp b/src/plugins/qbsprojectmanager/qbsproject.cpp index dba851338e0..f5b2538dc87 100644 --- a/src/plugins/qbsprojectmanager/qbsproject.cpp +++ b/src/plugins/qbsprojectmanager/qbsproject.cpp @@ -67,6 +67,7 @@ #include #include #include +#include #include #include #include @@ -214,8 +215,7 @@ QbsBuildSystem::QbsBuildSystem(QbsBuildConfiguration *bc) connect(&m_parsingDelay, &QTimer::timeout, this, &QbsBuildSystem::triggerParsing); connect(bc->project(), &Project::projectFileIsDirty, this, &QbsBuildSystem::delayParsing); - - rebuildProjectTree(); + updateProjectNodes({}); } QbsBuildSystem::~QbsBuildSystem() @@ -447,6 +447,7 @@ bool QbsBuildSystem::checkCancelStatus() qCDebug(qbsPmLog) << "Cancel request while parsing, starting re-parse"; m_qbsProjectParser->deleteLater(); m_qbsProjectParser = nullptr; + m_treeCreationWatcher = nullptr; m_guard = {}; parseCurrentBuildConfiguration(); return true; @@ -456,15 +457,18 @@ void QbsBuildSystem::updateAfterParse() { qCDebug(qbsPmLog) << "Updating data after parse"; OpTimer opTimer("updateAfterParse"); - updateProjectNodes(); - updateDocuments(); - updateBuildTargetData(); - updateCppCodeModel(); - updateExtraCompilers(); - updateQmlJsCodeModel(); - emit project()->fileListChanged(); - m_envCache.clear(); - emitBuildSystemUpdated(); + updateProjectNodes([this] { + updateDocuments(); + updateBuildTargetData(); + updateCppCodeModel(); + updateExtraCompilers(); + updateQmlJsCodeModel(); + emit project()->fileListChanged(); + m_envCache.clear(); + m_guard.markAsSuccess(); + m_guard = {}; + emitBuildSystemUpdated(); + }); } void QbsBuildSystem::delayedUpdateAfterParse() @@ -472,10 +476,32 @@ void QbsBuildSystem::delayedUpdateAfterParse() QTimer::singleShot(0, this, &QbsBuildSystem::updateAfterParse); } -void QbsBuildSystem::updateProjectNodes() +void QbsBuildSystem::updateProjectNodes(const std::function &continuation) { - OpTimer opTimer("updateProjectNodes"); - rebuildProjectTree(); + m_treeCreationWatcher = new TreeCreationWatcher(this); + connect(m_treeCreationWatcher, &TreeCreationWatcher::finished, this, + [this, watcher = m_treeCreationWatcher, continuation] { + std::unique_ptr rootNode(m_treeCreationWatcher->result()); + if (watcher != m_treeCreationWatcher) { + watcher->deleteLater(); + return; + } + OpTimer("updateProjectNodes continuation"); + m_treeCreationWatcher->deleteLater(); + m_treeCreationWatcher = nullptr; + if (target() != project()->activeTarget() + || target()->activeBuildConfiguration()->buildSystem() != this) { + return; + } + project()->setDisplayName(rootNode->displayName()); + setRootProjectNode(std::move(rootNode)); + if (continuation) + continuation(); + }); + m_treeCreationWatcher->setFuture(runAsync(ProjectExplorerPlugin::sharedThreadPool(), + QThread::LowPriority, &QbsNodeTreeBuilder::buildTree, + project()->displayName(), project()->projectFilePath(), project()->projectDirectory(), + projectData())); } FilePath QbsBuildSystem::installRoot() @@ -525,8 +551,10 @@ void QbsBuildSystem::handleQbsParsingDone(bool success) delete m_qbsUpdateFutureInterface; m_qbsUpdateFutureInterface = nullptr; - if (dataChanged) + if (dataChanged) { updateAfterParse(); + return; + } else if (envChanged) updateCppCodeModel(); if (success) @@ -539,13 +567,6 @@ void QbsBuildSystem::handleQbsParsingDone(bool success) emitBuildSystemUpdated(); } -void QbsBuildSystem::rebuildProjectTree() -{ - std::unique_ptr newRoot = QbsNodeTreeBuilder::buildTree(this); - project()->setDisplayName(newRoot->displayName()); - setRootProjectNode(std::move(newRoot)); -} - void QbsBuildSystem::changeActiveTarget(Target *t) { if (t) @@ -609,6 +630,7 @@ void QbsBuildSystem::parseCurrentBuildConfiguration() QTC_ASSERT(!m_qbsProjectParser, return); m_qbsProjectParser = new QbsProjectParser(this, m_qbsUpdateFutureInterface); + m_treeCreationWatcher = nullptr; connect(m_qbsProjectParser, &QbsProjectParser::done, this, &QbsBuildSystem::handleQbsParsingDone); @@ -636,10 +658,11 @@ void QbsBuildSystem::updateAfterBuild() } qCDebug(qbsPmLog) << "Updating data after build"; m_projectData = projectData; - updateProjectNodes(); - updateBuildTargetData(); - updateExtraCompilers(); - m_envCache.clear(); + updateProjectNodes([this] { + updateBuildTargetData(); + updateExtraCompilers(); + m_envCache.clear(); + }); } void QbsBuildSystem::generateErrors(const ErrorInfo &e) diff --git a/src/plugins/qbsprojectmanager/qbsproject.h b/src/plugins/qbsprojectmanager/qbsproject.h index 8f19cc0f036..250d544e3b0 100644 --- a/src/plugins/qbsprojectmanager/qbsproject.h +++ b/src/plugins/qbsprojectmanager/qbsproject.h @@ -36,10 +36,13 @@ #include +#include #include #include #include +#include + namespace CppTools { class CppProjectUpdater; } namespace QbsProjectManager { @@ -125,11 +128,7 @@ private: friend class QbsProject; void handleQbsParsingDone(bool success); - - void rebuildProjectTree(); - void changeActiveTarget(ProjectExplorer::Target *t); - void prepareForParsing(); void updateDocuments(); void updateCppCodeModel(); @@ -141,7 +140,7 @@ private: bool checkCancelStatus(); void updateAfterParse(); void delayedUpdateAfterParse(); - void updateProjectNodes(); + void updateProjectNodes(const std::function &continuation); Utils::FilePath installRoot(); static bool ensureWriteableQbsFile(const QString &file); @@ -153,6 +152,8 @@ private: QTimer m_parsingDelay; QbsProjectParser *m_qbsProjectParser = nullptr; QFutureInterface *m_qbsUpdateFutureInterface = nullptr; + using TreeCreationWatcher = QFutureWatcher; + TreeCreationWatcher *m_treeCreationWatcher = nullptr; Utils::Environment m_lastParseEnv; bool m_parsingScheduled = false;