gcctoolchain.cpp 55.8 KB
Newer Older
hjk's avatar
hjk committed
1
/****************************************************************************
2
**
Eike Ziller's avatar
Eike Ziller committed
3
4
** Copyright (C) 2015 The Qt Company Ltd.
** Contact: http://www.qt.io/licensing
5
**
hjk's avatar
hjk committed
6
** This file is part of Qt Creator.
7
**
hjk's avatar
hjk committed
8
9
10
11
** 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
Eike Ziller's avatar
Eike Ziller committed
12
13
** a written agreement between you and The Qt Company.  For licensing terms and
** conditions see http://www.qt.io/terms-conditions.  For further information
Eike Ziller's avatar
Eike Ziller committed
14
** use the contact form at http://www.qt.io/contact-us.
15
16
**
** GNU Lesser General Public License Usage
hjk's avatar
hjk committed
17
** Alternatively, this file may be used under the terms of the GNU Lesser
Eike Ziller's avatar
Eike Ziller committed
18
19
20
21
22
23
** General Public License version 2.1 or version 3 as published by the Free
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
** LICENSE.LGPLv3 included in the packaging of this file.  Please review the
** following information to ensure the GNU Lesser General Public License
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
hjk's avatar
hjk committed
24
**
Eike Ziller's avatar
Eike Ziller committed
25
26
** In addition, as a special exception, The Qt Company gives you certain additional
** rights.  These rights are described in The Qt Company LGPL Exception
27
28
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
hjk's avatar
hjk committed
29
****************************************************************************/
30
31

#include "gcctoolchain.h"
32
#include "clangparser.h"
33
#include "gcctoolchainfactories.h"
34
35
36
#include "gccparser.h"
#include "linuxiccparser.h"
#include "projectexplorerconstants.h"
37
#include "toolchainmanager.h"
38

39
#include <utils/algorithm.h>
40
#include <utils/environment.h>
41
#include <utils/hostosinfo.h>
42
#include <utils/synchronousprocess.h>
43
#include <utils/pathchooser.h>
44
#include <utils/qtcassert.h>
45
#include <utils/qtcprocess.h>
46

47
48
49
50
51
#include <QBuffer>
#include <QCoreApplication>
#include <QFileInfo>
#include <QProcess>
#include <QScopedPointer>
52

53
#include <QLineEdit>
54
#include <QFormLayout>
55

hjk's avatar
hjk committed
56
57
using namespace Utils;

58
59
namespace ProjectExplorer {

60
61
using namespace Internal;

62
63
64
65
// --------------------------------------------------------------------------
// Helpers:
// --------------------------------------------------------------------------

66
static const char compilerCommandKeyC[] = "ProjectExplorer.GccToolChain.Path";
67
68
static const char compilerPlatformCodeGenFlagsKeyC[] = "ProjectExplorer.GccToolChain.PlatformCodeGenFlags";
static const char compilerPlatformLinkerFlagsKeyC[] = "ProjectExplorer.GccToolChain.PlatformLinkerFlags";
Tobias Hunger's avatar
Tobias Hunger committed
69
static const char targetAbiKeyC[] = "ProjectExplorer.GccToolChain.TargetAbi";
70
static const char supportedAbisKeyC[] = "ProjectExplorer.GccToolChain.SupportedAbis";
71

hjk's avatar
hjk committed
72
static QByteArray runGcc(const FileName &gcc, const QStringList &arguments, const QStringList &env)
73
{
74
    if (gcc.isEmpty() || !gcc.toFileInfo().isExecutable())
75
76
77
        return QByteArray();

    QProcess cpp;
78
79
    // Force locale: This function is used only to detect settings inside the tool chain, so this is save.
    QStringList environment(env);
80
    environment.append(QLatin1String("LC_ALL=C"));
81
82

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

100
101
    const QByteArray stdErr = SynchronousProcess::normalizeNewlines(
                QString::fromLocal8Bit(cpp.readAllStandardError())).toLocal8Bit();
102
103
104
    if (cpp.exitCode() != 0) {
        qWarning().nospace()
            << Q_FUNC_INFO << ": " << gcc.toUserOutput() << ' '
hjk's avatar
hjk committed
105
            << arguments.join(QLatin1Char(' ')) << " returned exit code "
106
107
108
109
            << cpp.exitCode() << ": " << stdErr;
        return QByteArray();
    }

110
111
    QByteArray data = SynchronousProcess::normalizeNewlines(
                QString::fromLocal8Bit(cpp.readAllStandardOutput())).toLocal8Bit();
112
113
    if (!data.isEmpty() && !data.endsWith('\n'))
        data.append('\n');
114
    data.append(stdErr);
115
    return data;
116
117
}

118
119
120
121
122
static const QStringList gccPredefinedMacrosOptions()
{
    return QStringList() << QLatin1String("-xc++") << QLatin1String("-E") << QLatin1String("-dM");
}

hjk's avatar
hjk committed
123
static QByteArray gccPredefinedMacros(const FileName &gcc, const QStringList &args, const QStringList &env)
124
{
125
    QStringList arguments = args;
126
    arguments << QLatin1String("-");
127
128

    QByteArray predefinedMacros = runGcc(gcc, arguments, env);
129
    // Sanity check in case we get an error message instead of real output:
130
    QTC_CHECK(predefinedMacros.isNull() || predefinedMacros.startsWith("#define "));
131
    if (HostOsInfo::isMacHost()) {
132
133
134
135
        // 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);
136
        if (idx != -1)
137
            predefinedMacros.replace(idx, blocksDefine.length(), blocksUndefine);
138

139
140
141
142
        // 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");
    }
143
144
145
    return predefinedMacros;
}

146
147
const int GccToolChain::PREDEFINED_MACROS_CACHE_SIZE = 16;

148
149
QList<HeaderPath> GccToolChain::gccHeaderPaths(const FileName &gcc, const QStringList &arguments,
                                               const QStringList &env)
150
151
152
153
154
{
    QList<HeaderPath> systemHeaderPaths;
    QByteArray line;
    QByteArray data = runGcc(gcc, arguments, env);
    QBuffer cpp(&data);
dt's avatar
dt committed
155
    cpp.open(QIODevice::ReadOnly);
156
157
158
159
160
161
162
163
164
165
166
167
    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;
168
            } else if (! line.isEmpty() && QChar(QLatin1Char(line.at(0))).isSpace()) {
169
170
171
172
173
174
175
176
177
178
179
180
181
182
                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 {
183
                qWarning("%s: Ignoring line: %s", __FUNCTION__, line.constData());
184
185
186
187
188
189
            }
        }
    }
    return systemHeaderPaths;
}

