s60devicerunconfiguration.cpp 29.9 KB
Newer Older
con's avatar
con committed
1
2
3
4
/**************************************************************************
**
** This file is part of Qt Creator
**
hjk's avatar
hjk committed
5
** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
con's avatar
con committed
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
**
** 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
hjk's avatar
hjk committed
26
** contact the sales department at http://qt.nokia.com/contact.
con's avatar
con committed
27
28
29
**
**************************************************************************/

30
#include "s60devicerunconfiguration.h"
31
#include "s60devicerunconfigurationwidget.h"
32
#include "s60deployconfiguration.h"
33
#include "qt4project.h"
Tobias Hunger's avatar
Tobias Hunger committed
34
#include "qt4target.h"
35
36
#include "s60manager.h"
#include "s60devices.h"
37
#include "s60runconfigbluetoothstarter.h"
38
#include "qt4projectmanagerconstants.h"
dt's avatar
dt committed
39
#include "qtoutputformatter.h"
40

41
42
43
44
45
46
#include <symbianutils/bluetoothlistener_gui.h>
#include <symbianutils/launcher.h>
#include <symbianutils/symbiandevicemanager.h>

#include <utils/qtcassert.h>

47
#include <coreplugin/icore.h>
48
#include <coreplugin/progressmanager/progressmanager.h>
49

50
#include <debugger/debuggerengine.h>
51

52
53
#include <QtGui/QMessageBox>
#include <QtGui/QMainWindow>
54
55
56
#include <QtCore/QFileInfo>
#include <QtCore/QDateTime>
#include <QtCore/QDir>
57

58
using namespace ProjectExplorer;
dt's avatar
dt committed
59
using namespace Qt4ProjectManager;
60
61
using namespace Qt4ProjectManager::Internal;

62
63
64
65
66
67
68
69
namespace {
const char * const S60_DEVICE_RC_ID("Qt4ProjectManager.S60DeviceRunConfiguration");
const char * const S60_DEVICE_RC_PREFIX("Qt4ProjectManager.S60DeviceRunConfiguration.");

const char * const PRO_FILE_KEY("Qt4ProjectManager.S60DeviceRunConfiguration.ProFile");
const char * const COMMUNICATION_TYPE_KEY("Qt4ProjectManager.S60DeviceRunConfiguration.CommunicationType");
const char * const COMMAND_LINE_ARGUMENTS_KEY("Qt4ProjectManager.S60DeviceRunConfiguration.CommandLineArguments");

70
const int    PROGRESS_MAX = 200;
71

72
enum { debug = 0 };
73

74
// Format information about a file
75
static inline QString msgListFile(const QString &f)
76
77
78
79
{
    QString rc;
    const QFileInfo fi(f);
    QTextStream str(&rc);
80
81
82
83
84
    if (fi.exists()) {
        str << fi.size() << ' ' << fi.lastModified().toString(Qt::ISODate) << ' ' << QDir::toNativeSeparators(fi.absoluteFilePath());
    } else {
        str << "<non-existent> " << QDir::toNativeSeparators(fi.absoluteFilePath());
    }
85
86
    return rc;
}
87

88
89
90
91
92
93
94
95
96
97
98
99
100
101
QString pathFromId(const QString &id)
{
    if (!id.startsWith(QLatin1String(S60_DEVICE_RC_PREFIX)))
        return QString();
    return id.mid(QString::fromLatin1(S60_DEVICE_RC_PREFIX).size());
}

QString pathToId(const QString &path)
{
    return QString::fromLatin1(S60_DEVICE_RC_PREFIX) + path;
}

}

102
// ======== S60DeviceRunConfiguration
103

104
S60DeviceRunConfiguration::S60DeviceRunConfiguration(Qt4Target *parent, const QString &proFilePath) :
Tobias Hunger's avatar
Tobias Hunger committed
105
    RunConfiguration(parent,  QLatin1String(S60_DEVICE_RC_ID)),
106
107
    m_proFilePath(proFilePath),
    m_validParse(parent->qt4Project()->validParse(proFilePath))
108
109
110
111
{
    ctor();
}

112
S60DeviceRunConfiguration::S60DeviceRunConfiguration(Qt4Target *target, S60DeviceRunConfiguration *source) :
Tobias Hunger's avatar
Tobias Hunger committed
113
    RunConfiguration(target, source),
114
    m_proFilePath(source->m_proFilePath),
115
116
    m_commandLineArguments(source->m_commandLineArguments),
    m_validParse(source->m_validParse)
117
118
119
120
121
{
    ctor();
}

