OSDN Git Service

Update license.
[qt-creator-jp/qt-creator-jp.git] / src / plugins / qt4projectmanager / qt-s60 / s60devicerunconfiguration.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 (info@qt.nokia.com)
8 **
9 **
10 ** GNU Lesser General Public License Usage
11 **
12 ** This file may be used under the terms of the GNU Lesser General Public
13 ** License version 2.1 as published by the Free Software Foundation and
14 ** appearing in the file LICENSE.LGPL included in the packaging of this file.
15 ** Please review the following information to ensure the GNU Lesser General
16 ** Public License version 2.1 requirements will be met:
17 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
18 **
19 ** In addition, as a special exception, Nokia gives you certain additional
20 ** rights. These rights are described in the Nokia Qt LGPL Exception
21 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
22 **
23 ** Other Usage
24 **
25 ** Alternatively, this file may be used in accordance with the terms and
26 ** conditions contained in a signed written agreement between you and Nokia.
27 **
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
30 **
31 **************************************************************************/
32
33 #include "s60devicerunconfiguration.h"
34 #include "s60devicerunconfigurationwidget.h"
35 #include "s60deployconfiguration.h"
36 #include "qt4project.h"
37 #include "qt4target.h"
38 #include "s60manager.h"
39 #include "s60runconfigbluetoothstarter.h"
40 #include "qt4projectmanagerconstants.h"
41 #include "qtoutputformatter.h"
42 #include "qt4symbiantarget.h"
43
44 #include <utils/qtcassert.h>
45
46 #include <coreplugin/icore.h>
47 #include <coreplugin/progressmanager/progressmanager.h>
48
49 #include <debugger/debuggerengine.h>
50 #include <debugger/debuggerstartparameters.h>
51
52 #include <QtGui/QMessageBox>
53 #include <QtGui/QMainWindow>
54 #include <QtCore/QFileInfo>
55 #include <QtCore/QDateTime>
56 #include <QtCore/QDir>
57
58 #include <QtNetwork/QTcpSocket>
59
60 using namespace ProjectExplorer;
61 using namespace Qt4ProjectManager;
62 using namespace Qt4ProjectManager::Internal;
63
64 namespace {
65
66 const char * const S60_DEVICE_RC_ID("Qt4ProjectManager.S60DeviceRunConfiguration");
67 const char * const S60_DEVICE_RC_PREFIX("Qt4ProjectManager.S60DeviceRunConfiguration.");
68
69 const char * const PRO_FILE_KEY("Qt4ProjectManager.S60DeviceRunConfiguration.ProFile");
70 const char * const COMMUNICATION_TYPE_KEY("Qt4ProjectManager.S60DeviceRunConfiguration.CommunicationType");
71 const char * const COMMAND_LINE_ARGUMENTS_KEY("Qt4ProjectManager.S60DeviceRunConfiguration.CommandLineArguments");
72
73 enum { debug = 0 };
74
75 QString pathFromId(const QString &id)
76 {
77     if (!id.startsWith(QLatin1String(S60_DEVICE_RC_PREFIX)))
78         return QString();
79     return id.mid(QString::fromLatin1(S60_DEVICE_RC_PREFIX).size());
80 }
81
82 QString pathToId(const QString &path)
83 {
84     return QString::fromLatin1(S60_DEVICE_RC_PREFIX) + path;
85 }
86
87 } // anonymous namespace
88
89 // ======== S60DeviceRunConfiguration
90
91 S60DeviceRunConfiguration::S60DeviceRunConfiguration(Qt4BaseTarget *parent, const QString &proFilePath) :
92     RunConfiguration(parent,  QLatin1String(S60_DEVICE_RC_ID)),
93     m_proFilePath(proFilePath),
94     m_validParse(parent->qt4Project()->validParse(proFilePath))
95 {
96     ctor();
97 }
98
99 S60DeviceRunConfiguration::S60DeviceRunConfiguration(Qt4BaseTarget *target, S60DeviceRunConfiguration *source) :
100     RunConfiguration(target, source),
101     m_proFilePath(source->m_proFilePath),
102     m_commandLineArguments(source->m_commandLineArguments),
103     m_validParse(source->m_validParse)
104 {
105     ctor();
106 }
107
108 void S60DeviceRunConfiguration::ctor()
109 {
110     if (!m_proFilePath.isEmpty())
111         //: S60 device runconfiguration default display name, %1 is base pro-File name
112         setDefaultDisplayName(tr("%1 on Symbian Device").arg(QFileInfo(m_proFilePath).completeBaseName()));
113     else
114         //: S60 device runconfiguration default display name (no profile set)
115         setDefaultDisplayName(tr("Run on Symbian device"));
116
117     Qt4Project *pro = qt4Target()->qt4Project();
118     connect(pro, SIGNAL(proFileUpdated(Qt4ProjectManager::Internal::Qt4ProFileNode*,bool)),
119             this, SLOT(proFileUpdate(Qt4ProjectManager::Internal::Qt4ProFileNode*,bool)));
120     connect(pro, SIGNAL(proFileInvalidated(Qt4ProjectManager::Internal::Qt4ProFileNode *)),
121             this, SLOT(proFileInvalidated(Qt4ProjectManager::Internal::Qt4ProFileNode *)));
122 }
123
124 void S60DeviceRunConfiguration::handleParserState(bool success)
125 {
126     bool enabled = isEnabled();
127     m_validParse = success;
128     if (enabled != isEnabled())
129         emit isEnabledChanged(!enabled);
130 }
131
132 void S60DeviceRunConfiguration::proFileInvalidated(Qt4ProjectManager::Internal::Qt4ProFileNode *pro)
133 {
134     if (m_proFilePath != pro->path())
135         return;
136     handleParserState(false);
137 }
138
139 void S60DeviceRunConfiguration::proFileUpdate(Qt4ProjectManager::Internal::Qt4ProFileNode *pro, bool success)
140 {
141     if (m_proFilePath != pro->path())
142         return;
143     handleParserState(success);
144     emit targetInformationChanged();
145 }
146
147 S60DeviceRunConfiguration::~S60DeviceRunConfiguration()
148 {
149 }
150
151 Qt4SymbianTarget *S60DeviceRunConfiguration::qt4Target() const
152 {
153     return static_cast<Qt4SymbianTarget *>(target());
154 }
155
156 bool S60DeviceRunConfiguration::isEnabled(ProjectExplorer::BuildConfiguration *configuration) const
157 {
158     if (!m_validParse)
159         return false;
160
161     Q_ASSERT(configuration->target() == target());
162     Q_ASSERT(target()->id() == Constants::S60_DEVICE_TARGET_ID);
163
164     const Qt4BuildConfiguration *qt4bc = qobject_cast<const Qt4BuildConfiguration *>(configuration);
165     return qt4bc && qt4bc->toolChain();
166 }
167
168 QWidget *S60DeviceRunConfiguration::createConfigurationWidget()
169 {
170     return new S60DeviceRunConfigurationWidget(this);
171 }
172
173 ProjectExplorer::OutputFormatter *S60DeviceRunConfiguration::createOutputFormatter() const
174 {
175     return new QtOutputFormatter(qt4Target()->qt4Project());
176 }
177
178 QVariantMap S60DeviceRunConfiguration::toMap() const
179 {
180     QVariantMap map = ProjectExplorer::RunConfiguration::toMap();
181     const QDir projectDir = QDir(target()->project()->projectDirectory());
182
183     map.insert(QLatin1String(PRO_FILE_KEY), projectDir.relativeFilePath(m_proFilePath));
184     map.insert(QLatin1String(COMMAND_LINE_ARGUMENTS_KEY), m_commandLineArguments);
185
186     return map;
187 }
188
189 bool S60DeviceRunConfiguration::fromMap(const QVariantMap &map)
190 {
191     const QDir projectDir = QDir(target()->project()->projectDirectory());
192
193     m_proFilePath = projectDir.filePath(map.value(QLatin1String(PRO_FILE_KEY)).toString());
194     m_commandLineArguments = map.value(QLatin1String(COMMAND_LINE_ARGUMENTS_KEY)).toString();
195
196     if (m_proFilePath.isEmpty())
197         return false;
198     if (!QFileInfo(m_proFilePath).exists())
199         return false;
200
201     m_validParse = qt4Target()->qt4Project()->validParse(m_proFilePath);
202
203     setDefaultDisplayName(tr("%1 on Symbian Device").arg(QFileInfo(m_proFilePath).completeBaseName()));
204
205     return RunConfiguration::fromMap(map);
206 }
207
208 static inline QString fixBaseNameTarget(const QString &in)
209 {
210     if (in == QLatin1String("udeb"))
211         return QLatin1String("debug");
212     if (in == QLatin1String("urel"))
213         return QLatin1String("release");
214     return in;
215 }
216
217 QString S60DeviceRunConfiguration::targetName() const
218 {
219     TargetInformation ti = qt4Target()->qt4Project()->rootProjectNode()->targetInformation(projectFilePath());
220     if (!ti.valid)
221         return QString();
222     return ti.target;
223 }
224
225 const QtVersion *S60DeviceRunConfiguration::qtVersion() const
226 {
227     if (const BuildConfiguration *bc = target()->activeBuildConfiguration())
228         if (const Qt4BuildConfiguration *qt4bc = qobject_cast<const Qt4BuildConfiguration *>(bc))
229             return qt4bc->qtVersion();
230     return 0;
231 }
232
233 bool S60DeviceRunConfiguration::isDebug() const
234 {
235     const Qt4BuildConfiguration *qt4bc = qt4Target()->activeBuildConfiguration();
236     return (qt4bc->qmakeBuildConfiguration() & QtVersion::DebugBuild);
237 }
238
239 QString S60DeviceRunConfiguration::symbianTarget() const
240 {
241     return isDebug() ? QLatin1String("udeb") : QLatin1String("urel");
242 }
243
244 /* Grep a package file for the '.exe' file. Currently for use on Linux only
245  * as the '.pkg'-files on Windows do not contain drive letters, which is not
246  * handled here. \code
247 ; Executable and default resource files
248 "./foo.exe"    - "!:\sys\bin\foo.exe"
249 \endcode  */
250
251 static inline QString executableFromPackageUnix(const QString &packageFileName)
252 {
253     QFile packageFile(packageFileName);
254     if (!packageFile.open(QIODevice::ReadOnly|QIODevice::Text))
255         return QString();
256     QRegExp pattern(QLatin1String("^\"(.*.exe)\" *- \"!:.*.exe\"$"));
257     QTC_ASSERT(pattern.isValid(), return QString());
258     foreach(const QString &line, QString::fromLocal8Bit(packageFile.readAll()).split(QLatin1Char('\n')))
259         if (pattern.exactMatch(line)) {
260             // Expand relative paths by package file paths
261             QString rc = pattern.cap(1);
262             if (rc.startsWith(QLatin1String("./")))
263                 rc.remove(0, 2);
264             const QFileInfo fi(rc);
265             if (fi.isAbsolute())
266                 return rc;
267             return QFileInfo(packageFileName).absolutePath() + QLatin1Char('/') + rc;
268         }
269     return QString();
270 }
271
272 // ABLD/Raptor: Return executable from device/EPOC
273 static inline QString localExecutableFromVersion(const QtVersion *qtv,
274                                                 const QString &symbianTarget, /* udeb/urel */
275                                                 const QString &targetName,
276                                                 const ProjectExplorer::ToolChain *tc)
277 {
278     Q_ASSERT(qtv);
279     if (!tc)
280         return QString();
281
282     QString localExecutable;
283     QString platform = S60Manager::platform(tc);
284     if (qtv->isBuildWithSymbianSbsV2() && platform == QLatin1String("gcce"))
285         platform = "armv5";
286     QTextStream(&localExecutable) << qtv->systemRoot() << "/epoc32/release/"
287             << platform << '/' << symbianTarget << '/' << targetName << ".exe";
288     return localExecutable;
289 }
290
291 QString S60DeviceRunConfiguration::localExecutableFileName() const
292 {
293     TargetInformation ti = qt4Target()->qt4Project()->rootProjectNode()->targetInformation(projectFilePath());
294     if (!ti.valid)
295         return QString();
296
297     ProjectExplorer::ToolChain *tc = target()->activeBuildConfiguration()->toolChain();
298     return localExecutableFromVersion(qtVersion(), symbianTarget(), targetName(), tc);
299 }
300
301 quint32 S60DeviceRunConfiguration::executableUid() const
302 {
303     quint32 uid = 0;
304     QString executablePath = localExecutableFileName();
305     if (!executablePath.isEmpty()) {
306         QFile file(executablePath);
307         if (file.open(QIODevice::ReadOnly)) {
308             // executable's UID is 4 bytes starting at 8.
309             const QByteArray data = file.read(12);
310             if (data.size() == 12) {
311                 const unsigned char *d = reinterpret_cast<const unsigned char*>(data.data() + 8);
312                 uid = *d++;
313                 uid += *d++ << 8;
314                 uid += *d++ << 16;
315                 uid += *d++ << 24;
316             }
317         }
318     }
319     return uid;
320 }
321
322 QString S60DeviceRunConfiguration::projectFilePath() const
323 {
324     return m_proFilePath;
325 }
326
327 QString S60DeviceRunConfiguration::commandLineArguments() const
328 {
329     return m_commandLineArguments;
330 }
331
332 void S60DeviceRunConfiguration::setCommandLineArguments(const QString &args)
333 {
334     m_commandLineArguments = args;
335 }
336
337 QString S60DeviceRunConfiguration::proFilePath() const
338 {
339     return m_proFilePath;
340 }
341
342 // ======== S60DeviceRunConfigurationFactory
343
344 S60DeviceRunConfigurationFactory::S60DeviceRunConfigurationFactory(QObject *parent) :
345     IRunConfigurationFactory(parent)
346 {
347 }
348
349 S60DeviceRunConfigurationFactory::~S60DeviceRunConfigurationFactory()
350 {
351 }
352
353 QStringList S60DeviceRunConfigurationFactory::availableCreationIds(Target *parent) const
354 {
355     Qt4SymbianTarget *target = qobject_cast<Qt4SymbianTarget *>(parent);
356     if (!target || target->id() != QLatin1String(Constants::S60_DEVICE_TARGET_ID))
357         return QStringList();
358
359     return target->qt4Project()->applicationProFilePathes(QLatin1String(S60_DEVICE_RC_PREFIX));
360 }
361
362 QString S60DeviceRunConfigurationFactory::displayNameForId(const QString &id) const
363 {
364     if (!pathFromId(id).isEmpty())
365         return tr("%1 on Symbian Device").arg(QFileInfo(pathFromId(id)).completeBaseName());
366     return QString();
367 }
368
369 bool S60DeviceRunConfigurationFactory::canCreate(Target *parent, const QString &id) const
370 {
371     Qt4SymbianTarget *t = qobject_cast<Qt4SymbianTarget *>(parent);
372     if (!t || t->id() != QLatin1String(Constants::S60_DEVICE_TARGET_ID))
373         return false;
374     return t->qt4Project()->hasApplicationProFile(pathFromId(id));
375 }
376
377 RunConfiguration *S60DeviceRunConfigurationFactory::create(Target *parent, const QString &id)
378 {
379     if (!canCreate(parent, id))
380         return 0;
381
382     Qt4SymbianTarget *t = static_cast<Qt4SymbianTarget *>(parent);
383     return new S60DeviceRunConfiguration(t, pathFromId(id));
384 }
385
386 bool S60DeviceRunConfigurationFactory::canRestore(Target *parent, const QVariantMap &map) const
387 {
388     Qt4SymbianTarget *t = qobject_cast<Qt4SymbianTarget *>(parent);
389     if (!t || t->id() != QLatin1String(Constants::S60_DEVICE_TARGET_ID))
390         return false;
391     QString id = ProjectExplorer::idFromMap(map);
392     return id == QLatin1String(S60_DEVICE_RC_ID);
393 }
394
395 RunConfiguration *S60DeviceRunConfigurationFactory::restore(Target *parent, const QVariantMap &map)
396 {
397     if (!canRestore(parent, map))
398         return 0;
399     Qt4SymbianTarget *t = static_cast<Qt4SymbianTarget *>(parent);
400     S60DeviceRunConfiguration *rc = new S60DeviceRunConfiguration(t, QString());
401     if (rc->fromMap(map))
402         return rc;
403
404     delete rc;
405     return 0;
406 }
407
408 bool S60DeviceRunConfigurationFactory::canClone(Target *parent, RunConfiguration *source) const
409 {
410     if (!qobject_cast<Qt4SymbianTarget *>(parent))
411         return false;
412     return source->id() == QLatin1String(S60_DEVICE_RC_ID);
413 }
414
415 RunConfiguration *S60DeviceRunConfigurationFactory::clone(Target *parent, RunConfiguration *source)
416 {
417     if (!canClone(parent, source))
418         return 0;
419     Qt4SymbianTarget *t = static_cast<Qt4SymbianTarget *>(parent);
420     S60DeviceRunConfiguration *old = static_cast<S60DeviceRunConfiguration *>(source);
421     return new S60DeviceRunConfiguration(t, old);
422 }
423
424 // ======== S60DeviceDebugRunControl
425
426 // Return symbol file which should co-exist with the executable.
427 // location in debug builds. This can be 'foo.sym' (ABLD) or 'foo.exe.sym' (Raptor)
428 static inline QString symbolFileFromExecutable(const QString &executable)
429 {
430     // 'foo.exe.sym' (Raptor)
431     const QFileInfo raptorSymFi(executable + QLatin1String(".sym"));
432     if (raptorSymFi.isFile())
433         return raptorSymFi.absoluteFilePath();
434     // 'foo.sym' (ABLD)
435     const int lastDotPos = executable.lastIndexOf(QLatin1Char('.'));
436     if (lastDotPos != -1) {
437         const QString symbolFileName = executable.mid(0, lastDotPos) + QLatin1String(".sym");
438         const QFileInfo symbolFileNameFi(symbolFileName);
439         if (symbolFileNameFi.isFile())
440             return symbolFileNameFi.absoluteFilePath();
441     }
442     return QString();
443 }
444
445 // Create start parameters from run configuration
446 static Debugger::DebuggerStartParameters s60DebuggerStartParams(const S60DeviceRunConfiguration *rc)
447 {
448     Debugger::DebuggerStartParameters sp;
449     QTC_ASSERT(rc, return sp);
450
451     const S60DeployConfiguration *activeDeployConf =
452         qobject_cast<S60DeployConfiguration *>(rc->qt4Target()->activeDeployConfiguration());
453
454     const QString debugFileName = QString::fromLatin1("%1:\\sys\\bin\\%2.exe")
455             .arg(activeDeployConf->installationDrive()).arg(rc->targetName());
456
457     sp.remoteChannel = activeDeployConf->serialPortName();
458     sp.processArgs = rc->commandLineArguments();
459     sp.startMode = Debugger::StartInternal;
460     sp.toolChainAbi = rc->abi();
461     sp.executable = debugFileName;
462     sp.executableUid = rc->executableUid();
463     sp.serverAddress = activeDeployConf->deviceAddress();
464     sp.serverPort = activeDeployConf->devicePort().toInt();
465     sp.displayName = rc->displayName();
466
467     sp.communicationChannel = activeDeployConf->communicationChannel() == S60DeployConfiguration::CommunicationCodaTcpConnection?
468                 Debugger::DebuggerStartParameters::CommunicationChannelTcpIp:
469                 Debugger::DebuggerStartParameters::CommunicationChannelUsb;
470
471     sp.debugClient = activeDeployConf->communicationChannel() == S60DeployConfiguration::CommunicationTrkSerialConnection?
472                 Debugger::DebuggerStartParameters::DebugClientTrk:
473                 Debugger::DebuggerStartParameters::DebugClientCoda;
474
475     QTC_ASSERT(sp.executableUid, return sp);
476
477     // Prefer the '*.sym' file over the '.exe', which should exist at the same
478     // location in debug builds. This can be 'foo.exe' (ABLD) or 'foo.exe.sym' (Raptor)
479     sp.symbolFileName = symbolFileFromExecutable(rc->localExecutableFileName());
480     return sp;
481 }
482
483 S60DeviceDebugRunControl::S60DeviceDebugRunControl(S60DeviceRunConfiguration *rc,
484                                                    const Debugger::DebuggerStartParameters &sp,
485                                                    const QPair<Debugger::DebuggerEngineType, Debugger::DebuggerEngineType> &masterSlaveEngineTypes) :
486     Debugger::DebuggerRunControl(rc, sp, masterSlaveEngineTypes)
487 {
488     if (startParameters().symbolFileName.isEmpty()) {
489         const QString msg = tr("Warning: Cannot locate the symbol file belonging to %1.").
490                                arg(rc->localExecutableFileName());
491         appendMessage(msg, ErrorMessageFormat);
492     }
493 }
494
495 void S60DeviceDebugRunControl::start()
496 {
497     appendMessage(tr("Launching debugger..."), NormalMessageFormat);
498     Debugger::DebuggerRunControl::start();
499 }
500
501 bool S60DeviceDebugRunControl::promptToStop(bool *) const
502 {
503     // We override the settings prompt
504     return Debugger::DebuggerRunControl::promptToStop(0);
505 }
506
507 S60DeviceDebugRunControlFactory::S60DeviceDebugRunControlFactory(QObject *parent) :
508     IRunControlFactory(parent)
509 {
510 }
511
512 bool S60DeviceDebugRunControlFactory::canRun(ProjectExplorer::RunConfiguration *runConfiguration, const QString &mode) const
513 {
514     return mode == QLatin1String(Debugger::Constants::DEBUGMODE)
515             && qobject_cast<S60DeviceRunConfiguration *>(runConfiguration) != 0;
516 }
517
518 ProjectExplorer::RunControl* S60DeviceDebugRunControlFactory::create(ProjectExplorer::RunConfiguration *runConfiguration, const QString &mode)
519 {
520     S60DeviceRunConfiguration *rc = qobject_cast<S60DeviceRunConfiguration *>(runConfiguration);
521     QTC_ASSERT(rc && mode == QLatin1String(Debugger::Constants::DEBUGMODE), return 0);
522     const Debugger::DebuggerStartParameters startParameters = s60DebuggerStartParams(rc);
523     const Debugger::ConfigurationCheck check = Debugger::checkDebugConfiguration(startParameters);
524     if (!check) {
525         Core::ICore::instance()->showWarningWithOptions(S60DeviceDebugRunControl::tr("Debugger for Symbian Platform"),
526             check.errorMessage, check.errorDetailsString(), check.settingsCategory, check.settingsPage);
527         return 0;
528     }
529     return new S60DeviceDebugRunControl(rc, startParameters, check.masterSlaveEngineTypes);
530 }
531
532 QString S60DeviceDebugRunControlFactory::displayName() const
533 {
534     return S60DeviceDebugRunControl::tr("Debug on Device");
535 }
536
537 ProjectExplorer::RunConfigWidget *S60DeviceDebugRunControlFactory::createConfigurationWidget(RunConfiguration* /*runConfiguration */)
538 {
539     return 0;
540 }