forked from qt-creator/qt-creator
Initial import of processlauncher copy from qbs project
Change-Id: I9d646dd1a820e4e69c808998d9f12ff2f5dabe6d Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org> Reviewed-by: hjk <hjk@qt.io>
This commit is contained in:
@@ -76,6 +76,9 @@ add_qtc_library(Utils
|
||||
itemviews.cpp itemviews.h
|
||||
json.cpp json.h
|
||||
jsontreeitem.cpp jsontreeitem.h
|
||||
launcherinterface.cpp launcherinterface.h
|
||||
launcherpackets.cpp launcherpackets.h
|
||||
launchersocket.cpp launchersocket.h
|
||||
layoutbuilder.cpp layoutbuilder.h
|
||||
linecolumn.cpp linecolumn.h
|
||||
link.cpp link.h
|
||||
|
169
src/libs/utils/launcherinterface.cpp
Normal file
169
src/libs/utils/launcherinterface.cpp
Normal file
@@ -0,0 +1,169 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2021 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 "launcherinterface.h"
|
||||
|
||||
#include "launcherpackets.h"
|
||||
#include "launchersocket.h"
|
||||
|
||||
#include <QtCore/qcoreapplication.h>
|
||||
#include <QtCore/qdebug.h>
|
||||
#include <QtCore/qdir.h>
|
||||
#include <QtCore/qprocess.h>
|
||||
#include <QtNetwork/qlocalserver.h>
|
||||
|
||||
#ifdef Q_OS_UNIX
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
namespace Utils {
|
||||
|
||||
namespace Internal {
|
||||
|
||||
class LauncherProcess : public QProcess
|
||||
{
|
||||
public:
|
||||
LauncherProcess(QObject *parent) : QProcess(parent)
|
||||
{
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) && defined(Q_OS_UNIX)
|
||||
setChildProcessModifier([this] { setupChildProcess_impl(); });
|
||||
#endif
|
||||
}
|
||||
|
||||
private:
|
||||
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
|
||||
void setupChildProcess() override
|
||||
{
|
||||
setupChildProcess_impl();
|
||||
}
|
||||
#endif
|
||||
|
||||
void setupChildProcess_impl()
|
||||
{
|
||||
#ifdef Q_OS_UNIX
|
||||
const auto pid = static_cast<pid_t>(processId());
|
||||
setpgid(pid, pid);
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
|
||||
using namespace Utils::Internal;
|
||||
|
||||
static QString launcherSocketName()
|
||||
{
|
||||
return QStringLiteral("qtcreator_processlauncher-%1")
|
||||
.arg(QString::number(qApp->applicationPid()));
|
||||
}
|
||||
|
||||
LauncherInterface::LauncherInterface()
|
||||
: m_server(new QLocalServer(this)), m_socket(new LauncherSocket(this))
|
||||
{
|
||||
QObject::connect(m_server, &QLocalServer::newConnection,
|
||||
this, &LauncherInterface::handleNewConnection);
|
||||
}
|
||||
|
||||
LauncherInterface &LauncherInterface::instance()
|
||||
{
|
||||
static LauncherInterface p;
|
||||
return p;
|
||||
}
|
||||
|
||||
LauncherInterface::~LauncherInterface()
|
||||
{
|
||||
m_server->disconnect();
|
||||
}
|
||||
|
||||
void LauncherInterface::doStart()
|
||||
{
|
||||
if (++m_startRequests > 1)
|
||||
return;
|
||||
const QString &socketName = launcherSocketName();
|
||||
QLocalServer::removeServer(socketName);
|
||||
if (!m_server->listen(socketName)) {
|
||||
emit errorOccurred(m_server->errorString());
|
||||
return;
|
||||
}
|
||||
m_process = new LauncherProcess(this);
|
||||
connect(m_process, &QProcess::errorOccurred, this, &LauncherInterface::handleProcessError);
|
||||
connect(m_process,
|
||||
static_cast<void (QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished),
|
||||
this, &LauncherInterface::handleProcessFinished);
|
||||
connect(m_process, &QProcess::readyReadStandardError,
|
||||
this, &LauncherInterface::handleProcessStderr);
|
||||
m_process->start(qApp->applicationDirPath() + QLatin1Char('/')
|
||||
+ QLatin1String(RELATIVE_LIBEXEC_PATH)
|
||||
+ QLatin1String("/qtcreator_processlauncher"),
|
||||
QStringList(m_server->fullServerName()));
|
||||
}
|
||||
|
||||
void LauncherInterface::doStop()
|
||||
{
|
||||
if (--m_startRequests > 0)
|
||||
return;
|
||||
m_server->close();
|
||||
if (!m_process)
|
||||
return;
|
||||
m_process->disconnect();
|
||||
m_socket->shutdown();
|
||||
m_process->waitForFinished(3000);
|
||||
m_process->deleteLater();
|
||||
m_process = nullptr;
|
||||
}
|
||||
|
||||
void LauncherInterface::handleNewConnection()
|
||||
{
|
||||
QLocalSocket * const socket = m_server->nextPendingConnection();
|
||||
if (!socket)
|
||||
return;
|
||||
m_server->close();
|
||||
m_socket->setSocket(socket);
|
||||
}
|
||||
|
||||
void LauncherInterface::handleProcessError()
|
||||
{
|
||||
if (m_process->error() == QProcess::FailedToStart) {
|
||||
const QString launcherPathForUser
|
||||
= QDir::toNativeSeparators(QDir::cleanPath(m_process->program()));
|
||||
emit errorOccurred(QCoreApplication::translate("Utils::LauncherSocket",
|
||||
"Failed to start process launcher at '%1': %2")
|
||||
.arg(launcherPathForUser, m_process->errorString()));
|
||||
}
|
||||
}
|
||||
|
||||
void LauncherInterface::handleProcessFinished()
|
||||
{
|
||||
emit errorOccurred(QCoreApplication::translate("Utils::LauncherSocket",
|
||||
"Process launcher closed unexpectedly: %1")
|
||||
.arg(m_process->errorString()));
|
||||
}
|
||||
|
||||
void LauncherInterface::handleProcessStderr()
|
||||
{
|
||||
qDebug() << "[launcher]" << m_process->readAllStandardError();
|
||||
}
|
||||
|
||||
} // namespace Utils
|
72
src/libs/utils/launcherinterface.h
Normal file
72
src/libs/utils/launcherinterface.h
Normal file
@@ -0,0 +1,72 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2021 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_global.h"
|
||||
|
||||
#include <QtCore/qobject.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QLocalServer;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
namespace Utils {
|
||||
namespace Internal {
|
||||
class LauncherProcess;
|
||||
class LauncherSocket;
|
||||
}
|
||||
|
||||
class QTCREATOR_UTILS_EXPORT LauncherInterface : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
static LauncherInterface &instance();
|
||||
~LauncherInterface() override;
|
||||
|
||||
static void startLauncher() { instance().doStart(); }
|
||||
static void stopLauncher() { instance().doStop(); }
|
||||
static Internal::LauncherSocket *socket() { return instance().m_socket; }
|
||||
|
||||
signals:
|
||||
void errorOccurred(const QString &error);
|
||||
|
||||
private:
|
||||
LauncherInterface();
|
||||
|
||||
void doStart();
|
||||
void doStop();
|
||||
void handleNewConnection();
|
||||
void handleProcessError();
|
||||
void handleProcessFinished();
|
||||
void handleProcessStderr();
|
||||
|
||||
QLocalServer * const m_server;
|
||||
Internal::LauncherSocket *const m_socket;
|
||||
Internal::LauncherProcess *m_process = nullptr;
|
||||
int m_startRequests = 0;
|
||||
};
|
||||
|
||||
} // namespace Utils
|
160
src/libs/utils/launcherpackets.cpp
Normal file
160
src/libs/utils/launcherpackets.cpp
Normal file
@@ -0,0 +1,160 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2021 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 "launcherpackets.h"
|
||||
|
||||
#include <QtCore/qbytearray.h>
|
||||
#include <QtCore/qcoreapplication.h>
|
||||
|
||||
namespace Utils {
|
||||
namespace Internal {
|
||||
|
||||
LauncherPacket::~LauncherPacket() = default;
|
||||
|
||||
QByteArray LauncherPacket::serialize() const
|
||||
{
|
||||
QByteArray data;
|
||||
QDataStream stream(&data, QIODevice::WriteOnly);
|
||||
stream << static_cast<int>(0) << static_cast<quint8>(type) << token;
|
||||
doSerialize(stream);
|
||||
stream.device()->reset();
|
||||
stream << static_cast<int>(data.size() - sizeof(int));
|
||||
return data;
|
||||
}
|
||||
|
||||
void LauncherPacket::deserialize(const QByteArray &data)
|
||||
{
|
||||
QDataStream stream(data);
|
||||
doDeserialize(stream);
|
||||
}
|
||||
|
||||
|
||||
StartProcessPacket::StartProcessPacket(quintptr token)
|
||||
: LauncherPacket(LauncherPacketType::StartProcess, token)
|
||||
{
|
||||
}
|
||||
|
||||
void StartProcessPacket::doSerialize(QDataStream &stream) const
|
||||
{
|
||||
stream << command << arguments << workingDir << env;
|
||||
}
|
||||
|
||||
void StartProcessPacket::doDeserialize(QDataStream &stream)
|
||||
{
|
||||
stream >> command >> arguments >> workingDir >> env;
|
||||
}
|
||||
|
||||
|
||||
StopProcessPacket::StopProcessPacket(quintptr token)
|
||||
: LauncherPacket(LauncherPacketType::StopProcess, token)
|
||||
{
|
||||
}
|
||||
|
||||
void StopProcessPacket::doSerialize(QDataStream &stream) const
|
||||
{
|
||||
Q_UNUSED(stream);
|
||||
}
|
||||
|
||||
void StopProcessPacket::doDeserialize(QDataStream &stream)
|
||||
{
|
||||
Q_UNUSED(stream);
|
||||
}
|
||||
|
||||
|
||||
ProcessErrorPacket::ProcessErrorPacket(quintptr token)
|
||||
: LauncherPacket(LauncherPacketType::ProcessError, token)
|
||||
{
|
||||
}
|
||||
|
||||
void ProcessErrorPacket::doSerialize(QDataStream &stream) const
|
||||
{
|
||||
stream << static_cast<quint8>(error) << errorString;
|
||||
}
|
||||
|
||||
void ProcessErrorPacket::doDeserialize(QDataStream &stream)
|
||||
{
|
||||
quint8 e;
|
||||
stream >> e;
|
||||
error = static_cast<QProcess::ProcessError>(e);
|
||||
stream >> errorString;
|
||||
}
|
||||
|
||||
|
||||
ProcessFinishedPacket::ProcessFinishedPacket(quintptr token)
|
||||
: LauncherPacket(LauncherPacketType::ProcessFinished, token)
|
||||
{
|
||||
}
|
||||
|
||||
void ProcessFinishedPacket::doSerialize(QDataStream &stream) const
|
||||
{
|
||||
stream << errorString << stdOut << stdErr
|
||||
<< static_cast<quint8>(exitStatus) << static_cast<quint8>(error)
|
||||
<< exitCode;
|
||||
}
|
||||
|
||||
void ProcessFinishedPacket::doDeserialize(QDataStream &stream)
|
||||
{
|
||||
stream >> errorString >> stdOut >> stdErr;
|
||||
quint8 val;
|
||||
stream >> val;
|
||||
exitStatus = static_cast<QProcess::ExitStatus>(val);
|
||||
stream >> val;
|
||||
error = static_cast<QProcess::ProcessError>(val);
|
||||
stream >> exitCode;
|
||||
}
|
||||
|
||||
ShutdownPacket::ShutdownPacket() : LauncherPacket(LauncherPacketType::Shutdown, 0) { }
|
||||
void ShutdownPacket::doSerialize(QDataStream &stream) const { Q_UNUSED(stream); }
|
||||
void ShutdownPacket::doDeserialize(QDataStream &stream) { Q_UNUSED(stream); }
|
||||
|
||||
void PacketParser::setDevice(QIODevice *device)
|
||||
{
|
||||
m_stream.setDevice(device);
|
||||
m_sizeOfNextPacket = -1;
|
||||
}
|
||||
|
||||
bool PacketParser::parse()
|
||||
{
|
||||
static const int commonPayloadSize = static_cast<int>(1 + sizeof(quintptr));
|
||||
if (m_sizeOfNextPacket == -1) {
|
||||
if (m_stream.device()->bytesAvailable() < static_cast<int>(sizeof m_sizeOfNextPacket))
|
||||
return false;
|
||||
m_stream >> m_sizeOfNextPacket;
|
||||
if (m_sizeOfNextPacket < commonPayloadSize)
|
||||
throw InvalidPacketSizeException(m_sizeOfNextPacket);
|
||||
}
|
||||
if (m_stream.device()->bytesAvailable() < m_sizeOfNextPacket)
|
||||
return false;
|
||||
quint8 type;
|
||||
m_stream >> type;
|
||||
m_type = static_cast<LauncherPacketType>(type);
|
||||
m_stream >> m_token;
|
||||
m_packetData = m_stream.device()->read(m_sizeOfNextPacket - commonPayloadSize);
|
||||
m_sizeOfNextPacket = -1;
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Utils
|
161
src/libs/utils/launcherpackets.h
Normal file
161
src/libs/utils/launcherpackets.h
Normal file
@@ -0,0 +1,161 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2021 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 <QtCore/qdatastream.h>
|
||||
#include <QtCore/qprocess.h>
|
||||
#include <QtCore/qstringlist.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QByteArray;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
namespace Utils {
|
||||
namespace Internal {
|
||||
|
||||
enum class LauncherPacketType {
|
||||
Shutdown, StartProcess, StopProcess, ProcessError, ProcessFinished
|
||||
};
|
||||
|
||||
class PacketParser
|
||||
{
|
||||
public:
|
||||
class InvalidPacketSizeException
|
||||
{
|
||||
public:
|
||||
InvalidPacketSizeException(int size) : size(size) { }
|
||||
const int size;
|
||||
};
|
||||
|
||||
void setDevice(QIODevice *device);
|
||||
bool parse();
|
||||
LauncherPacketType type() const { return m_type; }
|
||||
quintptr token() const { return m_token; }
|
||||
const QByteArray &packetData() const { return m_packetData; }
|
||||
|
||||
private:
|
||||
QDataStream m_stream;
|
||||
LauncherPacketType m_type = LauncherPacketType::Shutdown;
|
||||
quintptr m_token = 0;
|
||||
QByteArray m_packetData;
|
||||
int m_sizeOfNextPacket = -1;
|
||||
};
|
||||
|
||||
class LauncherPacket
|
||||
{
|
||||
public:
|
||||
virtual ~LauncherPacket();
|
||||
|
||||
template<class Packet> static Packet extractPacket(quintptr token, const QByteArray &data)
|
||||
{
|
||||
Packet p(token);
|
||||
p.deserialize(data);
|
||||
return p;
|
||||
}
|
||||
|
||||
QByteArray serialize() const;
|
||||
void deserialize(const QByteArray &data);
|
||||
|
||||
const LauncherPacketType type;
|
||||
const quintptr token = 0;
|
||||
|
||||
protected:
|
||||
LauncherPacket(LauncherPacketType type, quintptr token) : type(type), token(token) { }
|
||||
|
||||
private:
|
||||
virtual void doSerialize(QDataStream &stream) const = 0;
|
||||
virtual void doDeserialize(QDataStream &stream) = 0;
|
||||
};
|
||||
|
||||
class StartProcessPacket : public LauncherPacket
|
||||
{
|
||||
public:
|
||||
StartProcessPacket(quintptr token);
|
||||
|
||||
QString command;
|
||||
QStringList arguments;
|
||||
QString workingDir;
|
||||
QStringList env;
|
||||
|
||||
private:
|
||||
void doSerialize(QDataStream &stream) const override;
|
||||
void doDeserialize(QDataStream &stream) override;
|
||||
};
|
||||
|
||||
class StopProcessPacket : public LauncherPacket
|
||||
{
|
||||
public:
|
||||
StopProcessPacket(quintptr token);
|
||||
|
||||
private:
|
||||
void doSerialize(QDataStream &stream) const override;
|
||||
void doDeserialize(QDataStream &stream) override;
|
||||
};
|
||||
|
||||
class ShutdownPacket : public LauncherPacket
|
||||
{
|
||||
public:
|
||||
ShutdownPacket();
|
||||
|
||||
private:
|
||||
void doSerialize(QDataStream &stream) const override;
|
||||
void doDeserialize(QDataStream &stream) override;
|
||||
};
|
||||
|
||||
class ProcessErrorPacket : public LauncherPacket
|
||||
{
|
||||
public:
|
||||
ProcessErrorPacket(quintptr token);
|
||||
|
||||
QProcess::ProcessError error = QProcess::UnknownError;
|
||||
QString errorString;
|
||||
|
||||
private:
|
||||
void doSerialize(QDataStream &stream) const override;
|
||||
void doDeserialize(QDataStream &stream) override;
|
||||
};
|
||||
|
||||
class ProcessFinishedPacket : public LauncherPacket
|
||||
{
|
||||
public:
|
||||
ProcessFinishedPacket(quintptr token);
|
||||
|
||||
QString errorString;
|
||||
QByteArray stdOut;
|
||||
QByteArray stdErr;
|
||||
QProcess::ExitStatus exitStatus = QProcess::ExitStatus::NormalExit;
|
||||
QProcess::ProcessError error = QProcess::ProcessError::UnknownError;
|
||||
int exitCode = 0;
|
||||
|
||||
private:
|
||||
void doSerialize(QDataStream &stream) const override;
|
||||
void doDeserialize(QDataStream &stream) override;
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Utils
|
||||
|
||||
Q_DECLARE_METATYPE(Utils::Internal::LauncherPacketType);
|
141
src/libs/utils/launchersocket.cpp
Normal file
141
src/libs/utils/launchersocket.cpp
Normal file
@@ -0,0 +1,141 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2021 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 "launchersocket.h"
|
||||
|
||||
#include "qtcassert.h"
|
||||
|
||||
#include <QtCore/qcoreapplication.h>
|
||||
#include <QtCore/qtimer.h>
|
||||
#include <QtNetwork/qlocalsocket.h>
|
||||
|
||||
namespace Utils {
|
||||
namespace Internal {
|
||||
|
||||
LauncherSocket::LauncherSocket(QObject *parent) : QObject(parent)
|
||||
{
|
||||
qRegisterMetaType<Utils::Internal::LauncherPacketType>();
|
||||
qRegisterMetaType<quintptr>("quintptr");
|
||||
}
|
||||
|
||||
void LauncherSocket::sendData(const QByteArray &data)
|
||||
{
|
||||
if (!isReady())
|
||||
return;
|
||||
std::lock_guard<std::mutex> locker(m_requestsMutex);
|
||||
m_requests.push_back(data);
|
||||
if (m_requests.size() == 1)
|
||||
QTimer::singleShot(0, this, &LauncherSocket::handleRequests);
|
||||
}
|
||||
|
||||
void LauncherSocket::shutdown()
|
||||
{
|
||||
const auto socket = m_socket.exchange(nullptr);
|
||||
if (!socket)
|
||||
return;
|
||||
socket->disconnect();
|
||||
socket->write(ShutdownPacket().serialize());
|
||||
socket->waitForBytesWritten(1000);
|
||||
socket->deleteLater();
|
||||
}
|
||||
|
||||
void LauncherSocket::setSocket(QLocalSocket *socket)
|
||||
{
|
||||
QTC_ASSERT(!m_socket, return);
|
||||
m_socket.store(socket);
|
||||
m_packetParser.setDevice(m_socket);
|
||||
connect(m_socket,
|
||||
#if (QT_VERSION < QT_VERSION_CHECK(5, 15, 0))
|
||||
static_cast<void(QLocalSocket::*)(QLocalSocket::LocalSocketError)>(&QLocalSocket::error),
|
||||
#else
|
||||
&QLocalSocket::errorOccurred,
|
||||
#endif
|
||||
this, &LauncherSocket::handleSocketError);
|
||||
connect(m_socket, &QLocalSocket::readyRead,
|
||||
this, &LauncherSocket::handleSocketDataAvailable);
|
||||
connect(m_socket, &QLocalSocket::disconnected,
|
||||
this, &LauncherSocket::handleSocketDisconnected);
|
||||
emit ready();
|
||||
}
|
||||
|
||||
void LauncherSocket::handleSocketError()
|
||||
{
|
||||
auto socket = m_socket.load();
|
||||
if (socket->error() != QLocalSocket::PeerClosedError)
|
||||
handleError(QCoreApplication::translate("Utils::LauncherSocket",
|
||||
"Socket error: %1").arg(socket->errorString()));
|
||||
}
|
||||
|
||||
void LauncherSocket::handleSocketDataAvailable()
|
||||
{
|
||||
try {
|
||||
if (!m_packetParser.parse())
|
||||
return;
|
||||
} catch (const PacketParser::InvalidPacketSizeException &e) {
|
||||
handleError(QCoreApplication::translate("Utils::LauncherSocket",
|
||||
"Internal protocol error: invalid packet size %1.").arg(e.size));
|
||||
return;
|
||||
}
|
||||
switch (m_packetParser.type()) {
|
||||
case LauncherPacketType::ProcessError:
|
||||
case LauncherPacketType::ProcessFinished:
|
||||
emit packetArrived(m_packetParser.type(), m_packetParser.token(),
|
||||
m_packetParser.packetData());
|
||||
break;
|
||||
default:
|
||||
handleError(QCoreApplication::translate("Utils::LauncherSocket",
|
||||
"Internal protocol error: invalid packet type %1.")
|
||||
.arg(static_cast<int>(m_packetParser.type())));
|
||||
return;
|
||||
}
|
||||
handleSocketDataAvailable();
|
||||
}
|
||||
|
||||
void LauncherSocket::handleSocketDisconnected()
|
||||
{
|
||||
handleError(QCoreApplication::translate("Utils::LauncherSocket",
|
||||
"Launcher socket closed unexpectedly"));
|
||||
}
|
||||
|
||||
void LauncherSocket::handleError(const QString &error)
|
||||
{
|
||||
const auto socket = m_socket.exchange(nullptr);
|
||||
socket->disconnect();
|
||||
socket->deleteLater();
|
||||
emit errorOccurred(error);
|
||||
}
|
||||
|
||||
void LauncherSocket::handleRequests()
|
||||
{
|
||||
const auto socket = m_socket.load();
|
||||
QTC_ASSERT(socket, return);
|
||||
std::lock_guard<std::mutex> locker(m_requestsMutex);
|
||||
for (const QByteArray &request : qAsConst(m_requests))
|
||||
socket->write(request);
|
||||
m_requests.clear();
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Utils
|
77
src/libs/utils/launchersocket.h
Normal file
77
src/libs/utils/launchersocket.h
Normal file
@@ -0,0 +1,77 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2021 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 "launcherpackets.h"
|
||||
|
||||
#include <QtCore/qobject.h>
|
||||
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QLocalSocket;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
namespace Utils {
|
||||
class LauncherInterface;
|
||||
|
||||
namespace Internal {
|
||||
|
||||
class LauncherSocket : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
friend class Utils::LauncherInterface;
|
||||
public:
|
||||
bool isReady() const { return m_socket.load(); }
|
||||
void sendData(const QByteArray &data);
|
||||
|
||||
signals:
|
||||
void ready();
|
||||
void errorOccurred(const QString &error);
|
||||
void packetArrived(Utils::Internal::LauncherPacketType type, quintptr token,
|
||||
const QByteArray &payload);
|
||||
|
||||
private:
|
||||
LauncherSocket(QObject *parent);
|
||||
|
||||
void setSocket(QLocalSocket *socket);
|
||||
void shutdown();
|
||||
|
||||
void handleSocketError();
|
||||
void handleSocketDataAvailable();
|
||||
void handleSocketDisconnected();
|
||||
void handleError(const QString &error);
|
||||
void handleRequests();
|
||||
|
||||
std::atomic<QLocalSocket *> m_socket{nullptr};
|
||||
PacketParser m_packetParser;
|
||||
std::vector<QByteArray> m_requests;
|
||||
std::mutex m_requestsMutex;
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Utils
|
@@ -142,6 +142,9 @@ SOURCES += \
|
||||
$$PWD/layoutbuilder.cpp \
|
||||
$$PWD/variablechooser.cpp \
|
||||
$$PWD/futuresynchronizer.cpp \
|
||||
$$PWD/launcherinterface.cpp \
|
||||
$$PWD/launcherpackets.cpp \
|
||||
$$PWD/launchersocket.cpp \
|
||||
$$PWD/qtcsettings.cpp \
|
||||
$$PWD/link.cpp \
|
||||
$$PWD/linecolumn.cpp \
|
||||
@@ -302,6 +305,9 @@ HEADERS += \
|
||||
$$PWD/variablechooser.h \
|
||||
$$PWD/set_algorithm.h \
|
||||
$$PWD/futuresynchronizer.h \
|
||||
$$PWD/launcherinterface.h \
|
||||
$$PWD/launcherpackets.h \
|
||||
$$PWD/launchersocket.h \
|
||||
$$PWD/qtcsettings.h
|
||||
|
||||
FORMS += $$PWD/filewizardpage.ui \
|
||||
|
@@ -159,6 +159,12 @@ Project {
|
||||
"json.h",
|
||||
"jsontreeitem.cpp",
|
||||
"jsontreeitem.h",
|
||||
"launcherinterface.cpp",
|
||||
"launcherinterface.h",
|
||||
"launcherpackets.cpp",
|
||||
"launcherpackets.h",
|
||||
"launchersocket.cpp",
|
||||
"launchersocket.h",
|
||||
"layoutbuilder.cpp",
|
||||
"layoutbuilder.h",
|
||||
"linecolumn.cpp",
|
||||
|
@@ -33,6 +33,7 @@ if (APPLE)
|
||||
add_subdirectory(iostool)
|
||||
endif()
|
||||
|
||||
add_subdirectory(processlauncher)
|
||||
add_subdirectory(qml2puppet)
|
||||
add_subdirectory(qtcdebugger) ## windows only
|
||||
# add_subdirectory(qtcrashhandler)
|
||||
|
14
src/tools/processlauncher/CMakeLists.txt
Normal file
14
src/tools/processlauncher/CMakeLists.txt
Normal file
@@ -0,0 +1,14 @@
|
||||
set(UTILSDIR "${PROJECT_SOURCE_DIR}/src/libs/utils")
|
||||
|
||||
add_qtc_executable(qtcreator_processlauncher
|
||||
INCLUDES "${UTILSDIR}"
|
||||
DEPENDS Qt5::Core Qt5::Network
|
||||
SOURCES
|
||||
launcherlogging.cpp
|
||||
launcherlogging.h
|
||||
launchersockethandler.cpp
|
||||
launchersockethandler.h
|
||||
processlauncher-main.cpp
|
||||
${UTILSDIR}/launcherpackets.cpp
|
||||
${UTILSDIR}/launcherpackets.h
|
||||
)
|
32
src/tools/processlauncher/launcherlogging.cpp
Normal file
32
src/tools/processlauncher/launcherlogging.cpp
Normal file
@@ -0,0 +1,32 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2021 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 "launcherlogging.h"
|
||||
|
||||
namespace Utils {
|
||||
namespace Internal {
|
||||
Q_LOGGING_CATEGORY(launcherLog, "qtc.utils.launcher", QtWarningMsg)
|
||||
}
|
||||
}
|
38
src/tools/processlauncher/launcherlogging.h
Normal file
38
src/tools/processlauncher/launcherlogging.h
Normal file
@@ -0,0 +1,38 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2021 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 <QtCore/qloggingcategory.h>
|
||||
#include <QtCore/qstring.h>
|
||||
|
||||
namespace Utils {
|
||||
namespace Internal {
|
||||
Q_DECLARE_LOGGING_CATEGORY(launcherLog)
|
||||
template<typename T> void logDebug(const T &msg) { qCDebug(launcherLog) << msg; }
|
||||
template<typename T> void logWarn(const T &msg) { qCWarning(launcherLog) << msg; }
|
||||
template<typename T> void logError(const T &msg) { qCCritical(launcherLog) << msg; }
|
||||
}
|
||||
}
|
281
src/tools/processlauncher/launchersockethandler.cpp
Normal file
281
src/tools/processlauncher/launchersockethandler.cpp
Normal file
@@ -0,0 +1,281 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2021 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 "launchersockethandler.h"
|
||||
|
||||
#include "launcherlogging.h"
|
||||
|
||||
#include <QtCore/qcoreapplication.h>
|
||||
#include <QtCore/qprocess.h>
|
||||
#include <QtCore/qtimer.h>
|
||||
#include <QtNetwork/qlocalsocket.h>
|
||||
|
||||
namespace Utils {
|
||||
namespace Internal {
|
||||
|
||||
class Process : public QProcess
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
Process(quintptr token, QObject *parent = nullptr) :
|
||||
QProcess(parent), m_token(token), m_stopTimer(new QTimer(this))
|
||||
{
|
||||
m_stopTimer->setSingleShot(true);
|
||||
connect(m_stopTimer, &QTimer::timeout, this, &Process::cancel);
|
||||
}
|
||||
|
||||
void cancel()
|
||||
{
|
||||
switch (m_stopState) {
|
||||
case StopState::Inactive:
|
||||
m_stopState = StopState::Terminating;
|
||||
m_stopTimer->start(3000);
|
||||
terminate();
|
||||
break;
|
||||
case StopState::Terminating:
|
||||
m_stopState = StopState::Killing;
|
||||
m_stopTimer->start(3000);
|
||||
kill();
|
||||
break;
|
||||
case StopState::Killing:
|
||||
m_stopState = StopState::Inactive;
|
||||
emit failedToStop();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void stopStopProcedure()
|
||||
{
|
||||
m_stopState = StopState::Inactive;
|
||||
m_stopTimer->stop();
|
||||
}
|
||||
|
||||
quintptr token() const { return m_token; }
|
||||
|
||||
signals:
|
||||
void failedToStop();
|
||||
|
||||
private:
|
||||
const quintptr m_token;
|
||||
QTimer * const m_stopTimer;
|
||||
enum class StopState { Inactive, Terminating, Killing } m_stopState = StopState::Inactive;
|
||||
};
|
||||
|
||||
LauncherSocketHandler::LauncherSocketHandler(QString serverPath, QObject *parent)
|
||||
: QObject(parent),
|
||||
m_serverPath(std::move(serverPath)),
|
||||
m_socket(new QLocalSocket(this))
|
||||
{
|
||||
m_packetParser.setDevice(m_socket);
|
||||
}
|
||||
|
||||
LauncherSocketHandler::~LauncherSocketHandler()
|
||||
{
|
||||
m_socket->disconnect();
|
||||
if (m_socket->state() != QLocalSocket::UnconnectedState) {
|
||||
logWarn("socket handler destroyed while connection was active");
|
||||
m_socket->close();
|
||||
}
|
||||
for (auto it = m_processes.cbegin(); it != m_processes.cend(); ++it)
|
||||
it.value()->disconnect();
|
||||
}
|
||||
|
||||
void LauncherSocketHandler::start()
|
||||
{
|
||||
connect(m_socket, &QLocalSocket::disconnected,
|
||||
this, &LauncherSocketHandler::handleSocketClosed);
|
||||
connect(m_socket, &QLocalSocket::readyRead, this, &LauncherSocketHandler::handleSocketData);
|
||||
connect(m_socket,
|
||||
#if (QT_VERSION < QT_VERSION_CHECK(5, 15, 0))
|
||||
static_cast<void(QLocalSocket::*)(QLocalSocket::LocalSocketError)>(&QLocalSocket::error),
|
||||
#else
|
||||
&QLocalSocket::errorOccurred,
|
||||
#endif
|
||||
this, &LauncherSocketHandler::handleSocketError);
|
||||
m_socket->connectToServer(m_serverPath);
|
||||
}
|
||||
|
||||
void LauncherSocketHandler::handleSocketData()
|
||||
{
|
||||
try {
|
||||
if (!m_packetParser.parse())
|
||||
return;
|
||||
} catch (const PacketParser::InvalidPacketSizeException &e) {
|
||||
logWarn(QStringLiteral("Internal protocol error: invalid packet size %1.")
|
||||
.arg(e.size));
|
||||
return;
|
||||
}
|
||||
switch (m_packetParser.type()) {
|
||||
case LauncherPacketType::StartProcess:
|
||||
handleStartPacket();
|
||||
break;
|
||||
case LauncherPacketType::StopProcess:
|
||||
handleStopPacket();
|
||||
break;
|
||||
case LauncherPacketType::Shutdown:
|
||||
handleShutdownPacket();
|
||||
return;
|
||||
default:
|
||||
logWarn(QStringLiteral("Internal protocol error: invalid packet type %1.")
|
||||
.arg(static_cast<int>(m_packetParser.type())));
|
||||
return;
|
||||
}
|
||||
handleSocketData();
|
||||
}
|
||||
|
||||
void LauncherSocketHandler::handleSocketError()
|
||||
{
|
||||
if (m_socket->error() != QLocalSocket::PeerClosedError) {
|
||||
logError(QStringLiteral("socket error: %1").arg(m_socket->errorString()));
|
||||
m_socket->disconnect();
|
||||
qApp->quit();
|
||||
}
|
||||
}
|
||||
|
||||
void LauncherSocketHandler::handleSocketClosed()
|
||||
{
|
||||
for (auto it = m_processes.cbegin(); it != m_processes.cend(); ++it) {
|
||||
if (it.value()->state() != QProcess::NotRunning) {
|
||||
logWarn("client closed connection while process still running");
|
||||
break;
|
||||
}
|
||||
}
|
||||
m_socket->disconnect();
|
||||
qApp->quit();
|
||||
}
|
||||
|
||||
void LauncherSocketHandler::handleProcessError()
|
||||
{
|
||||
Process * proc = senderProcess();
|
||||
if (proc->error() != QProcess::FailedToStart)
|
||||
return;
|
||||
proc->stopStopProcedure();
|
||||
ProcessErrorPacket packet(proc->token());
|
||||
packet.error = proc->error();
|
||||
packet.errorString = proc->errorString();
|
||||
sendPacket(packet);
|
||||
}
|
||||
|
||||
void LauncherSocketHandler::handleProcessFinished()
|
||||
{
|
||||
Process * proc = senderProcess();
|
||||
proc->stopStopProcedure();
|
||||
ProcessFinishedPacket packet(proc->token());
|
||||
packet.error = proc->error();
|
||||
packet.errorString = proc->errorString();
|
||||
packet.exitCode = proc->exitCode();
|
||||
packet.exitStatus = proc->exitStatus();
|
||||
packet.stdErr = proc->readAllStandardError();
|
||||
packet.stdOut = proc->readAllStandardOutput();
|
||||
sendPacket(packet);
|
||||
}
|
||||
|
||||
void LauncherSocketHandler::handleStopFailure()
|
||||
{
|
||||
// Process did not react to a kill signal. Rare, but not unheard of.
|
||||
// Forget about the associated Process object and report process exit to the client.
|
||||
Process * proc = senderProcess();
|
||||
proc->disconnect();
|
||||
m_processes.remove(proc->token());
|
||||
ProcessFinishedPacket packet(proc->token());
|
||||
packet.error = QProcess::Crashed;
|
||||
packet.exitCode = -1;
|
||||
packet.exitStatus = QProcess::CrashExit;
|
||||
packet.stdErr = proc->readAllStandardError();
|
||||
packet.stdOut = proc->readAllStandardOutput();
|
||||
sendPacket(packet);
|
||||
}
|
||||
|
||||
void LauncherSocketHandler::handleStartPacket()
|
||||
{
|
||||
Process *& process = m_processes[m_packetParser.token()];
|
||||
if (!process)
|
||||
process = setupProcess(m_packetParser.token());
|
||||
if (process->state() != QProcess::NotRunning) {
|
||||
logWarn("got start request while process was running");
|
||||
return;
|
||||
}
|
||||
const auto packet = LauncherPacket::extractPacket<StartProcessPacket>(
|
||||
m_packetParser.token(),
|
||||
m_packetParser.packetData());
|
||||
process->setEnvironment(packet.env);
|
||||
process->setWorkingDirectory(packet.workingDir);
|
||||
process->start(packet.command, packet.arguments);
|
||||
}
|
||||
|
||||
void LauncherSocketHandler::handleStopPacket()
|
||||
{
|
||||
Process * const process = m_processes.value(m_packetParser.token());
|
||||
if (!process) {
|
||||
logWarn("got stop request for unknown process");
|
||||
return;
|
||||
}
|
||||
if (process->state() == QProcess::NotRunning) {
|
||||
// This can happen if the process finishes on its own at about the same time the client
|
||||
// sends the request.
|
||||
logDebug("got stop request when process was not running");
|
||||
return;
|
||||
}
|
||||
process->cancel();
|
||||
}
|
||||
|
||||
void LauncherSocketHandler::handleShutdownPacket()
|
||||
{
|
||||
logDebug("got shutdown request, closing down");
|
||||
for (auto it = m_processes.cbegin(); it != m_processes.cend(); ++it) {
|
||||
it.value()->disconnect();
|
||||
if (it.value()->state() != QProcess::NotRunning) {
|
||||
logWarn("got shutdown request while process was running");
|
||||
it.value()->terminate();
|
||||
}
|
||||
}
|
||||
m_socket->disconnect();
|
||||
qApp->quit();
|
||||
}
|
||||
|
||||
void LauncherSocketHandler::sendPacket(const LauncherPacket &packet)
|
||||
{
|
||||
m_socket->write(packet.serialize());
|
||||
}
|
||||
|
||||
Process *LauncherSocketHandler::setupProcess(quintptr token)
|
||||
{
|
||||
const auto p = new Process(token, this);
|
||||
connect(p, &QProcess::errorOccurred, this, &LauncherSocketHandler::handleProcessError);
|
||||
connect(p, static_cast<void (QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished),
|
||||
this, &LauncherSocketHandler::handleProcessFinished);
|
||||
connect(p, &Process::failedToStop, this, &LauncherSocketHandler::handleStopFailure);
|
||||
return p;
|
||||
}
|
||||
|
||||
Process *LauncherSocketHandler::senderProcess() const
|
||||
{
|
||||
return static_cast<Process *>(sender());
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Utils
|
||||
|
||||
#include <launchersockethandler.moc>
|
75
src/tools/processlauncher/launchersockethandler.h
Normal file
75
src/tools/processlauncher/launchersockethandler.h
Normal file
@@ -0,0 +1,75 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2021 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 <launcherpackets.h>
|
||||
|
||||
#include <QtCore/qbytearray.h>
|
||||
#include <QtCore/qhash.h>
|
||||
#include <QtCore/qobject.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QLocalSocket;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
namespace Utils {
|
||||
namespace Internal {
|
||||
class Process;
|
||||
|
||||
class LauncherSocketHandler : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit LauncherSocketHandler(QString socketPath, QObject *parent = nullptr);
|
||||
~LauncherSocketHandler() override;
|
||||
|
||||
void start();
|
||||
|
||||
private:
|
||||
void handleSocketData();
|
||||
void handleSocketError();
|
||||
void handleSocketClosed();
|
||||
void handleProcessError();
|
||||
void handleProcessFinished();
|
||||
void handleStopFailure();
|
||||
|
||||
void handleStartPacket();
|
||||
void handleStopPacket();
|
||||
void handleShutdownPacket();
|
||||
|
||||
void sendPacket(const LauncherPacket &packet);
|
||||
|
||||
Process *setupProcess(quintptr token);
|
||||
Process *senderProcess() const;
|
||||
|
||||
const QString m_serverPath;
|
||||
QLocalSocket * const m_socket;
|
||||
PacketParser m_packetParser;
|
||||
QHash<quintptr, Process *> m_processes;
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Utils
|
57
src/tools/processlauncher/processlauncher-main.cpp
Normal file
57
src/tools/processlauncher/processlauncher-main.cpp
Normal file
@@ -0,0 +1,57 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2021 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 "launcherlogging.h"
|
||||
#include "launchersockethandler.h"
|
||||
|
||||
#include <QtCore/qcoreapplication.h>
|
||||
#include <QtCore/qtimer.h>
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
#include <QtCore/qt_windows.h>
|
||||
|
||||
BOOL WINAPI consoleCtrlHandler(DWORD)
|
||||
{
|
||||
// Ignore Ctrl-C / Ctrl-Break. QtCreator will tell us to exit gracefully.
|
||||
return TRUE;
|
||||
}
|
||||
#endif
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
#ifdef Q_OS_WIN
|
||||
SetConsoleCtrlHandler(consoleCtrlHandler, TRUE);
|
||||
#endif
|
||||
|
||||
QCoreApplication app(argc, argv);
|
||||
if (app.arguments().size() != 2) {
|
||||
Utils::Internal::logError("Need exactly one argument (path to socket)");
|
||||
return 1;
|
||||
}
|
||||
|
||||
Utils::Internal::LauncherSocketHandler launcher(app.arguments().constLast());
|
||||
QTimer::singleShot(0, &launcher, &Utils::Internal::LauncherSocketHandler::start);
|
||||
return app.exec();
|
||||
}
|
21
src/tools/processlauncher/processlauncher.pro
Normal file
21
src/tools/processlauncher/processlauncher.pro
Normal file
@@ -0,0 +1,21 @@
|
||||
include(../../qtcreatortool.pri)
|
||||
|
||||
TARGET = qtcreator_processlauncher
|
||||
CONFIG += console c++17
|
||||
CONFIG -= app_bundle
|
||||
QT = core network
|
||||
|
||||
UTILS_DIR = $$PWD/../../libs/utils
|
||||
|
||||
INCLUDEPATH += $$UTILS_DIR
|
||||
|
||||
HEADERS += \
|
||||
launcherlogging.h \
|
||||
launchersockethandler.h \
|
||||
$$UTILS_DIR/launcherpackets.h
|
||||
|
||||
SOURCES += \
|
||||
launcherlogging.cpp \
|
||||
launchersockethandler.cpp \
|
||||
processlauncher-main.cpp \
|
||||
$$UTILS_DIR/launcherpackets.cpp
|
28
src/tools/processlauncher/processlauncher.qbs
Normal file
28
src/tools/processlauncher/processlauncher.qbs
Normal file
@@ -0,0 +1,28 @@
|
||||
import qbs
|
||||
import qbs.FileInfo
|
||||
|
||||
QtcTool {
|
||||
name: "qtcreator_processlauncher"
|
||||
|
||||
Depends { name: "Qt.network" }
|
||||
|
||||
cpp.includePaths: base.concat(pathToUtils)
|
||||
|
||||
files: [
|
||||
"launcherlogging.cpp",
|
||||
"launcherlogging.h",
|
||||
"launchersockethandler.cpp",
|
||||
"launchersockethandler.h",
|
||||
"processlauncher-main.cpp",
|
||||
]
|
||||
|
||||
property string pathToUtils: sourceDirectory + "/../../libs/utils"
|
||||
Group {
|
||||
name: "protocol sources"
|
||||
prefix: pathToUtils + '/'
|
||||
files: [
|
||||
"launcherpackets.cpp",
|
||||
"launcherpackets.h",
|
||||
]
|
||||
}
|
||||
}
|
@@ -7,6 +7,7 @@ SUBDIRS = qtpromaker \
|
||||
valgrindfake \
|
||||
3rdparty \
|
||||
buildoutputparser \
|
||||
processlauncher \
|
||||
qtc-askpass
|
||||
|
||||
isEmpty(QTC_SKIP_SDKTOOL): SUBDIRS += sdktool
|
||||
|
@@ -7,6 +7,7 @@ Project {
|
||||
"buildoutputparser/buildoutputparser.qbs",
|
||||
"clangbackend/clangbackend.qbs",
|
||||
"cplusplustools.qbs",
|
||||
"processlauncher/processlauncher.qbs",
|
||||
"qml2puppet/qml2puppet.qbs",
|
||||
"qtcdebugger/qtcdebugger.qbs",
|
||||
"qtcreatorcrashhandler/qtcreatorcrashhandler.qbs",
|
||||
|
Reference in New Issue
Block a user