void S60DeviceRunConfiguration::ctor()
122
123
{
    if (!m_proFilePath.isEmpty())
124
125
        //: S60 device runconfiguration default display name, %1 is base pro-File name
        setDefaultDisplayName(tr("%1 on Symbian Device").arg(QFileInfo(m_proFilePath).completeBaseName()));
126
    else
127
128
        //: S60 device runconfiguration default display name (no profile set)
        setDefaultDisplayName(tr("Run on Symbian device"));
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149

    Qt4Project *pro = qt4Target()->qt4Project();
    connect(pro, SIGNAL(proFileUpdated(Qt4ProjectManager::Internal::Qt4ProFileNode*,bool)),
            this, SLOT(proFileUpdate(Qt4ProjectManager::Internal::Qt4ProFileNode*,bool)));
    connect(pro, SIGNAL(proFileInvalidated(Qt4ProjectManager::Internal::Qt4ProFileNode *)),
            this, SLOT(proFileInvalidated(Qt4ProjectManager::Internal::Qt4ProFileNode *)));
}

void S60DeviceRunConfiguration::handleParserState(bool success)
{
    bool enabled = isEnabled();
    m_validParse = success;
    if (enabled != isEnabled())
        emit isEnabledChanged(!enabled);
}

void S60DeviceRunConfiguration::proFileInvalidated(Qt4ProjectManager::Internal::Qt4ProFileNode *pro)
{
    if (m_proFilePath != pro->path())
        return;
    handleParserState(false);
dt's avatar
dt committed
150
151
}

152
void S60DeviceRunConfiguration::proFileUpdate(Qt4ProjectManager::Internal::Qt4ProFileNode *pro, bool success)
dt's avatar
dt committed
153
{
154
155
156
157
    if (m_proFilePath != pro->path())
        return;
    handleParserState(success);
    emit targetInformationChanged();
158
159
160
161
162
163
}

S60DeviceRunConfiguration::~S60DeviceRunConfiguration()
{
}

Tobias Hunger's avatar
Tobias Hunger committed
164
Qt4Target *S60DeviceRunConfiguration::qt4Target() const
dt's avatar
dt committed
165
{
Tobias Hunger's avatar
Tobias Hunger committed
166
    return static_cast<Qt4Target *>(target());
dt's avatar
dt committed
167
168
}

169
ProjectExplorer::ToolChainType S60DeviceRunConfiguration::toolChainType(
170
171
        ProjectExplorer::BuildConfiguration *configuration) const
{
172
173
    if (Qt4BuildConfiguration *bc = qobject_cast<Qt4BuildConfiguration *>(configuration))
        return bc->toolChainType();
174
    return ProjectExplorer::ToolChain_INVALID;
175
176
}

177
ProjectExplorer::ToolChainType S60DeviceRunConfiguration::toolChainType() const
178
{
Tobias Hunger's avatar
Tobias Hunger committed
179
    if (Qt4BuildConfiguration *bc = qobject_cast<Qt4BuildConfiguration *>(target()->activeBuildConfiguration()))
180
        return bc->toolChainType();
181
    return ProjectExplorer::ToolChain_INVALID;
182
183
}

184
bool S60DeviceRunConfiguration::isEnabled(ProjectExplorer::BuildConfiguration *configuration) const
185
{
186
187
    if (!m_validParse)
        return false;
188
189
    const Qt4BuildConfiguration *qt4bc = static_cast<const Qt4BuildConfiguration *>(configuration);
    switch (qt4bc->toolChainType()) {
190
    case ProjectExplorer::ToolChain_GCCE:
191
192
    case ProjectExplorer::ToolChain_RVCT2_ARMV5:
    case ProjectExplorer::ToolChain_RVCT2_ARMV6:
193
194
    case ProjectExplorer::ToolChain_RVCT4_ARMV5:
    case ProjectExplorer::ToolChain_RVCT4_ARMV6:
195
196
    case ProjectExplorer::ToolChain_GCCE_GNUPOC:
    case ProjectExplorer::ToolChain_RVCT_ARMV5_GNUPOC:
197
198
199
200
201
        return true;
    default:
        break;
    }
    return false;
202
203
}

204
QWidget *S60DeviceRunConfiguration::createConfigurationWidget()
205
206
207
208
{
    return new S60DeviceRunConfigurationWidget(this);
}

dt's avatar
dt committed
209
210
211
212
213
ProjectExplorer::OutputFormatter *S60DeviceRunConfiguration::createOutputFormatter() const
{
    return new QtOutputFormatter(qt4Target()->qt4Project());
}

214
QVariantMap S60DeviceRunConfiguration::toMap() const
215
{
216
    QVariantMap map(ProjectExplorer::RunConfiguration::toMap());
217
    const QDir projectDir = QDir(target()->project()->projectDirectory());
218
219
220
221
222

    map.insert(QLatin1String(PRO_FILE_KEY), projectDir.relativeFilePath(m_proFilePath));
    map.insert(QLatin1String(COMMAND_LINE_ARGUMENTS_KEY), m_commandLineArguments);

    return map;
223
224
}

