Commit 05ae17ea authored by dt's avatar dt
Browse files

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.
parent 253cbf3c
......@@ -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;
......
......@@ -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
......
......@@ -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)) {
......
......@@ -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;
......
......@@ -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 =
......
......@@ -82,15 +82,14 @@ public:
char installationDrive() const;
void setInstallationDrive(char drive);