jbuild specification¶
jbuild
files are the main part of Jbuilder, and are the origin of
its name. They are used to describe libraries, executables, tests, and
everything Jbuilder needs to know about.
Stanzas¶
jbuild
files are composed of stanzas. For instance a typical
jbuild
looks like:
(library
((name mylib)
(libraries (base lwt))))
(rule
((targets (foo.ml))
(deps (generator/gen.exe))
(action (run ${<} -o ${@}))))
The following sections describe the available stanzas and their meaning.
jbuild_version¶
(jbuild_version 1)
specifies that we are using the version 1 of
the Jbuilder metadata format in this jbuild
file.
library¶
The library
stanza must be used to describe OCaml libraries. The
format of library stanzas is as follows:
(library
((name <library-name>)
<optional-fields>
))
<library-name>
is the real name of the library. It determines the
names of the archive files generated for the library as well as the
module name under which the library will be available, unless
(wrapped false)
is used (see below). It must be a valid OCaml
module name but doesn’t need to start with a uppercase letter.
For instance, the modules of a library named foo
will be
available as Foo.XXX
outside of foo
itself. It is however
allowed to write an explicit Foo
module, in which case this will
be the interface of the library and you are free to expose only the
modules you want.
<optional-fields>
are:
(public_name <name>)
this is the name under which the library can be- referred to as a dependency when it is not part of the current workspace,
i.e. when it is installed. Without a
(public_name ...)
field, the library will not be installed by Jbuilder. The public name must start by the package name it is part of and optionally followed by a dot and anything else you want. The package name must be one of the packages that Jbuilder knows about, as determined by the <package>.opam files
(synopsis <string>)
should give a one-line description of the library.- This is used by tools that list installed libraries
(modules <modules>)
specifies what modules are part of the library. By- default Jbuilder will use all the .ml/.re files in the same directory as the
jbuild
file. This include ones that are present in the file system as well as ones generated by user rules. You can restrict this list by using a(modules <modules>)
field.<modules>
uses the ordered set language where elements are module names and don’t need to start with a uppercase letter. For instance to exclude moduleFoo
:(modules (:standard \ foo))
(libraries (<library-dependencies>))
is used to specify the dependencies- of the library. See the section about library dependencies for more details
(wrapped <boolean>)
specifies whether the modules of the library should be- available only through the top-level library module, or should all be exposed
at the top level. The default is
true
and it is highly recommended to keep it this way. Because OCaml top-level modules must all be unique when linking an executables, polluting the top-level namespace will make your library unusable with other libraries if there is a module name clash. This option is only intended for libraries that manually prefix all their modules by the library name and to ease porting of existing projects to Jbuilder
(preprocess <preprocess-spec>)
specifies how to preprocess files if- needed. The default is
no_processing
. Other options are described in the preprocessing specification section
(preprocessor_deps (<deps-conf list>))
specifies extra dependencies of the- preprocessor, for instance if the preprocessor reads a generated file. The specification of dependencies is described in the dependency specification section
(optional)
, if present it indicates that the library should only be built- and installed if all the dependencies are available, either in the workspace or in the installed world. You can use this to provide extra features without adding hard dependencies to your project
(c_names (<names>))
, if your library has stubs, you must list the C files- in this field, without the
.c
extension
(cxx_names (<names>))
is the same asc_names
but for C++ stubs(install_c_headers (<names>))
, if your library has public C header files- that must be installed, you must list them in this field, with the
.h
extension
(modes (<modes>))
modes (byte
andnative
) which should be built by- default. This is only useful when writing libraries for the OCaml toplevel
(no_dynlink)
is to disable dynamic linking of the library. This is for- advanced use only, by default you shouldn’t set this option
(kind <kind>)
is the kind of the library. The default isnormal
, other- available choices are
ppx_rewriter
andppx_deriver
and must be set when the library is intended to be used as a ppx rewriter or a[@@deriving ...]
plugin. The reason whyppx_rewriter
andppx_deriver
are split is historical and hopefully we won’t need two options soon
(ppx_runtime_libraries (<library-names>))
is for when the library is a ppx- rewriter or a
[@@deriving ...]
plugin and has runtime dependencies. You need to specify these runtime dependencies here
(virtual_deps (<opam-packages>)
. Sometimes opam packages enable a specific- feature only if another package is installed. This is for instance the case
of
ctypes
which will only installctypes.foreign
if the dummyctypes-foreign
package is installed. You can specify such virtual dependencies here. You don’t need to do so unless you use Jbuilder to synthesize thedepends
anddepopts
sections of your opam file
js_of_ocaml
. See the section about js_of_ocamlflags
,ocamlc_flags
andocamlopt_flags
. See the section about specifying OCaml flags(library_flags (<flags>))
is a list of flags that are passed as it toocamlc
andocamlopt
when building the library archive files. You can use this to specify-linkall
for instance.<flags>
is a list of strings supporting variables expansion
(c_flags <flags>)
specifies the compilation flags for C stubs, using the ordered set language. This field supports(:include ...)
forms(cxx_flags <flags>)
is the same asc_flags
but for C++ stubs(c_library_flags <flags>)
specifies the flags to pass to the C compiler- when constructing the library archive file for the C stubs.
<flags>
uses the ordered set language and supports(:include ...)
forms. When you are writing bindings for a C library namedbar
, you should typically write-lbar
here, or whatever flags are necessary to to link against this library
(self_build_stubs_archive <c-libname>)
indicates to Jbuilder that the- library has stubs, but that the stubs are built manually. The aim of the field is to embed a library written in foreign language and/or building with another build system. It is not for casual uses, see the re2 library for an example of use
Note that when binding C libraries, Jbuilder doesn’t provide special
support for tools such as pkg-config
, however it integrates
easily with
configurator by
using (c_flags (:include ...))
and
(c_library_flags (:include ...))
.
executable¶
The executable
stanza must be used to describe an executable. The
format of executable stanzas is as follows:
(executable
((name <name>)
<optional-fields>
))
<name>
is a module name that contains the main entry point of the
executable. There can be additional modules in the current directory, you only
need to specify the entry point. Given an executable
stanza with (name
<name>)
, Jbuilder will know how to build <name>.exe
, <name>.bc
and
<name>.bc.js
. <name>.exe
is a native code executable, <name>.bc
is a
bytecode executable which requires ocamlrun
to run and <name>.bc.js
is a
JavaScript generated using js_of_ocaml.
Note that in case native compilation is not available, <name>.exe
will in fact be a custom byte-code executable. Custom in the sense of
ocamlc -custom
, meaning that it is a native executable that
embeds the ocamlrun
virtual machine as well as the byte code. As
such you can always rely on <name>.exe
being available.
<optional-fields>
are:
(public_name <public-name>)
specifies that the executable should be installed under that name. It is the same as adding the following stanza to yourjbuild
file:(install ((section bin) (files ((<name>.exe as <public-name>)))))
(package <package>)
if there is a(public_name ...)
field, this specifies the package the executables are part of(libraries (<library-dependencies>))
specifies the library dependencies. See the section about library dependencies for more details(modules <modules>)
specifies which modules in the current directory Jbuilder should consider when building this executable. Modules not listed here will be ignored and cannot be used inside the executable described by the current stanza. It is interpreted in the same way as the(modules ...)
field of libraries(preprocess <preprocess-spec>)
is the same as the(preprocess ...)
field of libraries(preprocessor_deps (<deps-conf list>))
is the same as the(preprocessor_deps ...)
field of librariesjs_of_ocaml
. See the section about js_of_ocamlflags
,ocamlc_flags
andocamlopt_flags
. See the section about specifying OCaml flags
executables¶
The executables
stanza is the same as the executable
stanza,
except that it is used to describe several executables sharing the
same configuration.
It shares the same fields as the executable
stanza, except that
instead of (name ...)
and (public_name ...)
you must use:
(names (<names>))
where<names>
is a list of entry point names. As forexecutable
you only need to specify the modules containing the entry point of each executable(public_names (<names>))
describes under what name each executable should be installed. The list of names must be of the same length as the list in the(names ...)
field. Moreover you can use-
for executables that shouldn’t be installed
rule¶
The rule
stanza is used to create custom user rules. It tells
Jbuilder how to generate a specific set of files from a specific set
of dependencies.
The syntax is as follows:
(rule
((targets (<filenames>))
(deps (<deps-conf list>))
(action <action>)))
<filenames>
is a list of file names. Note that currently Jbuilder
only support user rules with targets in the current directory.
<deps-conf list>
specifies the dependencies of the rule. See the
dependency specification section for more details.
<action>
is the action to run to produce the targets from the
dependencies. See the actions section for more details.
Note that contrary to makefiles or other build systems, user rules
currently don’t support patterns, such as a rule to produce %.y
from %.x
for any given %
. This might be supported in the
future.
ocamllex¶
(ocamllex (<names>))
is essentially a shorthand for:
(rule
((targets (<name>.ml))
(deps (<name>.mll))
(action (chdir ${ROOT} (run ${bin:ocamllex} -q -o ${<})))))
ocamlyacc¶
(ocamlyacc (<names>))
is essentially a shorthand for:
(rule
((targets (<name>.ml <name>.mli))
(deps (<name>.mly))
(action (chdir ${ROOT} (run ${bin:ocamlyacc} ${<})))))
menhir¶
The basic form for defining menhir parsers (analogous to ocamlyacc) is:
(menhir
((modules (<parser1> <parser2> ...))))
Modular parsers can be defined by adding a merge_into
field. This correspond
to the --base
command line option of menhir
. With this option, a single
parser named base_name
is generated.
(menhir
((merge_into <base_name>)
(modules (<parser1> <parser2> ...))))
Extra flags can be passed to menhir using the flags
flag:
(menhir
((flags (<option1> <option2> ...))
(modules (<parser1> <parser2> ...))))
alias¶
The alias
stanza lets you add dependencies to an alias, or specify an action
to run to construct the alias.
The syntax is as follows:
(alias
((name <alias-name>)
(deps (<deps-conf list>))
<optional-fields>
))
<name>
is an alias name such as runtest
.
<deps-conf list>
specifies the dependencies of the alias. See the
dependency specification section for more details.
<optional-fields>
are:
<action>
, an action to run when constructing the alias. See the actions section for more details.(package <name>)
indicates that this alias stanza is part of package<name>
and should be filtered out if<name>
is filtered out from the command line, either with--only-packages <pkgs>
or-p <pkgs>
The typical use of the alias
stanza is to define tests:
(alias
((name runtest)
(action (run ${exe:my-test-program.exe} blah))))
See the section about running tests for details.
Note that if your project contains several packages and you run test the tests
from the opam file using a build-test
field, then all your runtest
alias
stanzas should have a (package ...)
field in order to partition the set of
tests.
install¶
The install
stanza is what lets you describe what Jbuilder should install,
either when running jbuilder install
or through opam.
Libraries don’t need an install
stanza to be installed, just a
public_name
field. Everything else needs an install
stanza.
The syntax is as follows:
(install
((section <section>)
(files (<filenames>))
<optional-fields>
))
<section>
is the installation section, as described in the opam
manual. The following sections are available:
lib
libexec
bin
sbin
toplevel
share
share_root
etc
doc
stublibs
man
misc
=<files>= is the list of files to install.
<optional-fields>
are:
(package <name>)
. If there are no ambiguities, you can omit this field. Otherwise you need it to specify which package these files are part of. The package is not ambiguous when the first parent directory to contain a<package>.opam
file contains exactly one<package>.opam
file
Common items¶
Ordered set language¶
A few fields takes as argument an ordered set and can be specified using a small DSL.
This DSL is interpreted by jbuilder into an ordered set of strings using the following rules:
:standard
denotes the standard value of the field when it is absent- an atom not starting with a
:
is a singleton containing only this atom - a list of sets is the concatenation of its inner sets
(<sets1> \ <sets2>)
is the set composed of elements of<sets1>
that do- not appear in
<sets2>
In addition, some fields support the inclusion of an external file using the
syntax (:include <filename>)
. This is useful for instance when you need to
run a script to figure out some compilation flags. <filename>
is expected to
contain a single S-expression and cannot contain (:include ...)
forms.
Most fields using the ordered set language also support variables expansion. Variables are expanded after the set language is interpreted.
Variables expansion¶
Some fields can contains variables of the form $(var)
or ${var}
that are
expanded by Jbuilder.
Jbuilder supports the following variables:
ROOT
is the relative path to the root of the build contextCC
is the C compiler command line being used in the current build contextCXX
is the C++ compiler command line being used in the current build contextocaml_bin
is the path whereocamlc
livesOCAML
is theocaml
binaryOCAMLC
is theocamlc
binaryOCAMLOPT
is theocamlopt
binaryocaml_version
is the version of the compiler used in the current build contextocaml_where
is the output ofocamlc -where
ARCH_SIXTYFOUR
istrue
if using a compiler targeting a 64 bit architecture andfalse
otherwisenull
is/dev/null
on Unix ornul
on Windows
In addition, (action ...)
fields support the following special variables:
@
expands to the list of target, separated by spaces<
expands to the first dependency, or the empty string if there are no dependencies^
expands to the list of dependencies, separated by spacespath:<path>
expands to<path>
exe:<path>
is the same as<path>
, except when cross-compiling, in which case it will expand to<path>
from the host build contextbin:<program>
expands to a path toprogram
. Ifprogram
is installed by a package in the workspace (see install stanzas), the locally built binary will be used, otherwise it will be searched in thePATH
of the current build contextlib:<public-library-name>:<file>
expands to a path to file<file>
of library<public-library-name>
. If<public-library-name>
is available in the current workspace, the local file will be used, otherwise the one from the installed world will be usedlibexec:<public-library-name>:<file>
is the same aslib:...
except when cross-compiling, in which case it will expand to the file from the host build contextlib-available:<library-name>
expands totrue
orfalse
depending on wether the library is available or not. A library is available iff at least one of the following condition holds:- it is part the installed worlds
- it is available locally and is not optional
- it is available locally and all its library dependencies are available
version:<package>
expands to the version of the given package. Note that this is only supported for packages that are being defined in the current scope
The ${<kind>:...}
forms are what allows you to write custom rules that work
transparently whether things are installed or not.
Library dependencies¶
Dependencies on libraries are specified using (libraries ...)
fields in library
and executables
stanzas.
For libraries that are present in the workspace, you can use
either the real name (with some restrictions, see below) or the
public name. For libraries that are part of the installed world,
you need to use the public name. For instance:
(libraries (base re))
.
When resolving libraries, libraries that are part of the workspace are always prefered to ones that are part of the installed world.
Scope of internal library names
The scope of internal library names is not the whole workspace. It is restricted to the subtree starting from the closest parent containing a
<package>.opam
file, or the whole workspace if no such directory exist. Moreover, a subtree containing<package>.opam
doesn’ t inherit the internal names available in its parent scope.The idea behing this rule is that public library names must be universally unique, but internal ones don’t need to. In particular you might have private libraries that are only used for tests or building an executable.
As a result, when you create a workspace including several projects there might be a name clash between internal library names.
This scoping rule ensure that this won’t be a problem.
Alternative dependencies
In addition to direct dependencies you can specify alternative dependencies. This is described in the alternative dependencies section
It is sometimes the case that one wants to not depend on a specific library, but instead on whatever is already installed. For instance to use a different backend depending on the target.
Jbuilder allows this by using a
(select ... from ...)
form inside the list of library dependencies.Select forms are specified as follows:
(select <target-filename> from ((<literals> -> <filename>) (<literals> -> <filename>) ...))
<literals>
are lists of literals, where each literal is one of:<library-name>
, which will evaluate to true if<library-name>
is available, either in the workspace or in the installed world!<library-name>
, which will evaluate to true if<library-name>
is not available in the workspace or in the installed world
When evaluating a select form, Jbuilder will create
<target-filename>
by copying the file given by the first(<literals> -> <filename>)
case where all the literals evaluate to true. It is an error if none of the clauses are selectable. You can add a fallback by adding a clause of the form(-> <file>)
at the end of the list.
Preprocessing specification¶
Jbuilder accepts three kinds of preprocessing:
no_preprocessing
, meaning that files are given as it to the compiler, this is the default(action <action>)
to preprocess files using the given action(pps (<ppx-rewriters-and-flags>))
to preprocess files using the given list of ppx rewriters
Note that in any cases, files are preprocessed only once. Jbuilder
doesn’t use the -pp
or -ppx
of the various OCaml tools.
Preprocessing with actions
<action>
uses the same DSL as described in the user actions section, and for the same reason given in that section, it will be executed from the root of the current build context. It is expected to be an action that reads the file given as only dependency and outputs the preprocessed file on its standard output.More precisely,
(preprocess (action <action>))
acts as if you had setup a rule for every file of the form:(rule ((targets (file.pp.ml)) (deps (file.ml)) (action (with-stdout-to ${@} (chdir ${ROOT} <action>)))))
The equivalent of a
-pp <command>
option passed to the OCaml compiler is(system "<command> ${<}")
.Preprocessing with ppx rewriters
<ppx-rewriters-and-flags>
is expected to be a list where each element is either a command line flag if starting with a-
or the name of a library. Additionnally, any sub-list will be treated as a list of command line arguments. So for instance from the followingpreprocess
field:(preprocess (pps (ppx1 -foo ppx2 (-bar 42))))
The list of libraries will be
ppx1
andppx2
and the command line arguments will be:-foo -bar 42
.Libraries listed here should be libraries implementing an OCaml AST rewriter and registering themselves using the ocaml-migrate-parsetree.driver API.
Jbuilder will build a single executable by linking all these libraries and their dependencies. Note that it is important that all these libraries are linked with
-linkall
. Jbuilder automatically uses-linkall
when the(kind ...)
field is set toppx_rewriter
orppx_deriver
.It is guaranteed that the last library in the list will be linked last. You can use this feature to use a custom ppx driver. By default Jbuilder will use
ocaml-migrate-parsetree.driver-main
. See the section about using a custom ppx driver for more details.Per module preprocessing specification
By default a preprocessing specification will apply to all modules in the library/set of executables. It is possible to select the preprocessing on a module-by-module basis by using the following syntax:
(preprocess (per_file (<spec1> (<module-list1>)) (<spec2> (<module-list2>)) ...))
Where
<spec1>
,<spec2>
, ... are preprocessing specifications and<module-list1>
,<module-list2>
, ... are list of module names. It is currently not possible to distinguish between .ml/.mli files, however it wouldn’t be hard to support if needed.For instance:
(preprocess (per_file ((command "./pp.sh X=1" (foo bar))) ((command "./pp.sh X=2" (baz)))))
Dependency specification¶
Dependencies in jbuild
files can be specified using one of the
following syntax:
(file <filename>)
or simply<filename>
: depend on this file(alias <alias-name>)
: depend on the construction of this alias, for instance:(alias src/runtest)
(glob_files <glob>)
: depend on all files matched by<glob>
, see the glob section for details(files_recursively_in <dir>)
: depend on all files in the subtree with root<dir>
In all these cases, the argument supports variables expansion.
Glob
You can use globs to declare dependencies on a set of files. Note that globs will match files that exist in the source tree as well as buildable targets, so for instance you can depend on
*.cmi
.Currently jbuilder only support globbing files in a single directory. And in particular the glob is interpreted as follows:
- anything before the last
/
is taken as a literal path - anything after the last
/
, or everything if the glob contains no/
, is interpreted using the glob syntax
The glob syntax is interpreted as follows:
\<char>
matches exactly<char>
, even if it is a special character (*
,?
, ...)*
matches any sequence of characters, except if it comes first in which case it matches any character that is not.
followed by anything**
matches any character that is not.
followed by anything, except if it comes first in which case it matches anything?
matches any single character[<set>]
matches any character that is part of<set>
[!<set>]
matches any character that is not part of<set>
{<glob1>,<glob2>,...,<globn>}
matches any string that is matched by one of<glob1>
,<glob2>
, ...
- anything before the last
OCaml flags¶
In library
and executables
stanzas, you can specify OCaml
compilation flags using the following fields:
(flags <flags>)
to specify flags passed to bothocamlc
andocamlopt
(ocamlc_flags <flags>)
to specify flags passed toocamlc
only(ocamlopt_flags <flags>)
to specify flags passed toocamlopt
only
For all these fields, <flags>
is specified in the ordered set
language.
The default value for (flags ...)
includes some -w
options
to set warnings. The exact set depends on whether --dev
is
passed to Jbuilder. As a result it is recommended to write
(flags ...)
fields as follows:
(flags (:standard <my options>))
js_of_ocaml¶
In library
and executables
stanzas, you can specify js_of_ocaml options
using (js_of_ocaml (<js_of_ocaml-options>))
.
<js_of_ocaml-options>
are all optional:
(flags <flags>)
to specify flags passed tojs_of_ocaml
(javascript_files (<files-list>))
to specifyjs_of_ocaml
JavaScript- runtime files.
=<flags>= is specified in the ordered set language.
The default value for (flags ...)
depends on whether --dev
is passed to
Jbuilder. --dev
will enable sourcemap and the pretty JavaScript output.
User actions¶
(action ...)
fields describe user actions.
User actions are always run from the same subdirectory of the current build
context as the jbuild they are defined in. So for instance an action defined in
src/foo/jbuild
will be run from _build/<context>/src/foo
.
The argument of (action ...)
fields is a small DSL that is interpreted by
jbuilder directly and doesn’t require an external shell. All atoms in the DSL
support variables expansion. Moreover, you don’t need to specify dependencies
explicitly for the special ${<kind>:...}
forms, these are recognized and
automatically handled by Jbuilder.
The DSL is currently quite limited, so if you want to do something complicated it is recommended to write a small OCaml program and use the DSL to invoke it. You can use shexp to write portable scripts or configurator for configuration related tasks.
The following constructions are available:
(run <prog> <args>)
to execute a program(chdir <dir> <DSL>)
to change the current directory(setenv <var> <value> <DSL>)
to set an environment variable(with-<outputs>-to <file> <DSL>)
to redirect the output to a file, where<outputs>
is one of:stdout
,stderr
oroutputs
(for bothstdout
andstderr
)
(ignore-<outputs> <DSL)
to ignore the output, where<outputs>
is one of:stdout
,stderr
oroutputs
(progn <DSL>...)
to execute several commands in sequence(echo <string>)
to output a string on stdout(cat <file>)
to print the contents of a file to stdout(copy <src> <dst>)
to copy a file(copy-and-add-line-directive <src> <dst>)
to copy a file and add a line- directive at the beginning
(system <cmd>)
to execute a command using the system shell:sh
on Unix- and
cmd
on Windows
(bash <cmd>)
to execute a command using/bin/bash
. This is obviously- not very portable
Note: expansion of the special ${<kind>:...}
is done relative to the current
working directory of the part of the DSL being executed. So for instance if you
have this action in a src/foo/jbuild
:
(action (chdir ../../.. (echo ${path:jbuild})))
Then ${path:jbuild}
will expand to src/foo/jbuild
. When you run various
tools, they often use the filename given on the command line in error messages.
As a result, if you execute the command from the original directory, it will
only see the basename.
To understand why this is important, let’s consider this jbuild living in
src/foo
:
(rule
((targets (blah.ml))
(deps (blah.mll))
(action (run ocamllex -o ${@} ${<}))))
Here the command that will be executed is:
ocamllex -o blah.ml blah.mll
And it will be executed in _build/<context>/src/foo
. As a result, if there
is an error in the generated blah.ml
file it will be reported as:
File "blah.ml", line 42, characters 5-10:
Error: ...
Which can be a problem as you editor might think that blah.ml
is at the root
of your project. What you should write instead is:
(rule
((targets (blah.ml))
(deps (blah.mll))
(action (chdir ${ROOT} (run ocamllex -o ${@} ${<})))))
OCaml syntax¶
If a jbuild
file starts with (* -*- tuareg -*- *)
, then it is
interpreted as an OCaml script that generates the jbuild
file as described
in the rest of this section. The code in the script will have access to a
Jbuild_plugin
module containing details about the build context it is executed in.
The script can use the directive #require
to access libraries:
#require "base,re";;
Note that any library required by a jbuild
file must be part of the
installed world.
If you don’t like the S-expression syntax, then this method gives you a way to use whatever else you want. For instance you could have an API to describe your project in OCaml directly:
(* -*- tuareg -*- *)
#require "my_jbuild_api"
open My_jbuild_api
let () =
library "foo" ~modules:["plop"; "bidule"]
Currently the Jbuild_plugin
module is only available inside plugins. It is
however planned to make it a proper library, see the roadmap for details.