ProjectExplorer: Introduce generic application runner.

This class aims to be a flexible worker class for SSH-based
run controls. It supersedes AbstractRemoteLinuxApplicationRunner
as well as all of its derived classes, while having no
RemoteLinux dependencies itself.

Change-Id: If24f03a32126b36fc3d0b253a1615ad0af5f2b46
Reviewed-by: hjk <qthjk@ovi.com>
This commit is contained in:
Christian Kandeler
2012-07-26 09:02:34 +02:00
committed by hjk
parent 93756975e1
commit 1d85d8e706
40 changed files with 1063 additions and 1753 deletions

View File

@@ -29,8 +29,8 @@
#include "remotelinuxdebugsupport.h"
#include "remotelinuxapplicationrunner.h"
#include "remotelinuxrunconfiguration.h"
#include "remotelinuxutils.h"
#include <debugger/debuggerengine.h>
#include <debugger/debuggerstartparameters.h>
@@ -42,6 +42,8 @@
#include <projectexplorer/project.h>
#include <projectexplorer/target.h>
#include <projectexplorer/toolchain.h>
#include <projectexplorer/devicesupport/deviceapplicationrunner.h>
#include <utils/portlist.h>
#include <utils/qtcassert.h>
#include <QPointer>
@@ -54,19 +56,23 @@ using namespace ProjectExplorer;
namespace RemoteLinux {
namespace Internal {
namespace {
enum State { Inactive, StartingRunner, StartingRemoteProcess, Debugging };
enum State { Inactive, GatheringPorts, StartingRunner, Debugging };
} // anonymous namespace
class AbstractRemoteLinuxDebugSupportPrivate
class LinuxDeviceDebugSupportPrivate
{
public:
AbstractRemoteLinuxDebugSupportPrivate(RunConfiguration *runConfig,
LinuxDeviceDebugSupportPrivate(const RemoteLinuxRunConfiguration *runConfig,
DebuggerEngine *engine)
: engine(engine),
qmlDebugging(runConfig->debuggerAspect()->useQmlDebugger()),
cppDebugging(runConfig->debuggerAspect()->useCppDebugger()),
state(Inactive),
gdbServerPort(-1), qmlPort(-1)
gdbServerPort(-1), qmlPort(-1),
device(DeviceProfileInformation::device(runConfig->target()->profile())),
remoteFilePath(runConfig->remoteExecutableFilePath()),
arguments(runConfig->arguments()),
commandPrefix(runConfig->commandPrefix())
{
}
@@ -77,21 +83,20 @@ public:
State state;
int gdbServerPort;
int qmlPort;
};
class RemoteLinuxDebugSupportPrivate
{
public:
RemoteLinuxDebugSupportPrivate(RemoteLinuxRunConfiguration *runConfig) : runner(runConfig) {}
GenericRemoteLinuxApplicationRunner runner;
DeviceApplicationRunner appRunner;
DeviceUsedPortsGatherer portsGatherer;
const ProjectExplorer::IDevice::ConstPtr device;
Utils::PortList portList;
const QString remoteFilePath;
const QString arguments;
const QString commandPrefix;
};
} // namespace Internal
using namespace Internal;
DebuggerStartParameters AbstractRemoteLinuxDebugSupport::startParameters(const RemoteLinuxRunConfiguration *runConfig)
DebuggerStartParameters LinuxDeviceDebugSupport::startParameters(const RemoteLinuxRunConfiguration *runConfig)
{
DebuggerStartParameters params;
Target *target = runConfig->target();
@@ -130,39 +135,90 @@ DebuggerStartParameters AbstractRemoteLinuxDebugSupport::startParameters(const R
return params;
}
AbstractRemoteLinuxDebugSupport::AbstractRemoteLinuxDebugSupport(RunConfiguration *runConfig,
LinuxDeviceDebugSupport::LinuxDeviceDebugSupport(RunConfiguration *runConfig,
DebuggerEngine *engine)
: QObject(engine), d(new AbstractRemoteLinuxDebugSupportPrivate(runConfig, engine))
: QObject(engine),
d(new LinuxDeviceDebugSupportPrivate(static_cast<RemoteLinuxRunConfiguration *>(runConfig), engine))
{
connect(d->engine, SIGNAL(requestRemoteSetup()), this, SLOT(handleRemoteSetupRequested()));
}
AbstractRemoteLinuxDebugSupport::~AbstractRemoteLinuxDebugSupport()
LinuxDeviceDebugSupport::~LinuxDeviceDebugSupport()
{
setFinished();
delete d;
}
void AbstractRemoteLinuxDebugSupport::showMessage(const QString &msg, int channel)
void LinuxDeviceDebugSupport::setApplicationRunnerPreRunAction(DeviceApplicationHelperAction *action)
{
if (d->engine)
d->appRunner.setPreRunAction(action);
}
void LinuxDeviceDebugSupport::setApplicationRunnerPostRunAction(DeviceApplicationHelperAction *action)
{
d->appRunner.setPostRunAction(action);
}
void LinuxDeviceDebugSupport::showMessage(const QString &msg, int channel)
{
if (d->state != Inactive && d->engine)
d->engine->showMessage(msg, channel);
}
void AbstractRemoteLinuxDebugSupport::handleRemoteSetupRequested()
void LinuxDeviceDebugSupport::handleRemoteSetupRequested()
{
QTC_ASSERT(d->state == Inactive, return);
d->state = StartingRunner;
showMessage(tr("Preparing remote side...\n"), AppStuff);
disconnect(runner(), 0, this, 0);
connect(runner(), SIGNAL(error(QString)), this, SLOT(handleSshError(QString)));
connect(runner(), SIGNAL(readyForExecution()), this, SLOT(startExecution()));
connect(runner(), SIGNAL(reportProgress(QString)), this, SLOT(handleProgressReport(QString)));
runner()->start();
d->state = GatheringPorts;
showMessage(tr("Checking available ports...\n"), LogStatus);
connect(&d->portsGatherer, SIGNAL(error(QString)), SLOT(handlePortsGathererError(QString)));
connect(&d->portsGatherer, SIGNAL(portListReady()), SLOT(handlePortListReady()));
d->portsGatherer.start(d->device);
}
void AbstractRemoteLinuxDebugSupport::handleSshError(const QString &error)
void LinuxDeviceDebugSupport::handlePortsGathererError(const QString &message)
{
QTC_ASSERT(d->state == GatheringPorts, return);
handleAdapterSetupFailed(message);
}
void LinuxDeviceDebugSupport::handlePortListReady()
{
QTC_ASSERT(d->state == GatheringPorts, return);
d->portList = d->device->freePorts();
startExecution();
}
void LinuxDeviceDebugSupport::startExecution()
{
QTC_ASSERT(d->state == GatheringPorts, return);
if (d->cppDebugging && !setPort(d->gdbServerPort))
return;
if (d->qmlDebugging && !setPort(d->qmlPort))
return;
d->state = StartingRunner;
d->gdbserverOutput.clear();
connect(&d->appRunner, SIGNAL(remoteStderr(QByteArray)),
SLOT(handleRemoteErrorOutput(QByteArray)));
connect(&d->appRunner, SIGNAL(remoteStdout(QByteArray)), SLOT(handleRemoteOutput(QByteArray)));
if (d->qmlDebugging && !d->cppDebugging)
connect(&d->appRunner, SIGNAL(remoteProcessStarted()), SLOT(handleRemoteProcessStarted()));
QString args = d->arguments;
if (d->qmlDebugging)
args += QString::fromLocal8Bit(" -qmljsdebugger=port:%1,block").arg(d->qmlPort);
const QString remoteCommandLine = (d->qmlDebugging && !d->cppDebugging)
? QString::fromLatin1("%1 %2 %3").arg(d->commandPrefix).arg(d->remoteFilePath).arg(args)
: QString::fromLatin1("%1 gdbserver :%2 %3 %4").arg(d->commandPrefix)
.arg(d->gdbServerPort).arg(d->remoteFilePath).arg(args);
connect(&d->appRunner, SIGNAL(finished(bool)), SLOT(handleAppRunnerFinished(bool)));
d->appRunner.start(d->device, remoteCommandLine.toUtf8());
}
void LinuxDeviceDebugSupport::handleAppRunnerError(const QString &error)
{
if (d->state == Debugging) {
showMessage(error, AppError);
@@ -173,49 +229,7 @@ void AbstractRemoteLinuxDebugSupport::handleSshError(const QString &error)
}
}
void AbstractRemoteLinuxDebugSupport::startExecution()
{
if (d->state == Inactive)
return;
QTC_ASSERT(d->state == StartingRunner, return);
if (d->cppDebugging && !setPort(d->gdbServerPort))
return;
if (d->qmlDebugging && !setPort(d->qmlPort))
return;
d->state = StartingRemoteProcess;
d->gdbserverOutput.clear();
connect(runner(), SIGNAL(remoteErrorOutput(QByteArray)), this,
SLOT(handleRemoteErrorOutput(QByteArray)));
connect(runner(), SIGNAL(remoteOutput(QByteArray)), this,
SLOT(handleRemoteOutput(QByteArray)));
if (d->qmlDebugging && !d->cppDebugging) {
connect(runner(), SIGNAL(remoteProcessStarted()),
SLOT(handleRemoteProcessStarted()));
}
const QString &remoteExe = runner()->remoteExecutable();
QString args = runner()->arguments();
if (d->qmlDebugging) {
args += QString::fromLatin1(" -qmljsdebugger=port:%1,block")
.arg(d->qmlPort);
}
const QHostAddress peerAddress = runner()->connection()->connectionInfo().peerAddress;
QString peerAddressString = peerAddress.toString();
if (peerAddress.protocol() == QAbstractSocket::IPv6Protocol)
peerAddressString.prepend(QLatin1Char('[')).append(QLatin1Char(']'));
const QString remoteCommandLine = (d->qmlDebugging && !d->cppDebugging)
? QString::fromLatin1("%1 %2 %3").arg(runner()->commandPrefix()).arg(remoteExe).arg(args)
: QString::fromLatin1("%1 gdbserver %5:%2 %3 %4").arg(runner()->commandPrefix())
.arg(d->gdbServerPort).arg(remoteExe).arg(args).arg(peerAddressString);
connect(runner(), SIGNAL(remoteProcessFinished(qint64)),
SLOT(handleRemoteProcessFinished(qint64)));
runner()->startExecution(remoteCommandLine.toUtf8());
}
void AbstractRemoteLinuxDebugSupport::handleRemoteProcessFinished(qint64 exitCode)
void LinuxDeviceDebugSupport::handleAppRunnerFinished(bool success)
{
if (!d->engine || d->state == Inactive)
return;
@@ -224,39 +238,35 @@ void AbstractRemoteLinuxDebugSupport::handleRemoteProcessFinished(qint64 exitCod
// The QML engine does not realize on its own that the application has finished.
if (d->qmlDebugging && !d->cppDebugging)
d->engine->quitDebugger();
else if (exitCode != 0)
else if (!success)
d->engine->notifyInferiorIll();
} else {
const QString errorMsg = (d->qmlDebugging && !d->cppDebugging)
? tr("Remote application failed with exit code %1.").arg(exitCode)
: tr("The gdbserver process closed unexpectedly.");
d->engine->notifyEngineRemoteSetupFailed(errorMsg);
d->engine->notifyEngineRemoteSetupFailed(tr("Debugging failed."));
}
}
void AbstractRemoteLinuxDebugSupport::handleDebuggingFinished()
void LinuxDeviceDebugSupport::handleDebuggingFinished()
{
setFinished();
}
void AbstractRemoteLinuxDebugSupport::handleRemoteOutput(const QByteArray &output)
void LinuxDeviceDebugSupport::handleRemoteOutput(const QByteArray &output)
{
QTC_ASSERT(d->state == Inactive || d->state == Debugging, return);
showMessage(QString::fromUtf8(output), AppOutput);
}
void AbstractRemoteLinuxDebugSupport::handleRemoteErrorOutput(const QByteArray &output)
void LinuxDeviceDebugSupport::handleRemoteErrorOutput(const QByteArray &output)
{
QTC_ASSERT(d->state == Inactive || d->state == StartingRemoteProcess || d->state == Debugging,
return);
QTC_ASSERT(d->state != GatheringPorts, return);
if (!d->engine)
return;
showMessage(QString::fromUtf8(output), AppOutput);
if (d->state == StartingRemoteProcess && d->cppDebugging) {
showMessage(QString::fromUtf8(output), AppError);
if (d->state == StartingRunner && d->cppDebugging) {
d->gdbserverOutput += output;
if (d->gdbserverOutput.contains("Listening on port")) {
handleAdapterSetupDone();
@@ -265,42 +275,45 @@ void AbstractRemoteLinuxDebugSupport::handleRemoteErrorOutput(const QByteArray &
}
}
void AbstractRemoteLinuxDebugSupport::handleProgressReport(const QString &progressOutput)
void LinuxDeviceDebugSupport::handleProgressReport(const QString &progressOutput)
{
showMessage(progressOutput + QLatin1Char('\n'), AppStuff);
showMessage(progressOutput + QLatin1Char('\n'), LogStatus);
}
void AbstractRemoteLinuxDebugSupport::handleAdapterSetupFailed(const QString &error)
void LinuxDeviceDebugSupport::handleAdapterSetupFailed(const QString &error)
{
setFinished();
d->engine->notifyEngineRemoteSetupFailed(tr("Initial setup failed: %1").arg(error));
}
void AbstractRemoteLinuxDebugSupport::handleAdapterSetupDone()
void LinuxDeviceDebugSupport::handleAdapterSetupDone()
{
d->state = Debugging;
d->engine->notifyEngineRemoteSetupDone(d->gdbServerPort, d->qmlPort);
}
void AbstractRemoteLinuxDebugSupport::handleRemoteProcessStarted()
void LinuxDeviceDebugSupport::handleRemoteProcessStarted()
{
Q_ASSERT(d->qmlDebugging && !d->cppDebugging);
QTC_ASSERT(d->state == StartingRemoteProcess, return);
QTC_ASSERT(d->qmlDebugging && !d->cppDebugging, return);
QTC_ASSERT(d->state == StartingRunner, return);
handleAdapterSetupDone();
}
void AbstractRemoteLinuxDebugSupport::setFinished()
void LinuxDeviceDebugSupport::setFinished()
{
if (d->state == Inactive)
return;
d->portsGatherer.disconnect(this);
d->appRunner.disconnect(this);
if (d->state == StartingRunner)
d->appRunner.stop(RemoteLinuxUtils::killApplicationCommandLine(d->remoteFilePath).toUtf8());
d->state = Inactive;
runner()->stop();
}
bool AbstractRemoteLinuxDebugSupport::setPort(int &port)
bool LinuxDeviceDebugSupport::setPort(int &port)
{
port = runner()->usedPortsGatherer()->getNextFreePort(runner()->freePorts());
port = d->portsGatherer.getNextFreePort(&d->portList);
if (port == -1) {
handleAdapterSetupFailed(tr("Not enough free ports on device for debugging."));
return false;
@@ -308,22 +321,4 @@ bool AbstractRemoteLinuxDebugSupport::setPort(int &port)
return true;
}
RemoteLinuxDebugSupport::RemoteLinuxDebugSupport(RemoteLinuxRunConfiguration *runConfig,
DebuggerEngine *engine)
: AbstractRemoteLinuxDebugSupport(runConfig, engine),
d(new RemoteLinuxDebugSupportPrivate(runConfig))
{
}
RemoteLinuxDebugSupport::~RemoteLinuxDebugSupport()
{
delete d;
}
AbstractRemoteLinuxApplicationRunner *RemoteLinuxDebugSupport::runner() const
{
return &d->runner;
}
} // namespace RemoteLinux