From 4aee38e04c5d30dc90300300443a6357262fc6a5 Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Fri, 26 Jul 2019 13:57:24 +0200 Subject: [PATCH] CMake: Extract and show more CMake location information for targets Report more cmake file locations that are relevant to a target in the "Open..." menu entry in the target's context menu. This information is extracted from the backtrace information that is provided by fileapi. Change-Id: I01659a6cc7254cd0ef6b533a0785d2f15d31c3c6 Reviewed-by: Alexandru Croitor Reviewed-by: hjk --- .../cmakeprojectmanager/builddirmanager.cpp | 6 +- .../cmakeprojectmanager/cmakebuildtarget.h | 13 +- .../cmakelocatorfilter.cpp | 9 +- .../fileapidataextractor.cpp | 163 ++++++++++-------- 4 files changed, 115 insertions(+), 76 deletions(-) diff --git a/src/plugins/cmakeprojectmanager/builddirmanager.cpp b/src/plugins/cmakeprojectmanager/builddirmanager.cpp index 90d29576093..f029d9cdab9 100644 --- a/src/plugins/cmakeprojectmanager/builddirmanager.cpp +++ b/src/plugins/cmakeprojectmanager/builddirmanager.cpp @@ -368,8 +368,10 @@ QList BuildDirManager::takeBuildTargets(QString &errorMessage) // Guess at the target definition position when no details are known for (CMakeBuildTarget &t : readerTargets) { - if (t.definitionFile.isEmpty()) { - t.definitionFile = t.sourceDirectory.pathAppended("CMakeLists.txt"); + if (t.backtrace.isEmpty()) { + t.backtrace.append( + FolderNode::LocationInfo(tr("CMakeLists.txt in source directory"), + t.sourceDirectory.pathAppended("CMakeLists.txt"))); } } result.append(readerTargets); diff --git a/src/plugins/cmakeprojectmanager/cmakebuildtarget.h b/src/plugins/cmakeprojectmanager/cmakebuildtarget.h index ad941221289..98127f20b36 100644 --- a/src/plugins/cmakeprojectmanager/cmakebuildtarget.h +++ b/src/plugins/cmakeprojectmanager/cmakebuildtarget.h @@ -28,6 +28,7 @@ #include "cmake_global.h" #include +#include #include @@ -43,6 +44,9 @@ enum TargetType { UtilityType }; +using Backtrace = QVector; +using Backtraces = QVector; + class CMAKE_EXPORT CMakeBuildTarget { public: @@ -53,8 +57,13 @@ public: Utils::FilePath sourceDirectory; Utils::FilePath makeCommand; - Utils::FilePath definitionFile; - int definitionLine = -1; + Backtrace backtrace; + + Backtraces dependencyDefinitions; + Backtraces sourceDefinitions; + Backtraces defineDefinitions; + Backtraces includeDefinitions; + Backtraces installDefinitions; // code model QList includeFiles; diff --git a/src/plugins/cmakeprojectmanager/cmakelocatorfilter.cpp b/src/plugins/cmakeprojectmanager/cmakelocatorfilter.cpp index cfaaefe1789..4745dc4cfa3 100644 --- a/src/plugins/cmakeprojectmanager/cmakelocatorfilter.cpp +++ b/src/plugins/cmakeprojectmanager/cmakelocatorfilter.cpp @@ -75,12 +75,13 @@ void CMakeTargetLocatorFilter::prepareSearch(const QString &entry) for (const CMakeBuildTarget &target : buildTargets) { const int index = target.title.indexOf(entry); if (index >= 0) { - const FilePath path = target.definitionFile.isEmpty() - ? cmakeProject->projectFilePath() - : target.definitionFile; + const FilePath path = target.backtrace.isEmpty() ? cmakeProject->projectFilePath() + : target.backtrace.first().path; + const int line = target.backtrace.isEmpty() ? -1 : target.backtrace.first().line; + QVariantMap extraData; extraData.insert("project", cmakeProject->projectFilePath().toString()); - extraData.insert("line", target.definitionLine); + extraData.insert("line", line); extraData.insert("file", path.toString()); Core::LocatorFilterEntry filterEntry(this, target.title, extraData); diff --git a/src/plugins/cmakeprojectmanager/fileapidataextractor.cpp b/src/plugins/cmakeprojectmanager/fileapidataextractor.cpp index 82505de6442..fa6b5729b7a 100644 --- a/src/plugins/cmakeprojectmanager/fileapidataextractor.cpp +++ b/src/plugins/cmakeprojectmanager/fileapidataextractor.cpp @@ -165,6 +165,39 @@ PreprocessedData preprocess(FileApiData &data, return result; } +QVector extractBacktraceInformation(const BacktraceInfo &backtraces, + const QDir &sourceDir, + int backtraceIndex, + unsigned int locationInfoPriority) +{ + QVector info; + // Set up a default target path: + while (backtraceIndex != -1) { + const size_t bi = static_cast(backtraceIndex); + QTC_ASSERT(bi < backtraces.nodes.size(), break); + const BacktraceNode &btNode = backtraces.nodes[bi]; + backtraceIndex = btNode.parent; // advance to next node + + const size_t fileIndex = static_cast(btNode.file); + QTC_ASSERT(fileIndex < backtraces.files.size(), break); + const FilePath path = FilePath::fromString( + sourceDir.absoluteFilePath(backtraces.files[fileIndex])); + + if (btNode.command < 0) { + // No command, skip: The file itself is already covered:-) + continue; + } + + const size_t commandIndex = static_cast(btNode.command); + QTC_ASSERT(commandIndex < backtraces.commands.size(), break); + + const QString command = backtraces.commands[commandIndex]; + + info.append(FolderNode::LocationInfo(command, path, btNode.line, locationInfoPriority)); + } + return info; +} + QList generateBuildTargets(const PreprocessedData &input, const FilePath &sourceDirectory, const FilePath &buildDirectory) @@ -198,15 +231,29 @@ QList generateBuildTargets(const PreprocessedData &input, ct.sourceDirectory = FilePath::fromString( QDir::cleanPath(sourceDir.absoluteFilePath(t.sourceDir.toString()))); - if (t.backtrace >= 0) { - const BacktraceNode &node = t.backtraceGraph.nodes[static_cast(t.backtrace)]; - const int fileIndex = node.file; - if (fileIndex >= 0) { - ct.definitionFile = FilePath::fromString( - QDir::cleanPath(sourceDir.absoluteFilePath( - t.backtraceGraph.files[static_cast(fileIndex)]))); - ct.definitionLine = node.line; + ct.backtrace = extractBacktraceInformation(t.backtraceGraph, sourceDir, t.backtrace, 0); + + for (const DependencyInfo &d : t.dependencies) { + ct.dependencyDefinitions.append( + extractBacktraceInformation(t.backtraceGraph, sourceDir, d.backtrace, 100)); + } + for (const SourceInfo &si : t.sources) { + ct.sourceDefinitions.append( + extractBacktraceInformation(t.backtraceGraph, sourceDir, si.backtrace, 200)); + } + for (const CompileInfo &ci : t.compileGroups) { + for (const IncludeInfo &ii : ci.includes) { + ct.includeDefinitions.append( + extractBacktraceInformation(t.backtraceGraph, sourceDir, ii.backtrace, 300)); } + for (const DefineInfo &di : ci.defines) { + ct.defineDefinitions.append( + extractBacktraceInformation(t.backtraceGraph, sourceDir, di.backtrace, 400)); + } + } + for (const InstallDestination &id : t.installDestination) { + ct.includeDefinitions.append( + extractBacktraceInformation(t.backtraceGraph, sourceDir, id.backtrace, 500)); } return ct; @@ -309,64 +356,6 @@ void addProjects(const QHash &cmakeListsNodes, } } -void addBacktraceInformation(FolderNode *node, - const BacktraceInfo &backtraces, - const QDir &sourceDir, - int backtraceIndex) -{ - QVector info; - // Set up a default target path: - FilePath targetPath = node->filePath().pathAppended("CMakeLists.txt"); - while (backtraceIndex != -1) { - const size_t bi = static_cast(backtraceIndex); - QTC_ASSERT(bi < backtraces.nodes.size(), break); - const BacktraceNode &btNode = backtraces.nodes[bi]; - backtraceIndex = btNode.parent; // advance to next node - - const size_t fileIndex = static_cast(btNode.file); - QTC_ASSERT(fileIndex < backtraces.files.size(), break); - const FilePath path = FilePath::fromString( - sourceDir.absoluteFilePath(backtraces.files[fileIndex])); - - if (btNode.command < 0) { - // No command, skip: The file itself is already covered:-) - continue; - } - - const size_t commandIndex = static_cast(btNode.command); - QTC_ASSERT(commandIndex < backtraces.commands.size(), break); - - const QString command = backtraces.commands[commandIndex]; - - QString dn; - if (path == targetPath) { - if (btNode.line > 0) { - dn = QCoreApplication::translate("CMakeProjectManager::Internal::FileApiReader", - "%1 in line %2") - .arg(command) - .arg(btNode.line); - } else { - dn = command; - } - } else { - if (btNode.line > 0) { - dn = QCoreApplication::translate("CMakeProjectManager::Internal::FileApiReader", - "%1 in %2:%3") - .arg(command) - .arg(path.toUserOutput()) - .arg(btNode.line); - } else { - dn = QCoreApplication::translate("CMakeProjectManager::Internal::FileApiReader", - "%1 in %2") - .arg(command) - .arg(path.toUserOutput()); - } - } - info.append(FolderNode::LocationInfo(dn, path, btNode.line)); - } - node->setLocationInfo(info); -} - QVector addSourceGroups(ProjectNode *targetRoot, const TargetDetails &td, const FilePath &sourceDirectory) @@ -479,8 +468,6 @@ void addTargets(const QHash &cm tNode->setTargetInformation(td.artifacts, td.type); tNode->setBuildDirectory(directoryBuildDir(config, buildDir, t.directory)); - addBacktraceInformation(tNode, td.backtraceGraph, sourceDir, td.backtrace); - addCompileGroups(tNode, topSourceDir, dir, tNode->buildDirectory(), td, knownHeaderNodes); } } @@ -534,6 +521,44 @@ std::pair, QSet> generateRootProject return result; } +void setupLocationInfoForTargets(CMakeProjectNode *rootNode, const QList &targets) +{ + for (const CMakeBuildTarget &t : targets) { + FolderNode *folderNode = static_cast( + rootNode->findNode(Utils::equal(&Node::buildKey, t.title))); + if (folderNode) { + QSet> locations; + auto dedup = [&locations](const Backtrace &bt) { + QVector result; + for (const FolderNode::LocationInfo &i : bt) { + int count = locations.count(); + locations.insert(std::make_pair(i.path, i.line)); + if (count != locations.count()) { + result.append(i); + } + } + return result; + }; + + QVector result = dedup(t.backtrace); + auto dedupMulti = [&dedup](const Backtraces &bts) { + QVector result; + for (const Backtrace &bt : bts) { + result.append(dedup(bt)); + } + return result; + }; + result += dedupMulti(t.dependencyDefinitions); + result += dedupMulti(t.includeDefinitions); + result += dedupMulti(t.defineDefinitions); + result += dedupMulti(t.sourceDefinitions); + result += dedupMulti(t.installDefinitions); + + folderNode->setLocationInfo(result); + } + } +} + } // namespace namespace CMakeProjectManager { @@ -566,6 +591,8 @@ FileApiQtcData extractData(FileApiData &input, result.rootProjectNode = std::move(pair.first); result.knownHeaders = std::move(pair.second); + setupLocationInfoForTargets(result.rootProjectNode.get(), result.buildTargets); + return result; }