gcctoolchain.cpp 56.6 KB
Newer Older
hjk's avatar
hjk committed
1
/****************************************************************************
2
**
3
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
hjk's avatar
hjk committed
4
** Contact: http://www.qt-project.org/legal
5
**
hjk's avatar
hjk committed
6
** This file is part of Qt Creator.
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.
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
26
27
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
hjk's avatar
hjk committed
28
****************************************************************************/
29
30

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

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

45
46
47
48
49
#include <QBuffer>
#include <QCoreApplication>
#include <QFileInfo>
#include <QProcess>
#include <QScopedPointer>
50

51
#include <QLineEdit>
52
#include <QFormLayout>
53

hjk's avatar
hjk committed
54
55
using namespace Utils;

56
57
namespace ProjectExplorer {

58
59
using namespace Internal;

60
61
62
63
// --------------------------------------------------------------------------
// Helpers:
// --------------------------------------------------------------------------

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

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

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

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

98
99
100
101
102
103
104
105
106
    const QByteArray stdErr = cpp.readAllStandardError();
    if (cpp.exitCode() != 0) {
        qWarning().nospace()
            << Q_FUNC_INFO << ": " << gcc.toUserOutput() << ' '
            << arguments.join(QLatin1String(" ")) << " returned exit code "
            << cpp.exitCode() << ": " << stdErr;
        return QByteArray();
    }

107
108
109
    QByteArray data = cpp.readAllStandardOutput();
    if (!data.isEmpty() && !data.endsWith('\n'))
        data.append('\n');
110
    data.append(stdErr);
111
    return data;
112
113
}

114
115
116
117
118
static const QStringList gccPredefinedMacrosOptions()
{
    return QStringList() << QLatin1String("-xc++") << QLatin1String("-E") << QLatin1String("-dM");
}

hjk's avatar
hjk committed
119
static QByteArray gccPredefinedMacros(const FileName &gcc, const QStringList &args, const QStringList &env)
120
{
121
    QStringList arguments = args;
122
    arguments << QLatin1String("-");
123
124

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

135
136
137
138
        // 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");
    }
139
140
141
    return predefinedMacros;
}

142
143
const int GccToolChain::PREDEFINED_MACROS_CACHE_SIZE = 16;

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

186
static QList<Abi> guessGccAbi(const QString &m, const QByteArray &macros)
187
{
hjk's avatar
hjk committed
188
    QList<Abi> abiList;
Tobias Hunger's avatar
Tobias Hunger committed
189

190
191
    Abi guessed = Abi::abiFromTargetTriplet(m);
    if (guessed.isNull())
Tobias Hunger's avatar
Tobias Hunger committed
192
        return abiList;
193

194
195
196
197
198
    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
199

200
201
202
203
204
    if (macros.contains("#define __SIZEOF_SIZE_T__ 8"))
        width = 64;
    else if (macros.contains("#define __SIZEOF_SIZE_T__ 4"))
        width = 32;

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

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

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

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

241
242
243
244
// --------------------------------------------------------------------------
// GccToolChain
// --------------------------------------------------------------------------

245
246
GccToolChain::GccToolChain(Detection d) :
    ToolChain(QLatin1String(Constants::GCC_TOOLCHAIN_ID), d)
247
248
{ }

249
250
GccToolChain::GccToolChain(const QString &id, Detection d) :
    ToolChain(id, d)
251
252
253
254
{ }

GccToolChain::GccToolChain(const GccToolChain &tc) :
    ToolChain(tc),
255
    m_predefinedMacros(tc.m_predefinedMacros),
256
    m_compilerCommand(tc.compilerCommand()),
257
258
    m_targetAbi(tc.m_targetAbi),
    m_supportedAbis(tc.m_supportedAbis),
259
    m_headerPaths(tc.m_headerPaths),
260
261
    m_version(tc.m_version)
{ }
262

263
264
265
266
267
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
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();
}

