qtversionmanager.cpp 63 KB
Newer Older
1
/**************************************************************************
con's avatar
con committed
2
3
4
**
** This file is part of Qt Creator
**
con's avatar
con committed
5
** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
con's avatar
con committed
6
**
7
** Contact: Nokia Corporation (qt-info@nokia.com)
con's avatar
con committed
8
**
con's avatar
con committed
9
** No Commercial Usage
10
**
con's avatar
con committed
11
12
13
14
** This file contains pre-release code and may not be distributed.
** You may use this file in accordance with the terms and conditions
** contained in the Technology Preview License Agreement accompanying
** this package.
15
**
16
** GNU Lesser General Public License Usage
17
**
18
19
20
21
22
23
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24
**
con's avatar
con committed
25
26
27
28
29
30
** In addition, as a special exception, Nokia gives you certain additional
** rights.  These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** If you have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
con's avatar
con committed
31
**
32
**************************************************************************/
hjk's avatar
hjk committed
33

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

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

40
#include "qt-maemo/maemoglobal.h"
ck's avatar
ck committed
41
#include "qt-maemo/maemomanager.h"
42
#include "qt-s60/s60manager.h"
43
#include "qt-s60/s60projectchecker.h"
44
45
#include "qt-s60/abldparser.h"
#include "qt-s60/sbsv2parser.h"
46

47
#include "qmlobservertool.h"
48
#include "qmldumptool.h"
49
#include <projectexplorer/debugginghelper.h>
50
#include <projectexplorer/gnumakeparser.h>
51
#include <projectexplorer/projectexplorer.h>
52
#include <projectexplorer/projectexplorerconstants.h>
53
#include <projectexplorer/cesdkhandler.h>
54
#include <utils/synchronousprocess.h>
55
#include <coreplugin/coreconstants.h>
56
#include <coreplugin/icore.h>
kh1's avatar
kh1 committed
57
#include <coreplugin/helpmanager.h>
58
#include <extensionsystem/pluginmanager.h>
hjk's avatar
hjk committed
59
#include <utils/qtcassert.h>
60
#include <utils/qtcprocess.h>
61
62
63
#ifdef Q_OS_WIN
#    include <utils/winutils.h>
#endif
con's avatar
con committed
64

65
#include <QtCore/QFile>
hjk's avatar
hjk committed
66
#include <QtCore/QProcess>
con's avatar
con committed
67
68
#include <QtCore/QSettings>
#include <QtCore/QTime>
69
#include <QtCore/QTimer>
70
#include <QtCore/QTextStream>
71
#include <QtCore/QDir>
72
73
#include <QtGui/QApplication>
#include <QtGui/QDesktopServices>
con's avatar
con committed
74

75
76
77
78
using namespace Qt4ProjectManager;
using namespace Qt4ProjectManager::Internal;

using ProjectExplorer::DebuggingHelperLibrary;
con's avatar
con committed
79
80
81

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

84
85
enum { debug = 0 };

86
QtVersionManager *QtVersionManager::m_self = 0;
87

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

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

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

    writeVersionsIntoSettings();
153

con's avatar
con committed
154
    updateDocumentation();
155

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

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

168
QtVersionManager *QtVersionManager::instance()
169
{
170
    return m_self;
171
172
}

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

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

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

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

240
241
242
void QtVersionManager::updateExamples()
{
    QtVersion *version = 0;
243
244
    QList<QtVersion*> candidates;

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

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

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

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

284
285
}

con's avatar
con committed
286
287
288
289
290
291
292
293
int QtVersionManager::getUniqueId()
{
    return m_idcount++;
}

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

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

323
QList<QtVersion *> QtVersionManager::versions() const
con's avatar
con committed
324
325
326
327
{
    return m_versions;
}

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

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

con's avatar
con committed
344
345
346
347
348
349
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
350
    return m_emptyVersion;
con's avatar
con committed
351
352
353
354
355
}

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

364
365
366
367
    QDateTime lastUpdateFromGlobalSettings = globalSettings->value(
            QLatin1String("General/LastQtVersionUpdate")).toDateTime();

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

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

    QString newVersionsValue = settings->value(newQtVersionsKey).toString();
379
380
381
    if (newVersionsValue.isEmpty())
        newVersionsValue = settings->value(QLatin1String("Installer/")+newQtVersionsKey).toString();

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

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

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

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

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

Tobias Hunger's avatar
Tobias Hunger committed
448
QtVersion *QtVersionManager::emptyVersion() const
con's avatar
con committed
449
{
Tobias Hunger's avatar
Tobias Hunger committed
450
    return m_emptyVersion;
con's avatar
con committed
451
452
}

453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
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;
472
473
    if (a->m_displayName != b->displayName())
        return false;
474
475
476
    return true;
}

