• en

A plugin example for compiling LaTeX

Besides the example below, another plugin for LaTeX can be found here it handles pdfLaTeX, MakeIndex, HeVeA (both monolithic html and html by chapter via HaChA) and Pgf/TikZ picture extraction to PNG for the html versions. However as the plugin below it assumes that LaTeX will converge in two iterations for the PDF and the master tex file chapter dependencies must be explicitly stated.

open Ocamlbuild_plugin
open Command

let pdflatex = ref (A"pdflatex")
let ocamlweb = ref (A"ocamlweb")

let () =
  dispatch begin function
  
    (* Here one can change the default value of options, they can still
       be updated by a command line option. *)
    | Before_options ->
       (* This will put all warnings to \texttt{ocaml\{c,opt\}} by default. *)
       Options.ocaml_cflags := ["-w";"A"]
  
    (* Here one can change the final value of options. *)
    | After_options ->
       (* This avoids the creation of symbolic links to the build directory. *)
       Options.make_links := false
  
    (* This hook is called before the hygiene phase.
       This phase also serve as collecting all the information about the
       source tree. *)
    | Before_hygiene ->
  
        (* Here you can dynamically tag some files or directories. *)
        (* This is done here by checking the [SOME_COND] variable which is
           impossible in the \tags file. *)
        if getenv "SOME_COND" ~default:"false" = "true" then
  
          (* By setting foo_dir as not_hygienic one say that the foo directory
             can contains non hygienic files (such as \texttt{.cmi},
             \texttt{.cmo}\ldots). *)
          tag_file "foo_dir" ["not_hygienic"]
  
    (* One can also do things after the hygiene step. *)
    | After_hygiene -> ()
  
    (* One can setup rules before the standard ones but that's not recommended. *)
    | Before_rules -> ()
  
    (* Here one can add or override new rules *)
    | After_rules ->
        (* Rules can be declared by a call of the form
            [rule name ~prod ~dep action].
            The first argument is the name of the rule.
            [~prod:string] specifies the product of the rule.
            Note that [~prods:string list] also exists.
            [~dep] and [~deps] are for dependencies *)
        rule "LaTeX to PDF conversion rule"
          ~prod:"%.pdf"
          ~dep:"%.tex"
          begin fun env _build ->
  
            (* The action is a function that receive two arguments:
            [env] is a conversion function that substitutes `%'
            occurrences according to the targets to which the rule
            applies.  [_build] can be called to build new things
            (dynamic dependencies). *)
            let tex = env "%.tex" and _pdf = env "%.pdf" in
  
            (* Here we say: ``We compile the file tex form LaTeX to
               PDF''.  Note that in fact that is a set of tags, thus
               the order does not matter. But you got the idea. *)
            let tags = tags_of_pathname tex++"compile"++"LaTeX"++"pdf" in
  
            (* Here we produce the command to run.
               [S]  is for giving a sequence of command pieces.
               [A]  is for atoms.
               [P]  is for pathnames.
               [Px] is a special pathname that should be the main product
                    of the rule (for display purposes).
               [T]  is for tags.
  
               The other constructors are given in the documentation of the
               [Command] module in [Signatures.COMMAND]. *)
            let cmd = Cmd(S[!pdflatex; T tags; P tex; Sh"< /dev/null"]) in
            (* Hoping that LaTeX will converge in two iterations *)
            Seq[cmd; cmd]
          end;
  
        (* Here we make an extension of any rule that produces a command
           containing these tags. *)
        flag ["compile"; "LaTeX"; "pdf"; "safe"] (A"-halt-on-error");
  
        (* Here we give an exception: the file ``manual.tex'' is tagged
           ``safe''.  With this tag we add the -halt-on-error flag during
           the LaTeX compilation. *)
        tag_file "manual.tex" ["safe"];
  
        (* The generic LaTeX rule could look at the file searching for
           some \verb'\input{}' command, but LaTeX is so complex that it
           will be hard to make this solution complete.  Here we manually
           inject some dependencies at one particular point. *)
  
        (* The [dep] function takes tags and pathnames. This will build
           pathnames if a command contains these tags. Note that every
           file [some_file_name] is tagged [file:some_file_name]. *)
        dep ["compile"; "LaTeX"; "pdf"; "file:manual.tex"]
            ["ocamlweb.sty"; "myocamlbuild.tex"];
  
        rule "OCaml to LaTeX conversion rule (using ocamlweb)"
          ~prod:"%.tex"
          ~dep:"%.ml"
          begin fun env _build ->
            let tex = env "%.tex" and ml = env "%.ml" in
            let tags = tags_of_pathname ml++"ocamlweb"++"LaTeX" in
            Cmd(S[!ocamlweb; T tags; P ml; A"-o"; Px tex])
          end;
  end

Notes

  • The (relative and absolute) dependencies of a tex file can be found by running latex with -recorder and parse the resulting .fls file. Unfortunately as this does execute the tex code this takes too much time to sort out the deps.