qtversionmanager.cpp 61.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
**
9
** Commercial Usage
10
**
11
12
13
14
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
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
**
25
** If you are unsure which license is appropriate for your use, please
hjk's avatar
hjk committed
26
** contact the sales department at http://qt.nokia.com/contact.
con's avatar
con committed
27
**
28
**************************************************************************/
hjk's avatar
hjk committed
29

con's avatar
con committed
30
#include "qtversionmanager.h"
hjk's avatar
hjk committed
31

32
#include "qt4projectmanagerconstants.h"
Tobias Hunger's avatar
Tobias Hunger committed
33
#include "qt4target.h"
34
#include "profilereader.h"
con's avatar
con committed
35

ck's avatar
ck committed
36
#include "qt-maemo/maemomanager.h"
37
#include "qt-s60/s60manager.h"
38
#include "qt-s60/s60projectchecker.h"
39

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

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

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

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

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

76
77
enum { debug = 0 };

78
QtVersionManager *QtVersionManager::m_self = 0;
79

con's avatar
con committed
80
81
82
QtVersionManager::QtVersionManager()
    : m_emptyVersion(new QtVersion)
{
83
    m_self = this;
84
    QSettings *s = Core::ICore::instance()->settings();
con's avatar
con committed
85
86
87
88
89
90
91
92
93
94
95
96

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

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

    writeVersionsIntoSettings();
144

con's avatar
con committed
145
    updateDocumentation();
146

147
148
    // cannot call from ctor, needs to get connected extenernally first
    QTimer::singleShot(0, this, SLOT(updateExamples()));
con's avatar
con committed
149
150
151
152
153
154
155
156
157
158
}

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

159
QtVersionManager *QtVersionManager::instance()
160
{
161
    return m_self;
162
163
}

con's avatar
con committed
164
165
void QtVersionManager::addVersion(QtVersion *version)
{
166
    QTC_ASSERT(version != 0, return);
167
168
169
    if (m_versions.contains(version))
        return;

con's avatar
con committed
170
    m_versions.append(version);
171
172
173
    int uniqueId = version->uniqueId();
    m_uniqueIdToIndex.insert(uniqueId, m_versions.count() - 1);
    emit qtVersionsChanged(QList<int>() << uniqueId);
174
    writeVersionsIntoSettings();
con's avatar
con committed
175
176
}

177
178
void QtVersionManager::removeVersion(QtVersion *version)
{
179
    QTC_ASSERT(version != 0, return);
180
    m_versions.removeAll(version);
181
182
183
    int uniqueId = version->uniqueId();
    m_uniqueIdToIndex.remove(uniqueId);
    emit qtVersionsChanged(QList<int>() << uniqueId);
184
185
186
187
    writeVersionsIntoSettings();
    delete version;
}

Tobias Hunger's avatar
Tobias Hunger committed
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
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
215
216
void QtVersionManager::updateDocumentation()
{
kh1's avatar
kh1 committed
217
    Core::HelpManager *helpManager = Core::HelpManager::instance();
dt's avatar
dt committed
218
    Q_ASSERT(helpManager);
con's avatar
con committed
219
220
    QStringList files;
    foreach (QtVersion *version, m_versions) {
221
222
223
224
225
226
        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
227
228
229
230
    }
    helpManager->registerDocumentation(files);
}

231
232
233
void QtVersionManager::updateExamples()
{
    QtVersion *version = 0;
234
235
    QList<QtVersion*> candidates;

Tobias Hunger's avatar
Tobias Hunger committed
236
    // try to find a version which has both, demos and examples
237
238
239
240
241
    foreach (version, m_versions) {
        if (version->hasExamples() && version->hasDemos())
        candidates.append(version);
    }

242
243
244
245
246
247
248
249
250
    // in SDKs, we want to prefer the Qt version shipping with the SDK
    QString preferred = Core::ICore::instance()->settings()->value("General/PreferredQMakePath").toString();
    if (!preferred.isEmpty()) {
        foreach (version, candidates) {
            if (version->qmakeCommand() == preferred) {
                emit updateExamples(version->examplesPath(), version->demosPath(), version->sourcePath());
                return;
            }
        }
251
252
253
254
255
256
    }

    // prefer versions with declarative examples
    foreach (version, candidates) {
        if (QDir(version->examplesPath()+"/declarative").exists()) {
            emit updateExamples(version->examplesPath(), version->demosPath(), version->sourcePath());
257
258
259
            return;
        }
    }
260
261
262
263
264
265
266
267

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

268
269
}