225
bool S60DeviceRunConfiguration::fromMap(const QVariantMap &map)
226
{
227
    const QDir projectDir = QDir(target()->project()->projectDirectory());
228
229

    m_proFilePath = projectDir.filePath(map.value(QLatin1String(PRO_FILE_KEY)).toString());
230
    m_commandLineArguments = map.value(QLatin1String(COMMAND_LINE_ARGUMENTS_KEY)).toString();
231

232
233
234
235
    if (m_proFilePath.isEmpty())
        return false;
    if (!QFileInfo(m_proFilePath).exists())
        return false;
236
237
238

    m_validParse = qt4Target()->qt4Project()->validParse(m_proFilePath);

239
240
    setDefaultDisplayName(tr("%1 on Symbian Device").arg(QFileInfo(m_proFilePath).completeBaseName()));

241
    return RunConfiguration::fromMap(map);
con's avatar
con committed
242
243
}

dt's avatar
dt committed
244
245
246
247
248
249
250
static inline QString fixBaseNameTarget(const QString &in)
{
    if (in == QLatin1String("udeb"))
        return QLatin1String("debug");
    if (in == QLatin1String("urel"))
        return QLatin1String("release");
    return in;
251
252
}

253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
QString S60DeviceRunConfiguration::targetName() const
{
    TargetInformation ti = qt4Target()->qt4Project()->rootProjectNode()->targetInformation(projectFilePath());
    if (!ti.valid)
        return QString();
    return ti.target;
}

const QtVersion *S60DeviceRunConfiguration::qtVersion() const
{
    if (const BuildConfiguration *bc = target()->activeBuildConfiguration())
        if (const Qt4BuildConfiguration *qt4bc = qobject_cast<const Qt4BuildConfiguration *>(bc))
            return qt4bc->qtVersion();
    return 0;
}

bool S60DeviceRunConfiguration::isDebug() const
{
    const Qt4BuildConfiguration *qt4bc = qt4Target()->activeBuildConfiguration();
    return (qt4bc->qmakeBuildConfiguration() & QtVersion::DebugBuild);
}

QString S60DeviceRunConfiguration::symbianTarget() const
{
    return isDebug() ? QLatin1String("udeb") : QLatin1String("urel");
}

280
static inline QString symbianPlatformForToolChain(ProjectExplorer::ToolChainType t)
281
{
282
    switch (t) {
283
284
    case ProjectExplorer::ToolChain_GCCE:
    case ProjectExplorer::ToolChain_GCCE_GNUPOC:
285
        return QLatin1String("gcce");
286
    case ProjectExplorer::ToolChain_RVCT2_ARMV5:
287
    case ProjectExplorer::ToolChain_RVCT4_ARMV5:
288
        return QLatin1String("armv5");
289
    default: // including ProjectExplorer::RVCT_ARMV6_GNUPOC:
290
        break;
291
    }
292
    return QLatin1String("armv6");
293
294
}

295
/* Grep a package file for the '.exe' file. Currently for use on Linux only
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
 * as the '.pkg'-files on Windows do not contain drive letters, which is not
 * handled here. \code
; Executable and default resource files
"./foo.exe"    - "!:\sys\bin\foo.exe"
\endcode  */

static inline QString executableFromPackageUnix(const QString &packageFileName)
{
    QFile packageFile(packageFileName);
    if (!packageFile.open(QIODevice::ReadOnly|QIODevice::Text))
        return QString();
    QRegExp pattern(QLatin1String("^\"(.*.exe)\" *- \"!:.*.exe\"$"));
    QTC_ASSERT(pattern.isValid(), return QString());
    foreach(const QString &line, QString::fromLocal8Bit(packageFile.readAll()).split(QLatin1Char('\n')))
        if (pattern.exactMatch(line)) {
            // Expand relative paths by package file paths
            QString rc = pattern.cap(1);
            if (rc.startsWith(QLatin1String("./")))
                rc.remove(0, 2);
            const QFileInfo fi(rc);
            if (fi.isAbsolute())
                return rc;
            return QFileInfo(packageFileName).absolutePath() + QLatin1Char('/') + rc;
        }
    return QString();
}

323
324
325
326
327
// ABLD/Raptor: Return executable from device/EPOC
static inline QString localExecutableFromDevice(const QtVersion *qtv,
                                                const QString &symbianTarget, /* udeb/urel */
                                                const QString &targetName,
                                                ProjectExplorer::ToolChainType t)
328
{
329
330
331
    QTC_ASSERT(qtv, return QString(); )

    const S60Devices::Device device = S60Manager::instance()->deviceForQtVersion(qtv);
332
    QString localExecutable;
333
334
335
336
337
338
339
340
341
342
343
    QTextStream(&localExecutable) << device.epocRoot << "/epoc32/release/"
            << symbianPlatformForToolChain(t)
            << '/' << symbianTarget << '/' << targetName
            << ".exe";
    return localExecutable;
}

