. "$BST_LIB" # xorgproto 2023.2 -- the X Window System's unified protocol definitions (poky # recipes-graphics/xorg-proto). 129 headers and 29 .pc files, no executable code # whatsoever. See xorgproto_%.bbappend for what the port adds and why. # # --------------------------------------------------------------------------- # WHAT CAN AND CANNOT BE ASSERTED ABOUT A PACKAGE THAT IS PURE DEFINITIONS # # There is nothing to run. The whole deliverable is bytes that other recipes # compile against, and this image ships no C compiler, so a shell suite on its # own can say exactly one thing: the files arrived. That is worth asserting and # it is nowhere near enough -- a header set can be complete, installed, and # still describe a different wire format than the peer at the other end of an X # connection, and nothing here would notice until an X client is ported. # # So the port builds an ABI PROBE at recipe time and this suite drives it. Every # X protocol structure is a wire format whose size is fixed by the protocol, and # xorgproto states that size itself -- `#define sz_ ` beside each struct -- # so `sizeof(T)` against `sz_T` is an oracle the package carries in its own # bytes, over 1074 pairs. # # THE PROBE DOES NOT JUDGE, AND THAT IS DELIBERATE. The equality does not hold # universally: where a wire form is unaligned the C struct pads and sizeof # legitimately exceeds sz_. Ten of the 1074 differ for that reason at this # version. So the recipe builds the probe TWICE from one generated source -- # once with ${BUILD_CC}, once for wasm32 -- and ships the build host's run as # xorgproto-abi.ref. What §5 below asserts is that the two runs AGREE. Anything # both report is upstream's business; anything only wasm32 reports is a layout # fault in this arm, which is the one question a headers-only port cannot # otherwise answer. # # The reference is built with -D_XSERVER64 -- the wire data model -- so that the # comparison is not confounded by the build host being 64-bit. §5's # abi-wire-types-32bit is the precondition that makes that valid on this arm, and # the bbappend has the measurement behind it. # # THE COUNTS ARE ASSERTED AT BUILD TIME, NOT HERE, and the division is not # arbitrary. In the recipe, ${D}${includedir} holds this package's files and # nothing else, so `129 headers` is exact and the bbappend makes a short install # fatal. On the target /usr/include is shared -- libXext will one day install # into X11/extensions too -- so a count taken here would start failing for a # reason that has nothing to do with xorgproto. What this suite asserts instead # is the build-time census file, the presence of the specific files, and the # complete pinned .pc set, each of which stays true as the image grows. # # --------------------------------------------------------------------------- # THE RECIPE'S PV IS NOT A VERSION ANY .pc REPORTS, and §4 pins that rather than # papering over it. xorgproto is the union of the ~29 formerly separate protocol # packages, each of which kept its own version: xproto is at 7.0.33, randrproto # at 1.6.0, renderproto at 0.11.1. `2023.2` is the bundle's release tag and # appears in NONE of the installed bytes -- measured, `grep -rl 2023.2` over the # installed tree returns nothing. So `pkg-config --modversion` reporting the # recipe's version is not achievable by any configuration of this recipe, and # what the port pins instead is all 29 protocol versions against their own # sources, so an upstream bump is a changed number here rather than a silent # pass. INC=/usr/include PCD=/usr/lib/pkgconfig XT=/usr/libexec/xorgproto-tests SURF=/usr/lib/xorgproto/INSTALL-SURFACE # The complete non-legacy .pc set with the protocol version each advertises, # measured 2026-08-22 against xorgproto 2023.2. meson gates ten more # (evieproto, fontcacheproto, lg3dproto, printproto, trapproto, windowswmproto, # xcalibrateproto, xf86miscproto, xf86rushproto, xproxymngproto) behind # -Dlegacy, which poky pins false and every distribution leaves false -- so 29 # is the COMPLETE default set and not a short one. The ticket's "roughly forty" # counted 29 + 10. XP_PCS="applewmproto:1.4.2 bigreqsproto:1.1.2 compositeproto:0.4.2 \ damageproto:1.2.1 dmxproto:2.3.1 dpmsproto:1.2 dri2proto:2.8 dri3proto:1.3 \ fixesproto:6.1 fontsproto:2.1.3 glproto:1.4.17 inputproto:2.3.99.2 \ kbproto:1.0.7 presentproto:1.3 randrproto:1.6.0 recordproto:1.14.2 \ renderproto:0.11.1 resourceproto:1.2.0 scrnsaverproto:1.2.3 videoproto:2.3.3 \ xcmiscproto:1.2.2 xextproto:7.3.0 xf86bigfontproto:1.2.0 xf86dgaproto:2.1 \ xf86driproto:2.1.1 xf86vidmodeproto:2.3.1 xineramaproto:1.2.1 xproto:7.0.33 \ xwaylandproto:1.0" work=${TMPDIR:-/tmp}/bst-xorgproto.$$ rm -rf "$work"; mkdir -p "$work" # hasfile -- ok iff is a non-empty regular file. Fork-free. hasfile() { if [ -s "$2" ]; then bst_ok "$1"; else bst_notok "$1" "missing or empty: $2"; fi } # --------------------------------------------------------------------------- # 0. THE CANARY, which is not an assertion about xorgproto at all. # # The guest console drops a suite's FIRST BST result line before the host parser # sees it -- brintos/linux#96. The harness computes its pass count as # total-minus-failures, so THE DROPPED LINE IS COUNTED AS A PASS, and a suite # whose first case is a genuine `not ok` loses the one line that mattered. # So the first line out of this file asserts something already true. # Remove this case when #96 is fixed. bst_ok xorgproto/suite-canary # --------------------------------------------------------------------------- # 1. PAYLOAD -- the directories and the files that anchor them. check_rc xorgproto/dir-X11 0 test -d "$INC/X11" check_rc xorgproto/dir-extensions 0 test -d "$INC/X11/extensions" check_rc xorgproto/dir-fonts 0 test -d "$INC/X11/fonts" check_rc xorgproto/dir-dri 0 test -d "$INC/X11/dri" check_rc xorgproto/dir-GL 0 test -d "$INC/GL" check_rc xorgproto/dir-pkgconfig 0 test -d "$PCD" hasfile xorgproto/hdr-Xproto "$INC/X11/Xproto.h" hasfile xorgproto/hdr-Xmd "$INC/X11/Xmd.h" hasfile xorgproto/hdr-X "$INC/X11/X.h" hasfile xorgproto/hdr-Xdefs "$INC/X11/Xdefs.h" hasfile xorgproto/hdr-Xatom "$INC/X11/Xatom.h" hasfile xorgproto/hdr-keysymdef "$INC/X11/keysymdef.h" hasfile xorgproto/hdr-XF86keysym "$INC/X11/XF86keysym.h" hasfile xorgproto/hdr-randrproto "$INC/X11/extensions/randrproto.h" hasfile xorgproto/hdr-XI2proto "$INC/X11/extensions/XI2proto.h" hasfile xorgproto/hdr-fontsproto "$INC/X11/fonts/FSproto.h" hasfile xorgproto/hdr-glxproto "$INC/GL/glxproto.h" # Xpoll.h is asserted BY NAME because it is the one installed header that is not # in the release tarball: meson generates it with configure_file. A build that # skipped that step would ship 128 headers and every other case here would still # pass. hasfile xorgproto/hdr-Xpoll "$INC/X11/Xpoll.h" hasfile xorgproto/surface-file "$SURF" hasfile xorgproto/probe-shipped "$XT/xorgproto-abi-probe" hasfile xorgproto/abi-ref-shipped "$XT/xorgproto-abi.ref" # --------------------------------------------------------------------------- # 2. THE BUILD-TIME CENSUS, read back on target. # # The recipe counted ${D}${includedir} and ${D}${libdir}/pkgconfig at install # time, where the tree is unambiguously this package's, and refused to package a # short one. Reading the file here is what carries that measurement onto the # image: a rootfs assembled from a stale .deb would present the old numbers. surf=$(cat "$SURF" 2>/dev/null) check_eq xorgproto/surface-headers 129 \ "$(printf '%s\n' "$surf" | sed -n 's/.*headers=\([0-9][0-9]*\).*/\1/p')" check_eq xorgproto/surface-pc 29 \ "$(printf '%s\n' "$surf" | sed -n 's/.*[ ]pc=\([0-9][0-9]*\).*/\1/p')" # --------------------------------------------------------------------------- # 3. THE .pc SET -- every one of the 29 present, and no fewer. missing= npc=0 for e in $XP_PCS; do n=${e%%:*} npc=$((npc + 1)) [ -s "$PCD/$n.pc" ] || missing="$missing $n" done if [ -n "$missing" ]; then bst_notok xorgproto/pc-all-present "absent from $PCD:$missing" else bst_ok xorgproto/pc-all-present fi check_eq xorgproto/pc-set-size 29 "$npc" require pkg-config xorgproto/pkg-config-present # Batched: a spawn is the expensive operation on this target and the whole set # is ~470 bytes of argv, far inside the platform's 8 KiB __wasm32_args_buf. # --exists over a batch succeeds only if every member does, so a green batch # answers for all 29 and only a red one has to be re-asked per file. names= for e in $XP_PCS; do names="$names ${e%%:*}"; done if pkg-config --exists $names 2>/dev/null; then bst_ok xorgproto/pc-exists else bad= for n in $names; do pkg-config --exists "$n" 2>/dev/null || bad="$bad $n" done bst_notok xorgproto/pc-exists "pkg-config cannot evaluate:$bad" fi # --validate parses the file and resolves its Requires without emitting flags. # A .pc that --exists but does not validate is the shape that hides best: the # consumer fails at configure with a message about a package it never named. pkg-config --validate $names >"$work/v.out" 2>&1 vrc=$? if [ "$vrc" -eq 0 ] && [ ! -s "$work/v.out" ]; then bst_ok xorgproto/pc-validate else badv= for n in $names; do pkg-config --validate "$n" >"$work/v1.out" 2>&1 || badv="$badv $n" [ -s "$work/v1.out" ] && badv="$badv $n(noisy)" done bst_notok xorgproto/pc-validate "did not validate cleanly:${badv:- (batch rc=$vrc: $(_bst_1line "$(cat "$work/v.out")"))}" fi # --------------------------------------------------------------------------- # 4. IDENTITY -- all 29 protocol versions, pinned against their own sources. # # This is what the ticket's "--modversion reports the version the recipe pins" # was after, met the only way it can be: the recipe's PV is a bundle tag that # appears nowhere in the shipped bytes (§ header), so each protocol's own # version is pinned instead. An upstream bump then changes a number here rather # than passing silently. # Batched, then re-asked per package only to attribute a mismatch -- # --modversion over a list prints one version per line in the order given, so a # green batch answers for all 29 in one spawn. want_all= for e in $XP_PCS; do want_all="$want_all${e#*:} "; done got_all=$(pkg-config --modversion $names 2>/dev/null) if [ "$got_all" = "$(printf '%s' "$want_all")" ]; then bst_ok xorgproto/pc-modversion else badver= for e in $XP_PCS; do n=${e%%:*}; want=${e#*:} got=$(pkg-config --modversion "$n" 2>/dev/null) [ "$got" = "$want" ] || badver="$badver $n(got=${got:-none},want=$want)" done bst_notok xorgproto/pc-modversion "protocol versions moved:$badver" fi # The recipe's own PV, asserted ABSENT. Recorded as a case rather than a comment # because it is the finding that changed the ticket's requirement, and a future # reader who "fixes" the .pc files to carry 2023.2 should see this go red. # # THE FILE LIST IS BUILT FROM THE 29 NAMES, NEVER GLOBBED. `"$PCD"/*.pc` looks # equivalent and is not: /usr/lib/pkgconfig is shared, the image carries several # hundred .pc files, and the expansion is a 22 KB argv against the platform's # 8192-byte __wasm32_args_buf. The guest then refuses the exec, which does not # fail the case -- it takes the shell down, so the suite neither fails nor # returns and the whole run ends INCOMPLETE with no ==BST-END== # (brintos/hardwarejs#15). Measured here, at 22239B: the first draft of this # case wedged the verify exactly so. The 29 explicit paths are ~950 bytes. pvpaths= for e in $XP_PCS; do pvpaths="$pvpaths $PCD/${e%%:*}.pc"; done if grep -l '2023\.2' $pvpaths >/dev/null 2>&1; then bst_notok xorgproto/pv-absent-from-pc \ "a .pc now carries the recipe PV 2023.2; the 29 protocol versions are the identity this package advertises" else bst_ok xorgproto/pv-absent-from-pc fi # --cflags has to name a directory that actually holds the headers. A .pc whose # prefix arithmetic moved would answer with a path that is simply not there, and # every check above would still pass. # # PKG_CONFIG_ALLOW_SYSTEM_CFLAGS IS REQUIRED, and its absence is what made both # these cases go red on the first on-target run. pkg-config SUPPRESSES an -I that # names a directory already on the compiler's system include path, so # `pkg-config --cflags xproto` answers with the empty string here -- correctly: # the prefix is /usr and -I/usr/include would be redundant. The build-host # harness did not show it, because there the .pc prefix was a scratch directory # and the flag was emitted. Setting the variable asks for the flag pkg-config # COMPUTED rather than the one it decided to print, which is the thing worth # asserting: whether the .pc's prefix arithmetic still resolves to the directory # holding the headers. # # The trailing space is pkg-config's too -- it emits one after the last flag -- # and is trimmed rather than tolerated, because ${cf#-I} below is used as a PATH # and a trailing blank makes the `test -f` fail for a reason unrelated to the port. cf=$(PKG_CONFIG_ALLOW_SYSTEM_CFLAGS=1 pkg-config --cflags xproto 2>/dev/null | sed 's/[[:space:]]*$//') check_eq xorgproto/pc-cflags-xproto "-I$INC" "$cf" d=${cf#-I} check_rc xorgproto/pc-cflags-dir-real 0 test -f "$d/X11/Xproto.h" # xproto.pc also advertises includex11dir, which is what X clients use to reach # the core headers by short name. check_eq xorgproto/pc-var-includex11dir "$INC/X11" \ "$(pkg-config --variable=includex11dir xproto 2>/dev/null | sed 's/[[:space:]]*$//')" # --------------------------------------------------------------------------- # 5. THE WIRE ABI -- the target run against the build host's. # # do_install installs the payload with -m 0644 and the rootfs is read-only at # runtime, so the probe arrives non-executable and cannot be made executable # where it sits. Copy it to /tmp, which is a mode=1777 tmpfs with no noexec. probe=$work/xorgproto-abi-probe if cp "$XT/xorgproto-abi-probe" "$probe" 2>/dev/null && chmod +x "$probe" 2>/dev/null; then # The probe exits 0 whatever it finds, deliberately: the recipe redirects the # build-host run into xorgproto-abi.ref inside a bitbake shell task, and a # non-zero exit there would fail do_install over the ten upstream padding # cases. So a non-zero rc HERE means the binary did not run -- a trap, a # missing import, an exec refusal -- which on this arm is a real and common # failure and is what this case is for. "$probe" > "$work/abi.out" 2>"$work/abi.err" rc=$? if [ "$rc" -ne 0 ]; then bst_notok xorgproto/abi-probe-runs "rc=$rc $(_bst_1line "$(cat "$work/abi.err")")" else bst_ok xorgproto/abi-probe-runs fi tgt_total=$(sed -n 's/^XP-TOTAL //p' "$work/abi.out") ref_total=$(sed -n 's/^XP-TOTAL //p' "$XT/xorgproto-abi.ref") # The probe reached its end. Without this, a run truncated after ten lines # would present a short BAD list that happened to match a short reference. if [ -n "$tgt_total" ]; then bst_ok xorgproto/abi-probe-complete else bst_notok xorgproto/abi-probe-complete \ "no XP-TOTAL line: $(_bst_1line "$(cat "$work/abi.out")")" fi # Coverage floor, bounded from below and dated 2026-08-22. It is the same # number the recipe refuses to build under; asserting it again here is what # catches a rootfs carrying a probe from an older, thinner build. if [ -n "$tgt_total" ] && [ "$tgt_total" -ge 1074 ]; then bst_ok xorgproto/abi-coverage else bst_notok xorgproto/abi-coverage "covers ${tgt_total:-0} structures, expected not less than 1074" fi # The two runs must cover the SAME set, or the comparison below is between # different questions. check_eq xorgproto/abi-total-agrees "$ref_total" "$tgt_total" # THE ASSERTION THIS SUITE EXISTS FOR. Identical mismatch sets means wasm32 # lays out every X wire structure exactly as the build host does. A line only # the target reports is an arm-specific layout fault; one only the reference # reports means wasm32 silently AGREES where x86-64 pads, which is just as # much a divergence and is caught by the same comparison. sed -n 's/^XP BAD //p' "$work/abi.out" | sort > "$work/tgt.bad" sed -n 's/^XP BAD //p' "$XT/xorgproto-abi.ref" | sort > "$work/ref.bad" if cmp -s "$work/tgt.bad" "$work/ref.bad"; then bst_ok xorgproto/abi-matches-buildhost else bst_notok xorgproto/abi-matches-buildhost \ "wasm32 and the build host disagree: $(_bst_1line "$(diff "$work/ref.bad" "$work/tgt.bad" 2>&1)")" fi # The ten upstream padding cases, asserted by count so the suite states what it # is tolerating rather than merely tolerating it. They are freedesktop's, in a # public repository, and this project files nothing there # (docs/conventions/no-public-upstream-reports.md). check_eq xorgproto/abi-known-upstream-mismatches 10 "$(wc -l < "$work/ref.bad" | tr -d ' ')" # THE PRECONDITION THAT MAKES THE COMPARISON ABOVE MEAN ANYTHING. # # X11/X.h types XID, Atom, Mask, VisualID and Time as `unsigned long` unless # _XSERVER64 is defined. The reference is built WITH that define, so those are # CARD32 there; the target probe is built WITHOUT it, so on this arm they are # `unsigned long` -- and the two agree only because `unsigned long` is 4 bytes # here. Assert that, rather than assume it: on an arm where long were 8 bytes # the target would carry 8-byte XIDs, every wire struct containing one would # miss its own sz_, and abi-matches-buildhost would go red without saying why. # This case says why. mdl=$(sed -n 's/^XP-MODEL //p' "$work/abi.out") check_eq xorgproto/abi-wire-types-32bit "long=4 ptr=4 xid=4 time=4" "$mdl" else bst_notok xorgproto/abi-probe-stage "cannot stage $XT/xorgproto-abi-probe" fi rm -rf "$work"