baseqtversion.cpp 52.6 KB
Newer Older
hjk's avatar
hjk committed
1
/****************************************************************************
dt's avatar
dt committed
2
**
3
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
hjk's avatar
hjk committed
4
** Contact: http://www.qt-project.org/legal
dt's avatar
dt committed
5
**
hjk's avatar
hjk committed
6
** This file is part of Qt Creator.
dt's avatar
dt committed
7
**
hjk's avatar
hjk committed
8
9
10
11
12
13
14
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia.  For licensing terms and
** conditions see http://qt.digia.com/licensing.  For further information
** use the contact form at http://qt.digia.com/contact-us.
dt's avatar
dt committed
15
16
**
** GNU Lesser General Public License Usage
hjk's avatar
hjk committed
17
18
19
20
21
22
23
24
25
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights.  These rights are described in the Digia Qt LGPL Exception
dt's avatar
dt committed
26
27
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
hjk's avatar
hjk committed
28
****************************************************************************/
dt's avatar
dt committed
29
30

#include "baseqtversion.h"
31
#include "qtconfigwidget.h"
dt's avatar
dt committed
32
#include "qmldumptool.h"
Tobias Hunger's avatar
Tobias Hunger committed
33
#include "qtkitinformation.h"
dt's avatar
dt committed
34
35

#include "qtversionmanager.h"
36
#include "profilereader.h"
37
38
#include <coreplugin/icore.h>
#include <coreplugin/progressmanager/progressmanager.h>
39
#include <proparser/qmakevfs.h>
dt's avatar
dt committed
40
#include <projectexplorer/toolchainmanager.h>
41
#include <projectexplorer/toolchain.h>
dt's avatar
dt committed
42
#include <projectexplorer/projectexplorer.h>
43
#include <projectexplorer/headerpath.h>
44
#include <qtsupport/debugginghelperbuildtask.h>
45
#include <qtsupport/qtkitinformation.h>
46
#include <qtsupport/qtsupportconstants.h>
dt's avatar
dt committed
47

48
#include <utils/algorithm.h>
49
#include <utils/hostosinfo.h>
50
#include <utils/macroexpander.h>
Tobias Hunger's avatar
Tobias Hunger committed
51
#include <utils/qtcassert.h>
52
#include <utils/runextensions.h>
dt's avatar
dt committed
53
#include <utils/synchronousprocess.h>
54
#include <utils/winutils.h>
55
#include <utils/algorithm.h>
dt's avatar
dt committed
56

57
#include <QDir>
58
#include <QUrl>
59
#include <QFileInfo>
60
#include <QFuture>
61
62
#include <QCoreApplication>
#include <QProcess>
dt's avatar
dt committed
63

64
using namespace Core;
65
66
using namespace QtSupport;
using namespace QtSupport::Internal;
hjk's avatar
hjk committed
67
using namespace ProjectExplorer;
68
using namespace Utils;
dt's avatar
dt committed
69
70
71
72
73

static const char QTVERSIONAUTODETECTED[] = "isAutodetected";
static const char QTVERSIONAUTODETECTIONSOURCE []= "autodetectionSource";
static const char QTVERSIONQMAKEPATH[] = "QMakePath";

74
75
76
static const char MKSPEC_VALUE_LIBINFIX[] = "QT_LIBINFIX";
static const char MKSPEC_VALUE_NAMESPACE[] = "QT_NAMESPACE";

dt's avatar
dt committed
77
78
79
80
81
82
83
84
85
86
///////////////
// QtVersionNumber
///////////////
QtVersionNumber::QtVersionNumber(int ma, int mi, int p)
    : majorVersion(ma), minorVersion(mi), patchVersion(p)
{
}

QtVersionNumber::QtVersionNumber(const QString &versionString)
{
87
88
    if (::sscanf(versionString.toLatin1().constData(), "%d.%d.%d",
           &majorVersion, &minorVersion, &patchVersion) != 3)
dt's avatar
dt committed
89
90
91
92
93
94
95
96
97
98
        majorVersion = minorVersion = patchVersion = -1;
}

QtVersionNumber::QtVersionNumber()
{
    majorVersion = minorVersion = patchVersion = -1;
}

bool QtVersionNumber::operator <(const QtVersionNumber &b) const
{
99
100
101
102
103
    if (majorVersion != b.majorVersion)
        return majorVersion < b.majorVersion;
    if (minorVersion != b.minorVersion)
        return minorVersion < b.minorVersion;
    return patchVersion < b.patchVersion;
dt's avatar
dt committed
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
}

bool QtVersionNumber::operator >(const QtVersionNumber &b) const
{
    return b < *this;
}

bool QtVersionNumber::operator ==(const QtVersionNumber &b) const
{
    return majorVersion == b.majorVersion
            && minorVersion == b.minorVersion
            && patchVersion == b.patchVersion;
}

bool QtVersionNumber::operator !=(const QtVersionNumber &b) const
{
    return !(*this == b);
}

bool QtVersionNumber::operator <=(const QtVersionNumber &b) const
{
    return !(*this > b);
}

bool QtVersionNumber::operator >=(const QtVersionNumber &b) const
{
    return b <= *this;
}

///////////////
// BaseQtVersion
///////////////
int BaseQtVersion::getUniqueId()
{
hjk's avatar
hjk committed
138
    return QtVersionManager::getUniqueId();
dt's avatar
dt committed
139
140
}

141
BaseQtVersion::BaseQtVersion(const FileName &qmakeCommand, bool isAutodetected, const QString &autodetectionSource)
dt's avatar
dt committed
142
143
144
145
146
147
148
    : m_id(getUniqueId()),
      m_isAutodetected(isAutodetected),
      m_hasQmlDump(false),
      m_mkspecUpToDate(false),
      m_mkspecReadUpToDate(false),
      m_defaultConfigIsDebug(true),
      m_defaultConfigIsDebugAndRelease(true),
149
      m_frameworkBuild(false),
