diff options
Diffstat (limited to '')
| -rwxr-xr-x | mkdeps.sh | 383 |
1 files changed, 280 insertions, 103 deletions
@@ -4,163 +4,340 @@ set -eu export LANG=POSIX.UTF-8 +## -m so a path need not exist: po4a's outputs are named here +## before po4a has written them --- naming them is what the rules +## being generated are for --- and realpath without it fails on +## the first one, taking the whole script with it under set -e. +## For a path that does exist the answer is the same either way. normalize() { - xargs realpath --relative-to=. | sort + ## realpath(1) did this, but it is a page of string work and + ## this runs it dozens of times: doing it here saves a + ## process and most of the time. + awk ' + { + n = split($0, c, "/") + top = 0 + for (i = 1; i <= n; i++) { + if (c[i] == "" || c[i] == ".") { + continue + } + if (c[i] == ".." && top > 0 && + out[top] != "..") { + top-- + continue + } + out[++top] = c[i] + } + p = "" + for (i = 1; i <= top; i++) { + p = p (i > 1 ? "/" : "") out[i] + } + if (p != "") { + print p + } + } + ' | sort } -pages() { - find src/pages/*/*.adoc -type f | normalize +## Every page there is: what the repository carries, plus what +## po4a writes from it. A translation need not exist yet -- on a +## fresh clone none of them do -- so it is named, not found. +universe() { + { + find src/content -type f -name '*.adoc' + page_translations + } | normalize | uniq } -articles() { - find src/collections/*/*/*/ -type f -name '*.adoc' | normalize +## The classifying rules read the masters, which the repository +## always carries; a translation is whatever its master was. +homes() { + find src/content/*/index.adoc -type f | normalize | + only_masters | with_translations | normalize } -slides() { - find src/slides/*/*/ -type f -name '*.adoc' | normalize +collections() { + grep -l '^:type: collection$' src/content/*/*/index.adoc | + normalize | only_masters | xargs -n1 dirname } indexes() { - find src/collections/*/*/index.adoc | normalize + all_collections | sed 's|$|/index.adoc|' | normalize +} + +## Every collection there is, translations included: the rules +## below emit a listing, a feed and a sortdata list for each, and +## a translated collection needs all three as much as its master. +all_collections() { + collections | sed 's|$|/index.adoc|' | with_translations | + sed 's|/index.adoc$||' | normalize } categories() { - grep -l '^:type: categories$' src/collections/*/*/*.adoc | normalize + grep -l '^:type: categories$' src/content/*/*/*.adoc | + normalize | only_masters | with_translations | normalize +} + +articles() { + collections | xargs -I% find % -mindepth 3 -name '*.adoc' | + normalize | only_masters | with_translations | normalize +} + +slides() { + find src/content -type f -name '*.eslaides' | normalize +} + +## Whatever no other rule claimed, at any depth. +pages() { + claimed="$(mktemp)" + { homes; indexes; categories; articles; } | sort -u > "$claimed" + universe | grep -vxF -f "$claimed" + rm -f "$claimed" } media() { - find src/content/ \( -type f -and \! -type l \) -and \( \ + # src/content/music is excluded: those .ogg files are score + # renderings, not published media, and the old site never + # offered them over BitTorrent. + find src/content/ -path 'src/content/music' -prune -or \ + \( \( -type f -and \! -type l \) -and \( \ -name '*.flac' -or \ -name '*.ogg' -or \ -name '*.webm' \ - \) + \) -print \) | normalize +} + +## A glob that matches nothing makes find exit non-zero, which +## under set -e would end the script. There is no music yet. +music() { + find src/content/music/*.ly 2>/dev/null | normalize || true } tarballs() { find src/content/ \( -type f -and \! -type l \) -and \( \ -name '*.tar.gz' \ - \) + \) | normalize +} + +## Short paths that stand in for a page's own, kept as symlinks +## beside it. Guarded like music() above: a site need not have any, +## and find exits non-zero on a directory that is not there. +shortener() { + find src/content/l -type l 2>/dev/null | normalize || true } extras() { media tarballs + shortener } -listings() { - indexes - categories +## Which pages are one another's translations. po4a already says +## so, set by set, so read it there rather than keep a second list +## that can quietly disagree with the first. +## The languages po4a translates into: whichever .po files exist. +langs() { + if [ -z "${LANGS_CACHE:-}" ]; then + LANGS_CACHE="$( + find po -maxdepth 1 -name '*.po' | + sed 's|.*/||; s|\.po$||' | sort + )" + export LANGS_CACHE + fi + echo "$LANGS_CACHE" } -files() { - pages - articles - listings +## One stanza per line, master first, then the files po4a writes +## from it. "$lang:" stands for every language, and "add_$lang:" +## names an addendum rather than an output. +po4a_sets() { + langs | tr '\n' ' ' | { + ## read hits EOF on the last field and reports it, which + ## under set -e would end the block before awk ran. + read -r all || true + awk -v langs="$all" ' + /^\[type:/ { set = "" } + /^\[type:/ || cont { + line = $0 + sub(/^\[type:[^]]*\][ \t]*/, "", line) + cont = (line ~ /\\$/) + sub(/[ \t]*\\$/, "", line) + n = split(line, f, /[ \t]+/) + nl = split(langs, L, " ") + for (i = 1; i <= n; i++) { + if (f[i] == "" || f[i] ~ /^add_/) continue + if (f[i] ~ /^\$lang:/) { + sub(/^\$lang:/, "", f[i]) + for (j = 1; j <= nl; j++) { + t = f[i] + gsub(/\$lang/, L[j], t) + set = set " " t + } + continue + } + sub(/^[a-z]+:/, "", f[i]) + set = set (set == "" ? "" : " ") f[i] + } + if (!cont && set != "") { print set; set = "" } + } + ' po/po4a.cfg + } } +## Everything po4a writes: every field of a stanza but the first. +translations() { + cut -d' ' -f2- "$(po4a_cache)" | + tr ' ' '\n' | grep . | normalize +} + +## Just the pages among them. The switcher offers a reader the +## same page in another language; the data files under src/i18n +## are not pages. +page_translations() { + translations | grep '\.adoc$' +} + +## Drops the translations from a list of paths, so a rule reads +## the masters whether or not po4a has run. +## po4a.cfg does not change while this runs, so parse it once: +## every selector below asks for the same answer. +po4a_cache() { + if [ -z "${PO4A_CACHE:-}" ]; then + PO4A_CACHE="$(mktemp)" + po4a_sets > "$PO4A_CACHE" + export PO4A_CACHE + fi + echo "$PO4A_CACHE" +} + +translations_cache() { + if [ -z "${TRANSLATIONS_CACHE:-}" ]; then + TRANSLATIONS_CACHE="$(mktemp)" + translations > "$TRANSLATIONS_CACHE" + export TRANSLATIONS_CACHE + fi + echo "$TRANSLATIONS_CACHE" +} + +only_masters() { + awk 'NR == FNR { t[$0]; next } !($0 in t)' \ + "$(translations_cache)" - +} + +## Reads masters on stdin, writes them and their translations. +with_translations() { + awk -v sets="$(po4a_cache)" ' + BEGIN { + while ((getline line < sets) > 0) { + n = split(line, f, /[ \t]+/) + for (i = 2; i <= n; i++) { + t[f[1]] = t[f[1]] " " f[i] + } + } + } + { + print + if ($0 in t) { + n = split(t[$0], g, " ") + for (i = 1; i <= n; i++) { + print g[i] + } + } + } + ' +} + +## Prime the caches here, in this shell. Every pipeline stage +## below runs in a subshell, so a cache filled inside one is gone +## by the next: filling them once, up here, is what makes them +## caches rather than seven identical computations. +PO4A_CACHE="$(mktemp)" +po4a_sets > "$PO4A_CACHE" +TRANSLATIONS_CACHE="$(mktemp)" +translations > "$TRANSLATIONS_CACHE" +LANGS_CACHE="$(find po -maxdepth 1 -name '*.po' | + sed 's|.*/||; s|\.po$||' | sort)" +export PO4A_CACHE TRANSLATIONS_CACHE LANGS_CACHE +trap 'rm -f "$PO4A_CACHE" "$TRANSLATIONS_CACHE"' EXIT pages | varlist 'pages.adoc' +homes | varlist 'homes.adoc' articles | varlist 'articles.adoc' -slides | varlist 'slides.adoc' +slides | varlist 'slides.eslaides' categories | varlist 'categories.adoc' indexes | varlist 'indexes.adoc' indexes | sed 's|/index\.adoc$|/feed.xml|' | varlist 'feeds.xml' -find src/content/img/ -name '*.svg' | varlist 'images.svg' +find src/content/img/ -name '*.svg' | sort | varlist 'images.svg' media | varlist 'sources.media' tarballs | varlist 'sources.tarballs' extras | varlist 'sources.extras' find po/*.po po/*.pot | varlist 'sources.po' +music | varlist 'music.ly' +find src/content/music/*.ogg 2>/dev/null | varlist 'music.ogg' +find src/content/music/*.ly.include 2>/dev/null | \ + varlist 'music.include' +# lilypond emits the score alongside the MIDI, from one run. +music | sed 's/^\(.*\)\.ly$/\1.pdf:\t\1.midi/' -{ - files | sed 's/^\(.*\)\.adoc$/\1.html/' - files | sed 's/^\(.*\)\.adoc$/\1.snippets/' - slides | sed 's/^\(.*\)\.adoc$/\1.pdf/' - indexes | sed 's|^\(.*\)/index\.adoc$|\1/feed.xml|' - media | sed 's/^\(.*\)$/\1.torrent/' -} | sed 's/^\(.*\)$/\1.gz:\t\1/' -printf '\n' - -files | sed 's/^\(.*\)\.adoc$/\1.htmlbody\t\1.snippets\t\1.conf:\t\1.adoc/' -files | sed 's/^\(.*\)\.adoc$/\1.html:\t\1.conf\t\1.htmlbody/' -printf '\n' - -files | sed 's/^\(.*\)\.adoc$/\1.updatedat-check:\t\1.conf/' -files | sed 's/^\(.*\)\.adoc$/\1.links-internal-check:\t\1.links/' -files | sed 's/^\(.*\)\.adoc$/\1.caslinks:\t\1.links/' -printf '\n' - -articles | sed 's/^\(.*\)\.adoc$/\1.feedentry:\t\1.conf\t\1.htmlbody/' -articles | sed 's/^\(.*\)\.adoc$/\1.sortdata:\t\1.conf/' -articles | sed 's/^\(.*\)\.adoc$/\1.categorydata:\t\1.conf/' -printf '\n' - -listings | sed 's/^\(.*\)\.adoc$/\1.htmlheader\t\1.htmlfooter:\t\1.conf/' -listings | sed 's/^\(.*\)\.adoc$/\1.htmllisting:\t\1.conf/' -listings | sed 's/^\(.*\)\.adoc$/\1.html:\t\1.htmlheader\t\1.htmlfooter/' -listings | sed 's/^\(.*\)\.adoc$/\1.html:\t\1.htmllisting\t\1.htmlbody/' -printf '\n' - -media | sed 's/^\(.*\)$/\1.torrent:\t\1/' -printf '\n' - -slides | sed 's/^\(.*\)\.adoc$/\1.ps:\t\1.adoc/' -slides | sed 's/^\(.*\)\.adoc$/\1.pdf:\t\1.ps/' -printf '\n' - - +## A short link points into the built tree, so on a fresh clone it +## dangles, and make --- which stats through a symlink --- reports no +## rule for a file that is right there. Name the page each one points +## at as what the link is made from. +shortener | while read -r link; do + target="$(dirname "$link")/$(readlink "$link")" + printf '%s:\t%s\n' "$link" \ + "$(printf '%s\n' "$target" | normalize)" +done -collectionentries() { - langlink="$1" - colllink="$2" - lang="$(basename "$langlink")" - c="$(printf '%s' "$colllink" | normalize)" - plural="$(cat src/names/categories/"$lang".txt)" - printf '\n\n' - name="$(basename "$c")" - art=articles."$lang" - find "$c"/*/ -type f -name '*.adoc' | varlist "$art.$name.adoc" - echo "$art.$name.sortdata = \$($art.$name.adoc:.adoc=.sortdata)" - echo "$art.$name.indexentry = \$($art.$name.adoc:.adoc=.indexentry)" - echo "$art.$name.feedentry = \$($art.$name.adoc:.adoc=.feedentry)" - echo "$art.$name.categorydata = \$($art.$name.adoc:.adoc=.categorydata)" +## What this site has, for mkwb to say what each artifact is +## built from. Finding them is the site's half: only it knows +## where its pages live, or that it keeps decks and no music. +{ + pages | sed 's/^/page\t/' + homes | sed 's/^/home\t/' + articles | sed 's/^/article\t/' + indexes | sed 's/^/index\t/' + categories | sed 's/^/category\t/' + slides | sed 's/^/slide\t/' + media | sed 's/^/media\t/' +} | mkwb deps src/config.json - printf '%s/sortdata.txt:\tdeps.mk\n' "$c" - printf '\tprintf %s $(%s.%s.sortdata) > $@\n\n' "'%s\n'" "$art" "$name" +## What po4a writes. Naming them here is what lets them be +## derived: deps.mk is written before make runs, so a translation +## that is not named cannot be built, and would have to be +## committed instead. +translations | varlist 'translations' - listings=" - feed.xml - index.htmllisting - $plural.htmllisting - $plural.txt - " - for lst in $listings; do - printf '%s/%s:\t%s/sortdata.txt\n' "$c" "$lst" "$c" - printf '%s/%s:\t$(%s.%s.sortdata)\n' "$c" "$lst" "$art" "$name" - done +## One run writes all of them, so they hang off a stamp rather +## than each invoking po4a for itself. +printf 'src/i18n.stamp: po/po4a.cfg %s\n' "$(find po -maxdepth 1 -name '*.po' | sort | tr '\n' ' ')" +printf '\tpo4a po/po4a.cfg\n' +printf '\ttouch $@\n\n' +printf '$(translations): src/i18n.stamp\n\n' - printf '%s/index.htmllisting\t' "$c" - printf '%s/%s.htmllisting:\t' "$c" "$plural" - printf '$(%s.%s.indexentry)\n' "$art" "$name" - printf '%s/%s.txt:\t' "$c" "$plural" - printf '$(%s.%s.categorydata)\n' "$art" "$name" +## The map mkwb conf reads to link a page to its translations, +## written out here so it cannot drift from po4a.cfg. +printf '\ntranslations.txt = src/translations.txt\n\n' +printf 'src/translations.txt: deps.mk\n' +printf '\tprintf %s \\\n' "'%s\\n'" +po4a_sets | grep '\.adoc' | sed "s/^/\t\t'/; s/\$/' \\\\/" +printf '\t\t> $@\n\n' - printf '%s/%s.txt\t' "$c" "$plural" - printf '%s/feed.xml:\t' "$c" - printf '$(%s.%s.feedentry)\n' "$art" "$name" - printf '%s/%s.htmllisting\t' "$c" "$plural" - printf '%s/%s.xml:\t' "$c" "$plural" - printf '%s/%s.txt\n' "$c" "$plural" +## The canonical rules, inlined so no include is needed +## at make time. +echo +mkwb rules - printf '%s/%s.xml.gz:\t%s/%s.xml\n' "$c" "$plural" "$c" "$plural" -} - -for langlink in src/collections/*; do - for colllink in "$langlink"/*; do - collectionentries "$langlink" "$colllink" - done -done +## Everything mkwb renders reads the site's wording table, which +## po4a writes. The rules above define the variables, so this +## comes after them. It has to name what renders rather than +## what a page is made of: an article never writes a .htmlheader +## of its own, so naming that missed every one of them. +printf '\n$(sources.html) $(sources.json): src/i18n.stamp\n' +printf '$(listings.htmllisting) $(categories.xml): src/i18n.stamp\n' +printf '$(articles.feedentry): src/i18n.stamp\n' |
