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
|
itemviews.cpp itemviews.h
|
||||||
json.cpp json.h
|
json.cpp json.h
|
||||||
jsontreeitem.cpp jsontreeitem.h
|
jsontreeitem.cpp jsontreeitem.h
|
||||||
|
launcherinterface.cpp launcherinterface.h
|
||||||
|
launcherpackets.cpp launcherpackets.h
|
||||||
|
launchersocket.cpp launchersocket.h
|
||||||
layoutbuilder.cpp layoutbuilder.h
|
layoutbuilder.cpp layoutbuilder.h
|
||||||
linecolumn.cpp linecolumn.h
|
linecolumn.cpp linecolumn.h
|
||||||
link.cpp link.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/layoutbuilder.cpp \
|
||||||
$$PWD/variablechooser.cpp \
|
$$PWD/variablechooser.cpp \
|
||||||
$$PWD/futuresynchronizer.cpp \
|
$$PWD/futuresynchronizer.cpp \
|
||||||
|
$$PWD/launcherinterface.cpp \
|
||||||
|
$$PWD/launcherpackets.cpp \
|
||||||
|
$$PWD/launchersocket.cpp \
|
||||||
$$PWD/qtcsettings.cpp \
|
$$PWD/qtcsettings.cpp \
|
||||||
$$PWD/link.cpp \
|
$$PWD/link.cpp \
|
||||||
$$PWD/linecolumn.cpp \
|
$$PWD/linecolumn.cpp \
|
||||||
@@ -302,6 +305,9 @@ HEADERS += \
|
|||||||
$$PWD/variablechooser.h \
|
$$PWD/variablechooser.h \
|
||||||
$$PWD/set_algorithm.h \
|
$$PWD/set_algorithm.h \
|
||||||
$$PWD/futuresynchronizer.h \
|
$$PWD/futuresynchronizer.h \
|
||||||
|
$$PWD/launcherinterface.h \
|
||||||
|
$$PWD/launcherpackets.h \
|
||||||
|
$$PWD/launchersocket.h \
|
||||||
$$PWD/qtcsettings.h
|
$$PWD/qtcsettings.h
|
||||||
|
|
||||||
FORMS += $$PWD/filewizardpage.ui \
|
FORMS += $$PWD/filewizardpage.ui \
|
||||||
|
@@ -159,6 +159,12 @@ Project {
|
|||||||
"json.h",
|
"json.h",
|
||||||
"jsontreeitem.cpp",
|
"jsontreeitem.cpp",
|
||||||
"jsontreeitem.h",
|
"jsontreeitem.h",
|
||||||
|
"launcherinterface.cpp",
|
||||||
|
"launcherinterface.h",
|
||||||
|
"launcherpackets.cpp",
|
||||||
|
"launcherpackets.h",
|
||||||
|
"launchersocket.cpp",
|
||||||
|
"launchersocket.h",
|
||||||
"layoutbuilder.cpp",
|
"layoutbuilder.cpp",
|
||||||
"layoutbuilder.h",
|
"layoutbuilder.h",
|
||||||
"linecolumn.cpp",
|
"linecolumn.cpp",
|
||||||
|
@@ -33,6 +33,7 @@ if (APPLE)
|
|||||||
add_subdirectory(iostool)
|
add_subdirectory(iostool)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
add_subdirectory(processlauncher)
|
||||||
add_subdirectory(qml2puppet)
|
add_subdirectory(qml2puppet)
|
||||||
add_subdirectory(qtcdebugger) ## windows only
|
add_subdirectory(qtcdebugger) ## windows only
|
||||||
# add_subdirectory(qtcrashhandler)
|
# 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 \
|
valgrindfake \
|
||||||
3rdparty \
|
3rdparty \
|
||||||
buildoutputparser \
|
buildoutputparser \
|
||||||
|
processlauncher \
|
||||||
qtc-askpass
|
qtc-askpass
|
||||||
|
|
||||||
isEmpty(QTC_SKIP_SDKTOOL): SUBDIRS += sdktool
|
isEmpty(QTC_SKIP_SDKTOOL): SUBDIRS += sdktool
|
||||||
|
@@ -7,6 +7,7 @@ Project {
|
|||||||
"buildoutputparser/buildoutputparser.qbs",
|
"buildoutputparser/buildoutputparser.qbs",
|
||||||
"clangbackend/clangbackend.qbs",
|
"clangbackend/clangbackend.qbs",
|
||||||
"cplusplustools.qbs",
|
"cplusplustools.qbs",
|
||||||
|
"processlauncher/processlauncher.qbs",
|
||||||
"qml2puppet/qml2puppet.qbs",
|
"qml2puppet/qml2puppet.qbs",
|
||||||
"qtcdebugger/qtcdebugger.qbs",
|
"qtcdebugger/qtcdebugger.qbs",
|
||||||
"qtcreatorcrashhandler/qtcreatorcrashhandler.qbs",
|
"qtcreatorcrashhandler/qtcreatorcrashhandler.qbs",
|
||||||
|
Reference in New Issue
Block a user