dt's avatar
dt committed
150
      m_versionInfoUpToDate(false),
hjk's avatar
hjk committed
151
      m_installed(true),
dt's avatar
dt committed
152
153
154
      m_hasExamples(false),
      m_hasDemos(false),
      m_hasDocumentation(false),
155
      m_qmakeIsExecutable(true),
156
      m_hasQtAbis(false),
157
      m_autodetectionSource(autodetectionSource)
dt's avatar
dt committed
158
159
160
161
{
    ctor(qmakeCommand);
}

162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
BaseQtVersion::BaseQtVersion(const BaseQtVersion &other) :
    m_id(other.m_id),
    m_isAutodetected(other.m_isAutodetected),
    m_hasQmlDump(other.m_hasQmlDump),
    m_mkspecUpToDate(other.m_mkspecUpToDate),
    m_mkspecReadUpToDate(other.m_mkspecReadUpToDate),
    m_defaultConfigIsDebug(other.m_defaultConfigIsDebug),
    m_defaultConfigIsDebugAndRelease(other.m_defaultConfigIsDebugAndRelease),
    m_frameworkBuild(other.m_frameworkBuild),
    m_versionInfoUpToDate(other.m_versionInfoUpToDate),
    m_installed(other.m_installed),
    m_hasExamples(other.m_hasExamples),
    m_hasDemos(other.m_hasDemos),
    m_hasDocumentation(other.m_hasDocumentation),
    m_qmakeIsExecutable(other.m_qmakeIsExecutable),
    m_hasQtAbis(other.m_hasQtAbis),
    m_configValues(other.m_configValues),
    m_qtConfigValues(other.m_qtConfigValues),
    m_unexpandedDisplayName(other.m_unexpandedDisplayName),
    m_autodetectionSource(other.m_autodetectionSource),
    m_sourcePath(other.m_sourcePath),
    m_mkspec(other.m_mkspec),
    m_mkspecFullPath(other.m_mkspecFullPath),
    m_mkspecValues(other.m_mkspecValues),
    m_versionInfo(other.m_versionInfo),
    m_qmakeCommand(other.m_qmakeCommand),
    m_qtVersionString(other.m_qtVersionString),
    m_uicCommand(other.m_uicCommand),
    m_designerCommand(other.m_designerCommand),
    m_linguistCommand(other.m_linguistCommand),
    m_qmlsceneCommand(other.m_qmlsceneCommand),
    m_qmlviewerCommand(other.m_qmlviewerCommand),
    m_qtAbis(other.m_qtAbis),
    m_expander(0)
{ }

dt's avatar
dt committed
198
199
200
201
202
203
204
BaseQtVersion::BaseQtVersion()
    :  m_id(-1), m_isAutodetected(false),
    m_hasQmlDump(false),
    m_mkspecUpToDate(false),
    m_mkspecReadUpToDate(false),
    m_defaultConfigIsDebug(true),
    m_defaultConfigIsDebugAndRelease(true),
205
    m_frameworkBuild(false),
dt's avatar
dt committed
206
    m_versionInfoUpToDate(false),
hjk's avatar
hjk committed
207
    m_installed(true),
dt's avatar
dt committed
208
209
    m_hasExamples(false),
    m_hasDemos(false),
210
    m_hasDocumentation(false),
211
212
    m_qmakeIsExecutable(true),
    m_hasQtAbis(false)
dt's avatar
dt committed
213
{
214
    ctor(FileName());
dt's avatar
dt committed
215
216
}

217
void BaseQtVersion::ctor(const FileName &qmakePath)
dt's avatar
dt committed
218
{
219
    m_qmakeCommand = qmakePath;
dt's avatar
dt committed
220
221
222
223
224
225
226
    m_designerCommand.clear();
    m_linguistCommand.clear();
    m_qmlviewerCommand.clear();
    m_uicCommand.clear();
    m_mkspecUpToDate = false;
    m_mkspecReadUpToDate = false;
    m_versionInfoUpToDate = false;
227
    m_hasQtAbis = false;
dt's avatar
dt committed
228
229
    m_qtVersionString.clear();
    m_sourcePath.clear();
230
    m_expander = 0;
dt's avatar
dt committed
231
232
233
234
}

BaseQtVersion::~BaseQtVersion()
{
235
    delete m_expander;
236
237
}

238
QString BaseQtVersion::defaultUnexpandedDisplayName(const FileName &qmakePath, bool fromPath)
239
240
241
242
243
244
245
{
    QString location;
    if (qmakePath.isEmpty()) {
        location = QCoreApplication::translate("QtVersion", "<unknown>");
    } else {
        // Deduce a description from '/foo/qt-folder/[qtbase]/bin/qmake' -> '/foo/qt-folder'.
        // '/usr' indicates System Qt 4.X on Linux.
246
        QDir dir = qmakePath.toFileInfo().absoluteDir();
247
248
249
250
251
252
        do {
            const QString dirName = dir.dirName();
            if (dirName == QLatin1String("usr")) { // System-installed Qt.
                location = QCoreApplication::translate("QtVersion", "System");
                break;
            }
253
254
            location = dirName;
            // Also skip default checkouts named 'qt'. Parent dir might have descriptive name.
255
            if (dirName.compare(QLatin1String("bin"), Qt::CaseInsensitive)
256
257
                && dirName.compare(QLatin1String("qtbase"), Qt::CaseInsensitive)
                && dirName.compare(QLatin1String("qt"), Qt::CaseInsensitive)) {
258
259
                break;
            }
Orgad Shaneh's avatar
Orgad Shaneh committed
260
        } while (!dir.isRoot() && dir.cdUp());
261
    }
dt's avatar
dt committed
262

263
    return fromPath ?
264
265
        QCoreApplication::translate("QtVersion", "Qt %{Qt:version} in PATH (%2)").arg(location) :
        QCoreApplication::translate("QtVersion", "Qt %{Qt:version} (%2)").arg(location);
dt's avatar
dt committed
266
267
}

