OCamlbuild’s job is to determine the sequence of calls to the compiler — with the right set of command-line flags — needed to build your OCaml-centric software project.
- 1. Overview
- 1.1. Is OCamlbuild well-suited for your project?
- 1.2. Examples
- 1.3. Pros, cons, and alternatives
- 1.4. Core concepts
- 1.5. Working with a simple program
- 1.6. Hygiene
- 1.7. Findlib-based packages
- 1.8. Syntax extensions
- 1.9. Archives, documentation
- 1.10. Source and build directories, module paths, include paths
- 2. Reference documentation
- 3. Enriching OCamlbuild through plugins
OCamlbuild is extremely convenient to use for simple projects.
If you have a small OCaml project (program or library), you could likely directly invoke ocamlbuild
to automatically discover various source files and dependencies, and build executables, library archives or documentation with one-line commands.
In simple cases you don’t need to write a configuration file at all.
A few examples of quick ocamlbuild
commands:
# builds a bytecode executable out of foo.ml and its local dependencies
ocamlbuild 'foo.byte'
# builds a native executable
ocamlbuild 'foo.native'
# builds a library archive from the modules listed (capitalized) in lib.mllib
ocamlbuild 'lib.cma'
# builds OCamldoc documentation from the modules listed in lib.odocl
ocamlbuild 'lib.docdir/index.html'
# enable a few ocamlfind packages and compile in debug mode
ocamlbuild -use-ocamlfind -pkgs 'lwt,react,yojson,sedlex.ppx' -tag 'debug' 'foo.native'
If repeating -pkgs 'lwt,react,yojson,sedlex.ppx' -tag 'debug'
becomes bothersome, you can create a _tags
file in the project directory with the content:
true: package(lwt), package(react), package(sedlex), package(yojson), debug
, and then just use:
ocamlbuild -use-ocamlfind 'foo.native'
OCamlbuild was designed as a generic build system (it is in fact not OCaml-specific), but also to be expressive enough to cover the specifics of the OCaml language that make writing good Makefile
s difficult, such as the dreaded units Foo and Bar make inconsistent assumptions about Baz
error.
We maintain a few self-contained examples of projects using various OCamlbuild features under examples/
:
Directory | Description |
---|---|
A minimal example with self-contained OCaml code in the project directory. |
|
OCaml code in |
|
A simple example of use of an Findlib package ( |
|
A simple example of using a |
|
A simple program using |
|
A simple example illustrating the use of OCamldoc to generate documentation as html, TeX, man-pages and graphviz files. See Archives, documentation for more details. |
|
A simple example demonstrating the use of symbolic links to include library code from another local project. See here for more details. |
🛈 NOTE
|
There are many ways to integrate OCamlbuild in your project.
The examples provided so far use a Makefile on top of OCamlbuild to provide the familiar make; make install; make clean interface to users, but you are free to do otherwise.
|
The main strengths of OCamlbuild are the following:
-
It ‘Just Works’ for most OCaml projects, with minimal configuration work on your part.
-
It is designed from the scratch with dynamic dependencies in mind. Dynamic dependencies are the dependencies that are only determined during the build of the target, e.g., the local modules on which a source file depends, and are not explicitly listed in a configuration file. This avoids, for example, the dance of pre-generating or post-generating
.depends
files that is occasionally bothersome withMakefile
projects, without requiring you to describe all local dependency relations manually either.
Two weaknesses are the following:
-
Instead of a homegrown soon-to-become-Turing-complete configuration language, OCamlbuild made the choice of using OCaml as its configuration language, and many users dislike this choice. Most features of OCamlbuild can be controlled through the purely declarative
_tags
file whose structure is explained in detail in this documentation, but for more complex projects you will eventually need to create amyocamlbuild.ml
to configure the tool through its OCaml library interface. We strive for a simple API and we document it, so that should be more pleasant than you expect. -
OCamlbuild is not the most efficient build system out there, and in particular its support for build parallelization is currently disappointing.
💡 TIPThis could be solved with more engineering effort. OCamlbuild is maintained by volunteers, and your contributions are warmly welcome, see CONTRIBUTING.adoc
.For now, OCamlbuild’s default build rules will not scale to millions-of-lines codebases. It is however used in countless useful libraries and projects where this has not been a limitation.
Some alternatives for building OCaml projects are:
-
OCaml-Makefile, a generic set of Makefile rules for OCaml projects. Valuable if you want to build OCaml projects with
make
, without rewriting your own boilerplate from scratch. -
OMake, a generic build system that has been succesfully used by several relatively large OCaml projects.
-
jenga, a build tool developed internally at Jane Street. The design is interestingly close to OCamlbuild’s, but with large engineering efforts oriented towards building their huge internal codebase. There is no easy-to-use frontend layer provided to build OCaml projects (and little documentation), it’s more of a build-your-own-build-system toolkit for now.
-
obuild wants to be a really-simple build system with a declarative configuration language that has 80% the features, to cover most simple projects.
🛈 NOTE
|
The Real World OCaml textbook uses a tool named corebuild .
This is in fact just a simple wrapper on top of ocamlbuild provided by the OCaml library named core , with some common options for core projects baked in.
|
OCamlbuild knows about a set of rules to build programs.
Rules are OCaml code that specifies on a high level how to build certain kinds of files, named targets, from some dependencies (statically known or dynamically discovered).
For example, a built-in "%.ml → %.cmo" rule describes how to build any .cmo
compilation unit file from the .ml
of the same name; if you call ocamlbuild foo.cmo
, it will either use foo.ml
in your source directory or, if it doesn’t exist, try to build it, for example from foo.mll
or foo.mly
.
OCamlbuild knows various targets to build all sorts of useful things: byte or native programs (.byte
, .native
), library archives (.cma
, .cmxa
, .cmxs
), documentation (.docdir/index.html
, .docdir/man
), etc.
We will detail these in the Reference section.
Tags are an abstraction layer designed to specify command-line flags in a declarative style.
If you’re invoking the compiler directly and wish to build a program with debug information enabled, you need to
pass the -g
flag to the compilation and linking step of the build process, but not during an initial syntactic preprocessing step (if any), when building .cma
library archives, or when calling ocamldoc
.
With OCamlbuild, you can simply add the debug
tag to your program’s targets, and it will sort out when to insert the -g
flag or not.
To attach tags to your OCamlbuild targets, you write them in a _tags
file.
Each line is of the form foo: bar
, where bar
is a list of tags, and foo
is a filter that determines which targets bar
applies to.
For example, the _tags
file:
true: package(toto), package(tata) <foo.*> or <bar.*>: debug "strange.ml": rectypes
will make your whole project (true
matches anything) depend on the Findlib packages toto
and tata
, compile modules foo
and bar
with debug information, and pass -rectypes
when compiling strange.ml
— but not strange.mli
.
For more detail, see the syntax of predicates in tags, and the set of built-in tags.
The _tags
file provides a convenient but limited interface to tune your project.
For any more general purpose, we chose to use a configuration file directly written in OCaml, instead of reinventing a home-made configuration language — or using your shell as Make does.
Code put in the myocamlbuild.ml
file at the root of your project will be compiled and executed by ocamlbuild
upon invocation.
For simple use cases, you should not have to write a myocamlbuild.ml
file, except maybe to specify project-wide configuration options — similar to command-line options you would pass to OCamlbuild.
But it also allows to define new rules and targets (for example to support a shiny new preprocessing program), to define new tags or refine the meaning of existing tags.
We will cover these use-cases in the more advanced section Enriching OCamlbuild through plugins.
Simple OCaml projects often have a set of .ml
and .mli
files that provide useful modules depending on each other, and possibly a main file myprog.ml
that contains the main program code.
mod1.ml mod1.mli mod2.ml myprog.ml
You can build your program using either the bytecode compiler, with
ocamlbuild 'myprog.byte'
or the native compiler, with
ocamlbuild 'myprog.native'
Let’s look at the organization of your source directory after this compilation command:
_build/ mod1.ml mod1.mli mod2.ml myprog.byte -> _build/myprog.byte* myprog.ml
OCamlbuild does all its work in a single _build
directory, to help keep your source code directory tree clean.
Targets are therefore built inside _build
.
It will generally add a symbolic link for the requested target in the user directory, but if a target does not appear after being built, chances are it is in _build
.
OCamlbuild will proactively complain if some compiled files/build artifacts were left in the source directory:
# In the source directory of the previous example.
ocamlc -c 'mod2.ml'
ocamlbuild 'myprog.byte'
SANITIZE: a total of 2 files that should probably not be in your source tree has been found. A script shell file "_build/sanitize.sh" is being created. Check this script and run it to remove unwanted files or use other options (such as defining hygiene exceptions or using the -no-hygiene option). IMPORTANT: I cannot work with leftover compiled files. ERROR: Leftover OCaml compilation files: File mod2.cmo in . has suffix .cmo File mod2.cmi in . has suffix .cmi Exiting due to hygiene violations.
rm mod2.cm*
If this annoys you, it is possible to exclude some files from this hygiene checking by tagging them with the precious
or not_hygienic
tags, or to disable the check globally using the -no-hygiene
command-line option.
The reason for this check is that leftover intermediate files can disrupt the way your build system works.
OCamlbuild knows which target you need (library archives or program executables), and tries to build their dependencies, which first builds the dependencies of those dependencies, etc., until it eventually reaches your source files (the inputs of the build process).
Everything present in the source directory is considered to be an input; if you keep old .cmo
files in your source repository, OCamlbuild will not try to rebuild them from source files, but take them as references to produce the final targets, which is not what you want if they are stale.
Your project will probably depend on external libraries as well.
Let’s assume they are provided via the Findlib tool ocamlfind
. Suppose we depend on packages tata
and toto
. To tell OCamlbuild about them, use the tags package(tata)
and package(toto)
.
You also need to tell OCamlbuild to enable support for ocamlfind
by passing the -use-ocamlfind
command-line option.
So you will have the following _tags
file:
true: package(tata), package(toto)
and invoke compilation with
ocamlbuild -use-ocamlfind 'myprog.byte'
🛈 NOTE
|
Because of the popularity of Findlib you can expect to always invoke ocamlbuild with the -use-ocamlfind option.
We will probably enable -use-ocamlfind by default in future versions of OCamlbuild, but in the meantime feel free to define a shell alias for convenience.
|
If you have a myocamlbuild.ml
file (see Enriching OCamlbuild through plugins) at the root of your OCamlbuild project, you can use it to set this option, instead of using one command line parameter.
Something like this:
open Ocamlbuild_plugin
let () =
dispatch (function
| Before_options ->
Options.use_ocamlfind := true
| _ -> ())
Since 0.9.4, ocamlbuild
also accepts a -toolchain foo
option that is passed to ocamlfind
.
If you use syntax extensions distributed through ocamlfind
, you can use them as any Findlib package, but you must also use the syntax(…)
tag to indicate which preprocessor you use: camlp4o
, camlp4r
, camlp5o
, etc.
true: syntax(camlp4o) true: package(toto), package(blah.syntax)
In recent versions of OCamlbuild (since OCaml 4.01), you can also specify this using the -syntax
command-line option:
ocamlbuild -use-ocamlfind -syntax 'camlp4o' 'myprog.byte'
🛈 NOTE
|
Passing the option -tag "syntax(camlp4o)" will also work in older versions.
More generally, -tag foo will apply the tag foo to all targets, it is equivalent to adding true: foo in your tag line.
Also note that the quoting, -tag "syntax(camlp4o)" instead of -tag syntax(camlp4o) , is necessary for your shell to understand tags that have parentheses.
|
If you use -ppx
preprocessors, you can use the parametrized tag ppx(…)
(-tag "ppx(…)"
) to specify the preprocessor to use.
Since ocamlbuild NEXT_RELASE, the tags ppopt(option)
and ppxopt(package,option)
are also supported — if you have set -use-ocamlfind
.
Some OCamlbuild features require you to add new kind of files in your source directory.
Suppose you would like to distribute an archive file mylib.cma
that would contain the compilation unit for your modules mod1.ml
and mod2.ml
.
For this, you should create a file mylib.mllib
listing the name of desired modules — capitalized, as in OCaml source code:
Mod1 Mod2
OCamlbuild knows about a rule "%.mllib → %.cma"
, so you can then use:
ocamlbuild 'mylib.cma'
or, for a native archive
ocamlbuild 'mylib.cmxa'
Similarly, if you want to invoke ocamldoc
to document your program, you should list the modules you want documented in a .odocl
file. If you name it mydoc.odocl
for example, you can then invoke:
ocamlbuild 'mydoc.docdir/index.html'
, which will produce the documentation in the subdirectory mydoc.docdir
, thanks to a rule "%.odocl → %.docdir/index.html"
.
The "source directories" that ocamlbuild
will traverse to look for rule dependencies are a subset of the subdirectory tree rooted at the "root directory", the place where you invoke ocamlbuild
.
❗ IMPORTANT
|
All filesystem paths discussed in this section are relative to the root directory. |
The build directory contains a copy of the source directories hierarchy, with the source file imported and additional targets produced during the previous builds.
It is by convention the subdirectory _build
of the root directory, although this can be set with the -build-dir
command-line option.
The build directory is not part of the source directories considered by OCamlbuild.
A subdirectory of the subdirectory tree is included in the source directories if it has the "traverse" tag set.
That means that if you want to add "foo/bar" (and its files) as part of the source directories and remove "foo/baz", you can use the following in your _tags
file:
"foo/bar": traverse "foo/baz": -traverse
If the option -r
(for recursive) is passed, then all subdirectories (recursively) are considered part of the source directories by default, except the build directory and directories that look like version-control information (.svn
, .bzr
, .hg
, .git
, _darcs
) and _opam
, used for project-local opam switches.
This option is enabled by default if the root directory looks like an OCamlbuild project: either a myocamlbuild.ml
or a _tags
file is present.
🛈 NOTE
|
The reason for this heuristic is that calling ocamlbuild from your home directory could take a very long time if it recursively traverses your subdirectories, to check for hygiene for example.
|
If the root directory does not look like an OCamlbuild project, but you still wish to use it as such, you can just add the -r
option explicitly.
In the other direction, you can explicitly disable recursive traverse with true: -traverse
in your _tags
file.
On many occasions OCamlbuild needs you to indicate compilation units (set of source and object files for a given OCaml module) located somewhere in the source directories.
The syntax to do this is to use an OCaml module name (in particular, capitalized), prefixed by the relative path from the root directory.
For example, bar/baz/Foo
will work with the files bar/baz/[fF]oo.{ml[i],cm*}
, depending on the compilation phase.
For convenience, it is possible to add a source directory to the set of include paths: paths that do not have to be explicitly prefixed to each module path.
If bar/
is in the include path, you can refer to bar/baz/Foo
as just baz/Foo
.
To add bar
to the include path, one can pass the -I bar
option to ocamlbuild, or tag "bar": include
in the _tags
file.
🛈 NOTE
|
Some have come to see the use of the same syntax -I foo in ocamlbuild and in OCaml compilers as a mistake, because the underlying concepts are rather different.
In particular, it is not the case that passing -I foo to ocamlbuild will transfer this command-line option to underlying compiler invocation.
If foo is inside the source directories, this should not be needed, and if it is outside you are encouraged to rely on ocamlfind packages instead of absolute paths.
|
In this chapter, we cover the built-in targets and tags provided by OCamlbuild.
We will omit features that are deprecated, because we found they lead to bad practices or were superseded by better options.
Of course, given that a myocamlbuild.ml
can add new rules and tags, this documentation will always be incomplete.
A large part of the file extensions in OCamlbuild rules have not been designed by OCamlbuild itself, but are standard extensions manipulated by the OCaml compiler. As you may not be familiar with them, we will recapitulate them now. For most use-cases OCamlbuild will hide most of those subtleties from you, but having this reference is still useful to understand advanced usage scenarios or read build logs.
The OCaml compilers also accept native files (name extensions {.o,.obj}
, {.a,.lib}
) and even source files (.c
) as input arguments, which get passed to the C toolchain (compiler or linker) when producing mixed C/OCaml programs or libraries.
File name, with extension | Description |
---|---|
|
Lexer description, to be processed by a lexer generator to produce a |
|
Grammar description, to be processed by a parser generator to produce a |
|
OCaml bytecode-compiled object file, providing the implementation of the module |
|
OCaml bytecode-compiled object file, providing the interface of the module |
|
OCaml bytecode-compiled archive file, containing a collection of |
|
OCaml native-compiled object file, providing the implementation of the module |
|
Complementary native object file for the module |
|
OCaml native-compiled archive file, containing a collection of |
|
Complementary native library files for a native-compiled archive file |
|
OCaml native-compiled archive file (or "plugin") for dynamic linking, containing a collection of |
In addition, the following extensions are not enforced by the compiler itself, but are commonly used by OCaml tools:
File name, with extension | Description |
---|---|
|
Lexer description, to be processed by a lexer generator to produce a |
|
Grammar description, to be processed by a parser generator to produce a |
|
Common extensions for files to be processed by an external preprocessor ( |
The built-in OCamlbuild targets for OCaml compilation all rely on file extensions to know which rule to use. Note that this is not imposed by OCamlbuild rule system, which would allow more flexible patterns. But it is always the filename of the target that determines which rules to apply to build it.
In consequence, OCamlbuild adds specific file extensions to the one listed above (or variations of them), that are the user-interface to use its rules providing certain features.
For example, .inferred.mli
is not a standard extension in the OCaml compiler, but it is understood by a built-in rule of OCamlbuild to ask for the .mli
that the compiler can auto-generate by typing a .ml
file without an explicit interface: running ocamlbuild foo.inferred.mli
will first build foo.ml
(or find it in the source directory), then generate foo.inferred.mli
from it. You are expected to then inspect foo.inferred.mli
, ideally add documentation, and then move it to foo.mli
by themselves.
The target extensions understood by the built-in OCamlbuild rules are listed in the following subsections.
Again, note that myocamlbuild
plugins may add new targets and rules. To see them programmatically, see The -documentation
option.
File name extension | Description |
---|---|
|
Builds those intermediate files from the corresponding source files ( |
|
Executables generated from a module and its dependencies for bytecode and native compilation. |
|
Contains a list of module paths ( |
|
The preferred way to build a library archive is to use a |
|
Contains a list of module paths ( |
|
The preferred way to build a plugin archive is to list its content in a WARNING: Before OCamlbuild 0.10.0, in absence of a |
|
Building |
These targets will call the documentation generator ocamldoc
.
ocamldoc
targets
File name | Description |
---|---|
|
Contains a list of module names for which to produce documentation, using one of the targets listed below. |
|
Building the target |
|
As |
|
Building the target |
|
Same as above, but generates documentation in TeXinfo format. |
|
Same as above, but generates a |
OCamlbuild will by default use ocamlyacc
, a legacy parser generator that is included in the OCaml distribution.
The third-party parser generator Menhir is superior in all aspects, so you are encouraged to use it instead.
To enable the use of Menhir instead of ocamlyacc
, you should pass the -use-menhir
option, or have true: use_menhir
in your _tags
file.
OCamlbuild will then activate menhir-specific builtin rule listed below.
ocamlyacc
and Menhir targets
File name extension | Description |
---|---|
|
Grammar description files. They will be passed on to |
|
Menhir (not ocamlyacc) supports building a parser by composing several |
|
Menhir (but not |
File name extension | Description | ||
---|---|---|---|
|
Call the |
||
|
Infer a |
||
|
Contains a list of module paths (
|
||
|
Produces object files for static or dynamic linking, or a C source file, by passing the |
||
|
Produces object files for static or dynamic linking by passing the |
||
|
OCamlbuild can build |
||
|
Contains a list of file paths (e.g., |
||
|
Requesting the build of |
File name extension | Description | ||
---|---|---|---|
|
OCamlbuild supports requesting
The more robust solution is to build |
||
|
This target produces a pretty-printing (as OCaml source code) of the OCaml AST produced by preprocessing the corresponding |
For convenience, we try to offer a tag for each setting exported as command-line parameters by the OCaml compilers and tools.
A builtin tag foo_bar
corresponding to the option -foo-bar
is in general better than trying to pass -cflags -foo-bar
to the ocamlbuild compilation command, as it can enable the -foo-bar
flag only when it make sense, in a more fine-grained way that "during a compilation command".
💡 TIP
|
If you notice that a compiler-provided command-line option is missing its tag counterpart, this is a bug that you should report against OCamlbuild. Feel free to look at the implementation and send a patch adding this tag, it is really easy. |
-
absname
-
annot
-
asm
(forocamlopt’s `-S
) -
bin_annot
-
color(mode)
(for colors in compiler messages, mode can beauto
,always
ornever
) -
compat_32
-
custom
-
debug
(for-g
) -
dtypes
-
for-pack(PackModule)
-
inline(5)
-
cc(ccomp)
-
cclib(-llibname)
-
ccopt(option)
-
keep_locs
-
linkall
-
no_alias_deps
-
no_float_const_prop
-
noassert
-
noautolink
(since 0.11.0) -
nolabels
-
nopervasives
-
nostdlib
(since 0.9.4) -
opaque
-
open(MyPervasives)
-
output_obj
-
output_shared
(for-cclib -shared
, automatically set by.{byte,native}.{so,dll,dylib}
targets) -
pp(my_pp_preprocessor)
-
ppx(my_ppx_preprocessor)
-
principal
-
profile
(for-p
) -
rectypes
-
runtime_variant(_pic)
-
safe_string
-
short_paths
-
strict_formats
-
strict_sequence
-
thread
-
unsafe
-
unsafe_string
-
warn(A@10-28@40-42-45)
-
warn_error(+10+40)
-
optimize(2)
(2
gives the defaults of the tags below,3
optimizes more,classic
switches off flambda) -
optimization_rounds(2)
-
inline_toplevel(160)
-
inline_branch_factor(0.1)
-
inline_alloc_cost(7)
-
inline_branch_cost(5)
-
inline_call_cost(5)
-
inline_indirect_cost(4)
-
inline_prim_cost(3)
-
inline_lifting_benefit(1300)
-
inline_max_depth(1)
-
inline_max_unroll(0)
-
unbox_closures_factor(1)
-
inlining_report
-
remove_unused_arguments
-
unbox_closures
-
no_unbox_free_vars_of_closures
-
no_unbox_specialized_args
-
afl_instrument
(since 0.12.0) -
afl_inst_ratio
(since 0.12.0)
-
package(pkgname)
-
linkpkg
-
dontlink(pkgname)
-
predicate(foo)
-
syntax(bar)
-
ppopt(camlp4-option)
(since 0.13.0) -
ppxopt(package,option)
(since 0.13.0)
-
quiet
(-q
) -
generate_ml
(-ml
)
-
only_tokens
-
infer
-
explain
-
external_tokens(TokenModule)
-
use_caml4_{,bin}
-
camlp4{rrr,orrr,oof,orf,rf,of,r,o}{,.opt}
The tags use_{ocamlbuild,ocamldoc,toplevel,graphics,dbm,nums,bigarray,str,unix,dynlink}
were designed to indicate that the tagged modules depend on the corresponding libraries from the OCaml distributions (use_{ocamlbuild,ocamldoc,toplevel}
allows to compile against the tools' libraries to build plugins).
We now recommend to enable those libraries through their corresponding ocamlfind
package.
-
not_hygienic
andprecious
: explicitly indicate that a file is part of the source directory and should not be warned about by the hygiene-checking tools. This is useful if for some reason you are given, for example, a.cmi
file to use unchanged in your project. -
traverse
: explicitly indicate that OCamlbuild should consider this subdirectory as part of the current project; this flag is set for all subdirectories by default (so OCamlbuild will look in subdirectories recursively to find module dependencies) as soon as the current directory "looks like an OCamlbuild project" (there is either amyocamlbuild.ml
ortags
file present). This tag is usefully used negative,"foo": -traverse
, to say that a part of the local directory hierarchy should _not be considered by OCamlbuild. -
include
,traverse
: see the section above on source directories and include paths. -
global tags: setting
true: use_menhir
in the root_tags
file is equivalent to passing the-use-menhir
command-line parameter.
These tags are generally not meant to be used directly in _tags
file, but rather to serve as the context part of tag declarations.
For example, the link
flag is automatically added thet set of tags of linking-related command, allowing tag declarations to add specific flags during linking phase only — but it would make little sense to explicitly add the link
tag to a target in your _tags
file.
-
language context:
c
orocaml
indicate whether the compiler invocation are working with OCaml files, or C files (to be passed to the underlying C toolchain). If you wished to use OCamlbuild for a completely different purpose (not necessarily OCaml-related), for example building LaTeX documents, you could use a correspondinglatex
tag. -
compilation stage context:
pp
(syntactic preprocessing),compile
(compilation of source files),link
(linking of object files), but alsopack
(when packing compiled object files),library
(when creating library archives),infer_interface
(producing a.mli
from the corresponding.ml
) andtoplevel
(when building custom toplevels). -
byte or native compilation context:
byte
(ocamlc
) ornative
(ocamlopt
). -
File tags: when building the target
foo.bar
, a tagfile:foo.bar
is added to the set of current tags. This can be used for one-off customizations. -
extension tags: when building the target
foo.bar
, a tagextension:bar
is added to the set of current tags. This is used by the builtinocamldoc
rules to enable either-latex
or-dot
depending on the requested target extension. -
tool-specific tags:
menhir
,ocamlyacc
,ocamllex
,doc
(forocamldoc
)
Invoking ocamlbuild -documentation
will give a list of rules and tags known to OCamlbuild in the current project (including those defined in the myocamlbuild
plugin).
This is a good way to quickly look for the tag name corresponding to a particular option, and also more accurate than the above reference manual, which does cannot describe plugin-specific features.
⚠ WARNING
|
This output is sensitive to the current configuration.
For example, ocamlbuild -use-ocamlfind -documentation and ocamlbuild -no-ocamlfind -documentation produce different outputs, as the latter does not include ocamlfind -specific tags.
|
Here is an example of rule documentation included in the ocamlbuild -documentation
output:
rule "ocaml: modular menhir (mlypack)"
~deps:[ %.mlypack ]
~prods:[ %.mli; %.ml ]
~doc:"Menhir supports building a parser by composing several .mly files
together, containing different parts of the grammar description. To
use that feature with ocamlbuild, you should create a .mlypack file
with the same syntax as .mllib or .mlpack files: a
whitespace-separated list of the capitalized module names of the .mly
files you want to combine together."
your_build_function
⚠ WARNING
|
The previous sample resembles OCaml code (see Rule declarations), but is not valid OCaml code. |
Note that rule declaration only indicate the static dependencies of rules (those that determine whether or not the rule will be tried). This rule is explicit about the fact that invoking Menhir produces both a .ml
and .mli
.
Parameterized tags are documented since OCaml version 4.03. An example:
parametrized flag {. compile, ocaml, ppx(my_ppx) .} "-ppx my_ppx" [...] flag {. compile, no_alias_deps, ocaml .} "-no-alias-deps"
A _tags
file associates file name patterns with tags like this:
# This is a comment true: bin_annot, debug <protocol_*> or <main.*>: package(yojson)
The general syntax is:
{pattern}: {comma-separated tag list}
These items are ended by a newline.
Comments (starting with #
) and empty lines are ignored, and an escaped line break is considered as whitespace (so those items can span multiple lines).
The {pattern}
part is what we call a "glob expression", which is an expression built of basic logic connective on top of "glob patterns".
Syntax | Example | Meaning |
---|---|---|
|
|
Paths matching the pattern |
|
|
The exact string |
|
|
Paths matching at least one of the expression |
|
|
Paths matching both expressions |
|
|
Paths not matching the expression |
|
|
All pathnames. |
|
|
Nothing. |
|
|
Same as |
Syntax | Example | Matches | Does not match | Meaning |
---|---|---|---|---|
|
|
|
|
The exact string |
|
|
The empty path |
|
Any string not containing a slash |
|
|
|
|
Any one-letter string, excluding the slash |
|
|
|
|
The empty string, or any string ending with a slash |
|
|
|
|
The empty string, or any string starting with a slash |
|
|
|
|
Any string starting and ending with a slash |
|
|
|
|
Any one-letter string made up of characters from one of the given ranges. |
|
|
|
|
Any one-letter string not made up of characters from one of the given ranges. |
|
|
|
|
Any string with a (possibly empty) prefix matching the pattern |
|
|
|
|
Any string matching one of the given patterns. |
In addition, rule patterns may include pattern variables. %(foo: p)
will match for the pattern p
and name the result %(foo)
.
For example, %(path: <**/>)foo.ml
is useful. %(foo)
will match the pattern true
and name the result %(foo)
, and finally %
will match the pattern true
and match the result %
.
Consider the following examples:
%.cmx %(dir).docdir/%(file) %(path:<**/>)lib%(libname:<*> and not <*.*>).so
If you have a myocamlbuild.ml
file at the root of your OCamlbuild project, the building process will run in two steps.
First, OCamlbuild will compile myocamlbuild.ml
, linking it with all the modules that are part of the globally installed ocamlbuild
executable.
This will produce a program _build/myocamlbuild
that behaves exactly like ocamlbuild
itself, except that it also runs the code of your myocamlbuild.ml
file.
Immediately after, OCamlbuild will stop (before doing any work on the targets you gave it) and start the _build/myocamlbuild
program instead, that will handle the rest of the job.
This is quite close to how, for example, XMonad (a window manager whose configuration files are pure Haskell) works.
This means that it is technically possible to do anything in myocamlbuild.ml
that could be done by adding more code to the upstream OCamlbuild sources.
But in practice, relying on the implementation internals would be fragile with respect to OCamlbuild version changes.
We thus isolated a subset of the OCamlbuild API, exposed by the Ocamlbuild_plugin
module, that defines a stable interface for plugin developers.
It lets you manipulate command-line options, define new rules and targets, add new tags or refine the meaning of existing flags, etc.
The signature of this module is the PLUGIN
module type of the interface-only signatures.mli
file of the OCamlbuild distribution.
🛈 NOTE
|
You will find the module source code littered with comments explaining the purpose of the exposed values, but this documentation aspect can still be improved.
We warmly welcome patches to improve this aspect of ocamlbuild — or any other aspect.
|
You can influence the myocamlbuild.ml
compilation-and-launch process in several ways:
-
The
-no-plugin
option allows to ignore themyocamlbuild.ml
file and just run the stockocamlbuild
executable on your project. This means that fancy new rules introduced bymyocamlbuild.ml
will not be available. -
The
-just-plugin
option instructs OCamlbuild to stop compilation after having built the plugin. It also guarantees that OCamlbuild will try to compile the plugin, which it may not always do, for example when you only ask for cleaning or documentation. -
The
-plugin-option FOO
option will pass the command-line optionFOO
to themyocamlbuild
invocation — and ignore it during plugin compilation. -
The
-plugin-tag
and-plugin-tags
options allow to pass tags that will be used to compile the plugin. For example, if someone develops a nice library to help writing OCamlbuild plugins and distribute as 'toto.ocamlbuild' inocamlfind
then-plugin-tag "package(toto.ocamlbuild)"
will let you use it in yourmyocamlbuild.ml
.
🛈 NOTE
|
The rationale for -plugin-option and -plugin-tag to apply during different phases of the process is that an option is meaningful at runtime for the plugin, while a plugin tag is meaningful at compile-time.
|
Tag and rule declarations, or configuration option manipulation, are side-effects that modify a global OCamlbuild state.
It would be fragile to write your myocamlbuild.ml
with such side-effects performed at module initialization time, in the following style:
open Ocamlbuild_plugin
(* bad style *)
let () =
Options.ocamlc := "/better/path/to/ocamlc"
;;
The problem is that you have little idea, and absolutely no flexibility, of the time at which those actions will be performed with respect to all the other actions of OCamlbuild. In this example, command-line argument parsing will happen after this plugin effect, so the changed option would be overridden by command-line options, which may or may not be what a plugin developer expects.
To alleviate this side-effect order issue, OCamlbuild lets you register actions at hook points, to be called at a well-defined place during the OCamlbuild process. If you want your configuration change to happen after options have been processed, you should in fact write:
open Ocamlbuild_plugin
let () =
dispatch (function
| After_options ->
Options.ocamlc := "..."
| _ -> ())
The dispatch
function register a hook-listening function provided by the user.
Its type is (hook → unit) → unit
.
The hooks are currently defined as:
(** Here is the list of hooks that the dispatch function have to handle.
Generally one responds to one or two hooks (like After_rules) and do
nothing in the default case. *)
type hook =
| Before_hygiene
| After_hygiene
| Before_options
| After_options
| Before_rules
| After_rules
⚠ WARNING
|
OCamlbuild does not guarantee any order in which it will call various hooks, except of course that Before_foo always happens before After_foo .
In particular, the hygiene hooks may be called before or after other hooks, or not be called at all if OCamlbuild decides not to check Hygiene.
|
A flag declaration maps a set of tags to a list of command-line arguments/flags/options/parameters. These arguments will be added to a given compilation command if each of the tags are present on the given target.
The following example can be found in ocaml_specific.ml
, the file of the OCamlbuild sources that defines most OCaml-specific tags and rules of OCamlbuild:
flag ["ocaml"; "annot"; "compile"] (A "-annot");
This means that the -annot
command-line option is added to any compilation command for which those three tags are present.
The tags "ocaml"
and "compile"
are activated by default by OCamlbuild, "ocaml"
for any ocaml-related command, and "compile"
specifically for compilation steps — as opposed to linking, documentation generation, etc.
The "annot"
flag is not passed by default, so this tag declaration will only take effects on targets that are explicitly marked annot
in the _tags
file.
This very simple declarative language, mapping sets of tags to command-line options, is the way to give meaning to OCamlbuild tags — either add new ones or overload existing ones. It is very easy, for example, to pass a different command-line argument depending on whether byte or native-compilation is happening.
flag ["ocaml"; "use_camlp4_bin"; "link"; "byte"]
(A"+camlp4/Camlp4Bin.cmo");
flag ["ocaml"; "use_camlp4_bin"; "link"; "native"]
(A"+camlp4/Camlp4Bin.cmx");
The A
constructor stands for ‘atom(ic)’, and is part of a spec
datatype, representing specifications of fragments of command.
We will not describe its most advanced constructors — it is again exposed and documented in signatures.mli
— but the most relevant here are:
(** The type for command specifications. That is pieces of command. *)
and spec =
| N (** No operation. *)
| S of spec list (** A sequence. This gets flattened in the last stages *)
| A of string (** An atom. *)
| P of pathname (** A pathname. *)
[...]
💡 TIP
|
When introducing new flags, it is sometime difficult to guess which combination of tags to use.
A hint to find the right combination is to have a look at OCamlbuild’s log file that is saved in _build/_log each time ocamlbuild is run.
It contains the targets OCamlbuild tried to produce, with the associated list of tags and the corresponding command lines.
|
You can also define families of parametrized tags such as package(foo)
or inline(30)
.
This is done through the pflag
function, which takes a list of usual tags, the special parametrized tag, and a function from the tag parameter to the corresponding command specification.
Again from the PLUGIN
module type in signatures.mli
:
(** Allows to use [flag] with a parametrized tag (as [pdep] for [dep]).
Example:
pflag ["ocaml"; "compile"] "inline"
(fun count -> S [A "-inline"; A count])
says that command line option "-inline 42" should be added
when compiling OCaml modules tagged with "inline(42)". *)
val pflag : Tags.elt list -> Tags.elt -> (string -> Command.spec) -> unit
OCamlbuild let you build your own rules, to teach it how to build new kind of targets.
This is done by calling the rule
function from a plugin, which is declared and documented in the PLUGIN
module in signatures.mli
.
We will not write an exhaustive documentation here (for this, have a look at signatures.mli
), but rather expose the most common features through representative examples.
Our first example is simple, as it is a rule without dynamic dependencies:
rule "ocaml dependencies ml"
~prod:"%.ml.depends"
~dep:"%.ml"
~doc:"call ocamldep to compute a syntactic over-approximation \\
of the dependencies of the corresponding implementation file"
ocamldep_ml_command
The first string parameter is the name of the rule.
This rule tells OCamlbuild how to build foo.ml.depends
from its foo.ml
. The %
character here is a pattern variable: if the target name (e.g., foo.ml.depends
) matches the pattern of the rule production %.ml.depends
then OCamlbuild will try to build the static dependency of the rule.
The static dependency is the evaluation of %.ml
in the pattern-matching environment % → "foo"
(that is, foo.ml
).
If this static dependency can be built, then the "action" ocamldep_ml_command
will be invoked to produce the expected result.
The action, of type PLUGIN.action
, is a function that takes the current pattern-matching environment (in our example, mapping the pattern variable %
to foo
), a builder function, and returns a command, the command to execute to produce the final target of this rule.
A plugin developer defining this rule should define the ocamldep_ml_command
as follows:
let ocamldep_ml_command env _build =
let arg = env "%.ml" and out = env "%.ml.depends" in
let tags = tags_of_pathname arg ++ "ocaml" ++ "ocamldep" in
Cmd(S[A "ocamldep"; T tags; A "-modules"; P arg; Sh ">"; Px out])
The first line in this definition uses the pattern environment to compute the actual name of the input and output files.
These are then passed in as arguments to the ocamldep
command and shell redirect, respectively, on the third line.
The environment type PLUGIN.env
is just string → string
, it takes a pattern and substitutes its pattern variables to return a closed result.
The second line in this definition computes the tags to include in the command invocation.
When OCamlbuild is passed back the command, it uses the tag declarations to determine which, if any, additional flags to insert into the command invocation.
The call tags_of_pathname arg
looks up in the _tags
file any tags associated with file foo.ml
.
To these tags the rule code also adds the two contextual tags ocaml
and ocamldep
(on which flag declarations may depend).
Finally, the command is built:
Cmd(S[A "ocamldep"; T tags; A "-modules"; P arg; Sh ">"; Px out])
We already mentioned above the constructors S
, A
and P
of the command.spec
type:
-
S
just builds a sequence by concatenating sequent fragments; -
A
is used for ‘atoms’ (fragments of text to be included as-is, but may be escaped to make them syntactic shell code); and -
P
denotes a filesystem path that should be quoted.
The constructor T
is used to embed tags within a command.
Mind that passing T
twice, one with the tag set ocaml, ocamldep
and the other with the tag foo
, is not equivalent to passing ocaml, ocamldep, foo
together, as the transformation of tags into flags proceeds on each T
fragment separately.
Sh
is used for bits of raw shell code that should not be quoted at all, here the output redirection >
.
Finally, Px
indicates a filesystem path just as P
, but it adds the information that this filesystem path is the path of the target produced by this rule.
This information is used by OCamlbuild for logging purposes.
🛈 NOTE
|
Tags handling in rules
It is entirely the rule author’s responsibility to include tags in the action’s command. In particular, it is the code of the rule’s action that decides which, if any, tags are taken into account and if they come from the rule dependencies, products or both. (Unfortunately, the built-in rules themselves are sometimes inconsistent on this.) |
In the action ocamldep_ml_command
of the previous example, the ~build
parameter (of type PLUGIN.builder
) was ignored, because the rule had no dynamic dependencies.
Therefore there was no need to build extra targets determined during the execution of the rule itself.
The static dependency is built by ocamlbuild
's resolution engine before the action executed.
On the contrary, dynamic dependencies are targets that are not indicated as dependencies in the rule declaration, but who are dynamically requested to build during the execution of the rule action.
For example, the target_list
action below reads a targets of paths in a %.itarget
file, and builds all those targets.
The targets cannot be known at the time the rule is declared, they are dynamic dependencies.
let target_list env build =
let itarget = env "%.itarget" in
let targets =
let dir = Pathname.dirname itarget in
let files = string_list_of_file itarget in
List.map (fun file -> [Pathname.concat dir file]) files
in
let results = List.map Outcome.good (build targets) in
let link_command result =
Cmd (S [A "ln"; A "-sf";
P (Pathname.concat !Options.build_dir result);
A Pathname.pwd])
in
Seq (List.map link_command results)
rule "target files"
~dep:"%.itarget"
~stamp:"%.otarget"
~doc:"If foo.itarget contains a list of ocamlbuild targets, \
asking ocamlbuild to produce foo.otarget will \
build each of those targets in turn."
target_list
The string_list_of_file
function reads a file and returns the list of its lines.
It is used in the various built-in rules for files containing other file or module paths (e.g., .mllib
, .odocl
or here .itarget
).
The function build
takes as argument a list of lists, to be understood as a conjunction of disjunctions.
For example, if passed the input [["a/foo.byte"; "b/foo.byte"]; ["a/foo.native"; "b/foo.native"]]
, it
tries to build ( (a/foo.byte
OR b/foo.byte
) AND (a/foo.native
OR b/foo.native
) ).
The disjunctive structure (this OR that) is useful because we are often not quite sure where a particular target may be (for example the module Foo
may be in any of the subdirectories in the include path).
The conjunctive structure (this AND that) is essential to parallelizing the build, since ocamlbuild
tries to build all these targets in parallel, whereas sequential invocation of the build function on each of the disjunctions would give sequential builds.
The function build
returns a list of outcomes ((string, exn) Outcome.t
— Outcome.t
is just a disjoint-sum type),
that is either a string
(the possible target that could be built) or an exception.
Outcome.good
returns the good result if it exists, or raises the exception.
In the rule above, the production "%.otarget"
is not passed as ~prod
parameter, but as a ~stamp
.
Stamps are special files that record the list of digests of the dynamic dependencies of the rule that produced them.
This is useful to know whether a target should be re-compiled, or whether it is already up-to-date from a previous build and can be just kept as-is.
Imagine that a rule to produce a file foo.weird
depends on the rules listed in the corresponding foo.itarget
(and then performs some build action).
When should foo.weird
be rebuilt, and when is it up-to-date?
More precisely, after we have built the targets of foo.itarget
, how do we know whether we should re-run the build action of foo.weird
?
Obviously, just checking if the foo.itarget
file changed is not enough (the list of targets could be identical and yet, if one of the target changed, foo.weird
must be rebuilt).
This is where foo.otarget
comes in: because it contains a list of digests of the dependencies of foo.itarget
, foo.weird
can statically depend on foo.otarget
.
foo.weird
does not need to depend on foo.itarget
directly, and it will transitively depend on it through foo.otarget
.
This stamp file will change each time one of the foo.itarget
elements changes, and thus foo.weird
will be rebuilt exactly as necessary.
Such stamps should be used each time a rule has no natural file output to use as output (the case of .itarget
), or when this file output does not contain enough information for its digest to correctly require rebuilding.
The latter case occurs in the rule to build ocamldoc
documentation %.docdir/index.html
: the index.html
only lists the documented modules.
It does not contain their documentation — that is in other files generated.
The rule thus produces a stamp %.docdir/html.stamp
.
You should only depend on that stamp if you want your rule to be executed each time the documentation changes.
Most rules need exactly one pattern variable and use %
for this purpose.
You may use any string of the form %(identifier)
as pattern variable, or even %(identifier:pattern)
, in which case the pattern will be only be matched by a string matching the corresponding pattern
.
For example, the rule to produce the dynamic library archive dllfoo.so
from the file list libfoo.clib
starts as follows:
rule "ocaml C stubs: clib & (o|obj)* -> (a|lib) & (so|dll)"
~prods:(["%(path:<**/>)lib%(libname:<*> and not <*.*>)"-.-ext_lib] @
if Ocamlbuild_config.supports_shared_libraries then
["%(path:<**/>)dll%(libname:<*> and not <*.*>)"-.-ext_dll]
else
[])
~dep:"%(path)lib%(libname).clib"
One cannot define a rule if there already exists a built-in rule of the same name.
Directly including the rule "ocaml dependencies ml"
used above in your plugin would result in a failure when loading the plugin:
Rule.Exit_rule_error("Rule.add_rule: already exists: (ocaml dependencies ml)").
Even if you pick another name for the rule, you may find out that the rule you are defining is not used. OCamlbuild orders rule definitions, and the new rule needs to come before the rule that it is replacing.
Use the ~insert
argument to rule
to either (suggested) place it before the built in rule (~insert:(`before "ocaml dependencies ml)
) or at the top (~insert:(`top)
).
The default behavior, in absence of ~insert
argument, is to place custom rules at the bottom; this is fine when defining rules for new targets, but not when overriding built-in rules.
link:../samples/complete-example-menhir-support-in-ocamlbuild/myocamlbuild.ml[role=include]
, where the Menhir-specific actions in Ocaml_tools
are defined as follows:
link:../samples/complete-example-menhir-support-in-ocamlbuild/Ocaml_tools.ml[role=include]