qtversionmanager.cpp 69.2 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 "qt-s60/gccetoolchain.h"
#include "qt-s60/winscwtoolchain.h"
48

49
#include "qmlobservertool.h"
50
#include "qmldumptool.h"
51
52
#include "qmldebugginglibrary.h"

53
#include <projectexplorer/debugginghelper.h>
54
#include <projectexplorer/gnumakeparser.h>
55
#include <projectexplorer/projectexplorer.h>
56
#include <projectexplorer/projectexplorerconstants.h>
57
#include <projectexplorer/toolchainmanager.h>
58
#include <projectexplorer/cesdkhandler.h>
59
60
#include <projectexplorer/gcctoolchain.h>
#include <projectexplorer/toolchainmanager.h>
61
#include <utils/synchronousprocess.h>
62
#include <coreplugin/coreconstants.h>
63
#include <coreplugin/icore.h>
kh1's avatar
kh1 committed
64
#include <coreplugin/helpmanager.h>
65
#include <extensionsystem/pluginmanager.h>
hjk's avatar
hjk committed
66
#include <utils/qtcassert.h>
67
#include <utils/qtcprocess.h>
68
69
70
#ifdef Q_OS_WIN
#    include <utils/winutils.h>
#endif
con's avatar
con committed
71

72
#include <QtCore/QFile>
hjk's avatar
hjk committed
73
#include <QtCore/QProcess>
con's avatar
con committed
74
75
#include <QtCore/QSettings>
#include <QtCore/QTime>
76
#include <QtCore/QTimer>
77
#include <QtCore/QTextStream>
78
#include <QtCore/QDir>
79
80
#include <QtGui/QApplication>
#include <QtGui/QDesktopServices>
con's avatar
con committed
81

82
83
84
85
using namespace Qt4ProjectManager;
using namespace Qt4ProjectManager::Internal;

using ProjectExplorer::DebuggingHelperLibrary;
con's avatar
con committed
86
87
88

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

91
92
enum { debug = 0 };

93
94
95
96
97
98
99
100
101
102
103
104
105
106
template<class T>
static T *createToolChain(const QString &id)
{
    QList<ProjectExplorer::ToolChainFactory *> factories =
            ExtensionSystem::PluginManager::instance()->getObjects<ProjectExplorer::ToolChainFactory>();
    foreach (ProjectExplorer::ToolChainFactory *f, factories) {
       if (f->id() == id) {
           Q_ASSERT(f->canCreate());
           return static_cast<T *>(f->create());
       }
    }
    return 0;
}

107
108
109
110
111
112
113

// prefer newer qts otherwise compare on id
bool qtVersionNumberCompare(QtVersion *a, QtVersion *b)
{
    return a->qtVersion() > b->qtVersion() || (a->qtVersion() == b->qtVersion() && a->uniqueId() < b->uniqueId());
}

114
115
116
// --------------------------------------------------------------------------
// QtVersionManager
// --------------------------------------------------------------------------
117
QtVersionManager *QtVersionManager::m_self = 0;
118

