abstractremotelinuxdeployservice.cpp 10.9 KB
Newer Older
hjk's avatar
hjk committed
1
/****************************************************************************
2
**
3
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
hjk's avatar
hjk committed
4
** Contact: http://www.qt-project.org/legal
5
**
hjk's avatar
hjk committed
6
** This file is part of Qt Creator.
7
**
hjk's avatar
hjk committed
8 9 10 11 12
** 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
Eike Ziller's avatar
Eike Ziller committed
13 14
** conditions see http://www.qt.io/licensing.  For further information
** use the contact form at http://www.qt.io/contact-us.
15 16
**
** GNU Lesser General Public License Usage
hjk's avatar
hjk committed
17
** Alternatively, this file may be used under the terms of the GNU Lesser
Eike Ziller's avatar
Eike Ziller committed
18 19 20 21 22 23
** General Public License version 2.1 or version 3 as published by the Free
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
** LICENSE.LGPLv3 included in the packaging of this file.  Please review the
** following information to ensure the GNU Lesser General Public License
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
hjk's avatar
hjk committed
24 25 26
**
** In addition, as a special exception, Digia gives you certain additional
** rights.  These rights are described in the Digia Qt LGPL Exception
27 28
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
hjk's avatar
hjk committed
29
****************************************************************************/
30

31 32
#include "abstractremotelinuxdeployservice.h"

33
#include <projectexplorer/deployablefile.h>
Tobias Hunger's avatar
Tobias Hunger committed
34
#include <projectexplorer/target.h>
Tobias Hunger's avatar
Tobias Hunger committed
35
#include <qtsupport/qtkitinformation.h>
36
#include <utils/qtcassert.h>
37 38
#include <ssh/sshconnection.h>
#include <ssh/sshconnectionmanager.h>
39

40 41 42 43
#include <QDateTime>
#include <QFileInfo>
#include <QPointer>
#include <QString>
44

45
using namespace ProjectExplorer;
46
using namespace QSsh;
47 48 49 50 51

namespace RemoteLinux {
namespace Internal {

namespace {
52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68
class DeployParameters
{
public:
    DeployParameters(const DeployableFile &d, const QString &h, const QString &s)
        : file(d), host(h), sysroot(s) {}

    bool operator==(const DeployParameters &other) const {
        return file == other.file && host == other.host && sysroot == other.sysroot;
    }

