qtversionmanager.cpp 55.4 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
41
#include <projectexplorer/debugginghelper.h>
#include <projectexplorer/projectexplorer.h>
42
#include <projectexplorer/projectexplorerconstants.h>
43
#include <projectexplorer/cesdkhandler.h>
44
#include <coreplugin/coreconstants.h>
45
#include <coreplugin/icore.h>
46
#include <extensionsystem/pluginmanager.h>
kh1's avatar
kh1 committed
47
#include <help/helpmanager.h>
hjk's avatar
hjk committed
48
#include <utils/qtcassert.h>
con's avatar
con committed
49

50
#include <QtCore/QFile>
hjk's avatar
hjk committed
51
#include <QtCore/QProcess>
con's avatar
con committed
52
53
#include <QtCore/QSettings>
#include <QtCore/QTime>
54
#include <QtCore/QTimer>
55
#include <QtCore/QTextStream>
56
#include <QtCore/QDir>
57
58
#include <QtGui/QApplication>
#include <QtGui/QDesktopServices>
con's avatar
con committed
59

60
61
62
63
#ifdef Q_OS_WIN32
#include <windows.h>
#endif

64
65
66
67
using namespace Qt4ProjectManager;
using namespace Qt4ProjectManager::Internal;

using ProjectExplorer::DebuggingHelperLibrary;
con's avatar
con committed
68
69
70

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

73
74
enum { debug = 0 };

75
QtVersionManager *QtVersionManager::m_self = 0;
76

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

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

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

    writeVersionsIntoSettings();
141

con's avatar
con committed
142
    updateDocumentation();
143

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

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

156
QtVersionManager *QtVersionManager::instance()
157
{
158
    return m_self;
159
160
}

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

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

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

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

229
230
231
232
233
234
235
236
void QtVersionManager::updateExamples()
{
    QList<QtVersion *> versions;
    versions.append(m_versions);

    QString examplesPath;
    QString demosPath;
    QtVersion *version = 0;
Tobias Hunger's avatar
Tobias Hunger committed
237
    // try to find a version which has both, demos and examples
238
239
240
241
242
    foreach (version, versions) {
        if (version->hasExamples())
            examplesPath = version->examplesPath();
        if (version->hasDemos())
            demosPath = version->demosPath();
243
        if (!examplesPath.isEmpty() && !demosPath.isEmpty()) {
244
            emit updateExamples(examplesPath, demosPath, version->sourcePath());
245
246
247
248
249
            return;
        }
    }
}

con's avatar
con committed
250
251
252
253
254
255
256
257
int QtVersionManager::getUniqueId()
{
    return m_idcount++;
}

void QtVersionManager::updateUniqueIdToIndexMap()
{
    m_uniqueIdToIndex.clear();
hjk's avatar
hjk committed
258
    for (int i = 0; i < m_versions.size(); ++i)
con's avatar
con committed
259
260
261
262
263
        m_uniqueIdToIndex.insert(m_versions.at(i)->uniqueId(), i);
}

void QtVersionManager::writeVersionsIntoSettings()
{
264
    QSettings *s = Core::ICore::instance()->settings();
265
    s->beginWriteArray(QtVersionsSectionName);
con's avatar
con committed
266
    for (int i = 0; i < m_versions.size(); ++i) {
267
        const QtVersion *version = m_versions.at(i);
con's avatar
con committed
268
        s->setArrayIndex(i);
269
        s->setValue("Name", version->displayName());
270
271
272
        // for downwards compat
        s->setValue("Path", version->versionInfo().value("QT_INSTALL_DATA"));
        s->setValue("QMakePath", version->qmakeCommand());
273
274
275
276
277
278
        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
279
        s->setValue("MwcDirectory", version->mwcDirectory());
280
        s->setValue("S60SDKDirectory", version->s60SDKDirectory());
con's avatar
con committed
281
        s->setValue("GcceDirectory", version->gcceDirectory());
con's avatar
con committed
282
283
284
285
    }
    s->endArray();
}