con's avatar
con committed
119
120
121
QtVersionManager::QtVersionManager()
    : m_emptyVersion(new QtVersion)
{
122
    m_self = this;
123
    QSettings *s = Core::ICore::instance()->settings();
con's avatar
con committed
124
125
126
127
128
129
130
131
132
133
134
135

    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
136
137
        else if (m_idcount < id)
            m_idcount = id + 1;
138
        bool isAutodetected;
139
140
        QString autodetectionSource;
        if (s->contains("isAutodetected")) {
141
            isAutodetected = s->value("isAutodetected", false).toBool();
142
143
            autodetectionSource = s->value("autodetectionSource", QString()).toString();
        } else {// compatibility
144
            isAutodetected = s->value("IsSystemVersion", false).toBool();
145
146
147
            if (isAutodetected)
                autodetectionSource = QLatin1String(PATH_AUTODETECTION_SOURCE);
        }
148
149
150
        QString qmakePath = s->value("QMakePath").toString();
        if (qmakePath.isEmpty()) {
            QString path = s->value("Path").toString();
151
152
153
154
155
156
157
158
159
160
161
            if (!path.isEmpty()) {
                foreach(const QString& command, ProjectExplorer::DebuggingHelperLibrary::possibleQMakeCommands())
                {
                    QFileInfo fi(path + "/bin/" + command);
                    if (fi.exists())
                    {
                        qmakePath = fi.filePath();
                        break;
                    }
                }
            }
162
        }
con's avatar
con committed
163
        QtVersion *version = new QtVersion(s->value("Name").toString(),
164
                                           qmakePath,
con's avatar
con committed
165
                                           id,
166
167
                                           isAutodetected,
                                           autodetectionSource);
168
        version->setS60SDKDirectory(s->value("S60SDKDirectory").toString());
169
        version->setSbsV2Directory(s->value(QLatin1String("SBSv2Directory")).toString());
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201

        // Update from 2.1 or earlier:
        QString mingwDir = s->value(QLatin1String("MingwDirectory")).toString();
        if (!mingwDir.isEmpty()) {
            ProjectExplorer::MingwToolChain *tc = createToolChain<ProjectExplorer::MingwToolChain>(ProjectExplorer::Constants::MINGW_TOOLCHAIN_ID);
            if (tc) {
                tc->setCompilerPath(QDir::fromNativeSeparators(mingwDir) + QLatin1String("/bin/gcc.exe"));
                tc->setDisplayName(tr("Mingw from %1").arg(version->displayName()));
                ProjectExplorer::ToolChainManager::instance()->registerToolChain(tc);
            }
        }
        QString mwcDir = s->value(QLatin1String("MwcDirectory")).toString();
        if (!mwcDir.isEmpty()) {
            WinscwToolChain *tc = createToolChain<WinscwToolChain>(Constants::WINSCW_TOOLCHAIN_ID);
            if (tc) {
                tc->setCompilerPath(QDir::fromNativeSeparators(mwcDir)
                                    + QLatin1String("/x86Build/Symbian_Tools/Command_Line_Tools/mwwinrc.exe"));
                tc->setDisplayName(tr("WINSCW from %1").arg(version->displayName()));
                ProjectExplorer::ToolChainManager::instance()->registerToolChain(tc);
            }
        }
        QString gcceDir = s->value(QLatin1String("GcceDirectory")).toString();
        if (!gcceDir.isEmpty()) {
            GcceToolChain *tc = createToolChain<GcceToolChain>(Constants::GCCE_TOOLCHAIN_ID);
            if (tc) {
                tc->setCompilerPath(QDir::fromNativeSeparators(gcceDir)
                                    + QLatin1String("/bin/arm-none-symbianelf-g++.exe"));
                tc->setDisplayName(tr("GCCE from %1").arg(version->displayName()));
                ProjectExplorer::ToolChainManager::instance()->registerToolChain(tc);
            }
        }

202
        m_versions.insert(version->uniqueId(), version);
con's avatar
con committed
203
204
205
206
207
208
209
210
    }
    s->endArray();

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

    writeVersionsIntoSettings();
211

con's avatar
con committed
212
    updateDocumentation();
213

214
215
    // cannot call from ctor, needs to get connected extenernally first
    QTimer::singleShot(0, this, SLOT(updateExamples()));
con's avatar
con committed
216
217
218
219
220
221
222
223
224
225
}

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

226
QtVersionManager *QtVersionManager::instance()
227
{
228
    return m_self;
229
230
}

con's avatar
con committed
231
232
void QtVersionManager::addVersion(QtVersion *version)
{
233
    QTC_ASSERT(version != 0, return);
234
    if (m_versions.contains(version->uniqueId()))
235
236
        return;

237
    int uniqueId = version->uniqueId();
238
239
    m_versions.insert(uniqueId, version);

240
    emit qtVersionsChanged(QList<int>() << uniqueId);
241
    writeVersionsIntoSettings();
con's avatar
con committed
242
243
}

244
245
void QtVersionManager::removeVersion(QtVersion *version)
{
246
    QTC_ASSERT(version != 0, return);
247
248
    m_versions.remove(version->uniqueId());
    emit qtVersionsChanged(QList<int>() << version->uniqueId());
249
250
251
252
    writeVersionsIntoSettings();
    delete version;
}

Tobias Hunger's avatar
Tobias Hunger committed
253
254
255
256
257
258
259
260
261
bool QtVersionManager::supportsTargetId(const QString &id) const
{
    foreach (QtVersion *version, m_versions) {
        if (version->supportsTargetId(id))
            return true;
    }
    return false;
}

