Commit e7576436 authored by Fawzi Mohamed's avatar Fawzi Mohamed Committed by Fawzi Mohamed
Browse files

iOS: fix simulator selection



get simulator type and SDK version dynamically from the available ones,
and let the user choose which one to use.
This fixes the static solution that did break with Xcode 6

Change-Id: I5cb2be68b9ea8736fc880cf3dd9d39d77f030293
Reviewed-by: default avatarFawzi Mohamed <fawzi.mohamed@theqtcompany.com>
parent a3c9104e
......@@ -413,10 +413,11 @@ void IosConfigurations::updateSimulators()
DeviceManager *devManager = DeviceManager::instance();
Core::Id devId = Constants::IOS_SIMULATOR_DEVICE_ID;
IDevice::ConstPtr dev = devManager->find(devId);
if (!dev.isNull())
return;
IosSimulator *newDev = new IosSimulator(devId);
devManager->addDevice(IDevice::ConstPtr(newDev));
if (dev.isNull()) {
dev = IDevice::ConstPtr(new IosSimulator(devId));
devManager->addDevice(dev);
}
IosSimulator::updateAvailableDevices();
}
void IosConfigurations::setDeveloperPath(const FileName &devPath)
......
......@@ -38,17 +38,6 @@ namespace Internal {
Q_DECLARE_LOGGING_CATEGORY(iosLog)
} // namespace Internal
namespace IosDeviceType {
enum Enum {
IosDevice,
SimulatedIphone,
SimulatedIpad,
SimulatedIphoneRetina4Inch,
SimulatedIphoneRetina3_5Inch,
SimulatedIpadRetina
};
}
namespace Constants {
const char IOS_SETTINGS_ID[] = "ZZ.Ios Configurations";
const char IOS_SETTINGS_CATEGORY[] = "XA.Ios";
......
......@@ -134,7 +134,7 @@ void IosDeployStep::run(QFutureInterface<bool> &fi)
}
m_transferStatus = TransferInProgress;
QTC_CHECK(m_toolHandler == 0);
m_toolHandler = new IosToolHandler(IosDeviceType::IosDevice, this);
m_toolHandler = new IosToolHandler(IosDeviceType(), this);
m_futureInterface.setProgressRange(0, 200);
m_futureInterface.setProgressValueAndText(0, QLatin1String("Transferring application"));
m_futureInterface.reportStarted();
......
......@@ -29,6 +29,7 @@
****************************************************************************/
#include "iosdevice.h"
#include "iossimulator.h"
#include "iosconstants.h"
#include "iosconfigurations.h"
#include "iostoolhandler.h"
......@@ -291,7 +292,7 @@ void IosDeviceManager::deviceDisconnected(const QString &uid)
void IosDeviceManager::updateInfo(const QString &devId)
{
IosToolHandler *requester = new IosToolHandler(IosDeviceType::IosDevice, this);
IosToolHandler *requester = new IosToolHandler(IosDeviceType(), this);
connect(requester, SIGNAL(deviceInfo(Ios::IosToolHandler*,QString,Ios::IosToolHandler::Dict)),
SLOT(deviceInfo(Ios::IosToolHandler*,QString,Ios::IosToolHandler::Dict)), Qt::QueuedConnection);
connect(requester, SIGNAL(finished(Ios::IosToolHandler*)),
......
......@@ -47,6 +47,7 @@
#include <qtsupport/qtkitinformation.h>
#include <QList>
#include <QStandardItemModel>
#include <utils/qtcprocess.h>
#include <utils/qtcassert.h>
......@@ -79,6 +80,7 @@ private slots:
private:
Ui::IosRunConfiguration *m_ui;
IosRunConfiguration *m_runConfiguration;
QStandardItemModel m_deviceTypeModel;
};
IosRunConfiguration::IosRunConfiguration(Target *parent, Core::Id id, const QString &path)
......@@ -103,7 +105,6 @@ void IosRunConfiguration::init()
m_parseInProgress = project->parseInProgress(m_profilePath);
m_lastIsEnabled = isEnabled();
m_lastDisabledReason = disabledReason();
m_deviceType = IosDeviceType::IosDevice;
updateDisplayNames();
connect(DeviceManager::instance(), SIGNAL(updated()),
SLOT(deviceChanges()));
......@@ -161,9 +162,9 @@ QStringList IosRunConfiguration::commandLineArguments()
void IosRunConfiguration::updateDisplayNames()
{
if (DeviceTypeKitInformation::deviceTypeId(target()->kit()) == Constants::IOS_DEVICE_TYPE)
m_deviceType = IosDeviceType::IosDevice;
else if (m_deviceType == IosDeviceType::IosDevice)
m_deviceType = IosDeviceType::SimulatedIphoneRetina4Inch;
m_deviceType = IosDeviceType();
else if (m_deviceType.type == IosDeviceType::IosDevice)
m_deviceType = IosDeviceType(IosDeviceType::SimulatedDevice);
IDevice::ConstPtr dev = DeviceKitInformation::device(target()->kit());
const QString devName = dev.isNull() ? IosDevice::name() : dev->displayName();
setDefaultDisplayName(tr("Run on %1").arg(devName));
......@@ -264,19 +265,14 @@ Utils::FileName IosRunConfiguration::localExecutable() const
bool IosRunConfiguration::fromMap(const QVariantMap &map)
{
m_arguments = map.value(runConfigurationKey).toStringList();
IosDeviceType::Enum deviceType = static_cast<IosDeviceType::Enum>(map.value(deviceTypeKey)
.toInt());
bool valid = false;
for (int i = 0 ; i < nSimulatedDevices; ++i)
if (simulatedDevices[i] == m_deviceType)
valid = true;
if (valid)
m_deviceType = deviceType;
else if (DeviceTypeKitInformation::deviceTypeId(target()->kit()) == Constants::IOS_DEVICE_TYPE)
m_deviceType = IosDeviceType::IosDevice;
else
m_deviceType = IosDeviceType::SimulatedIphoneRetina4Inch;
bool deviceTypeIsInt;
map.value(deviceTypeKey).toInt(&deviceTypeIsInt);
if (deviceTypeIsInt || !m_deviceType.fromMap(map.value(deviceTypeKey).toMap())) {
if (DeviceTypeKitInformation::deviceTypeId(target()->kit()) == Constants::IOS_DEVICE_TYPE)
m_deviceType = IosDeviceType(IosDeviceType::IosDevice);
else
m_deviceType = IosDeviceType(IosDeviceType::SimulatedDevice);
}
return RunConfiguration::fromMap(map);
}
......@@ -284,7 +280,7 @@ QVariantMap IosRunConfiguration::toMap() const
{
QVariantMap res = RunConfiguration::toMap();
res[runConfigurationKey] = m_arguments;
res[deviceTypeKey] = m_deviceType;
res[deviceTypeKey] = deviceType().toMap();
return res;
}
......@@ -358,12 +354,29 @@ QString IosRunConfiguration::disabledReason() const
return RunConfiguration::disabledReason();
}
IosDeviceType::Enum IosRunConfiguration::deviceType() const
IosDeviceType IosRunConfiguration::deviceType() const
{
QList<IosDeviceType> availableSimulators;
if (m_deviceType.type == IosDeviceType::SimulatedDevice)
availableSimulators = IosSimulator::availableDevices();
if (!availableSimulators.isEmpty()) {
QList<IosDeviceType> elegibleDevices;
QString devname = m_deviceType.identifier.split(QLatin1Char(',')).value(0);
foreach (const IosDeviceType &dType, availableSimulators) {
if (dType == m_deviceType)
return m_deviceType;
if (!devname.isEmpty() && dType.identifier.startsWith(devname)
&& dType.identifier.split(QLatin1Char(',')).value(0) == devname)
elegibleDevices << dType;
}
if (!elegibleDevices.isEmpty())
return elegibleDevices.last();
return availableSimulators.last();
}
return m_deviceType;
}
void IosRunConfiguration::setDeviceType(IosDeviceType::Enum deviceType)
void IosRunConfiguration::setDeviceType(const IosDeviceType &deviceType)
{
m_deviceType = deviceType;
}
......@@ -372,6 +385,7 @@ IosRunConfigurationWidget::IosRunConfigurationWidget(IosRunConfiguration *runCon
m_ui(new Ui::IosRunConfiguration), m_runConfiguration(runConfiguration)
{
m_ui->setupUi(this);
m_ui->deviceTypeComboBox->setModel(&m_deviceTypeModel);
updateValues();
connect(m_ui->deviceTypeComboBox, SIGNAL(currentIndexChanged(int)),
......@@ -436,25 +450,46 @@ void IosRunConfigurationWidget::argumentsLineEditTextEdited()
void IosRunConfigurationWidget::setDeviceTypeIndex(int devIndex)
{
if (devIndex >= 0 && devIndex < nSimulatedDevices)
m_runConfiguration->setDeviceType(simulatedDevices[devIndex]);
else
m_runConfiguration->setDeviceType(IosDeviceType::SimulatedIphoneRetina4Inch);
QVariant selectedDev = m_deviceTypeModel.data(m_deviceTypeModel.index(devIndex, 0), Qt::UserRole + 1);
if (selectedDev.isValid())
m_runConfiguration->setDeviceType(selectedDev.value<IosDeviceType>());
}
void IosRunConfigurationWidget::updateValues()
{
bool showDeviceSelector = m_runConfiguration->deviceType() != IosDeviceType::IosDevice;
bool showDeviceSelector = m_runConfiguration->deviceType().type != IosDeviceType::IosDevice;
m_ui->deviceTypeLabel->setVisible(showDeviceSelector);
m_ui->deviceTypeComboBox->setVisible(showDeviceSelector);
if (showDeviceSelector && m_deviceTypeModel.rowCount() == 0) {
foreach (const IosDeviceType &dType, IosSimulator::availableDevices()) {
QStandardItem *item = new QStandardItem(dType.displayName);
QVariant v;
v.setValue(dType);
item->setData(v);
m_deviceTypeModel.appendRow(item);
}
}
QStringList args = m_runConfiguration->commandLineArguments();
QString argsString = argListToString(args);
if (m_runConfiguration->deviceType() == IosDeviceType::IosDevice)
for (int i = 0; i < nSimulatedDevices; ++i)
if (simulatedDevices[i] == m_runConfiguration->deviceType())
IosDeviceType currentDType = m_runConfiguration->deviceType();
if (!m_ui->deviceTypeComboBox->currentData().isValid()
|| currentDType != m_ui->deviceTypeComboBox->currentData().value<IosDeviceType>()) {
bool didSet = false;
for (int i = 0; m_deviceTypeModel.hasIndex(i, 0); ++i) {
QVariant vData = m_deviceTypeModel.data(m_deviceTypeModel.index(i, 0), Qt::UserRole + 1);
IosDeviceType dType = vData.value<IosDeviceType>();
if (dType == currentDType) {
m_ui->deviceTypeComboBox->setCurrentIndex(i);
didSet = true;
break;
}
}
if (!didSet) {
qCWarning(iosLog) << "could not set " << currentDType << " as it is not in model";
}
}
m_ui->argumentsLineEdit->setText(argsString);
m_ui->executableLineEdit->setText(m_runConfiguration->localExecutable().toUserOutput());
}
......
......@@ -32,6 +32,7 @@
#include "iosconstants.h"
#include "iosconfigurations.h"
#include "iossimulator.h"
#include <projectexplorer/runconfiguration.h>
#include <utils/fileutils.h>
......@@ -43,16 +44,6 @@ class QmakeProFileNode;
namespace Ios {
namespace Internal {
enum { nSimulatedDevices = 4 };
static const IosDeviceType::Enum simulatedDevices[nSimulatedDevices] = {
// skip iPhone as it does not support iOS7
// TODO: clean solution would be to check also sdk version or make it configurable
IosDeviceType::SimulatedIphoneRetina3_5Inch,
IosDeviceType::SimulatedIphoneRetina4Inch,
IosDeviceType::SimulatedIpad,
IosDeviceType::SimulatedIpadRetina
};
class IosDeployStep;
class IosRunConfigurationFactory;
class IosRunConfigurationWidget;
......@@ -76,8 +67,8 @@ public:
Utils::FileName localExecutable() const;
bool isEnabled() const;
QString disabledReason() const;
IosDeviceType::Enum deviceType() const;
void setDeviceType(IosDeviceType::Enum deviceType);
IosDeviceType deviceType() const;
void setDeviceType(const IosDeviceType &deviceType);
bool fromMap(const QVariantMap &map) Q_DECL_OVERRIDE;
QVariantMap toMap() const Q_DECL_OVERRIDE;
......@@ -102,7 +93,7 @@ private:
bool m_lastIsEnabled;
bool m_parseInProgress;
bool m_parseSuccess;
IosDeviceType::Enum m_deviceType;
IosDeviceType m_deviceType;
};
} // namespace Internal
......
......@@ -32,7 +32,7 @@
#include "iosconfigurations.h"
#include "iostoolhandler.h"
#include "iosconstants.h"
#include "iossimulator.h"
#include <projectexplorer/devicesupport/idevice.h>
......@@ -90,7 +90,7 @@ private:
QString m_bundleDir;
QStringList m_arguments;
ProjectExplorer::IDevice::ConstPtr m_device;
IosDeviceType::Enum m_deviceType;
IosDeviceType m_deviceType;
bool m_cppDebug;
bool m_qmlDebug;
bool m_cleanExit;
......
......@@ -30,10 +30,13 @@
#include "iossimulator.h"
#include "iosconstants.h"
#include "iostoolhandler.h"
#include <projectexplorer/kitinformation.h>
#include <QCoreApplication>
#include <QMapIterator>
#include <QMutexLocker>
#include <QProcess>
using namespace ProjectExplorer;
......@@ -41,6 +44,13 @@ using namespace ProjectExplorer;
namespace Ios {
namespace Internal {
static const QLatin1String iosDeviceTypeDisplayNameKey = QLatin1String("displayName");
static const QLatin1String iosDeviceTypeTypeKey = QLatin1String("type");
static const QLatin1String iosDeviceTypeIdentifierKey = QLatin1String("identifier");
QMutex IosSimulator::_mutex;
QList<IosDeviceType> IosSimulator::_availableDevices;
IosSimulator::IosSimulator(Core::Id id)
: IDevice(Core::Id(Constants::IOS_SIMULATOR_TYPE),
IDevice::AutoDetected,
......@@ -113,6 +123,48 @@ IDevice::Ptr IosSimulator::clone() const
return IDevice::Ptr(new IosSimulator(*this));
}
QList<IosDeviceType> IosSimulator::availableDevices()
{
QMutexLocker l(&_mutex);
return _availableDevices;
}
void IosSimulator::setAvailableDevices(QList<IosDeviceType> value)
{
QMutexLocker l(&_mutex);
_availableDevices = value;
}
namespace {
void handleDeviceInfo(Ios::IosToolHandler *handler, const QString &deviceId,
const Ios::IosToolHandler::Dict &info)
{
Q_UNUSED(deviceId);
QList<IosDeviceType> res;
QMapIterator<QString, QString> i(info);
while (i.hasNext()) {
i.next();
IosDeviceType simulatorType(IosDeviceType::SimulatedDevice);
simulatorType.displayName = i.value();
simulatorType.identifier = i.key();
QStringList ids = i.key().split(QLatin1Char(','));
if (ids.length() > 1)
simulatorType.displayName += QLatin1String(", iOS ") + ids.last().trimmed();
res.append(simulatorType);
}
handler->deleteLater();
std::stable_sort(res.begin(), res.end());
IosSimulator::setAvailableDevices(res);
}
}
void IosSimulator::updateAvailableDevices()
{
IosToolHandler *toolHandler = new IosToolHandler(IosDeviceType(IosDeviceType::SimulatedDevice));
QObject::connect(toolHandler, &IosToolHandler::deviceInfo, &handleDeviceInfo);
toolHandler->requestDeviceInfo(QString());
}
void IosSimulator::fromMap(const QVariantMap &map)
{
IDevice::fromMap(map);
......@@ -162,5 +214,137 @@ IosSimulator::ConstPtr IosKitInformation::simulator(Kit *kit)
return res;
}
IosDeviceType::IosDeviceType(IosDeviceType::Type type, const QString &identifier, const QString &displayName) :
type(type), identifier(identifier), displayName(displayName)
{ }
bool IosDeviceType::fromMap(const QVariantMap &map)
{
bool validType;
displayName = map.value(iosDeviceTypeDisplayNameKey, QVariant()).toString();
type = IosDeviceType::Type(map.value(iosDeviceTypeTypeKey, QVariant()).toInt(&validType));
identifier = map.value(iosDeviceTypeIdentifierKey, QVariant()).toString();
return validType && !displayName.isEmpty()
&& (type != IosDeviceType::SimulatedDevice || !identifier.isEmpty());
}
QVariantMap IosDeviceType::toMap() const
{
QVariantMap res;
res[iosDeviceTypeDisplayNameKey] = displayName;
res[iosDeviceTypeTypeKey] = type;
res[iosDeviceTypeIdentifierKey] = identifier;
return res;
}
bool IosDeviceType::operator ==(const IosDeviceType &o) const
{
return o.type == type && o.identifier == identifier && o.displayName == displayName;
}
// compare strings comparing embedded numbers as numeric values.
// the result is negative if x<y, zero if x==y, positive if x>y
// Prefixed 0 are used to resolve ties, so that this ordering is still a total ordering (equality
// only for identical strings)
// "20" > "3" , "03-4" < "3-10", "3-5" < "03-5"
static int numberCompare(const QString &s1, const QString &s2)
{
int i1 = 0;
int i2 = 0;
int solveTie = 0;
while (i1 < s1.size() && i2 < s2.size()) {
QChar c1 = s1.at(i1);
QChar c2 = s2.at(i2);
if (c1.isDigit() && c2.isDigit()) {
// we found a number on both sides, find where the number ends
int j1 = i1 + 1;
int j2 = i2 + 1;
while (j1 < s1.size() && s1.at(j1).isDigit())
++j1;
while (j2 < s2.size() && s2.at(j2).isDigit())
++j2;
// and compare it from the right side, first units, then decimals,....
int cmp = 0;
int newI1 = j1;
int newI2 = j2;
while (j1 > i1 && j2 > i2) {
--j1;
--j2;
QChar cc1 = s1.at(j1);
QChar cc2 = s2.at(j2);
if (cc1 < cc2)
cmp = -1;
else if (cc1 > cc2)
cmp = 1;
}
int tie = 0;
// if the left number has more digits, if they are all 0, use this info only to break
// ties, otherwise the left number is larger
while (j1-- > i1) {
tie = 1;
if (s1.at(j1) != QLatin1Char('0'))
cmp = 1;
}
// same for the right number
while (j2-- > i2) {
tie = -1;
if (s2.at(j2) != QLatin1Char('0'))
cmp = -1;
}
// if not equal return
if (cmp != 0)
return cmp;
// otherwise possibly store info to break ties (first nomber with more leading zeros is
// larger)
if (solveTie == 0)
solveTie = tie;
// continue comparing after the numbers
i1 = newI1;
i2 = newI2;
} else {
// compare plain characters (non numbers)
if (c1 < c2)
return -1;
if (c1 > c2)
return 1;
++i1; ++i2;
}
}
// if one side has more characters it is the larger one
if (i1 < s1.size())
return 1;
if (i2 < s2.size())
return -1;
// if we had differences in prefixed 0, use that choose the largest string, otherwise they are
// equal
return solveTie;
}
bool IosDeviceType::operator <(const IosDeviceType &o) const
{
if (type < o.type)
return true;
if (type > o.type)
return false;
int cmp = numberCompare(displayName, o.displayName);
if (cmp < 0)
return true;
if (cmp > 0)
return false;
cmp = numberCompare(identifier, o.identifier);
if (cmp < 0)
return true;
return false;
}
QDebug operator <<(QDebug debug, const IosDeviceType &deviceType)
{
if (deviceType.type == IosDeviceType::IosDevice)
debug << "iOS Device " << deviceType.displayName << deviceType.identifier;
else
debug << deviceType.displayName << " (" << deviceType.identifier << ")";
return debug;
}
} // namespace Internal
} // namespace Ios
......@@ -33,6 +33,8 @@
#include <projectexplorer/devicesupport/idevice.h>
#include <utils/fileutils.h>
#include <QMutex>
#include <QDebug>
#include <QSharedPointer>
namespace ProjectExplorer { class Kit; }
......@@ -41,6 +43,28 @@ namespace Internal {
class IosConfigurations;
class IosSimulatorFactory;
class IosDeviceType {
public:
enum Type {
IosDevice,
SimulatedDevice
};
IosDeviceType(Type type = IosDevice, const QString &identifier = QString(),
const QString &displayName = QString());
bool fromMap(const QVariantMap &map);
QVariantMap toMap() const;
bool operator ==(const IosDeviceType &o) const;
bool operator !=(const IosDeviceType &o) const { return !(*this == o); }
bool operator <(const IosDeviceType &o) const;
Type type;
QString identifier;
QString displayName;
};
QDebug operator <<(QDebug debug, const IosDeviceType &deviceType);
class IosSimulator : public ProjectExplorer::IDevice
{
public:
......@@ -48,6 +72,10 @@ public:
typedef QSharedPointer<IosSimulator> Ptr;
ProjectExplorer::IDevice::DeviceInfo deviceInformation() const Q_DECL_OVERRIDE;
static QList<IosDeviceType> availableDevices();
static void setAvailableDevices(QList<IosDeviceType> value);
static void updateAvailableDevices();
QString displayType() const Q_DECL_OVERRIDE;
ProjectExplorer::IDeviceWidget *createWidget() Q_DECL_OVERRIDE;
QList<Core::Id> actionIds() const Q_DECL_OVERRIDE;
......@@ -60,7 +88,6 @@ public:
bool canAutoDetectPorts() const Q_DECL_OVERRIDE;
ProjectExplorer::IDevice::Ptr clone() const Q_DECL_OVERRIDE;
protected:
friend class IosSimulatorFactory;
friend class IosConfigurations;
......@@ -69,6 +96,8 @@ protected:
IosSimulator(const IosSimulator &other);
private:
mutable quint16 m_lastPort;
static QMutex _mutex;
static QList<IosDeviceType> _availableDevices;
};
namespace IosKitInformation {
......@@ -77,4 +106,6 @@ IosSimulator::ConstPtr simulator(ProjectExplorer::Kit *kit);
} // namespace Internal
} // namespace Ios
Q_DECLARE_METATYPE(Ios::Internal::IosDeviceType)
#endif // IOSSIMULATOR_H
......@@ -31,6 +31,7 @@
#include "iostoolhandler.h"
#include "iosconfigurations.h"
#include "iosconstants.h"
#include "iossimulator.h"
#include <coreplugin/icore.h>
#include <utils/qtcassert.h>
......@@ -126,7 +127,7 @@ public:
OpAppRun
};
explicit IosToolHandlerPrivate(IosDeviceType::Enum devType, IosToolHandler *q);
explicit IosToolHandlerPrivate(const IosDeviceType &devType, IosToolHandler *q);
virtual ~IosToolHandlerPrivate() {}
virtual void requestTransferApp(const QString &bundlePath, const QString &deviceId,
int timeout = 1000) = 0;
......@@ -170,7 +171,7 @@ protected:
IosToolHandler::RunKind runKind;
State state;
Op op;
IosDeviceType::Enum devType;
IosDeviceType devType;
static const int lookaheadSize = 67;
int iBegin, iEnd, gdbSocket;
QList<ParserState> stack;
......@@ -179,7 +180,7 @@ protected:
class IosDeviceToolHandlerPrivate : public IosToolHandlerPrivate
{
public:
explicit IosDeviceToolHandlerPrivate(IosDeviceType::Enum devType, IosToolHandler *q);