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

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

36
#include "qt4projectmanagerconstants.h"
Tobias Hunger's avatar
Tobias Hunger committed
37
#include "qt4target.h"
38
#include "profilereader.h"
con's avatar
con committed
39

ck's avatar
ck committed
40
#include "qt-maemo/maemomanager.h"
41
#include "qt-s60/s60manager.h"
42
#include "qt-s60/s60projectchecker.h"
43

44
#include "qmlobservertool.h"
45
#include "qmldumptool.h"
46
47
#include <projectexplorer/debugginghelper.h>
#include <projectexplorer/projectexplorer.h>
48
#include <projectexplorer/projectexplorerconstants.h>
49
#include <projectexplorer/cesdkhandler.h>
50
#include <utils/synchronousprocess.h>
51
#include <coreplugin/coreconstants.h>
52
#include <coreplugin/icore.h>
kh1's avatar
kh1 committed
53
#include <coreplugin/helpmanager.h>
54
#include <extensionsystem/pluginmanager.h>
kh1's avatar
kh1 committed
55
#include <help/helpmanager.h>
hjk's avatar
hjk committed
56
#include <utils/qtcassert.h>
con's avatar
con committed
57

58
#include <QtCore/QFile>
hjk's avatar
hjk committed
59
#include <QtCore/QProcess>
con's avatar
con committed
60
61
#include <QtCore/QSettings>
#include <QtCore/QTime>
62
#include <QtCore/QTimer>
63
#include <QtCore/QTextStream>
64
#include <QtCore/QDir>
65
66
#include <QtGui/QApplication>
#include <QtGui/QDesktopServices>
con's avatar
con committed
67

68
69
70
71
#ifdef Q_OS_WIN32
#include <windows.h>
#endif

72
73
74
75
using namespace Qt4ProjectManager;
using namespace Qt4ProjectManager::Internal;

using ProjectExplorer::DebuggingHelperLibrary;
con's avatar
con committed
76
77
78

static const char *QtVersionsSectionName = "QtVersions";
static const char *newQtVersionsKey = "NewQtVersions";
79
static const char *PATH_AUTODETECTION_SOURCE = "PATH";
con's avatar
con committed
80

81
82
enum { debug = 0 };

83
QtVersionManager *QtVersionManager::m_self = 0;
84

con's avatar
con committed
85
86
87
QtVersionManager::QtVersionManager()
    : m_emptyVersion(new QtVersion)
{
88
    m_self = this;
89
    QSettings *s = Core::ICore::instance()->settings();
con's avatar
con committed
90
91
92
93
94
95
96
97
98
99
100
101

    m_idcount = 1;
    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
102
103
        else if (m_idcount < id)
            m_idcount = id + 1;
104
        bool isAutodetected;
105
106
        QString autodetectionSource;
        if (s->contains("isAutodetected")) {
107
            isAutodetected = s->value("isAutodetected", false).toBool();
108
109
            autodetectionSource = s->value("autodetectionSource", QString()).toString();
        } else {// compatibility
110
            isAutodetected = s->value("IsSystemVersion", false).toBool();
111
112
113
            if (isAutodetected)
                autodetectionSource = QLatin1String(PATH_AUTODETECTION_SOURCE);
        }
114
115
116
        QString qmakePath = s->value("QMakePath").toString();
        if (qmakePath.isEmpty()) {
            QString path = s->value("Path").toString();
117
118
119
120
121
122
123
124
125
126
127
            if (!path.isEmpty()) {
                foreach(const QString& command, ProjectExplorer::DebuggingHelperLibrary::possibleQMakeCommands())
                {
                    QFileInfo fi(path + "/bin/" + command);
                    if (fi.exists())
                    {
                        qmakePath = fi.filePath();
                        break;
                    }
                }
            }
128
        }
con's avatar
con committed
129
        QtVersion *version = new QtVersion(s->value("Name").toString(),
130
                                           qmakePath,
con's avatar
con committed
131
                                           id,
132
133
                                           isAutodetected,
                                           autodetectionSource);
con's avatar
con committed
134
135
        version->setMingwDirectory(s->value("MingwDirectory").toString());
        version->setMsvcVersion(s->value("msvcVersion").toString());
con's avatar
con committed
136
        version->setMwcDirectory(s->value("MwcDirectory").toString());
137
        version->setS60SDKDirectory(s->value("S60SDKDirectory").toString());
con's avatar
con committed
138
        version->setGcceDirectory(s->value("GcceDirectory").toString());
139
        version->setSbsV2Directory(s->value(QLatin1String("SBSv2Directory")).toString());
con's avatar
con committed
140
141
142
143
144
145
146
147
148
149
        m_versions.append(version);
    }
    s->endArray();
    updateUniqueIdToIndexMap();

    ++m_idcount;
    addNewVersionsFromInstaller();
    updateSystemVersion();

    writeVersionsIntoSettings();
150

con's avatar
con committed
151
    updateDocumentation();
152

153
154
    // cannot call from ctor, needs to get connected extenernally first
    QTimer::singleShot(0, this, SLOT(updateExamples()));
con's avatar
con committed
155
156
157
158
159
160
161
162
163
164
}

QtVersionManager::~QtVersionManager()
{
    qDeleteAll(m_versions);
    m_versions.clear();
    delete m_emptyVersion;
    m_emptyVersion = 0;
}