190
static QList<Abi> guessGccAbi(const QString &m, const QByteArray &macros)
191
{
hjk's avatar
hjk committed
192
    QList<Abi> abiList;
Tobias Hunger's avatar
Tobias Hunger committed
193

194
195
    Abi guessed = Abi::abiFromTargetTriplet(m);
    if (guessed.isNull())
Tobias Hunger's avatar
Tobias Hunger committed
196
        return abiList;
197

198
199
200
201
202
    Abi::Architecture arch = guessed.architecture();
    Abi::OS os = guessed.os();
    Abi::OSFlavor flavor = guessed.osFlavor();
    Abi::BinaryFormat format = guessed.binaryFormat();
    int width = guessed.wordWidth();
Tobias Hunger's avatar
Tobias Hunger committed
203

204
205
206
207
208
    if (macros.contains("#define __SIZEOF_SIZE_T__ 8"))
        width = 64;
    else if (macros.contains("#define __SIZEOF_SIZE_T__ 4"))
        width = 32;

209
    if (os == Abi::MacOS) {
Tobias Hunger's avatar
Tobias Hunger committed
210
        // Apple does PPC and x86!
hjk's avatar
hjk committed
211
212
        abiList << Abi(arch, os, flavor, format, width);
        abiList << Abi(arch, os, flavor, format, width == 64 ? 32 : 64);
Tobias Hunger's avatar
Tobias Hunger committed
213
    } else if (arch == Abi::X86Architecture && (width == 0 || width == 64)) {
214
        abiList << Abi(arch, os, flavor, format, 64);
hjk's avatar
hjk committed
215
        abiList << Abi(arch, os, flavor, format, 32);
Tobias Hunger's avatar
Tobias Hunger committed
216
    } else {
hjk's avatar
hjk committed
217
        abiList << Abi(arch, os, flavor, format, width);
Tobias Hunger's avatar
Tobias Hunger committed
218
219
    }
    return abiList;
220
221
}

222
static QList<Abi> guessGccAbi(const FileName &path, const QStringList &env,
223
                              const QByteArray &macros,
224
                              const QStringList &extraArgs = QStringList())
225
{
226
    if (path.isEmpty())
hjk's avatar
hjk committed
227
        return QList<Abi>();
228

229
230
    QStringList arguments = extraArgs;
    arguments << QLatin1String("-dumpmachine");
231
    QString machine = QString::fromLocal8Bit(runGcc(path, arguments, env)).trimmed();
232
233
    if (machine.isEmpty())
        return QList<Abi>(); // no need to continue if running failed once...
234
    return guessGccAbi(machine, macros);
235
236
}

hjk's avatar
hjk committed
237
static QString gccVersion(const FileName &path, const QStringList &env)
238
239
240
241
242
{
    QStringList arguments(QLatin1String("-dumpversion"));
    return QString::fromLocal8Bit(runGcc(path, arguments, env)).trimmed();
}

243
244
245
246
// --------------------------------------------------------------------------
// GccToolChain
// --------------------------------------------------------------------------

247
GccToolChain::GccToolChain(Detection d) :
248
    ToolChain(Constants::GCC_TOOLCHAIN_TYPEID, d)
249
250
{ }

251
252
GccToolChain::GccToolChain(Core::Id typeId, Detection d) :
    ToolChain(typeId, d)
253
254
{ }

255
256
257
258
259
260
261
262
263
264
265
266
267
void GccToolChain::setCompilerCommand(const FileName &path)
{
    if (path == m_compilerCommand)
        return;

    m_compilerCommand = path;
}

