forked from qt-creator/qt-creator
CMake: Implement helper to talk to CMake server-mode
Implement a helper class that can be used to talk to CMake's server-mode. Change-Id: I1df4af665991a5e0a3acb301ffd28008dd4fe86f Reviewed-by: Tim Jenssen <tim.jenssen@qt.io>
This commit is contained in:
@@ -30,6 +30,7 @@ HEADERS = builddirmanager.h \
|
|||||||
cmakeautocompleter.h \
|
cmakeautocompleter.h \
|
||||||
configmodel.h \
|
configmodel.h \
|
||||||
configmodelitemdelegate.h \
|
configmodelitemdelegate.h \
|
||||||
|
servermode.h \
|
||||||
tealeafreader.h
|
tealeafreader.h
|
||||||
|
|
||||||
SOURCES = builddirmanager.cpp \
|
SOURCES = builddirmanager.cpp \
|
||||||
@@ -58,6 +59,7 @@ SOURCES = builddirmanager.cpp \
|
|||||||
cmakeautocompleter.cpp \
|
cmakeautocompleter.cpp \
|
||||||
configmodel.cpp \
|
configmodel.cpp \
|
||||||
configmodelitemdelegate.cpp \
|
configmodelitemdelegate.cpp \
|
||||||
|
servermode.cpp \
|
||||||
tealeafreader.cpp
|
tealeafreader.cpp
|
||||||
|
|
||||||
RESOURCES += cmakeproject.qrc
|
RESOURCES += cmakeproject.qrc
|
||||||
|
|||||||
@@ -74,6 +74,8 @@ QtcPlugin {
|
|||||||
"configmodel.h",
|
"configmodel.h",
|
||||||
"configmodelitemdelegate.cpp",
|
"configmodelitemdelegate.cpp",
|
||||||
"configmodelitemdelegate.h",
|
"configmodelitemdelegate.h",
|
||||||
|
"servermode.cpp",
|
||||||
|
"servermode.h",
|
||||||
"tealeafreader.cpp",
|
"tealeafreader.cpp",
|
||||||
"tealeafreader.h"
|
"tealeafreader.h"
|
||||||
]
|
]
|
||||||
|
|||||||
447
src/plugins/cmakeprojectmanager/servermode.cpp
Normal file
447
src/plugins/cmakeprojectmanager/servermode.cpp
Normal file
@@ -0,0 +1,447 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** 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/reaper.h>
|
||||||
|
|
||||||
|
#include <utils/algorithm.h>
|
||||||
|
#include <utils/qtcassert.h>
|
||||||
|
#include <utils/qtcprocess.h>
|
||||||
|
|
||||||
|
#include <QByteArray>
|
||||||
|
#include <QJsonDocument>
|
||||||
|
#include <QJsonObject>
|
||||||
|
#include <QLocalSocket>
|
||||||
|
|
||||||
|
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";
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------
|
||||||
|
// Helpers:
|
||||||
|
// ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
QString socketName(const Utils::FileName &buildDirectory)
|
||||||
|
{
|
||||||
|
return buildDirectory.toString() + "/socket";
|
||||||
|
}
|
||||||
|
|
||||||
|
// --------------------------------------------------------------------
|
||||||
|
// ServerMode:
|
||||||
|
// --------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
ServerMode::ServerMode(const Environment &env,
|
||||||
|
const FileName &sourceDirectory, const FileName &buildDirectory,
|
||||||
|
const FileName &cmakeExecutable,
|
||||||
|
const QString &generator, const QString &extraGenerator,
|
||||||
|
const QString &platform, const QString &toolset,
|
||||||
|
bool experimental, int major, int minor,
|
||||||
|
QObject *parent) :
|
||||||
|
QObject(parent),
|
||||||
|
m_sourceDirectory(sourceDirectory), m_buildDirectory(buildDirectory),
|
||||||
|
m_cmakeExecutable(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.reset(new QtcProcess);
|
||||||
|
|
||||||
|
m_cmakeProcess->setEnvironment(env);
|
||||||
|
m_cmakeProcess->setWorkingDirectory(buildDirectory.toString());
|
||||||
|
const QStringList args = QStringList({ "-E", "server", "--pipe=" + socketName(buildDirectory) });
|
||||||
|
|
||||||
|
connect(m_cmakeProcess.get(), &QtcProcess::started, this, [this]() { m_connectionTimer.start(); });
|
||||||
|
connect(m_cmakeProcess.get(),
|
||||||
|
static_cast<void(QtcProcess::*)(int, QProcess::ExitStatus)>(&QtcProcess::finished),
|
||||||
|
this, &ServerMode::handleCMakeFinished);
|
||||||
|
|
||||||
|
QString argumentString;
|
||||||
|
QtcProcess::addArgs(&argumentString, args);
|
||||||
|
if (m_useExperimental)
|
||||||
|
QtcProcess::addArg(&argumentString, "--experimental");
|
||||||
|
|
||||||
|
m_cmakeProcess->setCommand(cmakeExecutable.toString(), argumentString);
|
||||||
|
|
||||||
|
// Delay start:
|
||||||
|
QTimer::singleShot(0, [argumentString, this] {
|
||||||
|
emit message(tr("Running \"%1 %2\" in %3.")
|
||||||
|
.arg(m_cmakeExecutable.toUserOutput())
|
||||||
|
.arg(argumentString)
|
||||||
|
.arg(m_buildDirectory.toUserOutput()));
|
||||||
|
|
||||||
|
m_cmakeProcess->start();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
ServerMode::~ServerMode()
|
||||||
|
{
|
||||||
|
if (m_cmakeProcess)
|
||||||
|
m_cmakeProcess->disconnect();
|
||||||
|
if (m_cmakeSocket) {
|
||||||
|
m_cmakeSocket->disconnect();
|
||||||
|
m_cmakeSocket->disconnectFromServer();
|
||||||
|
delete(m_cmakeSocket);
|
||||||
|
}
|
||||||
|
m_cmakeSocket = nullptr;
|
||||||
|
Core::Reaper::reap(m_cmakeProcess.release());
|
||||||
|
}
|
||||||
|
|
||||||
|
void ServerMode::sendRequest(const QString &type, const QVariantMap &extra, const QVariant &cookie)
|
||||||
|
{
|
||||||
|
QTC_ASSERT(m_cmakeSocket, return);
|
||||||
|
++m_requestCounter;
|
||||||
|
|
||||||
|
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() + END_MAGIC;
|
||||||
|
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...
|
||||||
|
|
||||||
|
const QString socketString = socketName(m_buildDirectory);
|
||||||
|
|
||||||
|
static int counter = 0;
|
||||||
|
++counter;
|
||||||
|
|
||||||
|
if (counter > 50) {
|
||||||
|
counter = 0;
|
||||||
|
m_cmakeProcess->disconnect();
|
||||||
|
reportError(tr("Running \"%1\" failed: Timeout waiting for pipe \"%2\".")
|
||||||
|
.arg(m_cmakeExecutable.toUserOutput())
|
||||||
|
.arg(socketString));
|
||||||
|
|
||||||
|
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);
|
||||||
|
connect(socket, static_cast<void(QLocalSocket::*)(QLocalSocket::LocalSocketError)>(&QLocalSocket::error),
|
||||||
|
[this, socket]() {
|
||||||
|
reportError(socket->errorString());
|
||||||
|
socket->disconnect();
|
||||||
|
socket->deleteLater();
|
||||||
|
});
|
||||||
|
connect(socket, &QLocalSocket::connected, [this, socket]() { m_cmakeSocket = socket; });
|
||||||
|
connect(socket, &QLocalSocket::disconnected, [this, socket]() {
|
||||||
|
m_cmakeSocket = nullptr;
|
||||||
|
socket->disconnect();
|
||||||
|
socket->deleteLater();
|
||||||
|
});
|
||||||
|
|
||||||
|
socket->connectToServer(socketString);
|
||||||
|
m_connectionTimer.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ServerMode::handleCMakeFinished(int code, QProcess::ExitStatus status)
|
||||||
|
{
|
||||||
|
QString msg;
|
||||||
|
if (status != QProcess::NormalExit)
|
||||||
|
msg = tr("CMake process \"%1\" crashed.").arg(m_cmakeExecutable.toUserOutput());
|
||||||
|
else if (code != 0)
|
||||||
|
msg = tr("CMake process \"%1\" quit with exit code %2.").arg(m_cmakeExecutable.toUserOutput()).arg(code);
|
||||||
|
|
||||||
|
if (!msg.isEmpty()) {
|
||||||
|
reportError(msg);
|
||||||
|
} else {
|
||||||
|
emit message(tr("CMake process \"%1\" quit normally.").arg(m_cmakeExecutable.toUserOutput()));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_cmakeSocket) {
|
||||||
|
m_cmakeSocket->disconnect();
|
||||||
|
delete m_cmakeSocket;
|
||||||
|
m_cmakeSocket = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
QFile::remove(socketName(m_buildDirectory));
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
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") {
|
||||||
|
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);
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
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->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);
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
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 QVariant cookie = data.value(COOKIE_KEY);
|
||||||
|
const QString name = data.value(NAME_KEY).toString();
|
||||||
|
|
||||||
|
if (name.isEmpty()) {
|
||||||
|
reportError(tr("Received a signal without a name."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!replyTo.isEmpty() || cookie.isValid()) {
|
||||||
|
reportError(tr("Received a signal in reply to a request."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
emit cmakeSignal(name, data);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
emit message(msg);
|
||||||
|
emit errorOccured(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Internal
|
||||||
|
} // namespace CMakeProjectManager
|
||||||
117
src/plugins/cmakeprojectmanager/servermode.h
Normal file
117
src/plugins/cmakeprojectmanager/servermode.h
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** 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 <utils/qtcprocess.h>
|
||||||
|
|
||||||
|
#include <QTemporaryDir>
|
||||||
|
#include <QTimer>
|
||||||
|
#include <QVariantMap>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
QT_FORWARD_DECLARE_CLASS(QLocalSocket);
|
||||||
|
|
||||||
|
namespace Utils { class QtcProcess; }
|
||||||
|
|
||||||
|
namespace CMakeProjectManager {
|
||||||
|
namespace Internal {
|
||||||
|
|
||||||
|
class ServerMode : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
ServerMode(const Utils::Environment &env,
|
||||||
|
const Utils::FileName &sourceDirectory, const Utils::FileName &buildDirectory,
|
||||||
|
const Utils::FileName &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 errorOccured(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);
|
||||||
|
|
||||||
|
std::unique_ptr<Utils::QtcProcess> m_cmakeProcess;
|
||||||
|
QLocalSocket *m_cmakeSocket = nullptr;
|
||||||
|
QTimer m_connectionTimer;
|
||||||
|
|
||||||
|
Utils::FileName m_sourceDirectory;
|
||||||
|
Utils::FileName m_buildDirectory;
|
||||||
|
Utils::FileName m_cmakeExecutable;
|
||||||
|
|
||||||
|
QByteArray m_buffer;
|
||||||
|
|
||||||
|
struct ExpectedReply {
|
||||||
|
QString type;
|
||||||
|
QVariant cookie;
|
||||||
|
};
|
||||||
|
std::vector<ExpectedReply> m_expectedReplies;
|
||||||
|
|
||||||
|
const QString m_generator;
|
||||||
|
const QString m_extraGenerator;
|
||||||
|
const QString m_platform;
|
||||||
|
const QString m_toolset;
|
||||||
|
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
|
||||||
Reference in New Issue
Block a user