iosrunconfiguration.cpp 15.7 KB
Newer Older
1
2
/****************************************************************************
**
3
4
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
5
6
7
8
9
10
11
**
** 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
12
13
14
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
15
**
16
17
18
19
20
21
22
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
23
24
25
26
**
****************************************************************************/

#include "iosrunconfiguration.h"
27
#include "iosconstants.h"
28
29
#include "iosmanager.h"
#include "iosdeploystep.h"
30
#include "simulatorcontrol.h"
31
32
33
34

#include <projectexplorer/kitinformation.h>
#include <projectexplorer/target.h>
#include <projectexplorer/deployconfiguration.h>
Fawzi Mohamed's avatar
Fawzi Mohamed committed
35
#include <projectexplorer/devicesupport/devicemanager.h>
36
37
#include <projectexplorer/buildstep.h>
#include <projectexplorer/buildsteplist.h>
38
#include <projectexplorer/runconfigurationaspects.h>
39
40
41
#include <qmakeprojectmanager/qmakebuildconfiguration.h>
#include <qmakeprojectmanager/qmakeproject.h>
#include <qmakeprojectmanager/qmakenodes.h>
42
43
44
#include <qtsupport/qtoutputformatter.h>
#include <qtsupport/qtkitinformation.h>

45
#include <utils/algorithm.h>
46
#include <utils/fileutils.h>
47
48
#include <utils/qtcprocess.h>
#include <utils/qtcassert.h>
49

50
51
52
53
54
55
56
#include <QAction>
#include <QApplication>
#include <QComboBox>
#include <QFormLayout>
#include <QHeaderView>
#include <QLabel>
#include <QLineEdit>
57
#include <QList>
Fawzi Mohamed's avatar
Fawzi Mohamed committed
58
#include <QStandardItemModel>
59
60
#include <QVariant>
#include <QWidget>
61
62

using namespace ProjectExplorer;
63
using namespace QmakeProjectManager;
64
using namespace Utils;
65
66
67
68

