msvctoolchain.cpp 38.5 KB
Newer Older
hjk's avatar
hjk committed
1
/****************************************************************************
2
**
3 4
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://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
12 13 14
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
15
**
16 17 18 19 20 21 22
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
23
**
hjk's avatar
hjk committed
24
****************************************************************************/
25 26

#include "msvctoolchain.h"
27

28 29 30
#include "msvcparser.h"
#include "projectexplorerconstants.h"

31
#include <utils/algorithm.h>
32
#include <utils/qtcfallthrough.h>
33
#include <utils/synchronousprocess.h>
34
#include <utils/winutils.h>
SteveKing's avatar
SteveKing committed
35
#include <utils/qtcassert.h>
Daniel Teske's avatar
Daniel Teske committed
36
#include <utils/hostosinfo.h>
37
#include <utils/temporarydirectory.h>
38

39
#include <QDir>
40
#include <QFileInfo>
41 42
#include <QProcess>
#include <QSettings>
43 44
#include <QVector>
#include <QVersionNumber>
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
namespace ProjectExplorer {
namespace Internal {

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

63 64 65 66 67 68 69 70 71
struct MsvcPlatform {
    MsvcToolChain::Platform platform;
    const char *name;
    const char *prefix; // VS up until 14.0 (MSVC2015)
    const char *bat;
};

const MsvcPlatform platforms[] =
{
72 73 74 75 76 77 78 79 80
    {MsvcToolChain::x86, "x86", "/bin", "vcvars32.bat"},
    {MsvcToolChain::amd64, "amd64", "/bin/amd64", "vcvars64.bat"},
    {MsvcToolChain::x86_amd64, "x86_amd64", "/bin/x86_amd64", "vcvarsx86_amd64.bat"},
    {MsvcToolChain::ia64, "ia64", "/bin/ia64", "vcvars64.bat"},
    {MsvcToolChain::x86_ia64, "x86_ia64", "/bin/x86_ia64", "vcvarsx86_ia64.bat"},
    {MsvcToolChain::arm, "arm", "/bin/arm", "vcvarsarm.bat"},
    {MsvcToolChain::x86_arm, "x86_arm", "/bin/x86_arm", "vcvarsx86_arm.bat"},
    {MsvcToolChain::amd64_arm, "amd64_arm", "/bin/amd64_arm", "vcvarsamd64_arm.bat"},
    {MsvcToolChain::amd64_x86, "amd64_x86", "/bin/amd64_x86", "vcvarsamd64_x86.bat"}
81 82 83 84 85 86 87 88 89 90 91
};

static const MsvcPlatform *platformEntry(MsvcToolChain::Platform t)
{
    for (const MsvcPlatform &p : platforms) {
        if (p.platform == t)
            return &p;
    }
    return nullptr;
}

92 93
static QString platformName(MsvcToolChain::Platform t)
{
94 95
    if (const MsvcPlatform *p = platformEntry(t))
        return QLatin1String(p->name);
96 97 98
    return QString();
}

99 100 101 102
static bool hostSupportsPlatform(MsvcToolChain::Platform platform)
{
    switch (Utils::HostOsInfo::hostArchitecture()) {
    case Utils::HostOsInfo::HostArchitectureAMD64:
103 104
        if (platform == MsvcToolChain::amd64 || platform == MsvcToolChain::amd64_arm
            || platform == MsvcToolChain::amd64_x86)
105
            return true;
106
        Q_FALLTHROUGH(); // all x86 toolchains are also working on an amd64 host
107 108 109 110 111 112 113 114 115 116 117 118
    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;
    }
}

119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190
static QString fixRegistryPath(const QString &path)
{
    QString result = QDir::fromNativeSeparators(path);
    if (result.endsWith(QLatin1Char('/')))
        result.chop(1);
    return result;
}

struct VisualStudioInstallation
{
    QString vsName;
    QVersionNumber version;
    QString path; // Main installation path
    QString vcVarsPath; // Path under which the various vc..bat are to be found
    QString vcVarsAll;
};

QDebug operator<<(QDebug d, const VisualStudioInstallation &i)
{
    QDebugStateSaver saver(d);
    d.noquote();
    d.nospace();
    d << "VisualStudioInstallation(\"" << i.vsName << "\", v=" << i.version
      << ", path=\"" << QDir::toNativeSeparators(i.path)
      << "\", vcVarsPath=\"" << QDir::toNativeSeparators(i.vcVarsPath)
      << "\", vcVarsAll=\"" << QDir::toNativeSeparators(i.vcVarsAll) << "\")";
    return d;
}

static QVector<VisualStudioInstallation> detectVisualStudio()
{
    QVector<VisualStudioInstallation> result;
#ifdef Q_OS_WIN64
    const QString keyRoot = QStringLiteral("HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\Microsoft\\VisualStudio\\SxS\\");
#else
    const QString keyRoot = QStringLiteral("HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\SxS\\");
#endif
    QSettings vsRegistry(keyRoot + QStringLiteral("VS7"), QSettings::NativeFormat);
    QScopedPointer<QSettings> vcRegistry;
    const QString vcvarsall = QStringLiteral("/vcvarsall.bat");
    foreach (const QString &vsName, vsRegistry.allKeys()) {
        const QVersionNumber version = QVersionNumber::fromString(vsName);
        if (!version.isNull()) {
            VisualStudioInstallation installation;
            installation.vsName = vsName;
            installation.version = version;
            if (version.majorVersion() > 14) {
                // Starting with 15 (MSVC2017): There are no more VC entries,
                // build path from VS installation
                installation.path = fixRegistryPath(vsRegistry.value(vsName).toString());
                installation.vcVarsPath = installation.path + QStringLiteral("/VC/Auxiliary/Build");
                installation.vcVarsAll =  installation.vcVarsPath + vcvarsall;
            } else {
                // Up to 14 (MSVC2015): Look up via matching VC entry
                if (vcRegistry.isNull())
                    vcRegistry.reset(new QSettings(keyRoot + QStringLiteral("VC7"), QSettings::NativeFormat));
                installation.path = fixRegistryPath(vcRegistry->value(vsName).toString());
                installation.vcVarsPath = installation.path;
                installation.vcVarsAll = installation.vcVarsPath + vcvarsall;
            }
            if (QFileInfo(installation.vcVarsAll).isFile()) {
                result.append(installation);
            } else {
                qWarning().noquote() << "Unable to find MSVC setup script "
                    << QDir::toNativeSeparators(installation.vcVarsPath) << " in version "
                    << version;
            }
        }
    }
    return result;
}

191 192 193 194 195 196 197 198
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)
    {
199
    case MsvcToolChain::x86:
200
    case MsvcToolChain::amd64_x86:
201 202
        wordWidth = 32;
        break;
203 204
    case MsvcToolChain::ia64:
    case MsvcToolChain::x86_ia64:
205 206
        arch = Abi::ItaniumArchitecture;
        break;
207 208
    case MsvcToolChain::amd64:
    case MsvcToolChain::x86_amd64:
Daniel Teske's avatar
Daniel Teske committed
209
        break;
210 211 212
    case MsvcToolChain::arm:
    case MsvcToolChain::x86_arm:
    case MsvcToolChain::amd64_arm:
Daniel Teske's avatar
Daniel Teske committed
213 214
        arch = Abi::ArmArchitecture;
        wordWidth = 32;
215 216 217 218 219
        break;
    };