262
QList<QtVersion *> QtVersionManager::versionsForTargetId(const QString &id, const QtVersionNumber &minimumQtVersion) const
Tobias Hunger's avatar
Tobias Hunger committed
263
264
265
{
    QList<QtVersion *> targetVersions;
    foreach (QtVersion *version, m_versions) {
266
        if (version->supportsTargetId(id) && version->qtVersion() >= minimumQtVersion)
Tobias Hunger's avatar
Tobias Hunger committed
267
268
            targetVersions.append(version);
    }
269
    qSort(targetVersions.begin(), targetVersions.end(), &qtVersionNumberCompare);
Tobias Hunger's avatar
Tobias Hunger committed
270
271
272
273
274
275
276
277
278
279
280
    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
281
282
void QtVersionManager::updateDocumentation()
{
kh1's avatar
kh1 committed
283
    Core::HelpManager *helpManager = Core::HelpManager::instance();
dt's avatar
dt committed
284
    Q_ASSERT(helpManager);
con's avatar
con committed
285
286
    QStringList files;
    foreach (QtVersion *version, m_versions) {
287
288
289
290
291
292
        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
293
294
295
296
    }
    helpManager->registerDocumentation(files);
}

297
298
299
void QtVersionManager::updateExamples()
{
    QtVersion *version = 0;
300
301
    QList<QtVersion*> candidates;

Tobias Hunger's avatar
Tobias Hunger committed
302
    // try to find a version which has both, demos and examples
303
304
305
306
307
    foreach (version, m_versions) {
        if (version->hasExamples() && version->hasDemos())
        candidates.append(version);
    }

308
    // in SDKs, we want to prefer the Qt version shipping with the SDK
309
310
311
    QSettings *settings = Core::ICore::instance()->settings();
    QString preferred = settings->value(QLatin1String("PreferredQMakePath")).toString();
    preferred = QDir::fromNativeSeparators(preferred);
312
    if (!preferred.isEmpty()) {
313
314
315
316
317
#ifdef Q_OS_WIN
        preferred = preferred.toLower();
        if (!preferred.endsWith(QLatin1String(".exe")))
            preferred.append(QLatin1String(".exe"));
#endif
318
319
320
321
322
323
        foreach (version, candidates) {
            if (version->qmakeCommand() == preferred) {
                emit updateExamples(version->examplesPath(), version->demosPath(), version->sourcePath());
                return;
            }
        }
324
325
326
327
328
329
    }

    // prefer versions with declarative examples
    foreach (version, candidates) {
        if (QDir(version->examplesPath()+"/declarative").exists()) {
            emit updateExamples(version->examplesPath(), version->demosPath(), version->sourcePath());
330
331
332
            return;
        }
    }
333
334
335
336
337
338
339
340

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

341
342
}

con's avatar
con committed
343
344
345
346
347
348
349
int QtVersionManager::getUniqueId()
{
    return m_idcount++;
}

void QtVersionManager::writeVersionsIntoSettings()
{
350
    QSettings *s = Core::ICore::instance()->settings();
351
    s->beginWriteArray(QtVersionsSectionName);
352
    QMap<int, QtVersion *>::const_iterator it = m_versions.constBegin();
con's avatar
con committed
353
    for (int i = 0; i < m_versions.size(); ++i) {
354
        const QtVersion *version = it.value();
con's avatar
con committed
355
        s->setArrayIndex(i);
356
        s->setValue("Name", version->displayName());
357
358
359
        // for downwards compat
        s->setValue("Path", version->versionInfo().value("QT_INSTALL_DATA"));
        s->setValue("QMakePath", version->qmakeCommand());
360
361
362
363
        s->setValue("Id", version->uniqueId());
        s->setValue("isAutodetected", version->isAutodetected());
        if (version->isAutodetected())
            s->setValue("autodetectionSource", version->autodetectionSource());
364
        s->setValue("S60SDKDirectory", version->s60SDKDirectory());
365
        s->setValue(QLatin1String("SBSv2Directory"), version->sbsV2Directory());
366
        ++it;
con's avatar
con committed
367
368
369
370
    }
    s->endArray();
}

371
QList<QtVersion *> QtVersionManager::versions() const
con's avatar
con committed
372
{
373
374
375
376
377
    QList<QtVersion *> versions;
    foreach (QtVersion *version, m_versions)
        versions << version;
    qSort(versions.begin(), versions.end(), &qtVersionNumberCompare);
    return versions;
con's avatar
con committed
378
379
}

380
381
382
383
384
385
386
QList<QtVersion *> QtVersionManager::validVersions() const
{
    QList<QtVersion *> results;
    foreach(QtVersion *v, m_versions) {
        if (v->isValid())
            results.append(v);
    }
387
    qSort(results.begin(), results.end(), &qtVersionNumberCompare);
388
389
390
    return results;
}

391
392
bool QtVersionManager::isValidId(int id) const
{
393
    return m_versions.contains(id);
394
395
}

con's avatar
con committed
396
397
QtVersion *QtVersionManager::version(int id) const
{
398
399
400
401
    QMap<int, QtVersion *>::const_iterator it = m_versions.find(id);
    if (it == m_versions.constEnd())
        return m_emptyVersion;
    return it.value();
con's avatar
con committed
402
403
}

404
// FIXME: Rework this!
con's avatar
con committed
405
406
407
void QtVersionManager::addNewVersionsFromInstaller()
{
    // Add new versions which may have been installed by the WB installer in the form:
408
    // NewQtVersions="qt 4.3.2=c:\\qt\\qt432\bin\qmake.exe;qt embedded=c:\\qtembedded;"
409
    // or NewQtVersions="qt 4.3.2=c:\\qt\\qt432bin\qmake.exe;
410
    // i.e.
411
    // NewQtVersions="versionname=pathtoversion=s60sdk;"
con's avatar
con committed
412
    // Duplicate entries are not added, the first new version is set as default.
413
    QSettings *settings = Core::ICore::instance()->settings();
414
    QSettings *globalSettings = Core::ICore::instance()->settings(QSettings::SystemScope);
415

416
417
418
419
    QDateTime lastUpdateFromGlobalSettings = globalSettings->value(
            QLatin1String("General/LastQtVersionUpdate")).toDateTime();

    const QFileInfo gsFi(globalSettings->fileName());
420
421
    if ( !lastUpdateFromGlobalSettings.isNull() &&
         (!gsFi.exists() || (gsFi.lastModified() > lastUpdateFromGlobalSettings)) )
con's avatar
con committed
422
423
        return;

424
425
426
427
428
    if (!globalSettings->contains(newQtVersionsKey) &&
        !globalSettings->contains(QLatin1String("Installer/")+newQtVersionsKey))
    {
        return;
    }
con's avatar
con committed
429
430

    QString newVersionsValue = settings->value(newQtVersionsKey).toString();
431
432
433
    if (newVersionsValue.isEmpty())
        newVersionsValue = settings->value(QLatin1String("Installer/")+newQtVersionsKey).toString();

con's avatar
con committed
434
    QStringList newVersionsList = newVersionsValue.split(';', QString::SkipEmptyParts);
435
    foreach (const QString &newVersion, newVersionsList) {
con's avatar
con committed
436
        QStringList newVersionData = newVersion.split('=');
437
        if (newVersionData.count() >= 2) {
438
            if (QFile::exists(newVersionData[1])) {
con's avatar
con committed
439
                QtVersion *version = new QtVersion(newVersionData[0], newVersionData[1], m_idcount++ );
hjk's avatar
hjk committed
440
                if (newVersionData.count() >= 3)
441
                    version->setS60SDKDirectory(QDir::fromNativeSeparators(newVersionData[2]));
442
                if (newVersionData.count() >= 4)
443
                    version->setSbsV2Directory(QDir::fromNativeSeparators(newVersionData[3]));
con's avatar
con committed
444
445
446

                bool versionWasAlreadyInList = false;
                foreach(const QtVersion * const it, m_versions) {
447
                    if (QDir(version->qmakeCommand()).canonicalPath() == QDir(it->qmakeCommand()).canonicalPath()) {
con's avatar
con committed
448
449
450
451
452
453
                        versionWasAlreadyInList = true;
                        break;
                    }
                }

                if (!versionWasAlreadyInList) {
454
                    m_versions.insert(version->uniqueId(), version);
con's avatar
con committed
455
456
457
458
459
460
461
                } else {
                    // clean up
                    delete version;
                }
            }
        }
    }
462
    settings->setValue(QLatin1String("General/LastQtVersionUpdate"), QDateTime::currentDateTime());
con's avatar
con committed
463
464
465
466
467
}

void QtVersionManager::updateSystemVersion()
{
    bool haveSystemVersion = false;
468
    QString systemQMakePath = DebuggingHelperLibrary::findSystemQt(Utils::Environment::systemEnvironment());
469
470
    if (systemQMakePath.isNull())
        systemQMakePath = tr("<not found>");
471

con's avatar
con committed
472
    foreach (QtVersion *version, m_versions) {
473
474
        if (version->isAutodetected()
            && version->autodetectionSource() == PATH_AUTODETECTION_SOURCE) {
475
            version->setQMakeCommand(systemQMakePath);
476
            version->setDisplayName(tr("Qt in PATH"));
con's avatar
con committed
477
478
479
480
481
            haveSystemVersion = true;
        }
    }
    if (haveSystemVersion)
        return;
482
    QtVersion *version = new QtVersion(tr("Qt in PATH"),
483
                                       systemQMakePath,
con's avatar
con committed
484
                                       getUniqueId(),
485
486
                                       true,
                                       PATH_AUTODETECTION_SOURCE);
487
    m_versions.insert(version->uniqueId(), version);
con's avatar
con committed
488
489
}

Tobias Hunger's avatar
Tobias Hunger committed
490
QtVersion *QtVersionManager::emptyVersion() const
con's avatar
con committed
491
{
Tobias Hunger's avatar
Tobias Hunger committed
492
    return m_emptyVersion;
con's avatar
con committed
493
494
}

495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
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;
510
511
    if (a->m_displayName != b->displayName())
        return false;
512
513
514
    return true;
}