286
QList<QtVersion *> QtVersionManager::versions() const
con's avatar
con committed
287
288
289
290
{
    return m_versions;
}

291
292
293
294
295
296
297
298
299
300
QList<QtVersion *> QtVersionManager::validVersions() const
{
    QList<QtVersion *> results;
    foreach(QtVersion *v, m_versions) {
        if (v->isValid())
            results.append(v);
    }
    return results;
}

301
302
303
304
305
306
bool QtVersionManager::isValidId(int id) const
{
    int pos = m_uniqueIdToIndex.value(id, -1);
    return (pos != -1);
}

con's avatar
con committed
307
308
309
310
311
312
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
313
    return m_emptyVersion;
con's avatar
con committed
314
315
316
317
318
}

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

327
328
329
330
    QDateTime lastUpdateFromGlobalSettings = globalSettings->value(
            QLatin1String("General/LastQtVersionUpdate")).toDateTime();

    const QFileInfo gsFi(globalSettings->fileName());
331
332
    if ( !lastUpdateFromGlobalSettings.isNull() &&
         (!gsFi.exists() || (gsFi.lastModified() > lastUpdateFromGlobalSettings)) )
con's avatar
con committed
333
334
        return;

335
336
337
338
339
    if (!globalSettings->contains(newQtVersionsKey) &&
        !globalSettings->contains(QLatin1String("Installer/")+newQtVersionsKey))
    {
        return;
    }
con's avatar
con committed
340
341

    QString newVersionsValue = settings->value(newQtVersionsKey).toString();
342
343
344
    if (newVersionsValue.isEmpty())
        newVersionsValue = settings->value(QLatin1String("Installer/")+newQtVersionsKey).toString();

con's avatar
con committed
345
    QStringList newVersionsList = newVersionsValue.split(';', QString::SkipEmptyParts);
346
    foreach (const QString &newVersion, newVersionsList) {
con's avatar
con committed
347
        QStringList newVersionData = newVersion.split('=');
348
        if (newVersionData.count() >= 2) {
349
            if (QFile::exists(newVersionData[1])) {
con's avatar
con committed
350
                QtVersion *version = new QtVersion(newVersionData[0], newVersionData[1], m_idcount++ );
hjk's avatar
hjk committed
351
                if (newVersionData.count() >= 3)
con's avatar
con committed
352
                    version->setMingwDirectory(newVersionData[2]);
353
                if (newVersionData.count() >= 4)
354
                    version->setS60SDKDirectory(QDir::fromNativeSeparators(newVersionData[3]));
355
                if (newVersionData.count() >= 5)
356
                    version->setGcceDirectory(QDir::fromNativeSeparators(newVersionData[4]));
357
                if (newVersionData.count() >= 6)
358
                    version->setMwcDirectory(QDir::fromNativeSeparators(newVersionData[5]));
359
360
                if (newVersionData.count() >= 7)
                    version->setMsvcVersion(newVersionData[6]);
con's avatar
con committed
361
362
363

                bool versionWasAlreadyInList = false;
                foreach(const QtVersion * const it, m_versions) {
364
                    if (QDir(version->qmakeCommand()).canonicalPath() == QDir(it->qmakeCommand()).canonicalPath()) {
con's avatar
con committed
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
                        versionWasAlreadyInList = true;
                        break;
                    }
                }

                if (!versionWasAlreadyInList) {
                    m_versions.append(version);
                } else {
                    // clean up
                    delete version;
                }
            }
        }
    }
    updateUniqueIdToIndexMap();
380
    settings->setValue(QLatin1String("General/LastQtVersionUpdate"), QDateTime::currentDateTime());
con's avatar
con committed
381
382
383
384
385
}

