Automatisierte Builds

„Rerun to get cross-references right.“ LaTeX gehört zu den wenigen Satzsystemen, bei denen ein einziger Durchlauf des Compilers noch nicht das richtige Ergebnis liefert. Querverweise, Inhaltsverzeichnis und Zitate werden im ersten Durchlauf nur in Dateien geschrieben; deshalb übernimmt ein automatisierter Build mit einem Werkzeug wie latexmk die Wiederholungen, bis sich die Ausgabe nicht mehr ändert. Diese Seite beginnt bei der Frage, warum überhaupt mehrere Läufe nötig sind, und geht dann latexmk -pdf durch, den Modus -pvc, der bei jedem Speichern neu baut, die Aufräumoptionen -c und -C, die Konfigurationsdatei latexmkrc sowie die Alternativen arara, llmk und make.

Warum LaTeX mehrere Kompilierläufe braucht

Die Antwort ist einfach: LaTeX liest ein Dokument genau einmal, von vorn nach hinten. Wenn auf Seite eins das Inhaltsverzeichnis gesetzt wird, steht noch nicht fest, auf welcher Seite Abschnitt 7 landet. Also schreibt LaTeX alles, was es unterwegs erfährt – Abschnitts- und Seitennummer jeder Marke, die Verzeichniszeilen, die Zitierschlüssel –, in Hilfsdateien wie .aux, .toc, .lof und .lot und liest sie zu Beginn des nächsten Laufs wieder ein. Die Ausgabe entsteht damit immer aus dem, was der vorherige Lauf herausgefunden hat. Genau deshalb hat das erste PDF ein leeres Inhaltsverzeichnis und zeigt ?? an den Stellen der Verweise.

Darin steckt ein hübscher Kniff: LaTeX zählt nicht, wie viele Durchläufe noch fehlen. Bei \end{document} vergleicht es den soeben berechneten Wert jeder Marke mit dem Wert, den es aus der .aux-Datei des vorherigen Laufs gelesen hat, Stück für Stück; weicht auch nur einer ab, erscheint LaTeX Warning: Label(s) may have changed. Rerun to get cross-references right. Umgekehrt heißt das: Verschwindet die Warnung, hat sich die .aux-Datei nicht mehr geändert – das Dokument hat einen Fixpunkt erreicht. Ob ein Dokument fertig ist, entscheidet nicht das Aussehen der Seiten, sondern die Übereinstimmung dieser Hilfsdateien.

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.

Eine Bibliografie verlängert den Hin- und Rückweg zusätzlich. Die von \cite angeforderten Schlüssel landen im ersten Lauf in der .aux-Datei; bibtex oder biber liest sie und erzeugt eine .bbl; der zweite Lauf bindet die .bbl ein; ein dritter korrigiert Verweise, deren Nummern sich verschoben haben. Das ist die ganze Geschichte hinter der bekannten Formel latex → bibtex → latex → latex. Ein Index schiebt zusätzlich makeindex in dieselbe Kette. Von Hand bedeutet das, jedes Mal aufs Neue zu beurteilen, wie weit zurückgegangen werden muss.

latexmk: ein Befehl, der die ganze Schleife übernimmt

Zu tippen ist eine einzige Zeile: latexmk -pdf document.tex. Danach beobachtet latexmk die Änderungen der .aux-Datei, ruft pdflatex so oft auf wie nötig, schiebt bibtex/biber und makeindex in der richtigen Reihenfolge ein und hört auf, sobald die Warnungen verschwunden sind. Das Werkzeug hat eine ungewöhnliche Herkunft: Angefangen hat es als kleines Skript namens go von David J. Musliner. Evan McLean baute daraus latexmk, und seither pflegt John Collins, Physiker an der Penn State University, das Perl-Programm – unter TeX Live 2024 antwortet latexmk -v mit „Latexmk, John Collins, 31 Jan. 2024. Version 4.83.“ Da es sowohl bei TeX Live als auch bei MiKTeX dabei ist, muss in der Regel nichts nachinstalliert werden.

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

