1
/****************************************************************************
3
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
4
** Contact: http://www.qt-project.org/legal
6
** This file is part of Qt Creator.
8
** Commercial License Usage
9
** Licensees holding valid commercial Qt licenses may use this file in
10
** accordance with the commercial license agreement provided with the
11
** Software or, alternatively, in accordance with the terms contained in
12
** a written agreement between you and Digia. For licensing terms and
13
** conditions see http://qt.digia.com/licensing. For further information
14
** use the contact form at http://qt.digia.com/contact-us.
16
** GNU Lesser General Public License Usage
17
** Alternatively, this file may be used under the terms of the GNU Lesser
18
** General Public License version 2.1 as published by the Free Software
19
** Foundation and appearing in the file LICENSE.LGPL included in the
20
** packaging of this file. Please review the following information to
21
** ensure the GNU Lesser General Public License version 2.1 requirements
22
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24
** In addition, as a special exception, Digia gives you certain additional
25
** rights. These rights are described in the Digia Qt LGPL Exception
26
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28
****************************************************************************/
30
#include "debuggeritemmanager.h"
32
#include "debuggeritemmodel.h"
33
#include "debuggerkitinformation.h"
35
#include <coreplugin/icore.h>
37
#include <utils/environment.h>
38
#include <utils/fileutils.h>
39
#include <utils/persistentsettings.h>
40
#include <utils/qtcassert.h>
41
#include <utils/hostosinfo.h>
48
using namespace ProjectExplorer;
49
using namespace Utils;
53
static const char DEBUGGER_COUNT_KEY[] = "DebuggerItem.Count";
54
static const char DEBUGGER_DATA_KEY[] = "DebuggerItem.";
55
static const char DEBUGGER_LEGACY_FILENAME[] = "/qtcreator/profiles.xml";
56
static const char DEBUGGER_FILE_VERSION_KEY[] = "Version";
57
static const char DEBUGGER_FILENAME[] = "/qtcreator/debuggers.xml";
59
// --------------------------------------------------------------------------
60
// DebuggerItemManager
61
// --------------------------------------------------------------------------
63
static DebuggerItemManager *m_instance = 0;
65
static FileName userSettingsFileName()
67
QFileInfo settingsLocation(Core::ICore::settings()->fileName());
68
return FileName::fromString(settingsLocation.absolutePath() + QLatin1String(DEBUGGER_FILENAME));
71
static void readDebuggers(const FileName &fileName, bool isSystem)
73
PersistentSettingsReader reader;
74
if (!reader.load(fileName))
76
QVariantMap data = reader.restoreValues();
79
int version = data.value(QLatin1String(DEBUGGER_FILE_VERSION_KEY), 0).toInt();
83
int count = data.value(QLatin1String(DEBUGGER_COUNT_KEY), 0).toInt();
84
for (int i = 0; i < count; ++i) {
85
const QString key = QString::fromLatin1(DEBUGGER_DATA_KEY) + QString::number(i);
86
if (!data.contains(key))
88
const QVariantMap dbMap = data.value(key).toMap();
89
DebuggerItem item(dbMap);
91
item.setAutoDetected(true);
92
// SDK debuggers are always considered to be up-to-date, so no need to recheck them.
95
if (item.isAutoDetected() && (!item.isValid() || item.engineType() == NoEngineType)) {
96
qWarning() << QString::fromLatin1("DebuggerItem \"%1\" (%2) dropped since it is not valid")
97
.arg(item.command().toString()).arg(item.id().toString());
101
DebuggerItemManager::registerDebugger(item);
105
QList<DebuggerItem> DebuggerItemManager::m_debuggers;
106
PersistentSettingsWriter * DebuggerItemManager::m_writer = 0;
108
DebuggerItemManager::DebuggerItemManager(QObject *parent)
112
m_writer = new PersistentSettingsWriter(userSettingsFileName(), QLatin1String("QtCreatorDebugger"));
113
connect(Core::ICore::instance(), SIGNAL(saveSettingsRequested()),
114
this, SLOT(saveDebuggers()));
117
QObject *DebuggerItemManager::instance()
122
DebuggerItemManager::~DebuggerItemManager()
124
disconnect(Core::ICore::instance(), SIGNAL(saveSettingsRequested()),
125
this, SLOT(saveDebuggers()));
129
QList<DebuggerItem> DebuggerItemManager::debuggers()
134
void DebuggerItemManager::autoDetectCdbDebuggers()
136
QList<FileName> cdbs;
138
QStringList programDirs;
139
programDirs.append(QString::fromLocal8Bit(qgetenv("ProgramFiles")));
140
programDirs.append(QString::fromLocal8Bit(qgetenv("ProgramFiles(x86)")));
141
programDirs.append(QString::fromLocal8Bit(qgetenv("ProgramW6432")));
143
foreach (const QString &dirName, programDirs) {
144
if (dirName.isEmpty())
147
// Windows SDK's starting from version 8 live in
148
// "ProgramDir\Windows Kits\<version>"
149
const QString windowsKitsFolderName = QLatin1String("Windows Kits");
150
if (dir.exists(windowsKitsFolderName)) {
151
QDir windowKitsFolder = dir;
152
if (windowKitsFolder.cd(windowsKitsFolderName)) {
153
// Check in reverse order (latest first)
154
const QFileInfoList kitFolders =
155
windowKitsFolder.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot,
156
QDir::Time | QDir::Reversed);
157
foreach (const QFileInfo &kitFolderFi, kitFolders) {
158
const QString path = kitFolderFi.absoluteFilePath();
159
const QFileInfo cdb32(path + QLatin1String("/Debuggers/x86/cdb.exe"));
160
if (cdb32.isExecutable())
161
cdbs.append(FileName::fromString(cdb32.absoluteFilePath()));
162
const QFileInfo cdb64(path + QLatin1String("/Debuggers/x64/cdb.exe"));
163
if (cdb64.isExecutable())
164
cdbs.append(FileName::fromString(cdb64.absoluteFilePath()));
169
// Pre Windows SDK 8: Check 'Debugging Tools for Windows'
170
foreach (const QFileInfo &fi, dir.entryInfoList(QStringList(QLatin1String("Debugging Tools for Windows*")),
171
QDir::Dirs | QDir::NoDotAndDotDot)) {
172
FileName filePath(fi);
173
filePath.appendPath(QLatin1String("cdb.exe"));
174
if (!cdbs.contains(filePath))
175
cdbs.append(filePath);
179
foreach (const FileName &cdb, cdbs) {
180
if (findByCommand(cdb))
184
item.setAutoDetected(true);
185
item.setAbis(Abi::abisOfBinary(cdb));
186
item.setCommand(cdb);
187
item.setEngineType(CdbEngineType);
188
item.setDisplayName(uniqueDisplayName(tr("Auto-detected CDB at %1").arg(cdb.toUserOutput())));
193
void DebuggerItemManager::autoDetectGdbOrLldbDebuggers()
196
filters.append(QLatin1String("gdb-i686-pc-mingw32"));
197
filters.append(QLatin1String("gdb"));
198
filters.append(QLatin1String("lldb"));
199
filters.append(QLatin1String("lldb-*"));
201
// DebuggerItem result;
202
// result.setAutoDetected(true);
203
// result.setDisplayName(tr("Auto-detected for Tool Chain %1").arg(tc->displayName()));
205
// Check suggestions from the SDK.
206
Environment env = Environment::systemEnvironment();
208
tc->addToEnvironment(env); // Find MinGW gdb in toolchain environment.
209
QString path = tc->suggestedDebugger().toString();
210
if (!path.isEmpty()) {
211
const QFileInfo fi(path);
212
if (!fi.isAbsolute())
213
path = env.searchInPath(path);
214
result.command = FileName::fromString(path);
215
result.engineType = engineTypeFromBinary(path);
216
return maybeAddDebugger(result, false);
221
QFileInfoList suspects;
223
if (HostOsInfo::isMacHost()) {
225
lldbInfo.start(QLatin1String("xcrun"), QStringList() << QLatin1String("--find")
226
<< QLatin1String("lldb"));
227
if (!lldbInfo.waitForFinished(2000)) {
229
lldbInfo.waitForFinished();
231
QByteArray lPath = lldbInfo.readAll();
232
suspects.append(QFileInfo(QString::fromLocal8Bit(lPath.data(), lPath.size() -1)));
236
QStringList path = Environment::systemEnvironment().path();
237
foreach (const QString &base, path) {
239
dir.setNameFilters(filters);
240
suspects += dir.entryInfoList();
243
foreach (const QFileInfo &fi, suspects) {
245
FileName command = FileName::fromString(fi.absoluteFilePath());
246
if (findByCommand(command))
250
item.setCommand(command);
251
item.reinitializeFromFile();
252
//: %1: Debugger engine type (GDB, LLDB, CDB...), %2: Path
253
item.setDisplayName(tr("System %1 at %2")
254
.arg(item.engineTypeName()).arg(QDir::toNativeSeparators(fi.absoluteFilePath())));
255
item.setAutoDetected(true);
261
void DebuggerItemManager::readLegacyDebuggers()
263
QFileInfo systemLocation(Core::ICore::settings(QSettings::SystemScope)->fileName());
264
readLegacyDebuggers(FileName::fromString(systemLocation.absolutePath() + QLatin1String(DEBUGGER_LEGACY_FILENAME)));
265
QFileInfo userLocation(Core::ICore::settings()->fileName());
266
readLegacyDebuggers(FileName::fromString(userLocation.absolutePath() + QLatin1String(DEBUGGER_LEGACY_FILENAME)));
269
void DebuggerItemManager::readLegacyDebuggers(const FileName &file)
271
PersistentSettingsReader reader;
272
if (!reader.load(file))
275
foreach (const QVariant &v, reader.restoreValues()) {
276
QVariantMap data1 = v.toMap();
277
QString kitName = data1.value(QLatin1String("PE.Profile.Name")).toString();
278
QVariantMap data2 = data1.value(QLatin1String("PE.Profile.Data")).toMap();
279
QVariant v3 = data2.value(DebuggerKitInformation::id().toString());
281
if (v3.type() == QVariant::String)
284
fn = v3.toMap().value(QLatin1String("Binary")).toString();
287
if (fn.startsWith(QLatin1Char('{')))
289
if (fn == QLatin1String("auto"))
291
FileName command = FileName::fromUserInput(fn);
292
if (findByCommand(command))
296
item.setCommand(command);
297
item.setAutoDetected(true);
298
item.reinitializeFromFile();
299
item.setDisplayName(tr("Extracted from Kit %1").arg(kitName));
304
const DebuggerItem *DebuggerItemManager::findByCommand(const FileName &command)
306
foreach (const DebuggerItem &item, m_debuggers)
307
if (item.command() == command)
313
const DebuggerItem *DebuggerItemManager::findById(const QVariant &id)
315
foreach (const DebuggerItem &item, m_debuggers)
322
const DebuggerItem *DebuggerItemManager::findByEngineType(DebuggerEngineType engineType)
324
foreach (const DebuggerItem &item, m_debuggers)
325
if (item.engineType() == engineType && item.isValid())
330
void DebuggerItemManager::restoreDebuggers()
332
// Read debuggers from SDK
333
QFileInfo systemSettingsFile(Core::ICore::settings(QSettings::SystemScope)->fileName());
334
readDebuggers(FileName::fromString(systemSettingsFile.absolutePath() + QLatin1String(DEBUGGER_FILENAME)), true);
336
// Read all debuggers from user file.
337
readDebuggers(userSettingsFileName(), false);
339
// Auto detect current.
340
autoDetectCdbDebuggers();
341
autoDetectGdbOrLldbDebuggers();
343
// Add debuggers from pre-3.x profiles.xml
344
readLegacyDebuggers();
347
void DebuggerItemManager::saveDebuggers()
349
QTC_ASSERT(m_writer, return);
351
data.insert(QLatin1String(DEBUGGER_FILE_VERSION_KEY), 1);
354
foreach (const DebuggerItem &item, m_debuggers) {
355
if (item.isValid() && item.engineType() != NoEngineType) {
356
QVariantMap tmp = item.toMap();
359
data.insert(QString::fromLatin1(DEBUGGER_DATA_KEY) + QString::number(count), tmp);
363
data.insert(QLatin1String(DEBUGGER_COUNT_KEY), count);
364
m_writer->save(data, Core::ICore::mainWindow());
366
// Do not save default debuggers as they are set by the SDK.
369
QVariant DebuggerItemManager::registerDebugger(const DebuggerItem &item)
371
// Force addition when Id is set.
372
if (item.id().isValid())
373
return addDebugger(item);
375
// Otherwise, try re-using existing item first.
376
foreach (const DebuggerItem &d, m_debuggers) {
377
if (d.command() == item.command()
378
&& d.isAutoDetected() == item.isAutoDetected()
379
&& d.engineType() == item.engineType()
380
&& d.displayName() == item.displayName()
381
&& d.abis() == item.abis())
385
// Nothing suitable. Create a new id and add the item.
386
DebuggerItem di = item;
388
return addDebugger(di);
391
void DebuggerItemManager::deregisterDebugger(const QVariant &id)
397
QVariant DebuggerItemManager::addDebugger(const DebuggerItem &item)
399
QTC_ASSERT(item.id().isValid(), return QVariant());
400
m_debuggers.append(item);
401
QVariant id = item.id();
402
emit m_instance->debuggerAdded(id);
406
void DebuggerItemManager::removeDebugger(const QVariant &id)
409
for (int i = 0, n = m_debuggers.size(); i != n; ++i) {
410
if (m_debuggers.at(i).id() == id) {
411
emit m_instance->aboutToRemoveDebugger(id);
412
m_debuggers.removeAt(i);
413
emit m_instance->debuggerRemoved(id);
419
QTC_ASSERT(ok, return);
422
QString DebuggerItemManager::uniqueDisplayName(const QString &base)
424
foreach (const DebuggerItem &item, m_debuggers)
425
if (item.displayName() == base)
426
return uniqueDisplayName(base + QLatin1String(" (1)"));
431
void DebuggerItemManager::setItemData(const QVariant &id, const QString &displayName, const FileName &fileName)
433
for (int i = 0, n = m_debuggers.size(); i != n; ++i) {
434
DebuggerItem &item = m_debuggers[i];
435
if (item.id() == id) {
436
item.setDisplayName(displayName);
437
item.setCommand(fileName);
438
item.reinitializeFromFile();
439
emit m_instance->debuggerUpdated(id);
445
} // namespace Debugger;