From 36227d5c3a3f0ed5b399cf8da2efa923bd24455d Mon Sep 17 00:00:00 2001 From: hjk Date: Wed, 14 Aug 2013 18:30:40 +0200 Subject: [PATCH] ProjectExplorer: Introduce per-kit debugger configuration page Change-Id: I65c76f3ff43e1479075926c7e3fa460cca74d8fe Reviewed-by: Tobias Hunger --- src/plugins/android/androidconfigurations.cpp | 6 +- src/plugins/debugger/debuggerdialogs.cpp | 2 +- .../debugger/debuggerkitconfigwidget.cpp | 1271 +++++++++++++++-- .../debugger/debuggerkitconfigwidget.h | 147 +- .../debugger/debuggerkitinformation.cpp | 355 ----- src/plugins/debugger/debuggerkitinformation.h | 61 +- src/plugins/debugger/debuggerplugin.cpp | 6 +- .../projectexplorerconstants.h | 1 + 8 files changed, 1313 insertions(+), 536 deletions(-) diff --git a/src/plugins/android/androidconfigurations.cpp b/src/plugins/android/androidconfigurations.cpp index b2f33a11fc8..15d25850e0c 100644 --- a/src/plugins/android/androidconfigurations.cpp +++ b/src/plugins/android/androidconfigurations.cpp @@ -786,10 +786,8 @@ void AndroidConfigurations::updateAutomaticKitList() ToolChainKitInformation::setToolChain(newKit, tc); QtSupport::QtKitInformation::setQtVersion(newKit, qt); DeviceKitInformation::setDevice(newKit, device); - Debugger::DebuggerKitInformation::DebuggerItem item; - item.engineType = Debugger::GdbEngineType; - item.binary = tc->suggestedDebugger(); - Debugger::DebuggerKitInformation::setDebuggerItem(newKit, item); + Debugger::DebuggerKitInformation::setDebuggerItem(newKit, + Debugger::GdbEngineType, tc->suggestedDebugger()); AndroidGdbServerKitInformation::setGdbSever(newKit, tc->suggestedGdbServer()); newKit->makeSticky(); newKits << newKit; diff --git a/src/plugins/debugger/debuggerdialogs.cpp b/src/plugins/debugger/debuggerdialogs.cpp index 20ed3a4367b..68983be6d6e 100644 --- a/src/plugins/debugger/debuggerdialogs.cpp +++ b/src/plugins/debugger/debuggerdialogs.cpp @@ -121,7 +121,7 @@ bool DebuggerKitChooser::kitMatches(const ProjectExplorer::Kit *k) const QString DebuggerKitChooser::kitToolTip(Kit *k) const { - return DebuggerKitInformation::userOutput(DebuggerKitInformation::debuggerItem(k)); + return DebuggerKitInformation::debuggerItem(k).userOutput(); } /////////////////////////////////////////////////////////////////////// diff --git a/src/plugins/debugger/debuggerkitconfigwidget.cpp b/src/plugins/debugger/debuggerkitconfigwidget.cpp index 4992bdc789e..e6d64376737 100644 --- a/src/plugins/debugger/debuggerkitconfigwidget.cpp +++ b/src/plugins/debugger/debuggerkitconfigwidget.cpp @@ -32,44 +32,877 @@ #include #include +#include +#include +#include -#include #include +#include +#include +#include +#include #ifdef Q_OS_WIN #include #endif -#include +#include #include +#include +#include +#include +#include +#include +#include #include -#include +#include +#include +#include +#include + +using namespace ProjectExplorer; +using namespace Utils; +using namespace Debugger::Internal; namespace Debugger { -namespace Internal { static const char debuggingToolsWikiLinkC[] = "http://qt-project.org/wiki/Qt_Creator_Windows_Debugging"; -// ----------------------------------------------------------------------- -// DebuggerKitConfigWidget: -// ----------------------------------------------------------------------- +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 DEFAULT_DEBUGGER_COUNT_KEY[] = "DefaultDebugger.Count"; +static const char DEFAULT_DEBUGGER_ABI_KEY[] = "DefaultDebugger.Abi."; +static const char DEFAULT_DEBUGGER_PATH_KEY[] = "DefaultDebugger.Path."; +static const char DEBUGGER_FILENAME[] = "/qtcreator/debuggers.xml"; -DebuggerKitConfigWidget::DebuggerKitConfigWidget(ProjectExplorer::Kit *workingCopy, bool sticky) - : KitConfigWidget(workingCopy, sticky), - m_main(new QWidget), - m_label(new Utils::ElidingLabel(m_main)), - m_autoDetectButton(new QPushButton(tr("Auto-detect"))), - m_editButton(new QPushButton(tr("Edit..."))) +static const char DEBUGGER_INFORMATION[] = "Debugger.Information"; +static const char DEBUGGER_INFORMATION_COMMAND[] = "Binary"; +static const char DEBUGGER_INFORMATION_DISPLAYNAME[] = "DisplayName"; +static const char DEBUGGER_INFORMATION_ID[] = "Id"; +static const char DEBUGGER_INFORMATION_ENGINETYPE[] = "EngineType"; +static const char DEBUGGER_INFORMATION_AUTODETECTED[] = "AutoDetected"; +static const char DEBUGGER_INFORMATION_ABIS[] = "Abis"; + +// -------------------------------------------------------------------------- +// Helpers +// -------------------------------------------------------------------------- + +static DebuggerItemManager *theDebuggerItemManager() { - QHBoxLayout *mainLayout = new QHBoxLayout(m_main); - mainLayout->addWidget(m_label); - mainLayout->setMargin(0); - mainLayout->addWidget(m_autoDetectButton); - m_autoDetectButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred); + static DebuggerItemManager *manager = new DebuggerItemManager(0); + return manager; +} + +// -------------------------------------------------------------------------- +// DebuggerItem +// -------------------------------------------------------------------------- + +DebuggerItem::DebuggerItem() +{ + engineType = NoEngineType; + isAutoDetected = false; +} + +bool DebuggerItem::looksLike(const DebuggerItem &rhs) const +{ + return engineType == rhs.engineType + && displayName == rhs.displayName + && command == rhs.command; +} + +QString DebuggerItem::engineTypeName() const +{ + switch (engineType) { + case Debugger::GdbEngineType: + return QLatin1String("GDB"); + case Debugger::CdbEngineType: + return QLatin1String("CDB"); + case Debugger::LldbEngineType: + return QLatin1String("LLDB"); + default: + return QString(); + } +} + +static QStringList toList(const QList &abis) +{ + QStringList list; + foreach (const Abi &abi, abis) + list.append(abi.toString()); + return list; +} + +QVariantMap DebuggerItem::toMap() const +{ + QVariantMap data; + data.insert(QLatin1String(DEBUGGER_INFORMATION_DISPLAYNAME), displayName); + data.insert(QLatin1String(DEBUGGER_INFORMATION_ID), id); + data.insert(QLatin1String(DEBUGGER_INFORMATION_COMMAND), command.toUserOutput()); + data.insert(QLatin1String(DEBUGGER_INFORMATION_ENGINETYPE), int(engineType)); + data.insert(QLatin1String(DEBUGGER_INFORMATION_AUTODETECTED), isAutoDetected); + data.insert(QLatin1String(DEBUGGER_INFORMATION_ABIS), toList(abis)); + return data; +} + +void DebuggerItem::fromMap(const QVariantMap &data) +{ + command = FileName::fromUserInput(data.value(QLatin1String(DEBUGGER_INFORMATION_COMMAND)).toString()); + id = data.value(QLatin1String(DEBUGGER_INFORMATION_ID)).toString(); + displayName = data.value(QLatin1String(DEBUGGER_INFORMATION_DISPLAYNAME)).toString(); + isAutoDetected = data.value(QLatin1String(DEBUGGER_INFORMATION_AUTODETECTED)).toBool(); + engineType = DebuggerEngineType(data.value(QLatin1String(DEBUGGER_INFORMATION_ENGINETYPE)).toInt()); + + abis.clear(); + foreach (const QString &a, data.value(QLatin1String(DEBUGGER_INFORMATION_ABIS)).toStringList()) { + Abi abi(a); + if (abi.isValid()) + abis.append(abi); + } + +// // Check for special 'auto' entry for binary written by the sdktool during +// // installation. Try to autodetect. +// if (command == QLatin1String("auto")) { +// command.clear(); +// switch (engineType) { +// case Debugger::GdbEngineType: // Auto-detect system gdb on Unix +// if (Abi::hostAbi().os() != Abi::WindowsOS) +// comman = Environment::systemEnvironment().searchInPath(QLatin1String("gdb")); +// break; +// case Debugger::CdbEngineType: { // Auto-detect system CDB on Windows. +// const QPair cdbs = autoDetectCdbDebugger(); +// binary = cdbs.second.isEmpty() ? cdbs.first : cdbs.second; +// } +// break; +// default: +// break; +// } +// } +// command = FileName::fromString(binary); +} + +QString DebuggerItem::userOutput() const +{ + const QString binary = command.toUserOutput(); + const QString name = DebuggerKitInformation::tr("%1 Engine").arg(engineTypeName()); + + return binary.isEmpty() ? DebuggerKitInformation::tr("%1 ").arg(name) + : DebuggerKitInformation::tr("%1 using \"%2\"").arg(name, binary); +} + +void DebuggerItem::reinitializeFromFile() +{ + QProcess proc; + proc.start(command.toString(), QStringList() << QLatin1String("--version")); + proc.waitForStarted(); + proc.waitForFinished(); + QByteArray ba = proc.readAll(); + if (ba.contains("gdb")) { + engineType = GdbEngineType; +// const char needle[] = "This GDB was configured as \""; +// int pos1 = ba.indexOf(needle); +// if (pos1 != -1) { +// pos1 += sizeof(needle); +// int pos2 = ba.indexOf('"', pos1 + 1); +// QByteArray target = ba.mid(pos1, pos2 - pos1); +// abis.append(Abi::abiFromTargetTriplet(target)); // FIXME: Doesn't exist yet. +// } + abis = Abi::abisOfBinary(command); // FIXME: Wrong. + return; + } + if (ba.contains("lldb")) { + engineType = LldbEngineType; + abis = Abi::abisOfBinary(command); + return; + } + if (ba.startsWith("Python")) { + engineType = PdbEngineType; + return; + } + engineType = NoEngineType; +} + +// -------------------------------------------------------------------------- +// DebuggerKitInformation +// -------------------------------------------------------------------------- + +DebuggerKitInformation::DebuggerKitInformation() +{ + setObjectName(QLatin1String("DebuggerKitInformation")); +} + +Core::Id DebuggerKitInformation::dataId() const +{ + static Core::Id id = Core::Id(DEBUGGER_INFORMATION); + return id; +} + +unsigned int DebuggerKitInformation::priority() const +{ + return 28000; +} + +QVariant DebuggerKitInformation::defaultValue(Kit *k) const +{ + if (isValidDebugger(k)) { + DebuggerItem item = debuggerItem(k); + return theDebuggerItemManager()->maybeAddDebugger(item, false); + } + + ToolChain *tc = ToolChainKitInformation::toolChain(k); + return theDebuggerItemManager()->defaultDebugger(tc); +} + +void DebuggerKitInformation::setup(Kit *k) +{ + QTC_ASSERT(ToolChainManager::instance()->isLoaded(), return); + k->setValue(DEBUGGER_INFORMATION, defaultValue(k)); +} + +// 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 +}; + +static unsigned debuggerConfigurationErrors(const Kit *k) +{ + unsigned result = 0; + const DebuggerItem item = DebuggerKitInformation::debuggerItem(k); + if (item.engineType == NoEngineType || item.command.isEmpty()) + return NoDebugger; + + const QFileInfo fi = item.command.toFileInfo(); + if (!fi.exists() || fi.isDir()) + result |= DebuggerNotFound; + else if (!fi.isExecutable()) + result |= DebuggerNotExecutable; + + if (!fi.exists() || fi.isDir()) + // We need an absolute path to be able to locate Python on Windows. + if (item.engineType == GdbEngineType) + if (const ToolChain *tc = ToolChainKitInformation::toolChain(k)) + if (tc->targetAbi().os() == Abi::WindowsOS && !fi.isAbsolute()) + result |= DebuggerNeedsAbsolutePath; + return result; +} + +bool DebuggerKitInformation::isValidDebugger(const Kit *k) +{ + return debuggerConfigurationErrors(k) == 0; +} + +QList DebuggerKitInformation::validateDebugger(const Kit *k) +{ + QList result; + const unsigned errors = debuggerConfigurationErrors(k); + + if (!errors) + return result; + + const QString path = DebuggerKitInformation::debuggerCommand(k).toUserOutput(); + + 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 +{ + return new Internal::DebuggerKitConfigWidget(k, isSticky(k)); +} + +KitInformation::ItemList DebuggerKitInformation::toUserOutput(const Kit *k) const +{ + return ItemList() << qMakePair(tr("Debugger"), debuggerItem(k).userOutput()); +} + +DebuggerItem DebuggerKitInformation::debuggerItem(const ProjectExplorer::Kit *k) +{ + if (!k) + return DebuggerItem(); + + QVariant id = k->value(DEBUGGER_INFORMATION); + if (!id.isValid()) + return DebuggerItem(); + + DebuggerItem *item = DebuggerItemManager::debuggerFromId(id); + QTC_ASSERT(item, return DebuggerItem()); + return *item; +} + + +void DebuggerKitInformation::setDebuggerItem(Kit *k, + DebuggerEngineType type, const Utils::FileName &command) +{ + QTC_ASSERT(k, return); + DebuggerItem item; + item.engineType = type; + item.command = command; + QVariant id = theDebuggerItemManager()->maybeAddDebugger(item); + k->setValue(DEBUGGER_INFORMATION, id); +} + +void DebuggerKitInformation::setDebuggerCommand(Kit *k, const FileName &command) +{ + DebuggerItem item = debuggerItem(k); + item.command = command; + QVariant id = theDebuggerItemManager()->maybeAddDebugger(item); + k->setValue(DEBUGGER_INFORMATION, id); +} + +void DebuggerKitInformation::makeSticky(Kit *k) +{ + k->makeSticky(DEBUGGER_INFORMATION); +} + + +namespace Internal { + +static FileName userSettingsFileName() +{ + QFileInfo settingsLocation(Core::ICore::settings()->fileName()); + return FileName::fromString(settingsLocation.absolutePath() + QLatin1String(DEBUGGER_FILENAME)); +} + +static QList describeItem(DebuggerItem *item) +{ + QList row; + row.append(new QStandardItem(item->displayName)); + row.append(new QStandardItem(item->command.toString())); + 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 createRow(const QString &display) +{ + QList 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; + +// -------------------------------------------------------------------------- +// DebuggerItemManager +// -------------------------------------------------------------------------- + +DebuggerItemManager::DebuggerItemManager(QObject *parent) + : QStandardItemModel(parent) +{ + m_currentDebugger = 0; + setColumnCount(3); + + QList row = createRow(tr("Auto-detected")); + m_autoRoot = row.at(0); + appendRow(row); + + row = createRow(tr("Manual")); + m_manualRoot = row.at(0); + appendRow(row); + + m_writer = new PersistentSettingsWriter(userSettingsFileName(), QLatin1String("QtCreatorDebugger")); + + connect(Core::ICore::instance(), SIGNAL(saveSettingsRequested()), + this, SLOT(saveDebuggers())); +} + +DebuggerItemManager::~DebuggerItemManager() +{ + disconnect(Core::ICore::instance(), SIGNAL(saveSettingsRequested()), + this, SLOT(saveDebuggers())); + qDeleteAll(m_debuggers); + m_debuggers.clear(); + delete m_writer; +} + +QVariant DebuggerItemManager::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(); +} + +void DebuggerItemManager::autoDetectCdbDebugger() +{ + QList 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\" + 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) { + DebuggerItem item; + item.isAutoDetected = true; + item.abis = Abi::abisOfBinary(cdb); + item.command = cdb; + item.engineType = CdbEngineType; + item.displayName = tr("Auto-detected CDB at %s").arg(cdb.toUserOutput()); + maybeAddDebugger(item, false); + } +} + +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()) { + DebuggerItem item; + item.command = FileName::fromString(fi.absoluteFilePath()); + item.reinitializeFromFile(); + item.displayName = tr("System %1 at %2") + .arg(item.engineTypeName()).arg(fi.absoluteFilePath()); + item.isAutoDetected = true; + maybeAddDebugger(item); + } + } +} + +QVariant DebuggerItemManager::maybeAddDebugger(const DebuggerItem &testItem, bool makeCurrent) +{ + foreach (DebuggerItem *it, m_debuggers) { + if (it->looksLike(testItem)) { + m_currentDebugger = it; + return it->id; + } + } + + DebuggerItem *item = new DebuggerItem(testItem); + if (item->id.isNull()) + item->id = QUuid::createUuid().toString(); + + QList row = describeItem(item); + (item->isAutoDetected ? m_autoRoot : m_manualRoot)->appendRow(row); + m_debuggers.append(item); + m_itemFromDebugger[item] = row.at(0); + m_debuggerFromItem[row.at(0)] = item; + + if (makeCurrent) + m_currentDebugger = item; + + emit debuggerAdded(item); + return item->id; +} + +void DebuggerItemManager::updateCurrentItem() +{ + QTC_ASSERT(m_currentDebugger, return); + QStandardItem *item = m_itemFromDebugger.value(m_currentDebugger); + QTC_ASSERT(item, return); + m_currentDebugger->reinitializeFromFile(); + QStandardItem *parent = item->parent(); + QTC_ASSERT(parent, return); + int row = item->row(); + parent->child(row, 0)->setData(m_currentDebugger->displayName, Qt::DisplayRole); + parent->child(row, 1)->setData(m_currentDebugger->command.toString(), Qt::DisplayRole); + parent->child(row, 2)->setData(m_currentDebugger->engineTypeName(), Qt::DisplayRole); + emit debuggerUpdated(m_currentDebugger); +} + +static QList readDebuggers(const FileName &fileName) +{ + QList result; + + PersistentSettingsReader reader; + if (!reader.load(fileName)) + return result; + QVariantMap data = reader.restoreValues(); + + // Check version + int version = data.value(QLatin1String(DEBUGGER_FILE_VERSION_KEY), 0).toInt(); + if (version < 1) + return result; + + // Read default debugger settings (if any) +// int count = data.value(QLatin1String(DEFAULT_DEBUGGER_COUNT_KEY)).toInt(); +// for (int i = 0; i < count; ++i) { +// const QString abiKey = QString::fromLatin1(DEFAULT_DEBUGGER_ABI_KEY) + QString::number(i); +// if (!data.contains(abiKey)) +// continue; +// const QString pathKey = QString::fromLatin1(DEFAULT_DEBUGGER_PATH_KEY) + QString::number(i); +// if (!data.contains(pathKey)) +// continue; +// m_abiToDebugger.insert(data.value(abiKey).toString(), +// FileName::fromString(data.value(pathKey).toString())); +// } + +// QList factories = ExtensionSystem::PluginManager::getObjects(); + + 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 *debugger = new DebuggerItem; + debugger->fromMap(dbMap); + result.append(debugger); + } + + return result; +} + +void DebuggerItemManager::restoreDebuggers() +{ + QList dbsToCheck; + + // Read debuggers from SDK + QFileInfo systemSettingsFile(Core::ICore::settings(QSettings::SystemScope)->fileName()); + QList dbsToRegister = + readDebuggers(FileName::fromString(systemSettingsFile.absolutePath() + QLatin1String(DEBUGGER_FILENAME))); + + // These are autodetected. + foreach (DebuggerItem *item, dbsToRegister) + item->isAutoDetected = true; + + // SDK debuggers are always considered to be up-to-date, so no need to recheck them. + + // Read all debuggers from user file. + foreach (DebuggerItem *item, readDebuggers(userSettingsFileName())) { + if (item->isAutoDetected) + dbsToCheck.append(item); + else + dbsToRegister.append(item); + } + + // Remove debuggers configured by the SDK. + foreach (DebuggerItem *item, dbsToRegister) { + for (int i = dbsToCheck.count(); --i >= 0; ) { + if (dbsToCheck.at(i)->id == item->id) { + delete dbsToCheck.at(i); + dbsToCheck.removeAt(i); + } + } + } + +// QList detectedDbs; +// QList factories = ExtensionSystem::PluginManager::getObjects(); +// foreach (DebuggerFactory *f, factories) +// detectedDbs.append(f->autoDetect()); + + // Find/update autodetected debuggers +// DebuggerItem *toStore = 0; +// foreach (DebuggerItem *currentDetected, detectedDbs) { +// toStore = currentDetected; + +// // Check whether we had this debugger stored and prefer the old one with the old id: +// for (int i = 0; i < dbsToCheck.count(); ++i) { +// if (*(dbsToCheck.at(i)) == *currentDetected) { +// toStore = dbsToCheck.at(i); +// dbsToCheck.removeAt(i); +// delete currentDetected; +// break; +// } +// } +// dbsToRegister += toStore; +// } + + // Keep debuggers that were not rediscovered but are still executable and delete the rest + foreach (DebuggerItem *item, dbsToCheck) { + if (!item->isValid()) { + qWarning() << QString::fromLatin1("DebuggerItem \"%1\" (%2) dropped since it is not valid") + .arg(item->command.toString()).arg(item->id.toString()); + delete item; + } else { + dbsToRegister += item; + } + } + + // Store manual debuggers + DebuggerItemManager *manager = theDebuggerItemManager(); + foreach (DebuggerItem *item, dbsToRegister) + manager->maybeAddDebugger(*item); + + // Then auto detect + manager->autoDetectDebuggers(); +} + +void DebuggerItemManager::saveDebuggers() +{ + QTC_ASSERT(m_writer, return); + QVariantMap data; + data.insert(QLatin1String(DEBUGGER_FILE_VERSION_KEY), 1); + + int count = 0; + foreach (DebuggerItem *item, m_debuggers) { + if (item->isValid()) { + QVariantMap tmp = item->toMap(); + 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. +} + +QList DebuggerItemManager::findDebuggers(const Abi &abi) const +{ + QList result; + foreach (DebuggerItem *item, m_debuggers) + foreach (const Abi targetAbi, item->abis) + if (targetAbi.isCompatibleWith(abi)) + result.append(item); + return result; +} + +DebuggerItem *DebuggerItemManager::debuggerFromId(const QVariant &id) +{ + foreach (DebuggerItem *item, theDebuggerItemManager()->m_debuggers) + if (item->id == id) + return item; + return 0; +} + +QModelIndex DebuggerItemManager::currentIndex() const +{ + QStandardItem *current = m_itemFromDebugger.value(m_currentDebugger); + return current ? current->index() : QModelIndex(); +} + +bool DebuggerItemManager::isLoaded() const +{ + return m_writer; +} + +void DebuggerItemManager::addDebugger() +{ + DebuggerItem item; + item.displayName = tr("New Debugger"); + item.engineType = NoEngineType; + item.isAutoDetected = false; + maybeAddDebugger(item); +} + +void DebuggerItemManager::cloneDebugger() +{ + DebuggerItem item; + if (m_currentDebugger) + item = *m_currentDebugger; + item.displayName = tr("Clone of %1").arg(item.displayName); + item.isAutoDetected = false; + maybeAddDebugger(item); +} + +void DebuggerItemManager::removeDebugger() +{ + if (!m_currentDebugger) + return; + + emit debuggerRemoved(m_currentDebugger); + bool ok = m_debuggers.removeOne(m_currentDebugger); + QTC_ASSERT(ok, return); + QStandardItem *item = m_itemFromDebugger.take(m_currentDebugger); + DebuggerItem *debugger = m_debuggerFromItem.take(item); + QTC_ASSERT(debugger == m_currentDebugger, return); + QTC_ASSERT(item, return); + QTC_ASSERT(debugger, return); + QStandardItem *parent = item->parent(); + QTC_ASSERT(parent, return); + // This will trigger a change of m_currentDebugger via changing the + // view selection. So don't delete m_currentDebugger but debugger. + parent->removeRow(item->row()); + QTC_ASSERT(debugger != m_currentDebugger, return); + delete debugger; +} + +void DebuggerItemManager::setCurrentIndex(const QModelIndex &index) +{ + m_currentDebugger = m_debuggerFromItem.value(itemFromIndex(index)); +} + +QVariant DebuggerItemManager::defaultDebugger(ToolChain *tc) +{ + QTC_ASSERT(tc, return QVariant()); + + DebuggerItem result; + result.isAutoDetected = true; + result.displayName = tr("Auto-detected for Tool Chain %1").arg(tc->displayName()); + + 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; + + + QList compatible = findDebuggers(abi); + if (!compatible.isEmpty()) + return compatible.front()->id; + + return QVariant(); + + /* + // CDB for windows: + if (abi.os() == Abi::WindowsOS && abi.osFlavor() != Abi::WindowsMSysFlavor) { + QPair 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); + */ +} + +// ----------------------------------------------------------------------- +// DebuggerKitConfigWidget +// ----------------------------------------------------------------------- + +DebuggerKitConfigWidget::DebuggerKitConfigWidget(Kit *workingCopy, bool sticky) + : KitConfigWidget(workingCopy, sticky) +{ + DebuggerItemManager *manager = theDebuggerItemManager(); + QTC_CHECK(manager); + + m_comboBox = new QComboBox; + m_comboBox->setEnabled(true); + m_comboBox->setToolTip(toolTip()); + m_comboBox->addItem(tr("None"), QString()); + foreach (DebuggerItem *item, manager->debuggers()) + m_comboBox->addItem(item->displayName, item->id); - connect(m_autoDetectButton, SIGNAL(pressed()), SLOT(autoDetectDebugger())); - connect(m_editButton, SIGNAL(pressed()), SLOT(showDialog())); 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())); + + connect(manager, SIGNAL(debuggerAdded(DebuggerItem*)), + this, SLOT(onDebuggerAdded(DebuggerItem*))); + connect(manager, SIGNAL(debuggerUpdated(DebuggerItem*)), + this, SLOT(onDebuggerUpdated(DebuggerItem*))); + connect(manager, SIGNAL(debuggerRemoved(DebuggerItem*)), + this, SLOT(onDebuggerRemoved(DebuggerItem*))); } QString DebuggerKitConfigWidget::toolTip() const @@ -77,16 +910,6 @@ QString DebuggerKitConfigWidget::toolTip() const return tr("The debugger to use for this kit."); } -QWidget *DebuggerKitConfigWidget::mainWidget() const -{ - return m_main; -} - -QWidget *DebuggerKitConfigWidget::buttonWidget() const -{ - return m_editButton; -} - QString DebuggerKitConfigWidget::displayName() const { return tr("Debugger:"); @@ -94,109 +917,165 @@ QString DebuggerKitConfigWidget::displayName() const void DebuggerKitConfigWidget::makeReadOnly() { - m_editButton->setEnabled(false); - m_autoDetectButton->setEnabled(false); + m_manageButton->setEnabled(false); + m_comboBox->setEnabled(false); } void DebuggerKitConfigWidget::refresh() { - m_label->setText(DebuggerKitInformation::userOutput(DebuggerKitInformation::debuggerItem(m_kit))); + DebuggerItem item = DebuggerKitInformation::debuggerItem(m_kit); + updateComboBox(item.id); } -void DebuggerKitConfigWidget::autoDetectDebugger() +QWidget *DebuggerKitConfigWidget::buttonWidget() const { - DebuggerKitInformation::setDebuggerItem(m_kit, DebuggerKitInformation::autoDetectItem(m_kit)); + return m_manageButton; } -void DebuggerKitConfigWidget::showDialog() +QWidget *DebuggerKitConfigWidget::mainWidget() const { - DebuggerKitConfigDialog dialog(Core::ICore::mainWindow()); - dialog.setWindowTitle(tr("Debugger for \"%1\"").arg(m_kit->displayName())); - dialog.setDebuggerItem(DebuggerKitInformation::debuggerItem(m_kit)); - if (dialog.exec() == QDialog::Accepted) - DebuggerKitInformation::setDebuggerItem(m_kit, dialog.item()); + return m_comboBox; } -// ----------------------------------------------------------------------- -// DebuggerKitConfigDialog: -// ----------------------------------------------------------------------- - -DebuggerKitConfigDialog::DebuggerKitConfigDialog(QWidget *parent) - : QDialog(parent) - , m_comboBox(new QComboBox(this)) - , m_label(new QLabel(this)) - , m_chooser(new Utils::PathChooser(this)) +void DebuggerKitConfigWidget::manageDebuggers() { - setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); - QVBoxLayout *layout = new QVBoxLayout(this); - QFormLayout *formLayout = new QFormLayout; - formLayout->setFieldGrowthPolicy(QFormLayout::AllNonFixedFieldsGrow); + Core::ICore::showOptionsDialog(ProjectExplorer::Constants::PROJECTEXPLORER_SETTINGS_CATEGORY, + ProjectExplorer::Constants::DEBUGGER_SETTINGS_PAGE_ID); +} - m_comboBox->addItem(DebuggerKitInformation::debuggerEngineName(GdbEngineType), QVariant(int(GdbEngineType))); - if (ProjectExplorer::Abi::hostAbi().os() == ProjectExplorer::Abi::WindowsOS) { - m_comboBox->addItem(DebuggerKitInformation::debuggerEngineName(CdbEngineType), QVariant(int(CdbEngineType))); - } else { - m_comboBox->addItem(DebuggerKitInformation::debuggerEngineName(LldbEngineType), QVariant(int(LldbEngineType))); - m_comboBox->addItem(DebuggerKitInformation::debuggerEngineName(LldbLibEngineType), QVariant(int(LldbLibEngineType))); +void DebuggerKitConfigWidget::currentDebuggerChanged(int) +{ + m_kit->setValue(DEBUGGER_INFORMATION, m_comboBox->currentData()); +} + +void DebuggerKitConfigWidget::onDebuggerAdded(DebuggerItem *item) +{ + m_comboBox->setEnabled(true); + QVariant id = currentId(); + m_comboBox->addItem(item->displayName, item->id); + updateComboBox(id); +} + +void DebuggerKitConfigWidget::onDebuggerUpdated(DebuggerItem *item) +{ + m_comboBox->setEnabled(true); + const int pos = indexOf(item); + if (pos < 0) + return; + m_comboBox->setItemText(pos, item->displayName); +} + +void DebuggerKitConfigWidget::onDebuggerRemoved(DebuggerItem *item) +{ + const int pos = indexOf(item); + if (pos <= 0) + return; + QVariant id = currentId(); + m_comboBox->removeItem(pos); + updateComboBox(id); +} + +int DebuggerKitConfigWidget::indexOf(const DebuggerItem *debugger) +{ + QTC_ASSERT(debugger, return -1); + const QVariant id = debugger->id; + for (int i = 0; i < m_comboBox->count(); ++i) { + if (id == m_comboBox->itemData(i)) + return i; } - connect(m_comboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(refreshLabel())); - QLabel *engineTypeLabel = new QLabel(tr("&Engine:")); - engineTypeLabel->setBuddy(m_comboBox); - formLayout->addRow(engineTypeLabel, m_comboBox); - - m_label->setTextInteractionFlags(Qt::TextBrowserInteraction); - m_label->setOpenExternalLinks(true); - formLayout->addRow(m_label); - - QLabel *binaryLabel = new QLabel(tr("&Binary:")); - m_chooser->setExpectedKind(Utils::PathChooser::ExistingCommand); - m_chooser->setMinimumWidth(400); - binaryLabel->setBuddy(m_chooser); - formLayout->addRow(binaryLabel, m_chooser); - layout->addLayout(formLayout); - - QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal, this); - connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept())); - connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); - layout->addWidget(buttonBox); + return -1; } -DebuggerEngineType DebuggerKitConfigDialog::engineType() const +QVariant DebuggerKitConfigWidget::currentId() const { - const int index = m_comboBox->currentIndex(); - return static_cast(m_comboBox->itemData(index).toInt()); + return m_comboBox->itemData(m_comboBox->currentIndex()); } -void DebuggerKitConfigDialog::setEngineType(DebuggerEngineType et) +void DebuggerKitConfigWidget::updateComboBox(const QVariant &id) { - const int size = m_comboBox->count(); - for (int i = 0; i < size; ++i) { - if (m_comboBox->itemData(i).toInt() == et) { + for (int i = 0; i < m_comboBox->count(); ++i) { + if (id == m_comboBox->itemData(i)) { m_comboBox->setCurrentIndex(i); - refreshLabel(); - break; + return; } } + m_comboBox->setCurrentIndex(0); } -Utils::FileName DebuggerKitConfigDialog::fileName() const +// ----------------------------------------------------------------------- +// DebuggerItemConfigWidget +// ----------------------------------------------------------------------- + +class DebuggerItemConfigWidget : public QWidget { - return m_chooser->fileName(); +public: + explicit DebuggerItemConfigWidget(); + void loadItem(DebuggerItem *item); + void saveItem(DebuggerItem *item); +private: + QLineEdit *m_displayNameLineEdit; + QComboBox *m_engineTypeComboBox; + QLabel *m_cdbLabel; + PathChooser *m_binaryChooser; + QLineEdit *m_abis; +}; + +DebuggerItemConfigWidget::DebuggerItemConfigWidget() +{ + m_displayNameLineEdit = new QLineEdit(this); + + m_engineTypeComboBox = new QComboBox(this); + + m_binaryChooser = new PathChooser(this); + m_binaryChooser->setExpectedKind(PathChooser::ExistingCommand); + m_binaryChooser->setMinimumWidth(400); + + m_cdbLabel = new QLabel(this); + m_cdbLabel->setTextInteractionFlags(Qt::TextBrowserInteraction); + m_cdbLabel->setOpenExternalLinks(true); + + m_abis = new QLineEdit(this); + m_abis->setEnabled(false); + + QHBoxLayout *nameLayout = new QHBoxLayout; + nameLayout->addWidget(m_displayNameLineEdit); + nameLayout->addWidget(new QLabel(tr("Type:"))); + nameLayout->addWidget(m_engineTypeComboBox); + + QFormLayout *formLayout = new QFormLayout; + formLayout->setFieldGrowthPolicy(QFormLayout::AllNonFixedFieldsGrow); + formLayout->addRow(new QLabel(tr("Name:")), nameLayout); +// formLayout->addRow(new QLabel(tr("Name:")), m_displayNameLineEdit); +// formLayout->addRow(new QLabel(tr("Type:")), m_engineTypeComboBox); + formLayout->addRow(m_cdbLabel); + formLayout->addRow(new QLabel(tr("Path:")), m_binaryChooser); + formLayout->addRow(new QLabel(tr("Abis:")), m_abis); + + setLayout(formLayout); } -void DebuggerKitConfigDialog::setFileName(const Utils::FileName &fn) +void DebuggerItemConfigWidget::loadItem(DebuggerItem *item) { - m_chooser->setFileName(fn); -} + if (!item) + return; + + m_displayNameLineEdit->setEnabled(!item->isAutoDetected); + m_displayNameLineEdit->setText(item->displayName); + + // This is immutable. + m_engineTypeComboBox->clear(); + m_engineTypeComboBox->addItem(item->engineTypeName()); + m_engineTypeComboBox->setCurrentIndex(0); + //m_engineTypeComboBox->setEnabled(false); + + m_binaryChooser->setEnabled(!item->isAutoDetected); + m_binaryChooser->setFileName(item->command); -void DebuggerKitConfigDialog::refreshLabel() -{ QString text; - const DebuggerEngineType type = engineType(); - switch (type) { - case CdbEngineType: { + QString versionCommand; + if (item->engineType == CdbEngineType) { #ifdef Q_OS_WIN - const bool is64bit = Utils::winIs64BitSystem(); + const bool is64bit = winIs64BitSystem(); #else const bool is64bit = false; #endif @@ -206,22 +1085,190 @@ void DebuggerKitConfigDialog::refreshLabel() "Windows Console Debugger executable" " (%2) here.

