/**************************************************************************** ** ** Copyright (C) 2014 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 "debuggeritemmanager.h" #include "debuggerkitconfigwidget.h" #include "projectexplorer/toolchain.h" #include "projectexplorer/projectexplorerconstants.h" #include #include #include using namespace ProjectExplorer; using namespace Utils; namespace Debugger { // -------------------------------------------------------------------------- // 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: // {75ecf347-f221-44c3-b613-ea1d29929cd4} // Before we had: // // /data/dev/debugger/gdb-git/gdb/gdb // 1 // // Or for force auto-detected CDB // // auto // 4 // if (rawId.isNull()) { // Initial setup of a kit detection = NotDetected; } else if (rawId.type() == QVariant::String) { detection = DetectedById; } else { QMap 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 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 DebuggerKitInformation::validateDebugger(const Kit *k) { QList 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 ").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"; } } // namespace Debugger