CMake: make project file system tree scanner persistent

Project source tree is a same for all build configurations so it is
a good idea to keep it persistent between CMake runs, configurations
switches and so on. It safes a lot of time for big projects.

Move more operations to the scanner thread:
 - Nodes filtering: skip .user files on top level of the project, skip
well-known extensions and octet-streams: In most cases these are not
required to be shown in the project tree.
 - Nodes sorting

Fix small memory leak: we have .user in the scanner result. After this
node filtered out, but is not feed (old code at the
BuildDirManager::generateProjectTree()). Now .user file skips during scan
without memory allocation at all.

Allow user manually rescan project tree by call Build -> Rescan project
tree. It runs CMake and Tree Scanner together: in most cases only CMake
run requires but time to time (VCS update) full rescan also required.

Change-Id: I4a6e6c897202da557509291c79932dd7751860e5
Reviewed-by: Tobias Hunger <tobias.hunger@qt.io>
This commit is contained in:
Alexander Drozdov
2016-12-04 03:36:12 +10:00
parent 357cefe67c
commit dfaf01614d
13 changed files with 428 additions and 85 deletions

View File

@@ -32,9 +32,11 @@
#include "cmakerunconfiguration.h"
#include "cmakeprojectmanager.h"
#include <coreplugin/progressmanager/progressmanager.h>
#include <cpptools/cppmodelmanager.h>
#include <cpptools/generatedcodemodelsupport.h>
#include <cpptools/projectinfo.h>
#include <cpptools/cpptoolsconstants.h>
#include <cpptools/projectpartbuilder.h>
#include <projectexplorer/buildtargetinfo.h>
#include <projectexplorer/deploymentdata.h>
@@ -85,6 +87,43 @@ CMakeProject::CMakeProject(CMakeManager *manager, const FileName &fileName)
rootProjectNode()->setDisplayName(fileName.parentDir().fileName());
connect(this, &CMakeProject::activeTargetChanged, this, &CMakeProject::handleActiveTargetChanged);
connect(&m_treeScanner, &TreeScanner::finished, this, &CMakeProject::handleTreeScanningFinished);
m_treeScanner.setFilter([this](const Utils::MimeType &mimeType, const Utils::FileName &fn) {
// Mime checks requires more resources, so keep it last in check list
auto isIgnored =
fn.toString().startsWith(projectFilePath().toString() + ".user") ||
TreeScanner::isWellKnownBinary(mimeType, fn);
// Cache mime check result for speed up
if (!isIgnored) {
auto it = m_mimeBinaryCache.find(mimeType.name());
if (it != m_mimeBinaryCache.end()) {
isIgnored = *it;
} else {
isIgnored = TreeScanner::isMimeBinary(mimeType, fn);
m_mimeBinaryCache[mimeType.name()] = isIgnored;
}
}
return isIgnored;
});
m_treeScanner.setTypeFactory([](const Utils::MimeType &mimeType, const Utils::FileName &fn) {
auto type = TreeScanner::genericFileType(mimeType, fn);
if (type == FileType::Unknown) {
if (mimeType.isValid()) {
const QString mt = mimeType.name();
if (mt == CMakeProjectManager::Constants::CMAKEPROJECTMIMETYPE
|| mt == CMakeProjectManager::Constants::CMAKEMIMETYPE)
type = FileType::Project;
}
}
return type;
});
scanProjectTree();
}
CMakeProject::~CMakeProject()
@@ -92,6 +131,7 @@ CMakeProject::~CMakeProject()
setRootProjectNode(nullptr);
m_codeModelFuture.cancel();
qDeleteAll(m_extraCompilers);
qDeleteAll(m_allFiles);
}
void CMakeProject::updateProjectData(CMakeBuildConfiguration *bc)
@@ -101,9 +141,13 @@ void CMakeProject::updateProjectData(CMakeBuildConfiguration *bc)
Target *t = activeTarget();
if (!t || t->activeBuildConfiguration() != bc)
return;
if (!m_treeScanner.isFinished() || bc->isParsing())
return;
Kit *k = t->kit();
bc->generateProjectTree(static_cast<CMakeListsNode *>(rootProjectNode()));
bc->generateProjectTree(static_cast<CMakeListsNode *>(rootProjectNode()), m_allFiles);
updateApplicationAndDeploymentTargets();
updateTargetRunConfigurations(t);
@@ -290,6 +334,16 @@ bool CMakeProject::setupTarget(Target *t)
return true;
}
void CMakeProject::scanProjectTree()
{
if (!m_treeScanner.isFinished())
return;
m_treeScanner.asyncScanForFiles(projectDirectory());
Core::ProgressManager::addTask(m_treeScanner.future(),
tr("Scan \"%1\" project tree").arg(displayName()),
"CMake.Scan.Tree");
}
void CMakeProject::handleActiveTargetChanged()
{
if (m_connectedTarget) {
@@ -335,6 +389,22 @@ void CMakeProject::handleParsingStarted()
emit parsingStarted();
}
void CMakeProject::handleTreeScanningFinished()
{
qDeleteAll(m_allFiles);
m_allFiles = m_treeScanner.release();
auto t = activeTarget();
if (!t)
return;
auto bc = qobject_cast<CMakeBuildConfiguration*>(t->activeBuildConfiguration());
if (!bc)
return;
updateProjectData(bc);
}
CMakeBuildTarget CMakeProject::buildTargetForTitle(const QString &title)
{
foreach (const CMakeBuildTarget &ct, buildTargets())