295
296
297
QString GccToolChain::defaultDisplayName() const
{
    if (!m_targetAbi.isValid())
Tobias Hunger's avatar
Tobias Hunger committed
298
        return typeDisplayName();
299
300
301
302
303
    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());
304
305
}

306
307
308
309
310
ToolChain::CompilerFlags GccToolChain::defaultCompilerFlags() const
{
    return CompilerFlags(GnuExtensions);
}

Tobias Hunger's avatar
Tobias Hunger committed
311
312
313
314
315
316
QString GccToolChain::type() const
{
    return QLatin1String("gcc");
}

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
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
    QStringList arguments = gccPredefinedMacrosOptions();
    foreach (const QString &a, allCxxflags) {
        if (a == QLatin1String("-m128bit-long-double") || a == QLatin1String("-m32")
                || 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"))
                || a == QLatin1String("-fopenmp") || a == QLatin1String("-Wno-deprecated"))
            arguments << a;
    }
406
407
    macros = gccPredefinedMacros(m_compilerCommand, reinterpretOptions(arguments),
                                 env.toStringList());
408

409
410
    setMacroCache(allCxxflags, macros);
    return macros;
411
412
}

413
414
415
416
/**
 * @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
417
ToolChain::CompilerFlags GccToolChain::compilerFlags(const QStringList &cxxflags) const
418
{
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
    CompilerFlags flags = defaultCompilerFlags();

    const QStringList allCxxflags = m_platformCodeGenFlags + cxxflags; // add only cxxflags is empty?
    foreach (const QString &flag, allCxxflags) {
        if (flag.startsWith(QLatin1String("-std="))) {
            const QByteArray std = flag.mid(5).toAscii();
            if (std == "c++98" || std == "c++03") {
                flags &= ~CompilerFlags(StandardCxx11 | GnuExtensions);
            } else if (std == "gnu++98" || std == "gnu++03") {
                flags &= ~StandardCxx11;
                flags |= GnuExtensions;
            } else if (std == "c++0x" || std == "c++11" || std== "c++1y") {
                flags |= StandardCxx11;
                flags &= ~GnuExtensions;
            } else if (std == "gnu++0x" || std == "gnu++11" || std== "gnu++1y") {
                flags |= CompilerFlags(StandardCxx11 | GnuExtensions);
            } else if (std == "c89" || std == "c90"
436
                       || std == "iso9899:1990" || std == "iso9899:199409") {
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
                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;
464
465
}

466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
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
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;
}

511
QList<HeaderPath> GccToolChain::systemHeaderPaths(const QStringList &cxxflags, const Utils::FileName &sysRoot) const
512
{
513
    if (m_headerPaths.isEmpty()) {
514
        // Using a clean environment breaks ccache/distcc/etc.
hjk's avatar
hjk committed
515
        Environment env = Environment::systemEnvironment();
516
        addToEnvironment(env);
517
518
519
520
521
522
523
524
525
526
527
528
        // 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
529
530
531
532
        arguments << QLatin1String("-xc++")
                  << QLatin1String("-E")
                  << QLatin1String("-v")
                  << QLatin1String("-");
533
534

        m_headerPaths = gccHeaderPaths(m_compilerCommand, reinterpretOptions(arguments), env.toStringList());
535
    }
536
    return m_headerPaths;
537
538
}

539
540
541
542
543
544
void GccToolChain::addCommandPathToEnvironment(const FileName &command, Environment &env)
{
    if (!command.isEmpty())
        env.prependOrSetPath(command.parentDir().toString());
}

hjk's avatar
hjk committed
545
void GccToolChain::addToEnvironment(Environment &env) const
546
{
547
    addCommandPathToEnvironment(m_compilerCommand, env);
548
549
}

hjk's avatar
hjk committed
550
QList<FileName> GccToolChain::suggestedMkspecList() const
551
552
{
    Abi abi = targetAbi();
553
554
555
556
557
558
    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
559
        return QList<FileName>();
560

561
562
563
    if (abi.os() == Abi::MacOS) {
        QString v = version();
        // prefer versioned g++ on mac. This is required to enable building for older Mac OS versions
564
        if (v.startsWith(QLatin1String("4.0")) && m_compilerCommand.endsWith(QLatin1String("-4.0")))
hjk's avatar
hjk committed
565
            return QList<FileName>() << FileName::fromString(QLatin1String("macx-g++40"));
566
        if (v.startsWith(QLatin1String("4.2")) && m_compilerCommand.endsWith(QLatin1String("-4.2")))
hjk's avatar
hjk committed
567
568
            return QList<FileName>() << FileName::fromString(QLatin1String("macx-g++42"));
        return QList<FileName>() << FileName::fromString(QLatin1String("macx-g++"));
569
570
571
572
    }

    if (abi.os() == Abi::LinuxOS) {
        if (abi.osFlavor() != Abi::GenericLinuxFlavor)
hjk's avatar
hjk committed
573
            return QList<FileName>(); // most likely not a desktop, so leave the mkspec alone.
574
575
576
        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.
hjk's avatar
hjk committed
577
578
            return QList<FileName>() << FileName::fromString(QLatin1String("linux-g++"))
                                            << FileName::fromString(QLatin1String("linux-g++-") + QString::number(m_targetAbi.wordWidth()));
579
        }
hjk's avatar
hjk committed
580
        return QList<FileName>() << FileName::fromString(QLatin1String("linux-g++-") + QString::number(m_targetAbi.wordWidth()));
581
    }
582

583
    if (abi.os() == Abi::BsdOS && abi.osFlavor() == Abi::FreeBsdFlavor)
hjk's avatar
hjk committed
584
        return QList<FileName>() << FileName::fromString(QLatin1String("freebsd-g++"));
585

hjk's avatar
hjk committed
586
    return QList<FileName>();
587
588
}

589
QString GccToolChain::makeCommand(const Utils::Environment &environment) const
590
{
591
592
593
    QString make = QLatin1String("make");
    QString tmp = environment.searchInPath(make);
    return tmp.isEmpty() ? make : tmp;
594
595
596
597
598
599
600
}

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

hjk's avatar
hjk committed
601
void GccToolChain::setCompilerCommand(const FileName &path)
602
{
603
    if (path == m_compilerCommand)
604
605
        return;

606
607
    bool resetDisplayName = displayName() == defaultDisplayName();

608
    m_compilerCommand = path;
609

610
    Abi currentAbi = m_targetAbi;
611
    m_supportedAbis = detectSupportedAbis();
612

Tobias Hunger's avatar
Tobias Hunger committed
613
    m_targetAbi = Abi();
614
615
616
617
618
    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
619
    }
620
621
622
623
624

    if (resetDisplayName)
        setDisplayName(defaultDisplayName()); // calls toolChainUpdated()!
    else
        toolChainUpdated();
625
626
}

hjk's avatar
hjk committed
627
FileName GccToolChain::compilerCommand() const
628
{
629
    return m_compilerCommand;
630
631
}

632
633
634
635
636
637
638
639
640
void GccToolChain::setPlatformCodeGenFlags(const QStringList &flags)
{
    if (flags != m_platformCodeGenFlags) {
        m_platformCodeGenFlags = flags;
        toolChainUpdated();
    }
}

/*!
641
    Code gen flags that have to be passed to the compiler.
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
 */
