# cantarell-fonts-verify.awk -- turn one find, one sha256sum, one dpkg file list # and one fc-scan into the payload, inventory, packaging-footprint and # fontconfig-derivation cases. # # WHY ONE AWK AND NOT A SHELL LOOP. On this arm a process's memory is not # returned when it exits and /bin/sh is a 16 MB wasm module, so a suite's cost is # dominated by how many programs it starts. Everything below is derived from four # files the .test already produced with four execs. # # INPUTS: # REF the reference manifest, " " # SUMS `sha256sum` over the one payload file, " " # INV `find ... -printf '%p %y %s %m %l\n'` over the payload font directory # AND the two co-tenant font directories # PKGF /var/lib/dpkg/info/cantarell-fonts.list -- one path per line # FACES the native fontconfig control, "||...||" # SCAN this image's own fc-scan over the one installed file, same format # # THIS PORT GETS A DIRECTORY OF ITS OWN, WHICH NO FONT PORT BEFORE IT DID. The # recipe's fontsdir defaults to ${datadir}/fonts/cantarell and the recipe does not # override it, so /usr/share/fonts/cantarell is created by this package and holds # nothing else. That removes the whole class of co-tenancy repair the last four # font ports each had to do -- there is no neighbour to scope a census against, # and no neighbour's suite to amend. What is asserted instead is the other # direction: that the two directories this port does NOT write to are unchanged, # so a packaging or fontconfig change that moved the fonts already here is a # named red rather than a silent one. # # THE FILES VARIABLE NAMES A DIRECTORY THE BUILD NEVER CREATES, AND THAT IS # ASSERTED RATHER THAN LEFT TO LOOK LIKE AN OMISSION. The recipe sets # FILES:${PN} = "${datadir}/fonts ${datadir}/fontconfig" # as an ASSIGNMENT, and ${datadir}/fontconfig is over-declaration: nothing in the # tarball installs there, on either the prebuilt or the from-source path, and # there is no fontconfig fragment anywhere in cantarell-fonts-0.303.1. The # directory nevertheless EXISTS on the image, owned by the `fontconfig` package # (conf.avail/ and conf.d/ live under it), so a case asserting "the directory is # absent" would be wrong and a case asserting "it is empty" would fail for a # reason that has nothing to do with this port. pkgfiles-* asserts the only thing # that is actually this port's business: the cantarell-fonts package's own file # list contains exactly one path, and no path under ${datadir}/fontconfig. function emit(id, ok, detail) { if (ok) printf "BST ok %s/%s\n", S, id else printf "BST not ok %s/%s : %s\n", S, id, substr(detail, 1, 100) } function base(p) { sub(/^.*\//, "", p); return p } BEGIN { S = "cantarell-fonts" PROCINFO["sorted_in"] = "@ind_str_asc" FDIR = "/usr/share/fonts/cantarell" COT1 = "/usr/share/fonts/ttf" # 12 Liberation + 10 Vera COT2 = "/usr/share/fonts/truetype" # 1 Inconsolata + 4 Gentium + 7 Droid # + 14 Source Code Pro (BRINTOS-1455) # + 12 ttf-liberation (BRINTOS-1721) # + 20 Roboto (BRINTOS-1723) # + 14 Ubuntu Font Family (BRINTOS-1724) # + 6 Hunky (BRINTOS-1727) # + 1 Abyssinica SIL (BRINTOS-1728) WANT_COT1 = 22 WANT_COT2 = 137 NFIELD = 12 # fields on an fcfaces/fc-scan line NFILE = 1 # payload files this package installs NPAT = 6 # fc-scan patterns the ONE file produces: 5 named instances + 1 variable OURFILE = "Cantarell-VF.otf" } FILENAME == REF && /^#/ { next } FILENAME == REF && NF == 3 { WSIZE[$3] = $1; WSUM[$3] = $2; NREF++; next } FILENAME == SUMS && NF >= 2 { p = $2; sub(/^\*/, "", p); GSUM[p] = $1; next } FILENAME == INV && NF >= 4 { ITYPE[$1] = $2; ISIZE[$1] = $3; IMODE[$1] = $4; next } FILENAME == PKGF && NF >= 1 { PKGPATH[$0] = 1; NPKG++; next } FILENAME == FACES && /^#/ { next } FILENAME == FACES && NF >= 1 { split($0, a, "|"); WFC[a[12] "|" a[11]] = $0; NFC++; next } FILENAME == SCAN && NF >= 1 { split($0, a, "|"); GFC[a[12] "|" a[11]] = $0; NGFC++; next } END { # THE INPUTS WERE REALLY READ. Every case below is a comparison, and a # comparison over two empty tables is vacuously true -- the failure that # reads as a full green. emit("ref-count", NREF == NFILE, "the manifest carried " NREF " entries, want " NFILE) emit("faces-count", NFC == NPAT, "the fcfaces control carried " NFC " lines, want " NPAT) # ------------------------------------------------------------ the payload # Size, bytes, type and mode in one case. %y catches a symlink where a # regular file is meant and %m the mode, neither of which `test -f` sees; the # digest catches a file of the right length that is not the right file. # # MODE 644 IS A REAL ASSERTION, NOT BOILERPLATE. meson's install_data # defaults to 0644 and the upstream blob is 0644 in the tarball, so this row # says the install went through the class's own path rather than a copy that # preserved something else. for (p in WSIZE) { id = "file-" base(p) if (!(p in ITYPE)) { emit(id, 0, p " is absent"); continue } if (ITYPE[p] != "f") { emit(id, 0, p " is type '" ITYPE[p] "', want a regular file"); continue } if (ISIZE[p] != WSIZE[p]) { emit(id, 0, p " is " ISIZE[p] " bytes, want " WSIZE[p]); continue } if (IMODE[p] != "644") { emit(id, 0, p " is mode " IMODE[p] ", want 644"); continue } if (!(p in GSUM)) { emit(id, 0, p " was not digested"); continue } if (GSUM[p] != WSUM[p]) { emit(id, 0, p " sha256 " substr(GSUM[p],1,16) "..., want " substr(WSUM[p],1,16) "..."); continue } emit(id, 1) } # ---------------------------------------------------- the port's directory # THE DIRECTORY IS THIS PACKAGE'S ALONE, so unlike every font suite before it # this census is NOT scoped by basename -- it is entitled to say the whole # directory. A future font port that chose the same fontsdir would redden # this, which is the correct outcome: it would also be sharing a directory # with a suite that assumes it does not, and the two facts should surface # together rather than one silently. n = 0; extra = "" for (p in ITYPE) { if (p !~ ("^" FDIR "/")) continue if (ITYPE[p] != "f") { extra = extra " " base(p) "(type " ITYPE[p] ")"; continue } n++ if (base(p) != OURFILE) extra = extra " " base(p) } emit("dir-count", n == NFILE, FDIR " holds " n " regular file(s), want " NFILE) emit("dir-no-extras", extra == "", FDIR " also holds:" extra) # ------------------------------------------- the package's own file list # WHAT THE PACKAGE CLAIMS, not what is on disk. file-* above would look # identical if a second package had installed the same bytes, and dir-count # would look identical if this package ALSO owned paths elsewhere -- which is # exactly what FILES:${PN} invites, since it names ${datadir}/fontconfig as # well. dpkg's own list is the only place the package's real footprint is # visible on target. # # dpkg lists the directories it created as well as the files, so the count # is of the FILE entries: the four parent directories (/usr, /usr/share, # /usr/share/fonts, /usr/share/fonts/cantarell) are legitimately in the list # and are not what this case is about. nf = 0; nfc = 0; stray = "" for (p in PKGPATH) { if (p ~ /^\/usr\/share\/fontconfig(\/|$)/) { nfc++; continue } if (!(p in ITYPE) || ITYPE[p] != "f") continue # a directory entry nf++ if (p != FDIR "/" OURFILE) stray = stray " " p } emit("pkgfiles-loaded", NPKG > 0, "/var/lib/dpkg/info/cantarell-fonts.list was empty or unreadable") emit("pkgfiles-count", nf == NFILE, "cantarell-fonts owns " nf " file(s), want " NFILE ";" stray) emit("pkgfiles-no-fontconfig", nfc == 0, "cantarell-fonts owns " nfc " path(s) under /usr/share/fontconfig -- FILES names ${datadir}/fontconfig but nothing in the tarball installs there") # ------------------------------------------------------- the co-tenants # THIS PORT MUST NOT TOUCH THE TWO DIRECTORIES THAT WERE ALREADY HERE. It # writes to neither, so these are the cases that say a fontconfig or # packaging change did not move the faces the image already scans on every # fc-cache. Both neighbourhoods assert their own payloads from the other # side, so a disagreement between the suites is visible rather than silent. # # WANT_COT2 MOVED 12 -> 26 FOR BRINTOS-1455, 26 -> 38 FOR BRINTOS-1721, # 38 -> 58 FOR BRINTOS-1723, 58 -> 72 FOR BRINTOS-1724 AND 72 -> 78 FOR # BRINTOS-1727, 78 -> 79 FOR BRINTOS-1728 AND 79 -> 137 FOR BRINTOS-1729, # AND THAT IS THIS CASE # WORKING. This is a WHOLE-DIRECTORY count, so it is meant to move # every time a font port lands in COT2; ttf-liberation added twelve faces, # ttf-roboto twenty (12 Roboto-* + 8 RobotoCondensed-*), the Ubuntu Font # Family fourteen (4 UbuntuMono-* + 10 Ubuntu-*). Each of the three was cut # from a base that predated the others and moved this constant for its own # faces alone, so the sides do NOT compose by taking any one of them: each # merge that lands later adds its own contribution to what is already there. # # HOW 72 WAS ESTABLISHED, AND WHAT IT IS NOT. It is NOT the sum in a conflict # marker, which is the thing that has been wrong before. It is also NOT a # count of the merged tree's staged rootfs: that build was run (2026-08-29, # hz1) and reached task 17128 of 17246 WITHOUT producing one, because nmap # and python3 fail do_compile on brintos/hwjs-cc#81's # vla/dynamic-alloca-address-escape-opaque-sink refusal since BRINTOS-902 # landed shared libc -- so do_rootfs never ran and there was nothing to # count. See BRINTOS-1430 and BRINTOS-1767; nmap is not in # BRINTOS_HWJS81_EXCLUDED at all. # # What it IS: the union of four file sets, each measured on its own and # proved DISJOINT BY BASENAME (72 names, 72 distinct, no collision) -- # # 26 the pre-existing tenants, live-counted in the staged rootfs of this # branch's own pre-merge image build (Droid 7, Gentium 4, Inconsolata # 1, Source Code Pro 14) # 14 Ubuntu Font Family, from ttf-ubuntu-font-family-manifest.txt, read # off the built .debs and byte-compared to the upstream zip # 20 Roboto, from ttf-roboto-manifest.txt (BRINTOS-1723, live-confirmed # there at 58) # 12 ttf-liberation, from ttf-liberation-manifest.txt (BRINTOS-1721) # # 78 WAS DERIVED THE SAME WAY, AT THE MERGE, BY THE FIFTH PORT. # BRINTOS-1727 (ttf-hunkyfonts) adds six -- HunkySans-{Bold,BoldOblique, # Oblique,Roman}.ttf and HunkySerif-{Bold,Roman}.ttf -- and its own 26 is # the same pre-existing set counted above, live in the staged rootfs of its # pre-merge image build (which held 32). Not the sum in the conflict marker: # the union of ALL EIGHT truetype manifests in this merged tree was swept by # basename and came to 78 distinct names, with the per-manifest # contributions summing to 78 and NO collision between any two sets -- # 1 Inconsolata, 4 Gentium, 7 Droid, 14 Source Code Pro, 12 ttf-liberation, # 20 Roboto, 14 Ubuntu, 6 Hunky. # # STILL NOT A LIVE COUNT OF A MERGED ROOTFS, for the reason above: nmap and # python3 still fail do_compile on this base, so the merged image still # cannot reach do_rootfs. Whoever lands the seventh port should re-derive # it the same way, and prefer a live count the moment the image builds # again. # # 79 IS THE SIXTH PORT'S NUMBER, RE-DERIVED THE SAME WAY AND NOT TAKEN FROM # THE CONFLICT MARKER. BRINTOS-1728 (ttf-abyssinica) adds ONE face, # AbyssinicaSIL-Regular.ttf. The union of every truetype-bearing manifest in # this merged tree was swept by basename again rather than 78 being taken on # trust: 78 distinct names on `main` alone -- 1 Inconsolata, 4 Gentium, # 7 Droid, 14 Source Code Pro, 12 ttf-liberation, 20 Roboto, 14 Ubuntu, # 6 Hunky -- and 79 with this port's face, with no collision between any two # sets. # # TWO MANIFESTS THAT REACHED `main` AFTER 78 WAS WRITTEN CONTRIBUTE NOTHING # HERE, and were checked rather than assumed: ttf-pt-sans installs eight # faces under /usr/share/fonts/X11/TTF, and ttf-liberation-sans-narrow four # under /usr/share/fonts/TTF. Neither directory is COT1 or COT2 -- and note # /usr/share/fonts/TTF is NOT COT1's /usr/share/fonts/ttf, which is why # WANT_COT1 is still 22 and why reading these two paths case-insensitively # would get both constants wrong. # These are TOTALS, not basename scopes -- deliberately, because cantarell # owns a directory of its own and is entitled to say the neighbours' two are # exactly as it found them. The price is one line of maintenance per font # port that lands in either, and source-code-pro-fonts is the first to # collect it: fourteen SourceCodePro-*.otf joined COT2. 99-source-code-pro- # fonts asserts the same twelve pre-existing faces from its own side, BY # NAME, so a displacement is still localised to the package that caused it # even though this case only reports a count. c1 = 0; c2 = 0 for (p in ITYPE) { if (ITYPE[p] != "f") continue if (p ~ ("^" COT1 "/")) c1++ if (p ~ ("^" COT2 "/")) c2++ } emit("cotenant-ttf", c1 == WANT_COT1, COT1 " holds " c1 " files, want " WANT_COT1 " (12 Liberation + 10 Vera)") emit("cotenant-truetype", c2 == WANT_COT2, COT2 " holds " c2 " files, want " WANT_COT2 " (1 Inconsolata + 4 Gentium + 7 Droid + 14 Source Code Pro + 12 ttf-liberation + 20 Roboto + 14 Ubuntu + 6 Hunky + 1 Abyssinica SIL + 58 TLWG)") # ------------------------------- fontconfig's own reading of the six patterns # ONE FILE, SIX PATTERNS. See the fcfaces control's header for why, and for # why the key is | rather than the basename every other font # suite on this image uses. # # FIELD BY FIELD AGAINST THE NATIVE CONTROL, not as one string compare: a # whole-line case would name the pattern and nothing else, and these fields # fail for different reasons. emit("fcq-count", NGFC == NPAT, "fc-scan produced " NGFC " pattern(s) for the one file, want " NPAT) split("file family style slant weight spacing postscriptname fontformat scalable outline variable index", FN, " ") for (k in WFC) { id = "fcq-" k; gsub(/\|/, "-", id) if (!(k in GFC)) { emit(id, 0, "fc-scan produced no pattern at index/variable " k); continue } nw = split(WFC[k], w, "|"); ng = split(GFC[k], g, "|") if (nw != NFIELD || ng != NFIELD) { emit(id, 0, "field count " ng ", control " nw ", want " NFIELD); continue } bad = "" for (i = 1; i <= NFIELD; i++) if (w[i] != g[i]) bad = bad " " FN[i] "='" substr(g[i],1,24) "'(want '" substr(w[i],1,24) "')" emit(id, bad == "", k ":" bad) } # THE THREE DERIVATIONS THIS PORT TURNS ON, NAMED SEPARATELY. fcq-* above # already rejects any input these reject, so on their own they would be the # subsumed-guard mistake -- they are here because each localises a DIFFERENT # downstream consequence and a reviewer reading a red needs to know which. # # spacing decides whether this port could answer `fc-match monospace` and # displace the generic ttf-inconsolata holds. It cannot: unset on # all six. A single "unset" case is right here where 99-ttf-droid # needed two, because that port shipped a mono face and this one # does not. # format CFF on all six. 99-ttf-inconsolata asserts CFF for a STATIC # `CFF ` font and 99-ttf-gentium/99-ttf-droid assert TrueType; # this is the only place CFF2 is exercised, and fontconfig reports # it under the same name. # variable EXACTLY ONE of the six patterns is the variable font itself. # This is the case that says fontconfig enumerated the axis rather # than treating the file as a single static face -- the whole # reason this suite keys on index at all. nsp = 0; nfmt = 0; nvar = 0; spbad = ""; fmtbad = "" for (k in GFC) { split(GFC[k], g, "|") if (g[6] == "") nsp++; else spbad = spbad " " k "='" g[6] "'" if (g[8] == "CFF") nfmt++; else fmtbad = fmtbad " " k "='" g[8] "'" if (g[11] == "True") nvar++ } emit("fcq-spacing-proportional", nsp == NPAT && NGFC == NPAT, "spacing must be unset on all " NPAT " patterns;" spbad) emit("fcq-format-cff", nfmt == NPAT && NGFC == NPAT, "fontformat must be CFF on all " NPAT " patterns;" fmtbad) emit("fcq-one-variable", nvar == 1 && NGFC == NPAT, "exactly one of the " NPAT " patterns must carry variable=True; " nvar " did") }