con's avatar
con committed
270
271
272
273
274
275
276
277
int QtVersionManager::getUniqueId()
{
    return m_idcount++;
}

void QtVersionManager::updateUniqueIdToIndexMap()
{
    m_uniqueIdToIndex.clear();
hjk's avatar
hjk committed
278
    for (int i = 0; i < m_versions.size(); ++i)
con's avatar
con committed
279
280
281
282
283
        m_uniqueIdToIndex.insert(m_versions.at(i)->uniqueId(), i);
}

void QtVersionManager::writeVersionsIntoSettings()
{
284
    QSettings *s = Core::ICore::instance()->settings();
285
    s->beginWriteArray(QtVersionsSectionName);
con's avatar
con committed
286
    for (int i = 0; i < m_versions.size(); ++i) {
287
        const QtVersion *version = m_versions.at(i);
con's avatar
con committed
288
        s->setArrayIndex(i);
289
        s->setValue("Name", version->displayName());
290
291
292
        // for downwards compat
        s->setValue("Path", version->versionInfo().value("QT_INSTALL_DATA"));
        s->setValue("QMakePath", version->qmakeCommand());
293
294
295
296
297
298
        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
299
        s->setValue("MwcDirectory", version->mwcDirectory());
300
        s->setValue("S60SDKDirectory", version->s60SDKDirectory());
con's avatar
con committed
301
        s->setValue("GcceDirectory", version->gcceDirectory());
con's avatar
con committed
302
303
304
305
    }
    s->endArray();
}

306
QList<QtVersion *> QtVersionManager::versions() const
con's avatar
con committed
307
308
309
310
{
    return m_versions;
}

311
312
313
314
315
316
317
318
319
320
QList<QtVersion *> QtVersionManager::validVersions() const
{
    QList<QtVersion *> results;
    foreach(QtVersion *v, m_versions) {
        if (v->isValid())
            results.append(v);
    }
    return results;
}

321
322
323
324
325
326
bool QtVersionManager::isValidId(int id) const
{
    int pos = m_uniqueIdToIndex.value(id, -1);
    return (pos != -1);
}

con's avatar
con committed
327
328
329
330
331
332
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
333
    return m_emptyVersion;
con's avatar
con committed
334
335
336
337
338
}

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

347
348
349
350
    QDateTime lastUpdateFromGlobalSettings = globalSettings->value(
            QLatin1String("General/LastQtVersionUpdate")).toDateTime();

    const QFileInfo gsFi(globalSettings->fileName());
351
352
    if ( !lastUpdateFromGlobalSettings.isNull() &&
         (!gsFi.exists() || (gsFi.lastModified() > lastUpdateFromGlobalSettings)) )
con's avatar
con committed
353
354
        return;

355
356
357
358
359
    if (!globalSettings->contains(newQtVersionsKey) &&
        !globalSettings->contains(QLatin1String("Installer/")+newQtVersionsKey))
    {
        return;
    }
con's avatar
con committed
360
361

    QString newVersionsValue = settings->value(newQtVersionsKey).toString();
362
363
364
    if (newVersionsValue.isEmpty())
        newVersionsValue = settings->value(QLatin1String("Installer/")+newQtVersionsKey).toString();

con's avatar
con committed
365
    QStringList newVersionsList = newVersionsValue.split(';', QString::SkipEmptyParts);