Tobias Hunger's avatar
Tobias Hunger committed
477
void QtVersionManager::setNewQtVersions(QList<QtVersion *> newVersions)
478
{
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
    // 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;
511
512
        }
    }
513
514
515

    while (nit != nend) {
        changedVersions.push_back((*nit)->uniqueId());
516
        ++nit;
517
518
519
520
    }

    while (oit != oend) {
        changedVersions.push_back((*oit)->uniqueId());
521
        ++oit;
522
523
    }

524
525
    qDeleteAll(m_versions);
    m_versions.clear();
dt's avatar
dt committed
526
    m_versions = newVersions;
527
528

    if (!changedVersions.isEmpty())
529
530
531
        updateDocumentation();
    updateUniqueIdToIndexMap();

532
    updateExamples();
533
    writeVersionsIntoSettings();
534
535
536

    if (!changedVersions.isEmpty())
        emit qtVersionsChanged(changedVersions);
537
538
}

con's avatar
con committed
539
540
541
542
///
/// QtVersion
///

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

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


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

612
QtVersion::QtVersion()
613
    :  m_id(-1),
614
615
    m_isAutodetected(false),
    m_hasDebuggingHelper(false),
616
617
    m_hasQmlDump(false),
    m_hasQmlObserver(false),
618
    m_toolChainUpToDate(false),
619
620
621
622
623
624
    m_versionInfoUpToDate(false),
    m_notInstalled(false),
    m_defaultConfigIsDebug(true),
    m_defaultConfigIsDebugAndRelease(true),
    m_hasExamples(false),
    m_hasDemos(false),
625
626
    m_hasDocumentation(false),
    m_isBuildUsingSbsV2(false)
627
{
628
    setQMakeCommand(QString());
629
630
631
}


632
633
634
635
QtVersion::~QtVersion()
{
}

636
637
638
639
QString QtVersion::toHtml() const
{
    QString rc;
    QTextStream str(&rc);
Robert Loehning's avatar
Robert Loehning committed
640
    str << "<html><body><table>";
641
    str << "<tr><td><b>" << QtVersionManager::tr("Name:")
642
        << "</b></td><td>" << displayName() << "</td></tr>";
643
644
645
646
647
648
    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
649
    updateToolChainAndMkspec();
650
    if (m_defaultConfigIsDebug || m_defaultConfigIsDebugAndRelease) {
651
652
        str << "<tr><td><b>" << QtVersionManager::tr("Default:") << "</b></td><td>"
            << (m_defaultConfigIsDebug ? "debug" : "release");
653
        if (m_defaultConfigIsDebugAndRelease)
654
            str << " debug_and_release";
655
656
657
658
659
660
661
662
663
664
665
666
667
        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
668
    str << "</table></body></html>";
669
670
671
    return rc;
}

672
673
674
675
676
677
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
678
        // We can not support shadow building with the ABLD system
679
680
681
682
683
        return false;
    }
    return true;
}

684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
ProjectExplorer::IOutputParser *QtVersion::createOutputParser() const
{
    if (supportsTargetId(Qt4ProjectManager::Constants::S60_DEVICE_TARGET_ID) ||
        supportsTargetId(Qt4ProjectManager::Constants::S60_EMULATOR_TARGET_ID)) {
        if (isBuildWithSymbianSbsV2()) {
            return new SbsV2Parser;
        } else {
            ProjectExplorer::IOutputParser *parser = new AbldParser;
            parser->appendOutputParser(new ProjectExplorer::GnuMakeParser);
            return parser;
        }
    }
    return new ProjectExplorer::GnuMakeParser;
}

699
QList<ProjectExplorer::Task>
700
QtVersion::reportIssues(const QString &proFile, const QString &buildDir)
701
702
703
{
    QList<ProjectExplorer::Task> results;

704
705
706
    QString tmpBuildDir = QDir(buildDir).absolutePath();
    if (!tmpBuildDir.endsWith(QLatin1Char('/')))
        tmpBuildDir.append(QLatin1Char('/'));
707

Friedemann Kleint's avatar
Friedemann Kleint committed
708
709
710
711
    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,
712
                                             QLatin1String(ProjectExplorer::Constants::TASK_CATEGORY_BUILDSYSTEM)));
Friedemann Kleint's avatar
Friedemann Kleint committed
713
    }
714
715
716

    QFileInfo qmakeInfo(qmakeCommand());
    if (!qmakeInfo.exists() ||
Friedemann Kleint's avatar
Friedemann Kleint committed
717
718
719
720
721
        !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,
722
                                             QLatin1String(ProjectExplorer::Constants::TASK_CATEGORY_BUILDSYSTEM)));
Friedemann Kleint's avatar
Friedemann Kleint committed
723
    }
724

725
    QString sourcePath = QFileInfo(proFile).absolutePath();
726
727
    if (!sourcePath.endsWith(QLatin1Char('/')))
        sourcePath.append(QLatin1Char('/'));
