2012-10-02 09:12:39 +02:00
|
|
|
/****************************************************************************
|
2012-04-24 15:49:09 +02:00
|
|
|
**
|
2013-01-28 17:12:19 +01:00
|
|
|
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
|
2012-10-02 09:12:39 +02:00
|
|
|
** Contact: http://www.qt-project.org/legal
|
2012-04-24 15:49:09 +02:00
|
|
|
**
|
2012-10-02 09:12:39 +02:00
|
|
|
** This file is part of Qt Creator.
|
2012-04-24 15:49:09 +02:00
|
|
|
**
|
2012-10-02 09:12:39 +02:00
|
|
|
** 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.
|
2012-04-24 15:49:09 +02:00
|
|
|
**
|
|
|
|
|
** GNU Lesser General Public License Usage
|
2012-10-02 09:12:39 +02:00
|
|
|
** 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
|
2012-04-24 15:49:09 +02:00
|
|
|
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
|
|
|
|
**
|
2012-10-02 09:12:39 +02:00
|
|
|
****************************************************************************/
|
2013-09-25 11:38:26 +02:00
|
|
|
|
|
|
|
|
#include "debuggerkitinformation.h"
|
2013-10-25 13:47:08 +02:00
|
|
|
|
|
|
|
|
#include "debuggeritemmanager.h"
|
2013-09-25 11:38:26 +02:00
|
|
|
#include "debuggerkitconfigwidget.h"
|
|
|
|
|
|
2013-10-25 13:47:08 +02:00
|
|
|
#include "projectexplorer/toolchain.h"
|
|
|
|
|
#include "projectexplorer/projectexplorerconstants.h"
|
|
|
|
|
|
2013-09-25 11:38:26 +02:00
|
|
|
#include <utils/fileutils.h>
|
2013-10-25 13:47:08 +02:00
|
|
|
#include <utils/qtcassert.h>
|
2013-09-25 11:38:26 +02:00
|
|
|
|
2013-10-25 13:47:08 +02:00
|
|
|
#include <QFileInfo>
|
2013-09-25 11:38:26 +02:00
|
|
|
|
|
|
|
|
using namespace ProjectExplorer;
|
|
|
|
|
using namespace Utils;
|
|
|
|
|
|
|
|
|
|
namespace Debugger {
|
|
|
|
|
|
|
|
|
|
// --------------------------------------------------------------------------
|
2013-10-25 13:47:08 +02:00
|
|
|
// DebuggerKitInformation
|
2013-09-25 11:38:26 +02:00
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
|
|
2013-10-25 13:47:08 +02:00
|
|
|
DebuggerKitInformation::DebuggerKitInformation()
|
2013-09-25 11:38:26 +02:00
|
|
|
{
|
2013-10-25 13:47:08 +02:00
|
|
|
setObjectName(QLatin1String("DebuggerKitInformation"));
|
|
|
|
|
setId(DebuggerKitInformation::id());
|
|
|
|
|
setPriority(28000);
|
2013-09-25 11:38:26 +02:00
|
|
|
}
|
|
|
|
|
|
2013-10-25 13:47:08 +02:00
|
|
|
QVariant DebuggerKitInformation::defaultValue(Kit *k) const
|
2013-10-25 12:18:36 +02:00
|
|
|
{
|
2013-10-25 13:47:08 +02:00
|
|
|
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();
|
2013-10-25 12:18:36 +02:00
|
|
|
}
|
|
|
|
|
|
2013-10-25 13:47:08 +02:00
|
|
|
void DebuggerKitInformation::setup(Kit *k)
|
2013-09-25 11:38:26 +02:00
|
|
|
{
|
2013-10-25 13:47:08 +02:00
|
|
|
// 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());
|
2013-10-16 13:30:15 +02:00
|
|
|
} else {
|
2013-10-25 13:47:08 +02:00
|
|
|
detection = DetectedByFile;
|
|
|
|
|
fileName = FileName::fromUserInput(binary);
|
2013-10-16 13:30:15 +02:00
|
|
|
}
|
2013-09-25 11:38:26 +02:00
|
|
|
}
|
2013-10-25 13:47:08 +02:00
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
}
|
2013-09-25 11:38:26 +02:00
|
|
|
}
|
2013-10-25 13:47:08 +02:00
|
|
|
|
|
|
|
|
// If we have an existing debugger with matching id _and_
|
|
|
|
|
// matching target ABI we are fine.
|
|
|
|
|
if (bestItem) {
|
|
|
|
|
k->setValue(DebuggerKitInformation::id(), bestItem->id());
|
2013-09-25 11:38:26 +02:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2013-10-25 13:47:08 +02:00
|
|
|
// 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();
|
|
|
|
|
}
|
2013-09-25 11:38:26 +02:00
|
|
|
}
|
|
|
|
|
|
2013-10-25 13:47:08 +02:00
|
|
|
k->setValue(DebuggerKitInformation::id(), bestId);
|
2013-09-25 11:38:26 +02:00
|
|
|
}
|
|
|
|
|
|
2013-10-25 13:47:08 +02:00
|
|
|
|
|
|
|
|
// This handles the upgrade path from 2.8 to 3.0
|
|
|
|
|
void DebuggerKitInformation::fix(Kit *k)
|
2013-09-25 11:38:26 +02:00
|
|
|
{
|
2013-10-25 13:47:08 +02:00
|
|
|
// 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());
|
2013-09-25 11:38:26 +02:00
|
|
|
}
|
|
|
|
|
|
2013-10-25 13:47:08 +02:00
|
|
|
// 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)
|
2013-09-25 11:38:26 +02:00
|
|
|
{
|
2013-10-25 13:47:08 +02:00
|
|
|
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;
|
2013-09-25 11:38:26 +02:00
|
|
|
}
|
|
|
|
|
|
2013-10-25 13:47:08 +02:00
|
|
|
const DebuggerItem *DebuggerKitInformation::debugger(const Kit *kit)
|
2013-09-25 11:38:26 +02:00
|
|
|
{
|
2013-10-25 13:47:08 +02:00
|
|
|
QTC_ASSERT(kit, return 0);
|
|
|
|
|
const QVariant id = kit->value(DebuggerKitInformation::id());
|
|
|
|
|
return DebuggerItemManager::findById(id);
|
2013-09-25 11:38:26 +02:00
|
|
|
}
|
|
|
|
|
|
2013-10-25 13:47:08 +02:00
|
|
|
bool DebuggerKitInformation::isValidDebugger(const Kit *k)
|
2013-09-25 11:38:26 +02:00
|
|
|
{
|
2013-10-25 13:47:08 +02:00
|
|
|
return debuggerConfigurationErrors(k) == 0;
|
2013-09-25 11:38:26 +02:00
|
|
|
}
|
|
|
|
|
|
2013-10-25 13:47:08 +02:00
|
|
|
QList<Task> DebuggerKitInformation::validateDebugger(const Kit *k)
|
2013-09-25 11:38:26 +02:00
|
|
|
{
|
2013-10-25 13:47:08 +02:00
|
|
|
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;
|
2013-09-25 11:38:26 +02:00
|
|
|
}
|
|
|
|
|
|
2013-10-25 13:47:08 +02:00
|
|
|
KitConfigWidget *DebuggerKitInformation::createConfigWidget(Kit *k) const
|
2013-09-25 11:38:26 +02:00
|
|
|
{
|
2013-10-25 13:47:08 +02:00
|
|
|
return new Internal::DebuggerKitConfigWidget(k, this);
|
2013-09-25 11:38:26 +02:00
|
|
|
}
|
|
|
|
|
|
2013-10-25 13:47:08 +02:00
|
|
|
KitInformation::ItemList DebuggerKitInformation::toUserOutput(const Kit *k) const
|
2013-09-25 11:38:26 +02:00
|
|
|
{
|
2013-10-25 13:47:08 +02:00
|
|
|
return ItemList() << qMakePair(tr("Debugger"), displayString(k));
|
2013-09-25 11:38:26 +02:00
|
|
|
}
|
|
|
|
|
|
2013-10-25 13:47:08 +02:00
|
|
|
FileName DebuggerKitInformation::debuggerCommand(const ProjectExplorer::Kit *k)
|
2013-10-17 12:48:16 +02:00
|
|
|
{
|
2013-10-25 13:47:08 +02:00
|
|
|
const DebuggerItem *item = debugger(k);
|
|
|
|
|
QTC_ASSERT(item, return FileName());
|
|
|
|
|
return item->command();
|
2013-10-17 12:48:16 +02:00
|
|
|
}
|
|
|
|
|
|
2013-10-25 13:47:08 +02:00
|
|
|
DebuggerEngineType DebuggerKitInformation::engineType(const ProjectExplorer::Kit *k)
|
2013-10-17 12:48:16 +02:00
|
|
|
{
|
2013-10-25 13:47:08 +02:00
|
|
|
const DebuggerItem *item = debugger(k);
|
|
|
|
|
QTC_ASSERT(item, return NoEngineType);
|
|
|
|
|
return item->engineType();
|
2013-10-17 12:48:16 +02:00
|
|
|
}
|
|
|
|
|
|
2013-10-25 13:47:08 +02:00
|
|
|
QString DebuggerKitInformation::displayString(const Kit *k)
|
2013-09-25 11:38:26 +02:00
|
|
|
{
|
2013-10-25 13:47:08 +02:00
|
|
|
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);
|
2013-09-25 11:38:26 +02:00
|
|
|
}
|
|
|
|
|
|
2013-10-25 13:47:08 +02:00
|
|
|
void DebuggerKitInformation::setDebugger(Kit *k, const QVariant &id)
|
2013-10-22 17:23:34 +02:00
|
|
|
{
|
2013-10-25 13:47:08 +02:00
|
|
|
// Only register reasonably complete debuggers.
|
|
|
|
|
QTC_ASSERT(DebuggerItemManager::findById(id), return);
|
|
|
|
|
k->setValue(DebuggerKitInformation::id(), id);
|
2013-10-22 17:23:34 +02:00
|
|
|
}
|
|
|
|
|
|
2013-10-25 13:47:08 +02:00
|
|
|
Core::Id DebuggerKitInformation::id()
|
2013-10-22 17:23:34 +02:00
|
|
|
{
|
2013-10-25 13:47:08 +02:00
|
|
|
return "Debugger.Information";
|
2013-10-22 17:23:34 +02:00
|
|
|
}
|
2013-10-25 13:47:08 +02:00
|
|
|
|
|
|
|
|
} // namespace Debugger
|