Automated builds

“Rerun to get cross-references right.” LaTeX is one of the few typesetting systems where running the compiler once is not enough to get the right answer. Cross-references, the table of contents and citations are only written out to files on the first pass, so an automated build tool such as latexmk runs the round trips for you until the output settles. This page starts with why several compilation runs are needed at all, then works through latexmk -pdf, the -pvc mode that rebuilds every time you save, the -c and -C cleanups, the latexmkrc configuration file, and the alternatives — arara, llmk and plain make.

Why LaTeX needs more than one compilation run

The answer is simple: LaTeX reads a document once, from front to back. When it sets the table of contents on page one, it does not yet know what page section 7 will land on. So it writes down what it learns as it goes — each label's section and page number, the contents lines, the citation keys — into auxiliary files such as .aux, .toc, .lof and .lot, and reads them back at the start of the next run. The output is therefore always assembled out of what the previous run discovered. That is exactly why the first PDF has an empty table of contents and shows ?? where the references should be.

There is a neat trick hidden here: LaTeX does not count how many passes are left. At \end{document} it compares the value it just computed for each label against the value it read from the previous run's .aux, one by one, and if a single one differs it prints LaTeX Warning: Label(s) may have changed. Rerun to get cross-references right. So the warning disappearing is the signal that the .aux file has stopped changing — that the document has reached a fixed point. Whether a document is finished is decided not by how the pages look but by whether those auxiliary files agree.

text
% doc.aux -- what one run leaves behind for the next one to read
\@writefile{toc}{\contentsline {section}{\numberline {1}One}{1}{}}
\newlabel{sec:one}{{1}{1}{}{}{}}

