qtversionmanager.cpp 32.5 KB
Newer Older
1
/**************************************************************************
con's avatar
con committed
2 3 4
**
** This file is part of Qt Creator
**
con's avatar
con committed
5
** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
con's avatar
con committed
6
**
hjk's avatar
hjk committed
7
** Contact: Nokia Corporation (info@qt.nokia.com)
con's avatar
con committed
8
**
9
**
10
** GNU Lesser General Public License Usage
11
**
hjk's avatar
hjk committed
12 13 14 15 16 17
** 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.
18
**
con's avatar
con committed
19
** In addition, as a special exception, Nokia gives you certain additional
hjk's avatar
hjk committed
20
** rights. These rights are described in the Nokia Qt LGPL Exception
con's avatar
con committed
21 22
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
hjk's avatar
hjk committed
23 24 25 26 27
** Other Usage
**
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
con's avatar
con committed
28
** If you have questions regarding the use of this file, please contact
Tobias Hunger's avatar
Tobias Hunger committed
29
** Nokia at info@qt.nokia.com.
con's avatar
con committed
30
**
31
**************************************************************************/
hjk's avatar
hjk committed
32

con's avatar
con committed
33
#include "qtversionmanager.h"
hjk's avatar
hjk committed
34

35
#include "qt4projectmanagerconstants.h"
dt's avatar
dt committed
36 37 38 39 40
#include "qtversionfactory.h"

// only for legay restore
#include "qt-desktop/desktopqtversion.h"
#include "qt-s60/symbianqtversion.h"
41

42
#include <projectexplorer/debugginghelper.h>
dt's avatar
dt committed
43 44
#include <projectexplorer/persistentsettings.h>
// only for legay restore
45
#include <projectexplorer/projectexplorerconstants.h>
46 47
#include <projectexplorer/toolchainmanager.h>
#include <projectexplorer/gcctoolchain.h>
48

49
#include <coreplugin/icore.h>
kh1's avatar
kh1 committed
50
#include <coreplugin/helpmanager.h>
51

52
#include <extensionsystem/pluginmanager.h>
53

54
#include <utils/qtcprocess.h>
dt's avatar
dt committed
55
#include <utils/qtcassert.h>
56 57 58
#ifdef Q_OS_WIN
#    include <utils/winutils.h>
#endif
con's avatar
con committed
59

60
#include <QtCore/QFile>
con's avatar
con committed
61
#include <QtCore/QSettings>
62
#include <QtCore/QTextStream>
63
#include <QtCore/QDir>
dt's avatar
dt committed
64
#include <QtGui/QMainWindow>
con's avatar
con committed
65

66 67
#include <algorithm>

68 69 70 71
using namespace Qt4ProjectManager;
using namespace Qt4ProjectManager::Internal;

using ProjectExplorer::DebuggingHelperLibrary;
con's avatar
con committed
72

dt's avatar
dt committed
73 74 75
static const char QTVERSION_DATA_KEY[] = "QtVersion.";
static const char QTVERSION_TYPE_KEY[] = "QtVersion.Type";
static const char QTVERSION_COUNT_KEY[] = "QtVersion.Count";
dt's avatar
dt committed
76 77 78 79
static const char OLDQTVERSION_COUNT_KEY[] = "QtVersion.Old.Count";
static const char OLDQTVERSION_DATA_KEY[] = "QtVersion.Old.";
static const char OLDQTVERSION_SDKSOURCE[] = "QtVersion.Old.SdkSource";
static const char OLDQTVERSION_PATH[] = "QtVersion.Old.Path";
dt's avatar
dt committed
80 81 82 83
static const char QTVERSION_FILE_VERSION_KEY[] = "Version";
static const char QTVERSION_FILENAME[] = "/qtversion.xml";

// legacy settings
84
static const char QtVersionsSectionName[] = "QtVersions";
con's avatar
con committed
85

86 87
enum { debug = 0 };

88 89 90 91 92 93 94 95 96 97 98 99 100 101
template<class T>
static T *createToolChain(const QString &id)
{
    QList<ProjectExplorer::ToolChainFactory *> factories =
            ExtensionSystem::PluginManager::instance()->getObjects<ProjectExplorer::ToolChainFactory>();
    foreach (ProjectExplorer::ToolChainFactory *f, factories) {
       if (f->id() == id) {
           Q_ASSERT(f->canCreate());
           return static_cast<T *>(f->create());
       }
    }
    return 0;
}

dt's avatar
dt committed
102 103 104 105 106 107 108
static QString settingsFileName()
{
    ExtensionSystem::PluginManager *pm = ExtensionSystem::PluginManager::instance();
    QFileInfo settingsLocation(pm->settings()->fileName());
    return settingsLocation.absolutePath() + QLatin1String(QTVERSION_FILENAME);
}

109 110

// prefer newer qts otherwise compare on id
dt's avatar
dt committed
111
bool qtVersionNumberCompare(BaseQtVersion *a, BaseQtVersion *b)
112 113 114 115
{
    return a->qtVersion() > b->qtVersion() || (a->qtVersion() == b->qtVersion() && a->uniqueId() < b->uniqueId());
}

