Plugins

(Since findlib-1.6.)

Plugins are packages that can be loaded at runtime. Plugins are intended as a mechanism for loading add-on code into executables. Technically, there is some overlap with the concept of a "shared library". Note, however, that there is no universal support for plugins, as it is unimplemented on some platforms, and only poorly on some others (including x86 in the 32 bit case). Also, there must always be an explicit "load" statement in the loading executable. You cannot link an executable directly against plugins.

Preparing a plugin

For bytecode there is no problem to load cma files at runtime. For native-code, though, you need to convert cmxa files into cmxs files first:

ocamlopt -shared -linkall -o m.cmxs m.cmxa

As mentioned, be prepared that this command fails on platforms where plugins are unavailable.

The cmxs files can then be referenced from META. We don't use the "archive" variable in this case but "plugin":

plugin(byte) = "m.cma"
plugin(native) = "m.cmxs"

Before findlib-1.6, there was some half-official convention using the "plugin" predicate. This is still supported, but deprecated:

archive(byte) = "m.cma"
archive(native,plugin) = "m.cmxs"

Load-time dependencies

Plugins can be, of course, be dependent on other plugins. You run into a problem, though, when you make a plugin dependent on a package that doesn't qualify as plugin (i.e. lacks the "plugin" definition). In this case, the loader simply skips the dependency, and you cannot load the plugin.

Because of this dependency issue, it is recommended to add the "plugin" variable to all packages that are installed on a system, because this does not only allow it to load all packages at runtime, but also to use these packages as dependency of the actual plugin code.

That said, you just need to add a "requires" directive, e.g.

requires = "pkg1"
plugin(byte) = "m.cma"
plugin(native) = "m.cmxs"

How to load a plugin

Linking an executable that can load a plugin: An executable must link the package "findlib.dynload". This does not only add the loader, but also special initialization code to the executable:

ocamlfind ocamlopt -o program -package findlib.dynload -linkpkg ...

In particular, this records the packages that are already included into the executable (in-core packages). If a plugin is now dependent on such a package, this is recognized, and the package is not loaded (which would not work anyway).

Now, you can load a plugin "foo" with:

Fl_dynload.load_packages ["foo"]

This loads the cma or cmxs files, and runs the initialization code of all top-level definitions of the included modules.

Of course, you can also call load_packages from a library if "findlib.dynload" is a required package of the library package.

Fat plugins

Sometimes it is handy to create plugins that already include all the required packages. The plugin acts more like a dynamically loadable executable that already includes whatever it needs. You can create such a "fat" plugin with:

ocamlfind ocamlopt -shared -linkpkg -linkall -o m.cmxs -package p1,p2,p3 m.cmxa
In this case, the "requires" line in the META file should remain empty.

Note, however, that you cannot handle packages this way that are already linked into the main program, because it is invalid to load a module that is already part of the main executable. Let's assume that the executable links in q1, q2, and q3. Then, you need to exclude these packages from the plugin. The -dont-link option comes handy in this case:

ocamlfind ocamlopt -shared -linkpkg -linkall -o m.cmxs -package p1,p2,p3 -dont-link q1,q2,q3 m.cmxa
This excludes q1,q2,q3 even if these packages occur as dependencies of p1,p2,p3. In this case, though, you need to put q1,q2,q3 into the "requires" field of META.