Commit fbfa8255 authored by Friedemann Kleint's avatar Friedemann Kleint
Browse files

Symbian/Linux: Remove GnuPoc autodetection code

and make it possible to configure it in the settings.
Split up S60Devices up into a class hierarchy and implement
the Autodetected Windows case and the manually configured
Linux case separately for code clarity. Same with the settings
widgets.
Reviewed-by: con
parent 850c7251
......@@ -51,7 +51,7 @@ static QString gcceCommand(const QString &dir)
gcce += QLatin1String(".exe");
#endif
const QString rc = env.searchInPath(gcce);
if (rc.isEmpty()) {
if (debug && rc.isEmpty()) {
const QString msg = QString::fromLatin1("GCCEToolChain: Unable to locate '%1' in '%2' (GCCE root: '%3')")
.arg(gcce, env.value(QLatin1String("PATH")), dir);
qWarning("%s", qPrintable(msg));
......
......@@ -31,6 +31,7 @@
#include "gccetoolchain.h"
#include <projectexplorer/environment.h>
#include <coreplugin/icore.h>
#include <QtCore/QSettings>
#include <QtCore/QXmlStreamReader>
......@@ -51,16 +52,36 @@ namespace {
const char * const DEVICE_DEFAULT = "default";
const char * const DEVICE_EPOCROOT = "epocroot";
const char * const DEVICE_TOOLSROOT = "toolsroot";
const char * const GNUPOC_SETTINGS_GROUP = "GnuPocSDKs";
const char * const AUTODETECT_SETTINGS_GROUP = "SymbianSDKs";
const char * const SDK_QT_ASSOC_SETTINGS_KEY_ROOT = "SymbianSDK";
const char * const SETTINGS_DEFAULT_SDK_POSTFIX = ",default";
}
namespace Qt4ProjectManager {
namespace Internal {
static int findDefaultDevice(const QList<S60Devices::Device> &d)
{
const int count = d.size();
for (int i = 0; i < count; i++)
if (d.at(i).isDefault)
return i;
return -1;
}
S60Devices::Device::Device() :
isDefault(false)
{
}
bool S60Devices::Device::equals(const Device &rhs) const
{
return id == rhs.id && name == rhs.name && isDefault == rhs.isDefault
&& epocRoot == rhs.epocRoot && toolsRoot == rhs.toolsRoot
&& qt == rhs.qt;
}
QString S60Devices::Device::toHtml() const
{
QString rc;
......@@ -79,60 +100,180 @@ QString S60Devices::Device::toHtml() const
return rc;
}
S60Devices::S60Devices(QObject *parent)
: QObject(parent)
// ------------ S60Devices
S60Devices::S60Devices(QObject *parent) : QObject(parent)
{
}
// GNU-Poc stuff
static const char *epocRootC = "EPOCROOT";
QList<S60Devices::Device> S60Devices::devices() const
{
return m_devices;
}
static inline QString msgEnvVarNotSet(const char *var)
void S60Devices::setDevices(const QList<Device> &devices)
{
return QString::fromLatin1("The environment variable %1 is not set.").arg(QLatin1String(var));
if (m_devices != devices) {
m_devices = devices;
// Ensure a default device
if (!m_devices.isEmpty() && findDefaultDevice(m_devices) == -1)
m_devices.front().isDefault = true;
writeSettings();
emit qtVersionsChanged();
}
}
static inline QString msgEnvVarDirNotExist(const QString &dir, const char *var)
const QList<S60Devices::Device> &S60Devices::devicesList() const
{
return QString::fromLatin1("The directory %1 pointed to by the environment variable %2 is not set.").arg(dir, QLatin1String(var));
return m_devices;
}
bool S60Devices::readLinux()
QList<S60Devices::Device> &S60Devices::devicesList()
{
// Detect GNUPOC_ROOT/EPOC ROOT
const QByteArray epocRootA = qgetenv(epocRootC);
if (epocRootA.isEmpty()) {
m_errorString = msgEnvVarNotSet(epocRootC);
return false;
}
return m_devices;
}
const QDir epocRootDir(QString::fromLocal8Bit(epocRootA));
if (!epocRootDir.exists()) {
m_errorString = msgEnvVarDirNotExist(epocRootDir.absolutePath(), epocRootC);
return false;
}
S60Devices *S60Devices::createS60Devices(QObject *parent)
{
S60Devices *rc = 0;
#ifdef Q_OS_WIN
AutoDetectS60QtDevices *ad = new AutoDetectS60QtDevices(parent);
ad->detectDevices();
rc = ad;
#else
rc = new GnuPocS60Devices(parent);
#endif
rc->readSettings();
return rc;
}
// Check Qt
Device device;
device.id = device.name = QLatin1String("GnuPoc");
device.toolsRoot = device.epocRoot = epocRootDir.absolutePath();
device.isDefault = true;
m_devices.push_back(device);
return true;
int S60Devices::findById(const QString &id) const
{
const int count = m_devices.size();
for (int i = 0; i < count; i++)
if (m_devices.at(i).id == id)
return i;
return -1;
}
bool S60Devices::read()
int S60Devices::findByEpocRoot(const QString &er) const
{
m_devices.clear();
m_errorString.clear();
const int count = m_devices.size();
for (int i = 0; i < count; i++)
if (m_devices.at(i).epocRoot == er)
return i;
return -1;
}
S60Devices::Device S60Devices::deviceForId(const QString &id) const
{
const int index = findById(id);
return index == -1 ? Device() : m_devices.at(index);
}
S60Devices::Device S60Devices::deviceForEpocRoot(const QString &root) const
{
const int index = findByEpocRoot(root);
return index == -1 ? Device() : m_devices.at(index);
}
S60Devices::Device S60Devices::defaultDevice() const
{
const int index = findDefaultDevice(m_devices);
return index == -1 ? Device() : m_devices.at(index);
}
QString S60Devices::cleanedRootPath(const QString &deviceRoot)
{
QString path = deviceRoot;
#ifdef Q_OS_WIN
return readWin();
#else
return readLinux();
// sbsv2 actually recommends having the DK on a separate drive...
// But qmake breaks when doing that!
if (path.size() > 1 && path.at(1) == QChar(':'))
path = path.mid(2);
#endif
if (!path.size() || path.at(path.size()-1) != '/')
path.append('/');
return path;
}
S60Devices::StringStringPairList S60Devices::readSdkQtAssociationSettings(const QSettings *settings,
const QString &group,
int *defaultIndexPtr)
{
StringStringPairList rc;
// Read out numbered pairs of EpocRoot/QtDir as many as exist
// "SymbianSDK1=/epoc,/qt[,default]".
const QChar separator = QLatin1Char(',');
const QString keyRoot = group + QLatin1Char('/') + QLatin1String(SDK_QT_ASSOC_SETTINGS_KEY_ROOT);
int defaultIndex = -1;
for (int i = 1; ; i++) {
// Split pairs of epocroot/qtdir.
const QVariant valueV = settings->value(keyRoot + QString::number(i));
if (!valueV.isValid())
break;
// Check for default postfix
QString value = valueV.toString();
if (value.endsWith(QLatin1String(SETTINGS_DEFAULT_SDK_POSTFIX))) {
value.truncate(value.size() - qstrlen(SETTINGS_DEFAULT_SDK_POSTFIX));
defaultIndex = rc.size();
}
// Split into SDK and Qt
const int separatorPos = value.indexOf(separator);
if (separatorPos == -1)
break;
const QString epocRoot = value.left(separatorPos);
const QString qtDir = value.mid(separatorPos + 1);
rc.push_back(StringStringPair(epocRoot, qtDir));
}
if (defaultIndexPtr)
*defaultIndexPtr = defaultIndex;
return rc;
}
void S60Devices::writeSdkQtAssociationSettings(QSettings *settings, const QString &group) const
{
// Write out as numbered pairs of EpocRoot/QtDir and indicate default
// "SymbianSDK1=/epoc,/qt[,default]".
settings->beginGroup(group);
settings->remove(QString()); // remove all keys
if (const int count = devicesList().size()) {
const QString keyRoot = QLatin1String(SDK_QT_ASSOC_SETTINGS_KEY_ROOT);
const QChar separator = QLatin1Char(',');
for (int i = 0; i < count; i++) {
const QString key = keyRoot + QString::number(i + 1);
QString value = devicesList().at(i).epocRoot;
value += separator;
value += devicesList().at(i).qt;
// Indicate default by postfix ",default"
if (devicesList().at(i).isDefault)
value += QLatin1String(SETTINGS_DEFAULT_SDK_POSTFIX);
settings->setValue(key, value);
}
}
settings->endGroup();
}
void S60Devices::readSettings()
{
}
void S60Devices::writeSettings()
{
}
// ------------------ S60Devices
AutoDetectS60Devices::AutoDetectS60Devices(QObject *parent) :
S60Devices(parent)
{
}
QString AutoDetectS60Devices::errorString() const
{
return m_errorString;
}
// Windows: Get list of paths containing common program data
// as pointed to by environment.
static QStringList commonProgramFilesPaths()
{
......@@ -147,8 +288,6 @@ static QStringList commonProgramFilesPaths()
return rc;
}
// Windows EPOC
// Find the "devices.xml" file containing the SDKs
static QString devicesXmlFile(QString *errorMessage)
{
......@@ -174,8 +313,10 @@ static QString devicesXmlFile(QString *errorMessage)
return QString();
}
bool S60Devices::readWin()
bool AutoDetectS60Devices::detectDevices()
{
devicesList().clear();
m_errorString.clear();
const QString devicesXmlPath = devicesXmlFile(&m_errorString);
if (devicesXmlPath.isEmpty())
return false;
......@@ -210,7 +351,7 @@ bool S60Devices::readWin()
}
if (device.toolsRoot.isEmpty())
device.toolsRoot = device.epocRoot;
m_devices.append(device);
devicesList().append(device);
}
}
} else {
......@@ -227,6 +368,35 @@ bool S60Devices::readWin()
return true;
}
void AutoDetectS60Devices::readSettings()
{
// Read the associated Qt version from the settings
// and set on the autodetected SDKs.
bool changed = false;
const QSettings *settings = Core::ICore::instance()->settings();
foreach (const StringStringPair &p, readSdkQtAssociationSettings(settings, QLatin1String(GNUPOC_SETTINGS_GROUP))) {
const int index = findByEpocRoot(p.first);
if (index != -1 && devicesList().at(index).qt != p.second) {
devicesList()[index].qt = p.second;
changed = true;
}
}
if (changed)
emit qtVersionsChanged();
}
void AutoDetectS60Devices::writeSettings()
{
writeSdkQtAssociationSettings(Core::ICore::instance()->settings(), QLatin1String(AUTODETECT_SETTINGS_GROUP));
}
// ========== AutoDetectS60QtDevices
AutoDetectS60QtDevices::AutoDetectS60QtDevices(QObject *parent) :
AutoDetectS60Devices(parent)
{
}
// Detect a Qt version that is installed into a Symbian SDK
static QString detect_SDK_installedQt(const QString &epocRoot)
{
......@@ -263,28 +433,14 @@ static QString detect_SDK_installedQt(const QString &epocRoot)
return QDir(QString::fromLatin1(buffer.mid(index, lastIndex-index))).absolutePath();
}
// GnuPoc: Detect a Qt version that is symlinked/below an SDK
// TODO: Find a proper way of doing that
static QString detectGnuPocQt(const QString &epocRoot)
{
const QFileInfo fi(epocRoot + QLatin1String("/qt"));
if (!fi.exists())
return QString();
if (fi.isSymLink())
return QFileInfo(fi.symLinkTarget()).absoluteFilePath();
return fi.absoluteFilePath();
}
bool S60Devices::detectQtForDevices()
bool AutoDetectS60QtDevices::detectQtForDevices()
{
bool changed = false;
const int deviceCount = m_devices.size();
const int deviceCount = devicesList().size();
for (int i = 0; i < deviceCount; ++i) {
Device &device = m_devices[i];
Device &device = devicesList()[i];
if (device.qt.isEmpty()) {
device.qt = detect_SDK_installedQt(device.epocRoot);
if (device.qt.isEmpty())
device.qt = detectGnuPocQt(device.epocRoot);
if (device.qt.isEmpty()) {
qWarning("Unable to detect Qt version for '%s'.", qPrintable(device.epocRoot));
} else {
......@@ -297,59 +453,50 @@ bool S60Devices::detectQtForDevices()
return true;
}
QString S60Devices::errorString() const
{
return m_errorString;
}
QList<S60Devices::Device> S60Devices::devices() const
bool AutoDetectS60QtDevices::detectDevices()
{
return m_devices;
return AutoDetectS60Devices::detectDevices() && detectQtForDevices();
}
S60Devices::Device S60Devices::deviceForId(const QString &id) const
// ------- GnuPocS60Devices
GnuPocS60Devices::GnuPocS60Devices(QObject *parent) :
S60Devices(parent)
{
foreach (const S60Devices::Device &i, m_devices) {
if (i.id == id) {
return i;
}
}
return Device();
}
S60Devices::Device S60Devices::deviceForEpocRoot(const QString &root) const
S60Devices::Device GnuPocS60Devices::createDevice(const QString &epoc, const QString &qtDir)
{
foreach (const S60Devices::Device &i, m_devices) {
if (i.epocRoot == root) {
return i;
}
}
return Device();
Device device;
device.id = device.name = QLatin1String("GnuPoc");
device.toolsRoot = device.epocRoot = epoc;
device.qt = qtDir;
return device;
}
S60Devices::Device S60Devices::defaultDevice() const
// GnuPoc settings are just the pairs of EpocRoot and Qt Dir.
void GnuPocS60Devices::readSettings()
{
foreach (const S60Devices::Device &i, m_devices) {
if (i.isDefault) {
return i;
// Read out numbered pairs of EpocRoot/QtDir as many as exist
// "SymbianSDK1=/epoc,/qt".
devicesList().clear();
int defaultIndex = 0;
const QSettings *settings = Core::ICore::instance()->settings();
const StringStringPairList devices =readSdkQtAssociationSettings(settings, QLatin1String(GNUPOC_SETTINGS_GROUP), &defaultIndex);
foreach (const StringStringPair &p, devices)
devicesList().append(createDevice(p.first, p.second));
// Ensure a default
if (!devicesList().isEmpty()) {
if (defaultIndex >= 0 && defaultIndex < devicesList().size()) {
devicesList()[defaultIndex].isDefault = true;
} else {
devicesList().front().isDefault = true;
}
}
return Device();
}
QString S60Devices::cleanedRootPath(const QString &deviceRoot)
void GnuPocS60Devices::writeSettings()
{
QString path = deviceRoot;
#ifdef Q_OS_WIN
// sbsv2 actually recommends having the DK on a separate drive...
// But qmake breaks when doing that!
if (path.size() > 1 && path.at(1) == QChar(':'))
path = path.mid(2);
#endif
if (!path.size() || path.at(path.size()-1) != '/')
path.append('/');
return path;
writeSdkQtAssociationSettings(Core::ICore::instance()->settings(), QLatin1String(GNUPOC_SETTINGS_GROUP));
}
// S60ToolChainMixin
......
......@@ -35,20 +35,25 @@
#include <QtCore/QObject>
#include <QtCore/QString>
#include <QtCore/QList>
#include <QtCore/QPair>
QT_BEGIN_NAMESPACE
class QDebug;
class QSettings;
QT_END_NAMESPACE
namespace Qt4ProjectManager {
namespace Internal {
// List of S60 devices.
class S60Devices : public QObject
{
Q_OBJECT
public:
struct Device {
Device();
bool equals(const Device &rhs) const;
QString toHtml() const;
QString id;
......@@ -59,26 +64,106 @@ public:
QString qt;
};
S60Devices(QObject *parent = 0);
bool read();
QString errorString() const;
// Construct a devices object, does autodetection if applicable
// and restores settings.
static S60Devices *createS60Devices(QObject *parent);
QList<Device> devices() const;
bool detectQtForDevices();
// Set devices, write settings and emit changed signals accordingly.
void setDevices(const QList<Device> &device);
Device deviceForId(const QString &id) const;
Device deviceForEpocRoot(const QString &root) const;
Device defaultDevice() const;
int findByEpocRoot(const QString &er) const;
static QString cleanedRootPath(const QString &deviceRoot);
signals:
void qtVersionsChanged();
protected:
typedef QPair<QString, QString> StringStringPair;
typedef QList<StringStringPair> StringStringPairList;
explicit S60Devices(QObject *parent = 0);
const QList<Device> &devicesList() const;
QList<Device> &devicesList();
int findById(const QString &id) const;
// Helpers to serialize the association of Symbian SDK<->Qt
// to QSettings (pair of SDK/Qt).
static StringStringPairList readSdkQtAssociationSettings(const QSettings *settings,
const QString &group,
int *defaultIndex = 0);
void writeSdkQtAssociationSettings(QSettings *settings, const QString &group) const;
private:
bool readLinux();
bool readWin();
QString m_errorString;
virtual void readSettings(); // empty stubs
virtual void writeSettings();
QList<Device> m_devices;
};
inline bool operator==(const S60Devices::Device &d1, const S60Devices::Device &d2)
{ return d1.equals(d2); }
inline bool operator!=(const S60Devices::Device &d1, const S60Devices::Device &d2)
{ return !d1.equals(d2); }
// Autodetected Symbian Devices (as parsed from devices.xml file on Windows)
// with a manually set version of Qt. Currently not used, but might be by
// makefile-based builds on Windows.
class AutoDetectS60Devices : public S60Devices
{
Q_OBJECT
public:
explicit AutoDetectS60Devices(QObject *parent = 0);
virtual bool detectDevices();
QString errorString() const;
private:
// Write and restore Qt-SDK associations.
virtual void readSettings();
virtual void writeSettings();
QString m_errorString;
};
// Autodetected Symbian-Qt-Devices (with Qt installed
// into the SDK) for ABLD, Raptor. Completely autodetected.
class AutoDetectS60QtDevices : public AutoDetectS60Devices
{
Q_OBJECT
public:
explicit AutoDetectS60QtDevices(QObject *parent = 0);
// Overwritten to detect associated Qt versions in addition.
virtual bool detectDevices();
private:
// No settings as it is completely autodetected.
virtual void readSettings() {}
virtual void writeSettings() {}
bool detectQtForDevices();
};
// Manually configured Symbian Devices completely based on QSettings.
class GnuPocS60Devices : public S60Devices
{
Q_OBJECT
public:
explicit GnuPocS60Devices(QObject *parent = 0);
static Device createDevice(const QString &epoc, const QString &qtDir);
private:
virtual void readSettings();
virtual void writeSettings();