qtversionmanager.cpp 61.2 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>
con's avatar
con committed
53

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

64
65
66
67
#ifdef Q_OS_WIN32
#include <windows.h>
#endif

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

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

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

77
78
enum { debug = 0 };

79
QtVersionManager *QtVersionManager::m_self = 0;
80

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

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

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

    writeVersionsIntoSettings();
145

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

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

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

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

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

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

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

Tobias Hunger's avatar
Tobias Hunger committed
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
215
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
216
217
void QtVersionManager::updateDocumentation()
{
kh1's avatar
kh1 committed
218
    Core::HelpManager *helpManager = Core::HelpManager::instance();
dt's avatar
dt committed
219
    Q_ASSERT(helpManager);
con's avatar
con committed
220
221
    QStringList files;
    foreach (QtVersion *version, m_versions) {
222
223
224
225
226
227
        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
228
229
230
231
    }
    helpManager->registerDocumentation(files);
}

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

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

    // prefer versions with declarative examples
    foreach (version, candidates) {
        if (QDir(version->examplesPath()+"/declarative").exists()) {
            emit updateExamples(version->examplesPath(), version->demosPath(), version->sourcePath());
247
248
249
            return;
        }
    }
250
251
252
253
254
255
256
257

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

258
259
}

con's avatar
con committed
260
261
262
263
264
265
266
267
int QtVersionManager::getUniqueId()
{
    return m_idcount++;
}

void QtVersionManager::updateUniqueIdToIndexMap()
{
    m_uniqueIdToIndex.clear();
hjk's avatar
hjk committed
268
    for (int i = 0; i < m_versions.size(); ++i)
con's avatar
con committed
269
270
271
272
273
        m_uniqueIdToIndex.insert(m_versions.at(i)->uniqueId(), i);
}

void QtVersionManager::writeVersionsIntoSettings()
{
274
    QSettings *s = Core::ICore::instance()->settings();
275
    s->beginWriteArray(QtVersionsSectionName);
con's avatar
con committed
276
    for (int i = 0; i < m_versions.size(); ++i) {
277
        const QtVersion *version = m_versions.at(i);
con's avatar
con committed
278
        s->setArrayIndex(i);
279
        s->setValue("Name", version->displayName());
280
281
282
        // for downwards compat
        s->setValue("Path", version->versionInfo().value("QT_INSTALL_DATA"));
        s->setValue("QMakePath", version->qmakeCommand());
283
284
285
286
287
288
        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
289
        s->setValue("MwcDirectory", version->mwcDirectory());
290
        s->setValue("S60SDKDirectory", version->s60SDKDirectory());
con's avatar
con committed
291
        s->setValue("GcceDirectory", version->gcceDirectory());
con's avatar
con committed
292
293
294
295
    }
    s->endArray();
}

296
QList<QtVersion *> QtVersionManager::versions() const
con's avatar
con committed
297
298
299
300
{
    return m_versions;
}

301
302
303
304
305
306
307
308
309
310
QList<QtVersion *> QtVersionManager::validVersions() const
{
    QList<QtVersion *> results;
    foreach(QtVersion *v, m_versions) {
        if (v->isValid())
            results.append(v);
    }
    return results;
}

311
312
313
314
315
316
bool QtVersionManager::isValidId(int id) const
{
    int pos = m_uniqueIdToIndex.value(id, -1);
    return (pos != -1);
}

con's avatar
con committed
317
318
319
320
321
322
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
323
    return m_emptyVersion;
con's avatar
con committed
324
325
326
327
328
}

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

337
338
339
340
    QDateTime lastUpdateFromGlobalSettings = globalSettings->value(
            QLatin1String("General/LastQtVersionUpdate")).toDateTime();

    const QFileInfo gsFi(globalSettings->fileName());
341
342
    if ( !lastUpdateFromGlobalSettings.isNull() &&
         (!gsFi.exists() || (gsFi.lastModified() > lastUpdateFromGlobalSettings)) )
con's avatar
con committed
343
344
        return;

345
346
347
348
349
    if (!globalSettings->contains(newQtVersionsKey) &&
        !globalSettings->contains(QLatin1String("Installer/")+newQtVersionsKey))
    {
        return;
    }
