2

I'd like to make a GTK plugin for my OCaml application, loaded using Dynlink. How can I get ocamlbuild to include the lablgtk2 library in the generated plugin?

As a test, I have main.ml:

let () =
  try
    Dynlink.loadfile "_build/gtk_plugin.cmxs"
  with Dynlink.Error err ->
    failwith (Dynlink.error_message err)

gtk_plugin.ml:

let () =
  print_endline "GTK plugin loaded!";
  GMain.Main.main ()

_tags:

<main.*>: package(dynlink)
<gtk_plugin.*>: package(lablgtk2)

But I get:

$ ocamlbuild -use-ocamlfind main.native gtk_plugin.cmxs
$ ./main.native 
Fatal error: exception Failure("error loading shared library:
.../_build/gtk_plugin.cmxs: undefined symbol: camlGtkMain")

Note: the main binary must not depend on libgtk (which might not be installed on the target system) - if the plugin fails to load I want to fall back to console mode.

anol
  • 8,264
  • 3
  • 34
  • 78
Thomas Leonard
  • 7,068
  • 2
  • 36
  • 40
  • You may be interested in [this new script](https://sympa.inria.fr/sympa/arc/caml-list/2013-12/msg00010.html). What is your email contact information? – gasche Dec 02 '13 at 15:32
  • Thanks. I've got it working now - here's my myocamlbuild.ml: https://github.com/0install/0install/blob/master/ocaml/myocamlbuild.ml Email is talex5 gmail.com. – Thomas Leonard Dec 02 '13 at 19:45

1 Answers1

3

You need to

  1. add the linkall flag to main, otherwise it will remove parts of the OCaml runtime that will later be needed by dynamic plugins

  2. compile the gtk_plugin.cmxs file with option -lflag lablgtk.cma (which I deduced from seeing in the _log that this option was not passed)

The way ocamlbuild deduces .cmxs dependencies is not optimal right now, and it's hard because different users may want different things (minimal plugins assuming libs are present, or on the contrary portable statically linked stuff). For modules coming from your project you can write a foo.mldylib file to be explicit about what you want excluded, but I don't know whether it's possible to include "all modules of this external library".

Note that it is also possible to distribute lablgtk.cmxs and the relevant .cmi along with your plugin, and load it dynamically first.

mkdir lablgtk
cp `ocamlfind query lablgtk2`/lablgtk.cmxs lablgtk
cp `ocamlfind query lablgtk2`/*.cmi lablgtk
echo "\"lablgtk\": not_hygienic" >> _tags

then in your main.ml

let () =
  try
    Dynlink.loadfile "lablgtk/lablgtk.cmxs";
    Dynlink.loadfile "_build/gtk_plugin.cmxs"
  ...
gasche
  • 31,259
  • 3
  • 78
  • 100
  • Perfect - thanks! I used these lines in myocamlbuild.ml: `After_rules -> flag ["library"; "native"; "link_gtk"] (S [A".../lablgtk.cmxa"]); flag ["library"; "byte"; "link_gtk"] (S [A".../lablgtk.cma"]);` and tagged the plugin "link_gtk" in the _tags file. – Thomas Leonard Nov 30 '13 at 10:44
  • One half of me wants to support parametric `lflag()` and `cflag()` tags, the other is not sure that it makes sense as a tag (what would it mean to attach a `lflag()` to an `.inferred.mli` target?). – gasche Nov 30 '13 at 13:59