Clang: Move QLocalServer in ConnectionClient

Before the QLocalServer was in the ConnectionServer so more than one
client could connect to the server. But we never used that possibility
which made the hand shaking much more difficult. It is now moved
in the client, so that there is always a QLocalServer.

Change-Id: Ifa357074b0c0809434c49d23b1cee38496f72f43
Reviewed-by: Ivan Donchevskii <ivan.donchevskii@qt.io>
Reviewed-by: Nikolai Kosjar <nikolai.kosjar@qt.io>
This commit is contained in:
Marco Bubke
2016-08-17 15:29:37 +02:00
parent 45c667d52c
commit f70bf3d2d1
44 changed files with 1170 additions and 351 deletions

View File

@@ -0,0 +1,61 @@
/****************************************************************************
**
** Copyright (C) 2017 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 "baseserverproxy.h"
#include "messageenvelop.h"
#include <QIODevice>
namespace ClangBackEnd {
BaseServerProxy::BaseServerProxy(IpcClientInterface *client, QIODevice *ioDevice)
: m_writeMessageBlock(ioDevice),
m_readMessageBlock(ioDevice),
m_client(client)
{
if (ioDevice)
QObject::connect(ioDevice, &QIODevice::readyRead, [this] () { BaseServerProxy::readMessages(); });
}
void BaseServerProxy::readMessages()
{
for (const auto &message : m_readMessageBlock.readAll())
m_client->dispatch(message);
}
void BaseServerProxy::resetCounter()
{
m_writeMessageBlock.resetCounter();
m_readMessageBlock.resetCounter();
}
void BaseServerProxy::setIoDevice(QIODevice *ioDevice)
{
QObject::connect(ioDevice, &QIODevice::readyRead, [this] () { BaseServerProxy::readMessages(); });
m_writeMessageBlock.setIoDevice(ioDevice);
m_readMessageBlock.setIoDevice(ioDevice);
}
} // namespace ClangBackEnd

View File

@@ -0,0 +1,54 @@
/****************************************************************************
**
** Copyright (C) 2017 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 "ipcclientinterface.h"
#include "readmessageblock.h"
#include "writemessageblock.h"
namespace ClangBackEnd {
class CLANGSUPPORT_EXPORT BaseServerProxy
{
BaseServerProxy(const BaseServerProxy&) = delete;
BaseServerProxy &operator=(const BaseServerProxy&) = delete;
public:
BaseServerProxy(IpcClientInterface *client, QIODevice *ioDevice);
void readMessages();
void resetCounter();
void setIoDevice(QIODevice *ioDevice);
protected:
ClangBackEnd::WriteMessageBlock m_writeMessageBlock;
ClangBackEnd::ReadMessageBlock m_readMessageBlock;
IpcClientInterface *m_client;
};
} // namespace ClangBackEnd

View File

@@ -25,6 +25,8 @@
#include "clangcodemodelconnectionclient.h" #include "clangcodemodelconnectionclient.h"
#include <utils/temporarydirectory.h>
#include <QCoreApplication> #include <QCoreApplication>
namespace ClangBackEnd { namespace ClangBackEnd {
@@ -40,8 +42,14 @@ QString currentProcessId()
ClangCodeModelConnectionClient::ClangCodeModelConnectionClient( ClangCodeModelConnectionClient::ClangCodeModelConnectionClient(
ClangCodeModelClientInterface *client) ClangCodeModelClientInterface *client)
: serverProxy_(client, ioDevice()) : ConnectionClient(Utils::TemporaryDirectory::masterDirectoryPath()
+ QStringLiteral("/ClangBackEnd-")
+ currentProcessId()),
m_serverProxy(client, nullptr),
m_client(client)
{ {
m_processCreator.setTemporaryDirectoryPattern("clangbackend-XXXXXX");
stdErrPrefixer().setPrefix("clangbackend.stderr: "); stdErrPrefixer().setPrefix("clangbackend.stderr: ");
stdOutPrefixer().setPrefix("clangbackend.stdout: "); stdOutPrefixer().setPrefix("clangbackend.stdout: ");
} }
@@ -53,22 +61,17 @@ ClangCodeModelConnectionClient::~ClangCodeModelConnectionClient()
ClangCodeModelServerProxy &ClangCodeModelConnectionClient::serverProxy() ClangCodeModelServerProxy &ClangCodeModelConnectionClient::serverProxy()
{ {
return serverProxy_; return m_serverProxy;
} }
void ClangCodeModelConnectionClient::sendEndCommand() void ClangCodeModelConnectionClient::sendEndCommand()
{ {
serverProxy_.end(); m_serverProxy.end();
} }
void ClangCodeModelConnectionClient::resetCounter() void ClangCodeModelConnectionClient::resetCounter()
{ {
serverProxy_.resetCounter(); m_serverProxy.resetCounter();
}
QString ClangCodeModelConnectionClient::connectionName() const
{
return temporaryDirectory().path() + QStringLiteral("/ClangBackEnd-") + currentProcessId();
} }
QString ClangCodeModelConnectionClient::outputName() const QString ClangCodeModelConnectionClient::outputName() const
@@ -76,4 +79,9 @@ QString ClangCodeModelConnectionClient::outputName() const
return QStringLiteral("ClangCodeModelConnectionClient"); return QStringLiteral("ClangCodeModelConnectionClient");
} }
void ClangCodeModelConnectionClient::newConnectedServer(QIODevice *ioDevice)
{
m_serverProxy.setIoDevice(ioDevice);
}
} // namespace ClangBackEnd } // namespace ClangBackEnd

View File

@@ -35,17 +35,17 @@ public:
ClangCodeModelConnectionClient(ClangCodeModelClientInterface *client); ClangCodeModelConnectionClient(ClangCodeModelClientInterface *client);
~ClangCodeModelConnectionClient(); ~ClangCodeModelConnectionClient();
ClangCodeModelServerProxy &serverProxy(); ClangCodeModelServerProxy &serverProxy();
protected: protected:
void sendEndCommand() override; void sendEndCommand() override;
void resetCounter() override; void resetCounter() override;
QString connectionName() const override;
QString outputName() const override; QString outputName() const override;
void newConnectedServer(QIODevice *ioDevice) override;
private: private:
ClangCodeModelServerProxy serverProxy_; ClangCodeModelServerProxy m_serverProxy;
ClangCodeModelClientInterface *m_client;
}; };
} // namespace ClangBackEnd } // namespace ClangBackEnd

View File

@@ -29,30 +29,11 @@
#include <clangcodemodelservermessages.h> #include <clangcodemodelservermessages.h>
#include <messageenvelop.h> #include <messageenvelop.h>
#include <QLocalServer>
#include <QLocalSocket>
#include <QProcess>
namespace ClangBackEnd { namespace ClangBackEnd {
ClangCodeModelServerProxy::ClangCodeModelServerProxy(ClangCodeModelClientInterface *client, QIODevice *ioDevice) ClangCodeModelServerProxy::ClangCodeModelServerProxy(ClangCodeModelClientInterface *client, QIODevice *ioDevice)
: m_writeMessageBlock(ioDevice), : BaseServerProxy(client, ioDevice)
m_readMessageBlock(ioDevice),
m_client(client)
{ {
QObject::connect(ioDevice, &QIODevice::readyRead, [this] () {ClangCodeModelServerProxy::readMessages();});
}
void ClangCodeModelServerProxy::readMessages()
{
for (const auto &message : m_readMessageBlock.readAll())
m_client->dispatch(message);
}
void ClangCodeModelServerProxy::resetCounter()
{
m_writeMessageBlock.resetCounter();
m_readMessageBlock.resetCounter();
} }
void ClangCodeModelServerProxy::end() void ClangCodeModelServerProxy::end()

View File

@@ -24,9 +24,9 @@
****************************************************************************/ ****************************************************************************/
#pragma once #pragma once
#include "baseserverproxy.h"
#include "clangcodemodelserverinterface.h" #include "clangcodemodelserverinterface.h"
#include "readmessageblock.h"
#include "writemessageblock.h"
#include <QtGlobal> #include <QtGlobal>
#include <QTimer> #include <QTimer>
@@ -42,12 +42,11 @@ QT_END_NAMESPACE
namespace ClangBackEnd { namespace ClangBackEnd {
class CLANGSUPPORT_EXPORT ClangCodeModelServerProxy : public ClangCodeModelServerInterface class CLANGSUPPORT_EXPORT ClangCodeModelServerProxy : public BaseServerProxy,
public ClangCodeModelServerInterface
{ {
public: public:
ClangCodeModelServerProxy(ClangCodeModelClientInterface *client, QIODevice *ioDevice); ClangCodeModelServerProxy(ClangCodeModelClientInterface *client, QIODevice *ioDevice);
ClangCodeModelServerProxy(const ClangCodeModelServerProxy&) = delete;
ClangCodeModelServerProxy &operator=(const ClangCodeModelServerProxy&) = delete;
void end() override; void end() override;
void registerTranslationUnitsForEditor(const RegisterTranslationUnitForEditorMessage &message) override; void registerTranslationUnitsForEditor(const RegisterTranslationUnitForEditorMessage &message) override;
@@ -62,15 +61,6 @@ public:
void requestReferences(const RequestReferencesMessage &message) override; void requestReferences(const RequestReferencesMessage &message) override;
void requestFollowSymbol(const RequestFollowSymbolMessage &message) override; void requestFollowSymbol(const RequestFollowSymbolMessage &message) override;
void updateVisibleTranslationUnits(const UpdateVisibleTranslationUnitsMessage &message) override; void updateVisibleTranslationUnits(const UpdateVisibleTranslationUnitsMessage &message) override;
void readMessages();
void resetCounter();
private:
ClangBackEnd::WriteMessageBlock m_writeMessageBlock;
ClangBackEnd::ReadMessageBlock m_readMessageBlock;
ClangCodeModelClientInterface *m_client;
}; };
} // namespace ClangBackEnd } // namespace ClangBackEnd

View File

@@ -74,6 +74,9 @@ SOURCES += \
$$PWD/sourcelocationscontainer.cpp \ $$PWD/sourcelocationscontainer.cpp \
$$PWD/sourcelocationsforrenamingmessage.cpp \ $$PWD/sourcelocationsforrenamingmessage.cpp \
$$PWD/sourcerangecontainer.cpp \ $$PWD/sourcerangecontainer.cpp \
$$PWD/processcreator.cpp \
$$PWD/processexception.cpp \
$$PWD/processstartedevent.cpp \
$$PWD/sourcerangecontainerv2.cpp \ $$PWD/sourcerangecontainerv2.cpp \
$$PWD/sourcerangesanddiagnosticsforquerymessage.cpp \ $$PWD/sourcerangesanddiagnosticsforquerymessage.cpp \
$$PWD/sourcerangescontainer.cpp \ $$PWD/sourcerangescontainer.cpp \
@@ -85,7 +88,8 @@ SOURCES += \
$$PWD/updatevisibletranslationunitsmessage.cpp \ $$PWD/updatevisibletranslationunitsmessage.cpp \
$$PWD/writemessageblock.cpp \ $$PWD/writemessageblock.cpp \
$$PWD/filepathcaching.cpp \ $$PWD/filepathcaching.cpp \
$$PWD/filepathid.cpp $$PWD/filepathid.cpp \
$$PWD/baseserverproxy.cpp
HEADERS += \ HEADERS += \
$$PWD/cancelmessage.h \ $$PWD/cancelmessage.h \
@@ -161,6 +165,11 @@ HEADERS += \
$$PWD/sourcelocationscontainer.h \ $$PWD/sourcelocationscontainer.h \
$$PWD/sourcelocationsforrenamingmessage.h \ $$PWD/sourcelocationsforrenamingmessage.h \
$$PWD/sourcerangecontainer.h \ $$PWD/sourcerangecontainer.h \
$$PWD/filepath.h \
$$PWD/processcreator.h \
$$PWD/processexception.h \
$$PWD/processhandle.h \
$$PWD/processstartedevent.h \
$$PWD/sourcerangecontainerv2.h \ $$PWD/sourcerangecontainerv2.h \
$$PWD/sourcerangesanddiagnosticsforquerymessage.h \ $$PWD/sourcerangesanddiagnosticsforquerymessage.h \
$$PWD/sourcerangescontainer.h \ $$PWD/sourcerangescontainer.h \
@@ -187,6 +196,7 @@ HEADERS += \
$$PWD/filepathcachinginterface.h \ $$PWD/filepathcachinginterface.h \
$$PWD/filepathcaching.h \ $$PWD/filepathcaching.h \
$$PWD/filepathcachingfwd.h \ $$PWD/filepathcachingfwd.h \
$$PWD/baseserverproxy.h \
$$PWD/nativefilepathview.h \ $$PWD/nativefilepathview.h \
$$PWD/filepath.h \ $$PWD/filepath.h \
$$PWD/nativefilepath.h \ $$PWD/nativefilepath.h \

View File

@@ -26,6 +26,10 @@
#include "connectionclient.h" #include "connectionclient.h"
#include "clangsupportdebugutils.h" #include "clangsupportdebugutils.h"
#include "processstartedevent.h"
#include "processexception.h"
#include <utils/hostosinfo.h>
#include <QCoreApplication> #include <QCoreApplication>
#include <QMetaMethod> #include <QMetaMethod>
@@ -34,50 +38,52 @@
namespace ClangBackEnd { namespace ClangBackEnd {
ConnectionClient::ConnectionClient() ConnectionClient::ConnectionClient(const QString &connectionName)
: m_connectionName(connectionName)
{ {
m_processCreator.setArguments({connectionName});
m_processCreator.setObserver(this);
listenForConnections();
m_processAliveTimer.setInterval(10000); m_processAliveTimer.setInterval(10000);
resetTemporaryDir(); resetTemporaryDirectory();
static const bool startAliveTimer = !qEnvironmentVariableIntValue("QTC_CLANG_NO_ALIVE_TIMER"); static const bool startAliveTimer = !qEnvironmentVariableIntValue("QTC_CLANG_NO_ALIVE_TIMER");
if (startAliveTimer) if (startAliveTimer)
connectAliveTimer(); connectAliveTimer();
connectLocalSocketError(); connectNewConnection();
connectLocalSocketConnected(); }
connectLocalSocketDisconnected();
ConnectionClient::~ConnectionClient()
{
QLocalServer::removeServer(connectionName());
} }
void ConnectionClient::startProcessAndConnectToServerAsynchronously() void ConnectionClient::startProcessAndConnectToServerAsynchronously()
{ {
m_process = startProcess(); m_processIsStarting = true;
}
bool ConnectionClient::disconnectFromServer() m_processFuture = m_processCreator.createProcess();
{
localSocket.disconnectFromServer();
if (localSocket.state() != QLocalSocket::UnconnectedState)
return localSocket.waitForDisconnected();
return true;
} }
bool ConnectionClient::isConnected() const bool ConnectionClient::isConnected() const
{ {
return localSocket.state() == QLocalSocket::ConnectedState; return m_localSocket && m_localSocket->state() == QLocalSocket::ConnectedState;
} }
void ConnectionClient::ensureMessageIsWritten() void ConnectionClient::ensureMessageIsWritten()
{ {
while (isConnected() && localSocket.bytesToWrite() > 0) while (isConnected() && m_localSocket->bytesToWrite() > 0)
localSocket.waitForBytesWritten(50); m_localSocket->waitForBytesWritten(50);
} }
void ConnectionClient::sendEndMessage() void ConnectionClient::sendEndMessage()
{ {
sendEndCommand(); sendEndCommand();
localSocket.flush(); m_localSocket->flush();
ensureMessageIsWritten(); ensureMessageIsWritten();
} }
@@ -108,7 +114,7 @@ QProcessEnvironment ConnectionClient::processEnvironment() const
const QTemporaryDir &ConnectionClient::temporaryDirectory() const const QTemporaryDir &ConnectionClient::temporaryDirectory() const
{ {
return *m_temporaryDirectory; return m_processCreator.temporaryDirectory();
} }
LinePrefixer &ConnectionClient::stdErrPrefixer() LinePrefixer &ConnectionClient::stdErrPrefixer()
@@ -121,29 +127,31 @@ LinePrefixer &ConnectionClient::stdOutPrefixer()
return m_stdOutPrefixer; return m_stdOutPrefixer;
} }
std::unique_ptr<QProcess> ConnectionClient::startProcess() QString ConnectionClient::connectionName() const
{ {
m_processIsStarting = true; return m_connectionName;
}
auto process = std::unique_ptr<QProcess>(new QProcess); bool ConnectionClient::event(QEvent *event)
connectProcessFinished(process.get()); {
connectProcessStarted(process.get()); if (event->type() == int(ProcessStartedEvent::ProcessStarted)) {
connectStandardOutputAndError(process.get()); getProcessFromFuture();
process->setProcessEnvironment(processEnvironment());
process->start(processPath(), {connectionName()});
resetProcessAliveTimer();
return process; return true;
};
return false;
} }
void ConnectionClient::restartProcessAsynchronously() void ConnectionClient::restartProcessAsynchronously()
{ {
if (!m_processIsStarting) { getProcessFromFuture();
finishProcess(std::move(m_process));
resetTemporaryDir(); // clear left-over preambles finishProcess(std::move(m_process));
resetTemporaryDirectory(); // clear left-over preambles
startProcessAndConnectToServerAsynchronously();
startProcessAndConnectToServerAsynchronously();
}
} }
void ConnectionClient::restartProcessIfTimerIsNotResettedAndSocketIsEmpty() void ConnectionClient::restartProcessIfTimerIsNotResettedAndSocketIsEmpty()
@@ -153,23 +161,15 @@ void ConnectionClient::restartProcessIfTimerIsNotResettedAndSocketIsEmpty()
return; // Already reset, but we were scheduled after. return; // Already reset, but we were scheduled after.
} }
if (localSocket.bytesAvailable() > 0) if (!m_localSocket || m_localSocket->bytesAvailable() > 0)
return; // We come first, the incoming data was not yet processed. return; // We come first, the incoming data was not yet processed.
restartProcessAsynchronously(); restartProcessAsynchronously();
} }
void ConnectionClient::connectToLocalSocket()
{
if (!isConnected()) {
localSocket.connectToServer(connectionName());
QTimer::singleShot(20, this, &ConnectionClient::connectToLocalSocket);
}
}
void ConnectionClient::endProcess(QProcess *process) void ConnectionClient::endProcess(QProcess *process)
{ {
if (isProcessIsRunning() && isConnected()) { if (isProcessRunning(process) && isConnected()) {
sendEndMessage(); sendEndMessage();
process->waitForFinished(); process->waitForFinished();
} }
@@ -177,32 +177,33 @@ void ConnectionClient::endProcess(QProcess *process)
void ConnectionClient::terminateProcess(QProcess *process) void ConnectionClient::terminateProcess(QProcess *process)
{ {
Q_UNUSED(process) if (!Utils::HostOsInfo::isWindowsHost() && isProcessRunning()) {
#ifndef Q_OS_WIN32
if (isProcessIsRunning()) {
process->terminate(); process->terminate();
process->waitForFinished(); process->waitForFinished(1000);
} }
#endif
} }
void ConnectionClient::killProcess(QProcess *process) void ConnectionClient::killProcess(QProcess *process)
{ {
if (isProcessIsRunning()) { if (isProcessRunning(process)) {
process->kill(); process->kill();
process->waitForFinished(); process->waitForFinished(1000);
} }
} }
void ConnectionClient::resetProcessIsStarting() void ConnectionClient::finishConnection()
{ {
m_processIsStarting = false; if (m_localSocket) {
if (m_localSocket->state() != QLocalSocket::UnconnectedState)
m_localSocket->disconnectFromServer();
m_localSocket = nullptr;
}
} }
void ConnectionClient::printLocalSocketError(QLocalSocket::LocalSocketError socketError) void ConnectionClient::printLocalSocketError(QLocalSocket::LocalSocketError socketError)
{ {
if (socketError != QLocalSocket::ServerNotFoundError) if (m_localSocket && socketError != QLocalSocket::ServerNotFoundError)
qWarning() << outputName() << "LocalSocket Error:" << localSocket.errorString(); qWarning() << outputName() << "LocalSocket Error:" << m_localSocket->errorString();
} }
void ConnectionClient::printStandardOutput() void ConnectionClient::printStandardOutput()
@@ -215,55 +216,74 @@ void ConnectionClient::printStandardError()
qDebug("%s", m_stdErrPrefixer.prefix(m_process->readAllStandardError()).constData()); qDebug("%s", m_stdErrPrefixer.prefix(m_process->readAllStandardError()).constData());
} }
void ConnectionClient::resetTemporaryDir() void ConnectionClient::resetTemporaryDirectory()
{ {
m_temporaryDirectory = std::make_unique<Utils::TemporaryDirectory>("clang-XXXXXX"); m_processCreator.resetTemporaryDirectory();
} }
void ConnectionClient::connectLocalSocketConnected() void ConnectionClient::initializeProcess(QProcess *process)
{ {
connect(&localSocket, connectStandardOutputAndError(process);
&QLocalSocket::connected,
this,
&ConnectionClient::connectedToLocalSocket);
connect(&localSocket, resetProcessAliveTimer();
&QLocalSocket::connected,
this,
&ConnectionClient::resetProcessIsStarting);
} }
void ConnectionClient::connectLocalSocketDisconnected() void ConnectionClient::connectLocalSocketDisconnected()
{ {
connect(&localSocket, connect(m_localSocket,
&QLocalSocket::disconnected, &QLocalSocket::disconnected,
this, this,
&ConnectionClient::disconnectedFromLocalSocket); &ConnectionClient::disconnectedFromLocalSocket);
connect(m_localSocket,
&QLocalSocket::disconnected,
this,
&ConnectionClient::restartProcessAsynchronously);
}
void ConnectionClient::disconnectLocalSocketDisconnected()
{
if (m_localSocket) {
disconnect(m_localSocket,
&QLocalSocket::disconnected,
this,
&ConnectionClient::restartProcessAsynchronously);
}
} }
void ConnectionClient::finishProcess() void ConnectionClient::finishProcess()
{ {
finishProcess(std::move(m_process)); finishProcess(std::move(m_process));
emit processFinished();
} }
void ConnectionClient::finishProcess(std::unique_ptr<QProcess> &&process) bool ConnectionClient::isProcessRunning()
{ {
getProcessFromFuture();
return isProcessRunning(m_process.get());
}
void ConnectionClient::finishProcess(QProcessUniquePointer &&process)
{
disconnectLocalSocketDisconnected();
if (process) { if (process) {
m_processAliveTimer.stop(); m_processAliveTimer.stop();
disconnectProcessFinished(process.get());
endProcess(process.get()); endProcess(process.get());
disconnectFromServer(); finishConnection();
terminateProcess(process.get()); terminateProcess(process.get());
killProcess(process.get()); killProcess(process.get());
resetCounter(); resetCounter();
} else {
finishConnection();
} }
} }
bool ConnectionClient::waitForEcho() bool ConnectionClient::waitForEcho()
{ {
return localSocket.waitForReadyRead(); return m_localSocket->waitForReadyRead();
} }
bool ConnectionClient::waitForConnected() bool ConnectionClient::waitForConnected()
@@ -271,7 +291,7 @@ bool ConnectionClient::waitForConnected()
bool isConnected = false; bool isConnected = false;
for (int counter = 0; counter < 100; counter++) { for (int counter = 0; counter < 100; counter++) {
isConnected = localSocket.waitForConnected(20); isConnected = m_localSocket && m_localSocket->waitForConnected(20);
if (isConnected) if (isConnected)
return isConnected; return isConnected;
else { else {
@@ -280,52 +300,28 @@ bool ConnectionClient::waitForConnected()
} }
} }
qWarning() << outputName() << "cannot connect:" << localSocket.errorString(); if (m_localSocket)
qWarning() << outputName() << "cannot connect:" << m_localSocket->errorString();
return isConnected; return isConnected;
} }
QProcess *ConnectionClient::processForTestOnly() const QProcess *ConnectionClient::processForTestOnly()
{ {
getProcessFromFuture();
return m_process.get(); return m_process.get();
} }
QIODevice *ConnectionClient::ioDevice() QIODevice *ConnectionClient::ioDevice()
{ {
return &localSocket; return m_localSocket;
} }
bool ConnectionClient::isProcessIsRunning() const bool ConnectionClient::isProcessRunning(QProcess *process)
{ {
return m_process && m_process->state() == QProcess::Running; return process && process->state() == QProcess::Running;
}
void ConnectionClient::connectProcessFinished(QProcess *process) const
{
connect(process,
static_cast<void (QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished),
this,
&ConnectionClient::restartProcessAsynchronously);
}
void ConnectionClient::connectProcessStarted(QProcess *process) const
{
connect(process,
&QProcess::started,
this,
&ConnectionClient::connectToLocalSocket);
}
void ConnectionClient::disconnectProcessFinished(QProcess *process) const
{
if (process) {
disconnect(process,
static_cast<void (QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished),
this,
&ConnectionClient::restartProcessAsynchronously);
}
} }
void ConnectionClient::connectStandardOutputAndError(QProcess *process) const void ConnectionClient::connectStandardOutputAndError(QProcess *process) const
@@ -336,7 +332,7 @@ void ConnectionClient::connectStandardOutputAndError(QProcess *process) const
void ConnectionClient::connectLocalSocketError() const void ConnectionClient::connectLocalSocketError() const
{ {
connect(&localSocket, connect(m_localSocket,
static_cast<void (QLocalSocket::*)(QLocalSocket::LocalSocketError)>(&QLocalSocket::error), static_cast<void (QLocalSocket::*)(QLocalSocket::LocalSocketError)>(&QLocalSocket::error),
this, this,
&ConnectionClient::printLocalSocketError); &ConnectionClient::printLocalSocketError);
@@ -350,14 +346,52 @@ void ConnectionClient::connectAliveTimer()
&ConnectionClient::restartProcessIfTimerIsNotResettedAndSocketIsEmpty); &ConnectionClient::restartProcessIfTimerIsNotResettedAndSocketIsEmpty);
} }
const QString &ConnectionClient::processPath() const void ConnectionClient::connectNewConnection()
{ {
return m_processPath; QObject::connect(&m_localServer,
&QLocalServer::newConnection,
this,
&ConnectionClient::handleNewConnection);
}
void ConnectionClient::handleNewConnection()
{
m_localSocket = m_localServer.nextPendingConnection();
connectLocalSocketError();
connectLocalSocketDisconnected();
newConnectedServer(m_localSocket);
emit connectedToLocalSocket();
}
void ConnectionClient::getProcessFromFuture()
{
try {
if (m_processFuture.valid()) {
m_process = m_processFuture.get();
m_processIsStarting = false;
initializeProcess(m_process.get());
}
} catch (const ProcessException &processExeption) {
qWarning() << "Clang backend process is not working."
<< QLatin1String(processExeption.what());
}
}
void ConnectionClient::listenForConnections()
{
bool isListing = m_localServer.listen(connectionName());
if (!isListing)
qWarning() << "ConnectionClient: QLocalServer is not listing for connections!";
} }
void ConnectionClient::setProcessPath(const QString &processPath) void ConnectionClient::setProcessPath(const QString &processPath)
{ {
m_processPath = processPath; m_processCreator.setProcessPath(processPath);
} }
} // namespace ClangBackEnd } // namespace ClangBackEnd