165
QtVersionManager *QtVersionManager::instance()
166
{
167
    return m_self;
168
169
}

con's avatar
con committed
170
171
void QtVersionManager::addVersion(QtVersion *version)
{
172
    QTC_ASSERT(version != 0, return);
173
174
175
    if (m_versions.contains(version))
        return;

con's avatar
con committed
176
    m_versions.append(version);
177
178
179
    int uniqueId = version->uniqueId();
    m_uniqueIdToIndex.insert(uniqueId, m_versions.count() - 1);
    emit qtVersionsChanged(QList<int>() << uniqueId);
180
    writeVersionsIntoSettings();
con's avatar
con committed
181
182
}

183
184
void QtVersionManager::removeVersion(QtVersion *version)
{
185
    QTC_ASSERT(version != 0, return);
186
    m_versions.removeAll(version);
187
188
189
    int uniqueId = version->uniqueId();
    m_uniqueIdToIndex.remove(uniqueId);
    emit qtVersionsChanged(QList<int>() << uniqueId);
190
191
192
193
    writeVersionsIntoSettings();
    delete version;
}

Tobias Hunger's avatar
Tobias Hunger committed
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
bool QtVersionManager::supportsTargetId(const QString &id) const
{
    foreach (QtVersion *version, m_versions) {
        if (version->supportsTargetId(id))
            return true;
    }
    return false;
}

QList<QtVersion *> QtVersionManager::versionsForTargetId(const QString &id) const
{
    QList<QtVersion *> targetVersions;
    foreach (QtVersion *version, m_versions) {
        if (version->supportsTargetId(id))
            targetVersions.append(version);
    }
    return targetVersions;
}

QSet<QString> QtVersionManager::supportedTargetIds() const
{
    QSet<QString> results;
    foreach (QtVersion *version, m_versions)
        results.unite(version->supportedTargetIds());
    return results;
}

con's avatar
con committed
221
222
void QtVersionManager::updateDocumentation()
{
kh1's avatar
kh1 committed
223
    Core::HelpManager *helpManager = Core::HelpManager::instance();
dt's avatar
dt committed
224
    Q_ASSERT(helpManager);
con's avatar
con committed
225
226
    QStringList files;
    foreach (QtVersion *version, m_versions) {
227
228
229
230
231
232
        const QString docPath = version->documentationPath() + QLatin1String("/qch/");
        const QDir versionHelpDir(docPath);
        foreach (const QString &helpFile,
                versionHelpDir.entryList(QStringList() << QLatin1String("*.qch"), QDir::Files))
            files << docPath + helpFile;

con's avatar
con committed
233
234
235
236
    }
    helpManager->registerDocumentation(files);
}

237
238
239
void QtVersionManager::updateExamples()
{
    QtVersion *version = 0;
240
241
    QList<QtVersion*> candidates;

Tobias Hunger's avatar
Tobias Hunger committed
242
    // try to find a version which has both, demos and examples
243
244
245
    foreach (version, m_versions) {
        if (version->hasExamples() && version->hasDemos())
        candidates.append(version);
246
247
248
    }

    // in SDKs, we want to prefer the Qt version shipping with the SDK
249
250
251
    QSettings *settings = Core::ICore::instance()->settings();
    QString preferred = settings->value(QLatin1String("PreferredQMakePath")).toString();
    preferred = QDir::fromNativeSeparators(preferred);
252
    if (!preferred.isEmpty()) {
253
254
255
256
257
#ifdef Q_OS_WIN
        preferred = preferred.toLower();
        if (!preferred.endsWith(QLatin1String(".exe")))
            preferred.append(QLatin1String(".exe"));
#endif
258
259
260
261
262
263
        foreach (version, candidates) {
            if (version->qmakeCommand() == preferred) {
                emit updateExamples(version->examplesPath(), version->demosPath(), version->sourcePath());
                return;
            }
        }
264
265
266
267
268
269
    }

    // prefer versions with declarative examples
    foreach (version, candidates) {
        if (QDir(version->examplesPath()+"/declarative").exists()) {
            emit updateExamples(version->examplesPath(), version->demosPath(), version->sourcePath());
270
271
272
            return;
        }
    }
273
274
275
276
277
278
279
280

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

281
282
}

con's avatar
con committed
283
284
285
286
287
288
289
290
int QtVersionManager::getUniqueId()
{
    return m_idcount++;
}

void QtVersionManager::updateUniqueIdToIndexMap()
{
    m_uniqueIdToIndex.clear();
hjk's avatar
hjk committed
291
    for (int i = 0; i < m_versions.size(); ++i)
con's avatar
con committed
292
293
294
295
296
        m_uniqueIdToIndex.insert(m_versions.at(i)->uniqueId(), i);
}