    QString msvcVersionString = version;
    if (type == MsvcToolChain::WindowsSDK) {
Daniel Teske's avatar
Daniel Teske committed
220
        if (version == QLatin1String("v7.0") || version.startsWith(QLatin1String("6.")))
221
            msvcVersionString = QLatin1String("9.0");
Daniel Teske's avatar
Daniel Teske committed
222 223
        else if (version == QLatin1String("v7.0A") || version == QLatin1String("v7.1"))
            msvcVersionString = QLatin1String("10.0");
224
    }
225 226 227
    if (msvcVersionString.startsWith(QLatin1String("15.")))
        flavor = Abi::WindowsMsvc2017Flavor;
    else if (msvcVersionString.startsWith(QLatin1String("14.")))
Joerg Bornemann's avatar
Joerg Bornemann committed
228 229
        flavor = Abi::WindowsMsvc2015Flavor;
    else if (msvcVersionString.startsWith(QLatin1String("12.")))
Yuchen Deng's avatar
Yuchen Deng committed
230 231
        flavor = Abi::WindowsMsvc2013Flavor;
    else if (msvcVersionString.startsWith(QLatin1String("11.")))
Tobias Hunger's avatar
Tobias Hunger committed
232 233
        flavor = Abi::WindowsMsvc2012Flavor;
    else if (msvcVersionString.startsWith(QLatin1String("10.")))
234
        flavor = Abi::WindowsMsvc2010Flavor;
235
    else if (msvcVersionString.startsWith(QLatin1String("9.")))
236 237 238
        flavor = Abi::WindowsMsvc2008Flavor;
    else
        flavor = Abi::WindowsMsvc2005Flavor;
Tobias Hunger's avatar
Tobias Hunger committed
239 240 241 242 243
    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;
244 245
}

246 247 248 249 250 251
static QString generateDisplayName(const QString &name,
                                   MsvcToolChain::Type t,
                                   MsvcToolChain::Platform p)
{
    if (t == MsvcToolChain::WindowsSDK) {
        QString sdkName = name;
252
        sdkName += QString::fromLatin1(" (%1)").arg(platformName(p));
253 254 255 256 257
        return sdkName;
    }
    // Comes as "9.0" from the registry
    QString vcName = QLatin1String("Microsoft Visual C++ Compiler ");
    vcName += name;
258
    vcName += QString::fromLatin1(" (%1)").arg(platformName(p));
259 260 261
    return vcName;
}

262 263 264 265 266 267
static QByteArray msvcCompilationDefine(const char *def)
{
    const QByteArray macro(def);
    return "#if defined(" + macro + ")\n__PPOUT__(" + macro + ")\n#endif\n";
}

