1 /**************************************************************************
3 ** This file is part of Qt Creator
5 ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
7 ** Contact: Nokia Corporation (info@qt.nokia.com)
10 ** GNU Lesser General Public License Usage
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.
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.
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.
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
31 **************************************************************************/
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"
44 #include <utils/qtcassert.h>
46 #include <coreplugin/icore.h>
47 #include <coreplugin/progressmanager/progressmanager.h>
49 #include <debugger/debuggerengine.h>
50 #include <debugger/debuggerstartparameters.h>
52 #include <QtGui/QMessageBox>
53 #include <QtGui/QMainWindow>
54 #include <QtCore/QFileInfo>
55 #include <QtCore/QDateTime>
56 #include <QtCore/QDir>
58 #include <QtNetwork/QTcpSocket>
60 using namespace ProjectExplorer;
61 using namespace Qt4ProjectManager;
62 using namespace Qt4ProjectManager::Internal;
66 const char * const S60_DEVICE_RC_ID("Qt4ProjectManager.S60DeviceRunConfiguration");
67 const char * const S60_DEVICE_RC_PREFIX("Qt4ProjectManager.S60DeviceRunConfiguration.");
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");
75 QString pathFromId(const QString &id)
77 if (!id.startsWith(QLatin1String(S60_DEVICE_RC_PREFIX)))
79 return id.mid(QString::fromLatin1(S60_DEVICE_RC_PREFIX).size());
82 QString pathToId(const QString &path)
84 return QString::fromLatin1(S60_DEVICE_RC_PREFIX) + path;
87 } // anonymous namespace
89 // ======== S60DeviceRunConfiguration
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))
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)
108 void S60DeviceRunConfiguration::ctor()
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()));
114 //: S60 device runconfiguration default display name (no profile set)
115 setDefaultDisplayName(tr("Run on Symbian device"));
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 *)));
124 void S60DeviceRunConfiguration::handleParserState(bool success)
126 bool enabled = isEnabled();
127 m_validParse = success;
128 if (enabled != isEnabled())
129 emit isEnabledChanged(!enabled);
132 void S60DeviceRunConfiguration::proFileInvalidated(Qt4ProjectManager::Internal::Qt4ProFileNode *pro)
134 if (m_proFilePath != pro->path())
136 handleParserState(false);
139 void S60DeviceRunConfiguration::proFileUpdate(Qt4ProjectManager::Internal::Qt4ProFileNode *pro, bool success)
141 if (m_proFilePath != pro->path())
143 handleParserState(success);
144 emit targetInformationChanged();
147 S60DeviceRunConfiguration::~S60DeviceRunConfiguration()
151 Qt4SymbianTarget *S60DeviceRunConfiguration::qt4Target() const
153 return static_cast<Qt4SymbianTarget *>(target());
156 bool S60DeviceRunConfiguration::isEnabled(ProjectExplorer::BuildConfiguration *configuration) const
161 Q_ASSERT(configuration->target() == target());
162 Q_ASSERT(target()->id() == Constants::S60_DEVICE_TARGET_ID);
164 const Qt4BuildConfiguration *qt4bc = qobject_cast<const Qt4BuildConfiguration *>(configuration);
165 return qt4bc && qt4bc->toolChain();
168 QWidget *S60DeviceRunConfiguration::createConfigurationWidget()
170 return new S60DeviceRunConfigurationWidget(this);
173 ProjectExplorer::OutputFormatter *S60DeviceRunConfiguration::createOutputFormatter() const
175 return new QtOutputFormatter(qt4Target()->qt4Project());
178 QVariantMap S60DeviceRunConfiguration::toMap() const
180 QVariantMap map = ProjectExplorer::RunConfiguration::toMap();
181 const QDir projectDir = QDir(target()->project()->projectDirectory());
183 map.insert(QLatin1String(PRO_FILE_KEY), projectDir.relativeFilePath(m_proFilePath));
184 map.insert(QLatin1String(COMMAND_LINE_ARGUMENTS_KEY), m_commandLineArguments);
189 bool S60DeviceRunConfiguration::fromMap(const QVariantMap &map)
191 const QDir projectDir = QDir(target()->project()->projectDirectory());
193 m_proFilePath = projectDir.filePath(map.value(QLatin1String(PRO_FILE_KEY)).toString());
194 m_commandLineArguments = map.value(QLatin1String(COMMAND_LINE_ARGUMENTS_KEY)).toString();
196 if (m_proFilePath.isEmpty())
198 if (!QFileInfo(m_proFilePath).exists())
201 m_validParse = qt4Target()->qt4Project()->validParse(m_proFilePath);
203 setDefaultDisplayName(tr("%1 on Symbian Device").arg(QFileInfo(m_proFilePath).completeBaseName()));
205 return RunConfiguration::fromMap(map);
208 static inline QString fixBaseNameTarget(const QString &in)
210 if (in == QLatin1String("udeb"))
211 return QLatin1String("debug");
212 if (in == QLatin1String("urel"))
213 return QLatin1String("release");
217 QString S60DeviceRunConfiguration::targetName() const
219 TargetInformation ti = qt4Target()->qt4Project()->rootProjectNode()->targetInformation(projectFilePath());
225 const QtVersion *S60DeviceRunConfiguration::qtVersion() const
227 if (const BuildConfiguration *bc = target()->activeBuildConfiguration())
228 if (const Qt4BuildConfiguration *qt4bc = qobject_cast<const Qt4BuildConfiguration *>(bc))
229 return qt4bc->qtVersion();
233 bool S60DeviceRunConfiguration::isDebug() const
235 const Qt4BuildConfiguration *qt4bc = qt4Target()->activeBuildConfiguration();
236 return (qt4bc->qmakeBuildConfiguration() & QtVersion::DebugBuild);
239 QString S60DeviceRunConfiguration::symbianTarget() const
241 return isDebug() ? QLatin1String("udeb") : QLatin1String("urel");
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"
251 static inline QString executableFromPackageUnix(const QString &packageFileName)
253 QFile packageFile(packageFileName);
254 if (!packageFile.open(QIODevice::ReadOnly|QIODevice::Text))
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("./")))
264 const QFileInfo fi(rc);
267 return QFileInfo(packageFileName).absolutePath() + QLatin1Char('/') + rc;
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)
282 QString localExecutable;
283 QString platform = S60Manager::platform(tc);
284 if (qtv->isBuildWithSymbianSbsV2() && platform == QLatin1String("gcce"))
286 QTextStream(&localExecutable) << qtv->systemRoot() << "/epoc32/release/"
287 << platform << '/' << symbianTarget << '/' << targetName << ".exe";
288 return localExecutable;
291 QString S60DeviceRunConfiguration::localExecutableFileName() const
293 TargetInformation ti = qt4Target()->qt4Project()->rootProjectNode()->targetInformation(projectFilePath());
297 ProjectExplorer::ToolChain *tc = target()->activeBuildConfiguration()->toolChain();
298 return localExecutableFromVersion(qtVersion(), symbianTarget(), targetName(), tc);
301 quint32 S60DeviceRunConfiguration::executableUid() const
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);
322 QString S60DeviceRunConfiguration::projectFilePath() const
324 return m_proFilePath;
327 QString S60DeviceRunConfiguration::commandLineArguments() const
329 return m_commandLineArguments;
332 void S60DeviceRunConfiguration::setCommandLineArguments(const QString &args)
334 m_commandLineArguments = args;
337 QString S60DeviceRunConfiguration::proFilePath() const
339 return m_proFilePath;
342 // ======== S60DeviceRunConfigurationFactory
344 S60DeviceRunConfigurationFactory::S60DeviceRunConfigurationFactory(QObject *parent) :
345 IRunConfigurationFactory(parent)
349 S60DeviceRunConfigurationFactory::~S60DeviceRunConfigurationFactory()
353 QStringList S60DeviceRunConfigurationFactory::availableCreationIds(Target *parent) const
355 Qt4SymbianTarget *target = qobject_cast<Qt4SymbianTarget *>(parent);
356 if (!target || target->id() != QLatin1String(Constants::S60_DEVICE_TARGET_ID))
357 return QStringList();
359 return target->qt4Project()->applicationProFilePathes(QLatin1String(S60_DEVICE_RC_PREFIX));
362 QString S60DeviceRunConfigurationFactory::displayNameForId(const QString &id) const
364 if (!pathFromId(id).isEmpty())
365 return tr("%1 on Symbian Device").arg(QFileInfo(pathFromId(id)).completeBaseName());
369 bool S60DeviceRunConfigurationFactory::canCreate(Target *parent, const QString &id) const
371 Qt4SymbianTarget *t = qobject_cast<Qt4SymbianTarget *>(parent);
372 if (!t || t->id() != QLatin1String(Constants::S60_DEVICE_TARGET_ID))
374 return t->qt4Project()->hasApplicationProFile(pathFromId(id));
377 RunConfiguration *S60DeviceRunConfigurationFactory::create(Target *parent, const QString &id)
379 if (!canCreate(parent, id))
382 Qt4SymbianTarget *t = static_cast<Qt4SymbianTarget *>(parent);
383 return new S60DeviceRunConfiguration(t, pathFromId(id));
386 bool S60DeviceRunConfigurationFactory::canRestore(Target *parent, const QVariantMap &map) const
388 Qt4SymbianTarget *t = qobject_cast<Qt4SymbianTarget *>(parent);
389 if (!t || t->id() != QLatin1String(Constants::S60_DEVICE_TARGET_ID))
391 QString id = ProjectExplorer::idFromMap(map);
392 return id == QLatin1String(S60_DEVICE_RC_ID);
395 RunConfiguration *S60DeviceRunConfigurationFactory::restore(Target *parent, const QVariantMap &map)
397 if (!canRestore(parent, map))
399 Qt4SymbianTarget *t = static_cast<Qt4SymbianTarget *>(parent);
400 S60DeviceRunConfiguration *rc = new S60DeviceRunConfiguration(t, QString());
401 if (rc->fromMap(map))
408 bool S60DeviceRunConfigurationFactory::canClone(Target *parent, RunConfiguration *source) const
410 if (!qobject_cast<Qt4SymbianTarget *>(parent))
412 return source->id() == QLatin1String(S60_DEVICE_RC_ID);
415 RunConfiguration *S60DeviceRunConfigurationFactory::clone(Target *parent, RunConfiguration *source)
417 if (!canClone(parent, source))
419 Qt4SymbianTarget *t = static_cast<Qt4SymbianTarget *>(parent);
420 S60DeviceRunConfiguration *old = static_cast<S60DeviceRunConfiguration *>(source);
421 return new S60DeviceRunConfiguration(t, old);
424 // ======== S60DeviceDebugRunControl
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)
430 // 'foo.exe.sym' (Raptor)
431 const QFileInfo raptorSymFi(executable + QLatin1String(".sym"));
432 if (raptorSymFi.isFile())
433 return raptorSymFi.absoluteFilePath();
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();
445 // Create start parameters from run configuration
446 static Debugger::DebuggerStartParameters s60DebuggerStartParams(const S60DeviceRunConfiguration *rc)
448 Debugger::DebuggerStartParameters sp;
449 QTC_ASSERT(rc, return sp);
451 const S60DeployConfiguration *activeDeployConf =
452 qobject_cast<S60DeployConfiguration *>(rc->qt4Target()->activeDeployConfiguration());
454 const QString debugFileName = QString::fromLatin1("%1:\\sys\\bin\\%2.exe")
455 .arg(activeDeployConf->installationDrive()).arg(rc->targetName());
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();
467 sp.communicationChannel = activeDeployConf->communicationChannel() == S60DeployConfiguration::CommunicationCodaTcpConnection?
468 Debugger::DebuggerStartParameters::CommunicationChannelTcpIp:
469 Debugger::DebuggerStartParameters::CommunicationChannelUsb;
471 sp.debugClient = activeDeployConf->communicationChannel() == S60DeployConfiguration::CommunicationTrkSerialConnection?
472 Debugger::DebuggerStartParameters::DebugClientTrk:
473 Debugger::DebuggerStartParameters::DebugClientCoda;
475 QTC_ASSERT(sp.executableUid, return sp);
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());
483 S60DeviceDebugRunControl::S60DeviceDebugRunControl(S60DeviceRunConfiguration *rc,
484 const Debugger::DebuggerStartParameters &sp,
485 const QPair<Debugger::DebuggerEngineType, Debugger::DebuggerEngineType> &masterSlaveEngineTypes) :
486 Debugger::DebuggerRunControl(rc, sp, masterSlaveEngineTypes)
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);
495 void S60DeviceDebugRunControl::start()
497 appendMessage(tr("Launching debugger..."), NormalMessageFormat);
498 Debugger::DebuggerRunControl::start();
501 bool S60DeviceDebugRunControl::promptToStop(bool *) const
503 // We override the settings prompt
504 return Debugger::DebuggerRunControl::promptToStop(0);
507 S60DeviceDebugRunControlFactory::S60DeviceDebugRunControlFactory(QObject *parent) :
508 IRunControlFactory(parent)
512 bool S60DeviceDebugRunControlFactory::canRun(ProjectExplorer::RunConfiguration *runConfiguration, const QString &mode) const
514 return mode == QLatin1String(Debugger::Constants::DEBUGMODE)
515 && qobject_cast<S60DeviceRunConfiguration *>(runConfiguration) != 0;
518 ProjectExplorer::RunControl* S60DeviceDebugRunControlFactory::create(ProjectExplorer::RunConfiguration *runConfiguration, const QString &mode)
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);
525 Core::ICore::instance()->showWarningWithOptions(S60DeviceDebugRunControl::tr("Debugger for Symbian Platform"),
526 check.errorMessage, check.errorDetailsString(), check.settingsCategory, check.settingsPage);
529 return new S60DeviceDebugRunControl(rc, startParameters, check.masterSlaveEngineTypes);
532 QString S60DeviceDebugRunControlFactory::displayName() const
534 return S60DeviceDebugRunControl::tr("Debug on Device");
537 ProjectExplorer::RunConfigWidget *S60DeviceDebugRunControlFactory::createConfigurationWidget(RunConfiguration* /*runConfiguration */)