void QtVersionManager::writeVersionsIntoSettings()
{
297
    QSettings *s = Core::ICore::instance()->settings();
298
    s->beginWriteArray(QtVersionsSectionName);
con's avatar
con committed
299
    for (int i = 0; i < m_versions.size(); ++i) {
300
        const QtVersion *version = m_versions.at(i);
con's avatar
con committed
301
        s->setArrayIndex(i);
302
        s->setValue("Name", version->displayName());
303
304
305
        // for downwards compat
        s->setValue("Path", version->versionInfo().value("QT_INSTALL_DATA"));
        s->setValue("QMakePath", version->qmakeCommand());
306
307
308
309
310
311
        s->setValue("Id", version->uniqueId());
        s->setValue("MingwDirectory", version->mingwDirectory());
        s->setValue("msvcVersion", version->msvcVersion());
        s->setValue("isAutodetected", version->isAutodetected());
        if (version->isAutodetected())
            s->setValue("autodetectionSource", version->autodetectionSource());
con's avatar
con committed
312
        s->setValue("MwcDirectory", version->mwcDirectory());
313
        s->setValue("S60SDKDirectory", version->s60SDKDirectory());
con's avatar
con committed
314
        s->setValue("GcceDirectory", version->gcceDirectory());
315
        s->setValue(QLatin1String("SBSv2Directory"), version->sbsV2Directory());
con's avatar
con committed
316
317
318
319
    }
    s->endArray();
}

320
QList<QtVersion *> QtVersionManager::versions() const
con's avatar
con committed
321
322
323
324
{
    return m_versions;
}

325
326
327
328
329
330
331
332
333
334
QList<QtVersion *> QtVersionManager::validVersions() const
{
    QList<QtVersion *> results;
    foreach(QtVersion *v, m_versions) {
        if (v->isValid())
            results.append(v);
    }
    return results;
}

335
336
337
338
339
340
bool QtVersionManager::isValidId(int id) const
{
    int pos = m_uniqueIdToIndex.value(id, -1);
    return (pos != -1);
}

con's avatar
con committed
341
342
343
344
345
346
QtVersion *QtVersionManager::version(int id) const
{
    int pos = m_uniqueIdToIndex.value(id, -1);
    if (pos != -1)
        return m_versions.at(pos);

Tobias Hunger's avatar
Tobias Hunger committed
347
    return m_emptyVersion;
con's avatar
con committed
348
349
350
351
352
}

void QtVersionManager::addNewVersionsFromInstaller()
{
    // Add new versions which may have been installed by the WB installer in the form:
353
    // NewQtVersions="qt 4.3.2=c:\\qt\\qt432\bin\qmake.exe;qt embedded=c:\\qtembedded;"
354
    // or NewQtVersions="qt 4.3.2=c:\\qt\\qt432bin\qmake.exe=c:\\qtcreator\\mingw\\=MSVCName;
355
356
    // i.e.
    // NewQtVersions="versionname=pathtoversion=mingw=s60sdk=gcce=carbide;"
con's avatar
con committed
357
    // Duplicate entries are not added, the first new version is set as default.
358
    QSettings *settings = Core::ICore::instance()->settings();
359
    QSettings *globalSettings = Core::ICore::instance()->settings(QSettings::SystemScope);
360

361
362
363
364
    QDateTime lastUpdateFromGlobalSettings = globalSettings->value(
            QLatin1String("General/LastQtVersionUpdate")).toDateTime();

    const QFileInfo gsFi(globalSettings->fileName());
365
366
    if ( !lastUpdateFromGlobalSettings.isNull() &&
         (!gsFi.exists() || (gsFi.lastModified() > lastUpdateFromGlobalSettings)) )
con's avatar
con committed
367
368
        return;

369
370
371
372
373
    if (!globalSettings->contains(newQtVersionsKey) &&
        !globalSettings->contains(QLatin1String("Installer/")+newQtVersionsKey))
    {
        return;
    }
con's avatar
con committed
374
375

    QString newVersionsValue = settings->value(newQtVersionsKey).toString();
376
377
378
    if (newVersionsValue.isEmpty())
        newVersionsValue = settings->value(QLatin1String("Installer/")+newQtVersionsKey).toString();

con's avatar
con committed
379
    QStringList newVersionsList = newVersionsValue.split(';', QString::SkipEmptyParts);
380
    foreach (const QString &newVersion, newVersionsList) {
con's avatar
con committed
381
        QStringList newVersionData = newVersion.split('=');
382
        if (newVersionData.count() >= 2) {
383
            if (QFile::exists(newVersionData[1])) {
con's avatar
con committed
384
                QtVersion *version = new QtVersion(newVersionData[0], newVersionData[1], m_idcount++ );
hjk's avatar
hjk committed
385
                if (newVersionData.count() >= 3)
con's avatar
con committed
386
                    version->setMingwDirectory(newVersionData[2]);
387
                if (newVersionData.count() >= 4)
388
                    version->setS60SDKDirectory(QDir::fromNativeSeparators(newVersionData[3]));
389
                if (newVersionData.count() >= 5)
390
                    version->setGcceDirectory(QDir::fromNativeSeparators(newVersionData[4]));
391
                if (newVersionData.count() >= 6)
392
                    version->setMwcDirectory(QDir::fromNativeSeparators(newVersionData[5]));
393
394
                if (newVersionData.count() >= 7)
                    version->setMsvcVersion(newVersionData[6]);
395
                if (newVersionData.count() >= 8)
396
                    version->setSbsV2Directory(QDir::fromNativeSeparators(newVersionData[7]));
con's avatar
con committed
397
398
399

                bool versionWasAlreadyInList = false;
                foreach(const QtVersion * const it, m_versions) {
400
                    if (QDir(version->qmakeCommand()).canonicalPath() == QDir(it->qmakeCommand()).canonicalPath()) {
con's avatar
con committed
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
                        versionWasAlreadyInList = true;
                        break;
                    }
                }

                if (!versionWasAlreadyInList) {
                    m_versions.append(version);
                } else {
                    // clean up
                    delete version;
                }
            }
        }
    }
    updateUniqueIdToIndexMap();
416
    settings->setValue(QLatin1String("General/LastQtVersionUpdate"), QDateTime::currentDateTime());
con's avatar
con committed
417
418
419
420
421
}