QString S60DeviceRunConfiguration::localExecutableFileName() const
{
    const ProjectExplorer::ToolChainType toolChain = toolChainType();
    switch (toolChain) {
344
345
    case ProjectExplorer::ToolChain_GCCE_GNUPOC:
    case ProjectExplorer::ToolChain_RVCT_ARMV5_GNUPOC: {
346
347
348
        TargetInformation ti = qt4Target()->qt4Project()->rootProjectNode()->targetInformation(projectFilePath());
        if (!ti.valid)
            return QString();
349
350
        return executableFromPackageUnix(ti.buildDir + QLatin1Char('/') + ti.target + QLatin1String("_template.pkg"));
    }
Tobias Hunger's avatar
Tobias Hunger committed
351
352
    case ProjectExplorer::ToolChain_RVCT2_ARMV5:
    case ProjectExplorer::ToolChain_RVCT2_ARMV6:
353
        return localExecutableFromDevice(qtVersion(), symbianTarget(), targetName(), toolChain);
354
        break;
355
356
357
358
359
    case ProjectExplorer::ToolChain_GCCE: {
        // As of 4.7.1, qmake-gcce-Raptor builds were changed to put all executables into 'armv5'
        const QtVersion *qtv = qtVersion();
        QTC_ASSERT(qtv, return QString(); )
        return qtv->isBuildWithSymbianSbsV2() ?
Tobias Hunger's avatar
Tobias Hunger committed
360
            localExecutableFromDevice(qtv, symbianTarget(), targetName(), ProjectExplorer::ToolChain_RVCT2_ARMV5) :
361
362
363
364
            localExecutableFromDevice(qtv, symbianTarget(), targetName(), toolChain);
    }
        break;
    default:
365
366
        break;
    }
367
    return QString();
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
}

quint32 S60DeviceRunConfiguration::executableUid() const
{
    quint32 uid = 0;
    QString executablePath(localExecutableFileName());
    if (!executablePath.isEmpty()) {
        QFile file(executablePath);
        if (file.open(QIODevice::ReadOnly)) {
            // executable's UID is 4 bytes starting at 8.
            const QByteArray data = file.read(12);
            if (data.size() == 12) {
                const unsigned char *d = reinterpret_cast<const unsigned char*>(data.data() + 8);
                uid = *d++;
                uid += *d++ << 8;
                uid += *d++ << 16;
                uid += *d++ << 24;
            }
        }
    }
    return uid;
}

391
QString S60DeviceRunConfiguration::projectFilePath() const
392
{
393
    return m_proFilePath;
394
395
}

396
QString S60DeviceRunConfiguration::commandLineArguments() const
397
398
399
400
{
    return m_commandLineArguments;
}

401
void S60DeviceRunConfiguration::setCommandLineArguments(const QString &args)
402
403
404
405
{
    m_commandLineArguments = args;
}

406
407
// ======== S60DeviceRunConfigurationFactory

408
409
S60DeviceRunConfigurationFactory::S60DeviceRunConfigurationFactory(QObject *parent) :
    IRunConfigurationFactory(parent)
410
411
412
413
414
415
416
{
}

S60DeviceRunConfigurationFactory::~S60DeviceRunConfigurationFactory()
{
}

Tobias Hunger's avatar
Tobias Hunger committed
417
QStringList S60DeviceRunConfigurationFactory::availableCreationIds(Target *parent) const
418
{
Tobias Hunger's avatar
Tobias Hunger committed
419
420
    Qt4Target *target = qobject_cast<Qt4Target *>(parent);
    if (!target ||
421
        target->id() != QLatin1String(Constants::S60_DEVICE_TARGET_ID))
422
423
        return QStringList();

Tobias Hunger's avatar
Tobias Hunger committed
424
    return target->qt4Project()->applicationProFilePathes(QLatin1String(S60_DEVICE_RC_PREFIX));
425
426
}

427
QString S60DeviceRunConfigurationFactory::displayNameForId(const QString &id) const
428
{
429
430
431
    if (!pathFromId(id).isEmpty())
        return tr("%1 on Symbian Device").arg(QFileInfo(pathFromId(id)).completeBaseName());
    return QString();
432
433
}

Tobias Hunger's avatar
Tobias Hunger committed
434
bool S60DeviceRunConfigurationFactory::canCreate(Target *parent, const QString &id) const
435
{
Tobias Hunger's avatar
Tobias Hunger committed
436
437
    Qt4Target * t(qobject_cast<Qt4Target *>(parent));
    if (!t ||
438
        t->id() != QLatin1String(Constants::S60_DEVICE_TARGET_ID))
439
        return false;
Tobias Hunger's avatar
Tobias Hunger committed
440
    return t->qt4Project()->hasApplicationProFile(pathFromId(id));
441
442
}