268 269
static QByteArray msvcCompilationFile()
{
270 271
    static const char* macros[] = {
        "_ATL_VER",
272 273 274
        "__ATOM__",
        "__AVX__",
        "__AVX2__",
275 276
        "_CHAR_UNSIGNED",
        "__CLR_VER",
277 278
        "_CMMN_INTRIN_FUNC",
        "_CONTROL_FLOW_GUARD",
279 280 281
        "__COUNTER__",
        "__cplusplus",
        "__cplusplus_cli",
282
        "__cplusplus_winrt",
283 284 285 286 287 288
        "_CPPLIB_VER",
        "_CPPRTTI",
        "_CPPUNWIND",
        "__DATE__",
        "_DEBUG",
        "_DLL",
289 290
        "__FILE__",
        "__func__",
291 292 293 294
        "__FUNCDNAME__",
        "__FUNCSIG__",
        "__FUNCTION__",
        "_INTEGRAL_MAX_BITS",
295 296 297 298
        "__INTELLISENSE__",
        "_ISO_VOLATILE",
        "_KERNEL_MODE",
        "__LINE__",
299 300
        "_M_AAMD64",
        "_M_ALPHA",
301
        "_M_AMD64",
302
        "_MANAGED",
303 304 305 306 307 308
        "_M_ARM",
        "_M_ARM64",
        "_M_ARM_ARMV7VE",
        "_M_ARM_FP",
        "_M_ARM_NT",
        "_M_ARMT",
309 310 311 312
        "_M_CEE",
        "_M_CEE_PURE",
        "_M_CEE_SAFE",
        "_MFC_VER",
313 314 315 316
        "_M_FP_EXCEPT",
        "_M_FP_FAST",
        "_M_FP_PRECISE",
        "_M_FP_STRICT",
317 318 319 320 321 322 323 324 325 326
        "_M_IA64",
        "_M_IX86",
        "_M_IX86_FP",
        "_M_MPPC",
        "_M_MRX000",
        "_M_PPC",
        "_MSC_BUILD",
        "_MSC_EXTENSIONS",
        "_MSC_FULL_VER",
        "_MSC_VER",
327
        "_MSVC_LANG",
328 329
        "__MSVC_RUNTIME_CHECKS",
        "_MT",
330
        "_M_THUMB",
331 332 333
        "_M_X64",
        "_NATIVE_WCHAR_T_DEFINED",
        "_OPENMP",
334 335 336 337
        "_PREFAST_",
        "__STDC__",
        "__STDC_HOSTED__",
        "__STDCPP_THREADS__",
338 339 340 341 342 343 344
        "__TIME__",
        "__TIMESTAMP__",
        "_VC_NODEFAULTLIB",
        "_WCHAR_T_DEFINED",
        "_WIN32",
        "_WIN32_WCE",
        "_WIN64",
345
        "_WINRT_DLL",
346 347 348
        "_Wp64",
        0
    };
349
    QByteArray file = "#define __PPOUT__(x) V##x=x\n\n";
350 351
    for (int i = 0; macros[i] != 0; ++i)
        file += msvcCompilationDefine(macros[i]);
352 353 354 355 356
    file += "\nvoid main(){}\n\n";
    return file;
}

// Run MSVC 'cl' compiler to obtain #defines.
357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 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 406 407 408 409 410 411 412
// This function must be thread-safe!
//
// Some notes regarding the used approach:
//
// It seems that there is no reliable way to get all the
// predefined macros for a cl invocation. The following two
// approaches are unfortunately limited since both lead to an
// incomplete list of actually predefined macros and come with
// other problems, too.
//
// 1) Maintain a list of predefined macros from the official
//    documentation (for MSVC2015, e.g. [1]). Feed cl with a
//    temporary file that queries the values of those macros.
//
//    Problems:
//     * Maintaining that list.
//     * The documentation is incomplete, we do not get all
//       predefined macros. E.g. the cl from MSVC2015, set up
//       with "vcvars.bat x86_arm", predefines among others
//       _M_ARMT, but that's not reflected in the
//       documentation.
//
// 2) Run cl with the undocumented options /B1 and /Bx, as
//    described in [2].
//
//    Note: With qmake from Qt >= 5.8 it's possible to print
//    the macros formatted as preprocessor code in an easy to
//    read/compare/diff way:
//
//      > cl /nologo /c /TC /B1 qmake NUL
//      > cl /nologo /c /TP /Bx qmake NUL
//
//    Problems:
//     * Using undocumented options.
//     * Resulting macros are incomplete.
//       For example, the nowadays default option /Zc:wchar_t
//       predefines _WCHAR_T_DEFINED, but this is not reflected
//       with this approach.
//
//       To work around this we would need extra cl invocations
//       to get the actual values of the missing macros
//       (approach 1).
//
// Currently we combine both approaches in this way:
//  * As base, maintain the list from the documentation and
//    update it once a new MSVC version is released.
//  * Enrich it with macros that we discover with approach 2
//    once a new MSVC version is released.
//  * Enrich it further with macros that are not covered with
//    the above points.
//
// TODO: Update the predefined macros for MSVC 2017 once the
//       page exists.
//
// [1] https://msdn.microsoft.com/en-us/library/b0084kay.aspx
// [2] http://stackoverflow.com/questions/3665537/how-to-find-out-cl-exes-built-in-macros
413 414
QByteArray MsvcToolChain::msvcPredefinedMacros(const QStringList cxxflags,
                                               const Utils::Environment &env) const
415
{
416
    QByteArray predefinedMacros;
417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437

    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';
438 439
        } else if (arg.startsWith(QLatin1String("-I"))) {
            // Include paths should not have any effect on defines
440 441 442 443
        } else {
            toProcess.append(arg);
        }
    }
444

445
    Utils::TempFileSaver saver(Utils::TemporaryDirectory::masterDirectoryPath() + "/envtestXXXXXX.cpp");