void QtVersionManager::updateSystemVersion()
{
    bool haveSystemVersion = false;
422
    QString systemQMakePath = DebuggingHelperLibrary::findSystemQt(Utils::Environment::systemEnvironment());
423
424
    if (systemQMakePath.isNull())
        systemQMakePath = tr("<not found>");
425

con's avatar
con committed
426
    foreach (QtVersion *version, m_versions) {
427
428
        if (version->isAutodetected()
            && version->autodetectionSource() == PATH_AUTODETECTION_SOURCE) {
429
            version->setQMakeCommand(systemQMakePath);
430
            version->setDisplayName(tr("Qt in PATH"));
con's avatar
con committed
431
432
433
434
435
            haveSystemVersion = true;
        }
    }
    if (haveSystemVersion)
        return;
436
    QtVersion *version = new QtVersion(tr("Qt in PATH"),
437
                                       systemQMakePath,
con's avatar
con committed
438
                                       getUniqueId(),
439
440
                                       true,
                                       PATH_AUTODETECTION_SOURCE);
con's avatar
con committed
441
442
443
444
    m_versions.prepend(version);
    updateUniqueIdToIndexMap();
}

Tobias Hunger's avatar
Tobias Hunger committed
445
QtVersion *QtVersionManager::emptyVersion() const
con's avatar
con committed
446
{
Tobias Hunger's avatar
Tobias Hunger committed
447
    return m_emptyVersion;
con's avatar
con committed
448
449
}

450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
class SortByUniqueId
{
public:
    bool operator()(QtVersion *a, QtVersion *b)
    {
        return a->uniqueId() < b->uniqueId();
    }
};

bool QtVersionManager::equals(QtVersion *a, QtVersion *b)
{
    if (a->m_qmakeCommand != b->m_qmakeCommand)
        return false;
    if (a->m_id != b->m_id)
        return false;
    if (a->m_mingwDirectory != b->m_mingwDirectory
        || a->m_msvcVersion != b->m_msvcVersion
        || a->m_mwcDirectory != b->m_mwcDirectory)
        return false;
469
470
    if (a->m_displayName != b->displayName())
        return false;
471
472
473
    return true;
}

Tobias Hunger's avatar
Tobias Hunger committed
474
void QtVersionManager::setNewQtVersions(QList<QtVersion *> newVersions)
475
{
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
    // We want to preserve the same order as in the settings dialog
    // so we sort a copy
    QList<QtVersion *> sortedNewVersions = newVersions;
    SortByUniqueId sortByUniqueId;
    qSort(sortedNewVersions.begin(), sortedNewVersions.end(), sortByUniqueId);
    qSort(m_versions.begin(), m_versions.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
    QList<QtVersion *>::const_iterator nit, nend, oit, oend;
    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;
508
509
        }
    }
510
511
512

    while (nit != nend) {
        changedVersions.push_back((*nit)->uniqueId());
513
        ++nit;
514
515
516
517
    }

    while (oit != oend) {
        changedVersions.push_back((*oit)->uniqueId());
518
        ++oit;
519
520
    }

521
522
    qDeleteAll(m_versions);
    m_versions.clear();
dt's avatar
dt committed
523
    m_versions = newVersions;
524
525

    if (!changedVersions.isEmpty())
526
527
528
        updateDocumentation();
    updateUniqueIdToIndexMap();

529
    updateExamples();
530
    writeVersionsIntoSettings();
531
532
533

    if (!changedVersions.isEmpty())
        emit qtVersionsChanged(changedVersions);
534
535
}

con's avatar
con committed
536
537
538
539
///
/// QtVersion
///

540
QtVersion::QtVersion(const QString &name, const QString &qmakeCommand, int id,
541
                     bool isAutodetected, const QString &autodetectionSource)
542
    : m_displayName(name),
543
    m_isAutodetected(isAutodetected),
544
    m_autodetectionSource(autodetectionSource),
545
    m_hasDebuggingHelper(false),
546
547
    m_hasQmlDump(false),
    m_hasQmlObserver(false),
548
    m_toolChainUpToDate(false),
549
    m_versionInfoUpToDate(false),
550
551
552
    m_notInstalled(false),
    m_defaultConfigIsDebug(true),
    m_defaultConfigIsDebugAndRelease(true),
553
554
    m_hasExamples(false),
    m_hasDemos(false),
555
    m_hasDocumentation(false)
con's avatar
con committed
556
{
hjk's avatar
hjk committed
557
    if (id == -1)
con's avatar
con committed
558
559
560
        m_id = getUniqueId();
    else
        m_id = id;
561
    setQMakeCommand(qmakeCommand);
con's avatar
con committed
562
563
}

