debuggerkitconfigwidget.cpp 40.7 KB
Newer Older
hjk's avatar
hjk committed
1
/****************************************************************************
Tobias Hunger's avatar
Tobias Hunger committed
2
**
3
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
hjk's avatar
hjk committed
4
** Contact: http://www.qt-project.org/legal
Tobias Hunger's avatar
Tobias Hunger committed
5
**
hjk's avatar
hjk committed
6
** This file is part of Qt Creator.
Tobias Hunger's avatar
Tobias Hunger committed
7
**
hjk's avatar
hjk committed
8 9 10 11 12 13 14
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia.  For licensing terms and
** conditions see http://qt.digia.com/licensing.  For further information
** use the contact form at http://qt.digia.com/contact-us.
Tobias Hunger's avatar
Tobias Hunger committed
15 16
**
** GNU Lesser General Public License Usage
hjk's avatar
hjk committed
17 18 19 20 21 22 23 24 25
** Alternatively, 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.
**
** In addition, as a special exception, Digia gives you certain additional
** rights.  These rights are described in the Digia Qt LGPL Exception
Tobias Hunger's avatar
Tobias Hunger committed
26 27
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
hjk's avatar
hjk committed
28
****************************************************************************/
Tobias Hunger's avatar
Tobias Hunger committed
29

Tobias Hunger's avatar
Tobias Hunger committed
30
#include "debuggerkitconfigwidget.h"
Tobias Hunger's avatar
Tobias Hunger committed
31

32 33
#include <coreplugin/icore.h>

Tobias Hunger's avatar
Tobias Hunger committed
34
#include <projectexplorer/abi.h>
35 36 37
#include <projectexplorer/projectexplorerconstants.h>
#include <projectexplorer/toolchain.h>
#include <projectexplorer/toolchainmanager.h>
Tobias Hunger's avatar
Tobias Hunger committed
38

39
#include <utils/elidinglabel.h>
40 41 42 43
#include <utils/environment.h>
#include <utils/pathchooser.h>
#include <utils/persistentsettings.h>
#include <utils/qtcassert.h>
Tobias Hunger's avatar
Tobias Hunger committed
44 45 46 47 48

#ifdef Q_OS_WIN
#include <utils/winutils.h>
#endif

49
#include <QApplication>
50
#include <QComboBox>
51 52 53 54 55
#include <QDirIterator>
#include <QFileInfo>
#include <QFormLayout>
#include <QHeaderView>
#include <QLineEdit>
56
#include <QPushButton>
57 58 59 60 61 62 63 64
#include <QStandardItem>
#include <QStandardItemModel>
#include <QTreeView>
#include <QUuid>

using namespace ProjectExplorer;
using namespace Utils;
using namespace Debugger::Internal;
Tobias Hunger's avatar
Tobias Hunger committed
65 66 67