Tobias Hunger's avatar
Tobias Hunger committed
443
RunConfiguration *S60DeviceRunConfigurationFactory::create(Target *parent, const QString &id)
444
{
445
446
447
    if (!canCreate(parent, id))
        return 0;

Tobias Hunger's avatar
Tobias Hunger committed
448
449
    Qt4Target *t(static_cast<Qt4Target *>(parent));
    return new S60DeviceRunConfiguration(t, pathFromId(id));
450
451
}

Tobias Hunger's avatar
Tobias Hunger committed
452
bool S60DeviceRunConfigurationFactory::canRestore(Target *parent, const QVariantMap &map) const
453
{
Tobias Hunger's avatar
Tobias Hunger committed
454
455
    Qt4Target * t(qobject_cast<Qt4Target *>(parent));
    if (!t ||
456
        t->id() != QLatin1String(Constants::S60_DEVICE_TARGET_ID))
457
458
459
460
461
        return false;
    QString id(ProjectExplorer::idFromMap(map));
    return id == QLatin1String(S60_DEVICE_RC_ID);
}

Tobias Hunger's avatar
Tobias Hunger committed
462
RunConfiguration *S60DeviceRunConfigurationFactory::restore(Target *parent, const QVariantMap &map)
463
464
465
{
    if (!canRestore(parent, map))
        return 0;
Tobias Hunger's avatar
Tobias Hunger committed
466
467
    Qt4Target *t(static_cast<Qt4Target *>(parent));
    S60DeviceRunConfiguration *rc(new S60DeviceRunConfiguration(t, QString()));
468
469
470
471
472
473
474
    if (rc->fromMap(map))
        return rc;

    delete rc;
    return 0;
}

Tobias Hunger's avatar
Tobias Hunger committed
475
bool S60DeviceRunConfigurationFactory::canClone(Target *parent, RunConfiguration *source) const
476
{
Tobias Hunger's avatar
Tobias Hunger committed
477
    if (!qobject_cast<Qt4Target *>(parent))
478
479
480
481
        return false;
    return source->id() == QLatin1String(S60_DEVICE_RC_ID);
}

Tobias Hunger's avatar
Tobias Hunger committed
482
RunConfiguration *S60DeviceRunConfigurationFactory::clone(Target *parent, RunConfiguration *source)
483
484
485
{
    if (!canClone(parent, source))
        return 0;
Tobias Hunger's avatar
Tobias Hunger committed
486
    Qt4Target *t = static_cast<Qt4Target *>(parent);
487
    S60DeviceRunConfiguration * old(static_cast<S60DeviceRunConfiguration *>(source));
Tobias Hunger's avatar
Tobias Hunger committed
488
    return new S60DeviceRunConfiguration(t, old);
489
490
}

491
// ======== S60DeviceRunControlBase
Friedemann Kleint's avatar
Friedemann Kleint committed
492

493
S60DeviceRunControl::S60DeviceRunControl(RunConfiguration *runConfiguration, QString mode) :
dt's avatar
dt committed
494
    RunControl(runConfiguration, mode),
495
    m_toolChain(ProjectExplorer::ToolChain_INVALID),
496
    m_launcher(0)
497
498
499
500
{
    // connect for automatically reporting the "finished deploy" state to the progress manager
    connect(this, SIGNAL(finished()), this, SLOT(reportDeployFinished()));

501
    S60DeviceRunConfiguration *s60runConfig = qobject_cast<S60DeviceRunConfiguration *>(runConfiguration);
502
    const Qt4BuildConfiguration *activeBuildConf = s60runConfig->qt4Target()->activeBuildConfiguration();
503
    S60DeployConfiguration *activeDeployConf = qobject_cast<S60DeployConfiguration *>(s60runConfig->qt4Target()->activeDeployConfiguration());
dt's avatar
dt committed
504

505
    QTC_ASSERT(s60runConfig, return);
506
    m_toolChain = s60runConfig->toolChainType();
507
    m_serialPortName = activeDeployConf->serialPortName();
508
    m_serialPortFriendlyName = SymbianUtils::SymbianDeviceManager::instance()->friendlyNameForPort(m_serialPortName);
509
    m_targetName = s60runConfig->targetName();
510
    m_commandLineArguments = s60runConfig->commandLineArguments();
511
    m_qtDir = activeBuildConf->qtVersion()->versionInfo().value("QT_INSTALL_DATA");
512
513
    m_installationDrive = activeDeployConf->installationDrive();
    if (const QtVersion *qtv = activeDeployConf->qtVersion())
514
515
        m_qtBinPath = qtv->versionInfo().value(QLatin1String("QT_INSTALL_BINS"));
    QTC_ASSERT(!m_qtBinPath.isEmpty(), return);
516
    m_executableFileName = s60runConfig->localExecutableFileName();
517
    if (debug)
518
        qDebug() << "S60DeviceRunControl::CT" << m_targetName << ProjectExplorer::ToolChain::toolChainName(m_toolChain)
519
                 << m_serialPortName;
520
}
521