QStringList GccToolChain::platformCodeGenFlags() const
{
    return m_platformCodeGenFlags;
}

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

/*!
657
    Flags that have to be passed to the linker.
658

659
    For example: \c{-arch armv7}
660
661
662
663
664
665
 */
QStringList GccToolChain::platformLinkerFlags() const
{
    return m_platformLinkerFlags;
}

666
667
668
669
670
671
672
673
ToolChain *GccToolChain::clone() const
{
    return new GccToolChain(*this);
}

QVariantMap GccToolChain::toMap() const
{
    QVariantMap data = ToolChain::toMap();
674
    data.insert(QLatin1String(compilerCommandKeyC), m_compilerCommand.toString());
675
676
    data.insert(QLatin1String(compilerPlatformCodeGenFlagsKeyC), m_platformCodeGenFlags);
    data.insert(QLatin1String(compilerPlatformLinkerFlagsKeyC), m_platformLinkerFlags);
Tobias Hunger's avatar
Tobias Hunger committed
677
    data.insert(QLatin1String(targetAbiKeyC), m_targetAbi.toString());
678
    QStringList abiList;
hjk's avatar
hjk committed
679
    foreach (const Abi &a, m_supportedAbis)
680
681
        abiList.append(a.toString());
    data.insert(QLatin1String(supportedAbisKeyC), abiList);
682
683
684
685
686
687
688
689
    return data;
}

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