"""). arg(QLatin1String(debuggingToolsWikiLinkC), versionString); + versionCommand = QLatin1String("-version"); + } else { + versionCommand = QLatin1String("--version"); } - break; - default: - break; - } - m_label->setText(text); - m_label->setVisible(!text.isEmpty()); - m_chooser->setCommandVersionArguments(type == CdbEngineType ? - QStringList(QLatin1String("-version")) : - QStringList(QLatin1String("--version"))); + + m_cdbLabel->setText(text); + m_cdbLabel->setVisible(!text.isEmpty()); + m_binaryChooser->setCommandVersionArguments(QStringList(versionCommand)); + + m_abis->setText(toList(item->abis).join(QLatin1String(", "))); } -void DebuggerKitConfigDialog::setDebuggerItem(const DebuggerKitInformation::DebuggerItem &item) +void DebuggerItemConfigWidget::saveItem(DebuggerItem *item) { - setEngineType(item.engineType); - setFileName(item.binary); + QTC_ASSERT(item, return); + item->displayName = m_displayNameLineEdit->text(); + item->command = m_binaryChooser->fileName(); + // item->engineType is immutable +} + +// -------------------------------------------------------------------------- +// DebuggerOptionsPage +// -------------------------------------------------------------------------- + +DebuggerOptionsPage::DebuggerOptionsPage() +{ + m_manager = 0; + m_debuggerView = 0; + m_container = 0; + m_addButton = 0; + m_cloneButton = 0; + m_delButton = 0; + + setId(ProjectExplorer::Constants::DEBUGGER_SETTINGS_PAGE_ID); + setDisplayName(tr("Debuggers")); + setCategory(ProjectExplorer::Constants::PROJECTEXPLORER_SETTINGS_CATEGORY); + setDisplayCategory(QCoreApplication::translate("ProjectExplorer", + ProjectExplorer::Constants::PROJECTEXPLORER_SETTINGS_TR_CATEGORY)); + setCategoryIcon(QLatin1String(ProjectExplorer::Constants::PROJECTEXPLORER_SETTINGS_CATEGORY_ICON)); +} + +QWidget *DebuggerOptionsPage::createPage(QWidget *parent) +{ + m_configWidget = new QWidget(parent); + + m_addButton = new QPushButton(tr("Add"), m_configWidget); + m_cloneButton = new QPushButton(tr("Clone"), m_configWidget); + m_delButton = new QPushButton(tr("Remove"), m_configWidget); + + m_container = new DetailsWidget(m_configWidget); + m_container->setState(DetailsWidget::NoSummary); + m_container->setVisible(false); + + m_manager = theDebuggerItemManager(); + + m_debuggerView = new QTreeView(m_configWidget); + m_debuggerView->setModel(m_manager); + m_debuggerView->setUniformRowHeights(true); + m_debuggerView->setSelectionMode(QAbstractItemView::SingleSelection); + m_debuggerView->setSelectionBehavior(QAbstractItemView::SelectRows); + m_debuggerView->expandAll(); + + QHeaderView *header = m_debuggerView->header(); + header->setStretchLastSection(false); + header->setResizeMode(0, QHeaderView::ResizeToContents); + header->setResizeMode(1, QHeaderView::ResizeToContents); + header->setResizeMode(2, QHeaderView::Stretch); + + QVBoxLayout *buttonLayout = new QVBoxLayout(); + buttonLayout->setSpacing(6); + buttonLayout->setContentsMargins(0, 0, 0, 0); + buttonLayout->addWidget(m_addButton); + buttonLayout->addWidget(m_cloneButton); + buttonLayout->addWidget(m_delButton); + buttonLayout->addItem(new QSpacerItem(10, 40, QSizePolicy::Minimum, QSizePolicy::Expanding)); + + QVBoxLayout *verticalLayout = new QVBoxLayout(); + verticalLayout->addWidget(m_debuggerView); + verticalLayout->addWidget(m_container); + + QHBoxLayout *horizontalLayout = new QHBoxLayout(m_configWidget); + horizontalLayout->addLayout(verticalLayout); + horizontalLayout->addLayout(buttonLayout); + + connect(m_debuggerView->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), + this, SLOT(debuggerSelectionChanged())); + + connect(m_addButton, SIGNAL(clicked()), this, SLOT(addDebugger()), Qt::QueuedConnection); + connect(m_cloneButton, SIGNAL(clicked()), this, SLOT(cloneDebugger()), Qt::QueuedConnection); + connect(m_delButton, SIGNAL(clicked()), this, SLOT(removeDebugger()), Qt::QueuedConnection); + + m_searchKeywords = tr("Debuggers"); + + m_itemConfigWidget = new DebuggerItemConfigWidget; + m_container->setWidget(m_itemConfigWidget); + + updateState(); + + return m_configWidget; +} + +void DebuggerOptionsPage::apply() +{ + m_itemConfigWidget->saveItem(m_manager->currentDebugger()); + m_manager->updateCurrentItem(); + debuggerModelChanged(); +} + +void DebuggerOptionsPage::cloneDebugger() +{ + m_manager->cloneDebugger(); + debuggerModelChanged(); +} + +void DebuggerOptionsPage::addDebugger() +{ + m_manager->addDebugger(); + debuggerModelChanged(); +} + +void DebuggerOptionsPage::removeDebugger() +{ + m_manager->removeDebugger(); + debuggerModelChanged(); +} + +void DebuggerOptionsPage::finish() +{ + // Deleted by settingsdialog. + m_configWidget = 0; + + // Children of m_configWidget. + m_container = 0; + m_debuggerView = 0; + m_addButton = 0; + m_cloneButton = 0; + m_delButton = 0; +} + +bool DebuggerOptionsPage::matches(const QString &s) const +{ + return m_searchKeywords.contains(s, Qt::CaseInsensitive); +} + +void DebuggerOptionsPage::debuggerSelectionChanged() +{ + QTC_ASSERT(m_container, return); + + QModelIndex mi = m_debuggerView->currentIndex(); + mi = mi.sibling(mi.row(), 0); + m_manager->setCurrentIndex(mi); + + DebuggerItem *item = m_manager->currentDebugger(); + m_itemConfigWidget->loadItem(item); + m_container->setVisible(item != 0); + updateState(); +} + +void DebuggerOptionsPage::debuggerModelChanged() +{ + QTC_ASSERT(m_container, return); + + DebuggerItem *item = m_manager->currentDebugger(); + m_itemConfigWidget->loadItem(item); + m_container->setVisible(item != 0); + m_debuggerView->setCurrentIndex(m_manager->currentIndex()); + updateState(); +} + +void DebuggerOptionsPage::updateState() +{ + if (!m_cloneButton) + return; + + bool canCopy = false; + bool canDelete = false; + + if (DebuggerItem *item = m_manager->currentDebugger()) { + canCopy = item->isValid() && item->canClone(); + canDelete = !item->isAutoDetected; + canDelete = true; // Do we want to remove auto-detected items? + } + m_cloneButton->setEnabled(canCopy); + m_delButton->setEnabled(canDelete); } } // namespace Internal diff --git a/src/plugins/debugger/debuggerkitconfigwidget.h b/src/plugins/debugger/debuggerkitconfigwidget.h index 9a5fbc9e832..72503393cbd 100644 --- a/src/plugins/debugger/debuggerkitconfigwidget.h +++ b/src/plugins/debugger/debuggerkitconfigwidget.h @@ -30,29 +30,91 @@ #ifndef DEBUGGER_DEBUGGERKITCONFIGWIDGET_H #define DEBUGGER_DEBUGGERKITCONFIGWIDGET_H -#include - #include "debuggerkitinformation.h" +#include +#include +#include +#include +#include +#include +#include + #include +#include +#include QT_BEGIN_NAMESPACE -class QLabel; class QComboBox; +class QLabel; class QPushButton; QT_END_NAMESPACE -namespace ProjectExplorer { class Kit; } -namespace Utils { -class PathChooser; -class FileName; -} - namespace Debugger { - namespace Internal { + +class DebuggerItemConfigWidget; + // ----------------------------------------------------------------------- -// DebuggerKitConfigWidget: +// DebuggerItemManager +// ----------------------------------------------------------------------- + +class DebuggerItemManager : public QStandardItemModel +{ + Q_OBJECT + +public: + DebuggerItemManager(QObject *parent); + ~DebuggerItemManager(); + + QList debuggers() const { return m_debuggers; } + QList findDebuggers(const ProjectExplorer::Abi &abi) const; + DebuggerItem *currentDebugger() const { return m_currentDebugger; } + static DebuggerItem *debuggerFromId(const QVariant &id); + QModelIndex currentIndex() const; + void setCurrentIndex(const QModelIndex &index); + + bool isLoaded() const; + void updateCurrentItem(); + // Returns id. + QVariant defaultDebugger(ProjectExplorer::ToolChain *tc); + // Adds item unless present. Return id of a matching item. + QVariant maybeAddDebugger(const DebuggerItem &item, bool makeCurrent = true); + static void restoreDebuggers(); + +public slots: + void saveDebuggers(); + void autoDetectDebuggers(); + void addDebugger(); + void cloneDebugger(); + void removeDebugger(); + +signals: + void debuggerAdded(DebuggerItem *item); + void debuggerRemoved(DebuggerItem *item); + void debuggerUpdated(DebuggerItem *item); + +private: + friend class DebuggerOptionsPage; + QVariant headerData(int section, Qt::Orientation orientation, int role) const; + + void autoDetectCdbDebugger(); + + QMap m_abiToDebugger; + Utils::PersistentSettingsWriter *m_writer; + QList m_debuggers; + DebuggerItem *m_currentDebugger; + + QStandardItem *m_autoRoot; + QStandardItem *m_manualRoot; + QStringList added; + QStringList removed; + QHash m_itemFromDebugger; + QHash m_debuggerFromItem; +}; + +// ----------------------------------------------------------------------- +// DebuggerKitConfigWidget // ----------------------------------------------------------------------- class DebuggerKitConfigWidget : public ProjectExplorer::KitConfigWidget @@ -64,48 +126,63 @@ public: QString displayName() const; QString toolTip() const; - void makeReadOnly(); - void refresh(); - QWidget *buttonWidget() const; QWidget *mainWidget() const; private slots: - void autoDetectDebugger(); - void showDialog(); + void manageDebuggers(); + void currentDebuggerChanged(int idx); + void onDebuggerAdded(DebuggerItem *); + void onDebuggerUpdated(DebuggerItem *); + void onDebuggerRemoved(DebuggerItem *); private: - QWidget *m_main; - QLabel *m_label; - QPushButton *m_autoDetectButton; - QPushButton *m_editButton; + int indexOf(const DebuggerItem *debugger); + QVariant currentId() const; + void updateComboBox(const QVariant &id); + + bool m_isReadOnly; + QComboBox *m_comboBox; + QPushButton *m_manageButton; }; -class DebuggerKitConfigDialog : public QDialog +// -------------------------------------------------------------------------- +// DebuggerOptionsPage +// -------------------------------------------------------------------------- + +class DebuggerOptionsPage : public Core::IOptionsPage { Q_OBJECT -public: - explicit DebuggerKitConfigDialog(QWidget *parent = 0); - void setDebuggerItem(const DebuggerKitInformation::DebuggerItem &item); - DebuggerKitInformation::DebuggerItem item() const - { return DebuggerKitInformation::DebuggerItem(engineType(), fileName()); } +public: + DebuggerOptionsPage(); + + QWidget *createPage(QWidget *parent); + void apply(); + void finish(); + bool matches(const QString &) const; private slots: - void refreshLabel(); + void debuggerSelectionChanged(); + void debuggerModelChanged(); + void updateState(); + void cloneDebugger(); + void addDebugger(); + void removeDebugger(); private: - DebuggerEngineType engineType() const; - void setEngineType(DebuggerEngineType et); + QWidget *m_configWidget; + QString m_searchKeywords; - Utils::FileName fileName() const; - void setFileName(const Utils::FileName &fn); - - QComboBox *m_comboBox; - QLabel *m_label; - Utils::PathChooser *m_chooser; + DebuggerItemManager *m_manager; + DebuggerItemConfigWidget *m_itemConfigWidget; + QTreeView *m_debuggerView; + Utils::DetailsWidget *m_container; + QPushButton *m_addButton; + QPushButton *m_cloneButton; + QPushButton *m_delButton; }; } // namespace Internal diff --git a/src/plugins/debugger/debuggerkitinformation.cpp b/src/plugins/debugger/debuggerkitinformation.cpp index 9cba3a2a7bd..942ed3d892c 100644 --- a/src/plugins/debugger/debuggerkitinformation.cpp +++ b/src/plugins/debugger/debuggerkitinformation.cpp @@ -26,358 +26,3 @@ ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ****************************************************************************/ - -#include "debuggerkitinformation.h" - -#include "debuggerkitconfigwidget.h" - -#include -#include -#include -#include - -#include -#include - -#include - -using namespace ProjectExplorer; -using namespace Utils; - -// -------------------------------------------------------------------------- -// Helpers: -// -------------------------------------------------------------------------- - -static QPair autoDetectCdbDebugger() -{ - QPair result; - QList 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\" - 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.push_back(FileName::fromString(cdb32.absoluteFilePath())); - const QFileInfo cdb64(path + QLatin1String("/Debuggers/x64/cdb.exe")); - if (cdb64.isExecutable()) - cdbs.push_back(FileName::fromString(cdb64.absoluteFilePath())); - } // for Kits - } // can cd to "Windows Kits" - } // "Windows Kits" exists - - // 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) { - QList abis = Abi::abisOfBinary(cdb); - if (abis.isEmpty()) - continue; - 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; - - return result; -} - -namespace Debugger { - -static DebuggerEngineType engineTypeFromBinary(const QString &binary) -{ - if (binary.contains(QLatin1String("cdb"), Qt::CaseInsensitive)) - return CdbEngineType; - if (binary.contains(QLatin1String("lldb"), Qt::CaseInsensitive)) - return LldbEngineType; - return GdbEngineType; -} - -// -------------------------------------------------------------------------- -// DebuggerKitInformation: -// -------------------------------------------------------------------------- - -static const char DEBUGGER_INFORMATION[] = "Debugger.Information"; - -DebuggerKitInformation::DebuggerItem::DebuggerItem() - : engineType(NoEngineType) -{ -} - -DebuggerKitInformation::DebuggerItem::DebuggerItem(DebuggerEngineType et, const Utils::FileName &fn) - : engineType(et) - , binary(fn) -{ -} - -DebuggerKitInformation::DebuggerKitInformation() -{ - setObjectName(QLatin1String("DebuggerKitInformation")); -} - -Core::Id DebuggerKitInformation::dataId() const -{ - static Core::Id id = Core::Id(DEBUGGER_INFORMATION); - return id; -} - -unsigned int DebuggerKitInformation::priority() const -{ - return 28000; -} - -DebuggerKitInformation::DebuggerItem DebuggerKitInformation::autoDetectItem(const Kit *k) -{ - if (DebuggerKitInformation::isValidDebugger(k)) - return DebuggerKitInformation::debuggerItem(k); - - DebuggerItem result; - const ToolChain *tc = ToolChainKitInformation::toolChain(k); - Abi abi = Abi::hostAbi(); - if (tc) - abi = tc->targetAbi(); - - // CDB for windows: - if (abi.os() == Abi::WindowsOS && abi.osFlavor() != Abi::WindowsMSysFlavor) { - QPair cdbs = autoDetectCdbDebugger(); - result.binary = Utils::FileName::fromString(abi.wordWidth() == 32 ? cdbs.first : cdbs.second); - result.engineType = CdbEngineType; - return result; - } - - // 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.binary = Utils::FileName::fromString(path); - result.engineType = engineTypeFromBinary(path); - return result; - } - } - - // 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.binary = Utils::FileName::fromString(env.searchInPath(gdb.isEmpty() ? systemGdb : gdb)); - return result; -} - -void DebuggerKitInformation::setup(Kit *k) -{ - QTC_ASSERT(ToolChainManager::instance()->isLoaded(), return); - setDebuggerItem(k, autoDetectItem(k)); -} - -// 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 -}; - -static unsigned debuggerConfigurationErrors(const ProjectExplorer::Kit *k) -{ - unsigned result = 0; - const DebuggerKitInformation::DebuggerItem item = DebuggerKitInformation::debuggerItem(k); - if (item.engineType == NoEngineType || item.binary.isEmpty()) - return NoDebugger; - - const QFileInfo fi = item.binary.toFileInfo(); - if (!fi.exists() || fi.isDir()) - result |= DebuggerNotFound; - else if (!fi.isExecutable()) - result |= DebuggerNotExecutable; - - if (!fi.exists() || fi.isDir()) - // We need an absolute path to be able to locate Python on Windows. - if (item.engineType == GdbEngineType) - if (const ToolChain *tc = ToolChainKitInformation::toolChain(k)) - if (tc->targetAbi().os() == Abi::WindowsOS && !fi.isAbsolute()) - result |= DebuggerNeedsAbsolutePath; - return result; -} - -bool DebuggerKitInformation::isValidDebugger(const ProjectExplorer::Kit *k) -{ - return debuggerConfigurationErrors(k) == 0; -} - -QList DebuggerKitInformation::validateDebugger(const ProjectExplorer::Kit *k) -{ - const unsigned errors = debuggerConfigurationErrors(k); - const Core::Id id(ProjectExplorer::Constants::TASK_CATEGORY_BUILDSYSTEM); - QList result; - if (errors & NoDebugger) - result << Task(Task::Warning, tr("No debugger set up."), FileName(), -1, id); - - if (errors & DebuggerNotFound) { - const QString path = DebuggerKitInformation::debuggerCommand(k).toUserOutput(); - result << Task(Task::Error, tr("Debugger '%1' not found.").arg(path), - FileName(), -1, id); - } - if (errors & DebuggerNotExecutable) { - const QString path = DebuggerKitInformation::debuggerCommand(k).toUserOutput(); - result << Task(Task::Error, tr("Debugger '%1' not executable.").arg(path), FileName(), -1, id); - } - if (errors & DebuggerNeedsAbsolutePath) { - const QString path = DebuggerKitInformation::debuggerCommand(k).toUserOutput(); - 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 -{ - return new Internal::DebuggerKitConfigWidget(k, isSticky(k)); -} - -QString DebuggerKitInformation::userOutput(const DebuggerItem &item) -{ - const QString binary = item.binary.toUserOutput(); - const QString name = debuggerEngineName(item.engineType); - return binary.isEmpty() ? tr("%1 ").arg(name) : tr("%1 using \"%2\"").arg(name, binary); -} - -KitInformation::ItemList DebuggerKitInformation::toUserOutput(const Kit *k) const -{ - return ItemList() << qMakePair(tr("Debugger"), DebuggerKitInformation::userOutput(DebuggerKitInformation::debuggerItem(k))); -} - -static const char engineTypeKeyC[] = "EngineType"; -static const char binaryKeyC[] = "Binary"; - -DebuggerKitInformation::DebuggerItem DebuggerKitInformation::variantToItem(const QVariant &v) -{ - DebuggerItem result; - if (v.isNull()) - return result; - if (v.type() == QVariant::String) { // Convert legacy config items, remove later. - const QString binary = v.toString(); - result.binary = Utils::FileName::fromString(binary); - result.engineType = engineTypeFromBinary(binary); - return result; - } - QTC_ASSERT(v.type() == QVariant::Map, return result); - const QVariantMap vmap = v.toMap(); - result.engineType = static_cast(vmap.value(QLatin1String(engineTypeKeyC)).toInt()); - QString binary = vmap.value(QLatin1String(binaryKeyC)).toString(); - // Check for special 'auto' entry for binary written by the sdktool during - // installation. Try to autodetect. - if (binary == QLatin1String("auto")) { - binary.clear(); - switch (result.engineType) { - case Debugger::GdbEngineType: // Auto-detect system gdb on Unix - if (Abi::hostAbi().os() != Abi::WindowsOS) - binary = Environment::systemEnvironment().searchInPath(QLatin1String("gdb")); - break; - case Debugger::CdbEngineType: { // Auto-detect system CDB on Windows. - const QPair cdbs = autoDetectCdbDebugger(); - binary = cdbs.second.isEmpty() ? cdbs.first : cdbs.second; - } - break; - default: - break; - } - } - result.binary = Utils::FileName::fromUserInput(binary); - return result; -} - -QVariant DebuggerKitInformation::itemToVariant(const DebuggerItem &i) -{ - QVariantMap vmap; - vmap.insert(QLatin1String(binaryKeyC), QVariant(i.binary.toString())); - vmap.insert(QLatin1String(engineTypeKeyC), QVariant(int(i.engineType))); - return QVariant(vmap); -} - -DebuggerKitInformation::DebuggerItem DebuggerKitInformation::debuggerItem(const ProjectExplorer::Kit *k) -{ - return k ? - DebuggerKitInformation::variantToItem(k->value(Core::Id(DEBUGGER_INFORMATION))) : - DebuggerItem(); -} - -void DebuggerKitInformation::setDebuggerItem(ProjectExplorer::Kit *k, const DebuggerItem &item) -{ - QTC_ASSERT(k, return); - k->setValue(Core::Id(DEBUGGER_INFORMATION), itemToVariant(item)); -} - -void DebuggerKitInformation::setDebuggerCommand(ProjectExplorer::Kit *k, const FileName &command) -{ - setDebuggerItem(k, DebuggerItem(engineType(k), command)); -} - -void DebuggerKitInformation::setEngineType(ProjectExplorer::Kit *k, DebuggerEngineType type) -{ - setDebuggerItem(k, DebuggerItem(type, debuggerCommand(k))); -} - -QString DebuggerKitInformation::debuggerEngineName(DebuggerEngineType t) -{ - switch (t) { - case Debugger::GdbEngineType: - return tr("GDB Engine"); - case Debugger::CdbEngineType: - return tr("CDB Engine"); - case Debugger::LldbEngineType: - return tr("LLDB Engine"); - default: - break; - } - return QString(); -} - -void DebuggerKitInformation::makeSticky(Kit *k) -{ - k->makeSticky(DEBUGGER_INFORMATION); -} - -} // namespace Debugger diff --git a/src/plugins/debugger/debuggerkitinformation.h b/src/plugins/debugger/debuggerkitinformation.h index 9131344bf57..8e46c2d3610 100644 --- a/src/plugins/debugger/debuggerkitinformation.h +++ b/src/plugins/debugger/debuggerkitinformation.h @@ -33,34 +33,47 @@ #include "debugger_global.h" #include "debuggerconstants.h" +#include #include namespace Debugger { +class DEBUGGER_EXPORT DebuggerItem +{ +public: + DebuggerItem(); + + bool looksLike(const DebuggerItem &rhs) const; + bool canClone() const { return true; } + bool isValid() const { return true; } + QString engineTypeName() const; + QString userOutput() const; + + QVariantMap toMap() const; + void fromMap(const QVariantMap &data); + void reinitializeFromFile(); + +public: + QVariant id; + QString displayName; + DebuggerEngineType engineType; + Utils::FileName command; + bool isAutoDetected; + QList abis; +}; + class DEBUGGER_EXPORT DebuggerKitInformation : public ProjectExplorer::KitInformation { Q_OBJECT public: - class DEBUGGER_EXPORT DebuggerItem { - public: - DebuggerItem(); - DebuggerItem(DebuggerEngineType engineType, const Utils::FileName &fn); - bool equals(const DebuggerItem &rhs) const { return engineType == rhs.engineType && binary == rhs.binary; } - - DebuggerEngineType engineType; - Utils::FileName binary; - }; - DebuggerKitInformation(); Core::Id dataId() const; unsigned int priority() const; // the higher the closer to the top. - static DebuggerItem autoDetectItem(const ProjectExplorer::Kit *k); - QVariant defaultValue(ProjectExplorer::Kit *k) const - { return DebuggerKitInformation::itemToVariant(DebuggerKitInformation::autoDetectItem(k)); } + QVariant defaultValue(ProjectExplorer::Kit *k) const; QList validate(const ProjectExplorer::Kit *k) const { return DebuggerKitInformation::validateDebugger(k); } @@ -73,34 +86,26 @@ public: ProjectExplorer::KitConfigWidget *createConfigWidget(ProjectExplorer::Kit *k) const; ItemList toUserOutput(const ProjectExplorer::Kit *k) const; - static QString userOutput(const DebuggerItem &item); static DebuggerItem debuggerItem(const ProjectExplorer::Kit *k); - static void setDebuggerItem(ProjectExplorer::Kit *k, const DebuggerItem &item); + static void setDebuggerItem(ProjectExplorer::Kit *k, + DebuggerEngineType type, const Utils::FileName &command); static Utils::FileName debuggerCommand(const ProjectExplorer::Kit *k) - { return debuggerItem(k).binary; } + { return debuggerItem(k).command; } static void setDebuggerCommand(ProjectExplorer::Kit *k, const Utils::FileName &command); static DebuggerEngineType engineType(const ProjectExplorer::Kit *k) { return debuggerItem(k).engineType; } - static void setEngineType(ProjectExplorer::Kit *k, DebuggerEngineType type); - - static QString debuggerEngineName(DebuggerEngineType t); - static void makeSticky(ProjectExplorer::Kit *k); - -private: - static DebuggerItem variantToItem(const QVariant &v); - static QVariant itemToVariant(const DebuggerItem &i); }; -inline bool operator==(const DebuggerKitInformation::DebuggerItem &i1, const DebuggerKitInformation::DebuggerItem &i2) - { return i1.equals(i2); } -inline bool operator!=(const DebuggerKitInformation::DebuggerItem &i1, const DebuggerKitInformation::DebuggerItem &i2) - { return !i1.equals(i2); } +inline bool operator==(const DebuggerItem &i1, const DebuggerItem &i2) + { return i1.looksLike(i2); } +inline bool operator!=(const DebuggerItem &i1, const DebuggerItem &i2) + { return !i1.looksLike(i2); } } // namespace Debugger diff --git a/src/plugins/debugger/debuggerplugin.cpp b/src/plugins/debugger/debuggerplugin.cpp index d3e368dfb6c..fec22e0a445 100644 --- a/src/plugins/debugger/debuggerplugin.cpp +++ b/src/plugins/debugger/debuggerplugin.cpp @@ -33,6 +33,7 @@ #include "debuggeractions.h" #include "debuggerinternalconstants.h" #include "debuggercore.h" +#include "debuggerkitconfigwidget.h" #include "debuggerdialogs.h" #include "debuggerengine.h" #include "debuggermainwindow.h" @@ -1626,7 +1627,7 @@ void DebuggerPluginPrivate::attachCore() DebuggerStartParameters sp; QString display = dlg.useLocalCoreFile() ? dlg.localCoreFile() : dlg.remoteCoreFile(); QTC_ASSERT(fillParameters(&sp, dlg.kit()), return); - DebuggerKitInformation::DebuggerItem info = DebuggerKitInformation::debuggerItem(dlg.kit()); + DebuggerItem info = DebuggerKitInformation::debuggerItem(dlg.kit()); sp.masterEngineType = info.engineType; sp.executable = dlg.localExecutableFile(); sp.coreFile = dlg.localCoreFile(); @@ -3182,6 +3183,7 @@ void DebuggerPluginPrivate::extensionsInitialized() foreach (IOptionsPage *op, engineOptionPages) m_plugin->addAutoReleasedObject(op); m_plugin->addAutoReleasedObject(new LocalsAndExpressionsOptionsPage); + m_plugin->addAutoReleasedObject(new DebuggerOptionsPage); connect(ModeManager::instance(), SIGNAL(currentModeChanged(Core::IMode*)), SLOT(onModeChanged(Core::IMode*))); @@ -3436,6 +3438,8 @@ bool DebuggerPlugin::initialize(const QStringList &arguments, QString *errorMess mstart->addSeparator(globalcontext, Constants::G_GENERAL); mstart->addSeparator(globalcontext, Constants::G_SPECIAL); + DebuggerItemManager::restoreDebuggers(); + KitManager::registerKitInformation(new DebuggerKitInformation); return theDebuggerCore->initialize(arguments, errorMessage); diff --git a/src/plugins/projectexplorer/projectexplorerconstants.h b/src/plugins/projectexplorer/projectexplorerconstants.h index 1f3f5a5dcef..5cfd9f004aa 100644 --- a/src/plugins/projectexplorer/projectexplorerconstants.h +++ b/src/plugins/projectexplorer/projectexplorerconstants.h @@ -179,6 +179,7 @@ const char PROJECTEXPLORER_SETTINGS_TR_CATEGORY[] = QT_TRANSLATE_NOOP("ProjectEx const char PROJECTEXPLORER_SETTINGS_CATEGORY_ICON[] = ":/core/images/category_buildrun.png"; const char PROJECTEXPLORER_SETTINGS_ID[] = "A.ProjectExplorer.ProjectExplorer"; const char TOOLCHAIN_SETTINGS_PAGE_ID[] = "M.ProjectExplorer.ToolChainOptions"; +const char DEBUGGER_SETTINGS_PAGE_ID[] = "N.ProjectExplorer.DebuggerOptions"; const char KITS_SETTINGS_PAGE_ID[] = "D.ProjectExplorer.KitsOptions"; // Device settings page