728
729
730
731
732
733
734
    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",
735
                                                        "The build directory needs to be at the same level as the source directory.");
736

737
738
739
740
        results.append(ProjectExplorer::Task(ProjectExplorer::Task::Warning, msg, QString(), -1,
                                             QLatin1String(ProjectExplorer::Constants::TASK_CATEGORY_BUILDSYSTEM)));
    }

741
742
743
744
745
746
747
    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;
}

748
QString QtVersion::displayName() const
con's avatar
con committed
749
{
750
    return m_displayName;
con's avatar
con committed
751
752
}

753
QString QtVersion::qmakeCommand() const
con's avatar
con committed
754
{
755
    return m_qmakeCommand;
con's avatar
con committed
756
757
758
759
760
761
762
763
764
}

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

QString QtVersion::mkspec() const
{
dt's avatar
dt committed
765
    updateToolChainAndMkspec();
con's avatar
con committed
766
767
768
    return m_mkspec;
}

dt's avatar
dt committed
769
770
QString QtVersion::mkspecPath() const
{
dt's avatar
dt committed
771
    updateToolChainAndMkspec();
dt's avatar
dt committed
772
773
774
    return m_mkspecFullPath;
}

775
776
777
778
779
780
bool QtVersion::isBuildWithSymbianSbsV2() const
{
    updateToolChainAndMkspec();
    return m_isBuildUsingSbsV2;
}

781
782
QString QtVersion::qtVersionString() const
{
783
784
785
786
787
788
789
790
    if (m_qtVersionString.isNull()) {
        QFileInfo qmake(m_qmakeCommand);
        if (qmake.exists() && qmake.isExecutable()) {
            m_qtVersionString = DebuggingHelperLibrary::qtVersionForQMake(qmake.absoluteFilePath());
        } else {
            m_qtVersionString = QLatin1String("");
        }
    }
791
792
793
    return m_qtVersionString;
}

con's avatar
con committed
794
795
796
797
798
799
QHash<QString,QString> QtVersion::versionInfo() const
{
    updateVersionInfo();
    return m_versionInfo;
}

800
void QtVersion::setDisplayName(const QString &name)
con's avatar
con committed
801
{
802
    m_displayName = name;
con's avatar
con committed
803
804
}

805
void QtVersion::setQMakeCommand(const QString& qmakeCommand)
con's avatar
con committed
806
{
807
    m_qmakeCommand = QDir::fromNativeSeparators(qmakeCommand);
808
#ifdef Q_OS_WIN
809
    m_qmakeCommand = m_qmakeCommand.toLower();
810
#endif
811
812
    m_designerCommand.clear();
    m_linguistCommand.clear();
813
    m_qmlviewerCommand.clear();
814
    m_uicCommand.clear();
815
    m_toolChainUpToDate = false;
816
817
    // TODO do i need to optimize this?
    m_versionInfoUpToDate = false;
818
    m_qtVersionString = QString();
819
    updateSourcePath();
con's avatar
con committed
820
821
822
823
}

void QtVersion::updateSourcePath()
{
824
825
826
827
    updateVersionInfo();
    const QString installData = m_versionInfo["QT_INSTALL_DATA"];
    m_sourcePath = installData;
    QFile qmakeCache(installData + QLatin1String("/.qmake.cache"));
con's avatar
con committed
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
    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;
            }
        }
    }
843
    m_sourcePath = QDir::cleanPath(m_sourcePath);
dt's avatar
dt committed
844
845
846
#ifdef Q_OS_WIN
    m_sourcePath = m_sourcePath.toLower();
#endif
con's avatar
con committed
847
848
849
850
}

// 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
851
// To find out whether we already have a qtversion for that directory call
con's avatar
con committed
852
// QtVersion *QtVersionManager::qtVersionForDirectory(const QString directory);
853
QString QtVersionManager::findQMakeBinaryFromMakefile(const QString &makefile)
con's avatar
con committed
854
855
{
    bool debugAdding = false;
856
857
858
    QFile fi(makefile);
    if (fi.exists() && fi.open(QFile::ReadOnly)) {
        QTextStream ts(&fi);
859
        QRegExp r1("QMAKE\\s*=(.*)");
con's avatar
con committed
860
861
862
863
864
865
        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());
866
                QString qmakePath = qmake.filePath();
867
#ifdef Q_OS_WIN
868
869
                if (!qmakePath.endsWith(QLatin1String(".exe")))
                    qmakePath.append(QLatin1String(".exe"));
870
#endif
871
                // Is qmake still installed?
872
873
874
875
876
877
                QFileInfo fi(qmakePath);
                if (fi.exists()) {
                    qmakePath = fi.absoluteFilePath();
#ifdef Q_OS_WIN
                    qmakePath = qmakePath.toLower();
#endif
878
                    return qmakePath;
879
                }
con's avatar
con committed
880
881
882
            }
        }
    }
