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>
This commit is contained in:
Daniel Teske
2013-01-22 16:03:31 +01:00
parent 259145f080
commit dc40099d0b
5 changed files with 223 additions and 34 deletions

View File

@@ -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) {