msvctoolchain.cpp 29.6 KB
Newer Older
1 2 3 4 5 6
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
**
hjk's avatar
hjk committed
7
** Contact: Nokia Corporation (info@qt.nokia.com)
8 9 10 11
**
**
** GNU Lesser General Public License Usage
**
hjk's avatar
hjk committed
12 13 14 15 16 17
** This file may be used under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation and
** appearing in the file LICENSE.LGPL included in the packaging of this file.
** Please review the following information to ensure the GNU Lesser General
** Public License version 2.1 requirements will be met:
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
18 19
**
** In addition, as a special exception, Nokia gives you certain additional
hjk's avatar
hjk committed
20
** rights. These rights are described in the Nokia Qt LGPL Exception
21 22
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
hjk's avatar
hjk committed
23 24 25 26 27
** Other Usage
**
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
28
** If you have questions regarding the use of this file, please contact
Tobias Hunger's avatar
Tobias Hunger committed
29
** Nokia at info@qt.nokia.com.
30 31 32 33
**
**************************************************************************/

#include "msvctoolchain.h"
34

35 36
#include "msvcparser.h"
#include "projectexplorerconstants.h"
37
#include "headerpath.h"
38 39 40 41

#include <projectexplorer/projectexplorer.h>
#include <projectexplorer/projectexplorersettings.h>

42
#include <utils/fileutils.h>
43
#include <utils/qtcprocess.h>
44
#include <utils/qtcassert.h>
45
#include <utils/synchronousprocess.h>
46
#include <utils/winutils.h>
47 48 49 50 51

#include <QtCore/QCoreApplication>
#include <QtCore/QDir>
#include <QtCore/QFileInfo>
#include <QtCore/QSettings>
52
#include <QtCore/QUrl>
53 54
#include <QtCore/QTemporaryFile>
#include <QtGui/QLabel>
55
#include <QtGui/QFormLayout>
56
#include <QtGui/QDesktopServices>
57 58

static const char debuggerCommandKeyC[] = "ProjectExplorer.MsvcToolChain.Debugger";
59

Friedemann Kleint's avatar
Friedemann Kleint committed
60 61
enum { debug = 0 };

