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 <hjk@qt.io>
This commit is contained in:
Christian Kandeler
2020-02-04 14:59:08 +01:00
parent ede1d31a3f
commit a6e6a53e82
4 changed files with 81 additions and 46 deletions

View File

@@ -194,32 +194,32 @@ static QStringList unreferencedBuildSystemFiles(const QJsonObject &project)
return unreferenced; return unreferenced;
} }
std::unique_ptr<QbsProjectNode> 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<QbsProjectNode>(projectData);
auto root = std::make_unique<QbsProjectNode>(buildSystem->projectData());
// If we have no project information at all (i.e. it could not be properly parsed), // If we have no project information at all (i.e. it could not be properly parsed),
// create the main project file node "manually". // create the main project file node "manually".
if (buildSystem->projectData().isEmpty()) { if (projectData.isEmpty()) {
auto fileNode = std::make_unique<FileNode>(project->projectFilePath(), FileType::Project); auto fileNode = std::make_unique<FileNode>(projectFile, FileType::Project);
root->addNode(std::move(fileNode)); root->addNode(std::move(fileNode));
} else { } else {
setupProjectNode(root.get()); setupProjectNode(root.get());
} }
if (root->displayName().isEmpty()) if (root->displayName().isEmpty())
root->setDisplayName(project->displayName()); root->setDisplayName(projectName);
if (root->displayName().isEmpty()) if (root->displayName().isEmpty())
root->setDisplayName(project->projectFilePath().toFileInfo().completeBaseName()); root->setDisplayName(projectFile.toFileInfo().completeBaseName());
auto buildSystemFiles = std::make_unique<FolderNode>(project->projectDirectory()); auto buildSystemFiles = std::make_unique<FolderNode>(projectDir);
buildSystemFiles->setDisplayName(QCoreApplication::translate("QbsProjectNode", "Qbs files")); buildSystemFiles->setDisplayName(QCoreApplication::translate("QbsProjectNode", "Qbs files"));
const FilePath projectDir = project->projectDirectory(); const FilePath buildDir = FilePath::fromString(projectData.value("build-directory").toString());
const FilePath buildDir = FilePath::fromString(buildSystem->projectData() const QStringList files = unreferencedBuildSystemFiles(projectData);
.value("build-directory").toString());
const QStringList files = unreferencedBuildSystemFiles(buildSystem->projectData());
for (const QString &f : files) { for (const QString &f : files) {
const FilePath filePath = FilePath::fromString(f); const FilePath filePath = FilePath::fromString(f);
if (filePath.isChildOf(projectDir)) { if (filePath.isChildOf(projectDir)) {
@@ -230,7 +230,7 @@ std::unique_ptr<QbsProjectNode> QbsNodeTreeBuilder::buildTree(const QbsBuildSyst
} }
buildSystemFiles->compress(); buildSystemFiles->compress();
root->addNode(std::move(buildSystemFiles)); root->addNode(std::move(buildSystemFiles));
return root; return root.release();
} }
} // namespace Internal } // namespace Internal

View File

