ToolChain: Refactor toolchain support

Refactor ToolChains in Qt Creator:

 * Allow for several toolchains of the same type
 * Be smarter wrt. guessing what kind of output a toolchain
   produces. This allows us to eventually handle e.g. embedded
   linux setups way better than before.
 * Be smarter wrt. guessing what kind of environment a Qt version
   needs.
 * Improve auto-detection of toolchains a bit
 * Decide on which debugger to use based on the kind of output
   produced by the compiler.
 * Add options page to configure toolchains
 * Remove toolchain related options from the Qt version dialog

Reviewed-by: dt
This commit is contained in:
Tobias Hunger
2011-02-01 18:36:00 +01:00
parent be31c80b02
commit 8d0c477245
112 changed files with 6498 additions and 3687 deletions

View File

@@ -52,51 +52,15 @@
#include <QtGui/QIcon>
#include <QtGui/QGroupBox>
#include <QtGui/QCheckBox>
#include <QtCore/QDebug>
#include <QtCore/QSet>
#include <QtCore/QDir>
#include <QtCore/QFileInfo>
#include <QtCore/QProcess>
enum { binaryRole = Qt::UserRole + 1, toolChainRole = Qt::UserRole + 2 };
enum Columns { binaryColumn, toolChainColumn, ColumnCount };
enum Columns { abiColumn, binaryColumn, ColumnCount };
typedef QList<QStandardItem *> StandardItemList;
Q_DECLARE_METATYPE(QList<int>)
static QList<int> allGdbToolChains()
{
QList<int> rc;
rc
#ifdef Q_OS_UNIX
<< ProjectExplorer::ToolChain_GCC
<< ProjectExplorer::ToolChain_LINUX_ICC
#endif
#ifdef Q_OS_WIN
<< ProjectExplorer::ToolChain_MinGW
<< ProjectExplorer::ToolChain_WINSCW
<< ProjectExplorer::ToolChain_GCCE
<< ProjectExplorer::ToolChain_RVCT2_ARMV5
<< ProjectExplorer::ToolChain_RVCT2_ARMV6
#endif
<< ProjectExplorer::ToolChain_GCC_MAEMO5
<< ProjectExplorer::ToolChain_GCC_HARMATTAN
<< ProjectExplorer::ToolChain_GCC_MEEGO
#ifdef Q_OS_UNIX
<< ProjectExplorer::ToolChain_GCCE_GNUPOC
<< ProjectExplorer::ToolChain_RVCT_ARMV5_GNUPOC
#endif
<< ProjectExplorer::ToolChain_OTHER
<< ProjectExplorer::ToolChain_UNKNOWN;
return rc;
}
static inline QString toolChainName(int tc)
{
return ProjectExplorer::ToolChain::toolChainName(static_cast<ProjectExplorer::ToolChainType>(tc));
}
namespace Debugger {
namespace Internal {
@@ -105,6 +69,10 @@ namespace Internal {
// Obtain a tooltip for a gdb binary by running --version
static inline QString gdbToolTip(const QString &binary)
{
if (binary.isEmpty())
return QString();
if (!QFileInfo(binary).exists())
return GdbChooserWidget::tr("File not found.");
QProcess process;
process.start(binary, QStringList(QLatin1String("--version")));
process.closeWriteChannel();
@@ -122,37 +90,32 @@ static inline QString gdbToolTip(const QString &binary)
// Provides a delayed tooltip listing the gdb version as
// obtained by running it. Provides conveniences for getting/setting the maps and
// for listing the toolchains used and the ones still available.
class GdbBinaryModel : public QStandardItemModel {
public:
typedef GdbChooserWidget::BinaryToolChainMap BinaryToolChainMap;
explicit GdbBinaryModel(QObject * parent = 0);
virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
bool setData(const QModelIndex &index, const QVariant &value, int role);
// get / set data as map.
BinaryToolChainMap gdbBinaries() const;
void setGdbBinaries(const BinaryToolChainMap &m);
QMap<QString, QString> gdbMapping() const;
void setGdbMapping(const QMap<QString, QString> &m);
QString binary(int row) const;
QList<int> toolChains(int row) const;
QString abi(int row) const;
QStringList binaries() const;
QList<int> usedToolChains() const;
QSet<int> unusedToolChainSet() const;
QList<int> unusedToolChains() const;
void append(const QString &abi, const QString &binary);
void append(const QString &binary, const QList<int> &toolChains);
bool isDirty() const;
static void setAbiItem(QStandardItem *item, const QString &abi);
static void setBinaryItem(QStandardItem *item, const QString &binary);
static void setToolChainItem(QStandardItem *item, const QList<int> &toolChain);
};
GdbBinaryModel::GdbBinaryModel(QObject *parent) :
QStandardItemModel(0, ColumnCount, parent)
{
QStringList headers;
headers << GdbChooserWidget::tr("Binary") << GdbChooserWidget::tr("Toolchains");
headers << GdbChooserWidget::tr("ABI") << GdbChooserWidget::tr("Debugger");
setHorizontalHeaderLabels(headers);
}
@@ -166,455 +129,119 @@ QVariant GdbBinaryModel::data(const QModelIndex &index, int role) const
// Run the gdb and obtain the tooltip
const QString tooltip = gdbToolTip(binary(index.row()));
// Set on the whole row
item(index.row(), abiColumn)->setToolTip(tooltip);
item(index.row(), binaryColumn)->setToolTip(tooltip);
item(index.row(), toolChainColumn)->setToolTip(tooltip);
return QVariant(tooltip);
}
return QStandardItemModel::data(index, role);
}
QStringList GdbBinaryModel::binaries() const
bool GdbBinaryModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
QStringList rc;
const int binaryCount = rowCount();
for (int b = 0; b < binaryCount; b++)
rc.push_back(binary(b));
return rc;
}
QList<int> GdbBinaryModel::usedToolChains() const
{
// Loop over model and collect all toolchains.
QList<int> rc;
const int binaryCount = rowCount();
for (int b = 0; b < binaryCount; b++)
foreach(int tc, toolChains(b))
rc.push_back(tc);
return rc;
}
QSet<int> GdbBinaryModel::unusedToolChainSet() const
{
const QSet<int> used = usedToolChains().toSet();
QSet<int> all = allGdbToolChains().toSet();
return all.subtract(used);
}
QList<int> GdbBinaryModel::unusedToolChains() const
{
QList<int> unused = unusedToolChainSet().toList();
qSort(unused);
return unused;
}
GdbBinaryModel::BinaryToolChainMap GdbBinaryModel::gdbBinaries() const
{
BinaryToolChainMap rc;
const int binaryCount = rowCount();
for (int r = 0; r < binaryCount; r++) {
const QString bin = binary(r);
foreach(int tc, toolChains(r))
rc.insert(bin, tc);
if (index.isValid() && role == Qt::EditRole) {
Q_ASSERT(index.column() == binaryColumn);
item(index.row(), abiColumn)->setToolTip(QString());
item(index.row(), binaryColumn)->setToolTip(QString());
item(index.row(), binaryColumn)->setData(true);
QFont f(item(index.row(), binaryColumn)->font());
f.setBold(true);
item(index.row(), binaryColumn)->setFont(f);
}
return QStandardItemModel::setData(index, value, role);
}
QMap<QString, QString> GdbBinaryModel::gdbMapping() const
{
QMap<QString, QString> rc;
const int binaryCount = rowCount();
for (int r = 0; r < binaryCount; ++r)
rc.insert(abi(r), binary(r));
return rc;
}
void GdbBinaryModel::setGdbBinaries(const BinaryToolChainMap &m)
void GdbBinaryModel::setGdbMapping(const QMap<QString, QString> &m)
{
removeRows(0, rowCount());
foreach(const QString &binary, m.uniqueKeys())
append(binary, m.values(binary));
for (QMap<QString, QString>::const_iterator i = m.constBegin(); i != m.constEnd(); ++i)
append(i.key(), i.value());
}
QString GdbBinaryModel::binary(int row) const
{
return item(row, binaryColumn)->data(binaryRole).toString();
return QDir::fromNativeSeparators(item(row, binaryColumn)->data(Qt::DisplayRole).toString());
}
QList<int> GdbBinaryModel::toolChains(int row) const
QString GdbBinaryModel::abi(int row) const
{
const QVariant data = item(row, toolChainColumn)->data(toolChainRole);
return qVariantValue<QList<int> >(data);
return item(row, abiColumn)->data(Qt::DisplayRole).toString();
}
void GdbBinaryModel::setBinaryItem(QStandardItem *item, const QString &binary)
{
const QFileInfo fi(binary);
item->setText(fi.isAbsolute() ? fi.fileName() : QDir::toNativeSeparators(binary));
item->setText(binary.isEmpty() ? QString() : QDir::toNativeSeparators(binary));
item->setToolTip(QString());; // clean out delayed tooltip
item->setData(QVariant(binary), binaryRole);
item->setFlags(Qt::ItemIsEnabled|Qt::ItemIsSelectable);
item->setFlags(Qt::ItemIsEnabled|Qt::ItemIsSelectable|Qt::ItemIsEditable);
item->setData(false);
}
void GdbBinaryModel::setToolChainItem(QStandardItem *item, const QList<int> &toolChains)
void GdbBinaryModel::setAbiItem(QStandardItem *item, const QString &abi)
{
// Format comma-separated list
const QString toolChainSeparator = QLatin1String(", ");
QString toolChainDesc;
const int count = toolChains.size();
for (int i = 0; i < count; i++) {
if (i)
toolChainDesc += toolChainSeparator;
toolChainDesc += toolChainName(toolChains.at(i));
}
item->setText(toolChainDesc);
item->setToolTip(QString());; // clean out delayed tooltip
item->setData(qVariantFromValue(toolChains), toolChainRole);
item->setText(abi);
item->setToolTip(QString()); // clean out delayed tooltip
item->setFlags(Qt::ItemIsEnabled|Qt::ItemIsSelectable);
}
void GdbBinaryModel::append(const QString &binary, const QList<int> &toolChains)
void GdbBinaryModel::append(const QString &abi, const QString &binary)
{
QStandardItem *binaryItem = new QStandardItem;
QStandardItem *toolChainItem = new QStandardItem;
QStandardItem *abiItem = new QStandardItem;
GdbBinaryModel::setAbiItem(abiItem, abi);
GdbBinaryModel::setBinaryItem(binaryItem, binary);
GdbBinaryModel::setToolChainItem(toolChainItem, toolChains);
StandardItemList row;
row << binaryItem << toolChainItem;
row << abiItem << binaryItem;
appendRow(row);
}
bool GdbBinaryModel::isDirty() const
{
for (int i = 0; i < rowCount(); ++i) {
if (item(i, binaryColumn)->data().toBool())
return true;
}
return false;
}
// ----------- GdbChooserWidget
GdbChooserWidget::GdbChooserWidget(QWidget *parent) :
QWidget(parent),
m_treeView(new QTreeView),
m_model(new GdbBinaryModel(m_treeView)),
m_addButton(new QToolButton),
m_deleteButton(new QToolButton),
m_dirty(false)
m_model(new GdbBinaryModel(m_treeView))
{
QHBoxLayout *mainHLayout = new QHBoxLayout;
QVBoxLayout *layout = new QVBoxLayout(this);
m_treeView->setRootIsDecorated(false);
m_treeView->setModel(m_model);
m_treeView->setUniformRowHeights(true);
m_treeView->setAllColumnsShowFocus(true);
m_treeView->setSelectionMode(QAbstractItemView::SingleSelection);
connect(m_treeView->selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)),
this, SLOT(slotCurrentChanged(QModelIndex,QModelIndex)));
connect(m_treeView, SIGNAL(doubleClicked(QModelIndex)),
this, SLOT(slotDoubleClicked(QModelIndex)));
mainHLayout->addWidget(m_treeView);
m_addButton->setIcon(QIcon(QLatin1String(Core::Constants::ICON_PLUS)));
connect(m_addButton, SIGNAL(clicked()), this, SLOT(slotAdd()));
m_deleteButton->setIcon(QIcon(QLatin1String(Core::Constants::ICON_MINUS)));
m_deleteButton->setEnabled(false);
connect(m_deleteButton, SIGNAL(clicked()), this, SLOT(slotRemove()));
QVBoxLayout *vButtonLayout = new QVBoxLayout;
vButtonLayout->addWidget(m_addButton);
vButtonLayout->addWidget(m_deleteButton);
vButtonLayout->addItem(new QSpacerItem(0, 0, QSizePolicy::Ignored, QSizePolicy::MinimumExpanding));
mainHLayout->addLayout(vButtonLayout);
setLayout(mainHLayout);
layout->addWidget(m_treeView);
}
QStandardItem *GdbChooserWidget::currentItem() const
QMap<QString, QString> GdbChooserWidget::gdbMapping() const
{
// Return the column-0-item
QModelIndex currentIndex = m_treeView->currentIndex();
if (!currentIndex.isValid())
return 0;
if (currentIndex.column() != binaryColumn)
currentIndex = currentIndex.sibling(currentIndex.row(), binaryColumn);
return m_model->itemFromIndex(currentIndex);
return m_model->gdbMapping();
}
void GdbChooserWidget::slotAdd()
void GdbChooserWidget::setGdbMapping(const QMap<QString, QString> &m)
{
// Any toolchains left?
const QList<int> unusedToolChains = m_model->unusedToolChains();
if (unusedToolChains.isEmpty())
return;
// On a binary or no current item: Add binary + toolchain
BinaryToolChainDialog binaryDialog(this);
binaryDialog.setToolChainChoices(unusedToolChains);
if (binaryDialog.exec() != QDialog::Accepted)
return;
// Refuse binaries that already exist
const QString path = binaryDialog.path();
if (m_model->binaries().contains(path)) {
QMessageBox::warning(this, tr("Duplicate binary"),
tr("The binary '%1' already exists.").arg(path));
return;
}
// Add binary + toolchain to model
m_model->append(path, binaryDialog.toolChains());
m_dirty = true;
}
void GdbChooserWidget::slotRemove()
{
if (QStandardItem *item = currentItem())
removeItem(item);
}
void GdbChooserWidget::removeItem(QStandardItem *item)
{
m_model->removeRow(item->row());
m_dirty = true;
}
void GdbChooserWidget::slotCurrentChanged(const QModelIndex &current, const QModelIndex &)
{
const bool hasItem = current.isValid() && m_model->itemFromIndex(current);
m_deleteButton->setEnabled(hasItem);
}
void GdbChooserWidget::slotDoubleClicked(const QModelIndex &current)
{
QTC_ASSERT(current.isValid(), return)
// Show dialog to edit. Make all unused toolchains including the ones
// previously assigned to that binary available.
const int row = current.row();
const QString oldBinary = m_model->binary(row);
const QList<int> oldToolChains = m_model->toolChains(row);
const QSet<int> toolChainChoices = m_model->unusedToolChainSet().unite(oldToolChains.toSet());
BinaryToolChainDialog dialog(this);
dialog.setPath(oldBinary);
const BinaryToolChainMap map = gdbBinaries();
dialog.setToolChainChoices(toolChainChoices.toList(), &map);
dialog.setToolChains(oldToolChains);
if (dialog.exec() != QDialog::Accepted)
return;
// Check if anything changed.
const QString newBinary = dialog.path();
const QList<int> newToolChains = dialog.toolChains();
if (newBinary == oldBinary && newToolChains == oldToolChains)
return;
GdbBinaryModel::setBinaryItem(m_model->item(row, binaryColumn), newBinary);
GdbBinaryModel::setToolChainItem(m_model->item(row, toolChainColumn), newToolChains);
m_dirty = true;
}
GdbChooserWidget::BinaryToolChainMap GdbChooserWidget::gdbBinaries() const
{
return m_model->gdbBinaries();
}
void GdbChooserWidget::setGdbBinaries(const BinaryToolChainMap &m)
{
m_model->setGdbBinaries(m);
m_model->setGdbMapping(m);
for (int c = 0; c < ColumnCount; c++)
m_treeView->resizeColumnToContents(c);
m_dirty = false;
}
bool GdbChooserWidget::isDirty() const
{
return m_dirty;
}
void GdbChooserWidget::clearDirty()
{
m_dirty = false;
}
// -------------- ToolChainSelectorWidget
static const char *toolChainPropertyC = "toolChain";
static inline int toolChainOfCheckBox(const QCheckBox *c)
{
return c->property(toolChainPropertyC).toInt();
}
static inline QVBoxLayout *createGroupBox(const QString &title, QVBoxLayout *lt)
{
QGroupBox *gb = new QGroupBox(title);
QVBoxLayout *gbLayout = new QVBoxLayout;
gb->setLayout(gbLayout);
lt->addWidget(gb);
return gbLayout;
}
ToolChainSelectorWidget::ToolChainSelectorWidget(QWidget *parent) :
QWidget(parent), m_valid(false)
{
QVBoxLayout *mainLayout = new QVBoxLayout;
QVBoxLayout *desktopLayout = createGroupBox(tr("Desktop/General"), mainLayout);
QVBoxLayout *symbianLayout = createGroupBox(tr("Symbian"), mainLayout);
QVBoxLayout *maemoLayout = createGroupBox(tr("Maemo"), mainLayout);
// Group checkboxes into categories
foreach(int tc, allGdbToolChains()) {
switch (tc) {
case ProjectExplorer::ToolChain_GCC:
case ProjectExplorer::ToolChain_LINUX_ICC:
case ProjectExplorer::ToolChain_MinGW:
case ProjectExplorer::ToolChain_OTHER:
case ProjectExplorer::ToolChain_UNKNOWN:
desktopLayout->addWidget(createToolChainCheckBox(tc));
break;
case ProjectExplorer::ToolChain_MSVC:
case ProjectExplorer::ToolChain_WINCE:
break;
case ProjectExplorer::ToolChain_WINSCW:
case ProjectExplorer::ToolChain_GCCE:
case ProjectExplorer::ToolChain_RVCT2_ARMV5:
case ProjectExplorer::ToolChain_RVCT2_ARMV6:
case ProjectExplorer::ToolChain_GCCE_GNUPOC:
case ProjectExplorer::ToolChain_RVCT_ARMV5_GNUPOC:
symbianLayout->addWidget(createToolChainCheckBox(tc));
break;
case ProjectExplorer::ToolChain_GCC_MAEMO5:
case ProjectExplorer::ToolChain_GCC_HARMATTAN:
case ProjectExplorer::ToolChain_GCC_MEEGO:
maemoLayout->addWidget(createToolChainCheckBox(tc));
break;
case ProjectExplorer::ToolChain_INVALID:
break;
}
}
setLayout(mainLayout);
}
QCheckBox *ToolChainSelectorWidget::createToolChainCheckBox(int tc)
{
// Add checkbox
QCheckBox *cb = new QCheckBox(toolChainName(tc));
cb->setProperty(toolChainPropertyC, QVariant(tc));
connect(cb, SIGNAL(stateChanged(int)), this, SLOT(slotCheckStateChanged(int)));
m_checkBoxes.push_back(cb);
return cb;
}
static inline QString msgDisabledToolChainToolTip(const QString &binary, int toolChain)
{
return ToolChainSelectorWidget::tr(
"<html><head/><body><p>Another gdb binary (<i>%1</i>) is currently configured "
"to handle the toolchain <i>%2</i>.</p></body></html>").
arg(QFileInfo(binary).fileName(), toolChainName(toolChain));
}
void ToolChainSelectorWidget::setEnabledToolChains(const QList<int> &enabled,
const BinaryToolChainMap *binaryToolChainMap)
{
foreach(QCheckBox *cb, m_checkBoxes) {
const int toolChain = toolChainOfCheckBox(cb);
if (enabled.contains(toolChain)) {
cb->setToolTip(QString());
} else {
// Toolchain is handled by a different binary, hint to user.
cb->setEnabled(false);
const QString binary = binaryToolChainMap ? binaryToolChainMap->key(toolChain) : QString();
if (!binary.isEmpty())
cb->setToolTip(msgDisabledToolChainToolTip(binary, toolChain));
}
}
}
void ToolChainSelectorWidget::setCheckedToolChains(const QList<int> &checked)
{
foreach(QCheckBox *cb, m_checkBoxes)
if (checked.contains(toolChainOfCheckBox(cb)))
cb->setChecked(true);
// Trigger 'valid changed'
slotCheckStateChanged(checked.isEmpty() ? Qt::Unchecked : Qt::Checked);
}
QList<int> ToolChainSelectorWidget::checkedToolChains() const
{
QList<int> rc;
foreach(const QCheckBox *cb, m_checkBoxes)
if (cb->isChecked())
rc.push_back(toolChainOfCheckBox(cb));
return rc;
}
bool ToolChainSelectorWidget::isValid() const
{
return m_valid;
}
void ToolChainSelectorWidget::slotCheckStateChanged(int state)
{
// Emit signal if valid state changed
const bool newValid = state == Qt::Checked || hasCheckedToolChain();
if (newValid != m_valid) {
m_valid = newValid;
emit validChanged(m_valid);
}
}
bool ToolChainSelectorWidget::hasCheckedToolChain() const
{
foreach(const QCheckBox *cb, m_checkBoxes)
if (cb->isChecked())
return true;
return false;
}
// -------------- ToolChainDialog
BinaryToolChainDialog::BinaryToolChainDialog(QWidget *parent) :
QDialog(parent),
m_toolChainSelector(new ToolChainSelectorWidget),
m_mainLayout(new QFormLayout),
m_buttonBox(new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel)),
m_pathChooser(new Utils::PathChooser)
{
setModal(true);
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
setWindowTitle(tr("Select binary and toolchains"));
m_pathChooser->setExpectedKind(Utils::PathChooser::ExistingCommand);
m_pathChooser->setPromptDialogTitle(tr("Gdb binary"));
connect(m_pathChooser, SIGNAL(validChanged()), this, SLOT(slotValidChanged()));
m_mainLayout->addRow(tr("Path:"), m_pathChooser);
connect(m_toolChainSelector, SIGNAL(validChanged(bool)), this, SLOT(slotValidChanged()));
m_mainLayout->addRow(m_toolChainSelector);
connect(m_buttonBox, SIGNAL(accepted()), this, SLOT(accept()));
connect(m_buttonBox, SIGNAL(rejected()), this, SLOT(reject()));
m_mainLayout->addRow(m_buttonBox);
setLayout(m_mainLayout);
setOkButtonEnabled(false);
m_pathChooser->setFocus();
}
void BinaryToolChainDialog::setToolChainChoices(const QList<int> &tcs,
const BinaryToolChainMap *binaryToolChainMap)
{
m_toolChainSelector->setEnabledToolChains(tcs, binaryToolChainMap);
}
void BinaryToolChainDialog::setToolChains(const QList<int> &tcs)
{
m_toolChainSelector->setCheckedToolChains(tcs);
}
QList<int> BinaryToolChainDialog::toolChains() const
{
return m_toolChainSelector->checkedToolChains();
}
void BinaryToolChainDialog::setOkButtonEnabled(bool v)
{
m_buttonBox->button(QDialogButtonBox::Ok)->setEnabled(v);
}
void BinaryToolChainDialog::setPath(const QString &p)
{
m_pathChooser->setPath(p);
}
QString BinaryToolChainDialog::path() const
{
return m_pathChooser->rawPath();
}
void BinaryToolChainDialog::slotValidChanged()
{
setOkButtonEnabled(m_pathChooser->isValid() && m_toolChainSelector->isValid());
return m_model->isDirty();
}
} // namespace Internal

