aboutsummaryrefslogtreecommitdiffstats
path: root/src/lib/corelib/api
diff options
context:
space:
mode:
authorJake Petroules <jake.petroules@qt.io>2017-09-29 09:18:58 -0700
committerJake Petroules <jake.petroules@qt.io>2017-10-04 22:09:17 +0000
commit9d1a039f674417add84105dd75b58a1151ce450b (patch)
treeee726e86c3f41c736b5119ded4e9b01136110c6a /src/lib/corelib/api
parent4e811eada68dc26a425b7a7e4e9b0d367ad95c74 (diff)
Add support for running Android apps on devices
Change-Id: I1a3a4afb9742f1c89e6396430d178277ea41c451 Reviewed-by: Christian Kandeler <christian.kandeler@qt.io>
Diffstat (limited to 'src/lib/corelib/api')
-rw-r--r--src/lib/corelib/api/project.cpp2
-rw-r--r--src/lib/corelib/api/projectdata.cpp3
-rw-r--r--src/lib/corelib/api/projectdata_p.h8
-rw-r--r--src/lib/corelib/api/runenvironment.cpp68
4 files changed, 77 insertions, 4 deletions
diff --git a/src/lib/corelib/api/project.cpp b/src/lib/corelib/api/project.cpp
index 19b2072f9..ad49a7f2c 100644
--- a/src/lib/corelib/api/project.cpp
+++ b/src/lib/corelib/api/project.cpp
@@ -760,7 +760,7 @@ RuleCommandList ProjectPrivate::ruleCommands(const ProductData &product,
static bool productIsRunnable(const ResolvedProductConstPtr &product)
{
- return product->fileTags.contains("application");
+ return isRunnableArtifact(product->fileTags);
}
static bool productIsMultiplexed(const ResolvedProductConstPtr &product)
diff --git a/src/lib/corelib/api/projectdata.cpp b/src/lib/corelib/api/projectdata.cpp
index b5e1ddd9d..03aa95f60 100644
--- a/src/lib/corelib/api/projectdata.cpp
+++ b/src/lib/corelib/api/projectdata.cpp
@@ -261,8 +261,7 @@ bool ArtifactData::isGenerated() const
*/
bool ArtifactData::isExecutable() const
{
- return d->fileTags.contains(QLatin1String("application"))
- || d->fileTags.contains(QLatin1String("msi"));
+ return Internal::isRunnableArtifact(Internal::FileTags::fromStringList(d->fileTags));
}
/*!
diff --git a/src/lib/corelib/api/projectdata_p.h b/src/lib/corelib/api/projectdata_p.h
index 58ae2ffea..41617fc81 100644
--- a/src/lib/corelib/api/projectdata_p.h
+++ b/src/lib/corelib/api/projectdata_p.h
@@ -40,6 +40,7 @@
#define QBS_PROJECTDATA_P_H
#include "projectdata.h"
+#include <language/filetags.h>
#include <QtCore/qshareddata.h>
@@ -138,6 +139,13 @@ public:
QString buildDir;
};
+static inline bool isRunnableArtifact(const FileTags &fileTags)
+{
+ return fileTags.contains("application")
+ || fileTags.contains("android.apk")
+ || fileTags.contains("msi");
+}
+
} // namespace Internal
} // namespace qbs
diff --git a/src/lib/corelib/api/runenvironment.cpp b/src/lib/corelib/api/runenvironment.cpp
index a2caea987..16c188442 100644
--- a/src/lib/corelib/api/runenvironment.cpp
+++ b/src/lib/corelib/api/runenvironment.cpp
@@ -60,6 +60,8 @@
#include <QtCore/qtemporaryfile.h>
#include <QtCore/qvariant.h>
+#include <QtXml/qdom.h>
+
#include <stdlib.h>
namespace qbs {
@@ -208,6 +210,34 @@ static QString findExecutable(const QStringList &fileNames)
return QString();
}
+static QString findMainIntent(const QString &aapt, const QString &apkFilePath)
+{
+ QString packageId;
+ QString activity;
+ QProcess aaptProcess;
+ aaptProcess.start(aapt, QStringList()
+ << QStringLiteral("dump")
+ << QStringLiteral("badging")
+ << apkFilePath);
+ if (aaptProcess.waitForFinished(-1)) {
+ for (auto line : aaptProcess.readAllStandardOutput().split('\n')) {
+ if (line.startsWith(QByteArrayLiteral("package:"))) {
+ QDomDocument doc;
+ doc.setContent(QByteArrayLiteral("<") + line + QByteArrayLiteral("/>"));
+ packageId = doc.firstChild().toElement().attribute(QStringLiteral("name"));
+ } else if (line.startsWith(QByteArrayLiteral("launchable-activity:"))) {
+ QDomDocument doc;
+ doc.setContent(QByteArrayLiteral("<") + line + QByteArrayLiteral("/>"));
+ activity = doc.firstChild().toElement().attribute(QStringLiteral("name"));
+ }
+ }
+ }
+
+ if (!packageId.isEmpty() && !activity.isEmpty())
+ return packageId + QStringLiteral("/") + activity;
+ return QString();
+}
+
int RunEnvironment::doRunTarget(const QString &targetBin, const QStringList &arguments)
{
const QStringList targetOS = d->resolvedProduct->moduleProperties->qbsPropertyValue(
@@ -219,7 +249,43 @@ int RunEnvironment::doRunTarget(const QString &targetBin, const QStringList &arg
QStringList targetArguments = arguments;
const QString completeSuffix = QFileInfo(targetBin).completeSuffix();
- if (targetOS.contains(QLatin1String("ios")) || targetOS.contains(QLatin1String("tvos"))) {
+ if (targetOS.contains(QLatin1String("android"))) {
+ const auto aapt = d->resolvedProduct->moduleProperties->moduleProperty(
+ QStringLiteral("Android.sdk"), QStringLiteral("aaptFilePath")).toString();
+ const auto intent = findMainIntent(aapt, targetBin);
+ const auto sdkDir = d->resolvedProduct->moduleProperties->moduleProperty(
+ QStringLiteral("Android.sdk"), QStringLiteral("sdkDir")).toString();
+ targetExecutable = sdkDir + QStringLiteral("/platform-tools/adb");
+
+ QProcess process;
+ process.setProcessChannelMode(QProcess::ForwardedChannels);
+ process.start(targetExecutable, QStringList()
+ << QStringLiteral("install")
+ << QStringLiteral("-r") // replace existing application
+ << QStringLiteral("-t") // allow test packages
+ << QStringLiteral("-d") // allow version code downgrade
+ << targetBin);
+ if (!process.waitForFinished()) {
+ if (process.error() == QProcess::FailedToStart) {
+ throw ErrorInfo(Tr::tr("The process '%1' could not be started: %2")
+ .arg(targetExecutable)
+ .arg(process.errorString()));
+ } else {
+ d->logger.qbsWarning()
+ << "QProcess error: " << process.errorString();
+ }
+
+ return EXIT_FAILURE;
+ }
+
+ targetArguments << QStringList()
+ << QStringLiteral("shell")
+ << QStringLiteral("am")
+ << QStringLiteral("start")
+ << QStringLiteral("-W") // wait for launch to complete
+ << QStringLiteral("-n")
+ << intent;
+ } else if (targetOS.contains(QLatin1String("ios")) || targetOS.contains(QLatin1String("tvos"))) {
const QString bundlePath = targetBin + QLatin1String("/..");
if (QFileInfo(targetExecutable = findExecutable(QStringList()