con's avatar
con committed
350
351

    QString newVersionsValue = settings->value(newQtVersionsKey).toString();
352
353
354
    if (newVersionsValue.isEmpty())
        newVersionsValue = settings->value(QLatin1String("Installer/")+newQtVersionsKey).toString();

con's avatar
con committed
355
    QStringList newVersionsList = newVersionsValue.split(';', QString::SkipEmptyParts);
356
    foreach (const QString &newVersion, newVersionsList) {
con's avatar
con committed
357
        QStringList newVersionData = newVersion.split('=');
358
        if (newVersionData.count() >= 2) {
359
            if (QFile::exists(newVersionData[1])) {
con's avatar
con committed
360
                QtVersion *version = new QtVersion(newVersionData[0], newVersionData[1], m_idcount++ );
hjk's avatar
hjk committed
361
                if (newVersionData.count() >= 3)
con's avatar
con committed
362
                    version->setMingwDirectory(newVersionData[2]);
363
                if (newVersionData.count() >= 4)
364
                    version->setS60SDKDirectory(QDir::fromNativeSeparators(newVersionData[3]));
365
                if (newVersionData.count() >= 5)
366
                    version->setGcceDirectory(QDir::fromNativeSeparators(newVersionData[4]));
367
                if (newVersionData.count() >= 6)
368
                    version->setMwcDirectory(QDir::fromNativeSeparators(newVersionData[5]));
369
370
                if (newVersionData.count() >= 7)
                    version->setMsvcVersion(newVersionData[6]);
con's avatar
con committed
371
372
373

                bool versionWasAlreadyInList = false;
                foreach(const QtVersion * const it, m_versions) {
374
                    if (QDir(version->qmakeCommand()).canonicalPath() == QDir(it->qmakeCommand()).canonicalPath()) {
con's avatar
con committed
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
                        versionWasAlreadyInList = true;
                        break;
                    }
                }

                if (!versionWasAlreadyInList) {
                    m_versions.append(version);
                } else {
                    // clean up
                    delete version;
                }
            }
        }
    }
    updateUniqueIdToIndexMap();
390
    settings->setValue(QLatin1String("General/LastQtVersionUpdate"), QDateTime::currentDateTime());
con's avatar
con committed
391
392
393
394
395
}

void QtVersionManager::updateSystemVersion()
{
    bool haveSystemVersion = false;
396
    QString systemQMakePath = DebuggingHelperLibrary::findSystemQt(Utils::Environment::systemEnvironment());
397
398
    if (systemQMakePath.isNull())
        systemQMakePath = tr("<not found>");
399

con's avatar
con committed
400
    foreach (QtVersion *version, m_versions) {
401
402
        if (version->isAutodetected()
            && version->autodetectionSource() == PATH_AUTODETECTION_SOURCE) {
403
            version->setQMakeCommand(systemQMakePath);
404
            version->setDisplayName(tr("Qt in PATH"));
con's avatar
con committed
405
406
407
408
409
            haveSystemVersion = true;
        }
    }
    if (haveSystemVersion)
        return;
410
    QtVersion *version = new QtVersion(tr("Qt in PATH"),
411
                                       systemQMakePath,
con's avatar
con committed
412
                                       getUniqueId(),
413
414
                                       true,
                                       PATH_AUTODETECTION_SOURCE);
con's avatar
con committed
415
416
417
418
    m_versions.prepend(version);
    updateUniqueIdToIndexMap();
}

Tobias Hunger's avatar
Tobias Hunger committed
419
QtVersion *QtVersionManager::emptyVersion() const
con's avatar
con committed
420
{
Tobias Hunger's avatar
Tobias Hunger committed
421
    return m_emptyVersion;
con's avatar
con committed
422
423
}

424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
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;
443
444
    if (a->m_displayName != b->displayName())
        return false;
445
446
447
    return true;
}

Tobias Hunger's avatar
Tobias Hunger committed
448
void QtVersionManager::setNewQtVersions(QList<QtVersion *> newVersions)
449
{
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
    // 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;
482
483
        }
    }
