OSDN Git Service

Update license.
[qt-creator-jp/qt-creator-jp.git] / src / plugins / qt4projectmanager / qtversionmanager.cpp
1 /**************************************************************************
2 **
3 ** This file is part of Qt Creator
4 **
5 ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
6 **
7 ** Contact: Nokia Corporation (info@qt.nokia.com)
8 **
9 **
10 ** GNU Lesser General Public License Usage
11 **
12 ** This file may be used under the terms of the GNU Lesser General Public
13 ** License version 2.1 as published by the Free Software Foundation and
14 ** appearing in the file LICENSE.LGPL included in the packaging of this file.
15 ** Please review the following information to ensure the GNU Lesser General
16 ** Public License version 2.1 requirements will be met:
17 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
18 **
19 ** In addition, as a special exception, Nokia gives you certain additional
20 ** rights. These rights are described in the Nokia Qt LGPL Exception
21 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
22 **
23 ** Other Usage
24 **
25 ** Alternatively, this file may be used in accordance with the terms and
26 ** conditions contained in a signed written agreement between you and Nokia.
27 **
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
30 **
31 **************************************************************************/
32
33 #include "qtversionmanager.h"
34
35 #include "qt4projectmanagerconstants.h"
36 #include "qt4target.h"
37 #include "profilereader.h"
38
39 #include "qt-maemo/maemoglobal.h"
40 #include "qt-maemo/maemomanager.h"
41 #include "qt-s60/s60manager.h"
42 #include "qt-s60/abldparser.h"
43 #include "qt-s60/sbsv2parser.h"
44 #include "qt-s60/gccetoolchain.h"
45 #include "qt-s60/winscwtoolchain.h"
46 #include "qt4basetargetfactory.h"
47
48 #include "qmlobservertool.h"
49 #include "qmldumptool.h"
50 #include "qmldebugginglibrary.h"
51
52 #include <projectexplorer/debugginghelper.h>
53 #include <projectexplorer/gnumakeparser.h>
54 #include <projectexplorer/projectexplorer.h>
55 #include <projectexplorer/projectexplorerconstants.h>
56 #include <projectexplorer/toolchainmanager.h>
57 #include <projectexplorer/cesdkhandler.h>
58 #include <projectexplorer/gcctoolchain.h>
59 #include <projectexplorer/toolchainmanager.h>
60 #include <projectexplorer/headerpath.h>
61 #include <projectexplorer/ioutputparser.h>
62 #include <projectexplorer/task.h>
63
64 #include <coreplugin/coreconstants.h>
65 #include <coreplugin/icore.h>
66 #include <coreplugin/helpmanager.h>
67
68 #include <extensionsystem/pluginmanager.h>
69
70 #include <utils/synchronousprocess.h>
71 #include <utils/qtcassert.h>
72 #include <utils/qtcprocess.h>
73 #ifdef Q_OS_WIN
74 #    include <utils/winutils.h>
75 #endif
76
77 #include <QtCore/QFile>
78 #include <QtCore/QProcess>
79 #include <QtCore/QSettings>
80 #include <QtCore/QTime>
81 #include <QtCore/QTimer>
82 #include <QtCore/QTextStream>
83 #include <QtCore/QDir>
84 #include <QtGui/QApplication>
85 #include <QtGui/QDesktopServices>
86
87 #include <algorithm>
88
89 using namespace Qt4ProjectManager;
90 using namespace Qt4ProjectManager::Internal;
91
92 using ProjectExplorer::DebuggingHelperLibrary;
93
94 static const char *QtVersionsSectionName = "QtVersions";
95 static const char *newQtVersionsKey = "NewQtVersions";
96 static const char *PATH_AUTODETECTION_SOURCE = "PATH";
97
98 enum { debug = 0 };
99
100 template<class T>
101 static T *createToolChain(const QString &id)
102 {
103     QList<ProjectExplorer::ToolChainFactory *> factories =
104             ExtensionSystem::PluginManager::instance()->getObjects<ProjectExplorer::ToolChainFactory>();
105     foreach (ProjectExplorer::ToolChainFactory *f, factories) {
106        if (f->id() == id) {
107            Q_ASSERT(f->canCreate());
108            return static_cast<T *>(f->create());
109        }
110     }
111     return 0;
112 }
113
114
115 // prefer newer qts otherwise compare on id
116 bool qtVersionNumberCompare(QtVersion *a, QtVersion *b)
117 {
118     return a->qtVersion() > b->qtVersion() || (a->qtVersion() == b->qtVersion() && a->uniqueId() < b->uniqueId());
119 }
120
121 // --------------------------------------------------------------------------
122 // QtVersionManager
123 // --------------------------------------------------------------------------
124 QtVersionManager *QtVersionManager::m_self = 0;
125
126 QtVersionManager::QtVersionManager()
127     : m_emptyVersion(new QtVersion)
128 {
129     m_self = this;
130     QSettings *s = Core::ICore::instance()->settings();
131
132     m_idcount = 1;
133     int size = s->beginReadArray(QtVersionsSectionName);
134     for (int i = 0; i < size; ++i) {
135         s->setArrayIndex(i);
136         // Find the right id
137         // Either something saved or something generated
138         // Note: This code assumes that either all ids are read from the settings
139         // or generated on the fly.
140         int id = s->value("Id", -1).toInt();
141         if (id == -1)
142             id = getUniqueId();
143         else if (m_idcount < id)
144             m_idcount = id + 1;
145         bool isAutodetected;
146         QString autodetectionSource;
147         if (s->contains("isAutodetected")) {
148             isAutodetected = s->value("isAutodetected", false).toBool();
149             autodetectionSource = s->value("autodetectionSource", QString()).toString();
150         } else {// compatibility
151             isAutodetected = s->value("IsSystemVersion", false).toBool();
152             if (isAutodetected)
153                 autodetectionSource = QLatin1String(PATH_AUTODETECTION_SOURCE);
154         }
155         QString qmakePath = s->value("QMakePath").toString();
156         if (qmakePath.isEmpty()) {
157             QString path = s->value("Path").toString();
158             if (!path.isEmpty()) {
159                 foreach(const QString& command, ProjectExplorer::DebuggingHelperLibrary::possibleQMakeCommands())
160                 {
161                     QFileInfo fi(path + "/bin/" + command);
162                     if (fi.exists())
163                     {
164                         qmakePath = fi.filePath();
165                         break;
166                     }
167                 }
168             }
169         }
170         QtVersion *version = new QtVersion(s->value("Name").toString(),
171                                            qmakePath,
172                                            id,
173                                            isAutodetected,
174                                            autodetectionSource);
175         // Make sure we do not import non-native separators from old Qt Creator versions:
176         version->setSystemRoot(QDir::fromNativeSeparators(s->value("S60SDKDirectory").toString()));
177         version->setSbsV2Directory(QDir::fromNativeSeparators(s->value(QLatin1String("SBSv2Directory")).toString()));
178
179         // Update from pre-2.2:
180         const QString mingwDir = s->value(QLatin1String("MingwDirectory")).toString();
181         if (!mingwDir.isEmpty()) {
182             QFileInfo fi(mingwDir + QLatin1String("/bin/g++.exe"));
183             if (fi.exists() && fi.isExecutable()) {
184                 ProjectExplorer::MingwToolChain *tc = createToolChain<ProjectExplorer::MingwToolChain>(ProjectExplorer::Constants::MINGW_TOOLCHAIN_ID);
185                 if (tc) {
186                     tc->setCompilerPath(fi.absoluteFilePath());
187                     tc->setDisplayName(tr("MinGW from %1").arg(version->displayName()));
188                     ProjectExplorer::ToolChainManager::instance()->registerToolChain(tc);
189                 }
190             }
191         }
192         const QString mwcDir = s->value(QLatin1String("MwcDirectory")).toString();
193         if (!mwcDir.isEmpty())
194             m_pendingMwcUpdates.append(mwcDir);
195         const QString gcceDir = s->value(QLatin1String("GcceDirectory")).toString();
196         if (!gcceDir.isEmpty())
197             m_pendingGcceUpdates.append(gcceDir);
198
199         m_versions.insert(version->uniqueId(), version);
200     }
201     s->endArray();
202
203     ++m_idcount;
204     addNewVersionsFromInstaller();
205     updateSystemVersion();
206
207     // cannot call from ctor, needs to get connected extenernally first
208     QTimer::singleShot(0, this, SLOT(updateSettings()));
209 }
210
211 QtVersionManager::~QtVersionManager()
212 {
213     qDeleteAll(m_versions);
214     m_versions.clear();
215     delete m_emptyVersion;
216     m_emptyVersion = 0;
217 }
218
219 QtVersionManager *QtVersionManager::instance()
220 {
221     return m_self;
222 }
223
224 void QtVersionManager::addVersion(QtVersion *version)
225 {
226     QTC_ASSERT(version != 0, return);
227     if (m_versions.contains(version->uniqueId()))
228         return;
229
230     int uniqueId = version->uniqueId();
231     m_versions.insert(uniqueId, version);
232
233     emit qtVersionsChanged(QList<int>() << uniqueId);
234     writeVersionsIntoSettings();
235 }
236
237 void QtVersionManager::removeVersion(QtVersion *version)
238 {
239     QTC_ASSERT(version != 0, return);
240     m_versions.remove(version->uniqueId());
241     emit qtVersionsChanged(QList<int>() << version->uniqueId());
242     writeVersionsIntoSettings();
243     delete version;
244 }
245
246 bool QtVersionManager::supportsTargetId(const QString &id) const
247 {
248     QList<QtVersion *> versions = QtVersionManager::instance()->versionsForTargetId(id);
249     foreach (QtVersion *v, versions)
250         if (v->isValid() && v->toolChainAvailable())
251             return true;
252     return false;
253 }
254
255 QList<QtVersion *> QtVersionManager::versionsForTargetId(const QString &id, const QtVersionNumber &minimumQtVersion) const
256 {
257     QList<QtVersion *> targetVersions;
258     foreach (QtVersion *version, m_versions) {
259         if (version->supportsTargetId(id) && version->qtVersion() >= minimumQtVersion)
260             targetVersions.append(version);
261     }
262     qSort(targetVersions.begin(), targetVersions.end(), &qtVersionNumberCompare);
263     return targetVersions;
264 }
265
266 QSet<QString> QtVersionManager::supportedTargetIds() const
267 {
268     QSet<QString> results;
269     foreach (QtVersion *version, m_versions)
270         results.unite(version->supportedTargetIds());
271     return results;
272 }
273
274 void QtVersionManager::updateDocumentation()
275 {
276     Core::HelpManager *helpManager = Core::HelpManager::instance();
277     Q_ASSERT(helpManager);
278     QStringList files;
279     foreach (QtVersion *version, m_versions) {
280         const QString docPath = version->documentationPath() + QLatin1String("/qch/");
281         const QDir versionHelpDir(docPath);
282         foreach (const QString &helpFile,
283                 versionHelpDir.entryList(QStringList() << QLatin1String("*.qch"), QDir::Files))
284             files << docPath + helpFile;
285
286     }
287     helpManager->registerDocumentation(files);
288 }
289
290 void QtVersionManager::updateSettings()
291 {
292     writeVersionsIntoSettings();
293
294     updateDocumentation();
295
296     QtVersion *version = 0;
297     QList<QtVersion*> candidates;
298
299     // try to find a version which has both, demos and examples
300     foreach (version, m_versions) {
301         if (version->hasExamples() && version->hasDemos())
302         candidates.append(version);
303     }
304
305     // in SDKs, we want to prefer the Qt version shipping with the SDK
306     QSettings *settings = Core::ICore::instance()->settings();
307     QString preferred = settings->value(QLatin1String("PreferredQMakePath")).toString();
308     preferred = QDir::fromNativeSeparators(preferred);
309     if (!preferred.isEmpty()) {
310 #ifdef Q_OS_WIN
311         preferred = preferred.toLower();
312         if (!preferred.endsWith(QLatin1String(".exe")))
313             preferred.append(QLatin1String(".exe"));
314 #endif
315         foreach (version, candidates) {
316             if (version->qmakeCommand() == preferred) {
317                 emit updateExamples(version->examplesPath(), version->demosPath(), version->sourcePath());
318                 return;
319             }
320         }
321     }
322
323     // prefer versions with declarative examples
324     foreach (version, candidates) {
325         if (QDir(version->examplesPath()+"/declarative").exists()) {
326             emit updateExamples(version->examplesPath(), version->demosPath(), version->sourcePath());
327             return;
328         }
329     }
330
331     if (!candidates.isEmpty()) {
332         version = candidates.first();
333         emit updateExamples(version->examplesPath(), version->demosPath(), version->sourcePath());
334         return;
335     }
336     return;
337
338 }
339
340 int QtVersionManager::getUniqueId()
341 {
342     return m_idcount++;
343 }
344
345 void QtVersionManager::writeVersionsIntoSettings()
346 {
347     QSettings *s = Core::ICore::instance()->settings();
348     s->beginWriteArray(QtVersionsSectionName);
349     QMap<int, QtVersion *>::const_iterator it = m_versions.constBegin();
350     for (int i = 0; i < m_versions.size(); ++i) {
351         const QtVersion *version = it.value();
352         s->setArrayIndex(i);
353         s->setValue("Name", version->displayName());
354         // for downwards compat
355         s->setValue("Path", version->versionInfo().value("QT_INSTALL_DATA"));
356         s->setValue("QMakePath", version->qmakeCommand());
357         s->setValue("Id", version->uniqueId());
358         s->setValue("isAutodetected", version->isAutodetected());
359         if (version->isAutodetected())
360             s->setValue("autodetectionSource", version->autodetectionSource());
361         s->setValue("S60SDKDirectory", version->systemRoot());
362         s->setValue(QLatin1String("SBSv2Directory"), version->sbsV2Directory());
363         // Remove obsolete settings: New tool chains would be created at each startup
364         // otherwise, overriding manually set ones.
365         s->remove(QLatin1String("MingwDirectory"));
366         s->remove(QLatin1String("MwcDirectory"));
367         s->remove(QLatin1String("GcceDirectory"));
368         ++it;
369     }
370     s->endArray();
371 }
372
373 QList<QtVersion *> QtVersionManager::versions() const
374 {
375     QList<QtVersion *> versions;
376     foreach (QtVersion *version, m_versions)
377         versions << version;
378     qSort(versions.begin(), versions.end(), &qtVersionNumberCompare);
379     return versions;
380 }
381
382 QList<QtVersion *> QtVersionManager::validVersions() const
383 {
384     QList<QtVersion *> results;
385     foreach(QtVersion *v, m_versions) {
386         if (v->isValid())
387             results.append(v);
388     }
389     qSort(results.begin(), results.end(), &qtVersionNumberCompare);
390     return results;
391 }
392
393 bool QtVersionManager::isValidId(int id) const
394 {
395     return m_versions.contains(id);
396 }
397
398 QString QtVersionManager::popPendingMwcUpdate()
399 {
400     if (m_pendingMwcUpdates.isEmpty())
401         return QString();
402     return m_pendingMwcUpdates.takeFirst();
403 }
404
405 QString QtVersionManager::popPendingGcceUpdate()
406 {
407     if (m_pendingGcceUpdates.isEmpty())
408         return QString();
409     return m_pendingGcceUpdates.takeFirst();
410 }
411
412 QtVersion *QtVersionManager::version(int id) const
413 {
414     QMap<int, QtVersion *>::const_iterator it = m_versions.find(id);
415     if (it == m_versions.constEnd())
416         return m_emptyVersion;
417     return it.value();
418 }
419
420 // FIXME: Rework this!
421 void QtVersionManager::addNewVersionsFromInstaller()
422 {
423     // Add new versions which may have been installed by the WB installer in the form:
424     // NewQtVersions="qt 4.3.2=c:\\qt\\qt432\bin\qmake.exe;qt embedded=c:\\qtembedded;"
425     // or NewQtVersions="qt 4.3.2=c:\\qt\\qt432bin\qmake.exe;
426     // i.e.
427     // NewQtVersions="versionname=pathtoversion=s60sdk;"
428     // Duplicate entries are not added, the first new version is set as default.
429     QSettings *settings = Core::ICore::instance()->settings();
430     QSettings *globalSettings = Core::ICore::instance()->settings(QSettings::SystemScope);
431
432     QDateTime lastUpdateFromGlobalSettings = globalSettings->value(
433             QLatin1String("General/LastQtVersionUpdate")).toDateTime();
434
435     const QFileInfo gsFi(globalSettings->fileName());
436     if ( !lastUpdateFromGlobalSettings.isNull() &&
437          (!gsFi.exists() || (gsFi.lastModified() > lastUpdateFromGlobalSettings)) )
438         return;
439
440     if (!globalSettings->contains(newQtVersionsKey) &&
441         !globalSettings->contains(QLatin1String("Installer/")+newQtVersionsKey))
442     {
443         return;
444     }
445
446     QString newVersionsValue = settings->value(newQtVersionsKey).toString();
447     if (newVersionsValue.isEmpty())
448         newVersionsValue = settings->value(QLatin1String("Installer/")+newQtVersionsKey).toString();
449
450     QStringList newVersionsList = newVersionsValue.split(';', QString::SkipEmptyParts);
451     foreach (const QString &newVersion, newVersionsList) {
452         QStringList newVersionData = newVersion.split('=');
453         if (newVersionData.count() >= 2) {
454             if (QFile::exists(newVersionData[1])) {
455                 QtVersion *version = new QtVersion(newVersionData[0], newVersionData[1], m_idcount++ );
456                 if (newVersionData.count() >= 3)
457                     version->setSystemRoot(QDir::fromNativeSeparators(newVersionData[2]));
458                 if (newVersionData.count() >= 4)
459                     version->setSbsV2Directory(QDir::fromNativeSeparators(newVersionData[3]));
460
461                 bool versionWasAlreadyInList = false;
462                 foreach(const QtVersion * const it, m_versions) {
463                     if (QDir(version->qmakeCommand()).canonicalPath() == QDir(it->qmakeCommand()).canonicalPath()) {
464                         versionWasAlreadyInList = true;
465                         break;
466                     }
467                 }
468
469                 if (!versionWasAlreadyInList) {
470                     m_versions.insert(version->uniqueId(), version);
471                 } else {
472                     // clean up
473                     delete version;
474                 }
475             }
476         }
477     }
478     settings->setValue(QLatin1String("General/LastQtVersionUpdate"), QDateTime::currentDateTime());
479 }
480
481 void QtVersionManager::updateSystemVersion()
482 {
483     bool haveSystemVersion = false;
484     QString systemQMakePath = DebuggingHelperLibrary::findSystemQt(Utils::Environment::systemEnvironment());
485     if (systemQMakePath.isNull())
486         systemQMakePath = tr("<not found>");
487
488     foreach (QtVersion *version, m_versions) {
489         if (version->isAutodetected()
490             && version->autodetectionSource() == PATH_AUTODETECTION_SOURCE) {
491             version->setQMakeCommand(systemQMakePath);
492             version->setDisplayName(tr("Qt in PATH"));
493             haveSystemVersion = true;
494         }
495     }
496     if (haveSystemVersion)
497         return;
498     QtVersion *version = new QtVersion(tr("Qt in PATH"),
499                                        systemQMakePath,
500                                        getUniqueId(),
501                                        true,
502                                        PATH_AUTODETECTION_SOURCE);
503     m_versions.insert(version->uniqueId(), version);
504 }
505
506 QtVersion *QtVersionManager::emptyVersion() const
507 {
508     return m_emptyVersion;
509 }
510
511 class SortByUniqueId
512 {
513 public:
514     bool operator()(QtVersion *a, QtVersion *b)
515     {
516         return a->uniqueId() < b->uniqueId();
517     }
518 };
519
520 bool QtVersionManager::equals(QtVersion *a, QtVersion *b)
521 {
522     if (a->m_qmakeCommand != b->m_qmakeCommand)
523         return false;
524     if (a->m_id != b->m_id)
525         return false;
526     if (a->m_displayName != b->displayName())
527         return false;
528     return true;
529 }
530
531 void QtVersionManager::setNewQtVersions(QList<QtVersion *> newVersions)
532 {
533     // We want to preserve the same order as in the settings dialog
534     // so we sort a copy
535     QList<QtVersion *> sortedNewVersions = newVersions;
536     SortByUniqueId sortByUniqueId;
537     qSort(sortedNewVersions.begin(), sortedNewVersions.end(), sortByUniqueId);
538
539     QList<int> changedVersions;
540     // So we trying to find the minimal set of changed versions,
541     // iterate over both sorted list
542
543     // newVersions and oldVersions iterator
544     QList<QtVersion *>::const_iterator nit, nend;
545     QMap<int, QtVersion *>::const_iterator oit, oend;
546     nit = sortedNewVersions.constBegin();
547     nend = sortedNewVersions.constEnd();
548     oit = m_versions.constBegin();
549     oend = m_versions.constEnd();
550
551     while (nit != nend && oit != oend) {
552         int nid = (*nit)->uniqueId();
553         int oid = (*oit)->uniqueId();
554         if (nid < oid) {
555             changedVersions.push_back(nid);
556             ++nit;
557         } else if (oid < nid) {
558             changedVersions.push_back(oid);
559             ++oit;
560         } else {
561             if (!equals(*oit, *nit))
562                 changedVersions.push_back(oid);
563             ++oit;
564             ++nit;
565         }
566     }
567
568     while (nit != nend) {
569         changedVersions.push_back((*nit)->uniqueId());
570         ++nit;
571     }
572
573     while (oit != oend) {
574         changedVersions.push_back((*oit)->uniqueId());
575         ++oit;
576     }
577
578     qDeleteAll(m_versions);
579     m_versions.clear();
580     foreach (QtVersion *v, sortedNewVersions)
581         m_versions.insert(v->uniqueId(), v);
582
583     if (!changedVersions.isEmpty())
584         updateDocumentation();
585
586     updateSettings();
587     writeVersionsIntoSettings();
588
589     if (!changedVersions.isEmpty())
590         emit qtVersionsChanged(changedVersions);
591 }
592
593 // --------------------------------------------------------------------------
594 // QtVersion
595 // --------------------------------------------------------------------------
596
597 QtVersion::QtVersion(const QString &name, const QString &qmakeCommand, int id,
598                      bool isAutodetected, const QString &autodetectionSource)
599     : m_displayName(name),
600     m_isAutodetected(isAutodetected),
601     m_autodetectionSource(autodetectionSource),
602     m_hasDebuggingHelper(false),
603     m_hasQmlDump(false),
604     m_hasQmlDebuggingLibrary(false),
605     m_hasQmlObserver(false),
606     m_abiUpToDate(false),
607     m_versionInfoUpToDate(false),
608     m_notInstalled(false),
609     m_defaultConfigIsDebug(true),
610     m_defaultConfigIsDebugAndRelease(true),
611     m_hasExamples(false),
612     m_hasDemos(false),
613     m_hasDocumentation(false),
614     m_qmakeIsExecutable(false),
615     m_validSystemRoot(true)
616 {
617     if (id == -1)
618         m_id = getUniqueId();
619     else
620         m_id = id;
621     setQMakeCommand(qmakeCommand);
622 }
623
624 QtVersion::QtVersion(const QString &name, const QString &qmakeCommand,
625                      bool isAutodetected, const QString &autodetectionSource)
626     : m_displayName(name),
627     m_isAutodetected(isAutodetected),
628     m_autodetectionSource(autodetectionSource),
629     m_hasDebuggingHelper(false),
630     m_hasQmlDump(false),
631     m_hasQmlDebuggingLibrary(false),
632     m_hasQmlObserver(false),
633     m_abiUpToDate(false),
634     m_versionInfoUpToDate(false),
635     m_notInstalled(false),
636     m_defaultConfigIsDebug(true),
637     m_defaultConfigIsDebugAndRelease(true),
638     m_hasExamples(false),
639     m_hasDemos(false),
640     m_hasDocumentation(false),
641     m_qmakeIsExecutable(false),
642     m_validSystemRoot(true)
643 {
644     m_id = getUniqueId();
645     setQMakeCommand(qmakeCommand);
646 }
647
648
649 QtVersion::QtVersion(const QString &qmakeCommand, bool isAutodetected, const QString &autodetectionSource)
650     : m_isAutodetected(isAutodetected),
651     m_autodetectionSource(autodetectionSource),
652     m_hasDebuggingHelper(false),
653     m_hasQmlDump(false),
654     m_hasQmlDebuggingLibrary(false),
655     m_hasQmlObserver(false),
656     m_abiUpToDate(false),
657     m_versionInfoUpToDate(false),
658     m_notInstalled(false),
659     m_defaultConfigIsDebug(true),
660     m_defaultConfigIsDebugAndRelease(true),
661     m_hasExamples(false),
662     m_hasDemos(false),
663     m_hasDocumentation(false),
664     m_qmakeIsExecutable(false),
665     m_validSystemRoot(true)
666 {
667     m_id = getUniqueId();
668     setQMakeCommand(qmakeCommand);
669     m_displayName = qtVersionString();
670 }
671
672 QtVersion::QtVersion()
673     :  m_id(-1),
674     m_isAutodetected(false),
675     m_hasDebuggingHelper(false),
676     m_hasQmlDump(false),
677     m_hasQmlDebuggingLibrary(false),
678     m_hasQmlObserver(false),
679     m_abiUpToDate(false),
680     m_versionInfoUpToDate(false),
681     m_notInstalled(false),
682     m_defaultConfigIsDebug(true),
683     m_defaultConfigIsDebugAndRelease(true),
684     m_hasExamples(false),
685     m_hasDemos(false),
686     m_hasDocumentation(false) ,
687     m_qmakeIsExecutable(false),
688     m_validSystemRoot(true)
689 {
690     setQMakeCommand(QString());
691 }
692
693 QtVersion::~QtVersion()
694 {
695 }
696
697 QString QtVersion::toHtml(bool verbose) const
698 {
699     QString rc;
700     QTextStream str(&rc);
701     str << "<html><body><table>";
702     str << "<tr><td><b>" << QtVersionManager::tr("Name:")
703         << "</b></td><td>" << displayName() << "</td></tr>";
704     if (!isValid()) {
705         str << "<tr><td colspan=2><b>" + QtVersionManager::tr("Invalid Qt version") +"</b></td></tr>";
706     } else {
707         QString prefix = QLatin1String("<tr><td><b>") + QtVersionManager::tr("ABI:") + QLatin1String("</b></td>");
708         foreach (const ProjectExplorer::Abi &abi, qtAbis()) {
709             str << prefix << "<td>" << abi.toString() << "</td></tr>";
710             prefix = QLatin1String("<tr><td></td>");
711         }
712         str << "<tr><td><b>" << QtVersionManager::tr("Source:")
713             << "</b></td><td>" << sourcePath() << "</td></tr>";
714         str << "<tr><td><b>" << QtVersionManager::tr("mkspec:")
715             << "</b></td><td>" << mkspec() << "</td></tr>";
716         str << "<tr><td><b>" << QtVersionManager::tr("qmake:")
717             << "</b></td><td>" << m_qmakeCommand << "</td></tr>";
718         updateAbiAndMkspec();
719         if (m_defaultConfigIsDebug || m_defaultConfigIsDebugAndRelease) {
720             str << "<tr><td><b>" << QtVersionManager::tr("Default:") << "</b></td><td>"
721                 << (m_defaultConfigIsDebug ? "debug" : "release");
722             if (m_defaultConfigIsDebugAndRelease)
723                 str << " debug_and_release";
724             str << "</td></tr>";
725         } // default config.
726         str << "<tr><td><b>" << QtVersionManager::tr("Version:")
727             << "</b></td><td>" << qtVersionString() << "</td></tr>";
728         if (verbose) {
729             const QHash<QString,QString> vInfo = versionInfo();
730             if (!vInfo.isEmpty()) {
731                 const QHash<QString,QString>::const_iterator vcend = vInfo.constEnd();
732                 for (QHash<QString,QString>::const_iterator it = vInfo.constBegin(); it != vcend; ++it)
733                     str << "<tr><td><pre>" << it.key() <<  "</pre></td><td>" << it.value() << "</td></tr>";
734             }
735         }
736     }
737     str << "</table></body></html>";
738     return rc;
739 }
740
741 bool QtVersion::supportsShadowBuilds() const
742 {
743     QSet<QString> targets = supportedTargetIds();
744     // Symbian does not support shadow building
745     if (targets.contains(Constants::S60_DEVICE_TARGET_ID) ||
746         targets.contains(Constants::S60_EMULATOR_TARGET_ID)) {
747         // We can not support shadow building with the ABLD system
748         return false;
749     }
750 #ifdef Q_OS_WIN
751     if (targets.contains(Constants::MEEGO_DEVICE_TARGET_ID)
752         || targets.contains(Constants::MAEMO5_DEVICE_TARGET_ID)
753         || targets.contains(Constants::HARMATTAN_DEVICE_TARGET_ID))
754         return false;
755 #endif
756     return true;
757 }
758
759 ProjectExplorer::IOutputParser *QtVersion::createOutputParser() const
760 {
761     if (supportsTargetId(Qt4ProjectManager::Constants::S60_DEVICE_TARGET_ID) ||
762         supportsTargetId(Qt4ProjectManager::Constants::S60_EMULATOR_TARGET_ID)) {
763         if (isBuildWithSymbianSbsV2()) {
764             return new SbsV2Parser;
765         } else {
766             ProjectExplorer::IOutputParser *parser = new AbldParser;
767             parser->appendOutputParser(new ProjectExplorer::GnuMakeParser);
768             return parser;
769         }
770     }
771     return new ProjectExplorer::GnuMakeParser;
772 }
773
774 QList<ProjectExplorer::Task>
775 QtVersion::reportIssues(const QString &proFile, const QString &buildDir, bool includeTargetSpecificErrors)
776 {
777     QList<ProjectExplorer::Task> results;
778
779     QString tmpBuildDir = QDir(buildDir).absolutePath();
780     if (!tmpBuildDir.endsWith(QLatin1Char('/')))
781         tmpBuildDir.append(QLatin1Char('/'));
782
783     if (!isValid()) {
784         //: %1: Reason for being invalid
785         const QString msg = QCoreApplication::translate("Qt4ProjectManager::QtVersion", "The Qt version is invalid: %1").arg(invalidReason());
786         results.append(ProjectExplorer::Task(ProjectExplorer::Task::Error, msg, QString(), -1,
787                                              QLatin1String(ProjectExplorer::Constants::TASK_CATEGORY_BUILDSYSTEM)));
788     }
789
790     QFileInfo qmakeInfo(qmakeCommand());
791     if (!qmakeInfo.exists() ||
792         !qmakeInfo.isExecutable()) {
793         //: %1: Path to qmake executable
794         const QString msg = QCoreApplication::translate("Qt4ProjectManager::QtVersion",
795                                                         "The qmake command \"%1\" was not found or is not executable.").arg(qmakeCommand());
796         results.append(ProjectExplorer::Task(ProjectExplorer::Task::Error, msg, QString(), -1,
797                                              QLatin1String(ProjectExplorer::Constants::TASK_CATEGORY_BUILDSYSTEM)));
798     }
799
800     QString sourcePath = QFileInfo(proFile).absolutePath();
801     if (!sourcePath.endsWith(QLatin1Char('/')))
802         sourcePath.append(QLatin1Char('/'));
803     if ((tmpBuildDir.startsWith(sourcePath)) && (tmpBuildDir != sourcePath)) {
804         const QString msg = QCoreApplication::translate("Qt4ProjectManager::QtVersion",
805                                                         "Qmake does not support build directories below the source directory.");
806         results.append(ProjectExplorer::Task(ProjectExplorer::Task::Warning, msg, QString(), -1,
807                                              QLatin1String(ProjectExplorer::Constants::TASK_CATEGORY_BUILDSYSTEM)));
808     } else if (tmpBuildDir.count(QChar('/')) != sourcePath.count(QChar('/'))) {
809         const QString msg = QCoreApplication::translate("Qt4ProjectManager::QtVersion",
810                                                         "The build directory needs to be at the same level as the source directory.");
811
812         results.append(ProjectExplorer::Task(ProjectExplorer::Task::Warning, msg, QString(), -1,
813                                              QLatin1String(ProjectExplorer::Constants::TASK_CATEGORY_BUILDSYSTEM)));
814     }
815
816 #if defined (Q_OS_WIN)
817     QSet<QString> targets = supportedTargetIds();
818     if (targets.contains(Constants::S60_DEVICE_TARGET_ID) ||
819             targets.contains(Constants::S60_EMULATOR_TARGET_ID)) {
820         const QString epocRootDir = systemRoot();
821         // Report an error if project- and epoc directory are on different drives:
822         if (!epocRootDir.startsWith(proFile.left(3), Qt::CaseInsensitive) && !isBuildWithSymbianSbsV2()) {
823             results.append(ProjectExplorer::Task(ProjectExplorer::Task::Error,
824                                                  QCoreApplication::translate("ProjectExplorer::Internal::S60ProjectChecker",
825                                                                              "The Symbian SDK and the project sources must reside on the same drive."),
826                                                  QString(), -1, ProjectExplorer::Constants::TASK_CATEGORY_BUILDSYSTEM));
827         }
828     }
829 #endif
830
831     if (includeTargetSpecificErrors) {
832         QList<Qt4BaseTargetFactory *> factories;
833         foreach (const QString &id, supportedTargetIds())
834             if (Qt4BaseTargetFactory *factory = Qt4BaseTargetFactory::qt4BaseTargetFactoryForId(id))
835                 factories << factory;
836
837         qSort(factories);
838         QList<Qt4BaseTargetFactory *>::iterator newend = std::unique(factories.begin(), factories.end());
839         QList<Qt4BaseTargetFactory *>::iterator it = factories.begin();
840         for ( ; it != newend; ++it)
841             results.append((*it)->reportIssues(proFile));
842     }
843     return results;
844 }
845
846 QString QtVersion::displayName() const
847 {
848     return m_displayName;
849 }
850
851 QString QtVersion::qmakeCommand() const
852 {
853     return m_qmakeCommand;
854 }
855
856 QString QtVersion::sourcePath() const
857 {
858     return m_sourcePath;
859 }
860
861 QString QtVersion::mkspec() const
862 {
863     updateAbiAndMkspec();
864     return m_mkspec;
865 }
866
867 QString QtVersion::mkspecPath() const
868 {
869     updateAbiAndMkspec();
870     return m_mkspecFullPath;
871 }
872
873 bool QtVersion::isBuildWithSymbianSbsV2() const
874 {
875     updateAbiAndMkspec();
876     return m_isBuildUsingSbsV2;
877 }
878
879 QString QtVersion::qtVersionString() const
880 {
881     if (m_qtVersionString.isNull()) {
882         QFileInfo qmake(m_qmakeCommand);
883         if (qmake.exists() && qmake.isExecutable()) {
884             m_qtVersionString = DebuggingHelperLibrary::qtVersionForQMake(qmake.absoluteFilePath());
885         } else {
886             m_qtVersionString = QLatin1String("");
887         }
888     }
889     return m_qtVersionString;
890 }
891
892 QtVersionNumber QtVersion::qtVersion() const
893 {
894     //todo cache this;
895     return QtVersionNumber(qtVersionString());
896 }
897
898 QHash<QString,QString> QtVersion::versionInfo() const
899 {
900     updateVersionInfo();
901     return m_versionInfo;
902 }
903
904 void QtVersion::setDisplayName(const QString &name)
905 {
906     m_displayName = name;
907 }
908
909 void QtVersion::setQMakeCommand(const QString& qmakeCommand)
910 {
911     m_qmakeCommand = QDir::fromNativeSeparators(qmakeCommand);
912 #ifdef Q_OS_WIN
913     m_qmakeCommand = m_qmakeCommand.toLower();
914 #endif
915     m_designerCommand.clear();
916     m_linguistCommand.clear();
917     m_qmlviewerCommand.clear();
918     m_uicCommand.clear();
919     m_abiUpToDate = false;
920     // TODO do i need to optimize this?
921     m_versionInfoUpToDate = false;
922     m_qtVersionString.clear();
923     updateSourcePath();
924 }
925
926 void QtVersion::updateSourcePath()
927 {
928     updateVersionInfo();
929     const QString installData = m_versionInfo["QT_INSTALL_DATA"];
930     m_sourcePath = installData;
931     QFile qmakeCache(installData + QLatin1String("/.qmake.cache"));
932     if (qmakeCache.exists()) {
933         qmakeCache.open(QIODevice::ReadOnly | QIODevice::Text);
934         QTextStream stream(&qmakeCache);
935         while (!stream.atEnd()) {
936             QString line = stream.readLine().trimmed();
937             if (line.startsWith(QLatin1String("QT_SOURCE_TREE"))) {
938                 m_sourcePath = line.split(QLatin1Char('=')).at(1).trimmed();
939                 if (m_sourcePath.startsWith(QLatin1String("$$quote("))) {
940                     m_sourcePath.remove(0, 8);
941                     m_sourcePath.chop(1);
942                 }
943                 break;
944             }
945         }
946     }
947     m_sourcePath = QDir::cleanPath(m_sourcePath);
948 #ifdef Q_OS_WIN
949     m_sourcePath = m_sourcePath.toLower();
950 #endif
951 }
952
953 // Returns the version that was used to build the project in that directory
954 // That is returns the directory
955 // To find out whether we already have a qtversion for that directory call
956 // QtVersion *QtVersionManager::qtVersionForDirectory(const QString directory);
957 QString QtVersionManager::findQMakeBinaryFromMakefile(const QString &makefile)
958 {
959     bool debugAdding = false;
960     QFile fi(makefile);
961     if (fi.exists() && fi.open(QFile::ReadOnly)) {
962         QTextStream ts(&fi);
963         QRegExp r1("QMAKE\\s*=(.*)");
964         while (!ts.atEnd()) {
965             QString line = ts.readLine();
966             if (r1.exactMatch(line)) {
967                 if (debugAdding)
968                     qDebug()<<"#~~ QMAKE is:"<<r1.cap(1).trimmed();
969                 QFileInfo qmake(r1.cap(1).trimmed());
970                 QString qmakePath = qmake.filePath();
971 #ifdef Q_OS_WIN
972                 if (!qmakePath.endsWith(QLatin1String(".exe")))
973                     qmakePath.append(QLatin1String(".exe"));
974 #endif
975                 // Is qmake still installed?
976                 QFileInfo fi(qmakePath);
977                 if (fi.exists()) {
978                     qmakePath = fi.absoluteFilePath();
979 #ifdef Q_OS_WIN
980                     qmakePath = qmakePath.toLower();
981 #endif
982                     return qmakePath;
983                 }
984             }
985         }
986     }
987     return QString();
988 }
989
990 QtVersion *QtVersionManager::qtVersionForQMakeBinary(const QString &qmakePath)
991 {
992    foreach(QtVersion *v, versions()) {
993        if (v->qmakeCommand() == qmakePath) {
994            return v;
995            break;
996        }
997    }
998    return 0;
999 }
1000
1001 void dumpQMakeAssignments(const QList<QMakeAssignment> &list)
1002 {
1003     foreach(const QMakeAssignment &qa, list) {
1004         qDebug()<<qa.variable<<qa.op<<qa.value;
1005     }
1006 }
1007
1008 bool QtVersionManager::makefileIsFor(const QString &makefile, const QString &proFile)
1009 {
1010     if (proFile.isEmpty())
1011         return true;
1012
1013     QString line = findQMakeLine(makefile, QLatin1String("# Project:")).trimmed();
1014     if (line.isEmpty())
1015         return false;
1016
1017     line = line.mid(line.indexOf(QChar(':')) + 1);
1018     line = line.trimmed();
1019
1020     QFileInfo srcFileInfo(QFileInfo(makefile).absoluteDir(), line);
1021     QFileInfo proFileInfo(proFile);
1022     return srcFileInfo == proFileInfo;
1023 }
1024
1025 QPair<QtVersion::QmakeBuildConfigs, QString> QtVersionManager::scanMakeFile(const QString &makefile, QtVersion::QmakeBuildConfigs defaultBuildConfig)
1026 {
1027     if (debug)
1028         qDebug()<<"ScanMakeFile, the gory details:";
1029     QtVersion::QmakeBuildConfigs result = defaultBuildConfig;
1030     QString result2;
1031
1032     QString line = findQMakeLine(makefile, QLatin1String("# Command:"));
1033     if (!line.isEmpty()) {
1034         if (debug)
1035             qDebug()<<"Found line"<<line;
1036         line = trimLine(line);
1037         QList<QMakeAssignment> assignments;
1038         QList<QMakeAssignment> afterAssignments;
1039         parseArgs(line, &assignments, &afterAssignments, &result2);
1040
1041         if (debug) {
1042             dumpQMakeAssignments(assignments);
1043             if (!afterAssignments.isEmpty())
1044                 qDebug()<<"-after";
1045             dumpQMakeAssignments(afterAssignments);
1046         }
1047
1048         // Search in assignments for CONFIG(+=,-=,=)(debug,release,debug_and_release)
1049         // Also remove them from the list
1050         result = qmakeBuildConfigFromCmdArgs(&assignments, defaultBuildConfig);
1051
1052         if (debug)
1053             dumpQMakeAssignments(assignments);
1054
1055         foreach(const QMakeAssignment &qa, assignments)
1056             Utils::QtcProcess::addArg(&result2, qa.variable + qa.op + qa.value);
1057         if (!afterAssignments.isEmpty()) {
1058             Utils::QtcProcess::addArg(&result2, QLatin1String("-after"));
1059             foreach(const QMakeAssignment &qa, afterAssignments)
1060                 Utils::QtcProcess::addArg(&result2, qa.variable + qa.op + qa.value);
1061         }
1062     }
1063
1064     // Dump the gathered information:
1065     if (debug) {
1066         qDebug()<<"\n\nDumping information from scanMakeFile";
1067         qDebug()<<"QMake CONFIG variable parsing";
1068         qDebug()<<"  "<< (result & QtVersion::NoBuild ? "No Build" : QString::number(int(result)));
1069         qDebug()<<"  "<< (result & QtVersion::DebugBuild ? "debug" : "release");
1070         qDebug()<<"  "<< (result & QtVersion::BuildAll ? "debug_and_release" : "no debug_and_release");
1071         qDebug()<<"\nAddtional Arguments";
1072         qDebug()<<result2;
1073         qDebug()<<"\n\n";
1074     }
1075     return qMakePair(result, result2);
1076 }
1077
1078 QString QtVersionManager::findQMakeLine(const QString &makefile, const QString &key)
1079 {
1080     QFile fi(makefile);
1081     if (fi.exists() && fi.open(QFile::ReadOnly)) {
1082         QTextStream ts(&fi);
1083         while (!ts.atEnd()) {
1084             const QString line = ts.readLine();
1085             if (line.startsWith(key))
1086                 return line;
1087         }
1088     }
1089     return QString();
1090 }
1091
1092 /// This function trims the "#Command /path/to/qmake" from the the line
1093 QString QtVersionManager::trimLine(const QString line)
1094 {
1095
1096     // Actually the first space after #Command: /path/to/qmake
1097     const int firstSpace = line.indexOf(QLatin1Char(' '), 11);
1098     return line.mid(firstSpace).trimmed();
1099 }
1100
1101 void QtVersionManager::parseArgs(const QString &args, QList<QMakeAssignment> *assignments, QList<QMakeAssignment> *afterAssignments, QString *additionalArguments)
1102 {
1103     QRegExp regExp("([^\\s\\+-]*)\\s*(\\+=|=|-=|~=)(.*)");
1104     bool after = false;
1105     bool ignoreNext = false;
1106     *additionalArguments = args;
1107     Utils::QtcProcess::ArgIterator ait(additionalArguments);
1108     while (ait.next()) {
1109         if (ignoreNext) {
1110             // Ignoring
1111             ignoreNext = false;
1112             ait.deleteArg();
1113         } else if (ait.value() == QLatin1String("-after")) {
1114             after = true;
1115             ait.deleteArg();
1116         } else if (ait.value().contains(QLatin1Char('='))) {
1117             if (regExp.exactMatch(ait.value())) {
1118                 QMakeAssignment qa;
1119                 qa.variable = regExp.cap(1);
1120                 qa.op = regExp.cap(2);
1121                 qa.value = regExp.cap(3).trimmed();
1122                 if (after)
1123                     afterAssignments->append(qa);
1124                 else
1125                     assignments->append(qa);
1126             } else {
1127                 qDebug()<<"regexp did not match";
1128             }
1129             ait.deleteArg();
1130         } else if (ait.value() == QLatin1String("-o")) {
1131             ignoreNext = true;
1132             ait.deleteArg();
1133 #if defined(Q_OS_WIN32)
1134         } else if (ait.value() == QLatin1String("-win32")) {
1135 #elif defined(Q_OS_MAC)
1136         } else if (ait.value() == QLatin1String("-macx")) {
1137 #elif defined(Q_OS_QNX6)
1138         } else if (ait.value() == QLatin1String("-qnx6")) {
1139 #else
1140         } else if (ait.value() == QLatin1String("-unix")) {
1141 #endif
1142             ait.deleteArg();
1143         }
1144     }
1145     ait.deleteArg();  // The .pro file is always the last arg
1146 }
1147
1148 /// This function extracts all the CONFIG+=debug, CONFIG+=release
1149 QtVersion::QmakeBuildConfigs QtVersionManager::qmakeBuildConfigFromCmdArgs(QList<QMakeAssignment> *assignments, QtVersion::QmakeBuildConfigs defaultBuildConfig)
1150 {
1151     QtVersion::QmakeBuildConfigs result = defaultBuildConfig;
1152     QList<QMakeAssignment> oldAssignments = *assignments;
1153     assignments->clear();
1154     foreach(const QMakeAssignment &qa, oldAssignments) {
1155         if (qa.variable == "CONFIG") {
1156             QStringList values = qa.value.split(' ');
1157             QStringList newValues;
1158             foreach(const QString &value, values) {
1159                 if (value == "debug") {
1160                     if (qa.op == "+=")
1161                         result = result  | QtVersion::DebugBuild;
1162                     else
1163                         result = result  & ~QtVersion::DebugBuild;
1164                 } else if (value == "release") {
1165                     if (qa.op == "+=")
1166                         result = result & ~QtVersion::DebugBuild;
1167                     else
1168                         result = result | QtVersion::DebugBuild;
1169                 } else if (value == "debug_and_release") {
1170                     if (qa.op == "+=")
1171                         result = result | QtVersion::BuildAll;
1172                     else
1173                         result = result & ~QtVersion::BuildAll;
1174                 } else {
1175                     newValues.append(value);
1176                 }
1177                 QMakeAssignment newQA = qa;
1178                 newQA.value = newValues.join(" ");
1179                 if (!newValues.isEmpty())
1180                     assignments->append(newQA);
1181             }
1182         } else {
1183             assignments->append(qa);
1184         }
1185     }
1186     return result;
1187 }
1188
1189 static bool queryQMakeVariables(const QString &binary, QHash<QString, QString> *versionInfo)
1190 {
1191     const int timeOutMS = 30000; // Might be slow on some machines.
1192     QFileInfo qmake(binary);
1193     static const char * const variables[] = {
1194              "QT_VERSION",
1195              "QT_INSTALL_DATA",
1196              "QT_INSTALL_LIBS",
1197              "QT_INSTALL_HEADERS",
1198              "QT_INSTALL_DEMOS",
1199              "QT_INSTALL_EXAMPLES",
1200              "QT_INSTALL_CONFIGURATION",
1201              "QT_INSTALL_TRANSLATIONS",
1202              "QT_INSTALL_PLUGINS",
1203              "QT_INSTALL_BINS",
1204              "QT_INSTALL_DOCS",
1205              "QT_INSTALL_PREFIX",
1206              "QT_INSTALL_IMPORTS",
1207              "QMAKEFEATURES"
1208         };
1209     QStringList args;
1210     for (uint i = 0; i < sizeof variables / sizeof variables[0]; ++i)
1211         args << "-query" << variables[i];
1212     QProcess process;
1213     process.start(qmake.absoluteFilePath(), args, QIODevice::ReadOnly);
1214     if (!process.waitForStarted()) {
1215         qWarning("Cannot start '%s': %s", qPrintable(binary), qPrintable(process.errorString()));
1216         return false;
1217     }
1218     if (!process.waitForFinished(timeOutMS)) {
1219         Utils::SynchronousProcess::stopProcess(process);
1220         qWarning("Timeout running '%s' (%dms).", qPrintable(binary), timeOutMS);
1221         return false;
1222     }
1223     if (process.exitStatus() != QProcess::NormalExit) {
1224         qWarning("'%s' crashed.", qPrintable(binary));
1225         return false;
1226     }
1227     QByteArray output = process.readAllStandardOutput();
1228     QTextStream stream(&output);
1229     while (!stream.atEnd()) {
1230         const QString line = stream.readLine();
1231         const int index = line.indexOf(QLatin1Char(':'));
1232         if (index != -1) {
1233             const QString value = QDir::fromNativeSeparators(line.mid(index+1));
1234             if (value != "**Unknown**")
1235                 versionInfo->insert(line.left(index), value);
1236         }
1237     }
1238     return true;
1239 }
1240
1241 void QtVersion::updateVersionInfo() const
1242 {
1243     if (m_versionInfoUpToDate)
1244         return;
1245
1246     // extract data from qmake executable
1247     m_versionInfo.clear();
1248     m_notInstalled = false;
1249     m_hasExamples = false;
1250     m_hasDocumentation = false;
1251     m_hasDebuggingHelper = false;
1252     m_hasQmlDump = false;
1253     m_hasQmlDebuggingLibrary = false;
1254     m_hasQmlObserver = false;
1255     m_qmakeIsExecutable = true;
1256
1257     QFileInfo fi(qmakeCommand());
1258     if (!fi.exists() || !fi.isExecutable()) {
1259         m_qmakeIsExecutable = false;
1260         return;
1261     }
1262
1263     if (!queryQMakeVariables(qmakeCommand(), &m_versionInfo))
1264         return;
1265
1266     if (m_versionInfo.contains("QT_INSTALL_DATA")) {
1267         QString qtInstallData = m_versionInfo.value("QT_INSTALL_DATA");
1268         m_versionInfo.insert("QMAKE_MKSPECS", QDir::cleanPath(qtInstallData+"/mkspecs"));
1269
1270         if (!qtInstallData.isEmpty()) {
1271             m_hasDebuggingHelper = !DebuggingHelperLibrary::debuggingHelperLibraryByInstallData(qtInstallData).isEmpty();
1272             m_hasQmlDump = !QmlDumpTool::toolByInstallData(qtInstallData, false).isEmpty() || !QmlDumpTool::toolByInstallData(qtInstallData, true).isEmpty();
1273             m_hasQmlDebuggingLibrary
1274                     = !QmlDebuggingLibrary::libraryByInstallData(qtInstallData, false).isEmpty()
1275                 || !QmlDebuggingLibrary::libraryByInstallData(qtInstallData, true).isEmpty();
1276             m_hasQmlObserver = !QmlObserverTool::toolByInstallData(qtInstallData).isEmpty();
1277         }
1278     }
1279
1280     // Now check for a qt that is configured with a prefix but not installed
1281     if (m_versionInfo.contains("QT_INSTALL_BINS")) {
1282         QFileInfo fi(m_versionInfo.value("QT_INSTALL_BINS"));
1283         if (!fi.exists())
1284             m_notInstalled = true;
1285     }
1286     if (m_versionInfo.contains("QT_INSTALL_HEADERS")){
1287         QFileInfo fi(m_versionInfo.value("QT_INSTALL_HEADERS"));
1288         if (!fi.exists())
1289             m_notInstalled = true;
1290     }
1291     if (m_versionInfo.contains("QT_INSTALL_DOCS")){
1292         QFileInfo fi(m_versionInfo.value("QT_INSTALL_DOCS"));
1293         if (fi.exists())
1294             m_hasDocumentation = true;
1295     }
1296     if (m_versionInfo.contains("QT_INSTALL_EXAMPLES")){
1297         QFileInfo fi(m_versionInfo.value("QT_INSTALL_EXAMPLES"));
1298         if (fi.exists())
1299             m_hasExamples = true;
1300     }
1301     if (m_versionInfo.contains("QT_INSTALL_DEMOS")){
1302         QFileInfo fi(m_versionInfo.value("QT_INSTALL_DEMOS"));
1303         if (fi.exists())
1304             m_hasDemos = true;
1305     }
1306
1307     m_versionInfoUpToDate = true;
1308 }
1309
1310 QString QtVersion::findQtBinary(const QStringList &possibleCommands) const
1311 {
1312     const QString qtdirbin = versionInfo().value(QLatin1String("QT_INSTALL_BINS")) + QLatin1Char('/');
1313     foreach (const QString &possibleCommand, possibleCommands) {
1314         const QString fullPath = qtdirbin + possibleCommand;
1315         if (QFileInfo(fullPath).isFile())
1316             return QDir::cleanPath(fullPath);
1317     }
1318     return QString();
1319 }
1320
1321 QString QtVersion::uicCommand() const
1322 {
1323     if (!isValid())
1324         return QString();
1325     if (!m_uicCommand.isNull())
1326         return m_uicCommand;
1327 #ifdef Q_OS_WIN
1328     const QStringList possibleCommands(QLatin1String("uic.exe"));
1329 #else
1330     QStringList possibleCommands;
1331     possibleCommands << QLatin1String("uic-qt4") << QLatin1String("uic4") << QLatin1String("uic");
1332 #endif
1333     m_uicCommand = findQtBinary(possibleCommands);
1334     return m_uicCommand;
1335 }
1336
1337 // Return a list of GUI binary names
1338 // 'foo', 'foo.exe', 'Foo.app/Contents/MacOS/Foo'
1339 static inline QStringList possibleGuiBinaries(const QString &name)
1340 {
1341 #ifdef Q_OS_WIN
1342     return QStringList(name + QLatin1String(".exe"));
1343 #elif defined(Q_OS_MAC) // 'Foo.app/Contents/MacOS/Foo'
1344     QString upCaseName = name;
1345     upCaseName[0] = upCaseName.at(0).toUpper();
1346     QString macBinary = upCaseName;
1347     macBinary += QLatin1String(".app/Contents/MacOS/");
1348     macBinary += upCaseName;
1349     return QStringList(macBinary);
1350 #else
1351     return QStringList(name);
1352 #endif
1353 }
1354
1355 QString QtVersion::designerCommand() const
1356 {
1357     if (!isValid())
1358         return QString();
1359     if (m_designerCommand.isNull())
1360         m_designerCommand = findQtBinary(possibleGuiBinaries(QLatin1String("designer")));
1361     return m_designerCommand;
1362 }
1363
1364 QString QtVersion::linguistCommand() const
1365 {
1366     if (!isValid())
1367         return QString();
1368     if (m_linguistCommand.isNull())
1369         m_linguistCommand = findQtBinary(possibleGuiBinaries(QLatin1String("linguist")));
1370     return m_linguistCommand;
1371 }
1372
1373 QString QtVersion::qmlviewerCommand() const
1374 {
1375     if (!isValid())
1376         return QString();
1377
1378     if (m_qmlviewerCommand.isNull()) {
1379 #ifdef Q_OS_MAC
1380         const QString qmlViewerName = QLatin1String("QMLViewer");
1381 #else
1382         const QString qmlViewerName = QLatin1String("qmlviewer");
1383 #endif
1384
1385         m_qmlviewerCommand = findQtBinary(possibleGuiBinaries(qmlViewerName));
1386     }
1387     return m_qmlviewerCommand;
1388 }
1389
1390 void QtVersion::setSystemRoot(const QString &root)
1391 {
1392     if (root == m_systemRoot)
1393         return;
1394     m_systemRoot = root;
1395     m_abiUpToDate = false;
1396 }
1397
1398 QString QtVersion::systemRoot() const
1399 {
1400     updateAbiAndMkspec();
1401     return m_systemRoot;
1402 }
1403
1404 bool QtVersion::supportsTargetId(const QString &id) const
1405 {
1406     updateAbiAndMkspec();
1407     return m_targetIds.contains(id);
1408 }
1409
1410 QSet<QString> QtVersion::supportedTargetIds() const
1411 {
1412     updateAbiAndMkspec();
1413     return m_targetIds;
1414 }
1415
1416 QList<ProjectExplorer::Abi> QtVersion::qtAbis() const
1417 {
1418     updateAbiAndMkspec();
1419     return m_abis;
1420 }
1421
1422 // if none, then it's INVALID everywhere this function is called
1423 void QtVersion::updateAbiAndMkspec() const
1424 {
1425     if (m_id == -1 || m_abiUpToDate)
1426         return;
1427
1428     m_abiUpToDate = true;
1429
1430     m_targetIds.clear();
1431     m_abis.clear();
1432     m_validSystemRoot = true;
1433
1434 //    qDebug()<<"Finding mkspec for"<<qmakeCommand();
1435
1436     // no .qmake.cache so look at the default mkspec
1437
1438     QString baseMkspecDir = versionInfo().value("QMAKE_MKSPECS");
1439     if (baseMkspecDir.isEmpty())
1440         baseMkspecDir = versionInfo().value("QT_INSTALL_DATA") + "/mkspecs";
1441
1442 #ifdef Q_OS_WIN
1443     baseMkspecDir = baseMkspecDir.toLower();
1444 #endif
1445
1446     QString mkspecFullPath = baseMkspecDir + "/default";
1447
1448     // qDebug() << "default mkspec is located at" << mkspecFullPath;
1449
1450 #ifdef Q_OS_WIN
1451     QFile f2(mkspecFullPath + "/qmake.conf");
1452     if (f2.exists() && f2.open(QIODevice::ReadOnly)) {
1453         while (!f2.atEnd()) {
1454             QByteArray line = f2.readLine();
1455             if (line.startsWith("QMAKESPEC_ORIGINAL")) {
1456                 const QList<QByteArray> &temp = line.split('=');
1457                 if (temp.size() == 2) {
1458                     QString possibleFullPath = temp.at(1).trimmed();
1459                     // We sometimes get a mix of different slash styles here...
1460                     possibleFullPath = possibleFullPath.replace('\\', '/');
1461                     if (QFileInfo(possibleFullPath).exists()) // Only if the path exists
1462                         mkspecFullPath = possibleFullPath;
1463                 }
1464                 break;
1465             }
1466         }
1467         f2.close();
1468     }
1469 #elif defined(Q_OS_MAC)
1470     QFile f2(mkspecFullPath + "/qmake.conf");
1471     if (f2.exists() && f2.open(QIODevice::ReadOnly)) {
1472         while (!f2.atEnd()) {
1473             QByteArray line = f2.readLine();
1474             if (line.startsWith("MAKEFILE_GENERATOR")) {
1475                 const QList<QByteArray> &temp = line.split('=');
1476                 if (temp.size() == 2) {
1477                     const QByteArray &value = temp.at(1);
1478                     if (value.contains("XCODE")) {
1479                         // we don't want to generate xcode projects...
1480 //                      qDebug() << "default mkspec is xcode, falling back to g++";
1481                         mkspecFullPath = baseMkspecDir + "/macx-g++";
1482                     }
1483                     //resolve mkspec link
1484                     mkspecFullPath = resolveLink(mkspecFullPath);
1485                 }
1486                 break;
1487             }
1488         }
1489         f2.close();
1490     }
1491 #else
1492     mkspecFullPath =resolveLink(mkspecFullPath);
1493 #endif
1494
1495 #ifdef Q_OS_WIN
1496     mkspecFullPath = mkspecFullPath.toLower();
1497 #endif
1498
1499     m_mkspecFullPath = mkspecFullPath;
1500     QString mkspec = m_mkspecFullPath;
1501
1502     if (mkspec.startsWith(baseMkspecDir)) {
1503         mkspec = mkspec.mid(baseMkspecDir.length() + 1);
1504 //        qDebug() << "Setting mkspec to"<<mkspec;
1505     } else {
1506         QString sourceMkSpecPath = sourcePath() + "/mkspecs";
1507         if (mkspec.startsWith(sourceMkSpecPath)) {
1508             mkspec = mkspec.mid(sourceMkSpecPath.length() + 1);
1509         } else {
1510             // Do nothing
1511         }
1512     }
1513
1514     m_mkspec = mkspec;
1515     m_isBuildUsingSbsV2 = false;
1516
1517     ProFileOption option;
1518     option.properties = versionInfo();
1519     ProMessageHandler msgHandler(true);
1520     ProFileCacheManager::instance()->incRefCount();
1521     ProFileParser parser(ProFileCacheManager::instance()->cache(), &msgHandler);
1522     ProFileEvaluator evaluator(&option, &parser, &msgHandler);
1523     if (ProFile *pro = parser.parsedProFile(m_mkspecFullPath + "/qmake.conf")) {
1524         evaluator.setCumulative(false);
1525         evaluator.accept(pro, ProFileEvaluator::LoadProOnly);
1526         pro->deref();
1527     }
1528
1529     QString qmakeCXX = evaluator.values("QMAKE_CXX").join(" ");
1530     QString makefileGenerator = evaluator.value("MAKEFILE_GENERATOR");
1531     QString ce_sdk = evaluator.values("CE_SDK").join(QLatin1String(" "));
1532     QString ce_arch = evaluator.value("CE_ARCH");
1533
1534     const QString coreLibrary = qtCorePath();
1535
1536     // Evaluate all the information we have:
1537     if (!ce_sdk.isEmpty() && !ce_arch.isEmpty()) {
1538         // Treat windows CE as desktop.
1539         m_abis.append(ProjectExplorer::Abi(ProjectExplorer::Abi::ArmArchitecture, ProjectExplorer::Abi::WindowsOS,
1540                                            ProjectExplorer::Abi::WindowsCEFlavor, ProjectExplorer::Abi::PEFormat, false));
1541         m_targetIds.insert(Constants::DESKTOP_TARGET_ID);
1542     } else if (makefileGenerator == QLatin1String("SYMBIAN_ABLD") ||
1543                makefileGenerator == QLatin1String("SYMBIAN_SBSV2") ||
1544                makefileGenerator == QLatin1String("SYMBIAN_UNIX")) {
1545         m_isBuildUsingSbsV2 = (makefileGenerator == QLatin1String("SYMBIAN_SBSV2"));
1546         if (S60Manager::instance()) {
1547             m_abis.append(ProjectExplorer::Abi(ProjectExplorer::Abi::ArmArchitecture, ProjectExplorer::Abi::SymbianOS,
1548                                                ProjectExplorer::Abi::UnknownFlavor,
1549                                                ProjectExplorer::Abi::ElfFormat,
1550                                                32));
1551             m_targetIds.insert(QLatin1String(Constants::S60_DEVICE_TARGET_ID));
1552             m_targetIds.insert(QLatin1String(Constants::S60_EMULATOR_TARGET_ID));
1553         }
1554     } else if (MaemoGlobal::isValidMaemo5QtVersion(this)) {
1555         m_abis.append(ProjectExplorer::Abi(ProjectExplorer::Abi::ArmArchitecture, ProjectExplorer::Abi::LinuxOS,
1556                                            ProjectExplorer::Abi::MaemoLinuxFlavor, ProjectExplorer::Abi::ElfFormat,
1557                                            32));
1558         m_targetIds.insert(QLatin1String(Constants::MAEMO5_DEVICE_TARGET_ID));
1559     } else if (MaemoGlobal::isValidHarmattanQtVersion(this)) {
1560         m_abis.append(ProjectExplorer::Abi(ProjectExplorer::Abi::ArmArchitecture, ProjectExplorer::Abi::LinuxOS,
1561                                            ProjectExplorer::Abi::HarmattanLinuxFlavor,
1562                                            ProjectExplorer::Abi::ElfFormat,
1563                                            32));
1564         m_targetIds.insert(QLatin1String(Constants::HARMATTAN_DEVICE_TARGET_ID));
1565     } else if (MaemoGlobal::isValidMeegoQtVersion(this)) {
1566         m_abis.append(ProjectExplorer::Abi(ProjectExplorer::Abi::ArmArchitecture, ProjectExplorer::Abi::LinuxOS,
1567                                            ProjectExplorer::Abi::MeegoLinuxFlavor,
1568                                            ProjectExplorer::Abi::ElfFormat, 32));
1569         m_targetIds.insert(QLatin1String(Constants::MEEGO_DEVICE_TARGET_ID));
1570     } else if (qmakeCXX.contains("g++")
1571                || qmakeCXX == "cl" || qmakeCXX == "icl" // intel cl
1572                || qmakeCXX == QLatin1String("icpc")) {
1573         m_abis = ProjectExplorer::Abi::abisOfBinary(coreLibrary);
1574 #if defined (Q_OS_WIN)
1575         if (makefileGenerator == "MINGW") {
1576             QList<ProjectExplorer::Abi> tmp = m_abis;
1577             m_abis.clear();
1578             foreach (const ProjectExplorer::Abi &abi, tmp)
1579                 m_abis.append(ProjectExplorer::Abi(abi.architecture(), abi.os(), ProjectExplorer::Abi::WindowsMSysFlavor,
1580                                                    abi.binaryFormat(), abi.wordWidth()));
1581         }
1582 #endif
1583         m_targetIds.insert(QLatin1String(Constants::DESKTOP_TARGET_ID));
1584     }
1585
1586     if (m_abis.isEmpty() && !coreLibrary.isEmpty()) {
1587         qWarning("Warning: Could not find ABI for '%s' ('%s', %s)/%s by looking at %s:"
1588                  "Qt Creator does not know about the system includes, "
1589                  "nor the system defines.",
1590                  qPrintable(m_mkspecFullPath), qPrintable(displayName()),
1591                  qPrintable(qmakeCommand()), qPrintable(qmakeCXX), qPrintable(coreLibrary));
1592     }
1593
1594     QStringList configValues = evaluator.values("CONFIG");
1595     m_defaultConfigIsDebugAndRelease = false;
1596     foreach(const QString &value, configValues) {
1597         if (value == "debug")
1598             m_defaultConfigIsDebug = true;
1599         else if (value == "release")
1600             m_defaultConfigIsDebug = false;
1601         else if (value == "build_all")
1602             m_defaultConfigIsDebugAndRelease = true;
1603     }
1604     // Is this actually a simulator Qt?
1605     if (configValues.contains(QLatin1String("simulator"))) {
1606         m_targetIds.clear();
1607         m_targetIds.insert(QLatin1String(Constants::QT_SIMULATOR_TARGET_ID));
1608     }
1609     ProFileCacheManager::instance()->decRefCount();
1610
1611     // Set up systemroot
1612     if (supportsTargetId(Constants::MAEMO5_DEVICE_TARGET_ID)
1613             || supportsTargetId(Constants::HARMATTAN_DEVICE_TARGET_ID)) {
1614         if (m_systemRoot.isNull()) {
1615             QFile file(QDir::cleanPath(MaemoGlobal::targetRoot(this))
1616                        + QLatin1String("/information"));
1617             if (file.exists() && file.open(QIODevice::ReadOnly | QIODevice::Text)) {
1618                 QTextStream stream(&file);
1619                 while (!stream.atEnd()) {
1620                     const QString &line = stream.readLine().trimmed();
1621                     const QStringList &list = line.split(QLatin1Char(' '));
1622                     if (list.count() <= 1)
1623                         continue;
1624                     if (list.at(0) == QLatin1String("sysroot")) {
1625                         m_systemRoot = MaemoGlobal::maddeRoot(this)
1626                                 + QLatin1String("/sysroots/") + list.at(1);
1627                     }
1628                 }
1629             }
1630         }
1631     } else if (supportsTargetId(Constants::S60_DEVICE_TARGET_ID)
1632            || supportsTargetId(Constants::S60_EMULATOR_TARGET_ID)) {
1633         if (m_systemRoot.isEmpty())
1634             m_validSystemRoot = false;
1635
1636         if (!m_systemRoot.endsWith(QLatin1Char('/')))
1637             m_systemRoot.append(QLatin1Char('/'));
1638
1639         QFileInfo cppheader(m_systemRoot + QLatin1String("epoc32/include/stdapis/string.h"));
1640         if (!cppheader.exists())
1641             m_validSystemRoot = false;
1642     } else {
1643         m_systemRoot = QLatin1String("");
1644     }
1645 }
1646
1647 QString QtVersion::resolveLink(const QString &path) const
1648 {
1649     QFileInfo f(path);
1650     int links = 16;
1651     while (links-- && f.isSymLink())
1652         f.setFile(f.symLinkTarget());
1653     if (links <= 0)
1654         return QString();
1655     return f.filePath();
1656 }
1657
1658 QString QtVersion::qtCorePath() const
1659 {
1660     QList<QDir> dirs;
1661     dirs << QDir(libraryInstallPath()) << QDir(versionInfo().value(QLatin1String("QT_INSTALL_BINS")));
1662
1663     QFileInfoList staticLibs;
1664     foreach (const QDir &d, dirs) {
1665         QFileInfoList infoList = d.entryInfoList();
1666         foreach (const QFileInfo &info, infoList) {
1667             const QString file = info.fileName();
1668             if (info.isDir()
1669                     && file.startsWith(QLatin1String("QtCore"))
1670                     && file.endsWith(QLatin1String(".framework"))) {
1671                 // handle Framework
1672                 const QString libName = file.left(file.lastIndexOf('.'));
1673                 return info.absoluteFilePath() + '/' + libName;
1674             }
1675             if (info.isReadable()) {
1676                 if (file.startsWith(QLatin1String("libQtCore"))
1677                         || file.startsWith(QLatin1String("QtCore"))) {
1678                     // Only handle static libs if we can not find dynamic ones:
1679                     if (file.endsWith(".a") || file.endsWith(".lib"))
1680                         staticLibs.append(info);
1681                     else if (file.endsWith(QLatin1String(".dll"))
1682                                 || file.endsWith(QString::fromLatin1(".so.") + qtVersionString())
1683                                 || file.endsWith(QLatin1Char('.') + qtVersionString() + QLatin1String(".dylib")))
1684                         return info.absoluteFilePath();
1685                 }
1686             }
1687         }
1688     }
1689     // Return path to first static library found:
1690     if (!staticLibs.isEmpty())
1691         return staticLibs.at(0).absoluteFilePath();
1692     return QString();
1693 }
1694
1695 QString QtVersion::sbsV2Directory() const
1696 {
1697     return m_sbsV2Directory;
1698 }
1699
1700 void QtVersion::setSbsV2Directory(const QString &directory)
1701 {
1702     m_sbsV2Directory = directory;
1703 }
1704
1705 void QtVersion::addToEnvironment(Utils::Environment &env) const
1706 {
1707     // Generic:
1708     env.set("QTDIR", QDir::toNativeSeparators(versionInfo().value("QT_INSTALL_DATA")));
1709     env.prependOrSetPath(versionInfo().value("QT_INSTALL_BINS"));
1710
1711     // Symbian specific:
1712     if (supportsTargetId(Constants::S60_DEVICE_TARGET_ID)
1713             || supportsTargetId(Constants::S60_EMULATOR_TARGET_ID)) {
1714         // Generic Symbian environment:
1715         QString epocRootPath(systemRoot());
1716         QDir epocDir(epocRootPath);
1717
1718         // Clean up epoc root path for the environment:
1719         if (!epocRootPath.endsWith(QLatin1Char('/')))
1720             epocRootPath.append(QLatin1Char('/'));
1721         if (!isBuildWithSymbianSbsV2()) {
1722 #ifdef Q_OS_WIN
1723             if (epocRootPath.count() > 2
1724                     && epocRootPath.at(0).toLower() >= QLatin1Char('a')
1725                     && epocRootPath.at(0).toLower() <= QLatin1Char('z')
1726                     && epocRootPath.at(1) == QLatin1Char(':')) {
1727                 epocRootPath = epocRootPath.mid(2);
1728             }
1729 #endif
1730         }
1731         env.set(QLatin1String("EPOCROOT"), QDir::toNativeSeparators(epocRootPath));
1732
1733         env.prependOrSetPath(epocDir.filePath(QLatin1String("epoc32/tools"))); // e.g. make.exe
1734         // Windows only:
1735         if (ProjectExplorer::Abi::hostAbi().os() == ProjectExplorer::Abi::WindowsOS) {
1736             QString winDir = QLatin1String(qgetenv("WINDIR"));
1737             if (!winDir.isEmpty())
1738                 env.prependOrSetPath(QDir(winDir).filePath(QLatin1String("system32")));
1739
1740             if (epocDir.exists(QLatin1String("epoc32/gcc/bin")))
1741                 env.prependOrSetPath(epocDir.filePath(QLatin1String("epoc32/gcc/bin"))); // e.g. cpp.exe, *NOT* gcc.exe
1742             // Find perl in the special Symbian flavour:
1743             if (epocDir.exists(QLatin1String("../../tools/perl/bin"))) {
1744                 epocDir.cd(QLatin1String("../../tools/perl/bin"));
1745                 env.prependOrSetPath(epocDir.absolutePath());
1746             } else {
1747                 env.prependOrSetPath(epocDir.filePath(QLatin1String("perl/bin")));
1748             }
1749         }
1750
1751         // SBSv2:
1752         if (isBuildWithSymbianSbsV2()) {
1753             QString sbsHome(env.value(QLatin1String("SBS_HOME")));
1754             if (!m_sbsV2Directory.isEmpty()) {
1755                 env.prependOrSetPath(m_sbsV2Directory + QLatin1String("/bin"));
1756                 env.unset(QLatin1String("SBS_HOME")); // unset SBS_HOME to prevent SBS from picking it up
1757             } else if (!sbsHome.isEmpty()) {
1758                 env.prependOrSetPath(sbsHome + QLatin1Char('/') + QLatin1String("bin"));
1759             }
1760         }
1761     }
1762 }
1763
1764 static const char *S60_EPOC_HEADERS[] = {
1765     "include", "mkspecs/common/symbian", "epoc32/include",
1766     "epoc32/include/osextensions/stdapis", "epoc32/include/osextensions/stdapis/sys",
1767     "epoc32/include/stdapis", "epoc32/include/stdapis/sys",
1768     "epoc32/include/osextensions/stdapis/stlport", "epoc32/include/stdapis/stlport",
1769     "epoc32/include/oem", "epoc32/include/middleware", "epoc32/include/domain/middleware",
1770     "epoc32/include/osextensions", "epoc32/include/domain/osextensions",
1771     "epoc32/include/domain/osextensions/loc", "epoc32/include/domain/middleware/loc",
1772     "epoc32/include/domain/osextensions/loc/sc", "epoc32/include/domain/middleware/loc/sc"
1773 };
1774
1775 QList<ProjectExplorer::HeaderPath> QtVersion::systemHeaderPathes() const
1776 {
1777     QList<ProjectExplorer::HeaderPath> result;
1778     if (supportsTargetId(Constants::S60_DEVICE_TARGET_ID)
1779             || supportsTargetId(Constants::S60_EMULATOR_TARGET_ID)) {
1780         QString root = systemRoot() + QLatin1Char('/');
1781         const int count = sizeof(S60_EPOC_HEADERS) / sizeof(const char *);
1782         for (int i = 0; i < count; ++i) {
1783             const QDir dir(root + QLatin1String(S60_EPOC_HEADERS[i]));
1784             if (dir.exists())
1785             result.append(ProjectExplorer::HeaderPath(dir.absolutePath(),
1786                                                       ProjectExplorer::HeaderPath::GlobalHeaderPath));
1787         }
1788     }
1789     return result;
1790 }
1791
1792 int QtVersion::uniqueId() const
1793 {
1794     return m_id;
1795 }
1796
1797 int QtVersion::getUniqueId()
1798 {
1799     return QtVersionManager::instance()->getUniqueId();
1800 }
1801
1802 bool QtVersion::isValid() const
1803 {
1804     updateVersionInfo();
1805     updateAbiAndMkspec();
1806
1807     return m_id != -1
1808             && !qmakeCommand().isEmpty()
1809             && !displayName().isEmpty()
1810             && !m_notInstalled
1811             && m_versionInfo.contains("QT_INSTALL_BINS")
1812             && (!m_mkspecFullPath.isEmpty() || !m_abiUpToDate)
1813             && !m_abis.isEmpty()
1814             && m_qmakeIsExecutable
1815             && m_validSystemRoot;
1816 }
1817
1818 bool QtVersion::toolChainAvailable() const
1819 {
1820     if (!isValid())
1821         return false;
1822     foreach (const ProjectExplorer::Abi &abi, qtAbis())
1823         if (!ProjectExplorer::ToolChainManager::instance()->findToolChains(abi).isEmpty())
1824             return true;
1825     return false;
1826 }
1827
1828 QString QtVersion::invalidReason() const
1829 {
1830     if (isValid())
1831         return QString();
1832     if (qmakeCommand().isEmpty())
1833         return QCoreApplication::translate("QtVersion", "No qmake path set");
1834     if (!m_qmakeIsExecutable)
1835         return QCoreApplication::translate("QtVersion", "qmake does not exist or is not executable");
1836     if (displayName().isEmpty())
1837         return QCoreApplication::translate("QtVersion", "Qt version has no name");
1838     if (m_notInstalled)
1839         return QCoreApplication::translate("QtVersion", "Qt version is not properly installed, please run make install");
1840     if (!m_versionInfo.contains("QT_INSTALL_BINS"))
1841         return QCoreApplication::translate("QtVersion",
1842                                            "Could not determine the path to the binaries of the Qt installation, maybe the qmake path is wrong?");
1843     if (m_abiUpToDate && m_mkspecFullPath.isEmpty())
1844         return QCoreApplication::translate("QtVersion", "The default mkspec symlink is broken.");
1845     if (m_abiUpToDate && m_abis.isEmpty())
1846         return QCoreApplication::translate("QtVersion", "Failed to detect the ABI(s) used by the Qt version.");
1847     if (!m_validSystemRoot)
1848         return QCoreApplication::translate("QtVersion", "The \"Open C/C++ plugin\" is not installed in the Symbian SDK or the Symbian SDK path is misconfigured");
1849     return QString();
1850 }
1851
1852 QString QtVersion::description() const
1853 {
1854     if (!isValid())
1855         return invalidReason();
1856     QSet<QString> targets = supportedTargetIds();
1857     QString envs;
1858     if (targets.contains(Constants::DESKTOP_TARGET_ID))
1859         envs = QCoreApplication::translate("QtVersion", "Desktop", "Qt Version is meant for the desktop");
1860     else if (targets.contains(Constants::S60_DEVICE_TARGET_ID) ||
1861              targets.contains(Constants::S60_EMULATOR_TARGET_ID))
1862         envs = QCoreApplication::translate("QtVersion", "Symbian", "Qt Version is meant for Symbian");
1863     else if (targets.contains(Constants::MAEMO5_DEVICE_TARGET_ID))
1864         envs = QCoreApplication::translate("QtVersion", "Maemo", "Qt Version is meant for Maemo5");
1865     else if (targets.contains(Constants::HARMATTAN_DEVICE_TARGET_ID))
1866         envs = QCoreApplication::translate("QtVersion", "Harmattan ", "Qt Version is meant for Harmattan");
1867     else if (targets.contains(Constants::MEEGO_DEVICE_TARGET_ID))
1868         envs = QCoreApplication::translate("QtVersion", "Meego", "Qt Version is meant for Meego");
1869     else if (targets.contains(Constants::QT_SIMULATOR_TARGET_ID))
1870         envs = QCoreApplication::translate("QtVersion", "Qt Simulator", "Qt Version is meant for Qt Simulator");
1871     else
1872         envs = QCoreApplication::translate("QtVersion", "unkown", "No idea what this Qt Version is meant for!");
1873     return QCoreApplication::translate("QtVersion", "Qt version %1, using mkspec %2 (%3)")
1874            .arg(qtVersionString(), mkspec(), envs);
1875 }
1876
1877 QtVersion::QmakeBuildConfigs QtVersion::defaultBuildConfig() const
1878 {
1879     updateAbiAndMkspec();
1880     QtVersion::QmakeBuildConfigs result = QtVersion::QmakeBuildConfig(0);
1881
1882     if (m_defaultConfigIsDebugAndRelease)
1883         result = QtVersion::BuildAll;
1884     if (m_defaultConfigIsDebug)
1885         result = result | QtVersion::DebugBuild;
1886     return result;
1887 }
1888
1889 bool QtVersion::hasGdbDebuggingHelper() const
1890 {
1891     updateVersionInfo();
1892     return m_hasDebuggingHelper;
1893 }
1894
1895
1896 bool QtVersion::hasQmlDump() const
1897 {
1898     updateVersionInfo();
1899     return m_hasQmlDump;
1900 }
1901
1902 bool QtVersion::hasQmlDebuggingLibrary() const
1903 {
1904     updateVersionInfo();
1905     return m_hasQmlDebuggingLibrary;
1906 }
1907
1908 bool QtVersion::hasQmlObserver() const
1909 {
1910     updateVersionInfo();
1911     return m_hasQmlObserver;
1912 }
1913
1914 Utils::Environment QtVersion::qmlToolsEnvironment() const
1915 {
1916     // FIXME: This seems broken!
1917     Utils::Environment environment = Utils::Environment::systemEnvironment();
1918     addToEnvironment(environment);
1919
1920     // add preferred tool chain, as that is how the tools are built, compare QtVersion::buildDebuggingHelperLibrary
1921     QList<ProjectExplorer::ToolChain *> alltc =
1922             ProjectExplorer::ToolChainManager::instance()->findToolChains(qtAbis().at(0));
1923     if (!alltc.isEmpty())
1924         alltc.first()->addToEnvironment(environment);
1925
1926     return environment;
1927 }
1928
1929 QString QtVersion::gdbDebuggingHelperLibrary() const
1930 {
1931     QString qtInstallData = versionInfo().value("QT_INSTALL_DATA");
1932     if (qtInstallData.isEmpty())
1933         return QString();
1934     return DebuggingHelperLibrary::debuggingHelperLibraryByInstallData(qtInstallData);
1935 }
1936
1937 QString QtVersion::qmlDumpTool(bool debugVersion) const
1938 {
1939     QString qtInstallData = versionInfo().value("QT_INSTALL_DATA");
1940     if (qtInstallData.isEmpty())
1941         return QString();
1942     return QmlDumpTool::toolByInstallData(qtInstallData, debugVersion);
1943 }
1944
1945 QString QtVersion::qmlDebuggingHelperLibrary(bool debugVersion) const
1946 {
1947     QString qtInstallData = versionInfo().value("QT_INSTALL_DATA");
1948     if (qtInstallData.isEmpty())
1949         return QString();
1950     return QmlDebuggingLibrary::libraryByInstallData(qtInstallData, debugVersion);
1951 }
1952
1953 QString QtVersion::qmlObserverTool() const
1954 {
1955     QString qtInstallData = versionInfo().value("QT_INSTALL_DATA");
1956     if (qtInstallData.isEmpty())
1957         return QString();
1958     return QmlObserverTool::toolByInstallData(qtInstallData);
1959 }
1960
1961 QStringList QtVersion::debuggingHelperLibraryLocations() const
1962 {
1963     QString qtInstallData = versionInfo().value("QT_INSTALL_DATA");
1964     if (qtInstallData.isEmpty())
1965         return QStringList();
1966     return DebuggingHelperLibrary::locationsByInstallData(qtInstallData);
1967 }
1968
1969 bool QtVersion::supportsBinaryDebuggingHelper() const
1970 {
1971     if (!isValid())
1972         return false;
1973     return qtAbis().at(0).os() != ProjectExplorer::Abi::SymbianOS;
1974 }
1975
1976 bool QtVersion::hasDocumentation() const
1977 {
1978     updateVersionInfo();
1979     return m_hasDocumentation;
1980 }
1981
1982 QString QtVersion::documentationPath() const
1983 {
1984     updateVersionInfo();
1985     return m_versionInfo["QT_INSTALL_DOCS"];
1986 }
1987
1988 bool QtVersion::hasDemos() const
1989 {
1990     updateVersionInfo();
1991     return m_hasDemos;
1992 }
1993
1994 QString QtVersion::demosPath() const
1995 {
1996     updateVersionInfo();
1997     return m_versionInfo["QT_INSTALL_DEMOS"];
1998 }
1999
2000 QString QtVersion::headerInstallPath() const
2001 {
2002     updateVersionInfo();
2003     return m_versionInfo["QT_INSTALL_HEADERS"];
2004 }
2005
2006 QString QtVersion::frameworkInstallPath() const
2007 {
2008 #ifdef Q_OS_MAC
2009     updateVersionInfo();
2010     return m_versionInfo["QT_INSTALL_LIBS"];
2011 #else
2012     return QString();
2013 #endif
2014 }
2015
2016 QString QtVersion::libraryInstallPath() const
2017 {
2018     updateVersionInfo();
2019     return m_versionInfo["QT_INSTALL_LIBS"];
2020 }
2021
2022 bool QtVersion::hasExamples() const
2023 {
2024     updateVersionInfo();
2025     return m_hasExamples;
2026 }
2027
2028 QString QtVersion::examplesPath() const
2029 {
2030     updateVersionInfo();
2031     return m_versionInfo["QT_INSTALL_EXAMPLES"];
2032 }
2033
2034 void QtVersion::invalidateCache()
2035 {
2036     m_versionInfoUpToDate = false;
2037 }
2038
2039 ///////////////
2040 // QtVersionNumber
2041 ///////////////
2042
2043
2044 QtVersionNumber::QtVersionNumber(int ma, int mi, int p)
2045     : majorVersion(ma), minorVersion(mi), patchVersion(p)
2046 {
2047 }
2048
2049 QtVersionNumber::QtVersionNumber(const QString &versionString)
2050 {
2051     if (!checkVersionString(versionString)) {
2052         majorVersion = minorVersion = patchVersion = -1;
2053         return;
2054     }
2055
2056     QStringList parts = versionString.split(QLatin1Char('.'));
2057     majorVersion = parts.at(0).toInt();
2058     minorVersion = parts.at(1).toInt();
2059     patchVersion = parts.at(2).toInt();
2060 }
2061
2062 QtVersionNumber::QtVersionNumber()
2063 {
2064     majorVersion = minorVersion = patchVersion = -1;
2065 }
2066
2067
2068 bool QtVersionNumber::checkVersionString(const QString &version) const
2069 {
2070     int dots = 0;
2071     QString validChars = "0123456789.";
2072     foreach (const QChar &c, version) {
2073         if (!validChars.contains(c))
2074             return false;
2075         if (c == '.')
2076             ++dots;
2077     }
2078     if (dots != 2)
2079         return false;
2080     return true;
2081 }
2082
2083 bool QtVersionNumber::operator <(const QtVersionNumber &b) const
2084 {
2085     if (majorVersion < b.majorVersion)
2086         return true;
2087     if (majorVersion > b.majorVersion)
2088         return false;
2089     if (minorVersion < b.minorVersion)
2090         return true;
2091     if (minorVersion > b.minorVersion)
2092         return false;
2093     if (patchVersion < b.patchVersion)
2094         return true;
2095     return false;
2096 }
2097
2098 bool QtVersionNumber::operator >(const QtVersionNumber &b) const
2099 {
2100     return b < *this;
2101 }
2102
2103 bool QtVersionNumber::operator ==(const QtVersionNumber &b) const
2104 {
2105     return majorVersion == b.majorVersion
2106             && minorVersion == b.minorVersion
2107             && patchVersion == b.patchVersion;
2108 }
2109
2110 bool QtVersionNumber::operator !=(const QtVersionNumber &b) const
2111 {
2112     return !(*this == b);
2113 }
2114
2115 bool QtVersionNumber::operator <=(const QtVersionNumber &b) const
2116 {
2117     return !(*this > b);
2118 }
2119
2120 bool QtVersionNumber::operator >=(const QtVersionNumber &b) const
2121 {
2122     return b <= *this;
2123 }