Das -recorder in dieser Ausgabe fügt latexmk selbst hinzu. Mit dieser Option schreibt die TeX-Engine eine .fls-Datei mit allen Dateien, die der Lauf gelesen und geschrieben hat; latexmk gleicht sie mit dem Log ab, ermittelt daraus die Abhängigkeiten und hält den Zustand jeder Datei in einer Datenbank namens .fdb_latexmk fest. Entscheidend ist das Kriterium: latexmk vergleicht Prüfsummen der Dateiinhalte, nicht Änderungszeiten. Das Handbuch nennt den Grund unmissverständlich. Eine während eines LaTeX-Laufs geschriebene Datei ist stets jünger als die zuvor eingelesene, wirkt also nach Zeitstempeln dauerhaft veraltet. Diese zirkuläre Abhängigkeit sei LaTeX eigen, heißt es dort, und latexmk sei genau dafür geschrieben worden, sie zu überwinden. Auch eine Reißleine gibt es: Hat sich das Dokument nach $max_repeat Läufen – standardmäßig fünf – nicht stabilisiert, nimmt latexmk eine Endlosschleife an und bricht ab.

-pdf, -lualatex, -xelatex: die Engine wählen

-pdf wählt pdflatex, -lualatex wählt lualatex, -xelatex wählt xelatex. Ohne Angabe verhält sich latexmk wie seine frühesten Versionen und erzeugt ein .dvi; wer ein PDF will, muss also eine dieser Optionen setzen. Ein Detail lohnt hier die Aufmerksamkeit: Selbst mit -xelatex lässt latexmk xelatex das PDF nicht direkt schreiben. Es erzeugt zunächst eine Zwischendatei .xdv, erledigt darauf alle Wiederholungsläufe und ruft erst danach einmal xdvipdfmx auf. Bei großen .png-Grafiken ist die PDF-Erzeugung langsam – so müssen die Bilder nicht bei jedem Durchlauf neu eingebettet werden. -lualatex ist die Kurzform von -pdflua -dvi- -ps-, -xelatex die von -pdfxe -dvi- -ps-. Für einen Weg über DVI, etwa das japanische Gespann upLaTeX + dvipdfmx, dient -pdfdvi.

OptionWirkungEinsatz
-pdferzeugt das PDF mit pdflatexStandardfall für Dokumente in lateinischer Schrift
-lualatexerzeugt das PDF mit lualatex (wie -pdflua -dvi- -ps-)OpenType-Schriften oder Erweiterungen in Lua
-xelatexlässt xelatex ein .xdv erzeugen und ruft am Ende xdvipdfmx aufwenn Systemschriften direkt verwendet werden
-pdfdvierzeugt zuerst ein .dvi und wandelt es in PDF umDVI-Wege wie upLaTeX + dvipdfmx
-pvcüberwacht die Quellen und baut bei jeder Änderung neubeim Schreiben, um nach jedem Speichern das Ergebnis zu sehen
-pvctimeoutbeendet -pvc nach einer Phase der Untätigkeit (standardmäßig 30 Minuten)wenn die Überwachung nicht unbeaufsichtigt weiterlaufen soll
-centfernt die neu erzeugbaren Zwischendateien, behält das PDFzum Aufräumen des Arbeitsverzeichnisses
-Cmacht -c und löscht zusätzlich .dvi, .ps und .pdfNachweis eines Clean Builds; Vorbereitung einer Auslieferung
-ggräumt wie -C auf und baut anschließend normalNeubau von Grund auf in einem Befehl
-fsetzt die Verarbeitung trotz Fehlern fortwenn die gesamte Log-Ausgabe auf einmal gebraucht wird
-silentdämpft die Ausgabe der Engine (wie -quiet)um CI-Logs lesbar zu halten
-rliest zusätzlich eine angegebene Konfigurationsdateiwenn einmalig über einen anderen Weg gebaut wird

Bei jedem Speichern neu bauen – latexmk -pvc