484
485
486

    while (nit != nend) {
        changedVersions.push_back((*nit)->uniqueId());
487
        ++nit;
488
489
490
491
    }

    while (oit != oend) {
        changedVersions.push_back((*oit)->uniqueId());
492
        ++oit;
493
494
    }

495
496
    qDeleteAll(m_versions);
    m_versions.clear();
dt's avatar
dt committed
497
    m_versions = newVersions;
498
499

    if (!changedVersions.isEmpty())
500
501
502
        updateDocumentation();
    updateUniqueIdToIndexMap();

503
    updateExamples();
504
    writeVersionsIntoSettings();
505
506
507

    if (!changedVersions.isEmpty())
        emit qtVersionsChanged(changedVersions);
508
509
}

con's avatar
con committed
510
511
512
513
///
/// QtVersion
///

514
QtVersion::QtVersion(const QString &name, const QString &qmakeCommand, int id,
515
                     bool isAutodetected, const QString &autodetectionSource)
516
    : m_displayName(name),
517
    m_isAutodetected(isAutodetected),
518
    m_autodetectionSource(autodetectionSource),
519
    m_hasDebuggingHelper(false),
520
521
    m_hasQmlDump(false),
    m_hasQmlObserver(false),
522
    m_toolChainUpToDate(false),
523
    m_versionInfoUpToDate(false),
524
525
526
    m_notInstalled(false),
    m_defaultConfigIsDebug(true),
    m_defaultConfigIsDebugAndRelease(true),
527
528
    m_hasExamples(false),
    m_hasDemos(false),
529
    m_hasDocumentation(false)
con's avatar
con committed
530
{
hjk's avatar
hjk committed
531
    if (id == -1)
con's avatar
con committed
532
533
534
        m_id = getUniqueId();
    else
        m_id = id;
535
    setQMakeCommand(qmakeCommand);
con's avatar
con committed
536
537
}

538
QtVersion::QtVersion(const QString &name, const QString &qmakeCommand,
539
                     bool isAutodetected, const QString &autodetectionSource)
540
    : m_displayName(name),
541
542
    m_isAutodetected(isAutodetected),
    m_autodetectionSource(autodetectionSource),
543
    m_hasDebuggingHelper(false),
544
545
    m_hasQmlDump(false),
    m_hasQmlObserver(false),
546
    m_toolChainUpToDate(false),
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
    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),
564
565
    m_hasQmlDump(false),
    m_hasQmlObserver(false),
566
    m_toolChainUpToDate(false),
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)
con's avatar
con committed
574
575
{
    m_id = getUniqueId();
576
    setQMakeCommand(qmakeCommand);
577
    m_displayName = qtVersionString();
con's avatar
con committed
578
579
}

580
QtVersion::QtVersion()
581
    :  m_id(-1),
582
583
    m_isAutodetected(false),
    m_hasDebuggingHelper(false),
584
585
    m_hasQmlDump(false),
    m_hasQmlObserver(false),
586
    m_toolChainUpToDate(false),
587
588
589
590
591
592
593
594
    m_versionInfoUpToDate(false),
    m_notInstalled(false),
    m_defaultConfigIsDebug(true),
    m_defaultConfigIsDebugAndRelease(true),
    m_hasExamples(false),
    m_hasDemos(false),
    m_hasDocumentation(false)
{
595
    setQMakeCommand(QString());
596
597
598
}


599
600
601
602
QtVersion::~QtVersion()
{
}

603
604
605
606
QString QtVersion::toHtml() const
{
    QString rc;
    QTextStream str(&rc);
Robert Loehning's avatar
Robert Loehning committed
607
    str << "<html><body><table>";
608
    str << "<tr><td><b>" << QtVersionManager::tr("Name:")
609
        << "</b></td><td>" << displayName() << "</td></tr>";
610
611
612
613
614
615
    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
616
    updateToolChainAndMkspec();
617
    if (m_defaultConfigIsDebug || m_defaultConfigIsDebugAndRelease) {
618
619
        str << "<tr><td><b>" << QtVersionManager::tr("Default:") << "</b></td><td>"
            << (m_defaultConfigIsDebug ? "debug" : "release");
620
        if (m_defaultConfigIsDebugAndRelease)
621
            str << " debug_and_release";
622
623
624
625
626
627
628
629
630
631
632
633
634
        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
635
    str << "</table></body></html>";
636
637
638
    return rc;
}

639
640
641
642
643
644
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
645
        // We can not support shadow building with the ABLD system
646
647
648
649
650
        return false;
    }
    return true;
}