366
    foreach (const QString &newVersion, newVersionsList) {
con's avatar
con committed
367
        QStringList newVersionData = newVersion.split('=');
368
        if (newVersionData.count() >= 2) {
369
            if (QFile::exists(newVersionData[1])) {
con's avatar
con committed
370
                QtVersion *version = new QtVersion(newVersionData[0], newVersionData[1], m_idcount++ );
hjk's avatar
hjk committed
371
                if (newVersionData.count() >= 3)
con's avatar
con committed
372
                    version->setMingwDirectory(newVersionData[2]);
373
                if (newVersionData.count() >= 4)
374
                    version->setS60SDKDirectory(QDir::fromNativeSeparators(newVersionData[3]));
375
                if (newVersionData.count() >= 5)
376
                    version->setGcceDirectory(QDir::fromNativeSeparators(newVersionData[4]));
377
                if (newVersionData.count() >= 6)
378
                    version->setMwcDirectory(QDir::fromNativeSeparators(newVersionData[5]));
379
380
                if (newVersionData.count() >= 7)
                    version->setMsvcVersion(newVersionData[6]);
con's avatar
con committed
381
382
383

                bool versionWasAlreadyInList = false;
                foreach(const QtVersion * const it, m_versions) {
384
                    if (QDir(version->qmakeCommand()).canonicalPath() == QDir(it->qmakeCommand()).canonicalPath()) {
con's avatar
con committed
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
                        versionWasAlreadyInList = true;
                        break;
                    }
                }

                if (!versionWasAlreadyInList) {
                    m_versions.append(version);
                } else {
                    // clean up
                    delete version;
                }
            }
        }
    }
    updateUniqueIdToIndexMap();
400
    settings->setValue(QLatin1String("General/LastQtVersionUpdate"), QDateTime::currentDateTime());
con's avatar
con committed
401
402
403
404
405
}

void QtVersionManager::updateSystemVersion()
{
    bool haveSystemVersion = false;
406
    QString systemQMakePath = DebuggingHelperLibrary::findSystemQt(Utils::Environment::systemEnvironment());
407
408
    if (systemQMakePath.isNull())
        systemQMakePath = tr("<not found>");
409

con's avatar
con committed
410
    foreach (QtVersion *version, m_versions) {
411
412
        if (version->isAutodetected()
            && version->autodetectionSource() == PATH_AUTODETECTION_SOURCE) {
413
            version->setQMakeCommand(systemQMakePath);
414
            version->setDisplayName(tr("Qt in PATH"));
con's avatar
con committed
415
416
417
418
419
            haveSystemVersion = true;
        }
    }
    if (haveSystemVersion)
        return;
420
    QtVersion *version = new QtVersion(tr("Qt in PATH"),
421
                                       systemQMakePath,
con's avatar
con committed
422
                                       getUniqueId(),
423
424
                                       true,
                                       PATH_AUTODETECTION_SOURCE);
con's avatar
con committed
425
426
427
428
    m_versions.prepend(version);
    updateUniqueIdToIndexMap();
}

Tobias Hunger's avatar
Tobias Hunger committed
429
QtVersion *QtVersionManager::emptyVersion() const
con's avatar
con committed
430
{
Tobias Hunger's avatar
Tobias Hunger committed
431
    return m_emptyVersion;
con's avatar
con committed
432
433
}

434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
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;
453
454
    if (a->m_displayName != b->displayName())
        return false;
455
456
457
    return true;
}

Tobias Hunger's avatar
Tobias Hunger committed
458
void QtVersionManager::setNewQtVersions(QList<QtVersion *> newVersions)
459
{
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
    // 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;
492
493
        }
    }
494
495
496

    while (nit != nend) {
        changedVersions.push_back((*nit)->uniqueId());
497
        ++nit;
498
499
500
501
    }

    while (oit != oend) {
        changedVersions.push_back((*oit)->uniqueId());
502
        ++oit;
503
504
    }

505
506
    qDeleteAll(m_versions);
    m_versions.clear();
dt's avatar
dt committed
507
    m_versions = newVersions;
508
509

    if (!changedVersions.isEmpty())
510
511
512
        updateDocumentation();
    updateUniqueIdToIndexMap();

513
    updateExamples();
514
    writeVersionsIntoSettings();
