# BST-TIMEOUT: 600 # DERIVED, no longer provisional. This suite's own line on # the first image run, 2026-09-14: # # ==BST-SUITE gtk+3 elapsed=10s bound=600s cases=36== # # 10 s is the STATIC HALF ALONE: the probe does not link on # this arm, so its own 300 s bound is unspent. The bound # stays at 600 for the day it does link -- 300 + the 10 # measured + margin. Lowering it toward 10 would be green # today and would fail on the boundary lifting, which is # the one change this suite exists to notice. # # It is deliberately far below 57-pango's 1400. That suite # warms the font roster because its probe's first act reaches # fontconfig; this one's does not. gtk3-probe never lays out # text: gtk_init_check() fails at gdk_display_open() before # any font is selected, and the CSS, builder, text-buffer and # paper-size cases are all display-free and font-free. If # this bound ever has to rise toward pango's, something in # the probe has started mapping fonts and THAT is the finding # -- raise the bound only after saying which case did it. # # THE BOUND MUST EXCEED THE BOUNDS THE SUITE SETS ITSELF: # 600 = 300 probe + room for the static cases. Do not lower # it below the probe bound further down. # gtk+3 3.24.41 -- the widget toolkit (poky recipes-gnome/gtk+). BRINTOS-2605. # See gtk+3_%.bbappend for what the port adds. # # --------------------------------------------------------------------------- # WHAT IS MEASURED HERE, AND FIRST WHAT IS NOT CLAIMED # # THIS IMAGE SERVES NO X PROTOCOL. Every route to a window in gtk+3 ends at # XOpenDisplay, so nothing here asserts that a widget drew, sized or responded # to anything -- the same reading core-image-brintos-full.bb:47492-47493 records # for fltk. This is a link-and-call port, and the ticket's own acceptance says # so: the smallest GTK3 client the recipe ships must run far enough to fail on a # missing DISPLAY rather than on a missing symbol. # # SO THE SUITE'S JOB IS TO TELL THOSE TWO FAILURES APART, and that is what the # case ORDER in gtk3-probe is for. BRINTOS-2692 records every program in pango's # runtime package trapping at the first GObject interface-vtable init in a # coroutinized static main, and names this port as the open question because # gtk+3 links libpango-1.0.a, libpangocairo-1.0.a and libpangoft2-1.0.a. If that # trap applies here it lands at gtk3/gtype-widget, BEFORE # gtk3/init-check-no-display -- so the suite reports which wall was hit rather # than a bare non-zero exit. # # BUT IT CANNOT ANSWER THAT QUESTION YET, AND SAYS SO. The probe does not LINK # on this arm at all: any merged module that initialises GTK or GDK carries # five GLib frames the coroutinize pass refuses (brintos/hwjs-cc#15) -- measured # four ways, see the probe block below. So gtk3/probe-link-present is # an attributed red and every case the probe would have produced is an explicit # skip. 2692 stays open for gtk+3: nothing here has run far enough to meet it. # # THE NEGATIVE CASES ARE FALSIFIABLE, AND THEY ARE OF TWO KINDS. Some are the # port's own DECISIONS, invisible in a passing build and otherwise free to rot: # the builtin-immodule arm (no immodule .so) and the demo/example trim (no # gtk3-demo). The rest are the arm's LIMITS rather than choices -- the four # missing programs and the missing print backend are targets that could not # link. Both kinds get a case, and each case says which kind it is, because the # remedies are opposite: undoing a decision is a regression, while a limit # lifting is news that wants this record rewritten. . "$BST_LIB" S=gtk3 # THE CANARY. brintos_local_verify.py drops a suite's FIRST BST result line # before the host parser sees it (BRINTOS-1149, our own host-side tooling), and # counts the dropped line as a pass. A case that asserts nothing stands here so # the eaten line costs nothing. bst_ok $S/canary TESTDIR=/usr/libexec/gtk3-tests PROBE=$TESTDIR/gtk3-probe GTKLIBV=/usr/lib/gtk-3.0/3.0.0 # --- 1. the packaging landed where the recipe says ---------------------------- # Read through dpkg-query rather than by globbing the rootfs, so this asserts the # PACKAGE owns these paths and a file that arrived from some other recipe is not # mistaken for this port's. # # `gtk+3`, AND THAT IS THE SECOND ANSWER TO THIS QUESTION. poky's gtk+3.inc # renames the runtime package to `libgtk-3.0` under DEBIAN_NAMES, so this case # read `libgtk-3.0` for one iteration and was right about the package that # existed then. The port has since turned that rename off # (`DEBIAN_NAMES:class-target = ""`, gtk+3_%.bbappend): the name encodes a # shared-library ABI version this static-only arm does not ship, and while it # stood, the roster could name the package neither way -- `gtk+3` raised # oe/packagedata.py's allarch-packagegroup error, and `libgtk-3.0` was # unresolvable. # # So a red HERE means the rename came back. dpkg-query -W matches real package # names and never a Provides, which is what makes this case able to tell the # difference at all. check_out $S/pkg-installed "install ok installed" \ dpkg-query -W -f='${Status}\n' gtk+3 check_out $S/pkg-dev-installed "install ok installed" \ dpkg-query -W -f='${Status}\n' gtk+3-dev check_out $S/pkg-staticdev-installed "install ok installed" \ dpkg-query -W -f='${Status}\n' gtk+3-staticdev # ${PN}-tests is this port's own package, added because gtk+3 declares no test # package of its own (PACKAGES: -src -dbg -demo -staticdev -dev -doc + locales) # and the probe needs a home that a consumer of the library does not pay for. check_out $S/pkg-tests-installed "install ok installed" \ dpkg-query -W -f='${Status}\n' gtk+3-tests # The deliverable. This arm ships no shared package libraries, so the archive IS # the library and its absence is the port shipping nothing. check $S/archive-gtk test -s /usr/lib/libgtk-3.a check $S/archive-gdk test -s /usr/lib/libgdk-3.a check $S/archive-gailutil test -s /usr/lib/libgailutil-3.a check $S/header test -f /usr/include/gtk-3.0/gtk/gtk.h check $S/header-gdk test -f /usr/include/gtk-3.0/gdk/gdk.h check $S/pc-file test -f /usr/lib/pkgconfig/gtk+-3.0.pc check $S/pc-file-gdk test -f /usr/lib/pkgconfig/gdk-3.0.pc # The x11 backend is the one this port keeps -- see the PACKAGECONFIG pin in the # bbappend, which runs the OPPOSITE way to pango's. gtk+-x11-3.0.pc exists only # when the x11 backend was built, so this is the pin made falsifiable: if the # filter ever silently resolves to nothing, this goes red here rather than # surfacing as a mysteriously smaller library. # # VERIFIED rather than assumed: the -dev deb ships gail-3.0.pc, gdk-3.0.pc, # gdk-x11-3.0.pc, gtk+-3.0.pc, gtk+-unix-print-3.0.pc and gtk+-x11-3.0.pc, so # meson does emit the per-backend pair under this configuration. check $S/pc-file-x11 test -f /usr/lib/pkgconfig/gtk+-x11-3.0.pc # The two programs this port's runtime package actually ships. # gtk-query-immodules-3.0 is the most 2692-exposed binary the recipe installs -- # a full static GTK main -- so its presence is worth asserting even though this # image has no modules for it to find. check $S/bin-query-immodules test -x /usr/bin/gtk-query-immodules-3.0 check $S/bin-update-icon-cache test -x /usr/bin/gtk-update-icon-cache-3.0 # AND THE FOUR THAT CANNOT BE SHIPPED, asserted as ABSENT rather than left # unmentioned. Each is a full static link whose merged module pulls GLib's # g_dpgettext, g_dpgettext2 and three g_socket_*_with_timeout frames, every one # REFUSEd for an escaping dynamic alloca -- GLib's frames, not gtk's, the same # CUT 4 glib-2.0_%.bbappend records, inherited by a consumer. # # ALL OF IT IS brintos/hwjs-cc#15, AND TWO DIFFERENT CHECKS REPORT IT. Which one # a target gets is a property of its link closure, not of the target -- measured # per-target on the closing build, where the tally is 5 non-benign and 1 setjmp: # # these four, plus doc-shooter FATAL "the coroutinize pass REFUSED # function(s) ... non-benign class ... a # refused def is a live ABI chimera" -- the # whole-program rule over a #15-class refusal. # the print backend FATAL setjmp-2b-unwind-through-refused-frame- # (HWJS_CORO_SIDE_MODULE=1) xmod -- the ESCALATION, #102/#104, and the # build's ONLY setjmp refusal. # a GTK or GDK client either one: the recipe's own probe link gets # (see the probe block) the non-benign rule, the hand-link gets the # escalation, same source file both times. # # #15 closing returns every one of them, because the escalation fires only on a # frame a vla refusal left native. #104 closing alone would return the print # backend and leave the rest with refused defs. # # So these cases are GREEN on the arm as it stands and go red the day one starts # linking, which is the day this port's own record needs rewriting. Read a red # here as good news that is not yet written down, never as a regression. check $S/bin-launch-absent test ! -e /usr/bin/gtk-launch check $S/bin-builder-tool-absent test ! -e /usr/bin/gtk-builder-tool check $S/bin-query-settings-absent test ! -e /usr/bin/gtk-query-settings check $S/bin-encode-symbolic-absent test ! -e /usr/bin/gtk-encode-symbolic-svg # --- 1b. the port's own decisions, made falsifiable --------------------------- # BUILTIN IMMODULES. -Dbuiltin_immodules=all links every input method into # libgtk-3.a and never declares the shared_module, so this directory must hold # no module at all. A .so appearing here means the option was lost and the image # has gained a file nothing on this arm can dlopen. if [ -n "$(find $GTKLIBV/immodules -name '*.so' 2>/dev/null | head -1)" ]; then bst_notok $S/no-immodule-so "$GTKLIBV/immodules holds a shared module; -Dbuiltin_immodules=all should have linked them into libgtk-3.a instead, and nothing on this arm can dlopen one" else bst_ok $S/no-immodule-so fi # AND THE CACHE THAT WOULD HAVE BEEN WRITTEN FOR THEM. gtk-immodules-cache.bbclass # attaches a postinst that runs gtk-query-immodules-3.0 ON TARGET at first boot; # the bbappend blanks the script bodies because the cache has no reader here. # If the neutralisation is undone, run-postinsts writes this file at boot. check $S/no-immodules-cache test ! -e $GTKLIBV/immodules.cache # PRINT BACKENDS. -Dprint_backends=file narrows meson's mandatory set to one # declaration, and the measurement came back: that last shared_module DOES NOT # LINK on this arm. It is the only one of the six failing targets that reports # through the setjmp check rather than the whole-program one -- confirmed on the # closing build, whose whole log carries exactly one setjmp refusal, this one -- # # fatal error: error in backend: [hwjs-coro] REFUSE fn=g_dpgettext # reason=setjmp-2b-unwind-through-refused-frame-xmod # # -- so the image ships no print backend because none could be built, NOT because # the port excluded one. The distinction matters to whoever reads this next: the # exclusion story would say "delete the filter and it comes back", and that is # false while brintos/hwjs-cc#15 is open -- or, for this one target, #104. if [ -n "$(find $GTKLIBV/printbackends -name '*.so' 2>/dev/null | head -1)" ]; then bst_notok $S/no-printbackend-so "$GTKLIBV/printbackends holds a shared module; this arm cannot dlopen one, so it is dead weight that also reads as a working print backend" else bst_ok $S/no-printbackend-so fi # DEMOS AND EXAMPLES. gtk+3.inc trims them :class-native only; the bbappend # extends that to :class-target, because each is a full static link of # libgtk/libgdk on the coro arm for a binary this image cannot run. The # ${PN}-demo package therefore has no files and is never produced -- which is # how the roster partition stays total without rostering an empty package. check $S/no-demo-binary test ! -e /usr/bin/gtk3-demo check $S/no-widget-factory test ! -e /usr/bin/gtk3-widget-factory check_rc $S/no-demo-package 1 dpkg-query -W gtk+3-demo # THE CLOSURE MUST BE DISCOVERABLE, AND pkg-config IS WHAT ANSWERS THAT. # # A static consumer of gtk+3 gets its transitive libraries only through the .pc # graph; a closure that cannot be resolved leaves that consumer with an # undefined-symbol link and nothing pointing at gtk. On a static-only arm the # .pc graph IS the API. # # THE FIRST CUT OF THIS CASE GREPPED THE FILE FOR LITERAL NAMES AND WENT RED ON # A CORRECT .pc. Measured on the first image run: # # Requires: gdk-3.0 atk cairo cairo-gobject gdk-pixbuf-2.0 gio-2.0 # Requires.private: atk atk-bridge-2.0 fribidi pangoft2 gio-unix-2.0 # # -- which names neither `pango` nor `glib-2.0`, and does not have to: pangoft2 # Requires pango, gio-2.0 Requires glib-2.0, and pkg-config resolves # transitively. The old case's own comment said "either spelling satisfies # this" while its code asserted the spelling; the comment was right. So ask the # resolver, not the file. check_out $S/pc-version "3.24.41" pkg-config --modversion gtk+-3.0 check_out $S/pc-libs "-lgtk-3" pkg-config --libs gtk+-3.0 check_out $S/pc-closure-gdk "-lgdk-3" pkg-config --static --libs gtk+-3.0 check_out $S/pc-closure-pango "-lpango-1.0" pkg-config --static --libs gtk+-3.0 check_out $S/pc-closure-cairo "-lcairo" pkg-config --static --libs gtk+-3.0 check_out $S/pc-closure-glib "-lglib-2.0" pkg-config --static --libs gtk+-3.0 check_out $S/pc-closure-atk "-latk-1.0" pkg-config --static --libs gtk+-3.0 # AND pkg-config MUST DISCRIMINATE. Every case above reads "pkg-config # answered", and all of them would be green against a pkg-config that answers # yes to anything -- the control at-spi2-core's suite carries for the same # reason. check $S/pc-discriminates sh -c '! pkg-config --exists gtk+-9.0' # Whatever shared objects the recipe ships must be usable; a package that ships # none is unconverted and passes. See bst_lib_shape. bst_lib_shape $S/lib-shape "/usr/lib/libgtk-3.so* /usr/lib/libgdk-3.so*" # --- 1c. THE LINK RECORD, WHICH SHIPS AS A FILE ------------------------------ # do_compile writes one LINKED/UNLINKABLE line per program it was asked to # build, into a file that travels in this package. It is a file and not a log # line because bbnote sits below the default loglevel: on iteration 6 the LINKED # half of that enumeration never reached build-all.log and had to be derived by # subtraction, which is exactly the reading error the record exists to prevent. # # Asserting on it here also moves the boundary onto the IMAGE. A reader on # target can then tell "this port never built it" from "the rootfs lost it", # without access to the build host. if [ ! -f $TESTDIR/target-link.txt ]; then bst_notok $S/link-record "no target-link.txt in $TESTDIR; do_compile writes one on every build" else bst_ok $S/link-record check $S/link-record-immodules-linked \ grep -qx "LINKED gtk/gtk-query-immodules-3.0" $TESTDIR/target-link.txt check $S/link-record-icon-cache-linked \ grep -qx "LINKED gtk/gtk-update-icon-cache" $TESTDIR/target-link.txt # Five, named in the bbappend: gtk-builder-tool, gtk-encode-symbolic-svg, # gtk-launch, gtk-query-settings, libprintbackend-file.so. A DIFFERENT count # either way is the finding -- fewer means the arm improved, more means # something that used to link stopped. check_eq $S/link-record-five-unlinkable 5 \ "$(grep -c '^UNLINKABLE ' $TESTDIR/target-link.txt 2>/dev/null)" fi # --- 2. the probe ------------------------------------------------------------- WORK=${WORK:-/tmp/bst-gtk3.$$} rm -rf "$WORK" # BRINTOS-WORKAROUND: brintos/linux#73 remove -- `mkdir -p` IS WHAT BREAKS HERE, not mkdir. # The kernel's private restart sentinel (517) reaches glibc's errno return path, # so a syscall reports a failure it did not have. `mkdir -p` calls mkdir on EVERY # component including the ones that already exist, and reads the sentinel where # it would have read EEXIST -- so it aborts on the parent and never reaches the # leaf. Plain mkdir on a leaf creates the directory even when it reports 517, # because the effect happens and only the return value is wrong. Bounded at # three attempts with no sleep, and stderr is kept so the one line naming the # cause travels into the failure detail. _wp=$(dirname "$WORK") [ -d "$_wp" ] || mkdir -p "$_wp" 2>/dev/null _w=0 _werr= while [ "$_w" -lt 3 ] && [ ! -d "$WORK" ]; do _werr=$(mkdir "$WORK" 2>&1) _w=$((_w + 1)) done # The recipe records whether the probe linked, beside the binary, so a reader on # target can tell "not built" from "not installed". On this port that # distinction carries more than usual: a REFUSED probe is the coroutinize pass # declining a full static GTK main, which is a finding about the arm rather than # about the recipe. # # AND THE REFUSAL IS THE EXPECTED STATE TODAY -- this is the port's central # finding, not a gap in the suite. Hand-linked three ways against these archives # on hz1: gtk3_probe.c, a six-line client whose whole body is # gtk_get_major_version plus gtk_init_check, and a three-line gdk_init_check # client. No client that initialises GTK **or GDK** links on this arm, which is # why the only two gtk+3 programs that do link are the two that never call # gtk_init. The frames are GLib's -- the cut glib-2.0_%.bbappend records as # unfixable from a recipe -- and out of gtk+3's reach. # # #15 IS THE CAUSE, AND THE SAME CLIENT IS REFUSED THROUGH TWO DIFFERENT CHECKS. # Five GLib frames are REFUSEd reason=vla/dynamic-alloca-address-escape-opaque- # sink -- g_dpgettext, g_dpgettext2 and three g_socket_*_with_timeout -- and that # family IS #15. What happens next depends on the link closure, which is the # reason this attribution took four passes to get right: # # from the recipe the whole-program non-benign rule, "a refused def is a live # ABI chimera". Not one reason=setjmp line in the 139 MB log. # by hand on hz1 the ESCALATION of a refused frame sitting on a static # setjmp unwind path: reason=setjmp-2b-unwind-through-non- # coroutinizable-frame, then a pinned-clang fatal. That is # #102 (a plain alloca in ncftp) and #104 (libXt's # _XtConvert, "makes every Xt toolkit unlinkable"), to which # gtk+3 adds an Xt-free witness. # # Both reduce to #15: the escalation fires on a frame a vla refusal left native, # so once #15 lowers it there is nothing on a setjmp path to escalate. Hence #15 # below, with #104 named as the path rather than as a second defect to wait on. # # probe-link-present is therefore an ATTRIBUTED RED: it names the open defect, # and it goes green on the day that defect closes, which is the same day every # case below it starts reporting. if [ ! -f "$TESTDIR/probe-link.txt" ]; then bst_notok $S/probe-link-present "no probe-link.txt in $TESTDIR; the recipe writes one on every build" elif grep -q '^LINKED gtk3_probe$' "$TESTDIR/probe-link.txt" 2>/dev/null; then bst_ok $S/probe-link-present else bst_notok $S/probe-link-present "REFUSED: the coroutinize pass declines five GLib vla frames (g_dpgettext, g_dpgettext2, three g_socket_*_with_timeout) in any merged module that initialises GTK or GDK, and the whole-program rule then refuses the link -- brintos/hwjs-cc#15; by hand the same client escalates through the setjmp path instead, brintos/hwjs-cc#104. See gtk3_probe.link.log in the build tree \${T}" fi if [ ! -x "$PROBE" ]; then # A refused link is reported ONCE, above. Reporting it again here would # double-count one defect -- but the cases the probe would have produced are # skipped BY NAME rather than left unreported, because a suite that quietly # shrinks reads as a suite that passed. if grep -q '^REFUSED gtk3_probe$' "$TESTDIR/probe-link.txt" 2>/dev/null; then bst_skip $S/probe-present "not built: the link was refused -- see gtk3/probe-link-present" bst_skip $S/probe-run "no probe binary (brintos/hwjs-cc#15)" bst_skip $S/probe-terminated "no probe binary (brintos/hwjs-cc#15)" bst_skip $S/probe-case-count "no probe binary (brintos/hwjs-cc#15)" else bst_notok $S/probe-present "not installed at $PROBE, and probe-link.txt does not say REFUSED -- so the recipe linked the probe and then lost it, which is a packaging fault rather than the known refusal" fi else bst_ok $S/probe-present # Fail here rather than at suite level: an absent $WORK left unchecked puts # every later case against a directory that is not there, and bst_probe then # spends the whole suite budget on an output file it can never open -- # reported as gtk3/suite-timeout, which names neither cause nor case. if [ ! -d "$WORK" ]; then bst_notok $S/workdir "cannot create $WORK after 3 attempts: ${_werr:-no diagnostic} -- the probe and every case below it need it. A RESTART-SENTINEL-LEAK line here is brintos/linux#73 (BRINTOS-2547)" exit 0 fi bst_probe $S/probe-run "$WORK/out" 300 "$PROBE" prc=$? if [ "$prc" -eq 124 ]; then bst_notok $S/probe-terminated "the probe was killed at its 300 s bound. Nothing it does should take seconds: it opens no display, lays out no text and maps no font. Read \$WORK/out for the last case printed -- if it stops at a 'time' line, something in that phase started reaching fontconfig, which is a change in what the probe does and not a reason to raise the bound." bst_notok $S/probe-case-count "the probe did not finish" else seen=0 done_line=0 # set -f for the split: a FAIL detail can carry * or ?, which `for` would # glob against the cwd and turn one case's message into a directory # listing. set -f for line in $(sed -n 's/^gtk3-probe //p' "$WORK/out" | tr ' ' '\036'); do l=$(printf '%s' "$line" | tr '\036' ' ') case "$l" in done\ failures=*) done_line=1 continue ;; # The measurement lines. Recorded, never scored. time\ *) phase=$(printf '%s' "$l" | cut -d' ' -f2) bst_skip "$S/time-$phase" "$(printf '%s' "$l" | cut -d' ' -f3-)" continue ;; esac case=${l%% *} rest=${l#* } seen=$((seen + 1)) case "$rest" in ok) bst_ok "$S/$case" ;; FAIL\ *) bst_notok "$S/$case" "${rest#FAIL }" ;; *) bst_notok "$S/$case" "unparsable probe line: $(_bst_1line "$l")" ;; esac done set +f # A probe that stopped early emits fewer lines AND no terminator. Both # are checked: the count alone would pass a run that printed the right # number of lines and then died, and the terminator alone would pass a # run that emitted half its cases and reached the end. On this port the # terminator is the case that separates BRINTOS-2692 from a clean # missing-display refusal, so it is the one to read first on a red. if [ "$done_line" -eq 1 ]; then bst_ok $S/probe-terminated else bst_notok $S/probe-terminated "no 'done failures=' line; the probe did not reach the end of main. Read which case printed LAST: stopping at gtk3-probe/gtype-* is BRINTOS-2692 reaching this port, and stopping after init-check-no-display is not." fi # BOUNDED FROM BELOW, never pinned: 35 scored cases today (3 version, 8 # gtype, 6 text buffer, 4 css, 5 builder, 5 paper size, 3 accelerator, # 1 init-check). A case added to the probe must not red this line. if [ "$seen" -ge 35 ]; then bst_ok $S/probe-case-count else bst_notok $S/probe-case-count "reported $seen cases, expected at least 35 -- coverage was lost, not merely failed" fi fi fi rm -rf "$WORK"