namespace Debugger {

68
static const char debuggingToolsWikiLinkC[] = "http://qt-project.org/wiki/Qt_Creator_Windows_Debugging";
69

70 71 72 73
static const char DEBUGGER_DATA_KEY[] = "DebuggerItem.";
static const char DEBUGGER_COUNT_KEY[] = "DebuggerItem.Count";
static const char DEBUGGER_FILE_VERSION_KEY[] = "Version";
static const char DEBUGGER_FILENAME[] = "/qtcreator/debuggers.xml";
74
static const char DEBUGGER_LEGACY_FILENAME[] = "/qtcreator/profiles.xml";
Tobias Hunger's avatar
Tobias Hunger committed
75

76 77 78 79 80 81 82
// --------------------------------------------------------------------------
// DebuggerKitInformation
// --------------------------------------------------------------------------

DebuggerKitInformation::DebuggerKitInformation()
{
    setObjectName(QLatin1String("DebuggerKitInformation"));
83
    setId(DebuggerKitInformation::id());
84
    setPriority(28000);
85 86 87 88
}

QVariant DebuggerKitInformation::defaultValue(Kit *k) const
{
89 90 91 92 93 94
// This is only called from Kit::Kit()
//    if (isValidDebugger(k)) {
//        DebuggerItem *item = DebuggerItemManager::debuggerFromKit(k);
//        QTC_ASSERT(item, return QVariant());
//        return item->id;
//    }
95

96
    ToolChain *tc = ToolChainKitInformation::toolChain(k);
97
    return DebuggerItemManager::defaultDebugger(tc);
98 99 100 101
}

void DebuggerKitInformation::setup(Kit *k)
{
102
    k->setValue(DebuggerKitInformation::id(), defaultValue(k));
103 104 105 106 107 108 109 110 111 112 113 114
}

// Check the configuration errors and return a flag mask. Provide a quick check and
// a verbose one with a list of errors.

enum DebuggerConfigurationErrors {
    NoDebugger = 0x1,
    DebuggerNotFound = 0x2,
    DebuggerNotExecutable = 0x4,
    DebuggerNeedsAbsolutePath = 0x8
};

115 116 117
static QVariant debuggerPathOrId(const Kit *k)
{
    QTC_ASSERT(k, return QString());
118
    QVariant id = k->value(DebuggerKitInformation::id());
119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134
    if (!id.isValid())
        return id; // Invalid.

    // With 3.0 we have:
    // <value type="QString" key="Debugger.Information">{75ecf347-f221-44c3-b613-ea1d29929cd4}</value>
    if (id.type() == QVariant::String)
        return id;

    // Before we had:
    // <valuemap type="QVariantMap" key="Debugger.Information">
    //    <value type="QString" key="Binary">/data/dev/debugger/gdb-git/gdb/gdb</value>
    //    <value type="int" key="EngineType">1</value>
    //  </valuemap>
    return id.toMap().value(QLatin1String("Binary"));
}

135 136
static unsigned debuggerConfigurationErrors(const Kit *k)
{
137 138
    QTC_ASSERT(k, return NoDebugger);

139
    const DebuggerItem *item = DebuggerKitInformation::debugger(k);
140 141 142
    if (!item)
        return NoDebugger;

143
    if (item->command().isEmpty())
144 145
        return NoDebugger;

146
    unsigned result = 0;
147
    const QFileInfo fi = item->command().toFileInfo();
148 149 150 151 152
    if (!fi.exists() || fi.isDir())
        result |= DebuggerNotFound;
    else if (!fi.isExecutable())
        result |= DebuggerNotExecutable;

153
    if (!fi.exists() || fi.isDir()) {
154
        if (item->engineType() == NoEngineType)
155 156
            return NoDebugger;

157
        // We need an absolute path to be able to locate Python on Windows.
158
        if (item->engineType() == GdbEngineType)
159 160 161
            if (const ToolChain *tc = ToolChainKitInformation::toolChain(k))
                if (tc->targetAbi().os() == Abi::WindowsOS && !fi.isAbsolute())
                    result |= DebuggerNeedsAbsolutePath;
162
    }
163 164 165
    return result;
}

166 167 168 169 170 171 172 173 174 175 176 177 178 179
const DebuggerItem *DebuggerKitInformation::debugger(const Kit *kit)
{
    if (!kit)
        return 0;
    QVariant pathOrId = debuggerPathOrId(kit);
    foreach (const DebuggerItem &item, DebuggerItemManager::debuggers()) {
        if (item.id() == pathOrId)
            return &item;
        if (item.command() == FileName::fromUserInput(pathOrId.toString()))
            return &item;
    }
    return 0;
}

180 181 182 183 184 185 186 187 188
bool DebuggerKitInformation::isValidDebugger(const Kit *k)
{
    return debuggerConfigurationErrors(k) == 0;
}

QList<Task> DebuggerKitInformation::validateDebugger(const Kit *k)
{
    QList<Task> result;

189
    const unsigned errors = debuggerConfigurationErrors(k);
190 191 192
    if (!errors)
        return result;

193
    QString path;
194
    if (const DebuggerItem *item = debugger(k))
195
        path = item->command().toUserOutput();
196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217

    const Core::Id id = ProjectExplorer::Constants::TASK_CATEGORY_BUILDSYSTEM;
    if (errors & NoDebugger)
        result << Task(Task::Warning, tr("No debugger set up."), FileName(), -1, id);

    if (errors & DebuggerNotFound)
        result << Task(Task::Error, tr("Debugger '%1' not found.").arg(path),
                       FileName(), -1, id);
    if (errors & DebuggerNotExecutable)
        result << Task(Task::Error, tr("Debugger '%1' not executable.").arg(path), FileName(), -1, id);

    if (errors & DebuggerNeedsAbsolutePath) {
        const QString message =
                tr("The debugger location must be given as an "
                   "absolute path (%1).").arg(path);
        result << Task(Task::Error, message, FileName(), -1, id);
    }
    return result;
}

KitConfigWidget *DebuggerKitInformation::createConfigWidget(Kit *k) const
{
218
    return new Internal::DebuggerKitConfigWidget(k, this);
219 220 221 222
}

KitInformation::ItemList DebuggerKitInformation::toUserOutput(const Kit *k) const
{
223
    return ItemList() << qMakePair(tr("Debugger"), displayString(k));
224 225
}

226
FileName DebuggerKitInformation::debuggerCommand(const ProjectExplorer::Kit *k)
227
{
228
    const DebuggerItem *item = debugger(k);
229
    QTC_ASSERT(item, return FileName());
230
    return item->command();
231
}
232

233 234
DebuggerEngineType DebuggerKitInformation::engineType(const ProjectExplorer::Kit *k)
{
235
    const DebuggerItem *item = debugger(k);
236
    QTC_ASSERT(item, return NoEngineType);
237
    return item->engineType();
238 239
}

240
QString DebuggerKitInformation::displayString(const Kit *k)
241
{
242
    const DebuggerItem *item = debugger(k);
243 244
    if (!item)
        return tr("No Debugger");
245
    QString binary = item->command().toUserOutput();
246 247
    QString name = tr("%1 Engine").arg(item->engineTypeName());
    return binary.isEmpty() ? tr("%1 <None>").arg(name) : tr("%1 using \"%2\"").arg(name, binary);
248 249
}

250 251 252 253 254 255 256
void DebuggerKitInformation::setDebugger(Kit *k, const DebuggerItem &item)
{
    // Only register reasonably complete debuggers.
    QTC_ASSERT(!item.id().isValid(), return);
    QTC_ASSERT(!item.command().isEmpty(), return);
    QTC_ASSERT(!item.displayName().isEmpty(), return);
    QTC_ASSERT(item.engineType() != NoEngineType, return);
257 258 259
    // Only set registered/existing debuggers
    QTC_ASSERT(DebuggerItemManager::findByCommand(item.command()), return);
    k->setValue(DebuggerKitInformation::id(), item.id());
260 261
}

262
Core::Id DebuggerKitInformation::id()
263
{
264
    return "Debugger.Information";
265 266
}

267 268 269 270 271
// --------------------------------------------------------------------------
// DebuggerItemManager
// --------------------------------------------------------------------------

static DebuggerItemManager *m_instance = 0;
272 273 274 275 276 277 278

static FileName userSettingsFileName()
{
    QFileInfo settingsLocation(Core::ICore::settings()->fileName());
    return FileName::fromString(settingsLocation.absolutePath() + QLatin1String(DEBUGGER_FILENAME));
}

279
static QList<DebuggerItem> readDebuggers(const FileName &fileName)
280
{
281
    QList<DebuggerItem> result;
282

283 284 285 286
    PersistentSettingsReader reader;
    if (!reader.load(fileName))
        return result;
    QVariantMap data = reader.restoreValues();
287

288 289 290 291
    // Check version
    int version = data.value(QLatin1String(DEBUGGER_FILE_VERSION_KEY), 0).toInt();
    if (version < 1)
        return result;
292

293 294 295 296 297 298 299 300 301 302
    int count = data.value(QLatin1String(DEBUGGER_COUNT_KEY), 0).toInt();
    for (int i = 0; i < count; ++i) {
        const QString key = QString::fromLatin1(DEBUGGER_DATA_KEY) + QString::number(i);
        if (!data.contains(key))
            break;
        const QVariantMap dbMap = data.value(key).toMap();
        DebuggerItem item;
        item.fromMap(dbMap);
        result.append(item);
    }
303

304 305
    return result;
}
306

307 308 309
QList<DebuggerItem> DebuggerItemManager::m_debuggers;
DebuggerItemModel* DebuggerItemManager::m_model = 0;
PersistentSettingsWriter * DebuggerItemManager::m_writer = 0;
310

311 312 313 314
DebuggerItemManager::DebuggerItemManager(QObject *parent)
    : QObject(parent)
{
    m_instance = this;
315
    m_writer = new PersistentSettingsWriter(userSettingsFileName(), QLatin1String("QtCreatorDebugger"));
316
    m_model = new Debugger::Internal::DebuggerItemModel(this);
317 318 319 320
    connect(Core::ICore::instance(), SIGNAL(saveSettingsRequested()),
            this, SLOT(saveDebuggers()));
}

321 322 323 324 325
QObject *DebuggerItemManager::instance()
{
    return m_instance;
}

326 327 328 329 330 331 332
DebuggerItemManager::~DebuggerItemManager()
{
    disconnect(Core::ICore::instance(), SIGNAL(saveSettingsRequested()),
            this, SLOT(saveDebuggers()));
    delete m_writer;
}

333
QList<DebuggerItem> DebuggerItemManager::debuggers()
334
{
335
    return m_debuggers;
336 337
}

338
DebuggerItemModel *DebuggerItemManager::model()
339
{
340
    return m_model;
341 342
}

343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388
void DebuggerItemManager::autoDetectCdbDebugger()
{
    QList<FileName> cdbs;

    QStringList programDirs;
    programDirs.append(QString::fromLocal8Bit(qgetenv("ProgramFiles")));
    programDirs.append(QString::fromLocal8Bit(qgetenv("ProgramFiles(x86)")));
    programDirs.append(QString::fromLocal8Bit(qgetenv("ProgramW6432")));

    foreach (const QString &dirName, programDirs) {
        if (dirName.isEmpty())
            continue;
        QDir dir(dirName);
        // Windows SDK's starting from version 8 live in
        // "ProgramDir\Windows Kits\<version>"
        const QString windowsKitsFolderName = QLatin1String("Windows Kits");
        if (dir.exists(windowsKitsFolderName)) {
            QDir windowKitsFolder = dir;
            if (windowKitsFolder.cd(windowsKitsFolderName)) {
                // Check in reverse order (latest first)
                const QFileInfoList kitFolders =
                    windowKitsFolder.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot,
                                                   QDir::Time | QDir::Reversed);
                foreach (const QFileInfo &kitFolderFi, kitFolders) {
                    const QString path = kitFolderFi.absoluteFilePath();
                    const QFileInfo cdb32(path + QLatin1String("/Debuggers/x86/cdb.exe"));
                    if (cdb32.isExecutable())
                        cdbs.append(FileName::fromString(cdb32.absoluteFilePath()));
                    const QFileInfo cdb64(path + QLatin1String("/Debuggers/x64/cdb.exe"));
                    if (cdb64.isExecutable())
                        cdbs.append(FileName::fromString(cdb64.absoluteFilePath()));
                }
            }
        }

        // Pre Windows SDK 8: Check 'Debugging Tools for Windows'
        foreach (const QFileInfo &fi, dir.entryInfoList(QStringList(QLatin1String("Debugging Tools for Windows*")),
                                                        QDir::Dirs | QDir::NoDotAndDotDot)) {
            FileName filePath(fi);
            filePath.appendPath(QLatin1String("cdb.exe"));
            if (!cdbs.contains(filePath))
                cdbs.append(filePath);
        }
    }