446 447 448 449
    saver.write(msvcCompilationFile());
    if (!saver.finalize()) {
        qWarning("%s: %s", Q_FUNC_INFO, qPrintable(saver.errorString()));
        return predefinedMacros;
450
    }
451
    Utils::SynchronousProcess cpp;
Friedemann Kleint's avatar
Friedemann Kleint committed
452
    cpp.setEnvironment(env.toStringList());
453
    cpp.setWorkingDirectory(Utils::TemporaryDirectory::masterDirectoryPath());
454
    QStringList arguments;
455
    const Utils::FileName binary = env.searchInPath(QLatin1String("cl.exe"));
Friedemann Kleint's avatar
Friedemann Kleint committed
456 457 458 459 460
    if (binary.isEmpty()) {
        qWarning("%s: The compiler binary cl.exe could not be found in the path.", Q_FUNC_INFO);
        return predefinedMacros;
    }

461 462
    if (language() == ProjectExplorer::Constants::C_LANGUAGE_ID)
        arguments << QLatin1String("/TC");
463
    arguments << toProcess << QLatin1String("/EP") << QDir::toNativeSeparators(saver.fileName());
464
    Utils::SynchronousProcessResponse response = cpp.runBlocking(binary.toString(), arguments);
465 466
    if (response.result != Utils::SynchronousProcessResponse::Finished ||
            response.exitCode != 0)
467 468
        return predefinedMacros;

469 470
    const QStringList output = Utils::filtered(response.stdOut().split('\n'),
                                               [](const QString &s) { return s.startsWith('V'); });
471
    foreach (const QString& line, output) {
472 473 474 475 476 477 478 479
        QStringList split = line.split('=');
        const QString key = split.at(0).mid(1);
        QString value = split.at(1);
        predefinedMacros += "#define ";
        predefinedMacros += key.toUtf8();
        predefinedMacros += ' ';
        predefinedMacros += value.toUtf8();
        predefinedMacros += '\n';
480
    }
Friedemann Kleint's avatar
Friedemann Kleint committed
481 482
    if (debug)
        qDebug() << "msvcPredefinedMacros" << predefinedMacros;
483 484 485
    return predefinedMacros;
}

Friedemann Kleint's avatar
Friedemann Kleint committed
486 487 488
// 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
489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507
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;
}

508
QList<Utils::EnvironmentItem> MsvcToolChain::environmentModifications() const
509
{
510 511
    const Utils::Environment inEnv = Utils::Environment::systemEnvironment();
    Utils::Environment outEnv;
SteveKing's avatar
SteveKing committed
512
    QMap<QString, QString> envPairs;
513 514
    if (!generateEnvironmentSettings(inEnv, m_vcvarsBat, m_varsBatArg, envPairs))
        return QList<Utils::EnvironmentItem>();
Friedemann Kleint's avatar
Friedemann Kleint committed
515

SteveKing's avatar
SteveKing committed
516
    // Now loop through and process them
517 518
    for (auto envIter = envPairs.cbegin(), eend = envPairs.cend(); envIter != eend; ++envIter) {
        const QString expandedValue = winExpandDelayedEnvReferences(envIter.value(), inEnv);
SteveKing's avatar
SteveKing committed
519
        if (!expandedValue.isEmpty())
520
            outEnv.set(envIter.key(), expandedValue);
521 522
    }

Friedemann Kleint's avatar
Friedemann Kleint committed
523
    if (debug) {
524 525
        const QStringList newVars = outEnv.toStringList();
        const QStringList oldVars = inEnv.toStringList();
Friedemann Kleint's avatar
Friedemann Kleint committed
526 527 528 529 530 531
        QDebug nsp = qDebug().nospace();
        foreach (const QString &n, newVars) {
            if (!oldVars.contains(n))
                nsp << n << '\n';
        }
    }
532

533
    QList<Utils::EnvironmentItem> diff = inEnv.diff(outEnv, true);
534 535 536 537 538 539 540 541 542 543 544 545 546 547 548
    for (int i = diff.size() - 1; i >= 0; --i) {
        if (diff.at(i).name.startsWith(QLatin1Char('='))) { // Exclude "=C:", "=EXITCODE"
            diff.removeAt(i);
        }
    }

    return diff;
}

Utils::Environment MsvcToolChain::readEnvironmentSetting(const Utils::Environment& env) const
{
    if (m_environmentModifications.isEmpty())
        m_environmentModifications = environmentModifications();
    Utils::Environment result = env;
    result.modify(m_environmentModifications);
Friedemann Kleint's avatar
Friedemann Kleint committed
549
    return result;
550 551 552 553 554 555
}

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

556
MsvcToolChain::MsvcToolChain(const QString &name, const Abi &abi,
557
                             const QString &varsBat, const QString &varsBatArg, Core::Id l,
558 559
                             Detection d) :
    MsvcToolChain(Constants::MSVC_TOOLCHAIN_TYPEID, name, abi, varsBat, varsBatArg, l, d)
560
{ }
561 562

MsvcToolChain::MsvcToolChain(Core::Id typeId, const QString &name, const Abi &abi,
563
                             const QString &varsBat, const QString &varsBatArg, Core::Id l,
564
                             Detection d) : AbstractMsvcToolChain(typeId, l, d, abi, varsBat),
