Tectonic + ninja = ♥: reproducible "augmented" document builds

You may not have heard of ninja before. It’s a low-level build tool that fills the same kind of role as the venerable Unix program make. But unlike make, ninja is super fast and well engineered, without the decade’s worth of strange cruft that make has. I can say personally that I now use ninja everywhere that I used to use make; it’s so much nicer!

Here’s the suggested way to tell ninja how to build a PDF from a TeX file using Tectonic. I won’t explain the syntax of this build.ninja file, here but I hope it’s pretty self-explanatory:

rule tectonic
  command = tectonic -c minimal --makefile-rules=$out.d $in
  description = TECTONIC $out
  depfile = $out.d
  deps = gcc

build example.pdf: tectonic example.tex | figure.png

The only tricky parts are:

  • We use -c minimal to make Tectonic be quiet except for warnings and so on
  • We use --makefile-rules to share Tectonic’s knowledge of which files were opened during the build with ninja. This way if you change a support file that’s \input, ninja will know to rerun the build even though you haven’t explicitly mentioned it in build.ninja.

I also have documents where I use a special tool to process LaTeX’s .aux file to construct the .bib file used by BibTeX on-the-fly. You can write Ninja rules that will go from source to final PDF with fully correct rebuilds like so:

rule tectonic_pass_1
  command = tectonic -c minimal --pass=tex --outfmt=aux --hide=$out --makefile-rules=$out.d $in
  description = TECTONIC $out
  depfile = $out.d
  deps = gcc

rule makebib
  command = ./my-makebib-program $in $out
  description = MAKEBIB $out

rule tectonic_pass_2
  command = tectonic -c minimal --pass=bibtex_first --makefile-rules=$out.d $in
  description = TECTONIC $out
  depfile = $out.d
  deps = gcc

build example.aux: tectonic_pass_1 example.tex | figure.png

build example.bib: makebib example.aux

build example.pdf: tectonic_pass_2 example.tex | example.aux example.bib figure.png

This is definitely hairer:

  • The first pass just runs one TeX pass (--pass=tex) and only writes the .aux file output (--outfmt=aux). If the file example.aux exists, Tectonics pretends that it doesn’t (--hide=example.aux) — this is essential to avoid creating a circular dependency when you rebuild the document.
  • Then the makebib tool (not shown here) uses example.aux to make example.bib
  • The second pass starts right off with BibTeX (--pass=bibtex_first) because the needed example.aux and example.bib have been created. It then does whatever needs to be done to finish the compilation.

It may seem a bit messy, but I think it’s really neat to just be able to type ninja and have the full document get built correctly every single time! And, of course, you can integrate the document build into a larger project — for instance, for my scientific papers I have rules that generate all of the figures from my data files.