summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Teske <daniel.teske@digia.com>2013-01-22 16:03:31 +0100
committerDaniel Teske <daniel.teske@digia.com>2013-01-24 15:07:07 +0100
commitdc40099d0b3d4a4fa56501d0927a59c2233d7642 (patch)
treee5dab901a1e8978d9ec87e46b09879ef93f1c562
parent259145f08010242d23938b314c7592d9f5ba930d (diff)
downloadqt-creator-dc40099d0b3d4a4fa56501d0927a59c2233d7642.tar.gz
Android: Make local deployment smart
On deploying, first check the timestamps of the files on the device, and then only copy+strip+deploy the files that are newer. Add a button for cleaning /data/local/tmp/qt as that is no longer done on each deployment. Task-number: QTCREATORBUG-8538 Change-Id: I93c4e6266d7d748b0a4735e00806282315227d4b Reviewed-by: BogDan Vatra <bogdan@kde.org> Reviewed-by: Daniel Teske <daniel.teske@digia.com>
-rw-r--r--src/plugins/android/androiddeploystep.cpp208
-rw-r--r--src/plugins/android/androiddeploystep.h33
-rw-r--r--src/plugins/android/androiddeploystepwidget.cpp6
-rw-r--r--src/plugins/android/androiddeploystepwidget.h1
-rw-r--r--src/plugins/android/androiddeploystepwidget.ui9
5 files changed, 223 insertions, 34 deletions
diff --git a/src/plugins/android/androiddeploystep.cpp b/src/plugins/android/androiddeploystep.cpp
index 03853c59bc..828205cabb 100644
--- a/src/plugins/android/androiddeploystep.cpp
+++ b/src/plugins/android/androiddeploystep.cpp
@@ -36,6 +36,7 @@
#include "androidrunconfiguration.h"
#include "androidmanager.h"
+#include <coreplugin/messagemanager.h>
#include <projectexplorer/buildconfiguration.h>
#include <projectexplorer/projectexplorerconstants.h>
#include <projectexplorer/target.h>
@@ -56,6 +57,7 @@ namespace Android {
namespace Internal {
static const char USE_LOCAL_QT_KEY[] = "Qt4ProjectManager.AndroidDeployStep.UseLocalQtLibs";
+static const char DEPLOY_ACTION_KEY[] = "Qt4ProjectManager.AndroidDeployStep.DeployAction";
const Core::Id AndroidDeployStep::Id("Qt4ProjectManager.AndroidDeployStep");
@@ -138,6 +140,9 @@ bool AndroidDeployStep::useLocalQtLibs()
bool AndroidDeployStep::fromMap(const QVariantMap &map)
{
m_useLocalQtLibs = map.value(QLatin1String(USE_LOCAL_QT_KEY), false).toBool();
+ m_deployAction = AndroidDeployAction(map.value(QLatin1String(DEPLOY_ACTION_KEY), NoDeploy).toInt());
+ if (m_deployAction == InstallQASI)
+ m_deployAction = NoDeploy;
return ProjectExplorer::BuildStep::fromMap(map);
}
@@ -145,9 +150,40 @@ QVariantMap AndroidDeployStep::toMap() const
{
QVariantMap map = ProjectExplorer::BuildStep::toMap();
map.insert(QLatin1String(USE_LOCAL_QT_KEY), m_useLocalQtLibs);
+ map.insert(QLatin1String(DEPLOY_ACTION_KEY), m_deployAction);
return map;
}
+void AndroidDeployStep::cleanLibsOnDevice()
+{
+ const QString targetSDK = AndroidManager::targetSDK(target());
+
+ int deviceAPILevel = targetSDK.mid(targetSDK.indexOf(QLatin1Char('-')) + 1).toInt();
+ QString deviceSerialNumber = AndroidConfigurations::instance().getDeployDeviceSerialNumber(&deviceAPILevel);
+ if (!deviceSerialNumber.length()) {
+ Core::MessageManager::instance()->printToOutputPanePopup(tr("Could not run adb. No device found."));
+ return;
+ }
+ QProcess *process = new QProcess(this);
+ QStringList arguments;
+ arguments << QLatin1String("-s") << deviceSerialNumber
+ << QLatin1String("shell") << QLatin1String("rm") << QLatin1String("-r") << QLatin1String("/data/local/tmp/qt");
+ connect(process, SIGNAL(finished(int)), this, SLOT(cleanLibsFinished()));
+ const QString adb = AndroidConfigurations::instance().adbToolPath().toString();
+ Core::MessageManager::instance()->printToOutputPanePopup(adb + QLatin1String(" ")
+ + arguments.join(QLatin1String(" ")));
+ process->start(adb, arguments);
+}
+
+void AndroidDeployStep::cleanLibsFinished()
+{
+ QProcess *process = qobject_cast<QProcess *>(sender());
+ if (!process)
+ return;
+ Core::MessageManager::instance()->printToOutputPanePopup(QString::fromLocal8Bit(process->readAll()));
+ Core::MessageManager::instance()->printToOutputPane(tr("adb finished with exit code %1.").arg(process->exitCode()));
+}
+
void AndroidDeployStep::setDeployAction(AndroidDeployStep::AndroidDeployAction deploy)
{
m_deployAction = deploy;
@@ -222,24 +258,122 @@ Utils::FileName AndroidDeployStep::localLibsRulesFilePath()
return AndroidManager::localLibsRulesFilePath(target());
}
-void AndroidDeployStep::copyLibs(const QString &srcPath, const QString &destPath, QStringList &copiedLibs, const QStringList &filter)
+unsigned int AndroidDeployStep::remoteModificationTime(const QString &fullDestination, QHash<QString, unsigned int> *cache)
{
- QDir dir;
- dir.mkpath(destPath);
- QDirIterator libsIt(srcPath, filter, QDir::NoFilter, QDirIterator::Subdirectories);
- int pos = srcPath.size();
+ QString destination = QFileInfo(fullDestination).absolutePath();
+ QProcess process;
+ QHash<QString, unsigned int>::const_iterator it = cache->find(fullDestination);
+ if (it != cache->constEnd())
+ return *it;
+ QStringList arguments;
+ arguments << QLatin1String("-s") << m_deviceSerialNumber
+ << QLatin1String("ls") << destination;
+ process.start(AndroidConfigurations::instance().adbToolPath().toString(), arguments);
+ process.waitForFinished(-1);
+ if (process.error() != QProcess::UnknownError
+ || process.exitCode() != 0)
+ return -1;
+ QByteArray output = process.readAll();
+ output.replace("\r\n", "\n");
+ QList<QByteArray> lines = output.split('\n');
+ foreach (const QByteArray &line, lines) {
+ // do some checks if we got what we expected..
+ if (line.count() < (3 * 8 + 3))
+ continue;
+ if (line.at(8) != ' '
+ || line.at(17) != ' '
+ || line.at(26) != ' ')
+ continue;
+ bool ok;
+ int time = line.mid(18, 8).toUInt(&ok, 16);
+ if (!ok)
+ continue;
+ QString fileName = QString::fromLocal8Bit(line.mid(27));
+ cache->insert(destination + QLatin1Char('/') + fileName, time);
+ }
+ it = cache->find(fullDestination);
+ if (it != cache->constEnd())
+ return *it;
+ return 0;
+}
+
+void AndroidDeployStep::collectFiles(QList<DeployItem> *deployList, const QString &localPath, const QString &remotePath,
+ bool strip, const QStringList &filter)
+{
+ QDirIterator libsIt(localPath, filter, QDir::NoFilter, QDirIterator::Subdirectories);
+ int pos = localPath.size();
while (libsIt.hasNext()) {
libsIt.next();
- const QString destFile(destPath + libsIt.filePath().mid(pos));
- if (libsIt.fileInfo().isDir()) {
- dir.mkpath(destFile);
- } else {
- QFile::copy(libsIt.filePath(), destFile);
- copiedLibs.append(destFile);
+ const QString destFile(remotePath + libsIt.filePath().mid(pos));
+ if (!libsIt.fileInfo().isDir()) {
+ deployList->append(DeployItem(libsIt.filePath(),
+ libsIt.fileInfo().lastModified().toTime_t(),
+ destFile, strip));
}
}
}
+void AndroidDeployStep::fetchRemoteModificationTimes(QList<DeployItem> *deployList)
+{
+ QHash<QString, unsigned int> cache;
+ for (int i = 0; i < deployList->count(); ++i) {
+ DeployItem &item = (*deployList)[i];
+ item.remoteTimeStamp
+ = remoteModificationTime(item.remoteFileName, &cache);
+ }
+}
+
+void AndroidDeployStep::filterModificationTimes(QList<DeployItem> *deployList)
+{
+ QList<DeployItem>::iterator it = deployList->begin();
+ while (it != deployList->end()) {
+ int index = it - deployList->begin();
+ Q_UNUSED(index);
+ if ((*it).localTimeStamp <= (*it).remoteTimeStamp)
+ it = deployList->erase(it);
+ else
+ ++it;
+ }
+}
+
+void AndroidDeployStep::copyFilesToTemp(QList<DeployItem> *deployList, const QString &tempDirectory, const QString &sourcePrefix)
+{
+ QDir dir;
+
+ int pos = sourcePrefix.size();
+ for (int i = 0; i < deployList->count(); ++i) {
+ DeployItem &item = (*deployList)[i];
+ if (!item.needsStrip)
+ continue;
+ const QString destFile(tempDirectory + item.localFileName.mid(pos));
+ dir.mkpath(QFileInfo(destFile).absolutePath());
+ QFile::copy(item.localFileName, destFile);
+ item.localFileName = destFile;
+ }
+}
+
+void AndroidDeployStep::stripFiles(const QList<DeployItem> &deployList, Abi::Architecture architecture)
+{
+ QProcess stripProcess;
+ foreach (const DeployItem &item, deployList) {
+ stripProcess.start(AndroidConfigurations::instance().stripPath(architecture).toString(),
+ QStringList()<<QLatin1String("--strip-unneeded") << item.localFileName);
+ stripProcess.waitForStarted();
+ if (!stripProcess.waitForFinished())
+ stripProcess.kill();
+ }
+}
+
+void AndroidDeployStep::deployFiles(QProcess *process, const QList<DeployItem> &deployList)
+{
+ foreach (const DeployItem &item, deployList) {
+ runCommand(process, AndroidConfigurations::instance().adbToolPath().toString(),
+ QStringList() << QLatin1String("-s") << m_deviceSerialNumber
+ << QLatin1String("push") << item.localFileName
+ << item.remoteFileName);
+ }
+}
+
bool AndroidDeployStep::deployPackage()
{
QProcess *const deployProc = new QProcess;
@@ -249,31 +383,41 @@ bool AndroidDeployStep::deployPackage()
SLOT(handleBuildError()));
if (m_runDeployAction == DeployLocal) {
- writeOutput(tr("Clean old Qt libraries"));
- runCommand(deployProc, AndroidConfigurations::instance().adbToolPath().toString(),
- QStringList() << QLatin1String("-s") << m_deviceSerialNumber
- << QLatin1String("shell") << QLatin1String("rm") << QLatin1String("-r") << QLatin1String("/data/local/tmp/qt"));
-
writeOutput(tr("Deploy Qt libraries. This may take some time, please wait."));
const QString tempPath = QDir::tempPath() + QLatin1String("/android_qt_libs_") + m_packageName;
AndroidPackageCreationStep::removeDirectory(tempPath);
- QStringList stripFiles;
- copyLibs(m_qtVersionSourcePath + QLatin1String("/lib"),
- tempPath + QLatin1String("/lib"), stripFiles, QStringList() << QLatin1String("*.so"));
- copyLibs(m_qtVersionSourcePath + QLatin1String("/plugins"),
- tempPath + QLatin1String("/plugins"), stripFiles);
- copyLibs(m_qtVersionSourcePath + QLatin1String("/imports"),
- tempPath + QLatin1String("/imports"), stripFiles);
- copyLibs(m_qtVersionSourcePath + QLatin1String("/qml"),
- tempPath + QLatin1String("/qml"), stripFiles);
- copyLibs(m_qtVersionSourcePath + QLatin1String("/jar"),
- tempPath + QLatin1String("/jar"), stripFiles);
- AndroidPackageCreationStep::stripAndroidLibs(stripFiles, target()->activeRunConfiguration()->abi().architecture());
- runCommand(deployProc, AndroidConfigurations::instance().adbToolPath().toString(),
- QStringList() << QLatin1String("-s") << m_deviceSerialNumber
- << QLatin1String("push") << tempPath << QLatin1String("/data/local/tmp/qt"));
+
+ const QString remoteRoot = QLatin1String("/data/local/tmp/qt");
+ QList<DeployItem> deployList;
+ collectFiles(&deployList,
+ m_qtVersionSourcePath + QLatin1String("/lib"),
+ remoteRoot + QLatin1String("/lib"),
+ true,
+ QStringList() << QLatin1String("*.so"));
+ collectFiles(&deployList,
+ m_qtVersionSourcePath + QLatin1String("/plugins"),
+ remoteRoot + QLatin1String("/plugins"),
+ true);
+ collectFiles(&deployList,
+ m_qtVersionSourcePath + QLatin1String("/imports"),
+ remoteRoot + QLatin1String("/imports"),
+ true);
+ collectFiles(&deployList,
+ m_qtVersionSourcePath + QLatin1String("/qml"),
+ remoteRoot + QLatin1String("/qml"),
+ true);
+ collectFiles(&deployList,
+ m_qtVersionSourcePath + QLatin1String("/jar"),
+ remoteRoot + QLatin1String("/jar"),
+ true);
+
+ fetchRemoteModificationTimes(&deployList);
+ filterModificationTimes(&deployList);
+ copyFilesToTemp(&deployList, tempPath, m_qtVersionSourcePath);
+ stripFiles(deployList, target()->activeRunConfiguration()->abi().architecture());
+ deployFiles(deployProc, deployList);
+
AndroidPackageCreationStep::removeDirectory(tempPath);
- emit (resetDelopyAction());
}
if (m_runDeployAction == InstallQASI) {
diff --git a/src/plugins/android/androiddeploystep.h b/src/plugins/android/androiddeploystep.h
index 7668327693..6d21a80750 100644
--- a/src/plugins/android/androiddeploystep.h
+++ b/src/plugins/android/androiddeploystep.h
@@ -47,6 +47,26 @@ namespace Internal {
class AndroidDeviceConfigListModel;
class AndroidPackageCreationStep;
+class DeployItem
+{
+public:
+ DeployItem(const QString &_localFileName,
+ unsigned int _localTimeStamp,
+ const QString &_remoteFileName,
+ bool _needsStrip)
+ : localFileName(_localFileName),
+ remoteFileName(_remoteFileName),
+ localTimeStamp(_localTimeStamp),
+ remoteTimeStamp(0),
+ needsStrip(_needsStrip)
+ {}
+ QString localFileName;
+ QString remoteFileName;
+ unsigned int localTimeStamp;
+ unsigned int remoteTimeStamp;
+ bool needsStrip;
+};
+
class AndroidDeployStep : public ProjectExplorer::BuildStep
{
Q_OBJECT
@@ -75,6 +95,8 @@ public:
bool fromMap(const QVariantMap &map);
QVariantMap toMap() const;
+ void cleanLibsOnDevice();
+
public slots:
void setDeployAction(AndroidDeployAction deploy);
void setDeployQASIPackagePath(const QString &package);
@@ -89,6 +111,7 @@ private slots:
bool deployPackage();
void handleBuildOutput();
void handleBuildError();
+ void cleanLibsFinished();
private:
AndroidDeployStep(ProjectExplorer::BuildStepList *bc,
@@ -98,11 +121,19 @@ private:
virtual ProjectExplorer::BuildStepConfigWidget *createConfigWidget();
virtual bool immutable() const { return true; }
- void copyLibs(const QString &srcPath, const QString &destPath, QStringList &copiedLibs, const QStringList &filter = QStringList());
void ctor();
void raiseError(const QString &error);
void writeOutput(const QString &text, OutputFormat = MessageOutput);
bool runCommand(QProcess *buildProc, const QString &program, const QStringList &arguments);
+ unsigned int remoteModificationTime(const QString &fullDestination,
+ QHash<QString, unsigned int> *cache);
+ void collectFiles(QList<DeployItem> *deployList, const QString &localPath,
+ const QString &remotePath, bool strip, const QStringList &filter = QStringList());
+ void filterModificationTimes(QList<DeployItem> *deployList);
+ void copyFilesToTemp(QList<DeployItem> *deployList, const QString &tempDirectory, const QString &sourcePrefix);
+ void fetchRemoteModificationTimes(QList<DeployItem> *deployList);
+ void stripFiles(const QList<DeployItem> &deployList, ProjectExplorer::Abi::Architecture architecture);
+ void deployFiles(QProcess *process, const QList<DeployItem> &deployList);
private:
QString m_deviceSerialNumber;
diff --git a/src/plugins/android/androiddeploystepwidget.cpp b/src/plugins/android/androiddeploystepwidget.cpp
index 8f168b61f9..1149972a26 100644
--- a/src/plugins/android/androiddeploystepwidget.cpp
+++ b/src/plugins/android/androiddeploystepwidget.cpp
@@ -63,6 +63,7 @@ AndroidDeployStepWidget::AndroidDeployStepWidget(AndroidDeployStep *step) :
connect(ui->chooseButton, SIGNAL(clicked()), SLOT(setQASIPackagePath()));
connect(ui->useLocalQtLibs, SIGNAL(stateChanged(int)), SLOT(useLocalQtLibsStateChanged(int)));
connect(ui->editRulesFilePushButton, SIGNAL(clicked()), SLOT(editRulesFile()));
+ connect(ui->cleanLibsPushButton, SIGNAL(clicked()), SLOT(cleanLibsOnDevice()));
}
AndroidDeployStepWidget::~AndroidDeployStepWidget()
@@ -110,5 +111,10 @@ void AndroidDeployStepWidget::editRulesFile()
Core::ICore::instance()->openFiles(QStringList() << m_step->localLibsRulesFilePath().toString(), Core::ICore::SwitchMode);
}
+void AndroidDeployStepWidget::cleanLibsOnDevice()
+{
+ m_step->cleanLibsOnDevice();
+}
+
} // namespace Internal
} // namespace Qt4ProjectManager
diff --git a/src/plugins/android/androiddeploystepwidget.h b/src/plugins/android/androiddeploystepwidget.h
index 387f0f35d5..ed6cd489b4 100644
--- a/src/plugins/android/androiddeploystepwidget.h
+++ b/src/plugins/android/androiddeploystepwidget.h
@@ -54,6 +54,7 @@ private slots:
void setQASIPackagePath();
void useLocalQtLibsStateChanged(int);
void editRulesFile();
+ void cleanLibsOnDevice();
private:
virtual QString summaryText() const;
diff --git a/src/plugins/android/androiddeploystepwidget.ui b/src/plugins/android/androiddeploystepwidget.ui
index dd7c51e784..3732f7a501 100644
--- a/src/plugins/android/androiddeploystepwidget.ui
+++ b/src/plugins/android/androiddeploystepwidget.ui
@@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>560</width>
- <height>136</height>
+ <height>145</height>
</rect>
</property>
<property name="sizePolicy">
@@ -57,6 +57,13 @@ You must have Qt libraries compiled for that platform</string>
</widget>
</item>
<item>
+ <widget class="QPushButton" name="cleanLibsPushButton">
+ <property name="text">
+ <string>Clean libs on device</string>
+ </property>
+ </widget>
+ </item>
+ <item>
<widget class="QPushButton" name="editRulesFilePushButton">
<property name="text">
<string>Edit Rules File</string>