# BST-TIMEOUT: 120 . "$BST_LIB" # span-lite 0.11.0 (SRCREV 50f55c59, upstream master) -- a single-header # std::span for C++98/11/14/17: a non-owning view over contiguous storage, # carrying a pointer and a length. Header-only: the port ships ONE header, # /usr/include/nonstd/span.hpp, plus the find_package(span-lite) cmake package # under /usr/lib/cmake/span-lite. No archive, no shared object, no CLI, no .pc # (upstream ships no pkg-config template, which is a difference from glm and is # recorded rather than repaired). # # WHY THERE IS A PROBE. A header-only port ships bytes NO COMPILER ON THIS ARM # NEED EVER HAVE LOOKED AT, so "the file is present" is compatible with a # header that does not compile. For a SPAN it is also compatible with one that # compiles and hands back a view of the wrong bytes -- which is a silent wrong # answer, not a crash. The recipe therefore builds span-lite-probe from # ${S}/include during the port, making do_compile the port's first consumer, # and the probe RUNS here. Sections 1-2 assert the PAYLOAD; sections 3-6 assert # the LIBRARY. # # THE CONTRACT BEHAVIOUR IS THE HEADLINE FINDING AND SECTION 6 IS WHERE IT IS # ESTABLISHED. An out-of-range access through nonstd::span on this image # TERMINATES; it does not throw. That is not a divergence this port introduced # -- span.hpp:138-151 auto-discovers the compiler's exceptions state, finds # none on the distro's C++ driver, and FORCES span_CONFIG_CONTRACT_VIOLATION_ # THROWS to 0 and ..._TERMINATES to 1 itself. A consumer substituting # nonstd::span for a pointer/length pair has to know that before writing a # catch it will never reach. The span-lite entry in docs/brintos-test-plan.md # records the choice; it is cited by SUBJECT and not by number, because entry # numbers are assigned at merge time and a number written into a SHIPPED file # goes stale the moment another port lands first. # # SECTION 3 IS THE TRIPWIRE. It asserts the compile-time state everything after # it was measured under. A red there means the toolchain or the header # configuration moved -- not necessarily bad news, but every value below would # have to be re-measured rather than merely re-read. # # CROSS-ARM: every value in sections 4-5 is byte-identical to the same probe # source compiled with g++ -Os -std=c++17 -fno-exceptions on x86-64 and run # there, EXCEPT the five that are word-size facts by construction # (P-PTR-BITS, P-SIZE-T-BITS, P-SPAN-DYN-BYTES, P-SPAN-STATIC-BYTES, # P-DYNAMIC-EXTENT). Those five are asserted at their wasm32 values with the # x86-64 reading named beside them, so a reader can tell an arm difference from # a regression without a rebuild. Nothing here is floating point: a span # computes indices and lengths, so there is no tolerance to widen and a # divergence is a real finding. # # Style follows 76-glm and 76-libeigen: the probe is run ONCE for the report # and its output parsed in a single fork-free loop, because the runner's # per-suite bound is a budget in process spawns rather than in cases. The two # contract arms are the exception and must be separate processes -- each one # dies on purpose. inc=/usr/include/nonstd hdr=/usr/include/nonstd/span.hpp cm=/usr/lib/cmake/span-lite PT=/usr/libexec/span-lite-tests # --------------------------------------------------------------------------- # 1. The payload. check span-lite/inc-dir test -d "$inc" check span-lite/header test -f "$hdr" # ONE header and nothing else under nonstd/ -- span-lite is a single-file # library, and a tree that grew a second file would mean the recipe installed # something upstream does not ship or a sibling port leaked into the same # namespace directory. nh=$(find "$inc" -type f 2>/dev/null | grep -c .) check_eq span-lite/nonstd-file-count "1" "$nh" # No build system in ${includedir}: a CMakeLists.txt reaching the installed # tree is what this pins. check_eq span-lite/no-cmakelists "0" "$(find "$inc" -name CMakeLists.txt 2>/dev/null | grep -c .)" # The header's own statement of its version. Three independent statements ship # in this package -- these macros, the cmake config, and the cmake config # version -- and a port that got one of them wrong would still pass any # existence check. h=$(cat "$hdr" 2>/dev/null) case "$h" in *"#define span_lite_MAJOR 0"*) hm=0 ;; *) hm=? ;; esac case "$h" in *"#define span_lite_MINOR 11"*) hn=11 ;; *) hn=? ;; esac case "$h" in *"#define span_lite_PATCH 0"*) hp=0 ;; *) hp=? ;; esac check_eq span-lite/header-version "0.11.0" "$hm.$hn.$hp" # The auto-discovery block that decides this image's contract channel. It is # asserted as TEXT here and as BEHAVIOUR in sections 3 and 6, because a header # that silently lost it would still compile and would then return garbage for # an out-of-range index instead of terminating. case "$h" in *"span_CONFIG_NO_EXCEPTIONS"*) ax=present ;; *) ax=absent ;; esac check_eq span-lite/header-no-exceptions-block "present" "$ax" case "$h" in *"report_contract_violation"*) rc=present ;; *) rc=absent ;; esac check_eq span-lite/header-contract-reporter "present" "$rc" case "$h" in *"std::terminate();"*) tm=present ;; *) tm=absent ;; esac check_eq span-lite/header-terminate-arm "present" "$tm" # The names a consumer writes. nonstd::span and nonstd::dynamic_extent are the # whole public surface most code touches. case "$h" in *"namespace nonstd"*) ns=present ;; *) ns=absent ;; esac check_eq span-lite/header-namespace "present" "$ns" case "$h" in *"dynamic_extent"*) de=present ;; *) de=absent ;; esac check_eq span-lite/header-dynamic-extent "present" "$de" # --------------------------------------------------------------------------- # 2. The integration surface. # # NO .pc FILE, AND THAT IS UPSTREAM'S ARRANGEMENT rather than a defect of this # port -- span-lite ships no pkg-config template at all, where glm does. It is # asserted as an absence rather than skipped, so that an upstream release which # starts shipping one shows up here as a red with this comment attached instead # of passing unnoticed. check_eq span-lite/no-pkgconfig "0" "$(find /usr/lib/pkgconfig -name 'span-lite*' 2>/dev/null | grep -c .)" check span-lite/cmake-dir test -d "$cm" check span-lite/cmake-config test -f "$cm/span-lite-config.cmake" check span-lite/cmake-version test -f "$cm/span-lite-config-version.cmake" check span-lite/cmake-targets test -f "$cm/span-lite-targets.cmake" cfgv=$(cat "$cm/span-lite-config-version.cmake" 2>/dev/null) case "$cfgv" in *'set(PACKAGE_VERSION "0.11.0")'*) pkv=ok ;; *) pkv=absent ;; esac check_eq span-lite/cmake-pkg-version "ok" "$pkv" # The include path a consumer compiles against must be the SHIPPED one, not a # build-tree path -- the failure a bare existence check cannot see. cmake's # configure_package_config_file emits it relative to PACKAGE_PREFIX_DIR. tg=$(cat "$cm/span-lite-targets.cmake" 2>/dev/null) case "$tg" in *'add_library(nonstd::span-lite INTERFACE IMPORTED)'*) ti=interface ;; *) ti=other ;; esac check_eq span-lite/cmake-targets-interface "interface" "$ti" case "$tg" in *'_IMPORT_PREFIX}/include'*) tp=relative ;; *) tp=other ;; esac check_eq span-lite/cmake-targets-include-relative "relative" "$tp" # A build-tree path leaking into a shipped cmake file is invisible to every # check above and is what this one catches. case "$tg" in *"/usr/src/debug"*|*"/build/tmp"*|*"/workdir"*) tl=leak ;; *) tl=clean ;; esac check_eq span-lite/cmake-targets-no-buildpath "clean" "$tl" # --------------------------------------------------------------------------- # 3-6. The probe. Run ONCE for the report; everything below reads the capture. if [ ! -x "$PT/span-lite-probe" ]; then bst_skip span-lite/probe-present "span-lite-probe not installed on this image" exit 0 fi bst_ok span-lite/probe-present # The probe's size is a functional property on this target, not trivia: the # guest stages an exec image as a segment list capped at WASM32_IMG_MAX_SEGS, # so a large image is refused -ENOMEM on a fragmented heap before it runs # (nopoll measured exactly that at 41 MB). Read against this arm's floor rather # than on its own -- an empty main() costs ~2.3 MB here, glibc and libc++ # merged whole, so most of this is baseline and not span-lite. 8 MB is far # above what the port stages and far below where the cap bites. sz=$(( $(wc -c < "$PT/span-lite-probe") )) if [ "$sz" -lt 8388608 ]; then bst_ok span-lite/probe-size else bst_notok span-lite/probe-size "probe is $sz bytes, over the 8388608 ceiling"; fi out=$("$PT/span-lite-probe" 2>&1); prc=$? # Parse every P- line in ONE pass. No forks in the loop. P_CXXSTD=; P_EXC=; P_NOEXC=; P_THROWSV=; P_TERM=; P_MASK=; P_ELEXP=; P_ELENS= P_VER=; P_MAJ=; P_MIN=; P_PAT=; P_USESSTD= P_HAVEARR=; P_HAVEBYTE=; P_HAVECC=; P_HAVEIC= P_FWC=; P_FAT=; P_FBF=; P_FCO=; P_FCMP=; P_FMS=; P_FBS= P_PTRB=; P_SZTB=; P_INTB=; P_DYNB=; P_STATB=; P_DYNEXT=; P_STATEXT4= P_CASZ=; P_CAAL=; P_SASZ=; P_SACSZ=; P_SAAL=; P_VSZ=; P_VAL=; P_STRSZ=; P_STRAL= P_PCSZ=; P_PPSZ=; P_DEFSZ=; P_DEFE=; P_STSZ= P_SIZE=; P_SIZEB=; P_ISUM=; P_FRONT=; P_BACK=; P_IFWD=; P_IREV=; P_BYSZ= P_F3=; P_L3=; P_S2=; P_S13=; P_TF3=; P_TS12=; P_CHAIN=; P_DEG= P_MUT=; P_VMUT=; P_WBL= P_CHECKS=; P_FAILURES=; P_RESULT= oIFS=$IFS; IFS=' ' for line in $out; do tag=${line%% *}; val=${line#* } case "$tag" in P-CXXSTD) P_CXXSTD=$val ;; P-CXX-EXCEPTIONS) P_EXC=$val ;; P-SPAN-NO-EXCEPTIONS) P_NOEXC=$val ;; P-SPAN-CONTRACT-THROWS-V) P_THROWSV=$val ;; P-SPAN-CONTRACT-TERMINATES) P_TERM=$val ;; P-SPAN-CONTRACT-LEVEL-MASK) P_MASK=$val ;; P-SPAN-ELIDE-EXPECTS) P_ELEXP=$val ;; P-SPAN-ELIDE-ENSURES) P_ELENS=$val ;; P-SPAN-VERSION) P_VER=$val ;; P-SPAN-MAJOR) P_MAJ=$val ;; P-SPAN-MINOR) P_MIN=$val ;; P-SPAN-PATCH) P_PAT=$val ;; P-SPAN-USES-STD) P_USESSTD=$val ;; P-SPAN-HAVE-ARRAY) P_HAVEARR=$val ;; P-SPAN-HAVE-BYTE) P_HAVEBYTE=$val ;; P-SPAN-HAVE-CONSTRAINED-CTOR) P_HAVECC=$val ;; P-SPAN-HAVE-ITERATOR-CTOR) P_HAVEIC=$val ;; P-SPAN-FEATURE-WITH-CONTAINER) P_FWC=$val ;; P-SPAN-FEATURE-MEMBER-AT) P_FAT=$val ;; P-SPAN-FEATURE-MEMBER-BACK-FRONT) P_FBF=$val ;; P-SPAN-FEATURE-MEMBER-CALL-OPERATOR) P_FCO=$val ;; P-SPAN-FEATURE-COMPARISON) P_FCMP=$val ;; P-SPAN-FEATURE-MAKE-SPAN) P_FMS=$val ;; P-SPAN-FEATURE-BYTE-SPAN) P_FBS=$val ;; P-PTR-BITS) P_PTRB=$val ;; P-SIZE-T-BITS) P_SZTB=$val ;; P-INT-BYTES) P_INTB=$val ;; P-SPAN-DYN-BYTES) P_DYNB=$val ;; P-SPAN-STATIC-BYTES) P_STATB=$val ;; P-DYNAMIC-EXTENT) P_DYNEXT=$val ;; P-STATIC-EXTENT-4) P_STATEXT4=$val ;; P-CARRAY-SIZE) P_CASZ=$val ;; P-CARRAY-ALIASES) P_CAAL=$val ;; P-STDARRAY-SIZE) P_SASZ=$val ;; P-STDARRAY-CONST-SIZE) P_SACSZ=$val ;; P-STDARRAY-ALIASES) P_SAAL=$val ;; P-VECTOR-SIZE) P_VSZ=$val ;; P-VECTOR-ALIASES) P_VAL=$val ;; P-STRING-SIZE) P_STRSZ=$val ;; P-STRING-ALIASES) P_STRAL=$val ;; P-PTRCOUNT-SIZE) P_PCSZ=$val ;; P-PTRPAIR-SIZE) P_PPSZ=$val ;; P-DEFAULT-SIZE) P_DEFSZ=$val ;; P-DEFAULT-EMPTY) P_DEFE=$val ;; P-STATIC-SIZE) P_STSZ=$val ;; P-SIZE) P_SIZE=$val ;; P-SIZE-BYTES) P_SIZEB=$val ;; P-INDEX-SUM) P_ISUM=$val ;; P-FRONT) P_FRONT=$val ;; P-BACK) P_BACK=$val ;; P-ITER-FWD) P_IFWD=$val ;; P-ITER-REV) P_IREV=$val ;; P-BYTES-SIZE) P_BYSZ=$val ;; P-FIRST3) P_F3=$val ;; P-LAST3) P_L3=$val ;; P-SUBSPAN2) P_S2=$val ;; P-SUBSPAN13) P_S13=$val ;; P-TFIRST3) P_TF3=$val ;; P-TSUBSPAN12) P_TS12=$val ;; P-CHAIN) P_CHAIN=$val ;; P-DEGENERATE-OK) P_DEG=$val ;; P-MUTATE) P_MUT=$val ;; P-VECTOR-MUTATE) P_VMUT=$val ;; P-WBYTES-LOW) P_WBL=$val ;; P-CHECKS) P_CHECKS=$val ;; P-FAILURES) P_FAILURES=$val ;; P-RESULT) P_RESULT=$val ;; esac done IFS=$oIFS # The probe's own verdict, first. Its 50 internal assertions are a whole-run # gate; the named cases below are what makes a red attributable without a # rebuild, and both have to hold. check_eq span-lite/probe-rc "0" "$prc" check_eq span-lite/probe-result "OK" "$P_RESULT" check_eq span-lite/probe-failures "0" "$P_FAILURES" # A lower bound, not the exact count: a later ticket adding a case to the probe # should not redden this suite, but a probe that silently stopped emitting # should. if [ -n "$P_CHECKS" ] && [ "$P_CHECKS" -ge 50 ] 2>/dev/null; then bst_ok span-lite/probe-check-count else bst_notok span-lite/probe-check-count "probe reported '$P_CHECKS' checks, want >= 50"; fi # --------------------------------------------------------------------------- # 3. The tripwire: the compile-time state every value below was measured under. check_eq span-lite/cxxstd "201703" "$P_CXXSTD" # 0 is the DISTRO's state, not a flag this recipe passes: the C++ driver # compiles without exceptions, and the probe asserts it rather than assuming. check_eq span-lite/cxx-exceptions "0" "$P_EXC" # ...and this is span-lite's own reading of that state. The pair is the finding. check_eq span-lite/no-exceptions "1" "$P_NOEXC" check_eq span-lite/contract-throws "0" "$P_THROWSV" check_eq span-lite/contract-terminates "1" "$P_TERM" # 17 == 0x11: both Expects and Ensures live. Elision would turn section 6's # out-of-range read from a termination into undefined behaviour returning a # plausible number -- measured on x86-64 with span_CONFIG_CONTRACT_LEVEL_OFF, # which returned garbage and exited 0. check_eq span-lite/contract-level-mask "17" "$P_MASK" check_eq span-lite/elide-expects "0" "$P_ELEXP" check_eq span-lite/elide-ensures "0" "$P_ELENS" # 0 means the code under test is span-lite's OWN implementation. At C++20 the # header aliases std::span and this suite would be measuring libc++ instead of # the package this ticket ports. check_eq span-lite/uses-std-span "0" "$P_USESSTD" check_eq span-lite/probe-version "0.11.0" "$P_VER" check_eq span-lite/probe-version-parts "0.11.0" "$P_MAJ.$P_MIN.$P_PAT" # Which constructors exist. The CONSTRAINED container constructor is the one a # std::vector or std::string selects and it is on by DEFAULT from C++11; # span_FEATURE_WITH_CONTAINER is the older tagged `span(with_container, c)` # spelling and is a SEPARATE, off-by-default thing. Confusing the two is how a # port concludes containers are unsupported on this image when they are not. check_eq span-lite/have-array "1" "$P_HAVEARR" check_eq span-lite/have-byte "1" "$P_HAVEBYTE" check_eq span-lite/have-constrained-ctor "1" "$P_HAVECC" check_eq span-lite/have-iterator-ctor "1" "$P_HAVEIC" check_eq span-lite/feature-with-container "0" "$P_FWC" check_eq span-lite/feature-member-at "0" "$P_FAT" check_eq span-lite/feature-member-back-front "1" "$P_FBF" check_eq span-lite/feature-call-operator "0" "$P_FCO" check_eq span-lite/feature-comparison "0" "$P_FCMP" check_eq span-lite/feature-make-span "0" "$P_FMS" check_eq span-lite/feature-byte-span "0" "$P_FBS" # The five word-size facts. x86-64 reads 64 / 64 / 16 / 16 / 18446744073709551615 # for these; they are the only values in this suite that differ across the two # arms, and they differ by construction rather than by behaviour. check_eq span-lite/ptr-bits "32" "$P_PTRB" check_eq span-lite/size-t-bits "32" "$P_SZTB" check_eq span-lite/int-bytes "4" "$P_INTB" # span-lite stores pointer + size for BOTH extents -- it does not shrink a # static-extent span the way a std::span implementation may. That is an ABI # fact a consumer can see, so both are pinned. check_eq span-lite/sizeof-span-dynamic "8" "$P_DYNB" check_eq span-lite/sizeof-span-static "8" "$P_STATB" check_eq span-lite/dynamic-extent "4294967295" "$P_DYNEXT" check_eq span-lite/static-extent-4 "4" "$P_STATEXT4" # --------------------------------------------------------------------------- # 4. Construction. WHICH constructor an argument selects is the fact, because # every one of them produces a span that looks identical afterwards -- and # every "aliases" case below is the difference between a VIEW and a copy. check_eq span-lite/ctor-carray-size "5" "$P_CASZ" check_eq span-lite/ctor-carray-aliases "1" "$P_CAAL" check_eq span-lite/ctor-stdarray-size "6" "$P_SASZ" check_eq span-lite/ctor-stdarray-const "6" "$P_SACSZ" check_eq span-lite/ctor-stdarray-aliases "1" "$P_SAAL" check_eq span-lite/ctor-vector-size "7" "$P_VSZ" check_eq span-lite/ctor-vector-aliases "1" "$P_VAL" # 11, not 12: a span over a std::string counts characters, not the NUL. check_eq span-lite/ctor-string-size "11" "$P_STRSZ" check_eq span-lite/ctor-string-aliases "1" "$P_STRAL" check_eq span-lite/ctor-ptr-count "3" "$P_PCSZ" # 4, not 5: the pointer pair is [first, last), and an implementation that took # it inclusively would be one too long. check_eq span-lite/ctor-ptr-pair-halfopen "4" "$P_PPSZ" check_eq span-lite/ctor-default-size "0" "$P_DEFSZ" check_eq span-lite/ctor-default-empty "1" "$P_DEFE" check_eq span-lite/ctor-static-size "5" "$P_STSZ" # --------------------------------------------------------------------------- # 5. Size, access, iteration, subviews and aliasing. check_eq span-lite/size "5" "$P_SIZE" # 20 == 5 elements x 4 bytes. The PAIR is the fact: a span substituted for a # byte-oriented pointer/length pair that confuses the two reads four times too # far, and no existence check can see it. check_eq span-lite/size-bytes "20" "$P_SIZEB" check_eq span-lite/index-sum "15" "$P_ISUM" check_eq span-lite/front "1" "$P_FRONT" check_eq span-lite/back "5" "$P_BACK" check_eq span-lite/iter-forward "1,2,3,4,5" "$P_IFWD" # Reverse iteration is a separate implementation and is where an off-by-one # shows up as a value rather than a crash. check_eq span-lite/iter-reverse "5,4,3,2,1" "$P_IREV" check_eq span-lite/as-bytes-size "20" "$P_BYSZ" check_eq span-lite/first3 "1,2,3" "$P_F3" check_eq span-lite/last3 "3,4,5" "$P_L3" check_eq span-lite/subspan-offset "3,4,5" "$P_S2" check_eq span-lite/subspan-count "2,3,4" "$P_S13" # The template forms take their bounds at compile time and are a distinct code # path from the runtime ones above. check_eq span-lite/template-first3 "1,2,3" "$P_TF3" check_eq span-lite/template-subspan "2,3" "$P_TS12" check_eq span-lite/subspan-chain "3,4" "$P_CHAIN" # The legal degenerate cases -- first(0), last(0), subspan(size()), # subspan(n,0). Each is IN contract: an implementation that terminated on these # would break correct code, so they are asserted as passes and NOT left to # section 6. check_eq span-lite/degenerate-in-contract "1" "$P_DEG" # Aliasing, the property that makes a span worth substituting: if a constructor # copied its argument every case above would still pass and the substitution # would still be wrong. check_eq span-lite/mutate-through-span "7,42,7,99" "$P_MUT" check_eq span-lite/mutate-through-vector "77" "$P_VMUT" check_eq span-lite/as-writable-bytes "42" "$P_WBL" # --------------------------------------------------------------------------- # 6. The contract arms -- the headline finding, established rather than # asserted from the header text. # # Each arm is its own process because each one DIES on purpose. Three # statements per arm, and all three are needed: that the programme reached the # violation (PRE), that it did not survive it (no SURVIVED line), and that the # shell saw a non-zero status. PRE alone would pass on a binary that could not # start; a non-zero status alone would pass on one that crashed for an # unrelated reason. # # The measured status is recorded in its own case rather than folded into the # died/survived pair, so a platform change in how an aborted process is # reported is visible as ONE red naming the number, not as a suite that fails # to distinguish "terminated correctly" from "terminated differently". for arm in contract contract-subspan; do cout=$("$PT/span-lite-probe" "$arm" 2>&1); crc=$? case "$cout" in *"P-CONTRACT-PRE"*) pre=1 ;; *) pre=0 ;; esac check_eq "span-lite/$arm-reached" "1" "$pre" # THE LOAD-BEARING CASE. Measured on x86-64: compiling the same probe with # -Dspan_CONFIG_CONTRACT_LEVEL_OFF makes this line APPEAR, carrying a # garbage value, and the process then exits 0. So a suite that saw # SURVIVED would be looking at elided contracts, which is exactly the # silent-wrong-answer mode this port exists to rule out. case "$cout" in *"P-CONTRACT-SURVIVED"*) surv=1 ;; *) surv=0 ;; esac check_eq "span-lite/$arm-no-survivor" "0" "$surv" if [ "$crc" -ne 0 ]; then bst_ok "span-lite/$arm-died" else bst_notok "span-lite/$arm-died" "exited 0; span_EXPECTS did not fire"; fi check_eq "span-lite/$arm-status" "134" "$crc" done