@@ -25,18 +25,29 @@
#pragma once #pragma once
#include <QtGlobal>
#include <memory> #include <memory>
QT_BEGIN_NAMESPACE
class QJsonObject;
class QString;
QT_END_NAMESPACE
namespace Utils { class FilePath; }
namespace QbsProjectManager { namespace QbsProjectManager {
namespace Internal { namespace Internal {
class QbsBuildSystem;
class QbsProjectNode; class QbsProjectNode;
class QbsNodeTreeBuilder class QbsNodeTreeBuilder
{ {
public: public:
static std::unique_ptr<QbsProjectNode> buildTree(const QbsBuildSystem *buildSystem); static QbsProjectNode *buildTree(const QString &projectName,
const Utils::FilePath &projectFile,
const Utils::FilePath &projectDir,
const QJsonObject &projectData);
}; };
} // namespace Internal } // namespace Internal

View File

@@ -67,6 +67,7 @@
#include <utils/algorithm.h> #include <utils/algorithm.h>
#include <utils/hostosinfo.h> #include <utils/hostosinfo.h>
#include <utils/qtcassert.h> #include <utils/qtcassert.h>
#include <utils/runextensions.h>
#include <qmljs/qmljsmodelmanagerinterface.h> #include <qmljs/qmljsmodelmanagerinterface.h>
#include <qmljstools/qmljsmodelmanager.h> #include <qmljstools/qmljsmodelmanager.h>
#include <qtsupport/qtcppkitinfo.h> #include <qtsupport/qtcppkitinfo.h>
@@ -214,8 +215,7 @@ QbsBuildSystem::QbsBuildSystem(QbsBuildConfiguration *bc)
connect(&m_parsingDelay, &QTimer::timeout, this, &QbsBuildSystem::triggerParsing); connect(&m_parsingDelay, &QTimer::timeout, this, &QbsBuildSystem::triggerParsing);
connect(bc->project(), &Project::projectFileIsDirty, this, &QbsBuildSystem::delayParsing); connect(bc->project(), &Project::projectFileIsDirty, this, &QbsBuildSystem::delayParsing);
updateProjectNodes({});
rebuildProjectTree();
} }
QbsBuildSystem::~QbsBuildSystem() QbsBuildSystem::~QbsBuildSystem()
@@ -447,6 +447,7 @@ bool QbsBuildSystem::checkCancelStatus()
qCDebug(qbsPmLog) << "Cancel request while parsing, starting re-parse"; qCDebug(qbsPmLog) << "Cancel request while parsing, starting re-parse";
m_qbsProjectParser->deleteLater(); m_qbsProjectParser->deleteLater();
m_qbsProjectParser = nullptr; m_qbsProjectParser = nullptr;
m_treeCreationWatcher = nullptr;
m_guard = {}; m_guard = {};
parseCurrentBuildConfiguration(); parseCurrentBuildConfiguration();
return true; return true;
@@ -456,7 +457,7 @@ void QbsBuildSystem::updateAfterParse()
{ {
qCDebug(qbsPmLog) << "Updating data after parse"; qCDebug(qbsPmLog) << "Updating data after parse";
OpTimer opTimer("updateAfterParse"); OpTimer opTimer("updateAfterParse");
updateProjectNodes(); updateProjectNodes([this] {
updateDocuments(); updateDocuments();
updateBuildTargetData(); updateBuildTargetData();
updateCppCodeModel(); updateCppCodeModel();
@@ -464,7 +465,10 @@ void QbsBuildSystem::updateAfterParse()
updateQmlJsCodeModel(); updateQmlJsCodeModel();
emit project()->fileListChanged(); emit project()->fileListChanged();
m_envCache.clear(); m_envCache.clear();
m_guard.markAsSuccess();
m_guard = {};
emitBuildSystemUpdated(); emitBuildSystemUpdated();
});
} }
void QbsBuildSystem::delayedUpdateAfterParse() void QbsBuildSystem::delayedUpdateAfterParse()
@@ -472,10 +476,32 @@ void QbsBuildSystem::delayedUpdateAfterParse()
QTimer::singleShot(0, this, &QbsBuildSystem::updateAfterParse); QTimer::singleShot(0, this, &QbsBuildSystem::updateAfterParse);
} }
void QbsBuildSystem::updateProjectNodes() void QbsBuildSystem::updateProjectNodes(const std::function<void ()> &continuation)
{ {
OpTimer opTimer("updateProjectNodes"); m_treeCreationWatcher = new TreeCreationWatcher(this);
rebuildProjectTree(); connect(m_treeCreationWatcher, &TreeCreationWatcher::finished, this,
[this, watcher = m_treeCreationWatcher, continuation] {
std::unique_ptr<QbsProjectNode> 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() FilePath QbsBuildSystem::installRoot()
@@ -525,8 +551,10 @@ void QbsBuildSystem::handleQbsParsingDone(bool success)
delete m_qbsUpdateFutureInterface; delete m_qbsUpdateFutureInterface;
m_qbsUpdateFutureInterface = nullptr; m_qbsUpdateFutureInterface = nullptr;
if (dataChanged) if (dataChanged) {
updateAfterParse(); updateAfterParse();
return;
}
else if (envChanged) else if (envChanged)
updateCppCodeModel(); updateCppCodeModel();
if (success) if (success)
@@ -539,13 +567,6 @@ void QbsBuildSystem::handleQbsParsingDone(bool success)
emitBuildSystemUpdated(); emitBuildSystemUpdated();
} }
void QbsBuildSystem::rebuildProjectTree()
{
std::unique_ptr<QbsProjectNode> newRoot = QbsNodeTreeBuilder::buildTree(this);
project()->setDisplayName(newRoot->displayName());
setRootProjectNode(std::move(newRoot));
}
void QbsBuildSystem::changeActiveTarget(Target *t) void QbsBuildSystem::changeActiveTarget(Target *t)
{ {
if (t) if (t)
@@ -609,6 +630,7 @@ void QbsBuildSystem::parseCurrentBuildConfiguration()
QTC_ASSERT(!m_qbsProjectParser, return); QTC_ASSERT(!m_qbsProjectParser, return);
m_qbsProjectParser = new QbsProjectParser(this, m_qbsUpdateFutureInterface); m_qbsProjectParser = new QbsProjectParser(this, m_qbsUpdateFutureInterface);
m_treeCreationWatcher = nullptr;
connect(m_qbsProjectParser, &QbsProjectParser::done, connect(m_qbsProjectParser, &QbsProjectParser::done,
this, &QbsBuildSystem::handleQbsParsingDone); this, &QbsBuildSystem::handleQbsParsingDone);
@@ -636,10 +658,11 @@ void QbsBuildSystem::updateAfterBuild()
} }
qCDebug(qbsPmLog) << "Updating data after build"; qCDebug(qbsPmLog) << "Updating data after build";
m_projectData = projectData; m_projectData = projectData;
updateProjectNodes(); updateProjectNodes([this] {
updateBuildTargetData(); updateBuildTargetData();
updateExtraCompilers(); updateExtraCompilers();
m_envCache.clear(); m_envCache.clear();
});
} }
void QbsBuildSystem::generateErrors(const ErrorInfo &e) void QbsBuildSystem::generateErrors(const ErrorInfo &e)

