forked from qt-creator/qt-creator
Allow optional plugin dependencies.
Adds a 'type' attribute to the 'dependency' tag, with possible values 'required' and 'optional'. Optional dependencies may not be linked against. You'll need to use the new dynamic methods in plugin manager (retrieving objects and calling methods by name) if you want to access functionality of optional dependencies.
This commit is contained in:
@@ -152,6 +152,11 @@
|
|||||||
\o version
|
\o version
|
||||||
\o The version to which the plugin must be compatible to
|
\o The version to which the plugin must be compatible to
|
||||||
fill the dependency, in the form \c {"x.y.z_n"}.
|
fill the dependency, in the form \c {"x.y.z_n"}.
|
||||||
|
Can be empty if the version does not matter.
|
||||||
|
\row
|
||||||
|
\o type
|
||||||
|
\o Value 'required' or 'optional'. Defines if the dependency is
|
||||||
|
a hard requirement or optional. Defaults to 'required'.
|
||||||
\endtable
|
\endtable
|
||||||
|
|
||||||
\section2 Example \c plugin.xml
|
\section2 Example \c plugin.xml
|
||||||
|
|||||||
@@ -95,6 +95,8 @@ void PluginDetailsView::update(PluginSpec *spec)
|
|||||||
QString depString = dep.name;
|
QString depString = dep.name;
|
||||||
depString += QLatin1String(" (");
|
depString += QLatin1String(" (");
|
||||||
depString += dep.version;
|
depString += dep.version;
|
||||||
|
if (dep.type == PluginDependency::Optional)
|
||||||
|
depString += QLatin1String(", optional");
|
||||||
depString += QLatin1Char(')');
|
depString += QLatin1Char(')');
|
||||||
depStrings.append(depString);
|
depStrings.append(depString);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -996,7 +996,13 @@ void PluginManagerPrivate::loadPlugin(PluginSpec *spec, PluginSpec::State destSt
|
|||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
foreach (const PluginSpec *depSpec, spec->dependencySpecs()) {
|
// check if dependencies have loaded without error
|
||||||
|
QHashIterator<PluginDependency, PluginSpec *> it(spec->dependencySpecs());
|
||||||
|
while (it.hasNext()) {
|
||||||
|
it.next();
|
||||||
|
if (it.key().type == PluginDependency::Optional)
|
||||||
|
continue;
|
||||||
|
PluginSpec *depSpec = it.value();
|
||||||
if (depSpec->state() != destState) {
|
if (depSpec->state() != destState) {
|
||||||
spec->d->hasError = true;
|
spec->d->hasError = true;
|
||||||
spec->d->errorString =
|
spec->d->errorString =
|
||||||
|
|||||||
@@ -87,6 +87,23 @@
|
|||||||
Version string that a plugin must match to fill this dependency.
|
Version string that a plugin must match to fill this dependency.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\variable ExtensionSystem::PluginDependency::type
|
||||||
|
Defines whether the dependency is required or optional.
|
||||||
|
\sa ExtensionSystem::PluginDependency::Type
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\enum ExtensionSystem::PluginDependency::Type
|
||||||
|
Whether the dependency is required or optional.
|
||||||
|
\value Required
|
||||||
|
Dependency needs to be there.
|
||||||
|
\value Optional
|
||||||
|
Dependency is not necessarily needed. You need to make sure that
|
||||||
|
the plugin is able to load without this dependency installed, so
|
||||||
|
for example you may not link to the dependency's library.
|
||||||
|
*/
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
\class ExtensionSystem::PluginSpec
|
\class ExtensionSystem::PluginSpec
|
||||||
\brief Contains the information of the plugins xml description file and
|
\brief Contains the information of the plugins xml description file and
|
||||||
@@ -127,16 +144,26 @@
|
|||||||
\value Deleted
|
\value Deleted
|
||||||
The plugin instance has been deleted.
|
The plugin instance has been deleted.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
using namespace ExtensionSystem;
|
using namespace ExtensionSystem;
|
||||||
using namespace ExtensionSystem::Internal;
|
using namespace ExtensionSystem::Internal;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\fn uint qHash(const ExtensionSystem::PluginDependency &value)
|
||||||
|
\internal
|
||||||
|
*/
|
||||||
|
uint ExtensionSystem::qHash(const ExtensionSystem::PluginDependency &value)
|
||||||
|
{
|
||||||
|
return qHash(value.name);
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
\fn bool PluginDependency::operator==(const PluginDependency &other)
|
\fn bool PluginDependency::operator==(const PluginDependency &other)
|
||||||
\internal
|
\internal
|
||||||
*/
|
*/
|
||||||
bool PluginDependency::operator==(const PluginDependency &other)
|
bool PluginDependency::operator==(const PluginDependency &other) const
|
||||||
{
|
{
|
||||||
return name == other.name && version == other.version;
|
return name == other.name && version == other.version && type == other.type;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -395,22 +422,11 @@ IPlugin *PluginSpec::plugin() const
|
|||||||
|
|
||||||
\sa PluginSpec::dependencies()
|
\sa PluginSpec::dependencies()
|
||||||
*/
|
*/
|
||||||
QList<PluginSpec *> PluginSpec::dependencySpecs() const
|
QHash<PluginDependency, PluginSpec *> PluginSpec::dependencySpecs() const
|
||||||
{
|
{
|
||||||
return d->dependencySpecs;
|
return d->dependencySpecs;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
\fn QList<PluginSpec *> PluginSpec::providesForSpecs() const
|
|
||||||
Returns the list of plugins that depend on this one.
|
|
||||||
|
|
||||||
\sa PluginSpec::dependencySpecs()
|
|
||||||
*/
|
|
||||||
QList<PluginSpec *> PluginSpec::providesForSpecs() const
|
|
||||||
{
|
|
||||||
return d->providesSpecs;
|
|
||||||
}
|
|
||||||
|
|
||||||
//==========PluginSpecPrivate==================
|
//==========PluginSpecPrivate==================
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
@@ -429,6 +445,9 @@ namespace {
|
|||||||
const char * const DEPENDENCY = "dependency";
|
const char * const DEPENDENCY = "dependency";
|
||||||
const char * const DEPENDENCY_NAME = "name";
|
const char * const DEPENDENCY_NAME = "name";
|
||||||
const char * const DEPENDENCY_VERSION = "version";
|
const char * const DEPENDENCY_VERSION = "version";
|
||||||
|
const char * const DEPENDENCY_TYPE = "type";
|
||||||
|
const char * const DEPENDENCY_TYPE_SOFT = "optional";
|
||||||
|
const char * const DEPENDENCY_TYPE_HARD = "required";
|
||||||
const char * const ARGUMENTLIST = "argumentList";
|
const char * const ARGUMENTLIST = "argumentList";
|
||||||
const char * const ARGUMENT = "argument";
|
const char * const ARGUMENT = "argument";
|
||||||
const char * const ARGUMENT_NAME = "name";
|
const char * const ARGUMENT_NAME = "name";
|
||||||
@@ -726,6 +745,18 @@ void PluginSpecPrivate::readDependencyEntry(QXmlStreamReader &reader)
|
|||||||
reader.raiseError(msgInvalidFormat(DEPENDENCY_VERSION));
|
reader.raiseError(msgInvalidFormat(DEPENDENCY_VERSION));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
dep.type = PluginDependency::Required;
|
||||||
|
if (reader.attributes().hasAttribute(DEPENDENCY_TYPE)) {
|
||||||
|
QString typeValue = reader.attributes().value(DEPENDENCY_TYPE).toString();
|
||||||
|
if (typeValue == QLatin1String(DEPENDENCY_TYPE_HARD)) {
|
||||||
|
dep.type = PluginDependency::Required;
|
||||||
|
} else if (typeValue == QLatin1String(DEPENDENCY_TYPE_SOFT)) {
|
||||||
|
dep.type = PluginDependency::Optional;
|
||||||
|
} else {
|
||||||
|
reader.raiseError(msgInvalidFormat(DEPENDENCY_TYPE));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
dependencies.append(dep);
|
dependencies.append(dep);
|
||||||
reader.readNext();
|
reader.readNext();
|
||||||
if (reader.tokenType() != QXmlStreamReader::EndElement)
|
if (reader.tokenType() != QXmlStreamReader::EndElement)
|
||||||
@@ -801,26 +832,27 @@ bool PluginSpecPrivate::resolveDependencies(const QList<PluginSpec *> &specs)
|
|||||||
hasError = true;
|
hasError = true;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
QList<PluginSpec *> resolvedDependencies;
|
QHash<PluginDependency, PluginSpec *> resolvedDependencies;
|
||||||
foreach (const PluginDependency &dependency, dependencies) {
|
foreach (const PluginDependency &dependency, dependencies) {
|
||||||
PluginSpec *found = 0;
|
PluginSpec *found = 0;
|
||||||
|
|
||||||
foreach (PluginSpec *spec, specs) {
|
foreach (PluginSpec *spec, specs) {
|
||||||
if (spec->provides(dependency.name, dependency.version)) {
|
if (spec->provides(dependency.name, dependency.version)) {
|
||||||
found = spec;
|
found = spec;
|
||||||
spec->d->addProvidesForPlugin(q);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!found) {
|
if (!found) {
|
||||||
hasError = true;
|
if (dependency.type == PluginDependency::Required) {
|
||||||
if (!errorString.isEmpty())
|
hasError = true;
|
||||||
errorString.append(QLatin1Char('\n'));
|
if (!errorString.isEmpty())
|
||||||
errorString.append(QCoreApplication::translate("PluginSpec", "Could not resolve dependency '%1(%2)'")
|
errorString.append(QLatin1Char('\n'));
|
||||||
.arg(dependency.name).arg(dependency.version));
|
errorString.append(QCoreApplication::translate("PluginSpec", "Could not resolve dependency '%1(%2)'")
|
||||||
|
.arg(dependency.name).arg(dependency.version));
|
||||||
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
resolvedDependencies.append(found);
|
resolvedDependencies.insert(dependency, found);
|
||||||
}
|
}
|
||||||
if (hasError)
|
if (hasError)
|
||||||
return false;
|
return false;
|
||||||
@@ -840,7 +872,12 @@ void PluginSpecPrivate::disableIndirectlyIfDependencyDisabled()
|
|||||||
if (disabledIndirectly)
|
if (disabledIndirectly)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
foreach (PluginSpec *dependencySpec, dependencySpecs) {
|
QHashIterator<PluginDependency, PluginSpec *> it(dependencySpecs);
|
||||||
|
while (it.hasNext()) {
|
||||||
|
it.next();
|
||||||
|
if (it.key().type == PluginDependency::Optional)
|
||||||
|
continue;
|
||||||
|
PluginSpec *dependencySpec = it.value();
|
||||||
if (dependencySpec->isDisabledIndirectly() || !dependencySpec->isEnabled()) {
|
if (dependencySpec->isDisabledIndirectly() || !dependencySpec->isEnabled()) {
|
||||||
disabledIndirectly = true;
|
disabledIndirectly = true;
|
||||||
break;
|
break;
|
||||||
@@ -984,17 +1021,3 @@ void PluginSpecPrivate::kill()
|
|||||||
plugin = 0;
|
plugin = 0;
|
||||||
state = PluginSpec::Deleted;
|
state = PluginSpec::Deleted;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
\fn void PluginSpecPrivate::addProvidesForPlugin(PluginSpec *dependent)
|
|
||||||
\internal
|
|
||||||
*/
|
|
||||||
void PluginSpecPrivate::addProvidesForPlugin(PluginSpec *dependent)
|
|
||||||
{
|
|
||||||
providesSpecs.append(dependent);
|
|
||||||
}
|
|
||||||
|
|
||||||
void PluginSpecPrivate::removeProvidesForPlugin(PluginSpec *dependent)
|
|
||||||
{
|
|
||||||
providesSpecs.removeOne(dependent);
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -38,6 +38,7 @@
|
|||||||
|
|
||||||
#include <QtCore/QString>
|
#include <QtCore/QString>
|
||||||
#include <QtCore/QList>
|
#include <QtCore/QList>
|
||||||
|
#include <QtCore/QHash>
|
||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
QT_BEGIN_NAMESPACE
|
||||||
class QStringList;
|
class QStringList;
|
||||||
@@ -54,11 +55,19 @@ class IPlugin;
|
|||||||
|
|
||||||
struct EXTENSIONSYSTEM_EXPORT PluginDependency
|
struct EXTENSIONSYSTEM_EXPORT PluginDependency
|
||||||
{
|
{
|
||||||
|
enum Type {
|
||||||
|
Required,
|
||||||
|
Optional
|
||||||
|
};
|
||||||
|
|
||||||
QString name;
|
QString name;
|
||||||
QString version;
|
QString version;
|
||||||
bool operator==(const PluginDependency &other);
|
Type type;
|
||||||
|
bool operator==(const PluginDependency &other) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
uint qHash(const ExtensionSystem::PluginDependency &value);
|
||||||
|
|
||||||
struct EXTENSIONSYSTEM_EXPORT PluginArgumentDescription
|
struct EXTENSIONSYSTEM_EXPORT PluginArgumentDescription
|
||||||
{
|
{
|
||||||
QString name;
|
QString name;
|
||||||
@@ -105,10 +114,7 @@ public:
|
|||||||
bool provides(const QString &pluginName, const QString &version) const;
|
bool provides(const QString &pluginName, const QString &version) const;
|
||||||
|
|
||||||
// dependency specs, valid after 'Resolved' state is reached
|
// dependency specs, valid after 'Resolved' state is reached
|
||||||
QList<PluginSpec *> dependencySpecs() const;
|
QHash<PluginDependency, PluginSpec *> dependencySpecs() const;
|
||||||
|
|
||||||
// list of plugins that depend on this - e.g. this plugins provides for them
|
|
||||||
QList<PluginSpec *> providesForSpecs() const;
|
|
||||||
|
|
||||||
// linked plugin instance, valid after 'Loaded' state is reached
|
// linked plugin instance, valid after 'Loaded' state is reached
|
||||||
IPlugin *plugin() const;
|
IPlugin *plugin() const;
|
||||||
|
|||||||
@@ -82,8 +82,7 @@ public:
|
|||||||
QString filePath;
|
QString filePath;
|
||||||
QStringList arguments;
|
QStringList arguments;
|
||||||
|
|
||||||
QList<PluginSpec *> providesSpecs;
|
QHash<PluginDependency, PluginSpec *> dependencySpecs;
|
||||||
QList<PluginSpec *> dependencySpecs;
|
|
||||||
PluginSpec::PluginArgumentDescriptions argumentDescriptions;
|
PluginSpec::PluginArgumentDescriptions argumentDescriptions;
|
||||||
IPlugin *plugin;
|
IPlugin *plugin;
|
||||||
|
|
||||||
@@ -94,9 +93,6 @@ public:
|
|||||||
static bool isValidVersion(const QString &version);
|
static bool isValidVersion(const QString &version);
|
||||||
static int versionCompare(const QString &version1, const QString &version2);
|
static int versionCompare(const QString &version1, const QString &version2);
|
||||||
|
|
||||||
// add/remove from providesSpecs
|
|
||||||
void addProvidesForPlugin(PluginSpec *dependent);
|
|
||||||
void removeProvidesForPlugin(PluginSpec *dependent);
|
|
||||||
void disableIndirectlyIfDependencyDisabled();
|
void disableIndirectlyIfDependencyDisabled();
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -351,7 +351,12 @@ void PluginView::updatePluginDependencies()
|
|||||||
if (m_whitelist.contains(spec->name()))
|
if (m_whitelist.contains(spec->name()))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
foreach(const PluginSpec *depSpec, spec->dependencySpecs()) {
|
QHashIterator<PluginDependency, PluginSpec *> it(spec->dependencySpecs());
|
||||||
|
while (it.hasNext()) {
|
||||||
|
it.next();
|
||||||
|
if (it.key().type == PluginDependency::Optional)
|
||||||
|
continue;
|
||||||
|
PluginSpec *depSpec = it.value();
|
||||||
if (!depSpec->isEnabled() || depSpec->isDisabledIndirectly()) {
|
if (!depSpec->isEnabled() || depSpec->isDisabledIndirectly()) {
|
||||||
disableIndirectly = true;
|
disableIndirectly = true;
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ Alternatively, this plugin may be used under the terms of the GNU Lesser General
|
|||||||
<dependency name=\"Core\" version=\"$$QTCREATOR_VERSION\"/>
|
<dependency name=\"Core\" version=\"$$QTCREATOR_VERSION\"/>
|
||||||
<dependency name=\"Find\" version=\"$$QTCREATOR_VERSION\"/>
|
<dependency name=\"Find\" version=\"$$QTCREATOR_VERSION\"/>
|
||||||
<!-- Debugger plugin adds items to the editor's context menu -->
|
<!-- Debugger plugin adds items to the editor's context menu -->
|
||||||
<dependency name=\"CppEditor\" version=\"$$QTCREATOR_VERSION\" type=\"soft\"/>
|
<dependency name=\"CppEditor\" version=\"$$QTCREATOR_VERSION\" type=\"optional\"/>
|
||||||
</dependencyList>
|
</dependencyList>
|
||||||
<argumentList>
|
<argumentList>
|
||||||
<argument name=\"-disable-cdb\">Disable Cdb debugger engine</argument>
|
<argument name=\"-disable-cdb\">Disable Cdb debugger engine</argument>
|
||||||
|
|||||||
Reference in New Issue
Block a user