void GccToolChain::setSupportedAbis(const QList<Abi> &m_abis)
{
    m_supportedAbis = m_abis;
}

268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
void GccToolChain::setMacroCache(const QStringList &allCxxflags, const QByteArray &macros) const
{
    if (macros.isNull())
        return;

    CacheItem runResults;
    QByteArray data = macros;
    runResults.first = allCxxflags;
    if (macros.isNull())
        data = QByteArray("");
    runResults.second = data;

    m_predefinedMacros.push_back(runResults);
    if (m_predefinedMacros.size() > PREDEFINED_MACROS_CACHE_SIZE)
        m_predefinedMacros.pop_front();
}

QByteArray GccToolChain::macroCache(const QStringList &allCxxflags) const
{
    for (GccCache::iterator it = m_predefinedMacros.begin(); it != m_predefinedMacros.end(); ++it) {
        if (it->first == allCxxflags) {
            // Increase cached item priority
            CacheItem pair = *it;
            m_predefinedMacros.erase(it);
            m_predefinedMacros.push_back(pair);

            return pair.second;
        }
    }
    return QByteArray();
}

300
301
302
QString GccToolChain::defaultDisplayName() const
{
    if (!m_targetAbi.isValid())
Tobias Hunger's avatar
Tobias Hunger committed
303
        return typeDisplayName();
304
305
306
307
308
    return QCoreApplication::translate("ProjectExplorer::GccToolChain",
                                       "%1 (%2 %3 in %4)").arg(typeDisplayName(),
                                                               Abi::toString(m_targetAbi.architecture()),
                                                               Abi::toString(m_targetAbi.wordWidth()),
                                                               compilerCommand().parentDir().toUserOutput());
309
310
}

311
312
313
314
315
ToolChain::CompilerFlags GccToolChain::defaultCompilerFlags() const
{
    return CompilerFlags(GnuExtensions);
}

Tobias Hunger's avatar
Tobias Hunger committed
316
QString GccToolChain::typeDisplayName() const
317
{
318
    return GccToolChainFactory::tr("GCC");
319
320
321
322
323
324
325
}

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

326
327
328
329
330
331
332
QString GccToolChain::version() const
{
    if (m_version.isEmpty())
        m_version = detectVersion();
    return m_version;
}

Tobias Hunger's avatar
Tobias Hunger committed
333
334
void GccToolChain::setTargetAbi(const Abi &abi)
{
Tobias Hunger's avatar
Tobias Hunger committed
335
336
337
    if (abi == m_targetAbi)
        return;

338
339
    m_targetAbi = abi;
    toolChainUpdated();
Tobias Hunger's avatar
Tobias Hunger committed
340
341
342
343
344
345
346
}

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

347
348
bool GccToolChain::isValid() const
{
349
350
351
352
353
    if (m_compilerCommand.isNull())
        return false;

    QFileInfo fi = compilerCommand().toFileInfo();
    return fi.isExecutable();
354
355
}

356
357
358
359
360
361
362
363
364
/**
 * @brief Asks compiler for set of predefined macros
 * @param cxxflags - compiler flags collected from project settings
 * @return defines list, one per line, e.g. "#define __GXX_WEAK__ 1"
 *
 * @note changing compiler flags sometimes changes macros set, e.g. -fopenmp
 * adds _OPENMP macro, for full list of macro search by word "when" on this page:
 * http://gcc.gnu.org/onlinedocs/cpp/Common-Predefined-Macros.html
 */
