<commit_before>/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (<EMAIL>)
**
** Commercial Usage
**
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
**
** 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.
**
** If you are unsure which license is appropriate for your use, please
** contact the sales department at http://qt.nokia.com/contact.
**
**************************************************************************/

#include "maemodeploystep.h"

#include "maemoconstants.h"
#include "maemodeployables.h"
#include "maemodeploystepwidget.h"
#include "maemodeviceconfiglistmodel.h"
#include "maemoglobal.h"
#include "maemopackagecreationstep.h"
#include "maemoremotemounter.h"
#include "maemorunconfiguration.h"
#include "maemotoolchain.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 <qt4projectmanager/qt4buildconfiguration.h>

#include <QtCore/QCryptographicHash>
#include <QtCore/QDir>
#include <QtCore/QEventLoop>
#include <QtCore/QFileInfo>
#include <QtCore/QTimer>

using namespace Core;
using namespace ProjectExplorer;

namespace Qt4ProjectManager {
namespace Internal {
namespace { const int DefaultMountPort = 1050; }

const QLatin1String MaemoDeployStep::Id("Qt4ProjectManager.MaemoDeployStep");

MaemoDeployStep::MaemoDeployStep(ProjectExplorer::BuildStepList *parent)
    : BuildStep(parent, Id), m_deployables(new MaemoDeployables(this))
{
    ctor();
}

MaemoDeployStep::MaemoDeployStep(ProjectExplorer::BuildStepList *parent,
    MaemoDeployStep *other)
    : BuildStep(parent, other), m_deployables(new MaemoDeployables(this)),
      m_lastDeployed(other->m_lastDeployed)
{
    ctor();
}

MaemoDeployStep::~MaemoDeployStep()
{
    delete m_deployables;
}

void MaemoDeployStep::ctor()
{
    m_connecting = false;
    m_needsInstall = false;
    m_stopped = false;
    m_deviceConfigModel = new MaemoDeviceConfigListModel(this);
#ifdef DEPLOY_VIA_MOUNT
    m_canStart = true;
    m_cleanupTimer = new QTimer(this);
    m_cleanupTimer->setSingleShot(true);
    connect(m_cleanupTimer, SIGNAL(timeout()), this,
        SLOT(handleCleanupTimeout()));
    const Qt4BuildConfiguration * const buildConfig
        = qobject_cast<Qt4BuildConfiguration *>(buildConfiguration());
    const MaemoToolChain * const toolchain
        = dynamic_cast<MaemoToolChain *>(buildConfig->toolChain());
    m_mounter = new MaemoRemoteMounter(this,toolchain);
    connect(m_mounter, SIGNAL(mounted()), this, SLOT(handleMounted()));
    connect(m_mounter, SIGNAL(unmounted()), this, SLOT(handleUnmounted()));
    connect(m_mounter, SIGNAL(error(QString)), this,
        SLOT(handleMountError(QString)));
    connect(m_mounter, SIGNAL(reportProgress(QString)), this,
        SLOT(handleProgressReport(QString)));
#endif
}

bool MaemoDeployStep::init()
{
    return true;
}

void MaemoDeployStep::run(QFutureInterface<bool> &fi)
{
    // Move to GUI thread for connection sharing with run control.
    QTimer::singleShot(0, this, SLOT(start()));

    MaemoDeployEventHandler eventHandler(this, fi);
    connect (&eventHandler, SIGNAL(destroyed()), this, SLOT(stop()));
}

BuildStepConfigWidget *MaemoDeployStep::createConfigWidget()
{
    return new MaemoDeployStepWidget(this);
}

QVariantMap MaemoDeployStep::toMap() const
{
    QVariantMap map(BuildStep::toMap());
#ifdef DEPLOY_VIA_MOUNT
    map.insert(DeployMountPortKey, m_mountPort);
#endif
    addDeployTimesToMap(map);
    map.unite(m_deviceConfigModel->toMap());
    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;
#ifdef DEPLOY_VIA_MOUNT
    m_mountPort = map.value(DeployMountPortKey, DefaultMountPort).toInt();
#endif
    getDeployTimesFromMap(map);
    m_deviceConfigModel->fromMap(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 MaemoPackageCreationStep * const step
        = MaemoGlobal::buildStep<MaemoPackageCreationStep>(target()->activeDeployConfiguration());
    Q_ASSERT_X(step, Q_FUNC_INFO,
        "Impossible: Maemo build configuration without packaging step.");
    return step;
}

void MaemoDeployStep::raiseError(const QString &errorString)
{
    emit addTask(Task(Task::Error, errorString, QString(), -1,
        Constants::TASK_CATEGORY_BUILDSYSTEM));
    emit error();
}

void MaemoDeployStep::writeOutput(const QString &text,
    BuildStep::OutputFormat format)
{
    emit addOutput(text, format);
}

void MaemoDeployStep::stop()
{
#ifdef DEPLOY_VIA_MOUNT
    if (m_stopped)
        return;

    if (m_connecting || m_needsInstall || !m_filesToCopy.isEmpty()
        || (m_installer && m_installer->isRunning())
        || m_currentDeployAction) {
        if (m_connection && m_connection->state() == SshConnection::Connected
            && ((m_installer && m_installer->isRunning())
                || m_currentDeployAction)) {
            const QByteArray programToKill
                = m_currentDeployAction ? "cp" : "dpkg";
            const QByteArray cmdLine = "pkill -x " + programToKill
                + "; sleep 1; pkill -x -9 " + programToKill;
            SshRemoteProcess::Ptr killProc
                = m_connection->createRemoteProcess(cmdLine);
            killProc->start();
        }
        m_stopped = true;
        m_canStart = false;
        m_needsInstall = false;
        m_filesToCopy.clear();
        m_connecting = false;
        m_cleanupTimer->start(5000);
        m_mounter->stop();
    }
#else
    m_stopped = true;
    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();
        disconnect(m_uploader.data(), 0, this, 0);
        m_uploader->closeChannel();
    }
    if (m_connection)
        disconnect(m_connection.data(), 0, this, 0);
#endif
}

#ifndef DEPLOY_VIA_MOUNT
QString MaemoDeployStep::uploadDir() const
{
    return MaemoGlobal::homeDirOnDevice(m_connection->connectionParameters().uname);
}
#endif

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
{
    return deviceConfigModel()->current();
}

MaemoDeviceConfigListModel *MaemoDeployStep::deviceConfigModel() const
{
    const MaemoRunConfiguration * const rc =
        qobject_cast<const MaemoRunConfiguration *>(buildConfiguration()
            ->target()->activeRunConfiguration());
    return rc ? rc->deviceConfigModel() : m_deviceConfigModel;
}

void MaemoDeployStep::start()
{
#ifdef DEPLOY_VIA_MOUNT
    if (!m_canStart) {
        raiseError(tr("Can't start deployment, haven't cleaned up from last time yet."));
        return;
    }
    m_cleanupTimer->stop();
#endif
    m_stopped = false;

    // 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);
    m_connecting = true;
}
<commit_msg>Maemo: Cache SSH connection in deployment step.<commit_after>/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (<EMAIL>)
**
** Commercial Usage
**
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
**
** 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.
**
** If you are unsure which license is appropriate for your use, please
** contact the sales department at http://qt.nokia.com/contact.
**
**************************************************************************/

#include "maemodeploystep.h"

#include "maemoconstants.h"
#include "maemodeployables.h"
#include "maemodeploystepwidget.h"
#include "maemodeviceconfiglistmodel.h"
#include "maemoglobal.h"
#include "maemopackagecreationstep.h"
#include "maemoremotemounter.h"
#include "maemorunconfiguration.h"
#include "maemotoolchain.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 <qt4projectmanager/qt4buildconfiguration.h>

#include <QtCore/QCryptographicHash>
#include <QtCore/QDir>
#include <QtCore/QEventLoop>
#include <QtCore/QFileInfo>
#include <QtCore/QTimer>

using namespace Core;
using namespace ProjectExplorer;

namespace Qt4ProjectManager {
namespace Internal {
namespace { const int DefaultMountPort = 1050; }

const QLatin1String MaemoDeployStep::Id("Qt4ProjectManager.MaemoDeployStep");

MaemoDeployStep::MaemoDeployStep(ProjectExplorer::BuildStepList *parent)
    : BuildStep(parent, Id), m_deployables(new MaemoDeployables(this))
{
    ctor();
}

MaemoDeployStep::MaemoDeployStep(ProjectExplorer::BuildStepList *parent,
    MaemoDeployStep *other)
    : BuildStep(parent, other), m_deployables(new MaemoDeployables(this)),
      m_lastDeployed(other->m_lastDeployed)
{
    ctor();
}

MaemoDeployStep::~MaemoDeployStep()
{
    delete m_deployables;
}

void MaemoDeployStep::ctor()
{
    m_connecting = false;
    m_needsInstall = false;
    m_stopped = false;
    m_deviceConfigModel = new MaemoDeviceConfigListModel(this);
#ifdef DEPLOY_VIA_MOUNT
    m_canStart = true;
    m_cleanupTimer = new QTimer(this);
    m_cleanupTimer->setSingleShot(true);
    connect(m_cleanupTimer, SIGNAL(timeout()), this,
        SLOT(handleCleanupTimeout()));
    const Qt4BuildConfiguration * const buildConfig
        = qobject_cast<Qt4BuildConfiguration *>(buildConfiguration());
    const MaemoToolChain * const toolchain
        = dynamic_cast<MaemoToolChain *>(buildConfig->toolChain());
    m_mounter = new MaemoRemoteMounter(this,toolchain);
    connect(m_mounter, SIGNAL(mounted()), this, SLOT(handleMounted()));
    connect(m_mounter, SIGNAL(unmounted()), this, SLOT(handleUnmounted()));
    connect(m_mounter, SIGNAL(error(QString)), this,
        SLOT(handleMountError(QString)));
    connect(m_mounter, SIGNAL(reportProgress(QString)), this,
        SLOT(handleProgressReport(QString)));
#endif
}

bool MaemoDeployStep::init()
{
    return true;
}

void MaemoDeployStep::run(QFutureInterface<bool> &fi)
{
    // Move to GUI thread for connection sharing with run control.
    QTimer::singleShot(0, this, SLOT(start()));

    MaemoDeployEventHandler eventHandler(this, fi);
    connect (&eventHandler, SIGNAL(destroyed()), this, SLOT(stop()));
}

BuildStepConfigWidget *MaemoDeployStep::createConfigWidget()
{
    return new MaemoDeployStepWidget(this);
}

QVariantMap MaemoDeployStep::toMap() const
{
    QVariantMap map(BuildStep::toMap());
#ifdef DEPLOY_VIA_MOUNT
    map.insert(DeployMountPortKey, m_mountPort);
#endif
    addDeployTimesToMap(map);
    map.unite(m_deviceConfigModel->toMap());
    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;
#ifdef DEPLOY_VIA_MOUNT
    m_mountPort = map.value(DeployMountPortKey, DefaultMountPort).toInt();
#endif
    getDeployTimesFromMap(map);
    m_deviceConfigModel->fromMap(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 MaemoPackageCreationStep * const step
        = MaemoGlobal::buildStep<MaemoPackageCreationStep>(target()->activeDeployConfiguration());
    Q_ASSERT_X(step, Q_FUNC_INFO,
        "Impossible: Maemo build configuration without packaging step.");
    return step;
}

void MaemoDeployStep::raiseError(const QString &errorString)
{
    emit addTask(Task(Task::Error, errorString, QString(), -1,
        Constants::TASK_CATEGORY_BUILDSYSTEM));
    emit error();
}

void MaemoDeployStep::writeOutput(const QString &text,
    BuildStep::OutputFormat format)
{
    emit addOutput(text, format);
}

void MaemoDeployStep::stop()
{
#ifdef DEPLOY_VIA_MOUNT
    if (m_stopped)
        return;

    if (m_connecting || m_needsInstall || !m_filesToCopy.isEmpty()
        || (m_installer && m_installer->isRunning())
        || m_currentDeployAction) {
        if (m_connection && m_connection->state() == SshConnection::Connected
            && ((m_installer && m_installer->isRunning())
                || m_currentDeployAction)) {
            const QByteArray programToKill
                = m_currentDeployAction ? "cp" : "dpkg";
            const QByteArray cmdLine = "pkill -x " + programToKill
                + "; sleep 1; pkill -x -9 " + programToKill;
            SshRemoteProcess::Ptr killProc
                = m_connection->createRemoteProcess(cmdLine);
            killProc->start();
        }
        m_stopped = true;
        m_canStart = false;
        m_needsInstall = false;
        m_filesToCopy.clear();
        m_connecting = false;
        m_cleanupTimer->start(5000);
        m_mounter->stop();
    }
#else
    m_stopped = true;
    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();
        disconnect(m_uploader.data(), 0, this, 0);
        m_uploader->closeChannel();
    }
    if (m_connection)
        disconnect(m_connection.data(), 0, this, 0);
#endif
}

#ifndef DEPLOY_VIA_MOUNT
QString MaemoDeployStep::uploadDir() const
{
    return MaemoGlobal::homeDirOnDevice(m_connection->connectionParameters().uname);
}
#endif

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
{
    return deviceConfigModel()->current();
}

MaemoDeviceConfigListModel *MaemoDeployStep::deviceConfigModel() const
{
    const MaemoRunConfiguration * const rc =
        qobject_cast<const MaemoRunConfiguration *>(buildConfiguration()
            ->target()->activeRunConfiguration());
    return rc ? rc->deviceConfigModel() : m_deviceConfigModel;
}

void MaemoDeployStep::start()
{
#ifdef DEPLOY_VIA_MOUNT
    if (!m_canStart) {
        raiseError(tr("Can't start deployment, haven't cleaned up from last time yet."));
        return;
    }
    m_cleanupTimer->stop();
#endif
    m_stopped = false;

    const MaemoDeviceConfig &devConfig = deviceConfig();
    if (!devConfig.isValid()) {
        raiseError(tr("Deployment failed: No valid device set."));
        return;
    }

    if (m_connection)
        disconnect(m_connection.data(), 0, this, 0);
    const bool canReUse = m_connection
        && m_connection->state() == SshConnection::Connected
        && m_connection->connectionParameters() == devConfig.server;
    if (!canReUse)
        m_connection = SshConnection::create();
    connect(m_connection.data(), SIGNAL(connected()), this,
        SLOT(handleConnected()));
    connect(m_connection.data(), SIGNAL(error(SshError)), this,
        SLOT(handleConnectionFailure()));
    if (canReUse) {
        handleConnected();
    } else {
        writeOutput(tr("Connecting to device..."));
        m_connecting = true;
        m_connection->connectToHost(devConfig.server);
    }
}

{'fraction_non_alphanumeric': 0.13006868002634303, 'fraction_numerical': 0.003575124658951924, 'mean_word_length': 4.368434343434344, 'pattern_counts': {'":': 0, '<': 59, '<?xml version=': 0, '>': 89, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6200137', 'n_tokens_mistral': 6769, 'n_tokens_neox': 6506, 'n_words': 1339}