268
FeatureSet BaseQtVersion::availableFeatures() const
269
{
270
271
272
273
    FeatureSet features = FeatureSet(Constants::FEATURE_QWIDGETS)
            | FeatureSet(Constants::FEATURE_QT)
            | FeatureSet(Constants::FEATURE_QT_WEBKIT)
            | FeatureSet(Constants::FEATURE_QT_CONSOLE);
274

275
276
277
278
    if (qtVersion() < QtVersionNumber(5, 0, 0))
        features |= FeatureSet(Constants::FEATURE_QT4);
    else
        features |= FeatureSet(Constants::FEATURE_QT5);
jkobus's avatar
jkobus committed
279

280
281
    if (qtVersion() < QtVersionNumber(4, 7, 0))
        return features;
jkobus's avatar
jkobus committed
282

283
284
    features |= FeatureSet(Constants::FEATURE_QT_QUICK);
    features |= FeatureSet(Constants::FEATURE_QT_QUICK_1);
jkobus's avatar
jkobus committed
285

286
287
    if (qtVersion() < QtVersionNumber(4, 7, 1))
        return features;
jkobus's avatar
jkobus committed
288

289
    features |= FeatureSet(Constants::FEATURE_QT_QUICK_1_1);
jkobus's avatar
jkobus committed
290

291
292
    if (qtVersion() < QtVersionNumber(5, 0, 0))
        return features;
jkobus's avatar
jkobus committed
293

294
295
    features |= FeatureSet(Constants::FEATURE_QT_QUICK_2);
    features |= FeatureSet(Constants::FEATURE_QT_QUICK_2_0);
jkobus's avatar
jkobus committed
296

297
298
    if (qtVersion() < QtVersionNumber(5, 1, 0))
        return features;
jkobus's avatar
jkobus committed
299

300
301
302
303
    features |= FeatureSet(Constants::FEATURE_QT_QUICK_2_1);
    features |= FeatureSet(Constants::FEATURE_QT_QUICK_CONTROLS);
    features |= FeatureSet(Constants::FEATURE_QT_QUICK_CONTROLS_1);
    features |= FeatureSet(Constants::FEATURE_QT_QUICK_CONTROLS_1_0);
jkobus's avatar
jkobus committed
304

305
306
    if (qtVersion() < QtVersionNumber(5, 2, 0))
        return features;
307

308
309
    features |= FeatureSet(Constants::FEATURE_QT_QUICK_2_2);
    features |= FeatureSet(Constants::FEATURE_QT_QUICK_CONTROLS_1_1);
310

311
312
    if (qtVersion() < QtVersionNumber(5, 3, 0))
        return features;
313

314
315
316
    features |= FeatureSet(Constants::FEATURE_QT_QUICK_2_3);
    features |= FeatureSet(Constants::FEATURE_QT_QUICK_CONTROLS_1_2);

317
318
319
320
321
322
323
    if (qtVersion() < QtVersionNumber(5, 4, 0))
        return features;

    features |= FeatureSet(Constants::FEATURE_QT_QUICK_2_4);
    features |= FeatureSet(Constants::FEATURE_QT_QUICK_CONTROLS_1_3);
    features |= FeatureSet(Constants::FEATURE_QT_QUICK_UI_FILES);

324
    return features;
325
326
}

327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
QString BaseQtVersion::platformName() const
{
    return QString();
}

QString BaseQtVersion::platformDisplayName() const
{
    return platformName();
}

bool BaseQtVersion::supportsPlatform(const QString &platform) const
{
    if (platform.isEmpty()) // empty target == target independent
        return true;
    return platform == platformName();
}

hjk's avatar
hjk committed
344
QList<Task> BaseQtVersion::validateKit(const Kit *k)
Tobias Hunger's avatar
Tobias Hunger committed
345
{
hjk's avatar
hjk committed
346
    QList<Task> result;
Tobias Hunger's avatar
Tobias Hunger committed
347

Tobias Hunger's avatar
Tobias Hunger committed
348
    BaseQtVersion *version = QtKitInformation::qtVersion(k);
Tobias Hunger's avatar
Tobias Hunger committed
349
350
    Q_ASSERT(version == this);

hjk's avatar
hjk committed
351
    const QList<Abi> qtAbis = version->qtAbis();
352
    if (qtAbis.isEmpty()) // No need to test if Qt does not know anyway...
353
354
        return result;

hjk's avatar
hjk committed
355
    ToolChain *tc = ToolChainKitInformation::toolChain(k);
356
    if (tc) {
hjk's avatar
hjk committed
357
        Abi targetAbi = tc->targetAbi();
358
359
        bool fuzzyMatch = false;
        bool fullMatch = false;
360
361

        QString qtAbiString;
hjk's avatar
hjk committed
362
        foreach (const Abi &qtAbi, qtAbis) {
363
364
365
            if (!qtAbiString.isEmpty())
                qtAbiString.append(QLatin1Char(' '));
            qtAbiString.append(qtAbi.toString());
366
367
368
369
370
371
372
373
374
375
376

            if (!fullMatch)
                fullMatch = (targetAbi == qtAbi);
            if (!fuzzyMatch)
                fuzzyMatch = targetAbi.isCompatibleWith(qtAbi);
        }

        QString message;
        if (!fullMatch) {
            if (!fuzzyMatch)
                message = QCoreApplication::translate("BaseQtVersion",
377
                                                      "The compiler \"%1\" (%2) cannot produce code for the Qt version \"%3\" (%4).");
378
379
            else
                message = QCoreApplication::translate("BaseQtVersion",
380
                                                      "The compiler \"%1\" (%2) may not produce code compatible with the Qt version \"%3\" (%4).");
381
382
            message = message.arg(tc->displayName(), targetAbi.toString(),
                                  version->displayName(), qtAbiString);
hjk's avatar
hjk committed
383
384
            result << Task(fuzzyMatch ? Task::Warning : Task::Error, message, FileName(), -1,
                           ProjectExplorer::Constants::TASK_CATEGORY_BUILDSYSTEM);
385
        }
386
    }
Tobias Hunger's avatar
Tobias Hunger committed
387
388
389
    return result;
}