namespace Ios {
namespace Internal {

69
static const QLatin1String deviceTypeKey("Ios.device_type");
70

71
72
73
74
75
76
77
78
static IosDeviceType toIosDeviceType(const SimulatorInfo &device)
{
    IosDeviceType iosDeviceType(IosDeviceType::SimulatedDevice,
                                device.identifier,
                                QString("%1, %2").arg(device.name).arg(device.runtimeName));
    return iosDeviceType;
}

79
80
81
82
class IosRunConfigurationWidget : public RunConfigWidget
{
public:
    IosRunConfigurationWidget(IosRunConfiguration *runConfiguration);
83
    QString displayName() const override;
84

85
private:
86
87
    void argumentsLineEditTextEdited();
    void updateValues();
88
    void setDeviceTypeIndex(int devIndex);
89

90
    IosRunConfiguration *m_runConfiguration;
Fawzi Mohamed's avatar
Fawzi Mohamed committed
91
    QStandardItemModel m_deviceTypeModel;
92
93
94
    QLabel *m_deviceTypeLabel;
    QLineEdit *m_executableLineEdit;
    QComboBox *m_deviceTypeComboBox;
95
96
};

97
IosRunConfiguration::IosRunConfiguration(Target *parent, Core::Id id, const FileName &path)
98
99
100
    : RunConfiguration(parent, id)
    , m_profilePath(path)
{
101
    addExtraAspect(new ArgumentsAspect(this, QLatin1String("Ios.run_arguments")));
102
103
104
105
106
107
108
109
110
111
112
113
    init();
}

IosRunConfiguration::IosRunConfiguration(Target *parent, IosRunConfiguration *source)
    : RunConfiguration(parent, source)
    , m_profilePath(source->m_profilePath)
{
    init();
}

void IosRunConfiguration::init()
{
114
    updateDisplayNames();
115
116
117
118
    connect(DeviceManager::instance(), &DeviceManager::updated,
            this, &IosRunConfiguration::deviceChanges);
    connect(KitManager::instance(), &KitManager::kitsChanged,
            this, &IosRunConfiguration::deviceChanges);
Fawzi Mohamed's avatar
Fawzi Mohamed committed
119
120
121
}

void IosRunConfiguration::deviceChanges() {
122
    updateDisplayNames();
123
    updateEnabledState();
124
125
126
127
128
129
130
}

QWidget *IosRunConfiguration::createConfigurationWidget()
{
    return new IosRunConfigurationWidget(this);
}

131
OutputFormatter *IosRunConfiguration::createOutputFormatter() const
132
133
134
135
{
    return new QtSupport::QtOutputFormatter(target()->project());
}

136
QString IosRunConfiguration::commandLineArguments() const
137
{
138
    return extraAspect<ArgumentsAspect>()->arguments();
139
140
}

141
void IosRunConfiguration::updateDisplayNames()
142
{
143
    if (DeviceTypeKitInformation::deviceTypeId(target()->kit()) == Constants::IOS_DEVICE_TYPE)
144
        m_deviceType = IosDeviceType(IosDeviceType::IosDevice);
Fawzi Mohamed's avatar
Fawzi Mohamed committed
145
146
    else if (m_deviceType.type == IosDeviceType::IosDevice)
        m_deviceType = IosDeviceType(IosDeviceType::SimulatedDevice);
Orgad Shaneh's avatar
Orgad Shaneh committed
147
    IDevice::ConstPtr dev = DeviceKitInformation::device(target()->kit());
148
    const QString devName = dev.isNull() ? IosDevice::name() : dev->displayName();
149
    setDefaultDisplayName(tr("Run on %1").arg(devName));
150
    setDisplayName(tr("Run %1 on %2").arg(applicationName()).arg(devName));
151
152
}

153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
void IosRunConfiguration::updateEnabledState()
{
    Core::Id devType = DeviceTypeKitInformation::deviceTypeId(target()->kit());
    if (devType != Constants::IOS_DEVICE_TYPE && devType != Constants::IOS_SIMULATOR_TYPE) {
        setEnabled(false);
        return;
    }
    IDevice::ConstPtr dev = DeviceKitInformation::device(target()->kit());
    if (dev.isNull() || dev->deviceState() != IDevice::DeviceReadyToUse) {
        setEnabled(false);
        return;
    }
    return RunConfiguration::updateEnabledState();
}

168
169
170
IosDeployStep *IosRunConfiguration::deployStep() const
{
    DeployConfiguration *config = target()->activeDeployConfiguration();
171
    return config ? config->stepList()->firstOfType<IosDeployStep>() : nullptr;
172
173
}

174
FileName IosRunConfiguration::profilePath() const
175
176
177
178
{
    return m_profilePath;
}

179
180
181
182
183
184
185
186
187
static QmakeProFile *proFile(const IosRunConfiguration *rc)
{
    QmakeProject *pro = qobject_cast<QmakeProject *>(rc->target()->project());
    QmakeProFile *proFile = pro ? pro->rootProFile() : nullptr;
    if (proFile)
        proFile = proFile->findProFile(rc->profilePath());
    return proFile;
}

188
QString IosRunConfiguration::applicationName() const
189
{
190
191
192
    QmakeProFile *pro = proFile(this);
    if (pro) {
        TargetInformation ti = pro->targetInformation();
193
194
        if (ti.valid)
            return ti.target;
195
196
197
198
    }
    return QString();
}

199
FileName IosRunConfiguration::bundleDirectory() const
200
{
201
    FileName res;
Orgad Shaneh's avatar
Orgad Shaneh committed
202
    Core::Id devType = DeviceTypeKitInformation::deviceTypeId(target()->kit());
203
204
    bool isDevice = (devType == Constants::IOS_DEVICE_TYPE);
    if (!isDevice && devType != Constants::IOS_SIMULATOR_TYPE) {
205
        qCWarning(iosLog) << "unexpected device type in bundleDirForTarget: " << devType.toString();
206
207
        return res;
    }
208
209
    QmakeBuildConfiguration *bc =
            qobject_cast<QmakeBuildConfiguration *>(target()->activeBuildConfiguration());
210
    if (bc) {
211
212
213
        const QmakeProFile *pro = proFile(this);
        if (pro) {
            TargetInformation ti = pro->targetInformation();
214
            if (ti.valid)
215
                res = ti.buildDir;
216
217
218
        }
        if (res.isEmpty())
            res = bc->buildDirectory();
219
220
221
222
223
224
225
226
        switch (bc->buildType()) {
        case BuildConfiguration::Debug :
        case BuildConfiguration::Unknown :
            if (isDevice)
                res.appendPath(QLatin1String("Debug-iphoneos"));
            else
                res.appendPath(QLatin1String("Debug-iphonesimulator"));
            break;
Ulf Hermann's avatar
Ulf Hermann committed
227
        case BuildConfiguration::Profile :
228
229
230
231
        case BuildConfiguration::Release :
            if (isDevice)
                res.appendPath(QLatin1String("Release-iphoneos"));
            else
232
                res.appendPath(QLatin1String("Release-iphonesimulator"));
233
234
            break;
        default:
235
            qCWarning(iosLog) << "IosBuildStep had an unknown buildType "
236
237
238
                     << target()->activeBuildConfiguration()->buildType();
        }
    }
239
    res.appendPath(applicationName() + QLatin1String(".app"));
240
241
242
    return res;
}

243
FileName IosRunConfiguration::localExecutable() const
244
{
245
    return bundleDirectory().appendPath(applicationName());
246
247
}

248
249
bool IosRunConfiguration::fromMap(const QVariantMap &map)
{
Fawzi Mohamed's avatar
Fawzi Mohamed committed
250
251
252
253
254
255
256
257
    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);
    }
258
259
260
261
262
263
    return RunConfiguration::fromMap(map);
}