365
QByteArray GccToolChain::predefinedMacros(const QStringList &cxxflags) const
366
{
367
    QStringList allCxxflags = m_platformCodeGenFlags + cxxflags;  // add only cxxflags is empty?
368

369
370
371
    QByteArray macros = macroCache(allCxxflags);
    if (!macros.isNull())
        return macros;
372
373
374
375

    // Using a clean environment breaks ccache/distcc/etc.
    Environment env = Environment::systemEnvironment();
    addToEnvironment(env);
376
    QStringList arguments = gccPredefinedMacrosOptions();
377
378
    for (int iArg = 0; iArg < allCxxflags.length(); ++iArg) {
        const QString &a = allCxxflags.at(iArg);
379
380
381
        if (a == QLatin1String("-arch")) {
            if (++iArg < allCxxflags.length() && !arguments.contains(a))
                arguments << a << allCxxflags.at(iArg);
Daniel Teske's avatar
Daniel Teske committed
382
383
        } else if (a == QLatin1String("--sysroot") || a == QLatin1String("-isysroot")
                   || a == QLatin1String("-D") ||a == QLatin1String("-U")) {
384
385
386
            if (++iArg < allCxxflags.length())
                arguments << a << allCxxflags.at(iArg);
        } else if (a == QLatin1String("-m128bit-long-double") || a == QLatin1String("-m32")
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
                || a == QLatin1String("-m3dnow") || a == QLatin1String("-m3dnowa")
                || a == QLatin1String("-m64") || a == QLatin1String("-m96bit-long-double")
                || a == QLatin1String("-mabm") || a == QLatin1String("-maes")
                || a.startsWith(QLatin1String("-march=")) || a == QLatin1String("-mavx")
                || a.startsWith(QLatin1String("-masm=")) || a == QLatin1String("-mcx16")
                || a == QLatin1String("-mfma") || a == QLatin1String("-mfma4")
                || a == QLatin1String("-mlwp") || a == QLatin1String("-mpclmul")
                || a == QLatin1String("-mpopcnt") || a == QLatin1String("-msse")
                || a == QLatin1String("-msse2") || a == QLatin1String("-msse2avx")
                || a == QLatin1String("-msse3") || a == QLatin1String("-msse4")
                || a == QLatin1String("-msse4.1") || a == QLatin1String("-msse4.2")
                || a == QLatin1String("-msse4a") || a == QLatin1String("-mssse3")
                || a.startsWith(QLatin1String("-mtune=")) || a == QLatin1String("-mxop")
                || a == QLatin1String("-Os") || a == QLatin1String("-O0") || a == QLatin1String("-O1")
                || a == QLatin1String("-O2") || a == QLatin1String("-O3")
                || a == QLatin1String("-ffinite-math-only") || a == QLatin1String("-fshort-double")
                || a == QLatin1String("-fshort-wchar") || a == QLatin1String("-fsignaling-nans")
                || a == QLatin1String("-fno-inline") || a == QLatin1String("-fno-exceptions")
                || a == QLatin1String("-fstack-protector") || a == QLatin1String("-fstack-protector-all")
                || a == QLatin1String("-fsanitize=address") || a == QLatin1String("-fno-rtti")
                || a.startsWith(QLatin1String("-std=")) || a.startsWith(QLatin1String("-stdlib="))
                || a.startsWith(QLatin1String("-specs="))
                || a == QLatin1String("-ansi") || a == QLatin1String("-undef")
                || a.startsWith(QLatin1String("-D")) || a.startsWith(QLatin1String("-U"))
411
412
413
                || a == QLatin1String("-fopenmp") || a == QLatin1String("-Wno-deprecated")
                || a == QLatin1String("-fPIC") || a == QLatin1String("-fpic")
                || a == QLatin1String("-fPIE") || a == QLatin1String("-fpie"))
414
415
            arguments << a;
    }
416
417
    macros = gccPredefinedMacros(m_compilerCommand, reinterpretOptions(arguments),
                                 env.toStringList());
418

419
420
    setMacroCache(allCxxflags, macros);
    return macros;
421
422
}

423
424
425
426
/**
 * @brief Parses gcc flags -std=*, -fopenmp, -fms-extensions, -ansi.
 * @see http://gcc.gnu.org/onlinedocs/gcc/C-Dialect-Options.html
 */
hjk's avatar
hjk committed
427
ToolChain::CompilerFlags GccToolChain::compilerFlags(const QStringList &cxxflags) const
428
{
429
430
431
432
433
    CompilerFlags flags = defaultCompilerFlags();

    const QStringList allCxxflags = m_platformCodeGenFlags + cxxflags; // add only cxxflags is empty?
    foreach (const QString &flag, allCxxflags) {
        if (flag.startsWith(QLatin1String("-std="))) {
434
            const QByteArray std = flag.mid(5).toLatin1();
435
            if (std == "c++98" || std == "c++03") {
436
                flags &= ~CompilerFlags(StandardCxx11 | StandardCxx14 | StandardCxx17 | GnuExtensions);
437
                flags |= StandardCxx98;
438
            } else if (std == "gnu++98" || std == "gnu++03") {
439
                flags &= ~CompilerFlags(StandardCxx11 | StandardCxx14 | StandardCxx17);
440
                flags |= GnuExtensions;
441
            } else if (std == "c++0x" || std == "c++11") {
442
                flags |= StandardCxx11;
443
                flags &= ~CompilerFlags(StandardCxx14 | StandardCxx17 | GnuExtensions);
444
445
            } else if (std == "c++14" || std == "c++1y") {
                flags |= StandardCxx14;
446
                flags &= ~CompilerFlags(StandardCxx11 | StandardCxx17 | GnuExtensions);
447
448
            } else if (std == "c++17" || std == "c++1z") {
                flags |= StandardCxx17;
449
                flags &= ~CompilerFlags(StandardCxx11 | StandardCxx14 | GnuExtensions);
450
451
            } else if (std == "gnu++0x" || std == "gnu++11" || std== "gnu++1y") {
                flags |= CompilerFlags(StandardCxx11 | GnuExtensions);
452
                flags &= ~CompilerFlags(StandardCxx14 | StandardCxx17);
453
            } else if (std == "c89" || std == "c90"
454
                       || std == "iso9899:1990" || std == "iso9899:199409") {
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
                flags &= ~CompilerFlags(StandardC99 | StandardC11);
            } else if (std == "gnu89" || std == "gnu90") {
                flags &= ~CompilerFlags(StandardC99 | StandardC11);
                flags |= GnuExtensions;
            } else if (std == "c99" || std == "c9x"
                       || std == "iso9899:1999" || std == "iso9899:199x") {
                flags |= StandardC99;
                flags &= ~StandardC11;
            } else if (std == "gnu99" || std == "gnu9x") {
                flags |= CompilerFlags(StandardC99 | GnuExtensions);
                flags &= ~StandardC11;
            } else if (std == "c11" || std == "c1x" || std == "iso9899:2011") {
                flags |= CompilerFlags(StandardC99 | StandardC11);
            } else if (std == "gnu11" || std == "gnu1x") {
                flags |= CompilerFlags(StandardC99 | StandardC11 | GnuExtensions);
            }
        } else if (flag == QLatin1String("-fopenmp")) {
            flags |= OpenMP;
        } else if (flag == QLatin1String("-fms-extensions")) {
            flags |= MicrosoftExtensions;
        } else if (flag == QLatin1String("-ansi")) {
            flags &= ~CompilerFlags(StandardCxx11 | GnuExtensions
                                    | StandardC99 | StandardC11);
        }
    }

    return flags;
482
483
}

