OSDN Git Service

It's 2011 now.
[qt-creator-jp/qt-creator-jp.git] / src / plugins / qt4projectmanager / qt-s60 / s60deploystep.cpp
1 /**************************************************************************
2 **
3 ** This file is part of Qt Creator
4 **
5 ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
6 **
7 ** Contact: Nokia Corporation (qt-info@nokia.com)
8 **
9 ** No Commercial Usage
10 **
11 ** This file contains pre-release code and may not be distributed.
12 ** You may use this file in accordance with the terms and conditions
13 ** contained in the Technology Preview License Agreement accompanying
14 ** this package.
15 **
16 ** GNU Lesser General Public License Usage
17 **
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file.  Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 **
25 ** In addition, as a special exception, Nokia gives you certain additional
26 ** rights.  These rights are described in the Nokia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 **
29 ** If you have questions regarding the use of this file, please contact
30 ** Nokia at qt-info@nokia.com.
31 **
32 **************************************************************************/
33
34 #include "s60deploystep.h"
35
36 #include "qt4buildconfiguration.h"
37 #include "s60deployconfiguration.h"
38 #include "s60devicerunconfiguration.h"
39 #include "s60runconfigbluetoothstarter.h"
40
41 #include <coreplugin/icore.h>
42 #include <projectexplorer/buildsteplist.h>
43 #include <projectexplorer/target.h>
44 #include <projectexplorer/projectexplorerconstants.h>
45 #include <qt4projectmanagerconstants.h>
46
47 #include <symbianutils/launcher.h>
48 #include <symbianutils/symbiandevicemanager.h>
49
50 #include <QtGui/QMessageBox>
51 #include <QtGui/QMainWindow>
52
53 #include <QtCore/QTimer>
54 #include <QtCore/QDateTime>
55 #include <QtCore/QDir>
56 #include <QtCore/QEventLoop>
57
58 using namespace ProjectExplorer;
59 using namespace Qt4ProjectManager::Internal;
60
61 namespace {
62     const char * const S60_DEPLOY_STEP_ID = "Qt4ProjectManager.S60DeployStep";
63 }
64
65 static inline bool ensureDeleteFile(const QString &fileName, QString *errorMessage)
66 {
67     QFile file(fileName);
68     if (file.exists() && !file.remove()) {
69         *errorMessage = S60DeployStep::tr("Unable to remove existing file '%1': %2").arg(fileName, file.errorString());
70         return false;
71     }
72     return true;
73 }
74
75 static inline bool renameFile(const QString &sourceName, const QString &targetName,
76                               QString *errorMessage)
77 {
78     if (sourceName == targetName)
79         return true;
80     if (!ensureDeleteFile(targetName, errorMessage))
81         return false;
82     QFile source(sourceName);
83     if (!source.rename(targetName)) {
84         *errorMessage = S60DeployStep::tr("Unable to rename file '%1' to '%2': %3")
85                         .arg(sourceName, targetName, source.errorString());
86         return false;
87     }
88     return true;
89 }
90
91 // #pragma mark -- S60DeployStep
92
93 S60DeployStep::S60DeployStep(ProjectExplorer::BuildStepList *bc,
94                              S60DeployStep *bs):
95         BuildStep(bc, bs), m_timer(0),
96         m_releaseDeviceAfterLauncherFinish(bs->m_releaseDeviceAfterLauncherFinish),
97         m_handleDeviceRemoval(bs->m_handleDeviceRemoval),
98         m_launcher(0), m_eventLoop(0)
99 {
100     ctor();
101 }
102
103 S60DeployStep::S60DeployStep(ProjectExplorer::BuildStepList *bc):
104         BuildStep(bc, QLatin1String(S60_DEPLOY_STEP_ID)), m_timer(0),
105         m_releaseDeviceAfterLauncherFinish(true),
106         m_handleDeviceRemoval(true), m_launcher(0), m_eventLoop(0)
107 {
108     ctor();
109 }
110
111 void S60DeployStep::ctor()
112 {
113     //: Qt4 Deploystep display name
114     setDefaultDisplayName(tr("Deploy"));
115 }
116
117 S60DeployStep::~S60DeployStep()
118 {
119     delete m_timer;
120     delete m_launcher;
121     delete m_eventLoop;
122 }
123
124
125 bool S60DeployStep::init()
126 {
127     Qt4BuildConfiguration *bc = static_cast<Qt4BuildConfiguration *>(buildConfiguration());
128     S60DeployConfiguration* deployConfiguration = static_cast<S60DeployConfiguration *>(bc->target()->activeDeployConfiguration());
129     if(!deployConfiguration)
130         return false;
131     m_serialPortName = deployConfiguration->serialPortName();
132     m_serialPortFriendlyName = SymbianUtils::SymbianDeviceManager::instance()->friendlyNameForPort(m_serialPortName);
133     m_packageFileNamesWithTarget = deployConfiguration->packageFileNamesWithTargetInfo();
134     m_signedPackages = deployConfiguration->signedPackages();
135     m_installationDrive = deployConfiguration->installationDrive();
136     m_silentInstall = deployConfiguration->silentInstall();
137
138     QString message;
139     if (m_launcher) {
140         trk::Launcher::releaseToDeviceManager(m_launcher);
141         delete m_launcher;
142         m_launcher = 0;
143     }
144
145     m_launcher = trk::Launcher::acquireFromDeviceManager(m_serialPortName, this, &message);
146     if (!message.isEmpty() || !m_launcher) {
147         if (m_launcher)
148             trk::Launcher::releaseToDeviceManager(m_launcher);
149         delete m_launcher;
150         m_launcher = 0;
151         appendMessage(message, true);
152         return true;
153     }
154     // Prompt the user to start up the Blue tooth connection
155     const trk::PromptStartCommunicationResult src =
156             S60RunConfigBluetoothStarter::startCommunication(m_launcher->trkDevice(),
157                                                              0, &message);
158     if (src != trk::PromptStartCommunicationConnected) {
159         if (!message.isEmpty())
160             trk::Launcher::releaseToDeviceManager(m_launcher);
161         delete m_launcher;
162         m_launcher = 0;
163         appendMessage(message, true);
164         return false;
165     }
166     return true;
167 }
168
169 QVariantMap S60DeployStep::toMap() const
170 {
171     return BuildStep::toMap();
172 }
173
174 bool S60DeployStep::fromMap(const QVariantMap &map)
175 {
176     return BuildStep::fromMap(map);
177 }
178
179 void S60DeployStep::appendMessage(const QString &error, bool isError)
180 {
181     emit addOutput(error, isError?ProjectExplorer::BuildStep::ErrorMessageOutput:
182                                   ProjectExplorer::BuildStep::MessageOutput);
183 }
184
185 bool S60DeployStep::processPackageName(QString &errorMessage)
186 {
187     for (int i = 0; i < m_signedPackages.count(); ++i) {
188         QFileInfo packageInfo(m_signedPackages.at(i));
189         // support for 4.6.1 and pre, where make sis creates 'targetname_armX_udeb.sis' instead of 'targetname.sis'
190         QFileInfo packageWithTargetInfo(m_packageFileNamesWithTarget.at(i));
191         // does the 4.6.1 version exist?
192         if (packageWithTargetInfo.exists() && packageWithTargetInfo.isFile()) {
193             // is the 4.6.1 version newer? (to guard against behavior change Qt Creator 1.3 --> 2.0)
194             if (!packageInfo.exists() || packageInfo.lastModified() < packageWithTargetInfo.lastModified()) { //TODO change the QtCore
195                 // the 'targetname_armX_udeb.sis' crap exists and is new, rename it
196                 appendMessage(tr("Renaming new package '%1' to '%2'")
197                               .arg(QDir::toNativeSeparators(m_packageFileNamesWithTarget.at(i)),
198                                    QDir::toNativeSeparators(m_signedPackages.at(i))), false);
199                 return renameFile(m_packageFileNamesWithTarget.at(i), m_signedPackages.at(i), &errorMessage);
200             } else {
201                 // the 'targetname_armX_udeb.sis' crap exists but is old, remove it
202                 appendMessage(tr("Removing old package '%1'")
203                               .arg(QDir::toNativeSeparators(m_packageFileNamesWithTarget.at(i))),
204                               false);
205                 ensureDeleteFile(m_packageFileNamesWithTarget.at(i), &errorMessage);
206             }
207         }
208         if (!packageInfo.exists() || !packageInfo.isFile()) {
209             errorMessage = tr("'%1': Package file not found").arg(m_signedPackages.at(i));
210             return false;
211         }
212     }
213     return true;
214 }
215
216 void S60DeployStep::start()
217 {
218     QString errorMessage;
219
220     if (m_serialPortName.isEmpty() || !m_launcher) {
221         errorMessage = tr("No device is connected. Please connect a device and try again.");
222         appendMessage(errorMessage, true);
223         emit addTask(ProjectExplorer::Task(ProjectExplorer::Task::Error,
224                                            errorMessage,
225                                            QString(), -1,
226                                            ProjectExplorer::Constants::TASK_CATEGORY_BUILDSYSTEM));
227         emit finished();
228         return;
229     }
230
231     // make sure we have the right name of the sis package
232     if (processPackageName(errorMessage)) {
233         startDeployment();
234     } else {
235         errorMessage = tr("Failed to find package %1").arg(errorMessage);
236         appendMessage(errorMessage, true);
237         emit addTask(ProjectExplorer::Task(ProjectExplorer::Task::Error,
238                                            errorMessage,
239                                            QString(), -1,
240                                            ProjectExplorer::Constants::TASK_CATEGORY_BUILDSYSTEM));
241         stop();
242         emit finished();
243     }
244 }
245
246 void S60DeployStep::stop()
247 {
248     if (m_launcher)
249         m_launcher->terminate();
250     emit finished();
251 }
252
253 void S60DeployStep::setupConnections()
254 {
255     connect(SymbianUtils::SymbianDeviceManager::instance(), SIGNAL(deviceRemoved(SymbianUtils::SymbianDevice)),
256             this, SLOT(deviceRemoved(SymbianUtils::SymbianDevice)));
257     connect(m_launcher, SIGNAL(finished()), this, SLOT(launcherFinished()));
258
259     connect(m_launcher, SIGNAL(canNotConnect(QString)), this, SLOT(connectFailed(QString)));
260     connect(m_launcher, SIGNAL(copyingStarted(QString)), this, SLOT(printCopyingNotice(QString)));
261     connect(m_launcher, SIGNAL(canNotCreateFile(QString,QString)), this, SLOT(createFileFailed(QString,QString)));
262     connect(m_launcher, SIGNAL(canNotWriteFile(QString,QString)), this, SLOT(writeFileFailed(QString,QString)));
263     connect(m_launcher, SIGNAL(canNotCloseFile(QString,QString)), this, SLOT(closeFileFailed(QString,QString)));
264     connect(m_launcher, SIGNAL(installingStarted(QString)), this, SLOT(printInstallingNotice(QString)));
265     connect(m_launcher, SIGNAL(canNotInstall(QString,QString)), this, SLOT(installFailed(QString,QString)));
266     connect(m_launcher, SIGNAL(installingFinished()), this, SLOT(printInstallingFinished()));
267     connect(m_launcher, SIGNAL(stateChanged(int)), this, SLOT(slotLauncherStateChanged(int)));
268 }
269
270 void S60DeployStep::startDeployment()
271 {
272     Q_ASSERT(m_launcher);
273
274     setupConnections();
275
276     QStringList copyDst;
277     foreach (const QString &signedPackage, m_signedPackages)
278         copyDst << QString::fromLatin1("%1:\\Data\\%2").arg(m_installationDrive).arg(QFileInfo(signedPackage).fileName());
279
280     m_launcher->setCopyFileNames(m_signedPackages, copyDst);
281     m_launcher->setInstallFileNames(copyDst);
282     m_launcher->setInstallationDrive(m_installationDrive);
283     m_launcher->setInstallationMode(m_silentInstall?trk::Launcher::InstallationModeSilentAndUser:
284                                                     trk::Launcher::InstallationModeUser);
285     m_launcher->addStartupActions(trk::Launcher::ActionCopyInstall);
286
287     // TODO readd information about packages? msgListFile(m_signedPackage)
288     appendMessage(tr("Deploying application to '%2'...").arg(m_serialPortFriendlyName), false);
289
290     QString errorMessage;
291     if (!m_launcher->startServer(&errorMessage)) {
292         errorMessage = tr("Could not connect to phone on port '%1': %2\n"
293                           "Check if the phone is connected and App TRK is running.").arg(m_serialPortName, errorMessage);
294         appendMessage(errorMessage, true);
295         stop();
296         emit finished();
297     }
298 }
299
300 void S60DeployStep::run(QFutureInterface<bool> &fi)
301 {
302     m_futureInterface = &fi;
303     m_deployResult = true;
304     connect(this, SIGNAL(finished()),
305             this, SLOT(launcherFinished()));
306     connect(this, SIGNAL(finishNow()),
307             this, SLOT(launcherFinished()), Qt::DirectConnection);
308
309     start();
310     m_timer = new QTimer();
311     connect(m_timer, SIGNAL(timeout()), this, SLOT(checkForCancel()), Qt::DirectConnection);
312     m_timer->start(500);
313     m_eventLoop = new QEventLoop();
314     m_eventLoop->exec();
315     m_timer->stop();
316     delete m_timer;
317     m_timer = 0;
318
319     delete m_eventLoop;
320     m_eventLoop = 0;
321     fi.reportResult(m_deployResult);
322     m_futureInterface = 0;
323 }
324
325 void S60DeployStep::setReleaseDeviceAfterLauncherFinish(bool v)
326 {
327     m_releaseDeviceAfterLauncherFinish = v;
328 }
329
330 void S60DeployStep::slotLauncherStateChanged(int s)
331 {
332     if (s == trk::Launcher::WaitingForTrk) {
333         QMessageBox *mb = S60DeviceRunControl::createTrkWaitingMessageBox(m_launcher->trkServerName(),
334                                                                               Core::ICore::instance()->mainWindow());
335         connect(m_launcher, SIGNAL(stateChanged(int)), mb, SLOT(close()));
336         connect(mb, SIGNAL(finished(int)), this, SLOT(slotWaitingForTrkClosed()));
337         mb->open();
338     }
339 }
340
341 void S60DeployStep::slotWaitingForTrkClosed()
342 {
343     if (m_launcher && m_launcher->state() == trk::Launcher::WaitingForTrk) {
344         stop();
345         appendMessage(tr("Canceled."), true);
346         emit finished();
347     }
348 }
349
350 void S60DeployStep::createFileFailed(const QString &filename, const QString &errorMessage)
351 {
352     appendMessage(tr("Could not create file %1 on device: %2").arg(filename, errorMessage), true);
353     m_deployResult = false;
354 }
355
356 void S60DeployStep::writeFileFailed(const QString &filename, const QString &errorMessage)
357 {
358     appendMessage(tr("Could not write to file %1 on device: %2").arg(filename, errorMessage), true);
359     m_deployResult = false;
360 }
361
362 void S60DeployStep::closeFileFailed(const QString &filename, const QString &errorMessage)
363 {
364     const QString msg = tr("Could not close file %1 on device: %2. It will be closed when App TRK is closed.");
365     appendMessage( msg.arg(filename, errorMessage), true);
366     m_deployResult = false;
367 }
368
369 void S60DeployStep::connectFailed(const QString &errorMessage)
370 {
371     appendMessage(tr("Could not connect to App TRK on device: %1. Restarting App TRK might help.").arg(errorMessage), true);
372     m_deployResult = false;
373 }
374
375 void S60DeployStep::printCopyingNotice(const QString &fileName)
376 {
377     appendMessage(tr("Copying \"%1\"...").arg(fileName), false);
378 }
379
380 void S60DeployStep::printInstallingNotice(const QString &packageName)
381 {
382     appendMessage(tr("Installing package \"%1\" on drive %2:...").arg(packageName).arg(m_installationDrive), false);
383 }
384
385 void S60DeployStep::printInstallingFinished()
386 {
387     appendMessage(tr("Installation has finished"), false);
388 }
389
390 void S60DeployStep::installFailed(const QString &filename, const QString &errorMessage)
391 {
392     appendMessage(tr("Could not install from package %1 on device: %2").arg(filename, errorMessage), true);
393     m_deployResult = false;
394 }
395
396 void S60DeployStep::checkForCancel()
397 {
398     if (m_futureInterface->isCanceled() && m_timer->isActive()) {
399         m_timer->stop();
400         stop();
401         appendMessage(tr("Canceled."), true);
402         emit finishNow();
403     }
404 }
405
406 void S60DeployStep::launcherFinished()
407 {
408     if (m_releaseDeviceAfterLauncherFinish && m_launcher) {
409         m_handleDeviceRemoval = false;
410         trk::Launcher::releaseToDeviceManager(m_launcher);
411     }
412     if(m_launcher)
413         m_launcher->deleteLater();
414     m_launcher = 0;
415     if(m_eventLoop)
416         m_eventLoop->exit();
417 }
418
419 void S60DeployStep::deviceRemoved(const SymbianUtils::SymbianDevice &d)
420 {
421     if (m_handleDeviceRemoval && d.portName() == m_serialPortName) {
422         appendMessage(tr("The device '%1' has been disconnected").arg(d.friendlyName()), true);
423         emit finished();
424     }
425 }
426
427 // #pragma mark -- S60DeployStepWidget
428
429 BuildStepConfigWidget *S60DeployStep::createConfigWidget()
430 {
431     return new S60DeployStepWidget();
432 }
433
434 S60DeployStepWidget::S60DeployStepWidget() : ProjectExplorer::BuildStepConfigWidget()
435 {
436 }
437
438 void S60DeployStepWidget::init()
439 {
440 }
441
442 QString S60DeployStepWidget::summaryText() const
443 {
444     return QString("<b>%1</b>").arg(displayName());
445 }
446
447 QString S60DeployStepWidget::displayName() const
448 {
449     return tr("Deploy SIS Package");
450 }
451
452 // #pragma mark -- S60DeployStepFactory
453
454 S60DeployStepFactory::S60DeployStepFactory(QObject *parent) :
455         ProjectExplorer::IBuildStepFactory(parent)
456 {
457 }
458
459 S60DeployStepFactory::~S60DeployStepFactory()
460 {
461 }
462
463 bool S60DeployStepFactory::canCreate(ProjectExplorer::BuildStepList *parent, const QString &id) const
464 {
465     if (parent->id() != QLatin1String(ProjectExplorer::Constants::BUILDSTEPS_DEPLOY))
466         return false;
467     if (parent->target()->id() != QLatin1String(Constants::S60_DEVICE_TARGET_ID))
468         return false;
469     return (id == QLatin1String(S60_DEPLOY_STEP_ID));
470 }
471
472 ProjectExplorer::BuildStep *S60DeployStepFactory::create(ProjectExplorer::BuildStepList *parent, const QString &id)
473 {
474     if (!canCreate(parent, id))
475         return 0;
476     return new S60DeployStep(parent);
477 }
478
479 bool S60DeployStepFactory::canClone(ProjectExplorer::BuildStepList *parent, ProjectExplorer::BuildStep *source) const
480 {
481     if (!canCreate(parent, source->id()))
482         return false;
483     if (!qobject_cast<S60DeployStep *>(source))
484         return false;
485     return true;
486 }
487
488 ProjectExplorer::BuildStep *S60DeployStepFactory::clone(ProjectExplorer::BuildStepList *parent, ProjectExplorer::BuildStep *source)
489 {
490     if (!canClone(parent, source))
491         return 0;
492     return new S60DeployStep(parent, static_cast<S60DeployStep *>(source));
493 }
494
495 bool S60DeployStepFactory::canRestore(ProjectExplorer::BuildStepList *parent, const QVariantMap &map) const
496 {
497     QString id(ProjectExplorer::idFromMap(map));
498     return canCreate(parent, id);
499 }
500
501 ProjectExplorer::BuildStep *S60DeployStepFactory::restore(ProjectExplorer::BuildStepList *parent, const QVariantMap &map)
502 {
503     if (!canRestore(parent, map))
504         return 0;
505     S60DeployStep *bs = new S60DeployStep(parent);
506     if (bs->fromMap(map))
507         return bs;
508     delete bs;
509     return 0;
510 }
511
512 QStringList S60DeployStepFactory::availableCreationIds(ProjectExplorer::BuildStepList *parent) const
513 {
514     if (parent->id() == QLatin1String(ProjectExplorer::Constants::BUILDSTEPS_DEPLOY)
515         && parent->target()->id() == QLatin1String(Constants::S60_DEVICE_TARGET_ID))
516         return QStringList() << QLatin1String(S60_DEPLOY_STEP_ID);
517     return QStringList();
518 }
519
520 QString S60DeployStepFactory::displayNameForId(const QString &id) const
521 {
522     if (id == QLatin1String(S60_DEPLOY_STEP_ID))
523         return tr("Deploy SIS Package");
524     return QString();
525 }