564
QtVersion::QtVersion(const QString &name, const QString &qmakeCommand,
565
                     bool isAutodetected, const QString &autodetectionSource)
566
    : m_displayName(name),
567
568
    m_isAutodetected(isAutodetected),
    m_autodetectionSource(autodetectionSource),
569
    m_hasDebuggingHelper(false),
570
571
    m_hasQmlDump(false),
    m_hasQmlObserver(false),
572
    m_toolChainUpToDate(false),
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
    m_versionInfoUpToDate(false),
    m_notInstalled(false),
    m_defaultConfigIsDebug(true),
    m_defaultConfigIsDebugAndRelease(true),
    m_hasExamples(false),
    m_hasDemos(false),
    m_hasDocumentation(false)
{
    m_id = getUniqueId();
    setQMakeCommand(qmakeCommand);
}


QtVersion::QtVersion(const QString &qmakeCommand, bool isAutodetected, const QString &autodetectionSource)
    : m_isAutodetected(isAutodetected),
    m_autodetectionSource(autodetectionSource),
    m_hasDebuggingHelper(false),
590
591
    m_hasQmlDump(false),
    m_hasQmlObserver(false),
592
    m_toolChainUpToDate(false),
593
594
595
596
597
598
599
    m_versionInfoUpToDate(false),
    m_notInstalled(false),
    m_defaultConfigIsDebug(true),
    m_defaultConfigIsDebugAndRelease(true),
    m_hasExamples(false),
    m_hasDemos(false),
    m_hasDocumentation(false)
con's avatar
con committed
600
601
{
    m_id = getUniqueId();
602
    setQMakeCommand(qmakeCommand);
603
    m_displayName = qtVersionString();
con's avatar
con committed
604
605
}

606
QtVersion::QtVersion()
607
    :  m_id(-1),
608
609
    m_isAutodetected(false),
    m_hasDebuggingHelper(false),
610
611
    m_hasQmlDump(false),
    m_hasQmlObserver(false),
612
    m_toolChainUpToDate(false),
613
614
615
616
617
618
619
620
    m_versionInfoUpToDate(false),
    m_notInstalled(false),
    m_defaultConfigIsDebug(true),
    m_defaultConfigIsDebugAndRelease(true),
    m_hasExamples(false),
    m_hasDemos(false),
    m_hasDocumentation(false)
{
621
    setQMakeCommand(QString());
622
623
624
}


625
626
627
628
QtVersion::~QtVersion()
{
}

629
630
631
632
QString QtVersion::toHtml() const
{
    QString rc;
    QTextStream str(&rc);
Robert Loehning's avatar
Robert Loehning committed
633
    str << "<html><body><table>";
634
    str << "<tr><td><b>" << QtVersionManager::tr("Name:")
635
        << "</b></td><td>" << displayName() << "</td></tr>";
636
637
638
639
640
641
    str << "<tr><td><b>" << QtVersionManager::tr("Source:")
        << "</b></td><td>" << sourcePath() << "</td></tr>";
    str << "<tr><td><b>" << QtVersionManager::tr("mkspec:")
        << "</b></td><td>" << mkspec() << "</td></tr>";
    str << "<tr><td><b>" << QtVersionManager::tr("qmake:")
        << "</b></td><td>" << m_qmakeCommand << "</td></tr>";
dt's avatar
dt committed
642
    updateToolChainAndMkspec();
643
    if (m_defaultConfigIsDebug || m_defaultConfigIsDebugAndRelease) {
644
645
        str << "<tr><td><b>" << QtVersionManager::tr("Default:") << "</b></td><td>"
            << (m_defaultConfigIsDebug ? "debug" : "release");
646
        if (m_defaultConfigIsDebugAndRelease)
647
            str << " debug_and_release";
648
649
650
651
652
653
654
655
656
657
658
659
660
        str << "</td></tr>";
    } // default config.
    str << "<tr><td><b>" << QtVersionManager::tr("Version:")
        << "</b></td><td>" << qtVersionString() << "</td></tr>";
    if (hasDebuggingHelper())
        str << "<tr><td><b>" << QtVersionManager::tr("Debugging helper:")
            << "</b></td><td>" << debuggingHelperLibrary() << "</td></tr>";
    const QHash<QString,QString> vInfo = versionInfo();
    if (!vInfo.isEmpty()) {
        const QHash<QString,QString>::const_iterator vcend = vInfo.constEnd();
        for (QHash<QString,QString>::const_iterator it = vInfo.constBegin(); it != vcend; ++it)
            str << "<tr><td><pre>" << it.key() <<  "</pre></td><td>" << it.value() << "</td></tr>";
    }
Robert Loehning's avatar
Robert Loehning committed
661
    str << "</table></body></html>";
662
663
664
    return rc;
}

665
666
667
668
669
670
bool QtVersion::supportsShadowBuilds() const
{
    QSet<QString> targets = supportedTargetIds();
    // Symbian does not support shadow building
    if (targets.contains(Constants::S60_DEVICE_TARGET_ID) ||
        targets.contains(Constants::S60_EMULATOR_TARGET_ID)) {
Tobias Hunger's avatar
Tobias Hunger committed
671
        // We can not support shadow building with the ABLD system
672
673
674
675
676
        return false;
    }
    return true;
}