void QtVersionManager::updateSystemVersion()
{
    bool haveSystemVersion = false;
386
    QString systemQMakePath = DebuggingHelperLibrary::findSystemQt(ProjectExplorer::Environment::systemEnvironment());
387
388
    if (systemQMakePath.isNull())
        systemQMakePath = tr("<not found>");
389

con's avatar
con committed
390
    foreach (QtVersion *version, m_versions) {
391
392
        if (version->isAutodetected()
            && version->autodetectionSource() == PATH_AUTODETECTION_SOURCE) {
393
            version->setQMakeCommand(systemQMakePath);
394
            version->setDisplayName(tr("Qt in PATH"));
con's avatar
con committed
395
396
397
398
399
            haveSystemVersion = true;
        }
    }
    if (haveSystemVersion)
        return;
400
    QtVersion *version = new QtVersion(tr("Qt in PATH"),
401
                                       systemQMakePath,
con's avatar
con committed
402
                                       getUniqueId(),
403
404
                                       true,
                                       PATH_AUTODETECTION_SOURCE);
con's avatar
con committed
405
406
407
408
    m_versions.prepend(version);
    updateUniqueIdToIndexMap();
}

Tobias Hunger's avatar
Tobias Hunger committed
409
QtVersion *QtVersionManager::emptyVersion() const
con's avatar
con committed
410
{
Tobias Hunger's avatar
Tobias Hunger committed
411
    return m_emptyVersion;
con's avatar
con committed
412
413
}

414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
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;
433
434
    if (a->m_displayName != b->displayName())
        return false;
435
436
437
    return true;
}

Tobias Hunger's avatar
Tobias Hunger committed
438
void QtVersionManager::setNewQtVersions(QList<QtVersion *> newVersions)
439
{
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
    // 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;
472
473
        }
    }
474
475
476

    while (nit != nend) {
        changedVersions.push_back((*nit)->uniqueId());
477
        ++nit;
478
479
480
481
    }

    while (oit != oend) {
        changedVersions.push_back((*oit)->uniqueId());
482
        ++oit;
483
484
    }

485
486
    qDeleteAll(m_versions);
    m_versions.clear();
dt's avatar
dt committed
487
    m_versions = newVersions;
488
489

    if (!changedVersions.isEmpty())
490
491
492
        updateDocumentation();
    updateUniqueIdToIndexMap();

493
    updateExamples();
494
    writeVersionsIntoSettings();
495
496
497

    if (!changedVersions.isEmpty())
        emit qtVersionsChanged(changedVersions);
498
499
}

con's avatar
con committed
500
501
502
503
///
/// QtVersion
///

504
QtVersion::QtVersion(const QString &name, const QString &qmakeCommand, int id,
505
                     bool isAutodetected, const QString &autodetectionSource)
506
    : m_displayName(name),
507
    m_isAutodetected(isAutodetected),
508
    m_autodetectionSource(autodetectionSource),
509
    m_hasDebuggingHelper(false),
510
    m_toolChainUpToDate(false),
511
    m_versionInfoUpToDate(false),
512
513
514
    m_notInstalled(false),
    m_defaultConfigIsDebug(true),
    m_defaultConfigIsDebugAndRelease(true),
515
516
    m_hasExamples(false),
    m_hasDemos(false),
517
    m_hasDocumentation(false)
con's avatar
con committed
518
{
hjk's avatar
hjk committed
519
    if (id == -1)
con's avatar
con committed
520
521
522
        m_id = getUniqueId();
    else
        m_id = id;
523
    setQMakeCommand(qmakeCommand);
con's avatar
con committed
524
525
}

526
QtVersion::QtVersion(const QString &name, const QString &qmakeCommand,
527
                     bool isAutodetected, const QString &autodetectionSource)
528
    : m_displayName(name),
529
530
    m_isAutodetected(isAutodetected),
    m_autodetectionSource(autodetectionSource),
531
    m_hasDebuggingHelper(false),
532
    m_toolChainUpToDate(false),
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
    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),
550
    m_toolChainUpToDate(false),
551
552
553
554
555
556
557
    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
558
559
{
    m_id = getUniqueId();
560
    setQMakeCommand(qmakeCommand);
561
    m_displayName = qtVersionString();
con's avatar
con committed
562
563
}

