msvctoolchain.cpp 24.5 KB
Newer Older
hjk's avatar
hjk committed
1
/****************************************************************************
2
**
3
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
hjk's avatar
hjk committed
4
** Contact: http://www.qt-project.org/legal
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
** 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
Eike Ziller's avatar
Eike Ziller committed
13
14
** conditions see http://www.qt.io/licensing.  For further information
** 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
25
26
**
** In addition, as a special exception, Digia gives you certain additional
** rights.  These rights are described in the Digia Qt 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 "msvctoolchain.h"
32

33
34
35
36
#include "msvcparser.h"
#include "projectexplorerconstants.h"

#include <utils/synchronousprocess.h>
37
#include <utils/winutils.h>
SteveKing's avatar
SteveKing committed
38
#include <utils/qtcassert.h>
Daniel Teske's avatar
Daniel Teske committed
39
#include <utils/hostosinfo.h>
40

41
#include <QDir>
42
#include <QFileInfo>
43
44
#include <QProcess>
#include <QSettings>
45
46

#include <QLabel>
47
#include <QFormLayout>
48

49
50
51
52
#define KEY_ROOT "ProjectExplorer.MsvcToolChain."
static const char varsBatKeyC[] = KEY_ROOT"VarsBat";
static const char varsBatArgKeyC[] = KEY_ROOT"VarsBatArg";
static const char supportedAbiKeyC[] = KEY_ROOT"SupportedAbi";
53

Friedemann Kleint's avatar
Friedemann Kleint committed
54
55
enum { debug = 0 };

56
57
58
59
60
61
62
63
64
65
namespace ProjectExplorer {
namespace Internal {

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

static QString platformName(MsvcToolChain::Platform t)
{
    switch (t) {
66
    case MsvcToolChain::x86:
67
        return QLatin1String("x86");
68
    case MsvcToolChain::amd64:
69
70
71
        return QLatin1String("amd64");
    case MsvcToolChain::x86_amd64:
        return QLatin1String("x86_amd64");
72
    case MsvcToolChain::ia64:
73
74
75
        return QLatin1String("ia64");
    case MsvcToolChain::x86_ia64:
        return QLatin1String("x86_ia64");
Daniel Teske's avatar
Daniel Teske committed
76
    case MsvcToolChain::arm:
77
78
79
80
81
        return QLatin1String("arm");
    case MsvcToolChain::x86_arm:
        return QLatin1String("x86_arm");
    case MsvcToolChain::amd64_arm:
        return QLatin1String("amd64_arm");
82
83
84
85
    }
    return QString();
}

86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
static bool hostSupportsPlatform(MsvcToolChain::Platform platform)
{
    switch (Utils::HostOsInfo::hostArchitecture()) {
    case Utils::HostOsInfo::HostArchitectureAMD64:
        if (platform == MsvcToolChain::amd64 || platform == MsvcToolChain::amd64_arm)
            return true;
        // fall through (all x86 toolchains are also working on an amd64 host)
    case Utils::HostOsInfo::HostArchitectureX86:
        return platform == MsvcToolChain::x86 || platform == MsvcToolChain::x86_amd64
                || platform == MsvcToolChain::x86_ia64 || platform == MsvcToolChain::x86_arm;
    case Utils::HostOsInfo::HostArchitectureArm:
        return platform == MsvcToolChain::arm;
    case Utils::HostOsInfo::HostArchitectureItanium:
        return platform == MsvcToolChain::ia64;
    default:
        return false;
    }
}

105
106
107
108
109
110
111
112
static Abi findAbiOfMsvc(MsvcToolChain::Type type, MsvcToolChain::Platform platform, const QString &version)
{
    Abi::Architecture arch = Abi::X86Architecture;
    Abi::OSFlavor flavor = Abi::UnknownFlavor;
    int wordWidth = 64;

    switch (platform)
    {
113
    case MsvcToolChain::x86:
114
115
        wordWidth = 32;
        break;
116
117
    case MsvcToolChain::ia64:
    case MsvcToolChain::x86_ia64:
118
119
        arch = Abi::ItaniumArchitecture;
        break;
120
121
    case MsvcToolChain::amd64:
    case MsvcToolChain::x86_amd64:
Daniel Teske's avatar
Daniel Teske committed
122
        break;
123
124
125
    case MsvcToolChain::arm:
    case MsvcToolChain::x86_arm:
    case MsvcToolChain::amd64_arm:
Daniel Teske's avatar
Daniel Teske committed
126
127
        arch = Abi::ArmArchitecture;
        wordWidth = 32;
128
129
130
131
132
        break;
    };

    QString msvcVersionString = version;
    if (type == MsvcToolChain::WindowsSDK) {
Daniel Teske's avatar
Daniel Teske committed
133
        if (version == QLatin1String("v7.0") || version.startsWith(QLatin1String("6.")))
134
            msvcVersionString = QLatin1String("9.0");
Daniel Teske's avatar
Daniel Teske committed
135
136
        else if (version == QLatin1String("v7.0A") || version == QLatin1String("v7.1"))
            msvcVersionString = QLatin1String("10.0");
137
    }
Yuchen Deng's avatar
Yuchen Deng committed
138
139
140
    if (msvcVersionString.startsWith(QLatin1String("12.")))
        flavor = Abi::WindowsMsvc2013Flavor;
    else if (msvcVersionString.startsWith(QLatin1String("11.")))
Tobias Hunger's avatar
Tobias Hunger committed
141
142
        flavor = Abi::WindowsMsvc2012Flavor;
    else if (msvcVersionString.startsWith(QLatin1String("10.")))
143
        flavor = Abi::WindowsMsvc2010Flavor;
144
    else if (msvcVersionString.startsWith(QLatin1String("9.")))
145
146
147
        flavor = Abi::WindowsMsvc2008Flavor;
    else
        flavor = Abi::WindowsMsvc2005Flavor;
Tobias Hunger's avatar
Tobias Hunger committed
148
149
150
151
152
    const Abi result = Abi(arch, Abi::WindowsOS, flavor, Abi::PEFormat, wordWidth);
    if (!result.isValid())
        qWarning("Unable to completely determine the ABI of MSVC version %s (%s).",
                 qPrintable(version), qPrintable(result.toString()));
    return result;
153
154
}

155
156
157
158
159
160
static QString generateDisplayName(const QString &name,
                                   MsvcToolChain::Type t,
                                   MsvcToolChain::Platform p)
{
    if (t == MsvcToolChain::WindowsSDK) {
        QString sdkName = name;
161
        sdkName += QString::fromLatin1(" (%1)").arg(platformName(p));
162
163
164
165
166
        return sdkName;
    }
    // Comes as "9.0" from the registry
    QString vcName = QLatin1String("Microsoft Visual C++ Compiler ");
    vcName += name;
167
    vcName += QString::fromLatin1(" (%1)").arg(platformName(p));
168
169
170
171
172
173
174
175
176
    return vcName;
}

static QByteArray msvcCompilationFile()
{
    static const char* macros[] = {"_ATL_VER", "_CHAR_UNSIGNED", "__CLR_VER",
                                   "__cplusplus_cli", "__COUNTER__", "__cplusplus",
                                   "_CPPLIB_VER", "_CPPRTTI", "_CPPUNWIND",
                                   "_DEBUG", "_DLL", "__FUNCDNAME__",
177
178
179
180
181
182
183
                                   "__FUNCSIG__", "__FUNCTION__", "_INTEGRAL_MAX_BITS",
                                   "_M_ALPHA", "_M_AAMD64", "_M_CEE", "_M_CEE_PURE",
                                   "_M_CEE_SAFE", "_M_IX86", "_M_IA64",
                                   "_M_IX86_FP", "_M_MPPC", "_M_MRX000",
                                   "_M_PPC", "_M_X64", "_MANAGED",
                                   "_MFC_VER", "_MSC_BUILD", "_MSC_EXTENSIONS",
                                   "_MSC_FULL_VER", "_MSC_VER", "__MSVC_RUNTIME_CHECKS",
184
185
                                   "_MT", "_NATIVE_WCHAR_T_DEFINED", "_OPENMP",
                                   "_VC_NODEFAULTLIB", "_WCHAR_T_DEFINED", "_WIN32",
186
                                   "_WIN32_WCE", "_WIN64", "_Wp64",
187
188
189
190
191
192
193
194
195
196
197
198
199
                                   "__DATE__", "__TIME__", "__TIMESTAMP__",
                                   0};
    QByteArray file = "#define __PPOUT__(x) V##x=x\n\n";
    for (int i = 0; macros[i] != 0; ++i) {
        const QByteArray macro(macros[i]);
        file += "#if defined(" + macro + ")\n__PPOUT__("
                + macro + ")\n#endif\n";
    }
    file += "\nvoid main(){}\n\n";
    return file;
}

// Run MSVC 'cl' compiler to obtain #defines.
200
201
QByteArray MsvcToolChain::msvcPredefinedMacros(const QStringList cxxflags,
                                               const Utils::Environment &env) const
202
{
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
    QByteArray predefinedMacros = AbstractMsvcToolChain::msvcPredefinedMacros(cxxflags, env);

    QStringList toProcess;
    foreach (const QString &arg, cxxflags) {
        if (arg.startsWith(QLatin1String("/D"))) {
            QString define = arg.mid(2);
            int pos = define.indexOf(QLatin1Char('='));
            if (pos < 0) {
                predefinedMacros += "#define ";
                predefinedMacros += define.toLocal8Bit();
                predefinedMacros += '\n';
            } else {
                predefinedMacros += "#define ";
                predefinedMacros += define.left(pos).toLocal8Bit();
                predefinedMacros += ' ';
                predefinedMacros += define.mid(pos + 1).toLocal8Bit();
                predefinedMacros += '\n';
            }
        } else if (arg.startsWith(QLatin1String("/U"))) {
            predefinedMacros += "#undef ";
            predefinedMacros += arg.mid(2).toLocal8Bit();
            predefinedMacros += '\n';
225
226
        } else if (arg.startsWith(QLatin1String("-I"))) {
            // Include paths should not have any effect on defines
227
228
229
230
        } else {
            toProcess.append(arg);
        }
    }
231

232
    Utils::TempFileSaver saver(QDir::tempPath() + QLatin1String("/envtestXXXXXX.cpp"));
233
234
235
236
    saver.write(msvcCompilationFile());
    if (!saver.finalize()) {
        qWarning("%s: %s", Q_FUNC_INFO, qPrintable(saver.errorString()));
        return predefinedMacros;
237
238
    }
    QProcess cpp;
Friedemann Kleint's avatar
Friedemann Kleint committed
239
    cpp.setEnvironment(env.toStringList());
240
241
    cpp.setWorkingDirectory(QDir::tempPath());
    QStringList arguments;
242
    const Utils::FileName binary = env.searchInPath(QLatin1String("cl.exe"));
Friedemann Kleint's avatar
Friedemann Kleint committed
243
244
245
246
247
    if (binary.isEmpty()) {
        qWarning("%s: The compiler binary cl.exe could not be found in the path.", Q_FUNC_INFO);
        return predefinedMacros;
    }

248
    arguments << toProcess << QLatin1String("/EP") << QDir::toNativeSeparators(saver.fileName());
249
    cpp.start(binary.toString(), arguments);
250
    if (!cpp.waitForStarted()) {
251
        qWarning("%s: Cannot start '%s': %s", Q_FUNC_INFO, qPrintable(binary.toUserOutput()),
252
253
254
255
256
257
            qPrintable(cpp.errorString()));
        return predefinedMacros;
    }
    cpp.closeWriteChannel();
    if (!cpp.waitForFinished()) {
        Utils::SynchronousProcess::stopProcess(cpp);
258
        qWarning("%s: Timeout running '%s'.", Q_FUNC_INFO, qPrintable(binary.toUserOutput()));
259
260
261
        return predefinedMacros;
    }
    if (cpp.exitStatus() != QProcess::NormalExit) {
262
        qWarning("%s: '%s' crashed.", Q_FUNC_INFO, qPrintable(binary.toUserOutput()));
263
264
265
266
267
268
269
270
271
        return predefinedMacros;
    }

    const QList<QByteArray> output = cpp.readAllStandardOutput().split('\n');
    foreach (const QByteArray& line, output) {
        if (line.startsWith('V')) {
            QList<QByteArray> split = line.split('=');
            const QByteArray key = split.at(0).mid(1);
            QByteArray value = split.at(1);
272
            if (!value.isEmpty())
273
274
275
276
277
278
279
280
                value.chop(1); //remove '\n'
            predefinedMacros += "#define ";
            predefinedMacros += key;
            predefinedMacros += ' ';
            predefinedMacros += value;
            predefinedMacros += '\n';
        }
    }
Friedemann Kleint's avatar
Friedemann Kleint committed
281
282
    if (debug)
        qDebug() << "msvcPredefinedMacros" << predefinedMacros;
283
284
285
    return predefinedMacros;
}

Friedemann Kleint's avatar
Friedemann Kleint committed
286
287
288
// Windows: Expand the delayed evaluation references returned by the
// SDK setup scripts: "PATH=!Path!;foo". Some values might expand
// to empty and should not be added
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
static QString winExpandDelayedEnvReferences(QString in, const Utils::Environment &env)
{
    const QChar exclamationMark = QLatin1Char('!');
    for (int pos = 0; pos < in.size(); ) {
        // Replace "!REF!" by its value in process environment
        pos = in.indexOf(exclamationMark, pos);
        if (pos == -1)
            break;
        const int nextPos = in.indexOf(exclamationMark, pos + 1);
        if (nextPos == -1)
            break;
        const QString var = in.mid(pos + 1, nextPos - pos - 1);
        const QString replacement = env.value(var.toUpper());
        in.replace(pos, nextPos + 1 - pos, replacement);
        pos += replacement.size();
    }
    return in;
}

SteveKing's avatar
SteveKing committed
308
Utils::Environment MsvcToolChain::readEnvironmentSetting(Utils::Environment& env) const
309
{
Friedemann Kleint's avatar
Friedemann Kleint committed
310
    Utils::Environment result = env;
311
    if (!QFileInfo::exists(m_vcvarsBat))
Friedemann Kleint's avatar
Friedemann Kleint committed
312
        return result;
313

SteveKing's avatar
SteveKing committed
314
315
    QMap<QString, QString> envPairs;
    if (!generateEnvironmentSettings(env, m_vcvarsBat, m_varsBatArg, envPairs))
316
        return result;
Friedemann Kleint's avatar
Friedemann Kleint committed
317

SteveKing's avatar
SteveKing committed
318
319
320
321
322
323
    // Now loop through and process them
    QMap<QString,QString>::const_iterator envIter;
    for (envIter = envPairs.begin(); envIter!=envPairs.end(); ++envIter) {
        const QString expandedValue = winExpandDelayedEnvReferences(envIter.value(), env);
        if (!expandedValue.isEmpty())
            result.set(envIter.key(), expandedValue);
324
325
    }

Friedemann Kleint's avatar
Friedemann Kleint committed
326
327
328
329
330
331
332
333
334
335
    if (debug) {
        const QStringList newVars = result.toStringList();
        const QStringList oldVars = env.toStringList();
        QDebug nsp = qDebug().nospace();
        foreach (const QString &n, newVars) {
            if (!oldVars.contains(n))
                nsp << n << '\n';
        }
    }
    return result;
336
337
338
339
340
341
}

// --------------------------------------------------------------------------
// MsvcToolChain
// --------------------------------------------------------------------------

342
MsvcToolChain::MsvcToolChain(const QString &name, const Abi &abi,
343
344
                             const QString &varsBat, const QString &varsBatArg, Detection d) :
    AbstractMsvcToolChain(QLatin1String(Constants::MSVC_TOOLCHAIN_ID), d, abi, varsBat),
SteveKing's avatar
SteveKing committed
345
    m_varsBatArg(varsBatArg)
346
347
348
{
    Q_ASSERT(!name.isEmpty());

349
    setDisplayName(name);
350
351
}

352
353
354
355
356
bool MsvcToolChain::isValid() const
{
    if (!AbstractMsvcToolChain::isValid())
        return false;
    QString vcVarsBat = MsvcToolChainFactory::vcVarsBatFor(QFileInfo(m_vcvarsBat).absolutePath(), m_varsBatArg);
357
    return QFileInfo::exists(vcVarsBat);
358
359
}

360
MsvcToolChain::MsvcToolChain() :
361
    AbstractMsvcToolChain(QLatin1String(Constants::MSVC_TOOLCHAIN_ID), ManualDetection)
362
363
364
365
366
367
368
369
370
371
372
373
{
}

MsvcToolChain *MsvcToolChain::readFromMap(const QVariantMap &data)
{
    MsvcToolChain *tc = new MsvcToolChain;
    if (tc->fromMap(data))
        return tc;
    delete tc;
    return 0;
}

Tobias Hunger's avatar
Tobias Hunger committed
374
375
376
377
378
379
QString MsvcToolChain::type() const
{
    return QLatin1String("msvc");
}

QString MsvcToolChain::typeDisplayName() const
380
381
382
383
{
    return MsvcToolChainFactory::tr("MSVC");
}

384
QList<Utils::FileName> MsvcToolChain::suggestedMkspecList() const
385
{
Tobias Hunger's avatar
Tobias Hunger committed
386
    switch (m_abi.osFlavor()) {
387
    case Abi::WindowsMsvc2005Flavor:
388
        return QList<Utils::FileName>() << Utils::FileName::fromLatin1("win32-msvc2005");
389
    case Abi::WindowsMsvc2008Flavor:
390
        return QList<Utils::FileName>() << Utils::FileName::fromLatin1("win32-msvc2008");
391
    case Abi::WindowsMsvc2010Flavor:
392
        return QList<Utils::FileName>() << Utils::FileName::fromLatin1("win32-msvc2010");
393
    case Abi::WindowsMsvc2012Flavor:
394
        return QList<Utils::FileName>()
395
396
            << Utils::FileName::fromLatin1("win32-msvc2012")
            << Utils::FileName::fromLatin1("win32-msvc2010");
397
    case Abi::WindowsMsvc2013Flavor:
398
        return QList<Utils::FileName>()
399
            << Utils::FileName::fromLatin1("win32-msvc2013")
400
401
402
403
404
            << Utils::FileName::fromLatin1("winphone-arm-msvc2013")
            << Utils::FileName::fromLatin1("winphone-x86-msvc2013")
            << Utils::FileName::fromLatin1("winrt-arm-msvc2013")
            << Utils::FileName::fromLatin1("winrt-x86-msvc2013")
            << Utils::FileName::fromLatin1("winrt-x64-msvc2013")
405
406
            << Utils::FileName::fromLatin1("win32-msvc2012")
            << Utils::FileName::fromLatin1("win32-msvc2010");
Tobias Hunger's avatar
Tobias Hunger committed
407
408
409
    default:
        break;
    }
410
    return QList<Utils::FileName>();
411
412
}

413
414
QVariantMap MsvcToolChain::toMap() const
{
415
    QVariantMap data = AbstractMsvcToolChain::toMap();
SteveKing's avatar
SteveKing committed
416
    data.insert(QLatin1String(varsBatKeyC), m_vcvarsBat);
417
418
419
    if (!m_varsBatArg.isEmpty())
        data.insert(QLatin1String(varsBatArgKeyC), m_varsBatArg);
    data.insert(QLatin1String(supportedAbiKeyC), m_abi.toString());
420
421
422
423
424
425
426
    return data;
}

bool MsvcToolChain::fromMap(const QVariantMap &data)
{
    if (!ToolChain::fromMap(data))
        return false;
427
    m_vcvarsBat = QDir::fromNativeSeparators(data.value(QLatin1String(varsBatKeyC)).toString());
428
429
430
    m_varsBatArg = data.value(QLatin1String(varsBatArgKeyC)).toString();
    const QString abiString = data.value(QLatin1String(supportedAbiKeyC)).toString();
    m_abi = Abi(abiString);
431

SteveKing's avatar
SteveKing committed
432
    return !m_vcvarsBat.isEmpty() && m_abi.isValid();
433
434
}

SteveKing's avatar
SteveKing committed
435

436
437
438
439
440
441
442
ToolChainConfigWidget *MsvcToolChain::configurationWidget()
{
    return new MsvcToolChainConfigWidget(this);
}

ToolChain *MsvcToolChain::clone() const
{
443
    return new MsvcToolChain(*this);
444
445
446
447
448
449
450
}

// --------------------------------------------------------------------------
// MsvcToolChainConfigWidget
// --------------------------------------------------------------------------

MsvcToolChainConfigWidget::MsvcToolChainConfigWidget(ToolChain *tc) :
451
452
    ToolChainConfigWidget(tc),
    m_varsBatDisplayLabel(new QLabel(this))
453
{
454
    m_mainLayout->addRow(new QLabel(tc->displayName()));
455
    m_varsBatDisplayLabel->setTextInteractionFlags(Qt::TextBrowserInteraction);
456
457
    m_mainLayout->addRow(tr("Initialization:"), m_varsBatDisplayLabel);
    addErrorLabel();
458
    setFromToolChain();
459
460
}

461
void MsvcToolChainConfigWidget::setFromToolChain()
462
{
463
464
    MsvcToolChain *tc = static_cast<MsvcToolChain *>(toolChain());
    QTC_ASSERT(tc, return);
465
    QString varsBatDisplay = QDir::toNativeSeparators(tc->varsBat());
466
467
468
469
470
    if (!tc->varsBatArg().isEmpty()) {
        varsBatDisplay += QLatin1Char(' ');
        varsBatDisplay += tc->varsBatArg();
    }
    m_varsBatDisplayLabel->setText(varsBatDisplay);
471
472
473
474
475
476
}

// --------------------------------------------------------------------------
// MsvcToolChainFactory
// --------------------------------------------------------------------------

477
MsvcToolChainFactory::MsvcToolChainFactory()
478
{
479
480
    setId(Constants::MSVC_TOOLCHAIN_ID);
    setDisplayName(tr("MSVC"));
481
482
}

483
484
485
486
487
488
489
490
491
492
493
494
495
bool MsvcToolChainFactory::checkForVisualStudioInstallation(const QString &vsName)
{
    const QSettings vsRegistry(
#ifdef Q_OS_WIN64
                QLatin1String("HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\Microsoft\\VisualStudio\\SxS\\VS7"),
#else
                QLatin1String("HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\SxS\\VS7"),
#endif
                QSettings::NativeFormat);

    return vsRegistry.contains(vsName);
}

496
497
QString MsvcToolChainFactory::vcVarsBatFor(const QString &basePath, const QString &toolchainName)
{
498
499
    if (toolchainName.startsWith(QLatin1Char('/'))) // windows sdk case, all use SetEnv.cmd
        return basePath + QLatin1String("/SetEnv.cmd");
500
501
    if (toolchainName == QLatin1String("x86"))
        return basePath + QLatin1String("/bin/vcvars32.bat");
502
503
    if (toolchainName == QLatin1String("amd64_arm"))
        return basePath + QLatin1String("/bin/amd64_arm/vcvarsamd64_arm.bat");
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
    if (toolchainName == QLatin1String("x86_amd64"))
        return basePath + QLatin1String("/bin/x86_amd64/vcvarsx86_amd64.bat");
    if (toolchainName == QLatin1String("amd64"))
        return basePath + QLatin1String("/bin/amd64/vcvars64.bat");
    if (toolchainName == QLatin1String("x86_arm"))
        return basePath + QLatin1String("/bin/x86_arm/vcvarsx86_arm.bat");
    if (toolchainName == QLatin1String("arm"))
        return basePath + QLatin1String("/bin/arm/vcvarsarm.bat");
    if (toolchainName == QLatin1String("ia64"))
        return basePath + QLatin1String("/bin/ia64/vcvars64.bat");
    if (toolchainName == QLatin1String("x86_ia64"))
        return basePath + QLatin1String("/bin/x86_ia64/vcvarsx86_ia64.bat");

    return QString();
}

520
521
522
523
524
QString MsvcToolChainFactory::vcVarsBatFor(const QString &basePath, MsvcToolChain::Platform platform)
{
    return vcVarsBatFor(basePath, platformName(platform));
}

525
526
527
528
529
QList<ToolChain *> MsvcToolChainFactory::autoDetect()
{
    QList<ToolChain *> results;

    // 1) Installed SDKs preferred over standalone Visual studio
530
    const QSettings sdkRegistry(QLatin1String("HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Microsoft SDKs\\Windows"),
531
532
533
534
535
536
537
538
539
                                QSettings::NativeFormat);
    const QString defaultSdkPath = sdkRegistry.value(QLatin1String("CurrentInstallFolder")).toString();
    if (!defaultSdkPath.isEmpty()) {
        foreach (const QString &sdkKey, sdkRegistry.childGroups()) {
            const QString name = sdkRegistry.value(sdkKey + QLatin1String("/ProductName")).toString();
            const QString folder = sdkRegistry.value(sdkKey + QLatin1String("/InstallationFolder")).toString();
            if (folder.isEmpty())
                continue;

Yuchen Deng's avatar
Yuchen Deng committed
540
541
542
543
544
            QDir dir(folder);
            if (!dir.cd(QLatin1String("bin")))
                continue;
            QFileInfo fi(dir, QLatin1String("SetEnv.cmd"));
            if (!fi.exists())
545
                continue;
546

Yuchen Deng's avatar
Yuchen Deng committed
547
            QList<ToolChain *> tmp;
548
            tmp.append(new MsvcToolChain(generateDisplayName(name, MsvcToolChain::WindowsSDK, MsvcToolChain::x86),
Daniel Teske's avatar
Daniel Teske committed
549
                                         findAbiOfMsvc(MsvcToolChain::WindowsSDK, MsvcToolChain::x86, sdkKey),
550
                                         fi.absoluteFilePath(), QLatin1String("/x86"), ToolChain::AutoDetection));
551
            // Add all platforms, cross-compiler is automatically selected by SetEnv.cmd if needed
552
            tmp.append(new MsvcToolChain(generateDisplayName(name, MsvcToolChain::WindowsSDK, MsvcToolChain::amd64),
Daniel Teske's avatar
Daniel Teske committed
553
                                         findAbiOfMsvc(MsvcToolChain::WindowsSDK, MsvcToolChain::amd64, sdkKey),
554
                                         fi.absoluteFilePath(), QLatin1String("/x64"), ToolChain::AutoDetection));
555
            tmp.append(new MsvcToolChain(generateDisplayName(name, MsvcToolChain::WindowsSDK, MsvcToolChain::ia64),
Daniel Teske's avatar
Daniel Teske committed
556
                                         findAbiOfMsvc(MsvcToolChain::WindowsSDK, MsvcToolChain::ia64, sdkKey),
557
                                         fi.absoluteFilePath(), QLatin1String("/ia64"), ToolChain::AutoDetection));
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
            // Make sure the default is front.
            if (folder == defaultSdkPath)
                results = tmp + results;
            else
                results += tmp;
        } // foreach
    }

    // 2) Installed MSVCs
    const QSettings vsRegistry(
#ifdef Q_OS_WIN64
                QLatin1String("HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\Microsoft\\VisualStudio\\SxS\\VC7"),
#else
                QLatin1String("HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\SxS\\VC7"),
#endif
                QSettings::NativeFormat);
    foreach (const QString &vsName, vsRegistry.allKeys()) {
        // Scan for version major.minor
        const int dotPos = vsName.indexOf(QLatin1Char('.'));
        if (dotPos == -1)
            continue;
579
580
        if (!checkForVisualStudioInstallation(vsName))
            continue;
581

582
        QString path = QDir::fromNativeSeparators(vsRegistry.value(vsName).toString());
583
584
        if (path.endsWith(QLatin1Char('/')))
            path.chop(1);
585
        const int version = vsName.left(dotPos).toInt();
586
        const QString vcvarsAllbat = path + QLatin1String("/vcvarsall.bat");
Daniel Teske's avatar
Daniel Teske committed
587
        if (QFileInfo(vcvarsAllbat).isFile()) {
588
589
590
591
592
593
594
595
596
597
598
599
600
601
            QList<MsvcToolChain::Platform> platforms; // prioritized list
            platforms << MsvcToolChain::x86
                      << MsvcToolChain::amd64 << MsvcToolChain::x86_amd64
                      << MsvcToolChain::arm << MsvcToolChain::amd64_arm << MsvcToolChain::x86_arm
                      << MsvcToolChain::ia64 << MsvcToolChain::x86_ia64;
            foreach (const MsvcToolChain::Platform &platform, platforms) {
                if (hostSupportsPlatform(platform)
                        && QFileInfo(vcVarsBatFor(path, platform)).isFile()) {
                    results.append(new MsvcToolChain(
                                       generateDisplayName(vsName, MsvcToolChain::VS, platform),
                                       findAbiOfMsvc(MsvcToolChain::VS, platform, vsName),
                                       vcvarsAllbat,
                                       platformName(platform),
                                       ToolChain::AutoDetection));
Daniel Teske's avatar
Daniel Teske committed
602
603
                }
            }
604
        } else {
Daniel Teske's avatar
Daniel Teske committed
605
            qWarning("Unable to find MSVC setup script %s in version %d", qPrintable(vcvarsAllbat), version);
606
607
        }
    }
SteveKing's avatar
SteveKing committed
608

609
610
611
    return results;
}

612
613
614
615
616
617
618
619
bool MsvcToolChain::operator ==(const ToolChain &other) const
{
    if (!AbstractMsvcToolChain::operator ==(other))
        return false;
    const MsvcToolChain *msvcTc = static_cast<const MsvcToolChain *>(&other);
    return m_varsBatArg == msvcTc->m_varsBatArg;
}

620
621
622
623
624
bool MsvcToolChainFactory::canRestore(const QVariantMap &data)
{
    return idFromMap(data).startsWith(QLatin1String(Constants::MSVC_TOOLCHAIN_ID) + QLatin1Char(':'));
}

625
626
} // namespace Internal
} // namespace ProjectExplorer