565
    m_varsBatArg(varsBatArg)
566 567 568
{
    Q_ASSERT(!name.isEmpty());

569
    setDisplayName(name);
570 571
}

572
MsvcToolChain::MsvcToolChain(Core::Id typeId) : AbstractMsvcToolChain(typeId, ManualDetection)
573
{ }
574

575
MsvcToolChain::MsvcToolChain() : MsvcToolChain(Constants::MSVC_TOOLCHAIN_TYPEID)
576
{ }
577

Tobias Hunger's avatar
Tobias Hunger committed
578
QString MsvcToolChain::typeDisplayName() const
579 580 581 582
{
    return MsvcToolChainFactory::tr("MSVC");
}

583
Utils::FileNameList MsvcToolChain::suggestedMkspecList() const
584
{
585 586
    Utils::FileNameList result;
    result << Utils::FileName::fromLatin1("win32-msvc"); // Common MSVC mkspec introduced in 5.8.1
Tobias Hunger's avatar
Tobias Hunger committed
587
    switch (m_abi.osFlavor()) {
588
    case Abi::WindowsMsvc2005Flavor:
589 590
        result << Utils::FileName::fromLatin1("win32-msvc2005");
        break;
591
    case Abi::WindowsMsvc2008Flavor:
592 593
        result << Utils::FileName::fromLatin1("win32-msvc2008");
        break;
594
    case Abi::WindowsMsvc2010Flavor:
595 596
        result << Utils::FileName::fromLatin1("win32-msvc2010");
        break;
597
    case Abi::WindowsMsvc2012Flavor:
598
        result << Utils::FileName::fromLatin1("win32-msvc2012")
599
            << Utils::FileName::fromLatin1("win32-msvc2010");
600
        break;
601
    case Abi::WindowsMsvc2013Flavor:
602
        result << Utils::FileName::fromLatin1("win32-msvc2013")
603 604 605 606 607
            << 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")
608 609
            << Utils::FileName::fromLatin1("win32-msvc2012")
            << Utils::FileName::fromLatin1("win32-msvc2010");
610
        break;
Joerg Bornemann's avatar
Joerg Bornemann committed
611
    case Abi::WindowsMsvc2015Flavor:
612
        result << Utils::FileName::fromLatin1("win32-msvc2015")
Joerg Bornemann's avatar
Joerg Bornemann committed
613 614 615 616 617
            << 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");
618
        break;
619
    case Abi::WindowsMsvc2017Flavor:
620 621 622 623
        result << Utils::FileName::fromLatin1("win32-msvc2017")
               << Utils::FileName::fromLatin1("winrt-arm-msvc2017")
               << Utils::FileName::fromLatin1("winrt-x86-msvc2017")
               << Utils::FileName::fromLatin1("winrt-x64-msvc2017");
624
        break;
Tobias Hunger's avatar
Tobias Hunger committed
625
    default:
626
        result.clear();
Tobias Hunger's avatar
Tobias Hunger committed
627 628
        break;
    }
629
    return result;
630 631
}

632 633
QVariantMap MsvcToolChain::toMap() const
{
634
    QVariantMap data = AbstractMsvcToolChain::toMap();
SteveKing's avatar
SteveKing committed
635
    data.insert(QLatin1String(varsBatKeyC), m_vcvarsBat);
636 637 638
    if (!m_varsBatArg.isEmpty())
        data.insert(QLatin1String(varsBatArgKeyC), m_varsBatArg);
    data.insert(QLatin1String(supportedAbiKeyC), m_abi.toString());
639 640 641 642 643
    return data;
}

bool MsvcToolChain::fromMap(const QVariantMap &data)
{
644
    if (!AbstractMsvcToolChain::fromMap(data))
645
        return false;
646
    m_vcvarsBat = QDir::fromNativeSeparators(data.value(QLatin1String(varsBatKeyC)).toString());
647 648 649
    m_varsBatArg = data.value(QLatin1String(varsBatArgKeyC)).toString();
    const QString abiString = data.value(QLatin1String(supportedAbiKeyC)).toString();
    m_abi = Abi(abiString);
650

SteveKing's avatar
SteveKing committed
651
    return !m_vcvarsBat.isEmpty() && m_abi.isValid();
652 653
}

SteveKing's avatar
SteveKing committed
654

655 656 657 658 659 660 661
ToolChainConfigWidget *MsvcToolChain::configurationWidget()
{
    return new MsvcToolChainConfigWidget(this);
}

ToolChain *MsvcToolChain::clone() const
{
662
    return new MsvcToolChain(*this);
663 664 665
}

// --------------------------------------------------------------------------
666 667 668
// MsvcBasedToolChainConfigWidget: Creates a simple GUI without error label
// to display name and varsBat. Derived classes should add the error label and
// call setFromMsvcToolChain().
669 670
// --------------------------------------------------------------------------

671 672 673 674
MsvcBasedToolChainConfigWidget::MsvcBasedToolChainConfigWidget(ToolChain *tc) :
    ToolChainConfigWidget(tc),
    m_nameDisplayLabel(new QLabel(this)),
    m_varsBatDisplayLabel(new QLabel(this))
