Project Layout and Metadata Specification¶
A typical jbuilder project will have one or more
at toplevel as well as
jbuild files wherever interesting things are:
libraries, executables, tests, documents to install, etc...
It is recommended to organize your project so that you have exactly one
library per directory. You can have several executables in the same
directory, as long as they share the same build configuration. If you’d
like to have multiple executables with different configurations in the
same directory, you will have to make an explicit module list for every
The next sections describe the format of Jbuilder metadata files.
Note that the Jbuilder metadata format is versioned in order to ensure
forward compatibility. There is currently only one version available,
but to be future proof, you should still specify it in your
files. If no version is specified, the latest one will be used.
Most configuration files read by Jbuilder are using the S-expression syntax, which is very simple. Everything is either an atom or a list. The exact specification of S-expressions is described in the documentation of the parsexp library.
In a nutshell, the syntax is as follows:
- atoms that do no contain special characters are simply written as
is. For instance:
barare valid atomic S-expressions
- atoms containing special characters or spaces must be quoted using
- lists are formed by surrounding a sequence of S-expressions separated
by spaces with parentheses:
(a b (c d))
- single-line comments are introduced with the
;character and may appear anywhere except in the middle of a quoted atom
- block comment are enclosed by
|#and can be nested
Note that the format is completely static. However you can do meta-programming on jbuilds files by writing them in OCaml syntax.
<package>.opam file is present, Jbuilder will know that the
<package> exists. It will know how to construct a
<package>.install file in the same directory to handle installation
via opam. Jbuilder also defines the
install alias, which depends on all the buildable
<package>.install files in the workspace. So for instance to build
everything that is installable in a workspace, run at the root:
$ jbuilder build @install
Declaring a package this way will allow you to add elements such as
libraries, executables, documentation, ... to your package by declaring
Such elements can only be declared in the scope defined by the
<package>.opam file. Typically, your
<package>.opam files should be at the root of your project, since
this is where
opam pin ... will look for them.
<package> must be non-empty, so in particular
files are ignored.
Any directory containing at least one
<package>.opam file defines
a scope. This scope is the sub-tree starting from this directory,
excluding any other scopes rooted in sub-direcotries.
Typically, any given project will define a single scope. Libraries and executables that are not meant to be installed will be visible inside this scope only.
Because scopes are exclusive, if you whish to include the dependencies
of the project you are currently working on into your workspace, you
may copy them in a
vendor directory, or any other name of your
choice. Jbuilder will look for them there rather than in the installed
world and there will be no overlap between the various scopes.
Note that Jbuilder will try to determine the version number of packages defined in the workspace. While Jbuilder itself makes no use of version numbers, it can be use by external tools such as ocamlfind.
Jbuilder determines the version of a package by first looking in the
<package>.opam for a
version variable. If not found, it will try
to read the first line of a version file in the same directory as the
<package>.opam file. The version file is any file whose name is, in
order in which they are looked for:
The version file can be generated by a user rule.
If the version can’t be determined, Jbuilder just won’t assign one.
Note that if you are using Topkg
as well in your project, you shouldn’t manually set a version in your
<package>.opam file or write/generate on of the file listed above.
See the section about Using topkg with jbuilder for more details.
Jbuilder follows the odig
conventions and automatically installs any README*, CHANGE*, HISTORY*
and LICENSE* files in the same directory as the
to a location where odig will find them.
Note that this includes files present in the source tree as well as generated files. So for instance a changelog generated by a user rule will be automatically installed as well.
By default Jbuilder traverses the whole source tree, ignoring the following files and directories:
- any file that start with
- any directory that start with either
To ignore a subtree, simply write a
jbuild-ignore file in the
parent directory containing the name of the sub-directories to ignore.
So for instance, if you write
src/foo won’t be traversed and any
jbuild file it contains will
jbuild-ignore files contain a list of directory names, one per line.