Tobias Hunger's avatar
Tobias Hunger committed
515
void QtVersionManager::setNewQtVersions(QList<QtVersion *> newVersions)
516
{
517
518
519
520
521
522
523
524
525
526
527
    // 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);

    QList<int> changedVersions;
    // So we trying to find the minimal set of changed versions,
    // iterate over both sorted list

    // newVersions and oldVersions iterator
528
529
    QList<QtVersion *>::const_iterator nit, nend;
    QMap<int, QtVersion *>::const_iterator oit, oend;
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
    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;
549
550
        }
    }
551
552
553

    while (nit != nend) {
        changedVersions.push_back((*nit)->uniqueId());
554
        ++nit;
555
556
557
558
    }

    while (oit != oend) {
        changedVersions.push_back((*oit)->uniqueId());
559
        ++oit;
560
561
    }

562
563
    qDeleteAll(m_versions);
    m_versions.clear();
564
565
    foreach (QtVersion *v, sortedNewVersions)
        m_versions.insert(v->uniqueId(), v);
566
567

    if (!changedVersions.isEmpty())
568
569
        updateDocumentation();

570
    updateExamples();
571
    writeVersionsIntoSettings();
572
573
574

    if (!changedVersions.isEmpty())
        emit qtVersionsChanged(changedVersions);
