aboutsummaryrefslogtreecommitdiffstats
path: root/src/plugins/cmakeprojectmanager/servermode.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/cmakeprojectmanager/servermode.cpp')
-rw-r--r--src/plugins/cmakeprojectmanager/servermode.cpp495
1 files changed, 0 insertions, 495 deletions
diff --git a/src/plugins/cmakeprojectmanager/servermode.cpp b/src/plugins/cmakeprojectmanager/servermode.cpp
deleted file mode 100644
index ce57e9d979..0000000000
--- a/src/plugins/cmakeprojectmanager/servermode.cpp
+++ /dev/null
@@ -1,495 +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 <coreplugin/icore.h>
-#include <coreplugin/reaper.h>
-
-#include <utils/algorithm.h>
-#include <utils/qtcassert.h>
-#include <utils/qtcprocess.h>
-#include <utils/temporarydirectory.h>
-
-#include <QByteArray>
-#include <QCryptographicHash>
-#include <QFile>
-#include <QJsonDocument>
-#include <QJsonObject>
-#include <QLocalSocket>
-#include <QUuid>
-
-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<QtcProcess>();
-
- 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<int, QProcess::ExitStatus>::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 errorOccured(msg);
-}
-
-} // namespace Internal
-} // namespace CMakeProjectManager