564
QtVersion::QtVersion()
565
    :  m_id(-1),
566
567
    m_isAutodetected(false),
    m_hasDebuggingHelper(false),
568
    m_toolChainUpToDate(false),
569
570
571
572
573
574
575
576
    m_versionInfoUpToDate(false),
    m_notInstalled(false),
    m_defaultConfigIsDebug(true),
    m_defaultConfigIsDebugAndRelease(true),
    m_hasExamples(false),
    m_hasDemos(false),
    m_hasDocumentation(false)
{
577
    setQMakeCommand(QString());
578
579
580
}


581
582
583
584
QtVersion::~QtVersion()
{
}

585
586
587
588
589
590
QString QtVersion::toHtml() const
{
    QString rc;
    QTextStream str(&rc);
    str << "<html></head><body><table>";
    str << "<tr><td><b>" << QtVersionManager::tr("Name:")
591
        << "</b></td><td>" << displayName() << "</td></tr>";
592
593
594
595
596
597
    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
598
    updateToolChainAndMkspec();
599
    if (m_defaultConfigIsDebug || m_defaultConfigIsDebugAndRelease) {
600
601
        str << "<tr><td><b>" << QtVersionManager::tr("Default:") << "</b></td><td>"
            << (m_defaultConfigIsDebug ? "debug" : "release");
602
        if (m_defaultConfigIsDebugAndRelease)
603
            str << " debug_and_release";
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
        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>";
    }
    str << "<table></body></html>";
    return rc;
}

621
622
623
624
625
626
627
628
629
630
631
632
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)) {
	// We can not support shadow building with the ABLD system
        return false;
    }
    return true;
}

633
634
635
636
637
QList<ProjectExplorer::Task>
QtVersion::reportIssues(const QString &proFile)
{
    QList<ProjectExplorer::Task> results;

Friedemann Kleint's avatar
Friedemann Kleint committed
638
639
640
641
    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,
642
                                             QLatin1String(ProjectExplorer::Constants::TASK_CATEGORY_BUILDSYSTEM)));
Friedemann Kleint's avatar
Friedemann Kleint committed
643
    }
644
645
646

    QFileInfo qmakeInfo(qmakeCommand());
    if (!qmakeInfo.exists() ||
Friedemann Kleint's avatar
Friedemann Kleint committed
647
648
649
650
651
        !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,
652
                                             QLatin1String(ProjectExplorer::Constants::TASK_CATEGORY_BUILDSYSTEM)));
Friedemann Kleint's avatar
Friedemann Kleint committed
653
    }
654
655
656
657
658
659
660
661

    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;
}

662
QString QtVersion::displayName() const
con's avatar
con committed
663
{
664
    return m_displayName;
con's avatar
con committed
665
666
}

667
QString QtVersion::qmakeCommand() const
con's avatar
con committed
668
{
669
    return m_qmakeCommand;
con's avatar
con committed
670
671
672
673
674
675
676
677
678
}

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

QString QtVersion::mkspec() const
{
dt's avatar
dt committed
679
    updateToolChainAndMkspec();
con's avatar
con committed
680
681
682
    return m_mkspec;
}

dt's avatar
dt committed
683
684
QString QtVersion::mkspecPath() const
{
dt's avatar
dt committed
685
    updateToolChainAndMkspec();
dt's avatar
dt committed
686
687
688
    return m_mkspecFullPath;
}

689
690
QString QtVersion::qtVersionString() const
{
691
692
693
694
695
696
697
698
    if (m_qtVersionString.isNull()) {
        QFileInfo qmake(m_qmakeCommand);
        if (qmake.exists() && qmake.isExecutable()) {
            m_qtVersionString = DebuggingHelperLibrary::qtVersionForQMake(qmake.absoluteFilePath());
        } else {
            m_qtVersionString = QLatin1String("");
        }
    }
699
700
701
    return m_qtVersionString;
}