-pvc steht für „preview continuously“: latexmk bleibt mit geöffnetem Betrachter im Speicher und lässt die gesamte Schleife erneut laufen, sobald sich irgendeine Quelldatei ändert. Überwacht wird nicht nur die Haupt-.tex. Die aus der .fls gewonnene Abhängigkeitsliste wird zur Beobachtungsliste, also gehören auch mit \input/\include eingebundene Kapiteldateien, eingebettete Grafiken und die .bib-Datei dazu. Das Gefühl entspricht einem Dev-Server für ein Dokument. Ein paar Eigenheiten kommen mit: -pvc funktioniert nur mit einer einzigen Datei und verträgt sich nicht mit -p und -pv. Außerdem schaltet der Modus den Force-Modus -f ab; wer beides braucht, schreibt sie in der Reihenfolge -pvc -f. Von sich aus endet der Lauf nicht; erst -pvctimeout fügt eine Zeitsperre bei Untätigkeit hinzu, deren Dauer standardmäßig 30 Minuten beträgt (-pvctimeoutmins= ändert sie, -pvctimeout- schaltet sie wieder ab). Auch der Betrachter zählt: Das Handbuch warnt ausdrücklich davor, unter MS-Windows acroread zu verwenden, weil es die PDF-Datei sperrt und neue Versionen nicht geschrieben werden können.

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

Die Schaltfläche „beim Speichern bauen“ in einem Editor ist meist latexmk unter der Haube. LaTeX Workshop für VS Code, TeXstudio, TeXShop, AUCTeX in Emacs, Overleaf – die Namen unterscheiden sich, ausgeführt wird entweder derselbe Befehl oder eine eingebaute Umsetzung derselben Idee. Wer -pvc auf der Kommandozeile kennt, hat deshalb einen Rückzugsort: Spinnt der Editor, klärt der nackte Befehl, ob das Dokument oder die Konfiguration schuld ist. Scheitert nur der Build im Editor, während latexmk durchläuft, liegt der Verdacht auf den Editor-Einstellungen, nicht auf dem Dokument.

latexmk -c und -C: erzeugte Dateien aufräumen

Der Unterschied besteht in einem Punkt: ob das PDF erhalten bleibt. -c entfernt die neu erzeugbaren Dateien – .aux, .log, .toc, .fls, .fdb_latexmk und dergleichen –, behält aber .dvi, .ps und .pdf. -C löscht diese Ausgaben ebenfalls. Wer aufräumen und in einem Zug neu bauen will, nimmt -gg. Praktisch wichtig ist das, weil eine veraltete .aux Unfälle verdeckt. Ein paar Abschnitte umgestellt, ein \label gelöscht – und das PDF auf dem eigenen Rechner sieht weiterhin plausibel aus, weil die alten Werte noch herumliegen, während eine Mitautorin mit frisch geklontem Repository oder die CI einen kaputten Build bekommt. latexmk -C auszuführen und danach latexmk -pdf durchlaufen zu sehen, ist vor der Abgabe der Beweis, dass sich das Dokument tatsächlich allein aus seinen Quellen bauen lässt.

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

Den Build in eine latexmkrc-Datei schreiben

Liegt neben dem Dokument eine Datei namens latexmkrc oder .latexmkrc, nimmt jeder, der in diesem Verzeichnis latexmk eintippt, denselben Weg. Beim Start liest latexmk der Reihe nach: die systemweite Datei, dann $HOME/.latexmkrc (oder $XDG_CONFIG_HOME/latexmk/latexmkrc), dann latexmkrc oder .latexmkrc im aktuellen Verzeichnis, dann alles, was mit -r angegeben wurde. Später Gelesenes gewinnt, die Projekteinstellungen überschreiben also persönliche Vorlieben. Der Inhalt ist Perl-Code, # leitet einen Kommentar ein, und meist genügen ein paar Zuweisungen an Variablen. Bei gemeinsamer Arbeit sorgt es für den geringsten Streit, diese Datei ins Repository zu legen und sie als Abmachung zu behandeln: „So wird dieses Dokument gebaut.“

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';

Alternativen zu latexmk: arara, llmk, make

