summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTobias Hunger <tobias.hunger@qt.io>2019-06-13 14:24:04 +0200
committerTobias Hunger <tobias.hunger@qt.io>2019-06-20 12:25:36 +0000
commita95eb53d3ba880efd880616a2725ac7657dffea2 (patch)
tree7c3357c6e5a519adb64ee76676d7e16ae0126304
parentf02fcaf02c4a023efc102fae230943a0c0f1458f (diff)
downloadqt-creator-a95eb53d3ba880efd880616a2725ac7657dffea2.tar.gz
CMake: Add initial fileapireader class
Change-Id: I620cba7cc1c2a5ac56789fa9770dce573c6b19cd Reviewed-by: Eike Ziller <eike.ziller@qt.io>
-rw-r--r--src/plugins/cmakeprojectmanager/CMakeLists.txt3
-rw-r--r--src/plugins/cmakeprojectmanager/builddirmanager.cpp11
-rw-r--r--src/plugins/cmakeprojectmanager/builddirmanager.h3
-rw-r--r--src/plugins/cmakeprojectmanager/builddirreader.cpp3
-rw-r--r--src/plugins/cmakeprojectmanager/builddirreader.h8
-rw-r--r--src/plugins/cmakeprojectmanager/cmakeproject.cpp3
-rw-r--r--src/plugins/cmakeprojectmanager/cmakeprojectmanager.pro6
-rw-r--r--src/plugins/cmakeprojectmanager/cmakeprojectmanager.qbs6
-rw-r--r--src/plugins/cmakeprojectmanager/fileapidataextractor.cpp558
-rw-r--r--src/plugins/cmakeprojectmanager/fileapidataextractor.h58
-rw-r--r--src/plugins/cmakeprojectmanager/fileapiparser.cpp947
-rw-r--r--src/plugins/cmakeprojectmanager/fileapiparser.h269
-rw-r--r--src/plugins/cmakeprojectmanager/fileapireader.cpp283
-rw-r--r--src/plugins/cmakeprojectmanager/fileapireader.h96
-rw-r--r--src/plugins/cmakeprojectmanager/projecttreehelper.cpp16
-rw-r--r--src/plugins/cmakeprojectmanager/projecttreehelper.h6
-rw-r--r--src/plugins/cmakeprojectmanager/servermodereader.cpp35
-rw-r--r--src/plugins/cmakeprojectmanager/servermodereader.h16
-rw-r--r--src/plugins/cmakeprojectmanager/tealeafreader.cpp12
-rw-r--r--src/plugins/cmakeprojectmanager/tealeafreader.h7
20 files changed, 2296 insertions, 50 deletions
diff --git a/src/plugins/cmakeprojectmanager/CMakeLists.txt b/src/plugins/cmakeprojectmanager/CMakeLists.txt
index 8a5abb739b..93113b643b 100644
--- a/src/plugins/cmakeprojectmanager/CMakeLists.txt
+++ b/src/plugins/cmakeprojectmanager/CMakeLists.txt
@@ -37,6 +37,9 @@ add_qtc_plugin(CMakeProjectManager
cmaketoolsettingsaccessor.cpp cmaketoolsettingsaccessor.h
configmodel.cpp configmodel.h
configmodelitemdelegate.cpp configmodelitemdelegate.h
+ fileapidataextractor.cpp fileapidataextractor.h
+ fileapiparser.cpp fileapiparser.h
+ fileapireader.cpp fileapireader.h
projecttreehelper.cpp projecttreehelper.h
servermode.cpp servermode.h
servermodereader.cpp servermodereader.h
diff --git a/src/plugins/cmakeprojectmanager/builddirmanager.cpp b/src/plugins/cmakeprojectmanager/builddirmanager.cpp
index 4c06e4fddd..da2dcc5458 100644
--- a/src/plugins/cmakeprojectmanager/builddirmanager.cpp
+++ b/src/plugins/cmakeprojectmanager/builddirmanager.cpp
@@ -302,14 +302,13 @@ void BuildDirManager::parse(int reparseParameters)
reparseParameters & REPARSE_FORCE_CONFIGURATION);
}
-void BuildDirManager::generateProjectTree(CMakeProjectNode *root,
- const QList<const FileNode *> &allFiles,
- QString &errorMessage) const
+std::unique_ptr<CMakeProjectNode> BuildDirManager::generateProjectTree(
+ const QList<const FileNode *> &allFiles, QString &errorMessage) const
{
- QTC_ASSERT(!m_isHandlingError, return);
- QTC_ASSERT(m_reader, return);
+ QTC_ASSERT(!m_isHandlingError, return {});
+ QTC_ASSERT(m_reader, return {});
- m_reader->generateProjectTree(root, allFiles, errorMessage);
+ return m_reader->generateProjectTree(allFiles, errorMessage);
}
CppTools::RawProjectParts BuildDirManager::createRawProjectParts(QString &errorMessage) const
diff --git a/src/plugins/cmakeprojectmanager/builddirmanager.h b/src/plugins/cmakeprojectmanager/builddirmanager.h
index 0b364c764a..a7dca6db9a 100644
--- a/src/plugins/cmakeprojectmanager/builddirmanager.h
+++ b/src/plugins/cmakeprojectmanager/builddirmanager.h
@@ -78,8 +78,7 @@ public:
void parse(int reparseParameters);
- void generateProjectTree(CMakeProjectNode *root,
- const QList<const ProjectExplorer::FileNode *> &allFiles,
+ std::unique_ptr<CMakeProjectNode> generateProjectTree(const QList<const ProjectExplorer::FileNode *> &allFiles,
QString &errorMessage) const;
CppTools::RawProjectParts createRawProjectParts(QString &errorMessage) const;
diff --git a/src/plugins/cmakeprojectmanager/builddirreader.cpp b/src/plugins/cmakeprojectmanager/builddirreader.cpp
index c0dea25350..6c71218ec7 100644
--- a/src/plugins/cmakeprojectmanager/builddirreader.cpp
+++ b/src/plugins/cmakeprojectmanager/builddirreader.cpp
@@ -25,6 +25,7 @@
#include "builddirreader.h"
+#include "fileapireader.h"
#include "servermodereader.h"
#include "tealeafreader.h"
@@ -43,6 +44,8 @@ std::unique_ptr<BuildDirReader> BuildDirReader::createReader(const BuildDirParam
{
CMakeTool *cmake = p.cmakeTool();
QTC_ASSERT(p.isValid() && cmake, return {});
+ if (cmake->hasFileApi())
+ return std::make_unique<FileApiReader>();
if (cmake->hasServerMode())
return std::make_unique<ServerModeReader>();
return std::make_unique<TeaLeafReader>();
diff --git a/src/plugins/cmakeprojectmanager/builddirreader.h b/src/plugins/cmakeprojectmanager/builddirreader.h
index 04f243df79..755e7ed095 100644
--- a/src/plugins/cmakeprojectmanager/builddirreader.h
+++ b/src/plugins/cmakeprojectmanager/builddirreader.h
@@ -64,10 +64,10 @@ public:
virtual QList<CMakeBuildTarget> takeBuildTargets(QString &errorMessage) = 0;
virtual CMakeConfig takeParsedConfiguration(QString &errorMessage) = 0;
- virtual void generateProjectTree(CMakeProjectNode *root,
- const QList<const ProjectExplorer::FileNode *> &allFiles,
- QString &errorMessage) = 0;
- virtual CppTools::RawProjectParts createRawProjectParts(QString &errorMessage) const = 0;
+ virtual std::unique_ptr<CMakeProjectNode> generateProjectTree(
+ const QList<const ProjectExplorer::FileNode *> &allFiles, QString &errorMessage)
+ = 0;
+ virtual CppTools::RawProjectParts createRawProjectParts(QString &errorMessage) = 0;
signals:
void isReadyNow() const;
diff --git a/src/plugins/cmakeprojectmanager/cmakeproject.cpp b/src/plugins/cmakeprojectmanager/cmakeproject.cpp
index 34d546335e..6c6b9f4c37 100644
--- a/src/plugins/cmakeprojectmanager/cmakeproject.cpp
+++ b/src/plugins/cmakeprojectmanager/cmakeproject.cpp
@@ -401,9 +401,8 @@ CMakeProject::generateProjectTree(const QList<const FileNode *> &allFiles) const
if (m_buildDirManager.isParsing())
return nullptr;
- auto root = std::make_unique<CMakeProjectNode>(projectDirectory());
QString errorMessage;
- m_buildDirManager.generateProjectTree(root.get(), allFiles, errorMessage);
+ auto root = m_buildDirManager.generateProjectTree(allFiles, errorMessage);
checkAndReportError(errorMessage);
return root;
}
diff --git a/src/plugins/cmakeprojectmanager/cmakeprojectmanager.pro b/src/plugins/cmakeprojectmanager/cmakeprojectmanager.pro
index 07ee7f3555..6407308ad9 100644
--- a/src/plugins/cmakeprojectmanager/cmakeprojectmanager.pro
+++ b/src/plugins/cmakeprojectmanager/cmakeprojectmanager.pro
@@ -34,6 +34,9 @@ HEADERS = builddirmanager.h \
cmakespecificsettingspage.h \
configmodel.h \
configmodelitemdelegate.h \
+ fileapidataextractor.h \
+ fileapiparser.h \
+ fileapireader.h \
projecttreehelper.h \
servermode.h \
servermodereader.h \
@@ -69,6 +72,9 @@ SOURCES = builddirmanager.cpp \
cmakespecificsettingspage.cpp \
configmodel.cpp \
configmodelitemdelegate.cpp \
+ fileapidataextractor.cpp \
+ fileapiparser.cpp \
+ fileapireader.cpp \
projecttreehelper.cpp \
servermode.cpp \
servermodereader.cpp \
diff --git a/src/plugins/cmakeprojectmanager/cmakeprojectmanager.qbs b/src/plugins/cmakeprojectmanager/cmakeprojectmanager.qbs
index c2352684b0..68f8fc47d2 100644
--- a/src/plugins/cmakeprojectmanager/cmakeprojectmanager.qbs
+++ b/src/plugins/cmakeprojectmanager/cmakeprojectmanager.qbs
@@ -84,6 +84,12 @@ QtcPlugin {
"configmodel.h",
"configmodelitemdelegate.cpp",
"configmodelitemdelegate.h",
+ "fileapidataextractor.cpp",
+ "fileapidataextractor.h",
+ "fileapiparser.cpp",
+ "fileapiparser.h",
+ "fileapireader.cpp",
+ "fileapireader.h",
"projecttreehelper.cpp",
"projecttreehelper.h",
"servermode.cpp",
diff --git a/src/plugins/cmakeprojectmanager/fileapidataextractor.cpp b/src/plugins/cmakeprojectmanager/fileapidataextractor.cpp
new file mode 100644
index 0000000000..aa881e856e
--- /dev/null
+++ b/src/plugins/cmakeprojectmanager/fileapidataextractor.cpp
@@ -0,0 +1,558 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** 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 "fileapidataextractor.h"
+
+#include "cmakeprojectnodes.h"
+#include "projecttreehelper.h"
+
+#include <projectexplorer/projectnodes.h>
+
+#include <utils/algorithm.h>
+#include <utils/qtcassert.h>
+#include <utils/qtcprocess.h>
+
+#include <QDir>
+
+using namespace ProjectExplorer;
+using namespace Utils;
+
+namespace {
+
+using namespace CMakeProjectManager;
+using namespace CMakeProjectManager::Internal;
+using namespace CMakeProjectManager::Internal::FileApiDetails;
+
+// --------------------------------------------------------------------
+// Helpers:
+// --------------------------------------------------------------------
+
+class CMakeFileResult
+{
+public:
+ QSet<FilePath> cmakeFiles;
+
+ std::vector<std::unique_ptr<ProjectExplorer::FileNode>> cmakeNodesSource;
+ std::vector<std::unique_ptr<ProjectExplorer::FileNode>> cmakeNodesBuild;
+ std::vector<std::unique_ptr<ProjectExplorer::FileNode>> cmakeNodesOther;
+ std::vector<std::unique_ptr<ProjectExplorer::FileNode>> cmakeListNodes;
+};
+
+CMakeFileResult extractCMakeFilesData(const std::vector<FileApiDetails::CMakeFileInfo> &cmakefiles,
+ const FilePath &sourceDirectory,
+ const FilePath &buildDirectory)
+{
+ CMakeFileResult result;
+
+ QDir sourceDir(sourceDirectory.toString());
+ QDir buildDir(buildDirectory.toString());
+
+ for (const CMakeFileInfo &info : cmakefiles) {
+ const FilePath sfn = FilePath::fromString(
+ QDir::cleanPath(sourceDir.absoluteFilePath(info.path)));
+ const int oldCount = result.cmakeFiles.count();
+ result.cmakeFiles.insert(sfn);
+ if (oldCount < result.cmakeFiles.count()) {
+ if (info.isCMake && !info.isCMakeListsDotTxt) {
+ // Skip files that cmake considers to be part of the installation -- but include
+ // CMakeLists.txt files. This unbreaks cmake binaries running from their own
+ // build directory.
+ continue;
+ }
+
+ auto node = std::make_unique<FileNode>(sfn, FileType::Project);
+ node->setIsGenerated(info.isGenerated
+ && !info.isCMakeListsDotTxt); // CMakeLists.txt are never
+ // generated, independent
+ // what cmake thinks:-)
+
+ if (info.isCMakeListsDotTxt) {
+ result.cmakeListNodes.emplace_back(std::move(node));
+ } else if (sfn.isChildOf(sourceDir)) {
+ result.cmakeNodesSource.emplace_back(std::move(node));
+ } else if (sfn.isChildOf(buildDir)) {
+ result.cmakeNodesBuild.emplace_back(std::move(node));
+ } else {
+ result.cmakeNodesOther.emplace_back(std::move(node));
+ }
+ }
+ }
+
+ return result;
+}
+
+Configuration extractConfiguration(std::vector<Configuration> &codemodel, QString &errorMessage)
+{
+ if (codemodel.size() == 0) {
+ qWarning() << "No configuration found!";
+ errorMessage = "No configuration found!";
+ return {};
+ }
+ if (codemodel.size() > 1)
+ qWarning() << "Multi-configuration generator found, ignoring all but first configuration";
+
+ Configuration result = std::move(codemodel[0]);
+ codemodel.clear();
+
+ return result;
+}
+
+class PreprocessedData
+{
+public:
+ CMakeProjectManager::CMakeConfig cache;
+
+ QSet<FilePath> cmakeFiles;
+
+ std::vector<std::unique_ptr<ProjectExplorer::FileNode>> cmakeNodesSource;
+ std::vector<std::unique_ptr<ProjectExplorer::FileNode>> cmakeNodesBuild;
+ std::vector<std::unique_ptr<ProjectExplorer::FileNode>> cmakeNodesOther;
+ std::vector<std::unique_ptr<ProjectExplorer::FileNode>> cmakeListNodes;
+
+ Configuration codemodel;
+ std::vector<TargetDetails> targetDetails;
+};
+
+PreprocessedData preprocess(FileApiData &data,
+ const FilePath &sourceDirectory,
+ const FilePath &buildDirectory,
+ QString &errorMessage)
+{
+ PreprocessedData result;
+
+ result.cache = std::move(data.cache); // Make sure this is available, even when nothing else is
+
+ // Simplify to only one configuration:
+ result.codemodel = extractConfiguration(data.codemodel, errorMessage);
+ if (!errorMessage.isEmpty()) {
+ return result;
+ }
+
+ CMakeFileResult cmakeFileResult = extractCMakeFilesData(data.cmakeFiles,
+ sourceDirectory,
+ buildDirectory);
+
+ result.cmakeFiles = std::move(cmakeFileResult.cmakeFiles);
+ result.cmakeNodesSource = std::move(cmakeFileResult.cmakeNodesSource);
+ result.cmakeNodesBuild = std::move(cmakeFileResult.cmakeNodesBuild);
+ result.cmakeNodesOther = std::move(cmakeFileResult.cmakeNodesOther);
+ result.cmakeListNodes = std::move(cmakeFileResult.cmakeListNodes);
+
+ result.targetDetails = std::move(data.targetDetails);
+
+ return result;
+}
+
+QList<CMakeBuildTarget> generateBuildTargets(const PreprocessedData &input,
+ const FilePath &sourceDirectory)
+{
+ QDir sourceDir(sourceDirectory.toString());
+ const QList<CMakeBuildTarget> result = transform<
+ QList>(input.targetDetails, [&sourceDir](const TargetDetails &t) -> CMakeBuildTarget {
+ CMakeBuildTarget ct;
+ ct.title = t.name;
+ ct.executable = t.artifacts.isEmpty() ? FilePath() : t.artifacts.at(0);
+ TargetType type = UtilityType;
+ if (t.type == "EXECUTABLE")
+ type = ExecutableType;
+ else if (t.type == "STATIC_LIBRARY")
+ type = StaticLibraryType;
+ else if (t.type == "OBJECT_LIBRARY")
+ type = ObjectLibraryType;
+ else if (t.type == "MODULE_LIBRARY" || t.type == "SHARED_LIBRARY")
+ type = DynamicLibraryType;
+ else
+ type = UtilityType;
+ ct.targetType = type;
+ if (t.artifacts.isEmpty()) {
+ ct.workingDirectory = t.buildDir;
+ } else {
+ ct.workingDirectory = FilePath::fromString(QDir::cleanPath(
+ QDir(t.buildDir.toString()).absoluteFilePath(t.artifacts.at(0).toString() + "/..")));
+ }
+ ct.sourceDirectory = FilePath::fromString(
+ QDir::cleanPath(sourceDir.absoluteFilePath(t.sourceDir.toString())));
+ return ct;
+ });
+ return result;
+}
+
+static QStringList splitFragments(const QStringList &fragments)
+{
+ QStringList result;
+ for (const QString &f : fragments) {
+ result += QtcProcess::splitArgs(f);
+ }
+ return result;
+}
+
+CppTools::RawProjectParts generateRawProjectParts(const PreprocessedData &input,
+ const FilePath &sourceDirectory)
+{
+ CppTools::RawProjectParts rpps;
+
+ int counter = 0;
+ for (const TargetDetails &t : input.targetDetails) {
+ QDir sourceDir(sourceDirectory.toString());
+
+ for (const CompileInfo &ci : t.compileGroups) {
+ if (ci.language != "C" && ci.language != "CXX" && ci.language != "CUDA")
+ continue; // No need to bother the C++ codemodel
+
+ // CMake users worked around Creator's inability of listing header files by creating
+ // custom targets with all the header files. This target breaks the code model, so
+ // keep quiet about it:-)
+ if (ci.defines.empty() && ci.includes.empty() && allOf(ci.sources, [t](const int sid) {
+ const SourceInfo &source = t.sources[static_cast<size_t>(sid)];
+ return Node::fileTypeForFileName(FilePath::fromString(source.path))
+ == FileType::Header;
+ })) {
+ qWarning() << "Not reporting all-header compilegroup of target" << t.name
+ << "to code model.";
+ continue;
+ }
+
+ ++counter;
+ CppTools::RawProjectPart rpp;
+ rpp.setProjectFileLocation(t.sourceDir.pathAppended("CMakeLists.txt").toString());
+ rpp.setBuildSystemTarget(CMakeTargetNode::generateId(t.sourceDir, t.name));
+ rpp.setDisplayName(t.id);
+ rpp.setMacros(transform<QVector>(ci.defines, &DefineInfo::define));
+ rpp.setHeaderPaths(transform<QVector>(ci.includes, &IncludeInfo::path));
+
+ CppTools::RawProjectPartFlags cProjectFlags;
+ cProjectFlags.commandLineFlags = splitFragments(ci.fragments);
+ rpp.setFlagsForC(cProjectFlags);
+
+ CppTools::RawProjectPartFlags cxxProjectFlags;
+ cxxProjectFlags.commandLineFlags = cProjectFlags.commandLineFlags;
+ rpp.setFlagsForCxx(cxxProjectFlags);
+
+ rpp.setFiles(transform<QList>(ci.sources, [&t, &sourceDir](const int si) {
+ return sourceDir.absoluteFilePath(t.sources[static_cast<size_t>(si)].path);
+ }));
+
+ const bool isExecutable = t.type == "EXECUTABLE";
+ rpp.setBuildTargetType(isExecutable ? CppTools::ProjectPart::Executable
+ : CppTools::ProjectPart::Library);
+ rpps.append(rpp);
+ }
+ }
+
+ return rpps;
+}
+
+FilePath directorySourceDir(const Configuration &c, const QDir &sourceDir, int directoryIndex)
+{
+ const size_t di = static_cast<size_t>(directoryIndex);
+ QTC_ASSERT(di >= 0 && di < c.directories.size(), return FilePath());
+
+ return FilePath::fromString(
+ QDir::cleanPath(sourceDir.absoluteFilePath(c.directories[di].sourcePath)));
+}
+
+FilePath directoryBuildDir(const Configuration &c, const QDir &buildDir, int directoryIndex)
+{
+ const size_t di = static_cast<size_t>(directoryIndex);
+ QTC_ASSERT(di >= 0 && di < c.directories.size(), return FilePath());
+
+ return FilePath::fromString(
+ QDir::cleanPath(buildDir.absoluteFilePath(c.directories[di].buildPath)));
+}
+
+void addProjects(const QHash<Utils::FilePath, ProjectNode *> &cmakeListsNodes,
+ const Configuration &config,
+ const QDir &sourceDir)
+{
+ for (const FileApiDetails::Project &p : config.projects) {
+ if (p.parent == -1)
+ continue; // Top-level project has already been covered
+ FilePath dir = directorySourceDir(config, sourceDir, p.directories[0]);
+ createProjectNode(cmakeListsNodes, dir, p.name);
+ }
+}
+
+void addBacktraceInformation(FolderNode *node,
+ const BacktraceInfo &backtraces,
+ const QDir &sourceDir,
+ int backtraceIndex)
+{
+ QList<FolderNode::LocationInfo> info;
+ // Set up a default target path:
+ FilePath targetPath = node->filePath().pathAppended("CMakeLists.txt");
+ while (backtraceIndex != -1) {
+ const size_t bi = static_cast<size_t>(backtraceIndex);
+ QTC_ASSERT((bi >= 0 && bi < backtraces.nodes.size()), break);
+ const BacktraceNode &btNode = backtraces.nodes[bi];
+ backtraceIndex = btNode.parent; // advance to next node
+
+ const size_t fileIndex = static_cast<size_t>(btNode.file);
+ QTC_ASSERT((fileIndex >= 0 && 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<size_t>(btNode.command);
+ QTC_ASSERT((commandIndex >= 0 && 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<FolderNode *> addSourceGroups(ProjectNode *targetRoot,
+ const TargetDetails &td,
+ const Utils::FileName &sourceDirectory)
+{
+ QVector<FolderNode *> sourceGroupNodes;
+ if (td.sourceGroups.size() == 1) {
+ sourceGroupNodes.append(
+ targetRoot); // Only one source group, so do not bother to display any:-)
+ } else {
+ for (const QString &sg : td.sourceGroups) {
+ if (sg.isEmpty() || sg == "Source Files") {
+ sourceGroupNodes.append(targetRoot);
+ } else {
+ auto sgNode = createCMakeVFolder(sourceDirectory,
+ Node::DefaultFolderPriority + 5,
+ sg);
+ sgNode->setListInProject(false);
+
+ sourceGroupNodes.append(sgNode.get());
+ targetRoot->addNode(std::move(sgNode));
+ }
+ }
+ }
+
+ return sourceGroupNodes;
+}
+
+void addCompileGroups(ProjectNode *targetRoot,
+ const Utils::FilePath &topSourceDirectory,
+ const Utils::FilePath &sourceDirectory,
+ const Utils::FilePath &buildDirectory,
+ const TargetDetails &td,
+ QVector<FileNode *> &knownHeaderNodes)
+{
+ const bool inSourceBuild = (sourceDirectory == buildDirectory);
+ const QDir currentSourceDir(sourceDirectory.toString());
+
+ std::vector<std::unique_ptr<FileNode>> toList;
+ QSet<Utils::FilePath> alreadyListed;
+
+ // Files already added by other configurations:
+ targetRoot->forEachGenericNode(
+ [&alreadyListed](const Node *n) { alreadyListed.insert(n->filePath()); });
+
+ QVector<FolderNode *> sourceGroupNodes = addSourceGroups(targetRoot, td, sourceDirectory);
+ const QDir topSourceDir(topSourceDirectory.toString());
+
+ std::vector<std::unique_ptr<FileNode>> buildFileNodes;
+ std::vector<std::unique_ptr<FileNode>> otherFileNodes;
+
+ for (const SourceInfo &si : td.sources) {
+ const FilePath sourcePath = FilePath::fromString(
+ QDir::cleanPath(topSourceDir.absoluteFilePath(si.path)));
+
+ // Filter out already known files:
+ const int count = alreadyListed.count();
+ alreadyListed.insert(sourcePath);
+ if (count == alreadyListed.count())
+ continue;
+
+ // Create FileNodes from the file
+ auto node = std::make_unique<FileNode>(sourcePath, Node::fileTypeForFileName(sourcePath));
+ node->setIsGenerated(si.isGenerated);
+
+ // Register headers:
+ if (node->fileType() == FileType::Header)
+ knownHeaderNodes.append(node.get());
+
+ // Where does the file node need to go?
+ if (sourcePath.isChildOf(buildDirectory) && !inSourceBuild) {
+ buildFileNodes.emplace_back(std::move(node));
+ } else if (sourcePath.isChildOf(sourceDirectory)) {
+ sourceGroupNodes[si.sourceGroup]->addNode(std::move(node));
+ } else {
+ otherFileNodes.emplace_back(std::move(node));
+ }
+ }
+
+ addCMakeVFolder(targetRoot,
+ buildDirectory,
+ 100,
+ QCoreApplication::translate("CMakeProjectManager::Internal::FileApi",
+ "<Build Directory>"),
+ std::move(buildFileNodes));
+ addCMakeVFolder(targetRoot,
+ Utils::FilePath(),
+ 10,
+ QCoreApplication::translate("CMakeProjectManager::Internal::FileApi",
+ "<Other Locations>"),
+ std::move(otherFileNodes));
+}
+
+void addTargets(const QHash<Utils::FilePath, ProjectExplorer::ProjectNode *> &cmakeListsNodes,
+ const Configuration &config,
+ const std::vector<TargetDetails> &targetDetails,
+ const FilePath &topSourceDir,
+ const QDir &sourceDir,
+ const QDir &buildDir,
+ QVector<ProjectExplorer::FileNode *> &knownHeaderNodes)
+{
+ for (const FileApiDetails::Target &t : config.targets) {
+ const TargetDetails &td = Utils::findOrDefault(targetDetails,
+ Utils::equal(&TargetDetails::id, t.id));
+
+ const FilePath dir = directorySourceDir(config, sourceDir, t.directory);
+
+ CMakeTargetNode *tNode = createTargetNode(cmakeListsNodes, dir, t.name);
+ QTC_ASSERT(tNode, continue);
+
+ 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);
+ }
+}
+
+std::pair<std::unique_ptr<CMakeProjectNode>, QVector<FileNode *>> generateRootProjectNode(
+ PreprocessedData &data, const FilePath &sourceDirectory, const FilePath &buildDirectory)
+{
+ std::pair<std::unique_ptr<CMakeProjectNode>, QVector<FileNode *>> result;
+ result.first = std::make_unique<CMakeProjectNode>(sourceDirectory);
+
+ const QDir sourceDir(sourceDirectory.toString());
+ const QDir buildDir(buildDirectory.toString());
+
+ const FileApiDetails::Project topLevelProject
+ = findOrDefault(data.codemodel.projects, equal(&FileApiDetails::Project::parent, -1));
+ if (!topLevelProject.name.isEmpty())
+ result.first->setDisplayName(topLevelProject.name);
+
+ QHash<FilePath, ProjectNode *> cmakeListsNodes = addCMakeLists(result.first.get(),
+ std::move(data.cmakeListNodes));
+ data.cmakeListNodes.clear(); // Remove all the nullptr in the vector...
+
+ QVector<FileNode *> knownHeaders;
+ addProjects(cmakeListsNodes, data.codemodel, sourceDir);
+
+ addTargets(cmakeListsNodes,
+ data.codemodel,
+ data.targetDetails,
+ sourceDirectory,
+ sourceDir,
+ buildDir,
+ knownHeaders);
+
+ // addHeaderNodes(root.get(), knownHeaders, allFiles);
+
+ if (!data.cmakeNodesSource.empty() || !data.cmakeNodesBuild.empty()
+ || !data.cmakeNodesOther.empty())
+ addCMakeInputs(result.first.get(),
+ sourceDirectory,
+ buildDirectory,
+ std::move(data.cmakeNodesSource),
+ std::move(data.cmakeNodesBuild),
+ std::move(data.cmakeNodesOther));
+
+ data.cmakeNodesSource.clear(); // Remove all the nullptr in the vector...
+ data.cmakeNodesBuild.clear(); // Remove all the nullptr in the vector...
+ data.cmakeNodesOther.clear(); // Remove all the nullptr in the vector...
+
+ result.second = knownHeaders;
+
+ return result;
+}
+
+} // namespace
+
+namespace CMakeProjectManager {
+namespace Internal {
+
+using namespace FileApiDetails;
+
+// --------------------------------------------------------------------
+// extractData:
+// --------------------------------------------------------------------
+
+FileApiQtcData extractData(FileApiData &input,
+ const FilePath &sourceDirectory,
+ const FilePath &buildDirectory)
+{
+ FileApiQtcData result;
+
+ // Preprocess our input:
+ PreprocessedData data = preprocess(input, sourceDirectory, buildDirectory, result.errorMessage);
+ result.cache = std::move(data.cache); // Make sure this is available, even when nothing else is
+ if (!result.errorMessage.isEmpty()) {
+ return {};
+ }
+
+ result.buildTargets = generateBuildTargets(data, sourceDirectory);
+ result.cmakeFiles = std::move(data.cmakeFiles);
+ result.projectParts = generateRawProjectParts(data, sourceDirectory);
+
+ auto pair = generateRootProjectNode(data, sourceDirectory, buildDirectory);
+ result.rootProjectNode = std::move(pair.first);
+ result.knownHeaders = std::move(pair.second);
+
+ return result;
+}
+
+} // namespace Internal
+} // namespace CMakeProjectManager
diff --git a/src/plugins/cmakeprojectmanager/fileapidataextractor.h b/src/plugins/cmakeprojectmanager/fileapidataextractor.h
new file mode 100644
index 0000000000..b84abbf7c8
--- /dev/null
+++ b/src/plugins/cmakeprojectmanager/fileapidataextractor.h
@@ -0,0 +1,58 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** 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 "fileapiparser.h"
+
+#include "cmakebuildtarget.h"
+#include "cmakeprocess.h"
+#include "cmakeprojectnodes.h"
+
+#include <cpptools/cpprawprojectpart.h>
+
+#include <memory>
+
+namespace CMakeProjectManager {
+namespace Internal {
+
+class FileApiQtcData
+{
+public:
+ QString errorMessage;
+ CMakeConfig cache;
+ QSet<Utils::FilePath> cmakeFiles;
+ QList<CMakeBuildTarget> buildTargets;
+ CppTools::RawProjectParts projectParts;
+ std::unique_ptr<CMakeProjectNode> rootProjectNode;
+ QVector<ProjectExplorer::FileNode *> knownHeaders;
+};
+
+FileApiQtcData extractData(FileApiData &data,
+ const Utils::FilePath &sourceDirectory,
+ const Utils::FilePath &buildDirectory);
+
+} // namespace Internal
+} // namespace CMakeProjectManager
diff --git a/src/plugins/cmakeprojectmanager/fileapiparser.cpp b/src/plugins/cmakeprojectmanager/fileapiparser.cpp
new file mode 100644
index 0000000000..19af8d2a72
--- /dev/null
+++ b/src/plugins/cmakeprojectmanager/fileapiparser.cpp
@@ -0,0 +1,947 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** 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 "fileapiparser.h"
+
+#include <coreplugin/messagemanager.h>
+#include <cpptools/cpprawprojectpart.h>
+#include <projectexplorer/headerpath.h>
+
+#include <utils/algorithm.h>
+#include <utils/qtcassert.h>
+
+#include <QDir>
+#include <QJsonArray>
+#include <QJsonDocument>
+#include <QJsonObject>
+#include <QLoggingCategory>
+
+namespace CMakeProjectManager {
+namespace Internal {
+
+using namespace FileApiDetails;
+using namespace Utils;
+
+const char CMAKE_RELATIVE_REPLY_PATH[] = ".cmake/api/v1/reply";
+const char CMAKE_RELATIVE_QUERY_PATH[] = ".cmake/api/v1/query";
+
+Q_LOGGING_CATEGORY(cmakeFileApi, "qtc.cmake.fileApi", QtWarningMsg);
+
+// --------------------------------------------------------------------
+// Helper:
+// --------------------------------------------------------------------
+
+static void reportFileApiSetupFailure()
+{
+ Core::MessageManager::write(QCoreApplication::translate(
+ "CMakeProjectManager::Internal",
+ "Failed to set up cmake fileapi support. Creator can not extract project information."));
+}
+
+static bool shouldProcessFile(const QString &filePath, bool update = true)
+{
+ static QString lastSeenFilePath;
+ if (filePath == lastSeenFilePath)
+ return false;
+ if (update)
+ lastSeenFilePath = filePath;
+ return true;
+}
+
+static std::pair<int, int> cmakeVersion(const QJsonObject &obj)
+{
+ const QJsonObject version = obj.value("version").toObject();
+ const int major = version.value("major").toInt(-1);
+ const int minor = version.value("minor").toInt(-1);
+ return std::make_pair(major, minor);
+}
+
+static bool checkJsonObject(const QJsonObject &obj, const QString &kind, int major, int minor = -1)
+{
+ auto version = cmakeVersion(obj);
+ if (major == -1)
+ version.first = major;
+ if (minor == -1)
+ version.second = minor;
+ return obj.value("kind").toString() == kind && version == std::make_pair(major, minor);
+}
+
+static std::pair<QString, QString> nameValue(const QJsonObject &obj)
+{
+ return std::make_pair(obj.value("name").toString(), obj.value("value").toString());
+}
+
+static QJsonDocument readJsonFile(const QString &path)
+{
+ qCDebug(cmakeFileApi) << "readJsonFile:" << path;
+
+ QFile file(path);
+ file.open(QIODevice::ReadOnly | QIODevice::Text);
+ const QJsonDocument doc = QJsonDocument::fromJson(file.readAll());
+
+ return doc;
+}
+
+std::vector<int> indexList(const QJsonValue &v)
+{
+ const QJsonArray &indexList = v.toArray();
+ std::vector<int> result;
+ result.reserve(static_cast<size_t>(indexList.count()));
+
+ for (const QJsonValue &v : indexList) {
+ result.push_back(v.toInt(-1));
+ }
+ return result;
+}
+
+// Reply file:
+
+static ReplyFileContents readReplyFile(const QFileInfo &fi, QString &errorMessage)
+{
+ const QJsonDocument document = readJsonFile(fi.filePath());
+ static const QString msg = QCoreApplication::translate("CMakeProjectManager::Internal",
+ "Invalid reply file created by cmake.");
+
+ ReplyFileContents result;
+ if (document.isNull() || document.isEmpty() || !document.isObject()) {
+ errorMessage = msg;
+ return result;
+ }
+
+ const QJsonObject rootObject = document.object();
+
+ {
+ const QJsonObject cmakeObject = rootObject.value("cmake").toObject();
+ {
+ const QJsonObject paths = cmakeObject.value("paths").toObject();
+ {
+ result.cmakeExecutable = paths.value("cmake").toString();
+ result.cmakeRoot = paths.value("root").toString();
+ }
+ const QJsonObject generator = cmakeObject.value("generator").toObject();
+ {
+ result.generator = generator.value("name").toString();
+ }
+ }
+ }
+
+ bool hadInvalidObject = false;
+ {
+ const QJsonArray objects = rootObject.value("objects").toArray();
+ for (const QJsonValue &v : objects) {
+ const QJsonObject object = v.toObject();
+ {
+ ReplyObject r;
+ r.kind = object.value("kind").toString();
+ r.file = object.value("jsonFile").toString();
+ r.version = cmakeVersion(object);
+
+ if (r.kind.isEmpty() || r.file.isEmpty() || r.version.first == -1
+ || r.version.second == -1)
+ hadInvalidObject = true;
+ else
+ result.replies.append(r);
+ }
+ }
+ }
+
+ if (result.generator.isEmpty() || result.cmakeExecutable.isEmpty() || result.cmakeRoot.isEmpty()
+ || result.replies.isEmpty() || hadInvalidObject)
+ errorMessage = msg;
+
+ return result;
+}
+
+// Cache file:
+
+static CMakeConfig readCacheFile(const QString &cacheFile, QString &errorMessage)
+{
+ CMakeConfig result;
+
+ const QJsonDocument doc = readJsonFile(cacheFile);
+ const QJsonObject root = doc.object();
+
+ if (!checkJsonObject(root, "cache", 2)) {
+ errorMessage = QCoreApplication::translate("CMakeProjectManager::Internal",
+ "Invalid cache file generated by cmake.");
+ return {};
+ }
+
+ const QJsonArray entries = root.value("entries").toArray();
+ for (const QJsonValue &v : entries) {
+ CMakeConfigItem item;
+
+ const QJsonObject entry = v.toObject();
+ auto nv = nameValue(entry);
+ item.key = nv.first.toUtf8();
+ item.value = nv.second.toUtf8();
+
+ item.type = CMakeConfigItem::typeStringToType(entry.value("type").toString().toUtf8());
+
+ {
+ const QJsonArray properties = entry.value("properties").toArray();
+ for (const QJsonValue &v : properties) {
+ const QJsonObject prop = v.toObject();
+ auto nv = nameValue(prop);
+ if (nv.first == "ADVANCED") {
+ const auto boolValue = CMakeConfigItem::toBool(nv.second.toUtf8());
+ item.isAdvanced = boolValue.has_value() && boolValue.value();
+ } else if (nv.first == "HELPSTRING") {
+ item.documentation = nv.second.toUtf8();
+ } else if (nv.first == "STRINGS") {
+ item.values = nv.second.split(';');
+ }
+ }
+ }
+ result.append(item);
+ }
+ return result;
+}
+
+// CMake Files:
+
+std::vector<CMakeFileInfo> readCMakeFilesFile(const QString &cmakeFilesFile, QString &errorMessage)
+{
+ std::vector<CMakeFileInfo> result;
+
+ const QJsonDocument doc = readJsonFile(cmakeFilesFile);
+ const QJsonObject root = doc.object();
+
+ if (!checkJsonObject(root, "cmakeFiles", 1)) {
+ errorMessage = QCoreApplication::translate("CMakeProjectManager::Internal",
+ "Invalid cmakeFiles file generated by cmake.");
+ return {};
+ }
+
+ const QJsonArray inputs = root.value("inputs").toArray();
+ for (const QJsonValue &v : inputs) {
+ CMakeFileInfo info;
+ const QJsonObject input = v.toObject();
+ info.path = input.value("path").toString();
+
+ info.isCMake = input.value("isCMake").toBool();
+ const QString filename = FilePath::fromString(info.path).fileName();
+ info.isCMakeListsDotTxt = (filename.compare("CMakeLists.txt",
+ HostOsInfo::fileNameCaseSensitivity())
+ == 0);
+
+ info.isGenerated = input.value("isGenerated").toBool();
+ info.isExternal = input.value("isExternal").toBool();
+
+ result.emplace_back(std::move(info));
+ }
+ return result;
+}
+
+// Codemodel file:
+
+std::vector<Directory> extractDirectories(const QJsonArray &directories, QString &errorMessage)
+{
+ if (directories.isEmpty()) {
+ errorMessage = QCoreApplication::translate(
+ "CMakeProjectManager::Internal",
+ "Invalid codemodel file generated by cmake: No directories.");
+ return {};
+ }
+
+ std::vector<Directory> result;
+ for (const QJsonValue &v : directories) {
+ const QJsonObject obj = v.toObject();
+ if (obj.isEmpty()) {
+ errorMessage = QCoreApplication::translate(
+ "CMakeProjectManager::Internal",
+ "Invalid codemodel file generated by cmake: Empty directory object.");
+ continue;
+ }
+ Directory dir;
+ dir.sourcePath = obj.value("source").toString();
+ dir.buildPath = obj.value("build").toString();
+ dir.parent = obj.value("parentIndex").toInt(-1);
+ dir.project = obj.value("projectIndex").toInt(-1);
+ dir.children = indexList(obj.value("childIndexes"));
+ dir.targets = indexList(obj.value("targetIndexes"));
+ dir.hasInstallRule = obj.value("hasInstallRule").toBool();
+
+ result.emplace_back(std::move(dir));
+ }
+ return result;
+}
+
+static std::vector<Project> extractProjects(const QJsonArray &projects, QString &errorMessage)
+{
+ if (projects.isEmpty()) {
+ errorMessage = QCoreApplication::translate(
+ "CMakeProjectManager::Internal",
+ "Invalid codemodel file generated by cmake: No projects.");
+ return {};
+ }
+
+ std::vector<Project> result;
+ for (const QJsonValue &v : projects) {
+ const QJsonObject obj = v.toObject();
+ if (obj.isEmpty()) {
+ errorMessage = QCoreApplication::translate(
+ "CMakeProjectManager::Internal",
+ "Invalid codemodel file generated by cmake: Empty project object.");
+ continue;
+ }
+ Project project;
+ project.name = obj.value("name").toString();
+ project.parent = obj.value("parentIndex").toInt(-1);
+ project.children = indexList(obj.value("childIndexes"));
+ project.directories = indexList(obj.value("directoryIndexes"));
+ project.targets = indexList(obj.value("targetIndexes"));
+
+ qCDebug(cmakeFileApi) << "Project read:" << project.name << project.directories;
+
+ if (project.name.isEmpty() || project.directories.empty()) {
+ errorMessage = QCoreApplication::translate(
+ "CMakeProjectManager::Internal",
+ "Invalid codemodel file generated by cmake: Broken project data.");
+ continue;
+ }
+
+ result.emplace_back(std::move(project));
+ }
+ return result;
+}
+
+static std::vector<Target> extractTargets(const QJsonArray &targets, QString &errorMessage)
+{
+ if (targets.isEmpty()) {
+ errorMessage
+ = QCoreApplication::translate("CMakeProjectManager::Internal",
+ "Invalid codemodel file generated by cmake: No targets.");
+ return {};
+ }
+
+ std::vector<Target> result;
+ for (const QJsonValue &v : targets) {
+ const QJsonObject obj = v.toObject();
+ if (obj.isEmpty()) {
+ errorMessage = QCoreApplication::translate(
+ "CMakeProjectManager::Internal",
+ "Invalid codemodel file generated by cmake: Empty target object.");
+ continue;
+ }
+ Target target;
+ target.name = obj.value("name").toString();
+ target.id = obj.value("id").toString();
+ target.directory = obj.value("directoryIndex").toInt(-1);
+ target.project = obj.value("projectIndex").toInt(-1);
+ target.jsonFile = obj.value("jsonFile").toString();
+
+ if (target.name.isEmpty() || target.id.isEmpty() || target.jsonFile.isEmpty()
+ || target.directory == -1 || target.project == -1) {
+ errorMessage = QCoreApplication::translate(
+ "CMakeProjectManager::Internal",
+ "Invalid codemodel file generated by cmake: Broken target data.");
+ continue;
+ }
+
+ result.emplace_back(std::move(target));
+ }
+ return result;
+}
+
+static bool validateIndexes(const Configuration &config)
+{
+ const int directoryCount = static_cast<int>(config.directories.size());
+ const int projectCount = static_cast<int>(config.projects.size());
+ const int targetCount = static_cast<int>(config.targets.size());
+
+ int topLevelCount = 0;
+ for (const Directory &d : config.directories) {
+ if (d.parent == -1)
+ ++topLevelCount;
+
+ if (d.parent < -1 || d.parent >= directoryCount) {
+ qCWarning(cmakeFileApi)
+ << "Directory" << d.sourcePath << ": parent index" << d.parent << "is broken.";
+ return false;
+ }
+ if (d.project < 0 || d.project >= projectCount) {
+ qCWarning(cmakeFileApi)
+ << "Directory" << d.sourcePath << ": project index" << d.project << "is broken.";
+ return false;
+ }
+ if (contains(d.children, [directoryCount](int c) { return c < 0 || c >= directoryCount; })) {
+ qCWarning(cmakeFileApi)
+ << "Directory" << d.sourcePath << ": A child index" << d.children << "is broken.";
+ return false;
+ }
+ if (contains(d.targets, [targetCount](int t) { return t < 0 || t >= targetCount; })) {
+ qCWarning(cmakeFileApi)
+ << "Directory" << d.sourcePath << ": A target index" << d.targets << "is broken.";
+ return false;
+ }
+ }
+ if (topLevelCount != 1) {
+ qCWarning(cmakeFileApi) << "Directories: Invalid number of top level directories, "
+ << topLevelCount << " (expected: 1).";
+ return false;
+ }
+
+ topLevelCount = 0;
+ for (const Project &p : config.projects) {
+ if (p.parent == -1)
+ ++topLevelCount;
+
+ if (p.parent < -1 || p.parent >= projectCount) {
+ qCWarning(cmakeFileApi)
+ << "Project" << p.name << ": parent index" << p.parent << "is broken.";
+ return false;
+ }
+ if (contains(p.children, [projectCount](int p) { return p < 0 || p >= projectCount; })) {
+ qCWarning(cmakeFileApi)
+ << "Project" << p.name << ": A child index" << p.children << "is broken.";
+ return false;
+ }
+ if (contains(p.targets, [targetCount](int t) { return t < 0 || t >= targetCount; })) {
+ qCWarning(cmakeFileApi)
+ << "Project" << p.name << ": A target index" << p.targets << "is broken.";
+ return false;
+ }
+ if (contains(p.directories,
+ [directoryCount](int d) { return d < 0 || d >= directoryCount; })) {
+ qCWarning(cmakeFileApi)
+ << "Project" << p.name << ": A directory index" << p.directories << "is broken.";
+ return false;
+ }
+ }
+ if (topLevelCount != 1) {
+ qCWarning(cmakeFileApi) << "Projects: Invalid number of top level projects, "
+ << topLevelCount << " (expected: 1).";
+ return false;
+ }
+
+ for (const Target &t : config.targets) {
+ if (t.directory < 0 || t.directory >= directoryCount) {
+ qCWarning(cmakeFileApi)
+ << "Target" << t.name << ": directory index" << t.directory << "is broken.";
+ return false;
+ }
+ if (t.project < 0 || t.project >= projectCount) {
+ qCWarning(cmakeFileApi)
+ << "Target" << t.name << ": project index" << t.project << "is broken.";
+ return false;
+ }
+ }
+ return true;
+}
+
+static std::vector<Configuration> extractConfigurations(const QJsonArray &configs,
+ QString &errorMessage)
+{
+ if (configs.isEmpty()) {
+ errorMessage = QCoreApplication::translate(
+ "CMakeProjectManager::Internal",
+ "Invalid codemodel file generated by cmake: No configurations.");
+ return {};
+ }
+
+ std::vector<FileApiDetails::Configuration> result;
+ for (const QJsonValue &v : configs) {
+ const QJsonObject obj = v.toObject();
+ if (obj.isEmpty()) {
+ errorMessage = QCoreApplication::translate(
+ "CMakeProjectManager::Internal",
+ "Invalid codemodel file generated by cmake: Empty configuration object.");
+ continue;
+ }
+ Configuration config;
+ config.name = obj.value("name").toString();
+
+ config.directories = extractDirectories(obj.value("directories").toArray(), errorMessage);
+ config.projects = extractProjects(obj.value("projects").toArray(), errorMessage);
+ config.targets = extractTargets(obj.value("targets").toArray(), errorMessage);
+
+ if (!validateIndexes(config)) {
+ errorMessage
+ = QCoreApplication::translate("CMakeProjectManager::Internal",
+ "Invalid codemodel file generated by cmake: Broken "
+ "indexes in directories/projects/targets.");
+ return {};
+ }
+
+ result.emplace_back(std::move(config));
+ }
+ return result;
+}
+
+static std::vector<Configuration> readCodemodelFile(const QString &codemodelFile,
+ QString &errorMessage)
+{
+ const QJsonDocument doc = readJsonFile(codemodelFile);
+ const QJsonObject root = doc.object();
+
+ if (!checkJsonObject(root, "codemodel", 2)) {
+ errorMessage = QCoreApplication::translate("CMakeProjectManager::Internal",
+ "Invalid codemodel file generated by cmake.");
+ return {};
+ }
+
+ return extractConfigurations(root.value("configurations").toArray(), errorMessage);
+}
+
+// TargetDetails:
+
+std::vector<FileApiDetails::FragmentInfo> extractFragments(const QJsonObject &obj)
+{
+ const QJsonArray fragments = obj.value("commandFragments").toArray();
+ return Utils::transform<std::vector>(fragments, [](const QJsonValue &v) {
+ const QJsonObject o = v.toObject();
+ return FileApiDetails::FragmentInfo{o.value("fragment").toString(),
+ o.value("role").toString()};
+ });
+}
+
+TargetDetails extractTargetDetails(const QJsonObject &root, QString &errorMessage)
+{
+ TargetDetails t;
+ t.name = root.value("name").toString();
+ t.id = root.value("id").toString();
+ t.type = root.value("type").toString();
+
+ if (t.name.isEmpty() || t.id.isEmpty() || t.type.isEmpty()) {
+ errorMessage = QCoreApplication::translate("CMakeProjectManager::Internal",
+ "Invalid target file: Information is missing.");
+ return {};
+ }
+
+ t.backtrace = root.value("backtrace").toInt(-1);
+ {
+ const QJsonObject folder = root.value("folder").toObject();
+ t.folderTargetProperty = folder.value("name").toString();
+ }
+ {
+ const QJsonObject paths = root.value("paths").toObject();
+ t.sourceDir = FilePath::fromString(paths.value("source").toString());
+ t.buildDir = FilePath::fromString(paths.value("build").toString());
+ }
+ t.nameOnDisk = root.value("nameOnDisk").toString();
+ {
+ const QJsonArray artifacts = root.value("artifacts").toArray();
+ t.artifacts = transform<QList>(artifacts, [](const QJsonValue &v) {
+ return FilePath::fromString(v.toObject().value("path").toString());
+ });
+ }
+ t.isGeneratorProvided = root.value("isGeneratorProvided").toBool();
+ {
+ const QJsonObject install = root.value("install").toObject();
+ t.installPrefix = install.value("prefix").toObject().value("path").toString();
+ {
+ const QJsonArray destinations = install.value("destinations").toArray();
+ t.installDestination = transform<std::vector>(destinations, [](const QJsonValue &v) {
+ const QJsonObject o = v.toObject();
+ return InstallDestination{o.value("path").toString(),
+ o.value("backtrace").toInt(-1)};
+ });
+ }
+ }
+ {
+ const QJsonObject link = root.value("link").toObject();
+ if (link.isEmpty()) {
+ t.link = {};
+ } else {
+ LinkInfo info;
+ info.language = link.value("language").toString();
+ info.isLto = link.value("lto").toBool();
+ info.sysroot = link.value("sysroot").toObject().value("path").toString();
+ info.fragments = extractFragments(link);
+ t.link = info;
+ }
+ }
+ {
+ const QJsonObject archive = root.value("archive").toObject();
+ if (archive.isEmpty()) {
+ t.archive = {};
+ } else {
+ ArchiveInfo info;
+ info.isLto = archive.value("lto").toBool();
+ info.fragments = extractFragments(archive);
+ t.archive = info;
+ }
+ }
+ {
+ const QJsonArray dependencies = root.value("dependencies").toArray();
+ t.dependencies = transform<std::vector>(dependencies, [](const QJsonValue &v) {
+ const QJsonObject o = v.toObject();
+ return DependencyInfo{o.value("id").toString(), o.value("backtrace").toInt(-1)};
+ });
+ }
+ {
+ const QJsonArray sources = root.value("sources").toArray();
+ t.sources = transform<std::vector>(sources, [](const QJsonValue &v) {
+ const QJsonObject o = v.toObject();
+ return SourceInfo{o.value("path").toString(),
+ o.value("compileGroupIndex").toInt(-1),
+ o.value("sourceGroupIndex").toInt(-1),
+ o.value("backtrace").toInt(-1),
+ o.value("isGenerated").toBool()};
+ });
+ }
+ {
+ const QJsonArray sourceGroups = root.value("sourceGroups").toArray();
+ t.sourceGroups = transform<std::vector>(sourceGroups, [](const QJsonValue &v) {
+ const QJsonObject o = v.toObject();
+ return o.value("name").toString();
+ });
+ }
+ {
+ const QJsonArray compileGroups = root.value("compileGroups").toArray();
+ t.compileGroups = transform<std::vector>(compileGroups, [](const QJsonValue &v) {
+ const QJsonObject o = v.toObject();
+ return CompileInfo{
+ transform<std::vector>(o.value("sourceIndexes").toArray(),
+ [](const QJsonValue &v) { return v.toInt(-1); }),
+ o.value("language").toString(),
+ transform<QList>(o.value("compileCommandFragments").toArray(),
+ [](const QJsonValue &v) {
+ const QJsonObject o = v.toObject();
+ return o.value("fragment").toString();
+ }),
+ transform<std::vector>(
+ o.value("includes").toArray(),
+ [](const QJsonValue &v) {
+ const QJsonObject i = v.toObject();
+ const QString path = i.value("path").toString();
+ const bool isSystem = i.value("isSystem").toBool();
+ const ProjectExplorer::HeaderPath
+ hp(path,
+ isSystem ? ProjectExplorer::HeaderPathType::System
+ : ProjectExplorer::HeaderPathType::User);
+
+ return IncludeInfo{CppTools::RawProjectPart::frameworkDetectionHeuristic(hp),
+ i.value("backtrace").toInt(-1)};
+ }),
+ transform<std::vector>(o.value("defines").toArray(),
+ [](const QJsonValue &v) {
+ const QJsonObject d = v.toObject();
+ return DefineInfo{
+ ProjectExplorer::Macro::fromKeyValue(
+ d.value("define").toString()),
+ d.value("backtrace").toInt(-1),
+ };
+ }),
+ o.value("sysroot").toString(),
+ };
+ });
+ }
+ {
+ const QJsonObject backtraceGraph = root.value("backtraceGraph").toObject();
+ t.backtraceGraph.files = transform<std::vector>(backtraceGraph.value("files").toArray(),
+ [](const QJsonValue &v) {
+ return v.toString();
+ });
+ t.backtraceGraph.commands
+ = transform<std::vector>(backtraceGraph.value("commands").toArray(),
+ [](const QJsonValue &v) { return v.toString(); });
+ t.backtraceGraph.nodes = transform<std::vector>(backtraceGraph.value("nodes").toArray(),
+ [](const QJsonValue &v) {
+ const QJsonObject o = v.toObject();
+ return BacktraceNode{
+ o.value("file").toInt(-1),
+ o.value("line").toInt(-1),
+ o.value("command").toInt(-1),
+ o.value("parent").toInt(-1),
+ };
+ });
+ }
+
+ return t;
+}
+
+int validateBacktraceGraph(const TargetDetails &t)
+{
+ const int backtraceFilesCount = static_cast<int>(t.backtraceGraph.files.size());
+ const int backtraceCommandsCount = static_cast<int>(t.backtraceGraph.commands.size());
+ const int backtraceNodeCount = static_cast<int>(t.backtraceGraph.nodes.size());
+
+ int topLevelNodeCount = 0;
+ for (const BacktraceNode &n : t.backtraceGraph.nodes) {
+ if (n.parent == -1) {
+ ++topLevelNodeCount;
+ }
+ if (n.file < 0 || n.file >= backtraceFilesCount) {
+ qCWarning(cmakeFileApi) << "BacktraceNode: file index" << n.file << "is broken.";
+ return -1;
+ }
+ if (n.command < -1 || n.command >= backtraceCommandsCount) {
+ qCWarning(cmakeFileApi) << "BacktraceNode: command index" << n.command << "is broken.";
+ return -1;
+ }
+ if (n.parent < -1 || n.parent >= backtraceNodeCount) {
+ qCWarning(cmakeFileApi) << "BacktraceNode: parent index" << n.parent << "is broken.";
+ return -1;
+ }
+ }
+
+ if (topLevelNodeCount == 0 && backtraceNodeCount > 0) { // This is a forest, not a tree
+ qCWarning(cmakeFileApi) << "BacktraceNode: Invalid number of top level nodes"
+ << topLevelNodeCount;
+ return -1;
+ }
+
+ return backtraceNodeCount;
+}
+
+bool validateTargetDetails(const TargetDetails &t)
+{
+ // The part filled in by the codemodel file has already been covered!
+
+ // Internal consistency of backtraceGraph:
+ const int backtraceCount = validateBacktraceGraph(t);
+ if (backtraceCount < 0)
+ return false;
+
+ const int sourcesCount = static_cast<int>(t.sources.size());
+ const int sourceGroupsCount = static_cast<int>(t.sourceGroups.size());
+ const int compileGroupsCount = static_cast<int>(t.compileGroups.size());
+
+ if (t.backtrace < -1 || t.backtrace >= backtraceCount) {
+ qCWarning(cmakeFileApi) << "TargetDetails" << t.name << ": backtrace index" << t.backtrace
+ << "is broken.";
+ return false;
+ }
+ for (const InstallDestination &id : t.installDestination) {
+ if (id.backtrace < -1 || id.backtrace >= backtraceCount) {
+ qCWarning(cmakeFileApi) << "TargetDetails" << t.name << ": backtrace index"
+ << t.backtrace << "of install destination is broken.";
+ return false;
+ }
+ }
+
+ for (const DependencyInfo &dep : t.dependencies) {
+ if (dep.backtrace < -1 || dep.backtrace >= backtraceCount) {
+ qCWarning(cmakeFileApi) << "TargetDetails" << t.name << ": backtrace index"
+ << t.backtrace << "of dependency is broken.";
+ return false;
+ }
+ }
+
+ for (const SourceInfo &s : t.sources) {
+ if (s.compileGroup < -1 || s.compileGroup >= compileGroupsCount) {
+ qCWarning(cmakeFileApi) << "TargetDetails" << t.name << ": compile group index"
+ << s.compileGroup << "of source info is broken.";
+ return false;
+ }
+ if (s.sourceGroup < -1 || s.sourceGroup >= sourceGroupsCount) {
+ qCWarning(cmakeFileApi) << "TargetDetails" << t.name << ": source group index"
+ << s.sourceGroup << "of source info is broken.";
+ return false;
+ }
+ if (s.backtrace < -1 || s.backtrace >= backtraceCount) {
+ qCWarning(cmakeFileApi) << "TargetDetails" << t.name << ": backtrace index"
+ << s.backtrace << "of source info is broken.";
+ return false;
+ }
+ }
+
+ for (const CompileInfo &cg : t.compileGroups) {
+ for (int s : cg.sources) {
+ if (s < 0 || s >= sourcesCount) {
+ qCWarning(cmakeFileApi) << "TargetDetails" << t.name << ": sources index" << s
+ << "of compile group is broken.";
+ return false;
+ }
+ }
+ for (const IncludeInfo &i : cg.includes) {
+ if (i.backtrace < -1 || i.backtrace >= backtraceCount) {
+ qCWarning(cmakeFileApi) << "TargetDetails" << t.name << ": includes/backtrace index"
+ << i.backtrace << "of compile group is broken.";
+ return false;
+ }
+ }
+ for (const DefineInfo &d : cg.defines) {
+ if (d.backtrace < -1 || d.backtrace >= backtraceCount) {
+ qCWarning(cmakeFileApi) << "TargetDetails" << t.name << ": defines/backtrace index"
+ << d.backtrace << "of compile group is broken.";
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+TargetDetails readTargetFile(const QString &targetFile, QString &errorMessage)
+{
+ const QJsonDocument doc = readJsonFile(targetFile);
+ const QJsonObject root = doc.object();
+
+ TargetDetails result = extractTargetDetails(root, errorMessage);
+ if (errorMessage.isEmpty() && !validateTargetDetails(result)) {
+ errorMessage = QCoreApplication::translate(
+ "CMakeProjectManager::Internal",
+ "Invalid target file generated by cmake: Broken indexes in target details.");
+ }
+ return result;
+}
+
+// --------------------------------------------------------------------
+// ReplyFileContents:
+// --------------------------------------------------------------------
+
+QString FileApiDetails::ReplyFileContents::jsonFile(const QString &kind, const QDir &replyDir) const
+{
+ const auto ro = findOrDefault(replies, equal(&ReplyObject::kind, kind));
+ if (ro.file.isEmpty())
+ return QString();
+ else
+ return replyDir.absoluteFilePath(ro.file);
+}
+
+// --------------------------------------------------------------------
+// FileApi:
+// --------------------------------------------------------------------
+
+FileApiParser::FileApiParser(const FilePath &sourceDirectory, const FilePath &buildDirectory)
+ : m_sourceDirectory(sourceDirectory)
+ , m_buildDirectory(buildDirectory)
+{
+ setupCMakeFileApi();
+
+ QObject::connect(&m_watcher,
+ &FileSystemWatcher::directoryChanged,
+ this,
+ &FileApiParser::replyDirectoryHasChanged);
+
+ m_watcher.addDirectory(cmakeReplyDirectory().toString(), FileSystemWatcher::WatchAllChanges);
+}
+
+FilePath FileApiParser::cmakeReplyDirectory() const
+{
+ return m_buildDirectory.pathAppended(CMAKE_RELATIVE_REPLY_PATH);
+}
+
+FileApiParser::~FileApiParser() = default;
+
+void FileApiParser::setupCMakeFileApi() const
+{
+ const QDir buildDir = QDir(m_buildDirectory.toString());
+ const QString relativeQueryPath = QString::fromLatin1(CMAKE_RELATIVE_QUERY_PATH);
+
+ buildDir.mkpath(relativeQueryPath);
+ buildDir.mkpath(
+ QString::fromLatin1(CMAKE_RELATIVE_REPLY_PATH)); // So that we have a directory to watch!
+
+ QDir queryDir = buildDir;
+ queryDir.cd(relativeQueryPath);
+
+ if (!queryDir.exists()) {
+ reportFileApiSetupFailure();
+ return;
+ }
+ QTC_ASSERT(queryDir.exists(), );
+
+ bool failedBefore = false;
+ for (const QString &fileName : QStringList({"cache-v2", "codemodel-v2", "cmakeFiles-v1"})) {
+ const QString filePath = queryDir.filePath(fileName);
+
+ QFile f(filePath);
+ if (!f.exists()) {
+ f.open(QFile::WriteOnly);
+ f.close();
+ }
+
+ if (!f.exists() && !failedBefore) {
+ failedBefore = true;
+ reportFileApiSetupFailure();
+ }
+ }
+}
+
+static QStringList uniqueTargetFiles(const std::vector<Configuration> &configs)
+{
+ QSet<QString> knownIds;
+ QStringList files;
+ for (const Configuration &config : configs) {
+ for (const Target &t : config.targets) {
+ const int knownCount = knownIds.count();
+ knownIds.insert(t.id);
+ if (knownIds.count() > knownCount) {
+ files.append(t.jsonFile);
+ }
+ }
+ }
+ return files;
+}
+
+FileApiData FileApiParser::parseData(const QFileInfo &replyFileInfo, QString &errorMessage)
+{
+ QTC_CHECK(errorMessage.isEmpty());
+ const QDir replyDir = replyFileInfo.dir();
+
+ FileApiData result;
+
+ result.replyFile = readReplyFile(replyFileInfo, errorMessage);
+ result.cache = readCacheFile(result.replyFile.jsonFile("cache", replyDir), errorMessage);
+ result.cmakeFiles = readCMakeFilesFile(result.replyFile.jsonFile("cmakeFiles", replyDir),
+ errorMessage);
+ result.codemodel = readCodemodelFile(result.replyFile.jsonFile("codemodel", replyDir),
+ errorMessage);
+
+ const QStringList targetFiles = uniqueTargetFiles(result.codemodel);
+
+ for (const QString &targetFile : targetFiles) {
+ QString targetErrorMessage;
+ TargetDetails td = readTargetFile(replyDir.absoluteFilePath(targetFile), targetErrorMessage);
+ if (targetErrorMessage.isEmpty()) {
+ result.targetDetails.emplace_back(std::move(td));
+ } else {
+ qWarning() << "Failed to retrieve target data from cmake fileapi:"
+ << targetErrorMessage;
+ errorMessage = targetErrorMessage;
+ }
+ }
+
+ return result;
+}
+
+QFileInfo FileApiParser::scanForCMakeReplyFile() const
+{
+ QDir replyDir(cmakeReplyDirectory().toString());
+ if (!replyDir.exists())
+ return {};
+
+ const QFileInfoList fis = replyDir.entryInfoList(QStringList("index-*.json"),
+ QDir::Files,
+ QDir::Name);
+ return fis.isEmpty() ? QFileInfo() : fis.last();
+}
+
+void FileApiParser::replyDirectoryHasChanged(const QString &directory) const
+{
+ if (directory == cmakeReplyDirectory().toString()) {
+ QFileInfo fi = scanForCMakeReplyFile();
+ if (fi.isFile() && shouldProcessFile(fi.filePath(), false)) {
+ emit dirty();
+ }
+ }
+}
+
+} // namespace Internal
+} // namespace CMakeProjectManager
diff --git a/src/plugins/cmakeprojectmanager/fileapiparser.h b/src/plugins/cmakeprojectmanager/fileapiparser.h
new file mode 100644
index 0000000000..3a4fdfbd82
--- /dev/null
+++ b/src/plugins/cmakeprojectmanager/fileapiparser.h
@@ -0,0 +1,269 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** 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 "cmakeconfigitem.h"
+
+#include <projectexplorer/headerpath.h>
+#include <projectexplorer/projectmacro.h>
+
+#include <utils/filesystemwatcher.h>
+#include <utils/fileutils.h>
+
+#include <QObject>
+
+namespace CMakeProjectManager {
+namespace Internal {
+
+namespace FileApiDetails {
+
+class ReplyObject
+{
+public:
+ QString kind;
+ QString file;
+ std::pair<int, int> version;
+};
+
+class ReplyFileContents
+{
+public:
+ QString generator;
+ QString cmakeExecutable;
+ QString cmakeRoot;
+
+ QVector<ReplyObject> replies;
+
+ QString jsonFile(const QString &kind, const QDir &replyDir) const;
+};
+
+class CMakeFileInfo
+{
+public:
+ QString path;
+ bool isCMake = false;
+ bool isCMakeListsDotTxt = false;
+ bool isExternal = false;
+ bool isGenerated = false;
+};
+
+class Directory
+{
+public:
+ QString buildPath;
+ QString sourcePath;
+ int parent = -1;
+ int project = -1;
+ std::vector<int> children;
+ std::vector<int> targets;
+ bool hasInstallRule = false;
+};
+
+class Project
+{
+public:
+ QString name;
+ int parent = -1;
+ std::vector<int> children;
+ std::vector<int> directories;
+ std::vector<int> targets;
+};
+
+class Target
+{
+public:
+ // From codemodel file:
+ QString name;
+ QString id;
+ int directory = -1;
+ int project = -1;
+ QString jsonFile;
+};
+
+class Configuration
+{
+public:
+ QString name;
+ std::vector<Directory> directories;
+ std::vector<Project> projects;
+ std::vector<Target> targets;
+};
+
+class InstallDestination
+{
+public:
+ QString path;
+ int backtrace;
+};
+
+class FragmentInfo
+{
+public:
+ QString fragment;
+ QString role;
+};
+
+class LinkInfo
+{
+public:
+ QString language;
+ std::vector<FragmentInfo> fragments;
+ bool isLto = false;
+ QString sysroot;
+};
+
+class ArchiveInfo
+{
+public:
+ std::vector<FragmentInfo> fragments;
+ bool isLto = false;
+};
+
+class DependencyInfo
+{
+public:
+ QString targetId;
+ int backtrace;
+};
+
+class SourceInfo
+{
+public:
+ QString path;
+ int compileGroup = -1;
+ int sourceGroup = -1;
+ int backtrace = -1;
+ bool isGenerated = false;
+};
+
+class IncludeInfo
+{
+public:
+ ProjectExplorer::HeaderPath path;
+ int backtrace;
+};
+
+class DefineInfo
+{
+public:
+ ProjectExplorer::Macro define;
+ int backtrace;
+};
+
+class CompileInfo
+{
+public:
+ std::vector<int> sources;
+ QString language;
+ QStringList fragments;
+ std::vector<IncludeInfo> includes;
+ std::vector<DefineInfo> defines;
+ QString sysroot;
+};
+
+class BacktraceNode
+{
+public:
+ int file = -1;
+ int line = -1;
+ int command = -1;
+ int parent = -1;
+};
+
+class BacktraceInfo
+{
+public:
+ std::vector<QString> commands;
+ std::vector<QString> files;
+ std::vector<BacktraceNode> nodes;
+};
+
+class TargetDetails
+{
+public:
+ QString name;
+ QString id;
+ QString type;
+ QString folderTargetProperty;
+ Utils::FilePath sourceDir;
+ Utils::FilePath buildDir;
+ int backtrace = -1;
+ bool isGeneratorProvided = false;
+ QString nameOnDisk;
+ QList<Utils::FilePath> artifacts;
+ QString installPrefix;
+ std::vector<InstallDestination> installDestination;
+ Utils::optional<LinkInfo> link;
+ Utils::optional<ArchiveInfo> archive;
+ std::vector<DependencyInfo> dependencies;
+ std::vector<SourceInfo> sources;
+ std::vector<QString> sourceGroups;
+ std::vector<CompileInfo> compileGroups;
+ BacktraceInfo backtraceGraph;
+};
+
+} // namespace FileApiDetails
+
+class FileApiData
+{
+public:
+ FileApiDetails::ReplyFileContents replyFile;
+ CMakeConfig cache;
+ std::vector<FileApiDetails::CMakeFileInfo> cmakeFiles;
+ std::vector<FileApiDetails::Configuration> codemodel;
+ std::vector<FileApiDetails::TargetDetails> targetDetails;
+};
+
+class FileApiParser : public QObject
+{
+ Q_OBJECT
+
+public:
+ FileApiParser(const Utils::FilePath &sourceDirectory, const Utils::FilePath &buildDirectory);
+ ~FileApiParser() final;
+
+ Utils::FilePath cmakeReplyDirectory() const;
+ QFileInfo scanForCMakeReplyFile() const;
+
+ static FileApiData parseData(const QFileInfo &replyFileInfo, QString &errorMessage);
+
+signals:
+ void dataAvailable() const;
+ void errorOccurred(const QString &message) const;
+ void dirty() const;
+
+private:
+ void setupCMakeFileApi() const;
+
+ const Utils::FilePath &m_sourceDirectory;
+ const Utils::FilePath &m_buildDirectory;
+
+ void replyDirectoryHasChanged(const QString &directory) const;
+ Utils::FileSystemWatcher m_watcher;
+};
+
+} // namespace Internal
+} // namespace CMakeProjectManager
diff --git a/src/plugins/cmakeprojectmanager/fileapireader.cpp b/src/plugins/cmakeprojectmanager/fileapireader.cpp
new file mode 100644
index 0000000000..957ebaaea0
--- /dev/null
+++ b/src/plugins/cmakeprojectmanager/fileapireader.cpp
@@ -0,0 +1,283 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** 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 "fileapireader.h"
+
+#include "cmakebuildconfiguration.h"
+#include "cmakeprojectconstants.h"
+#include "cmakeprojectmanager.h"
+#include "fileapidataextractor.h"
+#include "projecttreehelper.h"
+
+#include <coreplugin/editormanager/editormanager.h>
+#include <coreplugin/fileiconprovider.h>
+#include <coreplugin/messagemanager.h>
+#include <coreplugin/progressmanager/progressmanager.h>
+#include <projectexplorer/projectexplorer.h>
+#include <projectexplorer/projectexplorerconstants.h>
+#include <projectexplorer/task.h>
+#include <projectexplorer/taskhub.h>
+#include <projectexplorer/toolchain.h>
+
+#include <utils/algorithm.h>
+#include <utils/optional.h>
+#include <utils/qtcassert.h>
+#include <utils/runextensions.h>
+
+#include <QDateTime>
+#include <QLoggingCategory>
+
+using namespace ProjectExplorer;
+using namespace Utils;
+
+namespace CMakeProjectManager {
+namespace Internal {
+
+Q_LOGGING_CATEGORY(cmakeFileApiMode, "qtc.cmake.fileApiMode", QtWarningMsg);
+
+using namespace FileApiDetails;
+
+// --------------------------------------------------------------------
+// FileApiReader:
+// --------------------------------------------------------------------
+
+FileApiReader::FileApiReader()
+{
+ connect(Core::EditorManager::instance(),
+ &Core::EditorManager::aboutToSave,
+ this,
+ [this](const Core::IDocument *document) {
+ if (m_cmakeFiles.contains(document->filePath()) || !m_parameters.cmakeTool()
+ || !m_parameters.cmakeTool()->isAutoRun()) {
+ qCDebug(cmakeFileApiMode) << "FileApiReader: DIRTY SIGNAL";
+ emit dirty();
+ }
+ });
+}
+
+FileApiReader::~FileApiReader()
+{
+ stop();
+ resetData();
+}
+
+void FileApiReader::setParameters(const BuildDirParameters &p)
+{
+ qCDebug(cmakeFileApiMode)
+ << "\n\n\n\n\n=============================================================\n";
+
+ // Update:
+ m_parameters = p;
+ qCDebug(cmakeFileApiMode) << "Work directory:" << m_parameters.workDirectory.toUserOutput();
+
+ resetData();
+
+ m_fileApi = std::make_unique<FileApiParser>(m_parameters.sourceDirectory, m_parameters.workDirectory);
+ connect(m_fileApi.get(), &FileApiParser::dirty, this, [this]() {
+ if (!m_isParsing)
+ emit dirty();
+ });
+
+ qCDebug(cmakeFileApiMode) << "FileApiReader: IS READY NOW SIGNAL";
+ emit isReadyNow();
+}
+
+bool FileApiReader::isCompatible(const BuildDirParameters &p)
+{
+ const CMakeTool *cmakeTool = p.cmakeTool();
+ return cmakeTool && cmakeTool->hasFileApi();
+}
+
+void FileApiReader::resetData()
+{
+ m_cmakeFiles.clear();
+ if (!m_parameters.sourceDirectory.isEmpty())
+ m_cmakeFiles.insert(m_parameters.sourceDirectory.pathAppended("CMakeLists.txt"));
+
+ m_cache.clear();
+ m_buildTargets.clear();
+ m_projectParts.clear();
+ m_rootProjectNode.reset();
+ m_knownHeaders.clear();
+}
+
+void FileApiReader::parse(bool forceCMakeRun, bool forceConfiguration)
+{
+ qCDebug(cmakeFileApiMode) << "\n\nParse: ForceCMakeRun:" << forceCMakeRun
+ << " - forceConfiguration:" << forceConfiguration;
+ startState();
+
+ if (forceConfiguration) {
+ // Initial create:
+ qCDebug(cmakeFileApiMode) << "FileApiReader: Starting CMake with forced configuration.";
+ startCMakeState(
+ CMakeProcess::toArguments(m_parameters.configuration, m_parameters.expander));
+ // Keep m_isParsing enabled!
+ return;
+ }
+
+ const QFileInfo replyFi = m_fileApi->scanForCMakeReplyFile();
+ const bool mustUpdate = forceCMakeRun || !replyFi.exists() || m_cmakeFiles.isEmpty()
+ || anyOf(m_cmakeFiles, [&replyFi](const FilePath &f) {
+ return f.toFileInfo().lastModified() > replyFi.lastModified();
+ });
+
+ if (mustUpdate) {
+ qCDebug(cmakeFileApiMode) << "FileApiReader: Starting CMake with no arguments.";
+ startCMakeState(QStringList());
+ // Keep m_isParsing enabled!
+ return;
+ }
+
+ endState(replyFi);
+}
+
+void FileApiReader::stop()
+{
+ m_cmakeProcess.reset();
+}
+
+bool FileApiReader::isParsing() const
+{
+ return m_isParsing;
+}
+
+QList<CMakeBuildTarget> FileApiReader::takeBuildTargets(QString &errorMessage){
+ Q_UNUSED(errorMessage)
+
+ auto result = std::move(m_buildTargets);
+ m_buildTargets.clear();
+ return result;
+}
+
+CMakeConfig FileApiReader::takeParsedConfiguration(QString &errorMessage)
+{
+ Q_UNUSED(errorMessage)
+
+ CMakeConfig cache = m_cache;
+ m_cache.clear();
+ return cache;
+}
+
+std::unique_ptr<CMakeProjectNode> FileApiReader::generateProjectTree(
+ const QList<const FileNode *> &allFiles, QString &errorMessage)
+{
+ Q_UNUSED(errorMessage)
+
+ addHeaderNodes(m_rootProjectNode.get(), m_knownHeaders, allFiles);
+ return std::move(m_rootProjectNode);
+}
+
+CppTools::RawProjectParts FileApiReader::createRawProjectParts(QString &errorMessage)
+{
+ Q_UNUSED(errorMessage)
+
+ CppTools::RawProjectParts result = std::move(m_projectParts);
+ m_projectParts.clear();
+ return result;
+}
+
+void FileApiReader::startState()
+{
+ qCDebug(cmakeFileApiMode) << "FileApiReader: START STATE.";
+ QTC_ASSERT(!m_isParsing, return );
+ QTC_ASSERT(!m_future.has_value(), return );
+
+ m_isParsing = true;
+
+ qCDebug(cmakeFileApiMode) << "FileApiReader: CONFIGURATION STARTED SIGNAL";
+ emit configurationStarted();
+}
+
+void FileApiReader::endState(const QFileInfo &replyFi)
+{
+ qCDebug(cmakeFileApiMode) << "FileApiReader: END STATE.";
+ QTC_ASSERT(m_isParsing, return );
+ QTC_ASSERT(!m_future.has_value(), return );
+
+ const FilePath sourceDirectory = m_parameters.sourceDirectory;
+ const FilePath buildDirectory = m_parameters.workDirectory;
+
+ m_future = runAsync(ProjectExplorerPlugin::sharedThreadPool(),
+ [replyFi, sourceDirectory, buildDirectory]() {
+ auto result = std::make_unique<FileApiQtcData>();
+ FileApiData data = FileApiParser::parseData(replyFi,
+ result->errorMessage);
+ if (!result->errorMessage.isEmpty()) {
+ qWarning() << result->errorMessage;
+ return result.release();
+ }
+ *result = extractData(data, sourceDirectory, buildDirectory);
+ if (!result->errorMessage.isEmpty()) {
+ qWarning() << result->errorMessage;
+ }
+
+ return result.release();
+ });
+ onFinished(m_future.value(), this, [this](const QFuture<FileApiQtcData *> &f) {
+ std::unique_ptr<FileApiQtcData> value(f.result()); // Adopt the pointer again:-)
+
+ m_future = {};
+ m_isParsing = false;
+ m_cache = std::move(value->cache);
+ m_cmakeFiles = std::move(value->cmakeFiles);
+ m_buildTargets = std::move(value->buildTargets);
+ m_projectParts = std::move(value->projectParts);
+ m_rootProjectNode = std::move(value->rootProjectNode);
+ m_knownHeaders = std::move(value->knownHeaders);
+
+ if (value->errorMessage.isEmpty()) {
+ emit this->dataAvailable();
+ } else {
+ emit this->errorOccured(value->errorMessage);
+ }
+ });
+}
+
+void FileApiReader::startCMakeState(const QStringList &configurationArguments)
+{
+ qCDebug(cmakeFileApiMode) << "FileApiReader: START CMAKE STATE.";
+ QTC_ASSERT(!m_cmakeProcess, return );
+
+ m_cmakeProcess = std::make_unique<CMakeProcess>();
+
+ connect(m_cmakeProcess.get(), &CMakeProcess::finished, this, &FileApiReader::cmakeFinishedState);
+
+ qCDebug(cmakeFileApiMode) << ">>>>>> Running cmake with arguments:" << configurationArguments;
+ m_cmakeProcess->run(m_parameters, configurationArguments);
+}
+
+void FileApiReader::cmakeFinishedState(int code, QProcess::ExitStatus status)
+{
+ qCDebug(cmakeFileApiMode) << "FileApiReader: CMAKE FINISHED STATE.";
+
+ Q_UNUSED(code)
+ Q_UNUSED(status)
+
+ endState(m_fileApi->scanForCMakeReplyFile());
+}
+
+} // namespace Internal
+} // namespace CMakeProjectManager
diff --git a/src/plugins/cmakeprojectmanager/fileapireader.h b/src/plugins/cmakeprojectmanager/fileapireader.h
new file mode 100644
index 0000000000..b6e2218f0f
--- /dev/null
+++ b/src/plugins/cmakeprojectmanager/fileapireader.h
@@ -0,0 +1,96 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** 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 "builddirreader.h"
+#include "fileapiparser.h"
+
+#include "cmakeprocess.h"
+
+#include <utils/optional.h>
+
+#include <memory>
+
+#include <QFuture>
+
+namespace ProjectExplorer {
+class ProjectNode;
+}
+
+namespace CMakeProjectManager {
+namespace Internal {
+
+class FileApiQtcData;
+
+class FileApiReader : public BuildDirReader
+{
+ Q_OBJECT
+
+public:
+ FileApiReader();
+ ~FileApiReader() final;
+
+ void setParameters(const BuildDirParameters &p) final;
+
+ bool isCompatible(const BuildDirParameters &p) final;
+ void resetData() final;
+ void parse(bool forceCMakeRun, bool forceConfiguration) final;
+ void stop() final;
+
+ bool isParsing() const final;
+
+ QList<CMakeBuildTarget> takeBuildTargets(QString &errorMessage) final;
+ CMakeConfig takeParsedConfiguration(QString &errorMessage) final;
+ std::unique_ptr<CMakeProjectNode> generateProjectTree(
+ const QList<const ProjectExplorer::FileNode *> &allFiles, QString &errorMessage) final;
+ CppTools::RawProjectParts createRawProjectParts(QString &errorMessage) final;
+
+private:
+ void startState();
+ void endState(const QFileInfo &replyFi);
+ void startCMakeState(const QStringList &configurationArguments);
+ void cmakeFinishedState(int code, QProcess::ExitStatus status);
+
+ std::unique_ptr<CMakeProcess> m_cmakeProcess;
+
+ // cmake data:
+ CMakeConfig m_cache;
+ QSet<Utils::FilePath> m_cmakeFiles;
+ QList<CMakeBuildTarget> m_buildTargets;
+ CppTools::RawProjectParts m_projectParts;
+ std::unique_ptr<CMakeProjectNode> m_rootProjectNode;
+ QVector<ProjectExplorer::FileNode *> m_knownHeaders;
+
+ Utils::optional<QFuture<FileApiQtcData *>> m_future;
+
+ // Update related:
+ bool m_isParsing = false;
+
+ std::unique_ptr<FileApiParser> m_fileApi;
+};
+
+} // namespace Internal
+} // namespace CMakeProjectManager
diff --git a/src/plugins/cmakeprojectmanager/projecttreehelper.cpp b/src/plugins/cmakeprojectmanager/projecttreehelper.cpp
index c1a3abaaae..1bc86189d7 100644
--- a/src/plugins/cmakeprojectmanager/projecttreehelper.cpp
+++ b/src/plugins/cmakeprojectmanager/projecttreehelper.cpp
@@ -36,6 +36,16 @@ using namespace ProjectExplorer;
namespace CMakeProjectManager {
namespace Internal {
+std::unique_ptr<FolderNode> createCMakeVFolder(const Utils::FilePath &basePath,
+ int priority,
+ const QString &displayName)
+{
+ auto newFolder = std::make_unique<VirtualFolderNode>(basePath);
+ newFolder->setPriority(priority);
+ newFolder->setDisplayName(displayName);
+ return std::move(newFolder);
+}
+
void addCMakeVFolder(FolderNode *base,
const Utils::FilePath &basePath,
int priority,
@@ -46,9 +56,7 @@ void addCMakeVFolder(FolderNode *base,
return;
FolderNode *folder = base;
if (!displayName.isEmpty()) {
- auto newFolder = std::make_unique<VirtualFolderNode>(basePath);
- newFolder->setPriority(priority);
- newFolder->setDisplayName(displayName);
+ auto newFolder = createCMakeVFolder(basePath, priority, displayName);
folder = newFolder.get();
base->addNode(std::move(newFolder));
}
@@ -166,7 +174,7 @@ CMakeTargetNode *createTargetNode(const QHash<Utils::FilePath, ProjectNode *> &c
}
void addHeaderNodes(ProjectNode *root,
- const QList<FileNode *> knownHeaders,
+ const QVector<FileNode *> knownHeaders,
const QList<const FileNode *> &allFiles)
{
if (root->isEmpty())
diff --git a/src/plugins/cmakeprojectmanager/projecttreehelper.h b/src/plugins/cmakeprojectmanager/projecttreehelper.h
index 79eeecf0da..58266cd401 100644
--- a/src/plugins/cmakeprojectmanager/projecttreehelper.h
+++ b/src/plugins/cmakeprojectmanager/projecttreehelper.h
@@ -34,6 +34,10 @@
namespace CMakeProjectManager {
namespace Internal {
+std::unique_ptr<ProjectExplorer::FolderNode> createCMakeVFolder(const Utils::FilePath &basePath,
+ int priority,
+ const QString &displayName);
+
void addCMakeVFolder(ProjectExplorer::FolderNode *base,
const Utils::FilePath &basePath,
int priority,
@@ -63,7 +67,7 @@ CMakeTargetNode *createTargetNode(
const QString &displayName);
void addHeaderNodes(ProjectExplorer::ProjectNode *root,
- const QList<ProjectExplorer::FileNode *> knownHeaders,
+ const QVector<ProjectExplorer::FileNode *> knownHeaders,
const QList<const ProjectExplorer::FileNode *> &allFiles);
} // namespace Internal
diff --git a/src/plugins/cmakeprojectmanager/servermodereader.cpp b/src/plugins/cmakeprojectmanager/servermodereader.cpp
index 7dd06ef0ea..d4104dbe1d 100644
--- a/src/plugins/cmakeprojectmanager/servermodereader.cpp
+++ b/src/plugins/cmakeprojectmanager/servermodereader.cpp
@@ -229,11 +229,12 @@ CMakeConfig ServerModeReader::takeParsedConfiguration(QString &errorMessage)
return config;
}
-void ServerModeReader::generateProjectTree(CMakeProjectNode *root,
- const QList<const FileNode *> &allFiles,
+std::unique_ptr<CMakeProjectNode> ServerModeReader::generateProjectTree(const QList<const FileNode *> &allFiles,
QString &errorMessage)
{
Q_UNUSED(errorMessage)
+ auto root = std::make_unique<CMakeProjectNode>(m_parameters.sourceDirectory);
+
// Split up cmake inputs into useful chunks:
std::vector<std::unique_ptr<FileNode>> cmakeFilesSource;
std::vector<std::unique_ptr<FileNode>> cmakeFilesBuild;
@@ -259,20 +260,25 @@ void ServerModeReader::generateProjectTree(CMakeProjectNode *root,
if (topLevel)
root->setDisplayName(topLevel->name);
- QHash<Utils::FilePath, ProjectNode *> cmakeListsNodes
- = addCMakeLists(root, std::move(cmakeLists));
- QList<FileNode *> knownHeaders;
+ QHash<Utils::FilePath, ProjectNode *> cmakeListsNodes = addCMakeLists(root.get(),
+ std::move(cmakeLists));
+ QVector<FileNode *> knownHeaders;
addProjects(cmakeListsNodes, m_projects, knownHeaders);
- addHeaderNodes(root, knownHeaders, allFiles);
+ addHeaderNodes(root.get(), knownHeaders, allFiles);
if (cmakeFilesSource.size() > 0 || cmakeFilesBuild.size() > 0 || cmakeFilesOther.size() > 0)
- addCMakeInputs(root, m_parameters.sourceDirectory, m_parameters.workDirectory,
- std::move(cmakeFilesSource), std::move(cmakeFilesBuild),
+ addCMakeInputs(root.get(),
+ m_parameters.sourceDirectory,
+ m_parameters.workDirectory,
+ std::move(cmakeFilesSource),
+ std::move(cmakeFilesBuild),
std::move(cmakeFilesOther));
+
+ return root;
}
-CppTools::RawProjectParts ServerModeReader::createRawProjectParts(QString &errorMessage) const
+CppTools::RawProjectParts ServerModeReader::createRawProjectParts(QString &errorMessage)
{
Q_UNUSED(errorMessage)
CppTools::RawProjectParts rpps;
@@ -742,7 +748,7 @@ void ServerModeReader::fixTarget(ServerModeReader::Target *target) const
void ServerModeReader::addProjects(const QHash<Utils::FilePath, ProjectNode *> &cmakeListsNodes,
const QList<Project *> &projects,
- QList<FileNode *> &knownHeaderNodes)
+ QVector<FileNode *> &knownHeaderNodes)
{
for (const Project *p : projects) {
createProjectNode(cmakeListsNodes, p->sourceDirectory, p->name);
@@ -750,9 +756,10 @@ void ServerModeReader::addProjects(const QHash<Utils::FilePath, ProjectNode *> &
}
}
-void ServerModeReader::addTargets(const QHash<Utils::FilePath, ProjectExplorer::ProjectNode *> &cmakeListsNodes,
- const QList<Target *> &targets,
- QList<ProjectExplorer::FileNode *> &knownHeaderNodes)
+void ServerModeReader::addTargets(
+ const QHash<Utils::FilePath, ProjectExplorer::ProjectNode *> &cmakeListsNodes,
+ const QList<Target *> &targets,
+ QVector<ProjectExplorer::FileNode *> &knownHeaderNodes)
{
for (const Target *t : targets) {
CMakeTargetNode *tNode = createTargetNode(cmakeListsNodes, t->sourceDirectory, t->name);
@@ -796,7 +803,7 @@ void ServerModeReader::addFileGroups(ProjectNode *targetRoot,
const Utils::FilePath &sourceDirectory,
const Utils::FilePath &buildDirectory,
const QList<ServerModeReader::FileGroup *> &fileGroups,
- QList<FileNode *> &knownHeaderNodes)
+ QVector<FileNode *> &knownHeaderNodes)
{
std::vector<std::unique_ptr<FileNode>> toList;
QSet<Utils::FilePath> alreadyListed;
diff --git a/src/plugins/cmakeprojectmanager/servermodereader.h b/src/plugins/cmakeprojectmanager/servermodereader.h
index a3b29fd871..17737990f0 100644
--- a/src/plugins/cmakeprojectmanager/servermodereader.h
+++ b/src/plugins/cmakeprojectmanager/servermodereader.h
@@ -57,10 +57,9 @@ public:
QList<CMakeBuildTarget> takeBuildTargets(QString &errorMessage) final;
CMakeConfig takeParsedConfiguration(QString &errorMessage) final;
- void generateProjectTree(CMakeProjectNode *root,
- const QList<const ProjectExplorer::FileNode *> &allFiles,
- QString &errorMessage) final;
- CppTools::RawProjectParts createRawProjectParts(QString &errorMessage) const final;
+ std::unique_ptr<CMakeProjectNode> generateProjectTree(
+ const QList<const ProjectExplorer::FileNode *> &allFiles, QString &errorMessage) final;
+ CppTools::RawProjectParts createRawProjectParts(QString &errorMessage) final;
private:
void createNewServer();
@@ -148,14 +147,15 @@ private:
void addProjects(const QHash<Utils::FilePath, ProjectExplorer::ProjectNode *> &cmakeListsNodes,
const QList<Project *> &projects,
- QList<ProjectExplorer::FileNode *> &knownHeaderNodes);
+ QVector<ProjectExplorer::FileNode *> &knownHeaderNodes);
void addTargets(const QHash<Utils::FilePath, ProjectExplorer::ProjectNode *> &cmakeListsNodes,
const QList<Target *> &targets,
- QList<ProjectExplorer::FileNode *> &knownHeaderNodes);
+ QVector<ProjectExplorer::FileNode *> &knownHeaderNodes);
void addFileGroups(ProjectExplorer::ProjectNode *targetRoot,
const Utils::FilePath &sourceDirectory,
- const Utils::FilePath &buildDirectory, const QList<FileGroup *> &fileGroups,
- QList<ProjectExplorer::FileNode *> &knowHeaderNodes);
+ const Utils::FilePath &buildDirectory,
+ const QList<FileGroup *> &fileGroups,
+ QVector<ProjectExplorer::FileNode *> &knowHeaderNodes);
std::unique_ptr<ServerMode> m_cmakeServer;
std::unique_ptr<QFutureInterface<void>> m_future;
diff --git a/src/plugins/cmakeprojectmanager/tealeafreader.cpp b/src/plugins/cmakeprojectmanager/tealeafreader.cpp
index 2ed39cfb74..4e355a003c 100644
--- a/src/plugins/cmakeprojectmanager/tealeafreader.cpp
+++ b/src/plugins/cmakeprojectmanager/tealeafreader.cpp
@@ -236,14 +236,14 @@ CMakeConfig TeaLeafReader::takeParsedConfiguration(QString &errorMessage)
return result;
}
-void TeaLeafReader::generateProjectTree(CMakeProjectNode *root,
- const QList<const FileNode *> &allFiles,
- QString &errorMessage)
+std::unique_ptr<CMakeProjectNode> TeaLeafReader::generateProjectTree(
+ const QList<const FileNode *> &allFiles, QString &errorMessage)
{
Q_UNUSED(errorMessage)
if (m_files.size() == 0)
- return;
+ return {};
+ auto root = std::make_unique<CMakeProjectNode>(m_parameters.sourceDirectory);
root->setDisplayName(m_projectName);
// Delete no longer necessary file watcher based on m_cmakeFiles:
@@ -302,6 +302,8 @@ void TeaLeafReader::generateProjectTree(CMakeProjectNode *root,
return std::unique_ptr<FileNode>(fn->clone());
});
root->addNestedNodes(std::move(fileNodes), m_parameters.sourceDirectory);
+
+ return root;
}
static void processCMakeIncludes(const CMakeBuildTarget &cbt, const ToolChain *tc,
@@ -319,7 +321,7 @@ static void processCMakeIncludes(const CMakeBuildTarget &cbt, const ToolChain *t
}
}
-CppTools::RawProjectParts TeaLeafReader::createRawProjectParts(QString &errorMessage) const
+CppTools::RawProjectParts TeaLeafReader::createRawProjectParts(QString &errorMessage)
{
Q_UNUSED(errorMessage)
const ToolChain *tcCxx = ToolChainManager::findToolChain(m_parameters.cxxToolChainId);
diff --git a/src/plugins/cmakeprojectmanager/tealeafreader.h b/src/plugins/cmakeprojectmanager/tealeafreader.h
index d1ad3026eb..db64cbaf9a 100644
--- a/src/plugins/cmakeprojectmanager/tealeafreader.h
+++ b/src/plugins/cmakeprojectmanager/tealeafreader.h
@@ -58,10 +58,9 @@ public:
QList<CMakeBuildTarget> takeBuildTargets(QString &errorMessage) final;
CMakeConfig takeParsedConfiguration(QString &errorMessage) final;
- void generateProjectTree(CMakeProjectNode *root,
- const QList<const ProjectExplorer::FileNode *> &allFiles,
- QString &errorMessage) final;
- CppTools::RawProjectParts createRawProjectParts(QString &errorMessage) const final;
+ std::unique_ptr<CMakeProjectNode> generateProjectTree(
+ const QList<const ProjectExplorer::FileNode *> &allFiles, QString &errorMessage) final;
+ CppTools::RawProjectParts createRawProjectParts(QString &errorMessage) final;
private:
void extractData();