QVariantMap IosRunConfiguration::toMap() const
{
    QVariantMap res = RunConfiguration::toMap();
Fawzi Mohamed's avatar
Fawzi Mohamed committed
264
    res[deviceTypeKey] = deviceType().toMap();
265
266
267
    return res;
}

268
269
270
271
272
QString IosRunConfiguration::buildSystemTarget() const
{
    return static_cast<QmakeProject *>(target()->project())->mapProFilePathToTarget(m_profilePath);
}

Fawzi Mohamed's avatar
Fawzi Mohamed committed
273
274
QString IosRunConfiguration::disabledReason() const
{
Orgad Shaneh's avatar
Orgad Shaneh committed
275
    Core::Id devType = DeviceTypeKitInformation::deviceTypeId(target()->kit());
Fawzi Mohamed's avatar
Fawzi Mohamed committed
276
277
    if (devType != Constants::IOS_DEVICE_TYPE && devType != Constants::IOS_SIMULATOR_TYPE)
        return tr("Kit has incorrect device type for running on iOS devices.");
Orgad Shaneh's avatar
Orgad Shaneh committed
278
    IDevice::ConstPtr dev = DeviceKitInformation::device(target()->kit());
Fawzi Mohamed's avatar
Fawzi Mohamed committed
279
280
281
282
283
284
285
286
    QString validDevName;
    bool hasConncetedDev = false;
    if (devType == Constants::IOS_DEVICE_TYPE) {
        DeviceManager *dm = DeviceManager::instance();
        for (int idev = 0; idev < dm->deviceCount(); ++idev) {
            IDevice::ConstPtr availDev = dm->deviceAt(idev);
            if (!availDev.isNull() && availDev->type() == Constants::IOS_DEVICE_TYPE) {
                if (availDev->deviceState() == IDevice::DeviceReadyToUse) {
287
                    validDevName += QLatin1Char(' ');
Fawzi Mohamed's avatar
Fawzi Mohamed committed
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
                    validDevName += availDev->displayName();
                } else if (availDev->deviceState() == IDevice::DeviceConnected) {
                    hasConncetedDev = true;
                }
            }
        }
    }

    if (dev.isNull()) {
        if (!validDevName.isEmpty())
            return tr("No device chosen. Select %1.").arg(validDevName); // should not happen
        else if (hasConncetedDev)
            return tr("No device chosen. Enable developer mode on a device."); // should not happen
        else
            return tr("No device available.");
    } else {
        switch (dev->deviceState()) {
        case IDevice::DeviceReadyToUse:
            break;
        case IDevice::DeviceConnected:
            return tr("To use this device you need to enable developer mode on it.");
        case IDevice::DeviceDisconnected:
        case IDevice::DeviceStateUnknown:
            if (!validDevName.isEmpty())
                return tr("%1 is not connected. Select %2?")
                        .arg(dev->displayName(), validDevName);
            else if (hasConncetedDev)
                return tr("%1 is not connected. Enable developer mode on a device?")
                        .arg(dev->displayName());
            else
                return tr("%1 is not connected.").arg(dev->displayName());
        }
    }
    return RunConfiguration::disabledReason();
}

Fawzi Mohamed's avatar
Fawzi Mohamed committed
324
IosDeviceType IosRunConfiguration::deviceType() const
325
{
326
327
328
329
330
331
332
    if (m_deviceType.type == IosDeviceType::SimulatedDevice) {
        QList<SimulatorInfo> availableSimulators = SimulatorControl::availableSimulators();
        if (availableSimulators.isEmpty())
            return m_deviceType;
        if (Utils::contains(availableSimulators,
                            Utils::equal(&SimulatorInfo::identifier, m_deviceType.identifier))) {
                 return m_deviceType;
Fawzi Mohamed's avatar
Fawzi Mohamed committed
333
        }
334
335
336
337
338
339
340
341
342
343
        const QStringList parts = m_deviceType.displayName.split(QLatin1Char(','));
        if (parts.count() < 2)
            return toIosDeviceType(availableSimulators.last());

        QList<SimulatorInfo> eligibleDevices;
        eligibleDevices = Utils::filtered(availableSimulators, [parts](const SimulatorInfo &info) {
            return info.name == parts.at(0) && info.runtimeName == parts.at(1);
        });
        return toIosDeviceType(eligibleDevices.isEmpty() ? availableSimulators.last()
                                                         : eligibleDevices.last());
Fawzi Mohamed's avatar
Fawzi Mohamed committed
344
    }
345
346
347
    return m_deviceType;
}