hjk's avatar
hjk committed
690
    m_compilerCommand = FileName::fromString(data.value(QLatin1String(compilerCommandKeyC)).toString());
691
692
    m_platformCodeGenFlags = data.value(QLatin1String(compilerPlatformCodeGenFlagsKeyC)).toStringList();
    m_platformLinkerFlags = data.value(QLatin1String(compilerPlatformLinkerFlagsKeyC)).toStringList();
Tobias Hunger's avatar
Tobias Hunger committed
693
    m_targetAbi = Abi(data.value(QLatin1String(targetAbiKeyC)).toString());
694
695
696
    QStringList abiList = data.value(QLatin1String(supportedAbisKeyC)).toStringList();
    m_supportedAbis.clear();
    foreach (const QString &a, abiList) {
hjk's avatar
hjk committed
697
        Abi abi(a);
698
699
700
701
        if (!abi.isValid())
            continue;
        m_supportedAbis.append(abi);
    }
702
703
704
705
706
707
708
709
710
    return true;
}

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

    const GccToolChain *gccTc = static_cast<const GccToolChain *>(&other);
711
712
713
    return m_compilerCommand == gccTc->m_compilerCommand && m_targetAbi == gccTc->m_targetAbi
            && m_platformCodeGenFlags == gccTc->m_platformCodeGenFlags
            && m_platformLinkerFlags == gccTc->m_platformLinkerFlags;
714
715
716
717
}

ToolChainConfigWidget *GccToolChain::configurationWidget()
{
718
    return new GccToolChainConfigWidget(this);
719
720
}

Tobias Hunger's avatar
Tobias Hunger committed
721
722
void GccToolChain::updateSupportedAbis() const
{
723
724
725
726
727
728
    if (m_supportedAbis.isEmpty())
        m_supportedAbis = detectSupportedAbis();
}

QList<Abi> GccToolChain::detectSupportedAbis() const
{
hjk's avatar
hjk committed
729
    Environment env = Environment::systemEnvironment();
730
    addToEnvironment(env);
731
732
    QByteArray macros = predefinedMacros(QStringList());
    return guessGccAbi(m_compilerCommand, env.toStringList(), macros, platformCodeGenFlags());
Tobias Hunger's avatar
Tobias Hunger committed
733
734
}

735
736
QString GccToolChain::detectVersion() const
{
hjk's avatar
hjk committed
737
    Environment env = Environment::systemEnvironment();
738
    addToEnvironment(env);
739
    return gccVersion(m_compilerCommand, env.toStringList());
740
741
}

742
743
744
745
// --------------------------------------------------------------------------
// GccToolChainFactory
// --------------------------------------------------------------------------

746
GccToolChainFactory::GccToolChainFactory()
747
{
748
749
    setId(Constants::GCC_TOOLCHAIN_ID);
    setDisplayName(tr("GCC"));
750
751
}

752
bool GccToolChainFactory::canCreate()
753
754
755
756
{
    return true;
}