675
{
676 677
    m_nameDisplayLabel->setTextInteractionFlags(Qt::TextBrowserInteraction);
    m_mainLayout->addRow(m_nameDisplayLabel);
678
    m_varsBatDisplayLabel->setTextInteractionFlags(Qt::TextBrowserInteraction);
679
    m_mainLayout->addRow(tr("Initialization:"), m_varsBatDisplayLabel);
680 681
}

682
void MsvcBasedToolChainConfigWidget::setFromMsvcToolChain()
683
{
684
    const MsvcToolChain *tc = static_cast<const MsvcToolChain *>(toolChain());
685
    QTC_ASSERT(tc, return);
686
    m_nameDisplayLabel->setText(tc->displayName());
687
    QString varsBatDisplay = QDir::toNativeSeparators(tc->varsBat());
688 689 690 691 692
    if (!tc->varsBatArg().isEmpty()) {
        varsBatDisplay += QLatin1Char(' ');
        varsBatDisplay += tc->varsBatArg();
    }
    m_varsBatDisplayLabel->setText(varsBatDisplay);
693 694
}

695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736
// --------------------------------------------------------------------------
// MsvcToolChainConfigWidget
// --------------------------------------------------------------------------

MsvcToolChainConfigWidget::MsvcToolChainConfigWidget(ToolChain *tc) :
    MsvcBasedToolChainConfigWidget(tc)
{
    addErrorLabel();
    setFromMsvcToolChain();
}

// --------------------------------------------------------------------------
// ClangClToolChainConfigWidget
// --------------------------------------------------------------------------

ClangClToolChainConfigWidget::ClangClToolChainConfigWidget(ToolChain *tc)
    : MsvcBasedToolChainConfigWidget(tc)
    , m_llvmDirLabel(new QLabel(this))
{
    m_llvmDirLabel->setTextInteractionFlags(Qt::TextBrowserInteraction);
    m_mainLayout->addRow(tr("LLVM:"), m_llvmDirLabel);
    addErrorLabel();
    setFromClangClToolChain();
}

void ClangClToolChainConfigWidget::setFromClangClToolChain()
{
    setFromMsvcToolChain();
    const ClangClToolChain *tc = static_cast<const ClangClToolChain *>(toolChain());
    QTC_ASSERT(tc, return);
    m_llvmDirLabel->setText(QDir::toNativeSeparators(tc-> llvmDir()));
}

// --------------------------------------------------------------------------
// ClangClToolChain, piggy-backing on MSVC2015 and providing the compiler
// clang-cl.exe as a [to some extent] compatible drop-in replacement for cl.
// --------------------------------------------------------------------------

static const char clangClBinary[] = "clang-cl.exe";

ClangClToolChain::ClangClToolChain(const QString &name, const QString &llvmDir,
                                   const Abi &abi,
737
                                   const QString &varsBat, const QString &varsBatArg, Core::Id language,
738
                                   Detection d)
739
    : MsvcToolChain(Constants::CLANG_CL_TOOLCHAIN_TYPEID, name, abi, varsBat, varsBatArg, language, d)
740 741
    , m_llvmDir(llvmDir)
    , m_compiler(Utils::FileName::fromString(m_llvmDir + QStringLiteral("/bin/") + QLatin1String(clangClBinary)))
742
{ }
743

744 745
ClangClToolChain::ClangClToolChain() : MsvcToolChain(Constants::CLANG_CL_TOOLCHAIN_TYPEID)
{ }
746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803

bool ClangClToolChain::isValid() const
{
    return MsvcToolChain::isValid() && m_compiler.exists();
}

void ClangClToolChain::addToEnvironment(Utils::Environment &env) const
{
    MsvcToolChain::addToEnvironment(env);
    env.prependOrSetPath(m_llvmDir + QStringLiteral("/bin"));
}

QString ClangClToolChain::typeDisplayName() const
{
    return QCoreApplication::translate("ProjectExplorer::ClangToolChainFactory", "Clang");
}

QList<Utils::FileName> ClangClToolChain::suggestedMkspecList() const
{
    const QString mkspec = QLatin1String("win32-clang-") + Abi::toString(targetAbi().osFlavor());
    return QList<Utils::FileName>{Utils::FileName::fromString(mkspec)};
}

IOutputParser *ClangClToolChain::outputParser() const
{
    return new ClangClParser;
}

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

static inline QString llvmDirKey() { return QStringLiteral("ProjectExplorer.ClangClToolChain.LlvmDir"); }

QVariantMap ClangClToolChain::toMap() const
{
    QVariantMap result = MsvcToolChain::toMap();
    result.insert(llvmDirKey(), m_llvmDir);
    return result;
}

bool ClangClToolChain::fromMap(const QVariantMap &data)
{
    if (!MsvcToolChain::fromMap(data))
        return false;
    const QString llvmDir = data.value(llvmDirKey()).toString();
    if (llvmDir.isEmpty())
        return false;
    m_llvmDir = llvmDir;
    return true;
}

ToolChainConfigWidget *ClangClToolChain::configurationWidget()
{
    return new ClangClToolChainConfigWidget(this);
}

804 805 806 807
// --------------------------------------------------------------------------
// MsvcToolChainFactory
// --------------------------------------------------------------------------

808
MsvcToolChainFactory::MsvcToolChainFactory()
809
{
810
    setDisplayName(tr("MSVC"));
811 812
}

813
QSet<Core::Id> MsvcToolChainFactory::supportedLanguages() const
814
{
815
    return {Constants::C_LANGUAGE_ID, Constants::CXX_LANGUAGE_ID};
816 817
}

