aboutsummaryrefslogtreecommitdiffstats
path: root/src/plugins/cmakeprojectmanager/servermodereader.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/cmakeprojectmanager/servermodereader.cpp')
-rw-r--r--src/plugins/cmakeprojectmanager/servermodereader.cpp935
1 files changed, 0 insertions, 935 deletions
diff --git a/src/plugins/cmakeprojectmanager/servermodereader.cpp b/src/plugins/cmakeprojectmanager/servermodereader.cpp
deleted file mode 100644
index f86081bd38..0000000000
--- a/src/plugins/cmakeprojectmanager/servermodereader.cpp
+++ /dev/null
@@ -1,935 +0,0 @@
-/****************************************************************************
-**
-** 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 "servermodereader.h"
-
-#include "cmakebuildconfiguration.h"
-#include "cmakeprojectconstants.h"
-#include "cmakeprojectmanager.h"
-#include "cmakeprojectnodes.h"
-#include "servermode.h"
-#include "projecttreehelper.h"
-
-#include <coreplugin/editormanager/editormanager.h>
-#include <coreplugin/messagemanager.h>
-#include <coreplugin/progressmanager/progressmanager.h>
-#include <projectexplorer/projectexplorerconstants.h>
-#include <projectexplorer/toolchain.h>
-#include <projectexplorer/task.h>
-#include <projectexplorer/taskhub.h>
-
-#include <utils/algorithm.h>
-#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
-
-#include <QVector>
-
-using namespace ProjectExplorer;
-using namespace Utils;
-
-namespace CMakeProjectManager {
-namespace Internal {
-
-const char CACHE_TYPE[] = "cache";
-const char CODEMODEL_TYPE[] = "codemodel";
-const char CONFIGURE_TYPE[] = "configure";
-const char CMAKE_INPUTS_TYPE[] = "cmakeInputs";
-const char COMPUTE_TYPE[] = "compute";
-
-const char BACKTRACE_KEY[] = "backtrace";
-const char LINE_KEY[] = "line";
-const char NAME_KEY[] = "name";
-const char PATH_KEY[] = "path";
-const char SOURCE_DIRECTORY_KEY[] = "sourceDirectory";
-const char SOURCES_KEY[] = "sources";
-
-const int MAX_PROGRESS = 1400;
-
-// --------------------------------------------------------------------
-// ServerModeReader:
-// --------------------------------------------------------------------
-
-ServerModeReader::ServerModeReader()
-{
- connect(&m_parser, &CMakeParser::addOutput,
- this, [](const QString &m) { Core::MessageManager::write(m); });
- connect(&m_parser, &CMakeParser::addTask, this, [this](const Task &t) {
- Task editable(t);
- if (!editable.file.isEmpty()) {
- QDir srcDir(m_parameters.sourceDirectory.toString());
- editable.file = FilePath::fromString(srcDir.absoluteFilePath(editable.file.toString()));
- }
- TaskHub::addTask(editable);
- });
-}
-
-ServerModeReader::~ServerModeReader()
-{
- stop();
-}
-
-void ServerModeReader::setParameters(const BuildDirParameters &p)
-{
- CMakeTool *cmake = p.cmakeTool();
- QTC_ASSERT(cmake, return);
-
- m_parameters = p;
-
- m_parser.setSourceDirectory(m_parameters.sourceDirectory.toString());
- createNewServer();
-}
-
-bool ServerModeReader::isCompatible(const BuildDirParameters &p)
-{
- CMakeTool *newCmake = p.cmakeTool();
- if (newCmake->readerType() != CMakeTool::FileApi)
- return false;
-
- CMakeTool *oldCmake = m_parameters.cmakeTool();
- if (!newCmake || !oldCmake)
- return false;
-
- // Server mode connection got lost, reset...
- if (!oldCmake && oldCmake->cmakeExecutable().isEmpty() && !m_cmakeServer)
- return false;
-
- return newCmake->hasServerMode()
- && newCmake->cmakeExecutable() == oldCmake->cmakeExecutable()
- && p.environment == m_parameters.environment
- && p.generator == m_parameters.generator
- && p.extraGenerator == m_parameters.extraGenerator
- && p.platform == m_parameters.platform
- && p.toolset == m_parameters.toolset
- && p.sourceDirectory == m_parameters.sourceDirectory
- && p.workDirectory == m_parameters.workDirectory;
-}
-
-void ServerModeReader::resetData()
-{
- m_cmakeConfiguration.clear();
- // m_cmakeFiles: Keep these!
- m_cmakeInputsFileNodes.clear();
- qDeleteAll(m_projects); // Also deletes targets and filegroups that are its children!
- m_projects.clear();
- m_targets.clear();
- m_fileGroups.clear();
-}
-
-void ServerModeReader::parse(bool forceCMakeRun, bool forceConfiguration)
-{
- emit configurationStarted();
-
- QTC_ASSERT(m_cmakeServer, return);
- QVariantMap extra;
-
- bool delayConfigurationRun = false;
- if (forceCMakeRun && m_cmakeServer->isConnected()) {
- createNewServer();
- delayConfigurationRun = true;
- }
-
- if (forceConfiguration) {
- QStringList cacheArguments = transform(m_parameters.configuration,
- [this](const CMakeConfigItem &i) {
- return i.toArgument(m_parameters.expander);
- });
- Core::MessageManager::write(tr("Starting to parse CMake project, using: \"%1\".")
- .arg(cacheArguments.join("\", \"")));
- cacheArguments.prepend(QString()); // Work around a bug in CMake 3.7.0 and 3.7.1 where
- // the first argument gets lost!
- extra.insert("cacheArguments", QVariant(cacheArguments));
- } else {
- Core::MessageManager::write(tr("Starting to parse CMake project."));
- }
-
- m_future.reset(new QFutureInterface<void>());
- m_future->setProgressRange(0, MAX_PROGRESS);
- m_progressStepMinimum = 0;
- m_progressStepMaximum = 1000;
- Core::ProgressManager::addTask(m_future->future(),
- tr("Configuring \"%1\"").arg(m_parameters.projectName),
- "CMake.Configure");
-
- if (!delayConfigurationRun) {
- sendConfigureRequest(extra);
- } else {
- m_delayedConfigurationData = extra;
- }
-}
-
-void ServerModeReader::stop()
-{
- if (m_future) {
- m_future->reportCanceled();
- m_future->reportFinished();
- m_future.reset();
- }
- m_parser.flush();
-}
-
-bool ServerModeReader::isParsing() const
-{
- return static_cast<bool>(m_future);
-}
-
-QList<CMakeBuildTarget> ServerModeReader::takeBuildTargets(QString &errorMessage)
-{
- Q_UNUSED(errorMessage)
- QDir topSourceDir(m_parameters.sourceDirectory.toString());
- const QList<CMakeBuildTarget> result
- = transform(m_targets, [&topSourceDir](const Target *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"
- || t->type == "INTERFACE_LIBRARY")
- type = DynamicLibraryType;
- else
- type = UtilityType;
- ct.targetType = type;
- if (t->artifacts.isEmpty()) {
- ct.workingDirectory = t->buildDirectory;
- } else {
- ct.workingDirectory = Utils::FilePath::fromString(
- t->artifacts.at(0).toFileInfo().absolutePath());
- }
- ct.sourceDirectory = FilePath::fromString(
- QDir::cleanPath(topSourceDir.absoluteFilePath(t->sourceDirectory.toString())));
- return ct;
- });
- m_targets.clear();
- return result;
-}
-
-CMakeConfig ServerModeReader::takeParsedConfiguration(QString &errorMessage)
-{
- Q_UNUSED(errorMessage)
- CMakeConfig config = m_cmakeConfiguration;
- m_cmakeConfiguration.clear();
- return config;
-}
-
-std::unique_ptr<CMakeProjectNode> ServerModeReader::generateProjectTree(const QList<const FileNode *> &allFiles,
- QString &errorMessage)
-{
- Q_UNUSED(errorMessage)
- auto root = std::make_unique<CMakeProjectNode>(m_parameters.sourceDirectory);
-
- // Split up cmake inputs into useful chunks:
- std::vector<std::unique_ptr<FileNode>> cmakeFilesSource;
- std::vector<std::unique_ptr<FileNode>> cmakeFilesBuild;
- std::vector<std::unique_ptr<FileNode>> cmakeFilesOther;
- std::vector<std::unique_ptr<FileNode>> cmakeLists;
-
- for (std::unique_ptr<FileNode> &fn : m_cmakeInputsFileNodes) {
- const FilePath path = fn->filePath();
- if (path.fileName().compare("CMakeLists.txt", HostOsInfo::fileNameCaseSensitivity()) == 0)
- cmakeLists.emplace_back(std::move(fn));
- else if (path.isChildOf(m_parameters.workDirectory))
- cmakeFilesBuild.emplace_back(std::move(fn));
- else if (path.isChildOf(m_parameters.sourceDirectory))
- cmakeFilesSource.emplace_back(std::move(fn));
- else
- cmakeFilesOther.emplace_back(std::move(fn));
- }
- m_cmakeInputsFileNodes.clear(); // Clean out, they are not going to be used anymore!
-
- const Project *topLevel = Utils::findOrDefault(m_projects, [this](const Project *p) {
- return m_parameters.sourceDirectory == p->sourceDirectory;
- });
- if (topLevel)
- root->setDisplayName(topLevel->name);
-
- QHash<Utils::FilePath, ProjectNode *> cmakeListsNodes = addCMakeLists(root.get(),
- std::move(cmakeLists));
- QSet<FilePath> knownHeaders;
- addProjects(cmakeListsNodes, m_projects, knownHeaders);
-
- addHeaderNodes(root.get(), knownHeaders, allFiles);
-
- if (cmakeFilesSource.size() > 0 || cmakeFilesBuild.size() > 0 || cmakeFilesOther.size() > 0)
- addCMakeInputs(root.get(),
- m_parameters.sourceDirectory,
- m_parameters.workDirectory,
- std::move(cmakeFilesSource),
- std::move(cmakeFilesBuild),
- std::move(cmakeFilesOther));
-
- return root;
-}
-
-RawProjectParts ServerModeReader::createRawProjectParts(QString &errorMessage)
-{
- Q_UNUSED(errorMessage)
- RawProjectParts rpps;
-
- int counter = 0;
- for (const FileGroup *fg : qAsConst(m_fileGroups)) {
- // 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 (fg->macros.isEmpty()
- && fg->includePaths.isEmpty()
- && !fg->isGenerated
- && Utils::allOf(fg->sources, [](const Utils::FilePath &source) {
- return Node::fileTypeForFileName(source) == FileType::Header;
- })) {
- qWarning() << "Not reporting all-header file group of target" << fg->target << "to code model.";
- continue;
- }
-
- ++counter;
- const QStringList flags = QtcProcess::splitArgs(fg->compileFlags);
- const QStringList includes = transform(fg->includePaths, [](const IncludePath *ip) { return ip->path.toString(); });
-
- RawProjectPart rpp;
- rpp.setProjectFileLocation(fg->target->sourceDirectory.toString() + "/CMakeLists.txt");
- rpp.setBuildSystemTarget(fg->target->name);
- rpp.setDisplayName(fg->target->name + QString::number(counter));
- rpp.setMacros(fg->macros);
- rpp.setIncludePaths(includes);
-
- RawProjectPartFlags cProjectFlags;
- cProjectFlags.commandLineFlags = flags;
- rpp.setFlagsForC(cProjectFlags);
-
- RawProjectPartFlags cxxProjectFlags;
- cxxProjectFlags.commandLineFlags = flags;
- rpp.setFlagsForCxx(cxxProjectFlags);
-
- rpp.setFiles(transform(fg->sources, &FilePath::toString));
-
- const bool isExecutable = fg->target->type == "EXECUTABLE";
- rpp.setBuildTargetType(isExecutable ? ProjectExplorer::BuildTargetType::Executable
- : ProjectExplorer::BuildTargetType::Library);
- rpps.append(rpp);
- }
-
- return rpps;
-}
-
-void ServerModeReader::createNewServer()
-{
- QTC_ASSERT(m_parameters.cmakeTool(), return);
- m_cmakeServer
- = std::make_unique<ServerMode>(
- m_parameters.environment,
- m_parameters.sourceDirectory, m_parameters.workDirectory,
- m_parameters.cmakeTool()->cmakeExecutable(),
- m_parameters.generator,
- m_parameters.extraGenerator,
- m_parameters.platform, m_parameters.toolset,
- true, 1);
-
- connect(m_cmakeServer.get(), &ServerMode::errorOccured,
- this, &ServerModeReader::errorOccured);
- connect(m_cmakeServer.get(), &ServerMode::cmakeReply,
- this, &ServerModeReader::handleReply);
- connect(m_cmakeServer.get(), &ServerMode::cmakeError,
- this, &ServerModeReader::handleError);
- connect(m_cmakeServer.get(), &ServerMode::cmakeProgress,
- this, &ServerModeReader::handleProgress);
- connect(m_cmakeServer.get(), &ServerMode::cmakeSignal,
- this, &ServerModeReader::handleSignal);
- connect(m_cmakeServer.get(), &ServerMode::cmakeMessage, [this](const QString &m) {
- const QStringList lines = m.split('\n');
- for (const QString &l : lines) {
- m_parser.stdError(l);
- Core::MessageManager::write(l);
- }
- });
- connect(m_cmakeServer.get(), &ServerMode::message,
- this, [](const QString &m) { Core::MessageManager::write(m); });
- connect(m_cmakeServer.get(),
- &ServerMode::connected,
- this,
- &ServerModeReader::handleServerConnected,
- Qt::QueuedConnection); // Delay
- connect(m_cmakeServer.get(), &ServerMode::disconnected,
- this, [this]() {
- stop();
- Core::MessageManager::write(tr("Parsing of CMake project failed: Connection to CMake server lost."));
- m_cmakeServer.reset();
- }, Qt::QueuedConnection); // Delay
-
-}
-
-void ServerModeReader::handleReply(const QVariantMap &data, const QString &inReplyTo)
-{
- if (!m_delayedErrorMessage.isEmpty()) {
- // Handle reply to cache after error:
- if (inReplyTo == CACHE_TYPE)
- extractCacheData(data);
- reportError();
- } else {
- // No error yet:
- if (inReplyTo == CONFIGURE_TYPE) {
- m_cmakeServer->sendRequest(COMPUTE_TYPE);
- if (m_future)
- m_future->setProgressValue(1000);
- m_progressStepMinimum = m_progressStepMaximum;
- m_progressStepMaximum = 1100;
- } else if (inReplyTo == COMPUTE_TYPE) {
- m_cmakeServer->sendRequest(CODEMODEL_TYPE);
- if (m_future)
- m_future->setProgressValue(1100);
- m_progressStepMinimum = m_progressStepMaximum;
- m_progressStepMaximum = 1200;
- } else if (inReplyTo == CODEMODEL_TYPE) {
- extractCodeModelData(data);
- m_cmakeServer->sendRequest(CMAKE_INPUTS_TYPE);
- if (m_future)
- m_future->setProgressValue(1200);
- m_progressStepMinimum = m_progressStepMaximum;
- m_progressStepMaximum = 1300;
- } else if (inReplyTo == CMAKE_INPUTS_TYPE) {
- extractCMakeInputsData(data);
- m_cmakeServer->sendRequest(CACHE_TYPE);
- if (m_future)
- m_future->setProgressValue(1300);
- m_progressStepMinimum = m_progressStepMaximum;
- m_progressStepMaximum = 1400;
- } else if (inReplyTo == CACHE_TYPE) {
- extractCacheData(data);
- if (m_future) {
- m_future->setProgressValue(MAX_PROGRESS);
- m_future->reportFinished();
- m_future.reset();
- }
- Core::MessageManager::write(tr("CMake Project was parsed successfully."));
- emit dataAvailable();
- }
- }
-}
-
-void ServerModeReader::handleError(const QString &message)
-{
- TaskHub::addTask(BuildSystemTask(Task::Error, message));
-
- if (!m_delayedErrorMessage.isEmpty()) {
- reportError();
- return;
- }
-
- m_delayedErrorMessage = message;
-
- // Always try to read CMakeCache, even after an error!
- m_cmakeServer->sendRequest(CACHE_TYPE);
- if (m_future)
- m_future->setProgressValue(1300);
-}
-
-void ServerModeReader::handleProgress(int min, int cur, int max, const QString &inReplyTo)
-{
- Q_UNUSED(inReplyTo)
-
- if (!m_future)
- return;
- const int progress = calculateProgress(m_progressStepMinimum, min, cur, max, m_progressStepMaximum);
- m_future->setProgressValue(progress);
-}
-
-void ServerModeReader::handleSignal(const QString &signal, const QVariantMap &data)
-{
- Q_UNUSED(signal)
- Q_UNUSED(data)
- // We do not need to act on fileChanged signals nor on dirty signals!
-}
-
-void ServerModeReader::handleServerConnected()
-{
- if (m_delayedConfigurationData) {
- sendConfigureRequest(*m_delayedConfigurationData);
- m_delayedConfigurationData.reset();
- } else {
- emit isReadyNow();
- }
-}
-
-void ServerModeReader::sendConfigureRequest(const QVariantMap &extra)
-{
- m_delayedErrorMessage.clear();
- m_cmakeServer->sendRequest(CONFIGURE_TYPE, extra);
-}
-
-void ServerModeReader::reportError()
-{
- stop();
- Core::MessageManager::write(tr("CMake Project parsing failed."));
- emit errorOccured(m_delayedErrorMessage);
-
- if (m_future)
- m_future->reportCanceled();
-
- m_delayedErrorMessage.clear();
-}
-
-int ServerModeReader::calculateProgress(const int minRange, const int min, const int cur, const int max, const int maxRange)
-{
- if (minRange == maxRange || min == max)
- return minRange;
- const int clampedCur = std::min(std::max(cur, min), max);
- return minRange + ((clampedCur - min) / (max - min)) * (maxRange - minRange);
-}
-
-void ServerModeReader::extractCodeModelData(const QVariantMap &data)
-{
- const QVariantList configs = data.value("configurations").toList();
- for (const QVariant &c : configs) {
- const QVariantMap &cData = c.toMap();
- extractConfigurationData(cData);
- }
-}
-
-void ServerModeReader::extractConfigurationData(const QVariantMap &data)
-{
- const QString name = data.value(NAME_KEY).toString();
- Q_UNUSED(name)
- QSet<QString> knownTargets; // To filter duplicate target names:-/
- const QVariantList projects = data.value("projects").toList();
- for (const QVariant &p : projects) {
- const QVariantMap pData = p.toMap();
- m_projects.append(extractProjectData(pData, knownTargets));
- }
-}
-
-ServerModeReader::Project *ServerModeReader::extractProjectData(const QVariantMap &data,
- QSet<QString> &knownTargets)
-{
- auto project = new Project;
- project->name = data.value(NAME_KEY).toString();
- project->sourceDirectory = FilePath::fromString(data.value(SOURCE_DIRECTORY_KEY).toString());
-
- const QVariantList targets = data.value("targets").toList();
- for (const QVariant &t : targets) {
- const QVariantMap tData = t.toMap();
- Target *tp = extractTargetData(tData, project, knownTargets);
- if (tp)
- project->targets.append(tp);
- }
- return project;
-}
-
-ServerModeReader::Target *ServerModeReader::extractTargetData(const QVariantMap &data, Project *p,
- QSet<QString> &knownTargets)
-{
- const QString targetName = data.value(NAME_KEY).toString();
-
- // Remove duplicate targets: CMake unfortunately does duplicate targets for all projects that
- // contain them. Keep at least till cmake 3.9 is deprecated.
- const int count = knownTargets.count();
- knownTargets.insert(targetName);
- if (knownTargets.count() == count)
- return nullptr;
-
- auto target = new Target;
- target->project = p;
- target->name = targetName;
- target->sourceDirectory = FilePath::fromString(data.value(SOURCE_DIRECTORY_KEY).toString());
- target->buildDirectory = FilePath::fromString(data.value("buildDirectory").toString());
-
- target->crossReferences = extractCrossReferences(data.value("crossReferences").toMap());
-
- QDir srcDir(target->sourceDirectory.toString());
-
- target->type = data.value("type").toString();
- const QStringList artifacts = data.value("artifacts").toStringList();
- target->artifacts = transform(artifacts, [&srcDir](const QString &a) { return FilePath::fromString(srcDir.absoluteFilePath(a)); });
-
- const QVariantList fileGroups = data.value("fileGroups").toList();
- for (const QVariant &fg : fileGroups) {
- const QVariantMap fgData = fg.toMap();
- target->fileGroups.append(extractFileGroupData(fgData, srcDir, target));
- }
-
- fixTarget(target);
-
- m_targets.append(target);
- return target;
-}
-
-ServerModeReader::FileGroup *ServerModeReader::extractFileGroupData(const QVariantMap &data,
- const QDir &srcDir,
- Target *t)
-{
- auto fileGroup = new FileGroup;
- fileGroup->target = t;
- fileGroup->compileFlags = data.value("compileFlags").toString();
- fileGroup->macros = Utils::transform<QVector>(data.value("defines").toStringList(), [](const QString &s) {
- return ProjectExplorer::Macro::fromKeyValue(s);
- });
- fileGroup->includePaths = transform(data.value("includePath").toList(),
- [](const QVariant &i) -> IncludePath* {
- const QVariantMap iData = i.toMap();
- auto result = new IncludePath;
- result->path = FilePath::fromString(iData.value("path").toString());
- result->isSystem = iData.value("isSystem", false).toBool();
- return result;
- });
- fileGroup->isGenerated = data.value("isGenerated", false).toBool();
- fileGroup->sources = transform(data.value(SOURCES_KEY).toStringList(),
- [&srcDir](const QString &s) {
- return FilePath::fromString(QDir::cleanPath(srcDir.absoluteFilePath(s)));
- });
-
- m_fileGroups.append(fileGroup);
- return fileGroup;
-}
-
-QList<ServerModeReader::CrossReference *> ServerModeReader::extractCrossReferences(const QVariantMap &data)
-{
- QList<CrossReference *> crossReferences;
-
- if (data.isEmpty())
- return crossReferences;
-
- auto cr = std::make_unique<CrossReference>();
- cr->backtrace = extractBacktrace(data.value(BACKTRACE_KEY, QVariantList()).toList());
- QTC_ASSERT(!cr->backtrace.isEmpty(), return {});
- crossReferences.append(cr.release());
-
- const QVariantList related = data.value("relatedStatements", QVariantList()).toList();
- for (const QVariant &relatedData : related) {
- auto cr = std::make_unique<CrossReference>();
-
- // extract information:
- const QVariantMap map = relatedData.toMap();
- const QString typeString = map.value("type", QString()).toString();
- if (typeString.isEmpty())
- cr->type = CrossReference::TARGET;
- else if (typeString == "target_link_libraries")
- cr->type = CrossReference::LIBRARIES;
- else if (typeString == "target_compile_defines")
- cr->type = CrossReference::DEFINES;
- else if (typeString == "target_include_directories")
- cr->type = CrossReference::INCLUDES;
- else
- cr->type = CrossReference::UNKNOWN;
- cr->backtrace = extractBacktrace(map.value(BACKTRACE_KEY, QVariantList()).toList());
-
- // sanity check:
- if (cr->backtrace.isEmpty())
- continue;
-
- // store information:
- crossReferences.append(cr.release());
- }
- return crossReferences;
-}
-
-ServerModeReader::BacktraceItem *ServerModeReader::extractBacktraceItem(const QVariantMap &data)
-{
- QTC_ASSERT(!data.isEmpty(), return nullptr);
- auto item = std::make_unique<BacktraceItem>();
-
- item->line = data.value(LINE_KEY, -1).toInt();
- item->name = data.value(NAME_KEY, QString()).toString();
- item->path = data.value(PATH_KEY, QString()).toString();
-
- QTC_ASSERT(!item->path.isEmpty(), return nullptr);
- return item.release();
-}
-
-QList<ServerModeReader::BacktraceItem *> ServerModeReader::extractBacktrace(const QVariantList &data)
-{
- QList<BacktraceItem *> btResult;
- for (const QVariant &bt : data) {
- BacktraceItem *btItem = extractBacktraceItem(bt.toMap());
- QTC_ASSERT(btItem, continue);
-
- btResult.append(btItem);
- }
- return btResult;
-}
-
-void ServerModeReader::extractCMakeInputsData(const QVariantMap &data)
-{
- const FilePath src = FilePath::fromString(data.value(SOURCE_DIRECTORY_KEY).toString());
- QTC_ASSERT(src == m_parameters.sourceDirectory, return);
- QDir srcDir(src.toString());
-
- m_cmakeFiles.clear();
-
- const QVariantList buildFiles = data.value("buildFiles").toList();
- for (const QVariant &bf : buildFiles) {
- const QVariantMap &section = bf.toMap();
- const QStringList sources = section.value(SOURCES_KEY).toStringList();
-
- const bool isTemporary = section.value("isTemporary").toBool(); // generated file
- const bool isCMake = section.value("isCMake").toBool(); // part of the cmake installation
-
- for (const QString &s : sources) {
- const FilePath sfn = FilePath::fromString(QDir::cleanPath(srcDir.absoluteFilePath(s)));
- const int oldCount = m_cmakeFiles.count();
- m_cmakeFiles.insert(sfn);
- if (oldCount < m_cmakeFiles.count()) {
- const bool isCMakeListsFile = sfn.toString().endsWith("/CMakeLists.txt");
-
- if (isCMake && !isCMakeListsFile)
- // 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(isTemporary && !isCMakeListsFile); // CMakeLists.txt are never
- // generated, independent
- // what cmake thinks:-)
-
- m_cmakeInputsFileNodes.emplace_back(std::move(node));
- }
- }
- }
-}
-
-void ServerModeReader::extractCacheData(const QVariantMap &data)
-{
- CMakeConfig config;
- const QVariantList entries = data.value("cache").toList();
- for (const QVariant &e : entries) {
- const QVariantMap eData = e.toMap();
- CMakeConfigItem item;
- item.key = eData.value("key").toByteArray();
- item.value = eData.value("value").toByteArray();
- item.type = CMakeConfigItem::typeStringToType(eData.value("type").toByteArray());
- const QVariantMap properties = eData.value("properties").toMap();
- item.isAdvanced = properties.value("ADVANCED", false).toBool();
- item.documentation = properties.value("HELPSTRING").toByteArray();
- item.values = CMakeConfigItem::cmakeSplitValue(properties.value("STRINGS").toString(), true);
- config.append(item);
- }
- m_cmakeConfiguration = config;
-}
-
-void ServerModeReader::fixTarget(ServerModeReader::Target *target) const
-{
- QHash<QString, const FileGroup *> languageFallbacks;
-
- for (const FileGroup *group : qAsConst(target->fileGroups)) {
- if (group->includePaths.isEmpty() && group->compileFlags.isEmpty()
- && group->macros.isEmpty())
- continue;
-
- const FileGroup *fallback = languageFallbacks.value(group->language);
- if (!fallback || fallback->sources.count() < group->sources.count())
- languageFallbacks.insert(group->language, group);
- }
-
- if (!languageFallbacks.value(""))
- return; // No empty language groups found, no need to proceed.
-
- const FileGroup *fallback = languageFallbacks.value("CXX");
- if (!fallback)
- fallback = languageFallbacks.value("C");
- if (!fallback)
- fallback = languageFallbacks.value("");
-
- if (!fallback)
- return;
-
- for (auto it = target->fileGroups.begin(); it != target->fileGroups.end(); ++it) {
- if (!(*it)->language.isEmpty())
- continue;
- (*it)->language = fallback->language.isEmpty() ? "CXX" : fallback->language;
-
- if (*it == fallback
- || !(*it)->includePaths.isEmpty() || !(*it)->macros.isEmpty()
- || !(*it)->compileFlags.isEmpty())
- continue;
-
- for (const IncludePath *ip : fallback->includePaths)
- (*it)->includePaths.append(new IncludePath(*ip));
- (*it)->macros = fallback->macros;
- (*it)->compileFlags = fallback->compileFlags;
- }
-}
-
-void ServerModeReader::addProjects(const QHash<Utils::FilePath, ProjectNode *> &cmakeListsNodes,
- const QList<Project *> &projects,
- QSet<FilePath> &knownHeaders)
-{
- for (const Project *p : projects) {
- createProjectNode(cmakeListsNodes, p->sourceDirectory, p->name);
- addTargets(cmakeListsNodes, p->targets, knownHeaders);
- }
-}
-
-void ServerModeReader::addTargets(
- const QHash<Utils::FilePath, ProjectExplorer::ProjectNode *> &cmakeListsNodes,
- const QList<Target *> &targets,
- QSet<Utils::FilePath> &knownHeaders)
-{
- for (const Target *t : targets) {
- CMakeTargetNode *tNode = createTargetNode(cmakeListsNodes, t->sourceDirectory, t->name);
- QTC_ASSERT(tNode, qDebug() << "No target node for" << t->sourceDirectory << t->name; continue);
- tNode->setTargetInformation(t->artifacts, t->type);
- tNode->setBuildDirectory(t->buildDirectory);
- QVector<FolderNode::LocationInfo> info;
- // Set up a default target path:
- FilePath targetPath = t->sourceDirectory.pathAppended("CMakeLists.txt");
- for (CrossReference *cr : qAsConst(t->crossReferences)) {
- BacktraceItem *bt = cr->backtrace.isEmpty() ? nullptr : cr->backtrace.at(0);
- if (bt) {
- const QString btName = bt->name.toLower();
- const FilePath path = Utils::FilePath::fromUserInput(bt->path);
- QString dn;
- if (cr->type != CrossReference::TARGET) {
- if (path == targetPath) {
- if (bt->line >= 0)
- dn = tr("%1 in line %2").arg(btName).arg(bt->line);
- else
- dn = tr("%1").arg(btName);
- } else {
- if (bt->line >= 0)
- dn = tr("%1 in %2:%3").arg(btName, path.toUserOutput()).arg(bt->line);
- else
- dn = tr("%1 in %2").arg(btName, path.toUserOutput());
- }
- } else {
- dn = tr("Target Definition");
- targetPath = path;
- }
- info.append(FolderNode::LocationInfo(dn, path, bt->line));
- }
- }
- tNode->setLocationInfo(info);
- addFileGroups(tNode, t->sourceDirectory, t->buildDirectory, t->fileGroups, knownHeaders);
- }
-}
-
-void ServerModeReader::addFileGroups(ProjectNode *targetRoot,
- const Utils::FilePath &sourceDirectory,
- const Utils::FilePath &buildDirectory,
- const QList<ServerModeReader::FileGroup *> &fileGroups,
- QSet<Utils::FilePath> &knownHeaders)
-{
- 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());
- });
-
- for (const FileGroup *f : fileGroups) {
- const QList<FilePath> newSources = Utils::filtered(f->sources, [&alreadyListed](const Utils::FilePath &fn) {
- const int count = alreadyListed.count();
- alreadyListed.insert(fn);
- return count != alreadyListed.count();
- });
- std::vector<std::unique_ptr<FileNode>> newFileNodes = Utils::transform<std::vector>(
- newSources, [f, &knownHeaders](const Utils::FilePath &fn) {
- auto node = std::make_unique<FileNode>(fn, Node::fileTypeForFileName(fn));
- node->setIsGenerated(f->isGenerated);
- if (node->fileType() == FileType::Header)
- knownHeaders.insert(node->filePath());
- return node;
- });
- std::move(std::begin(newFileNodes), std::end(newFileNodes), std::back_inserter(toList));
- }
-
- // Split up files in groups (based on location):
- const bool inSourceBuild = (m_parameters.workDirectory == m_parameters.sourceDirectory);
- std::vector<std::unique_ptr<FileNode>> sourceFileNodes;
- std::vector<std::unique_ptr<FileNode>> buildFileNodes;
- std::vector<std::unique_ptr<FileNode>> otherFileNodes;
- for (std::unique_ptr<FileNode> &fn : toList) {
- if (fn->filePath().isChildOf(m_parameters.workDirectory) && !inSourceBuild)
- buildFileNodes.emplace_back(std::move(fn));
- else if (fn->filePath().isChildOf(m_parameters.sourceDirectory))
- sourceFileNodes.emplace_back(std::move(fn));
- else
- otherFileNodes.emplace_back(std::move(fn));
- }
-
- addCMakeVFolder(targetRoot, sourceDirectory, 1000, QString(), std::move(sourceFileNodes));
- addCMakeVFolder(targetRoot, buildDirectory, 100, tr("<Build Directory>"), std::move(buildFileNodes));
- addCMakeVFolder(targetRoot, Utils::FilePath(), 10, tr("<Other Locations>"), std::move(otherFileNodes));
-}
-
-} // namespace Internal
-} // namespace CMakeProjectManager
-
-#if defined(WITH_TESTS)
-
-#include "cmakeprojectplugin.h"
-#include <QTest>
-
-namespace CMakeProjectManager {
-namespace Internal {
-
-void CMakeProjectPlugin::testServerModeReaderProgress_data()
-{
- QTest::addColumn<int>("minRange");
- QTest::addColumn<int>("min");
- QTest::addColumn<int>("cur");
- QTest::addColumn<int>("max");
- QTest::addColumn<int>("maxRange");
- QTest::addColumn<int>("expected");
-
- QTest::newRow("empty range") << 100 << 10 << 11 << 20 << 100 << 100;
- QTest::newRow("one range (low)") << 0 << 10 << 11 << 20 << 1 << 0;
- QTest::newRow("one range (high)") << 20 << 10 << 19 << 20 << 20 << 20;
- QTest::newRow("large range") << 30 << 10 << 11 << 20 << 100000 << 30;
-
- QTest::newRow("empty progress") << -5 << 10 << 10 << 10 << 99995 << -5;
- QTest::newRow("one progress (low)") << 42 << 10 << 10 << 11 << 100042 << 42;
- QTest::newRow("one progress (high)") << 0 << 10 << 11 << 11 << 100000 << 100000;
- QTest::newRow("large progress") << 0 << 10 << 10 << 11 << 100000 << 0;
-
- QTest::newRow("cur too low") << 0 << 10 << 9 << 100 << 100000 << 0;
- QTest::newRow("cur too high") << 0 << 10 << 101 << 100 << 100000 << 100000;
- QTest::newRow("cur much too low") << 0 << 10 << -1000 << 100 << 100000 << 0;
- QTest::newRow("cur much too high") << 0 << 10 << 1110000 << 100 << 100000 << 100000;
-}
-
-void CMakeProjectPlugin::testServerModeReaderProgress()
-{
- QFETCH(int, minRange);
- QFETCH(int, min);
- QFETCH(int, cur);
- QFETCH(int, max);
- QFETCH(int, maxRange);
- QFETCH(int, expected);
-
- ServerModeReader reader;
- const int r = reader.calculateProgress(minRange, min, cur, max, maxRange);
-
- QCOMPARE(r, expected);
-
- QVERIFY(r <= maxRange);
- QVERIFY(r >= minRange);
-}
-
-} // namespace Internal
-} // namespace CMakeProjectManager
-
-#endif