575
576
}

577
578
579
// --------------------------------------------------------------------------
// QtVersion
// --------------------------------------------------------------------------
con's avatar
con committed
580

581
QtVersion::QtVersion(const QString &name, const QString &qmakeCommand, int id,
582
                     bool isAutodetected, const QString &autodetectionSource)
583
    : m_displayName(name),
584
    m_isAutodetected(isAutodetected),
585
    m_autodetectionSource(autodetectionSource),
586
    m_hasDebuggingHelper(false),
587
    m_hasQmlDump(false),
588
    m_hasQmlDebuggingLibrary(false),
589
    m_hasQmlObserver(false),
590
    m_abiUpToDate(false),
591
    m_versionInfoUpToDate(false),
592
593
594
    m_notInstalled(false),
    m_defaultConfigIsDebug(true),
    m_defaultConfigIsDebugAndRelease(true),
595
596
    m_hasExamples(false),
    m_hasDemos(false),
597
    m_hasDocumentation(false)
con's avatar
con committed
598
{
hjk's avatar
hjk committed
599
    if (id == -1)
con's avatar
con committed
600
601
602
        m_id = getUniqueId();
    else
        m_id = id;
603
    setQMakeCommand(qmakeCommand);
con's avatar
con committed
604
605
}

606
QtVersion::QtVersion(const QString &name, const QString &qmakeCommand,
607
                     bool isAutodetected, const QString &autodetectionSource)
608
    : m_displayName(name),
609
610
    m_isAutodetected(isAutodetected),
    m_autodetectionSource(autodetectionSource),