390
391
FileName BaseQtVersion::headerPath() const
{
hjk's avatar
hjk committed
392
    return FileName::fromUserInput(qmakeProperty("QT_INSTALL_HEADERS"));
393
394
}

395
396
FileName BaseQtVersion::docsPath() const
{
hjk's avatar
hjk committed
397
    return FileName::fromUserInput(qmakeProperty("QT_INSTALL_DOCS"));
398
399
}

400
401
FileName BaseQtVersion::libraryPath() const
{
hjk's avatar
hjk committed
402
    return FileName::fromUserInput(qmakeProperty("QT_INSTALL_LIBS"));
403
404
}

405
406
FileName BaseQtVersion::pluginPath() const
{
hjk's avatar
hjk committed
407
    return FileName::fromUserInput(qmakeProperty("QT_INSTALL_PLUGINS"));
408
409
}

410
411
FileName BaseQtVersion::binPath() const
{
hjk's avatar
hjk committed
412
    return FileName::fromUserInput(qmakeProperty("QT_HOST_BINS"));
413
414
}

hjk's avatar
hjk committed
415
FileName BaseQtVersion::mkspecsPath() const
416
{
hjk's avatar
hjk committed
417
    FileName result = FileName::fromUserInput(qmakeProperty("QT_HOST_DATA"));
418
    if (result.isEmpty())
hjk's avatar
hjk committed
419
        result = FileName::fromUserInput(qmakeProperty("QMAKE_MKSPECS"));
420
421
422
423
424
    else
        result.appendPath(QLatin1String("mkspecs"));
    return result;
}

425
QString BaseQtVersion::qtNamespace() const
426
{
427
428
    ensureMkSpecParsed();
    return m_mkspecValues.value(QLatin1String(MKSPEC_VALUE_NAMESPACE));
429
430
}

431
QString BaseQtVersion::qtLibInfix() const
432
{
433
434
    ensureMkSpecParsed();
    return m_mkspecValues.value(QLatin1String(MKSPEC_VALUE_LIBINFIX));
435
436
}

437
438
bool BaseQtVersion::isFrameworkBuild() const
{
439
    ensureMkSpecParsed();
440
441
442
    return m_frameworkBuild;
}

443
444
445
446
447
448
449
450
451
452
bool BaseQtVersion::hasDebugBuild() const
{
    return m_defaultConfigIsDebug || m_defaultConfigIsDebugAndRelease;
}

bool BaseQtVersion::hasReleaseBuild() const
{
    return !m_defaultConfigIsDebug || m_defaultConfigIsDebugAndRelease;
}

dt's avatar
dt committed
453
454
455
456
457
458
459
void BaseQtVersion::setId(int id)
{
    m_id = id;
}

void BaseQtVersion::fromMap(const QVariantMap &map)
{
460
    m_id = map.value(QLatin1String(Constants::QTVERSIONID)).toInt();
dt's avatar
dt committed
461
    if (m_id == -1) // this happens on adding from installer, see updateFromInstaller => get a new unique id
hjk's avatar
hjk committed
462
        m_id = QtVersionManager::getUniqueId();
463
    m_unexpandedDisplayName = map.value(QLatin1String(Constants::QTVERSIONNAME)).toString();
dt's avatar
dt committed
464
465
466
    m_isAutodetected = map.value(QLatin1String(QTVERSIONAUTODETECTED)).toBool();
    if (m_isAutodetected)
        m_autodetectionSource = map.value(QLatin1String(QTVERSIONAUTODETECTIONSOURCE)).toString();
467
    QString string = map.value(QLatin1String(QTVERSIONQMAKEPATH)).toString();
468
    if (string.startsWith(QLatin1Char('~')))
469
        string.remove(0, 1).prepend(QDir::homePath());
470
471
472
473
474
475

    QFileInfo fi(string);
    if (BuildableHelperLibrary::isQtChooser(fi)) {
        // we don't want to treat qtchooser as a normal qmake
        // see e.g. QTCREATORBUG-9841, also this lead to users changing what
        // qtchooser forwards too behind our backs, which will inadvertly lead to bugs
476
        string = BuildableHelperLibrary::qtChooserToQmakePath(fi.symLinkTarget());
477
478
    }

Eike Ziller's avatar
Eike Ziller committed
479
    ctor(FileName::fromString(string));
dt's avatar
dt committed
480
481
482
483
484
}

QVariantMap BaseQtVersion::toMap() const
{
    QVariantMap result;
485
    result.insert(QLatin1String(Constants::QTVERSIONID), uniqueId());
486
    result.insert(QLatin1String(Constants::QTVERSIONNAME), unexpandedDisplayName());
dt's avatar
dt committed
487
488
489
    result.insert(QLatin1String(QTVERSIONAUTODETECTED), isAutodetected());
    if (isAutodetected())
        result.insert(QLatin1String(QTVERSIONAUTODETECTIONSOURCE), autodetectionSource());
490
    result.insert(QLatin1String(QTVERSIONQMAKEPATH), qmakeCommand().toString());
dt's avatar
dt committed
491
492
493
494
495
    return result;
}

bool BaseQtVersion::isValid() const
{
dt's avatar
dt committed
496
    if (uniqueId() == -1 || displayName().isEmpty())
dt's avatar
dt committed
497
498
499
500
501
        return false;
    updateVersionInfo();
    updateMkspec();

    return  !qmakeCommand().isEmpty()
hjk's avatar
hjk committed
502
            && m_installed
503
            && !qmakeProperty("QT_HOST_BINS").isNull()
504
            && !m_mkspecFullPath.isEmpty()
dt's avatar
dt committed
505
506
507
508
509
510
511
512
513
514
515
            && m_qmakeIsExecutable;
}