Die Trennlinie verläuft an einer Frage: Wer legt die Schrittfolge fest? latexmk erschließt sie aus Logs und Abhängigkeiten. arara erschließt gar nichts. Es liest im Dokument notierte Direktiven – eine Kommentarzeile wie % arara: pdflatex – und führt genau das aus, was dort steht, in der dort angegebenen Reihenfolge. Wie der CTAN-Eintrag formuliert, bestimmt arara seine Aktionen aus Metadaten im Quelltext statt aus indirekten Quellen wie einer Logdatei-Analyse. Entwickelt wird es von Island of TeX um Paulo Roberto Massa Cereda; zum Ausführen ist Java nötig. llmk (in TeX Live als light-latex-make paketiert, geschrieben von Takuto Asakura) ist noch deklarativer: Der Ablauf steht in llmk.toml oder in einem TOML-Feld der Quelle, und es läuft allein mit texlua – der Entwurf stellt gleiches Verhalten in jeder Umgebung an die erste Stelle.

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"]

Und schlichtes make? Ein Makefile kann LaTeX durchaus steuern, doch make entscheidet nach Änderungszeit. Da die .aux-Datei bei jedem Lauf neu geschrieben wird, liegt sie nach Zeitstempeln immer nach der eingelesenen Datei – und gilt damit dauerhaft als veraltet. Genau an diesem Punkt hält das Handbuch von latexmk fest, dass die zirkuläre Abhängigkeit LaTeX eigen ist und latexmk geschrieben wurde, um sie zu überwinden. Wer dennoch make einsetzt, fährt am besten damit, eine Kopie der .aux aufzubewahren und zu vergleichen – oder aus dem Makefile-Ziel einfach latexmk aufzurufen. Tatsächlich laufen sehr viele Projekt-Makefiles auf eine einzige Zeile hinaus: latexmk -pdf $<.

WerkzeugWie die Schritte bestimmt werdenWo die Konfiguration liegtVoraussetzung
latexmkabgeleitet aus Log, .fls und Prüfsummen der Inhaltelatexmkrc / .latexmkrc (Perl)Perl; bei TeX Live und MiKTeX dabei
araragenau so ausgeführt, wie es die Direktiven im Dokument sagen% arara:-Kommentare im DokumentJava
llmkfolgt der in TOML deklarierten sequencellmk.toml oder ein TOML-Feld in der Quellenur texlua
makenach Änderungszeiten entschieden; anfällig für den .aux-ZyklusMakefilemake; fast überall bereits vorhanden

Welcher Befehl beim Schreiben, Teilen und Abgeben

Die Wahl lässt sich auf drei Zeitpunkte bringen. Während des Schreibens mit -pvc beobachten und das Ergebnis bei jedem Speichern ansehen. Bevor das Dokument weitergegeben wird, einmal schlicht latexmk laufen lassen. Kurz vor der Abgabe mit latexmk -C alles löschen und neu bauen. Gerade der letzte Schritt als Gewohnheit verhindert den klassischen Unfall, kurz vor Fristende zu bemerken, dass das Dokument nur auf dem eigenen Rechner kompiliert. Und sind die Einstellungen erst in latexmkrc festgehalten und eingecheckt, nehmen CI-Server wie Mitautoren denselben Weg – die Diskussion „bei mir läuft es doch“ kommt dann gar nicht erst auf.

  • Beim Schreibenlatexmk -pdf -pvc doc.tex: baut bei jedem Speichern automatisch neu; -pvctimeout ergänzen, wenn es nicht unbeaufsichtigt weiterlaufen soll.
  • Engine festlegen$pdf_mode und Verwandte in latexmkrc setzen und die Datei einchecken, damit alle sie teilen.
  • Vor der Übergabe an Mitautoren → einmal schlicht latexmk -pdf laufen lassen und prüfen, dass kein LaTeX Warning: Label(s) may have changed. übrig bleibt.
  • Kurz vor Abgabe oder Veröffentlichung → mit latexmk -C alles löschen, dann clean bauen; latexmk -gg -pdf doc.tex erledigt beides auf einmal.
  • Build auf Server oder in CI → siehe die CI-Seite; -silent hält die Logs lesbar.