My LaTeX build setup

Table of Contents

Over the years I’ve had to wrestle with the \(\LaTeX\) typesetting system a lot. Along the way, I’ve tried to automate and refine the build process as much as possible, while including plots created with matplotlib, pgf, etc. In this note, I attempt to describe this system in the hope that it will be useful to others.

The tool that I use to track non-TeX file dependencies is none other than the venerable GNU Make. If you are familiar with Makefiles and just want to see the the final product, here it is:

main := <name of your document>

all: $(main).pdf

plots := $(patsubst python_scripts/plots/,\
                   $(wildcard python_scripts/plots/*.py))

texfiles := $(patsubst python_scripts/texfiles/,\
                  $(wildcard python_scripts/texfiles/*.py))

data := $(wildcard data/*)

build/plots/%.pdf: python_scripts/plots/ $(data) 
	mkdir -p build/plots
	python $< $@

build/texfiles/%.tex: python_scripts/texfiles/ $(data)
	mkdir -p build/texfiles
	python $< $@

$(main).pdf: FORCE $(plots) $(texfiles) $(wildcard images/*)
	time ./latexrun $(main).tex

In the following sections, I’ll describe the directory structure, and the logic behind the design of the Makefile.

Directory structure

This is what the directory structure of a typical TeX project of mine might look like:

├── Makefile
├── main.tex
├── bibliography.bib
├── images
├── data
│   └── data.tsv
├── latexrun
└── python_scripts
    ├── plots
    │   └──
    └── texfiles

Now that we have gone over the directory structure, let us look at the structure of the Makefile.

Design of the Makefile

Let us go through the Makefile, section by section. The basic syntax for a Makefile rule looks like this:

target: dependency1 dependency2 ...
    recipe for building target

Let us take a look at the first line:

all: main.pdf

The target all represents the default target of the Makefile, that is, what gets built if we just invoke make without any arguments. In this case, we have specified the default target to be main.pdf.

Next, we define the Makefile variables plots and texfiles, defined below.

To construct these variables, we use wildcard to gather the names of the python scripts, and substituting either .pdf or .tex for the suffixes, using patsubst.

plots := $(patsubst python_scripts/plots/,\
                   $(wildcard python_scripts/plots/*.py))

texfiles := $(patsubst python_scripts/texfiles/,\
                  $(wildcard python_scripts/texfiles/*.py))

We then specify the rules to build plots and texfiles. We specify the contents of the data directory as dependencies, so that the plots and tables will be rebuilt when the data changes.

build/plots/%.pdf: python_scripts/plots/ $(wildcard data/*)
	mkdir -p build/plots
	python $< $@

build/texfiles/%.tex: python_scripts/texfiles/ $(wildcard data/*)
	mkdir -p build/texfiles
	python $< $@

Note that the rules includes making the directories build, build/plots, and build/texfiles, if they do not already exist. The last line of each rule makes use of the following automatic variables:

So, the line python $< $@ translates to commands like:

python python_scripts/plots/ build/plots/plotName.pdf
python python_scripts/texfiles/ build/texfiles/tableName.tex

Each Python script should accept one command line argument, which is the name of the output file that it produces.

Finally, we specify how to build the PDF.

$(main).pdf: FORCE $(plots) $(texfiles) ($wildcard images/*)
	time ./latexrun $(main).tex

The target $(main).pdf depends on the the following.

Note however, that the first dependency is FORCE, and we also have a .PHONY target above it. This is because the latexrun script does its own dependency tracking for TeX files, and does it in a more automated fashion than make. For example, if I use a custom LaTeX class file myclass.cls, and I change that file, Make will not detect that the $(main).tex file depends upon the class file, but latexrun will. The rule for building $(main).pdf involves calling the latexrun script, (which I include in each project directory, so that my collaborators will not have to hunt it down).


I like using TikZ and PGF to generate diagrams that match the native look and feel of the document. To speed up document compilation when you’re using PGF and TikZ and you have a lot of diagrams, you should use the external library. To integrate with this build system, we specify the build directory name to be the same as the one we used in the Makefile. The code snippet below is what I put in my preamble to make this work.

\immediate\write18{mkdir -p build}


I hope you find this build system useful, especially for longer TeX documents. I expect that I will continue to refine it even further in the future - and I will update this page when that happens.