gcctoolchain.cpp 40.7 KB
Newer Older
1
2
3
4
/**************************************************************************
**
** This file is part of Qt Creator
**
hjk's avatar
hjk committed
5
** Copyright (c) 2012 Nokia Corporation and/or its subsidiary(-ies).
6
**
7
** Contact: Nokia Corporation (qt-info@nokia.com)
8
9
10
11
**
**
** GNU Lesser General Public License Usage
**
hjk's avatar
hjk committed
12
13
14
15
16
17
** 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.
18
19
**
** In addition, as a special exception, Nokia gives you certain additional
hjk's avatar
hjk committed
20
** rights. These rights are described in the Nokia Qt LGPL Exception
21
22
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
hjk's avatar
hjk committed
23
24
25
26
27
** Other Usage
**
** Alternatively, this file may be used in accordance with the terms and
** 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.
30
31
32
33
**
**************************************************************************/

#include "gcctoolchain.h"
34
#include "clangparser.h"
35
#include "gcctoolchainfactories.h"
36
37
38
#include "gccparser.h"
#include "linuxiccparser.h"
#include "projectexplorerconstants.h"
39
#include "toolchainmanager.h"
40
41
42

#include <utils/environment.h>
#include <utils/synchronousprocess.h>
43
#include <utils/qtcassert.h>
44
#include <utils/pathchooser.h>
45
46
47
48
49

#include <QtCore/QBuffer>
#include <QtCore/QCoreApplication>
#include <QtCore/QFileInfo>
#include <QtCore/QProcess>
50
#include <QtCore/QScopedPointer>
51

Tobias Hunger's avatar
Tobias Hunger committed
52
#include <QtGui/QComboBox>
53
#include <QtGui/QFormLayout>
54
55
56
57
58
59
60
61
#include <QtGui/QLabel>

