Ein LaTeX-Projekt aus mehreren Dateien lässt sich auf ein Dutzend .tex-Dateien verteilen, ohne dass der Compiler mit der Wimper zuckt: pdflatex main.tex liest alle ein und liefert ein PDF. Den Faden verliert der Editor. Ein Druck auf die Build-Taste, während Kapitel drei im Vordergrund steht, und zurück kommt ! LaTeX Error: Missing \begin{document}., weil der Editor brav die gerade sichtbare Datei gesetzt hat. Abhilfe schafft eine einzige Zeile am Kopf jedes Kapitels, % !TEX root = ../main.tex — und das Merkwürdige daran ist, dass LaTeX diese Zeile nie zu Gesicht bekommt. Sie ist ein Kommentar, gerichtet an den Editor und nicht an den Compiler. Diese Seite behandelt genau jene zweite Schicht eines aufgeteilten Projekts: welche Editoren den magischen Kommentar lesen und was die übrigen stattdessen verwenden, wie SyncTeX in die richtige Kapiteldatei zurückfindet, wo die Build-Dateien tatsächlich landen und was zerbricht, wenn die geöffnete Datei nicht die Hauptdatei ist. Die Befehle, die das Aufteilen selbst besorgen — \input, \include, \includeonly —, sind eine eigene Geschichte und am Fuß der Seite verlinkt.
% !TEX root: die Hauptdatei bauen, während ein Kapitel offen ist
Ein % !TEX root = ../main.tex am Kopf jeder Datei, die nicht die Hauptdatei ist, und die Build-Taste tut das Richtige, gleich welche Datei gerade im Vordergrund liegt. Zwei Details aus der Dokumentation von TeXShop lohnen sich, denn über beide stolpern viele. Erstens muss die Zeile innerhalb der ersten zwanzig Zeilen der Datei stehen — unter einem langen Lizenzkopf vergraben wird sie schlicht nicht gefunden. Zweitens wird der Pfad relativ zu der Datei aufgelöst, in der die Zeile steht, nicht relativ zum Projektstamm: Ein Kapitel in chapters/ braucht ../main.tex, nicht main.tex. Ein absoluter Pfad geht ebenfalls, um den Preis, dass sich das Projekt nicht mehr verschieben lässt. Die Hauptdatei selbst braucht keine solche Zeile; sie ist bereits die Wurzel.
thesis/
main.tex <- the root; needs no magic comment
chapters/
03-results.tex <- carries the line below% chapters/03-results.tex -- the first lines of the file
% !TEX root = ../main.tex % relative to THIS file, not to thesis/
% !TEX TS-program = xelatex % pin the engine for the whole project
% !TEX encoding = UTF-8 Unicode
\chapter{Results}Die Zeile hat einen Vorgänger, und warum der Nachfolger gewann, ist aufschlussreich. TeXShop bot früher den Menübefehl „Set Project Root…“ an, der die Antwort in einer Begleitdatei neben dem Kapitel ablegte: Zu two.tex gesellte sich ein two.texshop. Wurde diese unsichtbare Datei weggeworfen, setzte TeXShop sofort wieder das Kapitel. Die Dokumentation von TeXShop beschreibt den Befehl heute als aus den Menüs entfernt, weil die Methode % !TEX root robuster sei — und in diesem einen Wort steckt der Grund. Eine Zeile innerhalb der Datei reist mit der Datei. Sie übersteht eine Kopie, das Umbenennen des übergeordneten Ordners, einen Git-Clone und eine Mitautorin, die den eigenen Editor nie geöffnet hat. Konfiguration, die neben einer Datei geparkt wird, verliert die Datei irgendwann.
Warum LaTeX selbst % !TEX root nie liest
Weil % einen Kommentar einleitet und Kommentare vom Scanner in TeX verworfen werden, bevor irgendetwas anderes geschieht. pdflatex, xelatex und lualatex sehen auf dieser Zeile schlicht nichts. Sie ist eine Nachricht von einem Programm (dem Editor) an ein anderes Programm (den Build-Befehl des Editors), die nur zufällig durch die Quelldatei reist. Daraus folgen zwei praktische Dinge. Erstens: Vor einer falschen Zeile warnt niemand. Zeigt sie auf eine nicht vorhandene Datei, fällt der Editor stillschweigend auf die eigene Vermutung zurück, meist die geöffnete Datei, und schon steht wieder ! LaTeX Error: Missing \begin{document}. da. Zweitens ignoriert ein Build aus dem Terminal oder in der CI — latexmk main.tex, ein Makefile, ein Schritt in GitHub Actions — den magischen Kommentar vollständig, weil er die Hauptdatei auf der Kommandozeile benennt. Die Zeile ist eine Bequemlichkeit für interaktives Arbeiten, kein Bestandteil der Projektdefinition.
Genau einen Kommentar liest TeX doch, und den sollte man kennen, damit die beiden nie verwechselt werden. Beginnt die allererste Zeile der Haupteingabedatei mit %&, wertet die Engine sie selbst aus, um ein Format zu wählen — %&pdflatex, %&latex —, ein Verhalten, das die Handbuchseite von tex als über die Option -parse-first-line und die Konfigurationsvariable parse_first_line steuerbar beschreibt. Dieser Mechanismus stammt aus dem Format-Ladewerk von TeX und sitzt in der Engine. Alles, was % !TEX ... geschrieben wird, sitzt dagegen im Editor. Die optische Ähnlichkeit ist Zufall: Beide wollten Anweisungen dort verstecken, wo LaTeX nicht darüber stolpert.
% main.tex -- first line only: the ENGINE parses this one
%&pdflatex
% chapters/03-results.tex -- the engine never sees this one
% !TEX root = ../main.texWelche Editoren % !TEX root lesen und was die übrigen stattdessen nutzen
TeXShop, TeXworks, TeXstudio und VS Code mit der Erweiterung LaTeX Workshop lesen die Zeile allesamt; Emacs mit AUCTeX verwendet eine eigene dateilokale Variable, und Overleaf entnimmt die Antwort einer Projekteinstellung statt der Quelle. Am lehrreichsten ist LaTeX Workshop, weil es sein gesamtes Entscheidungsverfahren dokumentiert: Zuerst zählt der magische Kommentar im aktiven Editor, dann die Frage, ob die aktive Datei selbst \documentclass oder \begin{document} enthält, dann werden die .tex-Dateien im Arbeitsbereichsstamm nach einer durchsucht, die die aktive Datei einbindet, anschließend wird das subfiles-Muster \documentclass[main.tex]{subfiles} erkannt, und zuletzt dient die Dateiliste .fls des letzten Laufs als Rückfallebene. Der magische Kommentar gewinnt, weil er zuerst abgefragt wird — und wenn er das einmal nicht soll, heißt die Einstellung latex-workshop.latex.build.enableMagicComments.
| Editor | Was gelesen wird | Hinweis |
|---|---|---|
TeXShop | % !TEX root | Ursprung der Direktive; Geschwister % !TEX TS-program, encoding, spellcheck |
TeXworks | % !TEX root | übernimmt dasselbe Schema magischer Kommentare |
TeXstudio | % !TeX root | erkennt die Wurzel zunächst automatisch; die Zeile hat Vorrang |
LaTeX Workshop | % !TEX root | für VS Code; erste von fünf Stufen, abschaltbar über latex-workshop.latex.build.enableMagicComments |
AUCTeX | TeX-master | für Emacs; eine dateilokale Variable, üblicherweise am Ende der Datei |
Overleaf | eine Projekteinstellung | im Projektmenü als Hauptdokument gewählt; in der Quelle steht nichts |
Der interessante Ausreißer ist Emacs. AUCTeX stellt dieselbe Frage, legt die Antwort aber als dateilokale Variable ab, üblicherweise in einem Block am Ende der Datei. Da jeder Editor nur seine eigene Konvention liest, kostet es nichts, beide mitzuführen: Der AUCTeX-Block ist für jeden anderen Editor ein gewöhnlicher Kommentar, und % !TEX root ist für Emacs ein gewöhnlicher Kommentar. In gemeinsamen Repositorien tragen Kapiteldateien häufig beides, was richtig ist und zwei Zeilen kostet. Overleaf steht ganz außerhalb dieser Debatte: Das Hauptdokument ist eine Eigenschaft des Projekts und wird im Projektmenü gesetzt, sodass in der Quelle nichts aus dem Tritt geraten kann — und ebenso wenig etwas mitkommt, wenn das Projekt heruntergeladen und lokal geöffnet wird.
% chapters/03-results.tex -- both conventions in one file
% !TEX root = ../main.tex
\chapter{Results}
%%% Local Variables:
%%% mode: latex
%%% TeX-master: "../main"
%%% End:SyncTeX über Dateien hinweg: warum ein Klick ins PDF das richtige Kapitel öffnet
Weil SyncTeX für jede Box auf jeder Seite festhält, aus welcher Eingabedatei und aus welcher Zeile sie stammt. Ein Doppelklick auf einen Absatz von Kapitel drei im PDF öffnet chapters/03-results.tex und nicht main.tex. Eingeschaltet wird das mit -synctex=1, und es entsteht genau eine Datei main.synctex.gz im Projektstamm, benannt nach der Wurzeldatei. Eine synctex-Datei je Kapitel gibt es nicht: Ein einziger Index deckt das ganze Projekt ab, und genau deshalb kann er auf jede Datei darin zeigen.
$ pdflatex -synctex=1 main.tex
$ synctex edit -o "2:100:200:main.pdf"
SyncTeX result begin
Output:main.pdf
Input:./chapters/ch2.tex
Line:2
SyncTeX result endDen Kommandozeilenclient einmal auszuprobieren macht den Mechanismus greifbar. synctex edit nimmt eine Seite und einen Punkt im PDF entgegen und liefert Dateinamen und Zeilennummer; synctex view geht den umgekehrten Weg, von einer Quellzeile zu einem Ort auf der Seite. Die dahinterstehende Synchronize TeXnology geht, in den Worten ihrer eigenen Handbuchseite, im Wesentlichen auf Jérôme Laurens zurück und wird heute als Teil von TeX Live gepflegt. Die Dokumentation von TeXShop stellt die Verbindung zum vorigen Abschnitt ausdrücklich her: Erst die Zeile % !TEX root sorgt dafür, dass ein Klick bei der Rückwärtssuche das richtige Kapitelfenster öffnet und aktiviert, statt in der Hauptdatei zu landen. Genau darum werden beide Funktionen üblicherweise zusammen eingerichtet.
Die Falle schnappt zu, sobald ein Kapitel allein kompiliert wird. Ein Engine-Lauf legt seine Nebenprodukte in das Arbeitsverzeichnis, aus dem der Build gestartet wurde, nicht neben die Eingabedatei. Ein pdflatex -synctex=1 chapters/03-results.tex aus dem Projektstamm lässt 03-results.synctex.gz im Stamm entstehen, direkt neben main.synctex.gz. Nun beschreiben zwei Indizes dieselben Quellzeilen, und einer davon zeigt in ein Ein-Kapitel-PDF, das bei Seite 1 beginnt. Welchen der Betrachter gerade liest, entscheidet, wo der Klick landet, und die Seitenzahlen stimmen nicht mehr überein. Vor der Rückkehr zum Gesamtbuild sollten das PDF und die synctex-Datei des Kapitel-Builds gelöscht werden.
Wo die Build-Dateien landen und was in .gitignore gehört
\include schreibt eine .aux je Kapitel, und zwar neben die Kapiteldatei. Wird ein Projekt mit chapters/01-intro.tex gebaut, liegt anschließend chapters/01-intro.aux direkt daneben. Alles Übrige bleibt im Stamm bei der Hauptdatei: main.aux, main.log, main.toc, main.out, main.synctex.gz und mit latexmk zusätzlich main.fls und main.fdb_latexmk. Die erzeugten Dateien liegen also nicht an einem Ort, sondern dünn über den gesamten Quellbaum verteilt.
thesis/
main.tex main.pdf
main.aux main.log main.toc main.out
main.synctex.gz main.fls main.fdb_latexmk
chapters/
01-intro.tex 01-intro.aux <- one .aux per \include, here
02-method.tex 02-method.auxFür Git ist das weniger lästig, als es aussieht, denn ein .gitignore-Muster ohne Schrägstrich greift in jeder Tiefe: Eine schlichte Zeile *.aux deckt chapters/01-intro.aux bereits ab. Nicht abgedeckt wird es von einem am Stamm verankerten /*.aux, und ebenso wenig von der Gewohnheit, im Projektstamm mit rm *.aux aufzuräumen. Und, überraschender, auch nicht von latexmk: Unter TeX Live 2024 getestet, entfernen latexmk -c und selbst latexmk -C die Zwischendateien im Stamm und lassen chapters/*.aux liegen. Wer also eine veraltete .aux im Verdacht hat — jene Sorte Fehler, die auf ein nie berührtes Kapitel zeigt —, räumt ausdrücklich auf, etwa mit find . -name "*.aux" -delete.
An einer Stelle bringt die Aufteilung ein Werkzeug wirklich zu Fall: bei -output-directory. Wer für ein Projekt mit \include einen Build außerhalb des Quellbaums mit pdflatex -output-directory=build main.tex verlangt, erlebt einen Abbruch. TeX versucht build/chapters/01-intro.aux zu öffnen, das Unterverzeichnis existiert nicht, und es erscheint ! I can't write on file, gefolgt von einem fatalen Fehler und gar keinem PDF. TeX legt keine Verzeichnisse an. Zwei Auswege gibt es: die gespiegelten Unterverzeichnisse vorab selbst anlegen oder die Aufgabe an latexmk -outdir=build übergeben, das sie selbst erzeugt. Genau deshalb werden Mehrdatei-Projekte, die außerhalb des Quellbaums bauen, fast immer von latexmk statt direkt von der Engine gesteuert.
# fails: TeX will not create build/chapters/ by itself
pdflatex -output-directory=build main.tex
# ! I can't write on file ... chapters/01-intro.aux
# works: mirror the subdirectories first
mkdir -p build/chapters && pdflatex -output-directory=build main.tex
# works: latexmk creates them for you
latexmk -pdf -outdir=build main.texEben diese .fls-Datei sorgt dafür, dass Neuübersetzungen beim Speichern auch in einem aufgeteilten Projekt funktionieren. Läuft der Durchgang mit -recorder — was latexmk von sich aus ergänzt —, hält die Engine jede geöffnete Datei fest, und main.fls enthält für jedes Kapitel eine Zeile INPUT chapters/01-intro.tex. latexmk bewahrt die daraus gewonnene Abhängigkeitsliste in main.fdb_latexmk auf und überwacht sie vollständig; deshalb baut das Speichern von Kapitel drei das ganze Buch neu, ohne dass irgendwo stünde, Kapitel drei gehöre dazu. Die Struktur der Aufteilung muss nie zweimal erklärt werden: Die Folge der \include-Zeilen ist bereits die Abhängigkeitserklärung.
Was zerbricht, wenn die geöffnete Datei nicht die Hauptdatei ist
Drei Symptome, die einander kein bisschen ähneln. Erstens bricht eine gewöhnliche Kapiteldatei, allein kompiliert, sofort ab: ! Undefined control sequence. beim ersten \chapter, dann ! LaTeX Error: Missing \begin{document}., dann ! Emergency stop. und kein PDF — zwangsläufig bei einer Datei ohne \documentclass. Zweitens ist ein allein kompiliertes subfiles-Kapitel schlimmer, weil es gelingt: Es entsteht ein plausibles Ein-Kapitel-PDF, das bei Seite 1 beginnt und dessen Querverweise in andere Kapitel als ?? erscheinen. Drittens scheitert ein aus dem falschen Arbeitsverzeichnis gestarteter Lauf stattdessen an den Bildern, denn jeder relative Pfad im Projekt wird von dort aufgelöst, wo der Build lief, nicht von dort, wo die Datei liegt.
- Die Build-Taste setzt die falsche Datei →
% !TEX rootin jede Datei außer der Hauptdatei schreiben, innerhalb der ersten zwanzig Zeilen, mit einem Pfad relativ zu eben dieser Datei. ! LaTeX Error: Missing \begin{document}.→ hier wird ein Kapitel direkt kompiliert; diese Datei hat keine Präambel und soll auch keine haben.- Bilder verschwinden oder der Lauf bricht an einer fehlenden Datei ab → der Build läuft nicht im Projektstamm; relative Pfade gelten ab dem Arbeitsverzeichnis.
- Ein Klick ins PDF öffnet die Hauptdatei statt des Kapitels → dem Lauf fehlte
-synctex=1, oder der Betrachter liest eine veraltete.synctex.gz. - Nach einem gescheiterten Versuch liegen einzelne
.log- und.pdf-Dateien im Stamm → ein Engine-Lauf schreibt seine Nebenprodukte ins Arbeitsverzeichnis, nicht neben die Eingabedatei.
Am Ende halten zwei Gewohnheiten diese Schicht ruhig. Erstens: keine Leerzeichen in Datei- und Ordnernamen. Jedes Werkzeug auf dieser Seite reicht den Pfad irgendwann an eine Shell oder an einen mit % eingeleiteten magischen Kommentar weiter, und im Leerzeichen wohnen die Quoting-Fehler. Zweitens: den Build stets im Projektstamm starten, ob von Hand, aus einem Makefile oder über den Editor. Das Arbeitsverzeichnis ist der eine Bezugspunkt, den \includegraphics, \include und -output-directory gemeinsam nutzen; verrutscht er, verrutschen alle drei zugleich. Sind diese beiden Dinge in Ordnung, wird die Mehrdatei-Schicht unsichtbar — und nur in diesem Zustand tut sie ihre Arbeit.