2021-07-07 11:36:03 +02:00
|
|
|
/****************************************************************************
|
|
|
|
|
**
|
|
|
|
|
** 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"
|
2021-08-06 16:20:47 +02:00
|
|
|
#include "processutils.h"
|
2021-07-07 11:36:03 +02:00
|
|
|
|
|
|
|
|
#include <QtCore/qobject.h>
|
|
|
|
|
|
2021-07-14 16:02:25 +02:00
|
|
|
#include <QHash>
|
|
|
|
|
#include <QMutex>
|
|
|
|
|
#include <QMutexLocker>
|
|
|
|
|
#include <QProcess>
|
|
|
|
|
#include <QWaitCondition>
|
|
|
|
|
|
2021-07-07 11:36:03 +02:00
|
|
|
#include <vector>
|
2021-07-14 16:02:25 +02:00
|
|
|
#include <atomic>
|
2021-07-07 11:36:03 +02:00
|
|
|
|
|
|
|
|
QT_BEGIN_NAMESPACE
|
|
|
|
|
class QLocalSocket;
|
|
|
|
|
QT_END_NAMESPACE
|
|
|
|
|
|
|
|
|
|
namespace Utils {
|
|
|
|
|
class LauncherInterface;
|
|
|
|
|
|
|
|
|
|
namespace Internal {
|
|
|
|
|
|
2021-07-14 16:02:25 +02:00
|
|
|
class LauncherInterfacePrivate;
|
|
|
|
|
class CallerHandle;
|
|
|
|
|
|
|
|
|
|
// 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 QtcProcess is alive.
|
|
|
|
|
|
|
|
|
|
class LauncherHandle : public QObject
|
|
|
|
|
{
|
|
|
|
|
Q_OBJECT
|
|
|
|
|
public:
|
2021-08-18 10:45:57 +02:00
|
|
|
// All the public methods in this class are called exclusively from the caller's thread.
|
2021-08-03 13:29:28 +02:00
|
|
|
bool waitForStarted(int msecs)
|
|
|
|
|
{ return waitForSignal(msecs, SignalType::Started); }
|
|
|
|
|
bool waitForReadyRead(int msces)
|
|
|
|
|
{ return waitForSignal(msces, SignalType::ReadyRead); }
|
2021-07-14 16:02:25 +02:00
|
|
|
bool waitForFinished(int msecs)
|
2021-08-03 13:29:28 +02:00
|
|
|
{ return waitForSignal(msecs, SignalType::Finished); }
|
2021-07-14 16:02:25 +02:00
|
|
|
|
|
|
|
|
QProcess::ProcessState state() const
|
|
|
|
|
{ QMutexLocker locker(&m_mutex); return m_processState; }
|
|
|
|
|
void cancel();
|
|
|
|
|
|
|
|
|
|
QByteArray readAllStandardOutput()
|
|
|
|
|
{ QMutexLocker locker(&m_mutex); return readAndClear(m_stdout); }
|
|
|
|
|
QByteArray readAllStandardError()
|
|
|
|
|
{ QMutexLocker locker(&m_mutex); return readAndClear(m_stderr); }
|
|
|
|
|
|
|
|
|
|
qint64 processId() const { QMutexLocker locker(&m_mutex); return m_processId; }
|
|
|
|
|
QString errorString() const { QMutexLocker locker(&m_mutex); return m_errorString; }
|
|
|
|
|
void setErrorString(const QString &str) { QMutexLocker locker(&m_mutex); m_errorString = str; }
|
|
|
|
|
|
2021-08-06 16:20:47 +02:00
|
|
|
void start(const QString &program, const QStringList &arguments, const QByteArray &writeData);
|
2021-07-14 16:02:25 +02:00
|
|
|
|
2021-08-06 14:43:03 +02:00
|
|
|
qint64 write(const QByteArray &data);
|
|
|
|
|
|
2021-07-14 16:02:25 +02:00
|
|
|
QProcess::ProcessError error() const { QMutexLocker locker(&m_mutex); return m_error; }
|
|
|
|
|
QString program() const { QMutexLocker locker(&m_mutex); return m_command; }
|
2021-07-12 15:11:30 +02:00
|
|
|
void setStandardInputFile(const QString &fileName) { QMutexLocker locker(&m_mutex); m_standardInputFile = fileName; }
|
2021-07-14 16:02:25 +02:00
|
|
|
void setProcessChannelMode(QProcess::ProcessChannelMode mode) {
|
|
|
|
|
QMutexLocker locker(&m_mutex);
|
|
|
|
|
if (mode != QProcess::SeparateChannels && mode != QProcess::MergedChannels) {
|
|
|
|
|
qWarning("setProcessChannelMode: The only supported modes are SeparateChannels and MergedChannels.");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
m_channelMode = mode;
|
|
|
|
|
}
|
|
|
|
|
void setProcessEnvironment(const QProcessEnvironment &environment)
|
|
|
|
|
{ QMutexLocker locker(&m_mutex); m_environment = environment; }
|
|
|
|
|
void setWorkingDirectory(const QString &dir) { QMutexLocker locker(&m_mutex); m_workingDirectory = dir; }
|
|
|
|
|
QProcess::ExitStatus exitStatus() const { QMutexLocker locker(&m_mutex); return m_exitStatus; }
|
2021-08-09 12:17:13 +02:00
|
|
|
|
|
|
|
|
void setBelowNormalPriority() { m_belowNormalPriority = true; }
|
2021-08-09 12:57:22 +02:00
|
|
|
void setNativeArguments(const QString &arguments) { m_nativeArguments = arguments; }
|
2021-08-09 14:01:14 +02:00
|
|
|
void setLowPriority() { m_lowPriority = true; }
|
|
|
|
|
void setUnixTerminalDisabled() { m_unixTerminalDisabled = true; }
|
2021-08-09 12:17:13 +02:00
|
|
|
|
2021-07-14 16:02:25 +02:00
|
|
|
signals:
|
|
|
|
|
void errorOccurred(QProcess::ProcessError error);
|
|
|
|
|
void started();
|
|
|
|
|
void finished(int exitCode, QProcess::ExitStatus status);
|
|
|
|
|
void readyReadStandardOutput();
|
|
|
|
|
void readyReadStandardError();
|
|
|
|
|
private:
|
2021-08-03 13:29:28 +02:00
|
|
|
enum class SignalType {
|
|
|
|
|
NoSignal,
|
2021-08-18 09:31:18 +02:00
|
|
|
Error,
|
2021-07-14 16:02:25 +02:00
|
|
|
Started,
|
|
|
|
|
ReadyRead,
|
|
|
|
|
Finished
|
|
|
|
|
};
|
2021-08-18 10:45:57 +02:00
|
|
|
|
|
|
|
|
// Called from caller's thread exclusively.
|
2021-08-03 13:29:28 +02:00
|
|
|
bool waitForSignal(int msecs, SignalType newSignal);
|
|
|
|
|
bool doWaitForSignal(int msecs, SignalType newSignal);
|
|
|
|
|
bool canWaitFor(SignalType newSignal) const;
|
2021-07-14 16:02:25 +02:00
|
|
|
|
2021-08-18 10:45:57 +02:00
|
|
|
// Called from caller's or launcher's thread.
|
2021-07-14 16:02:25 +02:00
|
|
|
void doStart();
|
|
|
|
|
|
2021-08-18 10:45:57 +02:00
|
|
|
// Called from caller's thread exclusively.
|
2021-08-18 09:31:18 +02:00
|
|
|
void slotErrorOccurred();
|
2021-07-14 16:02:25 +02:00
|
|
|
void slotStarted();
|
2021-07-16 11:38:34 +02:00
|
|
|
void slotReadyRead();
|
2021-07-14 16:02:25 +02:00
|
|
|
void slotFinished();
|
|
|
|
|
|
2021-08-18 10:45:57 +02:00
|
|
|
// Called from caller's thread, moved to launcher's thread.
|
2021-08-06 16:20:47 +02:00
|
|
|
LauncherHandle(quintptr token, ProcessMode mode) : m_token(token), m_processMode(mode) {}
|
2021-08-18 10:45:57 +02:00
|
|
|
|
|
|
|
|
// Called from caller's thread exclusively.
|
2021-07-14 16:02:25 +02:00
|
|
|
void createCallerHandle();
|
|
|
|
|
void destroyCallerHandle();
|
|
|
|
|
|
2021-08-18 10:45:57 +02:00
|
|
|
// Called from launcher's thread exclusively.
|
2021-07-14 16:02:25 +02:00
|
|
|
void flushCaller();
|
|
|
|
|
void handlePacket(LauncherPacketType type, const QByteArray &payload);
|
|
|
|
|
void handleErrorPacket(const QByteArray &packetData);
|
|
|
|
|
void handleStartedPacket(const QByteArray &packetData);
|
2021-08-02 13:26:33 +02:00
|
|
|
void handleReadyReadStandardOutput(const QByteArray &packetData);
|
|
|
|
|
void handleReadyReadStandardError(const QByteArray &packetData);
|
2021-07-14 16:02:25 +02:00
|
|
|
void handleFinishedPacket(const QByteArray &packetData);
|
|
|
|
|
|
2021-08-18 10:45:57 +02:00
|
|
|
// Called from launcher's thread exclusively.
|
2021-07-14 16:02:25 +02:00
|
|
|
void handleSocketReady();
|
|
|
|
|
void handleSocketError(const QString &message);
|
|
|
|
|
|
2021-08-18 10:45:57 +02:00
|
|
|
// Called from launcher's thread exclusively.
|
2021-08-18 09:31:18 +02:00
|
|
|
void wakeUpIfWaitingFor(SignalType newSignal);
|
2021-07-14 16:02:25 +02:00
|
|
|
|
2021-08-18 10:45:57 +02:00
|
|
|
// Called from caller's thread exclusively.
|
|
|
|
|
QByteArray readAndClear(QByteArray &data) const
|
2021-07-14 16:02:25 +02:00
|
|
|
{
|
|
|
|
|
const QByteArray tmp = data;
|
|
|
|
|
data.clear();
|
|
|
|
|
return tmp;
|
|
|
|
|
}
|
|
|
|
|
|
2021-08-18 10:45:57 +02:00
|
|
|
// Called from caller's or launcher's thread.
|
2021-07-14 16:02:25 +02:00
|
|
|
void sendPacket(const Internal::LauncherPacket &packet);
|
|
|
|
|
|
|
|
|
|
mutable QMutex m_mutex;
|
|
|
|
|
QWaitCondition m_waitCondition;
|
|
|
|
|
const quintptr m_token;
|
2021-08-06 16:20:47 +02:00
|
|
|
const ProcessMode m_processMode;
|
2021-08-03 13:29:28 +02:00
|
|
|
SignalType m_waitingFor = SignalType::NoSignal;
|
2021-07-14 16:02:25 +02:00
|
|
|
|
|
|
|
|
QProcess::ProcessState m_processState = QProcess::NotRunning;
|
2021-08-18 10:45:57 +02:00
|
|
|
// cancel() sets it to false, modified only in caller's thread.
|
|
|
|
|
bool m_awaitingShouldContinue = false;
|
2021-07-14 16:02:25 +02:00
|
|
|
int m_processId = 0;
|
|
|
|
|
int m_exitCode = 0;
|
|
|
|
|
QProcess::ExitStatus m_exitStatus = QProcess::ExitStatus::NormalExit;
|
|
|
|
|
QByteArray m_stdout;
|
|
|
|
|
QByteArray m_stderr;
|
|
|
|
|
QString m_errorString;
|
|
|
|
|
QProcess::ProcessError m_error = QProcess::UnknownError;
|
|
|
|
|
bool m_socketError = false;
|
|
|
|
|
|
|
|
|
|
QString m_command;
|
|
|
|
|
QStringList m_arguments;
|
|
|
|
|
QProcessEnvironment m_environment;
|
|
|
|
|
QString m_workingDirectory;
|
2021-08-06 16:20:47 +02:00
|
|
|
QByteArray m_writeData;
|
2021-07-14 16:02:25 +02:00
|
|
|
QProcess::ProcessChannelMode m_channelMode = QProcess::SeparateChannels;
|
2021-07-12 15:11:30 +02:00
|
|
|
QString m_standardInputFile;
|
2021-07-14 16:02:25 +02:00
|
|
|
|
2021-08-18 10:45:57 +02:00
|
|
|
// Lives in caller's thread.
|
2021-07-14 16:02:25 +02:00
|
|
|
CallerHandle *m_callerHandle = nullptr;
|
|
|
|
|
|
2021-08-09 12:17:13 +02:00
|
|
|
bool m_belowNormalPriority = false;
|
2021-08-09 12:57:22 +02:00
|
|
|
QString m_nativeArguments;
|
2021-08-09 14:01:14 +02:00
|
|
|
bool m_lowPriority = false;
|
|
|
|
|
bool m_unixTerminalDisabled = false;
|
2021-08-09 12:17:13 +02:00
|
|
|
|
2021-07-14 16:02:25 +02:00
|
|
|
friend class LauncherSocket;
|
2021-08-03 13:29:28 +02:00
|
|
|
friend class CallerHandle;
|
2021-07-14 16:02:25 +02:00
|
|
|
};
|
|
|
|
|
|
2021-07-07 11:36:03 +02:00
|
|
|
class LauncherSocket : public QObject
|
|
|
|
|
{
|
|
|
|
|
Q_OBJECT
|
2021-07-14 16:02:25 +02:00
|
|
|
friend class LauncherInterfacePrivate;
|
2021-07-07 11:36:03 +02:00
|
|
|
public:
|
2021-08-18 10:45:57 +02:00
|
|
|
// Called from caller's or launcher's thread.
|
2021-07-07 11:36:03 +02:00
|
|
|
bool isReady() const { return m_socket.load(); }
|
|
|
|
|
void sendData(const QByteArray &data);
|
|
|
|
|
|
2021-08-18 10:45:57 +02:00
|
|
|
// Called from caller's thread exclusively.
|
2021-08-06 16:20:47 +02:00
|
|
|
LauncherHandle *registerHandle(quintptr token, ProcessMode mode);
|
2021-07-14 16:02:25 +02:00
|
|
|
void unregisterHandle(quintptr token);
|
|
|
|
|
|
2021-07-07 11:36:03 +02:00
|
|
|
signals:
|
|
|
|
|
void ready();
|
|
|
|
|
void errorOccurred(const QString &error);
|
|
|
|
|
|
|
|
|
|
private:
|
2021-08-18 10:45:57 +02:00
|
|
|
// Called from caller's thread, moved to launcher's thread.
|
2021-07-14 16:02:25 +02:00
|
|
|
LauncherSocket(QObject *parent = nullptr);
|
|
|
|
|
|
2021-08-18 10:45:57 +02:00
|
|
|
// Called from launcher's thread exclusively.
|
2021-07-14 16:02:25 +02:00
|
|
|
LauncherHandle *handleForToken(quintptr token) const;
|
2021-07-07 11:36:03 +02:00
|
|
|
|
2021-08-18 10:45:57 +02:00
|
|
|
// Called from launcher's thread exclusively.
|
2021-07-07 11:36:03 +02:00
|
|
|
void setSocket(QLocalSocket *socket);
|
|
|
|
|
void shutdown();
|
|
|
|
|
|
2021-08-18 10:45:57 +02:00
|
|
|
// Called from launcher's thread exclusively.
|
2021-07-07 11:36:03 +02:00
|
|
|
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;
|
2021-07-14 16:02:25 +02:00
|
|
|
mutable QMutex m_mutex;
|
|
|
|
|
QHash<quintptr, LauncherHandle *> m_handles;
|
2021-07-07 11:36:03 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
} // namespace Internal
|
|
|
|
|
} // namespace Utils
|