View File

@@ -36,10 +36,13 @@
#include <utils/environment.h> #include <utils/environment.h>
#include <QFutureWatcher>
#include <QHash> #include <QHash>
#include <QJsonObject> #include <QJsonObject>
#include <QTimer> #include <QTimer>
#include <functional>
namespace CppTools { class CppProjectUpdater; } namespace CppTools { class CppProjectUpdater; }
namespace QbsProjectManager { namespace QbsProjectManager {
@@ -125,11 +128,7 @@ private:
friend class QbsProject; friend class QbsProject;
void handleQbsParsingDone(bool success); void handleQbsParsingDone(bool success);
void rebuildProjectTree();
void changeActiveTarget(ProjectExplorer::Target *t); void changeActiveTarget(ProjectExplorer::Target *t);
void prepareForParsing(); void prepareForParsing();
void updateDocuments(); void updateDocuments();
void updateCppCodeModel(); void updateCppCodeModel();
@@ -141,7 +140,7 @@ private:
bool checkCancelStatus(); bool checkCancelStatus();
void updateAfterParse(); void updateAfterParse();
void delayedUpdateAfterParse(); void delayedUpdateAfterParse();
void updateProjectNodes(); void updateProjectNodes(const std::function<void()> &continuation);
Utils::FilePath installRoot(); Utils::FilePath installRoot();
static bool ensureWriteableQbsFile(const QString &file); static bool ensureWriteableQbsFile(const QString &file);
@@ -153,6 +152,8 @@ private:
QTimer m_parsingDelay; QTimer m_parsingDelay;
QbsProjectParser *m_qbsProjectParser = nullptr; QbsProjectParser *m_qbsProjectParser = nullptr;
QFutureInterface<bool> *m_qbsUpdateFutureInterface = nullptr; QFutureInterface<bool> *m_qbsUpdateFutureInterface = nullptr;
using TreeCreationWatcher = QFutureWatcher<QbsProjectNode *>;
TreeCreationWatcher *m_treeCreationWatcher = nullptr;
Utils::Environment m_lastParseEnv; Utils::Environment m_lastParseEnv;
bool m_parsingScheduled = false; bool m_parsingScheduled = false;