forked from qt-creator/qt-creator
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:
@@ -54,7 +54,6 @@
|
||||
#include <utils/mimetypes/mimedatabase.h>
|
||||
#include <utils/qtcassert.h>
|
||||
#include <utils/qtcprocess.h>
|
||||
#include <utils/runextensions.h>
|
||||
#include <utils/synchronousprocess.h>
|
||||
|
||||
#include <QDateTime>
|
||||
@@ -82,9 +81,6 @@ BuildDirManager::BuildDirManager(CMakeBuildConfiguration *bc) :
|
||||
m_reparseTimer.setSingleShot(true);
|
||||
|
||||
connect(&m_reparseTimer, &QTimer::timeout, this, &BuildDirManager::parse);
|
||||
|
||||
connect(&m_futureWatcher, &QFutureWatcher<QList<FileNode *>>::finished,
|
||||
this, &BuildDirManager::emitDataAvailable);
|
||||
}
|
||||
|
||||
BuildDirManager::~BuildDirManager() = default;
|
||||
@@ -105,7 +101,7 @@ const Utils::FileName BuildDirManager::workDirectory() const
|
||||
|
||||
void BuildDirManager::emitDataAvailable()
|
||||
{
|
||||
if (!isParsing() && m_futureWatcher.isFinished())
|
||||
if (!isParsing())
|
||||
emit dataAvailable();
|
||||
}
|
||||
|
||||
@@ -145,16 +141,7 @@ void BuildDirManager::parseOnceReaderReady(bool force)
|
||||
TaskHub::clearTasks(ProjectExplorer::Constants::TASK_CATEGORY_BUILDSYSTEM);
|
||||
|
||||
m_buildTargets.clear();
|
||||
|
||||
auto fi = new QFutureInterface<QList<ProjectExplorer::FileNode *>>();
|
||||
m_scanFuture = fi->future();
|
||||
m_futureWatcher.setFuture(m_scanFuture);
|
||||
|
||||
m_cmakeCache.clear();
|
||||
|
||||
Core::ProgressManager::addTask(fi->future(), "Scan CMake project tree", "CMake.Scan.Tree");
|
||||
Utils::runAsync([this, fi]() { BuildDirManager::asyncScanForFiles(fi); });
|
||||
|
||||
checkConfiguration();
|
||||
m_reader->stop();
|
||||
m_reader->parse(force);
|
||||
@@ -246,50 +233,6 @@ void BuildDirManager::becameDirty()
|
||||
m_reparseTimer.start(1000);
|
||||
}
|
||||
|
||||
void BuildDirManager::asyncScanForFiles(QFutureInterface<QList<FileNode *>> *fi)
|
||||
{
|
||||
std::unique_ptr<QFutureInterface<QList<FileNode *>>> fip(fi);
|
||||
fip->reportStarted();
|
||||
Utils::MimeDatabase mdb;
|
||||
|
||||
QList<FileNode *> nodes
|
||||
= FileNode::scanForFiles(m_buildConfiguration->target()->project()->projectDirectory(),
|
||||
[&mdb](const Utils::FileName &fn) -> FileNode * {
|
||||
QTC_ASSERT(!fn.isEmpty(), return nullptr);
|
||||
const Utils::MimeType mimeType = mdb.mimeTypeForFile(fn.toString());
|
||||
FileType type = FileType::Unknown;
|
||||
if (mimeType.isValid()) {
|
||||
const QString mt = mimeType.name();
|
||||
if (mt == CppTools::Constants::C_SOURCE_MIMETYPE
|
||||
|| mt == CppTools::Constants::CPP_SOURCE_MIMETYPE
|
||||
|| mt == CppTools::Constants::OBJECTIVE_C_SOURCE_MIMETYPE
|
||||
|| mt == CppTools::Constants::OBJECTIVE_CPP_SOURCE_MIMETYPE
|
||||
|| mt == CppTools::Constants::QDOC_MIMETYPE
|
||||
|| mt == CppTools::Constants::MOC_MIMETYPE)
|
||||
type = FileType::Source;
|
||||
else if (mt == CppTools::Constants::C_HEADER_MIMETYPE
|
||||
|| mt == CppTools::Constants::CPP_HEADER_MIMETYPE)
|
||||
type = FileType::Header;
|
||||
else if (mt == ProjectExplorer::Constants::FORM_MIMETYPE)
|
||||
type = FileType::Form;
|
||||
else if (mt == ProjectExplorer::Constants::RESOURCE_MIMETYPE)
|
||||
type = FileType::Resource;
|
||||
else if (mt == ProjectExplorer::Constants::SCXML_MIMETYPE)
|
||||
type = FileType::StateChart;
|
||||
else if (mt == CMakeProjectManager::Constants::CMAKEPROJECTMIMETYPE
|
||||
|| mt == CMakeProjectManager::Constants::CMAKEMIMETYPE)
|
||||
type = FileType::Project;
|
||||
else if (mt == ProjectExplorer::Constants::QML_MIMETYPE)
|
||||
type = FileType::QML;
|
||||
}
|
||||
return new FileNode(fn, type, false);
|
||||
},
|
||||
fip.get());
|
||||
fip->setProgressValue(fip->progressMaximum());
|
||||
fip->reportResult(nodes);
|
||||
fip->reportFinished();
|
||||
}
|
||||
|
||||
void BuildDirManager::forceReparse()
|
||||
{
|
||||
if (m_buildConfiguration->target()->activeBuildConfiguration() != m_buildConfiguration)
|
||||
@@ -308,7 +251,6 @@ void BuildDirManager::resetData()
|
||||
m_reader->resetData();
|
||||
|
||||
m_cmakeCache.clear();
|
||||
m_futureWatcher.setFuture(QFuture<QList<FileNode *>>());
|
||||
m_reader.reset();
|
||||
|
||||
m_buildTargets.clear();
|
||||
@@ -334,22 +276,19 @@ bool BuildDirManager::persistCMakeState()
|
||||
return true;
|
||||
}
|
||||
|
||||
void BuildDirManager::generateProjectTree(CMakeListsNode *root)
|
||||
void BuildDirManager::generateProjectTree(CMakeListsNode *root, const QList<FileNode *> &allFiles)
|
||||
{
|
||||
QTC_ASSERT(m_reader, return);
|
||||
QTC_ASSERT(m_scanFuture.isFinished(), return);
|
||||
|
||||
const Utils::FileName projectFile = m_buildConfiguration->target()->project()->projectFilePath();
|
||||
QList<FileNode *> tmp = Utils::filtered(m_scanFuture.result(),
|
||||
[projectFile](const FileNode *fn) -> bool {
|
||||
return !fn->filePath().toString().startsWith(projectFile.toString() + ".user");
|
||||
|
||||
// input files only a reference, it persistent between calls
|
||||
// make copy of them for concrete configuration
|
||||
auto tmp = Utils::transform(allFiles, [](const FileNode* fn) {
|
||||
return new FileNode(*fn);
|
||||
});
|
||||
Utils::sort(tmp, ProjectExplorer::Node::sortByPath);
|
||||
|
||||
m_scanFuture = QFuture<QList<FileNode *>>(); // flush stale results
|
||||
|
||||
const QList<FileNode *> allFiles = tmp;
|
||||
m_reader->generateProjectTree(root, allFiles);
|
||||
m_reader->generateProjectTree(root, tmp);
|
||||
QSet<FileNode *> usedNodes;
|
||||
foreach (FileNode *fn, root->recursiveFileNodes())
|
||||
usedNodes.insert(fn);
|
||||
@@ -358,7 +297,7 @@ void BuildDirManager::generateProjectTree(CMakeListsNode *root)
|
||||
if (root->fileNodes().isEmpty()
|
||||
&& root->folderNodes().isEmpty()
|
||||
&& root->projectNodes().isEmpty()) {
|
||||
FileNode *cm = Utils::findOrDefault(allFiles, [&projectFile](const FileNode *fn) {
|
||||
FileNode *cm = Utils::findOrDefault(tmp, [&projectFile](const FileNode *fn) {
|
||||
return fn->filePath() == projectFile;
|
||||
});
|
||||
if (cm) {
|
||||
@@ -367,7 +306,7 @@ void BuildDirManager::generateProjectTree(CMakeListsNode *root)
|
||||
}
|
||||
}
|
||||
|
||||
QList<FileNode *> leftOvers = Utils::filtered(allFiles, [&usedNodes](FileNode *fn) {
|
||||
QList<FileNode *> leftOvers = Utils::filtered(tmp, [&usedNodes](FileNode *fn) {
|
||||
return !usedNodes.contains(fn);
|
||||
});
|
||||
qDeleteAll(leftOvers);
|
||||
|
||||
@@ -30,7 +30,6 @@
|
||||
|
||||
#include <utils/fileutils.h>
|
||||
|
||||
#include <QFutureWatcher>
|
||||
#include <QObject>
|
||||
#include <QTemporaryDir>
|
||||
#include <QTimer>
|
||||
@@ -72,7 +71,7 @@ public:
|
||||
bool updateCMakeStateBeforeBuild();
|
||||
bool persistCMakeState();
|
||||
|
||||
void generateProjectTree(CMakeListsNode *root);
|
||||
void generateProjectTree(CMakeListsNode *root, const QList<ProjectExplorer::FileNode *> &allFiles);
|
||||
QSet<Core::Id> updateCodeModel(CppTools::ProjectPartBuilder &ppBuilder);
|
||||
|
||||
QList<CMakeBuildTarget> buildTargets() const;
|
||||
@@ -103,8 +102,6 @@ private:
|
||||
|
||||
void becameDirty();
|
||||
|
||||
void asyncScanForFiles(QFutureInterface<QList<ProjectExplorer::FileNode *>> *fi);
|
||||
|
||||
CMakeBuildConfiguration *m_buildConfiguration = nullptr;
|
||||
mutable std::unique_ptr<QTemporaryDir> m_tempDir = nullptr;
|
||||
mutable CMakeConfig m_cmakeCache;
|
||||
@@ -112,8 +109,6 @@ private:
|
||||
QTimer m_reparseTimer;
|
||||
|
||||
std::unique_ptr<BuildDirReader> m_reader;
|
||||
QFutureWatcher<QList<ProjectExplorer::FileNode*>> m_futureWatcher;
|
||||
QFuture<QList<ProjectExplorer::FileNode*>> m_scanFuture;
|
||||
|
||||
mutable QList<CMakeBuildTarget> m_buildTargets;
|
||||
};
|
||||
|
||||
@@ -210,12 +210,12 @@ QList<CMakeBuildTarget> CMakeBuildConfiguration::buildTargets() const
|
||||
return m_buildDirManager->buildTargets();
|
||||
}
|
||||
|
||||
void CMakeBuildConfiguration::generateProjectTree(CMakeListsNode *root) const
|
||||
void CMakeBuildConfiguration::generateProjectTree(CMakeListsNode *root, const QList<FileNode*> &allFiles) const
|
||||
{
|
||||
if (!m_buildDirManager || m_buildDirManager->isParsing())
|
||||
return;
|
||||
|
||||
return m_buildDirManager->generateProjectTree(root);
|
||||
m_buildDirManager->generateProjectTree(root, allFiles);
|
||||
}
|
||||
|
||||
QSet<Core::Id> CMakeBuildConfiguration::updateCodeModel(CppTools::ProjectPartBuilder &ppBuilder)
|
||||
|
||||
@@ -85,7 +85,7 @@ public:
|
||||
void clearCache();
|
||||
|
||||
QList<CMakeBuildTarget> buildTargets() const;
|
||||
void generateProjectTree(CMakeListsNode *root) const;
|
||||
void generateProjectTree(CMakeListsNode *root, const QList<ProjectExplorer::FileNode *> &allFiles) const;
|
||||
QSet<Core::Id> updateCodeModel(CppTools::ProjectPartBuilder &ppBuilder);
|
||||
|
||||
static Utils::FileName
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "cmake_global.h"
|
||||
#include "treescanner.h"
|
||||
|
||||
#include <projectexplorer/extracompiler.h>
|
||||
#include <projectexplorer/project.h>
|
||||
@@ -33,6 +34,7 @@
|
||||
#include <utils/fileutils.h>
|
||||
|
||||
#include <QFuture>
|
||||
#include <QHash>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QFileSystemWatcher;
|
||||
@@ -95,6 +97,7 @@ public:
|
||||
bool supportsKit(ProjectExplorer::Kit *k, QString *errorMessage = 0) const final;
|
||||
|
||||
void runCMake();
|
||||
void scanProjectTree();
|
||||
|
||||
// Context menu actions:
|
||||
void buildCMakeTarget(const QString &buildTarget);
|
||||
@@ -113,6 +116,7 @@ private:
|
||||
void handleActiveTargetChanged();
|
||||
void handleActiveBuildConfigurationChanged();
|
||||
void handleParsingStarted();
|
||||
void handleTreeScanningFinished();
|
||||
void updateProjectData(Internal::CMakeBuildConfiguration *cmakeBc);
|
||||
void updateQmlJSCodeModel();
|
||||
|
||||
@@ -128,6 +132,10 @@ private:
|
||||
QFuture<void> m_codeModelFuture;
|
||||
QList<ProjectExplorer::ExtraCompiler *> m_extraCompilers;
|
||||
|
||||
Internal::TreeScanner m_treeScanner;
|
||||
QHash<QString, bool> m_mimeBinaryCache;
|
||||
QList<ProjectExplorer::FileNode *> m_allFiles;
|
||||
|
||||
friend class Internal::CMakeBuildConfiguration;
|
||||
friend class Internal::CMakeBuildSettingsWidget;
|
||||
};
|
||||
|
||||
@@ -37,6 +37,7 @@ const char CMAKE_EDITOR_ID[] = "CMakeProject.CMakeEditor";
|
||||
const char CMAKE_EDITOR_DISPLAY_NAME[] = QT_TRANSLATE_NOOP("CMakeProjectManager::Internal::CMakeEditorFactory", "CMake Editor");
|
||||
const char RUNCMAKE[] = "CMakeProject.RunCMake";
|
||||
const char CLEARCMAKECACHE[] = "CMakeProject.ClearCache";
|
||||
const char RESCANPROJECT[] = "CMakeProject.RescanProject";
|
||||
const char RUNCMAKECONTEXTMENU[] = "CMakeProject.RunCMakeContextMenu";
|
||||
|
||||
// Project
|
||||
|
||||
@@ -56,7 +56,8 @@ using namespace CMakeProjectManager::Internal;
|
||||
CMakeManager::CMakeManager() :
|
||||
m_runCMakeAction(new QAction(QIcon(), tr("Run CMake"), this)),
|
||||
m_clearCMakeCacheAction(new QAction(QIcon(), tr("Clear CMake Configuration"), this)),
|
||||
m_runCMakeActionContextMenu(new QAction(QIcon(), tr("Run CMake"), this))
|
||||
m_runCMakeActionContextMenu(new QAction(QIcon(), tr("Run CMake"), this)),
|
||||
m_rescanProjectAction(new QAction(QIcon(), tr("Rescan project"), this))
|
||||
{
|
||||
Core::ActionContainer *mbuild =
|
||||
Core::ActionManager::actionContainer(ProjectExplorer::Constants::M_BUILDPROJECT);
|
||||
@@ -93,6 +94,14 @@ CMakeManager::CMakeManager() :
|
||||
runCMake(ProjectTree::currentProject());
|
||||
});
|
||||
|
||||
command = Core::ActionManager::registerAction(m_rescanProjectAction,
|
||||
Constants::RESCANPROJECT, globalContext);
|
||||
command->setAttribute(Core::Command::CA_Hide);
|
||||
mbuild->addAction(command, ProjectExplorer::Constants::G_BUILD_DEPLOY);
|
||||
connect(m_rescanProjectAction, &QAction::triggered, [this]() {
|
||||
rescanProject(ProjectTree::currentProject());
|
||||
});
|
||||
|
||||
connect(SessionManager::instance(), &SessionManager::startupProjectChanged,
|
||||
this, &CMakeManager::updateCmakeActions);
|
||||
connect(BuildManager::instance(), &BuildManager::buildStateChanged,
|
||||
@@ -107,6 +116,7 @@ void CMakeManager::updateCmakeActions()
|
||||
const bool visible = project && !BuildManager::isBuilding(project);
|
||||
m_runCMakeAction->setVisible(visible);
|
||||
m_clearCMakeCacheAction->setVisible(visible);
|
||||
m_rescanProjectAction->setVisible(visible);
|
||||
}
|
||||
|
||||
void CMakeManager::clearCMakeCache(Project *project)
|
||||
@@ -134,6 +144,18 @@ void CMakeManager::runCMake(Project *project)
|
||||
cmakeProject->runCMake();
|
||||
}
|
||||
|
||||
void CMakeManager::rescanProject(Project *project)
|
||||
{
|
||||
if (!project)
|
||||
return;
|
||||
CMakeProject *cmakeProject = qobject_cast<CMakeProject *>(project);
|
||||
if (!cmakeProject || !cmakeProject->activeTarget() || !cmakeProject->activeTarget()->activeBuildConfiguration())
|
||||
return;
|
||||
|
||||
cmakeProject->scanProjectTree();
|
||||
cmakeProject->runCMake(); // by my experience: every rescan run requires cmake run too
|
||||
}
|
||||
|
||||
Project *CMakeManager::openProject(const QString &fileName, QString *errorString)
|
||||
{
|
||||
Utils::FileName file = Utils::FileName::fromString(fileName);
|
||||
|
||||
@@ -61,10 +61,12 @@ private:
|
||||
void updateCmakeActions();
|
||||
void clearCMakeCache(ProjectExplorer::Project *project);
|
||||
void runCMake(ProjectExplorer::Project *project);
|
||||
void rescanProject(ProjectExplorer::Project *project);
|
||||
|
||||
QAction *m_runCMakeAction;
|
||||
QAction *m_clearCMakeCacheAction;
|
||||
QAction *m_runCMakeActionContextMenu;
|
||||
QAction *m_rescanProjectAction;
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
|
||||
@@ -32,7 +32,8 @@ HEADERS = builddirmanager.h \
|
||||
configmodelitemdelegate.h \
|
||||
servermode.h \
|
||||
servermodereader.h \
|
||||
tealeafreader.h
|
||||
tealeafreader.h \
|
||||
treescanner.h
|
||||
|
||||
SOURCES = builddirmanager.cpp \
|
||||
builddirreader.cpp \
|
||||
@@ -62,6 +63,7 @@ SOURCES = builddirmanager.cpp \
|
||||
configmodelitemdelegate.cpp \
|
||||
servermode.cpp \
|
||||
servermodereader.cpp \
|
||||
tealeafreader.cpp
|
||||
tealeafreader.cpp \
|
||||
treescanner.cpp
|
||||
|
||||
RESOURCES += cmakeproject.qrc
|
||||
|
||||
@@ -79,6 +79,8 @@ QtcPlugin {
|
||||
"servermodereader.cpp",
|
||||
"servermodereader.h",
|
||||
"tealeafreader.cpp",
|
||||
"tealeafreader.h"
|
||||
"tealeafreader.h",
|
||||
"treescanner.cpp",
|
||||
"treescanner.h"
|
||||
]
|
||||
}
|
||||
|
||||
202
src/plugins/cmakeprojectmanager/treescanner.cpp
Normal file
202
src/plugins/cmakeprojectmanager/treescanner.cpp
Normal file
@@ -0,0 +1,202 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2016 Alexander Drozdov.
|
||||
** Contact: Alexander Drozdov (adrozdoff@gmail.com)
|
||||
**
|
||||
** 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 https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "treescanner.h"
|
||||
|
||||
#include <projectexplorer/projectexplorerconstants.h>
|
||||
|
||||
#include <cpptools/cpptoolsconstants.h>
|
||||
|
||||
#include <utils/qtcassert.h>
|
||||
#include <utils/algorithm.h>
|
||||
#include <utils/runextensions.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
using namespace ProjectExplorer;
|
||||
|
||||
namespace CMakeProjectManager {
|
||||
namespace Internal {
|
||||
|
||||
TreeScanner::TreeScanner(QObject *parent) : QObject(parent)
|
||||
{
|
||||
m_factory = TreeScanner::genericFileType;
|
||||
m_filter = [](const Utils::MimeType &mimeType, const Utils::FileName &fn) {
|
||||
return isWellKnownBinary(mimeType, fn) && isMimeBinary(mimeType, fn);
|
||||
};
|
||||
|
||||
connect(&m_futureWatcher, &FutureWatcher::finished, this, &TreeScanner::finished);
|
||||
}
|
||||
|
||||
bool TreeScanner::asyncScanForFiles(const Utils::FileName &directory)
|
||||
{
|
||||
if (!m_futureWatcher.isFinished())
|
||||
return false;
|
||||
|
||||
auto fi = new FutureInterface();
|
||||
m_scanFuture = fi->future();
|
||||
m_futureWatcher.setFuture(m_scanFuture);
|
||||
|
||||
Utils::runAsync([this, fi, directory]() { TreeScanner::scanForFiles(fi, directory, m_filter, m_factory); });
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void TreeScanner::setFilter(TreeScanner::FileFilter filter)
|
||||
{
|
||||
if (isFinished())
|
||||
m_filter = filter;
|
||||
}
|
||||
|
||||
void TreeScanner::setTypeFactory(TreeScanner::FileTypeFactory factory)
|
||||
{
|
||||
if (isFinished())
|
||||
m_factory = factory;
|
||||
}
|
||||
|
||||
TreeScanner::Future TreeScanner::future() const
|
||||
{
|
||||
return m_scanFuture;
|
||||
}
|
||||
|
||||
bool TreeScanner::isFinished() const
|
||||
{
|
||||
return m_futureWatcher.isFinished();
|
||||
}
|
||||
|
||||
TreeScanner::Result TreeScanner::result() const
|
||||
{
|
||||
if (isFinished())
|
||||
return m_scanFuture.result();
|
||||
return Result();
|
||||
}
|
||||
|
||||
TreeScanner::Result TreeScanner::release()
|
||||
{
|
||||
if (isFinished()) {
|
||||
auto result = m_scanFuture.result();
|
||||
m_scanFuture = Future();
|
||||
return result;
|
||||
}
|
||||
return Result();
|
||||
}
|
||||
|
||||
void TreeScanner::reset()
|
||||
{
|
||||
if (isFinished())
|
||||
m_scanFuture = Future();
|
||||
}
|
||||
|
||||
bool TreeScanner::isWellKnownBinary(const Utils::MimeType & /*mdb*/, const Utils::FileName &fn)
|
||||
{
|
||||
return fn.endsWith(QLatin1String(".a")) ||
|
||||
fn.endsWith(QLatin1String(".o")) ||
|
||||
fn.endsWith(QLatin1String(".d")) ||
|
||||
fn.endsWith(QLatin1String(".exe")) ||
|
||||
fn.endsWith(QLatin1String(".dll")) ||
|
||||
fn.endsWith(QLatin1String(".obj")) ||
|
||||
fn.endsWith(QLatin1String(".elf"));
|
||||
}
|
||||
|
||||
bool TreeScanner::isMimeBinary(const Utils::MimeType &mimeType, const Utils::FileName &/*fn*/)
|
||||
{
|
||||
bool isBinary = false;
|
||||
if (mimeType.isValid()) {
|
||||
QStringList mimes;
|
||||
mimes << mimeType.name() << mimeType.allAncestors();
|
||||
isBinary = !mimes.contains(QLatin1String("text/plain"));
|
||||
}
|
||||
return isBinary;
|
||||
}
|
||||
|
||||
FileType TreeScanner::genericFileType(const Utils::MimeType &mimeType, const Utils::FileName &/*fn*/)
|
||||
{
|
||||
FileType type = FileType::Unknown;
|
||||
if (mimeType.isValid()) {
|
||||
const QString mt = mimeType.name();
|
||||
if (mt == CppTools::Constants::C_SOURCE_MIMETYPE
|
||||
|| mt == CppTools::Constants::CPP_SOURCE_MIMETYPE
|
||||
|| mt == CppTools::Constants::OBJECTIVE_C_SOURCE_MIMETYPE
|
||||
|| mt == CppTools::Constants::OBJECTIVE_CPP_SOURCE_MIMETYPE
|
||||
|| mt == CppTools::Constants::QDOC_MIMETYPE
|
||||
|| mt == CppTools::Constants::MOC_MIMETYPE)
|
||||
type = FileType::Source;
|
||||
else if (mt == CppTools::Constants::C_HEADER_MIMETYPE
|
||||
|| mt == CppTools::Constants::CPP_HEADER_MIMETYPE)
|
||||
type = FileType::Header;
|
||||
else if (mt == ProjectExplorer::Constants::FORM_MIMETYPE)
|
||||
type = FileType::Form;
|
||||
else if (mt == ProjectExplorer::Constants::RESOURCE_MIMETYPE)
|
||||
type = FileType::Resource;
|
||||
else if (mt == ProjectExplorer::Constants::SCXML_MIMETYPE)
|
||||
type = FileType::StateChart;
|
||||
else if (mt == ProjectExplorer::Constants::QML_MIMETYPE)
|
||||
type = FileType::QML;
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
void TreeScanner::scanForFiles(FutureInterface *fi, const Utils::FileName& directory, const FileFilter &filter, const FileTypeFactory &factory)
|
||||
{
|
||||
std::unique_ptr<FutureInterface> fip(fi);
|
||||
fip->reportStarted();
|
||||
Utils::MimeDatabase mdb;
|
||||
|
||||
Result nodes
|
||||
= FileNode::scanForFiles(directory,
|
||||
[&mdb,&filter,&factory](const Utils::FileName &fn) -> FileNode * {
|
||||
QTC_ASSERT(!fn.isEmpty(), return nullptr);
|
||||
|
||||
const Utils::MimeType mimeType = mdb.mimeTypeForFile(fn.toString());
|
||||
|
||||
// Skip some files during scan.
|
||||
// Filter out nullptr records after.
|
||||
if (filter && filter(mimeType, fn))
|
||||
return nullptr;
|
||||
|
||||
// Type detection
|
||||
FileType type = FileType::Unknown;
|
||||
if (factory)
|
||||
type = factory(mimeType, fn);
|
||||
|
||||
return new FileNode(fn, type, false);
|
||||
},
|
||||
fip.get());
|
||||
|
||||
// Clean up nodes and keep it sorted
|
||||
Result tmp = Utils::filtered(nodes, [](const FileNode *fn) -> bool {
|
||||
// Simple skip null entries
|
||||
// TODO: fix Node::scanForFiles() to skip null factory results
|
||||
return fn;
|
||||
});
|
||||
Utils::sort(tmp, ProjectExplorer::Node::sortByPath);
|
||||
|
||||
fip->setProgressValue(fip->progressMaximum());
|
||||
fip->reportResult(tmp);
|
||||
fip->reportFinished();
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace CMakeProjectManager
|
||||
100
src/plugins/cmakeprojectmanager/treescanner.h
Normal file
100
src/plugins/cmakeprojectmanager/treescanner.h
Normal file
@@ -0,0 +1,100 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2016 Alexander Drozdov.
|
||||
** Contact: Alexander Drozdov (adrozdoff@gmail.com)
|
||||
**
|
||||
** 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 https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "projectexplorer/projectnodes.h"
|
||||
|
||||
#include <utils/mimetypes/mimedatabase.h>
|
||||
#include "utils/fileutils.h"
|
||||
|
||||
#include <QObject>
|
||||
#include <QFuture>
|
||||
#include <QFutureWatcher>
|
||||
|
||||
#include <functional>
|
||||
|
||||
namespace CMakeProjectManager {
|
||||
namespace Internal {
|
||||
|
||||
class TreeScanner : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
using Result = QList<ProjectExplorer::FileNode *>;
|
||||
using Future = QFuture<Result>;
|
||||
using FutureWatcher = QFutureWatcher<Result>;
|
||||
using FutureInterface = QFutureInterface<Result>;
|
||||
|
||||
using FileFilter = std::function<bool(const Utils::MimeType &, const Utils::FileName &)>;
|
||||
using FileTypeFactory = std::function<ProjectExplorer::FileType(const Utils::MimeType &, const Utils::FileName &)>;
|
||||
|
||||
explicit TreeScanner(QObject *parent = nullptr);
|
||||
|
||||
// Start scanning in given directory
|
||||
bool asyncScanForFiles(const Utils::FileName& directory);
|
||||
|
||||
// Setup filter for ignored files
|
||||
void setFilter(FileFilter filter);
|
||||
|
||||
// Setup factory for file types
|
||||
void setTypeFactory(FileTypeFactory factory);
|
||||
|
||||
Future future() const;
|
||||
bool isFinished() const;
|
||||
|
||||
// Takes not-owning result
|
||||
Result result() const;
|
||||
// Takes owning of result
|
||||
Result release();
|
||||
// Clear scan results
|
||||
void reset();
|
||||
|
||||
// Standard filters helpers
|
||||
static bool isWellKnownBinary(const Utils::MimeType &mimeType, const Utils::FileName &fn);
|
||||
static bool isMimeBinary(const Utils::MimeType &mimeType, const Utils::FileName &fn);
|
||||
|
||||
// Standard file factory
|
||||
static ProjectExplorer::FileType genericFileType(const Utils::MimeType &mdb, const Utils::FileName& fn);
|
||||
|
||||
signals:
|
||||
void finished();
|
||||
|
||||
private:
|
||||
static void scanForFiles(FutureInterface *fi, const Utils::FileName &directory, const FileFilter &filter, const FileTypeFactory &factory);
|
||||
|
||||
private:
|
||||
FileFilter m_filter;
|
||||
FileTypeFactory m_factory;
|
||||
|
||||
FutureWatcher m_futureWatcher;
|
||||
Future m_scanFuture;
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace CMakeProjectManager
|
||||
|
||||
|
||||
Reference in New Issue
Block a user