515
516
517

    if (!changedVersions.isEmpty())
        emit qtVersionsChanged(changedVersions);
518
519
}

con's avatar
con committed
520
521
522
523
///
/// QtVersion
///

524
QtVersion::QtVersion(const QString &name, const QString &qmakeCommand, int id,
525
                     bool isAutodetected, const QString &autodetectionSource)
526
    : m_displayName(name),
527
    m_isAutodetected(isAutodetected),
528
    m_autodetectionSource(autodetectionSource),
529
    m_hasDebuggingHelper(false),
530
531
    m_hasQmlDump(false),
    m_hasQmlObserver(false),
532
    m_toolChainUpToDate(false),
533
    m_versionInfoUpToDate(false),
534
535
536
    m_notInstalled(false),
    m_defaultConfigIsDebug(true),
    m_defaultConfigIsDebugAndRelease(true),
537
538
    m_hasExamples(false),
    m_hasDemos(false),
539
    m_hasDocumentation(false)
con's avatar
con committed
540
{
hjk's avatar
hjk committed
541
    if (id == -1)
con's avatar
con committed
542
543
544
        m_id = getUniqueId();
    else
        m_id = id;
545
    setQMakeCommand(qmakeCommand);
con's avatar
con committed
546
547
}

548
QtVersion::QtVersion(const QString &name, const QString &qmakeCommand,
549
                     bool isAutodetected, const QString &autodetectionSource)
550
    : m_displayName(name),
551
552
    m_isAutodetected(isAutodetected),
    m_autodetectionSource(autodetectionSource),
553
    m_hasDebuggingHelper(false),
554
555
    m_hasQmlDump(false),
    m_hasQmlObserver(false),
556
    m_toolChainUpToDate(false),
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
    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),
574
575
    m_hasQmlDump(false),
    m_hasQmlObserver(false),
576
    m_toolChainUpToDate(false),
577
578
579
580
581
582
583
    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
584
585
{
    m_id = getUniqueId();
586
    setQMakeCommand(qmakeCommand);
587
    m_displayName = qtVersionString();
con's avatar
con committed
588
589
}

590
QtVersion::QtVersion()
591
    :  m_id(-1),
592
593
    m_isAutodetected(false),
    m_hasDebuggingHelper(false),
594
595
    m_hasQmlDump(false),
    m_hasQmlObserver(false),
596
    m_toolChainUpToDate(false),
597
598
599
600
601
602
603
604
    m_versionInfoUpToDate(false),
    m_notInstalled(false),
    m_defaultConfigIsDebug(true),
    m_defaultConfigIsDebugAndRelease(true),
    m_hasExamples(false),
    m_hasDemos(false),
    m_hasDocumentation(false)
{
605
    setQMakeCommand(QString());
606
607
608
}


609
610
611
612
QtVersion::~QtVersion()
{
}

613
614
615
616
QString QtVersion::toHtml() const
{
    QString rc;
    QTextStream str(&rc);
Robert Loehning's avatar
Robert Loehning committed
617
    str << "<html><body><table>";
618
    str << "<tr><td><b>" << QtVersionManager::tr("Name:")
619
        << "</b></td><td>" << displayName() << "</td></tr>";
620
621
622
623
624
625
    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
626
    updateToolChainAndMkspec();
627
    if (m_defaultConfigIsDebug || m_defaultConfigIsDebugAndRelease) {
628
629
        str << "<tr><td><b>" << QtVersionManager::tr("Default:") << "</b></td><td>"
            << (m_defaultConfigIsDebug ? "debug" : "release");
630
        if (m_defaultConfigIsDebugAndRelease)
631
            str << " debug_and_release";
632
633
634
635
636
637
638
639
640
641
642
643
644
        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
645
    str << "</table></body></html>";
646
647
648
    return rc;
}

649
650
651
652
653
654
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
655
        // We can not support shadow building with the ABLD system
656
657
658
659
660
        return false;
    }
    return true;
}

