ExtensionSystem: Use Qt 5 plugin metadata instead of .pluginspec files

Change-Id: I2b2c704260c613985a4bda179658ec1f8879e70f
Reviewed-by: Christian Kandeler <christian.kandeler@digia.com>
Reviewed-by: Christian Stenger <christian.stenger@digia.com>
Reviewed-by: Daniel Teske <daniel.teske@digia.com>
Reviewed-by: hjk <hjk121@nokiamail.com>
This commit is contained in:
Eike Ziller
2014-08-26 17:29:38 +02:00
parent 8dc9d67e89
commit b5b6a46b4e
212 changed files with 2045 additions and 1921 deletions

View File

@@ -39,17 +39,20 @@
#include <QDir>
#include <QFile>
#include <QFileInfo>
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonValue>
#include <QPluginLoader>
#include <QRegExp>
#include <QXmlStreamReader>
/*!
\class ExtensionSystem::PluginDependency
\brief The PluginDependency class contains the name and required compatible
version number of a plugin's dependency.
This reflects the data of a dependency tag in the plugin's XML description
file. The name and version are used to resolve the dependency. That is,
This reflects the data of a dependency object in the plugin's meta data.
The name and version are used to resolve the dependency. That is,
a plugin with the given name and
plugin \c {compatibility version <= dependency version <= plugin version} is searched for.
@@ -86,9 +89,8 @@
/*!
\class ExtensionSystem::PluginSpec
\brief The PluginSpec class contains the information of the plugin's XML
description file and
information about the plugin's current state.
\brief The PluginSpec class contains the information of the plugin's embedded meta data
and information about the plugin's current state.
The plugin spec is also filled with more information as the plugin
goes through its loading process (see PluginSpec::State).
@@ -104,9 +106,9 @@
The state gives a hint on what went wrong in case of an error.
\value Invalid
Starting point: Even the XML description file was not read.
Starting point: Even the plugin meta data was not read.
\value Read
The XML description file has been successfully read, and its
The plugin meta data has been successfully read, and its
information is available via the PluginSpec.
\value Resolved
The dependencies given in the description file have been
@@ -448,31 +450,30 @@ QHash<PluginDependency, PluginSpec *> PluginSpec::dependencySpecs() const
//==========PluginSpecPrivate==================
namespace {
const char PLUGIN[] = "plugin";
const char PLUGIN_NAME[] = "name";
const char PLUGIN_VERSION[] = "version";
const char PLUGIN_COMPATVERSION[] = "compatVersion";
const char PLUGIN_REQUIRED[] = "required";
const char PLUGIN_EXPERIMENTAL[] = "experimental";
const char PLUGIN_DISABLED_BY_DEFAULT[] = "disabledByDefault";
const char VENDOR[] = "vendor";
const char COPYRIGHT[] = "copyright";
const char LICENSE[] = "license";
const char DESCRIPTION[] = "description";
const char URL[] = "url";
const char CATEGORY[] = "category";
const char PLATFORM[] = "platform";
const char DEPENDENCYLIST[] = "dependencyList";
const char DEPENDENCY[] = "dependency";
const char DEPENDENCY_NAME[] = "name";
const char DEPENDENCY_VERSION[] = "version";
const char DEPENDENCY_TYPE[] = "type";
const char PLUGIN_METADATA[] = "MetaData";
const char PLUGIN_NAME[] = "Name";
const char PLUGIN_VERSION[] = "Version";
const char PLUGIN_COMPATVERSION[] = "CompatVersion";
const char PLUGIN_REQUIRED[] = "Required";
const char PLUGIN_EXPERIMENTAL[] = "Experimental";
const char PLUGIN_DISABLED_BY_DEFAULT[] = "DisabledByDefault";
const char VENDOR[] = "Vendor";
const char COPYRIGHT[] = "Copyright";
const char LICENSE[] = "License";
const char DESCRIPTION[] = "Description";
const char URL[] = "Url";
const char CATEGORY[] = "Category";
const char PLATFORM[] = "Platform";
const char DEPENDENCIES[] = "Dependencies";
const char DEPENDENCY_NAME[] = "Name";
const char DEPENDENCY_VERSION[] = "Version";
const char DEPENDENCY_TYPE[] = "Type";
const char DEPENDENCY_TYPE_SOFT[] = "optional";
const char DEPENDENCY_TYPE_HARD[] = "required";
const char ARGUMENTLIST[] = "argumentList";
const char ARGUMENT[] = "argument";
const char ARGUMENT_NAME[] = "name";
const char ARGUMENT_PARAMETER[] = "parameter";
const char ARGUMENTS[] = "Arguments";
const char ARGUMENT_NAME[] = "Name";
const char ARGUMENT_PARAMETER[] = "Parameter";
const char ARGUMENT_DESCRIPTION[] = "Description";
}
/*!
\internal
@@ -494,9 +495,11 @@ PluginSpecPrivate::PluginSpecPrivate(PluginSpec *spec)
/*!
\internal
Returns false if the file does not represent a Qt Creator plugin.
*/
bool PluginSpecPrivate::read(const QString &fileName)
{
qCDebug(pluginLog) << "\nReading meta data of" << fileName;
name
= version
= compatVersion
@@ -512,30 +515,18 @@ bool PluginSpecPrivate::read(const QString &fileName)
hasError = false;
errorString.clear();
dependencies.clear();
QFile file(fileName);
if (!file.open(QIODevice::ReadOnly))
return reportError(tr("Cannot open file %1 for reading: %2")
.arg(QDir::toNativeSeparators(file.fileName()), file.errorString()));
QFileInfo fileInfo(file);
QFileInfo fileInfo(fileName);
location = fileInfo.absolutePath();
filePath = fileInfo.absoluteFilePath();
QXmlStreamReader reader(&file);
while (!reader.atEnd()) {
reader.readNext();
switch (reader.tokenType()) {
case QXmlStreamReader::StartElement:
readPluginSpec(reader);
break;
default:
break;
}
loader.setFileName(filePath);
if (loader.fileName().isEmpty()) {
qCDebug(pluginLog) << "Cannot open file";
return false;
}
if (reader.hasError())
return reportError(tr("Error parsing file %1: %2, at line %3, column %4")
.arg(QDir::toNativeSeparators(file.fileName()))
.arg(reader.errorString())
.arg(reader.lineNumber())
.arg(reader.columnNumber()));
if (!readMetaData(loader.metaData()))
return false;
state = PluginSpec::Read;
return true;
}
@@ -576,238 +567,251 @@ bool PluginSpecPrivate::reportError(const QString &err)
{
errorString = err;
hasError = true;
return false;
return true;
}
static inline QString msgAttributeMissing(const char *elt, const char *attribute)
static inline QString msgValueMissing(const char *key)
{
return QCoreApplication::translate("PluginSpec", "\"%1\" misses attribute \"%2\"").arg(QLatin1String(elt), QLatin1String(attribute));
return QCoreApplication::translate("PluginSpec", "\"%1\" is missing").arg(QLatin1String(key));
}
static inline QString msgInvalidFormat(const char *content)
static inline QString msgValueIsNotAString(const char *key)
{
return QCoreApplication::translate("PluginSpec", "\"%1\" has invalid format").arg(QLatin1String(content));
return QCoreApplication::translate("PluginSpec", "Value for key \"%1\" is not a string")
.arg(QLatin1String(key));
}
static inline QString msgInvalidElement(const QString &name)
static inline QString msgValueIsNotABool(const char *key)
{
return QCoreApplication::translate("PluginSpec", "Invalid element \"%1\"").arg(name);
return QCoreApplication::translate("PluginSpec", "Value for key \"%1\" is not a bool")
.arg(QLatin1String(key));
}
static inline QString msgUnexpectedClosing(const QString &name)
static inline QString msgValueIsNotAObjectArray(const char *key)
{
return QCoreApplication::translate("PluginSpec", "Unexpected closing element \"%1\"").arg(name);
return QCoreApplication::translate("PluginSpec", "Value for key \"%1\" is not an array of objects")
.arg(QLatin1String(key));
}
static inline QString msgUnexpectedToken()
static inline QString msgValueIsNotAMultilineString(const char *key)
{
return QCoreApplication::translate("PluginSpec", "Unexpected token");
return QCoreApplication::translate("PluginSpec", "Value for key \"%1\" is not a string and not an array of strings")
.arg(QLatin1String(key));
}
static inline QString msgInvalidFormat(const char *key, const QString &content)
{
return QCoreApplication::translate("PluginSpec", "Value \"%2\" for key \"%1\" has invalid format")
.arg(QLatin1String(key), content);
}
static inline bool readMultiLineString(const QJsonValue &value, QString *out)
{
if (!out) {
qCWarning(pluginLog) << Q_FUNC_INFO << "missing output parameter";
return false;
}
if (value.isString()) {
*out = value.toString();
} else if (value.isArray()) {
QJsonArray array = value.toArray();
QStringList lines;
foreach (const QJsonValue &v, array) {
if (!v.isString())
return false;
lines.append(v.toString());
}
*out = lines.join(QLatin1Char('\n'));
} else {
return false;
}
return true;
}
/*!
\internal
*/
void PluginSpecPrivate::readPluginSpec(QXmlStreamReader &reader)
bool PluginSpecPrivate::readMetaData(const QJsonObject &metaData)
{
if (reader.name() != QLatin1String(PLUGIN)) {
reader.raiseError(QCoreApplication::translate("PluginSpec", "Expected element \"%1\" as top level element")
.arg(QLatin1String(PLUGIN)));
return;
qCDebug(pluginLog) << "MetaData:" << QJsonDocument(metaData).toJson();
QJsonValue value;
value = metaData.value(QLatin1String("IID"));
if (!value.isString()) {
qCDebug(pluginLog) << "Not a plugin (no string IID found)";
return false;
}
name = reader.attributes().value(QLatin1String(PLUGIN_NAME)).toString();
if (name.isEmpty()) {
reader.raiseError(msgAttributeMissing(PLUGIN, PLUGIN_NAME));
return;
if (value.toString() != PluginManager::pluginIID()) {
qCDebug(pluginLog) << "Plugin ignored (IID does not match)";
return false;
}
version = reader.attributes().value(QLatin1String(PLUGIN_VERSION)).toString();
if (version.isEmpty()) {
reader.raiseError(msgAttributeMissing(PLUGIN, PLUGIN_VERSION));
return;
}
if (!isValidVersion(version)) {
reader.raiseError(msgInvalidFormat(PLUGIN_VERSION));
return;
}
compatVersion = reader.attributes().value(QLatin1String(PLUGIN_COMPATVERSION)).toString();
if (!compatVersion.isEmpty() && !isValidVersion(compatVersion)) {
reader.raiseError(msgInvalidFormat(PLUGIN_COMPATVERSION));
return;
} else if (compatVersion.isEmpty()) {
compatVersion = version;
}
required = readBooleanValue(reader, PLUGIN_REQUIRED);
experimental = readBooleanValue(reader, PLUGIN_EXPERIMENTAL);
disabledByDefault = readBooleanValue(reader, PLUGIN_DISABLED_BY_DEFAULT);
if (reader.hasError())
return;
value = metaData.value(QLatin1String(PLUGIN_METADATA));
if (!value.isObject())
return reportError(tr("Plugin meta data not found"));
QJsonObject pluginInfo = value.toObject();
value = pluginInfo.value(QLatin1String(PLUGIN_NAME));
if (value.isUndefined())
return reportError(msgValueMissing(PLUGIN_NAME));
if (!value.isString())
return reportError(msgValueIsNotAString(PLUGIN_NAME));
name = value.toString();
value = pluginInfo.value(QLatin1String(PLUGIN_VERSION));
if (value.isUndefined())
return reportError(msgValueMissing(PLUGIN_VERSION));
if (!value.isString())
return reportError(msgValueIsNotAString(PLUGIN_VERSION));
version = value.toString();
if (!isValidVersion(version))
return reportError(msgInvalidFormat(PLUGIN_VERSION, version));
value = pluginInfo.value(QLatin1String(PLUGIN_COMPATVERSION));
if (!value.isUndefined() && !value.isString())
return reportError(msgValueIsNotAString(PLUGIN_COMPATVERSION));
compatVersion = value.toString(version);
if (!value.isUndefined() && !isValidVersion(compatVersion))
return reportError(msgInvalidFormat(PLUGIN_COMPATVERSION, compatVersion));
value = pluginInfo.value(QLatin1String(PLUGIN_REQUIRED));
if (!value.isUndefined() && !value.isBool())
return reportError(msgValueIsNotABool(PLUGIN_REQUIRED));
required = value.toBool(false);
qCDebug(pluginLog) << "required =" << required;
value = pluginInfo.value(QLatin1String(PLUGIN_EXPERIMENTAL));
if (!value.isUndefined() && !value.isBool())
return reportError(msgValueIsNotABool(PLUGIN_EXPERIMENTAL));
experimental = value.toBool(false);
qCDebug(pluginLog) << "experimental =" << experimental;
value = pluginInfo.value(QLatin1String(PLUGIN_DISABLED_BY_DEFAULT));
if (!value.isUndefined() && !value.isBool())
return reportError(msgValueIsNotABool(PLUGIN_DISABLED_BY_DEFAULT));
disabledByDefault = value.toBool(false);
qCDebug(pluginLog) << "disabledByDefault =" << disabledByDefault;
if (experimental)
disabledByDefault = true;
enabledInSettings = !disabledByDefault;
while (!reader.atEnd()) {
reader.readNext();
switch (reader.tokenType()) {
case QXmlStreamReader::StartElement: {
const QStringRef element = reader.name();
if (element == QLatin1String(VENDOR))
vendor = reader.readElementText().trimmed();
else if (element == QLatin1String(COPYRIGHT))
copyright = reader.readElementText().trimmed();
else if (element == QLatin1String(LICENSE))
license = reader.readElementText().trimmed();
else if (element == QLatin1String(DESCRIPTION))
description = reader.readElementText().trimmed();
else if (element == QLatin1String(URL))
url = reader.readElementText().trimmed();
else if (element == QLatin1String(CATEGORY))
category = reader.readElementText().trimmed();
else if (element == QLatin1String(PLATFORM)) {
const QString platformSpec = reader.readElementText().trimmed();
if (!platformSpec.isEmpty()) {
platformSpecification.setPattern(platformSpec);
if (!platformSpecification.isValid())
reader.raiseError(QLatin1String("Invalid platform specification \"")
+ platformSpec + QLatin1String("\": ")
+ platformSpecification.errorString());
}
} else if (element == QLatin1String(DEPENDENCYLIST))
readDependencies(reader);
else if (element == QLatin1String(ARGUMENTLIST))
readArgumentDescriptions(reader);
else
reader.raiseError(msgInvalidElement(element.toString()));
}
break;
case QXmlStreamReader::EndDocument:
case QXmlStreamReader::Comment:
case QXmlStreamReader::EndElement:
case QXmlStreamReader::Characters:
break;
default:
reader.raiseError(msgUnexpectedToken());
break;
}
}
}
/*!
\internal
*/
void PluginSpecPrivate::readArgumentDescriptions(QXmlStreamReader &reader)
{
while (!reader.atEnd()) {
reader.readNext();
switch (reader.tokenType()) {
case QXmlStreamReader::StartElement:
if (reader.name() == QLatin1String(ARGUMENT))
readArgumentDescription(reader);
else
reader.raiseError(msgInvalidElement(reader.name().toString()));
break;
case QXmlStreamReader::Comment:
case QXmlStreamReader::Characters:
break;
case QXmlStreamReader::EndElement:
if (reader.name() == QLatin1String(ARGUMENTLIST))
return;
reader.raiseError(msgUnexpectedClosing(reader.name().toString()));
break;
default:
reader.raiseError(msgUnexpectedToken());
break;
}
}
}
value = pluginInfo.value(QLatin1String(VENDOR));
if (!value.isUndefined() && !value.isString())
return reportError(msgValueIsNotAString(VENDOR));
vendor = value.toString();
/*!
\internal
*/
void PluginSpecPrivate::readArgumentDescription(QXmlStreamReader &reader)
{
PluginArgumentDescription arg;
arg.name = reader.attributes().value(QLatin1String(ARGUMENT_NAME)).toString();
if (arg.name.isEmpty()) {
reader.raiseError(msgAttributeMissing(ARGUMENT, ARGUMENT_NAME));
return;
}
arg.parameter = reader.attributes().value(QLatin1String(ARGUMENT_PARAMETER)).toString();
arg.description = reader.readElementText();
if (reader.tokenType() != QXmlStreamReader::EndElement)
reader.raiseError(msgUnexpectedToken());
argumentDescriptions.push_back(arg);
}
value = pluginInfo.value(QLatin1String(COPYRIGHT));
if (!value.isUndefined() && !value.isString())
return reportError(msgValueIsNotAString(COPYRIGHT));
copyright = value.toString();
bool PluginSpecPrivate::readBooleanValue(QXmlStreamReader &reader, const char *key)
{
const QStringRef valueString = reader.attributes().value(QLatin1String(key));
const bool isOn = valueString.compare(QLatin1String("true"), Qt::CaseInsensitive) == 0;
if (!valueString.isEmpty() && !isOn
&& valueString.compare(QLatin1String("false"), Qt::CaseInsensitive) != 0) {
reader.raiseError(msgInvalidFormat(key));
}
return isOn;
}
value = pluginInfo.value(QLatin1String(DESCRIPTION));
if (!value.isUndefined() && !readMultiLineString(value, &description))
return reportError(msgValueIsNotAString(DESCRIPTION));
/*!
\internal
*/
void PluginSpecPrivate::readDependencies(QXmlStreamReader &reader)
{
while (!reader.atEnd()) {
reader.readNext();
switch (reader.tokenType()) {
case QXmlStreamReader::StartElement:
if (reader.name() == QLatin1String(DEPENDENCY))
readDependencyEntry(reader);
else
reader.raiseError(msgInvalidElement(reader.name().toString()));
break;
case QXmlStreamReader::Comment:
case QXmlStreamReader::Characters:
break;
case QXmlStreamReader::EndElement:
if (reader.name() == QLatin1String(DEPENDENCYLIST))
return;
reader.raiseError(msgUnexpectedClosing(reader.name().toString()));
break;
default:
reader.raiseError(msgUnexpectedToken());
break;
}
}
}
value = pluginInfo.value(QLatin1String(URL));
if (!value.isUndefined() && !value.isString())
return reportError(msgValueIsNotAString(URL));
url = value.toString();
/*!
\internal
*/
void PluginSpecPrivate::readDependencyEntry(QXmlStreamReader &reader)
{
PluginDependency dep;
dep.name = reader.attributes().value(QLatin1String(DEPENDENCY_NAME)).toString();
if (dep.name.isEmpty()) {
reader.raiseError(msgAttributeMissing(DEPENDENCY, DEPENDENCY_NAME));
return;
value = pluginInfo.value(QLatin1String(CATEGORY));
if (!value.isUndefined() && !value.isString())
return reportError(msgValueIsNotAString(CATEGORY));
category = value.toString();
value = pluginInfo.value(QLatin1String(LICENSE));
if (!value.isUndefined() && !readMultiLineString(value, &license))
return reportError(msgValueIsNotAMultilineString(LICENSE));
value = pluginInfo.value(QLatin1String(PLATFORM));
if (!value.isUndefined() && !value.isString())
return reportError(msgValueIsNotAString(PLATFORM));
const QString platformSpec = value.toString().trimmed();
if (!platformSpec.isEmpty()) {
platformSpecification.setPattern(platformSpec);
if (!platformSpecification.isValid())
return reportError(tr("Invalid platform specification \"%1\": %2")
.arg(platformSpec, platformSpecification.errorString()));
}
dep.version = reader.attributes().value(QLatin1String(DEPENDENCY_VERSION)).toString();
if (!dep.version.isEmpty() && !isValidVersion(dep.version)) {
reader.raiseError(msgInvalidFormat(DEPENDENCY_VERSION));
return;
}
dep.type = PluginDependency::Required;
if (reader.attributes().hasAttribute(QLatin1String(DEPENDENCY_TYPE))) {
const QStringRef typeValue = reader.attributes().value(QLatin1String(DEPENDENCY_TYPE));
if (typeValue == QLatin1String(DEPENDENCY_TYPE_HARD)) {
value = pluginInfo.value(QLatin1String(DEPENDENCIES));
if (!value.isUndefined() && !value.isArray())
return reportError(msgValueIsNotAObjectArray(DEPENDENCIES));
if (!value.isUndefined()) {
QJsonArray array = value.toArray();
foreach (const QJsonValue &v, array) {
if (!v.isObject())
return reportError(msgValueIsNotAObjectArray(DEPENDENCIES));
QJsonObject dependencyObject = v.toObject();
PluginDependency dep;
value = dependencyObject.value(QLatin1String(DEPENDENCY_NAME));
if (value.isUndefined())
return reportError(tr("Dependency: %1").arg(msgValueMissing(DEPENDENCY_NAME)));
if (!value.isString())
return reportError(tr("Dependency: %1").arg(msgValueIsNotAString(DEPENDENCY_NAME)));
dep.name = value.toString();
value = dependencyObject.value(QLatin1String(DEPENDENCY_VERSION));
if (!value.isUndefined() && !value.isString())
return reportError(tr("Dependency: %1").arg(msgValueIsNotAString(DEPENDENCY_VERSION)));
dep.version = value.toString();
if (!isValidVersion(dep.version))
return reportError(tr("Dependency: %1").arg(msgInvalidFormat(DEPENDENCY_VERSION,
dep.version)));
dep.type = PluginDependency::Required;
} else if (typeValue == QLatin1String(DEPENDENCY_TYPE_SOFT)) {
dep.type = PluginDependency::Optional;
} else {
reader.raiseError(msgInvalidFormat(DEPENDENCY_TYPE));
return;
value = dependencyObject.value(QLatin1String(DEPENDENCY_TYPE));
if (!value.isUndefined() && !value.isString())
return reportError(tr("Dependency: %1").arg(msgValueIsNotAString(DEPENDENCY_TYPE)));
if (!value.isUndefined()) {
const QString typeValue = value.toString();
if (typeValue.toLower() == QLatin1String(DEPENDENCY_TYPE_HARD)) {
dep.type = PluginDependency::Required;
} else if (typeValue.toLower() == QLatin1String(DEPENDENCY_TYPE_SOFT)) {
dep.type = PluginDependency::Optional;
} else {
return reportError(tr("Dependency: \"%1\" must be \"%2\" or \"%3\" (is \"%4\")")
.arg(QLatin1String(DEPENDENCY_TYPE),
QLatin1String(DEPENDENCY_TYPE_HARD),
QLatin1String(DEPENDENCY_TYPE_SOFT),
typeValue));
}
}
dependencies.append(dep);
}
}
dependencies.append(dep);
reader.readNext();
if (reader.tokenType() != QXmlStreamReader::EndElement)
reader.raiseError(msgUnexpectedToken());
value = pluginInfo.value(QLatin1String(ARGUMENTS));
if (!value.isUndefined() && !value.isArray())
return reportError(msgValueIsNotAObjectArray(ARGUMENTS));
if (!value.isUndefined()) {
QJsonArray array = value.toArray();
foreach (const QJsonValue &v, array) {
if (!v.isObject())
return reportError(msgValueIsNotAObjectArray(ARGUMENTS));
QJsonObject argumentObject = v.toObject();
PluginArgumentDescription arg;
value = argumentObject.value(QLatin1String(ARGUMENT_NAME));
if (value.isUndefined())
return reportError(tr("Argument: %1").arg(msgValueMissing(ARGUMENT_NAME)));
if (!value.isString())
return reportError(tr("Argument: %1").arg(msgValueIsNotAString(ARGUMENT_NAME)));
arg.name = value.toString();
if (arg.name.isEmpty())
return reportError(tr("Argument: \"%1\" is empty").arg(QLatin1String(ARGUMENT_NAME)));
value = argumentObject.value(QLatin1String(ARGUMENT_DESCRIPTION));
if (!value.isUndefined() && !value.isString())
return reportError(tr("Argument: %1").arg(msgValueIsNotAString(ARGUMENT_DESCRIPTION)));
arg.description = value.toString();
value = argumentObject.value(QLatin1String(ARGUMENT_PARAMETER));
if (!value.isUndefined() && !value.isString())
return reportError(tr("Argument: %1").arg(msgValueIsNotAString(ARGUMENT_PARAMETER)));
arg.parameter = value.toString();
argumentDescriptions.append(arg);
qCDebug(pluginLog) << "Argument:" << arg.name << "Parameter:" << arg.parameter
<< "Description:" << arg.description;
}
}
return true;
}
/*!
@@ -941,32 +945,9 @@ bool PluginSpecPrivate::loadLibrary()
hasError = true;
return false;
}
#ifdef QT_NO_DEBUG
#ifdef Q_OS_WIN
QString libName = QString::fromLatin1("%1/%2.dll").arg(location).arg(name);
#elif defined(Q_OS_MAC)
QString libName = QString::fromLatin1("%1/lib%2.dylib").arg(location).arg(name);
#else
QString libName = QString::fromLatin1("%1/lib%2.so").arg(location).arg(name);
#endif
#else //Q_NO_DEBUG
#ifdef Q_OS_WIN
QString libName = QString::fromLatin1("%1/%2d.dll").arg(location).arg(name);
#elif defined(Q_OS_MAC)
QString libName = QString::fromLatin1("%1/lib%2_debug.dylib").arg(location).arg(name);
#else
QString libName = QString::fromLatin1("%1/lib%2.so").arg(location).arg(name);
#endif
#endif
QPluginLoader loader(libName);
if (!loader.load()) {
hasError = true;
errorString = QDir::toNativeSeparators(libName)
errorString = QDir::toNativeSeparators(filePath)
+ QString::fromLatin1(": ") + loader.errorString();
return false;
}