s60deploystep.cpp 17.4 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** 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.
**
**************************************************************************/

Tobias Hunger's avatar
Tobias Hunger committed
30
#include "s60deploystep.h"
31
32

#include "qt4buildconfiguration.h"
33
#include "s60deployconfiguration.h"
34
35
36
37
#include "s60devicerunconfiguration.h"
#include "s60runconfigbluetoothstarter.h"

#include <coreplugin/icore.h>
Tobias Hunger's avatar
Tobias Hunger committed
38
#include <projectexplorer/buildsteplist.h>
39
#include <projectexplorer/target.h>
Tobias Hunger's avatar
Tobias Hunger committed
40
#include <projectexplorer/projectexplorerconstants.h>
41
42
#include <qt4projectmanagerconstants.h>

43
44
45
46
47
48
49
50
51
52
53
#include <symbianutils/launcher.h>
#include <symbianutils/symbiandevicemanager.h>

#include <QtGui/QMessageBox>
#include <QtGui/QMainWindow>

#include <QtCore/QTimer>
#include <QtCore/QDateTime>
#include <QtCore/QDir>
#include <QtCore/QEventLoop>

54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
using namespace ProjectExplorer;
using namespace Qt4ProjectManager::Internal;

namespace {
    const char * const S60_DEPLOY_STEP_ID = "Qt4ProjectManager.S60DeployStep";
}

static inline bool ensureDeleteFile(const QString &fileName, QString *errorMessage)
{
    QFile file(fileName);
    if (file.exists() && !file.remove()) {
        *errorMessage = S60DeployStep::tr("Unable to remove existing file '%1': %2").arg(fileName, file.errorString());
        return false;
    }
    return true;
}

static inline bool renameFile(const QString &sourceName, const QString &targetName,
                              QString *errorMessage)
{
    if (sourceName == targetName)
        return true;
    if (!ensureDeleteFile(targetName, errorMessage))
        return false;
    QFile source(sourceName);
    if (!source.rename(targetName)) {
        *errorMessage = S60DeployStep::tr("Unable to rename file '%1' to '%2': %3")
                        .arg(sourceName, targetName, source.errorString());
        return false;
    }
    return true;
}

// #pragma mark -- S60DeployStep

Tobias Hunger's avatar
Tobias Hunger committed
89
S60DeployStep::S60DeployStep(ProjectExplorer::BuildStepList *bc,
90
                             S60DeployStep *bs):
Pawel Polanski's avatar
Pawel Polanski committed
91
        BuildStep(bc, bs), m_timer(0),
92
93
        m_releaseDeviceAfterLauncherFinish(bs->m_releaseDeviceAfterLauncherFinish),
        m_handleDeviceRemoval(bs->m_handleDeviceRemoval),
Pawel Polanski's avatar
Pawel Polanski committed
94
        m_launcher(0), m_eventLoop(0)
95
{
96
    ctor();
97
98
}

Tobias Hunger's avatar
Tobias Hunger committed
99
S60DeployStep::S60DeployStep(ProjectExplorer::BuildStepList *bc):
100
        BuildStep(bc, QLatin1String(S60_DEPLOY_STEP_ID)), m_timer(0),
Pawel Polanski's avatar
Pawel Polanski committed
101
102
        m_releaseDeviceAfterLauncherFinish(true),
        m_handleDeviceRemoval(true), m_launcher(0), m_eventLoop(0)
103
{
104
105
106
107
108
109
110
    ctor();
}

void S60DeployStep::ctor()
{
    //: Qt4 Deploystep display name
    setDefaultDisplayName(tr("Deploy"));
111
112
113
114
115
116
117
118
119
120
121
122
123
}

S60DeployStep::~S60DeployStep()
{
    delete m_timer;
    delete m_launcher;
    delete m_eventLoop;
}