818 819
QString MsvcToolChainFactory::vcVarsBatFor(const QString &basePath, MsvcToolChain::Platform platform,
                                           const QVersionNumber &v)
820
{
821 822 823 824 825 826 827 828 829 830
    QString result;
    if (const MsvcPlatform *p = platformEntry(platform)) {
        result += basePath;
        // Starting with 15.0 (MSVC2017), the .bat are in one folder.
        if (v.majorVersion() <= 14)
            result += QLatin1String(p->prefix);
        result += QLatin1Char('/');
        result += QLatin1String(p->bat);
    }
    return result;
831 832
}

833 834 835 836 837 838 839
static QList<ToolChain *> findOrCreateToolChain(
        const QList<ToolChain *> &alreadyKnown,
        const QString &name, const Abi &abi,
        const QString &varsBat, const QString &varsBatArg,
        ToolChain::Detection d = ToolChain::ManualDetection)
{
    QList<ToolChain *> res;
840
    for (auto language: {Constants::C_LANGUAGE_ID, Constants::CXX_LANGUAGE_ID}) {
841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858
        ToolChain *tc = Utils::findOrDefault(
                    alreadyKnown,
                    [&varsBat, &varsBatArg, &abi, &language](ToolChain *tc) -> bool {
            if (tc->typeId() != Constants::MSVC_TOOLCHAIN_TYPEID)
                return false;
            if (tc->targetAbi() != abi)
                return false;
            if (tc->language() != language)
                return false;
            auto mtc = static_cast<MsvcToolChain *>(tc);
            return mtc->varsBat() == varsBat
                    && mtc->varsBatArg() == varsBatArg;
        });
        if (!tc)
            tc = new MsvcToolChain(name, abi, varsBat, varsBatArg, language, d);
        res << tc;
    }
    return res;
859 860
}

861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893
// Detect build tools introduced with MSVC2015
static void detectCppBuildTools(QList<ToolChain *> *list)
{
    struct Entry {
        const char *postFix;
        const char *varsBatArg;
        Abi::Architecture architecture;
        Abi::BinaryFormat format;
        unsigned char wordSize;
    };

    const Entry entries[] = {
        {" (x86)", "x86", Abi::X86Architecture, Abi::PEFormat, 32},
        {" (x64)", "amd64", Abi::X86Architecture, Abi::PEFormat, 64},
        {" (x86_arm)", "x86_arm", Abi::ArmArchitecture, Abi::PEFormat, 32},
        {" (x64_arm)", "amd64_arm", Abi::ArmArchitecture, Abi::PEFormat, 64}
    };

#ifdef Q_OS_WIN64
    const char programFilesC[] = "ProgramFiles(x86)";
#else
    const char programFilesC[] = "ProgramFiles";
#endif
    const QString name = QStringLiteral("Microsoft Visual C++ Build Tools");
    const QString vcVarsBat = QFile::decodeName(qgetenv(programFilesC))
        + QLatin1Char('/') + name + QStringLiteral("/vcbuildtools.bat");
    if (!QFileInfo(vcVarsBat).isFile())
        return;
    const size_t count = sizeof(entries) / sizeof(entries[0]);
    for (size_t i = 0; i < count; ++i) {
        const Entry &e = entries[i];
        const Abi abi(e.architecture, Abi::WindowsOS, Abi::WindowsMsvc2015Flavor,
                      e.format, e.wordSize);
894
        for (auto language: {Constants::C_LANGUAGE_ID, Constants::CXX_LANGUAGE_ID}) {
895 896 897 898
            list->append(new MsvcToolChain(name + QLatin1String(e.postFix), abi,
                                           vcVarsBat, QLatin1String(e.varsBatArg),
                                           language, ToolChain::AutoDetection));
        }
899 900 901
    }
}

902 903 904 905 906 907 908 909 910 911
static ToolChain *findMsvcToolChain(const QList<ToolChain *> &list,
                                    unsigned char wordWidth, Abi::OSFlavor flavor)
{
    return Utils::findOrDefault(list, [wordWidth, flavor] (const ToolChain *tc)
        { const Abi abi = tc->targetAbi();
          return abi.osFlavor() == flavor
              && wordWidth == abi.wordWidth();} );
}

// Detect Clang-cl on top of MSVC2015 or MSVC2013.
912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927
static void detectClangClToolChain(QList<ToolChain *> *list)
{
#ifdef Q_OS_WIN64
    const char registryNode[] = "HKEY_LOCAL_MACHINE\\SOFTWARE\\WOW6432Node\\LLVM\\LLVM";
#else
    const char registryNode[] = "HKEY_LOCAL_MACHINE\\SOFTWARE\\LLVM\\LLVM";
#endif

    const QSettings registry(QLatin1String(registryNode), QSettings::NativeFormat);
    if (registry.status() != QSettings::NoError)
        return;
    const QString path = QDir::cleanPath(registry.value(QStringLiteral(".")).toString());
    if (path.isEmpty())
        return;
    const unsigned char wordWidth = Utils::is64BitWindowsBinary(path + QStringLiteral("/bin/") + QLatin1String(clangClBinary))
        ? 64 : 32;
928 929 930 931
    const ToolChain *toolChain = findMsvcToolChain(*list, wordWidth, Abi::WindowsMsvc2015Flavor);
    if (!toolChain)
        toolChain = findMsvcToolChain(*list, wordWidth, Abi::WindowsMsvc2013Flavor);
    if (!toolChain) {
932
        qWarning("Unable to find a suitable MSVC version for \"%s\".", qPrintable(QDir::toNativeSeparators(path)));
933 934 935 936
        return;
    }
    const MsvcToolChain *msvcToolChain = static_cast<const MsvcToolChain *>(toolChain);
    const Abi targetAbi = msvcToolChain->targetAbi();
937
    const QString name = QStringLiteral("LLVM ") + QString::number(wordWidth)
938 939 940
        + QStringLiteral("bit based on ")
        + Abi::toString(targetAbi.osFlavor()).toUpper();
    list->append(new ClangClToolChain(name, path, targetAbi,
941
                                      msvcToolChain->varsBat(), msvcToolChain->varsBatArg(),
942
                                      Constants::CXX_LANGUAGE_ID, ToolChain::AutoDetection));
943 944
}

