forked from qt-creator/qt-creator
Just use the target name as buildkey. This is unique in cmake projects, so there is no need to mangle the source directory into the whole thing. This is a problem since different readers might report different source directories. That will then result in RunConfigurations getting duplicated after switching the reader types. Task-number: QTCREATORBUG-22129 Change-Id: I849ab68f221d732341e98faa9a4e757d3a495b2a Reviewed-by: hjk <hjk@qt.io>
559 lines
22 KiB
C++
559 lines
22 KiB
C++
/****************************************************************************
|
|
**
|
|
** 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(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,
|
|
QSet<FilePath> &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.insert(node->filePath());
|
|
|
|
// 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,
|
|
QSet<FilePath> &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>, QSet<FilePath>> generateRootProjectNode(
|
|
PreprocessedData &data, const FilePath &sourceDirectory, const FilePath &buildDirectory)
|
|
{
|
|
std::pair<std::unique_ptr<CMakeProjectNode>, QSet<FilePath>> 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...
|
|
|
|
QSet<FilePath> 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
|