661
QList<ProjectExplorer::Task>
662
QtVersion::reportIssues(const QString &proFile, const QString &buildDir)
663
664
665
{
    QList<ProjectExplorer::Task> results;

666
667
668
669
    QString tmpBuildDir = buildDir;
    if (!buildDir.endsWith(QChar('/')))
        tmpBuildDir.append(QChar('/'));

Friedemann Kleint's avatar
Friedemann Kleint committed
670
671
672
673
    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,
674
                                             QLatin1String(ProjectExplorer::Constants::TASK_CATEGORY_BUILDSYSTEM)));
Friedemann Kleint's avatar
Friedemann Kleint committed
675
    }
676
677
678

    QFileInfo qmakeInfo(qmakeCommand());
    if (!qmakeInfo.exists() ||
Friedemann Kleint's avatar
Friedemann Kleint committed
679
680
681
682
683
        !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,
684
                                             QLatin1String(ProjectExplorer::Constants::TASK_CATEGORY_BUILDSYSTEM)));
Friedemann Kleint's avatar
Friedemann Kleint committed
685
    }
686

687
688
689
690
691
692
693
694
695
696
697
    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",
698
                                                        "The build directory needs to be at the same level as the source directory.");
699

700
701
702
703
        results.append(ProjectExplorer::Task(ProjectExplorer::Task::Warning, msg, QString(), -1,
                                             QLatin1String(ProjectExplorer::Constants::TASK_CATEGORY_BUILDSYSTEM)));
    }

704
705
706
707
708
709
710
    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;
}

711
QString QtVersion::displayName() const
con's avatar
con committed
712
{
713
    return m_displayName;
con's avatar
con committed
714
715
}

716
QString QtVersion::qmakeCommand() const
con's avatar
con committed
717
{
718
    return m_qmakeCommand;
con's avatar
con committed
719
720
721
722
723
724
725
726
727
}

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

QString QtVersion::mkspec() const
{
dt's avatar
dt committed
728
    updateToolChainAndMkspec();
con's avatar
con committed
729
730
731
    return m_mkspec;
}

dt's avatar
dt committed
732
733
QString QtVersion::mkspecPath() const
{
dt's avatar
dt committed
734
    updateToolChainAndMkspec();
dt's avatar
dt committed
735
736
737
    return m_mkspecFullPath;
}

738
739
740
741
742
743
bool QtVersion::isBuildWithSymbianSbsV2() const
{
    updateToolChainAndMkspec();
    return m_isBuildUsingSbsV2;
}

744
745
QString QtVersion::qtVersionString() const
{
746
747
748
749
750
751
752
753
    if (m_qtVersionString.isNull()) {
        QFileInfo qmake(m_qmakeCommand);
        if (qmake.exists() && qmake.isExecutable()) {
            m_qtVersionString = DebuggingHelperLibrary::qtVersionForQMake(qmake.absoluteFilePath());
        } else {
            m_qtVersionString = QLatin1String("");
        }
    }
754
755
756
    return m_qtVersionString;
}

con's avatar
con committed
757
758
759
760
761
762
QHash<QString,QString> QtVersion::versionInfo() const
{
    updateVersionInfo();
    return m_versionInfo;
}

763
void QtVersion::setDisplayName(const QString &name)
con's avatar
con committed
764
{
765
    m_displayName = name;
con's avatar
con committed
766
767
}

768
void QtVersion::setQMakeCommand(const QString& qmakeCommand)
con's avatar
con committed
769
{
770
    m_qmakeCommand = QDir::fromNativeSeparators(qmakeCommand);
771
#ifdef Q_OS_WIN
772
    m_qmakeCommand = m_qmakeCommand.toLower();
773
#endif
774
775
    m_designerCommand.clear();
    m_linguistCommand.clear();
776
    m_qmlviewerCommand.clear();
777
    m_uicCommand.clear();
778
    m_toolChainUpToDate = false;
779
780
    // TODO do i need to optimize this?
    m_versionInfoUpToDate = false;
781
    m_qtVersionString = QString();
782
    updateSourcePath();
con's avatar
con committed
783
784
785
786
}