namespace ProjectExplorer {

// --------------------------------------------------------------------------
// Helpers:
// --------------------------------------------------------------------------

62
static const char compilerCommandKeyC[] = "ProjectExplorer.GccToolChain.Path";
Tobias Hunger's avatar
Tobias Hunger committed
63
static const char targetAbiKeyC[] = "ProjectExplorer.GccToolChain.TargetAbi";
64
static const char supportedAbisKeyC[] = "ProjectExplorer.GccToolChain.SupportedAbis";
65
static const char debuggerCommandKeyC[] = "ProjectExplorer.GccToolChain.Debugger";
66

67
static QByteArray runGcc(const Utils::FileName &gcc, const QStringList &arguments, const QStringList &env)
68
{
69
    if (gcc.isEmpty() || !gcc.toFileInfo().isExecutable())
70
71
72
        return QByteArray();

    QProcess cpp;
73
74
    // Force locale: This function is used only to detect settings inside the tool chain, so this is save.
    QStringList environment(env);
75
    environment.append(QLatin1String("LC_ALL=C"));
76
77

    cpp.setEnvironment(environment);
78
    cpp.start(gcc.toString(), arguments);
79
    if (!cpp.waitForStarted()) {
80
        qWarning("%s: Cannot start '%s': %s", Q_FUNC_INFO, qPrintable(gcc.toUserOutput()),
81
82
83
84
85
86
            qPrintable(cpp.errorString()));
        return QByteArray();
    }
    cpp.closeWriteChannel();
    if (!cpp.waitForFinished()) {
        Utils::SynchronousProcess::stopProcess(cpp);
87
        qWarning("%s: Timeout running '%s'.", Q_FUNC_INFO, qPrintable(gcc.toUserOutput()));
88
89
90
        return QByteArray();
    }
    if (cpp.exitStatus() != QProcess::NormalExit) {
91
        qWarning("%s: '%s' crashed.", Q_FUNC_INFO, qPrintable(gcc.toUserOutput()));
92
93
        return QByteArray();
    }
dt's avatar
dt committed
94

Friedemann Kleint's avatar
Friedemann Kleint committed
95
    return cpp.readAllStandardOutput() + '\n' + cpp.readAllStandardError();
96
97
}

98
static QByteArray gccPredefinedMacros(const Utils::FileName &gcc, const QStringList &env)
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
{
    QStringList arguments;
    arguments << QLatin1String("-xc++")
              << QLatin1String("-E")
              << QLatin1String("-dM")
              << QLatin1String("-");

    QByteArray predefinedMacros = runGcc(gcc, arguments, env);
#ifdef Q_OS_MAC
    // Turn off flag indicating Apple's blocks support
    const QByteArray blocksDefine("#define __BLOCKS__ 1");
    const QByteArray blocksUndefine("#undef __BLOCKS__");
    const int idx = predefinedMacros.indexOf(blocksDefine);
    if (idx != -1) {
        predefinedMacros.replace(idx, blocksDefine.length(), blocksUndefine);
    }

    // Define __strong and __weak (used for Apple's GC extension of C) to be empty
    predefinedMacros.append("#define __strong\n");
    predefinedMacros.append("#define __weak\n");
#endif // Q_OS_MAC
    return predefinedMacros;
}

123
static QList<HeaderPath> gccHeaderPathes(const Utils::FileName &gcc, const QStringList &env)
124
125
126
127
128
129
130
131
132
133
134
{
    QList<HeaderPath> systemHeaderPaths;
    QStringList arguments;
    arguments << QLatin1String("-xc++")
              << QLatin1String("-E")
              << QLatin1String("-v")
              << QLatin1String("-");

    QByteArray line;
    QByteArray data = runGcc(gcc, arguments, env);
    QBuffer cpp(&data);
dt's avatar
dt committed
135
    cpp.open(QIODevice::ReadOnly);
136
137
138
139
140
141
142
143
144
145
146
147
    while (cpp.canReadLine()) {
        line = cpp.readLine();
        if (line.startsWith("#include"))
            break;
    }

    if (!line.isEmpty() && line.startsWith("#include")) {
        HeaderPath::Kind kind = HeaderPath::UserHeaderPath;
        while (cpp.canReadLine()) {
            line = cpp.readLine();
            if (line.startsWith("#include")) {
                kind = HeaderPath::GlobalHeaderPath;
148
            } else if (! line.isEmpty() && QChar(QLatin1Char(line.at(0))).isSpace()) {
149
150
151
152
153
154
155
156
157
158
159
160
161
162
                HeaderPath::Kind thisHeaderKind = kind;

                line = line.trimmed();

                const int index = line.indexOf(" (framework directory)");
                if (index != -1) {
                    line.truncate(index);
                    thisHeaderKind = HeaderPath::FrameworkHeaderPath;
                }

                systemHeaderPaths.append(HeaderPath(QFile::decodeName(line), thisHeaderKind));
            } else if (line.startsWith("End of search list.")) {
                break;
            } else {
163
                qWarning("%s: Ignoring line: %s", __FUNCTION__, line.constData());
164
165
166
167
168
169
            }
        }
    }
    return systemHeaderPaths;
}

Tobias Hunger's avatar
Tobias Hunger committed
170
static QList<ProjectExplorer::Abi> guessGccAbi(const QString &m)
171
{
Tobias Hunger's avatar
Tobias Hunger committed
172
173
    QList<ProjectExplorer::Abi> abiList;

174
175
    QString machine = m.toLower();
    if (machine.isEmpty())
Tobias Hunger's avatar
Tobias Hunger committed
176
        return abiList;
177

178
    QStringList parts = machine.split(QRegExp(QLatin1String("[ /-]")));
179

hjk's avatar
hjk committed
180
181
182
183
    ProjectExplorer::Abi::Architecture arch = ProjectExplorer::Abi::UnknownArchitecture;
    ProjectExplorer::Abi::OS os = ProjectExplorer::Abi::UnknownOS;
    ProjectExplorer::Abi::OSFlavor flavor = ProjectExplorer::Abi::UnknownFlavor;
    ProjectExplorer::Abi::BinaryFormat format = ProjectExplorer::Abi::UnknownFormat;
184
    int width = 0;
185
186
187
188
    int unknownCount = 0;

    foreach (const QString &p, parts) {
        if (p == QLatin1String("unknown") || p == QLatin1String("pc") || p == QLatin1String("none")
Tobias Hunger's avatar
Tobias Hunger committed
189
            || p == QLatin1String("gnu") || p == QLatin1String("uclibc")
Tobias Hunger's avatar
Tobias Hunger committed
190
            || p == QLatin1String("86_64") || p == QLatin1String("redhat") || p == QLatin1String("gnueabi")) {
191
192
193
            continue;
        } else if (p == QLatin1String("i386") || p == QLatin1String("i486") || p == QLatin1String("i586")
                   || p == QLatin1String("i686") || p == QLatin1String("x86")) {
hjk's avatar
hjk committed
194
            arch = ProjectExplorer::Abi::X86Architecture;
195
            width = 32;
196
        } else if (p.startsWith(QLatin1String("arm"))) {
hjk's avatar
hjk committed
197
            arch = ProjectExplorer::Abi::ArmArchitecture;
198
            width = 32;
Tobias Hunger's avatar
Tobias Hunger committed
199
        } else if (p == QLatin1String("mipsel")) {
Tobias Hunger's avatar
Tobias Hunger committed
200
            arch = ProjectExplorer::Abi::MipsArchitecture;
Tobias Hunger's avatar
Tobias Hunger committed
201
            width = 32;
Tobias Hunger's avatar
Tobias Hunger committed
202
        } else if (p == QLatin1String("x86_64") || p == QLatin1String("amd64")) {
hjk's avatar
hjk committed
203
            arch = ProjectExplorer::Abi::X86Architecture;
204
            width = 64;
205
206
        } else if (p == QLatin1String("powerpc")) {
            arch = ProjectExplorer::Abi::PowerPCArchitecture;
207
208
        } else if (p == QLatin1String("w64")) {
            width = 64;
Tobias Hunger's avatar
Tobias Hunger committed
209
        } else if (p == QLatin1String("linux") || p == QLatin1String("linux6e")) {
hjk's avatar
hjk committed
210
            os = ProjectExplorer::Abi::LinuxOS;
Tobias Hunger's avatar
Tobias Hunger committed
211
212
213
            if (flavor == Abi::UnknownFlavor)
                flavor = ProjectExplorer::Abi::GenericLinuxFlavor;
            format = ProjectExplorer::Abi::ElfFormat;
214
        } else if (p.startsWith(QLatin1String("freebsd"))) {
215
            os = ProjectExplorer::Abi::BsdOS;
216
            if (flavor == Abi::UnknownFlavor)
217
                flavor = ProjectExplorer::Abi::FreeBsdFlavor;
218
            format = ProjectExplorer::Abi::ElfFormat;
Tobias Hunger's avatar
Tobias Hunger committed
219
220
221
        } else if (p == QLatin1String("meego")) {
            os = ProjectExplorer::Abi::LinuxOS;
            flavor = ProjectExplorer::Abi::MeegoLinuxFlavor;
hjk's avatar
hjk committed
222
            format = ProjectExplorer::Abi::ElfFormat;
223
        } else if (p == QLatin1String("symbianelf")) {
hjk's avatar
hjk committed
224
225
226
            os = ProjectExplorer::Abi::SymbianOS;
            flavor = ProjectExplorer::Abi::SymbianDeviceFlavor;
            format = ProjectExplorer::Abi::ElfFormat;
227
            width = 32;
Tobias Hunger's avatar
Tobias Hunger committed
228
        } else if (p == QLatin1String("mingw32") || p == QLatin1String("win32") || p == QLatin1String("mingw32msvc")) {
hjk's avatar
hjk committed
229
230
231
232
            arch = ProjectExplorer::Abi::X86Architecture;
            os = ProjectExplorer::Abi::WindowsOS;
            flavor = ProjectExplorer::Abi::WindowsMSysFlavor;
            format = ProjectExplorer::Abi::PEFormat;
233
234
            if (width == 0)
                width = 32;
235
        } else if (p == QLatin1String("apple")) {
hjk's avatar
hjk committed
236
237
238
            os = ProjectExplorer::Abi::MacOS;
            flavor = ProjectExplorer::Abi::GenericMacFlavor;
            format = ProjectExplorer::Abi::MachOFormat;
239
240
        } else if (p == QLatin1String("darwin10")) {
            width = 64;
241
242
        } else if (p == QLatin1String("darwin9")) {
            width = 32;
243
        } else if (p == QLatin1String("gnueabi")) {
hjk's avatar
hjk committed
244
            format = ProjectExplorer::Abi::ElfFormat;
245
246
247
248
249
250
        } else {
            ++unknownCount;
        }
    }

    if (unknownCount == parts.count())
Tobias Hunger's avatar
Tobias Hunger committed
251
252
        return abiList;

253
    if (os == Abi::MacOS && arch != Abi::ArmArchitecture) {
Tobias Hunger's avatar
Tobias Hunger committed
254
255
        // Apple does PPC and x86!
        abiList << ProjectExplorer::Abi(arch, os, flavor, format, width);
256
257
258
        abiList << ProjectExplorer::Abi(arch, os, flavor, format, width == 64 ? 32 : 64);
        abiList << ProjectExplorer::Abi(arch == Abi::X86Architecture ? Abi::PowerPCArchitecture : Abi::X86Architecture, os, flavor, format, width);
        abiList << ProjectExplorer::Abi(arch == Abi::X86Architecture ? Abi::PowerPCArchitecture : Abi::X86Architecture, os, flavor, format, width == 64 ? 32 : 64);
Tobias Hunger's avatar
Tobias Hunger committed
259
260
261
262
263
264
265
    } else if (width == 64) {
        abiList << ProjectExplorer::Abi(arch, os, flavor, format, width);
        abiList << ProjectExplorer::Abi(arch, os, flavor, format, 32);
    } else {
        abiList << ProjectExplorer::Abi(arch, os, flavor, format, width);
    }
    return abiList;
266
267
}

268
static QList<ProjectExplorer::Abi> guessGccAbi(const Utils::FileName &path, const QStringList &env)
269
270
271
272
273
274
{
    QStringList arguments(QLatin1String("-dumpmachine"));
    QString machine = QString::fromLocal8Bit(runGcc(path, arguments, env)).trimmed();
    return guessGccAbi(machine);
}

275
static QString gccVersion(const Utils::FileName &path, const QStringList &env)
276
277
278
279
280
{
    QStringList arguments(QLatin1String("-dumpversion"));
    return QString::fromLocal8Bit(runGcc(path, arguments, env)).trimmed();
}

281
282
283
284
285
// --------------------------------------------------------------------------
// GccToolChain
// --------------------------------------------------------------------------

GccToolChain::GccToolChain(bool autodetect) :
Tobias Hunger's avatar
Tobias Hunger committed
286
    ToolChain(QLatin1String(Constants::GCC_TOOLCHAIN_ID), autodetect)
287
288
289
{ }

GccToolChain::GccToolChain(const QString &id, bool autodetect) :
Tobias Hunger's avatar
Tobias Hunger committed
290
    ToolChain(id, autodetect)
291
292
293
294
{ }

GccToolChain::GccToolChain(const GccToolChain &tc) :
    ToolChain(tc),
295
    m_predefinedMacros(tc.predefinedMacros()),
296
    m_compilerCommand(tc.compilerCommand()),
297
    m_debuggerCommand(tc.debuggerCommand()),
298
299
300
301
302
    m_targetAbi(tc.m_targetAbi),
    m_supportedAbis(tc.m_supportedAbis),
    m_headerPathes(tc.m_headerPathes),
    m_version(tc.m_version)
{ }
303
304
305
306

QString GccToolChain::defaultDisplayName() const
{
    if (!m_targetAbi.isValid())
Tobias Hunger's avatar
Tobias Hunger committed
307
308
        return typeDisplayName();
    return QString::fromLatin1("%1 (%2 %3)").arg(typeDisplayName(),
309
310
311
312
                                                 ProjectExplorer::Abi::toString(m_targetAbi.architecture()),
                                                 ProjectExplorer::Abi::toString(m_targetAbi.wordWidth()));
}

313
314
315
316
317
318
319
320
QList<Abi> GccToolChain::findAbiForCompilerPath(const QString &path)
{
    if (path.isEmpty())
        return QList<Abi>();

    return detectSupportedAbis();
}

321
QString GccToolChain::legacyId() const
322
323
324
{
    QString i = id();
    i = i.left(i.indexOf(QLatin1Char(':')));
325
    return QString::fromLatin1("%1:%2.%3.%4")
326
               .arg(i).arg(m_compilerCommand.toString())
327
               .arg(m_targetAbi.toString()).arg(m_debuggerCommand.toString());
328
329
}

Tobias Hunger's avatar
Tobias Hunger committed
330
331
332
333
334
335
QString GccToolChain::type() const
{
    return QLatin1String("gcc");
}

QString GccToolChain::typeDisplayName() const
336
337
338
339
340
341
342
343
344
{
    return Internal::GccToolChainFactory::tr("GCC");
}

Abi GccToolChain::targetAbi() const
{
    return m_targetAbi;
}

345
346
347
348
349
350
351
QString GccToolChain::version() const
{
    if (m_version.isEmpty())
        m_version = detectVersion();
    return m_version;
}

Tobias Hunger's avatar
Tobias Hunger committed
352
353
void GccToolChain::setTargetAbi(const Abi &abi)
{
Tobias Hunger's avatar
Tobias Hunger committed
354
355
356
    if (abi == m_targetAbi)
        return;

357
358
    m_targetAbi = abi;
    toolChainUpdated();
Tobias Hunger's avatar
Tobias Hunger committed
359
360
361
362
363
364
365
}

QList<Abi> GccToolChain::supportedAbis() const
{
    return m_supportedAbis;
}

366
367
bool GccToolChain::isValid() const
{
368
    return !m_compilerCommand.isNull();
369
370
371
372
373
374
375
376
}

QByteArray GccToolChain::predefinedMacros() const
{
    if (m_predefinedMacros.isEmpty()) {
        // Using a clean environment breaks ccache/distcc/etc.
        Utils::Environment env = Utils::Environment::systemEnvironment();
        addToEnvironment(env);
377
        m_predefinedMacros = gccPredefinedMacros(m_compilerCommand, env.toStringList());
378
379
380
381
382
383
384
385
386
387
    }
    return m_predefinedMacros;
}

QList<HeaderPath> GccToolChain::systemHeaderPaths() const
{
    if (m_headerPathes.isEmpty()) {
        // Using a clean environment breaks ccache/distcc/etc.
        Utils::Environment env = Utils::Environment::systemEnvironment();
        addToEnvironment(env);
388
        m_headerPathes = gccHeaderPathes(m_compilerCommand, env.toStringList());
389
390
391
392
393
394
    }
    return m_headerPathes;
}

void GccToolChain::addToEnvironment(Utils::Environment &env) const
{
Tobias Hunger's avatar
Tobias Hunger committed
395
396
397
398
    if (!m_compilerCommand.isEmpty()) {
        Utils::FileName path = m_compilerCommand.parentDir();
        env.prependOrSetPath(path.toString());
    }
399
400
}

401
void GccToolChain::setDebuggerCommand(const Utils::FileName &d)
402
{
Tobias Hunger's avatar
Tobias Hunger committed
403
404
    if (m_debuggerCommand == d)
        return;
405
    m_debuggerCommand = d;
Tobias Hunger's avatar
Tobias Hunger committed
406
    toolChainUpdated();
407
408
}

409
Utils::FileName GccToolChain::debuggerCommand() const
410
411
412
413
{
    return m_debuggerCommand;
}

414
Utils::FileName GccToolChain::mkspec() const
415
416
{
    Abi abi = targetAbi();
417
418
419
    if (abi.os() == Abi::MacOS) {
        QString v = version();
        // prefer versioned g++ on mac. This is required to enable building for older Mac OS versions
420
        if (v.startsWith(QLatin1String("4.0")) && m_compilerCommand.endsWith(QLatin1String("-4.0")))
421
            return Utils::FileName::fromString(QLatin1String("macx-g++40"));
422
        if (v.startsWith(QLatin1String("4.2")) && m_compilerCommand.endsWith(QLatin1String("-4.2")))
423
424
            return Utils::FileName::fromString(QLatin1String("macx-g++42"));
        return Utils::FileName::fromString(QLatin1String("macx-g++"));
425
426
    }

427
    QList<Abi> gccAbiList = Abi::abisOfBinary(m_compilerCommand);
Tobias Hunger's avatar
Tobias Hunger committed
428
429
430
431
432
433
434
    Abi gccAbi;
    if (!gccAbiList.isEmpty())
        gccAbi  = gccAbiList.first();
    if (!gccAbi.isNull()
            && (gccAbi.architecture() != abi.architecture()
                || gccAbi.os() != abi.os()
                || gccAbi.osFlavor() != abi.osFlavor())) {
435
        // Note: This can fail:-(
436
        return Utils::FileName(); // this is a cross-compiler, leave the mkspec alone!
437
438
439
    }
    if (abi.os() == Abi::LinuxOS) {
        if (abi.osFlavor() != Abi::GenericLinuxFlavor)
440
            return Utils::FileName(); // most likely not a desktop, so leave the mkspec alone.
441
        if (abi.wordWidth() == gccAbi.wordWidth())
442
443
            return Utils::FileName::fromString(QLatin1String("linux-g++")); // no need to explicitly set the word width
        return Utils::FileName::fromString(QLatin1String("linux-g++-") + QString::number(m_targetAbi.wordWidth()));
444
    }
445
    if (abi.os() == Abi::BsdOS && abi.osFlavor() == Abi::FreeBsdFlavor)
446
        return Utils::FileName::fromString(QLatin1String("freebsd-g++"));
447
    return Utils::FileName();
448
449
}

450
451
452
453
454
455
456
457
458
459
QString GccToolChain::makeCommand() const
{
    return QLatin1String("make");
}

IOutputParser *GccToolChain::outputParser() const
{
    return new GccParser;
}

460
void GccToolChain::setCompilerCommand(const Utils::FileName &path)
461
{
462
    if (path == m_compilerCommand)
463
464
        return;

465
466
    bool resetDisplayName = displayName() == defaultDisplayName();

467
    m_compilerCommand = path;
468

469
    Abi currentAbi = m_targetAbi;
470
    m_supportedAbis = findAbiForCompilerPath(m_compilerCommand.toString());
471

Tobias Hunger's avatar
Tobias Hunger committed
472
    m_targetAbi = Abi();
473
474
475
476
477
    if (!m_supportedAbis.isEmpty()) {
        if (m_supportedAbis.contains(currentAbi))
            m_targetAbi = currentAbi;
        else
            m_targetAbi = m_supportedAbis.at(0);
Tobias Hunger's avatar
Tobias Hunger committed
478
    }
479
480
481
482
483

    if (resetDisplayName)
        setDisplayName(defaultDisplayName()); // calls toolChainUpdated()!
    else
        toolChainUpdated();
484
485
}

486
Utils::FileName GccToolChain::compilerCommand() const
487
{
488
    return m_compilerCommand;
489
490
491
492
493
494
495
496
497
498
}

ToolChain *GccToolChain::clone() const
{
    return new GccToolChain(*this);
}

QVariantMap GccToolChain::toMap() const
{
    QVariantMap data = ToolChain::toMap();
499
    data.insert(QLatin1String(compilerCommandKeyC), m_compilerCommand.toString());
Tobias Hunger's avatar
Tobias Hunger committed
500
    data.insert(QLatin1String(targetAbiKeyC), m_targetAbi.toString());
501
502
503
504
    QStringList abiList;
    foreach (const ProjectExplorer::Abi &a, m_supportedAbis)
        abiList.append(a.toString());
    data.insert(QLatin1String(supportedAbisKeyC), abiList);
505
    data.insert(QLatin1String(debuggerCommandKeyC), m_debuggerCommand.toString());
506
507
508
509
510
511
512
513
    return data;
}

bool GccToolChain::fromMap(const QVariantMap &data)
{
    if (!ToolChain::fromMap(data))
        return false;

514
    m_compilerCommand = Utils::FileName::fromString(data.value(QLatin1String(compilerCommandKeyC)).toString());
Tobias Hunger's avatar
Tobias Hunger committed
515
    m_targetAbi = Abi(data.value(QLatin1String(targetAbiKeyC)).toString());
516
517
518
519
520
521
522
523
    QStringList abiList = data.value(QLatin1String(supportedAbisKeyC)).toStringList();
    m_supportedAbis.clear();
    foreach (const QString &a, abiList) {
        ProjectExplorer::Abi abi(a);
        if (!abi.isValid())
            continue;
        m_supportedAbis.append(abi);
    }
524
    m_debuggerCommand = Utils::FileName::fromString(data.value(QLatin1String(debuggerCommandKeyC)).toString());
525
526
527
528
529
530
531
532
533
    return true;
}

bool GccToolChain::operator ==(const ToolChain &other) const
{
    if (!ToolChain::operator ==(other))
        return false;

    const GccToolChain *gccTc = static_cast<const GccToolChain *>(&other);
534
    return m_compilerCommand == gccTc->m_compilerCommand && m_targetAbi == gccTc->m_targetAbi
535
            && m_debuggerCommand == gccTc->m_debuggerCommand;
536
537
538
539
540
541
542
}

ToolChainConfigWidget *GccToolChain::configurationWidget()
{
    return new Internal::GccToolChainConfigWidget(this);
}

Tobias Hunger's avatar
Tobias Hunger committed
543
544
void GccToolChain::updateSupportedAbis() const
{
545
546
547
548
549
550
551
552
    if (m_supportedAbis.isEmpty())
        m_supportedAbis = detectSupportedAbis();
}

QList<Abi> GccToolChain::detectSupportedAbis() const
{
    Utils::Environment env = Utils::Environment::systemEnvironment();
    addToEnvironment(env);
553
    return guessGccAbi(m_compilerCommand, env.toStringList());
Tobias Hunger's avatar
Tobias Hunger committed
554
555
}

556
557
558
559
QString GccToolChain::detectVersion() const
{
    Utils::Environment env = Utils::Environment::systemEnvironment();
    addToEnvironment(env);
560
    return gccVersion(m_compilerCommand, env.toStringList());
561
562
}

563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
// --------------------------------------------------------------------------
// GccToolChainFactory
// --------------------------------------------------------------------------

QString Internal::GccToolChainFactory::displayName() const
{
    return tr("GCC");
}

QString Internal::GccToolChainFactory::id() const
{
    return QLatin1String(Constants::GCC_TOOLCHAIN_ID);
}

bool Internal::GccToolChainFactory::canCreate()
{
    return true;
}

ToolChain *Internal::GccToolChainFactory::create()
{
    return createToolChain(false);
}

QList<ToolChain *> Internal::GccToolChainFactory::autoDetect()
{
589
590
591
592
593
    QStringList debuggers;
#ifdef Q_OS_MAC
    // Fixme Prefer lldb once it is implemented: debuggers.push_back(QLatin1String("lldb"));
#endif
    debuggers.push_back(QLatin1String("gdb"));
594
595
596
597
598
599
600
    QList<ToolChain *> tcs = autoDetectToolchains(QLatin1String("g++"), debuggers, Abi::hostAbi());

    // Old mac compilers needed to support macx-gccXY mkspecs:
    tcs.append(autoDetectToolchains(QLatin1String("g++-4.0"), debuggers, Abi::hostAbi()));
    tcs.append(autoDetectToolchains(QLatin1String("g++-4.2"), debuggers, Abi::hostAbi()));

    return tcs;
601
602
}

603
// Used by the ToolChainManager to restore user-generated tool chains
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
bool Internal::GccToolChainFactory::canRestore(const QVariantMap &data)
{
    return idFromMap(data).startsWith(QLatin1String(Constants::GCC_TOOLCHAIN_ID) + QLatin1Char(':'));
}

ToolChain *Internal::GccToolChainFactory::restore(const QVariantMap &data)
{
    GccToolChain *tc = new GccToolChain(false);
    if (tc->fromMap(data))
        return tc;

    delete tc;
    return 0;
}

GccToolChain *Internal::GccToolChainFactory::createToolChain(bool autoDetect)
{
    return new GccToolChain(autoDetect);
}

624
625
626
QList<ToolChain *> Internal::GccToolChainFactory::autoDetectToolchains(const QString &compiler,
                                                                       const QStringList &debuggers,
                                                                       const Abi &requiredAbi)
627
628
629
{
    QList<ToolChain *> result;

630
    const Utils::Environment systemEnvironment = Utils::Environment::systemEnvironment();
631
    const Utils::FileName compilerPath = Utils::FileName::fromString(systemEnvironment.searchInPath(compiler));
632
    if (compilerPath.isEmpty())
633
        return result;
Tobias Hunger's avatar
Tobias Hunger committed
634
635

    QList<Abi> abiList = guessGccAbi(compilerPath, systemEnvironment.toStringList());
636
637
638
639
640
641
    if (!abiList.contains(requiredAbi)) {
        if (requiredAbi.wordWidth() != 64
                || !abiList.contains(Abi(requiredAbi.architecture(), requiredAbi.os(), requiredAbi.osFlavor(),
                                         requiredAbi.binaryFormat(), 32)))
            return result;
    }
Tobias Hunger's avatar
Tobias Hunger committed
642

643
    Utils::FileName debuggerPath = ToolChainManager::instance()->defaultDebugger(requiredAbi);
644
645
    if (debuggerPath.isEmpty()) {
        foreach (const QString &debugger, debuggers) {
646
            debuggerPath = Utils::FileName::fromString(systemEnvironment.searchInPath(debugger));
647
648
649
            if (!debuggerPath.isEmpty())
                break;
        }
650
    }
651

Tobias Hunger's avatar
Tobias Hunger committed
652
653
654
655
    foreach (const Abi &abi, abiList) {
        QScopedPointer<GccToolChain> tc(createToolChain(true));
        if (tc.isNull())
            return result;
656

657
        tc->setCompilerCommand(compilerPath);
Tobias Hunger's avatar
Tobias Hunger committed
658
659
        tc->setDebuggerCommand(debuggerPath);
        tc->setTargetAbi(abi);
660
        tc->setDisplayName(tc->defaultDisplayName()); // reset displayname
661
662

        result.append(tc.take());
Tobias Hunger's avatar
Tobias Hunger committed
663
    }
664
665
666
667
668
669
670
671
672

    return result;
}

// --------------------------------------------------------------------------
// GccToolChainConfigWidget
// --------------------------------------------------------------------------

Internal::GccToolChainConfigWidget::GccToolChainConfigWidget(GccToolChain *tc) :
673
    ToolChainConfigWidget(tc),
674
    m_compilerCommand(new Utils::PathChooser),
675
676
    m_abiWidget(new AbiWidget),
    m_isReadOnly(false)
677
678
679
{
    Q_ASSERT(tc);

Tobias Hunger's avatar
Tobias Hunger committed
680
681
    QFormLayout *layout = new QFormLayout(this);

682
    const QStringList gnuVersionArgs = QStringList(QLatin1String("--version"));
683
684
685
    m_compilerCommand->setExpectedKind(Utils::PathChooser::ExistingCommand);
    m_compilerCommand->setCommandVersionArguments(gnuVersionArgs);
    layout->addRow(tr("&Compiler path:"), m_compilerCommand);
686
    layout->addRow(tr("&ABI:"), m_abiWidget);
Tobias Hunger's avatar
Tobias Hunger committed
687
    m_abiWidget->setEnabled(false);
688

689
690
691
    addDebuggerCommandControls(layout, gnuVersionArgs);
    addErrorLabel(layout);

692
    setFromToolchain();
693

694
    connect(m_compilerCommand, SIGNAL(changed(QString)), this, SLOT(handleCompilerCommandChange()));
695
    connect(m_abiWidget, SIGNAL(abiChanged()), this, SLOT(handleAbiChange()));
696
697
698
699
700
701
702
703
704
705
}

void Internal::GccToolChainConfigWidget::apply()
{
    if (toolChain()->isAutoDetected())
        return;

    GccToolChain *tc = static_cast<GccToolChain *>(toolChain());
    Q_ASSERT(tc);
    QString displayName = tc->displayName();
706
    tc->setCompilerCommand(m_compilerCommand->fileName());
707
    tc->setTargetAbi(m_abiWidget->currentAbi());
708
    tc->setDisplayName(displayName); // reset display name
709
    tc->setDebuggerCommand(debuggerCommand());
710
    m_autoDebuggerCommand = Utils::FileName::fromString(QLatin1String("<manually set>"));
711
712
}

713
void Internal::GccToolChainConfigWidget::setFromToolchain()
714
{
715
716
    // subwidgets are not yet connected!
    bool blocked = blockSignals(true);
717
    GccToolChain *tc = static_cast<GccToolChain *>(toolChain());
718
    m_compilerCommand->setFileName(tc->compilerCommand());
Tobias Hunger's avatar
Tobias Hunger committed
719
    m_abiWidget->setAbis(tc->supportedAbis(), tc->targetAbi());
720
    if (!m_isReadOnly && !m_compilerCommand->path().isEmpty())
721
        m_abiWidget->setEnabled(true);
722
    setDebuggerCommand(tc->debuggerCommand());
723
    blockSignals(blocked);
724
725
726
727
728
729
}

bool Internal::GccToolChainConfigWidget::isDirty() const
{
    GccToolChain *tc = static_cast<GccToolChain *>(toolChain());
    Q_ASSERT(tc);
730
    return m_compilerCommand->fileName() != tc->compilerCommand()
731
            || m_abiWidget->currentAbi() != tc->targetAbi();
732
733
}

734
735
void Internal::GccToolChainConfigWidget::makeReadOnly()
{
736
    m_compilerCommand->setEnabled(false);
737
    m_abiWidget->setEnabled(false);
738
    m_isReadOnly = true;
739
740
741
    ToolChainConfigWidget::makeReadOnly();
}

742
void Internal::GccToolChainConfigWidget::handleCompilerCommandChange()
743
{
744
    Utils::FileName path = m_compilerCommand->fileName();
Tobias Hunger's avatar
Tobias Hunger committed
745
    QList<Abi> abiList;
Tobias Hunger's avatar
Tobias Hunger committed
746
747
    bool haveCompiler = false;
    if (!path.isEmpty()) {
748
        QFileInfo fi(path.toFileInfo());
Tobias Hunger's avatar
Tobias Hunger committed
749
750
751
        haveCompiler = fi.isExecutable() && fi.isFile();
    }
    if (haveCompiler)
Tobias Hunger's avatar
Tobias Hunger committed
752
        abiList = guessGccAbi(path, Utils::Environment::systemEnvironment().toStringList());
Tobias Hunger's avatar
Tobias Hunger committed
753
754
755
756
    m_abiWidget->setEnabled(haveCompiler);
    Abi currentAbi = m_abiWidget->currentAbi();
    m_abiWidget->setAbis(abiList, abiList.contains(currentAbi) ? currentAbi : Abi());
    handleAbiChange();
757
758
}

Tobias Hunger's avatar
Tobias Hunger committed
759
void Internal::GccToolChainConfigWidget::handleAbiChange()
760
{
761
762
    if (m_autoDebuggerCommand == debuggerCommand()) {
        ProjectExplorer::Abi abi = m_abiWidget->currentAbi();
763
        m_autoDebuggerCommand = ToolChainManager::instance()->defaultDebugger(abi);
764
765
        setDebuggerCommand(m_autoDebuggerCommand);
    }
766
767
768
    emit dirty(toolChain());
}

Tobias Hunger's avatar
Tobias Hunger committed
769
770
771
772
773
// --------------------------------------------------------------------------
// ClangToolChain
// --------------------------------------------------------------------------

ClangToolChain::ClangToolChain(bool autodetect) :
Tobias Hunger's avatar
Tobias Hunger committed
774
    GccToolChain(QLatin1String(Constants::CLANG_TOOLCHAIN_ID), autodetect)
Tobias Hunger's avatar
Tobias Hunger committed
775
776
{ }

Tobias Hunger's avatar
Tobias Hunger committed
777
778
779
780
781
782
QString ClangToolChain::type() const
{
    return QLatin1String("clang");
}

QString ClangToolChain::typeDisplayName() const
Tobias Hunger's avatar
Tobias Hunger committed
783
784
785
786
787
788
789
790
791
792
793
794
795
{
    return Internal::ClangToolChainFactory::tr("Clang");
}

QString ClangToolChain::makeCommand() const
{
#if defined(Q_OS_WIN)
    return QLatin1String("mingw32-make.exe");
#else
    return QLatin1String("make");
#endif
}

796
Utils::FileName ClangToolChain::mkspec() const
Tobias Hunger's avatar
Tobias Hunger committed
797
{
798
799
    Abi abi = targetAbi();
    if (abi.os() == Abi::MacOS)
800
        return Utils::FileName::fromString(QLatin1String("unsupported/macx-clang"));
801
    else if (abi.os() == Abi::LinuxOS)
802
        return Utils::FileName::fromString(QLatin1String("unsupported/linux-clang"));
803
    return Utils::FileName(); // Note: Not supported by Qt yet, so default to the mkspec the Qt was build with
804
805
806
807
808
}

IOutputParser *ClangToolChain::outputParser() const
{
    return new ClangParser;
Tobias Hunger's avatar
Tobias Hunger committed
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
}

ToolChain *ClangToolChain::clone() const
{
    return new ClangToolChain(*this);
}

// --------------------------------------------------------------------------
// ClangToolChainFactory
// --------------------------------------------------------------------------

QString Internal::ClangToolChainFactory::displayName() const
{
    return tr("Clang");
}

QString Internal::ClangToolChainFactory::id() const
{
    return QLatin1String(Constants::CLANG_TOOLCHAIN_ID);
}

QList<ToolChain *> Internal::ClangToolChainFactory::autoDetect()
{
    Abi ha = Abi::hostAbi();
    return autoDetectToolchains(QLatin1String("clang++"), QStringList(), ha);
}

bool Internal::ClangToolChainFactory::canCreate()
{
    return true;
}

ToolChain *Internal::ClangToolChainFactory::create()
{
    return createToolChain(false);
}

bool Internal::ClangToolChainFactory::canRestore(const QVariantMap &data)
{
    return idFromMap(data).startsWith(QLatin1String(Constants::CLANG_TOOLCHAIN_ID) + QLatin1Char(':'));
}

ToolChain *Internal::ClangToolChainFactory::restore(const QVariantMap &data)
{
    ClangToolChain *tc = new ClangToolChain(false);
    if (tc->fromMap(data))
        return tc;

    delete tc;
    return 0;
}

GccToolChain *Internal::ClangToolChainFactory::createToolChain(bool autoDetect)
{
    return new ClangToolChain(autoDetect);
}

866
867
868
869
870
871
872
873
// --------------------------------------------------------------------------
// MingwToolChain
// --------------------------------------------------------------------------

MingwToolChain::MingwToolChain(bool autodetect) :
    GccToolChain(QLatin1String(Constants::MINGW_TOOLCHAIN_ID), autodetect)
{ }

Tobias Hunger's avatar
Tobias Hunger committed
874
875
876
877
878
879
QString MingwToolChain::type() const
{
    return QLatin1String("mingw");
}

QString MingwToolChain::typeDisplayName() const
880
881
882
883
{
    return Internal::MingwToolChainFactory::tr("MinGW");
}

884
Utils::FileName MingwToolChain::mkspec() const
885
{
Tobias Hunger's avatar
Tobias Hunger committed
886
#if defined(Q_OS_WIN)
887
    return Utils::FileName::fromString(QLatin1String("win32-g++"));
Tobias Hunger's avatar
Tobias Hunger committed
888
889
890
891
892
893
#elif defined(Q_OS_LINUX)
    if (version().startsWith("4.6."))
        return Utils::FileName::fromString(QLatin1String("unsupported/win32-g++-4.6-cross"));
    else
        return Utils::FileName::fromString(QLatin1String("unsupported/win32-g++-cross"));
#endif
894
895
}

896
897
QString MingwToolChain::makeCommand() const
{
Tobias Hunger's avatar
Tobias Hunger committed
898
#ifdef Q_OS_WIN
899
    return QLatin1String("mingw32-make.exe");
Tobias Hunger's avatar
Tobias Hunger committed
900
901
902
#else
    return QLatin1String("make");
#endif
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
}

ToolChain *MingwToolChain::clone() const
{
    return new MingwToolChain(*this);
}

// --------------------------------------------------------------------------
// MingwToolChainFactory
// --------------------------------------------------------------------------

QString Internal::MingwToolChainFactory::displayName() const
{
    return tr("MinGW");
}

QString Internal::MingwToolChainFactory::id() const
{
    return QLatin1String(Constants::MINGW_TOOLCHAIN_ID);
}

QList<ToolChain *> Internal::MingwToolChainFactory::autoDetect()
{
926
927
928
929
930
931
932
    // Compatibility to pre-2.2:
    // All Mingw toolchains that exist so far are either installed by the SDK itself (in
    // which case they most likely have debuggers set up) or were created when updating
    // from a previous Qt version. Add debugger in that case.
    foreach (ToolChain *tc, ToolChainManager::instance()->toolChains()) {
        if (tc->debuggerCommand().isEmpty() && tc->id().startsWith(QLatin1String(Constants::MINGW_TOOLCHAIN_ID)))
            static_cast<MingwToolChain *>(tc)
933
                ->setDebuggerCommand(ToolChainManager::instance()->defaultDebugger(tc->targetAbi()));
934
935
    }

Tobias Hunger's avatar
Tobias Hunger committed
936
937
938
    Abi ha = Abi::hostAbi();
    return autoDetectToolchains(QLatin1String("g++"), QStringList(),
                                Abi(ha.architecture(), Abi::WindowsOS, Abi::WindowsMSysFlavor, Abi::PEFormat, ha.wordWidth()));
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
}

bool Internal::MingwToolChainFactory::canCreate()
{
    return true;
}

ToolChain *Internal::MingwToolChainFactory::create()
{
    return createToolChain(false);
}

bool Internal::MingwToolChainFactory::canRestore(const QVariantMap &data)
{
    return idFromMap(data).startsWith(QLatin1String(Constants::MINGW_TOOLCHAIN_ID) + QLatin1Char(':'));
}

ToolChain *Internal::MingwToolChainFactory::restore(const QVariantMap &data)
{
    MingwToolChain *tc = new MingwToolChain(false);
    if (tc->fromMap(data))
        return tc;

    delete tc;
    return 0;
}

GccToolChain *Internal::MingwToolChainFactory::createToolChain(bool autoDetect)
{
    return new MingwToolChain(autoDetect);
}

// --------------------------------------------------------------------------
// LinuxIccToolChain
// --------------------------------------------------------------------------

LinuxIccToolChain::LinuxIccToolChain(bool autodetect) :
    GccToolChain(QLatin1String(Constants::LINUXICC_TOOLCHAIN_ID), autodetect)
{ }

Tobias Hunger's avatar
Tobias Hunger committed
979
980
981
982
983
984
QString LinuxIccToolChain::type() const
{
    return QLatin1String("icc");
}

QString LinuxIccToolChain::typeDisplayName() const
985
986
987
988
989
990
991
992
993
{
    return Internal::LinuxIccToolChainFactory::tr("Linux ICC");
}

IOutputParser *LinuxIccToolChain::outputParser() const
{
    return new LinuxIccParser;
}

994
Utils::FileName LinuxIccToolChain::mkspec() const
995
{
996
    return Utils::FileName::fromString(QLatin1String("linux-icc-") + QString::number(targetAbi().wordWidth()));
997
998
}

999
1000
ToolChain *LinuxIccToolChain::clone() const
{
For faster browsing, not all history is shown. View entire blame