aboutsummaryrefslogtreecommitdiffstats
path: root/src/lib
diff options
context:
space:
mode:
authorChristian Kandeler <christian.kandeler@qt.io>2016-12-20 10:06:49 +0100
committerJoerg Bornemann <joerg.bornemann@qt.io>2017-01-25 11:30:02 +0000
commit557775b1ad3316ec158b9d930bf733b4fa502456 (patch)
tree1911bcdcf65c7ed1d106f101ef15e7083c56af7e /src/lib
parentab89e84d71b277744df98a7037263e7bd44efbeb (diff)
Use a dedicated launcher process to run all ProcessCommands
On Linux (and very likely other Unices as well), QProcess::start() incurs a certain overhead, because forking off the child process duplicates some of the parent process' resources. This overhead appears to be proportional to the amount of memory allocated by the parent process, presumably due to page table entries getting copied. This has consequences for qbs, particularly when being used from an IDE such as Qt Creator, which has a higher memory footprint than the command line tool. When using a high job count, as is typical on machines with lots of CPU cores or in a distributed compilation environment, the following problems were observed: - High CPU load in the starting process (Qt Creator). Profiling showed that most of the time was spent in fork() and related functions. - As a result, the number of parallel jobs stalled at a value well below the requested one, slowing down the build. - In some cases, QProcess::start() failed altogether, emitting a message such as "fork() failed: Could not allocate memory". We solve these issues by outsourcing the starting of ProcessCommands to a dedicated launcher tool with modest memory requirements. For each qbs process, we have one instance of this tool running while a build job is going on. Communication with qbs happens via QLocalSocket. The protocol is encapsulated in a QProcess replacement, so almost no changes to existing code were necessary. No performance regressions were observed when using lower job counts. This patch will also enable us to properly support the incredibuild tool in IDEs such as Qt Creator, which you do not want to start via ibconsole. [ChangeLog] Improved scalability of parallel builds on Linux by starting Process commands via a dedicated launcher process. Change-Id: I8966c00a2d67a94c3a380f0e089d61eda122209e Reviewed-by: Oswald Buddenhagen <oswald.buddenhagen@qt.io> Reviewed-by: Joerg Bornemann <joerg.bornemann@qt.io>
Diffstat (limited to 'src/lib')
-rw-r--r--src/lib/corelib/api/jobs.cpp17
-rw-r--r--src/lib/corelib/api/jobs.h5
-rw-r--r--src/lib/corelib/buildgraph/processcommandexecutor.cpp8
-rw-r--r--src/lib/corelib/buildgraph/processcommandexecutor.h6
-rw-r--r--src/lib/corelib/corelib.pro3
-rw-r--r--src/lib/corelib/corelib.qbs9
-rw-r--r--src/lib/corelib/tools/launcherinterface.cpp148
-rw-r--r--src/lib/corelib/tools/launcherinterface.h88
-rw-r--r--src/lib/corelib/tools/launcherpackets.cpp175
-rw-r--r--src/lib/corelib/tools/launcherpackets.h178
-rw-r--r--src/lib/corelib/tools/launchersocket.cpp133
-rw-r--r--src/lib/corelib/tools/launchersocket.h91
-rw-r--r--src/lib/corelib/tools/qbsprocess.cpp173
-rw-r--r--src/lib/corelib/tools/qbsprocess.h106
-rw-r--r--src/lib/corelib/tools/tools.pri8
15 files changed, 1140 insertions, 8 deletions
diff --git a/src/lib/corelib/api/jobs.cpp b/src/lib/corelib/api/jobs.cpp
index 6589aa359..b1b5b7e5f 100644
--- a/src/lib/corelib/api/jobs.cpp
+++ b/src/lib/corelib/api/jobs.cpp
@@ -41,6 +41,7 @@
#include "internaljobs.h"
#include "project_p.h"
#include <language/language.h>
+#include <tools/launcherinterface.h>
#include <tools/qbsassert.h>
#include <QtCore/qtimer.h>
@@ -161,6 +162,8 @@ AbstractJob::~AbstractJob()
*/
ErrorInfo AbstractJob::error() const
{
+ if (m_error.hasError())
+ return m_error;
return internalJob()->error();
}
@@ -300,6 +303,8 @@ void SetupProjectJob::finish()
BuildJob::BuildJob(const Logger &logger, QObject *parent)
: AbstractJob(new InternalBuildJob(logger), parent)
{
+ connect(&LauncherInterface::instance(), &LauncherInterface::errorOccurred,
+ this, &BuildJob::handleLauncherError);
InternalBuildJob *job = static_cast<InternalBuildJob *>(internalJob());
connect(job, &BuildGraphTouchingJob::reportCommandDescription,
this, &BuildJob::reportCommandDescription);
@@ -312,9 +317,21 @@ void BuildJob::build(const TopLevelProjectPtr &project, const QList<ResolvedProd
{
if (!lockProject(project))
return;
+ LauncherInterface::startLauncher();
qobject_cast<InternalBuildJob *>(internalJob())->build(project, products, options);
}
+void BuildJob::handleLauncherError(const ErrorInfo &error)
+{
+ setError(error);
+ cancel();
+}
+
+void BuildJob::finish()
+{
+ LauncherInterface::stopLauncher();
+}
+
/*!
* \class CleanJob
diff --git a/src/lib/corelib/api/jobs.h b/src/lib/corelib/api/jobs.h
index cf9f337f2..04a0093c1 100644
--- a/src/lib/corelib/api/jobs.h
+++ b/src/lib/corelib/api/jobs.h
@@ -81,6 +81,7 @@ protected:
Internal::InternalJob *internalJob() const { return m_internalJob; }
bool lockProject(const Internal::TopLevelProjectPtr &project);
+ void setError(const ErrorInfo &error) { m_error = error; }
signals:
void taskStarted(const QString &description, int maximumProgressValue, qbs::AbstractJob *job);
@@ -99,6 +100,7 @@ private:
Internal::InternalJob * const m_internalJob;
Internal::TopLevelProjectPtr m_project;
+ ErrorInfo m_error;
State m_state;
};
@@ -136,6 +138,9 @@ private:
void build(const Internal::TopLevelProjectPtr &project,
const QList<qbs::Internal::ResolvedProductPtr> &products,
const BuildOptions &options);
+ void handleLauncherError(const ErrorInfo &error);
+
+ void finish();
};
diff --git a/src/lib/corelib/buildgraph/processcommandexecutor.cpp b/src/lib/corelib/buildgraph/processcommandexecutor.cpp
index 9eae591eb..befa66563 100644
--- a/src/lib/corelib/buildgraph/processcommandexecutor.cpp
+++ b/src/lib/corelib/buildgraph/processcommandexecutor.cpp
@@ -71,9 +71,9 @@ namespace Internal {
ProcessCommandExecutor::ProcessCommandExecutor(const Logger &logger, QObject *parent)
: AbstractCommandExecutor(logger, parent)
{
- connect(&m_process, static_cast<void (QProcess::*)(QProcess::ProcessError)>(&QProcess::error),
+ connect(&m_process, static_cast<void (QbsProcess::*)(QProcess::ProcessError)>(&QbsProcess::error),
this, &ProcessCommandExecutor::onProcessError);
- connect(&m_process, static_cast<void (QProcess::*)(int)>(&QProcess::finished),
+ connect(&m_process, static_cast<void (QbsProcess::*)(int)>(&QbsProcess::finished),
this, &ProcessCommandExecutor::onProcessFinished);
}
@@ -169,9 +169,7 @@ void ProcessCommandExecutor::cancel()
// We don't want this command to be reported as failing, since we explicitly terminated it.
disconnect(this, &ProcessCommandExecutor::reportProcessResult, 0, 0);
- m_process.terminate();
- if (!m_process.waitForFinished(1000))
- m_process.kill();
+ m_process.cancel();
}
QString ProcessCommandExecutor::filterProcessOutput(const QByteArray &_output,
diff --git a/src/lib/corelib/buildgraph/processcommandexecutor.h b/src/lib/corelib/buildgraph/processcommandexecutor.h
index d04f305c3..b96f6a0f1 100644
--- a/src/lib/corelib/buildgraph/processcommandexecutor.h
+++ b/src/lib/corelib/buildgraph/processcommandexecutor.h
@@ -42,8 +42,8 @@
#include "abstractcommandexecutor.h"
-#include <QtCore/qprocess.h>
-#include <QtCore/qprocess.h>
+#include <tools/qbsprocess.h>
+
#include <QtCore/qstring.h>
namespace qbs {
@@ -87,7 +87,7 @@ private:
QStringList m_arguments;
QString m_shellInvocation;
- QProcess m_process;
+ QbsProcess m_process;
QProcessEnvironment m_buildEnvironment;
QProcessEnvironment m_commandEnvironment;
QString m_responseFileName;
diff --git a/src/lib/corelib/corelib.pro b/src/lib/corelib/corelib.pro
index f7046582e..0ca361850 100644
--- a/src/lib/corelib/corelib.pro
+++ b/src/lib/corelib/corelib.pro
@@ -1,6 +1,9 @@
TARGET = qbscore
include(../library.pri)
+isEmpty(QBS_RELATIVE_LIBEXEC_PATH):QBS_RELATIVE_LIBEXEC_PATH=../libexec/qbs
+DEFINES += QBS_RELATIVE_LIBEXEC_PATH=\\\"$${QBS_RELATIVE_LIBEXEC_PATH}\\\"
+
QT += core-private network script
qbs_enable_unit_tests:QT += testlib
qbs_enable_project_file_updates: QT += gui
diff --git a/src/lib/corelib/corelib.qbs b/src/lib/corelib/corelib.qbs
index 85528550b..eee8600a5 100644
--- a/src/lib/corelib/corelib.qbs
+++ b/src/lib/corelib/corelib.qbs
@@ -18,6 +18,7 @@ QbsLibrary {
property stringList projectFileUpdateDefines:
qbsbuildconfig.enableProjectFileUpdates ? ["QBS_ENABLE_PROJECT_FILE_UPDATES"] : []
cpp.defines: base.concat([
+ 'QBS_RELATIVE_LIBEXEC_PATH="' + qbsbuildconfig.relativeLibexecPath + '"',
"QBS_VERSION=\"" + version + "\"",
"QT_CREATOR", "QML_BUILD_STATIC_LIB", // needed for QmlJS
"SRCDIR=\"" + path + "\""
@@ -360,6 +361,12 @@ QbsLibrary {
"jsliterals.cpp",
"jsliterals.h",
"installoptions.cpp",
+ "launcherinterface.cpp",
+ "launcherinterface.h",
+ "launcherpackets.cpp",
+ "launcherpackets.h",
+ "launchersocket.cpp",
+ "launchersocket.h",
"msvcinfo.cpp",
"msvcinfo.h",
"pathutils.h",
@@ -379,6 +386,8 @@ QbsLibrary {
"projectgeneratormanager.cpp",
"qbsassert.cpp",
"qbsassert.h",
+ "qbsprocess.cpp",
+ "qbsprocess.h",
"qttools.cpp",
"qttools.h",
"scannerpluginmanager.cpp",
diff --git a/src/lib/corelib/tools/launcherinterface.cpp b/src/lib/corelib/tools/launcherinterface.cpp
new file mode 100644
index 000000000..7e9679379
--- /dev/null
+++ b/src/lib/corelib/tools/launcherinterface.cpp
@@ -0,0 +1,148 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qbs.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** 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 Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** 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-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "launcherinterface.h"
+
+#include "launcherpackets.h"
+#include "launchersocket.h"
+#include "qbsassert.h"
+#include <logging/logger.h>
+#include <logging/translator.h>
+
+#include <QtCore/qcoreapplication.h>
+#include <QtCore/qdebug.h>
+#include <QtCore/qprocess.h>
+#include <QtNetwork/qlocalserver.h>
+
+namespace qbs {
+namespace Internal {
+
+static QString launcherSocketName()
+{
+ return QString::fromLatin1("qbs_processlauncher-%1")
+ .arg(QString::number(qApp->applicationPid()));
+}
+
+LauncherInterface::LauncherInterface()
+ : m_server(new QLocalServer(this)), m_socket(new LauncherSocket(this))
+{
+ QObject::connect(m_server, &QLocalServer::newConnection,
+ this, &LauncherInterface::handleNewConnection);
+}
+
+LauncherInterface &LauncherInterface::instance()
+{
+ static LauncherInterface p;
+ return p;
+}
+
+LauncherInterface::~LauncherInterface()
+{
+ m_server->disconnect();
+}
+
+void LauncherInterface::doStart()
+{
+ if (++m_startRequests > 1)
+ return;
+ const QString &socketName = launcherSocketName();
+ QLocalServer::removeServer(socketName);
+ if (!m_server->listen(socketName)) {
+ emit errorOccurred(ErrorInfo(m_server->errorString()));
+ return;
+ }
+ m_process = new QProcess(this);
+ connect(m_process,
+ static_cast<void (QProcess::*)(QProcess::ProcessError)>(&QProcess::error),
+ this, &LauncherInterface::handleProcessError);
+ connect(m_process, static_cast<void (QProcess::*)(int)>(&QProcess::finished),
+ this, &LauncherInterface::handleProcessFinished);
+ connect(m_process, &QProcess::readyReadStandardError,
+ this, &LauncherInterface::handleProcessStderr);
+ m_process->start(qApp->applicationDirPath() + QLatin1Char('/')
+ + QLatin1String(QBS_RELATIVE_LIBEXEC_PATH)
+ + QLatin1String("/qbs_processlauncher"),
+ QStringList(m_server->fullServerName()));
+}
+
+void LauncherInterface::doStop()
+{
+ if (--m_startRequests > 0)
+ return;
+ m_server->close();
+ if (!m_process)
+ return;
+ m_process->disconnect();
+ if (m_socket->isReady())
+ m_socket->shutdown();
+ m_process->waitForFinished(3000);
+ m_process->deleteLater();
+ m_process = nullptr;
+}
+
+void LauncherInterface::handleNewConnection()
+{
+ QLocalSocket * const socket = m_server->nextPendingConnection();
+ if (!socket)
+ return;
+ m_server->close();
+ m_socket->setSocket(socket);
+}
+
+void LauncherInterface::handleProcessError()
+{
+ if (m_process->error() == QProcess::FailedToStart)
+ handleProcessFinished();
+}
+
+void LauncherInterface::handleProcessFinished()
+{
+ if (!m_socket->isReady()) {
+ emit errorOccurred(ErrorInfo(m_process->errorString()));
+ return;
+ }
+}
+
+void LauncherInterface::handleProcessStderr()
+{
+ qDebug() << "[launcher]" << m_process->readAllStandardError();
+}
+
+} // namespace Internal
+} // namespace qbs
diff --git a/src/lib/corelib/tools/launcherinterface.h b/src/lib/corelib/tools/launcherinterface.h
new file mode 100644
index 000000000..6ee0d80b5
--- /dev/null
+++ b/src/lib/corelib/tools/launcherinterface.h
@@ -0,0 +1,88 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qbs.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** 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 Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** 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-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QBS_LAUNCHERINTERFACE_H
+#define QBS_LAUNCHERINTERFACE_H
+
+#include <QtCore/qobject.h>
+
+QT_BEGIN_NAMESPACE
+class QProcess;
+class QLocalServer;
+QT_END_NAMESPACE
+
+namespace qbs {
+class ErrorInfo;
+namespace Internal {
+class LauncherSocket;
+
+class LauncherInterface : public QObject
+{
+ Q_OBJECT
+public:
+ static LauncherInterface &instance();
+ ~LauncherInterface();
+
+ static void startLauncher() { instance().doStart(); }
+ static void stopLauncher() { instance().doStop(); }
+ static LauncherSocket *socket() { return instance().m_socket; }
+
+signals:
+ void errorOccurred(const ErrorInfo &error);
+
+private:
+ LauncherInterface();
+
+ void doStart();
+ void doStop();
+ void handleNewConnection();
+ void handleProcessError();
+ void handleProcessFinished();
+ void handleProcessStderr();
+
+ QLocalServer * const m_server;
+ LauncherSocket * const m_socket;
+ QProcess * m_process = nullptr;
+ int m_startRequests = 0;
+};
+
+} // namespace Internal
+} // namespace qbs
+
+#endif // Include guard
diff --git a/src/lib/corelib/tools/launcherpackets.cpp b/src/lib/corelib/tools/launcherpackets.cpp
new file mode 100644
index 000000000..9c131bd69
--- /dev/null
+++ b/src/lib/corelib/tools/launcherpackets.cpp
@@ -0,0 +1,175 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qbs.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** 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 Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** 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-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "launcherpackets.h"
+
+#include <QtCore/qbytearray.h>
+#include <QtCore/qcoreapplication.h>
+#include <QtCore/qendian.h>
+
+namespace qbs {
+namespace Internal {
+
+LauncherPacket::~LauncherPacket() { }
+
+QByteArray LauncherPacket::serialize() const
+{
+ QByteArray data;
+ QDataStream stream(&data, QIODevice::WriteOnly);
+ stream << static_cast<int>(0) << static_cast<quint8>(type) << token;
+ doSerialize(stream);
+ stream.device()->reset();
+ stream << static_cast<int>(data.size() - sizeof(int));
+ return data;
+}
+
+void LauncherPacket::deserialize(const QByteArray &data)
+{
+ QDataStream stream(data);
+ doDeserialize(stream);
+}
+
+
+StartProcessPacket::StartProcessPacket(quintptr token)
+ : LauncherPacket(LauncherPacketType::StartProcess, token)
+{
+}
+
+void StartProcessPacket::doSerialize(QDataStream &stream) const
+{
+ stream << command << arguments << workingDir << env;
+}
+
+void StartProcessPacket::doDeserialize(QDataStream &stream)
+{
+ stream >> command >> arguments >> workingDir >> env;
+}
+
+
+StopProcessPacket::StopProcessPacket(quintptr token)
+ : LauncherPacket(LauncherPacketType::StopProcess, token)
+{
+}
+
+void StopProcessPacket::doSerialize(QDataStream &stream) const
+{
+ Q_UNUSED(stream);
+}
+
+void StopProcessPacket::doDeserialize(QDataStream &stream)
+{
+ Q_UNUSED(stream);
+}
+
+
+ProcessErrorPacket::ProcessErrorPacket(quintptr token)
+ : LauncherPacket(LauncherPacketType::ProcessError, token)
+{
+}
+
+void ProcessErrorPacket::doSerialize(QDataStream &stream) const
+{
+ stream << static_cast<quint8>(error) << errorString;
+}
+
+void ProcessErrorPacket::doDeserialize(QDataStream &stream)
+{
+ quint8 e;
+ stream >> e;
+ error = static_cast<QProcess::ProcessError>(e);
+ stream >> errorString;
+}
+
+
+ProcessFinishedPacket::ProcessFinishedPacket(quintptr token)
+ : LauncherPacket(LauncherPacketType::ProcessFinished, token)
+{
+}
+
+void ProcessFinishedPacket::doSerialize(QDataStream &stream) const
+{
+ stream << errorString << stdOut << stdErr
+ << static_cast<quint8>(exitStatus) << static_cast<quint8>(error)
+ << exitCode;
+}
+
+void ProcessFinishedPacket::doDeserialize(QDataStream &stream)
+{
+ stream >> errorString >> stdOut >> stdErr;
+ quint8 val;
+ stream >> val;
+ exitStatus = static_cast<QProcess::ExitStatus>(val);
+ stream >> val;
+ error = static_cast<QProcess::ProcessError>(val);
+ stream >> exitCode;
+}
+
+ShutdownPacket::ShutdownPacket() : LauncherPacket(LauncherPacketType::Shutdown, 0) { }
+void ShutdownPacket::doSerialize(QDataStream &stream) const { Q_UNUSED(stream); }
+void ShutdownPacket::doDeserialize(QDataStream &stream) { Q_UNUSED(stream); }
+
+void PacketParser::setDevice(QIODevice *device)
+{
+ m_stream.setDevice(device);
+ m_sizeOfNextPacket = -1;
+}
+
+bool PacketParser::parse()
+{
+ static const int commonPayloadSize = static_cast<int>(1 + sizeof LauncherPacket::token);
+ if (m_sizeOfNextPacket == -1) {
+ if (m_stream.device()->bytesAvailable() < static_cast<int>(sizeof m_sizeOfNextPacket))
+ return false;
+ m_stream >> m_sizeOfNextPacket;
+ if (m_sizeOfNextPacket < commonPayloadSize)
+ throw InvalidPacketSizeException(m_sizeOfNextPacket);
+ }
+ if (m_stream.device()->bytesAvailable() < m_sizeOfNextPacket)
+ return false;
+ quint8 type;
+ m_stream >> type;
+ m_type = static_cast<LauncherPacketType>(type);
+ m_stream >> m_token;
+ m_packetData = m_stream.device()->read(m_sizeOfNextPacket - commonPayloadSize);
+ m_sizeOfNextPacket = -1;
+ return true;
+}
+
+} // namespace Internal
+} // namespace qbs
diff --git a/src/lib/corelib/tools/launcherpackets.h b/src/lib/corelib/tools/launcherpackets.h
new file mode 100644
index 000000000..0988761e7
--- /dev/null
+++ b/src/lib/corelib/tools/launcherpackets.h
@@ -0,0 +1,178 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qbs.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** 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 Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** 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-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QBS_LAUNCHERPACKETS_H
+#define QBS_LAUNCHERPACKETS_H
+
+#include <QtCore/qdatastream.h>
+#include <QtCore/qprocess.h>
+#include <QtCore/qstringlist.h>
+
+QT_BEGIN_NAMESPACE
+class QByteArray;
+QT_END_NAMESPACE
+
+namespace qbs {
+namespace Internal {
+
+enum class LauncherPacketType {
+ Shutdown, StartProcess, StopProcess, ProcessError, ProcessFinished
+};
+
+class PacketParser
+{
+public:
+ class InvalidPacketSizeException
+ {
+ public:
+ InvalidPacketSizeException(int size) : size(size) { }
+ const int size;
+ };
+
+ void setDevice(QIODevice *device);
+ bool parse();
+ LauncherPacketType type() const { return m_type; }
+ quintptr token() const { return m_token; }
+ const QByteArray &packetData() const { return m_packetData; }
+
+private:
+ QDataStream m_stream;
+ LauncherPacketType m_type;
+ quintptr m_token;
+ QByteArray m_packetData;
+ int m_sizeOfNextPacket = -1;
+};
+
+class LauncherPacket
+{
+public:
+ virtual ~LauncherPacket();
+
+ template<class Packet> static Packet extractPacket(quintptr token, const QByteArray &data)
+ {
+ Packet p(token);
+ p.deserialize(data);
+ return p;
+ }
+
+ QByteArray serialize() const;
+ void deserialize(const QByteArray &data);
+
+ const LauncherPacketType type;
+ const quintptr token;
+
+protected:
+ LauncherPacket(LauncherPacketType type, quintptr token) : type(type), token(token) { }
+
+private:
+ virtual void doSerialize(QDataStream &stream) const = 0;
+ virtual void doDeserialize(QDataStream &stream) = 0;
+};
+
+class StartProcessPacket : public LauncherPacket
+{
+public:
+ StartProcessPacket(quintptr token);
+
+ QString command;
+ QStringList arguments;
+ QString workingDir;
+ QStringList env;
+
+private:
+ void doSerialize(QDataStream &stream) const override;
+ void doDeserialize(QDataStream &stream) override;
+};
+
+class StopProcessPacket : public LauncherPacket
+{
+public:
+ StopProcessPacket(quintptr token);
+
+private:
+ void doSerialize(QDataStream &stream) const override;
+ void doDeserialize(QDataStream &stream) override;
+};
+
+class ShutdownPacket : public LauncherPacket
+{
+public:
+ ShutdownPacket();
+
+private:
+ void doSerialize(QDataStream &stream) const override;
+ void doDeserialize(QDataStream &stream) override;
+};
+
+class ProcessErrorPacket : public LauncherPacket
+{
+public:
+ ProcessErrorPacket(quintptr token);
+
+ QProcess::ProcessError error;
+ QString errorString;
+
+private:
+ void doSerialize(QDataStream &stream) const override;
+ void doDeserialize(QDataStream &stream) override;
+};
+
+class ProcessFinishedPacket : public LauncherPacket
+{
+public:
+ ProcessFinishedPacket(quintptr token);
+
+ QString errorString;
+ QByteArray stdOut;
+ QByteArray stdErr;
+ QProcess::ExitStatus exitStatus;
+ QProcess::ProcessError error;
+ int exitCode;
+
+private:
+ void doSerialize(QDataStream &stream) const override;
+ void doDeserialize(QDataStream &stream) override;
+};
+
+} // namespace Internal
+} // namespace qbs
+
+Q_DECLARE_METATYPE(qbs::Internal::LauncherPacketType);
+
+#endif // Include guard
diff --git a/src/lib/corelib/tools/launchersocket.cpp b/src/lib/corelib/tools/launchersocket.cpp
new file mode 100644
index 000000000..fe809601e
--- /dev/null
+++ b/src/lib/corelib/tools/launchersocket.cpp
@@ -0,0 +1,133 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qbs.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** 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 Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** 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-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "launchersocket.h"
+
+#include "qbsassert.h"
+
+#include <logging/translator.h>
+
+#include <QtCore/qtimer.h>
+#include <QtNetwork/qlocalsocket.h>
+
+namespace qbs {
+namespace Internal {
+
+LauncherSocket::LauncherSocket(QObject *parent) : QObject(parent)
+{
+ qRegisterMetaType<qbs::Internal::LauncherPacketType>();
+ qRegisterMetaType<quintptr>("quintptr");
+}
+
+void LauncherSocket::sendData(const QByteArray &data)
+{
+ QMutexLocker locker(&m_requestsMutex);
+ m_requests << data;
+ if (m_requests.count() == 1)
+ QTimer::singleShot(0, this, &LauncherSocket::handleRequests);
+}
+
+void LauncherSocket::shutdown()
+{
+ m_socket->disconnect();
+ m_socket->write(ShutdownPacket().serialize());
+ m_socket->waitForBytesWritten(1000);
+ m_socket->deleteLater();
+ m_socket = nullptr;
+}
+
+void LauncherSocket::setSocket(QLocalSocket *socket)
+{
+ QBS_ASSERT(!m_socket, return);
+ m_socket = socket;
+ m_packetParser.setDevice(m_socket);
+ connect(m_socket,
+ static_cast<void(QLocalSocket::*)(QLocalSocket::LocalSocketError)>(&QLocalSocket::error),
+ this, &LauncherSocket::handleSocketError);
+ connect(m_socket, &QLocalSocket::readyRead,
+ this, &LauncherSocket::handleSocketDataAvailable);
+ emit ready();
+}
+
+void LauncherSocket::handleSocketError()
+{
+ handleError(Tr::tr("Socket error: %1").arg(m_socket->errorString()));
+}
+
+void LauncherSocket::handleSocketDataAvailable()
+{
+ 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;
+ }
+ switch (m_packetParser.type()) {
+ case LauncherPacketType::ProcessError:
+ case LauncherPacketType::ProcessFinished:
+ emit packetArrived(m_packetParser.type(), m_packetParser.token(),
+ m_packetParser.packetData());
+ break;
+ default:
+ handleError(Tr::tr("Internal protocol error: invalid packet type %1.")
+ .arg(static_cast<int>(m_packetParser.type())));
+ return;
+ }
+ handleSocketDataAvailable();
+}
+
+void LauncherSocket::handleError(const QString &error)
+{
+ m_socket->disconnect();
+ m_socket->deleteLater();
+ m_socket = nullptr;
+ emit errorOccurred(error);
+}
+
+void LauncherSocket::handleRequests()
+{
+ QMutexLocker locker(&m_requestsMutex);
+ for (auto it = m_requests.cbegin(); it != m_requests.cend(); ++it)
+ m_socket->write(*it);
+ m_requests.clear();
+}
+
+} // namespace Internal
+} // namespace qbs
diff --git a/src/lib/corelib/tools/launchersocket.h b/src/lib/corelib/tools/launchersocket.h
new file mode 100644
index 000000000..130d9f337
--- /dev/null
+++ b/src/lib/corelib/tools/launchersocket.h
@@ -0,0 +1,91 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qbs.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** 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 Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** 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-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QBS_LAUNCHERSOCKET_H
+#define QBS_LAUNCHERSOCKET_H
+
+#include "launcherpackets.h"
+
+#include <QtCore/qbytearraylist.h>
+#include <QtCore/qmutex.h>
+#include <QtCore/qobject.h>
+
+QT_BEGIN_NAMESPACE
+class QLocalSocket;
+QT_END_NAMESPACE
+
+namespace qbs {
+namespace Internal {
+class LauncherInterface;
+
+class LauncherSocket : public QObject
+{
+ Q_OBJECT
+ friend class LauncherInterface;
+public:
+ bool isReady() const { return m_socket; }
+ void sendData(const QByteArray &data);
+
+signals:
+ void ready();
+ void errorOccurred(const QString &error);
+ void packetArrived(qbs::Internal::LauncherPacketType type, quintptr token,
+ const QByteArray &payload);
+
+private:
+ LauncherSocket(QObject *parent);
+
+ void setSocket(QLocalSocket *socket);
+ void shutdown();
+
+ void handleSocketError();
+ void handleSocketDataAvailable();
+ void handleError(const QString &error);
+ void handleRequests();
+
+ QLocalSocket *m_socket = nullptr;
+ PacketParser m_packetParser;
+ QByteArrayList m_requests;
+ QMutex m_requestsMutex;
+};
+
+} // namespace Internal
+} // namespace qbs
+
+#endif // Include guard
diff --git a/src/lib/corelib/tools/qbsprocess.cpp b/src/lib/corelib/tools/qbsprocess.cpp
new file mode 100644
index 000000000..8495d72a0
--- /dev/null
+++ b/src/lib/corelib/tools/qbsprocess.cpp
@@ -0,0 +1,173 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qbs.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** 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 Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** 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-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qbsprocess.h"
+
+#include "launcherinterface.h"
+#include "launchersocket.h"
+#include "qbsassert.h"
+
+#include <logging/translator.h>
+
+#include <QtCore/qcoreapplication.h>
+#include <QtCore/qtimer.h>
+
+namespace qbs {
+namespace Internal {
+
+QbsProcess::QbsProcess(QObject *parent) : QObject(parent)
+{
+ connect(LauncherInterface::socket(), &LauncherSocket::ready,
+ this, &QbsProcess::handleSocketReady);
+ connect(LauncherInterface::socket(), &LauncherSocket::errorOccurred,
+ this, &QbsProcess::handleSocketError);
+ connect(LauncherInterface::socket(), &LauncherSocket::packetArrived,
+ this, &QbsProcess::handlePacket);
+}
+
+void QbsProcess::start(const QString &command, const QStringList &arguments)
+{
+ if (m_socketError) {
+ m_error = QProcess::FailedToStart;
+ emit error(m_error);
+ return;
+ }
+ m_command = command;
+ m_arguments = arguments;
+ m_state = QProcess::Starting;
+ if (LauncherInterface::socket()->isReady())
+ doStart();
+}
+
+void QbsProcess::doStart()
+{
+ m_state = QProcess::Running;
+ StartProcessPacket p(token());
+ p.command = m_command;
+ p.arguments = m_arguments;
+ p.env = m_environment.toStringList();
+ p.workingDir = m_workingDirectory;
+ sendPacket(p);
+}
+
+void QbsProcess::cancel()
+{
+ sendPacket(StopProcessPacket(token()));
+}
+
+QByteArray QbsProcess::readAllStandardOutput()
+{
+ return readAndClear(m_stdout);
+}
+
+QByteArray QbsProcess::readAllStandardError()
+{
+ return readAndClear(m_stderr);
+}
+
+void QbsProcess::sendPacket(const LauncherPacket &packet)
+{
+ LauncherInterface::socket()->sendData(packet.serialize());
+}
+
+QByteArray QbsProcess::readAndClear(QByteArray &data)
+{
+ const QByteArray tmp = data;
+ data.clear();
+ return tmp;
+}
+
+void QbsProcess::handlePacket(LauncherPacketType type, quintptr token, const QByteArray &payload)
+{
+ if (token != this->token())
+ return;
+ switch (type) {
+ case LauncherPacketType::ProcessError:
+ handleErrorPacket(payload);
+ break;
+ case LauncherPacketType::ProcessFinished:
+ handleFinishedPacket(payload);
+ break;
+ default:
+ QBS_ASSERT(false, break);
+ }
+}
+
+void QbsProcess::handleSocketReady()
+{
+ m_socketError = false;
+ if (m_state == QProcess::Starting)
+ doStart();
+}
+
+void QbsProcess::handleSocketError(const QString &message)
+{
+ m_socketError = true;
+ m_errorString = Tr::tr("Internal socket error: %1").arg(message);
+ if (m_state != QProcess::NotRunning) {
+ m_state = QProcess::NotRunning;
+ m_error = QProcess::FailedToStart;
+ emit error(m_error);
+ }
+}
+
+void QbsProcess::handleErrorPacket(const QByteArray &packetData)
+{
+ QBS_ASSERT(m_state != QProcess::NotRunning, return);
+ const auto packet = LauncherPacket::extractPacket<ProcessErrorPacket>(token(), packetData);
+ m_error = packet.error;
+ m_errorString = packet.errorString;
+ m_state = QProcess::NotRunning;
+ emit error(m_error);
+}
+
+void QbsProcess::handleFinishedPacket(const QByteArray &packetData)
+{
+ QBS_ASSERT(m_state == QProcess::Running, return);
+ m_state = QProcess::NotRunning;
+ const auto packet = LauncherPacket::extractPacket<ProcessFinishedPacket>(token(), packetData);
+ m_exitCode = packet.exitCode;
+ m_stdout = packet.stdOut;
+ m_stderr = packet.stdErr;
+ m_errorString = packet.errorString;
+ emit finished(m_exitCode);
+}
+
+} // namespace Internal
+} // namespace qbs
diff --git a/src/lib/corelib/tools/qbsprocess.h b/src/lib/corelib/tools/qbsprocess.h
new file mode 100644
index 000000000..9d2343010
--- /dev/null
+++ b/src/lib/corelib/tools/qbsprocess.h
@@ -0,0 +1,106 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qbs.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** 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 Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** 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-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QBS_QBSPROCESS_H
+#define QBS_QBSPROCESS_H
+
+#include "launcherpackets.h"
+
+#include <QtCore/qbytearray.h>
+#include <QtCore/qobject.h>
+#include <QtCore/qprocess.h>
+#include <QtCore/qstringlist.h>
+
+namespace qbs {
+namespace Internal {
+
+class QbsProcess : public QObject
+{
+ Q_OBJECT
+public:
+ explicit QbsProcess(QObject *parent = 0);
+
+ QProcess::ProcessState state() const { return m_state; }
+ void setProcessEnvironment(const QProcessEnvironment &env) { m_environment = env; }
+ void setWorkingDirectory(const QString &workingDir) { m_workingDirectory = workingDir; }
+ QString workingDirectory() const { return m_workingDirectory; }
+ void start(const QString &command, const QStringList &arguments);
+ void cancel();
+ QByteArray readAllStandardOutput();
+ QByteArray readAllStandardError();
+ int exitCode() const { return m_exitCode; }
+ QProcess::ProcessError error() const { return m_error; }
+ QString errorString() const { return m_errorString; }
+
+signals:
+ void error(QProcess::ProcessError error);
+ void finished(int exitCode);
+
+private:
+ void doStart();
+ void sendPacket(const LauncherPacket &packet);
+ QByteArray readAndClear(QByteArray &data);
+
+ void handleSocketError(const QString &message);
+ void handlePacket(qbs::Internal::LauncherPacketType type, quintptr token,
+ const QByteArray &payload);
+ void handleErrorPacket(const QByteArray &packetData);
+ void handleFinishedPacket(const QByteArray &packetData);
+ void handleSocketReady();
+
+ quintptr token() const { return reinterpret_cast<quintptr>(this); }
+
+ QString m_command;
+ QStringList m_arguments;
+ QProcessEnvironment m_environment;
+ QString m_workingDirectory;
+ QByteArray m_stdout;
+ QByteArray m_stderr;
+ QString m_errorString;
+ QProcess::ProcessError m_error = QProcess::UnknownError;
+ QProcess::ProcessState m_state = QProcess::NotRunning;
+ int m_exitCode;
+ int m_connectionAttempts = 0;
+ bool m_socketError = false;
+};
+
+} // namespace Internal
+} // namespace qbs
+
+#endif // QBSPROCESS_H
diff --git a/src/lib/corelib/tools/tools.pri b/src/lib/corelib/tools/tools.pri
index 337deb4a3..a9c8c8bc0 100644
--- a/src/lib/corelib/tools/tools.pri
+++ b/src/lib/corelib/tools/tools.pri
@@ -15,6 +15,9 @@ HEADERS += \
$$PWD/generateoptions.h \
$$PWD/id.h \
$$PWD/jsliterals.h \
+ $$PWD/launcherinterface.h \
+ $$PWD/launcherpackets.h \
+ $$PWD/launchersocket.h \
$$PWD/msvcinfo.h \
$$PWD/persistence.h \
$$PWD/scannerpluginmanager.h \
@@ -31,6 +34,7 @@ HEADERS += \
$$PWD/processutils.h \
$$PWD/progressobserver.h \
$$PWD/projectgeneratormanager.h \
+ $$PWD/qbsprocess.h \
$$PWD/shellutils.h \
$$PWD/toolchains.h \
$$PWD/hostosinfo.h \
@@ -61,6 +65,9 @@ SOURCES += \
$$PWD/generateoptions.cpp \
$$PWD/id.cpp \
$$PWD/jsliterals.cpp \
+ $$PWD/launcherinterface.cpp \
+ $$PWD/launcherpackets.cpp \
+ $$PWD/launchersocket.cpp \
$$PWD/msvcinfo.cpp \
$$PWD/persistence.cpp \
$$PWD/scannerpluginmanager.cpp \
@@ -74,6 +81,7 @@ SOURCES += \
$$PWD/profiling.cpp \
$$PWD/progressobserver.cpp \
$$PWD/projectgeneratormanager.cpp \
+ $$PWD/qbsprocess.cpp \
$$PWD/shellutils.cpp \
$$PWD/buildoptions.cpp \
$$PWD/installoptions.cpp \