2012-04-18 20:30:57 +03:00
|
|
|
/**************************************************************************
|
|
|
|
|
**
|
2013-01-28 17:12:19 +01:00
|
|
|
** Copyright (c) 2013 BogDan Vatra <bog_dan_ro@yahoo.com>
|
2012-10-02 09:12:39 +02:00
|
|
|
** Contact: http://www.qt-project.org/legal
|
2012-04-18 20:30:57 +03:00
|
|
|
**
|
2012-10-02 09:12:39 +02:00
|
|
|
** This file is part of Qt Creator.
|
2012-04-18 20:30:57 +03:00
|
|
|
**
|
2012-10-02 09:12:39 +02:00
|
|
|
** 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 Digia. For licensing terms and
|
|
|
|
|
** conditions see http://qt.digia.com/licensing. For further information
|
|
|
|
|
** use the contact form at http://qt.digia.com/contact-us.
|
2012-04-18 20:30:57 +03:00
|
|
|
**
|
|
|
|
|
** GNU Lesser General Public License Usage
|
2012-10-02 09:12:39 +02:00
|
|
|
** Alternatively, this file may be used under the terms of the GNU Lesser
|
|
|
|
|
** General Public License version 2.1 as published by the Free Software
|
|
|
|
|
** Foundation and appearing in the file LICENSE.LGPL included in the
|
|
|
|
|
** packaging of this file. Please review the following information to
|
|
|
|
|
** ensure the GNU Lesser General Public License version 2.1 requirements
|
|
|
|
|
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
|
|
|
|
**
|
|
|
|
|
** In addition, as a special exception, Digia gives you certain additional
|
|
|
|
|
** rights. These rights are described in the Digia Qt LGPL Exception
|
2012-04-18 20:30:57 +03:00
|
|
|
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
|
|
|
|
**
|
2012-10-02 09:12:39 +02:00
|
|
|
****************************************************************************/
|
2012-04-18 20:30:57 +03:00
|
|
|
|
|
|
|
|
#include "androidpackagecreationstep.h"
|
|
|
|
|
|
|
|
|
|
#include "androidconstants.h"
|
|
|
|
|
#include "androiddeploystep.h"
|
|
|
|
|
#include "androidglobal.h"
|
|
|
|
|
#include "androidpackagecreationwidget.h"
|
2012-04-24 15:49:09 +02:00
|
|
|
#include "androidmanager.h"
|
2013-01-25 16:49:22 +01:00
|
|
|
#include "androidgdbserverkitinformation.h"
|
|
|
|
|
#include "androidtoolchain.h"
|
2012-04-18 20:30:57 +03:00
|
|
|
|
|
|
|
|
#include <projectexplorer/buildsteplist.h>
|
|
|
|
|
#include <projectexplorer/projectexplorerconstants.h>
|
|
|
|
|
#include <projectexplorer/runconfiguration.h>
|
2012-04-24 15:49:09 +02:00
|
|
|
#include <projectexplorer/target.h>
|
2012-04-18 20:30:57 +03:00
|
|
|
#include <qt4projectmanager/qt4buildconfiguration.h>
|
|
|
|
|
#include <qt4projectmanager/qt4project.h>
|
|
|
|
|
#include <qt4projectmanager/qt4nodes.h>
|
2013-04-19 12:27:58 +02:00
|
|
|
#include <qtsupport/qtkitinformation.h>
|
2012-04-18 20:30:57 +03:00
|
|
|
|
|
|
|
|
#include <coreplugin/icore.h>
|
|
|
|
|
#include <coreplugin/fileutils.h>
|
|
|
|
|
|
|
|
|
|
#include <QAbstractListModel>
|
|
|
|
|
#include <QProcess>
|
|
|
|
|
#include <QVector>
|
|
|
|
|
#include <QPair>
|
|
|
|
|
#include <QWidget>
|
|
|
|
|
#include <QMessageBox>
|
|
|
|
|
#include <QInputDialog>
|
|
|
|
|
#include <QMainWindow>
|
|
|
|
|
|
|
|
|
|
using namespace ProjectExplorer;
|
|
|
|
|
using namespace ProjectExplorer::Constants;
|
|
|
|
|
|
|
|
|
|
namespace Android {
|
|
|
|
|
namespace Internal {
|
|
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
const QLatin1String KeystoreLocationKey("KeystoreLocation");
|
2013-08-30 17:28:36 +02:00
|
|
|
const QLatin1String SignPackageKey("SignPackage");
|
2012-04-18 20:30:57 +03:00
|
|
|
const QLatin1String AliasString("Alias name:");
|
|
|
|
|
const QLatin1String CertificateSeparator("*******************************************");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
using namespace Qt4ProjectManager;
|
|
|
|
|
|
|
|
|
|
class CertificatesModel: public QAbstractListModel
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
CertificatesModel(const QString &rowCertificates, QObject *parent)
|
|
|
|
|
: QAbstractListModel(parent)
|
|
|
|
|
{
|
|
|
|
|
int from = rowCertificates.indexOf(AliasString);
|
|
|
|
|
QPair<QString, QString> item;
|
|
|
|
|
while (from > -1) {
|
|
|
|
|
from += 11;// strlen(AliasString);
|
2012-07-16 11:04:42 +02:00
|
|
|
const int eol = rowCertificates.indexOf(QLatin1Char('\n'), from);
|
2012-04-18 20:30:57 +03:00
|
|
|
item.first = rowCertificates.mid(from, eol - from).trimmed();
|
|
|
|
|
const int eoc = rowCertificates.indexOf(CertificateSeparator, eol);
|
|
|
|
|
item.second = rowCertificates.mid(eol + 1, eoc - eol - 2).trimmed();
|
|
|
|
|
from = rowCertificates.indexOf(AliasString, eoc);
|
|
|
|
|
m_certs.push_back(item);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected:
|
|
|
|
|
int rowCount(const QModelIndex &parent = QModelIndex()) const
|
|
|
|
|
{
|
|
|
|
|
if (parent.isValid())
|
|
|
|
|
return 0;
|
|
|
|
|
return m_certs.size();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const
|
|
|
|
|
{
|
|
|
|
|
if (!index.isValid() || (role != Qt::DisplayRole && role != Qt::ToolTipRole))
|
|
|
|
|
return QVariant();
|
|
|
|
|
if (role == Qt::DisplayRole)
|
|
|
|
|
return m_certs[index.row()].first;
|
|
|
|
|
return m_certs[index.row()].second;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
QVector<QPair<QString, QString> > m_certs;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
AndroidPackageCreationStep::AndroidPackageCreationStep(BuildStepList *bsl)
|
|
|
|
|
: BuildStep(bsl, CreatePackageId)
|
|
|
|
|
{
|
|
|
|
|
ctor();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
AndroidPackageCreationStep::AndroidPackageCreationStep(BuildStepList *bsl,
|
|
|
|
|
AndroidPackageCreationStep *other)
|
|
|
|
|
: BuildStep(bsl, other)
|
|
|
|
|
{
|
|
|
|
|
ctor();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void AndroidPackageCreationStep::ctor()
|
|
|
|
|
{
|
|
|
|
|
setDefaultDisplayName(tr("Packaging for Android"));
|
|
|
|
|
m_openPackageLocation = true;
|
2013-04-19 12:27:58 +02:00
|
|
|
m_bundleQt = false;
|
2013-08-30 17:28:36 +02:00
|
|
|
m_signPackage = false;
|
2012-04-18 20:30:57 +03:00
|
|
|
connect(&m_outputParser, SIGNAL(addTask(ProjectExplorer::Task)), this, SIGNAL(addTask(ProjectExplorer::Task)));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool AndroidPackageCreationStep::init()
|
|
|
|
|
{
|
2012-04-24 15:49:09 +02:00
|
|
|
const Qt4BuildConfiguration *bc = qobject_cast<Qt4BuildConfiguration *>(target()->activeBuildConfiguration());
|
|
|
|
|
if (!bc) {
|
2012-08-01 15:25:05 +02:00
|
|
|
raiseError(tr("Cannot create Android package: current build configuration is not Qt 4."));
|
2012-04-18 20:30:57 +03:00
|
|
|
return false;
|
|
|
|
|
}
|
2012-04-24 15:49:09 +02:00
|
|
|
Qt4Project *project = static_cast<Qt4Project *>(target()->project());
|
|
|
|
|
m_outputParser.setProjectFileList(project->files(Project::AllFiles));
|
2012-04-18 20:30:57 +03:00
|
|
|
|
|
|
|
|
// Copying
|
2012-04-24 15:49:09 +02:00
|
|
|
m_androidDir = AndroidManager::dirPath(target());
|
2012-06-23 12:24:44 +03:00
|
|
|
Utils::FileName path = m_androidDir;
|
2013-02-27 16:29:32 +01:00
|
|
|
QString androidTargetArch = project->rootQt4ProjectNode()->singleVariableValue(Qt4ProjectManager::AndroidArchVar);
|
|
|
|
|
if (androidTargetArch.isEmpty()) {
|
|
|
|
|
raiseError(tr("Cannot create Android package: No ANDROID_TARGET_ARCH set in make spec."));
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Utils::FileName androidLibPath = path.appendPath(QLatin1String("libs/") + androidTargetArch);
|
2012-07-03 16:57:44 +03:00
|
|
|
m_gdbServerDestination = androidLibPath.appendPath(QLatin1String("gdbserver"));
|
2013-01-25 16:49:22 +01:00
|
|
|
m_gdbServerSource = AndroidGdbServerKitInformation::gdbServer(target()->kit());
|
2012-04-18 20:30:57 +03:00
|
|
|
|
2012-04-24 15:49:09 +02:00
|
|
|
if (!AndroidManager::createAndroidTemplatesIfNecessary(target()))
|
2012-04-18 20:30:57 +03:00
|
|
|
return false;
|
|
|
|
|
|
2013-08-28 15:20:54 +02:00
|
|
|
AndroidManager::updateTarget(target(), AndroidManager::buildTargetSDK(target()), AndroidManager::applicationName(target()));
|
2012-04-18 20:30:57 +03:00
|
|
|
m_antToolPath = AndroidConfigurations::instance().antToolPath();
|
2012-04-24 15:49:09 +02:00
|
|
|
m_apkPathUnsigned = AndroidManager::apkPath(target(), AndroidManager::ReleaseBuildUnsigned);
|
|
|
|
|
m_apkPathSigned = AndroidManager::apkPath(target(), AndroidManager::ReleaseBuildSigned);
|
2013-08-30 17:28:36 +02:00
|
|
|
m_signPackageForRun = m_signPackage;
|
2012-04-18 20:30:57 +03:00
|
|
|
m_keystorePathForRun = m_keystorePath;
|
|
|
|
|
m_certificatePasswdForRun = m_certificatePasswd;
|
|
|
|
|
m_jarSigner = AndroidConfigurations::instance().jarsignerPath();
|
2012-06-23 13:18:01 +03:00
|
|
|
m_zipAligner = AndroidConfigurations::instance().zipalignPath();
|
2013-06-10 18:18:38 +02:00
|
|
|
m_environment = bc->environment();
|
2013-01-25 16:49:22 +01:00
|
|
|
|
|
|
|
|
ProjectExplorer::ToolChain *tc = ProjectExplorer::ToolChainKitInformation::toolChain(target()->kit());
|
|
|
|
|
if (tc->type() != QLatin1String(Constants::ANDROID_TOOLCHAIN_TYPE))
|
|
|
|
|
return false;
|
|
|
|
|
|
2012-04-18 20:30:57 +03:00
|
|
|
initCheckRequiredLibrariesForRun();
|
2013-08-30 17:28:36 +02:00
|
|
|
|
|
|
|
|
if (m_signPackage && (bc->qmakeBuildConfiguration() & QtSupport::BaseQtVersion::DebugBuild))
|
|
|
|
|
emit addOutput(tr("Warning: Signing a debug package."), BuildStep::ErrorMessageOutput);
|
|
|
|
|
|
2012-04-18 20:30:57 +03:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void AndroidPackageCreationStep::run(QFutureInterface<bool> &fi)
|
|
|
|
|
{
|
|
|
|
|
fi.reportResult(createPackage());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
BuildStepConfigWidget *AndroidPackageCreationStep::createConfigWidget()
|
|
|
|
|
{
|
|
|
|
|
return new AndroidPackageCreationWidget(this);
|
|
|
|
|
}
|
|
|
|
|
|
2012-08-01 15:25:05 +02:00
|
|
|
static inline QString msgCannotFindElfInformation()
|
|
|
|
|
{
|
|
|
|
|
return AndroidPackageCreationStep::tr("Cannot find ELF information");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline QString msgCannotFindExecutable(const QString &appPath)
|
|
|
|
|
{
|
|
|
|
|
return AndroidPackageCreationStep::tr("Cannot find '%1'.\n"
|
|
|
|
|
"Please make sure your application is "
|
|
|
|
|
"built successfully and is selected in Application tab ('Run option').").arg(appPath);
|
|
|
|
|
}
|
|
|
|
|
|
2012-12-07 19:53:10 +02:00
|
|
|
static void parseSharedLibs(const QByteArray &buffer, QStringList *libs)
|
|
|
|
|
{
|
|
|
|
|
#if defined(_WIN32)
|
|
|
|
|
QList<QByteArray> lines = buffer.trimmed().split('\r');
|
|
|
|
|
#else
|
|
|
|
|
QList<QByteArray> lines = buffer.trimmed().split('\n');
|
|
|
|
|
#endif
|
2013-03-08 09:45:52 +01:00
|
|
|
foreach (const QByteArray &line, lines) {
|
2012-12-07 19:53:10 +02:00
|
|
|
if (line.contains("(NEEDED)") && line.contains("Shared library:") ) {
|
|
|
|
|
const int pos = line.lastIndexOf('[') + 1;
|
|
|
|
|
(*libs) << QString::fromLatin1(line.mid(pos, line.length() - pos - 1));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-03-05 14:20:00 +01:00
|
|
|
void markNeeded(const QString &library,
|
|
|
|
|
const QVector<AndroidManager::Library> &dependencies,
|
|
|
|
|
QMap<QString, bool> *neededMap)
|
|
|
|
|
{
|
|
|
|
|
if (!neededMap->contains(library))
|
|
|
|
|
return;
|
|
|
|
|
if (neededMap->value(library))
|
|
|
|
|
return;
|
|
|
|
|
neededMap->insert(library, true);
|
|
|
|
|
for (int i = 0; i < dependencies.size(); ++i) {
|
|
|
|
|
if (dependencies.at(i).name == library) {
|
|
|
|
|
foreach (const QString &dependency, dependencies.at(i).dependencies)
|
|
|
|
|
markNeeded(dependency, dependencies, neededMap);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QStringList requiredLibraries(QVector<AndroidManager::Library> availableLibraries,
|
|
|
|
|
const QStringList &checkedLibs, const QStringList &dependencies)
|
|
|
|
|
{
|
|
|
|
|
QMap<QString, bool> neededLibraries;
|
|
|
|
|
QVector<AndroidManager::Library>::const_iterator it, end;
|
|
|
|
|
it = availableLibraries.constBegin();
|
|
|
|
|
end = availableLibraries.constEnd();
|
|
|
|
|
|
|
|
|
|
for (; it != end; ++it)
|
|
|
|
|
neededLibraries[(*it).name] = false;
|
|
|
|
|
|
|
|
|
|
// Checked items are always needed
|
|
|
|
|
foreach (const QString &lib, checkedLibs)
|
|
|
|
|
markNeeded(lib, availableLibraries, &neededLibraries);
|
|
|
|
|
|
|
|
|
|
foreach (const QString &lib, dependencies) {
|
|
|
|
|
if (lib.startsWith(QLatin1String("lib"))
|
|
|
|
|
&& lib.endsWith(QLatin1String(".so")))
|
|
|
|
|
markNeeded(lib.mid(3, lib.size() - 6), availableLibraries, &neededLibraries);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (int i = availableLibraries.size() - 1; i>= 0; --i)
|
|
|
|
|
if (!neededLibraries.value(availableLibraries.at(i).name))
|
|
|
|
|
availableLibraries.remove(i);
|
|
|
|
|
|
|
|
|
|
QStringList requiredLibraries;
|
|
|
|
|
foreach (const AndroidManager::Library &lib, availableLibraries) {
|
|
|
|
|
if (neededLibraries.value(lib.name))
|
|
|
|
|
requiredLibraries << lib.name;
|
|
|
|
|
}
|
|
|
|
|
return requiredLibraries;
|
|
|
|
|
}
|
|
|
|
|
|
2012-04-18 20:30:57 +03:00
|
|
|
void AndroidPackageCreationStep::checkRequiredLibraries()
|
|
|
|
|
{
|
|
|
|
|
QProcess readelfProc;
|
2012-04-24 15:49:09 +02:00
|
|
|
QString appPath = AndroidManager::targetApplicationPath(target());
|
2012-04-18 20:30:57 +03:00
|
|
|
if (!QFile::exists(appPath)) {
|
2012-08-01 15:25:05 +02:00
|
|
|
raiseError(msgCannotFindElfInformation(), msgCannotFindExecutable(appPath));
|
2012-04-18 20:30:57 +03:00
|
|
|
return;
|
|
|
|
|
}
|
2013-01-25 16:49:22 +01:00
|
|
|
|
|
|
|
|
ProjectExplorer::ToolChain *tc = ProjectExplorer::ToolChainKitInformation::toolChain(target()->kit());
|
|
|
|
|
if (tc->type() != QLatin1String(Constants::ANDROID_TOOLCHAIN_TYPE))
|
|
|
|
|
return;
|
|
|
|
|
AndroidToolChain *atc = static_cast<AndroidToolChain *>(tc);
|
|
|
|
|
|
|
|
|
|
readelfProc.start(AndroidConfigurations::instance().readelfPath(target()->activeRunConfiguration()->abi().architecture(), atc->ndkToolChainVersion()).toString(),
|
2012-04-18 20:30:57 +03:00
|
|
|
QStringList() << QLatin1String("-d") << QLatin1String("-W") << appPath);
|
|
|
|
|
if (!readelfProc.waitForFinished(-1)) {
|
2012-12-10 23:43:21 +00:00
|
|
|
readelfProc.kill();
|
2012-04-18 20:30:57 +03:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
QStringList libs;
|
2012-12-07 19:53:10 +02:00
|
|
|
parseSharedLibs(readelfProc.readAll(), &libs);
|
2013-03-05 14:20:00 +01:00
|
|
|
AndroidManager::setQtLibs(target(), requiredLibraries(AndroidManager::availableQtLibsWithDependencies(target()),
|
|
|
|
|
AndroidManager::qtLibs(target()), libs));
|
2012-04-18 20:30:57 +03:00
|
|
|
emit updateRequiredLibrariesModels();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void AndroidPackageCreationStep::initCheckRequiredLibrariesForRun()
|
|
|
|
|
{
|
2013-01-25 16:49:22 +01:00
|
|
|
ProjectExplorer::ToolChain *tc = ProjectExplorer::ToolChainKitInformation::toolChain(target()->kit());
|
|
|
|
|
if (tc->type() != QLatin1String(Constants::ANDROID_TOOLCHAIN_TYPE))
|
|
|
|
|
return;
|
|
|
|
|
AndroidToolChain *atc = static_cast<AndroidToolChain *>(tc);
|
|
|
|
|
|
2012-04-24 15:49:09 +02:00
|
|
|
m_appPath = Utils::FileName::fromString(AndroidManager::targetApplicationPath(target()));
|
2013-01-25 16:49:22 +01:00
|
|
|
m_readElf = AndroidConfigurations::instance().readelfPath(target()->activeRunConfiguration()->abi().architecture(),
|
|
|
|
|
atc->ndkToolChainVersion());
|
2012-04-24 15:49:09 +02:00
|
|
|
m_qtLibs = AndroidManager::qtLibs(target());
|
2013-03-05 14:20:00 +01:00
|
|
|
m_availableQtLibs = AndroidManager::availableQtLibsWithDependencies(target());
|
2012-04-24 15:49:09 +02:00
|
|
|
m_prebundledLibs = AndroidManager::prebundledLibs(target());
|
2012-04-18 20:30:57 +03:00
|
|
|
}
|
|
|
|
|
|
2013-04-19 12:27:58 +02:00
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-04-18 20:30:57 +03:00
|
|
|
void AndroidPackageCreationStep::checkRequiredLibrariesForRun()
|
|
|
|
|
{
|
|
|
|
|
QProcess readelfProc;
|
2012-04-24 15:49:09 +02:00
|
|
|
if (!m_appPath.toFileInfo().exists()) {
|
2012-08-01 15:25:05 +02:00
|
|
|
raiseError(msgCannotFindElfInformation(), msgCannotFindExecutable(m_appPath.toUserOutput()));
|
2012-04-18 20:30:57 +03:00
|
|
|
return;
|
|
|
|
|
}
|
2012-04-24 15:49:09 +02:00
|
|
|
readelfProc.start(m_readElf.toString(), QStringList() << QLatin1String("-d") << QLatin1String("-W") << m_appPath.toUserOutput());
|
2012-04-18 20:30:57 +03:00
|
|
|
if (!readelfProc.waitForFinished(-1)) {
|
2012-12-10 23:43:21 +00:00
|
|
|
readelfProc.kill();
|
2012-04-18 20:30:57 +03:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
QStringList libs;
|
2012-12-07 19:53:10 +02:00
|
|
|
parseSharedLibs(readelfProc.readAll(), &libs);
|
2013-03-05 14:20:00 +01:00
|
|
|
|
2013-04-19 12:27:58 +02:00
|
|
|
m_qtLibsWithDependencies = requiredLibraries(m_availableQtLibs, m_qtLibs, libs);
|
2012-04-18 20:30:57 +03:00
|
|
|
QMetaObject::invokeMethod(this, "setQtLibs",Qt::BlockingQueuedConnection,
|
2013-04-19 12:27:58 +02:00
|
|
|
Q_ARG(QStringList, m_qtLibsWithDependencies));
|
|
|
|
|
|
2013-05-29 15:07:12 +03:00
|
|
|
QMetaObject::invokeMethod(this, "getBundleInformation", Qt::BlockingQueuedConnection);
|
2012-04-18 20:30:57 +03:00
|
|
|
|
|
|
|
|
emit updateRequiredLibrariesModels();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void AndroidPackageCreationStep::setQtLibs(const QStringList &qtLibs)
|
|
|
|
|
{
|
2012-04-24 15:49:09 +02:00
|
|
|
AndroidManager::setQtLibs(target(), qtLibs);
|
2012-04-18 20:30:57 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void AndroidPackageCreationStep::setPrebundledLibs(const QStringList &prebundledLibs)
|
|
|
|
|
{
|
2012-04-24 15:49:09 +02:00
|
|
|
AndroidManager::setPrebundledLibs(target(), prebundledLibs);
|
2012-04-18 20:30:57 +03:00
|
|
|
}
|
|
|
|
|
|
2012-04-24 15:49:09 +02:00
|
|
|
Utils::FileName AndroidPackageCreationStep::keystorePath()
|
2012-04-18 20:30:57 +03:00
|
|
|
{
|
|
|
|
|
return m_keystorePath;
|
|
|
|
|
}
|
|
|
|
|
|
2012-04-24 15:49:09 +02:00
|
|
|
void AndroidPackageCreationStep::setKeystorePath(const Utils::FileName &path)
|
2012-04-18 20:30:57 +03:00
|
|
|
{
|
|
|
|
|
m_keystorePath = path;
|
|
|
|
|
m_certificatePasswd.clear();
|
|
|
|
|
m_keystorePasswd.clear();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void AndroidPackageCreationStep::setKeystorePassword(const QString &pwd)
|
|
|
|
|
{
|
|
|
|
|
m_keystorePasswd = pwd;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void AndroidPackageCreationStep::setCertificateAlias(const QString &alias)
|
|
|
|
|
{
|
|
|
|
|
m_certificateAlias = alias;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void AndroidPackageCreationStep::setCertificatePassword(const QString &pwd)
|
|
|
|
|
{
|
|
|
|
|
m_certificatePasswd = pwd;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void AndroidPackageCreationStep::setOpenPackageLocation(bool open)
|
|
|
|
|
{
|
|
|
|
|
m_openPackageLocation = open;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QAbstractItemModel *AndroidPackageCreationStep::keystoreCertificates()
|
|
|
|
|
{
|
|
|
|
|
QString rawCerts;
|
|
|
|
|
QProcess keytoolProc;
|
|
|
|
|
while (!rawCerts.length() || !m_keystorePasswd.length()) {
|
|
|
|
|
QStringList params;
|
2012-07-07 16:36:25 +03:00
|
|
|
params << QLatin1String("-list") << QLatin1String("-v") << QLatin1String("-keystore") << m_keystorePath.toUserOutput() << QLatin1String("-storepass");
|
2012-04-18 20:30:57 +03:00
|
|
|
if (!m_keystorePasswd.length())
|
|
|
|
|
keystorePassword();
|
|
|
|
|
if (!m_keystorePasswd.length())
|
|
|
|
|
return 0;
|
|
|
|
|
params << m_keystorePasswd;
|
2013-08-08 16:21:56 +02:00
|
|
|
Utils::Environment env = Utils::Environment::systemEnvironment();
|
|
|
|
|
env.set(QLatin1String("LANG"), QLatin1String("C"));
|
|
|
|
|
keytoolProc.setProcessEnvironment(env.toProcessEnvironment());
|
2012-04-24 15:49:09 +02:00
|
|
|
keytoolProc.start(AndroidConfigurations::instance().keytoolPath().toString(), params);
|
2012-04-18 20:30:57 +03:00
|
|
|
if (!keytoolProc.waitForStarted() || !keytoolProc.waitForFinished()) {
|
|
|
|
|
QMessageBox::critical(0, tr("Error"),
|
|
|
|
|
tr("Failed to run keytool"));
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (keytoolProc.exitCode()) {
|
|
|
|
|
QMessageBox::critical(0, tr("Error"),
|
|
|
|
|
tr("Invalid password"));
|
|
|
|
|
m_keystorePasswd.clear();
|
|
|
|
|
}
|
|
|
|
|
rawCerts = QString::fromLatin1(keytoolProc.readAllStandardOutput());
|
|
|
|
|
}
|
|
|
|
|
return new CertificatesModel(rawCerts, this);
|
|
|
|
|
}
|
|
|
|
|
|
2013-08-30 17:28:36 +02:00
|
|
|
bool AndroidPackageCreationStep::signPackage() const
|
|
|
|
|
{
|
|
|
|
|
return m_signPackage;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void AndroidPackageCreationStep::setSignPackage(bool b)
|
|
|
|
|
{
|
|
|
|
|
m_signPackage = b;
|
|
|
|
|
}
|
|
|
|
|
|
2012-04-18 20:30:57 +03:00
|
|
|
bool AndroidPackageCreationStep::fromMap(const QVariantMap &map)
|
|
|
|
|
{
|
|
|
|
|
if (!BuildStep::fromMap(map))
|
|
|
|
|
return false;
|
2012-04-24 15:49:09 +02:00
|
|
|
m_keystorePath = Utils::FileName::fromString(map.value(KeystoreLocationKey).toString());
|
2013-08-30 17:28:36 +02:00
|
|
|
m_signPackage = map.value(SignPackageKey).toBool();
|
2012-04-18 20:30:57 +03:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QVariantMap AndroidPackageCreationStep::toMap() const
|
|
|
|
|
{
|
|
|
|
|
QVariantMap map(BuildStep::toMap());
|
2012-04-24 15:49:09 +02:00
|
|
|
map.insert(KeystoreLocationKey, m_keystorePath.toString());
|
2013-08-30 17:28:36 +02:00
|
|
|
map.insert(SignPackageKey, m_signPackage);
|
2012-04-18 20:30:57 +03:00
|
|
|
return map;
|
|
|
|
|
}
|
|
|
|
|
|
2013-04-19 12:27:58 +02:00
|
|
|
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");
|
|
|
|
|
|
2013-05-13 11:39:59 +02:00
|
|
|
// If the Qt lib/ folder contains libgnustl_shared.so, don't deploy it from there, since
|
|
|
|
|
// it will be deployed directly from the NDK instead.
|
|
|
|
|
if (qtLib != QLatin1String("gnustl_shared")) {
|
|
|
|
|
DeployItem deployItem(fullPath, 0, destinationPath, true);
|
|
|
|
|
deployList->append(deployItem);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!androidTargetArch.isEmpty()) {
|
|
|
|
|
ProjectExplorer::ToolChain *tc = ProjectExplorer::ToolChainKitInformation::toolChain(target()->kit());
|
|
|
|
|
if (tc->type() != QLatin1String(Constants::ANDROID_TOOLCHAIN_TYPE))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
AndroidToolChain *atc = static_cast<AndroidToolChain *>(tc);
|
|
|
|
|
|
|
|
|
|
QString libgnustl = AndroidManager::libGnuStl(androidTargetArch, atc->ndkToolChainVersion());
|
|
|
|
|
DeployItem deployItem(libgnustl, 0, androidLibPath + QLatin1String("/libgnustl_shared.so"), false);
|
2013-04-19 12:27:58 +02:00
|
|
|
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) {
|
2013-05-07 14:25:13 +02:00
|
|
|
QStringList allFiles;
|
|
|
|
|
if (QFileInfo(qtVersionSourcePath + QLatin1Char('/') + bundledFile).isDir()) {
|
|
|
|
|
if (!bundledFile.endsWith(QLatin1Char('/')))
|
|
|
|
|
bundledFile.append(QLatin1Char('/'));
|
|
|
|
|
|
|
|
|
|
allFiles = collectRelativeFilePaths(qtVersionSourcePath + QLatin1Char('/') + bundledFile);
|
|
|
|
|
} else {
|
|
|
|
|
// If we need to bundle a specific file, we just add an empty string and the file
|
|
|
|
|
// names and data will be prepared correctly in the loop below.
|
|
|
|
|
allFiles = QStringList(QString());
|
|
|
|
|
}
|
2013-04-19 12:27:58 +02:00
|
|
|
|
|
|
|
|
foreach (QString file, allFiles) {
|
2013-05-07 14:25:13 +02:00
|
|
|
QString fullPath = qtVersionSourcePath + QLatin1Char('/') + bundledFile + file;
|
2013-04-19 12:27:58 +02:00
|
|
|
if (alreadyListed.contains(fullPath))
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
alreadyListed.insert(fullPath);
|
|
|
|
|
|
|
|
|
|
QString garbledFileName;
|
|
|
|
|
QString destinationPath;
|
|
|
|
|
bool shouldStrip = false;
|
2013-05-07 14:25:13 +02:00
|
|
|
|
|
|
|
|
QString fullFileName = bundledFile + file;
|
|
|
|
|
if (fullFileName.endsWith(QLatin1String(".so"))) {
|
|
|
|
|
if (fullFileName.startsWith(QLatin1String("lib/"))) {
|
|
|
|
|
// Special case when the destination folder is lib/
|
|
|
|
|
// Since this is also the source folder, there is no need to garble the file
|
|
|
|
|
// name and copy it. We also won't have write access to this folder, so we
|
|
|
|
|
// couldn't if we wanted to.
|
|
|
|
|
garbledFileName = fullFileName.mid(sizeof("lib/") - 1);
|
|
|
|
|
} else {
|
|
|
|
|
garbledFileName = QLatin1String("lib")
|
|
|
|
|
+ AndroidManager::libraryPrefix()
|
|
|
|
|
+ QString(fullFileName).replace(QLatin1Char('/'), QLatin1Char('_'));
|
|
|
|
|
}
|
2013-04-19 12:27:58 +02:00
|
|
|
destinationPath = androidLibPath + QLatin1Char('/') + garbledFileName;
|
|
|
|
|
shouldStrip = true;
|
|
|
|
|
} else {
|
2013-05-14 15:22:06 +02:00
|
|
|
garbledFileName = AndroidManager::libraryPrefix() + QLatin1Char('/') + fullFileName;
|
2013-04-19 12:27:58 +02:00
|
|
|
destinationPath = androidAssetsPath + garbledFileName;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
deployList->append(DeployItem(fullPath, 0, destinationPath, shouldStrip));
|
|
|
|
|
pluginsAndImportsList->append(DeployItem(garbledFileName,
|
|
|
|
|
0,
|
2013-05-07 14:25:13 +02:00
|
|
|
fullFileName,
|
2013-04-19 12:27:58 +02:00
|
|
|
shouldStrip));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-08-08 17:19:39 +02:00
|
|
|
void AndroidPackageCreationStep::removeManagedFilesFromPackage(const Utils::FileName &qtLibraryDir)
|
2013-04-19 12:27:58 +02:00
|
|
|
{
|
|
|
|
|
// 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"));
|
|
|
|
|
|
2013-08-08 17:19:39 +02:00
|
|
|
if (isQtLibrary) {
|
|
|
|
|
Utils::FileName qtLibraryFile = qtLibraryDir;
|
|
|
|
|
qtLibraryFile.appendPath(dirIt.fileName());
|
|
|
|
|
isQtLibrary = qtLibraryFile.toFileInfo().exists();
|
|
|
|
|
}
|
|
|
|
|
|
2013-04-19 12:27:58 +02:00
|
|
|
if (dirIt.filePath().contains(AndroidManager::libraryPrefix()) || isQtLibrary)
|
|
|
|
|
QFile::remove(dirIt.filePath());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2013-05-14 15:22:06 +02:00
|
|
|
|
|
|
|
|
removeDirectory(m_androidDir.toString() + QLatin1String("/assets/") + AndroidManager::libraryPrefix());
|
2013-04-19 12:27:58 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2012-04-18 20:30:57 +03:00
|
|
|
bool AndroidPackageCreationStep::createPackage()
|
|
|
|
|
{
|
|
|
|
|
checkRequiredLibrariesForRun();
|
|
|
|
|
|
|
|
|
|
emit addOutput(tr("Copy Qt app & libs to Android package ..."), MessageOutput);
|
|
|
|
|
|
|
|
|
|
QStringList build;
|
2013-09-04 17:48:40 +02:00
|
|
|
build << QLatin1String("-quiet");
|
2012-04-18 20:30:57 +03:00
|
|
|
build << QLatin1String("clean");
|
2012-04-24 15:49:09 +02:00
|
|
|
QFile::remove(m_gdbServerDestination.toString());
|
2013-08-30 17:28:36 +02:00
|
|
|
if (m_signPackageForRun) {
|
|
|
|
|
build << QLatin1String("release");
|
|
|
|
|
} else {
|
2012-04-18 20:30:57 +03:00
|
|
|
build << QLatin1String("debug");
|
2013-03-27 13:27:02 +01:00
|
|
|
QDir dir;
|
|
|
|
|
dir.mkpath(m_gdbServerDestination.toFileInfo().absolutePath());
|
2012-04-24 15:49:09 +02:00
|
|
|
if (!QFile::copy(m_gdbServerSource.toString(), m_gdbServerDestination.toString())) {
|
|
|
|
|
raiseError(tr("Can't copy gdbserver from '%1' to '%2'").arg(m_gdbServerSource.toUserOutput())
|
|
|
|
|
.arg(m_gdbServerDestination.toUserOutput()));
|
2012-04-18 20:30:57 +03:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-04-19 12:27:58 +02:00
|
|
|
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
|
2013-08-08 17:19:39 +02:00
|
|
|
removeManagedFilesFromPackage(version->libraryPath());
|
2013-04-19 12:27:58 +02:00
|
|
|
|
|
|
|
|
// 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));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-04-18 20:30:57 +03:00
|
|
|
emit addOutput(tr("Creating package file ..."), MessageOutput);
|
|
|
|
|
|
|
|
|
|
QProcess *const buildProc = new QProcess;
|
2013-06-10 18:18:38 +02:00
|
|
|
buildProc->setProcessEnvironment(m_environment.toProcessEnvironment());
|
2012-04-18 20:30:57 +03:00
|
|
|
|
|
|
|
|
connect(buildProc, SIGNAL(readyReadStandardOutput()), this,
|
2013-06-26 18:18:25 +02:00
|
|
|
SLOT(handleBuildStdOutOutput()), Qt::DirectConnection);
|
2012-04-18 20:30:57 +03:00
|
|
|
connect(buildProc, SIGNAL(readyReadStandardError()), this,
|
2013-06-26 18:18:25 +02:00
|
|
|
SLOT(handleBuildStdErrOutput()), Qt::DirectConnection);
|
2012-04-18 20:30:57 +03:00
|
|
|
|
2012-04-24 15:49:09 +02:00
|
|
|
buildProc->setWorkingDirectory(m_androidDir.toString());
|
2012-04-18 20:30:57 +03:00
|
|
|
|
2012-04-24 15:49:09 +02:00
|
|
|
if (!runCommand(buildProc, m_antToolPath.toString(), build)) {
|
2012-04-18 20:30:57 +03:00
|
|
|
disconnect(buildProc, 0, this, 0);
|
|
|
|
|
buildProc->deleteLater();
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2013-08-30 17:28:36 +02:00
|
|
|
if (m_signPackageForRun) {
|
2012-04-18 20:30:57 +03:00
|
|
|
emit addOutput(tr("Signing package ..."), MessageOutput);
|
|
|
|
|
while (true) {
|
|
|
|
|
if (m_certificatePasswdForRun.isEmpty())
|
|
|
|
|
QMetaObject::invokeMethod(this, "certificatePassword", Qt::BlockingQueuedConnection);
|
|
|
|
|
|
|
|
|
|
if (m_certificatePasswdForRun.isEmpty()) {
|
|
|
|
|
disconnect(buildProc, 0, this, 0);
|
|
|
|
|
buildProc->deleteLater();
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QByteArray keyPass = m_certificatePasswdForRun.toUtf8();
|
|
|
|
|
build.clear();
|
2013-09-20 09:20:01 +03:00
|
|
|
build << QLatin1String("-verbose") << QLatin1String("-digestalg") << QLatin1String("SHA1")
|
|
|
|
|
<< QLatin1String("-sigalg") << QLatin1String("MD5withRSA")
|
|
|
|
|
<< QLatin1String("-keystore") << m_keystorePathForRun.toUserOutput()
|
2012-04-18 20:30:57 +03:00
|
|
|
<< QLatin1String("-storepass") << m_keystorePasswd
|
2012-04-24 15:49:09 +02:00
|
|
|
<< m_apkPathUnsigned.toUserOutput()
|
2012-04-18 20:30:57 +03:00
|
|
|
<< m_certificateAlias;
|
2012-06-23 13:18:01 +03:00
|
|
|
buildProc->start(m_jarSigner.toString(), build);
|
2012-04-18 20:30:57 +03:00
|
|
|
if (!buildProc->waitForStarted()) {
|
|
|
|
|
disconnect(buildProc, 0, this, 0);
|
|
|
|
|
buildProc->deleteLater();
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2012-07-16 11:04:42 +02:00
|
|
|
keyPass += '\n';
|
2012-04-18 20:30:57 +03:00
|
|
|
buildProc->write(keyPass);
|
|
|
|
|
buildProc->waitForBytesWritten();
|
|
|
|
|
buildProc->waitForFinished();
|
|
|
|
|
|
|
|
|
|
if (!buildProc->exitCode())
|
|
|
|
|
break;
|
|
|
|
|
emit addOutput(tr("Failed, try again"), ErrorMessageOutput);
|
|
|
|
|
m_certificatePasswdForRun.clear();
|
|
|
|
|
}
|
2012-06-23 13:18:01 +03:00
|
|
|
build.clear();
|
|
|
|
|
build << QLatin1String("-f") << QLatin1String("-v") << QLatin1String("4") << m_apkPathUnsigned.toString() << m_apkPathSigned.toString();
|
|
|
|
|
buildProc->start(m_zipAligner.toString(), build);
|
|
|
|
|
buildProc->waitForFinished();
|
|
|
|
|
if (!buildProc->exitCode()) {
|
|
|
|
|
QFile::remove(m_apkPathUnsigned.toString());
|
2012-04-18 20:30:57 +03:00
|
|
|
emit addOutput(tr("Release signed package created to %1")
|
2012-04-24 15:49:09 +02:00
|
|
|
.arg(m_apkPathSigned.toUserOutput())
|
2012-04-18 20:30:57 +03:00
|
|
|
, MessageOutput);
|
|
|
|
|
|
|
|
|
|
if (m_openPackageLocation)
|
|
|
|
|
QMetaObject::invokeMethod(this, "showInGraphicalShell", Qt::QueuedConnection);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
emit addOutput(tr("Package created."), BuildStep::MessageOutput);
|
|
|
|
|
disconnect(buildProc, 0, this, 0);
|
|
|
|
|
buildProc->deleteLater();
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2013-01-25 16:49:22 +01:00
|
|
|
void AndroidPackageCreationStep::stripAndroidLibs(const QStringList & files, Abi::Architecture architecture, const QString &ndkToolchainVersion)
|
2012-04-18 20:30:57 +03:00
|
|
|
{
|
|
|
|
|
QProcess stripProcess;
|
|
|
|
|
foreach (const QString &file, files) {
|
2013-01-25 16:49:22 +01:00
|
|
|
stripProcess.start(AndroidConfigurations::instance().stripPath(architecture, ndkToolchainVersion).toString(),
|
2012-04-24 15:49:09 +02:00
|
|
|
QStringList()<<QLatin1String("--strip-unneeded") << file);
|
2012-04-29 11:16:05 +03:00
|
|
|
stripProcess.waitForStarted();
|
|
|
|
|
if (!stripProcess.waitForFinished())
|
2012-12-10 23:43:21 +00:00
|
|
|
stripProcess.kill();
|
2012-04-18 20:30:57 +03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool AndroidPackageCreationStep::removeDirectory(const QString &dirPath)
|
|
|
|
|
{
|
|
|
|
|
QDir dir(dirPath);
|
|
|
|
|
if (!dir.exists())
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
const QStringList &files
|
|
|
|
|
= dir.entryList(QDir::Files | QDir::Hidden | QDir::System);
|
|
|
|
|
foreach (const QString &fileName, files) {
|
|
|
|
|
if (!dir.remove(fileName))
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const QStringList &subDirs
|
|
|
|
|
= dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
|
|
|
|
|
foreach (const QString &subDirName, subDirs) {
|
|
|
|
|
if (!removeDirectory(dirPath + QLatin1Char('/') + subDirName))
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return dir.rmdir(dirPath);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool AndroidPackageCreationStep::runCommand(QProcess *buildProc
|
|
|
|
|
, const QString &program, const QStringList &arguments)
|
|
|
|
|
{
|
|
|
|
|
emit addOutput(tr("Package deploy: Running command '%1 %2'.").arg(program).arg(arguments.join(QLatin1String(" "))), BuildStep::MessageOutput);
|
|
|
|
|
buildProc->start(program, arguments);
|
|
|
|
|
if (!buildProc->waitForStarted()) {
|
|
|
|
|
raiseError(tr("Packaging failed."),
|
|
|
|
|
tr("Packaging error: Could not start command '%1 %2'. Reason: %3")
|
|
|
|
|
.arg(program).arg(arguments.join(QLatin1String(" "))).arg(buildProc->errorString()));
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
buildProc->waitForFinished(-1);
|
2013-06-06 17:00:46 +02:00
|
|
|
|
|
|
|
|
handleProcessOutput(buildProc, false);
|
|
|
|
|
handleProcessOutput(buildProc, true);
|
|
|
|
|
|
2012-04-18 20:30:57 +03:00
|
|
|
if (buildProc->error() != QProcess::UnknownError
|
|
|
|
|
|| buildProc->exitCode() != 0) {
|
|
|
|
|
QString mainMessage = tr("Packaging Error: Command '%1 %2' failed.")
|
|
|
|
|
.arg(program).arg(arguments.join(QLatin1String(" ")));
|
|
|
|
|
if (buildProc->error() != QProcess::UnknownError)
|
|
|
|
|
mainMessage += tr(" Reason: %1").arg(buildProc->errorString());
|
|
|
|
|
else
|
|
|
|
|
mainMessage += tr("Exit code: %1").arg(buildProc->exitCode());
|
|
|
|
|
raiseError(mainMessage);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void AndroidPackageCreationStep::handleBuildStdOutOutput()
|
|
|
|
|
{
|
|
|
|
|
QProcess *const process = qobject_cast<QProcess *>(sender());
|
|
|
|
|
if (!process)
|
|
|
|
|
return;
|
2013-06-06 17:00:46 +02:00
|
|
|
handleProcessOutput(process, false);
|
2012-04-18 20:30:57 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void AndroidPackageCreationStep::handleBuildStdErrOutput()
|
|
|
|
|
{
|
|
|
|
|
QProcess *const process = qobject_cast<QProcess *>(sender());
|
|
|
|
|
if (!process)
|
|
|
|
|
return;
|
|
|
|
|
|
2013-06-06 17:00:46 +02:00
|
|
|
handleProcessOutput(process, true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void AndroidPackageCreationStep::handleProcessOutput(QProcess *process, bool stdErr)
|
|
|
|
|
{
|
|
|
|
|
process->setReadChannel(stdErr ? QProcess::StandardError : QProcess::StandardOutput);
|
2012-04-18 20:30:57 +03:00
|
|
|
while (process->canReadLine()) {
|
|
|
|
|
QString line = QString::fromLocal8Bit(process->readLine());
|
2013-06-06 17:00:46 +02:00
|
|
|
if (stdErr)
|
|
|
|
|
m_outputParser.stdError(line);
|
|
|
|
|
else
|
|
|
|
|
m_outputParser.stdOutput(line);
|
|
|
|
|
emit addOutput(line, stdErr ? BuildStep::ErrorOutput
|
|
|
|
|
: BuildStep::NormalOutput,
|
|
|
|
|
BuildStep::DontAppendNewline);
|
2012-04-18 20:30:57 +03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void AndroidPackageCreationStep::keystorePassword()
|
|
|
|
|
{
|
|
|
|
|
m_keystorePasswd.clear();
|
|
|
|
|
bool ok;
|
|
|
|
|
QString text = QInputDialog::getText(0, tr("Keystore"),
|
|
|
|
|
tr("Keystore password:"), QLineEdit::Password,
|
|
|
|
|
QString(), &ok);
|
|
|
|
|
if (ok && !text.isEmpty())
|
|
|
|
|
m_keystorePasswd = text;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void AndroidPackageCreationStep::certificatePassword()
|
|
|
|
|
{
|
|
|
|
|
m_certificatePasswdForRun.clear();
|
|
|
|
|
bool ok;
|
|
|
|
|
QString text = QInputDialog::getText(0, tr("Certificate"),
|
|
|
|
|
tr("Certificate password (%1):").arg(m_certificateAlias), QLineEdit::Password,
|
|
|
|
|
QString(), &ok);
|
|
|
|
|
if (ok && !text.isEmpty())
|
|
|
|
|
m_certificatePasswdForRun = text;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void AndroidPackageCreationStep::showInGraphicalShell()
|
|
|
|
|
{
|
2013-09-20 23:17:22 +02:00
|
|
|
Core::FileUtils::showInGraphicalShell(Core::ICore::mainWindow(), m_apkPathSigned.toString());
|
2012-04-18 20:30:57 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void AndroidPackageCreationStep::raiseError(const QString &shortMsg,
|
|
|
|
|
const QString &detailedMsg)
|
|
|
|
|
{
|
|
|
|
|
emit addOutput(detailedMsg.isNull() ? shortMsg : detailedMsg, BuildStep::ErrorOutput);
|
|
|
|
|
emit addTask(Task(Task::Error, shortMsg, Utils::FileName::fromString(QString()), -1,
|
2013-08-19 15:35:14 +02:00
|
|
|
TASK_CATEGORY_DEPLOYMENT));
|
2012-04-18 20:30:57 +03:00
|
|
|
}
|
|
|
|
|
|
2012-03-15 17:17:40 +01:00
|
|
|
const Core::Id AndroidPackageCreationStep::CreatePackageId("Qt4ProjectManager.AndroidPackageCreationStep");
|
2012-04-18 20:30:57 +03:00
|
|
|
|
|
|
|
|
} // namespace Internal
|
|
|
|
|
} // namespace Qt4ProjectManager
|