Commit 22ab8d56 authored by Friedemann Kleint's avatar Friedemann Kleint
Browse files

Debugger: Do configuration error checking early on.



Add a configuration checking method to the Debugger manager,
depending on toolchain, wire it to the engines.
Check that in the debugger run controls.

Add a convenience method to ICore that shows a warning
message with a "Settings" button, pointing the user
to a configuration error on a settings page.

Remove leftovers of the dumper parser.
Acked-by: default avatarcon <qtc-committer@nokia.com>
parent c6de8d45
......@@ -69,6 +69,17 @@ bool CoreImpl::showOptionsDialog(const QString &group, const QString &page, QWid
return m_mainwindow->showOptionsDialog(group, page, parent);
}
bool CoreImpl::showWarningWithOptions(const QString &title, const QString &text,
const QString &details,
const QString &settingsCategory,
const QString &settingsId,
QWidget *parent)
{
return m_mainwindow->showWarningWithOptions(title, text,
details, settingsCategory,
settingsId, parent);
}
ActionManager *CoreImpl::actionManager() const
{
return m_mainwindow->actionManager();
......
......@@ -50,6 +50,11 @@ public:
bool showOptionsDialog(const QString &group = QString(),
const QString &page = QString(),
QWidget *parent = 0);
bool showWarningWithOptions(const QString &title, const QString &text,
const QString &details = QString(),
const QString &settingsCategory = QString(),
const QString &settingsId = QString(),
QWidget *parent = 0);
ActionManager *actionManager() const;
FileManager *fileManager() const ;
......
......@@ -68,7 +68,7 @@
*/
/*!
\fn void ICore::showOptionsDialog(const QString &group = QString(),
\fn bool ICore::showOptionsDialog(const QString &group = QString(),
const QString &page = QString())
\brief Opens the application options/preferences dialog with preselected
\a page in a specified \a group.
......@@ -76,6 +76,20 @@
The arguments refer to the string IDs of the corresponding IOptionsPage.
*/
/*!
\fn bool ICore::showWarningWithOptions(const QString &title, const QString &text,
const QString &details = QString(),
const QString &settingsCategory = QString(),
const QString &settingsId = QString(),
QWidget *parent = 0);
\brief Show a warning message with a button that opens a settings page.
Should be used to display configuration errors and point users to the setting.
Returns true if the settings dialog was accepted.
*/
/*!
\fn ActionManager *ICore::actionManager() const
\brief Returns the application's action manager.
......
......@@ -75,6 +75,12 @@ public:
const QString &page = QString(),
QWidget *parent = 0) = 0;
virtual bool showWarningWithOptions(const QString &title, const QString &text,
const QString &details = QString(),
const QString &settingsCategory = QString(),
const QString &settingsId = QString(),
QWidget *parent = 0) = 0;
virtual ActionManager *actionManager() const = 0;
virtual FileManager *fileManager() const = 0;
virtual UniqueIDManager *uniqueIDManager() const = 0;
......
......@@ -87,6 +87,7 @@
#include <QtGui/QWizard>
#include <QtGui/QPrinter>
#include <QtGui/QToolButton>
#include <QtGui/QMessageBox>
/*
#ifdef Q_OS_UNIX
......@@ -1262,3 +1263,28 @@ void MainWindow::setFullScreen(bool on)
}
}
// Display a warning with an additional button to open
// the debugger settings dialog if settingsId is nonempty.
bool MainWindow::showWarningWithOptions(const QString &title,
const QString &text,
const QString &details,
const QString &settingsCategory,
const QString &settingsId,
QWidget *parent)
{
if (parent == 0)
parent = this;
QMessageBox msgBox(QMessageBox::Warning, title, text,
QMessageBox::Ok, parent);
if (details.isEmpty())
msgBox.setDetailedText(details);
QAbstractButton *settingsButton = 0;
if (!settingsId.isEmpty() || !settingsCategory.isEmpty())
settingsButton = msgBox.addButton(tr("Settings..."), QMessageBox::AcceptRole);
msgBox.exec();
if (settingsButton && msgBox.clickedButton() == settingsButton) {
return showOptionsDialog(settingsCategory, settingsId);
}
return false;
}
......@@ -138,6 +138,12 @@ public slots:
const QString &page = QString(),
QWidget *parent = 0);
bool showWarningWithOptions(const QString &title, const QString &text,
const QString &details = QString(),
const QString &settingsCategory = QString(),
const QString &settingsId = QString(),
QWidget *parent = 0);
protected:
virtual void changeEvent(QEvent *e);
virtual void closeEvent(QCloseEvent *event);
......
......@@ -51,7 +51,6 @@
#include <QtGui/QPushButton>
#include <QtGui/QProxyModel>
#include <QtGui/QSortFilterProxyModel>
#include <QtGui/QMessageBox>
namespace Debugger {
namespace Internal {
......@@ -519,28 +518,5 @@ bool AddressDialog::isValid() const
return ok;
}
int warningWithSettings(const QString &title,
const QString &text,
const QString &details,
const QString &settingsId,
QWidget *parent)
{
QMessageBox msgBox(QMessageBox::Warning, title, text,
QMessageBox::Ok, parent);
if (details.isEmpty())
msgBox.setDetailedText(details);
QAbstractButton *settingsButton = 0;
if (!settingsId.isEmpty())
settingsButton = msgBox.addButton(QCoreApplication::translate("Debugger::MessageBox", "Settings..."),
QMessageBox::AcceptRole);
const int dialogCode = msgBox.exec();
if (settingsButton && msgBox.clickedButton() == settingsButton) {
Core::ICore::instance()->showOptionsDialog(QLatin1String(Debugger::Constants::DEBUGGER_SETTINGS_CATEGORY),
settingsId);
return 2;
}
return dialogCode;
}
} // namespace Internal
} // namespace Debugger
......@@ -62,14 +62,6 @@ struct ProcData
QString state;
};
// Display a warning with an additional button to open
// the debugger settings dialog if settingsId is nonempty.
int warningWithSettings(const QString &title,
const QString &text,
const QString &details = QString(),
const QString &settingsId = QString(),
QWidget *parent = 0);
class AttachCoreDialog : public QDialog
{
Q_OBJECT
......
......@@ -925,11 +925,8 @@ static IDebuggerEngine *determineDebuggerEngine(const QString &executable,
// We need the CDB debugger in order to be able to debug VS
// executables
if (!winEngine) {
*errorMessage = DebuggerManager::tr("Debugging VS executables is currently not enabled.");
*settingsIdHint = QLatin1String("Cdb");
if (!DebuggerManager::instance()->checkDebugConfiguration(ProjectExplorer::ToolChain::MSVC, errorMessage, 0 , settingsIdHint))
return 0;
}
return winEngine;
#endif
}
......@@ -991,7 +988,9 @@ void DebuggerManager::startNewDebugger(const DebuggerStartParametersPtr &sp)
// Create Message box with possibility to go to settings
const QString msg = tr("Cannot debug '%1' (tool chain: '%2'): %3").
arg(d->m_startParameters->executable, toolChainName, errorMessage);
warningWithSettings(tr("Warning"), msg, QString(), settingsIdHint);
Core::ICore::instance()->showWarningWithOptions(tr("Warning"), msg, QString(),
QLatin1String(DEBUGGER_SETTINGS_CATEGORY),
settingsIdHint);
return;
}
......@@ -1713,6 +1712,49 @@ bool DebuggerManager::debuggerActionsEnabled() const
return false;
}
bool DebuggerManager::checkDebugConfiguration(int toolChain,
QString *errorMessage,
QString *settingsCategory /* = 0 */,
QString *settingsPage /* = 0 */) const
{
errorMessage->clear();
if (settingsCategory)
settingsCategory->clear();
if (settingsPage)
settingsPage->clear();
bool success = true;
switch(toolChain) {
case ProjectExplorer::ToolChain::GCC:
case ProjectExplorer::ToolChain::LinuxICC:
case ProjectExplorer::ToolChain::MinGW:
case ProjectExplorer::ToolChain::WINCE: // S60
case ProjectExplorer::ToolChain::WINSCW:
case ProjectExplorer::ToolChain::GCCE:
case ProjectExplorer::ToolChain::RVCT_ARMV5:
case ProjectExplorer::ToolChain::RVCT_ARMV6:
if (gdbEngine) {
success = gdbEngine->checkConfiguration(toolChain, errorMessage, settingsPage);
} else {
success = false;
*errorMessage = msgEngineNotAvailable("Gdb");
}
break;
case ProjectExplorer::ToolChain::MSVC:
if (winEngine) {
success = winEngine->checkConfiguration(toolChain, errorMessage, settingsPage);
} else {
success = false;
*errorMessage = msgEngineNotAvailable("Cdb");
if (settingsPage)
*settingsPage = QLatin1String("Cdb");
}
break;
}
if (!success && settingsCategory && settingsPage && !settingsPage->isEmpty())
*settingsCategory = QLatin1String(DEBUGGER_SETTINGS_CATEGORY);
return success;
}
QDebug operator<<(QDebug d, DebuggerState state)
{
return d << stateName(state) << '(' << int(state) << ')';
......
......@@ -173,6 +173,11 @@ public:
bool debuggerActionsEnabled() const;
bool checkDebugConfiguration(int toolChain,
QString *errorMessage,
QString *settingsCategory = 0,
QString *settingsPage = 0) const;
static DebuggerManager *instance();
public slots:
......
......@@ -36,6 +36,7 @@
#include <projectexplorer/projectexplorerconstants.h>
#include <utils/qtcassert.h>
#include <coreplugin/icore.h>
#include <QtCore/QDebug>
#include <QtCore/QDir>
......@@ -174,7 +175,19 @@ DebuggerRunControl::DebuggerRunControl(DebuggerManager *manager,
void DebuggerRunControl::start()
{
m_running = true;
m_manager->startNewDebugger(m_startParameters);
QString errorMessage;
QString settingsCategory;
QString settingsPage;
if (m_manager->checkDebugConfiguration(startParameters()->toolChainType, &errorMessage,
&settingsCategory, &settingsPage)) {
m_manager->startNewDebugger(m_startParameters);
} else {
error(this, errorMessage);
emit finished();
Core::ICore::instance()->showWarningWithOptions(tr("Debugger"), errorMessage,
QString(),
settingsCategory, settingsPage);
}
}
void DebuggerRunControl::slotAddToOutputWindowInline(const QString &data)
......
......@@ -1409,6 +1409,24 @@ int GdbEngine::currentFrame() const
return manager()->stackHandler()->currentIndex();
}
bool GdbEngine::checkConfiguration(int toolChain, QString *errorMessage, QString *settingsPage) const
{
switch (toolChain) {
case ProjectExplorer::ToolChain::WINSCW: // S60
case ProjectExplorer::ToolChain::GCCE:
case ProjectExplorer::ToolChain::RVCT_ARMV5:
case ProjectExplorer::ToolChain::RVCT_ARMV6:
if (!m_trkOptions->check(errorMessage)) {
if (settingsPage)
*settingsPage = TrkOptionsPage::settingsId();
return false;
}
default:
break;
}
return true;
}
AbstractGdbAdapter *GdbEngine::createAdapter(const DebuggerStartParametersPtr &sp)
{
switch (sp->toolChainType) {
......@@ -3983,7 +4001,8 @@ void GdbEngine::handleAdapterStartFailed(const QString &msg, const QString &sett
{
setState(AdapterStartFailed);
debugMessage(_("ADAPTER START FAILED"));
warningWithSettings(tr("Adapter start failed"), msg, QString(), settingsIdHint);
Core::ICore::instance()->showWarningWithOptions(tr("Adapter start failed"), msg, QString(),
QLatin1String(Debugger::Constants::DEBUGGER_SETTINGS_CATEGORY), settingsIdHint);
shutdown();
}
......
......@@ -151,6 +151,8 @@ private:
Q_SLOT void setAutoDerefPointers(const QVariant &on);
virtual bool isGdbEngine() const { return true; }
virtual bool checkConfiguration(int toolChain, QString *errorMessage, QString *settingsPage= 0) const;
//
// Own stuff
//
......
......@@ -28,9 +28,11 @@
**************************************************************************/
#include "trkoptions.h"
#include <utils/synchronousprocess.h>
#include <QtCore/QSettings>
#include <QtCore/QFileInfo>
#include <QtCore/QCoreApplication>
#ifdef Q_OS_WIN
# define SERIALPORT_ROOT "COM"
......@@ -104,6 +106,24 @@ void TrkOptions::toSettings(QSettings *s) const
s->endGroup();
}
bool TrkOptions::check(QString *errorMessage) const
{
if (gdb.isEmpty()) {
*errorMessage = QCoreApplication::translate("TrkOptions", "No Symbian gdb executable specified.");
return false;
}
const QString expanded = Utils::SynchronousProcess::locateBinary(gdb);
if (expanded.isEmpty()) {
*errorMessage = QCoreApplication::translate("TrkOptions", "The Symbian gdb executable '%1' could not be found in the search path.").arg(gdb);
return false;
}
if (!cygwin.isEmpty() && !QFileInfo(cygwin).isDir()) {
*errorMessage = QCoreApplication::translate("TrkOptions", "The Cygwin directory '%1' does not exist.").arg(cygwin);
return false;
}
return true;
}
bool TrkOptions::equals(const TrkOptions &o) const
{
return mode == o.mode
......
......@@ -54,6 +54,8 @@ struct TrkOptions
void toSettings(QSettings *s) const;
bool equals(const TrkOptions &o) const;
bool check(QString *errorMessage) const;
// Lists of choices for the devices
static QStringList serialPorts();
static QStringList blueToothDevices();
......
......@@ -117,6 +117,7 @@ public:
virtual void addOptionPages(QList<Core::IOptionsPage*> *) const {}
virtual bool isGdbEngine() const { return false; }
virtual bool checkConfiguration(int /* toolChain */, QString * /* errorMessage */, QString * /* settingsPage */ = 0) const { return true; }
protected:
void showStatusMessage(const QString &msg, int timeout = -1);
......
......@@ -695,179 +695,6 @@ void QtDumperHelper::parseQueryTypes(const QStringList &l, Debugger debugger)
}
}
/* A parse for dumper output:
* "iname="local.sl",addr="0x0012BA84",value="<3 items>",valuedisabled="true",
* numchild="3",childtype="QString",childnumchild="0",children=[{name="0",value="<binhex>",
* valueencoded="2"},{name="1",value="dAB3AG8A",valueencoded="2"},{name="2",
* value="dABoAHIAZQBlAA==",valueencoded="2"}]"
* Default implementation can be used for debugging purposes. */
class DumperParser
{
public:
explicit DumperParser(const char *s) : m_s(s) {}
bool run();
virtual ~DumperParser() {}
protected:
// handle 'key="value"'
virtual bool handleKeyword(const char *k, int size);
virtual bool handleListStart();
virtual bool handleListEnd();
virtual bool handleHashStart();
virtual bool handleHashEnd();
virtual bool handleValue(const char *k, int size);
private:
bool parseHash(int level, const char *&pos);
bool parseValue(int level, const char *&pos);
bool parseStringValue(const char *&ptr, int &size, const char *&pos) const;
const char *m_s;
};
// get a string value with pos at the opening double quote
bool DumperParser::parseStringValue(const char *&ptr, int &size, const char *&pos) const
{
pos++;
const char *endValuePtr = strchr(pos, '"');
if (!endValuePtr)
return false;
size = endValuePtr - pos;
ptr = pos;
pos = endValuePtr + 1;
return true;
}
bool DumperParser::run()
{
const char *ptr = m_s;
const bool rc = parseHash(0, ptr);
if (debug > 1)
qDebug() << Q_FUNC_INFO << '\n' << m_s << rc;
return rc;
}
// Parse a non-empty hash with pos at the first keyword.
// Curly braces are present at level 0 only.
// '{a="X", b="X"}'
bool DumperParser::parseHash(int level, const char *&pos)
{
while (true) {
switch (*pos) {
case '\0': // EOS is acceptable at level 0 only
return level == 0;
case '}':
pos++;
return true;
default:
break;
}
const char *equalsPtr = strchr(pos, '=');
if (!equalsPtr)
return false;
const int keywordLen = equalsPtr - pos;
if (!handleKeyword(pos, keywordLen))
return false;
pos = equalsPtr + 1;
if (!*pos)
return false;
if (!parseValue(level + 1, pos))
return false;
if (*pos == ',')
pos++;
}
return false;
}
bool DumperParser::parseValue(int level, const char *&pos)
{
// Simple string literal
switch (*pos) {
case '"': {
const char *valuePtr;
int valueSize;
return parseStringValue(valuePtr, valueSize, pos) && handleValue(valuePtr, valueSize);
}
// A List. Note that it has a trailing comma '["a",]'
case '[': {
if (!handleListStart())
return false;
pos++;
while (true) {
switch (*pos) {
case ']':
pos++;
return handleListEnd();
case '\0':
return false;
default:
break;
}
if (!parseValue(level + 1, pos))
return false;
if (*pos == ',')
pos++;
}
}
return false;
// A hash '{a="b",b="c"}'
case '{': {
if (!handleHashStart())
return false;
pos++;
if (!parseHash(level + 1, pos))
return false;
return handleHashEnd();
}
return false;
}
return false;
}
bool DumperParser::handleKeyword(const char *k, int size)
{
if (debug > 1)
qDebug() << Q_FUNC_INFO << '\n' << QByteArray(k, size);
return true;
}
bool DumperParser::handleListStart()
{
if (debug > 1)
qDebug() << Q_FUNC_INFO;
return true;
}
bool DumperParser::handleListEnd()
{
if (debug > 1)
qDebug() << Q_FUNC_INFO;
return true;
}
bool DumperParser::handleHashStart()
{
if (debug > 1)
qDebug() << Q_FUNC_INFO;
return true;
}
bool DumperParser::handleHashEnd()
{
if (debug > 1)
qDebug() << Q_FUNC_INFO;
return true;
}
bool DumperParser::handleValue(const char *k, int size)
{
if (debug > 1)
qDebug() << Q_FUNC_INFO << '\n' << QByteArray(k, size);
return true;
}
static inline QString qClassName(const QString &qtNamespace, const char *className)
{
if (qtNamespace.isEmpty())
......
......@@ -558,8 +558,22 @@ void S60DeviceRunControlBase::start()
emit addToOutputWindow(this, tr("Creating %1.sisx ...").arg(QDir::toNativeSeparators(m_baseFileName)));
emit addToOutputWindow(this, tr("Executable file: %1").arg(m_executableFileName));
if (!createPackageFileFromTemplate())
QString errorMessage;
QString settingsCategory;
QString settingsPage;
if (!checkConfiguration(&errorMessage, &settingsCategory, &settingsPage)) {
error(this, errorMessage);
emit finished();
Core::ICore::instance()->showWarningWithOptions(tr("S60 Debugger"), errorMessage, QString(),
settingsCategory, settingsPage);
return;
}
if (!createPackageFileFromTemplate(&errorMessage)) {
error(this, errorMessage);
emit finished();
return;
}
m_makesis->setWorkingDirectory(