Using ocamlfind with ocamlbuild
There are many ways to make ocamlbuild use ocamlfind. The first section gives the simplest one, starting from ocamlfind 3.12. The second section gives a generic plugin which replaces the "ocamlc", "ocamldep"... commands with ocamlfind equivalents. The third shows how to call "ocamlfind query" from a plugin.
Simple tag (from 3.12)
Starting with version 3.12, the support of ocamlfind is native in
ocamlbuild. To activate it, you need to pass the command line option
-use-ocamlfind. Then you can add packages in the
_tags file with the
package(...) tag. Syntax extensions are enabled by requiring the
related package and using the
syntax(camlp4o) tag. Finally ocamlfind
predicates can be activated with the
# Suppose that prog.ml is the main module and # that your project use num and str libraries. $ cat _tags <*.ml> or "prog.byte": package(num), package(str) $ ocamlbuild -use-ocamlfind prog.byte -- Hello World!
To include sub-packages, use the doted notation
<*.ml>: package(lwt.unix), package(lwt.syntax), syntax(camlp4o) "prog.byte": package(lwt.unix)
Here is presented a rather generic plugin to access packages using ocamlfind.
The following code should be in a file named
myocamlbuild.ml, at the
root of your project.
open Ocamlbuild_plugin (* open Command -- no longer needed for OCaml >= 3.10.2 *)` (* these functions are not really officially exported *) let run_and_read = Ocamlbuild_pack.My_unix.run_and_read let blank_sep_strings = Ocamlbuild_pack.Lexers.blank_sep_strings let split s ch = let x = ref  in let rec go s = let pos = String.index s ch in x := (String.before s pos)::!x; go (String.after s (pos + 1)) in try go s with Not_found -> !x let split_nl s = split s '\n' let before_space s = try String.before s (String.index s ' ') with Not_found -> s` (* this lists all supported packages *) let find_packages () = List.map before_space (split_nl & run_and_read "ocamlfind list") (* this is supposed to list available syntaxes, but I don't know how to do it. *) let find_syntaxes () = ["camlp4o"; "camlp4r"] (* ocamlfind command *) let ocamlfind x = S[A"ocamlfind"; x] let _ = dispatch begin function | Before_options -> (* by using Before_options one let command line options have an higher priority on the contrary using After_options will guarantee to have the higher priority *) (* override default commands by ocamlfind ones *) Options.ocamlc := ocamlfind & A"ocamlc"; Options.ocamlopt := ocamlfind & A"ocamlopt"; Options.ocamldep := ocamlfind & A"ocamldep"; Options.ocamldoc := ocamlfind & A"ocamldoc"; Options.ocamlmktop := ocamlfind & A"ocamlmktop" | After_rules -> (* When one link an OCaml library/binary/package, one should use -linkpkg *) flag ["ocaml"; "link"; "program"] & A"-linkpkg"; (* For each ocamlfind package one inject the -package option when compiling, computing dependencies, generating documentation and linking. *) List.iter begin fun pkg -> flag ["ocaml"; "compile"; "pkg_"^pkg] & S[A"-package"; A pkg]; flag ["ocaml"; "ocamldep"; "pkg_"^pkg] & S[A"-package"; A pkg]; flag ["ocaml"; "doc"; "pkg_"^pkg] & S[A"-package"; A pkg]; flag ["ocaml"; "link"; "pkg_"^pkg] & S[A"-package"; A pkg]; flag ["ocaml"; "infer_interface"; "pkg_"^pkg] & S[A"-package"; A pkg]; end (find_packages ()); (* Like -package but for extensions syntax. Morover -syntax is useless when linking. *) List.iter begin fun syntax -> flag ["ocaml"; "compile"; "syntax_" ^ syntax] & S[A"-syntax"; A syntax]; flag ["ocaml"; "ocamldep"; "syntax_" ^ syntax] & S[A"-syntax"; A syntax]; flag ["ocaml"; "doc"; "syntax_" ^ syntax] & S[A"-syntax"; A syntax]; flag ["ocaml"; "infer_interface"; "syntax_" ^ syntax] & S[A"-syntax"; A syntax]; end (find_syntaxes ()); (* The default "thread" tag is not compatible with ocamlfind. Indeed, the default rules add the "threads.cma" or "threads.cmxa" options when using this tag. When using the "-linkpkg" option with ocamlfind, this module will then be added twice on the command line. To solve this, one approach is to add the "-thread" option when using the "threads" package using the previous plugin. *) flag ["ocaml"; "pkg_threads"; "compile"] (S[A "-thread"]); flag ["ocaml"; "pkg_threads"; "link"] (S[A "-thread"]); flag ["ocaml"; "pkg_threads"; "infer_interface"] (S[A "-thread"]) | _ -> () end
Packages to be used are supplied using the tagging system.
This plugin declares a tag
pkg_<thepackagename> per package (for
Tag your source files with them if you want the package to be used a compile-time, and your .byte or .native files if you want the package to be used at link-time (usually you'll want both).
# Suppose that prog.ml is the main module and # that your project use nums and str libraries. $ cat _tags <*.ml> or "prog.byte": pkg_nums, pkg_str $ ocamlbuild prog.byte -- Hello World!
This plugin also provide a little support for the
-syntax flag of
ocamlfind. However the list of syntaxes are hard listed in the
myocamlbuild.ml. Generally if you use packages that provide a syntax
camlp4o will suffice to trigger the
$ wget http://martin.jambon.free.fr/yahoo.ml $ cat _tags <yahoo.\*\>`: pkg_json-static, pkg_netclient, syntax_camlp4o $ ocamlbuild yahoo.byte -- ocaml ...
- Tag the files that need the "foo" package with
pkg_foo(sources and binaries)
- Don't use the "thread" tag, use
Using ocamlfind only for querying package installation directories
Maybe you do not want to invoke ocamllfind as a wrapper around the
toolchain commands (ocamlc, etc.). In that case, you can still invoke
ocamlfind to locate the directories in which registered packages are
installed. The "ocamlfind query pkgname" command yields the installation
directory of the command. Here is the ocamlbuild equivalent of
$(ocamlfind query pkgname):
let ocamlfind_query pkg = let cmd = Printf.sprintf "ocamlfind query %s" (Filename.quote pkg) in Ocamlbuild_pack.My_unix.run_and_open cmd (fun ic -> Log.dprintf 5 "Getting Ocaml directory from command %s" cmd; input_line ic )
You can use it as follows in the After_rules section:
ocaml_lib ~extern:true ~dir:(ocamlfind_query "sexplib") "sexplib";