. "$BST_LIB" # argp-standalone: the GNU hierarchical argument parser as a standalone # library. Options are declared as a table carrying their own help text rather # than as a getopt string; --help, --usage and --version are generated from # that table and word-wrapped; and child parsers compose, so a library can own # its share of a program's command line. It ships here as two files and no # program -- /usr/lib/libargp.a and /usr/include/argp.h -- which is why this # suite is mostly two cross-linked probes. # # --------------------------------------------------------------------------- # WHY THIS PORT EXISTS ON A glibc IMAGE AT ALL # # poky refuses the recipe on every non-musl host (COMPATIBLE_HOST = # ".*-musl.*") because on a GNU system argp is part of libc. The premise is # half true here, and the half that fails is what the port is for: the # toolchain's glibc 2.43 DOES carry argp -- argp-parse.o and its eight siblings # are in the bitcode pool -- but that libc is linked into each binary rather # than packaged, so this image ships no libc.a and no libc headers at all. # Nothing on the rootfs can reach argp without this package. The layer change # is one bbappend relaxing COMPATIBLE_HOST for this MACHINE; see # recipes-support/argp-standalone/argp-standalone_%.bbappend. # # --------------------------------------------------------------------------- # THE A/B THAT MAKES A RED ATTRIBUTABLE # # src/argp_probe.c is cross-linked TWICE with wasmcc-glibc from one source: # # data/argp-probe + /usr/lib/libargp.a, -DPROBE_IMPL_LIBARGP # -> the argp THIS PORT SHIPS # data/argp-libc-probe (no archive) # -> glibc's own argp, out of the pool # # Both are scored below, case for case, under two id prefixes. A case red in # argp/ and green in argp-libc/ is argp-standalone's; a case red in both is the # platform's. argp/ab-parity states that comparison as one case. Provenance is # not taken on trust: argp/impl/libargp-linked passes only if the archive's own # objects are in the module, because it calls _argp_input(), which # argp-standalone exports and glibc does not. # # THE hwjs-cc#15 SITES ARE ALL ON THE HELP PATH, which is why help is scored # apart from parse. argp-help.c has two dynamic alloca()s whose address escapes # into a called function -- short_no_arg_opts in hol_usage and pattern_levels # in argp_state_help -- the class that cost acl, sharutils, mbuffer, onig, # mtd-utils, squashfs-tools and dpkg a patch each. argp_parse() allocas too, # but at CONSTANT size (the four-entry argp_child array and the synthetic # top_argp it wraps the user's argp in), and that address escapes as well. # Parse green + help red is the first class; both red is the second. # # ARGP IS LAYERED OVER THE LIBC'S getopt_long/getopt_long_only AND THE GLOBAL # optind/optarg/opterr/optopt, so every parse case is also a test of glibc's # getopt on this arm. parse/repeat-same-result is the one that says so: argp # resets optind before each call, and a second parse in the same process # diverges from the first only if getopt's reinitialisation is broken. # # EVERY EXPECTATION IN THE PROBE IS THE VALUE A NATIVE x86_64 BUILD OF THE # IDENTICAL SOURCE PRODUCES, checked before this suite was ever run on target: # 64/64 for the libargp build and 63/63 for the libc build (the difference is # impl/libargp-linked, which exists only in the former), with every AGPV- value # line byte-identical between the two. That control caught four wrong # expectations of mine that would otherwise have been reported as target reds # -- argp_help() correctly does NOT list --help/--version, because those exist # only in the synthetic top_argp that argp_parse builds at call time; getopt # rather than argp prints "invalid option", and prints it straight to stderr # instead of through state->err_stream; and _argp_input(state->root_argp, ...) # is always NULL, because convert_options creates no group for an option-less, # parser-less argp. inc=/usr/include/argp.h lib=/usr/lib/libargp.a check argp/header-present test -f "$inc" check argp/archive-present test -f "$lib" if [ ! -f "$lib" ] || [ ! -f "$inc" ]; then bst_skip argp/present "argp-standalone-dev/-staticdev not installed" exit 0 fi check argp/archive-nonempty test -s "$lib" # The public API the header must declare. argp.h is copied from glibc's, so a # truncated or wrong-project header shows up here rather than as a compile # error nobody on the target can see. for sym in argp_parse argp_help argp_error argp_failure argp_usage \ argp_state_help argp_program_version argp_program_bug_address \ argp_err_exit_status ARGP_KEY_ARG ARGP_KEY_END OPTION_ARG_OPTIONAL \ OPTION_HIDDEN ARGP_IN_ORDER ARGP_LONG_ONLY; do if grep -q "$sym" "$inc"; then bst_ok "argp/header-declares-$sym" else bst_notok "argp/header-declares-$sym" "not in $inc"; fi done # --- the archive member table --------------------------------------------- # ar keeps each member's filename in its header, so the object list is readable # on target with nothing but grep. Both directions are asserted: absence alone # would also be satisfied by an archive that built nothing. arcobjs=$(grep -ao '[A-Za-z0-9_-]*\.o' "$lib" 2>/dev/null | sort -u) nl=' ' missing="" for o in argp-ba.o argp-eexst.o argp-fmtstream.o argp-help.o argp-parse.o \ argp-pv.o argp-pvh.o; do case "$nl$arcobjs$nl" in *"$nl$o$nl"*) ;; *) missing="$missing $o" ;; esac done if [ -z "$missing" ]; then bst_ok argp/archive-members-present else bst_notok argp/archive-members-present "missing:$missing"; fi # Deliberately NOT in the archive, each for its own reason: # argp-xinl.o / argp-fs-xinl.o Makefile.am's libargp_a_SOURCES leaves them # out; the extern-inline bodies are static # inline in argp.h on this build. # mempcpy.o strndup.o AC_REPLACE_FUNCS compiles these only when the # strchrnul.o libc lacks them, and glibc has all three -- so # a member here would mean configure's libc # probes came back wrong on this arm. # argp-test.o noinst_PROGRAMS, never in the library. extra="" for o in argp-xinl.o argp-fs-xinl.o mempcpy.o strndup.o strchrnul.o \ argp-test.o; do case "$nl$arcobjs$nl" in *"$nl$o$nl"*) extra="$extra $o" ;; esac done if [ -z "$extra" ]; then bst_ok argp/archive-no-unexpected-members else bst_notok argp/archive-no-unexpected-members "unexpected:$extra"; fi # --- the two probes -------------------------------------------------------- scratch=${TMPDIR:-/tmp}/bst-argp.$$ mkdir -p "$scratch" 2>/dev/null probe_pass=0; probe_total=0; probe_ok_ids="" # Run one probe and emit one BST case per AGP- line it printed. The translation # loop is pure parameter expansion -- no sed/awk per line -- because a fork per # case would be the expensive part of this suite on a wasm32 target, not the # work itself. run_probe() { # $1 = data file name, $2 = BST id prefix probe_pass=0; probe_total=0; probe_ok_ids="" _bin=$scratch/$1 if ! cp "$BST_DATA/$1" "$_bin" 2>/dev/null || ! chmod +x "$_bin" 2>/dev/null; then bst_notok "$2/stage" "cannot stage $BST_DATA/$1" return 1 fi _out=$("$_bin" "$scratch" 2>&1); _rc=$? printf '%s\n' "$_out" | while IFS= read -r _l; do case "$_l" in "AGP-TOTAL "*) ;; "AGP-"*" OK") _id=${_l#AGP-}; _id=${_id% OK} printf 'BST ok %s/%s\n' "$2" "$_id" ;; "AGP-"*" FAIL("*) _id=${_l#AGP-}; _id=${_id%% FAIL(*} _d=${_l#*FAIL(}; _d=${_d%\)} printf 'BST not ok %s/%s : %s\n' "$2" "$_id" "$_d" ;; "AGPV-"*) printf '# %s\n' "$_l" ;; esac done # Count the case lines only -- AGP-TOTAL is the probe's own summary, not a case. probe_pass=$(printf '%s\n' "$_out" | grep -c '^AGP-[^ ]* OK$') _probe_fail=$(printf '%s\n' "$_out" | grep -c '^AGP-[^ ]* FAIL(') probe_total=$((probe_pass + _probe_fail)) probe_ok_ids=$(printf '%s\n' "$_out" | sed -n 's|^AGP-\([^ ]*\) OK$|\1|p' | sort) if [ "$_rc" -eq 0 ]; then bst_ok "$2/probe-exit0" else bst_notok "$2/probe-exit0" "rc=$_rc $probe_pass/$probe_total pass"; fi return 0 } if [ -f "$BST_DATA/argp-probe" ]; then run_probe argp-probe argp port_ok=$probe_ok_ids; port_pass=$probe_pass; port_total=$probe_total else bst_notok argp/probe-present "data/argp-probe missing from the image" port_ok=""; port_pass=0; port_total=0 fi if [ -f "$BST_DATA/argp-libc-probe" ]; then run_probe argp-libc-probe argp-libc libc_ok=$probe_ok_ids; libc_pass=$probe_pass; libc_total=$probe_total else bst_notok argp-libc/probe-present "data/argp-libc-probe missing from the image" libc_ok=""; libc_pass=0; libc_total=0 fi # --- the A/B, stated as one case ------------------------------------------ # Every case the libc build passes, the libargp build must pass too. The # converse is not required: impl/libargp-linked exists only in the port build. # A non-empty regression list is the port's own defect; an empty one with both # sides short of their totals is the platform's, and the per-case reds above # say which cases. if [ -n "$port_ok" ] || [ -n "$libc_ok" ]; then regress=$(printf '%s\n' "$libc_ok" | while IFS= read -r id; do [ -n "$id" ] || continue case "$nl$port_ok$nl" in *"$nl$id$nl"*) ;; *) printf '%s ' "$id" ;; esac done) if [ -z "$regress" ]; then bst_ok argp/ab-parity else bst_notok argp/ab-parity "libc passes, libargp does not: $(_bst_1line "$regress")"; fi printf '# AGPV-ab-tally libargp=%s/%s libc=%s/%s\n' \ "$port_pass" "$port_total" "$libc_pass" "$libc_total" fi rm -rf "$scratch" 2>/dev/null