void QtVersion::updateSourcePath()
{
787
788
789
790
    updateVersionInfo();
    const QString installData = m_versionInfo["QT_INSTALL_DATA"];
    m_sourcePath = installData;
    QFile qmakeCache(installData + QLatin1String("/.qmake.cache"));
con's avatar
con committed
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
    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;
            }
        }
    }
806
    m_sourcePath = QDir::cleanPath(m_sourcePath);
dt's avatar
dt committed
807
808
809
#ifdef Q_OS_WIN
    m_sourcePath = m_sourcePath.toLower();
#endif
con's avatar
con committed
810
811
812
813
}

// 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
814
// To find out whether we already have a qtversion for that directory call
con's avatar
con committed
815
// QtVersion *QtVersionManager::qtVersionForDirectory(const QString directory);
816
QString QtVersionManager::findQMakeBinaryFromMakefile(const QString &makefile)
con's avatar
con committed
817
818
{
    bool debugAdding = false;
819
820
821
    QFile fi(makefile);
    if (fi.exists() && fi.open(QFile::ReadOnly)) {
        QTextStream ts(&fi);
822
        QRegExp r1("QMAKE\\s*=(.*)");
con's avatar
con committed
823
824
825
826
827
828
        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());
829
                QString qmakePath = qmake.filePath();
830
#ifdef Q_OS_WIN
831
832
                if (!qmakePath.endsWith(QLatin1String(".exe")))
                    qmakePath.append(QLatin1String(".exe"));
833
#endif
834
                // Is qmake still installed?
835
836
837
838
839
840
                QFileInfo fi(qmakePath);
                if (fi.exists()) {
                    qmakePath = fi.absoluteFilePath();
#ifdef Q_OS_WIN
                    qmakePath = qmakePath.toLower();
#endif
841
                    return qmakePath;
842
                }
con's avatar
con committed
843
844
845
            }
        }
    }
846
    return QString();
con's avatar
con committed
847
848
}

849
QtVersion *QtVersionManager::qtVersionForQMakeBinary(const QString &qmakePath)
con's avatar
con committed
850
851
{
   foreach(QtVersion *v, versions()) {
Tobias Hunger's avatar
Tobias Hunger committed
852
853
854
855
856
       if (v->qmakeCommand() == qmakePath) {
           return v;
           break;
       }
   }
con's avatar
con committed
857
858
859
   return 0;
}

860
void dumpQMakeAssignments(const QList<QMakeAssignment> &list)
con's avatar
con committed
861
{
862
    foreach(const QMakeAssignment &qa, list) {
863
864
865
866
        qDebug()<<qa.variable<<qa.op<<qa.value;
    }
}

867
bool QtVersionManager::makefileIsFor(const QString &makefile, const QString &proFile)
868
869
870
871
{
    if (proFile.isEmpty())
        return true;

872
    QString line = findQMakeLine(makefile, QLatin1String("# Project:")).trimmed();
873
874
875
876
877
878
    if (line.isEmpty())
        return false;

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

879
    QFileInfo srcFileInfo(QFileInfo(makefile).absoluteDir(), line);
880
881
882
883
    QFileInfo proFileInfo(proFile);
    return srcFileInfo == proFileInfo;
}