484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
GccToolChain::WarningFlags GccToolChain::warningFlags(const QStringList &cflags) const
{
    // based on 'LC_ALL="en" gcc -Q --help=warnings | grep enabled'
    WarningFlags flags(WarnDeprecated | WarnIgnoredQualfiers
                       | WarnSignedComparison | WarnUninitializedVars);
    WarningFlags groupWall(WarningsAll | WarnUnknownPragma |WarnUnusedFunctions
                           | WarnUnusedLocals | WarnUnusedResult | WarnUnusedValue
                           | WarnSignedComparison | WarnUninitializedVars);
    WarningFlags groupWextra(WarningsExtra | WarnIgnoredQualfiers | WarnUnusedParams);

    foreach (const QString &flag, cflags) {
        if (flag == QLatin1String("--all-warnings"))
            flags |= groupWall;
        else if (flag == QLatin1String("--extra-warnings"))
            flags |= groupWextra;

        WarningFlagAdder add(flag, flags);
        if (add.triggered())
            continue;

        // supported by clang too
        add("error", WarningsAsErrors);
        add("all", groupWall);
        add("extra", groupWextra);
        add("deprecated", WarnDeprecated);
        add("effc++", WarnEffectiveCxx);
        add("ignored-qualifiers", WarnIgnoredQualfiers);
        add("non-virtual-dtor", WarnNonVirtualDestructor);
        add("overloaded-virtual", WarnOverloadedVirtual);
        add("shadow", WarnHiddenLocals);
        add("sign-compare", WarnSignedComparison);
        add("unknown-pragmas", WarnUnknownPragma);
        add("unused", ToolChain::WarningFlag(
                WarnUnusedFunctions | WarnUnusedLocals | WarnUnusedParams
                | WarnUnusedResult | WarnUnusedValue));
        add("unused-function", WarnUnusedFunctions);
        add("unused-variable", WarnUnusedLocals);
        add("unused-parameter", WarnUnusedParams);
        add("unused-result", WarnUnusedResult);
        add("unused-value", WarnUnusedValue);
        add("uninitialized", WarnUninitializedVars);
    }
    return flags;
}

529
QList<HeaderPath> GccToolChain::systemHeaderPaths(const QStringList &cxxflags, const FileName &sysRoot) const
530
{
531
    if (m_headerPaths.isEmpty()) {
532
        // Using a clean environment breaks ccache/distcc/etc.
hjk's avatar
hjk committed
533
        Environment env = Environment::systemEnvironment();
534
        addToEnvironment(env);
535
536
537
538
539
540
541
542
543
544
545
546
        // Prepare arguments
        QStringList arguments;
        if (!sysRoot.isEmpty())
            arguments.append(QString::fromLatin1("--sysroot=%1").arg(sysRoot.toString()));

        QStringList flags;
        flags << m_platformCodeGenFlags << cxxflags;
        foreach (const QString &a, flags) {
            if (a.startsWith(QLatin1String("-stdlib=")))
                arguments << a;
        }

Tobias Hunger's avatar
Tobias Hunger committed
547
548
549
550
        arguments << QLatin1String("-xc++")
                  << QLatin1String("-E")
                  << QLatin1String("-v")
                  << QLatin1String("-");
551
552

        m_headerPaths = gccHeaderPaths(m_compilerCommand, reinterpretOptions(arguments), env.toStringList());
553
    }
554
    return m_headerPaths;
555
556
}

557
558
559
560
561
562
void GccToolChain::addCommandPathToEnvironment(const FileName &command, Environment &env)
{
    if (!command.isEmpty())
        env.prependOrSetPath(command.parentDir().toString());
}

hjk's avatar
hjk committed
563
void GccToolChain::addToEnvironment(Environment &env) const
564
{
565
    addCommandPathToEnvironment(m_compilerCommand, env);
566
567
}