116 117 118
// --------------------------------------------------------------------------
// QtVersionManager
// --------------------------------------------------------------------------
119
QtVersionManager *QtVersionManager::m_self = 0;
120

con's avatar
con committed
121 122
QtVersionManager::QtVersionManager()
{
123
    m_self = this;
con's avatar
con committed
124
    m_idcount = 1;
dt's avatar
dt committed
125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 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 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208
}

void QtVersionManager::extensionsInitialized()
{
    bool success = restoreQtVersions();
    if (!success)
        success = legacyRestore();
    updateFromInstaller();
    if (!success) {
        // We did neither restore our settings or upgraded
        // in that case figure out if there's a qt in path
        // and add it to the qt versions
        findSystemQt();
    }
    updateDocumentation();

    updateSettings();
    saveQtVersions();
}

QtVersionManager::~QtVersionManager()
{
    qDeleteAll(m_versions);
    m_versions.clear();
}

QtVersionManager *QtVersionManager::instance()
{
    return m_self;
}

bool QtVersionManager::restoreQtVersions()
{
    ExtensionSystem::PluginManager *pm = ExtensionSystem::PluginManager::instance();
    QList<QtVersionFactory *> factories = pm->getObjects<QtVersionFactory>();

    ProjectExplorer::PersistentSettingsReader reader;
    if (!reader.load(settingsFileName()))
        return false;
    QVariantMap data = reader.restoreValues();

    // Check version:
    int version = data.value(QLatin1String(QTVERSION_FILE_VERSION_KEY), 0).toInt();
    if (version < 1)
        return false;


    int count = data.value(QLatin1String(QTVERSION_COUNT_KEY), 0).toInt();
    for (int i = 0; i < count; ++i) {
        const QString key = QString::fromLatin1(QTVERSION_DATA_KEY) + QString::number(i);
        if (!data.contains(key))
            break;

        const QVariantMap qtversionMap = data.value(key).toMap();
        const QString type = qtversionMap.value(QTVERSION_TYPE_KEY).toString();

        bool restored = false;
        foreach (QtVersionFactory *f, factories) {
            if (f->canRestore(type)) {
                if (BaseQtVersion *qtv = f->restore(qtversionMap)) {
                    if (m_versions.contains(qtv->uniqueId())) {
                        // This shouldn't happen, we are restoring the same id multiple times?
                        qWarning() << "A qt version with id"<<qtv->uniqueId()<<"already exists";
                        delete qtv;
                    } else {
                        m_versions.insert(qtv->uniqueId(), qtv);
                        m_idcount = qtv->uniqueId() > m_idcount ? qtv->uniqueId() : m_idcount;
                        restored = true;
                        break;
                    }
                }
            }
        }
        if (!restored)
            qWarning("Warning: Unable to restore qtversion '%s' stored in %s.",
                     qPrintable(type),
                     qPrintable(QDir::toNativeSeparators(settingsFileName())));
    }
    ++m_idcount;
    return true;
}

