1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of Qt Creator.
7**
8** Commercial License Usage
9** Licensees holding valid commercial Qt licenses may use this file in
10** accordance with the commercial license agreement provided with the
11** Software or, alternatively, in accordance with the terms contained in
12** a written agreement between you and The Qt Company. For licensing terms
13** and conditions see https://www.qt.io/terms-conditions. For further
14** information use the contact form at https://www.qt.io/contact-us.
15**
16** GNU General Public License Usage
17** Alternatively, this file may be used under the terms of the GNU
18** General Public License version 3 as published by the Free Software
19** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
20** included in the packaging of this file. Please review the following
21** information to ensure the GNU General Public License requirements will
22** be met: https://www.gnu.org/licenses/gpl-3.0.html.
23**
24****************************************************************************/
25
26#include "msvctoolchain.h"
27
28#include "gcctoolchain.h"
29#include "msvcparser.h"
30#include "projectexplorer.h"
31#include "projectexplorerconstants.h"
32#include "projectexplorersettings.h"
33#include "taskhub.h"
34#include "toolchainmanager.h"
35
36#include <coreplugin/icore.h>
37
38#include <utils/algorithm.h>
39#include <utils/hostosinfo.h>
40#include <utils/optional.h>
41#include <utils/qtcassert.h>
42#include <utils/qtcprocess.h>
43#include <utils/synchronousprocess.h>
44#include <utils/runextensions.h>
45#include <utils/temporarydirectory.h>
46#include <utils/pathchooser.h>
47#include <utils/winutils.h>
48
49#include <QDir>
50#include <QFileInfo>
51#include <QJsonArray>
52#include <QJsonDocument>
53#include <QProcess>
54#include <QRegularExpression>
55#include <QSettings>
56#include <QVector>
57#include <QVersionNumber>
58
59#include <QLabel>
60#include <QComboBox>
61#include <QFormLayout>
62
63#define KEY_ROOT "ProjectExplorer.MsvcToolChain."
64static const char varsBatKeyC[] = KEY_ROOT "VarsBat";
65static const char varsBatArgKeyC[] = KEY_ROOT "VarsBatArg";
66static const char supportedAbiKeyC[] = KEY_ROOT "SupportedAbi";
67static const char environModsKeyC[] = KEY_ROOT "environmentModifications";
68
69enum { debug = 0 };
70
71namespace ProjectExplorer {
72namespace Internal {
73
74// --------------------------------------------------------------------------
75// Helpers:
76// --------------------------------------------------------------------------
77
78static QThreadPool *envModThreadPool()
79{
80 static QThreadPool *pool = nullptr;
81 if (!pool) {
82 pool = new QThreadPool(ProjectExplorerPlugin::instance());
83 pool->setMaxThreadCount(1);
84 }
85 return pool;
86}
87
88struct MsvcPlatform
89{
90 MsvcToolChain::Platform platform;
91 const char *name;
92 const char *prefix; // VS up until 14.0 (MSVC2015)
93 const char *bat;
94};
95
96const MsvcPlatform platforms[]
97 = {{MsvcToolChain::x86, "x86", "/bin", "vcvars32.bat"},
98 {MsvcToolChain::amd64, "amd64", "/bin/amd64", "vcvars64.bat"},
99 {MsvcToolChain::x86_amd64, "x86_amd64", "/bin/x86_amd64", "vcvarsx86_amd64.bat"},
100 {MsvcToolChain::ia64, "ia64", "/bin/ia64", "vcvars64.bat"},
101 {MsvcToolChain::x86_ia64, "x86_ia64", "/bin/x86_ia64", "vcvarsx86_ia64.bat"},
102 {MsvcToolChain::arm, "arm", "/bin/arm", "vcvarsarm.bat"},
103 {MsvcToolChain::x86_arm, "x86_arm", "/bin/x86_arm", "vcvarsx86_arm.bat"},
104 {MsvcToolChain::amd64_arm, "amd64_arm", "/bin/amd64_arm", "vcvarsamd64_arm.bat"},
105 {MsvcToolChain::amd64_x86, "amd64_x86", "/bin/amd64_x86", "vcvarsamd64_x86.bat"}};
106
107static QList<const MsvcToolChain *> g_availableMsvcToolchains;
108
109static const MsvcPlatform *platformEntry(MsvcToolChain::Platform t)
110{
111 for (const MsvcPlatform &p : platforms) {
112 if (p.platform == t)
113 return &p;
114 }
115 return nullptr;
116}
117
118static QString platformName(MsvcToolChain::Platform t)
119{
120 if (const MsvcPlatform *p = platformEntry(t))
121 return QLatin1String(p->name);
122 return QString();
123}
124
125static bool hostSupportsPlatform(MsvcToolChain::Platform platform)
126{
127 switch (Utils::HostOsInfo::hostArchitecture()) {
128 case Utils::HostOsInfo::HostArchitectureAMD64:
129 if (platform == MsvcToolChain::amd64 || platform == MsvcToolChain::amd64_arm
130 || platform == MsvcToolChain::amd64_x86)
131 return true;
132 Q_FALLTHROUGH(); // all x86 toolchains are also working on an amd64 host
133 case Utils::HostOsInfo::HostArchitectureX86:
134 return platform == MsvcToolChain::x86 || platform == MsvcToolChain::x86_amd64
135 || platform == MsvcToolChain::x86_ia64 || platform == MsvcToolChain::x86_arm;
136 case Utils::HostOsInfo::HostArchitectureArm:
137 return platform == MsvcToolChain::arm;
138 case Utils::HostOsInfo::HostArchitectureItanium:
139 return platform == MsvcToolChain::ia64;
140 default:
141 return false;
142 }
143}
144
145static QString fixRegistryPath(const QString &path)
146{
147 QString result = QDir::fromNativeSeparators(path);
148 if (result.endsWith(QLatin1Char('/')))
149 result.chop(1);
150 return result;
151}
152
153struct VisualStudioInstallation
154{
155 QString vsName;
156 QVersionNumber version;
157 QString path; // Main installation path
158 QString vcVarsPath; // Path under which the various vc..bat are to be found
159 QString vcVarsAll;
160};
161
162QDebug operator<<(QDebug d, const VisualStudioInstallation &i)
163{
164 QDebugStateSaver saver(d);
165 d.noquote();
166 d.nospace();
167 d << "VisualStudioInstallation(\"" << i.vsName << "\", v=" << i.version << ", path=\""
168 << QDir::toNativeSeparators(i.path) << "\", vcVarsPath=\""
169 << QDir::toNativeSeparators(i.vcVarsPath) << "\", vcVarsAll=\""
170 << QDir::toNativeSeparators(i.vcVarsAll) << "\")";
171 return d;
172}
173
174static QString windowsProgramFilesDir()
175{
176#ifdef Q_OS_WIN64
177 const char programFilesC[] = "ProgramFiles(x86)";
178#else
179 const char programFilesC[] = "ProgramFiles";
180#endif
181 return QDir::fromNativeSeparators(QFile::decodeName(qgetenv(programFilesC)));
182}
183
184static Utils::optional<VisualStudioInstallation> installationFromPathAndVersion(
185 const QString &installationPath, const QVersionNumber &version)
186{
187 QString vcVarsPath = QDir::fromNativeSeparators(installationPath);
188 if (!vcVarsPath.endsWith('/'))
189 vcVarsPath += '/';
190 if (version.majorVersion() > 14)
191 vcVarsPath += QStringLiteral("VC/Auxiliary/Build");
192 else
193 vcVarsPath += QStringLiteral("VC");
194
195 const QString vcVarsAllPath = vcVarsPath + QStringLiteral("/vcvarsall.bat");
196 if (!QFileInfo(vcVarsAllPath).isFile()) {
197 qWarning().noquote() << "Unable to find MSVC setup script "
198 << QDir::toNativeSeparators(vcVarsPath) << " in version " << version;
199 return Utils::nullopt;
200 }
201
202 const QString versionString = version.toString();
203 VisualStudioInstallation installation;
204 installation.path = installationPath;
205 installation.version = version;
206 installation.vsName = versionString;
207 installation.vcVarsPath = vcVarsPath;
208 installation.vcVarsAll = vcVarsAllPath;
209 return std::move(installation);
210}
211
212// Detect build tools introduced with MSVC2017
213static Utils::optional<VisualStudioInstallation> detectCppBuildTools2017()
214{
215 const QString installPath = windowsProgramFilesDir()
216 + "/Microsoft Visual Studio/2017/BuildTools";
217 const QString vcVarsPath = installPath + "/VC/Auxiliary/Build";
218 const QString vcVarsAllPath = vcVarsPath + "/vcvarsall.bat";
219
220 if (!QFileInfo::exists(vcVarsAllPath))
221 return Utils::nullopt;
222
223 VisualStudioInstallation installation;
224 installation.path = installPath;
225 installation.vcVarsAll = vcVarsAllPath;
226 installation.vcVarsPath = vcVarsPath;
227 installation.version = QVersionNumber(15);
228 installation.vsName = "15.0";
229
230 return installation;
231}
232
233static QVector<VisualStudioInstallation> detectVisualStudioFromVsWhere(const QString &vswhere)
234{
235 QVector<VisualStudioInstallation> installations;
236 Utils::SynchronousProcess vsWhereProcess;
237 const int timeoutS = 5;
238 vsWhereProcess.setTimeoutS(timeoutS);
239 const QStringList
240 arguments{"-products", "*", "-prerelease", "-legacy", "-format", "json", "-utf8"};
241 Utils::SynchronousProcessResponse response = vsWhereProcess.runBlocking(vswhere, arguments);
242 switch (response.result) {
243 case Utils::SynchronousProcessResponse::Finished:
244 break;
245 case Utils::SynchronousProcessResponse::StartFailed:
246 qWarning().noquote() << QDir::toNativeSeparators(vswhere) << "could not be started.";
247 return installations;
248 case Utils::SynchronousProcessResponse::FinishedError:
249 qWarning().noquote().nospace() << QDir::toNativeSeparators(vswhere)
250 << " finished with exit code "
251 << response.exitCode << ".";
252 return installations;
253 case Utils::SynchronousProcessResponse::TerminatedAbnormally:
254 qWarning().noquote().nospace()
255 << QDir::toNativeSeparators(vswhere) << " crashed. Exit code: " << response.exitCode;
256 return installations;
257 case Utils::SynchronousProcessResponse::Hang:
258 qWarning().noquote() << QDir::toNativeSeparators(vswhere) << "did not finish in" << timeoutS
259 << "seconds.";
260 return installations;
261 }
262
263 QByteArray output = response.stdOut().toUtf8();
264 QJsonParseError error;
265 const QJsonDocument doc = QJsonDocument::fromJson(output, &error);
266 if (error.error != QJsonParseError::NoError || doc.isNull()) {
267 qWarning() << "Could not parse json document from vswhere output.";
268 return installations;
269 }
270
271 const QJsonArray versions = doc.array();
272 if (versions.isEmpty()) {
273 qWarning() << "Could not detect any versions from vswhere output.";
274 return installations;
275 }
276
277 for (const QJsonValue &vsVersion : versions) {
278 const QJsonObject vsVersionObj = vsVersion.toObject();
279 if (vsVersionObj.isEmpty()) {
280 qWarning() << "Could not obtain object from vswhere version";
281 continue;
282 }
283
284 QJsonValue value = vsVersionObj.value("installationVersion");
285 if (value.isUndefined()) {
286 qWarning() << "Could not obtain VS version from json output";
287 continue;
288 }
289 const QString versionString = value.toString();
290 QVersionNumber version = QVersionNumber::fromString(versionString);
291 value = vsVersionObj.value("installationPath");
292 if (value.isUndefined()) {
293 qWarning() << "Could not obtain VS installation path from json output";
294 continue;
295 }
296 const QString installationPath = value.toString();
297 Utils::optional<VisualStudioInstallation> installation
298 = installationFromPathAndVersion(installationPath, version);
299
300 if (installation)
301 installations.append(*installation);
302 }
303 return installations;
304}
305
306static QVector<VisualStudioInstallation> detectVisualStudioFromRegistry()
307{
308 QVector<VisualStudioInstallation> result;
309#ifdef Q_OS_WIN64
310 const QString keyRoot = QStringLiteral(
311 "HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\Microsoft\\VisualStudio\\SxS\\");
312#else
313 const QString keyRoot = QStringLiteral(
314 "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\SxS\\");
315#endif
316 QSettings vsRegistry(keyRoot + QStringLiteral("VS7"), QSettings::NativeFormat);
317 QScopedPointer<QSettings> vcRegistry;
318 foreach (const QString &vsName, vsRegistry.allKeys()) {
319 const QVersionNumber version = QVersionNumber::fromString(vsName);
320 if (!version.isNull()) {
321 const QString installationPath = fixRegistryPath(vsRegistry.value(vsName).toString());
322
323 Utils::optional<VisualStudioInstallation> installation
324 = installationFromPathAndVersion(installationPath, version);
325 if (installation)
326 result.append(*installation);
327 }
328 }
329
330 // Detect VS 2017 Build Tools
331 auto installation = detectCppBuildTools2017();
332 if (installation)
333 result.append(*installation);
334
335 return result;
336}
337
338static QVector<VisualStudioInstallation> detectVisualStudio()
339{
340 const QString vswhere = windowsProgramFilesDir()
341 + "/Microsoft Visual Studio/Installer/vswhere.exe";
342 if (QFileInfo::exists(vswhere)) {
343 const QVector<VisualStudioInstallation> installations = detectVisualStudioFromVsWhere(
344 vswhere);
345 if (!installations.isEmpty())
346 return installations;
347 }
348
349 return detectVisualStudioFromRegistry();
350}
351
352static Abi findAbiOfMsvc(MsvcToolChain::Type type,
353 MsvcToolChain::Platform platform,
354 const QString &version)
355{
356 Abi::Architecture arch = Abi::X86Architecture;
357 Abi::OSFlavor flavor = Abi::UnknownFlavor;
358 int wordWidth = 64;
359
360 switch (platform) {
361 case MsvcToolChain::x86:
362 case MsvcToolChain::amd64_x86:
363 wordWidth = 32;
364 break;
365 case MsvcToolChain::ia64:
366 case MsvcToolChain::x86_ia64:
367 arch = Abi::ItaniumArchitecture;
368 break;
369 case MsvcToolChain::amd64:
370 case MsvcToolChain::x86_amd64:
371 break;
372 case MsvcToolChain::arm:
373 case MsvcToolChain::x86_arm:
374 case MsvcToolChain::amd64_arm:
375 arch = Abi::ArmArchitecture;
376 wordWidth = 32;
377 break;
378 };
379
380 QString msvcVersionString = version;
381 if (type == MsvcToolChain::WindowsSDK) {
382 if (version == QLatin1String("v7.0") || version.startsWith(QLatin1String("6.")))
383 msvcVersionString = QLatin1String("9.0");
384 else if (version == QLatin1String("v7.0A") || version == QLatin1String("v7.1"))
385 msvcVersionString = QLatin1String("10.0");
386 }
387 if (msvcVersionString.startsWith(QLatin1String("16.")))
388 flavor = Abi::WindowsMsvc2019Flavor;
389 else if (msvcVersionString.startsWith(QLatin1String("15.")))
390 flavor = Abi::WindowsMsvc2017Flavor;
391 else if (msvcVersionString.startsWith(QLatin1String("14.")))
392 flavor = Abi::WindowsMsvc2015Flavor;
393 else if (msvcVersionString.startsWith(QLatin1String("12.")))
394 flavor = Abi::WindowsMsvc2013Flavor;
395 else if (msvcVersionString.startsWith(QLatin1String("11.")))
396 flavor = Abi::WindowsMsvc2012Flavor;
397 else if (msvcVersionString.startsWith(QLatin1String("10.")))
398 flavor = Abi::WindowsMsvc2010Flavor;
399 else if (msvcVersionString.startsWith(QLatin1String("9.")))
400 flavor = Abi::WindowsMsvc2008Flavor;
401 else
402 flavor = Abi::WindowsMsvc2005Flavor;
403 const Abi result = Abi(arch, Abi::WindowsOS, flavor, Abi::PEFormat, wordWidth);
404 if (!result.isValid())
405 qWarning("Unable to completely determine the ABI of MSVC version %s (%s).",
406 qPrintable(version),
407 qPrintable(result.toString()));
408 return result;
409}
410
411static QString generateDisplayName(const QString &name,
412 MsvcToolChain::Type t,
413 MsvcToolChain::Platform p)
414{
415 if (t == MsvcToolChain::WindowsSDK) {
416 QString sdkName = name;
417 sdkName += QString::fromLatin1(" (%1)").arg(platformName(p));
418 return sdkName;
419 }
420 // Comes as "9.0" from the registry
421 QString vcName = QLatin1String("Microsoft Visual C++ Compiler ");
422 vcName += name;
423 vcName += QString::fromLatin1(" (%1)").arg(platformName(p));
424 return vcName;
425}
426
427static QByteArray msvcCompilationDefine(const char *def)
428{
429 const QByteArray macro(def);
430 return "#if defined(" + macro + ")\n__PPOUT__(" + macro + ")\n#endif\n";
431}
432
433static QByteArray msvcCompilationFile()
434{
435 static const char *macros[] = {"_ATL_VER",
436 "__ATOM__",
437 "__AVX__",
438 "__AVX2__",
439 "_CHAR_UNSIGNED",
440 "__CLR_VER",
441 "_CMMN_INTRIN_FUNC",
442 "_CONTROL_FLOW_GUARD",
443 "__cplusplus",
444 "__cplusplus_cli",
445 "__cplusplus_winrt",
446 "_CPPLIB_VER",
447 "_CPPRTTI",
448 "_CPPUNWIND",
449 "_DEBUG",
450 "_DLL",
451 "_INTEGRAL_MAX_BITS",
452 "__INTELLISENSE__",
453 "_ISO_VOLATILE",
454 "_KERNEL_MODE",
455 "_M_AAMD64",
456 "_M_ALPHA",
457 "_M_AMD64",
458 "_MANAGED",
459 "_M_ARM",
460 "_M_ARM64",
461 "_M_ARM_ARMV7VE",
462 "_M_ARM_FP",
463 "_M_ARM_NT",
464 "_M_ARMT",
465 "_M_CEE",
466 "_M_CEE_PURE",
467 "_M_CEE_SAFE",
468 "_MFC_VER",
469 "_M_FP_EXCEPT",
470 "_M_FP_FAST",
471 "_M_FP_PRECISE",
472 "_M_FP_STRICT",
473 "_M_IA64",
474 "_M_IX86",
475 "_M_IX86_FP",
476 "_M_MPPC",
477 "_M_MRX000",
478 "_M_PPC",
479 "_MSC_BUILD",
480 "_MSC_EXTENSIONS",
481 "_MSC_FULL_VER",
482 "_MSC_VER",
483 "_MSVC_LANG",
484 "__MSVC_RUNTIME_CHECKS",
485 "_MT",
486 "_M_THUMB",
487 "_M_X64",
488 "_NATIVE_WCHAR_T_DEFINED",
489 "_OPENMP",
490 "_PREFAST_",
491 "__STDC__",
492 "__STDC_HOSTED__",
493 "__STDCPP_THREADS__",
494 "_VC_NODEFAULTLIB",
495 "_WCHAR_T_DEFINED",
496 "_WIN32",
497 "_WIN32_WCE",
498 "_WIN64",
499 "_WINRT_DLL",
500 "_Wp64",
501 nullptr};
502 QByteArray file = "#define __PPOUT__(x) V##x=x\n\n";
503 for (int i = 0; macros[i] != nullptr; ++i)
504 file += msvcCompilationDefine(macros[i]);
505 file += "\nvoid main(){}\n\n";
506 return file;
507}
508
509// Run MSVC 'cl' compiler to obtain #defines.
510// This function must be thread-safe!
511//
512// Some notes regarding the used approach:
513//
514// It seems that there is no reliable way to get all the
515// predefined macros for a cl invocation. The following two
516// approaches are unfortunately limited since both lead to an
517// incomplete list of actually predefined macros and come with
518// other problems, too.
519//
520// 1) Maintain a list of predefined macros from the official
521// documentation (for MSVC2015, e.g. [1]). Feed cl with a
522// temporary file that queries the values of those macros.
523//
524// Problems:
525// * Maintaining that list.
526// * The documentation is incomplete, we do not get all
527// predefined macros. E.g. the cl from MSVC2015, set up
528// with "vcvars.bat x86_arm", predefines among others
529// _M_ARMT, but that's not reflected in the
530// documentation.
531//
532// 2) Run cl with the undocumented options /B1 and /Bx, as
533// described in [2].
534//
535// Note: With qmake from Qt >= 5.8 it's possible to print
536// the macros formatted as preprocessor code in an easy to
537// read/compare/diff way:
538//
539// > cl /nologo /c /TC /B1 qmake NUL
540// > cl /nologo /c /TP /Bx qmake NUL
541//
542// Problems:
543// * Using undocumented options.
544// * Resulting macros are incomplete.
545// For example, the nowadays default option /Zc:wchar_t
546// predefines _WCHAR_T_DEFINED, but this is not reflected
547// with this approach.
548//
549// To work around this we would need extra cl invocations
550// to get the actual values of the missing macros
551// (approach 1).
552//
553// Currently we combine both approaches in this way:
554// * As base, maintain the list from the documentation and
555// update it once a new MSVC version is released.
556// * Enrich it with macros that we discover with approach 2
557// once a new MSVC version is released.
558// * Enrich it further with macros that are not covered with
559// the above points.
560//
561// TODO: Update the predefined macros for MSVC 2017 once the
562// page exists.
563//
564// [1] https://msdn.microsoft.com/en-us/library/b0084kay.aspx
565// [2] http://stackoverflow.com/questions/3665537/how-to-find-out-cl-exes-built-in-macros
566Macros MsvcToolChain::msvcPredefinedMacros(const QStringList &cxxflags,
567 const Utils::Environment &env) const
568{
569 Macros predefinedMacros;
570
571 QStringList toProcess;
572 for (const QString &arg : cxxflags) {
573 if (arg.startsWith(QLatin1String("/D"))) {
574 const QString define = arg.mid(2);
575 predefinedMacros.append(Macro::fromKeyValue(define));
576 } else if (arg.startsWith(QLatin1String("/U"))) {
577 predefinedMacros.append(
578 {arg.mid(2).toLocal8Bit(), ProjectExplorer::MacroType::Undefine});
579 } else {
580 toProcess.append(arg);
581 }
582 }
583
584 Utils::TempFileSaver saver(Utils::TemporaryDirectory::masterDirectoryPath()
585 + "/envtestXXXXXX.cpp");
586 saver.write(msvcCompilationFile());
587 if (!saver.finalize()) {
588 qWarning("%s: %s", Q_FUNC_INFO, qPrintable(saver.errorString()));
589 return predefinedMacros;
590 }
591 Utils::SynchronousProcess cpp;
592 cpp.setEnvironment(env.toStringList());
593 cpp.setWorkingDirectory(Utils::TemporaryDirectory::masterDirectoryPath());
594 QStringList arguments;
595 const Utils::FileName binary = env.searchInPath(QLatin1String("cl.exe"));
596 if (binary.isEmpty()) {
597 qWarning("%s: The compiler binary cl.exe could not be found in the path.", Q_FUNC_INFO);
598 return predefinedMacros;
599 }
600
601 if (language() == ProjectExplorer::Constants::C_LANGUAGE_ID)
602 arguments << QLatin1String("/TC");
603 arguments << toProcess << QLatin1String("/EP") << QDir::toNativeSeparators(saver.fileName());
604 Utils::SynchronousProcessResponse response = cpp.runBlocking(binary.toString(), arguments);
605 if (response.result != Utils::SynchronousProcessResponse::Finished || response.exitCode != 0)
606 return predefinedMacros;
607
608 const QStringList output = Utils::filtered(response.stdOut().split('\n'),
609 [](const QString &s) { return s.startsWith('V'); });
610 for (const QString &line : output)
611 predefinedMacros.append(Macro::fromKeyValue(line.mid(1)));
612 return predefinedMacros;
613}
614
615//
616// We want to detect the language version based on the predefined macros.
617// Unfortunately MSVC does not conform to standard when it comes to the predefined
618// __cplusplus macro - it reports "199711L", even for newer language versions.
619//
620// However:
621// * For >= Visual Studio 2015 Update 3 predefines _MSVC_LANG which has the proper value
622// of __cplusplus.
623// See https://docs.microsoft.com/en-us/cpp/preprocessor/predefined-macros?view=vs-2017
624// * For >= Visual Studio 2017 Version 15.7 __cplusplus is correct once /Zc:__cplusplus
625// is provided on the command line. Then __cplusplus == _MSVC_LANG.
626// See https://blogs.msdn.microsoft.com/vcblog/2018/04/09/msvc-now-correctly-reports-__cplusplus
627//
628// We rely on _MSVC_LANG if possible, otherwise on some hard coded language versions
629// depending on _MSC_VER.
630//
631// For _MSV_VER values, see https://docs.microsoft.com/en-us/cpp/preprocessor/predefined-macros?view=vs-2017.
632//
633Utils::LanguageVersion MsvcToolChain::msvcLanguageVersion(const QStringList & /*cxxflags*/,
634 const Core::Id &language,
635 const Macros &macros) const
636{
637 using Utils::LanguageVersion;
638
639 int mscVer = -1;
640 QByteArray msvcLang;
641 for (const ProjectExplorer::Macro &macro : macros) {
642 if (macro.key == "_MSVC_LANG")
643 msvcLang = macro.value;
644 if (macro.key == "_MSC_VER")
645 mscVer = macro.value.toInt(nullptr);
646 }
647 QTC_CHECK(mscVer > 0);
648
649 if (language == Constants::CXX_LANGUAGE_ID) {
650 if (!msvcLang.isEmpty()) // >= Visual Studio 2015 Update 3
651 return ToolChain::cxxLanguageVersion(msvcLang);
652 if (mscVer >= 1800) // >= Visual Studio 2013 (12.0)
653 return LanguageVersion::CXX14;
654 if (mscVer >= 1600) // >= Visual Studio 2010 (10.0)
655 return LanguageVersion::CXX11;
656 return LanguageVersion::CXX98;
657 } else if (language == Constants::C_LANGUAGE_ID) {
658 if (mscVer >= 1910) // >= Visual Studio 2017 RTW (15.0)
659 return LanguageVersion::C11;
660 return LanguageVersion::C99;
661 } else {
662 QTC_CHECK(false && "Unexpected toolchain language, assuming latest C++ we support.");
663 return LanguageVersion::LatestCxx;
664 }
665}
666
667// Windows: Expand the delayed evaluation references returned by the
668// SDK setup scripts: "PATH=!Path!;foo". Some values might expand
669// to empty and should not be added
670static QString winExpandDelayedEnvReferences(QString in, const Utils::Environment &env)
671{
672 const QChar exclamationMark = QLatin1Char('!');
673 for (int pos = 0; pos < in.size();) {
674 // Replace "!REF!" by its value in process environment
675 pos = in.indexOf(exclamationMark, pos);
676 if (pos == -1)
677 break;
678 const int nextPos = in.indexOf(exclamationMark, pos + 1);
679 if (nextPos == -1)
680 break;
681 const QString var = in.mid(pos + 1, nextPos - pos - 1);
682 const QString replacement = env.value(var.toUpper());
683 in.replace(pos, nextPos + 1 - pos, replacement);
684 pos += replacement.size();
685 }
686 return in;
687}
688
689void MsvcToolChain::environmentModifications(
690 QFutureInterface<MsvcToolChain::GenerateEnvResult> &future,
691 QString vcvarsBat,
692 QString varsBatArg)
693{
694 const Utils::Environment inEnv = Utils::Environment::systemEnvironment();
695 Utils::Environment outEnv;
696 QMap<QString, QString> envPairs;
697 QList<Utils::EnvironmentItem> diff;
698 Utils::optional<QString> error = generateEnvironmentSettings(inEnv,
699 vcvarsBat,
700 varsBatArg,
701 envPairs);
702 if (!error) {
703 // Now loop through and process them
704 for (auto envIter = envPairs.cbegin(), end = envPairs.cend(); envIter != end; ++envIter) {
705 const QString expandedValue = winExpandDelayedEnvReferences(envIter.value(), inEnv);
706 if (!expandedValue.isEmpty())
707 outEnv.set(envIter.key(), expandedValue);
708 }
709
710 if (debug) {
711 const QStringList newVars = outEnv.toStringList();
712 const QStringList oldVars = inEnv.toStringList();
713 QDebug nsp = qDebug().nospace();
714 foreach (const QString &n, newVars) {
715 if (!oldVars.contains(n))
716 nsp << n << '\n';
717 }
718 }
719
720 diff = inEnv.diff(outEnv, true);
721 for (int i = diff.size() - 1; i >= 0; --i) {
722 if (diff.at(i).name.startsWith(QLatin1Char('='))) { // Exclude "=C:", "=EXITCODE"
723 diff.removeAt(i);
724 }
725 }
726 }
727
728 future.reportResult({error, diff});
729}
730
731void MsvcToolChain::initEnvModWatcher(const QFuture<GenerateEnvResult> &future)
732{
733 QObject::connect(&m_envModWatcher, &QFutureWatcher<GenerateEnvResult>::resultReadyAt, [&]() {
734 const GenerateEnvResult &result = m_envModWatcher.result();
735 if (result.error) {
736 const QString &errorMessage = *result.error;
737 if (!errorMessage.isEmpty())
738 TaskHub::addTask(Task::Error, errorMessage, Constants::TASK_CATEGORY_COMPILE);
739 } else {
740 updateEnvironmentModifications(result.environmentItems);
741 }
742 });
743 m_envModWatcher.setFuture(future);
744}
745
746void MsvcToolChain::updateEnvironmentModifications(QList<Utils::EnvironmentItem> modifications)
747{
748 Utils::EnvironmentItem::sort(&modifications);
749 if (modifications != m_environmentModifications) {
750 m_environmentModifications = modifications;
751 toolChainUpdated();
752 }
753}
754
755Utils::Environment MsvcToolChain::readEnvironmentSetting(const Utils::Environment &env) const
756{
757 Utils::Environment resultEnv = env;
758 if (m_environmentModifications.isEmpty()) {
759 m_envModWatcher.waitForFinished();
760 if (m_envModWatcher.future().isFinished() && !m_envModWatcher.future().isCanceled()) {
761 const GenerateEnvResult &result = m_envModWatcher.result();
762 if (result.error) {
763 const QString &errorMessage = *result.error;
764 if (!errorMessage.isEmpty())
765 TaskHub::addTask(Task::Error, errorMessage, Constants::TASK_CATEGORY_COMPILE);
766 } else {
767 resultEnv.modify(result.environmentItems);
768 }
769 }
770 } else {
771 resultEnv.modify(m_environmentModifications);
772 }
773 return resultEnv;
774}
775
776// --------------------------------------------------------------------------
777// MsvcToolChain
778// --------------------------------------------------------------------------
779
780MsvcToolChain::MsvcToolChain(const QString &name,
781 const Abi &abi,
782 const QString &varsBat,
783 const QString &varsBatArg,
784 Core::Id l,
785 Detection d)
786 : MsvcToolChain(Constants::MSVC_TOOLCHAIN_TYPEID, name, abi, varsBat, varsBatArg, l, d)
787{}
788
789MsvcToolChain::MsvcToolChain(const MsvcToolChain &other)
790 : ToolChain(other)
791 , m_headerPathsMutex(new QMutex)
792 , m_environmentModifications(other.m_environmentModifications)
793 , m_debuggerCommand(other.m_debuggerCommand)
794 , m_predefinedMacrosCache(other.m_predefinedMacrosCache)
795 , m_lastEnvironment(other.m_lastEnvironment)
796 , m_resultEnvironment(other.m_resultEnvironment)
797 , m_abi(other.m_abi)
798 , m_vcvarsBat(other.m_vcvarsBat)
799 , m_varsBatArg(other.m_varsBatArg)
800{
801 if (other.m_envModWatcher.isRunning()) {
802 initEnvModWatcher(other.m_envModWatcher.future());
803 } else if (m_environmentModifications.isEmpty() && other.m_envModWatcher.future().isFinished()
804 && !other.m_envModWatcher.future().isCanceled()) {
805 const GenerateEnvResult &result = m_envModWatcher.result();
806 if (result.error) {
807 const QString &errorMessage = *result.error;
808 if (!errorMessage.isEmpty())
809 TaskHub::addTask(Task::Error, errorMessage, Constants::TASK_CATEGORY_COMPILE);
810 } else {
811 updateEnvironmentModifications(result.environmentItems);
812 }
813 }
814
815 setDisplayName(other.displayName());
816}
817
818static void addToAvailableMsvcToolchains(const MsvcToolChain *toolchain)
819{
820 if (toolchain->typeId() != Constants::MSVC_TOOLCHAIN_TYPEID)
821 return;
822
823 g_availableMsvcToolchains.push_back(toolchain);
824}
825
826MsvcToolChain::MsvcToolChain(Core::Id typeId,
827 const QString &name,
828 const Abi &abi,
829 const QString &varsBat,
830 const QString &varsBatArg,
831 Core::Id l,
832 Detection d)
833 : ToolChain(typeId, d)
834 , m_headerPathsMutex(new QMutex)
835 , m_predefinedMacrosCache(std::make_shared<Cache<MacroInspectionReport, 64>>())
836 , m_lastEnvironment(Utils::Environment::systemEnvironment())
837 , m_abi(abi)
838 , m_vcvarsBat(varsBat)
839 , m_varsBatArg(varsBatArg)
840{
841 addToAvailableMsvcToolchains(this);
842
843 setLanguage(l);
844 initEnvModWatcher(Utils::runAsync(envModThreadPool(),
845 &MsvcToolChain::environmentModifications,
846 varsBat,
847 varsBatArg));
848
849 Q_ASSERT(!name.isEmpty());
850
851 setDisplayName(name);
852}
853
854MsvcToolChain::MsvcToolChain(Core::Id typeId)
855 : ToolChain(typeId, ManualDetection)
856 , m_predefinedMacrosCache(std::make_shared<Cache<MacroInspectionReport, 64>>())
857 , m_lastEnvironment(Utils::Environment::systemEnvironment())
858{}
859
860void MsvcToolChain::inferWarningsForLevel(int warningLevel, WarningFlags &flags)
861{
862 // reset all except unrelated flag
863 flags = flags & WarningFlags::AsErrors;
864
865 if (warningLevel >= 1)
866 flags |= WarningFlags(WarningFlags::Default | WarningFlags::IgnoredQualfiers
867 | WarningFlags::HiddenLocals | WarningFlags::UnknownPragma);
868 if (warningLevel >= 2)
869 flags |= WarningFlags::All;
870 if (warningLevel >= 3) {
871 flags |= WarningFlags(WarningFlags::Extra | WarningFlags::NonVirtualDestructor
872 | WarningFlags::SignedComparison | WarningFlags::UnusedLocals
873 | WarningFlags::Deprecated);
874 }
875 if (warningLevel >= 4)
876 flags |= WarningFlags::UnusedParams;
877}
878
879void MsvcToolChain::toolChainUpdated()
880{
881 m_predefinedMacrosCache->invalidate();
882}
883
884MsvcToolChain::MsvcToolChain()
885 : MsvcToolChain(Constants::MSVC_TOOLCHAIN_TYPEID)
886{}
887
888MsvcToolChain::~MsvcToolChain()
889{
890 g_availableMsvcToolchains.removeOne(this);
891 delete m_headerPathsMutex;
892}
893
894Abi MsvcToolChain::targetAbi() const
895{
896 return m_abi;
897}
898
899bool MsvcToolChain::isValid() const
900{
901 if (m_vcvarsBat.isEmpty())
902 return false;
903 QFileInfo fi(m_vcvarsBat);
904 return fi.isFile() && fi.isExecutable();
905}
906
907QString MsvcToolChain::originalTargetTriple() const
908{
909 return m_abi.wordWidth() == 64 ? QLatin1String("x86_64-pc-windows-msvc")
910 : QLatin1String("i686-pc-windows-msvc");
911}
912
913QString MsvcToolChain::typeDisplayName() const
914{
915 return MsvcToolChainFactory::tr("MSVC");
916}
917
918Utils::FileNameList MsvcToolChain::suggestedMkspecList() const
919{
920 Utils::FileNameList result;
921 result << Utils::FileName::fromLatin1("win32-msvc"); // Common MSVC mkspec introduced in 5.8.1
922 switch (m_abi.osFlavor()) {
923 case Abi::WindowsMsvc2005Flavor:
924 result << Utils::FileName::fromLatin1("win32-msvc2005");
925 break;
926 case Abi::WindowsMsvc2008Flavor:
927 result << Utils::FileName::fromLatin1("win32-msvc2008");
928 break;
929 case Abi::WindowsMsvc2010Flavor:
930 result << Utils::FileName::fromLatin1("win32-msvc2010");
931 break;
932 case Abi::WindowsMsvc2012Flavor:
933 result << Utils::FileName::fromLatin1("win32-msvc2012")
934 << Utils::FileName::fromLatin1("win32-msvc2010");
935 break;
936 case Abi::WindowsMsvc2013Flavor:
937 result << Utils::FileName::fromLatin1("win32-msvc2013")
938 << Utils::FileName::fromLatin1("winphone-arm-msvc2013")
939 << Utils::FileName::fromLatin1("winphone-x86-msvc2013")
940 << Utils::FileName::fromLatin1("winrt-arm-msvc2013")
941 << Utils::FileName::fromLatin1("winrt-x86-msvc2013")
942 << Utils::FileName::fromLatin1("winrt-x64-msvc2013")
943 << Utils::FileName::fromLatin1("win32-msvc2012")
944 << Utils::FileName::fromLatin1("win32-msvc2010");
945 break;
946 case Abi::WindowsMsvc2015Flavor:
947 result << Utils::FileName::fromLatin1("win32-msvc2015")
948 << Utils::FileName::fromLatin1("winphone-arm-msvc2015")
949 << Utils::FileName::fromLatin1("winphone-x86-msvc2015")
950 << Utils::FileName::fromLatin1("winrt-arm-msvc2015")
951 << Utils::FileName::fromLatin1("winrt-x86-msvc2015")
952 << Utils::FileName::fromLatin1("winrt-x64-msvc2015");
953 break;
954 case Abi::WindowsMsvc2017Flavor:
955 result << Utils::FileName::fromLatin1("win32-msvc2017")
956 << Utils::FileName::fromLatin1("winrt-arm-msvc2017")
957 << Utils::FileName::fromLatin1("winrt-x86-msvc2017")
958 << Utils::FileName::fromLatin1("winrt-x64-msvc2017");
959 break;
960 case Abi::WindowsMsvc2019Flavor:
961 result << Utils::FileName::fromLatin1("win32-msvc2019")
962 << Utils::FileName::fromLatin1("winrt-arm-msvc2019")
963 << Utils::FileName::fromLatin1("winrt-x86-msvc2019")
964 << Utils::FileName::fromLatin1("winrt-x64-msvc2019");
965 break;
966 default:
967 result.clear();
968 break;
969 }
970 return result;
971}
972
973QVariantMap MsvcToolChain::toMap() const
974{
975 QVariantMap data = ToolChain::toMap();
976 data.insert(QLatin1String(varsBatKeyC), m_vcvarsBat);
977 if (!m_varsBatArg.isEmpty())
978 data.insert(QLatin1String(varsBatArgKeyC), m_varsBatArg);
979 data.insert(QLatin1String(supportedAbiKeyC), m_abi.toString());
980 Utils::EnvironmentItem::sort(&m_environmentModifications);
981 data.insert(QLatin1String(environModsKeyC),
982 Utils::EnvironmentItem::toVariantList(m_environmentModifications));
983 return data;
984}
985
986bool MsvcToolChain::fromMap(const QVariantMap &data)
987{
988 if (!ToolChain::fromMap(data))
989 return false;
990 m_vcvarsBat = QDir::fromNativeSeparators(data.value(QLatin1String(varsBatKeyC)).toString());
991 m_varsBatArg = data.value(QLatin1String(varsBatArgKeyC)).toString();
992 addToAvailableMsvcToolchains(this);
993
994 const QString abiString = data.value(QLatin1String(supportedAbiKeyC)).toString();
995 m_abi = Abi::fromString(abiString);
996 m_environmentModifications = Utils::EnvironmentItem::itemsFromVariantList(
997 data.value(QLatin1String(environModsKeyC)).toList());
998
999 initEnvModWatcher(Utils::runAsync(envModThreadPool(),
1000 &MsvcToolChain::environmentModifications,
1001 m_vcvarsBat,
1002 m_varsBatArg));
1003
1004 return !m_vcvarsBat.isEmpty() && m_abi.isValid();
1005}
1006
1007std::unique_ptr<ToolChainConfigWidget> MsvcToolChain::createConfigurationWidget()
1008{
1009 return std::make_unique<MsvcToolChainConfigWidget>(this);
1010}
1011
1012bool MsvcToolChain::canClone() const
1013{
1014 return true;
1015}
1016
1017ToolChain *MsvcToolChain::clone() const
1018{
1019 return new MsvcToolChain(*this);
1020}
1021
1022bool static hasFlagEffectOnMacros(const QString &flag)
1023{
1024 if (flag.startsWith("-") || flag.startsWith("/")) {
1025 const QString f = flag.mid(1);
1026 if (f.startsWith("I"))
1027 return false; // Skip include paths
1028 if (f.startsWith("w", Qt::CaseInsensitive))
1029 return false; // Skip warning options
1030 }
1031 return true;
1032}
1033
1034ToolChain::MacroInspectionRunner MsvcToolChain::createMacroInspectionRunner() const
1035{
1036 Utils::Environment env(m_lastEnvironment);
1037 addToEnvironment(env);
1038 std::shared_ptr<Cache<MacroInspectionReport, 64>> macroCache = m_predefinedMacrosCache;
1039 const Core::Id lang = language();
1040
1041 // This runner must be thread-safe!
1042 return [this, env, macroCache, lang](const QStringList &cxxflags) {
1043 const QStringList filteredFlags = Utils::filtered(cxxflags, [](const QString &arg) {
1044 return hasFlagEffectOnMacros(arg);
1045 });
1046
1047 const Utils::optional<MacroInspectionReport> cachedMacros = macroCache->check(filteredFlags);
1048 if (cachedMacros)
1049 return cachedMacros.value();
1050
1051 const Macros macros = msvcPredefinedMacros(filteredFlags, env);
1052
1053 const auto report = MacroInspectionReport{macros,
1054 msvcLanguageVersion(filteredFlags, lang, macros)};
1055 macroCache->insert(filteredFlags, report);
1056
1057 return report;
1058 };
1059}
1060
1061Macros MsvcToolChain::predefinedMacros(const QStringList &cxxflags) const
1062{
1063 return createMacroInspectionRunner()(cxxflags).macros;
1064}
1065
1066Utils::LanguageExtensions MsvcToolChain::languageExtensions(const QStringList &cxxflags) const
1067{
1068 using Utils::LanguageExtension;
1069 Utils::LanguageExtensions extensions(LanguageExtension::Microsoft);
1070 if (cxxflags.contains(QLatin1String("/openmp")))
1071 extensions |= LanguageExtension::OpenMP;
1072
1073 // see http://msdn.microsoft.com/en-us/library/0k0w269d%28v=vs.71%29.aspx
1074 if (cxxflags.contains(QLatin1String("/Za")))
1075 extensions &= ~Utils::LanguageExtensions(LanguageExtension::Microsoft);
1076
1077 return extensions;
1078}
1079
1080WarningFlags MsvcToolChain::warningFlags(const QStringList &cflags) const
1081{
1082 WarningFlags flags = WarningFlags::NoWarnings;
1083 foreach (QString flag, cflags) {
1084 if (!flag.isEmpty() && flag[0] == QLatin1Char('-'))
1085 flag[0] = QLatin1Char('/');
1086
1087 if (flag == QLatin1String("/WX")) {
1088 flags |= WarningFlags::AsErrors;
1089 } else if (flag == QLatin1String("/W0") || flag == QLatin1String("/w")) {
1090 inferWarningsForLevel(0, flags);
1091 } else if (flag == QLatin1String("/W1")) {
1092 inferWarningsForLevel(1, flags);
1093 } else if (flag == QLatin1String("/W2")) {
1094 inferWarningsForLevel(2, flags);
1095 } else if (flag == QLatin1String("/W3") || flag == QLatin1String("/W4")
1096 || flag == QLatin1String("/Wall")) {
1097 inferWarningsForLevel(3, flags);
1098 }
1099
1100 WarningFlagAdder add(flag, flags);
1101 if (add.triggered())
1102 continue;
1103 // http://msdn.microsoft.com/en-us/library/ay4h0tc9.aspx
1104 add(4263, WarningFlags::OverloadedVirtual);
1105 // http://msdn.microsoft.com/en-us/library/ytxde1x7.aspx
1106 add(4230, WarningFlags::IgnoredQualfiers);
1107 // not exact match, http://msdn.microsoft.com/en-us/library/0hx5ckb0.aspx
1108 add(4258, WarningFlags::HiddenLocals);
1109 // http://msdn.microsoft.com/en-us/library/wzxffy8c.aspx
1110 add(4265, WarningFlags::NonVirtualDestructor);
1111 // http://msdn.microsoft.com/en-us/library/y92ktdf2%28v=vs.90%29.aspx
1112 add(4018, WarningFlags::SignedComparison);
1113 // http://msdn.microsoft.com/en-us/library/w099eeey%28v=vs.90%29.aspx
1114 add(4068, WarningFlags::UnknownPragma);
1115 // http://msdn.microsoft.com/en-us/library/26kb9fy0%28v=vs.80%29.aspx
1116 add(4100, WarningFlags::UnusedParams);
1117 // http://msdn.microsoft.com/en-us/library/c733d5h9%28v=vs.90%29.aspx
1118 add(4101, WarningFlags::UnusedLocals);
1119 // http://msdn.microsoft.com/en-us/library/xb1db44s%28v=vs.90%29.aspx
1120 add(4189, WarningFlags::UnusedLocals);
1121 // http://msdn.microsoft.com/en-us/library/ttcz0bys%28v=vs.90%29.aspx
1122 add(4996, WarningFlags::Deprecated);
1123 }
1124 return flags;
1125}
1126
1127ToolChain::BuiltInHeaderPathsRunner MsvcToolChain::createBuiltInHeaderPathsRunner() const
1128{
1129 Utils::Environment env(m_lastEnvironment);
1130 addToEnvironment(env);
1131
1132 return [this, env](const QStringList &, const QString &) {
1133 QMutexLocker locker(m_headerPathsMutex);
1134 if (m_headerPaths.isEmpty()) {
1135 foreach (const QString &path,
1136 env.value(QLatin1String("INCLUDE")).split(QLatin1Char(';'))) {
1137 m_headerPaths.append({path, HeaderPathType::BuiltIn});
1138 }
1139 }
1140 return m_headerPaths;
1141 };
1142}
1143
1144HeaderPaths MsvcToolChain::builtInHeaderPaths(const QStringList &cxxflags,
1145 const Utils::FileName &sysRoot) const
1146{
1147 return createBuiltInHeaderPathsRunner()(cxxflags, sysRoot.toString());
1148}
1149
1150void MsvcToolChain::addToEnvironment(Utils::Environment &env) const
1151{
1152 // We cache the full environment (incoming + modifications by setup script).
1153 if (!m_resultEnvironment.size() || env != m_lastEnvironment) {
1154 if (debug)
1155 qDebug() << "addToEnvironment: " << displayName();
1156 m_lastEnvironment = env;
1157 m_resultEnvironment = readEnvironmentSetting(env);
1158 }
1159 env = m_resultEnvironment;
1160}
1161
1162static QString wrappedMakeCommand(const QString &command)
1163{
1164 const QString wrapperPath = QDir::currentPath() + "/msvc_make.bat";
1165 QFile wrapper(wrapperPath);
1166 if (!wrapper.open(QIODevice::WriteOnly))
1167 return command;
1168 QTextStream stream(&wrapper);
1169 stream << "chcp 65001\n";
1170 stream << command << " %*";
1171
1172 return wrapperPath;
1173}
1174
1175QString MsvcToolChain::makeCommand(const Utils::Environment &environment) const
1176{
1177 bool useJom = ProjectExplorerPlugin::projectExplorerSettings().useJom;
1178 const QString jom("jom.exe");
1179 const QString nmake("nmake.exe");
1180 Utils::FileName tmp;
1181
1182 QString command;
1183 if (useJom) {
1184 tmp = environment.searchInPath(jom,
1185 {Utils::FileName::fromString(
1186 QCoreApplication::applicationDirPath())});
1187 if (!tmp.isEmpty())
1188 command = tmp.toString();
1189 }
1190
1191 if (command.isEmpty()) {
1192 tmp = environment.searchInPath(nmake);
1193 if (!tmp.isEmpty())
1194 command = tmp.toString();
1195 }
1196
1197 if (command.isEmpty())
1198 command = useJom ? jom : nmake;
1199
1200 if (environment.hasKey("VSLANG"))
1201 return wrappedMakeCommand(command);
1202
1203 return command;
1204}
1205
1206Utils::FileName MsvcToolChain::compilerCommand() const
1207{
1208 Utils::Environment env = Utils::Environment::systemEnvironment();
1209 addToEnvironment(env);
1210
1211 Utils::FileName clexe
1212 = env.searchInPath(QLatin1String("cl.exe"), {}, [](const Utils::FileName &name) {
1213 QDir dir(QDir::cleanPath(name.toFileInfo().absolutePath() + QStringLiteral("/..")));
1214 do {
1215 if (QFile::exists(dir.absoluteFilePath(QStringLiteral("vcvarsall.bat")))
1216 || QFile::exists(dir.absolutePath() + "/Auxiliary/Build/vcvarsall.bat"))
1217 return true;
1218 } while (dir.cdUp() && !dir.isRoot());
1219 return false;
1220 });
1221 return clexe;
1222}
1223
1224IOutputParser *MsvcToolChain::outputParser() const
1225{
1226 return new MsvcParser;
1227}
1228
1229// --------------------------------------------------------------------------
1230// MsvcBasedToolChainConfigWidget: Creates a simple GUI without error label
1231// to display name and varsBat. Derived classes should add the error label and
1232// call setFromMsvcToolChain().
1233// --------------------------------------------------------------------------
1234
1235MsvcBasedToolChainConfigWidget::MsvcBasedToolChainConfigWidget(ToolChain *tc)
1236 : ToolChainConfigWidget(tc)
1237 , m_nameDisplayLabel(new QLabel(this))
1238 , m_varsBatDisplayLabel(new QLabel(this))
1239{
1240 m_nameDisplayLabel->setTextInteractionFlags(Qt::TextBrowserInteraction);
1241 m_mainLayout->addRow(m_nameDisplayLabel);
1242 m_varsBatDisplayLabel->setTextInteractionFlags(Qt::TextBrowserInteraction);
1243 m_mainLayout->addRow(tr("Initialization:"), m_varsBatDisplayLabel);
1244}
1245
1246static QString msvcVarsToDisplay(const MsvcToolChain &tc)
1247{
1248 QString varsBatDisplay = QDir::toNativeSeparators(tc.varsBat());
1249 if (!tc.varsBatArg().isEmpty()) {
1250 varsBatDisplay += QLatin1Char(' ');
1251 varsBatDisplay += tc.varsBatArg();
1252 }
1253 return varsBatDisplay;
1254}
1255
1256void MsvcBasedToolChainConfigWidget::setFromMsvcToolChain()
1257{
1258 const auto *tc = static_cast<const MsvcToolChain *>(toolChain());
1259 QTC_ASSERT(tc, return );
1260 m_nameDisplayLabel->setText(tc->displayName());
1261 m_varsBatDisplayLabel->setText(msvcVarsToDisplay(*tc));
1262}
1263
1264// --------------------------------------------------------------------------
1265// MsvcToolChainConfigWidget
1266// --------------------------------------------------------------------------
1267
1268MsvcToolChainConfigWidget::MsvcToolChainConfigWidget(ToolChain *tc)
1269 : MsvcBasedToolChainConfigWidget(tc)
1270{
1271 addErrorLabel();
1272 setFromMsvcToolChain();
1273}
1274
1275// --------------------------------------------------------------------------
1276// ClangClToolChainConfigWidget
1277// --------------------------------------------------------------------------
1278
1279ClangClToolChainConfigWidget::ClangClToolChainConfigWidget(ToolChain *tc) :
1280 MsvcBasedToolChainConfigWidget(tc),
1281 m_varsBatDisplayCombo(new QComboBox(this))
1282{
1283 m_mainLayout->removeRow(m_mainLayout->rowCount() - 1);
1284
1285 m_varsBatDisplayCombo->setSizeAdjustPolicy(QComboBox::AdjustToContents);
1286 m_mainLayout->addRow(tr("Initialization:"), m_varsBatDisplayCombo);
1287
1288 if (tc->isAutoDetected()) {
1289 m_llvmDirLabel = new QLabel(this);
1290 m_llvmDirLabel->setTextInteractionFlags(Qt::TextBrowserInteraction);
1291 m_mainLayout->addRow(tr("&Compiler path:"), m_llvmDirLabel);
1292 } else {
1293 const QStringList gnuVersionArgs = QStringList("--version");
1294 m_compilerCommand = new Utils::PathChooser(this);
1295 m_compilerCommand->setExpectedKind(Utils::PathChooser::ExistingCommand);
1296 m_compilerCommand->setCommandVersionArguments(gnuVersionArgs);
1297 m_compilerCommand->setHistoryCompleter("PE.Clang.Command.History");
1298 m_mainLayout->addRow(tr("&Compiler path:"), m_compilerCommand);
1299 }
1300 addErrorLabel();
1301 setFromClangClToolChain();
1302
1303 if (m_compilerCommand) {
1304 connect(m_compilerCommand,
1305 &Utils::PathChooser::rawPathChanged,
1306 this,
1307 &ClangClToolChainConfigWidget::dirty);
1308 }
1309}
1310
1311void ClangClToolChainConfigWidget::setFromClangClToolChain()
1312{
1313 const auto *currentTC = static_cast<const MsvcToolChain *>(toolChain());
1314 m_nameDisplayLabel->setText(currentTC->displayName());
1315 m_varsBatDisplayCombo->clear();
1316 m_varsBatDisplayCombo->addItem(msvcVarsToDisplay(*currentTC));
1317 for (const MsvcToolChain *tc : g_availableMsvcToolchains) {
1318 const QString varsToDisplay = msvcVarsToDisplay(*tc);
1319 if (m_varsBatDisplayCombo->findText(varsToDisplay) == -1)
1320 m_varsBatDisplayCombo->addItem(varsToDisplay);
1321 }
1322
1323 const auto *clangClToolChain = static_cast<const ClangClToolChain *>(toolChain());
1324 if (clangClToolChain->isAutoDetected())
1325 m_llvmDirLabel->setText(QDir::toNativeSeparators(clangClToolChain->clangPath()));
1326 else
1327 m_compilerCommand->setFileName(Utils::FileName::fromString(clangClToolChain->clangPath()));
1328}
1329
1330static const MsvcToolChain *findMsvcToolChain(unsigned char wordWidth, Abi::OSFlavor flavor)
1331{
1332 return Utils::findOrDefault(g_availableMsvcToolchains,
1333 [wordWidth, flavor](const MsvcToolChain *tc) {
1334 const Abi abi = tc->targetAbi();
1335 return abi.osFlavor() == flavor && wordWidth == abi.wordWidth();
1336 });
1337}
1338
1339static const MsvcToolChain *findMsvcToolChain(const QString &displayedVarsBat)
1340{
1341 return Utils::findOrDefault(g_availableMsvcToolchains,
1342 [&displayedVarsBat] (const MsvcToolChain *tc) {
1343 return msvcVarsToDisplay(*tc) == displayedVarsBat;
1344 });
1345}
1346
1347static QVersionNumber clangClVersion(const QString &clangClPath)
1348{
1349 Utils::SynchronousProcess clangClProcess;
1350 const Utils::SynchronousProcessResponse response
1351 = clangClProcess.runBlocking(clangClPath, {QStringLiteral("--version")});
1352 if (response.result != Utils::SynchronousProcessResponse::Finished || response.exitCode != 0)
1353 return {};
1354 const QRegularExpressionMatch match = QRegularExpression(
1355 QStringLiteral("clang version (\\d+(\\.\\d+)+)"))
1356 .match(response.stdOut());
1357 if (!match.hasMatch())
1358 return {};
1359 return QVersionNumber::fromString(match.captured(1));
1360}
1361
1362static const MsvcToolChain *selectMsvcToolChain(const QString &displayedVarsBat,
1363 const QString &clangClPath,
1364 unsigned char wordWidth)
1365{
1366 const MsvcToolChain *toolChain = nullptr;
1367 if (!displayedVarsBat.isEmpty()) {
1368 toolChain = findMsvcToolChain(displayedVarsBat);
1369 if (toolChain)
1370 return toolChain;
1371 }
1372
1373 QTC_CHECK(displayedVarsBat.isEmpty());
1374 const QVersionNumber version = clangClVersion(clangClPath);
1375 if (version.majorVersion() >= 6)
1376 toolChain = findMsvcToolChain(wordWidth, Abi::WindowsMsvc2017Flavor);
1377 if (!toolChain) {
1378 toolChain = findMsvcToolChain(wordWidth, Abi::WindowsMsvc2015Flavor);
1379 if (!toolChain)
1380 toolChain = findMsvcToolChain(wordWidth, Abi::WindowsMsvc2013Flavor);
1381 }
1382 return toolChain;
1383}
1384
1385static QList<ToolChain *> detectClangClToolChainInPath(const QString &clangClPath,
1386 const QList<ToolChain *> &alreadyKnown,
1387 const QString &displayedVarsBat,
1388 bool isDefault = false)
1389{
1390 QList<ToolChain *> res;
1391 const unsigned char wordWidth = Utils::is64BitWindowsBinary(clangClPath) ? 64 : 32;
1392 const MsvcToolChain *toolChain = selectMsvcToolChain(displayedVarsBat, clangClPath, wordWidth);
1393
1394 if (!toolChain) {
1395 qWarning("Unable to find a suitable MSVC version for \"%s\".",
1396 qPrintable(QDir::toNativeSeparators(clangClPath)));
1397 return res;
1398 }
1399
1400 Utils::Environment systemEnvironment = Utils::Environment::systemEnvironment();
1401 const Abi targetAbi = toolChain->targetAbi();
1402 const QString name = QString("%1LLVM %2 bit based on %3")
1403 .arg(QLatin1String(isDefault ? "Default " : ""))
1404 .arg(wordWidth)
1405 .arg(Abi::toString(targetAbi.osFlavor()).toUpper());
1406 for (auto language : {Constants::C_LANGUAGE_ID, Constants::CXX_LANGUAGE_ID}) {
1407 ClangClToolChain *tc = static_cast<ClangClToolChain *>(
1408 Utils::findOrDefault(alreadyKnown, [&](ToolChain *tc) -> bool {
1409 if (tc->typeId() != Constants::CLANG_CL_TOOLCHAIN_TYPEID)
1410 return false;
1411 if (tc->targetAbi() != targetAbi)
1412 return false;
1413 if (tc->language() != language)
1414 return false;
1415 return systemEnvironment.isSameExecutable(tc->compilerCommand().toString(),
1416 clangClPath);
1417 }));
1418 if (!tc) {
1419 tc = new ClangClToolChain(name, clangClPath, language, ToolChain::AutoDetection);
1420 tc->resetMsvcToolChain(toolChain);
1421 }
1422 res << tc;
1423 }
1424 return res;
1425}
1426
1427static QString compilerFromPath(const QString &path)
1428{
1429 return path + "/bin/clang-cl.exe";
1430}
1431
1432void ClangClToolChainConfigWidget::applyImpl()
1433{
1434 Utils::FileName clangClPath = m_compilerCommand->fileName();
1435 auto clangClToolChain = static_cast<ClangClToolChain *>(toolChain());
1436 clangClToolChain->setClangPath(clangClPath.toString());
1437
1438 if (clangClPath.fileName() != "clang-cl.exe") {
1439 clangClToolChain->resetMsvcToolChain();
1440 setFromClangClToolChain();
1441 return;
1442 }
1443
1444 const QString displayedVarsBat = m_varsBatDisplayCombo->currentText();
1445 QList<ToolChain *> results = detectClangClToolChainInPath(clangClPath.toString(),
1446 {},
1447 displayedVarsBat);
1448
1449 if (results.isEmpty()) {
1450 clangClToolChain->resetMsvcToolChain();
1451 } else {
1452 for (const ToolChain *toolchain : results) {
1453 if (toolchain->language() == clangClToolChain->language()) {
1454 clangClToolChain->resetMsvcToolChain(static_cast<const MsvcToolChain *>(toolchain));
1455 break;
1456 }
1457 }
1458
1459 qDeleteAll(results);
1460 }
1461 setFromClangClToolChain();
1462}
1463
1464void ClangClToolChainConfigWidget::discardImpl()
1465{
1466 setFromClangClToolChain();
1467}
1468
1469void ClangClToolChainConfigWidget::makeReadOnlyImpl()
1470{
1471 m_varsBatDisplayCombo->setEnabled(false);
1472}
1473
1474// --------------------------------------------------------------------------
1475// ClangClToolChain, piggy-backing on MSVC2015 and providing the compiler
1476// clang-cl.exe as a [to some extent] compatible drop-in replacement for cl.
1477// --------------------------------------------------------------------------
1478
1479ClangClToolChain::ClangClToolChain(const QString &name,
1480 const QString &clangPath,
1481 Core::Id language,
1482 Detection d)
1483 : MsvcToolChain(Constants::CLANG_CL_TOOLCHAIN_TYPEID, name, Abi(), "", "", language, d)
1484 , m_clangPath(clangPath)
1485{}
1486
1487ClangClToolChain::ClangClToolChain()
1488 : MsvcToolChain(Constants::CLANG_CL_TOOLCHAIN_TYPEID)
1489{}
1490
1491bool ClangClToolChain::isValid() const
1492{
1493 return MsvcToolChain::isValid() && compilerCommand().exists()
1494 && compilerCommand().fileName() == "clang-cl.exe";
1495}
1496
1497void ClangClToolChain::addToEnvironment(Utils::Environment &env) const
1498{
1499 MsvcToolChain::addToEnvironment(env);
1500 QDir path = QFileInfo(m_clangPath).absoluteDir(); // bin folder
1501 env.prependOrSetPath(path.canonicalPath());
1502}
1503
1504Utils::FileName ClangClToolChain::compilerCommand() const
1505{
1506 return Utils::FileName::fromString(m_clangPath);
1507}
1508
1509QString ClangClToolChain::typeDisplayName() const
1510{
1511 return QCoreApplication::translate("ProjectExplorer::ClangToolChainFactory", "Clang");
1512}
1513
1514QList<Utils::FileName> ClangClToolChain::suggestedMkspecList() const
1515{
1516 const QString mkspec = QLatin1String("win32-clang-") + Abi::toString(targetAbi().osFlavor());
1517 return QList<Utils::FileName>{Utils::FileName::fromString(mkspec),
1518 Utils::FileName::fromString("win32-clang-msvc")};
1519}
1520
1521IOutputParser *ClangClToolChain::outputParser() const
1522{
1523 return new ClangClParser;
1524}
1525
1526ToolChain *ClangClToolChain::clone() const
1527{
1528 return new ClangClToolChain(*this);
1529}
1530
1531static inline QString llvmDirKey()
1532{
1533 return QStringLiteral("ProjectExplorer.ClangClToolChain.LlvmDir");
1534}
1535
1536QVariantMap ClangClToolChain::toMap() const
1537{
1538 QVariantMap result = MsvcToolChain::toMap();
1539 result.insert(llvmDirKey(), m_clangPath);
1540 return result;
1541}
1542
1543bool ClangClToolChain::fromMap(const QVariantMap &data)
1544{
1545 if (!MsvcToolChain::fromMap(data))
1546 return false;
1547 const QString clangPath = data.value(llvmDirKey()).toString();
1548 if (clangPath.isEmpty())
1549 return false;
1550 m_clangPath = clangPath;
1551
1552 return true;
1553}
1554
1555std::unique_ptr<ToolChainConfigWidget> ClangClToolChain::createConfigurationWidget()
1556{
1557 return std::make_unique<ClangClToolChainConfigWidget>(this);
1558}
1559
1560void ClangClToolChain::resetMsvcToolChain(const MsvcToolChain *base)
1561{
1562 if (!base) {
1563 m_abi = Abi();
1564 m_vcvarsBat.clear();
1565 setVarsBatArg("");
1566 return;
1567 }
1568 m_abi = base->targetAbi();
1569 m_vcvarsBat = base->varsBat();
1570 setVarsBatArg(base->varsBatArg());
1571
1572 initEnvModWatcher(Utils::runAsync(envModThreadPool(),
1573 &ClangClToolChain::environmentModifications,
1574 m_vcvarsBat,
1575 base->varsBatArg()));
1576}
1577
1578bool ClangClToolChain::operator==(const ToolChain &other) const
1579{
1580 if (!MsvcToolChain::operator==(other))
1581 return false;
1582
1583 const auto *clangClTc = static_cast<const ClangClToolChain *>(&other);
1584 return m_clangPath == clangClTc->m_clangPath;
1585}
1586
1587Macros ClangClToolChain::msvcPredefinedMacros(const QStringList &cxxflags,
1588 const Utils::Environment &env) const
1589{
1590 if (!cxxflags.contains("--driver-mode=g++"))
1591 return MsvcToolChain::msvcPredefinedMacros(cxxflags, env);
1592
1593 Utils::SynchronousProcess cpp;
1594 cpp.setEnvironment(env.toStringList());
1595 cpp.setWorkingDirectory(Utils::TemporaryDirectory::masterDirectoryPath());
1596
1597 QStringList arguments = cxxflags;
1598 arguments.append(gccPredefinedMacrosOptions(language()));
1599 arguments.append("-");
1600 Utils::SynchronousProcessResponse response = cpp.runBlocking(compilerCommand().toString(),
1601 arguments);
1602 if (response.result != Utils::SynchronousProcessResponse::Finished || response.exitCode != 0) {
1603 // Show the warning but still parse the output.
1604 QTC_CHECK(false && "clang-cl exited with non-zero code.");
1605 }
1606
1607 return Macro::toMacros(response.allRawOutput());
1608}
1609
1610Utils::LanguageVersion ClangClToolChain::msvcLanguageVersion(const QStringList &cxxflags,
1611 const Core::Id &language,
1612 const Macros &macros) const
1613{
1614 if (cxxflags.contains("--driver-mode=g++"))
1615 return ToolChain::languageVersion(language, macros);
1616 return MsvcToolChain::msvcLanguageVersion(cxxflags, language, macros);
1617}
1618
1619void ClangClToolChain::toolChainUpdated()
1620{
1621 MsvcToolChain::toolChainUpdated();
1622 ToolChain::toolChainUpdated();
1623}
1624
1625ClangClToolChain::BuiltInHeaderPathsRunner ClangClToolChain::createBuiltInHeaderPathsRunner() const
1626{
1627 {
1628 QMutexLocker locker(m_headerPathsMutex);
1629 m_headerPaths.clear();
1630 }
1631
1632 return MsvcToolChain::createBuiltInHeaderPathsRunner();
1633}
1634
1635// --------------------------------------------------------------------------
1636// MsvcToolChainFactory
1637// --------------------------------------------------------------------------
1638
1639MsvcToolChainFactory::MsvcToolChainFactory()
1640{
1641 setDisplayName(tr("MSVC"));
1642}
1643
1644QSet<Core::Id> MsvcToolChainFactory::supportedLanguages() const
1645{
1646 return {Constants::C_LANGUAGE_ID, Constants::CXX_LANGUAGE_ID};
1647}
1648
1649QString MsvcToolChainFactory::vcVarsBatFor(const QString &basePath,
1650 MsvcToolChain::Platform platform,
1651 const QVersionNumber &v)
1652{
1653 QString result;
1654 if (const MsvcPlatform *p = platformEntry(platform)) {
1655 result += basePath;
1656 // Starting with 15.0 (MSVC2017), the .bat are in one folder.
1657 if (v.majorVersion() <= 14)
1658 result += QLatin1String(p->prefix);
1659 result += QLatin1Char('/');
1660 result += QLatin1String(p->bat);
1661 }
1662 return result;
1663}
1664
1665static QList<ToolChain *> findOrCreateToolChain(const QList<ToolChain *> &alreadyKnown,
1666 const QString &name,
1667 const Abi &abi,
1668 const QString &varsBat,
1669 const QString &varsBatArg,
1670 ToolChain::Detection d = ToolChain::ManualDetection)
1671{
1672 QList<ToolChain *> res;
1673 for (auto language : {Constants::C_LANGUAGE_ID, Constants::CXX_LANGUAGE_ID}) {
1674 ToolChain *tc = Utils::findOrDefault(alreadyKnown, [&](ToolChain *tc) -> bool {
1675 if (tc->typeId() != Constants::MSVC_TOOLCHAIN_TYPEID)
1676 return false;
1677 if (tc->targetAbi() != abi)
1678 return false;
1679 if (tc->language() != language)
1680 return false;
1681 auto mtc = static_cast<MsvcToolChain *>(tc);
1682 return mtc->varsBat() == varsBat && mtc->varsBatArg() == varsBatArg;
1683 });
1684 if (!tc)
1685 tc = new MsvcToolChain(name, abi, varsBat, varsBatArg, language, d);
1686 res << tc;
1687 }
1688 return res;
1689}
1690
1691// Detect build tools introduced with MSVC2015
1692static void detectCppBuildTools2015(QList<ToolChain *> *list)
1693{
1694 struct Entry
1695 {
1696 const char *postFix;
1697 const char *varsBatArg;
1698 Abi::Architecture architecture;
1699 Abi::BinaryFormat format;
1700 unsigned char wordSize;
1701 };
1702
1703 const Entry entries[] = {{" (x86)", "x86", Abi::X86Architecture, Abi::PEFormat, 32},
1704 {" (x64)", "amd64", Abi::X86Architecture, Abi::PEFormat, 64},
1705 {" (x86_arm)", "x86_arm", Abi::ArmArchitecture, Abi::PEFormat, 32},
1706 {" (x64_arm)", "amd64_arm", Abi::ArmArchitecture, Abi::PEFormat, 64}};
1707
1708 const QString name = QStringLiteral("Microsoft Visual C++ Build Tools");
1709 const QString vcVarsBat = windowsProgramFilesDir() + QLatin1Char('/') + name
1710 + QStringLiteral("/vcbuildtools.bat");
1711 if (!QFileInfo(vcVarsBat).isFile())
1712 return;
1713 for (const Entry &e : entries) {
1714 const Abi abi(e.architecture,
1715 Abi::WindowsOS,
1716 Abi::WindowsMsvc2015Flavor,
1717 e.format,
1718 e.wordSize);
1719 for (auto language : {Constants::C_LANGUAGE_ID, Constants::CXX_LANGUAGE_ID}) {
1720 list->append(new MsvcToolChain(name + QLatin1String(e.postFix),
1721 abi,
1722 vcVarsBat,
1723 QLatin1String(e.varsBatArg),
1724 language,
1725 ToolChain::AutoDetection));
1726 }
1727 }
1728}
1729
1730QList<ToolChain *> MsvcToolChainFactory::autoDetect(const QList<ToolChain *> &alreadyKnown)
1731{
1732 QList<ToolChain *> results;
1733
1734 // 1) Installed SDKs preferred over standalone Visual studio
1735 const QSettings
1736 sdkRegistry(QLatin1String(
1737 "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Microsoft SDKs\\Windows"),
1738 QSettings::NativeFormat);
1739 const QString defaultSdkPath = sdkRegistry.value(QLatin1String("CurrentInstallFolder"))
1740 .toString();
1741 if (!defaultSdkPath.isEmpty()) {
1742 foreach (const QString &sdkKey, sdkRegistry.childGroups()) {
1743 const QString name = sdkRegistry.value(sdkKey + QLatin1String("/ProductName")).toString();
1744 const QString folder = sdkRegistry.value(sdkKey + QLatin1String("/InstallationFolder"))
1745 .toString();
1746 if (folder.isEmpty())
1747 continue;
1748
1749 QDir dir(folder);
1750 if (!dir.cd(QLatin1String("bin")))
1751 continue;
1752 QFileInfo fi(dir, QLatin1String("SetEnv.cmd"));
1753 if (!fi.exists())
1754 continue;
1755
1756 QList<ToolChain *> tmp;
1757 const QVector<QPair<MsvcToolChain::Platform, QString>> platforms = {
1758 {MsvcToolChain::x86, "x86"},
1759 {MsvcToolChain::amd64, "x64"},
1760 {MsvcToolChain::ia64, "ia64"},
1761 };
1762 for (const auto &platform : platforms) {
1763 tmp.append(findOrCreateToolChain(alreadyKnown,
1764 generateDisplayName(name,
1765 MsvcToolChain::WindowsSDK,
1766 platform.first),
1767 findAbiOfMsvc(MsvcToolChain::WindowsSDK,
1768 platform.first,
1769 sdkKey),
1770 fi.absoluteFilePath(),
1771 "/" + platform.second,
1772 ToolChain::AutoDetection));
1773 }
1774 // Make sure the default is front.
1775 if (folder == defaultSdkPath)
1776 results = tmp + results;
1777 else
1778 results += tmp;
1779 } // foreach
1780 }
1781
1782 // 2) Installed MSVCs
1783 // prioritized list.
1784 // x86_arm was put before amd64_arm as a workaround for auto detected windows phone
1785 // toolchains. As soon as windows phone builds support x64 cross builds, this change
1786 // can be reverted.
1787 const MsvcToolChain::Platform platforms[] = {MsvcToolChain::x86,
1788 MsvcToolChain::amd64_x86,
1789 MsvcToolChain::amd64,
1790 MsvcToolChain::x86_amd64,
1791 MsvcToolChain::arm,
1792 MsvcToolChain::x86_arm,
1793 MsvcToolChain::amd64_arm,
1794 MsvcToolChain::ia64,
1795 MsvcToolChain::x86_ia64};
1796
1797 foreach (const VisualStudioInstallation &i, detectVisualStudio()) {
1798 for (MsvcToolChain::Platform platform : platforms) {
1799 const bool toolchainInstalled
1800 = QFileInfo(vcVarsBatFor(i.vcVarsPath, platform, i.version)).isFile();
1801 if (hostSupportsPlatform(platform) && toolchainInstalled) {
1802 results.append(
1803 findOrCreateToolChain(alreadyKnown,
1804 generateDisplayName(i.vsName, MsvcToolChain::VS, platform),
1805 findAbiOfMsvc(MsvcToolChain::VS, platform, i.vsName),
1806 i.vcVarsAll,
1807 platformName(platform),
1808 ToolChain::AutoDetection));
1809 }
1810 }
1811 }
1812
1813 detectCppBuildTools2015(&results);
1814
1815 return results;
1816}
1817
1818ClangClToolChainFactory::ClangClToolChainFactory()
1819{
1820 setDisplayName(tr("clang-cl"));
1821}
1822
1823bool ClangClToolChainFactory::canCreate()
1824{
1825 return !g_availableMsvcToolchains.isEmpty();
1826}
1827
1828QList<ToolChain *> ClangClToolChainFactory::autoDetect(const QList<ToolChain *> &alreadyKnown)
1829{
1830 Q_UNUSED(alreadyKnown)
1831
1832#ifdef Q_OS_WIN64
1833 const char registryNode[] = "HKEY_LOCAL_MACHINE\\SOFTWARE\\WOW6432Node\\LLVM\\LLVM";
1834#else
1835 const char registryNode[] = "HKEY_LOCAL_MACHINE\\SOFTWARE\\LLVM\\LLVM";
1836#endif
1837
1838 QList<ToolChain *> results;
1839 QList<ToolChain *> known = alreadyKnown;
1840
1841 QString qtCreatorsClang = Core::ICore::clangExecutable(CLANG_BINDIR);
1842 if (!qtCreatorsClang.isEmpty()) {
1843 qtCreatorsClang = Utils::FileName::fromString(qtCreatorsClang)
1844 .parentDir()
1845 .appendPath("clang-cl.exe")
1846 .toString();
1847 results.append(detectClangClToolChainInPath(qtCreatorsClang, alreadyKnown, "", true));
1848 known.append(results);
1849 }
1850
1851 const QSettings registry(QLatin1String(registryNode), QSettings::NativeFormat);
1852 if (registry.status() == QSettings::NoError) {
1853 const QString path = QDir::cleanPath(registry.value(QStringLiteral(".")).toString());
1854 const QString clangClPath = compilerFromPath(path);
1855 if (!path.isEmpty()) {
1856 results.append(detectClangClToolChainInPath(clangClPath, known, ""));
1857 known.append(results);
1858 }
1859 }
1860
1861 const Utils::Environment systemEnvironment = Utils::Environment::systemEnvironment();
1862 const Utils::FileName clangClPath = systemEnvironment.searchInPath("clang-cl");
1863 if (!clangClPath.isEmpty())
1864 results.append(detectClangClToolChainInPath(clangClPath.toString(), known, ""));
1865
1866 return results;
1867}
1868
1869ToolChain *ClangClToolChainFactory::create(Core::Id l)
1870{
1871 return new ClangClToolChain("clang-cl", "", l, ToolChain::ManualDetection);
1872}
1873
1874bool MsvcToolChain::operator==(const ToolChain &other) const
1875{
1876 if (!ToolChain::operator==(other))
1877 return false;
1878
1879 const auto *msvcTc = dynamic_cast<const MsvcToolChain *>(&other);
1880 return targetAbi() == msvcTc->targetAbi() && m_vcvarsBat == msvcTc->m_vcvarsBat
1881 && m_varsBatArg == msvcTc->m_varsBatArg;
1882}
1883
1884void MsvcToolChain::cancelMsvcToolChainDetection()
1885{
1886 envModThreadPool()->clear();
1887}
1888
1889Utils::optional<QString> MsvcToolChain::generateEnvironmentSettings(const Utils::Environment &env,
1890 const QString &batchFile,
1891 const QString &batchArgs,
1892 QMap<QString, QString> &envPairs)
1893{
1894 const QString marker = "####################";
1895 // Create a temporary file name for the output. Use a temporary file here
1896 // as I don't know another way to do this in Qt...
1897
1898 // Create a batch file to create and save the env settings
1899 Utils::TempFileSaver saver(Utils::TemporaryDirectory::masterDirectoryPath() + "/XXXXXX.bat");
1900
1901 QByteArray call = "call ";
1902 call += Utils::QtcProcess::quoteArg(batchFile).toLocal8Bit();
1903 if (!batchArgs.isEmpty()) {
1904 call += ' ';
1905 call += batchArgs.toLocal8Bit();
1906 }
1907 if (Utils::HostOsInfo::isWindowsHost())
1908 saver.write("chcp 65001\r\n");
1909 saver.write(call + "\r\n");
1910 saver.write("@echo " + marker.toLocal8Bit() + "\r\n");
1911 saver.write("set\r\n");
1912 saver.write("@echo " + marker.toLocal8Bit() + "\r\n");
1913 if (!saver.finalize()) {
1914 qWarning("%s: %s", Q_FUNC_INFO, qPrintable(saver.errorString()));
1915 return QString();
1916 }
1917
1918 Utils::SynchronousProcess run;
1919
1920 // As of WinSDK 7.1, there is logic preventing the path from being set
1921 // correctly if "ORIGINALPATH" is already set. That can cause problems
1922 // if Creator is launched within a session set up by setenv.cmd.
1923 Utils::Environment runEnv = env;
1924 runEnv.unset(QLatin1String("ORIGINALPATH"));
1925 run.setEnvironment(runEnv.toStringList());
1926 run.setTimeoutS(30);
1927 Utils::FileName cmdPath = Utils::FileName::fromUserInput(
1928 QString::fromLocal8Bit(qgetenv("COMSPEC")));
1929 if (cmdPath.isEmpty())
1930 cmdPath = env.searchInPath(QLatin1String("cmd.exe"));
1931 // Windows SDK setup scripts require command line switches for environment expansion.
1932 QStringList cmdArguments({QLatin1String("/E:ON"), QLatin1String("/V:ON"), QLatin1String("/c")});
1933 cmdArguments << QDir::toNativeSeparators(saver.fileName());
1934 if (debug)
1935 qDebug() << "readEnvironmentSetting: " << call << cmdPath << cmdArguments.join(' ')
1936 << " Env: " << runEnv.size();
1937 run.setCodec(QTextCodec::codecForName("UTF-8"));
1938 Utils::SynchronousProcessResponse response = run.runBlocking(cmdPath.toString(), cmdArguments);
1939
1940 if (response.result != Utils::SynchronousProcessResponse::Finished) {
1941 const QString message = !response.stdErr().isEmpty()
1942 ? response.stdErr()
1943 : response.exitMessage(cmdPath.toString(), 10);
1944 qWarning().noquote() << message;
1945 QString command = QDir::toNativeSeparators(batchFile);
1946 if (!batchArgs.isEmpty())
1947 command += ' ' + batchArgs;
1948 return QCoreApplication::translate("ProjectExplorer::Internal::MsvcToolChain",
1949 "Failed to retrieve MSVC Environment from \"%1\":\n"
1950 "%2")
1951 .arg(command, message);
1952 }
1953
1954 // The SDK/MSVC scripts do not return exit codes != 0. Check on stdout.
1955 const QString stdOut = response.stdOut();
1956
1957 //
1958 // Now parse the file to get the environment settings
1959 const int start = stdOut.indexOf(marker);
1960 if (start == -1) {
1961 qWarning("Could not find start marker in stdout output.");
1962 return QString();
1963 }
1964
1965 const int end = stdOut.indexOf(marker, start + 1);
1966 if (end == -1) {
1967 qWarning("Could not find end marker in stdout output.");
1968 return QString();
1969 }
1970
1971 const QString output = stdOut.mid(start, end - start);
1972
1973 foreach (const QString &line, output.split(QLatin1String("\n"))) {
1974 const int pos = line.indexOf('=');
1975 if (pos > 0) {
1976 const QString varName = line.mid(0, pos);
1977 const QString varValue = line.mid(pos + 1);
1978 envPairs.insert(varName, varValue);
1979 }
1980 }
1981
1982 return Utils::nullopt;
1983}
1984
1985bool MsvcToolChainFactory::canRestore(const QVariantMap &data)
1986{
1987 const Core::Id id = typeIdFromMap(data);
1988 return id == Constants::MSVC_TOOLCHAIN_TYPEID;
1989}
1990
1991template<class ToolChainType>
1992ToolChainType *readFromMap(const QVariantMap &data)
1993{
1994 auto result = new ToolChainType;
1995 if (result->fromMap(data))
1996 return result;
1997 delete result;
1998 return nullptr;
1999}
2000
2001ToolChain *MsvcToolChainFactory::restore(const QVariantMap &data)
2002{
2003 return readFromMap<MsvcToolChain>(data);
2004}
2005
2006bool ClangClToolChainFactory::canRestore(const QVariantMap &data)
2007{
2008 const Core::Id id = typeIdFromMap(data);
2009 return id == Constants::CLANG_CL_TOOLCHAIN_TYPEID;
2010}
2011
2012ToolChain *ClangClToolChainFactory::restore(const QVariantMap &data)
2013{
2014 return readFromMap<ClangClToolChain>(data);
2015}
2016
2017MsvcToolChain::WarningFlagAdder::WarningFlagAdder(const QString &flag, WarningFlags &flags)
2018 : m_flags(flags)
2019{
2020 if (flag.startsWith(QLatin1String("-wd"))) {
2021 m_doesEnable = false;
2022 } else if (flag.startsWith(QLatin1String("-w"))) {
2023 m_doesEnable = true;
2024 } else {
2025 m_triggered = true;
2026 return;
2027 }
2028 bool ok = false;
2029 if (m_doesEnable)
2030 m_warningCode = flag.midRef(2).toInt(&ok);
2031 else
2032 m_warningCode = flag.midRef(3).toInt(&ok);
2033 if (!ok)
2034 m_triggered = true;
2035}
2036
2037void MsvcToolChain::WarningFlagAdder::operator()(int warningCode, WarningFlags flagsSet)
2038{
2039 if (m_triggered)
2040 return;
2041 if (warningCode == m_warningCode) {
2042 m_triggered = true;
2043 if (m_doesEnable)
2044 m_flags |= flagsSet;
2045 else
2046 m_flags &= ~flagsSet;
2047 }
2048}
2049
2050bool MsvcToolChain::WarningFlagAdder::triggered() const
2051{
2052 return m_triggered;
2053}
2054
2055} // namespace Internal
2056} // namespace ProjectExplorer
2057