Maemo: Move deploying to dedicated deploy step.

Reviewed-by: kh1
This commit is contained in:
ck
2010-07-13 15:24:21 +02:00
parent e85d215460
commit ce5e473438
11 changed files with 654 additions and 368 deletions

View File

@@ -1,7 +1,25 @@
#include "maemodeploystep.h"
#include "maemoconstants.h"
#include "maemodeployables.h"
#include "maemodeploystepwidget.h"
#include "maemopackagecreationstep.h"
#include "maemorunconfiguration.h"
#include <coreplugin/ssh/sftpchannel.h>
#include <coreplugin/ssh/sshconnection.h>
#include <coreplugin/ssh/sshremoteprocess.h>
#include <projectexplorer/buildconfiguration.h>
#include <projectexplorer/projectexplorerconstants.h>
#include <projectexplorer/target.h>
#include <QtCore/QCryptographicHash>
#include <QtCore/QEventLoop>
#include <QtCore/QFileInfo>
#include <QtCore/QTimer>
using namespace Core;
using namespace ProjectExplorer;
namespace Qt4ProjectManager {
@@ -13,11 +31,19 @@ const QLatin1String MaemoDeployStep::Id("Qt4ProjectManager.MaemoDeployStep");
MaemoDeployStep::MaemoDeployStep(ProjectExplorer::BuildConfiguration *bc)
: BuildStep(bc, Id)
{
ctor();
}
MaemoDeployStep::MaemoDeployStep(ProjectExplorer::BuildConfiguration *bc,
MaemoDeployStep *other)
: BuildStep(bc, other)
: BuildStep(bc, other), m_lastDeployed(other->m_lastDeployed)
{
ctor();
}
MaemoDeployStep::~MaemoDeployStep() {}
void MaemoDeployStep::ctor()
{
}
@@ -28,12 +54,398 @@ bool MaemoDeployStep::init()
void MaemoDeployStep::run(QFutureInterface<bool> &fi)
{
fi.reportResult(true);
// Move to GUI thread for connection sharing with run control.
QTimer::singleShot(0, this, SLOT(start()));
MaemoDeployEventHandler eventHandler(this, fi);
}
BuildStepConfigWidget *MaemoDeployStep::createConfigWidget()
{
return new MaemoDeployStepWidget;
return new MaemoDeployStepWidget(this);
}
QVariantMap MaemoDeployStep::toMap() const
{
QVariantMap map(BuildStep::toMap());
addDeployTimesToMap(map);
return map;
}
void MaemoDeployStep::addDeployTimesToMap(QVariantMap &map) const
{
QVariantList hostList;
QVariantList fileList;
QVariantList remotePathList;
QVariantList timeList;
typedef QHash<DeployablePerHost, QDateTime>::ConstIterator DepIt;
for (DepIt it = m_lastDeployed.begin(); it != m_lastDeployed.end(); ++it) {
fileList << it.key().first.localFilePath;
remotePathList << it.key().first.remoteDir;
hostList << it.key().second;
timeList << it.value();
}
map.insert(LastDeployedHostsKey, hostList);
map.insert(LastDeployedFilesKey, fileList);
map.insert(LastDeployedRemotePathsKey, remotePathList);
map.insert(LastDeployedTimesKey, timeList);
}
bool MaemoDeployStep::fromMap(const QVariantMap &map)
{
if (!BuildStep::fromMap(map))
return false;
getDeployTimesFromMap(map);
return true;
}
void MaemoDeployStep::getDeployTimesFromMap(const QVariantMap &map)
{
const QVariantList &hostList = map.value(LastDeployedHostsKey).toList();
const QVariantList &fileList = map.value(LastDeployedFilesKey).toList();
const QVariantList &remotePathList
= map.value(LastDeployedRemotePathsKey).toList();
const QVariantList &timeList = map.value(LastDeployedTimesKey).toList();
const int elemCount
= qMin(qMin(hostList.size(), fileList.size()),
qMin(remotePathList.size(), timeList.size()));
for (int i = 0; i < elemCount; ++i) {
const MaemoDeployable d(fileList.at(i).toString(),
remotePathList.at(i).toString());
m_lastDeployed.insert(DeployablePerHost(d, hostList.at(i).toString()),
timeList.at(i).toDateTime());
}
}
const MaemoPackageCreationStep *MaemoDeployStep::packagingStep() const
{
const QList<ProjectExplorer::BuildStep *> &buildSteps
= buildConfiguration()->steps(ProjectExplorer::BuildStep::Deploy);
for (int i = buildSteps.count() - 1; i >= 0; --i) {
const MaemoPackageCreationStep * const pStep
= qobject_cast<MaemoPackageCreationStep *>(buildSteps.at(i));
if (pStep)
return pStep;
}
Q_ASSERT(!"Impossible: Maemo run configuration without packaging step.");
return 0;
}
void MaemoDeployStep::raiseError(const QString &errorString)
{
emit addTask(Task(Task::Error, errorString, QString(), -1,
Constants::TASK_CATEGORY_BUILDSYSTEM));
stop();
emit error();
}
void MaemoDeployStep::writeOutput(const QString &text,
const QTextCharFormat &format)
{
emit addOutput(text, format);
}
void MaemoDeployStep::stop()
{
if (m_installer && m_installer->isRunning()) {
disconnect(m_installer.data(), 0, this, 0);
} else if (!m_uploadsInProgress.isEmpty() || !m_linksInProgress.isEmpty()) {
m_uploadsInProgress.clear();
m_linksInProgress.clear();
m_uploader->closeChannel();
disconnect(m_uploader.data(), 0, this, 0);
}
if (m_connection)
disconnect(m_connection.data(), 0, this, 0);
m_stopped = true;
}
QString MaemoDeployStep::uploadDir() const
{
return homeDirOnDevice(m_connection->connectionParameters().uname);
}
bool MaemoDeployStep::currentlyNeedsDeployment(const QString &host,
const MaemoDeployable &deployable) const
{
const QDateTime &lastDeployed
= m_lastDeployed.value(DeployablePerHost(deployable, host));
return !lastDeployed.isValid()
|| QFileInfo(deployable.localFilePath).lastModified() > lastDeployed;
}
void MaemoDeployStep::setDeployed(const QString &host,
const MaemoDeployable &deployable)
{
m_lastDeployed.insert(DeployablePerHost(deployable, host),
QDateTime::currentDateTime());
}
MaemoDeviceConfig MaemoDeployStep::deviceConfig() const
{
// TODO: For lib template, get info from config widget
const RunConfiguration * const rc =
buildConfiguration()->target()->activeRunConfiguration();
return rc ? qobject_cast<const MaemoRunConfiguration *>(rc)->deviceConfig()
: MaemoDeviceConfig();
}
QString MaemoDeployStep::packageFileName() const
{
return QFileInfo(packageFilePath()).fileName();
}
QString MaemoDeployStep::packageFilePath() const
{
return packagingStep()->packageFilePath();
}
void MaemoDeployStep::start()
{
m_stopped = false;
if (m_stopped)
return;
// TODO: Re-use if possible (disconnect + reconnect).
if (m_connection)
disconnect(m_connection.data(), 0, this, 0);
m_connection = SshConnection::create();
const MaemoDeviceConfig &devConfig = deviceConfig();
if (!devConfig.isValid()) {
raiseError(tr("Deployment failed: No valid device set."));
return;
}
connect(m_connection.data(), SIGNAL(connected()), this,
SLOT(handleConnected()));
connect(m_connection.data(), SIGNAL(error(SshError)), this,
SLOT(handleConnectionFailure()));
m_connection->connectToHost(devConfig.server);
}
void MaemoDeployStep::handleConnected()
{
if (m_stopped)
return;
// TODO: If nothing to deploy, skip this step.
m_uploader = m_connection->createSftpChannel();
connect(m_uploader.data(), SIGNAL(initialized()), this,
SLOT(handleSftpChannelInitialized()));
connect(m_uploader.data(), SIGNAL(initializationFailed(QString)), this,
SLOT(handleSftpChannelInitializationFailed(QString)));
connect(m_uploader.data(), SIGNAL(finished(Core::SftpJobId, QString)),
this, SLOT(handleSftpJobFinished(Core::SftpJobId, QString)));
m_uploader->initialize();
}
void MaemoDeployStep::handleConnectionFailure()
{
if (m_stopped)
return;
raiseError(tr("Could not connect to host: %1")
.arg(m_connection->errorString()));
}
void MaemoDeployStep::handleSftpChannelInitialized()
{
if (m_stopped)
return;
m_uploadsInProgress.clear();
m_linksInProgress.clear();
m_needsInstall = false;
const MaemoPackageCreationStep * const pStep = packagingStep();
const QString hostName = m_connection->connectionParameters().host;
if (pStep->isPackagingEnabled()) {
const MaemoDeployable d(packageFilePath(), uploadDir());
if (currentlyNeedsDeployment(hostName, d)) {
if (!deploy(MaemoDeployable(d)))
return;
m_needsInstall = true;
} else {
m_needsInstall = false;
}
} else {
const MaemoDeployables * const deployables = pStep->deployables();
const int deployableCount = deployables->deployableCount();
for (int i = 0; i < deployableCount; ++i) {
const MaemoDeployable &d = deployables->deployableAt(i);
if (currentlyNeedsDeployment(hostName, d)
&& !deploy(MaemoDeployable(d)))
return;
}
m_needsInstall = false;
}
if (m_uploadsInProgress.isEmpty())
emit done();
}
bool MaemoDeployStep::deploy(const MaemoDeployable &deployable)
{
const QString fileName = QFileInfo(deployable.localFilePath).fileName();
const QString remoteFilePath = deployable.remoteDir + '/' + fileName;
const QString uploadFilePath = uploadDir() + '/' + fileName + '.'
+ QCryptographicHash::hash(remoteFilePath.toUtf8(),
QCryptographicHash::Md5).toHex();
const SftpJobId job = m_uploader->uploadFile(deployable.localFilePath,
uploadFilePath, SftpOverwriteExisting);
if (job == SftpInvalidJob) {
raiseError(tr("Upload failed: Could not open file '%1'")
.arg(deployable.localFilePath));
return false;
}
writeOutput(tr("Started uploading file '%1'.").arg(deployable.localFilePath));
m_uploadsInProgress.insert(job, DeployInfo(deployable, uploadFilePath));
return true;
}
void MaemoDeployStep::handleSftpChannelInitializationFailed(const QString &error)
{
if (m_stopped)
return;
raiseError(tr("Could not set up SFTP connection: %1").arg(error));
}
void MaemoDeployStep::handleSftpJobFinished(Core::SftpJobId job,
const QString &error)
{
if (m_stopped)
return;
QMap<SftpJobId, DeployInfo>::Iterator it = m_uploadsInProgress.find(job);
if (it == m_uploadsInProgress.end()) {
qWarning("%s: Job %u not found in map.", Q_FUNC_INFO, job);
return;
}
const DeployInfo &deployInfo = it.value();
if (!error.isEmpty()) {
raiseError(tr("Failed to upload file %1: %2")
.arg(deployInfo.first.localFilePath, error));
return;
}
writeOutput(tr("Successfully uploaded file '%1'.")
.arg(deployInfo.first.localFilePath));
const QString remoteFilePath = deployInfo.first.remoteDir + '/'
+ QFileInfo(deployInfo.first.localFilePath).fileName();
QByteArray linkCommand = remoteSudo().toUtf8() + " ln -sf "
+ deployInfo.second.toUtf8() + ' ' + remoteFilePath.toUtf8();
SshRemoteProcess::Ptr linkProcess
= m_connection->createRemoteProcess(linkCommand);
connect(linkProcess.data(), SIGNAL(closed(int)), this,
SLOT(handleLinkProcessFinished(int)));
m_linksInProgress.insert(linkProcess, deployInfo.first);
linkProcess->start();
m_uploadsInProgress.erase(it);
}
void MaemoDeployStep::handleLinkProcessFinished(int exitStatus)
{
if (m_stopped)
return;
SshRemoteProcess * const proc = static_cast<SshRemoteProcess *>(sender());
// TODO: List instead of map? We can't use it for lookup anyway.
QMap<SshRemoteProcess::Ptr, MaemoDeployable>::Iterator it;
for (it = m_linksInProgress.begin(); it != m_linksInProgress.end(); ++it) {
if (it.key().data() == proc)
break;
}
if (it == m_linksInProgress.end()) {
qWarning("%s: Remote process %p not found in process list.",
Q_FUNC_INFO, proc);
return;
}
const MaemoDeployable &deployable = it.value();
if (exitStatus != SshRemoteProcess::ExitedNormally
|| proc->exitCode() != 0) {
raiseError(tr("Deployment failed for file '%1': "
"Could not create link '%2' on remote system.")
.arg(deployable.localFilePath, deployable.remoteDir + '/'
+ QFileInfo(deployable.localFilePath).fileName()));
return;
}
setDeployed(m_connection->connectionParameters().host, it.value());
m_linksInProgress.erase(it);
if (m_linksInProgress.isEmpty() && m_uploadsInProgress.isEmpty()) {
if (m_needsInstall) {
writeOutput(tr("Installing package ..."));
const QByteArray cmd = remoteSudo().toUtf8() + " dpkg -i "
+ packageFileName().toUtf8();
m_installer = m_connection->createRemoteProcess(cmd);
connect(m_installer.data(), SIGNAL(closed(int)), this,
SLOT(handleInstallationFinished(int)));
connect(m_installer.data(), SIGNAL(outputAvailable(QByteArray)),
this, SLOT(handleInstallerOutput(QByteArray)));
connect(m_installer.data(),
SIGNAL(errorOutputAvailable(QByteArray)), this,
SLOT(handleInstallerErrorOutput(QByteArray)));
m_installer->start();
} else {
emit done();
}
}
}
void MaemoDeployStep::handleInstallationFinished(int exitStatus)
{
if (m_stopped)
return;
if (exitStatus != SshRemoteProcess::ExitedNormally
|| m_installer->exitCode() != 0) {
raiseError(tr("Installing package failed."));
} else {
writeOutput(tr("Package installation finished."));
emit done();
}
}
void MaemoDeployStep::handleInstallerOutput(const QByteArray &output)
{
writeOutput(QString::fromUtf8(output));
}
void MaemoDeployStep::handleInstallerErrorOutput(const QByteArray &output)
{
QTextCharFormat format;
format.setForeground(QBrush(QColor("red")));
writeOutput(output, format);
}
MaemoDeployEventHandler::MaemoDeployEventHandler(MaemoDeployStep *deployStep,
QFutureInterface<bool> &future)
: m_deployStep(deployStep), m_future(future), m_eventLoop(new QEventLoop)
{
connect(m_deployStep, SIGNAL(done()), this, SLOT(handleDeployingDone()));
connect(m_deployStep, SIGNAL(error()), this, SLOT(handleDeployingFailed()));
QTimer cancelChecker;
connect(&cancelChecker, SIGNAL(timeout()), this, SLOT(checkForCanceled()));
cancelChecker.start(500);
future.reportResult(m_eventLoop->exec() == 0);
}
void MaemoDeployEventHandler::handleDeployingDone()
{
m_eventLoop->exit(0);
}
void MaemoDeployEventHandler::handleDeployingFailed()
{
m_eventLoop->exit(1);
}
void MaemoDeployEventHandler::checkForCanceled()
{
if (m_future.isCanceled())
handleDeployingFailed();
}
} // namespace Internal

