diff options
author | Tobias Hunger <tobias.hunger@digia.com> | 2013-10-25 13:47:08 +0200 |
---|---|---|
committer | hjk <hjk121@nokiamail.com> | 2013-10-28 17:50:49 +0100 |
commit | ab8999832a7ce3dbb5bd6d4a7e9b6c5bc1f322c2 (patch) | |
tree | 6e1e1e09c6efb57546443e1a08c34004f20b9ab6 /src/plugins | |
parent | 3a24b2d33239a4666b40c4f54e497f31e283e769 (diff) | |
download | qt-creator-ab8999832a7ce3dbb5bd6d4a7e9b6c5bc1f322c2.tar.gz |
Debugger: Move classes into their own files
Change-Id: I89853ffb4192b0da1f34d471e250d4ec32daa3da
Reviewed-by: hjk <hjk121@nokiamail.com>
Diffstat (limited to 'src/plugins')
-rw-r--r-- | src/plugins/android/androidconfigurations.cpp | 1 | ||||
-rw-r--r-- | src/plugins/debugger/debugger.pro | 8 | ||||
-rw-r--r-- | src/plugins/debugger/debugger.qbs | 4 | ||||
-rw-r--r-- | src/plugins/debugger/debuggeritem.cpp | 337 | ||||
-rw-r--r-- | src/plugins/debugger/debuggeritem.h | 96 | ||||
-rw-r--r-- | src/plugins/debugger/debuggeritemmanager.cpp | 408 | ||||
-rw-r--r-- | src/plugins/debugger/debuggeritemmanager.h | 92 | ||||
-rw-r--r-- | src/plugins/debugger/debuggeritemmodel.cpp | 198 | ||||
-rw-r--r-- | src/plugins/debugger/debuggeritemmodel.h | 90 | ||||
-rw-r--r-- | src/plugins/debugger/debuggerkitconfigwidget.cpp | 1095 | ||||
-rw-r--r-- | src/plugins/debugger/debuggerkitconfigwidget.h | 85 | ||||
-rw-r--r-- | src/plugins/debugger/debuggerkitinformation.cpp | 476 | ||||
-rw-r--r-- | src/plugins/debugger/debuggerkitinformation.h | 97 | ||||
-rw-r--r-- | src/plugins/debugger/debuggeroptionspage.cpp | 350 | ||||
-rw-r--r-- | src/plugins/debugger/debuggeroptionspage.h | 90 | ||||
-rw-r--r-- | src/plugins/debugger/debuggerplugin.cpp | 2 | ||||
-rw-r--r-- | src/plugins/ios/iosconfigurations.cpp | 1 | ||||
-rw-r--r-- | src/plugins/qnx/blackberryconfiguration.cpp | 1 |
18 files changed, 1919 insertions, 1512 deletions
diff --git a/src/plugins/android/androidconfigurations.cpp b/src/plugins/android/androidconfigurations.cpp index 80d1c9451a..ee39135f5d 100644 --- a/src/plugins/android/androidconfigurations.cpp +++ b/src/plugins/android/androidconfigurations.cpp @@ -44,6 +44,7 @@ #include <projectexplorer/devicesupport/devicemanager.h> #include <projectexplorer/toolchainmanager.h> #include <projectexplorer/session.h> +#include <debugger/debuggeritemmanager.h> #include <debugger/debuggerkitinformation.h> #include <qtsupport/baseqtversion.h> #include <qtsupport/qtkitinformation.h> diff --git a/src/plugins/debugger/debugger.pro b/src/plugins/debugger/debugger.pro index ce31216535..7798cf0bfe 100644 --- a/src/plugins/debugger/debugger.pro +++ b/src/plugins/debugger/debugger.pro @@ -24,9 +24,13 @@ HEADERS += \ debuggercore.h \ debuggerconstants.h \ debuggerinternalconstants.h \ + debuggeritem.h \ + debuggeritemmanager.h \ + debuggeritemmodel.h \ debuggerdialogs.h \ debuggerengine.h \ debuggermainwindow.h \ + debuggeroptionspage.h \ debuggerplugin.h \ debuggerprotocol.h \ debuggerrunconfigurationaspect.h \ @@ -80,12 +84,16 @@ SOURCES += \ debuggeractions.cpp \ debuggerdialogs.cpp \ debuggerengine.cpp \ + debuggeritem.cpp \ + debuggeritemmanager.cpp \ + debuggeritemmodel.cpp \ debuggermainwindow.cpp \ debuggerplugin.cpp \ debuggerprotocol.cpp \ debuggerrunconfigurationaspect.cpp \ debuggerrunner.cpp \ debuggerstreamops.cpp \ + debuggeroptionspage.cpp \ debuggerkitconfigwidget.cpp \ debuggerkitinformation.cpp \ disassembleragent.cpp \ diff --git a/src/plugins/debugger/debugger.qbs b/src/plugins/debugger/debugger.qbs index 9032e84660..709dc128c3 100644 --- a/src/plugins/debugger/debugger.qbs +++ b/src/plugins/debugger/debugger.qbs @@ -40,9 +40,13 @@ QtcPlugin { "debuggerdialogs.cpp", "debuggerdialogs.h", "debuggerengine.cpp", "debuggerengine.h", "debuggerinternalconstants.h", + "debuggeritem.cpp", "debuggeritem.h", + "debuggeritemmanager.cpp", "debuggeritemmanager.h", + "debuggeritemmodel.cpp", "debuggeritemmodel.h", "debuggerkitconfigwidget.cpp", "debuggerkitconfigwidget.h", "debuggerkitinformation.cpp", "debuggerkitinformation.h", "debuggermainwindow.cpp", "debuggermainwindow.h", + "debuggeroptionspage.cpp", "debuggeroptionspage.h", "debuggerplugin.cpp", "debuggerplugin.h", "debuggerprotocol.cpp", "debuggerprotocol.h", "debuggerruncontrolfactory.h", diff --git a/src/plugins/debugger/debuggeritem.cpp b/src/plugins/debugger/debuggeritem.cpp new file mode 100644 index 0000000000..9ac30bbb7d --- /dev/null +++ b/src/plugins/debugger/debuggeritem.cpp @@ -0,0 +1,337 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of Qt Creator. +** +** 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. +** +** GNU Lesser General Public License Usage +** 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 +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#include "debuggerkitinformation.h" +#include "debuggerkitconfigwidget.h" +#include "debuggeroptionspage.h" + +#include <projectexplorer/abi.h> +#include <utils/fileutils.h> + +#include <QProcess> +#include <QUuid> + +using namespace Debugger::Internal; +using namespace ProjectExplorer; +using namespace Utils; + +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"; + +namespace Debugger { + +// -------------------------------------------------------------------------- +// DebuggerItem +// -------------------------------------------------------------------------- + +DebuggerItem::DebuggerItem() +{ + m_id = QUuid::createUuid().toString(); + m_engineType = NoEngineType; + m_isAutoDetected = false; +} + +DebuggerItem::DebuggerItem(const QVariantMap &data) +{ + m_command = FileName::fromUserInput(data.value(QLatin1String(DEBUGGER_INFORMATION_COMMAND)).toString()); + m_id = data.value(QLatin1String(DEBUGGER_INFORMATION_ID)).toString(); + m_displayName = data.value(QLatin1String(DEBUGGER_INFORMATION_DISPLAYNAME)).toString(); + m_isAutoDetected = data.value(QLatin1String(DEBUGGER_INFORMATION_AUTODETECTED), false).toBool(); + m_engineType = DebuggerEngineType(data.value(QLatin1String(DEBUGGER_INFORMATION_ENGINETYPE), + static_cast<int>(NoEngineType)).toInt()); + + foreach (const QString &a, data.value(QLatin1String(DEBUGGER_INFORMATION_ABIS)).toStringList()) { + Abi abi(a); + if (abi.isValid()) + m_abis.append(abi); + } +} + +void DebuggerItem::reinitializeFromFile() +{ + QProcess proc; + proc.start(m_command.toString(), QStringList() << QLatin1String("--version")); + proc.waitForStarted(); + proc.waitForFinished(); + QByteArray ba = proc.readAll(); + if (ba.contains("gdb")) { + m_engineType = GdbEngineType; + const char needle[] = "This GDB was configured as \""; + // E.g. "--host=i686-pc-linux-gnu --target=arm-unknown-nto-qnx6.5.0". + // or "i686-linux-gnu" + int pos1 = ba.indexOf(needle); + if (pos1 != -1) { + pos1 += int(sizeof(needle)); + int pos2 = ba.indexOf('"', pos1 + 1); + QByteArray target = ba.mid(pos1, pos2 - pos1); + int pos3 = target.indexOf("--target="); + if (pos3 >= 0) + target = target.mid(pos3 + 9); + m_abis.append(Abi::abiFromTargetTriplet(QString::fromLatin1(target))); + } else { + // Fallback. + m_abis = Abi::abisOfBinary(m_command); // FIXME: Wrong. + } + return; + } + if (ba.contains("lldb") || ba.startsWith("LLDB")) { + m_engineType = LldbEngineType; + m_abis = Abi::abisOfBinary(m_command); + return; + } + if (ba.startsWith("Python")) { + m_engineType = PdbEngineType; + return; + } + m_engineType = NoEngineType; +} + +QString DebuggerItem::engineTypeName() const +{ + switch (m_engineType) { + case Debugger::NoEngineType: + return DebuggerOptionsPage::tr("Not recognized"); + case Debugger::GdbEngineType: + return QLatin1String("GDB"); + case Debugger::CdbEngineType: + return QLatin1String("CDB"); + case Debugger::LldbEngineType: + return QLatin1String("LLDB"); + default: + return QString(); + } +} + +QStringList DebuggerItem::abiNames() const +{ + QStringList list; + foreach (const Abi &abi, m_abis) + list.append(abi.toString()); + return list; +} + +QVariantMap DebuggerItem::toMap() const +{ + QVariantMap data; + data.insert(QLatin1String(DEBUGGER_INFORMATION_DISPLAYNAME), m_displayName); + data.insert(QLatin1String(DEBUGGER_INFORMATION_ID), m_id); + data.insert(QLatin1String(DEBUGGER_INFORMATION_COMMAND), m_command.toUserOutput()); + data.insert(QLatin1String(DEBUGGER_INFORMATION_ENGINETYPE), int(m_engineType)); + data.insert(QLatin1String(DEBUGGER_INFORMATION_AUTODETECTED), m_isAutoDetected); + data.insert(QLatin1String(DEBUGGER_INFORMATION_ABIS), abiNames()); + return data; +} + +void DebuggerItem::setDisplayName(const QString &displayName) +{ + m_displayName = displayName; +} + +void DebuggerItem::setEngineType(const DebuggerEngineType &engineType) +{ + m_engineType = engineType; +} + +void DebuggerItem::setCommand(const Utils::FileName &command) +{ + m_command = command; +} + +void DebuggerItem::setAutoDetected(bool isAutoDetected) +{ + m_isAutoDetected = isAutoDetected; +} + +void DebuggerItem::setAbis(const QList<ProjectExplorer::Abi> &abis) +{ + m_abis = abis; +} + +void DebuggerItem::setAbi(const Abi &abi) +{ + m_abis.clear(); + m_abis.append(abi); +} + +static DebuggerItem::MatchLevel matchSingle(const Abi &debuggerAbi, const Abi &targetAbi) +{ + if (debuggerAbi.architecture() != Abi::UnknownArchitecture + && debuggerAbi.architecture() != targetAbi.architecture()) + return DebuggerItem::DoesNotMatch; + + if (debuggerAbi.os() != Abi::UnknownOS + && debuggerAbi.os() != targetAbi.os()) + return DebuggerItem::DoesNotMatch; + + if (debuggerAbi.binaryFormat() != Abi::UnknownFormat + && debuggerAbi.binaryFormat() != targetAbi.binaryFormat()) + return DebuggerItem::DoesNotMatch; + + if (debuggerAbi.os() == Abi::WindowsOS) { + if (debuggerAbi.osFlavor() == Abi::WindowsMSysFlavor && targetAbi.osFlavor() != Abi::WindowsMSysFlavor) + return DebuggerItem::DoesNotMatch; + if (debuggerAbi.osFlavor() != Abi::WindowsMSysFlavor && targetAbi.osFlavor() == Abi::WindowsMSysFlavor) + return DebuggerItem::DoesNotMatch; + } + + if (debuggerAbi.wordWidth() == 64 && targetAbi.wordWidth() == 32) + return DebuggerItem::MatchesSomewhat; + if (debuggerAbi.wordWidth() != 0 && debuggerAbi.wordWidth() != targetAbi.wordWidth()) + return DebuggerItem::DoesNotMatch; + + return DebuggerItem::MatchesPerfectly; +} + +DebuggerItem::MatchLevel DebuggerItem::matchTarget(const Abi &targetAbi) const +{ + MatchLevel bestMatch = DoesNotMatch; + foreach (const Abi &debuggerAbi, m_abis) { + MatchLevel currentMatch = matchSingle(debuggerAbi, targetAbi); + if (currentMatch > bestMatch) + bestMatch = currentMatch; + } + return bestMatch; +} + +bool Debugger::DebuggerItem::isValid() const +{ + return m_engineType != NoEngineType; +} + +} // namespace Debugger; + +#ifdef WITH_TESTS + +# include <QTest> +# include "debuggerplugin.h" + +void Debugger::DebuggerPlugin::testDebuggerMatching_data() +{ + QTest::addColumn<QStringList>("debugger"); + QTest::addColumn<QString>("target"); + QTest::addColumn<int>("result"); + + QTest::newRow("Invalid data") + << QStringList() + << QString() + << int(DebuggerItem::DoesNotMatch); + QTest::newRow("Invalid debugger") + << QStringList() + << QString::fromLatin1("x86-linux-generic-elf-32bit") + << int(DebuggerItem::DoesNotMatch); + QTest::newRow("Invalid target") + << (QStringList() << QLatin1String("x86-linux-generic-elf-32bit")) + << QString() + << int(DebuggerItem::DoesNotMatch); + + QTest::newRow("Fuzzy match 1") + << (QStringList() << QLatin1String("unknown-unknown-unknown-unknown-0bit")) + << QString::fromLatin1("x86-linux-generic-elf-32bit") + << int(DebuggerItem::MatchesPerfectly); // Is this the expected behavior? + QTest::newRow("Fuzzy match 2") + << (QStringList() << QLatin1String("unknown-unknown-unknown-unknown-0bit")) + << QString::fromLatin1("arm-windows-msys-pe-64bit") + << int(DebuggerItem::MatchesPerfectly); // Is this the expected behavior? + + QTest::newRow("Architecture mismatch") + << (QStringList() << QLatin1String("x86-linux-generic-elf-32bit")) + << QString::fromLatin1("arm-linux-generic-elf-32bit") + << int(DebuggerItem::DoesNotMatch); + QTest::newRow("OS mismatch") + << (QStringList() << QLatin1String("x86-linux-generic-elf-32bit")) + << QString::fromLatin1("x86-macosx-generic-elf-32bit") + << int(DebuggerItem::DoesNotMatch); + QTest::newRow("Format mismatch") + << (QStringList() << QLatin1String("x86-linux-generic-elf-32bit")) + << QString::fromLatin1("x86-linux-generic-pe-32bit") + << int(DebuggerItem::DoesNotMatch); + + QTest::newRow("Linux perfect match") + << (QStringList() << QLatin1String("x86-linux-generic-elf-32bit")) + << QString::fromLatin1("x86-linux-generic-elf-32bit") + << int(DebuggerItem::MatchesPerfectly); + QTest::newRow("Linux match") + << (QStringList() << QLatin1String("x86-linux-generic-elf-64bit")) + << QString::fromLatin1("x86-linux-generic-elf-32bit") + << int(DebuggerItem::MatchesSomewhat); + + QTest::newRow("Windows perfect match 1") + << (QStringList() << QLatin1String("x86-windows-msvc2013-pe-64bit")) + << QString::fromLatin1("x86-windows-msvc2013-pe-64bit") + << int(DebuggerItem::MatchesPerfectly); + QTest::newRow("Windows perfect match 2") + << (QStringList() << QLatin1String("x86-windows-msvc2013-pe-64bit")) + << QString::fromLatin1("x86-windows-msvc2012-pe-64bit") + << int(DebuggerItem::MatchesPerfectly); + QTest::newRow("Windows match 1") + << (QStringList() << QLatin1String("x86-windows-msvc2013-pe-64bit")) + << QString::fromLatin1("x86-windows-msvc2013-pe-32bit") + << int(DebuggerItem::MatchesSomewhat); + QTest::newRow("Windows match 2") + << (QStringList() << QLatin1String("x86-windows-msvc2013-pe-64bit")) + << QString::fromLatin1("x86-windows-msvc2012-pe-32bit") + << int(DebuggerItem::MatchesSomewhat); + QTest::newRow("Windows mismatch on word size") + << (QStringList() << QLatin1String("x86-windows-msvc2013-pe-32bit")) + << QString::fromLatin1("x86-windows-msvc2013-pe-64bit") + << int(DebuggerItem::DoesNotMatch); + QTest::newRow("Windows mismatch on osflavor 1") + << (QStringList() << QLatin1String("x86-windows-msvc2013-pe-32bit")) + << QString::fromLatin1("x86-windows-msys-pe-64bit") + << int(DebuggerItem::DoesNotMatch); + QTest::newRow("Windows mismatch on osflavor 2") + << (QStringList() << QLatin1String("x86-windows-msys-pe-32bit")) + << QString::fromLatin1("x86-windows-msvc2010-pe-64bit") + << int(DebuggerItem::DoesNotMatch); +} + +void Debugger::DebuggerPlugin::testDebuggerMatching() +{ + QFETCH(QStringList, debugger); + QFETCH(QString, target); + QFETCH(int, result); + + DebuggerItem::MatchLevel expectedLevel = static_cast<DebuggerItem::MatchLevel>(result); + + QList<Abi> debuggerAbis; + foreach (const QString &abi, debugger) + debuggerAbis << Abi(abi); + + DebuggerItem item; + item.setAbis(debuggerAbis); + + DebuggerItem::MatchLevel level = item.matchTarget(Abi(target)); + + QCOMPARE(expectedLevel, level); +} +#endif diff --git a/src/plugins/debugger/debuggeritem.h b/src/plugins/debugger/debuggeritem.h new file mode 100644 index 0000000000..9809da22c7 --- /dev/null +++ b/src/plugins/debugger/debuggeritem.h @@ -0,0 +1,96 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of Qt Creator. +** +** 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. +** +** GNU Lesser General Public License Usage +** 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 +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#ifndef DEBUGGER_DEBUGGERITEM_H +#define DEBUGGER_DEBUGGERITEM_H + +#include "debugger_global.h" +#include "debuggerconstants.h" + +#include <projectexplorer/abi.h> + +#include <utils/fileutils.h> + +#include <QList> +#include <QVariant> + +namespace Debugger { + +// ----------------------------------------------------------------------- +// DebuggerItem +// ----------------------------------------------------------------------- + +class DEBUGGER_EXPORT DebuggerItem +{ +public: + DebuggerItem(); + DebuggerItem(const QVariantMap &data); + + bool canClone() const { return true; } + bool isValid() const; + QString engineTypeName() const; + + QVariantMap toMap() const; + void reinitializeFromFile(); + + QVariant id() const { return m_id; } + + QString displayName() const { return m_displayName; } + void setDisplayName(const QString &displayName); + + DebuggerEngineType engineType() const { return m_engineType; } + void setEngineType(const DebuggerEngineType &engineType); + + Utils::FileName command() const { return m_command; } + void setCommand(const Utils::FileName &command); + + bool isAutoDetected() const { return m_isAutoDetected; } + void setAutoDetected(bool isAutoDetected); + + QList<ProjectExplorer::Abi> abis() const { return m_abis; } + void setAbis(const QList<ProjectExplorer::Abi> &abis); + void setAbi(const ProjectExplorer::Abi &abi); + + enum MatchLevel { DoesNotMatch, MatchesSomewhat, MatchesPerfectly }; + MatchLevel matchTarget(const ProjectExplorer::Abi &targetAbi) const; + + QStringList abiNames() const; + +private: + QVariant m_id; + QString m_displayName; + DebuggerEngineType m_engineType; + Utils::FileName m_command; + bool m_isAutoDetected; + QList<ProjectExplorer::Abi> m_abis; +}; + +} // namespace Debugger + +#endif // DEBUGGER_DEBUGGERITEM_H diff --git a/src/plugins/debugger/debuggeritemmanager.cpp b/src/plugins/debugger/debuggeritemmanager.cpp new file mode 100644 index 0000000000..e9df26be1e --- /dev/null +++ b/src/plugins/debugger/debuggeritemmanager.cpp @@ -0,0 +1,408 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of Qt Creator. +** +** 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. +** +** GNU Lesser General Public License Usage +** 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 +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#include "debuggeritemmanager.h" + +#include "debuggeritemmodel.h" +#include "debuggerkitinformation.h" + +#include <coreplugin/icore.h> + +#include <utils/environment.h> +#include <utils/fileutils.h> +#include <utils/persistentsettings.h> +#include <utils/qtcassert.h> + +#include <QDebug> +#include <QDir> +#include <QFileInfo> + +using namespace ProjectExplorer; +using namespace Utils; + +namespace Debugger { + +static const char DEBUGGER_COUNT_KEY[] = "DebuggerItem.Count"; +static const char DEBUGGER_DATA_KEY[] = "DebuggerItem."; +static const char DEBUGGER_LEGACY_FILENAME[] = "/qtcreator/profiles.xml"; +static const char DEBUGGER_FILE_VERSION_KEY[] = "Version"; +static const char DEBUGGER_FILENAME[] = "/qtcreator/debuggers.xml"; + +// -------------------------------------------------------------------------- +// DebuggerItemManager +// -------------------------------------------------------------------------- + +static DebuggerItemManager *m_instance = 0; + +static FileName userSettingsFileName() +{ + QFileInfo settingsLocation(Core::ICore::settings()->fileName()); + return FileName::fromString(settingsLocation.absolutePath() + QLatin1String(DEBUGGER_FILENAME)); +} + +static void readDebuggers(const FileName &fileName, bool isSystem) +{ + PersistentSettingsReader reader; + if (!reader.load(fileName)) + return; + QVariantMap data = reader.restoreValues(); + + // Check version + int version = data.value(QLatin1String(DEBUGGER_FILE_VERSION_KEY), 0).toInt(); + if (version < 1) + return; + + 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)) + continue; + const QVariantMap dbMap = data.value(key).toMap(); + DebuggerItem item(dbMap); + if (isSystem) { + item.setAutoDetected(true); + // SDK debuggers are always considered to be up-to-date, so no need to recheck them. + } else { + // User settings. + if (item.isAutoDetected() && !item.isValid()) { + qWarning() << QString::fromLatin1("DebuggerItem \"%1\" (%2) dropped since it is not valid") + .arg(item.command().toString()).arg(item.id().toString()); + continue; + } + } + DebuggerItemManager::registerDebugger(item); + } +} + +QList<DebuggerItem> DebuggerItemManager::m_debuggers; +Internal::DebuggerItemModel* DebuggerItemManager::m_model = 0; +PersistentSettingsWriter * DebuggerItemManager::m_writer = 0; + +DebuggerItemManager::DebuggerItemManager(QObject *parent) + : QObject(parent) +{ + m_instance = this; + m_writer = new PersistentSettingsWriter(userSettingsFileName(), QLatin1String("QtCreatorDebugger")); + m_model = new Debugger::Internal::DebuggerItemModel(this); + connect(Core::ICore::instance(), SIGNAL(saveSettingsRequested()), + this, SLOT(saveDebuggers())); +} + +QObject *DebuggerItemManager::instance() +{ + return m_instance; +} + +DebuggerItemManager::~DebuggerItemManager() +{ + disconnect(Core::ICore::instance(), SIGNAL(saveSettingsRequested()), + this, SLOT(saveDebuggers())); + delete m_writer; +} + +QList<DebuggerItem> DebuggerItemManager::debuggers() +{ + return m_debuggers; +} + +Internal::DebuggerItemModel *DebuggerItemManager::model() +{ + return m_model; +} + +void DebuggerItemManager::autoDetectCdbDebuggers() +{ + 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) { + if (findByCommand(cdb)) + continue; + DebuggerItem item; + 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()))); + addDebugger(item); + } +} + +void DebuggerItemManager::autoDetectGdbOrLldbDebuggers() +{ + QStringList filters; + filters.append(QLatin1String("gdb-i686-pc-mingw32")); + filters.append(QLatin1String("gdb")); + filters.append(QLatin1String("lldb")); + filters.append(QLatin1String("lldb-*")); + +// DebuggerItem result; +// result.setAutoDetected(true); +// result.setDisplayName(tr("Auto-detected for Tool Chain %1").arg(tc->displayName())); + /* + // 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); + } + } + */ + + 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()) { + FileName command = FileName::fromString(fi.absoluteFilePath()); + if (findByCommand(command)) + continue; + DebuggerItem item; + item.setCommand(command); + item.reinitializeFromFile(); + //: %1: Debugger engine type (GDB, LLDB, CDB...), %2: Path + item.setDisplayName(tr("System %1 at %2") + .arg(item.engineTypeName()).arg(QDir::toNativeSeparators(fi.absoluteFilePath()))); + item.setAutoDetected(true); + addDebugger(item); + } + } +} + +void DebuggerItemManager::readLegacyDebuggers() +{ + QFileInfo settingsLocation(Core::ICore::settings()->fileName()); + FileName legacyKits = FileName::fromString(settingsLocation.absolutePath() + QLatin1String(DEBUGGER_LEGACY_FILENAME)); + + PersistentSettingsReader reader; + if (!reader.load(legacyKits)) + return; + + 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(); + QVariant v3 = data2.value(DebuggerKitInformation::id().toString()); + QString fn; + if (v3.type() == QVariant::String) + fn = v3.toString(); + else + fn = v3.toMap().value(QLatin1String("Binary")).toString(); + if (fn.isEmpty()) + continue; + if (fn.startsWith(QLatin1Char('{'))) + continue; + if (fn == QLatin1String("auto")) + continue; + FileName command = FileName::fromUserInput(fn); + if (findByCommand(command)) + continue; + DebuggerItem item; + item.setCommand(command); + item.setAutoDetected(true); + item.reinitializeFromFile(); + item.setDisplayName(tr("Extracted from Kit %1").arg(kitName)); + addDebugger(item); + } +} + +const DebuggerItem *DebuggerItemManager::findByCommand(const FileName &command) +{ + foreach (const DebuggerItem &item, m_debuggers) + if (item.command() == command) + return &item; + + return 0; +} + +const DebuggerItem *DebuggerItemManager::findById(const QVariant &id) +{ + foreach (const DebuggerItem &item, m_debuggers) + if (item.id() == id) + return &item; + + return 0; +} + +void DebuggerItemManager::restoreDebuggers() +{ + // Read debuggers from SDK + QFileInfo systemSettingsFile(Core::ICore::settings(QSettings::SystemScope)->fileName()); + readDebuggers(FileName::fromString(systemSettingsFile.absolutePath() + QLatin1String(DEBUGGER_FILENAME)), true); + + // Read all debuggers from user file. + readDebuggers(userSettingsFileName(), false); + + // Auto detect current. + autoDetectCdbDebuggers(); + autoDetectGdbOrLldbDebuggers(); + + // Add debuggers from pre-3.x profiles.xml + readLegacyDebuggers(); +} + +void DebuggerItemManager::saveDebuggers() +{ + QTC_ASSERT(m_writer, return); + QVariantMap data; + data.insert(QLatin1String(DEBUGGER_FILE_VERSION_KEY), 1); + + int count = 0; + foreach (const 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. +} + +QVariant DebuggerItemManager::registerDebugger(const DebuggerItem &item) +{ + if (findByCommand(item.command())) + return item.id(); + + return addDebugger(item); +} + +void DebuggerItemManager::deregisterDebugger(const DebuggerItem &item) +{ + if (findByCommand(item.command())) + removeDebugger(item.id()); +} + +QVariant DebuggerItemManager::addDebugger(const DebuggerItem &item) +{ + QTC_ASSERT(!item.command().isEmpty(), return QVariant()); + QTC_ASSERT(!item.displayName().isEmpty(), return QVariant()); + QTC_ASSERT(item.engineType() != NoEngineType, return QVariant()); + QTC_ASSERT(item.id().isValid(), return QVariant()); + m_debuggers.append(item); + m_model->addDebugger(item); + return item.id(); +} + +void DebuggerItemManager::removeDebugger(const QVariant &id) +{ + bool ok = false; + for (int i = 0, n = m_debuggers.size(); i != n; ++i) { + if (m_debuggers.at(i).id() == id) { + m_debuggers.removeAt(i); + ok = true; + break; + } + } + + QTC_ASSERT(ok, return); + m_model->removeDebugger(id); +} + +QString DebuggerItemManager::uniqueDisplayName(const QString &base) +{ + foreach (const DebuggerItem &item, m_debuggers) + if (item.displayName() == base) + return uniqueDisplayName(base + QLatin1String(" (1)")); + + return base; +} + +void DebuggerItemManager::setItemData(const QVariant &id, const QString &displayName, const FileName &fileName) +{ + for (int i = 0, n = m_debuggers.size(); i != n; ++i) { + DebuggerItem &item = m_debuggers[i]; + if (item.id() == id) { + item.setDisplayName(displayName); + item.setCommand(fileName); + item.reinitializeFromFile(); + emit m_model->updateDebugger(item.id()); + break; + } + } +} + +} // namespace Debugger; diff --git a/src/plugins/debugger/debuggeritemmanager.h b/src/plugins/debugger/debuggeritemmanager.h new file mode 100644 index 0000000000..057cfd58b1 --- /dev/null +++ b/src/plugins/debugger/debuggeritemmanager.h @@ -0,0 +1,92 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of Qt Creator. +** +** 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. +** +** GNU Lesser General Public License Usage +** 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 +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#ifndef DEBUGGER_DEBUGGERITEMMANAGER_H +#define DEBUGGER_DEBUGGERITEMMANAGER_H + +#include "debugger_global.h" +#include "debuggeritem.h" +#include "debuggeritemmodel.h" + +#include <QList> +#include <QObject> +#include <QString> + +namespace Utils { class PersistentSettingsWriter; } + +namespace Debugger { + +// ----------------------------------------------------------------------- +// DebuggerItemManager +// ----------------------------------------------------------------------- + +class DEBUGGER_EXPORT DebuggerItemManager : public QObject +{ + Q_OBJECT + +public: + static QObject *instance(); + ~DebuggerItemManager(); + + static QList<DebuggerItem> debuggers(); + static Debugger::Internal::DebuggerItemModel *model(); + + static QVariant registerDebugger(const DebuggerItem &item); + static void deregisterDebugger(const DebuggerItem &item); + + static const DebuggerItem *findByCommand(const Utils::FileName &command); + static const DebuggerItem *findById(const QVariant &id); + + static void restoreDebuggers(); + static QString uniqueDisplayName(const QString &base); + static void setItemData(const QVariant &id, const QString& displayName, const Utils::FileName &fileName); + + static void removeDebugger(const QVariant &id); + static QVariant addDebugger(const DebuggerItem &item); + +public slots: + void saveDebuggers(); + +private: + explicit DebuggerItemManager(QObject *parent = 0); + static void autoDetectGdbOrLldbDebuggers(); + static void autoDetectCdbDebuggers(); + static void readLegacyDebuggers(); + + static Utils::PersistentSettingsWriter *m_writer; + static QList<DebuggerItem> m_debuggers; + static Debugger::Internal::DebuggerItemModel *m_model; + + friend class Internal::DebuggerItemModel; + friend class DebuggerPlugin; // Enable constrcutor for DebuggerPlugin +}; + +} // namespace Debugger + +#endif // DEBUGGER_DEBUGGERITEMMANAGER_H diff --git a/src/plugins/debugger/debuggeritemmodel.cpp b/src/plugins/debugger/debuggeritemmodel.cpp new file mode 100644 index 0000000000..635404684f --- /dev/null +++ b/src/plugins/debugger/debuggeritemmodel.cpp @@ -0,0 +1,198 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of Qt Creator. +** +** 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. +** +** GNU Lesser General Public License Usage +** 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 +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#include "debuggeritemmodel.h" + +#include "debuggeritem.h" +#include "debuggeritemmanager.h" + +#include <utils/qtcassert.h> + +namespace Debugger { +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; +} + +// -------------------------------------------------------------------------- +// 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(); +} + +QModelIndex DebuggerItemModel::lastIndex() const +{ + int n = m_manualRoot->rowCount(); + QStandardItem *current = m_manualRoot->child(n - 1); + return current ? current->index() : QModelIndex(); +} + +void DebuggerItemModel::markCurrentDirty() +{ + QStandardItem *sitem = currentStandardItem(); + QTC_ASSERT(sitem, return); + QFont font = sitem->font(); + font.setBold(true); + sitem->setFont(font); +} + +void DebuggerItemModel::addDebugger(const DebuggerItem &item) +{ + QTC_ASSERT(item.id().isValid(), return); + QList<QStandardItem *> row = describeItem(item); + (item.isAutoDetected() ? m_autoRoot : m_manualRoot)->appendRow(row); + emit debuggerAdded(item.id(), item.displayName()); +} + +void DebuggerItemModel::removeDebugger(const QVariant &id) +{ + 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); +} + +void DebuggerItemModel::updateDebugger(const QVariant &id) +{ + 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(); +} + +} // namespace Internal +} // namespace Debugger diff --git a/src/plugins/debugger/debuggeritemmodel.h b/src/plugins/debugger/debuggeritemmodel.h new file mode 100644 index 0000000000..bb53c708ab --- /dev/null +++ b/src/plugins/debugger/debuggeritemmodel.h @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of Qt Creator. +** +** 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. +** +** GNU Lesser General Public License Usage +** 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 +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#ifndef DEBUGGER_DEBUGGERITEMMODEL_H +#define DEBUGGER_DEBUGGERITEMMODEL_H + +#include "debuggeritem.h" + +#include <QStandardItemModel> +#include <QVariant> + +namespace Debugger { +namespace Internal { + +// ----------------------------------------------------------------------- +// DebuggerItemModel +//------------------------------------------------------------------------ + +class DebuggerItemModel : public QStandardItemModel +{ + Q_OBJECT + +public: + DebuggerItemModel(QObject *parent); + + QModelIndex currentIndex() const; + QModelIndex lastIndex() const; + void setCurrentIndex(const QModelIndex &index); + QVariant currentDebugger() const { return m_currentDebugger; } + void addDebugger(const DebuggerItem &item); + void removeDebugger(const QVariant &id); + void updateDebugger(const QVariant &id); + +public slots: + void markCurrentDirty(); + +signals: + void debuggerAdded(const QVariant &id, const QString &display); + void debuggerUpdated(const QVariant &id, const QString &display); + void debuggerRemoved(const QVariant &id); + +private: + // <debug> + // friend class Debugger::DebuggerKitInformation; + // friend class DebuggerKitConfigWidget; + // friend class DebuggerItemConfigWidget; + // friend class DebuggerOptionsPage; + // </debug> + + QStandardItem *currentStandardItem() const; + QStandardItem *findStandardItemById(const QVariant &id) const; + QVariant headerData(int section, Qt::Orientation orientation, int role) const; + + QVariant m_currentDebugger; + + QStandardItem *m_autoRoot; + QStandardItem *m_manualRoot; + QStringList removed; +}; + +} // namespace Internal +} // namespace Debugger + +#endif // DEBUGGER_DEBUGGERITEMMODEL_H diff --git a/src/plugins/debugger/debuggerkitconfigwidget.cpp b/src/plugins/debugger/debuggerkitconfigwidget.cpp index a3e0b611d3..85f4ded641 100644 --- a/src/plugins/debugger/debuggerkitconfigwidget.cpp +++ b/src/plugins/debugger/debuggerkitconfigwidget.cpp @@ -29,6 +29,9 @@ #include "debuggerkitconfigwidget.h" +#include "debuggeritemmanager.h" +#include "debuggeritemmodel.h" + #include <coreplugin/icore.h> #include <projectexplorer/abi.h> @@ -64,811 +67,10 @@ using namespace Utils; using namespace Debugger::Internal; namespace Debugger { - -static const char debuggingToolsWikiLinkC[] = "http://qt-project.org/wiki/Qt_Creator_Windows_Debugging"; - -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"; -static const char DEBUGGER_LEGACY_FILENAME[] = "/qtcreator/profiles.xml"; - -// -------------------------------------------------------------------------- -// DebuggerKitInformation -// -------------------------------------------------------------------------- - -DebuggerKitInformation::DebuggerKitInformation() -{ - setObjectName(QLatin1String("DebuggerKitInformation")); - setId(DebuggerKitInformation::id()); - setPriority(28000); -} - -QVariant DebuggerKitInformation::defaultValue(Kit *k) const -{ - ToolChain *tc = ToolChainKitInformation::toolChain(k); - QTC_ASSERT(tc, return QVariant()); - - const Abi toolChainAbi = tc->targetAbi(); - foreach (const DebuggerItem &item, DebuggerItemManager::debuggers()) - foreach (const Abi targetAbi, item.abis()) - if (targetAbi.isCompatibleWith(toolChainAbi)) - return item.id(); - - return QVariant(); -} - -void DebuggerKitInformation::setup(Kit *k) -{ - // Get one of the available debugger matching the kit's toolchain. - const ToolChain *tc = ToolChainKitInformation::toolChain(k); - const Abi toolChainAbi = tc ? tc->targetAbi() : Abi::hostAbi(); - - // This can be anything (Id, binary path, "auto") - const QVariant rawId = k->value(DebuggerKitInformation::id()); - - enum { - NotDetected, DetectedAutomatically, DetectedByFile, DetectedById - } detection = NotDetected; - DebuggerEngineType autoEngine = NoEngineType; - FileName fileName; - - // With 3.0 we have: - // <value type="QString" key="Debugger.Information">{75ecf347-f221-44c3-b613-ea1d29929cd4}</value> - // 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> - // Or for force auto-detected CDB - // <valuemap type="QVariantMap" key="Debugger.Information"> - // <value type="QString" key="Binary">auto</value> - // <value type="int" key="EngineType">4</value> - // </valuemap> - - if (rawId.isNull()) { - // Initial setup of a kit - detection = NotDetected; - } else if (rawId.type() == QVariant::String) { - detection = DetectedById; - } else { - QMap<QString, QVariant> map = rawId.toMap(); - QString binary = map.value(QLatin1String("Binary")).toString(); - if (binary == QLatin1String("auto")) { - detection = DetectedAutomatically; - autoEngine = DebuggerEngineType(map.value(QLatin1String("EngineType")).toInt()); - } else { - detection = DetectedByFile; - fileName = FileName::fromUserInput(binary); - } - } - - const DebuggerItem *bestItem = 0; - DebuggerItem::MatchLevel bestLevel = DebuggerItem::DoesNotMatch; - foreach (const DebuggerItem &item, DebuggerItemManager::debuggers()) { - const DebuggerItem *goodItem = 0; - if (detection == DetectedById && item.id() == rawId) - goodItem = &item; - if (detection == DetectedByFile && item.command() == fileName) - goodItem = &item; - if (detection == DetectedAutomatically && item.engineType() == autoEngine) - goodItem = &item; - - if (goodItem) { - DebuggerItem::MatchLevel level = goodItem->matchTarget(toolChainAbi); - if (level > bestLevel) { - bestLevel = level; - bestItem = goodItem; - } - } - } - - // If we have an existing debugger with matching id _and_ - // matching target ABI we are fine. - if (bestItem) { - k->setValue(DebuggerKitInformation::id(), bestItem->id()); - return; - } - - // We didn't find an existing debugger that matched by whatever - // data we found in the kit (i.e. no id, filename, "auto") - // (or what we found did not match ABI-wise) - // Let's try to pick one with matching ABI. - QVariant bestId; - bestLevel = DebuggerItem::DoesNotMatch; - foreach (const DebuggerItem &item, DebuggerItemManager::debuggers()) { - DebuggerItem::MatchLevel level = item.matchTarget(toolChainAbi); - if (level > bestLevel) { - bestLevel = level; - bestId = item.id(); - } - } - - k->setValue(DebuggerKitInformation::id(), bestId); -} - - -// This handles the upgrade path from 2.8 to 3.0 -void DebuggerKitInformation::fix(Kit *k) -{ - // This can be Id, binary path, but not "auto" anymore. - const QVariant rawId = k->value(DebuggerKitInformation::id()); - - if (rawId.isNull()) // No debugger set, that is fine. - return; - - if (rawId.type() == QVariant::String) { - if (!DebuggerItemManager::findById(rawId)) { - qWarning("Unknown debugger id %s in kit %s", - qPrintable(rawId.toString()), qPrintable(k->displayName())); - k->setValue(DebuggerKitInformation::id(), QVariant()); - } - return; // All fine (now). - } - - QMap<QString, QVariant> map = rawId.toMap(); - QString binary = map.value(QLatin1String("Binary")).toString(); - if (binary == QLatin1String("auto")) { - // This should not happen as "auto" is handled by setup() already. - QTC_CHECK(false); - k->setValue(DebuggerKitInformation::id(), QVariant()); - return; - } - - FileName fileName = FileName::fromUserInput(binary); - const DebuggerItem *item = DebuggerItemManager::findByCommand(fileName); - if (!item) { - qWarning("Debugger command %s invalid in kit %s", - qPrintable(binary), qPrintable(k->displayName())); - k->setValue(DebuggerKitInformation::id(), QVariant()); - return; - } - - k->setValue(DebuggerKitInformation::id(), item->id()); -} - -// 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) -{ - QTC_ASSERT(k, return NoDebugger); - - const DebuggerItem *item = DebuggerKitInformation::debugger(k); - if (!item) - return NoDebugger; - - if (item->command().isEmpty()) - return NoDebugger; - - unsigned result = 0; - const QFileInfo fi = item->command().toFileInfo(); - if (!fi.exists() || fi.isDir()) - result |= DebuggerNotFound; - else if (!fi.isExecutable()) - result |= DebuggerNotExecutable; - - if (!fi.exists() || fi.isDir()) { - if (item->engineType() == NoEngineType) - return NoDebugger; - - // 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; -} - -const DebuggerItem *DebuggerKitInformation::debugger(const Kit *kit) -{ - QTC_ASSERT(kit, return 0); - const QVariant id = kit->value(DebuggerKitInformation::id()); - return DebuggerItemManager::findById(id); -} - -bool DebuggerKitInformation::isValidDebugger(const Kit *k) -{ - return debuggerConfigurationErrors(k) == 0; -} - -QList<Task> DebuggerKitInformation::validateDebugger(const Kit *k) -{ - QList<Task> result; - - const unsigned errors = debuggerConfigurationErrors(k); - if (!errors) - return result; - - QString path; - if (const DebuggerItem *item = debugger(k)) - path = item->command().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, this); -} - -KitInformation::ItemList DebuggerKitInformation::toUserOutput(const Kit *k) const -{ - return ItemList() << qMakePair(tr("Debugger"), displayString(k)); -} - -FileName DebuggerKitInformation::debuggerCommand(const ProjectExplorer::Kit *k) -{ - const DebuggerItem *item = debugger(k); - QTC_ASSERT(item, return FileName()); - return item->command(); -} - -DebuggerEngineType DebuggerKitInformation::engineType(const ProjectExplorer::Kit *k) -{ - const DebuggerItem *item = debugger(k); - QTC_ASSERT(item, return NoEngineType); - return item->engineType(); -} - -QString DebuggerKitInformation::displayString(const Kit *k) -{ - const DebuggerItem *item = debugger(k); - if (!item) - return tr("No Debugger"); - QString binary = item->command().toUserOutput(); - QString name = tr("%1 Engine").arg(item->engineTypeName()); - return binary.isEmpty() ? tr("%1 <None>").arg(name) : tr("%1 using \"%2\"").arg(name, binary); -} - -void DebuggerKitInformation::setDebugger(Kit *k, const QVariant &id) -{ - // Only register reasonably complete debuggers. - QTC_ASSERT(DebuggerItemManager::findById(id), return); - k->setValue(DebuggerKitInformation::id(), id); -} - -Core::Id DebuggerKitInformation::id() -{ - return "Debugger.Information"; -} - -// -------------------------------------------------------------------------- -// DebuggerItemManager -// -------------------------------------------------------------------------- - -static DebuggerItemManager *m_instance = 0; - -static FileName userSettingsFileName() -{ - QFileInfo settingsLocation(Core::ICore::settings()->fileName()); - return FileName::fromString(settingsLocation.absolutePath() + QLatin1String(DEBUGGER_FILENAME)); -} - -static void readDebuggers(const FileName &fileName, bool isSystem) -{ - PersistentSettingsReader reader; - if (!reader.load(fileName)) - return; - QVariantMap data = reader.restoreValues(); - - // Check version - int version = data.value(QLatin1String(DEBUGGER_FILE_VERSION_KEY), 0).toInt(); - if (version < 1) - return; - - 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)) - continue; - const QVariantMap dbMap = data.value(key).toMap(); - DebuggerItem item(dbMap); - if (isSystem) { - item.setAutoDetected(true); - // SDK debuggers are always considered to be up-to-date, so no need to recheck them. - } else { - // User settings. - if (item.isAutoDetected() && !item.isValid()) { - qWarning() << QString::fromLatin1("DebuggerItem \"%1\" (%2) dropped since it is not valid") - .arg(item.command().toString()).arg(item.id().toString()); - continue; - } - } - DebuggerItemManager::registerDebugger(item); - } -} - -QList<DebuggerItem> DebuggerItemManager::m_debuggers; -DebuggerItemModel* DebuggerItemManager::m_model = 0; -PersistentSettingsWriter * DebuggerItemManager::m_writer = 0; - -DebuggerItemManager::DebuggerItemManager(QObject *parent) - : QObject(parent) -{ - m_instance = this; - m_writer = new PersistentSettingsWriter(userSettingsFileName(), QLatin1String("QtCreatorDebugger")); - m_model = new Debugger::Internal::DebuggerItemModel(this); - connect(Core::ICore::instance(), SIGNAL(saveSettingsRequested()), - this, SLOT(saveDebuggers())); -} - -QObject *DebuggerItemManager::instance() -{ - return m_instance; -} - -DebuggerItemManager::~DebuggerItemManager() -{ - disconnect(Core::ICore::instance(), SIGNAL(saveSettingsRequested()), - this, SLOT(saveDebuggers())); - delete m_writer; -} - -QList<DebuggerItem> DebuggerItemManager::debuggers() -{ - return m_debuggers; -} - -DebuggerItemModel *DebuggerItemManager::model() -{ - return m_model; -} - -void DebuggerItemManager::autoDetectCdbDebuggers() -{ - 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) { - if (findByCommand(cdb)) - continue; - DebuggerItem item; - 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()))); - addDebugger(item); - } -} - -void DebuggerItemManager::autoDetectGdbOrLldbDebuggers() -{ - QStringList filters; - filters.append(QLatin1String("gdb-i686-pc-mingw32")); - filters.append(QLatin1String("gdb")); - filters.append(QLatin1String("lldb")); - filters.append(QLatin1String("lldb-*")); - -// DebuggerItem result; -// result.setAutoDetected(true); -// result.setDisplayName(tr("Auto-detected for Tool Chain %1").arg(tc->displayName())); - /* - // 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); - } - } - */ - - 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()) { - FileName command = FileName::fromString(fi.absoluteFilePath()); - if (findByCommand(command)) - continue; - DebuggerItem item; - item.setCommand(command); - item.reinitializeFromFile(); - //: %1: Debugger engine type (GDB, LLDB, CDB...), %2: Path - item.setDisplayName(tr("System %1 at %2") - .arg(item.engineTypeName()).arg(QDir::toNativeSeparators(fi.absoluteFilePath()))); - item.setAutoDetected(true); - addDebugger(item); - } - } -} - -void DebuggerItemManager::readLegacyDebuggers() -{ - QFileInfo settingsLocation(Core::ICore::settings()->fileName()); - FileName legacyKits = FileName::fromString(settingsLocation.absolutePath() + QLatin1String(DEBUGGER_LEGACY_FILENAME)); - - PersistentSettingsReader reader; - if (!reader.load(legacyKits)) - return; - - 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(); - QVariant v3 = data2.value(DebuggerKitInformation::id().toString()); - QString fn; - if (v3.type() == QVariant::String) - fn = v3.toString(); - else - fn = v3.toMap().value(QLatin1String("Binary")).toString(); - if (fn.isEmpty()) - continue; - if (fn.startsWith(QLatin1Char('{'))) - continue; - if (fn == QLatin1String("auto")) - continue; - FileName command = FileName::fromUserInput(fn); - if (findByCommand(command)) - continue; - DebuggerItem item; - item.setCommand(command); - item.setAutoDetected(true); - item.reinitializeFromFile(); - item.setDisplayName(tr("Extracted from Kit %1").arg(kitName)); - addDebugger(item); - } -} - -const DebuggerItem *DebuggerItemManager::findByCommand(const FileName &command) -{ - foreach (const DebuggerItem &item, m_debuggers) - if (item.command() == command) - return &item; - - return 0; -} - -const DebuggerItem *DebuggerItemManager::findById(const QVariant &id) -{ - foreach (const DebuggerItem &item, m_debuggers) - if (item.id() == id) - return &item; - - return 0; -} - -void DebuggerItemManager::restoreDebuggers() -{ - // Read debuggers from SDK - QFileInfo systemSettingsFile(Core::ICore::settings(QSettings::SystemScope)->fileName()); - readDebuggers(FileName::fromString(systemSettingsFile.absolutePath() + QLatin1String(DEBUGGER_FILENAME)), true); - - // Read all debuggers from user file. - readDebuggers(userSettingsFileName(), false); - - // Auto detect current. - autoDetectCdbDebuggers(); - autoDetectGdbOrLldbDebuggers(); - - // Add debuggers from pre-3.x profiles.xml - readLegacyDebuggers(); -} - -void DebuggerItemManager::saveDebuggers() -{ - QTC_ASSERT(m_writer, return); - QVariantMap data; - data.insert(QLatin1String(DEBUGGER_FILE_VERSION_KEY), 1); - - int count = 0; - foreach (const 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. -} - -QVariant DebuggerItemManager::registerDebugger(const DebuggerItem &item) -{ - QTC_ASSERT(!item.command().isEmpty(), return QVariant()); - QTC_ASSERT(!item.displayName().isEmpty(), return QVariant()); - QTC_ASSERT(item.engineType() != NoEngineType, return QVariant()); - if (findByCommand(item.command())) - return item.id(); - - return addDebugger(item); -} - -void DebuggerItemManager::deregisterDebugger(const DebuggerItem &item) -{ - if (findByCommand(item.command())) - removeDebugger(item.id()); -} - -QVariant DebuggerItemManager::addDebugger(const DebuggerItem &item) -{ - QTC_ASSERT(item.id().isValid(), return QVariant()); - m_debuggers.append(item); - m_model->addDebugger(item); - return item.id(); -} - -void DebuggerItemManager::removeDebugger(const QVariant &id) -{ - bool ok = false; - for (int i = 0, n = m_debuggers.size(); i != n; ++i) { - if (m_debuggers.at(i).id() == id) { - m_debuggers.removeAt(i); - ok = true; - break; - } - } - - QTC_ASSERT(ok, return); - m_model->removeDebugger(id); -} - -QString DebuggerItemManager::uniqueDisplayName(const QString &base) -{ - foreach (const DebuggerItem &item, m_debuggers) - if (item.displayName() == base) - return uniqueDisplayName(base + QLatin1String(" (1)")); - - return base; -} - -void DebuggerItemManager::setItemData(const QVariant &id, const QString &displayName, const FileName &fileName) -{ - for (int i = 0, n = m_debuggers.size(); i != n; ++i) { - DebuggerItem &item = m_debuggers[i]; - if (item.id() == id) { - item.setDisplayName(displayName); - item.setCommand(fileName); - item.reinitializeFromFile(); - emit m_model->updateDebugger(item.id()); - break; - } - } -} - 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(); -} - -QModelIndex DebuggerItemModel::lastIndex() const -{ - int n = m_manualRoot->rowCount(); - QStandardItem *current = m_manualRoot->child(n - 1); - return current ? current->index() : QModelIndex(); -} - -void DebuggerItemModel::markCurrentDirty() -{ - QStandardItem *sitem = currentStandardItem(); - QTC_ASSERT(sitem, return); - QFont font = sitem->font(); - font.setBold(true); - sitem->setFont(font); -} - -void DebuggerItemModel::addDebugger(const DebuggerItem &item) -{ - QTC_ASSERT(item.id().isValid(), return); - QList<QStandardItem *> row = describeItem(item); - (item.isAutoDetected() ? m_autoRoot : m_manualRoot)->appendRow(row); - emit debuggerAdded(item.id(), item.displayName()); -} - -void DebuggerItemModel::removeDebugger(const QVariant &id) -{ - 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); -} - -void DebuggerItemModel::updateDebugger(const QVariant &id) -{ - 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(); -} - // ----------------------------------------------------------------------- // DebuggerKitConfigWidget // ----------------------------------------------------------------------- @@ -1002,296 +204,5 @@ void DebuggerKitConfigWidget::updateComboBox(const QVariant &id) m_comboBox->setCurrentIndex(0); } -// ----------------------------------------------------------------------- -// DebuggerItemConfigWidget -// ----------------------------------------------------------------------- - -class DebuggerItemConfigWidget : public QWidget -{ - Q_DECLARE_TR_FUNCTIONS(Debugger::Internal::DebuggerItemConfigWidget) -public: - explicit DebuggerItemConfigWidget(); - void loadItem(); - void saveItem(); - void connectDirty(); - void disconnectDirty(); - -private: - QLineEdit *m_displayNameLineEdit; - QLabel *m_cdbLabel; - PathChooser *m_binaryChooser; - QLineEdit *m_abis; -}; - -DebuggerItemConfigWidget::DebuggerItemConfigWidget() -{ - m_displayNameLineEdit = new QLineEdit(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); - - QFormLayout *formLayout = new QFormLayout(this); - formLayout->setFieldGrowthPolicy(QFormLayout::AllNonFixedFieldsGrow); - 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); - - connectDirty(); -} - -void DebuggerItemConfigWidget::connectDirty() -{ - DebuggerItemModel *model = DebuggerItemManager::model(); - connect(m_displayNameLineEdit, SIGNAL(textChanged(QString)), - model, SLOT(markCurrentDirty())); - connect(m_binaryChooser, SIGNAL(changed(QString)), - model, SLOT(markCurrentDirty())); -} - -void DebuggerItemConfigWidget::disconnectDirty() -{ - DebuggerItemModel *model = DebuggerItemManager::model(); - disconnect(m_displayNameLineEdit, SIGNAL(textChanged(QString)), - model, SLOT(markCurrentDirty())); - disconnect(m_binaryChooser, SIGNAL(changed(QString)), - model, SLOT(markCurrentDirty())); -} - -void DebuggerItemConfigWidget::loadItem() -{ - DebuggerItemModel *model = DebuggerItemManager::model(); - const DebuggerItem *item = DebuggerItemManager::findById(model->m_currentDebugger); - if (!item) - return; - - disconnectDirty(); - m_displayNameLineEdit->setEnabled(!item->isAutoDetected()); - m_displayNameLineEdit->setText(item->displayName()); - - m_binaryChooser->setEnabled(!item->isAutoDetected()); - m_binaryChooser->setFileName(item->command()); - connectDirty(); - - QString text; - QString versionCommand; - if (item->engineType() == CdbEngineType) { -#ifdef Q_OS_WIN - const bool is64bit = winIs64BitSystem(); -#else - const bool is64bit = false; -#endif - const QString versionString = is64bit ? tr("64-bit version") : tr("32-bit version"); - //: Label text for path configuration. %2 is "x-bit version". - text = tr("<html><body><p>Specify the path to the " - "<a href=\"%1\">Windows Console Debugger executable</a>" - " (%2) here.</p>""</body></html>"). - arg(QLatin1String(debuggingToolsWikiLinkC), versionString); - versionCommand = QLatin1String("-version"); - } else { - versionCommand = QLatin1String("--version"); - } - - m_cdbLabel->setText(text); - m_cdbLabel->setVisible(!text.isEmpty()); - m_binaryChooser->setCommandVersionArguments(QStringList(versionCommand)); - - m_abis->setText(item->abiNames().join(QLatin1String(", "))); -} - -void DebuggerItemConfigWidget::saveItem() -{ - DebuggerItemModel *model = DebuggerItemManager::model(); - const DebuggerItem *item = DebuggerItemManager::findById(model->m_currentDebugger); - QTC_ASSERT(item, return); - DebuggerItemManager::setItemData(item->id(), m_displayNameLineEdit->text(), - m_binaryChooser->fileName()); -} - -// -------------------------------------------------------------------------- -// DebuggerOptionsPage -// -------------------------------------------------------------------------- - -DebuggerOptionsPage::DebuggerOptionsPage() -{ - m_model = 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_model = DebuggerItemManager::model(); - - m_debuggerView = new QTreeView(m_configWidget); - m_debuggerView->setModel(m_model); - 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(); - debuggerModelChanged(); -} - -void DebuggerOptionsPage::cloneDebugger() -{ - const DebuggerItem *item = DebuggerItemManager::findById(m_model->currentDebugger()); - QTC_ASSERT(item, return); - DebuggerItem newItem; - newItem.setCommand(item->command()); - newItem.setEngineType(item->engineType()); - newItem.setAbis(item->abis()); - newItem.setDisplayName(DebuggerItemManager::uniqueDisplayName(tr("Clone of %1").arg(item->displayName()))); - newItem.setAutoDetected(false); - DebuggerItemManager::addDebugger(newItem); - m_debuggerView->setCurrentIndex(m_model->lastIndex()); -} - -void DebuggerOptionsPage::addDebugger() -{ - DebuggerItem item; - item.setEngineType(NoEngineType); - item.setDisplayName(DebuggerItemManager::uniqueDisplayName(tr("New Debugger"))); - item.setAutoDetected(false); - DebuggerItemManager::addDebugger(item); - m_debuggerView->setCurrentIndex(m_model->lastIndex()); -} - -void DebuggerOptionsPage::removeDebugger() -{ - QVariant id = m_model->currentDebugger(); - DebuggerItemManager::removeDebugger(id); - m_debuggerView->setCurrentIndex(m_model->lastIndex()); -} - -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_model->setCurrentIndex(mi); - - m_itemConfigWidget->loadItem(); - m_container->setVisible(m_model->m_currentDebugger.isValid()); - updateState(); -} - -void DebuggerOptionsPage::debuggerModelChanged() -{ - QTC_ASSERT(m_container, return); - - m_itemConfigWidget->loadItem(); - m_container->setVisible(m_model->m_currentDebugger.isValid()); - m_debuggerView->setCurrentIndex(m_model->currentIndex()); - updateState(); -} - -void DebuggerOptionsPage::updateState() -{ - if (!m_cloneButton) - return; - - bool canCopy = false; - bool canDelete = false; - - if (const DebuggerItem *item = DebuggerItemManager::findById(m_model->m_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 } // namespace Debugger diff --git a/src/plugins/debugger/debuggerkitconfigwidget.h b/src/plugins/debugger/debuggerkitconfigwidget.h index f2ab06695f..c6a5433218 100644 --- a/src/plugins/debugger/debuggerkitconfigwidget.h +++ b/src/plugins/debugger/debuggerkitconfigwidget.h @@ -52,52 +52,6 @@ QT_END_NAMESPACE namespace Debugger { namespace Internal { -class DebuggerItemConfigWidget; -class DebuggerKitConfigWidget; - -// ----------------------------------------------------------------------- -// DebuggerItemModel -//------------------------------------------------------------------------ -class DebuggerItemModel : public QStandardItemModel -{ - Q_OBJECT - -public: - DebuggerItemModel(QObject *parent); - - QModelIndex currentIndex() const; - QModelIndex lastIndex() const; - void setCurrentIndex(const QModelIndex &index); - QVariant currentDebugger() const { return m_currentDebugger; } - void addDebugger(const DebuggerItem &item); - void removeDebugger(const QVariant &id); - void updateDebugger(const QVariant &id); - -public slots: - void markCurrentDirty(); - -signals: - void debuggerAdded(const QVariant &id, const QString &display); - void debuggerUpdated(const QVariant &id, const QString &display); - void debuggerRemoved(const QVariant &id); - -private: - friend class Debugger::DebuggerKitInformation; - friend class DebuggerKitConfigWidget; - friend class DebuggerItemConfigWidget; - friend class DebuggerOptionsPage; - - QStandardItem *currentStandardItem() const; - QStandardItem *findStandardItemById(const QVariant &id) const; - QVariant headerData(int section, Qt::Orientation orientation, int role) const; - - QVariant m_currentDebugger; - - QStandardItem *m_autoRoot; - QStandardItem *m_manualRoot; - QStringList removed; -}; - // ----------------------------------------------------------------------- // DebuggerKitConfigWidget // ----------------------------------------------------------------------- @@ -135,44 +89,7 @@ private: QPushButton *m_manageButton; }; -// -------------------------------------------------------------------------- -// DebuggerOptionsPage -// -------------------------------------------------------------------------- - -class DebuggerOptionsPage : public Core::IOptionsPage -{ - Q_OBJECT - -public: - DebuggerOptionsPage(); - - QWidget *createPage(QWidget *parent); - void apply(); - void finish(); - bool matches(const QString &) const; - -private slots: - void debuggerSelectionChanged(); - void debuggerModelChanged(); - void updateState(); - void cloneDebugger(); - void addDebugger(); - void removeDebugger(); - -private: - QWidget *m_configWidget; - QString m_searchKeywords; - - DebuggerItemModel *m_model; - DebuggerItemConfigWidget *m_itemConfigWidget; - QTreeView *m_debuggerView; - Utils::DetailsWidget *m_container; - QPushButton *m_addButton; - QPushButton *m_cloneButton; - QPushButton *m_delButton; -}; - } // namespace Internal } // namespace Debugger -#endif // DEBUGGER_DEBUGGERKITINFORMATION_H +#endif // DEBUGGER_DEBUGGERKITCONFIGWIDGET_H diff --git a/src/plugins/debugger/debuggerkitinformation.cpp b/src/plugins/debugger/debuggerkitinformation.cpp index 5436206bcb..7ee98c619d 100644 --- a/src/plugins/debugger/debuggerkitinformation.cpp +++ b/src/plugins/debugger/debuggerkitinformation.cpp @@ -28,309 +28,305 @@ ****************************************************************************/ #include "debuggerkitinformation.h" + +#include "debuggeritemmanager.h" #include "debuggerkitconfigwidget.h" -#include <projectexplorer/abi.h> +#include "projectexplorer/toolchain.h" +#include "projectexplorer/projectexplorerconstants.h" + #include <utils/fileutils.h> +#include <utils/qtcassert.h> -#include <QProcess> -#include <QUuid> +#include <QFileInfo> -using namespace Debugger::Internal; using namespace ProjectExplorer; using namespace Utils; -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"; - namespace Debugger { // -------------------------------------------------------------------------- -// DebuggerItem +// DebuggerKitInformation // -------------------------------------------------------------------------- -DebuggerItem::DebuggerItem() +DebuggerKitInformation::DebuggerKitInformation() { - m_id = QUuid::createUuid().toString(); - m_engineType = NoEngineType; - m_isAutoDetected = false; + setObjectName(QLatin1String("DebuggerKitInformation")); + setId(DebuggerKitInformation::id()); + setPriority(28000); } -DebuggerItem::DebuggerItem(const QVariantMap &data) +QVariant DebuggerKitInformation::defaultValue(Kit *k) const { - m_command = FileName::fromUserInput(data.value(QLatin1String(DEBUGGER_INFORMATION_COMMAND)).toString()); - m_id = data.value(QLatin1String(DEBUGGER_INFORMATION_ID)).toString(); - m_displayName = data.value(QLatin1String(DEBUGGER_INFORMATION_DISPLAYNAME)).toString(); - m_isAutoDetected = data.value(QLatin1String(DEBUGGER_INFORMATION_AUTODETECTED), false).toBool(); - m_engineType = DebuggerEngineType(data.value(QLatin1String(DEBUGGER_INFORMATION_ENGINETYPE), - static_cast<int>(NoEngineType)).toInt()); - - foreach (const QString &a, data.value(QLatin1String(DEBUGGER_INFORMATION_ABIS)).toStringList()) { - Abi abi(a); - if (abi.isValid()) - m_abis.append(abi); - } + ToolChain *tc = ToolChainKitInformation::toolChain(k); + QTC_ASSERT(tc, return QVariant()); + + const Abi toolChainAbi = tc->targetAbi(); + foreach (const DebuggerItem &item, DebuggerItemManager::debuggers()) + foreach (const Abi targetAbi, item.abis()) + if (targetAbi.isCompatibleWith(toolChainAbi)) + return item.id(); + + return QVariant(); } -void DebuggerItem::reinitializeFromFile() +void DebuggerKitInformation::setup(Kit *k) { - QProcess proc; - proc.start(m_command.toString(), QStringList() << QLatin1String("--version")); - proc.waitForStarted(); - proc.waitForFinished(); - QByteArray ba = proc.readAll(); - if (ba.contains("gdb")) { - m_engineType = GdbEngineType; - const char needle[] = "This GDB was configured as \""; - // E.g. "--host=i686-pc-linux-gnu --target=arm-unknown-nto-qnx6.5.0". - // or "i686-linux-gnu" - int pos1 = ba.indexOf(needle); - if (pos1 != -1) { - pos1 += int(sizeof(needle)); - int pos2 = ba.indexOf('"', pos1 + 1); - QByteArray target = ba.mid(pos1, pos2 - pos1); - int pos3 = target.indexOf("--target="); - if (pos3 >= 0) - target = target.mid(pos3 + 9); - m_abis.append(Abi::abiFromTargetTriplet(QString::fromLatin1(target))); + // Get one of the available debugger matching the kit's toolchain. + const ToolChain *tc = ToolChainKitInformation::toolChain(k); + const Abi toolChainAbi = tc ? tc->targetAbi() : Abi::hostAbi(); + + // This can be anything (Id, binary path, "auto") + const QVariant rawId = k->value(DebuggerKitInformation::id()); + + enum { + NotDetected, DetectedAutomatically, DetectedByFile, DetectedById + } detection = NotDetected; + DebuggerEngineType autoEngine = NoEngineType; + FileName fileName; + + // With 3.0 we have: + // <value type="QString" key="Debugger.Information">{75ecf347-f221-44c3-b613-ea1d29929cd4}</value> + // 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> + // Or for force auto-detected CDB + // <valuemap type="QVariantMap" key="Debugger.Information"> + // <value type="QString" key="Binary">auto</value> + // <value type="int" key="EngineType">4</value> + // </valuemap> + + if (rawId.isNull()) { + // Initial setup of a kit + detection = NotDetected; + } else if (rawId.type() == QVariant::String) { + detection = DetectedById; + } else { + QMap<QString, QVariant> map = rawId.toMap(); + QString binary = map.value(QLatin1String("Binary")).toString(); + if (binary == QLatin1String("auto")) { + detection = DetectedAutomatically; + autoEngine = DebuggerEngineType(map.value(QLatin1String("EngineType")).toInt()); } else { - // Fallback. - m_abis = Abi::abisOfBinary(m_command); // FIXME: Wrong. + detection = DetectedByFile; + fileName = FileName::fromUserInput(binary); } - return; } - if (ba.contains("lldb") || ba.startsWith("LLDB")) { - m_engineType = LldbEngineType; - m_abis = Abi::abisOfBinary(m_command); - return; + + const DebuggerItem *bestItem = 0; + DebuggerItem::MatchLevel bestLevel = DebuggerItem::DoesNotMatch; + foreach (const DebuggerItem &item, DebuggerItemManager::debuggers()) { + const DebuggerItem *goodItem = 0; + if (detection == DetectedById && item.id() == rawId) + goodItem = &item; + if (detection == DetectedByFile && item.command() == fileName) + goodItem = &item; + if (detection == DetectedAutomatically && item.engineType() == autoEngine) + goodItem = &item; + + if (goodItem) { + DebuggerItem::MatchLevel level = goodItem->matchTarget(toolChainAbi); + if (level > bestLevel) { + bestLevel = level; + bestItem = goodItem; + } + } } - if (ba.startsWith("Python")) { - m_engineType = PdbEngineType; + + // If we have an existing debugger with matching id _and_ + // matching target ABI we are fine. + if (bestItem) { + k->setValue(DebuggerKitInformation::id(), bestItem->id()); return; } - m_engineType = NoEngineType; -} -QString DebuggerItem::engineTypeName() const -{ - switch (m_engineType) { - case Debugger::NoEngineType: - return DebuggerOptionsPage::tr("Not recognized"); - case Debugger::GdbEngineType: - return QLatin1String("GDB"); - case Debugger::CdbEngineType: - return QLatin1String("CDB"); - case Debugger::LldbEngineType: - return QLatin1String("LLDB"); - default: - return QString(); + // We didn't find an existing debugger that matched by whatever + // data we found in the kit (i.e. no id, filename, "auto") + // (or what we found did not match ABI-wise) + // Let's try to pick one with matching ABI. + QVariant bestId; + bestLevel = DebuggerItem::DoesNotMatch; + foreach (const DebuggerItem &item, DebuggerItemManager::debuggers()) { + DebuggerItem::MatchLevel level = item.matchTarget(toolChainAbi); + if (level > bestLevel) { + bestLevel = level; + bestId = item.id(); + } } -} -QStringList DebuggerItem::abiNames() const -{ - QStringList list; - foreach (const Abi &abi, m_abis) - list.append(abi.toString()); - return list; + k->setValue(DebuggerKitInformation::id(), bestId); } -QVariantMap DebuggerItem::toMap() const + +// This handles the upgrade path from 2.8 to 3.0 +void DebuggerKitInformation::fix(Kit *k) { - QVariantMap data; - data.insert(QLatin1String(DEBUGGER_INFORMATION_DISPLAYNAME), m_displayName); - data.insert(QLatin1String(DEBUGGER_INFORMATION_ID), m_id); - data.insert(QLatin1String(DEBUGGER_INFORMATION_COMMAND), m_command.toUserOutput()); - data.insert(QLatin1String(DEBUGGER_INFORMATION_ENGINETYPE), int(m_engineType)); - data.insert(QLatin1String(DEBUGGER_INFORMATION_AUTODETECTED), m_isAutoDetected); - data.insert(QLatin1String(DEBUGGER_INFORMATION_ABIS), abiNames()); - return data; + // This can be Id, binary path, but not "auto" anymore. + const QVariant rawId = k->value(DebuggerKitInformation::id()); + + if (rawId.isNull()) // No debugger set, that is fine. + return; + + if (rawId.type() == QVariant::String) { + if (!DebuggerItemManager::findById(rawId)) { + qWarning("Unknown debugger id %s in kit %s", + qPrintable(rawId.toString()), qPrintable(k->displayName())); + k->setValue(DebuggerKitInformation::id(), QVariant()); + } + return; // All fine (now). + } + + QMap<QString, QVariant> map = rawId.toMap(); + QString binary = map.value(QLatin1String("Binary")).toString(); + if (binary == QLatin1String("auto")) { + // This should not happen as "auto" is handled by setup() already. + QTC_CHECK(false); + k->setValue(DebuggerKitInformation::id(), QVariant()); + return; + } + + FileName fileName = FileName::fromUserInput(binary); + const DebuggerItem *item = DebuggerItemManager::findByCommand(fileName); + if (!item) { + qWarning("Debugger command %s invalid in kit %s", + qPrintable(binary), qPrintable(k->displayName())); + k->setValue(DebuggerKitInformation::id(), QVariant()); + return; + } + + k->setValue(DebuggerKitInformation::id(), item->id()); } -void DebuggerItem::setDisplayName(const QString &displayName) +// 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) { - m_displayName = displayName; + QTC_ASSERT(k, return NoDebugger); + + const DebuggerItem *item = DebuggerKitInformation::debugger(k); + if (!item) + return NoDebugger; + + if (item->command().isEmpty()) + return NoDebugger; + + unsigned result = 0; + const QFileInfo fi = item->command().toFileInfo(); + if (!fi.exists() || fi.isDir()) + result |= DebuggerNotFound; + else if (!fi.isExecutable()) + result |= DebuggerNotExecutable; + + if (!fi.exists() || fi.isDir()) { + if (item->engineType() == NoEngineType) + return NoDebugger; + + // 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; } -void DebuggerItem::setEngineType(const DebuggerEngineType &engineType) +const DebuggerItem *DebuggerKitInformation::debugger(const Kit *kit) { - m_engineType = engineType; + QTC_ASSERT(kit, return 0); + const QVariant id = kit->value(DebuggerKitInformation::id()); + return DebuggerItemManager::findById(id); } -void DebuggerItem::setCommand(const Utils::FileName &command) +bool DebuggerKitInformation::isValidDebugger(const Kit *k) { - m_command = command; + return debuggerConfigurationErrors(k) == 0; } -void DebuggerItem::setAutoDetected(bool isAutoDetected) +QList<Task> DebuggerKitInformation::validateDebugger(const Kit *k) { - m_isAutoDetected = isAutoDetected; + QList<Task> result; + + const unsigned errors = debuggerConfigurationErrors(k); + if (!errors) + return result; + + QString path; + if (const DebuggerItem *item = debugger(k)) + path = item->command().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; } -void DebuggerItem::setAbis(const QList<ProjectExplorer::Abi> &abis) +KitConfigWidget *DebuggerKitInformation::createConfigWidget(Kit *k) const { - m_abis = abis; + return new Internal::DebuggerKitConfigWidget(k, this); } -void DebuggerItem::setAbi(const Abi &abi) +KitInformation::ItemList DebuggerKitInformation::toUserOutput(const Kit *k) const { - m_abis.clear(); - m_abis.append(abi); + return ItemList() << qMakePair(tr("Debugger"), displayString(k)); } -static DebuggerItem::MatchLevel matchSingle(const Abi &debuggerAbi, const Abi &targetAbi) +FileName DebuggerKitInformation::debuggerCommand(const ProjectExplorer::Kit *k) { - if (debuggerAbi.architecture() != Abi::UnknownArchitecture - && debuggerAbi.architecture() != targetAbi.architecture()) - return DebuggerItem::DoesNotMatch; - - if (debuggerAbi.os() != Abi::UnknownOS - && debuggerAbi.os() != targetAbi.os()) - return DebuggerItem::DoesNotMatch; - - if (debuggerAbi.binaryFormat() != Abi::UnknownFormat - && debuggerAbi.binaryFormat() != targetAbi.binaryFormat()) - return DebuggerItem::DoesNotMatch; - - if (debuggerAbi.os() == Abi::WindowsOS) { - if (debuggerAbi.osFlavor() == Abi::WindowsMSysFlavor && targetAbi.osFlavor() != Abi::WindowsMSysFlavor) - return DebuggerItem::DoesNotMatch; - if (debuggerAbi.osFlavor() != Abi::WindowsMSysFlavor && targetAbi.osFlavor() == Abi::WindowsMSysFlavor) - return DebuggerItem::DoesNotMatch; - } - - if (debuggerAbi.wordWidth() == 64 && targetAbi.wordWidth() == 32) - return DebuggerItem::MatchesSomewhat; - if (debuggerAbi.wordWidth() != 0 && debuggerAbi.wordWidth() != targetAbi.wordWidth()) - return DebuggerItem::DoesNotMatch; - - return DebuggerItem::MatchesPerfectly; + const DebuggerItem *item = debugger(k); + QTC_ASSERT(item, return FileName()); + return item->command(); } -DebuggerItem::MatchLevel DebuggerItem::matchTarget(const Abi &targetAbi) const +DebuggerEngineType DebuggerKitInformation::engineType(const ProjectExplorer::Kit *k) { - MatchLevel bestMatch = DoesNotMatch; - foreach (const Abi &debuggerAbi, m_abis) { - MatchLevel currentMatch = matchSingle(debuggerAbi, targetAbi); - if (currentMatch > bestMatch) - bestMatch = currentMatch; - } - return bestMatch; + const DebuggerItem *item = debugger(k); + QTC_ASSERT(item, return NoEngineType); + return item->engineType(); } -bool Debugger::DebuggerItem::isValid() const +QString DebuggerKitInformation::displayString(const Kit *k) { - return m_engineType != NoEngineType; + const DebuggerItem *item = debugger(k); + if (!item) + return tr("No Debugger"); + QString binary = item->command().toUserOutput(); + QString name = tr("%1 Engine").arg(item->engineTypeName()); + return binary.isEmpty() ? tr("%1 <None>").arg(name) : tr("%1 using \"%2\"").arg(name, binary); } -} // namespace Debugger; - -#ifdef WITH_TESTS - -# include <QTest> -# include "debuggerplugin.h" - -void Debugger::DebuggerPlugin::testDebuggerMatching_data() +void DebuggerKitInformation::setDebugger(Kit *k, const QVariant &id) { - QTest::addColumn<QStringList>("debugger"); - QTest::addColumn<QString>("target"); - QTest::addColumn<int>("result"); - - QTest::newRow("Invalid data") - << QStringList() - << QString() - << int(DebuggerItem::DoesNotMatch); - QTest::newRow("Invalid debugger") - << QStringList() - << QString::fromLatin1("x86-linux-generic-elf-32bit") - << int(DebuggerItem::DoesNotMatch); - QTest::newRow("Invalid target") - << (QStringList() << QLatin1String("x86-linux-generic-elf-32bit")) - << QString() - << int(DebuggerItem::DoesNotMatch); - - QTest::newRow("Fuzzy match 1") - << (QStringList() << QLatin1String("unknown-unknown-unknown-unknown-0bit")) - << QString::fromLatin1("x86-linux-generic-elf-32bit") - << int(DebuggerItem::MatchesPerfectly); // Is this the expected behavior? - QTest::newRow("Fuzzy match 2") - << (QStringList() << QLatin1String("unknown-unknown-unknown-unknown-0bit")) - << QString::fromLatin1("arm-windows-msys-pe-64bit") - << int(DebuggerItem::MatchesPerfectly); // Is this the expected behavior? - - QTest::newRow("Architecture mismatch") - << (QStringList() << QLatin1String("x86-linux-generic-elf-32bit")) - << QString::fromLatin1("arm-linux-generic-elf-32bit") - << int(DebuggerItem::DoesNotMatch); - QTest::newRow("OS mismatch") - << (QStringList() << QLatin1String("x86-linux-generic-elf-32bit")) - << QString::fromLatin1("x86-macosx-generic-elf-32bit") - << int(DebuggerItem::DoesNotMatch); - QTest::newRow("Format mismatch") - << (QStringList() << QLatin1String("x86-linux-generic-elf-32bit")) - << QString::fromLatin1("x86-linux-generic-pe-32bit") - << int(DebuggerItem::DoesNotMatch); - - QTest::newRow("Linux perfect match") - << (QStringList() << QLatin1String("x86-linux-generic-elf-32bit")) - << QString::fromLatin1("x86-linux-generic-elf-32bit") - << int(DebuggerItem::MatchesPerfectly); - QTest::newRow("Linux match") - << (QStringList() << QLatin1String("x86-linux-generic-elf-64bit")) - << QString::fromLatin1("x86-linux-generic-elf-32bit") - << int(DebuggerItem::MatchesSomewhat); - - QTest::newRow("Windows perfect match 1") - << (QStringList() << QLatin1String("x86-windows-msvc2013-pe-64bit")) - << QString::fromLatin1("x86-windows-msvc2013-pe-64bit") - << int(DebuggerItem::MatchesPerfectly); - QTest::newRow("Windows perfect match 2") - << (QStringList() << QLatin1String("x86-windows-msvc2013-pe-64bit")) - << QString::fromLatin1("x86-windows-msvc2012-pe-64bit") - << int(DebuggerItem::MatchesPerfectly); - QTest::newRow("Windows match 1") - << (QStringList() << QLatin1String("x86-windows-msvc2013-pe-64bit")) - << QString::fromLatin1("x86-windows-msvc2013-pe-32bit") - << int(DebuggerItem::MatchesSomewhat); - QTest::newRow("Windows match 2") - << (QStringList() << QLatin1String("x86-windows-msvc2013-pe-64bit")) - << QString::fromLatin1("x86-windows-msvc2012-pe-32bit") - << int(DebuggerItem::MatchesSomewhat); - QTest::newRow("Windows mismatch on word size") - << (QStringList() << QLatin1String("x86-windows-msvc2013-pe-32bit")) - << QString::fromLatin1("x86-windows-msvc2013-pe-64bit") - << int(DebuggerItem::DoesNotMatch); - QTest::newRow("Windows mismatch on osflavor 1") - << (QStringList() << QLatin1String("x86-windows-msvc2013-pe-32bit")) - << QString::fromLatin1("x86-windows-msys-pe-64bit") - << int(DebuggerItem::DoesNotMatch); - QTest::newRow("Windows mismatch on osflavor 2") - << (QStringList() << QLatin1String("x86-windows-msys-pe-32bit")) - << QString::fromLatin1("x86-windows-msvc2010-pe-64bit") - << int(DebuggerItem::DoesNotMatch); + // Only register reasonably complete debuggers. + QTC_ASSERT(DebuggerItemManager::findById(id), return); + k->setValue(DebuggerKitInformation::id(), id); } -void Debugger::DebuggerPlugin::testDebuggerMatching() +Core::Id DebuggerKitInformation::id() { - QFETCH(QStringList, debugger); - QFETCH(QString, target); - QFETCH(int, result); - - DebuggerItem::MatchLevel expectedLevel = static_cast<DebuggerItem::MatchLevel>(result); - - QList<Abi> debuggerAbis; - foreach (const QString &abi, debugger) - debuggerAbis << Abi(abi); - - DebuggerItem item; - item.setAbis(debuggerAbis); - - DebuggerItem::MatchLevel level = item.matchTarget(Abi(target)); - - QCOMPARE(expectedLevel, level); + return "Debugger.Information"; } -#endif + +} // namespace Debugger diff --git a/src/plugins/debugger/debuggerkitinformation.h b/src/plugins/debugger/debuggerkitinformation.h index 4a185d79e3..3e03e19d2e 100644 --- a/src/plugins/debugger/debuggerkitinformation.h +++ b/src/plugins/debugger/debuggerkitinformation.h @@ -32,6 +32,7 @@ #include "debugger_global.h" #include "debuggerconstants.h" +#include "debuggeritem.h" #include <projectexplorer/abi.h> #include <projectexplorer/kitinformation.h> @@ -40,102 +41,6 @@ namespace Debugger { -namespace Internal { class DebuggerItemModel; } - -// ----------------------------------------------------------------------- -// DebuggerItem -// ----------------------------------------------------------------------- - -class DEBUGGER_EXPORT DebuggerItem -{ -public: - DebuggerItem(); - DebuggerItem(const QVariantMap &data); - - bool canClone() const { return true; } - bool isValid() const; - QString engineTypeName() const; - - QVariantMap toMap() const; - void reinitializeFromFile(); - - QVariant id() const { return m_id; } - - QString displayName() const { return m_displayName; } - void setDisplayName(const QString &displayName); - - DebuggerEngineType engineType() const { return m_engineType; } - void setEngineType(const DebuggerEngineType &engineType); - - Utils::FileName command() const { return m_command; } - void setCommand(const Utils::FileName &command); - - bool isAutoDetected() const { return m_isAutoDetected; } - void setAutoDetected(bool isAutoDetected); - - QList<ProjectExplorer::Abi> abis() const { return m_abis; } - void setAbis(const QList<ProjectExplorer::Abi> &abis); - void setAbi(const ProjectExplorer::Abi &abi); - - enum MatchLevel { DoesNotMatch, MatchesSomewhat, MatchesPerfectly }; - MatchLevel matchTarget(const ProjectExplorer::Abi &targetAbi) const; - - QStringList abiNames() const; - -private: - QVariant m_id; - QString m_displayName; - DebuggerEngineType m_engineType; - Utils::FileName m_command; - bool m_isAutoDetected; - QList<ProjectExplorer::Abi> m_abis; -}; - -// ----------------------------------------------------------------------- -// DebuggerItemManager -// ----------------------------------------------------------------------- - -class DEBUGGER_EXPORT DebuggerItemManager : public QObject -{ - Q_OBJECT - -public: - static QObject *instance(); - ~DebuggerItemManager(); - - static QList<DebuggerItem> debuggers(); - static Debugger::Internal::DebuggerItemModel *model(); - - static QVariant registerDebugger(const DebuggerItem &item); - static void deregisterDebugger(const DebuggerItem &item); - - static const DebuggerItem *findByCommand(const Utils::FileName &command); - static const DebuggerItem *findById(const QVariant &id); - - static void restoreDebuggers(); - static QString uniqueDisplayName(const QString &base); - static void setItemData(const QVariant &id, const QString& displayName, const Utils::FileName &fileName); - - static void removeDebugger(const QVariant &id); - static QVariant addDebugger(const DebuggerItem &item); - -public slots: - void saveDebuggers(); - -private: - explicit DebuggerItemManager(QObject *parent = 0); - static void autoDetectGdbOrLldbDebuggers(); - static void autoDetectCdbDebuggers(); - static void readLegacyDebuggers(); - - static Utils::PersistentSettingsWriter *m_writer; - static QList<DebuggerItem> m_debuggers; - static Debugger::Internal::DebuggerItemModel *m_model; - - friend class Internal::DebuggerItemModel; - friend class DebuggerPlugin; // Enable constrcutor for DebuggerPlugin -}; - class DEBUGGER_EXPORT DebuggerKitInformation : public ProjectExplorer::KitInformation { Q_OBJECT diff --git a/src/plugins/debugger/debuggeroptionspage.cpp b/src/plugins/debugger/debuggeroptionspage.cpp new file mode 100644 index 0000000000..893b28582b --- /dev/null +++ b/src/plugins/debugger/debuggeroptionspage.cpp @@ -0,0 +1,350 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of Qt Creator. +** +** 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. +** +** GNU Lesser General Public License Usage +** 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 +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#include "debuggeroptionspage.h" + +#include "debuggeritemmanager.h" +#include "debuggeritemmodel.h" + +#include <projectexplorer/projectexplorerconstants.h> + +#include <utils/detailswidget.h> +#include <utils/pathchooser.h> +#include <utils/qtcassert.h> + +#include <QFormLayout> +#include <QHeaderView> +#include <QLabel> +#include <QLineEdit> +#include <QObject> +#include <QPushButton> +#include <QTreeView> +#include <QWidget> + +using namespace Utils; + +namespace Debugger { +namespace Internal { + +static const char debuggingToolsWikiLinkC[] = "http://qt-project.org/wiki/Qt_Creator_Windows_Debugging"; + +// ----------------------------------------------------------------------- +// DebuggerItemConfigWidget +// ----------------------------------------------------------------------- + +class DebuggerItemConfigWidget : public QWidget +{ + Q_DECLARE_TR_FUNCTIONS(Debugger::Internal::DebuggerItemConfigWidget) + +public: + explicit DebuggerItemConfigWidget(); + void loadItem(); + void saveItem(); + void connectDirty(); + void disconnectDirty(); + +private: + QLineEdit *m_displayNameLineEdit; + QLabel *m_cdbLabel; + PathChooser *m_binaryChooser; + QLineEdit *m_abis; +}; + +DebuggerItemConfigWidget::DebuggerItemConfigWidget() +{ + m_displayNameLineEdit = new QLineEdit(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); + + QFormLayout *formLayout = new QFormLayout(this); + formLayout->setFieldGrowthPolicy(QFormLayout::AllNonFixedFieldsGrow); + 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); + + connectDirty(); +} + +void DebuggerItemConfigWidget::connectDirty() +{ + DebuggerItemModel *model = DebuggerItemManager::model(); + connect(m_displayNameLineEdit, SIGNAL(textChanged(QString)), + model, SLOT(markCurrentDirty())); + connect(m_binaryChooser, SIGNAL(changed(QString)), + model, SLOT(markCurrentDirty())); +} + +void DebuggerItemConfigWidget::disconnectDirty() +{ + DebuggerItemModel *model = DebuggerItemManager::model(); + disconnect(m_displayNameLineEdit, SIGNAL(textChanged(QString)), + model, SLOT(markCurrentDirty())); + disconnect(m_binaryChooser, SIGNAL(changed(QString)), + model, SLOT(markCurrentDirty())); +} + +void DebuggerItemConfigWidget::loadItem() +{ + DebuggerItemModel *model = DebuggerItemManager::model(); + const DebuggerItem *item = DebuggerItemManager::findById(model->currentDebugger()); + if (!item) + return; + + disconnectDirty(); + m_displayNameLineEdit->setEnabled(!item->isAutoDetected()); + m_displayNameLineEdit->setText(item->displayName()); + + m_binaryChooser->setEnabled(!item->isAutoDetected()); + m_binaryChooser->setFileName(item->command()); + connectDirty(); + + QString text; + QString versionCommand; + if (item->engineType() == CdbEngineType) { +#ifdef Q_OS_WIN + const bool is64bit = winIs64BitSystem(); +#else + const bool is64bit = false; +#endif + const QString versionString = is64bit ? tr("64-bit version") : tr("32-bit version"); + //: Label text for path configuration. %2 is "x-bit version". + text = tr("<html><body><p>Specify the path to the " + "<a href=\"%1\">Windows Console Debugger executable</a>" + " (%2) here.</p>""</body></html>"). + arg(QLatin1String(debuggingToolsWikiLinkC), versionString); + versionCommand = QLatin1String("-version"); + } else { + versionCommand = QLatin1String("--version"); + } + + m_cdbLabel->setText(text); + m_cdbLabel->setVisible(!text.isEmpty()); + m_binaryChooser->setCommandVersionArguments(QStringList(versionCommand)); + + m_abis->setText(item->abiNames().join(QLatin1String(", "))); +} + +void DebuggerItemConfigWidget::saveItem() +{ + DebuggerItemModel *model = DebuggerItemManager::model(); + const DebuggerItem *item = DebuggerItemManager::findById(model->currentDebugger()); + QTC_ASSERT(item, return); + DebuggerItemManager::setItemData(item->id(), m_displayNameLineEdit->text(), + m_binaryChooser->fileName()); +} + +// -------------------------------------------------------------------------- +// DebuggerOptionsPage +// -------------------------------------------------------------------------- + +DebuggerOptionsPage::DebuggerOptionsPage() +{ + m_model = 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_model = DebuggerItemManager::model(); + + m_debuggerView = new QTreeView(m_configWidget); + m_debuggerView->setModel(m_model); + 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(); + debuggerModelChanged(); +} + +void DebuggerOptionsPage::cloneDebugger() +{ + const DebuggerItem *item = DebuggerItemManager::findById(m_model->currentDebugger()); + QTC_ASSERT(item, return); + DebuggerItem newItem; + newItem.setCommand(item->command()); + newItem.setEngineType(item->engineType()); + newItem.setAbis(item->abis()); + newItem.setDisplayName(DebuggerItemManager::uniqueDisplayName(tr("Clone of %1").arg(item->displayName()))); + newItem.setAutoDetected(false); + DebuggerItemManager::addDebugger(newItem); + m_debuggerView->setCurrentIndex(m_model->lastIndex()); +} + +void DebuggerOptionsPage::addDebugger() +{ + DebuggerItem item; + item.setEngineType(NoEngineType); + item.setDisplayName(DebuggerItemManager::uniqueDisplayName(tr("New Debugger"))); + item.setAutoDetected(false); + DebuggerItemManager::addDebugger(item); + m_debuggerView->setCurrentIndex(m_model->lastIndex()); +} + +void DebuggerOptionsPage::removeDebugger() +{ + QVariant id = m_model->currentDebugger(); + DebuggerItemManager::removeDebugger(id); + m_debuggerView->setCurrentIndex(m_model->lastIndex()); +} + +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_model->setCurrentIndex(mi); + + m_itemConfigWidget->loadItem(); + m_container->setVisible(m_model->currentDebugger().isValid()); + updateState(); +} + +void DebuggerOptionsPage::debuggerModelChanged() +{ + QTC_ASSERT(m_container, return); + + m_itemConfigWidget->loadItem(); + m_container->setVisible(m_model->currentDebugger().isValid()); + m_debuggerView->setCurrentIndex(m_model->currentIndex()); + updateState(); +} + +void DebuggerOptionsPage::updateState() +{ + if (!m_cloneButton) + return; + + bool canCopy = false; + bool canDelete = false; + + if (const DebuggerItem *item = DebuggerItemManager::findById(m_model->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 +} // namespace Debugger diff --git a/src/plugins/debugger/debuggeroptionspage.h b/src/plugins/debugger/debuggeroptionspage.h new file mode 100644 index 0000000000..fd1782f4b4 --- /dev/null +++ b/src/plugins/debugger/debuggeroptionspage.h @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of Qt Creator. +** +** 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. +** +** GNU Lesser General Public License Usage +** 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 +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#ifndef DEBUGGER_DEBUGGEROPTIONSPAGE_H +#define DEBUGGER_DEBUGGEROPTIONSPAGE_H + +#include <coreplugin/dialogs/ioptionspage.h> + +QT_BEGIN_NAMESPACE +class QPushButton; +class QTreeView; +class QWidget; +QT_END_NAMESPACE + +namespace Utils { class DetailsWidget; } + +namespace Debugger { +namespace Internal { + +class DebuggerItemModel; +class DebuggerItemConfigWidget; +class DebuggerKitConfigWidget; + +// -------------------------------------------------------------------------- +// DebuggerOptionsPage +// -------------------------------------------------------------------------- + +class DebuggerOptionsPage : public Core::IOptionsPage +{ + Q_OBJECT + +public: + DebuggerOptionsPage(); + + QWidget *createPage(QWidget *parent); + void apply(); + void finish(); + bool matches(const QString &) const; + +private slots: + void debuggerSelectionChanged(); + void debuggerModelChanged(); + void updateState(); + void cloneDebugger(); + void addDebugger(); + void removeDebugger(); + +private: + QWidget *m_configWidget; + QString m_searchKeywords; + + DebuggerItemModel *m_model; + DebuggerItemConfigWidget *m_itemConfigWidget; + QTreeView *m_debuggerView; + Utils::DetailsWidget *m_container; + QPushButton *m_addButton; + QPushButton *m_cloneButton; + QPushButton *m_delButton; +}; + +} // namespace Internal +} // namespace Debugger + +#endif // DEBUGGER_DEBUGGEROPTIONSPAGE_H diff --git a/src/plugins/debugger/debuggerplugin.cpp b/src/plugins/debugger/debuggerplugin.cpp index a01bdc9b63..67953afcfc 100644 --- a/src/plugins/debugger/debuggerplugin.cpp +++ b/src/plugins/debugger/debuggerplugin.cpp @@ -36,11 +36,13 @@ #include "debuggerkitconfigwidget.h" #include "debuggerdialogs.h" #include "debuggerengine.h" +#include "debuggeritemmanager.h" #include "debuggermainwindow.h" #include "debuggerrunner.h" #include "debuggerrunconfigurationaspect.h" #include "debuggerruncontrolfactory.h" #include "debuggerstringutils.h" +#include "debuggeroptionspage.h" #include "debuggerkitinformation.h" #include "memoryagent.h" #include "breakhandler.h" diff --git a/src/plugins/ios/iosconfigurations.cpp b/src/plugins/ios/iosconfigurations.cpp index ebfca0293f..1eaa690bc9 100644 --- a/src/plugins/ios/iosconfigurations.cpp +++ b/src/plugins/ios/iosconfigurations.cpp @@ -42,6 +42,7 @@ #include <projectexplorer/toolchain.h> #include <projectexplorer/gcctoolchain.h> #include <projectexplorer/projectexplorerconstants.h> +#include <debugger/debuggeritemmanager.h> #include <debugger/debuggerkitinformation.h> #include <qtsupport/baseqtversion.h> #include <qtsupport/qtkitinformation.h> diff --git a/src/plugins/qnx/blackberryconfiguration.cpp b/src/plugins/qnx/blackberryconfiguration.cpp index 78b01a30d8..cc45468e53 100644 --- a/src/plugins/qnx/blackberryconfiguration.cpp +++ b/src/plugins/qnx/blackberryconfiguration.cpp @@ -45,6 +45,7 @@ #include <qt4projectmanager/qmakekitinformation.h> +#include <debugger/debuggeritemmanager.h> #include <debugger/debuggerkitinformation.h> #include <QFileInfo> |