To create different variants of the same feature, but avoid code duplications within plugins, you have multiple options:
- Define a needs clause in a contract and reuse another plugin as it is. This should be preferred for filter and validation tasks.
- Have common code together in a helper library (or core library),
see the CMake function
add_libfor creating such a library (in the folder libs). This should be used for rather common functionality that might be useful for many plugins or even applications. - Have configuration for plugins (See elektraPluginGetConfig()
and dynamically switch with
ifaccording to the configuration. This should be preferred when you want to (de)activate some features of a plugin at run-time. - Or use compilation variants to compile the plugin code multiple
times with different
COMPILE_DEFINITIONS(that are Macro definitions). This should be preferred when different macro definitions lead to different plugins. It should especially be used when the resulting plugins have different dependencies: it is possible to have differentLINK_LIBRARIES.
The advantage of compilation variants are:
- No run-time overhead
- Can be used during bootstrapping (when no configuration is available)
- Different compilation variants can be built at once (no recompilation with different CMake flags required)
- Different compilation variants can have different dependencies
- Different compilation variants can be mounted without
#refnames
To use compilation variants, add your plugin in the CMake Cache
Variable PLUGINS multiple times.
Then there can be an arbitrary number of variants.
As naming convention you should have a base name with an additional
variant appended with underscore, e.g.:
myplugin_varianta;myplugin_variantbIn the CMakeLists.txt of your plugin, you have two options.
Option (A): When you can easily enlist every variant you
simply list all plugins one after the other (outside of if (DEPENDENCY_PHASE)):
add_plugin(myplugin_varianta
SOURCES <your sources for varianta here..>
COMPILE_DEFINITIONS VARIANTA ELEKTRA_VARIANT=varianta
LINK_LIBRARIES <libraries for varianta>
)
add_plugin(myplugin_variantb
SOURCES <your sources for variantb here..>
COMPILE_DEFINITIONS VARIANTB ELEKTRA_VARIANT=variantb
LINK_LIBRARIES <libraries for variantb>
)Option (B): If you cannot enlist every possible compilation variant, you can iterate over all PLUGINS and check which names are requested. Then you create a plugin for every name that matches:
foreach (plugin ${PLUGINS})
if (${plugin} MATCHES "myplugin_.*")
# somehow process the variant names and include
# or change sources and compile definitions
# based on that.
add_plugin(${plugin}
SOURCES <your sources here..>
COMPILE_DEFINITIONS <definitions here..>
ELEKTRA_VARIANT=${plugin without prefix}
LINK_LIBRARIES <libraries for variantb>
if (${plugin} MATCHES "ALL")
# handle categories of plugins
add_plugin(myplugin_all1, ...)
add_plugin(myplugin_all2, ...)For the categories such as ALL, however, you need to automatically
append (using add_plugin) a useful set of plugins.
Note that every plugin needs to have
ELEKTRA_VARIANT differently set in COMPILE_DEFINITIONS, otherwise you will
get a linker error that libelektra_<pluginname>_LTX_elektraPluginSymbol has
multiple definitions.
Now every public function of the plugin conflicts with itself. To avoid that, you can use:
- static functions, but they are only visible within one file. This should be preferred, when possible.
- use helper libraries using
add_libto share code between compilation variants (only if code is also potentially useful for other plugins/applications) - Get a unique name for every variant using the macro
ELEKTRA_PLUGIN_FUNCTION(myplugin, open)where myplugin is the name of the plugin and the second argument is how the function should be called. - Including a readme for every variant (with
#ifdeffor different text) using the macro#include ELEKTRA_README(myplugin)
As a summary, you can have many plugins build out of the same source.
Using pluginname_variantnames many plugins will be compiled, each
with other SOURCES or COMPILE_DEFINITIONS and even LINK_LIBRARIES:
If you, e.g. just set
the variants name as macro you can use
#ifdef varianta
#endifwithin the code and can have two plugins: one (called myplugin_varianta)
compiled included the #ifdef the other (base variant called
myplugin) without.
Currently compilation variants are used in the resolver plugin.