Advanced topics¶
This section describes some details of Jbuilder for advanced users.
META file generation¶
Jbuilder uses META
files from the findlib library
manager in order
to interoperate with the rest of the world when installing libraries. It
is able to generate them automatically. However, for the rare cases
where you would need a specific META
file, or to ease the transition
of a project to Jbuilder, it is allowed to write/generate a specific
one.
In order to do that, write or setup a rule to generate a
META.<package>.template
file in the same directory as the
<package>.opam
file. Jbuilder will generate a META.<package>
file from the META.<package>.template
file by replacing lines of
the form # JBUILDER_GEN
by the contents of the META
it would
normally generate.
For instance if you want to extend the META
file generated by
Jbuilder you can write the folliwing META.foo.template
file:
# JBUILDER_GEN
blah = "..."
Using a custom ppx driver¶
You can use a custom ppx driver by putting it as the last library in (pps
...)
forms. An example of alternative driver is ppx_driver. To use it instead of
ocaml-migrate-parsetree.driver-main
, simply write ppx_driver.runner
as
the last library:
(preprocess (pps (ppx_sexp_conv ppx_bin_prot ppx_driver.runner)))
Driver expectation¶
Jbuilder will invoke the executable resulting from linking the libraries
given in the (pps ...)
form as follows:
ppx.exe <flags-written-by-user> --dump-ast -o <output-file> \
[--cookie library-name="<name>"] [--impl|--intf] <source-file>
Where <source-file>
is either an implementation (.ml
) or
interface (.mli
) OCaml source file. The command is expected to write
a binary OCaml AST in <output-file>
.
Additionally, it is expected that if the executable is invoked with
--as-ppx
as its first argument, then it will behave as a standard
ppx rewriter as passed to -ppx
option of OCaml. This is for two
reasons:
- to improve interoperability with build systems other than Jbuilder
- so that it can be used with merlin
Findlib integration and limitations¶
Jbuilder uses META
files to support external libraries. However, it
doesn’t export the full power of findlib to the user, and especially
it doesn’t let the user specify predicates.
The reason for this limitation is that so far they haven’t been
needed, and adding full support for them would complicate things quite
a lot. In particular, complex META
files are often hand-written and
the various features they offer are only available once the package is
installed, which goes against the root ideas jbuilder is built on.
In practice, jbuilder interpret META
files assuming the following
set of predicates:
mt
: what this means is that using a library that can be used with or without threads with jbuilder will force the threaded versionmt_posix
: forces the use of posix threads rather than VM threads. VM threadws are deprecated and are likely to go away soonppx_driver
: when a library acts differently depending on whether it is linked as part of a driver or meant to add a-ppx
argument to the compiler, choose the former behavior
Cross Compilation¶
Jbuilder allows for cross compilation by defining build contexts with
multiple targets. Targets are specified by adding a targets
field
to the definition of a build context.
targets
takes a list of target name. It can be either:
native
which means using the native tools that can build binaries that run on the machine doing the build- the name of an alternative toolchain
Note that at the moment, there is no official support for cross-compilation in OCaml. Jbuilder supports the opam-cross-x repositories from the ocaml-cross organization on github, such as:
In particular:
- to build Windows binaries using opam-cross-windows, write
windows
in the list of targets - to build Android binaries using opam-cross-android, write
android
in the list of targets - to build Android binaries using opam-cross-ios, write
ios
in the list of targets
For example, the following workspace file defines three different
targets for the default
build context:
(context (default (targets (native windows android))))
This configuration defines three build contexts:
default
default.windows
default.android
Note that the native
target is always implicitly added when not
present. However, when implicitly added jbuilder build @install
will skip this context, i.e. default
will only be used for
building executables needed by the other contexts.
With such a setup, calling jbuilder build @install
will build all
the packages three times.
Note that instead of writing a jbuild-workspace
file, you can also
use the -x
command line option. Passing -x foo
to jbuilder
without having a jbuild-workspace
file is the same as writing the
following jbuild-workspace
file:
(context (default (targets (foo))))
If you have a jbuild-workspace
and pass a -x foo
option,
foo
will be added as target of all context stanzas.
How does it work?¶
In such a setup, binaries that need to be built and executed in the
default.windows
or default.android
contexts as part of the
build, will no longer be executed. Instead, all the binaries that will
be executed will come from the default
context. One consequence of
this is that all preprocessing (ppx or otherwise) will be done using
binaries built in the default
context.
To clarify this with an example, let’s assume that you have the
following src/jbuild
file:
(executable ((name foo)))
(rule (with-stdout-to blah (run ./foo.exe)))
When building _build/default/src/blah
, jbuilder will resolve ./foo.exe
to
_build/default/src/foo.exe
as expected. However, for
_build/default.windows/src/blah
jbuilder will resolve ./foo.exe
to
_build/default/src/foo.exe
Assuming that the right packages are installed or that your workspace has no external dependencies, jbuilder will be able to cross-compile a given package without doing anything special.
Some packages might still have to be updated to support cross-compilation. For
instance if the foo.exe
program in the previous example was using
Sys.os_type
, it should instead take it as a command line argument:
(rule (with-stdout-to blah (run ./foo.exe -os-type ${os_type})))
Classical ppx¶
classical ppx refers to running ppx using the -ppx compiler option, which is composed using Findlib. Even though this is useful to run some (usually old) ppx’s which don’t support drivers, Jbuilder does not support preprocessing with ppx this way. but a workaround exists using the ppxfind tool.