ocamlocamlfind

Are ocamlfind package predicates implemented?


I have a package named sundialsml, in which I want to load a slightly different .cm(x)a file depending on whether a subpackage named no_sens is loaded. Promisingly enough, the findlib 1.6.2 reference manual describes a "package predicate" feature:

[...] there are package predicates for every package that is finally selected. [They] have the form "pkg_" plus the name of the package (fully qualified).

So I wrote up this META file, where archive branches on the package predicate for the subpackage:

version = "2.6.2"
description = "OCaml interface to Sundials"
requires = "bigarray"
archive(byte) = "sundials.cma"
archive(byte,pkg_sundialsml.no_sens) = "sundials_no_sens.cma"
archive(native) = "sundials.cmxa"
archive(native,pkg_sundialsml.no_sens) = "sundials_no_sens.cmxa"
package "no_sens" (
  version = "2.6.2"
  description = "Sundials/ML without sensitivity analysis (CVODE, IDA, KINSOL)"
  requires = "sundialsml"
)

But findlib loads sundials.cma regardless of whether the subpackage no_sens is loaded, e.g.:

# #use "topfind";;
- : unit = ()
Findlib has been successfully loaded. Additional directives:
  #require "package";;      to load a package
  #list;;                   to list the available packages
  #camlp4o;;                to load camlp4 (standard syntax)
  #camlp4r;;                to load camlp4 (revised syntax)
  #predicates "p,q,...";;   to set these predicates
  Topfind.reset();;         to force that packages will be reloaded
  #thread;;                 to enable threads

- : unit = ()
# #require "sundialsml.no_sens";;
/home/jun/.opam/4.01.0/lib/ocaml/unix.cma: loaded
/home/jun/.opam/4.01.0/lib/ocaml/bigarray.cma: loaded
/home/jun/.opam/4.01.0/lib/sundialsml: added to search path
/home/jun/.opam/4.01.0/lib/sundialsml/sundials.cma: loaded

Same happens if I try branching on a top-level package, like ao. In fact, far as I can tell, predicates of the form pkg_foo are never defined (unless we say #predicates "pkg_foo";; of course).

Am I using package predicates incorrectly? Or are they not really implemented? If so, is there any other way to select different archives based on the presence/absence of subpackages?

Please note the point here is to compute the archive from the set of subpackages the user selected. So "why don't you use #predicates" is not the solution I'm looking for.


Solution

  • It looks like that the pkg_ predicates are not implemented for the #require directive at all. Of course, I may be wrong, since I inferred this by just greping the code and experimenting. In fact, it is only implemented in the frontend, so it is also not available if someone is using the library interface (so it will not work out of box for ocamlbuild). Also, the pkg_ predicate is set only for selected packages, not for installed one. Where selected means, that the package is in the set of dependencies.

    Here is an example. We define package ttt with the following META:

    archive(byte,pkg_ttt.foo) = "foo.cma"
    archive(byte,pkg_ttt.bar) = "bar.cma"
    
    package "foo" (
      requires = "ttt"
    )
    
    package "bar" (
      requires = "ttt"
    )
    

    Now we can verify, that it works:

    $ ocamlfind c -only-show -linkpkg -package "ttt.bar" main.ml 
    ocamlc.opt -I opam/lib/ttt opam/lib/ttt/bar.cma main.ml
    

    Note: I've used opam instead of a real path to my ocaml installation to shorten the output for readability.

    $ ocamlfind c -only-show -linkpkg -package "ttt.foo" main.ml 
    ocamlc.opt -I opam/lib/ttt opam/lib/ttt/foo.cma main.ml
    

    So, everything works, when we use the frontend. But if we will try from the toplevel:

    # #require "ttt.foo";;
    opam/lib/ttt: added to search path
    

    Then nothing is loaded at all.

    We can also try to use ocamlbuild:

    $ ocamlbuild -classic-display -package ttt.foo main.byte
    opam/bin/ocamldep.opt -modules main.ml > main.ml.depends
    opam/bin/ocamlc.opt -c -I opam/lib/ttt -o main.cmo main.ml
    opam/bin/ocamlc.opt -I opam/lib/ttt main.cmo -o main.byte
    

    So nothing is linked, it doesn't work. But if you will use -use-ocamlfind option, it will work, since this option prescribes ocamlbuild to use ocamlfind fronted.

    $ ocamlbuild -use-ocamlfind -classic-display -package ttt.foo main.byte
    ocamlfind ocamldep -package ttt.foo -modules main.ml > main.ml.depends
    ocamlfind ocamlc -c -package ttt.foo -o main.cmo main.ml
    ocamlfind ocamlc -linkpkg -package ttt.foo main.cmo -o main.byte
    

    So, to conclude, the idea was nice, and can work theoretically, but it is better not to use it, since the implementation is not complete.