883
    return QString();
con's avatar
con committed
884
885
}

886
QtVersion *QtVersionManager::qtVersionForQMakeBinary(const QString &qmakePath)
con's avatar
con committed
887
888
{
   foreach(QtVersion *v, versions()) {
Tobias Hunger's avatar
Tobias Hunger committed
889
890
891
892
893
       if (v->qmakeCommand() == qmakePath) {
           return v;
           break;
       }
   }
con's avatar
con committed
894
895
896
   return 0;
}

897
void dumpQMakeAssignments(const QList<QMakeAssignment> &list)
con's avatar
con committed
898
{
899
    foreach(const QMakeAssignment &qa, list) {
900
901
902
903
        qDebug()<<qa.variable<<qa.op<<qa.value;
    }
}

904
bool QtVersionManager::makefileIsFor(const QString &makefile, const QString &proFile)
905
906
907
908
{
    if (proFile.isEmpty())
        return true;

909
    QString line = findQMakeLine(makefile, QLatin1String("# Project:")).trimmed();
910
911
912
913
914
915
    if (line.isEmpty())
        return false;

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

916
    QFileInfo srcFileInfo(QFileInfo(makefile).absoluteDir(), line);
917
918
919
920
    QFileInfo proFileInfo(proFile);
    return srcFileInfo == proFileInfo;
}

921
QPair<QtVersion::QmakeBuildConfigs, QString> QtVersionManager::scanMakeFile(const QString &makefile, QtVersion::QmakeBuildConfigs defaultBuildConfig)
922
{
923
924
    if (debug)
        qDebug()<<"ScanMakeFile, the gory details:";
925
    QtVersion::QmakeBuildConfigs result = defaultBuildConfig;
926
    QString result2;
927

928
    QString line = findQMakeLine(makefile, QLatin1String("# Command:"));
929
    if (!line.isEmpty()) {
930
931
        if (debug)
            qDebug()<<"Found line"<<line;
932
933
934
        line = trimLine(line);
        QList<QMakeAssignment> assignments;
        QList<QMakeAssignment> afterAssignments;
935
        parseArgs(line, &assignments, &afterAssignments, &result2);
936

937
938
939
940
941
942
        if (debug) {
            dumpQMakeAssignments(assignments);
            if (!afterAssignments.isEmpty())
                qDebug()<<"-after";
            dumpQMakeAssignments(afterAssignments);
        }
943
944
945
946
947

        // 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
948
949
        if (debug)
            dumpQMakeAssignments(assignments);
950

951
        foreach(const QMakeAssignment &qa, assignments)
952
            Utils::QtcProcess::addArg(&result2, qa.variable + qa.op + qa.value);
953
        if (!afterAssignments.isEmpty()) {
954
            Utils::QtcProcess::addArg(&result2, QLatin1String("-after"));
955
            foreach(const QMakeAssignment &qa, afterAssignments)
956
                Utils::QtcProcess::addArg(&result2, qa.variable + qa.op + qa.value);
957
958
959
960
        }
    }

    // Dump the gathered information:
961
962
963
964
965
966
967
968
969
970
    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";
    }
971
972
973
    return qMakePair(result, result2);
}

974
QString QtVersionManager::findQMakeLine(const QString &makefile, const QString &key)
975
{
976
977
978
    QFile fi(makefile);
    if (fi.exists() && fi.open(QFile::ReadOnly)) {
        QTextStream ts(&fi);
con's avatar
con committed
979
        while (!ts.atEnd()) {
980
            const QString line = ts.readLine();
981
            if (line.startsWith(key))
982
983
984
985
986
987
988
989
990
991
992
                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
993
    const int firstSpace = line.indexOf(QLatin1Char(' '), 11);
994
995
996
    return line.mid(firstSpace).trimmed();
}

997
void QtVersionManager::parseArgs(const QString &args, QList<QMakeAssignment> *assignments, QList<QMakeAssignment> *afterAssignments, QString *additionalArguments)
998
999
1000
1001
{
    QRegExp regExp("([^\\s\\+-]*)\\s*(\\+=|=|-=|~=)(.*)");
    bool after = false;
    bool ignoreNext = false;
1002
1003
1004
    *additionalArguments = args;
    Utils::QtcProcess::ArgIterator ait(additionalArguments);
    while (ait.next()) {
1005
1006
1007
        if (ignoreNext) {
            // Ignoring
            ignoreNext = false;
1008
1009
            ait.deleteArg();
        } else if (ait.value() == QLatin1String("-after")) {
1010
            after = true;
1011
1012
1013
            ait.deleteArg();
        } else if (ait.value().contains(QLatin1Char('='))) {
            if (regExp.exactMatch(ait.value())) {
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
                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";
            }