Android: Deploy Qt live apk for QML projects

Task-number: QDS-16
Change-Id: Iafb1f7ea5d354e73020ff3c5175efa82f99bdffe
Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
This commit is contained in:
Vikas Pachdha
2018-06-18 13:40:02 +02:00
parent 18b2865834
commit f10002e3f5
5 changed files with 90 additions and 69 deletions

View File

@@ -93,7 +93,6 @@ namespace {
const QLatin1String OpenJDKLocationKey("OpenJDKLocation");
const QLatin1String KeystoreLocationKey("KeystoreLocation");
const QLatin1String AutomaticKitCreationKey("AutomatiKitCreation");
const QLatin1String DeviceQmlsceneCommandKey("DeviceQmlsceneCommand");
const QLatin1String MakeExtraSearchDirectory("MakeExtraSearchDirectory");
const QLatin1String PartitionSizeKey("PartitionSize");
const QLatin1String ToolchainHostKey("ToolchainHost");
@@ -126,8 +125,8 @@ namespace {
const QLatin1String sdkToolsVersionKey("Pkg.Revision");
const QLatin1String ndkRevisionKey("Pkg.Revision");
const QLatin1String defaultQmlScene("org.qtproject.example.qmlscene/"
"org.qtproject.qt5.android.bindings.QtActivity");
const QString defaultQtLiveApk = Core::ICore::resourcePath() +
("/qtlive/android/armv7/qt_live_viewer.apk");
static QString sdkSettingsFileName()
{
@@ -257,7 +256,6 @@ void AndroidConfig::load(const QSettings &settings)
m_keystoreLocation = FileName::fromString(settings.value(KeystoreLocationKey).toString());
m_toolchainHost = settings.value(ToolchainHostKey).toString();
m_automaticKitCreation = settings.value(AutomaticKitCreationKey, true).toBool();
m_deviceQmlsceneCommand = settings.value(DeviceQmlsceneCommandKey, defaultQmlScene).toString();
QString extraDirectory = settings.value(MakeExtraSearchDirectory).toString();
m_makeExtraSearchDirectories.clear();
if (!extraDirectory.isEmpty())
@@ -274,7 +272,6 @@ void AndroidConfig::load(const QSettings &settings)
m_keystoreLocation = FileName::fromString(reader.restoreValue(KeystoreLocationKey, m_keystoreLocation.toString()).toString());
m_toolchainHost = reader.restoreValue(ToolchainHostKey, m_toolchainHost).toString();
m_automaticKitCreation = reader.restoreValue(AutomaticKitCreationKey, m_automaticKitCreation).toBool();
m_deviceQmlsceneCommand = reader.restoreValue(DeviceQmlsceneCommandKey, m_deviceQmlsceneCommand).toString();
QString extraDirectory = reader.restoreValue(MakeExtraSearchDirectory).toString();
m_makeExtraSearchDirectories.clear();
if (!extraDirectory.isEmpty())
@@ -298,7 +295,6 @@ void AndroidConfig::save(QSettings &settings) const
settings.setValue(KeystoreLocationKey, m_keystoreLocation.toString());
settings.setValue(PartitionSizeKey, m_partitionSize);
settings.setValue(AutomaticKitCreationKey, m_automaticKitCreation);
settings.setValue(DeviceQmlsceneCommandKey, m_deviceQmlsceneCommand);
settings.setValue(ToolchainHostKey, m_toolchainHost);
settings.setValue(MakeExtraSearchDirectory,
m_makeExtraSearchDirectories.isEmpty() ? QString()
@@ -876,14 +872,12 @@ void AndroidConfig::setAutomaticKitCreation(bool b)
m_automaticKitCreation = b;
}
QString AndroidConfig::deviceQmlsceneCommand() const
FileName AndroidConfig::qtLiveApkPath() const
{
return m_deviceQmlsceneCommand;
}
void AndroidConfig::setDeviceQmlsceneCommand(const QString &qmlsceneCommand)
{
m_deviceQmlsceneCommand = qmlsceneCommand;
QString apkPathStr(defaultQtLiveApk);
if (qEnvironmentVariableIsSet("QTC_QT_LIVE_APK_PATH"))
apkPathStr = QString::fromLocal8Bit(qgetenv("QTC_QT_LIVE_APK_PATH"));
return Utils::FileName::fromString(apkPathStr);
}
///////////////////////////////////

View File

@@ -124,8 +124,7 @@ public:
bool automaticKitCreation() const;
void setAutomaticKitCreation(bool b);
QString deviceQmlsceneCommand() const;
void setDeviceQmlsceneCommand(const QString &qmlsceneCommand);
Utils::FileName qtLiveApkPath() const;
Utils::FileName adbToolPath() const;
Utils::FileName androidToolPath() const;
@@ -182,7 +181,6 @@ private:
QStringList m_makeExtraSearchDirectories;
unsigned m_partitionSize = 1024;
bool m_automaticKitCreation = true;
QString m_deviceQmlsceneCommand;
//caches
mutable bool m_NdkInformationUpToDate = false;

View File

@@ -43,6 +43,7 @@
#include <projectexplorer/buildsteplist.h>
#include <projectexplorer/projectexplorerconstants.h>
#include <projectexplorer/project.h>
#include <projectexplorer/runconfiguration.h>
#include <projectexplorer/target.h>
#include <projectexplorer/toolchain.h>
@@ -72,6 +73,7 @@ const QLatin1String InstallFailedUpdateIncompatible("INSTALL_FAILED_UPDATE_INCOM
const QLatin1String InstallFailedPermissionModelDowngrade("INSTALL_FAILED_PERMISSION_MODEL_DOWNGRADE");
const QLatin1String InstallFailedVersionDowngrade("INSTALL_FAILED_VERSION_DOWNGRADE");
const Core::Id AndroidDeployQtStep::Id("Qt4ProjectManager.AndroidDeployQtStep");
static const char *qmlProjectRunConfigIdName = "QmlProjectManager.QmlRunConfiguration";
// AndroidDeployQtStepFactory
@@ -121,10 +123,17 @@ bool AndroidDeployQtStep::init(QList<const BuildStep *> &earlierSteps)
return false;
}
AndroidBuildApkStep *androidBuildApkStep
= AndroidGlobal::buildStep<AndroidBuildApkStep>(buildConfiguration());
if (!androidBuildApkStep)
RunConfiguration *rc = target()->activeRunConfiguration();
QTC_ASSERT(rc, return false);
const bool deployQtLive = rc->id().name().startsWith(qmlProjectRunConfigIdName);
ProjectExplorer::BuildConfiguration *bc = buildConfiguration();
QTC_ASSERT(deployQtLive || bc, return false);
AndroidBuildApkStep *androidBuildApkStep = AndroidGlobal::buildStep<AndroidBuildApkStep>(bc);
if (!androidBuildApkStep && !deployQtLive) {
emit addOutput(tr("Cannot find the android build step."), OutputFormat::Stderr);
return false;
}
int deviceAPILevel = AndroidManager::minimumSDK(target());
AndroidDeviceInfo info = earlierDeviceInfo(earlierSteps, Id);
@@ -140,40 +149,9 @@ bool AndroidDeployQtStep::init(QList<const BuildStep *> &earlierSteps)
m_serialNumber = info.serialNumber;
qCDebug(deployStepLog) << "AVD:" << m_avdName << "Device Serial:" << m_serialNumber;
ProjectExplorer::BuildConfiguration *bc = buildConfiguration();
m_filesToPull.clear();
QString buildDir = bc ? bc->buildDirectory().toString() : QString();
if (bc && !buildDir.endsWith("/")) {
buildDir += "/";
}
if (!deployQtLive)
gatherFilesToPull();
ProjectExplorer::ToolChain *tc = ProjectExplorer::ToolChainKitInformation::toolChain(
target()->kit(), ProjectExplorer::Constants::CXX_LANGUAGE_ID);
qCDebug(deployStepLog) << "Device CPU ABI: " << info.cpuAbi.join(", ");
qCDebug(deployStepLog) << "TC ABI: " << (tc ? tc->targetAbi().toString() : "No Toolchain");
QString linkerName("linker");
QString libDirName("lib");
if (info.cpuAbi.contains(QLatin1String("arm64-v8a")) ||
info.cpuAbi.contains(QLatin1String("x86_64"))) {
if (tc && tc->targetAbi().wordWidth() == 64) {
m_filesToPull["/system/bin/app_process64"] = buildDir + "app_process";
libDirName = "lib64";
linkerName = "linker64";
} else {
m_filesToPull["/system/bin/app_process32"] = buildDir + "app_process";
}
} else {
m_filesToPull["/system/bin/app_process32"] = buildDir + "app_process";
m_filesToPull["/system/bin/app_process"] = buildDir + "app_process";
}
m_filesToPull["/system/bin/" + linkerName] = buildDir + linkerName;
m_filesToPull["/system/" + libDirName + "/libc.so"] = buildDir + "libc.so";
qCDebug(deployStepLog) << "Files to pull from device:";
for (auto itr = m_filesToPull.constBegin(); itr != m_filesToPull.constEnd(); ++itr)
qCDebug(deployStepLog) << itr.key() << "to" << itr.value();
AndroidManager::setDeviceSerialNumber(target(), m_serialNumber);
AndroidManager::setDeviceApiLevel(target(), info.sdk);
@@ -186,7 +164,8 @@ bool AndroidDeployQtStep::init(QList<const BuildStep *> &earlierSteps)
m_manifestName = AndroidManager::manifestPath(target());
AndroidQtSupport *qtSupport = AndroidManager::androidQtSupport(target());
m_useAndroiddeployqt = qtSupport && version->qtVersion() >= QtSupport::QtVersionNumber(5, 4, 0);
m_useAndroiddeployqt = !deployQtLive && qtSupport &&
version->qtVersion() >= QtSupport::QtVersionNumber(5, 4, 0);
if (m_useAndroiddeployqt) {
m_command = version->qmakeProperty("QT_HOST_BINS");
@@ -199,8 +178,7 @@ bool AndroidDeployQtStep::init(QList<const BuildStep *> &earlierSteps)
m_command += QLatin1Char('/');
m_command += Utils::HostOsInfo::withExecutableSuffix(QLatin1String("androiddeployqt"));
m_workingDirectory = bc ? bc->buildDirectory().appendPath(QLatin1String(Constants::ANDROID_BUILDDIRECTORY)).toString()
: QString();
m_workingDirectory = bc->buildDirectory().appendPath(QLatin1String(Constants::ANDROID_BUILDDIRECTORY)).toString();
Utils::QtcProcess::addArg(&m_androiddeployqtArgs, QLatin1String("--verbose"));
Utils::QtcProcess::addArg(&m_androiddeployqtArgs, QLatin1String("--output"));
@@ -233,7 +211,9 @@ bool AndroidDeployQtStep::init(QList<const BuildStep *> &earlierSteps)
} else {
m_uninstallPreviousPackageRun = true;
m_command = AndroidConfigurations::currentConfig().adbToolPath().toString();
m_apkPath = qtSupport ? qtSupport->apkPath(target()).toString() : QString();
const AndroidConfig &config = AndroidConfigurations::currentConfig();
m_apkPath = deployQtLive ? config.qtLiveApkPath() :
(qtSupport ? qtSupport->apkPath(target()) : Utils::FileName());
m_workingDirectory = bc ? bc->buildDirectory().toString() : QString();
}
m_environment = bc ? bc->environment() : Utils::Environment();
@@ -262,14 +242,24 @@ AndroidDeployQtStep::DeployErrorCode AndroidDeployQtStep::runDeploy(QFutureInter
Utils::QtcProcess::addArg(&args, m_serialNumber);
}
} else {
RunConfiguration *rc = target()->activeRunConfiguration();
QTC_ASSERT(rc, return DeployErrorCode::Failure);
const bool deployQtLive = rc->id().name().startsWith(qmlProjectRunConfigIdName);
QString packageName;
if (deployQtLive) {
AndroidManager::apkInfo(m_apkPath, &packageName);
if (AndroidManager::packageInstalled(m_serialNumber, packageName))
return DeployErrorCode::NoError;
}
if (m_uninstallPreviousPackageRun) {
qCDebug(deployStepLog) << "Uninstalling previous package";
const QString packageName = AndroidManager::packageName(m_manifestName);
if (!deployQtLive)
packageName = AndroidManager::packageName(m_manifestName);
if (packageName.isEmpty()) {
emit addOutput(tr("Cannot find the package name."), OutputFormat::Stderr);
return Failure;
}
qCDebug(deployStepLog) << "Uninstalling previous package";
emit addOutput(tr("Uninstall previous package %1.").arg(packageName), OutputFormat::NormalMessage);
runCommand(m_adbPath,
AndroidDeviceInfo::adbSelector(m_serialNumber)
@@ -281,7 +271,7 @@ AndroidDeployQtStep::DeployErrorCode AndroidDeployQtStep::runDeploy(QFutureInter
Utils::QtcProcess::addArg(&args, QLatin1String("install"));
Utils::QtcProcess::addArg(&args, QLatin1String("-r"));
Utils::QtcProcess::addArg(&args, m_apkPath);
Utils::QtcProcess::addArg(&args, m_apkPath.toString());
}
m_process = new Utils::QtcProcess;
@@ -407,12 +397,6 @@ void AndroidDeployQtStep::run(QFutureInterface<bool> &fi)
emit setSerialNumber(serialNumber);
}
if (!buildConfiguration()) { // nothing to deploy
qCDebug(deployStepLog) << "Skip deployment. No build configuration. (e.g. *.qmlproject)";
reportRunResult(fi, true);
return;
}
DeployErrorCode returnValue = runDeploy(fi);
if (returnValue > DeployErrorCode::NoError && returnValue < DeployErrorCode::Failure) {
emit askForUninstall(returnValue);
@@ -422,7 +406,9 @@ void AndroidDeployQtStep::run(QFutureInterface<bool> &fi)
}
}
if (!m_filesToPull.isEmpty())
emit addOutput(tr("Pulling files necessary for debugging."), OutputFormat::NormalMessage);
for (auto itr = m_filesToPull.constBegin(); itr != m_filesToPull.constEnd(); ++itr) {
QFile::remove(itr.value());
runCommand(m_adbPath,
@@ -438,6 +424,44 @@ void AndroidDeployQtStep::run(QFutureInterface<bool> &fi)
reportRunResult(fi, returnValue == NoError);
}
void AndroidDeployQtStep::gatherFilesToPull()
{
m_filesToPull.clear();
ProjectExplorer::BuildConfiguration *bc = buildConfiguration();
QString buildDir = bc ? bc->buildDirectory().toString() : QString();
if (bc && !buildDir.endsWith("/")) {
buildDir += "/";
}
if (!m_deviceInfo.isValid())
return;
QString linkerName("linker");
QString libDirName("lib");
if (m_deviceInfo.cpuAbi.contains(QLatin1String("arm64-v8a")) ||
m_deviceInfo.cpuAbi.contains(QLatin1String("x86_64"))) {
const Core::Id cxxLanguageId = ProjectExplorer::Constants::CXX_LANGUAGE_ID;
ToolChain *tc = ToolChainKitInformation::toolChain(target()->kit(), cxxLanguageId);
if (tc && tc->targetAbi().wordWidth() == 64) {
m_filesToPull["/system/bin/app_process64"] = buildDir + "app_process";
libDirName = "lib64";
linkerName = "linker64";
} else {
m_filesToPull["/system/bin/app_process32"] = buildDir + "app_process";
}
} else {
m_filesToPull["/system/bin/app_process32"] = buildDir + "app_process";
m_filesToPull["/system/bin/app_process"] = buildDir + "app_process";
}
m_filesToPull["/system/bin/" + linkerName] = buildDir + linkerName;
m_filesToPull["/system/" + libDirName + "/libc.so"] = buildDir + "libc.so";
qCDebug(deployStepLog) << "Files to pull from device:";
for (auto itr = m_filesToPull.constBegin(); itr != m_filesToPull.constEnd(); ++itr)
qCDebug(deployStepLog) << itr.key() << "to" << itr.value();
}
void AndroidDeployQtStep::runCommand(const QString &program, const QStringList &arguments)
{
Utils::SynchronousProcess buildProc;

View File

@@ -91,6 +91,7 @@ private:
bool init(QList<const BuildStep *> &earlierSteps) override;
void run(QFutureInterface<bool> &fi) override;
void gatherFilesToPull();
DeployErrorCode runDeploy(QFutureInterface<bool> &fi);
void slotAskForUninstall(DeployErrorCode errorCode);
void slotSetSerialNumber(const QString &serialNumber);
@@ -113,7 +114,7 @@ private:
Utils::FileName m_manifestName;
QString m_serialNumber;
QString m_avdName;
QString m_apkPath;
Utils::FileName m_apkPath;
QMap<QString, QString> m_filesToPull;
QString m_targetArch;

View File

@@ -27,6 +27,7 @@
#include "androidconstants.h"
#include "androidsignaloperation.h"
#include "androidconfigurations.h"
#include "androidmanager.h"
#include <projectexplorer/runconfiguration.h>
@@ -47,7 +48,10 @@ AndroidDevice::AndroidDevice()
{
setDisplayName(QCoreApplication::translate("Android::Internal::AndroidDevice", "Run on Android"));
setDeviceState(DeviceReadyToUse);
setQmlsceneCommand(AndroidConfigurations::currentConfig().deviceQmlsceneCommand());
QString activityPath;
const AndroidConfig &config = AndroidConfigurations::currentConfig();
AndroidManager::apkInfo(config.qtLiveApkPath(), nullptr, nullptr, &activityPath);
setQmlsceneCommand(activityPath);
}
AndroidDevice::AndroidDevice(const AndroidDevice &other)