611
    m_hasDebuggingHelper(false),
612
    m_hasQmlDump(false),
613
    m_hasQmlDebuggingLibrary(false),
614
    m_hasQmlObserver(false),
615
    m_abiUpToDate(false),
616
617
618
619
620
621
    m_versionInfoUpToDate(false),
    m_notInstalled(false),
    m_defaultConfigIsDebug(true),
    m_defaultConfigIsDebugAndRelease(true),
    m_hasExamples(false),
    m_hasDemos(false),
622
    m_hasDocumentation(false)
623
624
625
626
627
628
629
630
631
632
{
    m_id = getUniqueId();
    setQMakeCommand(qmakeCommand);
}


QtVersion::QtVersion(const QString &qmakeCommand, bool isAutodetected, const QString &autodetectionSource)
    : m_isAutodetected(isAutodetected),
    m_autodetectionSource(autodetectionSource),
    m_hasDebuggingHelper(false),
633
    m_hasQmlDump(false),
634
    m_hasQmlDebuggingLibrary(false),
635
    m_hasQmlObserver(false),
636
    m_abiUpToDate(false),
637
638
639
640
641
642
    m_versionInfoUpToDate(false),
    m_notInstalled(false),
    m_defaultConfigIsDebug(true),
    m_defaultConfigIsDebugAndRelease(true),
    m_hasExamples(false),
    m_hasDemos(false),
643
    m_hasDocumentation(false)
con's avatar
con committed
644
645
{
    m_id = getUniqueId();
646
    setQMakeCommand(qmakeCommand);
647
    m_displayName = qtVersionString();
con's avatar
con committed
648
649
}

650
QtVersion::QtVersion()
651
    :  m_id(-1),
652
653
    m_isAutodetected(false),
    m_hasDebuggingHelper(false),
654
    m_hasQmlDump(false),
655
    m_hasQmlDebuggingLibrary(false),
656
    m_hasQmlObserver(false),
657
    m_abiUpToDate(false),
658
659
660
661
662
663
    m_versionInfoUpToDate(false),
    m_notInstalled(false),
    m_defaultConfigIsDebug(true),
    m_defaultConfigIsDebugAndRelease(true),
    m_hasExamples(false),
    m_hasDemos(false),
664
    m_hasDocumentation(false)
665
{
666
    setQMakeCommand(QString());
667
668
}

669
670
671
672
QtVersion::~QtVersion()
{
}

673
674
675
676
QString QtVersion::toHtml() const
{
    QString rc;
    QTextStream str(&rc);
Robert Loehning's avatar
Robert Loehning committed
677
    str << "<html><body><table>";
678
    str << "<tr><td><b>" << QtVersionManager::tr("Name:")
679
        << "</b></td><td>" << displayName() << "</td></tr>";
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
    if (!isValid()) {
        str << "<tr><td colspan=2><b>" + QtVersionManager::tr("Invalid Qt version") +"</b></td></tr>";
    } else {
        QString prefix = QLatin1String("<tr><td><b>") + QtVersionManager::tr("ABI:") + QLatin1String("</b></td>");
        foreach (const ProjectExplorer::Abi &abi, qtAbis()) {
            str << prefix << "<td>" << abi.toString() << "</td></tr>";
            prefix = QLatin1String("<tr><td></td>");
        }
        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>";
        updateAbiAndMkspec();
        if (m_defaultConfigIsDebug || m_defaultConfigIsDebugAndRelease) {
            str << "<tr><td><b>" << QtVersionManager::tr("Default:") << "</b></td><td>"
                << (m_defaultConfigIsDebug ? "debug" : "release");
            if (m_defaultConfigIsDebugAndRelease)
                str << " debug_and_release";
            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>";
        }
713
    }
Robert Loehning's avatar
Robert Loehning committed
714
    str << "</table></body></html>";
715
716
717
    return rc;
}

718
719
720
721
722
723
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
724
        // We can not support shadow building with the ABLD system
725
726
727
728
729
        return false;
    }
    return true;
}

730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
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;
}