hjk's avatar
hjk committed
568
QList<FileName> GccToolChain::suggestedMkspecList() const
569
570
{
    Abi abi = targetAbi();
571
572
573
574
575
576
    Abi host = Abi::hostAbi();

    // Cross compile: Leave the mkspec alone!
    if (abi.architecture() != host.architecture()
            || abi.os() != host.os()
            || abi.osFlavor() != host.osFlavor()) // Note: This can fail:-(
hjk's avatar
hjk committed
577
        return QList<FileName>();
578

579
580
581
    if (abi.os() == Abi::MacOS) {
        QString v = version();
        // prefer versioned g++ on mac. This is required to enable building for older Mac OS versions
582
        if (v.startsWith(QLatin1String("4.0")) && m_compilerCommand.endsWith(QLatin1String("-4.0")))
583
            return QList<FileName>() << FileName::fromLatin1("macx-g++40");
584
        if (v.startsWith(QLatin1String("4.2")) && m_compilerCommand.endsWith(QLatin1String("-4.2")))
585
586
            return QList<FileName>() << FileName::fromLatin1("macx-g++42");
        return QList<FileName>() << FileName::fromLatin1("macx-g++");
587
588
589
590
    }

    if (abi.os() == Abi::LinuxOS) {
        if (abi.osFlavor() != Abi::GenericLinuxFlavor)
hjk's avatar
hjk committed
591
            return QList<FileName>(); // most likely not a desktop, so leave the mkspec alone.
592
593
594
        if (abi.wordWidth() == host.wordWidth()) {
            // no need to explicitly set the word width, but provide that mkspec anyway to make sure
            // that the correct compiler is picked if a mkspec with a wordwidth is given.
595
            return QList<FileName>() << FileName::fromLatin1("linux-g++")
hjk's avatar
hjk committed
596
                                            << FileName::fromString(QLatin1String("linux-g++-") + QString::number(m_targetAbi.wordWidth()));
597
        }
hjk's avatar
hjk committed
598
        return QList<FileName>() << FileName::fromString(QLatin1String("linux-g++-") + QString::number(m_targetAbi.wordWidth()));
599
    }
600

601
    if (abi.os() == Abi::BsdOS && abi.osFlavor() == Abi::FreeBsdFlavor)
602
        return QList<FileName>() << FileName::fromLatin1("freebsd-g++");
603

hjk's avatar
hjk committed
604
    return QList<FileName>();
605
606
}

607
QString GccToolChain::makeCommand(const Environment &environment) const
608
{
609
    QString make = QLatin1String("make");
610
611
    FileName tmp = environment.searchInPath(make);
    return tmp.isEmpty() ? make : tmp.toString();
612
613
614
615
616
617
618
}

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

619
void GccToolChain::resetToolChain(const FileName &path)
620
{
621
    if (path == m_compilerCommand)
622
623
        return;

624
625
    bool resetDisplayName = displayName() == defaultDisplayName();

626
    setCompilerCommand(path);
627

628
    Abi currentAbi = m_targetAbi;
629
    m_supportedAbis = detectSupportedAbis();
630

Tobias Hunger's avatar
Tobias Hunger committed
631
    m_targetAbi = Abi();
632
633
634
635
636
    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
637
    }
638
639
640
641
642

    if (resetDisplayName)
        setDisplayName(defaultDisplayName()); // calls toolChainUpdated()!
    else
        toolChainUpdated();
643
644
}

hjk's avatar
hjk committed
645
FileName GccToolChain::compilerCommand() const
646
{
647
    return m_compilerCommand;
648
649
}

650
651
652
653
654
655
656
657
658
void GccToolChain::setPlatformCodeGenFlags(const QStringList &flags)
{
    if (flags != m_platformCodeGenFlags) {
        m_platformCodeGenFlags = flags;
        toolChainUpdated();
    }
}

/*!
659
    Code gen flags that have to be passed to the compiler.
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
 */
QStringList GccToolChain::platformCodeGenFlags() const
{
    return m_platformCodeGenFlags;
}

void GccToolChain::setPlatformLinkerFlags(const QStringList &flags)
{
    if (flags != m_platformLinkerFlags) {
        m_platformLinkerFlags = flags;
        toolChainUpdated();
    }
}

/*!
675
    Flags that have to be passed to the linker.
676

677
    For example: \c{-arch armv7}
678
679
680
681
682
683
 */
QStringList GccToolChain::platformLinkerFlags() const
{
    return m_platformLinkerFlags;
}

684
685
686
687
688
689
690
691
ToolChain *GccToolChain::clone() const
{
    return new GccToolChain(*this);
}

QVariantMap GccToolChain::toMap() const
{
    QVariantMap data = ToolChain::toMap();
692
    data.insert(QLatin1String(compilerCommandKeyC), m_compilerCommand.toString());
693
694
    data.insert(QLatin1String(compilerPlatformCodeGenFlagsKeyC), m_platformCodeGenFlags);
    data.insert(QLatin1String(compilerPlatformLinkerFlagsKeyC), m_platformLinkerFlags);
Tobias Hunger's avatar
Tobias Hunger committed
695
    data.insert(QLatin1String(targetAbiKeyC), m_targetAbi.toString());
696
    QStringList abiList = Utils::transform(m_supportedAbis, &Abi::toString);
697
    data.insert(QLatin1String(supportedAbisKeyC), abiList);
698
699
700
701
702
703
704
705
    return data;
}

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

hjk's avatar
hjk committed
706
    m_compilerCommand = FileName::fromString(data.value(QLatin1String(compilerCommandKeyC)).toString());
