#!/bin/sh # Assemble: preamble, title (p0001), colophon, remaining pages in PDF-page order, # endnotes; compile with XeLaTeX (3 passes: marks/backrefs). set -e cd "$(dirname "$0")" OUT=ross-1988-retypeset.pdf # Two builds must never share work/: they share main.aux, and the second reads # the first's half-written copy and dies at \begin{document} with "Undefined # control sequence" or "File ended while scanning use of \enotez@note" — which # reads like an unbalanced brace in a page file and is nothing of the kind. # mkdir is atomic, so it is the lock. The way a second build actually gets # started is mundane: someone runs `make` again while an earlier build is still # going. Check `docker ps` before starting one — a build takes several minutes # and gives no output until it finishes, so a running one is easy to forget. if ! mkdir work/.buildlock 2>/dev/null; then echo "build.sh: another build holds work/.buildlock — refusing to start." >&2 echo " check: docker ps (if nothing is running: rmdir work/.buildlock)" >&2 exit 1 fi trap 'rmdir work/.buildlock 2>/dev/null' EXIT INT TERM # Three full passes run below regardless, so a stale aux buys nothing. rm -f work/main.aux work/main.toc work/main.out python3 tools/polish.py >/dev/null # typographic pass: src/pages -> work/pages # Version seal: a mark generated from a hash of the sources, drawn in the # colophon. Regenerated here so the PDF can never carry a stale one. The hash # covers src/ only -- never main.tex, work/seal.tex or the PDF, all of which # this build produces, which would make the seed depend on its own output. python3 tools/gen_seal.py --content >/dev/null # Cover mosaic. Tracked at plates/cover-mosaic.png so an ordinary build is # fast, but remade here if it is missing, so the cover survives losing any # derived file -- the generator needs only plates/ and fonts/, both in git. [ -f plates/cover-mosaic.png ] || python3 tools/gen_cover_mosaic.py # Re-encode the plates to match what they actually hold: bilevel type goes to # lossless JBIG2, genuine halftone stays 8-bit as JPEG. Writes copies under # work/plates-opt and repoints work/pages at them -- plates/ and src/ are # never touched, so deleting work/ reverts it. Must run AFTER polish.py, which # is what creates work/pages. python3 tools/optimize_plates.py { cat src/preamble.tex printf '%s\n' '\begin{document}\immediate\openout\unsurefile=unsure.log\immediate\openout\erratafile=errata.log\immediate\openout\qslipfile=qslips.log' printf '\\input{%s}\n' src/cover.tex work/pages/p0001.tex src/colophon.tex for f in work/pages/p*.tex; do [ "$f" = work/pages/p0001.tex ] || printf '\\input{%s}\n' "$f"; done printf '%s\n' '\notesection' '\printerrata' '\immediate\closeout\unsurefile\immediate\closeout\erratafile\immediate\closeout\qslipfile\end{document}' } > src/main.tex for i in 1 2; do xelatex -interaction=nonstopmode -halt-on-error -output-directory=work src/main.tex >work/pass$i.log 2>&1 || { grep -E "^!" -A3 work/pass$i.log; exit 1; }; done xelatex -interaction=nonstopmode -halt-on-error -output-directory=work src/main.tex | grep -E "Overfull|Warning: .*(undefined|multiply)" | sort | uniq -c || true cp work/main.pdf "$OUT"; cp work/unsure.log . 2>/dev/null || true; cp work/errata.log . 2>/dev/null || true; cp work/qslips.log . 2>/dev/null || true echo "built $OUT: $(pdfinfo "$OUT" | grep Pages)"