s60deploystep.cpp 18 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
    }
    return true;
}

void S60DeployStep::start()
{
214
215
    QString errorMessage;

216
    if (m_serialPortName.isEmpty()) {
217
218
219
220
221
222
        errorMessage = tr("No device is connected. Please connect a device and try again.");
        appendMessage(errorMessage, true);
        emit addTask(ProjectExplorer::Task(ProjectExplorer::Task::Error,
                                           errorMessage,
                                           QString(), -1,
                                           ProjectExplorer::Constants::TASK_CATEGORY_BUILDSYSTEM));
223
224
225
226
227
228
229
230
        emit finished();
        return;
    }

    // make sure we have the right name of the sis package
    if (processPackageName(errorMessage)) {
        startDeployment();
    } else {
dt's avatar
dt committed
231
        errorMessage = tr("Failed to find package %1").arg(errorMessage);
232
        appendMessage(errorMessage, true);
233
234
235
236
        emit addTask(ProjectExplorer::Task(ProjectExplorer::Task::Error,
                                           errorMessage,
                                           QString(), -1,
                                           ProjectExplorer::Constants::TASK_CATEGORY_BUILDSYSTEM));
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
        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()));
254
255

    connect(m_launcher, SIGNAL(canNotConnect(QString)), this, SLOT(connectFailed(QString)));
256
    connect(m_launcher, SIGNAL(copyingStarted(QString)), this, SLOT(printCopyingNotice(QString)));
257
258
259
    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)));
260
    connect(m_launcher, SIGNAL(installingStarted(QString)), this, SLOT(printInstallingNotice(QString)));
261
    connect(m_launcher, SIGNAL(canNotInstall(QString,QString)), this, SLOT(installFailed(QString,QString)));
262
263
264
265
266
267
268
269
    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);

270
    setupConnections();
271

dt's avatar
dt committed
272
273
    QStringList copyDst;
    foreach (const QString &signedPackage, m_signedPackages)
274
        copyDst << QString::fromLatin1("%1:\\Data\\%2").arg(m_installationDrive).arg(QFileInfo(signedPackage).fileName());
275

dt's avatar
dt committed
276
277
    m_launcher->setCopyFileNames(m_signedPackages, copyDst);
    m_launcher->setInstallFileNames(copyDst);
278
    m_launcher->setInstallationDrive(m_installationDrive);
279
    m_launcher->setInstallationMode(m_silentInstall?trk::Launcher::InstallationModeSilentAndUser:
280
                                                    trk::Launcher::InstallationModeUser);
281
282
    m_launcher->addStartupActions(trk::Launcher::ActionCopyInstall);

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

286
287
288
289
290
    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);
291
292
293
294
295
296
297
298
        stop();
        emit finished();
    }
}

void S60DeployStep::run(QFutureInterface<bool> &fi)
{
    m_futureInterface = &fi;
299
    m_deployResult = true;
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
    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) {
329
        QMessageBox *mb = S60DeviceRunControl::createTrkWaitingMessageBox(m_launcher->trkServerName(),
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
                                                                              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();
    }
}

346
void S60DeployStep::createFileFailed(const QString &filename, const QString &errorMessage)
347
348
{
    appendMessage(tr("Could not create file %1 on device: %2").arg(filename, errorMessage), true);
349
    m_deployResult = false;
350
351
}

352
void S60DeployStep::writeFileFailed(const QString &filename, const QString &errorMessage)
353
354
{
    appendMessage(tr("Could not write to file %1 on device: %2").arg(filename, errorMessage), true);
355
    m_deployResult = false;
356
357
}

358
void S60DeployStep::closeFileFailed(const QString &filename, const QString &errorMessage)
359
360
361
{
    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);
362
    m_deployResult = false;
363
364
}

365
void S60DeployStep::connectFailed(const QString &errorMessage)
366
367
{
    appendMessage(tr("Could not connect to App TRK on device: %1. Restarting App TRK might help.").arg(errorMessage), true);
368
    m_deployResult = false;
369
370
}

371
void S60DeployStep::printCopyingNotice(const QString &fileName)
372
{
373
    appendMessage(tr("Copying \"%1\"...").arg(fileName), false);
374
375
}

376
void S60DeployStep::printInstallingNotice(const QString &packageName)
377
{
378
    appendMessage(tr("Installing package \"%1\" on drive %2:...").arg(packageName).arg(m_installationDrive), false);
379
380
381
382
383
384
385
}

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