745
QList<ProjectExplorer::Task>
746
QtVersion::reportIssues(const QString &proFile, const QString &buildDir)
747
748
749
{
    QList<ProjectExplorer::Task> results;

750
751
752
    QString tmpBuildDir = QDir(buildDir).absolutePath();
    if (!tmpBuildDir.endsWith(QLatin1Char('/')))
        tmpBuildDir.append(QLatin1Char('/'));
753

Friedemann Kleint's avatar
Friedemann Kleint committed
754
755
756
757
    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,
758
                                             QLatin1String(ProjectExplorer::Constants::TASK_CATEGORY_BUILDSYSTEM)));
Friedemann Kleint's avatar
Friedemann Kleint committed
759
    }
760
761
762

    QFileInfo qmakeInfo(qmakeCommand());
    if (!qmakeInfo.exists() ||
Friedemann Kleint's avatar
Friedemann Kleint committed
763
764
765
766
767
        !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,
768
                                             QLatin1String(ProjectExplorer::Constants::TASK_CATEGORY_BUILDSYSTEM)));
Friedemann Kleint's avatar
Friedemann Kleint committed
769
    }
770

771
    QString sourcePath = QFileInfo(proFile).absolutePath();
772
773
    if (!sourcePath.endsWith(QLatin1Char('/')))
        sourcePath.append(QLatin1Char('/'));
774
775
776
777
778
779
780
    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",
781
                                                        "The build directory needs to be at the same level as the source directory.");
782

783
784
785
786
        results.append(ProjectExplorer::Task(ProjectExplorer::Task::Warning, msg, QString(), -1,
                                             QLatin1String(ProjectExplorer::Constants::TASK_CATEGORY_BUILDSYSTEM)));
    }

787
788
789
790
791
792
793
    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;
}

794
QString QtVersion::displayName() const
con's avatar
con committed
795
{
796
    return m_displayName;
con's avatar
con committed
797
798
}

799
QString QtVersion::qmakeCommand() const
con's avatar
con committed
800
{
801
    return m_qmakeCommand;
con's avatar
con committed
802
803
804
805
806
807
808
809
810
}

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

QString QtVersion::mkspec() const
{
811
    updateAbiAndMkspec();
con's avatar
con committed
812
813
814
    return m_mkspec;
}

dt's avatar
dt committed
815
816
QString QtVersion::mkspecPath() const
{
817
    updateAbiAndMkspec();
dt's avatar
dt committed
818
819
820
    return m_mkspecFullPath;
}

821
822
bool QtVersion::isBuildWithSymbianSbsV2() const
{
823
    updateAbiAndMkspec();
824
825
826
    return m_isBuildUsingSbsV2;
}

827
828
QString QtVersion::qtVersionString() const
{
829
830
831
832
833
834
835
836
    if (m_qtVersionString.isNull()) {
        QFileInfo qmake(m_qmakeCommand);
        if (qmake.exists() && qmake.isExecutable()) {
            m_qtVersionString = DebuggingHelperLibrary::qtVersionForQMake(qmake.absoluteFilePath());
        } else {
            m_qtVersionString = QLatin1String("");
        }
    }
837
838
839
    return m_qtVersionString;
}

840
QtVersionNumber QtVersion::qtVersion() const
841
{
842
843
    //todo cache this;
    return QtVersionNumber(qtVersionString());
844
845
}

con's avatar
con committed
846
847
848
849
850
851
QHash<QString,QString> QtVersion::versionInfo() const
{
    updateVersionInfo();
    return m_versionInfo;
}

852
void QtVersion::setDisplayName(const QString &name)
con's avatar
con committed
853
{
854
    m_displayName = name;
con's avatar
con committed
855
856
}

857
void QtVersion::setQMakeCommand(const QString& qmakeCommand)
con's avatar
con committed
858
{
859
    m_qmakeCommand = QDir::fromNativeSeparators(qmakeCommand);
860
#ifdef Q_OS_WIN
861
    m_qmakeCommand = m_qmakeCommand.toLower();
862
#endif
863
864
    m_designerCommand.clear();
    m_linguistCommand.clear();
865
    m_qmlviewerCommand.clear();
866
    m_uicCommand.clear();
867
    m_abiUpToDate = false;
868
869
    // TODO do i need to optimize this?
    m_versionInfoUpToDate = false;
870
    m_qtVersionString = QString();
871
    updateSourcePath();
con's avatar
con committed
872
873
874
875
}

void QtVersion::updateSourcePath()
{
876
877
878
879
    updateVersionInfo();
    const QString installData = m_versionInfo["QT_INSTALL_DATA"];
    m_sourcePath = installData;
    QFile qmakeCache(installData + QLatin1String("/.qmake.cache"));
con's avatar
con committed
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
    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;
            }
        }
    }