QString BaseQtVersion::invalidReason() const
{
    if (displayName().isEmpty())
        return QCoreApplication::translate("QtVersion", "Qt version has no name");
    if (qmakeCommand().isEmpty())
        return QCoreApplication::translate("QtVersion", "No qmake path set");
    if (!m_qmakeIsExecutable)
        return QCoreApplication::translate("QtVersion", "qmake does not exist or is not executable");
hjk's avatar
hjk committed
516
    if (!m_installed)
dt's avatar
dt committed
517
        return QCoreApplication::translate("QtVersion", "Qt version is not properly installed, please run make install");
518
    if (qmakeProperty("QT_HOST_BINS").isNull())
dt's avatar
dt committed
519
520
521
522
523
524
525
        return QCoreApplication::translate("QtVersion",
                                           "Could not determine the path to the binaries of the Qt installation, maybe the qmake path is wrong?");
    if (m_mkspecUpToDate && m_mkspecFullPath.isEmpty())
        return QCoreApplication::translate("QtVersion", "The default mkspec symlink is broken.");
    return QString();
}

526
QStringList BaseQtVersion::warningReason() const
527
{
528
    QStringList ret;
529
    if (qtAbis().isEmpty())
530
        ret << QCoreApplication::translate("QtVersion", "ABI detection failed: Make sure to use a matching compiler when building.");
531
532
533
534
    if (m_versionInfo.value(QLatin1String("QT_INSTALL_PREFIX/get"))
        != m_versionInfo.value(QLatin1String("QT_INSTALL_PREFIX"))) {
        ret << QCoreApplication::translate("QtVersion", "Non-installed -prefix build - for internal development only.");
    }
535
    return ret;
536
537
}

538
FileName BaseQtVersion::qmakeCommand() const
dt's avatar
dt committed
539
{
Tobias Hunger's avatar
Tobias Hunger committed
540
    return m_qmakeCommand;
dt's avatar
dt committed
541
542
}

hjk's avatar
hjk committed
543
QList<Abi> BaseQtVersion::qtAbis() const
544
{
545
    if (!m_hasQtAbis) {
546
        m_qtAbis = detectQtAbis();
547
548
        m_hasQtAbis = true;
    }
549
550
551
    return m_qtAbis;
}

dt's avatar
dt committed
552
553
554
555
556
557
558
559
bool BaseQtVersion::equals(BaseQtVersion *other)
{
    if (type() != other->type())
        return false;
    if (uniqueId() != other->uniqueId())
        return false;
    if (displayName() != other->displayName())
        return false;
560
561
    if (isValid() != other->isValid())
        return false;
dt's avatar
dt committed
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580

    return true;
}

int BaseQtVersion::uniqueId() const
{
    return m_id;
}

bool BaseQtVersion::isAutodetected() const
{
    return m_isAutodetected;
}

QString BaseQtVersion::autodetectionSource() const
{
    return m_autodetectionSource;
}

dt's avatar
dt committed
581
582
583
584
585
void BaseQtVersion::setAutoDetectionSource(const QString &autodetectionSource)
{
    m_autodetectionSource = autodetectionSource;
}

dt's avatar
dt committed
586
587
QString BaseQtVersion::displayName() const
{
588
    return Utils::expandMacros(unexpandedDisplayName(), macroExpander());
dt's avatar
dt committed
589
590
}

591
QString BaseQtVersion::unexpandedDisplayName() const
dt's avatar
dt committed
592
{
593
594
595
596
597
598
    return m_unexpandedDisplayName;
}

void BaseQtVersion::setUnexpandedDisplayName(const QString &name)
{
    m_unexpandedDisplayName = name;
dt's avatar
dt committed
599
600
601
602
603
604
605
606
607
608
}

QString BaseQtVersion::toHtml(bool verbose) const
{
    QString rc;
    QTextStream str(&rc);
    str << "<html><body><table>";
    str << "<tr><td><b>" << QCoreApplication::translate("BaseQtVersion", "Name:")
        << "</b></td><td>" << displayName() << "</td></tr>";
    if (!isValid()) {
609
610
611
        str << "<tr><td colspan=2><b>"
            << QCoreApplication::translate("BaseQtVersion", "Invalid Qt version")
            << "</b></td></tr>";
dt's avatar
dt committed
612
    } else {
613
614
        str << "<tr><td><b>" << QCoreApplication::translate("BaseQtVersion", "ABI:")
            << "</b></td>";
hjk's avatar
hjk committed
615
        const QList<Abi> abis = qtAbis();
616
        if (abis.isEmpty()) {
hjk's avatar
hjk committed
617
            str << "<td>" << Abi().toString() << "</td></tr>";
618
619
620
621
622
623
        } else {
            for (int i = 0; i < abis.size(); ++i) {
                if (i)
                    str << "<tr><td></td>";
                str << "<td>" << abis.at(i).toString() << "</td></tr>";
            }
dt's avatar
dt committed
624
625
        }
        str << "<tr><td><b>" << QCoreApplication::translate("BaseQtVersion", "Source:")
626
            << "</b></td><td>" << sourcePath().toUserOutput() << "</td></tr>";
dt's avatar
dt committed
627
        str << "<tr><td><b>" << QCoreApplication::translate("BaseQtVersion", "mkspec:")
628
            << "</b></td><td>" << mkspec().toUserOutput() << "</td></tr>";
dt's avatar
dt committed
629
        str << "<tr><td><b>" << QCoreApplication::translate("BaseQtVersion", "qmake:")
630
            << "</b></td><td>" << m_qmakeCommand.toUserOutput() << "</td></tr>";
dt's avatar
dt committed
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
        ensureMkSpecParsed();
        if (!mkspecPath().isEmpty()) {
            if (m_defaultConfigIsDebug || m_defaultConfigIsDebugAndRelease) {
                str << "<tr><td><b>" << QCoreApplication::translate("BaseQtVersion", "Default:") << "</b></td><td>"
                    << (m_defaultConfigIsDebug ? "debug" : "release");
                if (m_defaultConfigIsDebugAndRelease)
                    str << " debug_and_release";
                str << "</td></tr>";
            } // default config.
        }
        str << "<tr><td><b>" << QCoreApplication::translate("BaseQtVersion", "Version:")
            << "</b></td><td>" << qtVersionString() << "</td></tr>";
        if (verbose) {
            const QHash<QString,QString> vInfo = versionInfo();
            if (!vInfo.isEmpty()) {
646
647
648
649
                QStringList keys = vInfo.keys();
                keys.sort();
                foreach (QString variableName, keys) {
                    const QString &value = vInfo.value(variableName);
650
                    if (variableName != QLatin1String("QMAKE_MKSPECS")
651
652
653
654
655
656
657
658
                        && !variableName.endsWith(QLatin1String("/raw"))) {
                        bool isPath = false;
                        if (variableName.contains(QLatin1String("_HOST_"))
                            || variableName.contains(QLatin1String("_INSTALL_"))) {
                            if (!variableName.endsWith(QLatin1String("/get")))
                                continue;
                            variableName.chop(4);
                            isPath = true;
659
660
                        } else if (variableName == QLatin1String("QT_SYSROOT")) {
                            isPath = true;
661
                        }
662
                        str << "<tr><td><pre>" << variableName <<  "</pre></td><td>";
663
664
                        if (value.isEmpty())
                            isPath = false;
665
666
667
668
                        if (isPath) {
                            str << "<a href=\"" << QUrl::fromLocalFile(value).toString()
                                << "\">" << QDir::toNativeSeparators(value) << "</a>";
                        } else {
669
                            str << value;
670
671
672
673
                        }
                        str << "</td></tr>";
                    }
                }
dt's avatar
dt committed
674
675
676
677
678
679
680
681
682
683
684
685
            }
        }
    }
    str << "</table></body></html>";
    return rc;
}

