forked from qt-creator/qt-creator
CMake: Add initial fileapireader class
Change-Id: I620cba7cc1c2a5ac56789fa9770dce573c6b19cd Reviewed-by: Eike Ziller <eike.ziller@qt.io>
This commit is contained in:
@@ -37,6 +37,9 @@ add_qtc_plugin(CMakeProjectManager
|
|||||||
cmaketoolsettingsaccessor.cpp cmaketoolsettingsaccessor.h
|
cmaketoolsettingsaccessor.cpp cmaketoolsettingsaccessor.h
|
||||||
configmodel.cpp configmodel.h
|
configmodel.cpp configmodel.h
|
||||||
configmodelitemdelegate.cpp configmodelitemdelegate.h
|
configmodelitemdelegate.cpp configmodelitemdelegate.h
|
||||||
|
fileapidataextractor.cpp fileapidataextractor.h
|
||||||
|
fileapiparser.cpp fileapiparser.h
|
||||||
|
fileapireader.cpp fileapireader.h
|
||||||
projecttreehelper.cpp projecttreehelper.h
|
projecttreehelper.cpp projecttreehelper.h
|
||||||
servermode.cpp servermode.h
|
servermode.cpp servermode.h
|
||||||
servermodereader.cpp servermodereader.h
|
servermodereader.cpp servermodereader.h
|
||||||
|
@@ -302,14 +302,13 @@ void BuildDirManager::parse(int reparseParameters)
|
|||||||
reparseParameters & REPARSE_FORCE_CONFIGURATION);
|
reparseParameters & REPARSE_FORCE_CONFIGURATION);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BuildDirManager::generateProjectTree(CMakeProjectNode *root,
|
std::unique_ptr<CMakeProjectNode> BuildDirManager::generateProjectTree(
|
||||||
const QList<const FileNode *> &allFiles,
|
const QList<const FileNode *> &allFiles, QString &errorMessage) const
|
||||||
QString &errorMessage) const
|
|
||||||
{
|
{
|
||||||
QTC_ASSERT(!m_isHandlingError, return);
|
QTC_ASSERT(!m_isHandlingError, return {});
|
||||||
QTC_ASSERT(m_reader, 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
|
CppTools::RawProjectParts BuildDirManager::createRawProjectParts(QString &errorMessage) const
|
||||||
|
@@ -78,8 +78,7 @@ public:
|
|||||||
|
|
||||||
void parse(int reparseParameters);
|
void parse(int reparseParameters);
|
||||||
|
|
||||||
void generateProjectTree(CMakeProjectNode *root,
|
std::unique_ptr<CMakeProjectNode> generateProjectTree(const QList<const ProjectExplorer::FileNode *> &allFiles,
|
||||||
const QList<const ProjectExplorer::FileNode *> &allFiles,
|
|
||||||
QString &errorMessage) const;
|
QString &errorMessage) const;
|
||||||
CppTools::RawProjectParts createRawProjectParts(QString &errorMessage) const;
|
CppTools::RawProjectParts createRawProjectParts(QString &errorMessage) const;
|
||||||
|
|
||||||
|
@@ -25,6 +25,7 @@
|
|||||||
|
|
||||||
#include "builddirreader.h"
|
#include "builddirreader.h"
|
||||||
|
|
||||||
|
#include "fileapireader.h"
|
||||||
#include "servermodereader.h"
|
#include "servermodereader.h"
|
||||||
#include "tealeafreader.h"
|
#include "tealeafreader.h"
|
||||||
|
|
||||||
@@ -43,6 +44,8 @@ std::unique_ptr<BuildDirReader> BuildDirReader::createReader(const BuildDirParam
|
|||||||
{
|
{
|
||||||
CMakeTool *cmake = p.cmakeTool();
|
CMakeTool *cmake = p.cmakeTool();
|
||||||
QTC_ASSERT(p.isValid() && cmake, return {});
|
QTC_ASSERT(p.isValid() && cmake, return {});
|
||||||
|
if (cmake->hasFileApi())
|
||||||
|
return std::make_unique<FileApiReader>();
|
||||||
if (cmake->hasServerMode())
|
if (cmake->hasServerMode())
|
||||||
return std::make_unique<ServerModeReader>();
|
return std::make_unique<ServerModeReader>();
|
||||||
return std::make_unique<TeaLeafReader>();
|
return std::make_unique<TeaLeafReader>();
|
||||||
|
@@ -64,10 +64,10 @@ public:
|
|||||||
|
|
||||||
virtual QList<CMakeBuildTarget> takeBuildTargets(QString &errorMessage) = 0;
|
virtual QList<CMakeBuildTarget> takeBuildTargets(QString &errorMessage) = 0;
|
||||||
virtual CMakeConfig takeParsedConfiguration(QString &errorMessage) = 0;
|
virtual CMakeConfig takeParsedConfiguration(QString &errorMessage) = 0;
|
||||||
virtual void generateProjectTree(CMakeProjectNode *root,
|
virtual std::unique_ptr<CMakeProjectNode> generateProjectTree(
|
||||||
const QList<const ProjectExplorer::FileNode *> &allFiles,
|
const QList<const ProjectExplorer::FileNode *> &allFiles, QString &errorMessage)
|
||||||
QString &errorMessage) = 0;
|
= 0;
|
||||||
virtual CppTools::RawProjectParts createRawProjectParts(QString &errorMessage) const = 0;
|
virtual CppTools::RawProjectParts createRawProjectParts(QString &errorMessage) = 0;
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void isReadyNow() const;
|
void isReadyNow() const;
|
||||||
|
@@ -401,9 +401,8 @@ CMakeProject::generateProjectTree(const QList<const FileNode *> &allFiles) const
|
|||||||
if (m_buildDirManager.isParsing())
|
if (m_buildDirManager.isParsing())
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
auto root = std::make_unique<CMakeProjectNode>(projectDirectory());
|
|
||||||
QString errorMessage;
|
QString errorMessage;
|
||||||
m_buildDirManager.generateProjectTree(root.get(), allFiles, errorMessage);
|
auto root = m_buildDirManager.generateProjectTree(allFiles, errorMessage);
|
||||||
checkAndReportError(errorMessage);
|
checkAndReportError(errorMessage);
|
||||||
return root;
|
return root;
|
||||||
}
|
}
|
||||||
|
@@ -34,6 +34,9 @@ HEADERS = builddirmanager.h \
|
|||||||
cmakespecificsettingspage.h \
|
cmakespecificsettingspage.h \
|
||||||
configmodel.h \
|
configmodel.h \
|
||||||
configmodelitemdelegate.h \
|
configmodelitemdelegate.h \
|
||||||
|
fileapidataextractor.h \
|
||||||
|
fileapiparser.h \
|
||||||
|
fileapireader.h \
|
||||||
projecttreehelper.h \
|
projecttreehelper.h \
|
||||||
servermode.h \
|
servermode.h \
|
||||||
servermodereader.h \
|
servermodereader.h \
|
||||||
@@ -69,6 +72,9 @@ SOURCES = builddirmanager.cpp \
|
|||||||
cmakespecificsettingspage.cpp \
|
cmakespecificsettingspage.cpp \
|
||||||
configmodel.cpp \
|
configmodel.cpp \
|
||||||
configmodelitemdelegate.cpp \
|
configmodelitemdelegate.cpp \
|
||||||
|
fileapidataextractor.cpp \
|
||||||
|
fileapiparser.cpp \
|
||||||
|
fileapireader.cpp \
|
||||||
projecttreehelper.cpp \
|
projecttreehelper.cpp \
|
||||||
servermode.cpp \
|
servermode.cpp \
|
||||||
servermodereader.cpp \
|
servermodereader.cpp \
|
||||||
|
@@ -84,6 +84,12 @@ QtcPlugin {
|
|||||||
"configmodel.h",
|
"configmodel.h",
|
||||||
"configmodelitemdelegate.cpp",
|
"configmodelitemdelegate.cpp",
|
||||||
"configmodelitemdelegate.h",
|
"configmodelitemdelegate.h",
|
||||||
|
"fileapidataextractor.cpp",
|
||||||
|
"fileapidataextractor.h",
|
||||||
|
"fileapiparser.cpp",
|
||||||
|
"fileapiparser.h",
|
||||||
|
"fileapireader.cpp",
|
||||||
|
"fileapireader.h",
|
||||||
"projecttreehelper.cpp",
|
"projecttreehelper.cpp",
|
||||||
"projecttreehelper.h",
|
"projecttreehelper.h",
|
||||||
"servermode.cpp",
|
"servermode.cpp",
|
||||||
|
558
src/plugins/cmakeprojectmanager/fileapidataextractor.cpp
Normal file
558
src/plugins/cmakeprojectmanager/fileapidataextractor.cpp
Normal file
@@ -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
|
58
src/plugins/cmakeprojectmanager/fileapidataextractor.h
Normal file
58
src/plugins/cmakeprojectmanager/fileapidataextractor.h
Normal file
@@ -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
|
947
src/plugins/cmakeprojectmanager/fileapiparser.cpp
Normal file
947
src/plugins/cmakeprojectmanager/fileapiparser.cpp
Normal file
@@ -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
|
269
src/plugins/cmakeprojectmanager/fileapiparser.h
Normal file
269
src/plugins/cmakeprojectmanager/fileapiparser.h
Normal file
@@ -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
|
283
src/plugins/cmakeprojectmanager/fileapireader.cpp
Normal file
283
src/plugins/cmakeprojectmanager/fileapireader.cpp
Normal file
@@ -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
|
96
src/plugins/cmakeprojectmanager/fileapireader.h
Normal file
96
src/plugins/cmakeprojectmanager/fileapireader.h
Normal file
@@ -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
|
@@ -36,6 +36,16 @@ using namespace ProjectExplorer;
|
|||||||
namespace CMakeProjectManager {
|
namespace CMakeProjectManager {
|
||||||
namespace Internal {
|
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,
|
void addCMakeVFolder(FolderNode *base,
|
||||||
const Utils::FilePath &basePath,
|
const Utils::FilePath &basePath,
|
||||||
int priority,
|
int priority,
|
||||||
@@ -46,9 +56,7 @@ void addCMakeVFolder(FolderNode *base,
|
|||||||
return;
|
return;
|
||||||
FolderNode *folder = base;
|
FolderNode *folder = base;
|
||||||
if (!displayName.isEmpty()) {
|
if (!displayName.isEmpty()) {
|
||||||
auto newFolder = std::make_unique<VirtualFolderNode>(basePath);
|
auto newFolder = createCMakeVFolder(basePath, priority, displayName);
|
||||||
newFolder->setPriority(priority);
|
|
||||||
newFolder->setDisplayName(displayName);
|
|
||||||
folder = newFolder.get();
|
folder = newFolder.get();
|
||||||
base->addNode(std::move(newFolder));
|
base->addNode(std::move(newFolder));
|
||||||
}
|
}
|
||||||
@@ -166,7 +174,7 @@ CMakeTargetNode *createTargetNode(const QHash<Utils::FilePath, ProjectNode *> &c
|
|||||||
}
|
}
|
||||||
|
|
||||||
void addHeaderNodes(ProjectNode *root,
|
void addHeaderNodes(ProjectNode *root,
|
||||||
const QList<FileNode *> knownHeaders,
|
const QVector<FileNode *> knownHeaders,
|
||||||
const QList<const FileNode *> &allFiles)
|
const QList<const FileNode *> &allFiles)
|
||||||
{
|
{
|
||||||
if (root->isEmpty())
|
if (root->isEmpty())
|
||||||
|
@@ -34,6 +34,10 @@
|
|||||||
namespace CMakeProjectManager {
|
namespace CMakeProjectManager {
|
||||||
namespace Internal {
|
namespace Internal {
|
||||||
|
|
||||||
|
std::unique_ptr<ProjectExplorer::FolderNode> createCMakeVFolder(const Utils::FilePath &basePath,
|
||||||
|
int priority,
|
||||||
|
const QString &displayName);
|
||||||
|
|
||||||
void addCMakeVFolder(ProjectExplorer::FolderNode *base,
|
void addCMakeVFolder(ProjectExplorer::FolderNode *base,
|
||||||
const Utils::FilePath &basePath,
|
const Utils::FilePath &basePath,
|
||||||
int priority,
|
int priority,
|
||||||
@@ -63,7 +67,7 @@ CMakeTargetNode *createTargetNode(
|
|||||||
const QString &displayName);
|
const QString &displayName);
|
||||||
|
|
||||||
void addHeaderNodes(ProjectExplorer::ProjectNode *root,
|
void addHeaderNodes(ProjectExplorer::ProjectNode *root,
|
||||||
const QList<ProjectExplorer::FileNode *> knownHeaders,
|
const QVector<ProjectExplorer::FileNode *> knownHeaders,
|
||||||
const QList<const ProjectExplorer::FileNode *> &allFiles);
|
const QList<const ProjectExplorer::FileNode *> &allFiles);
|
||||||
|
|
||||||
} // namespace Internal
|
} // namespace Internal
|
||||||
|
@@ -229,11 +229,12 @@ CMakeConfig ServerModeReader::takeParsedConfiguration(QString &errorMessage)
|
|||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ServerModeReader::generateProjectTree(CMakeProjectNode *root,
|
std::unique_ptr<CMakeProjectNode> ServerModeReader::generateProjectTree(const QList<const FileNode *> &allFiles,
|
||||||
const QList<const FileNode *> &allFiles,
|
|
||||||
QString &errorMessage)
|
QString &errorMessage)
|
||||||
{
|
{
|
||||||
Q_UNUSED(errorMessage)
|
Q_UNUSED(errorMessage)
|
||||||
|
auto root = std::make_unique<CMakeProjectNode>(m_parameters.sourceDirectory);
|
||||||
|
|
||||||
// Split up cmake inputs into useful chunks:
|
// Split up cmake inputs into useful chunks:
|
||||||
std::vector<std::unique_ptr<FileNode>> cmakeFilesSource;
|
std::vector<std::unique_ptr<FileNode>> cmakeFilesSource;
|
||||||
std::vector<std::unique_ptr<FileNode>> cmakeFilesBuild;
|
std::vector<std::unique_ptr<FileNode>> cmakeFilesBuild;
|
||||||
@@ -259,20 +260,25 @@ void ServerModeReader::generateProjectTree(CMakeProjectNode *root,
|
|||||||
if (topLevel)
|
if (topLevel)
|
||||||
root->setDisplayName(topLevel->name);
|
root->setDisplayName(topLevel->name);
|
||||||
|
|
||||||
QHash<Utils::FilePath, ProjectNode *> cmakeListsNodes
|
QHash<Utils::FilePath, ProjectNode *> cmakeListsNodes = addCMakeLists(root.get(),
|
||||||
= addCMakeLists(root, std::move(cmakeLists));
|
std::move(cmakeLists));
|
||||||
QList<FileNode *> knownHeaders;
|
QVector<FileNode *> knownHeaders;
|
||||||
addProjects(cmakeListsNodes, m_projects, 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)
|
if (cmakeFilesSource.size() > 0 || cmakeFilesBuild.size() > 0 || cmakeFilesOther.size() > 0)
|
||||||
addCMakeInputs(root, m_parameters.sourceDirectory, m_parameters.workDirectory,
|
addCMakeInputs(root.get(),
|
||||||
std::move(cmakeFilesSource), std::move(cmakeFilesBuild),
|
m_parameters.sourceDirectory,
|
||||||
|
m_parameters.workDirectory,
|
||||||
|
std::move(cmakeFilesSource),
|
||||||
|
std::move(cmakeFilesBuild),
|
||||||
std::move(cmakeFilesOther));
|
std::move(cmakeFilesOther));
|
||||||
|
|
||||||
|
return root;
|
||||||
}
|
}
|
||||||
|
|
||||||
CppTools::RawProjectParts ServerModeReader::createRawProjectParts(QString &errorMessage) const
|
CppTools::RawProjectParts ServerModeReader::createRawProjectParts(QString &errorMessage)
|
||||||
{
|
{
|
||||||
Q_UNUSED(errorMessage)
|
Q_UNUSED(errorMessage)
|
||||||
CppTools::RawProjectParts rpps;
|
CppTools::RawProjectParts rpps;
|
||||||
@@ -742,7 +748,7 @@ void ServerModeReader::fixTarget(ServerModeReader::Target *target) const
|
|||||||
|
|
||||||
void ServerModeReader::addProjects(const QHash<Utils::FilePath, ProjectNode *> &cmakeListsNodes,
|
void ServerModeReader::addProjects(const QHash<Utils::FilePath, ProjectNode *> &cmakeListsNodes,
|
||||||
const QList<Project *> &projects,
|
const QList<Project *> &projects,
|
||||||
QList<FileNode *> &knownHeaderNodes)
|
QVector<FileNode *> &knownHeaderNodes)
|
||||||
{
|
{
|
||||||
for (const Project *p : projects) {
|
for (const Project *p : projects) {
|
||||||
createProjectNode(cmakeListsNodes, p->sourceDirectory, p->name);
|
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,
|
void ServerModeReader::addTargets(
|
||||||
|
const QHash<Utils::FilePath, ProjectExplorer::ProjectNode *> &cmakeListsNodes,
|
||||||
const QList<Target *> &targets,
|
const QList<Target *> &targets,
|
||||||
QList<ProjectExplorer::FileNode *> &knownHeaderNodes)
|
QVector<ProjectExplorer::FileNode *> &knownHeaderNodes)
|
||||||
{
|
{
|
||||||
for (const Target *t : targets) {
|
for (const Target *t : targets) {
|
||||||
CMakeTargetNode *tNode = createTargetNode(cmakeListsNodes, t->sourceDirectory, t->name);
|
CMakeTargetNode *tNode = createTargetNode(cmakeListsNodes, t->sourceDirectory, t->name);
|
||||||
@@ -796,7 +803,7 @@ void ServerModeReader::addFileGroups(ProjectNode *targetRoot,
|
|||||||
const Utils::FilePath &sourceDirectory,
|
const Utils::FilePath &sourceDirectory,
|
||||||
const Utils::FilePath &buildDirectory,
|
const Utils::FilePath &buildDirectory,
|
||||||
const QList<ServerModeReader::FileGroup *> &fileGroups,
|
const QList<ServerModeReader::FileGroup *> &fileGroups,
|
||||||
QList<FileNode *> &knownHeaderNodes)
|
QVector<FileNode *> &knownHeaderNodes)
|
||||||
{
|
{
|
||||||
std::vector<std::unique_ptr<FileNode>> toList;
|
std::vector<std::unique_ptr<FileNode>> toList;
|
||||||
QSet<Utils::FilePath> alreadyListed;
|
QSet<Utils::FilePath> alreadyListed;
|
||||||
|
@@ -57,10 +57,9 @@ public:
|
|||||||
|
|
||||||
QList<CMakeBuildTarget> takeBuildTargets(QString &errorMessage) final;
|
QList<CMakeBuildTarget> takeBuildTargets(QString &errorMessage) final;
|
||||||
CMakeConfig takeParsedConfiguration(QString &errorMessage) final;
|
CMakeConfig takeParsedConfiguration(QString &errorMessage) final;
|
||||||
void generateProjectTree(CMakeProjectNode *root,
|
std::unique_ptr<CMakeProjectNode> generateProjectTree(
|
||||||
const QList<const ProjectExplorer::FileNode *> &allFiles,
|
const QList<const ProjectExplorer::FileNode *> &allFiles, QString &errorMessage) final;
|
||||||
QString &errorMessage) final;
|
CppTools::RawProjectParts createRawProjectParts(QString &errorMessage) final;
|
||||||
CppTools::RawProjectParts createRawProjectParts(QString &errorMessage) const final;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void createNewServer();
|
void createNewServer();
|
||||||
@@ -148,14 +147,15 @@ private:
|
|||||||
|
|
||||||
void addProjects(const QHash<Utils::FilePath, ProjectExplorer::ProjectNode *> &cmakeListsNodes,
|
void addProjects(const QHash<Utils::FilePath, ProjectExplorer::ProjectNode *> &cmakeListsNodes,
|
||||||
const QList<Project *> &projects,
|
const QList<Project *> &projects,
|
||||||
QList<ProjectExplorer::FileNode *> &knownHeaderNodes);
|
QVector<ProjectExplorer::FileNode *> &knownHeaderNodes);
|
||||||
void addTargets(const QHash<Utils::FilePath, ProjectExplorer::ProjectNode *> &cmakeListsNodes,
|
void addTargets(const QHash<Utils::FilePath, ProjectExplorer::ProjectNode *> &cmakeListsNodes,
|
||||||
const QList<Target *> &targets,
|
const QList<Target *> &targets,
|
||||||
QList<ProjectExplorer::FileNode *> &knownHeaderNodes);
|
QVector<ProjectExplorer::FileNode *> &knownHeaderNodes);
|
||||||
void addFileGroups(ProjectExplorer::ProjectNode *targetRoot,
|
void addFileGroups(ProjectExplorer::ProjectNode *targetRoot,
|
||||||
const Utils::FilePath &sourceDirectory,
|
const Utils::FilePath &sourceDirectory,
|
||||||
const Utils::FilePath &buildDirectory, const QList<FileGroup *> &fileGroups,
|
const Utils::FilePath &buildDirectory,
|
||||||
QList<ProjectExplorer::FileNode *> &knowHeaderNodes);
|
const QList<FileGroup *> &fileGroups,
|
||||||
|
QVector<ProjectExplorer::FileNode *> &knowHeaderNodes);
|
||||||
|
|
||||||
std::unique_ptr<ServerMode> m_cmakeServer;
|
std::unique_ptr<ServerMode> m_cmakeServer;
|
||||||
std::unique_ptr<QFutureInterface<void>> m_future;
|
std::unique_ptr<QFutureInterface<void>> m_future;
|
||||||
|
@@ -236,14 +236,14 @@ CMakeConfig TeaLeafReader::takeParsedConfiguration(QString &errorMessage)
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TeaLeafReader::generateProjectTree(CMakeProjectNode *root,
|
std::unique_ptr<CMakeProjectNode> TeaLeafReader::generateProjectTree(
|
||||||
const QList<const FileNode *> &allFiles,
|
const QList<const FileNode *> &allFiles, QString &errorMessage)
|
||||||
QString &errorMessage)
|
|
||||||
{
|
{
|
||||||
Q_UNUSED(errorMessage)
|
Q_UNUSED(errorMessage)
|
||||||
if (m_files.size() == 0)
|
if (m_files.size() == 0)
|
||||||
return;
|
return {};
|
||||||
|
|
||||||
|
auto root = std::make_unique<CMakeProjectNode>(m_parameters.sourceDirectory);
|
||||||
root->setDisplayName(m_projectName);
|
root->setDisplayName(m_projectName);
|
||||||
|
|
||||||
// Delete no longer necessary file watcher based on m_cmakeFiles:
|
// 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());
|
return std::unique_ptr<FileNode>(fn->clone());
|
||||||
});
|
});
|
||||||
root->addNestedNodes(std::move(fileNodes), m_parameters.sourceDirectory);
|
root->addNestedNodes(std::move(fileNodes), m_parameters.sourceDirectory);
|
||||||
|
|
||||||
|
return root;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void processCMakeIncludes(const CMakeBuildTarget &cbt, const ToolChain *tc,
|
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)
|
Q_UNUSED(errorMessage)
|
||||||
const ToolChain *tcCxx = ToolChainManager::findToolChain(m_parameters.cxxToolChainId);
|
const ToolChain *tcCxx = ToolChainManager::findToolChain(m_parameters.cxxToolChainId);
|
||||||
|
@@ -58,10 +58,9 @@ public:
|
|||||||
|
|
||||||
QList<CMakeBuildTarget> takeBuildTargets(QString &errorMessage) final;
|
QList<CMakeBuildTarget> takeBuildTargets(QString &errorMessage) final;
|
||||||
CMakeConfig takeParsedConfiguration(QString &errorMessage) final;
|
CMakeConfig takeParsedConfiguration(QString &errorMessage) final;
|
||||||
void generateProjectTree(CMakeProjectNode *root,
|
std::unique_ptr<CMakeProjectNode> generateProjectTree(
|
||||||
const QList<const ProjectExplorer::FileNode *> &allFiles,
|
const QList<const ProjectExplorer::FileNode *> &allFiles, QString &errorMessage) final;
|
||||||
QString &errorMessage) final;
|
CppTools::RawProjectParts createRawProjectParts(QString &errorMessage) final;
|
||||||
CppTools::RawProjectParts createRawProjectParts(QString &errorMessage) const final;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void extractData();
|
void extractData();
|
||||||
|
Reference in New Issue
Block a user