62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83
namespace ProjectExplorer {
namespace Internal {

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

static QString platformName(MsvcToolChain::Platform t)
{
    switch (t) {
    case MsvcToolChain::s32:
        return QLatin1String(" (x86)");
    case MsvcToolChain::s64:
        return QLatin1String(" (x64)");
    case MsvcToolChain::ia64:
        return QLatin1String(" (ia64)");
    case MsvcToolChain::amd64:
        return QLatin1String(" (amd64)");
    }
    return QString();
}

84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104
static Abi findAbiOfMsvc(MsvcToolChain::Type type, MsvcToolChain::Platform platform, const QString &version)
{
    Abi::Architecture arch = Abi::X86Architecture;
    Abi::OSFlavor flavor = Abi::UnknownFlavor;
    int wordWidth = 64;

    switch (platform)
    {
    case ProjectExplorer::Internal::MsvcToolChain::s32:
        wordWidth = 32;
        break;
    case ProjectExplorer::Internal::MsvcToolChain::ia64:
        arch = Abi::ItaniumArchitecture;
        break;
    case ProjectExplorer::Internal::MsvcToolChain::s64:
    case ProjectExplorer::Internal::MsvcToolChain::amd64:
        break;
    };

    QString msvcVersionString = version;
    if (type == MsvcToolChain::WindowsSDK) {
105 106 107 108 109 110
        if (version.startsWith(QLatin1String("7.")))
            msvcVersionString = QLatin1String("10.0");
        else if (version.startsWith(QLatin1String("6.1"))
                 || (version.startsWith(QLatin1String("6.0")) && version != QLatin1String("6.0")))
            // The 6.0 SDK is shipping MSVC2005, Starting at 6.0a it is MSVC2008.
            msvcVersionString = QLatin1String("9.0");
111
        else
112
            msvcVersionString = QLatin1String("8.0");
113
    }
114
    if (msvcVersionString.startsWith(QLatin1String("10.")))
115
        flavor = Abi::WindowsMsvc2010Flavor;
116
    else if (msvcVersionString.startsWith(QLatin1String("9.")))
117 118 119 120 121 122 123
        flavor = Abi::WindowsMsvc2008Flavor;
    else
        flavor = Abi::WindowsMsvc2005Flavor;

    return Abi(arch, Abi::WindowsOS, flavor, Abi::PEFormat, wordWidth);
}

124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168
static QString generateDisplayName(const QString &name,
                                   MsvcToolChain::Type t,
                                   MsvcToolChain::Platform p)
{
    if (t == MsvcToolChain::WindowsSDK) {
        QString sdkName = name;
        sdkName += platformName(p);
        return sdkName;
    }
    // Comes as "9.0" from the registry
    QString vcName = QLatin1String("Microsoft Visual C++ Compiler ");
    vcName += name;
    vcName += platformName(p);
    return vcName;
}

static QByteArray msvcCompilationFile()
{
    static const char* macros[] = {"_ATL_VER", "_CHAR_UNSIGNED", "__CLR_VER",
                                   "__cplusplus_cli", "__COUNTER__", "__cplusplus",
                                   "_CPPLIB_VER", "_CPPRTTI", "_CPPUNWIND",
                                   "_DEBUG", "_DLL", "__FUNCDNAME__",
                                   "__FUNCSIG__","__FUNCTION__","_INTEGRAL_MAX_BITS",
                                   "_M_ALPHA","_M_CEE","_M_CEE_PURE",
                                   "_M_CEE_SAFE","_M_IX86","_M_IA64",
                                   "_M_IX86_FP","_M_MPPC","_M_MRX000",
                                   "_M_PPC","_M_X64","_MANAGED",
                                   "_MFC_VER","_MSC_BUILD", /* "_MSC_EXTENSIONS", */
                                   "_MSC_FULL_VER","_MSC_VER","__MSVC_RUNTIME_CHECKS",
                                   "_MT", "_NATIVE_WCHAR_T_DEFINED", "_OPENMP",
                                   "_VC_NODEFAULTLIB", "_WCHAR_T_DEFINED", "_WIN32",
                                   "_WIN32_WCE", "_WIN64", "_Wp64", "__DATE__",
                                   "__DATE__", "__TIME__", "__TIMESTAMP__",
                                   0};
    QByteArray file = "#define __PPOUT__(x) V##x=x\n\n";
    for (int i = 0; macros[i] != 0; ++i) {
        const QByteArray macro(macros[i]);
        file += "#if defined(" + macro + ")\n__PPOUT__("
                + macro + ")\n#endif\n";
    }
    file += "\nvoid main(){}\n\n";
    return file;
}

// Run MSVC 'cl' compiler to obtain #defines.
Friedemann Kleint's avatar
Friedemann Kleint committed
169
static QByteArray msvcPredefinedMacros(const Utils::Environment &env)
170 171 172 173 174 175 176 177 178 179
{
    QByteArray predefinedMacros = "#define __MSVCRT__\n"
                      "#define __w64\n"
                      "#define __int64 long long\n"
                      "#define __int32 long\n"
                      "#define __int16 short\n"
                      "#define __int8 char\n"
                      "#define __ptr32\n"
                      "#define __ptr64\n";

180 181 182 183 184
    Utils::TempFileSaver saver(QDir::tempPath()+"/envtestXXXXXX.cpp");
    saver.write(msvcCompilationFile());
    if (!saver.finalize()) {
        qWarning("%s: %s", Q_FUNC_INFO, qPrintable(saver.errorString()));
        return predefinedMacros;
185 186
    }
    QProcess cpp;
Friedemann Kleint's avatar
Friedemann Kleint committed
187
    cpp.setEnvironment(env.toStringList());
188 189
    cpp.setWorkingDirectory(QDir::tempPath());
    QStringList arguments;
Friedemann Kleint's avatar
Friedemann Kleint committed
190 191 192 193 194 195
    const QString binary = env.searchInPath(QLatin1String("cl.exe"));
    if (binary.isEmpty()) {
        qWarning("%s: The compiler binary cl.exe could not be found in the path.", Q_FUNC_INFO);
        return predefinedMacros;
    }

196
    arguments << QLatin1String("/EP") << QDir::toNativeSeparators(saver.fileName());
Friedemann Kleint's avatar
Friedemann Kleint committed
197
    cpp.start(binary, arguments);
198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229
    if (!cpp.waitForStarted()) {
        qWarning("%s: Cannot start '%s': %s", Q_FUNC_INFO, qPrintable(binary),
            qPrintable(cpp.errorString()));
        return predefinedMacros;
    }
    cpp.closeWriteChannel();
    if (!cpp.waitForFinished()) {
        Utils::SynchronousProcess::stopProcess(cpp);
        qWarning("%s: Timeout running '%s'.", Q_FUNC_INFO, qPrintable(binary));
        return predefinedMacros;
    }
    if (cpp.exitStatus() != QProcess::NormalExit) {
        qWarning("%s: '%s' crashed.", Q_FUNC_INFO, qPrintable(binary));
        return predefinedMacros;
    }

    const QList<QByteArray> output = cpp.readAllStandardOutput().split('\n');
    foreach (const QByteArray& line, output) {
        if (line.startsWith('V')) {
            QList<QByteArray> split = line.split('=');
            const QByteArray key = split.at(0).mid(1);
            QByteArray value = split.at(1);
            if (!value.isEmpty()) {
                value.chop(1); //remove '\n'
            }
            predefinedMacros += "#define ";
            predefinedMacros += key;
            predefinedMacros += ' ';
            predefinedMacros += value;
            predefinedMacros += '\n';
        }
    }
Friedemann Kleint's avatar
Friedemann Kleint committed
230 231
    if (debug)
        qDebug() << "msvcPredefinedMacros" << predefinedMacros;
232 233 234
    return predefinedMacros;
}

Friedemann Kleint's avatar
Friedemann Kleint committed
235 236 237
// Windows: Expand the delayed evaluation references returned by the
// SDK setup scripts: "PATH=!Path!;foo". Some values might expand
// to empty and should not be added
238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258
static QString winExpandDelayedEnvReferences(QString in, const Utils::Environment &env)
{
    const QChar exclamationMark = QLatin1Char('!');
    for (int pos = 0; pos < in.size(); ) {
        // Replace "!REF!" by its value in process environment
        pos = in.indexOf(exclamationMark, pos);
        if (pos == -1)
            break;
        const int nextPos = in.indexOf(exclamationMark, pos + 1);
        if (nextPos == -1)
            break;
        const QString var = in.mid(pos + 1, nextPos - pos - 1);
        const QString replacement = env.value(var.toUpper());
        in.replace(pos, nextPos + 1 - pos, replacement);
        pos += replacement.size();
    }
    return in;
}

static Utils::Environment msvcReadEnvironmentSetting(const QString &varsBat,
                                                     const QString &args,
Friedemann Kleint's avatar
Friedemann Kleint committed
259
                                                     const Utils::Environment &env)
260 261
{
    // Run the setup script and extract the variables
Friedemann Kleint's avatar
Friedemann Kleint committed
262
    Utils::Environment result = env;
263
    if (!QFileInfo(varsBat).exists())
Friedemann Kleint's avatar
Friedemann Kleint committed
264
        return result;
265 266

    const QString tempOutputFileName = QDir::tempPath() + QLatin1String("\\qtcreator-msvc-environment.txt");
267
    Utils::TempFileSaver saver(QDir::tempPath() + "\\XXXXXX.bat");
268 269 270 271 272 273 274
    QByteArray call = "call ";
    call += Utils::QtcProcess::quoteArg(varsBat).toLocal8Bit();
    if (!args.isEmpty()) {
        call += ' ';
        call += args.toLocal8Bit();
    }
    call += "\r\n";
275
    saver.write(call);
276 277
    const QByteArray redirect = "set > " + Utils::QtcProcess::quoteArg(
                QDir::toNativeSeparators(tempOutputFileName)).toLocal8Bit() + "\r\n";
278 279 280 281 282
    saver.write(redirect);
    if (!saver.finalize()) {
        qWarning("%s: %s", Q_FUNC_INFO, qPrintable(saver.errorString()));
        return result;
    }
283 284 285 286

    Utils::QtcProcess run;
    run.setEnvironment(env);
    const QString cmdPath = QString::fromLocal8Bit(qgetenv("COMSPEC"));
Friedemann Kleint's avatar
Friedemann Kleint committed
287 288
    // Windows SDK setup scripts require command line switches for environment expansion.
    QString cmdArguments = QLatin1String(" /E:ON /V:ON /c \"");
289
    cmdArguments += QDir::toNativeSeparators(saver.fileName());
Friedemann Kleint's avatar
Friedemann Kleint committed
290 291 292 293 294
    cmdArguments += QLatin1Char('"');
    run.setCommand(cmdPath, cmdArguments);
    if (debug)
        qDebug() << "msvcReadEnvironmentSetting: " << call << cmdPath << cmdArguments
                 << " Env: " << env.size();
295
    run.start();
Friedemann Kleint's avatar
Friedemann Kleint committed
296

297 298 299
    if (!run.waitForStarted()) {
        qWarning("%s: Unable to run '%s': %s", Q_FUNC_INFO, qPrintable(varsBat),
            qPrintable(run.errorString()));
Friedemann Kleint's avatar
Friedemann Kleint committed
300
        return result;
301 302 303 304
    }
    if (!run.waitForFinished()) {
        qWarning("%s: Timeout running '%s'", Q_FUNC_INFO, qPrintable(varsBat));
        Utils::SynchronousProcess::stopProcess(run);
Friedemann Kleint's avatar
Friedemann Kleint committed
305
        return result;
306 307 308 309
    }

    QFile varsFile(tempOutputFileName);
    if (!varsFile.open(QIODevice::ReadOnly|QIODevice::Text))
Friedemann Kleint's avatar
Friedemann Kleint committed
310
        return result;
311 312 313 314 315 316 317 318

    QRegExp regexp(QLatin1String("(\\w*)=(.*)"));
    while (!varsFile.atEnd()) {
        const QString line = QString::fromLocal8Bit(varsFile.readLine()).trimmed();
        if (regexp.exactMatch(line)) {
            const QString varName = regexp.cap(1);
            const QString expandedValue = winExpandDelayedEnvReferences(regexp.cap(2), env);
            if (!expandedValue.isEmpty())
Friedemann Kleint's avatar
Friedemann Kleint committed
319
                result.set(varName, expandedValue);
320 321 322 323
        }
    }
    varsFile.close();
    varsFile.remove();
Friedemann Kleint's avatar
Friedemann Kleint committed
324 325 326 327 328 329 330 331 332 333
    if (debug) {
        const QStringList newVars = result.toStringList();
        const QStringList oldVars = env.toStringList();
        QDebug nsp = qDebug().nospace();
        foreach (const QString &n, newVars) {
            if (!oldVars.contains(n))
                nsp << n << '\n';
        }
    }
    return result;
334 335 336 337 338 339
}

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

340
MsvcToolChain::MsvcToolChain(const QString &name, const Abi &abi,
341 342 343 344
                             const QString &varsBat, const QString &varsBatArg, bool autodetect) :
    ToolChain(QLatin1String(Constants::MSVC_TOOLCHAIN_ID), autodetect),
    m_varsBat(varsBat),
    m_varsBatArg(varsBatArg),
Friedemann Kleint's avatar
Friedemann Kleint committed
345
    m_lastEnvironment(Utils::Environment::systemEnvironment()),
346
    m_abi(abi)
347 348 349 350
{
    Q_ASSERT(!name.isEmpty());
    Q_ASSERT(!m_varsBat.isEmpty());
    Q_ASSERT(QFileInfo(m_varsBat).exists());
351 352 353
    Q_ASSERT(abi.os() == Abi::WindowsOS);
    Q_ASSERT(abi.binaryFormat() == Abi::PEFormat);
    Q_ASSERT(abi.osFlavor() != Abi::WindowsMSysFlavor);
354

Tobias Hunger's avatar
Tobias Hunger committed
355 356
    setId(QString::fromLatin1("%1:%2.%3.%4").arg(Constants::MSVC_TOOLCHAIN_ID).arg(m_varsBat)
            .arg(m_varsBatArg).arg(m_debuggerCommand));
357

358
    setDisplayName(name);
359 360 361 362 363 364 365 366 367
}

QString MsvcToolChain::typeName() const
{
    return MsvcToolChainFactory::tr("MSVC");
}

Abi MsvcToolChain::targetAbi() const
{
368
    return m_abi;
369 370 371 372 373 374 375 376 377 378 379 380
}

bool MsvcToolChain::isValid() const
{
    return !m_varsBat.isEmpty();
}

QByteArray MsvcToolChain::predefinedMacros() const
{
    if (m_predefinedMacros.isEmpty()) {
        Utils::Environment env(m_lastEnvironment);
        addToEnvironment(env);
Friedemann Kleint's avatar
Friedemann Kleint committed
381
        m_predefinedMacros = msvcPredefinedMacros(env);
382 383 384 385 386 387
    }
    return m_predefinedMacros;
}

QList<HeaderPath> MsvcToolChain::systemHeaderPaths() const
{
Friedemann Kleint's avatar
Friedemann Kleint committed
388
    if (m_headerPaths.isEmpty()) {
389 390 391
        Utils::Environment env(m_lastEnvironment);
        addToEnvironment(env);
        foreach (const QString &path, env.value("INCLUDE").split(QLatin1Char(';')))
Friedemann Kleint's avatar
Friedemann Kleint committed
392
            m_headerPaths.append(HeaderPath(path, HeaderPath::GlobalHeaderPath));
393
    }
Friedemann Kleint's avatar
Friedemann Kleint committed
394
    return m_headerPaths;
395 396 397 398 399 400
}

void MsvcToolChain::addToEnvironment(Utils::Environment &env) const
{
    // We cache the full environment (incoming + modifications by setup script).
    if (!m_resultEnvironment.size() || env != m_lastEnvironment) {
Friedemann Kleint's avatar
Friedemann Kleint committed
401 402
        if (debug)
            qDebug() << "addToEnvironment: " << displayName();
403 404 405 406 407 408
        m_lastEnvironment = env;
        m_resultEnvironment = msvcReadEnvironmentSetting(m_varsBat, m_varsBatArg, env);
    }
    env = m_resultEnvironment;
}

409 410 411 412 413 414 415 416 417 418 419
QString MsvcToolChain::mkspec() const
{
    if (m_abi.osFlavor() == Abi::WindowsMsvc2005Flavor)
        return QLatin1String("win32-msvc2005");
    if (m_abi.osFlavor() == Abi::WindowsMsvc2008Flavor)
        return QLatin1String("win32-msvc2008");
    if (m_abi.osFlavor() == Abi::WindowsMsvc2010Flavor)
        return QLatin1String("win32-msvc2010");
    return QString();
}

420 421 422 423
QString MsvcToolChain::makeCommand() const
{
    if (ProjectExplorerPlugin::instance()->projectExplorerSettings().useJom) {
        // We want jom! Try to find it.
Friedemann Kleint's avatar
Friedemann Kleint committed
424 425 426 427 428 429
        const QString jom = QLatin1String("jom.exe");
        const QFileInfo installedJom = QFileInfo(QCoreApplication::applicationDirPath()
                                                 + QLatin1Char('/') + jom);
        if (installedJom.isFile() && installedJom.isExecutable()) {
            return installedJom.absoluteFilePath();
        } else {
430
            return jom;
Friedemann Kleint's avatar
Friedemann Kleint committed
431
        }
432 433 434 435
    }
    return QLatin1String("nmake.exe");
}

436 437
void MsvcToolChain::setDebuggerCommand(const QString &d)
{
Tobias Hunger's avatar
Tobias Hunger committed
438 439
    if (m_debuggerCommand == d)
        return;
440
    m_debuggerCommand = d;
Tobias Hunger's avatar
Tobias Hunger committed
441
    toolChainUpdated();
442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464
}

QString MsvcToolChain::debuggerCommand() const
{
    return m_debuggerCommand;
}

QVariantMap MsvcToolChain::toMap() const
{
    QVariantMap data = ToolChain::toMap();
    data.insert(QLatin1String(debuggerCommandKeyC), m_debuggerCommand);
    return data;
}

bool MsvcToolChain::fromMap(const QVariantMap &data)
{
    if (!ToolChain::fromMap(data))
        return false;

    m_debuggerCommand= data.value(QLatin1String(debuggerCommandKeyC)).toString();
    return true;
}

465 466 467 468 469 470 471 472 473 474 475 476
IOutputParser *MsvcToolChain::outputParser() const
{
    return new MsvcParser;
}

ToolChainConfigWidget *MsvcToolChain::configurationWidget()
{
    return new MsvcToolChainConfigWidget(this);
}

bool MsvcToolChain::canClone() const
{
477
    return true;
478 479 480 481
}

ToolChain *MsvcToolChain::clone() const
{
482
    return new MsvcToolChain(*this);
483 484
}

485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520
// --------------------------------------------------------------------------
// MsvcDebuggerConfigLabel
// --------------------------------------------------------------------------

static const char dgbToolsDownloadLink32C[] = "http://www.microsoft.com/whdc/devtools/debugging/installx86.Mspx";
static const char dgbToolsDownloadLink64C[] = "http://www.microsoft.com/whdc/devtools/debugging/install64bit.Mspx";

QString MsvcDebuggerConfigLabel::labelText()
{
#ifdef Q_OS_WIN
    const bool is64bit = Utils::winIs64BitSystem();
#else
    const bool is64bit = false;
#endif
    const QString link = is64bit ? QLatin1String(dgbToolsDownloadLink64C) : QLatin1String(dgbToolsDownloadLink32C);
    //: Label text for path configuration. %2 is "x-bit version".
    return tr(
    "<html><body><p>Specify the path to the "
    "<a href=\"%1\">Windows Console Debugger executable</a>"
    " (%2) here.</p>"
    "</body></html>").arg(link, (is64bit ? tr("64-bit version")
                                         : tr("32-bit version")));
}

MsvcDebuggerConfigLabel::MsvcDebuggerConfigLabel(QWidget *parent) :
        QLabel(labelText(), parent)
{
    connect(this, SIGNAL(linkActivated(QString)), this, SLOT(slotLinkActivated(QString)));
    setTextInteractionFlags(Qt::TextBrowserInteraction);
}

void MsvcDebuggerConfigLabel::slotLinkActivated(const QString &link)
{
    QDesktopServices::openUrl(QUrl(link));
}

521 522 523 524 525 526 527
// --------------------------------------------------------------------------
// MsvcToolChainConfigWidget
// --------------------------------------------------------------------------

MsvcToolChainConfigWidget::MsvcToolChainConfigWidget(ToolChain *tc) :
    ToolChainConfigWidget(tc)
{
528 529
    QFormLayout *formLayout = new QFormLayout(this);
    formLayout->addRow(new QLabel(tc->displayName()));
530
    formLayout->addRow(new MsvcDebuggerConfigLabel);
531 532 533 534
    addDebuggerCommandControls(formLayout, QStringList(QLatin1String("-version")));
    addDebuggerAutoDetection(this, SLOT(autoDetectDebugger()));
    addErrorLabel(formLayout);
    setFromToolChain();
535 536 537 538
}

void MsvcToolChainConfigWidget::apply()
{
539 540 541
    MsvcToolChain *tc = static_cast<MsvcToolChain *>(toolChain());
    QTC_ASSERT(tc, return; );
    tc->setDebuggerCommand(debuggerCommand());
542 543
}

544
void MsvcToolChainConfigWidget::setFromToolChain()
545
{
546 547 548
    MsvcToolChain *tc = static_cast<MsvcToolChain *>(toolChain());
    QTC_ASSERT(tc, return);
    setDebuggerCommand(tc->debuggerCommand());
549 550 551 552
}

bool MsvcToolChainConfigWidget::isDirty() const
{
553 554 555
    MsvcToolChain *tc = static_cast<MsvcToolChain *>(toolChain());
    QTC_ASSERT(tc, return false);
    return debuggerCommand() != tc->debuggerCommand();
556 557
}

558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573
void MsvcToolChainConfigWidget::autoDetectDebugger()
{
    QStringList directories;
    const QString cdbExecutable = MsvcToolChain::autoDetectCdbDebugger(&directories);
    if (cdbExecutable.isEmpty()) {
        const QString msg = tr("The CDB debugger could not be found in %1").arg(directories.join(QLatin1String(", ")));
        setErrorMessage(msg);
    } else {
        clearErrorMessage();
        if (cdbExecutable != debuggerCommand()) {
            setDebuggerCommand(cdbExecutable);
            emitDirty();
        }
    }
}

574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599
// --------------------------------------------------------------------------
// MsvcToolChainFactory
// --------------------------------------------------------------------------

QString MsvcToolChainFactory::displayName() const
{
    return tr("MSVC");
}

QString MsvcToolChainFactory::id() const
{
    return QLatin1String(Constants::MSVC_TOOLCHAIN_ID);
}

QList<ToolChain *> MsvcToolChainFactory::autoDetect()
{
    QList<ToolChain *> results;

#ifdef Q_OS_WIN
    // 1) Installed SDKs preferred over standalone Visual studio
    const QSettings sdkRegistry("HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Microsoft SDKs\\Windows",
                                QSettings::NativeFormat);
    const QString defaultSdkPath = sdkRegistry.value(QLatin1String("CurrentInstallFolder")).toString();
    if (!defaultSdkPath.isEmpty()) {
        foreach (const QString &sdkKey, sdkRegistry.childGroups()) {
            const QString name = sdkRegistry.value(sdkKey + QLatin1String("/ProductName")).toString();
600
            const QString version = sdkRegistry.value(sdkKey + QLatin1String("/ProductVersion")).toString();
601 602 603 604 605 606 607 608
            const QString folder = sdkRegistry.value(sdkKey + QLatin1String("/InstallationFolder")).toString();
            if (folder.isEmpty())
                continue;

            const QString sdkVcVarsBat = folder + QLatin1String("bin\\SetEnv.cmd");
            if (!QFileInfo(sdkVcVarsBat).exists())
                continue;
            QList<ToolChain *> tmp;
609 610 611

            tmp.append(new MsvcToolChain(generateDisplayName(name, MsvcToolChain::WindowsSDK, MsvcToolChain::s32),
                                         findAbiOfMsvc(MsvcToolChain::WindowsSDK, MsvcToolChain::s32, version),
612 613
                                         sdkVcVarsBat, QLatin1String("/x86"), true));
            // Add all platforms
614 615
            tmp.append(new MsvcToolChain(generateDisplayName(name, MsvcToolChain::WindowsSDK, MsvcToolChain::s64),
                                         findAbiOfMsvc(MsvcToolChain::WindowsSDK, MsvcToolChain::s64, version),
616
                                         sdkVcVarsBat, QLatin1String("/x64"), true));
617 618
            tmp.append(new MsvcToolChain(generateDisplayName(name, MsvcToolChain::WindowsSDK, MsvcToolChain::ia64),
                                         findAbiOfMsvc(MsvcToolChain::WindowsSDK, MsvcToolChain::ia64, version),
619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646
                                         sdkVcVarsBat, QLatin1String("/ia64"), true));
            // Make sure the default is front.
            if (folder == defaultSdkPath)
                results = tmp + results;
            else
                results += tmp;
        } // foreach
    }

    // 2) Installed MSVCs
    const QSettings vsRegistry(
#ifdef Q_OS_WIN64
                QLatin1String("HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\Microsoft\\VisualStudio\\SxS\\VC7"),
#else
                QLatin1String("HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\SxS\\VC7"),
#endif
                QSettings::NativeFormat);
    foreach (const QString &vsName, vsRegistry.allKeys()) {
        // Scan for version major.minor
        const int dotPos = vsName.indexOf(QLatin1Char('.'));
        if (dotPos == -1)
            continue;

        const QString path = vsRegistry.value(vsName).toString();
        const int version = vsName.left(dotPos).toInt();
        // Check existence of various install scripts
        const QString vcvars32bat = path + QLatin1String("bin\\vcvars32.bat");
        if (QFileInfo(vcvars32bat).isFile())
647 648
            results.append(new MsvcToolChain(generateDisplayName(vsName, MsvcToolChain::VS, MsvcToolChain::s32),
                                             findAbiOfMsvc(MsvcToolChain::VS, MsvcToolChain::s32, vsName),
649 650 651 652 653
                                             vcvars32bat, QString(), true));
        if (version >= 10) {
            // Just one common file
            const QString vcvarsAllbat = path + QLatin1String("vcvarsall.bat");
            if (QFileInfo(vcvarsAllbat).isFile()) {
654 655
                results.append(new MsvcToolChain(generateDisplayName(vsName, MsvcToolChain::VS, MsvcToolChain::s32),
                                                 findAbiOfMsvc(MsvcToolChain::VS, MsvcToolChain::s32, vsName),
656
                                                 vcvarsAllbat, QLatin1String("x86"), true));
657 658
                results.append(new MsvcToolChain(generateDisplayName(vsName, MsvcToolChain::VS, MsvcToolChain::amd64),
                                                 findAbiOfMsvc(MsvcToolChain::VS, MsvcToolChain::amd64, vsName),
659
                                                 vcvarsAllbat, QLatin1String("amd64"), true));
660 661
                results.append(new MsvcToolChain(generateDisplayName(vsName, MsvcToolChain::VS, MsvcToolChain::s64),
                                                 findAbiOfMsvc(MsvcToolChain::VS, MsvcToolChain::s64, vsName),
662
                                                 vcvarsAllbat, QLatin1String("x64"), true));
663 664
                results.append(new MsvcToolChain(generateDisplayName(vsName, MsvcToolChain::VS, MsvcToolChain::ia64),
                                                 findAbiOfMsvc(MsvcToolChain::VS, MsvcToolChain::ia64, vsName),
665 666 667 668 669 670 671 672
                                                 vcvarsAllbat, QLatin1String("ia64"), true));
            } else {
                qWarning("Unable to find MSVC setup script %s in version %d", qPrintable(vcvarsAllbat), version);
            }
        } else {
            // Amd 64 is the preferred 64bit platform
            const QString vcvarsAmd64bat = path + QLatin1String("bin\\amd64\\vcvarsamd64.bat");
            if (QFileInfo(vcvarsAmd64bat).isFile())
673 674
                results.append(new MsvcToolChain(generateDisplayName(vsName, MsvcToolChain::VS, MsvcToolChain::amd64),
                                                 findAbiOfMsvc(MsvcToolChain::VS, MsvcToolChain::amd64, vsName),
675 676 677
                                                 vcvarsAmd64bat, QString(), true));
            const QString vcvarsAmd64bat2 = path + QLatin1String("bin\\vcvarsx86_amd64.bat");
            if (QFileInfo(vcvarsAmd64bat2).isFile())
678 679
                results.append(new MsvcToolChain(generateDisplayName(vsName, MsvcToolChain::VS, MsvcToolChain::amd64),
                                                 findAbiOfMsvc(MsvcToolChain::VS, MsvcToolChain::amd64, vsName),
680 681 682
                                                 vcvarsAmd64bat2, QString(), true));
            const QString vcvars64bat = path + QLatin1String("bin\\vcvars64.bat");
            if (QFileInfo(vcvars64bat).isFile())
683 684
                results.append(new MsvcToolChain(generateDisplayName(vsName, MsvcToolChain::VS, MsvcToolChain::s64),
                                                 findAbiOfMsvc(MsvcToolChain::VS, MsvcToolChain::s64, vsName),
685 686 687
                                                 vcvars64bat, QString(), true));
            const QString vcvarsIA64bat = path + QLatin1String("bin\\vcvarsx86_ia64.bat");
            if (QFileInfo(vcvarsIA64bat).isFile())
688 689
                results.append(new MsvcToolChain(generateDisplayName(vsName, MsvcToolChain::VS, MsvcToolChain::ia64),
                                                 findAbiOfMsvc(MsvcToolChain::VS, MsvcToolChain::ia64, vsName),
690 691 692 693
                                                 vcvarsIA64bat, QString(), true));
        }
    }
#endif
694 695 696 697 698 699 700
    if (!results.isEmpty()) { // Detect debugger
        const QString cdbDebugger = MsvcToolChain::autoDetectCdbDebugger();
        if (!cdbDebugger.isEmpty()) {
            foreach (ToolChain *tc, results)
                static_cast<MsvcToolChain *>(tc)->setDebuggerCommand(cdbDebugger);
        }
    }
701 702 703
    return results;
}

704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733
// Check the CDB executable and accumulate the list of checked paths
// for reporting.
static QString checkCdbExecutable(const QString &programDir, const QString &postfix,
                                  QStringList *checkedDirectories = 0)
{
    QString executable = programDir;
    executable += QLatin1String("/Debugging Tools For Windows");
    executable += postfix;
    if (checkedDirectories)
        checkedDirectories->push_back(QDir::toNativeSeparators(executable));
    executable += QLatin1String("/cdb.exe");
    const QFileInfo fi(executable);
    return fi.isFile() && fi.isExecutable() ? fi.absoluteFilePath() : QString();
}

QString MsvcToolChain::autoDetectCdbDebugger(QStringList *checkedDirectories /* = 0 */)
{
    // Look for $ProgramFiles/"Debugging Tools For Windows <bit-idy>/cdb.exe" and its
    // " (x86)", " (x64)" variations.
    static const char *postFixes[] = {"", " (x64)", " 64-bit", " (x86)", " (x32)" };

    if (checkedDirectories)
        checkedDirectories->clear();

    const QString programDir = QString::fromLocal8Bit(qgetenv("ProgramFiles"));
    if (programDir.isEmpty())
        return QString();

    // Try the post fixes
    QString outPath;
Friedemann Kleint's avatar
Friedemann Kleint committed
734
    for (unsigned i = 0; i < sizeof(postFixes)/sizeof(const char*); ++i) {
735 736 737 738 739 740 741 742 743 744 745 746 747
        outPath = checkCdbExecutable(programDir, QLatin1String(postFixes[i]), checkedDirectories);
        if (!outPath.isEmpty())
            return outPath;
    }
    // A 32bit-compile running on a 64bit system sees the 64 bit installation
    // as "$ProgramFiles (x64)/Debugging Tools..." and (untested), a 64 bit-
    // compile running on a 64bit system sees the 32 bit installation as
    // "$ProgramFiles (x86)/Debugging Tools..." (assuming this works at all)
#ifdef Q_OS_WIN64
    outPath = checkCdbExecutable(programDir + QLatin1String(" (x32)"), QString(), checkedDirectories);
    if (!outPath.isEmpty())
        return QString();
#else
748
    // A 32bit process on 64 bit sees "ProgramFiles\Debg.. (x64)".
749
    if (programDir.endsWith(QLatin1String(" (x86)"))) {
750
        const QString programDir64 = programDir.left(programDir.size() - 6);
Friedemann Kleint's avatar
Friedemann Kleint committed
751
        for (unsigned i = 0; i < sizeof(postFixes)/sizeof(const char*); ++i) {
752 753 754 755
            outPath = checkCdbExecutable(programDir64, QLatin1String(postFixes[i]), checkedDirectories);
            if (!outPath.isEmpty())
                return outPath;
        }
756 757 758 759 760
    }
#endif
    return QString();
}

761 762
} // namespace Internal
} // namespace ProjectExplorer