Preprocessing Specification

Some stanzas including (library) accept a (preprocessing) field. The possible values for its argument are:

pp-spec    ::=  <pp-module>
                (per-module <per-module>+)
per-module ::=  ( <pp-module> <module>+ )
pp-module  ::=  no_preprocessing
                (action <action>)
                (pps <ppx-rewriters-and-flags>)
                (staged_pps <ppx-rewriters-and-flags>)
                future_syntax

no_preprocessing

When no_preprocessing is passed, files are given as-is to the compiler. This is the default behavior.

Preprocessing With Actions

In (action <action>), <action> uses the same DSL as described in Actions, and for the same reason given in that section, it will be executed from the root of the current build context. It’s expected to be an action that reads the file given as a dependency named input-file and outputs the preprocessed file on its standard output.

More precisely, (preprocess (action <action>)) acts as if you had set up a rule for every file of the form:

(rule
 (target file.pp.ml)
 (deps file.ml)
 (action
  (with-stdout-to %{target}
   (chdir %{workspace_root} <action>))))

The equivalent of a -pp <command> option passed to the OCaml compiler is (system "<command> %{input-file}").

Using PPX Rewriters

If (pps <ppx-rewriters-and-flags>) is used, the corresponding rewriters are set up using the “fast pipeline” (using a separate preprocessing step). If (staged_pps <ppx-rewriters-and-flags>) is used, they are set up using the “classic pipeline” (using the -ppx command-line argument).

The distinction between these pipelines is explained in How Preprocessing Works.

PPX rewriters need to be compiled as a driver to be used by Dune. To run PPXs that do not support this (usually old ones), it is possible to use the ppxfind tool.

Arguments to PPX Rewriters

In (pps <ppx-rewriters-and-flags>) and (staged_pps <ppx-rewriters-and-flags>), <ppx-rewriters-and-flags> is a sequence where each element is either a command line flag if it starts with a - or the name of a library.

If you want to pass command line flags that don’t start with a -, you can separate library names from flags using --. So for instance from the following preprocess field:

(preprocess (pps ppx1 -foo ppx2 -- -bar 42))

The list of libraries will be ppx1 and ppx2, and the command line arguments will be: -foo -bar 42.

Future Syntax

The future_syntax specification is a special value that brings some of the newer OCaml syntaxes to older compilers.

It is equivalent to no_preprocessing when using one of the most recent versions of the compiler. When using an older one, it is a shim preprocessor that backports some of the newer syntax elements. This allows you to use some of the new OCaml features while keeping compatibility with older compilers.

One example of supported syntax is the custom let-syntax that was introduced in 4.08, allowing the user to define custom let operators.

Note that this feature is implemented by the third-party ocaml-syntax-shims project, so if you use this feature, you must also declare a dependency on this package.

Per-Module Preprocessing Specification

By default, a preprocessing specification applies to all modules in the library/set of executables. It’s possible to select the preprocessing on a module-by-module basis by using the (per-module ...) syntax. For instance:

(preprocess
 (per_module
  ((action (run ./pp.sh X=1 %{input-file})) foo bar)
  ((action (run ./pp.sh X=2 %{input-file})) baz)))

The modules Foo and Bar will be preprocessed with pp.sh X=1, and Baz will be preprocessed with pp.sh X=2.

Preprocessor Dependencies

If your preprocessor needs extra dependencies, you should use the preprocessor_deps field available in the library, executable, and executables stanzas. It uses the Dependency Specification to declare what the preprocessor needs.