757
ToolChain *GccToolChainFactory::create()
758
759
760
761
{
    return createToolChain(false);
}

762
QList<ToolChain *> GccToolChainFactory::autoDetect()
763
{
Tobias Hunger's avatar
Tobias Hunger committed
764
    QList<ToolChain *> tcs;
765
766
767
768
769
    if (Utils::HostOsInfo::isMacHost()) {
        // 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
770
    tcs.append(autoDetectToolchains(QLatin1String("g++"), Abi::hostAbi()));
771
772

    return tcs;
773
774
}

775
// Used by the ToolChainManager to restore user-generated tool chains
776
bool GccToolChainFactory::canRestore(const QVariantMap &data)
777
{
Tobias Hunger's avatar
Tobias Hunger committed
778
    const QString id = idFromMap(data);
779
    return id.startsWith(QLatin1String(Constants::GCC_TOOLCHAIN_ID) + QLatin1Char(':'));
780
781
}

782
ToolChain *GccToolChainFactory::restore(const QVariantMap &data)
783
{
784
    GccToolChain *tc = new GccToolChain(ToolChain::ManualDetection);
Tobias Hunger's avatar
Tobias Hunger committed
785
786
787
788
    // Updating from 2.5:
    QVariantMap updated = data;
    QString id = idFromMap(updated);
    if (tc->fromMap(updated))
789
790
791
792
793
794
        return tc;

    delete tc;
    return 0;
}

795
GccToolChain *GccToolChainFactory::createToolChain(bool autoDetect)
796
{
797
    return new GccToolChain(autoDetect ? ToolChain::AutoDetection : ToolChain::ManualDetection);
798
799
}

800
QList<ToolChain *> GccToolChainFactory::autoDetectToolchains(const QString &compiler,
801
                                                                       const Abi &requiredAbi)
802
803
804
{
    QList<ToolChain *> result;

805
    Environment systemEnvironment = Environment::systemEnvironment();
hjk's avatar
hjk committed
806
    const FileName compilerPath = FileName::fromString(systemEnvironment.searchInPath(compiler));
807
    if (compilerPath.isEmpty())
808
        return result;
Tobias Hunger's avatar
Tobias Hunger committed
809

810
    GccToolChain::addCommandPathToEnvironment(compilerPath, systemEnvironment);
811
812
813
    QByteArray macros
            = gccPredefinedMacros(compilerPath, gccPredefinedMacrosOptions(), systemEnvironment.toStringList());
    QList<Abi> abiList = guessGccAbi(compilerPath, systemEnvironment.toStringList(), macros);
814
815
816
817
818
819
    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
820
821
822

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

827
        tc->setCompilerCommand(compilerPath);
Tobias Hunger's avatar
Tobias Hunger committed
828
        tc->setTargetAbi(abi);
829
        tc->setDisplayName(tc->defaultDisplayName()); // reset displayname
830
831

        result.append(tc.take());
Tobias Hunger's avatar
Tobias Hunger committed
832
    }
833
834
835
836
837
838
839
840

    return result;
}

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

841
GccToolChainConfigWidget::GccToolChainConfigWidget(GccToolChain *tc) :
842
    ToolChainConfigWidget(tc),
hjk's avatar
hjk committed
843
    m_compilerCommand(new PathChooser),
844
845
    m_abiWidget(new AbiWidget),
    m_isReadOnly(false)
846
847
848
{
    Q_ASSERT(tc);

849
    const QStringList gnuVersionArgs = QStringList(QLatin1String("--version"));
hjk's avatar
hjk committed
850
    m_compilerCommand->setExpectedKind(PathChooser::ExistingCommand);
851
    m_compilerCommand->setCommandVersionArguments(gnuVersionArgs);
852
    m_mainLayout->addRow(tr("&Compiler path:"), m_compilerCommand);
853
854
855
856
857
858
    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);
859
    m_mainLayout->addRow(tr("&ABI:"), m_abiWidget);
860