651
QList<ProjectExplorer::Task>
652
QtVersion::reportIssues(const QString &proFile, const QString &buildDir)
653
654
655
{
    QList<ProjectExplorer::Task> results;

656
657
658
659
    QString tmpBuildDir = buildDir;
    if (!buildDir.endsWith(QChar('/')))
        tmpBuildDir.append(QChar('/'));

Friedemann Kleint's avatar
Friedemann Kleint committed
660
661
662
663
    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,
664
                                             QLatin1String(ProjectExplorer::Constants::TASK_CATEGORY_BUILDSYSTEM)));
Friedemann Kleint's avatar
Friedemann Kleint committed
665
    }
666
667
668

    QFileInfo qmakeInfo(qmakeCommand());
    if (!qmakeInfo.exists() ||
Friedemann Kleint's avatar
Friedemann Kleint committed
669
670
671
672
673
        !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,
674
                                             QLatin1String(ProjectExplorer::Constants::TASK_CATEGORY_BUILDSYSTEM)));
Friedemann Kleint's avatar
Friedemann Kleint committed
675
    }
676

677
678
679
680
681
682
683
684
685
686
687
    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",
688
                                                        "The build directory needs to be at the same level as the source directory.");
689

690
691
692
693
        results.append(ProjectExplorer::Task(ProjectExplorer::Task::Warning, msg, QString(), -1,
                                             QLatin1String(ProjectExplorer::Constants::TASK_CATEGORY_BUILDSYSTEM)));
    }

694
695
696
697
698
699
700
    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;
}

701
QString QtVersion::displayName() const
con's avatar
con committed
702
{
703
    return m_displayName;
con's avatar
con committed
704
705
}

706
QString QtVersion::qmakeCommand() const
con's avatar
con committed
707
{
708
    return m_qmakeCommand;
con's avatar
con committed
709
710
711
712
713
714
715
716
717
}

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

QString QtVersion::mkspec() const
{
dt's avatar
dt committed
718
    updateToolChainAndMkspec();
con's avatar
con committed
719
720
721
    return m_mkspec;
}

dt's avatar
dt committed
722
723
QString QtVersion::mkspecPath() const
{
dt's avatar
dt committed
724
    updateToolChainAndMkspec();
dt's avatar
dt committed
725
726
727
    return m_mkspecFullPath;
}

728
729
730
731
732
733
bool QtVersion::isBuildWithSymbianSbsV2() const
{
    updateToolChainAndMkspec();
    return m_isBuildUsingSbsV2;
}

734
735
QString QtVersion::qtVersionString() const
{
736
737
738
739
740
741
742
743
    if (m_qtVersionString.isNull()) {
        QFileInfo qmake(m_qmakeCommand);
        if (qmake.exists() && qmake.isExecutable()) {
            m_qtVersionString = DebuggingHelperLibrary::qtVersionForQMake(qmake.absoluteFilePath());
        } else {
            m_qtVersionString = QLatin1String("");
        }
    }
744
745
746
    return m_qtVersionString;
}

con's avatar
con committed
747
748
749
750
751
752
QHash<QString,QString> QtVersion::versionInfo() const
{
    updateVersionInfo();
    return m_versionInfo;
}

753
void QtVersion::setDisplayName(const QString &name)
con's avatar
con committed
754
{
755
    m_displayName = name;
con's avatar
con committed
756
757
}

758
void QtVersion::setQMakeCommand(const QString& qmakeCommand)
con's avatar
con committed
759
{
760
    m_qmakeCommand = QDir::fromNativeSeparators(qmakeCommand);
761
#ifdef Q_OS_WIN
762
    m_qmakeCommand = m_qmakeCommand.toLower();
763
#endif
764
765
    m_designerCommand.clear();
    m_linguistCommand.clear();
766
    m_qmlviewerCommand.clear();
767
    m_uicCommand.clear();
768
    m_toolChainUpToDate = false;
769
770
    // TODO do i need to optimize this?
    m_versionInfoUpToDate = false;
771
    m_qtVersionString = QString();
772
    updateSourcePath();
con's avatar
con committed
773
774
775
776
}