void QtVersionManager::updateFromInstaller()
{
dt's avatar
dt committed
209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 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 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348
    bool debug = true;
    ExtensionSystem::PluginManager *pm = ExtensionSystem::PluginManager::instance();
    QList<QtVersionFactory *> factories = pm->getObjects<QtVersionFactory>();
    ProjectExplorer::PersistentSettingsReader reader;
    if (!reader.load(Core::ICore::instance()->resourcePath()
                     + QLatin1String("/Nokia") + QLatin1String(QTVERSION_FILENAME)))
        return;

    QVariantMap data = reader.restoreValues();

    if (debug) {
        qDebug()<< "======= Existing qt versions =======";
        foreach (BaseQtVersion *version, m_versions) {
            qDebug() << version->qmakeCommand() << "id:"<<version->uniqueId();
            qDebug() << "  autodetection source:"<< version->autodetectionSource();
            qDebug() << "";
        }
    }

    int oldcount = data.value(QLatin1String(OLDQTVERSION_COUNT_KEY), 0).toInt();
    for (int i=0; i < oldcount; ++i) {
        const QString key = QString::fromLatin1(OLDQTVERSION_DATA_KEY) +QString::number(i);
        if (!data.contains(key))
            break;
        QVariantMap map = data.value(key).toMap();
        QString path = map.value(OLDQTVERSION_PATH).toString();
#ifdef Q_OS_WIN
        path = path.toLower();
#endif
        QString autodetectionSource = map.value(OLDQTVERSION_SDKSOURCE).toString();
        foreach (BaseQtVersion *v, m_versions) {
            if (v->qmakeCommand() == path) {
                if (v->autodetectionSource().isEmpty()) {
                    v->setAutoDetectionSource(autodetectionSource);
                } else {
                    if (debug)
                        qDebug() << "## Conflicting autodetictonSource for"<<path<<"\n"
                                 <<"     version retains"<<v->autodetectionSource();
                }
                // No break, we want to mark all qt versions matching that path
                // There's no way for us to decide whether this qt was added
                // by the user or by the installer, so we treat them all as coming
                // from the installer. Thus removing/updating them deletes/updates them all
                // Note: This only applies to versions that are marked via QtVersion.Old
            }
        }
    }

    if (debug) {
        qDebug()<< "======= After using OLD QtVersion data to mark versions =======";
        foreach (BaseQtVersion *version, m_versions) {
            qDebug() << version->qmakeCommand() << "id:"<<version->uniqueId();
            qDebug() << "  autodetection source:"<< version->autodetectionSource();
            qDebug() << "";
        }

        qDebug()<< "======= Adding sdk versions =======";
    }
    QStringList sdkVersions;
    int count = data.value(QLatin1String(QTVERSION_COUNT_KEY), 0).toInt();
    for (int i = 0; i < count; ++i) {
        const QString key = QString::fromLatin1(QTVERSION_DATA_KEY) + QString::number(i);
        if (!data.contains(key))
            break;

        QVariantMap qtversionMap = data.value(key).toMap();
        const QString type = qtversionMap.value(QTVERSION_TYPE_KEY).toString();
        const QString autoDetectionSource = qtversionMap.value(QLatin1String("autodetectionSource")).toString();
        sdkVersions << autoDetectionSource;
        int id = -1; // see BaseQtVersion::fromMap()
        QtVersionFactory *factory = 0;
        foreach (QtVersionFactory *f, factories) {
            if (f->canRestore(type)) {
                factory = f;
            }
        }
        if (!factory) {
            if (debug)
                qDebug("Warning: Unable to find factory for type '%s'", qPrintable(type));
            continue;
        }
        // First try to find a existing qt version to update
        bool restored = false;
        foreach (BaseQtVersion *v, m_versions) {
            if (v->autodetectionSource() == autoDetectionSource) {
                id = v->uniqueId();
                if (debug)
                    qDebug() << " Qt version found with same autodetection source" << autoDetectionSource << " => Migrating id:" << id;
                removeVersion(v);
                qtversionMap[QLatin1String("Id")] = id;

                if (BaseQtVersion *qtv = factory->restore(qtversionMap)) {
                    Q_ASSERT(qtv->isAutodetected());
                    addVersion(qtv);
                    restored = true;
                }
            }
        }
        // Create a new qtversion
        if (!restored) { // didn't replace any existing versions
            if (debug)
                qDebug() << " No Qt version found matching" << autoDetectionSource << " => Creating new version";
            if (BaseQtVersion *qtv = factory->restore(qtversionMap)) {
                Q_ASSERT(qtv->isAutodetected());
                addVersion(qtv);
                restored = true;
            }
        }
        if (!restored)
            if (debug)
                qDebug("Warning: Unable to update qtversion '%s' from sdk installer.",
                       qPrintable(autoDetectionSource));
    }

    if (debug) {
        qDebug() << "======= Before removing outdated sdk versions =======";
        foreach (BaseQtVersion *version, m_versions) {
            qDebug() << version->qmakeCommand() << "id:"<<version->uniqueId();
            qDebug() << "  autodetection source:"<< version->autodetectionSource();
            qDebug() << "";
        }
    }
    foreach (BaseQtVersion *qtVersion, QtVersionManager::instance()->versions()) {
        if (qtVersion->autodetectionSource().startsWith("SDK.")) {
            if (!sdkVersions.contains(qtVersion->autodetectionSource())) {
                if (debug)
                    qDebug() << "  removing version"<<qtVersion->autodetectionSource();
                removeVersion(qtVersion);
            }
        }
    }

    if (debug) {
        qDebug()<< "======= End result =======";
        foreach (BaseQtVersion *version, m_versions) {
            qDebug() << version->qmakeCommand() << "id:"<<version->uniqueId();
            qDebug() << "  autodetection source:"<< version->autodetectionSource();
            qDebug() << "";
        }
    }
dt's avatar
dt committed
349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385
}

void QtVersionManager::saveQtVersions()
{
    ProjectExplorer::PersistentSettingsWriter writer;
    writer.saveValue(QLatin1String(QTVERSION_FILE_VERSION_KEY), 1);

    int count = 0;
    foreach (BaseQtVersion *qtv, m_versions) {
        QVariantMap tmp = qtv->toMap();
        if (tmp.isEmpty())
            continue;
        tmp.insert(QTVERSION_TYPE_KEY, qtv->type());
        writer.saveValue(QString::fromLatin1(QTVERSION_DATA_KEY) + QString::number(count), tmp);
        ++count;

    }
    writer.saveValue(QLatin1String(QTVERSION_COUNT_KEY), count);
    writer.save(settingsFileName(), "QtCreatorQtVersions", Core::ICore::instance()->mainWindow());
}

void QtVersionManager::findSystemQt()
{
    QString systemQMakePath = ProjectExplorer::DebuggingHelperLibrary::findSystemQt(Utils::Environment::systemEnvironment());
    if (systemQMakePath.isNull())
        return;

    BaseQtVersion *version = QtVersionFactory::createQtVersionFromQMakePath(systemQMakePath);
    version->setDisplayName(tr("Qt in PATH (%1)").arg(version->qtVersionString()));
    m_versions.insert(version->uniqueId(), version);
}