void BaseQtVersion::updateSourcePath() const
{
    if (!m_sourcePath.isEmpty())
        return;
    updateVersionInfo();
686
    m_sourcePath = sourcePath(m_versionInfo);
dt's avatar
dt committed
687
688
}

689
FileName BaseQtVersion::sourcePath() const
dt's avatar
dt committed
690
691
692
693
694
695
696
697
698
699
{
    updateSourcePath();
    return m_sourcePath;
}

QString BaseQtVersion::designerCommand() const
{
    if (!isValid())
        return QString();
    if (m_designerCommand.isNull())
700
        m_designerCommand = findQtBinary(Designer);
dt's avatar
dt committed
701
702
703
704
705
706
707
708
    return m_designerCommand;
}

QString BaseQtVersion::linguistCommand() const
{
    if (!isValid())
        return QString();
    if (m_linguistCommand.isNull())
709
        m_linguistCommand = findQtBinary(Linguist);
dt's avatar
dt committed
710
711
712
    return m_linguistCommand;
}

713
714
715
716
717
718
719
720
721
722
QString BaseQtVersion::qmlsceneCommand() const
{
    if (!isValid())
        return QString();

    if (m_qmlsceneCommand.isNull())
        m_qmlsceneCommand = findQtBinary(QmlScene);
    return m_qmlsceneCommand;
}

dt's avatar
dt committed
723
724
725
726
727
QString BaseQtVersion::qmlviewerCommand() const
{
    if (!isValid())
        return QString();

728
729
    if (m_qmlviewerCommand.isNull())
        m_qmlviewerCommand = findQtBinary(QmlViewer);
dt's avatar
dt committed
730
731
732
    return m_qmlviewerCommand;
}

Daniel Teske's avatar
Compile    
Daniel Teske committed
733
QString BaseQtVersion::findQtBinary(Binaries binary) const
dt's avatar
dt committed
734
{
735
736
    QString baseDir;
    if (qtVersion() < QtVersionNumber(5, 0, 0)) {
737
        baseDir = qmakeProperty("QT_HOST_BINS");
738
739
740
    } else {
        ensureMkSpecParsed();
        switch (binary) {
741
        case QmlScene:
Aurindam Jana's avatar
Aurindam Jana committed
742
            baseDir = m_mkspecValues.value(QLatin1String("QT.qml.bins"));
743
            break;
744
745
746
        case QmlViewer:
            baseDir = m_mkspecValues.value(QLatin1String("QT.declarative.bins"));
            break;
747
748
        case Designer:
        case Linguist:
749
            baseDir = m_mkspecValues.value(QLatin1String("QT.designer.bins"));
750
751
            break;
        case Uic:
752
            baseDir = qmakeProperty("QT_HOST_BINS");
753
754
755
756
757
758
759
760
            break;
        default:
            // Can't happen
            Q_ASSERT(false);
        }
    }

    if (baseDir.isEmpty())
dt's avatar
dt committed
761
        return QString();
762
    if (!baseDir.endsWith(QLatin1Char('/')))
763
        baseDir += QLatin1Char('/');
dt's avatar
dt committed
764

765
766
    QStringList possibleCommands;
    switch (binary) {
767
768
    case QmlScene:
        possibleCommands << HostOsInfo::withExecutableSuffix(QLatin1String("qmlscene"));
769
        break;
770
    case QmlViewer: {
771
        if (HostOsInfo::isMacHost())
772
773
            possibleCommands << QLatin1String("QMLViewer.app/Contents/MacOS/QMLViewer");
        else
774
            possibleCommands << HostOsInfo::withExecutableSuffix(QLatin1String("qmlviewer"));
775
    }
776
777
        break;
    case Designer:
778
        if (HostOsInfo::isMacHost())
779
780
            possibleCommands << QLatin1String("Designer.app/Contents/MacOS/Designer");
        else
781
            possibleCommands << HostOsInfo::withExecutableSuffix(QLatin1String("designer"));
782
783
        break;
    case Linguist:
784
        if (HostOsInfo::isMacHost())
785
            possibleCommands << QLatin1String("Linguist.app/Contents/MacOS/Linguist");
786
            possibleCommands << HostOsInfo::withExecutableSuffix(QLatin1String("linguist"));
787
788
        break;
    case Uic:
789
790
791
792
793
794
        if (HostOsInfo::isWindowsHost()) {
            possibleCommands << QLatin1String("uic.exe");
        } else {
            possibleCommands << QLatin1String("uic-qt4") << QLatin1String("uic4")
                             << QLatin1String("uic");
        }
795
796
797
798
        break;
    default:
        Q_ASSERT(false);
    }
dt's avatar
dt committed
799
    foreach (const QString &possibleCommand, possibleCommands) {
800
        const QString fullPath = baseDir + possibleCommand;
dt's avatar
dt committed
801
802
803
804
805
806
807
808
809
810
811
812
        if (QFileInfo(fullPath).isFile())
            return QDir::cleanPath(fullPath);
    }
    return QString();
}

