-
Notifications
You must be signed in to change notification settings - Fork 0
1. minimath
While this project ultimately compiles down to just a single PDF, the supporting infrastructure is very comprehensive, one that I've been working on in parallel with the book for a year. Here are its components:
- The
minimath
binary (written in Rust). -
texlive-small
: a Docker image (built on GitHub Actions and hosted on GHCR) -
minimath-rg
: a ripgrep-inspired indexer. - Build + Test + Compile PDF + Publish on GitHub Actions.
- Formatted with latexindent.
- The user selects a preset to compile. If
none is specified,
minimath
compiles everything. - Spawn a child process of
pdflatex
and keep the handle on its stdin. - Traverse the
*.tex
files across the project based on the preset and write content directly topdflatex
's stdin. - Write build artifacts to the
.build
directory and move the generated PDF file to the current working directory.
The machinery that orchestrates all of this is the minimath
binary.
After installing a copy/distribution of LaTeX, the simplest way to compile a PDF
from plain-source TeX is to write all your TeX into one file, say
one-shot-job.tex
:
\documentclass{article}
\begin{document}
Khang was here.
\end{document}
and then run the command
pdflatex one-shot-job.tex
to produce a one-shot-job.pdf
file (That example above actually works).
Now, when pdflatex
is ran without a path as argument, it will interpret all
remaining commands as TeX input. To see how this works, try running just
pdflatex
It will enter a REPL that looks like this,
This is pdfTeX, Version 3.141592653-2.6-1.40.26 (TeX Live 2024) (preloaded format=pdflatex)
restricted \write18 enabled.
**
where you can start typing in what would have been the contents of
one-shot-job.tex
. After you've done all that, the screen should look something
like this:
This is pdfTeX, Version 3.141592653-2.6-1.40.26 (TeX Live 2024) (preloaded format=pdflatex) restricted \write18 enabled. **\documentclass{article} entering extended mode LaTeX2e <2023-11-01> patch level 1 L3 programming layer <2024-02-20> *\begin{document} (/usr/local/texlive/2024basic/texmf-dist/tex/latex/base/article.cls Document Class: article 2023/05/17 v1.4n Standard LaTeX document class (/usr/local/texlive/2024basic/texmf-dist/tex/latex/base/size10.clo)) (/usr/local/texlive/2024basic/texmf-dist/tex/latex/l3backend/l3backend-pdftex.d ef) No file texput.aux. *Khang was here *\end{document} [1\{/usr/local/texlive/2024basic/texmf-var/fonts/map/pdftex/updmap/pdftex.map}] (./texput.aux)</usr/local/texlive/2024basic/texmf-dist/fonts/type1/public/amsfo nts/cm/cmr10.pfb> Output written on texput.pdf (1 page, 13532 bytes). Transcript written on texput.log.
and you'd find a texput.pdf
existing in the current directory
that should match the previously generated one-shot-job.pdf
exactly. The core
compilation flow of minimath
exploits this behavior
of pdflatex
.
We use Rust standard library's Command to spawn a pdflatex
subprocess and keep it open as we write to its stdin. Then, instead of us typing
the contents of the .tex
document line-by-line, we can now programmatically
send characters to the subprocess. It is through this and a strategic traversal
of all the *.tex
files in the repository that we build the final PDF.
There's just one more thing: in the spirit of
reproducibility, we've downloaded a minimal set of
packages and committed them to this repository at .github/tex/*.sty
. But how
will pdflatex
know to look in there for TeX packages? The linux
documentation tells us to set the TEXINPUTS
environment
variable to .github/tex:
like so
TEXINPUTS=.github/tex: pdflatex ...
and we're all set.