Files
LithosAnanke/docs/book/Makefile
T
Claude 1a9b142d80 docs: The Dictionary of v4 -- every live word, alphabetical, its stack notation and description; built and published with every release (#157) [skip ci]
make dictionary -> build/docs/Dictionary.pdf (20 pages): mkdictionary.py checks docs/reference/v4-dictionary.tsv against the booted system's words first, so a stale dictionary fails the build; then Markdown, pandoc, docs/book/dictionary.tex (the books' preamble, its own title page: version and commit). Quoted FORTH tokens set as code, so their quotes print straight. The nightly's docs stage builds it; publish sends classifier dictionary; the Gitea release attaches it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GE3SDvQn5EoxdGufk84zz2
2026-11-10 11:31:07 +00:00

174 lines
7.8 KiB
Makefile

# SPDX-License-Identifier: LicenseRef-Starship-1.0
# Copyright (c) 2023-2026 Robert A. James
# docs/book/Makefile -- the single LithosAnanke book, built from LaTeX.
#
# Run from the repo root (the root Makefile does this):
# make docs -> build/docs/LithosAnanke.pdf
# make docs TARGET=<board> -> build/docs/LithosAnanke-<board>.pdf
# (same book + that board's appendix)
#
# docs/book/main.tex is the master. Markdown sources are converted by pandoc
# into LaTeX fragments under build/docs/gen/ and \input from main.tex; the
# Markdown stays the source of truth until a chapter is rewritten in LaTeX.
# Figures and tables that present data read the CSV directly at LaTeX time
# (pgfplots / pgfplotstable) and cite it with \datasource{path}; see
# docs/book/README.md.
BOOK_DIR := docs/book
OUT := build/docs
GEN := $(OUT)/gen
BOARD ?=
BOARD_NAME ?=
VARIANT := $(if $(BOARD),$(BOARD),book)
VAR_DIR := $(OUT)/$(VARIANT)
PDF := $(OUT)/LithosAnanke$(if $(BOARD),-$(BOARD)).pdf
PANDOC ?= pandoc
LATEXMK ?= latexmk
PANDOC_FILTERS := $(BOOK_DIR)/pandoc/table-widths.lua $(BOOK_DIR)/pandoc/code-breaks.lua
PANDOC_FLAGS := -f gfm -t latex --top-level-division=chapter --wrap=preserve \
$(foreach f,$(PANDOC_FILTERS),--lua-filter=$(f))
# fragment name : Markdown source. The order of \input lines is main.tex's.
CHAPTERS := \
ontology:docs/reference/ONTOLOGY.md \
roadmap:docs/working/archive/superseded/2025-12-14-starforth-roadmap.md \
boards:boards/README.md \
justification:docs/v4.0.0/JUSTIFICATION.md \
decomposition:docs/v4.0.0/DECOMPOSITION.md \
fabric-0:docs/fabric/FABRIC-0.md \
fabric-1:docs/fabric/FABRIC-1.md \
fabric-2:docs/fabric/FABRIC-2.md \
fabric-3:docs/fabric/FABRIC-3.md \
fabric-3-5:docs/fabric/FABRIC-3.5.md \
fabric-3-6:docs/fabric/FABRIC-3.6.md \
fabric-3-7:docs/fabric/FABRIC-3.7.md \
fabric-4:docs/fabric/FABRIC-4.md
ifneq ($(BOARD),)
ifeq ($(wildcard boards/$(BOARD)/README.md),)
$(error boards/$(BOARD)/README.md is missing: it is the board's appendix)
endif
CHAPTERS += board-$(BOARD):boards/$(BOARD)/README.md
endif
chapter_name = $(word 1,$(subst :, ,$(1)))
chapter_src = $(word 2,$(subst :, ,$(1)))
FRAGMENTS := $(foreach c,$(CHAPTERS),$(GEN)/$(call chapter_name,$(c)).tex)
# The Book of Fabric (#138): the FABRIC design record alone, as one book, from
# the same fragments -- `make fabric` -> build/docs/BookOfFabric.pdf.
FABRIC_DIR := $(OUT)/fabric
FABRIC_PDF := $(OUT)/BookOfFabric.pdf
FABRIC_FRAGS := $(foreach n,fabric-0 fabric-1 fabric-2 fabric-3 fabric-3-5 fabric-3-6 fabric-3-7 fabric-4,$(GEN)/$(n).tex)
# The Dictionary of v4 (#157): every live word, alphabetical, with its stack
# notation and description -- `make dictionary` -> build/docs/Dictionary.pdf.
# Its names are the booted system's (v4/tools/mkdictionary.py); the check
# refuses a table that has fallen behind them.
DICT_DIR := $(OUT)/dictionary
DICT_PDF := $(OUT)/Dictionary.pdf
DICT_TSV := docs/reference/v4-dictionary.tsv
.PHONY: all fabric dictionary tools FORCE
all: $(PDF)
fabric: $(FABRIC_PDF)
dictionary: $(DICT_PDF)
tools:
@missing=""; \
for t in $(PANDOC) $(LATEXMK) xelatex; do command -v $$t >/dev/null 2>&1 || missing="$$missing $$t"; done; \
if [ -n "$$missing" ]; then \
echo "Error: make docs needs:$$missing"; \
echo " sudo apt-get install -y pandoc latexmk texlive-xetex texlive-latex-extra texlive-pictures texlive-fonts-recommended fonts-dejavu fonts-dejavu-extra"; \
echo " (or a user-local TinyTeX + pandoc in ~/.local; see docs/book/README.md)"; \
exit 1; \
fi
# --id-prefix keeps heading labels unique across documents that reuse
# section names ("Summary", "Open questions", ...).
define chapter_rule
$(GEN)/$(call chapter_name,$(1)).tex: $(call chapter_src,$(1)) $(PANDOC_FILTERS) | tools
@mkdir -p $(GEN)
@echo " PANDOC $$< -> $$@"
@$(PANDOC) $(PANDOC_FLAGS) --id-prefix=$(call chapter_name,$(1))- $$< -o $$@
endef
$(foreach c,$(CHAPTERS),$(eval $(call chapter_rule,$(c))))
# Pandoc's syntax-highlighting macros, taken from the installed pandoc so the
# fragments and their macros always come from the same version.
$(GEN)/pandoc-highlighting.tex: $(BOOK_DIR)/pandoc/highlighting.latex | tools
@mkdir -p $(GEN)
@printf '```c\nx\n```\n' | $(PANDOC) -f gfm -t latex -s --template=$< -o $@
# Per-build facts the books print: the commit every \datasource refers to,
# the version (git describe: a release's tag, or the tag and the commits since),
# and which board appendix (if any) is included.
define meta_cmds
@mkdir -p $(dir $@)
@{ \
c=$$(git rev-parse --short=12 HEAD 2>/dev/null || echo unknown); \
git diff --quiet HEAD -- 2>/dev/null || c="$$c (+ uncommitted changes)"; \
printf '\\newcommand{\\bookcommit}{%s}\n' "$$c"; \
printf '\\newcommand{\\bookdate}{%s}\n' "$$(date -u +%Y-%m-%d)"; \
printf '\\newcommand{\\bookversion}{%s}\n' "$$(git describe --tags --always 2>/dev/null || echo unknown)"; \
printf '\\def\\bookboard{%s}\n' "$(BOARD_NAME)"; \
$(if $(BOARD),printf '\\newcommand{\\bookboardappendix}{board-%s}\n' "$(BOARD)";) \
} > $@.tmp
@cmp -s $@.tmp $@ && rm -f $@.tmp || mv $@.tmp $@
endef
$(VAR_DIR)/meta.tex: FORCE
$(meta_cmds)
$(FABRIC_DIR)/meta.tex: FORCE
$(meta_cmds)
$(DICT_DIR)/meta.tex: FORCE
$(meta_cmds)
FORCE:
TEXINPUTS_BOOK := $(abspath $(VAR_DIR)):$(abspath $(GEN)):$(abspath $(BOOK_DIR)):
$(PDF): $(BOOK_DIR)/main.tex $(BOOK_DIR)/preamble.tex $(FRAGMENTS) $(GEN)/pandoc-highlighting.tex $(VAR_DIR)/meta.tex | tools
@echo " LATEXMK $(BOOK_DIR)/main.tex -> $@"
@TEXINPUTS=$(TEXINPUTS_BOOK) $(LATEXMK) -xelatex -interaction=nonstopmode -halt-on-error \
-file-line-error -outdir=$(VAR_DIR) $(BOOK_DIR)/main.tex > $(VAR_DIR)/latexmk.log 2>&1 || { \
grep -A4 -E '^(.*:[0-9]+:|!)' $(VAR_DIR)/main.log | head -40; \
echo "Error: LaTeX failed; full log: $(VAR_DIR)/main.log"; exit 1; }
@cp $(VAR_DIR)/main.pdf $@
@echo " PDF $@"
TEXINPUTS_FABRIC := $(abspath $(FABRIC_DIR)):$(abspath $(GEN)):$(abspath $(BOOK_DIR)):
$(FABRIC_PDF): $(BOOK_DIR)/fabric.tex $(BOOK_DIR)/preamble.tex $(FABRIC_FRAGS) $(GEN)/pandoc-highlighting.tex $(FABRIC_DIR)/meta.tex | tools
@echo " LATEXMK $(BOOK_DIR)/fabric.tex -> $@"
@TEXINPUTS=$(TEXINPUTS_FABRIC) $(LATEXMK) -xelatex -interaction=nonstopmode -halt-on-error \
-file-line-error -outdir=$(FABRIC_DIR) $(BOOK_DIR)/fabric.tex > $(FABRIC_DIR)/latexmk.log 2>&1 || { \
grep -A4 -E '^(.*:[0-9]+:|!)' $(FABRIC_DIR)/fabric.log | head -40; \
echo "Error: LaTeX failed; full log: $(FABRIC_DIR)/fabric.log"; exit 1; }
@cp $(FABRIC_DIR)/fabric.pdf $@
@echo " PDF $@"
# the live word list needs the hosted v4; the check fails the build on a word with no entry, an
# unfinished entry, or an entry for a word the system no longer has
$(GEN)/dictionary-words.md: $(DICT_TSV) v4/tools/mkdictionary.py FORCE | tools
@mkdir -p $(GEN)
@$(MAKE) --no-print-directory -C v4 hosted >/dev/null
@python3 v4/tools/mkdictionary.py check $(DICT_TSV)
@python3 v4/tools/mkdictionary.py md $(DICT_TSV) $@
$(GEN)/dictionary-words.tex: $(GEN)/dictionary-words.md $(PANDOC_FILTERS) | tools
@echo " PANDOC $< -> $@"
@$(PANDOC) $(PANDOC_FLAGS) --id-prefix=dictionary- $< -o $@
TEXINPUTS_DICT := $(abspath $(DICT_DIR)):$(abspath $(GEN)):$(abspath $(BOOK_DIR)):
$(DICT_PDF): $(BOOK_DIR)/dictionary.tex $(BOOK_DIR)/preamble.tex $(GEN)/dictionary-words.tex $(GEN)/pandoc-highlighting.tex $(DICT_DIR)/meta.tex | tools
@echo " LATEXMK $(BOOK_DIR)/dictionary.tex -> $@"
@TEXINPUTS=$(TEXINPUTS_DICT) $(LATEXMK) -xelatex -interaction=nonstopmode -halt-on-error \
-file-line-error -outdir=$(DICT_DIR) $(BOOK_DIR)/dictionary.tex > $(DICT_DIR)/latexmk.log 2>&1 || { \
grep -A4 -E '^(.*:[0-9]+:|!)' $(DICT_DIR)/dictionary.log | head -40; \
echo "Error: LaTeX failed; full log: $(DICT_DIR)/dictionary.log"; exit 1; }
@cp $(DICT_DIR)/dictionary.pdf $@
@echo " PDF $@"