con's avatar
con committed
702
703
704
705
706
707
QHash<QString,QString> QtVersion::versionInfo() const
{
    updateVersionInfo();
    return m_versionInfo;
}

708
void QtVersion::setDisplayName(const QString &name)
con's avatar
con committed
709
{
710
    m_displayName = name;
con's avatar
con committed
711
712
}

713
void QtVersion::setQMakeCommand(const QString& qmakeCommand)
con's avatar
con committed
714
{
715
    m_qmakeCommand = QDir::fromNativeSeparators(qmakeCommand);
716
#ifdef Q_OS_WIN
717
    m_qmakeCommand = m_qmakeCommand.toLower();
718
#endif
719
720
721
    m_designerCommand.clear();
    m_linguistCommand.clear();
    m_uicCommand.clear();
722
    m_toolChainUpToDate = false;
723
724
    // TODO do i need to optimize this?
    m_versionInfoUpToDate = false;
725
    m_qtVersionString = QString();
726
    updateSourcePath();
con's avatar
con committed
727
728
729
730
}

void QtVersion::updateSourcePath()
{
731
732
733
734
    updateVersionInfo();
    const QString installData = m_versionInfo["QT_INSTALL_DATA"];
    m_sourcePath = installData;
    QFile qmakeCache(installData + QLatin1String("/.qmake.cache"));
con's avatar
con committed
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
    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;
            }
        }
    }
750
    m_sourcePath = QDir::cleanPath(m_sourcePath);
dt's avatar
dt committed
751
752
753
#ifdef Q_OS_WIN
    m_sourcePath = m_sourcePath.toLower();
#endif
con's avatar
con committed
754
755
756
757
}

// 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
758
// To find out whether we already have a qtversion for that directory call
con's avatar
con committed
759
// QtVersion *QtVersionManager::qtVersionForDirectory(const QString directory);
760
QString QtVersionManager::findQMakeBinaryFromMakefile(const QString &directory)
con's avatar
con committed
761
762
763
764
765
{
    bool debugAdding = false;
    QFile makefile(directory + "/Makefile" );
    if (makefile.exists() && makefile.open(QFile::ReadOnly)) {
        QTextStream ts(&makefile);
766
        QRegExp r1("QMAKE\\s*=(.*)");
con's avatar
con committed
767
768
769
770
771
772
        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());
773
                QString qmakePath = qmake.filePath();
774
#ifdef Q_OS_WIN
775
                qmakePath = qmakePath.toLower();
776
777
                if (!qmakePath.endsWith(QLatin1String(".exe")))
                    qmakePath.append(QLatin1String(".exe"));
778
#endif
779
780
781
                // Is qmake still installed?
                if (QFile::exists(qmakePath))
                    return qmakePath;
con's avatar
con committed
782
783
784
785
            }
        }
        makefile.close();
    }
786
    return QString();
con's avatar
con committed
787
788
}

789
QtVersion *QtVersionManager::qtVersionForQMakeBinary(const QString &qmakePath)
con's avatar
con committed
790
791
{
   foreach(QtVersion *v, versions()) {
792
        if (v->qmakeCommand() == qmakePath) {
con's avatar
con committed
793
794
795
796
797
798
799
            return v;
            break;
        }
    }
   return 0;
}

800
void dumpQMakeAssignments(const QList<QMakeAssignment> &list)
con's avatar
con committed
801
{
802
    foreach(const QMakeAssignment &qa, list) {
803
804
805
806
        qDebug()<<qa.variable<<qa.op<<qa.value;
    }
}

807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
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;
}

