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 if (f.startsWith("Y") || (f.startsWith("F") && f != "F"))
1031 return false; // Skip pch-related flags
1032 }
1033 return true;
1034}
1035
1036ToolChain::MacroInspectionRunner MsvcToolChain::createMacroInspectionRunner() const
1037{
1038 Utils::Environment env(m_lastEnvironment);
1039 addToEnvironment(env);
1040 std::shared_ptr<Cache<MacroInspectionReport, 64>> macroCache = m_predefinedMacrosCache;
1041 const Core::Id lang = language();
1042
1043 // This runner must be thread-safe!
1044 return [this, env, macroCache, lang](const QStringList &cxxflags) {
1045 const QStringList filteredFlags = Utils::filtered(cxxflags, [](const QString &arg) {
1046 return hasFlagEffectOnMacros(arg);
1047 });
1048
1049 const Utils::optional<MacroInspectionReport> cachedMacros = macroCache->check(filteredFlags);
1050 if (cachedMacros)
1051 return cachedMacros.value();
1052
1053 const Macros macros = msvcPredefinedMacros(filteredFlags, env);
1054
1055 const auto report = MacroInspectionReport{macros,
1056 msvcLanguageVersion(filteredFlags, lang, macros)};
1057 macroCache->insert(filteredFlags, report);
1058
1059 return report;
1060 };
1061}
1062
1063Macros MsvcToolChain::predefinedMacros(const QStringList &cxxflags) const
1064{
1065 return createMacroInspectionRunner()(cxxflags).macros;
1066}
1067
1068Utils::LanguageExtensions MsvcToolChain::languageExtensions(const QStringList &cxxflags) const
1069{
1070 using Utils::LanguageExtension;
1071 Utils::LanguageExtensions extensions(LanguageExtension::Microsoft);
1072 if (cxxflags.contains(QLatin1String("/openmp")))
1073 extensions |= LanguageExtension::OpenMP;
1074
1075 // see http://msdn.microsoft.com/en-us/library/0k0w269d%28v=vs.71%29.aspx
1076 if (cxxflags.contains(QLatin1String("/Za")))
1077 extensions &= ~Utils::LanguageExtensions(LanguageExtension::Microsoft);
1078
1079 return extensions;
1080}
1081
1082WarningFlags MsvcToolChain::warningFlags(const QStringList &cflags) const
1083{
1084 WarningFlags flags = WarningFlags::NoWarnings;
1085 foreach (QString flag, cflags) {
1086 if (!flag.isEmpty() && flag[0] == QLatin1Char('-'))
1087 flag[0] = QLatin1Char('/');
1088
1089 if (flag == QLatin1String("/WX")) {
1090 flags |= WarningFlags::AsErrors;
1091 } else if (flag == QLatin1String("/W0") || flag == QLatin1String("/w")) {
1092 inferWarningsForLevel(0, flags);
1093 } else if (flag == QLatin1String("/W1")) {
1094 inferWarningsForLevel(1, flags);
1095 } else if (flag == QLatin1String("/W2")) {
1096 inferWarningsForLevel(2, flags);
1097 } else if (flag == QLatin1String("/W3") || flag == QLatin1String("/W4")
1098 || flag == QLatin1String("/Wall")) {
1099 inferWarningsForLevel(3, flags);
1100 }
1101
1102 WarningFlagAdder add(flag, flags);
1103 if (add.triggered())
1104 continue;
1105 // http://msdn.microsoft.com/en-us/library/ay4h0tc9.aspx
1106 add(4263, WarningFlags::OverloadedVirtual);
1107 // http://msdn.microsoft.com/en-us/library/ytxde1x7.aspx
1108 add(4230, WarningFlags::IgnoredQualfiers);
1109 // not exact match, http://msdn.microsoft.com/en-us/library/0hx5ckb0.aspx
1110 add(4258, WarningFlags::HiddenLocals);
1111 // http://msdn.microsoft.com/en-us/library/wzxffy8c.aspx
1112 add(4265, WarningFlags::NonVirtualDestructor);
1113 // http://msdn.microsoft.com/en-us/library/y92ktdf2%28v=vs.90%29.aspx
1114 add(4018, WarningFlags::SignedComparison);
1115 // http://msdn.microsoft.com/en-us/library/w099eeey%28v=vs.90%29.aspx
1116 add(4068, WarningFlags::UnknownPragma);
1117 // http://msdn.microsoft.com/en-us/library/26kb9fy0%28v=vs.80%29.aspx
1118 add(4100, WarningFlags::UnusedParams);
1119 // http://msdn.microsoft.com/en-us/library/c733d5h9%28v=vs.90%29.aspx
1120 add(4101, WarningFlags::UnusedLocals);
1121 // http://msdn.microsoft.com/en-us/library/xb1db44s%28v=vs.90%29.aspx
1122 add(4189, WarningFlags::UnusedLocals);
1123 // http://msdn.microsoft.com/en-us/library/ttcz0bys%28v=vs.90%29.aspx
1124 add(4996, WarningFlags::Deprecated);
1125 }
1126 return flags;
1127}
1128
1129ToolChain::BuiltInHeaderPathsRunner MsvcToolChain::createBuiltInHeaderPathsRunner() const
1130{
1131 Utils::Environment env(m_lastEnvironment);
1132 addToEnvironment(env);
1133
1134 return [this, env](const QStringList &, const QString &) {
1135 QMutexLocker locker(m_headerPathsMutex);
1136 if (m_headerPaths.isEmpty()) {
1137 foreach (const QString &path,
1138 env.value(QLatin1String("INCLUDE")).split(QLatin1Char(';'))) {
1139 m_headerPaths.append({path, HeaderPathType::BuiltIn});
1140 }
1141 }
1142 return m_headerPaths;
1143 };
1144}
1145
1146HeaderPaths MsvcToolChain::builtInHeaderPaths(const QStringList &cxxflags,
1147 const Utils::FileName &sysRoot) const
1148{
1149 return createBuiltInHeaderPathsRunner()(cxxflags, sysRoot.toString());
1150}
1151
1152void MsvcToolChain::addToEnvironment(Utils::Environment &env) const
1153{
1154 // We cache the full environment (incoming + modifications by setup script).
1155 if (!m_resultEnvironment.size() || env != m_lastEnvironment) {
1156 if (debug)
1157 qDebug() << "addToEnvironment: " << displayName();
1158 m_lastEnvironment = env;
1159 m_resultEnvironment = readEnvironmentSetting(env);
1160 }
1161 env = m_resultEnvironment;
1162}
1163
1164static QString wrappedMakeCommand(const QString &command)
1165{
1166 const QString wrapperPath = QDir::currentPath() + "/msvc_make.bat";
1167 QFile wrapper(wrapperPath);
1168 if (!wrapper.open(QIODevice::WriteOnly))
1169 return command;
1170 QTextStream stream(&wrapper);
1171 stream << "chcp 65001\n";
1172 stream << command << " %*";
1173
1174 return wrapperPath;
1175}
1176
1177QString MsvcToolChain::makeCommand(const Utils::Environment &environment) const
1178{
1179 bool useJom = ProjectExplorerPlugin::projectExplorerSettings().useJom;
1180 const QString jom("jom.exe");
1181 const QString nmake("nmake.exe");
1182 Utils::FileName tmp;
1183
1184 QString command;
1185 if (useJom) {
1186 tmp = environment.searchInPath(jom,
1187 {Utils::FileName::fromString(
1188 QCoreApplication::applicationDirPath())});
1189 if (!tmp.isEmpty())
1190 command = tmp.toString();
1191 }
1192
1193 if (command.isEmpty()) {
1194 tmp = environment.searchInPath(nmake);
1195 if (!tmp.isEmpty())
1196 command = tmp.toString();
1197 }
1198
1199 if (command.isEmpty())
1200 command = useJom ? jom : nmake;
1201
1202 if (environment.hasKey("VSLANG"))
1203 return wrappedMakeCommand(command);
1204
1205 return command;
1206}
1207
1208Utils::FileName MsvcToolChain::compilerCommand() const
1209{
1210 Utils::Environment env = Utils::Environment::systemEnvironment();
1211 addToEnvironment(env);
1212
1213 Utils::FileName clexe
1214 = env.searchInPath(QLatin1String("cl.exe"), {}, [](const Utils::FileName &name) {
1215 QDir dir(QDir::cleanPath(name.toFileInfo().absolutePath() + QStringLiteral("/..")));
1216 do {
1217 if (QFile::exists(dir.absoluteFilePath(QStringLiteral("vcvarsall.bat")))
1218 || QFile::exists(dir.absolutePath() + "/Auxiliary/Build/vcvarsall.bat"))
1219 return true;
1220 } while (dir.cdUp() && !dir.isRoot());
1221 return false;
1222 });
1223 return clexe;
1224}
1225
1226IOutputParser *MsvcToolChain::outputParser() const
1227{
1228 return new MsvcParser;
1229}
1230
1231// --------------------------------------------------------------------------
1232// MsvcBasedToolChainConfigWidget: Creates a simple GUI without error label
1233// to display name and varsBat. Derived classes should add the error label and
1234// call setFromMsvcToolChain().
1235// --------------------------------------------------------------------------
1236
1237MsvcBasedToolChainConfigWidget::MsvcBasedToolChainConfigWidget(ToolChain *tc)
1238 : ToolChainConfigWidget(tc)
1239 , m_nameDisplayLabel(new QLabel(this))
1240 , m_varsBatDisplayLabel(new QLabel(this))
1241{
1242 m_nameDisplayLabel->setTextInteractionFlags(Qt::TextBrowserInteraction);
1243 m_mainLayout->addRow(m_nameDisplayLabel);
1244 m_varsBatDisplayLabel->setTextInteractionFlags(Qt::TextBrowserInteraction);
1245 m_mainLayout->addRow(tr("Initialization:"), m_varsBatDisplayLabel);
1246}
1247
1248static QString msvcVarsToDisplay(const MsvcToolChain &tc)
1249{
1250 QString varsBatDisplay = QDir::toNativeSeparators(tc.varsBat());
1251 if (!tc.varsBatArg().isEmpty()) {
1252 varsBatDisplay += QLatin1Char(' ');
1253 varsBatDisplay += tc.varsBatArg();
1254 }
1255 return varsBatDisplay;
1256}
1257
1258void MsvcBasedToolChainConfigWidget::setFromMsvcToolChain()
1259{
1260 const auto *tc = static_cast<const MsvcToolChain *>(toolChain());
1261 QTC_ASSERT(tc, return );
1262 m_nameDisplayLabel->setText(tc->displayName());
1263 m_varsBatDisplayLabel->setText(msvcVarsToDisplay(*tc));
1264}
1265
1266// --------------------------------------------------------------------------
1267// MsvcToolChainConfigWidget
1268// --------------------------------------------------------------------------
1269
1270MsvcToolChainConfigWidget::MsvcToolChainConfigWidget(ToolChain *tc)
1271 : MsvcBasedToolChainConfigWidget(tc)
1272{
1273 addErrorLabel();
1274 setFromMsvcToolChain();
1275}
1276
1277// --------------------------------------------------------------------------
1278// ClangClToolChainConfigWidget
1279// --------------------------------------------------------------------------
1280
1281ClangClToolChainConfigWidget::ClangClToolChainConfigWidget(ToolChain *tc) :
1282 MsvcBasedToolChainConfigWidget(tc),
1283 m_varsBatDisplayCombo(new QComboBox(this))
1284{
1285 m_mainLayout->removeRow(m_mainLayout->rowCount() - 1);
1286
1287 m_varsBatDisplayCombo->setSizeAdjustPolicy(QComboBox::AdjustToContents);
1288 m_mainLayout->addRow(tr("Initialization:"), m_varsBatDisplayCombo);
1289
1290 if (tc->isAutoDetected()) {
1291 m_llvmDirLabel = new QLabel(this);
1292 m_llvmDirLabel->setTextInteractionFlags(Qt::TextBrowserInteraction);
1293 m_mainLayout->addRow(tr("&Compiler path:"), m_llvmDirLabel);
1294 } else {
1295 const QStringList gnuVersionArgs = QStringList("--version");
1296 m_compilerCommand = new Utils::PathChooser(this);
1297 m_compilerCommand->setExpectedKind(Utils::PathChooser::ExistingCommand);
1298 m_compilerCommand->setCommandVersionArguments(gnuVersionArgs);
1299 m_compilerCommand->setHistoryCompleter("PE.Clang.Command.History");
1300 m_mainLayout->addRow(tr("&Compiler path:"), m_compilerCommand);
1301 }
1302 addErrorLabel();
1303 setFromClangClToolChain();
1304
1305 if (m_compilerCommand) {
1306 connect(m_compilerCommand,
1307 &Utils::PathChooser::rawPathChanged,
1308 this,
1309 &ClangClToolChainConfigWidget::dirty);
1310 }
1311}
1312
1313void ClangClToolChainConfigWidget::setFromClangClToolChain()
1314{
1315 const auto *currentTC = static_cast<const MsvcToolChain *>(toolChain());
1316 m_nameDisplayLabel->setText(currentTC->displayName());
1317 m_varsBatDisplayCombo->clear();
1318 m_varsBatDisplayCombo->addItem(msvcVarsToDisplay(*currentTC));
1319 for (const MsvcToolChain *tc : g_availableMsvcToolchains) {
1320 const QString varsToDisplay = msvcVarsToDisplay(*tc);
1321 if (m_varsBatDisplayCombo->findText(varsToDisplay) == -1)
1322 m_varsBatDisplayCombo->addItem(varsToDisplay);
1323 }
1324
1325 const auto *clangClToolChain = static_cast<const ClangClToolChain *>(toolChain());
1326 if (clangClToolChain->isAutoDetected())
1327 m_llvmDirLabel->setText(QDir::toNativeSeparators(clangClToolChain->clangPath()));
1328 else
1329 m_compilerCommand->setFileName(Utils::FileName::fromString(clangClToolChain->clangPath()));
1330}
1331
1332static const MsvcToolChain *findMsvcToolChain(unsigned char wordWidth, Abi::OSFlavor flavor)
1333{
1334 return Utils::findOrDefault(g_availableMsvcToolchains,
1335 [wordWidth, flavor](const MsvcToolChain *tc) {
1336 const Abi abi = tc->targetAbi();
1337 return abi.osFlavor() == flavor && wordWidth == abi.wordWidth();
1338 });
1339}
1340
1341static const MsvcToolChain *findMsvcToolChain(const QString &displayedVarsBat)
1342{
1343 return Utils::findOrDefault(g_availableMsvcToolchains,
1344 [&displayedVarsBat] (const MsvcToolChain *tc) {
1345 return msvcVarsToDisplay(*tc) == displayedVarsBat;
1346 });
1347}
1348
1349static QVersionNumber clangClVersion(const QString &clangClPath)
1350{
1351 Utils::SynchronousProcess clangClProcess;
1352 const Utils::SynchronousProcessResponse response
1353 = clangClProcess.runBlocking(clangClPath, {QStringLiteral("--version")});
1354 if (response.result != Utils::SynchronousProcessResponse::Finished || response.exitCode != 0)
1355 return {};
1356 const QRegularExpressionMatch match = QRegularExpression(
1357 QStringLiteral("clang version (\\d+(\\.\\d+)+)"))
1358 .match(response.stdOut());
1359 if (!match.hasMatch())
1360 return {};
1361 return QVersionNumber::fromString(match.captured(1));
1362}
1363
1364static const MsvcToolChain *selectMsvcToolChain(const QString &displayedVarsBat,
1365 const QString &clangClPath,
1366 unsigned char wordWidth)
1367{
1368 const MsvcToolChain *toolChain = nullptr;
1369 if (!displayedVarsBat.isEmpty()) {
1370 toolChain = findMsvcToolChain(displayedVarsBat);
1371 if (toolChain)
1372 return toolChain;
1373 }
1374
1375 QTC_CHECK(displayedVarsBat.isEmpty());
1376 const QVersionNumber version = clangClVersion(clangClPath);
1377 if (version.majorVersion() >= 6)
1378 toolChain = findMsvcToolChain(wordWidth, Abi::WindowsMsvc2017Flavor);
1379 if (!toolChain) {
1380 toolChain = findMsvcToolChain(wordWidth, Abi::WindowsMsvc2015Flavor);
1381 if (!toolChain)
1382 toolChain = findMsvcToolChain(wordWidth, Abi::WindowsMsvc2013Flavor);
1383 }
1384 return toolChain;
1385}
1386
1387static QList<ToolChain *> detectClangClToolChainInPath(const QString &clangClPath,
1388 const QList<ToolChain *> &alreadyKnown,
1389 const QString &displayedVarsBat,
1390 bool isDefault = false)
1391{
1392 QList<ToolChain *> res;
1393 const unsigned char wordWidth = Utils::is64BitWindowsBinary(clangClPath) ? 64 : 32;
1394 const MsvcToolChain *toolChain = selectMsvcToolChain(displayedVarsBat, clangClPath, wordWidth);
1395
1396 if (!toolChain) {
1397 qWarning("Unable to find a suitable MSVC version for \"%s\".",
1398 qPrintable(QDir::toNativeSeparators(clangClPath)));
1399 return res;
1400 }
1401
1402 Utils::Environment systemEnvironment = Utils::Environment::systemEnvironment();
1403 const Abi targetAbi = toolChain->targetAbi();
1404 const QString name = QString("%1LLVM %2 bit based on %3")
1405 .arg(QLatin1String(isDefault ? "Default " : ""))
1406 .arg(wordWidth)
1407 .arg(Abi::toString(targetAbi.osFlavor()).toUpper());
1408 for (auto language : {Constants::C_LANGUAGE_ID, Constants::CXX_LANGUAGE_ID}) {
1409 ClangClToolChain *tc = static_cast<ClangClToolChain *>(
1410 Utils::findOrDefault(alreadyKnown, [&](ToolChain *tc) -> bool {
1411 if (tc->typeId() != Constants::CLANG_CL_TOOLCHAIN_TYPEID)
1412 return false;
1413 if (tc->targetAbi() != targetAbi)
1414 return false;
1415 if (tc->language() != language)
1416 return false;
1417 return systemEnvironment.isSameExecutable(tc->compilerCommand().toString(),
1418 clangClPath);
1419 }));
1420 if (!tc) {
1421 tc = new ClangClToolChain(name, clangClPath, language, ToolChain::AutoDetection);
1422 tc->resetMsvcToolChain(toolChain);
1423 }
1424 res << tc;
1425 }
1426 return res;
1427}
1428
1429static QString compilerFromPath(const QString &path)
1430{
1431 return path + "/bin/clang-cl.exe";
1432}
1433
1434void ClangClToolChainConfigWidget::applyImpl()
1435{
1436 Utils::FileName clangClPath = m_compilerCommand->fileName();
1437 auto clangClToolChain = static_cast<ClangClToolChain *>(toolChain());
1438 clangClToolChain->setClangPath(clangClPath.toString());
1439
1440 if (clangClPath.fileName() != "clang-cl.exe") {
1441 clangClToolChain->resetMsvcToolChain();
1442 setFromClangClToolChain();
1443 return;
1444 }
1445
1446 const QString displayedVarsBat = m_varsBatDisplayCombo->currentText();
1447 QList<ToolChain *> results = detectClangClToolChainInPath(clangClPath.toString(),
1448 {},
1449 displayedVarsBat);
1450
1451 if (results.isEmpty()) {
1452 clangClToolChain->resetMsvcToolChain();
1453 } else {
1454 for (const ToolChain *toolchain : results) {
1455 if (toolchain->language() == clangClToolChain->language()) {
1456 clangClToolChain->resetMsvcToolChain(static_cast<const MsvcToolChain *>(toolchain));
1457 break;
1458 }
1459 }
1460
1461 qDeleteAll(results);
1462 }
1463 setFromClangClToolChain();
1464}
1465
1466void ClangClToolChainConfigWidget::discardImpl()
1467{
1468 setFromClangClToolChain();
1469}
1470
1471void ClangClToolChainConfigWidget::makeReadOnlyImpl()
1472{
1473 m_varsBatDisplayCombo->setEnabled(false);
1474}
1475
1476// --------------------------------------------------------------------------
1477// ClangClToolChain, piggy-backing on MSVC2015 and providing the compiler
1478// clang-cl.exe as a [to some extent] compatible drop-in replacement for cl.
1479// --------------------------------------------------------------------------
1480
1481ClangClToolChain::ClangClToolChain(const QString &name,
1482 const QString &clangPath,
1483 Core::Id language,
1484 Detection d)
1485 : MsvcToolChain(Constants::CLANG_CL_TOOLCHAIN_TYPEID, name, Abi(), "", "", language, d)
1486 , m_clangPath(clangPath)
1487{}
1488
1489ClangClToolChain::ClangClToolChain()
1490 : MsvcToolChain(Constants::CLANG_CL_TOOLCHAIN_TYPEID)
1491{}
1492
1493bool ClangClToolChain::isValid() const
1494{
1495 return MsvcToolChain::isValid() && compilerCommand().exists()
1496 && compilerCommand().fileName() == "clang-cl.exe";
1497}
1498
1499void ClangClToolChain::addToEnvironment(Utils::Environment &env) const
1500{
1501 MsvcToolChain::addToEnvironment(env);
1502 QDir path = QFileInfo(m_clangPath).absoluteDir(); // bin folder
1503 env.prependOrSetPath(path.canonicalPath());
1504}
1505
1506Utils::FileName ClangClToolChain::compilerCommand() const
1507{
1508 return Utils::FileName::fromString(m_clangPath);
1509}
1510
1511QString ClangClToolChain::typeDisplayName() const
1512{
1513 return QCoreApplication::translate("ProjectExplorer::ClangToolChainFactory", "Clang");
1514}
1515
1516QList<Utils::FileName> ClangClToolChain::suggestedMkspecList() const
1517{
1518 const QString mkspec = QLatin1String("win32-clang-") + Abi::toString(targetAbi().osFlavor());
1519 return QList<Utils::FileName>{Utils::FileName::fromString(mkspec),
1520 Utils::FileName::fromString("win32-clang-msvc")};
1521}
1522
1523IOutputParser *ClangClToolChain::outputParser() const
1524{
1525 return new ClangClParser;
1526}
1527
1528ToolChain *ClangClToolChain::clone() const
1529{
1530 return new ClangClToolChain(*this);
1531}
1532
1533static inline QString llvmDirKey()
1534{
1535 return QStringLiteral("ProjectExplorer.ClangClToolChain.LlvmDir");
1536}
1537
1538QVariantMap ClangClToolChain::toMap() const
1539{
1540 QVariantMap result = MsvcToolChain::toMap();
1541 result.insert(llvmDirKey(), m_clangPath);
1542 return result;
1543}
1544
1545bool ClangClToolChain::fromMap(const QVariantMap &data)
1546{
1547 if (!MsvcToolChain::fromMap(data))
1548 return false;
1549 const QString clangPath = data.value(llvmDirKey()).toString();
1550 if (clangPath.isEmpty())
1551 return false;
1552 m_clangPath = clangPath;
1553
1554 return true;
1555}
1556
1557std::unique_ptr<ToolChainConfigWidget> ClangClToolChain::createConfigurationWidget()
1558{
1559 return std::make_unique<ClangClToolChainConfigWidget>(this);
1560}
1561
1562void ClangClToolChain::resetMsvcToolChain(const MsvcToolChain *base)
1563{
1564 if (!base) {
1565 m_abi = Abi();
1566 m_vcvarsBat.clear();
1567 setVarsBatArg("");
1568 return;
1569 }
1570 m_abi = base->targetAbi();
1571 m_vcvarsBat = base->varsBat();
1572 setVarsBatArg(base->varsBatArg());
1573
1574 initEnvModWatcher(Utils::runAsync(envModThreadPool(),
1575 &ClangClToolChain::environmentModifications,
1576 m_vcvarsBat,
1577 base->varsBatArg()));
1578}
1579
1580bool ClangClToolChain::operator==(const ToolChain &other) const
1581{
1582 if (!MsvcToolChain::operator==(other))
1583 return false;
1584
1585 const auto *clangClTc = static_cast<const ClangClToolChain *>(&other);
1586 return m_clangPath == clangClTc->m_clangPath;
1587}
1588
1589Macros ClangClToolChain::msvcPredefinedMacros(const QStringList &cxxflags,
1590 const Utils::Environment &env) const
1591{
1592 if (!cxxflags.contains("--driver-mode=g++"))
1593 return MsvcToolChain::msvcPredefinedMacros(cxxflags, env);
1594
1595 Utils::SynchronousProcess cpp;
1596 cpp.setEnvironment(env.toStringList());
1597 cpp.setWorkingDirectory(Utils::TemporaryDirectory::masterDirectoryPath());
1598
1599 QStringList arguments = cxxflags;
1600 arguments.append(gccPredefinedMacrosOptions(language()));
1601 arguments.append("-");
1602 Utils::SynchronousProcessResponse response = cpp.runBlocking(compilerCommand().toString(),
1603 arguments);
1604 if (response.result != Utils::SynchronousProcessResponse::Finished || response.exitCode != 0) {
1605 // Show the warning but still parse the output.
1606 QTC_CHECK(false && "clang-cl exited with non-zero code.");
1607 }
1608
1609 return Macro::toMacros(response.allRawOutput());
1610}
1611
1612Utils::LanguageVersion ClangClToolChain::msvcLanguageVersion(const QStringList &cxxflags,
1613 const Core::Id &language,
1614 const Macros &macros) const
1615{
1616 if (cxxflags.contains("--driver-mode=g++"))
1617 return ToolChain::languageVersion(language, macros);
1618 return MsvcToolChain::msvcLanguageVersion(cxxflags, language, macros);
1619}
1620
1621void ClangClToolChain::toolChainUpdated()
1622{
1623 MsvcToolChain::toolChainUpdated();
1624 ToolChain::toolChainUpdated();
1625}
1626
1627ClangClToolChain::BuiltInHeaderPathsRunner ClangClToolChain::createBuiltInHeaderPathsRunner() const
1628{
1629 {
1630 QMutexLocker locker(m_headerPathsMutex);
1631 m_headerPaths.clear();
1632 }
1633
1634 return MsvcToolChain::createBuiltInHeaderPathsRunner();
1635}
1636
1637// --------------------------------------------------------------------------
1638// MsvcToolChainFactory
1639// --------------------------------------------------------------------------
1640
1641MsvcToolChainFactory::MsvcToolChainFactory()
1642{
1643 setDisplayName(tr("MSVC"));
1644}
1645
1646QSet<Core::Id> MsvcToolChainFactory::supportedLanguages() const
1647{
1648 return {Constants::C_LANGUAGE_ID, Constants::CXX_LANGUAGE_ID};
1649}
1650
1651QString MsvcToolChainFactory::vcVarsBatFor(const QString &basePath,
1652 MsvcToolChain::Platform platform,
1653 const QVersionNumber &v)
1654{
1655 QString result;
1656 if (const MsvcPlatform *p = platformEntry(platform)) {
1657 result += basePath;
1658 // Starting with 15.0 (MSVC2017), the .bat are in one folder.
1659 if (v.majorVersion() <= 14)
1660 result += QLatin1String(p->prefix);
1661 result += QLatin1Char('/');
1662 result += QLatin1String(p->bat);
1663 }
1664 return result;
1665}
1666
1667static QList<ToolChain *> findOrCreateToolChain(const QList<ToolChain *> &alreadyKnown,
1668 const QString &name,
1669 const Abi &abi,
1670 const QString &varsBat,
1671 const QString &varsBatArg,
1672 ToolChain::Detection d = ToolChain::ManualDetection)
1673{
1674 QList<ToolChain *> res;
1675 for (auto language : {Constants::C_LANGUAGE_ID, Constants::CXX_LANGUAGE_ID}) {
1676 ToolChain *tc = Utils::findOrDefault(alreadyKnown, [&](ToolChain *tc) -> bool {
1677 if (tc->typeId() != Constants::MSVC_TOOLCHAIN_TYPEID)
1678 return false;
1679 if (tc->targetAbi() != abi)
1680 return false;
1681 if (tc->language() != language)
1682 return false;
1683 auto mtc = static_cast<MsvcToolChain *>(tc);
1684 return mtc->varsBat() == varsBat && mtc->varsBatArg() == varsBatArg;
1685 });
1686 if (!tc)
1687 tc = new MsvcToolChain(name, abi, varsBat, varsBatArg, language, d);
1688 res << tc;
1689 }
1690 return res;
1691}
1692
1693// Detect build tools introduced with MSVC2015
1694static void detectCppBuildTools2015(QList<ToolChain *> *list)
1695{
1696 struct Entry
1697 {
1698 const char *postFix;
1699 const char *varsBatArg;
1700 Abi::Architecture architecture;
1701 Abi::BinaryFormat format;
1702 unsigned char wordSize;
1703 };
1704
1705 const Entry entries[] = {{" (x86)", "x86", Abi::X86Architecture, Abi::PEFormat, 32},
1706 {" (x64)", "amd64", Abi::X86Architecture, Abi::PEFormat, 64},
1707 {" (x86_arm)", "x86_arm", Abi::ArmArchitecture, Abi::PEFormat, 32},
1708 {" (x64_arm)", "amd64_arm", Abi::ArmArchitecture, Abi::PEFormat, 64}};
1709
1710 const QString name = QStringLiteral("Microsoft Visual C++ Build Tools");
1711 const QString vcVarsBat = windowsProgramFilesDir() + QLatin1Char('/') + name
1712 + QStringLiteral("/vcbuildtools.bat");
1713 if (!QFileInfo(vcVarsBat).isFile())
1714 return;
1715 for (const Entry &e : entries) {
1716 const Abi abi(e.architecture,
1717 Abi::WindowsOS,
1718 Abi::WindowsMsvc2015Flavor,
1719 e.format,
1720 e.wordSize);
1721 for (auto language : {Constants::C_LANGUAGE_ID, Constants::CXX_LANGUAGE_ID}) {
1722 list->append(new MsvcToolChain(name + QLatin1String(e.postFix),
1723 abi,
1724 vcVarsBat,
1725 QLatin1String(e.varsBatArg),
1726 language,
1727 ToolChain::AutoDetection));
1728 }
1729 }
1730}
1731
1732QList<ToolChain *> MsvcToolChainFactory::autoDetect(const QList<ToolChain *> &alreadyKnown)
1733{
1734 QList<ToolChain *> results;
1735
1736 // 1) Installed SDKs preferred over standalone Visual studio
1737 const QSettings
1738 sdkRegistry(QLatin1String(
1739 "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Microsoft SDKs\\Windows"),
1740 QSettings::NativeFormat);
1741 const QString defaultSdkPath = sdkRegistry.value(QLatin1String("CurrentInstallFolder"))
1742 .toString();
1743 if (!defaultSdkPath.isEmpty()) {
1744 foreach (const QString &sdkKey, sdkRegistry.childGroups()) {
1745 const QString name = sdkRegistry.value(sdkKey + QLatin1String("/ProductName")).toString();
1746 const QString folder = sdkRegistry.value(sdkKey + QLatin1String("/InstallationFolder"))
1747 .toString();
1748 if (folder.isEmpty())
1749 continue;
1750
1751 QDir dir(folder);
1752 if (!dir.cd(QLatin1String("bin")))
1753 continue;
1754 QFileInfo fi(dir, QLatin1String("SetEnv.cmd"));
1755 if (!fi.exists())
1756 continue;
1757
1758 QList<ToolChain *> tmp;
1759 const QVector<QPair<MsvcToolChain::Platform, QString>> platforms = {
1760 {MsvcToolChain::x86, "x86"},
1761 {MsvcToolChain::amd64, "x64"},
1762 {MsvcToolChain::ia64, "ia64"},
1763 };
1764 for (const auto &platform : platforms) {
1765 tmp.append(findOrCreateToolChain(alreadyKnown,
1766 generateDisplayName(name,
1767 MsvcToolChain::WindowsSDK,
1768 platform.first),
1769 findAbiOfMsvc(MsvcToolChain::WindowsSDK,
1770 platform.first,
1771 sdkKey),
1772 fi.absoluteFilePath(),
1773 "/" + platform.second,
1774 ToolChain::AutoDetection));
1775 }
1776 // Make sure the default is front.
1777 if (folder == defaultSdkPath)
1778 results = tmp + results;
1779 else
1780 results += tmp;
1781 } // foreach
1782 }
1783
1784 // 2) Installed MSVCs
1785 // prioritized list.
1786 // x86_arm was put before amd64_arm as a workaround for auto detected windows phone
1787 // toolchains. As soon as windows phone builds support x64 cross builds, this change
1788 // can be reverted.
1789 const MsvcToolChain::Platform platforms[] = {MsvcToolChain::x86,
1790 MsvcToolChain::amd64_x86,
1791 MsvcToolChain::amd64,
1792 MsvcToolChain::x86_amd64,
1793 MsvcToolChain::arm,
1794 MsvcToolChain::x86_arm,
1795 MsvcToolChain::amd64_arm,
1796 MsvcToolChain::ia64,
1797 MsvcToolChain::x86_ia64};
1798
1799 foreach (const VisualStudioInstallation &i, detectVisualStudio()) {
1800 for (MsvcToolChain::Platform platform : platforms) {
1801 const bool toolchainInstalled
1802 = QFileInfo(vcVarsBatFor(i.vcVarsPath, platform, i.version)).isFile();
1803 if (hostSupportsPlatform(platform) && toolchainInstalled) {
1804 results.append(
1805 findOrCreateToolChain(alreadyKnown,
1806 generateDisplayName(i.vsName, MsvcToolChain::VS, platform),
1807 findAbiOfMsvc(MsvcToolChain::VS, platform, i.vsName),
1808 i.vcVarsAll,
1809 platformName(platform),
1810 ToolChain::AutoDetection));
1811 }
1812 }
1813 }
1814
1815 detectCppBuildTools2015(&results);
1816
1817 return results;
1818}
1819
1820ClangClToolChainFactory::ClangClToolChainFactory()
1821{
1822 setDisplayName(tr("clang-cl"));
1823}
1824
1825bool ClangClToolChainFactory::canCreate()
1826{
1827 return !g_availableMsvcToolchains.isEmpty();
1828}
1829
1830QList<ToolChain *> ClangClToolChainFactory::autoDetect(const QList<ToolChain *> &alreadyKnown)
1831{
1832 Q_UNUSED(alreadyKnown)
1833
1834#ifdef Q_OS_WIN64
1835 const char registryNode[] = "HKEY_LOCAL_MACHINE\\SOFTWARE\\WOW6432Node\\LLVM\\LLVM";
1836#else
1837 const char registryNode[] = "HKEY_LOCAL_MACHINE\\SOFTWARE\\LLVM\\LLVM";
1838#endif
1839
1840 QList<ToolChain *> results;
1841 QList<ToolChain *> known = alreadyKnown;
1842
1843 QString qtCreatorsClang = Core::ICore::clangExecutable(CLANG_BINDIR);
1844 if (!qtCreatorsClang.isEmpty()) {
1845 qtCreatorsClang = Utils::FileName::fromString(qtCreatorsClang)
1846 .parentDir()
1847 .appendPath("clang-cl.exe")
1848 .toString();
1849 results.append(detectClangClToolChainInPath(qtCreatorsClang, alreadyKnown, "", true));
1850 known.append(results);
1851 }
1852
1853 const QSettings registry(QLatin1String(registryNode), QSettings::NativeFormat);
1854 if (registry.status() == QSettings::NoError) {
1855 const QString path = QDir::cleanPath(registry.value(QStringLiteral(".")).toString());
1856 const QString clangClPath = compilerFromPath(path);
1857 if (!path.isEmpty()) {
1858 results.append(detectClangClToolChainInPath(clangClPath, known, ""));
1859 known.append(results);
1860 }
1861 }
1862
1863 const Utils::Environment systemEnvironment = Utils::Environment::systemEnvironment();
1864 const Utils::FileName clangClPath = systemEnvironment.searchInPath("clang-cl");
1865 if (!clangClPath.isEmpty())
1866 results.append(detectClangClToolChainInPath(clangClPath.toString(), known, ""));
1867
1868 return results;
1869}
1870
1871ToolChain *ClangClToolChainFactory::create(Core::Id l)
1872{
1873 return new ClangClToolChain("clang-cl", "", l, ToolChain::ManualDetection);
1874}
1875
1876bool MsvcToolChain::operator==(const ToolChain &other) const
1877{
1878 if (!ToolChain::operator==(other))
1879 return false;
1880
1881 const auto *msvcTc = dynamic_cast<const MsvcToolChain *>(&other);
1882 return targetAbi() == msvcTc->targetAbi() && m_vcvarsBat == msvcTc->m_vcvarsBat
1883 && m_varsBatArg == msvcTc->m_varsBatArg;
1884}
1885
1886void MsvcToolChain::cancelMsvcToolChainDetection()
1887{
1888 envModThreadPool()->clear();
1889}
1890
1891Utils::optional<QString> MsvcToolChain::generateEnvironmentSettings(const Utils::Environment &env,
1892 const QString &batchFile,
1893 const QString &batchArgs,
1894 QMap<QString, QString> &envPairs)
1895{
1896 const QString marker = "####################";
1897 // Create a temporary file name for the output. Use a temporary file here
1898 // as I don't know another way to do this in Qt...
1899
1900 // Create a batch file to create and save the env settings
1901 Utils::TempFileSaver saver(Utils::TemporaryDirectory::masterDirectoryPath() + "/XXXXXX.bat");
1902
1903 QByteArray call = "call ";
1904 call += Utils::QtcProcess::quoteArg(batchFile).toLocal8Bit();
1905 if (!batchArgs.isEmpty()) {
1906 call += ' ';
1907 call += batchArgs.toLocal8Bit();
1908 }
1909 if (Utils::HostOsInfo::isWindowsHost())
1910 saver.write("chcp 65001\r\n");
1911 saver.write(call + "\r\n");
1912 saver.write("@echo " + marker.toLocal8Bit() + "\r\n");
1913 saver.write("set\r\n");
1914 saver.write("@echo " + marker.toLocal8Bit() + "\r\n");
1915 if (!saver.finalize()) {
1916 qWarning("%s: %s", Q_FUNC_INFO, qPrintable(saver.errorString()));
1917 return QString();
1918 }
1919
1920 Utils::SynchronousProcess run;
1921
1922 // As of WinSDK 7.1, there is logic preventing the path from being set
1923 // correctly if "ORIGINALPATH" is already set. That can cause problems
1924 // if Creator is launched within a session set up by setenv.cmd.
1925 Utils::Environment runEnv = env;
1926 runEnv.unset(QLatin1String("ORIGINALPATH"));
1927 run.setEnvironment(runEnv.toStringList());
1928 run.setTimeoutS(30);
1929 Utils::FileName cmdPath = Utils::FileName::fromUserInput(
1930 QString::fromLocal8Bit(qgetenv("COMSPEC")));
1931 if (cmdPath.isEmpty())
1932 cmdPath = env.searchInPath(QLatin1String("cmd.exe"));
1933 // Windows SDK setup scripts require command line switches for environment expansion.
1934 QStringList cmdArguments({QLatin1String("/E:ON"), QLatin1String("/V:ON"), QLatin1String("/c")});
1935 cmdArguments << QDir::toNativeSeparators(saver.fileName());
1936 if (debug)
1937 qDebug() << "readEnvironmentSetting: " << call << cmdPath << cmdArguments.join(' ')
1938 << " Env: " << runEnv.size();
1939 run.setCodec(QTextCodec::codecForName("UTF-8"));
1940 Utils::SynchronousProcessResponse response = run.runBlocking(cmdPath.toString(), cmdArguments);
1941
1942 if (response.result != Utils::SynchronousProcessResponse::Finished) {
1943 const QString message = !response.stdErr().isEmpty()
1944 ? response.stdErr()
1945 : response.exitMessage(cmdPath.toString(), 10);
1946 qWarning().noquote() << message;
1947 QString command = QDir::toNativeSeparators(batchFile);
1948 if (!batchArgs.isEmpty())
1949 command += ' ' + batchArgs;
1950 return QCoreApplication::translate("ProjectExplorer::Internal::MsvcToolChain",
1951 "Failed to retrieve MSVC Environment from \"%1\":\n"
1952 "%2")
1953 .arg(command, message);
1954 }
1955
1956 // The SDK/MSVC scripts do not return exit codes != 0. Check on stdout.
1957 const QString stdOut = response.stdOut();
1958
1959 //
1960 // Now parse the file to get the environment settings
1961 const int start = stdOut.indexOf(marker);
1962 if (start == -1) {
1963 qWarning("Could not find start marker in stdout output.");
1964 return QString();
1965 }
1966
1967 const int end = stdOut.indexOf(marker, start + 1);
1968 if (end == -1) {
1969 qWarning("Could not find end marker in stdout output.");
1970 return QString();
1971 }
1972
1973 const QString output = stdOut.mid(start, end - start);
1974
1975 foreach (const QString &line, output.split(QLatin1String("\n"))) {
1976 const int pos = line.indexOf('=');
1977 if (pos > 0) {
1978 const QString varName = line.mid(0, pos);
1979 const QString varValue = line.mid(pos + 1);
1980 envPairs.insert(varName, varValue);
1981 }
1982 }
1983
1984 return Utils::nullopt;
1985}
1986
1987bool MsvcToolChainFactory::canRestore(const QVariantMap &data)
1988{
1989 const Core::Id id = typeIdFromMap(data);
1990 return id == Constants::MSVC_TOOLCHAIN_TYPEID;
1991}
1992
1993template<class ToolChainType>
1994ToolChainType *readFromMap(const QVariantMap &data)
1995{
1996 auto result = new ToolChainType;
1997 if (result->fromMap(data))
1998 return result;
1999 delete result;
2000 return nullptr;
2001}
2002
2003ToolChain *MsvcToolChainFactory::restore(const QVariantMap &data)
2004{
2005 return readFromMap<MsvcToolChain>(data);
2006}
2007
2008bool ClangClToolChainFactory::canRestore(const QVariantMap &data)
2009{
2010 const Core::Id id = typeIdFromMap(data);
2011 return id == Constants::CLANG_CL_TOOLCHAIN_TYPEID;
2012}
2013
2014ToolChain *ClangClToolChainFactory::restore(const QVariantMap &data)
2015{
2016 return readFromMap<ClangClToolChain>(data);
2017}
2018
2019MsvcToolChain::WarningFlagAdder::WarningFlagAdder(const QString &flag, WarningFlags &flags)
2020 : m_flags(flags)
2021{
2022 if (flag.startsWith(QLatin1String("-wd"))) {
2023 m_doesEnable = false;
2024 } else if (flag.startsWith(QLatin1String("-w"))) {
2025 m_doesEnable = true;
2026 } else {
2027 m_triggered = true;
2028 return;
2029 }
2030 bool ok = false;
2031 if (m_doesEnable)
2032 m_warningCode = flag.midRef(2).toInt(&ok);
2033 else
2034 m_warningCode = flag.midRef(3).toInt(&ok);
2035 if (!ok)
2036 m_triggered = true;
2037}
2038
2039void MsvcToolChain::WarningFlagAdder::operator()(int warningCode, WarningFlags flagsSet)
2040{
2041 if (m_triggered)
2042 return;
2043 if (warningCode == m_warningCode) {
2044 m_triggered = true;
2045 if (m_doesEnable)
2046 m_flags |= flagsSet;
2047 else
2048 m_flags &= ~flagsSet;
2049 }
2050}
2051
2052bool MsvcToolChain::WarningFlagAdder::triggered() const
2053{
2054 return m_triggered;
2055}
2056
2057} // namespace Internal
2058} // namespace ProjectExplorer
2059