A quick and dirty blog post about the -documentation option of ocamlbuild, how to use and understand its output.

I’ve been overloaded since returning from POPL, and all the nice and interesting blog posts I planned to polish and publish are still in the drawers. In the meantime, I’m recycling emails that may be of interest to some of our beloved readers.

This post comes from a conversation with Cedric Cellier, which was wondering how to tell ocamlbuild to use the -custom option to (bytecode) compile his OCaml program.

The ocamlbuild -documentation option

The command ocamlbuild ... -documentation explains the set of rules and tags understood by this particular ocamlbuild invocation (which can depend on the plugin, other options passed to ocamlbuild, etc..). It returns a very long output, that contains information about both all rules known by ocamlbuild (which tells him how to dynamically look for the dependencies of a given target) and all tags (which add flags to the compilation command for a given target). Some representative examples on my system:

...

rule
  "ocaml: cmx* & o* -> native"
  ~deps:[ %.cmx; %.o ]
  ~prods:[ %.native ]
  <fun>

rule
  "ocaml: mllib & d.cmo* -> d.cma"
  ~deps:[ %.mllib ]
  ~prods:[ %.d.cma ]
  <fun>

...

flag {. byte, debug, link, ocaml, program .} "-g"

flag {. byte, compile, debug, ocaml .} "-g"

flag {. link, native, ocaml, use_camlp4_bin .} "+camlp4/Camlp4Bin.cmx"

flag {. byte, link, ocaml, use_camlp4_bin .} "+camlp4/Camlp4Bin.cmo"

flag {. compile, ocaml, use_camlp4_full .}
  "-I +camlp4/Camlp4Parsers -I +camlp4/Camlp4Printers -I +camlp4/Camlp4Filters"

While it won’t tell you everything (you don’t know which <fun> is hiding under the .d.cma rule above, so you’ll need to look at the manual or try it to what this extension means), grepping this output is a good way to find about ocamlbuild tags you didn’t know about, and look for specific features. So for our -custom question:

$ ocamlbuild -documentation | grep custom
flag {. byte, custom, library, link, ocaml .} "-custom"
flag {. byte, custom, link, ocaml, program .} "-custom"

This tells us that -custom will be added to the compilation command when either one of this list of flags is present for the target being compiled – this is similar to the predicate system of ocamlfind.

Knowing which flags are present: that’s what _build/_log is for

In this simple case it’s enough to just try to add the custom tag to your ocamlbuild invocation (either in the _tags or on the command line directly) and check that it works. But in general you may want to know about precisely which of those flags are already present under your current compilation settings, and which should be added. For that, the log is your friend.

$ cd /tmp
$ mkdir ocamlbuild-test; cd ocamlbuild-test
$ touch test.ml
$ ocamlbuild test.byte
Finished, 3 targets (0 cached) in 00:00:00.
$ cat _build/_log 
### Starting build.
# Target: test.ml.depends, tags: { extension:ml, file:test.ml, ocaml, ocamldep, quiet }
.../bin/ocamldep.opt -modules test.ml > test.ml.depends
# Target: test.cmo, tags: { byte, compile, extension:cmo, extension:ml, file:test.cmo, file:test.ml, implem, ocaml, quiet }
.../bin/ocamlc.opt -c -o test.cmo test.ml
# Target: test.byte, tags: { byte, dont_link_with, extension:byte, file:test.byte, link, ocaml, program, quiet }
.../bin/ocamlc.opt test.cmo -o test.byte
# Compilation successful.

There you are: ocamlbuild called ocamldep to look for dependencies, then compiled the module, then linked it. And you have the list of all flag presents at each step in the process. You can check that besides custom, the necessary byte, link, ocaml, program are all present during the linking phase.

$ ocamlbuild -tag custom test.byte
Finished, 3 targets (2 cached) in 00:00:00.
scherer@jurancon:/tmp/ocamlbuild-test $ cat _build/_log 
### Starting build.
# Target: test.ml.depends, tags: { custom, extension:ml, file:test.ml, ocaml, ocamldep, quiet }
.../bin/ocamldep.opt -modules test.ml > test.ml.depends # cached
# Target: test.cmo, tags: { byte, compile, custom, extension:cmo, extension:ml, file:test.cmo, file:test.ml, implem, ocaml, quiet }
.../bin/ocamlc.opt -c -o test.cmo test.ml # cached
# Target: test.byte, tags: { byte, custom, dont_link_with, extension:byte, file:test.byte, link, ocaml, program, quiet }
.../bin/ocamlc.opt -custom test.cmo -o test.byte
# Compilation successful.

The custom tag was added to all compilation phases (we may have been more precise by adding it in _tags as a tag on test.byte only, so that it gets added only during the linking step), and resulted to the addition of the -custom flag. Job done. Notice that ocamlbuild only runs the linking command again, as the output of the others are already cached.