From 05ae17eaddbda31be9211fa9193861f5ab1fed06 Mon Sep 17 00:00:00 2001
From: dt <qtc-committer@nokia.com>
Date: Fri, 23 Jul 2010 16:37:10 +0200
Subject: [PATCH] Fix subdir project deployment for symbian

We create a .sis package per leaf project now, deploy all of them and
start the executable for the runconfiguration that was selected. This
does not cover all use cases, but works for development.
---
 .../qt-s60/s60createpackagestep.cpp           | 254 ++++++++++++++++--
 .../qt-s60/s60createpackagestep.h             |  29 +-
 .../qt-s60/s60deploystep.cpp                  |  42 +--
 .../qt4projectmanager/qt-s60/s60deploystep.h  |   4 +-
 .../qt-s60/s60devicerunconfiguration.cpp      |  60 ++++-
 .../qt-s60/s60devicerunconfiguration.h        |  16 +-
 .../s60devicerunconfigurationwidget.cpp       |   2 +-
 src/plugins/qt4projectmanager/qt4project.cpp  |  22 ++
 src/plugins/qt4projectmanager/qt4project.h    |   2 +
 src/shared/symbianutils/launcher.cpp          |  98 ++++---
 src/shared/symbianutils/launcher.h            |   4 +-
 11 files changed, 427 insertions(+), 106 deletions(-)

diff --git a/src/plugins/qt4projectmanager/qt-s60/s60createpackagestep.cpp b/src/plugins/qt4projectmanager/qt-s60/s60createpackagestep.cpp
index 54a88c18aaf..694db1012ee 100644
--- a/src/plugins/qt4projectmanager/qt-s60/s60createpackagestep.cpp
+++ b/src/plugins/qt4projectmanager/qt-s60/s60createpackagestep.cpp
@@ -31,13 +31,18 @@
 
 #include "qt4projectmanagerconstants.h"
 #include "qt4buildconfiguration.h"
+#include "qt4nodes.h"
+#include "qt4project.h"
 #include "abldparser.h"
 
 #include <projectexplorer/buildconfiguration.h>
 #include <projectexplorer/target.h>
+#include <projectexplorer/project.h>
 #include <projectexplorer/gnumakeparser.h>
+#include <projectexplorer/task.h>
 
 #include <QtCore/QDir>
+#include <QtCore/QTimer>
 
 using namespace Qt4ProjectManager::Internal;
 
@@ -49,30 +54,41 @@ namespace {
     const char * const SMART_INSTALLER_KEY("Qt4ProjectManager.S60CreatorPackageStep.SmartInstaller");
 }
 
-// #pragma mark -- S60SignBuildStep
-
 S60CreatePackageStep::S60CreatePackageStep(ProjectExplorer::BuildConfiguration *bc) :
-    MakeStep(bc, QLatin1String(SIGN_BS_ID)),
+    BuildStep(bc, QLatin1String(SIGN_BS_ID)),
     m_signingMode(SignSelf),
-    m_createSmartInstaller(false)
+    m_createSmartInstaller(false),
+    m_outputParserChain(0),
+    m_process(0),
+    m_timer(0),
+    m_eventLoop(0),
+    m_futureInterface(0)
 {
     ctor_package();
 }
 
 S60CreatePackageStep::S60CreatePackageStep(ProjectExplorer::BuildConfiguration *bc, S60CreatePackageStep *bs) :
-    MakeStep(bc, bs),
+    BuildStep(bc, bs),
     m_signingMode(bs->m_signingMode),
     m_customSignaturePath(bs->m_customSignaturePath),
     m_customKeyPath(bs->m_customKeyPath),
-    m_createSmartInstaller(bs->m_createSmartInstaller)
+    m_createSmartInstaller(bs->m_createSmartInstaller),
+    m_outputParserChain(0),
+    m_timer(0),
+    m_eventLoop(0),
+    m_futureInterface(0)
 {
     ctor_package();
 }
 
 S60CreatePackageStep::S60CreatePackageStep(ProjectExplorer::BuildConfiguration *bc, const QString &id) :
-    MakeStep(bc, id),
+    BuildStep(bc, id),
     m_signingMode(SignSelf),
-    m_createSmartInstaller(false)
+    m_createSmartInstaller(false),
+    m_outputParserChain(0),
+    m_timer(0),
+    m_eventLoop(0),
+    m_futureInterface(0)
 {
     ctor_package();
 }