bool QtVersionManager::legacyRestore()
{
    QSettings *s = Core::ICore::instance()->settings();
    if (!s->contains(QLatin1String(QtVersionsSectionName) + QLatin1String("/size")))
        return false;
con's avatar
con committed
386 387 388 389 390 391 392 393 394 395
    int size = s->beginReadArray(QtVersionsSectionName);
    for (int i = 0; i < size; ++i) {
        s->setArrayIndex(i);
        // Find the right id
        // Either something saved or something generated
        // Note: This code assumes that either all ids are read from the settings
        // or generated on the fly.
        int id = s->value("Id", -1).toInt();
        if (id == -1)
            id = getUniqueId();
dt's avatar
dt committed
396 397
        else if (m_idcount < id)
            m_idcount = id + 1;
dt's avatar
dt committed
398

399
        QString qmakePath = s->value("QMakePath").toString();
dt's avatar
dt committed
400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418
        if (qmakePath.isEmpty())
            continue; //skip this version

        // autodetection = false, from now on only sdk versions are autodetected
        BaseQtVersion *version = QtVersionFactory::createQtVersionFromQMakePath(qmakePath);
        if (!version) // Likely to be a invalid version
            continue;
        version->setId(id);
        version->setDisplayName(s->value("Name").toString());
        if (SymbianQtVersion *sqv = dynamic_cast<SymbianQtVersion *>(version)) {
            sqv->setSystemRoot(QDir::fromNativeSeparators(s->value("S60SDKDirectory").toString()));
            sqv->setSbsV2Directory(QDir::fromNativeSeparators(s->value(QLatin1String("SBSv2Directory")).toString()));
        }

        if (m_versions.contains(version->uniqueId())) {
            // oh uh;
            delete version;
        } else {
            m_versions.insert(version->uniqueId(), version);
419
        }
dt's avatar
dt committed
420 421
        // Update from 2.1 or earlier:
        QString mingwDir = s->value(QLatin1String("MingwDirectory")).toString();
422
        if (!mingwDir.isEmpty()) {
423 424 425 426 427 428
            QFileInfo fi(mingwDir + QLatin1String("/bin/g++.exe"));
            if (fi.exists() && fi.isExecutable()) {
                ProjectExplorer::MingwToolChain *tc = createToolChain<ProjectExplorer::MingwToolChain>(ProjectExplorer::Constants::MINGW_TOOLCHAIN_ID);
                if (tc) {
                    tc->setCompilerPath(fi.absoluteFilePath());
                    tc->setDisplayName(tr("MinGW from %1").arg(version->displayName()));
429 430
                    // The debugger is set later in the autoDetect method of the MinGw tool chain factory
                    // as the default debuggers are not yet registered.
431 432
                    ProjectExplorer::ToolChainManager::instance()->registerToolChain(tc);
                }
433 434
            }
        }
435 436 437 438 439 440
        const QString mwcDir = s->value(QLatin1String("MwcDirectory")).toString();
        if (!mwcDir.isEmpty())
            m_pendingMwcUpdates.append(mwcDir);
        const QString gcceDir = s->value(QLatin1String("GcceDirectory")).toString();
        if (!gcceDir.isEmpty())
            m_pendingGcceUpdates.append(gcceDir);
441

con's avatar
con committed
442 443
    }
    s->endArray();
dt's avatar
dt committed
444 445 446
    // TODO add removal of old settings
    // s->remove(QtVersionsSectionName);
    return true;
447 448
}

dt's avatar
dt committed
449
void QtVersionManager::addVersion(BaseQtVersion *version)
con's avatar
con committed
450
{
451
    QTC_ASSERT(version != 0, return);
452
    if (m_versions.contains(version->uniqueId()))
453 454
        return;

455
    int uniqueId = version->uniqueId();
456 457
    m_versions.insert(uniqueId, version);

458
    emit qtVersionsChanged(QList<int>() << uniqueId);
dt's avatar
dt committed
459
    saveQtVersions();
con's avatar
con committed
460 461
}

dt's avatar
dt committed
462
void QtVersionManager::removeVersion(BaseQtVersion *version)
463
{
464
    QTC_ASSERT(version != 0, return);
465 466
    m_versions.remove(version->uniqueId());
    emit qtVersionsChanged(QList<int>() << version->uniqueId());
dt's avatar
dt committed
467
    saveQtVersions();
468 469 470
    delete version;
}

Tobias Hunger's avatar
Tobias Hunger committed
471 472
bool QtVersionManager::supportsTargetId(const QString &id) const
{
dt's avatar
dt committed
473 474
    QList<BaseQtVersion *> versions = QtVersionManager::instance()->versionsForTargetId(id);
    foreach (BaseQtVersion *v, versions)
475
        if (v->isValid() && v->toolChainAvailable(id))
Tobias Hunger's avatar
Tobias Hunger committed
476 477 478 479
            return true;
    return false;
}