825
QPair<QtVersion::QmakeBuildConfigs, QStringList> QtVersionManager::scanMakeFile(const QString &directory, QtVersion::QmakeBuildConfigs defaultBuildConfig)
826
{
827
828
    if (debug)
        qDebug()<<"ScanMakeFile, the gory details:";
829
    QtVersion::QmakeBuildConfigs result = defaultBuildConfig;
830
831
    QStringList result2;

832
    QString line = findQMakeLine(directory, QLatin1String("# Command:"));
833
    if (!line.isEmpty()) {
834
835
        if (debug)
            qDebug()<<"Found line"<<line;
836
837
        line = trimLine(line);
        QStringList parts = splitLine(line);
838
        if (debug)
Tobias Hunger's avatar
Tobias Hunger committed
839
            qDebug()<<"Split into"<<parts;
840
841
842
843
844
        QList<QMakeAssignment> assignments;
        QList<QMakeAssignment> afterAssignments;
        QStringList additionalArguments;
        parseParts(parts, &assignments, &afterAssignments, &additionalArguments);

845
846
847
848
849
850
        if (debug) {
            dumpQMakeAssignments(assignments);
            if (!afterAssignments.isEmpty())
                qDebug()<<"-after";
            dumpQMakeAssignments(afterAssignments);
        }
851
852
853
854
855

        // 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
856
857
        if (debug)
            dumpQMakeAssignments(assignments);
858
859

        result2.append(additionalArguments);
860
        foreach(const QMakeAssignment &qa, assignments)
861
862
863
            result2.append(qa.variable + qa.op + qa.value);
        if (!afterAssignments.isEmpty()) {
            result2.append("-after");
864
            foreach(const QMakeAssignment &qa, afterAssignments)
865
866
867
868
869
                result2.append(qa.variable + qa.op + qa.value);
        }
    }

    // Dump the gathered information:
870
871
872
873
874
875
876
877
878
879
    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";
    }
880
881
882
    return qMakePair(result, result2);
}