@@ -88,7 +104,7 @@ S60CreatePackageStep::~S60CreatePackageStep()
 
 QVariantMap S60CreatePackageStep::toMap() const
 {
-    QVariantMap map(MakeStep::toMap());
+    QVariantMap map(BuildStep::toMap());
     map.insert(QLatin1String(SIGNMODE_KEY), (int)m_signingMode);
     map.insert(QLatin1String(CERTIFICATE_KEY), m_customSignaturePath);
     map.insert(QLatin1String(KEYFILE_KEY), m_customKeyPath);
@@ -102,34 +118,224 @@ bool S60CreatePackageStep::fromMap(const QVariantMap &map)
     m_customSignaturePath = map.value(QLatin1String(CERTIFICATE_KEY)).toString();
     m_customKeyPath = map.value(QLatin1String(KEYFILE_KEY)).toString();
     m_createSmartInstaller = map.value(QLatin1String(SMART_INSTALLER_KEY), false).toBool();
-    return MakeStep::fromMap(map);
+    return BuildStep::fromMap(map);
+}
+
+Qt4BuildConfiguration *S60CreatePackageStep::qt4BuildConfiguration() const
+{
+    return static_cast<Qt4BuildConfiguration *>(buildConfiguration());
 }
 
 bool S60CreatePackageStep::init()
 {
-    if (!MakeStep::init())
-        return false;
-    Qt4BuildConfiguration *bc = qt4BuildConfiguration();
-    ProjectExplorer::Environment environment = bc->environment();
-    setEnvironment(environment);
-    QStringList args;
+    Qt4Project *pro = qobject_cast<Qt4Project *>(buildConfiguration()->target()->project());
+
+    QList<Qt4ProFileNode *> nodes = pro->leafProFiles();
+
+    m_workingDirectories.clear();
+    foreach (Qt4ProFileNode *node, nodes)
+        m_workingDirectories << node->buildDir();
+
+    m_makeCmd = qt4BuildConfiguration()->makeCommand();
+    if (!QFileInfo(m_makeCmd).isAbsolute()) {
+        // Try to detect command in environment
+        const QString tmp = buildConfiguration()->environment().searchInPath(m_makeCmd);
+        if (tmp.isEmpty()) {
+            emit addOutput(tr("Could not find make command: %1 in the build environment").arg(m_makeCmd), BuildStep::ErrorOutput);
+            return false;
+        }
+        m_makeCmd = tmp;
+    }
+
+    m_environment = qt4BuildConfiguration()->environment();
+
+    m_args.clear();
     if (m_createSmartInstaller)
-        args << QLatin1String("installer_sis");
+        m_args << QLatin1String("installer_sis");
     else
-        args << QLatin1String("sis");
+        m_args << QLatin1String("sis");
     if (signingMode() == SignCustom) {
-        args << QLatin1String("QT_SIS_CERTIFICATE=") + QDir::toNativeSeparators(customSignaturePath())
-             << QLatin1String("QT_SIS_KEY=") + QDir::toNativeSeparators(customKeyPath());
+        m_args << QLatin1String("QT_SIS_CERTIFICATE=") + QDir::toNativeSeparators(customSignaturePath())
+               << QLatin1String("QT_SIS_KEY=") + QDir::toNativeSeparators(customKeyPath());
+    }
+
+    delete m_outputParserChain;
+    m_outputParserChain = new ProjectExplorer::GnuMakeParser;
+    m_outputParserChain->appendOutputParser(new Qt4ProjectManager::AbldParser);
+    connect(m_outputParserChain, SIGNAL(addOutput(QString, ProjectExplorer::BuildStep::OutputFormat)),
+            this, SLOT(outputAdded(QString, ProjectExplorer::BuildStep::OutputFormat)));
+    connect(m_outputParserChain, SIGNAL(addTask(ProjectExplorer::Task)),
+            this, SLOT(taskAdded(ProjectExplorer::Task)));
+
+    return true;
+}
+
+void S60CreatePackageStep::run(QFutureInterface<bool> &fi)
+{
+    m_futureInterface = &fi;
+
+    if (m_workingDirectories.isEmpty()) {
+        fi.reportResult(true);
+        return;
+    }
+
+    // Setup everything...
+    m_process = new QProcess();
+    m_process->setEnvironment(m_environment.toStringList());
+
+    connect(m_process, SIGNAL(readyReadStandardOutput()),
+            this, SLOT(processReadyReadStdOutput()),
+            Qt::DirectConnection);
+    connect(m_process, SIGNAL(readyReadStandardError()),
+            this, SLOT(processReadyReadStdError()),
+            Qt::DirectConnection);
+
+    connect(m_process, SIGNAL(finished(int, QProcess::ExitStatus)),
+            this, SLOT(slotProcessFinished(int, QProcess::ExitStatus)),
+            Qt::DirectConnection);
+
+    m_timer = new QTimer();
+    connect(m_timer, SIGNAL(timeout()), this, SLOT(checkForCancel()), Qt::DirectConnection);
+    m_timer->start(500);
+    m_eventLoop = new QEventLoop;
+
+    bool returnValue = false;
+    if (startProcess())
+        returnValue = m_eventLoop->exec();
+
+    // Finished
+    m_timer->stop();
+    delete m_timer;
+    m_timer = 0;
+
+    delete m_process;
+    m_process = 0;
+    delete m_eventLoop;
+    m_eventLoop = 0;
+    fi.reportResult(returnValue);
+    m_futureInterface = 0;
+
+    return;
+}
+
+void S60CreatePackageStep::slotProcessFinished(int, QProcess::ExitStatus)
+{
+    QString line = QString::fromLocal8Bit(m_process->readAllStandardError());
+    if (!line.isEmpty())
+        stdError(line);
+
+    line = QString::fromLocal8Bit(m_process->readAllStandardOutput());
+    if (!line.isEmpty())
+        stdOutput(line);
+
+    bool returnValue = false;
+    if (m_process->exitStatus() == QProcess::NormalExit && m_process->exitCode() == 0) {
+        emit addOutput(tr("The process \"%1\" exited normally.")
+                       .arg(QDir::toNativeSeparators(m_makeCmd)),
+                       BuildStep::MessageOutput);
+        returnValue = true;
+    } else if (m_process->exitStatus() == QProcess::NormalExit) {
+        emit addOutput(tr("The process \"%1\" exited with code %2.")
+                       .arg(QDir::toNativeSeparators(m_makeCmd), QString::number(m_process->exitCode())),
+                       BuildStep::ErrorMessageOutput);
+    } else {
+        emit addOutput(tr("The process \"%1\" crashed.").arg(QDir::toNativeSeparators(m_makeCmd)), BuildStep::ErrorMessageOutput);
+    }
+
+    if (m_workingDirectories.isEmpty() || !returnValue) {
+        m_eventLoop->exit(returnValue);
+    } else {
+        // Success, do next
+        if (!startProcess())
+            m_eventLoop->exit(returnValue);
     }
-    setArguments(args); // overwrite any stuff done in make step
+}
+
+bool S60CreatePackageStep::startProcess()
+{
+    QString workingDirectory = m_workingDirectories.takeFirst();
+    QDir wd(workingDirectory);
+    if (!wd.exists())
+        wd.mkpath(wd.absolutePath());
 
-    ProjectExplorer::GnuMakeParser *parser = new ProjectExplorer::GnuMakeParser;
-    parser->appendOutputParser(new Qt4ProjectManager::AbldParser);
-    setOutputParser(parser);
+    m_process->setWorkingDirectory(workingDirectory);
 
+    // Go for it!
+    m_process->start(m_makeCmd, m_args);
+    if (!m_process->waitForStarted()) {
+        emit addOutput(tr("Could not start process \"%1\" in %2")
+                       .arg(QDir::toNativeSeparators(m_makeCmd),
+                            workingDirectory),
+                       BuildStep::ErrorMessageOutput);
+        return false;
+    }
+    emit addOutput(tr("Starting: \"%1\" %2 in %3\n")
+                   .arg(QDir::toNativeSeparators(m_makeCmd),
+                        m_args.join(" "),
+                        workingDirectory),
+                   BuildStep::MessageOutput);
     return true;
 }
 
