diff --git a/src/plugins/cmakeprojectmanager/builddirmanager.cpp b/src/plugins/cmakeprojectmanager/builddirmanager.cpp index 0a30e0d72e2..1064d308243 100644 --- a/src/plugins/cmakeprojectmanager/builddirmanager.cpp +++ b/src/plugins/cmakeprojectmanager/builddirmanager.cpp @@ -49,7 +49,6 @@ #include #include #include -#include #include #include #include @@ -97,26 +96,14 @@ static QStringList toArguments(const CMakeConfig &config, const ProjectExplorer: // -------------------------------------------------------------------- BuildDirManager::BuildDirManager(CMakeBuildConfiguration *bc) : - m_buildConfiguration(bc), - m_watcher(new QFileSystemWatcher(this)) + m_buildConfiguration(bc) { QTC_ASSERT(bc, return); m_projectName = sourceDirectory().fileName(); m_reparseTimer.setSingleShot(true); - m_reparseTimer.setInterval(5000); + m_reparseTimer.setInterval(2000); connect(&m_reparseTimer, &QTimer::timeout, this, &BuildDirManager::parse); - - connect(m_watcher, &QFileSystemWatcher::fileChanged, this, [this]() { - if (isParsing()) - return; - - const CMakeTool *tool = CMakeKitInformation::cmakeTool(m_buildConfiguration->target()->kit()); - if (!tool->isAutoRun()) - return; - - m_reparseTimer.start(); - }); } BuildDirManager::~BuildDirManager() @@ -163,6 +150,18 @@ bool BuildDirManager::isParsing() const return false; } +void BuildDirManager::cmakeFilesChanged() +{ + if (isParsing()) + return; + + const CMakeTool *tool = CMakeKitInformation::cmakeTool(m_buildConfiguration->target()->kit()); + if (!tool->isAutoRun()) + return; + + m_reparseTimer.start(); +} + void BuildDirManager::forceReparse() { if (m_buildConfiguration->target()->activeBuildConfiguration() != m_buildConfiguration) @@ -186,13 +185,8 @@ void BuildDirManager::resetData() m_cmakeCache.clear(); m_projectName.clear(); m_buildTargets.clear(); - m_watchedFiles.clear(); qDeleteAll(m_files); m_files.clear(); - - const QStringList watchedFiles = m_watcher->files(); - if (!watchedFiles.isEmpty()) - m_watcher->removePaths(watchedFiles); } bool BuildDirManager::persistCMakeState() @@ -229,8 +223,8 @@ void BuildDirManager::parse() return; } - const bool mustUpdate = m_watchedFiles.isEmpty() - || Utils::anyOf(m_watchedFiles, [&cbpFileFi](const Utils::FileName &f) { + const bool mustUpdate = m_cmakeFiles.isEmpty() + || Utils::anyOf(m_cmakeFiles, [&cbpFileFi](const Utils::FileName &f) { return f.toFileInfo().lastModified() > cbpFileFi.lastModified(); }); if (mustUpdate) { @@ -257,11 +251,6 @@ void BuildDirManager::clearCache() forceReparse(); } -bool BuildDirManager::isProjectFile(const Utils::FileName &fileName) const -{ - return m_watchedFiles.contains(fileName); -} - QString BuildDirManager::projectName() const { return m_projectName; @@ -277,6 +266,11 @@ QList BuildDirManager::files() return m_files; } +QSet BuildDirManager::cmakeFiles() +{ + return m_cmakeFiles; +} + void BuildDirManager::clearFiles() { m_files.clear(); @@ -361,15 +355,19 @@ void BuildDirManager::extractData() m_projectName = sourceDirectory().fileName(); m_files.append(new ProjectExplorer::FileNode(topCMake, ProjectExplorer::ProjectFileType, false)); - m_watchedFiles.insert(topCMake); + // Do not insert topCMake into m_cmakeFiles: The project already watches that! // Find cbp file QString cbpFile = CMakeManager::findCbpFile(workDirectory().toString()); if (cbpFile.isEmpty()) return; + m_cmakeFiles.insert(Utils::FileName::fromString(cbpFile)); - m_watcher->addPath(cbpFile); - m_watcher->addPath(workDirectory().toString() + QLatin1String("/CMakeCache.txt")); + // Add CMakeCache.txt file: + Utils::FileName cacheFile = workDirectory(); + cacheFile.appendPath(QLatin1String("CMakeCache.txt")); + if (cacheFile.toFileInfo().exists()) + m_cmakeFiles.insert(cacheFile); // setFolderName CMakeCbpParser cbpparser; @@ -380,21 +378,14 @@ void BuildDirManager::extractData() m_projectName = cbpparser.projectName(); m_files = cbpparser.fileList(); - QSet projectFiles; if (cbpparser.hasCMakeFiles()) { m_files.append(cbpparser.cmakeFileList()); foreach (const ProjectExplorer::FileNode *node, cbpparser.cmakeFileList()) - projectFiles.insert(node->filePath()); + m_cmakeFiles.insert(node->filePath()); } else { m_files.append(new ProjectExplorer::FileNode(topCMake, ProjectExplorer::ProjectFileType, false)); - projectFiles.insert(topCMake); } - m_watchedFiles = projectFiles; - const QStringList toWatch - = Utils::transform(m_watchedFiles.toList(), [](const Utils::FileName &fn) { return fn.toString(); }); - m_watcher->addPaths(toWatch); - m_buildTargets = cbpparser.buildTargets(); } diff --git a/src/plugins/cmakeprojectmanager/builddirmanager.h b/src/plugins/cmakeprojectmanager/builddirmanager.h index 419a87c3d15..d830e8a8595 100644 --- a/src/plugins/cmakeprojectmanager/builddirmanager.h +++ b/src/plugins/cmakeprojectmanager/builddirmanager.h @@ -73,6 +73,8 @@ public: const CMakeConfig intendedConfiguration() const; bool isParsing() const; + void cmakeFilesChanged(); + void parse(); void clearCache(); void forceReparse(); @@ -80,10 +82,10 @@ public: void resetData(); bool persistCMakeState(); - bool isProjectFile(const Utils::FileName &fileName) const; QString projectName() const; QList buildTargets() const; QList files(); + QSet cmakeFiles(); void clearFiles(); CMakeConfig parsedConfiguration() const; @@ -115,10 +117,9 @@ private: QTemporaryDir *m_tempDir = nullptr; mutable CMakeConfig m_cmakeCache; - QSet m_watchedFiles; + QSet m_cmakeFiles; QString m_projectName; QList m_buildTargets; - QFileSystemWatcher *m_watcher; QList m_files; // For error reporting: diff --git a/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp b/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp index 8d9d46e4ba0..a013083c572 100644 --- a/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp +++ b/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp @@ -71,6 +71,11 @@ CMakeBuildConfiguration::~CMakeBuildConfiguration() m_buildDirManager->deleteLater(); // Do not block while waiting for cmake... } +void CMakeBuildConfiguration::cmakeFilesChanged() +{ + m_buildDirManager->cmakeFilesChanged(); +} + bool CMakeBuildConfiguration::isEnabled() const { return m_error.isEmpty(); diff --git a/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.h b/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.h index 37ec1aa4275..d56965ffb6c 100644 --- a/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.h +++ b/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.h @@ -52,6 +52,8 @@ public: CMakeBuildConfiguration(ProjectExplorer::Target *parent); ~CMakeBuildConfiguration(); + void cmakeFilesChanged(); + bool isEnabled() const override; QString disabledReason() const override; diff --git a/src/plugins/cmakeprojectmanager/cmakefile.cpp b/src/plugins/cmakeprojectmanager/cmakefile.cpp index 0d338840166..30a1db0e153 100644 --- a/src/plugins/cmakeprojectmanager/cmakefile.cpp +++ b/src/plugins/cmakeprojectmanager/cmakefile.cpp @@ -27,6 +27,8 @@ #include "cmakeproject.h" #include "cmakeprojectconstants.h" +#include + #include using namespace Utils; @@ -34,7 +36,7 @@ using namespace Utils; namespace CMakeProjectManager { namespace Internal { -CMakeFile::CMakeFile(const FileName &fileName) +CMakeFile::CMakeFile(CMakeProject *project, const FileName &fileName) : m_project(project) { setId("Cmake.ProjectFile"); setMimeType(QLatin1String(Constants::CMAKEPROJECTMIMETYPE)); @@ -48,5 +50,15 @@ Core::IDocument::ReloadBehavior CMakeFile::reloadBehavior(ChangeTrigger state, C return BehaviorSilent; } +bool CMakeFile::reload(QString *errorString, Core::IDocument::ReloadFlag flag, Core::IDocument::ChangeType type) +{ + Q_UNUSED(errorString); + Q_UNUSED(flag); + + if (type != TypePermissions) + m_project->handleCmakeFileChanged(); + return true; +} + } // namespace Internal } // namespace CMakeProjectManager diff --git a/src/plugins/cmakeprojectmanager/cmakefile.h b/src/plugins/cmakeprojectmanager/cmakefile.h index e5018ce7315..606012ea244 100644 --- a/src/plugins/cmakeprojectmanager/cmakefile.h +++ b/src/plugins/cmakeprojectmanager/cmakefile.h @@ -35,9 +35,13 @@ namespace Internal { class CMakeFile : public Core::IDocument { public: - CMakeFile(const Utils::FileName &fileName); + CMakeFile(CMakeProject *project, const Utils::FileName &fileName); ReloadBehavior reloadBehavior(ChangeTrigger state, ChangeType type) const override; + bool reload(QString *errorString, ReloadFlag flag, ChangeType type) override; + +private: + CMakeProject *m_project; }; } // namespace Internal diff --git a/src/plugins/cmakeprojectmanager/cmakeproject.cpp b/src/plugins/cmakeprojectmanager/cmakeproject.cpp index e7c78ab01df..bb09ec627a5 100644 --- a/src/plugins/cmakeprojectmanager/cmakeproject.cpp +++ b/src/plugins/cmakeprojectmanager/cmakeproject.cpp @@ -36,6 +36,7 @@ #include "cmakeprojectmanager.h" #include +#include #include #include #include @@ -88,11 +89,13 @@ CMakeProject::CMakeProject(CMakeManager *manager, const FileName &fileName) { setId(Constants::CMAKEPROJECT_ID); setProjectManager(manager); - setDocument(new CMakeFile(fileName)); + setDocument(new Internal::CMakeFile(this, fileName)); + setRootProjectNode(new CMakeProjectNode(fileName)); setProjectContext(Core::Context(CMakeProjectManager::Constants::PROJECTCONTEXT)); setProjectLanguages(Core::Context(ProjectExplorer::Constants::LANG_CXX)); + Core::DocumentManager::addDocument(document()); rootProjectNode()->setDisplayName(fileName.parentDir().fileName()); connect(this, &CMakeProject::activeTargetChanged, this, &CMakeProject::handleActiveTargetChanged); @@ -217,6 +220,29 @@ void CMakeProject::parseCMakeOutput() rootProjectNode()->setDisplayName(bdm->projectName()); + // Delete no longer necessary file watcher: + const QSet currentWatched + = Utils::transform(m_watchedFiles, [](CMakeFile *cmf) { return cmf->filePath(); }); + const QSet toWatch = bdm->cmakeFiles(); + QSet toDelete = currentWatched; + toDelete.subtract(toWatch); + m_watchedFiles = Utils::filtered(m_watchedFiles, [&toDelete](Internal::CMakeFile *cmf) { + if (toDelete.contains(cmf->filePath())) { + delete cmf; + return false; + } + return true; + }); + + // Add new file watchers: + QSet toAdd = toWatch; + toAdd.subtract(currentWatched); + foreach (const Utils::FileName &fn, toAdd) { + CMakeFile *cm = new CMakeFile(this, fn); + Core::DocumentManager::addDocument(cm); + m_watchedFiles.insert(cm); + } + buildTree(static_cast(rootProjectNode()), bdm->files()); bdm->clearFiles(); // Some of the FileNodes in files() were deleted! @@ -512,6 +538,15 @@ bool CMakeProject::setupTarget(Target *t) return true; } +void CMakeProject::handleCmakeFileChanged() +{ + if (Target *t = activeTarget()) { + if (auto bc = qobject_cast(t->activeBuildConfiguration())) { + bc->cmakeFilesChanged(); + } + } +} + void CMakeProject::handleActiveTargetChanged() { if (m_connectedTarget) { diff --git a/src/plugins/cmakeprojectmanager/cmakeproject.h b/src/plugins/cmakeprojectmanager/cmakeproject.h index 38126f5f9aa..27e07d8f073 100644 --- a/src/plugins/cmakeprojectmanager/cmakeproject.h +++ b/src/plugins/cmakeprojectmanager/cmakeproject.h @@ -120,6 +120,8 @@ protected: bool setupTarget(ProjectExplorer::Target *t) override; private: + void handleCmakeFileChanged(); + void handleActiveTargetChanged(); void handleActiveBuildConfigurationChanged(); void handleParsingStarted(); @@ -144,7 +146,10 @@ private: QFuture m_codeModelFuture; QList m_extraCompilers; + QSet m_watchedFiles; + friend class Internal::CMakeBuildConfiguration; + friend class Internal::CMakeFile; }; } // namespace CMakeProjectManager