bool S60DeployStep::init()
{
    Qt4BuildConfiguration *bc = static_cast<Qt4BuildConfiguration *>(buildConfiguration());
124
125
    S60DeployConfiguration* deployConfiguration = static_cast<S60DeployConfiguration *>(bc->target()->activeDeployConfiguration());
    if(!deployConfiguration)
126
        return false;
127
    m_serialPortName = deployConfiguration->serialPortName();
128
    m_serialPortFriendlyName = SymbianUtils::SymbianDeviceManager::instance()->friendlyNameForPort(m_serialPortName);
129
130
131
132
    m_packageFileNamesWithTarget = deployConfiguration->packageFileNamesWithTargetInfo();
    m_signedPackages = deployConfiguration->signedPackages();
    m_installationDrive = deployConfiguration->installationDrive();
    m_silentInstall = deployConfiguration->silentInstall();
133
134

    QString message;
135
136
137
138
139
140
    if (m_launcher) {
        trk::Launcher::releaseToDeviceManager(m_launcher);
        delete m_launcher;
        m_launcher = 0;
    }

141
    m_launcher = trk::Launcher::acquireFromDeviceManager(m_serialPortName, this, &message);
142
143
144
    if (!message.isEmpty() || !m_launcher) {
        if (m_launcher)
            trk::Launcher::releaseToDeviceManager(m_launcher);
145
146
147
        delete m_launcher;
        m_launcher = 0;
        appendMessage(message, true);
148
        return true;
149
    }
150
151
152
153
154
155
156
157
158
159
    // Prompt the user to start up the Blue tooth connection
    const trk::PromptStartCommunicationResult src =
            S60RunConfigBluetoothStarter::startCommunication(m_launcher->trkDevice(),
                                                             0, &message);
    if (src != trk::PromptStartCommunicationConnected) {
        if (!message.isEmpty())
            trk::Launcher::releaseToDeviceManager(m_launcher);
        delete m_launcher;
        m_launcher = 0;
        appendMessage(message, true);
160
        return true;
161
    }
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
    return true;
}

QVariantMap S60DeployStep::toMap() const
{
    return BuildStep::toMap();
}

bool S60DeployStep::fromMap(const QVariantMap &map)
{
    return BuildStep::fromMap(map);
}

void S60DeployStep::appendMessage(const QString &error, bool isError)
{
    emit addOutput(error, isError?ProjectExplorer::BuildStep::ErrorMessageOutput:
                                  ProjectExplorer::BuildStep::MessageOutput);
}

bool S60DeployStep::processPackageName(QString &errorMessage)
{
dt's avatar
dt committed
183
184
    for (int i = 0; i < m_signedPackages.count(); ++i) {
        QFileInfo packageInfo(m_signedPackages.at(i));
185
        // support for 4.6.1 and pre, where make sis creates 'targetname_armX_udeb.sis' instead of 'targetname.sis'
dt's avatar
dt committed
186
        QFileInfo packageWithTargetInfo(m_packageFileNamesWithTarget.at(i));
187
188
189
190
191
192
        // does the 4.6.1 version exist?
        if (packageWithTargetInfo.exists() && packageWithTargetInfo.isFile()) {
            // is the 4.6.1 version newer? (to guard against behavior change Qt Creator 1.3 --> 2.0)
            if (!packageInfo.exists() || packageInfo.lastModified() < packageWithTargetInfo.lastModified()) { //TODO change the QtCore
                // the 'targetname_armX_udeb.sis' crap exists and is new, rename it
                appendMessage(tr("Renaming new package '%1' to '%2'")
dt's avatar
dt committed
193
194
195
                              .arg(QDir::toNativeSeparators(m_packageFileNamesWithTarget.at(i)),
                                   QDir::toNativeSeparators(m_signedPackages.at(i))), false);
                return renameFile(m_packageFileNamesWithTarget.at(i), m_signedPackages.at(i), &errorMessage);
196
197
198
            } else {
                // the 'targetname_armX_udeb.sis' crap exists but is old, remove it
                appendMessage(tr("Removing old package '%1'")
dt's avatar
dt committed
199
                              .arg(QDir::toNativeSeparators(m_packageFileNamesWithTarget.at(i))),
200
                              false);
dt's avatar
dt committed
201
                ensureDeleteFile(m_packageFileNamesWithTarget.at(i), &errorMessage);
202
203
            }
        }
dt's avatar
dt committed
204
205
206
207
        if (!packageInfo.exists() || !packageInfo.isFile()) {
            errorMessage = tr("'%1': Package file not found").arg(m_signedPackages.at(i));
            return false;
        }
208
209
210
211
212
213
214
    }
    return true;
}