View File

@@ -1,10 +1,31 @@
#ifndef MAEMODEPLOYSTEP_H
#define MAEMODEPLOYSTEP_H
#include "maemodeployable.h"
#include "maemodeviceconfigurations.h"
#include <coreplugin/ssh/sftpdefs.h>
#include <projectexplorer/buildstep.h>
#include <QtCore/QHash>
#include <QtCore/QList>
#include <QtCore/QMap>
#include <QtCore/QPair>
#include <QtCore/QSharedPointer>
QT_BEGIN_NAMESPACE
class QEventLoop;
QT_END_NAMESPACE
namespace Core {
class SftpChannel;
class SshConnection;
class SshRemoteProcess;
}
namespace Qt4ProjectManager {
namespace Internal {
class MaemoPackageCreationStep;
class MaemoDeployStep : public ProjectExplorer::BuildStep
{
@@ -12,6 +33,27 @@ class MaemoDeployStep : public ProjectExplorer::BuildStep
friend class MaemoDeployStepFactory;
public:
MaemoDeployStep(ProjectExplorer::BuildConfiguration *bc);
virtual ~MaemoDeployStep();
MaemoDeviceConfig deviceConfig() const;
bool currentlyNeedsDeployment(const QString &host,
const MaemoDeployable &deployable) const;
void setDeployed(const QString &host, const MaemoDeployable &deployable);
signals:
void done();
void error();
private slots:
void start();
void handleConnected();
void handleConnectionFailure();
void handleSftpChannelInitialized();
void handleSftpChannelInitializationFailed(const QString &error);
void handleSftpJobFinished(Core::SftpJobId job, const QString &error);
void handleLinkProcessFinished(int exitStatus);
void handleInstallationFinished(int exitStatus);
void handleInstallerOutput(const QByteArray &output);
void handleInstallerErrorOutput(const QByteArray &output);
private:
MaemoDeployStep(ProjectExplorer::BuildConfiguration *bc,
@@ -20,8 +62,52 @@ private:
virtual void run(QFutureInterface<bool> &fi);
virtual ProjectExplorer::BuildStepConfigWidget *createConfigWidget();
virtual bool immutable() const { return true; }
virtual QVariantMap toMap() const;
virtual bool fromMap(const QVariantMap &map);
void ctor();
void stop();
void raiseError(const QString &error);
void writeOutput(const QString &text,
const QTextCharFormat &format = QTextCharFormat());
void addDeployTimesToMap(QVariantMap &map) const;
void getDeployTimesFromMap(const QVariantMap &map);
const MaemoPackageCreationStep *packagingStep() const;
bool deploy(const MaemoDeployable &deployable);
QString uploadDir() const;
QString packageFileName() const;
QString packageFilePath() const;
static const QLatin1String Id;
QSharedPointer<Core::SshConnection> m_connection;
QSharedPointer<Core::SftpChannel> m_uploader;
QSharedPointer<Core::SshRemoteProcess> m_installer;
typedef QPair<MaemoDeployable, QString> DeployInfo;
QMap<Core::SftpJobId, DeployInfo> m_uploadsInProgress;
QMap<QSharedPointer<Core::SshRemoteProcess>, MaemoDeployable> m_linksInProgress;
bool m_needsInstall;
bool m_stopped;
typedef QPair<MaemoDeployable, QString> DeployablePerHost;
QHash<DeployablePerHost, QDateTime> m_lastDeployed;
};
class MaemoDeployEventHandler : public QObject
{
Q_OBJECT
public:
MaemoDeployEventHandler(MaemoDeployStep *deployStep,
QFutureInterface<bool> &future);
private slots:
void handleDeployingDone();
void handleDeployingFailed();
void checkForCanceled();
private:
const MaemoDeployStep * const m_deployStep;
const QFutureInterface<bool> m_future;
QEventLoop * const m_eventLoop;
};
} // namespace Internal

View File

@@ -1,12 +1,19 @@
#include "maemodeploystepwidget.h"
#include "ui_maemodeploystepwidget.h"
#include "maemodeploystep.h"
#include "maemorunconfiguration.h"
#include <projectexplorer/buildconfiguration.h>
#include <projectexplorer/target.h>
namespace Qt4ProjectManager {
namespace Internal {
MaemoDeployStepWidget::MaemoDeployStepWidget() :
MaemoDeployStepWidget::MaemoDeployStepWidget(MaemoDeployStep *step) :
ProjectExplorer::BuildStepConfigWidget(),
ui(new Ui::MaemoDeployStepWidget)
ui(new Ui::MaemoDeployStepWidget),
m_step(step)
{
ui->setupUi(this);
}
@@ -18,11 +25,23 @@ MaemoDeployStepWidget::~MaemoDeployStepWidget()
void MaemoDeployStepWidget::init()
{
const ProjectExplorer::RunConfiguration * const rc =
m_step->buildConfiguration()->target()->activeRunConfiguration();
if (rc) {
connect(qobject_cast<const MaemoRunConfiguration *>(rc),
SIGNAL(deviceConfigurationChanged(ProjectExplorer::Target *)),
this, SLOT(handleDeviceUpdate()));
}
}
void MaemoDeployStepWidget::handleDeviceUpdate()
{
emit updateSummary();
}
QString MaemoDeployStepWidget::summaryText() const
{
return tr("<b>Deploy to device</b> ");
return tr("<b>Deploy to device</b>: ") + m_step->deviceConfig().name;
}
QString MaemoDeployStepWidget::displayName() const

View File

@@ -11,13 +11,14 @@ QT_END_NAMESPACE
namespace Qt4ProjectManager {
namespace Internal {
class MaemoDeployStep;
class MaemoDeployStepWidget : public ProjectExplorer::BuildStepConfigWidget
{
Q_OBJECT
public:
MaemoDeployStepWidget();
MaemoDeployStepWidget(MaemoDeployStep *step);
~MaemoDeployStepWidget();
private:
@@ -25,7 +26,10 @@ private:
virtual QString summaryText() const;
virtual QString displayName() const;
Q_SLOT void handleDeviceUpdate();
Ui::MaemoDeployStepWidget *ui;
MaemoDeployStep * const m_step;
};
} // namespace Internal

View File

@@ -36,6 +36,7 @@
#include <coreplugin/icore.h>
#include <QtCore/QCoreApplication>
#include <QtCore/QSettings>
#include <QtCore/QStringBuilder>
#include <QtGui/QDesktopServices>
@@ -54,6 +55,11 @@ QString homeDirOnDevice(const QString &uname)
: QLatin1String("/home/") + uname;
}
QString remoteSudo()
{
return QLatin1String("/usr/lib/mad-developer/devrootsh");
}
namespace {
const QLatin1String SettingsGroup("MaemoDeviceConfigs");
const QLatin1String IdCounterKey("IdCounter");
@@ -132,7 +138,8 @@ MaemoDeviceConfig::MaemoDeviceConfig(const QSettings &settings,
}
MaemoDeviceConfig::MaemoDeviceConfig()
: internalId(InvalidId)
: name(QCoreApplication::translate("MaemoDeviceConfig", "(Invalid device)")),
internalId(InvalidId)
{
}

View File

@@ -49,6 +49,7 @@ namespace Qt4ProjectManager {
namespace Internal {
QString homeDirOnDevice(const QString &uname);
QString remoteSudo();
class MaemoDeviceConfig
{

View File

@@ -29,6 +29,7 @@
#include "maemorunconfiguration.h"
#include "maemodeploystep.h"
#include "maemopackagecreationstep.h"
#include "maemorunconfigurationwidget.h"
#include "maemotoolchain.h"
@@ -67,7 +68,6 @@ MaemoRunConfiguration::MaemoRunConfiguration(Qt4Target *parent,
, m_gdbPath(source->m_gdbPath)
, m_devConfig(source->m_devConfig)
, m_arguments(source->m_arguments)
, m_lastDeployed(source->m_lastDeployed)
{
init();
}
@@ -123,32 +123,11 @@ QVariantMap MaemoRunConfiguration::toMap() const
QVariantMap map(RunConfiguration::toMap());
map.insert(DeviceIdKey, m_devConfig.internalId);
map.insert(ArgumentsKey, m_arguments);
addDeployTimesToMap(map);
const QDir dir = QDir(target()->project()->projectDirectory());
map.insert(ProFileKey, dir.relativeFilePath(m_proFilePath));
return map;
}
void MaemoRunConfiguration::addDeployTimesToMap(QVariantMap &map) const
{
QVariantList hostList;
QVariantList fileList;
QVariantList remotePathList;
QVariantList timeList;
typedef QHash<DeployablePerHost, QDateTime>::ConstIterator DepIt;
for (DepIt it = m_lastDeployed.begin(); it != m_lastDeployed.end(); ++it) {
hostList << it.key().first.localFilePath;
remotePathList << it.key().first.remoteDir;
fileList << it.key().second;
timeList << it.value();
}
map.insert(LastDeployedHostsKey, hostList);
map.insert(LastDeployedFilesKey, fileList);
map.insert(LastDeployedRemotePathsKey, remotePathList);
map.insert(LastDeployedTimesKey, timeList);
}
bool MaemoRunConfiguration::fromMap(const QVariantMap &map)
{
if (!RunConfiguration::fromMap(map))
@@ -157,47 +136,12 @@ bool MaemoRunConfiguration::fromMap(const QVariantMap &map)
setDeviceConfig(MaemoDeviceConfigurations::instance().
find(map.value(DeviceIdKey, 0).toInt()));
m_arguments = map.value(ArgumentsKey).toStringList();
getDeployTimesFromMap(map);
const QDir dir = QDir(target()->project()->projectDirectory());
m_proFilePath = dir.filePath(map.value(ProFileKey).toString());
return true;
}
void MaemoRunConfiguration::getDeployTimesFromMap(const QVariantMap &map)
{
const QVariantList &hostList = map.value(LastDeployedHostsKey).toList();
const QVariantList &fileList = map.value(LastDeployedFilesKey).toList();
const QVariantList &remotePathList
= map.value(LastDeployedRemotePathsKey).toList();
const QVariantList &timeList = map.value(LastDeployedTimesKey).toList();
const int elemCount
= qMin(qMin(hostList.size(), fileList.size()),
qMin(remotePathList.size(), timeList.size()));
for (int i = 0; i < elemCount; ++i) {
const MaemoDeployable d(fileList.at(i).toString(),
remotePathList.at(i).toString());
m_lastDeployed.insert(DeployablePerHost(d, hostList.at(i).toString()),
timeList.at(i).toDateTime());
}
}
bool MaemoRunConfiguration::currentlyNeedsDeployment(const QString &host,
const MaemoDeployable &deployable) const
{
const QDateTime &lastDeployed
= m_lastDeployed.value(DeployablePerHost(deployable, host));
return !lastDeployed.isValid()
|| QFileInfo(deployable.localFilePath).lastModified() > lastDeployed;
}
void MaemoRunConfiguration::setDeployed(const QString &host,
const MaemoDeployable &deployable)
{
m_lastDeployed.insert(DeployablePerHost(deployable, host),
QDateTime::currentDateTime());
}
void MaemoRunConfiguration::setDeviceConfig(const MaemoDeviceConfig &devConf)
{
m_devConfig = devConf;
@@ -239,6 +183,20 @@ const MaemoPackageCreationStep *MaemoRunConfiguration::packageStep() const
return 0;
}
MaemoDeployStep *MaemoRunConfiguration::deployStep() const
{
const QList<ProjectExplorer::BuildStep *> &buildSteps
= activeQt4BuildConfiguration()->steps(ProjectExplorer::BuildStep::Deploy);
for (int i = buildSteps.count() - 1; i >= 0; --i) {
MaemoDeployStep * const step
= qobject_cast<MaemoDeployStep*>(buildSteps.at(i));
if (step)
return step;
}
Q_ASSERT(!"Impossible: Maemo run configuration without deploy step.");
return 0;
}
QString MaemoRunConfiguration::maddeRoot() const
{
if (const MaemoToolChain *tc = toolchain())

View File

@@ -53,6 +53,7 @@ class Qt4BuildConfiguration;
class Qt4ProFileNode;
class Qt4Target;
class MaemoDeployStep;
class MaemoPackageCreationStep;
class MaemoRunConfigurationFactory;
@@ -70,11 +71,8 @@ public:
Qt4Target *qt4Target() const;
Qt4BuildConfiguration *activeQt4BuildConfiguration() const;
bool currentlyNeedsDeployment(const QString &host,
const MaemoDeployable &deployable) const;
void setDeployed(const QString &host, const MaemoDeployable &deployable);
const MaemoPackageCreationStep *packageStep() const;
MaemoDeployStep *deployStep() const;
QString maddeRoot() const;
QString executable() const;
@@ -107,17 +105,12 @@ private slots:
private:
void init();
const MaemoToolChain *toolchain() const;
void addDeployTimesToMap(QVariantMap &map) const;
void getDeployTimesFromMap(const QVariantMap &map);
QString m_proFilePath;
mutable QString m_gdbPath;
MaemoDeviceConfig m_devConfig;
QStringList m_arguments;
typedef QPair<MaemoDeployable, QString> DeployablePerHost;
QHash<DeployablePerHost, QDateTime> m_lastDeployed;
};
} // namespace Internal

View File

@@ -35,11 +35,11 @@
#include "maemoruncontrol.h"
#include "maemodeployables.h"
#include "maemodeploystep.h"
#include "maemopackagecreationstep.h"
#include "maemorunconfiguration.h"
#include <coreplugin/icore.h>
#include <coreplugin/progressmanager/progressmanager.h>
#include <coreplugin/ssh/sftpchannel.h>
#include <coreplugin/ssh/sshconnection.h>
#include <coreplugin/ssh/sshremoteprocess.h>
@@ -47,15 +47,12 @@
#include <debugger/debuggerplugin.h>
#include <debugger/debuggerrunner.h>
#include <extensionsystem/pluginmanager.h>
#include <projectexplorer/toolchain.h>
#include <utils/qtcassert.h>
#include <projectexplorer/projectexplorerconstants.h>
#include <projectexplorer/toolchain.h>
#include <qt4projectmanager/qt4buildconfiguration.h>
#include <utils/qtcassert.h>
#include <QtCore/QCryptographicHash>
#include <QtCore/QDir>
#include <QtCore/QFileInfo>
#include <QtCore/QFuture>
#include <QtCore/QProcess>
#include <QtCore/QStringBuilder>
#include <QtGui/QMessageBox>
@@ -137,236 +134,11 @@ void AbstractMaemoRunControl::stop()
} else if (m_initialCleaner && m_initialCleaner->isRunning()) {
disconnect(m_initialCleaner.data(), 0, this, 0);
emit finished();
} else if (m_installer && m_installer->isRunning()) {
disconnect(m_installer.data(), 0, this, 0);
emit finished();
} else if (isDeploying()) {
m_uploadsInProgress.clear();
m_linksInProgress.clear();
disconnect(m_uploader.data(), 0, this, 0);
m_progress.reportCanceled();
m_progress.reportFinished();
emit finished();
} else {
stopInternal();
}
}
void AbstractMaemoRunControl::startDeployment()
{
QTC_ASSERT(m_runConfig, return);
m_uploader = m_connection->createSftpChannel();
connect(m_uploader.data(), SIGNAL(initialized()), this,
SLOT(handleSftpChannelInitialized()));
connect(m_uploader.data(), SIGNAL(initializationFailed(QString)), this,
SLOT(handleSftpChannelInitializationFailed(QString)));
connect(m_uploader.data(), SIGNAL(finished(Core::SftpJobId, QString)),
this, SLOT(handleSftpJobFinished(Core::SftpJobId, QString)));
m_uploader->initialize();
}
void AbstractMaemoRunControl::handleSftpChannelInitialized()
{
if (m_stopped)
return;
m_uploadsInProgress.clear();
m_linksInProgress.clear();
m_needsInstall = false;
const QList<MaemoDeployable> &deployables = filesToDeploy();
foreach (const MaemoDeployable &d, deployables) {
const QString fileName = QFileInfo(d.localFilePath).fileName();
const QString remoteFilePath = d.remoteDir + '/' + fileName;
const QString uploadFilePath = uploadDir() + '/' + fileName + '.'
+ QCryptographicHash::hash(remoteFilePath.toUtf8(),
QCryptographicHash::Md5).toHex();
const SftpJobId job = m_uploader->uploadFile(d.localFilePath,
uploadFilePath, SftpOverwriteExisting);
if (job == SftpInvalidJob) {
handleError(tr("Upload failed: Could not open file '%1'")
.arg(d.localFilePath));
return;
}
emit appendMessage(this, tr("Started uploading file '%1'.")
.arg(d.localFilePath), false);
m_uploadsInProgress.insert(job, DeployInfo(d, uploadFilePath));
}
Core::ICore::instance()->progressManager()
->addTask(m_progress.future(), tr("Deploying"),
QLatin1String("Maemo.Deploy"));
if (!m_uploadsInProgress.isEmpty()) {
m_progress.setProgressRange(0, m_uploadsInProgress.count());
m_progress.setProgressValue(0);
m_progress.reportStarted();
} else {
m_progress.reportFinished();
startExecutionIfPossible();
}
}
void AbstractMaemoRunControl::handleSftpChannelInitializationFailed(const QString &error)
{
if (m_stopped)
return;
handleError(tr("Could not set up SFTP connection: %1").arg(error));
}
void AbstractMaemoRunControl::handleSftpJobFinished(Core::SftpJobId job,
const QString &error)
{
if (m_stopped)
return;
QMap<SftpJobId, DeployInfo>::Iterator it = m_uploadsInProgress.find(job);
if (it == m_uploadsInProgress.end()) {
qWarning("%s: Job %u not found in map.", Q_FUNC_INFO, job);
return;
}
const DeployInfo &deployInfo = it.value();
if (!error.isEmpty()) {
handleError(tr("Failed to upload file %1: %2")
.arg(deployInfo.first.localFilePath, error));
return;
}
m_progress.setProgressValue(m_progress.progressValue() + 1);
appendMessage(this, tr("Successfully uploaded file '%1'.")
.arg(deployInfo.first.localFilePath), false);
const QString remoteFilePath = deployInfo.first.remoteDir + '/'
+ QFileInfo(deployInfo.first.localFilePath).fileName();
QByteArray linkCommand = remoteSudo().toUtf8() + " ln -sf "
+ deployInfo.second.toUtf8() + ' ' + remoteFilePath.toUtf8();
SshRemoteProcess::Ptr linkProcess
= m_connection->createRemoteProcess(linkCommand);
connect(linkProcess.data(), SIGNAL(closed(int)), this,
SLOT(handleLinkProcessFinished(int)));
m_linksInProgress.insert(linkProcess, deployInfo.first);
linkProcess->start();
m_uploadsInProgress.erase(it);
}
void AbstractMaemoRunControl::handleLinkProcessFinished(int exitStatus)
{
if (m_stopped)
return;
SshRemoteProcess * const proc = static_cast<SshRemoteProcess *>(sender());
// TODO: List instead of map? We can't use it for lookup anyway.
QMap<SshRemoteProcess::Ptr, MaemoDeployable>::Iterator it;
for (it = m_linksInProgress.begin(); it != m_linksInProgress.end(); ++it) {
if (it.key().data() == proc)
break;
}
if (it == m_linksInProgress.end()) {
qWarning("%s: Remote process %p not found in process list.",
Q_FUNC_INFO, proc);
return;
}
const MaemoDeployable &deployable = it.value();
if (exitStatus != SshRemoteProcess::ExitedNormally
|| proc->exitCode() != 0) {
handleError(tr("Deployment failed for file '%1': "
"Could not create link '%2' on remote system.")
.arg(deployable.localFilePath, deployable.remoteDir + '/'
+ QFileInfo(deployable.localFilePath).fileName()));
return;
}
m_runConfig->setDeployed(m_devConfig.server.host, it.value());
m_linksInProgress.erase(it);
if (m_linksInProgress.isEmpty() && m_uploadsInProgress.isEmpty()) {
if (m_needsInstall) {
emit appendMessage(this, tr("Installing package ..."), false);
const QByteArray cmd = remoteSudo().toUtf8() + " dpkg -i "
+ packageFileName().toUtf8();
m_installer = m_connection->createRemoteProcess(cmd);
connect(m_installer.data(), SIGNAL(closed(int)), this,
SLOT(handleInstallationFinished(int)));
connect(m_installer.data(), SIGNAL(outputAvailable(QByteArray)),
this, SLOT(handleRemoteOutput(QByteArray)));
connect(m_installer.data(),
SIGNAL(errorOutputAvailable(QByteArray)), this,
SLOT(handleRemoteErrorOutput(QByteArray)));
m_installer->start();
} else {
handleDeploymentFinished();
}
}
}
void AbstractMaemoRunControl::handleInstallationFinished(int exitStatus)
{
if (m_stopped)
return;
if (exitStatus != SshRemoteProcess::ExitedNormally
|| m_installer->exitCode() != 0) {
handleError(tr("Installing package failed."));
} else {
emit appendMessage(this, tr("Package installation finished."), false);
handleDeploymentFinished();
}
}
void AbstractMaemoRunControl::handleDeploymentFinished()
{
emit appendMessage(this, tr("Deployment finished."), false);
m_progress.reportFinished();
startExecutionIfPossible();
}
QList<MaemoDeployable> AbstractMaemoRunControl::filesToDeploy()
{
QList<MaemoDeployable> deployableList;
if (m_runConfig->packageStep()->isPackagingEnabled()) {
const MaemoDeployable d(packageFilePath(), uploadDir());
m_needsInstall = addDeployableIfNeeded(deployableList, d);
} else {
const MaemoDeployables * const deployables
= m_runConfig->packageStep()->deployables();
const int deployableCount = deployables->deployableCount();
for (int i = 0; i < deployableCount; ++i) {
const MaemoDeployable &d = deployables->deployableAt(i);
addDeployableIfNeeded(deployableList, d);
}
m_needsInstall = false;
}
return deployableList;
}
bool AbstractMaemoRunControl::addDeployableIfNeeded(QList<MaemoDeployable> &deployables,
const MaemoDeployable &deployable)
{
if (m_runConfig->currentlyNeedsDeployment(m_devConfig.server.host,
deployable)) {
deployables << deployable;
return true;
} else {
return false;
}
}
bool AbstractMaemoRunControl::isDeploying() const
{
return !m_uploadsInProgress.isEmpty() || !m_linksInProgress.isEmpty();
}
QString AbstractMaemoRunControl::packageFileName() const
{
return QFileInfo(packageFilePath()).fileName();
}
QString AbstractMaemoRunControl::packageFilePath() const
{
return m_runConfig->packageStep()->packageFilePath();
}
QString AbstractMaemoRunControl::executableFilePathOnTarget() const
{
const MaemoDeployables * const deployables
@@ -374,11 +146,6 @@ QString AbstractMaemoRunControl::executableFilePathOnTarget() const
return deployables->remoteExecutableFilePath(m_runConfig->executable());
}
bool AbstractMaemoRunControl::isCleaning() const
{
return m_initialCleaner && m_initialCleaner->isRunning();
}
void AbstractMaemoRunControl::startExecutionIfPossible()
{
if (executableFilePathOnTarget().isEmpty()) {
@@ -436,7 +203,7 @@ void AbstractMaemoRunControl::handleRemoteErrorOutput(const QByteArray &output)
bool AbstractMaemoRunControl::isRunning() const
{
return isDeploying() || (m_runner && m_runner->isRunning());
return m_runner && m_runner->isRunning();
}
void AbstractMaemoRunControl::stopRunning(bool forDebugging)
@@ -488,7 +255,7 @@ void AbstractMaemoRunControl::handleInitialCleanupFinished(int exitStatus)
.arg(m_initialCleaner->errorString()));
} else {
emit appendMessage(this, tr("Initial cleanup done."), false);
startDeployment();
startExecutionIfPossible();
}
}
@@ -507,11 +274,6 @@ const QString AbstractMaemoRunControl::uploadDir() const
return homeDirOnDevice(m_devConfig.server.uname);
}
QString AbstractMaemoRunControl::remoteSudo() const
{
return QLatin1String("/usr/lib/mad-developer/devrootsh");
}
const QString AbstractMaemoRunControl::targetCmdLinePrefix() const
{
return QString::fromLocal8Bit("%1 chmod a+x %2 && source /etc/profile && DISPLAY=:0.0 ")
@@ -565,6 +327,7 @@ MaemoDebugRunControl::MaemoDebugRunControl(RunConfiguration *runConfiguration)
: AbstractMaemoRunControl(runConfiguration, ProjectExplorer::Constants::DEBUGMODE)
, m_debuggerRunControl(0)
, m_startParams(new DebuggerStartParameters)
, m_uploadJob(SftpInvalidJob)
{
#ifdef USE_GDBSERVER
m_startParams->startMode = AttachToRemote;
@@ -608,26 +371,101 @@ QString MaemoDebugRunControl::remoteCall() const
void MaemoDebugRunControl::startExecution()
{
#ifdef USE_GDBSERVER
AbstractMaemoRunControl::startExecution();
#else
const QString &dumperLib = m_runConfig->dumperLib();
if (!dumperLib.isEmpty()
&& m_runConfig->deployStep()->currentlyNeedsDeployment(m_devConfig.server.host,
MaemoDeployable(dumperLib, uploadDir()))) {
m_uploader = m_connection->createSftpChannel();
connect(m_uploader.data(), SIGNAL(initialized()), this,
SLOT(handleSftpChannelInitialized()));
connect(m_uploader.data(), SIGNAL(initializationFailed(QString)), this,
SLOT(handleSftpChannelInitializationFailed(QString)));
connect(m_uploader.data(), SIGNAL(finished(Core::SftpJobId, QString)),
this, SLOT(handleSftpJobFinished(Core::SftpJobId, QString)));
m_uploader->initialize();
} else {
startDebugging();
#endif
}
}
void MaemoDebugRunControl::handleSftpChannelInitialized()
{
if (m_stopped)
return;
const QString dumperLib = m_runConfig->dumperLib();
const QString fileName = QFileInfo(dumperLib).fileName();
const QString remoteFilePath = uploadDir() + '/' + fileName;
m_uploadJob = m_uploader->uploadFile(dumperLib, remoteFilePath,
SftpOverwriteExisting);
if (m_uploadJob == SftpInvalidJob) {
handleError(tr("Upload failed: Could not open file '%1'")
.arg(dumperLib));
} else {
emit appendMessage(this,
tr("Started uploading debugging helpers ('%1').").arg(dumperLib),
false);
}
}
void MaemoDebugRunControl::handleSftpChannelInitializationFailed(const QString &error)
{
handleError(error);
}
void MaemoDebugRunControl::handleSftpJobFinished(Core::SftpJobId job,
const QString &error)
{
if (m_stopped)
return;
if (job != m_uploadJob) {
qWarning("Warning: Unknown debugging helpers upload job %d finished.", job);
return;
}
if (!error.isEmpty()) {
handleError(tr("Error: Could not upload debugging helpers."));
} else {
m_runConfig->deployStep()->setDeployed(m_devConfig.server.host,
MaemoDeployable(m_runConfig->dumperLib(), uploadDir()));
emit appendMessage(this,
tr("Finished uploading debugging helpers."), false);
startDebugging();
}
}
void MaemoDebugRunControl::startDebugging()
{
#ifdef USE_GDBSERVER
AbstractMaemoRunControl::startExecution();
#else
DebuggerPlugin::startDebugger(m_debuggerRunControl);
#endif
}
bool MaemoDebugRunControl::isDeploying() const
{
return m_uploader && m_uploadJob != SftpInvalidJob;
}
void MaemoDebugRunControl::stopInternal()
{
if (isDeploying()) {
disconnect(m_uploader.data(), 0, this, 0);
m_uploader->closeChannel();
m_uploadJob = SftpInvalidJob;
emit finished();
} else if (m_debuggerRunControl && m_debuggerRunControl->engine()) {
m_debuggerRunControl->engine()->quitDebugger();
} else {
emit finished();
}
}
bool MaemoDebugRunControl::isRunning() const
{
return AbstractMaemoRunControl::isRunning()
return isDeploying() || AbstractMaemoRunControl::isRunning()
|| m_debuggerRunControl->state() != DebuggerNotReady;
}
@@ -642,7 +480,7 @@ void MaemoDebugRunControl::debuggingFinished()
void MaemoDebugRunControl::handleRemoteProcessStarted()
{
startDebugging();
DebuggerPlugin::startDebugger(m_debuggerRunControl);
}
void MaemoDebugRunControl::debuggerOutput(const QString &output)
@@ -659,14 +497,5 @@ QString MaemoDebugRunControl::gdbServerPort() const
// but we will make sure we use the right port from the information file.
}
QList<MaemoDeployable> MaemoDebugRunControl::filesToDeploy()
{
QList<MaemoDeployable> deployables
= AbstractMaemoRunControl::filesToDeploy();
const MaemoDeployable d(m_runConfig->dumperLib(), uploadDir());
addDeployableIfNeeded(deployables, d);
return deployables;
}
} // namespace Internal
} // namespace Qt4ProjectManager

View File

@@ -36,14 +36,10 @@
#define MAEMORUNCONTROL_H
#include "maemodeviceconfigurations.h"
#include "maemodeployable.h"
#include <coreplugin/ssh/sftpdefs.h>
#include <projectexplorer/runconfiguration.h>
#include <QtCore/QFutureInterface>
#include <QtCore/QMap>
#include <QtCore/QScopedPointer>
#include <QtCore/QString>
QT_BEGIN_NAMESPACE
@@ -78,7 +74,6 @@ protected:
virtual void start();
virtual void stop();
void startDeployment();
void stopRunning(bool forDebugging);
virtual void startExecution();
void handleError(const QString &errString);
@@ -87,22 +82,12 @@ protected:
const QString targetCmdLinePrefix() const;
QString targetCmdLineSuffix() const;
const QString uploadDir() const;
QString packageFileName() const;
QString packageFilePath() const;
QString executableFilePathOnTarget() const;
virtual QList<MaemoDeployable> filesToDeploy();
bool addDeployableIfNeeded(QList<MaemoDeployable> &deployables,
const MaemoDeployable &deployable);
private slots:
void handleConnected();
void handleConnectionFailure();
void handleInitialCleanupFinished(int exitStatus);
void handleSftpChannelInitialized();
void handleSftpChannelInitializationFailed(const QString &error);
void handleSftpJobFinished(Core::SftpJobId job, const QString &error);
void handleLinkProcessFinished(int exitStatus);
void handleInstallationFinished(int exitStatus);
virtual void handleRemoteProcessStarted() {}
void handleRemoteProcessFinished(int exitStatus);
void handleRemoteOutput(const QByteArray &output);
@@ -111,6 +96,8 @@ private slots:
protected:
MaemoRunConfiguration *m_runConfig; // TODO this pointer can be invalid
const MaemoDeviceConfig m_devConfig;
QSharedPointer<Core::SshConnection> m_connection;
bool m_stopped;
private:
virtual void stopInternal()=0;
@@ -120,25 +107,10 @@ private:
void cancelActions();
template<class SshChannel> void closeSshChannel(SshChannel &channel);
void startExecutionIfPossible();
bool isCleaning() const;
bool isDeploying() const;
QString remoteSudo() const;
QString uploadFilePath(const MaemoDeployable &deployable) const;
void handleDeploymentFinished();
QFutureInterface<void> m_progress;
QSharedPointer<Core::SshConnection> m_connection;
QSharedPointer<Core::SftpChannel> m_uploader;
QSharedPointer<Core::SshRemoteProcess> m_installer;
QSharedPointer<Core::SshRemoteProcess> m_runner;
QSharedPointer<Core::SshRemoteProcess> m_stopper;
QSharedPointer<Core::SshRemoteProcess> m_initialCleaner;
typedef QPair<MaemoDeployable, QString> DeployInfo;
QMap<Core::SftpJobId, DeployInfo> m_uploadsInProgress;
QMap<QSharedPointer<Core::SshRemoteProcess>, MaemoDeployable> m_linksInProgress;
bool m_needsInstall;
bool m_stopped;
};
class MaemoRunControl : public AbstractMaemoRunControl
@@ -165,18 +137,23 @@ private slots:
virtual void handleRemoteProcessStarted();
void debuggerOutput(const QString &output);
void debuggingFinished();
void handleSftpChannelInitialized();
void handleSftpChannelInitializationFailed(const QString &error);
void handleSftpJobFinished(Core::SftpJobId job, const QString &error);
private:
virtual void stopInternal();
virtual void startExecution();
virtual QString remoteCall() const;
virtual QList<MaemoDeployable> filesToDeploy();
QString gdbServerPort() const;
void startDebugging();
bool isDeploying() const;
Debugger::DebuggerRunControl *m_debuggerRunControl;
QSharedPointer<Debugger::DebuggerStartParameters> m_startParams;
QSharedPointer<Core::SftpChannel> m_uploader;
Core::SftpJobId m_uploadJob;
};
} // namespace Internal

View File

@@ -288,8 +288,8 @@ Qt4BuildConfiguration *Qt4Target::addQt4BuildConfiguration(QString displayName,
} else if (id() == Constants::MAEMO_DEVICE_TARGET_ID) {
bc->insertStep(ProjectExplorer::BuildStep::Deploy, 2,
new MaemoPackageCreationStep(bc));
// bc->insertStep(ProjectExplorer::BuildStep::Deploy, 2,
// new MaemoDeployStep(bc));
bc->insertStep(ProjectExplorer::BuildStep::Deploy, 3,
new MaemoDeployStep(bc));
}
MakeStep* cleanStep = new MakeStep(bc);