    DeployableFile file;
    QString host;
    QString sysroot;
};
uint qHash(const DeployParameters &p) {
    return qHash(qMakePair(qMakePair(p.file, p.host), p.sysroot));
}
69 70 71 72 73

enum State { Inactive, SettingUpDevice, Connecting, Deploying };

// TODO: Just change these...
const char LastDeployedHostsKey[] = "Qt4ProjectManager.MaemoRunConfiguration.LastDeployedHosts";
74
const char LastDeployedSysrootsKey[] = "Qt4ProjectManager.MaemoRunConfiguration.LastDeployedSysroots";
75 76 77 78 79 80 81 82 83
const char LastDeployedFilesKey[] = "Qt4ProjectManager.MaemoRunConfiguration.LastDeployedFiles";
const char LastDeployedRemotePathsKey[] = "Qt4ProjectManager.MaemoRunConfiguration.LastDeployedRemotePaths";
const char LastDeployedTimesKey[] = "Qt4ProjectManager.MaemoRunConfiguration.LastDeployedTimes";

} // anonymous namespace

class AbstractRemoteLinuxDeployServicePrivate
{
public:
84
    AbstractRemoteLinuxDeployServicePrivate()
Tobias Hunger's avatar
Tobias Hunger committed
85
        : kit(0), connection(0), state(Inactive), stopRequested(false) {}
86

87
    IDevice::ConstPtr deviceConfiguration;
88
    QPointer<Target> target;
Tobias Hunger's avatar
Tobias Hunger committed
89
    Kit *kit;
90
    SshConnection *connection;
91 92 93
    State state;
    bool stopRequested;

94
    QHash<DeployParameters, QDateTime> lastDeployed;
95 96 97 98 99 100
};
} // namespace Internal

using namespace Internal;

AbstractRemoteLinuxDeployService::AbstractRemoteLinuxDeployService(QObject *parent)
101
    : QObject(parent), d(new AbstractRemoteLinuxDeployServicePrivate)
102 103 104 105 106
{
}

AbstractRemoteLinuxDeployService::~AbstractRemoteLinuxDeployService()
{
107
    delete d;
108 109
}

110
const Target *AbstractRemoteLinuxDeployService::target() const
111
{
112
    return d->target;
113 114
}

Tobias Hunger's avatar
Tobias Hunger committed
115
const Kit *AbstractRemoteLinuxDeployService::profile() const
116
{
Tobias Hunger's avatar
Tobias Hunger committed
117
    return d->kit;
118 119
}

120
IDevice::ConstPtr AbstractRemoteLinuxDeployService::deviceConfiguration() const
121
{
122
    return d->deviceConfiguration;
123 124
}

125
SshConnection *AbstractRemoteLinuxDeployService::connection() const
126
{
127
    return d->connection;
128 129 130 131
}

void AbstractRemoteLinuxDeployService::saveDeploymentTimeStamp(const DeployableFile &deployableFile)
{
132
    if (!d->target)
133
        return;
Tobias Hunger's avatar
Tobias Hunger committed
134
    QString systemRoot;
Tobias Hunger's avatar
Tobias Hunger committed
135 136
    if (SysRootKitInformation::hasSysRoot(d->kit))
        systemRoot = SysRootKitInformation::sysRoot(d->kit).toString();
137
    d->lastDeployed.insert(DeployParameters(deployableFile,
Tobias Hunger's avatar
Tobias Hunger committed
138 139 140
                                            deviceConfiguration()->sshParameters().host,
                                            systemRoot),
                           QDateTime::currentDateTime());
141 142 143 144
}

bool AbstractRemoteLinuxDeployService::hasChangedSinceLastDeployment(const DeployableFile &deployableFile) const
{
145
    if (!target())
146
        return true;
Tobias Hunger's avatar
Tobias Hunger committed
147
    QString systemRoot;
Tobias Hunger's avatar
Tobias Hunger committed
148 149
    if (SysRootKitInformation::hasSysRoot(d->kit))
        systemRoot = SysRootKitInformation::sysRoot(d->kit).toString();
150
    const QDateTime &lastDeployed = d->lastDeployed.value(DeployParameters(deployableFile,
Tobias Hunger's avatar
Tobias Hunger committed
151
        deviceConfiguration()->sshParameters().host, systemRoot));
152
    return !lastDeployed.isValid()
153
        || deployableFile.localFilePath().toFileInfo().lastModified() > lastDeployed;
154 155
}

156
void AbstractRemoteLinuxDeployService::setTarget(Target *target)
157
{
158 159 160
    d->target = target;
    if (target)
        d->kit = target->kit();
161
    else
Tobias Hunger's avatar
Tobias Hunger committed
162 163
        d->kit = 0;
    d->deviceConfiguration = DeviceKitInformation::device(d->kit);
164 165
}

166 167 168 169 170
void AbstractRemoteLinuxDeployService::setDevice(const IDevice::ConstPtr &device)
{
    d->deviceConfiguration = device;
}

171 172
void AbstractRemoteLinuxDeployService::start()
{
173
    QTC_ASSERT(d->state == Inactive, return);
174 175 176 177 178 179 180 181 182 183 184 185 186 187

    QString errorMsg;
    if (!isDeploymentPossible(&errorMsg)) {
        emit errorMessage(errorMsg);
        emit finished();
        return;
    }

    if (!isDeploymentNecessary()) {
        emit progressMessage(tr("No deployment action necessary. Skipping."));
        emit finished();
        return;
    }

188
    d->state = SettingUpDevice;
189 190 191 192 193
    doDeviceSetup();
}

void AbstractRemoteLinuxDeployService::stop()
{
194
    if (d->stopRequested)
195 196
        return;

197
    switch (d->state) {
198 199 200
    case Inactive:
        break;
    case SettingUpDevice:
201
        d->stopRequested = true;
202 203 204 205 206 207
        stopDeviceSetup();
        break;
    case Connecting:
        setFinished();
        break;
    case Deploying:
208
        d->stopRequested = true;
209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228
        stopDeployment();
        break;
    }
}

bool AbstractRemoteLinuxDeployService::isDeploymentPossible(QString *whyNot) const
{
    if (!deviceConfiguration()) {
        if (whyNot)
            *whyNot = tr("No device configuration set.");
        return false;
    }
    return true;
}

QVariantMap AbstractRemoteLinuxDeployService::exportDeployTimes() const
{
    QVariantMap map;
    QVariantList hostList;
    QVariantList fileList;
229
    QVariantList sysrootList;
230 231
    QVariantList remotePathList;
    QVariantList timeList;
232
    typedef QHash<DeployParameters, QDateTime>::ConstIterator DepIt;
233
    for (DepIt it = d->lastDeployed.begin(); it != d->lastDeployed.end(); ++it) {
234 235
        fileList << it.key().file.localFilePath().toString();
        remotePathList << it.key().file.remoteDirectory();
236 237
        hostList << it.key().host;
        sysrootList << it.key().sysroot;
238 239 240
        timeList << it.value();
    }
    map.insert(QLatin1String(LastDeployedHostsKey), hostList);
241
    map.insert(QLatin1String(LastDeployedSysrootsKey), sysrootList);
242 243 244 245 246 247 248 249 250
    map.insert(QLatin1String(LastDeployedFilesKey), fileList);
    map.insert(QLatin1String(LastDeployedRemotePathsKey), remotePathList);
    map.insert(QLatin1String(LastDeployedTimesKey), timeList);
    return map;
}

void AbstractRemoteLinuxDeployService::importDeployTimes(const QVariantMap &map)
{
    const QVariantList &hostList = map.value(QLatin1String(LastDeployedHostsKey)).toList();
251
    const QVariantList &sysrootList = map.value(QLatin1String(LastDeployedSysrootsKey)).toList();
252 253 254 255 256
    const QVariantList &fileList = map.value(QLatin1String(LastDeployedFilesKey)).toList();
    const QVariantList &remotePathList
        = map.value(QLatin1String(LastDeployedRemotePathsKey)).toList();
    const QVariantList &timeList = map.value(QLatin1String(LastDeployedTimesKey)).toList();
    const int elemCount
257 258
        = qMin(qMin(qMin(hostList.size(), fileList.size()),
              qMin(remotePathList.size(), timeList.size())), sysrootList.size());
259
    for (int i = 0; i < elemCount; ++i) {
260
        const DeployableFile df(fileList.at(i).toString(), remotePathList.at(i).toString());
261 262
        d->lastDeployed.insert(DeployParameters(df, hostList.at(i).toString(),
            sysrootList.at(i).toString()), timeList.at(i).toDateTime());
263 264 265 266 267
    }
}

void AbstractRemoteLinuxDeployService::handleDeviceSetupDone(bool success)
{
268
    QTC_ASSERT(d->state == SettingUpDevice, return);
269

270
    if (!success || d->stopRequested) {
271 272 273 274
        setFinished();
        return;
    }

275
    d->state = Connecting;
hjk's avatar
hjk committed
276
    d->connection = QSsh::acquireConnection(deviceConfiguration()->sshParameters());
Montel Laurent's avatar
Montel Laurent committed
277 278
    connect(d->connection, &SshConnection::error,
            this, &AbstractRemoteLinuxDeployService::handleConnectionFailure);
279
    if (d->connection->state() == SshConnection::Connected) {
280 281
        handleConnected();
    } else {
Montel Laurent's avatar
Montel Laurent committed
282 283
        connect(d->connection, &SshConnection::connected,
                this, &AbstractRemoteLinuxDeployService::handleConnected);
284
        emit progressMessage(tr("Connecting to device..."));
285 286
        if (d->connection->state() == SshConnection::Unconnected)
            d->connection->connectToHost();
287 288 289 290 291
    }
}

void AbstractRemoteLinuxDeployService::handleDeploymentDone()
{
292
    QTC_ASSERT(d->state == Deploying, return);
293 294 295 296 297 298

    setFinished();
}

void AbstractRemoteLinuxDeployService::handleConnected()
{
299
    QTC_ASSERT(d->state == Connecting, return);
300

301
    if (d->stopRequested) {
302 303 304 305
        setFinished();
        return;
    }

306
    d->state = Deploying;
307 308 309 310 311
    doDeploy();
}

void AbstractRemoteLinuxDeployService::handleConnectionFailure()
{
312
    switch (d->state) {
313 314
    case Inactive:
    case SettingUpDevice:
315
        qWarning("%s: Unexpected state %d.", Q_FUNC_INFO, d->state);
316 317
        break;
    case Connecting: {
318
        QString errorMsg = tr("Could not connect to host: %1").arg(d->connection->errorString());
319
        errorMsg += QLatin1Char('\n');
320
        if (deviceConfiguration()->machineType() == IDevice::Emulator)
321
            errorMsg += tr("Did the emulator fail to start?");
322
        else
323
            errorMsg += tr("Is the device connected and set up for network access?");
324 325 326 327 328
        emit errorMessage(errorMsg);
        setFinished();
        break;
    }
    case Deploying:
329
        emit errorMessage(tr("Connection error: %1").arg(d->connection->errorString()));
330 331 332 333 334 335
        stopDeployment();
    }
}

void AbstractRemoteLinuxDeployService::setFinished()
{
336 337
    d->state = Inactive;
    if (d->connection) {
338
        disconnect(d->connection, 0, this, 0);
hjk's avatar
hjk committed
339
        QSsh::releaseConnection(d->connection);
340
        d->connection = 0;
341
    }
342
    d->stopRequested = false;
343 344 345 346
    emit finished();
}

} // namespace RemoteLinux