522
S60DeviceRunControl::~S60DeviceRunControl()
523
524
525
526
527
528
529
{
    if (m_launcher) {
        m_launcher->deleteLater();
        m_launcher = 0;
    }
}

530
void S60DeviceRunControl::start()
531
{
532
533
    m_launchProgress = new QFutureInterface<void>;
    Core::ICore::instance()->progressManager()->addTask(m_launchProgress->future(),
534
535
                                                        tr("Launching"),
                                                        QLatin1String("Symbian.Launch"));
536
537
538
    m_launchProgress->setProgressRange(0, PROGRESS_MAX);
    m_launchProgress->setProgressValue(0);
    m_launchProgress->reportStarted();
539
    emit started();
540
    if (m_serialPortName.isEmpty()) {
541
        m_launchProgress->reportCanceled();
542
        appendMessage(this, tr("No device is connected. Please connect a device and try again."), true);
543
544
545
        emit finished();
        return;
    }
546

547
    emit appendMessage(this, tr("Executable file: %1").arg(msgListFile(m_executableFileName)), false);
con's avatar
con committed
548

549
550
551
552
    QString errorMessage;
    QString settingsCategory;
    QString settingsPage;
    if (!checkConfiguration(&errorMessage, &settingsCategory, &settingsPage)) {
553
        m_launchProgress->reportCanceled();
554
        appendMessage(this, errorMessage, true);
555
        emit finished();
556
557
        Core::ICore::instance()->showWarningWithOptions(tr("Debugger for Symbian Platform"),
                                                        errorMessage, QString(),
558
559
560
561
                                                        settingsCategory, settingsPage);
        return;
    }

562
    startLaunching();
563
564
}

565
RunControl::StopResult S60DeviceRunControl::stop()
566
567
568
{
    if (m_launcher)
        m_launcher->terminate();
569
    return AsynchronousStop;
570
571
}

572
bool S60DeviceRunControl::isRunning() const
573
{
574
575
576
    return m_launcher && (m_launcher->state() == trk::Launcher::Connecting
                          || m_launcher->state() == trk::Launcher::Connected
                          || m_launcher->state() == trk::Launcher::WaitingForTrk);
577
578
}

579
void S60DeviceRunControl::startLaunching()
580
{
581
    QString errorMessage;
582
    if (setupLauncher(errorMessage)) {
583
584
        if (m_launchProgress)
                    m_launchProgress->setProgressValue(PROGRESS_MAX/2);
585
    } else {
586
        if (!errorMessage.isEmpty())
587
            appendMessage(this, errorMessage, true);
588
        stop();
con's avatar
con committed
589
590
        emit finished();
    }
591
592
}

593
bool S60DeviceRunControl::setupLauncher(QString &errorMessage)
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
{
    connect(SymbianUtils::SymbianDeviceManager::instance(), SIGNAL(deviceRemoved(const SymbianUtils::SymbianDevice)),
            this, SLOT(deviceRemoved(SymbianUtils::SymbianDevice)));
    m_launcher = trk::Launcher::acquireFromDeviceManager(m_serialPortName, 0, &errorMessage);
    if (!m_launcher)
        return false;

    connect(m_launcher, SIGNAL(finished()), this, SLOT(launcherFinished()));
    connect(m_launcher, SIGNAL(canNotConnect(QString)), this, SLOT(printConnectFailed(QString)));
    connect(m_launcher, SIGNAL(stateChanged(int)), this, SLOT(slotLauncherStateChanged(int)));
    connect(m_launcher, SIGNAL(processStopped(uint,uint,uint,QString)),
            this, SLOT(processStopped(uint,uint,uint,QString)));

    if (!m_commandLineArguments.isEmpty())
        m_launcher->setCommandLineArgs(m_commandLineArguments);

    const QString runFileName = QString::fromLatin1("%1:\\sys\\bin\\%2.exe").arg(m_installationDrive).arg(m_targetName);
    initLauncher(runFileName, m_launcher);
    const trk::PromptStartCommunicationResult src =
            S60RunConfigBluetoothStarter::startCommunication(m_launcher->trkDevice(),
                                                             0, &errorMessage);
    if (src != trk::PromptStartCommunicationConnected)
        return false;

    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);
        return false;
    }
    return true;
}