dt's avatar
dt committed
480
QList<BaseQtVersion *> QtVersionManager::versionsForTargetId(const QString &id, const QtVersionNumber &minimumQtVersion) const
Tobias Hunger's avatar
Tobias Hunger committed
481
{
dt's avatar
dt committed
482 483
    QList<BaseQtVersion *> targetVersions;
    foreach (BaseQtVersion *version, m_versions) {
484
        if (version->supportsTargetId(id) && version->qtVersion() >= minimumQtVersion)
Tobias Hunger's avatar
Tobias Hunger committed
485 486
            targetVersions.append(version);
    }
487
    qSort(targetVersions.begin(), targetVersions.end(), &qtVersionNumberCompare);
Tobias Hunger's avatar
Tobias Hunger committed
488 489 490 491 492 493
    return targetVersions;
}

QSet<QString> QtVersionManager::supportedTargetIds() const
{
    QSet<QString> results;
dt's avatar
dt committed
494
    foreach (BaseQtVersion *version, m_versions)
Tobias Hunger's avatar
Tobias Hunger committed
495 496 497 498
        results.unite(version->supportedTargetIds());
    return results;
}

con's avatar
con committed
499 500
void QtVersionManager::updateDocumentation()
{
kh1's avatar
kh1 committed
501
    Core::HelpManager *helpManager = Core::HelpManager::instance();
dt's avatar
dt committed
502
    Q_ASSERT(helpManager);
con's avatar
con committed
503
    QStringList files;
dt's avatar
dt committed
504 505
    foreach (BaseQtVersion *v, m_versions) {
        const QString docPath = v->documentationPath() + QLatin1String("/qch/");
506 507 508 509 510
        const QDir versionHelpDir(docPath);
        foreach (const QString &helpFile,
                versionHelpDir.entryList(QStringList() << QLatin1String("*.qch"), QDir::Files))
            files << docPath + helpFile;

con's avatar
con committed
511 512 513 514
    }
    helpManager->registerDocumentation(files);
}

515 516 517 518 519 520 521 522 523 524 525
void QtVersionManager::updateQtVersion(int id)
{
    BaseQtVersion *qtVersion = version(id);
    QTC_ASSERT(qtVersion, return);

    // update actually all Qt versions with the same qmake command
    const QString qmakeCommand = qtVersion->qmakeCommand();
    foreach (BaseQtVersion *v, versions()) {
        if (v->qmakeCommand() == qmakeCommand)
            v->recheckDumper();
    }
526
    emit dumpUpdatedFor(qmakeCommand);
527 528
}

Tobias Hunger's avatar
Tobias Hunger committed
529
void QtVersionManager::updateSettings()
530
{
Tobias Hunger's avatar
Tobias Hunger committed
531 532
    updateDocumentation();

dt's avatar
dt committed
533 534
    BaseQtVersion *version = 0;
    QList<BaseQtVersion *> candidates;
535

Tobias Hunger's avatar
Tobias Hunger committed
536
    // try to find a version which has both, demos and examples
dt's avatar
dt committed
537 538 539
    foreach (BaseQtVersion *version, m_versions) {
        if (version && version->hasExamples() && version->hasDemos())
            candidates.append(version);
540 541
    }

542
    // in SDKs, we want to prefer the Qt version shipping with the SDK
543 544 545
    QSettings *settings = Core::ICore::instance()->settings();
    QString preferred = settings->value(QLatin1String("PreferredQMakePath")).toString();
    preferred = QDir::fromNativeSeparators(preferred);
546
    if (!preferred.isEmpty()) {
547 548 549 550 551
#ifdef Q_OS_WIN
        preferred = preferred.toLower();
        if (!preferred.endsWith(QLatin1String(".exe")))
            preferred.append(QLatin1String(".exe"));
#endif
552 553 554 555 556 557
        foreach (version, candidates) {
            if (version->qmakeCommand() == preferred) {
                emit updateExamples(version->examplesPath(), version->demosPath(), version->sourcePath());
                return;
            }
        }
558 559 560 561 562 563
    }

    // prefer versions with declarative examples
    foreach (version, candidates) {
        if (QDir(version->examplesPath()+"/declarative").exists()) {
            emit updateExamples(version->examplesPath(), version->demosPath(), version->sourcePath());
564 565 566
            return;
        }
    }
567 568 569 570 571 572 573 574

    if (!candidates.isEmpty()) {
        version = candidates.first();
        emit updateExamples(version->examplesPath(), version->demosPath(), version->sourcePath());
        return;
    }
    return;

575 576
}

con's avatar
con committed
577 578 579 580 581
int QtVersionManager::getUniqueId()
{
    return m_idcount++;
}

dt's avatar
dt committed
582
QList<BaseQtVersion *> QtVersionManager::versions() const
con's avatar
con committed
583
{
dt's avatar
dt committed
584 585
    QList<BaseQtVersion *> versions;
    foreach (BaseQtVersion *version, m_versions)
586 587 588
        versions << version;
    qSort(versions.begin(), versions.end(), &qtVersionNumberCompare);
    return versions;
con's avatar
con committed
589 590
}

