iosconfigurations.cpp 19.9 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
using namespace Utils;
Fawzi Mohamed's avatar
Fawzi Mohamed committed
62
using namespace Debugger;
63

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

namespace Ios {
namespace Internal {

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

void IosConfigurations::updateAutomaticKitList()
{
74 75 76 77 78 79 80 81 82
    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
83 84 85
    }

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

Fawzi Mohamed's avatar
Fawzi Mohamed committed
247 248 249
    const DebuggerItem *possibleDebugger = DebuggerItemManager::findByEngineType(Debugger::LldbEngineType);
    QVariant debuggerId = (possibleDebugger ? possibleDebugger->id() : QVariant());

hjk's avatar
hjk committed
250
    QList<Kit *> existingKits;
251
    QList<bool> kitMatched;
hjk's avatar
hjk committed
252 253 254 255 256
    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) {
257 258 259 260 261 262 263 264 265 266 267 268 269 270 271
            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
272
            GccToolChain *pToolchain = platformToolchainMap.value(p.name, 0);
273 274
            if (!pToolchain)
                continue;
275
            Core::Id pDeviceType;
276 277 278
            if (debugProbe)
                qDebug() << "guaranteeing kit for " << p.name ;
            if (p.name.startsWith(QLatin1String("iphoneos-"))) {
hjk's avatar
hjk committed
279
                pDeviceType = Constants::IOS_DEVICE_TYPE;
280
            } else if (p.name.startsWith(QLatin1String("iphonesimulator-"))) {
hjk's avatar
hjk committed
281
                pDeviceType = Constants::IOS_SIMULATOR_TYPE;
282 283 284 285 286 287 288
                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
289
                //pDeviceType = Constants::DESKTOP_DEVICE_TYPE;
290
            }
hjk's avatar
hjk committed
291
            Abi::Architecture arch = pToolchain->targetAbi().architecture();
292

hjk's avatar
hjk committed
293 294
            QList<BaseQtVersion *> qtVersions = qtVersionsForArch.value(arch);
            foreach (BaseQtVersion *qt, qtVersions) {
295 296 297 298 299 300
                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
301
                            && QtKitInformation::qtVersion(k) == qt)
302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321
                    {
                        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
322
                    foreach (const Kit *k, existingKits) {
323 324 325 326 327 328 329 330 331
                        if (k->displayName() == displayName) {
                            unique = false;
                            break;
                        }
                    }
                    if (unique) break;
                    displayName = baseDisplayName + QLatin1String("-") + QString::number(iVers);
                }
                newKit->setDisplayName(displayName);
332 333
                newKit->setIconPath(Utils::FileName::fromString(
                                        QLatin1String(Constants::IOS_SETTINGS_CATEGORY_ICON)));
334 335
                DeviceTypeKitInformation::setDeviceTypeId(newKit, pDeviceType);
                ToolChainKitInformation::setToolChain(newKit, pToolchain);
hjk's avatar
hjk committed
336
                QtKitInformation::setQtVersion(newKit, qt);
337
                //DeviceKitInformation::setDevice(newKit, device);
Fawzi Mohamed's avatar
Fawzi Mohamed committed
338 339 340
                if (!debuggerId.isValid())
                    Debugger::DebuggerKitInformation::setDebugger(newKit,
                                                                  debuggerId);
hjk's avatar
hjk committed
341

Fawzi Mohamed's avatar
Fawzi Mohamed committed
342 343 344 345 346 347
                newKit->setMutable(DeviceKitInformation::id(), true);
                newKit->setSticky(QtKitInformation::id(), true);
                newKit->setSticky(ToolChainKitInformation::id(), true);
                newKit->setSticky(DeviceTypeKitInformation::id(), true);
                newKit->setSticky(SysRootKitInformation::id(), true);

348
                SysRootKitInformation::setSysRoot(newKit, p.sdkPath);
349
                // QmakeProjectManager::QmakeKitInformation::setMkspec(newKit,
350 351 352 353 354 355 356
                //    Utils::FileName::fromString(QLatin1String("macx-ios-clang")));
                KitManager::registerKit(newKit);
                existingKits << newKit;
            }
        }
    }
    for (int i = 0; i < kitMatched.size(); ++i) {
Fawzi Mohamed's avatar
Fawzi Mohamed committed
357
        // deleting extra (old) kits
358 359 360 361
        if (!kitMatched.at(i) && !existingKits.at(i)->isValid()) {
            qDebug() << "deleting kit " << existingKits.at(i)->displayName();
            KitManager::deregisterKit(existingKits.at(i));
        }
Fawzi Mohamed's avatar
Fawzi Mohamed committed
362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379
        // fix old kits
        if (kitMatched.at(i)) {
            Kit *kit = existingKits.at(i);
            kit->blockNotification();
            const Debugger::DebuggerItem *debugger = Debugger::DebuggerKitInformation::debugger(kit);
            if ((!debugger || !debugger->isValid()) && debuggerId.isValid())
                Debugger::DebuggerKitInformation::setDebugger(kit, debuggerId);
            if (!kit->isMutable(DeviceKitInformation::id())) {
                kit->setMutable(DeviceKitInformation::id(), true);
                kit->setSticky(QtKitInformation::id(), true);
                kit->setSticky(ToolChainKitInformation::id(), true);
                kit->setSticky(DeviceTypeKitInformation::id(), true);
                kit->setSticky(SysRootKitInformation::id(), true);
            }
            if (kit->isSticky(Debugger::DebuggerKitInformation::id()))
                kit->setSticky(Debugger::DebuggerKitInformation::id(), false);
            kit->unblockNotification();
        }
380 381 382
    }
}