Tobias Hunger's avatar
Tobias Hunger committed
861
    m_abiWidget->setEnabled(false);
862
    addErrorLabel();
863

864
    setFromToolchain();
865
    handleCompilerCommandChange();
866

867
    connect(m_compilerCommand, SIGNAL(changed(QString)), this, SLOT(handleCompilerCommandChange()));
868
869
    connect(m_platformCodeGenFlagsLineEdit, SIGNAL(editingFinished()), this, SLOT(handlePlatformCodeGenFlagsChange()));
    connect(m_platformLinkerFlagsLineEdit, SIGNAL(editingFinished()), this, SLOT(handlePlatformLinkerFlagsChange()));
Tobias Hunger's avatar
Tobias Hunger committed
870
    connect(m_abiWidget, SIGNAL(abiChanged()), this, SIGNAL(dirty()));
871
872
}

873
void GccToolChainConfigWidget::applyImpl()
874
875
876
877
878
879
880
{
    if (toolChain()->isAutoDetected())
        return;

    GccToolChain *tc = static_cast<GccToolChain *>(toolChain());
    Q_ASSERT(tc);
    QString displayName = tc->displayName();
881
    tc->setCompilerCommand(m_compilerCommand->fileName());
882
    tc->setTargetAbi(m_abiWidget->currentAbi());
883
    tc->setDisplayName(displayName); // reset display name
884
885
    tc->setPlatformCodeGenFlags(splitString(m_platformCodeGenFlagsLineEdit->text()));
    tc->setPlatformLinkerFlags(splitString(m_platformLinkerFlagsLineEdit->text()));
886
    tc->setMacroCache(tc->platformCodeGenFlags(), m_macros);
887
888
}

889
void GccToolChainConfigWidget::setFromToolchain()
890
{
891
892
    // subwidgets are not yet connected!
    bool blocked = blockSignals(true);
893
    GccToolChain *tc = static_cast<GccToolChain *>(toolChain());
894
    m_compilerCommand->setFileName(tc->compilerCommand());
895
896
    m_platformCodeGenFlagsLineEdit->setText(QtcProcess::joinArgs(tc->platformCodeGenFlags()));
    m_platformLinkerFlagsLineEdit->setText(QtcProcess::joinArgs(tc->platformLinkerFlags()));
Tobias Hunger's avatar
Tobias Hunger committed
897
    m_abiWidget->setAbis(tc->supportedAbis(), tc->targetAbi());
898
    if (!m_isReadOnly && !m_compilerCommand->path().isEmpty())
899
        m_abiWidget->setEnabled(true);
900
    blockSignals(blocked);
901
902
}

903
bool GccToolChainConfigWidget::isDirtyImpl() const
904
905
906
{
    GccToolChain *tc = static_cast<GccToolChain *>(toolChain());
    Q_ASSERT(tc);
907
    return m_compilerCommand->fileName() != tc->compilerCommand()
908
909
            || m_platformCodeGenFlagsLineEdit->text() != QtcProcess::joinArgs(tc->platformCodeGenFlags())
            || m_platformLinkerFlagsLineEdit->text() != QtcProcess::joinArgs(tc->platformLinkerFlags())
Tobias Hunger's avatar
Tobias Hunger committed
910
            || m_abiWidget->currentAbi() != tc->targetAbi();
911
912
}

913
void GccToolChainConfigWidget::makeReadOnlyImpl()
914
{
915
    m_compilerCommand->setEnabled(false);
916
    m_abiWidget->setEnabled(false);
917
918
    m_platformCodeGenFlagsLineEdit->setEnabled(false);
    m_platformLinkerFlagsLineEdit->setEnabled(false);
919
    m_isReadOnly = true;
920
921
}

