RemoteLinux: Refactor application runner.

This removes the remaining Maemo dependencies from the generic case.

Change-Id: If4f3e6c3b3768769af4f753c2d667df271de7ed4
Reviewed-on: http://codereview.qt.nokia.com/1823
Reviewed-by: Christian Kandeler <christian.kandeler@nokia.com>
This commit is contained in:
Christian Kandeler
2011-07-19 14:30:06 +02:00
parent a83d463fa5
commit 410d5bd0cf
16 changed files with 364 additions and 307 deletions

View File

@@ -146,7 +146,7 @@ void RemoteLinuxQmlProfilerRunner::handleRemoteProcessStarted()
void RemoteLinuxQmlProfilerRunner::handleRemoteProcessFinished(qint64 exitCode) void RemoteLinuxQmlProfilerRunner::handleRemoteProcessFinished(qint64 exitCode)
{ {
if (exitCode != RemoteLinuxApplicationRunner::InvalidExitCode) { if (exitCode != AbstractRemoteLinuxApplicationRunner::InvalidExitCode) {
appendMessage(tr("Finished running remote process. Exit code was %1.\n") appendMessage(tr("Finished running remote process. Exit code was %1.\n")
.arg(exitCode), Utils::NormalMessageFormat); .arg(exitCode), Utils::NormalMessageFormat);
} }
@@ -159,7 +159,7 @@ void RemoteLinuxQmlProfilerRunner::handleProgressReport(const QString &progressS
appendMessage(progressString + QLatin1Char('\n'), Utils::NormalMessageFormat); appendMessage(progressString + QLatin1Char('\n'), Utils::NormalMessageFormat);
} }
RemoteLinuxApplicationRunner *RemoteLinuxQmlProfilerRunner::runner() const AbstractRemoteLinuxApplicationRunner *RemoteLinuxQmlProfilerRunner::runner() const
{ {
if (!m_runControl) if (!m_runControl)
return 0; return 0;

View File

@@ -65,7 +65,7 @@ private slots:
void handleProgressReport(const QString &progressString); void handleProgressReport(const QString &progressString);
private: private:
RemoteLinux::RemoteLinuxApplicationRunner *runner() const; RemoteLinux::AbstractRemoteLinuxApplicationRunner *runner() const;
int m_port; int m_port;
RemoteLinux::AbstractRemoteLinuxRunControl *m_runControl; RemoteLinux::AbstractRemoteLinuxRunControl *m_runControl;

View File

@@ -47,7 +47,7 @@ MaemoDebugSupport::~MaemoDebugSupport()
{ {
} }
RemoteLinuxApplicationRunner *MaemoDebugSupport::runner() const { return m_runner; } AbstractRemoteLinuxApplicationRunner *MaemoDebugSupport::runner() const { return m_runner; }
} // namespace Internal } // namespace Internal
} // namespace RemoteLinux } // namespace RemoteLinux

View File

@@ -47,7 +47,7 @@ public:
~MaemoDebugSupport(); ~MaemoDebugSupport();
private: private:
RemoteLinuxApplicationRunner *runner() const; AbstractRemoteLinuxApplicationRunner *runner() const;
MaemoSshRunner * const m_runner; MaemoSshRunner * const m_runner;
}; };

View File

@@ -38,7 +38,6 @@
#include <coreplugin/filemanager.h> #include <coreplugin/filemanager.h>
#include <extensionsystem/pluginmanager.h> #include <extensionsystem/pluginmanager.h>
#include <utils/ssh/sshconnection.h>
#include <qt4projectmanager/qt4projectmanagerconstants.h> #include <qt4projectmanager/qt4projectmanagerconstants.h>
#include <qtsupport/qtversionmanager.h> #include <qtsupport/qtversionmanager.h>
#include <qt4projectmanager/qt4target.h> #include <qt4projectmanager/qt4target.h>
@@ -191,23 +190,6 @@ QString MaemoGlobal::remoteSourceProfilesCommand()
return QString::fromAscii(remoteCall); return QString::fromAscii(remoteCall);
} }
QString MaemoGlobal::failedToConnectToServerMessage(const Utils::SshConnection::Ptr &connection,
const LinuxDeviceConfiguration::ConstPtr &deviceConfig)
{
QString errorMsg = tr("Could not connect to host: %1")
.arg(connection->errorString());
if (deviceConfig->type() == LinuxDeviceConfiguration::Emulator) {
if (connection->errorState() == Utils::SshTimeoutError
|| connection->errorState() == Utils::SshSocketError) {
errorMsg += tr("\nDid you start Qemu?");
}
} else if (connection->errorState() == Utils::SshTimeoutError) {
errorMsg += tr("\nIs the device connected and set up for network access?");
}
return errorMsg;
}
QString MaemoGlobal::deviceConfigurationName(const LinuxDeviceConfiguration::ConstPtr &devConf) QString MaemoGlobal::deviceConfigurationName(const LinuxDeviceConfiguration::ConstPtr &devConf)
{ {
return devConf ? devConf->name() : tr("(No device)"); return devConf ? devConf->name() : tr("(No device)");

View File

@@ -56,7 +56,6 @@ class QString;
QT_END_NAMESPACE QT_END_NAMESPACE
namespace QtSupport { class BaseQtVersion; } namespace QtSupport { class BaseQtVersion; }
namespace Utils { class SshConnection; }
namespace RemoteLinux { namespace RemoteLinux {
namespace Internal { namespace Internal {
@@ -110,8 +109,6 @@ public:
static QString remoteSudo(const QString &osType, const QString &uname); static QString remoteSudo(const QString &osType, const QString &uname);
static QString remoteCommandPrefix(const QString &osType); static QString remoteCommandPrefix(const QString &osType);
static QString remoteSourceProfilesCommand(); static QString remoteSourceProfilesCommand();
static QString failedToConnectToServerMessage(const QSharedPointer<Utils::SshConnection> &connection,
const QSharedPointer<const LinuxDeviceConfiguration> &deviceConfig);
static QString deviceConfigurationName(const QSharedPointer<const LinuxDeviceConfiguration> &devConf); static QString deviceConfigurationName(const QSharedPointer<const LinuxDeviceConfiguration> &devConf);
static PortList freePorts(const QSharedPointer<const LinuxDeviceConfiguration> &devConf, static PortList freePorts(const QSharedPointer<const LinuxDeviceConfiguration> &devConf,
const QtSupport::BaseQtVersion *qtVersion); const QtSupport::BaseQtVersion *qtVersion);

View File

@@ -61,7 +61,7 @@ void MaemoRunControl::handleMountDebugOutput(const QString &output)
appendMessage(output, Utils::StdErrFormatSameLine); appendMessage(output, Utils::StdErrFormatSameLine);
} }
RemoteLinuxApplicationRunner *MaemoRunControl::runner() const { return m_runner; } AbstractRemoteLinuxApplicationRunner *MaemoRunControl::runner() const { return m_runner; }
} // namespace Internal } // namespace Internal
} // namespace RemoteLinux } // namespace RemoteLinux

View File

@@ -53,7 +53,7 @@ private slots:
void handleMountDebugOutput(const QString &output); void handleMountDebugOutput(const QString &output);
private: private:
virtual RemoteLinuxApplicationRunner *runner() const; virtual AbstractRemoteLinuxApplicationRunner *runner() const;
MaemoSshRunner * const m_runner; MaemoSshRunner * const m_runner;
}; };

