diff --git a/doc/qtcreator/src/cmake/creator-projects-cmake.qdoc b/doc/qtcreator/src/cmake/creator-projects-cmake.qdoc index c0d1c703ab9..511c0c9843d 100644 --- a/doc/qtcreator/src/cmake/creator-projects-cmake.qdoc +++ b/doc/qtcreator/src/cmake/creator-projects-cmake.qdoc @@ -60,11 +60,8 @@ \section1 Adding CMake Tools - \QC supports CMake version 3.7, or later. - - For best results use CMake version 3.14, or later, as \QC supports the - \l {https://cmake.org/cmake/help/latest/manual/cmake-file-api.7.html} - {file-based API}. + \QC requires CMake's {https://cmake.org/cmake/help/latest/manual/cmake-file-api.7.html} + {file-based API}. Please make sure to use CMake version 3.14, or later. To specify paths to CMake executables: diff --git a/src/plugins/cmakeprojectmanager/CMakeLists.txt b/src/plugins/cmakeprojectmanager/CMakeLists.txt index b48b6c622ed..0aaae8f77fe 100644 --- a/src/plugins/cmakeprojectmanager/CMakeLists.txt +++ b/src/plugins/cmakeprojectmanager/CMakeLists.txt @@ -40,6 +40,4 @@ add_qtc_plugin(CMakeProjectManager fileapiparser.cpp fileapiparser.h fileapireader.cpp fileapireader.h projecttreehelper.cpp projecttreehelper.h - servermode.cpp servermode.h - servermodereader.cpp servermodereader.h ) diff --git a/src/plugins/cmakeprojectmanager/builddirreader.cpp b/src/plugins/cmakeprojectmanager/builddirreader.cpp index 091e6d1a6f5..2b0c1ecb355 100644 --- a/src/plugins/cmakeprojectmanager/builddirreader.cpp +++ b/src/plugins/cmakeprojectmanager/builddirreader.cpp @@ -26,7 +26,6 @@ #include "builddirreader.h" #include "fileapireader.h" -#include "servermodereader.h" #include @@ -50,8 +49,6 @@ std::unique_ptr BuildDirReader::createReader(const BuildDirParam switch (type.value()) { case CMakeTool::FileApi: return std::make_unique(); - case CMakeTool::ServerMode: - return std::make_unique(); } } diff --git a/src/plugins/cmakeprojectmanager/cmakekitinformation.cpp b/src/plugins/cmakeprojectmanager/cmakekitinformation.cpp index 33f2395496e..89ab5a9aab0 100644 --- a/src/plugins/cmakeprojectmanager/cmakekitinformation.cpp +++ b/src/plugins/cmakeprojectmanager/cmakekitinformation.cpp @@ -254,10 +254,10 @@ Tasks CMakeKitAspect::validate(const Kit *k) const CMakeTool *tool = CMakeKitAspect::cmakeTool(k); if (tool) { CMakeTool::Version version = tool->version(); - if (version.major < 3 || (version.major == 3 && version.minor < 7)) { + if (version.major < 3 || (version.major == 3 && version.minor < 14)) { result << BuildSystemTask(Task::Warning, tr("CMake version %1 is unsupported. Please update to " - "version 3.7 or later.") + "version 3.14 (with file-api) or later.") .arg(QString::fromUtf8(version.fullVersion))); } } @@ -717,9 +717,8 @@ Tasks CMakeGeneratorKitAspect::validate(const Kit *k) const if (!it->supportsToolset && !info.toolset.isEmpty()) addWarning(tr("Toolset is not supported by the selected CMake generator.")); } - if (!tool->hasServerMode() && !tool->hasFileApi() && info.extraGenerator != "CodeBlocks") { - addWarning(tr("The selected CMake binary has no server-mode and the CMake " - "generator does not generate a CodeBlocks file. " + if (!tool->hasFileApi()) { + addWarning(tr("The selected CMake binary does not support file-api. " "%1 will not be able to parse CMake projects.") .arg(Core::Constants::IDE_DISPLAY_NAME)); } diff --git a/src/plugins/cmakeprojectmanager/cmakeprojectmanager.pro b/src/plugins/cmakeprojectmanager/cmakeprojectmanager.pro index efcc4744663..a363b395bd0 100644 --- a/src/plugins/cmakeprojectmanager/cmakeprojectmanager.pro +++ b/src/plugins/cmakeprojectmanager/cmakeprojectmanager.pro @@ -36,9 +36,7 @@ HEADERS = builddirmanager.h \ fileapidataextractor.h \ fileapiparser.h \ fileapireader.h \ - projecttreehelper.h \ - servermode.h \ - servermodereader.h + projecttreehelper.h SOURCES = builddirmanager.cpp \ builddirparameters.cpp \ @@ -72,9 +70,7 @@ SOURCES = builddirmanager.cpp \ fileapidataextractor.cpp \ fileapiparser.cpp \ fileapireader.cpp \ - projecttreehelper.cpp \ - servermode.cpp \ - servermodereader.cpp + projecttreehelper.cpp RESOURCES += cmakeproject.qrc diff --git a/src/plugins/cmakeprojectmanager/cmakeprojectmanager.qbs b/src/plugins/cmakeprojectmanager/cmakeprojectmanager.qbs index 83322217f89..916324ce7ae 100644 --- a/src/plugins/cmakeprojectmanager/cmakeprojectmanager.qbs +++ b/src/plugins/cmakeprojectmanager/cmakeprojectmanager.qbs @@ -89,10 +89,6 @@ QtcPlugin { "fileapireader.cpp", "fileapireader.h", "projecttreehelper.cpp", - "projecttreehelper.h", - "servermode.cpp", - "servermode.h", - "servermodereader.cpp", - "servermodereader.h" + "projecttreehelper.h" ] } diff --git a/src/plugins/cmakeprojectmanager/cmakeprojectplugin.h b/src/plugins/cmakeprojectmanager/cmakeprojectplugin.h index 84c13c3bd99..7a9a1ecce6d 100644 --- a/src/plugins/cmakeprojectmanager/cmakeprojectplugin.h +++ b/src/plugins/cmakeprojectmanager/cmakeprojectplugin.h @@ -54,9 +54,6 @@ private slots: void testCMakeProjectImporterToolChain_data(); void testCMakeProjectImporterToolChain(); - - void testServerModeReaderProgress_data(); - void testServerModeReaderProgress(); #endif private: diff --git a/src/plugins/cmakeprojectmanager/cmakesettingspage.cpp b/src/plugins/cmakeprojectmanager/cmakesettingspage.cpp index 98ad0b9c4f4..d1009d2a16d 100644 --- a/src/plugins/cmakeprojectmanager/cmakesettingspage.cpp +++ b/src/plugins/cmakeprojectmanager/cmakesettingspage.cpp @@ -114,14 +114,13 @@ public: , m_isAutoRun(item->isAutoRun()) , m_autoCreateBuildDirectory(item->autoCreateBuildDirectory()) , m_autodetected(item->isAutoDetected()) - , m_isSupported(item->hasFileApi() || item->hasServerMode()) + , m_isSupported(item->hasFileApi()) , m_changed(changed) { updateErrorFlags(); - m_tooltip = tr("Version: %1
Supports fileApi: %2
Supports server-mode: %3") + m_tooltip = tr("Version: %1
Supports fileApi: %2") .arg(QString::fromUtf8(item->version().fullVersion)) - .arg(item->hasFileApi() ? tr("yes") : tr("no")) - .arg(item->hasServerMode() ? tr("yes") : tr("no")); + .arg(item->hasFileApi() ? tr("yes") : tr("no")); } CMakeToolTreeItem(const QString &name, diff --git a/src/plugins/cmakeprojectmanager/cmaketool.cpp b/src/plugins/cmakeprojectmanager/cmaketool.cpp index f487734b3d8..2a2953e98ed 100644 --- a/src/plugins/cmakeprojectmanager/cmaketool.cpp +++ b/src/plugins/cmakeprojectmanager/cmaketool.cpp @@ -58,20 +58,11 @@ bool CMakeTool::Generator::matches(const QString &n, const QString &ex) const namespace Internal { -const char READER_TYPE_SERVERMODE[] = "servermode"; const char READER_TYPE_FILEAPI[] = "fileapi"; -static bool ignoreFileApi() -{ - static bool s_ignoreFileApi = qEnvironmentVariableIsSet("QTC_CMAKE_IGNORE_FILEAPI"); - return s_ignoreFileApi; -} - static Utils::optional readerTypeFromString(const QString &input) { // Do not try to be clever here, just use whatever is in the string! - if (input == READER_TYPE_SERVERMODE) - return CMakeTool::ServerMode; if (input == READER_TYPE_FILEAPI) return CMakeTool::FileApi; return {}; @@ -80,8 +71,6 @@ static Utils::optional readerTypeFromString(const QString static QString readerTypeToString(const CMakeTool::ReaderType &type) { switch (type) { - case CMakeTool::ServerMode: - return QString(READER_TYPE_SERVERMODE); case CMakeTool::FileApi: return QString(READER_TYPE_FILEAPI); } @@ -102,9 +91,7 @@ class IntrospectionData public: bool m_didAttemptToRun = false; bool m_didRun = true; - bool m_hasServerMode = false; - bool m_queriedServerMode = false; bool m_triedCapabilities = false; QList m_generators; @@ -193,13 +180,13 @@ void CMakeTool::setAutoCreateBuildDirectory(bool autoBuildDir) bool CMakeTool::isValid() const { - if (!m_id.isValid()) + if (!m_id.isValid() || !m_introspection) return false; if (!m_introspection->m_didAttemptToRun) - supportedGenerators(); + readInformation(); - return m_introspection->m_didRun && readerType().has_value(); + return m_introspection->m_didRun && !m_introspection->m_fileApis.isEmpty(); } Utils::SynchronousProcessResponse CMakeTool::run(const QStringList &args, int timeoutS) const @@ -284,12 +271,14 @@ bool CMakeTool::autoCreateBuildDirectory() const QList CMakeTool::supportedGenerators() const { - readInformation(QueryType::GENERATORS); - return m_introspection->m_generators; + return isValid() ? m_introspection->m_generators : QList(); } TextEditor::Keywords CMakeTool::keywords() { + if (!isValid()) + return {}; + if (m_introspection->m_functions.isEmpty() && m_introspection->m_didRun) { Utils::SynchronousProcessResponse response; response = run({"--help-command-list"}, 5); @@ -317,28 +306,19 @@ TextEditor::Keywords CMakeTool::keywords() m_introspection->m_functionArgs); } -bool CMakeTool::hasServerMode() const -{ - readInformation(QueryType::SERVER_MODE); - return m_introspection->m_hasServerMode; -} - bool CMakeTool::hasFileApi() const { - readInformation(QueryType::SERVER_MODE); - return !m_introspection->m_fileApis.isEmpty(); + return isValid() ? !m_introspection->m_fileApis.isEmpty() : false; } QVector> CMakeTool::supportedFileApiObjects() const { - readInformation(QueryType::SERVER_MODE); - return Utils::transform(m_introspection->m_fileApis, [](const Internal::FileApi &api) { return std::make_pair(api.kind, api.version.first); }); + return isValid() ? Utils::transform(m_introspection->m_fileApis, [](const Internal::FileApi &api) { return std::make_pair(api.kind, api.version.first); }) : QVector>(); } CMakeTool::Version CMakeTool::version() const { - readInformation(QueryType::VERSION); - return m_introspection->m_version; + return isValid() ? m_introspection->m_version : CMakeTool::Version(); } bool CMakeTool::isAutoDetected() const @@ -375,10 +355,8 @@ Utils::optional CMakeTool::readerType() const return m_readerType; // Allow overriding the auto-detected value via .user files // Find best possible reader type: - if (hasFileApi() && !Internal::ignoreFileApi()) + if (hasFileApi()) return FileApi; - if (hasServerMode()) - return ServerMode; return {}; } @@ -404,33 +382,16 @@ Utils::FilePath CMakeTool::searchQchFile(const Utils::FilePath &executable) return {}; } -void CMakeTool::readInformation(CMakeTool::QueryType type) const +void CMakeTool::readInformation() const { + QTC_ASSERT(m_introspection, return ); if (!m_introspection->m_didRun && m_introspection->m_didAttemptToRun) return; m_introspection->m_didAttemptToRun = true; - if (!m_introspection->m_triedCapabilities) { - fetchFromCapabilities(); - m_introspection->m_triedCapabilities = true; - m_introspection->m_queriedServerMode = true; // Got added after "-E capabilities" support! - } else { - if ((type == QueryType::GENERATORS && !m_introspection->m_generators.isEmpty()) - || (type == QueryType::SERVER_MODE && m_introspection->m_queriedServerMode) - || (type == QueryType::VERSION && !m_introspection->m_version.fullVersion.isEmpty())) - return; - - if (type == QueryType::GENERATORS) { - fetchGeneratorsFromHelp(); - } else if (type == QueryType::SERVER_MODE) { - // Nothing to do... - } else if (type == QueryType::VERSION) { - fetchVersionFromVersionOutput(); - } else { - QTC_ASSERT(false, return ); - } - } + fetchFromCapabilities(); + m_introspection->m_triedCapabilities = true; } static QStringList parseDefinition(const QString &definition) @@ -524,96 +485,16 @@ QStringList CMakeTool::parseVariableOutput(const QString &output) return result; } -void CMakeTool::fetchGeneratorsFromHelp() const -{ - Utils::SynchronousProcessResponse response = run({"--help"}); - m_introspection->m_didRun = m_introspection->m_didRun - && response.result == Utils::SynchronousProcessResponse::Finished; - - if (response.result == Utils::SynchronousProcessResponse::Finished) - parseGeneratorsFromHelp(response.stdOut().split('\n')); -} - -void CMakeTool::parseGeneratorsFromHelp(const QStringList &lines) const -{ - bool inGeneratorSection = false; - QHash generatorInfo; - foreach (const QString &line, lines) { - if (line.isEmpty()) - continue; - if (line == "Generators") { - inGeneratorSection = true; - continue; - } - if (!inGeneratorSection) - continue; - - if (line.startsWith(" ") && line.at(3) != ' ') { - int pos = line.indexOf('='); - if (pos < 0) - pos = line.length(); - if (pos >= 0) { - --pos; - while (pos > 2 && line.at(pos).isSpace()) - --pos; - } - if (pos > 2) { - const QString fullName = line.mid(2, pos - 1); - const int dashPos = fullName.indexOf(" - "); - QString generator; - QString extra; - if (dashPos < 0) { - generator = fullName; - } else { - extra = fullName.mid(0, dashPos); - generator = fullName.mid(dashPos + 3); - } - QStringList value = generatorInfo.value(generator); - if (!extra.isEmpty()) - value.append(extra); - generatorInfo.insert(generator, value); - } - } - } - - // Populate genertor list: - for (auto it = generatorInfo.constBegin(); it != generatorInfo.constEnd(); ++it) - m_introspection->m_generators.append(Generator(it.key(), it.value())); -} - -void CMakeTool::fetchVersionFromVersionOutput() const -{ - Utils::SynchronousProcessResponse response = run({"--version"}); - - m_introspection->m_didRun = m_introspection->m_didRun - && response.result == Utils::SynchronousProcessResponse::Finished; - - if (response.result == Utils::SynchronousProcessResponse::Finished) - parseVersionFormVersionOutput(response.stdOut().split('\n')); -} - -void CMakeTool::parseVersionFormVersionOutput(const QStringList &lines) const -{ - QRegularExpression versionLine("^cmake.* version ((\\d+).(\\d+).(\\d+).*)$"); - for (const QString &line : lines) { - QRegularExpressionMatch match = versionLine.match(line); - if (!match.hasMatch()) - continue; - - m_introspection->m_version.major = match.captured(2).toInt(); - m_introspection->m_version.minor = match.captured(3).toInt(); - m_introspection->m_version.patch = match.captured(4).toInt(); - m_introspection->m_version.fullVersion = match.captured(1).toUtf8(); - break; - } -} - void CMakeTool::fetchFromCapabilities() const { Utils::SynchronousProcessResponse response = run({"-E", "capabilities"}); - if (response.result == Utils::SynchronousProcessResponse::Finished) + if (response.result == Utils::SynchronousProcessResponse::Finished) { + m_introspection->m_didRun = true; parseFromCapabilities(response.stdOut()); + } else { + m_introspection->m_didRun = false; + } } static int getVersion(const QVariantMap &obj, const QString value) @@ -632,7 +513,6 @@ void CMakeTool::parseFromCapabilities(const QString &input) const return; const QVariantMap data = doc.object().toVariantMap(); - m_introspection->m_hasServerMode = data.value("serverMode").toBool(); const QVariantList generatorList = data.value("generators").toList(); for (const QVariant &v : generatorList) { const QVariantMap gen = v.toMap(); diff --git a/src/plugins/cmakeprojectmanager/cmaketool.h b/src/plugins/cmakeprojectmanager/cmaketool.h index 1d7bcde82e8..c22a07fb7f0 100644 --- a/src/plugins/cmakeprojectmanager/cmaketool.h +++ b/src/plugins/cmakeprojectmanager/cmaketool.h @@ -47,7 +47,7 @@ class CMAKE_EXPORT CMakeTool public: enum Detection { ManualDetection, AutoDetection }; - enum ReaderType { ServerMode, FileApi }; + enum ReaderType { FileApi }; struct Version { @@ -98,7 +98,6 @@ public: bool autoCreateBuildDirectory() const; QList supportedGenerators() const; TextEditor::Keywords keywords(); - bool hasServerMode() const; bool hasFileApi() const; QVector> supportedFileApiObjects() const; Version version() const; @@ -115,21 +114,12 @@ public: static Utils::FilePath searchQchFile(const Utils::FilePath &executable); private: - enum class QueryType { - GENERATORS, - SERVER_MODE, - VERSION - }; - void readInformation(QueryType type) const; + void readInformation() const; Utils::SynchronousProcessResponse run(const QStringList &args, int timeoutS = 1) const; void parseFunctionDetailsOutput(const QString &output); QStringList parseVariableOutput(const QString &output); - void fetchGeneratorsFromHelp() const; - void parseGeneratorsFromHelp(const QStringList &lines) const; - void fetchVersionFromVersionOutput() const; - void parseVersionFormVersionOutput(const QStringList &lines) const; void fetchFromCapabilities() const; void parseFromCapabilities(const QString &input) const; diff --git a/src/plugins/cmakeprojectmanager/projecttreehelper.cpp b/src/plugins/cmakeprojectmanager/projecttreehelper.cpp index 46d0810b608..8a930de35b1 100644 --- a/src/plugins/cmakeprojectmanager/projecttreehelper.cpp +++ b/src/plugins/cmakeprojectmanager/projecttreehelper.cpp @@ -97,13 +97,13 @@ void addCMakeInputs(FolderNode *root, addCMakeVFolder(cmakeVFolder.get(), buildDir, 100, - QCoreApplication::translate("CMakeProjectManager::Internal::ServerModeReader", + QCoreApplication::translate("CMakeProjectManager::Internal::ProjectTreeHelper", ""), removeKnownNodes(knownFiles, std::move(buildInputs))); addCMakeVFolder(cmakeVFolder.get(), Utils::FilePath(), 10, - QCoreApplication::translate("CMakeProjectManager::Internal::ServerModeReader", + QCoreApplication::translate("CMakeProjectManager::Internal::ProjectTreeHelper", ""), removeKnownNodes(knownFiles, std::move(rootInputs))); @@ -187,7 +187,8 @@ void addHeaderNodes(ProjectNode *root, auto headerNode = std::make_unique(root->filePath()); headerNode->setPriority(Node::DefaultPriority - 5); headerNode->setDisplayName( - QCoreApplication::translate("CMakeProjectManager::Internal::ServerModeReader", "")); + QCoreApplication::translate("CMakeProjectManager::Internal::ProjectTreeHelper", + "")); headerNode->setIcon(headerNodeIcon); // Add scanned headers: diff --git a/src/plugins/cmakeprojectmanager/servermode.cpp b/src/plugins/cmakeprojectmanager/servermode.cpp deleted file mode 100644 index 1faa6eb012d..00000000000 --- a/src/plugins/cmakeprojectmanager/servermode.cpp +++ /dev/null @@ -1,488 +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 "servermode.h" - -#include - -#include - -#include -#include -#include -#include - -using namespace Utils; - -namespace CMakeProjectManager { -namespace Internal { - -const char COOKIE_KEY[] = "cookie"; -const char IN_REPLY_TO_KEY[] = "inReplyTo"; -const char NAME_KEY[] = "name"; -const char TYPE_KEY[] = "type"; - -const char ERROR_TYPE[] = "error"; -const char HANDSHAKE_TYPE[] = "handshake"; - -const char START_MAGIC[] = "\n[== \"CMake Server\" ==[\n"; -const char END_MAGIC[] = "\n]== \"CMake Server\" ==]\n"; - -static Q_LOGGING_CATEGORY(cmakeServerMode, "qtc.cmake.serverMode", QtWarningMsg); - -// ---------------------------------------------------------------------- -// Helpers: -// ---------------------------------------------------------------------- - -bool isValid(const QVariant &v) -{ - if (v.isNull()) - return false; - if (v.type() == QVariant::String) // Workaround signals sending an empty string cookie - return !v.toString().isEmpty(); - return true; -} - -// -------------------------------------------------------------------- -// ServerMode: -// -------------------------------------------------------------------- - - -ServerMode::ServerMode(const Environment &env, - const FilePath &sourceDirectory, const FilePath &buildDirectory, - const FilePath &cmakeExecutable, - const QString &generator, const QString &extraGenerator, - const QString &platform, const QString &toolset, - bool experimental, int major, int minor, - QObject *parent) : - QObject(parent), -#if defined(Q_OS_UNIX) - // Some unixes (e.g. Darwin) limit the length of a local socket to about 100 char (or less). - // Since some unixes (e.g. Darwin) also point TMPDIR to /really/long/paths we need to create - // our own socket in a place closer to '/'. - m_socketDir("/tmp/cmake-"), -#endif - m_sourceDirectory(sourceDirectory), m_buildDirectory(buildDirectory), - m_cmakeCommand(cmakeExecutable, {}), - m_generator(generator), m_extraGenerator(extraGenerator), - m_platform(platform), m_toolset(toolset), - m_useExperimental(experimental), m_majorProtocol(major), m_minorProtocol(minor) -{ - QTC_ASSERT(!m_sourceDirectory.isEmpty() && m_sourceDirectory.exists(), return); - QTC_ASSERT(!m_buildDirectory.isEmpty() && m_buildDirectory.exists(), return); - - m_connectionTimer.setInterval(100); - connect(&m_connectionTimer, &QTimer::timeout, this, &ServerMode::connectToServer); - - m_cmakeProcess = std::make_unique(); - - m_cmakeProcess->setEnvironment(env); - m_cmakeProcess->setWorkingDirectory(buildDirectory.toString()); - -#if defined(Q_OS_UNIX) - m_socketName = m_socketDir.path() + "/socket"; -#else - m_socketName = QString::fromLatin1("\\\\.\\pipe\\") + QUuid::createUuid().toString(); -#endif - - connect(m_cmakeProcess.get(), &QtcProcess::started, this, [this]() { m_connectionTimer.start(); }); - connect(m_cmakeProcess.get(), - QOverload::of(&QtcProcess::finished), - this, &ServerMode::handleCMakeFinished); - - m_cmakeCommand.addArgs({"-E", "server", "--pipe=" + m_socketName}); - if (m_useExperimental) - m_cmakeCommand.addArg("--experimental"); - - qCInfo(cmakeServerMode) - << "Preparing cmake:" << m_cmakeCommand.toUserOutput() - << "in" << m_buildDirectory.toString(); - m_cmakeProcess->setCommand(m_cmakeCommand); - - // Delay start: - QTimer::singleShot(0, this, [this] { - emit message(tr("Running \"%1\" in %2.") - .arg(m_cmakeCommand.toUserOutput()) - .arg(m_buildDirectory.toUserOutput())); - - m_cmakeProcess->start(); - }); -} - -ServerMode::~ServerMode() -{ - if (m_cmakeProcess) - m_cmakeProcess->disconnect(); - if (m_cmakeSocket) { - m_cmakeSocket->disconnect(); - m_cmakeSocket->abort(); - delete(m_cmakeSocket); - } - m_cmakeSocket = nullptr; - Core::Reaper::reap(m_cmakeProcess.release()); - - qCDebug(cmakeServerMode) << "Server-Mode closed."; -} - -void ServerMode::sendRequest(const QString &type, const QVariantMap &extra, const QVariant &cookie) -{ - QTC_ASSERT(m_cmakeSocket, return); - ++m_requestCounter; - - qCInfo(cmakeServerMode) << "Sending Request" << type << "(" << cookie << ")"; - - QVariantMap data = extra; - data.insert(TYPE_KEY, type); - const QVariant realCookie = cookie.isNull() ? QVariant(m_requestCounter) : cookie; - data.insert(COOKIE_KEY, realCookie); - m_expectedReplies.push_back({type, realCookie}); - - QJsonObject object = QJsonObject::fromVariantMap(data); - QJsonDocument document; - document.setObject(object); - - const QByteArray rawData = START_MAGIC + document.toJson(QJsonDocument::Compact) + END_MAGIC; - qCDebug(cmakeServerMode) << ">>>" << rawData; - m_cmakeSocket->write(rawData); - m_cmakeSocket->flush(); -} - -bool ServerMode::isConnected() -{ - return m_cmakeSocket && m_isConnected; -} - -void ServerMode::connectToServer() -{ - QTC_ASSERT(m_cmakeProcess, return); - if (m_cmakeSocket) - return; // We connected in the meantime... - - static int counter = 0; - ++counter; - - if (counter > 50) { - counter = 0; - m_cmakeProcess->disconnect(); - qCInfo(cmakeServerMode) << "Timeout waiting for pipe" << m_socketName; - reportError(tr("Running \"%1\" failed: Timeout waiting for pipe \"%2\".") - .arg(m_cmakeCommand.toUserOutput()) - .arg(m_socketName)); - - Core::Reaper::reap(m_cmakeProcess.release()); - emit disconnected(); - return; - } - - QTC_ASSERT(!m_cmakeSocket, return); - - auto socket = new QLocalSocket(m_cmakeProcess.get()); - connect(socket, &QLocalSocket::readyRead, this, &ServerMode::handleRawCMakeServerData); - - constexpr void (QLocalSocket::*LocalSocketErrorFunction)(QLocalSocket::LocalSocketError) -#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) - = &QLocalSocket::error; -#else - = &QLocalSocket::errorOccurred; -#endif - - connect(socket, LocalSocketErrorFunction, this, [this, socket]() { - reportError(socket->errorString()); - m_cmakeSocket = nullptr; - socket->disconnect(); - socket->deleteLater(); - }); - connect(socket, &QLocalSocket::connected, this, [this, socket]() { m_cmakeSocket = socket; }); - connect(socket, &QLocalSocket::disconnected, this, [this, socket]() { - if (m_cmakeSocket) - emit disconnected(); - m_cmakeSocket = nullptr; - socket->disconnect(); - socket->deleteLater(); - }); - - socket->connectToServer(m_socketName); - m_connectionTimer.start(); -} - -void ServerMode::handleCMakeFinished(int code, QProcess::ExitStatus status) -{ - qCInfo(cmakeServerMode) << "CMake has finished" << code << status; - QString msg; - if (status != QProcess::NormalExit) - msg = tr("CMake process \"%1\" crashed.").arg(m_cmakeCommand.toUserOutput()); - else if (code != 0) - msg = tr("CMake process \"%1\" quit with exit code %2.").arg(m_cmakeCommand.toUserOutput()).arg(code); - - if (!msg.isEmpty()) { - reportError(msg); - } else { - emit message(tr("CMake process \"%1\" quit normally.").arg(m_cmakeCommand.toUserOutput())); - } - - if (m_cmakeSocket) { - m_cmakeSocket->disconnect(); - delete m_cmakeSocket; - m_cmakeSocket = nullptr; - } - - if (!HostOsInfo::isWindowsHost()) - QFile::remove(m_socketName); - - emit disconnected(); -} - -void ServerMode::handleRawCMakeServerData() -{ - const static QByteArray startNeedle(START_MAGIC); - const static QByteArray endNeedle(END_MAGIC); - - if (!m_cmakeSocket) // might happen during shutdown - return; - - m_buffer.append(m_cmakeSocket->readAll()); - - while (true) { - const int startPos = m_buffer.indexOf(startNeedle); - if (startPos >= 0) { - const int afterStartNeedle = startPos + startNeedle.count(); - const int endPos = m_buffer.indexOf(endNeedle, afterStartNeedle); - if (endPos > afterStartNeedle) { - // Process JSON, remove junk and JSON-part, continue to loop with shorter buffer - parseBuffer(m_buffer.mid(afterStartNeedle, endPos - afterStartNeedle)); - m_buffer.remove(0, endPos + endNeedle.count()); - } else { - // Remove junk up to the start needle and break out of the loop - if (startPos > 0) - m_buffer.remove(0, startPos); - break; - } - } else { - // Keep at last startNeedle.count() characters (as that might be a - // partial startNeedle), break out of the loop - if (m_buffer.count() > startNeedle.count()) - m_buffer.remove(0, m_buffer.count() - startNeedle.count()); - break; - } - } -} - -void ServerMode::parseBuffer(const QByteArray &buffer) -{ - qCDebug(cmakeServerMode) << "<<<" << buffer; - QJsonDocument document = QJsonDocument::fromJson(buffer); - if (document.isNull()) { - reportError(tr("Failed to parse JSON from CMake server.")); - return; - } - QJsonObject rootObject = document.object(); - if (rootObject.isEmpty()) { - reportError(tr("JSON data from CMake server was not a JSON object.")); - return; - } - - parseJson(rootObject.toVariantMap()); -} - -void ServerMode::parseJson(const QVariantMap &data) -{ - QString type = data.value(TYPE_KEY).toString(); - if (type == "hello") { - qCInfo(cmakeServerMode) << "Got \"hello\" message."; - if (m_gotHello) { - reportError(tr("Unexpected hello received from CMake server.")); - return; - } else { - handleHello(data); - m_gotHello = true; - return; - } - } - if (!m_gotHello && type != ERROR_TYPE) { - reportError(tr("Unexpected type \"%1\" received while waiting for \"hello\".").arg(type)); - return; - } - - if (type == "reply") { - if (m_expectedReplies.empty()) { - reportError(tr("Received a reply even though no request is open.")); - return; - } - const QString replyTo = data.value(IN_REPLY_TO_KEY).toString(); - const QVariant cookie = data.value(COOKIE_KEY); - qCInfo(cmakeServerMode) << "Got \"reply\" message." << replyTo << "(" << cookie << ")"; - - const auto expected = m_expectedReplies.begin(); - if (expected->type != replyTo) { - reportError(tr("Received a reply to a request of type \"%1\", when a request of type \"%2\" was sent.") - .arg(replyTo).arg(expected->type)); - return; - } - if (expected->cookie != cookie) { - reportError(tr("Received a reply with cookie \"%1\", when \"%2\" was expected.") - .arg(cookie.toString()).arg(expected->cookie.toString())); - return; - } - - m_expectedReplies.erase(expected); - if (replyTo != HANDSHAKE_TYPE) - emit cmakeReply(data, replyTo, cookie); - else { - m_isConnected = true; - emit connected(); - } - return; - } - if (type == "error") { - if (m_expectedReplies.empty()) { - reportError(tr("An error was reported even though no request is open.")); - return; - } - const QString replyTo = data.value(IN_REPLY_TO_KEY).toString(); - const QVariant cookie = data.value(COOKIE_KEY); - qCInfo(cmakeServerMode) << "Got \"error\" message." << replyTo << "(" << cookie << ")"; - - const auto expected = m_expectedReplies.begin(); - if (expected->type != replyTo) { - reportError(tr("Received an error in response to a request of type \"%1\", when a request of type \"%2\" was sent.") - .arg(replyTo).arg(expected->type)); - return; - } - if (expected->cookie != cookie) { - reportError(tr("Received an error with cookie \"%1\", when \"%2\" was expected.") - .arg(cookie.toString()).arg(expected->cookie.toString())); - return; - } - - m_expectedReplies.erase(expected); - - emit cmakeError(data.value("errorMessage").toString(), replyTo, cookie); - if (replyTo == HANDSHAKE_TYPE) { - Core::Reaper::reap(m_cmakeProcess.release()); - m_cmakeSocket->disconnect(); - m_cmakeSocket->disconnectFromServer(); - m_cmakeSocket = nullptr; - emit disconnected(); - } - return; - } - if (type == "message") { - const QString replyTo = data.value(IN_REPLY_TO_KEY).toString(); - const QVariant cookie = data.value(COOKIE_KEY); - qCInfo(cmakeServerMode) << "Got \"message\" message." << replyTo << "(" << cookie << ")"; - - const auto expected = m_expectedReplies.begin(); - if (expected->type != replyTo) { - reportError(tr("Received a message in response to a request of type \"%1\", when a request of type \"%2\" was sent.") - .arg(replyTo).arg(expected->type)); - return; - } - if (expected->cookie != cookie) { - reportError(tr("Received a message with cookie \"%1\", when \"%2\" was expected.") - .arg(cookie.toString()).arg(expected->cookie.toString())); - return; - } - - emit cmakeMessage(data.value("message").toString(), replyTo, cookie); - return; - } - if (type == "progress") { - const QString replyTo = data.value(IN_REPLY_TO_KEY).toString(); - const QVariant cookie = data.value(COOKIE_KEY); - qCInfo(cmakeServerMode) << "Got \"progress\" message." << replyTo << "(" << cookie << ")"; - - const auto expected = m_expectedReplies.begin(); - if (expected->type != replyTo) { - reportError(tr("Received a progress report in response to a request of type \"%1\", when a request of type \"%2\" was sent.") - .arg(replyTo).arg(expected->type)); - return; - } - if (expected->cookie != cookie) { - reportError(tr("Received a progress report with cookie \"%1\", when \"%2\" was expected.") - .arg(cookie.toString()).arg(expected->cookie.toString())); - return; - } - - emit cmakeProgress(data.value("progressMinimum").toInt(), - data.value("progressCurrent").toInt(), - data.value("progressMaximum").toInt(), replyTo, cookie); - return; - } - if (type == "signal") { - const QString replyTo = data.value(IN_REPLY_TO_KEY).toString(); - const QString cookie = data.value(COOKIE_KEY).toString(); - const QString name = data.value(NAME_KEY).toString(); - qCInfo(cmakeServerMode) << "Got \"signal\" message." << name << replyTo << "(" << cookie << ")"; - - if (name.isEmpty()) { - reportError(tr("Received a signal without a name.")); - return; - } - if (!replyTo.isEmpty() || isValid(cookie)) { - reportError(tr("Received a signal in reply to a request.")); - return; - } - - emit cmakeSignal(name, data); - return; - } - reportError("Got a message of an unknown type."); -} - -void ServerMode::handleHello(const QVariantMap &data) -{ - Q_UNUSED(data) - QVariantMap extra; - QVariantMap version; - version.insert("major", m_majorProtocol); - if (m_minorProtocol >= 0) - version.insert("minor", m_minorProtocol); - extra.insert("protocolVersion", version); - extra.insert("sourceDirectory", m_sourceDirectory.toString()); - extra.insert("buildDirectory", m_buildDirectory.toString()); - extra.insert("generator", m_generator); - if (!m_platform.isEmpty()) - extra.insert("platform", m_platform); - if (!m_toolset.isEmpty()) - extra.insert("toolset", m_toolset); - if (!m_extraGenerator.isEmpty()) - extra.insert("extraGenerator", m_extraGenerator); - if (!m_platform.isEmpty()) - extra.insert("platform", m_platform); - if (!m_toolset.isEmpty()) - extra.insert("toolset", m_toolset); - - sendRequest(HANDSHAKE_TYPE, extra); -} - -void ServerMode::reportError(const QString &msg) -{ - qCWarning(cmakeServerMode) << "Report Error:" << msg; - emit message(msg); - emit errorOccurred(msg); -} - -} // namespace Internal -} // namespace CMakeProjectManager diff --git a/src/plugins/cmakeprojectmanager/servermode.h b/src/plugins/cmakeprojectmanager/servermode.h deleted file mode 100644 index d527bdb7159..00000000000 --- a/src/plugins/cmakeprojectmanager/servermode.h +++ /dev/null @@ -1,127 +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. -** -****************************************************************************/ - -#pragma once - -#include -#include -#include - -#include -#include -#include -#include -#include - -#include - -QT_FORWARD_DECLARE_CLASS(QLocalSocket); - -namespace Utils { class QtcProcess; } - -namespace CMakeProjectManager { -namespace Internal { - -class ServerMode final : public QObject -{ - Q_OBJECT - -public: - ServerMode(const Utils::Environment &env, - const Utils::FilePath &sourceDirectory, const Utils::FilePath &buildDirectory, - const Utils::FilePath &cmakeExecutable, - const QString &generator, const QString &extraGenerator, - const QString &platform, const QString &toolset, - bool experimental, int major, int minor = -1, - QObject *parent = nullptr); - ~ServerMode() final; - - void sendRequest(const QString &type, const QVariantMap &extra = QVariantMap(), - const QVariant &cookie = QVariant()); - - bool isConnected(); - -signals: - void connected(); - void disconnected(); - void message(const QString &msg); - void errorOccurred(const QString &msg); - - // Forward stuff from the server - void cmakeReply(const QVariantMap &data, const QString &inResponseTo, const QVariant &cookie); - void cmakeError(const QString &errorMessage, const QString &inResponseTo, const QVariant &cookie); - void cmakeMessage(const QString &message, const QString &inResponseTo, const QVariant &cookie); - void cmakeProgress(int min, int cur, int max, const QString &inResponseTo, const QVariant &cookie); - void cmakeSignal(const QString &name, const QVariantMap &data); - -private: - void connectToServer(); - void handleCMakeFinished(int code, QProcess::ExitStatus status); - - void handleRawCMakeServerData(); - void parseBuffer(const QByteArray &buffer); - void parseJson(const QVariantMap &data); - - void handleHello(const QVariantMap &data); - - void reportError(const QString &msg); - -#if defined(Q_OS_UNIX) - QTemporaryDir m_socketDir; -#endif - std::unique_ptr m_cmakeProcess; - QLocalSocket *m_cmakeSocket = nullptr; - QTimer m_connectionTimer; - - Utils::FilePath m_sourceDirectory; - Utils::FilePath m_buildDirectory; - Utils::CommandLine m_cmakeCommand; - - QByteArray m_buffer; - - struct ExpectedReply { - QString type; - QVariant cookie; - }; - std::vector m_expectedReplies; - - const QString m_generator; - const QString m_extraGenerator; - const QString m_platform; - const QString m_toolset; - QString m_socketName; - const bool m_useExperimental; - bool m_gotHello = false; - bool m_isConnected = false; - const int m_majorProtocol = -1; - const int m_minorProtocol = -1; - - int m_requestCounter = 0; -}; - -} // namespace Internal -} // namespace CMakeProjectManager - -Q_DECLARE_LOGGING_CATEGORY(cmakeServerMode); diff --git a/src/plugins/cmakeprojectmanager/servermodereader.cpp b/src/plugins/cmakeprojectmanager/servermodereader.cpp deleted file mode 100644 index e9f9f994261..00000000000 --- a/src/plugins/cmakeprojectmanager/servermodereader.cpp +++ /dev/null @@ -1,915 +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 "cmakeparser.h" -#include "projecttreehelper.h" - -#include -#include -#include -#include - -#include - -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() -{ - m_cmakeParser = new CMakeParser; - m_parser.addLineParser(m_cmakeParser); -} - -ServerModeReader::~ServerModeReader() -{ - stop(); -} - -void ServerModeReader::setParameters(const BuildDirParameters &p) -{ - CMakeTool *cmake = p.cmakeTool(); - QTC_ASSERT(cmake, return); - - m_parameters = p; - m_cmakeParser->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()); - 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(m_future); -} - -QList ServerModeReader::takeBuildTargets(QString &errorMessage) -{ - Q_UNUSED(errorMessage) - QDir topSourceDir(m_parameters.sourceDirectory.toString()); - const QList 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 ServerModeReader::generateProjectTree(const QList &allFiles, - QString &errorMessage) -{ - Q_UNUSED(errorMessage) - auto root = std::make_unique(m_parameters.sourceDirectory); - - // Split up cmake inputs into useful chunks: - std::vector> cmakeFilesSource; - std::vector> cmakeFilesBuild; - std::vector> cmakeFilesOther; - std::vector> cmakeLists; - - for (std::unique_ptr &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 cmakeListsNodes = addCMakeLists(root.get(), - std::move(cmakeLists)); - QSet 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( - 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::errorOccurred, - this, &ServerModeReader::errorOccurred); - 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.appendMessage(l, StdErrFormat); - 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 errorOccurred(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 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 &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 &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(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::extractCrossReferences(const QVariantMap &data) -{ - QList crossReferences; - - if (data.isEmpty()) - return crossReferences; - - auto cr = std::make_unique(); - 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(); - - // 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(); - - 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::extractBacktrace(const QVariantList &data) -{ - QList 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 §ion = 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(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 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 &cmakeListsNodes, - const QList &projects, - QSet &knownHeaders) -{ - for (const Project *p : projects) { - createProjectNode(cmakeListsNodes, p->sourceDirectory, p->name); - addTargets(cmakeListsNodes, p->targets, knownHeaders); - } -} - -void ServerModeReader::addTargets( - const QHash &cmakeListsNodes, - const QList &targets, - QSet &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 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 &fileGroups, - QSet &knownHeaders) -{ - std::vector> toList; - QSet alreadyListed; - // Files already added by other configurations: - targetRoot->forEachGenericNode([&alreadyListed](const Node *n) { - alreadyListed.insert(n->filePath()); - }); - - for (const FileGroup *f : fileGroups) { - const QList newSources = Utils::filtered(f->sources, [&alreadyListed](const Utils::FilePath &fn) { - const int count = alreadyListed.count(); - alreadyListed.insert(fn); - return count != alreadyListed.count(); - }); - std::vector> newFileNodes = Utils::transform( - newSources, [f, &knownHeaders](const Utils::FilePath &fn) { - auto node = std::make_unique(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> sourceFileNodes; - std::vector> buildFileNodes; - std::vector> otherFileNodes; - for (std::unique_ptr &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(""), std::move(buildFileNodes)); - addCMakeVFolder(targetRoot, Utils::FilePath(), 10, tr(""), std::move(otherFileNodes)); -} - -} // namespace Internal -} // namespace CMakeProjectManager - -#if defined(WITH_TESTS) - -#include "cmakeprojectplugin.h" -#include - -namespace CMakeProjectManager { -namespace Internal { - -void CMakeProjectPlugin::testServerModeReaderProgress_data() -{ - QTest::addColumn("minRange"); - QTest::addColumn("min"); - QTest::addColumn("cur"); - QTest::addColumn("max"); - QTest::addColumn("maxRange"); - QTest::addColumn("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 diff --git a/src/plugins/cmakeprojectmanager/servermodereader.h b/src/plugins/cmakeprojectmanager/servermodereader.h deleted file mode 100644 index 66ab0a56be0..00000000000 --- a/src/plugins/cmakeprojectmanager/servermodereader.h +++ /dev/null @@ -1,199 +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. -** -****************************************************************************/ - -#pragma once - -#include "builddirreader.h" -#include "cmakeconfigitem.h" -#include "servermode.h" - -#include - -#include -#include - -#include - -namespace ProjectExplorer { class ProjectNode; } -namespace Utils { class OutputFormatter; } - -namespace CMakeProjectManager { - -class CMakeParser; - -namespace Internal { - -class ServerModeReader final : public BuildDirReader -{ - Q_OBJECT - -public: - ServerModeReader(); - ~ServerModeReader() 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; - - QSet projectFilesToWatch() const final { return {}; }; - QList takeBuildTargets(QString &errorMessage) final; - CMakeConfig takeParsedConfiguration(QString &errorMessage) final; - std::unique_ptr generateProjectTree( - const QList &allFiles, QString &errorMessage) final; - ProjectExplorer::RawProjectParts createRawProjectParts(QString &errorMessage) final; - -private: - void createNewServer(); - void handleReply(const QVariantMap &data, const QString &inReplyTo); - void handleError(const QString &message); - void handleProgress(int min, int cur, int max, const QString &inReplyTo); - void handleSignal(const QString &signal, const QVariantMap &data); - void handleServerConnected(); - - void sendConfigureRequest(const QVariantMap &extra); - - void reportError(); - - int calculateProgress(const int minRange, const int min, - const int cur, - const int max, const int maxRange); - - struct Target; - struct Project; - - struct IncludePath { - Utils::FilePath path; - bool isSystem; - }; - - struct FileGroup { - ~FileGroup() { qDeleteAll(includePaths); includePaths.clear(); } - - Target *target = nullptr; - QString compileFlags; - ProjectExplorer::Macros macros; - QList includePaths; - QString language; - QList sources; - bool isGenerated; - }; - - struct BacktraceItem { - int line = -1; - QString path; - QString name; - }; - - struct CrossReference { - ~CrossReference() { qDeleteAll(backtrace); backtrace.clear(); } - QList backtrace; - enum Type { TARGET, LIBRARIES, DEFINES, INCLUDES, UNKNOWN }; - Type type; - }; - - struct Target { - ~Target() { - qDeleteAll(fileGroups); - fileGroups.clear(); - qDeleteAll(crossReferences); - crossReferences.clear(); - } - - Project *project = nullptr; - QString name; - QString type; - QList artifacts; - Utils::FilePath sourceDirectory; - Utils::FilePath buildDirectory; - QList fileGroups; - QList crossReferences; - }; - - struct Project { - ~Project() { qDeleteAll(targets); targets.clear(); } - QString name; - Utils::FilePath sourceDirectory; - QList targets; - }; - - void extractCodeModelData(const QVariantMap &data); - void extractConfigurationData(const QVariantMap &data); - Project *extractProjectData(const QVariantMap &data, QSet &knownTargets); - Target *extractTargetData(const QVariantMap &data, Project *p, QSet &knownTargets); - FileGroup *extractFileGroupData(const QVariantMap &data, const QDir &srcDir, Target *t); - QList extractCrossReferences(const QVariantMap &data); - QList extractBacktrace(const QVariantList &data); - BacktraceItem *extractBacktraceItem(const QVariantMap &data); - void extractCMakeInputsData(const QVariantMap &data); - void extractCacheData(const QVariantMap &data); - - void fixTarget(Target *target) const; - - void addProjects(const QHash &cmakeListsNodes, - const QList &projects, - QSet &knownHeaders); - void addTargets(const QHash &cmakeListsNodes, - const QList &targets, - QSet &knownHeaders); - void addFileGroups(ProjectExplorer::ProjectNode *targetRoot, - const Utils::FilePath &sourceDirectory, - const Utils::FilePath &buildDirectory, - const QList &fileGroups, - QSet &knownHeaders); - - std::unique_ptr m_cmakeServer; - std::unique_ptr> m_future; - - int m_progressStepMinimum = 0; - int m_progressStepMaximum = 1000; - - Utils::optional m_delayedConfigurationData; - - QString m_delayedErrorMessage; - - CMakeConfig m_cmakeConfiguration; - - QSet m_cmakeFiles; - std::vector> m_cmakeInputsFileNodes; - - QList m_projects; - QList m_targets; - QList m_fileGroups; - - CMakeParser *m_cmakeParser = nullptr; - Utils::OutputFormatter m_parser; - -#if defined(WITH_TESTS) - friend class CMakeProjectPlugin; -#endif -}; - -} // namespace Internal -} // namespace CMakeProjectManager