626
void S60DeviceRunControl::printConnectFailed(const QString &errorMessage)
627
{
628
    emit appendMessage(this, tr("Could not connect to App TRK on device: %1. Restarting App TRK might help.").arg(errorMessage), true);
629
630
}

631
void S60DeviceRunControl::launcherFinished()
632
{
633
    trk::Launcher::releaseToDeviceManager(m_launcher);
634
635
636
637
638
    m_launcher->deleteLater();
    m_launcher = 0;
    handleLauncherFinished();
}

639
void S60DeviceRunControl::reportDeployFinished()
640
{
641
642
643
644
    if (m_launchProgress) {
        m_launchProgress->reportFinished();
        delete m_launchProgress;
        m_launchProgress = 0;
645
646
647
    }
}

648
void S60DeviceRunControl::processStopped(uint pc, uint pid, uint tid, const QString& reason)
649
{
650
    emit addToOutputWindow(this, trk::Launcher::msgStopped(pid, tid, pc, reason), false);
651
652
653
    m_launcher->terminate();
}

654
QMessageBox *S60DeviceRunControl::createTrkWaitingMessageBox(const QString &port, QWidget *parent)
655
{
Friedemann Kleint's avatar
Friedemann Kleint committed
656
657
658
659
660
    const QString title  = tr("Waiting for App TRK");
    const QString text = tr("Qt Creator is waiting for the TRK application to connect.<br>"
                            "Please make sure the application is running on "
                            "your mobile phone and the right port is "
                            "configured in the project settings.").arg(port);
661
662
663
664
665
    QMessageBox *rc = new QMessageBox(QMessageBox::Information, title, text,
                                      QMessageBox::Cancel, parent);
    return rc;
}

666
void S60DeviceRunControl::slotLauncherStateChanged(int s)
667
668
{
    if (s == trk::Launcher::WaitingForTrk) {
669
        QMessageBox *mb = S60DeviceRunControl::createTrkWaitingMessageBox(m_launcher->trkServerName(),
670
671
672
673
674
675
676
                                                     Core::ICore::instance()->mainWindow());
        connect(m_launcher, SIGNAL(stateChanged(int)), mb, SLOT(close()));
        connect(mb, SIGNAL(finished(int)), this, SLOT(slotWaitingForTrkClosed()));
        mb->open();
    }
}

677
void S60DeviceRunControl::slotWaitingForTrkClosed()
678
679
680
{
    if (m_launcher && m_launcher->state() == trk::Launcher::WaitingForTrk) {
        stop();
681
        appendMessage(this, tr("Canceled."), true);
682
683
684
685
        emit finished();
    }
}

686
void S60DeviceRunControl::printApplicationOutput(const QString &output)
687
688
689
690
{
    printApplicationOutput(output, false);
}

691
void S60DeviceRunControl::printApplicationOutput(const QString &output, bool onStdErr)
Friedemann Kleint's avatar
Friedemann Kleint committed
692
{
693
    emit addToOutputWindowInline(this, output, onStdErr);
Friedemann Kleint's avatar
Friedemann Kleint committed
694
695
}

696
void S60DeviceRunControl::deviceRemoved(const SymbianUtils::SymbianDevice &d)
697
{
698
699
700
701
    if (m_launcher && d.portName() == m_serialPortName) {
        trk::Launcher::releaseToDeviceManager(m_launcher);
        m_launcher->deleteLater();
        m_launcher = 0;
702
        appendMessage(this, tr("The device '%1' has been disconnected").arg(d.friendlyName()), true);
703
704
705
706
        emit finished();
    }
}

707
bool S60DeviceRunControl::checkConfiguration(QString * /* errorMessage */,
708
709
710
711
712
713
                                                 QString * /* settingsCategory */,
                                                 QString * /* settingsPage */) const
{
    return true;
}

714
715
716
void S60DeviceRunControl::initLauncher(const QString &executable, trk::Launcher *launcher)
{
     connect(launcher, SIGNAL(startingApplication()), this, SLOT(printStartingNotice()));
717
718
     connect(launcher, SIGNAL(applicationRunning(uint)), this, SLOT(applicationRunNotice(uint)));
     connect(launcher, SIGNAL(canNotRun(QString)), this, SLOT(applicationRunFailedNotice(QString)));
719
     connect(launcher, SIGNAL(applicationOutputReceived(QString)), this, SLOT(printApplicationOutput(QString)));
720
     launcher->addStartupActions(trk::Launcher::ActionRun);
721
722
723
724
725
726
     launcher->setFileName(executable);
}

void S60DeviceRunControl::handleLauncherFinished()
{
     emit finished();
727
     emit appendMessage(this, tr("Finished."), false);
728
729
 }

con's avatar
con committed
730
731
void S60DeviceRunControl::printStartingNotice()
{
732
    emit appendMessage(this, tr("Starting application..."), false);
con's avatar
con committed
733
734
}