Fawzi Mohamed's avatar
Fawzi Mohamed committed
348
void IosRunConfiguration::setDeviceType(const IosDeviceType &deviceType)
349
350
351
352
{
    m_deviceType = deviceType;
}

353
354
IosRunConfigurationWidget::IosRunConfigurationWidget(IosRunConfiguration *runConfiguration)
    : m_runConfiguration(runConfiguration)
355
{
356
357
358
359
360
361
362
363
364
365
366
367
368
369
    QSizePolicy sizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
    sizePolicy.setHorizontalStretch(0);
    sizePolicy.setVerticalStretch(0);
    setSizePolicy(sizePolicy);

    m_executableLineEdit = new QLineEdit(this);
    m_executableLineEdit->setReadOnly(true);

    m_deviceTypeComboBox = new QComboBox(this);
    m_deviceTypeComboBox->setModel(&m_deviceTypeModel);

    m_deviceTypeLabel = new QLabel(IosRunConfiguration::tr("Device type:"), this);

    auto layout = new QFormLayout(this);
370
    runConfiguration->extraAspect<ArgumentsAspect>()->addToMainConfigurationWidget(this, layout);
371
372
    layout->addRow(IosRunConfiguration::tr("Executable:"), m_executableLineEdit);
    layout->addRow(m_deviceTypeLabel, m_deviceTypeComboBox);
373
374
375

    updateValues();

376
377
378
379
    connect(m_deviceTypeComboBox, static_cast<void(QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
            this, &IosRunConfigurationWidget::setDeviceTypeIndex);
    connect(runConfiguration, &IosRunConfiguration::localExecutableChanged,
            this, &IosRunConfigurationWidget::updateValues);
380
381
382
383
}

QString IosRunConfigurationWidget::displayName() const
{
384
    return IosRunConfiguration::tr("iOS run settings");
385
386
}

387
388
void IosRunConfigurationWidget::setDeviceTypeIndex(int devIndex)
{
Fawzi Mohamed's avatar
Fawzi Mohamed committed
389
390
    QVariant selectedDev = m_deviceTypeModel.data(m_deviceTypeModel.index(devIndex, 0), Qt::UserRole + 1);
    if (selectedDev.isValid())
391
        m_runConfiguration->setDeviceType(toIosDeviceType(selectedDev.value<SimulatorInfo>()));
392
393
394
}


395
396
void IosRunConfigurationWidget::updateValues()
{
Fawzi Mohamed's avatar
Fawzi Mohamed committed
397
    bool showDeviceSelector = m_runConfiguration->deviceType().type != IosDeviceType::IosDevice;
398
399
    m_deviceTypeLabel->setVisible(showDeviceSelector);
    m_deviceTypeComboBox->setVisible(showDeviceSelector);
Fawzi Mohamed's avatar
Fawzi Mohamed committed
400
    if (showDeviceSelector && m_deviceTypeModel.rowCount() == 0) {
401
402
403
        foreach (const SimulatorInfo &device, SimulatorControl::availableSimulators()) {
            QStandardItem *item = new QStandardItem(QString("%1, %2").arg(device.name)
                                                    .arg(device.runtimeName));
Fawzi Mohamed's avatar
Fawzi Mohamed committed
404
            QVariant v;
405
            v.setValue(device);
Fawzi Mohamed's avatar
Fawzi Mohamed committed
406
407
408
409
            item->setData(v);
            m_deviceTypeModel.appendRow(item);
        }
    }
410

Fawzi Mohamed's avatar
Fawzi Mohamed committed
411
    IosDeviceType currentDType = m_runConfiguration->deviceType();
412
    QVariant currentData = m_deviceTypeComboBox->currentData();
413
    if (currentDType.type == IosDeviceType::SimulatedDevice && !currentDType.identifier.isEmpty()
414
415
            && (!currentData.isValid()
                || currentDType != toIosDeviceType(currentData.value<SimulatorInfo>())))
416
    {
Fawzi Mohamed's avatar
Fawzi Mohamed committed
417
418
419
        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);
420
421
            SimulatorInfo dType = vData.value<SimulatorInfo>();
            if (dType.identifier == currentDType.identifier) {
422
                m_deviceTypeComboBox->setCurrentIndex(i);
Fawzi Mohamed's avatar
Fawzi Mohamed committed
423
424
425
426
427
428
429
430
                didSet = true;
                break;
            }
        }
        if (!didSet) {
            qCWarning(iosLog) << "could not set " << currentDType << " as it is not in model";
        }
    }
431
    m_executableLineEdit->setText(m_runConfiguration->localExecutable().toUserOutput());
432
433
434
435
}

} // namespace Internal
} // namespace Ios