I am compiling an Ocaml project (named ‘cody’) which needs to use the ‘batteries’ external module (for UTF8 handling). By the way, I am not using Dune for this project.
Do you reference BatUTF8.look somewhere in the code of the plugin?
This error means that some code that is required by your plugin has not been linked either to the plugin itself or to the main program loading the plugin. Can you provide the full command-line used to build your main program? Using the -linkall flag (either when linking the plugin or the main program or both) may also be relevant, but may increase the size of your compiled artifacts.
Note that normally for dependencies of plugins (like batteries) you have a choice between linking them to the plugin itself or to the main program. The latter is typically better if the dependencies are shared between plugins; the former is preferrable if the dependencies are only used by the one plugin. At the moment you are linking batteries to the plugin, I believe (by using the -linkpkg flag in the command-line for the .cmxs).
EDIT: also, the output of ocamlobjinfo cody.cmxs may be useful to know what has been linked into the plugin.
I suspect that you are in the following situation:
The main program (your python program using ocaml-in-python) contains a number of libraries already loaded: the standard library, some libraries specific to ocaml-in-python, and (I assume) the unix library.
Your plugin is made of your own code (cody), and depends on batteries, which depends on unix.
So if you use -linkpkg, ocamlfind will resolve all libraries needed (except for the standard library) and add them to the linking command. This results in a .cmxs file containing cody, batteries, and unix. This means that you cannot load this file from a program already using unix, as it would lead to duplicate mofules.
If you don’t use -linkpkg, you only get cody, not batteries, and your .cmxs file cannot be loaded on its own.
I see two simple modifications that should enable you to progress:
You could manually add batteries.cmxs to your linking command (without -linkpkg). This should produce a cody.cmxs file containing both cody and batteries, but not unix, so you should be able to load it normally
You could call ocaml.loadfile("batteries.cmxs", "Batteries") in your python code before loading cody.cmxs (you may need to specify a path for batteries.cmxs, maybe something like "batteries/batteries.cmxs"). I’m not sure it will actually work (it depends on how ocaml.loadfile is implemented, and I’m not familiar with ocaml-in-python).
Just to complement @vlaviron’s explanation, I think it should be batteries.cmxa, not batteries.cmxs. If you are using ocamlfind you should be able to just write batteries.cmxa in the command-line. The -I flags needed to find that file will have been added by ocamlfind in response to the -package batteries argument.
If I also do the manual load, the error changes to the … undefined symbol: caml_mutex_lock\\\")\")")
The real problem here is my appalling ignorance of this ecosystem - the error messages mean nothing to me, and I can’t even be sure if this is an ocaml-in-python issue (though I assume it is).
I suppose that as a last resort, I could simply take the BatUTF8.ml file from the package and use it in my code. It’s not the ideal way, but I guess it may get the job done.
Thanks for the help, anyway. I am open to further suggestions …
EDIT: At least I can see where the problem Dynlink error of loading modules more than once is coming from - it was a patch applied several years ago to solve some other issues. They also talk about how it might affect plugins:
However we believe these problems can be solved by such systems using their own caching layer that can catch the new error value returned (Dynlink.Error(Module_already_loaded ...)).
Thanks. Yes, I looked at that, at it didn’t seem to do what I wanted.
I am interfacing between a PythonQT GUI front-end an an OCaml back-end.
Python treats UTF8 strings very effectively, that is, getting the character at position 4 will be the actual Unicode character, whereas in OCaml the same function gives back the 4th byte.
So I had to write small Ocaml routines to expose functions that behave like Python would. OCaml really shines in this department, and the implementation is trivial - 15 lines of code.