Commit 22599094 authored by Christian Kandeler's avatar Christian Kandeler
Browse files

Introduce the concept of a "device process".



Provide a QProcess-like abstraction that can be used
to implement processes running locally or on a remote
device. Objects of a concrete class implementing the functionality
are created by IDevice objects.
Current implementations are:
     - Local execution (QProcess-based), provided via the DesktopDevice.
     - Remote execution via SSH.
     - A specialized case of the former for remote Linux systems (provided by
       LinuxDevice).
The latter is already being used in a number of places. As a result, lots of
code dealing with details such as setting the remote environment could be
moved to a central location. These things are no longer the concern of whoever
is wishing to run a remote process.

Change-Id: I919260ee6e77a020ca47226a4a534e7b8398106f
Reviewed-by: default avatarhjk <hjk121@nokiamail.com>
parent dc07796c
......@@ -176,6 +176,11 @@ void SshRemoteProcess::addToEnvironment(const QByteArray &var, const QByteArray
d->m_env << qMakePair(var, value); // Cached locally and sent on start()
}
void SshRemoteProcess::clearEnvironment()
{
d->m_env.clear();
}
void SshRemoteProcess::requestTerminal(const SshPseudoTerminal &terminal)
{
QSSH_ASSERT_AND_RETURN(d->channelState() == Internal::SshRemoteProcessPrivate::Inactive);
......
......@@ -80,6 +80,7 @@ public:
* usually configured to ignore such requests for security reasons.
*/
void addToEnvironment(const QByteArray &var, const QByteArray &value);
void clearEnvironment();
void requestTerminal(const SshPseudoTerminal &terminal);
void start();
......
......@@ -31,10 +31,13 @@
#include "maddedevicetester.h"
#include "maemoconstants.h"
#include <remotelinux/linuxdeviceprocess.h>
#include <remotelinux/publickeydeploymentdialog.h>
#include <remotelinux/remotelinux_constants.h>
#include <utils/qtcassert.h>
#include <QStringList>
using namespace ProjectExplorer;
using namespace RemoteLinux;
......@@ -115,5 +118,14 @@ DeviceTester *MaddeDevice::createDeviceTester() const
return new MaddeDeviceTester;
}
DeviceProcess *MaddeDevice::createProcess(QObject *parent) const
{
LinuxDeviceProcess * const proc
= static_cast<LinuxDeviceProcess *>(LinuxDevice::createProcess(parent));
proc->setRcFilesToSource(QStringList() << QLatin1String("/etc/profile")
<< QLatin1String("/home/user/.profile") << QLatin1String("~/.profile"));
return proc;
}
} // namespace Internal
} // namespace Madde
......@@ -59,6 +59,7 @@ public:
static QSize packageManagerIconSize(Core::Id type);
ProjectExplorer::DeviceTester *createDeviceTester() const;
ProjectExplorer::DeviceProcess *createProcess(QObject *parent) const;
private:
MaddeDevice();
......
......@@ -112,24 +112,6 @@ bool MaemoRunConfiguration::fromMap(const QVariantMap &map)
return true;
}
QString MaemoRunConfiguration::environmentPreparationCommand() const
{
return MaemoGlobal::remoteSourceProfilesCommand();
}
QString MaemoRunConfiguration::commandPrefix() const
{
IDevice::ConstPtr dev = DeviceKitInformation::device(target()->kit());
if (!dev)
return QString();
const QString prefix = environmentPreparationCommand() + QLatin1Char(';');
RemoteLinuxEnvironmentAspect *aspect = extraAspect<RemoteLinuxEnvironmentAspect>();
QTC_ASSERT(aspect, return QString());
return QString::fromLatin1("%1 %2").arg(prefix, aspect->userEnvironmentChangesAsString());
}
Utils::PortList MaemoRunConfiguration::freePorts() const
{
return MaemoGlobal::freePorts(target()->kit());
......
......@@ -48,8 +48,6 @@ public:
bool fromMap(const QVariantMap &map);
bool isEnabled() const;
QWidget *createConfigurationWidget();
QString environmentPreparationCommand() const;
QString commandPrefix() const;
Utils::PortList freePorts() const;
Internal::MaemoRemoteMountsModel *remoteMounts() const { return m_remoteMounts; }
......
......@@ -29,6 +29,7 @@
#include "desktopdevice.h"
#include "projectexplorerconstants.h"
#include "desktopdeviceprocess.h"
#include "deviceprocesslist.h"
#include "localprocesslist.h"
#include "desktopdeviceconfigurationwidget.h"
......@@ -108,6 +109,11 @@ DeviceProcessList *DesktopDevice::createProcessListModel(QObject *parent) const
return new Internal::LocalProcessList(sharedFromThis(), parent);
}
DeviceProcess *DesktopDevice::createProcess(QObject *parent) const
{
return new Internal::DesktopDeviceProcess(sharedFromThis(), parent);
}
IDevice::Ptr DesktopDevice::clone() const
{
return Ptr(new DesktopDevice(*this));
......
......@@ -53,6 +53,8 @@ public:
bool canAutoDetectPorts() const;
bool canCreateProcessModel() const;
DeviceProcessList *createProcessListModel(QObject *parent) const;
bool canCreateProcess() const { return true; }
DeviceProcess *createProcess(QObject *parent) const;
IDevice::Ptr clone() const;
......
/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/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 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, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/
#include "desktopdeviceprocess.h"
#include <utils/environment.h>
#include <utils/qtcassert.h>
#ifdef Q_OS_UNIX
#include <signal.h>
#endif
namespace ProjectExplorer {
namespace Internal {
DesktopDeviceProcess::DesktopDeviceProcess(const QSharedPointer<const IDevice> &device,
QObject *parent)
: DeviceProcess(device, parent), m_process(new QProcess(this))
{
connect(m_process, SIGNAL(error(QProcess::ProcessError)),
SIGNAL(error(QProcess::ProcessError)));
connect(m_process, SIGNAL(finished(int)), SIGNAL(finished()));
connect(m_process, SIGNAL(readyReadStandardOutput()), SIGNAL(readyReadStandardOutput()));
connect(m_process, SIGNAL(readyReadStandardError()), SIGNAL(readyReadStandardError()));
connect(m_process, SIGNAL(started()), SIGNAL(started()));
}
void DesktopDeviceProcess::start(const QString &executable, const QStringList &arguments)
{
QTC_ASSERT(m_process->state() == QProcess::NotRunning, return);
m_process->start(executable, arguments);
}
void DesktopDeviceProcess::interrupt()
{
#ifdef Q_OS_UNIX
::kill(m_process->pid(), SIGINT);
#elif Q_OS_WIN
// tbd
#endif
}
void DesktopDeviceProcess::terminate()
{
m_process->terminate();
}
void DesktopDeviceProcess::kill()
{
m_process->kill();
}
QProcess::ProcessState DesktopDeviceProcess::state() const
{
return m_process->state();
}
QProcess::ExitStatus DesktopDeviceProcess::exitStatus() const
{
return m_process->exitStatus();
}
int DesktopDeviceProcess::exitCode() const
{
return m_process->exitCode();
}
QString DesktopDeviceProcess::errorString() const
{
return m_process->errorString();
}
Utils::Environment DesktopDeviceProcess::environment() const
{
return Utils::Environment(m_process->processEnvironment().toStringList());
}
void DesktopDeviceProcess::setEnvironment(const Utils::Environment &env)
{
m_process->setProcessEnvironment(env.toProcessEnvironment());
}
void DesktopDeviceProcess::setWorkingDirectory(const QString &directory)
{
m_process->setWorkingDirectory(directory);
}
QByteArray DesktopDeviceProcess::readAllStandardOutput()
{
return m_process->readAllStandardOutput();
}
QByteArray DesktopDeviceProcess::readAllStandardError()
{
return m_process->readAllStandardError();
}
} // namespace Internal
} // namespace ProjectExplorer
/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/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 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, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/
#ifndef QTC_DESKTOPDEVICEPROCESS_H
#define QTC_DESKTOPDEVICEPROCESS_H
#include "deviceprocess.h"
namespace ProjectExplorer {
namespace Internal {
class ProcessHelper;
class DesktopDeviceProcess : public DeviceProcess
{
Q_OBJECT
public:
DesktopDeviceProcess(const QSharedPointer<const IDevice> &device, QObject *parent = 0);
void start(const QString &executable, const QStringList &arguments);
void interrupt();
void terminate();
void kill();
QProcess::ProcessState state() const;
QProcess::ExitStatus exitStatus() const;
int exitCode() const;
QString errorString() const;
Utils::Environment environment() const;
void setEnvironment(const Utils::Environment &env);
void setWorkingDirectory(const QString &directory);
QByteArray readAllStandardOutput();
QByteArray readAllStandardError();
private:
QProcess * const m_process;
};
} // namespace Internal
} // namespace ProjectExplorer
#endif // Include guard
......@@ -28,11 +28,14 @@
****************************************************************************/
#include "deviceapplicationrunner.h"
#include "sshdeviceprocess.h"
#include <ssh/sshconnection.h>
#include <ssh/sshconnectionmanager.h>
#include <ssh/sshremoteprocess.h>
#include <utils/environment.h>
#include <utils/qtcassert.h>
#include <QStringList>
#include <QTimer>
using namespace QSsh;
......@@ -49,10 +52,13 @@ public:
SshConnection *connection;
DeviceApplicationHelperAction *preRunAction;
DeviceApplicationHelperAction *postRunAction;
DeviceProcess *deviceProcess;
IDevice::ConstPtr device;
SshRemoteProcess::Ptr remoteApp;
QTimer stopTimer;
QByteArray commandLine;
QString command;
QStringList arguments;
Utils::Environment environment;
QString workingDir;
State state;
bool stopRequested;
bool success;
......@@ -74,6 +80,7 @@ DeviceApplicationRunner::DeviceApplicationRunner(QObject *parent) :
d->preRunAction = 0;
d->postRunAction = 0;
d->connection = 0;
d->deviceProcess = 0;
d->state = Inactive;
d->stopTimer.setSingleShot(true);
......@@ -86,23 +93,33 @@ DeviceApplicationRunner::~DeviceApplicationRunner()
delete d;
}
void DeviceApplicationRunner::setEnvironment(const Utils::Environment &env)
{
d->environment = env;
}
void DeviceApplicationRunner::setWorkingDirectory(const QString &workingDirectory)
{
d->workingDir = workingDirectory;
}
void DeviceApplicationRunner::start(const IDevice::ConstPtr &device,
const QByteArray &commandLine)
const QString &command, const QStringList &arguments)
{
QTC_ASSERT(device->canCreateProcess(), return);
QTC_ASSERT(d->state == Inactive, return);
d->device = device;
d->commandLine = commandLine;
d->command = command;
d->arguments = arguments;
d->stopRequested = false;
d->success = true;
connectToServer();
}
void DeviceApplicationRunner::stop(const QByteArray &stopCommand)
void DeviceApplicationRunner::stop()
{
QTC_ASSERT(d->state != Inactive, return);
if (d->stopRequested)
return;
d->stopRequested = true;
......@@ -117,7 +134,7 @@ void DeviceApplicationRunner::stop(const QByteArray &stopCommand)
break;
case Run:
d->stopTimer.start(10000);
d->connection->createRemoteProcess(stopCommand)->start();
d->deviceProcess->terminate();
break;
case PostRun:
d->postRunAction->stop();
......@@ -188,10 +205,10 @@ void DeviceApplicationRunner::setFinished()
if (d->state == Inactive)
return;
if (d->remoteApp) {
d->remoteApp->disconnect(this);
d->remoteApp->close();
d->remoteApp.clear();
if (d->deviceProcess) {
d->deviceProcess->disconnect(this);
d->deviceProcess->deleteLater();
d->deviceProcess = 0;
}
if (d->connection) {
d->connection->disconnect(this);
......@@ -232,7 +249,7 @@ void DeviceApplicationRunner::handleConnectionFailure()
break;
case Run:
d->stopTimer.stop();
d->remoteApp->disconnect(this);
d->deviceProcess->disconnect(this);
executePostRunAction();
break;
case PostRun:
......@@ -290,16 +307,16 @@ void DeviceApplicationRunner::handleStopTimeout()
setFinished();
}
void DeviceApplicationRunner::handleApplicationFinished(int exitStatus)
void DeviceApplicationRunner::handleApplicationFinished()
{
QTC_ASSERT(d->state == Run, return);
d->stopTimer.stop();
if (exitStatus == SshRemoteProcess::CrashExit) {
emit reportError(tr("Remote application crashed: %1").arg(d->remoteApp->errorString()));
if (d->deviceProcess->exitStatus() == QProcess::CrashExit) {
emit reportError(tr("Remote application crashed: %1").arg(d->deviceProcess->errorString()));
d->success = false;
} else {
const int exitCode = d->remoteApp->exitCode();
const int exitCode = d->deviceProcess->exitCode();
if (exitCode != 0) {
emit reportError(tr("Remote application finished with exit code %1.").arg(exitCode));
d->success = false;
......@@ -313,13 +330,13 @@ void DeviceApplicationRunner::handleApplicationFinished(int exitStatus)
void DeviceApplicationRunner::handleRemoteStdout()
{
QTC_ASSERT(d->state == Run, return);
emit remoteStdout(d->remoteApp->readAllStandardOutput());
emit remoteStdout(d->deviceProcess->readAllStandardOutput());
}
void DeviceApplicationRunner::handleRemoteStderr()
{
QTC_ASSERT(d->state == Run, return);
emit remoteStderr(d->remoteApp->readAllStandardError());
emit remoteStderr(d->deviceProcess->readAllStandardError());
}
void DeviceApplicationRunner::runApplication()
......@@ -327,12 +344,14 @@ 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();
d->deviceProcess = d->device->createProcess(this);
connect(d->deviceProcess, SIGNAL(started()), SIGNAL(remoteProcessStarted()));
connect(d->deviceProcess, SIGNAL(readyReadStandardOutput()), SLOT(handleRemoteStdout()));
connect(d->deviceProcess, SIGNAL(readyReadStandardError()), SLOT(handleRemoteStderr()));
connect(d->deviceProcess, SIGNAL(finished()), SLOT(handleApplicationFinished()));
d->deviceProcess->setEnvironment(d->environment);
d->deviceProcess->setWorkingDirectory(d->workingDir);
d->deviceProcess->start(d->command, d->arguments);
}
} // namespace ProjectExplorer
......@@ -35,6 +35,12 @@
#include <QObject>
QT_BEGIN_NAMESPACE
class QStringList;
QT_END_NAMESPACE
namespace Utils { class Environment; }
namespace ProjectExplorer {
class PROJECTEXPLORER_EXPORT DeviceApplicationHelperAction : public QObject
......@@ -60,8 +66,12 @@ public:
explicit DeviceApplicationRunner(QObject *parent = 0);
virtual ~DeviceApplicationRunner();
void start(const IDevice::ConstPtr &device, const QByteArray &commandLine);
void stop(const QByteArray &stopCommand);
void setEnvironment(const Utils::Environment &env);
void setWorkingDirectory(const QString &workingDirectory);
void start(const IDevice::ConstPtr &device, const QString &command,
const QStringList &arguments);
void stop();
// Use these if you need to do something before and after the application is run, respectively.
// Typically, the post-run action reverts the effects of the pre-run action.
......@@ -82,7 +92,7 @@ private slots:
void handleConnectionFailure();
void handleHelperActionFinished(bool success);
void handleStopTimeout();
void handleApplicationFinished(int exitStatus);
void handleApplicationFinished();
void handleRemoteStdout();
void handleRemoteStderr();
......
/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/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 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, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/
#include "deviceprocess.h"
#include "idevice.h"
#include <utils/qtcassert.h>
namespace ProjectExplorer {
DeviceProcess::DeviceProcess(const IDevice::ConstPtr &device, QObject *parent)
: QObject(parent), m_device(device)
{
}
DeviceProcess::~DeviceProcess()
{
}
IDevice::ConstPtr DeviceProcess::device() const
{
return m_device;
}
} // namespace ProjectExplorer
/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of Qt Creator.
**
** Commercial License Usage