922
QStringList GccToolChainConfigWidget::splitString(const QString &s)
923
924
925
926
927
928
929
930
931
932
933
934
935
936
{
    QtcProcess::SplitError splitError;
    QStringList res = QtcProcess::splitArgs(s, false, &splitError);
    if (splitError != QtcProcess::SplitOk){
        res = QtcProcess::splitArgs(s + QLatin1Char('\\'), false, &splitError);
        if (splitError != QtcProcess::SplitOk){
            res = QtcProcess::splitArgs(s + QLatin1Char('"'), false, &splitError);
            if (splitError != QtcProcess::SplitOk)
                res = QtcProcess::splitArgs(s + QLatin1Char('\''), false, &splitError);
        }
    }
    return res;
}

937
void GccToolChainConfigWidget::handleCompilerCommandChange()
938
{
hjk's avatar
hjk committed
939
    FileName path = m_compilerCommand->fileName();
Tobias Hunger's avatar
Tobias Hunger committed
940
    QList<Abi> abiList;
Tobias Hunger's avatar
Tobias Hunger committed
941
942
    bool haveCompiler = false;
    if (!path.isEmpty()) {
943
        QFileInfo fi(path.toFileInfo());
Tobias Hunger's avatar
Tobias Hunger committed
944
945
        haveCompiler = fi.isExecutable() && fi.isFile();
    }
946
947
948
    if (haveCompiler) {
        Environment env = Environment::systemEnvironment();
        GccToolChain::addCommandPathToEnvironment(path, env);
949
950
951
        QStringList args = gccPredefinedMacrosOptions() + splitString(m_platformCodeGenFlagsLineEdit->text());
        m_macros = gccPredefinedMacros(path, args, env.toStringList());
        abiList = guessGccAbi(path, env.toStringList(), m_macros,
952
                              splitString(m_platformCodeGenFlagsLineEdit->text()));
953
    }
Tobias Hunger's avatar
Tobias Hunger committed
954
955
956
    m_abiWidget->setEnabled(haveCompiler);
    Abi currentAbi = m_abiWidget->currentAbi();
    m_abiWidget->setAbis(abiList, abiList.contains(currentAbi) ? currentAbi : Abi());
957
    emit dirty();
958
959
}

960
void GccToolChainConfigWidget::handlePlatformCodeGenFlagsChange()
961
962
963
964
965
966
967
968
969
{
    QString str1 = m_platformCodeGenFlagsLineEdit->text();
    QString str2 = QtcProcess::joinArgs(splitString(str1));
    if (str1 != str2)
        m_platformCodeGenFlagsLineEdit->setText(str2);
    else
        handleCompilerCommandChange();
}

970
void GccToolChainConfigWidget::handlePlatformLinkerFlagsChange()
971
972
973
974
975
976
977
978
979
{
    QString str1 = m_platformLinkerFlagsLineEdit->text();
    QString str2 = QtcProcess::joinArgs(splitString(str1));
    if (str1 != str2)
        m_platformLinkerFlagsLineEdit->setText(str2);
    else
        emit dirty();
}

Tobias Hunger's avatar
Tobias Hunger committed
980
981
982
983
// --------------------------------------------------------------------------
// ClangToolChain
// --------------------------------------------------------------------------

984
985
ClangToolChain::ClangToolChain(Detection d) :
    GccToolChain(QLatin1String(Constants::CLANG_TOOLCHAIN_ID), d)
Tobias Hunger's avatar
Tobias Hunger committed
986
987
{ }

Tobias Hunger's avatar
Tobias Hunger committed
988
989
990
991
992
993
QString ClangToolChain::type() const
{
    return QLatin1String("clang");
}

QString ClangToolChain::typeDisplayName() const
Tobias Hunger's avatar
Tobias Hunger committed
994
{
995
    return ClangToolChainFactory::tr("Clang");
Tobias Hunger's avatar
Tobias Hunger committed
996
997
}

998
QString ClangToolChain::makeCommand(const Utils::Environment &environment) const
Tobias Hunger's avatar
Tobias Hunger committed
999
{
1000
    QStringList makes;
For faster browsing, not all history is shown. View entire blame