diff --git a/CMakePresets.json b/CMakePresets.json index 0e4b60e22b7..b859e1ac4ef 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -8,7 +8,7 @@ "hidden" : true, "cacheVariables": { "BUILD_PLUGINS": "Core;Designer;DiffEditor;TextEditor;ProjectExplorer;CppEditor;CodePaster;Docker;Git;Help;QmakeProjectManager;CMakeProjectManager;ClangCodeModel;ClangTools;ClangFormat;Debugger;QtSupport;ResourceEditor;VcsBase;Welcome;LanguageClient;RemoteLinux", - "BUILD_EXECUTABLES": "QtCreator;qtcreator_ctrlc_stub;qtcreator_process_stub;win64interrupt;qtcreator_processlauncher", + "BUILD_EXECUTABLES": "QtCreator;qtcreator_ctrlc_stub;qtcreator_process_stub;win64interrupt", "WITH_QMLDESIGNER": "OFF" } } diff --git a/src/libs/utils/CMakeLists.txt b/src/libs/utils/CMakeLists.txt index f1f730dd389..c8a5f84196c 100644 --- a/src/libs/utils/CMakeLists.txt +++ b/src/libs/utils/CMakeLists.txt @@ -90,9 +90,6 @@ add_qtc_library(Utils infolabel.cpp infolabel.h itemviews.cpp itemviews.h jsontreeitem.cpp jsontreeitem.h - launcherinterface.cpp launcherinterface.h - launcherpackets.cpp launcherpackets.h - launchersocket.cpp launchersocket.h layoutbuilder.cpp layoutbuilder.h link.cpp link.h listmodel.h diff --git a/src/libs/utils/launcherinterface.cpp b/src/libs/utils/launcherinterface.cpp deleted file mode 100644 index d188e224fc1..00000000000 --- a/src/libs/utils/launcherinterface.cpp +++ /dev/null @@ -1,207 +0,0 @@ -// Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include "launcherinterface.h" - -#include "filepath.h" -#include "launchersocket.h" -#include "qtcassert.h" -#include "temporarydirectory.h" -#include "utilstr.h" - -#include -#include -#include -#include - -#ifdef Q_OS_UNIX -#include -#endif - -namespace Utils { -namespace Internal { - -static QString launcherSocketName() -{ - return TemporaryDirectory::masterDirectoryPath() - + QStringLiteral("/launcher-%1").arg(QString::number(qApp->applicationPid())); -} - -class LauncherInterfacePrivate : public QObject -{ - Q_OBJECT -public: - LauncherInterfacePrivate(); - ~LauncherInterfacePrivate() override; - - void doStart(); - void doStop(); - void handleNewConnection(); - void handleProcessError(); - void handleProcessFinished(); - void handleProcessStderr(); - Internal::LauncherSocket *socket() const { return m_socket; } - - void setPathToLauncher(const QString &path) { if (!path.isEmpty()) m_pathToLauncher = path; } - QString launcherFilePath() const { return m_pathToLauncher + QLatin1String("/qtcreator_processlauncher"); } -signals: - void errorOccurred(const QString &error); - -private: - QLocalServer * const m_server; - Internal::LauncherSocket *const m_socket; - QProcess *m_process = nullptr; - QString m_pathToLauncher; -}; - -LauncherInterfacePrivate::LauncherInterfacePrivate() - : m_server(new QLocalServer(this)), m_socket(new LauncherSocket(this)) -{ - m_pathToLauncher = qApp->applicationDirPath() + '/' + QLatin1String(RELATIVE_LIBEXEC_PATH); - QObject::connect(m_server, &QLocalServer::newConnection, - this, &LauncherInterfacePrivate::handleNewConnection); -} - -LauncherInterfacePrivate::~LauncherInterfacePrivate() -{ - m_server->disconnect(); -} - -void LauncherInterfacePrivate::doStart() -{ - const QString &socketName = launcherSocketName(); - QLocalServer::removeServer(socketName); - if (!m_server->listen(socketName)) { - emit errorOccurred(m_server->errorString()); - return; - } - m_process = new QProcess(this); - connect(m_process, &QProcess::errorOccurred, this, &LauncherInterfacePrivate::handleProcessError); - connect(m_process, &QProcess::finished, - this, &LauncherInterfacePrivate::handleProcessFinished); - connect(m_process, &QProcess::readyReadStandardError, - this, &LauncherInterfacePrivate::handleProcessStderr); -#ifdef Q_OS_UNIX -# if QT_VERSION >= QT_VERSION_CHECK(6, 7, 0) - m_process->setUnixProcessParameters(QProcess::UnixProcessFlag::CreateNewSession); -# else - m_process->setChildProcessModifier([] { - setpgid(0, 0); - }); -# endif -#endif - - m_process->start(launcherFilePath(), QStringList(m_server->fullServerName())); -} - -void LauncherInterfacePrivate::doStop() -{ - m_server->close(); - QTC_ASSERT(m_process, return); - m_socket->shutdown(); - m_process->waitForFinished(-1); // Let the process interface finish so that it finishes - // reaping any possible processes it has started. - delete m_process; - m_process = nullptr; -} - -void LauncherInterfacePrivate::handleNewConnection() -{ - QLocalSocket * const socket = m_server->nextPendingConnection(); - if (!socket) - return; - m_server->close(); - m_socket->setSocket(socket); -} - -void LauncherInterfacePrivate::handleProcessError() -{ - if (m_process->error() == QProcess::FailedToStart) { - const QString launcherPathForUser - = QDir::toNativeSeparators(QDir::cleanPath(m_process->program())); - emit errorOccurred(Tr::tr("Failed to start process launcher at \"%1\": %2") - .arg(launcherPathForUser, m_process->errorString())); - } -} - -void LauncherInterfacePrivate::handleProcessFinished() -{ - emit errorOccurred(Tr::tr("Process launcher closed unexpectedly: %1") - .arg(m_process->errorString())); -} - -void LauncherInterfacePrivate::handleProcessStderr() -{ - qDebug() << "[launcher]" << m_process->readAllStandardError(); -} - -} // namespace Internal - -using namespace Utils::Internal; - -static QMutex s_instanceMutex; -static QString s_pathToLauncher; -static std::atomic_bool s_started = false; - -LauncherInterface::LauncherInterface() - : m_private(new LauncherInterfacePrivate()) -{ - m_private->moveToThread(&m_thread); - QObject::connect(&m_thread, &QThread::finished, m_private, &QObject::deleteLater); - m_thread.start(); - m_thread.moveToThread(qApp->thread()); - - m_private->setPathToLauncher(s_pathToLauncher); - const FilePath launcherFilePath = FilePath::fromString(m_private->launcherFilePath()) - .cleanPath().withExecutableSuffix(); - auto launcherIsNotExecutable = [&launcherFilePath] { - qWarning() << "The Creator's process launcher" - << launcherFilePath << "is not executable."; - }; - QTC_ASSERT(launcherFilePath.isExecutableFile(), launcherIsNotExecutable(); return); - s_started = true; - // Call in launcher's thread. - QMetaObject::invokeMethod(m_private, &LauncherInterfacePrivate::doStart); -} - -LauncherInterface::~LauncherInterface() -{ - QMutexLocker locker(&s_instanceMutex); - LauncherInterfacePrivate *p = instance()->m_private; - // Call in launcher's thread. - QMetaObject::invokeMethod(p, &LauncherInterfacePrivate::doStop, Qt::BlockingQueuedConnection); - m_thread.quit(); - m_thread.wait(); -} - -void LauncherInterface::setPathToLauncher(const QString &pathToLauncher) -{ - s_pathToLauncher = pathToLauncher; -} - -bool LauncherInterface::isStarted() -{ - return s_started; -} - -void LauncherInterface::sendData(const QByteArray &data) -{ - QMutexLocker locker(&s_instanceMutex); - instance()->m_private->socket()->sendData(data); -} - -Internal::CallerHandle *LauncherInterface::registerHandle(QObject *parent, quintptr token) -{ - QMutexLocker locker(&s_instanceMutex); - return instance()->m_private->socket()->registerHandle(parent, token); -} - -void LauncherInterface::unregisterHandle(quintptr token) -{ - QMutexLocker locker(&s_instanceMutex); - instance()->m_private->socket()->unregisterHandle(token); -} - -} // namespace Utils - -#include "launcherinterface.moc" diff --git a/src/libs/utils/launcherinterface.h b/src/libs/utils/launcherinterface.h deleted file mode 100644 index 71d6c7977ac..00000000000 --- a/src/libs/utils/launcherinterface.h +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include "utils_global.h" - -#include "processreaper.h" -#include "singleton.h" - -#include - -namespace Utils { -namespace Internal { -class CallerHandle; -class LauncherHandle; -class LauncherInterfacePrivate; -class ProcessLauncherImpl; -} - -class QTCREATOR_UTILS_EXPORT LauncherInterface final - : public SingletonWithOptionalDependencies -{ -public: - static void setPathToLauncher(const QString &pathToLauncher); - -private: - friend class Internal::CallerHandle; - friend class Internal::LauncherHandle; - friend class Internal::ProcessLauncherImpl; - - static bool isStarted(); - static void sendData(const QByteArray &data); - static Internal::CallerHandle *registerHandle(QObject *parent, quintptr token); - static void unregisterHandle(quintptr token); - - LauncherInterface(); - ~LauncherInterface(); - - QThread m_thread; - Internal::LauncherInterfacePrivate *m_private; - friend class SingletonWithOptionalDependencies; -}; - -} // namespace Utils diff --git a/src/libs/utils/launcherpackets.cpp b/src/libs/utils/launcherpackets.cpp deleted file mode 100644 index a770e8865da..00000000000 --- a/src/libs/utils/launcherpackets.cpp +++ /dev/null @@ -1,195 +0,0 @@ -// Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include "launcherpackets.h" - -#include - -namespace Utils { -namespace Internal { - -LauncherPacket::~LauncherPacket() = default; - -QByteArray LauncherPacket::serialize() const -{ - QByteArray data; - QDataStream stream(&data, QIODevice::WriteOnly); - stream << static_cast(0) << static_cast(type) << token; - doSerialize(stream); - stream.device()->reset(); - stream << static_cast(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 - << int(processMode) - << writeData - << int(processChannelMode) - << standardInputFile - << belowNormalPriority - << nativeArguments - << lowPriority - << unixTerminalDisabled - << useCtrlCStub - << reaperTimeout - << createConsoleOnWindows - << forceDefaultErrorMode; -} - -void StartProcessPacket::doDeserialize(QDataStream &stream) -{ - int processModeInt; - int processChannelModeInt; - stream >> command - >> arguments - >> workingDir - >> env - >> processModeInt - >> writeData - >> processChannelModeInt - >> standardInputFile - >> belowNormalPriority - >> nativeArguments - >> lowPriority - >> unixTerminalDisabled - >> useCtrlCStub - >> reaperTimeout - >> createConsoleOnWindows - >> forceDefaultErrorMode; - processMode = Utils::ProcessMode(processModeInt); - processChannelMode = QProcess::ProcessChannelMode(processChannelModeInt); -} - - -ProcessStartedPacket::ProcessStartedPacket(quintptr token) - : LauncherPacket(LauncherPacketType::ProcessStarted, token) -{ -} - -void ProcessStartedPacket::doSerialize(QDataStream &stream) const -{ - stream << processId; -} - -void ProcessStartedPacket::doDeserialize(QDataStream &stream) -{ - stream >> processId; -} - - -ControlProcessPacket::ControlProcessPacket(quintptr token) - : LauncherPacket(LauncherPacketType::ControlProcess, token) -{ -} - -void ControlProcessPacket::doSerialize(QDataStream &stream) const -{ - stream << int(signalType); -} - -void ControlProcessPacket::doDeserialize(QDataStream &stream) -{ - int signalTypeInt; - stream >> signalTypeInt; - signalType = SignalType(signalTypeInt); -} - -void WritePacket::doSerialize(QDataStream &stream) const -{ - stream << inputData; -} - -void WritePacket::doDeserialize(QDataStream &stream) -{ - stream >> inputData; -} - -void ReadyReadPacket::doSerialize(QDataStream &stream) const -{ - stream << standardChannel; -} - -void ReadyReadPacket::doDeserialize(QDataStream &stream) -{ - stream >> standardChannel; -} - - -ProcessDonePacket::ProcessDonePacket(quintptr token) - : LauncherPacket(LauncherPacketType::ProcessDone, token) -{ -} - -void ProcessDonePacket::doSerialize(QDataStream &stream) const -{ - stream << exitCode - << int(exitStatus) - << int(error) - << errorString - << stdOut - << stdErr; -} - -void ProcessDonePacket::doDeserialize(QDataStream &stream) -{ - int exitStatusInt, errorInt; - stream >> exitCode - >> exitStatusInt - >> errorInt - >> errorString - >> stdOut - >> stdErr; - exitStatus = QProcess::ExitStatus(exitStatusInt); - error = QProcess::ProcessError(errorInt); -} - -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(1 + sizeof(quintptr)); - if (m_sizeOfNextPacket == -1) { - if (m_stream.device()->bytesAvailable() < static_cast(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(type); - m_stream >> m_token; - m_packetData = m_stream.device()->read(m_sizeOfNextPacket - commonPayloadSize); - m_sizeOfNextPacket = -1; - return true; -} - -} // namespace Internal -} // namespace Utils diff --git a/src/libs/utils/launcherpackets.h b/src/libs/utils/launcherpackets.h deleted file mode 100644 index d00169c1f6f..00000000000 --- a/src/libs/utils/launcherpackets.h +++ /dev/null @@ -1,210 +0,0 @@ -// Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include "processenums.h" - -#include -#include -#include - -QT_BEGIN_NAMESPACE -class QByteArray; -QT_END_NAMESPACE - -namespace Utils { -namespace Internal { - -enum class LauncherPacketType { - // client -> launcher packets: - Shutdown, - StartProcess, - WriteIntoProcess, - ControlProcess, - // launcher -> client packets: - ProcessStarted, - ReadyReadStandardOutput, - ReadyReadStandardError, - ProcessDone -}; - -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 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; - ProcessMode processMode = ProcessMode::Reader; - QByteArray writeData; - QProcess::ProcessChannelMode processChannelMode = QProcess::SeparateChannels; - QString standardInputFile; - bool belowNormalPriority = false; - QString nativeArguments; - bool lowPriority = false; - bool unixTerminalDisabled = false; - bool useCtrlCStub = false; - int reaperTimeout = 500; - bool createConsoleOnWindows = false; - bool forceDefaultErrorMode = false; - -private: - void doSerialize(QDataStream &stream) const override; - void doDeserialize(QDataStream &stream) override; -}; - -class ProcessStartedPacket : public LauncherPacket -{ -public: - ProcessStartedPacket(quintptr token); - - int processId = 0; - -private: - void doSerialize(QDataStream &stream) const override; - void doDeserialize(QDataStream &stream) override; -}; - -class ControlProcessPacket : public LauncherPacket -{ -public: - ControlProcessPacket(quintptr token); - - enum class SignalType { - Kill, // Calls QProcess::kill - Terminate, // Calls QProcess::terminate - Close, // Puts the process into the reaper, no confirmation signal is being sent. - CloseWriteChannel - }; - - SignalType signalType = SignalType::Kill; - -private: - void doSerialize(QDataStream &stream) const override; - void doDeserialize(QDataStream &stream) override; -}; - -class WritePacket : public LauncherPacket -{ -public: - WritePacket(quintptr token) : LauncherPacket(LauncherPacketType::WriteIntoProcess, token) { } - - QByteArray inputData; - -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 ReadyReadPacket : public LauncherPacket -{ -public: - QByteArray standardChannel; - -protected: - ReadyReadPacket(LauncherPacketType type, quintptr token) : LauncherPacket(type, token) { } - -private: - void doSerialize(QDataStream &stream) const override; - void doDeserialize(QDataStream &stream) override; -}; - -class ReadyReadStandardOutputPacket : public ReadyReadPacket -{ -public: - ReadyReadStandardOutputPacket(quintptr token) - : ReadyReadPacket(LauncherPacketType::ReadyReadStandardOutput, token) { } -}; - -class ReadyReadStandardErrorPacket : public ReadyReadPacket -{ -public: - ReadyReadStandardErrorPacket(quintptr token) - : ReadyReadPacket(LauncherPacketType::ReadyReadStandardError, token) { } -}; - -class ProcessDonePacket : public LauncherPacket -{ -public: - ProcessDonePacket(quintptr token); - - QByteArray stdOut; - QByteArray stdErr; - - int exitCode = 0; - QProcess::ExitStatus exitStatus = QProcess::NormalExit; - QProcess::ProcessError error = QProcess::UnknownError; - QString errorString; - -private: - void doSerialize(QDataStream &stream) const override; - void doDeserialize(QDataStream &stream) override; -}; - -} // namespace Internal -} // namespace Utils - -Q_DECLARE_METATYPE(Utils::Internal::LauncherPacketType); diff --git a/src/libs/utils/launchersocket.cpp b/src/libs/utils/launchersocket.cpp deleted file mode 100644 index 61796a2bb3d..00000000000 --- a/src/libs/utils/launchersocket.cpp +++ /dev/null @@ -1,669 +0,0 @@ -// Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include "launchersocket.h" - -#include "algorithm.h" -#include "launcherinterface.h" -#include "qtcassert.h" -#include "utilstr.h" - -#include -#include - -namespace Utils { -namespace Internal { - -class LauncherSignal -{ -public: - CallerHandle::SignalType signalType() const { return m_signalType; } - virtual ~LauncherSignal() = default; -protected: - LauncherSignal(CallerHandle::SignalType signalType) : m_signalType(signalType) {} -private: - const CallerHandle::SignalType m_signalType; -}; - -class LauncherStartedSignal : public LauncherSignal -{ -public: - LauncherStartedSignal(int processId) - : LauncherSignal(CallerHandle::SignalType::Started) - , m_processId(processId) {} - int processId() const { return m_processId; } -private: - const int m_processId; -}; - -class LauncherReadyReadSignal : public LauncherSignal -{ -public: - LauncherReadyReadSignal(const QByteArray &stdOut, const QByteArray &stdErr) - : LauncherSignal(CallerHandle::SignalType::ReadyRead) - , m_stdOut(stdOut) - , m_stdErr(stdErr) {} - QByteArray stdOut() const { return m_stdOut; } - QByteArray stdErr() const { return m_stdErr; } - -private: - QByteArray m_stdOut; - QByteArray m_stdErr; -}; - -class LauncherDoneSignal : public LauncherSignal -{ -public: - LauncherDoneSignal(const ProcessResultData &resultData) - : LauncherSignal(CallerHandle::SignalType::Done) - , m_resultData(resultData) {} - ProcessResultData resultData() const { return m_resultData; } -private: - const ProcessResultData m_resultData; -}; - -CallerHandle::~CallerHandle() -{ - qDeleteAll(m_signals); -} - -void CallerHandle::flush() -{ - flushFor(SignalType::NoSignal); -} - -bool CallerHandle::flushFor(SignalType signalType) -{ - QTC_ASSERT(isCalledFromCallersThread(), return {}); - QList oldSignals; - { - QMutexLocker locker(&m_mutex); - const QList storedSignals = - Utils::transform(std::as_const(m_signals), [](const LauncherSignal *launcherSignal) { - return launcherSignal->signalType(); - }); - - // If we are flushing for ReadyRead or Done - flush all. - // If we are flushing for Started: - // - if Started was buffered - flush Started only. - // - otherwise if Done signal was buffered - flush all. - const bool flushAll = (signalType != SignalType::Started) - || (!storedSignals.contains(SignalType::Started) - && storedSignals.contains(SignalType::Done)); - if (flushAll) { - oldSignals = m_signals; - m_signals = {}; - } else { - auto matchingIndex = storedSignals.lastIndexOf(signalType); - if (matchingIndex >= 0) { - oldSignals = m_signals.mid(0, matchingIndex + 1); - m_signals = m_signals.mid(matchingIndex + 1); - } - } - } - bool signalMatched = false; - for (const LauncherSignal *storedSignal : std::as_const(oldSignals)) { - const SignalType storedSignalType = storedSignal->signalType(); - if (storedSignalType == signalType) - signalMatched = true; - switch (storedSignalType) { - case SignalType::NoSignal: - break; - case SignalType::Started: - handleStarted(static_cast(storedSignal)); - break; - case SignalType::ReadyRead: - handleReadyRead(static_cast(storedSignal)); - break; - case SignalType::Done: - signalMatched = true; - handleDone(static_cast(storedSignal)); - break; - } - delete storedSignal; - } - return signalMatched; -} - -// Called from caller's thread exclusively. -bool CallerHandle::shouldFlush() const -{ - QTC_ASSERT(isCalledFromCallersThread(), return false); - QMutexLocker locker(&m_mutex); - return !m_signals.isEmpty(); -} - -void CallerHandle::handleStarted(const LauncherStartedSignal *launcherSignal) -{ - QTC_ASSERT(isCalledFromCallersThread(), return); - m_processState = QProcess::Running; - m_processId = launcherSignal->processId(); - emit started(m_processId); -} - -void CallerHandle::handleReadyRead(const LauncherReadyReadSignal *launcherSignal) -{ - QTC_ASSERT(isCalledFromCallersThread(), return); - emit readyRead(launcherSignal->stdOut(), launcherSignal->stdErr()); -} - -void CallerHandle::handleDone(const LauncherDoneSignal *launcherSignal) -{ - QTC_ASSERT(isCalledFromCallersThread(), return); - m_processState = QProcess::NotRunning; - emit done(launcherSignal->resultData()); - m_processId = 0; -} - -// Called from launcher's thread exclusively. -void CallerHandle::appendSignal(LauncherSignal *newSignal) -{ - QTC_ASSERT(!isCalledFromCallersThread(), return); - QTC_ASSERT(newSignal->signalType() != SignalType::NoSignal, delete newSignal; return); - - QMutexLocker locker(&m_mutex); - QTC_ASSERT(isCalledFromLaunchersThread(), return); - m_signals.append(newSignal); -} - -QProcess::ProcessState CallerHandle::state() const -{ - return m_processState; -} - -void CallerHandle::sendControlPacket(ControlProcessPacket::SignalType signalType) -{ - if (m_processState == QProcess::NotRunning) - return; - - // TODO: In case m_processState == QProcess::Starting and the launcher socket isn't ready yet - // we might want to remove posted start packet and finish the process immediately. - // In addition, we may always try to check if correspodning start packet for the m_token - // is still awaiting and do the same (remove the packet from the stack and finish immediately). - ControlProcessPacket packet(m_token); - packet.signalType = signalType; - sendPacket(packet); -} - -void CallerHandle::terminate() -{ - QTC_ASSERT(isCalledFromCallersThread(), return); - sendControlPacket(ControlProcessPacket::SignalType::Terminate); -} - -void CallerHandle::kill() -{ - QTC_ASSERT(isCalledFromCallersThread(), return); - sendControlPacket(ControlProcessPacket::SignalType::Kill); -} - -void CallerHandle::close() -{ - QTC_ASSERT(isCalledFromCallersThread(), return); - sendControlPacket(ControlProcessPacket::SignalType::Close); -} - -void CallerHandle::closeWriteChannel() -{ - QTC_ASSERT(isCalledFromCallersThread(), return); - sendControlPacket(ControlProcessPacket::SignalType::CloseWriteChannel); -} - -qint64 CallerHandle::processId() const -{ - QTC_ASSERT(isCalledFromCallersThread(), return 0); - return m_processId; -} - -void CallerHandle::start(const QString &program, const QStringList &arguments) -{ - QTC_ASSERT(isCalledFromCallersThread(), return); - if (!m_launcherHandle || m_launcherHandle->isSocketError()) { - const QString errorString = Tr::tr("Process launcher socket error."); - const ProcessResultData result = { 0, QProcess::NormalExit, QProcess::FailedToStart, - errorString }; - emit done(result); - return; - } - - auto startWhenRunning = [&program, &oldProgram = m_command] { - qWarning() << "Trying to start" << program << "while" << oldProgram - << "is still running for the same Process instance." - << "The current call will be ignored."; - }; - QTC_ASSERT(m_processState == QProcess::NotRunning, startWhenRunning(); return); - - auto processLauncherNotStarted = [&program] { - qWarning() << "Trying to start" << program << "while process launcher wasn't started yet."; - }; - QTC_ASSERT(LauncherInterface::isStarted(), processLauncherNotStarted()); - - QMutexLocker locker(&m_mutex); - m_command = program; - m_arguments = arguments; - m_processState = QProcess::Starting; - StartProcessPacket p(m_token); - p.command = m_command; - p.arguments = m_arguments; - p.env = m_setup->m_environment.toStringList(); - if (p.env.isEmpty()) - p.env = Environment::systemEnvironment().toStringList(); - p.workingDir = m_setup->m_workingDirectory.path(); - p.processMode = m_setup->m_processMode; - p.writeData = m_setup->m_writeData; - p.processChannelMode = m_setup->m_processChannelMode; - p.standardInputFile = m_setup->m_standardInputFile; - p.belowNormalPriority = m_setup->m_belowNormalPriority; - p.nativeArguments = m_setup->m_nativeArguments; - p.lowPriority = m_setup->m_lowPriority; - p.unixTerminalDisabled = m_setup->m_unixTerminalDisabled; - p.useCtrlCStub = m_setup->m_useCtrlCStub; - p.reaperTimeout = m_setup->m_reaperTimeout.count(); - p.createConsoleOnWindows = m_setup->m_createConsoleOnWindows; - p.forceDefaultErrorMode = m_setup->m_forceDefaultErrorMode; - sendPacket(p); -} - -// Called from caller's thread exclusively. -void CallerHandle::sendPacket(const Internal::LauncherPacket &packet) -{ - LauncherInterface::sendData(packet.serialize()); -} - -qint64 CallerHandle::write(const QByteArray &data) -{ - QTC_ASSERT(isCalledFromCallersThread(), return -1); - - if (m_processState != QProcess::Running) - return -1; - - WritePacket p(m_token); - p.inputData = data; - sendPacket(p); - return data.size(); -} - -QString CallerHandle::program() const -{ - QMutexLocker locker(&m_mutex); - return m_command; -} - -QStringList CallerHandle::arguments() const -{ - QMutexLocker locker(&m_mutex); - return m_arguments; -} - -void CallerHandle::setProcessSetupData(ProcessSetupData *setup) -{ - QTC_ASSERT(isCalledFromCallersThread(), return); - m_setup = setup; -} - -bool CallerHandle::waitForSignal(SignalType signalType, QDeadlineTimer timeout) -{ - QTC_ASSERT(isCalledFromCallersThread(), return false); - QTC_ASSERT(m_launcherHandle, return false); - return m_launcherHandle->waitForSignal(signalType, timeout); -} - -// Called from caller's or launcher's thread. -bool CallerHandle::isCalledFromCallersThread() const -{ - return QThread::currentThread() == thread(); -} - -// Called from caller's or launcher's thread. Call me with mutex locked. -bool CallerHandle::isCalledFromLaunchersThread() const -{ - if (!m_launcherHandle) - return false; - return QThread::currentThread() == m_launcherHandle->thread(); -} - -// Called from caller's thread exclusively. -bool LauncherHandle::waitForSignal(CallerHandle::SignalType newSignal, QDeadlineTimer timeout) -{ - QTC_ASSERT(!isCalledFromLaunchersThread(), return false); - while (true) { - if (timeout.hasExpired()) - break; - if (!doWaitForSignal(timeout)) - break; - // Matching (or Done) signal was flushed - if (m_callerHandle->flushFor(newSignal)) - return true; - // Otherwise continue awaiting (e.g. when ReadyRead came while waitForFinished()) - } - return false; -} - -// Called from caller's thread exclusively. -bool LauncherHandle::doWaitForSignal(QDeadlineTimer deadline) -{ - QMutexLocker locker(&m_mutex); - QTC_ASSERT(isCalledFromCallersThread(), return false); - - // Flush, if we have any stored signals. - // This must be called when holding laucher's mutex locked prior to the call to wait, - // so that it's done atomically. - if (m_callerHandle->shouldFlush()) - return true; - - return m_waitCondition.wait(&m_mutex, deadline); -} - -// Called from launcher's thread exclusively. Call me with mutex locked. -void LauncherHandle::flushCaller() -{ - QTC_ASSERT(isCalledFromLaunchersThread(), return); - if (!m_callerHandle) - return; - - m_waitCondition.wakeOne(); - - // call in callers thread - QMetaObject::invokeMethod(m_callerHandle, &CallerHandle::flush); -} - -void LauncherHandle::handlePacket(LauncherPacketType type, const QByteArray &payload) -{ - QTC_ASSERT(isCalledFromLaunchersThread(), return); - switch (type) { - case LauncherPacketType::ProcessStarted: - handleStartedPacket(payload); - break; - case LauncherPacketType::ReadyReadStandardOutput: - handleReadyReadStandardOutput(payload); - break; - case LauncherPacketType::ReadyReadStandardError: - handleReadyReadStandardError(payload); - break; - case LauncherPacketType::ProcessDone: - handleDonePacket(payload); - break; - default: - QTC_ASSERT(false, break); - } -} - -void LauncherHandle::handleStartedPacket(const QByteArray &packetData) -{ - QTC_ASSERT(isCalledFromLaunchersThread(), return); - QMutexLocker locker(&m_mutex); - if (!m_callerHandle) - return; - - const auto packet = LauncherPacket::extractPacket(m_token, packetData); - m_callerHandle->appendSignal(new LauncherStartedSignal(packet.processId)); - flushCaller(); -} - -void LauncherHandle::handleReadyReadStandardOutput(const QByteArray &packetData) -{ - QTC_ASSERT(isCalledFromLaunchersThread(), return); - QMutexLocker locker(&m_mutex); - if (!m_callerHandle) - return; - - const auto packet = LauncherPacket::extractPacket(m_token, packetData); - if (packet.standardChannel.isEmpty()) - return; - - m_callerHandle->appendSignal(new LauncherReadyReadSignal(packet.standardChannel, {})); - flushCaller(); -} - -void LauncherHandle::handleReadyReadStandardError(const QByteArray &packetData) -{ - QTC_ASSERT(isCalledFromLaunchersThread(), return); - QMutexLocker locker(&m_mutex); - if (!m_callerHandle) - return; - - const auto packet = LauncherPacket::extractPacket(m_token, packetData); - if (packet.standardChannel.isEmpty()) - return; - - m_callerHandle->appendSignal(new LauncherReadyReadSignal({}, packet.standardChannel)); - flushCaller(); -} - -void LauncherHandle::handleDonePacket(const QByteArray &packetData) -{ - QTC_ASSERT(isCalledFromLaunchersThread(), return); - QMutexLocker locker(&m_mutex); - if (!m_callerHandle) - return; - - const auto packet = LauncherPacket::extractPacket(m_token, packetData); - const QByteArray stdOut = packet.stdOut; - const QByteArray stdErr = packet.stdErr; - const ProcessResultData result = { packet.exitCode, packet.exitStatus, - packet.error, packet.errorString }; - - if (!stdOut.isEmpty() || !stdErr.isEmpty()) - m_callerHandle->appendSignal(new LauncherReadyReadSignal(stdOut, stdErr)); - m_callerHandle->appendSignal(new LauncherDoneSignal(result)); - flushCaller(); -} - -void LauncherHandle::handleSocketError(const QString &message) -{ - QTC_ASSERT(isCalledFromLaunchersThread(), return); - m_socketError = true; // TODO: ??? - QMutexLocker locker(&m_mutex); - if (!m_callerHandle) - return; - - // TODO: FailedToStart may be wrong in case process has already started - const QString errorString = Tr::tr("Internal socket error: %1").arg(message); - const ProcessResultData result = { 0, QProcess::NormalExit, QProcess::FailedToStart, - errorString }; - m_callerHandle->appendSignal(new LauncherDoneSignal(result)); - flushCaller(); -} - -bool LauncherHandle::isCalledFromLaunchersThread() const -{ - return QThread::currentThread() == thread(); -} - -// call me with mutex locked -bool LauncherHandle::isCalledFromCallersThread() const -{ - if (!m_callerHandle) - return false; - return QThread::currentThread() == m_callerHandle->thread(); -} - -LauncherSocket::LauncherSocket(QObject *parent) : QObject(parent) -{ - qRegisterMetaType(); - qRegisterMetaType("quintptr"); -} - -LauncherSocket::~LauncherSocket() -{ - QMutexLocker locker(&m_mutex); - auto displayHandles = [&handles = m_handles] { - qWarning() << "Destroying process launcher while" << handles.count() - << "processes are still alive. The following processes are still alive:"; - for (LauncherHandle *handle : handles) { - CallerHandle *callerHandle = handle->callerHandle(); - if (callerHandle->state() != QProcess::NotRunning) { - qWarning() << " " << callerHandle->program() << callerHandle->arguments() - << "in thread" << (void *)callerHandle->thread(); - } else { - qWarning() << " Not running process in thread" << (void *)callerHandle->thread(); - } - } - }; - QTC_ASSERT(m_handles.isEmpty(), displayHandles()); -} - -void LauncherSocket::sendData(const QByteArray &data) -{ - auto storeRequest = [this](const QByteArray &data) - { - QMutexLocker locker(&m_mutex); - m_requests.push_back(data); - return m_requests.size() == 1; // Returns true if requests handling should be triggered. - }; - - if (storeRequest(data)) // Call handleRequests() in launcher's thread. - QMetaObject::invokeMethod(this, &LauncherSocket::handleRequests); -} - -CallerHandle *LauncherSocket::registerHandle(QObject *parent, quintptr token) -{ - QTC_ASSERT(!isCalledFromLaunchersThread(), return nullptr); - QMutexLocker locker(&m_mutex); - if (m_handles.contains(token)) - return nullptr; // TODO: issue a warning - - CallerHandle *callerHandle = new CallerHandle(parent, token); - LauncherHandle *launcherHandle = new LauncherHandle(token); - callerHandle->setLauncherHandle(launcherHandle); - launcherHandle->setCallerHandle(callerHandle); - launcherHandle->moveToThread(thread()); - // Call it after moving LauncherHandle to the launcher's thread. - // Since this method is invoked from caller's thread, CallerHandle will live in caller's thread. - m_handles.insert(token, launcherHandle); - connect(this, &LauncherSocket::errorOccurred, - launcherHandle, &LauncherHandle::handleSocketError); - - return callerHandle; -} - -void LauncherSocket::unregisterHandle(quintptr token) -{ - QTC_ASSERT(!isCalledFromLaunchersThread(), return); - QMutexLocker locker(&m_mutex); - auto it = m_handles.constFind(token); - if (it == m_handles.constEnd()) - return; // TODO: issue a warning - - LauncherHandle *launcherHandle = it.value(); - CallerHandle *callerHandle = launcherHandle->callerHandle(); - launcherHandle->setCallerHandle(nullptr); - callerHandle->setLauncherHandle(nullptr); - launcherHandle->deleteLater(); - callerHandle->deleteLater(); - m_handles.erase(it); -} - -LauncherHandle *LauncherSocket::handleForToken(quintptr token) const -{ - QTC_ASSERT(isCalledFromLaunchersThread(), return nullptr); - QMutexLocker locker(&m_mutex); - return m_handles.value(token); -} - -void LauncherSocket::setSocket(QLocalSocket *socket) -{ - QTC_ASSERT(isCalledFromLaunchersThread(), return); - QTC_ASSERT(!m_socket, return); - m_socket.store(socket); - m_packetParser.setDevice(m_socket); - connect(m_socket, &QLocalSocket::errorOccurred, - this, &LauncherSocket::handleSocketError); - connect(m_socket, &QLocalSocket::readyRead, - this, &LauncherSocket::handleSocketDataAvailable); - connect(m_socket, &QLocalSocket::disconnected, - this, &LauncherSocket::handleSocketDisconnected); - handleRequests(); -} - -void LauncherSocket::shutdown() -{ - QTC_ASSERT(isCalledFromLaunchersThread(), return); - const auto socket = m_socket.exchange(nullptr); - if (!socket) - return; - socket->disconnect(); - socket->write(ShutdownPacket().serialize()); - socket->waitForBytesWritten(1000); - socket->deleteLater(); // or schedule a queued call to delete later? -} - -void LauncherSocket::handleSocketError() -{ - QTC_ASSERT(isCalledFromLaunchersThread(), return); - auto socket = m_socket.load(); - if (socket->error() != QLocalSocket::PeerClosedError) - handleError(Tr::tr("Socket error: %1").arg(socket->errorString())); -} - -void LauncherSocket::handleSocketDataAvailable() -{ - QTC_ASSERT(isCalledFromLaunchersThread(), return); - try { - if (!m_packetParser.parse()) - return; - } catch (const PacketParser::InvalidPacketSizeException &e) { - handleError(Tr::tr("Internal protocol error: invalid packet size %1.").arg(e.size)); - return; - } - LauncherHandle *handle = handleForToken(m_packetParser.token()); - if (handle) { - switch (m_packetParser.type()) { - case LauncherPacketType::ProcessStarted: - case LauncherPacketType::ReadyReadStandardOutput: - case LauncherPacketType::ReadyReadStandardError: - case LauncherPacketType::ProcessDone: - handle->handlePacket(m_packetParser.type(), m_packetParser.packetData()); - break; - default: - handleError(Tr::tr("Internal protocol error: invalid packet type %1.") - .arg(static_cast(m_packetParser.type()))); - return; - } - } else { -// qDebug() << "No handler for token" << m_packetParser.token() << m_handles; - // in this case the Process was canceled and deleted - } - handleSocketDataAvailable(); -} - -void LauncherSocket::handleSocketDisconnected() -{ - QTC_ASSERT(isCalledFromLaunchersThread(), return); - handleError(Tr::tr("Launcher socket closed unexpectedly.")); -} - -void LauncherSocket::handleError(const QString &error) -{ - QTC_ASSERT(isCalledFromLaunchersThread(), return); - const auto socket = m_socket.exchange(nullptr); - socket->disconnect(); - socket->deleteLater(); - emit errorOccurred(error); -} - -void LauncherSocket::handleRequests() -{ - QTC_ASSERT(isCalledFromLaunchersThread(), return); - const auto socket = m_socket.load(); - if (!socket) - return; - - std::vector requests; - { - QMutexLocker locker(&m_mutex); - requests = m_requests; - m_requests.clear(); - } - - for (const QByteArray &request : std::as_const(requests)) - socket->write(request); -} - -bool LauncherSocket::isCalledFromLaunchersThread() const -{ - return QThread::currentThread() == thread(); -} - -} // namespace Internal -} // namespace Utils diff --git a/src/libs/utils/launchersocket.h b/src/libs/utils/launchersocket.h deleted file mode 100644 index 9f906d1dcc4..00000000000 --- a/src/libs/utils/launchersocket.h +++ /dev/null @@ -1,214 +0,0 @@ -// Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include "launcherpackets.h" -#include "processinterface.h" - -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -QT_BEGIN_NAMESPACE -class QLocalSocket; -QT_END_NAMESPACE - -namespace Utils { -namespace Internal { - -class LauncherInterfacePrivate; -class LauncherHandle; -class LauncherSignal; -class LauncherStartedSignal; -class LauncherReadyReadSignal; -class LauncherDoneSignal; - -// All the methods and data fields in this class are called / accessed from the caller's thread. -// Exceptions are explicitly marked. -class CallerHandle : public QObject -{ - Q_OBJECT -public: - enum class SignalType { - NoSignal, - Started, - ReadyRead, - Done - }; - Q_ENUM(SignalType) - CallerHandle(QObject *parent, quintptr token) - : QObject(parent), m_token(token) {} - ~CallerHandle() override; - - LauncherHandle *launcherHandle() const { return m_launcherHandle; } - void setLauncherHandle(LauncherHandle *handle) { QMutexLocker locker(&m_mutex); m_launcherHandle = handle; } - - bool waitForSignal(CallerHandle::SignalType signalType, QDeadlineTimer timeout); - - // Returns the list of flushed signals. - void flush(); - bool flushFor(SignalType signalType); - bool shouldFlush() const; - // Called from launcher's thread exclusively. - void appendSignal(LauncherSignal *launcherSignal); - - // Called from caller's or launcher's thread. - QProcess::ProcessState state() const; - void sendControlPacket(ControlProcessPacket::SignalType signalType); - void terminate(); - void kill(); - void close(); - void closeWriteChannel(); - - qint64 processId() const; - - void start(const QString &program, const QStringList &arguments); - - qint64 write(const QByteArray &data); - - // Called from caller's or launcher's thread. - QString program() const; - // Called from caller's or launcher's thread. - QStringList arguments() const; - void setProcessSetupData(ProcessSetupData *setup); - -signals: - void started(qint64 processId, qint64 applicationMainThreadId = 0); - void readyRead(const QByteArray &outputData, const QByteArray &errorData); - void done(const Utils::ProcessResultData &resultData); - -private: - // Called from caller's thread exclusively. - void sendPacket(const Internal::LauncherPacket &packet); - // Called from caller's or launcher's thread. - bool isCalledFromCallersThread() const; - // Called from caller's or launcher's thread. Call me with mutex locked. - bool isCalledFromLaunchersThread() const; - - QByteArray readAndClear(QByteArray &data) const - { - const QByteArray tmp = data; - data.clear(); - return tmp; - } - - void handleStarted(const LauncherStartedSignal *launcherSignal); - void handleReadyRead(const LauncherReadyReadSignal *launcherSignal); - void handleDone(const LauncherDoneSignal *launcherSignal); - - // Lives in launcher's thread. Modified from caller's thread. - LauncherHandle *m_launcherHandle = nullptr; - - mutable QMutex m_mutex; - // Accessed from caller's and launcher's thread - QList m_signals; - - const quintptr m_token; - - // Modified from caller's thread, read from launcher's thread - std::atomic m_processState = QProcess::NotRunning; - int m_processId = 0; - - QString m_command; - QStringList m_arguments; - ProcessSetupData *m_setup = nullptr; -}; - -// Moved to the launcher thread, returned to caller's thread. -// It's assumed that this object will be alive at least -// as long as the corresponding Process is alive. - -class LauncherHandle : public QObject -{ -public: - // Called from caller's thread, moved to launcher's thread afterwards. - LauncherHandle(quintptr token) : m_token(token) {} - // Called from caller's thread exclusively. - bool waitForSignal(CallerHandle::SignalType newSignal, QDeadlineTimer timeout); - CallerHandle *callerHandle() const { return m_callerHandle; } - void setCallerHandle(CallerHandle *handle) { QMutexLocker locker(&m_mutex); m_callerHandle = handle; } - - // Called from launcher's thread exclusively. - void handleSocketError(const QString &message); - void handlePacket(LauncherPacketType type, const QByteArray &payload); - - // Called from caller's thread exclusively. - bool isSocketError() const { return m_socketError; } - -private: - // Called from caller's thread exclusively. - bool doWaitForSignal(QDeadlineTimer deadline); - // Called from launcher's thread exclusively. Call me with mutex locked. - void flushCaller(); - // Called from launcher's thread exclusively. - void handleStartedPacket(const QByteArray &packetData); - void handleReadyReadStandardOutput(const QByteArray &packetData); - void handleReadyReadStandardError(const QByteArray &packetData); - void handleDonePacket(const QByteArray &packetData); - - // Called from caller's or launcher's thread. - bool isCalledFromLaunchersThread() const; - bool isCalledFromCallersThread() const; - - // Lives in caller's thread. Modified only in caller's thread. TODO: check usages - all should be with mutex - CallerHandle *m_callerHandle = nullptr; - - mutable QMutex m_mutex; - QWaitCondition m_waitCondition; - const quintptr m_token; - std::atomic_bool m_socketError = false; -}; - -class LauncherSocket : public QObject -{ - Q_OBJECT - friend class LauncherInterfacePrivate; -public: - // Called from caller's thread exclusively. - void sendData(const QByteArray &data); - CallerHandle *registerHandle(QObject *parent, quintptr token); - void unregisterHandle(quintptr token); - -signals: - void errorOccurred(const QString &error); - -private: - // Called from caller's thread, moved to launcher's thread. - LauncherSocket(QObject *parent = nullptr); - // Called from launcher's thread exclusively. - ~LauncherSocket() override; - - // Called from launcher's thread exclusively. - LauncherHandle *handleForToken(quintptr token) const; - - // Called from launcher's thread exclusively. - void setSocket(QLocalSocket *socket); - void shutdown(); - - // Called from launcher's thread exclusively. - void handleSocketError(); - void handleSocketDataAvailable(); - void handleSocketDisconnected(); - void handleError(const QString &error); - void handleRequests(); - - // Called from caller's or launcher's thread. - bool isCalledFromLaunchersThread() const; - - std::atomic m_socket{nullptr}; - PacketParser m_packetParser; - std::vector m_requests; - mutable QMutex m_mutex; - QHash m_handles; -}; - -} // namespace Internal -} // namespace Utils diff --git a/src/libs/utils/processenums.h b/src/libs/utils/processenums.h index e19321fcd04..7990ca2a123 100644 --- a/src/libs/utils/processenums.h +++ b/src/libs/utils/processenums.h @@ -15,13 +15,6 @@ enum class ProcessMode { Writer // This opens in ReadWrite mode and doesn't close the write channel }; -enum class ProcessImpl { - QProcess, - ProcessLauncher, - Default // Defaults to ProcessLauncherImpl, if QTC_USE_QPROCESS env var is set - // it equals to QProcessImpl. -}; - enum class TerminalMode { Off, Run, // Start with process stub enabled diff --git a/src/libs/utils/processinterface.h b/src/libs/utils/processinterface.h index 7cea1420182..217ade70a8e 100644 --- a/src/libs/utils/processinterface.h +++ b/src/libs/utils/processinterface.h @@ -71,7 +71,6 @@ public: class QTCREATOR_UTILS_EXPORT ProcessSetupData { public: - ProcessImpl m_processImpl = ProcessImpl::Default; ProcessMode m_processMode = ProcessMode::Reader; TerminalMode m_terminalMode = TerminalMode::Off; @@ -165,8 +164,6 @@ private: // It's being called in Starting or Running state. virtual void sendControlSignal(ControlSignal controlSignal) = 0; - virtual ProcessBlockingInterface *processBlockingInterface() const { return nullptr; } - friend class Process; friend class Internal::ProcessPrivate; }; diff --git a/src/libs/utils/qtcprocess.cpp b/src/libs/utils/qtcprocess.cpp index fcd5a465bdb..239bad5fa63 100644 --- a/src/libs/utils/qtcprocess.cpp +++ b/src/libs/utils/qtcprocess.cpp @@ -7,9 +7,8 @@ #include "environment.h" #include "guard.h" #include "hostosinfo.h" -#include "launcherinterface.h" -#include "launchersocket.h" #include "processhelper.h" +#include "processinterface.h" #include "processreaper.h" #include "stringutils.h" #include "terminalhooks.h" @@ -28,6 +27,7 @@ #include #include #include +#include #ifdef QT_GUI_LIB // qmlpuppet does not use that. @@ -40,7 +40,6 @@ #include #include #include -#include #include using namespace Utils::Internal; @@ -300,38 +299,6 @@ bool DefaultImpl::ensureProgramExists(const QString &program) return false; } -// TODO: Remove QProcessBlockingImpl later, after Creator 13.0 is released at least. - -// Rationale: QProcess::waitForReadyRead() waits only for one channel, either stdOut or stdErr. -// Since we can't predict where the data will come first, -// setting the QProcess::setReadChannel() in advance is a mis-design of the QProcess API. -// This issue does not affect GeneralProcessBlockingImpl, but it might be not as optimal -// as QProcessBlockingImpl. However, since we are blocking the caller thread anyway, -// the small overhead in speed doesn't play the most significant role, thus the proper -// behavior of Process::waitForReadyRead(), which listens to both channels, wins. - -// class QProcessBlockingImpl : public ProcessBlockingInterface -// { -// public: -// QProcessBlockingImpl(QProcess *process) : m_process(process) {} - -// private: -// bool waitForSignal(ProcessSignalType signalType, int msecs) final -// { -// switch (signalType) { -// case ProcessSignalType::Started: -// return m_process->waitForStarted(msecs); -// case ProcessSignalType::ReadyRead: -// return m_process->waitForReadyRead(msecs); -// case ProcessSignalType::Done: -// return m_process->waitForFinished(msecs); -// } -// return false; -// } - -// QProcess *m_process = nullptr; -// }; - class PtyProcessImpl final : public DefaultImpl { public: @@ -555,113 +522,6 @@ private: // QProcessBlockingImpl *m_blockingImpl = nullptr; }; -static uint uniqueToken() -{ - static std::atomic_uint globalUniqueToken = 0; - return ++globalUniqueToken; -} - -class ProcessLauncherBlockingImpl : public ProcessBlockingInterface -{ -public: - ProcessLauncherBlockingImpl(CallerHandle *caller) : m_caller(caller) {} - -private: - bool waitForSignal(ProcessSignalType signalType, QDeadlineTimer timeout) final - { - // TODO: Remove CallerHandle::SignalType - const CallerHandle::SignalType type = [signalType] { - switch (signalType) { - case ProcessSignalType::Started: - return CallerHandle::SignalType::Started; - case ProcessSignalType::ReadyRead: - return CallerHandle::SignalType::ReadyRead; - case ProcessSignalType::Done: - return CallerHandle::SignalType::Done; - } - QTC_CHECK(false); - return CallerHandle::SignalType::NoSignal; - }(); - return m_caller->waitForSignal(type, timeout); - } - - CallerHandle *m_caller = nullptr; -}; - -class ProcessLauncherImpl final : public DefaultImpl -{ - Q_OBJECT -public: - ProcessLauncherImpl() : m_token(uniqueToken()) - { - m_handle = LauncherInterface::registerHandle(this, token()); - m_handle->setProcessSetupData(&m_setup); - connect(m_handle, &CallerHandle::started, - this, &ProcessInterface::started); - connect(m_handle, &CallerHandle::readyRead, - this, &ProcessInterface::readyRead); - connect(m_handle, &CallerHandle::done, - this, &ProcessInterface::done); - m_blockingImpl = new ProcessLauncherBlockingImpl(m_handle); - } - ~ProcessLauncherImpl() final - { - m_handle->close(); - LauncherInterface::unregisterHandle(token()); - m_handle = nullptr; - } - -private: - qint64 write(const QByteArray &data) final { return m_handle->write(data); } - void sendControlSignal(ControlSignal controlSignal) final { - switch (controlSignal) { - case ControlSignal::Terminate: - m_handle->terminate(); - break; - case ControlSignal::Kill: - m_handle->kill(); - break; - case ControlSignal::Interrupt: - ProcessHelper::interruptPid(m_handle->processId()); - break; - case ControlSignal::KickOff: - QTC_CHECK(false); - break; - case ControlSignal::CloseWriteChannel: - m_handle->closeWriteChannel(); - break; - } - } - - ProcessBlockingInterface *processBlockingInterface() const override { return m_blockingImpl; } - - void doDefaultStart(const QString &program, const QStringList &arguments) final - { - m_handle->start(program, arguments); - } - - quintptr token() const { return m_token; } - - const uint m_token = 0; - // Lives in caller's thread. - CallerHandle *m_handle = nullptr; - ProcessLauncherBlockingImpl *m_blockingImpl = nullptr; -}; - -static ProcessImpl defaultProcessImplHelper() -{ - const QString value = qtcEnvironmentVariable("QTC_USE_QPROCESS", "TRUE").toUpper(); - if (value != "FALSE" && value != "0") - return ProcessImpl::QProcess; - return ProcessImpl::ProcessLauncher; -} - -static ProcessImpl defaultProcessImpl() -{ - static const ProcessImpl impl = defaultProcessImplHelper(); - return impl; -} - class ProcessInterfaceSignal { public: @@ -799,12 +659,7 @@ public: return new PtyProcessImpl; if (m_setup.m_terminalMode != TerminalMode::Off) return Terminal::Hooks::instance().createTerminalProcessInterface(); - - const ProcessImpl impl = m_setup.m_processImpl == ProcessImpl::Default - ? defaultProcessImpl() : m_setup.m_processImpl; - if (impl == ProcessImpl::QProcess) - return new QProcessImpl; - return new ProcessLauncherImpl; + return new QProcessImpl; } void setProcessInterface(ProcessInterface *process) @@ -820,9 +675,7 @@ public: connect(m_process.get(), &ProcessInterface::done, this, &ProcessPrivate::handleDone); - m_blockingInterface.reset(process->processBlockingInterface()); - if (!m_blockingInterface) - m_blockingInterface.reset(new GeneralProcessBlockingImpl(this)); + m_blockingInterface.reset(new GeneralProcessBlockingImpl(this)); m_blockingInterface->setParent(this); } @@ -1157,11 +1010,6 @@ Process::~Process() delete d; } -void Process::setProcessImpl(ProcessImpl processImpl) -{ - d->m_setup.m_processImpl = processImpl; -} - void Process::setPtyData(const std::optional &data) { d->m_setup.m_ptyData = data; @@ -2200,5 +2048,3 @@ void ProcessTaskAdapter::start() } } // namespace Utils - -#include "qtcprocess.moc" diff --git a/src/libs/utils/qtcprocess.h b/src/libs/utils/qtcprocess.h index 65ca569d559..1de06a359b4 100644 --- a/src/libs/utils/qtcprocess.h +++ b/src/libs/utils/qtcprocess.h @@ -91,8 +91,6 @@ public: void setControlEnvironment(const Environment &env); // Possible helper process (ssh on host etc) const Environment &controlEnvironment() const; - void setProcessImpl(ProcessImpl processImpl); - void setPtyData(const std::optional &data); std::optional ptyData() const; diff --git a/src/libs/utils/utils.qbs b/src/libs/utils/utils.qbs index 0bba92bf04d..8f49a8486c3 100644 --- a/src/libs/utils/utils.qbs +++ b/src/libs/utils/utils.qbs @@ -177,12 +177,6 @@ QtcLibrary { "itemviews.h", "jsontreeitem.cpp", "jsontreeitem.h", - "launcherinterface.cpp", - "launcherinterface.h", - "launcherpackets.cpp", - "launcherpackets.h", - "launchersocket.cpp", - "launchersocket.h", "layoutbuilder.cpp", "layoutbuilder.h", "link.cpp", diff --git a/src/plugins/docker/dockerdevice.cpp b/src/plugins/docker/dockerdevice.cpp index f5ae6600b9a..dcb55c868a3 100644 --- a/src/plugins/docker/dockerdevice.cpp +++ b/src/plugins/docker/dockerdevice.cpp @@ -400,7 +400,6 @@ DockerProcessImpl::~DockerProcessImpl() void DockerProcessImpl::start() { - m_process.setProcessImpl(m_setup.m_processImpl); m_process.setProcessMode(m_setup.m_processMode); m_process.setTerminalMode(m_setup.m_terminalMode); m_process.setPtyData(m_setup.m_ptyData); diff --git a/src/plugins/mesonprojectmanager/tests/testmesoninfoparser.cpp b/src/plugins/mesonprojectmanager/tests/testmesoninfoparser.cpp index 88325392116..d9e29e33164 100644 --- a/src/plugins/mesonprojectmanager/tests/testmesoninfoparser.cpp +++ b/src/plugins/mesonprojectmanager/tests/testmesoninfoparser.cpp @@ -4,7 +4,6 @@ #include "../mesoninfoparser.h" #include "../mesontools.h" -#include #include #include #include @@ -40,8 +39,6 @@ private slots: { Utils::TemporaryDirectory::setMasterTemporaryDirectory(QDir::tempPath() + "/mesontest-XXXXXX"); - Utils::LauncherInterface::setPathToLauncher(qApp->applicationDirPath() + '/' - + QLatin1String(TEST_RELATIVE_LIBEXEC_PATH)); const auto path = findTool(ToolType::Meson); if (!path) diff --git a/src/plugins/mesonprojectmanager/tests/testmesonwrapper.cpp b/src/plugins/mesonprojectmanager/tests/testmesonwrapper.cpp index 7fc13e1d49e..1e049be7f16 100644 --- a/src/plugins/mesonprojectmanager/tests/testmesonwrapper.cpp +++ b/src/plugins/mesonprojectmanager/tests/testmesonwrapper.cpp @@ -3,7 +3,6 @@ #include "../mesontools.h" -#include #include #include #include @@ -28,8 +27,6 @@ private slots: { Utils::TemporaryDirectory::setMasterTemporaryDirectory(QDir::tempPath() + "/mesontest-XXXXXX"); - Utils::LauncherInterface::setPathToLauncher(qApp->applicationDirPath() + '/' - + QLatin1String(TEST_RELATIVE_LIBEXEC_PATH)); const auto path = findTool(ToolType::Meson); if (!path) diff --git a/src/plugins/remotelinux/linuxdevice.cpp b/src/plugins/remotelinux/linuxdevice.cpp index b52b3cb36d3..e8d9a1b80f8 100644 --- a/src/plugins/remotelinux/linuxdevice.cpp +++ b/src/plugins/remotelinux/linuxdevice.cpp @@ -674,7 +674,6 @@ void SshProcessInterfacePrivate::start() cmd.addArg(QString("%1:localhost:%1").arg(forwardPort)); } - m_process.setProcessImpl(q->m_setup.m_processImpl); m_process.setProcessMode(q->m_setup.m_processMode); m_process.setTerminalMode(q->m_setup.m_terminalMode); m_process.setPtyData(q->m_setup.m_ptyData); @@ -743,7 +742,6 @@ void SshProcessInterfacePrivate::clearForStart() void SshProcessInterfacePrivate::doStart() { - m_process.setProcessImpl(q->m_setup.m_processImpl); m_process.setProcessMode(q->m_setup.m_processMode); m_process.setTerminalMode(q->m_setup.m_terminalMode); m_process.setPtyData(q->m_setup.m_ptyData); diff --git a/src/tools/CMakeLists.txt b/src/tools/CMakeLists.txt index 576c626aaea..2d92b0d0974 100644 --- a/src/tools/CMakeLists.txt +++ b/src/tools/CMakeLists.txt @@ -26,7 +26,6 @@ if (APPLE) add_subdirectory(iostool) endif() -add_subdirectory(processlauncher) if (WITH_QMLDESIGNER) add_subdirectory(qml2puppet) add_subdirectory(sqlitetester) diff --git a/src/tools/processlauncher/CMakeLists.txt b/src/tools/processlauncher/CMakeLists.txt deleted file mode 100644 index d866bc9a8d4..00000000000 --- a/src/tools/processlauncher/CMakeLists.txt +++ /dev/null @@ -1,35 +0,0 @@ -set(LIBSDIR "${PROJECT_SOURCE_DIR}/src/libs") -set(UTILSDIR "${PROJECT_SOURCE_DIR}/src/libs/utils") - -add_qtc_executable(qtcreator_processlauncher - INCLUDES "${LIBSDIR}" - DEPENDS Qt::Core Qt::Network - DEFINES UTILS_STATIC_LIBRARY - SOURCES - launcherlogging.cpp - launcherlogging.h - launchersockethandler.cpp - launchersockethandler.h - processlauncher-main.cpp - ${UTILSDIR}/launcherpackets.cpp - ${UTILSDIR}/launcherpackets.h - ${UTILSDIR}/processenums.h - ${UTILSDIR}/processhelper.cpp - ${UTILSDIR}/processhelper.h - ${UTILSDIR}/processreaper.cpp - ${UTILSDIR}/processreaper.h - ${UTILSDIR}/qtcassert.cpp - ${UTILSDIR}/qtcassert.h - ${UTILSDIR}/singleton.cpp - ${UTILSDIR}/singleton.h - ${UTILSDIR}/threadutils.cpp - ${UTILSDIR}/threadutils.h - ) - -if (MSVC) - find_library(DbgHelpLib dbghelp) -endif() - -extend_qtc_executable(qtcreator_processlauncher CONDITION MSVC - DEPENDS ${DbgHelpLib} -) diff --git a/src/tools/processlauncher/launcherlogging.cpp b/src/tools/processlauncher/launcherlogging.cpp deleted file mode 100644 index 1a7dd50e323..00000000000 --- a/src/tools/processlauncher/launcherlogging.cpp +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include "launcherlogging.h" - -namespace Utils { -namespace Internal { -Q_LOGGING_CATEGORY(launcherLog, "qtc.utils.launcher", QtWarningMsg) -} -} diff --git a/src/tools/processlauncher/launcherlogging.h b/src/tools/processlauncher/launcherlogging.h deleted file mode 100644 index 25d14978855..00000000000 --- a/src/tools/processlauncher/launcherlogging.h +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include -#include - -namespace Utils { -namespace Internal { -Q_DECLARE_LOGGING_CATEGORY(launcherLog) -template void logDebug(const T &msg) { qCDebug(launcherLog) << msg; } -template void logWarn(const T &msg) { qCWarning(launcherLog) << msg; } -template void logError(const T &msg) { qCCritical(launcherLog) << msg; } -} -} diff --git a/src/tools/processlauncher/launchersockethandler.cpp b/src/tools/processlauncher/launchersockethandler.cpp deleted file mode 100644 index 53ef9171295..00000000000 --- a/src/tools/processlauncher/launchersockethandler.cpp +++ /dev/null @@ -1,284 +0,0 @@ -// Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include "launchersockethandler.h" -#include "launcherlogging.h" - -#include -#include - -#include -#include -#include - -namespace Utils { -namespace Internal { - -class ProcessWithToken : public ProcessHelper -{ - Q_OBJECT -public: - ProcessWithToken(quintptr token, QObject *parent = nullptr) : - ProcessHelper(parent), m_token(token) { } - - quintptr token() const { return m_token; } - void setReaperTimeout(int msecs) { m_reaperTimeout = msecs; }; - int reaperTimeout() const { return m_reaperTimeout; } - -private: - const quintptr m_token; - int m_reaperTimeout = 500; -}; - -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() -{ - for (auto it = m_processes.cbegin(); it != m_processes.cend(); ++it) { - ProcessWithToken *p = it.value(); - if (p->state() != QProcess::NotRunning) - logWarn(QStringLiteral("Shutting down while process %1 is running").arg(p->program())); - ProcessReaper::reap(p); - } - - m_socket->disconnect(); - m_socket->disconnectFromServer(); - if (m_socket->state() != QLocalSocket::UnconnectedState - && !m_socket->waitForDisconnected()) { - logWarn("Could not disconnect from server"); - m_socket->close(); - } -} - -void LauncherSocketHandler::start() -{ - connect(m_socket, &QLocalSocket::disconnected, - this, &LauncherSocketHandler::handleSocketClosed); - connect(m_socket, &QLocalSocket::readyRead, this, &LauncherSocketHandler::handleSocketData); - connect(m_socket, - &QLocalSocket::errorOccurred, - 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::WriteIntoProcess: - handleWritePacket(); - break; - case LauncherPacketType::ControlProcess: - handleControlPacket(); - break; - case LauncherPacketType::Shutdown: - handleShutdownPacket(); - return; - default: - logWarn(QStringLiteral("Internal protocol error: Invalid packet type %1") - .arg(static_cast(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() -{ - logWarn("The connection has closed unexpectedly, shutting down"); - m_socket->disconnect(); - qApp->quit(); -} - -void LauncherSocketHandler::handleProcessError(ProcessWithToken *process) -{ - // In case of FailedToStart we won't receive finished signal, so we send the error - // packet and remove the process here and now. For all other errors we should expect - // corresponding finished signal to appear, so we will send the error data together with - // the finished packet later on. - if (process->error() == QProcess::FailedToStart) - handleProcessFinished(process); -} - -void LauncherSocketHandler::handleProcessStarted(ProcessWithToken *process) -{ - ProcessStartedPacket packet(process->token()); - packet.processId = process->processId(); - process->processStartHandler()->handleProcessStarted(); - sendPacket(packet); -} - -void LauncherSocketHandler::handleReadyReadStandardOutput(ProcessWithToken *process) -{ - ReadyReadStandardOutputPacket packet(process->token()); - packet.standardChannel = process->readAllStandardOutput(); - sendPacket(packet); -} - -void LauncherSocketHandler::handleReadyReadStandardError(ProcessWithToken *process) -{ - ReadyReadStandardErrorPacket packet(process->token()); - packet.standardChannel = process->readAllStandardError(); - sendPacket(packet); -} - -void LauncherSocketHandler::handleProcessFinished(ProcessWithToken *process) -{ - ProcessDonePacket packet(process->token()); - packet.exitCode = process->exitCode(); - packet.exitStatus = process->exitStatus(); - packet.error = process->error(); - packet.errorString = process->errorString(); - if (process->processChannelMode() != QProcess::MergedChannels) - packet.stdErr = process->readAllStandardError(); - packet.stdOut = process->readAllStandardOutput(); - sendPacket(packet); - removeProcess(process->token()); -} - -void LauncherSocketHandler::handleStartPacket() -{ - ProcessWithToken *& 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( - m_packetParser.token(), - m_packetParser.packetData()); - - process->setEnvironment(packet.env); - process->setWorkingDirectory(packet.workingDir); - // Forwarding is handled by the LauncherInterface - process->setProcessChannelMode(packet.processChannelMode == QProcess::MergedChannels - ? QProcess::MergedChannels : QProcess::SeparateChannels); - process->setStandardInputFile(packet.standardInputFile); - ProcessStartHandler *handler = process->processStartHandler(); - handler->setWindowsSpecificStartupFlags(packet.belowNormalPriority, - packet.createConsoleOnWindows, - packet.forceDefaultErrorMode); - handler->setProcessMode(packet.processMode); - handler->setWriteData(packet.writeData); - handler->setNativeArguments(packet.nativeArguments); - if (packet.lowPriority) - process->setLowPriority(); - if (packet.unixTerminalDisabled) - process->setUnixTerminalDisabled(); - process->setUseCtrlCStub(packet.useCtrlCStub); - process->setReaperTimeout(packet.reaperTimeout); - process->start(packet.command, packet.arguments, handler->openMode()); - handler->handleProcessStart(); -} - -void LauncherSocketHandler::handleWritePacket() -{ - ProcessWithToken * const process = m_processes.value(m_packetParser.token()); - if (!process) { - logWarn("Got write request for unknown process"); - return; - } - if (process->state() != QProcess::Running) { - logDebug("Can't write into not running process"); - return; - } - const auto packet = LauncherPacket::extractPacket( - m_packetParser.token(), - m_packetParser.packetData()); - process->write(packet.inputData); -} - -void LauncherSocketHandler::handleControlPacket() -{ - ProcessWithToken * const process = m_processes.value(m_packetParser.token()); - if (!process) { - // This can happen when the process finishes on its own at about the same time the client - // sends the request. In this case the process was already deleted. - logDebug("Got stop request for unknown process"); - return; - } - const auto packet = LauncherPacket::extractPacket( - m_packetParser.token(), - m_packetParser.packetData()); - - switch (packet.signalType) { - case ControlProcessPacket::SignalType::Terminate: - process->terminate(); - break; - case ControlProcessPacket::SignalType::Kill: - process->kill(); - break; - case ControlProcessPacket::SignalType::Close: - removeProcess(process->token()); - break; - case ControlProcessPacket::SignalType::CloseWriteChannel: - process->closeWriteChannel(); - break; - } -} - -void LauncherSocketHandler::handleShutdownPacket() -{ - logDebug("Got shutdown request, closing down"); - m_socket->disconnect(); - qApp->quit(); -} - -void LauncherSocketHandler::sendPacket(const LauncherPacket &packet) -{ - m_socket->write(packet.serialize()); -} - -ProcessWithToken *LauncherSocketHandler::setupProcess(quintptr token) -{ - const auto p = new ProcessWithToken(token, this); - connect(p, &QProcess::started, this, [this, p] { handleProcessStarted(p); }); - connect(p, &QProcess::errorOccurred, this, [this, p] { handleProcessError(p); }); - connect(p, &QProcess::finished, this, [this, p] { handleProcessFinished(p); }); - connect(p, &QProcess::readyReadStandardOutput, - this, [this, p] { handleReadyReadStandardOutput(p); }); - connect(p, &QProcess::readyReadStandardError, - this, [this, p] { handleReadyReadStandardError(p); }); - return p; -} - -void LauncherSocketHandler::removeProcess(quintptr token) -{ - const auto it = m_processes.constFind(token); - if (it == m_processes.constEnd()) - return; - - ProcessWithToken *process = it.value(); - m_processes.erase(it); - ProcessReaper::reap(process, std::chrono::milliseconds(process->reaperTimeout())); -} - -} // namespace Internal -} // namespace Utils - -#include "launchersockethandler.moc" diff --git a/src/tools/processlauncher/launchersockethandler.h b/src/tools/processlauncher/launchersockethandler.h deleted file mode 100644 index 05eefb09b82..00000000000 --- a/src/tools/processlauncher/launchersockethandler.h +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include - -#include -#include -#include - -QT_BEGIN_NAMESPACE -class QLocalSocket; -QT_END_NAMESPACE - -namespace Utils { -namespace Internal { -class ProcessWithToken; - -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 handleProcessStarted(ProcessWithToken *process); - void handleProcessError(ProcessWithToken *process); - void handleProcessFinished(ProcessWithToken *process); - void handleReadyReadStandardOutput(ProcessWithToken *process); - void handleReadyReadStandardError(ProcessWithToken *process); - - void handleStartPacket(); - void handleWritePacket(); - void handleControlPacket(); - void handleShutdownPacket(); - - void sendPacket(const LauncherPacket &packet); - - ProcessWithToken *setupProcess(quintptr token); - void removeProcess(quintptr token); - - const QString m_serverPath; - QLocalSocket * const m_socket; - PacketParser m_packetParser; - QHash m_processes; -}; - -} // namespace Internal -} // namespace Utils diff --git a/src/tools/processlauncher/processlauncher-main.cpp b/src/tools/processlauncher/processlauncher-main.cpp deleted file mode 100644 index 980839cd8a8..00000000000 --- a/src/tools/processlauncher/processlauncher-main.cpp +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include "launcherlogging.h" -#include "launchersockethandler.h" - -#include - -#include -#include -#include - -#ifdef Q_OS_WIN -#include - -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; - } - - const QScopeGuard cleanup([] { Utils::Singleton::deleteAll(); }); - - Utils::Internal::LauncherSocketHandler launcher(app.arguments().constLast()); - QTimer::singleShot(0, &launcher, &Utils::Internal::LauncherSocketHandler::start); - return app.exec(); -} diff --git a/src/tools/processlauncher/processlauncher.qbs b/src/tools/processlauncher/processlauncher.qbs deleted file mode 100644 index ca2973cd952..00000000000 --- a/src/tools/processlauncher/processlauncher.qbs +++ /dev/null @@ -1,46 +0,0 @@ -import qbs -import qbs.FileInfo - -QtcTool { - name: "qtcreator_processlauncher" - - Depends { name: "Qt.network" } - - cpp.defines: base.concat("UTILS_STATIC_LIBRARY") - cpp.includePaths: base.concat(pathToLibs) - - Properties { - condition: qbs.targetOS.contains("windows") - cpp.dynamicLibraries: qbs.toolchain.contains("msvc") ? ["user32", "dbghelp"] : ["user32"] - } - - files: [ - "launcherlogging.cpp", - "launcherlogging.h", - "launchersockethandler.cpp", - "launchersockethandler.h", - "processlauncher-main.cpp", - ] - - property string pathToLibs: sourceDirectory + "/../../libs" - property string pathToUtils: sourceDirectory + "/../../libs/utils" - Group { - name: "protocol sources" - prefix: pathToUtils + '/' - files: [ - "launcherpackets.cpp", - "launcherpackets.h", - "processenums.h", - "processhelper.cpp", - "processhelper.h", - "processreaper.cpp", - "processreaper.h", - "qtcassert.cpp", - "qtcassert.h", - "singleton.cpp", - "singleton.h", - "threadutils.cpp", - "threadutils.h", - ] - } -} diff --git a/src/tools/tools.qbs b/src/tools/tools.qbs index 48a35448d7e..39012049493 100644 --- a/src/tools/tools.qbs +++ b/src/tools/tools.qbs @@ -8,7 +8,6 @@ Project { "cplusplustools.qbs", "disclaim/disclaim.qbs", "process_stub/process_stub.qbs", - "processlauncher/processlauncher.qbs", "qml2puppet/qml2puppet.qbs", "qtcdebugger/qtcdebugger.qbs", "qtcreatorcrashhandler/qtcreatorcrashhandler.qbs", diff --git a/tests/auto/utils/commandline/tst_commandline.cpp b/tests/auto/utils/commandline/tst_commandline.cpp index aca984adf54..7bc54db50d4 100644 --- a/tests/auto/utils/commandline/tst_commandline.cpp +++ b/tests/auto/utils/commandline/tst_commandline.cpp @@ -6,10 +6,9 @@ #include #include #include -#include #include #include -#include +#include #include #include @@ -43,10 +42,6 @@ private slots: TemporaryDirectory::setMasterTemporaryDirectory( QDir::tempPath() + "/" + Core::Constants::IDE_CASED_ID + "-XXXXXX"); - const QString libExecPath(qApp->applicationDirPath() + '/' - + QLatin1String(TEST_RELATIVE_LIBEXEC_PATH)); - LauncherInterface::setPathToLauncher(libExecPath); - testEnv.set("TEST_ECHO", "1"); if (HostOsInfo::isWindowsHost()) diff --git a/tests/auto/utils/deviceshell/tst_deviceshell.cpp b/tests/auto/utils/deviceshell/tst_deviceshell.cpp index 75150add83b..5ec8acab803 100644 --- a/tests/auto/utils/deviceshell/tst_deviceshell.cpp +++ b/tests/auto/utils/deviceshell/tst_deviceshell.cpp @@ -7,8 +7,8 @@ #include #include #include -#include #include +#include #include #include @@ -71,10 +71,6 @@ private slots: TemporaryDirectory::setMasterTemporaryDirectory( QDir::tempPath() + "/" + Core::Constants::IDE_CASED_ID + "-XXXXXX"); - const QString libExecPath(qApp->applicationDirPath() + '/' - + QLatin1String(TEST_RELATIVE_LIBEXEC_PATH)); - LauncherInterface::setPathToLauncher(libExecPath); - std::iota(m_asciiTestData.begin(), m_asciiTestData.end(), 0); const FilePath dockerExecutable = Environment::systemEnvironment() diff --git a/tests/auto/utils/process/processtestapp/main.cpp b/tests/auto/utils/process/processtestapp/main.cpp index 5b0dda04b32..51d2e37ccfc 100644 --- a/tests/auto/utils/process/processtestapp/main.cpp +++ b/tests/auto/utils/process/processtestapp/main.cpp @@ -5,8 +5,8 @@ #include -#include #include +#include #include #include @@ -32,9 +32,6 @@ int main(int argc, char **argv) TemporaryDirectory::setMasterTemporaryDirectory(QDir::tempPath() + "/" + Core::Constants::IDE_CASED_ID + "-XXXXXX"); - const QString libExecPath(qApp->applicationDirPath() + '/' - + QLatin1String(TEST_RELATIVE_LIBEXEC_PATH)); - LauncherInterface::setPathToLauncher(libExecPath); SubProcessConfig::setPathToProcessTestApp(QLatin1String(PROCESS_TESTAPP)); QMetaObject::invokeMethod(&app, [] { ProcessTestApp::invokeSubProcess(); }, Qt::QueuedConnection); diff --git a/tests/auto/utils/process/tst_process.cpp b/tests/auto/utils/process/tst_process.cpp index 01704d3d2ac..ef9553ea5bf 100644 --- a/tests/auto/utils/process/tst_process.cpp +++ b/tests/auto/utils/process/tst_process.cpp @@ -8,7 +8,6 @@ #include #include #include -#include #include #include #include @@ -21,9 +20,6 @@ #include #include -#include -#include - using namespace Utils; using namespace std::chrono; @@ -167,9 +163,6 @@ void tst_Process::initTestCase() msgHandler = new MessageHandler; TemporaryDirectory::setMasterTemporaryDirectory(QDir::tempPath() + "/" + Core::Constants::IDE_CASED_ID + "-XXXXXX"); - const QString libExecPath(qApp->applicationDirPath() + '/' - + QLatin1String(TEST_RELATIVE_LIBEXEC_PATH)); - LauncherInterface::setPathToLauncher(libExecPath); SubProcessConfig::setPathToProcessTestApp(QLatin1String(PROCESS_TESTAPP)); homeStr = QLatin1String("@HOME@"); @@ -1286,29 +1279,20 @@ void tst_Process::stdinToShell() void tst_Process::eventLoopMode_data() { - QTest::addColumn("processImpl"); QTest::addColumn("eventLoopMode"); - QTest::newRow("QProcess, blocking with event loop") - << ProcessImpl::QProcess << EventLoopMode::On; - QTest::newRow("QProcess, blocking without event loop") - << ProcessImpl::QProcess << EventLoopMode::Off; - QTest::newRow("ProcessLauncher, blocking with event loop") - << ProcessImpl::ProcessLauncher << EventLoopMode::On; - QTest::newRow("ProcessLauncher, blocking without event loop") - << ProcessImpl::ProcessLauncher << EventLoopMode::Off; + QTest::newRow("EventLoopMode::On") << EventLoopMode::On; + QTest::newRow("EventLoopMode::Off") << EventLoopMode::Off; } void tst_Process::eventLoopMode() { - QFETCH(ProcessImpl, processImpl); QFETCH(EventLoopMode, eventLoopMode); { SubProcessConfig subConfig(ProcessTestApp::SimpleTest::envVar(), {}); Process process; subConfig.setupSubProcess(&process); - process.setProcessImpl(processImpl); process.runBlocking(10s, eventLoopMode); QCOMPARE(process.result(), ProcessResult::FinishedWithSuccess); } @@ -1317,7 +1301,6 @@ void tst_Process::eventLoopMode() Process process; process.setCommand( CommandLine{"there_is_a_big_chance_that_executable_with_that_name_does_not_exists"}); - process.setProcessImpl(processImpl); process.runBlocking(10s, eventLoopMode); QCOMPARE(process.result(), ProcessResult::StartFailed); } diff --git a/tests/auto/valgrind/memcheck/modeldemo.cpp b/tests/auto/valgrind/memcheck/modeldemo.cpp index 04735fc5f40..83e2c55aca1 100644 --- a/tests/auto/valgrind/memcheck/modeldemo.cpp +++ b/tests/auto/valgrind/memcheck/modeldemo.cpp @@ -2,7 +2,6 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include -#include #include #include @@ -24,9 +23,6 @@ int main(int argc, char *argv[]) QApplication app(argc, argv); Utils::TemporaryDirectory::setMasterTemporaryDirectory(QDir::tempPath() + "/QtCreator-XXXXXX"); - const QString libExecPath(qApp->applicationDirPath() + '/' - + QLatin1String(TEST_RELATIVE_LIBEXEC_PATH)); - Utils::LauncherInterface::setPathToLauncher(libExecPath); qRegisterMetaType(); diff --git a/tests/manual/cmdbridge/tst_cmdbridge.cpp b/tests/manual/cmdbridge/tst_cmdbridge.cpp index d4c96b1d14e..f52a14378a1 100644 --- a/tests/manual/cmdbridge/tst_cmdbridge.cpp +++ b/tests/manual/cmdbridge/tst_cmdbridge.cpp @@ -8,8 +8,8 @@ #include #include -#include #include +#include #include #include @@ -60,9 +60,6 @@ private slots: { TemporaryDirectory::setMasterTemporaryDirectory( QDir::tempPath() + "/" + Core::Constants::IDE_CASED_ID + "-XXXXXX"); - - libExecPath = qApp->applicationDirPath() + '/' + QLatin1String(TEST_RELATIVE_LIBEXEC_PATH); - LauncherInterface::setPathToLauncher(libExecPath); } void cleanupTestCase() { Singleton::deleteAll(); } diff --git a/tests/manual/deviceshell/tst_deviceshell.cpp b/tests/manual/deviceshell/tst_deviceshell.cpp index 88f95fd7115..c93f05dff1c 100644 --- a/tests/manual/deviceshell/tst_deviceshell.cpp +++ b/tests/manual/deviceshell/tst_deviceshell.cpp @@ -5,8 +5,8 @@ #include #include -#include #include +#include #include #include @@ -131,10 +131,6 @@ private slots: TemporaryDirectory::setMasterTemporaryDirectory( QDir::tempPath() + "/" + Core::Constants::IDE_CASED_ID + "-XXXXXX"); - const QString libExecPath(qApp->applicationDirPath() + '/' - + QLatin1String(TEST_RELATIVE_LIBEXEC_PATH)); - LauncherInterface::setPathToLauncher(libExecPath); - if (TestShell::cmdLine().isEmpty()) { QSKIP("Skipping deviceshell tests, as no compatible shell could be found"); } diff --git a/tests/manual/subdirfilecontainer/tst_subdirfilecontainer.cpp b/tests/manual/subdirfilecontainer/tst_subdirfilecontainer.cpp index 6493bd9121c..a1bffbcda5c 100644 --- a/tests/manual/subdirfilecontainer/tst_subdirfilecontainer.cpp +++ b/tests/manual/subdirfilecontainer/tst_subdirfilecontainer.cpp @@ -5,8 +5,8 @@ #include #include -#include #include +#include #include #include @@ -114,10 +114,6 @@ private slots: TemporaryDirectory::setMasterTemporaryDirectory( QDir::tempPath() + "/" + Core::Constants::IDE_CASED_ID + "-XXXXXX"); - const QString libExecPath(qApp->applicationDirPath() + '/' - + QLatin1String(TEST_RELATIVE_LIBEXEC_PATH)); - LauncherInterface::setPathToLauncher(libExecPath); - qDebug() << "This manual test compares the performance of the SubDirFileContainer with a " "manually written iterator using QDir::entryInfoList() and with QDirIterator."; QTC_SCOPED_TIMER("GENERATING TEMPORARY FILES TREE"); diff --git a/tests/unit/tests/unittests/unittests-main.cpp b/tests/unit/tests/unittests/unittests-main.cpp index 06c8268f07e..4b16f32a273 100644 --- a/tests/unit/tests/unittests/unittests-main.cpp +++ b/tests/unit/tests/unittests/unittests-main.cpp @@ -7,7 +7,6 @@ #include #include -#include #include #include @@ -38,8 +37,6 @@ int main(int argc, char *argv[]) Sqlite::Database::activateLogging(); QGuiApplication application(argc, argv); - Utils::LauncherInterface::setPathToLauncher(qApp->applicationDirPath() + '/' - + QLatin1String(TEST_RELATIVE_LIBEXEC_PATH)); testing::InitGoogleTest(&argc, argv); #ifdef WITH_BENCHMARKS benchmark::Initialize(&argc, argv);