msvctoolchain.cpp 26.4 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 "msvctoolchain.h"
32

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

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

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

#include <QLabel>
48
#include <QFormLayout>
49

50
51
52
53
#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";
54

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

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

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

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

87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
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;
    }
}

106
107
108
109
110
111
112
113
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)
    {
114
    case MsvcToolChain::x86:
115
116
        wordWidth = 32;
        break;
117
118
    case MsvcToolChain::ia64:
    case MsvcToolChain::x86_ia64:
119
120
        arch = Abi::ItaniumArchitecture;
        break;
121
122
    case MsvcToolChain::amd64:
    case MsvcToolChain::x86_amd64:
Daniel Teske's avatar
Daniel Teske committed
123
        break;
124
125
126
    case MsvcToolChain::arm:
    case MsvcToolChain::x86_arm:
    case MsvcToolChain::amd64_arm:
Daniel Teske's avatar
Daniel Teske committed
127
128
        arch = Abi::ArmArchitecture;
        wordWidth = 32;
129
130
131
132
133
        break;
    };

    QString msvcVersionString = version;
    if (type == MsvcToolChain::WindowsSDK) {
Daniel Teske's avatar
Daniel Teske committed
134
        if (version == QLatin1String("v7.0") || version.startsWith(QLatin1String("6.")))
135
            msvcVersionString = QLatin1String("9.0");
Daniel Teske's avatar
Daniel Teske committed
136
137
        else if (version == QLatin1String("v7.0A") || version == QLatin1String("v7.1"))
            msvcVersionString = QLatin1String("10.0");
138
    }
Joerg Bornemann's avatar
Joerg Bornemann committed
139
140
141
    if (msvcVersionString.startsWith(QLatin1String("14.")))
        flavor = Abi::WindowsMsvc2015Flavor;
    else if (msvcVersionString.startsWith(QLatin1String("12.")))
Yuchen Deng's avatar
Yuchen Deng committed
142
143
        flavor = Abi::WindowsMsvc2013Flavor;
    else if (msvcVersionString.startsWith(QLatin1String("11.")))
Tobias Hunger's avatar
Tobias Hunger committed
144
145
        flavor = Abi::WindowsMsvc2012Flavor;
    else if (msvcVersionString.startsWith(QLatin1String("10.")))
146
        flavor = Abi::WindowsMsvc2010Flavor;
147
    else if (msvcVersionString.startsWith(QLatin1String("9.")))
148
149
150
        flavor = Abi::WindowsMsvc2008Flavor;
    else
        flavor = Abi::WindowsMsvc2005Flavor;
Tobias Hunger's avatar
Tobias Hunger committed
151
152
153
154
155
    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;
156
157
}

158
159
160
161
162
163
static QString generateDisplayName(const QString &name,
                                   MsvcToolChain::Type t,
                                   MsvcToolChain::Platform p)
{
    if (t == MsvcToolChain::WindowsSDK) {
        QString sdkName = name;
164
        sdkName += QString::fromLatin1(" (%1)").arg(platformName(p));
165
166
167
168
169
        return sdkName;
    }
    // Comes as "9.0" from the registry
    QString vcName = QLatin1String("Microsoft Visual C++ Compiler ");
    vcName += name;
170
    vcName += QString::fromLatin1(" (%1)").arg(platformName(p));
171
172
173
174
175
176
177
178
179
    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__",
180
181
182
183
184
185
186
                                   "__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",
187
188
                                   "_MT", "_NATIVE_WCHAR_T_DEFINED", "_OPENMP",
                                   "_VC_NODEFAULTLIB", "_WCHAR_T_DEFINED", "_WIN32",
189
                                   "_WIN32_WCE", "_WIN64", "_Wp64",
190
191
192
193
194
195
196
197
198
199
200
201
202
                                   "__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.
203
204
QByteArray MsvcToolChain::msvcPredefinedMacros(const QStringList cxxflags,
                                               const Utils::Environment &env) const
205
{
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
    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';
228
229
        } else if (arg.startsWith(QLatin1String("-I"))) {
            // Include paths should not have any effect on defines
230
231
232
233
        } else {
            toProcess.append(arg);
        }
    }