View File

@@ -27,14 +27,15 @@
#include "clangcodemodelserverproxy.h" #include "clangcodemodelserverproxy.h"
#include "lineprefixer.h" #include "lineprefixer.h"
#include "processcreator.h"
#include <utils/temporarydirectory.h> #include <QLocalServer>
#include <QLocalSocket> #include <QLocalSocket>
#include <QProcessEnvironment> #include <QProcessEnvironment>
#include <QScopedPointer> #include <QScopedPointer>
#include <QTemporaryDir> #include <QTemporaryDir>
#include <future>
#include <memory> #include <memory>
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
@@ -53,10 +54,10 @@ class CLANGSUPPORT_EXPORT ConnectionClient : public QObject
Q_OBJECT Q_OBJECT
public: public:
ConnectionClient(); ConnectionClient(const QString &connectionName);
virtual ~ConnectionClient();
void startProcessAndConnectToServerAsynchronously(); void startProcessAndConnectToServerAsynchronously();
bool disconnectFromServer();
bool isConnected() const; bool isConnected() const;
void sendEndMessage(); void sendEndMessage();
@@ -64,18 +65,17 @@ public:
void resetProcessAliveTimer(); void resetProcessAliveTimer();
void setProcessAliveTimerInterval(int processTimerInterval); void setProcessAliveTimerInterval(int processTimerInterval);
const QString &processPath() const;
void setProcessPath(const QString &processPath); void setProcessPath(const QString &processPath);
void restartProcessAsynchronously(); void restartProcessAsynchronously();
void restartProcessIfTimerIsNotResettedAndSocketIsEmpty(); void restartProcessIfTimerIsNotResettedAndSocketIsEmpty();
void finishProcess(); void finishProcess();
bool isProcessIsRunning() const; bool isProcessRunning();
bool waitForEcho(); bool waitForEcho();
bool waitForConnected(); bool waitForConnected();
QProcess *processForTestOnly() const; QProcess *processForTestOnly();
signals: signals:
void connectedToLocalSocket(); void connectedToLocalSocket();
@@ -90,48 +90,55 @@ protected:
virtual void sendEndCommand() = 0; virtual void sendEndCommand() = 0;
virtual void resetCounter() = 0; virtual void resetCounter() = 0;
virtual QString connectionName() const = 0;
virtual QString outputName() const = 0; virtual QString outputName() const = 0;
QString connectionName() const;
bool event(QEvent* event);
virtual void newConnectedServer(QIODevice *ioDevice) = 0;
private: private:
std::unique_ptr<QProcess> startProcess(); static bool isProcessRunning(QProcess *process);
void finishProcess(std::unique_ptr<QProcess> &&process); void finishProcess(QProcessUniquePointer &&process);
void connectToLocalSocket();
void endProcess(QProcess *process); void endProcess(QProcess *process);
void terminateProcess(QProcess *process); void terminateProcess(QProcess *process);
void killProcess(QProcess *process); void killProcess(QProcess *process);
void resetProcessIsStarting(); void finishConnection();
void printLocalSocketError(QLocalSocket::LocalSocketError socketError); void printLocalSocketError(QLocalSocket::LocalSocketError socketError);
void printStandardOutput(); void printStandardOutput();
void printStandardError(); void printStandardError();
void initializeProcess(QProcess *process);
void resetTemporaryDir(); void resetTemporaryDirectory();
void connectLocalSocketConnected();
void connectLocalSocketDisconnected(); void connectLocalSocketDisconnected();
void connectProcessFinished(QProcess *process) const; void disconnectLocalSocketDisconnected();
void connectProcessStarted(QProcess *process) const;
void disconnectProcessFinished(QProcess *process) const;
void connectStandardOutputAndError(QProcess *process) const; void connectStandardOutputAndError(QProcess *process) const;
void connectLocalSocketError() const; void connectLocalSocketError() const;
void connectAliveTimer(); void connectAliveTimer();
void connectNewConnection();
void handleNewConnection();
void getProcessFromFuture();
void listenForConnections();
void ensureMessageIsWritten(); void ensureMessageIsWritten();
QProcessEnvironment processEnvironment() const; QProcessEnvironment processEnvironment() const;
protected:
ProcessCreator m_processCreator;
private: private:
LinePrefixer m_stdErrPrefixer; LinePrefixer m_stdErrPrefixer;
LinePrefixer m_stdOutPrefixer; LinePrefixer m_stdOutPrefixer;
QLocalServer m_localServer;
mutable std::unique_ptr<QProcess> m_process; std::future<QProcessUniquePointer> m_processFuture;
QLocalSocket localSocket; mutable QProcessUniquePointer m_process;
std::unique_ptr<Utils::TemporaryDirectory> m_temporaryDirectory; QLocalSocket *m_localSocket = nullptr;
QTimer m_processAliveTimer; QTimer m_processAliveTimer;
QString m_processPath; QString m_connectionName;
bool m_isAliveTimerResetted = false; bool m_isAliveTimerResetted = false;
bool m_processIsStarting = false; bool m_processIsStarting = false;
}; };
} // namespace ClangBackEnd } // namespace ClangBackEnd