    foreach (const FileName &cdb, cdbs) {
389 390
        if (findByCommand(cdb))
            continue;
391
        DebuggerItem item;
392 393 394 395 396
        item.setAutoDetected(true);
        item.setAbis(Abi::abisOfBinary(cdb));
        item.setCommand(cdb);
        item.setEngineType(CdbEngineType);
        item.setDisplayName(uniqueDisplayName(tr("Auto-detected CDB at %1").arg(cdb.toUserOutput())));
397
        addDebugger(item);
398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421
    }
}

void DebuggerItemManager::autoDetectDebuggers()
{
    autoDetectCdbDebugger();

    QStringList filters;
    filters.append(QLatin1String("gdb-i686-pc-mingw32"));
    filters.append(QLatin1String("gdb"));
    filters.append(QLatin1String("lldb"));
    filters.append(QLatin1String("lldb-*"));

    QFileInfoList suspects;

    QStringList path = Environment::systemEnvironment().path();
    foreach (const QString &base, path) {
        QDir dir(base);
        dir.setNameFilters(filters);
        suspects += dir.entryInfoList();
    }

    foreach (const QFileInfo &fi, suspects) {
        if (fi.exists()) {
422 423 424
            FileName command = FileName::fromString(fi.absoluteFilePath());
            if (findByCommand(command))
                continue;
425
            DebuggerItem item;
426
            item.setCommand(command);
427
            item.reinitializeFromFile();
428
            //: %1: Debugger engine type (GDB, LLDB, CDB...), %2: Path
429
            item.setDisplayName(tr("System %1 at %2")
430
                .arg(item.engineTypeName()).arg(QDir::toNativeSeparators(fi.absoluteFilePath())));
431
            item.setAutoDetected(true);
432
            addDebugger(item);
433 434 435 436
        }
    }
}