234

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

251
    arguments << toProcess << QLatin1String("/EP") << QDir::toNativeSeparators(saver.fileName());
252
    cpp.start(binary.toString(), arguments);
253
    if (!cpp.waitForStarted()) {
254
        qWarning("%s: Cannot start '%s': %s", Q_FUNC_INFO, qPrintable(binary.toUserOutput()),
255
256
257
258
259
260
            qPrintable(cpp.errorString()));
        return predefinedMacros;
    }
    cpp.closeWriteChannel();
    if (!cpp.waitForFinished()) {
        Utils::SynchronousProcess::stopProcess(cpp);
261
        qWarning("%s: Timeout running '%s'.", Q_FUNC_INFO, qPrintable(binary.toUserOutput()));
262
263
264
        return predefinedMacros;
    }
    if (cpp.exitStatus() != QProcess::NormalExit) {
265
        qWarning("%s: '%s' crashed.", Q_FUNC_INFO, qPrintable(binary.toUserOutput()));
266
267
268
269
270
271
272
273
274
        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);
275
            if (!value.isEmpty())
276
277
278
279
280
281
282
283
                value.chop(1); //remove '\n'
            predefinedMacros += "#define ";
            predefinedMacros += key;
            predefinedMacros += ' ';
            predefinedMacros += value;
            predefinedMacros += '\n';
        }
    }
Friedemann Kleint's avatar
Friedemann Kleint committed
284
285
    if (debug)
        qDebug() << "msvcPredefinedMacros" << predefinedMacros;
286
287
288
    return predefinedMacros;
}

Friedemann Kleint's avatar
Friedemann Kleint committed
289
290
291
// 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
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
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
311
Utils::Environment MsvcToolChain::readEnvironmentSetting(Utils::Environment& env) const
312
{
Friedemann Kleint's avatar
Friedemann Kleint committed
313
    Utils::Environment result = env;
314
    if (!QFileInfo::exists(m_vcvarsBat))
Friedemann Kleint's avatar
Friedemann Kleint committed
315
        return result;
316

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

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

Friedemann Kleint's avatar
Friedemann Kleint committed
329
330
331
332
333
334
335
336
337
338
    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;
339
340
341
342
343
344
}

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

345
MsvcToolChain::MsvcToolChain(const QString &name, const Abi &abi,
346
                             const QString &varsBat, const QString &varsBatArg, Detection d) :
347
    AbstractMsvcToolChain(Constants::MSVC_TOOLCHAIN_TYPEID, d, abi, varsBat),
SteveKing's avatar
SteveKing committed
348
    m_varsBatArg(varsBatArg)
349
350
351
{
    Q_ASSERT(!name.isEmpty());

352
    setDisplayName(name);
353
354
}

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

363
MsvcToolChain::MsvcToolChain() :
364
    AbstractMsvcToolChain(Constants::MSVC_TOOLCHAIN_TYPEID, ManualDetection)
365
366
367
368
369
370
371
372
373
374
375
376
{
}

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
377
QString MsvcToolChain::typeDisplayName() const
378
379
380
381
{
    return MsvcToolChainFactory::tr("MSVC");
}

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

419
420
QVariantMap MsvcToolChain::toMap() const
{
421
    QVariantMap data = AbstractMsvcToolChain::toMap();
SteveKing's avatar
SteveKing committed
422
    data.insert(QLatin1String(varsBatKeyC), m_vcvarsBat);
423
424
425
    if (!m_varsBatArg.isEmpty())
        data.insert(QLatin1String(varsBatArgKeyC), m_varsBatArg);
    data.insert(QLatin1String(supportedAbiKeyC), m_abi.toString());
426
427
428
429
430
431
432
    return data;
}

bool MsvcToolChain::fromMap(const QVariantMap &data)
{
    if (!ToolChain::fromMap(data))
        return false;
433
    m_vcvarsBat = QDir::fromNativeSeparators(data.value(QLatin1String(varsBatKeyC)).toString());
434
435
436
    m_varsBatArg = data.value(QLatin1String(varsBatArgKeyC)).toString();
    const QString abiString = data.value(QLatin1String(supportedAbiKeyC)).toString();
    m_abi = Abi(abiString);
437

SteveKing's avatar
SteveKing committed
438
    return !m_vcvarsBat.isEmpty() && m_abi.isValid();
439
440
}

