forked from qt-creator/qt-creator
Say hello to Android CMake support
Requirements: - NDKr19 or newer - Qt 5.12.1 or newer QtCreator supports the following variables: - ANDROID_PACKAGE_SOURCE_DIR - ANDROID_EXTRA_LIBS Be aware, that there is a lot of magic done on QtCreator side, and you can't use only cmake to build an Android APK. [ChangeLog][Android][CMake] Add Android support for CMake projects. Change-Id: I1d351976ed56f424c2bc972f4ff7b5968147a2ed Reviewed-by: hjk <hjk@qt.io>
This commit is contained in:
@@ -54,10 +54,11 @@
|
||||
#include <utils/synchronousprocess.h>
|
||||
#include <utils/utilsicons.h>
|
||||
|
||||
#include <qmakeprojectmanager/qmakeprojectmanagerconstants.h>
|
||||
|
||||
#include <QDateTime>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QHBoxLayout>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <QLabel>
|
||||
#include <QLineEdit>
|
||||
#include <QLoggingCategory>
|
||||
@@ -192,9 +193,14 @@ bool AndroidBuildApkStep::init()
|
||||
&Utils::FileName::toString));
|
||||
|
||||
RunConfiguration *rc = target()->activeRunConfiguration();
|
||||
const ProjectNode *node = rc ? target()->project()->findNodeForBuildKey(rc->buildKey()) : nullptr;
|
||||
const QString buildKey = rc ? rc->buildKey() : QString();
|
||||
const ProjectNode *node = rc ? target()->project()->findNodeForBuildKey(buildKey) : nullptr;
|
||||
|
||||
QFileInfo sourceDirInfo(node ? node->data(Constants::AndroidPackageSourceDir).toString() : QString());
|
||||
QString sourceDirName;
|
||||
if (node)
|
||||
sourceDirName = node->data(Constants::AndroidPackageSourceDir).toString();
|
||||
|
||||
QFileInfo sourceDirInfo(sourceDirName);
|
||||
parser->setSourceDirectory(Utils::FileName::fromString(sourceDirInfo.canonicalFilePath()));
|
||||
parser->setBuildDirectory(Utils::FileName::fromString(bc->buildDirectory().appendPath(Constants::ANDROID_BUILDDIRECTORY).toString()));
|
||||
setOutputParser(parser);
|
||||
@@ -340,13 +346,89 @@ bool AndroidBuildApkStep::verifyCertificatePassword()
|
||||
return success;
|
||||
}
|
||||
|
||||
|
||||
static bool copyFileIfNewer(const QString &sourceFileName,
|
||||
const QString &destinationFileName)
|
||||
{
|
||||
if (QFile::exists(destinationFileName)) {
|
||||
QFileInfo destinationFileInfo(destinationFileName);
|
||||
QFileInfo sourceFileInfo(sourceFileName);
|
||||
if (sourceFileInfo.lastModified() <= destinationFileInfo.lastModified())
|
||||
return true;
|
||||
if (!QFile(destinationFileName).remove())
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!QDir().mkpath(QFileInfo(destinationFileName).path()))
|
||||
return false;
|
||||
return QFile::copy(sourceFileName, destinationFileName);
|
||||
}
|
||||
|
||||
void AndroidBuildApkStep::doRun()
|
||||
{
|
||||
if (m_skipBuilding) {
|
||||
emit addOutput(tr("No application .pro file found, not building an APK."), BuildStep::OutputFormat::ErrorMessage);
|
||||
emit addOutput(tr("Android deploy settings file not found, not building an APK."), BuildStep::OutputFormat::ErrorMessage);
|
||||
emit finished(true);
|
||||
return;
|
||||
}
|
||||
|
||||
auto setup = [this] {
|
||||
auto bc = target()->activeBuildConfiguration();
|
||||
Utils::FileName androidLibsDir = bc->buildDirectory()
|
||||
.appendPath("android-build/libs")
|
||||
.appendPath(AndroidManager::targetArch(target()));
|
||||
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;
|
||||
|
||||
if (!node)
|
||||
return false;
|
||||
|
||||
auto targets = node->data(Android::Constants::AndroidTargets).toStringList();
|
||||
if (targets.isEmpty())
|
||||
return true; // qmake does this job for us
|
||||
|
||||
// Copy targets to android build folder
|
||||
for (const auto &target : targets) {
|
||||
if (!copyFileIfNewer(target, androidLibsDir.appendPath(QFileInfo{target}.fileName()).toString()))
|
||||
return false;
|
||||
}
|
||||
|
||||
QString extraLibs = node->data(Android::Constants::AndroidExtraLibs).toString();
|
||||
if (!extraLibs.isEmpty())
|
||||
deploySettings["android-extra-libs"] = extraLibs;
|
||||
|
||||
QString androidSrcs = node->data(Android::Constants::AndroidPackageSourceDir).toString();
|
||||
if (!androidSrcs.isEmpty())
|
||||
deploySettings["android-package-source-directory"] = androidSrcs;
|
||||
|
||||
QString qmlImportPath = node->data("QML_IMPORT_PATH").toString();
|
||||
if (!qmlImportPath.isEmpty())
|
||||
deploySettings["qml-import-paths"] = qmlImportPath;
|
||||
|
||||
QString qmlRootPath = node->data("QML_ROOT_PATH").toString();
|
||||
if (qmlRootPath.isEmpty())
|
||||
qmlRootPath = target()->project()->rootProjectDirectory().toString();
|
||||
deploySettings["qml-root-path"] = qmlImportPath;
|
||||
|
||||
QFile f{bc->buildDirectory().appendPath("android_deployment_settings.json").toString()};
|
||||
if (!f.open(QIODevice::WriteOnly))
|
||||
return false;
|
||||
f.write(QJsonDocument{deploySettings}.toJson());
|
||||
return true;
|
||||
};
|
||||
|
||||
if (!setup()) {
|
||||
emit addOutput(tr("Cannot set up Android, not building an APK."), BuildStep::OutputFormat::ErrorMessage);
|
||||
emit finished(false);
|
||||
return;
|
||||
}
|
||||
|
||||
AbstractProcessStep::doRun();
|
||||
}
|
||||
|
||||
@@ -398,6 +480,18 @@ void AndroidBuildApkStep::setBuildTargetSdk(const QString &sdk)
|
||||
AndroidManager::updateGradleProperties(target());
|
||||
}
|
||||
|
||||
QVariant AndroidBuildApkStep::data(Core::Id id) const
|
||||
{
|
||||
if (id == Constants::AndroidNdkPlatform)
|
||||
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());
|
||||
|
||||
return AbstractProcessStep::data(id);
|
||||
}
|
||||
|
||||
void AndroidBuildApkStep::setKeystorePath(const Utils::FileName &path)
|
||||
{
|
||||
m_keystorePath = path;
|
||||
@@ -568,7 +662,6 @@ namespace Internal {
|
||||
AndroidBuildApkStepFactory::AndroidBuildApkStepFactory()
|
||||
{
|
||||
registerStep<AndroidBuildApkStep>(Constants::ANDROID_BUILD_APK_ID);
|
||||
setSupportedProjectType(QmakeProjectManager::Constants::QMAKEPROJECT_ID);
|
||||
setSupportedDeviceType(Constants::ANDROID_DEVICE_TYPE);
|
||||
setSupportedStepList(ProjectExplorer::Constants::BUILDSTEPS_BUILD);
|
||||
setDisplayName(AndroidBuildApkStep::tr("Build Android APK"));
|
||||
|
||||
@@ -76,6 +76,7 @@ public:
|
||||
QString buildTargetSdk() const;
|
||||
void setBuildTargetSdk(const QString &sdk);
|
||||
|
||||
QVariant data(Core::Id id) const override;
|
||||
private:
|
||||
Q_INVOKABLE void showInGraphicalShell();
|
||||
|
||||
|
||||
@@ -72,14 +72,19 @@ const char ANDROID_PACKAGE_SOURCE_DIR[] = "AndroidPackageSourceDir";
|
||||
const char ANDROID_EXTRA_LIBS[] = "AndroidExtraLibs";
|
||||
|
||||
const char ANDROID_PACKAGENAME[] = "Android.PackageName";
|
||||
const char ANDROID_PACKAGE_INSTALLATION_STEP_ID[] = "Qt4ProjectManager.AndroidPackageInstallationStep";
|
||||
const char ANDROID_BUILD_APK_ID[] = "QmakeProjectManager.AndroidBuildApkStep";
|
||||
const char ANDROID_PACKAGE_INSTALLATION_STEP_ID[] = "Android.PackageInstallationStep";
|
||||
const char ANDROID_BUILD_APK_ID[] = "Android.BuildApkStep";
|
||||
|
||||
const char AndroidPackageSourceDir[] = "AndroidPackageSourceDir"; // QString
|
||||
const char AndroidDeploySettingsFile[] = "AndroidDeploySettingsFile"; // QString
|
||||
const char AndroidExtraLibs[] = "AndroidExtraLibs"; // QStringList
|
||||
const char AndroidArch[] = "AndroidArch"; // QString
|
||||
const char AndroidSoLibPath[] = "AndroidSoLibPath"; // QStringList
|
||||
const char AndroidTargets[] = "AndroidTargets"; // QStringList
|
||||
|
||||
const char AndroidNdkPlatform[] = "AndroidNdkPlatform"; //QString
|
||||
const char NdkLocation[] = "NdkLocation"; // FileName
|
||||
const char AndroidABI[] = "AndroidABI"; // QString
|
||||
|
||||
} // namespace Constants;
|
||||
} // namespace Android
|
||||
|
||||
@@ -258,12 +258,40 @@ QString AndroidManager::buildTargetSDK(ProjectExplorer::Target *target)
|
||||
return fallback;
|
||||
}
|
||||
|
||||
QString AndroidManager::targetArch(ProjectExplorer::Target *target)
|
||||
QString AndroidManager::targetArch(const Target *target)
|
||||
{
|
||||
auto qt = static_cast<AndroidQtVersion *>(QtSupport::QtKitAspect::qtVersion(target->kit()));
|
||||
return qt->targetArch();
|
||||
}
|
||||
|
||||
QJsonObject AndroidManager::deploymentSettings(const Target *target)
|
||||
{
|
||||
QtSupport::BaseQtVersion *qt = QtSupport::QtKitAspect::qtVersion(target->kit());
|
||||
if (!qt)
|
||||
return {};
|
||||
|
||||
auto tc = ProjectExplorer::ToolChainKitAspect::toolChain(target->kit(), ProjectExplorer::Constants::CXX_LANGUAGE_ID);
|
||||
if (!tc || tc->typeId() != Constants::ANDROID_TOOLCHAIN_ID)
|
||||
return {};
|
||||
QJsonObject settings;
|
||||
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["toolchain-prefix"] = "llvm";
|
||||
settings["tool-prefix"] = "llvm";
|
||||
settings["useLLVM"] = true;
|
||||
settings["ndk-host"] = AndroidConfigurations::currentConfig().toolchainHost();
|
||||
settings["stdcpp-path"] = AndroidConfigurations::currentConfig().ndkLocation()
|
||||
.appendPath("/sources/cxx-stl/llvm-libc++/libs/")
|
||||
.appendString(targetArch(target))
|
||||
.appendPath("libc++_shared.so").toString();
|
||||
return settings;
|
||||
}
|
||||
|
||||
Utils::FileName AndroidManager::dirPath(const ProjectExplorer::Target *target)
|
||||
{
|
||||
if (target->activeBuildConfiguration())
|
||||
@@ -605,7 +633,8 @@ bool AndroidManager::updateGradleProperties(ProjectExplorer::Target *target)
|
||||
if (!node)
|
||||
return false;
|
||||
|
||||
QFileInfo sourceDirInfo(node->data(Constants::AndroidPackageSourceDir).toString());
|
||||
const QString sourceDirName = node->data(Constants::AndroidPackageSourceDir).toString();
|
||||
QFileInfo sourceDirInfo(sourceDirName);
|
||||
FileName packageSourceDir = FileName::fromString(sourceDirInfo.canonicalFilePath());
|
||||
if (!packageSourceDir.appendPath("gradlew").exists())
|
||||
return false;
|
||||
|
||||
@@ -87,7 +87,7 @@ public:
|
||||
static int minimumSDK(ProjectExplorer::Target *target);
|
||||
static int minimumSDK(const ProjectExplorer::Kit *kit);
|
||||
|
||||
static QString targetArch(ProjectExplorer::Target *target);
|
||||
static QString targetArch(const ProjectExplorer::Target *target);
|
||||
|
||||
static Utils::FileName dirPath(const ProjectExplorer::Target *target);
|
||||
static Utils::FileName manifestPath(ProjectExplorer::Target *target);
|
||||
@@ -114,6 +114,8 @@ public:
|
||||
int timeoutS = 30);
|
||||
static SdkToolResult runAaptCommand(const QStringList &args, int timeoutS = 30);
|
||||
|
||||
static QJsonObject deploymentSettings(const ProjectExplorer::Target *target);
|
||||
|
||||
private:
|
||||
static SdkToolResult runCommand(const QString &executable, const QStringList &args,
|
||||
const QByteArray &writeData = {}, int timeoutS = 30);
|
||||
|
||||
@@ -68,6 +68,7 @@ bool AndroidPackageInstallationStep::init()
|
||||
|
||||
ToolChain *tc = ToolChainKitAspect::toolChain(target()->kit(),
|
||||
ProjectExplorer::Constants::CXX_LANGUAGE_ID);
|
||||
QTC_ASSERT(tc, return false);
|
||||
|
||||
ProcessParameters *pp = processParameters();
|
||||
pp->setMacroExpander(bc->macroExpander());
|
||||
|
||||
Reference in New Issue
Block a user