OSDN Git Service

Update license.
[qt-creator-jp/qt-creator-jp.git] / src / plugins / projectexplorer / msvctoolchain.cpp
1 /**************************************************************************
2 **
3 ** This file is part of Qt Creator
4 **
5 ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
6 **
7 ** Contact: Nokia Corporation (info@qt.nokia.com)
8 **
9 **
10 ** GNU Lesser General Public License Usage
11 **
12 ** This file may be used under the terms of the GNU Lesser General Public
13 ** License version 2.1 as published by the Free Software Foundation and
14 ** appearing in the file LICENSE.LGPL included in the packaging of this file.
15 ** Please review the following information to ensure the GNU Lesser General
16 ** Public License version 2.1 requirements will be met:
17 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
18 **
19 ** In addition, as a special exception, Nokia gives you certain additional
20 ** rights. These rights are described in the Nokia Qt LGPL Exception
21 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
22 **
23 ** Other Usage
24 **
25 ** Alternatively, this file may be used in accordance with the terms and
26 ** conditions contained in a signed written agreement between you and Nokia.
27 **
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
30 **
31 **************************************************************************/
32
33 #include "msvctoolchain.h"
34
35 #include "msvcparser.h"
36 #include "projectexplorerconstants.h"
37 #include "headerpath.h"
38
39 #include <projectexplorer/projectexplorer.h>
40 #include <projectexplorer/projectexplorersettings.h>
41
42 #include <utils/qtcprocess.h>
43 #include <utils/qtcassert.h>
44 #include <utils/synchronousprocess.h>
45 #include <utils/winutils.h>
46
47 #include <QtCore/QCoreApplication>
48 #include <QtCore/QDir>
49 #include <QtCore/QFileInfo>
50 #include <QtCore/QSettings>
51 #include <QtCore/QUrl>
52 #include <QtCore/QTemporaryFile>
53 #include <QtGui/QLabel>
54 #include <QtGui/QFormLayout>
55 #include <QtGui/QDesktopServices>
56
57 static const char debuggerCommandKeyC[] = "ProjectExplorer.MsvcToolChain.Debugger";
58
59 enum { debug = 0 };
60
61 namespace ProjectExplorer {
62 namespace Internal {
63
64 // --------------------------------------------------------------------------
65 // Helpers:
66 // --------------------------------------------------------------------------
67
68 static QString platformName(MsvcToolChain::Platform t)
69 {
70     switch (t) {
71     case MsvcToolChain::s32:
72         return QLatin1String(" (x86)");
73     case MsvcToolChain::s64:
74         return QLatin1String(" (x64)");
75     case MsvcToolChain::ia64:
76         return QLatin1String(" (ia64)");
77     case MsvcToolChain::amd64:
78         return QLatin1String(" (amd64)");
79     }
80     return QString();
81 }
82
83 static Abi findAbiOfMsvc(MsvcToolChain::Type type, MsvcToolChain::Platform platform, const QString &version)
84 {
85     Abi::Architecture arch = Abi::X86Architecture;
86     Abi::OSFlavor flavor = Abi::UnknownFlavor;
87     int wordWidth = 64;
88
89     switch (platform)
90     {
91     case ProjectExplorer::Internal::MsvcToolChain::s32:
92         wordWidth = 32;
93         break;
94     case ProjectExplorer::Internal::MsvcToolChain::ia64:
95         arch = Abi::ItaniumArchitecture;
96         break;
97     case ProjectExplorer::Internal::MsvcToolChain::s64:
98     case ProjectExplorer::Internal::MsvcToolChain::amd64:
99         break;
100     };
101
102     QString msvcVersionString = version;
103     if (type == MsvcToolChain::WindowsSDK) {
104         if (version.startsWith(QLatin1String("7.")))
105             msvcVersionString = QLatin1String("10.0");
106         else if (version.startsWith(QLatin1String("6.1"))
107                  || (version.startsWith(QLatin1String("6.0")) && version != QLatin1String("6.0")))
108             // The 6.0 SDK is shipping MSVC2005, Starting at 6.0a it is MSVC2008.
109             msvcVersionString = QLatin1String("9.0");
110         else
111             msvcVersionString = QLatin1String("8.0");
112     }
113     if (msvcVersionString.startsWith(QLatin1String("10.")))
114         flavor = Abi::WindowsMsvc2010Flavor;
115     else if (msvcVersionString.startsWith(QLatin1String("9.")))
116         flavor = Abi::WindowsMsvc2008Flavor;
117     else
118         flavor = Abi::WindowsMsvc2005Flavor;
119
120     return Abi(arch, Abi::WindowsOS, flavor, Abi::PEFormat, wordWidth);
121 }
122
123 static QString generateDisplayName(const QString &name,
124                                    MsvcToolChain::Type t,
125                                    MsvcToolChain::Platform p)
126 {
127     if (t == MsvcToolChain::WindowsSDK) {
128         QString sdkName = name;
129         sdkName += platformName(p);
130         return sdkName;
131     }
132     // Comes as "9.0" from the registry
133     QString vcName = QLatin1String("Microsoft Visual C++ Compiler ");
134     vcName += name;
135     vcName += platformName(p);
136     return vcName;
137 }
138
139 static QByteArray msvcCompilationFile()
140 {
141     static const char* macros[] = {"_ATL_VER", "_CHAR_UNSIGNED", "__CLR_VER",
142                                    "__cplusplus_cli", "__COUNTER__", "__cplusplus",
143                                    "_CPPLIB_VER", "_CPPRTTI", "_CPPUNWIND",
144                                    "_DEBUG", "_DLL", "__FUNCDNAME__",
145                                    "__FUNCSIG__","__FUNCTION__","_INTEGRAL_MAX_BITS",
146                                    "_M_ALPHA","_M_CEE","_M_CEE_PURE",
147                                    "_M_CEE_SAFE","_M_IX86","_M_IA64",
148                                    "_M_IX86_FP","_M_MPPC","_M_MRX000",
149                                    "_M_PPC","_M_X64","_MANAGED",
150                                    "_MFC_VER","_MSC_BUILD", /* "_MSC_EXTENSIONS", */
151                                    "_MSC_FULL_VER","_MSC_VER","__MSVC_RUNTIME_CHECKS",
152                                    "_MT", "_NATIVE_WCHAR_T_DEFINED", "_OPENMP",
153                                    "_VC_NODEFAULTLIB", "_WCHAR_T_DEFINED", "_WIN32",
154                                    "_WIN32_WCE", "_WIN64", "_Wp64", "__DATE__",
155                                    "__DATE__", "__TIME__", "__TIMESTAMP__",
156                                    0};
157     QByteArray file = "#define __PPOUT__(x) V##x=x\n\n";
158     for (int i = 0; macros[i] != 0; ++i) {
159         const QByteArray macro(macros[i]);
160         file += "#if defined(" + macro + ")\n__PPOUT__("
161                 + macro + ")\n#endif\n";
162     }
163     file += "\nvoid main(){}\n\n";
164     return file;
165 }
166
167 // Run MSVC 'cl' compiler to obtain #defines.
168 static QByteArray msvcPredefinedMacros(const Utils::Environment &env)
169 {
170     QByteArray predefinedMacros = "#define __MSVCRT__\n"
171                       "#define __w64\n"
172                       "#define __int64 long long\n"
173                       "#define __int32 long\n"
174                       "#define __int16 short\n"
175                       "#define __int8 char\n"
176                       "#define __ptr32\n"
177                       "#define __ptr64\n";
178
179     QString tmpFilePath;
180     {
181         // QTemporaryFile is buggy and will not unlock the file for cl.exe
182         QTemporaryFile tmpFile(QDir::tempPath()+"/envtestXXXXXX.cpp");
183         tmpFile.setAutoRemove(false);
184         if (!tmpFile.open())
185             return predefinedMacros;
186         tmpFilePath = QFileInfo(tmpFile).canonicalFilePath();
187         tmpFile.write(msvcCompilationFile());
188         tmpFile.close();
189     }
190     QProcess cpp;
191     cpp.setEnvironment(env.toStringList());
192     cpp.setWorkingDirectory(QDir::tempPath());
193     QStringList arguments;
194     const QString binary = env.searchInPath(QLatin1String("cl.exe"));
195     if (binary.isEmpty()) {
196         qWarning("%s: The compiler binary cl.exe could not be found in the path.", Q_FUNC_INFO);
197         return predefinedMacros;
198     }
199
200     arguments << QLatin1String("/EP") << QDir::toNativeSeparators(tmpFilePath);
201     cpp.start(binary, arguments);
202     if (!cpp.waitForStarted()) {
203         qWarning("%s: Cannot start '%s': %s", Q_FUNC_INFO, qPrintable(binary),
204             qPrintable(cpp.errorString()));
205         return predefinedMacros;
206     }
207     cpp.closeWriteChannel();
208     if (!cpp.waitForFinished()) {
209         Utils::SynchronousProcess::stopProcess(cpp);
210         qWarning("%s: Timeout running '%s'.", Q_FUNC_INFO, qPrintable(binary));
211         return predefinedMacros;
212     }
213     if (cpp.exitStatus() != QProcess::NormalExit) {
214         qWarning("%s: '%s' crashed.", Q_FUNC_INFO, qPrintable(binary));
215         return predefinedMacros;
216     }
217
218     const QList<QByteArray> output = cpp.readAllStandardOutput().split('\n');
219     foreach (const QByteArray& line, output) {
220         if (line.startsWith('V')) {
221             QList<QByteArray> split = line.split('=');
222             const QByteArray key = split.at(0).mid(1);
223             QByteArray value = split.at(1);
224             if (!value.isEmpty()) {
225                 value.chop(1); //remove '\n'
226             }
227             predefinedMacros += "#define ";
228             predefinedMacros += key;
229             predefinedMacros += ' ';
230             predefinedMacros += value;
231             predefinedMacros += '\n';
232         }
233     }
234     QFile::remove(tmpFilePath);
235     if (debug)
236         qDebug() << "msvcPredefinedMacros" << predefinedMacros;
237     return predefinedMacros;
238 }
239
240 // Windows: Expand the delayed evaluation references returned by the
241 // SDK setup scripts: "PATH=!Path!;foo". Some values might expand
242 // to empty and should not be added
243 static QString winExpandDelayedEnvReferences(QString in, const Utils::Environment &env)
244 {
245     const QChar exclamationMark = QLatin1Char('!');
246     for (int pos = 0; pos < in.size(); ) {
247         // Replace "!REF!" by its value in process environment
248         pos = in.indexOf(exclamationMark, pos);
249         if (pos == -1)
250             break;
251         const int nextPos = in.indexOf(exclamationMark, pos + 1);
252         if (nextPos == -1)
253             break;
254         const QString var = in.mid(pos + 1, nextPos - pos - 1);
255         const QString replacement = env.value(var.toUpper());
256         in.replace(pos, nextPos + 1 - pos, replacement);
257         pos += replacement.size();
258     }
259     return in;
260 }
261
262 static Utils::Environment msvcReadEnvironmentSetting(const QString &varsBat,
263                                                      const QString &args,
264                                                      const Utils::Environment &env)
265 {
266     // Run the setup script and extract the variables
267     Utils::Environment result = env;
268     if (!QFileInfo(varsBat).exists())
269         return result;
270
271     const QString tempOutputFileName = QDir::tempPath() + QLatin1String("\\qtcreator-msvc-environment.txt");
272     QTemporaryFile tf(QDir::tempPath() + "\\XXXXXX.bat");
273     tf.setAutoRemove(true);
274     if (!tf.open())
275         return result;
276
277     const QString filename = tf.fileName();
278
279     QByteArray call = "call ";
280     call += Utils::QtcProcess::quoteArg(varsBat).toLocal8Bit();
281     if (!args.isEmpty()) {
282         call += ' ';
283         call += args.toLocal8Bit();
284     }
285     call += "\r\n";
286     tf.write(call);
287     const QByteArray redirect = "set > " + Utils::QtcProcess::quoteArg(
288                 QDir::toNativeSeparators(tempOutputFileName)).toLocal8Bit() + "\r\n";
289     tf.write(redirect);
290     tf.flush();
291     tf.waitForBytesWritten(30000);
292
293     Utils::QtcProcess run;
294     run.setEnvironment(env);
295     const QString cmdPath = QString::fromLocal8Bit(qgetenv("COMSPEC"));
296     // Windows SDK setup scripts require command line switches for environment expansion.
297     QString cmdArguments = QLatin1String(" /E:ON /V:ON /c \"");
298     cmdArguments += QDir::toNativeSeparators(filename);
299     cmdArguments += QLatin1Char('"');
300     run.setCommand(cmdPath, cmdArguments);
301     if (debug)
302         qDebug() << "msvcReadEnvironmentSetting: " << call << cmdPath << cmdArguments
303                  << " Env: " << env.size();
304     run.start();
305
306     if (!run.waitForStarted()) {
307         qWarning("%s: Unable to run '%s': %s", Q_FUNC_INFO, qPrintable(varsBat),
308             qPrintable(run.errorString()));
309         return result;
310     }
311     if (!run.waitForFinished()) {
312         qWarning("%s: Timeout running '%s'", Q_FUNC_INFO, qPrintable(varsBat));
313         Utils::SynchronousProcess::stopProcess(run);
314         return result;
315     }
316     tf.close();
317
318     QFile varsFile(tempOutputFileName);
319     if (!varsFile.open(QIODevice::ReadOnly|QIODevice::Text))
320         return result;
321
322     QRegExp regexp(QLatin1String("(\\w*)=(.*)"));
323     while (!varsFile.atEnd()) {
324         const QString line = QString::fromLocal8Bit(varsFile.readLine()).trimmed();
325         if (regexp.exactMatch(line)) {
326             const QString varName = regexp.cap(1);
327             const QString expandedValue = winExpandDelayedEnvReferences(regexp.cap(2), env);
328             if (!expandedValue.isEmpty())
329                 result.set(varName, expandedValue);
330         }
331     }
332     varsFile.close();
333     varsFile.remove();
334     if (debug) {
335         const QStringList newVars = result.toStringList();
336         const QStringList oldVars = env.toStringList();
337         QDebug nsp = qDebug().nospace();
338         foreach (const QString &n, newVars) {
339             if (!oldVars.contains(n))
340                 nsp << n << '\n';
341         }
342     }
343     return result;
344 }
345
346 // --------------------------------------------------------------------------
347 // MsvcToolChain
348 // --------------------------------------------------------------------------
349
350 MsvcToolChain::MsvcToolChain(const QString &name, const Abi &abi,
351                              const QString &varsBat, const QString &varsBatArg, bool autodetect) :
352     ToolChain(QLatin1String(Constants::MSVC_TOOLCHAIN_ID), autodetect),
353     m_varsBat(varsBat),
354     m_varsBatArg(varsBatArg),
355     m_lastEnvironment(Utils::Environment::systemEnvironment()),
356     m_abi(abi)
357 {
358     Q_ASSERT(!name.isEmpty());
359     Q_ASSERT(!m_varsBat.isEmpty());
360     Q_ASSERT(QFileInfo(m_varsBat).exists());
361     Q_ASSERT(abi.os() == Abi::WindowsOS);
362     Q_ASSERT(abi.binaryFormat() == Abi::PEFormat);
363     Q_ASSERT(abi.osFlavor() != Abi::WindowsMSysFlavor);
364
365     setId(QString::fromLatin1("%1:%2.%3.%4").arg(Constants::MSVC_TOOLCHAIN_ID).arg(m_varsBat)
366             .arg(m_varsBatArg).arg(m_debuggerCommand));
367
368     setDisplayName(name);
369 }
370
371 QString MsvcToolChain::typeName() const
372 {
373     return MsvcToolChainFactory::tr("MSVC");
374 }
375
376 Abi MsvcToolChain::targetAbi() const
377 {
378     return m_abi;
379 }
380
381 bool MsvcToolChain::isValid() const
382 {
383     return !m_varsBat.isEmpty();
384 }
385
386 QByteArray MsvcToolChain::predefinedMacros() const
387 {
388     if (m_predefinedMacros.isEmpty()) {
389         Utils::Environment env(m_lastEnvironment);
390         addToEnvironment(env);
391         m_predefinedMacros = msvcPredefinedMacros(env);
392     }
393     return m_predefinedMacros;
394 }
395
396 QList<HeaderPath> MsvcToolChain::systemHeaderPaths() const
397 {
398     if (m_headerPaths.isEmpty()) {
399         Utils::Environment env(m_lastEnvironment);
400         addToEnvironment(env);
401         foreach (const QString &path, env.value("INCLUDE").split(QLatin1Char(';')))
402             m_headerPaths.append(HeaderPath(path, HeaderPath::GlobalHeaderPath));
403     }
404     return m_headerPaths;
405 }
406
407 void MsvcToolChain::addToEnvironment(Utils::Environment &env) const
408 {
409     // We cache the full environment (incoming + modifications by setup script).
410     if (!m_resultEnvironment.size() || env != m_lastEnvironment) {
411         if (debug)
412             qDebug() << "addToEnvironment: " << displayName();
413         m_lastEnvironment = env;
414         m_resultEnvironment = msvcReadEnvironmentSetting(m_varsBat, m_varsBatArg, env);
415     }
416     env = m_resultEnvironment;
417 }
418
419 QString MsvcToolChain::makeCommand() const
420 {
421     if (ProjectExplorerPlugin::instance()->projectExplorerSettings().useJom) {
422         // We want jom! Try to find it.
423         const QString jom = QLatin1String("jom.exe");
424         const QFileInfo installedJom = QFileInfo(QCoreApplication::applicationDirPath()
425                                                  + QLatin1Char('/') + jom);
426         if (installedJom.isFile() && installedJom.isExecutable()) {
427             return installedJom.absoluteFilePath();
428         } else {
429             return jom;
430         }
431     }
432     return QLatin1String("nmake.exe");
433 }
434
435 void MsvcToolChain::setDebuggerCommand(const QString &d)
436 {
437     if (m_debuggerCommand == d)
438         return;
439     m_debuggerCommand = d;
440     toolChainUpdated();
441 }
442
443 QString MsvcToolChain::debuggerCommand() const
444 {
445     return m_debuggerCommand;
446 }
447
448 QVariantMap MsvcToolChain::toMap() const
449 {
450     QVariantMap data = ToolChain::toMap();
451     data.insert(QLatin1String(debuggerCommandKeyC), m_debuggerCommand);
452     return data;
453 }
454
455 bool MsvcToolChain::fromMap(const QVariantMap &data)
456 {
457     if (!ToolChain::fromMap(data))
458         return false;
459
460     m_debuggerCommand= data.value(QLatin1String(debuggerCommandKeyC)).toString();
461     return true;
462 }
463
464 IOutputParser *MsvcToolChain::outputParser() const
465 {
466     return new MsvcParser;
467 }
468
469 ToolChainConfigWidget *MsvcToolChain::configurationWidget()
470 {
471     return new MsvcToolChainConfigWidget(this);
472 }
473
474 bool MsvcToolChain::canClone() const
475 {
476     return true;
477 }
478
479 ToolChain *MsvcToolChain::clone() const
480 {
481     return new MsvcToolChain(*this);
482 }
483
484 // --------------------------------------------------------------------------
485 // MsvcDebuggerConfigLabel
486 // --------------------------------------------------------------------------
487
488 static const char dgbToolsDownloadLink32C[] = "http://www.microsoft.com/whdc/devtools/debugging/installx86.Mspx";
489 static const char dgbToolsDownloadLink64C[] = "http://www.microsoft.com/whdc/devtools/debugging/install64bit.Mspx";
490
491 QString MsvcDebuggerConfigLabel::labelText()
492 {
493 #ifdef Q_OS_WIN
494     const bool is64bit = Utils::winIs64BitSystem();
495 #else
496     const bool is64bit = false;
497 #endif
498     const QString link = is64bit ? QLatin1String(dgbToolsDownloadLink64C) : QLatin1String(dgbToolsDownloadLink32C);
499     //: Label text for path configuration. %2 is "x-bit version".
500     return tr(
501     "<html><body><p>Specify the path to the "
502     "<a href=\"%1\">Windows Console Debugger executable</a>"
503     " (%2) here.</p>"
504     "</body></html>").arg(link, (is64bit ? tr("64-bit version")
505                                          : tr("32-bit version")));
506 }
507
508 MsvcDebuggerConfigLabel::MsvcDebuggerConfigLabel(QWidget *parent) :
509         QLabel(labelText(), parent)
510 {
511     connect(this, SIGNAL(linkActivated(QString)), this, SLOT(slotLinkActivated(QString)));
512     setTextInteractionFlags(Qt::TextBrowserInteraction);
513 }
514
515 void MsvcDebuggerConfigLabel::slotLinkActivated(const QString &link)
516 {
517     QDesktopServices::openUrl(QUrl(link));
518 }
519
520 // --------------------------------------------------------------------------
521 // MsvcToolChainConfigWidget
522 // --------------------------------------------------------------------------
523
524 MsvcToolChainConfigWidget::MsvcToolChainConfigWidget(ToolChain *tc) :
525     ToolChainConfigWidget(tc)
526 {
527     QFormLayout *formLayout = new QFormLayout(this);
528     formLayout->addRow(new QLabel(tc->displayName()));
529     formLayout->addRow(new MsvcDebuggerConfigLabel);
530     addDebuggerCommandControls(formLayout, QStringList(QLatin1String("-version")));
531     addDebuggerAutoDetection(this, SLOT(autoDetectDebugger()));
532     addErrorLabel(formLayout);
533     setFromToolChain();
534 }
535
536 void MsvcToolChainConfigWidget::apply()
537 {
538     MsvcToolChain *tc = static_cast<MsvcToolChain *>(toolChain());
539     QTC_ASSERT(tc, return; );
540     tc->setDebuggerCommand(debuggerCommand());
541 }
542
543 void MsvcToolChainConfigWidget::setFromToolChain()
544 {
545     MsvcToolChain *tc = static_cast<MsvcToolChain *>(toolChain());
546     QTC_ASSERT(tc, return);
547     setDebuggerCommand(tc->debuggerCommand());
548 }
549
550 bool MsvcToolChainConfigWidget::isDirty() const
551 {
552     MsvcToolChain *tc = static_cast<MsvcToolChain *>(toolChain());
553     QTC_ASSERT(tc, return false);
554     return debuggerCommand() != tc->debuggerCommand();
555 }
556
557 void MsvcToolChainConfigWidget::autoDetectDebugger()
558 {
559     QStringList directories;
560     const QString cdbExecutable = MsvcToolChain::autoDetectCdbDebugger(&directories);
561     if (cdbExecutable.isEmpty()) {
562         const QString msg = tr("The CDB debugger could not be found in %1").arg(directories.join(QLatin1String(", ")));
563         setErrorMessage(msg);
564     } else {
565         clearErrorMessage();
566         if (cdbExecutable != debuggerCommand()) {
567             setDebuggerCommand(cdbExecutable);
568             emitDirty();
569         }
570     }
571 }
572
573 // --------------------------------------------------------------------------
574 // MsvcToolChainFactory
575 // --------------------------------------------------------------------------
576
577 QString MsvcToolChainFactory::displayName() const
578 {
579     return tr("MSVC");
580 }
581
582 QString MsvcToolChainFactory::id() const
583 {
584     return QLatin1String(Constants::MSVC_TOOLCHAIN_ID);
585 }
586
587 QList<ToolChain *> MsvcToolChainFactory::autoDetect()
588 {
589     QList<ToolChain *> results;
590
591 #ifdef Q_OS_WIN
592     // 1) Installed SDKs preferred over standalone Visual studio
593     const QSettings sdkRegistry("HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Microsoft SDKs\\Windows",
594                                 QSettings::NativeFormat);
595     const QString defaultSdkPath = sdkRegistry.value(QLatin1String("CurrentInstallFolder")).toString();
596     if (!defaultSdkPath.isEmpty()) {
597         foreach (const QString &sdkKey, sdkRegistry.childGroups()) {
598             const QString name = sdkRegistry.value(sdkKey + QLatin1String("/ProductName")).toString();
599             const QString version = sdkRegistry.value(sdkKey + QLatin1String("/ProductVersion")).toString();
600             const QString folder = sdkRegistry.value(sdkKey + QLatin1String("/InstallationFolder")).toString();
601             if (folder.isEmpty())
602                 continue;
603
604             const QString sdkVcVarsBat = folder + QLatin1String("bin\\SetEnv.cmd");
605             if (!QFileInfo(sdkVcVarsBat).exists())
606                 continue;
607             QList<ToolChain *> tmp;
608
609             tmp.append(new MsvcToolChain(generateDisplayName(name, MsvcToolChain::WindowsSDK, MsvcToolChain::s32),
610                                          findAbiOfMsvc(MsvcToolChain::WindowsSDK, MsvcToolChain::s32, version),
611                                          sdkVcVarsBat, QLatin1String("/x86"), true));
612 #ifdef Q_OS_WIN64
613             // Add all platforms
614             tmp.append(new MsvcToolChain(generateDisplayName(name, MsvcToolChain::WindowsSDK, MsvcToolChain::s64),
615                                          findAbiOfMsvc(MsvcToolChain::WindowsSDK, MsvcToolChain::s64, version),
616                                          sdkVcVarsBat, QLatin1String("/x64"), true));
617             tmp.append(new MsvcToolChain(generateDisplayName(name, MsvcToolChain::WindowsSDK, MsvcToolChain::ia64),
618                                          findAbiOfMsvc(MsvcToolChain::WindowsSDK, MsvcToolChain::ia64, version),
619                                          sdkVcVarsBat, QLatin1String("/ia64"), true));
620 #endif
621             // Make sure the default is front.
622             if (folder == defaultSdkPath)
623                 results = tmp + results;
624             else
625                 results += tmp;
626         } // foreach
627     }
628
629     // 2) Installed MSVCs
630     const QSettings vsRegistry(
631 #ifdef Q_OS_WIN64
632                 QLatin1String("HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\Microsoft\\VisualStudio\\SxS\\VC7"),
633 #else
634                 QLatin1String("HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\SxS\\VC7"),
635 #endif
636                 QSettings::NativeFormat);
637     foreach (const QString &vsName, vsRegistry.allKeys()) {
638         // Scan for version major.minor
639         const int dotPos = vsName.indexOf(QLatin1Char('.'));
640         if (dotPos == -1)
641             continue;
642
643         const QString path = vsRegistry.value(vsName).toString();
644         const int version = vsName.left(dotPos).toInt();
645         // Check existence of various install scripts
646         const QString vcvars32bat = path + QLatin1String("bin\\vcvars32.bat");
647         if (QFileInfo(vcvars32bat).isFile())
648             results.append(new MsvcToolChain(generateDisplayName(vsName, MsvcToolChain::VS, MsvcToolChain::s32),
649                                              findAbiOfMsvc(MsvcToolChain::VS, MsvcToolChain::s32, vsName),
650                                              vcvars32bat, QString(), true));
651         if (version >= 10) {
652             // Just one common file
653             const QString vcvarsAllbat = path + QLatin1String("vcvarsall.bat");
654             if (QFileInfo(vcvarsAllbat).isFile()) {
655                 results.append(new MsvcToolChain(generateDisplayName(vsName, MsvcToolChain::VS, MsvcToolChain::s32),
656                                                  findAbiOfMsvc(MsvcToolChain::VS, MsvcToolChain::s32, vsName),
657                                                  vcvarsAllbat, QLatin1String("x86"), true));
658                 results.append(new MsvcToolChain(generateDisplayName(vsName, MsvcToolChain::VS, MsvcToolChain::amd64),
659                                                  findAbiOfMsvc(MsvcToolChain::VS, MsvcToolChain::amd64, vsName),
660                                                  vcvarsAllbat, QLatin1String("amd64"), true));
661                 results.append(new MsvcToolChain(generateDisplayName(vsName, MsvcToolChain::VS, MsvcToolChain::s64),
662                                                  findAbiOfMsvc(MsvcToolChain::VS, MsvcToolChain::s64, vsName),
663                                                  vcvarsAllbat, QLatin1String("x64"), true));
664                 results.append(new MsvcToolChain(generateDisplayName(vsName, MsvcToolChain::VS, MsvcToolChain::ia64),
665                                                  findAbiOfMsvc(MsvcToolChain::VS, MsvcToolChain::ia64, vsName),
666                                                  vcvarsAllbat, QLatin1String("ia64"), true));
667             } else {
668                 qWarning("Unable to find MSVC setup script %s in version %d", qPrintable(vcvarsAllbat), version);
669             }
670         } else {
671             // Amd 64 is the preferred 64bit platform
672             const QString vcvarsAmd64bat = path + QLatin1String("bin\\amd64\\vcvarsamd64.bat");
673             if (QFileInfo(vcvarsAmd64bat).isFile())
674                 results.append(new MsvcToolChain(generateDisplayName(vsName, MsvcToolChain::VS, MsvcToolChain::amd64),
675                                                  findAbiOfMsvc(MsvcToolChain::VS, MsvcToolChain::amd64, vsName),
676                                                  vcvarsAmd64bat, QString(), true));
677             const QString vcvarsAmd64bat2 = path + QLatin1String("bin\\vcvarsx86_amd64.bat");
678             if (QFileInfo(vcvarsAmd64bat2).isFile())
679                 results.append(new MsvcToolChain(generateDisplayName(vsName, MsvcToolChain::VS, MsvcToolChain::amd64),
680                                                  findAbiOfMsvc(MsvcToolChain::VS, MsvcToolChain::amd64, vsName),
681                                                  vcvarsAmd64bat2, QString(), true));
682             const QString vcvars64bat = path + QLatin1String("bin\\vcvars64.bat");
683             if (QFileInfo(vcvars64bat).isFile())
684                 results.append(new MsvcToolChain(generateDisplayName(vsName, MsvcToolChain::VS, MsvcToolChain::s64),
685                                                  findAbiOfMsvc(MsvcToolChain::VS, MsvcToolChain::s64, vsName),
686                                                  vcvars64bat, QString(), true));
687             const QString vcvarsIA64bat = path + QLatin1String("bin\\vcvarsx86_ia64.bat");
688             if (QFileInfo(vcvarsIA64bat).isFile())
689                 results.append(new MsvcToolChain(generateDisplayName(vsName, MsvcToolChain::VS, MsvcToolChain::ia64),
690                                                  findAbiOfMsvc(MsvcToolChain::VS, MsvcToolChain::ia64, vsName),
691                                                  vcvarsIA64bat, QString(), true));
692         }
693     }
694 #endif
695     if (!results.isEmpty()) { // Detect debugger
696         const QString cdbDebugger = MsvcToolChain::autoDetectCdbDebugger();
697         if (!cdbDebugger.isEmpty()) {
698             foreach (ToolChain *tc, results)
699                 static_cast<MsvcToolChain *>(tc)->setDebuggerCommand(cdbDebugger);
700         }
701     }
702     return results;
703 }
704
705 // Check the CDB executable and accumulate the list of checked paths
706 // for reporting.
707 static QString checkCdbExecutable(const QString &programDir, const QString &postfix,
708                                   QStringList *checkedDirectories = 0)
709 {
710     QString executable = programDir;
711     executable += QLatin1String("/Debugging Tools For Windows");
712     executable += postfix;
713     if (checkedDirectories)
714         checkedDirectories->push_back(QDir::toNativeSeparators(executable));
715     executable += QLatin1String("/cdb.exe");
716     const QFileInfo fi(executable);
717     return fi.isFile() && fi.isExecutable() ? fi.absoluteFilePath() : QString();
718 }
719
720 QString MsvcToolChain::autoDetectCdbDebugger(QStringList *checkedDirectories /* = 0 */)
721 {
722     // Look for $ProgramFiles/"Debugging Tools For Windows <bit-idy>/cdb.exe" and its
723     // " (x86)", " (x64)" variations.
724     static const char *postFixes[] = {"", " (x64)", " 64-bit", " (x86)", " (x32)" };
725
726     if (checkedDirectories)
727         checkedDirectories->clear();
728
729     const QString programDir = QString::fromLocal8Bit(qgetenv("ProgramFiles"));
730     if (programDir.isEmpty())
731         return QString();
732
733     // Try the post fixes
734     QString outPath;
735     for (unsigned i = 0; i < sizeof(postFixes)/sizeof(const char*); i++) {
736         outPath = checkCdbExecutable(programDir, QLatin1String(postFixes[i]), checkedDirectories);
737         if (!outPath.isEmpty())
738             return outPath;
739     }
740     // A 32bit-compile running on a 64bit system sees the 64 bit installation
741     // as "$ProgramFiles (x64)/Debugging Tools..." and (untested), a 64 bit-
742     // compile running on a 64bit system sees the 32 bit installation as
743     // "$ProgramFiles (x86)/Debugging Tools..." (assuming this works at all)
744 #ifdef Q_OS_WIN64
745     outPath = checkCdbExecutable(programDir + QLatin1String(" (x32)"), QString(), checkedDirectories);
746     if (!outPath.isEmpty())
747         return QString();
748 #else
749     // A 32bit process on 64 bit sees "ProgramFiles\Debg.. (x64)".
750     if (programDir.endsWith(QLatin1String(" (x86)"))) {
751         const QString programDir64 = programDir.left(programDir.size() - 6);
752         for (unsigned i = 0; i < sizeof(postFixes)/sizeof(const char*); i++) {
753             outPath = checkCdbExecutable(programDir64, QLatin1String(postFixes[i]), checkedDirectories);
754             if (!outPath.isEmpty())
755                 return outPath;
756         }
757     }
758 #endif
759     return QString();
760 }
761
762 } // namespace Internal
763 } // namespace ProjectExplorer