forked from qt-creator/qt-creator
Enable mechanism to bundle Qt in APK
If the Qt version built against is Qt 5, and the deployment method is "Use libs on device" + "Use local Qt libs", Creator will copy the required files into the app bundle and set the necessary meta-data to make the Java code in the app actually load them from the app bundle. We also make this deployment method the default on Qt 5. Change-Id: Ib7a33e7d1fbd22f76c85c31e1dbc68912a38eda8 Reviewed-by: Daniel Teske <daniel.teske@digia.com> Reviewed-by: Shawn Rutledge <shawn.rutledge@digia.com> Reviewed-by: Eike Ziller <eike.ziller@digia.com>
This commit is contained in:
committed by
Eike Ziller
parent
8e18adc70f
commit
91d48fe727
@@ -82,7 +82,11 @@ void AndroidDeployStep::ctor()
|
|||||||
//: AndroidDeployStep default display name
|
//: AndroidDeployStep default display name
|
||||||
setDefaultDisplayName(tr("Deploy to Android device"));
|
setDefaultDisplayName(tr("Deploy to Android device"));
|
||||||
m_deployAction = NoDeploy;
|
m_deployAction = NoDeploy;
|
||||||
|
|
||||||
m_useLocalQtLibs = false;
|
m_useLocalQtLibs = false;
|
||||||
|
if (QtSupport::BaseQtVersion *qt = QtSupport::QtKitInformation::qtVersion(target()->kit()))
|
||||||
|
if (qt->qtVersion() >= QtSupport::QtVersionNumber(5, 0, 0))
|
||||||
|
m_useLocalQtLibs = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AndroidDeployStep::init()
|
bool AndroidDeployStep::init()
|
||||||
@@ -197,22 +201,20 @@ void AndroidDeployStep::cleanLibsFinished()
|
|||||||
void AndroidDeployStep::setDeployAction(AndroidDeployStep::AndroidDeployAction deploy)
|
void AndroidDeployStep::setDeployAction(AndroidDeployStep::AndroidDeployAction deploy)
|
||||||
{
|
{
|
||||||
m_deployAction = deploy;
|
m_deployAction = deploy;
|
||||||
|
|
||||||
|
AndroidManager::updateDeploymentSettings(target());
|
||||||
}
|
}
|
||||||
|
|
||||||
void AndroidDeployStep::setDeployQASIPackagePath(const QString &package)
|
void AndroidDeployStep::setDeployQASIPackagePath(const QString &package)
|
||||||
{
|
{
|
||||||
m_QASIPackagePath = package;
|
m_QASIPackagePath = package;
|
||||||
m_deployAction = InstallQASI;
|
setDeployAction(InstallQASI);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AndroidDeployStep::setUseLocalQtLibs(bool useLocal)
|
void AndroidDeployStep::setUseLocalQtLibs(bool useLocal)
|
||||||
{
|
{
|
||||||
m_useLocalQtLibs = useLocal;
|
m_useLocalQtLibs = useLocal;
|
||||||
|
AndroidManager::updateDeploymentSettings(target());
|
||||||
// ### Passes -1 for API level, which means it won't work with setups that require
|
|
||||||
// library selection based on API level. Use the old approach (command line argument)
|
|
||||||
// in these cases.
|
|
||||||
AndroidManager::setUseLocalLibs(target(), useLocal, -1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AndroidDeployStep::runCommand(QProcess *buildProc,
|
bool AndroidDeployStep::runCommand(QProcess *buildProc,
|
||||||
|
|||||||
@@ -62,6 +62,7 @@ namespace {
|
|||||||
const QLatin1String AndroidLibsFileName("/res/values/libs.xml");
|
const QLatin1String AndroidLibsFileName("/res/values/libs.xml");
|
||||||
const QLatin1String AndroidStringsFileName("/res/values/strings.xml");
|
const QLatin1String AndroidStringsFileName("/res/values/strings.xml");
|
||||||
const QLatin1String AndroidDefaultPropertiesName("project.properties");
|
const QLatin1String AndroidDefaultPropertiesName("project.properties");
|
||||||
|
const QLatin1String AndroidLibraryPrefix("--Managed_by_Qt_Creator--");
|
||||||
|
|
||||||
QString cleanPackageName(QString packageName)
|
QString cleanPackageName(QString packageName)
|
||||||
{
|
{
|
||||||
@@ -373,7 +374,20 @@ QString AndroidManager::targetApplication(ProjectExplorer::Target *target)
|
|||||||
return QString();
|
return QString();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AndroidManager::setUseLocalLibs(ProjectExplorer::Target *target, bool useLocalLibs, int deviceAPILevel)
|
bool AndroidManager::bundleQt(ProjectExplorer::Target *target)
|
||||||
|
{
|
||||||
|
ProjectExplorer::RunConfiguration *runConfiguration = target->activeRunConfiguration();
|
||||||
|
AndroidRunConfiguration *androidRunConfiguration = qobject_cast<AndroidRunConfiguration *>(runConfiguration);
|
||||||
|
if (androidRunConfiguration != 0) {
|
||||||
|
AndroidDeployStep *deployStep = androidRunConfiguration->deployStep();
|
||||||
|
return deployStep->deployAction() == AndroidDeployStep::NoDeploy
|
||||||
|
&& deployStep->useLocalQtLibs();
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AndroidManager::updateDeploymentSettings(ProjectExplorer::Target *target)
|
||||||
{
|
{
|
||||||
// For Qt 4, the "use local libs" options is handled by passing command line arguments to the
|
// For Qt 4, the "use local libs" options is handled by passing command line arguments to the
|
||||||
// app, so no need to alter the AndroidManifest.xml
|
// app, so no need to alter the AndroidManifest.xml
|
||||||
@@ -381,19 +395,32 @@ bool AndroidManager::setUseLocalLibs(ProjectExplorer::Target *target, bool useLo
|
|||||||
if (baseQtVersion == 0 || baseQtVersion->qtVersion() < QtSupport::QtVersionNumber(5,0,0))
|
if (baseQtVersion == 0 || baseQtVersion->qtVersion() < QtSupport::QtVersionNumber(5,0,0))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
ProjectExplorer::RunConfiguration *runConfiguration = target->activeRunConfiguration();
|
||||||
|
AndroidRunConfiguration *androidRunConfiguration = qobject_cast<AndroidRunConfiguration *>(runConfiguration);
|
||||||
|
if (androidRunConfiguration == 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
AndroidDeployStep *deployStep = androidRunConfiguration->deployStep();
|
||||||
|
bool useLocalLibs = deployStep->useLocalQtLibs();
|
||||||
|
bool deployQtLibs = deployStep->deployAction() != AndroidDeployStep::NoDeploy;
|
||||||
|
bool bundleQtLibs = useLocalLibs && !deployQtLibs;
|
||||||
|
|
||||||
QDomDocument doc;
|
QDomDocument doc;
|
||||||
if (!openManifest(target, doc))
|
if (!openManifest(target, doc))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
QDomElement metadataElem = doc.documentElement().firstChildElement(QLatin1String("application")).firstChildElement(QLatin1String("activity")).firstChildElement(QLatin1String("meta-data"));
|
QDomElement metadataElem = doc.documentElement().firstChildElement(QLatin1String("application")).firstChildElement(QLatin1String("activity")).firstChildElement(QLatin1String("meta-data"));
|
||||||
|
|
||||||
|
// ### Passes -1 for API level, which means it won't work with setups that require
|
||||||
|
// library selection based on API level. Use the old approach (command line argument)
|
||||||
|
// in these cases. Hence the Qt version > 4 condition at the beginning of this function.
|
||||||
QString localLibs;
|
QString localLibs;
|
||||||
QString localJars;
|
QString localJars;
|
||||||
QString staticInitClasses;
|
QString staticInitClasses;
|
||||||
if (useLocalLibs) {
|
if (useLocalLibs) {
|
||||||
localLibs = loadLocalLibs(target, deviceAPILevel);
|
localLibs = loadLocalLibs(target, -1);
|
||||||
localJars = loadLocalJars(target, deviceAPILevel);
|
localJars = loadLocalJars(target, -1);
|
||||||
staticInitClasses = loadLocalJarsInitClasses(target, deviceAPILevel);
|
staticInitClasses = loadLocalJarsInitClasses(target, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool changedManifest = false;
|
bool changedManifest = false;
|
||||||
@@ -418,6 +445,11 @@ bool AndroidManager::setUseLocalLibs(ProjectExplorer::Target *target, bool useLo
|
|||||||
metadataElem.setAttribute(QLatin1String("android:value"), staticInitClasses);
|
metadataElem.setAttribute(QLatin1String("android:value"), staticInitClasses);
|
||||||
changedManifest = true;
|
changedManifest = true;
|
||||||
}
|
}
|
||||||
|
} else if (metadataElem.attribute(QLatin1String("android:name")) == QLatin1String("android.app.bundle_local_qt_libs")) {
|
||||||
|
if (metadataElem.attribute(QLatin1String("android:value")).toInt() != bundleQtLibs) {
|
||||||
|
metadataElem.setAttribute(QLatin1String("android:value"), int(bundleQtLibs));
|
||||||
|
changedManifest = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
metadataElem = metadataElem.nextSiblingElement(QLatin1String("meta-data"));
|
metadataElem = metadataElem.nextSiblingElement(QLatin1String("meta-data"));
|
||||||
@@ -653,14 +685,21 @@ QString AndroidManager::loadLocalLibs(ProjectExplorer::Target *target, int apiLe
|
|||||||
return loadLocal(target, apiLevel, Lib);
|
return loadLocal(target, apiLevel, Lib);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString AndroidManager::loadLocalBundledFiles(ProjectExplorer::Target *target, int apiLevel)
|
||||||
|
{
|
||||||
|
return loadLocal(target, apiLevel, BundledFile);
|
||||||
|
}
|
||||||
|
|
||||||
QString AndroidManager::loadLocalJars(ProjectExplorer::Target *target, int apiLevel)
|
QString AndroidManager::loadLocalJars(ProjectExplorer::Target *target, int apiLevel)
|
||||||
{
|
{
|
||||||
return loadLocal(target, apiLevel, Jar);
|
ItemType type = bundleQt(target) ? BundledJar : Jar;
|
||||||
|
return loadLocal(target, apiLevel, type);
|
||||||
}
|
}
|
||||||
|
|
||||||
QString AndroidManager::loadLocalJarsInitClasses(ProjectExplorer::Target *target, int apiLevel)
|
QString AndroidManager::loadLocalJarsInitClasses(ProjectExplorer::Target *target, int apiLevel)
|
||||||
{
|
{
|
||||||
return loadLocal(target, apiLevel, Jar, QLatin1String("initClass"));
|
ItemType type = bundleQt(target) ? BundledJar : Jar;
|
||||||
|
return loadLocal(target, apiLevel, type, QLatin1String("initClass"));
|
||||||
}
|
}
|
||||||
|
|
||||||
QVector<AndroidManager::Library> AndroidManager::availableQtLibsWithDependencies(ProjectExplorer::Target *target)
|
QVector<AndroidManager::Library> AndroidManager::availableQtLibsWithDependencies(ProjectExplorer::Target *target)
|
||||||
@@ -756,6 +795,16 @@ bool AndroidManager::setQtLibs(ProjectExplorer::Target *target, const QStringLis
|
|||||||
return setLibsXml(target, libs, QLatin1String("qt_libs"));
|
return setLibsXml(target, libs, QLatin1String("qt_libs"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool AndroidManager::setBundledInAssets(ProjectExplorer::Target *target, const QStringList &fileList)
|
||||||
|
{
|
||||||
|
return setLibsXml(target, fileList, QLatin1String("bundled_in_assets"));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AndroidManager::setBundledInLib(ProjectExplorer::Target *target, const QStringList &fileList)
|
||||||
|
{
|
||||||
|
return setLibsXml(target, fileList, QLatin1String("bundled_in_lib"));
|
||||||
|
}
|
||||||
|
|
||||||
QStringList AndroidManager::availablePrebundledLibs(ProjectExplorer::Target *target)
|
QStringList AndroidManager::availablePrebundledLibs(ProjectExplorer::Target *target)
|
||||||
{
|
{
|
||||||
QStringList libs;
|
QStringList libs;
|
||||||
@@ -799,7 +848,9 @@ QString AndroidManager::loadLocal(ProjectExplorer::Target *target, int apiLevel,
|
|||||||
QString itemType;
|
QString itemType;
|
||||||
if (item == Lib)
|
if (item == Lib)
|
||||||
itemType = QLatin1String("lib");
|
itemType = QLatin1String("lib");
|
||||||
else
|
else if (item == BundledFile)
|
||||||
|
itemType = QLatin1String("bundled");
|
||||||
|
else // Jar or BundledJar
|
||||||
itemType = QLatin1String("jar");
|
itemType = QLatin1String("jar");
|
||||||
|
|
||||||
QString localLibs;
|
QString localLibs;
|
||||||
@@ -841,6 +892,7 @@ QString AndroidManager::loadLocal(ProjectExplorer::Target *target, int apiLevel,
|
|||||||
if (libs.contains(element.attribute(QLatin1String("name")))) {
|
if (libs.contains(element.attribute(QLatin1String("name")))) {
|
||||||
QDomElement libElement = element.firstChildElement(QLatin1String("depends")).firstChildElement(itemType);
|
QDomElement libElement = element.firstChildElement(QLatin1String("depends")).firstChildElement(itemType);
|
||||||
while (!libElement.isNull()) {
|
while (!libElement.isNull()) {
|
||||||
|
if (libElement.attribute(QLatin1String("bundling")).toInt() == (item == BundledJar ? 1 : 0)) {
|
||||||
if (libElement.hasAttribute(attribute)) {
|
if (libElement.hasAttribute(attribute)) {
|
||||||
QString dependencyLib = libElement.attribute(attribute).arg(apiLevel);
|
QString dependencyLib = libElement.attribute(attribute).arg(apiLevel);
|
||||||
if (!dependencyLibs.contains(dependencyLib))
|
if (!dependencyLibs.contains(dependencyLib))
|
||||||
@@ -852,6 +904,7 @@ QString AndroidManager::loadLocal(ProjectExplorer::Target *target, int apiLevel,
|
|||||||
if (!replacedLibs.contains(replacedLib))
|
if (!replacedLibs.contains(replacedLib))
|
||||||
replacedLibs << replacedLib;
|
replacedLibs << replacedLib;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
libElement = libElement.nextSiblingElement(itemType);
|
libElement = libElement.nextSiblingElement(itemType);
|
||||||
}
|
}
|
||||||
@@ -1058,5 +1111,10 @@ QString AndroidManager::libGnuStl(const QString &arch, const QString &ndkToolCha
|
|||||||
+ QLatin1String("/libgnustl_shared.so");
|
+ QLatin1String("/libgnustl_shared.so");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString AndroidManager::libraryPrefix()
|
||||||
|
{
|
||||||
|
return AndroidLibraryPrefix;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Internal
|
} // namespace Internal
|
||||||
} // namespace Qt4ProjectManager
|
} // namespace Qt4ProjectManager
|
||||||
|
|||||||
@@ -85,7 +85,8 @@ public:
|
|||||||
static bool setTargetApplication(ProjectExplorer::Target *target, const QString &name);
|
static bool setTargetApplication(ProjectExplorer::Target *target, const QString &name);
|
||||||
static QString targetApplicationPath(ProjectExplorer::Target *target);
|
static QString targetApplicationPath(ProjectExplorer::Target *target);
|
||||||
|
|
||||||
static bool setUseLocalLibs(ProjectExplorer::Target *target, bool useLocalLibs, int deviceAPILevel);
|
static bool updateDeploymentSettings(ProjectExplorer::Target *target);
|
||||||
|
static bool bundleQt(ProjectExplorer::Target *target);
|
||||||
|
|
||||||
static QString targetSDK(ProjectExplorer::Target *target);
|
static QString targetSDK(ProjectExplorer::Target *target);
|
||||||
static bool setTargetSDK(ProjectExplorer::Target *target, const QString &sdk);
|
static bool setTargetSDK(ProjectExplorer::Target *target, const QString &sdk);
|
||||||
@@ -103,9 +104,10 @@ public:
|
|||||||
const QString &name = QString());
|
const QString &name = QString());
|
||||||
|
|
||||||
static Utils::FileName localLibsRulesFilePath(ProjectExplorer::Target *target);
|
static Utils::FileName localLibsRulesFilePath(ProjectExplorer::Target *target);
|
||||||
static QString loadLocalLibs(ProjectExplorer::Target *target, int apiLevel);
|
static QString loadLocalLibs(ProjectExplorer::Target *target, int apiLevel = -1);
|
||||||
static QString loadLocalJars(ProjectExplorer::Target *target, int apiLevel);
|
static QString loadLocalJars(ProjectExplorer::Target *target, int apiLevel = -1);
|
||||||
static QString loadLocalJarsInitClasses(ProjectExplorer::Target *target, int apiLevel);
|
static QString loadLocalBundledFiles(ProjectExplorer::Target *target, int apiLevel = -1);
|
||||||
|
static QString loadLocalJarsInitClasses(ProjectExplorer::Target *target, int apiLevel = -1);
|
||||||
|
|
||||||
class Library
|
class Library
|
||||||
{
|
{
|
||||||
@@ -123,11 +125,17 @@ public:
|
|||||||
static QStringList qtLibs(ProjectExplorer::Target *target);
|
static QStringList qtLibs(ProjectExplorer::Target *target);
|
||||||
static bool setQtLibs(ProjectExplorer::Target *target, const QStringList &libs);
|
static bool setQtLibs(ProjectExplorer::Target *target, const QStringList &libs);
|
||||||
|
|
||||||
|
static bool setBundledInLib(ProjectExplorer::Target *target,
|
||||||
|
const QStringList &fileList);
|
||||||
|
static bool setBundledInAssets(ProjectExplorer::Target *target,
|
||||||
|
const QStringList &fileList);
|
||||||
|
|
||||||
static QStringList availablePrebundledLibs(ProjectExplorer::Target *target);
|
static QStringList availablePrebundledLibs(ProjectExplorer::Target *target);
|
||||||
static QStringList prebundledLibs(ProjectExplorer::Target *target);
|
static QStringList prebundledLibs(ProjectExplorer::Target *target);
|
||||||
static bool setPrebundledLibs(ProjectExplorer::Target *target, const QStringList &libs);
|
static bool setPrebundledLibs(ProjectExplorer::Target *target, const QStringList &libs);
|
||||||
|
|
||||||
static QString libGnuStl(const QString &arch, const QString &ndkToolChainVersion);
|
static QString libGnuStl(const QString &arch, const QString &ndkToolChainVersion);
|
||||||
|
static QString libraryPrefix();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static void raiseError(const QString &reason);
|
static void raiseError(const QString &reason);
|
||||||
@@ -143,7 +151,9 @@ private:
|
|||||||
enum ItemType
|
enum ItemType
|
||||||
{
|
{
|
||||||
Lib,
|
Lib,
|
||||||
Jar
|
Jar,
|
||||||
|
BundledFile,
|
||||||
|
BundledJar
|
||||||
};
|
};
|
||||||
static QString loadLocal(ProjectExplorer::Target *target, int apiLevel, ItemType item, const QString &attribute=QLatin1String("file"));
|
static QString loadLocal(ProjectExplorer::Target *target, int apiLevel, ItemType item, const QString &attribute=QLatin1String("file"));
|
||||||
|
|
||||||
|
|||||||
@@ -44,6 +44,7 @@
|
|||||||
#include <qt4projectmanager/qt4buildconfiguration.h>
|
#include <qt4projectmanager/qt4buildconfiguration.h>
|
||||||
#include <qt4projectmanager/qt4project.h>
|
#include <qt4projectmanager/qt4project.h>
|
||||||
#include <qt4projectmanager/qt4nodes.h>
|
#include <qt4projectmanager/qt4nodes.h>
|
||||||
|
#include <qtsupport/qtkitinformation.h>
|
||||||
|
|
||||||
#include <coreplugin/icore.h>
|
#include <coreplugin/icore.h>
|
||||||
#include <coreplugin/fileutils.h>
|
#include <coreplugin/fileutils.h>
|
||||||
@@ -129,6 +130,7 @@ void AndroidPackageCreationStep::ctor()
|
|||||||
{
|
{
|
||||||
setDefaultDisplayName(tr("Packaging for Android"));
|
setDefaultDisplayName(tr("Packaging for Android"));
|
||||||
m_openPackageLocation = true;
|
m_openPackageLocation = true;
|
||||||
|
m_bundleQt = false;
|
||||||
connect(&m_outputParser, SIGNAL(addTask(ProjectExplorer::Task)), this, SIGNAL(addTask(ProjectExplorer::Task)));
|
connect(&m_outputParser, SIGNAL(addTask(ProjectExplorer::Task)), this, SIGNAL(addTask(ProjectExplorer::Task)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -314,6 +316,17 @@ void AndroidPackageCreationStep::initCheckRequiredLibrariesForRun()
|
|||||||
m_prebundledLibs = AndroidManager::prebundledLibs(target());
|
m_prebundledLibs = AndroidManager::prebundledLibs(target());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AndroidPackageCreationStep::getBundleInformation()
|
||||||
|
{
|
||||||
|
m_bundleQt = AndroidManager::bundleQt(target());
|
||||||
|
if (m_bundleQt) {
|
||||||
|
m_bundledJars = AndroidManager::loadLocalJars(target()).split(QLatin1Char(':'),
|
||||||
|
QString::SkipEmptyParts);
|
||||||
|
m_otherBundledFiles = AndroidManager::loadLocalBundledFiles(target()).split(QLatin1Char(':'),
|
||||||
|
QString::SkipEmptyParts);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void AndroidPackageCreationStep::checkRequiredLibrariesForRun()
|
void AndroidPackageCreationStep::checkRequiredLibrariesForRun()
|
||||||
{
|
{
|
||||||
QProcess readelfProc;
|
QProcess readelfProc;
|
||||||
@@ -329,8 +342,11 @@ void AndroidPackageCreationStep::checkRequiredLibrariesForRun()
|
|||||||
QStringList libs;
|
QStringList libs;
|
||||||
parseSharedLibs(readelfProc.readAll(), &libs);
|
parseSharedLibs(readelfProc.readAll(), &libs);
|
||||||
|
|
||||||
|
m_qtLibsWithDependencies = requiredLibraries(m_availableQtLibs, m_qtLibs, libs);
|
||||||
QMetaObject::invokeMethod(this, "setQtLibs",Qt::BlockingQueuedConnection,
|
QMetaObject::invokeMethod(this, "setQtLibs",Qt::BlockingQueuedConnection,
|
||||||
Q_ARG(QStringList, requiredLibraries(m_availableQtLibs, m_qtLibs, libs)));
|
Q_ARG(QStringList, m_qtLibsWithDependencies));
|
||||||
|
|
||||||
|
QMetaObject::invokeMethod(this, "getBundleInformation");
|
||||||
|
|
||||||
QStringList prebundledLibraries;
|
QStringList prebundledLibraries;
|
||||||
foreach (const AndroidManager::Library &qtLib, m_availableQtLibs) {
|
foreach (const AndroidManager::Library &qtLib, m_availableQtLibs) {
|
||||||
@@ -429,6 +445,160 @@ QVariantMap AndroidPackageCreationStep::toMap() const
|
|||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QStringList AndroidPackageCreationStep::collectRelativeFilePaths(const QString &parentPath)
|
||||||
|
{
|
||||||
|
QStringList relativeFilePaths;
|
||||||
|
|
||||||
|
QDirIterator libsIt(parentPath, QDir::NoFilter, QDirIterator::Subdirectories);
|
||||||
|
int pos = parentPath.size();
|
||||||
|
while (libsIt.hasNext()) {
|
||||||
|
libsIt.next();
|
||||||
|
if (!libsIt.fileInfo().isDir())
|
||||||
|
relativeFilePaths.append(libsIt.filePath().mid(pos));
|
||||||
|
}
|
||||||
|
|
||||||
|
return relativeFilePaths;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AndroidPackageCreationStep::collectFiles(QList<DeployItem> *deployList,
|
||||||
|
QList<DeployItem> *pluginsAndImportsList)
|
||||||
|
{
|
||||||
|
Q_ASSERT(deployList != 0);
|
||||||
|
QtSupport::BaseQtVersion *version = QtSupport::QtKitInformation::qtVersion(target()->kit());
|
||||||
|
if (!version)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Qt4Project *project = static_cast<Qt4Project *>(target()->project());
|
||||||
|
QString androidTargetArch = project->rootQt4ProjectNode()->singleVariableValue(Qt4ProjectManager::AndroidArchVar);
|
||||||
|
|
||||||
|
QString androidAssetsPath = m_androidDir.toString() + QLatin1String("/assets/");
|
||||||
|
QString androidJarPath = m_androidDir.toString() + QLatin1String("/libs/");
|
||||||
|
QString androidLibPath = m_androidDir.toString() + QLatin1String("/libs/") + androidTargetArch;
|
||||||
|
|
||||||
|
QString qtVersionSourcePath = version->sourcePath().toString();
|
||||||
|
|
||||||
|
foreach (QString qtLib, m_qtLibsWithDependencies) {
|
||||||
|
QString fullPath = qtVersionSourcePath
|
||||||
|
+ QLatin1String("/lib/lib")
|
||||||
|
+ qtLib
|
||||||
|
+ QLatin1String(".so");
|
||||||
|
QString destinationPath = androidLibPath
|
||||||
|
+ QLatin1String("/lib")
|
||||||
|
+ qtLib
|
||||||
|
+ QLatin1String(".so");
|
||||||
|
|
||||||
|
DeployItem deployItem(fullPath, 0, destinationPath, true);
|
||||||
|
deployList->append(deployItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (QString jar, m_bundledJars) {
|
||||||
|
QString fullPath = qtVersionSourcePath + QLatin1Char('/') + jar;
|
||||||
|
QFileInfo fileInfo(fullPath);
|
||||||
|
if (fileInfo.exists()) {
|
||||||
|
QString destinationPath = androidJarPath
|
||||||
|
+ AndroidManager::libraryPrefix()
|
||||||
|
+ fileInfo.fileName();
|
||||||
|
deployList->append(DeployItem(fullPath, 0, destinationPath, true));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QSet<QString> alreadyListed;
|
||||||
|
foreach (QString bundledFile, m_otherBundledFiles) {
|
||||||
|
if (!bundledFile.endsWith(QLatin1Char('/')))
|
||||||
|
bundledFile.append(QLatin1Char('/'));
|
||||||
|
|
||||||
|
QStringList allFiles = collectRelativeFilePaths(qtVersionSourcePath + QLatin1Char('/') + bundledFile);
|
||||||
|
foreach (QString file, allFiles) {
|
||||||
|
QString fullPath = qtVersionSourcePath + QLatin1Char('/') + bundledFile + QLatin1Char('/') + file;
|
||||||
|
if (alreadyListed.contains(fullPath))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
alreadyListed.insert(fullPath);
|
||||||
|
|
||||||
|
QString garbledFileName;
|
||||||
|
QString destinationPath;
|
||||||
|
bool shouldStrip = false;
|
||||||
|
if (file.endsWith(QLatin1String(".so"))) {
|
||||||
|
garbledFileName = QLatin1String("lib")
|
||||||
|
+ AndroidManager::libraryPrefix()
|
||||||
|
+ QString(bundledFile).replace(QLatin1Char('/'), QLatin1Char('_'))
|
||||||
|
+ QString(file).replace(QLatin1Char('/'), QLatin1Char('_'));
|
||||||
|
destinationPath = androidLibPath + QLatin1Char('/') + garbledFileName;
|
||||||
|
shouldStrip = true;
|
||||||
|
} else {
|
||||||
|
garbledFileName = AndroidManager::libraryPrefix() + bundledFile + file;
|
||||||
|
destinationPath = androidAssetsPath + garbledFileName;
|
||||||
|
}
|
||||||
|
|
||||||
|
deployList->append(DeployItem(fullPath, 0, destinationPath, shouldStrip));
|
||||||
|
pluginsAndImportsList->append(DeployItem(garbledFileName,
|
||||||
|
0,
|
||||||
|
bundledFile + file,
|
||||||
|
shouldStrip));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AndroidPackageCreationStep::removeManagedFilesFromPackage()
|
||||||
|
{
|
||||||
|
// Clean up all files managed by Qt Creator
|
||||||
|
{
|
||||||
|
QString androidLibPath = m_androidDir.toString() + QLatin1String("/libs/");
|
||||||
|
QDirIterator dirIt(m_androidDir.toString(), QDirIterator::Subdirectories);
|
||||||
|
while (dirIt.hasNext()) {
|
||||||
|
dirIt.next();
|
||||||
|
|
||||||
|
if (!dirIt.fileInfo().isDir()) {
|
||||||
|
bool isQtLibrary = dirIt.fileInfo().path().startsWith(androidLibPath)
|
||||||
|
&& dirIt.fileName().startsWith(QLatin1String("libQt5"))
|
||||||
|
&& dirIt.fileName().endsWith(QLatin1String(".so"));
|
||||||
|
|
||||||
|
if (dirIt.filePath().contains(AndroidManager::libraryPrefix()) || isQtLibrary)
|
||||||
|
QFile::remove(dirIt.filePath());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AndroidPackageCreationStep::copyFilesIntoPackage(const QList<DeployItem> &deployList)
|
||||||
|
{
|
||||||
|
foreach (DeployItem item, deployList) {
|
||||||
|
QFileInfo info(item.remoteFileName);
|
||||||
|
if (info.exists())
|
||||||
|
QFile::remove(item.remoteFileName);
|
||||||
|
else
|
||||||
|
QDir().mkpath(info.absolutePath());
|
||||||
|
|
||||||
|
QFile::copy(item.localFileName, item.remoteFileName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AndroidPackageCreationStep::stripFiles(const QList<DeployItem> &deployList)
|
||||||
|
{
|
||||||
|
|
||||||
|
QStringList fileList;
|
||||||
|
foreach (DeployItem item, deployList)
|
||||||
|
if (item.needsStrip)
|
||||||
|
fileList.append(item.remoteFileName);
|
||||||
|
|
||||||
|
ProjectExplorer::ToolChain *tc = ProjectExplorer::ToolChainKitInformation::toolChain(target()->kit());
|
||||||
|
if (tc->type() != QLatin1String(Constants::ANDROID_TOOLCHAIN_TYPE))
|
||||||
|
return;
|
||||||
|
|
||||||
|
AndroidToolChain *atc = static_cast<AndroidToolChain *>(tc);
|
||||||
|
stripAndroidLibs(fileList,
|
||||||
|
target()->activeRunConfiguration()->abi().architecture(),
|
||||||
|
atc->ndkToolChainVersion());
|
||||||
|
}
|
||||||
|
|
||||||
|
void AndroidPackageCreationStep::updateXmlForFiles(const QStringList &inLibList,
|
||||||
|
const QStringList &inAssetsList)
|
||||||
|
{
|
||||||
|
AndroidManager::setBundledInLib(target(), inLibList);
|
||||||
|
AndroidManager::setBundledInAssets(target(), inAssetsList);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
bool AndroidPackageCreationStep::createPackage()
|
bool AndroidPackageCreationStep::createPackage()
|
||||||
{
|
{
|
||||||
checkRequiredLibrariesForRun();
|
checkRequiredLibrariesForRun();
|
||||||
@@ -451,6 +621,49 @@ bool AndroidPackageCreationStep::createPackage()
|
|||||||
build << QLatin1String("release");
|
build << QLatin1String("release");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QList<DeployItem> deployFiles;
|
||||||
|
QList<DeployItem> importsAndPlugins;
|
||||||
|
|
||||||
|
QtSupport::BaseQtVersion *version = QtSupport::QtKitInformation::qtVersion(target()->kit());
|
||||||
|
|
||||||
|
// Qt 5 supports bundling libraries inside the apk. We guard the code for Qt 5 to be sure we
|
||||||
|
// do not disrupt existing projects.
|
||||||
|
if (version && version->qtVersion() >= QtSupport::QtVersionNumber(5, 0, 0)) {
|
||||||
|
bool bundleQt = AndroidManager::bundleQt(target());
|
||||||
|
|
||||||
|
// Collect the files to bundle in the package
|
||||||
|
if (bundleQt)
|
||||||
|
collectFiles(&deployFiles, &importsAndPlugins);
|
||||||
|
|
||||||
|
// Remove files from package if they are not needed
|
||||||
|
removeManagedFilesFromPackage();
|
||||||
|
|
||||||
|
// Deploy files to package
|
||||||
|
if (bundleQt) {
|
||||||
|
copyFilesIntoPackage(deployFiles);
|
||||||
|
stripFiles(deployFiles);
|
||||||
|
|
||||||
|
QStringList inLibList;
|
||||||
|
QStringList inAssetsList;
|
||||||
|
foreach (DeployItem deployItem, importsAndPlugins) {
|
||||||
|
QString conversionInfo = deployItem.localFileName
|
||||||
|
+ QLatin1Char(':')
|
||||||
|
+ deployItem.remoteFileName;
|
||||||
|
|
||||||
|
if (deployItem.localFileName.endsWith(QLatin1String(".so")))
|
||||||
|
inLibList.append(conversionInfo);
|
||||||
|
else
|
||||||
|
inAssetsList.append(conversionInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
QMetaObject::invokeMethod(this,
|
||||||
|
"updateXmlForFiles",
|
||||||
|
Qt::BlockingQueuedConnection,
|
||||||
|
Q_ARG(QStringList, inLibList),
|
||||||
|
Q_ARG(QStringList, inAssetsList));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
emit addOutput(tr("Creating package file ..."), MessageOutput);
|
emit addOutput(tr("Creating package file ..."), MessageOutput);
|
||||||
|
|
||||||
QProcess *const buildProc = new QProcess;
|
QProcess *const buildProc = new QProcess;
|
||||||
|
|||||||
@@ -45,6 +45,8 @@ QT_END_NAMESPACE
|
|||||||
namespace Android {
|
namespace Android {
|
||||||
namespace Internal {
|
namespace Internal {
|
||||||
|
|
||||||
|
class DeployItem;
|
||||||
|
|
||||||
class AndroidPackageCreationStep : public ProjectExplorer::BuildStep
|
class AndroidPackageCreationStep : public ProjectExplorer::BuildStep
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
@@ -82,6 +84,8 @@ private slots:
|
|||||||
void showInGraphicalShell();
|
void showInGraphicalShell();
|
||||||
void setQtLibs(const QStringList &qtLibs);
|
void setQtLibs(const QStringList &qtLibs);
|
||||||
void setPrebundledLibs(const QStringList &prebundledLibs);
|
void setPrebundledLibs(const QStringList &prebundledLibs);
|
||||||
|
void updateXmlForFiles(const QStringList &inLibList, const QStringList &inAssetsList);
|
||||||
|
void getBundleInformation();
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void updateRequiredLibrariesModels();
|
void updateRequiredLibrariesModels();
|
||||||
@@ -100,6 +104,12 @@ private:
|
|||||||
void raiseError(const QString &shortMsg,
|
void raiseError(const QString &shortMsg,
|
||||||
const QString &detailedMsg = QString());
|
const QString &detailedMsg = QString());
|
||||||
|
|
||||||
|
QStringList collectRelativeFilePaths(const QString &parentPath);
|
||||||
|
void collectFiles(QList<DeployItem> *deployList, QList<DeployItem> *pluginsAndImports);
|
||||||
|
void removeManagedFilesFromPackage();
|
||||||
|
void copyFilesIntoPackage(const QList<DeployItem> &deployList);
|
||||||
|
void stripFiles(const QList<DeployItem> &deployList);
|
||||||
|
|
||||||
static const Core::Id CreatePackageId;
|
static const Core::Id CreatePackageId;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -126,8 +136,13 @@ private:
|
|||||||
Utils::FileName m_appPath;
|
Utils::FileName m_appPath;
|
||||||
Utils::FileName m_readElf;
|
Utils::FileName m_readElf;
|
||||||
QStringList m_qtLibs;
|
QStringList m_qtLibs;
|
||||||
|
QStringList m_qtLibsWithDependencies;
|
||||||
QVector<AndroidManager::Library> m_availableQtLibs;
|
QVector<AndroidManager::Library> m_availableQtLibs;
|
||||||
QStringList m_prebundledLibs;
|
QStringList m_prebundledLibs;
|
||||||
|
|
||||||
|
QStringList m_bundledJars;
|
||||||
|
QStringList m_otherBundledFiles;
|
||||||
|
bool m_bundleQt;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Internal
|
} // namespace Internal
|
||||||
|
|||||||
@@ -387,24 +387,16 @@ void AndroidPackageCreationWidget::setQtLibs(QModelIndex, QModelIndex)
|
|||||||
{
|
{
|
||||||
AndroidManager::setQtLibs(m_step->target(), m_qtLibsModel->checkedItems());
|
AndroidManager::setQtLibs(m_step->target(), m_qtLibsModel->checkedItems());
|
||||||
AndroidDeployStep * const deployStep = AndroidGlobal::buildStep<AndroidDeployStep>(m_step->target()->activeDeployConfiguration());
|
AndroidDeployStep * const deployStep = AndroidGlobal::buildStep<AndroidDeployStep>(m_step->target()->activeDeployConfiguration());
|
||||||
if (deployStep->useLocalQtLibs()) {
|
if (deployStep->useLocalQtLibs())
|
||||||
// ### Passes -1 for API level, which means it won't work with setups that require
|
AndroidManager::updateDeploymentSettings(m_step->target());
|
||||||
// library selection based on API level. Use the old approach (command line argument)
|
|
||||||
// in these cases.
|
|
||||||
AndroidManager::setUseLocalLibs(m_step->target(), true, -1);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AndroidPackageCreationWidget::setPrebundledLibs(QModelIndex, QModelIndex)
|
void AndroidPackageCreationWidget::setPrebundledLibs(QModelIndex, QModelIndex)
|
||||||
{
|
{
|
||||||
AndroidManager::setPrebundledLibs(m_step->target(), m_prebundledLibs->checkedItems());
|
AndroidManager::setPrebundledLibs(m_step->target(), m_prebundledLibs->checkedItems());
|
||||||
AndroidDeployStep * const deployStep = AndroidGlobal::buildStep<AndroidDeployStep>(m_step->target()->activeDeployConfiguration());
|
AndroidDeployStep * const deployStep = AndroidGlobal::buildStep<AndroidDeployStep>(m_step->target()->activeDeployConfiguration());
|
||||||
if (deployStep->useLocalQtLibs()) {
|
if (deployStep->useLocalQtLibs())
|
||||||
// ### Passes -1 for API level, which means it won't work with setups that require
|
AndroidManager::updateDeploymentSettings(m_step->target());
|
||||||
// library selection based on API level. Use the old approach (command line argument)
|
|
||||||
// in these cases.
|
|
||||||
AndroidManager::setUseLocalLibs(m_step->target(), true, -1);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AndroidPackageCreationWidget::prebundledLibSelected(const QModelIndex &index)
|
void AndroidPackageCreationWidget::prebundledLibSelected(const QModelIndex &index)
|
||||||
|
|||||||
Reference in New Issue
Block a user