QString BaseQtVersion::uicCommand() const
{
    if (!isValid())
        return QString();
    if (!m_uicCommand.isNull())
        return m_uicCommand;
813
    m_uicCommand = findQtBinary(Uic);
dt's avatar
dt committed
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
    return m_uicCommand;
}

void BaseQtVersion::updateMkspec() const
{
    if (uniqueId() == -1 || m_mkspecUpToDate)
        return;

    m_mkspecUpToDate = true;
    m_mkspecFullPath = mkspecFromVersionInfo(versionInfo());

    m_mkspec = m_mkspecFullPath;
    if (m_mkspecFullPath.isEmpty())
        return;

829
    FileName baseMkspecDir = mkspecDirectoryFromVersionInfo(versionInfo());
dt's avatar
dt committed
830

831
832
    if (m_mkspec.isChildOf(baseMkspecDir)) {
        m_mkspec = m_mkspec.relativeChildPath(baseMkspecDir);
dt's avatar
dt committed
833
834
//        qDebug() << "Setting mkspec to"<<mkspec;
    } else {
835
        FileName sourceMkSpecPath = sourcePath().appendPath(QLatin1String("mkspecs"));
836
837
        if (m_mkspec.isChildOf(sourceMkSpecPath)) {
            m_mkspec = m_mkspec.relativeChildPath(sourceMkSpecPath);
dt's avatar
dt committed
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
        } else {
            // Do nothing
        }
    }
}

void BaseQtVersion::ensureMkSpecParsed() const
{
    if (m_mkspecReadUpToDate)
        return;
    m_mkspecReadUpToDate = true;

    if (mkspecPath().isEmpty())
        return;

853
    QMakeVfs vfs;
854
    ProFileGlobals option;
855
    option.setProperties(versionInfo());
856
    option.environment = qmakeRunEnvironment().toProcessEnvironment();
dt's avatar
dt committed
857
858
    ProMessageHandler msgHandler(true);
    ProFileCacheManager::instance()->incRefCount();
859
860
    QMakeParser parser(ProFileCacheManager::instance()->cache(), &vfs, &msgHandler);
    ProFileEvaluator evaluator(&option, &parser, &vfs, &msgHandler);
861
    evaluator.loadNamedSpec(mkspecPath().toString(), false);
dt's avatar
dt committed
862
863
864
865
866
867
868
869

    parseMkSpec(&evaluator);

    ProFileCacheManager::instance()->decRefCount();
}

void BaseQtVersion::parseMkSpec(ProFileEvaluator *evaluator) const
{
870
871
    m_configValues = evaluator->values(QLatin1String("CONFIG"));
    m_qtConfigValues = evaluator->values(QLatin1String("QT_CONFIG"));
dt's avatar
dt committed
872
    m_defaultConfigIsDebugAndRelease = false;
873
    m_frameworkBuild = false;
874
    foreach (const QString &value, m_configValues) {
875
        if (value == QLatin1String("debug"))
dt's avatar
dt committed
876
            m_defaultConfigIsDebug = true;
877
        else if (value == QLatin1String("release"))
dt's avatar
dt committed
878
            m_defaultConfigIsDebug = false;
879
        else if (value == QLatin1String("build_all"))
dt's avatar
dt committed
880
            m_defaultConfigIsDebugAndRelease = true;
881
882
        else if (value == QLatin1String("qt_framework"))
            m_frameworkBuild = true;
dt's avatar
dt committed
883
    }
884
    const QString designerBins = QLatin1String("QT.designer.bins");
885
886
    const QString qmlBins = QLatin1String("QT.qml.bins");
    const QString declarativeBins = QLatin1String("QT.declarative.bins");
887
888
    const QString libinfix = QLatin1String(MKSPEC_VALUE_LIBINFIX);
    const QString ns = QLatin1String(MKSPEC_VALUE_NAMESPACE);
889
    m_mkspecValues.insert(designerBins, evaluator->value(designerBins));
890
    m_mkspecValues.insert(qmlBins, evaluator->value(qmlBins));
891
    m_mkspecValues.insert(declarativeBins, evaluator->value(declarativeBins));
892
893
    m_mkspecValues.insert(libinfix, evaluator->value(libinfix));
    m_mkspecValues.insert(ns, evaluator->value(ns));
dt's avatar
dt committed
894
895
}

896
897
AbstractMacroExpander *BaseQtVersion::createMacroExpander() const
{
898
899
900
901
902
    return new MacroExpander([this](const QString &name, QString *ret) -> bool {
        if (name == QLatin1String("Qt:name"))
            return false;
        return QtKitInformation::resolveQtMacro(this, name, ret);
    });
903
904
}

905
FileName BaseQtVersion::mkspec() const
dt's avatar
dt committed
906
907
908
909
910
{
    updateMkspec();
    return m_mkspec;
}