void S60DeployStep::start()
{
    if (m_serialPortName.isEmpty()) {
215
        appendMessage(tr("No device is connected. Please connect a device and try again."), true);
216
217
218
219
220
221
222
223
224
225
        emit finished();
        return;
    }

    QString errorMessage;

    // make sure we have the right name of the sis package
    if (processPackageName(errorMessage)) {
        startDeployment();
    } else {
dt's avatar
dt committed
226
        errorMessage = tr("Failed to find package %1").arg(errorMessage);
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
        appendMessage(errorMessage, true);
        stop();
        emit finished();
    }
}

void S60DeployStep::stop()
{
    if (m_launcher)
        m_launcher->terminate();
    emit finished();
}

void S60DeployStep::setupConnections()
{
    connect(SymbianUtils::SymbianDeviceManager::instance(), SIGNAL(deviceRemoved(SymbianUtils::SymbianDevice)),
            this, SLOT(deviceRemoved(SymbianUtils::SymbianDevice)));
    connect(m_launcher, SIGNAL(finished()), this, SLOT(launcherFinished()));
245
246

    connect(m_launcher, SIGNAL(canNotConnect(QString)), this, SLOT(connectFailed(QString)));
247
    connect(m_launcher, SIGNAL(copyingStarted(QString)), this, SLOT(printCopyingNotice(QString)));
248
249
250
    connect(m_launcher, SIGNAL(canNotCreateFile(QString,QString)), this, SLOT(createFileFailed(QString,QString)));
    connect(m_launcher, SIGNAL(canNotWriteFile(QString,QString)), this, SLOT(writeFileFailed(QString,QString)));
    connect(m_launcher, SIGNAL(canNotCloseFile(QString,QString)), this, SLOT(closeFileFailed(QString,QString)));
251
    connect(m_launcher, SIGNAL(installingStarted(QString)), this, SLOT(printInstallingNotice(QString)));
252
    connect(m_launcher, SIGNAL(canNotInstall(QString,QString)), this, SLOT(installFailed(QString,QString)));
253
254
255
256
257
258
259
260
    connect(m_launcher, SIGNAL(installingFinished()), this, SLOT(printInstallingFinished()));
    connect(m_launcher, SIGNAL(stateChanged(int)), this, SLOT(slotLauncherStateChanged(int)));
}

void S60DeployStep::startDeployment()
{
    Q_ASSERT(m_launcher);

261
    setupConnections();
262

dt's avatar
dt committed
263
264
    QStringList copyDst;
    foreach (const QString &signedPackage, m_signedPackages)
265
        copyDst << QString::fromLatin1("%1:\\Data\\%2").arg(m_installationDrive).arg(QFileInfo(signedPackage).fileName());
266

dt's avatar
dt committed
267
268
    m_launcher->setCopyFileNames(m_signedPackages, copyDst);
    m_launcher->setInstallFileNames(copyDst);
269
    m_launcher->setInstallationDrive(m_installationDrive);
270
    m_launcher->setInstallationMode(m_silentInstall?trk::Launcher::InstallationModeSilentAndUser:
271
                                                    trk::Launcher::InstallationModeUser);
272
273
    m_launcher->addStartupActions(trk::Launcher::ActionCopyInstall);

dt's avatar
dt committed
274
275
    // TODO readd information about packages? msgListFile(m_signedPackage)
    appendMessage(tr("Deploying application to '%2'...").arg(m_serialPortFriendlyName), false);
276

277
278
279
280
281
    QString errorMessage;
    if (!m_launcher->startServer(&errorMessage)) {
        errorMessage = tr("Could not connect to phone on port '%1': %2\n"
                          "Check if the phone is connected and App TRK is running.").arg(m_serialPortName, errorMessage);
        appendMessage(errorMessage, true);
282
283
284
285
286
287
288
289
        stop();
        emit finished();
    }
}

void S60DeployStep::run(QFutureInterface<bool> &fi)
{
    m_futureInterface = &fi;
290
    m_deployResult = true;
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
    connect(this, SIGNAL(finished()),
            this, SLOT(launcherFinished()));
    connect(this, SIGNAL(finishNow()),
            this, SLOT(launcherFinished()), Qt::DirectConnection);

    start();
    m_timer = new QTimer();
    connect(m_timer, SIGNAL(timeout()), this, SLOT(checkForCancel()), Qt::DirectConnection);
    m_timer->start(500);
    m_eventLoop = new QEventLoop();
    m_eventLoop->exec();
    m_timer->stop();
    delete m_timer;
    m_timer = 0;

    delete m_eventLoop;
    m_eventLoop = 0;
    fi.reportResult(m_deployResult);
    m_futureInterface = 0;
}

