Android multi arch support

[ChangeLog][Android] Android multi arch support for qmake

Change-Id: Ib8b1874604a3392130c96fbc00b26713b3d788ae
Reviewed-by: hjk <hjk@qt.io>
This commit is contained in:
BogDan Vatra
2019-08-26 14:19:07 +03:00
committed by BogDan Vatra
parent 567a20843f
commit 6b31f9cf23
37 changed files with 431 additions and 464 deletions

View File

@@ -20,7 +20,8 @@ find_package(Qt5 COMPONENTS Core Quick REQUIRED)
@endif
if(ANDROID)
add_library(%{ProjectName} SHARED
set(TARGET %{ProjectName}_${ANDROID_ABI})
add_library(${TARGET} SHARED
%{MainCppFileName}
qml.qrc
@if %{HasTranslation}
@@ -28,7 +29,8 @@ if(ANDROID)
@endif
)
else()
add_executable(%{ProjectName}
set(TARGET %{ProjectName})
add_executable(${TARGET}
%{MainCppFileName}
qml.qrc
@if %{HasTranslation}
@@ -37,9 +39,9 @@ else()
)
endif()
target_compile_definitions(%{ProjectName}
target_compile_definitions(${TARGET}
PRIVATE $<$<OR:$<CONFIG:Debug>,$<CONFIG:RelWithDebInfo>>:QT_QML_DEBUG>)
target_link_libraries(%{ProjectName}
target_link_libraries(${TARGET}
PRIVATE Qt5::Core Qt5::Quick)
@if %{HasTranslation}

View File

@@ -18,7 +18,6 @@ add_qtc_plugin(Android
androiddevicedialog.cpp androiddevicedialog.h androiddevicedialog.ui
androiderrormessage.cpp androiderrormessage.h
androidextralibrarylistmodel.cpp androidextralibrarylistmodel.h
androidgdbserverkitinformation.cpp androidgdbserverkitinformation.h
androidglobal.h
androidmanager.cpp androidmanager.h
androidmanifestdocument.cpp androidmanifestdocument.h

View File

@@ -24,7 +24,6 @@ HEADERS += \
javaparser.h \
androidplugin.h \
androiddevice.h \
androidgdbserverkitinformation.h \
androidqmltoolingsupport.h \
androidmanifesteditorfactory.h \
androidmanifesteditor.h \
@@ -69,7 +68,6 @@ SOURCES += \
javaparser.cpp \
androidplugin.cpp \
androiddevice.cpp \
androidgdbserverkitinformation.cpp \
androidqmltoolingsupport.cpp \
androidmanifesteditorfactory.cpp \
androidmanifesteditor.cpp \

View File

@@ -49,8 +49,6 @@ Project {
"androiderrormessage.cpp",
"androidextralibrarylistmodel.cpp",
"androidextralibrarylistmodel.h",
"androidgdbserverkitinformation.cpp",
"androidgdbserverkitinformation.h",
"androidglobal.h",
"androidmanager.cpp",
"androidmanager.h",

View File

@@ -205,8 +205,9 @@ bool AndroidBuildApkStep::init()
setOutputParser(parser);
m_openPackageLocationForRun = m_openPackageLocation;
m_apkPath = AndroidManager::apkPath(target()).toString();
qCDebug(buildapkstepLog) << "APK path:" << m_apkPath;
m_packagePath = m_buildAAB ? AndroidManager::aabPath(target()).toString()
: AndroidManager::apkPath(target()).toString();
qCDebug(buildapkstepLog) << "Package path:" << m_packagePath;
if (!AbstractProcessStep::init())
return false;
@@ -246,6 +247,9 @@ bool AndroidBuildApkStep::init()
arguments << "--gradle";
if (m_buildAAB)
arguments << "--aab" << "--jarsigner";
if (m_useMinistro)
arguments << "--deployment" << "ministro";
@@ -286,7 +290,7 @@ bool AndroidBuildApkStep::init()
void AndroidBuildApkStep::showInGraphicalShell()
{
Core::FileUtils::showInGraphicalShell(Core::ICore::mainWindow(), m_apkPath);
Core::FileUtils::showInGraphicalShell(Core::ICore::mainWindow(), m_packagePath);
}
ProjectExplorer::BuildStepConfigWidget *AndroidBuildApkStep::createConfigWidget()
@@ -373,14 +377,15 @@ void AndroidBuildApkStep::doRun()
auto setup = [this] {
auto bc = target()->activeBuildConfiguration();
Utils::FilePath androidLibsDir = bc->buildDirectory()
.pathAppended("android-build/libs")
.pathAppended(AndroidManager::targetArch(target()));
if (!androidLibsDir.exists() && !QDir{bc->buildDirectory().toString()}.mkpath(androidLibsDir.toString()))
return false;
const auto androidAbis = AndroidManager::applicationAbis(target());
for (const auto &abi : androidAbis) {
Utils::FilePath androidLibsDir = bc->buildDirectory()
.pathAppended("android-build/libs")
.pathAppended(abi);
if (!androidLibsDir.exists() && !QDir{bc->buildDirectory().toString()}.mkpath(androidLibsDir.toString()))
return false;
}
QJsonObject deploySettings = Android::AndroidManager::deploymentSettings(target());
RunConfiguration *rc = target()->activeRunConfiguration();
const QString buildKey = rc ? rc->buildKey() : QString();
const ProjectNode *node = rc ? target()->project()->findNodeForBuildKey(buildKey) : nullptr;
@@ -392,12 +397,41 @@ void AndroidBuildApkStep::doRun()
if (targets.isEmpty())
return true; // qmake does this job for us
QJsonObject deploySettings = Android::AndroidManager::deploymentSettings(target());
QJsonObject architectures;
// Copy targets to android build folder
for (const auto &target : targets) {
if (!copyFileIfNewer(target, androidLibsDir.pathAppended(QFileInfo{target}.fileName()).toString()))
return false;
QString applicationBinary = target()->activeRunConfiguration()->buildTargetInfo().targetFilePath.toFileInfo().fileName();
for (const auto &abi : androidAbis) {
QString targetSuffix = QString{"_%1.so"}.arg(abi);
if (applicationBinary.endsWith(targetSuffix)) {
// Keep only TargetName from "lib[TargetName]_abi.so"
applicationBinary.remove(0, 3).chop(targetSuffix.size());
}
Utils::FilePath androidLibsDir = bc->buildDirectory()
.pathAppended("android-build/libs")
.pathAppended(abi);
for (const auto &target : targets) {
if (target.endsWith(targetSuffix)) {
if (!copyFileIfNewer(target, androidLibsDir.pathAppended(QFileInfo{target}.fileName()).toString()))
return false;
if (abi == "x86") {
architectures[abi] = "i686-linux-android";
} else if (abi == "x86_64") {
architectures[abi] = "x86_64-linux-android";
} else if (abi == "arm64-v8a") {
architectures[abi] = "aarch64-linux-android";
} else {
architectures[abi] = "arm-linux-androideabi";
}
}
}
}
deploySettings["application-binary"] = applicationBinary;
deploySettings["architectures"] = architectures;
QString extraLibs = node->data(Android::Constants::AndroidExtraLibs).toString();
if (!extraLibs.isEmpty())
deploySettings["android-extra-libs"] = extraLibs;
@@ -485,8 +519,8 @@ QVariant AndroidBuildApkStep::data(Core::Id id) const
return AndroidConfigurations::currentConfig().bestNdkPlatformMatch(AndroidManager::minimumSDK(target())).mid(8);
if (id == Constants::NdkLocation)
return QVariant::fromValue(AndroidConfigurations::currentConfig().ndkLocation());
if (id == Constants::AndroidABI)
return AndroidManager::targetArch(target());
if (id == Constants::AndroidABIs)
return AndroidManager::applicationAbis(target());
return AbstractProcessStep::data(id);
}
@@ -523,6 +557,16 @@ void AndroidBuildApkStep::setSignPackage(bool b)
m_signPackage = b;
}
bool AndroidBuildApkStep::buildAAB() const
{
return m_buildAAB;
}
void AndroidBuildApkStep::setBuildAAB(bool aab)
{
m_buildAAB = aab;
}
bool AndroidBuildApkStep::openPackageLocation() const
{
return m_openPackageLocation;

View File

@@ -61,6 +61,9 @@ public:
bool signPackage() const;
void setSignPackage(bool b);
bool buildAAB() const;
void setBuildAAB(bool aab);
bool openPackageLocation() const;
void setOpenPackageLocation(bool open);
@@ -89,6 +92,7 @@ private:
void doRun() override;
bool m_buildAAB = false;
bool m_signPackage = false;
bool m_verbose = false;
bool m_useMinistro = false;
@@ -101,7 +105,7 @@ private:
QString m_keystorePasswd;
QString m_certificateAlias;
QString m_certificatePasswd;
QString m_apkPath;
QString m_packagePath;
QString m_command;
QString m_argumentsPasswordConcealed;

View File

@@ -236,6 +236,13 @@ QWidget *AndroidBuildApkWidget::createAdvancedGroup()
m_step, &AndroidBuildApkStep::setUseMinistro);
auto vbox = new QVBoxLayout(group);
QtSupport::BaseQtVersion *version = QtSupport::QtKitAspect::qtVersion(step()->target()->kit());
if (version && version->qtVersion() >= QtSupport::QtVersionNumber{5,14}) {
auto buildAAB = new QCheckBox(tr("Build .aab (Android App Bundle)"), group);
buildAAB->setChecked(m_step->buildAAB());
connect(buildAAB, &QAbstractButton::toggled, m_step, &AndroidBuildApkStep::setBuildAAB);
vbox->addWidget(buildAAB);
}
vbox->addWidget(openPackageLocationCheckBox);
vbox->addWidget(verboseOutputCheckBox);
vbox->addWidget(m_addDebuggerCheckBox);

View File

@@ -27,7 +27,6 @@
#include "androidconstants.h"
#include "androidtoolchain.h"
#include "androiddevice.h"
#include "androidgdbserverkitinformation.h"
#include "androidmanager.h"
#include "androidqtversion.h"
#include "androiddevicedialog.h"
@@ -364,11 +363,9 @@ FilePath AndroidConfig::aaptToolPath() const
return aaptToolPath.pathAppended(toolPath);
}
FilePath AndroidConfig::clangPath() const
FilePath AndroidConfig::toolchainPath() const
{
const FilePath clangPath = m_ndkLocation.pathAppended("toolchains/llvm/prebuilt/");
const FilePath oldNdkClangPath = m_ndkLocation.pathAppended("toolchains/llvm-3.6/prebuilt/");
const QVector<FilePath> clangSearchPaths{clangPath, oldNdkClangPath};
const FilePath toolchainPath = m_ndkLocation.pathAppended("toolchains/llvm/prebuilt/");
// detect toolchain host
QStringList hostPatterns;
@@ -385,18 +382,23 @@ FilePath AndroidConfig::clangPath() const
default: /* unknown host */ return FilePath();
}
for (const FilePath &path : clangSearchPaths) {
QDirIterator iter(path.toString(), hostPatterns, QDir::Dirs);
if (iter.hasNext()) {
iter.next();
return path.pathAppended(iter.fileName())
.pathAppended(HostOsInfo::withExecutableSuffix("bin/clang"));
}
QDirIterator iter(toolchainPath.toString(), hostPatterns, QDir::Dirs);
if (iter.hasNext()) {
iter.next();
return toolchainPath.pathAppended(iter.fileName());
}
return {};
}
FilePath AndroidConfig::clangPath() const
{
const FilePath path = toolchainPath();
if (path.isEmpty())
return {};
return path.pathAppended(HostOsInfo::withExecutableSuffix("bin/clang"));
}
FilePath AndroidConfig::gdbPath(const ProjectExplorer::Abi &abi) const
{
const FilePath path = m_ndkLocation.pathAppended(
@@ -733,22 +735,26 @@ FilePath AndroidConfig::ndkLocation() const
return m_ndkLocation;
}
static inline QString gdbServerArch(const Abi &abi)
static inline QString gdbServerArch(const QString &androidAbi)
{
switch (abi.architecture()) {
case Abi::X86Architecture:
return abi.wordWidth() == 64 ? QString{"x86_64"} : QString{"x86"};
case Abi::ArmArchitecture:
return abi.wordWidth() == 64 ? QString{"arm64"} : QString{"arm"};
default: return {};
};
if (androidAbi == "arm64-v8a") {
return "arm64";
} else if (androidAbi == "armeabi-v7a") {
return "arm";
} else if (androidAbi == "x86_64") {
return "x86_64";
} else if (androidAbi == "x86") {
return "x86";
} else {
return {};
}
}
FilePath AndroidConfig::gdbServer(const ProjectExplorer::Abi &abi) const
FilePath AndroidConfig::gdbServer(const QString &androidAbi) const
{
const FilePath path = AndroidConfigurations::currentConfig().ndkLocation()
.pathAppended(QString("prebuilt/android-%1/gdbserver/gdbserver")
.arg(gdbServerArch(abi)));
.arg(gdbServerArch(androidAbi)));
if (path.exists())
return path;
return {};
@@ -875,16 +881,21 @@ void AndroidConfigurations::setConfig(const AndroidConfig &devConfigs)
}
AndroidDeviceInfo AndroidConfigurations::showDeviceDialog(Project *project,
int apiLevel, const QString &abi)
int apiLevel, const QStringList &abis)
{
QString serialNumber = defaultDevice(project, abi);
AndroidDeviceDialog dialog(apiLevel, abi, serialNumber, Core::ICore::mainWindow());
QString serialNumber;
for (const QString &abi : abis) {
serialNumber = defaultDevice(project, abi);
if (!serialNumber.isEmpty())
break;
}
AndroidDeviceDialog dialog(apiLevel, abis, serialNumber, Core::ICore::mainWindow());
AndroidDeviceInfo info = dialog.device();
if (dialog.saveDeviceSelection() && info.isValid()) {
const QString serialNumber = info.type == AndroidDeviceInfo::Hardware ?
info.serialNumber : info.avdname;
if (!serialNumber.isEmpty())
AndroidConfigurations::setDefaultDevice(project, abi, serialNumber);
AndroidConfigurations::setDefaultDevice(project, AndroidManager::devicePreferredAbi(info.cpuAbi, abis), serialNumber);
}
return info;
}
@@ -1049,10 +1060,9 @@ void AndroidConfigurations::updateAutomaticKitList()
QtSupport::QtKitAspect::setQtVersion(k, qt);
DeviceKitAspect::setDevice(k, device);
Debugger::DebuggerKitAspect::setDebugger(k, findOrRegisterDebugger(tc));
AndroidGdbServerKitAspect::setGdbSever(k, currentConfig().gdbServer(tc->targetAbi()));
k->makeSticky();
k->setUnexpandedDisplayName(tr("Android for %1 (Clang %2)")
.arg(static_cast<const AndroidQtVersion *>(qt)->targetArch())
.arg(static_cast<const AndroidQtVersion *>(qt)->androidAbis().join(","))
.arg(qt->displayName()));
k->setValueSilently(Constants::ANDROID_KIT_NDK, currentConfig().ndkLocation().toString());
k->setValueSilently(Constants::ANDROID_KIT_SDK, currentConfig().sdkLocation().toString());

View File

@@ -105,7 +105,7 @@ public:
void setSdkManagerToolArgs(const QStringList &args);
Utils::FilePath ndkLocation() const;
Utils::FilePath gdbServer(const ProjectExplorer::Abi &abi) const;
Utils::FilePath gdbServer(const QString &androidAbi) const;
QVersionNumber ndkVersion() const;
void setNdkLocation(const Utils::FilePath &ndkLocation);
@@ -132,7 +132,9 @@ public:
Utils::FilePath avdManagerToolPath() const;
Utils::FilePath aaptToolPath() const;
Utils::FilePath toolchainPath() const;
Utils::FilePath clangPath() const;
Utils::FilePath gdbPath(const ProjectExplorer::Abi &abi) const;
Utils::FilePath makePath() const;
@@ -196,7 +198,7 @@ public:
static void setConfig(const AndroidConfig &config);
static AndroidConfigurations *instance();
static AndroidDeviceInfo showDeviceDialog(ProjectExplorer::Project *project, int apiLevel, const QString &abi);
static AndroidDeviceInfo showDeviceDialog(ProjectExplorer::Project *project, int apiLevel, const QStringList &abis);
static void setDefaultDevice(ProjectExplorer::Project *project, const QString &abi, const QString &serialNumber); // serial number or avd name
static QString defaultDevice(ProjectExplorer::Project *project, const QString &abi); // serial number or avd name
static void clearDefaultDevices(ProjectExplorer::Project *project);

View File

@@ -82,7 +82,7 @@ const char AndroidManifest[] = "Android.Manifest"; // QStringList
const char AndroidNdkPlatform[] = "AndroidNdkPlatform"; //QString
const char NdkLocation[] = "NdkLocation"; // FileName
const char AndroidABI[] = "AndroidABI"; // QString
const char AndroidABIs[] = "AndroidABIs"; // QString
} // namespace Constants;
} // namespace Android

View File

@@ -102,15 +102,6 @@ static QStringList getExtraLibs(const ProjectNode *node)
return node->data(Android::Constants::AndroidExtraLibs).toStringList();
}
static QString toNdkArch(const QString &arch)
{
if (arch == QLatin1String("armeabi-v7a") || arch == QLatin1String("armeabi"))
return QLatin1String("arch-arm");
if (arch == QLatin1String("arm64-v8a"))
return QLatin1String("arch-arm64");
return QLatin1String("arch-") + arch;
}
AndroidDebugSupport::AndroidDebugSupport(RunControl *runControl, const QString &intentName)
: Debugger::DebuggerRunTool(runControl)
{
@@ -155,6 +146,8 @@ void AndroidDebugSupport::start()
setSymbolFile(target->activeBuildConfiguration()->buildDirectory().pathAppended("app_process"));
setSkipExecutableValidation(true);
setUseExtendedRemote(true);
QString devicePreferredAbi = AndroidManager::devicePreferredAbi(target);
setAbi(AndroidManager::androidAbi2Abi(devicePreferredAbi));
QUrl gdbServer;
gdbServer.setHost(QHostAddress(QHostAddress::LocalHost).toString());
gdbServer.setPort(m_runner->gdbServerPort().number());
@@ -165,10 +158,13 @@ void AndroidDebugSupport::start()
const int minimumNdk = qt ? qt->minimumNDK() : 0;
int sdkVersion = qMax(AndroidManager::minimumSDK(kit), minimumNdk);
// TODO find a way to use the new sysroot layout
// instead ~/android/ndk-bundle/platforms/android-29/arch-arm64
// use ~/android/ndk-bundle/toolchains/llvm/prebuilt/linux-x86_64/sysroot
Utils::FilePath sysRoot = AndroidConfigurations::currentConfig().ndkLocation()
.pathAppended("platforms")
.pathAppended(QString("android-%1").arg(sdkVersion))
.pathAppended(toNdkArch(AndroidManager::targetArch(target)));
.pathAppended(devicePreferredAbi);
setSysRoot(sysRoot);
qCDebug(androidDebugSupportLog) << "Sysroot: " << sysRoot;
}

View File

@@ -110,9 +110,6 @@ public:
auto resetDefaultDevices = new QPushButton(this);
resetDefaultDevices->setText(AndroidDeployQtStep::tr("Reset Default Devices"));
auto cleanLibsPushButton = new QPushButton(this);
cleanLibsPushButton->setText(AndroidDeployQtStep::tr("Clean Temporary Libraries Directory on Device"));
auto installMinistroButton = new QPushButton(this);
installMinistroButton->setText(AndroidDeployQtStep::tr("Install Ministro from APK"));
@@ -126,10 +123,6 @@ public:
AndroidManager::installQASIPackage(step->target(), packagePath);
});
connect(cleanLibsPushButton, &QAbstractButton::clicked, this, [step] {
AndroidManager::cleanLibsOnDevice(step->target());
});
connect(resetDefaultDevices, &QAbstractButton::clicked, this, [step] {
AndroidConfigurations::clearDefaultDevices(step->project());
});
@@ -140,7 +133,6 @@ public:
auto layout = new QVBoxLayout(this);
layout->addWidget(uninstallPreviousPackage);
layout->addWidget(resetDefaultDevices);
layout->addWidget(cleanLibsPushButton);
layout->addWidget(installMinistroButton);
}
};
@@ -173,8 +165,8 @@ bool AndroidDeployQtStep::init()
{
m_androiddeployqtArgs = CommandLine();
m_targetArch = AndroidManager::targetArch(target());
if (m_targetArch.isEmpty()) {
m_androidABIs = AndroidManager::applicationAbis(target());
if (m_androidABIs.isEmpty()) {
emit addOutput(tr("No Android arch set by the .pro file."), OutputFormat::Stderr);
return false;
}
@@ -189,7 +181,7 @@ bool AndroidDeployQtStep::init()
auto androidBuildApkStep = AndroidBuildApkStep::findInBuild(bc);
int minTargetApi = AndroidManager::minimumSDK(target());
qCDebug(deployStepLog) << "Target architecture:" << m_targetArch
qCDebug(deployStepLog) << "Target architecture:" << m_androidABIs
<< "Min target API" << minTargetApi;
// Try to re-use user-provided information from an earlier step of the same type.
@@ -202,7 +194,7 @@ bool AndroidDeployQtStep::init()
info = androidDeployQtStep->m_deviceInfo;
if (!info.isValid()) {
info = AndroidConfigurations::showDeviceDialog(project(), minTargetApi, m_targetArch);
info = AndroidConfigurations::showDeviceDialog(project(), minTargetApi, m_androidABIs);
m_deviceInfo = info; // Keep around for later steps
}
@@ -218,6 +210,7 @@ bool AndroidDeployQtStep::init()
AndroidManager::setDeviceSerialNumber(target(), m_serialNumber);
AndroidManager::setDeviceApiLevel(target(), info.sdk);
AndroidManager::setDeviceAbis(target(), info.cpuAbi);
emit addOutput(tr("Deploying to %1").arg(m_serialNumber), OutputFormat::Stdout);
@@ -516,17 +509,11 @@ void AndroidDeployQtStep::gatherFilesToPull()
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 = ToolChainKitAspect::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";
}
auto preferreABI = AndroidManager::devicePreferredAbi(target());
if (preferreABI == "arm64-v8a" || preferreABI == "x86_64") {
m_filesToPull["/system/bin/app_process64"] = buildDir + "app_process";
libDirName = "lib64";
linkerName = "linker64";
} else {
m_filesToPull["/system/bin/app_process32"] = buildDir + "app_process";
m_filesToPull["/system/bin/app_process"] = buildDir + "app_process";

View File

@@ -111,7 +111,7 @@ private:
Utils::FilePath m_apkPath;
QMap<QString, QString> m_filesToPull;
QString m_targetArch;
QStringList m_androidABIs;
bool m_uninstallPreviousPackage = false;
bool m_uninstallPreviousPackageRun = false;
bool m_useAndroiddeployqt = false;

View File

@@ -236,7 +236,7 @@ class AndroidDeviceModel : public QAbstractItemModel
{
Q_OBJECT
public:
AndroidDeviceModel(int apiLevel, const QString &abi);
AndroidDeviceModel(int apiLevel, const QStringList &abis);
QModelIndex index(int row, int column,
const QModelIndex &parent = QModelIndex()) const override;
QModelIndex parent(const QModelIndex &child) const override;
@@ -251,7 +251,7 @@ public:
QModelIndex indexFor(AndroidDeviceInfo::AndroidDeviceType type, const QString &serial);
private:
int m_apiLevel;
QString m_abi;
QStringList m_abis;
AndroidDeviceModelNode *m_root;
};
@@ -260,8 +260,8 @@ private:
/////////////////
// AndroidDeviceModel
/////////////////
AndroidDeviceModel::AndroidDeviceModel(int apiLevel, const QString &abi)
: m_apiLevel(apiLevel), m_abi(abi), m_root(nullptr)
AndroidDeviceModel::AndroidDeviceModel(int apiLevel, const QStringList &abis)
: m_apiLevel(apiLevel), m_abis(abis), m_root(nullptr)
{
}
@@ -364,7 +364,7 @@ void AndroidDeviceModel::setDevices(const QVector<AndroidDeviceInfo> &devices)
}else if (device.state == AndroidDeviceInfo::OfflineState) {
error = AndroidDeviceDialog::tr("Offline. Please check the state of your device %1.")
.arg(device.serialNumber);
} else if (!device.cpuAbi.contains(m_abi)) {
} else if (!AndroidManager::matchedAbis(device.cpuAbi, m_abis)) {
error = AndroidDeviceDialog::tr("ABI is incompatible, device supports ABIs: %1.")
.arg(device.cpuAbi.join(QLatin1Char(' ')));
} else if (device.sdk < m_apiLevel) {
@@ -413,13 +413,13 @@ static inline QString msgAdbListDevices()
return AndroidDeviceDialog::tr("<p>The adb tool in the Android SDK lists all connected devices if run via &quot;adb devices&quot;.</p>");
}
AndroidDeviceDialog::AndroidDeviceDialog(int apiLevel, const QString &abi,
AndroidDeviceDialog::AndroidDeviceDialog(int apiLevel, const QStringList &abis,
const QString &serialNumber, QWidget *parent) :
QDialog(parent),
m_model(new AndroidDeviceModel(apiLevel, abi)),
m_model(new AndroidDeviceModel(apiLevel, abis)),
m_ui(new Ui::AndroidDeviceDialog),
m_apiLevel(apiLevel),
m_abi(abi),
m_abis(abis),
m_defaultDevice(serialNumber),
m_avdManager(new AndroidAvdManager)
{
@@ -431,7 +431,7 @@ AndroidDeviceDialog::AndroidDeviceDialog(int apiLevel, const QString &abi,
m_ui->deviceView->setUniformRowHeights(true);
m_ui->deviceView->setExpandsOnDoubleClick(false);
m_ui->defaultDeviceCheckBox->setText(tr("Always use this device for architecture %1 for this project").arg(abi));
m_ui->defaultDeviceCheckBox->setText(tr("Always use this device for this project"));
m_ui->noDeviceFoundLabel->setText(QLatin1String("<p align=\"center\"><span style=\" font-size:16pt;\">")
+ tr("No Device Found") + QLatin1String("</span></p><br/>")
@@ -578,7 +578,7 @@ void AndroidDeviceDialog::createAvd()
{
m_ui->createAVDButton->setEnabled(false);
CreateAvdInfo info = AvdDialog::gatherCreateAVDInfo(this, AndroidConfigurations::sdkManager(),
m_apiLevel, m_abi);
m_apiLevel, m_abis);
if (!info.isValid()) {
m_ui->createAVDButton->setEnabled(true);

View File

@@ -52,7 +52,7 @@ class AndroidDeviceDialog : public QDialog
Q_OBJECT
public:
explicit AndroidDeviceDialog(int apiLevel, const QString &abi,
explicit AndroidDeviceDialog(int apiLevel, const QStringList &abis,
const QString &serialNumber, QWidget *parent = nullptr);
~AndroidDeviceDialog() override;
@@ -74,7 +74,7 @@ private:
Ui::AndroidDeviceDialog *m_ui;
Utils::ProgressIndicator *m_progressIndicator;
int m_apiLevel;
QString m_abi;
QStringList m_abis;
QString m_avdNameFromAdd;
QString m_defaultDevice;
std::unique_ptr<AndroidAvdManager> m_avdManager;

View File

@@ -1,217 +0,0 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#include "androidgdbserverkitinformation.h"
#include "androidconstants.h"
#include "androidtoolchain.h"
#include "androidconfigurations.h"
#include <utils/pathchooser.h>
#include <utils/elidinglabel.h>
#include <utils/qtcassert.h>
#include <QDialogButtonBox>
#include <QLabel>
#include <QPushButton>
#include <QMenu>
#include <QDialog>
#include <QVBoxLayout>
#include <QFormLayout>
#include <projectexplorer/projectexplorerconstants.h>
#include <qtsupport/baseqtversion.h>
#include <qtsupport/qtkitinformation.h>
using namespace ProjectExplorer;
using namespace Utils;
namespace Android {
namespace Internal {
class AndroidGdbServerKitAspectWidget : public KitAspectWidget
{
Q_DECLARE_TR_FUNCTIONS(Android::Internal::AndroidGdbServerKitAspect)
public:
AndroidGdbServerKitAspectWidget(Kit *kit, const KitAspect *ki);
~AndroidGdbServerKitAspectWidget() override;
void makeReadOnly() override;
void refresh() override;
QWidget *mainWidget() const override;
QWidget *buttonWidget() const override;
private:
void autoDetectDebugger();
void showDialog();
QLabel *m_label;
QPushButton *m_button;
};
AndroidGdbServerKitAspect::AndroidGdbServerKitAspect()
{
setId(AndroidGdbServerKitAspect::id());
setDisplayName(tr("Android GDB server"));
setDescription(tr("The GDB server to use for this kit."));
setPriority(27999); // Just one less than Debugger!
}
void AndroidGdbServerKitAspect::setup(Kit *kit)
{
if (kit && !kit->hasValue(id()))
kit->setValue(id(), autoDetect(kit).toString());
}
Tasks AndroidGdbServerKitAspect::validate(const Kit *) const
{
return {};
}
bool AndroidGdbServerKitAspect::isApplicableToKit(const Kit *k) const
{
return DeviceKitAspect::deviceId(k) == Constants::ANDROID_DEVICE_ID;
}
KitAspect::ItemList AndroidGdbServerKitAspect::toUserOutput(const Kit *kit) const
{
return {{tr("GDB server"), AndroidGdbServerKitAspect::gdbServer(kit).toUserOutput()}};
}
KitAspectWidget *AndroidGdbServerKitAspect::createConfigWidget(Kit *kit) const
{
QTC_ASSERT(kit, return nullptr);
return new AndroidGdbServerKitAspectWidget(kit, this);
}
Core::Id AndroidGdbServerKitAspect::id()
{
return "Android.GdbServer.Information";
}
FilePath AndroidGdbServerKitAspect::gdbServer(const Kit *kit)
{
QTC_ASSERT(kit, return FilePath());
return FilePath::fromString(kit->value(AndroidGdbServerKitAspect::id()).toString());
}
void AndroidGdbServerKitAspect::setGdbSever(Kit *kit, const FilePath &gdbServerCommand)
{
QTC_ASSERT(kit, return);
kit->setValue(AndroidGdbServerKitAspect::id(), gdbServerCommand.toString());
}
FilePath AndroidGdbServerKitAspect::autoDetect(const Kit *kit)
{
ToolChain *tc = ToolChainKitAspect::toolChain(kit, ProjectExplorer::Constants::CXX_LANGUAGE_ID);
if (!tc || tc->typeId() != Constants::ANDROID_TOOLCHAIN_TYPEID)
return FilePath();
return AndroidConfigurations::currentConfig().gdbServer(tc->targetAbi());
}
///////////////
// AndroidGdbServerKitAspectWidget
///////////////
AndroidGdbServerKitAspectWidget::AndroidGdbServerKitAspectWidget(Kit *kit, const KitAspect *ki) :
KitAspectWidget(kit, ki),
m_label(new ElidingLabel),
m_button(new QPushButton(tr("Manage...")))
{
// ToolButton with Menu, defaulting to 'Autodetect'.
auto buttonMenu = new QMenu(m_button);
QAction *autoDetectAction = buttonMenu->addAction(tr("Auto-detect"));
connect(autoDetectAction, &QAction::triggered,
this, &AndroidGdbServerKitAspectWidget::autoDetectDebugger);
QAction *changeAction = buttonMenu->addAction(tr("Edit..."));
connect(changeAction, &QAction::triggered,
this, &AndroidGdbServerKitAspectWidget::showDialog);
m_button->setMenu(buttonMenu);
refresh();
}
AndroidGdbServerKitAspectWidget::~AndroidGdbServerKitAspectWidget()
{
delete m_button;
delete m_label;
}
void AndroidGdbServerKitAspectWidget::makeReadOnly()
{
m_button->setEnabled(false);
}
void AndroidGdbServerKitAspectWidget::refresh()
{
m_label->setText(AndroidGdbServerKitAspect::gdbServer(m_kit).toString());
}
QWidget *AndroidGdbServerKitAspectWidget::mainWidget() const
{
return m_label;
}
QWidget *AndroidGdbServerKitAspectWidget::buttonWidget() const
{
return m_button;
}
void AndroidGdbServerKitAspectWidget::autoDetectDebugger()
{
AndroidGdbServerKitAspect::setGdbSever(m_kit, AndroidGdbServerKitAspect::autoDetect(m_kit));
}
void AndroidGdbServerKitAspectWidget::showDialog()
{
QDialog dialog;
auto layout = new QVBoxLayout(&dialog);
auto formLayout = new QFormLayout;
formLayout->setFieldGrowthPolicy(QFormLayout::AllNonFixedFieldsGrow);
auto binaryLabel = new QLabel(tr("&Binary:"));
auto chooser = new PathChooser;
chooser->setExpectedKind(PathChooser::ExistingCommand);
chooser->setPath(AndroidGdbServerKitAspect::gdbServer(m_kit).toString());
binaryLabel->setBuddy(chooser);
formLayout->addRow(binaryLabel, chooser);
layout->addLayout(formLayout);
auto buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal, &dialog);
connect(buttonBox, &QDialogButtonBox::accepted, &dialog, &QDialog::accept);
connect(buttonBox, &QDialogButtonBox::rejected, &dialog, &QDialog::reject);
layout->addWidget(buttonBox);
dialog.setWindowTitle(tr("GDB Server for \"%1\"").arg(m_kit->displayName()));
if (dialog.exec() == QDialog::Accepted)
AndroidGdbServerKitAspect::setGdbSever(m_kit, chooser->fileName());
}
} // namespace Internal
} // namespace Android

View File

@@ -1,53 +0,0 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#pragma once
#include <projectexplorer/kitinformation.h>
namespace Android {
namespace Internal {
class AndroidGdbServerKitAspect : public ProjectExplorer::KitAspect
{
Q_OBJECT
public:
AndroidGdbServerKitAspect();
void setup(ProjectExplorer::Kit *) override;
ProjectExplorer::Tasks validate(const ProjectExplorer::Kit *) const override;
bool isApplicableToKit(const ProjectExplorer::Kit *k) const override;
ItemList toUserOutput(const ProjectExplorer::Kit *) const override;
ProjectExplorer::KitAspectWidget *createConfigWidget(ProjectExplorer::Kit *) const override;
static Core::Id id();
static Utils::FilePath gdbServer(const ProjectExplorer::Kit *kit);
static void setGdbSever(ProjectExplorer::Kit *kit, const Utils::FilePath &gdbServerCommand);
static Utils::FilePath autoDetect(const ProjectExplorer::Kit *kit);
};
} // namespace Internal
} // namespace Android

View File

@@ -70,6 +70,7 @@ namespace {
const QLatin1String AndroidManifestName("AndroidManifest.xml");
const QLatin1String AndroidDefaultPropertiesName("project.properties");
const QLatin1String AndroidDeviceSn("AndroidDeviceSerialNumber");
const QLatin1String AndroidDeviceAbis("AndroidDeviceAbis");
const QLatin1String ApiLevelKey("AndroidVersion.ApiLevel");
const QString packageNameRegEx("(?<token>package: )(.*?)(name=)'(?<target>.*?)'");
const QString activityRegEx("(?<token>launchable-activity: )(.*?)(name=)'(?<target>.*?)'");
@@ -258,10 +259,10 @@ QString AndroidManager::buildTargetSDK(ProjectExplorer::Target *target)
return fallback;
}
QString AndroidManager::targetArch(const Target *target)
QStringList AndroidManager::applicationAbis(const Target *target)
{
auto qt = static_cast<AndroidQtVersion *>(QtSupport::QtKitAspect::qtVersion(target->kit()));
return qt->targetArch();
return qt->androidAbis();
}
QJsonObject AndroidManager::deploymentSettings(const Target *target)
@@ -274,21 +275,15 @@ QJsonObject AndroidManager::deploymentSettings(const Target *target)
if (!tc || tc->typeId() != Constants::ANDROID_TOOLCHAIN_TYPEID)
return {};
QJsonObject settings;
settings["description"] = "This file is generated by QtCreator to be read by androiddeployqt and should not be modified by hand.";
settings["_description"] = "This file is generated by QtCreator to be read by androiddeployqt and should not be modified by hand.";
settings["qt"] = qt->qmakeProperty("QT_INSTALL_PREFIX");
settings["ndk"] = AndroidConfigurations::currentConfig().ndkLocation().toString();
settings["sdk"] = AndroidConfigurations::currentConfig().sdkLocation().toString();
settings["sdkBuildToolsRevision"] = AndroidConfigurations::currentConfig().buildToolsVersion().toString();
settings["application-binary"] = target->activeRunConfiguration()->buildTargetInfo().targetFilePath.toString();
settings["target-architecture"] = targetArch(target);
settings["stdcpp-path"] = AndroidConfigurations::currentConfig().toolchainPath().pathAppended("sysroot/usr/lib/").toString();
settings["toolchain-prefix"] = "llvm";
settings["tool-prefix"] = "llvm";
settings["useLLVM"] = true;
settings["ndk-host"] = AndroidConfigurations::currentConfig().toolchainHost();
settings["stdcpp-path"] = AndroidConfigurations::currentConfig().ndkLocation()
.pathAppended("/sources/cxx-stl/llvm-libc++/libs/"
+ targetArch(target)
+ "/libc++_shared.so").toString();
return settings;
}
@@ -316,6 +311,75 @@ Utils::FilePath AndroidManager::apkPath(const ProjectExplorer::Target *target)
return dirPath(target).pathAppended(apkPath);
}
FilePath AndroidManager::aabPath(const Target *target)
{
QTC_ASSERT(target, return Utils::FilePath());
auto buildApkStep = AndroidBuildApkStep::findInBuild(target->activeBuildConfiguration());
if (!buildApkStep)
return Utils::FilePath();
QString buildType;
if (buildApkStep->buildConfiguration()->buildType() == BuildConfiguration::Release)
buildType = "release";
else
buildType = "debug";
return dirPath(target).pathAppended(QString("build/outputs/bundle/%1/android-build-%1.aab").arg(buildType));
}
bool AndroidManager::matchedAbis(const QStringList &deviceAbis, const QStringList &appAbis)
{
for (const auto &abi : appAbis) {
if (deviceAbis.contains(abi))
return true;
}
return false;
}
QString AndroidManager::devicePreferredAbi(const QStringList &deviceAbis, const QStringList &appAbis)
{
for (const auto &abi : appAbis) {
if (deviceAbis.contains(abi))
return abi;
}
return {};
}
Abi AndroidManager::androidAbi2Abi(const QString &androidAbi)
{
if (androidAbi == "arm64-v8a") {
return Abi{Abi::Architecture::ArmArchitecture,
Abi::OS::LinuxOS,
Abi::OSFlavor::AndroidLinuxFlavor,
Abi::BinaryFormat::ElfFormat,
64, androidAbi};
} else if (androidAbi == "armeabi-v7a") {
return Abi{Abi::Architecture::ArmArchitecture,
Abi::OS::LinuxOS,
Abi::OSFlavor::AndroidLinuxFlavor,
Abi::BinaryFormat::ElfFormat,
32, androidAbi};
} else if (androidAbi == "x86_64") {
return Abi{Abi::Architecture::X86Architecture,
Abi::OS::LinuxOS,
Abi::OSFlavor::AndroidLinuxFlavor,
Abi::BinaryFormat::ElfFormat,
64, androidAbi};
} else if (androidAbi == "x86") {
return Abi{Abi::Architecture::X86Architecture,
Abi::OS::LinuxOS,
Abi::OSFlavor::AndroidLinuxFlavor,
Abi::BinaryFormat::ElfFormat,
32, androidAbi};
} else {
return Abi{Abi::Architecture::UnknownArchitecture,
Abi::OS::LinuxOS,
Abi::OSFlavor::AndroidLinuxFlavor,
Abi::BinaryFormat::ElfFormat,
0, androidAbi};
}
}
Utils::FilePath AndroidManager::manifestSourcePath(ProjectExplorer::Target *target)
{
if (const ProjectNode *node = currentProjectNode(target)) {
@@ -360,6 +424,22 @@ void AndroidManager::setDeviceSerialNumber(ProjectExplorer::Target *target, cons
target->setNamedSettings(AndroidDeviceSn, deviceSerialNumber);
}
QString AndroidManager::devicePreferredAbi(Target *target)
{
auto appAbis = applicationAbis(target);
const auto deviceAbis = target->namedSettings(AndroidDeviceAbis).toStringList();
for (const auto &abi : deviceAbis) {
if (appAbis.contains(abi))
return abi;
}
return {};
}
void AndroidManager::setDeviceAbis(ProjectExplorer::Target *target, const QStringList &deviceAbis)
{
target->setNamedSettings(AndroidDeviceAbis, deviceAbis);
}
int AndroidManager::deviceApiLevel(ProjectExplorer::Target *target)
{
return target->namedSettings(ApiLevelKey).toInt();
@@ -474,39 +554,13 @@ static int parseMinSdk(const QDomElement &manifestElem)
return 0;
}
void AndroidManager::cleanLibsOnDevice(ProjectExplorer::Target *target)
{
const QString targetArch = AndroidManager::targetArch(target);
if (targetArch.isEmpty())
return;
const int deviceAPILevel = AndroidManager::minimumSDK(target);
AndroidDeviceInfo info = AndroidConfigurations::showDeviceDialog(target->project(), deviceAPILevel, targetArch);
if (!info.isValid()) // aborted
return;
QString deviceSerialNumber = info.serialNumber;
if (info.type == AndroidDeviceInfo::Emulator) {
deviceSerialNumber = AndroidAvdManager().startAvd(info.avdname);
if (deviceSerialNumber.isEmpty())
Core::MessageManager::write(tr("Starting Android virtual device failed."));
}
QStringList arguments = AndroidDeviceInfo::adbSelector(deviceSerialNumber);
arguments << "shell" << "rm" << "-r" << "/data/local/tmp/qt";
QString error;
if (!runAdbCommandDetached(arguments, &error))
Core::MessageManager::write(tr("Cleaning Qt libraries on device failed.\n%1").arg(error));
}
void AndroidManager::installQASIPackage(ProjectExplorer::Target *target, const QString &packagePath)
{
const QString targetArch = AndroidManager::targetArch(target);
if (targetArch.isEmpty())
const QStringList appAbis = AndroidManager::applicationAbis(target);
if (appAbis.isEmpty())
return;
const int deviceAPILevel = AndroidManager::minimumSDK(target);
AndroidDeviceInfo info = AndroidConfigurations::showDeviceDialog(target->project(), deviceAPILevel, targetArch);
AndroidDeviceInfo info = AndroidConfigurations::showDeviceDialog(target->project(), deviceAPILevel, appAbis);
if (!info.isValid()) // aborted
return;

View File

@@ -31,6 +31,8 @@
#include <QObject>
#include <QVersionNumber>
#include <projectexplorer/abi.h>
QT_BEGIN_NAMESPACE
class QProcess;
QT_END_NAMESPACE
@@ -82,6 +84,9 @@ public:
static QString deviceSerialNumber(ProjectExplorer::Target *target);
static void setDeviceSerialNumber(ProjectExplorer::Target *target, const QString &deviceSerialNumber);
static QString devicePreferredAbi(ProjectExplorer::Target *target);
static void setDeviceAbis(ProjectExplorer::Target *target, const QStringList &deviceAbis);
static int deviceApiLevel(ProjectExplorer::Target *target);
static void setDeviceApiLevel(ProjectExplorer::Target *target, int level);
@@ -90,7 +95,7 @@ public:
static int minimumSDK(ProjectExplorer::Target *target);
static int minimumSDK(const ProjectExplorer::Kit *kit);
static QString targetArch(const ProjectExplorer::Target *target);
static QStringList applicationAbis(const ProjectExplorer::Target *target);
static Utils::FilePath dirPath(const ProjectExplorer::Target *target);
static Utils::FilePath manifestPath(ProjectExplorer::Target *target);
@@ -98,11 +103,14 @@ public:
static Utils::FilePath manifestSourcePath(ProjectExplorer::Target *target);
static Utils::FilePath defaultPropertiesPath(ProjectExplorer::Target *target);
static Utils::FilePath apkPath(const ProjectExplorer::Target *target);
static Utils::FilePath aabPath(const ProjectExplorer::Target *target);
static bool matchedAbis(const QStringList &deviceAbis, const QStringList &appAbis);
static QString devicePreferredAbi(const QStringList &deviceAbis, const QStringList &appAbis);
static ProjectExplorer::Abi androidAbi2Abi(const QString &androidAbi);
static QPair<int, int> apiLevelRange();
static QString androidNameForApiLevel(int x);
static void cleanLibsOnDevice(ProjectExplorer::Target *target);
static void installQASIPackage(ProjectExplorer::Target *target, const QString &packagePath);
static bool checkKeystorePassword(const QString &keystorePath, const QString &keystorePasswd);

View File

@@ -30,7 +30,6 @@
#include "androiddebugsupport.h"
#include "androiddeployqtstep.h"
#include "androiddevice.h"
#include "androidgdbserverkitinformation.h"
#include "androidmanager.h"
#include "androidmanifesteditorfactory.h"
#include "androidpackageinstallationstep.h"
@@ -168,7 +167,6 @@ public:
};
AndroidBuildApkStepFactory buildApkStepFactory;
AndroidGdbServerKitAspect gdbServerKitAspect;
};
AndroidPlugin::~AndroidPlugin()

View File

@@ -71,14 +71,42 @@ QString AndroidQtVersion::invalidReason() const
Abis AndroidQtVersion::detectQtAbis() const
{
Abis abis = BaseQtVersion::detectQtAbis();
for (int i = 0; i < abis.count(); ++i) {
abis[i] = Abi(abis.at(i).architecture(),
abis.at(i).os(),
Abi::AndroidLinuxFlavor,
abis.at(i).binaryFormat(),
abis.at(i).wordWidth());
}
auto androidAbi2Abi = [](const QString &androidAbi) {
if (androidAbi == "arm64-v8a") {
return Abi{Abi::Architecture::ArmArchitecture,
Abi::OS::LinuxOS,
Abi::OSFlavor::AndroidLinuxFlavor,
Abi::BinaryFormat::ElfFormat,
64, androidAbi};
} else if (androidAbi == "armeabi-v7a") {
return Abi{Abi::Architecture::ArmArchitecture,
Abi::OS::LinuxOS,
Abi::OSFlavor::AndroidLinuxFlavor,
Abi::BinaryFormat::ElfFormat,
32, androidAbi};
} else if (androidAbi == "x86_64") {
return Abi{Abi::Architecture::X86Architecture,
Abi::OS::LinuxOS,
Abi::OSFlavor::AndroidLinuxFlavor,
Abi::BinaryFormat::ElfFormat,
64, androidAbi};
} else if (androidAbi == "x86") {
return Abi{Abi::Architecture::X86Architecture,
Abi::OS::LinuxOS,
Abi::OSFlavor::AndroidLinuxFlavor,
Abi::BinaryFormat::ElfFormat,
32, androidAbi};
} else {
return Abi{Abi::Architecture::UnknownArchitecture,
Abi::OS::LinuxOS,
Abi::OSFlavor::AndroidLinuxFlavor,
Abi::BinaryFormat::ElfFormat,
0, androidAbi};
}
};
Abis abis;
for (const auto &abi : androidAbis())
abis << androidAbi2Abi(abi);
return abis;
}
@@ -105,10 +133,10 @@ QString AndroidQtVersion::description() const
return tr("Android");
}
QString AndroidQtVersion::targetArch() const
const QStringList &AndroidQtVersion::androidAbis() const
{
ensureMkSpecParsed();
return m_targetArch;
return m_androidAbis;
}
int AndroidQtVersion::minimumNDK() const
@@ -119,8 +147,11 @@ int AndroidQtVersion::minimumNDK() const
void AndroidQtVersion::parseMkSpec(ProFileEvaluator *evaluator) const
{
m_targetArch = evaluator->value(QLatin1String("ANDROID_TARGET_ARCH"));
const QString androidPlatform = evaluator->value(QLatin1String("ANDROID_PLATFORM"));
if (qtVersion() >= QtSupport::QtVersionNumber{5, 14})
m_androidAbis = evaluator->values("ALL_ANDROID_ABIS");
else
m_androidAbis = QStringList{evaluator->value("ANDROID_TARGET_ARCH")};
const QString androidPlatform = evaluator->value("ANDROID_PLATFORM");
if (!androidPlatform.isEmpty()) {
const QRegExp regex("android-(\\d+)");
if (regex.exactMatch(androidPlatform)) {

View File

@@ -52,13 +52,13 @@ public:
QSet<Core::Id> targetDeviceTypes() const override;
QString description() const override;
QString targetArch() const;
const QStringList &androidAbis() const;
int minimumNDK() const;
protected:
void parseMkSpec(ProFileEvaluator *) const override;
private:
mutable QString m_targetArch;
mutable QStringList m_androidAbis;
mutable int m_minNdk = -1;
};

View File

@@ -246,11 +246,11 @@ void AndroidRunner::launchAVD()
return;
int deviceAPILevel = AndroidManager::minimumSDK(m_target);
QString targetArch = AndroidManager::targetArch(m_target);
QStringList androidAbis = AndroidManager::applicationAbis(m_target);
// Get AVD info.
AndroidDeviceInfo info = AndroidConfigurations::showDeviceDialog(
m_target->project(), deviceAPILevel, targetArch);
m_target->project(), deviceAPILevel, androidAbis);
AndroidManager::setDeviceSerialNumber(m_target, info.serialNumber);
emit androidDeviceInfoChanged(info);
if (info.isValid()) {

View File

@@ -29,7 +29,6 @@
#include "androidconstants.h"
#include "androidmanager.h"
#include "androidrunconfiguration.h"
#include "androidgdbserverkitinformation.h"
#include <debugger/debuggerrunconfigurationaspect.h>
@@ -226,7 +225,7 @@ AndroidRunnerWorker::AndroidRunnerWorker(RunWorker *runner, const QString &packa
<< "Extra Start Args:" << m_amStartExtraArgs
<< "Before Start ADB cmds:" << m_beforeStartAdbCommands
<< "After finish ADB cmds:" << m_afterFinishAdbCommands;
m_gdbserverPath = AndroidGdbServerKitAspect::gdbServer(target->kit()).toString();
m_gdbserverPath = AndroidConfigurations::instance()->currentConfig().gdbServer(AndroidManager::devicePreferredAbi(target)).toString();
QtSupport::BaseQtVersion *version = QtSupport::QtKitAspect::qtVersion(target->kit());
m_useAppParamsForQmlDebugger = version->qtVersion() >= QtSupport::QtVersionNumber(5, 12);
}

View File

@@ -38,7 +38,7 @@
using namespace Android;
using namespace Android::Internal;
AvdDialog::AvdDialog(int minApiLevel, AndroidSdkManager *sdkManager, const QString &targetArch,
AvdDialog::AvdDialog(int minApiLevel, AndroidSdkManager *sdkManager, const QStringList &abis,
QWidget *parent) :
QDialog(parent),
m_sdkManager(sdkManager),
@@ -50,11 +50,11 @@ AvdDialog::AvdDialog(int minApiLevel, AndroidSdkManager *sdkManager, const QStri
m_hideTipTimer.setInterval(2000);
m_hideTipTimer.setSingleShot(true);
if (targetArch.isEmpty()) {
if (abis.isEmpty()) {
m_avdDialog.abiComboBox->addItems(QStringList({"armeabi-v7a", "armeabi", "x86",
"arm64-v8a", "x86_64"}));
} else {
m_avdDialog.abiComboBox->addItems(QStringList(targetArch));
m_avdDialog.abiComboBox->addItems(abis);
}
auto v = new QRegExpValidator(m_allowedNameChars, this);
@@ -79,10 +79,10 @@ bool AvdDialog::isValid() const
}
CreateAvdInfo AvdDialog::gatherCreateAVDInfo(QWidget *parent, AndroidSdkManager *sdkManager,
int minApiLevel, QString targetArch)
int minApiLevel, const QStringList &abis)
{
CreateAvdInfo result;
AvdDialog d(minApiLevel, sdkManager, targetArch, parent);
AvdDialog d(minApiLevel, sdkManager, abis, parent);
if (d.exec() != QDialog::Accepted || !d.isValid())
return result;

View File

@@ -40,7 +40,7 @@ class AvdDialog : public QDialog
{
Q_OBJECT
public:
explicit AvdDialog(int minApiLevel, AndroidSdkManager *sdkManager, const QString &targetArch,
explicit AvdDialog(int minApiLevel, AndroidSdkManager *sdkManager, const QStringList &abis,
QWidget *parent = nullptr);
const SdkPlatform *sdkPlatform() const;
@@ -49,7 +49,7 @@ public:
int sdcardSize() const;
bool isValid() const;
static CreateAvdInfo gatherCreateAVDInfo(QWidget *parent, AndroidSdkManager *sdkManager,
int minApiLevel = 0, QString targetArch = QString());
int minApiLevel = 0, const QStringList &abis = {});
private:
void updateApiLevelComboBox();

View File

@@ -196,10 +196,21 @@ void CMakeBuildConfiguration::initialize()
CMakeProjectManager::CMakeConfigItem::Type::PATH,
"Android CMake toolchain file",
ndkLocation.pathAppended("build/cmake/android.toolchain.cmake").toUserOutput().toUtf8()});
auto androidAbis = bs->data(Android::Constants::AndroidABIs).toStringList();
QString preferredAbi;
if (androidAbis.contains("arm64-v8a")) {
preferredAbi = "arm64-v8a";
} else if (androidAbis.isEmpty() || androidAbis.contains("armeabi-v7a")) {
preferredAbi = "armeabi-v7a";
} else {
preferredAbi = androidAbis.first();
}
// FIXME: configmodel doesn't care about our androidAbis list...
m_initialConfiguration.prepend(CMakeProjectManager::CMakeConfigItem{"ANDROID_ABI",
CMakeProjectManager::CMakeConfigItem::Type::STRING,
"Android ABI",
bs->data(Android::Constants::AndroidABI).toString().toUtf8()});
preferredAbi.toLatin1(),
androidAbis});
m_initialConfiguration.prepend(CMakeProjectManager::CMakeConfigItem{"ANDROID_STL",
CMakeProjectManager::CMakeConfigItem::Type::STRING,
"Android STL",

View File

@@ -43,8 +43,9 @@ namespace CMakeProjectManager {
CMakeConfigItem::CMakeConfigItem() = default;
CMakeConfigItem::CMakeConfigItem(const QByteArray &k, Type t,
const QByteArray &d, const QByteArray &v) :
key(k), type(t), value(v), documentation(d)
const QByteArray &d, const QByteArray &v,
const QStringList &s) :
key(k), type(t), value(v), documentation(d), values(s)
{ }
CMakeConfigItem::CMakeConfigItem(const QByteArray &k, const QByteArray &v) :

View File

@@ -44,7 +44,7 @@ class CMakeConfigItem {
public:
enum Type { FILEPATH, PATH, BOOL, STRING, INTERNAL, STATIC };
CMakeConfigItem();
CMakeConfigItem(const QByteArray &k, Type t, const QByteArray &d, const QByteArray &v);
CMakeConfigItem(const QByteArray &k, Type t, const QByteArray &d, const QByteArray &v, const QStringList &s = {});
CMakeConfigItem(const QByteArray &k, const QByteArray &v);
static QByteArray valueOf(const QByteArray &key, const QList<CMakeConfigItem> &input);

View File

@@ -445,6 +445,11 @@ void DebuggerRunTool::setOverrideStartScript(const QString &script)
m_runParameters.overrideStartScript = script;
}
void DebuggerRunTool::setAbi(const Abi &abi)
{
m_runParameters.toolChainAbi = abi;
}
void DebuggerRunTool::setInferior(const Runnable &runnable)
{
m_runParameters.inferior = runnable;

View File

@@ -122,6 +122,8 @@ public:
void setTestCase(int testCase);
void setOverrideStartScript(const QString &script);
void setAbi(const ProjectExplorer::Abi &abi);
Internal::TerminalRunner *terminalRunner() const;
private:

View File

@@ -443,8 +443,8 @@ static Abis abiOf(const QByteArray &data)
// --------------------------------------------------------------------------
Abi::Abi(const Architecture &a, const OS &o,
const OSFlavor &of, const BinaryFormat &f, unsigned char w) :
m_architecture(a), m_os(o), m_osFlavor(of), m_binaryFormat(f), m_wordWidth(w)
const OSFlavor &of, const BinaryFormat &f, unsigned char w, const QString &p) :
m_architecture(a), m_os(o), m_osFlavor(of), m_binaryFormat(f), m_wordWidth(w), m_param(p)
{
QTC_ASSERT(osSupportsFlavor(o, of), m_osFlavor = UnknownFlavor);
}
@@ -601,6 +601,13 @@ QString Abi::toString() const
return dn.join('-');
}
QString Abi::param() const
{
if (m_param.isEmpty())
return toString();
return m_param;
}
bool Abi::operator != (const Abi &other) const
{
return !operator ==(other);

View File

@@ -123,7 +123,7 @@ public:
Abi(const Architecture &a = UnknownArchitecture, const OS &o = UnknownOS,
const OSFlavor &so = UnknownFlavor, const BinaryFormat &f = UnknownFormat,
unsigned char w = 0);
unsigned char w = 0, const QString &p = {});
static Abi abiFromTargetTriplet(const QString &machineTriple);
@@ -143,6 +143,7 @@ public:
unsigned char wordWidth() const { return m_wordWidth; }
QString toString() const;
QString param() const;
static QString toString(const Architecture &a);
static QString toString(const OS &o);
@@ -173,6 +174,7 @@ private:
OSFlavor m_osFlavor;
BinaryFormat m_binaryFormat;
unsigned char m_wordWidth;
QString m_param;
};
inline int qHash(const ProjectExplorer::Abi &abi)

View File

@@ -45,6 +45,8 @@
#include <android/androidconstants.h>
#include <ios/iosconstants.h>
#include <qtsupport/baseqtversion.h>
#include <qtsupport/qtkitinformation.h>
#include <winrt/winrtconstants.h>
#include <QDir>
@@ -293,6 +295,18 @@ QVariantMap DefaultPropertyProvider::autoGeneratedProperties(const ProjectExplor
data.insert("Android.ndk.ndkDir", ndkDir);
}
}
data.remove(QBS_ARCHITECTURES);
data.remove(QBS_ARCHITECTURE);
QtSupport::BaseQtVersion *qtVersion = QtSupport::QtKitAspect::qtVersion(k);
if (qtVersion) {
QStringList abis;
for (const auto &abi : qtVersion->qtAbis())
abis << abi.param();
if (abis.size() == 1)
data.insert(QLatin1String(QBS_ARCHITECTURE), abis.first());
else
data.insert(QLatin1String(QBS_ARCHITECTURES), abis);
}
} else {
Utils::FilePath cCompilerPath;
if (tcC)

View File

@@ -596,6 +596,7 @@ QMakeStepConfigWidget::QMakeStepConfigWidget(QMakeStep *step)
connect(step->qmakeBuildConfiguration(), &QmakeBuildConfiguration::qmakeBuildConfigurationChanged,
this, &QMakeStepConfigWidget::qmakeBuildConfigChanged);
connect(step->target(), &Target::kitChanged, this, &QMakeStepConfigWidget::qtVersionChanged);
connect(m_ui->abisListWidget, &QListWidget::itemChanged, this, &QMakeStepConfigWidget::abisChanged);
auto chooser = new Core::VariableChooser(m_ui->qmakeAdditonalArgumentsLineEdit);
chooser->addMacroExpanderProvider([step] { return step->macroExpander(); });
chooser->addSupportedWidget(m_ui->qmakeAdditonalArgumentsLineEdit);
@@ -665,6 +666,36 @@ void QMakeStepConfigWidget::separateDebugInfoChanged()
updateEffectiveQMakeCall();
}
void QMakeStepConfigWidget::abisChanged()
{
if (m_abisParam.isEmpty())
return;
QStringList args = m_step->extraArguments();
for (auto it = args.begin(); it != args.end(); ++it) {
if (it->startsWith(m_abisParam)) {
args.erase(it);
break;
}
}
QStringList abis;
for (int i = 0; i < m_ui->abisListWidget->count(); ++i) {
auto item = m_ui->abisListWidget->item(i);
if (item->checkState() == Qt::CheckState::Checked)
abis << item->text();
}
if (abis.isEmpty()) {
m_ui->abisListWidget->item(m_preferredAbiIndex)->setCheckState(Qt::CheckState::Checked);
return;
}
args << QStringLiteral("%1\"%2\"").arg(m_abisParam, abis.join(' '));
m_step->setExtraArguments(args);
updateSummaryLabel();
updateEffectiveQMakeCall();
}
void QMakeStepConfigWidget::qmakeArgumentsLineEdited()
{
m_ignoreChange = true;
@@ -754,6 +785,32 @@ void QMakeStepConfigWidget::updateSummaryLabel()
setSummaryText(tr("<b>qmake:</b> No Qt version set. Cannot run qmake."));
return;
}
bool enableAbisSelect = qtVersion->qtAbis().size() > 1;
m_ui->abisLabel->setVisible(enableAbisSelect);
m_ui->abisListWidget->setVisible(enableAbisSelect);
if (enableAbisSelect && m_ui->abisListWidget->count() != qtVersion->qtAbis().size()) {
m_ui->abisListWidget->clear();
bool isAndroid = true;
m_preferredAbiIndex = -1;
for (const auto &abi : qtVersion->qtAbis()) {
auto item = new QListWidgetItem{abi.param(), m_ui->abisListWidget};
item->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsSelectable);
item->setCheckState(Qt::Unchecked);
isAndroid = isAndroid && abi.osFlavor() == Abi::OSFlavor::AndroidLinuxFlavor;
if (isAndroid && (item->text() == "arm64-v8a" ||
(m_preferredAbiIndex == -1 && item->text() == "armeabi-v7a"))) {
m_preferredAbiIndex = m_ui->abisListWidget->count() - 1;
}
}
if (isAndroid)
m_abisParam = "ANDROID_ABIS=";
if (m_preferredAbiIndex == -1)
m_preferredAbiIndex = 0;
m_ui->abisListWidget->item(m_preferredAbiIndex)->setCheckState(Qt::Checked);
abisChanged();
}
// We don't want the full path to the .pro file
const QString args = m_step->allArguments(
qtVersion,

View File

@@ -211,6 +211,7 @@ private:
void linkQmlDebuggingLibraryChanged();
void useQtQuickCompilerChanged();
void separateDebugInfoChanged();
void abisChanged();
// slots for dealing with user changes in our UI
void qmakeArgumentsLineEdited();
@@ -230,6 +231,8 @@ private:
Internal::Ui::QMakeStep *m_ui = nullptr;
QMakeStep *m_step = nullptr;
bool m_ignoreChange = false;
int m_preferredAbiIndex = -1;
QString m_abisParam;
};
} // namespace QmakeProjectManager

View File

@@ -6,26 +6,11 @@
<rect>
<x>0</x>
<y>0</y>
<width>440</width>
<height>250</height>
<width>738</width>
<height>300</height>
</rect>
</property>
<layout class="QFormLayout" name="formLayout">
<property name="fieldGrowthPolicy">
<enum>QFormLayout::ExpandingFieldsGrow</enum>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item row="0" column="0">
<widget class="QLabel" name="label_0">
<property name="text">
@@ -275,6 +260,19 @@
</property>
</widget>
</item>
<item row="6" column="0">
<widget class="QLabel" name="abisLabel">
<property name="text">
<string>ABIs:</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
</widget>
</item>
<item row="6" column="1">
<widget class="QListWidget" name="abisListWidget"/>
</item>
</layout>
</widget>
<tabstops>