/************************************************************************** ** ** Copyright (c) 2012 BogDan Vatra ** Contact: http://www.qt-project.org/legal ** ** 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 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. ** ** GNU Lesser General Public License Usage ** 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 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ****************************************************************************/ #include "androiddeploystep.h" #include "androidconstants.h" #include "androiddeploystepwidget.h" #include "androidglobal.h" #include "androidpackagecreationstep.h" #include "androidrunconfiguration.h" #include "androidmanager.h" #include #include #include #include #include #include #include #include #define ASSERT_STATE(state) ASSERT_STATE_GENERIC(State, state, m_state) using namespace ProjectExplorer; using namespace Qt4ProjectManager; namespace Android { namespace Internal { static const char USE_LOCAL_QT_KEY[] = "Qt4ProjectManager.AndroidDeployStep.UseLocalQtLibs"; const Core::Id AndroidDeployStep::Id("Qt4ProjectManager.AndroidDeployStep"); AndroidDeployStep::AndroidDeployStep(ProjectExplorer::BuildStepList *parent) : BuildStep(parent, Id) { ctor(); } AndroidDeployStep::AndroidDeployStep(ProjectExplorer::BuildStepList *parent, AndroidDeployStep *other) : BuildStep(parent, other) { ctor(); } AndroidDeployStep::~AndroidDeployStep() { } void AndroidDeployStep::ctor() { //: AndroidDeployStep default display name setDefaultDisplayName(tr("Deploy to Android device")); m_deployAction = NoDeploy; m_useLocalQtLibs = false; } bool AndroidDeployStep::init() { m_packageName = AndroidManager::packageName(target()); const QString targetSDK = AndroidManager::targetSDK(target()); writeOutput(tr("Please wait, searching for a suitable device for target:%1.").arg(targetSDK)); m_deviceAPILevel = targetSDK.mid(targetSDK.indexOf(QLatin1Char('-')) + 1).toInt(); m_deviceSerialNumber = AndroidConfigurations::instance().getDeployDeviceSerialNumber(&m_deviceAPILevel); if (!m_deviceSerialNumber.length()) { m_deviceSerialNumber.clear(); raiseError(tr("Cannot deploy: no devices or emulators found for your package.")); return false; } QtSupport::BaseQtVersion *version = QtSupport::QtKitInformation::qtVersion(target()->kit()); if (!version) return false; const Qt4BuildConfiguration *bc = static_cast(target()->activeBuildConfiguration()); if (!bc) return false; m_qtVersionSourcePath = version->sourcePath().toString(); m_qtVersionQMakeBuildConfig = bc->qmakeBuildConfiguration(); m_androidDirPath = AndroidManager::dirPath(target()); m_apkPathDebug = AndroidManager::apkPath(target(), AndroidManager::DebugBuild).toString(); m_apkPathRelease = AndroidManager::apkPath(target(), AndroidManager::ReleaseBuildSigned).toString(); m_buildDirectory = static_cast(target()->project())->rootQt4ProjectNode()->buildDir(); m_runQASIPackagePath = m_QASIPackagePath; m_runDeployAction = m_deployAction; return true; } void AndroidDeployStep::run(QFutureInterface &fi) { fi.reportResult(deployPackage()); } BuildStepConfigWidget *AndroidDeployStep::createConfigWidget() { return new AndroidDeployStepWidget(this); } AndroidDeployStep::AndroidDeployAction AndroidDeployStep::deployAction() { return m_deployAction; } bool AndroidDeployStep::useLocalQtLibs() { return m_useLocalQtLibs; } bool AndroidDeployStep::fromMap(const QVariantMap &map) { m_useLocalQtLibs = map.value(QLatin1String(USE_LOCAL_QT_KEY), false).toBool(); return ProjectExplorer::BuildStep::fromMap(map); } QVariantMap AndroidDeployStep::toMap() const { QVariantMap map = ProjectExplorer::BuildStep::toMap(); map.insert(QLatin1String(USE_LOCAL_QT_KEY), m_useLocalQtLibs); return map; } void AndroidDeployStep::setDeployAction(AndroidDeployStep::AndroidDeployAction deploy) { m_deployAction = deploy; } void AndroidDeployStep::setDeployQASIPackagePath(const QString &package) { m_QASIPackagePath = package; m_deployAction = InstallQASI; } void AndroidDeployStep::setUseLocalQtLibs(bool useLocal) { m_useLocalQtLibs = useLocal; } bool AndroidDeployStep::runCommand(QProcess *buildProc, const QString &program, const QStringList &arguments) { writeOutput(tr("Package deploy: Running command '%1 %2'.").arg(program).arg(arguments.join(QLatin1String(" "))), BuildStep::MessageOutput); buildProc->start(program, arguments); if (!buildProc->waitForStarted()) { writeOutput(tr("Packaging error: Could not start command '%1 %2'. Reason: %3") .arg(program).arg(arguments.join(QLatin1String(" "))).arg(buildProc->errorString()), BuildStep::ErrorMessageOutput); return false; } buildProc->waitForFinished(-1); 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()); writeOutput(mainMessage, BuildStep::ErrorMessageOutput); return false; } return true; } void AndroidDeployStep::handleBuildOutput() { QProcess *const buildProc = qobject_cast(sender()); if (!buildProc) return; emit addOutput(QString::fromLocal8Bit(buildProc->readAllStandardOutput()) , BuildStep::NormalOutput); } void AndroidDeployStep::handleBuildError() { QProcess *const buildProc = qobject_cast(sender()); if (!buildProc) return; emit addOutput(QString::fromLocal8Bit(buildProc->readAllStandardError()) , BuildStep::ErrorOutput); } QString AndroidDeployStep::deviceSerialNumber() { return m_deviceSerialNumber; } int AndroidDeployStep::deviceAPILevel() { return m_deviceAPILevel; } Utils::FileName AndroidDeployStep::localLibsRulesFilePath() { return AndroidManager::localLibsRulesFilePath(target()); } void AndroidDeployStep::copyLibs(const QString &srcPath, const QString &destPath, QStringList &copiedLibs, const QStringList &filter) { QDir dir; dir.mkpath(destPath); QDirIterator libsIt(srcPath, filter, QDir::NoFilter, QDirIterator::Subdirectories); int pos = srcPath.size(); while (libsIt.hasNext()) { libsIt.next(); const QString destFile(destPath + libsIt.filePath().mid(pos)); if (libsIt.fileInfo().isDir()) { dir.mkpath(destFile); } else { QFile::copy(libsIt.filePath(), destFile); copiedLibs.append(destFile); } } } bool AndroidDeployStep::deployPackage() { QProcess *const deployProc = new QProcess; connect(deployProc, SIGNAL(readyReadStandardOutput()), this, SLOT(handleBuildOutput())); connect(deployProc, SIGNAL(readyReadStandardError()), this, SLOT(handleBuildError())); if (m_runDeployAction == DeployLocal) { writeOutput(tr("Clean old Qt libraries")); runCommand(deployProc, AndroidConfigurations::instance().adbToolPath().toString(), QStringList() << QLatin1String("-s") << m_deviceSerialNumber << QLatin1String("shell") << QLatin1String("rm") << QLatin1String("-r") << QLatin1String("/data/local/qt")); writeOutput(tr("Deploy Qt libraries. This may take some time, please wait.")); const QString tempPath = QDir::tempPath() + QLatin1String("/android_qt_libs_") + m_packageName; AndroidPackageCreationStep::removeDirectory(tempPath); QStringList stripFiles; copyLibs(m_qtVersionSourcePath + QLatin1String("/lib"), tempPath + QLatin1String("/lib"), stripFiles, QStringList() << QLatin1String("*.so")); copyLibs(m_qtVersionSourcePath + QLatin1String("/plugins"), tempPath + QLatin1String("/plugins"), stripFiles); copyLibs(m_qtVersionSourcePath + QLatin1String("/imports"), tempPath + QLatin1String("/imports"), stripFiles); copyLibs(m_qtVersionSourcePath + QLatin1String("/jar"), tempPath + QLatin1String("/jar"), stripFiles); AndroidPackageCreationStep::stripAndroidLibs(stripFiles, target()->activeRunConfiguration()->abi().architecture()); runCommand(deployProc, AndroidConfigurations::instance().adbToolPath().toString(), QStringList() << QLatin1String("-s") << m_deviceSerialNumber << QLatin1String("push") << tempPath << QLatin1String("/data/local/qt")); AndroidPackageCreationStep::removeDirectory(tempPath); emit (resetDelopyAction()); } if (m_runDeployAction == InstallQASI) { if (!runCommand(deployProc, AndroidConfigurations::instance().adbToolPath().toString(), QStringList() << QLatin1String("-s") << m_deviceSerialNumber << QLatin1String("install") << QLatin1String("-r ") << m_runQASIPackagePath)) { raiseError(tr("Qt Android smart installer installation failed")); disconnect(deployProc, 0, this, 0); deployProc->deleteLater(); return false; } emit resetDelopyAction(); } deployProc->setWorkingDirectory(m_androidDirPath.toString()); writeOutput(tr("Installing package onto %1.").arg(m_deviceSerialNumber)); runCommand(deployProc, AndroidConfigurations::instance().adbToolPath().toString(), QStringList() << QLatin1String("-s") << m_deviceSerialNumber << QLatin1String("uninstall") << m_packageName); QString package = m_apkPathDebug; if (!(m_qtVersionQMakeBuildConfig & QtSupport::BaseQtVersion::DebugBuild) && QFile::exists(m_apkPathRelease)) package = m_apkPathRelease; if (!runCommand(deployProc, AndroidConfigurations::instance().adbToolPath().toString(), QStringList() << QLatin1String("-s") << m_deviceSerialNumber << QLatin1String("install") << package)) { raiseError(tr("Package installation failed.")); disconnect(deployProc, 0, this, 0); deployProc->deleteLater(); return false; } writeOutput(tr("Pulling files necessary for debugging.")); runCommand(deployProc, AndroidConfigurations::instance().adbToolPath().toString(), QStringList() << QLatin1String("-s") << m_deviceSerialNumber << QLatin1String("pull") << QLatin1String("/system/bin/app_process") << QString::fromLatin1("%1/app_process").arg(m_buildDirectory)); runCommand(deployProc, AndroidConfigurations::instance().adbToolPath().toString(), QStringList() << QLatin1String("-s") << m_deviceSerialNumber << QLatin1String("pull") << QLatin1String("/system/lib/libc.so") << QString::fromLatin1("%1/libc.so").arg(m_buildDirectory)); disconnect(deployProc, 0, this, 0); deployProc->deleteLater(); return true; } void AndroidDeployStep::raiseError(const QString &errorString) { emit addTask(Task(Task::Error, errorString, Utils::FileName::fromString(QString()), -1, ProjectExplorer::Constants::TASK_CATEGORY_BUILDSYSTEM)); } void AndroidDeployStep::writeOutput(const QString &text, OutputFormat format) { emit addOutput(text, format); } } // namespace Internal } // namespace Qt4ProjectManager