+void S60CreatePackageStep::processReadyReadStdOutput()
+{
+    m_process->setReadChannel(QProcess::StandardOutput);
+    while (m_process->canReadLine()) {
+        QString line = QString::fromLocal8Bit(m_process->readLine());
+        stdOutput(line);
+    }
+}
+
+void S60CreatePackageStep::stdOutput(const QString &line)
+{
+    if (m_outputParserChain)
+        m_outputParserChain->stdOutput(line);
+    emit addOutput(line, BuildStep::NormalOutput);
+}
+
+void S60CreatePackageStep::processReadyReadStdError()
+{
+    m_process->setReadChannel(QProcess::StandardError);
+    while (m_process->canReadLine()) {
+        QString line = QString::fromLocal8Bit(m_process->readLine());
+        stdError(line);
+    }
+}
+
+void S60CreatePackageStep::stdError(const QString &line)
+{
+    if (m_outputParserChain)
+        m_outputParserChain->stdError(line);
+    emit addOutput(line, BuildStep::ErrorOutput);
+}
+
+void S60CreatePackageStep::checkForCancel()
+{
+    if (m_futureInterface->isCanceled() && m_timer->isActive()) {
+        m_timer->stop();
+        m_process->terminate();
+        m_process->waitForFinished(5000);
+        m_process->kill();
+    }
+}
+
+void S60CreatePackageStep::taskAdded(const ProjectExplorer::Task &task)
+{
+    ProjectExplorer::Task editable(task);
+    QString filePath = QDir::cleanPath(task.file.trimmed());
+    if (!filePath.isEmpty() && !QDir::isAbsolutePath(filePath)) {
+        // TODO which kind of tasks do we get from package building?
+        // No absoulte path
+    }
+    emit addTask(editable);
+}
+
+void S60CreatePackageStep::outputAdded(const QString &string, ProjectExplorer::BuildStep::OutputFormat format)
+{
+    emit addOutput(string, format);
+}
+
+
 bool S60CreatePackageStep::immutable() const
 {
     return false;
diff --git a/src/plugins/qt4projectmanager/qt-s60/s60createpackagestep.h b/src/plugins/qt4projectmanager/qt-s60/s60createpackagestep.h
index fd74500b9b6..e4a03985adc 100644
--- a/src/plugins/qt4projectmanager/qt-s60/s60createpackagestep.h
+++ b/src/plugins/qt4projectmanager/qt-s60/s60createpackagestep.h
@@ -59,7 +59,8 @@ public:
     ProjectExplorer::BuildStep *clone(ProjectExplorer::BuildConfiguration *parent, ProjectExplorer::BuildStep::Type type, ProjectExplorer::BuildStep *product);
 };
 
-class S60CreatePackageStep : public MakeStep {
+class S60CreatePackageStep : public ProjectExplorer::BuildStep
+{
     Q_OBJECT
     friend class S60CreatePackageStepFactory;
 public:
@@ -72,6 +73,7 @@ public:
     virtual ~S60CreatePackageStep();
 
     virtual bool init();
+    virtual void run(QFutureInterface<bool> &fi);
     virtual ProjectExplorer::BuildStepConfigWidget *createConfigWidget();
     virtual bool immutable() const;
 
@@ -91,13 +93,38 @@ protected:
     S60CreatePackageStep(ProjectExplorer::BuildConfiguration *bc, const QString &id);
     bool fromMap(const QVariantMap &map);
 
+    Qt4BuildConfiguration *qt4BuildConfiguration() const;
+
+private slots:
+    void slotProcessFinished(int, QProcess::ExitStatus);
+    void processReadyReadStdOutput();
+    void processReadyReadStdError();
+    void taskAdded(const ProjectExplorer::Task &task);
+    void outputAdded(const QString &string, ProjectExplorer::BuildStep::OutputFormat format);
+    void checkForCancel();
+
 private:
+    void stdOutput(const QString &line);
+    void stdError(const QString &line);
+    bool startProcess();
+    QStringList m_workingDirectories;
+
+    QString m_makeCmd;
+    ProjectExplorer::Environment m_environment;
+    QStringList m_args;
+
     void ctor_package();
 
     SigningMode m_signingMode;
     QString m_customSignaturePath;
     QString m_customKeyPath;
     bool m_createSmartInstaller;
+    ProjectExplorer::IOutputParser *m_outputParserChain;
+
+    QProcess *m_process;
+    QTimer *m_timer;
+    QEventLoop *m_eventLoop;
+    QFutureInterface<bool> *m_futureInterface;
 };
 
 class S60CreatePackageStepConfigWidget : public ProjectExplorer::BuildStepConfigWidget
diff --git a/src/plugins/qt4projectmanager/qt-s60/s60deploystep.cpp b/src/plugins/qt4projectmanager/qt-s60/s60deploystep.cpp
index 129f52f7ccd..d1a9df646c6 100644
--- a/src/plugins/qt4projectmanager/qt-s60/s60deploystep.cpp
+++ b/src/plugins/qt4projectmanager/qt-s60/s60deploystep.cpp
@@ -126,8 +126,9 @@ bool S60DeployStep::init()
     }
     m_serialPortName = runConfiguration->serialPortName();
     m_serialPortFriendlyName = SymbianUtils::SymbianDeviceManager::instance()->friendlyNameForPort(m_serialPortName);