void S60DeployStep::setReleaseDeviceAfterLauncherFinish(bool v)
{
    m_releaseDeviceAfterLauncherFinish = v;
}

void S60DeployStep::slotLauncherStateChanged(int s)
{
    if (s == trk::Launcher::WaitingForTrk) {
320
        QMessageBox *mb = S60DeviceRunControl::createTrkWaitingMessageBox(m_launcher->trkServerName(),
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
                                                                              Core::ICore::instance()->mainWindow());
        connect(m_launcher, SIGNAL(stateChanged(int)), mb, SLOT(close()));
        connect(mb, SIGNAL(finished(int)), this, SLOT(slotWaitingForTrkClosed()));
        mb->open();
    }
}

void S60DeployStep::slotWaitingForTrkClosed()
{
    if (m_launcher && m_launcher->state() == trk::Launcher::WaitingForTrk) {
        stop();
        appendMessage(tr("Canceled."), true);
        emit finished();
    }
}

337
void S60DeployStep::createFileFailed(const QString &filename, const QString &errorMessage)
338
339
{
    appendMessage(tr("Could not create file %1 on device: %2").arg(filename, errorMessage), true);
340
    m_deployResult = false;
341
342
}

343
void S60DeployStep::writeFileFailed(const QString &filename, const QString &errorMessage)
344
345
{
    appendMessage(tr("Could not write to file %1 on device: %2").arg(filename, errorMessage), true);
346
    m_deployResult = false;
347
348
}

349
void S60DeployStep::closeFileFailed(const QString &filename, const QString &errorMessage)
350
351
352
{
    const QString msg = tr("Could not close file %1 on device: %2. It will be closed when App TRK is closed.");
    appendMessage( msg.arg(filename, errorMessage), true);
353
    m_deployResult = false;
354
355
}

356
void S60DeployStep::connectFailed(const QString &errorMessage)
357
358
{
    appendMessage(tr("Could not connect to App TRK on device: %1. Restarting App TRK might help.").arg(errorMessage), true);
359
    m_deployResult = false;
360
361
}

362
void S60DeployStep::printCopyingNotice(const QString &fileName)
363
{
364
    appendMessage(tr("Copying \"%1\"...").arg(fileName), false);
365
366
}

367
void S60DeployStep::printInstallingNotice(const QString &packageName)
368
{
369
    appendMessage(tr("Installing package \"%1\" on drive %2:...").arg(packageName).arg(m_installationDrive), false);
370
371
372
373
374
375
376
}

void S60DeployStep::printInstallingFinished()
{
    appendMessage(tr("Installation has finished"), false);
}

377
void S60DeployStep::installFailed(const QString &filename, const QString &errorMessage)
378
379
{
    appendMessage(tr("Could not install from package %1 on device: %2").arg(filename, errorMessage), true);
380
    m_deployResult = false;
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
}

void S60DeployStep::checkForCancel()
{
    if (m_futureInterface->isCanceled() && m_timer->isActive()) {
        m_timer->stop();
        stop();
        appendMessage(tr("Canceled."), true);
        emit finishNow();
    }
}

void S60DeployStep::launcherFinished()
{
    if (m_releaseDeviceAfterLauncherFinish && m_launcher) {
        m_handleDeviceRemoval = false;
        trk::Launcher::releaseToDeviceManager(m_launcher);
    }
    if(m_launcher)
        m_launcher->deleteLater();
    m_launcher = 0;
    if(m_eventLoop)
403
        m_eventLoop->exit();
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
}

void S60DeployStep::deviceRemoved(const SymbianUtils::SymbianDevice &d)
{
    if (m_handleDeviceRemoval && d.portName() == m_serialPortName) {
        appendMessage(tr("The device '%1' has been disconnected").arg(d.friendlyName()), true);
        emit finished();
    }
}

// #pragma mark -- S60DeployStepWidget

BuildStepConfigWidget *S60DeployStep::createConfigWidget()
{
    return new S60DeployStepWidget();
}

421
422
423
424
S60DeployStepWidget::S60DeployStepWidget() : ProjectExplorer::BuildStepConfigWidget()
{
}

425
426
427
428
429
430
void S60DeployStepWidget::init()
{
}

