-
-
Notifications
You must be signed in to change notification settings - Fork 571
Writting_plugin_dependent_on_other_plugin
- Prerequisites
- Brief
- Defining paths for compiler and linker
- Plugin's code
- More advanced dependency definitions
- Conflicts
You must first be familiar with plugin's json file. If you haven't read it yet, do it first, then come back here.
Sometimes one plugin can provide common code base for several plugins, or common interface for other plugins, etc.
We can have for example a MediaPlayer
plugin, which plays sound data extracted from database, but it can have couple of different implementations, depending on the output audio device. It can be a default audio output (speakers), but it can be saved to disk as a file. It also allows for other people to implement their own output plugin and they can use the existing MediaPlayer
plugin to have it extract the data from the database and prepare for writting it to the output. In such case there would be at least 2 plugins dependent on MediaPlayer
plugin - MediaPlayerSpeakers
and MediaPlayerFile
.
SQLiteStudio provides full support for inter-plugin dependencies, which also covers conflicts.
In order to use other plugin's symbols, you must provide proper configuration for includes and libraries, so the compilator and the linker will recognize those symbols.
Given the standard directory structure for the SQLiteStudio and its plugins sources, edit your plugin's .pro
file and add following statements:
INCLUDEPATH += $$PLUGINSDIR/MediaPlayer
DEPENDPATH += $$PLUGINSDIR/MediaPlayer
win32: {
LIBS += -lMediaPlayer
}
For win32 we need to add linking with the dependency plugin, because Windows expects all symbols to be resolved while linking, even those are shared libraries.
INCLUDEPATH += $$PWD/../../MediaPlayer
DEPENDPATH += $$PWD/../../MediaPlayer
or by relative path to the Plugins
dir:
INCLUDEPATH += $$PLUGINSDIR/../MediaPlayer
DEPENDPATH += $$PLUGINSDIR/../MediaPlayer
To declare plugin dependent on other plugin, just define it in plugin's json file, like this:
{
"type": "GeneralPurposePlugin",
"title": "Speakers output",
"description": "Provides support for sending sound to speakers",
"version": 10000,
"author": "An author",
"dependencies": "MediaPlayer"
}
The "MediaPlayer"
here is a name of the main class in the MediaPlayer
plugin, the one that implements Plugin interface. The class name and the plugin name is the same in almost all cases (the exception is only if someone doesn't use GenericPlugin to implement his plugin and he defines Plugin::getName()
method to return different name).
Note, that for "dependencies"
entry you can define more plugin names, using array construction (with [ and ] characters), like this:
"dependencies": ["MediaPlayer", "OtherPlugin"]
Having such a dependency declared at compile time will result in runtime with SQLiteStudio making sure, that MediaPlayer
is loaded before MediaPlayerOutput
. If the MediaPlayer
fails to load (or is not available), then MediaPlayerOutput
will also fail to load.
Also if user unloads MediaPlayer
manually, then SQLiteStudio makes sure that all plugins dependent on MediaPlayer
are unloaded first.
You already can include dependent plugin headers:
#include "mediaplayer.h"
If you declared some base class to be inherited/instanitiated in the local plugin, just do it:
SomeMediaPlayerClass* obj = new SomeMediaPlayerClass();
If you need to access the main instance of the dependent plugin, use PluginManager:
#include "service/pluginmanager.h"
// ...
Plugin* plugin = PLUGINS->getLoadedPlugin("MediaPlayer");
MediaPlayer* mp = dynamic_cast<MediaPlayer*>(plugin);
The dependencies
entry in Json file can be defined in various forms. It can be a single string, pointing a single dependency, but it can also be an array of strings to define dependency from numerous plugins.
It can also be Json object, which defines dependency name, minimum and maximum versions. It can albo be an array of such objects.
Example:
"dependencies": [
{
"name": "SomePlugin1",
"minVersion": 10001
},
{
"name": "SomePlugin2",
"maxVersion": 10300
}
]
The example above defines, that our plugin has dependency from SomePlugin1
and SomePlugin2
, with a condition, that the SomePlugin1
is in version at least 1.0.1 and SomePlugin2
is in version at most 1.3.0.
The minVersion
and maxVersion
can be useful if you know that the dependent plugins changed their API during version releases.
There may be the case, that you want to write a plugin that implements the same features as some other plugin and you don't want those two plugins to be both loaded at the same time.
In that case you can declare your plugin to be conflicting with that other plugin.
If any of two plugins declare a conflict with the other one, SQLiteStudio will prevent from loading them both at the same time. Simple, the first loaded is lucky and the other will fail to load. User can manually unload/load desired plugins, so he/she can decide which one to use. Once the unwonted plugin was unloaded and desired plugin is loaded, the configuration will be saved and the user will always get the right plugin loaded at startup.
This goes almost the same as for dependencies, except the key name is different:
{
"type": "GeneralPurposePlugin",
"title": "Speakers output",
"description": "Provides support for sending sound to speakers",
"version": 10000,
"author": "An author",
"conflicts": "MediaPlayerOtherOutput"
}
The "MediaPlayerOtherOutput"
is the name of the main class (the one that implements Plugin interface) from the conflicting plugin.
Just like in dependencies
, the conflicts
can be either single entry, or array of entries, but unlike in dependencies
, here only simple names are allowed, no complex objects.