677
QList<ProjectExplorer::Task>
678
QtVersion::reportIssues(const QString &proFile, const QString &buildDir)
679
680
681
{
    QList<ProjectExplorer::Task> results;

682
683
684
685
    QString tmpBuildDir = buildDir;
    if (!buildDir.endsWith(QChar('/')))
        tmpBuildDir.append(QChar('/'));

Friedemann Kleint's avatar
Friedemann Kleint committed
686
687
688
689
    if (!isValid()) {
        //: %1: Reason for being invalid
        const QString msg = QCoreApplication::translate("Qt4ProjectManager::QtVersion", "The Qt version is invalid: %1").arg(invalidReason());
        results.append(ProjectExplorer::Task(ProjectExplorer::Task::Error, msg, QString(), -1,
690
                                             QLatin1String(ProjectExplorer::Constants::TASK_CATEGORY_BUILDSYSTEM)));
Friedemann Kleint's avatar
Friedemann Kleint committed
691
    }
692
693
694

    QFileInfo qmakeInfo(qmakeCommand());
    if (!qmakeInfo.exists() ||
Friedemann Kleint's avatar
Friedemann Kleint committed
695
696
697
698
699
        !qmakeInfo.isExecutable()) {
        //: %1: Path to qmake executable
        const QString msg = QCoreApplication::translate("Qt4ProjectManager::QtVersion",
                                                        "The qmake command \"%1\" was not found or is not executable.").arg(qmakeCommand());
        results.append(ProjectExplorer::Task(ProjectExplorer::Task::Error, msg, QString(), -1,
700
                                             QLatin1String(ProjectExplorer::Constants::TASK_CATEGORY_BUILDSYSTEM)));
Friedemann Kleint's avatar
Friedemann Kleint committed
701
    }
702

703
704
705
706
707
708
709
710
711
712
713
    QString sourcePath = QFileInfo(proFile).absolutePath();
    if (!sourcePath.endsWith(QChar('/')))
        sourcePath.append(QChar('/'));

    if ((tmpBuildDir.startsWith(sourcePath)) && (tmpBuildDir != sourcePath)) {
        const QString msg = QCoreApplication::translate("Qt4ProjectManager::QtVersion",
                                                        "Qmake does not support build directories below the source directory.");
        results.append(ProjectExplorer::Task(ProjectExplorer::Task::Warning, msg, QString(), -1,
                                             QLatin1String(ProjectExplorer::Constants::TASK_CATEGORY_BUILDSYSTEM)));
    } else if (tmpBuildDir.count(QChar('/')) != sourcePath.count(QChar('/'))) {
        const QString msg = QCoreApplication::translate("Qt4ProjectManager::QtVersion",
714
                                                        "The build directory needs to be at the same level as the source directory.");
715

716
717
718
719
        results.append(ProjectExplorer::Task(ProjectExplorer::Task::Warning, msg, QString(), -1,
                                             QLatin1String(ProjectExplorer::Constants::TASK_CATEGORY_BUILDSYSTEM)));
    }

720
721
722
723
724
725
726
    QSet<QString> targets = supportedTargetIds();
    if (targets.contains(Constants::S60_DEVICE_TARGET_ID) ||
        targets.contains(Constants::S60_EMULATOR_TARGET_ID))
        results.append(S60ProjectChecker::reportIssues(proFile, this));
    return results;
}

727
QString QtVersion::displayName() const
con's avatar
con committed
728
{
729
    return m_displayName;
con's avatar
con committed
730
731
}

732
QString QtVersion::qmakeCommand() const
con's avatar
con committed
733
{
734
    return m_qmakeCommand;
con's avatar
con committed
735
736
737
738
739
740
741
742
743
}

QString QtVersion::sourcePath() const
{
    return m_sourcePath;
}

QString QtVersion::mkspec() const
{
dt's avatar
dt committed
744
    updateToolChainAndMkspec();
con's avatar
con committed
745
746
747
    return m_mkspec;
}

dt's avatar
dt committed
748
749
QString QtVersion::mkspecPath() const
{
dt's avatar
dt committed
750
    updateToolChainAndMkspec();
dt's avatar
dt committed
751
752
753
    return m_mkspecFullPath;
}

754
755
756
757
758
759
bool QtVersion::isBuildWithSymbianSbsV2() const
{
    updateToolChainAndMkspec();
    return m_isBuildUsingSbsV2;
}

760
761
QString QtVersion::qtVersionString() const
{
762
763
764
765
766
767
768
769
    if (m_qtVersionString.isNull()) {
        QFileInfo qmake(m_qmakeCommand);
        if (qmake.exists() && qmake.isExecutable()) {
            m_qtVersionString = DebuggingHelperLibrary::qtVersionForQMake(qmake.absoluteFilePath());
        } else {
            m_qtVersionString = QLatin1String("");
        }
    }
770
771
772
    return m_qtVersionString;
}

con's avatar
con committed
773
774
775
776
777
778
QHash<QString,QString> QtVersion::versionInfo() const
{
    updateVersionInfo();
    return m_versionInfo;
}

779
void QtVersion::setDisplayName(const QString &name)
con's avatar
con committed
780
{
781
    m_displayName = name;
con's avatar
con committed
782
783
}

