# Toilet Duck -- deck build
#
#   make            the deck with demonstrations interleaved
#   make talk       the deck without them
#   make both       both PDFs
#   make check      build, then fail on any error or overfull box
#   make watch      rebuild on save
#   make clean      remove LaTeX droppings, keep the PDFs
#   make distclean  remove the PDFs and the generated .tex as well
#
# XeLaTeX is required: the theme uses fontspec. Two passes, always --
# the footline carries a reference that is not resolved on the first.
#
# The generated deck is a build artifact. It is regenerated from
# toiletduck-acn.tex, demo-frames.tex and demo-preamble.tex whenever any
# of those is newer, so editing it directly loses the edit at the next
# build. Edit the sources; add it to .gitignore.

LATEX      ?= xelatex
PYTHON     ?= python3
LATEXFLAGS ?= -interaction=nonstopmode -halt-on-error

TALK      := toiletduck-acn
DEMOS     := $(TALK)-demos
FRAMES    := demo-frames.tex
PREAMBLE  := demo-preamble.tex
MERGE     := build-demos.py
WIDTHS    := check-widths.py

# Optional. The theme guards its \includegraphics, so the deck builds
# without the monogram -- just without the mark in the footer.
ASSETS    := $(wildcard assets/*.png assets/*.pdf)

# Everything xelatex leaves behind. .vrb only appears once a frame is
# marked [fragile], which every demonstration frame is.
LITTER    := aux log nav out snm toc vrb

.PHONY: all talk both check watch view clean distclean help
.DEFAULT_GOAL := all

all: $(DEMOS).pdf
talk: $(TALK).pdf
both: $(DEMOS).pdf $(TALK).pdf

# A failed pass leaves a PDF from the previous run on disk, which is the
# worst kind of stale: it opens, it looks right, it is last week's.
.DELETE_ON_ERROR:

# --------------------------------------------------------------------
# Merge
# --------------------------------------------------------------------

$(DEMOS).tex: $(TALK).tex $(FRAMES) $(PREAMBLE) $(MERGE)
	$(PYTHON) $(MERGE)

# --------------------------------------------------------------------
# Typeset
#
# Output is swallowed because xelatex is 400 lines of noise on success.
# On failure the log is replayed from the first error, which is the only
# part anyone reads anyway.
# --------------------------------------------------------------------

%.pdf: %.tex $(ASSETS)
	@echo "  $(LATEX)  $< (pass 1)"
	@$(LATEX) $(LATEXFLAGS) $< >/dev/null || { sed -n '/^!/,+8p' $*.log; exit 1; }
	@echo "  $(LATEX)  $< (pass 2)"
	@$(LATEX) $(LATEXFLAGS) $< >/dev/null || { sed -n '/^!/,+8p' $*.log; exit 1; }
	@echo "  $@  --  $$(pdfinfo $@ 2>/dev/null | awk '/^Pages/{print $$2}') pages"

# --------------------------------------------------------------------
# Check
#
# An overfull box on a slide is content past the edge of the projector,
# so it is an error here rather than a warning. Underfull boxes are not
# checked: beamer generates them constantly and none of them are visible.
#
# The width check is not redundant with the overfull check. fancyvrb
# sets each transcript line in its own \hbox, which TeX places without
# complaint however wide it is -- so a command eight characters too long
# runs off the right edge of the screen and the log stays clean. That
# failure is invisible until you are standing in front of the room.
# --------------------------------------------------------------------

check: $(DEMOS).pdf
	@$(PYTHON) $(WIDTHS) $(FRAMES) || exit 1
	@fail=0; \
	if grep -q '^!' $(DEMOS).log; then \
	  echo "FAIL  LaTeX errors:"; grep -n '^!' $(DEMOS).log; fail=1; fi; \
	if grep -q 'Overfull' $(DEMOS).log; then \
	  echo "FAIL  overfull boxes -- content past the frame edge:"; \
	  grep -n 'Overfull' $(DEMOS).log; fail=1; fi; \
	if grep -q 'Font.*not found' $(DEMOS).log; then \
	  echo "FAIL  substituted font -- install TeX Gyre Heros and DejaVu Sans Mono"; fail=1; fi; \
	if [ $$fail -eq 0 ]; then \
	  echo "ok    $(DEMOS).pdf clean: no errors, no overfull boxes"; \
	else exit 1; fi

# --------------------------------------------------------------------
# Convenience
# --------------------------------------------------------------------

# entr if it is installed, latexmk otherwise. Both watch the sources
# rather than the generated deck, so a demo edit triggers a full rebuild
# through the merge step.
watch:
	@if command -v entr >/dev/null 2>&1; then \
	  ls $(TALK).tex $(FRAMES) $(PREAMBLE) $(MERGE) | entr -c $(MAKE) all; \
	else \
	  echo "entr not found -- falling back to latexmk (merge step will not re-run)"; \
	  latexmk -pvc -xelatex $(DEMOS).tex; \
	fi

view: $(DEMOS).pdf
	@(xdg-open $< || open $<) >/dev/null 2>&1 &

clean:
	@rm -f $(foreach e,$(LITTER),$(TALK).$(e) $(DEMOS).$(e))
	@rm -f $(TALK).fls $(TALK).fdb_latexmk $(DEMOS).fls $(DEMOS).fdb_latexmk
	@echo "  cleaned"

distclean: clean
	@rm -f $(TALK).pdf $(DEMOS).pdf $(DEMOS).tex
	@echo "  removed PDFs and the generated deck"

help:
	@sed -n '3,10p' $(MAKEFILE_LIST) | sed 's/^# \{0,1\}//'