707
708
    m_platformCodeGenFlags = data.value(QLatin1String(compilerPlatformCodeGenFlagsKeyC)).toStringList();
    m_platformLinkerFlags = data.value(QLatin1String(compilerPlatformLinkerFlagsKeyC)).toStringList();
Tobias Hunger's avatar
Tobias Hunger committed
709
    m_targetAbi = Abi(data.value(QLatin1String(targetAbiKeyC)).toString());
710
711
712
    QStringList abiList = data.value(QLatin1String(supportedAbisKeyC)).toStringList();
    m_supportedAbis.clear();
    foreach (const QString &a, abiList) {
hjk's avatar
hjk committed
713
        Abi abi(a);
714
715
716
717
        if (!abi.isValid())
            continue;
        m_supportedAbis.append(abi);
    }
718
719
720
721
722
723
724
725
726
    return true;
}

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

    const GccToolChain *gccTc = static_cast<const GccToolChain *>(&other);
727
728
729
    return m_compilerCommand == gccTc->m_compilerCommand && m_targetAbi == gccTc->m_targetAbi
            && m_platformCodeGenFlags == gccTc->m_platformCodeGenFlags
            && m_platformLinkerFlags == gccTc->m_platformLinkerFlags;
730
731
732
733
}

ToolChainConfigWidget *GccToolChain::configurationWidget()
{
734
    return new GccToolChainConfigWidget(this);
735
736
}

Tobias Hunger's avatar
Tobias Hunger committed
737
738
void GccToolChain::updateSupportedAbis() const
{
739
740
741
742
743
744
    if (m_supportedAbis.isEmpty())
        m_supportedAbis = detectSupportedAbis();
}

QList<Abi> GccToolChain::detectSupportedAbis() const
{
hjk's avatar
hjk committed
745
    Environment env = Environment::systemEnvironment();
746
    addToEnvironment(env);
747
748
    QByteArray macros = predefinedMacros(QStringList());
    return guessGccAbi(m_compilerCommand, env.toStringList(), macros, platformCodeGenFlags());
Tobias Hunger's avatar
Tobias Hunger committed
749
750
}

751
752
QString GccToolChain::detectVersion() const
{
hjk's avatar
hjk committed
753
    Environment env = Environment::systemEnvironment();
754
    addToEnvironment(env);
755
    return gccVersion(m_compilerCommand, env.toStringList());
756
757
}

758
759
760
761
// --------------------------------------------------------------------------
// GccToolChainFactory
// --------------------------------------------------------------------------

762
GccToolChainFactory::GccToolChainFactory()
763
{
764
    setTypeId(Constants::GCC_TOOLCHAIN_TYPEID);
765
    setDisplayName(tr("GCC"));
766
767
}

768
bool GccToolChainFactory::canCreate()
769
770
771
772
{
    return true;
}

773
ToolChain *GccToolChainFactory::create()
774
775
776
777
{
    return createToolChain(false);
}

778
QList<ToolChain *> GccToolChainFactory::autoDetect()
779
{
Tobias Hunger's avatar
Tobias Hunger committed
780
    QList<ToolChain *> tcs;
781
    if (HostOsInfo::isMacHost()) {
782
783
784
785
        // Old mac compilers needed to support macx-gccXY mkspecs:
        tcs.append(autoDetectToolchains(QLatin1String("g++-4.0"), Abi::hostAbi()));
        tcs.append(autoDetectToolchains(QLatin1String("g++-4.2"), Abi::hostAbi()));
    }
Tobias Hunger's avatar
Tobias Hunger committed
786
    tcs.append(autoDetectToolchains(QLatin1String("g++"), Abi::hostAbi()));
787
788

    return tcs;
789
790
}

791
// Used by the ToolChainManager to restore user-generated tool chains
792
bool GccToolChainFactory::canRestore(const QVariantMap &data)
793
{
794
    return typeIdFromMap(data) == Constants::GCC_TOOLCHAIN_TYPEID;
795
796
}

797
ToolChain *GccToolChainFactory::restore(const QVariantMap &data)
798
{
799
800
    GccToolChain *tc = createToolChain(false);
    if (tc->fromMap(data))
801
802
803
804
805
806
        return tc;

    delete tc;
    return 0;
}

807
GccToolChain *GccToolChainFactory::createToolChain(bool autoDetect)
808
{
809
    return new GccToolChain(autoDetect ? ToolChain::AutoDetection : ToolChain::ManualDetection);
810
811
}

812
QList<ToolChain *> GccToolChainFactory::autoDetectToolchains(const QString &compiler,
813
                                                                       const Abi &requiredAbi)