884
QPair<QtVersion::QmakeBuildConfigs, QStringList> QtVersionManager::scanMakeFile(const QString &makefile, QtVersion::QmakeBuildConfigs defaultBuildConfig)
885
{
886
887
    if (debug)
        qDebug()<<"ScanMakeFile, the gory details:";
888
    QtVersion::QmakeBuildConfigs result = defaultBuildConfig;
889
890
    QStringList result2;

891
    QString line = findQMakeLine(makefile, QLatin1String("# Command:"));
892
    if (!line.isEmpty()) {
893
894
        if (debug)
            qDebug()<<"Found line"<<line;
895
896
        line = trimLine(line);
        QStringList parts = splitLine(line);
897
        if (debug)
Tobias Hunger's avatar
Tobias Hunger committed
898
            qDebug()<<"Split into"<<parts;
899
900
901
902
903
        QList<QMakeAssignment> assignments;
        QList<QMakeAssignment> afterAssignments;
        QStringList additionalArguments;
        parseParts(parts, &assignments, &afterAssignments, &additionalArguments);

904
905
906
907
908
909
        if (debug) {
            dumpQMakeAssignments(assignments);
            if (!afterAssignments.isEmpty())
                qDebug()<<"-after";
            dumpQMakeAssignments(afterAssignments);
        }
910
911
912
913
914

        // 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
915
916
        if (debug)
            dumpQMakeAssignments(assignments);
917
918

        result2.append(additionalArguments);
919
        foreach(const QMakeAssignment &qa, assignments)
920
921
922
            result2.append(qa.variable + qa.op + qa.value);
        if (!afterAssignments.isEmpty()) {
            result2.append("-after");
923
            foreach(const QMakeAssignment &qa, afterAssignments)
924
925
926
927
928
                result2.append(qa.variable + qa.op + qa.value);
        }
    }

    // Dump the gathered information:
929
930
931
932
933
934
935
936
937
938
    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";
    }
939
940
941
    return qMakePair(result, result2);
}

942
QString QtVersionManager::findQMakeLine(const QString &makefile, const QString &key)
943
{
944
945
946
    QFile fi(makefile);
    if (fi.exists() && fi.open(QFile::ReadOnly)) {
        QTextStream ts(&fi);
con's avatar
con committed
947
        while (!ts.atEnd()) {
948
            const QString line = ts.readLine();
949
            if (line.startsWith(key))
950
951
952
953
954
955
956
957
958
959
960
                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
961
    const int firstSpace = line.indexOf(QLatin1Char(' '), 11);
962
963
964
965
966
967
    return line.mid(firstSpace).trimmed();
}

QStringList QtVersionManager::splitLine(const QString &line)
{
    // Split on each " ", except on those which are escaped
968
969
    // On Unix also remove all escaping
    // On Windows also, but different escaping
970
971
972
973
974
    bool escape = false;
    QString currentWord;
    QStringList results;
    int length = line.length();
    for (int i=0; i<length; ++i) {
975
976
977
978
979
980
981
982
983
984
#ifdef Q_OS_WIN
        if (line.at(i) == '"') {
            escape = !escape;
        } else if (escape || line.at(i) != ' ') {
            currentWord += line.at(i);
        } else {
            results << currentWord;
            currentWord.clear();;
        }
#else
985
986
987
988
989
990
991
992
993
994
995
        if (escape) {
            currentWord += line.at(i);
            escape = false;
        } else if (line.at(i) == ' ') {
            results << currentWord;
            currentWord.clear();
        } else if (line.at(i) == '\\') {
            escape = true;
        } else {
            currentWord += line.at(i);
        }
996
#endif
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
    }
    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;
1010
        } else if (part == "-after") {
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
            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
1043
QtVersion::QmakeBuildConfigs QtVersionManager::qmakeBuildConfigFromCmdArgs(QList<QMakeAssignment> *assignments, QtVersion::QmakeBuildConfigs defaultBuildConfig)
1044
{
1045
    QtVersion::QmakeBuildConfigs result = defaultBuildConfig;
1046
1047
    QList<QMakeAssignment> oldAssignments = *assignments;
    assignments->clear();
1048
    foreach(const QMakeAssignment &qa, oldAssignments) {
1049
1050
1051
1052
1053
1054
        if (qa.variable == "CONFIG") {
            QStringList values = qa.value.split(' ');
            QStringList newValues;
            foreach(const QString &value, values) {
                if (value == "debug") {
                    if (qa.op == "+=")
1055
                        result = result  | QtVersion::DebugBuild;
1056
                    else
1057
                        result = result  & ~QtVersion::DebugBuild;
1058
1059
                } else if (value == "release") {
                    if (qa.op == "+=")
1060
                        result = result & ~QtVersion::DebugBuild;
1061
                    else
1062
                        result = result | QtVersion::DebugBuild;
1063