SteveKing's avatar
SteveKing committed
441

442
443
444
445
446
447
448
ToolChainConfigWidget *MsvcToolChain::configurationWidget()
{
    return new MsvcToolChainConfigWidget(this);
}

ToolChain *MsvcToolChain::clone() const
{
449
    return new MsvcToolChain(*this);
450
451
452
453
454
455
456
}

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

MsvcToolChainConfigWidget::MsvcToolChainConfigWidget(ToolChain *tc) :
457
458
    ToolChainConfigWidget(tc),
    m_varsBatDisplayLabel(new QLabel(this))
459
{
460
    m_mainLayout->addRow(new QLabel(tc->displayName()));
461
    m_varsBatDisplayLabel->setTextInteractionFlags(Qt::TextBrowserInteraction);
462
463
    m_mainLayout->addRow(tr("Initialization:"), m_varsBatDisplayLabel);
    addErrorLabel();
464
    setFromToolChain();
465
466
}

467
void MsvcToolChainConfigWidget::setFromToolChain()
468
{
469
470
    MsvcToolChain *tc = static_cast<MsvcToolChain *>(toolChain());
    QTC_ASSERT(tc, return);
471
    QString varsBatDisplay = QDir::toNativeSeparators(tc->varsBat());
472
473
474
475
476
    if (!tc->varsBatArg().isEmpty()) {
        varsBatDisplay += QLatin1Char(' ');
        varsBatDisplay += tc->varsBatArg();
    }
    m_varsBatDisplayLabel->setText(varsBatDisplay);
477
478
479
480
481
482
}

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

483
MsvcToolChainFactory::MsvcToolChainFactory()
484
{
485
    setDisplayName(tr("MSVC"));
486
487
}

488
489
490
491
492
493
494
495
496
497
498
499
500
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);
}

501
502
QString MsvcToolChainFactory::vcVarsBatFor(const QString &basePath, const QString &toolchainName)
{
503
504
    if (toolchainName.startsWith(QLatin1Char('/'))) // windows sdk case, all use SetEnv.cmd
        return basePath + QLatin1String("/SetEnv.cmd");
505
506
    if (toolchainName == QLatin1String("x86"))
        return basePath + QLatin1String("/bin/vcvars32.bat");
507
508
    if (toolchainName == QLatin1String("amd64_arm"))
        return basePath + QLatin1String("/bin/amd64_arm/vcvarsamd64_arm.bat");
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
    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();
}

525
526
527
528
529
QString MsvcToolChainFactory::vcVarsBatFor(const QString &basePath, MsvcToolChain::Platform platform)
{
    return vcVarsBatFor(basePath, platformName(platform));
}

530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
static ToolChain *findOrCreateToolChain(const QList<ToolChain *> &alreadyKnown,
                                        const QString &name, const Abi &abi,
                                        const QString &varsBat, const QString &varsBatArg,
                                        ToolChain::Detection d = ToolChain::ManualDetection)
{
    ToolChain *tc = Utils::findOrDefault(alreadyKnown,
                                         [&varsBat, &varsBatArg](ToolChain *tc) -> bool {
                                              if (tc->typeId() != Constants::MSVC_TOOLCHAIN_TYPEID)
                                                  return false;
                                              auto mtc = static_cast<MsvcToolChain *>(tc);
                                              return mtc->varsBat() == varsBat
                                                      && mtc->varsBatArg() == varsBatArg;
                                         });
    if (!tc)
        tc = new MsvcToolChain(name, abi, varsBat, varsBatArg, d);
    return tc;
}