QString S60DeployStepWidget::summaryText() const
{
Tobias Hunger's avatar
Tobias Hunger committed
431
    return QString("<b>%1</b>").arg(displayName());
432
433
434
435
}

QString S60DeployStepWidget::displayName() const
{
Tobias Hunger's avatar
Tobias Hunger committed
436
    return tr("Deploy SIS Package");
437
438
439
440
441
442
443
444
445
446
447
448
449
}

// #pragma mark -- S60DeployStepFactory

S60DeployStepFactory::S60DeployStepFactory(QObject *parent) :
        ProjectExplorer::IBuildStepFactory(parent)
{
}

S60DeployStepFactory::~S60DeployStepFactory()
{
}

Tobias Hunger's avatar
Tobias Hunger committed
450
bool S60DeployStepFactory::canCreate(ProjectExplorer::BuildStepList *parent, const QString &id) const
451
{
Tobias Hunger's avatar
Tobias Hunger committed
452
    if (parent->id() != QLatin1String(ProjectExplorer::Constants::BUILDSTEPS_DEPLOY))
453
454
455
456
457
458
        return false;
    if (parent->target()->id() != QLatin1String(Constants::S60_DEVICE_TARGET_ID))
        return false;
    return (id == QLatin1String(S60_DEPLOY_STEP_ID));
}

Tobias Hunger's avatar
Tobias Hunger committed
459
ProjectExplorer::BuildStep *S60DeployStepFactory::create(ProjectExplorer::BuildStepList *parent, const QString &id)
460
{
Tobias Hunger's avatar
Tobias Hunger committed
461
    if (!canCreate(parent, id))
462
        return 0;
Tobias Hunger's avatar
Tobias Hunger committed
463
    return new S60DeployStep(parent);
464
465
}

Tobias Hunger's avatar
Tobias Hunger committed
466
bool S60DeployStepFactory::canClone(ProjectExplorer::BuildStepList *parent, ProjectExplorer::BuildStep *source) const
467
{
Tobias Hunger's avatar
Tobias Hunger committed
468
469
470
471
472
    if (!canCreate(parent, source->id()))
        return false;
    if (!qobject_cast<S60DeployStep *>(source))
        return false;
    return true;
473
474
}

Tobias Hunger's avatar
Tobias Hunger committed
475
ProjectExplorer::BuildStep *S60DeployStepFactory::clone(ProjectExplorer::BuildStepList *parent, ProjectExplorer::BuildStep *source)
476
{
Tobias Hunger's avatar
Tobias Hunger committed
477
    if (!canClone(parent, source))
478
479
480
481
        return 0;
    return new S60DeployStep(parent, static_cast<S60DeployStep *>(source));
}

Tobias Hunger's avatar
Tobias Hunger committed
482
bool S60DeployStepFactory::canRestore(ProjectExplorer::BuildStepList *parent, const QVariantMap &map) const
483
484
{
    QString id(ProjectExplorer::idFromMap(map));
Tobias Hunger's avatar
Tobias Hunger committed
485
    return canCreate(parent, id);
486
487
}

Tobias Hunger's avatar
Tobias Hunger committed
488
ProjectExplorer::BuildStep *S60DeployStepFactory::restore(ProjectExplorer::BuildStepList *parent, const QVariantMap &map)
489
{
Tobias Hunger's avatar
Tobias Hunger committed
490
    if (!canRestore(parent, map))
491
492
493
494
495
496
497
498
        return 0;
    S60DeployStep *bs = new S60DeployStep(parent);
    if (bs->fromMap(map))
        return bs;
    delete bs;
    return 0;
}

Tobias Hunger's avatar
Tobias Hunger committed
499
QStringList S60DeployStepFactory::availableCreationIds(ProjectExplorer::BuildStepList *parent) const
500
{
Tobias Hunger's avatar
Tobias Hunger committed
501
502
    if (parent->id() == QLatin1String(ProjectExplorer::Constants::BUILDSTEPS_DEPLOY)
        && parent->target()->id() == QLatin1String(Constants::S60_DEVICE_TARGET_ID))
503
504
505
506
507
508
509
510
511
512
        return QStringList() << QLatin1String(S60_DEPLOY_STEP_ID);
    return QStringList();
}

QString S60DeployStepFactory::displayNameForId(const QString &id) const
{
    if (id == QLatin1String(S60_DEPLOY_STEP_ID))
        return tr("Deploy SIS Package");
    return QString();
}