1 /**************************************************************************
3 ** This file is part of Qt Creator
5 ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
7 ** Contact: Nokia Corporation (qt-info@nokia.com)
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
16 ** GNU Lesser General Public License Usage
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.
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.
29 ** If you have questions regarding the use of this file, please contact
30 ** Nokia at qt-info@nokia.com.
32 **************************************************************************/
34 #include "maemodeploystep.h"
36 #include "maemoconstants.h"
37 #include "maemodeploystepwidget.h"
38 #include "maemoglobal.h"
39 #include "maemopackagecreationstep.h"
40 #include "maemopertargetdeviceconfigurationlistmodel.h"
41 #include "maemoqemumanager.h"
42 #include "maemoremotemounter.h"
43 #include "maemorunconfiguration.h"
44 #include "maemotoolchain.h"
45 #include "maemousedportsgatherer.h"
46 #include "qt4maemotarget.h"
48 #include <utils/ssh/sftpchannel.h>
49 #include <utils/ssh/sshconnection.h>
50 #include <utils/ssh/sshremoteprocess.h>
52 #include <projectexplorer/buildconfiguration.h>
53 #include <projectexplorer/projectexplorerconstants.h>
54 #include <projectexplorer/target.h>
56 #include <qt4projectmanager/qt4buildconfiguration.h>
57 #include <qt4projectmanager/qt4projectmanagerconstants.h>
58 #include <qt4projectmanager/qt4target.h>
60 #include <QtCore/QCoreApplication>
61 #include <QtCore/QDir>
62 #include <QtCore/QEventLoop>
63 #include <QtCore/QFileInfo>
64 #include <QtCore/QTimer>
66 #define ASSERT_STATE(state) ASSERT_STATE_GENERIC(State, state, m_state)
69 using namespace Utils;
70 using namespace ProjectExplorer;
72 namespace Qt4ProjectManager {
74 namespace { const int DefaultMountPort = 1050; }
76 const QLatin1String MaemoDeployStep::Id("Qt4ProjectManager.MaemoDeployStep");
78 MaemoDeployStep::MaemoDeployStep(ProjectExplorer::BuildStepList *parent)
79 : BuildStep(parent, Id)
84 MaemoDeployStep::MaemoDeployStep(ProjectExplorer::BuildStepList *parent,
85 MaemoDeployStep *other)
86 : BuildStep(parent, other), m_lastDeployed(other->m_lastDeployed)
91 MaemoDeployStep::~MaemoDeployStep() { }
93 void MaemoDeployStep::ctor()
95 //: MaemoDeployStep default display name
96 if (target()->id() == QLatin1String(Constants::MAEMO5_DEVICE_TARGET_ID))
97 setDefaultDisplayName(tr("Deploy to Maemo5 device"));
98 else if (target()->id() == QLatin1String(Constants::HARMATTAN_DEVICE_TARGET_ID))
99 setDefaultDisplayName(tr("Deploy to Harmattan device"));
100 else if (target()->id() == QLatin1String(Constants::MEEGO_DEVICE_TARGET_ID))
101 setDefaultDisplayName(tr("Deploy to Meego device"));
103 // A MaemoDeployables object is only dependent on the active build
104 // configuration and therefore can (and should) be shared among all
106 const QList<DeployConfiguration *> &deployConfigs
107 = target()->deployConfigurations();
108 if (deployConfigs.isEmpty()) {
109 const AbstractQt4MaemoTarget * const qt4Target = qobject_cast<AbstractQt4MaemoTarget *>(target());
111 m_deployables = QSharedPointer<MaemoDeployables>(new MaemoDeployables(qt4Target));
113 const MaemoDeployStep *const other
114 = MaemoGlobal::buildStep<MaemoDeployStep>(deployConfigs.first());
115 m_deployables = other->deployables();
119 m_deviceConfig = maemotarget()->deviceConfigurationsModel()->defaultDeviceConfig();
120 m_needsInstall = false;
121 m_sysrootInstaller = new QProcess(this);
122 connect(m_sysrootInstaller, SIGNAL(finished(int,QProcess::ExitStatus)),
123 this, SLOT(handleSysrootInstallerFinished()));
124 connect(m_sysrootInstaller, SIGNAL(readyReadStandardOutput()), this,
125 SLOT(handleSysrootInstallerOutput()));
126 connect(m_sysrootInstaller, SIGNAL(readyReadStandardError()), this,
127 SLOT(handleSysrootInstallerErrorOutput()));
128 m_mounter = new MaemoRemoteMounter(this);
129 connect(m_mounter, SIGNAL(mounted()), this, SLOT(handleMounted()));
130 connect(m_mounter, SIGNAL(unmounted()), this, SLOT(handleUnmounted()));
131 connect(m_mounter, SIGNAL(error(QString)), this,
132 SLOT(handleMountError(QString)));
133 connect(m_mounter, SIGNAL(reportProgress(QString)), this,
134 SLOT(handleProgressReport(QString)));
135 connect(m_mounter, SIGNAL(debugOutput(QString)), this,
136 SLOT(handleMountDebugOutput(QString)));
137 m_portsGatherer = new MaemoUsedPortsGatherer(this);
138 connect(m_portsGatherer, SIGNAL(error(QString)), this,
139 SLOT(handlePortsGathererError(QString)));
140 connect(m_portsGatherer, SIGNAL(portListReady()), this,
141 SLOT(handlePortListReady()));
142 connect(maemotarget()->deviceConfigurationsModel(), SIGNAL(updated()),
143 SLOT(handleDeviceConfigurationsUpdated()));
146 bool MaemoDeployStep::init()
151 void MaemoDeployStep::run(QFutureInterface<bool> &fi)
153 // Move to GUI thread for connection sharing with run control.
154 QTimer::singleShot(0, this, SLOT(start()));
156 MaemoDeployEventHandler eventHandler(this, fi);
159 BuildStepConfigWidget *MaemoDeployStep::createConfigWidget()
161 return new MaemoDeployStepWidget(this);
164 QVariantMap MaemoDeployStep::toMap() const
166 QVariantMap map(BuildStep::toMap());
167 addDeployTimesToMap(map);
168 map.insert(DeployToSysrootKey, m_deployToSysroot);
169 map.insert(DeviceIdKey,
170 MaemoDeviceConfigurations::instance()->internalId(m_deviceConfig));
174 void MaemoDeployStep::addDeployTimesToMap(QVariantMap &map) const
176 QVariantList hostList;
177 QVariantList fileList;
178 QVariantList remotePathList;
179 QVariantList timeList;
180 typedef QHash<DeployablePerHost, QDateTime>::ConstIterator DepIt;
181 for (DepIt it = m_lastDeployed.begin(); it != m_lastDeployed.end(); ++it) {
182 fileList << it.key().first.localFilePath;
183 remotePathList << it.key().first.remoteDir;
184 hostList << it.key().second;
185 timeList << it.value();
187 map.insert(LastDeployedHostsKey, hostList);
188 map.insert(LastDeployedFilesKey, fileList);
189 map.insert(LastDeployedRemotePathsKey, remotePathList);
190 map.insert(LastDeployedTimesKey, timeList);
193 bool MaemoDeployStep::fromMap(const QVariantMap &map)
195 if (!BuildStep::fromMap(map))
197 getDeployTimesFromMap(map);
198 setDeviceConfig(map.value(DeviceIdKey, MaemoDeviceConfig::InvalidId).toULongLong());
199 m_deployToSysroot = map.value(DeployToSysrootKey, true).toBool();
203 void MaemoDeployStep::getDeployTimesFromMap(const QVariantMap &map)
205 const QVariantList &hostList = map.value(LastDeployedHostsKey).toList();
206 const QVariantList &fileList = map.value(LastDeployedFilesKey).toList();
207 const QVariantList &remotePathList
208 = map.value(LastDeployedRemotePathsKey).toList();
209 const QVariantList &timeList = map.value(LastDeployedTimesKey).toList();
211 = qMin(qMin(hostList.size(), fileList.size()),
212 qMin(remotePathList.size(), timeList.size()));
213 for (int i = 0; i < elemCount; ++i) {
214 const MaemoDeployable d(fileList.at(i).toString(),
215 remotePathList.at(i).toString());
216 m_lastDeployed.insert(DeployablePerHost(d, hostList.at(i).toString()),
217 timeList.at(i).toDateTime());
221 const MaemoPackageCreationStep *MaemoDeployStep::packagingStep() const
223 const MaemoPackageCreationStep * const step
224 = MaemoGlobal::buildStep<MaemoPackageCreationStep>(target()->activeDeployConfiguration());
225 Q_ASSERT_X(step, Q_FUNC_INFO,
226 "Impossible: Maemo build configuration without packaging step.");
230 void MaemoDeployStep::raiseError(const QString &errorString)
232 emit addTask(Task(Task::Error, errorString, QString(), -1,
233 ProjectExplorer::Constants::TASK_CATEGORY_BUILDSYSTEM));
238 void MaemoDeployStep::writeOutput(const QString &text, OutputFormat format)
240 emit addOutput(text, format);
243 void MaemoDeployStep::stop()
245 if (m_state == StopRequested || m_state == Inactive)
248 const State oldState = m_state;
249 setState(StopRequested);
251 case InstallingToSysroot:
253 m_sysrootInstaller->terminate();
256 m_connection->disconnectFromHost();
259 case InstallingToDevice:
261 const QByteArray programToKill = oldState == CopyingFile
263 const QByteArray killCommand
264 = MaemoGlobal::remoteSudo().toUtf8() + " pkill -f ";
265 const QByteArray cmdLine = killCommand + programToKill + "; sleep 1; "
266 + killCommand + "-9 " + programToKill;
267 SshRemoteProcess::Ptr killProc
268 = m_connection->createRemoteProcess(cmdLine);
273 m_uploader->closeChannel();
275 case UnmountingOldDirs:
276 case UnmountingCurrentDirs:
277 case UnmountingCurrentMounts:
280 case InitializingSftp:
281 break; // Nothing to do here.
283 Q_ASSERT_X(false, Q_FUNC_INFO, "Missing switch case.");
287 QString MaemoDeployStep::uploadDir() const
289 return MaemoGlobal::homeDirOnDevice(m_connection->connectionParameters().userName);
292 bool MaemoDeployStep::currentlyNeedsDeployment(const QString &host,
293 const MaemoDeployable &deployable) const
295 const QDateTime &lastDeployed
296 = m_lastDeployed.value(DeployablePerHost(deployable, host));
297 return !lastDeployed.isValid()
298 || QFileInfo(deployable.localFilePath).lastModified() > lastDeployed;
301 void MaemoDeployStep::setDeployed(const QString &host,
302 const MaemoDeployable &deployable)
304 m_lastDeployed.insert(DeployablePerHost(deployable, host),
305 QDateTime::currentDateTime());
308 void MaemoDeployStep::handleDeviceConfigurationsUpdated()
310 setDeviceConfig(MaemoDeviceConfigurations::instance()->internalId(m_deviceConfig));
313 void MaemoDeployStep::setDeviceConfig(MaemoDeviceConfig::Id internalId)
315 m_deviceConfig = maemotarget()->deviceConfigurationsModel()->find(internalId);
316 emit deviceConfigChanged();
319 void MaemoDeployStep::setDeviceConfig(int i)
321 m_deviceConfig = maemotarget()->deviceConfigurationsModel()->deviceAt(i);
322 emit deviceConfigChanged();
325 void MaemoDeployStep::start()
327 if (m_state != Inactive) {
328 raiseError(tr("Cannot deploy: Still cleaning up from last time."));
333 m_cachedDeviceConfig = m_deviceConfig;
334 if (!m_cachedDeviceConfig) {
335 raiseError(tr("Deployment failed: No valid device set."));
340 Q_ASSERT(!m_currentDeviceDeployAction);
341 Q_ASSERT(!m_needsInstall);
342 Q_ASSERT(m_filesToCopy.isEmpty());
343 m_installerStderr.clear();
345 const MaemoPackageCreationStep * const pStep = packagingStep();
346 const QString hostName = m_cachedDeviceConfig->sshParameters().host;
347 if (pStep->isPackagingEnabled()) {
348 const MaemoDeployable d(pStep->packageFilePath(), QString());
349 if (currentlyNeedsDeployment(hostName, d))
350 m_needsInstall = true;
352 const int deployableCount = m_deployables->deployableCount();
353 for (int i = 0; i < deployableCount; ++i) {
354 const MaemoDeployable &d = m_deployables->deployableAt(i);
355 if (currentlyNeedsDeployment(hostName, d)
356 || QFileInfo(d.localFilePath).isDir()) {
362 if (m_needsInstall || !m_filesToCopy.isEmpty()) {
363 if (m_cachedDeviceConfig->type() == MaemoDeviceConfig::Emulator
364 && !MaemoQemuManager::instance().qemuIsRunning()) {
365 MaemoQemuManager::instance().startRuntime();
366 raiseError(tr("Deployment failed: Qemu was not running. "
367 "It has now been started up for you, but it will take "
368 "a bit of time until it is ready."));
369 m_needsInstall = false;
370 m_filesToCopy.clear();
375 if (m_deployToSysroot)
380 writeOutput(tr("All files up to date, no installation necessary."));
385 void MaemoDeployStep::handleConnectionFailure()
387 if (m_state == Inactive)
390 const QString errorMsg = m_state == Connecting
391 ? MaemoGlobal::failedToConnectToServerMessage(m_connection, m_cachedDeviceConfig)
392 : tr("Connection error: %1").arg(m_connection->errorString());
393 raiseError(errorMsg);
397 void MaemoDeployStep::handleSftpChannelInitialized()
399 ASSERT_STATE(QList<State>() << InitializingSftp << StopRequested);
402 case InitializingSftp: {
403 const QString filePath = packagingStep()->packageFilePath();
404 const QString filePathNative = QDir::toNativeSeparators(filePath);
405 const QString fileName = QFileInfo(filePath).fileName();
406 const QString remoteFilePath = uploadDir() + QLatin1Char('/') + fileName;
407 const SftpJobId job = m_uploader->uploadFile(filePath,
408 remoteFilePath, SftpOverwriteExisting);
409 if (job == SftpInvalidJob) {
410 raiseError(tr("Upload failed: Could not open file '%1'")
411 .arg(filePathNative));
415 writeOutput(tr("Started uploading file '%1'.").arg(filePathNative));
427 void MaemoDeployStep::handleSftpChannelInitializationFailed(const QString &error)
429 ASSERT_STATE(QList<State>() << InitializingSftp << StopRequested);
432 case InitializingSftp:
434 raiseError(tr("Could not set up SFTP connection: %1").arg(error));
442 void MaemoDeployStep::handleSftpJobFinished(Utils::SftpJobId,
443 const QString &error)
445 ASSERT_STATE(QList<State>() << Uploading << StopRequested);
447 const QString filePathNative
448 = QDir::toNativeSeparators(packagingStep()->packageFilePath());
449 if (!error.isEmpty()) {
450 raiseError(tr("Failed to upload file %1: %2")
451 .arg(filePathNative, error));
452 if (m_state == Uploading)
454 } else if (m_state == Uploading) {
455 writeOutput(tr("Successfully uploaded file '%1'.")
456 .arg(filePathNative));
457 const QString remoteFilePath
458 = uploadDir() + QLatin1Char('/') + QFileInfo(filePathNative).fileName();
459 runPackageInstaller(remoteFilePath);
463 void MaemoDeployStep::handleSftpChannelClosed()
465 ASSERT_STATE(StopRequested);
469 void MaemoDeployStep::handleMounted()
471 ASSERT_STATE(QList<State>() << Mounting << StopRequested << Inactive);
475 if (m_needsInstall) {
476 const QString remoteFilePath = deployMountPoint() + QLatin1Char('/')
477 + QFileInfo(packagingStep()->packageFilePath()).fileName();
478 runPackageInstaller(remoteFilePath);
480 setState(CopyingFile);
481 copyNextFileToDevice();
493 void MaemoDeployStep::handleUnmounted()
495 ASSERT_STATE(QList<State>() << UnmountingOldDirs << UnmountingCurrentDirs
496 << UnmountingCurrentMounts << StopRequested << Inactive);
500 m_mounter->resetMountSpecifications();
503 case UnmountingOldDirs:
504 if (maemotarget()->allowsRemoteMounts())
507 prepareSftpConnection();
509 case UnmountingCurrentDirs:
510 setState(GatheringPorts);
511 m_portsGatherer->start(m_connection, freePorts());
513 case UnmountingCurrentMounts:
515 writeOutput(tr("Deployment failed."), ErrorMessageOutput);
517 writeOutput(tr("Deployment finished."));
526 void MaemoDeployStep::handleMountError(const QString &errorMsg)
528 ASSERT_STATE(QList<State>() << UnmountingOldDirs << UnmountingCurrentDirs
529 << UnmountingCurrentMounts << Mounting << StopRequested << Inactive);
532 case UnmountingOldDirs:
533 case UnmountingCurrentDirs:
534 case UnmountingCurrentMounts:
537 raiseError(errorMsg);
546 void MaemoDeployStep::handleMountDebugOutput(const QString &output)
548 ASSERT_STATE(QList<State>() << UnmountingOldDirs << UnmountingCurrentDirs
549 << UnmountingCurrentMounts << Mounting << StopRequested << Inactive);
552 case UnmountingOldDirs:
553 case UnmountingCurrentDirs:
554 case UnmountingCurrentMounts:
557 writeOutput(output, ErrorOutput);
565 void MaemoDeployStep::setupMount()
567 ASSERT_STATE(UnmountingOldDirs);
568 setState(UnmountingCurrentDirs);
570 Q_ASSERT(m_needsInstall || !m_filesToCopy.isEmpty());
571 m_mounter->resetMountSpecifications();
572 m_mounter->setBuildConfiguration(qt4BuildConfiguration());
573 if (m_needsInstall) {
574 const QString localDir
575 = QFileInfo(packagingStep()->packageFilePath()).absolutePath();
576 const MaemoMountSpecification mountSpec(localDir, deployMountPoint());
577 m_mounter->addMountSpecification(mountSpec, true);
580 bool drivesToMount[26];
581 qFill(drivesToMount, drivesToMount + sizeof drivesToMount / sizeof drivesToMount[0], false);
582 for (int i = 0; i < m_filesToCopy.count(); ++i) {
583 const QString localDir
584 = QFileInfo(m_filesToCopy.at(i).localFilePath).canonicalPath();
585 const char driveLetter = localDir.at(0).toLower().toLatin1();
586 if (driveLetter < 'a' || driveLetter > 'z') {
587 qWarning("Weird: drive letter is '%c'.", driveLetter);
591 const int index = driveLetter - 'a';
592 if (drivesToMount[index])
595 const QString mountPoint = deployMountPoint() + QLatin1Char('/')
596 + QLatin1Char(driveLetter);
597 const MaemoMountSpecification mountSpec(localDir.left(3),
599 m_mounter->addMountSpecification(mountSpec, true);
600 drivesToMount[index] = true;
603 m_mounter->addMountSpecification(MaemoMountSpecification(QLatin1String("/"),
604 deployMountPoint()), true);
610 void MaemoDeployStep::prepareSftpConnection()
612 setState(InitializingSftp);
613 m_uploader = m_connection->createSftpChannel();
614 connect(m_uploader.data(), SIGNAL(initialized()), this,
615 SLOT(handleSftpChannelInitialized()));
616 connect(m_uploader.data(), SIGNAL(initializationFailed(QString)), this,
617 SLOT(handleSftpChannelInitializationFailed(QString)));
618 connect(m_uploader.data(), SIGNAL(finished(Utils::SftpJobId, QString)),
619 this, SLOT(handleSftpJobFinished(Utils::SftpJobId, QString)));
620 connect(m_uploader.data(), SIGNAL(closed()), this,
621 SLOT(handleSftpChannelClosed()));
622 m_uploader->initialize();
625 void MaemoDeployStep::installToSysroot()
627 ASSERT_STATE(Inactive);
628 setState(InstallingToSysroot);
630 if (m_needsInstall) {
631 writeOutput(tr("Installing package to sysroot ..."));
632 const QtVersion * const qtVersion = qt4BuildConfiguration()->qtVersion();
633 const QString command = QLatin1String(
634 packagingStep()->debBasedMaemoTarget() ? "xdpkg" : "xrpm");
635 QStringList args = QStringList() << command << QLatin1String("-i");
636 if (packagingStep()->debBasedMaemoTarget())
637 args << QLatin1String("--no-force-downgrade");
638 args << packagingStep()->packageFilePath();
639 MaemoGlobal::callMadAdmin(*m_sysrootInstaller, args, qtVersion, true);
640 if (!m_sysrootInstaller->waitForStarted()) {
641 writeOutput(tr("Installation to sysroot failed, continuing anyway."),
646 writeOutput(tr("Copying files to sysroot ..."));
647 Q_ASSERT(!m_filesToCopy.isEmpty());
648 QDir sysRootDir(toolChain()->sysroot());
649 foreach (const MaemoDeployable &d, m_filesToCopy) {
650 const QLatin1Char sep('/');
651 const QString targetFilePath = toolChain()->sysroot() + sep
652 + d.remoteDir + sep + QFileInfo(d.localFilePath).fileName();
653 sysRootDir.mkpath(d.remoteDir.mid(1));
654 QFile::remove(targetFilePath);
656 MaemoGlobal::removeRecursively(targetFilePath, dummy);
657 if (!MaemoGlobal::copyRecursively(d.localFilePath, targetFilePath)) {
658 writeOutput(tr("Sysroot installation failed: "
659 "Could not copy '%1' to '%2'. Continuing anyway.")
660 .arg(QDir::toNativeSeparators(d.localFilePath),
661 QDir::toNativeSeparators(targetFilePath)),
664 QCoreApplication::processEvents();
665 if (m_state == StopRequested) {
674 void MaemoDeployStep::handleSysrootInstallerFinished()
676 ASSERT_STATE(QList<State>() << InstallingToSysroot << StopRequested);
678 if (m_state == StopRequested) {
683 if (m_sysrootInstaller->error() != QProcess::UnknownError
684 || m_sysrootInstaller->exitCode() != 0) {
685 writeOutput(tr("Installation to sysroot failed, continuing anyway."),
691 void MaemoDeployStep::connectToDevice()
693 ASSERT_STATE(QList<State>() << Inactive << InstallingToSysroot);
694 setState(Connecting);
696 const bool canReUse = m_connection
697 && m_connection->state() == SshConnection::Connected
698 && m_connection->connectionParameters() == m_cachedDeviceConfig->sshParameters();
700 m_connection = SshConnection::create();
701 connect(m_connection.data(), SIGNAL(connected()), this,
702 SLOT(handleConnected()));
703 connect(m_connection.data(), SIGNAL(error(Utils::SshError)), this,
704 SLOT(handleConnectionFailure()));
708 writeOutput(tr("Connecting to device..."));
709 m_connection->connectToHost(m_cachedDeviceConfig->sshParameters());
713 void MaemoDeployStep::handleConnected()
715 ASSERT_STATE(QList<State>() << Connecting << StopRequested);
717 if (m_state == Connecting)
721 void MaemoDeployStep::unmountOldDirs()
723 setState(UnmountingOldDirs);
724 m_mounter->setConnection(m_connection);
728 void MaemoDeployStep::runPackageInstaller(const QString &packageFilePath)
730 ASSERT_STATE(QList<State>() << Mounting << Uploading);
731 const bool removeAfterInstall = m_state == Uploading;
732 setState(InstallingToDevice);
734 writeOutput(tr("Installing package to device..."));
735 const QByteArray installCommand = packagingStep()->debBasedMaemoTarget()
736 ? "dpkg -i --no-force-downgrade"
737 : "rpm -U --replacepkgs --replacefiles";
738 QByteArray cmd = MaemoGlobal::remoteSudo().toUtf8() + ' '
739 + installCommand + ' ' + packageFilePath.toUtf8();
740 if (removeAfterInstall)
741 cmd += " && (rm " + packageFilePath.toUtf8() + " || :)";
742 m_deviceInstaller = m_connection->createRemoteProcess(cmd);
743 connect(m_deviceInstaller.data(), SIGNAL(closed(int)), this,
744 SLOT(handleInstallationFinished(int)));
745 connect(m_deviceInstaller.data(), SIGNAL(outputAvailable(QByteArray)),
746 this, SLOT(handleDeviceInstallerOutput(QByteArray)));
747 connect(m_deviceInstaller.data(),
748 SIGNAL(errorOutputAvailable(QByteArray)), this,
749 SLOT(handleDeviceInstallerErrorOutput(QByteArray)));
750 m_deviceInstaller->start();
753 void MaemoDeployStep::handleProgressReport(const QString &progressMsg)
755 ASSERT_STATE(QList<State>() << UnmountingOldDirs << UnmountingCurrentDirs
756 << UnmountingCurrentMounts << Mounting << StopRequested << Inactive);
759 case UnmountingOldDirs:
760 case UnmountingCurrentDirs:
761 case UnmountingCurrentMounts:
764 writeOutput(progressMsg);
772 void MaemoDeployStep::copyNextFileToDevice()
774 ASSERT_STATE(CopyingFile);
775 Q_ASSERT(!m_filesToCopy.isEmpty());
776 Q_ASSERT(!m_currentDeviceDeployAction);
777 const MaemoDeployable d = m_filesToCopy.takeFirst();
778 QString sourceFilePath = deployMountPoint();
780 const QString localFilePath = QDir::fromNativeSeparators(d.localFilePath);
781 sourceFilePath += QLatin1Char('/') + localFilePath.at(0).toLower()
782 + localFilePath.mid(2);
784 sourceFilePath += d.localFilePath;
787 QString command = QString::fromLatin1("%1 mkdir -p %3 && %1 cp -r %2 %3")
788 .arg(MaemoGlobal::remoteSudo(), sourceFilePath, d.remoteDir);
789 SshRemoteProcess::Ptr copyProcess
790 = m_connection->createRemoteProcess(command.toUtf8());
791 connect(copyProcess.data(), SIGNAL(errorOutputAvailable(QByteArray)),
792 this, SLOT(handleDeviceInstallerErrorOutput(QByteArray)));
793 connect(copyProcess.data(), SIGNAL(closed(int)), this,
794 SLOT(handleCopyProcessFinished(int)));
795 m_currentDeviceDeployAction.reset(new DeviceDeployAction(d, copyProcess));
796 writeOutput(tr("Copying file '%1' to path '%2' on the device...")
797 .arg(d.localFilePath, d.remoteDir));
798 copyProcess->start();
801 void MaemoDeployStep::handleCopyProcessFinished(int exitStatus)
803 ASSERT_STATE(QList<State>() << CopyingFile << StopRequested << Inactive);
807 Q_ASSERT(m_currentDeviceDeployAction);
808 const QString localFilePath
809 = m_currentDeviceDeployAction->first.localFilePath;
810 if (exitStatus != SshRemoteProcess::ExitedNormally
811 || m_currentDeviceDeployAction->second->exitCode() != 0) {
812 raiseError(tr("Copying file '%1' failed.").arg(localFilePath));
813 m_currentDeviceDeployAction.reset(0);
814 setState(UnmountingCurrentMounts);
817 writeOutput(tr("Successfully copied file '%1'.").arg(localFilePath));
818 setDeployed(m_connection->connectionParameters().host,
819 m_currentDeviceDeployAction->first);
820 m_currentDeviceDeployAction.reset(0);
821 if (m_filesToCopy.isEmpty()) {
822 writeOutput(tr("All files copied."));
823 setState(UnmountingCurrentMounts);
826 copyNextFileToDevice();
840 QString MaemoDeployStep::deployMountPoint() const
842 return MaemoGlobal::homeDirOnDevice(m_cachedDeviceConfig->sshParameters().userName)
843 + QLatin1String("/deployMountPoint_") + packagingStep()->projectName();
846 const MaemoToolChain *MaemoDeployStep::toolChain() const
848 return static_cast<MaemoToolChain *>(qt4BuildConfiguration()->toolChain());
851 const AbstractQt4MaemoTarget *MaemoDeployStep::maemotarget() const
853 return static_cast<AbstractQt4MaemoTarget *>(qt4BuildConfiguration()->target());
856 void MaemoDeployStep::handleSysrootInstallerOutput()
858 ASSERT_STATE(QList<State>() << InstallingToSysroot << StopRequested);
861 case InstallingToSysroot:
863 writeOutput(QString::fromLocal8Bit(m_sysrootInstaller->readAllStandardOutput()),
871 void MaemoDeployStep::handleSysrootInstallerErrorOutput()
873 ASSERT_STATE(QList<State>() << InstallingToSysroot << StopRequested);
876 case InstallingToSysroot:
878 writeOutput(QString::fromLocal8Bit(m_sysrootInstaller->readAllStandardError()),
879 BuildStep::ErrorOutput);
886 void MaemoDeployStep::handleInstallationFinished(int exitStatus)
888 ASSERT_STATE(QList<State>() << InstallingToDevice << StopRequested
892 case InstallingToDevice:
893 if (exitStatus != SshRemoteProcess::ExitedNormally
894 || m_deviceInstaller->exitCode() != 0) {
895 raiseError(tr("Installing package failed."));
896 } else if (m_installerStderr.contains("Will not downgrade")) {
897 raiseError(tr("Installation failed: "
898 "You tried to downgrade a package, which is not allowed."));
900 m_needsInstall = false;
901 setDeployed(m_connection->connectionParameters().host,
902 MaemoDeployable(packagingStep()->packageFilePath(), QString()));
903 writeOutput(tr("Package installed."));
905 setState(UnmountingCurrentMounts);
917 void MaemoDeployStep::handlePortsGathererError(const QString &errorMsg)
919 ASSERT_STATE(QList<State>() << GatheringPorts << StopRequested << Inactive);
921 if (m_state != Inactive) {
922 raiseError(errorMsg);
927 void MaemoDeployStep::handlePortListReady()
929 ASSERT_STATE(QList<State>() << GatheringPorts << StopRequested);
931 if (m_state == GatheringPorts) {
933 m_freePorts = freePorts();
934 m_mounter->mount(&m_freePorts, m_portsGatherer);
940 void MaemoDeployStep::setState(State newState)
942 if (newState == m_state)
945 if (m_state == Inactive) {
946 m_needsInstall = false;
947 m_filesToCopy.clear();
948 m_currentDeviceDeployAction.reset(0);
950 disconnect(m_connection.data(), 0, this, 0);
952 disconnect(m_uploader.data(), 0, this, 0);
953 m_uploader->closeChannel();
955 if (m_deviceInstaller)
956 disconnect(m_deviceInstaller.data(), 0, this, 0);
961 void MaemoDeployStep::unmount()
963 if (m_mounter->hasValidMountSpecifications())
964 m_mounter->unmount();
969 void MaemoDeployStep::handleDeviceInstallerOutput(const QByteArray &output)
971 ASSERT_STATE(QList<State>() << InstallingToDevice << StopRequested);
974 case InstallingToDevice:
976 writeOutput(QString::fromUtf8(output), NormalOutput);
983 void MaemoDeployStep::handleDeviceInstallerErrorOutput(const QByteArray &output)
985 ASSERT_STATE(QList<State>() << InstallingToDevice << CopyingFile
989 case InstallingToDevice:
992 m_installerStderr += output;
993 writeOutput(QString::fromUtf8(output), ErrorOutput);
1000 MaemoPortList MaemoDeployStep::freePorts() const
1002 const Qt4BuildConfiguration * const qt4bc = qt4BuildConfiguration();
1003 const MaemoDeviceConfig::ConstPtr &devConf
1004 = m_cachedDeviceConfig ? m_cachedDeviceConfig : m_deviceConfig;
1006 return MaemoPortList();
1007 if (devConf->type() == MaemoDeviceConfig::Emulator && qt4bc) {
1008 MaemoQemuRuntime rt;
1009 const int id = qt4bc->qtVersion()->uniqueId();
1010 if (MaemoQemuManager::instance().runtimeForQtVersion(id, &rt))
1011 return rt.m_freePorts;
1013 return devConf->freePorts();
1016 const Qt4BuildConfiguration *MaemoDeployStep::qt4BuildConfiguration() const
1018 return static_cast<Qt4BuildConfiguration *>(buildConfiguration());
1022 MaemoDeployEventHandler::MaemoDeployEventHandler(MaemoDeployStep *deployStep,
1023 QFutureInterface<bool> &future)
1024 : m_deployStep(deployStep), m_future(future), m_eventLoop(new QEventLoop),
1027 connect(m_deployStep, SIGNAL(done()), this, SLOT(handleDeployingDone()));
1028 connect(m_deployStep, SIGNAL(error()), this, SLOT(handleDeployingFailed()));
1029 QTimer cancelChecker;
1030 connect(&cancelChecker, SIGNAL(timeout()), this, SLOT(checkForCanceled()));
1031 cancelChecker.start(500);
1032 future.reportResult(m_eventLoop->exec() == 0);
1035 void MaemoDeployEventHandler::handleDeployingDone()
1037 m_eventLoop->exit(m_error ? 1 : 0);
1040 void MaemoDeployEventHandler::handleDeployingFailed()
1045 void MaemoDeployEventHandler::checkForCanceled()
1047 if (!m_error && m_future.isCanceled()) {
1048 QMetaObject::invokeMethod(m_deployStep, "stop");
1050 handleDeployingDone();
1054 } // namespace Internal
1055 } // namespace Qt4ProjectManager