548
QList<ToolChain *> MsvcToolChainFactory::autoDetect(const QList<ToolChain *> &alreadyKnown)
549
550
551
552
{
    QList<ToolChain *> results;

    // 1) Installed SDKs preferred over standalone Visual studio
553
    const QSettings sdkRegistry(QLatin1String("HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Microsoft SDKs\\Windows"),
554
555
556
557
558
559
560
561
562
                                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
563
564
565
566
567
            QDir dir(folder);
            if (!dir.cd(QLatin1String("bin")))
                continue;
            QFileInfo fi(dir, QLatin1String("SetEnv.cmd"));
            if (!fi.exists())
568
                continue;
569

Yuchen Deng's avatar
Yuchen Deng committed
570
            QList<ToolChain *> tmp;
571
572
573
574
            tmp.append(findOrCreateToolChain(alreadyKnown,
                                             generateDisplayName(name, MsvcToolChain::WindowsSDK, MsvcToolChain::x86),
                                             findAbiOfMsvc(MsvcToolChain::WindowsSDK, MsvcToolChain::x86, sdkKey),
                                             fi.absoluteFilePath(), QLatin1String("/x86"), ToolChain::AutoDetection));
575
            // Add all platforms, cross-compiler is automatically selected by SetEnv.cmd if needed
576
577
578
579
580
581
582
583
            tmp.append(findOrCreateToolChain(alreadyKnown,
                                             generateDisplayName(name, MsvcToolChain::WindowsSDK, MsvcToolChain::amd64),
                                             findAbiOfMsvc(MsvcToolChain::WindowsSDK, MsvcToolChain::amd64, sdkKey),
                                             fi.absoluteFilePath(), QLatin1String("/x64"), ToolChain::AutoDetection));
            tmp.append(findOrCreateToolChain(alreadyKnown,
                                             generateDisplayName(name, MsvcToolChain::WindowsSDK, MsvcToolChain::ia64),
                                             findAbiOfMsvc(MsvcToolChain::WindowsSDK, MsvcToolChain::ia64, sdkKey),
                                             fi.absoluteFilePath(), QLatin1String("/ia64"), ToolChain::AutoDetection));
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
            // 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;
605
606
        if (!checkForVisualStudioInstallation(vsName))
            continue;
607

608
        QString path = QDir::fromNativeSeparators(vsRegistry.value(vsName).toString());
609
610
        if (path.endsWith(QLatin1Char('/')))
            path.chop(1);
611
        const int version = vsName.left(dotPos).toInt();
612
        const QString vcvarsAllbat = path + QLatin1String("/vcvarsall.bat");
Daniel Teske's avatar
Daniel Teske committed
613
        if (QFileInfo(vcvarsAllbat).isFile()) {
614
            QList<MsvcToolChain::Platform> platforms; // prioritized list
615
616
617
            // x86_arm was put before amd64_arm as a workaround for auto detected windows phone
            // toolchains. As soon as windows phone builds support x64 cross builds, this change
            // can be reverted.
618
619
            platforms << MsvcToolChain::x86
                      << MsvcToolChain::amd64 << MsvcToolChain::x86_amd64
620
                      << MsvcToolChain::arm << MsvcToolChain::x86_arm << MsvcToolChain::amd64_arm
621
622
623
624
                      << MsvcToolChain::ia64 << MsvcToolChain::x86_ia64;
            foreach (const MsvcToolChain::Platform &platform, platforms) {
                if (hostSupportsPlatform(platform)
                        && QFileInfo(vcVarsBatFor(path, platform)).isFile()) {
625
626
                    results.append(findOrCreateToolChain(
                                       alreadyKnown,
627
628
                                       generateDisplayName(vsName, MsvcToolChain::VS, platform),
                                       findAbiOfMsvc(MsvcToolChain::VS, platform, vsName),
629
                                       vcvarsAllbat, platformName(platform),
630
                                       ToolChain::AutoDetection));
Daniel Teske's avatar
Daniel Teske committed
631
632
                }
            }
633
        } else {
Daniel Teske's avatar
Daniel Teske committed
634
            qWarning("Unable to find MSVC setup script %s in version %d", qPrintable(vcvarsAllbat), version);
635
636
        }
    }
SteveKing's avatar
SteveKing committed
637

638
639
640
    return results;
}

641
642
643
644
645
646
647
648
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;
}

649
650
bool MsvcToolChainFactory::canRestore(const QVariantMap &data)
{
651
    return typeIdFromMap(data) == Constants::MSVC_TOOLCHAIN_TYPEID;
652
653
}

654
655
} // namespace Internal
} // namespace ProjectExplorer