void QtVersion::updateSourcePath()
{
777
778
779
780
    updateVersionInfo();
    const QString installData = m_versionInfo["QT_INSTALL_DATA"];
    m_sourcePath = installData;
    QFile qmakeCache(installData + QLatin1String("/.qmake.cache"));
con's avatar
con committed
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
    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;
            }
        }
    }
796
    m_sourcePath = QDir::cleanPath(m_sourcePath);
dt's avatar
dt committed
797
798
799
#ifdef Q_OS_WIN
    m_sourcePath = m_sourcePath.toLower();
#endif
con's avatar
con committed
800
801
802
803
}

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

839
QtVersion *QtVersionManager::qtVersionForQMakeBinary(const QString &qmakePath)
con's avatar
con committed
840
841
{
   foreach(QtVersion *v, versions()) {
Tobias Hunger's avatar
Tobias Hunger committed
842
843
844
845
846
       if (v->qmakeCommand() == qmakePath) {
           return v;
           break;
       }
   }
con's avatar
con committed
847
848
849
   return 0;
}

850
void dumpQMakeAssignments(const QList<QMakeAssignment> &list)
con's avatar
con committed
851
{
852
    foreach(const QMakeAssignment &qa, list) {
853
854
855
856
        qDebug()<<qa.variable<<qa.op<<qa.value;
    }
}

857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
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;
}

875
QPair<QtVersion::QmakeBuildConfigs, QStringList> QtVersionManager::scanMakeFile(const QString &directory, QtVersion::QmakeBuildConfigs defaultBuildConfig)
876
{
877
878
    if (debug)
        qDebug()<<"ScanMakeFile, the gory details:";
879
    QtVersion::QmakeBuildConfigs result = defaultBuildConfig;
880
881
    QStringList result2;

882
    QString line = findQMakeLine(directory, QLatin1String("# Command:"));
883
    if (!line.isEmpty()) {
884
885
        if (debug)
            qDebug()<<"Found line"<<line;
886
887
        line = trimLine(line);
        QStringList parts = splitLine(line);
888
        if (debug)
Tobias Hunger's avatar
Tobias Hunger committed
889
            qDebug()<<"Split into"<<parts;
890
891
892
893
894
        QList<QMakeAssignment> assignments;
        QList<QMakeAssignment> afterAssignments;
        QStringList additionalArguments;
        parseParts(parts, &assignments, &afterAssignments, &additionalArguments);

895
896
897
898
899
900
        if (debug) {
            dumpQMakeAssignments(assignments);
            if (!afterAssignments.isEmpty())
                qDebug()<<"-after";
            dumpQMakeAssignments(afterAssignments);
        }
901
902
903
904
905

        // 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
906
907
        if (debug)
            dumpQMakeAssignments(assignments);
908
909

        result2.append(additionalArguments);
910
        foreach(const QMakeAssignment &qa, assignments)
911
912
913
            result2.append(qa.variable + qa.op + qa.value);
        if (!afterAssignments.isEmpty()) {
            result2.append("-after");
914
            foreach(const QMakeAssignment &qa, afterAssignments)
915
916
917
918
919
                result2.append(qa.variable + qa.op + qa.value);
        }
    }

    // Dump the gathered information:
920
921
922
923
924
925
926
927
928
929
    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";
    }
930
931
932
    return qMakePair(result, result2);
}

933
QString QtVersionManager::findQMakeLine(const QString &directory, const QString &key)
934
{
935
    QFile makefile(directory + QLatin1String("/Makefile" ));
con's avatar
con committed
936
937
938
    if (makefile.exists() && makefile.open(QFile::ReadOnly)) {
        QTextStream ts(&makefile);
        while (!ts.atEnd()) {
939
            const QString line = ts.readLine();
940
            if (line.startsWith(key))
941
942
943
944
945
946
947
948
949
950
951
                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
952
    const int firstSpace = line.indexOf(QLatin1Char(' '), 11);
953
954
955
956
957
958
    return line.mid(firstSpace).trimmed();
}

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