1 /**************************************************************************
3 ** This file is part of Qt Creator
5 ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
7 ** Contact: Nokia Corporation (info@qt.nokia.com)
10 ** GNU Lesser General Public License Usage
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.
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.
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.
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
31 **************************************************************************/
33 #include "qtversionmanager.h"
35 #include "qt4projectmanagerconstants.h"
36 #include "qt4target.h"
37 #include "profilereader.h"
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"
48 #include "qmlobservertool.h"
49 #include "qmldumptool.h"
50 #include "qmldebugginglibrary.h"
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>
64 #include <coreplugin/coreconstants.h>
65 #include <coreplugin/icore.h>
66 #include <coreplugin/helpmanager.h>
68 #include <extensionsystem/pluginmanager.h>
70 #include <utils/synchronousprocess.h>
71 #include <utils/qtcassert.h>
72 #include <utils/qtcprocess.h>
74 # include <utils/winutils.h>
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>
89 using namespace Qt4ProjectManager;
90 using namespace Qt4ProjectManager::Internal;
92 using ProjectExplorer::DebuggingHelperLibrary;
94 static const char *QtVersionsSectionName = "QtVersions";
95 static const char *newQtVersionsKey = "NewQtVersions";
96 static const char *PATH_AUTODETECTION_SOURCE = "PATH";
101 static T *createToolChain(const QString &id)
103 QList<ProjectExplorer::ToolChainFactory *> factories =
104 ExtensionSystem::PluginManager::instance()->getObjects<ProjectExplorer::ToolChainFactory>();
105 foreach (ProjectExplorer::ToolChainFactory *f, factories) {
107 Q_ASSERT(f->canCreate());
108 return static_cast<T *>(f->create());
115 // prefer newer qts otherwise compare on id
116 bool qtVersionNumberCompare(QtVersion *a, QtVersion *b)
118 return a->qtVersion() > b->qtVersion() || (a->qtVersion() == b->qtVersion() && a->uniqueId() < b->uniqueId());
121 // --------------------------------------------------------------------------
123 // --------------------------------------------------------------------------
124 QtVersionManager *QtVersionManager::m_self = 0;
126 QtVersionManager::QtVersionManager()
127 : m_emptyVersion(new QtVersion)
130 QSettings *s = Core::ICore::instance()->settings();
133 int size = s->beginReadArray(QtVersionsSectionName);
134 for (int i = 0; i < size; ++i) {
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();
143 else if (m_idcount < id)
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();
153 autodetectionSource = QLatin1String(PATH_AUTODETECTION_SOURCE);
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())
161 QFileInfo fi(path + "/bin/" + command);
164 qmakePath = fi.filePath();
170 QtVersion *version = new QtVersion(s->value("Name").toString(),
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()));
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);
186 tc->setCompilerPath(fi.absoluteFilePath());
187 tc->setDisplayName(tr("MinGW from %1").arg(version->displayName()));
188 ProjectExplorer::ToolChainManager::instance()->registerToolChain(tc);
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);
199 m_versions.insert(version->uniqueId(), version);
204 addNewVersionsFromInstaller();
205 updateSystemVersion();
207 // cannot call from ctor, needs to get connected extenernally first
208 QTimer::singleShot(0, this, SLOT(updateSettings()));
211 QtVersionManager::~QtVersionManager()
213 qDeleteAll(m_versions);
215 delete m_emptyVersion;
219 QtVersionManager *QtVersionManager::instance()
224 void QtVersionManager::addVersion(QtVersion *version)
226 QTC_ASSERT(version != 0, return);
227 if (m_versions.contains(version->uniqueId()))
230 int uniqueId = version->uniqueId();
231 m_versions.insert(uniqueId, version);
233 emit qtVersionsChanged(QList<int>() << uniqueId);
234 writeVersionsIntoSettings();
237 void QtVersionManager::removeVersion(QtVersion *version)
239 QTC_ASSERT(version != 0, return);
240 m_versions.remove(version->uniqueId());
241 emit qtVersionsChanged(QList<int>() << version->uniqueId());
242 writeVersionsIntoSettings();
246 bool QtVersionManager::supportsTargetId(const QString &id) const
248 QList<QtVersion *> versions = QtVersionManager::instance()->versionsForTargetId(id);
249 foreach (QtVersion *v, versions)
250 if (v->isValid() && v->toolChainAvailable())
255 QList<QtVersion *> QtVersionManager::versionsForTargetId(const QString &id, const QtVersionNumber &minimumQtVersion) const
257 QList<QtVersion *> targetVersions;
258 foreach (QtVersion *version, m_versions) {
259 if (version->supportsTargetId(id) && version->qtVersion() >= minimumQtVersion)
260 targetVersions.append(version);
262 qSort(targetVersions.begin(), targetVersions.end(), &qtVersionNumberCompare);
263 return targetVersions;
266 QSet<QString> QtVersionManager::supportedTargetIds() const
268 QSet<QString> results;
269 foreach (QtVersion *version, m_versions)
270 results.unite(version->supportedTargetIds());
274 void QtVersionManager::updateDocumentation()
276 Core::HelpManager *helpManager = Core::HelpManager::instance();
277 Q_ASSERT(helpManager);
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;
287 helpManager->registerDocumentation(files);
290 void QtVersionManager::updateSettings()
292 writeVersionsIntoSettings();
294 updateDocumentation();
296 QtVersion *version = 0;
297 QList<QtVersion*> candidates;
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);
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()) {
311 preferred = preferred.toLower();
312 if (!preferred.endsWith(QLatin1String(".exe")))
313 preferred.append(QLatin1String(".exe"));
315 foreach (version, candidates) {
316 if (version->qmakeCommand() == preferred) {
317 emit updateExamples(version->examplesPath(), version->demosPath(), version->sourcePath());
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());
331 if (!candidates.isEmpty()) {
332 version = candidates.first();
333 emit updateExamples(version->examplesPath(), version->demosPath(), version->sourcePath());
340 int QtVersionManager::getUniqueId()
345 void QtVersionManager::writeVersionsIntoSettings()
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();
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"));
373 QList<QtVersion *> QtVersionManager::versions() const
375 QList<QtVersion *> versions;
376 foreach (QtVersion *version, m_versions)
378 qSort(versions.begin(), versions.end(), &qtVersionNumberCompare);
382 QList<QtVersion *> QtVersionManager::validVersions() const
384 QList<QtVersion *> results;
385 foreach(QtVersion *v, m_versions) {
389 qSort(results.begin(), results.end(), &qtVersionNumberCompare);
393 bool QtVersionManager::isValidId(int id) const
395 return m_versions.contains(id);
398 QString QtVersionManager::popPendingMwcUpdate()
400 if (m_pendingMwcUpdates.isEmpty())
402 return m_pendingMwcUpdates.takeFirst();
405 QString QtVersionManager::popPendingGcceUpdate()
407 if (m_pendingGcceUpdates.isEmpty())
409 return m_pendingGcceUpdates.takeFirst();
412 QtVersion *QtVersionManager::version(int id) const
414 QMap<int, QtVersion *>::const_iterator it = m_versions.find(id);
415 if (it == m_versions.constEnd())
416 return m_emptyVersion;
420 // FIXME: Rework this!
421 void QtVersionManager::addNewVersionsFromInstaller()
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;
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);
432 QDateTime lastUpdateFromGlobalSettings = globalSettings->value(
433 QLatin1String("General/LastQtVersionUpdate")).toDateTime();
435 const QFileInfo gsFi(globalSettings->fileName());
436 if ( !lastUpdateFromGlobalSettings.isNull() &&
437 (!gsFi.exists() || (gsFi.lastModified() > lastUpdateFromGlobalSettings)) )
440 if (!globalSettings->contains(newQtVersionsKey) &&
441 !globalSettings->contains(QLatin1String("Installer/")+newQtVersionsKey))
446 QString newVersionsValue = settings->value(newQtVersionsKey).toString();
447 if (newVersionsValue.isEmpty())
448 newVersionsValue = settings->value(QLatin1String("Installer/")+newQtVersionsKey).toString();
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]));
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;
469 if (!versionWasAlreadyInList) {
470 m_versions.insert(version->uniqueId(), version);
478 settings->setValue(QLatin1String("General/LastQtVersionUpdate"), QDateTime::currentDateTime());
481 void QtVersionManager::updateSystemVersion()
483 bool haveSystemVersion = false;
484 QString systemQMakePath = DebuggingHelperLibrary::findSystemQt(Utils::Environment::systemEnvironment());
485 if (systemQMakePath.isNull())
486 systemQMakePath = tr("<not found>");
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;
496 if (haveSystemVersion)
498 QtVersion *version = new QtVersion(tr("Qt in PATH"),
502 PATH_AUTODETECTION_SOURCE);
503 m_versions.insert(version->uniqueId(), version);
506 QtVersion *QtVersionManager::emptyVersion() const
508 return m_emptyVersion;
514 bool operator()(QtVersion *a, QtVersion *b)
516 return a->uniqueId() < b->uniqueId();
520 bool QtVersionManager::equals(QtVersion *a, QtVersion *b)
522 if (a->m_qmakeCommand != b->m_qmakeCommand)
524 if (a->m_id != b->m_id)
526 if (a->m_displayName != b->displayName())
531 void QtVersionManager::setNewQtVersions(QList<QtVersion *> newVersions)
533 // We want to preserve the same order as in the settings dialog
535 QList<QtVersion *> sortedNewVersions = newVersions;
536 SortByUniqueId sortByUniqueId;
537 qSort(sortedNewVersions.begin(), sortedNewVersions.end(), sortByUniqueId);
539 QList<int> changedVersions;
540 // So we trying to find the minimal set of changed versions,
541 // iterate over both sorted list
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();
551 while (nit != nend && oit != oend) {
552 int nid = (*nit)->uniqueId();
553 int oid = (*oit)->uniqueId();
555 changedVersions.push_back(nid);
557 } else if (oid < nid) {
558 changedVersions.push_back(oid);
561 if (!equals(*oit, *nit))
562 changedVersions.push_back(oid);
568 while (nit != nend) {
569 changedVersions.push_back((*nit)->uniqueId());
573 while (oit != oend) {
574 changedVersions.push_back((*oit)->uniqueId());
578 qDeleteAll(m_versions);
580 foreach (QtVersion *v, sortedNewVersions)
581 m_versions.insert(v->uniqueId(), v);
583 if (!changedVersions.isEmpty())
584 updateDocumentation();
587 writeVersionsIntoSettings();
589 if (!changedVersions.isEmpty())
590 emit qtVersionsChanged(changedVersions);
593 // --------------------------------------------------------------------------
595 // --------------------------------------------------------------------------
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),
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),
613 m_hasDocumentation(false),
614 m_qmakeIsExecutable(false),
615 m_validSystemRoot(true)
618 m_id = getUniqueId();
621 setQMakeCommand(qmakeCommand);
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),
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),
640 m_hasDocumentation(false),
641 m_qmakeIsExecutable(false),
642 m_validSystemRoot(true)
644 m_id = getUniqueId();
645 setQMakeCommand(qmakeCommand);
649 QtVersion::QtVersion(const QString &qmakeCommand, bool isAutodetected, const QString &autodetectionSource)
650 : m_isAutodetected(isAutodetected),
651 m_autodetectionSource(autodetectionSource),
652 m_hasDebuggingHelper(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),
663 m_hasDocumentation(false),
664 m_qmakeIsExecutable(false),
665 m_validSystemRoot(true)
667 m_id = getUniqueId();
668 setQMakeCommand(qmakeCommand);
669 m_displayName = qtVersionString();
672 QtVersion::QtVersion()
674 m_isAutodetected(false),
675 m_hasDebuggingHelper(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),
686 m_hasDocumentation(false) ,
687 m_qmakeIsExecutable(false),
688 m_validSystemRoot(true)
690 setQMakeCommand(QString());
693 QtVersion::~QtVersion()
697 QString QtVersion::toHtml(bool verbose) const
700 QTextStream str(&rc);
701 str << "<html><body><table>";
702 str << "<tr><td><b>" << QtVersionManager::tr("Name:")
703 << "</b></td><td>" << displayName() << "</td></tr>";
705 str << "<tr><td colspan=2><b>" + QtVersionManager::tr("Invalid Qt version") +"</b></td></tr>";
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>");
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";
726 str << "<tr><td><b>" << QtVersionManager::tr("Version:")
727 << "</b></td><td>" << qtVersionString() << "</td></tr>";
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>";
737 str << "</table></body></html>";
741 bool QtVersion::supportsShadowBuilds() const
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
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))
759 ProjectExplorer::IOutputParser *QtVersion::createOutputParser() const
761 if (supportsTargetId(Qt4ProjectManager::Constants::S60_DEVICE_TARGET_ID) ||
762 supportsTargetId(Qt4ProjectManager::Constants::S60_EMULATOR_TARGET_ID)) {
763 if (isBuildWithSymbianSbsV2()) {
764 return new SbsV2Parser;
766 ProjectExplorer::IOutputParser *parser = new AbldParser;
767 parser->appendOutputParser(new ProjectExplorer::GnuMakeParser);
771 return new ProjectExplorer::GnuMakeParser;
774 QList<ProjectExplorer::Task>
775 QtVersion::reportIssues(const QString &proFile, const QString &buildDir, bool includeTargetSpecificErrors)
777 QList<ProjectExplorer::Task> results;
779 QString tmpBuildDir = QDir(buildDir).absolutePath();
780 if (!tmpBuildDir.endsWith(QLatin1Char('/')))
781 tmpBuildDir.append(QLatin1Char('/'));
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)));
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)));
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.");
812 results.append(ProjectExplorer::Task(ProjectExplorer::Task::Warning, msg, QString(), -1,
813 QLatin1String(ProjectExplorer::Constants::TASK_CATEGORY_BUILDSYSTEM)));
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));
831 if (includeTargetSpecificErrors) {
832 QList<Qt4BaseTargetFactory *> factories;
833 foreach (const QString &id, supportedTargetIds())
834 if (Qt4BaseTargetFactory *factory = Qt4BaseTargetFactory::qt4BaseTargetFactoryForId(id))
835 factories << factory;
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));
846 QString QtVersion::displayName() const
848 return m_displayName;
851 QString QtVersion::qmakeCommand() const
853 return m_qmakeCommand;
856 QString QtVersion::sourcePath() const
861 QString QtVersion::mkspec() const
863 updateAbiAndMkspec();
867 QString QtVersion::mkspecPath() const
869 updateAbiAndMkspec();
870 return m_mkspecFullPath;
873 bool QtVersion::isBuildWithSymbianSbsV2() const
875 updateAbiAndMkspec();
876 return m_isBuildUsingSbsV2;
879 QString QtVersion::qtVersionString() const
881 if (m_qtVersionString.isNull()) {
882 QFileInfo qmake(m_qmakeCommand);
883 if (qmake.exists() && qmake.isExecutable()) {
884 m_qtVersionString = DebuggingHelperLibrary::qtVersionForQMake(qmake.absoluteFilePath());
886 m_qtVersionString = QLatin1String("");
889 return m_qtVersionString;
892 QtVersionNumber QtVersion::qtVersion() const
895 return QtVersionNumber(qtVersionString());
898 QHash<QString,QString> QtVersion::versionInfo() const
901 return m_versionInfo;
904 void QtVersion::setDisplayName(const QString &name)
906 m_displayName = name;
909 void QtVersion::setQMakeCommand(const QString& qmakeCommand)
911 m_qmakeCommand = QDir::fromNativeSeparators(qmakeCommand);
913 m_qmakeCommand = m_qmakeCommand.toLower();
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();
926 void QtVersion::updateSourcePath()
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);
947 m_sourcePath = QDir::cleanPath(m_sourcePath);
949 m_sourcePath = m_sourcePath.toLower();
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)
959 bool debugAdding = false;
961 if (fi.exists() && fi.open(QFile::ReadOnly)) {
963 QRegExp r1("QMAKE\\s*=(.*)");
964 while (!ts.atEnd()) {
965 QString line = ts.readLine();
966 if (r1.exactMatch(line)) {
968 qDebug()<<"#~~ QMAKE is:"<<r1.cap(1).trimmed();
969 QFileInfo qmake(r1.cap(1).trimmed());
970 QString qmakePath = qmake.filePath();
972 if (!qmakePath.endsWith(QLatin1String(".exe")))
973 qmakePath.append(QLatin1String(".exe"));
975 // Is qmake still installed?
976 QFileInfo fi(qmakePath);
978 qmakePath = fi.absoluteFilePath();
980 qmakePath = qmakePath.toLower();
990 QtVersion *QtVersionManager::qtVersionForQMakeBinary(const QString &qmakePath)
992 foreach(QtVersion *v, versions()) {
993 if (v->qmakeCommand() == qmakePath) {
1001 void dumpQMakeAssignments(const QList<QMakeAssignment> &list)
1003 foreach(const QMakeAssignment &qa, list) {
1004 qDebug()<<qa.variable<<qa.op<<qa.value;
1008 bool QtVersionManager::makefileIsFor(const QString &makefile, const QString &proFile)
1010 if (proFile.isEmpty())
1013 QString line = findQMakeLine(makefile, QLatin1String("# Project:")).trimmed();
1017 line = line.mid(line.indexOf(QChar(':')) + 1);
1018 line = line.trimmed();
1020 QFileInfo srcFileInfo(QFileInfo(makefile).absoluteDir(), line);
1021 QFileInfo proFileInfo(proFile);
1022 return srcFileInfo == proFileInfo;
1025 QPair<QtVersion::QmakeBuildConfigs, QString> QtVersionManager::scanMakeFile(const QString &makefile, QtVersion::QmakeBuildConfigs defaultBuildConfig)
1028 qDebug()<<"ScanMakeFile, the gory details:";
1029 QtVersion::QmakeBuildConfigs result = defaultBuildConfig;
1032 QString line = findQMakeLine(makefile, QLatin1String("# Command:"));
1033 if (!line.isEmpty()) {
1035 qDebug()<<"Found line"<<line;
1036 line = trimLine(line);
1037 QList<QMakeAssignment> assignments;
1038 QList<QMakeAssignment> afterAssignments;
1039 parseArgs(line, &assignments, &afterAssignments, &result2);
1042 dumpQMakeAssignments(assignments);
1043 if (!afterAssignments.isEmpty())
1045 dumpQMakeAssignments(afterAssignments);
1048 // Search in assignments for CONFIG(+=,-=,=)(debug,release,debug_and_release)
1049 // Also remove them from the list
1050 result = qmakeBuildConfigFromCmdArgs(&assignments, defaultBuildConfig);
1053 dumpQMakeAssignments(assignments);
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);
1064 // Dump the gathered information:
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";
1075 return qMakePair(result, result2);
1078 QString QtVersionManager::findQMakeLine(const QString &makefile, const QString &key)
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))
1092 /// This function trims the "#Command /path/to/qmake" from the the line
1093 QString QtVersionManager::trimLine(const QString line)
1096 // Actually the first space after #Command: /path/to/qmake
1097 const int firstSpace = line.indexOf(QLatin1Char(' '), 11);
1098 return line.mid(firstSpace).trimmed();
1101 void QtVersionManager::parseArgs(const QString &args, QList<QMakeAssignment> *assignments, QList<QMakeAssignment> *afterAssignments, QString *additionalArguments)
1103 QRegExp regExp("([^\\s\\+-]*)\\s*(\\+=|=|-=|~=)(.*)");
1105 bool ignoreNext = false;
1106 *additionalArguments = args;
1107 Utils::QtcProcess::ArgIterator ait(additionalArguments);
1108 while (ait.next()) {
1113 } else if (ait.value() == QLatin1String("-after")) {
1116 } else if (ait.value().contains(QLatin1Char('='))) {
1117 if (regExp.exactMatch(ait.value())) {
1119 qa.variable = regExp.cap(1);
1120 qa.op = regExp.cap(2);
1121 qa.value = regExp.cap(3).trimmed();
1123 afterAssignments->append(qa);
1125 assignments->append(qa);
1127 qDebug()<<"regexp did not match";
1130 } else if (ait.value() == QLatin1String("-o")) {
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")) {
1140 } else if (ait.value() == QLatin1String("-unix")) {
1145 ait.deleteArg(); // The .pro file is always the last arg
1148 /// This function extracts all the CONFIG+=debug, CONFIG+=release
1149 QtVersion::QmakeBuildConfigs QtVersionManager::qmakeBuildConfigFromCmdArgs(QList<QMakeAssignment> *assignments, QtVersion::QmakeBuildConfigs defaultBuildConfig)
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") {
1161 result = result | QtVersion::DebugBuild;
1163 result = result & ~QtVersion::DebugBuild;
1164 } else if (value == "release") {
1166 result = result & ~QtVersion::DebugBuild;
1168 result = result | QtVersion::DebugBuild;
1169 } else if (value == "debug_and_release") {
1171 result = result | QtVersion::BuildAll;
1173 result = result & ~QtVersion::BuildAll;
1175 newValues.append(value);
1177 QMakeAssignment newQA = qa;
1178 newQA.value = newValues.join(" ");
1179 if (!newValues.isEmpty())
1180 assignments->append(newQA);
1183 assignments->append(qa);
1189 static bool queryQMakeVariables(const QString &binary, QHash<QString, QString> *versionInfo)
1191 const int timeOutMS = 30000; // Might be slow on some machines.
1192 QFileInfo qmake(binary);
1193 static const char * const variables[] = {
1197 "QT_INSTALL_HEADERS",
1199 "QT_INSTALL_EXAMPLES",
1200 "QT_INSTALL_CONFIGURATION",
1201 "QT_INSTALL_TRANSLATIONS",
1202 "QT_INSTALL_PLUGINS",
1205 "QT_INSTALL_PREFIX",
1206 "QT_INSTALL_IMPORTS",
1210 for (uint i = 0; i < sizeof variables / sizeof variables[0]; ++i)
1211 args << "-query" << variables[i];
1213 process.start(qmake.absoluteFilePath(), args, QIODevice::ReadOnly);
1214 if (!process.waitForStarted()) {
1215 qWarning("Cannot start '%s': %s", qPrintable(binary), qPrintable(process.errorString()));
1218 if (!process.waitForFinished(timeOutMS)) {
1219 Utils::SynchronousProcess::stopProcess(process);
1220 qWarning("Timeout running '%s' (%dms).", qPrintable(binary), timeOutMS);
1223 if (process.exitStatus() != QProcess::NormalExit) {
1224 qWarning("'%s' crashed.", qPrintable(binary));
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(':'));
1233 const QString value = QDir::fromNativeSeparators(line.mid(index+1));
1234 if (value != "**Unknown**")
1235 versionInfo->insert(line.left(index), value);
1241 void QtVersion::updateVersionInfo() const
1243 if (m_versionInfoUpToDate)
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;
1257 QFileInfo fi(qmakeCommand());
1258 if (!fi.exists() || !fi.isExecutable()) {
1259 m_qmakeIsExecutable = false;
1263 if (!queryQMakeVariables(qmakeCommand(), &m_versionInfo))
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"));
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();
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"));
1284 m_notInstalled = true;
1286 if (m_versionInfo.contains("QT_INSTALL_HEADERS")){
1287 QFileInfo fi(m_versionInfo.value("QT_INSTALL_HEADERS"));
1289 m_notInstalled = true;
1291 if (m_versionInfo.contains("QT_INSTALL_DOCS")){
1292 QFileInfo fi(m_versionInfo.value("QT_INSTALL_DOCS"));
1294 m_hasDocumentation = true;
1296 if (m_versionInfo.contains("QT_INSTALL_EXAMPLES")){
1297 QFileInfo fi(m_versionInfo.value("QT_INSTALL_EXAMPLES"));
1299 m_hasExamples = true;
1301 if (m_versionInfo.contains("QT_INSTALL_DEMOS")){
1302 QFileInfo fi(m_versionInfo.value("QT_INSTALL_DEMOS"));
1307 m_versionInfoUpToDate = true;
1310 QString QtVersion::findQtBinary(const QStringList &possibleCommands) const
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);
1321 QString QtVersion::uicCommand() const
1325 if (!m_uicCommand.isNull())
1326 return m_uicCommand;
1328 const QStringList possibleCommands(QLatin1String("uic.exe"));
1330 QStringList possibleCommands;
1331 possibleCommands << QLatin1String("uic-qt4") << QLatin1String("uic4") << QLatin1String("uic");
1333 m_uicCommand = findQtBinary(possibleCommands);
1334 return m_uicCommand;
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)
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);
1351 return QStringList(name);
1355 QString QtVersion::designerCommand() const
1359 if (m_designerCommand.isNull())
1360 m_designerCommand = findQtBinary(possibleGuiBinaries(QLatin1String("designer")));
1361 return m_designerCommand;
1364 QString QtVersion::linguistCommand() const
1368 if (m_linguistCommand.isNull())
1369 m_linguistCommand = findQtBinary(possibleGuiBinaries(QLatin1String("linguist")));
1370 return m_linguistCommand;
1373 QString QtVersion::qmlviewerCommand() const
1378 if (m_qmlviewerCommand.isNull()) {
1380 const QString qmlViewerName = QLatin1String("QMLViewer");
1382 const QString qmlViewerName = QLatin1String("qmlviewer");
1385 m_qmlviewerCommand = findQtBinary(possibleGuiBinaries(qmlViewerName));
1387 return m_qmlviewerCommand;
1390 void QtVersion::setSystemRoot(const QString &root)
1392 if (root == m_systemRoot)
1394 m_systemRoot = root;
1395 m_abiUpToDate = false;
1398 QString QtVersion::systemRoot() const
1400 updateAbiAndMkspec();
1401 return m_systemRoot;
1404 bool QtVersion::supportsTargetId(const QString &id) const
1406 updateAbiAndMkspec();
1407 return m_targetIds.contains(id);
1410 QSet<QString> QtVersion::supportedTargetIds() const
1412 updateAbiAndMkspec();
1416 QList<ProjectExplorer::Abi> QtVersion::qtAbis() const
1418 updateAbiAndMkspec();
1422 // if none, then it's INVALID everywhere this function is called
1423 void QtVersion::updateAbiAndMkspec() const
1425 if (m_id == -1 || m_abiUpToDate)
1428 m_abiUpToDate = true;
1430 m_targetIds.clear();
1432 m_validSystemRoot = true;
1434 // qDebug()<<"Finding mkspec for"<<qmakeCommand();
1436 // no .qmake.cache so look at the default mkspec
1438 QString baseMkspecDir = versionInfo().value("QMAKE_MKSPECS");
1439 if (baseMkspecDir.isEmpty())
1440 baseMkspecDir = versionInfo().value("QT_INSTALL_DATA") + "/mkspecs";
1443 baseMkspecDir = baseMkspecDir.toLower();
1446 QString mkspecFullPath = baseMkspecDir + "/default";
1448 // qDebug() << "default mkspec is located at" << mkspecFullPath;
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;
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++";
1483 //resolve mkspec link
1484 mkspecFullPath = resolveLink(mkspecFullPath);
1492 mkspecFullPath =resolveLink(mkspecFullPath);
1496 mkspecFullPath = mkspecFullPath.toLower();
1499 m_mkspecFullPath = mkspecFullPath;
1500 QString mkspec = m_mkspecFullPath;
1502 if (mkspec.startsWith(baseMkspecDir)) {
1503 mkspec = mkspec.mid(baseMkspecDir.length() + 1);
1504 // qDebug() << "Setting mkspec to"<<mkspec;
1506 QString sourceMkSpecPath = sourcePath() + "/mkspecs";
1507 if (mkspec.startsWith(sourceMkSpecPath)) {
1508 mkspec = mkspec.mid(sourceMkSpecPath.length() + 1);
1515 m_isBuildUsingSbsV2 = false;
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);
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");
1534 const QString coreLibrary = qtCorePath();
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,
1551 m_targetIds.insert(QLatin1String(Constants::S60_DEVICE_TARGET_ID));
1552 m_targetIds.insert(QLatin1String(Constants::S60_EMULATOR_TARGET_ID));
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,
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,
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;
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()));
1583 m_targetIds.insert(QLatin1String(Constants::DESKTOP_TARGET_ID));
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));
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;
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));
1609 ProFileCacheManager::instance()->decRefCount();
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)
1624 if (list.at(0) == QLatin1String("sysroot")) {
1625 m_systemRoot = MaemoGlobal::maddeRoot(this)
1626 + QLatin1String("/sysroots/") + list.at(1);
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;
1636 if (!m_systemRoot.endsWith(QLatin1Char('/')))
1637 m_systemRoot.append(QLatin1Char('/'));
1639 QFileInfo cppheader(m_systemRoot + QLatin1String("epoc32/include/stdapis/string.h"));
1640 if (!cppheader.exists())
1641 m_validSystemRoot = false;
1643 m_systemRoot = QLatin1String("");
1647 QString QtVersion::resolveLink(const QString &path) const
1651 while (links-- && f.isSymLink())
1652 f.setFile(f.symLinkTarget());
1655 return f.filePath();
1658 QString QtVersion::qtCorePath() const
1661 dirs << QDir(libraryInstallPath()) << QDir(versionInfo().value(QLatin1String("QT_INSTALL_BINS")));
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();
1669 && file.startsWith(QLatin1String("QtCore"))
1670 && file.endsWith(QLatin1String(".framework"))) {
1672 const QString libName = file.left(file.lastIndexOf('.'));
1673 return info.absoluteFilePath() + '/' + libName;
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();
1689 // Return path to first static library found:
1690 if (!staticLibs.isEmpty())
1691 return staticLibs.at(0).absoluteFilePath();
1695 QString QtVersion::sbsV2Directory() const
1697 return m_sbsV2Directory;
1700 void QtVersion::setSbsV2Directory(const QString &directory)
1702 m_sbsV2Directory = directory;
1705 void QtVersion::addToEnvironment(Utils::Environment &env) const
1708 env.set("QTDIR", QDir::toNativeSeparators(versionInfo().value("QT_INSTALL_DATA")));
1709 env.prependOrSetPath(versionInfo().value("QT_INSTALL_BINS"));
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);
1718 // Clean up epoc root path for the environment:
1719 if (!epocRootPath.endsWith(QLatin1Char('/')))
1720 epocRootPath.append(QLatin1Char('/'));
1721 if (!isBuildWithSymbianSbsV2()) {
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);
1731 env.set(QLatin1String("EPOCROOT"), QDir::toNativeSeparators(epocRootPath));
1733 env.prependOrSetPath(epocDir.filePath(QLatin1String("epoc32/tools"))); // e.g. make.exe
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")));
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());
1747 env.prependOrSetPath(epocDir.filePath(QLatin1String("perl/bin")));
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"));
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"
1775 QList<ProjectExplorer::HeaderPath> QtVersion::systemHeaderPathes() const
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]));
1785 result.append(ProjectExplorer::HeaderPath(dir.absolutePath(),
1786 ProjectExplorer::HeaderPath::GlobalHeaderPath));
1792 int QtVersion::uniqueId() const
1797 int QtVersion::getUniqueId()
1799 return QtVersionManager::instance()->getUniqueId();
1802 bool QtVersion::isValid() const
1804 updateVersionInfo();
1805 updateAbiAndMkspec();
1808 && !qmakeCommand().isEmpty()
1809 && !displayName().isEmpty()
1811 && m_versionInfo.contains("QT_INSTALL_BINS")
1812 && (!m_mkspecFullPath.isEmpty() || !m_abiUpToDate)
1813 && !m_abis.isEmpty()
1814 && m_qmakeIsExecutable
1815 && m_validSystemRoot;
1818 bool QtVersion::toolChainAvailable() const
1822 foreach (const ProjectExplorer::Abi &abi, qtAbis())
1823 if (!ProjectExplorer::ToolChainManager::instance()->findToolChains(abi).isEmpty())
1828 QString QtVersion::invalidReason() const
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");
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");
1852 QString QtVersion::description() const
1855 return invalidReason();
1856 QSet<QString> targets = supportedTargetIds();
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");
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);
1877 QtVersion::QmakeBuildConfigs QtVersion::defaultBuildConfig() const
1879 updateAbiAndMkspec();
1880 QtVersion::QmakeBuildConfigs result = QtVersion::QmakeBuildConfig(0);
1882 if (m_defaultConfigIsDebugAndRelease)
1883 result = QtVersion::BuildAll;
1884 if (m_defaultConfigIsDebug)
1885 result = result | QtVersion::DebugBuild;
1889 bool QtVersion::hasGdbDebuggingHelper() const
1891 updateVersionInfo();
1892 return m_hasDebuggingHelper;
1896 bool QtVersion::hasQmlDump() const
1898 updateVersionInfo();
1899 return m_hasQmlDump;
1902 bool QtVersion::hasQmlDebuggingLibrary() const
1904 updateVersionInfo();
1905 return m_hasQmlDebuggingLibrary;
1908 bool QtVersion::hasQmlObserver() const
1910 updateVersionInfo();
1911 return m_hasQmlObserver;
1914 Utils::Environment QtVersion::qmlToolsEnvironment() const
1916 // FIXME: This seems broken!
1917 Utils::Environment environment = Utils::Environment::systemEnvironment();
1918 addToEnvironment(environment);
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);
1929 QString QtVersion::gdbDebuggingHelperLibrary() const
1931 QString qtInstallData = versionInfo().value("QT_INSTALL_DATA");
1932 if (qtInstallData.isEmpty())
1934 return DebuggingHelperLibrary::debuggingHelperLibraryByInstallData(qtInstallData);
1937 QString QtVersion::qmlDumpTool(bool debugVersion) const
1939 QString qtInstallData = versionInfo().value("QT_INSTALL_DATA");
1940 if (qtInstallData.isEmpty())
1942 return QmlDumpTool::toolByInstallData(qtInstallData, debugVersion);
1945 QString QtVersion::qmlDebuggingHelperLibrary(bool debugVersion) const
1947 QString qtInstallData = versionInfo().value("QT_INSTALL_DATA");
1948 if (qtInstallData.isEmpty())
1950 return QmlDebuggingLibrary::libraryByInstallData(qtInstallData, debugVersion);
1953 QString QtVersion::qmlObserverTool() const
1955 QString qtInstallData = versionInfo().value("QT_INSTALL_DATA");
1956 if (qtInstallData.isEmpty())
1958 return QmlObserverTool::toolByInstallData(qtInstallData);
1961 QStringList QtVersion::debuggingHelperLibraryLocations() const
1963 QString qtInstallData = versionInfo().value("QT_INSTALL_DATA");
1964 if (qtInstallData.isEmpty())
1965 return QStringList();
1966 return DebuggingHelperLibrary::locationsByInstallData(qtInstallData);
1969 bool QtVersion::supportsBinaryDebuggingHelper() const
1973 return qtAbis().at(0).os() != ProjectExplorer::Abi::SymbianOS;
1976 bool QtVersion::hasDocumentation() const
1978 updateVersionInfo();
1979 return m_hasDocumentation;
1982 QString QtVersion::documentationPath() const
1984 updateVersionInfo();
1985 return m_versionInfo["QT_INSTALL_DOCS"];
1988 bool QtVersion::hasDemos() const
1990 updateVersionInfo();
1994 QString QtVersion::demosPath() const
1996 updateVersionInfo();
1997 return m_versionInfo["QT_INSTALL_DEMOS"];
2000 QString QtVersion::headerInstallPath() const
2002 updateVersionInfo();
2003 return m_versionInfo["QT_INSTALL_HEADERS"];
2006 QString QtVersion::frameworkInstallPath() const
2009 updateVersionInfo();
2010 return m_versionInfo["QT_INSTALL_LIBS"];
2016 QString QtVersion::libraryInstallPath() const
2018 updateVersionInfo();
2019 return m_versionInfo["QT_INSTALL_LIBS"];
2022 bool QtVersion::hasExamples() const
2024 updateVersionInfo();
2025 return m_hasExamples;
2028 QString QtVersion::examplesPath() const
2030 updateVersionInfo();
2031 return m_versionInfo["QT_INSTALL_EXAMPLES"];
2034 void QtVersion::invalidateCache()
2036 m_versionInfoUpToDate = false;
2044 QtVersionNumber::QtVersionNumber(int ma, int mi, int p)
2045 : majorVersion(ma), minorVersion(mi), patchVersion(p)
2049 QtVersionNumber::QtVersionNumber(const QString &versionString)
2051 if (!checkVersionString(versionString)) {
2052 majorVersion = minorVersion = patchVersion = -1;
2056 QStringList parts = versionString.split(QLatin1Char('.'));
2057 majorVersion = parts.at(0).toInt();
2058 minorVersion = parts.at(1).toInt();
2059 patchVersion = parts.at(2).toInt();
2062 QtVersionNumber::QtVersionNumber()
2064 majorVersion = minorVersion = patchVersion = -1;
2068 bool QtVersionNumber::checkVersionString(const QString &version) const
2071 QString validChars = "0123456789.";
2072 foreach (const QChar &c, version) {
2073 if (!validChars.contains(c))
2083 bool QtVersionNumber::operator <(const QtVersionNumber &b) const
2085 if (majorVersion < b.majorVersion)
2087 if (majorVersion > b.majorVersion)
2089 if (minorVersion < b.minorVersion)
2091 if (minorVersion > b.minorVersion)
2093 if (patchVersion < b.patchVersion)
2098 bool QtVersionNumber::operator >(const QtVersionNumber &b) const
2103 bool QtVersionNumber::operator ==(const QtVersionNumber &b) const
2105 return majorVersion == b.majorVersion
2106 && minorVersion == b.minorVersion
2107 && patchVersion == b.patchVersion;
2110 bool QtVersionNumber::operator !=(const QtVersionNumber &b) const
2112 return !(*this == b);
2115 bool QtVersionNumber::operator <=(const QtVersionNumber &b) const
2117 return !(*this > b);
2120 bool QtVersionNumber::operator >=(const QtVersionNumber &b) const