784
void QtVersion::setQMakeCommand(const QString& qmakeCommand)
con's avatar
con committed
785
{
786
    m_qmakeCommand = QDir::fromNativeSeparators(qmakeCommand);
787
#ifdef Q_OS_WIN
788
    m_qmakeCommand = m_qmakeCommand.toLower();
789
#endif
790
791
    m_designerCommand.clear();
    m_linguistCommand.clear();
792
    m_qmlviewerCommand.clear();
793
    m_uicCommand.clear();
794
    m_toolChainUpToDate = false;
795
796
    // TODO do i need to optimize this?
    m_versionInfoUpToDate = false;
797
    m_qtVersionString = QString();
798
    updateSourcePath();
con's avatar
con committed
799
800
801
802
}

void QtVersion::updateSourcePath()
{
803
804
805
806
    updateVersionInfo();
    const QString installData = m_versionInfo["QT_INSTALL_DATA"];
    m_sourcePath = installData;
    QFile qmakeCache(installData + QLatin1String("/.qmake.cache"));
con's avatar
con committed
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
    if (qmakeCache.exists()) {
        qmakeCache.open(QIODevice::ReadOnly | QIODevice::Text);
        QTextStream stream(&qmakeCache);
        while (!stream.atEnd()) {
            QString line = stream.readLine().trimmed();
            if (line.startsWith(QLatin1String("QT_SOURCE_TREE"))) {
                m_sourcePath = line.split(QLatin1Char('=')).at(1).trimmed();
                if (m_sourcePath.startsWith(QLatin1String("$$quote("))) {
                    m_sourcePath.remove(0, 8);
                    m_sourcePath.chop(1);
                }
                break;
            }
        }
    }
822
    m_sourcePath = QDir::cleanPath(m_sourcePath);
dt's avatar
dt committed
823
824
825
#ifdef Q_OS_WIN
    m_sourcePath = m_sourcePath.toLower();
#endif
con's avatar
con committed
826
827
828
829
}

// 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
830
// To find out whether we already have a qtversion for that directory call
con's avatar
con committed
831
// QtVersion *QtVersionManager::qtVersionForDirectory(const QString directory);
832
QString QtVersionManager::findQMakeBinaryFromMakefile(const QString &directory)
con's avatar
con committed
833
834
835
836
837
{
    bool debugAdding = false;
    QFile makefile(directory + "/Makefile" );
    if (makefile.exists() && makefile.open(QFile::ReadOnly)) {
        QTextStream ts(&makefile);
838
        QRegExp r1("QMAKE\\s*=(.*)");
con's avatar
con committed
839
840
841
842
843
844
        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());
845
                QString qmakePath = qmake.filePath();
846
#ifdef Q_OS_WIN
847
848
                if (!qmakePath.endsWith(QLatin1String(".exe")))
                    qmakePath.append(QLatin1String(".exe"));
849
#endif
850
                // Is qmake still installed?
851
852
853
854
855
856
                QFileInfo fi(qmakePath);
                if (fi.exists()) {
                    qmakePath = fi.absoluteFilePath();
#ifdef Q_OS_WIN
                    qmakePath = qmakePath.toLower();
#endif
857
                    return qmakePath;
858
                }
con's avatar
con committed
859
860
861
            }
        }
    }
862
    return QString();
con's avatar
con committed
863
864
}

865
QtVersion *QtVersionManager::qtVersionForQMakeBinary(const QString &qmakePath)
con's avatar
con committed
866
867
{
   foreach(QtVersion *v, versions()) {
Tobias Hunger's avatar
Tobias Hunger committed
868
869
870
871
872
       if (v->qmakeCommand() == qmakePath) {
           return v;
           break;
       }
   }
con's avatar
con committed
873
874
875
   return 0;
}

876
void dumpQMakeAssignments(const QList<QMakeAssignment> &list)
con's avatar
con committed
877
{
878
    foreach(const QMakeAssignment &qa, list) {
879
880
881
882
        qDebug()<<qa.variable<<qa.op<<qa.value;
    }
}

883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
bool QtVersionManager::makefileIsFor(const QString &directory, const QString &proFile)
{
    if (proFile.isEmpty())
        return true;

    QString line = findQMakeLine(directory, QLatin1String("# Project:")).trimmed();
    if (line.isEmpty())
        return false;


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

    QFileInfo srcFileInfo(QDir(directory), line);
    QFileInfo proFileInfo(proFile);
    return srcFileInfo == proFileInfo;
}