437
void DebuggerItemManager::readLegacyDebuggers()
438
{
439 440
    QFileInfo settingsLocation(Core::ICore::settings()->fileName());
    FileName legacyKits = FileName::fromString(settingsLocation.absolutePath() + QLatin1String(DEBUGGER_LEGACY_FILENAME));
441

442 443 444
    PersistentSettingsReader reader;
    if (!reader.load(legacyKits))
        return;
445

446 447 448 449
    foreach (const QVariant &v, reader.restoreValues()) {
        QVariantMap data1 = v.toMap();
        QString kitName = data1.value(QLatin1String("PE.Profile.Name")).toString();
        QVariantMap data2 = data1.value(QLatin1String("PE.Profile.Data")).toMap();
450
        QVariant v3 = data2.value(DebuggerKitInformation::id().toString());
451 452 453 454
        QString fn;
        if (v3.type() == QVariant::String)
            fn = v3.toString();
        else
455
            fn = v3.toMap().value(QLatin1String("Binary")).toString();
456 457 458 459 460 461 462 463
        if (fn.isEmpty())
            continue;
        if (fn.startsWith(QLatin1Char('{')))
            continue;
        FileName command = FileName::fromUserInput(fn);
        if (findByCommand(command))
            continue;
        DebuggerItem item;
464 465
        item.setCommand(command);
        item.setAutoDetected(true);
466
        item.reinitializeFromFile();
467
        item.setDisplayName(tr("Extracted from Kit %1").arg(kitName));
468
        addDebugger(item);
469 470 471 472 473 474
    }
}