883
QString QtVersionManager::findQMakeLine(const QString &directory, const QString &key)
884
{
885
    QFile makefile(directory + QLatin1String("/Makefile" ));
con's avatar
con committed
886
887
888
    if (makefile.exists() && makefile.open(QFile::ReadOnly)) {
        QTextStream ts(&makefile);
        while (!ts.atEnd()) {
889
            const QString line = ts.readLine();
890
            if (line.startsWith(key))
891
892
893
894
895
896
897
898
899
900
901
                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
902
    const int firstSpace = line.indexOf(QLatin1Char(' '), 11);
903
904
905
906
907
908
    return line.mid(firstSpace).trimmed();
}

QStringList QtVersionManager::splitLine(const QString &line)
{
    // Split on each " ", except on those which are escaped
909
910
    // On Unix also remove all escaping
    // On Windows also, but different escaping
911
912
913
914
915
    bool escape = false;
    QString currentWord;
    QStringList results;
    int length = line.length();
    for (int i=0; i<length; ++i) {
916
917
918
919
920
921
922
923
924
925
#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
926
927
928
929
930
931
932
933
934
935
936
        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);
        }
937
#endif
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
    }
    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;
        } else if (part == "after") {
            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
984
QtVersion::QmakeBuildConfigs QtVersionManager::qmakeBuildConfigFromCmdArgs(QList<QMakeAssignment> *assignments, QtVersion::QmakeBuildConfigs defaultBuildConfig)
985
{
986
    QtVersion::QmakeBuildConfigs result = defaultBuildConfig;
987
988
    QList<QMakeAssignment> oldAssignments = *assignments;
    assignments->clear();
989
    foreach(const QMakeAssignment &qa, oldAssignments) {
990
991
992
993
994
995
        if (qa.variable == "CONFIG") {
            QStringList values = qa.value.split(' ');
            QStringList newValues;
            foreach(const QString &value, values) {
                if (value == "debug") {
                    if (qa.op == "+=")
996
                        result = result  | QtVersion::DebugBuild;
997
                    else
998
                        result = result  & ~QtVersion::DebugBuild;
999
1000
                } else if (value == "release") {
                    if (qa.op == "+=")
1001
                        result = result & ~QtVersion::DebugBuild;
1002
                    else
1003
                        result = result | QtVersion::DebugBuild;
1004
1005
                } else if (value == "debug_and_release") {
                    if (qa.op == "+=")
1006
                        result = result | QtVersion::BuildAll;
1007
                    else
1008
                        result = result & ~QtVersion::BuildAll;
1009
1010
                } else {
                    newValues.append(value);
con's avatar
con committed
1011
                }
1012
1013
1014
1015
                QMakeAssignment newQA = qa;
                newQA.value = newValues.join(" ");
                if (!newValues.isEmpty())
                    assignments->append(newQA);
con's avatar
con committed
1016
            }
1017
1018
        } else {
            assignments->append(qa);
con's avatar
con committed
1019
1020
1021
1022
1023
1024
1025
1026
1027
        }
    }
    return result;
}

void QtVersion::updateVersionInfo() const
{
    if (m_versionInfoUpToDate)
        return;
1028

con's avatar
con committed
1029
1030
1031
    // extract data from qmake executable
    m_versionInfo.clear();
    m_notInstalled = false;
1032
1033
    m_hasExamples = false;
    m_hasDocumentation = false;
dt's avatar
dt committed
1034
    m_hasDebuggingHelper = false;
1035

con's avatar
con committed
1036
    QFileInfo qmake(qmakeCommand());
1037
    if (qmake.exists() && qmake.isExecutable()) {
1038
        static const char * const variables[] = {
1039
             "QT_VERSION",
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
             "QT_INSTALL_DATA",
             "QT_INSTALL_LIBS",
             "QT_INSTALL_HEADERS",
             "QT_INSTALL_DEMOS",
             "QT_INSTALL_EXAMPLES",
             "QT_INSTALL_CONFIGURATION",
             "QT_INSTALL_TRANSLATIONS",
             "QT_INSTALL_PLUGINS",
             "QT_INSTALL_BINS",
             "QT_INSTALL_DOCS",
dt's avatar
dt committed
1050
1051
             "QT_INSTALL_PREFIX",
             "QMAKEFEATURES"
1052
1053
1054
1055
        };
        QStringList args;
        for (uint i = 0; i < sizeof variables / sizeof variables[0]; ++i)
            args << "-query" << variables[i];
con's avatar
con committed
1056
1057
1058
1059
1060
1061
        QProcess process;
        process.start(qmake.absoluteFilePath(), args, QIODevice::ReadOnly);
        if (process.waitForFinished(2000)) {
            QByteArray output = process.readAllStandardOutput();
            QTextStream stream(&output);
            while (!stream.atEnd()) {
1062
1063
                const QString line = stream.readLine();
                const int index = line.indexOf(QLatin1Char(':'));
dt's avatar
dt committed
1064
1065
1066
1067
1068
                if (index != -1) {
                    QString value = QDir::fromNativeSeparators(line.mid(index+1));
                    if (value != "**Unknown**")
                        m_versionInfo.insert(line.left(index), value);
                }
con's avatar
con committed
1069
1070
1071
            }
        }

1072
1073
1074
1075
        if (m_versionInfo.contains("QT_INSTALL_DATA")) {
            QString qtInstallData = m_versionInfo.value("QT_INSTALL_DATA");
            m_versionInfo.insert("QMAKE_MKSPECS", QDir::cleanPath(qtInstallData+"/mkspecs"));

dt's avatar
dt committed
1076
1077
            if (!qtInstallData.isEmpty())
                m_hasDebuggingHelper = !DebuggingHelperLibrary::debuggingHelperLibraryByInstallData(qtInstallData).isEmpty();
1078
        }
con's avatar
con committed
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090

        // Now check for a qt that is configured with a prefix but not installed
        if (m_versionInfo.contains("QT_INSTALL_BINS")) {
            QFileInfo fi(m_versionInfo.value("QT_INSTALL_BINS"));
            if (!fi.exists())
                m_notInstalled = true;
        }
        if (m_versionInfo.contains("QT_INSTALL_HEADERS")){
            QFileInfo fi(m_versionInfo.value("QT_INSTALL_HEADERS"));
            if (!fi.exists())
                m_notInstalled = true;
        }
1091
1092
1093
1094