735
void S60DeviceRunControl::applicationRunNotice(uint pid)
con's avatar
con committed
736
{
737
    emit appendMessage(this, tr("Application running with pid %1.").arg(pid), false);
738
739
    if (m_launchProgress)
        m_launchProgress->setProgressValue(PROGRESS_MAX);
con's avatar
con committed
740
741
}

742
743
void S60DeviceRunControl::applicationRunFailedNotice(const QString &errorMessage)
{
744
    emit appendMessage(this, tr("Could not start application: %1").arg(errorMessage), true);
745
746
}

747
748
// ======== S60DeviceDebugRunControl

749
static inline QString localExecutable(const S60DeviceRunConfiguration *rc)
750
{
751
752
    if (const S60DeviceRunConfiguration *s60runConfig = qobject_cast<const S60DeviceRunConfiguration *>(rc))
        return s60runConfig->localExecutableFileName();
753
754
    return QString();
}
755

756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
// Return symbol file which should co-exist with the executable.
// location in debug builds. This can be 'foo.sym' (ABLD) or 'foo.exe.sym' (Raptor)
static inline QString symbolFileFromExecutable(const QString &executable)
{
    // 'foo.exe.sym' (Raptor)
    const QFileInfo raptorSymFi(executable + QLatin1String(".sym"));
    if (raptorSymFi.isFile())
        return raptorSymFi.absoluteFilePath();
    // 'foo.sym' (ABLD)
    const int lastDotPos = executable.lastIndexOf(QLatin1Char('.'));
    if (lastDotPos != -1) {
        const QString symbolFileName = executable.mid(0, lastDotPos) + QLatin1String(".sym");
        const QFileInfo symbolFileNameFi(symbolFileName);
        if (symbolFileNameFi.isFile())
            return symbolFileNameFi.absoluteFilePath();
    }
    return QString();
}

775
776
777
778
779
780
781
// Create start parameters from run configuration
Debugger::DebuggerStartParameters S60DeviceDebugRunControl::s60DebuggerStartParams(const S60DeviceRunConfiguration *rc)
{
    Debugger::DebuggerStartParameters sp;
    QTC_ASSERT(rc, return sp);

    const S60DeployConfiguration *activeDeployConf = qobject_cast<S60DeployConfiguration *>(rc->qt4Target()->activeDeployConfiguration());
782

783
    const QString debugFileName = QString::fromLatin1("%1:\\sys\\bin\\%2.exe")
784
            .arg(activeDeployConf->installationDrive()).arg(rc->targetName());
785

786
787
788
789
790
    sp.remoteChannel = activeDeployConf->serialPortName();
    sp.processArgs = rc->commandLineArguments();
    sp.startMode = Debugger::StartInternal;
    sp.toolChainType = rc->toolChainType();
    sp.executable = debugFileName;
791
    sp.executableUid = rc->executableUid();
792

793
    QTC_ASSERT(sp.executableUid, return sp);
794
795

    // Prefer the '*.sym' file over the '.exe', which should exist at the same
796
797
    // location in debug builds. This can be 'foo.exe' (ABLD) or 'foo.exe.sym' (Raptor)
    sp.symbolFileName = symbolFileFromExecutable(localExecutable(rc));
798
799
800
801
802
803
804
805
806
807
808
809
    return sp;
}

S60DeviceDebugRunControl::S60DeviceDebugRunControl(S60DeviceRunConfiguration *rc,
                                                   const QString &) :
    Debugger::DebuggerRunControl(rc, Debugger::GdbEngineType,
                                 S60DeviceDebugRunControl::s60DebuggerStartParams(rc))
{
    if (startParameters().symbolFileName.isEmpty()) {
        const QString msg = tr("Warning: Cannot locate the symbol file belonging to %1.").
                               arg(localExecutable(rc));
        emit appendMessage(this, msg, true);
810
    }
811
812
}

813
void S60DeviceDebugRunControl::start()
814
{
815
816
817
    QString errorMessage;
    QString settingsCategory;
    QString settingsPage;
818
819
    if (!Debugger::DebuggerRunControl::checkDebugConfiguration(startParameters().toolChainType,
                                                               &errorMessage, &settingsCategory, &settingsPage)) {
820
821
822
823
824
825
        appendMessage(this, errorMessage, true);
        emit finished();
        Core::ICore::instance()->showWarningWithOptions(tr("Debugger for Symbian Platform"),
                                                        errorMessage, QString(),
                                                        settingsCategory, settingsPage);
        return;
826
    }
827

828
    emit appendMessage(this, tr("Launching debugger..."), false);
829
    Debugger::DebuggerRunControl::start();
830
831
}

832
833
834
S60DeviceDebugRunControl::~S60DeviceDebugRunControl()
{
}