summaryrefslogtreecommitdiff
path: root/src/plugins/projectexplorer/devicesupport/deviceapplicationrunner.cpp
diff options
context:
space:
mode:
authorChristian Kandeler <christian.kandeler@nokia.com>2012-07-26 09:02:34 +0200
committerhjk <qthjk@ovi.com>2012-08-01 11:55:53 +0200
commit1d85d8e706cd2f0af3731d71781caaa8d528dc68 (patch)
treebb15350bb3a15d01987f77b477bdf67ffbb9cb3f /src/plugins/projectexplorer/devicesupport/deviceapplicationrunner.cpp
parent93756975e1960052f0d6f17b4f63ba22007e967b (diff)
downloadqt-creator-1d85d8e706cd2f0af3731d71781caaa8d528dc68.tar.gz
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>
Diffstat (limited to 'src/plugins/projectexplorer/devicesupport/deviceapplicationrunner.cpp')
-rw-r--r--src/plugins/projectexplorer/devicesupport/deviceapplicationrunner.cpp339
1 files changed, 339 insertions, 0 deletions
diff --git a/src/plugins/projectexplorer/devicesupport/deviceapplicationrunner.cpp b/src/plugins/projectexplorer/devicesupport/deviceapplicationrunner.cpp
new file mode 100644
index 0000000000..0638b9a7ba
--- /dev/null
+++ b/src/plugins/projectexplorer/devicesupport/deviceapplicationrunner.cpp
@@ -0,0 +1,339 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: http://www.qt-project.org/
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**************************************************************************/
+#include "deviceapplicationrunner.h"
+
+#include <ssh/sshconnection.h>
+#include <ssh/sshconnectionmanager.h>
+#include <ssh/sshremoteprocess.h>
+#include <utils/qtcassert.h>
+
+#include <QTimer>
+
+using namespace QSsh;
+
+namespace ProjectExplorer {
+
+namespace {
+enum State { Inactive, Connecting, PreRun, Run, PostRun };
+} // anonymous namespace
+
+class DeviceApplicationRunner::DeviceApplicationRunnerPrivate
+{
+public:
+ SshConnection *connection;
+ DeviceApplicationHelperAction *preRunAction;
+ DeviceApplicationHelperAction *postRunAction;
+ IDevice::ConstPtr device;
+ SshRemoteProcess::Ptr remoteApp;
+ QTimer stopTimer;
+ QByteArray commandLine;
+ State state;
+ bool stopRequested;
+ bool success;
+};
+
+
+DeviceApplicationHelperAction::DeviceApplicationHelperAction(QObject *parent) : QObject(parent)
+{
+}
+
+DeviceApplicationHelperAction::~DeviceApplicationHelperAction()
+{
+}
+
+
+DeviceApplicationRunner::DeviceApplicationRunner(QObject *parent) :
+ QObject(parent), d(new DeviceApplicationRunnerPrivate)
+{
+ d->preRunAction = 0;
+ d->postRunAction = 0;
+ d->connection = 0;
+ d->state = Inactive;
+
+ d->stopTimer.setSingleShot(true);
+ connect(&d->stopTimer, SIGNAL(timeout()), SLOT(handleStopTimeout()));
+}
+
+DeviceApplicationRunner::~DeviceApplicationRunner()
+{
+ setFinished();
+ delete d;
+}
+
+void DeviceApplicationRunner::start(const IDevice::ConstPtr &device,
+ const QByteArray &commandLine)
+{
+ QTC_ASSERT(d->state == Inactive, return);
+
+ d->device = device;
+ d->commandLine = commandLine;
+ d->stopRequested = false;
+ d->success = true;
+
+ connectToServer();
+}
+
+void DeviceApplicationRunner::stop(const QByteArray &stopCommand)
+{
+ QTC_ASSERT(d->state != Inactive, return);
+
+ if (d->stopRequested)
+ return;
+ d->stopRequested = true;
+ d->success = false;
+ emit reportProgress(tr("User requested stop. Shutting down..."));
+ switch (d->state) {
+ case Connecting:
+ setFinished();
+ break;
+ case PreRun:
+ d->preRunAction->stop();
+ break;
+ case Run:
+ d->stopTimer.start(10000);
+ d->connection->createRemoteProcess(stopCommand)->start();
+ break;
+ case PostRun:
+ d->postRunAction->stop();
+ break;
+ case Inactive:
+ break;
+ }
+}
+
+void DeviceApplicationRunner::setPreRunAction(DeviceApplicationHelperAction *action)
+{
+ addAction(d->preRunAction, action);
+}
+
+void DeviceApplicationRunner::setPostRunAction(DeviceApplicationHelperAction *action)
+{
+ addAction(d->postRunAction, action);
+}
+
+void DeviceApplicationRunner::connectToServer()
+{
+ QTC_CHECK(!d->connection);
+
+ d->state = Connecting;
+
+ if (!d->device) {
+ emit reportError(tr("Cannot run: No device."));
+ setFinished();
+ return;
+ }
+
+ d->connection = SshConnectionManager::instance().acquireConnection(d->device->sshParameters());
+ connect(d->connection, SIGNAL(error(QSsh::SshError)), SLOT(handleConnectionFailure()));
+ if (d->connection->state() == SshConnection::Connected) {
+ handleConnected();
+ } else {
+ emit reportProgress(tr("Connecting to device..."));
+ connect(d->connection, SIGNAL(connected()), SLOT(handleConnected()));
+ if (d->connection->state() == QSsh::SshConnection::Unconnected)
+ d->connection->connectToHost();
+ }
+}
+
+void DeviceApplicationRunner::executePreRunAction()
+{
+ QTC_ASSERT(d->state == Connecting, return);
+
+ d->state = PreRun;
+ if (d->preRunAction)
+ d->preRunAction->start();
+ else
+ runApplication();
+}
+
+void DeviceApplicationRunner::executePostRunAction()
+{
+ QTC_ASSERT(d->state == PreRun || d->state == Run, return);
+
+ d->state = PostRun;
+ if (d->postRunAction)
+ d->postRunAction->start();
+ else
+ setFinished();
+}
+
+void DeviceApplicationRunner::setFinished()
+{
+ if (d->state == Inactive)
+ return;
+
+ if (d->remoteApp) {
+ d->remoteApp->disconnect(this);
+ d->remoteApp->close();
+ d->remoteApp.clear();
+ }
+ if (d->connection) {
+ d->connection->disconnect(this);
+ SshConnectionManager::instance().releaseConnection(d->connection);
+ d->connection = 0;
+ }
+
+ d->state = Inactive;
+ emit finished(d->success);
+}
+
+void DeviceApplicationRunner::handleConnected()
+{
+ QTC_ASSERT(d->state == Connecting, return);
+
+ if (d->stopRequested) {
+ setFinished();
+ return;
+ }
+
+ executePreRunAction();
+}
+
+void DeviceApplicationRunner::handleConnectionFailure()
+{
+ QTC_ASSERT(d->state != Inactive, return);
+
+ emit reportError(tr("SSH connection failed: %1").arg(d->connection->errorString()));
+ d->success = false;
+ switch (d->state) {
+ case Inactive:
+ break; // Can't happen.
+ case Connecting:
+ setFinished();
+ break;
+ case PreRun:
+ d->preRunAction->stop();
+ break;
+ case Run:
+ d->stopTimer.stop();
+ d->remoteApp->disconnect(this);
+ executePostRunAction();
+ break;
+ case PostRun:
+ d->postRunAction->stop();
+ break;
+ }
+}
+
+void DeviceApplicationRunner::handleHelperActionFinished(bool success)
+{
+ switch (d->state) {
+ case Inactive:
+ break;
+ case PreRun:
+ if (success && d->success) {
+ runApplication();
+ } else if (success && !d->success) {
+ executePostRunAction();
+ } else {
+ d->success = false;
+ setFinished();
+ }
+ break;
+ case PostRun:
+ if (!success)
+ d->success = false;
+ setFinished();
+ break;
+ default:
+ QTC_CHECK(false);
+ }
+}
+
+void DeviceApplicationRunner::addAction(DeviceApplicationHelperAction *&target,
+ DeviceApplicationHelperAction *source)
+{
+ QTC_ASSERT(d->state == Inactive, return);
+
+ if (target)
+ disconnect(target, 0, this, 0);
+ target = source;
+ if (target) {
+ connect(target, SIGNAL(finished(bool)), SLOT(handleHelperActionFinished(bool)));
+ connect(target, SIGNAL(reportProgress(QString)), SIGNAL(reportProgress(QString)));
+ connect(target, SIGNAL(reportError(QString)), SIGNAL(reportError(QString)));
+ }
+}
+
+void DeviceApplicationRunner::handleStopTimeout()
+{
+ QTC_ASSERT(d->stopRequested && d->state == Run, return);
+
+ emit reportError(tr("Application did not finish in time, aborting."));
+ d->success = false;
+ setFinished();
+}
+
+void DeviceApplicationRunner::handleApplicationFinished(int exitStatus)
+{
+ QTC_ASSERT(d->state == Run, return);
+
+ d->stopTimer.stop();
+ if (exitStatus == SshRemoteProcess::CrashExit) {
+ emit reportError(tr("Remote application crashed: %1").arg(d->remoteApp->errorString()));
+ d->success = false;
+ } else {
+ const int exitCode = d->remoteApp->exitCode();
+ if (exitCode != 0) {
+ emit reportError(tr("Remote application finished with exit code %1.").arg(exitCode));
+ d->success = false;
+ } else {
+ emit reportProgress(tr("Remote application finished with exit code 0."));
+ }
+ }
+ executePostRunAction();
+}
+
+void DeviceApplicationRunner::handleRemoteStdout()
+{
+ QTC_ASSERT(d->state == Run, return);
+ emit remoteStdout(d->remoteApp->readAllStandardOutput());
+}
+
+void DeviceApplicationRunner::handleRemoteStderr()
+{
+ QTC_ASSERT(d->state == Run, return);
+ emit remoteStderr(d->remoteApp->readAllStandardError());
+}
+
+void DeviceApplicationRunner::runApplication()
+{
+ QTC_ASSERT(d->state == PreRun, return);
+
+ d->state = Run;
+ d->remoteApp = d->connection->createRemoteProcess(d->commandLine);
+ connect(d->remoteApp.data(), SIGNAL(started()), SIGNAL(remoteProcessStarted()));
+ connect(d->remoteApp.data(), SIGNAL(readyReadStandardOutput()), SLOT(handleRemoteStdout()));
+ connect(d->remoteApp.data(), SIGNAL(readyReadStandardError()), SLOT(handleRemoteStderr()));
+ connect(d->remoteApp.data(), SIGNAL(closed(int)), SLOT(handleApplicationFinished(int)));
+ d->remoteApp->start();
+}
+
+} // namespace ProjectExplorer