Run ls -l inside a TeX Live 2024 installation and the first surprise arrives immediately: latex, the LaTeX compile command everyone learns first, is not a program. It is a symbolic link to pdftex, and nineteen other names — pdflatex, etex, xmltex, amstex among them — point at that same single file. Which command you type still matters enormously, but not for the reason most people assume: the name does not choose a different program, it chooses a different format file, and sometimes the whole difference between two commands comes down to one integer. This page covers the commands you actually type — pdflatex, xelatex, lualatex, latex plus dvipdfmx, and the CJK routes platex and uplatex — along with the handful of options worth memorising and how to read the console when a run goes wrong.
How to compile a .tex file, and what the run leaves behind
One line does it: pdflatex document.tex produces document.pdf. The .tex extension is optional, and options always go before the file name. What you get back, though, is not only the PDF. Two more files appear beside it: document.aux, a ledger that accumulates cross-reference and table-of-contents data, and document.log, a complete transcript containing everything that scrolled past on screen and rather more besides. Both are disposable intermediates — but the first one decides what happens next.
pdflatex document.tex # -> document.pdf, document.aux, document.log
lualatex document # the .tex extension is optional
xelatex -synctex=1 document.tex # options come before the file nameBecause of that .aux file, a compile command normally has to be run twice. LaTeX reads a document once, front to back, so when it sets the table of contents on page one it does not yet know what page section 7 will land on. The first pass writes the numbers it discovered into .aux; the second reads them back and puts them into the text. Bibliographies and indexes lengthen the round trip further. How LaTeX decides the loop has settled, and how latexmk automates it, belongs to the automated-builds page. What this page covers is the commands that get invoked inside that loop.
latex vs pdflatex: the difference is one integer
latex produces .dvi and pdflatex produces .pdf, yet the binary running underneath is the same file. In TeX Live 2024 both links resolve to pdftex, and latex --version announces itself, unabashed, as pdfTeX 3.141592653-2.6-1.40.26 (TeX Live 2024). The difference does not live in the program; it lives in the format file each name loads. pdflatex.ini inputs pdftexconfig.tex, where \pdfoutput = 1 is set. latex.ini inputs that very same file and then immediately overrides it with \pdfoutput=0. Boiled all the way down, that single integer is what separates the two commands.
# TeX Live 2024: four commands, three binaries
readlink $(which latex) $(which pdflatex) $(which xelatex) $(which lualatex)
# pdftex
# pdftex
# xetex
# luahbtex
latex --version
# pdfTeX 3.141592653-2.6-1.40.26 (TeX Live 2024)So how does one binary know which format to load? It looks at the name it was called by. The pdfTeX help text says so plainly: it looks for NAME.fmt, where NAME is the program invocation name. Rename the link and you launch a different LaTeX. To override that guess, pass -fmt=NAME or -progname=NAME, or put a %&format line at the very top of the source file. The same trick is what lets twenty separate names hang off the single pdftex executable.
\pdfoutput is not merely a build-time setting; it is a live pdfTeX primitive. Put \pdfoutput=0 on the very first line of a file, before \documentclass, and even a pdflatex run hands you a .dvi. In practice the cleaner way is the command line: -output-format=dvi or -output-format=pdf, accepted by both pdfTeX and LuaTeX (XeTeX alone lacks it and uses -no-pdf instead). LaTeX itself also reads the value and switches its graphics handling accordingly: in DVI mode it loads l3backend-dvips.def, in PDF mode l3backend-pdftex.def. That is precisely why one route accepts .eps figures and the other accepts .pdf and .png.
pdflatex vs xelatex vs lualatex: choosing by fonts and Unicode
One criterion is enough. If the document is Latin-script and you want speed and maximum package compatibility, use pdflatex. The moment you want to name a font that is already installed on your operating system, or to set characters outside the Latin range, move to xelatex or lualatex. All three emit PDF and accept the same .tex file; it is the door that fonts come in through that differs completely.
Why did the commands multiply like this? History explains it in a straight line. Knuth began writing TeX in 1978, and its output format was DVI — device-independent — because PDF, which Adobe introduced in 1993, did not yet exist. pdfTeX is the extension that closed the gap. Its author, Hàn Thế Thành, was working on microtypography for his doctorate: letting characters protrude very slightly past the right margin and stretching glyph widths imperceptibly to even out the greyness of a page. Writing PDF directly, with no DVI in between, came out of that same work. pdflatex is the fastest today and the one most packages assume, for the simple reason that it has been in use the longest.
xelatex runs XeTeX, developed by Jonathan Kew around 2004. With the fontspec package you name an OpenType font that lives in the operating system and it is used. XeTeX, though, does not write PDF directly: it produces .xdv, an extended form of DVI, and hands that to xdvipdfmx, which makes the PDF. -no-pdf stops the run at the .xdv stage, and -output-driver=CMD swaps out the converter itself. XeTeX being the only one of the three without an -output-format option is a direct consequence of this architecture.
There is a twist inside lualatex. In TeX Live 2024 the link does not lead to luatex but to luahbtex — LuaTeX with the HarfBuzz shaping library built in, which is what makes scripts with complex shaping rules, Arabic and the Indic scripts among them, come out right. Plain luatex still exists; it is what dvilualatex, below, points at. LuaTeX’s headline feature is the one in its name: an embedded Lua interpreter that lets a document reach into line breaking, font loading and other internals of the typesetter. Line up the version numbers here and something odd shows up. tex --version reports TeX 3.141592653: Knuth numbers TeX by extending the digits of pi by one with each update, and pdfTeX and XeTeX both inherit that prefix verbatim. LuaTeX alone stepped off the tradition and calls itself Version 1.18.0.
| Command | Binary (TeX Live 2024) | Output | Fonts and characters |
|---|---|---|---|
pdflatex | pdftex | TeX fonts; fastest, widest package support | |
xelatex | xetex | PDF (via .xdv internally) | System OpenType fonts through fontspec |
lualatex | luahbtex | System fonts + HarfBuzz shaping + Lua scripting | |
latex | pdftex | DVI | TeX fonts; the route for EPS figures and PSTricks |
dvilualatex | luatex | DVI | LuaTeX features when DVI output is required |
platex | euptex | DVI | Japanese; EUC internals, limited to JIS X 0208 |
uplatex | euptex | DVI | Japanese; Unicode internals, rare characters included |
latex then dvipdfmx: why the DVI route survives
Two reasons still hold. First, some machinery speaks only DVI: packages that draw through PostScript, PSTricks foremost among them, are at their best on the latex to dvips route. Second, Japanese typesetting — the subject of the next section — has traditionally travelled this way. latex document.tex writes document.dvi, and dvipdfmx document.dvi turns it into a PDF; if you need PostScript, dvips does that instead. For the rarer wish to use LuaTeX features and still get DVI there is dvilualatex, which points at plain luatex without HarfBuzz.
latex document.tex # -> document.dvi
dvipdfmx document.dvi # -> document.pdf
dvips document.dvi # -> document.ps (for PSTricks and friends)CJK commands: platex, uplatex and the alternatives
Japanese has its own commands because Japanese typesetting has its own rules: vertical writing, the kinsoku prohibitions on which characters may begin or end a line, and the fixed gap that belongs between Japanese and Latin text. pTeX built those rules into the engine itself rather than into macros, and LaTeX running on top of it is pLaTeX — the command platex. The first line of the format file platex.ini still reads “for pLaTeX (ASCII Nihongo LaTeX)”, a trace of its origin at ASCII. upTeX, by Takuji Tanaka, extends pTeX so that its internal character code is full Unicode; LaTeX on that is upLaTeX, the command uplatex. Both always write DVI and never PDF directly.
The same surprise repeats here. In TeX Live 2024 both platex and uplatex are links to a single binary, euptex, and ptex, eptex and uptex converge on it too. What separates them is the internal kanji encoding. platex --version prints e-upTeX 3.141592653-p4.1.1-u1.30-230214-2.6 (utf8.euc), while uplatex --version ends in (utf8.uptex). One executable, in other words, switching -kanji-internal according to the name it was called by. pLaTeX behaves as classic pTeX with EUC internals, so the characters it can handle stay broadly inside JIS X 0208; upLaTeX is Unicode internally and passes rare name kanji and the full CJK Unified Ideographs straight through. That single difference is why uplatex is the default choice for a new Japanese document. The consolidation is fairly recent, and precisely dated: the official pTeX guide shipped with TeX Live records that platex ran on e-pTeX from TeX Live 2012 through 2022, and that on 2023-06-01 it moved to e-upTeX in what the guide calls legacy-encoding-compatibility mode. The extra upTeX primitives became available to pLaTeX, while the internal code for Japanese characters was deliberately left non-Unicode to preserve backward compatibility.
The internal encoding and the encoding of the input file are two different things. The input side is set with -kanji=STRING, which takes euc, jis, sjis, utf8 or uptex. Recent TeX Live defaults to UTF-8, so it can often be left out — but stating it explicitly keeps the build stable when the environment changes. To force BOM-less UTF-8 through reliably, add -no-guess-input-enc, which turns the encoding guesswork off entirely. The output is DVI, so dvipdfmx finishes the job.
# Japanese, the traditional route: typeset -> DVI -> PDF
uplatex -kanji=utf8 -no-guess-input-enc document.tex # -> document.dvi
dvipdfmx document.dvi # -> document.pdfChinese and Korean do not use that route at all. Both are normally handed to a Unicode-native engine: for Chinese, xelatex or lualatex with the ctex bundle (which calls xeCJK and friends underneath); for Korean, the same engines with kotex. Japanese has the same option — load luatexja under lualatex and the PDF comes out directly. Take that road and -kanji becomes unnecessary, because an engine that is Unicode all the way through has no internal encoding left to switch.
The options worth typing: -interaction=nonstopmode, -halt-on-error, -output-directory
Options always precede the file name, and the ones below work with any of the pdfTeX, XeTeX and LuaTeX commands. Four of them earn their keep daily: -synctex=1 for editor integration, -interaction=nonstopmode to keep a run from stalling, -halt-on-error to give up at the first problem instead, and -file-line-error to put the message in a form a machine can parse.
| Option | What it does |
|---|---|
-synctex=1 | Writes document.synctex.gz, enabling jumps between editor and PDF |
-interaction=nonstopmode | Runs to the end instead of waiting for input on an error; batchmode also silences the terminal |
-halt-on-error | Gives up at the very first error; no PDF is produced |
-file-line-error | Rewrites the message header as ./document.tex:3:, which IDEs and CI can parse |
-output-directory=DIR | Writes output and helper files into DIR; DIR has to exist beforehand |
-jobname=NAME | Sets the base name, before the extension, of every output file |
-draftmode | Runs without writing the PDF (pdfTeX / LuaTeX); useful for intermediate passes that only settle references |
-output-format=FORMAT | Chooses dvi or pdf (pdfTeX and LuaTeX only; XeTeX uses -no-pdf) |
-shell-escape | Lifts all restrictions on running external commands through \write18 (read the warning below) |
pdflatex -synctex=1 -interaction=nonstopmode -halt-on-error -file-line-error document.tex
mkdir -p build # -output-directory will NOT create it for you
pdflatex -output-directory=build document.tex-output-directory hides a trap that CI pipelines hit constantly: the directory you name has to exist already. pdfTeX says as much in its own help text — it uses an existing DIR. If it is missing, the run announces “Please type another transcript file name”, then ends with ! Emergency stop and “Fatal error occurred, no output PDF file produced!”. Because the cause has nothing to do with typesetting, even readers fluent in LaTeX logs lose a moment on it. One mkdir -p on the line before is the whole fix.
-shell-escape: why minted needs it and why it is dangerous
This option grants the document the right to run arbitrary shell commands on your machine. External commands are not entirely closed off by default, though. TeX Live runs in a restricted mode, and every run prints the line restricted \write18 enabled. In that state \write18 may only call programs on an allow-list in texmf.cnf, and in TeX Live 2024 that list is short: bibtex, bibtex8, extractbb, gregorio, kpsewhich, makeindex, memoize-extract.pl, memoize-extract.py, repstopdf, r-mpost, texosquery-jre8. Bibliographies and indexes work with no options at all precisely because those two entries were permitted from the start.
pygmentize is not on that list. texmf.cnf leaves it out deliberately, with a comment questioning whether its filter feature is safe. minted, which colours code listings, calls precisely that program — so it will not work in restricted mode and full -shell-escape is the only way. And full means full: a .tex file received from someone else can, in that one compilation, delete files, send local data out, or install something. The rule can stay simple — turn it on only for documents you wrote yourself, in a directory you control. Never for a downloaded template or a submission that arrived for review.
# minted calls pygmentize, which the restricted allow-list does not include
pdflatex -shell-escape document.tex
# turn it off explicitly when compiling a file you did not write
pdflatex -no-shell-escape untrusted.texReading the console output when a compile fails
Read from the first line that begins with !, not from the end. LaTeX errors cascade, so what is left on screen at the end is usually collateral damage caused by the first one; the cause is further up. Every error report has the same shape: the ! line names the symptom, and the line below starting with l. shows where it happened.
! Missing $ inserted.
<inserted text>
$
l.3 Some text with a bare x^
2 here.
?The l.3 line is the one that matters. It means source line 3, but it is printed broken in two at exactly the point TeX had read up to. Everything before the break was consumed; everything after it has not been read yet. Here the break falls immediately after x^, so the superscript character is visibly the culprit. ! Undefined control sequence. follows the same pattern, and the misspelled command sitting just before the break is the answer. The trailing ? is a prompt: the run has stopped in the default errorstopmode and is waiting for input. Press Enter to carry on, or type x to abandon the run.
That dialogue is exactly what scripts and CI do not want, which is what the interaction modes are for. -interaction=nonstopmode prints everything without stopping, batchmode additionally silences the terminal, and scrollmode only asks when a file cannot be found. If instead you want to see just the first problem, -halt-on-error ends the run with ! Emergency stop. and “Fatal error occurred, no output PDF file produced!”. Adding -file-line-error rewrites the header as ./document.tex:3: Missing $ inserted., which editors and CI can turn into a clickable link. Whatever you choose, the complete transcript always lands in .log — miss it in the terminal and the same text is waiting in the file.
Which compile command should you use
- A Latin-script document —
pdflatex. The fastest, and the default that most packages assume. - You want to name a font from the operating system —
xelatexorlualatex; both take it throughfontspec. - Complex-shaping scripts, or scripting the typesetter itself —
lualatex(which is reallyluahbtex, HarfBuzz included). - A new Japanese document —
uplatexthendvipdfmx, orlualatexwithluatexja. - Chinese or Korean —
xelatexorlualatexwithctexorkotex; no DVI stage at all. - PSTricks and other DVI-only machinery —
latexthendvipsordvipdfmx.
One last diagnostic habit. When the build button in your editor fails, type the same command by hand in a terminal first. If it succeeds there, the problem is in the editor configuration, not the document. Before handing a file to anyone or putting it into CI, do one run with -halt-on-error -file-line-error -interaction=nonstopmode so that the first real error position is legible. And in daily use almost nobody types these commands twice by hand — the job goes to latexmk, which counts the passes and calls dvipdfmx for you. How it knows when to stop is the subject of the automated-builds page.