386
void S60DeployStep::installFailed(const QString &filename, const QString &errorMessage)
387
388
{
    appendMessage(tr("Could not install from package %1 on device: %2").arg(filename, errorMessage), true);
389
    m_deployResult = false;
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
}

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)
412
        m_eventLoop->exit();
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
}

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();
}

430
431
432
433
S60DeployStepWidget::S60DeployStepWidget() : ProjectExplorer::BuildStepConfigWidget()
{
}

434
435
436
437
438
439
void S60DeployStepWidget::init()
{
}

QString S60DeployStepWidget::summaryText() const
{
Tobias Hunger's avatar
Tobias Hunger committed
440
    return QString("<b>%1</b>").arg(displayName());
441
442
443
444
}

QString S60DeployStepWidget::displayName() const
{
Tobias Hunger's avatar
Tobias Hunger committed
445
    return tr("Deploy SIS Package");
446
447
448
449
450
451
452
453
454
455
456
457
458
}

// #pragma mark -- S60DeployStepFactory

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

S60DeployStepFactory::~S60DeployStepFactory()
{
}

Tobias Hunger's avatar
Tobias Hunger committed
459
bool S60DeployStepFactory::canCreate(ProjectExplorer::BuildStepList *parent, const QString &id) const
460
{
Tobias Hunger's avatar
Tobias Hunger committed
461
    if (parent->id() != QLatin1String(ProjectExplorer::Constants::BUILDSTEPS_DEPLOY))
462
463
464
465
466
467
        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
468
ProjectExplorer::BuildStep *S60DeployStepFactory::create(ProjectExplorer::BuildStepList *parent, const QString &id)
469
{
Tobias Hunger's avatar
Tobias Hunger committed
470
    if (!canCreate(parent, id))
471
        return 0;
Tobias Hunger's avatar
Tobias Hunger committed
472
    return new S60DeployStep(parent);
473
474
}

Tobias Hunger's avatar
Tobias Hunger committed
475
bool S60DeployStepFactory::canClone(ProjectExplorer::BuildStepList *parent, ProjectExplorer::BuildStep *source) const
476
{
Tobias Hunger's avatar
Tobias Hunger committed
477
478
479
480
481
    if (!canCreate(parent, source->id()))
        return false;
    if (!qobject_cast<S60DeployStep *>(source))
        return false;
    return true;
482
483
}

Tobias Hunger's avatar
Tobias Hunger committed
484
ProjectExplorer::BuildStep *S60DeployStepFactory::clone(ProjectExplorer::BuildStepList *parent, ProjectExplorer::BuildStep *source)
485
{
Tobias Hunger's avatar
Tobias Hunger committed
486
    if (!canClone(parent, source))
487
488
489
490
        return 0;
    return new S60DeployStep(parent, static_cast<S60DeployStep *>(source));
}

Tobias Hunger's avatar
Tobias Hunger committed
491
bool S60DeployStepFactory::canRestore(ProjectExplorer::BuildStepList *parent, const QVariantMap &map) const
492
493
{
    QString id(ProjectExplorer::idFromMap(map));
Tobias Hunger's avatar
Tobias Hunger committed
494
    return canCreate(parent, id);
495
496
}

Tobias Hunger's avatar
Tobias Hunger committed
497
ProjectExplorer::BuildStep *S60DeployStepFactory::restore(ProjectExplorer::BuildStepList *parent, const QVariantMap &map)
498
{
Tobias Hunger's avatar
Tobias Hunger committed
499
    if (!canRestore(parent, map))
500
501
502
503
504
505
506
507
        return 0;
    S60DeployStep *bs = new S60DeployStep(parent);
    if (bs->fromMap(map))
        return bs;
    delete bs;
    return 0;
}

Tobias Hunger's avatar
Tobias Hunger committed
508
QStringList S60DeployStepFactory::availableCreationIds(ProjectExplorer::BuildStepList *parent) const
509
{
Tobias Hunger's avatar
Tobias Hunger committed
510
511
    if (parent->id() == QLatin1String(ProjectExplorer::Constants::BUILDSTEPS_DEPLOY)
        && parent->target()->id() == QLatin1String(Constants::S60_DEVICE_TARGET_ID))
512
513
514
515
516
517
518
519
520
521
        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();
}