View File

@@ -67,96 +67,17 @@ class GdbChooserWidget : public QWidget
public:
explicit GdbChooserWidget(QWidget *parent = 0);
typedef QMultiMap<QString, int> BinaryToolChainMap;
BinaryToolChainMap gdbBinaries() const;
void setGdbBinaries(const BinaryToolChainMap &m);
QMap<QString, QString> gdbMapping() const;
void setGdbMapping(const QMap<QString, QString> &m);
bool isDirty() const;
public slots:
void clearDirty();
private slots:
void slotAdd();
void slotRemove();
void slotCurrentChanged(const QModelIndex &current, const QModelIndex & previous);
void slotDoubleClicked(const QModelIndex &current);
private:
void removeItem(QStandardItem *item);
QToolButton *createAddToolMenuButton();
QStandardItem *currentItem() const;
QTreeView *m_treeView;
GdbBinaryModel *m_model;
QToolButton *m_addButton;
QToolButton *m_deleteButton;
bool m_dirty;
};
// Present toolchains with checkboxes grouped in QGroupBox panes
// and provide valid-handling. Unavailabe toolchains can be grayed
// out using setEnabledToolChains().
class ToolChainSelectorWidget : public QWidget {
Q_OBJECT
public:
typedef GdbChooserWidget::BinaryToolChainMap BinaryToolChainMap;
explicit ToolChainSelectorWidget(QWidget *parent = 0);
void setEnabledToolChains(const QList<int> &enabled,
// Optionally used for generating a tooltip for the disabled check boxes
const BinaryToolChainMap *binaryToolChainMap = 0);
void setCheckedToolChains(const QList<int> &);
QList<int> checkedToolChains() const;
bool isValid() const;
signals:
void validChanged(bool);
private slots:
void slotCheckStateChanged(int);
private:
bool hasCheckedToolChain() const;
QCheckBox *createToolChainCheckBox(int tc);
QList<QCheckBox*> m_checkBoxes;
bool m_valid;
};
// Internal helper dialog for selecting a binary and its
// associated toolchains.
class BinaryToolChainDialog : public QDialog {
Q_OBJECT
public:
typedef GdbChooserWidget::BinaryToolChainMap BinaryToolChainMap;
explicit BinaryToolChainDialog(QWidget *parent);
void setToolChainChoices(const QList<int> &,
// Optionally used for generating a tooltip for the disabled check boxes
const BinaryToolChainMap *binaryToolChainMap = 0);
void setToolChains(const QList<int> &);
QList<int> toolChains() const;
void setPath(const QString &);
QString path() const;
private slots:
void slotValidChanged();
private:
void setOkButtonEnabled(bool e);
ToolChainSelectorWidget *m_toolChainSelector;
QFormLayout *m_mainLayout;
QDialogButtonBox *m_buttonBox;
Utils::PathChooser *m_pathChooser;
};
} // namespace Internal

