# BST-TIMEOUT: 600 # MEASURED: 350 cases in 62 s (0.18 s/case), so the bound is # # ~9.7x. Stable across three on-target runs at base pin # # 07806b1158b4 -- 63 s, 62 s, 62 s. The per-case figure is a # # third of 99-ttf-hunkyfonts' 0.53 s because this suite is # # exec-bound rather than case-bound: all 350 cases come out of # # ONE awk over twelve files, and the ~29 execs are the clock. # # ttf-tlwg 0.6.1 -- the Thai Linux Working Group font collection, wasm32-hwjs # glibc (BRINTOS-1729). # # WHAT THIS PORT INSTALLS. 74 files in ONE package: 58 TrueType faces # (5,932,464 bytes) in THIRTEEN families under the shared # /usr/share/fonts/truetype, and 16 fontconfig fragments (5,998 bytes) under the # shared /etc/fonts/conf.d. Read off the built package's own FILES_INFO, not # predicted from the recipe's FILES globs, which are `${datadir}/fonts` and the # whole of `${sysconfdir}`. # # ================================================================= THE FINDING # # INSTALLING THIS PACKAGE TAKES `serif` AND `sans-serif` IMAGE-WIDE, AND LEAVES # `monospace` ALONE. ttf.inc's do_install is only half this recipe; the # do_install:append copies sixteen fontconfig fragments into /etc/fonts/conf.d, # and ten of them are rules over the three classic generics. # fontconfig loads them at 64, and PREPENDS ahead of the generic while # APPENDS after it: # # serif 30-liberation-aliases.conf s Liberation Serif, so it is # behind the generic; 64-01-tlwg-kinnari.conf s Kinnari, # which goes in front. Liberation Serif -> Kinnari # sans-serif same shape, five fragments, and the FIRST loaded wins because # each later one prepends ahead of the generic and behind its # predecessor. Liberation Sans -> Waree # monospace 44-source-code-pro-fonts-fontconfig.conf ALSO s, and 44 # loads before 64, so it keeps the generic. Source Code Pro, unchanged # # MEASURED, NOT DERIVED. The paragraph above is the explanation; the answers # came first, from an A/B at fontconfig 2.15.0 -- the release this image ships -- # over this image's own /etc/fonts and font set, with and without this package. # The first reading of the XML predicted the LAST fragment would win each # generic (so Norasi and Laksaman) and that monospace would go to TlwgMono. All # three were wrong, and only the measurement says so. # # THIS IS THE THIRD GENERIC HANDOVER ON THIS IMAGE and it is handled the way the # first two were: the taking port asserts it positively (generic-serif and # generic-sans-serif below), and the suites that pinned the old answer are # AMENDED, not deleted -- 99-liberation-fonts' fc-match-serif and # fc-match-sans-serif, and 99-ttf-gentium's fc-match-serif-unchanged. What those # cases are worth is that the generic resolves to the family the configuration # actually selects, and they are still worth that from the other side. # # generic-monospace is the case worth having. Two of three moved and one did not, # from three fragments of the same shape; pinning only the two that moved would # leave a future fragment landing between 44 and 64 free to take monospace with # nothing going red. # # ============================================================= mmap and #165 # # THIS PORT INTRODUCES NO NEW MAXIMUM AND CANNOT REACH THE BRACKET. On this arm a # file-backed mapping of a rootfs file is populated eagerly by pread64 over the # cloud FUSE bridge, and above a length threshold that population is REFUSED -- # the caller does not get an error, it PARKS beyond SIGKILL # (linux-in-the-browser#165, and #72 for the refusal itself). The largest face # here is 163,188 bytes (Purisa-Oblique.ttf). The 67 font files this image # already walks on every fc-cache run span 23,895 to 415,816 bytes, and # TWENTY-THREE of them are larger than our largest. BRINTOS-1725's refusals were # at 553,132 and 584,812. So this package adds nothing the image was not already # mapping, and the two mitigations below are the standing conventions rather than # a response to a park seen here. # # fc-cache IS CALLED WITHOUT -f. Nine font suites dropped that token under # BRINTOS-1725. `fc-cache -f` rebuilds every directory whether or not its cache # is valid, which maps every face on the image; without -f it rebuilds only # what is stale, and nothing is, because /var/cache/fontconfig ships populated # by the update_font_cache intercept fontcache.bbclass registers at rootfs # time. fc-cache-shipped asserts that precondition rather than assuming it. # # fc-scan READS A TMPFS COPY. /tmp is a tmpfs, so a mapping of a copy there # never reaches the FUSE bridge. All 58 faces are staged at once -- 5.9 MB, # which is the bound 99-ttf-mplus deliberately chose for ITS largest per-family # batch (4.8 MB) and a fifth of what it refused to stage at once (26.6 MB) -- # so this costs ONE cp and ONE fc-scan rather than thirteen of each. The bytes # are proved identical by the sha256sum pass, which uses read() and not mmap, # so moving the PARSE off the bridge weakens no payload assertion: the payload # cases still read the installed files. # # BRINTOS-WORKAROUND: linux-in-the-browser#72 remove -- fc-scan a tmpfs copy # rather than the installed file; goes when a file-backed mmap whose pread64 # population fails returns an error to the caller instead of parking it. # # BRINTOS-WORKAROUND: linux-in-the-browser#165 revisit -- call fc-cache without # -f so only a stale cache is rebuilt; the -f goes back in when the mmap refusal # above is lifted. # # ======================================================== what is NOT asserted # # "fc-match FOR A FAMILY THIS PACKAGE DOES NOT PROVIDE IS ASSERTED TO NOT # RESOLVE" IS NARROWED. fc-match always returns a best match -- it cannot express # non-resolution -- so a case built on that clause would assert nothing while # looking strict. Narrowed to the property fc-list CAN answer: nothing on this # image is listed under a family this release does not provide. Same narrowing # BRINTOS-1723, -1724, -1725 and -1726 made. The absent set deliberately includes # `TlwgTypist` and `TlwgTypo` WITHOUT the space, because those are the two # spellings a suite deriving families from basenames would have asserted, and # both are wrong -- see the fcfaces control's header. # # THE CO-TENANT TOTAL IS NOT ASSERTED HERE. This port is the fifth tenant of # /usr/share/fonts/truetype; the exact directory total belongs to # 99-cantarell-fonts' WANT_COT2, which this change moves 26 -> 84. What this # suite asserts is that the 26 files that were there before are still there -- # by counting the ones that are NOT ours -- which localises a displacement to # the package that caused it without pinning a number every future font port # would have to move. . "$BST_LIB" S=ttf-tlwg # THE CANARY, which asserts nothing about ttf-tlwg. # # A suite's FIRST BST result line is lost between the guest's printf and the # host's read -- per-suite, deterministic, and NOT a defect in the host tool # (BRINTOS-1149 established that and filed it as linux-in-the-browser#177). The # harness reports the deficit but computes its pass count as total-minus- # failures, so THE DROPPED LINE IS COUNTED AS A PASS. A nothing-case therefore # stands in that position and the eaten line costs nothing. It sits ahead of the # `require` guards deliberately, because a `require` that skips would emit its # skip as the first line and be eaten. # # Remove this case when linux-in-the-browser#177 is fixed. bst_ok $S/suite-canary D=$BST_DATA REF=$D/ttf-tlwg-manifest.txt FACES=$D/ttf-tlwg-fcfaces.txt TTFDIR=/usr/share/fonts/truetype CONFDIR=/etc/fonts/conf.d COT1=/usr/share/fonts/ttf PKGLIST=/var/lib/dpkg/info/ttf-tlwg.list tmp=${TMPDIR:-/tmp} W=$tmp/bst-ttf-tlwg.$$ require gawk $S/gawk-present require sha256sum $S/sha256sum-present require find $S/find-present # fc-* run in the payload and consumer blocks below. They are NOT `require`s: # fontconfig-utils is a hard Depends of ttf-tlwg (fontcache.bbclass adds it, and # the built package's RDEPENDS carries it), so an absent tool is a defect in the # port rather than a legitimately absent tool, and a skip would report it green. for t in fc-scan fc-cache fc-list fc-match fc-conflist; do if [ -x "/usr/bin/$t" ]; then bst_ok "$S/$t-present" else bst_notok "$S/$t-present" "fontconfig-utils is a Depends of ttf-tlwg but /usr/bin/$t is absent"; fi done if [ ! -f "$REF" ] || [ ! -f "$FACES" ]; then bst_notok $S/ref-present "the reference data did not ship: $REF / $FACES" exit 0 fi bst_ok $S/ref-present # ------------------------------------------------------------------ payload shape # One find over the two directories this port writes into and the one co-tenant # directory it must not have touched, so every count below is a measurement from # the same pass. %y catches a symlink where a regular file is meant and %m the # mode -- neither of which `test -f` can see. -maxdepth 1 because all three are # flat and the fragment directory is full of symlinks into conf.avail. find "$TTFDIR" "$CONFDIR" "$COT1" \ -maxdepth 1 -printf '%p %y %s %m %l\n' > "$W.inv" 2>/dev/null # One sha256sum over the whole payload. The face globs are SCOPED to the thirteen # family prefixes, not `*.ttf`: this directory is shared and holds 26 files from # four other packages, and an unscoped glob would digest all 84 and report a file # this package does not own. `Tlwg*` covers all four Tlwg* families in one. sha256sum "$TTFDIR"/Garuda*.ttf "$TTFDIR"/Kinnari*.ttf "$TTFDIR"/Laksaman*.ttf \ "$TTFDIR"/Loma*.ttf "$TTFDIR"/Norasi*.ttf "$TTFDIR"/Purisa*.ttf \ "$TTFDIR"/Sawasdee*.ttf "$TTFDIR"/Tlwg*.ttf "$TTFDIR"/Umpush*.ttf \ "$TTFDIR"/Waree*.ttf \ "$CONFDIR"/64-*-tlwg-*.conf "$CONFDIR"/64-15-laksaman.conf \ "$CONFDIR"/89-tlwg-*.conf > "$W.sums" 2>&1 # ------------------------------------------------- fontconfig parse, off the bridge # One fc-scan over TMPFS COPIES, for the mmap reason in the header. This is a # second, independent parse: it reads the FILES rather than the cache, and reports # fields FreeType's own API does not name. rm -rf "$W.d"; mkdir -p "$W.d" 2>/dev/null cp "$TTFDIR"/Garuda*.ttf "$TTFDIR"/Kinnari*.ttf "$TTFDIR"/Laksaman*.ttf \ "$TTFDIR"/Loma*.ttf "$TTFDIR"/Norasi*.ttf "$TTFDIR"/Purisa*.ttf \ "$TTFDIR"/Sawasdee*.ttf "$TTFDIR"/Tlwg*.ttf "$TTFDIR"/Umpush*.ttf \ "$TTFDIR"/Waree*.ttf "$W.d/" 2>/dev/null _staged=$(ls "$W.d" 2>/dev/null | wc -l) if [ "$_staged" -eq 58 ]; then bst_ok $S/fcq-stage else bst_notok $S/fcq-stage "staged $_staged of 58 faces into $W.d for the fc-scan parse"; fi LC_ALL=C fc-scan --format '%{file|basename}|%{family}|%{style}|%{slant}|%{weight}|%{spacing}|%{postscriptname}|%{fontformat}|%{scalable}|%{outline}|%{charset}\n' \ "$W.d" > "$W.scanraw" 2>/dev/null rm -rf "$W.d" # Split the scan into its metadata half and one charset file per face, so the 58 # charset digests cost ONE sha256sum exec rather than 58. The control commits the # DIGEST rather than the charset itself -- see the fcfaces header. mkdir -p "$W.cs" 2>/dev/null LC_ALL=C gawk -F'|' -v CS="$W.cs" 'NF>=11 { printf "%s|%s|%s|%s|%s|%s|%s|%s|%s|%s\n", $1,$2,$3,$4,$5,$6,$7,$8,$9,$10 f = CS "/" $1 ".cs"; printf "%s", $11 > f; close(f) }' "$W.scanraw" > "$W.scan" 2>/dev/null ( cd "$W.cs" 2>/dev/null && LC_ALL=C sha256sum *.cs 2>/dev/null ) > "$W.cssums" 2>/dev/null # ----------------------------------------------------------------- consumer block # # EVERYTHING BELOW THIS LINE CAN COST THE GUEST, AND THAT IS WHY IT IS LAST. # Every case above has already been emitted by the time the first fc-cache # mapping happens, so a park here costs this suite its consumer cases and not its # evidence. # # fc-cache WITHOUT -f. The precondition is asserted first: if the shipped cache # were empty the call below would rebuild every directory, and a red here is the # thing to read rather than the timeout under it. _nc=$(find /var/cache/fontconfig -type f 2>/dev/null | wc -l) if [ "$_nc" -ge 1 ]; then bst_ok $S/fc-cache-shipped else bst_notok $S/fc-cache-shipped "/var/cache/fontconfig is empty -- no cache was built at image time, so the fc-cache below rebuilds every directory and may park (linux-in-the-browser#165)"; fi timeout -k 5 90 fc-cache > "$W.cache" 2>&1 _rc=$? if [ "$_rc" -eq 0 ]; then bst_ok $S/fc-cache-rc elif [ "$_rc" -eq 124 ] || [ "$_rc" -eq 137 ]; then bst_notok $S/fc-cache-rc "fc-cache did not return within 90s -- linux-in-the-browser#165 (mmap population parked); a cache went stale, so this call rebuilt a directory" else bst_notok $S/fc-cache-rc "fc-cache exited $_rc: $(_bst_1line "$(cat "$W.cache")")"; fi # One fc-list for the whole consumer read; the parsing is done in the awk. fc-list : file family > "$W.list" 2>&1 # THE THAI COVERAGE, asked through fontconfig's own language derivation rather # than by grepping a charset for a range. :lang=th is true exactly when the face # covers the Thai orthography fontconfig requires. fc-list :lang=th file > "$W.th" 2>&1 # fc-match per family, and the three classic generics. fc-match never fails, so # what is asserted is WHICH file and family come back. : > "$W.match" for _fam in Garuda Kinnari Laksaman Loma Norasi Purisa Sawasdee \ "Tlwg Typist" "Tlwg Typo" TlwgMono TlwgTypewriter Umpush Waree; do printf '%s\t%s\n' "$_fam" "$(fc-match --format '%{file}|%{family}' "$_fam" 2>&1)" >> "$W.match" done : > "$W.generic" for _g in serif sans-serif monospace; do printf '%s\t%s\n' "$_g" "$(fc-match --format '%{family}' "$_g" 2>&1)" >> "$W.generic" done # THE FRAGMENTS ARE LOADED, asked directly rather than inferred from the generics. fc-conflist > "$W.conf" 2>&1 # ------------------------------------------------------------- packaging evidence # dpkg's own record, which is the only layer that can answer what this package # ended up owning -- and FILES:${PN} claims the WHOLE of ${sysconfdir}, so that # question has a real answer here. `grep -H .` and not a redirect: a # `while read ... < /var/lib/dpkg/info/*.list` in the top-level shell ends the # suite where it stands on this arm (brintos/linux#73). grep -H . "$PKGLIST" > "$W.dpkg" 2>/dev/null || : > "$W.dpkg" LC_ALL=C gawk -v S="$S" \ -f "$D/ttf-tlwg-verify.awk" \ "$REF" "$W.sums" "$W.inv" "$FACES" "$W.scan" "$W.cssums" \ "$W.list" "$W.th" "$W.match" "$W.generic" "$W.conf" "$W.dpkg" rm -rf "$W".* 2>/dev/null