814
815
816
{
    QList<ToolChain *> result;

817
    Environment systemEnvironment = Environment::systemEnvironment();
818
    const FileName compilerPath = systemEnvironment.searchInPath(compiler);
819
    if (compilerPath.isEmpty())
820
        return result;
Tobias Hunger's avatar
Tobias Hunger committed
821

822
    GccToolChain::addCommandPathToEnvironment(compilerPath, systemEnvironment);
823
824
825
    QByteArray macros
            = gccPredefinedMacros(compilerPath, gccPredefinedMacrosOptions(), systemEnvironment.toStringList());
    QList<Abi> abiList = guessGccAbi(compilerPath, systemEnvironment.toStringList(), macros);
826
827
828
829
830
831
    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
832
833
834

    foreach (const Abi &abi, abiList) {
        QScopedPointer<GccToolChain> tc(createToolChain(true));
835
        tc->setMacroCache(QStringList(), macros);
Tobias Hunger's avatar
Tobias Hunger committed
836
837
        if (tc.isNull())
            return result;
838

839
        tc->setCompilerCommand(compilerPath);
840
        tc->setSupportedAbis(abiList);
Tobias Hunger's avatar
Tobias Hunger committed
841
        tc->setTargetAbi(abi);
842
        tc->setDisplayName(tc->defaultDisplayName()); // reset displayname
843
844

        result.append(tc.take());
Tobias Hunger's avatar
Tobias Hunger committed
845
    }
846
847
848
849
850
851
852
853

    return result;
}

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

854
GccToolChainConfigWidget::GccToolChainConfigWidget(GccToolChain *tc) :
855
    ToolChainConfigWidget(tc),
hjk's avatar
hjk committed
856
    m_compilerCommand(new PathChooser),
857
858
    m_abiWidget(new AbiWidget),
    m_isReadOnly(false)
859
860
861
{
    Q_ASSERT(tc);

862
    const QStringList gnuVersionArgs = QStringList(QLatin1String("--version"));
hjk's avatar
hjk committed
863
    m_compilerCommand->setExpectedKind(PathChooser::ExistingCommand);
864
    m_compilerCommand->setCommandVersionArguments(gnuVersionArgs);
865
    m_compilerCommand->setHistoryCompleter(QLatin1String("PE.Gcc.Command.History"));
866
    m_mainLayout->addRow(tr("&Compiler path:"), m_compilerCommand);
867
868
869
870
871
872
    m_platformCodeGenFlagsLineEdit = new QLineEdit(this);
    m_platformCodeGenFlagsLineEdit->setText(QtcProcess::joinArgs(tc->platformCodeGenFlags()));
    m_mainLayout->addRow(tr("Platform codegen flags:"), m_platformCodeGenFlagsLineEdit);
    m_platformLinkerFlagsLineEdit = new QLineEdit(this);
    m_platformLinkerFlagsLineEdit->setText(QtcProcess::joinArgs(tc->platformLinkerFlags()));
    m_mainLayout->addRow(tr("Platform linker flags:"), m_platformLinkerFlagsLineEdit);
873
    m_mainLayout->addRow(tr("&ABI:"), m_abiWidget);
874

Tobias Hunger's avatar
Tobias Hunger committed
875
    m_abiWidget->setEnabled(false);
876
    addErrorLabel();
877

878
    setFromToolchain();
879

880
    connect(m_compilerCommand, SIGNAL(rawPathChanged(QString)), this, SLOT(handleCompilerCommandChange()));
881
882
    connect(m_platformCodeGenFlagsLineEdit, SIGNAL(editingFinished()), this, SLOT(handlePlatformCodeGenFlagsChange()));
    connect(m_platformLinkerFlagsLineEdit, SIGNAL(editingFinished()), this, SLOT(handlePlatformLinkerFlagsChange()));
Tobias Hunger's avatar
Tobias Hunger committed
883
    connect(m_abiWidget, SIGNAL(abiChanged()), this, SIGNAL(dirty()));
884
885
}

886
void GccToolChainConfigWidget::applyImpl()
887
888
889
890
891
892
893
{
    if (toolChain()->isAutoDetected())
        return;

    GccToolChain *tc = static_cast<GccToolChain *>(toolChain());
    Q_ASSERT(tc);
    QString displayName = tc->displayName();
894
    tc->setCompilerCommand(m_compilerCommand->fileName());
895
    tc->setSupportedAbis(m_abiWidget->supportedAbis());
896
    tc->setTargetAbi(m_abiWidget->currentAbi());
897
    tc->setDisplayName(displayName); // reset display name
898
899
    tc->setPlatformCodeGenFlags(splitString(m_platformCodeGenFlagsLineEdit->text()));
    tc->setPlatformLinkerFlags(splitString(m_platformLinkerFlagsLineEdit->text()));
900
    tc->setMacroCache(tc->platformCodeGenFlags(), m_macros);
901
902
}

903
void GccToolChainConfigWidget::setFromToolchain()
904
{
905
906
    // subwidgets are not yet connected!
    bool blocked = blockSignals(true);
907
    GccToolChain *tc = static_cast<GccToolChain *>(toolChain());
908
    m_compilerCommand->setFileName(tc->compilerCommand());
909
910
    m_platformCodeGenFlagsLineEdit->setText(QtcProcess::joinArgs(tc->platformCodeGenFlags()));
    m_platformLinkerFlagsLineEdit->setText(QtcProcess::joinArgs(tc->platformLinkerFlags()));
Tobias Hunger's avatar
Tobias Hunger committed
911
    m_abiWidget->setAbis(tc->supportedAbis(), tc->targetAbi());
912
    if (!m_isReadOnly && !m_compilerCommand->path().isEmpty())
913
        m_abiWidget->setEnabled(true);
914
    blockSignals(blocked);
915
916
}