hjk's avatar
hjk committed
383 384 385
static IosConfigurations *m_instance = 0;

QObject *IosConfigurations::instance()
386
{
387 388 389
    return m_instance;
}

hjk's avatar
hjk committed
390 391 392 393 394 395 396 397
void IosConfigurations::initialize()
{
    QTC_CHECK(m_instance == 0);
    m_instance = new IosConfigurations(0);
    m_instance->updateSimulators();
    QTimer::singleShot(10000, IosDeviceManager::instance(), SLOT(monitorAvailableDevices()));
}

398 399
bool IosConfigurations::ignoreAllDevices()
{
hjk's avatar
hjk committed
400
    return m_instance->m_ignoreAllDevices;
401 402 403 404
}

void IosConfigurations::setIgnoreAllDevices(bool ignoreDevices)
{
hjk's avatar
hjk committed
405 406 407 408
    if (ignoreDevices != m_instance->m_ignoreAllDevices) {
        m_instance->m_ignoreAllDevices = ignoreDevices;
        m_instance->save();
        emit m_instance->updated();
409 410 411 412 413
    }
}

FileName IosConfigurations::developerPath()
{
hjk's avatar
hjk committed
414
    return m_instance->m_developerPath;
415 416 417 418
}

void IosConfigurations::save()
{
hjk's avatar
hjk committed
419
    QSettings *settings = Core::ICore::settings();
420
    settings->beginGroup(SettingsGroup);
421
    settings->setValue(ignoreAllDevicesKey, m_ignoreAllDevices);
422 423 424 425 426 427 428 429 430 431 432
    settings->endGroup();
}

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

void IosConfigurations::load()
{
hjk's avatar
hjk committed
433
    QSettings *settings = Core::ICore::settings();
434
    settings->beginGroup(SettingsGroup);
435
    m_ignoreAllDevices = settings->value(ignoreAllDevicesKey, false).toBool();
436 437 438
    settings->endGroup();
}

hjk's avatar
hjk committed
439 440
void IosConfigurations::updateSimulators()
{
441 442
    // currently we have just one simulator
    DeviceManager *devManager = DeviceManager::instance();
hjk's avatar
hjk committed
443
    Core::Id devId = Constants::IOS_SIMULATOR_DEVICE_ID;
444
    QMap<QString, Platform> platforms = IosProbe::detectPlatforms();
445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471
    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);
    }
}

472 473
void IosConfigurations::setDeveloperPath(const FileName &devPath)
{
hjk's avatar
hjk committed
474 475 476 477 478
    if (devPath != m_instance->m_developerPath) {
        m_instance->m_developerPath = devPath;
        m_instance->save();
        updateAutomaticKitList();
        emit m_instance->updated();
479 480 481
    }
}

482 483
} // namespace Internal
} // namespace Ios