const DebuggerItem *DebuggerItemManager::findByCommand(const FileName &command)
{
    foreach (const DebuggerItem &item, m_debuggers)
475
        if (item.command() == command)
476
            return &item;
477

478
    return 0;
479 480
}

481
const DebuggerItem *DebuggerItemManager::findById(const QVariant &id)
482
{
483
    foreach (const DebuggerItem &item, m_debuggers)
484
        if (item.id() == id)
485 486 487 488 489
            return &item;

    return 0;
}

490 491
void DebuggerItemManager::restoreDebuggers()
{
492
    QList<DebuggerItem> dbsToCheck;
493 494 495

    // Read debuggers from SDK
    QFileInfo systemSettingsFile(Core::ICore::settings(QSettings::SystemScope)->fileName());
496
    QList<DebuggerItem> dbsToRegister =
497 498 499
            readDebuggers(FileName::fromString(systemSettingsFile.absolutePath() + QLatin1String(DEBUGGER_FILENAME)));

    // These are autodetected.
500
    for (int i = 0, n = dbsToRegister.size(); i != n; ++i)
501
        dbsToRegister[i].setAutoDetected(true);
502 503 504 505

    // SDK debuggers are always considered to be up-to-date, so no need to recheck them.

    // Read all debuggers from user file.
506
    foreach (const DebuggerItem &item, readDebuggers(userSettingsFileName())) {
507
        if (item.isAutoDetected())
508 509 510 511 512 513
            dbsToCheck.append(item);
        else
            dbsToRegister.append(item);
    }

    // Keep debuggers that were not rediscovered but are still executable and delete the rest
514 515
    foreach (const DebuggerItem &item, dbsToCheck) {
        if (!item.isValid()) {
516
            qWarning() << QString::fromLatin1("DebuggerItem \"%1\" (%2) dropped since it is not valid")
517
                          .arg(item.command().toString()).arg(item.id().toString());
518
        } else {
519
            dbsToRegister.append(item);
520 521 522
        }
    }

523 524
    for (int i = 0, n = dbsToRegister.size(); i != n; ++i) {
        DebuggerItem item = dbsToRegister.at(i);
525
        if (findByCommand(item.command()))
526
            continue;
527
        addDebugger(item);
528
    }
529

530
    // Auto detect current.
531
    autoDetectDebuggers();
532 533

    // Add debuggers from pre-3.x profiles.xml
534
    readLegacyDebuggers();
535 536 537 538 539 540 541 542 543
}

void DebuggerItemManager::saveDebuggers()
{
    QTC_ASSERT(m_writer, return);
    QVariantMap data;
    data.insert(QLatin1String(DEBUGGER_FILE_VERSION_KEY), 1);

    int count = 0;
544 545 546
    foreach (const DebuggerItem &item, m_debuggers) {
        if (item.isValid()) {
            QVariantMap tmp = item.toMap();
547 548 549 550 551 552 553 554 555 556 557 558
            if (tmp.isEmpty())
                continue;
            data.insert(QString::fromLatin1(DEBUGGER_DATA_KEY) + QString::number(count), tmp);
            ++count;
        }
    }
    data.insert(QLatin1String(DEBUGGER_COUNT_KEY), count);
    m_writer->save(data, Core::ICore::mainWindow());

    // Do not save default debuggers as they are set by the SDK.
}

559
void DebuggerItemManager::registerDebugger(const DebuggerItem &item)
560
{
561 562
    if (findByCommand(item.command()))
        return;
563

564
    addDebugger(item);
565 566
}

567
void DebuggerItemManager::deregisterDebugger(const DebuggerItem &item)
568
{
569 570
    if (findByCommand(item.command()))
        removeDebugger(item.id());
571 572
}

573
void DebuggerItemManager::addDebugger(const DebuggerItem& item0)
574
{
575 576 577 578
    DebuggerItem item = item0;
    if (item.id().isNull())
        item.setId(QUuid::createUuid().toString());
    m_debuggers.append(item);
hjk's avatar
hjk committed
579
    m_model->addDebugger(item);
580 581
}

hjk's avatar
hjk committed
582
void DebuggerItemManager::removeDebugger(const QVariant &id)
583
{
584 585
    bool ok = false;
    for (int i = 0, n = m_debuggers.size(); i != n; ++i) {
586
        if (m_debuggers.at(i).id() == id) {
587 588 589 590 591
            m_debuggers.removeAt(i);
            ok = true;
            break;
        }
    }
592

593
    QTC_ASSERT(ok, return);
hjk's avatar
hjk committed
594
    m_model->removeDebugger(id);
595 596
}

597
QString DebuggerItemManager::uniqueDisplayName(const QString &base)
598
{
599 600 601
    foreach (const DebuggerItem &item, m_debuggers)
        if (item.displayName() == base)
            return uniqueDisplayName(base + QLatin1String(" (1)"));
602

603
    return base;
604 605
}