% doc.log -- the first run, before the .aux settles
LaTeX Warning: Reference `sec:two' on page 1 undefined on input line 5.
LaTeX Warning: There were undefined references.
LaTeX Warning: Label(s) may have changed. Rerun to get cross-references right.

Adding a bibliography lengthens the round trip further. The keys requested by \cite are recorded in the .aux on the first run; bibtex or biber reads that file and produces a .bbl; the second run pulls the .bbl in; and a third is needed to fix references whose numbers shifted. That is the whole story behind the famous incantation latex → bibtex → latex → latex. An index inserts makeindex into the same chain. Done by hand, this means judging afresh every single time how far back to go.

latexmk: one command that runs the loop for you

You type one line: latexmk -pdf document.tex. From there latexmk watches the .aux file change, runs pdflatex as many times as it takes, calls bibtex/biber and makeindex in the right order along the way, and stops once the warnings are gone. The tool has an unusual pedigree: it began as a small script called go, written by David J. Musliner. Evan McLean reworked it into latexmk, and it has since been maintained in Perl by John Collins, a physicist at Penn State University — on a TeX Live 2024 installation, latexmk -v answers “Latexmk, John Collins, 31 Jan. 2024. Version 4.83.” It ships with both TeX Live and MiKTeX, so there is normally nothing to install.

terminal
$ latexmk -pdf doc.tex
Latexmk: applying rule 'pdflatex'...
Run number 1 of rule 'pdflatex'
Running 'pdflatex  -recorder  "doc.tex"'
Latexmk: References changed.
Latexmk: applying rule 'pdflatex'...
Run number 2 of rule 'pdflatex'
Running 'pdflatex  -recorder  "doc.tex"'
Latexmk: All targets (doc.pdf) are up-to-date

The -recorder in that output is latexmk's own addition. With that option the TeX engine writes out a .fls file listing every file the run read and wrote; latexmk cross-checks it against the log to work out the dependencies, and keeps the state of each file in a database called .fdb_latexmk. The important part is the criterion: latexmk compares checksums of file contents, not modification times. Its manual spells out why. A file written during a run of LaTeX is always later than the file that was read in before it, so by timestamps alone it looks perpetually out of date. That circular dependency, the manual says, is endemic to LaTeX, and latexmk was programmed to overcome it. There is a safety net too: if the document has not settled after $max_repeat runs — five by default — latexmk assumes an infinite loop and bails out.

-pdf, -lualatex, -xelatex: choosing the engine

-pdf selects pdflatex, -lualatex selects lualatex and -xelatex selects xelatex. Left alone, latexmk still does what its earliest versions did and produces a .dvi, so one of these is required whenever a PDF is what you want. There is a detail here worth knowing: even with -xelatex, latexmk does not let xelatex write the PDF directly. It makes an intermediate .xdv file, finishes all the reruns on that, and only then calls xdvipdfmx once. With large .png graphics the PDF stage is slow, so this avoids re-embedding every image on every pass. -lualatex is shorthand for -pdflua -dvi- -ps-, and -xelatex for -pdfxe -dvi- -ps-. For a route that goes through DVI, such as the Japanese upLaTeX + dvipdfmx pairing, choose -pdfdvi.

OptionWhat it doesWhen to use it
-pdfbuilds the PDF with pdflatexthe standard choice for Latin-script documents
-lualatexbuilds the PDF with lualatex (same as -pdflua -dvi- -ps-)OpenType fonts, or extensions written in Lua
-xelatexruns xelatex to an .xdv, then calls xdvipdfmx at the endwhen system fonts are used directly
-pdfdvimakes a .dvi first and converts it to PDFDVI routes such as upLaTeX + dvipdfmx
-pvcwatches the sources and rebuilds on every changewhile writing, to see the result on every save
-pvctimeoutends -pvc after a spell of inactivity (30 minutes by default)when the watcher should not be left running unattended
-cremoves the regeneratable intermediates, keeping the PDFtidying up a working directory
-Cdoes -c and also deletes the .dvi, .ps and .pdfproving a clean build; preparing a release
-ggcleans as -C would, then does a normal builda from-scratch rebuild in one command
-fkeeps processing despite errorswhen you want all the log output in one go
-silentquietens the engine output (same as -quiet)keeping CI logs readable
-rreads an additional named configuration filebuilding through a different route just this once

latexmk -pvc: rebuild every time you save

-pvc stands for “preview continuously”: latexmk stays resident with a viewer open and runs the whole loop again whenever any source file changes. What it watches is not just the main .tex. The dependency list it built from .fls becomes the watch list, so chapter files pulled in with \input/\include, embedded graphics and the .bib file are all covered. It feels like a dev server for a document. A few quirks come with it. -pvc works with only one file, and it is incompatible with -p and -pv. It also switches force mode -f off, so if you really want both you must write them in the order -pvc -f. Left alone it never gives up by itself; -pvctimeout adds an inactivity timeout whose period is 30 minutes unless -pvctimeoutmins= says otherwise, and -pvctimeout- switches it back off. The viewer matters too: the manual explicitly warns that acroread on MS-Windows locks the PDF file and prevents new versions being written, so it is a bad choice for continuous preview.

terminal
latexmk -pdf -pvc doc.tex                 # watch the sources, rebuild on every save
latexmk -pdf -pvc -pvctimeout doc.tex     # same, but give up after 30 idle minutes
latexmk -lualatex -pvc doc.tex            # the same loop, driven by lualatex

The “build on save” button in an editor is usually latexmk underneath. LaTeX Workshop for VS Code, TeXstudio, TeXShop, AUCTeX in Emacs, Overleaf — the names differ, but what runs is either the same command or a built-in implementation of the same idea. Knowing -pvc at the terminal therefore gives you somewhere to fall back to: drop to the bare command when the editor misbehaves, and you can tell whether the document or the configuration is at fault. If only the editor's build fails while plain latexmk succeeds, the suspect is the editor's settings, not the document.

latexmk -c vs -C: cleaning up the generated files

The difference is one thing: whether the PDF survives. -c removes the regeneratable files — .aux, .log, .toc, .fls, .fdb_latexmk and friends — but keeps the .dvi, .ps and .pdf. -C removes those outputs as well. To clean and rebuild in a single step, use -gg. This matters in practice because a stale .aux hides accidents. Reorder some sections or delete a \label, and the PDF on your own machine still comes out looking plausible, because the old values are still lying around — while a co-author who has just cloned the repository, or CI, gets a broken build. Running latexmk -C and then having latexmk -pdf succeed before submission is the proof that the document really can be built from its sources alone.

terminal
latexmk -c                  # remove aux, log, toc, fls, fdb_latexmk ... keep the PDF
latexmk -C                  # remove all of that plus the dvi / ps / pdf output
latexmk -gg -pdf doc.tex    # clean first, then build again from scratch

Putting the build into a latexmkrc file

Put a file named latexmkrc or .latexmkrc next to the document and everyone who types latexmk in that directory takes the same route. At startup latexmk reads, in order: the system-wide file, then the user's $HOME/.latexmkrc (or $XDG_CONFIG_HOME/latexmk/latexmkrc), then latexmkrc or .latexmkrc in the current directory, then anything named with -r. Later files win, so the project's settings override individual preferences. The contents are Perl: # starts a comment, and for most purposes a handful of variable assignments is all it takes. In collaborative work, committing this file and treating it as the agreement — “this document is built like this” — causes the fewest arguments.

perl
# latexmkrc -- lives next to the document and is committed with it

$pdf_mode = 4;           # 4 = build the PDF with lualatex
$max_repeat = 7;         # allow a couple of extra passes on a long document

# Alternative route: upLaTeX -> DVI -> dvipdfmx
# $latex    = 'uplatex -interaction=nonstopmode -halt-on-error %O %S';
# $dvipdf   = 'dvipdfmx %O -o %D %S';
# $pdf_mode = 3;         # 3 = make the PDF from the DVI file

# Extra extensions that -c and -C should remove as well.
$clean_ext = 'synctex.gz run.xml bcf';

Alternatives to latexmk: arara, llmk, make

The dividing line is one question: who decides the sequence of steps. latexmk infers it from logs and dependencies. arara infers nothing. It reads directives written into the document — a comment line such as % arara: pdflatex — and runs exactly what is written, in the order it is written. As its CTAN entry puts it, arara determines its actions from metadata in the source code rather than from indirect resources such as log file analysis. It is developed by Island of TeX around Paulo Roberto Massa Cereda, and it needs Java to run. llmk (packaged in TeX Live as light-latex-make, written by Takuto Asakura) is more declarative still: the workflow goes into llmk.toml or a TOML field in the source, and it runs on texlua alone — the design puts identical behaviour in every environment first.

latex
% arara directives: the document itself states the workflow
% arara: pdflatex
% arara: biber
% arara: pdflatex
% arara: pdflatex
\documentclass{article}
toml
# llmk.toml -- next to the document; "source" is required in this file
source = "doc.tex"
latex = "lualatex"
bibtex = "biber"
sequence = ["latex", "bibtex", "latex", "latex"]

What about plain make? A Makefile can certainly drive LaTeX, but make decides by modification time. Since the .aux file is rewritten on every run, timestamps alone always place it after the file that was read in — and therefore perpetually out of date. That is exactly the point on which latexmk's own manual says the circular dependency is endemic to LaTeX and that latexmk was programmed to overcome it. If you do use make anyway, the workable patterns are to keep a copy of the .aux and compare it, or simply to call latexmk from the Makefile target. In practice a great many project Makefiles come down to one line: latexmk -pdf $<.

ToolHow the steps are decidedWhere the configuration livesRequires
latexmkinferred from the log, the .fls and content checksumslatexmkrc / .latexmkrc (Perl)Perl; ships with TeX Live and MiKTeX
araraexecuted exactly as the directives in the document say% arara: comments in the documentJava
llmkfollows the sequence declared in TOMLllmk.toml, or a TOML field in the sourcetexlua only
makedecided by modification times; weak against the .aux cycleMakefilemake; already present nearly everywhere

Which command to use while writing, sharing and submitting

The choice comes down to three moments. While writing, watch with -pvc and look at the result on every save. Before handing the document to anyone, run plain latexmk once. Just before submission, wipe everything with latexmk -C and build again. Making that last step a habit is what prevents the classic accident of discovering, at the deadline, that the document only compiles on your own machine. And once the settings are pinned down in latexmkrc and committed, the CI server and every co-author follow the same route, so the “it works on my machine” argument tends not to arise at all.

  • While writinglatexmk -pdf -pvc doc.tex: rebuilds automatically on every save; add -pvctimeout if it should not be left running unattended.
  • Pin the engine → set $pdf_mode and friends in latexmkrc, and commit it so everyone shares it.
  • Before handing it to a co-author → run plain latexmk -pdf once and check that no LaTeX Warning: Label(s) may have changed. is left.
  • Just before submission or releaselatexmk -C to wipe everything, then a clean build; latexmk -gg -pdf doc.tex does both at once.
  • Building on a server or in CI → see the CI page; adding -silent keeps the logs readable.