# BST-TIMEOUT: 1200 # 186 cases, 101s measured locally; the nightly's browser run still exceeds 300s . "$BST_LIB" # libmxml (Mini-XML 3.3.1) is a small tree-building XML parser in plain C -- the # C counterpart to the C++ tinyxml2 this image already ships (74-libtinyxml2). # Like every library port here the distro is static-only, so the payload is one # header plus a pkg-config file (-dev) and libmxml.a (-staticdev). There is no # on-target compiler, so the on-target assertion is that the shipped payload is # real and complete; the RUNTIME evidence lives in the ticket's design doc, where # a probe cross-linked against this very archive was executed on the image. # # Three things this suite pins that a suite copied from a neighbour would get # wrong, all of them measured rather than assumed: # # 1. THE HEADER'S VERSION MACROS ARE STALE. mxml.h says MXML_MAJOR_VERSION 3 / # MXML_MINOR_VERSION 2 while the package, the .pc and the tarball all say # 3.3.1 -- upstream never bumped the macros for the 3.3.x series. 74-libtinyxml2 # asserts its header macros AGREE with the recipe version; asserting that here # would fail against a correct port. So the suite asserts the values that are # actually there, and separately asserts the .pc carries 3.3.1, which is the # number a consumer's pkg-config check reads. # 2. Cflags IS EMPTY AT THE SOURCE, not by pkgconf filtering. libtinyxml2.pc # carries `-I${includedir}` and 76-libdaemon.test established that pkgconf # keeps a -D and filters the system -I. mxml's configure computes # PC_CFLAGS="" outright when includedir is /usr/include -- so the field in the # file is empty before pkgconf ever sees it. The suite asserts BOTH: the empty # field in the file, and that --keep-system-cflags still prints nothing, which # is what distinguishes "empty at source" from "filtered". # 3. THREADS ARE ON. The recipe's PACKAGECONFIG defaults to "threads", and # mxml's configure decides that with an AC_LINK_IFELSE -- so it can silently # turn ITSELF off on an arm where pthread_create will not link, with no recipe # change and no build warning. The pthread_* references in the archive are the # only on-target evidence that it did not, and they are what makes mxml's # per-thread global state (the _mxml_global pthread key) real here. root=${BST_ROOT:-} inc=$root/usr/include/mxml.h pc=$root/usr/lib/pkgconfig/mxml.pc lib=$root/usr/lib/libmxml.a check libmxml/header-present test -f "$inc" check libmxml/header-nonempty test -s "$inc" check_out libmxml/header-guard "# define _mxml_h_" cat "$inc" check_out libmxml/header-extern-c 'extern "C" {' cat "$inc" # Version, as it actually is. See note 1 above: these two are 3/2, the .pc is # 3.3.1, and both readings are correct for this upstream. check_out libmxml/ver-major "# define MXML_MAJOR_VERSION" cat "$inc" check_eq libmxml/ver-major-is-3 3 \ "$(awk '/define[ \t]+MXML_MAJOR_VERSION/{print $4}' "$inc")" check_eq libmxml/ver-minor-is-2 2 \ "$(awk '/define[ \t]+MXML_MINOR_VERSION/{print $4}' "$inc")" # The header pulls in the five C headers a caller would otherwise have to include # before it -- mxml.h is self-sufficient, which is why no consumer recipe needs a # prologue. for h in stdio.h stdlib.h string.h ctype.h errno.h; do check_out "libmxml/hdr-includes-${h%.h}" "# include <$h>" cat "$inc" done # The constants a caller names. The four *_CALLBACK macros are the load-callback # selectors and they expand to the four semi-private symbols asserted in the # archive further down -- so these lines and those symbols have to agree or a # consumer gets a link error rather than a compile error. check_out libmxml/const-tab "# define MXML_TAB" cat "$inc" check_out libmxml/cb-no "# define MXML_NO_CALLBACK" cat "$inc" check_out libmxml/cb-text "# define MXML_TEXT_CALLBACK" cat "$inc" check_out libmxml/cb-integer "MXML_INTEGER_CALLBACK mxml_integer_cb" cat "$inc" check_out libmxml/cb-opaque "MXML_OPAQUE_CALLBACK mxml_opaque_cb" cat "$inc" check_out libmxml/cb-real "MXML_REAL_CALLBACK mxml_real_cb" cat "$inc" check_out libmxml/cb-ignore "MXML_IGNORE_CALLBACK mxml_ignore_cb" cat "$inc" check_out libmxml/const-no-parent "# define MXML_NO_PARENT" cat "$inc" for c in MXML_DESCEND MXML_NO_DESCEND MXML_DESCEND_FIRST \ MXML_WS_BEFORE_OPEN MXML_WS_AFTER_OPEN MXML_WS_BEFORE_CLOSE \ MXML_WS_AFTER_CLOSE MXML_ADD_BEFORE MXML_ADD_AFTER MXML_ADD_TO_PARENT; do check_out "libmxml/const-$c" "# define $c" cat "$inc" done # MXML_DESCEND_FIRST is -1 and MXML_IGNORE is -1: both are sentinels a caller # passes by name, and a header that silently changed either would break search # and typed-load semantics without any symbol moving. check_eq libmxml/const-descend-first--1 -1 \ "$(awk '/define[ \t]+MXML_DESCEND_FIRST/{print $4}' "$inc")" # The two enums. mxml_type_t is the node-kind tag every Get/Set accessor keys # off; mxml_sax_event_t is the streaming half's event set. check_out libmxml/enum-type "typedef enum mxml_type_e" cat "$inc" check_out libmxml/enum-sax "typedef enum mxml_sax_event_e" cat "$inc" for e in "MXML_IGNORE = -1" MXML_ELEMENT MXML_INTEGER MXML_OPAQUE MXML_REAL \ MXML_TEXT MXML_CUSTOM; do check_out "libmxml/type-${e% =*}" "$e" cat "$inc" done for e in MXML_SAX_CDATA MXML_SAX_COMMENT MXML_SAX_DATA MXML_SAX_DIRECTIVE \ MXML_SAX_ELEMENT_CLOSE MXML_SAX_ELEMENT_OPEN; do check_out "libmxml/sax-$e" "$e" cat "$inc" done # The opaque struct handles and the seven function-pointer typedefs. A caller # never sees inside mxml_node_t; these typedefs are the whole of the extension # surface (custom data, entities, load/save shaping, SAX, error reporting). check_out libmxml/type-node "typedef struct _mxml_node_s mxml_node_t" cat "$inc" check_out libmxml/type-index "typedef struct _mxml_index_s mxml_index_t" cat "$inc" for t in mxml_custom_destroy_cb_t mxml_error_cb_t mxml_custom_load_cb_t \ mxml_custom_save_cb_t mxml_entity_cb_t mxml_load_cb_t \ mxml_save_cb_t mxml_sax_cb_t; do check_out "libmxml/typedef-$t" "$t" cat "$inc" done # pkg-config metadata. This is where 3.3.1 is stated on target, and the one place # a future recipe DEPENDing on mxml reads its link line from. check libmxml/pc-present test -f "$pc" check_out libmxml/pc-name "Name: Mini-XML" cat "$pc" check_out libmxml/pc-version "Version: 3.3.1" cat "$pc" check_out libmxml/pc-desc "Description: Lightweight XML support library" cat "$pc" check_out libmxml/pc-libs "Libs: -lmxml" cat "$pc" # The paths in the .pc are LITERAL, not ${prefix}-interpolated -- mxml.pc.in takes # them from autoconf's @libdir@/@includedir@, which OE passes as absolute paths. # tinyxml2.pc's `libdir=${prefix}/lib` is the other convention; both resolve the # same through pkgconf, and asserting the wrong one silently passes nothing. check_out libmxml/pc-prefix "prefix=/usr" cat "$pc" check_out libmxml/pc-exec-prefix "exec_prefix=/usr" cat "$pc" check_out libmxml/pc-libdir "libdir=/usr/lib" cat "$pc" check_out libmxml/pc-includedir "includedir=/usr/include" cat "$pc" # Cflags is present as a field and empty as a value (note 2 above). Written as an # exact field match so a future -I or -D creeping in is caught rather than # absorbed by a substring test. check libmxml/pc-cflags-field grep -q '^Cflags:' "$pc" check_eq libmxml/pc-cflags-empty "" \ "$(sed -n 's/^Cflags:[ ]*//p' "$pc")" # No -L: PC_LIBS drops it when libdir is the system /usr/lib, so a -L here would # mean the port was configured with a non-default libdir. check libmxml/pc-no-bigL sh -c 'grep "^Libs:" "$1" | grep -qv -- "-L"' _ "$pc" check libmxml/lib-present test -f "$lib" check libmxml/lib-nonempty test -s "$lib" check_out libmxml/lib-archive "!" head -c 8 "$lib" # Flatten the archive to printable text once. Same formulation 74-libtinyxml2 and # 76-libdaemon use: GNU grep suppresses matches inside binary data, and busybox tr # does not handle [:print:] under -c, so the octal range is what both agree on. flat=${TMPDIR:-/tmp}/bst-libmxml.$$ tr -c '\40-\176\n' ' ' < "$lib" > "$flat" 2>/dev/null # The ten members, one per upstream translation unit. All ten names are short, so # this archive carries no `//` long-name string table -- the member names appear # verbatim in the headers. Checking them by name rather than by count means a # member that is renamed, dropped or added is named in the failure. for m in mxml-attr mxml-entity mxml-file mxml-get mxml-index \ mxml-node mxml-search mxml-set mxml-private mxml-string; do check "libmxml/member-$m" grep -q "$m\.o" "$flat" done # The public API, in full: all 73 mxml* functions the header declares. This is # the deliverable -- nothing in mxml is inline or macro-only, so every one of # these has to be an out-of-line definition in the archive or a consumer that # compiles will fail to link. for s in mxmlAdd mxmlDelete mxmlElementDeleteAttr mxmlElementGetAttr \ mxmlElementGetAttrByIndex mxmlElementGetAttrCount mxmlElementSetAttr \ mxmlElementSetAttrf mxmlEntityAddCallback mxmlEntityGetName \ mxmlEntityGetValue mxmlEntityRemoveCallback mxmlFindElement \ mxmlFindPath mxmlGetCDATA mxmlGetCustom mxmlGetElement \ mxmlGetFirstChild mxmlGetInteger mxmlGetLastChild mxmlGetNextSibling \ mxmlGetOpaque mxmlGetParent mxmlGetPrevSibling mxmlGetReal \ mxmlGetRefCount mxmlGetText mxmlGetType mxmlGetUserData \ mxmlIndexDelete mxmlIndexEnum mxmlIndexFind mxmlIndexGetCount \ mxmlIndexNew mxmlIndexReset mxmlLoadFd mxmlLoadFile mxmlLoadString \ mxmlNewCDATA mxmlNewCustom mxmlNewElement mxmlNewInteger \ mxmlNewOpaque mxmlNewOpaquef mxmlNewReal mxmlNewText mxmlNewTextf \ mxmlNewXML mxmlRelease mxmlRemove mxmlRetain mxmlSaveAllocString \ mxmlSaveFd mxmlSaveFile mxmlSaveString mxmlSAXLoadFd mxmlSAXLoadFile \ mxmlSAXLoadString mxmlSetCDATA mxmlSetCustom mxmlSetCustomHandlers \ mxmlSetElement mxmlSetErrorCallback mxmlSetInteger mxmlSetOpaque \ mxmlSetOpaquef mxmlSetReal mxmlSetText mxmlSetTextf mxmlSetUserData \ mxmlSetWrapMargin mxmlWalkNext mxmlWalkPrev; do check "libmxml/sym-$s" grep -q "$s" "$flat" done # The five "semi-private" names the header declares below its own # `Semi-private functions...` banner. Four of them are what the *_CALLBACK macros # above expand to, so they are reachable from ordinary consumer source and have to # be exported; mxml_error is the printf-style reporter the error callback wraps. for s in mxml_error mxml_ignore_cb mxml_integer_cb mxml_opaque_cb mxml_real_cb; do check "libmxml/semipriv-$s" grep -q "$s" "$flat" done # Threads (note 3 above). mxml keeps its per-thread state -- the error callback, # the wrap margin, the custom/entity handler tables -- behind a pthread key, and # _mxml_global() is the accessor. These five undefined references are the only # on-target proof the AC_LINK_IFELSE probe linked and the threaded build was kept. check libmxml/threads-global grep -q '_mxml_global' "$flat" for s in pthread_key_create pthread_getspecific pthread_setspecific \ pthread_once pthread_key_delete; do check "libmxml/threads-$s" grep -q "$s" "$flat" done # The two string helpers that are exported but NOT declared in the header. They # exist because mxml-string.c fills in what a strict-POSIX target may lack; the # assertion is that they are still present, since several members call them. for s in _mxml_strdupf _mxml_vstrdupf; do check "libmxml/internal-$s" grep -q "$s" "$flat" done # The wasm32 arm witness: the archive members are wasm objects, not host ELF. check libmxml/arm-not-elf sh -c '! head -c 512 "$1" | grep -q "ELF"' _ "$lib" # Size window. libmxml.a is ~300 KB here; a build that dropped members or lost # the symbol index would fall well below, and one that accidentally kept debug # info would blow past. sz=$(wc -c < "$lib") check libmxml/lib-size-window sh -c '[ "$1" -gt 200000 ] && [ "$1" -lt 500000 ]' _ "$sz" # Static-only packaging policy. mxml's configure builds libmxml.so.1.6 by default # and the distro's blanket --disable-shared is what turns it off, so a stray .so # here would mean that flag stopped reaching this recipe. check libmxml/no-shared sh -c 'set -- "$1"/usr/lib/libmxml.so*; [ ! -e "$1" ]' _ "$root" check libmxml/no-libtool sh -c '[ ! -e "$1/usr/lib/libmxml.la" ]' _ "$root" # No binary. mxml's Makefile builds testmxml as part of ALLTARGETS, but configure # selects CROSSTARGETS -- the library alone -- once build != host. A testmxml on # the image would mean the cross detection had silently flipped. check libmxml/no-testmxml sh -c '[ ! -e "$1/usr/bin/testmxml" ]' _ "$root" check libmxml/no-mxmldoc sh -c '[ ! -e "$1/usr/bin/mxmldoc" ]' _ "$root" # libmxml-doc is 1.6 MB of epub/html/png and is deliberately NOT installed, in # line with every other library port on this image. Its man page rides in the same # package, so both are absent together. check libmxml/no-doc-dir sh -c '[ ! -d "$1/usr/share/doc/mxml" ]' _ "$root" check libmxml/no-man sh -c '[ ! -e "$1/usr/share/man/man3/mxml.3" ]' _ "$root" rm -f "$flat" # pkgconf queries. These run only where pkgconf is on the image; the require # guard would end the file, so this leg is written as an explicit presence test # so the static assertions above always run. if command -v pkgconf >/dev/null 2>&1; then pcdir=$root/usr/lib/pkgconfig check_eq libmxml/pkgconf-modversion 3.3.1 \ "$(PKG_CONFIG_LIBDIR=$pcdir pkgconf --modversion mxml 2>&1)" check_eq libmxml/pkgconf-libs "-lmxml" \ "$(PKG_CONFIG_LIBDIR=$pcdir pkgconf --libs mxml 2>&1)" # Empty at the source, and still empty with filtering disabled -- the pair is # what distinguishes this from libatomic-ops, whose --cflags is empty only # because pkgconf filtered the system -I out of a non-empty field. check_eq libmxml/pkgconf-cflags "" \ "$(PKG_CONFIG_LIBDIR=$pcdir pkgconf --cflags mxml 2>&1)" check_eq libmxml/pkgconf-cflags-keep "" \ "$(PKG_CONFIG_LIBDIR=$pcdir pkgconf --keep-system-cflags --cflags mxml 2>&1)" check_eq libmxml/pkgconf-var-libdir "/usr/lib" \ "$(PKG_CONFIG_LIBDIR=$pcdir pkgconf --variable=libdir mxml 2>&1)" check_eq libmxml/pkgconf-var-includedir "/usr/include" \ "$(PKG_CONFIG_LIBDIR=$pcdir pkgconf --variable=includedir mxml 2>&1)" check_eq libmxml/pkgconf-var-prefix "/usr" \ "$(PKG_CONFIG_LIBDIR=$pcdir pkgconf --variable=prefix mxml 2>&1)" check libmxml/pkgconf-exists env PKG_CONFIG_LIBDIR=$pcdir pkgconf --exists mxml check libmxml/pkgconf-atleast env PKG_CONFIG_LIBDIR=$pcdir pkgconf --atleast-version=3.3.1 mxml check_rc libmxml/pkgconf-max 1 env PKG_CONFIG_LIBDIR=$pcdir pkgconf --max-version=3.3.0 mxml # --keep-system-libs prints the SAME -lmxml, with no -L/usr/lib appearing. That # is the libs-side counterpart of the Cflags pair above and it is the assertion # most likely to be got wrong by pattern-matching a neighbour: for a .pc that # DOES carry `-L${libdir}`, disabling the filter restores it. Here nothing is # restored, because mxml's configure never emitted a -L at all -- so identical # output under --keep-system-libs is what proves the field is empty at the # source rather than merely filtered at query time. check_eq libmxml/pkgconf-libs-keep "-lmxml" \ "$(PKG_CONFIG_LIBDIR=$pcdir pkgconf --keep-system-libs --libs mxml 2>&1)" else bst_skip libmxml/pkgconf-present "pkgconf not on this image" fi