606
void DebuggerItemManager::setItemData(const QVariant &id, const QString &displayName, const FileName &fileName)
607 608 609
{
    for (int i = 0, n = m_debuggers.size(); i != n; ++i) {
        DebuggerItem &item = m_debuggers[i];
610
        if (item.id() == id) {
611 612
            item.setDisplayName(displayName);
            item.setCommand(fileName);
613
            item.reinitializeFromFile();
hjk's avatar
hjk committed
614
            emit m_model->updateDebugger(item.id());
615
            break;
616 617
        }
    }
618 619 620 621 622 623 624
}

QVariant DebuggerItemManager::defaultDebugger(ToolChain *tc)
{
    QTC_ASSERT(tc, return QVariant());

    DebuggerItem result;
625 626
    result.setAutoDetected(true);
    result.setDisplayName(tr("Auto-detected for Tool Chain %1").arg(tc->displayName()));
627 628 629 630 631 632 633 634 635 636 637 638 639 640

    Abi abi = Abi::hostAbi();
    if (tc)
        abi = tc->targetAbi();

//        if (abis.first().wordWidth() == 32)
//            result.first = cdb.toString();
//        else if (abis.first().wordWidth() == 64)
//            result.second = cdb.toString();
//    // prefer 64bit debugger, even for 32bit binaries:
//    if (!result.second.isEmpty())
//        result.first = result.second;


641
    foreach (const DebuggerItem &item, m_debuggers)
642
        foreach (const Abi targetAbi, item.abis())
643
            if (targetAbi.isCompatibleWith(abi))
644
                return item.id();
645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685

    return QVariant();

    /*
    // CDB for windows:
    if (abi.os() == Abi::WindowsOS && abi.osFlavor() != Abi::WindowsMSysFlavor) {
        QPair<QString, QString> cdbs = autoDetectCdbDebugger();
        result.command = FileName::fromString(abi.wordWidth() == 32 ? cdbs.first : cdbs.second);
        result.engineType = CdbEngineType;
        return maybeAddDebugger(result, false);
    }

    // Check suggestions from the SDK.
    Environment env = Environment::systemEnvironment();
    if (tc) {
        tc->addToEnvironment(env); // Find MinGW gdb in toolchain environment.
        QString path = tc->suggestedDebugger().toString();
        if (!path.isEmpty()) {
            const QFileInfo fi(path);
            if (!fi.isAbsolute())
                path = env.searchInPath(path);
            result.command = FileName::fromString(path);
            result.engineType = engineTypeFromBinary(path);
            return maybeAddDebugger(result, false);
        }
    }

    // Default to GDB, system GDB
    result.engineType = GdbEngineType;
    QString gdb;
    const QString systemGdb = QLatin1String("gdb");
    // MinGW: Search for the python-enabled gdb first.
    if (abi.os() == Abi::WindowsOS && abi.osFlavor() == Abi::WindowsMSysFlavor)
        gdb = env.searchInPath(QLatin1String("gdb-i686-pc-mingw32"));
    if (gdb.isEmpty())
        gdb = env.searchInPath(systemGdb);
    result.command = FileName::fromString(env.searchInPath(gdb.isEmpty() ? systemGdb : gdb));
    return maybeAddDebugger(result, false);
    */
}