dt's avatar
dt committed
591
QList<BaseQtVersion *> QtVersionManager::validVersions() const
592
{
dt's avatar
dt committed
593
    QList<BaseQtVersion *> results;
dt's avatar
dt committed
594
    foreach (BaseQtVersion *v, m_versions) {
595 596 597
        if (v->isValid())
            results.append(v);
    }
598
    qSort(results.begin(), results.end(), &qtVersionNumberCompare);
599 600 601
    return results;
}

602 603
bool QtVersionManager::isValidId(int id) const
{
604
    return m_versions.contains(id);
605 606
}

607 608 609 610 611 612 613 614 615 616 617 618 619 620
QString QtVersionManager::popPendingMwcUpdate()
{
    if (m_pendingMwcUpdates.isEmpty())
        return QString();
    return m_pendingMwcUpdates.takeFirst();
}

QString QtVersionManager::popPendingGcceUpdate()
{
    if (m_pendingGcceUpdates.isEmpty())
        return QString();
    return m_pendingGcceUpdates.takeFirst();
}

dt's avatar
dt committed
621
BaseQtVersion *QtVersionManager::version(int id) const
con's avatar
con committed
622
{
dt's avatar
dt committed
623
    QMap<int, BaseQtVersion *>::const_iterator it = m_versions.find(id);
624
    if (it == m_versions.constEnd())
dt's avatar
dt committed
625
        return 0;
626
    return it.value();
con's avatar
con committed
627 628
}

629 630 631
class SortByUniqueId
{
public:
dt's avatar
dt committed
632
    bool operator()(BaseQtVersion *a, BaseQtVersion *b)
633 634 635 636 637
    {
        return a->uniqueId() < b->uniqueId();
    }
};

dt's avatar
dt committed
638
bool QtVersionManager::equals(BaseQtVersion *a, BaseQtVersion *b)
639
{
dt's avatar
dt committed
640
    return a->equals(b);
641 642
}

dt's avatar
dt committed
643
void QtVersionManager::setNewQtVersions(QList<BaseQtVersion *> newVersions)
644
{
645 646
    // We want to preserve the same order as in the settings dialog
    // so we sort a copy
dt's avatar
dt committed
647
    QList<BaseQtVersion *> sortedNewVersions = newVersions;
648 649 650 651 652 653 654 655
    SortByUniqueId sortByUniqueId;
    qSort(sortedNewVersions.begin(), sortedNewVersions.end(), sortByUniqueId);

    QList<int> changedVersions;
    // So we trying to find the minimal set of changed versions,
    // iterate over both sorted list

    // newVersions and oldVersions iterator
dt's avatar
dt committed
656 657
    QList<BaseQtVersion *>::const_iterator nit, nend;
    QMap<int, BaseQtVersion *>::const_iterator oit, oend;
658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676
    nit = sortedNewVersions.constBegin();
    nend = sortedNewVersions.constEnd();
    oit = m_versions.constBegin();
    oend = m_versions.constEnd();

    while (nit != nend && oit != oend) {
        int nid = (*nit)->uniqueId();
        int oid = (*oit)->uniqueId();
        if (nid < oid) {
            changedVersions.push_back(nid);
            ++nit;
        } else if (oid < nid) {
            changedVersions.push_back(oid);
            ++oit;
        } else {
            if (!equals(*oit, *nit))
                changedVersions.push_back(oid);
            ++oit;
            ++nit;
677 678
        }
    }
679 680 681

    while (nit != nend) {
        changedVersions.push_back((*nit)->uniqueId());
682
        ++nit;
683 684 685 686
    }

    while (oit != oend) {
        changedVersions.push_back((*oit)->uniqueId());
687
        ++oit;
688 689
    }

690 691
    qDeleteAll(m_versions);
    m_versions.clear();
dt's avatar
dt committed
692
    foreach (BaseQtVersion *v, sortedNewVersions)
693
        m_versions.insert(v->uniqueId(), v);
694 695

    if (!changedVersions.isEmpty())
696 697
        updateDocumentation();

Tobias Hunger's avatar
Tobias Hunger committed
698
    updateSettings();
dt's avatar
dt committed
699
    saveQtVersions();
700 701 702

    if (!changedVersions.isEmpty())
        emit qtVersionsChanged(changedVersions);
703 704
}

con's avatar
con committed
705 706
// Returns the version that was used to build the project in that directory
// That is returns the directory
Tobias Hunger's avatar
Tobias Hunger committed
707
// To find out whether we already have a qtversion for that directory call
con's avatar
con committed
708
// QtVersion *QtVersionManager::qtVersionForDirectory(const QString directory);
709
QString QtVersionManager::findQMakeBinaryFromMakefile(const QString &makefile)
con's avatar
con committed
710 711
{
    bool debugAdding = false;
712 713 714
    QFile fi(makefile);
    if (fi.exists() && fi.open(QFile::ReadOnly)) {
        QTextStream ts(&fi);
715
        QRegExp r1("QMAKE\\s*=(.*)");
con's avatar
con committed
716 717 718 719 720 721
        while (!ts.atEnd()) {
            QString line = ts.readLine();
            if (r1.exactMatch(line)) {
                if (debugAdding)
                    qDebug()<<"#~~ QMAKE is:"<<r1.cap(1).trimmed();
                QFileInfo qmake(r1.cap(1).trimmed());
722
                QString qmakePath = qmake.filePath();
723
#ifdef Q_OS_WIN
724 725
                if (!qmakePath.endsWith(QLatin1String(".exe")))
                    qmakePath.append(QLatin1String(".exe"));
726
#endif
727
                // Is qmake still installed?
728 729 730 731 732 733
                QFileInfo fi(qmakePath);
                if (fi.exists()) {
                    qmakePath = fi.absoluteFilePath();
#ifdef Q_OS_WIN
                    qmakePath = qmakePath.toLower();
#endif
734
                    return qmakePath;
735
                }
con's avatar
con committed
736 737 738
            }
        }
    }
739
    return QString();
con's avatar
con committed
740 741
}