901
QPair<QtVersion::QmakeBuildConfigs, QStringList> QtVersionManager::scanMakeFile(const QString &directory, QtVersion::QmakeBuildConfigs defaultBuildConfig)
902
{
903
904
    if (debug)
        qDebug()<<"ScanMakeFile, the gory details:";
905
    QtVersion::QmakeBuildConfigs result = defaultBuildConfig;
906
907
    QStringList result2;

908
    QString line = findQMakeLine(directory, QLatin1String("# Command:"));
909
    if (!line.isEmpty()) {
910
911
        if (debug)
            qDebug()<<"Found line"<<line;
912
913
        line = trimLine(line);
        QStringList parts = splitLine(line);
914
        if (debug)
Tobias Hunger's avatar
Tobias Hunger committed
915
            qDebug()<<"Split into"<<parts;
916
917
918
919
920
        QList<QMakeAssignment> assignments;
        QList<QMakeAssignment> afterAssignments;
        QStringList additionalArguments;
        parseParts(parts, &assignments, &afterAssignments, &additionalArguments);

921
922
923
924
925
926
        if (debug) {
            dumpQMakeAssignments(assignments);
            if (!afterAssignments.isEmpty())
                qDebug()<<"-after";
            dumpQMakeAssignments(afterAssignments);
        }
927
928
929
930
931

        // 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
932
933
        if (debug)
            dumpQMakeAssignments(assignments);
934
935

        result2.append(additionalArguments);
936
        foreach(const QMakeAssignment &qa, assignments)
937
938
939
            result2.append(qa.variable + qa.op + qa.value);
        if (!afterAssignments.isEmpty()) {
            result2.append("-after");
940
            foreach(const QMakeAssignment &qa, afterAssignments)
941
942
943
944
945
                result2.append(qa.variable + qa.op + qa.value);
        }
    }

    // Dump the gathered information:
946
947
948
949
950
951
952
953
954
955
    if (debug) {
        qDebug()<<"\n\nDumping information from scanMakeFile";
        qDebug()<<"QMake CONFIG variable parsing";
        qDebug()<<"  "<< (result & QtVersion::NoBuild ? "No Build" : QString::number(int(result)));
        qDebug()<<"  "<< (result & QtVersion::DebugBuild ? "debug" : "release");
        qDebug()<<"  "<< (result & QtVersion::BuildAll ? "debug_and_release" : "no debug_and_release");
        qDebug()<<"\nAddtional Arguments";
        qDebug()<<result2;
        qDebug()<<"\n\n";
    }
956
957
958
    return qMakePair(result, result2);
}

959
QString QtVersionManager::findQMakeLine(const QString &directory, const QString &key)
960
{
961
    QFile makefile(directory + QLatin1String("/Makefile" ));
con's avatar
con committed
962
963
964
    if (makefile.exists() && makefile.open(QFile::ReadOnly)) {
        QTextStream ts(&makefile);
        while (!ts.atEnd()) {
965
            const QString line = ts.readLine();
966
            if (line.startsWith(key))
967
968
969
970
971
972
973
974
975
976
977
                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
978
    const int firstSpace = line.indexOf(QLatin1Char(' '), 11);
979
980
981
982
983
984
    return line.mid(firstSpace).trimmed();
}

QStringList QtVersionManager::splitLine(const QString &line)
{
    // Split on each " ", except on those which are escaped
985
986
    // On Unix also remove all escaping
    // On Windows also, but different escaping
987
988
989
990
991
    bool escape = false;
    QString currentWord;
    QStringList results;
    int length = line.length();
    for (int i=0; i<length; ++i) {
992
993
994
995
996
#ifdef Q_OS_WIN
        if (line.at(i) == '"') {
            escape = !escape;
        } else if (escape || line.at(i) != ' ') {
            currentWord += line.at(i);
997
        } else if (!currentWord.isEmpty()) {
998
            results << currentWord;
999
            currentWord.clear();
1000
1001
        }
#else
1002
1003
1004
1005
        if (escape) {
            currentWord += line.at(i);
            escape = false;
        } else if (line.at(i) == ' ') {
con's avatar
con committed
1006
            if (!currentWord.isEmpty()) {
1007
1008
1009
                results << currentWord;
                currentWord.clear();
            }
1010
1011
1012
1013
1014
        } else if (line.at(i) == '\\') {
            escape = true;
        } else {
            currentWord += line.at(i);
        }
1015
#endif
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
    }
    return results;
}

void QtVersionManager::parseParts(const QStringList &parts, QList<QMakeAssignment> *assignments, QList<QMakeAssignment> *afterAssignments, QStringList *additionalArguments)
{
    QRegExp regExp("([^\\s\\+-]*)\\s*(\\+=|=|-=|~=)(.*)");
    bool after = false;
    bool ignoreNext = false;
    foreach (const QString &part, parts) {
        if (ignoreNext) {
            // Ignoring
            ignoreNext = false;
1029
        } else if (part == "-after") {
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
            after = true;
        } else if(part.contains('=')) {
            if (regExp.exactMatch(part)) {
                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";
            }
        } else if (part == "-o") {
            ignoreNext = true;
        } else {
            additionalArguments->append(part);
        }
    }
#if defined(Q_OS_WIN32)
    additionalArguments->removeAll("-win32");
#elif defined(Q_OS_MAC)
    additionalArguments->removeAll("-macx");
#elif defined(Q_OS_QNX6)
    additionalArguments->removeAll("-qnx6");
#else
    additionalArguments->removeAll("-unix");
#endif
}

/// This function extracts all the CONFIG+=debug, CONFIG+=release
1062
QtVersion::QmakeBuildConfigs QtVersionManager::qmakeBuildConfigFromCmdArgs(QList<QMakeAssignment> *assignments, QtVersion::QmakeBuildConfigs defaultBuildConfig)
1063
{
1064
    QtVersion::QmakeBuildConfigs result = defaultBuildConfig;
1065
1066
    QList<QMakeAssignment> oldAssignments = *assignments;
    assignments->clear();
1067
    foreach(const QMakeAssignment &qa, oldAssignments) {