hjk's avatar
hjk committed
911
FileName BaseQtVersion::mkspecFor(ToolChain *tc) const
912
{
hjk's avatar
hjk committed
913
    FileName versionSpec = mkspec();
914
    if (!tc)
915
        return versionSpec;
916

917
    const QList<FileName> tcSpecList = tc->suggestedMkspecList();
918
919
    if (tcSpecList.contains(versionSpec))
        return versionSpec;
920
    foreach (const FileName &tcSpec, tcSpecList) {
921
922
923
924
        if (hasMkspec(tcSpec))
            return tcSpec;
    }

925
    return versionSpec;
926
927
}

928
FileName BaseQtVersion::mkspecPath() const
dt's avatar
dt committed
929
930
931
932
933
{
    updateMkspec();
    return m_mkspecFullPath;
}

934
bool BaseQtVersion::hasMkspec(const FileName &spec) const
935
936
{
    QFileInfo fi;
937
    fi.setFile(QDir::fromNativeSeparators(qmakeProperty("QT_HOST_DATA"))
938
               + QLatin1String("/mkspecs/") + spec.toString());
939
940
    if (fi.isDir())
        return true;
941
    fi.setFile(sourcePath().toString() + QLatin1String("/mkspecs/") + spec.toString());
942
943
944
    return fi.isDir();
}

dt's avatar
dt committed
945
946
947
948
949
950
951
952
953
954
955
956
957
958
BaseQtVersion::QmakeBuildConfigs BaseQtVersion::defaultBuildConfig() const
{
    ensureMkSpecParsed();
    BaseQtVersion::QmakeBuildConfigs result = BaseQtVersion::QmakeBuildConfig(0);

    if (m_defaultConfigIsDebugAndRelease)
        result = BaseQtVersion::BuildAll;
    if (m_defaultConfigIsDebug)
        result = result | BaseQtVersion::DebugBuild;
    return result;
}

QString BaseQtVersion::qtVersionString() const
{
959
    updateVersionInfo();
dt's avatar
dt committed
960
961
962
963
964
965
966
967
968
969
970
971
    return m_qtVersionString;
}

QtVersionNumber BaseQtVersion::qtVersion() const
{
    return QtVersionNumber(qtVersionString());
}

void BaseQtVersion::updateVersionInfo() const
{
    if (m_versionInfoUpToDate)
        return;
972
    if (!m_qmakeIsExecutable)
973
        return;
dt's avatar
dt committed
974
975
976

    // extract data from qmake executable
    m_versionInfo.clear();
hjk's avatar
hjk committed
977
    m_installed = true;
dt's avatar
dt committed
978
979
980
981
    m_hasExamples = false;
    m_hasDocumentation = false;
    m_hasQmlDump = false;

982
983
    if (!queryQMakeVariables(qmakeCommand(), qmakeRunEnvironment(), &m_versionInfo)) {
        m_qmakeIsExecutable = false;
984
985
        qWarning("Cannot update Qt version information: %s cannot be run.",
                 qPrintable(qmakeCommand().toString()));
dt's avatar
dt committed
986
        return;
987
988
    }
    m_qmakeIsExecutable = true;
dt's avatar
dt committed
989

990
991
992
    const QString qtInstallData = qmakeProperty(m_versionInfo, "QT_INSTALL_DATA");
    const QString qtInstallBins = qmakeProperty(m_versionInfo, "QT_INSTALL_BINS");
    const QString qtHeaderData = qmakeProperty(m_versionInfo, "QT_INSTALL_HEADERS");
993
    if (!qtInstallData.isNull()) {
dt's avatar
dt committed
994
995
        if (!qtInstallData.isEmpty()) {
            m_hasQmlDump
996
997
                    = !QmlDumpTool::toolForQtPaths(qtInstallData, qtInstallBins, qtHeaderData, false).isEmpty()
                    || !QmlDumpTool::toolForQtPaths(qtInstallData, qtInstallBins, qtHeaderData, true).isEmpty();
dt's avatar
dt committed
998
999
1000
1001
        }
    }

    // Now check for a qt that is configured with a prefix but not installed
1002
    QString installDir = qmakeProperty(m_versionInfo, "QT_HOST_BINS");
1003
1004
    if (!installDir.isNull()) {
        QFileInfo fi(installDir);
dt's avatar
dt committed
1005
        if (!fi.exists())
hjk's avatar
hjk committed
1006
            m_installed = false;
dt's avatar
dt committed
1007
    }
1008
1009
1010
1011
1012
1013
1014
1015
    // Framework builds for Qt 4.8 don't use QT_INSTALL_HEADERS
    // so we don't check on mac
    if (!HostOsInfo::isMacHost()) {
        if (!qtHeaderData.isNull()) {
            const QFileInfo fi(qtHeaderData);
            if (!fi.exists())
                m_installed = false;
        }
dt's avatar
dt committed
1016
    }
1017
    const QString qtInstallDocs = qmakeProperty(m_versionInfo, "QT_INSTALL_DOCS");
1018
1019
    if (!qtInstallDocs.isNull()) {
        const QFileInfo fi(qtInstallDocs);
dt's avatar
dt committed
1020
1021
1022
        if (fi.exists())
            m_hasDocumentation = true;
    }
1023
    const QString qtInstallExamples = qmakeProperty(m_versionInfo, "QT_INSTALL_EXAMPLES");
1024
1025
    if (!qtInstallExamples.isNull()) {
        const QFileInfo fi(qtInstallExamples);
dt's avatar
dt committed
1026
1027
1028
        if (fi.exists())
            m_hasExamples = true;
    }
1029
    const QString qtInstallDemos = qmakeProperty(m_versionInfo, "QT_INSTALL_DEMOS");
1030
1031
    if (!qtInstallDemos.isNull()) {
        const QFileInfo fi(qtInstallDemos);
dt's avatar
dt committed
1032
1033
1034
        if (fi.exists())
            m_hasDemos = true;
    }
1035
    m_qtVersionString = qmakeProperty(m_versionInfo, "QT_VERSION");