View File

@@ -27,7 +27,5 @@
namespace ClangBackEnd { namespace ClangBackEnd {
QString ConnectionName::connectionName;
} // namespace ClangBackEnd } // namespace ClangBackEnd

View File

@@ -31,6 +31,7 @@
#include <QLocalServer> #include <QLocalServer>
#include <QLocalSocket> #include <QLocalSocket>
#include <QTimer> #include <QTimer>
#include <QDebug>
#include <cstdlib> #include <cstdlib>
#include <memory> #include <memory>
@@ -41,46 +42,28 @@ namespace ClangBackEnd {
class ClangCodeModelServerInterface; class ClangCodeModelServerInterface;
class ClangCodeModelClientProxy; class ClangCodeModelClientProxy;
struct CLANGSUPPORT_EXPORT ConnectionName {
static QString connectionName;
};
template <typename ServerInterface, template <typename ServerInterface,
typename ClientProxy> typename ClientProxy>
class ConnectionServer class ConnectionServer
{ {
public: public:
ConnectionServer(const QString &connectionName) ConnectionServer()
{ {
ConnectionName::connectionName = connectionName;
m_aliveTimer.start(5000); m_aliveTimer.start(5000);
m_localServer.setMaxPendingConnections(1); connectAliveTimer();
connectLocalSocketDisconnet();
QObject::connect(&m_localServer,
&QLocalServer::newConnection,
[&] { handleNewConnection(); });
QObject::connect(&m_aliveTimer,
&QTimer::timeout,
[&] { sendAliveMessage(); });
std::atexit(&ConnectionServer::removeServer);
#if defined(_GLIBCXX_HAVE_AT_QUICK_EXIT)
std::at_quick_exit(&ConnectionServer::removeServer);
#endif
std::set_terminate(&ConnectionServer::removeServer);
} }
~ConnectionServer() ~ConnectionServer()
{ {
removeServer(); if (m_localSocket.state() != QLocalSocket::UnconnectedState)
m_localSocket.disconnectFromServer();
} }
void start() void start(const QString &connectionName)
{ {
QLocalServer::removeServer(ConnectionName::connectionName); connectToLocalServer(connectionName);
m_localServer.listen(ConnectionName::connectionName);
} }
void setServer(ServerInterface *ipcServer) void setServer(ServerInterface *ipcServer)
@@ -89,17 +72,17 @@ public:
} }
static void removeServer()
{
QLocalServer::removeServer(ConnectionName::connectionName);
}
private: private:
void handleNewConnection() void connectToLocalServer(const QString &connectionName)
{ {
m_localSocket = nextPendingConnection(); QObject::connect(&m_localSocket,
static_cast<void (QLocalSocket::*)(QLocalSocket::LocalSocketError)>(&QLocalSocket::error),
[&] (QLocalSocket::LocalSocketError) {
qWarning() << "ConnectionServer error:" << m_localSocket.errorString() << connectionName;
});
m_ipcClientProxy.reset(new ClientProxy(m_ipcServer, m_localSocket)); m_localSocket.connectToServer(connectionName);
m_ipcClientProxy = std::make_unique<ClientProxy>(m_ipcServer, &m_localSocket);
m_ipcServer->setClient(m_ipcClientProxy.get()); m_ipcServer->setClient(m_ipcClientProxy.get());
} }
@@ -114,40 +97,40 @@ private:
{ {
m_ipcClientProxy.reset(); m_ipcClientProxy.reset();
m_localSocket = nullptr;
delayedExitApplicationIfNoSockedIsConnected(); delayedExitApplicationIfNoSockedIsConnected();
} }
QLocalSocket *nextPendingConnection()
{
QLocalSocket *localSocket = m_localServer.nextPendingConnection();
QObject::connect(localSocket,
&QLocalSocket::disconnected,
[&] { handleSocketDisconnect(); });
return localSocket;
}
void delayedExitApplicationIfNoSockedIsConnected() void delayedExitApplicationIfNoSockedIsConnected()
{ {
if (m_localSocket == nullptr) QTimer::singleShot(60000, [&] { exitApplicationIfNoSockedIsConnected(); });
QTimer::singleShot(60000, [&] { exitApplicationIfNoSockedIsConnected(); });
} }
void exitApplicationIfNoSockedIsConnected() void exitApplicationIfNoSockedIsConnected()
{ {
if (m_localSocket == nullptr) if (m_localSocket.state() != QLocalSocket::UnconnectedState)
QCoreApplication::exit(); m_localSocket.disconnectFromServer();
QCoreApplication::exit();
}
void connectAliveTimer()
{
QObject::connect(&m_aliveTimer,
&QTimer::timeout,
[&] { sendAliveMessage(); });
}
void connectLocalSocketDisconnet()
{
QObject::connect(&m_localSocket,
&QLocalSocket::disconnected,
[&] { handleSocketDisconnect(); });
} }
private: private:
std::unique_ptr<ClientProxy> m_ipcClientProxy; QLocalSocket m_localSocket;
QLocalSocket* m_localSocket;
ServerInterface *m_ipcServer;
QLocalServer m_localServer;
QTimer m_aliveTimer; QTimer m_aliveTimer;
std::unique_ptr<ClientProxy> m_ipcClientProxy;
ServerInterface *m_ipcServer;
}; };
} // namespace ClangBackEnd } // namespace ClangBackEnd