dt's avatar
dt committed
742
BaseQtVersion *QtVersionManager::qtVersionForQMakeBinary(const QString &qmakePath)
con's avatar
con committed
743
{
dt's avatar
dt committed
744
   foreach (BaseQtVersion *version, versions()) {
dt's avatar
dt committed
745 746
       if (version->qmakeCommand() == qmakePath) {
           return version;
Tobias Hunger's avatar
Tobias Hunger committed
747 748 749
           break;
       }
   }
con's avatar
con committed
750 751 752
   return 0;
}

753
void dumpQMakeAssignments(const QList<QMakeAssignment> &list)
con's avatar
con committed
754
{
755
    foreach(const QMakeAssignment &qa, list) {
756 757 758 759
        qDebug()<<qa.variable<<qa.op<<qa.value;
    }
}

760
QtVersionManager::MakefileCompatible QtVersionManager::makefileIsFor(const QString &makefile, const QString &proFile)
761 762
{
    if (proFile.isEmpty())
763
        return CouldNotParse;
764

765
    QString line = findQMakeLine(makefile, QLatin1String("# Project:")).trimmed();
766
    if (line.isEmpty())
767
        return CouldNotParse;
768 769 770 771

    line = line.mid(line.indexOf(QChar(':')) + 1);
    line = line.trimmed();

772
    QFileInfo srcFileInfo(QFileInfo(makefile).absoluteDir(), line);
773
    QFileInfo proFileInfo(proFile);
774
    return (srcFileInfo == proFileInfo) ? SameProject : DifferentProject;
775 776
}

dt's avatar
dt committed
777
QPair<BaseQtVersion::QmakeBuildConfigs, QString> QtVersionManager::scanMakeFile(const QString &makefile, BaseQtVersion::QmakeBuildConfigs defaultBuildConfig)
778
{
779 780
    if (debug)
        qDebug()<<"ScanMakeFile, the gory details:";
dt's avatar
dt committed
781
    BaseQtVersion::QmakeBuildConfigs result = defaultBuildConfig;
782
    QString result2;
783

784
    QString line = findQMakeLine(makefile, QLatin1String("# Command:"));
785
    if (!line.isEmpty()) {
786 787
        if (debug)
            qDebug()<<"Found line"<<line;
788 789 790
        line = trimLine(line);
        QList<QMakeAssignment> assignments;
        QList<QMakeAssignment> afterAssignments;
791
        parseArgs(line, &assignments, &afterAssignments, &result2);
792

793 794 795 796 797 798
        if (debug) {
            dumpQMakeAssignments(assignments);
            if (!afterAssignments.isEmpty())
                qDebug()<<"-after";
            dumpQMakeAssignments(afterAssignments);
        }
799 800 801 802 803

        // Search in assignments for CONFIG(+=,-=,=)(debug,release,debug_and_release)
        // Also remove them from the list
        result = qmakeBuildConfigFromCmdArgs(&assignments, defaultBuildConfig);

dt's avatar
dt committed
804 805
        if (debug)
            dumpQMakeAssignments(assignments);
806

807
        foreach(const QMakeAssignment &qa, assignments)
808
            Utils::QtcProcess::addArg(&result2, qa.variable + qa.op + qa.value);
809
        if (!afterAssignments.isEmpty()) {
810
            Utils::QtcProcess::addArg(&result2, QLatin1String("-after"));
811
            foreach(const QMakeAssignment &qa, afterAssignments)
812
                Utils::QtcProcess::addArg(&result2, qa.variable + qa.op + qa.value);
813 814 815 816
        }
    }

    // Dump the gathered information:
817 818 819
    if (debug) {
        qDebug()<<"\n\nDumping information from scanMakeFile";
        qDebug()<<"QMake CONFIG variable parsing";
dt's avatar
dt committed
820 821 822
        qDebug()<<"  "<< (result & BaseQtVersion::NoBuild ? "No Build" : QString::number(int(result)));
        qDebug()<<"  "<< (result & BaseQtVersion::DebugBuild ? "debug" : "release");
        qDebug()<<"  "<< (result & BaseQtVersion::BuildAll ? "debug_and_release" : "no debug_and_release");
823 824 825 826
        qDebug()<<"\nAddtional Arguments";
        qDebug()<<result2;
        qDebug()<<"\n\n";
    }
827 828 829
    return qMakePair(result, result2);
}