895
    m_sourcePath = QDir::cleanPath(m_sourcePath);
dt's avatar
dt committed
896
897
898
#ifdef Q_OS_WIN
    m_sourcePath = m_sourcePath.toLower();
#endif
con's avatar
con committed
899
900
901
902
}

// 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
903
// To find out whether we already have a qtversion for that directory call
con's avatar
con committed
904
// QtVersion *QtVersionManager::qtVersionForDirectory(const QString directory);
905
QString QtVersionManager::findQMakeBinaryFromMakefile(const QString &makefile)
con's avatar
con committed
906
907
{
    bool debugAdding = false;
908
909
910
    QFile fi(makefile);
    if (fi.exists() && fi.open(QFile::ReadOnly)) {
        QTextStream ts(&fi);
911
        QRegExp r1("QMAKE\\s*=(.*)");
con's avatar
con committed
912
913
914
915
916
917
        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());
918
                QString qmakePath = qmake.filePath();
919
#ifdef Q_OS_WIN
920
921
                if (!qmakePath.endsWith(QLatin1String(".exe")))
                    qmakePath.append(QLatin1String(".exe"));
922
#endif
923
                // Is qmake still installed?
924
925
926
927
928
929
                QFileInfo fi(qmakePath);
                if (fi.exists()) {
                    qmakePath = fi.absoluteFilePath();
#ifdef Q_OS_WIN
                    qmakePath = qmakePath.toLower();
#endif
930
                    return qmakePath;
931
                }
con's avatar
con committed
932
933
934
            }
        }
    }
935
    return QString();
con's avatar
con committed
936
937
}

938
QtVersion *QtVersionManager::qtVersionForQMakeBinary(const QString &qmakePath)
con's avatar
con committed
939
940
{
   foreach(QtVersion *v, versions()) {
Tobias Hunger's avatar
Tobias Hunger committed
941
942
943
944
945
       if (v->qmakeCommand() == qmakePath) {
           return v;
           break;
       }
   }
con's avatar
con committed
946
947
948
   return 0;
}

949
void dumpQMakeAssignments(const QList<QMakeAssignment> &list)
con's avatar
con committed
950
{
951
    foreach(const QMakeAssignment &qa, list) {
952
953
954
955
        qDebug()<<qa.variable<<qa.op<<qa.value;
    }
}

956
bool QtVersionManager::makefileIsFor(const QString &makefile, const QString &proFile)
957
958
959
960
{
    if (proFile.isEmpty())
        return true;

961
    QString line = findQMakeLine(makefile, QLatin1String("# Project:")).trimmed();
962
963
964
965
966
967
    if (line.isEmpty())
        return false;

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

968
    QFileInfo srcFileInfo(QFileInfo(makefile).absoluteDir(), line);
969
970
971
972
    QFileInfo proFileInfo(proFile);
    return srcFileInfo == proFileInfo;
}

973
QPair<QtVersion::QmakeBuildConfigs, QString> QtVersionManager::scanMakeFile(const QString &makefile, QtVersion::QmakeBuildConfigs defaultBuildConfig)
974
{
975
976
    if (debug)
        qDebug()<<"ScanMakeFile, the gory details:";
977
    QtVersion::QmakeBuildConfigs result = defaultBuildConfig;
978
    QString result2;
979

980
    QString line = findQMakeLine(makefile, QLatin1String("# Command:"));
981
    if (!line.isEmpty()) {
982
983
        if (debug)
            qDebug()<<"Found line"<<line;
984
985
986
        line = trimLine(line);
        QList<QMakeAssignment> assignments;
        QList<QMakeAssignment> afterAssignments;
987
        parseArgs(line, &assignments, &afterAssignments, &result2);
988

989
990
991
992
993
994
        if (debug) {
            dumpQMakeAssignments(assignments);
            if (!afterAssignments.isEmpty())
                qDebug()<<"-after";
            dumpQMakeAssignments(afterAssignments);
        }
995
996
997
998
999

        // 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
1000
1001
        if (debug)
            dumpQMakeAssignments(assignments);
1002

1003
        foreach(const QMakeAssignment &qa, assignments)
1004
            Utils::QtcProcess::addArg(&result2, qa.variable + qa.op + qa.value);