View File

@@ -1795,36 +1795,29 @@ int GdbEngine::currentFrame() const
return stackHandler()->currentIndex();
}
QString msgNoBinaryForToolChain(int tc)
QString msgNoBinaryForToolChain(const ProjectExplorer::Abi &tc)
{
using namespace ProjectExplorer;
return GdbEngine::tr("There is no gdb binary available for '%1'.")
.arg(ToolChain::toolChainName(ToolChainType(tc)));
return GdbEngine::tr("There is no gdb binary available for binaries in format '%1'")
.arg(tc.toString());
}
AbstractGdbAdapter *GdbEngine::createAdapter()
{
const DebuggerStartParameters &sp = startParameters();
switch (sp.toolChainType) {
case ProjectExplorer::ToolChain_WINSCW: // S60
case ProjectExplorer::ToolChain_GCCE:
case ProjectExplorer::ToolChain_RVCT2_ARMV5:
case ProjectExplorer::ToolChain_RVCT2_ARMV6:
case ProjectExplorer::ToolChain_RVCT_ARMV5_GNUPOC:
case ProjectExplorer::ToolChain_GCCE_GNUPOC:
if (sp.toolChainAbi.os() == ProjectExplorer::Abi::Symbian) {
// FIXME: 1 of 3 testing hacks.
if (sp.debugClient == DebuggerStartParameters::DebugClientCoda)
return new CodaGdbAdapter(this);
else
return new TrkGdbAdapter(this);
default:
break;
}
switch (sp.startMode) {
case AttachCore:
return new CoreGdbAdapter(this);
case AttachToRemote:
return new RemoteGdbServerAdapter(this, sp.toolChainType);
return new RemoteGdbServerAdapter(this, sp.toolChainAbi);
case StartRemoteGdb:
return new RemotePlainGdbAdapter(this);
case AttachExternal:
@@ -4220,12 +4213,12 @@ bool GdbEngine::startGdb(const QStringList &args, const QString &gdb,
const DebuggerStartParameters &sp = startParameters();
m_gdb = QString::fromLocal8Bit(qgetenv("QTC_DEBUGGER_PATH"));
if (m_gdb.isEmpty() && sp.startMode != StartRemoteGdb)
m_gdb = debuggerCore()->gdbBinaryForToolChain(sp.toolChainType);
m_gdb = debuggerCore()->gdbBinaryForAbi(startParameters().toolChainAbi);
if (m_gdb.isEmpty())
m_gdb = gdb;
if (m_gdb.isEmpty()) {
handleAdapterStartFailed(
msgNoBinaryForToolChain(sp.toolChainType),
msgNoBinaryForToolChain(sp.toolChainAbi),
GdbOptionsPage::settingsId());
return false;
}

View File

@@ -37,8 +37,8 @@
#include <coreplugin/icore.h>
#include <projectexplorer/projectexplorer.h>
#include <projectexplorer/toolchain.h>
#include <projectexplorer/toolchaintype.h>
#include <projectexplorer/toolchainmanager.h>
#include <projectexplorer/abi.h>
#include <QtCore/QCoreApplication>
#include <QtCore/QTextStream>
@@ -47,22 +47,37 @@
namespace Debugger {
namespace Internal {
const char gdbBinariesSettingsGroupC[] = "GdbBinaries";
const char debugModeGdbBinaryKeyC[] = "GdbBinary";
static const char *GDB_MAPPING_ARRAY = "GdbMapping";
static const char *GDB_ABI_KEY = "Abi";
static const char *GDB_BINARY_KEY = "Binary";
GdbOptionsPage::GdbBinaryToolChainMap GdbOptionsPage::gdbBinaryToolChainMap;
bool GdbOptionsPage::gdbBinariesChanged = true;
GdbOptionsPage::GdbBinaryToolChainMap GdbOptionsPage::abiToGdbMap;
bool GdbOptionsPage::gdbMappingChanged = true;
void GdbOptionsPage::readGdbBinarySettings() /* static */
void GdbOptionsPage::readGdbSettings() /* static */
{
// FIXME: Convert old settings!
using namespace ProjectExplorer;
QSettings *settings = Core::ICore::instance()->settings();
// Convert gdb binaries from flat settings list (see writeSettings)
// into map ("binary1=gdb,1,2", "binary2=symbian_gdb,3,4").
gdbBinaryToolChainMap.clear();
abiToGdbMap.clear();
int size = settings->beginReadArray(GDB_MAPPING_ARRAY);
for (int i = 0; i < size; ++i) {
settings->setArrayIndex(i);
ProjectExplorer::Abi abi(settings->value(GDB_ABI_KEY).toString());
if (!abi.isValid())
continue;
QString binary = settings->value(GDB_BINARY_KEY).toString();
if (binary.isEmpty())
continue;
abiToGdbMap.insert(abi.toString(), binary);
}
settings->endArray();
// Map old settings (pre 2.2):
const QChar separator = QLatin1Char(',');
const QString keyRoot = QLatin1String(gdbBinariesSettingsGroupC) + QLatin1Char('/') +
QLatin1String(debugModeGdbBinaryKeyC);
const QString keyRoot = QLatin1String("GdbBinaries/GdbBinaries");
for (int i = 1; ; i++) {
const QString value = settings->value(keyRoot + QString::number(i)).toString();
if (value.isEmpty())
@@ -71,81 +86,116 @@ void GdbOptionsPage::readGdbBinarySettings() /* static */
QStringList tokens = value.split(separator);
if (tokens.size() < 2)
break;
const QString binary = tokens.front();
// Skip non-existent absolute binaries allowing for upgrades by the installer.
// Force a rewrite of the settings file.
const QFileInfo binaryInfo(binary);
if (binaryInfo.isAbsolute() && !binaryInfo.isExecutable()) {
gdbBinariesChanged = true;
const QString msg = QString::fromLatin1("Warning: The gdb binary '%1' does not exist, skipping.\n").arg(binary);
qWarning("%s", qPrintable(msg));
continue;
}
// Create entries for all toolchains.
tokens.pop_front();
foreach (const QString &t, tokens) {
// Paranoia: Check if the there is already a binary configured for the toolchain.
const int toolChain = t.toInt();
const QString predefinedGdb = gdbBinaryToolChainMap.key(toolChain);
if (predefinedGdb.isEmpty()) {
gdbBinaryToolChainMap.insert(binary, toolChain);
} else {
const QString toolChainName =
ProjectExplorer::ToolChain::toolChainName(ToolChainType(toolChain));
const QString msg =
QString::fromLatin1("An inconsistency has been encountered in the Ini-file '%1':\n"
"Skipping gdb binary '%2' for toolchain '%3' as '%4' is already configured for it.").
arg(settings->fileName(), binary, toolChainName, predefinedGdb);
qWarning("%s", qPrintable(msg));
QString abi;
switch (t.toInt())
{
case 0: // GCC
case 1: // Linux ICC
#ifndef Q_OS_WIN
abi = ProjectExplorer::Abi::hostAbi().toString();
#endif
break;
case 2: // MinGW
case 3: // MSVC
case 4: // WINCE
#ifdef Q_OS_WIN
abi = ProjectExplorer::Abi::hostAbi().toString();
#endif
break;
case 5: // WINSCW
abi = ProjectExplorer::Abi(ProjectExplorer::Abi::ARM, ProjectExplorer::Abi::Symbian,
ProjectExplorer::Abi::Symbian_emulator,
ProjectExplorer::Abi::Format_ELF,
32).toString();
break;
case 6: // GCCE
case 7: // RVCT 2, ARM v5
case 8: // RVCT 2, ARM v6
case 11: // RVCT GNUPOC
case 12: // RVCT 4, ARM v5
case 13: // RVCT 4, ARM v6
abi = ProjectExplorer::Abi(ProjectExplorer::Abi::ARM, ProjectExplorer::Abi::Symbian,
ProjectExplorer::Abi::Symbian_device,
ProjectExplorer::Abi::Format_ELF,
32).toString();
break;
case 9: // GCC Maemo5
abi = ProjectExplorer::Abi(ProjectExplorer::Abi::ARM, ProjectExplorer::Abi::Linux,
ProjectExplorer::Abi::Linux_maemo,
ProjectExplorer::Abi::Format_ELF,
32).toString();
break;
case 14: // GCC Harmattan
abi = ProjectExplorer::Abi(ProjectExplorer::Abi::ARM, ProjectExplorer::Abi::Linux,
ProjectExplorer::Abi::Linux_harmattan,
ProjectExplorer::Abi::Format_ELF,
32).toString();
break;
case 15: // GCC Meego
abi = ProjectExplorer::Abi(ProjectExplorer::Abi::ARM, ProjectExplorer::Abi::Linux,
ProjectExplorer::Abi::Linux_meego,
ProjectExplorer::Abi::Format_ELF,
32).toString();
break;
default:
break;
}
if (abi.isEmpty() || abiToGdbMap.contains(abi))
continue;
abiToGdbMap.insert(abi, binary);
}
}
// Linux defaults
#ifdef Q_OS_UNIX
if (gdbBinaryToolChainMap.isEmpty()) {
const QString gdb = QLatin1String("gdb");
gdbBinaryToolChainMap.insert(gdb, ToolChain_GCC);
gdbBinaryToolChainMap.insert(gdb, ToolChain_LINUX_ICC);
gdbBinaryToolChainMap.insert(gdb, ToolChain_OTHER);
gdbBinaryToolChainMap.insert(gdb, ToolChain_UNKNOWN);
}
#endif
gdbMappingChanged = false;
}
void GdbOptionsPage::writeGdbBinarySettings() /* static */
void GdbOptionsPage::writeGdbSettings() /* static */
{
// FIXME: This should actually get called in response to ICore::saveSettingsRequested()
if (!gdbMappingChanged)
return;
QSettings *settings = Core::ICore::instance()->settings();
// Convert gdb binaries map into a flat settings list of
// ("binary1=gdb,1,2", "binary2=symbian_gdb,3,4"). It needs to be ASCII for installers
QString lastBinary;
QStringList settingsList;
const QChar separator = QLatin1Char(',');
const GdbBinaryToolChainMap::const_iterator cend = gdbBinaryToolChainMap.constEnd();
for (GdbBinaryToolChainMap::const_iterator it = gdbBinaryToolChainMap.constBegin(); it != cend; ++it) {
if (it.key() != lastBinary) {
lastBinary = it.key(); // Start new entry with first toolchain
settingsList.push_back(lastBinary);
}
settingsList.back().append(separator); // Append toolchain to last binary
settingsList.back().append(QString::number(it.value()));
settings->beginWriteArray(GDB_MAPPING_ARRAY);
int index = 0;
for (QMap<QString, QString>::const_iterator i = abiToGdbMap.constBegin();
i != abiToGdbMap.constEnd(); ++i) {
if (i.value().isEmpty())
continue;
settings->setArrayIndex(index);
++index;
settings->setValue(GDB_ABI_KEY, i.key());
settings->setValue(GDB_BINARY_KEY, i.value());
}
// Terminate settings list by an empty element such that consecutive keys resulting
// from ini-file merging are suppressed while reading.
settingsList.push_back(QString());
// Write out list
settings->beginGroup(QLatin1String(gdbBinariesSettingsGroupC));
settings->remove(QString()); // remove all keys in group.
const int count = settingsList.size();
const QString keyRoot = QLatin1String(debugModeGdbBinaryKeyC);
for (int i = 0; i < count; i++)
settings->setValue(keyRoot + QString::number(i + 1), settingsList.at(i));
settings->endGroup();
settings->endArray();
gdbMappingChanged = false;
}
GdbOptionsPage::GdbOptionsPage()
: m_ui(0)
{
}
{ }
QString GdbOptionsPage::settingsId()
{
@@ -174,10 +224,41 @@ QIcon GdbOptionsPage::categoryIcon() const
QWidget *GdbOptionsPage::createPage(QWidget *parent)
{
// Fix up abi mapping now that the ToolChainManager is available:
connect(ProjectExplorer::ToolChainManager::instance(), SIGNAL(toolChainAdded(ProjectExplorer::ToolChain*)),
this, SLOT(handleToolChainAdditions(ProjectExplorer::ToolChain*)));
connect(ProjectExplorer::ToolChainManager::instance(), SIGNAL(toolChainRemoved(ProjectExplorer::ToolChain*)),
this, SLOT(handleToolChainRemovals(ProjectExplorer::ToolChain*)));
// Update mapping now that toolchains are available
QList<ProjectExplorer::ToolChain *> tcs =
ProjectExplorer::ToolChainManager::instance()->toolChains();
QStringList abiList;
foreach (ProjectExplorer::ToolChain *tc, tcs) {
const QString abi = tc->targetAbi().toString();
if (!abiList.contains(abi))
abiList.append(abi);
if (!abiToGdbMap.contains(abi))
handleToolChainAdditions(tc);
}
QStringList toRemove;
for (QMap<QString, QString>::const_iterator i = abiToGdbMap.constBegin();
i != abiToGdbMap.constEnd(); ++i) {
if (!abiList.contains(i.key()))
toRemove.append(i.key());
}
foreach (const QString &key, toRemove)
abiToGdbMap.remove(key);
// Actual page setup:
QWidget *w = new QWidget(parent);
m_ui = new Ui::GdbOptionsPage;
m_ui->setupUi(w);
m_ui->gdbChooserWidget->setGdbBinaries(gdbBinaryToolChainMap);
m_ui->gdbChooserWidget->setGdbMapping(abiToGdbMap);
m_ui->scriptFileChooser->setExpectedKind(Utils::PathChooser::File);
m_ui->scriptFileChooser->setPromptDialogTitle(tr("Choose Location of Startup Script File"));
@@ -201,18 +282,7 @@ QWidget *GdbOptionsPage::createPage(QWidget *parent)
m_ui->checkBoxEnableReverseDebugging);
m_group.insert(debuggerCore()->action(GdbWatchdogTimeout), 0);
#if 1
m_ui->groupBoxPluginDebugging->hide();
#else // The related code (handleAqcuiredInferior()) is disabled as well.
m_group.insert(debuggerCore()->action(AllPluginBreakpoints),
m_ui->radioButtonAllPluginBreakpoints);
m_group.insert(debuggerCore()->action(SelectedPluginBreakpoints),
m_ui->radioButtonSelectedPluginBreakpoints);
m_group.insert(debuggerCore()->action(NoPluginBreakpoints),
m_ui->radioButtonNoPluginBreakpoints);
m_group.insert(debuggerCore()->action(SelectedPluginBreakpointsPattern),
m_ui->lineEditSelectedPluginBreakpointsPattern);
#endif
m_ui->lineEditSelectedPluginBreakpointsPattern->
setEnabled(debuggerCore()->action(SelectedPluginBreakpoints)->value().toBool());
@@ -246,11 +316,12 @@ void GdbOptionsPage::apply()
{
if (!m_ui) // page never shown
return;
m_group.apply(Core::ICore::instance()->settings());
if (m_ui->gdbChooserWidget->isDirty()) {
gdbBinariesChanged = true;
gdbBinaryToolChainMap = m_ui->gdbChooserWidget->gdbBinaries();
m_ui->gdbChooserWidget->clearDirty();
abiToGdbMap = m_ui->gdbChooserWidget->gdbMapping();
m_ui->gdbChooserWidget->setGdbMapping(abiToGdbMap);
gdbMappingChanged = true;
}
}
@@ -268,5 +339,37 @@ bool GdbOptionsPage::matches(const QString &s) const
return m_searchKeywords.contains(s, Qt::CaseInsensitive);
}
void GdbOptionsPage::handleToolChainAdditions(ProjectExplorer::ToolChain *tc)
{
ProjectExplorer::Abi tcAbi = tc->targetAbi();
if (tcAbi.binaryFormat() != ProjectExplorer::Abi::Format_ELF
&& tcAbi.binaryFormat() != ProjectExplorer::Abi::Format_Mach_O
&& !( tcAbi.os() == ProjectExplorer::Abi::Windows
&& tcAbi.osFlavor() == ProjectExplorer::Abi::Windows_msys ))
return;
if (abiToGdbMap.contains(tcAbi.toString()))
return;
QString binary;
#ifdef Q_OS_UNIX
ProjectExplorer::Abi hostAbi = ProjectExplorer::Abi::hostAbi();
if (hostAbi == tcAbi)
binary = QLatin1String("gdb");
#endif
abiToGdbMap.insert(tc->targetAbi().toString(), binary);
}
void GdbOptionsPage::handleToolChainRemovals(ProjectExplorer::ToolChain *tc)
{
QList<ProjectExplorer::ToolChain *> tcs = ProjectExplorer::ToolChainManager::instance()->toolChains();
foreach (ProjectExplorer::ToolChain *current, tcs) {
if (current->targetAbi() == tc->targetAbi())
return;
}
abiToGdbMap.remove(tc->targetAbi().toString());
}
} // namespace Internal
} // namespace Debugger

View File

@@ -39,6 +39,10 @@
#include <coreplugin/dialogs/ioptionspage.h>
#include <utils/savedaction.h>
namespace ProjectExplorer {
class ToolChain;
} // namespace ProjectExplorer
namespace Debugger {
namespace Internal {
@@ -61,11 +65,15 @@ public:
static QString settingsId();
typedef QMultiMap<QString, int> GdbBinaryToolChainMap;
static GdbBinaryToolChainMap gdbBinaryToolChainMap;
static bool gdbBinariesChanged;
static void readGdbBinarySettings();
static void writeGdbBinarySettings();
typedef QMap<QString, QString> GdbBinaryToolChainMap;
static GdbBinaryToolChainMap abiToGdbMap;
static bool gdbMappingChanged;
static void readGdbSettings();
static void writeGdbSettings();
private slots:
static void handleToolChainAdditions(ProjectExplorer::ToolChain *);
static void handleToolChainRemovals(ProjectExplorer::ToolChain *);
private:
Ui::GdbOptionsPage *m_ui;

View File

@@ -41,7 +41,7 @@
#include <utils/qtcassert.h>
#include <utils/fancymainwindow.h>
#include <projectexplorer/toolchaintype.h>
#include <projectexplorer/abi.h>
#include <QtCore/QFileInfo>
#include <QtGui/QMessageBox>
@@ -59,9 +59,11 @@ namespace Internal {
//
///////////////////////////////////////////////////////////////////////
RemoteGdbServerAdapter::RemoteGdbServerAdapter(GdbEngine *engine, int toolChainType, QObject *parent) :
RemoteGdbServerAdapter::RemoteGdbServerAdapter(GdbEngine *engine,
const ProjectExplorer::Abi &abi,
QObject *parent) :
AbstractGdbAdapter(engine, parent),
m_toolChainType(toolChainType)
m_abi(abi)
{
connect(&m_uploadProc, SIGNAL(error(QProcess::ProcessError)),
SLOT(uploadProcError(QProcess::ProcessError)));
@@ -75,21 +77,10 @@ RemoteGdbServerAdapter::RemoteGdbServerAdapter(GdbEngine *engine, int toolChainT
AbstractGdbAdapter::DumperHandling RemoteGdbServerAdapter::dumperHandling() const
{
switch (m_toolChainType) {
case ProjectExplorer::ToolChain_MinGW:
case ProjectExplorer::ToolChain_MSVC:
case ProjectExplorer::ToolChain_WINCE:
case ProjectExplorer::ToolChain_WINSCW:
case ProjectExplorer::ToolChain_GCCE:
case ProjectExplorer::ToolChain_RVCT2_ARMV5:
case ProjectExplorer::ToolChain_RVCT2_ARMV6:
case ProjectExplorer::ToolChain_GCC_MAEMO5:
case ProjectExplorer::ToolChain_GCC_HARMATTAN:
case ProjectExplorer::ToolChain_GCC_MEEGO:
if (m_abi.os() == ProjectExplorer::Abi::Symbian
|| m_abi.os() == ProjectExplorer::Abi::Windows
|| m_abi.binaryFormat() == ProjectExplorer::Abi::Format_ELF)
return DumperLoadedByGdb;
default:
break;
}
return DumperLoadedByGdbPreload;
}

View File

@@ -37,6 +37,8 @@
#include "abstractgdbadapter.h"
#include "localgdbprocess.h"
#include <projectexplorer/abi.h>
namespace Debugger {
namespace Internal {
@@ -51,7 +53,7 @@ class RemoteGdbServerAdapter : public AbstractGdbAdapter
Q_OBJECT
public:
RemoteGdbServerAdapter(GdbEngine *engine, int toolChainType, QObject *parent = 0);
RemoteGdbServerAdapter(GdbEngine *engine, const ProjectExplorer::Abi &abi, QObject *parent = 0);
private:
DumperHandling dumperHandling() const;
@@ -93,7 +95,7 @@ private:
void handleTargetRemote(const GdbResponse &response);
void handleInterruptInferior(const GdbResponse &response);
const int m_toolChainType;
const ProjectExplorer::Abi m_abi;
QProcess m_uploadProc;
LocalGdbProcess m_gdbProc;