830
QString QtVersionManager::findQMakeLine(const QString &makefile, const QString &key)
831
{
832 833 834
    QFile fi(makefile);
    if (fi.exists() && fi.open(QFile::ReadOnly)) {
        QTextStream ts(&fi);
con's avatar
con committed
835
        while (!ts.atEnd()) {
836
            const QString line = ts.readLine();
837
            if (line.startsWith(key))
838 839 840 841 842 843 844 845 846 847 848
                return line;
        }
    }
    return QString();
}

/// This function trims the "#Command /path/to/qmake" from the the line
QString QtVersionManager::trimLine(const QString line)
{

    // Actually the first space after #Command: /path/to/qmake
849
    const int firstSpace = line.indexOf(QLatin1Char(' '), 11);
850 851 852
    return line.mid(firstSpace).trimmed();
}

853
void QtVersionManager::parseArgs(const QString &args, QList<QMakeAssignment> *assignments, QList<QMakeAssignment> *afterAssignments, QString *additionalArguments)
854 855 856 857
{
    QRegExp regExp("([^\\s\\+-]*)\\s*(\\+=|=|-=|~=)(.*)");
    bool after = false;
    bool ignoreNext = false;
858 859 860
    *additionalArguments = args;
    Utils::QtcProcess::ArgIterator ait(additionalArguments);
    while (ait.next()) {
861 862 863
        if (ignoreNext) {
            // Ignoring
            ignoreNext = false;
864 865
            ait.deleteArg();
        } else if (ait.value() == QLatin1String("-after")) {
866
            after = true;
867 868 869
            ait.deleteArg();
        } else if (ait.value().contains(QLatin1Char('='))) {
            if (regExp.exactMatch(ait.value())) {
870 871 872 873 874 875 876 877 878 879 880
                QMakeAssignment qa;
                qa.variable = regExp.cap(1);
                qa.op = regExp.cap(2);
                qa.value = regExp.cap(3).trimmed();
                if (after)
                    afterAssignments->append(qa);
                else
                    assignments->append(qa);
            } else {
                qDebug()<<"regexp did not match";
            }
881 882
            ait.deleteArg();
        } else if (ait.value() == QLatin1String("-o")) {
883
            ignoreNext = true;
884
            ait.deleteArg();
885
#if defined(Q_OS_WIN32)
886
        } else if (ait.value() == QLatin1String("-win32")) {
887
#elif defined(Q_OS_MAC)
888
        } else if (ait.value() == QLatin1String("-macx")) {
889
#elif defined(Q_OS_QNX6)
890
        } else if (ait.value() == QLatin1String("-qnx6")) {
891
#else
892
        } else if (ait.value() == QLatin1String("-unix")) {
893
#endif
894 895 896 897
            ait.deleteArg();
        }
    }
    ait.deleteArg();  // The .pro file is always the last arg
898 899 900
}

/// This function extracts all the CONFIG+=debug, CONFIG+=release
dt's avatar
dt committed
901
BaseQtVersion::QmakeBuildConfigs QtVersionManager::qmakeBuildConfigFromCmdArgs(QList<QMakeAssignment> *assignments, BaseQtVersion::QmakeBuildConfigs defaultBuildConfig)
902
{
dt's avatar
dt committed
903
    BaseQtVersion::QmakeBuildConfigs result = defaultBuildConfig;
904 905
    QList<QMakeAssignment> oldAssignments = *assignments;
    assignments->clear();
906
    foreach(const QMakeAssignment &qa, oldAssignments) {
907 908 909 910 911 912
        if (qa.variable == "CONFIG") {
            QStringList values = qa.value.split(' ');
            QStringList newValues;
            foreach(const QString &value, values) {
                if (value == "debug") {
                    if (qa.op == "+=")
dt's avatar
dt committed
913
                        result = result  | BaseQtVersion::DebugBuild;
914
                    else
dt's avatar
dt committed
915
                        result = result  & ~BaseQtVersion::DebugBuild;
916 917
                } else if (value == "release") {
                    if (qa.op == "+=")
dt's avatar
dt committed
918
                        result = result & ~BaseQtVersion::DebugBuild;
919
                    else
dt's avatar
dt committed
920
                        result = result | BaseQtVersion::DebugBuild;
921 922
                } else if (value == "debug_and_release") {
                    if (qa.op == "+=")
dt's avatar
dt committed
923
                        result = result | BaseQtVersion::BuildAll;
924
                    else
dt's avatar
dt committed
925
                        result = result & ~BaseQtVersion::BuildAll;
926 927
                } else {
                    newValues.append(value);
con's avatar
con committed
928
                }
929 930 931 932
                QMakeAssignment newQA = qa;
                newQA.value = newValues.join(" ");
                if (!newValues.isEmpty())
                    assignments->append(newQA);
con's avatar
con committed
933
            }
934 935
        } else {
            assignments->append(qa);
con's avatar
con committed
936 937 938 939
        }
    }
    return result;
}