-    m_packageFileNameWithTarget = runConfiguration->packageFileNameWithTargetInfo();
-    m_signedPackage = runConfiguration->signedPackage();
+    m_packageFileNamesWithTarget = runConfiguration->packageFileNamesWithTargetInfo();
+    m_signedPackages = runConfiguration->signedPackages();
+
     m_installationDrive = runConfiguration->installationDrive();
     m_silentInstall = runConfiguration->silentInstall();
 
@@ -174,31 +175,31 @@ void S60DeployStep::appendMessage(const QString &error, bool isError)
 
 bool S60DeployStep::processPackageName(QString &errorMessage)
 {
-    QFileInfo packageInfo(m_signedPackage);
-    {
+    for (int i = 0; i < m_signedPackages.count(); ++i) {
+        QFileInfo packageInfo(m_signedPackages.at(i));
         // support for 4.6.1 and pre, where make sis creates 'targetname_armX_udeb.sis' instead of 'targetname.sis'
-        QFileInfo packageWithTargetInfo(m_packageFileNameWithTarget);
+        QFileInfo packageWithTargetInfo(m_packageFileNamesWithTarget.at(i));
         // does the 4.6.1 version exist?
         if (packageWithTargetInfo.exists() && packageWithTargetInfo.isFile()) {
             // is the 4.6.1 version newer? (to guard against behavior change Qt Creator 1.3 --> 2.0)
             if (!packageInfo.exists() || packageInfo.lastModified() < packageWithTargetInfo.lastModified()) { //TODO change the QtCore
                 // the 'targetname_armX_udeb.sis' crap exists and is new, rename it
                 appendMessage(tr("Renaming new package '%1' to '%2'")
-                              .arg(QDir::toNativeSeparators(m_packageFileNameWithTarget),
-                                   QDir::toNativeSeparators(m_signedPackage)), false);
-                return renameFile(m_packageFileNameWithTarget, m_signedPackage, &errorMessage);
+                              .arg(QDir::toNativeSeparators(m_packageFileNamesWithTarget.at(i)),
+                                   QDir::toNativeSeparators(m_signedPackages.at(i))), false);
+                return renameFile(m_packageFileNamesWithTarget.at(i), m_signedPackages.at(i), &errorMessage);
             } else {
                 // the 'targetname_armX_udeb.sis' crap exists but is old, remove it
                 appendMessage(tr("Removing old package '%1'")
-                              .arg(QDir::toNativeSeparators(m_packageFileNameWithTarget)),
+                              .arg(QDir::toNativeSeparators(m_packageFileNamesWithTarget.at(i))),
                               false);
-                ensureDeleteFile(m_packageFileNameWithTarget, &errorMessage);
+                ensureDeleteFile(m_packageFileNamesWithTarget.at(i), &errorMessage);
             }
         }
-    }
-    if (!packageInfo.exists() || !packageInfo.isFile()) {
-        errorMessage = tr("Package file not found");
-        return false;
+        if (!packageInfo.exists() || !packageInfo.isFile()) {
+            errorMessage = tr("'%1': Package file not found").arg(m_signedPackages.at(i));
+            return false;
+        }
     }
     return true;
 }