686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 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 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779
namespace Internal {

static QList<QStandardItem *> describeItem(const DebuggerItem &item)
{
    QList<QStandardItem *> row;
    row.append(new QStandardItem(item.displayName()));
    row.append(new QStandardItem(item.command().toUserOutput()));
    row.append(new QStandardItem(item.engineTypeName()));
    row.at(0)->setData(item.id());
    row.at(0)->setEditable(false);
    row.at(1)->setEditable(false);
    row.at(2)->setEditable(false);
    row.at(0)->setSelectable(true);
    row.at(1)->setSelectable(true);
    row.at(2)->setSelectable(true);
    return row;
}

static QList<QStandardItem *> createRow(const QString &display)
{
    QList<QStandardItem *> row;
    row.append(new QStandardItem(display));
    row.append(new QStandardItem());
    row.append(new QStandardItem());
    row.at(0)->setEditable(false);
    row.at(1)->setEditable(false);
    row.at(2)->setEditable(false);
    row.at(0)->setSelectable(false);
    row.at(1)->setSelectable(false);
    row.at(2)->setSelectable(false);
    return row;
}

class DebuggerItemConfigWidget;

// --------------------------------------------------------------------------
// DebuggerItemModel
// --------------------------------------------------------------------------

DebuggerItemModel::DebuggerItemModel(QObject *parent)
    : QStandardItemModel(parent)
{
    setColumnCount(3);

    QList<QStandardItem *> row = createRow(tr("Auto-detected"));
    m_autoRoot = row.at(0);
    appendRow(row);

    row = createRow(tr("Manual"));
    m_manualRoot = row.at(0);
    appendRow(row);
}

QVariant DebuggerItemModel::headerData(int section, Qt::Orientation orientation, int role) const
{
    if (role == Qt::DisplayRole && orientation == Qt::Horizontal) {
        switch (section) {
        case 0:
            return tr("Name");
        case 1:
            return tr("Path");
        case 2:
            return tr("Type");
        }
    }
    return QVariant();
}

QStandardItem *DebuggerItemModel::currentStandardItem() const
{
    return findStandardItemById(m_currentDebugger);
}

QStandardItem *DebuggerItemModel::findStandardItemById(const QVariant &id) const
{
    for (int i = 0, n = m_autoRoot->rowCount(); i != n; ++i) {
        QStandardItem *sitem = m_autoRoot->child(i);
        if (sitem->data() == id)
            return sitem;
    }
    for (int i = 0, n = m_manualRoot->rowCount(); i != n; ++i) {
        QStandardItem *sitem = m_manualRoot->child(i);
        if (sitem->data() == id)
            return sitem;
    }
    return 0;
}

QModelIndex DebuggerItemModel::currentIndex() const
{
    QStandardItem *current = currentStandardItem();
    return current ? current->index() : QModelIndex();
}

hjk's avatar
hjk committed
780
QModelIndex DebuggerItemModel::lastIndex() const
781
{
hjk's avatar
hjk committed
782 783 784
    int n = m_manualRoot->rowCount();
    QStandardItem *current = m_manualRoot->child(n - 1);
    return current ? current->index() : QModelIndex();
785 786 787 788 789 790 791 792 793 794 795
}

void DebuggerItemModel::markCurrentDirty()
{
    QStandardItem *sitem = currentStandardItem();
    QTC_ASSERT(sitem, return);
    QFont font = sitem->font();
    font.setBold(true);
    sitem->setFont(font);
}

hjk's avatar
hjk committed
796
void DebuggerItemModel::addDebugger(const DebuggerItem &item0)
797 798 799 800 801 802 803 804 805
{
    DebuggerItem item = item0;
    if (item.id().isNull())
        item.setId(QUuid::createUuid().toString());
    QList<QStandardItem *> row = describeItem(item);
    (item.isAutoDetected() ? m_autoRoot : m_manualRoot)->appendRow(row);
    emit debuggerAdded(item.id(), item.displayName());
}

hjk's avatar
hjk committed
806
void DebuggerItemModel::removeDebugger(const QVariant &id)
807 808 809 810 811 812 813 814 815 816 817
{
    QStandardItem *sitem = findStandardItemById(id);
    QTC_ASSERT(sitem, return);
    QStandardItem *parent = sitem->parent();
    QTC_ASSERT(parent, return);
    // This will trigger a change of m_currentDebugger via changing the
    // view selection.
    parent->removeRow(sitem->row());
    emit debuggerRemoved(id);
}

hjk's avatar
hjk committed
818
void DebuggerItemModel::updateDebugger(const QVariant &id)
819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848
{
    QList<DebuggerItem> debuggers = DebuggerItemManager::debuggers();
    for (int i = 0, n = debuggers.size(); i != n; ++i) {
        DebuggerItem &item = debuggers[i];
        if (item.id() == id) {
            QStandardItem *sitem = findStandardItemById(id);
            QTC_ASSERT(sitem, return);
            QStandardItem *parent = sitem->parent();
            QTC_ASSERT(parent, return);
            int row = sitem->row();
            QFont font = sitem->font();
            font.setBold(false);
            parent->child(row, 0)->setData(item.displayName(), Qt::DisplayRole);
            parent->child(row, 0)->setFont(font);
            parent->child(row, 1)->setData(item.command().toUserOutput(), Qt::DisplayRole);
            parent->child(row, 1)->setFont(font);
            parent->child(row, 2)->setData(item.engineTypeName(), Qt::DisplayRole);
            parent->child(row, 2)->setFont(font);
            emit debuggerUpdated(id, item.displayName());
            return;
        }
    }
}

void DebuggerItemModel::setCurrentIndex(const QModelIndex &index)
{
    QStandardItem *sit = itemFromIndex(index);
    m_currentDebugger = sit ? sit->data() : QVariant();
}

849 850 851 852
// -----------------------------------------------------------------------
// DebuggerKitConfigWidget
// -----------------------------------------------------------------------

853 854
DebuggerKitConfigWidget::DebuggerKitConfigWidget(Kit *workingCopy, const KitInformation *ki)
    : KitConfigWidget(workingCopy, ki)
855
{
856 857
    DebuggerItemModel *model = DebuggerItemManager::model();
    QTC_CHECK(model);
858 859 860 861 862

    m_comboBox = new QComboBox;
    m_comboBox->setEnabled(true);
    m_comboBox->setToolTip(toolTip());
    m_comboBox->addItem(tr("None"), QString());
863
    foreach (const DebuggerItem &item, DebuggerItemManager::debuggers())
864
        m_comboBox->addItem(item.displayName(), item.id());
865 866 867 868 869 870 871 872

    refresh();
    connect(m_comboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(currentDebuggerChanged(int)));

    m_manageButton = new QPushButton(tr("Manage..."));
    m_manageButton->setContentsMargins(0, 0, 0, 0);
    connect(m_manageButton, SIGNAL(clicked()), this, SLOT(manageDebuggers()));

873
    connect(model, SIGNAL(debuggerAdded(QVariant,QString)),
874
            this, SLOT(onDebuggerAdded(QVariant,QString)));
875
    connect(model, SIGNAL(debuggerUpdated(QVariant,QString)),
876
            this, SLOT(onDebuggerUpdated(QVariant,QString)));
877
    connect(model, SIGNAL(debuggerRemoved(QVariant)),
878
            this, SLOT(onDebuggerRemoved(QVariant)));
879 880
}

881 882 883 884 885 886
DebuggerKitConfigWidget::~DebuggerKitConfigWidget()
{
    delete m_comboBox;
    delete m_manageButton;
}

887
QString DebuggerKitConfigWidget::toolTip() const
888
{
889
    return tr("The debugger to use for this kit.");
890 891
}

Tobias Hunger's avatar
Tobias Hunger committed
892
QString DebuggerKitConfigWidget::displayName() const
Tobias Hunger's avatar
Tobias Hunger committed
893
{
894
    return tr("Debugger:");
Tobias Hunger's avatar
Tobias Hunger committed
895 896
}

Tobias Hunger's avatar
Tobias Hunger committed
897
void DebuggerKitConfigWidget::makeReadOnly()
Tobias Hunger's avatar
Tobias Hunger committed
898
{
899 900
    m_manageButton->setEnabled(false);
    m_comboBox->setEnabled(false);
Tobias Hunger's avatar
Tobias Hunger committed
901 902
}

Tobias Hunger's avatar
Tobias Hunger committed
903
void DebuggerKitConfigWidget::refresh()
Tobias Hunger's avatar
Tobias Hunger committed
904
{
905
    const DebuggerItem *item = DebuggerKitInformation::debugger(m_kit);
906
    updateComboBox(item ? item->id() : QVariant());
Tobias Hunger's avatar
Tobias Hunger committed
907 908
}

909
QWidget *DebuggerKitConfigWidget::buttonWidget() const
Tobias Hunger's avatar
Tobias Hunger committed
910
{
911
    return m_manageButton;
912 913
}

914
QWidget *DebuggerKitConfigWidget::mainWidget() const
915
{
916
    return m_comboBox;
917 918
}

919 920 921 922 923
void DebuggerKitConfigWidget::manageDebuggers()
{
    Core::ICore::showOptionsDialog(ProjectExplorer::Constants::PROJECTEXPLORER_SETTINGS_CATEGORY,
                                   ProjectExplorer::Constants::DEBUGGER_SETTINGS_PAGE_ID);
}
924

925
void DebuggerKitConfigWidget::currentDebuggerChanged(int)
926
{
927
    m_kit->setValue(DebuggerKitInformation::id(), m_comboBox->itemData(m_comboBox->currentIndex()));
928
}
929

930
void DebuggerKitConfigWidget::onDebuggerAdded(const QVariant &id, const QString &displayName)
931 932
{
    m_comboBox->setEnabled(true);
933
    m_comboBox->addItem(displayName, id);
934 935
    updateComboBox(id);
}
936

937
void DebuggerKitConfigWidget::onDebuggerUpdated(const QVariant &id, const QString &displayName)
938 939
{
    m_comboBox->setEnabled(true);
940
    const int pos = indexOf(id);
941 942
    if (pos < 0)
        return;
943
    m_comboBox->setItemText(pos, displayName);
944
}
945

946
void DebuggerKitConfigWidget::onDebuggerRemoved(const QVariant &id)
947
{
948 949 950 951
    if (const int pos = indexOf(id)) {
        m_comboBox->removeItem(pos);
        updateComboBox(id);
    }
952
}
953

954
int DebuggerKitConfigWidget::indexOf(const QVariant &id)
955
{
956
    QTC_ASSERT(id.isValid(), return -1);
957 958 959 960 961
    for (int i = 0; i < m_comboBox->count(); ++i) {
        if (id == m_comboBox->itemData(i))
            return i;
    }
    return -1;
962 963
}

964
QVariant DebuggerKitConfigWidget::currentId() const
965
{
966
    return m_comboBox->itemData(m_comboBox->currentIndex());
967 968
}

969
void DebuggerKitConfigWidget::updateComboBox(const QVariant &id)
970
{</