945
QList<ToolChain *> MsvcToolChainFactory::autoDetect(const QList<ToolChain *> &alreadyKnown)
946 947 948 949
{
    QList<ToolChain *> results;

    // 1) Installed SDKs preferred over standalone Visual studio
950
    const QSettings sdkRegistry(QLatin1String("HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Microsoft SDKs\\Windows"),
951 952 953 954 955 956 957 958 959
                                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
960 961 962 963 964
            QDir dir(folder);
            if (!dir.cd(QLatin1String("bin")))
                continue;
            QFileInfo fi(dir, QLatin1String("SetEnv.cmd"));
            if (!fi.exists())
965
                continue;
966

Yuchen Deng's avatar
Yuchen Deng committed
967
            QList<ToolChain *> tmp;
Orgad Shaneh's avatar
Orgad Shaneh committed
968 969 970 971 972 973 974 975 976 977 978 979
            const QVector<QPair<MsvcToolChain::Platform, QString> > platforms = {
                {MsvcToolChain::x86, "x86"},
                {MsvcToolChain::amd64, "x64"},
                {MsvcToolChain::ia64, "ia64"},
            };
            for (auto platform: platforms) {
                tmp.append(findOrCreateToolChain(
                               alreadyKnown,
                               generateDisplayName(name, MsvcToolChain::WindowsSDK, platform.first),
                               findAbiOfMsvc(MsvcToolChain::WindowsSDK, platform.first, sdkKey),
                               fi.absoluteFilePath(), "/" + platform.second, ToolChain::AutoDetection));
            }
980 981 982 983 984 985 986 987 988
            // Make sure the default is front.
            if (folder == defaultSdkPath)
                results = tmp + results;
            else
                results += tmp;
        } // foreach
    }

    // 2) Installed MSVCs
989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010
    // prioritized list.
    // 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.
    const MsvcToolChain::Platform platforms[] = {
        MsvcToolChain::x86, MsvcToolChain::amd64_x86,
        MsvcToolChain::amd64, MsvcToolChain::x86_amd64,
        MsvcToolChain::arm, MsvcToolChain::x86_arm, MsvcToolChain::amd64_arm,
        MsvcToolChain::ia64, MsvcToolChain::x86_ia64
    };

    foreach (const VisualStudioInstallation &i, detectVisualStudio()) {
        for (MsvcToolChain::Platform platform : platforms) {
            const bool toolchainInstalled =
                QFileInfo(vcVarsBatFor(i.vcVarsPath, platform, i.version)).isFile();
            if (hostSupportsPlatform(platform) && toolchainInstalled) {
                results.append(findOrCreateToolChain(
                                   alreadyKnown,
                                   generateDisplayName(i.vsName, MsvcToolChain::VS, platform),
                                   findAbiOfMsvc(MsvcToolChain::VS, platform, i.vsName),
                                   i.vcVarsAll, platformName(platform),
                                   ToolChain::AutoDetection));
Daniel Teske's avatar
Daniel Teske committed
1011
            }
1012 1013
        }
    }
SteveKing's avatar
SteveKing committed
1014

1015 1016
    detectCppBuildTools(&results);

1017 1018
    detectClangClToolChain(&results);

1019 1020 1021
    return results;
}

1022 1023 1024 1025 1026 1027 1028 1029
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;
}

1030 1031
bool MsvcToolChainFactory::canRestore(const QVariantMap &data)
{
1032 1033 1034 1035 1036 1037 1038
    const Core::Id id = typeIdFromMap(data);
    return id == Constants::MSVC_TOOLCHAIN_TYPEID || id == Constants::CLANG_CL_TOOLCHAIN_TYPEID;
}

template <class ToolChainType>
ToolChainType *readFromMap(const QVariantMap &data)
{
1039
    auto result = new ToolChainType;
1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051
    if (result->fromMap(data))
        return result;
    delete result;
    return nullptr;
}

ToolChain *MsvcToolChainFactory::restore(const QVariantMap &data)
{
    const Core::Id id = typeIdFromMap(data);
    if (id == Constants::CLANG_CL_TOOLCHAIN_TYPEID)
        return readFromMap<ClangClToolChain>(data);
    return readFromMap<MsvcToolChain>(data);
1052 1053
}

1054 1055
} // namespace Internal
} // namespace ProjectExplorer