iosconfigurations.cpp 18.1 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
/****************************************************************************
**
** 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/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>
45
#include <debugger/debuggeritemmanager.h>
46
47
48
49
50
51
52
53
#include <debugger/debuggerkitinformation.h>
#include <qtsupport/baseqtversion.h>
#include <qtsupport/qtkitinformation.h>
#include <qtsupport/qtversionmanager.h>
#include <qtsupport/qtversionfactory.h>

#include <QFileInfo>
#include <QList>
hjk's avatar
hjk committed
54
55
56
57
#include <QMap>
#include <QSettings>
#include <QStringList>
#include <QTimer>
58
59

using namespace ProjectExplorer;
hjk's avatar
hjk committed
60
using namespace QtSupport;
61
62
using namespace Utils;

hjk's avatar
hjk committed
63
const bool debugProbe = false;
64
65
66
67

namespace Ios {
namespace Internal {

hjk's avatar
hjk committed
68
69
const QLatin1String SettingsGroup("IosConfigurations");
const QLatin1String ignoreAllDevicesKey("IgnoreAllDevices");
70
71
72

void IosConfigurations::updateAutomaticKitList()
{
73
74
75
76
77
78
79
80
81
    QMap<QString, Platform> platforms = IosProbe::detectPlatforms();
    {
        QMapIterator<QString, Platform> iter(platforms);
        while (iter.hasNext()) {
            iter.next();
            const Platform &p = iter.value();
            setDeveloperPath(p.developerPath);
            break;
        }
hjk's avatar
hjk committed
82
83
84
    }

    QMap<QString, GccToolChain *> platformToolchainMap;
85
    // check existing toolchains (and remove old ones)
hjk's avatar
hjk committed
86
    foreach (ToolChain *tc, ToolChainManager::toolChains()) {
87
88
89
90
        if (!tc->isAutoDetected()) // use also user toolchains?
            continue;
        if (tc->type() != QLatin1String("clang") && tc->type() != QLatin1String("gcc"))
            continue;
hjk's avatar
hjk committed
91
        GccToolChain *toolchain = static_cast<GccToolChain *>(tc);
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
        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();
hjk's avatar
hjk committed
118
            ToolChainManager::deregisterToolChain(tc);
119
120
121
122
123
124
125
126
127
128
129
130
131
        }
    }
    // 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;
hjk's avatar
hjk committed
132
            GccToolChain *toolchain;
133
            if (p.compilerPath.toFileInfo().baseName().startsWith(QLatin1String("clang")))
hjk's avatar
hjk committed
134
                toolchain = new ClangToolChain(ToolChain::AutoDetection);
135
            else
hjk's avatar
hjk committed
136
                toolchain = new GccToolChain(
137
                            QLatin1String(ProjectExplorer::Constants::GCC_TOOLCHAIN_ID),
hjk's avatar
hjk committed
138
                            ToolChain::AutoDetection);
139
140
141
142
            QString baseDisplayName = p.name;
            QString displayName = baseDisplayName;
            for (int iVers = 1; iVers < 100; ++iVers) {
                bool unique = true;
hjk's avatar
hjk committed
143
                foreach (ToolChain *existingTC, ToolChainManager::toolChains()) {
144
145
146
147
148
149
150
151
152
153
154
155
                    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);
hjk's avatar
hjk committed
156
            ToolChainManager::registerToolChain(toolchain);
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
            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);
        }
    }
hjk's avatar
hjk committed
188
189
    QMap<Abi::Architecture, QList<BaseQtVersion *> > qtVersionsForArch;
    foreach (BaseQtVersion *qtVersion, QtVersionManager::versions()) {
190
191
192
193
194
195
        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
hjk's avatar
hjk committed
196
197
                BaseQtVersion *iosVersion =
                        QtVersionFactory::createQtVersionFromQMakePath(
198
199
200
201
202
203
                            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();
hjk's avatar
hjk committed
204
205
                    QtVersionManager::removeVersion(qtVersion);
                    QtVersionManager::addVersion(iosVersion);
206
207
208
209
210
211
212
213
214
215
                    qtVersion = iosVersion;
                } else {
                    continue;
                }
            } else {
                continue;
            }
        }
        if (!qtVersion->isValid())
            continue;
hjk's avatar
hjk committed
216
        QList<Abi> qtAbis = qtVersion->qtAbis();
217
218
219
220
        if (qtAbis.empty())
            continue;
        if (debugProbe)
            qDebug() << "qt arch " << qtAbis.first().architecture();
hjk's avatar
hjk committed
221
        foreach (const Abi &abi, qtAbis)
222
223
224
            qtVersionsForArch[abi.architecture()].append(qtVersion);
    }

hjk's avatar
hjk committed
225
    QList<Kit *> existingKits;
226
    QList<bool> kitMatched;
hjk's avatar
hjk committed
227
228
229
230
231
    foreach (Kit *k, KitManager::kits()) {
        Core::Id deviceKind = DeviceTypeKitInformation::deviceTypeId(k);
        if (deviceKind != Constants::IOS_DEVICE_TYPE
                && deviceKind != Constants::IOS_SIMULATOR_TYPE
                && deviceKind != ProjectExplorer::Constants::DESKTOP_DEVICE_TYPE) {
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
            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();
hjk's avatar
hjk committed
247
            GccToolChain *pToolchain = platformToolchainMap.value(p.name, 0);
248
249
            if (!pToolchain)
                continue;
250
            Core::Id pDeviceType;
251
252
253
            if (debugProbe)
                qDebug() << "guaranteeing kit for " << p.name ;
            if (p.name.startsWith(QLatin1String("iphoneos-"))) {
hjk's avatar
hjk committed
254
                pDeviceType = Constants::IOS_DEVICE_TYPE;
255
            } else if (p.name.startsWith(QLatin1String("iphonesimulator-"))) {
hjk's avatar
hjk committed
256
                pDeviceType = Constants::IOS_SIMULATOR_TYPE;
257
258
259
260
261
262
263
                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;
hjk's avatar
hjk committed
264
                //pDeviceType = Constants::DESKTOP_DEVICE_TYPE;
265
            }
hjk's avatar
hjk committed
266
            Abi::Architecture arch = pToolchain->targetAbi().architecture();
267

hjk's avatar
hjk committed
268
269
            QList<BaseQtVersion *> qtVersions = qtVersionsForArch.value(arch);
            foreach (BaseQtVersion *qt, qtVersions) {
270
271
272
273
274
275
                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
hjk's avatar
hjk committed
276
                            && QtKitInformation::qtVersion(k) == qt)
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
                    {
                        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;
hjk's avatar
hjk committed
297
                    foreach (const Kit *k, existingKits) {
298
299
300
301
302
303
304
305
306
                        if (k->displayName() == displayName) {
                            unique = false;
                            break;
                        }
                    }
                    if (unique) break;
                    displayName = baseDisplayName + QLatin1String("-") + QString::number(iVers);
                }
                newKit->setDisplayName(displayName);
307
308
                newKit->setIconPath(Utils::FileName::fromString(
                                        QLatin1String(Constants::IOS_SETTINGS_CATEGORY_ICON)));
309
310
                DeviceTypeKitInformation::setDeviceTypeId(newKit, pDeviceType);
                ToolChainKitInformation::setToolChain(newKit, pToolchain);
hjk's avatar
hjk committed
311
                QtKitInformation::setQtVersion(newKit, qt);
312
                //DeviceKitInformation::setDevice(newKit, device);
hjk's avatar
hjk committed
313
314

                Debugger::DebuggerItem debugger;
315
                debugger.setCommand(lldbPath());
hjk's avatar
hjk committed
316
317
318
319
                debugger.setEngineType(Debugger::LldbEngineType);
                debugger.setDisplayName(tr("IOS Debugger"));
                debugger.setAutoDetected(true);
                debugger.setAbi(pToolchain->targetAbi());
320
321
                QVariant id = Debugger::DebuggerItemManager::registerDebugger(debugger);
                Debugger::DebuggerKitInformation::setDebugger(newKit, id);
hjk's avatar
hjk committed
322

323
                SysRootKitInformation::setSysRoot(newKit, p.sdkPath);
324
                // QmakeProjectManager::QmakeKitInformation::setMkspec(newKit,
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
                //    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));
        }
    }
}

hjk's avatar
hjk committed
340
341
342
static IosConfigurations *m_instance = 0;

QObject *IosConfigurations::instance()
343
{
344
345
346
    return m_instance;
}

hjk's avatar
hjk committed
347
348
349
350
351
352
353
354
void IosConfigurations::initialize()
{
    QTC_CHECK(m_instance == 0);
    m_instance = new IosConfigurations(0);
    m_instance->updateSimulators();
    QTimer::singleShot(10000, IosDeviceManager::instance(), SLOT(monitorAvailableDevices()));
}

355
356
bool IosConfigurations::ignoreAllDevices()
{
hjk's avatar
hjk committed
357
    return m_instance->m_ignoreAllDevices;
358
359
360
361
}

void IosConfigurations::setIgnoreAllDevices(bool ignoreDevices)
{
hjk's avatar
hjk committed
362
363
364
365
    if (ignoreDevices != m_instance->m_ignoreAllDevices) {
        m_instance->m_ignoreAllDevices = ignoreDevices;
        m_instance->save();
        emit m_instance->updated();
366
367
368
369
370
    }
}

FileName IosConfigurations::developerPath()
{
hjk's avatar
hjk committed
371
    return m_instance->m_developerPath;
372
373
}

Fawzi Mohamed's avatar
Fawzi Mohamed committed
374
375
376
377
378
FileName IosConfigurations::lldbPath()
{
    return m_instance->m_lldbPath;
}

379
380
void IosConfigurations::save()
{
hjk's avatar
hjk committed
381
    QSettings *settings = Core::ICore::settings();
382
    settings->beginGroup(SettingsGroup);
383
    settings->setValue(ignoreAllDevicesKey, m_ignoreAllDevices);
384
385
386
387
388
389
390
391
392
393
394
    settings->endGroup();
}

IosConfigurations::IosConfigurations(QObject *parent)
    : QObject(parent)
{
    load();
}

void IosConfigurations::load()
{
hjk's avatar
hjk committed
395
    QSettings *settings = Core::ICore::settings();
396
    settings->beginGroup(SettingsGroup);
397
    m_ignoreAllDevices = settings->value(ignoreAllDevicesKey, false).toBool();
398
399
400
    settings->endGroup();
}

hjk's avatar
hjk committed
401
402
void IosConfigurations::updateSimulators()
{
403
404
    // currently we have just one simulator
    DeviceManager *devManager = DeviceManager::instance();
hjk's avatar
hjk committed
405
    Core::Id devId = Constants::IOS_SIMULATOR_DEVICE_ID;
406
    QMap<QString, Platform> platforms = IosProbe::detectPlatforms();
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
    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);
    }
}

434
435
void IosConfigurations::setDeveloperPath(const FileName &devPath)
{
hjk's avatar
hjk committed
436
437
438
439
    if (devPath != m_instance->m_developerPath) {
        m_instance->m_developerPath = devPath;
        m_instance->save();
        updateAutomaticKitList();
Fawzi Mohamed's avatar
Fawzi Mohamed committed
440
441
442
443
444
445
446
447
448
449
450
451
        QProcess lldbInfo;
        lldbInfo.start(QLatin1String("xcrun"), QStringList() << QLatin1String("--find")
                       << QLatin1String("lldb"));
        if (!lldbInfo.waitForFinished(2000)) {
            lldbInfo.kill();
        } else {
            QByteArray lPath=lldbInfo.readAll();
            lPath.chop(1);
            Utils::FileName lldbPath = Utils::FileName::fromString(QString::fromLocal8Bit(lPath.data(), lPath.size()));
            if (lldbPath.toFileInfo().exists())
                m_instance->m_lldbPath = lldbPath;
        }
hjk's avatar
hjk committed
452
        emit m_instance->updated();
453
454
455
    }
}

456
457
} // namespace Internal
} // namespace Ios