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