@@ -217,7 +218,7 @@ void S60DeployStep::start()
     if (processPackageName(errorMessage)) {
         startDeployment();
     } else {
-        errorMessage = tr("Failed to find package '%1': %2").arg(m_signedPackage, errorMessage);
+        errorMessage = tr("Failed to find package %1").arg(errorMessage);
         appendMessage(errorMessage, true);
         stop();
         emit finished();
@@ -253,16 +254,19 @@ void S60DeployStep::startDeployment()
 
     setupConnections();
 
-    const QString copyDst = QString::fromLatin1("%1:\\Data\\%2").arg(m_installationDrive).arg(QFileInfo(m_signedPackage).fileName());
+    QStringList copyDst;
+    foreach (const QString &signedPackage, m_signedPackages)
+        copyDst << QString::fromLatin1("%1:\\Data\\%1").arg(m_installationDrive).arg(QFileInfo(signedPackage).fileName());
 
-    m_launcher->setCopyFileName(m_signedPackage, copyDst);
-    m_launcher->setInstallFileName(copyDst);
+    m_launcher->setCopyFileNames(m_signedPackages, copyDst);
+    m_launcher->setInstallFileNames(copyDst);
     m_launcher->setInstallationDrive(m_installationDrive);
     m_launcher->setInstallationMode(m_silentInstall?trk::Launcher::InstallationModeSilentAndUser:
                                                     trk::Launcher::InstallationModeUser);
     m_launcher->addStartupActions(trk::Launcher::ActionCopyInstall);
 
-    appendMessage(tr("Package: %1\nDeploying application to '%2'...").arg(m_signedPackage, m_serialPortFriendlyName), false);
+    // TODO readd information about packages? msgListFile(m_signedPackage)
+    appendMessage(tr("Deploying application to '%2'...").arg(m_serialPortFriendlyName), false);
 
     QString errorMessage;
     if (!m_launcher->startServer(&errorMessage)) {
diff --git a/src/plugins/qt4projectmanager/qt-s60/s60deploystep.h b/src/plugins/qt4projectmanager/qt-s60/s60deploystep.h
index 2023270b5c6..e3c324669dc 100644
--- a/src/plugins/qt4projectmanager/qt-s60/s60deploystep.h
+++ b/src/plugins/qt4projectmanager/qt-s60/s60deploystep.h
@@ -129,8 +129,8 @@ signals:
 private:
     QString m_serialPortName;
     QString m_serialPortFriendlyName;
-    QString m_packageFileNameWithTarget; // Support for 4.6.1
-    QString m_signedPackage;
+    QStringList m_packageFileNamesWithTarget; // Support for 4.6.1
+    QStringList m_signedPackages;
 
     QTimer *m_timer;
 
diff --git a/src/plugins/qt4projectmanager/qt-s60/s60devicerunconfiguration.cpp b/src/plugins/qt4projectmanager/qt-s60/s60devicerunconfiguration.cpp
index 4c66d059c7f..8bb36a19823 100644
--- a/src/plugins/qt4projectmanager/qt-s60/s60devicerunconfiguration.cpp
+++ b/src/plugins/qt4projectmanager/qt-s60/s60devicerunconfiguration.cpp
@@ -286,16 +286,22 @@ static inline QString fixBaseNameTarget(const QString &in)
     return in;
 }
 
-QString S60DeviceRunConfiguration::packageFileNameWithTargetInfo() const
-{
-    TargetInformation ti = qt4Target()->qt4Project()->rootProjectNode()->targetInformation(m_proFilePath);
-    if (!ti.valid)
-        return QString();
-    QString baseFileName = ti.buildDir + QLatin1Char('/') + ti.target;
-    baseFileName += QLatin1Char('_')
-                    + (isDebug() ? QLatin1String("debug") : QLatin1String("release"))
-                    + QLatin1Char('-') + symbianPlatform() + QLatin1String(".sis");
-    return baseFileName;
+QStringList S60DeviceRunConfiguration::packageFileNamesWithTargetInfo() const
+{
+    QList<Qt4ProFileNode *> leafs = qt4Target()->qt4Project()->leafProFiles();
+
+    QStringList result;
+    foreach (Qt4ProFileNode *qt4ProFileNode, leafs) {
+        TargetInformation ti = qt4ProFileNode->targetInformation();
+        if (!ti.valid)
+            continue;
+        QString baseFileName = ti.buildDir + QLatin1Char('/') + ti.target;
+        baseFileName += QLatin1Char('_')
+                + (isDebug() ? QLatin1String("debug") : QLatin1String("release"))
+                + QLatin1Char('-') + symbianPlatform() + QLatin1String(".sis");
+        result << baseFileName;
+    }
+    return result;
 }
 
 QString S60DeviceRunConfiguration::symbianPlatform() const
@@ -323,7 +329,19 @@ QString S60DeviceRunConfiguration::symbianTarget() const
     return isDebug() ? QLatin1String("udeb") : QLatin1String("urel");
 }
 
-QString S60DeviceRunConfiguration::packageTemplateFileName() const
+QStringList S60DeviceRunConfiguration::packageTemplateFileNames() const
+{
+    QList<Qt4ProFileNode *> list = qt4Target()->qt4Project()->leafProFiles();
+    QStringList result;
+    foreach (Qt4ProFileNode *node, list) {
+        TargetInformation ti = node->targetInformation();
+        if (ti.valid)
+            result << ti.buildDir + QLatin1Char('/') + ti.target + QLatin1String("_template.pkg");
+    }
+    return result;
+}
+
+QString S60DeviceRunConfiguration::appPackageTemplateFileName() const
 {
     TargetInformation ti = qt4Target()->qt4Project()->rootProjectNode()->targetInformation(m_proFilePath);
     if (!ti.valid)
@@ -331,6 +349,7 @@ QString S60DeviceRunConfiguration::packageTemplateFileName() const
     return ti.buildDir + QLatin1Char('/') + ti.target + QLatin1String("_template.pkg");
 }
 
+
 /* Grep a package file for the '.exe' file. Curently for use on Linux only
  * as the '.pkg'-files on Windows do not contain drive letters, which is not
  * handled here. \code
@@ -373,7 +392,7 @@ QString S60DeviceRunConfiguration::localExecutableFileName() const
     switch (toolChainType()) {
     case ToolChain::GCCE_GNUPOC:
     case ToolChain::RVCT_ARMV5_GNUPOC:
-        localExecutable = executableFromPackageUnix(packageTemplateFileName());
+        localExecutable = executableFromPackageUnix(appPackageTemplateFileName());
         break;
     default: {
             const QtVersion *qtv = qtVersion();
@@ -403,7 +422,21 @@ bool S60DeviceRunConfiguration::runSmartInstaller() const
     return false;
 }
 
-QString S60DeviceRunConfiguration::signedPackage() const
+QStringList S60DeviceRunConfiguration::signedPackages() const
+{
+    QList<Qt4ProFileNode *> list = qt4Target()->qt4Project()->leafProFiles();
+    QStringList result;
+    foreach (Qt4ProFileNode *node, list) {
+        TargetInformation ti = node->targetInformation();
+        if (ti.valid)
+            result << ti.buildDir + QLatin1Char('/') + ti.target
+                      + (runSmartInstaller() ? QLatin1String("_installer") : QLatin1String(""))
+                      + QLatin1String(".sis");
+    }
+    return result;
+}
+
+QString S60DeviceRunConfiguration::appSignedPackage() const
 {
     TargetInformation ti = qt4Target()->qt4Project()->rootProjectNode()->targetInformation(m_proFilePath);
     if (!ti.valid)
@@ -640,7 +673,6 @@ void S60DeviceRunControlBase::startDeployment()
 
         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 =
diff --git a/src/plugins/qt4projectmanager/qt-s60/s60devicerunconfiguration.h b/src/plugins/qt4projectmanager/qt-s60/s60devicerunconfiguration.h
index d17716f9574..b2c7a4883a2 100644
--- a/src/plugins/qt4projectmanager/qt-s60/s60devicerunconfiguration.h
+++ b/src/plugins/qt4projectmanager/qt-s60/s60devicerunconfiguration.h
@@ -82,15 +82,14 @@ public:
     char installationDrive() const;
     void setInstallationDrive(char drive);
 
+    QStringList packageFileNamesWithTargetInfo() const;
+    QStringList packageTemplateFileNames() const;
+    QString appPackageTemplateFileName() const;
     QString targetName() const;
-    QString packageFileNameWithTargetInfo() const;
-    QString symbianPlatform() const;
-    QString symbianTarget() const;
-    bool isDebug() const;
-    QString packageTemplateFileName() const;
 
     QString localExecutableFileName() const;
-    QString signedPackage() const;
+    QStringList signedPackages() const;
+    QString appSignedPackage() const;
     const QtVersion *qtVersion() const;
 
     QStringList commandLineArguments() const;
@@ -107,6 +106,7 @@ signals:
     void targetInformationChanged();
     void serialPortNameChanged();
 
+
 private slots:
     void proFileUpdate(Qt4ProjectManager::Internal::Qt4ProFileNode *pro);
     void updateActiveBuildConfiguration(ProjectExplorer::BuildConfiguration *buildConfiguration);
@@ -116,6 +116,9 @@ protected:
     virtual bool fromMap(const QVariantMap &map);
 
 private:
+    QString symbianPlatform() const;
+    QString symbianTarget() const;
+    bool isDebug() const;
     ProjectExplorer::ToolChain::ToolChainType toolChainType(ProjectExplorer::BuildConfiguration *configuration) const;
     void ctor();
 
@@ -197,7 +200,6 @@ private:
     QString m_serialPortFriendlyName;
     QString m_targetName;
     QStringList m_commandLineArguments;
-    QString m_workingDirectory;
     QString m_executableFileName;
     QString m_qtDir;
     QString m_qtBinPath;
diff --git a/src/plugins/qt4projectmanager/qt-s60/s60devicerunconfigurationwidget.cpp b/src/plugins/qt4projectmanager/qt-s60/s60devicerunconfigurationwidget.cpp
index d180b234987..cb3a4c08c2e 100644
--- a/src/plugins/qt4projectmanager/qt-s60/s60devicerunconfigurationwidget.cpp
+++ b/src/plugins/qt4projectmanager/qt-s60/s60devicerunconfigurationwidget.cpp
@@ -247,7 +247,7 @@ void S60DeviceRunConfigurationWidget::argumentsEdited(const QString &text)
 
 void S60DeviceRunConfigurationWidget::updateTargetInformation()
 {
-    m_sisFileLabel->setText(QDir::toNativeSeparators(m_runConfiguration->signedPackage()));
+    m_sisFileLabel->setText(QDir::toNativeSeparators(m_runConfiguration->appPackageTemplateFileName()));
 }
 
 void S60DeviceRunConfigurationWidget::setInstallationDrive(int index)
diff --git a/src/plugins/qt4projectmanager/qt4project.cpp b/src/plugins/qt4projectmanager/qt4project.cpp
index 3e42ca965e8..20243154918 100644
--- a/src/plugins/qt4projectmanager/qt4project.cpp
+++ b/src/plugins/qt4projectmanager/qt4project.cpp
@@ -977,6 +977,19 @@ QList<BuildConfigWidget*> Qt4Project::subConfigWidgets()
     return subWidgets;
 }
 
+void Qt4Project::collectLeafProFiles(QList<Qt4ProFileNode *> &list, Qt4ProFileNode *node)
+{
+    if (node->projectType() != Internal::SubDirsTemplate) {
+        list.append(node);
+    }
+    foreach (ProjectNode *n, node->subProjectNodes()) {
+        Qt4ProFileNode *qt4ProFileNode = qobject_cast<Qt4ProFileNode *>(n);
+        if (qt4ProFileNode)
+            collectLeafProFiles(list, qt4ProFileNode);
+    }
+}
+
+
 void Qt4Project::collectApplicationProFiles(QList<Qt4ProFileNode *> &list, Qt4ProFileNode *node)
 {
     if (node->projectType() == Internal::ApplicationTemplate
@@ -1035,6 +1048,15 @@ void Qt4Project::createApplicationProjects()
     }
 }
 
+QList<Qt4ProFileNode *> Qt4Project::leafProFiles() const
+{
+    QList<Qt4ProFileNode *> list;
+    if (!rootProjectNode())
+        return list;
+    collectLeafProFiles(list, rootProjectNode());
+    return list;
+}
+
 QList<Qt4ProFileNode *> Qt4Project::applicationProFiles() const
 {
     QList<Qt4ProFileNode *> list;
diff --git a/src/plugins/qt4projectmanager/qt4project.h b/src/plugins/qt4projectmanager/qt4project.h
index 7cd90165059..2d805c21cb6 100644
--- a/src/plugins/qt4projectmanager/qt4project.h
+++ b/src/plugins/qt4projectmanager/qt4project.h
@@ -136,6 +136,7 @@ public:
     ProjectExplorer::BuildConfigWidget *createConfigWidget();
     QList<ProjectExplorer::BuildConfigWidget*> subConfigWidgets();
 
+    QList<Internal::Qt4ProFileNode *> leafProFiles() const;
     QList<Internal::Qt4ProFileNode *> applicationProFiles() const;
     bool hasApplicationProFile(const QString &path) const;
     QStringList applicationProFilePathes(const QString &prepend = QString()) const;
@@ -191,6 +192,7 @@ private:
     void updateCodeModel();
     void updateFileList();
 
+    static void collectLeafProFiles(QList<Internal::Qt4ProFileNode *> &list, Internal::Qt4ProFileNode *node);
     static void collectApplicationProFiles(QList<Internal::Qt4ProFileNode *> &list, Internal::Qt4ProFileNode *node);
     static void findProFile(const QString& fileName, Internal::Qt4ProFileNode *root, QList<Internal::Qt4ProFileNode *> &list);
     static bool hasSubNode(Internal::Qt4PriFileNode *root, const QString &path);
diff --git a/src/shared/symbianutils/launcher.cpp b/src/shared/symbianutils/launcher.cpp
index e6b790f45af..4f8eaf7ad28 100644
--- a/src/shared/symbianutils/launcher.cpp
+++ b/src/shared/symbianutils/launcher.cpp
@@ -74,15 +74,24 @@ void CrashReportState::clear()
 }
 
 struct LauncherPrivate {
-    struct CopyState {
-        QString sourceFileName;
-        QString destinationFileName;
+    struct TransferState {
+        int currentFileName;
         uint copyFileHandle;
         QScopedPointer<QByteArray> data;
         qint64 position;
         QScopedPointer<QFile> localFile;
     };
 
+    struct CopyState : public TransferState {
+        QStringList sourceFileNames;
+        QStringList destinationFileNames;
+    };
+
+    struct DownloadState : public TransferState {
+        QString sourceFileName;
+        QString destinationFileName;
+    };
+
     explicit LauncherPrivate(const TrkDevicePtr &d);
 
     TrkDevicePtr m_device;
@@ -94,10 +103,11 @@ struct LauncherPrivate {
     Session m_session; // global-ish data (process id, target information)
 
     CopyState m_copyState;
-    CopyState m_downloadState;
+    DownloadState m_downloadState;
     QString m_fileName;
     QStringList m_commandLineArgs;
-    QString m_installFileName;
+    QStringList m_installFileNames;
+    int m_currentInstallFileName;
     int m_verbose;
     Launcher::Actions m_startupActions;
     bool m_closeDevice;
@@ -188,10 +198,11 @@ void Launcher::setFileName(const QString &name)
     d->m_fileName = name;
 }
 
-void Launcher::setCopyFileName(const QString &srcName, const QString &dstName)
+void Launcher::setCopyFileNames(const QStringList &srcNames, const QStringList &dstNames)
 {
-    d->m_copyState.sourceFileName = srcName;
-    d->m_copyState.destinationFileName = dstName;
+    d->m_copyState.sourceFileNames = srcNames;
+    d->m_copyState.destinationFileNames = dstNames;
+    d->m_copyState.currentFileName = 0;
 }
 
 void Launcher::setDownloadFileName(const QString &srcName, const QString &dstName)
@@ -200,9 +211,10 @@ void Launcher::setDownloadFileName(const QString &srcName, const QString &dstNam
     d->m_downloadState.destinationFileName = dstName;
 }
 
-void Launcher::setInstallFileName(const QString &name)
+void Launcher::setInstallFileNames(const QStringList &names)
 {
-    d->m_installFileName = name;
+    d->m_installFileNames = names;
+    d->m_currentInstallFileName = 0;
 }
 
 void Launcher::setCommandLineArgs(const QStringList &args)
@@ -255,28 +267,29 @@ bool Launcher::startServer(QString *errorMessage)
             str << " Executable=" << d->m_fileName;
         if (!d->m_commandLineArgs.isEmpty())
             str << " Arguments= " << d->m_commandLineArgs.join(QString(QLatin1Char(' ')));
-        if (!d->m_copyState.sourceFileName.isEmpty())
-            str << " Package/Source=" << d->m_copyState.sourceFileName;
-        if (!d->m_copyState.destinationFileName.isEmpty())
-            str << " Remote Package/Destination=" << d->m_copyState.destinationFileName;
+        for (int i = 0; i < d->m_copyState.sourceFileNames.size(); ++i) {
+            str << " Package/Source=" << d->m_copyState.sourceFileNames.at(i);
+            str << " Remote Package/Destination=" << d->m_copyState.destinationFileNames.at(i);
+        }
         if (!d->m_downloadState.sourceFileName.isEmpty())
             str << " Source=" << d->m_downloadState.sourceFileName;
         if (!d->m_downloadState.destinationFileName.isEmpty())
             str << " Destination=" << d->m_downloadState.destinationFileName;
-        if (!d->m_installFileName.isEmpty())
-            str << " Install file=" << d->m_installFileName;
+        if (!d->m_installFileNames.isEmpty())
+            foreach (const QString &installFileName, d->m_installFileNames)
+                str << " Install file=" << installFileName;
         logMessage(msg);
     }
     if (d->m_startupActions & ActionCopy) {
-        if (d->m_copyState.sourceFileName.isEmpty()) {
+        if (d->m_copyState.sourceFileNames.isEmpty()) {
             qWarning("No local filename given for copying package.");
             return false;
-        } else if (d->m_copyState.destinationFileName.isEmpty()) {
+        } else if (d->m_copyState.destinationFileNames.isEmpty()) {
             qWarning("No remote filename given for copying package.");
             return false;
         }
     }
-    if (d->m_startupActions & ActionInstall && d->m_installFileName.isEmpty()) {
+    if (d->m_startupActions & ActionInstall && d->m_installFileNames.isEmpty()) {
         qWarning("No package name given for installing.");
         return false;
     }
@@ -573,15 +586,15 @@ static inline QString msgCannotOpenLocalFile(const QString &fileName, const QStr
 void Launcher::handleFileCreation(const TrkResult &result)
 {
     if (result.errorCode() || result.data.size() < 6) {
-        const QString msg = msgCannotOpenRemoteFile(d->m_copyState.destinationFileName, result.errorString());
+        const QString msg = msgCannotOpenRemoteFile(d->m_copyState.destinationFileNames.at(d->m_copyState.currentFileName), result.errorString());
         logMessage(msg);
-        emit canNotCreateFile(d->m_copyState.destinationFileName, msg);
+        emit canNotCreateFile(d->m_copyState.destinationFileNames.at(d->m_copyState.currentFileName), msg);
         disconnectTrk();
         return;
     }
     const char *data = result.data.data();
     d->m_copyState.copyFileHandle = extractInt(data + 2);
-    const QString localFileName = d->m_copyState.sourceFileName;
+    const QString localFileName = d->m_copyState.sourceFileNames.at(d->m_copyState.currentFileName);
     QFile file(localFileName);
     d->m_copyState.position = 0;
     if (!file.open(QIODevice::ReadOnly)) {
@@ -657,7 +670,7 @@ void Launcher::handleCopy(const TrkResult &result)
 {
     if (result.errorCode() || result.data.size() < 4) {
         closeRemoteFile(true);
-        emit canNotWriteFile(d->m_copyState.destinationFileName, result.errorString());
+        emit canNotWriteFile(d->m_copyState.destinationFileNames.at(d->m_copyState.currentFileName), result.errorString());
         disconnectTrk();
     } else {
         continueCopying(extractShort(result.data.data() + 2));
@@ -693,7 +706,7 @@ void Launcher::closeRemoteFile(bool failed)
     d->m_device->sendTrkMessage(TrkCloseFile,
                                failed ? TrkCallback() : TrkCallback(this, &Launcher::handleFileCopied),
                                ba);
-    d->m_copyState.data.reset();
+    d->m_copyState.data.reset(0);
     d->m_copyState.copyFileHandle = 0;
     d->m_copyState.position = 0;
 }
@@ -701,15 +714,21 @@ void Launcher::closeRemoteFile(bool failed)
 void Launcher::handleFileCopied(const TrkResult &result)
 {
     if (result.errorCode())
-        emit canNotCloseFile(d->m_copyState.destinationFileName, result.errorString());
-    if (d->m_startupActions & ActionInstall)
+        emit canNotCloseFile(d->m_copyState.destinationFileNames.at(d->m_copyState.currentFileName), result.errorString());
+
+    ++d->m_copyState.currentFileName;
+
+    if (d->m_startupActions & ActionInstall && d->m_copyState.currentFileName < d->m_copyState.sourceFileNames.size()) {
+        copyFileToRemote();
+    } else if (d->m_startupActions & ActionInstall) {
         installRemotePackage();
-    else if (d->m_startupActions & ActionRun)
+    } else if (d->m_startupActions & ActionRun) {
         startInferiorIfNeeded();
-    else if (d->m_startupActions & ActionDownload)
+    } else if (d->m_startupActions & ActionDownload) {
         copyFileFromRemote();
-    else
+    } else {
         disconnectTrk();
+    }
 }
 
 void Launcher::handleCpuType(const TrkResult &result)
@@ -856,7 +875,7 @@ void Launcher::copyFileToRemote()
     emit copyingStarted();
     QByteArray ba;
     ba.append(char(10)); //kDSFileOpenWrite | kDSFileOpenBinary
-    appendString(&ba, d->m_copyState.destinationFileName.toLocal8Bit(), TargetByteOrder, false);
+    appendString(&ba, d->m_copyState.destinationFileNames.at(d->m_copyState.currentFileName).toLocal8Bit(), TargetByteOrder, false);
     d->m_device->sendTrkMessage(TrkOpenFile, TrkCallback(this, &Launcher::handleFileCreation), ba);
 }
 
@@ -875,7 +894,7 @@ void Launcher::installRemotePackageSilently()
     d->m_currentInstallationStep = InstallationModeSilent;
     QByteArray ba;
     ba.append((char)QChar::toUpper((ushort)d->m_installationDrive));
-    appendString(&ba, d->m_installFileName.toLocal8Bit(), TargetByteOrder, false);
+    appendString(&ba, d->m_installFileNames.at(d->m_currentInstallFileName).toLocal8Bit(), TargetByteOrder, false);
     d->m_device->sendTrkMessage(TrkInstallFile, TrkCallback(this, &Launcher::handleInstallPackageFinished), ba);
 }
 
@@ -884,7 +903,7 @@ void Launcher::installRemotePackageByUser()
     emit installingStarted();
     d->m_currentInstallationStep = InstallationModeUser;
     QByteArray ba;
-    appendString(&ba, d->m_installFileName.toLocal8Bit(), TargetByteOrder, false);
+    appendString(&ba, d->m_installFileNames.at(d->m_currentInstallFileName).toLocal8Bit(), TargetByteOrder, false);
     d->m_device->sendTrkMessage(TrkInstallFile2, TrkCallback(this, &Launcher::handleInstallPackageFinished), ba);
 }
 
@@ -911,13 +930,19 @@ void Launcher::handleInstallPackageFinished(const TrkResult &result)
             installRemotePackageByUser();
             return;
         }
-        emit canNotInstall(d->m_installFileName, result.errorString());
+        emit canNotInstall(d->m_installFileNames.at(d->m_currentInstallFileName), result.errorString());
         disconnectTrk();
         return;
-    } else {
-        emit installingFinished();
     }
-    if (d->m_startupActions & ActionRun) {
+
+    ++d->m_currentInstallFileName;
+
+    if (d->m_currentInstallFileName == d->m_installFileNames.size())
+        emit installingFinished();
+
+    if (d->m_startupActions & ActionInstall && d->m_currentInstallFileName < d->m_installFileNames.size()) {
+        installRemotePackage();
+    } else if (d->m_startupActions & ActionRun) {
         startInferiorIfNeeded();
     } else if (d->m_startupActions & ActionDownload) {
         copyFileFromRemote();
@@ -973,6 +998,7 @@ void Launcher::startInferiorIfNeeded()
         logMessage("Process already 'started'");
         return;
     }
+
     d->m_device->sendTrkMessage(TrkCreateItem, TrkCallback(this, &Launcher::handleCreateProcess),
                                 startProcessMessage(d->m_fileName, d->m_commandLineArgs)); // Create Item
 }
diff --git a/src/shared/symbianutils/launcher.h b/src/shared/symbianutils/launcher.h
index c2270f1402f..1a822f357bd 100644
--- a/src/shared/symbianutils/launcher.h
+++ b/src/shared/symbianutils/launcher.h
@@ -89,9 +89,9 @@ public:
     void setTrkServerName(const QString &name);
     QString trkServerName() const;
     void setFileName(const QString &name);
-    void setCopyFileName(const QString &srcName, const QString &dstName);
+    void setCopyFileNames(const QStringList &srcName, const QStringList &dstName);
     void setDownloadFileName(const QString &srcName, const QString &dstName);
-    void setInstallFileName(const QString &name);
+    void setInstallFileNames(const QStringList &names);
     void setCommandLineArgs(const QStringList &args);
     bool startServer(QString *errorMessage);
     void setInstallationMode(InstallationMode installation);
-- 
GitLab