diff options
Diffstat (limited to 'pipeline')
| -rw-r--r-- | pipeline/README.md | 99 | ||||
| -rwxr-xr-x | pipeline/build.sh | 31 | ||||
| -rw-r--r-- | pipeline/config.sh | 37 | ||||
| -rwxr-xr-x | pipeline/one.sh | 38 | ||||
| -rwxr-xr-x | pipeline/pdf.sh | 26 | ||||
| -rwxr-xr-x | pipeline/read.sh | 47 | ||||
| -rwxr-xr-x | pipeline/run.sh | 19 | ||||
| -rwxr-xr-x | pipeline/write.sh | 89 |
8 files changed, 386 insertions, 0 deletions
diff --git a/pipeline/README.md b/pipeline/README.md new file mode 100644 index 0000000..59b98dc --- /dev/null +++ b/pipeline/README.md @@ -0,0 +1,99 @@ +# The pipeline: how to run the readers, the library and the writers + +Fixed scripts rather than a test suite. They produce output that is meant to +be **looked at**, and they double as the answer to "what do I type?". + +``` +config.sh where everything is, and where the output goes +build.sh build every reader, library and writer, in both languages +one.sh one tool, one document - the answer to "how do I run this?" +read.sh every reader and both libraries, over every document +write.sh every writer, in both languages, over every document +pdf.sh compile the .typ files that write.sh produced +run.sh all of the above, in order +``` + +## What you need first + +An **abstraction** to read: a directory holding a `.ssp` for each +document-language and a `.ocda.db` for each document, and, where there is +one, a `.sisupod` beside it. That is what spine writes: + +```sh +spine --abstraction --ocda-db --pod2 --output=<dir> <pod>... +``` + +Everything downstream reads those files and nothing else. **No tool here +parses markup**: parsing is spine's work, and the point of the exercise is +that the abstraction is enough without it. + +## Running it + +```sh +ARTEFACTS=<dir> OUT=<dir> sh pipeline/run.sh +``` + +`ARTEFACTS` is the directory above. `OUT` is where the output goes and +defaults to `./pipeline-out`. Both, and every path to a sibling repository, +are variables with defaults in `config.sh`; override them in the environment +rather than editing anything. + +`LIMIT=3` takes the first three documents, which is what to use while looking +at something. + +## Where the output goes + +``` +$OUT/read/ssp-d/ ssp-g/ the .ssp readers' summaries +$OUT/read/db-d/ db-g/ the .ocda.db readers' summaries +$OUT/read/dump-d/ dump-g/ the library's dump, from each serialisation +$OUT/write/text/{d,g}/ <doc>.txt +$OUT/write/md/{d,g}/ <doc>.md +$OUT/write/html/{d,g}/ <doc>.html +$OUT/write/epub/{d,g}/ <doc>.epub +$OUT/write/typst/{d,g}/<doc>/ <doc>.typ, and image/ beside it +$OUT/write/pdf/ <doc>.pdf +``` + +`d` is the D implementation and `g` the Gleam one, throughout. + +## One tool, one document + +```sh +sh pipeline/one.sh text-d <doc>.ssp +sh pipeline/one.sh md-g <doc>.ocda.db en +sh pipeline/one.sh html-d <doc>.sisupod +sh pipeline/one.sh dump-d <doc>.ocda.db en +sh pipeline/one.sh agree-g <doc>.ssp <doc>.ocda.db +``` + +The tool names are `<format>-<language>`: `text`, `md`, `html`, `epub`, +`typst` for the writers, and `ssp`, `db`, `dump`, `agree` for the readers and +the library; `-d` for D and `-g` for Gleam. + +Output goes to stdout, so a comparison is a pipeline. The epub and the typst +name a file instead, because an epub is a zip and a `.typ` names its images by +path: + +```sh +"$WRITERS/dlang/result/bin/ocda-write" --epub <doc>.ocda.db en --out x.epub +"$WRITERS/dlang/result/bin/ocda-write" --typst <doc>.ocda.db en --out d/x.typ +typst compile --root d d/x.typ d/x.pdf +``` + +## What the counts at the end mean + +Each stage prints a few numbers. They are not a pass, they are a way to see a +regression without reading 36 documents. + +| `all four dumps identical` | the library's whole claim: two serialisations, two languages, one model | +|---|---| +| `D == Gleam, <format>` | the writers agree, in unrelated languages, neither written from the other. This is what says the model is sufficient | +| `.ssp and .ocda.db give the same .typ` | a writer is not told which serialisation it was handed | +| `typst compiled silently` | a link to a label that does not exist is a typst compile error, so this says every internal link in every document resolves | + +## What this is not + +It is not the project's checking. The sweeps that hold each writer against an +oracle - `spine --text`, spine's scroll html, epubcheck, typst - are separate +and stricter. This is the pipeline you run to *look* at all of it at once. diff --git a/pipeline/build.sh b/pipeline/build.sh new file mode 100755 index 0000000..e4eac9c --- /dev/null +++ b/pipeline/build.sh @@ -0,0 +1,31 @@ +#!/bin/sh +# Build every reader, every library and every writer, in both languages. +# +# Each project is its own flake and builds on its own. The library has no +# public url yet, so the three that depend on it are given the local copy with +# --override-input; that argument goes away when it is published. +set -e +. "$(dirname "$0")/config.sh" + +build() { # <directory> [flake output] + dir=$1; out=${2:-} + printf '%-44s' "$(basename "$(dirname "$dir")")/$(basename "$dir") ${out}" + if ( cd "$dir" && nix build ${out:+"$out"} \ + --override-input ocda-lib "path:$OCDA_LIB" >/dev/null 2>&1 ) \ + || ( cd "$dir" && nix build ${out:+"$out"} >/dev/null 2>&1 ) + then echo ok + else echo FAILED; failed=1 + fi +} + +failed=0 +build "$OCDA_LIB/dlang" .#ocda-dump +build "$OCDA_LIB/dlang" .#ocda-agree +build "$OCDA_LIB/gleam" +build "$SSP_READER/dlang" +build "$SSP_READER/gleam" +build "$DB_READER/dlang" +build "$DB_READER/gleam" +build "$WRITERS/dlang" +build "$WRITERS/gleam" +exit $failed diff --git a/pipeline/config.sh b/pipeline/config.sh new file mode 100644 index 0000000..04c4bfd --- /dev/null +++ b/pipeline/config.sh @@ -0,0 +1,37 @@ +# Where everything is, and where the output goes. +# +# Every path is a variable with a default, and the defaults assume the layout +# these repositories are developed in: each project cloned beside the others, +# and this one among them. Nothing here reaches outside those clones, so a +# different arrangement needs only this file changed. +# +# Override any of them in the environment: +# +# OUT=/tmp/my-run sh pipeline/run.sh +# +# shellcheck shell=sh + +# this repository +WRITERS=${WRITERS:-$(cd "$(dirname "$0")/.." && pwd)} + +# the sibling repositories, by their own names. A clone puts each under its +# short name; where they sit relative to each other is the only local fact. +SIBLINGS=${SIBLINGS:-$(cd "$WRITERS/.." && pwd)} +OCDA_LIB=${OCDA_LIB:-$SIBLINGS/sisudoc-ocda-lib} +SSP_READER=${SSP_READER:-$SIBLINGS/sisudoc-ssp-reader} +DB_READER=${DB_READER:-$SIBLINGS/sisudoc-ocda-db-reader} + +# the abstraction artefacts to read: a directory holding .ssp files, and for +# each of them a .ocda.db and, where there is one, a .sisupod beside it. This +# is what spine writes with --abstraction --ocda-db --pod2; see pipeline/README. +ARTEFACTS=${ARTEFACTS:-} + +# where the output goes. One directory per stage, so a run can be looked at. +OUT=${OUT:-./pipeline-out} + +# the tools a pdf needs. Absent, the typst stage is skipped and says so. +TYPST=${TYPST:-typst} + +# how many documents to run. Empty means all of them; a number takes the first +# n, which is what to use while looking at something. +LIMIT=${LIMIT:-} diff --git a/pipeline/one.sh b/pipeline/one.sh new file mode 100755 index 0000000..18ffc5f --- /dev/null +++ b/pipeline/one.sh @@ -0,0 +1,38 @@ +#!/bin/sh +# One tool, one document: the answer to "how do I run this by hand?". +# +# sh pipeline/one.sh <tool> <file> [lang] +# +# where <tool> is one of +# +# ssp-d ssp-g the .ssp readers, D and Gleam +# db-d db-g the .ocda.db readers +# dump-d dump-g the library's canonical dump of the whole model +# agree-d agree-g the library's two loaders against each other +# text-d text-g the writers. Each takes a .ssp, a .ocda.db or a +# md-d md-g .sisupod and is not told which. +# html-d html-g +# epub-d epub-g these two write a file, so they take --out +# typst-d typst-g +# +# and <file> is a .ssp, a .ocda.db or a .sisupod. Output goes to stdout except +# for the epub and the typst, which name a file. +set -e +. "$(dirname "$0")/config.sh" +tool=${1:?usage: one.sh <tool> <file> [lang]} +file=${2:?usage: one.sh <tool> <file> [lang]} +lang=${3:-} + +case $tool in + ssp-d) exec "$SSP_READER/dlang/result/bin/ssp-reader" "$file" ;; + ssp-g) exec "$SSP_READER/gleam/result/bin/ssp_reader" "$file" ;; + db-d) exec "$DB_READER/dlang/result/bin/ocda-db-reader" "$file" ;; + db-g) exec "$DB_READER/gleam/result/bin/ocda-db-reader" "$file" ;; + dump-d) exec "$OCDA_LIB/dlang/result/bin/ocda-dump" "$file" $lang ;; + dump-g) exec "$OCDA_LIB/gleam/result/bin/ocda-lib" dump "$file" $lang ;; + agree-d) exec "$OCDA_LIB/dlang/result/bin/ocda-agree" "$file" $lang ;; + agree-g) exec "$OCDA_LIB/gleam/result/bin/ocda-lib" agree "$file" $lang ;; + *-d) exec "$WRITERS/dlang/result/bin/ocda-write" "--${tool%-d}" "$file" $lang ;; + *-g) exec "$WRITERS/gleam/result/bin/ocda-write" "--${tool%-g}" "$file" $lang ;; + *) echo "one.sh: unknown tool $tool" >&2; exit 2 ;; +esac diff --git a/pipeline/pdf.sh b/pipeline/pdf.sh new file mode 100755 index 0000000..a769137 --- /dev/null +++ b/pipeline/pdf.sh @@ -0,0 +1,26 @@ +#!/bin/sh +# Compile every .typ the write stage produced, into $OUT/write/pdf/. +# +# typst is the only thing this stage needs, and it is a single binary. A link +# to a label that does not exist is a compile error, so a stage that says +# nothing has also said that every internal link in every document resolves. +set -e +. "$(dirname "$0")/config.sh" +command -v "$TYPST" >/dev/null 2>&1 || { + echo "pdf.sh: no typst on the path; set TYPST=/path/to/typst"; exit 0; } +mkdir -p "$OUT/write/pdf" +n=0; ok=0 +for typ in "$OUT"/write/typst/d/*/*.typ; do + [ -f "$typ" ] || continue + dir=$(dirname "$typ"); stem=$(basename "$typ" .typ) + n=$((n+1)) + if "$TYPST" compile --root "$dir" "$typ" "$OUT/write/pdf/$stem.pdf" \ + > "$OUT/write/pdf/$stem.log" 2>&1 && [ ! -s "$OUT/write/pdf/$stem.log" ] + then ok=$((ok+1)) + else echo "TYPST $stem: $(head -3 "$OUT/write/pdf/$stem.log" | tr '\n' ' ' | cut -c1-140)" + fi +done +echo +echo "documents typeset: $n" +echo "typst compiled silently $ok" +echo " (so every internal link resolves)" diff --git a/pipeline/read.sh b/pipeline/read.sh new file mode 100755 index 0000000..b3129a5 --- /dev/null +++ b/pipeline/read.sh @@ -0,0 +1,47 @@ +#!/bin/sh +# Every reader and both libraries, over every document, into $OUT/read/. +# +# What each produces: +# +# read/ssp-{d,g}/<doc>.txt the .ssp readers' summary +# read/db-{d,g}/<doc>.txt the .ocda.db readers' summary +# read/dump-{d,g}/<doc>.ssp the library's dump, loaded from the .ssp +# read/dump-{d,g}/<doc>.db the same, loaded from the .ocda.db +# +# The dumps are the interesting ones. All four of them should be identical: +# the two serialisations say the same thing, and the two languages read it the +# same way. `diff` says so, and the summary at the end counts it. +set -e +. "$(dirname "$0")/config.sh" +: "${ARTEFACTS:?set ARTEFACTS to a directory of .ssp and .ocda.db files}" +mkdir -p "$OUT/read" +for d in ssp-d ssp-g db-d db-g dump-d dump-g; do mkdir -p "$OUT/read/$d"; done + +list=$(find "$ARTEFACTS" -name '*.ssp' | sort) +[ -n "$LIMIT" ] && list=$(echo "$list" | head -n "$LIMIT") + +n=0; same=0 +for ssp in $list; do + stem=$(basename "$ssp" .ssp); lang=${stem##*.}; uid=${stem%.*} + db=$(find "$ARTEFACTS" -name "$uid.ocda.db" | head -1) + n=$((n+1)) + sh "$(dirname "$0")/one.sh" ssp-d "$ssp" > "$OUT/read/ssp-d/$stem.txt" + sh "$(dirname "$0")/one.sh" ssp-g "$ssp" > "$OUT/read/ssp-g/$stem.txt" + sh "$(dirname "$0")/one.sh" dump-d "$ssp" > "$OUT/read/dump-d/$stem.ssp" + sh "$(dirname "$0")/one.sh" dump-g "$ssp" > "$OUT/read/dump-g/$stem.ssp" + [ -n "$db" ] || continue + sh "$(dirname "$0")/one.sh" db-d "$db" > "$OUT/read/db-d/$stem.txt" + sh "$(dirname "$0")/one.sh" db-g "$db" > "$OUT/read/db-g/$stem.txt" + sh "$(dirname "$0")/one.sh" dump-d "$db" "$lang" > "$OUT/read/dump-d/$stem.db" + sh "$(dirname "$0")/one.sh" dump-g "$db" "$lang" > "$OUT/read/dump-g/$stem.db" + # the four-way agreement, which is the library's whole claim + if cmp -s "$OUT/read/dump-d/$stem.ssp" "$OUT/read/dump-d/$stem.db" \ + && cmp -s "$OUT/read/dump-g/$stem.ssp" "$OUT/read/dump-g/$stem.db" \ + && cmp -s "$OUT/read/dump-d/$stem.ssp" "$OUT/read/dump-g/$stem.ssp" + then same=$((same+1)) + else echo "DUMPS DIFFER $stem" + fi +done +echo +echo "documents read: $n" +echo "all four dumps identical $same" diff --git a/pipeline/run.sh b/pipeline/run.sh new file mode 100755 index 0000000..811514e --- /dev/null +++ b/pipeline/run.sh @@ -0,0 +1,19 @@ +#!/bin/sh +# The whole pipeline: build, read, write, typeset. +# +# ARTEFACTS=<dir of .ssp and .ocda.db> OUT=<dir> sh pipeline/run.sh +# +# Nothing here is a test in the pass/fail sense; it is a run whose output is +# meant to be looked at. The counts it prints are there so that a change that +# breaks something is visible without reading 36 documents. +set -e +here=$(dirname "$0") +. "$here/config.sh" +: "${ARTEFACTS:?set ARTEFACTS to a directory of .ssp and .ocda.db files}" +mkdir -p "$OUT" +echo "== build"; sh "$here/build.sh" +echo; echo "== read"; sh "$here/read.sh" +echo; echo "== write"; sh "$here/write.sh" +echo; echo "== pdf"; sh "$here/pdf.sh" +echo +echo "output in $OUT" diff --git a/pipeline/write.sh b/pipeline/write.sh new file mode 100755 index 0000000..7e1bead --- /dev/null +++ b/pipeline/write.sh @@ -0,0 +1,89 @@ +#!/bin/sh +# Every writer, in both languages, over every document, into $OUT/write/. +# +# write/text/{d,g}/<doc>.txt +# write/md/{d,g}/<doc>.md +# write/html/{d,g}/<doc>.html +# write/epub/{d,g}/<doc>.epub +# write/typst/{d,g}/<doc>/<doc>.typ and image/ beside it +# +# Each writer is given the .ocda.db, because that carries the images. The .ssp +# is used as well, for the one comparison that matters at this level: a writer +# is not told which serialisation it was handed, so the two must agree. +# +# The summary counts, for each format, how many documents the two languages' +# writers agreed on byte for byte. That is the check that says the model is +# sufficient: neither writer was written from the other. +set -e +. "$(dirname "$0")/config.sh" +: "${ARTEFACTS:?set ARTEFACTS to a directory of .ssp and .ocda.db files}" + +list=$(find "$ARTEFACTS" -name '*.ssp' | sort) +[ -n "$LIMIT" ] && list=$(echo "$list" | head -n "$LIMIT") + +for fmt in text md html epub typst; do + for impl in d g; do mkdir -p "$OUT/write/$fmt/$impl"; done +done + +n=0 +agree_text=0; agree_md=0; agree_html=0; agree_epub=0; agree_typst=0 +forms=0 +for ssp in $list; do + stem=$(basename "$ssp" .ssp); lang=${stem##*.}; uid=${stem%.*} + db=$(find "$ARTEFACTS" -name "$uid.ocda.db" | head -1) + src=${db:-$ssp} + [ -n "$db" ] && args="$src $lang" || args="$src" + n=$((n+1)) + one() { sh "$(dirname "$0")/one.sh" "$@"; } + + one text-d $args > "$OUT/write/text/d/$stem.txt" + one text-g $args > "$OUT/write/text/g/$stem.txt" + cmp -s "$OUT/write/text/d/$stem.txt" "$OUT/write/text/g/$stem.txt" \ + && agree_text=$((agree_text+1)) || echo "TEXT DIFFERS $stem" + + one md-d $args > "$OUT/write/md/d/$stem.md" + one md-g $args > "$OUT/write/md/g/$stem.md" + cmp -s "$OUT/write/md/d/$stem.md" "$OUT/write/md/g/$stem.md" \ + && agree_md=$((agree_md+1)) || echo "MD DIFFERS $stem" + + one html-d $args > "$OUT/write/html/d/$stem.html" + one html-g $args > "$OUT/write/html/g/$stem.html" + cmp -s "$OUT/write/html/d/$stem.html" "$OUT/write/html/g/$stem.html" \ + && agree_html=$((agree_html+1)) || echo "HTML DIFFERS $stem" + + "$WRITERS/dlang/result/bin/ocda-write" --epub $args \ + --out "$OUT/write/epub/d/$stem.epub" + "$WRITERS/gleam/result/bin/ocda-write" --epub $args \ + --out "$OUT/write/epub/g/$stem.epub" + cmp -s "$OUT/write/epub/d/$stem.epub" "$OUT/write/epub/g/$stem.epub" \ + && agree_epub=$((agree_epub+1)) || echo "EPUB DIFFERS $stem" + + # a .typ names its images by path, so it and they go in a directory together + for impl in d g; do mkdir -p "$OUT/write/typst/$impl/$stem"; done + "$WRITERS/dlang/result/bin/ocda-write" --typst $args \ + --out "$OUT/write/typst/d/$stem/$stem.typ" + "$WRITERS/gleam/result/bin/ocda-write" --typst $args \ + --out "$OUT/write/typst/g/$stem/$stem.typ" + cmp -s "$OUT/write/typst/d/$stem/$stem.typ" \ + "$OUT/write/typst/g/$stem/$stem.typ" \ + && agree_typst=$((agree_typst+1)) || echo "TYPST DIFFERS $stem" + + # and the claim that a writer cannot tell the serialisations apart. A .typ + # names its images rather than carrying them, so this one holds for it even + # though only the .ocda.db has the image files. + if [ -n "$db" ]; then + "$WRITERS/dlang/result/bin/ocda-write" --typst "$ssp" > "$OUT/from-ssp.typ" + if cmp -s "$OUT/from-ssp.typ" "$OUT/write/typst/d/$stem/$stem.typ" + then forms=$((forms+1)) + else echo "FORMS DIFFER $stem" + fi + fi +done +echo +echo "documents written: $n" +echo "D == Gleam, text $agree_text" +echo "D == Gleam, markdown $agree_md" +echo "D == Gleam, html $agree_html" +echo "D == Gleam, epub $agree_epub" +echo "D == Gleam, typst $agree_typst" +echo ".ssp and .ocda.db give the same .typ $forms" |