View File

@@ -37,38 +37,23 @@
namespace ClangBackEnd { namespace ClangBackEnd {
PchManagerServerProxy::PchManagerServerProxy(PchManagerClientInterface *client, QIODevice *ioDevice) PchManagerServerProxy::PchManagerServerProxy(PchManagerClientInterface *client, QIODevice *ioDevice)
: writeMessageBlock(ioDevice), : BaseServerProxy(client, ioDevice)
readMessageBlock(ioDevice),
client(client)
{ {
QObject::connect(ioDevice, &QIODevice::readyRead, [this] () { readMessages(); });
} }
void PchManagerServerProxy::end() void PchManagerServerProxy::end()
{ {
writeMessageBlock.write(EndMessage()); m_writeMessageBlock.write(EndMessage());
} }
void PchManagerServerProxy::updatePchProjectParts(UpdatePchProjectPartsMessage &&message) void PchManagerServerProxy::updatePchProjectParts(UpdatePchProjectPartsMessage &&message)
{ {
writeMessageBlock.write(message); m_writeMessageBlock.write(message);
} }
void PchManagerServerProxy::removePchProjectParts(RemovePchProjectPartsMessage &&message) void PchManagerServerProxy::removePchProjectParts(RemovePchProjectPartsMessage &&message)
{ {
writeMessageBlock.write(message); m_writeMessageBlock.write(message);
}
void PchManagerServerProxy::readMessages()
{
for (const auto &message : readMessageBlock.readAll())
client->dispatch(message);
}
void PchManagerServerProxy::resetCounter()
{
writeMessageBlock.resetCounter();
readMessageBlock.resetCounter();
} }
} // namespace ClangBackEnd } // namespace ClangBackEnd

View File

@@ -25,6 +25,7 @@
#pragma once #pragma once
#include "baseserverproxy.h"
#include "clangsupport_global.h" #include "clangsupport_global.h"
#include "pchmanagerserverinterface.h" #include "pchmanagerserverinterface.h"
#include "readmessageblock.h" #include "readmessageblock.h"
@@ -42,25 +43,15 @@ namespace ClangBackEnd {
class PchManagerClientInterface; class PchManagerClientInterface;
class CLANGSUPPORT_EXPORT PchManagerServerProxy final : public PchManagerServerInterface class CLANGSUPPORT_EXPORT PchManagerServerProxy final : public BaseServerProxy,
public PchManagerServerInterface
{ {
public: public:
explicit PchManagerServerProxy(PchManagerClientInterface *client, QIODevice *ioDevice); explicit PchManagerServerProxy(PchManagerClientInterface *client, QIODevice *ioDevice);
PchManagerServerProxy(const PchManagerServerProxy&) = delete;
const PchManagerServerProxy &operator=(const PchManagerServerProxy&) = delete;
void end() override; void end() override;
void updatePchProjectParts(UpdatePchProjectPartsMessage &&message) override; void updatePchProjectParts(UpdatePchProjectPartsMessage &&message) override;
void removePchProjectParts(RemovePchProjectPartsMessage &&message) override; void removePchProjectParts(RemovePchProjectPartsMessage &&message) override;
void readMessages();
void resetCounter();
private:
ClangBackEnd::WriteMessageBlock writeMessageBlock;
ClangBackEnd::ReadMessageBlock readMessageBlock;
PchManagerClientInterface *client = nullptr;
}; };
} // namespace ClangBackEnd } // namespace ClangBackEnd

View File

@@ -0,0 +1,173 @@
/****************************************************************************
**
** 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 "processcreator.h"
#include "processexception.h"
#include "processstartedevent.h"
#include <QCoreApplication>
#include <QFileInfo>
#include <QTemporaryDir>
namespace ClangBackEnd {
using namespace std::chrono_literals;
ProcessCreator::ProcessCreator()
{
}
void ProcessCreator::setTemporaryDirectoryPattern(const QString &temporaryDirectoryPattern)
{
m_temporaryDirectoryPattern = temporaryDirectoryPattern;
resetTemporaryDirectory();
}
void ProcessCreator::setProcessPath(const QString &processPath)
{
m_processPath = processPath;
}
void ProcessCreator::setArguments(const QStringList &arguments)
{
m_arguments = arguments;
}
std::future<QProcessUniquePointer> ProcessCreator::createProcess() const
{
return std::async(std::launch::async, [&] {
checkIfProcessPathExists();
auto process = QProcessUniquePointer(new QProcess);
process->setProcessChannelMode(QProcess::QProcess::ForwardedChannels);
process->setProcessEnvironment(processEnvironment());
process->start(m_processPath, m_arguments);
process->waitForStarted(5000);
checkIfProcessWasStartingSuccessful(process.get());
postProcessStartedEvent();
process->moveToThread(QCoreApplication::instance()->thread());
return process;
});
}
void ProcessCreator::setObserver(QObject *observer)
{
this->m_observer = observer;
}
void ProcessCreator::checkIfProcessPathExists() const
{
if (!QFileInfo::exists(m_processPath)) {
const QString messageTemplate = QCoreApplication::translate("ProcessCreator",
"Executable does not exists: %1");
throwProcessException(messageTemplate.arg(m_processPath));
}
}
void ProcessCreator::checkIfProcessWasStartingSuccessful(QProcess *process) const
{
if (process->exitStatus() == QProcess::CrashExit || process->exitCode() != 0)
dispatchProcessError(process);
}
void ProcessCreator::dispatchProcessError(QProcess *process) const
{
switch (process->error()) {
case QProcess::UnknownError: {
const QString message = QCoreApplication::translate("ProcessCreator",
"Unknown error happend.");
throwProcessException(message);
};
case QProcess::Crashed: {
const QString message = QCoreApplication::translate("ProcessCreator",
"Process crashed.");
throwProcessException(message);
};
case QProcess::FailedToStart: {
const QString message = QCoreApplication::translate("ProcessCreator",
"Process failed at startup.");
throwProcessException(message);
};
case QProcess::Timedout: {
const QString message = QCoreApplication::translate("ProcessCreator",
"Process timeouted.");
throwProcessException(message);
};
case QProcess::WriteError: {
const QString message = QCoreApplication::translate("ProcessCreator",
"Cannot write to process.");
throwProcessException(message);
};
case QProcess::ReadError: {
const QString message = QCoreApplication::translate("ProcessCreator",
"Cannot read from process.");
throwProcessException(message);
};
}
throwProcessException("Internal impossible error!");
}
void ProcessCreator::postProcessStartedEvent() const
{
if (m_observer)
QCoreApplication::postEvent(m_observer, new ProcessStartedEvent);
}
void ProcessCreator::throwProcessException(const QString &message) const
{
postProcessStartedEvent();
throw ProcessException(message);
}
const QTemporaryDir &ProcessCreator::temporaryDirectory() const
{
return *m_temporaryDirectory.get();
}
void ProcessCreator::resetTemporaryDirectory()
{
m_temporaryDirectory = std::make_unique<Utils::TemporaryDirectory>(m_temporaryDirectoryPattern);
}
QProcessEnvironment ProcessCreator::processEnvironment() const
{
auto processEnvironment = QProcessEnvironment::systemEnvironment();
if (temporaryDirectory().isValid()) {
const QString temporaryDirectoryPath = temporaryDirectory().path();
processEnvironment.insert("TMPDIR", temporaryDirectoryPath);
processEnvironment.insert("TMP", temporaryDirectoryPath);
processEnvironment.insert("TEMP", temporaryDirectoryPath);
}
return processEnvironment;
}
} // namespace ClangBackEnd

View File

@@ -0,0 +1,78 @@
/****************************************************************************
**
** 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 <clangsupport_global.h>
#include "processhandle.h"
#include <utils/temporarydirectory.h>
#include <QStringList>
#include <future>
#include <memory>
QT_BEGIN_NAMESPACE
class QTemporaryDir;
class QProcessEnvironment;
QT_END_NAMESPACE
namespace ClangBackEnd {
class CLANGSUPPORT_EXPORT ProcessCreator
{
public:
ProcessCreator();
void setTemporaryDirectoryPattern(const QString &temporaryDirectoryPattern);
void setProcessPath(const QString &m_processPath);
void setArguments(const QStringList &m_arguments);
void setObserver(QObject *m_observer);
std::future<QProcessUniquePointer> createProcess() const;
const QTemporaryDir &temporaryDirectory() const;
void resetTemporaryDirectory();
private:
void checkIfProcessPathExists() const;
void checkIfProcessWasStartingSuccessful(QProcess *process) const;
[[noreturn]] void dispatchProcessError(QProcess *process) const;
void postProcessStartedEvent() const;
[[noreturn]] void throwProcessException(const QString &message) const;
QProcessEnvironment processEnvironment() const;
private:
std::unique_ptr<Utils::TemporaryDirectory> m_temporaryDirectory;
QString m_processPath;
QString m_temporaryDirectoryPattern;
QStringList m_arguments;
QObject *m_observer = nullptr;
};
} // namespace ClangBackEnd

View File

@@ -0,0 +1,40 @@
/****************************************************************************
**
** 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 "processexception.h"
namespace ClangBackEnd {
ProcessException::ProcessException(Utils::SmallString &&what)
: what_(std::move(what))
{
}
const char *ProcessException::what() const noexcept
{
return what_.data();
}
} // namespace ClangBackEnd

View File

@@ -0,0 +1,46 @@
/****************************************************************************
**
** 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/smallstring.h>
#include <exception>
namespace ClangBackEnd {
class ProcessException : public std::exception
{
public:
ProcessException() = default;
ProcessException(Utils::SmallString &&what);
const char *what() const noexcept final;
private:
Utils::SmallString what_;
};
} // namespace ClangBackEnd

View File

@@ -0,0 +1,46 @@
/****************************************************************************
**
** 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 <QProcess>
#include <memory>
namespace ClangBackEnd {
class QProcessUniquePointerDeleter
{
public:
void operator()(QProcess* process)
{
process->kill();
process->waitForFinished();
}
};
using QProcessUniquePointer = std::unique_ptr<QProcess, QProcessUniquePointerDeleter>;
} // namespace ClangBackEnd

View File

@@ -0,0 +1,34 @@
/****************************************************************************
**
** 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 "processstartedevent.h"
namespace ClangBackEnd {
ProcessStartedEvent::~ProcessStartedEvent()
{
}
} // namespace ClangBackEnd

View File

@@ -0,0 +1,44 @@
/****************************************************************************
**
** 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 <QEvent>
namespace ClangBackEnd {
class ProcessStartedEvent : public QEvent
{
public:
enum Type {
ProcessStarted = QEvent::User + 3456
};
ProcessStartedEvent()
: QEvent(static_cast<QEvent::Type>(ProcessStarted))
{}
~ProcessStartedEvent() override;
};
} // namespace ClangBackEnd

View File

@@ -77,14 +77,14 @@ MessageEnvelop ReadMessageBlock::read()
return message; return message;
} }
QVector<MessageEnvelop> ReadMessageBlock::readAll() std::vector<MessageEnvelop> ReadMessageBlock::readAll()
{ {
QVector<MessageEnvelop> messages; std::vector<MessageEnvelop> messages;
while (true) { while (true) {
const MessageEnvelop message = read(); MessageEnvelop message = read();
if (message.isValid()) if (message.isValid())
messages.append(message); messages.push_back(std::move(message));
else else
return messages; return messages;
} }
@@ -97,6 +97,11 @@ void ReadMessageBlock::resetCounter()
m_messageCounter = 0; m_messageCounter = 0;
} }
void ReadMessageBlock::setIoDevice(QIODevice *ioDevice)
{
m_ioDevice = ioDevice;
}
bool ReadMessageBlock::isTheWholeMessageReadable(QDataStream &in) bool ReadMessageBlock::isTheWholeMessageReadable(QDataStream &in)
{ {
if (m_ioDevice->bytesAvailable() < qint64(sizeof(m_blockSize))) if (m_ioDevice->bytesAvailable() < qint64(sizeof(m_blockSize)))

View File

@@ -27,6 +27,8 @@
#include <QtGlobal> #include <QtGlobal>
#include <vector>
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
class QDataStream; class QDataStream;
class QIODevice; class QIODevice;
@@ -42,10 +44,12 @@ public:
ReadMessageBlock(QIODevice *ioDevice = nullptr); ReadMessageBlock(QIODevice *ioDevice = nullptr);
MessageEnvelop read(); MessageEnvelop read();
QVector<MessageEnvelop> readAll(); std::vector<MessageEnvelop> readAll();
void resetCounter(); void resetCounter();
void setIoDevice(QIODevice *ioDevice);
private: private:
bool isTheWholeMessageReadable(QDataStream &in); bool isTheWholeMessageReadable(QDataStream &in);
bool checkIfMessageIsLost(QDataStream &in); bool checkIfMessageIsLost(QDataStream &in);

View File

@@ -35,58 +35,43 @@
namespace ClangBackEnd { namespace ClangBackEnd {
RefactoringServerProxy::RefactoringServerProxy(RefactoringClientInterface *client, QIODevice *ioDevice) RefactoringServerProxy::RefactoringServerProxy(RefactoringClientInterface *client, QIODevice *ioDevice)
: writeMessageBlock(ioDevice), : BaseServerProxy(client, ioDevice)
readMessageBlock(ioDevice),
client(client)
{ {
QObject::connect(ioDevice, &QIODevice::readyRead, [this] () { readMessages(); });
} }
void RefactoringServerProxy::end() void RefactoringServerProxy::end()
{ {
writeMessageBlock.write(EndMessage()); m_writeMessageBlock.write(EndMessage());
} }
void RefactoringServerProxy::requestSourceLocationsForRenamingMessage(RequestSourceLocationsForRenamingMessage &&message) void RefactoringServerProxy::requestSourceLocationsForRenamingMessage(RequestSourceLocationsForRenamingMessage &&message)
{ {
writeMessageBlock.write(message); m_writeMessageBlock.write(message);
} }
void RefactoringServerProxy::requestSourceRangesAndDiagnosticsForQueryMessage(RequestSourceRangesAndDiagnosticsForQueryMessage &&message) void RefactoringServerProxy::requestSourceRangesAndDiagnosticsForQueryMessage(RequestSourceRangesAndDiagnosticsForQueryMessage &&message)
{ {
writeMessageBlock.write(message); m_writeMessageBlock.write(message);
} }
void RefactoringServerProxy::requestSourceRangesForQueryMessage(RequestSourceRangesForQueryMessage &&message) void RefactoringServerProxy::requestSourceRangesForQueryMessage(RequestSourceRangesForQueryMessage &&message)
{ {
writeMessageBlock.write(message); m_writeMessageBlock.write(message);
} }
void RefactoringServerProxy::updatePchProjectParts(UpdatePchProjectPartsMessage &&message) void RefactoringServerProxy::updatePchProjectParts(UpdatePchProjectPartsMessage &&message)
{ {
writeMessageBlock.write(message); m_writeMessageBlock.write(message);
} }
void RefactoringServerProxy::removePchProjectParts(RemovePchProjectPartsMessage &&message) void RefactoringServerProxy::removePchProjectParts(RemovePchProjectPartsMessage &&message)
{ {
writeMessageBlock.write(message); m_writeMessageBlock.write(message);
} }
void RefactoringServerProxy::cancel() void RefactoringServerProxy::cancel()
{ {
writeMessageBlock.write(CancelMessage()); m_writeMessageBlock.write(CancelMessage());
}
void RefactoringServerProxy::readMessages()
{
for (const auto &message : readMessageBlock.readAll())
client->dispatch(message);
}
void RefactoringServerProxy::resetCounter()
{
writeMessageBlock.resetCounter();
readMessageBlock.resetCounter();
} }
} // namespace ClangBackEnd } // namespace ClangBackEnd

View File

@@ -25,6 +25,7 @@
#pragma once #pragma once
#include "baseserverproxy.h"
#include "clangsupport_global.h" #include "clangsupport_global.h"
#include "refactoringserverinterface.h" #include "refactoringserverinterface.h"
#include "readmessageblock.h" #include "readmessageblock.h"
@@ -42,12 +43,11 @@ namespace ClangBackEnd {
class RefactoringClientInterface; class RefactoringClientInterface;
class CLANGSUPPORT_EXPORT RefactoringServerProxy final : public RefactoringServerInterface class CLANGSUPPORT_EXPORT RefactoringServerProxy final : public BaseServerProxy,
public RefactoringServerInterface
{ {
public: public:
explicit RefactoringServerProxy(RefactoringClientInterface *client, QIODevice *ioDevice); explicit RefactoringServerProxy(RefactoringClientInterface *client, QIODevice *ioDevice);
RefactoringServerProxy(const RefactoringServerProxy&) = delete;
const RefactoringServerProxy &operator=(const RefactoringServerProxy&) = delete;
void end() override; void end() override;
void requestSourceLocationsForRenamingMessage(RequestSourceLocationsForRenamingMessage &&message) override; void requestSourceLocationsForRenamingMessage(RequestSourceLocationsForRenamingMessage &&message) override;
@@ -56,15 +56,6 @@ public:
void updatePchProjectParts(UpdatePchProjectPartsMessage &&message) override; void updatePchProjectParts(UpdatePchProjectPartsMessage &&message) override;
void removePchProjectParts(RemovePchProjectPartsMessage &&message) override; void removePchProjectParts(RemovePchProjectPartsMessage &&message) override;
void cancel() override; void cancel() override;
void readMessages();
void resetCounter();
private:
ClangBackEnd::WriteMessageBlock writeMessageBlock;
ClangBackEnd::ReadMessageBlock readMessageBlock;
RefactoringClientInterface *client = nullptr;
}; };
} // namespace ClangBackEnd } // namespace ClangBackEnd

View File

@@ -72,6 +72,10 @@ void WriteMessageBlock::resetCounter()
m_messageCounter = 0; m_messageCounter = 0;
} }
void WriteMessageBlock::setIoDevice(QIODevice *ioDevice)
{
m_ioDevice = ioDevice;
}
} // namespace ClangBackEnd } // namespace ClangBackEnd

View File

@@ -48,6 +48,8 @@ public:
void resetCounter(); void resetCounter();
void setIoDevice(QIODevice *ioDevice);
private: private:
qint64 m_messageCounter; qint64 m_messageCounter;
QIODevice *m_ioDevice; QIODevice *m_ioDevice;

View File

@@ -25,8 +25,9 @@
#include "pchmanagerconnectionclient.h" #include "pchmanagerconnectionclient.h"
#include <utils/temporarydirectory.h>
#include <QCoreApplication> #include <QCoreApplication>
#include <QTemporaryDir>
namespace ClangPchManager { namespace ClangPchManager {
@@ -41,30 +42,35 @@ QString currentProcessId()
ClangPchManager::PchManagerConnectionClient::PchManagerConnectionClient( ClangPchManager::PchManagerConnectionClient::PchManagerConnectionClient(
ClangBackEnd::PchManagerClientInterface *client) ClangBackEnd::PchManagerClientInterface *client)
: serverProxy_(client, ioDevice()) : ConnectionClient(Utils::TemporaryDirectory::masterDirectoryPath()
+ QStringLiteral("/ClangPchManagerBackEnd-")
+ currentProcessId()),
m_serverProxy(client, ioDevice())
{ {
m_processCreator.setTemporaryDirectoryPattern("clangpchmanagerbackend-XXXXXX");
stdErrPrefixer().setPrefix("PchManagerConnectionClient.stderr: "); stdErrPrefixer().setPrefix("PchManagerConnectionClient.stderr: ");
stdOutPrefixer().setPrefix("PchManagerConnectionClient.stdout: "); stdOutPrefixer().setPrefix("PchManagerConnectionClient.stdout: ");
} }
PchManagerConnectionClient::~PchManagerConnectionClient()
{
finishProcess();
}
ClangBackEnd::PchManagerServerProxy &ClangPchManager::PchManagerConnectionClient::serverProxy() ClangBackEnd::PchManagerServerProxy &ClangPchManager::PchManagerConnectionClient::serverProxy()
{ {
return serverProxy_; return m_serverProxy;
} }
void ClangPchManager::PchManagerConnectionClient::sendEndCommand() void ClangPchManager::PchManagerConnectionClient::sendEndCommand()
{ {
serverProxy_.end(); m_serverProxy.end();
} }
void PchManagerConnectionClient::resetCounter() void PchManagerConnectionClient::resetCounter()
{ {
serverProxy_.resetCounter(); m_serverProxy.resetCounter();
}
QString ClangPchManager::PchManagerConnectionClient::connectionName() const
{
return temporaryDirectory().path() + QStringLiteral("/ClangPchManagerBackEnd-") + currentProcessId();
} }
QString PchManagerConnectionClient::outputName() const QString PchManagerConnectionClient::outputName() const
@@ -72,4 +78,9 @@ QString PchManagerConnectionClient::outputName() const
return QStringLiteral("PchManagerConnectionClient"); return QStringLiteral("PchManagerConnectionClient");
} }
void PchManagerConnectionClient::newConnectedServer(QIODevice *ioDevice)
{
m_serverProxy.setIoDevice(ioDevice);
}
} // namespace ClangPchManager } // namespace ClangPchManager

View File

@@ -34,17 +34,18 @@ class PchManagerConnectionClient : public ClangBackEnd::ConnectionClient
{ {
public: public:
PchManagerConnectionClient(ClangBackEnd::PchManagerClientInterface *client); PchManagerConnectionClient(ClangBackEnd::PchManagerClientInterface *client);
~PchManagerConnectionClient();
ClangBackEnd::PchManagerServerProxy &serverProxy(); ClangBackEnd::PchManagerServerProxy &serverProxy();
protected: protected:
void sendEndCommand() override; void sendEndCommand() override;
void resetCounter() override; void resetCounter() override;
QString connectionName() const override;
QString outputName() const override; QString outputName() const override;
void newConnectedServer(QIODevice *ioDevice) override;
private: private:
ClangBackEnd::PchManagerServerProxy serverProxy_; ClangBackEnd::PchManagerServerProxy m_serverProxy;
}; };
} // namespace ClangPchManager } // namespace ClangPchManager

View File

@@ -25,6 +25,8 @@
#include "refactoringconnectionclient.h" #include "refactoringconnectionclient.h"
#include <utils/temporarydirectory.h>
#include <QCoreApplication> #include <QCoreApplication>
namespace ClangBackEnd { namespace ClangBackEnd {
@@ -39,8 +41,13 @@ QString currentProcessId()
} }
RefactoringConnectionClient::RefactoringConnectionClient(RefactoringClientInterface *client) RefactoringConnectionClient::RefactoringConnectionClient(RefactoringClientInterface *client)
: serverProxy_(client, ioDevice()) : ConnectionClient(Utils::TemporaryDirectory::masterDirectoryPath()
+ QStringLiteral("/ClangRefactoringBackEnd-")
+ currentProcessId()),
m_serverProxy(client, nullptr)
{ {
m_processCreator.setTemporaryDirectoryPattern("clangrefactoringbackend-XXXXXX");
stdErrPrefixer().setPrefix("RefactoringConnectionClient.stderr: "); stdErrPrefixer().setPrefix("RefactoringConnectionClient.stderr: ");
stdOutPrefixer().setPrefix("RefactoringConnectionClient.stdout: "); stdOutPrefixer().setPrefix("RefactoringConnectionClient.stdout: ");
} }
@@ -52,22 +59,17 @@ RefactoringConnectionClient::~RefactoringConnectionClient()
RefactoringServerProxy &RefactoringConnectionClient::serverProxy() RefactoringServerProxy &RefactoringConnectionClient::serverProxy()
{ {
return serverProxy_; return m_serverProxy;
} }
void RefactoringConnectionClient::sendEndCommand() void RefactoringConnectionClient::sendEndCommand()
{ {
serverProxy_.end(); m_serverProxy.end();
} }
void RefactoringConnectionClient::resetCounter() void RefactoringConnectionClient::resetCounter()
{ {
serverProxy_.resetCounter(); m_serverProxy.resetCounter();
}
QString RefactoringConnectionClient::connectionName() const
{
return temporaryDirectory().path() + QStringLiteral("/ClangRefactoringBackEnd-") + currentProcessId();
} }
QString RefactoringConnectionClient::outputName() const QString RefactoringConnectionClient::outputName() const
@@ -75,4 +77,9 @@ QString RefactoringConnectionClient::outputName() const
return QStringLiteral("RefactoringConnectionClient"); return QStringLiteral("RefactoringConnectionClient");
} }
void RefactoringConnectionClient::newConnectedServer(QIODevice *ioDevice)
{
m_serverProxy.setIoDevice(ioDevice);
}
} // namespace ClangBackEnd } // namespace ClangBackEnd

View File

@@ -43,11 +43,11 @@ public:
protected: protected:
void sendEndCommand() override; void sendEndCommand() override;
void resetCounter() override; void resetCounter() override;
QString connectionName() const override;
QString outputName() const override; QString outputName() const override;
void newConnectedServer(QIODevice *ioDevice) override;
private: private:
RefactoringServerProxy serverProxy_; RefactoringServerProxy m_serverProxy;
}; };
} // namespace ClangBackEnd } // namespace ClangBackEnd

View File

@@ -41,8 +41,8 @@ namespace ClangRefactoring {
class RefactoringEngine : public CppTools::RefactoringEngineInterface class RefactoringEngine : public CppTools::RefactoringEngineInterface
{ {
public: public:
RefactoringEngine(ClangBackEnd::RefactoringServerInterface &m_server, RefactoringEngine(ClangBackEnd::RefactoringServerInterface &server,
ClangBackEnd::RefactoringClientInterface &m_client, ClangBackEnd::RefactoringClientInterface &client,
ClangBackEnd::FilePathCachingInterface &filePathCache, ClangBackEnd::FilePathCachingInterface &filePathCache,
SymbolQueryInterface &symbolQuery); SymbolQueryInterface &symbolQuery);
~RefactoringEngine() override; ~RefactoringEngine() override;

View File

@@ -72,9 +72,9 @@ int main(int argc, char *argv[])
clang_enableStackTraces(); clang_enableStackTraces();
ClangCodeModelServer clangCodeModelServer; ClangCodeModelServer clangCodeModelServer;
ConnectionServer<ClangCodeModelServer, ClangCodeModelClientProxy> connectionServer(connection); ConnectionServer<ClangCodeModelServer, ClangCodeModelClientProxy> connectionServer;
connectionServer.start();
connectionServer.setServer(&clangCodeModelServer); connectionServer.setServer(&clangCodeModelServer);
connectionServer.start(connection);
return application.exec(); return application.exec();
} }

View File

@@ -103,7 +103,7 @@ int main(int argc, char *argv[])
QCoreApplication application(argc, argv); QCoreApplication application(argc, argv);
const QString connection = processArguments(application); const QString connectionName = processArguments(application);
Sqlite::Database database{Utils::PathString{QDir::tempPath() + "/symbol.db"}}; Sqlite::Database database{Utils::PathString{QDir::tempPath() + "/symbol.db"}};
ClangBackEnd::RefactoringDatabaseInitializer<Sqlite::Database> databaseInitializer{database}; ClangBackEnd::RefactoringDatabaseInitializer<Sqlite::Database> databaseInitializer{database};
@@ -120,9 +120,9 @@ int main(int argc, char *argv[])
includeWatcher.setNotifier(&clangPchManagerServer); includeWatcher.setNotifier(&clangPchManagerServer);
pchGenerator.setNotifier(&clangPchManagerServer); pchGenerator.setNotifier(&clangPchManagerServer);
ConnectionServer<PchManagerServer, PchManagerClientProxy> connectionServer(connection); ConnectionServer<PchManagerServer, PchManagerClientProxy> connectionServer;
connectionServer.start();
connectionServer.setServer(&clangPchManagerServer); connectionServer.setServer(&clangPchManagerServer);
connectionServer.start(connectionName);
return application.exec(); return application.exec();
} }

View File

@@ -76,9 +76,9 @@ try {
FilePathCaching filePathCache{database}; FilePathCaching filePathCache{database};
SymbolIndexing symbolIndexing{database, filePathCache}; SymbolIndexing symbolIndexing{database, filePathCache};
RefactoringServer clangCodeModelServer{symbolIndexing, filePathCache}; RefactoringServer clangCodeModelServer{symbolIndexing, filePathCache};
ConnectionServer<RefactoringServer, RefactoringClientProxy> connectionServer(connection); ConnectionServer<RefactoringServer, RefactoringClientProxy> connectionServer;
connectionServer.start();
connectionServer.setServer(&clangCodeModelServer); connectionServer.setServer(&clangCodeModelServer);
connectionServer.start(connection);
return application.exec(); return application.exec();

View File

@@ -31,7 +31,6 @@
#include <QCoreApplication> #include <QCoreApplication>
#include <QDebug> #include <QDebug>
namespace ClangBackEnd { namespace ClangBackEnd {
void EchoClangCodeModelServer::dispatch(const MessageEnvelop &message) void EchoClangCodeModelServer::dispatch(const MessageEnvelop &message)
@@ -41,7 +40,6 @@ void EchoClangCodeModelServer::dispatch(const MessageEnvelop &message)
void EchoClangCodeModelServer::end() void EchoClangCodeModelServer::end()
{ {
ConnectionServer<EchoClangCodeModelServer, ClangCodeModelClientProxy>::removeServer();
QCoreApplication::quit(); QCoreApplication::quit();
} }

View File

@@ -43,15 +43,18 @@ int main(int argc, char *argv[])
QCoreApplication application(argc, argv); QCoreApplication application(argc, argv);
if (application.arguments().count() != 2) {
qWarning() << "wrong argument count"; if (application.arguments().count() < 2)
return 1; return 1;
} else if (application.arguments().count() == 3)
*(int*)0 = 0;
else if (application.arguments().contains("connectionName"))
return 0;
EchoClangCodeModelServer echoClangCodeModelServer; EchoClangCodeModelServer echoClangCodeModelServer;
ConnectionServer<EchoClangCodeModelServer, ClangCodeModelClientProxy> connectionServer(application.arguments()[1]); ConnectionServer<EchoClangCodeModelServer, ClangCodeModelClientProxy> connectionServer;
connectionServer.start();
connectionServer.setServer(&echoClangCodeModelServer); connectionServer.setServer(&echoClangCodeModelServer);
connectionServer.start(application.arguments()[1]);
return application.exec(); return application.exec();
} }

View File

@@ -51,7 +51,7 @@ using ::testing::SizeIs;
struct Data { struct Data {
Data() : client(&mockClangCodeModelClient) {} Data() : client(&mockClangCodeModelClient) {}
MockClangCodeModelClient mockClangCodeModelClient; NiceMock<MockClangCodeModelClient> mockClangCodeModelClient;
ClangBackEnd::ClangCodeModelConnectionClient client; ClangBackEnd::ClangCodeModelConnectionClient client;
}; };
@@ -80,7 +80,7 @@ TEST_F(ClientServerOutsideProcessSlowTest, RestartProcessAsynchronously)
client.restartProcessAsynchronously(); client.restartProcessAsynchronously();
ASSERT_TRUE(clientSpy.wait(100000)); ASSERT_TRUE(clientSpy.wait(100000));
ASSERT_TRUE(client.isProcessIsRunning()); ASSERT_TRUE(client.isProcessRunning());
ASSERT_TRUE(client.isConnected()); ASSERT_TRUE(client.isConnected());
} }
@@ -200,6 +200,6 @@ void ClientServerOutsideProcess::TearDown()
client.setProcessAliveTimerInterval(1000000); client.setProcessAliveTimerInterval(1000000);
client.waitForConnected(); client.waitForConnected();
ASSERT_TRUE(client.isProcessIsRunning()); ASSERT_TRUE(client.isProcessRunning());
ASSERT_TRUE(client.isConnected()); ASSERT_TRUE(client.isConnected());
} }

View File

@@ -0,0 +1,62 @@
/****************************************************************************
**
** 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 "eventspy.h"
#include <QCoreApplication>
#include <QEvent>
using namespace std::literals::chrono_literals;
EventSpy::EventSpy(uint eventType)
: startTime(std::chrono::steady_clock::now()),
eventType(eventType)
{
}
bool EventSpy::waitForEvent()
{
while (shouldRun())
QCoreApplication::processEvents();
return eventHappened;
}
bool EventSpy::event(QEvent *event)
{
if (event->type() == eventType) {
eventHappened = true;
return true;
}
return false;
}
bool EventSpy::shouldRun() const
{
return !eventHappened
&& (std::chrono::steady_clock::now() - startTime) < 1s;
}

View File

@@ -0,0 +1,51 @@
/****************************************************************************
**
** 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 <QObject>
#include <chrono>
class EventSpy : public QObject
{
Q_OBJECT
public:
EventSpy(uint eventType);
bool waitForEvent();
protected:
bool event(QEvent *event) override;
private:
bool shouldRun() const;
private:
std::chrono::steady_clock::time_point startTime;
uint eventType;
bool eventHappened = false;
};

View File

@@ -33,7 +33,6 @@ using testing::AnyNumber;
using testing::AnyOf; using testing::AnyOf;
using testing::Contains; using testing::Contains;
using testing::ElementsAre; using testing::ElementsAre;
using testing::Eq;
using testing::Field; using testing::Field;
using testing::HasSubstr; using testing::HasSubstr;
using testing::InSequence; using testing::InSequence;
@@ -51,3 +50,10 @@ using testing::SizeIs;
using testing::StrEq; using testing::StrEq;
using testing::Throw; using testing::Throw;
using testing::UnorderedElementsAre; using testing::UnorderedElementsAre;
using testing::Eq;
using testing::Ge;
using testing::Gt;
using testing::Le;
using testing::Lt;
using testing::Ne;

View File

@@ -0,0 +1,121 @@
/****************************************************************************
**
** 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 "googletest.h"
#include "eventspy.h"
#include <processcreator.h>
#include <processexception.h>
#include <processstartedevent.h>
#include <utils/hostosinfo.h>
#include <QProcess>
#include <future>
using testing::NotNull;
using ClangBackEnd::ProcessCreator;
using ClangBackEnd::ProcessException;
using ClangBackEnd::ProcessStartedEvent;
namespace {
class ProcessCreator : public testing::Test
{
protected:
void SetUp();
protected:
::ProcessCreator processCreator;
QStringList m_arguments = {QStringLiteral("connectionName")};
};
TEST_F(ProcessCreator, ProcessIsNotNull)
{
auto future = processCreator.createProcess();
auto process = future.get();
ASSERT_THAT(process.get(), NotNull());
}
TEST_F(ProcessCreator, ProcessIsRunning)
{
auto future = processCreator.createProcess();
auto process = future.get();
ASSERT_THAT(process->state(), QProcess::Running);
}
TEST_F(ProcessCreator, ProcessPathIsNotExisting)
{
processCreator.setProcessPath(Utils::HostOsInfo::withExecutableSuffix(ECHOSERVER"fail"));
auto future = processCreator.createProcess();
ASSERT_THROW(future.get(), ProcessException);
}
TEST_F(ProcessCreator, ProcessStartIsSucessfull)
{
auto future = processCreator.createProcess();
ASSERT_NO_THROW(future.get());
}
TEST_F(ProcessCreator, ProcessObserverGetsEvent)
{
EventSpy eventSpy(ProcessStartedEvent::ProcessStarted);
processCreator.setObserver(&eventSpy);
auto future = processCreator.createProcess();
eventSpy.waitForEvent();
}
TEST_F(ProcessCreator, TemporayPathIsSetForDefaultInitialization)
{
QString path = processCreator.temporaryDirectory().path();
ASSERT_THAT(path.size(), Gt(0));
}
TEST_F(ProcessCreator, TemporayPathIsResetted)
{
std::string oldPath = processCreator.temporaryDirectory().path().toStdString();
processCreator.resetTemporaryDirectory();
ASSERT_THAT(processCreator.temporaryDirectory().path().toStdString(),
AllOf(Not(IsEmpty()), Ne(oldPath)));
}
void ProcessCreator::SetUp()
{
processCreator.setTemporaryDirectoryPattern("process-XXXXXXX");
processCreator.resetTemporaryDirectory();
processCreator.setProcessPath(Utils::HostOsInfo::withExecutableSuffix(ECHOSERVER));
processCreator.setArguments(m_arguments);
}
}

View File

@@ -119,7 +119,7 @@ TEST_F(ReadAndWriteMessageBlock, ReadThreeMessagesAndTestCount)
writeMessageBlock.write(ClangBackEnd::EndMessage()); writeMessageBlock.write(ClangBackEnd::EndMessage());
buffer.seek(0); buffer.seek(0);
ASSERT_EQ(3, readMessageBlock.readAll().count()); ASSERT_THAT(readMessageBlock.readAll(), SizeIs(3));
} }
TEST_F(ReadAndWriteMessageBlock, CompareEndMessage) TEST_F(ReadAndWriteMessageBlock, CompareEndMessage)

View File

@@ -41,6 +41,7 @@
#include <utils/smallstringvector.h> #include <utils/smallstringvector.h>
#include <QBuffer>
#include <QTextCursor> #include <QTextCursor>
#include <QTextDocument> #include <QTextDocument>
@@ -74,9 +75,10 @@ protected:
NiceMock<MockSearchHandle> mockSearchHandle; NiceMock<MockSearchHandle> mockSearchHandle;
NiceMock<MockSymbolQuery> mockSymbolQuery; NiceMock<MockSymbolQuery> mockSymbolQuery;
MockRefactoringClientCallBack callbackMock; MockRefactoringClientCallBack callbackMock;
QBuffer ioDevice;
ClangRefactoring::RefactoringClient client; ClangRefactoring::RefactoringClient client;
ClangBackEnd::RefactoringConnectionClient connectionClient{&client}; ClangBackEnd::RefactoringServerProxy serverProxy{&client, &ioDevice};
RefactoringEngine engine{connectionClient.serverProxy(), client, mockFilePathCaching, mockSymbolQuery}; RefactoringEngine engine{serverProxy, client, mockFilePathCaching, mockSymbolQuery};
QString fileContent{QStringLiteral("int x;\nint y;")}; QString fileContent{QStringLiteral("int x;\nint y;")};
QTextDocument textDocument{fileContent}; QTextDocument textDocument{fileContent};
QTextCursor cursor{&textDocument}; QTextCursor cursor{&textDocument};

View File

@@ -70,6 +70,7 @@ SOURCES += \
spydummy.cpp \ spydummy.cpp \
symbolindexer-test.cpp \ symbolindexer-test.cpp \
stringcache-test.cpp \ stringcache-test.cpp \
eventspy.cpp \
unittests-main.cpp \ unittests-main.cpp \
utf8-test.cpp \ utf8-test.cpp \
symbolstorage-test.cpp \ symbolstorage-test.cpp \
@@ -82,6 +83,7 @@ SOURCES += \
filepathcache-test.cpp \ filepathcache-test.cpp \
filepathstorage-test.cpp \ filepathstorage-test.cpp \
filepathstoragesqlitestatementfactory-test.cpp \ filepathstoragesqlitestatementfactory-test.cpp \
processcreator-test.cpp \
nativefilepath-test.cpp \ nativefilepath-test.cpp \
nativefilepathview-test.cpp nativefilepathview-test.cpp
@@ -176,6 +178,7 @@ HEADERS += \
conditionally-disabled-tests.h \ conditionally-disabled-tests.h \
dummyclangipcclient.h \ dummyclangipcclient.h \
dynamicastmatcherdiagnosticcontainer-matcher.h \ dynamicastmatcherdiagnosticcontainer-matcher.h \
eventspy.h \
fakeprocess.h \ fakeprocess.h \
faketimer.h \ faketimer.h \
filesystem-utilities.h \ filesystem-utilities.h \