summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/plugins/ios/Ios.pluginspec.in17
-rwxr-xr-xsrc/plugins/ios/images/QtIos.pngbin0 -> 16512 bytes
-rw-r--r--src/plugins/ios/ios.pro69
-rw-r--r--src/plugins/ios/ios.qbs73
-rw-r--r--src/plugins/ios/ios.qrc5
-rw-r--r--src/plugins/ios/ios_dependencies.pri8
-rw-r--r--src/plugins/ios/iosbuildstep.cpp433
-rw-r--r--src/plugins/ios/iosbuildstep.h134
-rw-r--r--src/plugins/ios/iosbuildstep.ui65
-rw-r--r--src/plugins/ios/iosconfigurations.cpp448
-rw-r--r--src/plugins/ios/iosconfigurations.h90
-rw-r--r--src/plugins/ios/iosconstants.h67
-rw-r--r--src/plugins/ios/iosdebugsupport.cpp136
-rw-r--r--src/plugins/ios/iosdebugsupport.h73
-rw-r--r--src/plugins/ios/iosdeployconfiguration.cpp138
-rw-r--r--src/plugins/ios/iosdeployconfiguration.h82
-rw-r--r--src/plugins/ios/iosdeploystep.cpp231
-rw-r--r--src/plugins/ios/iosdeploystep.h135
-rw-r--r--src/plugins/ios/iosdeploystepfactory.cpp111
-rw-r--r--src/plugins/ios/iosdeploystepfactory.h62
-rw-r--r--src/plugins/ios/iosdeploystepwidget.cpp70
-rw-r--r--src/plugins/ios/iosdeploystepwidget.h61
-rw-r--r--src/plugins/ios/iosdeploystepwidget.ui26
-rw-r--r--src/plugins/ios/iosdevice.cpp519
-rw-r--r--src/plugins/ios/iosdevice.h111
-rw-r--r--src/plugins/ios/iosdevicefactory.cpp81
-rw-r--r--src/plugins/ios/iosdevicefactory.h58
-rw-r--r--src/plugins/ios/iosmanager.cpp76
-rw-r--r--src/plugins/ios/iosmanager.h55
-rw-r--r--src/plugins/ios/iosplugin.cpp96
-rw-r--r--src/plugins/ios/iosplugin.h52
-rw-r--r--src/plugins/ios/iosprobe.cpp355
-rw-r--r--src/plugins/ios/iosprobe.h84
-rw-r--r--src/plugins/ios/iosqtversion.cpp131
-rw-r--r--src/plugins/ios/iosqtversion.h67
-rw-r--r--src/plugins/ios/iosqtversionfactory.cpp78
-rw-r--r--src/plugins/ios/iosqtversionfactory.h54
-rw-r--r--src/plugins/ios/iosrunconfiguration.cpp275
-rw-r--r--src/plugins/ios/iosrunconfiguration.h78
-rw-r--r--src/plugins/ios/iosrunconfiguration.ui51
-rw-r--r--src/plugins/ios/iosruncontrol.cpp112
-rw-r--r--src/plugins/ios/iosruncontrol.h69
-rw-r--r--src/plugins/ios/iosrunfactories.cpp202
-rw-r--r--src/plugins/ios/iosrunfactories.h97
-rw-r--r--src/plugins/ios/iosrunner.cpp195
-rw-r--r--src/plugins/ios/iosrunner.h95
-rw-r--r--src/plugins/ios/iossettingspage.cpp75
-rw-r--r--src/plugins/ios/iossettingspage.h59
-rw-r--r--src/plugins/ios/iossettingswidget.cpp134
-rw-r--r--src/plugins/ios/iossettingswidget.h73
-rw-r--r--src/plugins/ios/iossettingswidget.ui90
-rw-r--r--src/plugins/ios/iossimulator.cpp142
-rw-r--r--src/plugins/ios/iossimulator.h80
-rw-r--r--src/plugins/ios/iossimulatorfactory.cpp81
-rw-r--r--src/plugins/ios/iossimulatorfactory.h55
-rw-r--r--src/plugins/ios/iostoolhandler.cpp977
-rw-r--r--src/plugins/ios/iostoolhandler.h107
-rw-r--r--src/plugins/plugins.pro4
-rw-r--r--src/plugins/plugins.qbs1
-rw-r--r--src/plugins/projectexplorer/gcctoolchain.h3
-rw-r--r--src/plugins/qtsupport/qtsupportconstants.h2
-rw-r--r--src/tools/3rdparty/3rdparty.pro5
-rw-r--r--src/tools/3rdparty/iossim/IOSSIM_LICENSE31
-rw-r--r--src/tools/3rdparty/iossim/Info.plist16
-rw-r--r--src/tools/3rdparty/iossim/iossim.pro53
-rw-r--r--src/tools/3rdparty/iossim/iossim.qbs27
-rw-r--r--src/tools/3rdparty/iossim/iphonesimulator.h40
-rw-r--r--src/tools/3rdparty/iossim/iphonesimulator.m544
-rw-r--r--src/tools/3rdparty/iossim/iphonesimulatorremoteclient/iphonesimulatorremoteclient.h126
-rw-r--r--src/tools/3rdparty/iossim/main.mm64
-rw-r--r--src/tools/3rdparty/iossim/nsprintf.h6
-rw-r--r--src/tools/3rdparty/iossim/nsprintf.m44
-rw-r--r--src/tools/3rdparty/iossim/nsstringexpandpath.h11
-rw-r--r--src/tools/3rdparty/iossim/nsstringexpandpath.m18
-rw-r--r--src/tools/3rdparty/iossim/version.h1
-rw-r--r--src/tools/iostool/Info.plist16
-rw-r--r--src/tools/iostool/iosdevicemanager.cpp1584
-rw-r--r--src/tools/iostool/iosdevicemanager.h93
-rw-r--r--src/tools/iostool/iostool.pro27
-rw-r--r--src/tools/iostool/iostool.qbs21
-rw-r--r--src/tools/iostool/main.cpp386
-rw-r--r--src/tools/tools.pro7
-rw-r--r--src/tools/tools.qbs4
83 files changed, 10528 insertions, 4 deletions
diff --git a/src/plugins/ios/Ios.pluginspec.in b/src/plugins/ios/Ios.pluginspec.in
new file mode 100644
index 0000000000..dc4c0656d2
--- /dev/null
+++ b/src/plugins/ios/Ios.pluginspec.in
@@ -0,0 +1,17 @@
+<plugin name=\"Ios\" version=\"$$QTCREATOR_VERSION\" compatVersion=\"$$QTCREATOR_VERSION\" experimental=\"true\">
+ <vendor>Digia Plc</vendor>
+ <copyright>(C) 2013 Digia Plc</copyright>
+ <license>
+Commercial Usage
+
+Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt Commercial License Agreement provided with the Software or, alternatively, in accordance with the terms contained in a written agreement between you and Digia.
+
+GNU Lesser General Public License Usage
+
+Alternatively, this plugin may be used under the terms of the GNU Lesser General Public License version 2.1 as published by the Free Software Foundation. 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.
+ </license>
+ <description>Support for deployment to and execution on iOS Devices</description>
+ <category>Device Support</category>
+ <url>http://www.qt-project.org</url>
+ $$dependencyList
+</plugin>
diff --git a/src/plugins/ios/images/QtIos.png b/src/plugins/ios/images/QtIos.png
new file mode 100755
index 0000000000..b68467437a
--- /dev/null
+++ b/src/plugins/ios/images/QtIos.png
Binary files differ
diff --git a/src/plugins/ios/ios.pro b/src/plugins/ios/ios.pro
new file mode 100644
index 0000000000..476df08e52
--- /dev/null
+++ b/src/plugins/ios/ios.pro
@@ -0,0 +1,69 @@
+TEMPLATE = lib
+TARGET = Ios
+
+include(../../qtcreatorplugin.pri)
+
+QT += xml network
+
+HEADERS += \
+ iosconstants.h \
+ iosconfigurations.h \
+ iosmanager.h \
+ iosrunconfiguration.h \
+ iosruncontrol.h \
+ iosrunfactories.h \
+ iossettingspage.h \
+ iossettingswidget.h \
+ iosrunner.h \
+ iosdebugsupport.h \
+ iosqtversionfactory.h \
+ iosqtversion.h \
+ iosplugin.h \
+ iosdevicefactory.h \
+ iosdevice.h \
+ iossimulator.h \
+ iossimulatorfactory.h \
+ iosprobe.h \
+ iosbuildstep.h \
+ iostoolhandler.h \
+ iosdeployconfiguration.h \
+ iosdeploystep.h \
+ iosdeploystepfactory.h \
+ iosdeploystepwidget.h
+
+
+SOURCES += \
+ iosconfigurations.cpp \
+ iosmanager.cpp \
+ iosrunconfiguration.cpp \
+ iosruncontrol.cpp \
+ iosrunfactories.cpp \
+ iossettingspage.cpp \
+ iossettingswidget.cpp \
+ iosrunner.cpp \
+ iosdebugsupport.cpp \
+ iosqtversionfactory.cpp \
+ iosqtversion.cpp \
+ iosplugin.cpp \
+ iosdevicefactory.cpp \
+ iosdevice.cpp \
+ iossimulator.cpp \
+ iossimulatorfactory.cpp \
+ iosprobe.cpp \
+ iosbuildstep.cpp \
+ iostoolhandler.cpp \
+ iosdeployconfiguration.cpp \
+ iosdeploystep.cpp \
+ iosdeploystepfactory.cpp \
+ iosdeploystepwidget.cpp
+
+FORMS += \
+ iossettingswidget.ui \
+ iosbuildstep.ui \
+ iosrunconfiguration.ui \
+ iosdeploystepwidget.ui
+
+DEFINES += IOS_LIBRARY
+
+RESOURCES += ios.qrc
+
diff --git a/src/plugins/ios/ios.qbs b/src/plugins/ios/ios.qbs
new file mode 100644
index 0000000000..5c1ad996c2
--- /dev/null
+++ b/src/plugins/ios/ios.qbs
@@ -0,0 +1,73 @@
+import qbs.base 1.0
+
+import "../QtcPlugin.qbs" as QtcPlugin
+
+QtcPlugin {
+ name: "Ios"
+ condition: qbs.targetOS.contains("osx")
+
+ Depends { name: "Core" }
+ Depends { name: "ProjectExplorer" }
+ Depends { name: "Qt4ProjectManager" }
+ Depends { name: "Debugger" }
+ Depends { name: "QtSupport" }
+ Depends { name: "Qt"; submodules: ["widgets", "xml", "network"] }
+
+ cpp.includePaths: base.concat("../../shared")
+ cpp.frameworks: base.concat(["CoreFoundation", "IOKit"])
+
+ files: [
+ "ios.qrc",
+ "iosbuildstep.cpp",
+ "iosbuildstep.h",
+ "iosbuildstep.ui",
+ "iosconfigurations.cpp",
+ "iosconfigurations.h",
+ "iosconstants.h",
+ "iosdebugsupport.cpp",
+ "iosdebugsupport.h",
+ "iosdeployconfiguration.cpp",
+ "iosdeployconfiguration.h",
+ "iosdeploystep.cpp",
+ "iosdeploystep.h",
+ "iosdeploystepfactory.cpp",
+ "iosdeploystepfactory.h",
+ "iosdeploystepwidget.cpp",
+ "iosdeploystepwidget.h",
+ "iosdeploystepwidget.ui",
+ "iosdevice.cpp",
+ "iosdevice.h",
+ "iosdevicefactory.cpp",
+ "iosdevicefactory.h",
+ "iosmanager.cpp",
+ "iosmanager.h",
+ "iosplugin.cpp",
+ "iosplugin.h",
+ "iosprobe.cpp",
+ "iosprobe.h",
+ "iosqtversion.cpp",
+ "iosqtversion.h",
+ "iosqtversionfactory.cpp",
+ "iosqtversionfactory.h",
+ "iosrunconfiguration.cpp",
+ "iosrunconfiguration.h",
+ "iosrunconfiguration.ui",
+ "iosruncontrol.cpp",
+ "iosruncontrol.h",
+ "iosrunfactories.cpp",
+ "iosrunfactories.h",
+ "iosrunner.cpp",
+ "iosrunner.h",
+ "iossettingspage.cpp",
+ "iossettingspage.h",
+ "iossettingswidget.cpp",
+ "iossettingswidget.h",
+ "iossettingswidget.ui",
+ "iossimulator.cpp",
+ "iossimulator.h",
+ "iossimulatorfactory.cpp",
+ "iossimulatorfactory.h",
+ "iostoolhandler.cpp",
+ "iostoolhandler.h"
+ ]
+}
diff --git a/src/plugins/ios/ios.qrc b/src/plugins/ios/ios.qrc
new file mode 100644
index 0000000000..81314b84cd
--- /dev/null
+++ b/src/plugins/ios/ios.qrc
@@ -0,0 +1,5 @@
+<RCC>
+ <qresource prefix="/ios">
+ <file>images/QtIos.png</file>
+ </qresource>
+</RCC>
diff --git a/src/plugins/ios/ios_dependencies.pri b/src/plugins/ios/ios_dependencies.pri
new file mode 100644
index 0000000000..020498834b
--- /dev/null
+++ b/src/plugins/ios/ios_dependencies.pri
@@ -0,0 +1,8 @@
+QTC_PLUGIN_NAME = Ios
+QTC_LIB_DEPENDS += \
+ utils
+QTC_PLUGIN_DEPENDS += \
+ coreplugin \
+ debugger \
+ projectexplorer \
+ qt4projectmanager
diff --git a/src/plugins/ios/iosbuildstep.cpp b/src/plugins/ios/iosbuildstep.cpp
new file mode 100644
index 0000000000..df63f0ef8c
--- /dev/null
+++ b/src/plugins/ios/iosbuildstep.cpp
@@ -0,0 +1,433 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** 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 "iosbuildstep.h"
+#include "iosconstants.h"
+#include "ui_iosbuildstep.h"
+#include "iosmanager.h"
+
+#include <extensionsystem/pluginmanager.h>
+#include <projectexplorer/target.h>
+#include <projectexplorer/project.h>
+#include <projectexplorer/buildsteplist.h>
+#include <projectexplorer/gnumakeparser.h>
+#include <projectexplorer/kitinformation.h>
+#include <projectexplorer/projectexplorer.h>
+#include <projectexplorer/buildconfiguration.h>
+#include <projectexplorer/projectexplorerconstants.h>
+#include <projectexplorer/toolchain.h>
+#include <projectexplorer/gcctoolchain.h>
+#include <qt4projectmanager/qt4buildconfiguration.h>
+#include <qt4projectmanager/qt4projectmanagerconstants.h>
+#include <qt4projectmanager/qt4projectmanager.h>
+#include <qt4projectmanager/qt4project.h>
+#include <qt4projectmanager/qt4nodes.h>
+#include <qtsupport/qtkitinformation.h>
+#include <qtsupport/qtparser.h>
+#include <coreplugin/variablemanager.h>
+#include <utils/stringutils.h>
+#include <utils/qtcassert.h>
+#include <utils/qtcprocess.h>
+
+using namespace Core;
+using namespace ProjectExplorer;
+using namespace Qt4ProjectManager;
+
+namespace Ios {
+namespace Internal {
+
+const char IOS_BUILD_STEP_ID[] = "Ios.IosBuildStep";
+const char IOS_BUILD_STEP_DISPLAY_NAME[] = QT_TRANSLATE_NOOP("Ios::Internal::IosBuildStep",
+ "xcodebuild");
+
+const char BUILD_USE_DEFAULT_ARGS_KEY[] = "Ios.IosBuildStep.XcodeArgumentsUseDefault";
+const char BUILD_ARGUMENTS_KEY[] = "Ios.IosBuildStep.XcodeArguments";
+const char CLEAN_KEY[] = "Ios.IosBuildStep.Clean";
+
+IosBuildStep::IosBuildStep(BuildStepList *parent) :
+ AbstractProcessStep(parent, Id(IOS_BUILD_STEP_ID)),
+ m_useDefaultArguments(true),
+ m_clean(false)
+{
+ ctor();
+}
+
+IosBuildStep::IosBuildStep(BuildStepList *parent, const Id id) :
+ AbstractProcessStep(parent, id),
+ m_useDefaultArguments(true),
+ m_clean(false)
+{
+ ctor();
+}
+
+IosBuildStep::IosBuildStep(BuildStepList *parent, IosBuildStep *bs) :
+ AbstractProcessStep(parent, bs),
+ m_baseBuildArguments(bs->m_baseBuildArguments),
+ m_useDefaultArguments(bs->m_useDefaultArguments),
+ m_clean(bs->m_clean)
+{
+ ctor();
+}
+
+void IosBuildStep::ctor()
+{
+ setDefaultDisplayName(QCoreApplication::translate("GenericProjectManager::Internal::IosBuildStep",
+ IOS_BUILD_STEP_DISPLAY_NAME));
+}
+
+IosBuildStep::~IosBuildStep()
+{
+}
+
+bool IosBuildStep::init()
+{
+ BuildConfiguration *bc = buildConfiguration();
+ if (!bc)
+ bc = target()->activeBuildConfiguration();
+
+ m_tasks.clear();
+ ToolChain *tc = ToolChainKitInformation::toolChain(target()->kit());
+ if (!tc) {
+ Task t = Task(Task::Error, tr("Qt Creator needs a compiler set up to build. Configure a compiler in the kit preferences."),
+ Utils::FileName(), -1,
+ Core::Id(ProjectExplorer::Constants::TASK_CATEGORY_BUILDSYSTEM));
+ m_tasks.append(t);
+ emit addTask(t);
+ return false;
+ }
+ ProcessParameters *pp = processParameters();
+ pp->setMacroExpander(bc->macroExpander());
+ pp->setWorkingDirectory(bc->buildDirectory().toString());
+ Utils::Environment env = bc->environment();
+ // Force output to english for the parsers. Do this here and not in the toolchain's
+ // addToEnvironment() to not screw up the users run environment.
+ env.set(QLatin1String("LC_ALL"), QLatin1String("C"));
+ pp->setEnvironment(env);
+ pp->setCommand(buildCommand());
+ pp->setArguments(Utils::QtcProcess::joinArgs(allArguments()));
+ pp->resolveAll();
+
+ // If we are cleaning, then build can fail with an error code, but that doesn't mean
+ // we should stop the clean queue
+ // That is mostly so that rebuild works on an already clean project
+ setIgnoreReturnValue(m_clean);
+
+ setOutputParser(new GnuMakeParser());
+ IOutputParser *parser = target()->kit()->createOutputParser();
+ if (parser)
+ appendOutputParser(parser);
+ outputParser()->setWorkingDirectory(pp->effectiveWorkingDirectory());
+
+ return AbstractProcessStep::init();
+}
+
+void IosBuildStep::setClean(bool clean)
+{
+ m_clean = clean;
+}
+
+bool IosBuildStep::isClean() const
+{
+ return m_clean;
+}
+
+QVariantMap IosBuildStep::toMap() const
+{
+ QVariantMap map(AbstractProcessStep::toMap());
+
+ map.insert(QLatin1String(BUILD_ARGUMENTS_KEY), m_baseBuildArguments);
+ map.insert(QLatin1String(BUILD_USE_DEFAULT_ARGS_KEY), m_useDefaultArguments);
+ map.insert(QLatin1String(CLEAN_KEY), m_clean);
+ return map;
+}
+
+bool IosBuildStep::fromMap(const QVariantMap &map)
+{
+ QVariant bArgs = map.value(QLatin1String(BUILD_ARGUMENTS_KEY));
+ m_baseBuildArguments = bArgs.toStringList();
+ m_useDefaultArguments = map.value(QLatin1String(BUILD_USE_DEFAULT_ARGS_KEY)).toBool();
+ m_clean = map.value(QLatin1String(CLEAN_KEY)).toBool();
+
+ return BuildStep::fromMap(map);
+}
+
+QStringList IosBuildStep::allArguments() const
+{
+ return baseArguments() + m_extraArguments;
+}
+
+QStringList IosBuildStep::defaultArguments() const
+{
+ QStringList res;
+ Kit *kit = target()->kit();
+ ToolChain *tc = ToolChainKitInformation::toolChain(kit);
+ switch (target()->activeBuildConfiguration()->buildType()) {
+ case BuildConfiguration::Debug :
+ res << QLatin1String("-configuration") << QLatin1String("Debug");
+ break;
+ case BuildConfiguration::Release :
+ res << QLatin1String("-configuration") << QLatin1String("Release");
+ break;
+ case BuildConfiguration::Unknown :
+ break;
+ default:
+ qDebug() << "IosBuildStep had an unknown buildType "
+ << target()->activeBuildConfiguration()->buildType();
+ }
+ if (tc->type() == QLatin1String("gcc") || tc->type() == QLatin1String("clang")) {
+ GccToolChain *gtc = static_cast<GccToolChain *>(tc);
+ res << gtc->platformCodeGenFlags();
+ }
+ if (!SysRootKitInformation::sysRoot(kit).isEmpty())
+ res << QLatin1String("-sdk") << SysRootKitInformation::sysRoot(kit).toString();
+ res << QLatin1String("SYMROOT=") + IosManager::resDirForTarget(target());
+ return res;
+}
+
+QString IosBuildStep::buildCommand() const
+{
+ return QLatin1String("xcodebuild"); // add path?
+}
+
+void IosBuildStep::run(QFutureInterface<bool> &fi)
+{
+ bool canContinue = true;
+ foreach (const Task &t, m_tasks) {
+ addTask(t);
+ canContinue = false;
+ }
+ if (!canContinue) {
+ emit addOutput(tr("Configuration is faulty. Check the Issues output pane for details."),
+ BuildStep::MessageOutput);
+ fi.reportResult(false);
+ emit finished();
+ return;
+ }
+
+ AbstractProcessStep::run(fi);
+}
+
+BuildStepConfigWidget *IosBuildStep::createConfigWidget()
+{
+ return new IosBuildStepConfigWidget(this);
+}
+
+bool IosBuildStep::immutable() const
+{
+ return false;
+}
+
+void IosBuildStep::setBaseArguments(const QStringList &args)
+{
+ m_baseBuildArguments = args;
+ m_useDefaultArguments = (args == defaultArguments());
+}
+
+void IosBuildStep::setExtraArguments(const QStringList &extraArgs)
+{
+ m_extraArguments = extraArgs;
+}
+
+QStringList IosBuildStep::baseArguments() const
+{
+ if (m_useDefaultArguments)
+ return defaultArguments();
+ return m_baseBuildArguments;
+}
+
+//
+// IosBuildStepConfigWidget
+//
+
+IosBuildStepConfigWidget::IosBuildStepConfigWidget(IosBuildStep *buildStep)
+ : m_buildStep(buildStep)
+{
+ m_ui = new Ui::IosBuildStep;
+ m_ui->setupUi(this);
+
+ Project *pro = m_buildStep->target()->project();
+
+ m_ui->buildArgumentsTextEdit->setPlainText(Utils::QtcProcess::joinArgs(
+ m_buildStep->baseArguments()));
+ m_ui->extraArgumentsLineEdit->setText(Utils::QtcProcess::joinArgs(
+ m_buildStep->m_extraArguments));
+ m_ui->resetDefaultsButton->setEnabled(!m_buildStep->m_useDefaultArguments);
+ updateDetails();
+
+ connect(m_ui->buildArgumentsTextEdit, SIGNAL(textChanged()),
+ this, SLOT(buildArgumentsChanged()));
+ connect(m_ui->resetDefaultsButton, SIGNAL(clicked()),
+ this, SLOT(resetDefaultArguments()));
+ connect(m_ui->extraArgumentsLineEdit, SIGNAL(editingFinished()),
+ this, SLOT(extraArgumentsChanged()));
+
+ connect(ProjectExplorerPlugin::instance(), SIGNAL(settingsChanged()),
+ this, SLOT(updateDetails()));
+ connect(m_buildStep->target(), SIGNAL(kitChanged()),
+ this, SLOT(updateDetails()));
+ connect(pro, SIGNAL(environmentChanged()),
+ this, SLOT(updateDetails()));
+}
+
+IosBuildStepConfigWidget::~IosBuildStepConfigWidget()
+{
+ delete m_ui;
+}
+
+QString IosBuildStepConfigWidget::displayName() const
+{
+ return tr("iOS build", "iOS BuildStep display name.");
+}
+
+void IosBuildStepConfigWidget::updateDetails()
+{
+ BuildConfiguration *bc = m_buildStep->buildConfiguration();
+ if (!bc)
+ bc = m_buildStep->target()->activeBuildConfiguration();
+
+ ProcessParameters param;
+ param.setMacroExpander(bc->macroExpander());
+ param.setWorkingDirectory(bc->buildDirectory().toString());
+ param.setEnvironment(bc->environment());
+ param.setCommand(m_buildStep->buildCommand());
+ param.setArguments(Utils::QtcProcess::joinArgs(m_buildStep->allArguments()));
+ m_summaryText = param.summary(displayName());
+ emit updateSummary();
+}
+
+QString IosBuildStepConfigWidget::summaryText() const
+{
+ return m_summaryText;
+}
+
+void IosBuildStepConfigWidget::buildArgumentsChanged()
+{
+ m_buildStep->setBaseArguments(Utils::QtcProcess::splitArgs(
+ m_ui->buildArgumentsTextEdit->toPlainText()));
+ m_ui->resetDefaultsButton->setEnabled(!m_buildStep->m_useDefaultArguments);
+ updateDetails();
+}
+
+void IosBuildStepConfigWidget::resetDefaultArguments()
+{
+ m_buildStep->setBaseArguments(m_buildStep->defaultArguments());
+ m_ui->buildArgumentsTextEdit->setPlainText(Utils::QtcProcess::joinArgs(
+ m_buildStep->baseArguments()));
+ m_ui->resetDefaultsButton->setEnabled(!m_buildStep->m_useDefaultArguments);
+}
+
+void IosBuildStepConfigWidget::extraArgumentsChanged()
+{
+ m_buildStep->setExtraArguments(Utils::QtcProcess::splitArgs(
+ m_ui->extraArgumentsLineEdit->text()));
+}
+//
+// IosBuildStepFactory
+//
+
+IosBuildStepFactory::IosBuildStepFactory(QObject *parent) :
+ IBuildStepFactory(parent)
+{
+}
+
+bool IosBuildStepFactory::canCreate(BuildStepList *parent, const Id) const
+{
+ if (parent->id() != ProjectExplorer::Constants::BUILDSTEPS_CLEAN
+ && parent->id() != ProjectExplorer::Constants::BUILDSTEPS_BUILD)
+ return false;
+ Kit *kit = parent->target()->kit();
+ Core::Id deviceType = DeviceTypeKitInformation::deviceTypeId(kit);
+ return (deviceType == Constants::IOS_DEVICE_TYPE
+ || deviceType == Constants::IOS_SIMULATOR_TYPE);
+}
+
+BuildStep *IosBuildStepFactory::create(BuildStepList *parent, const Id id)
+{
+ if (!canCreate(parent, id))
+ return 0;
+ IosBuildStep *step = new IosBuildStep(parent);
+ if (parent->id() == ProjectExplorer::Constants::BUILDSTEPS_CLEAN) {
+ step->setClean(true);
+ step->setExtraArguments(QStringList(QLatin1String("clean")));
+ } else if (parent->id() == ProjectExplorer::Constants::BUILDSTEPS_BUILD) {
+ // nomal setup
+ }
+ return step;
+}
+
+bool IosBuildStepFactory::canClone(BuildStepList *parent, BuildStep *source) const
+{
+ return canCreate(parent, source->id());
+}
+
+BuildStep *IosBuildStepFactory::clone(BuildStepList *parent, BuildStep *source)
+{
+ if (!canClone(parent, source))
+ return 0;
+ IosBuildStep *old(qobject_cast<IosBuildStep *>(source));
+ Q_ASSERT(old);
+ return new IosBuildStep(parent, old);
+}
+
+bool IosBuildStepFactory::canRestore(BuildStepList *parent, const QVariantMap &map) const
+{
+ return canCreate(parent, idFromMap(map));
+}
+
+BuildStep *IosBuildStepFactory::restore(BuildStepList *parent, const QVariantMap &map)
+{
+ if (!canRestore(parent, map))
+ return 0;
+ IosBuildStep *bs(new IosBuildStep(parent));
+ if (bs->fromMap(map))
+ return bs;
+ delete bs;
+ return 0;
+}
+
+QList<Id> IosBuildStepFactory::availableCreationIds(BuildStepList *parent) const
+{
+ Kit *kit = parent->target()->kit();
+ Core::Id deviceType = DeviceTypeKitInformation::deviceTypeId(kit);
+ if (deviceType == Constants::IOS_DEVICE_TYPE
+ || deviceType == Constants::IOS_SIMULATOR_TYPE)
+ return QList<Id>() << Id(IOS_BUILD_STEP_ID);
+ return QList<Id>();
+}
+
+QString IosBuildStepFactory::displayNameForId(const Id id) const
+{
+ if (id == IOS_BUILD_STEP_ID)
+ return QCoreApplication::translate("GenericProjectManager::Internal::IosBuildStep",
+ IOS_BUILD_STEP_DISPLAY_NAME);
+ return QString();
+}
+
+} // namespace Internal
+} // namespace Ios
diff --git a/src/plugins/ios/iosbuildstep.h b/src/plugins/ios/iosbuildstep.h
new file mode 100644
index 0000000000..43d99ce8a4
--- /dev/null
+++ b/src/plugins/ios/iosbuildstep.h
@@ -0,0 +1,134 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** 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.
+**
+****************************************************************************/
+#ifndef IOSBUILDSTEP_H
+#define IOSBUILDSTEP_H
+
+#include <projectexplorer/abstractprocessstep.h>
+
+QT_BEGIN_NAMESPACE
+class QListWidgetItem;
+QT_END_NAMESPACE
+
+namespace Ios {
+namespace Internal {
+
+class IosBuildStepConfigWidget;
+class IosBuildStepFactory;
+namespace Ui { class IosBuildStep; }
+
+class IosBuildStep : public ProjectExplorer::AbstractProcessStep
+{
+ Q_OBJECT
+
+ friend class IosBuildStepConfigWidget;
+ friend class IosBuildStepFactory;
+
+public:
+ IosBuildStep(ProjectExplorer::BuildStepList *parent);
+ ~IosBuildStep();
+
+ bool init();
+ void run(QFutureInterface<bool> &fi);
+
+ ProjectExplorer::BuildStepConfigWidget *createConfigWidget();
+ bool immutable() const;
+ void setBaseArguments(const QStringList &args);
+ void setExtraArguments(const QStringList &extraArgs);
+ QStringList baseArguments() const;
+ QStringList allArguments() const;
+ QStringList defaultArguments() const;
+ QString buildCommand() const;
+
+ void setClean(bool clean);
+ bool isClean() const;
+
+ QVariantMap toMap() const;
+protected:
+ IosBuildStep(ProjectExplorer::BuildStepList *parent, IosBuildStep *bs);
+ IosBuildStep(ProjectExplorer::BuildStepList *parent, const Core::Id id);
+ bool fromMap(const QVariantMap &map);
+
+private:
+ void ctor();
+
+ QStringList m_baseBuildArguments;
+ QStringList m_extraArguments;
+ QString m_buildCommand;
+ bool m_useDefaultArguments;
+ bool m_clean;
+ QList<ProjectExplorer::Task> m_tasks;
+};
+
+class IosBuildStepConfigWidget : public ProjectExplorer::BuildStepConfigWidget
+{
+ Q_OBJECT
+
+public:
+ IosBuildStepConfigWidget(IosBuildStep *buildStep);
+ ~IosBuildStepConfigWidget();
+ QString displayName() const;
+ QString summaryText() const;
+
+private slots:
+ void buildArgumentsChanged();
+ void resetDefaultArguments();
+ void extraArgumentsChanged();
+ void updateDetails();
+
+private:
+ Ui::IosBuildStep *m_ui;
+ IosBuildStep *m_buildStep;
+ QString m_summaryText;
+};
+
+class IosBuildStepFactory : public ProjectExplorer::IBuildStepFactory
+{
+ Q_OBJECT
+
+public:
+ explicit IosBuildStepFactory(QObject *parent = 0);
+
+ bool canCreate(ProjectExplorer::BuildStepList *parent, const Core::Id id) const;
+ ProjectExplorer::BuildStep *create(ProjectExplorer::BuildStepList *parent, const Core::Id id);
+ bool canClone(ProjectExplorer::BuildStepList *parent,
+ ProjectExplorer::BuildStep *source) const;
+ ProjectExplorer::BuildStep *clone(ProjectExplorer::BuildStepList *parent,
+ ProjectExplorer::BuildStep *source);
+ bool canRestore(ProjectExplorer::BuildStepList *parent, const QVariantMap &map) const;
+ ProjectExplorer::BuildStep *restore(ProjectExplorer::BuildStepList *parent,
+ const QVariantMap &map);
+
+ QList<Core::Id> availableCreationIds(ProjectExplorer::BuildStepList *bc) const;
+ QString displayNameForId(const Core::Id id) const;
+};
+
+} // namespace Internal
+} // namespace Ios
+
+#endif // IOSBUILDSTEP_H
diff --git a/src/plugins/ios/iosbuildstep.ui b/src/plugins/ios/iosbuildstep.ui
new file mode 100644
index 0000000000..70f1599101
--- /dev/null
+++ b/src/plugins/ios/iosbuildstep.ui
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>Ios::Internal::IosBuildStep</class>
+ <widget class="QWidget" name="Ios::Internal::IosBuildStep">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>756</width>
+ <height>183</height>
+ </rect>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>0</height>
+ </size>
+ </property>
+ <layout class="QGridLayout" name="gridLayout">
+ <item row="0" column="0">
+ <widget class="QLabel" name="buildArgumentsLabel">
+ <property name="text">
+ <string>Base arguments:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1" rowspan="2">
+ <widget class="QPlainTextEdit" name="buildArgumentsTextEdit"/>
+ </item>
+ <item row="1" column="2">
+ <widget class="QPushButton" name="resetDefaultsButton">
+ <property name="layoutDirection">
+ <enum>Qt::RightToLeft</enum>
+ </property>
+ <property name="text">
+ <string>Reset defaults</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QLabel" name="extraArgumentsLabel">
+ <property name="text">
+ <string>Extra arguments:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <widget class="QLineEdit" name="extraArgumentsLineEdit"/>
+ </item>
+ </layout>
+ <zorder>buildArgumentsTextEdit</zorder>
+ <zorder>resetDefaultsButton</zorder>
+ <zorder>extraArgumentsLabel</zorder>
+ <zorder>extraArgumentsLineEdit</zorder>
+ <zorder>buildArgumentsLabel</zorder>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/src/plugins/ios/iosconfigurations.cpp b/src/plugins/ios/iosconfigurations.cpp
new file mode 100644
index 0000000000..19f0a4d8a8
--- /dev/null
+++ b/src/plugins/ios/iosconfigurations.cpp
@@ -0,0 +1,448 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** 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 "iosconfigurations.h"
+#include "iosconstants.h"
+#include "iosdevice.h"
+#include "iossimulator.h"
+#include "iosprobe.h"
+
+#include <coreplugin/icore.h>
+#include <utils/hostosinfo.h>
+#include <utils/persistentsettings.h>
+#include <utils/qtcassert.h>
+#include <projectexplorer/kitmanager.h>
+#include <projectexplorer/kitinformation.h>
+#include <projectexplorer/devicesupport/devicemanager.h>
+#include <projectexplorer/toolchainmanager.h>
+#include <projectexplorer/toolchain.h>
+#include <projectexplorer/gcctoolchain.h>
+#include <projectexplorer/projectexplorerconstants.h>
+#include <qt4projectmanager/qmakekitinformation.h>
+#include <debugger/debuggerkitinformation.h>
+#include <qtsupport/baseqtversion.h>
+#include <qtsupport/qtkitinformation.h>
+#include <qtsupport/qtversionmanager.h>
+#include <qtsupport/qtversionfactory.h>
+
+#include <QDateTime>
+#include <QSettings>
+#include <QStringList>
+#include <QProcess>
+#include <QFileInfo>
+#include <QDirIterator>
+#include <QMetaObject>
+#include <QList>
+
+#include <QStringListModel>
+#include <QMessageBox>
+
+#include <unistd.h>
+
+using namespace ProjectExplorer;
+using namespace Utils;
+
+static const bool debugProbe = true;
+
+namespace Ios {
+namespace Internal {
+
+namespace {
+ const QLatin1String SettingsGroup("IosConfigurations");
+ const QLatin1String developerPathKey("DeveloperPath");
+ const QLatin1String ignoreAllDevicesKey("IgnoreAllDevices");
+
+}
+
+IosConfig::IosConfig(const QSettings &settings)
+{
+ developerPath = FileName::fromString(settings.value(developerPathKey).toString());
+ ignoreAllDevices = settings.value(ignoreAllDevicesKey, false).toBool();
+}
+
+IosConfig::IosConfig() : ignoreAllDevices(false)
+{ }
+
+void IosConfig::save(QSettings &settings) const
+{
+ settings.setValue(developerPathKey, developerPath.toString());
+ settings.setValue(ignoreAllDevicesKey, ignoreAllDevices);
+}
+
+void IosConfigurations::setConfig(const IosConfig &devConfigs)
+{
+ m_config = devConfigs;
+ save();
+ updateAutomaticKitList();
+ emit updated();
+}
+
+FileName IosConfigurations::iosToolPath() const
+{
+ FileName res = FileName::fromString(QCoreApplication::applicationDirPath()
+ + QLatin1String("/iosTool"));
+ return res;
+}
+
+
+bool equalKits(Kit *a, Kit *b)
+{
+ return ToolChainKitInformation::toolChain(a) == ToolChainKitInformation::toolChain(b)
+ && QtSupport::QtKitInformation::qtVersion(a) == QtSupport::QtKitInformation::qtVersion(b);
+}
+
+void IosConfigurations::updateAutomaticKitList()
+{
+ QMap<QString, Platform> platforms = IosProbe::detectPlatforms(m_config.developerPath.toString());
+ QMap<QString, ProjectExplorer::GccToolChain *> platformToolchainMap;
+ // check existing toolchains (and remove old ones)
+ foreach (ProjectExplorer::ToolChain *tc, ProjectExplorer::ToolChainManager::toolChains()) {
+ if (!tc->isAutoDetected()) // use also user toolchains?
+ continue;
+ if (tc->type() != QLatin1String("clang") && tc->type() != QLatin1String("gcc"))
+ continue;
+ ProjectExplorer::GccToolChain *toolchain = static_cast<ProjectExplorer::GccToolChain *>(tc);
+ QMapIterator<QString, Platform> iter(platforms);
+ bool found = false;
+ while (iter.hasNext()) {
+ iter.next();
+ const Platform &p = iter.value();
+ if (p.compilerPath == toolchain->compilerCommand()
+ && p.backendFlags == toolchain->platformCodeGenFlags()) {
+ platformToolchainMap[p.name] = toolchain;
+ found = true;
+ }
+ }
+ iter.toFront();
+ while (iter.hasNext()) {
+ iter.next();
+ const Platform &p = iter.value();
+ if (p.platformKind)
+ continue;
+ if (p.compilerPath == toolchain->compilerCommand()
+ && p.backendFlags == toolchain->platformCodeGenFlags()) {
+ platformToolchainMap[p.name] = toolchain;
+ found = true;
+ }
+ }
+ if (!found && (tc->displayName().startsWith(QLatin1String("iphone"))
+ || tc->displayName().startsWith(QLatin1String("mac")))) {
+ qDebug() << "removing toolchain" << tc->displayName();
+ ProjectExplorer::ToolChainManager::deregisterToolChain(tc);
+ }
+ }
+ // add missing toolchains
+ {
+ QMapIterator<QString, Platform> iter(platforms);
+ while (iter.hasNext()) {
+ iter.next();
+ const Platform &p = iter.value();
+ if (platformToolchainMap.contains(p.name)
+ || (p.platformKind & Platform::BasePlatform) == 0
+ || (p.name.startsWith(QLatin1String("iphone"))
+ && (p.platformKind & Platform::Cxx11Support) != 0))
+ continue;
+ ProjectExplorer::GccToolChain *toolchain;
+ if (p.compilerPath.toFileInfo().baseName().startsWith(QLatin1String("clang")))
+ toolchain = new ProjectExplorer::ClangToolChain(
+ ProjectExplorer::ToolChain::AutoDetection);
+ else
+ toolchain = new ProjectExplorer::GccToolChain(
+ QLatin1String(ProjectExplorer::Constants::GCC_TOOLCHAIN_ID),
+ ProjectExplorer::ToolChain::AutoDetection);
+ QString baseDisplayName = p.name;
+ QString displayName = baseDisplayName;
+ for (int iVers = 1; iVers < 100; ++iVers) {
+ bool unique = true;
+ foreach (ProjectExplorer::ToolChain *existingTC, ProjectExplorer::ToolChainManager::toolChains()) {
+ if (existingTC->displayName() == displayName) {
+ unique = false;
+ break;
+ }
+ }
+ if (unique) break;
+ displayName = baseDisplayName + QLatin1String("-") + QString::number(iVers);
+ }
+ toolchain->setDisplayName(displayName);
+ toolchain->setPlatformCodeGenFlags(p.backendFlags);
+ toolchain->setPlatformLinkerFlags(p.backendFlags);
+ toolchain->setCompilerCommand(p.compilerPath);
+ ProjectExplorer::ToolChainManager::registerToolChain(toolchain);
+ platformToolchainMap.insert(p.name, toolchain);
+ QMapIterator<QString, Platform> iter2(iter);
+ while (iter2.hasNext()) {
+ iter2.next();
+ const Platform &p2 = iter2.value();
+ if (!platformToolchainMap.contains(p2.name)
+ && p2.compilerPath == toolchain->compilerCommand()
+ && p2.backendFlags == toolchain->platformCodeGenFlags()) {
+ platformToolchainMap[p2.name] = toolchain;
+ }
+ }
+ }
+ }
+ // filter out all non iphone, non base, non clang or cxx11 platforms, as we don't set up kits for those
+ {
+ QStringList toRemove;
+ QMapIterator<QString, Platform> iter(platforms);
+ while (iter.hasNext()) {
+ iter.next();
+ const Platform &p = iter.value();
+ if (!p.name.startsWith(QLatin1String("iphone")) || (p.platformKind & Platform::BasePlatform) == 0
+ || (p.platformKind & Platform::Cxx11Support) != 0
+ || !p.compilerPath.toString().contains(QLatin1String("clang")))
+ toRemove.append(p.name);
+ }
+ foreach (const QString &pName, toRemove) {
+ if (debugProbe)
+ qDebug() << "filtering out " << pName;
+ platforms.remove(pName);
+ }
+ }
+ QMap<ProjectExplorer::Abi::Architecture, QList<QtSupport::BaseQtVersion *> > qtVersionsForArch;
+ foreach (QtSupport::BaseQtVersion *qtVersion, QtSupport::QtVersionManager::versions()) {
+ if (debugProbe)
+ qDebug() << "qt type " << qtVersion->type();
+ if (qtVersion->type() != QLatin1String(Constants::IOSQT)) {
+ if (qtVersion->qmakeProperty("QMAKE_PLATFORM").contains(QLatin1String("ios"))
+ || qtVersion->qmakeProperty("QMAKE_XSPEC").contains(QLatin1String("ios"))) {
+ // replace with an ios version
+ QtSupport::BaseQtVersion *iosVersion =
+ QtSupport::QtVersionFactory::createQtVersionFromQMakePath(
+ qtVersion->qmakeCommand(),
+ qtVersion->isAutodetected(),
+ qtVersion->autodetectionSource());
+ if (iosVersion && iosVersion->type() == QLatin1String(Constants::IOSQT)) {
+ if (debugProbe)
+ qDebug() << "converting QT to iOS QT for " << qtVersion->qmakeCommand().toUserOutput();
+ QtSupport::QtVersionManager::removeVersion(qtVersion);
+ QtSupport::QtVersionManager::addVersion(iosVersion);
+ qtVersion = iosVersion;
+ } else {
+ continue;
+ }
+ } else {
+ continue;
+ }
+ }
+ if (!qtVersion->isValid())
+ continue;
+ QList<ProjectExplorer::Abi> qtAbis = qtVersion->qtAbis();
+ if (qtAbis.empty())
+ continue;
+ if (debugProbe)
+ qDebug() << "qt arch " << qtAbis.first().architecture();
+ foreach (const ProjectExplorer::Abi &abi, qtAbis)
+ qtVersionsForArch[abi.architecture()].append(qtVersion);
+ }
+
+ QList<ProjectExplorer::Kit *> existingKits;
+ QList<bool> kitMatched;
+ foreach (ProjectExplorer::Kit *k, ProjectExplorer::KitManager::kits()) {
+ Core::Id deviceKind=ProjectExplorer::DeviceTypeKitInformation::deviceTypeId(k);
+ if (deviceKind != Core::Id(Constants::IOS_DEVICE_TYPE)
+ && deviceKind != Core::Id(Constants::IOS_SIMULATOR_TYPE)
+ && deviceKind != Core::Id(ProjectExplorer::Constants::DESKTOP_DEVICE_TYPE)){
+ if (debugProbe)
+ qDebug() << "skipping existing kit with deviceKind " << deviceKind.toString();
+ continue;
+ }
+ if (!k->isAutoDetected()) // use also used set kits?
+ continue;
+ existingKits << k;
+ kitMatched << false;
+ }
+ // create missing kits
+ {
+ QMapIterator<QString, Platform> iter(platforms);
+ while (iter.hasNext()) {
+ iter.next();
+ const Platform &p = iter.value();
+ ProjectExplorer::GccToolChain *pToolchain = platformToolchainMap.value(p.name, 0);
+ if (!pToolchain)
+ continue;
+ Core::Id pDeviceType, pDeviceId;
+ if (debugProbe)
+ qDebug() << "guaranteeing kit for " << p.name ;
+ if (p.name.startsWith(QLatin1String("iphoneos-"))) {
+ pDeviceType = Core::Id(Constants::IOS_DEVICE_TYPE);
+ } else if (p.name.startsWith(QLatin1String("iphonesimulator-"))) {
+ pDeviceType = Core::Id(Constants::IOS_SIMULATOR_TYPE);
+ if (debugProbe)
+ qDebug() << "pDeviceType " << pDeviceType.toString();
+ } else {
+ if (debugProbe)
+ qDebug() << "skipping non ios kit " << p.name;
+ // we looked up only the ios qt build above...
+ continue;
+ //pDeviceType = Core::Id(ProjectExplorer::Constants::DESKTOP_DEVICE_TYPE);
+ }
+ ProjectExplorer::Abi::Architecture arch = pToolchain->targetAbi().architecture();
+
+ QList<QtSupport::BaseQtVersion *> qtVersions = qtVersionsForArch.value(arch);
+ foreach (QtSupport::BaseQtVersion *qt, qtVersions) {
+ bool kitExists = false;
+ for (int i = 0; i < existingKits.size(); ++i) {
+ Kit *k = existingKits.at(i);
+ if (DeviceTypeKitInformation::deviceTypeId(k) == pDeviceType
+ && ToolChainKitInformation::toolChain(k) == pToolchain
+ && SysRootKitInformation::sysRoot(k) == p.sdkPath
+ && QtSupport::QtKitInformation::qtVersion(k) == qt)
+ {
+ kitExists = true;
+ if (debugProbe)
+ qDebug() << "found existing kit " << k->displayName() << " for " << p.name
+ << "," << qt->displayName();
+ if (i<kitMatched.size())
+ kitMatched.replace(i, true);
+ break;
+ }
+ }
+ if (kitExists)
+ continue;
+ if (debugProbe)
+ qDebug() << "setting up new kit for " << p.name;
+ Kit *newKit = new Kit;
+ newKit->setAutoDetected(true);
+ QString baseDisplayName = tr("%1 %2").arg(p.name, qt->displayName());
+ QString displayName = baseDisplayName;
+ for (int iVers = 1; iVers < 100; ++iVers) {
+ bool unique = true;
+ foreach (const ProjectExplorer::Kit *k, existingKits) {
+ if (k->displayName() == displayName) {
+ unique = false;
+ break;
+ }
+ }
+ if (unique) break;
+ displayName = baseDisplayName + QLatin1String("-") + QString::number(iVers);
+ }
+ newKit->setDisplayName(displayName);
+ //newKit->setIconPath(QLatin1String(Constants::IOS_SETTINGS_CATEGORY_ICON));
+ //DeviceKitInformation::setDevice(newKit, pDeviceType);
+ DeviceTypeKitInformation::setDeviceTypeId(newKit, pDeviceType);
+ ToolChainKitInformation::setToolChain(newKit, pToolchain);
+ QtSupport::QtKitInformation::setQtVersion(newKit, qt);
+ //DeviceKitInformation::setDevice(newKit, device);
+ Debugger::DebuggerKitInformation::setDebugger(newKit, pToolchain->suggestedDebugger());
+ SysRootKitInformation::setSysRoot(newKit, p.sdkPath);
+ //Qt4ProjectManager::QmakeKitInformation::setMkspec(newKit,
+ // Utils::FileName::fromString(QLatin1String("macx-ios-clang")));
+ KitManager::registerKit(newKit);
+ existingKits << newKit;
+ }
+ }
+ }
+ // deleting extra (old) kits
+ for (int i = 0; i < kitMatched.size(); ++i) {
+ if (!kitMatched.at(i) && !existingKits.at(i)->isValid()) {
+ qDebug() << "deleting kit " << existingKits.at(i)->displayName();
+ KitManager::deregisterKit(existingKits.at(i));
+ }
+ }
+}
+
+IosConfigurations &IosConfigurations::instance()
+{
+ if (m_instance == 0) {
+ m_instance = new IosConfigurations(0);
+ m_instance->updateSimulators();
+ connect(&(m_instance->m_updateAvailableDevices),SIGNAL(timeout()),
+ IosDeviceManager::instance(),SLOT(monitorAvailableDevices()));
+ m_instance->m_updateAvailableDevices.setSingleShot(true);
+ m_instance->m_updateAvailableDevices.start(10000);
+ }
+ return *m_instance;
+}
+
+void IosConfigurations::save()
+{
+ QSettings *settings = Core::ICore::instance()->settings();
+ settings->beginGroup(SettingsGroup);
+ m_config.save(*settings);
+ settings->endGroup();
+}
+
+IosConfigurations::IosConfigurations(QObject *parent)
+ : QObject(parent)
+{
+ load();
+}
+
+void IosConfigurations::load()
+{
+ QSettings *settings = Core::ICore::instance()->settings();
+ settings->beginGroup(SettingsGroup);
+ m_config = IosConfig(*settings);
+ settings->endGroup();
+}
+
+IosConfigurations *IosConfigurations::m_instance = 0;
+
+QStringList IosConfigurations::sdkTargets()
+{
+ QStringList res;
+ QTC_CHECK(false);
+ return res;
+}
+
+void IosConfigurations::updateSimulators() {
+ // currently we have just one simulator
+ DeviceManager *devManager = DeviceManager::instance();
+ Core::Id devId(Constants::IOS_SIMULATOR_DEVICE_ID);
+ QMap<QString, Platform> platforms = IosProbe::detectPlatforms(m_config.developerPath.toString());
+ QMapIterator<QString, Platform> iter(platforms);
+ Utils::FileName simulatorPath;
+ while (iter.hasNext()) {
+ iter.next();
+ const Platform &p = iter.value();
+ if (p.name.startsWith(QLatin1String("iphonesimulator-"))) {
+ simulatorPath = p.platformPath;
+ simulatorPath.appendPath(QLatin1String(
+ "/Developer/Applications/iPhone Simulator.app/Contents/MacOS/iPhone Simulator"));
+ if (simulatorPath.toFileInfo().exists())
+ break;
+ }
+ }
+ IDevice::ConstPtr dev = devManager->find(devId);
+ if (!simulatorPath.isEmpty() && simulatorPath.toFileInfo().exists()) {
+ if (!dev.isNull()) {
+ if (static_cast<const IosSimulator*>(dev.data())->simulatorPath() == simulatorPath)
+ return;
+ devManager->removeDevice(devId);
+ }
+ IosSimulator *newDev = new IosSimulator(devId, simulatorPath);
+ devManager->addDevice(IDevice::ConstPtr(newDev));
+ } else if (!dev.isNull()) {
+ devManager->removeDevice(devId);
+ }
+}
+
+} // namespace Internal
+} // namespace Ios
diff --git a/src/plugins/ios/iosconfigurations.h b/src/plugins/ios/iosconfigurations.h
new file mode 100644
index 0000000000..0bb0f1abf3
--- /dev/null
+++ b/src/plugins/ios/iosconfigurations.h
@@ -0,0 +1,90 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** 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.
+**
+****************************************************************************/
+#ifndef IOSCONFIGURATIONS_H
+#define IOSCONFIGURATIONS_H
+
+#include <projectexplorer/abi.h>
+#include <utils/fileutils.h>
+
+#include <QMap>
+#include <QObject>
+#include <QString>
+#include <QStringList>
+#include <QTimer>
+
+QT_BEGIN_NAMESPACE
+class QSettings;
+QT_END_NAMESPACE
+
+namespace Ios {
+namespace Internal {
+
+class IosConfig
+{
+public:
+ IosConfig();
+ IosConfig(const QSettings &settings);
+ void save(QSettings &settings) const;
+
+ Utils::FileName developerPath;
+ bool ignoreAllDevices;
+};
+
+class IosConfigurations : public QObject
+{
+ Q_OBJECT
+
+public:
+ static IosConfigurations &instance();
+ IosConfig config() const { return m_config; }
+ void setConfig(const IosConfig &config);
+ Utils::FileName iosToolPath() const;
+
+ QStringList sdkTargets();
+ void updateSimulators();
+signals:
+ void updated();
+
+public slots:
+ void updateAutomaticKitList();
+
+private:
+ IosConfigurations(QObject *parent);
+ void load();
+ void save();
+
+ static IosConfigurations *m_instance;
+ IosConfig m_config;
+ QTimer m_updateAvailableDevices;
+};
+
+} // namespace Internal
+} // namespace Ios
+
+#endif // IOSCONFIGURATIONS_H
diff --git a/src/plugins/ios/iosconstants.h b/src/plugins/ios/iosconstants.h
new file mode 100644
index 0000000000..915b91f119
--- /dev/null
+++ b/src/plugins/ios/iosconstants.h
@@ -0,0 +1,67 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** 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.
+**
+****************************************************************************/
+#ifndef IOSCONSTANTS_H
+#define IOSCONSTANTS_H
+
+#include <QtGlobal>
+
+namespace Ios {
+namespace Internal {
+
+enum IosQemuStatus {
+ IosQemuStarting,
+ IosQemuFailedToStart,
+ IosQemuFinished,
+ IosQemuCrashed,
+ IosQemuUserReason
+};
+
+#ifdef Q_OS_WIN32
+#define IOS_BAT_SUFFIX ".bat"
+#else
+#define IOS_BAT_SUFFIX ""
+#endif
+
+} // namespace Internal
+
+namespace Constants {
+const char IOS_SETTINGS_ID[] = "ZZ.Ios Configurations";
+const char IOS_SETTINGS_CATEGORY[] = "XA.Ios";
+const char IOS_SETTINGS_TR_CATEGORY[] = QT_TRANSLATE_NOOP("Ios", "iOS");
+const char IOS_SETTINGS_CATEGORY_ICON[] = ":/ios/images/QtIos.png";
+const char IOSQT[] = "Qt4ProjectManager.QtVersion.Ios";
+
+const char IOS_DEVICE_TYPE[] = "Ios.Device.Type";
+const char IOS_SIMULATOR_TYPE[] = "Ios.Simulator.Type";
+const char IOS_DEVICE_ID[] = "iOS Device ";
+const char IOS_SIMULATOR_DEVICE_ID[] = "iOS Simulator Device ";
+} // namespace Constants;
+} // namespace Ios
+
+#endif // IOSCONSTANTS_H
diff --git a/src/plugins/ios/iosdebugsupport.cpp b/src/plugins/ios/iosdebugsupport.cpp
new file mode 100644
index 0000000000..2a9bf59445
--- /dev/null
+++ b/src/plugins/ios/iosdebugsupport.cpp
@@ -0,0 +1,136 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** 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 "iosdebugsupport.h"
+
+#include "iosrunner.h"
+#include "iosmanager.h"
+
+#include <debugger/debuggerengine.h>
+#include <debugger/debuggerplugin.h>
+#include <debugger/debuggerkitinformation.h>
+#include <debugger/debuggerrunner.h>
+#include <debugger/debuggerstartparameters.h>
+
+#include <projectexplorer/target.h>
+#include <qt4projectmanager/qt4buildconfiguration.h>
+#include <qt4projectmanager/qt4nodes.h>
+#include <qt4projectmanager/qt4project.h>
+#include <qtsupport/qtkitinformation.h>
+
+#include <QDir>
+
+using namespace Debugger;
+using namespace ProjectExplorer;
+using namespace Qt4ProjectManager;
+
+namespace Ios {
+namespace Internal {
+
+static const char * const qMakeVariables[] = {
+ "QT_INSTALL_LIBS",
+ "QT_INSTALL_PLUGINS",
+ "QT_INSTALL_IMPORTS"
+};
+
+
+RunControl *IosDebugSupport::createDebugRunControl(IosRunConfiguration *runConfig,
+ QString *errorMessage)
+{
+ //Target *target = runConfig->target();
+ //Qt4Project *project = static_cast<Qt4Project *>(target->project());
+
+ DebuggerStartParameters params;
+ params.startMode = AttachToRemoteServer;
+ //params.displayName = IosManager::packageName(target);
+ params.remoteSetupNeeded = true;
+
+ DebuggerRunControl * const debuggerRunControl
+ = DebuggerPlugin::createDebugger(params, runConfig, errorMessage);
+ new IosDebugSupport(runConfig, debuggerRunControl);
+ return debuggerRunControl;
+}
+
+IosDebugSupport::IosDebugSupport(IosRunConfiguration *runConfig,
+ DebuggerRunControl *runControl)
+ : QObject(runControl), m_runControl(runControl),
+ m_runner(new IosRunner(this, runConfig, true)),
+ m_gdbServerPort(0), m_qmlPort(0)
+{
+
+ connect(m_runControl->engine(), SIGNAL(requestRemoteSetup()),
+ m_runner, SLOT(start()));
+ connect(m_runControl, SIGNAL(finished()),
+ m_runner, SLOT(stop()));
+
+ connect(m_runner, SIGNAL(gotGdbSocket(int)),
+ SLOT(handleGdbServerFd(int)));
+ connect(m_runner, SIGNAL(finished(bool)),
+ SLOT(handleRemoteProcessFinished(bool)));
+
+ connect(m_runner, SIGNAL(errorMsg(QString)),
+ SLOT(handleRemoteErrorOutput(QString)));
+ connect(m_runner, SIGNAL(appOutput(QString)),
+ SLOT(handleRemoteOutput(QString)));
+}
+
+void IosDebugSupport::handleGdbServerFd(int gdbServerFd)
+{
+ QTC_CHECK(false); // to do transfer fd to debugger
+ //m_runControl->engine()->notifyEngineRemoteSetupDone(gdbServerPort, qmlPort);
+}
+
+void IosDebugSupport::handleRemoteProcessFinished(bool cleanEnd)
+{
+ if (!cleanEnd && m_runControl)
+ m_runControl->showMessage(tr("Run failed unexpectedly."), AppStuff);
+}
+
+void IosDebugSupport::handleRemoteOutput(const QString &output)
+{
+ if (m_runControl) {
+ if (m_runControl->engine())
+ m_runControl->engine()->showMessage(output, AppOutput);
+ else
+ m_runControl->showMessage(output, AppOutput);
+ }
+}
+
+void IosDebugSupport::handleRemoteErrorOutput(const QString &output)
+{
+ if (m_runControl) {
+ if (m_runControl->engine())
+ m_runControl->engine()->showMessage(output + QLatin1Char('\n'), AppError);
+ else
+ m_runControl->showMessage(output + QLatin1Char('\n'), AppError);
+ }
+}
+
+} // namespace Internal
+} // namespace Ios
diff --git a/src/plugins/ios/iosdebugsupport.h b/src/plugins/ios/iosdebugsupport.h
new file mode 100644
index 0000000000..7d2047bfc5
--- /dev/null
+++ b/src/plugins/ios/iosdebugsupport.h
@@ -0,0 +1,73 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** 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.
+**
+****************************************************************************/
+#ifndef IOSDEBUGSUPPORT_H
+#define IOSDEBUGSUPPORT_H
+
+#include "iosrunconfiguration.h"
+
+namespace Debugger { class DebuggerRunControl; }
+namespace ProjectExplorer { class RunControl; }
+
+namespace Ios {
+namespace Internal {
+
+class IosRunConfiguration;
+class IosRunner;
+
+class IosDebugSupport : public QObject
+{
+ Q_OBJECT
+
+public:
+ static ProjectExplorer::RunControl *createDebugRunControl(IosRunConfiguration *runConfig,
+ QString *errorMessage);
+
+ IosDebugSupport(IosRunConfiguration *runConfig,
+ Debugger::DebuggerRunControl *runControl);
+
+private slots:
+ void handleGdbServerFd(int gdbServerFd);
+ void handleRemoteProcessFinished(bool cleanEnd);
+
+ void handleRemoteOutput(const QString &output);
+ void handleRemoteErrorOutput(const QString &output);
+
+private:
+ Debugger::DebuggerRunControl *m_runControl;
+ IosRunner * const m_runner;
+ const QString m_dumperLib;
+
+ int m_gdbServerPort;
+ int m_qmlPort;
+};
+
+} // namespace Internal
+} // namespace Ios
+
+#endif // IOSDEBUGSUPPORT_H
diff --git a/src/plugins/ios/iosdeployconfiguration.cpp b/src/plugins/ios/iosdeployconfiguration.cpp
new file mode 100644
index 0000000000..975a62647a
--- /dev/null
+++ b/src/plugins/ios/iosdeployconfiguration.cpp
@@ -0,0 +1,138 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** 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 "iosconstants.h"
+#include "iosdeploystep.h"
+#include "iosdeployconfiguration.h"
+#include "iosmanager.h"
+
+#include <projectexplorer/buildsteplist.h>
+#include <projectexplorer/target.h>
+
+#include <qt4projectmanager/qt4project.h>
+#include <qtsupport/qtkitinformation.h>
+#include <qtsupport/qtsupportconstants.h>
+
+using namespace ProjectExplorer;
+
+namespace Ios {
+namespace Internal {
+
+IosDeployConfiguration::IosDeployConfiguration(Target *parent, Core::Id id)
+ : DeployConfiguration(parent, id)
+{
+ setDisplayName(tr("Deploy to iOS"));
+ setDefaultDisplayName(displayName());
+}
+
+IosDeployConfiguration::IosDeployConfiguration(Target *parent, DeployConfiguration *source)
+ : DeployConfiguration(parent, source)
+{
+ cloneSteps(source);
+}
+
+IosDeployConfigurationFactory::IosDeployConfigurationFactory(QObject *parent)
+ : DeployConfigurationFactory(parent)
+{
+ setObjectName(QLatin1String("IosDeployConfigurationFactory"));
+}
+
+bool IosDeployConfigurationFactory::canCreate(Target *parent, const Core::Id id) const
+{
+ return availableCreationIds(parent).contains(id);
+}
+
+DeployConfiguration *IosDeployConfigurationFactory::create(Target *parent, const Core::Id id)
+{
+ IosDeployConfiguration *dc = new IosDeployConfiguration(parent, id);
+ if (!dc)
+ return 0;
+ dc->stepList()->insertStep(0, new IosDeployStep(dc->stepList()));
+ return dc;
+}
+
+bool IosDeployConfigurationFactory::canRestore(Target *parent, const QVariantMap &map) const
+{
+ return canCreate(parent, idFromMap(map));
+}
+
+DeployConfiguration *IosDeployConfigurationFactory::restore(Target *parent, const QVariantMap &map)
+{
+ if (!canRestore(parent, map))
+ return 0;
+
+ IosDeployConfiguration *dc = new IosDeployConfiguration(parent, idFromMap(map));
+ if (dc->fromMap(map))
+ return dc;
+
+ delete dc;
+ return 0;
+}
+
+bool IosDeployConfigurationFactory::canClone(Target *parent, DeployConfiguration *source) const
+{
+ if (!IosManager::supportsIos(parent))
+ return false;
+ return source->id() == IOS_DEPLOYCONFIGURATION_ID;
+}
+
+DeployConfiguration *IosDeployConfigurationFactory::clone(Target *parent, DeployConfiguration *source)
+{
+ if (!canClone(parent, source))
+ return 0;
+ return new IosDeployConfiguration(parent, source);
+}
+
+QList<Core::Id> IosDeployConfigurationFactory::availableCreationIds(Target *parent) const
+{
+ QList<Core::Id> ids;
+ if (!qobject_cast<Qt4ProjectManager::Qt4Project *>(parent->project()))
+ return ids;
+ if (!parent->project()->supportsKit(parent->kit()))
+ return ids;
+ if (!IosManager::supportsIos(parent))
+ return ids;
+ ids << Core::Id(IOS_DEPLOYCONFIGURATION_ID);
+ return ids;
+}
+
+QString IosDeployConfigurationFactory::displayNameForId(const Core::Id id) const
+{
+ if (id.name().startsWith(IOS_DC_PREFIX))
+ return tr("Deploy on iOS");
+ return QString();
+}
+
+bool IosDeployConfigurationFactory::canHandle(Target *parent) const
+{
+ return IosManager::supportsIos(parent);
+}
+
+} // namespace Internal
+} // namespace Ios
diff --git a/src/plugins/ios/iosdeployconfiguration.h b/src/plugins/ios/iosdeployconfiguration.h
new file mode 100644
index 0000000000..632b2b5aa4
--- /dev/null
+++ b/src/plugins/ios/iosdeployconfiguration.h
@@ -0,0 +1,82 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** 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.
+**
+****************************************************************************/
+#ifndef IOSDEPLOYCONFIGURATION_H
+#define IOSDEPLOYCONFIGURATION_H
+
+#include <projectexplorer/deployconfiguration.h>
+
+namespace Ios {
+namespace Internal {
+
+const char IOS_DEPLOYCONFIGURATION_ID[] = "Qt4ProjectManager.IosDeployConfiguration";
+const char IOS_DC_PREFIX[] = "Qt4ProjectManager.IosDeployConfiguration.";
+
+class IosDeployConfiguration : public ProjectExplorer::DeployConfiguration
+{
+ Q_OBJECT
+ friend class IosDeployConfigurationFactory;
+
+public:
+ IosDeployConfiguration(ProjectExplorer::Target *parent, Core::Id id);
+
+protected:
+ IosDeployConfiguration(ProjectExplorer::Target *parent,
+ ProjectExplorer::DeployConfiguration *source);
+
+};
+
+class IosDeployConfigurationFactory : public ProjectExplorer::DeployConfigurationFactory
+{
+ Q_OBJECT
+
+public:
+ explicit IosDeployConfigurationFactory(QObject *parent = 0);
+
+ bool canCreate(ProjectExplorer::Target *parent, const Core::Id id) const;
+ ProjectExplorer::DeployConfiguration *create(ProjectExplorer::Target *parent, const Core::Id id);
+ bool canRestore(ProjectExplorer::Target *parent, const QVariantMap &map) const;
+ ProjectExplorer::DeployConfiguration *restore(ProjectExplorer::Target *parent,
+ const QVariantMap &map);
+ bool canClone(ProjectExplorer::Target *parent,
+ ProjectExplorer::DeployConfiguration *source) const;
+ ProjectExplorer::DeployConfiguration *clone(ProjectExplorer::Target *parent,
+ ProjectExplorer::DeployConfiguration *source);
+
+ QList<Core::Id> availableCreationIds(ProjectExplorer::Target *parent) const;
+ // used to translate the ids to names to display to the user
+ QString displayNameForId(const Core::Id id) const;
+
+private:
+ bool canHandle(ProjectExplorer::Target *parent) const;
+};
+
+} // namespace Internal
+} // namespace Ios
+
+#endif // IOSDEPLOYCONFIGURATION_H
diff --git a/src/plugins/ios/iosdeploystep.cpp b/src/plugins/ios/iosdeploystep.cpp
new file mode 100644
index 0000000000..f6bce58dbc
--- /dev/null
+++ b/src/plugins/ios/iosdeploystep.cpp
@@ -0,0 +1,231 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** 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 "iosdeploystepwidget.h"
+#include "iosdeploystep.h"
+#include "iosbuildstep.h"
+#include "iosconstants.h"
+#include "iosrunconfiguration.h"
+#include "iosmanager.h"
+#include "iostoolhandler.h"
+
+#include <coreplugin/messagemanager.h>
+#include <projectexplorer/buildconfiguration.h>
+#include <projectexplorer/projectexplorerconstants.h>
+#include <projectexplorer/target.h>
+#include <qt4projectmanager/qt4buildconfiguration.h>
+#include <qt4projectmanager/qt4project.h>
+#include <qt4projectmanager/qt4nodes.h>
+
+#include <qtsupport/qtkitinformation.h>
+
+#include <QDir>
+
+#define ASSERT_STATE(state) ASSERT_STATE_GENERIC(State, state, m_state)
+
+using namespace ProjectExplorer;
+using namespace Qt4ProjectManager;
+
+namespace Ios {
+namespace Internal {
+
+static const char USE_LOCAL_QT_KEY[] = "Qt4ProjectManager.IosDeployStep.UseLocalQtLibs";
+
+const Core::Id IosDeployStep::Id("Qt4ProjectManager.IosDeployStep");
+
+IosDeployStep::IosDeployStep(ProjectExplorer::BuildStepList *parent)
+ : BuildStep(parent, Id)
+{
+ ctor();
+}
+
+IosDeployStep::IosDeployStep(ProjectExplorer::BuildStepList *parent,
+ IosDeployStep *other)
+ : BuildStep(parent, other)
+{
+ ctor();
+}
+
+IosDeployStep::~IosDeployStep() { }
+
+void IosDeployStep::ctor()
+{
+ m_transferStatus = NoTransfer;
+ m_device = ProjectExplorer::DeviceKitInformation::device(target()->kit());
+ QString devName;
+ if (!m_device.isNull())
+ devName = m_device->displayName();
+ if (devName.isEmpty())
+ devName = tr("iOS device");
+ setDefaultDisplayName(tr("Deploy to %1").arg(devName));
+}
+
+bool IosDeployStep::init()
+{
+ QTC_CHECK(m_transferStatus == NoTransfer);
+ m_device = ProjectExplorer::DeviceKitInformation::device(target()->kit());
+ return true;
+}
+
+void IosDeployStep::run(QFutureInterface<bool> &fi)
+{
+ m_futureInterface = fi;
+ QTC_CHECK(m_transferStatus == NoTransfer);
+ if (iosdevice().isNull()) {
+ m_futureInterface.reportResult(!iossimulator().isNull());
+ cleanup();
+ m_futureInterface.reportFinished();
+ return;
+ }
+ m_transferStatus = TransferInProgress;
+ IosToolHandler *toolHandler = new IosToolHandler(IosToolHandler::IosDeviceType, this);
+ m_futureInterface.setProgressRange(0, 200);
+ m_futureInterface.setProgressValueAndText(0, QLatin1String("Transferring application"));
+ m_futureInterface.reportStarted();
+ connect(toolHandler, SIGNAL(isTransferringApp(Ios::IosToolHandler*,QString,QString,int,int,QString)),
+ SLOT(handleIsTransferringApp(Ios::IosToolHandler*,QString,QString,int,int,QString)));
+ connect(toolHandler, SIGNAL(didTransferApp(Ios::IosToolHandler*,QString,QString,Ios::IosToolHandler::OpStatus)),
+ SLOT(handleDidTransferApp(Ios::IosToolHandler*,QString,QString,Ios::IosToolHandler::OpStatus)));
+ connect(toolHandler, SIGNAL(finished(Ios::IosToolHandler*)),
+ SLOT(handleFinished(Ios::IosToolHandler*)));
+ toolHandler->requestTransferApp(appBundle(), deviceId());
+}
+
+void IosDeployStep::cancel()
+{
+ if (m_toolHandler)
+ m_toolHandler->stop();
+}
+
+void IosDeployStep::cleanup()
+{
+ QTC_CHECK(m_transferStatus != TransferInProgress);
+ m_transferStatus = NoTransfer;
+ m_device.clear();
+ m_toolHandler = 0;
+}
+
+void IosDeployStep::handleIsTransferringApp(IosToolHandler *handler, const QString &bundlePath,
+ const QString &deviceId, int progress, int maxProgress,
+ const QString &info)
+{
+ Q_UNUSED(handler); Q_UNUSED(bundlePath); Q_UNUSED(deviceId);
+ QTC_CHECK(m_transferStatus == TransferInProgress);
+ m_futureInterface.setProgressRange(0, maxProgress);
+ m_futureInterface.setProgressValueAndText(progress, info);
+}
+
+void IosDeployStep::handleDidTransferApp(IosToolHandler *handler, const QString &bundlePath,
+ const QString &deviceId, IosToolHandler::OpStatus status)
+{
+ Q_UNUSED(handler); Q_UNUSED(bundlePath); Q_UNUSED(deviceId);
+ QTC_CHECK(m_transferStatus == TransferInProgress);
+ if (status == IosToolHandler::Success)
+ m_transferStatus = TransferOk;
+ else
+ m_transferStatus = TransferFailed;
+ m_futureInterface.reportResult(status == IosToolHandler::Success);
+}
+
+void IosDeployStep::handleFinished(IosToolHandler *handler)
+{
+ switch (m_transferStatus) {
+ case TransferInProgress:
+ m_transferStatus = TransferFailed;
+ m_futureInterface.reportResult(false);
+ break;
+ case NoTransfer:
+ case TransferOk:
+ case TransferFailed:
+ break;
+ }
+ cleanup();
+ handler->deleteLater();
+ // move it when result is reported? (would need care to avoid problems with concurrent runs)
+ m_futureInterface.reportFinished();
+}
+
+BuildStepConfigWidget *IosDeployStep::createConfigWidget()
+{
+ return new IosDeployStepWidget(this);
+}
+
+bool IosDeployStep::fromMap(const QVariantMap &map)
+{
+ return ProjectExplorer::BuildStep::fromMap(map);
+}
+
+QVariantMap IosDeployStep::toMap() const
+{
+ QVariantMap map = ProjectExplorer::BuildStep::toMap();
+ return map;
+}
+
+QString IosDeployStep::deviceId() const
+{
+ if (iosdevice().isNull())
+ return QString();
+ return iosdevice()->uniqueDeviceID();
+}
+
+QString IosDeployStep::appBundle() const
+{
+ IosRunConfiguration * runConfig = qobject_cast<IosRunConfiguration *>(
+ this->target()->activeRunConfiguration());
+ return runConfig->bundleDir().toString();
+}
+
+void IosDeployStep::raiseError(const QString &errorString)
+{
+ emit addTask(Task(Task::Error, errorString, Utils::FileName::fromString(QString()), -1,
+ ProjectExplorer::Constants::TASK_CATEGORY_BUILDSYSTEM));
+}
+
+void IosDeployStep::writeOutput(const QString &text, OutputFormat format)
+{
+ emit addOutput(text, format);
+}
+
+IDevice::ConstPtr IosDeployStep::device() const
+{
+ return m_device;
+}
+
+IosDevice::ConstPtr IosDeployStep::iosdevice() const
+{
+ return m_device.dynamicCast<const IosDevice>();
+}
+
+IosSimulator::ConstPtr IosDeployStep::iossimulator() const
+{
+ return m_device.dynamicCast<const IosSimulator>();
+}
+
+} // namespace Internal
+} // namespace Qt4ProjectManager
diff --git a/src/plugins/ios/iosdeploystep.h b/src/plugins/ios/iosdeploystep.h
new file mode 100644
index 0000000000..23988cf68f
--- /dev/null
+++ b/src/plugins/ios/iosdeploystep.h
@@ -0,0 +1,135 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** 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.
+**
+****************************************************************************/
+#ifndef IOSDEPLOYSTEP_H
+#define IOSDEPLOYSTEP_H
+
+#include "iosconfigurations.h"
+#include "iosdevice.h"
+#include "iossimulator.h"
+
+#include <projectexplorer/buildstep.h>
+#include <projectexplorer/devicesupport/idevice.h>
+#include <qtsupport/baseqtversion.h>
+
+#include <QFutureInterface>
+#include <QProcess>
+
+QT_BEGIN_NAMESPACE
+class QEventLoop;
+class QTimer;
+QT_END_NAMESPACE
+
+namespace Ios {
+class IosToolHandler;
+namespace Internal {
+class IosDeviceConfigListModel;
+class IosPackageCreationStep;
+
+class DeployItem
+{
+public:
+ DeployItem(const QString &_localFileName,
+ unsigned int _localTimeStamp,
+ const QString &_remoteFileName,
+ bool _needsStrip)
+ : localFileName(_localFileName),
+ remoteFileName(_remoteFileName),
+ localTimeStamp(_localTimeStamp),
+ remoteTimeStamp(0),
+ needsStrip(_needsStrip)
+ {}
+ QString localFileName;
+ QString remoteFileName;
+ unsigned int localTimeStamp;
+ unsigned int remoteTimeStamp;
+ bool needsStrip;
+};
+
+class IosDeployStep : public ProjectExplorer::BuildStep
+{
+ Q_OBJECT
+public:
+ enum TransferStatus {
+ NoTransfer,
+ TransferInProgress,
+ TransferOk,
+ TransferFailed
+ };
+
+ friend class IosDeployStepFactory;
+ IosDeployStep(ProjectExplorer::BuildStepList *bc);
+
+ virtual ~IosDeployStep();
+
+ bool fromMap(const QVariantMap &map);
+ QVariantMap toMap() const;
+
+ virtual void run(QFutureInterface<bool> &fi);
+ void cleanup();
+ ProjectExplorer::IDevice::ConstPtr device() const;
+ IosDevice::ConstPtr iosdevice() const;
+ IosSimulator::ConstPtr iossimulator() const;
+ void cancel();
+signals:
+ //void done();
+ //void error();
+
+private slots:
+ void handleIsTransferringApp(Ios::IosToolHandler *handler, const QString &bundlePath,
+ const QString &deviceId, int progress, int maxProgress,
+ const QString &info);
+ void handleDidTransferApp(Ios::IosToolHandler *handler, const QString &bundlePath, const QString &deviceId,
+ Ios::IosToolHandler::OpStatus status);
+ void handleFinished(Ios::IosToolHandler *handler);
+
+private:
+ IosDeployStep(ProjectExplorer::BuildStepList *bc,
+ IosDeployStep *other);
+ bool init();
+ ProjectExplorer::BuildStepConfigWidget *createConfigWidget();
+ bool immutable() const { return true; }
+ bool runInGuiThread() const { return true; }
+
+ void ctor();
+ QString deviceId() const;
+ QString appBundle() const;
+ void raiseError(const QString &error);
+ void writeOutput(const QString &text, OutputFormat = MessageOutput);
+private:
+ TransferStatus m_transferStatus;
+ IosToolHandler *m_toolHandler;
+ QFutureInterface<bool> m_futureInterface;
+ ProjectExplorer::IDevice::ConstPtr m_device;
+ static const Core::Id Id;
+};
+
+} // namespace Internal
+} // namespace Ios
+
+#endif // IOSDEPLOYSTEP_H
diff --git a/src/plugins/ios/iosdeploystepfactory.cpp b/src/plugins/ios/iosdeploystepfactory.cpp
new file mode 100644
index 0000000000..8b8430fbe6
--- /dev/null
+++ b/src/plugins/ios/iosdeploystepfactory.cpp
@@ -0,0 +1,111 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** 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 "iosdeploystepfactory.h"
+
+#include "iosdeploystep.h"
+#include "iosmanager.h"
+
+#include <projectexplorer/buildsteplist.h>
+#include <projectexplorer/projectexplorerconstants.h>
+#include <projectexplorer/target.h>
+#include <qtsupport/qtsupportconstants.h>
+#include <qtsupport/qtkitinformation.h>
+
+#include <QCoreApplication>
+
+using namespace ProjectExplorer;
+
+namespace Ios {
+namespace Internal {
+
+IosDeployStepFactory::IosDeployStepFactory(QObject *parent)
+ : IBuildStepFactory(parent)
+{
+}
+
+QList<Core::Id> IosDeployStepFactory::availableCreationIds(BuildStepList *parent) const
+{
+ if (parent->id() != ProjectExplorer::Constants::BUILDSTEPS_DEPLOY)
+ return QList<Core::Id>();
+ if (!IosManager::supportsIos(parent->target()))
+ return QList<Core::Id>();
+ if (parent->contains(IosDeployStep::Id))
+ return QList<Core::Id>();
+ return QList<Core::Id>() << IosDeployStep::Id;
+}
+
+QString IosDeployStepFactory::displayNameForId(const Core::Id id) const
+{
+ if (id == IosDeployStep::Id)
+ return tr("Deploy to iOS device or emulator");
+ return QString();
+}
+
+bool IosDeployStepFactory::canCreate(BuildStepList *parent, const Core::Id id) const
+{
+ return availableCreationIds(parent).contains(id);
+}
+
+BuildStep *IosDeployStepFactory::create(BuildStepList *parent, const Core::Id id)
+{
+ Q_ASSERT(canCreate(parent, id));
+ Q_UNUSED(id);
+ return new IosDeployStep(parent);
+}
+
+bool IosDeployStepFactory::canRestore(BuildStepList *parent, const QVariantMap &map) const
+{
+ return canCreate(parent, idFromMap(map));
+}
+
+BuildStep *IosDeployStepFactory::restore(BuildStepList *parent, const QVariantMap &map)
+{
+ Q_ASSERT(canRestore(parent, map));
+ IosDeployStep * const step = new IosDeployStep(parent);
+ if (!step->fromMap(map)) {
+ delete step;
+ return 0;
+ }
+ return step;
+}
+
+bool IosDeployStepFactory::canClone(BuildStepList *parent, BuildStep *product) const
+{
+ return canCreate(parent, product->id());
+}
+
+BuildStep *IosDeployStepFactory::clone(BuildStepList *parent, BuildStep *product)
+{
+ Q_ASSERT(canClone(parent, product));
+ return new IosDeployStep(parent, static_cast<IosDeployStep *>(product));
+}
+
+} // namespace Internal
+} // namespace Ios
diff --git a/src/plugins/ios/iosdeploystepfactory.h b/src/plugins/ios/iosdeploystepfactory.h
new file mode 100644
index 0000000000..9249488392
--- /dev/null
+++ b/src/plugins/ios/iosdeploystepfactory.h
@@ -0,0 +1,62 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** 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.
+**
+****************************************************************************/
+#ifndef IOSDEPLOYSTEPFACTORY_H
+#define IOSDEPLOYSTEPFACTORY_H
+
+#include <projectexplorer/buildstep.h>
+
+namespace Ios {
+namespace Internal {
+
+class IosDeployStepFactory : public ProjectExplorer::IBuildStepFactory
+{
+ Q_OBJECT
+public:
+ explicit IosDeployStepFactory(QObject *parent = 0);
+
+ QList<Core::Id> availableCreationIds(ProjectExplorer::BuildStepList *parent) const;
+ QString displayNameForId(const Core::Id id) const;
+
+ bool canCreate(ProjectExplorer::BuildStepList *parent,
+ const Core::Id id) const;
+ ProjectExplorer::BuildStep *create(ProjectExplorer::BuildStepList *parent, const Core::Id id);
+
+ bool canRestore(ProjectExplorer::BuildStepList *parent, const QVariantMap &map) const;
+ ProjectExplorer::BuildStep *restore(ProjectExplorer::BuildStepList *parent, const QVariantMap &map);
+
+ bool canClone(ProjectExplorer::BuildStepList *parent,
+ ProjectExplorer::BuildStep *product) const;
+ ProjectExplorer::BuildStep *clone(ProjectExplorer::BuildStepList *parent,
+ ProjectExplorer::BuildStep *product);
+};
+
+} // namespace Internal
+} // namespace Ios
+
+#endif // IOSDEPLOYSTEPFACTORY_H
diff --git a/src/plugins/ios/iosdeploystepwidget.cpp b/src/plugins/ios/iosdeploystepwidget.cpp
new file mode 100644
index 0000000000..d1e09b7adb
--- /dev/null
+++ b/src/plugins/ios/iosdeploystepwidget.cpp
@@ -0,0 +1,70 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** 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 "iosdeploystepwidget.h"
+#include "ui_iosdeploystepwidget.h"
+
+#include "iosdeploystep.h"
+#include "iosrunconfiguration.h"
+
+#include <coreplugin/icore.h>
+
+#include <QFileDialog>
+
+namespace Ios {
+namespace Internal {
+
+IosDeployStepWidget::IosDeployStepWidget(IosDeployStep *step) :
+ ProjectExplorer::BuildStepConfigWidget(),
+ ui(new Ui::IosDeployStepWidget),
+ m_step(step)
+{
+ ui->setupUi(this);
+}
+
+IosDeployStepWidget::~IosDeployStepWidget()
+{
+ delete ui;
+}
+
+QString IosDeployStepWidget::displayName() const
+{
+ QString deviceName = tr("iOS Device");
+ if (!m_step->device().isNull())
+ deviceName = m_step->device()->displayName();
+ return tr("<b>Deploy to %1</b>").arg(deviceName);
+}
+
+QString IosDeployStepWidget::summaryText() const
+{
+ return displayName();
+}
+
+} // namespace Internal
+} // namespace Qt4ProjectManager
diff --git a/src/plugins/ios/iosdeploystepwidget.h b/src/plugins/ios/iosdeploystepwidget.h
new file mode 100644
index 0000000000..1c3be04e40
--- /dev/null
+++ b/src/plugins/ios/iosdeploystepwidget.h
@@ -0,0 +1,61 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** 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.
+**
+****************************************************************************/
+#ifndef IOSDEPLOYSTEPWIDGET_H
+#define IOSDEPLOYSTEPWIDGET_H
+
+#include <projectexplorer/buildstep.h>
+
+QT_BEGIN_NAMESPACE
+namespace Ui { class IosDeployStepWidget; }
+QT_END_NAMESPACE
+
+namespace Ios {
+namespace Internal {
+class IosDeployStep;
+
+class IosDeployStepWidget : public ProjectExplorer::BuildStepConfigWidget
+{
+ Q_OBJECT
+
+public:
+ IosDeployStepWidget(IosDeployStep *step);
+ ~IosDeployStepWidget();
+
+private:
+ virtual QString summaryText() const;
+ virtual QString displayName() const;
+
+ Ui::IosDeployStepWidget *ui;
+ IosDeployStep *m_step;
+};
+
+} // namespace Internal
+} // namespace Ios
+
+#endif // IOSDEPLOYSTEPWIDGET_H
diff --git a/src/plugins/ios/iosdeploystepwidget.ui b/src/plugins/ios/iosdeploystepwidget.ui
new file mode 100644
index 0000000000..7b97b387b2
--- /dev/null
+++ b/src/plugins/ios/iosdeploystepwidget.ui
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>IosDeployStepWidget</class>
+ <widget class="QWidget" name="IosDeployStepWidget">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>479</width>
+ <height>76</height>
+ </rect>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Minimum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="windowTitle">
+ <string>Form</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout"/>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/src/plugins/ios/iosdevice.cpp b/src/plugins/ios/iosdevice.cpp
new file mode 100644
index 0000000000..853328a0ff
--- /dev/null
+++ b/src/plugins/ios/iosdevice.cpp
@@ -0,0 +1,519 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** 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 "iosdevice.h"
+#include "iosconstants.h"
+#include "iosconfigurations.h"
+#include "iostoolhandler.h"
+#include <projectexplorer/devicesupport/devicemanager.h>
+#include <projectexplorer/kitinformation.h>
+
+#include <QCoreApplication>
+#include <QVariant>
+#include <QVariantMap>
+#include <QMessageBox>
+
+#include <IOKit/IOKitLib.h>
+#include <IOKit/usb/IOUSBLib.h>
+#include <CoreFoundation/CoreFoundation.h>
+using namespace ProjectExplorer;
+
+static bool debugDeviceDetection = false;
+
+namespace {
+QString CFStringRef2QString(CFStringRef s)
+{
+ unsigned char buf[250];
+ CFIndex len = CFStringGetLength(s);
+ CFIndex usedBufLen;
+ CFIndex converted = CFStringGetBytes(s, CFRangeMake(0,len), kCFStringEncodingUTF8,
+ '?', false, &buf[0], sizeof(buf), &usedBufLen);
+ if (converted == len)
+ return QString::fromUtf8(reinterpret_cast<char *>(&buf[0]), usedBufLen);
+ size_t bufSize = sizeof(buf)
+ + CFStringGetMaximumSizeForEncoding(len - converted, kCFStringEncodingUTF8);
+ unsigned char *bigBuf = new unsigned char[bufSize];
+ memcpy(bigBuf, buf, usedBufLen);
+ CFIndex newUseBufLen;
+ CFStringGetBytes(s, CFRangeMake(converted,len), kCFStringEncodingUTF8,
+ '?', false, &bigBuf[usedBufLen], bufSize, &newUseBufLen);
+ QString res = QString::fromUtf8(reinterpret_cast<char *>(bigBuf), usedBufLen + newUseBufLen);
+ delete[] bigBuf;
+ return res;
+}
+
+}
+
+namespace Ios {
+namespace Internal {
+
+const char extraInfoKey[] = "extraInfo";
+
+IosDevice::IosDevice()
+ : IDevice(Core::Id(Constants::IOS_DEVICE_TYPE),
+ IDevice::AutoDetected,
+ IDevice::Hardware,
+ Constants::IOS_DEVICE_ID)
+{
+ setDisplayName(QCoreApplication::translate("Ios::Internal::IosDevice", "iOS Device"));
+ setDeviceState(DeviceStateUnknown);
+}
+
+IosDevice::IosDevice(const IosDevice &other)
+ : IDevice(other), m_extraInfo(other.m_extraInfo), m_ignoreDevice(other.m_ignoreDevice)
+{ }
+
+IosDevice::IosDevice(const QString &uid)
+ : IDevice(Core::Id(Constants::IOS_DEVICE_TYPE),
+ IDevice::AutoDetected,
+ IDevice::Hardware,
+ Core::Id(Constants::IOS_DEVICE_ID).withSuffix(uid))
+{
+ setDisplayName(QCoreApplication::translate("Ios::Internal::IosDevice", "iOS Device"));
+ setDeviceState(DeviceStateUnknown);
+}
+
+
+IDevice::DeviceInfo IosDevice::deviceInformation() const
+{
+ IDevice::DeviceInfo res;
+ QMapIterator<QString, QString> i(m_extraInfo);
+ while (i.hasNext()) {
+ i.next();
+ IosDeviceManager::TranslationMap tMap = IosDeviceManager::translationMap();
+ if (tMap.contains(i.key()))
+ res.append(DeviceInfoItem(tMap.value(i.key()), tMap.value(i.value(), i.value())));
+ }
+ return res;
+}
+
+QString IosDevice::displayType() const
+{
+ return QCoreApplication::translate("Ios::Internal::IosDevice", "iOS");
+}
+
+IDeviceWidget *IosDevice::createWidget()
+{
+ return 0;
+}
+
+QList<Core::Id> IosDevice::actionIds() const
+{
+ return QList<Core::Id>(); // add activation action?
+}
+
+QString IosDevice::displayNameForActionId(Core::Id actionId) const
+{
+ Q_UNUSED(actionId)
+ return QString();
+}
+
+void IosDevice::executeAction(Core::Id actionId, QWidget *parent)
+{
+ Q_UNUSED(actionId)
+ Q_UNUSED(parent)
+}
+
+DeviceProcessSignalOperation::Ptr IosDevice::signalOperation() const
+{
+ return DeviceProcessSignalOperation::Ptr();
+}
+
+IDevice::Ptr IosDevice::clone() const
+{
+ return IDevice::Ptr(new IosDevice(*this));
+}
+
+void IosDevice::fromMap(const QVariantMap &map)
+{
+ IDevice::fromMap(map);
+ QVariantMap vMap = map.value(QLatin1String(extraInfoKey)).toMap();
+ QMapIterator<QString, QVariant> i(vMap);
+ m_extraInfo.clear();
+ while (i.hasNext()) {
+ i.next();
+ m_extraInfo.insert(i.key(), i.value().toString());
+ }
+}
+
+QVariantMap IosDevice::toMap() const
+{
+ QVariantMap res = IDevice::toMap();
+ QVariantMap vMap;
+ QMapIterator<QString, QString> i(m_extraInfo);
+ while (i.hasNext()) {
+ i.next();
+ vMap.insert(i.key(), i.value());
+ }
+ res.insert(QLatin1String(extraInfoKey), vMap);
+ return res;
+}
+
+QString IosDevice::uniqueDeviceID() const
+{
+ return id().suffixAfter(Core::Id(Constants::IOS_DEVICE_ID));
+}
+/*
+// add back?
+
+QString IosDevice::cpuArchitecure() const
+{
+ return m_extraInfo.value(QLatin1String("deviceInfo")).toMap()
+ .value(QLatin1String("CPUArchitecture")).toString();
+}
+
+QString IosDevice::productType() const
+{
+ return m_extraInfo.value(QLatin1String("deviceInfo")).toMap()
+ .value(QLatin1String("ProductType")).toString();
+}*/
+
+
+// IosDeviceManager
+
+IosDeviceManager::TranslationMap IosDeviceManager::translationMap()
+{
+ static TranslationMap *translationMap = 0;
+ if (translationMap)
+ return *translationMap;
+ TranslationMap &tMap = *new TranslationMap;
+ tMap[QLatin1String("deviceName")] = tr("Device name");
+ tMap[QLatin1String("developerStatus")] = tr("Developer status");
+ tMap[QLatin1String("deviceConnected")] = tr("Connected");
+ tMap[QLatin1String("YES")] = tr("yes");
+ tMap[QLatin1String("NO")] = tr("no");
+ tMap[QLatin1String("YES")] = tr("yes");
+ tMap[QLatin1String("*unknown*")] = tr("unknown");
+ translationMap = &tMap;
+ return tMap;
+}
+
+void IosDeviceManager::deviceConnected(const QString &uid, const QString &name)
+{
+ DeviceManager *devManager = DeviceManager::instance();
+ Core::Id baseDevId(Constants::IOS_DEVICE_ID);
+ Core::Id devType(Constants::IOS_DEVICE_TYPE);
+ Core::Id devId = baseDevId.withSuffix(uid);
+ IDevice::ConstPtr dev = devManager->find(devId);
+ if (dev.isNull()) {
+ IosDevice *newDev = new IosDevice(uid);
+ if (!name.isNull())
+ newDev->setDisplayName(name);
+ if (debugDeviceDetection)
+ qDebug() << "adding ios device " << uid;
+ devManager->addDevice(IDevice::ConstPtr(newDev));
+ } else if (dev->deviceState() != IDevice::DeviceConnected &&
+ dev->deviceState() != IDevice::DeviceReadyToUse) {
+ if (debugDeviceDetection)
+ qDebug() << "updating ios device " << uid;
+ IosDevice *newDev = 0;
+ if (dev->type() == devType) {
+ const IosDevice *iosDev = static_cast<const IosDevice *>(dev.data());
+ newDev = new IosDevice(*iosDev);
+ } else {
+ newDev = new IosDevice(uid);
+ }
+ devManager->addDevice(IDevice::ConstPtr(newDev));
+ }
+ updateInfo(uid);
+}
+
+void IosDeviceManager::deviceDisconnected(const QString &uid)
+{
+ if (debugDeviceDetection)
+ qDebug() << "detected disconnection of ios device " << uid;
+ DeviceManager *devManager = DeviceManager::instance();
+ Core::Id baseDevId(Constants::IOS_DEVICE_ID);
+ Core::Id devType(Constants::IOS_DEVICE_TYPE);
+ Core::Id devId = baseDevId.withSuffix(uid);
+ IDevice::ConstPtr dev = devManager->find(devId);
+ if (dev.isNull() || dev->type() != devType) {
+ qDebug() << "ignoring disconnection of ios device " << uid; // should neve happen
+ } else {
+ const IosDevice *iosDev = static_cast<const IosDevice *>(dev.data());
+ if (iosDev->deviceState() != IDevice::DeviceDisconnected) {
+ if (debugDeviceDetection)
+ qDebug() << "disconnecting device " << iosDev->uniqueDeviceID();
+ devManager->setDeviceState(iosDev->id(), IDevice::DeviceDisconnected);
+ }
+ }
+}
+
+void IosDeviceManager::updateInfo(const QString &devId)
+{
+ IosToolHandler *requester = new IosToolHandler(IosToolHandler::IosDeviceType, this);
+ connect(requester, SIGNAL(deviceInfo(Ios::IosToolHandler*,QString,Ios::IosToolHandler::Dict)),
+ SLOT(deviceInfo(Ios::IosToolHandler *,QString,Ios::IosToolHandler::Dict)));
+ connect(requester, SIGNAL(finished(Ios::IosToolHandler*)),
+ SLOT(infoGathererFinished(Ios::IosToolHandler*)));
+ requester->requestDeviceInfo(devId);
+}
+
+void IosDeviceManager::deviceInfo(IosToolHandler *, const QString &uid,
+ const Ios::IosToolHandler::Dict &info)
+{
+ DeviceManager *devManager = DeviceManager::instance();
+ Core::Id baseDevId(Constants::IOS_DEVICE_ID);
+ Core::Id devType(Constants::IOS_DEVICE_TYPE);
+ Core::Id devId = baseDevId.withSuffix(uid);
+ IDevice::ConstPtr dev = devManager->find(devId);
+ bool skipUpdate = false;
+ IosDevice *newDev = 0;
+ if (!dev.isNull() && dev->type() == devType) {
+ const IosDevice *iosDev = static_cast<const IosDevice *>(dev.data());
+ if (iosDev->m_extraInfo == info) {
+ skipUpdate = true;
+ newDev = const_cast<IosDevice *>(iosDev);
+ } else {
+ newDev = new IosDevice(*iosDev);
+ }
+ } else {
+ newDev = new IosDevice(uid);
+ }
+ if (!skipUpdate) {
+ QString devNameKey = QLatin1String("deviceName");
+ if (info.contains(devNameKey))
+ newDev->setDisplayName(info.value(devNameKey));
+ newDev->m_extraInfo = info;
+ if (debugDeviceDetection)
+ qDebug() << "updated info of ios device " << uid;
+ dev = IDevice::ConstPtr(newDev);
+ devManager->addDevice(dev);
+ }
+ QLatin1String devStatusKey = QLatin1String("developerStatus");
+ if (info.contains(devStatusKey)) {
+ QString devStatus = info.value(devStatusKey);
+ if (devStatus == QLatin1String("*off*")) {
+ devManager->setDeviceState(newDev->id(), IDevice::DeviceConnected);
+ if (!newDev->m_ignoreDevice && !IosConfigurations::instance().config().ignoreAllDevices) {
+ QMessageBox mBox;
+ mBox.setText(tr("An iOS device in user mode has been detected."));
+ mBox.setInformativeText(tr("Do you want to see how to set it up for development?"));
+ mBox.setStandardButtons(QMessageBox::NoAll | QMessageBox::No | QMessageBox::Yes);
+ mBox.setDefaultButton(QMessageBox::No);
+ int ret = mBox.exec();
+ switch (ret) {
+ case QMessageBox::Yes:
+ // open doc
+ break;
+ case QMessageBox::No:
+ newDev->m_ignoreDevice = true;
+ break;
+ case QMessageBox::NoAll:
+ {
+ IosConfig conf = IosConfigurations::instance().config();
+ conf.ignoreAllDevices = true;
+ IosConfigurations::instance().setConfig(conf);
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ } else if (devStatus == QLatin1String("Development")) {
+ devManager->setDeviceState(newDev->id(), IDevice::DeviceReadyToUse);
+ } else {
+ devManager->setDeviceState(newDev->id(), IDevice::DeviceConnected);
+ }
+ }
+}
+
+void IosDeviceManager::infoGathererFinished(IosToolHandler *gatherer)
+{
+ gatherer->deleteLater();
+}
+
+namespace {
+io_iterator_t gAddedIter;
+io_iterator_t gRemovedIter;
+
+extern "C" {
+void deviceConnectedCallback(void *refCon, io_iterator_t iterator)
+{
+ kern_return_t kr;
+ io_service_t usbDevice;
+ (void) refCon;
+
+ while ((usbDevice = IOIteratorNext(iterator))) {
+ io_name_t deviceName;
+
+ // Get the USB device's name.
+ kr = IORegistryEntryGetName(usbDevice, deviceName);
+ QString name;
+ if (KERN_SUCCESS == kr)
+ name = QString::fromLocal8Bit(deviceName);
+ if (debugDeviceDetection)
+ qDebug() << "ios device " << name << " in deviceAddedCallback";
+
+ CFStringRef cfUid = static_cast<CFStringRef>(IORegistryEntryCreateCFProperty(
+ usbDevice,
+ CFSTR(kUSBSerialNumberString),
+ kCFAllocatorDefault, 0));
+ QString uid = CFStringRef2QString(cfUid);
+ CFRelease(cfUid);
+ IosDeviceManager::instance()->deviceConnected(uid, name);
+
+ // Done with this USB device; release the reference added by IOIteratorNext
+ kr = IOObjectRelease(usbDevice);
+ }
+}
+
+void deviceDisconnectedCallback(void *refCon, io_iterator_t iterator)
+{
+ kern_return_t kr;
+ io_service_t usbDevice;
+ (void) refCon;
+
+ while ((usbDevice = IOIteratorNext(iterator))) {
+ io_name_t deviceName;
+
+ // Get the USB device's name.
+ kr = IORegistryEntryGetName(usbDevice, deviceName);
+ if (KERN_SUCCESS != kr)
+ deviceName[0] = '\0';
+ if (debugDeviceDetection)
+ qDebug() << "ios device " << deviceName << " in deviceDisconnectedCallback";
+
+ {
+ CFStringRef cfUid = static_cast<CFStringRef>(IORegistryEntryCreateCFProperty(
+ usbDevice,
+ CFSTR(kUSBSerialNumberString),
+ kCFAllocatorDefault, 0));
+ QString uid = CFStringRef2QString(cfUid);
+ CFRelease(cfUid);
+ IosDeviceManager::instance()->deviceDisconnected(uid);
+ }
+
+ // Done with this USB device; release the reference added by IOIteratorNext
+ kr = IOObjectRelease(usbDevice);
+ }
+}
+
+} // extern C
+
+} // anonymous namespace
+
+void IosDeviceManager::monitorAvailableDevices()
+{
+ CFMutableDictionaryRef matchingDictionary =
+ IOServiceMatching("IOUSBDevice" );
+ {
+ UInt32 vendorId = 0x05ac;
+ CFNumberRef cfVendorValue = CFNumberCreate( kCFAllocatorDefault, kCFNumberSInt32Type,
+ &vendorId );
+ CFDictionaryAddValue( matchingDictionary, CFSTR( kUSBVendorID ), cfVendorValue);
+ CFRelease( cfVendorValue );
+ UInt32 productId = 0x1280;
+ CFNumberRef cfProductIdValue = CFNumberCreate( kCFAllocatorDefault, kCFNumberSInt32Type,
+ &productId );
+ CFDictionaryAddValue( matchingDictionary, CFSTR( kUSBProductID ), cfProductIdValue);
+ CFRelease( cfProductIdValue );
+ UInt32 productIdMask = 0xFFC0;
+ CFNumberRef cfProductIdMaskValue = CFNumberCreate( kCFAllocatorDefault, kCFNumberSInt32Type,
+ &productIdMask );
+ CFDictionaryAddValue( matchingDictionary, CFSTR( kUSBProductIDMask ), cfProductIdMaskValue);
+ CFRelease( cfProductIdMaskValue );
+ }
+
+ IONotificationPortRef notificationPort = IONotificationPortCreate(kIOMasterPortDefault);
+ CFRunLoopSourceRef runLoopSource = IONotificationPortGetRunLoopSource(notificationPort);
+
+ CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSource, kCFRunLoopDefaultMode);
+
+ // IOServiceAddMatchingNotification releases this, so we retain for the next call
+ CFRetain(matchingDictionary);
+
+ // Now set up a notification to be called when a device is first matched by I/O Kit.
+ kern_return_t kr;
+ kr = IOServiceAddMatchingNotification(notificationPort,
+ kIOMatchedNotification,
+ matchingDictionary,
+ deviceConnectedCallback,
+ NULL,
+ &gAddedIter);
+
+
+ kr = IOServiceAddMatchingNotification(notificationPort,
+ kIOTerminatedNotification,
+ matchingDictionary,
+ deviceDisconnectedCallback,
+ NULL,
+ &gRemovedIter);
+
+ // Iterate once to get already-present devices and arm the notification
+ deviceConnectedCallback(NULL, gAddedIter);
+ deviceDisconnectedCallback(NULL, gRemovedIter);
+
+}
+
+
+IosDeviceManager::IosDeviceManager(QObject *parent) :
+ QObject(parent)
+{
+}
+
+IosDeviceManager *IosDeviceManager::instance()
+{
+ static IosDeviceManager obj;
+ return &obj;
+}
+
+void IosDeviceManager::updateAvailableDevices(const QStringList &devices)
+{
+ foreach (const QString &uid, devices)
+ deviceConnected(uid);
+
+ DeviceManager *devManager = DeviceManager::instance();
+ for (int iDevice = 0; iDevice < devManager->deviceCount(); ++iDevice) {
+ IDevice::ConstPtr dev = devManager->deviceAt(iDevice);
+ Core::Id devType(Constants::IOS_DEVICE_TYPE);
+ if (dev.isNull() || dev->type() != devType)
+ continue;
+ const IosDevice *iosDev = static_cast<const IosDevice *>(dev.data());
+ if (devices.contains(iosDev->uniqueDeviceID()))
+ continue;
+ if (iosDev->deviceState() != IDevice::DeviceDisconnected) {
+ if (debugDeviceDetection)
+ qDebug() << "disconnecting device " << iosDev->uniqueDeviceID();
+ devManager->setDeviceState(iosDev->id(), IDevice::DeviceDisconnected);
+ }
+ }
+}
+
+IosDevice::ConstPtr IosKitInformation::device(Kit *kit)
+{
+ if (!kit)
+ return IosDevice::ConstPtr();
+ ProjectExplorer::IDevice::ConstPtr dev = ProjectExplorer::DeviceKitInformation::device(kit);
+ IosDevice::ConstPtr res = dev.dynamicCast<const IosDevice>();
+ return res;
+}
+
+} // namespace Internal
+} // namespace Ios
diff --git a/src/plugins/ios/iosdevice.h b/src/plugins/ios/iosdevice.h
new file mode 100644
index 0000000000..f8801ffbf5
--- /dev/null
+++ b/src/plugins/ios/iosdevice.h
@@ -0,0 +1,111 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** 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.
+**
+****************************************************************************/
+#ifndef IOSDEVICE_H
+#define IOSDEVICE_H
+
+#include <projectexplorer/devicesupport/idevice.h>
+#include <QVariantMap>
+#include <QMap>
+#include <QString>
+#include "iostoolhandler.h"
+#include <QSharedPointer>
+
+namespace ProjectExplorer{
+class Kit;
+}
+namespace Ios {
+class IosConfigurations;
+
+namespace Internal {
+class IosDeviceManager;
+
+class IosDevice : public ProjectExplorer::IDevice
+{
+public:
+ typedef QMap<QString, QString> Dict;
+ typedef QSharedPointer<const IosDevice> ConstPtr;
+ typedef QSharedPointer<IosDevice> Ptr;
+
+ ProjectExplorer::IDevice::DeviceInfo deviceInformation() const;
+ ProjectExplorer::IDeviceWidget *createWidget();
+ QList<Core::Id> actionIds() const;
+ QString displayNameForActionId(Core::Id actionId) const;
+ void executeAction(Core::Id actionId, QWidget *parent = 0);
+ ProjectExplorer::DeviceProcessSignalOperation::Ptr signalOperation() const;
+ QString displayType() const;
+
+ ProjectExplorer::IDevice::Ptr clone() const;
+ void fromMap(const QVariantMap &map);
+ QVariantMap toMap() const;
+ QString uniqueDeviceID() const;
+ IosDevice(const QString &uid);
+ // add back? currently unused...
+ //QString cpuArchitecure() const;
+ //QString productType() const;
+protected:
+ friend class IosDeviceFactory;
+ friend class Ios::Internal::IosDeviceManager;
+ IosDevice();
+ IosDevice(const IosDevice &other);
+ Dict m_extraInfo;
+ bool m_ignoreDevice;
+};
+
+class IosDeviceManager : public QObject {
+ Q_OBJECT
+public:
+ typedef QHash<QString, QString> TranslationMap;
+
+ static TranslationMap translationMap();
+ static IosDeviceManager *instance();
+
+ void updateAvailableDevices(const QStringList &devices);
+ void deviceConnected(const QString &uid, const QString &name = QString());
+ void deviceDisconnected(const QString &uid);
+ friend class IosConfigurations;
+public slots:
+ void updateInfo(const QString &devId);
+//private slots:
+ void deviceInfo(Ios::IosToolHandler *gatherer, const QString &deviceId,
+ const Ios::IosToolHandler::Dict &info);
+ void infoGathererFinished(Ios::IosToolHandler *gatherer);
+ void monitorAvailableDevices();
+private:
+ IosDeviceManager(QObject *parent = 0);
+};
+
+namespace IosKitInformation {
+IosDevice::ConstPtr device(ProjectExplorer::Kit *);
+}
+
+} // namespace Internal
+
+} // namespace Ios
+
+#endif // IOSDEVICE_H
diff --git a/src/plugins/ios/iosdevicefactory.cpp b/src/plugins/ios/iosdevicefactory.cpp
new file mode 100644
index 0000000000..980e8ea412
--- /dev/null
+++ b/src/plugins/ios/iosdevicefactory.cpp
@@ -0,0 +1,81 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** 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 "iosdevicefactory.h"
+#include "iosdevice.h"
+
+#include "iosconstants.h"
+
+namespace Ios {
+namespace Internal {
+
+IosDeviceFactory::IosDeviceFactory()
+{
+ setObjectName(QLatin1String("IosDeviceFactory"));
+}
+
+QString IosDeviceFactory::displayNameForId(Core::Id type) const
+{
+ if (type == Constants::IOS_DEVICE_TYPE)
+ return tr("iOS Device");
+ return QString();
+}
+
+QList<Core::Id> IosDeviceFactory::availableCreationIds() const
+{
+ return QList<Core::Id>() << Core::Id(Constants::IOS_DEVICE_TYPE);
+}
+
+bool IosDeviceFactory::canCreate() const
+{
+ return false;
+}
+
+ProjectExplorer::IDevice::Ptr IosDeviceFactory::create(Core::Id id) const
+{
+ Q_UNUSED(id)
+ return ProjectExplorer::IDevice::Ptr();
+}
+
+bool IosDeviceFactory::canRestore(const QVariantMap &map) const
+{
+ return ProjectExplorer::IDevice::typeFromMap(map) == Constants::IOS_DEVICE_TYPE;
+}
+
+ProjectExplorer::IDevice::Ptr IosDeviceFactory::restore(const QVariantMap &map) const
+{
+ IosDevice *newDev = new IosDevice;
+ newDev->fromMap(map);
+ // updating the active ones should be enough...
+ //IosDeviceManager::instance()->updateInfo(newDev->uniqueDeviceID());
+ return ProjectExplorer::IDevice::Ptr(newDev);
+}
+
+} // namespace Internal
+} // namespace Ios
diff --git a/src/plugins/ios/iosdevicefactory.h b/src/plugins/ios/iosdevicefactory.h
new file mode 100644
index 0000000000..c77ecc7483
--- /dev/null
+++ b/src/plugins/ios/iosdevicefactory.h
@@ -0,0 +1,58 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** 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.
+**
+****************************************************************************/
+#ifndef IOSDEVICEFACTORY_H
+#define IOSDEVICEFACTORY_H
+
+#include <projectexplorer/devicesupport/idevicefactory.h>
+#include <QTimer>
+#include <QMap>
+#include <QString>
+
+namespace Ios {
+namespace Internal {
+
+class IosDeviceFactory : public ProjectExplorer::IDeviceFactory
+{
+ Q_OBJECT
+public:
+ IosDeviceFactory();
+
+ QString displayNameForId(Core::Id type) const;
+ QList<Core::Id> availableCreationIds() const;
+
+ bool canCreate() const;
+ ProjectExplorer::IDevice::Ptr create(Core::Id id) const;
+ bool canRestore(const QVariantMap &map) const;
+ ProjectExplorer::IDevice::Ptr restore(const QVariantMap &map) const;
+};
+
+} // namespace Internal
+} // namespace Ios
+
+#endif // IOSDEVICEFACTORY_H
diff --git a/src/plugins/ios/iosmanager.cpp b/src/plugins/ios/iosmanager.cpp
new file mode 100644
index 0000000000..4f25cba3b9
--- /dev/null
+++ b/src/plugins/ios/iosmanager.cpp
@@ -0,0 +1,76 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** 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 "iosmanager.h"
+#include "iosconfigurations.h"
+#include "iosrunconfiguration.h"
+
+#include <projectexplorer/projectexplorer.h>
+#include <projectexplorer/session.h>
+#include <projectexplorer/target.h>
+#include <projectexplorer/toolchain.h>
+#include <qt4projectmanager/qt4nodes.h>
+#include <qt4projectmanager/qt4project.h>
+#include <qt4projectmanager/qt4projectmanagerconstants.h>
+#include <qt4projectmanager/qt4buildconfiguration.h>
+#include <qtsupport/customexecutablerunconfiguration.h>
+#include <qtsupport/qtkitinformation.h>
+#include <qtsupport/qtsupportconstants.h>
+
+#include <QDir>
+#include <QFileSystemWatcher>
+#include <QList>
+#include <QProcess>
+#include <QMessageBox>
+#include <QApplication>
+#include <QDomDocument>
+
+using namespace Qt4ProjectManager;
+using namespace ProjectExplorer;
+
+namespace Ios {
+namespace Internal {
+
+bool IosManager::supportsIos(ProjectExplorer::Target *target)
+{
+ if (!qobject_cast<Qt4ProjectManager::Qt4Project *>(target->project()))
+ return false;
+ QtSupport::BaseQtVersion *version = QtSupport::QtKitInformation::qtVersion(target->kit());
+ return version && version->type() == QLatin1String(Ios::Constants::IOSQT);
+}
+
+QString IosManager::resDirForTarget(Target *target)
+{
+ Qt4BuildConfiguration *bc =
+ qobject_cast<Qt4BuildConfiguration *>(target->activeBuildConfiguration());
+ return bc->buildDirectory().toString();
+}
+
+} // namespace Internal
+} // namespace Qt4ProjectManager
diff --git a/src/plugins/ios/iosmanager.h b/src/plugins/ios/iosmanager.h
new file mode 100644
index 0000000000..83f1c58a02
--- /dev/null
+++ b/src/plugins/ios/iosmanager.h
@@ -0,0 +1,55 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** 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.
+**
+****************************************************************************/
+#ifndef IOSMANAGER_H
+#define IOSMANAGER_H
+
+#include <utils/fileutils.h>
+
+#include <QDomDocument>
+#include <QObject>
+#include <QStringList>
+
+namespace ProjectExplorer { class Target; }
+
+namespace Ios {
+namespace Internal {
+
+class IosManager : public QObject
+{
+ Q_OBJECT
+
+public:
+ static bool supportsIos(ProjectExplorer::Target *target);
+ static QString resDirForTarget(ProjectExplorer::Target *target);
+};
+
+} // namespace Internal
+} // namespace Ios
+
+#endif // IOSMANAGER_H
diff --git a/src/plugins/ios/iosplugin.cpp b/src/plugins/ios/iosplugin.cpp
new file mode 100644
index 0000000000..c4eb232575
--- /dev/null
+++ b/src/plugins/ios/iosplugin.cpp
@@ -0,0 +1,96 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** 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 "iosplugin.h"
+
+#include "iosconstants.h"
+#include "iosconfigurations.h"
+#include "iosdevicefactory.h"
+#include "iossimulator.h"
+#include "iossimulatorfactory.h"
+#include "iosconfigurations.h"
+#include "iosmanager.h"
+#include "iosrunfactories.h"
+#include "iossettingspage.h"
+#include "iosqtversionfactory.h"
+#include "iosbuildstep.h"
+#include "iosdeploystepfactory.h"
+#include "iosdeployconfiguration.h"
+#include <projectexplorer/kitmanager.h>
+#include <qtsupport/qtversionmanager.h>
+
+#include <QtPlugin>
+
+#include <projectexplorer/devicesupport/devicemanager.h>
+
+namespace Ios {
+
+IosPlugin::IosPlugin()
+{ }
+
+bool IosPlugin::initialize(const QStringList &arguments, QString *errorMessage)
+{
+ Q_UNUSED(arguments);
+ Q_UNUSED(errorMessage);
+
+ Internal::IosConfigurations::instance();
+
+ addAutoReleasedObject(new Internal::IosRunControlFactory);
+ addAutoReleasedObject(new Internal::IosRunConfigurationFactory);
+ addAutoReleasedObject(new Internal::IosSettingsPage);
+ addAutoReleasedObject(new Internal::IosQtVersionFactory);
+ addAutoReleasedObject(new Internal::IosDeviceFactory);
+ addAutoReleasedObject(new Internal::IosSimulatorFactory);
+ addAutoReleasedObject(new Internal::IosBuildStepFactory);
+ addAutoReleasedObject(new Internal::IosDeployStepFactory);
+ addAutoReleasedObject(new Internal::IosDeployConfigurationFactory);
+ //ProjectExplorer::KitManager::instance()->registerKitInformation(new Internal::IosGdbServerKitInformation);
+ return true;
+}
+
+void IosPlugin::extensionsInitialized()
+{
+ connect(ProjectExplorer::KitManager::instance(), SIGNAL(kitsLoaded()),
+ this, SLOT(kitsRestored()));
+}
+
+void IosPlugin::kitsRestored()
+{
+ disconnect(ProjectExplorer::KitManager::instance(), SIGNAL(kitsLoaded()),
+ this, SLOT(kitsRestored()));
+ Internal::IosConfigurations::instance().updateAutomaticKitList();
+ connect(QtSupport::QtVersionManager::instance(),
+ SIGNAL(qtVersionsChanged(QList<int>,QList<int>,QList<int>)),
+ &Internal::IosConfigurations::instance(),
+ SLOT(updateAutomaticKitList()));
+}
+
+} // namespace Ios
+
+Q_EXPORT_PLUGIN(Ios::IosPlugin)
diff --git a/src/plugins/ios/iosplugin.h b/src/plugins/ios/iosplugin.h
new file mode 100644
index 0000000000..832343919e
--- /dev/null
+++ b/src/plugins/ios/iosplugin.h
@@ -0,0 +1,52 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** 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.
+**
+****************************************************************************/
+#ifndef IOSPLUGIN_H
+#define IOSPLUGIN_H
+
+#include <extensionsystem/iplugin.h>
+
+namespace Ios {
+
+class IosPlugin : public ExtensionSystem::IPlugin
+{
+ Q_OBJECT
+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "Ios.json")
+
+public:
+ IosPlugin();
+
+ bool initialize(const QStringList &arguments, QString *errorMessage);
+ void extensionsInitialized();
+private slots:
+ void kitsRestored();
+};
+
+} // namespace Ios
+
+#endif // IOSPLUGIN_H
diff --git a/src/plugins/ios/iosprobe.cpp b/src/plugins/ios/iosprobe.cpp
new file mode 100644
index 0000000000..318220cb8d
--- /dev/null
+++ b/src/plugins/ios/iosprobe.cpp
@@ -0,0 +1,355 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** 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 "iosprobe.h"
+#include <QDebug>
+#include <QFileInfo>
+#include <QProcess>
+#include <QDir>
+#include <QFileInfoList>
+
+static const bool debugProbe = false;
+
+namespace Ios {
+
+static QString qsystem(const QString &exe, const QStringList &args = QStringList())
+{
+ QProcess p;
+ p.setProcessChannelMode(QProcess::MergedChannels);
+ p.start(exe, args);
+ p.waitForFinished();
+ return QString::fromLocal8Bit(p.readAll());
+}
+
+QMap<QString, Platform> IosProbe::detectPlatforms(const QString &devPath)
+{
+ IosProbe probe;
+ probe.addDeveloperPath(devPath);
+ probe.detectAll();
+ return probe.detectedPlatforms();
+}
+
+int IosProbe::compareVersions(const QString &v1, const QString &v2)
+{
+ QStringList v1L = v1.split(QLatin1Char('.'));
+ QStringList v2L = v2.split(QLatin1Char('.'));
+ int i = 0;
+ while (v1.length() > i && v1.length() > i) {
+ bool n1Ok, n2Ok;
+ int n1 = v1L.value(i).toInt(&n1Ok);
+ int n2 = v2L.value(i).toInt(&n2Ok);
+ if (!(n1Ok && n2Ok)) {
+ qDebug() << QString::fromLatin1("Failed to compare version %1 and %2").arg(v1,v2);
+ return 0;
+ }
+ if (n1 > n2)
+ return -1;
+ else if (n1 < n2)
+ return 1;
+ ++i;
+ }
+ if (v1.length() > v2.length())
+ return -1;
+ if (v1.length() < v2.length())
+ return 1;
+ return 0;
+}
+
+bool IosProbe::addDeveloperPath(const QString &path)
+{
+ if (path.isEmpty())
+ return false;
+ QFileInfo pInfo(path);
+ if (!pInfo.exists() || !pInfo.isDir())
+ return false;
+ if (m_developerPaths.contains(path))
+ return false;
+ m_developerPaths.append(path);
+ if (debugProbe)
+ qDebug() << QString::fromLatin1("Added developer path %1").arg(path);
+ return true;
+}
+
+void IosProbe::detectDeveloperPaths()
+{
+ QProcess selectedXcode;
+ QString program = QLatin1String("/usr/bin/xcode-select");
+ QStringList arguments(QLatin1String("--print-path"));
+ selectedXcode.start(program, arguments, QProcess::ReadOnly);
+ if (!selectedXcode.waitForFinished() || selectedXcode.exitCode()) {
+ qDebug() << QString::fromLatin1("Could not detect selected xcode with /usr/bin/xcode-select");
+ } else {
+ QString path = QString::fromLocal8Bit(selectedXcode.readAllStandardOutput());
+ addDeveloperPath(path);
+ }
+ addDeveloperPath(QLatin1String("/Applications/Xcode.app/Contents/Developer"));
+}
+
+void IosProbe::setArch(Platform *platform, const QString &pathToGcc, const QStringList &extraFlags)
+{
+ if (!extraFlags.isEmpty())
+ platform->backendFlags = extraFlags;
+ // setting architecture and endianness only here, bercause the same compiler
+ // can support several ones
+ QStringList flags(extraFlags);
+ flags << QLatin1String("-dumpmachine");
+ QString compilerTriplet = qsystem(pathToGcc, flags).simplified();
+ QStringList compilerTripletl = compilerTriplet.split(QLatin1Char('-'));
+ if (compilerTripletl.count() < 2) {
+ qDebug() << QString::fromLatin1("Detected '%1', but I don't understand "
+ "its architecture '%2'.")
+ .arg(pathToGcc, compilerTriplet);
+ return;
+ }
+
+ QString endianness, architecture;
+ architecture = compilerTripletl.at(0);
+ endianness = QLatin1String("little");
+
+ if (debugProbe)
+ qDebug() << QString::fromLatin1(" Toolchain %1 detected:\n"
+ " binary: %2\n"
+ " triplet: %3\n"
+ " arch: %4").arg(platform->name, pathToGcc,
+ compilerTriplet, architecture);
+
+ platform->architecture = architecture;
+}
+
+void IosProbe::setupDefaultToolchains(const QString &devPath, const QString &xCodeName)
+{
+ if (debugProbe)
+ qDebug() << QString::fromLatin1("Setting up platform '%1'.").arg(xCodeName);
+ QString indent = QLatin1String(" ");
+
+ // detect clang (default toolchain)
+ QFileInfo clangFileInfo(devPath
+ + QLatin1String("/Toolchains/XcodeDefault.xctoolchain/usr/bin")
+ + QLatin1String("/clang++"));
+ bool hasClang = clangFileInfo.exists();
+ QSettingsPtr toolchainInfo;
+ if (hasClang)
+ toolchainInfo = QSettingsPtr(new QSettings(
+ devPath + QLatin1String("/Toolchains/XcodeDefault.xctoolchain/ToolchainInfo.plist")
+ , QSettings::NativeFormat));
+ else
+ qDebug() << indent << QString::fromLatin1("Default toolchain %1 not found.")
+ .arg(clangFileInfo.canonicalFilePath());
+ // Platforms
+ QDir platformsDir(devPath + QLatin1String("/Platforms"));
+ QFileInfoList platforms = platformsDir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot);
+ foreach (const QFileInfo &fInfo, platforms) {
+ if (fInfo.isDir() && fInfo.suffix() == QLatin1String("platform")) {
+ if (debugProbe)
+ qDebug() << indent << QString::fromLatin1("Setting up %1").arg(fInfo.fileName());
+ QSettingsPtr infoSettings(new QSettings(
+ fInfo.absoluteFilePath() + QLatin1String("/Info.plist")
+ , QSettings::NativeFormat));
+ if (!infoSettings->contains(QLatin1String("Name"))) {
+ qDebug() << indent << QString::fromLatin1("Missing platform name in Info.plist of %1")
+ .arg(fInfo.absoluteFilePath());
+ continue;
+ }
+ QString targetOS;
+ QString name = infoSettings->value(QLatin1String("Name")).toString();
+ if (name != QLatin1String("macosx") && name != QLatin1String("iphoneos")
+ && name != QLatin1String("iphonesimulator"))
+ {
+ qDebug() << indent << QString::fromLatin1("Skipping unknown platform %1").arg(name);
+ continue;
+ }
+
+ // prepare default platform properties
+ QVariantMap defaultProp = infoSettings->value(QLatin1String("DefaultProperties"))
+ .toMap();
+ QVariantMap overrideProp = infoSettings->value(QLatin1String("OverrideProperties"))
+ .toMap();
+ QMapIterator<QString, QVariant> i(overrideProp);
+ while (i.hasNext()) {
+ i.next();
+ // use unite? might lead to double insertions...
+ defaultProp[i.key()] = i.value();
+ }
+
+ QString clangFullName = name + QLatin1String("-clang") + xCodeName;
+ QString clang11FullName = name + QLatin1String("-clang11") + xCodeName;
+ // detect gcc
+ QFileInfo gccFileInfo(fInfo.absoluteFilePath() + QLatin1String("/Developer/usr/bin/g++"));
+ QString gccFullName = name + QLatin1String("-gcc") + xCodeName;
+ if (!gccFileInfo.exists())
+ gccFileInfo = QFileInfo(devPath + QLatin1String("/usr/bin/g++"));
+ bool hasGcc = gccFileInfo.exists();
+
+ QStringList extraFlags;
+ if (defaultProp.contains(QLatin1String("ARCHS"))) {
+ QString arch = defaultProp.value(QLatin1String("ARCHS")).toString();
+ if (arch == QLatin1String("$(NATIVE_ARCH_32_BIT)"))
+ extraFlags << QLatin1String("-arch") << QLatin1String("i386");
+ }
+ if (defaultProp.contains(QLatin1String("NATIVE_ARCH"))) {
+ QString arch = defaultProp.value(QLatin1String("NATIVE_ARCH")).toString();
+ if (!arch.startsWith(QLatin1String("arm")))
+ qDebug() << indent << QString::fromLatin1("Expected arm architecture, not %1").arg(arch);
+ extraFlags << QLatin1String("-arch") << arch;
+ }
+ if (hasClang) {
+ Platform clangProfile;
+ clangProfile.platformKind = 0;
+ clangProfile.name = clangFullName;
+ clangProfile.platformPath = Utils::FileName(fInfo);
+ clangProfile.platformInfo = infoSettings;
+ clangProfile.compilerPath = Utils::FileName(clangFileInfo);
+ setArch(&clangProfile, clangFileInfo.canonicalFilePath(), extraFlags);
+ if (debugProbe)
+ qDebug() << indent << QString::fromLatin1("* adding profile %1").arg(clangProfile.name);
+ this->m_platforms[clangProfile.name] = clangProfile;
+ clangProfile.platformKind |= Platform::Cxx11Support;
+ clangProfile.backendFlags.append(QLatin1String("-std=c++11"));
+ clangProfile.backendFlags.append(QLatin1String("-stdlib=libc++"));
+ clangProfile.name = clang11FullName;
+ this->m_platforms[clangProfile.name] = clangProfile;
+ }
+ if (hasGcc) {
+ Platform gccProfile;
+ gccProfile.name = gccFullName;
+ gccProfile.platformKind = 0;
+ // use the arm-apple-darwin10-llvm-* variant and avoid the extraFlags if available???
+ gccProfile.platformPath = Utils::FileName(fInfo);
+ gccProfile.platformInfo = infoSettings;
+ gccProfile.compilerPath = Utils::FileName(gccFileInfo);
+ setArch(&gccProfile, gccFileInfo.canonicalFilePath(), extraFlags);
+ if (debugProbe)
+ qDebug() << indent << QString::fromLatin1("* adding profile %1").arg(gccProfile.name);
+ this->m_platforms[gccProfile.name] = gccProfile;
+ }
+
+ // set SDKs/sysroot
+ QString sysRoot;
+ QSettingsPtr sdkSettings;
+ {
+ QString sdkName;
+ if (defaultProp.contains(QLatin1String("SDKROOT")))
+ sdkName = defaultProp.value(QLatin1String("SDKROOT")).toString();
+ QString sdkPath;
+ QDir sdks(fInfo.absoluteFilePath() + QLatin1String("/Developer/SDKs"));
+ QString maxVersion;
+ foreach (const QFileInfo &sdkDirInfo, sdks.entryInfoList(QDir::Dirs
+ | QDir::NoDotAndDotDot)) {
+ indent = QLatin1String(" ");
+ QSettingsPtr sdkInfo(new QSettings(sdkDirInfo.absoluteFilePath()
+ + QLatin1String("/SDKSettings.plist")
+ , QSettings::NativeFormat));
+ QString versionStr = sdkInfo->value(QLatin1String("Version")).toString();
+ QVariant currentSdkName = sdkInfo->value(QLatin1String("CanonicalName"));
+ bool isBaseSdk = sdkInfo->value((QLatin1String("isBaseSDK"))).toString()
+ .toLower() != QLatin1String("no");
+ if (!isBaseSdk) {
+ if (debugProbe)
+ qDebug() << indent << QString::fromLatin1("Skipping non base Sdk %1")
+ .arg(currentSdkName.toString());
+ continue;
+ }
+ QString safeName = currentSdkName.toString().replace(QLatin1Char('-'), QLatin1Char('_'))
+ .replace(QRegExp(QLatin1String("[^-a-zA-Z0-9]")),QLatin1String("-"));
+ if (sdkName.isEmpty()) {
+ if (compareVersions(maxVersion,versionStr) > 0) {
+ maxVersion = versionStr;
+ sdkPath = sdkDirInfo.canonicalFilePath();
+ sdkSettings = sdkInfo;
+ }
+ } else if (currentSdkName == sdkName) {
+ sdkPath = sdkDirInfo.canonicalFilePath();
+ sdkSettings = sdkInfo;
+ }
+ if (hasClang){
+ Platform pSdk;
+ pSdk = this->m_platforms[clangFullName];
+ pSdk.name = safeName + QLatin1String("-clang") + xCodeName;
+ pSdk.sdkPath = Utils::FileName(sdkDirInfo);
+ pSdk.sdkSettings = sdkInfo;
+ if (debugProbe)
+ qDebug() << indent << QString::fromLatin1("* adding profile %1").arg(pSdk.name);
+ this->m_platforms[pSdk.name] = pSdk;
+ pSdk.backendFlags.append(QLatin1String("-std=c++11"));
+ pSdk.backendFlags.append(QLatin1String("-stdlib=libc++"));
+ pSdk.name = safeName + QLatin1String("-clang11") + xCodeName;
+ this->m_platforms[pSdk.name] = pSdk;
+ }
+ if (hasGcc) {
+ Platform pSdk;
+ pSdk = this->m_platforms[gccFullName];
+ pSdk.name = safeName + QLatin1String("-gcc") + xCodeName;
+ pSdk.sdkPath = Utils::FileName(sdkDirInfo);
+ pSdk.sdkSettings = sdkInfo;
+ if (debugProbe)
+ qDebug() << indent << QString::fromLatin1("* adding profile %1").arg(pSdk.name);
+ this->m_platforms[pSdk.name] = pSdk;
+ }
+ }
+ if (!sdkPath.isEmpty())
+ sysRoot = sdkPath;
+ else if (!sdkName.isEmpty())
+ qDebug() << indent << QString::fromLatin1("Failed to find sysroot %1").arg(sdkName);
+ }
+ //this->m_platforms.remove(clangFullName);
+ if (hasClang && !sysRoot.isEmpty()) {
+ this->m_platforms[clangFullName].platformKind |= Platform::BasePlatform;
+ this->m_platforms[clangFullName].sdkPath = Utils::FileName::fromString(sysRoot);
+ this->m_platforms[clangFullName].sdkSettings = sdkSettings;
+ this->m_platforms[clang11FullName].platformKind |= Platform::BasePlatform;
+ this->m_platforms[clang11FullName].sdkPath = Utils::FileName::fromString(sysRoot);
+ this->m_platforms[clang11FullName].sdkSettings = sdkSettings;
+ }
+ //this->m_platforms.remove(gccFullName);
+ if (hasGcc && !sysRoot.isEmpty()) {
+ this->m_platforms[gccFullName].platformKind |= Platform::BasePlatform;
+ this->m_platforms[gccFullName].sdkPath = Utils::FileName::fromString(sysRoot);
+ this->m_platforms[gccFullName].sdkSettings = sdkSettings;
+ }
+ }
+ indent = QLatin1String(" ");
+ }
+}
+
+void IosProbe::detectAll()
+{
+ detectDeveloperPaths();
+ QString xcodeName = QLatin1String("");
+ for (int iXcode = 0; iXcode < m_developerPaths.count(); ++iXcode) {
+ setupDefaultToolchains(m_developerPaths.value(iXcode), xcodeName);
+ xcodeName = QString::fromLatin1("-%1").arg(iXcode + 2);
+ }
+}
+
+QMap<QString, Platform> IosProbe::detectedPlatforms()
+{
+ return m_platforms;
+}
+
+}
diff --git a/src/plugins/ios/iosprobe.h b/src/plugins/ios/iosprobe.h
new file mode 100644
index 0000000000..cc481881b5
--- /dev/null
+++ b/src/plugins/ios/iosprobe.h
@@ -0,0 +1,84 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** 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.
+**
+****************************************************************************/
+#ifndef IOSPROBE_H
+#define IOSPROBE_H
+#include <QSettings>
+#include <QSharedPointer>
+#include <QString>
+#include <QStringList>
+#include <utils/fileutils.h>
+
+namespace Ios {
+
+typedef QSharedPointer<QSettings> QSettingsPtr;
+
+class Platform
+{
+public:
+ enum PlatformKind {
+ BasePlatform = 1 << 0,
+ Cxx11Support = 1 << 1
+ };
+
+ quint32 platformKind;
+ QString name;
+ Utils::FileName developerPath;
+ Utils::FileName platformPath;
+ Utils::FileName sdkPath;
+ Utils::FileName defaultToolchainPath;
+ Utils::FileName compilerPath;
+ QString architecture;
+ QStringList backendFlags;
+ QSettingsPtr platformInfo;
+ QSettingsPtr sdkSettings;
+ QSettingsPtr toolchainInfo;
+};
+
+class IosProbe
+{
+public:
+ static QMap<QString, Platform> detectPlatforms(const QString &devPath);
+ IosProbe()
+ { }
+
+private:
+ static int compareVersions(const QString &v1, const QString &v2);
+ bool addDeveloperPath(const QString &path);
+ void detectDeveloperPaths();
+ void setArch(Platform *platform, const QString &pathToGcc, const QStringList &extraFlags);
+ void setupDefaultToolchains(const QString &devPath, const QString &xCodeName);
+ void detectAll();
+ QMap<QString, Platform> detectedPlatforms();
+private:
+ QMap<QString, Platform> m_platforms;
+ QStringList m_developerPaths;
+};
+} // namespace Ios
+
+#endif // IOSPROBE_H
diff --git a/src/plugins/ios/iosqtversion.cpp b/src/plugins/ios/iosqtversion.cpp
new file mode 100644
index 0000000000..881cc41567
--- /dev/null
+++ b/src/plugins/ios/iosqtversion.cpp
@@ -0,0 +1,131 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** 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 "iosqtversion.h"
+#include "iosconstants.h"
+#include "iosconfigurations.h"
+#include "iosmanager.h"
+
+#include <utils/environment.h>
+#include <utils/hostosinfo.h>
+
+#include <qt4projectmanager/qt4project.h>
+#include <qt4projectmanager/qt4projectmanagerconstants.h>
+
+#include <qtsupport/qtkitinformation.h>
+#include <qtsupport/qtsupportconstants.h>
+#include <qtsupport/qtversionmanager.h>
+
+#include <projectexplorer/kit.h>
+#include <projectexplorer/projectexplorer.h>
+
+using namespace Ios::Internal;
+using namespace ProjectExplorer;
+using namespace Qt4ProjectManager;
+
+IosQtVersion::IosQtVersion()
+ : QtSupport::BaseQtVersion()
+{
+}
+
+IosQtVersion::IosQtVersion(const Utils::FileName &path, bool isAutodetected,
+ const QString &autodetectionSource)
+ : QtSupport::BaseQtVersion(path, isAutodetected, autodetectionSource)
+{
+ setDisplayName(defaultDisplayName(qtVersionString(), path, false));
+}
+
+IosQtVersion *IosQtVersion::clone() const
+{
+ return new IosQtVersion(*this);
+}
+
+QString IosQtVersion::type() const
+{
+ return QLatin1String(Constants::IOSQT);
+}
+
+bool IosQtVersion::isValid() const
+{
+ if (!BaseQtVersion::isValid())
+ return false;
+ if (qtAbis().isEmpty())
+ return false;
+ return true;
+}
+
+QString IosQtVersion::invalidReason() const
+{
+ QString tmp = BaseQtVersion::invalidReason();
+ if (tmp.isEmpty() && qtAbis().isEmpty())
+ return tr("Failed to detect the ABIs used by the Qt version.");
+ return tmp;
+}
+
+QList<ProjectExplorer::Abi> IosQtVersion::detectQtAbis() const
+{
+ QList<ProjectExplorer::Abi> abis = qtAbisFromLibrary(qtCorePath(versionInfo(), qtVersionString()));
+ for (int i = 0; i < abis.count(); ++i) {
+ abis[i] = Abi(abis.at(i).architecture(),
+ abis.at(i).os(),
+ ProjectExplorer::Abi::GenericMacFlavor,
+ abis.at(i).binaryFormat(),
+ abis.at(i).wordWidth());
+ }
+ return abis;
+}
+
+void IosQtVersion::addToEnvironment(const ProjectExplorer::Kit *k, Utils::Environment &env) const
+{
+ Q_UNUSED(k);
+ Q_UNUSED(env);
+}
+
+QString IosQtVersion::description() const
+{
+ //: Qt Version is meant for Ios
+ return tr("iOS");
+}
+
+Core::FeatureSet IosQtVersion::availableFeatures() const
+{
+ Core::FeatureSet features = QtSupport::BaseQtVersion::availableFeatures();
+ features |= Core::FeatureSet(QtSupport::Constants::FEATURE_MOBILE);
+ return features;
+}
+
+QString IosQtVersion::platformName() const
+{
+ return QLatin1String(QtSupport::Constants::IOS_PLATFORM);
+}
+
+QString IosQtVersion::platformDisplayName() const
+{
+ return QLatin1String(QtSupport::Constants::IOS_PLATFORM_TR);
+}
diff --git a/src/plugins/ios/iosqtversion.h b/src/plugins/ios/iosqtversion.h
new file mode 100644
index 0000000000..7c720a0590
--- /dev/null
+++ b/src/plugins/ios/iosqtversion.h
@@ -0,0 +1,67 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** 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.
+**
+****************************************************************************/
+#ifndef IOSQTVERSION_H
+#define IOSQTVERSION_H
+
+#include <qtsupport/baseqtversion.h>
+
+#include <QCoreApplication>
+
+namespace Ios {
+namespace Internal {
+
+class IosQtVersion : public QtSupport::BaseQtVersion
+{
+ Q_DECLARE_TR_FUNCTIONS(Ios::Internal::IosQtVersion)
+
+public:
+ IosQtVersion();
+ IosQtVersion(const Utils::FileName &path, bool isAutodetected = false,
+ const QString &autodetectionSource = QString());
+
+ IosQtVersion *clone() const;
+ QString type() const;
+ bool isValid() const;
+ QString invalidReason() const;
+
+ QList<ProjectExplorer::Abi> detectQtAbis() const;
+
+ void addToEnvironment(const ProjectExplorer::Kit *k, Utils::Environment &env) const;
+
+ Core::FeatureSet availableFeatures() const;
+ QString platformName() const;
+ QString platformDisplayName() const;
+
+ QString description() const;
+};
+
+} // namespace Internal
+} // namespace Ios
+
+#endif // IOSQTVERSION_H
diff --git a/src/plugins/ios/iosqtversionfactory.cpp b/src/plugins/ios/iosqtversionfactory.cpp
new file mode 100644
index 0000000000..c842c5a9c2
--- /dev/null
+++ b/src/plugins/ios/iosqtversionfactory.cpp
@@ -0,0 +1,78 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** 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 "iosqtversionfactory.h"
+#include "iosqtversion.h"
+#include "iosconstants.h"
+#include <qtsupport/qtsupportconstants.h>
+#include <utils/qtcassert.h>
+#include <proparser/profileevaluator.h>
+
+#include <QFileInfo>
+#include <QDebug>
+
+namespace Ios {
+namespace Internal {
+
+IosQtVersionFactory::IosQtVersionFactory(QObject *parent)
+ : QtSupport::QtVersionFactory(parent)
+{
+}
+
+bool IosQtVersionFactory::canRestore(const QString &type)
+{
+ return type == QLatin1String(Constants::IOSQT);
+}
+
+QtSupport::BaseQtVersion *IosQtVersionFactory::restore(const QString &type,
+ const QVariantMap &data)
+{
+ QTC_ASSERT(canRestore(type), return 0);
+ IosQtVersion *v = new IosQtVersion;
+ v->fromMap(data);
+ return v;
+}
+
+int IosQtVersionFactory::priority() const
+{
+ return 90;
+}
+
+QtSupport::BaseQtVersion *IosQtVersionFactory::create(const Utils::FileName &qmakePath,
+ ProFileEvaluator *evaluator,
+ bool isAutoDetected,
+ const QString &autoDetectionSource)
+{
+ if (!(evaluator->values(QLatin1String("QMAKE_PLATFORM")).contains(QLatin1String("ios"))))
+ return 0;
+ return new IosQtVersion(qmakePath, isAutoDetected, autoDetectionSource);
+}
+
+} // Internal
+} // Ios
diff --git a/src/plugins/ios/iosqtversionfactory.h b/src/plugins/ios/iosqtversionfactory.h
new file mode 100644
index 0000000000..d63b578086
--- /dev/null
+++ b/src/plugins/ios/iosqtversionfactory.h
@@ -0,0 +1,54 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** 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.
+**
+****************************************************************************/
+#ifndef IOSQTVERSIONFACTORY_H
+#define IOSQTVERSIONFACTORY_H
+
+#include <qtsupport/qtversionfactory.h>
+
+namespace Ios {
+namespace Internal {
+
+class IosQtVersionFactory : public QtSupport::QtVersionFactory
+{
+public:
+ explicit IosQtVersionFactory(QObject *parent = 0);
+
+ bool canRestore(const QString &type);
+ QtSupport::BaseQtVersion *restore(const QString &type, const QVariantMap &data);
+
+ int priority() const;
+ QtSupport::BaseQtVersion *create(const Utils::FileName &qmakePath, ProFileEvaluator *evaluator,
+ bool isAutoDetected = false,
+ const QString &autoDetectionSource = QString());
+};
+
+} // namespace Internal
+} // namespace Ios
+
+#endif // IOSQTVERSIONFACTORY_H
diff --git a/src/plugins/ios/iosrunconfiguration.cpp b/src/plugins/ios/iosrunconfiguration.cpp
new file mode 100644
index 0000000000..851d6574be
--- /dev/null
+++ b/src/plugins/ios/iosrunconfiguration.cpp
@@ -0,0 +1,275 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** 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 "iosrunconfiguration.h"
+#include "iosmanager.h"
+#include "iosdeploystep.h"
+
+#include <projectexplorer/kitinformation.h>
+#include <projectexplorer/target.h>
+#include <projectexplorer/deployconfiguration.h>
+#include <projectexplorer/buildstep.h>
+#include <projectexplorer/buildsteplist.h>
+#include <qt4projectmanager/qt4buildconfiguration.h>
+#include <qt4projectmanager/qt4project.h>
+#include <qt4projectmanager/qt4nodes.h>
+#include <qtsupport/qtoutputformatter.h>
+#include <qtsupport/qtkitinformation.h>
+#include "ui_iosrunconfiguration.h"
+
+#include <QList>
+
+#include <utils/qtcprocess.h>
+#include <utils/qtcassert.h>
+
+using namespace ProjectExplorer;
+using namespace Qt4ProjectManager;
+
+namespace Ios {
+namespace Internal {
+
+class IosRunConfigurationWidget : public RunConfigWidget
+{
+ Q_OBJECT
+
+public:
+ IosRunConfigurationWidget(IosRunConfiguration *runConfiguration);
+ ~IosRunConfigurationWidget();
+ QString argListToString(const QStringList &args) const;
+ QStringList stringToArgList(const QString &args) const;
+ QString displayName() const;
+
+private slots:
+ void argumentsLineEditTextEdited();
+ void updateValues();
+
+private:
+ Ui::IosRunConfiguration *m_ui;
+ IosRunConfiguration *m_runConfiguration;
+};
+
+IosRunConfiguration::IosRunConfiguration(Target *parent, Core::Id id, const QString &path)
+ : RunConfiguration(parent, id)
+ , m_profilePath(path)
+{
+ init();
+}
+
+IosRunConfiguration::IosRunConfiguration(Target *parent, IosRunConfiguration *source)
+ : RunConfiguration(parent, source)
+ , m_profilePath(source->m_profilePath)
+{
+ init();
+}
+
+void IosRunConfiguration::init()
+{
+ setDefaultDisplayName(defaultDisplayName());
+}
+
+QWidget *IosRunConfiguration::createConfigurationWidget()
+{
+ return new IosRunConfigurationWidget(this);
+}
+
+Utils::OutputFormatter *IosRunConfiguration::createOutputFormatter() const
+{
+ return new QtSupport::QtOutputFormatter(target()->project());
+}
+
+QStringList IosRunConfiguration::commandLineArguments()
+{
+ return m_arguments;
+}
+
+QString IosRunConfiguration::defaultDisplayName()
+{
+ ProjectExplorer::IDevice::ConstPtr dev =
+ ProjectExplorer::DeviceKitInformation::device(target()->kit());
+ QString devName;
+ if (!dev.isNull())
+ devName = dev->displayName();
+ if (devName.isEmpty())
+ devName = tr("iOS device");
+ return tr("Run on %1").arg(devName);
+}
+
+IosDeployStep *IosRunConfiguration::deployStep() const
+{
+ IosDeployStep * step = 0;
+ DeployConfiguration *config = target()->activeDeployConfiguration();
+ ProjectExplorer::BuildStepList *bsl = config->stepList();
+ if (bsl) {
+ const QList<ProjectExplorer::BuildStep *> &buildSteps = bsl->steps();
+ for (int i = buildSteps.count() - 1; i >= 0; --i) {
+ step = qobject_cast<IosDeployStep *>(buildSteps.at(i));
+ if (step)
+ break;
+ }
+ }
+ Q_ASSERT_X(step, Q_FUNC_INFO, "Impossible: iOS build configuration without deploy step.");
+ return step;
+}
+
+QString IosRunConfiguration::profilePath() const
+{
+ return m_profilePath;
+}
+
+QString IosRunConfiguration::appName() const
+{
+ Qt4Project *pro = qobject_cast<Qt4Project *>(target()->project());
+ if (pro) {
+ const Qt4ProFileNode *node = pro->rootQt4ProjectNode()->findProFileFor(profilePath());
+ if (node) {
+ TargetInformation ti = node->targetInformation();
+ if (ti.valid) {
+ return ti.target;
+ }
+ }
+ }
+ qDebug() << "IosRunConfiguration::appName failed";
+ return QString();
+}
+
+Utils::FileName IosRunConfiguration::bundleDir() const
+{
+ Utils::FileName res;
+ Core::Id devType = ProjectExplorer::DeviceTypeKitInformation::deviceTypeId(target()->kit());
+ bool isDevice = (devType == Constants::IOS_DEVICE_TYPE);
+ if (!isDevice && devType != Constants::IOS_SIMULATOR_TYPE) {
+ qDebug() << "unexpected device type in bundleDirForTarget: " << devType.toString();
+ return res;
+ }
+ Qt4BuildConfiguration *bc =
+ qobject_cast<Qt4BuildConfiguration *>(target()->activeBuildConfiguration());
+ if (bc) {
+ res = bc->buildDirectory();
+ switch (bc->buildType()) {
+ case BuildConfiguration::Debug :
+ case BuildConfiguration::Unknown :
+ if (isDevice)
+ res.appendPath(QLatin1String("Debug-iphoneos"));
+ else
+ res.appendPath(QLatin1String("Debug-iphonesimulator"));
+ break;
+ case BuildConfiguration::Release :
+ if (isDevice)
+ res.appendPath(QLatin1String("Release-iphoneos"));
+ else
+ res.appendPath(QLatin1String("/Release-iphonesimulator"));
+ break;
+ default:
+ qDebug() << "IosBuildStep had an unknown buildType "
+ << target()->activeBuildConfiguration()->buildType();
+ }
+ }
+ res.appendPath(appName() + QLatin1String(".app"));
+ return res;
+}
+
+Utils::FileName IosRunConfiguration::exePath() const
+{
+ return bundleDir().appendPath(appName());
+}
+
+IosRunConfigurationWidget::IosRunConfigurationWidget(IosRunConfiguration *runConfiguration) :
+ m_ui(new Ui::IosRunConfiguration), m_runConfiguration(runConfiguration)
+{
+ m_ui->setupUi(this);
+
+ updateValues();
+ connect(m_ui->argumentsLineEdit, SIGNAL(editingFinished()),
+ this, SLOT(argumentsLineEditTextEdited()));
+}
+
+IosRunConfigurationWidget::~IosRunConfigurationWidget()
+{
+ delete m_ui;
+}
+
+QString IosRunConfigurationWidget::argListToString(const QStringList &args) const
+{
+ return Utils::QtcProcess::joinArgs(args);
+}
+
+QStringList IosRunConfigurationWidget::stringToArgList(const QString &args) const
+{
+ Utils::QtcProcess::SplitError err;
+ QStringList res = Utils::QtcProcess::splitArgs(args, false, &err);
+ switch (err) {
+ case Utils::QtcProcess::SplitOk:
+ break;
+ case Utils::QtcProcess::BadQuoting:
+ if (args.at(args.size()-1) == QLatin1Char('\\')) {
+ res = Utils::QtcProcess::splitArgs(args + QLatin1Char('\\'), false, &err);
+ if (err != Utils::QtcProcess::SplitOk)
+ res = Utils::QtcProcess::splitArgs(args + QLatin1Char('\\') + QLatin1Char('\''),
+ false, &err);
+ if (err != Utils::QtcProcess::SplitOk)
+ res = Utils::QtcProcess::splitArgs(args + QLatin1Char('\\') + QLatin1Char('\"'),
+ false, &err);
+ }
+ if (err != Utils::QtcProcess::SplitOk)
+ res = Utils::QtcProcess::splitArgs(args + QLatin1Char('\''), false, &err);
+ if (err != Utils::QtcProcess::SplitOk)
+ res = Utils::QtcProcess::splitArgs(args + QLatin1Char('\"'), false, &err);
+ break;
+ case Utils::QtcProcess::FoundMeta:
+ qDebug() << "IosRunConfigurationWidget FoundMeta (should not happen)";
+ break;
+ }
+ return res;
+}
+
+QString IosRunConfigurationWidget::displayName() const
+{
+ return tr("iOS run settings");
+}
+
+void IosRunConfigurationWidget::argumentsLineEditTextEdited()
+{
+ QString argsString = m_ui->argumentsLineEdit->text();
+ QStringList args = stringToArgList(argsString);
+ m_runConfiguration->m_arguments = args;
+ m_ui->argumentsLineEdit->setText(argListToString(args));
+}
+
+void IosRunConfigurationWidget::updateValues()
+{
+ QStringList args = m_runConfiguration->m_arguments;
+ QString argsString = argListToString(args);
+ m_ui->argumentsLineEdit->setText(argsString);
+ m_ui->executableLineEdit->setText(m_runConfiguration->exePath().toUserOutput());
+}
+
+} // namespace Internal
+} // namespace Ios
+
+#include "iosrunconfiguration.moc"
diff --git a/src/plugins/ios/iosrunconfiguration.h b/src/plugins/ios/iosrunconfiguration.h
new file mode 100644
index 0000000000..bb8dc31765
--- /dev/null
+++ b/src/plugins/ios/iosrunconfiguration.h
@@ -0,0 +1,78 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** 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.
+**
+****************************************************************************/
+#ifndef IOSRUNCONFIGURATION_H
+#define IOSRUNCONFIGURATION_H
+
+#include "iosconstants.h"
+#include "iosconfigurations.h"
+
+#include <projectexplorer/runconfiguration.h>
+#include <utils/fileutils.h>
+
+namespace Ios {
+namespace Internal {
+
+class IosDeployStep;
+class IosRunConfigurationFactory;
+class IosRunConfigurationWidget;
+
+class IosRunConfiguration : public ProjectExplorer::RunConfiguration
+{
+ Q_OBJECT
+ friend class IosRunConfigurationFactory;
+
+public:
+ IosRunConfiguration(ProjectExplorer::Target *parent, Core::Id id, const QString &path);
+
+ QWidget *createConfigurationWidget();
+ Utils::OutputFormatter *createOutputFormatter() const;
+ IosDeployStep *deployStep() const;
+
+ QStringList commandLineArguments();
+ QString profilePath() const;
+ QString appName() const;
+ Utils::FileName bundleDir() const;
+ Utils::FileName exePath() const;
+
+protected:
+ IosRunConfiguration(ProjectExplorer::Target *parent, IosRunConfiguration *source);
+ QString defaultDisplayName();
+
+private:
+ void init();
+ friend class IosRunConfigurationWidget;
+
+ QString m_profilePath;
+ QStringList m_arguments;
+};
+
+} // namespace Internal
+} // namespace Ios
+
+#endif // IOSRUNCONFIGURATION_H
diff --git a/src/plugins/ios/iosrunconfiguration.ui b/src/plugins/ios/iosrunconfiguration.ui
new file mode 100644
index 0000000000..0b6e5f75b9
--- /dev/null
+++ b/src/plugins/ios/iosrunconfiguration.ui
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>IosRunConfiguration</class>
+ <widget class="QWidget" name="IosRunConfiguration">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>571</width>
+ <height>76</height>
+ </rect>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="windowTitle">
+ <string>Form</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout">
+ <item row="1" column="1">
+ <widget class="QLineEdit" name="argumentsLineEdit"/>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="argumentsLabel">
+ <property name="text">
+ <string>Aguments:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0">
+ <widget class="QLabel" name="exeLabel">
+ <property name="text">
+ <string>Executable:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QLineEdit" name="executableLineEdit">
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/src/plugins/ios/iosruncontrol.cpp b/src/plugins/ios/iosruncontrol.cpp
new file mode 100644
index 0000000000..cbba7f0245
--- /dev/null
+++ b/src/plugins/ios/iosruncontrol.cpp
@@ -0,0 +1,112 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** 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 "iosruncontrol.h"
+
+#include "iosrunconfiguration.h"
+#include "iosrunner.h"
+
+#include <projectexplorer/projectexplorerconstants.h>
+#include <QIcon>
+
+using namespace ProjectExplorer;
+
+namespace Ios {
+namespace Internal {
+
+IosRunControl::IosRunControl(IosRunConfiguration *rc)
+ : RunControl(rc, NormalRunMode)
+ , m_runner(new IosRunner(this, rc, false))
+ , m_running(false)
+{
+}
+
+IosRunControl::~IosRunControl()
+{
+ stop();
+}
+
+void IosRunControl::start()
+{
+ m_running = true;
+ emit started();
+ disconnect(m_runner, 0, this, 0);
+
+ connect(m_runner, SIGNAL(errorMsg(QString)),
+ SLOT(handleRemoteErrorOutput(QString)));
+ connect(m_runner, SIGNAL(appOutput(QString)),
+ SLOT(handleRemoteOutput(QString)));
+ connect(m_runner, SIGNAL(finished(bool)),
+ SLOT(handleRemoteProcessFinished(bool)));
+ appendMessage(tr("Starting remote process.\n"), Utils::NormalMessageFormat);
+ m_runner->start();
+}
+
+RunControl::StopResult IosRunControl::stop()
+{
+ m_runner->stop();
+ return StoppedSynchronously;
+}
+
+void IosRunControl::handleRemoteProcessFinished(bool cleanEnd)
+{
+ if (!cleanEnd)
+ appendMessage(tr("Run ended unexpectedly."), Utils::ErrorMessageFormat);
+ disconnect(m_runner, 0, this, 0);
+ m_running = false;
+ emit finished();
+}
+
+void IosRunControl::handleRemoteOutput(const QString &output)
+{
+ appendMessage(output, Utils::StdOutFormatSameLine);
+}
+
+void IosRunControl::handleRemoteErrorOutput(const QString &output)
+{
+ appendMessage(output, Utils::StdErrFormat);
+}
+
+bool IosRunControl::isRunning() const
+{
+ return m_running;
+}
+
+QString IosRunControl::displayName() const
+{
+ return m_runner->displayName();
+}
+
+QIcon IosRunControl::icon() const
+{
+ return QIcon(QLatin1String(ProjectExplorer::Constants::ICON_DEBUG_SMALL));
+}
+
+} // namespace Internal
+} // namespace Qt4ProjectManager
diff --git a/src/plugins/ios/iosruncontrol.h b/src/plugins/ios/iosruncontrol.h
new file mode 100644
index 0000000000..922a5bdd0c
--- /dev/null
+++ b/src/plugins/ios/iosruncontrol.h
@@ -0,0 +1,69 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** 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.
+**
+****************************************************************************/
+#ifndef IOSRUNCONTROL_H
+#define IOSRUNCONTROL_H
+
+#include <projectexplorer/runconfiguration.h>
+
+namespace Ios {
+namespace Internal {
+
+class IosRunConfiguration;
+class IosRunner;
+
+class IosRunControl : public ProjectExplorer::RunControl
+{
+ Q_OBJECT
+
+public:
+ explicit IosRunControl(IosRunConfiguration *runConfig);
+ ~IosRunControl();
+
+
+ void start();
+ StopResult stop();
+ bool isRunning() const;
+ QString displayName() const;
+ QIcon icon() const;
+
+private slots:
+ void handleRemoteProcessFinished(bool cleanEnd);
+ void handleRemoteOutput(const QString &output);
+ void handleRemoteErrorOutput(const QString &output);
+
+private:
+
+ IosRunner * const m_runner;
+ bool m_running;
+};
+
+} // namespace Internal
+} // namespace Ios
+
+#endif // IOSRUNCONTROL_H
diff --git a/src/plugins/ios/iosrunfactories.cpp b/src/plugins/ios/iosrunfactories.cpp
new file mode 100644
index 0000000000..2ddaf5e1b1
--- /dev/null
+++ b/src/plugins/ios/iosrunfactories.cpp
@@ -0,0 +1,202 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** 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 "iosrunfactories.h"
+
+#include "iosconstants.h"
+#include "iosdebugsupport.h"
+#include "iosrunconfiguration.h"
+#include "iosruncontrol.h"
+#include "iosmanager.h"
+
+#include <projectexplorer/project.h>
+#include <projectexplorer/projectexplorerconstants.h>
+#include <projectexplorer/target.h>
+#include <debugger/debuggerconstants.h>
+#include <qt4projectmanager/qt4project.h>
+#include <qt4projectmanager/qt4nodes.h>
+#include <qtsupport/customexecutablerunconfiguration.h>
+#include <qtsupport/qtkitinformation.h>
+#include <qtsupport/qtsupportconstants.h>
+#include <coreplugin/id.h>
+
+
+using namespace ProjectExplorer;
+using namespace Qt4ProjectManager;
+
+namespace Ios {
+namespace Internal {
+
+#define IOS_PREFIX "Qt4ProjectManager.IosRunConfiguration"
+#define IOS_RC_ID_PREFIX IOS_PREFIX ":"
+
+static QString pathFromId(const Core::Id id)
+{
+ QString pathStr = id.toString();
+ const QString prefix = QLatin1String(IOS_RC_ID_PREFIX);
+ if (!pathStr.startsWith(prefix))
+ return QString();
+ return pathStr.mid(prefix.size());
+}
+
+IosRunConfigurationFactory::IosRunConfigurationFactory(QObject *parent)
+ : QmakeRunConfigurationFactory(parent)
+{
+ setObjectName(QLatin1String("IosRunConfigurationFactory"));
+}
+
+bool IosRunConfigurationFactory::canCreate(Target *parent, const Core::Id id) const
+{
+ if (!canHandle(parent))
+ return false;
+ return availableCreationIds(parent).contains(id);
+}
+
+bool IosRunConfigurationFactory::canRestore(Target *parent, const QVariantMap &map) const
+{
+ if (!canHandle(parent))
+ return false;
+ QString id = ProjectExplorer::idFromMap(map).toString();
+ return id.startsWith(QLatin1String(IOS_RC_ID_PREFIX));
+}
+
+bool IosRunConfigurationFactory::canClone(Target *parent, RunConfiguration *source) const
+{
+ return canCreate(parent, source->id());
+}
+
+QList<Core::Id> IosRunConfigurationFactory::availableCreationIds(Target *parent) const
+{
+ QList<Core::Id> ids;
+ if (!IosManager::supportsIos(parent))
+ return ids;
+ Core::Id baseId(IOS_RC_ID_PREFIX);
+ QList<Qt4ProFileNode *> nodes = static_cast<Qt4Project *>(parent->project())->allProFiles();
+ foreach (Qt4ProFileNode *node, nodes)
+ if (node->projectType() == ApplicationTemplate || node->projectType() == LibraryTemplate
+ || node->projectType() == AuxTemplate)
+ ids << baseId.withSuffix(node->path());
+ return ids;
+}
+
+QString IosRunConfigurationFactory::displayNameForId(const Core::Id id) const
+{
+ return QFileInfo(pathFromId(id)).completeBaseName();
+}
+
+RunConfiguration *IosRunConfigurationFactory::create(Target *parent, const Core::Id id)
+{
+ if (!canCreate(parent, id))
+ return 0;
+ return new IosRunConfiguration(parent, id, pathFromId(id));
+}
+
+RunConfiguration *IosRunConfigurationFactory::restore(Target *parent,
+ const QVariantMap &map)
+{
+ if (!canRestore(parent, map))
+ return 0;
+ Core::Id id = ProjectExplorer::idFromMap(map);
+ IosRunConfiguration *rc = new IosRunConfiguration(parent, id, pathFromId(id));
+ if (rc->fromMap(map))
+ return rc;
+
+ delete rc;
+ return 0;
+}
+
+RunConfiguration *IosRunConfigurationFactory::clone(Target *parent, RunConfiguration *source)
+{
+ if (!canClone(parent, source))
+ return 0;
+
+ IosRunConfiguration *old = qobject_cast<IosRunConfiguration *>(source);
+ return new IosRunConfiguration(parent, old);
+}
+
+bool IosRunConfigurationFactory::canHandle(Target *t) const
+{
+ if (!t->project()->supportsKit(t->kit()))
+ return false;
+ return IosManager::supportsIos(t);
+}
+
+QList<RunConfiguration *> IosRunConfigurationFactory::runConfigurationsForNode(Target *t, ProjectExplorer::Node *n)
+{
+ QList<ProjectExplorer::RunConfiguration *> result;
+ foreach (ProjectExplorer::RunConfiguration *rc, t->runConfigurations())
+ if (IosRunConfiguration *qt4c = qobject_cast<IosRunConfiguration *>(rc))
+ if (qt4c->profilePath() == n->path())
+ result << rc;
+ return result;
+}
+
+RunConfiguration *IosRunConfigurationFactory::doCreate(Target *parent, const Core::Id id)
+{
+ return new IosRunConfiguration(parent, id, pathFromId(id));
+}
+
+RunConfiguration *IosRunConfigurationFactory::doRestore(Target *parent, const QVariantMap &map)
+{
+ Core::Id id = ProjectExplorer::idFromMap(map);
+ return new IosRunConfiguration(parent, id, pathFromId(id));
+}
+
+IosRunControlFactory::IosRunControlFactory(QObject *parent)
+ : IRunControlFactory(parent)
+{
+}
+
+bool IosRunControlFactory::canRun(RunConfiguration *runConfiguration,
+ ProjectExplorer::RunMode mode) const
+{
+ if (mode != NormalRunMode && mode != DebugRunMode)
+ return false;
+ return qobject_cast<IosRunConfiguration *>(runConfiguration);
+}
+
+RunControl *IosRunControlFactory::create(RunConfiguration *runConfig,
+ ProjectExplorer::RunMode mode, QString *errorMessage)
+{
+ Q_ASSERT(canRun(runConfig, mode));
+ IosRunConfiguration *rc = qobject_cast<IosRunConfiguration *>(runConfig);
+ Q_ASSERT(rc);
+ if (mode == NormalRunMode)
+ return new Ios::Internal::IosRunControl(rc);
+ else
+ return IosDebugSupport::createDebugRunControl(rc, errorMessage);
+}
+
+QString IosRunControlFactory::displayName() const
+{
+ return tr("Run on iOS device or simulator.");
+}
+
+} // namespace Internal
+} // namespace Qt4ProjectManager
diff --git a/src/plugins/ios/iosrunfactories.h b/src/plugins/ios/iosrunfactories.h
new file mode 100644
index 0000000000..d45bd47069
--- /dev/null
+++ b/src/plugins/ios/iosrunfactories.h
@@ -0,0 +1,97 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** 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.
+**
+****************************************************************************/
+#ifndef IOSRUNFACTORIES_H
+#define IOSRUNFACTORIES_H
+
+#include <projectexplorer/runconfiguration.h>
+#include <qt4projectmanager/qmakerunconfigurationfactory.h>
+#include <qcompilerdetection.h>
+
+namespace ProjectExplorer {
+class RunControl;
+class RunConfigWidget;
+class Target;
+class Node;
+} // namespace ProjectExplorer
+
+namespace Ios {
+namespace Internal {
+
+class IosRunConfigurationFactory : public Qt4ProjectManager::QmakeRunConfigurationFactory
+{
+ Q_OBJECT
+
+public:
+ explicit IosRunConfigurationFactory(QObject *parent = 0);
+
+ Q_DECL_OVERRIDE QString displayNameForId(const Core::Id id) const;
+ Q_DECL_OVERRIDE QList<Core::Id> availableCreationIds(ProjectExplorer::Target *parent) const;
+
+ Q_DECL_OVERRIDE bool canCreate(ProjectExplorer::Target *parent, const Core::Id id) const;
+ Q_DECL_OVERRIDE ProjectExplorer::RunConfiguration *create(ProjectExplorer::Target *parent,
+ const Core::Id id);
+
+ Q_DECL_OVERRIDE bool canRestore(ProjectExplorer::Target *parent, const QVariantMap &map) const;
+ Q_DECL_OVERRIDE ProjectExplorer::RunConfiguration *restore(ProjectExplorer::Target *parent,
+ const QVariantMap &map);
+
+ Q_DECL_OVERRIDE bool canClone(ProjectExplorer::Target *parent,
+ ProjectExplorer::RunConfiguration *source) const;
+ Q_DECL_OVERRIDE ProjectExplorer::RunConfiguration *clone(ProjectExplorer::Target *parent,
+ ProjectExplorer::RunConfiguration *source);
+
+ Q_DECL_OVERRIDE bool canHandle(ProjectExplorer::Target *t) const;
+ Q_DECL_OVERRIDE QList<ProjectExplorer::RunConfiguration *> runConfigurationsForNode(ProjectExplorer::Target *t,
+ ProjectExplorer::Node *n);
+private:
+ ProjectExplorer::RunConfiguration *doCreate(ProjectExplorer::Target *parent, const Core::Id id);
+ ProjectExplorer::RunConfiguration *doRestore(ProjectExplorer::Target *parent,
+ const QVariantMap &map);
+};
+
+class IosRunControlFactory : public ProjectExplorer::IRunControlFactory
+{
+ Q_OBJECT
+
+public:
+ explicit IosRunControlFactory(QObject *parent = 0);
+
+ QString displayName() const;
+
+ bool canRun(ProjectExplorer::RunConfiguration *runConfiguration,
+ ProjectExplorer::RunMode mode) const;
+ ProjectExplorer::RunControl *create(ProjectExplorer::RunConfiguration *runConfiguration,
+ ProjectExplorer::RunMode mode,
+ QString *errorMessage);
+};
+
+} // namespace Internal
+} // namespace Ios
+
+#endif // IOSRUNFACTORIES_H
diff --git a/src/plugins/ios/iosrunner.cpp b/src/plugins/ios/iosrunner.cpp
new file mode 100644
index 0000000000..e2cb6b6f8f
--- /dev/null
+++ b/src/plugins/ios/iosrunner.cpp
@@ -0,0 +1,195 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** 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 "iosbuildstep.h"
+#include "iosconfigurations.h"
+#include "iosdevice.h"
+#include "iosmanager.h"
+#include "iosrunconfiguration.h"
+#include "iosrunner.h"
+#include "iossimulator.h"
+
+#include <projectexplorer/devicesupport/deviceapplicationrunner.h>
+#include <projectexplorer/kitinformation.h>
+#include <projectexplorer/target.h>
+
+#include <QDir>
+#include <QTime>
+#include <QMessageBox>
+
+namespace Ios {
+namespace Internal {
+
+IosRunner::IosRunner(QObject *parent, IosRunConfiguration *runConfig, bool debuggingMode)
+ : QObject(parent), m_toolHandler(0), m_bundleDir(runConfig->bundleDir().toString()),
+ m_arguments(runConfig->commandLineArguments()),
+ m_device(ProjectExplorer::DeviceKitInformation::device(runConfig->target()->kit())),
+ m_debuggingMode(debuggingMode), m_cleanExit(false)
+{
+}
+
+IosRunner::~IosRunner()
+{
+ stop();
+}
+
+QString IosRunner::bundlePath()
+{
+ return m_bundleDir;
+}
+
+QStringList IosRunner::extraArgs()
+{
+ return m_arguments;
+}
+
+QString IosRunner::deviceId()
+{
+ IosDevice::ConstPtr dev = m_device.dynamicCast<const IosDevice>();
+ if (!dev)
+ return QString();
+ return dev->uniqueDeviceID();
+}
+
+IosToolHandler::RunKind IosRunner::runType()
+{
+ if (m_debuggingMode)
+ return IosToolHandler::DebugRun;
+ return IosToolHandler::NormalRun;
+}
+
+void IosRunner::start()
+{
+ if (m_toolHandler) {
+ m_toolHandler->stop();
+ emit finished(m_cleanExit);
+ }
+ m_cleanExit = false;
+ IosToolHandler::DeviceType devType = IosToolHandler::IosDeviceType;
+ if (m_device->type() != Ios::Constants::IOS_DEVICE_TYPE) {
+ IosSimulator::ConstPtr sim = m_device.dynamicCast<const IosSimulator>();
+ if (sim.isNull()) {
+ emit finished(m_cleanExit);
+ return;
+ }
+ devType = IosToolHandler::IosSimulatedIphoneRetina4InchType; // store type in sim?
+ }
+ m_toolHandler = new IosToolHandler(devType, this);
+ connect(m_toolHandler, SIGNAL(appOutput(Ios::IosToolHandler*,QString)),
+ SLOT(handleAppOutput(Ios::IosToolHandler*,QString)));
+ connect(m_toolHandler,
+ SIGNAL(didStartApp(Ios::IosToolHandler*,QString,QString,Ios::IosToolHandler::OpStatus)),
+ SLOT(handleDidStartApp(Ios::IosToolHandler*,QString,QString,Ios::IosToolHandler::OpStatus)));
+ connect(m_toolHandler, SIGNAL(errorMsg(Ios::IosToolHandler*,QString)),
+ SLOT(handleErrorMsg(Ios::IosToolHandler*,QString)));
+ connect(m_toolHandler, SIGNAL(gotGdbserverSocket(Ios::IosToolHandler*,QString,QString,int)),
+ SLOT(handleGotGdbserverSocket(Ios::IosToolHandler*,QString,QString,int)));
+ connect(m_toolHandler, SIGNAL(gotInferiorPid(Ios::IosToolHandler*,QString,QString,pid_t)),
+ SLOT(handleGotInferiorPid(Ios::IosToolHandler*,QString,QString,pid_t)));
+ connect(m_toolHandler, SIGNAL(toolExited(Ios::IosToolHandler*,int)),
+ SLOT(handleToolExited(Ios::IosToolHandler*,int)));
+ connect(m_toolHandler, SIGNAL(finished(Ios::IosToolHandler*)),
+ SLOT(handleFinished(Ios::IosToolHandler*)));
+ m_toolHandler->requestRunApp(bundlePath(), extraArgs(), runType(), deviceId());
+}
+
+void IosRunner::stop()
+{
+ if (m_toolHandler)
+ m_toolHandler->stop();
+}
+
+void IosRunner::handleDidStartApp(IosToolHandler *handler, const QString &bundlePath,
+ const QString &deviceId, IosToolHandler::OpStatus status)
+{
+ Q_UNUSED(bundlePath); Q_UNUSED(deviceId);
+ if (m_toolHandler == handler)
+ emit didStartApp(status);
+}
+
+void IosRunner::handleGotGdbserverSocket(IosToolHandler *handler, const QString &bundlePath,
+ const QString &deviceId, int gdbFd)
+{
+ Q_UNUSED(bundlePath); Q_UNUSED(deviceId);
+ if (m_toolHandler == handler)
+ emit gotGdbserverSocket(gdbFd);
+}
+
+void IosRunner::handleGotInferiorPid(IosToolHandler *handler, const QString &bundlePath,
+ const QString &deviceId, pid_t pid)
+{
+ Q_UNUSED(bundlePath); Q_UNUSED(deviceId);
+ if (m_toolHandler == handler)
+ emit gotInferiorPid(pid);
+}
+
+void IosRunner::handleAppOutput(IosToolHandler *handler, const QString &output)
+{
+ Q_UNUSED(handler);
+ emit appOutput(output);
+}
+
+void IosRunner::handleErrorMsg(IosToolHandler *handler, const QString &msg)
+{
+ if (msg.contains(QLatin1String("AMDeviceStartService returned -402653150"))) {
+ QMessageBox mBox;
+ mBox.setText(tr("Running on iOS device failed"));
+ mBox.setInformativeText(tr("This might be due to not up-to date certificates in Xcode or the device, go to the organizer window of Xcode to ensure that all certificates are up to date and try again."));
+ mBox.setStandardButtons(QMessageBox::Ok);
+ mBox.setDefaultButton(QMessageBox::Ok);
+ mBox.setIcon(QMessageBox::Information);
+ mBox.exec();
+ }
+ Q_UNUSED(handler);
+ emit errorMsg(msg);
+}
+
+void IosRunner::handleToolExited(IosToolHandler *handler, int code)
+{
+ Q_UNUSED(handler);
+ m_cleanExit = (code == 0);
+}
+
+void IosRunner::handleFinished(IosToolHandler *handler)
+{
+ if (m_toolHandler == handler) {
+ emit finished(m_cleanExit);
+ m_toolHandler = 0;
+ }
+ handler->deleteLater();
+}
+
+QString IosRunner::displayName() const
+{
+ return QString::fromLatin1("Run on %1").arg(m_device.isNull() ? QString()
+ : m_device->displayName());
+}
+
+} // namespace Internal
+} // namespace Qt4ProjectManager
diff --git a/src/plugins/ios/iosrunner.h b/src/plugins/ios/iosrunner.h
new file mode 100644
index 0000000000..f7a82552e9
--- /dev/null
+++ b/src/plugins/ios/iosrunner.h
@@ -0,0 +1,95 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** 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.
+**
+****************************************************************************/
+#ifndef IOSRUNNER_H
+#define IOSRUNNER_H
+
+#include "iosconfigurations.h"
+
+#include <QObject>
+#include <QTimer>
+#include <QThread>
+#include <QProcess>
+#include <QMutex>
+#include "iostoolhandler.h"
+#include <projectexplorer/devicesupport/idevice.h>
+
+namespace Ios {
+namespace Internal {
+
+class IosRunConfiguration;
+
+class IosRunner : public QObject
+{
+ Q_OBJECT
+
+public:
+ IosRunner(QObject *parent, IosRunConfiguration *m_runConfig, bool m_debuggingMode);
+ ~IosRunner();
+
+ QString displayName() const;
+
+ QString bundlePath();
+ QStringList extraArgs();
+ QString deviceId();
+ IosToolHandler::RunKind runType();
+public slots:
+ void start();
+ void stop();
+
+signals:
+ void didStartApp(Ios::IosToolHandler::OpStatus status);
+ void gotGdbserverSocket(int gdbFd);
+ void gotInferiorPid(pid_t pid);
+ void appOutput(const QString &output);
+ void errorMsg(const QString &msg);
+ void finished(bool cleanExit);
+private slots:
+ void handleDidStartApp(Ios::IosToolHandler *handler, const QString &bundlePath,
+ const QString &deviceId, Ios::IosToolHandler::OpStatus status);
+ void handleGotGdbserverSocket(Ios::IosToolHandler *handler, const QString &bundlePath,
+ const QString &deviceId, int gdbFd);
+ void handleGotInferiorPid(Ios::IosToolHandler *handler, const QString &bundlePath,
+ const QString &deviceId, pid_t pid);
+ void handleAppOutput(Ios::IosToolHandler *handler, const QString &output);
+ void handleErrorMsg(Ios::IosToolHandler *handler, const QString &msg);
+ void handleToolExited(Ios::IosToolHandler *handler, int code);
+ void handleFinished(Ios::IosToolHandler *handler);
+private:
+ IosToolHandler *m_toolHandler;
+ QString m_bundleDir;
+ QStringList m_arguments;
+ ProjectExplorer::IDevice::ConstPtr m_device;
+ bool m_debuggingMode;
+ bool m_cleanExit;
+};
+
+} // namespace Internal
+} // namespace Ios
+
+#endif // IOSRUNNER_H
diff --git a/src/plugins/ios/iossettingspage.cpp b/src/plugins/ios/iossettingspage.cpp
new file mode 100644
index 0000000000..75270a86d2
--- /dev/null
+++ b/src/plugins/ios/iossettingspage.cpp
@@ -0,0 +1,75 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** 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 "iossettingspage.h"
+
+#include "iossettingswidget.h"
+#include "iosconstants.h"
+
+#include <QCoreApplication>
+
+namespace Ios {
+namespace Internal {
+
+IosSettingsPage::IosSettingsPage(QObject *parent)
+ : Core::IOptionsPage(parent)
+{
+ setId(Constants::IOS_SETTINGS_ID);
+ setDisplayName(tr("iOS Configurations"));
+ setCategory(Constants::IOS_SETTINGS_CATEGORY);
+ setDisplayCategory(QCoreApplication::translate("iOS",
+ Constants::IOS_SETTINGS_TR_CATEGORY));
+ setCategoryIcon(QLatin1String(Constants::IOS_SETTINGS_CATEGORY_ICON));
+}
+
+bool IosSettingsPage::matches(const QString &searchKeyWord) const
+{
+ return m_keywords.contains(searchKeyWord, Qt::CaseInsensitive);
+}
+
+QWidget *IosSettingsPage::createPage(QWidget *parent)
+{
+ m_widget = new IosSettingsWidget(parent);
+ if (m_keywords.isEmpty())
+ m_keywords = m_widget->searchKeywords();
+ return m_widget;
+}
+
+void IosSettingsPage::apply()
+{
+ m_widget->saveSettings();
+ IosConfigurations::instance().updateAutomaticKitList();
+}
+
+void IosSettingsPage::finish()
+{
+}
+
+} // namespace Internal
+} // namespace Ios
diff --git a/src/plugins/ios/iossettingspage.h b/src/plugins/ios/iossettingspage.h
new file mode 100644
index 0000000000..96ee190905
--- /dev/null
+++ b/src/plugins/ios/iossettingspage.h
@@ -0,0 +1,59 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** 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.
+**
+****************************************************************************/
+#ifndef IOSSETTINGSPAGE_H
+#define IOSSETTINGSPAGE_H
+
+#include <coreplugin/dialogs/ioptionspage.h>
+
+namespace Ios {
+namespace Internal {
+
+class IosSettingsWidget;
+
+class IosSettingsPage : public Core::IOptionsPage
+{
+ Q_OBJECT
+
+public:
+ explicit IosSettingsPage(QObject *parent = 0);
+
+ bool matches(const QString &searchKeyWord) const;
+ QWidget *createPage(QWidget *parent);
+ void apply();
+ void finish();
+
+private:
+ QString m_keywords;
+ IosSettingsWidget *m_widget;
+};
+
+} // namespace Internal
+} // namespace Ios
+
+#endif // IOSSETTINGSPAGE_H
diff --git a/src/plugins/ios/iossettingswidget.cpp b/src/plugins/ios/iossettingswidget.cpp
new file mode 100644
index 0000000000..74d1aa9e68
--- /dev/null
+++ b/src/plugins/ios/iossettingswidget.cpp
@@ -0,0 +1,134 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** 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 "iossettingswidget.h"
+
+#include "ui_iossettingswidget.h"
+
+#include "iosconfigurations.h"
+#include "iosconstants.h"
+
+#include <utils/hostosinfo.h>
+#include <projectexplorer/toolchainmanager.h>
+#include <projectexplorer/kitmanager.h>
+#include <projectexplorer/kitinformation.h>
+#include <qtsupport/qtkitinformation.h>
+#include <qtsupport/qtversionmanager.h>
+
+#include <QFile>
+#include <QTextStream>
+#include <QProcess>
+
+#include <QFileDialog>
+#include <QMessageBox>
+#include <QModelIndex>
+
+namespace Ios {
+namespace Internal {
+
+IosSettingsWidget::IosSettingsWidget(QWidget *parent)
+ : QWidget(parent),
+ m_ui(new Ui_IosSettingsWidget),
+ m_iosConfig(IosConfigurations::instance().config()),
+ m_saveSettingsRequested(false)
+{
+ initGui();
+}
+
+IosSettingsWidget::~IosSettingsWidget()
+{
+ if (m_saveSettingsRequested)
+ IosConfigurations::instance().setConfig(m_iosConfig);
+ delete m_ui;
+}
+
+QString IosSettingsWidget::searchKeywords() const
+{
+ QString rc;
+ QTextStream(&rc) << m_ui->developerPathLabel->text();
+ rc.remove(QLatin1Char('&'));
+ return rc;
+}
+
+void IosSettingsWidget::initGui()
+{
+ m_ui->setupUi(this);
+ m_ui->developerPathLineEdit->setText(m_iosConfig.developerPath.toUserOutput());
+ m_ui->deviceAskCheckBox->setChecked(!m_iosConfig.ignoreAllDevices);
+ connect(m_ui->developerPathLineEdit, SIGNAL(editingFinished()),
+ SLOT(developerPathEditingFinished()));
+ connect(m_ui->developerPathPushButton, SIGNAL(clicked()),
+ SLOT(browseDeveloperPath()));
+ connect(m_ui->deviceAskCheckBox, SIGNAL(toggled(bool)),
+ SLOT(deviceAskToggled(bool)));
+}
+
+void IosSettingsWidget::deviceAskToggled(bool checkboxValue)
+{
+ m_iosConfig.ignoreAllDevices = !checkboxValue;
+ saveSettings(true);
+}
+
+void IosSettingsWidget::saveSettings(bool saveNow)
+{
+ // We must defer this step because of a stupid bug on MacOS. See QTCREATORBUG-1675.
+ if (saveNow) {
+ IosConfigurations::instance().setConfig(m_iosConfig);
+ m_saveSettingsRequested = false;
+ } else {
+ m_saveSettingsRequested = true;
+ }
+}
+
+void IosSettingsWidget::developerPathEditingFinished()
+{
+ Utils::FileName basePath = Utils::FileName::fromUserInput(m_ui->developerPathLineEdit->text());
+ // auto extend Contents/Developer if required
+ Utils::FileName devDir = basePath;
+ devDir.appendPath(QLatin1String("Contents/Developer"));
+ if (devDir.toFileInfo().isDir())
+ m_iosConfig.developerPath = devDir;
+ else
+ m_iosConfig.developerPath = basePath;
+ m_ui->developerPathLineEdit->setText(m_iosConfig.developerPath.toUserOutput());
+ saveSettings(true);
+}
+
+void IosSettingsWidget::browseDeveloperPath()
+{
+ Utils::FileName dir = Utils::FileName::fromString(
+ QFileDialog::getOpenFileName(this,
+ tr("Select Xcode application"),
+ QLatin1String("/Applications"), QLatin1String("*.app")));
+ m_ui->developerPathLineEdit->setText(dir.toUserOutput());
+ developerPathEditingFinished();
+}
+
+} // namespace Internal
+} // namespace Ios
diff --git a/src/plugins/ios/iossettingswidget.h b/src/plugins/ios/iossettingswidget.h
new file mode 100644
index 0000000000..6f9b946352
--- /dev/null
+++ b/src/plugins/ios/iossettingswidget.h
@@ -0,0 +1,73 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** 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.
+**
+****************************************************************************/
+#ifndef IOSSETTINGSWIDGET_H
+#define IOSSETTINGSWIDGET_H
+
+#include "iosconfigurations.h"
+
+#include <QList>
+#include <QString>
+#include <QWidget>
+#include <QAbstractTableModel>
+
+QT_BEGIN_NAMESPACE
+class Ui_IosSettingsWidget;
+QT_END_NAMESPACE
+
+namespace Ios {
+namespace Internal {
+
+class IosSettingsWidget : public QWidget
+{
+ Q_OBJECT
+public:
+ // Todo: This would be so much simpler if it just used Utils::PathChooser!!!
+ IosSettingsWidget(QWidget *parent);
+ ~IosSettingsWidget();
+
+ void saveSettings(bool saveNow = false);
+ QString searchKeywords() const;
+
+private slots:
+ void deviceAskToggled(bool checkboxValue);
+ void developerPathEditingFinished();
+ void browseDeveloperPath();
+
+private:
+ void initGui();
+
+ Ui_IosSettingsWidget *m_ui;
+ IosConfig m_iosConfig;
+ bool m_saveSettingsRequested;
+};
+
+} // namespace Internal
+} // namespace Ios
+
+#endif // IOSSETTINGSWIDGET_H
diff --git a/src/plugins/ios/iossettingswidget.ui b/src/plugins/ios/iossettingswidget.ui
new file mode 100644
index 0000000000..c2293246fc
--- /dev/null
+++ b/src/plugins/ios/iossettingswidget.ui
@@ -0,0 +1,90 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>IosSettingsWidget</class>
+ <widget class="QWidget" name="IosSettingsWidget">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>679</width>
+ <height>104</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>iOS Configuration</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <layout class="QGridLayout" name="gridLayout">
+ <item row="0" column="0">
+ <widget class="QLabel" name="developerPathLabel">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Xcode path:</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QLineEdit" name="developerPathLineEdit"/>
+ </item>
+ <item row="0" column="2">
+ <widget class="QPushButton" name="developerPathPushButton">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Browse</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QCheckBox" name="deviceAskCheckBox">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Ask about devices not in developer mode</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections/>
+ <slots>
+ <slot>sdkLocationEditingFinished()</slot>
+ <slot>ndkLocationEditingFinished()</slot>
+ <slot>browseSDKLocation()</slot>
+ <slot>browseNDKLocation()</slot>
+ <slot>antLocationEditingFinished()</slot>
+ <slot>browseAntLocation()</slot>
+ <slot>addAVD()</slot>
+ <slot>removeAVD()</slot>
+ <slot>startAVD()</slot>
+ <slot>avdActivated(QModelIndex)</slot>
+ <slot>dataPartitionSizeEditingFinished()</slot>
+ <slot>openJDKLocationEditingFinished()</slot>
+ <slot>browseOpenJDKLocation()</slot>
+ <slot>manageAVD()</slot>
+ <slot>createKitToggled()</slot>
+ </slots>
+</ui>
diff --git a/src/plugins/ios/iossimulator.cpp b/src/plugins/ios/iossimulator.cpp
new file mode 100644
index 0000000000..aa7ac3341b
--- /dev/null
+++ b/src/plugins/ios/iossimulator.cpp
@@ -0,0 +1,142 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** 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 "iossimulator.h"
+#include "iosconstants.h"
+
+#include <projectexplorer/kitinformation.h>
+
+#include <QCoreApplication>
+
+using namespace ProjectExplorer;
+
+namespace Ios {
+namespace Internal {
+
+const char *SIMULATOR_PATH_KEY = "SIMULATOR_PATH";
+
+IosSimulator::IosSimulator(Core::Id id, Utils::FileName simulatorPath)
+ : IDevice(Core::Id(Constants::IOS_SIMULATOR_TYPE),
+ IDevice::AutoDetected,
+ IDevice::Emulator,
+ id),
+ m_simulatorPath(simulatorPath)
+{
+ setDisplayName(QCoreApplication::translate("Ios::Internal::IosSimulator", "Ios Simulator"));
+}
+
+IosSimulator::IosSimulator()
+ : IDevice(Core::Id(Constants::IOS_SIMULATOR_TYPE),
+ IDevice::AutoDetected,
+ IDevice::Emulator,
+ Core::Id(Constants::IOS_SIMULATOR_DEVICE_ID))
+{
+ setDisplayName(QCoreApplication::translate("Ios::Internal::IosSimulator", "Ios Simulator"));
+ setDeviceState(DeviceReadyToUse);
+}
+
+IosSimulator::IosSimulator(const IosSimulator &other)
+ : IDevice(other)
+{
+ setDisplayName(QCoreApplication::translate("Ios::Internal::IosSimulator", "Ios Simulator"));
+}
+
+
+IDevice::DeviceInfo IosSimulator::deviceInformation() const
+{
+ return IDevice::DeviceInfo();
+}
+
+QString IosSimulator::displayType() const
+{
+ return QCoreApplication::translate("Ios::Internal::IosSimulator", "Ios Simulator");
+}
+
+IDeviceWidget *IosSimulator::createWidget()
+{
+ return 0;
+}
+
+QList<Core::Id> IosSimulator::actionIds() const
+{
+ return QList<Core::Id>()<<Core::Id(Constants::IOS_DEVICE_ID);
+}
+
+QString IosSimulator::displayNameForActionId(Core::Id actionId) const
+{
+ Q_UNUSED(actionId)
+ return QString();
+}
+
+void IosSimulator::executeAction(Core::Id actionId, QWidget *parent)
+{
+ Q_UNUSED(actionId)
+ Q_UNUSED(parent)
+}
+
+DeviceProcessSignalOperation::Ptr IosSimulator::signalOperation() const
+{
+ return DeviceProcessSignalOperation::Ptr();
+}
+
+Utils::FileName IosSimulator::simulatorPath() const
+{
+ return m_simulatorPath;
+}
+
+IDevice::Ptr IosSimulator::clone() const
+{
+ return IDevice::Ptr(new IosSimulator(*this));
+}
+
+void IosSimulator::fromMap(const QVariantMap &map)
+{
+ IDevice::fromMap(map);
+ m_simulatorPath = Utils::FileName::fromString(map.value(QLatin1String(SIMULATOR_PATH_KEY))
+ .toString());
+}
+
+QVariantMap IosSimulator::toMap() const
+{
+ QVariantMap res = IDevice::toMap();
+ res.insert(QLatin1String(SIMULATOR_PATH_KEY), simulatorPath().toString());
+ return res;
+}
+
+IosSimulator::ConstPtr IosKitInformation::simulator(Kit *kit)
+{
+ if (!kit)
+ return IosSimulator::ConstPtr();
+ ProjectExplorer::IDevice::ConstPtr dev = ProjectExplorer::DeviceKitInformation::device(kit);
+ IosSimulator::ConstPtr res = dev.dynamicCast<const IosSimulator>();
+ return res;
+}
+
+} // namespace Internal
+} // namespace Ios
diff --git a/src/plugins/ios/iossimulator.h b/src/plugins/ios/iossimulator.h
new file mode 100644
index 0000000000..2a6e85f123
--- /dev/null
+++ b/src/plugins/ios/iossimulator.h
@@ -0,0 +1,80 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** 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.
+**
+****************************************************************************/
+#ifndef IOSSIMULATOR_H
+#define IOSSIMULATOR_H
+
+#include <projectexplorer/devicesupport/idevice.h>
+#include <utils/fileutils.h>
+
+#include <QSharedPointer>
+
+namespace ProjectExplorer {
+class Kit;
+}
+namespace Ios {
+namespace Internal {
+class IosConfigurations;
+class IosSimulatorFactory;
+
+class IosSimulator : public ProjectExplorer::IDevice
+{
+public:
+ typedef QSharedPointer<const IosSimulator> ConstPtr;
+ typedef QSharedPointer<IosSimulator> Ptr;
+ ProjectExplorer::IDevice::DeviceInfo deviceInformation() const;
+
+ QString displayType() const;
+ ProjectExplorer::IDeviceWidget *createWidget();
+ QList<Core::Id> actionIds() const;
+ QString displayNameForActionId(Core::Id actionId) const;
+ void executeAction(Core::Id actionId, QWidget *parent = 0);
+ ProjectExplorer::DeviceProcessSignalOperation::Ptr signalOperation() const;
+ Utils::FileName simulatorPath() const;
+ void fromMap(const QVariantMap &map);
+ QVariantMap toMap() const;
+
+ ProjectExplorer::IDevice::Ptr clone() const;
+
+protected:
+ friend class IosSimulatorFactory;
+ friend class IosConfigurations;
+ IosSimulator();
+ IosSimulator(Core::Id id, Utils::FileName simulatorPath);
+ IosSimulator(const IosSimulator &other);
+private:
+ Utils::FileName m_simulatorPath;
+};
+
+namespace IosKitInformation {
+IosSimulator::ConstPtr simulator(ProjectExplorer::Kit *kit);
+} // namespace IosKitInformation
+} // namespace Internal
+} // namespace Ios
+
+#endif // IOSSIMULATOR_H
diff --git a/src/plugins/ios/iossimulatorfactory.cpp b/src/plugins/ios/iossimulatorfactory.cpp
new file mode 100644
index 0000000000..7ee5d8e211
--- /dev/null
+++ b/src/plugins/ios/iossimulatorfactory.cpp
@@ -0,0 +1,81 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** 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 "iossimulatorfactory.h"
+#include <QLatin1String>
+#include "iosconstants.h"
+#include "iossimulator.h"
+#include "utils/qtcassert.h"
+
+namespace Ios {
+namespace Internal {
+
+IosSimulatorFactory::IosSimulatorFactory()
+{
+ setObjectName(QLatin1String("IosSimulatorFactory"));
+}
+
+QString IosSimulatorFactory::displayNameForId(Core::Id type) const
+{
+ if (type == Constants::IOS_SIMULATOR_TYPE)
+ return tr("iOS Simulator");
+ return QString();
+}
+
+QList<Core::Id> IosSimulatorFactory::availableCreationIds() const
+{
+ return QList<Core::Id>() << Core::Id(Constants::IOS_SIMULATOR_TYPE);
+}
+
+bool IosSimulatorFactory::canCreate() const
+{
+ return false;
+}
+
+ProjectExplorer::IDevice::Ptr IosSimulatorFactory::create(Core::Id id) const
+{
+ Q_UNUSED(id)
+ return ProjectExplorer::IDevice::Ptr();
+}
+
+bool IosSimulatorFactory::canRestore(const QVariantMap &map) const
+{
+ return ProjectExplorer::IDevice::typeFromMap(map) == Constants::IOS_SIMULATOR_TYPE;
+}
+
+ProjectExplorer::IDevice::Ptr IosSimulatorFactory::restore(const QVariantMap &map) const
+{
+ QTC_ASSERT(canRestore(map), return ProjectExplorer::IDevice::Ptr());
+ const ProjectExplorer::IDevice::Ptr device = ProjectExplorer::IDevice::Ptr(new IosSimulator());
+ device->fromMap(map);
+ return device;
+}
+
+} // namespace Internal
+} // namespace Ios
diff --git a/src/plugins/ios/iossimulatorfactory.h b/src/plugins/ios/iossimulatorfactory.h
new file mode 100644
index 0000000000..763912ce8e
--- /dev/null
+++ b/src/plugins/ios/iossimulatorfactory.h
@@ -0,0 +1,55 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** 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.
+**
+****************************************************************************/
+#ifndef IOSSIMULATORFACTORY_H
+#define IOSSIMULATORFACTORY_H
+
+#include <projectexplorer/devicesupport/idevicefactory.h>
+
+namespace Ios {
+namespace Internal {
+
+class IosSimulatorFactory : public ProjectExplorer::IDeviceFactory
+{
+ Q_OBJECT
+public:
+ IosSimulatorFactory();
+
+ QString displayNameForId(Core::Id type) const;
+ QList<Core::Id> availableCreationIds() const;
+
+ bool canCreate() const;
+ ProjectExplorer::IDevice::Ptr create(Core::Id id) const;
+ bool canRestore(const QVariantMap &map) const;
+ ProjectExplorer::IDevice::Ptr restore(const QVariantMap &map) const;
+};
+
+} // namespace Internal
+} // namespace Ios
+
+#endif // IOSSIMULATORFACTORY_H
diff --git a/src/plugins/ios/iostoolhandler.cpp b/src/plugins/ios/iostoolhandler.cpp
new file mode 100644
index 0000000000..e91276b6c0
--- /dev/null
+++ b/src/plugins/ios/iostoolhandler.cpp
@@ -0,0 +1,977 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** 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 "iostoolhandler.h"
+#include "iosconfigurations.h"
+
+#include <utils/qtcassert.h>
+#include <utils/fileutils.h>
+
+#include <QProcess>
+#include <QXmlStreamReader>
+#include <QSocketNotifier>
+#include <QDebug>
+#include <QCoreApplication>
+#include <QList>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+
+static const bool debugToolHandler = false;
+
+namespace Ios {
+
+namespace Internal {
+
+class MyProcess: public QProcess
+{
+ Q_OBJECT
+public:
+ explicit MyProcess(QObject *parent = 0);
+ int processOutput();
+ QSocketNotifier *notifier();
+protected:
+ virtual void setupChildProcess();
+private:
+ int m_sockets[2];
+ QSocketNotifier *m_notifier;
+};
+
+struct ParserState {
+ enum Kind {
+ Msg,
+ DeviceId,
+ Key,
+ Value,
+ QueryResult,
+ AppOutput,
+ AppStarted,
+ InferiorPid,
+ Item,
+ Status,
+ AppTransfer,
+ DeviceInfo,
+ Exit
+ };
+ Kind kind;
+ QString elName;
+ QString chars;
+ QString key;
+ QString value;
+ QMap<QString,QString> info;
+ int progress, maxProgress;
+ bool collectChars() {
+ switch (kind) {
+ case Msg:
+ case DeviceId:
+ case Key:
+ case Value:
+ case Status:
+ case InferiorPid:
+ return true;
+ case QueryResult:
+ case AppOutput:
+ case AppStarted:
+ case AppTransfer:
+ case Item:
+ case DeviceInfo:
+ case Exit:
+ return false;
+ }
+ }
+
+ ParserState(Kind kind) :
+ kind(kind) { }
+};
+
+class IosToolHandlerPrivate
+{
+public:
+ enum State {
+ NonStarted,
+ Starting,
+ StartedInferior,
+ XmlEndSeenNotProcessed,
+ XmlEndProcessed,
+ Stopped
+ };
+ enum Op {
+ OpNone,
+ OpAppTransfer,
+ OpDeviceInfo,
+ OpAppRun
+ };
+
+ explicit IosToolHandlerPrivate(IosToolHandler::DeviceType devType, IosToolHandler *q);
+ virtual void requestTransferApp(const QString &bundlePath, const QString &deviceId,
+ int timeout = 1000) = 0;
+ virtual void requestRunApp(const QString &bundlePath, const QStringList &extraArgs,
+ IosToolHandler::RunKind runKind,
+ const QString &deviceId, int timeout = 1000) = 0;
+ virtual void requestDeviceInfo(const QString &deviceId, int timeout = 1000) = 0;
+ bool isRunning();
+ void start(const QString &exe, const QStringList &args);
+ void stop();
+
+ // signals
+ void isTransferringApp(const QString &bundlePath, const QString &deviceId, int progress,
+ int maxProgress, const QString &info);
+ void didTransferApp(const QString &bundlePath, const QString &deviceId,
+ IosToolHandler::OpStatus status);
+ void didStartApp(const QString &bundlePath, const QString &deviceId,
+ IosToolHandler::OpStatus status);
+ void gotGdbserverSocket(const QString &bundlePath, const QString &deviceId, int gdbFd);
+ void gotInferiorPid(const QString &bundlePath, const QString &deviceId, pid_t pid);
+ void deviceInfo(const QString &deviceId, const IosToolHandler::Dict &info);
+ void appOutput(const QString &output);
+ void errorMsg(const QString &msg);
+ void toolExited(int code);
+ // slots
+ void subprocessError(QProcess::ProcessError error);
+ void subprocessFinished(int exitCode, QProcess::ExitStatus exitStatus);
+ void subprocessHasData(int socket);
+ virtual bool expectsFileDescriptor() = 0;
+protected:
+ int checkForXmlEnd();
+ void processXml();
+
+ IosToolHandler *q;
+ MyProcess process;
+ QXmlStreamReader outputParser;
+ QString deviceId;
+ QString bundlePath;
+ IosToolHandler::RunKind runKind;
+ State state;
+ Op op;
+ IosToolHandler::DeviceType devType;
+ static const int lookaheadSize = 67;
+ QByteArray buffer;
+ QByteArray currentData;
+ int iBegin, iEnd, gdbSocket;
+ QList<ParserState> stack;
+};
+
+class IosDeviceToolHandlerPrivate : public IosToolHandlerPrivate
+{
+public:
+ explicit IosDeviceToolHandlerPrivate(IosToolHandler::DeviceType devType, IosToolHandler *q);
+ virtual void requestTransferApp(const QString &bundlePath, const QString &deviceId,
+ int timeout = 1000);
+ virtual void requestRunApp(const QString &bundlePath, const QStringList &extraArgs,
+ IosToolHandler::RunKind runKind,
+ const QString &deviceId, int timeout = 1000);
+ virtual void requestDeviceInfo(const QString &deviceId, int timeout = 1000);
+ virtual bool expectsFileDescriptor();
+};
+
+class IosSimulatorToolHandlerPrivate : public IosToolHandlerPrivate
+{
+public:
+ explicit IosSimulatorToolHandlerPrivate(IosToolHandler::DeviceType devType, IosToolHandler *q);
+ virtual void requestTransferApp(const QString &bundlePath, const QString &deviceId,
+ int timeout = 1000);
+ virtual void requestRunApp(const QString &bundlePath, const QStringList &extraArgs,
+ IosToolHandler::RunKind runKind,
+ const QString &deviceId, int timeout = 1000);
+ virtual void requestDeviceInfo(const QString &deviceId, int timeout = 1000);
+ virtual bool expectsFileDescriptor();
+private:
+ void addDeviceArguments(QStringList &args) const;
+};
+
+MyProcess::MyProcess(QObject *parent) : QProcess(parent)
+{
+ if (socketpair(PF_UNIX, SOCK_STREAM, 0, &m_sockets[0]) == -1) {
+ qDebug() << "IosToolHandler socketpair failed ";
+ }
+ shutdown(m_sockets[0], SHUT_WR);
+ m_notifier = new QSocketNotifier(m_sockets[0], QSocketNotifier::Read, this);
+}
+
+int MyProcess::processOutput()
+{
+ return m_sockets[0];
+}
+
+QSocketNotifier *MyProcess::notifier()
+{
+ return m_notifier;
+}
+
+void MyProcess::setupChildProcess()
+{
+ if (dup2(m_sockets[1], 1) == -1) { // use the unix socket as stdout
+ qDebug() << "IosToolHandler dup2 call failed";
+ emit finished(-1, QProcess::CrashExit);
+ exit(-1);
+ }
+ shutdown(1, SHUT_RD); // leave open for handshake when transferring fd?
+}
+
+IosToolHandlerPrivate::IosToolHandlerPrivate(IosToolHandler::DeviceType devType,
+ Ios::IosToolHandler *q) :
+ q(q), state(NonStarted), devType(devType), buffer(4*lookaheadSize, 0), iBegin(0), iEnd(0),
+ gdbSocket(-1)
+{
+ QObject::connect(process.notifier(), SIGNAL(activated(int)), q, SLOT(subprocessHasData(int)));
+ QObject::connect(&process, SIGNAL(finished(int,QProcess::ExitStatus)),
+ q, SLOT(subprocessFinished(int,QProcess::ExitStatus)));
+ QObject::connect(&process, SIGNAL(error(QProcess::ProcessError)),
+ q, SLOT(subprocessError(QProcess::ProcessError)));
+ int accessFlags = fcntl(process.processOutput(), F_GETFL);
+ if (fcntl(process.processOutput(), F_SETFL, accessFlags | O_NONBLOCK) == -1)
+ qDebug() << "IosToolHandler fcntl F_SETFL failed to set non blocking mode"
+ << qt_error_string(errno);
+}
+
+bool IosToolHandlerPrivate::isRunning()
+{
+ return process.state() != QProcess::NotRunning;
+}
+
+void IosToolHandlerPrivate::start(const QString &exe, const QStringList &args)
+{
+ QTC_CHECK(state == NonStarted);
+ state = Starting;
+ if (debugToolHandler)
+ qDebug() << "running " << exe << args;
+ process.start(exe, args);
+ state = StartedInferior;
+}
+
+void IosToolHandlerPrivate::stop()
+{
+ if (debugToolHandler)
+ qDebug() << "IosToolHandlerPrivate::stop";
+ if (process.state() != QProcess::NotRunning) {
+ close(process.processOutput());
+ process.close();
+ process.kill();
+ }
+ if (state != Stopped) {
+ state = Stopped;
+ emit q->finished(q);
+ }
+}
+
+// signals
+void IosToolHandlerPrivate::isTransferringApp(const QString &bundlePath, const QString &deviceId,
+ int progress, int maxProgress, const QString &info)
+{
+ emit q->isTransferringApp(q, bundlePath, deviceId, progress, maxProgress, info);
+}
+
+void IosToolHandlerPrivate::didTransferApp(const QString &bundlePath, const QString &deviceId,
+ Ios::IosToolHandler::OpStatus status)
+{
+ emit q->didTransferApp(q, bundlePath, deviceId, status);
+}
+
+void IosToolHandlerPrivate::didStartApp(const QString &bundlePath, const QString &deviceId,
+ IosToolHandler::OpStatus status)
+{
+ emit q->didStartApp(q, bundlePath, deviceId, status);
+}
+
+void IosToolHandlerPrivate::gotGdbserverSocket(const QString &bundlePath, const QString &deviceId,
+ int gdbFd)
+{
+ emit q->gotGdbserverSocket(q, bundlePath, deviceId, gdbFd);
+}
+
+void IosToolHandlerPrivate::gotInferiorPid(const QString &bundlePath, const QString &deviceId,
+ pid_t pid)
+{
+ emit q->gotInferiorPid(q, bundlePath, deviceId, pid);
+}
+
+void IosToolHandlerPrivate::deviceInfo(const QString &deviceId,
+ const Ios::IosToolHandler::Dict &info)
+{
+ emit q->deviceInfo(q, deviceId, info);
+}
+
+void IosToolHandlerPrivate::appOutput(const QString &output)
+{
+ emit q->appOutput(q, output);
+}
+
+void IosToolHandlerPrivate::errorMsg(const QString &msg)
+{
+ emit q->errorMsg(q, msg);
+}
+
+void IosToolHandlerPrivate::toolExited(int code)
+{
+ emit q->toolExited(q, code);
+}
+
+void IosToolHandlerPrivate::subprocessError(QProcess::ProcessError error)
+{
+ switch (state) {
+ case NonStarted:
+ qDebug() << "subprocessError() when state was NonStarted";
+ // pass
+ case Starting:
+ switch (op){
+ case OpNone:
+ qDebug() << "subprocessError() when op is OpNone";
+ break;
+ case OpAppTransfer:
+ didTransferApp(bundlePath, deviceId, IosToolHandler::Failure);
+ break;
+ case OpAppRun:
+ didStartApp(bundlePath, deviceId, IosToolHandler::Failure);
+ break;
+ case OpDeviceInfo:
+ break;
+ }
+ // pass
+ case StartedInferior:
+ case XmlEndSeenNotProcessed:
+ case XmlEndProcessed:
+ errorMsg(q->tr("Subprocess Error %1").arg(error));
+ toolExited(-1);
+ break;
+ case Stopped:
+ qDebug() << "IosToolHandler, subprocessError() in an already stopped process";
+ }
+}
+
+void IosToolHandlerPrivate::subprocessFinished(int exitCode, QProcess::ExitStatus exitStatus)
+{
+ // process potentially pending data
+ subprocessHasData(process.processOutput());
+ switch (state) {
+ case NonStarted:
+ qDebug() << "subprocessFinished() when state was NonStarted";
+ // pass
+ case Starting:
+ switch (op){
+ case OpNone:
+ qDebug() << "subprocessFinished() when op was OpNone";
+ break;
+ case OpAppTransfer:
+ didTransferApp(bundlePath, deviceId, IosToolHandler::Failure);
+ break;
+ case OpAppRun:
+ didStartApp(bundlePath, deviceId, IosToolHandler::Failure);
+ break;
+ case OpDeviceInfo:
+ break;
+ }
+ // pass
+ case StartedInferior:
+ case XmlEndSeenNotProcessed:
+ case XmlEndProcessed:
+ toolExited((exitStatus == QProcess::CrashExit && exitCode == 0) ? -1 : exitCode);
+ break;
+ case Stopped:
+ if (debugToolHandler)
+ qDebug() << "IosToolHandler, subprocessFinished() in an already stopped process (normal)";
+ break;
+ }
+}
+
+int recv_fd(int socket)
+{
+ int sent_fd;
+ char message_buffer[1];
+
+ iovec io_vector[1];
+ memset(&io_vector[0], 0, sizeof(iovec));
+ /* setup a place to fill in message contents */
+ io_vector[0].iov_base = message_buffer;
+ io_vector[0].iov_len = 1;
+
+ msghdr socket_message;
+ memset(&socket_message, 0, sizeof(struct msghdr));
+ socket_message.msg_iov = io_vector;
+ socket_message.msg_iovlen = 1;
+
+ /* provide space for the ancillary data */
+ char ancillary_element_buffer[CMSG_SPACE(sizeof(int))];
+ memset(ancillary_element_buffer, 0, CMSG_SPACE(sizeof(int)));
+ socket_message.msg_control = ancillary_element_buffer;
+ socket_message.msg_controllen = CMSG_SPACE(sizeof(int));
+
+ int flags = 0;
+#ifdef MSG_CMSG_CLOEXEC
+ flags = MSG_CMSG_CLOEXEC;
+#endif
+ if (recvmsg(socket, &socket_message, flags) < 0)
+ return -1;
+
+ if (message_buffer[0] != '.') {
+ qDebug() << "IosToolHandler, unexpected inband data when receiving socket";
+ return -1;
+ }
+
+ if ((socket_message.msg_flags & MSG_CTRUNC) == MSG_CTRUNC) {
+ qDebug() << "IosToolHandler, not provide enough space for the ancillary element array";
+ return -1;
+ }
+
+ /* iterate ancillary elements */
+ cmsghdr *control_message = NULL;
+ for (control_message = CMSG_FIRSTHDR(&socket_message);
+ control_message != NULL;
+ control_message = CMSG_NXTHDR(&socket_message, control_message)) {
+ if ( (control_message->cmsg_level == SOL_SOCKET) &&
+ (control_message->cmsg_type == SCM_RIGHTS) ) {
+ sent_fd = *((int *) CMSG_DATA(control_message));
+ return sent_fd;
+ }
+ }
+
+ return -1;
+}
+
+int IosToolHandlerPrivate::checkForXmlEnd()
+{
+ const char *xmlEnd = "</query_result>";
+ int lenXmlEnd = 15;
+ int i = 0, j = 0;
+ while (i < lenXmlEnd && j < iEnd) {
+ if (buffer.at(j) != xmlEnd[i]) {
+ if (i == 0) {
+ ++j;
+ if (j + lenXmlEnd > iEnd)
+ break;
+ } else {
+ i = 0;
+ }
+ } else {
+ ++i;
+ ++j;
+ }
+ }
+ if (i == lenXmlEnd)
+ return j;
+ return -1;
+}
+
+void IosToolHandlerPrivate::processXml()
+{
+ while (!outputParser.atEnd()) {
+ QXmlStreamReader::TokenType tt = outputParser.readNext();
+ //qDebug() << "processXml, tt=" << tt;
+ switch (tt) {
+ case QXmlStreamReader::NoToken:
+ // The reader has not yet read anything.
+ continue;
+ case QXmlStreamReader::Invalid:
+ // An error has occurred, reported in error() and errorString().
+ break;
+ case QXmlStreamReader::StartDocument:
+ // The reader reports the XML version number in documentVersion(), and the encoding
+ // as specified in the XML document in documentEncoding(). If the document is declared
+ // standalone, isStandaloneDocument() returns true; otherwise it returns false.
+ break;
+ case QXmlStreamReader::EndDocument:
+ // The reader reports the end of the document.
+ // state = XmlEndProcessed;
+ break;
+ case QXmlStreamReader::StartElement:
+ // The reader reports the start of an element with namespaceUri() and name(). Empty
+ // elements are also reported as StartElement, followed directly by EndElement.
+ // The convenience function readElementText() can be called to concatenate all content
+ // until the corresponding EndElement. Attributes are reported in attributes(),
+ // namespace declarations in namespaceDeclarations().
+ {
+ QStringRef elName = outputParser.name();
+ if (elName == QLatin1String("msg")) {
+ stack.append(ParserState(ParserState::Msg));
+ } else if (elName == QLatin1String("exit")) {
+ stack.append(ParserState(ParserState::Exit));
+ toolExited(outputParser.attributes().value(QLatin1String("code"))
+ .toString().toInt());
+ } else if (elName == QLatin1String("device_id")) {
+ stack.append(ParserState(ParserState::DeviceId));
+ } else if (elName == QLatin1String("key")) {
+ stack.append(ParserState(ParserState::Key));
+ } else if (elName == QLatin1String("value")) {
+ stack.append(ParserState(ParserState::Value));
+ } else if (elName == QLatin1String("query_result")) {
+ stack.append(ParserState(ParserState::QueryResult));
+ } else if (elName == QLatin1String("app_output")) {
+ stack.append(ParserState(ParserState::AppOutput));
+ } else if (elName == QLatin1String("item")) {
+ stack.append(ParserState(ParserState::Item));
+ } else if (elName == QLatin1String("status")) {
+ ParserState pState(ParserState::Status);
+ QXmlStreamAttributes attributes = outputParser.attributes();
+ pState.progress = attributes.value(QLatin1String("progress")).toString().toInt();
+ pState.maxProgress = attributes.value(QLatin1String("max_progress")).toString().toInt();
+ stack.append(pState);
+ } else if (elName == QLatin1String("app_started")) {
+ stack.append(ParserState(ParserState::AppStarted));
+ QXmlStreamAttributes attributes = outputParser.attributes();
+ QStringRef statusStr = attributes.value(QLatin1String("status"));
+ Ios::IosToolHandler::OpStatus status = Ios::IosToolHandler::Unknown;
+ if (statusStr.compare(QLatin1String("success"), Qt::CaseInsensitive) == 0)
+ status = Ios::IosToolHandler::Success;
+ else if (statusStr.compare(QLatin1String("failure"), Qt::CaseInsensitive) == 0)
+ status = Ios::IosToolHandler::Failure;
+ didStartApp(bundlePath, deviceId, status);
+ } else if (elName == QLatin1String("app_transfer")) {
+ stack.append(ParserState(ParserState::AppTransfer));
+ QXmlStreamAttributes attributes = outputParser.attributes();
+ QStringRef statusStr = attributes.value(QLatin1String("status"));
+ Ios::IosToolHandler::OpStatus status = Ios::IosToolHandler::Unknown;
+ if (statusStr.compare(QLatin1String("success"), Qt::CaseInsensitive) == 0)
+ status = Ios::IosToolHandler::Success;
+ else if (statusStr.compare(QLatin1String("failure"), Qt::CaseInsensitive) == 0)
+ status = Ios::IosToolHandler::Failure;
+ emit didTransferApp(bundlePath, deviceId, status);
+ } else if (elName == QLatin1String("device_info")) {
+ stack.append(ParserState(ParserState::DeviceInfo));
+ } else if (elName == QLatin1String("inferior_pid")) {
+ stack.append(ParserState(ParserState::InferiorPid));
+ } else {
+ qDebug() << "unexpected element " << elName;
+ }
+ break;
+ }
+ case QXmlStreamReader::EndElement:
+ // The reader reports the end of an element with namespaceUri() and name().
+ {
+ ParserState p = stack.last();
+ stack.removeLast();
+ switch (p.kind) {
+ case ParserState::Msg:
+ errorMsg(p.chars);
+ break;
+ case ParserState::DeviceId:
+ if (deviceId.isEmpty())
+ deviceId = p.chars;
+ else
+ QTC_CHECK(deviceId.compare(p.chars, Qt::CaseInsensitive) == 0);
+ break;
+ case ParserState::Key:
+ stack.last().key = p.chars;
+ break;
+ case ParserState::Value:
+ stack.last().value = p.chars;
+ break;
+ case ParserState::Status:
+ isTransferringApp(bundlePath, deviceId, p.progress, p.maxProgress, p.chars);
+ break;
+ case ParserState::QueryResult:
+ state = XmlEndProcessed;
+ break;
+ case ParserState::AppOutput:
+ break;
+ case ParserState::AppStarted:
+ break;
+ case ParserState::AppTransfer:
+ break;
+ case ParserState::Item:
+ stack.last().info.insert(p.key, p.value);
+ break;
+ case ParserState::DeviceInfo:
+ deviceInfo(deviceId, p.info);
+ break;
+ case ParserState::Exit:
+ break;
+ case ParserState::InferiorPid:
+ gotInferiorPid(bundlePath, deviceId, pid_t(p.chars.toInt()));
+ break;
+ }
+ break;
+ }
+ case QXmlStreamReader::Characters:
+ // The reader reports characters in text(). If the characters are all white-space,
+ // isWhitespace() returns true. If the characters stem from a CDATA section,
+ // isCDATA() returns true.
+ if (stack.isEmpty())
+ break;
+ if (stack.last().kind == ParserState::AppOutput)
+ emit appOutput(outputParser.text().toString());
+ else if (stack.last().collectChars())
+ stack.last().chars.append(outputParser.text());
+ break;
+ case QXmlStreamReader::Comment:
+ // The reader reports a comment in text().
+ break;
+ case QXmlStreamReader::DTD:
+ // The reader reports a DTD in text(), notation declarations in notationDeclarations(),
+ // and entity declarations in entityDeclarations(). Details of the DTD declaration are
+ // reported in in dtdName(), dtdPublicId(), and dtdSystemId().
+ break;
+ case QXmlStreamReader::EntityReference:
+ // The reader reports an entity reference that could not be resolved. The name of
+ // the reference is reported in name(), the replacement text in text().
+ break;
+ case QXmlStreamReader::ProcessingInstruction:
+ break;
+ }
+ }
+ if (outputParser.hasError()
+ && outputParser.error() != QXmlStreamReader::PrematureEndOfDocumentError) {
+ qDebug() << "error parsing iosTool output:" << outputParser.errorString();
+ stop();
+ }
+}
+
+void IosToolHandlerPrivate::subprocessHasData(int socket)
+{
+ if (debugToolHandler)
+ qDebug() << "subprocessHasData, state:" << state;
+ process.notifier()->setEnabled(false);
+ while (true) {
+ switch (state) {
+ case NonStarted:
+ qDebug() << "IosToolHandler unexpected state in subprocessHasData: NonStarted";
+ // pass
+ case Starting:
+ case StartedInferior:
+ // read some data
+ {
+ if (iEnd + lookaheadSize > buffer.size()) {
+ memmove(buffer.data(), buffer.data() + (iEnd - lookaheadSize), lookaheadSize);
+ iBegin = lookaheadSize;
+ iEnd = iBegin;
+ } else {
+ iBegin = iEnd;
+ }
+ currentData.clear();
+ ssize_t reallyRead = recv(socket, buffer.data() + iBegin, lookaheadSize, 0);
+ if (reallyRead == 0) { // eof
+ stop();
+ return;
+ }
+ if (reallyRead == -1) {
+ if (errno == EAGAIN) { // read all so far
+ if (debugToolHandler)
+ qDebug() << "read all for now";
+ process.notifier()->setEnabled(true);
+ return;
+ }
+ if (errno == EINTR)
+ continue;
+ qDebug() << "IosToolHandlerPrivate::subprocessHasData " << qt_error_string(errno);
+ stop();
+ return;
+ }
+ iEnd = iBegin + reallyRead;
+ int xmlEnd = checkForXmlEnd();
+ if (xmlEnd != -1) {
+ state = XmlEndSeenNotProcessed;
+ currentData = buffer.mid(iBegin, xmlEnd - iBegin);
+ } else {
+ currentData = buffer.mid(iBegin, reallyRead);
+ }
+ if (debugToolHandler)
+ qDebug() << "subprocessHasData read " << currentData;
+ outputParser.addData(currentData);
+ processXml();
+ break;
+ }
+ case XmlEndSeenNotProcessed:
+ qDebug() << "IosToolHandler unexpected state in subprocessHasData: XmlEndSeenNotProcessed";
+ // pass
+ case XmlEndProcessed:
+ {
+ // check for sent fd
+ if (!expectsFileDescriptor()) {
+ stop();
+ return;
+ }
+ int lenToRead = lookaheadSize;
+ int spacerStart = iBegin + currentData.size();
+ while (spacerStart < iEnd && buffer.at(spacerStart) != 'n')
+ ++spacerStart;
+ if (iEnd - (iBegin + currentData.size()) < lenToRead) {
+ int lastXmlSize = currentData.size();
+ if (iBegin > 0) {
+ memmove(buffer.data(), buffer.data() + iBegin, iEnd - iBegin);
+ iEnd -= iBegin;
+ iBegin = 0;
+ spacerStart -= iBegin;
+ currentData = buffer.mid(0, lastXmlSize); // remove this??
+ }
+ ssize_t toRead = lookaheadSize - (iEnd - spacerStart);
+ ssize_t reallyRead = recv(socket, buffer.data() + iBegin, toRead, 0);
+ if (reallyRead == 0) { // eof
+ stop();
+ return;
+ }
+ if (reallyRead == -1) {
+ if (errno == EAGAIN) { // read all so far
+ if (debugToolHandler)
+ qDebug() << "read all for now2";
+ process.notifier()->setEnabled(true);
+ return;
+ }
+ if (errno == EINTR)
+ continue;
+ if (debugToolHandler)
+ qDebug() << "IosToolHandlerPrivate::subprocessHasData " << qt_error_string(errno);
+ stop();
+ return;
+ }
+ iEnd += reallyRead;
+ if (reallyRead != toRead)
+ continue;
+ if (spacerStart < iEnd && buffer.at(spacerStart) != 'n') {
+ ++spacerStart;
+ while (spacerStart < iEnd && buffer.at(spacerStart) != 'n')
+ ++spacerStart;
+ continue;
+ }
+ }
+ gdbSocket = recv_fd(socket);
+ gotGdbserverSocket(bundlePath, deviceId, gdbSocket);
+ stop();
+ return;
+ }
+ case Stopped:
+ return;
+ }
+ }
+ process.notifier()->setEnabled(true);
+}
+
+// IosDeviceToolHandlerPrivate
+
+IosDeviceToolHandlerPrivate::IosDeviceToolHandlerPrivate(IosToolHandler::DeviceType devType,
+ IosToolHandler *q)
+ : IosToolHandlerPrivate(devType, q)
+{ }
+
+void IosDeviceToolHandlerPrivate::requestTransferApp(const QString &bundlePath,
+ const QString &deviceId, int timeout)
+{
+ this->bundlePath = bundlePath;
+ this->deviceId = deviceId;
+ QStringList args;
+ args << QLatin1String("-device-id") << deviceId << QLatin1String("-bundle")
+ << bundlePath << QLatin1String("-timeout") << QString::number(timeout)
+ << QLatin1String("-deploy");
+ start(IosToolHandler::iosDeviceToolPath(), args);
+}
+
+void IosDeviceToolHandlerPrivate::requestRunApp(const QString &bundlePath,
+ const QStringList &extraArgs,
+ IosToolHandler::RunKind runType,
+ const QString &deviceId, int timeout)
+{
+ this->bundlePath = bundlePath;
+ this->deviceId = deviceId;
+ this->runKind = runType;
+ QStringList args;
+ args << QLatin1String("-device-id") << deviceId << QLatin1String("-bundle")
+ << bundlePath << QLatin1String("-timeout") << QString::number(timeout);
+ //args << QLatin1String("--deploy"); // to remove when the separate deploy step is functional
+ switch (runType) {
+ case IosToolHandler::NormalRun:
+ args << QLatin1String("-run");
+ break;
+ case IosToolHandler::DebugRun:
+ args << QLatin1String("-debug");
+ break;
+ }
+ args << QLatin1String("-extra-args") << extraArgs;
+ op = OpAppRun;
+ start(IosToolHandler::iosDeviceToolPath(), args);
+}
+
+void IosDeviceToolHandlerPrivate::requestDeviceInfo(const QString &deviceId, int timeout)
+{
+ this->deviceId = deviceId;
+ QStringList args;
+ args << QLatin1String("-device-id") << deviceId << QLatin1String("-device-info")
+ << QLatin1String("-timeout") << QString::number(timeout);
+ op = OpDeviceInfo;
+ start(IosToolHandler::iosDeviceToolPath(), args);
+}
+
+bool IosDeviceToolHandlerPrivate::expectsFileDescriptor()
+{
+ return op == OpAppRun && runKind == IosToolHandler::DebugRun;
+}
+
+// IosSimulatorToolHandlerPrivate
+
+IosSimulatorToolHandlerPrivate::IosSimulatorToolHandlerPrivate(IosToolHandler::DeviceType devType,
+ IosToolHandler *q)
+ : IosToolHandlerPrivate(devType, q)
+{ }
+
+void IosSimulatorToolHandlerPrivate::requestTransferApp(const QString &bundlePath,
+ const QString &deviceId, int timeout)
+{
+ Q_UNUSED(timeout);
+ this->bundlePath = bundlePath;
+ this->deviceId = deviceId;
+ emit didTransferApp(bundlePath, deviceId, IosToolHandler::Success);
+}
+
+void IosSimulatorToolHandlerPrivate::requestRunApp(const QString &bundlePath,
+ const QStringList &extraArgs,
+ IosToolHandler::RunKind runType,
+ const QString &deviceId, int timeout)
+{
+ Q_UNUSED(timeout);
+ this->bundlePath = bundlePath;
+ this->deviceId = deviceId;
+ this->runKind = runType;
+ QStringList args;
+
+ args << QLatin1String("launch") << bundlePath;
+ Utils::FileName devPath = IosConfigurations::instance().config().developerPath;
+ if (!devPath.isEmpty())
+ args << QLatin1String("--developer-path") << devPath.toString();
+ addDeviceArguments(args);
+ switch (runType) {
+ case IosToolHandler::NormalRun:
+ break;
+ case IosToolHandler::DebugRun:
+ args << QLatin1String("--wait-for-debugger");
+ break;
+ }
+ args << QLatin1String("--args") << extraArgs;
+ op = OpAppRun;
+ start(IosToolHandler::iosSimulatorToolPath(), args);
+}
+
+void IosSimulatorToolHandlerPrivate::requestDeviceInfo(const QString &deviceId, int timeout)
+{
+ Q_UNUSED(timeout);
+ this->deviceId = deviceId;
+ QStringList args;
+ args << QLatin1String("showsdks");
+ op = OpDeviceInfo;
+ start(IosToolHandler::iosSimulatorToolPath(), args);
+}
+
+bool IosSimulatorToolHandlerPrivate::expectsFileDescriptor()
+{
+ return false;
+}
+
+void IosSimulatorToolHandlerPrivate::addDeviceArguments(QStringList &args) const
+{
+ switch (devType) {
+ case IosToolHandler::IosDeviceType:
+ qDebug() << "IosSimulatorToolHandlerPrivate has device type IosDeviceType";
+ break;
+ case IosToolHandler::IosSimulatedIphoneType:
+ args << QLatin1String("--family") << QLatin1String("iphone");
+ break;
+ case IosToolHandler::IosSimulatedIpadType:
+ args << QLatin1String("--family") << QLatin1String("ipad");
+ break;
+ case IosToolHandler::IosSimulatedIphoneRetina4InchType:
+ args << QLatin1String("--family") << QLatin1String("iphone")
+ << QLatin1String("--retina") << QLatin1String("--tall");
+ break;
+ case IosToolHandler::IosSimulatedIphoneRetina3_5InchType:
+ args << QLatin1String("--family") << QLatin1String("iphone") << QLatin1String("--retina");
+ break;
+ case IosToolHandler::IosSimulatedIpadRetinaType:
+ args << QLatin1String("--family") << QLatin1String("ipad") << QLatin1String("--retina");
+ break;
+ }
+}
+
+} // namespace Internal
+
+QString IosToolHandler::iosDeviceToolPath()
+{
+ QString res = QCoreApplication::applicationDirPath() + QLatin1String("/iosTool");
+ return res;
+}
+
+QString IosToolHandler::iosSimulatorToolPath()
+{
+ QString res = QCoreApplication::applicationDirPath() + QLatin1String("/iossim");
+ return res;
+}
+
+IosToolHandler::IosToolHandler(DeviceType devType, QObject *parent) :
+ QObject(parent)
+{
+ if (devType == IosDeviceType)
+ d = new Internal::IosDeviceToolHandlerPrivate(devType, this);
+ else
+ d = new Internal::IosSimulatorToolHandlerPrivate(devType, this);
+}
+
+void IosToolHandler::stop()
+{
+ d->stop();
+}
+
+void IosToolHandler::requestTransferApp(const QString &bundlePath, const QString &deviceId,
+ int timeout)
+{
+ d->requestTransferApp(bundlePath, deviceId, timeout);
+}
+
+void IosToolHandler::requestRunApp(const QString &bundlePath, const QStringList &extraArgs,
+ RunKind runType, const QString &deviceId, int timeout)
+{
+ d->requestRunApp(bundlePath, extraArgs, runType, deviceId, timeout);
+}
+
+void IosToolHandler::requestDeviceInfo(const QString &deviceId, int timeout)
+{
+ d->requestDeviceInfo(deviceId, timeout);
+}
+
+bool IosToolHandler::isRunning()
+{
+ return d->isRunning();
+}
+
+void IosToolHandler::subprocessError(QProcess::ProcessError error)
+{
+ d->subprocessError(error);
+}
+
+void IosToolHandler::subprocessFinished(int exitCode, QProcess::ExitStatus exitStatus)
+{
+ d->subprocessFinished(exitCode, exitStatus);
+}
+
+void IosToolHandler::subprocessHasData(int socket)
+{
+ d->subprocessHasData(socket);
+}
+
+} // namespace Ios
+
+#include "iostoolhandler.moc"
diff --git a/src/plugins/ios/iostoolhandler.h b/src/plugins/ios/iostoolhandler.h
new file mode 100644
index 0000000000..e817acc102
--- /dev/null
+++ b/src/plugins/ios/iostoolhandler.h
@@ -0,0 +1,107 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** 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.
+**
+****************************************************************************/
+
+#ifndef IOSTOOLHANDLER_H
+#define IOSTOOLHANDLER_H
+
+#include <QObject>
+#include <QMap>
+#include <QString>
+#include <QStringList>
+#include <QProcess>
+
+
+namespace Ios {
+namespace Internal {
+class IosToolHandlerPrivate;
+}
+
+class IosToolHandler : public QObject
+{
+ Q_OBJECT
+public:
+ typedef QMap<QString,QString> Dict;
+ enum RunKind {
+ NormalRun,
+ DebugRun
+ };
+ enum OpStatus {
+ Success = 0,
+ Unknown = 1,
+ Failure = 2
+ };
+ enum DeviceType {
+ IosDeviceType,
+ IosSimulatedIphoneType,
+ IosSimulatedIpadType,
+ IosSimulatedIphoneRetina4InchType,
+ IosSimulatedIphoneRetina3_5InchType,
+ IosSimulatedIpadRetinaType
+ };
+
+ static QString iosDeviceToolPath();
+ static QString iosSimulatorToolPath();
+ explicit IosToolHandler(DeviceType = IosDeviceType, QObject *parent = 0);
+ void requestTransferApp(const QString &bundlePath, const QString &deviceId, int timeout = 1000);
+ void requestRunApp(const QString &bundlePath, const QStringList &extraArgs, RunKind runType,
+ const QString &deviceId, int timeout = 1000);
+ void requestDeviceInfo(const QString &deviceId, int timeout = 1000);
+ bool isRunning();
+signals:
+ void isTransferringApp(Ios::IosToolHandler *handler, const QString &bundlePath,
+ const QString &deviceId, int progress, int maxProgress,
+ const QString &info);
+ void didTransferApp(Ios::IosToolHandler *handler, const QString &bundlePath,
+ const QString &deviceId, Ios::IosToolHandler::OpStatus status);
+ void didStartApp(Ios::IosToolHandler *handler, const QString &bundlePath,
+ const QString &deviceId, Ios::IosToolHandler::OpStatus status);
+ void gotGdbserverSocket(Ios::IosToolHandler *handler, const QString &bundlePath,
+ const QString &deviceId, int gdbFd);
+ void gotInferiorPid(Ios::IosToolHandler *handler, const QString &bundlePath,
+ const QString &deviceId, pid_t pid);
+ void deviceInfo(Ios::IosToolHandler *handler, const QString &deviceId,
+ const Ios::IosToolHandler::Dict &info);
+ void appOutput(Ios::IosToolHandler *handler, const QString &output);
+ void errorMsg(Ios::IosToolHandler *handler, const QString &msg);
+ void toolExited(Ios::IosToolHandler *handler, int code);
+ void finished(Ios::IosToolHandler *handler);
+public slots:
+ void stop();
+private slots:
+ void subprocessError(QProcess::ProcessError error);
+ void subprocessFinished(int exitCode, QProcess::ExitStatus exitStatus);
+ void subprocessHasData(int socket);
+private:
+ friend class Ios::Internal::IosToolHandlerPrivate;
+ Ios::Internal::IosToolHandlerPrivate *d;
+};
+
+} // namespace Ios
+
+#endif // IOSTOOLHANDLER_H
diff --git a/src/plugins/plugins.pro b/src/plugins/plugins.pro
index 430e135b40..6460e1d5fd 100644
--- a/src/plugins/plugins.pro
+++ b/src/plugins/plugins.pro
@@ -50,6 +50,10 @@ SUBDIRS = \
clearcase \
baremetal
+macx {
+ SUBDIRS += ios
+}
+
isEmpty(QBS_INSTALL_DIR): QBS_INSTALL_DIR = $$(QBS_INSTALL_DIR)
exists(../shared/qbs/qbs.pro)|!isEmpty(QBS_INSTALL_DIR): \
SUBDIRS += \
diff --git a/src/plugins/plugins.qbs b/src/plugins/plugins.qbs
index 0d276cf5b4..a91139a8ac 100644
--- a/src/plugins/plugins.qbs
+++ b/src/plugins/plugins.qbs
@@ -33,6 +33,7 @@ Project {
"helloworld/helloworld.qbs",
"help/help.qbs",
"imageviewer/imageviewer.qbs",
+ "ios/ios.qbs",
"locator/locator.qbs",
"macros/macros.qbs",
"madde/madde.qbs",
diff --git a/src/plugins/projectexplorer/gcctoolchain.h b/src/plugins/projectexplorer/gcctoolchain.h
index 64dda77a30..1361f4b433 100644
--- a/src/plugins/projectexplorer/gcctoolchain.h
+++ b/src/plugins/projectexplorer/gcctoolchain.h
@@ -148,6 +148,7 @@ private:
class PROJECTEXPLORER_EXPORT ClangToolChain : public GccToolChain
{
public:
+ explicit ClangToolChain(Detection d);
QString type() const;
QString typeDisplayName() const;
QString makeCommand(const Utils::Environment &environment) const;
@@ -166,8 +167,6 @@ protected:
virtual CompilerFlags defaultCompilerFlags() const;
private:
- explicit ClangToolChain(Detection d);
-
friend class Internal::ClangToolChainFactory;
friend class ToolChainFactory;
};
diff --git a/src/plugins/qtsupport/qtsupportconstants.h b/src/plugins/qtsupport/qtsupportconstants.h
index 386709e1fa..8d6860b3f3 100644
--- a/src/plugins/qtsupport/qtsupportconstants.h
+++ b/src/plugins/qtsupport/qtsupportconstants.h
@@ -69,6 +69,7 @@ const char DESKTOP_PLATFORM[] = "Desktop";
const char EMBEDDED_LINUX_PLATFORM[] = "Embedded Linux";
const char WINDOWS_CE_PLATFORM[] = "Windows CE";
const char ANDROID_PLATFORM[] = "Android";
+const char IOS_PLATFORM[] = "Ios";
const char MEEGO_HARMATTAN_PLATFORM_TR[] = QT_TRANSLATE_NOOP("QtSupport", "MeeGo/Harmattan");
const char MAEMO_FREMANTLE_PLATFORM_TR[] = QT_TRANSLATE_NOOP("QtSupport", "Maemo/Fremantle");
@@ -76,6 +77,7 @@ const char DESKTOP_PLATFORM_TR[] = QT_TRANSLATE_NOOP("QtSupport", "Desktop");
const char EMBEDDED_LINUX_PLATFORM_TR[] = QT_TRANSLATE_NOOP("QtSupport", "Embedded Linux");
const char WINDOWS_CE_PLATFORM_TR[] = QT_TRANSLATE_NOOP("QtSupport", "Windows CE");
const char ANDROID_PLATFORM_TR[] = QT_TRANSLATE_NOOP("QtSupport", "Android");
+const char IOS_PLATFORM_TR[] = QT_TRANSLATE_NOOP("QtSupport", "Ios");
// QML wizard icon
// both the qt4projectmanager and the qmlprojectmanager do have qt quick wizards
diff --git a/src/tools/3rdparty/3rdparty.pro b/src/tools/3rdparty/3rdparty.pro
new file mode 100644
index 0000000000..9b9062f2d9
--- /dev/null
+++ b/src/tools/3rdparty/3rdparty.pro
@@ -0,0 +1,5 @@
+TEMPLATE = subdirs
+
+mac {
+ SUBDIRS += iossim
+}
diff --git a/src/tools/3rdparty/iossim/IOSSIM_LICENSE b/src/tools/3rdparty/iossim/IOSSIM_LICENSE
new file mode 100644
index 0000000000..162622f1db
--- /dev/null
+++ b/src/tools/3rdparty/iossim/IOSSIM_LICENSE
@@ -0,0 +1,31 @@
+Author: Landon Fuller <landonf@plausiblelabs.com>
+Copyright (c) 2008-2011 Plausible Labs Cooperative, Inc.
+All rights reserved.
+
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the "Software"), to deal in the Software without
+restriction, including without limitation the rights to use,
+copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+Modifications made by the following entities are licensed as above:
+- Jeff Haynie, Appcelerator, Inc.
+- https://github.com/hborders
+- http://pivotallabs.com/users/scoward/blog
+- Eloy Duran, Fingertips <eloy@fngtps.com>
+- Fawzi Mohamed, digia <fawzi.mohamed@digia.com>
diff --git a/src/tools/3rdparty/iossim/Info.plist b/src/tools/3rdparty/iossim/Info.plist
new file mode 100644
index 0000000000..bc69bf3873
--- /dev/null
+++ b/src/tools/3rdparty/iossim/Info.plist
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundlePackageType</key>
+ <string>APPL</string>
+ <key>CFBundleSignature</key>
+ <string>@TYPEINFO@</string>
+ <key>CFBundleExecutable</key>
+ <string>@EXECUTABLE@</string>
+ <key>CFBundleIdentifier</key>
+ <string>org.qt-project.qt-creator.iosTool</string>
+ <key>LSUIElement</key>
+ <string>1</string>
+</dict>
+</plist>
diff --git a/src/tools/3rdparty/iossim/iossim.pro b/src/tools/3rdparty/iossim/iossim.pro
new file mode 100644
index 0000000000..b613d7046e
--- /dev/null
+++ b/src/tools/3rdparty/iossim/iossim.pro
@@ -0,0 +1,53 @@
+CONFIG += console
+
+CONFIG -= app_bundle
+
+include(../../../../qtcreator.pri)
+
+# Prevent from popping up in the dock when launched.
+# We embed the Info.plist file, so the application doesn't need to
+# be a bundle.
+QMAKE_LFLAGS += -sectcreate __TEXT __info_plist $$shell_quote($$PWD/Info.plist) \
+ -fobjc-link-runtime
+
+LIBS += \
+ -framework Foundation \
+ -framework CoreServices \
+ -framework ApplicationServices \
+ -framework CoreFoundation \
+ -F/System/Library/PrivateFrameworks \
+ -framework IOKit
+
+iPhoneSimulatorRemoteClientDirectLinking {
+ LIBS += \
+ -F/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/Library/PrivateFrameworks \
+ -F/Applications/Xcode.app/Contents/OtherFrameworks
+
+ QMAKE_LFLAGS += -Wl,-rpath,/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/Library/PrivateFrameworks \
+ -Wl,-rpath,/Applications/Xcode.app/Contents/OtherFrameworks \
+ LIBS += \
+ -framework iPhoneSimulatorRemoteClient
+ QMAKE_RPATHDIR += /Applications/Xcode5-DP5.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/Library/PrivateFrameworks \
+ /Applications/Xcode.app/Contents/OtherFrameworks
+ /System/Library/PrivateFrameworks \
+}
+
+TEMPLATE = app
+
+DESTDIR = $$IDE_BIN_PATH
+include(../../../rpath.pri)
+
+OBJECTIVE_SOURCES += \
+ main.mm \
+ nsprintf.m \
+ nsstringexpandPath.m \
+ iphonesimulator.m
+
+HEADERS += \
+ iphonesimulator.h \
+ nsprintf.h \
+ nsstringexpandpath.h \
+ version.h \
+ iphonesimulatorremoteclient/iphonesimulatorremoteclient.h
+
+OTHER_FILES = LICENSE
diff --git a/src/tools/3rdparty/iossim/iossim.qbs b/src/tools/3rdparty/iossim/iossim.qbs
new file mode 100644
index 0000000000..d78ef5361e
--- /dev/null
+++ b/src/tools/3rdparty/iossim/iossim.qbs
@@ -0,0 +1,27 @@
+import qbs.base 1.0
+import "../../QtcTool.qbs" as QtcTool
+
+
+QtcTool {
+ name: "iossim"
+ condition: qbs.targetOS.contains("osx")
+
+ Depends { name: "Qt"; submodules: ["widgets"] }
+ Depends { name: "app_version_header" }
+
+ files: [
+ "main.mm",
+ "nsprintf.m",
+ "nsstringexpandPath.m",
+ "iphonesimulator.m",
+ "iphonesimulator.h",
+ "nsprintf.h",
+ "nsstringexpandpath.h",
+ "version.h",
+ "iphonesimulatorremoteclient/iphonesimulatorremoteclient.h"
+ ]
+ cpp.linkerFlags: base.concat(["-sectcreate", "__TEXT", "__info_plist", path + "/Info.plist",
+ "-fobjc-link-runtime"])
+ cpp.frameworks: base.concat(["Foundation", "CoreServices", "ApplicationServices", "IOKit"])
+ cpp.frameworkPaths: base.concat("/System/Library/PrivateFrameworks")
+}
diff --git a/src/tools/3rdparty/iossim/iphonesimulator.h b/src/tools/3rdparty/iossim/iphonesimulator.h
new file mode 100644
index 0000000000..bb7588f45b
--- /dev/null
+++ b/src/tools/3rdparty/iossim/iphonesimulator.h
@@ -0,0 +1,40 @@
+/* Author: Landon Fuller <landonf@plausiblelabs.com>
+ * Copyright (c) 2008-2011 Plausible Labs Cooperative, Inc.
+ * All rights reserved.
+ *
+ * See the IOSSIM_LICENSE file in this directory for the license on the source code in this file.
+ */
+
+#import <Foundation/Foundation.h>
+#import "iphonesimulatorremoteclient/iphonesimulatorremoteclient.h"
+#import "version.h"
+
+@interface iPhoneSimulator : NSObject <DTiPhoneSimulatorSessionDelegate> {
+@private
+ DTiPhoneSimulatorSystemRoot *sdkRoot;
+ NSFileHandle *stdoutFileHandle;
+ NSFileHandle *stderrFileHandle;
+ DTiPhoneSimulatorSession *session;
+ NSTimer *pidCheckingTimer;
+ BOOL startOnly;
+ BOOL exitOnStartup;
+ BOOL shouldWaitDebugger;
+ BOOL shouldStartDebugger;
+ BOOL useGDB;
+ BOOL verbose;
+ BOOL alreadyPrintedData;
+ BOOL retinaDevice;
+ BOOL tallDevice;
+}
+
+- (id)init;
+- (void)dealloc;
+- (void)runWithArgc:(int)argc argv:(char **)argv;
+
+- (void)createStdioFIFO:(NSFileHandle **)fileHandle ofType:(NSString *)type atPath:(NSString **)path;
+- (void)removeStdioFIFO:(NSFileHandle *)fileHandle atPath:(NSString *)path;
+- (void)stop;
+- (void)checkPid:(NSTimer *)timer;
+- (void)doExit:(int)errorCode;
+
+@end
diff --git a/src/tools/3rdparty/iossim/iphonesimulator.m b/src/tools/3rdparty/iossim/iphonesimulator.m
new file mode 100644
index 0000000000..30a3751eda
--- /dev/null
+++ b/src/tools/3rdparty/iossim/iphonesimulator.m
@@ -0,0 +1,544 @@
+/* Author: Landon Fuller <landonf@plausiblelabs.com>
+ * Copyright (c) 2008-2011 Plausible Labs Cooperative, Inc.
+ * All rights reserved.
+ *
+ * See the IOSSIM_LICENSE file in this directory for the license on the source code in this file.
+ */
+
+#import "iphonesimulator.h"
+#import "nsstringexpandpath.h"
+#import "nsprintf.h"
+#import <sys/types.h>
+#import <sys/stat.h>
+#import <objc/runtime.h>
+
+NSString *simulatorPrefrencesName = @"com.apple.iphonesimulator";
+NSString *deviceProperty = @"SimulateDevice";
+NSString *deviceIphoneRetina3_5Inch = @"iPhone (Retina 3.5-inch)";
+NSString *deviceIphoneRetina4_0Inch = @"iPhone (Retina 4-inch)";
+NSString *deviceIphone = @"iPhone";
+NSString *deviceIpad = @"iPad";
+NSString *deviceIpadRetina = @"iPad (Retina)";
+
+/**
+ * A simple iPhoneSimulatorRemoteClient framework.
+ */
+@implementation iPhoneSimulator
+
+- (id)init {
+ self = [super init];
+ session = nil;
+ pidCheckingTimer = nil;
+ return self;
+}
+
+- (void)dealloc {
+ [session release];
+ [pidCheckingTimer release];
+ [super dealloc];
+}
+
+- (void)doExit:(int)errorCode {
+ nsprintf(@"<exit code=\"%d\"/>", errorCode);
+ nsprintf(@"</query_result>");
+ fflush(stdout);
+ fflush(stderr);
+ exit(errorCode);
+}
+
+- (void) printUsage {
+ fprintf(stdout, "<msg>Usage: iossim <command> <options> [--args ...]\n");
+ fprintf(stdout, "\n");
+ fprintf(stdout, "Commands:\n");
+ fprintf(stdout, " showsdks List the available iOS SDK versions\n");
+ fprintf(stdout, " launch <application path> Launch the application at the specified path on the iOS Simulator\n");
+ fprintf(stdout, " start Launch iOS Simulator without an app\n");
+ fprintf(stdout, "\n");
+ fprintf(stdout, "Options:\n");
+ fprintf(stdout, " --version Print the version of ios-sim\n");
+ fprintf(stdout, " --developer-path <developerDir> path to the developer directory (in Xcode)");
+ fprintf(stdout, " --help Show this help text\n");
+ fprintf(stdout, " --verbose Set the output level to verbose\n");
+ fprintf(stdout, " --exit Exit after startup\n");
+ fprintf(stdout, " --wait-for-debugger Wait for debugger to attach\n");
+ fprintf(stdout, " --debug Attach LLDB to the application on startup\n");
+ fprintf(stdout, " --use-gdb Use GDB instead of LLDB. (Requires --debug)\n");
+ fprintf(stdout, " --sdk <sdkversion> The iOS SDK version to run the application on (defaults to the latest)\n");
+ fprintf(stdout, " --family <device family> The device type that should be simulated (defaults to `iphone')\n");
+ fprintf(stdout, " --retina Start a retina device\n");
+ fprintf(stdout, " --tall In combination with --retina flag, start the tall version of the retina device (e.g. iPhone 5 (4-inch))\n");
+ fprintf(stdout, " --uuid <uuid> A UUID identifying the session (is that correct?)\n");
+ fprintf(stdout, " --env <environment file path> A plist file containing environment key-value pairs that should be set\n");
+ fprintf(stdout, " --setenv NAME=VALUE Set an environment variable\n");
+ fprintf(stdout, " --stdout <stdout file path> The path where stdout of the simulator will be redirected to (defaults to stdout of ios-sim)\n");
+ fprintf(stdout, " --stderr <stderr file path> The path where stderr of the simulator will be redirected to (defaults to stderr of ios-sim)\n");
+ fprintf(stdout, " --timeout <seconds> The timeout time to wait for a response from the Simulator. Default value: 30 seconds\n");
+ fprintf(stdout, " --args <...> All following arguments will be passed on to the application</msg>\n");
+ fflush(stdout);
+}
+
+
+- (int) showSDKs {
+ id tClass = objc_getClass("DTiPhoneSimulatorSystemRoot");
+ if (tClass == nil) {
+ nsprintf(@"<msg>DTiPhoneSimulatorSystemRoot class is nil.</msg>");
+ return EXIT_FAILURE;
+ }
+ NSArray *roots = [tClass knownRoots];
+
+ nsprintf(@"<device_info>");
+ for (NSUInteger i = 0; i < [roots count]; ++i) {
+ DTiPhoneSimulatorSystemRoot *root = [roots objectAtIndex:i];
+ nsprintf(@"<item><key>sdk%d_name</key><value>%@</value></item>", i, [root sdkDisplayName]);
+ nsprintf(@"<item><key>sdk%d_version</key><value>%@</value></item>", i, [root sdkVersion]);
+ nsprintf(@"<item><key>sdk%d_sysroot</key><value>%@</value></item>", i, [root sdkRootPath]);
+ }
+ nsprintf(@"</device_info>");
+ return EXIT_SUCCESS;
+}
+
+
+- (void)session:(DTiPhoneSimulatorSession *)mySession didEndWithError:(NSError *)error {
+ if (verbose) {
+ nsprintf(@"<msg>Session did end with error %@</msg>", error);
+ }
+
+ if (stderrFileHandle != nil) {
+ NSString *stderrPath = [[mySession sessionConfig] simulatedApplicationStdErrPath];
+ [self removeStdioFIFO:stderrFileHandle atPath:stderrPath];
+ }
+
+ if (stdoutFileHandle != nil) {
+ NSString *stdoutPath = [[mySession sessionConfig] simulatedApplicationStdOutPath];
+ [self removeStdioFIFO:stdoutFileHandle atPath:stdoutPath];
+ }
+
+ if (error != nil)
+ [self doExit:EXIT_FAILURE];
+ else
+ [self doExit:EXIT_SUCCESS];
+}
+
+
+- (void)session:(DTiPhoneSimulatorSession *)mySession didStart:(BOOL)started withError:(NSError *)error {
+ if (startOnly && mySession) {
+ nsprintf(@"<msg>Simulator started (no session)</msg>");
+ [self doExit:EXIT_SUCCESS];
+ return;
+ }
+ if (started) {
+ if (shouldStartDebugger) {
+ char*args[4] = { NULL, NULL, (char*)[[[mySession simulatedApplicationPID] description] UTF8String], NULL };
+ if (useGDB) {
+ args[0] = "gdb";
+ args[1] = "program";
+ } else {
+ args[0] = "lldb";
+ args[1] = "--attach-pid";
+ }
+ // The parent process must live on to process the stdout/stderr fifos,
+ // so start the debugger as a child process.
+ pid_t child_pid = fork();
+ if (child_pid == 0) {
+ execvp(args[0], args);
+ } else if (child_pid < 0) {
+ nsprintf(@"<msg>Could not start debugger process: %@</msg>", errno);
+ [self doExit:EXIT_FAILURE];
+ return;
+ }
+ }
+ if (verbose) {
+ nsprintf(@"<msg>Session started</msg>");
+ }
+ if (exitOnStartup) {
+ [self doExit:EXIT_SUCCESS];
+ return;
+ }
+ nsprintf(@"<inferior_pid>%@</inferior_pid>", [session simulatedApplicationPID]);
+ pidCheckingTimer = [[NSTimer scheduledTimerWithTimeInterval:5.0 target:self
+ selector:@selector(checkPid:) userInfo:nil repeats: TRUE] retain];
+ } else {
+ nsprintf(@"<msg>Session could not be started: %@</msg>", error);
+ [self doExit:EXIT_FAILURE];
+ }
+}
+
+- (void)stop {
+ if (session)
+ [session requestEndWithTimeout: 0.1];
+}
+
+- (void)checkPid:(NSTimer *)timer {
+ (void)timer;
+ if (session && [[session simulatedApplicationPID]intValue] > 0) {
+ if (kill((pid_t)[[session simulatedApplicationPID]intValue], 0) == -1) {
+ nsprintf(@"<msg>app stopped</msg>");
+ [self doExit:EXIT_SUCCESS];
+ return;
+ }
+ }
+}
+- (void)stdioDataIsAvailable:(NSNotification *)notification {
+ [[notification object] readInBackgroundAndNotify];
+ NSData *data = [[notification userInfo] valueForKey:NSFileHandleNotificationDataItem];
+ NSString *str = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; /* dangerous if partially encoded data is at the boundary */
+ if (!alreadyPrintedData) {
+ if ([str length] == 0) {
+ return;
+ } else {
+ alreadyPrintedData = YES;
+ }
+ }
+ if ([notification object] == stdoutFileHandle) {
+ printf("<app_output>%s</app_output>\n", [str UTF8String]);
+ } else {
+ nsprintf(@"<app_output>%@</app_output>", str); // handle stderr differently?
+ }
+ fflush(stdout);
+}
+
+
+- (void)createStdioFIFO:(NSFileHandle **)fileHandle ofType:(NSString *)type atPath:(NSString **)path {
+ *path = [NSString stringWithFormat:@"%@/ios-sim-%@-pipe-%d", NSTemporaryDirectory(), type, (int)time(NULL)];
+ if (mkfifo([*path UTF8String], S_IRUSR | S_IWUSR) == -1) {
+ nsprintf(@"<msg>Unable to create %@ named pipe `%@'</msg>", type, *path);
+ [self doExit:EXIT_FAILURE];
+ } else {
+ if (verbose) {
+ nsprintf(@"<msg>Creating named pipe at `%@'</msg>", *path);
+ }
+ int fd = open([*path UTF8String], O_RDONLY | O_NDELAY);
+ *fileHandle = [[[NSFileHandle alloc] initWithFileDescriptor:fd] retain];
+ [*fileHandle readInBackgroundAndNotify];
+ [[NSNotificationCenter defaultCenter] addObserver:self
+ selector:@selector(stdioDataIsAvailable:)
+ name:NSFileHandleReadCompletionNotification
+ object:*fileHandle];
+ }
+}
+
+
+- (void)removeStdioFIFO:(NSFileHandle *)fileHandle atPath:(NSString *)path {
+ if (verbose) {
+ nsprintf(@"<msg>Removing named pipe at `%@'</msg>", path);
+ }
+ [fileHandle closeFile];
+ [fileHandle release];
+ if (![[NSFileManager defaultManager] removeItemAtPath:path error:NULL]) {
+ nsprintf(@"<msg>Unable to remove named pipe `%@'</msg>", path);
+ }
+}
+
+
+- (int)launchApp:(NSString *)path withFamily:(NSString *)family
+ uuid:(NSString *)uuid
+ environment:(NSDictionary *)environment
+ stdoutPath:(NSString *)stdoutPath
+ stderrPath:(NSString *)stderrPath
+ timeout:(NSTimeInterval)timeout
+ args:(NSArray *)args {
+ DTiPhoneSimulatorApplicationSpecifier *appSpec;
+ DTiPhoneSimulatorSessionConfig *config;
+ NSError *error = 0;
+ id tClass;
+
+ NSFileManager *fileManager = [[[NSFileManager alloc] init] autorelease];
+ if (!startOnly && ![fileManager fileExistsAtPath:path]) {
+ nsprintf(@"<msg>Application path %@ doesn't exist!</msg>", path);
+ return EXIT_FAILURE;
+ }
+
+ /* Create the app specifier */
+ tClass = objc_getClass("DTiPhoneSimulatorApplicationSpecifier");
+ if (tClass == nil) {
+ nsprintf(@"<msg>DTiPhoneSimulatorApplicationSpecifier class is nil.</msg>");
+ return EXIT_FAILURE;
+ }
+ appSpec = startOnly ? nil : [tClass specifierWithApplicationPath:path];
+
+ if (verbose) {
+ nsprintf(@"<msg>App Spec: %@</msg>", appSpec);
+ nsprintf(@"SDK Root: %@", sdkRoot);
+
+ for (id key in environment) {
+ nsprintf(@"<msg>Env: %@ = %@</msg>", key, [environment objectForKey:key]);
+ }
+ }
+
+ /* Set up the session configuration */
+ tClass = objc_getClass("DTiPhoneSimulatorSessionConfig");
+ if (tClass == nil) {
+ nsprintf(@"<msg>DTiPhoneSimulatorApplicationSpecifier class is nil.</msg>");
+ return EXIT_FAILURE;
+ }
+ config = [[[tClass alloc] init] autorelease];
+ [config setApplicationToSimulateOnStart:appSpec];
+ [config setSimulatedSystemRoot:sdkRoot];
+ [config setSimulatedApplicationShouldWaitForDebugger:shouldStartDebugger];
+
+ [config setSimulatedApplicationLaunchArgs:args];
+ [config setSimulatedApplicationLaunchEnvironment:environment];
+
+ if (stderrPath) {
+ stderrFileHandle = nil;
+ } else if (!exitOnStartup) {
+ [self createStdioFIFO:&stderrFileHandle ofType:@"stderr" atPath:&stderrPath];
+ }
+ [config setSimulatedApplicationStdErrPath:stderrPath];
+
+ if (stdoutPath) {
+ stdoutFileHandle = nil;
+ } else if (!exitOnStartup) {
+ [self createStdioFIFO:&stdoutFileHandle ofType:@"stdout" atPath:&stdoutPath];
+ }
+ [config setSimulatedApplicationStdOutPath:stdoutPath];
+
+ [config setLocalizedClientName: @"iossim"];
+
+ // this was introduced in 3.2 of SDK
+ if ([config respondsToSelector:@selector(setSimulatedDeviceFamily:)]) {
+ if (family == nil) {
+ family = @"iphone";
+ }
+
+ if (verbose) {
+ nsprintf(@"using device family %@",family);
+ }
+
+ if ([family isEqualToString:@"ipad"]) {
+[config setSimulatedDeviceFamily:[NSNumber numberWithInt:2]];
+ } else{
+ [config setSimulatedDeviceFamily:[NSNumber numberWithInt:1]];
+ }
+ }
+
+ [self changeDeviceType:family retina:retinaDevice isTallDevice:tallDevice];
+
+ /* Start the session */
+ tClass = objc_getClass("DTiPhoneSimulatorSession");
+ if (tClass == nil) {
+ nsprintf(@"<msg>DTiPhoneSimulatorSession class is nil.</msg>");
+ return EXIT_FAILURE;
+ }
+ session = [[tClass alloc] init];
+ [session setDelegate:self];
+ if (uuid != nil){
+ [session setUuid:uuid];
+ }
+
+ if (![session requestStartWithConfig:config timeout:timeout error:&error]) {
+ nsprintf(@"<msg>Could not start simulator session: %@</msg>", error);
+ return EXIT_FAILURE;
+ }
+
+ return EXIT_SUCCESS;
+}
+
+- (void) changeDeviceType:(NSString *)family retina:(BOOL)retina isTallDevice:(BOOL)isTallDevice {
+ NSString *devicePropertyValue;
+ if (retina) {
+ if (verbose) {
+ nsprintf(@"<msg>using retina</msg>");
+ }
+ if ([family isEqualToString:@"ipad"]) {
+ devicePropertyValue = deviceIpadRetina;
+ }
+ else {
+ if (isTallDevice) {
+ devicePropertyValue = deviceIphoneRetina4_0Inch;
+ } else {
+ devicePropertyValue = deviceIphoneRetina3_5Inch;
+ }
+ }
+ } else {
+ if ([family isEqualToString:@"ipad"]) {
+ devicePropertyValue = deviceIpad;
+ } else {
+ devicePropertyValue = deviceIphone;
+ }
+ }
+ CFPreferencesSetAppValue((CFStringRef)deviceProperty, (CFPropertyListRef)devicePropertyValue, (CFStringRef)simulatorPrefrencesName);
+ CFPreferencesAppSynchronize((CFStringRef)simulatorPrefrencesName);
+}
+
+
+/**
+ * Execute 'main'
+ */
+- (void)runWithArgc:(int)argc argv:(char **)argv {
+ if (argc < 2) {
+ [self printUsage];
+ exit(EXIT_FAILURE);
+ }
+
+ retinaDevice = NO;
+ tallDevice = NO;
+ exitOnStartup = NO;
+ alreadyPrintedData = NO;
+ startOnly = strcmp(argv[1], "start") == 0;
+ nsprintf(@"<query_result>");
+
+ if (strcmp(argv[1], "showsdks") == 0) {
+ [self doExit:[self showSDKs]];
+ return;
+ } else if (strcmp(argv[1], "launch") == 0 || startOnly) {
+ if (strcmp(argv[1], "launch") == 0 && argc < 3) {
+ nsprintf(@"<msg>Missing application path argument</msg>");
+ [self printUsage];
+ [self doExit:EXIT_FAILURE];
+ return;
+ }
+
+ NSString *appPath = nil;
+ int argOffset;
+ if (startOnly) {
+ argOffset = 2;
+ }
+ else {
+ argOffset = 3;
+ appPath = [[NSString stringWithUTF8String:argv[2]] expandPath];
+ }
+
+ NSString *family = nil;
+ NSString *uuid = nil;
+ NSString *stdoutPath = nil;
+ NSString *stderrPath = nil;
+ NSTimeInterval timeout = 30;
+ NSMutableDictionary *environment = [NSMutableDictionary dictionary];
+
+ int i = argOffset;
+ for (; i < argc; i++) {
+ if (strcmp(argv[i], "--version") == 0) {
+ printf("%s\n", IOS_SIM_VERSION);
+ exit(EXIT_SUCCESS);
+ } else if (strcmp(argv[i], "--help") == 0) {
+ [self printUsage];
+ exit(EXIT_SUCCESS);
+ } else if (strcmp(argv[i], "--verbose") == 0) {
+ verbose = YES;
+ } else if (strcmp(argv[i], "--exit") == 0) {
+ exitOnStartup = YES;
+ } else if (strcmp(argv[i], "--wait-for-debugger") == 0) {
+ shouldWaitDebugger = YES;
+ } else if (strcmp(argv[i], "--debug") == 0) {
+ shouldWaitDebugger = YES;
+ shouldStartDebugger = YES;
+ } else if (strcmp(argv[i], "--use-gdb") == 0) {
+ useGDB = YES;
+ } else if (strcmp(argv[i], "--developer-path") == 0) {
+ ++i;
+ } else if (strcmp(argv[i], "--timeout") == 0) {
+ if (i + 1 < argc) {
+ timeout = [[NSString stringWithUTF8String:argv[++i]] doubleValue];
+ NSLog(@"<msg>Timeout: %f second(s)</msg>", timeout);
+ }
+ }
+ else if (strcmp(argv[i], "--sdk") == 0) {
+ i++;
+ NSString* ver = [NSString stringWithCString:argv[i] encoding:NSUTF8StringEncoding];
+ id tClass = objc_getClass("DTiPhoneSimulatorSystemRoot");
+ NSArray *roots;
+ if (tClass == nil) {
+ nsprintf(@"<msg>DTiPhoneSimulatorSystemRoot class is nil.</msg>");
+ [self doExit:EXIT_FAILURE];
+ return;
+ }
+ roots = [tClass knownRoots];
+ for (DTiPhoneSimulatorSystemRoot *root in roots) {
+ NSString *v = [root sdkVersion];
+ if ([v isEqualToString:ver]) {
+ sdkRoot = root;
+ break;
+ }
+ }
+ if (sdkRoot == nil) {
+ fprintf(stdout,"<msg>Unknown or unsupported SDK version: %s</msg>\n",argv[i]);
+ [self showSDKs];
+ exit(EXIT_FAILURE);
+ }
+ } else if (strcmp(argv[i], "--family") == 0) {
+ i++;
+ family = [NSString stringWithUTF8String:argv[i]];
+ } else if (strcmp(argv[i], "--uuid") == 0) {
+ i++;
+ uuid = [NSString stringWithUTF8String:argv[i]];
+ } else if (strcmp(argv[i], "--setenv") == 0) {
+ i++;
+ NSArray *parts = [[NSString stringWithUTF8String:argv[i]] componentsSeparatedByString:@"="];
+ [environment setObject:[parts objectAtIndex:1] forKey:[parts objectAtIndex:0]];
+ } else if (strcmp(argv[i], "--env") == 0) {
+ i++;
+ NSString *envFilePath = [[NSString stringWithUTF8String:argv[i]] expandPath];
+ environment = [NSDictionary dictionaryWithContentsOfFile:envFilePath];
+ if (!environment) {
+ fprintf(stdout, "<msg>Could not read environment from file: %s</msg>\n", argv[i]);
+ [self printUsage];
+ exit(EXIT_FAILURE);
+ }
+ } else if (strcmp(argv[i], "--stdout") == 0) {
+ i++;
+ stdoutPath = [[NSString stringWithUTF8String:argv[i]] expandPath];
+ NSLog(@"stdoutPath: %@", stdoutPath);
+ } else if (strcmp(argv[i], "--stderr") == 0) {
+ i++;
+ stderrPath = [[NSString stringWithUTF8String:argv[i]] expandPath];
+ NSLog(@"stderrPath: %@", stderrPath);
+ } else if (strcmp(argv[i], "--retina") == 0) {
+ retinaDevice = YES;
+ } else if (strcmp(argv[i], "--tall") == 0) {
+ tallDevice = YES;
+ } else if (strcmp(argv[i], "--args") == 0) {
+ i++;
+ break;
+ } else {
+ printf("<msg>unrecognized argument:%s</msg>\n", argv[i]);
+ [self printUsage];
+ [self doExit:EXIT_FAILURE];
+ return;
+ }
+ }
+ NSMutableArray *args = [NSMutableArray arrayWithCapacity:MAX(argc - i,0)];
+ for (; i < argc; i++) {
+ [args addObject:[NSString stringWithUTF8String:argv[i]]];
+ }
+
+ if (sdkRoot == nil) {
+ id tClass = objc_getClass("DTiPhoneSimulatorSystemRoot");
+ if (tClass == nil) {
+ nsprintf(@"<msg>DTiPhoneSimulatorSystemRoot class is nil.</msg>");
+ [self doExit:EXIT_FAILURE];
+ return;
+ }
+ sdkRoot = [tClass defaultRoot];
+ }
+
+ /* Don't exit, adds to runloop */
+ int res = [self launchApp:appPath
+ withFamily:family
+ uuid:uuid
+ environment:environment
+ stdoutPath:stdoutPath
+ stderrPath:stderrPath
+ timeout:timeout
+ args:args];
+ nsprintf(@"<app_started status=\"%@\" />", ((res == 0) ? @"SUCCESS" : @"FAILURE"));
+ fflush(stdout);
+ fflush(stderr);
+ if (res != 0)
+ [self doExit:EXIT_FAILURE];
+ } else {
+ if (argc == 2 && strcmp(argv[1], "--help") == 0) {
+ [self printUsage];
+ [self doExit:EXIT_SUCCESS];
+ } else if (argc == 2 && strcmp(argv[1], "--version") == 0) {
+ printf("%s\n", IOS_SIM_VERSION);
+ [self doExit:EXIT_SUCCESS];
+ } else {
+ fprintf(stdout, "<msg>Unknown command</msg>\n");
+ [self printUsage];
+ [self doExit:EXIT_FAILURE];
+ }
+ }
+}
+
+@end
diff --git a/src/tools/3rdparty/iossim/iphonesimulatorremoteclient/iphonesimulatorremoteclient.h b/src/tools/3rdparty/iossim/iphonesimulatorremoteclient/iphonesimulatorremoteclient.h
new file mode 100644
index 0000000000..abf69c8067
--- /dev/null
+++ b/src/tools/3rdparty/iossim/iphonesimulatorremoteclient/iphonesimulatorremoteclient.h
@@ -0,0 +1,126 @@
+#import <Cocoa/Cocoa.h>
+
+/*
+ * File: /Developer/Platforms/iPhoneSimulator.platform/Developer/Library/PrivateFrameworks/iPhoneSimulatorRemoteClient.framework/Versions/A/iPhoneSimulatorRemoteClient
+ * Arch: Intel 80x86 (i386)
+ * Current version: 12.0.0, Compatibility version: 1.0.0
+ *
+ * See the IOSSIM_LICENSE file in the parent directory for the license on the source code in this file.
+ */
+
+@class DTiPhoneSimulatorSession;
+
+@protocol DTiPhoneSimulatorSessionDelegate
+
+- (void) session: (DTiPhoneSimulatorSession *) session didEndWithError: (NSError *) error;
+- (void) session: (DTiPhoneSimulatorSession *) session didStart: (BOOL) started withError: (NSError *) error;
+
+@end
+
+@interface DTiPhoneSimulatorApplicationSpecifier : NSObject <NSCopying>
+{
+ NSString *_appPath;
+ NSString *_bundleID;
+}
+
++ (id) specifierWithApplicationPath: (NSString *) appPath;
++ (id) specifierWithApplicationBundleIdentifier: (NSString *) bundleID;
+- (NSString *) bundleID;
+- (void) setBundleID: (NSString *) bundleId;
+- (NSString *) appPath;
+- (void) setAppPath: (NSString *) appPath;
+
+@end
+
+@interface DTiPhoneSimulatorSystemRoot : NSObject <NSCopying>
+{
+ NSString *sdkRootPath;
+ NSString *sdkVersion;
+ NSString *sdkDisplayName;
+}
+
++ (id) defaultRoot;
+
++ (id)rootWithSDKPath:(id)fp8;
++ (id)rootWithSDKVersion:(id)fp8;
++ (NSArray *) knownRoots;
+- (id)initWithSDKPath:(id)fp8;
+- (id)sdkDisplayName;
+- (void)setSdkDisplayName:(id)fp8;
+- (id)sdkVersion;
+- (void)setSdkVersion:(id)fp8;
+- (id)sdkRootPath;
+- (void)setSdkRootPath:(id)fp8;
+
+@end
+
+
+
+@interface DTiPhoneSimulatorSessionConfig : NSObject <NSCopying>
+{
+ NSString *_localizedClientName;
+ DTiPhoneSimulatorSystemRoot *_simulatedSystemRoot;
+ DTiPhoneSimulatorApplicationSpecifier *_applicationToSimulateOnStart;
+ NSArray *_simulatedApplicationLaunchArgs;
+ NSDictionary *_simulatedApplicationLaunchEnvironment;
+ BOOL _simulatedApplicationShouldWaitForDebugger;
+ NSString *_simulatedApplicationStdOutPath;
+ NSString *_simulatedApplicationStdErrPath;
+}
+
+- (id)simulatedApplicationStdErrPath;
+- (void)setSimulatedApplicationStdErrPath:(id)fp8;
+- (id)simulatedApplicationStdOutPath;
+- (void)setSimulatedApplicationStdOutPath:(id)fp8;
+- (id)simulatedApplicationLaunchEnvironment;
+- (void)setSimulatedApplicationLaunchEnvironment:(id)fp8;
+- (id)simulatedApplicationLaunchArgs;
+- (void)setSimulatedApplicationLaunchArgs:(id)fp8;
+
+- (DTiPhoneSimulatorApplicationSpecifier *) applicationToSimulateOnStart;
+- (void) setApplicationToSimulateOnStart: (DTiPhoneSimulatorApplicationSpecifier *) appSpec;
+- (DTiPhoneSimulatorSystemRoot *) simulatedSystemRoot;
+- (void) setSimulatedSystemRoot: (DTiPhoneSimulatorSystemRoot *) simulatedSystemRoot;
+
+
+- (BOOL) simulatedApplicationShouldWaitForDebugger;
+- (void) setSimulatedApplicationShouldWaitForDebugger: (BOOL) waitForDebugger;
+
+- (id)localizedClientName;
+- (void)setLocalizedClientName:(id)fp8;
+
+// Added in 3.2 to support iPad/iPhone device families
+- (void)setSimulatedDeviceFamily:(NSNumber*)family;
+
+@end
+
+
+@interface DTiPhoneSimulatorSession : NSObject {
+ NSString *_uuid;
+ id <DTiPhoneSimulatorSessionDelegate> _delegate;
+ NSNumber *_simulatedApplicationPID;
+ int _sessionLifecycleProgress;
+ NSTimer *_timeoutTimer;
+ DTiPhoneSimulatorSessionConfig *_sessionConfig;
+ struct ProcessSerialNumber _simulatorPSN;
+}
+
+- (BOOL) requestStartWithConfig: (DTiPhoneSimulatorSessionConfig *) config timeout: (NSTimeInterval) timeout error: (NSError **) outError;
+- (void) requestEndWithTimeout: (NSTimeInterval) timeout;
+
+- (id)sessionConfig;
+- (void)setSessionConfig:(id)fp8;
+- (id)timeoutTimer;
+- (void)setTimeoutTimer:(id)fp8;
+- (int)sessionLifecycleProgress;
+- (void)setSessionLifecycleProgress:(int)fp8;
+- (id)simulatedApplicationPID;
+- (void)setSimulatedApplicationPID:(id)fp8;
+
+- (id<DTiPhoneSimulatorSessionDelegate>) delegate;
+- (void) setDelegate: (id<DTiPhoneSimulatorSessionDelegate>) delegate;
+
+- (id)uuid;
+- (void)setUuid:(id)fp8;
+
+@end
diff --git a/src/tools/3rdparty/iossim/main.mm b/src/tools/3rdparty/iossim/main.mm
new file mode 100644
index 0000000000..1f6d7ba412
--- /dev/null
+++ b/src/tools/3rdparty/iossim/main.mm
@@ -0,0 +1,64 @@
+/* Author: Landon Fuller <landonf@plausiblelabs.com>
+ * Copyright (c) 2008-2011 Plausible Labs Cooperative, Inc.
+ * All rights reserved.
+ *
+ * See the IOSSIM_LICENSE file in this directory for the license on the source code in this file.
+ */
+/* derived from https://github.com/phonegap/ios-sim */
+#import <AppKit/AppKit.h>
+#import "iphonesimulator.h"
+#include <QLibrary>
+#include <QGuiApplication>
+#include <QString>
+#include <QStringList>
+
+/* to do:
+ * - try to stop inferior when killed (or communicate with creator to allow killing the inferior)
+ * - remove unneeded functionality and streamline a bit
+ */
+
+/*
+ * Runs the iPhoneSimulator backed by a main runloop.
+ */
+int main (int argc, char *argv[]) {
+ QGuiApplication a(argc, argv);
+
+ //NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+
+ QString xcodePath = QLatin1String("/Applications/Xcode.app/Contents/Developer/");
+ for (int i = 0; i + 1 < argc; ++i) {
+ if (strcmp(argv[i], "--developer-path") == 0)
+ xcodePath = QCoreApplication::arguments().at(i+1);
+ }
+ if (!xcodePath.endsWith(QLatin1Char('/')))
+ xcodePath.append(QLatin1Char('/'));
+
+ /* manual loading of the private deps */
+ QStringList deps = QStringList()
+ << QLatin1String("/System/Library/PrivateFrameworks/DebugSymbols.framework/Versions/A/DebugSymbols")
+ << QLatin1String("/System/Library/PrivateFrameworks/CoreSymbolication.framework/CoreSymbolication")
+ << (xcodePath + QLatin1String("../OtherFrameworks/DevToolsFoundation.framework/DevToolsFoundation"));
+ foreach (const QString &libPath, deps) {
+ QLibrary *lib = new QLibrary(libPath);
+ //lib->setLoadHints(QLibrary::ExportExternalSymbolsHint);
+ if (!lib->load())
+ printf("<msg>error loading %s</msg>", libPath.toUtf8().constData());
+ }
+ QLibrary *libIPhoneSimulatorRemoteClient = new QLibrary(xcodePath
+ + QLatin1String("Platforms/iPhoneSimulator.platform/Developer/Library/PrivateFrameworks/iPhoneSimulatorRemoteClient.framework/iPhoneSimulatorRemoteClient"));
+ //libIPhoneSimulatorRemoteClient->setLoadHints(QLibrary::ResolveAllSymbolsHint|QLibrary::ExportExternalSymbolsHint);
+ if (!libIPhoneSimulatorRemoteClient->load())
+ printf("<msg>error loading iPhoneSimulatorRemoteClient</msg>");
+
+ iPhoneSimulator *sim = [[iPhoneSimulator alloc] init];
+
+ /* Execute command line handler */
+ [sim runWithArgc: argc argv: argv];
+
+ /* Run the loop to handle added input sources, if any */
+
+ int res = a.exec();
+ exit(res);
+ // [pool release];
+ return 0;
+}
diff --git a/src/tools/3rdparty/iossim/nsprintf.h b/src/tools/3rdparty/iossim/nsprintf.h
new file mode 100644
index 0000000000..cee7c00d28
--- /dev/null
+++ b/src/tools/3rdparty/iossim/nsprintf.h
@@ -0,0 +1,6 @@
+/*
+ * See the IOSSIM_LICENSE file in this directory for the license on the source code in this file.
+ */
+int nsvfprintf (FILE *stream, NSString *format, va_list args);
+int nsfprintf (FILE *stream, NSString *format, ...);
+int nsprintf (NSString *format, ...);
diff --git a/src/tools/3rdparty/iossim/nsprintf.m b/src/tools/3rdparty/iossim/nsprintf.m
new file mode 100644
index 0000000000..b7413f593c
--- /dev/null
+++ b/src/tools/3rdparty/iossim/nsprintf.m
@@ -0,0 +1,44 @@
+/*
+ * NSLog() clone, but writes to arbitrary output stream
+ *
+ * See the IOSSIM_LICENSE file in this directory for the license on the source code in this file.
+ */
+
+#import <Foundation/Foundation.h>
+#import <stdio.h>
+
+int nsvfprintf (FILE *stream, NSString *format, va_list args) {
+ int retval;
+
+ NSString *str = (NSString *) CFStringCreateWithFormatAndArguments(NULL, NULL, (CFStringRef) format, args);
+ retval = fprintf(stream, "%s\n", [str UTF8String]);
+ [str release];
+
+ return retval;
+}
+
+int nsfprintf (FILE *stream, NSString *format, ...) {
+ va_list ap;
+ int retval;
+
+ va_start(ap, format);
+ {
+ retval = nsvfprintf(stream, format, ap);
+ }
+ va_end(ap);
+
+ return retval;
+}
+
+int nsprintf (NSString *format, ...) {
+ va_list ap;
+ int retval;
+
+ va_start(ap, format);
+ {
+ retval = nsvfprintf(stdout, format, ap);
+ }
+ va_end(ap);
+
+ return retval;
+}
diff --git a/src/tools/3rdparty/iossim/nsstringexpandpath.h b/src/tools/3rdparty/iossim/nsstringexpandpath.h
new file mode 100644
index 0000000000..3eed139c0e
--- /dev/null
+++ b/src/tools/3rdparty/iossim/nsstringexpandpath.h
@@ -0,0 +1,11 @@
+/*
+ * See the IOSSIM_LICENSE file in this directory for the license on the source code in this file.
+ */
+
+#import <Foundation/Foundation.h>
+
+@interface NSString (ExpandPath)
+
+- (NSString *)expandPath;
+
+@end
diff --git a/src/tools/3rdparty/iossim/nsstringexpandpath.m b/src/tools/3rdparty/iossim/nsstringexpandpath.m
new file mode 100644
index 0000000000..53f43e3c12
--- /dev/null
+++ b/src/tools/3rdparty/iossim/nsstringexpandpath.m
@@ -0,0 +1,18 @@
+/*
+ * See the IOSSIM_LICENSE file in this directory for the license on the source code in this file.
+ */
+
+#import "nsstringexpandpath.h"
+
+@implementation NSString (ExpandPath)
+
+- (NSString *)expandPath {
+ if ([self isAbsolutePath]) {
+ return [self stringByStandardizingPath];
+ } else {
+ NSString *cwd = [[NSFileManager defaultManager] currentDirectoryPath];
+ return [[cwd stringByAppendingPathComponent:self] stringByStandardizingPath];
+ }
+}
+
+@end
diff --git a/src/tools/3rdparty/iossim/version.h b/src/tools/3rdparty/iossim/version.h
new file mode 100644
index 0000000000..9f8c0dfc91
--- /dev/null
+++ b/src/tools/3rdparty/iossim/version.h
@@ -0,0 +1 @@
+#define IOS_SIM_VERSION "1.8.2m"
diff --git a/src/tools/iostool/Info.plist b/src/tools/iostool/Info.plist
new file mode 100644
index 0000000000..bc69bf3873
--- /dev/null
+++ b/src/tools/iostool/Info.plist
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundlePackageType</key>
+ <string>APPL</string>
+ <key>CFBundleSignature</key>
+ <string>@TYPEINFO@</string>
+ <key>CFBundleExecutable</key>
+ <string>@EXECUTABLE@</string>
+ <key>CFBundleIdentifier</key>
+ <string>org.qt-project.qt-creator.iosTool</string>
+ <key>LSUIElement</key>
+ <string>1</string>
+</dict>
+</plist>
diff --git a/src/tools/iostool/iosdevicemanager.cpp b/src/tools/iostool/iosdevicemanager.cpp
new file mode 100644
index 0000000000..a78edaad76
--- /dev/null
+++ b/src/tools/iostool/iosdevicemanager.cpp
@@ -0,0 +1,1584 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** 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 "iosdevicemanager.h"
+#include <QLibrary>
+#include <QDebug>
+#include <QStringList>
+#include <QString>
+#include <QHash>
+#include <QMultiHash>
+#include <QTimer>
+#include <QThread>
+#include <QSettings>
+#include <mach/error.h>
+/* // annoying to import, do without
+#if QT_VERSION < 0x050000
+#include <private/qcore_mac_p.h>
+#else
+#include <QtCore/private/qcore_mac_p.h>
+#endif
+*/
+/* standard calling convention under Win32 is __stdcall */
+/* Note: When compiling Intel EFI (Extensible Firmware Interface) under MS Visual Studio, the */
+/* _WIN32 symbol is defined by the compiler even though it's NOT compiling code for Windows32 */
+#if defined(_WIN32) && !defined(EFI32) && !defined(EFI64)
+#define MDEV_API __stdcall
+#else
+#define MDEV_API
+#endif
+
+#include <CoreFoundation/CoreFoundation.h>
+#include <CoreFoundation/CFRunLoop.h>
+
+#ifdef MOBILE_DEV_DIRECT_LINK
+#include "MobileDevice.h"
+#endif
+
+static const bool debugGdbServer = false;
+static const bool debugAll = false;
+
+// ------- MobileDeviceLib interface --------
+namespace {
+
+#ifndef MOBILE_DEV_DIRECT_LINK
+/* Messages passed to device notification callbacks: passed as part of
+ * AMDeviceNotificationCallbackInfo. */
+enum ADNCI_MSG {
+ ADNCI_MSG_CONNECTED = 1,
+ ADNCI_MSG_DISCONNECTED = 2,
+ ADNCI_MSG_UNSUBSCRIBED = 3
+};
+#endif
+
+extern "C" {
+typedef unsigned int ServiceSocket; // match_port_t (i.e. natural_t) or socket (on windows, i.e sock_t)
+typedef int am_res_t; // mach_error_t
+
+#ifndef MOBILE_DEV_DIRECT_LINK
+class AMDeviceNotification;
+typedef const AMDeviceNotification *AMDeviceNotificationRef;
+class AMDevice;
+
+struct AMDeviceNotificationCallbackInfo {
+ AMDevice *_device;
+ unsigned int _message;
+ AMDeviceNotification *_subscription;
+};
+typedef void (MDEV_API *AMDeviceNotificationCallback)(AMDeviceNotificationCallbackInfo *, void *);
+typedef am_res_t (MDEV_API *AMDeviceInstallApplicationCallback)(CFDictionaryRef, void *);
+typedef AMDevice *AMDeviceRef;
+#endif
+typedef am_res_t (MDEV_API *AMDeviceMountImageCallback)(void *, void *);
+
+
+
+typedef void (MDEV_API *AMDSetLogLevelPtr)(int);
+typedef am_res_t (MDEV_API *AMDeviceNotificationSubscribePtr)(AMDeviceNotificationCallback,
+ unsigned int, unsigned int, void *,
+ const AMDeviceNotification **);
+typedef am_res_t (MDEV_API *AMDeviceNotificationUnsubscribePtr)(void *);
+typedef CFPropertyListRef (MDEV_API *AMDeviceCopyValuePtr)(AMDeviceRef,CFStringRef,CFStringRef);
+typedef unsigned int (MDEV_API *AMDeviceGetConnectionIDPtr)(AMDeviceRef);
+typedef CFStringRef (MDEV_API *AMDeviceCopyDeviceIdentifierPtr)(AMDeviceRef);
+typedef am_res_t (MDEV_API *AMDeviceConnectPtr)(AMDeviceRef);
+typedef am_res_t (MDEV_API *AMDevicePairPtr)(AMDeviceRef);
+typedef am_res_t (MDEV_API *AMDeviceIsPairedPtr)(AMDeviceRef);
+typedef am_res_t (MDEV_API *AMDeviceValidatePairingPtr)(AMDeviceRef);
+typedef am_res_t (MDEV_API *AMDeviceStartSessionPtr)(AMDeviceRef);
+typedef am_res_t (MDEV_API *AMDeviceStopSessionPtr)(AMDeviceRef);
+typedef am_res_t (MDEV_API *AMDeviceDisconnectPtr)(AMDeviceRef);
+typedef am_res_t (MDEV_API *AMDeviceMountImagePtr)(AMDeviceRef, CFStringRef, CFDictionaryRef,
+ AMDeviceMountImageCallback, void *);
+typedef am_res_t (MDEV_API *AMDeviceStartServicePtr)(AMDeviceRef, CFStringRef, ServiceSocket *, void *);
+typedef am_res_t (MDEV_API *AMDeviceTransferApplicationPtr)(ServiceSocket, CFStringRef, CFDictionaryRef,
+ AMDeviceInstallApplicationCallback,
+ void *);
+typedef am_res_t (MDEV_API *AMDeviceInstallApplicationPtr)(ServiceSocket, CFStringRef, CFDictionaryRef,
+ AMDeviceInstallApplicationCallback,
+ void*);
+typedef am_res_t (MDEV_API *AMDeviceUninstallApplicationPtr)(ServiceSocket, CFStringRef, CFDictionaryRef,
+ AMDeviceInstallApplicationCallback,
+ void*);
+typedef am_res_t (MDEV_API *AMDeviceLookupApplicationsPtr)(AMDeviceRef, unsigned int, CFDictionaryRef *);
+} // extern C
+
+QString CFStringRef2QString(CFStringRef s)
+{
+ if (!s)
+ return QString();
+ if (CFGetTypeID(s) != CFStringGetTypeID()) {
+ qDebug() << "CFStringRef2QString argument is not a string!";
+ return QLatin1String("*NON CFStringRef*");
+ }
+ unsigned char buf[250];
+ CFIndex len = CFStringGetLength(s);
+ CFIndex usedBufLen;
+ CFIndex converted = CFStringGetBytes(s, CFRangeMake(0,len), kCFStringEncodingUTF8,
+ '?', false, &buf[0], sizeof(buf), &usedBufLen);
+ if (converted == len)
+ return QString::fromUtf8(reinterpret_cast<char *>(&buf[0]), usedBufLen);
+ size_t bufSize = sizeof(buf)
+ + CFStringGetMaximumSizeForEncoding(len - converted, kCFStringEncodingUTF8);
+ unsigned char *bigBuf = new unsigned char[bufSize];
+ memcpy(bigBuf, buf, usedBufLen);
+ CFIndex newUseBufLen;
+ CFStringGetBytes(s, CFRangeMake(converted,len), kCFStringEncodingUTF8,
+ '?', false, &bigBuf[usedBufLen], bufSize, &newUseBufLen);
+ QString res = QString::fromUtf8(reinterpret_cast<char *>(bigBuf), usedBufLen + newUseBufLen);
+ delete[] bigBuf;
+ return res;
+}
+
+CFStringRef CFStringCreateWithQString(const QString &s)
+{
+ QByteArray bytes = s.toUtf8();
+ CFStringRef res = CFStringCreateWithBytes ( 0, reinterpret_cast<const unsigned char *>(bytes.constBegin()), bytes.length(),
+ kCFStringEncodingUTF8, false);
+ return res;
+}
+
+} // anonymous namespace
+
+namespace Ios {
+namespace Internal {
+
+class MobileDeviceLib {
+public :
+ MobileDeviceLib();
+
+ bool load();
+ bool isLoaded();
+ QStringList errors();
+//
+
+ void setLogLevel(int i) ;
+ am_res_t deviceNotificationSubscribe(AMDeviceNotificationCallback callback,
+ unsigned int v1, unsigned int v2, void *v3,
+ const AMDeviceNotification **handle);
+ am_res_t deviceNotificationUnsubscribe(void *handle);
+ CFPropertyListRef deviceCopyValue(AMDeviceRef,CFStringRef,CFStringRef);
+ unsigned int deviceGetConnectionID(AMDeviceRef);
+ CFStringRef deviceCopyDeviceIdentifier(AMDeviceRef);
+ am_res_t deviceConnect(AMDeviceRef);
+ am_res_t devicePair(AMDeviceRef);
+ am_res_t deviceIsPaired(AMDeviceRef);
+ am_res_t deviceValidatePairing(AMDeviceRef);
+ am_res_t deviceStartSession(AMDeviceRef);
+ am_res_t deviceStopSession(AMDeviceRef);
+ am_res_t deviceDisconnect(AMDeviceRef);
+ am_res_t deviceMountImage(AMDeviceRef, CFStringRef, CFDictionaryRef,
+ AMDeviceMountImageCallback, void *);
+ am_res_t deviceStartService(AMDeviceRef, CFStringRef, ServiceSocket *, void *);
+ am_res_t deviceTransferApplication(int, CFStringRef, CFDictionaryRef,
+ AMDeviceInstallApplicationCallback,
+ void *);
+ am_res_t deviceInstallApplication(int, CFStringRef, CFDictionaryRef,
+ AMDeviceInstallApplicationCallback,
+ void*);
+ am_res_t deviceUninstallApplication(int, CFStringRef, CFDictionaryRef,
+ AMDeviceInstallApplicationCallback,
+ void*);
+ am_res_t deviceLookupApplications(AMDeviceRef, unsigned int, CFDictionaryRef *);
+
+ void addError(const QString &msg);
+ void addError(const char *msg);
+ QStringList m_errors;
+private:
+ QLibrary lib;
+ QList<QLibrary *> deps;
+ AMDSetLogLevelPtr m_AMDSetLogLevel;
+ AMDeviceNotificationSubscribePtr m_AMDeviceNotificationSubscribe;
+ AMDeviceNotificationUnsubscribePtr m_AMDeviceNotificationUnsubscribe;
+ AMDeviceCopyValuePtr m_AMDeviceCopyValue;
+ AMDeviceGetConnectionIDPtr m_AMDeviceGetConnectionID;
+ AMDeviceCopyDeviceIdentifierPtr m_AMDeviceCopyDeviceIdentifier;
+ AMDeviceConnectPtr m_AMDeviceConnect;
+ AMDevicePairPtr m_AMDevicePair;
+ AMDeviceIsPairedPtr m_AMDeviceIsPaired;
+ AMDeviceValidatePairingPtr m_AMDeviceValidatePairing;
+ AMDeviceStartSessionPtr m_AMDeviceStartSession;
+ AMDeviceStopSessionPtr m_AMDeviceStopSession;
+ AMDeviceDisconnectPtr m_AMDeviceDisconnect;
+ AMDeviceMountImagePtr m_AMDeviceMountImage;
+ AMDeviceStartServicePtr m_AMDeviceStartService;
+ AMDeviceTransferApplicationPtr m_AMDeviceTransferApplication;
+ AMDeviceInstallApplicationPtr m_AMDeviceInstallApplication;
+ AMDeviceUninstallApplicationPtr m_AMDeviceUninstallApplication;
+ AMDeviceLookupApplicationsPtr m_AMDeviceLookupApplications;
+};
+
+extern "C" {
+typedef void (*DeviceAvailableCallback)(QString deviceId, AMDeviceRef, void *userData);
+}
+
+class PendingDeviceLookup {
+public:
+ QTimer timer;
+ DeviceAvailableCallback callback;
+ void *userData;
+};
+
+class CommandSession {
+public:
+ virtual ~CommandSession();
+ explicit CommandSession(const QString &deviceId);
+
+ void internalDeviceAvailableCallback(QString deviceId, AMDeviceRef device);
+ virtual void deviceCallbackReturned() { }
+ virtual am_res_t appTransferCallback(CFDictionaryRef) { return 0; }
+ virtual am_res_t appInstallCallback(CFDictionaryRef) { return 0; }
+ virtual void reportProgress(CFDictionaryRef dict);
+ virtual void reportProgress2(int progress, const QString &status);
+ virtual QString commandName();
+
+ bool connectDevice();
+ bool disconnectDevice();
+ bool startService(const QString &service, ServiceSocket &fd);
+ void stopService(ServiceSocket fd);
+ void startDeviceLookup(int timeout);
+ void addError(const QString &msg);
+ bool writeAll(ServiceSocket fd, const char *cmd, ssize_t len = -1);
+ bool sendGdbCommand(ServiceSocket fd, const char *cmd, ssize_t len = -1);
+ QByteArray readGdbReply(ServiceSocket fd);
+ bool expectGdbReply(ServiceSocket gdbFd, QByteArray expected);
+ bool expectGdbOkReply(ServiceSocket gdbFd);
+
+ MobileDeviceLib *lib();
+
+ QString deviceId;
+ AMDeviceRef device;
+ int progressBase;
+ int unexpectedChars;
+private:
+ bool checkRead(ssize_t nRead, int &maxRetry);
+ int handleChar(QByteArray &res, char c, int status);
+};
+
+// ------- IosManagerPrivate interface --------
+
+class IosDeviceManagerPrivate {
+public:
+ static IosDeviceManagerPrivate *instance();
+ explicit IosDeviceManagerPrivate (IosDeviceManager *q);
+ bool watchDevices();
+ void requestAppOp(const QString &bundlePath, const QStringList &extraArgs,
+ Ios::IosDeviceManager::AppOp appOp, const QString &deviceId, int timeout);
+ void requestDeviceInfo(const QString &deviceId, int timeout);
+ QStringList errors();
+ void addError(QString errorMsg);
+ void addDevice(AMDeviceRef device);
+ void removeDevice(AMDeviceRef device);
+ void checkPendingLookups();
+ MobileDeviceLib *lib();
+ void didTransferApp(const QString &bundlePath, const QString &deviceId,
+ Ios::IosDeviceManager::OpStatus status);
+ void didStartApp(const QString &bundlePath, const QString &deviceId,
+ Ios::IosDeviceManager::OpStatus status, int fd);
+ void isTransferringApp(const QString &bundlePath, const QString &deviceId, int progress,
+ const QString &info);
+ void deviceWithId(QString deviceId, int timeout, DeviceAvailableCallback callback, void *userData);
+ int processGdbServer(int fd);
+private:
+ IosDeviceManager *q;
+ QHash<QString, AMDeviceRef> m_devices;
+ QMultiHash<QString, PendingDeviceLookup *> m_pendingLookups;
+ AMDeviceNotificationRef m_notification;
+ MobileDeviceLib m_lib;
+};
+
+class DevInfoSession: public CommandSession {
+public:
+ DevInfoSession(const QString &deviceId);
+
+ void deviceCallbackReturned();
+ QString commandName();
+};
+
+class AppOpSession: public CommandSession {
+public:
+ QString bundlePath;
+ QStringList extraArgs;
+ Ios::IosDeviceManager::AppOp appOp;
+
+
+ AppOpSession(const QString &deviceId, const QString &bundlePath,
+ const QStringList &extraArgs, Ios::IosDeviceManager::AppOp appOp);
+
+ void deviceCallbackReturned();
+ bool installApp();
+ bool runApp();
+ am_res_t appTransferCallback(CFDictionaryRef dict);
+ am_res_t appInstallCallback(CFDictionaryRef dict);
+ void reportProgress2(int progress, const QString &status);
+ QString appPathOnDevice();
+ QString appId();
+ QString commandName();
+};
+
+} // namespace Internal
+} // namespace Ios
+
+
+namespace {
+// ------- callbacks --------
+
+extern "C" void deviceNotificationCallback(AMDeviceNotificationCallbackInfo *info, void *user)
+{
+ if (info == 0)
+ Ios::Internal::IosDeviceManagerPrivate::instance()->addError(QLatin1String("null info in deviceNotificationCallback"));
+ if (debugAll) {
+ QDebug dbg=qDebug();
+ dbg << "device_notification_callback(";
+ if (info)
+ dbg << " dev:" << info->_device << " msg:" << info->_message << " subscription:" << info->_subscription;
+ else
+ dbg << "*NULL*";
+ dbg << "," << user << ")";
+ }
+ switch (info->_message) {
+ case ADNCI_MSG_CONNECTED:
+ Ios::Internal::IosDeviceManagerPrivate::instance()->addDevice(info->_device);
+ break;
+ case ADNCI_MSG_DISCONNECTED:
+ Ios::Internal::IosDeviceManagerPrivate::instance()->removeDevice(info->_device);
+ break;
+ case ADNCI_MSG_UNSUBSCRIBED:
+ break;
+ default:
+ Ios::Internal::IosDeviceManagerPrivate::instance()->addError(QLatin1String("unexpected notification message value ")
+ + QString::number(info->_message));
+ }
+}
+
+extern "C" void deviceAvailableSessionCallback(QString deviceId, AMDeviceRef device, void *userData)
+{
+ if (debugAll)
+ qDebug() << "deviceAvailableSessionCallback" << QThread::currentThread();
+ if (userData == 0) {
+ Ios::Internal::IosDeviceManagerPrivate::instance()->addError(QLatin1String("deviceAvailableSessionCallback called with null userData"));
+ return;
+ }
+ Ios::Internal::CommandSession *session = static_cast<Ios::Internal::CommandSession *>(userData);
+ session->internalDeviceAvailableCallback(deviceId, device);
+}
+
+extern "C" am_res_t appTransferSessionCallback(CFDictionaryRef dict, void *userData)
+{
+ if (debugAll) {
+ qDebug() << "appTransferSessionCallback" << QThread::currentThread();
+ CFShow(dict);
+ }
+ if (userData == 0) {
+ Ios::Internal::IosDeviceManagerPrivate::instance()->addError(QLatin1String("appTransferSessionCallback called with null userData"));
+ return 0; // return -1?
+ }
+ Ios::Internal::CommandSession *session = static_cast<Ios::Internal::CommandSession *>(userData);
+ return session->appTransferCallback(dict);
+}
+
+extern "C" am_res_t appInstallSessionCallback(CFDictionaryRef dict, void *userData)
+{
+ if (debugAll) {
+ qDebug() << "appInstallSessionCallback" << QThread::currentThread();
+ CFShow(dict);
+ }
+ if (userData == 0) {
+ Ios::Internal::IosDeviceManagerPrivate::instance()->addError(QLatin1String("appInstallSessionCallback called with null userData"));
+ return 0; // return -1?
+ }
+ Ios::Internal::CommandSession *session = static_cast<Ios::Internal::CommandSession *>(userData);
+ return session->appInstallCallback(dict);
+}
+
+} // anonymous namespace
+
+namespace Ios {
+namespace Internal {
+
+// ------- IosManagerPrivate implementation --------
+
+IosDeviceManagerPrivate *IosDeviceManagerPrivate::instance()
+{
+ return IosDeviceManager::instance()->d;
+}
+
+IosDeviceManagerPrivate::IosDeviceManagerPrivate (IosDeviceManager *q) : q(q), m_notification(0) { }
+
+bool IosDeviceManagerPrivate::watchDevices()
+{
+ if (!m_lib.load())
+ addError(QLatin1String("Error loading MobileDevice.framework"));
+ if (!m_lib.errors().isEmpty())
+ foreach (const QString &msg, m_lib.errors())
+ addError(msg);
+ m_lib.setLogLevel(5);
+ am_res_t e = m_lib.deviceNotificationSubscribe(&deviceNotificationCallback, 0, 0,
+ 0, &m_notification);
+ if (e != 0) {
+ addError(QLatin1String("AMDeviceNotificationSubscribe failed"));
+ return false;
+ }
+ if (debugAll)
+ qDebug() << "AMDeviceNotificationSubscribe successful, m_notificationIter:" << m_notification << QThread::currentThread();
+
+ return true;
+}
+
+void IosDeviceManagerPrivate::requestAppOp(const QString &bundlePath,
+ const QStringList &extraArgs,
+ IosDeviceManager::AppOp appOp,
+ const QString &deviceId, int timeout)
+{
+ AppOpSession *session = new AppOpSession(deviceId, bundlePath, extraArgs, appOp);
+ session->startDeviceLookup(timeout);
+}
+
+void IosDeviceManagerPrivate::requestDeviceInfo(const QString &deviceId, int timeout)
+{
+ DevInfoSession *session = new DevInfoSession(deviceId);
+ session->startDeviceLookup(timeout);
+}
+
+QStringList IosDeviceManagerPrivate::errors()
+{
+ return m_lib.errors();
+}
+
+void IosDeviceManagerPrivate::addError(QString errorMsg)
+{
+ if (debugAll)
+ qDebug() << "IosManagerPrivate ERROR: " << errorMsg;
+ emit q->errorMsg(errorMsg);
+}
+
+void IosDeviceManagerPrivate::addDevice(AMDeviceRef device)
+{
+ CFStringRef s = m_lib.deviceCopyDeviceIdentifier(device);
+ QString devId = CFStringRef2QString(s);
+ if (s) CFRelease(s);
+ CFRetain(device);
+ if (debugAll)
+ qDebug() << "addDevice " << devId;
+ if (m_devices.contains(devId)) {
+ if (m_devices.value(devId) == device) {
+ addError(QLatin1String("double add of device ") + devId);
+ return;
+ }
+ addError(QLatin1String("device change without remove ") + devId);
+ removeDevice(m_devices.value(devId));
+ }
+ m_devices.insert(devId, device);
+ emit q->deviceAdded(devId);
+ if (m_pendingLookups.contains(devId) || m_pendingLookups.contains(QString())) {
+ QList<PendingDeviceLookup *> devices = m_pendingLookups.values(devId);
+ m_pendingLookups.remove(devId);
+ devices << m_pendingLookups.values(QString());
+ m_pendingLookups.remove(QString());
+ foreach (PendingDeviceLookup *devLookup, devices) {
+ if (debugAll) qDebug() << "found pending op";
+ devLookup->timer.stop();
+ devLookup->callback(devId, device, devLookup->userData);
+ delete devLookup;
+ }
+ }
+}
+
+void IosDeviceManagerPrivate::removeDevice(AMDeviceRef device)
+{
+ CFStringRef s = m_lib.deviceCopyDeviceIdentifier(device);
+ QString devId = CFStringRef2QString(s);
+ if (s)
+ CFRelease(s);
+ if (debugAll)
+ qDebug() << "removeDevice " << devId;
+ if (m_devices.contains(devId)) {
+ if (m_devices.value(devId) == device) {
+ CFRelease(device);
+ m_devices.remove(devId);
+ emit q->deviceRemoved(devId);
+ return;
+ }
+ addError(QLatin1String("ignoring remove of changed device ") + devId);
+ } else {
+ addError(QLatin1String("removal of unknown device ") + devId);
+ }
+}
+
+void IosDeviceManagerPrivate::checkPendingLookups()
+{
+ foreach (const QString &deviceId, m_pendingLookups.keys()) {
+ foreach (PendingDeviceLookup *deviceLookup, m_pendingLookups.values(deviceId)) {
+ if (!deviceLookup->timer.isActive()) {
+ m_pendingLookups.remove(deviceId, deviceLookup);
+ deviceLookup->callback(deviceId, 0, deviceLookup->userData);
+ delete deviceLookup;
+ }
+ }
+ }
+}
+
+MobileDeviceLib *IosDeviceManagerPrivate::lib()
+{
+ return &m_lib;
+}
+
+void IosDeviceManagerPrivate::didTransferApp(const QString &bundlePath, const QString &deviceId,
+ IosDeviceManager::OpStatus status)
+{
+ emit IosDeviceManagerPrivate::instance()->q->didTransferApp(bundlePath, deviceId, status);
+}
+
+void IosDeviceManagerPrivate::didStartApp(const QString &bundlePath, const QString &deviceId,
+ IosDeviceManager::OpStatus status, int fd)
+{
+ emit IosDeviceManagerPrivate::instance()->q->didStartApp(bundlePath, deviceId, status, fd);
+}
+
+void IosDeviceManagerPrivate::isTransferringApp(const QString &bundlePath, const QString &deviceId,
+ int progress, const QString &info)
+{
+ emit IosDeviceManagerPrivate::instance()->q->isTransferringApp(bundlePath, deviceId, progress, info);
+}
+
+void IosDeviceManagerPrivate::deviceWithId(QString deviceId, int timeout,
+ DeviceAvailableCallback callback,
+ void *userData)
+{
+ if (!m_notification) {
+ qDebug() << "null notification!!";
+ /*if (!watchDevices()) {
+ callback(deviceId, 0, userData);
+ return;
+ }*/
+ }
+ if (deviceId.isEmpty() && !m_devices.isEmpty()) {
+ QHash<QString,AMDeviceRef>::iterator i = m_devices.begin();
+ callback(i.key(), i.value() , userData);
+ return;
+ }
+ if (m_devices.contains(deviceId)) {
+ callback(deviceId, m_devices.value(deviceId), userData);
+ return;
+ }
+ if (timeout <= 0) {
+ callback(deviceId, 0, userData);
+ return;
+ }
+ PendingDeviceLookup *pendingLookup = new PendingDeviceLookup;
+ pendingLookup->callback = callback;
+ pendingLookup->userData = userData;
+ pendingLookup->timer.setSingleShot(true);
+ pendingLookup->timer.setInterval(timeout);
+ QObject::connect(&(pendingLookup->timer), SIGNAL(timeout()), q, SLOT(checkPendingLookups()));
+ m_pendingLookups.insertMulti(deviceId, pendingLookup);
+ pendingLookup->timer.start();
+}
+enum GdbServerStatus {
+ NORMAL_PROCESS,
+ PROTOCOL_ERROR,
+ STOP_FOR_SIGNAL,
+ INFERIOR_EXITED,
+ PROTOCOL_UNHANDLED
+};
+
+int IosDeviceManagerPrivate::processGdbServer(int fd)
+{
+ CommandSession session((QString()));
+ session.sendGdbCommand(fd, "vCont;c"); // resume all threads
+ GdbServerStatus state = NORMAL_PROCESS;
+ int maxRetry = 10;
+ int maxSignal = 5;
+ while (state == NORMAL_PROCESS) {
+ QByteArray repl = session.readGdbReply(fd);
+ int signal = 0;
+ if (repl.size() > 0) {
+ state = PROTOCOL_ERROR;
+ switch (repl.at(0)) {
+ case 'S':
+ if (repl.size() < 3) {
+ addError(QString::fromLatin1("invalid S signal message %1")
+ .arg(QString::fromLatin1(repl.constData(), repl.size())));
+ state = PROTOCOL_ERROR;
+ } else {
+ signal = QByteArray::fromHex(repl.mid(1,2)).at(0);
+ addError(QString::fromLatin1("program received signal %1")
+ .arg(signal));
+ state = STOP_FOR_SIGNAL;
+ }
+ break;
+ case 'T':
+ if (repl.size() < 3) {
+ addError(QString::fromLatin1("invalid T signal message %1")
+ .arg(QString::fromLatin1(repl.constData(), repl.size())));
+ state = PROTOCOL_ERROR;
+ } else {
+ signal = QByteArray::fromHex(repl.mid(1,2)).at(0);
+ addError(QString::fromLatin1("program received signal %1, %2")
+ .arg(signal)
+ .arg(QString::fromLatin1(repl.mid(3,repl.size()-3))));
+ state = STOP_FOR_SIGNAL;
+ }
+ break;
+ case 'W':
+ if (repl.size() < 3) {
+ addError(QString::fromLatin1("invalid W signal message %1")
+ .arg(QString::fromLatin1(repl.constData(), repl.size())));
+ state = PROTOCOL_ERROR;
+ } else {
+ int exitCode = QByteArray::fromHex(repl.mid(1,2)).at(0);
+ addError(QString::fromLatin1("exited with exit code %1, %2")
+ .arg(exitCode)
+ .arg(QString::fromLatin1(repl.mid(3,repl.size()-3))));
+ state = INFERIOR_EXITED;
+ }
+ break;
+ case 'X':
+ if (repl.size() < 3) {
+ addError(QString::fromLatin1("invalid X signal message %1")
+ .arg(QString::fromLatin1(repl.constData(), repl.size())));
+ state = PROTOCOL_ERROR;
+ } else {
+ int exitCode = QByteArray::fromHex(repl.mid(1,2)).at(0);
+ addError(QString::fromLatin1("exited due to signal %1, %2")
+ .arg(exitCode)
+ .arg(QString::fromLatin1(repl.mid(3,repl.size()-3))));
+ state = INFERIOR_EXITED;
+ }
+ break;
+ case 'O':
+ emit q->appOutput(QString::fromLocal8Bit(QByteArray::fromHex(repl.mid(1))));
+ state = NORMAL_PROCESS;
+ break;
+ default:
+ addError(QString::fromLatin1("non handled response:")
+ + QString::fromLatin1(repl.constData(), repl.size()));
+ state = PROTOCOL_UNHANDLED;
+ }
+ if (state == STOP_FOR_SIGNAL) {
+ QList<int> okSig = QList<int>() << SIGCHLD << SIGCONT << SIGALRM << SIGURG
+ << SIGUSR1 << SIGUSR2 << SIGPIPE
+ << SIGPROF << SIGWINCH << SIGINFO;
+ if (signal == 9) {
+ break;
+ } else if (!okSig.contains(signal) && --maxSignal < 0) {
+ addError(QLatin1String("hit maximum number of consecutive signals, stopping"));
+ break;
+ }
+ if (session.sendGdbCommand(fd, "vCont;c"))
+ state = NORMAL_PROCESS;
+ else
+ break;
+ } else {
+ maxSignal = 5;
+ }
+ maxRetry = 10;
+ } else {
+ if (--maxRetry < 0)
+ break;
+ }
+ }
+ return state != INFERIOR_EXITED;
+}
+
+// ------- ConnectSession implementation --------
+
+CommandSession::CommandSession(const QString &deviceId) : deviceId(deviceId), device(0),
+ progressBase(0), unexpectedChars(0)
+{ }
+
+CommandSession::~CommandSession() { }
+
+bool CommandSession::connectDevice()
+{
+ if (!device)
+ return false;
+ if (am_res_t error1 = lib()->deviceConnect(device)) {
+ addError(QString::fromLatin1("connectDevice %1 failed, AMDeviceConnect returned %2")
+ .arg(deviceId).arg(error1));
+ return false;
+ }
+ if (lib()->deviceIsPaired(device) == 0) { // not paired
+ if (am_res_t error = lib()->devicePair(device)) {
+ addError(QString::fromLatin1("connectDevice %1 failed, AMDevicePair returned %2")
+ .arg(deviceId).arg(error));
+ return false;
+ }
+ }
+ if (am_res_t error2 = lib()->deviceValidatePairing(device)) {
+ addError(QString::fromLatin1("connectDevice %1 failed, AMDeviceValidatePairing returned %2")
+ .arg(deviceId).arg(error2));
+ return false;
+ }
+ if (am_res_t error3 = lib()->deviceStartSession(device)) {
+ addError(QString::fromLatin1("connectDevice %1 failed, AMDeviceStartSession returned %2")
+ .arg(deviceId).arg(error3));
+ return false;
+ }
+ return true;
+}
+
+bool CommandSession::disconnectDevice()
+{
+ if (am_res_t error = lib()->deviceStopSession(device)) {
+ addError(QString::fromLatin1("stopSession %1 failed, AMDeviceStopSession returned %2")
+ .arg(deviceId).arg(error));
+ return false;
+ }
+ if (am_res_t error = lib()->deviceDisconnect(device)) {
+ addError(QString::fromLatin1("disconnectDevice %1 failed, AMDeviceDisconnect returned %2")
+ .arg(deviceId).arg(error));
+ return false;
+ }
+ return true;
+}
+
+bool CommandSession::startService(const QString &serviceName, ServiceSocket &fd)
+{
+ bool failure = false;
+ fd = 0;
+ if (!connectDevice())
+ return false;
+ CFStringRef cfsService = CFStringCreateWithQString(serviceName);
+ if (am_res_t error = lib()->deviceStartService(device, cfsService, &fd, 0)) {
+ addError(QString::fromLatin1("startService on device %1 failed, AMDeviceStartService returned %2")
+ .arg(deviceId).arg(error));
+ failure = true;
+ fd = -1;
+ }
+ CFRelease(cfsService);
+ disconnectDevice();
+ return !failure;
+}
+
+void CommandSession::stopService(ServiceSocket fd)
+{
+ // would be close socket on windows
+ close(fd);
+}
+
+void CommandSession::startDeviceLookup(int timeout)
+{
+ IosDeviceManagerPrivate::instance()->deviceWithId(deviceId, timeout,
+ &deviceAvailableSessionCallback, this);
+}
+
+void CommandSession::addError(const QString &msg)
+{
+ if (debugAll)
+ qDebug() << "CommandSession ERROR: " << msg;
+ IosDeviceManagerPrivate::instance()->addError(commandName() + msg);
+}
+
+bool CommandSession::writeAll(ServiceSocket fd, const char *cmd, ssize_t len)
+{
+ if (len == -1)
+ len = strlen(cmd);
+ if (debugGdbServer) {
+ QByteArray cmdBA(cmd,len);
+ qDebug() << "writeAll(" << fd << "," << QString::fromLocal8Bit(cmdBA.constData(), cmdBA.size())
+ << " (" << cmdBA.toHex() << "))";
+ }
+ ssize_t i = 0;
+ int maxRetry = 10;
+ while (i < len) {
+ ssize_t nWritten = write(fd, cmd, len - i);
+ if (nWritten < 1) {
+ --maxRetry;
+ if (nWritten == -1 && errno != 0 && errno != EINTR) {
+ char buf[256];
+ if (!strerror_r(errno, buf, sizeof(buf))) {
+ buf[sizeof(buf)-1] = 0;
+ addError(QString::fromLocal8Bit(buf));
+ } else {
+ addError(QLatin1String("Unknown writeAll error"));
+ }
+ return false;
+ }
+ if (maxRetry <= 0) {
+ addError(QLatin1String("Hit maximum retries in writeAll"));
+ return false;
+ }
+ } else {
+ maxRetry = 10;
+ }
+ i += nWritten;
+ }
+ return true;
+}
+
+bool CommandSession::sendGdbCommand(ServiceSocket fd, const char *cmd, ssize_t len)
+{
+ if (len == -1)
+ len = strlen(cmd);
+ unsigned char checkSum = 0;
+ for (int i = 0; i < len; ++i)
+ checkSum += static_cast<unsigned char>(cmd[i]);
+ bool failure = !writeAll(fd, "$", 1);
+ if (!failure)
+ failure = !writeAll(fd, cmd, len);
+ char buf[3];
+ buf[0] = '#';
+ const char *hex = "0123456789abcdef";
+ buf[1] = hex[(checkSum >> 4) & 0xF];
+ buf[2] = hex[checkSum & 0xF];
+ if (!failure)
+ failure = !writeAll(fd, buf, 3);
+ return !failure;
+}
+
+bool CommandSession::checkRead(ssize_t nRead, int &maxRetry)
+{
+ if (nRead < 1 || nRead > 4) {
+ --maxRetry;
+ if ((nRead < 0 || nRead > 4) && errno != 0 && errno != EINTR) {
+ char buf[256];
+ if (!strerror_r(errno, buf, sizeof(buf))) {
+ buf[sizeof(buf)-1] = 0;
+ addError(QString::fromLocal8Bit(buf));
+ } else {
+ addError(QLatin1String("Unknown writeAll error"));
+ }
+ return false;
+ }
+ if (maxRetry <= 0) {
+ addError(QLatin1String("Hit maximum retries in readGdbReply"));
+ return false;
+ }
+ } else {
+ maxRetry = 10;
+ }
+ return true;
+}
+
+int CommandSession::handleChar(QByteArray &res, char c, int status)
+{
+ switch (status) {
+ case 0:
+ if (c == '$')
+ return 1;
+ if (c != '+' && c != '-') {
+ if (unexpectedChars < 10) {
+ addError(QString::fromLatin1("unexpected char %1 in readGdbReply looking for $")
+ .arg(QChar::fromLatin1(c)));
+ ++unexpectedChars;
+ } else if (unexpectedChars == 10) {
+ addError(QString::fromLatin1("hit maximum number of unexpected chars, ignoring them in readGdbReply looking for $"));
+ ++unexpectedChars;
+ }
+ }
+ return 0;
+ case 1:
+ if (c != '#') {
+ res.append(c);
+ return 1;
+ }
+ return 2;
+ case 2:
+ case 3:
+ if ((c < '0' || c > '9') && (c < 'a' || c > 'f') && (c < 'A' | c > 'F')) {
+ if (unexpectedChars < 15) {
+ addError(QString::fromLatin1("unexpected char %1 in readGdbReply as checksum")
+ .arg(QChar::fromLatin1(c)));
+ ++unexpectedChars;
+ } else if (unexpectedChars == 15) {
+ addError(QString::fromLatin1("hit maximum number of unexpected chars in checksum, ignoring them in readGdbReply"));
+ ++unexpectedChars;
+ }
+ }
+ return status + 1;
+ case 4:
+ addError(QString::fromLatin1("gone past end in readGdbReply"));
+ return 5;
+ case 5:
+ return 5;
+ default:
+ addError(QString::fromLatin1("unexpected status readGdbReply"));
+ return 5;
+ }
+}
+
+QByteArray CommandSession::readGdbReply(ServiceSocket fd)
+{
+ // read with minimal buffering because we might want to give the socket away...
+ QByteArray res;
+ char buf[5];
+ int maxRetry = 10;
+ int status = 0;
+ int toRead = 4;
+ while (status < 4 && toRead > 0) {
+ ssize_t nRead = read(fd, buf, toRead);
+ if (!checkRead(nRead, maxRetry))
+ return QByteArray();
+ if (debugGdbServer) {
+ buf[nRead] = 0;
+ qDebug() << "gdbReply read " << buf;
+ }
+ for (ssize_t i = 0; i< nRead; ++i)
+ status = handleChar(res, buf[i], status);
+ toRead = 4 - status;
+ }
+ if (status != 4) {
+ addError(QString::fromLatin1("unexpected parser status %1 in readGdbReply").arg(status));
+ return QByteArray();
+ }
+ return res;
+}
+
+void CommandSession::reportProgress(CFDictionaryRef dict)
+{
+ QString status;
+ CFStringRef cfStatus;
+ if (CFDictionaryGetValueIfPresent(dict, CFSTR("Status"), reinterpret_cast<const void **>(&cfStatus))) {
+ if (cfStatus && CFGetTypeID(cfStatus) == CFStringGetTypeID())
+ status = CFStringRef2QString(cfStatus);
+ }
+ quint32 progress = 0;
+ CFNumberRef cfProgress;
+ if (CFDictionaryGetValueIfPresent(dict, CFSTR("PercentComplete"), reinterpret_cast<const void **>(&cfProgress))) {
+ if (cfProgress && CFGetTypeID(cfProgress) == CFNumberGetTypeID())
+ CFNumberGetValue(cfProgress,kCFNumberSInt32Type, reinterpret_cast<const void **>(&progress));
+ }
+ reportProgress2(progressBase + progress, status);
+}
+
+void CommandSession::reportProgress2(int progress, const QString &status)
+{
+ Q_UNUSED(progress);
+ Q_UNUSED(status);
+}
+
+QString CommandSession::commandName()
+{
+ return QString::fromLatin1("CommandSession(%1)").arg(deviceId);
+}
+
+bool CommandSession::expectGdbReply(ServiceSocket gdbFd, QByteArray expected)
+{
+ QByteArray repl = readGdbReply(gdbFd);
+ if (repl != expected) {
+ addError(QString::fromLatin1("Unexpected reply: %1 (%2) vs %3 (%3)")
+ .arg(QString::fromLocal8Bit(repl.constData(), repl.size()))
+ .arg(QString::fromLatin1(repl.toHex().constData(), 2*repl.size()))
+ .arg(QString::fromLocal8Bit(expected.constData(), expected.size()))
+ .arg(QString::fromLocal8Bit(expected.toHex().constData(), 2*expected.size())));
+ return false;
+ }
+ return true;
+}
+
+bool CommandSession::expectGdbOkReply(ServiceSocket gdbFd)
+{
+ return expectGdbReply(gdbFd, QByteArray("OK"));
+}
+
+MobileDeviceLib *CommandSession::lib()
+{
+ return IosDeviceManagerPrivate::instance()->lib();
+}
+
+AppOpSession::AppOpSession(const QString &deviceId, const QString &bundlePath,
+ const QStringList &extraArgs, IosDeviceManager::AppOp appOp):
+ CommandSession(deviceId), bundlePath(bundlePath), extraArgs(extraArgs), appOp(appOp)
+{ }
+
+QString AppOpSession::commandName()
+{
+ return QString::fromLatin1("TransferAppSession(%1, %2)").arg(deviceId, bundlePath);
+}
+
+
+bool AppOpSession::installApp()
+{
+ ServiceSocket fd;
+ bool failure = (device == 0);
+ if (!failure) {
+ failure = !startService(QLatin1String("com.apple.afc"), fd);
+ if (!failure) {
+ CFStringRef cfsBundlePath = CFStringCreateWithQString(bundlePath);
+ if (am_res_t error = lib()->deviceTransferApplication(fd, cfsBundlePath, 0,
+ &appTransferSessionCallback,
+ static_cast<CommandSession *>(this))) {
+ addError(QString::fromLatin1("TransferAppSession(%1,%2) failed, AMDeviceTransferApplication returned %3")
+ .arg(bundlePath, deviceId).arg(error));
+ failure = true;
+ }
+ progressBase += 100;
+ CFRelease(cfsBundlePath);
+ }
+ stopService(fd);
+ }
+ if (debugAll)
+ qDebug() << "AMDeviceTransferApplication finished request with " << failure;
+ if (!failure) {
+ failure = !startService(QLatin1String("com.apple.mobile.installation_proxy"), fd);
+ if (!failure) {
+ CFStringRef cfsBundlePath = CFStringCreateWithQString(bundlePath);
+
+ CFStringRef key[1] = {CFSTR("PackageType")};
+ CFStringRef value[1] = {CFSTR("Developer")};
+ CFDictionaryRef options = CFDictionaryCreate(0, reinterpret_cast<const void**>(&key[0]),
+ reinterpret_cast<const void**>(&value[0]), 1,
+ &kCFTypeDictionaryKeyCallBacks,
+ &kCFTypeDictionaryValueCallBacks);
+
+ if (am_res_t error = lib()->deviceInstallApplication(fd, cfsBundlePath, options,
+ &appInstallSessionCallback,
+ static_cast<CommandSession *>(this))) {
+ addError(QString::fromLatin1("InstallAppSession(%1,%2) failed, AMDeviceInstallApplication returned %3")
+ .arg(bundlePath, deviceId).arg(error));
+ failure = true;
+ }
+ progressBase += 100;
+ CFRelease(options);
+ CFRelease(cfsBundlePath);
+ }
+ stopService(fd);
+ }
+ if (debugAll)
+ qDebug() << "AMDeviceInstallApplication finished request with " << failure;
+ IosDeviceManagerPrivate::instance()->didTransferApp(bundlePath, deviceId,
+ (failure ? IosDeviceManager::Failure : IosDeviceManager::Success));
+ return !failure;
+}
+
+void AppOpSession::deviceCallbackReturned()
+{
+ switch (appOp) {
+ case IosDeviceManager::None:
+ break;
+ case IosDeviceManager::Install:
+ installApp();
+ break;
+ case IosDeviceManager::InstallAndRun:
+ if (installApp())
+ runApp();
+ break;
+ case IosDeviceManager::Run:
+ runApp();
+ break;
+ }
+}
+
+bool AppOpSession::runApp()
+{
+ bool failure = (device == 0);
+ ServiceSocket gdbFd = -1;
+ if (!failure && !startService(QLatin1String("com.apple.debugserver"), gdbFd))
+ gdbFd = -1;
+ if (gdbFd > 0) {
+ // gdbServer protocol, see http://sourceware.org/gdb/onlinedocs/gdb/Remote-Protocol.html#Remote-Protocol
+ // and the lldb handling of that (with apple specific stuff)
+ // http://llvm.org/viewvc/llvm-project/lldb/trunk/tools/debugserver/source/RNBRemote.cpp
+ failure = !sendGdbCommand(gdbFd, "QStartNoAckMode"); // avoid and send required aknowledgements?
+ if (!failure) failure = !expectGdbOkReply(gdbFd);
+ if (!failure) failure = !sendGdbCommand(gdbFd, "QEnvironmentHexEncoded:"); // send the environment with a series of these commands...
+ if (!failure) failure = !sendGdbCommand(gdbFd, "QSetDisableASLR:1"); // avoid address randomization to debug
+ if (!failure) failure = !expectGdbOkReply(gdbFd);
+ QString exe = appPathOnDevice();
+ QStringList args = extraArgs;
+ QByteArray runCommand("A");
+ args.insert(0, exe);
+ if (debugAll)
+ qDebug() << " trying to start " << args;
+ for (int iarg = 0; iarg < args.size(); ++iarg) {
+ if (iarg)
+ runCommand.append(',');
+ QByteArray arg = args.at(iarg).toLocal8Bit().toHex();
+ runCommand.append(QString::number(arg.size()).toLocal8Bit());
+ runCommand.append(',');
+ runCommand.append(QString::number(iarg).toLocal8Bit());
+ runCommand.append(',');
+ runCommand.append(arg);
+ }
+ if (!failure) failure = !sendGdbCommand(gdbFd, runCommand.constData(), runCommand.size());
+ if (!failure) failure = !expectGdbOkReply(gdbFd);
+ if (!failure) failure = !sendGdbCommand(gdbFd, "qLaunchSuccess");
+ if (!failure) failure = !expectGdbOkReply(gdbFd);
+ }
+ IosDeviceManagerPrivate::instance()->didStartApp(
+ bundlePath, deviceId,
+ (failure ? IosDeviceManager::Failure : IosDeviceManager::Success), gdbFd);
+ return !failure;
+}
+
+void AppOpSession::reportProgress2(int progress, const QString &status)
+{
+ IosDeviceManagerPrivate::instance()->isTransferringApp(
+ bundlePath, deviceId, progress, status);
+}
+
+QString AppOpSession::appId()
+{
+ QSettings settings(bundlePath + QLatin1String("/Info.plist"), QSettings::NativeFormat);
+ QString res = settings.value(QString::fromLatin1("CFBundleIdentifier")).toString();
+ if (debugAll)
+ qDebug() << "appId:" << res;
+ return res;
+}
+
+QString AppOpSession::appPathOnDevice()
+{
+ QString res;
+ if (!connectDevice())
+ return QString();
+ CFDictionaryRef apps;
+ if (int err = lib()->deviceLookupApplications(device, 0, &apps)) {
+ addError(QString::fromLatin1("app lookup failed, AMDeviceLookupApplications returned %1")
+ .arg(err));
+ }
+ if (debugAll)
+ CFShow(apps);
+ if (apps && CFGetTypeID(apps) == CFDictionaryGetTypeID()) {
+ CFStringRef cfAppId = CFStringCreateWithQString(appId());
+ CFDictionaryRef cfAppInfo = 0;
+ if (CFDictionaryGetValueIfPresent(apps, cfAppId, reinterpret_cast<const void**>(&cfAppInfo))) {
+ if (cfAppInfo && CFGetTypeID(cfAppInfo) == CFDictionaryGetTypeID()) {
+ CFStringRef cfPath, cfBundleExe;
+ QString path, bundleExe;
+ if (CFDictionaryGetValueIfPresent(cfAppInfo, CFSTR("Path"), reinterpret_cast<const void **>(&cfPath)))
+ path = CFStringRef2QString(cfPath);
+ if (CFDictionaryGetValueIfPresent(cfAppInfo, CFSTR("CFBundleExecutable"), reinterpret_cast<const void **>(&cfBundleExe)))
+ bundleExe = CFStringRef2QString(cfBundleExe);
+ if (!path.isEmpty() && ! bundleExe.isEmpty())
+ res = path + QLatin1Char('/') + bundleExe;
+ }
+ }
+ }
+ if (apps)
+ CFRelease(apps);
+ disconnectDevice();
+ if (res.isEmpty())
+ addError(QString::fromLatin1("failed to get app Path on device for bundle %1 with appId: %2")
+ .arg(bundlePath, appId()));
+ return res;
+}
+
+am_res_t AppOpSession::appTransferCallback(CFDictionaryRef dict)
+{
+ if (debugAll)
+ qDebug() << "TransferAppSession::appTransferCallback";
+ reportProgress(dict);
+ return 0;
+}
+
+am_res_t AppOpSession::appInstallCallback(CFDictionaryRef dict)
+{
+ if (debugAll)
+ qDebug() << "TransferAppSession::appInstallCallback";
+ reportProgress(dict);
+ return 0;
+}
+
+DevInfoSession::DevInfoSession(const QString &deviceId) : CommandSession(deviceId)
+{ }
+
+QString DevInfoSession::commandName()
+{
+ return QString::fromLatin1("DevInfoSession(%1, %2)").arg(deviceId);
+}
+
+void DevInfoSession::deviceCallbackReturned()
+{
+ if (debugAll)
+ qDebug() << "device available";
+ QMap<QString,QString> res;
+ QString deviceNameKey = QLatin1String("deviceName");
+ QString developerStatusKey = QLatin1String("developerStatus");
+ QString deviceConnectedKey = QLatin1String("deviceConnected");
+ bool failure = !device;
+ if (!failure) {
+ failure = !connectDevice();
+ if (!failure) {
+ res[deviceConnectedKey] = QLatin1String("YES");
+ CFPropertyListRef cfDeviceName = lib()->deviceCopyValue(device, 0,
+ CFSTR("DeviceName"));
+ // CFShow(cfDeviceName);
+ if (cfDeviceName) {
+ if (CFGetTypeID(cfDeviceName) == CFStringGetTypeID())
+ res[deviceNameKey] = CFStringRef2QString(reinterpret_cast<CFStringRef>(cfDeviceName));
+ CFRelease(cfDeviceName);
+ }
+ if (!res.contains(deviceNameKey))
+ res[deviceNameKey] = QString();
+ }
+ if (!failure) {
+ CFPropertyListRef cfDevStatus = lib()->deviceCopyValue(device,
+ CFSTR("com.apple.xcode.developerdomain"),
+ CFSTR("DeveloperStatus"));
+ // CFShow(cfDevStatus);
+ if (cfDevStatus) {
+ if (CFGetTypeID(cfDevStatus) == CFStringGetTypeID())
+ res[developerStatusKey] = CFStringRef2QString(reinterpret_cast<CFStringRef>(cfDevStatus));
+ CFRelease(cfDevStatus);
+ }
+ if (!res.contains(developerStatusKey))
+ res[developerStatusKey] = QLatin1String("*off*");
+ }
+ disconnectDevice();
+ }
+ if (!res.contains(deviceConnectedKey))
+ res[deviceConnectedKey] = QLatin1String("NO");
+ if (!res.contains(deviceNameKey))
+ res[deviceNameKey] = QLatin1String("*unknown*");
+ if (!res.contains(developerStatusKey))
+ res[developerStatusKey] = QLatin1String("*unknown*");
+ if (debugAll)
+ qDebug() << "deviceInfo:" << res << ", failure:" << failure;
+ emit Ios::IosDeviceManager::instance()->deviceInfo(deviceId, res);
+ /* should we also check the provision profiles??? i.e.
+ int fd;
+ startService(QLatin1String("com.apple.misagent"), &fd);
+ ... MISAgentCopyProvisioningProfiles, AMAuthInstallProvisioningGetProvisionedInfo & co still to add */
+}
+
+// ------- MobileDeviceLib implementation --------
+
+MobileDeviceLib::MobileDeviceLib() { }
+
+bool MobileDeviceLib::load()
+{
+#ifdef MOBILE_DEV_DIRECT_LINK
+ m_AMDSetLogLevel = &AMDSetLogLevel;
+ m_AMDeviceNotificationSubscribe = &AMDeviceNotificationSubscribe;
+ //m_AMDeviceNotificationUnsubscribe = &AMDeviceNotificationUnsubscribe;
+ m_AMDeviceCopyValue = &AMDeviceCopyValue;
+ m_AMDeviceGetConnectionID = &AMDeviceGetConnectionID;
+ m_AMDeviceCopyDeviceIdentifier = &AMDeviceCopyDeviceIdentifier;
+ m_AMDeviceConnect = &AMDeviceConnect;
+ //m_AMDevicePair = &AMDevicePair;
+ m_AMDeviceIsPaired = &AMDeviceIsPaired;
+ m_AMDeviceValidatePairing = &AMDeviceValidatePairing;
+ m_AMDeviceStartSession = &AMDeviceStartSession;
+ m_AMDeviceStopSession = &AMDeviceStopSession;
+ m_AMDeviceDisconnect = &AMDeviceDisconnect;
+ m_AMDeviceMountImage = &AMDeviceMountImage;
+ m_AMDeviceStartService = &AMDeviceStartService;
+ m_AMDeviceTransferApplication = &AMDeviceTransferApplication;
+ m_AMDeviceInstallApplication = &AMDeviceInstallApplication;
+ //m_AMDeviceUninstallApplication = &AMDeviceUninstallApplication;
+ //m_AMDeviceLookupApplications = &AMDeviceLookupApplications;
+#else
+ QLibrary *libAppleFSCompression = new QLibrary(QLatin1String("/System/Library/PrivateFrameworks/AppleFSCompression.framework/AppleFSCompression"));
+ if (!libAppleFSCompression->load())
+ addError("MobileDevice dependency AppleFSCompression failed to load");
+ deps << libAppleFSCompression;
+ QLibrary *libBom = new QLibrary(QLatin1String("/System/Library/PrivateFrameworks/Bom.framework/Bom"));
+ if (!libBom->load())
+ addError("MobileDevice dependency Bom failed to load");
+ deps << libBom;
+ lib.setFileName(QLatin1String("/System/Library/PrivateFrameworks/MobileDevice.framework/MobileDevice"));
+ if (!lib.load())
+ return false;
+ m_AMDSetLogLevel = reinterpret_cast<AMDSetLogLevelPtr>(lib.resolve("AMDSetLogLevel"));
+ if (m_AMDSetLogLevel == 0)
+ addError("MobileDeviceLib does not define AMDSetLogLevel");
+ m_AMDeviceNotificationSubscribe = reinterpret_cast<AMDeviceNotificationSubscribePtr>(lib.resolve("AMDeviceNotificationSubscribe"));
+ if (m_AMDeviceNotificationSubscribe == 0)
+ addError("MobileDeviceLib does not define AMDeviceNotificationSubscribe");
+ m_AMDeviceNotificationUnsubscribe = reinterpret_cast<AMDeviceNotificationUnsubscribePtr>(lib.resolve("AMDeviceNotificationUnsubscribe"));
+ if (m_AMDeviceNotificationUnsubscribe == 0)
+ addError("MobileDeviceLib does not define AMDeviceNotificationUnsubscribe");
+ m_AMDeviceCopyValue = reinterpret_cast<AMDeviceCopyValuePtr>(lib.resolve("AMDeviceCopyValue"));
+ if (m_AMDSetLogLevel == 0)
+ addError("MobileDeviceLib does not define AMDSetLogLevel");
+ m_AMDeviceGetConnectionID = reinterpret_cast<AMDeviceGetConnectionIDPtr>(lib.resolve("AMDeviceGetConnectionID"));
+ if (m_AMDeviceGetConnectionID == 0)
+ addError("MobileDeviceLib does not define AMDeviceGetConnectionID");
+ m_AMDeviceCopyDeviceIdentifier = reinterpret_cast<AMDeviceCopyDeviceIdentifierPtr>(lib.resolve("AMDeviceCopyDeviceIdentifier"));
+ if (m_AMDeviceCopyDeviceIdentifier == 0)
+ addError("MobileDeviceLib does not define AMDeviceCopyDeviceIdentifier");
+ m_AMDeviceConnect = reinterpret_cast<AMDeviceConnectPtr>(lib.resolve("AMDeviceConnect"));
+ if (m_AMDeviceConnect == 0)
+ addError("MobileDeviceLib does not define AMDeviceConnect");
+ m_AMDevicePair = reinterpret_cast<AMDevicePairPtr>(lib.resolve("AMDevicePair"));
+ if (m_AMDevicePair == 0)
+ addError("MobileDeviceLib does not define AMDevicePair");
+ m_AMDeviceIsPaired = reinterpret_cast<AMDeviceIsPairedPtr>(lib.resolve("AMDeviceIsPaired"));
+ if (m_AMDeviceIsPaired == 0)
+ addError("MobileDeviceLib does not define AMDeviceIsPaired");
+ m_AMDeviceValidatePairing = reinterpret_cast<AMDeviceValidatePairingPtr>(lib.resolve("AMDeviceValidatePairing"));
+ if (m_AMDeviceValidatePairing == 0)
+ addError("MobileDeviceLib does not define AMDeviceValidatePairing");
+ m_AMDeviceStartSession = reinterpret_cast<AMDeviceStartSessionPtr>(lib.resolve("AMDeviceStartSession"));
+ if (m_AMDeviceStartSession == 0)
+ addError("MobileDeviceLib does not define AMDeviceStartSession");
+ m_AMDeviceStopSession = reinterpret_cast<AMDeviceStopSessionPtr>(lib.resolve("AMDeviceStopSession"));
+ if (m_AMDeviceStopSession == 0)
+ addError("MobileDeviceLib does not define AMDeviceStopSession");
+ m_AMDeviceDisconnect = reinterpret_cast<AMDeviceDisconnectPtr>(lib.resolve("AMDeviceDisconnect"));
+ if (m_AMDeviceDisconnect == 0)
+ addError("MobileDeviceLib does not define AMDeviceDisconnect");
+ m_AMDeviceMountImage = reinterpret_cast<AMDeviceMountImagePtr>(lib.resolve("AMDeviceMountImage"));
+ if (m_AMDeviceMountImage == 0)
+ addError("MobileDeviceLib does not define AMDeviceMountImage");
+ m_AMDeviceStartService = reinterpret_cast<AMDeviceStartServicePtr>(lib.resolve("AMDeviceStartService"));
+ if (m_AMDeviceStartService == 0)
+ addError("MobileDeviceLib does not define AMDeviceStartService");
+ m_AMDeviceTransferApplication = reinterpret_cast<AMDeviceTransferApplicationPtr>(lib.resolve("AMDeviceTransferApplication"));
+ if (m_AMDeviceTransferApplication == 0)
+ addError("MobileDeviceLib does not define AMDeviceTransferApplication");
+ m_AMDeviceInstallApplication = reinterpret_cast<AMDeviceInstallApplicationPtr>(lib.resolve("AMDeviceInstallApplication"));
+ if (m_AMDeviceInstallApplication == 0)
+ addError("MobileDeviceLib does not define AMDeviceInstallApplication");
+ m_AMDeviceUninstallApplication = reinterpret_cast<AMDeviceUninstallApplicationPtr>(lib.resolve("AMDeviceUninstallApplication"));
+ if (m_AMDeviceUninstallApplication == 0)
+ addError("MobileDeviceLib does not define AMDeviceUninstallApplication");
+ m_AMDeviceLookupApplications = reinterpret_cast<AMDeviceLookupApplicationsPtr>(lib.resolve("AMDeviceLookupApplications"));
+ if (m_AMDeviceLookupApplications == 0)
+ addError("MobileDeviceLib does not define AMDeviceLookupApplications");
+#endif
+ return true;
+}
+
+bool MobileDeviceLib::isLoaded()
+{
+ return lib.isLoaded();
+}
+
+QStringList MobileDeviceLib::errors()
+{
+ return m_errors;
+}
+
+void MobileDeviceLib::setLogLevel(int i)
+{
+ if (m_AMDSetLogLevel)
+ m_AMDSetLogLevel(i);
+}
+
+am_res_t MobileDeviceLib::deviceNotificationSubscribe(AMDeviceNotificationCallback callback,
+ unsigned int v1, unsigned int v2, void *callbackArgs,
+ const AMDeviceNotification **handle)
+{
+ if (m_AMDeviceNotificationSubscribe)
+ return m_AMDeviceNotificationSubscribe(callback,v1,v2,callbackArgs,handle);
+ return -1;
+}
+
+am_res_t MobileDeviceLib::deviceNotificationUnsubscribe(void *handle)
+{
+ if (m_AMDeviceNotificationUnsubscribe)
+ return m_AMDeviceNotificationUnsubscribe(handle);
+ return -1;
+}
+
+CFPropertyListRef MobileDeviceLib::deviceCopyValue(AMDeviceRef device,CFStringRef group,CFStringRef key)
+{
+ if (m_AMDeviceCopyValue)
+ return m_AMDeviceCopyValue(device, group, key);
+ return 0;
+}
+
+unsigned int MobileDeviceLib::deviceGetConnectionID(AMDeviceRef device)
+{
+ if (m_AMDeviceGetConnectionID)
+ return m_AMDeviceGetConnectionID(device);
+ return -1;
+}
+
+CFStringRef MobileDeviceLib::deviceCopyDeviceIdentifier(AMDeviceRef device)
+{
+ if (m_AMDeviceCopyDeviceIdentifier)
+ return m_AMDeviceCopyDeviceIdentifier(device);
+ return 0;
+}
+
+am_res_t MobileDeviceLib::deviceConnect(AMDeviceRef device)
+{
+ if (m_AMDeviceConnect)
+ return m_AMDeviceConnect(device);
+ return -1;
+}
+
+am_res_t MobileDeviceLib::devicePair(AMDeviceRef device)
+{
+ if (m_AMDevicePair)
+ return m_AMDevicePair(device);
+ return -1;
+}
+
+am_res_t MobileDeviceLib::deviceIsPaired(AMDeviceRef device)
+{
+ if (m_AMDeviceIsPaired)
+ return m_AMDeviceIsPaired(device);
+ return -1;
+}
+
+am_res_t MobileDeviceLib::deviceValidatePairing(AMDeviceRef device)
+{
+ if (m_AMDeviceValidatePairing)
+ return m_AMDeviceValidatePairing(device);
+ return -1;
+}
+
+am_res_t MobileDeviceLib::deviceStartSession(AMDeviceRef device)
+{
+ if (m_AMDeviceStartSession)
+ return m_AMDeviceStartSession(device);
+ return -1;
+}
+
+am_res_t MobileDeviceLib::deviceStopSession(AMDeviceRef device)
+{
+ if (m_AMDeviceStopSession)
+ return m_AMDeviceStopSession(device);
+ return -1;
+}
+
+am_res_t MobileDeviceLib::deviceDisconnect(AMDeviceRef device)
+{
+ if (m_AMDeviceDisconnect)
+ return m_AMDeviceDisconnect(device);
+ return -1;
+}
+
+am_res_t MobileDeviceLib::deviceMountImage(AMDeviceRef device, CFStringRef imagePath,
+ CFDictionaryRef options,
+ AMDeviceMountImageCallback callback,
+ void * callbackExtraArgs)
+{
+ if (m_AMDeviceMountImage)
+ return m_AMDeviceMountImage(device, imagePath, options, callback, callbackExtraArgs);
+ return -1;
+}
+
+am_res_t MobileDeviceLib::deviceStartService(AMDeviceRef device, CFStringRef serviceName,
+ ServiceSocket *fdRef, void *extra)
+{
+ if (m_AMDeviceStartService)
+ return m_AMDeviceStartService(device, serviceName, fdRef, extra);
+ return -1;
+}
+
+am_res_t MobileDeviceLib::deviceTransferApplication(int serviceFd, CFStringRef appPath,
+ CFDictionaryRef options,
+ AMDeviceInstallApplicationCallback callback,
+ void *callbackExtraArgs)
+{
+ if (m_AMDeviceTransferApplication)
+ return m_AMDeviceTransferApplication(serviceFd, appPath, options, callback, callbackExtraArgs);
+ return -1;
+}
+
+am_res_t MobileDeviceLib::deviceInstallApplication(int serviceFd, CFStringRef appPath,
+ CFDictionaryRef options,
+ AMDeviceInstallApplicationCallback callback,
+ void *callbackExtraArgs)
+{
+ if (m_AMDeviceInstallApplication)
+ return m_AMDeviceInstallApplication(serviceFd, appPath, options, callback, callbackExtraArgs);
+ return -1;
+}
+
+am_res_t MobileDeviceLib::deviceUninstallApplication(int serviceFd, CFStringRef bundleId,
+ CFDictionaryRef options,
+ AMDeviceInstallApplicationCallback callback,
+ void* callbackExtraArgs)
+{
+ if (m_AMDeviceUninstallApplication)
+ return m_AMDeviceUninstallApplication(serviceFd, bundleId, options, callback, callbackExtraArgs);
+ return -1;
+}
+
+am_res_t MobileDeviceLib::deviceLookupApplications(AMDeviceRef device, unsigned int i,
+ CFDictionaryRef *res)
+{
+ if (m_AMDeviceLookupApplications)
+ return m_AMDeviceLookupApplications(device, i, res);
+ return -1;
+}
+
+void MobileDeviceLib::addError(const QString &msg)
+{
+ qDebug() << "MobileDeviceLib ERROR:" << msg;
+ m_errors << QLatin1String("MobileDeviceLib ERROR:") << msg;
+}
+
+void MobileDeviceLib::addError(const char *msg)
+{
+ addError(QLatin1String(msg));
+}
+
+void CommandSession::internalDeviceAvailableCallback(QString deviceId, AMDeviceRef device)
+{
+ if (deviceId != this->deviceId && !this->deviceId.isEmpty())
+ addError(QString::fromLatin1("deviceId mismatch in deviceAvailableCallback, %1 vs %2")
+ .arg(deviceId, this->deviceId));
+ this->deviceId = deviceId;
+ if (this->device)
+ addError(QString::fromLatin1("session had non null device in deviceAvailableCallback"));
+ this->device = device;
+ deviceCallbackReturned();
+}
+
+} // namespace Internal
+
+// ------- IosManager implementation (just forwarding) --------
+
+IosDeviceManager *IosDeviceManager::instance()
+{
+ static IosDeviceManager instanceVal;
+ return &instanceVal;
+}
+
+IosDeviceManager::IosDeviceManager(QObject *parent) :
+ QObject(parent)
+{
+ d = new Internal::IosDeviceManagerPrivate(this);
+}
+
+bool IosDeviceManager::watchDevices() {
+ return d->watchDevices();
+}
+
+void IosDeviceManager::requestAppOp(const QString &bundlePath, const QStringList &extraArgs,
+ AppOp appOp, const QString &deviceId, int timeout) {
+ d->requestAppOp(bundlePath, extraArgs, appOp, deviceId, timeout);
+}
+
+void IosDeviceManager::requestDeviceInfo(const QString &deviceId, int timeout)
+{
+ d->requestDeviceInfo(deviceId, timeout);
+}
+
+int IosDeviceManager::processGdbServer(int fd)
+{
+ return d->processGdbServer(fd);
+}
+
+QStringList IosDeviceManager::errors() {
+ return d->errors();
+}
+
+void IosDeviceManager::checkPendingLookups()
+{
+ d->checkPendingLookups();
+}
+
+} // namespace Ios
diff --git a/src/tools/iostool/iosdevicemanager.h b/src/tools/iostool/iosdevicemanager.h
new file mode 100644
index 0000000000..81fa646061
--- /dev/null
+++ b/src/tools/iostool/iosdevicemanager.h
@@ -0,0 +1,93 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** 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.
+**
+****************************************************************************/
+#ifndef IOSMANAGER_H
+#define IOSMANAGER_H
+
+#include <QObject>
+#include <QString>
+#include <QStringList>
+#include <QMap>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <unistd.h>
+#include <string.h>
+
+namespace Ios {
+namespace Internal {
+class IosDeviceManagerPrivate;
+} // namespace Internal
+
+class IosDeviceManager : public QObject
+{
+ Q_OBJECT
+public:
+ typedef QMap<QString,QString> Dict;
+ enum OpStatus {
+ Success = 0,
+ Warning = 1,
+ Failure = 2
+ };
+ enum AppOp {
+ None = 0,
+ Install = 1,
+ Run = 2,
+ InstallAndRun = 3
+ };
+
+ static IosDeviceManager *instance();
+ bool watchDevices();
+ void requestAppOp(const QString &bundlePath, const QStringList &extraArgs, AppOp appOp,
+ const QString &deviceId, int timeout = 1000);
+ void requestDeviceInfo(const QString &deviceId, int timeout = 1000);
+ int processGdbServer(int fd);
+ QStringList errors();
+signals:
+ void deviceAdded(const QString &deviceId);
+ void deviceRemoved(const QString &deviceId);
+ void isTransferringApp(const QString &bundlePath, const QString &deviceId, int progress,
+ const QString &info);
+ void didTransferApp(const QString &bundlePath, const QString &deviceId,
+ Ios::IosDeviceManager::OpStatus status);
+ void didStartApp(const QString &bundlePath, const QString &deviceId,
+ Ios::IosDeviceManager::OpStatus status, int gdbFd);
+ void deviceInfo(const QString &deviceId, const Ios::IosDeviceManager::Dict &info);
+ void appOutput(const QString &output);
+ void errorMsg(const QString &msg);
+private slots:
+ void checkPendingLookups();
+private:
+ friend class Internal::IosDeviceManagerPrivate;
+ IosDeviceManager(QObject *parent = 0);
+ Internal::IosDeviceManagerPrivate *d;
+};
+
+} // namespace Ios
+
+#endif // IOSMANAGER_H
diff --git a/src/tools/iostool/iostool.pro b/src/tools/iostool/iostool.pro
new file mode 100644
index 0000000000..1e503887e2
--- /dev/null
+++ b/src/tools/iostool/iostool.pro
@@ -0,0 +1,27 @@
+TARGET = iosTool
+
+QT += core
+QT += gui xml
+
+CONFIG += console
+
+# Prevent from popping up in the dock when launched.
+# We embed the Info.plist file, so the application doesn't need to
+# be a bundle.
+QMAKE_LFLAGS += -sectcreate __TEXT __info_plist $$shell_quote($$PWD/Info.plist)
+CONFIG -= app_bundle
+
+LIBS += -framework CoreFoundation -framework CoreServices -framework IOKit -lssl -lbz2 -framework Security -framework SystemConfiguration
+
+TEMPLATE = app
+
+include(../../../qtcreator.pri)
+
+DESTDIR = $$IDE_BIN_PATH
+include(../../rpath.pri)
+
+SOURCES += main.cpp \
+ iosdevicemanager.cpp
+
+HEADERS += \
+ iosdevicemanager.h
diff --git a/src/tools/iostool/iostool.qbs b/src/tools/iostool/iostool.qbs
new file mode 100644
index 0000000000..66c738d5b4
--- /dev/null
+++ b/src/tools/iostool/iostool.qbs
@@ -0,0 +1,21 @@
+import qbs.base 1.0
+import "../QtcTool.qbs" as QtcTool
+
+QtcTool {
+ name: "iostool"
+ condition: qbs.targetOS.contains("osx")
+
+ Depends { name: "Qt.widgets" }
+ Depends { name: "Qt.xml" }
+ Depends { name: "app_version_header" }
+
+ files: [
+ "main.cpp",
+ "iosdevicemanager.cpp",
+ "iosdevicemanager.h"
+ ]
+ cpp.linkerFlags: base.concat(["-sectcreate", "__TEXT", "__info_plist", path + "/Info.plist"])
+ cpp.frameworks: base.concat(["CoreFoundation", "CoreServices", "IOKit", "Security",
+ "SystemConfiguration"])
+ cpp.libraries: base.concat(["ssl", "bz2"])
+}
diff --git a/src/tools/iostool/main.cpp b/src/tools/iostool/main.cpp
new file mode 100644
index 0000000000..44136fc316
--- /dev/null
+++ b/src/tools/iostool/main.cpp
@@ -0,0 +1,386 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** 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 <QGuiApplication>
+#include <QTextStream>
+#include <QDebug>
+#include <QXmlStreamWriter>
+#include <QFile>
+#include <QMapIterator>
+#include "iosdevicemanager.h"
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <unistd.h>
+#include <string.h>
+
+
+class IosTool: public QObject {
+ Q_OBJECT
+public:
+ IosTool(QObject *parent = 0);
+ void run(const QStringList &args);
+ void doExit(int errorCode = 0);
+ void writeMsg(const char *msg);
+ void writeMsg(const QString &msg);
+ void stopXml(int errorCode);
+public slots:
+ void isTransferringApp(const QString &bundlePath, const QString &deviceId, int progress,
+ const QString &info);
+ void didTransferApp(const QString &bundlePath, const QString &deviceId,
+ Ios::IosDeviceManager::OpStatus status);
+ void didStartApp(const QString &bundlePath, const QString &deviceId,
+ Ios::IosDeviceManager::OpStatus status, int gdbFd);
+ void deviceInfo(const QString &deviceId, const Ios::IosDeviceManager::Dict &info);
+ void appOutput(const QString &output);
+ void errorMsg(const QString &msg);
+private:
+ int maxProgress;
+ int opLeft;
+ bool debug;
+ bool inAppOutput;
+ bool splitAppOutput; // as QXmlStreamReader reports the text attributes atomically it is better to split
+ Ios::IosDeviceManager::AppOp appOp;
+ QFile outFile;
+ QXmlStreamWriter out;
+};
+
+IosTool::IosTool(QObject *parent):
+ QObject(parent),
+ maxProgress(0),
+ opLeft(0),
+ debug(false),
+ inAppOutput(false),
+ splitAppOutput(true),
+ appOp(Ios::IosDeviceManager::Install),
+ outFile(),
+ out(&outFile)
+{
+ outFile.open(stdout, QIODevice::WriteOnly, QFileDevice::DontCloseHandle);
+ out.setAutoFormatting(true);
+ out.setCodec("UTF-8");
+}
+
+void IosTool::run(const QStringList &args)
+{
+ Ios::IosDeviceManager *manager = Ios::IosDeviceManager::instance();
+ QString deviceId;
+ QString bundlePath;
+ bool deviceInfo = false;
+ bool printHelp = false;
+ int timeout = 1000;
+ QStringList extraArgs;
+
+ out.writeStartDocument();
+ out.writeStartElement(QLatin1String("query_result"));
+ for (int iarg = 1; iarg < args.size(); ++iarg) {
+ const QString &arg = args[iarg];
+ if (arg == QLatin1String("-device-id")) {
+ if (++iarg == args.size()) {
+ writeMsg("missing device id value after -device-id");
+ printHelp = true;
+ }
+ deviceId = args.value(iarg);
+ } else if (arg == QLatin1String("-bundle")) {
+ if (++iarg == args.size()) {
+ writeMsg("missing bundle path after -bundle");
+ printHelp = true;
+ }
+ bundlePath = args.value(iarg);
+ } else if (arg == QLatin1String("-deploy")) {
+ appOp = Ios::IosDeviceManager::AppOp(appOp | Ios::IosDeviceManager::Install);
+ } else if (arg == QLatin1String("-run")) {
+ appOp = Ios::IosDeviceManager::AppOp(appOp | Ios::IosDeviceManager::Run);
+ } else if (arg == QLatin1String("-debug")) {
+ appOp = Ios::IosDeviceManager::AppOp(appOp | Ios::IosDeviceManager::Run);
+ debug = true;
+ } else if (arg == QLatin1String("-device-info")) {
+ deviceInfo = true;
+ } else if (arg == QLatin1String("-timeout")) {
+ if (++iarg == args.size()) {
+ writeMsg("missing timeout value after -timeout");
+ printHelp = true;
+ }
+ bool ok = false;
+ int tOut = args.value(iarg).toInt(&ok);
+ if (ok && tOut > 0) {
+ timeout = tOut;
+ } else {
+ writeMsg("timeout value should be an integer");
+ printHelp = true;
+ }
+ } else if (arg == QLatin1String("-extra-args")) {
+ ++iarg;
+ extraArgs = args.mid(iarg, args.size() - iarg);
+ } else if (arg == QLatin1String("-help") || arg == QLatin1String("--help")) {
+ printHelp = true;
+ } else {
+ writeMsg(QString::fromLatin1("unexpected argument '%1'").arg(arg));
+ }
+ }
+ if (printHelp) {
+ out.writeStartElement(QLatin1String("msg"));
+ out.writeCharacters(QLatin1String("iosTool [-device-id <deviceId>] [-bundle <pathToBundle>] [-deploy] [-run] [-debug]\n"));
+ out.writeCharacters(QLatin1String(" [-device-info] [-timeout <timeoutIn_ms>]\n")); // to do pass in env as stub does
+ out.writeCharacters(QLatin1String(" [-extra-args <arguments for the target app>]"));
+ out.writeEndElement();
+ doExit(-1);
+ return;
+ }
+ outFile.flush();
+ connect(manager,SIGNAL(isTransferringApp(QString,QString,int,QString)),
+ SLOT(isTransferringApp(QString,QString,int,QString)));
+ connect(manager,SIGNAL(didTransferApp(QString,QString,Ios::IosDeviceManager::OpStatus)),
+ SLOT(didTransferApp(QString,QString,Ios::IosDeviceManager::OpStatus)));
+ connect(manager,SIGNAL(didStartApp(QString,QString,Ios::IosDeviceManager::OpStatus,int)),
+ SLOT(didStartApp(QString,QString,Ios::IosDeviceManager::OpStatus,int)));
+ connect(manager,SIGNAL(deviceInfo(QString,Ios::IosDeviceManager::Dict)),
+ SLOT(deviceInfo(QString,Ios::IosDeviceManager::Dict)));
+ connect(manager,SIGNAL(appOutput(QString)), SLOT(appOutput(QString)));
+ connect(manager,SIGNAL(errorMsg(QString)), SLOT(errorMsg(QString)));
+ manager->watchDevices();
+ if (deviceInfo) {
+ if (!bundlePath.isEmpty())
+ writeMsg("-device-info overrides bundlePath");
+ ++opLeft;
+ manager->requestDeviceInfo(deviceId, timeout);
+ } else if (!bundlePath.isEmpty()) {
+ switch (appOp) {
+ case Ios::IosDeviceManager::None:
+ break;
+ case Ios::IosDeviceManager::Install:
+ case Ios::IosDeviceManager::Run:
+ ++opLeft;
+ break;
+ case Ios::IosDeviceManager::InstallAndRun:
+ opLeft += 2;
+ break;
+ }
+ maxProgress = 200;
+ manager->requestAppOp(bundlePath, extraArgs, appOp, deviceId, timeout);
+ }
+ if (opLeft == 0)
+ doExit(0);
+}
+
+void IosTool::stopXml(int errorCode)
+{
+ out.writeEmptyElement(QLatin1String("exit"));
+ out.writeAttribute(QLatin1String("code"), QString::number(errorCode));
+ out.writeEndElement(); // result element (hopefully)
+ out.writeEndDocument();
+ outFile.flush();
+}
+
+void IosTool::doExit(int errorCode)
+{
+ stopXml(errorCode);
+ QCoreApplication::exit(errorCode); // sometime does not really exit
+ exit(errorCode);
+}
+
+void IosTool::isTransferringApp(const QString &bundlePath, const QString &deviceId, int progress,
+ const QString &info)
+{
+ Q_UNUSED(bundlePath);
+ Q_UNUSED(deviceId);
+ out.writeStartElement(QLatin1String("status"));
+ out.writeAttribute(QLatin1String("progress"), QString::number(progress));
+ out.writeAttribute(QLatin1String("max_progress"), QString::number(maxProgress));
+ out.writeCharacters(info);
+ out.writeEndElement();
+ outFile.flush();
+}
+
+int send_fd(int socket, int fd_to_send)
+{
+ /* storage space needed for an ancillary element with a paylod of length is CMSG_SPACE(sizeof(length)) */
+ char ancillary_element_buffer[CMSG_SPACE(sizeof(int))];
+ int available_ancillary_element_buffer_space;
+
+ /* at least one vector of one byte must be sent */
+ char message_buffer[1];
+ message_buffer[0] = '.';
+ iovec io_vector[1];
+
+ io_vector[0].iov_base = message_buffer;
+ io_vector[0].iov_len = 1;
+
+ /* initialize socket message */
+ msghdr socket_message;
+ memset(&socket_message, 0, sizeof(msghdr));
+ socket_message.msg_iov = io_vector;
+ socket_message.msg_iovlen = 1;
+
+ /* provide space for the ancillary data */
+ available_ancillary_element_buffer_space = CMSG_SPACE(sizeof(int));
+ memset(ancillary_element_buffer, 0, available_ancillary_element_buffer_space);
+ socket_message.msg_control = ancillary_element_buffer;
+ socket_message.msg_controllen = available_ancillary_element_buffer_space;
+
+ /* initialize a single ancillary data element for fd passing */
+ cmsghdr *control_message = NULL;
+ control_message = CMSG_FIRSTHDR(&socket_message);
+ control_message->cmsg_level = SOL_SOCKET;
+ control_message->cmsg_type = SCM_RIGHTS;
+ control_message->cmsg_len = CMSG_LEN(sizeof(int));
+ *((int *) CMSG_DATA(control_message)) = fd_to_send;
+
+ return sendmsg(socket, &socket_message, 0);
+}
+
+void IosTool::didTransferApp(const QString &bundlePath, const QString &deviceId,
+ Ios::IosDeviceManager::OpStatus status)
+{
+ Q_UNUSED(bundlePath);
+ Q_UNUSED(deviceId);
+ if (status == Ios::IosDeviceManager::Success) {
+ out.writeStartElement(QLatin1String("status"));
+ out.writeAttribute(QLatin1String("progress"), QString::number(maxProgress));
+ out.writeAttribute(QLatin1String("max_progress"), QString::number(maxProgress));
+ out.writeCharacters(QLatin1String("App Transferred"));
+ out.writeEndElement();
+ }
+ out.writeEmptyElement(QLatin1String("app_transfer"));
+ out.writeAttribute(QLatin1String("status"),
+ (status == Ios::IosDeviceManager::Success) ?
+ QLatin1String("SUCCESS") :
+ QLatin1String("FAILURE"));
+ //out.writeCharacters(QString()); // trigger a complete closing of the empty element
+ outFile.flush();
+ if (status != Ios::IosDeviceManager::Success || --opLeft == 0)
+ doExit(-1);
+}
+
+void IosTool::didStartApp(const QString &bundlePath, const QString &deviceId,
+ Ios::IosDeviceManager::OpStatus status, int gdbFd)
+{
+ Q_UNUSED(bundlePath);
+ Q_UNUSED(deviceId);
+ out.writeEmptyElement(QLatin1String("app_started"));
+ out.writeAttribute(QLatin1String("status"),
+ (status == Ios::IosDeviceManager::Success) ?
+ QLatin1String("SUCCESS") :
+ QLatin1String("FAILURE"));
+ //out.writeCharacters(QString()); // trigger a complete closing of the empty element
+ outFile.flush();
+ if (appOp == Ios::IosDeviceManager::Install) {
+ doExit();
+ return;
+ }
+ if (gdbFd <= 0) {
+ writeMsg("no gdb connection");
+ doExit(-2);
+ return;
+ }
+ if (appOp != Ios::IosDeviceManager::InstallAndRun && appOp != Ios::IosDeviceManager::Run) {
+ writeMsg(QString::fromLatin1("unexpected appOp value %1").arg(appOp));
+ doExit(-3);
+ return;
+ }
+ if (debug) {
+ stopXml(0);
+ // these are 67 characters, this is used as read size on the other side...
+ const char *msg = "now sending the gdbserver socket, will need a unix socket to succeed";
+ outFile.write(msg, strlen(msg));
+ outFile.flush();
+ int sent = send_fd(1, gdbFd);
+ sleep(1);
+ QCoreApplication::exit(sent == -1);
+ } else {
+ if (!splitAppOutput) {
+ out.writeStartElement(QLatin1String("app_output"));
+ inAppOutput = true;
+ }
+ outFile.flush();
+ Ios::IosDeviceManager::instance()->processGdbServer(gdbFd);
+ if (!splitAppOutput) {
+ inAppOutput = false;
+ out.writeEndElement();
+ }
+ outFile.flush();
+ close(gdbFd);
+ doExit();
+ }
+}
+
+void IosTool::writeMsg(const char *msg)
+{
+ out.writeTextElement(QLatin1String("msg"), QLatin1String(msg));
+ outFile.flush();
+}
+
+void IosTool::writeMsg(const QString &msg)
+{
+ out.writeTextElement(QLatin1String("msg"), msg);
+ outFile.flush();
+}
+
+void IosTool::deviceInfo(const QString &deviceId, const Ios::IosDeviceManager::Dict &devInfo)
+{
+ Q_UNUSED(deviceId);
+ out.writeTextElement(QLatin1String("device_id"), deviceId);
+ out.writeStartElement(QLatin1String("device_info"));
+ QMapIterator<QString,QString> i(devInfo);
+ while (i.hasNext()) {
+ i.next();
+ out.writeStartElement(QLatin1String("item"));
+ out.writeTextElement(QLatin1String("key"), i.key());
+ out.writeTextElement(QLatin1String("value"), i.value());
+ out.writeEndElement();
+ }
+ out.writeEndElement();
+ outFile.flush();
+ doExit();
+}
+
+void IosTool::appOutput(const QString &output)
+{
+ if (inAppOutput)
+ out.writeCharacters(output);
+ else
+ out.writeTextElement(QLatin1String("app_output"), output);
+ outFile.flush();
+}
+
+void IosTool::errorMsg(const QString &msg)
+{
+ writeMsg(msg + QLatin1Char('\n'));
+}
+
+
+int main(int argc, char *argv[])
+{
+ QGuiApplication a(argc, argv);
+ IosTool tool;
+ tool.run(QCoreApplication::arguments());
+ int res = a.exec();
+ exit(res);
+}
+
+#include "main.moc"
diff --git a/src/tools/tools.pro b/src/tools/tools.pro
index fa12ab6449..6785bb782a 100644
--- a/src/tools/tools.pro
+++ b/src/tools/tools.pro
@@ -4,13 +4,18 @@ SUBDIRS = qtpromaker \
qmlpuppet \
../plugins/cpaster/frontend \
sdktool \
- valgrindfake
+ valgrindfake \
+ 3rdparty
win32 {
SUBDIRS += qtcdebugger
SUBDIRS += wininterrupt
}
+mac {
+ SUBDIRS += iostool
+}
+
QT_BREAKPAD_ROOT_PATH = $$(QT_BREAKPAD_ROOT_PATH)
!isEmpty(QT_BREAKPAD_ROOT_PATH) {
SUBDIRS += qtcrashhandler
diff --git a/src/tools/tools.qbs b/src/tools/tools.qbs
index 13888afada..454ee9b77f 100644
--- a/src/tools/tools.qbs
+++ b/src/tools/tools.qbs
@@ -7,6 +7,8 @@ Project {
"qtcreatorcrashhandler/qtcreatorcrashhandler.qbs",
"qtpromaker/qtpromaker.qbs",
"sdktool/sdktool.qbs",
- "valgrindfake/valgrindfake.qbs"
+ "valgrindfake/valgrindfake.qbs",
+ "3rdparty/iossim/iossim.qbs",
+ "iostool/iostool.qbs"
].concat(project.additionalTools)
}