View File

@@ -38,7 +38,7 @@
#include "remotelinuxrunconfiguration.h" #include "remotelinuxrunconfiguration.h"
#include <qt4projectmanager/qt4buildconfiguration.h> #include <qt4projectmanager/qt4buildconfiguration.h>
#include <qtsupport/baseqtversion.h> #include <utils/qtcassert.h>
#define ASSERT_STATE(state) ASSERT_STATE_GENERIC(MountState, state, m_mountState) #define ASSERT_STATE(state) ASSERT_STATE_GENERIC(MountState, state, m_mountState)
@@ -49,7 +49,7 @@ namespace RemoteLinux {
namespace Internal { namespace Internal {
MaemoSshRunner::MaemoSshRunner(QObject *parent, MaemoRunConfiguration *runConfig) MaemoSshRunner::MaemoSshRunner(QObject *parent, MaemoRunConfiguration *runConfig)
: RemoteLinuxApplicationRunner(parent, runConfig), : AbstractRemoteLinuxApplicationRunner(parent, runConfig),
m_mounter(new MaemoRemoteMounter(this)), m_mounter(new MaemoRemoteMounter(this)),
m_mountSpecs(runConfig->remoteMounts()->mountSpecs()), m_mountSpecs(runConfig->remoteMounts()->mountSpecs()),
m_mountState(InactiveMountState) m_mountState(InactiveMountState)
@@ -71,7 +71,7 @@ MaemoSshRunner::~MaemoSshRunner() {}
bool MaemoSshRunner::canRun(QString &whyNot) const bool MaemoSshRunner::canRun(QString &whyNot) const
{ {
if (!RemoteLinuxApplicationRunner::canRun(whyNot)) if (!AbstractRemoteLinuxApplicationRunner::canRun(whyNot))
return false; return false;
if (devConfig()->type() == LinuxDeviceConfiguration::Emulator if (devConfig()->type() == LinuxDeviceConfiguration::Emulator
@@ -90,6 +90,12 @@ bool MaemoSshRunner::canRun(QString &whyNot) const
return true; return true;
} }
void MaemoSshRunner::doDeviceSetup()
{
QTC_ASSERT(m_mountState == InactiveMountState, return);
handleDeviceSetupDone(true);
}
void MaemoSshRunner::doAdditionalInitialCleanup() void MaemoSshRunner::doAdditionalInitialCleanup()
{ {
ASSERT_STATE(InactiveMountState); ASSERT_STATE(InactiveMountState);
@@ -107,7 +113,7 @@ void MaemoSshRunner::doAdditionalInitializations()
mount(); mount();
} }
void MaemoSshRunner::doAdditionalPostRunCleanup() void MaemoSshRunner::doPostRunCleanup()
{ {
ASSERT_STATE(Mounted); ASSERT_STATE(Mounted);
m_mountState = PostRunUnmounting; m_mountState = PostRunUnmounting;
@@ -203,6 +209,24 @@ void MaemoSshRunner::unmount()
} }
} }
QString MaemoSshRunner::killApplicationCommandLine() const
{
// Prevent pkill from matching our own pkill call.
QString pkillArg = remoteExecutable();
const int lastPos = pkillArg.count() - 1;
pkillArg.replace(lastPos, 1, QLatin1Char('[') + pkillArg.at(lastPos) + QLatin1Char(']'));
// Fremantle's busybox configuration is strange.
const char *killTemplate;
if (devConfig()->osType() == LinuxDeviceConfiguration::Maemo5OsType)
killTemplate = "pkill -f -%2 %1";
else
killTemplate = "pkill -%2 -f %1";
const QString niceKill = QString::fromLocal8Bit(killTemplate).arg(pkillArg).arg("SIGTERM");
const QString brutalKill = QString::fromLocal8Bit(killTemplate).arg(pkillArg).arg("SIGKILL");
return niceKill + QLatin1String("; sleep 1; ") + brutalKill;
}
} // namespace Internal } // namespace Internal
} // namespace RemoteLinux } // namespace RemoteLinux

View File

@@ -40,7 +40,7 @@ namespace RemoteLinux {
namespace Internal { namespace Internal {
class MaemoRemoteMounter; class MaemoRemoteMounter;
class MaemoSshRunner : public RemoteLinuxApplicationRunner class MaemoSshRunner : public AbstractRemoteLinuxApplicationRunner
{ {
Q_OBJECT Q_OBJECT
public: public:
@@ -59,10 +59,12 @@ private:
enum MountState { InactiveMountState, InitialUnmounting, Mounting, Mounted, PostRunUnmounting }; enum MountState { InactiveMountState, InitialUnmounting, Mounting, Mounted, PostRunUnmounting };
bool canRun(QString &whyNot) const; bool canRun(QString &whyNot) const;
void doDeviceSetup();
void doAdditionalInitialCleanup(); void doAdditionalInitialCleanup();
void doAdditionalInitializations(); void doAdditionalInitializations();
void doAdditionalPostRunCleanup(); void doPostRunCleanup();
void doAdditionalConnectionErrorHandling(); void doAdditionalConnectionErrorHandling();
QString killApplicationCommandLine() const;
void mount(); void mount();
void unmount(); void unmount();

View File

@@ -32,7 +32,6 @@
#include "remotelinuxapplicationrunner.h" #include "remotelinuxapplicationrunner.h"
#include "linuxdeviceconfiguration.h" #include "linuxdeviceconfiguration.h"
#include "maemoglobal.h"
#include "remotelinuxrunconfiguration.h" #include "remotelinuxrunconfiguration.h"
#include "maemousedportsgatherer.h" #include "maemousedportsgatherer.h"
@@ -43,52 +42,108 @@
#include <limits> #include <limits>
#define ASSERT_STATE(state) ASSERT_STATE_GENERIC(State, state, m_state)
using namespace Qt4ProjectManager; using namespace Qt4ProjectManager;
using namespace Utils; using namespace Utils;
namespace RemoteLinux { namespace RemoteLinux {
namespace Internal {
namespace {
enum State {
Inactive, SettingUpDevice, Connecting, PreRunCleaning, AdditionalPreRunCleaning,
GatheringPorts, AdditionalInitializing, ReadyForExecution, ProcessStarting, ProcessStarted,
PostRunCleaning
};
} // anonymous namespace
class AbstractRemoteLinuxApplicationRunnerPrivate
{
public:
AbstractRemoteLinuxApplicationRunnerPrivate(const RemoteLinuxRunConfiguration *runConfig)
: devConfig(runConfig->deviceConfig()),
remoteExecutable(runConfig->remoteExecutableFilePath()),
appArguments(runConfig->arguments()),
commandPrefix(runConfig->commandPrefix()),
initialFreePorts(runConfig->freePorts()),
stopRequested(false),
state(Inactive)
{
}
MaemoUsedPortsGatherer portsGatherer;
const LinuxDeviceConfiguration::ConstPtr devConfig;
const QString remoteExecutable;
const QString appArguments;
const QString commandPrefix;
const PortList initialFreePorts;
Utils::SshConnection::Ptr connection;
Utils::SshRemoteProcess::Ptr runner;
Utils::SshRemoteProcess::Ptr cleaner;
PortList freePorts;
int exitStatus;
bool stopRequested;
State state;
};
} // namespace Internal
using namespace Internal; using namespace Internal;
RemoteLinuxApplicationRunner::RemoteLinuxApplicationRunner(QObject *parent, AbstractRemoteLinuxApplicationRunner::AbstractRemoteLinuxApplicationRunner(QObject *parent,
RemoteLinuxRunConfiguration *runConfig) RemoteLinuxRunConfiguration *runConfig)
: QObject(parent), : QObject(parent), m_d(new AbstractRemoteLinuxApplicationRunnerPrivate(runConfig))
m_portsGatherer(new MaemoUsedPortsGatherer(this)),
m_devConfig(runConfig->deviceConfig()),
m_remoteExecutable(runConfig->remoteExecutableFilePath()),
m_appArguments(runConfig->arguments()),
m_commandPrefix(runConfig->commandPrefix()),
m_initialFreePorts(runConfig->freePorts()),
m_stopRequested(false),
m_state(Inactive)
{ {
// Prevent pkill from matching our own pkill call. connect(&m_d->portsGatherer, SIGNAL(error(QString)), SLOT(handlePortsGathererError(QString)));
QString pkillArg = m_remoteExecutable; connect(&m_d->portsGatherer, SIGNAL(portListReady()), SLOT(handleUsedPortsAvailable()));
const int lastPos = pkillArg.count() - 1;
pkillArg.replace(lastPos, 1, QLatin1Char('[') + pkillArg.at(lastPos) + QLatin1Char(']'));
m_procsToKill << pkillArg;
connect(m_portsGatherer, SIGNAL(error(QString)), SLOT(handlePortsGathererError(QString)));
connect(m_portsGatherer, SIGNAL(portListReady()), SLOT(handleUsedPortsAvailable()));
} }
RemoteLinuxApplicationRunner::~RemoteLinuxApplicationRunner() {} AbstractRemoteLinuxApplicationRunner::~AbstractRemoteLinuxApplicationRunner()
SshConnection::Ptr RemoteLinuxApplicationRunner::connection() const
{ {
return m_connection; delete m_d;
} }
LinuxDeviceConfiguration::ConstPtr RemoteLinuxApplicationRunner::devConfig() const SshConnection::Ptr AbstractRemoteLinuxApplicationRunner::connection() const
{ {
return m_devConfig; return m_d->connection;
} }
void RemoteLinuxApplicationRunner::start() LinuxDeviceConfiguration::ConstPtr AbstractRemoteLinuxApplicationRunner::devConfig() const
{ {
QTC_ASSERT(!m_stopRequested, return); return m_d->devConfig;
ASSERT_STATE(Inactive); }
const MaemoUsedPortsGatherer *AbstractRemoteLinuxApplicationRunner::usedPortsGatherer() const
{
return &m_d->portsGatherer;
}
PortList *AbstractRemoteLinuxApplicationRunner::freePorts()
{
return &m_d->freePorts;
}
QString AbstractRemoteLinuxApplicationRunner::remoteExecutable() const
{
return m_d->remoteExecutable;
}
QString AbstractRemoteLinuxApplicationRunner::arguments() const
{
return m_d->appArguments;
}
QString AbstractRemoteLinuxApplicationRunner::commandPrefix() const
{
return m_d->commandPrefix;
}
void AbstractRemoteLinuxApplicationRunner::start()
{
QTC_ASSERT(!m_d->stopRequested && m_d->state == Inactive, return);
QString errorMsg; QString errorMsg;
if (!canRun(errorMsg)) { if (!canRun(errorMsg)) {
@@ -96,23 +151,23 @@ void RemoteLinuxApplicationRunner::start()
return; return;
} }
setState(SettingUpDevice); m_d->state = SettingUpDevice;
doDeviceSetup(); doDeviceSetup();
} }
void RemoteLinuxApplicationRunner::stop() void AbstractRemoteLinuxApplicationRunner::stop()
{ {
if (m_stopRequested) if (m_d->stopRequested)
return; return;
switch (m_state) { switch (m_d->state) {
case Connecting: case Connecting:
setState(Inactive); setInactive();
emit remoteProcessFinished(InvalidExitCode); emit remoteProcessFinished(InvalidExitCode);
break; break;
case GatheringPorts: case GatheringPorts:
m_portsGatherer->stop(); m_d->portsGatherer.stop();
setState(Inactive); setInactive();
emit remoteProcessFinished(InvalidExitCode); emit remoteProcessFinished(InvalidExitCode);
break; break;
case SettingUpDevice: case SettingUpDevice:
@@ -121,16 +176,15 @@ void RemoteLinuxApplicationRunner::stop()
case AdditionalInitializing: case AdditionalInitializing:
case ProcessStarting: case ProcessStarting:
case PostRunCleaning: case PostRunCleaning:
case AdditionalPostRunCleaning: m_d->stopRequested = true; // TODO: We might need stopPreRunCleaning() etc. for the subclasses
m_stopRequested = true;
break; break;
case ReadyForExecution: case ReadyForExecution:
m_stopRequested = true; m_d->stopRequested = true;
setState(AdditionalPostRunCleaning); m_d->state = PostRunCleaning;
doAdditionalPostRunCleanup(); doPostRunCleanup();
break; break;
case ProcessStarted: case ProcessStarted:
m_stopRequested = true; m_d->stopRequested = true;
cleanup(); cleanup();
break; break;
case Inactive: case Inactive:
@@ -138,119 +192,98 @@ void RemoteLinuxApplicationRunner::stop()
} }
} }
void RemoteLinuxApplicationRunner::handleConnected() void AbstractRemoteLinuxApplicationRunner::handleConnected()
{ {
ASSERT_STATE(Connecting); QTC_ASSERT(m_d->state == Connecting, return);
if (m_stopRequested) {
if (m_d->stopRequested) {
emit remoteProcessFinished(InvalidExitCode); emit remoteProcessFinished(InvalidExitCode);
setState(Inactive); setInactive();
} else { } else {
setState(PreRunCleaning); m_d->state = PreRunCleaning;
cleanup(); cleanup();
} }
} }
void RemoteLinuxApplicationRunner::handleConnectionFailure() void AbstractRemoteLinuxApplicationRunner::handleConnectionFailure()
{ {
if (m_state == Inactive) { QTC_ASSERT(m_d->state != Inactive, return);
qWarning("Unexpected state %d in %s.", m_state, Q_FUNC_INFO);
return;
}
if (m_state != Connecting || m_state != PreRunCleaning) if (m_d->state != Connecting || m_d->state != PreRunCleaning)
doAdditionalConnectionErrorHandling(); doAdditionalConnectionErrorHandling();
const QString errorMsg = m_state == Connecting const QString errorMsg = m_d->state == Connecting
? MaemoGlobal::failedToConnectToServerMessage(m_connection, m_devConfig) ? tr("Could not connect to host: %1") : tr("Connection error: %1");
: tr("Connection error: %1").arg(m_connection->errorString()); emitError(errorMsg.arg(m_d->connection->errorString()));
emitError(errorMsg);
} }
void RemoteLinuxApplicationRunner::cleanup() void AbstractRemoteLinuxApplicationRunner::cleanup()
{ {
ASSERT_STATE(QList<State>() << PreRunCleaning << PostRunCleaning << ProcessStarted); QTC_ASSERT(m_d->state == PreRunCleaning
|| (m_d->state == ProcessStarted && m_d->stopRequested), return);
emit reportProgress(tr("Killing remote process(es)...")); emit reportProgress(tr("Killing remote process(es)..."));
m_d->cleaner = m_d->connection->createRemoteProcess(killApplicationCommandLine().toUtf8());
// Fremantle's busybox configuration is strange. connect(m_d->cleaner.data(), SIGNAL(closed(int)), SLOT(handleCleanupFinished(int)));
const char *killTemplate; m_d->cleaner->start();
if (m_devConfig->osType() == LinuxDeviceConfiguration::Maemo5OsType)
killTemplate = "pkill -f -%2 %1;";
else
killTemplate = "pkill -%2 -f %1;";
QString niceKill;
QString brutalKill;
foreach (const QString &proc, m_procsToKill) {
niceKill += QString::fromLocal8Bit(killTemplate).arg(proc).arg("SIGTERM");
brutalKill += QString::fromLocal8Bit(killTemplate).arg(proc).arg("SIGKILL");
}
QString remoteCall = niceKill + QLatin1String("sleep 1; ") + brutalKill;
remoteCall.remove(remoteCall.count() - 1, 1); // Get rid of trailing semicolon.
m_cleaner = m_connection->createRemoteProcess(remoteCall.toUtf8());
connect(m_cleaner.data(), SIGNAL(closed(int)), this,
SLOT(handleCleanupFinished(int)));
m_cleaner->start();
} }
void RemoteLinuxApplicationRunner::handleCleanupFinished(int exitStatus) void AbstractRemoteLinuxApplicationRunner::handleCleanupFinished(int exitStatus)
{ {
Q_ASSERT(exitStatus == SshRemoteProcess::FailedToStart Q_ASSERT(exitStatus == SshRemoteProcess::FailedToStart
|| exitStatus == SshRemoteProcess::KilledBySignal || exitStatus == SshRemoteProcess::KilledBySignal
|| exitStatus == SshRemoteProcess::ExitedNormally); || exitStatus == SshRemoteProcess::ExitedNormally);
ASSERT_STATE(QList<State>() << PreRunCleaning << PostRunCleaning << ProcessStarted << Inactive); QTC_ASSERT(m_d->state == PreRunCleaning
|| (m_d->state == ProcessStarted && m_d->stopRequested) || m_d->state == Inactive, return);
if (m_state == Inactive) if (m_d->state == Inactive)
return; return;
if (m_stopRequested && m_state == PreRunCleaning) { if (m_d->stopRequested && m_d->state == PreRunCleaning) {
setState(Inactive); setInactive();
emit remoteProcessFinished(InvalidExitCode); emit remoteProcessFinished(InvalidExitCode);
return; return;
} }
if (m_stopRequested || m_state == PostRunCleaning) { if (m_d->stopRequested) {
setState(AdditionalPostRunCleaning); m_d->state = PostRunCleaning;
doAdditionalPostRunCleanup(); doPostRunCleanup();
return; return;
} }
if (exitStatus != SshRemoteProcess::ExitedNormally) { if (exitStatus != SshRemoteProcess::ExitedNormally) {
emitError(tr("Initial cleanup failed: %1").arg(m_cleaner->errorString())); emitError(tr("Initial cleanup failed: %1").arg(m_d->cleaner->errorString()));
emit remoteProcessFinished(InvalidExitCode); emit remoteProcessFinished(InvalidExitCode);
return; return;
} }
setState(AdditionalPreRunCleaning); m_d->state = AdditionalPreRunCleaning;
doAdditionalInitialCleanup(); doAdditionalInitialCleanup();
} }
void RemoteLinuxApplicationRunner::startExecution(const QByteArray &remoteCall) void AbstractRemoteLinuxApplicationRunner::startExecution(const QByteArray &remoteCall)
{ {
ASSERT_STATE(ReadyForExecution); QTC_ASSERT(m_d->state == ReadyForExecution, return);
if (m_stopRequested) if (m_d->stopRequested)
return; return;
m_runner = m_connection->createRemoteProcess(remoteCall); m_d->runner = m_d->connection->createRemoteProcess(remoteCall);
connect(m_runner.data(), SIGNAL(started()), this, connect(m_d->runner.data(), SIGNAL(started()), SLOT(handleRemoteProcessStarted()));
SLOT(handleRemoteProcessStarted())); connect(m_d->runner.data(), SIGNAL(closed(int)), SLOT(handleRemoteProcessFinished(int)));
connect(m_runner.data(), SIGNAL(closed(int)), this, connect(m_d->runner.data(), SIGNAL(outputAvailable(QByteArray)),
SLOT(handleRemoteProcessFinished(int)));
connect(m_runner.data(), SIGNAL(outputAvailable(QByteArray)), this,
SIGNAL(remoteOutput(QByteArray))); SIGNAL(remoteOutput(QByteArray)));
connect(m_runner.data(), SIGNAL(errorOutputAvailable(QByteArray)), this, connect(m_d->runner.data(), SIGNAL(errorOutputAvailable(QByteArray)),
SIGNAL(remoteErrorOutput(QByteArray))); SIGNAL(remoteErrorOutput(QByteArray)));
setState(ProcessStarting); m_d->state = ProcessStarting;
m_runner->start(); m_d->runner->start();
} }
void RemoteLinuxApplicationRunner::handleRemoteProcessStarted() void AbstractRemoteLinuxApplicationRunner::handleRemoteProcessStarted()
{ {
ASSERT_STATE(ProcessStarting); QTC_ASSERT(m_d->state == ProcessStarting, return);
setState(ProcessStarted); m_d->state = ProcessStarted;
if (m_stopRequested) { if (m_d->stopRequested) {
cleanup(); cleanup();
return; return;
} }
@@ -259,80 +292,72 @@ void RemoteLinuxApplicationRunner::handleRemoteProcessStarted()
emit remoteProcessStarted(); emit remoteProcessStarted();
} }
void RemoteLinuxApplicationRunner::handleRemoteProcessFinished(int exitStatus) void AbstractRemoteLinuxApplicationRunner::handleRemoteProcessFinished(int exitStatus)
{ {
Q_ASSERT(exitStatus == SshRemoteProcess::FailedToStart Q_ASSERT(exitStatus == SshRemoteProcess::FailedToStart
|| exitStatus == SshRemoteProcess::KilledBySignal || exitStatus == SshRemoteProcess::KilledBySignal
|| exitStatus == SshRemoteProcess::ExitedNormally); || exitStatus == SshRemoteProcess::ExitedNormally);
ASSERT_STATE(QList<State>() << ProcessStarted << Inactive); QTC_ASSERT(m_d->state == ProcessStarted || m_d->state == Inactive, return);
m_exitStatus = exitStatus; m_d->exitStatus = exitStatus;
if (!m_stopRequested && m_state != Inactive) { if (!m_d->stopRequested && m_d->state != Inactive) {
setState(PostRunCleaning); m_d->state = PostRunCleaning;
cleanup(); doPostRunCleanup();
} }
} }
bool RemoteLinuxApplicationRunner::isConnectionUsable() const void AbstractRemoteLinuxApplicationRunner::setInactive()
{ {
return m_connection && m_connection->state() == SshConnection::Connected m_d->portsGatherer.stop();
&& m_connection->connectionParameters() == m_devConfig->sshParameters(); if (m_d->connection) {
} disconnect(m_d->connection.data(), 0, this, 0);
SshConnectionManager::instance().releaseConnection(m_d->connection);
void RemoteLinuxApplicationRunner::setState(State newState) m_d->connection = SshConnection::Ptr();
{
if (newState == Inactive) {
m_portsGatherer->stop();
if (m_connection) {
disconnect(m_connection.data(), 0, this, 0);
SshConnectionManager::instance().releaseConnection(m_connection);
m_connection = SshConnection::Ptr();
}
if (m_cleaner)
disconnect(m_cleaner.data(), 0, this, 0);
m_stopRequested = false;
} }
m_state = newState; if (m_d->cleaner)
disconnect(m_d->cleaner.data(), 0, this, 0);
m_d->stopRequested = false;
m_d->state = Inactive;
} }
void RemoteLinuxApplicationRunner::emitError(const QString &errorMsg, bool force) void AbstractRemoteLinuxApplicationRunner::emitError(const QString &errorMsg, bool force)
{ {
if (m_state != Inactive) { if (m_d->state != Inactive) {
setState(Inactive); setInactive();
emit error(errorMsg); emit error(errorMsg);
} else if (force) { } else if (force) {
emit error(errorMsg); emit error(errorMsg);
} }
} }
void RemoteLinuxApplicationRunner::handlePortsGathererError(const QString &errorMsg) void AbstractRemoteLinuxApplicationRunner::handlePortsGathererError(const QString &errorMsg)
{ {
if (m_state != Inactive) if (m_d->state != Inactive)
emitError(errorMsg); emitError(errorMsg);
} }
void RemoteLinuxApplicationRunner::handleUsedPortsAvailable() void AbstractRemoteLinuxApplicationRunner::handleUsedPortsAvailable()
{ {
ASSERT_STATE(GatheringPorts); QTC_ASSERT(m_d->state == GatheringPorts, return);
if (m_stopRequested) { if (m_d->stopRequested) {
setState(Inactive); setInactive();
emit remoteProcessFinished(InvalidExitCode); emit remoteProcessFinished(InvalidExitCode);
return; return;
} }
setState(AdditionalInitializing); m_d->state = AdditionalInitializing;
doAdditionalInitializations(); doAdditionalInitializations();
} }
bool RemoteLinuxApplicationRunner::canRun(QString &whyNot) const bool AbstractRemoteLinuxApplicationRunner::canRun(QString &whyNot) const
{ {
if (m_remoteExecutable.isEmpty()) { if (m_d->remoteExecutable.isEmpty()) {
whyNot = tr("No remote executable set."); whyNot = tr("No remote executable set.");
return false; return false;
} }
if (!m_devConfig) { if (!m_d->devConfig) {
whyNot = tr("No device configuration set."); whyNot = tr("No device configuration set.");
return false; return false;
} }
@@ -340,107 +365,129 @@ bool RemoteLinuxApplicationRunner::canRun(QString &whyNot) const
return true; return true;
} }
void RemoteLinuxApplicationRunner::doDeviceSetup() void AbstractRemoteLinuxApplicationRunner::handleDeviceSetupDone(bool success)
{
QTC_ASSERT(m_d->state == SettingUpDevice, return);
if (!success || m_d->stopRequested) {
setInactive();
emit remoteProcessFinished(InvalidExitCode);
return;
}
m_d->connection = SshConnectionManager::instance().acquireConnection(m_d->devConfig->sshParameters());
m_d->state = Connecting;
m_d->exitStatus = -1;
m_d->freePorts = m_d->initialFreePorts;
connect(m_d->connection.data(), SIGNAL(connected()), SLOT(handleConnected()));
connect(m_d->connection.data(), SIGNAL(error(Utils::SshError)),
SLOT(handleConnectionFailure()));
if (m_d->connection->state() == SshConnection::Connected) {
handleConnected();
} else {
emit reportProgress(tr("Connecting to device..."));
if (m_d->connection->state() == Utils::SshConnection::Unconnected)
m_d->connection->connectToHost();
}
}
void AbstractRemoteLinuxApplicationRunner::handleInitialCleanupDone(bool success)
{
QTC_ASSERT(m_d->state == AdditionalPreRunCleaning, return);
if (!success || m_d->stopRequested) {
setInactive();
emit remoteProcessFinished(InvalidExitCode);
return;
}
m_d->state = GatheringPorts;
m_d->portsGatherer.start(m_d->connection, m_d->devConfig);
}
void AbstractRemoteLinuxApplicationRunner::handleInitializationsDone(bool success)
{
QTC_ASSERT(m_d->state == AdditionalInitializing, return);
if (!success) {
setInactive();
emit remoteProcessFinished(InvalidExitCode);
return;
}
if (m_d->stopRequested) {
m_d->state = PostRunCleaning;
doPostRunCleanup();
return;
}
m_d->state = ReadyForExecution;
emit readyForExecution();
}
void AbstractRemoteLinuxApplicationRunner::handlePostRunCleanupDone()
{
QTC_ASSERT(m_d->state == PostRunCleaning, return);
const bool wasStopRequested = m_d->stopRequested;
setInactive();
if (wasStopRequested)
emit remoteProcessFinished(InvalidExitCode);
else if (m_d->exitStatus == SshRemoteProcess::ExitedNormally)
emit remoteProcessFinished(m_d->runner->exitCode());
else
emit error(tr("Error running remote process: %1").arg(m_d->runner->errorString()));
}
const qint64 AbstractRemoteLinuxApplicationRunner::InvalidExitCode = std::numeric_limits<qint64>::min();
GenericRemoteLinuxApplicationRunner::GenericRemoteLinuxApplicationRunner(QObject *parent,
RemoteLinuxRunConfiguration *runConfig)
: AbstractRemoteLinuxApplicationRunner(parent, runConfig)
{
}
GenericRemoteLinuxApplicationRunner::~GenericRemoteLinuxApplicationRunner()
{
}
void GenericRemoteLinuxApplicationRunner::doDeviceSetup()
{ {
handleDeviceSetupDone(true); handleDeviceSetupDone(true);
} }
void RemoteLinuxApplicationRunner::doAdditionalInitialCleanup() void GenericRemoteLinuxApplicationRunner::doAdditionalInitialCleanup()
{ {
handleInitialCleanupDone(true); handleInitialCleanupDone(true);
} }
void RemoteLinuxApplicationRunner::doAdditionalInitializations() void GenericRemoteLinuxApplicationRunner::doAdditionalInitializations()
{ {
handleInitializationsDone(true); handleInitializationsDone(true);
} }
void RemoteLinuxApplicationRunner::doAdditionalPostRunCleanup() void GenericRemoteLinuxApplicationRunner::doPostRunCleanup()
{ {
handlePostRunCleanupDone(); handlePostRunCleanupDone();
} }
void RemoteLinuxApplicationRunner::handleDeviceSetupDone(bool success) void GenericRemoteLinuxApplicationRunner::doAdditionalConnectionErrorHandling()
{ {
ASSERT_STATE(SettingUpDevice);
if (m_state != SettingUpDevice)
return;
if (!success || m_stopRequested) {
setState(Inactive);
emit remoteProcessFinished(InvalidExitCode);
return;
}
m_connection = SshConnectionManager::instance().acquireConnection(m_devConfig->sshParameters());
setState(Connecting);
m_exitStatus = -1;
m_freePorts = m_initialFreePorts;
connect(m_connection.data(), SIGNAL(connected()), this,
SLOT(handleConnected()));
connect(m_connection.data(), SIGNAL(error(Utils::SshError)), this,
SLOT(handleConnectionFailure()));
if (isConnectionUsable()) {
handleConnected();
} else {
emit reportProgress(tr("Connecting to device..."));
if (m_connection->state() == Utils::SshConnection::Unconnected)
m_connection->connectToHost();
}
} }
void RemoteLinuxApplicationRunner::handleInitialCleanupDone(bool success) QString GenericRemoteLinuxApplicationRunner::killApplicationCommandLine() const
{ {
ASSERT_STATE(AdditionalPreRunCleaning); // Prevent pkill from matching our own pkill call.
QString pkillArg = remoteExecutable();
const int lastPos = pkillArg.count() - 1;
pkillArg.replace(lastPos, 1, QLatin1Char('[') + pkillArg.at(lastPos) + QLatin1Char(']'));
if (m_state != AdditionalPreRunCleaning) const char * const killTemplate = "pkill -%2 -f %1";
return; const QString niceKill = QString::fromLocal8Bit(killTemplate).arg(pkillArg).arg("SIGTERM");
if (!success || m_stopRequested) { const QString brutalKill = QString::fromLocal8Bit(killTemplate).arg(pkillArg).arg("SIGKILL");
setState(Inactive); return niceKill + QLatin1String("; sleep 1; ") + brutalKill;
emit remoteProcessFinished(InvalidExitCode);
return;
}
setState(GatheringPorts);
m_portsGatherer->start(m_connection, m_devConfig);
} }
void RemoteLinuxApplicationRunner::handleInitializationsDone(bool success)
{
ASSERT_STATE(AdditionalInitializing);
if (m_state != AdditionalInitializing)
return;
if (!success) {
setState(Inactive);
emit remoteProcessFinished(InvalidExitCode);
return;
}
if (m_stopRequested) {
setState(AdditionalPostRunCleaning);
doAdditionalPostRunCleanup();
return;
}
setState(ReadyForExecution);
emit readyForExecution();
}
void RemoteLinuxApplicationRunner::handlePostRunCleanupDone()
{
ASSERT_STATE(AdditionalPostRunCleaning);
const bool wasStopRequested = m_stopRequested;
setState(Inactive);
if (wasStopRequested)
emit remoteProcessFinished(InvalidExitCode);
else if (m_exitStatus == SshRemoteProcess::ExitedNormally)
emit remoteProcessFinished(m_runner->exitCode());
else
emit error(tr("Error running remote process: %1").arg(m_runner->errorString()));
}
const qint64 RemoteLinuxApplicationRunner::InvalidExitCode = std::numeric_limits<qint64>::min();
} // namespace RemoteLinux } // namespace RemoteLinux

View File

@@ -48,14 +48,18 @@ namespace RemoteLinux {
class LinuxDeviceConfiguration; class LinuxDeviceConfiguration;
class RemoteLinuxRunConfiguration; class RemoteLinuxRunConfiguration;
namespace Internal { class MaemoUsedPortsGatherer; } namespace Internal {
class AbstractRemoteLinuxApplicationRunnerPrivate;
class MaemoUsedPortsGatherer;
}
class REMOTELINUX_EXPORT RemoteLinuxApplicationRunner : public QObject class REMOTELINUX_EXPORT AbstractRemoteLinuxApplicationRunner : public QObject
{ {
Q_OBJECT Q_OBJECT
Q_DISABLE_COPY(AbstractRemoteLinuxApplicationRunner)
public: public:
RemoteLinuxApplicationRunner(QObject *parent, RemoteLinuxRunConfiguration *runConfig); AbstractRemoteLinuxApplicationRunner(QObject *parent, RemoteLinuxRunConfiguration *runConfig);
~RemoteLinuxApplicationRunner(); ~AbstractRemoteLinuxApplicationRunner();
void start(); void start();
void stop(); void stop();
@@ -63,12 +67,12 @@ public:
void startExecution(const QByteArray &remoteCall); void startExecution(const QByteArray &remoteCall);
QSharedPointer<Utils::SshConnection> connection() const; QSharedPointer<Utils::SshConnection> connection() const;
const Internal::MaemoUsedPortsGatherer *usedPortsGatherer() const { return m_portsGatherer; }
PortList *freePorts() { return &m_freePorts; }
QString remoteExecutable() const { return m_remoteExecutable; }
QString arguments() const { return m_appArguments; }
QString commandPrefix() const { return m_commandPrefix; }
QSharedPointer<const LinuxDeviceConfiguration> devConfig() const; QSharedPointer<const LinuxDeviceConfiguration> devConfig() const;
const Internal::MaemoUsedPortsGatherer *usedPortsGatherer() const;
PortList *freePorts();
QString remoteExecutable() const;
QString arguments() const;
QString commandPrefix() const;
static const qint64 InvalidExitCode; static const qint64 InvalidExitCode;
@@ -100,48 +104,49 @@ private slots:
void handleUsedPortsAvailable(); void handleUsedPortsAvailable();
private: private:
enum State { Inactive, SettingUpDevice, Connecting, PreRunCleaning, AdditionalPreRunCleaning,
GatheringPorts, AdditionalInitializing, ReadyForExecution, ProcessStarting, ProcessStarted,
PostRunCleaning, AdditionalPostRunCleaning
};
// Override to do custom setup of the device *before* connecting. virtual QString killApplicationCommandLine() const=0;
// Implement to do custom setup of the device *before* connecting.
// Call handleDeviceSetupDone() afterwards. // Call handleDeviceSetupDone() afterwards.
virtual void doDeviceSetup(); virtual void doDeviceSetup()=0;
// Override to do additional pre-run cleanup and call handleInitialCleanupDone(). // Implement to do additional pre-run cleanup and call handleInitialCleanupDone().
virtual void doAdditionalInitialCleanup(); virtual void doAdditionalInitialCleanup()=0;
// Override to do additional initializations right before the application is ready. // Implement to do additional initializations right before the application is ready.
// Call handleInitializationsDone() afterwards. // Call handleInitializationsDone() afterwards.
virtual void doAdditionalInitializations(); virtual void doAdditionalInitializations()=0;
// Override for additional cleanups after application exit and call handlePostRunCleanupDone(); // Implement to do cleanups after application exit and call handlePostRunCleanupDone();
virtual void doAdditionalPostRunCleanup(); virtual void doPostRunCleanup()=0;
virtual void doAdditionalConnectionErrorHandling() {} virtual void doAdditionalConnectionErrorHandling()=0;
void setState(State newState); void setInactive();
void emitError(const QString &errorMsg, bool force = false); void emitError(const QString &errorMsg, bool force = false);
void cleanup(); void cleanup();
bool isConnectionUsable() const;
Internal::MaemoUsedPortsGatherer * const m_portsGatherer; Internal::AbstractRemoteLinuxApplicationRunnerPrivate * const m_d;
const QSharedPointer<const LinuxDeviceConfiguration> m_devConfig; };
const QString m_remoteExecutable;
const QString m_appArguments;
const QString m_commandPrefix;
const PortList m_initialFreePorts;
QSharedPointer<Utils::SshConnection> m_connection;
QSharedPointer<Utils::SshRemoteProcess> m_runner;
QSharedPointer<Utils::SshRemoteProcess> m_cleaner;
QStringList m_procsToKill;
PortList m_freePorts;
int m_exitStatus; class REMOTELINUX_EXPORT GenericRemoteLinuxApplicationRunner : public AbstractRemoteLinuxApplicationRunner
bool m_stopRequested; {
State m_state; Q_OBJECT
public:
GenericRemoteLinuxApplicationRunner(QObject *parent, RemoteLinuxRunConfiguration *runConfig);
~GenericRemoteLinuxApplicationRunner();
protected:
void doDeviceSetup();
void doAdditionalInitialCleanup();
void doAdditionalInitializations();
void doPostRunCleanup();
void doAdditionalConnectionErrorHandling();
private:
QString killApplicationCommandLine() const;
}; };
} // namespace RemoteLinux } // namespace RemoteLinux

View File

@@ -275,7 +275,7 @@ bool AbstractRemoteLinuxDebugSupport::setPort(int &port)
RemoteLinuxDebugSupport::RemoteLinuxDebugSupport(RemoteLinuxRunConfiguration *runConfig, RemoteLinuxDebugSupport::RemoteLinuxDebugSupport(RemoteLinuxRunConfiguration *runConfig,
DebuggerEngine *engine) DebuggerEngine *engine)
: AbstractRemoteLinuxDebugSupport(runConfig, engine), : AbstractRemoteLinuxDebugSupport(runConfig, engine),
m_runner(new RemoteLinuxApplicationRunner(this, runConfig)) m_runner(new GenericRemoteLinuxApplicationRunner(this, runConfig))
{ {
} }

View File

@@ -49,7 +49,7 @@ namespace ProjectExplorer { class RunControl; }
namespace RemoteLinux { namespace RemoteLinux {
class LinuxDeviceConfiguration; class LinuxDeviceConfiguration;
class RemoteLinuxRunConfiguration; class RemoteLinuxRunConfiguration;
class RemoteLinuxApplicationRunner; class AbstractRemoteLinuxApplicationRunner;
class REMOTELINUX_EXPORT AbstractRemoteLinuxDebugSupport : public QObject class REMOTELINUX_EXPORT AbstractRemoteLinuxDebugSupport : public QObject
{ {
@@ -74,7 +74,7 @@ private slots:
private: private:
enum State { Inactive, StartingRunner, StartingRemoteProcess, Debugging }; enum State { Inactive, StartingRunner, StartingRemoteProcess, Debugging };
virtual RemoteLinuxApplicationRunner *runner() const=0; virtual AbstractRemoteLinuxApplicationRunner *runner() const=0;
void handleAdapterSetupFailed(const QString &error); void handleAdapterSetupFailed(const QString &error);
void handleAdapterSetupDone(); void handleAdapterSetupDone();
@@ -102,9 +102,9 @@ public:
~RemoteLinuxDebugSupport(); ~RemoteLinuxDebugSupport();
private: private:
RemoteLinuxApplicationRunner *runner() const { return m_runner; } AbstractRemoteLinuxApplicationRunner *runner() const { return m_runner; }
RemoteLinuxApplicationRunner * const m_runner; AbstractRemoteLinuxApplicationRunner * const m_runner;
}; };
} // namespace RemoteLinux } // namespace RemoteLinux

View File

@@ -99,7 +99,7 @@ void AbstractRemoteLinuxRunControl::startExecution()
void AbstractRemoteLinuxRunControl::handleRemoteProcessFinished(qint64 exitCode) void AbstractRemoteLinuxRunControl::handleRemoteProcessFinished(qint64 exitCode)
{ {
if (exitCode != RemoteLinuxApplicationRunner::InvalidExitCode) { if (exitCode != AbstractRemoteLinuxApplicationRunner::InvalidExitCode) {
appendMessage(tr("Finished running remote process. Exit code was %1.\n") appendMessage(tr("Finished running remote process. Exit code was %1.\n")
.arg(exitCode), Utils::NormalMessageFormat); .arg(exitCode), Utils::NormalMessageFormat);
} }
@@ -148,7 +148,7 @@ void AbstractRemoteLinuxRunControl::setFinished()
RemoteLinuxRunControl::RemoteLinuxRunControl(ProjectExplorer::RunConfiguration *runConfig) RemoteLinuxRunControl::RemoteLinuxRunControl(ProjectExplorer::RunConfiguration *runConfig)
: AbstractRemoteLinuxRunControl(runConfig), : AbstractRemoteLinuxRunControl(runConfig),
m_runner(new RemoteLinuxApplicationRunner(this, qobject_cast<RemoteLinuxRunConfiguration *>(runConfig))) m_runner(new GenericRemoteLinuxApplicationRunner(this, qobject_cast<RemoteLinuxRunConfiguration *>(runConfig)))
{ {
} }
@@ -156,7 +156,7 @@ RemoteLinuxRunControl::~RemoteLinuxRunControl()
{ {
} }
RemoteLinuxApplicationRunner *RemoteLinuxRunControl::runner() const AbstractRemoteLinuxApplicationRunner *RemoteLinuxRunControl::runner() const
{ {
return m_runner; return m_runner;
} }

View File

@@ -39,7 +39,7 @@
#include <QtCore/QString> #include <QtCore/QString>
namespace RemoteLinux { namespace RemoteLinux {
class RemoteLinuxApplicationRunner; class AbstractRemoteLinuxApplicationRunner;
class REMOTELINUX_EXPORT AbstractRemoteLinuxRunControl : public ProjectExplorer::RunControl class REMOTELINUX_EXPORT AbstractRemoteLinuxRunControl : public ProjectExplorer::RunControl
{ {
@@ -53,7 +53,7 @@ public:
virtual bool isRunning() const; virtual bool isRunning() const;
virtual QIcon icon() const; virtual QIcon icon() const;
virtual RemoteLinuxApplicationRunner *runner() const=0; virtual AbstractRemoteLinuxApplicationRunner *runner() const=0;
private slots: private slots:
void startExecution(); void startExecution();
@@ -81,9 +81,9 @@ public:
explicit RemoteLinuxRunControl(ProjectExplorer::RunConfiguration *runConfig); explicit RemoteLinuxRunControl(ProjectExplorer::RunConfiguration *runConfig);
virtual ~RemoteLinuxRunControl(); virtual ~RemoteLinuxRunControl();
private: private:
virtual RemoteLinuxApplicationRunner *runner() const; virtual AbstractRemoteLinuxApplicationRunner *runner() const;
RemoteLinuxApplicationRunner * const m_runner; AbstractRemoteLinuxApplicationRunner * const m_runner;
}; };
} // namespace RemoteLinux } // namespace RemoteLinux