# BST-TIMEOUT: 120 . "$BST_LIB" # boost-sml 1.1.11 (SRCREV b7df6180, upstream boost-ext/sml) -- [Boost].SML, a # COMPILE-TIME finite state machine. The transition table is written as a DSL of # `state + event [guard] / action = state` expressions, consumed entirely by # the compiler, and what is left at run time is one byte per orthogonal region. # Header-only: the port ships TWO headers, /usr/include/boost/sml.hpp and # /usr/include/boost/sml/utility/dispatch_table.hpp, plus the find_package(sml) # cmake package under /usr/lib/cmake/sml. No archive, no shared object, no CLI, # no .pc (upstream ships no pkg-config template, which 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 sml it has to go further: the library's entire # behaviour is decided at compile time, so a header that compiled but assembled # the table wrongly would give a machine that runs and takes the WRONG # transitions -- a silent wrong answer, not a crash. The recipe therefore builds # boost-sml-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-9 assert the LIBRARY. # # THE EXCEPTION ARM IS THE HEADLINE FINDING AND SECTION 8 IS WHERE IT LANDS. # A transition written on sml::exception COMPILES on this image -- no error, # no warning -- and can never fire. sml.hpp:1418-1424 auto-discovers the # compiler's exceptions state, finds none on the distro's C++ driver, and sets # BOOST_SML_DISABLE_EXCEPTIONS itself; that #if's out every process_event path # reaching the exception dispatch (sml.hpp:1631-1670). A consumer who writes # error recovery into their state machine gets a machine that silently skips it. # Measured on two arms: the same probe source built with exceptions ON reaches # "caught" and reports P-EXC-FIRED 1. The boost-sml 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-9 is byte-identical to the same probe # source compiled with clang 18 -Os -std=c++17 -fno-exceptions on x86-64 and run # there, EXCEPT P-PTR-BITS and P-SIZE-T-BITS, which are word-size facts by # construction (x86-64 reads 64 for both). Note what is NOT in that exception # list: P-SM-BYTES and P-SM-REGIONS-BYTES are the same on both arms, because an # sml machine stores `state_t current_state_[regions]` and nothing else -- no # pointer, no vtable, no table. Nothing here is floating point: a state machine # compares and assigns indices, so there is no tolerance to widen and a # divergence is a real finding. # # Style follows 76-span-lite and 76-glm: each 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. inc=/usr/include/boost hdr=/usr/include/boost/sml.hpp uhdr=/usr/include/boost/sml/utility/dispatch_table.hpp cm=/usr/lib/cmake/sml PT=/usr/libexec/boost-sml-tests # --------------------------------------------------------------------------- # 1. The payload. check boost-sml/inc-dir test -d "$inc" check boost-sml/header test -f "$hdr" check boost-sml/util-header test -f "$uhdr" # TWO headers and nothing else under boost/ -- sml is a two-file library, and a # tree that grew a third would mean the recipe installed something upstream does # not ship. It would also mean something else had landed in /usr/include/boost/, # which on this image is boost-sml's alone: real Boost is not ported to this # layer, so the namespace directory has a single owner and this case is what # would notice that changing. nh=$(find "$inc" -type f 2>/dev/null | grep -c .) check_eq boost-sml/boost-file-count "2" "$nh" # No build system in ${includedir}: a CMakeLists.txt reaching the installed tree # is what this pins. check_eq boost-sml/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 -- this macro, the cmake config version, and the probe's reading of # the macro -- and a port that got one of them wrong would still pass any # existence check. 1'1'11 uses C++14 digit separators, so the compiler sees 1111. check_eq boost-sml/header-version "1" \ "$(grep -c '^#define BOOST_SML_VERSION 1.1.11$' "$hdr" 2>/dev/null)" # sml.hpp carries NO #include directive at all -- the whole 2881-line header is # self-contained, which is what "no dependencies" means for this package and is # why nothing in DEPENDS survived the port but the C++ driver. Asserted as text # here and as behaviour in section 3 (P-SML-PULLED-STDLIB), because a header that # grew one would still compile and the cost would be invisible. check_eq boost-sml/header-no-includes "0" "$(grep -c '^#include' "$hdr" 2>/dev/null)" # The utility header includes exactly one thing: sml.hpp. check_eq boost-sml/util-header-one-include "1" "$(grep -c '^#include' "$uhdr" 2>/dev/null)" # The auto-discovery block that decides this image's exception configuration. It # is asserted as TEXT here and as BEHAVIOUR in sections 3 and 8, because a header # that silently lost it would still compile and would then emit `throw` on a # driver with no unwinder -- a compile failure for every consumer, arriving # nowhere near this package. check_eq boost-sml/header-exceptions-probe "1" \ "$(grep -c '__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)' "$hdr" 2>/dev/null)" if [ "$(grep -c 'BOOST_SML_DISABLE_EXCEPTIONS' "$hdr" 2>/dev/null)" -ge 8 ]; then bst_ok boost-sml/header-exceptions-guards else bst_notok boost-sml/header-exceptions-guards "fewer than 8 BOOST_SML_DISABLE_EXCEPTIONS sites" fi # The names a consumer writes. for n in make_transition_table process_event visit_current_states defer_queue; do case "$(grep -c "$n" "$hdr" 2>/dev/null)" in 0) bst_notok "boost-sml/header-api-$n" "not present in $hdr" ;; *) bst_ok "boost-sml/header-api-$n" ;; esac done # --------------------------------------------------------------------------- # 2. The integration surface. # # NO .pc FILE, AND THAT IS UPSTREAM'S ARRANGEMENT rather than a defect of this # port -- boost-sml 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 boost-sml/no-pkgconfig "0" \ "$(find /usr/lib/pkgconfig /usr/share/pkgconfig -name '*sml*' 2>/dev/null | grep -c .)" check boost-sml/cmake-dir test -d "$cm" check boost-sml/cmake-config test -f "$cm/smlConfig.cmake" check boost-sml/cmake-version test -f "$cm/smlConfigVersion.cmake" check boost-sml/cmake-targets test -f "$cm/smlTargets.cmake" case "$(cat "$cm/smlConfigVersion.cmake" 2>/dev/null)" in *'set(PACKAGE_VERSION "1.1.11")'*) pkv=ok ;; *) pkv=absent ;; esac check_eq boost-sml/cmake-pkg-version "ok" "$pkv" # The imported target a consumer links: upstream exports it under the sml:: # namespace as an INTERFACE library, which is the only shape a header-only # package can have. tg=$(cat "$cm/smlTargets.cmake" 2>/dev/null) case "$tg" in *'add_library(sml::sml INTERFACE IMPORTED)'*) ti=interface ;; *) ti=other ;; esac check_eq boost-sml/cmake-targets-interface "interface" "$ti" # 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 _IMPORT_PREFIX. case "$tg" in *'_IMPORT_PREFIX}/include'*) tp=relative ;; *) tp=other ;; esac check_eq boost-sml/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"*|*"/tmp-glibc"*) tl=leak ;; *) tl=clean ;; esac check_eq boost-sml/cmake-targets-no-buildpath "clean" "$tl" # The C++ standard upstream exports as its floor. Section 9 builds the probe at # exactly this standard to establish that the exported floor HOLDS on this # driver, rather than trusting the string. case "$tg" in *'cxx_std_14'*) cf=14 ;; *) cf=absent ;; esac check_eq boost-sml/cmake-compile-feature "14" "$cf" # --------------------------------------------------------------------------- # 3-8. The probe. Run ONCE for the report; everything below reads the capture. if [ ! -x "$PT/boost-sml-probe" ]; then bst_skip boost-sml/probe-present "boost-sml-probe not installed on this image" exit 0 fi bst_ok boost-sml/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). sml is a template metaprogram -- every # transition table is instantiated, not interpreted -- so this port has more # reason than most to watch it. 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 boost-sml. 8 MB is far above what the port stages # and far below where the cap bites. sz=$(( $(wc -c < "$PT/boost-sml-probe") )) if [ "$sz" -lt 8388608 ]; then bst_ok boost-sml/probe-size else bst_notok boost-sml/probe-size "probe is $sz bytes, over the 8388608 ceiling"; fi out=$("$PT/boost-sml-probe" 2>&1); prc=$? # Parse every P- line in ONE pass. No forks in the loop. P_CXXSTD=; P_EXC=; P_RTTI=; P_DISEXC=; P_VER=; P_PULL=; P_UPULL= P_PTRB=; P_SZTB=; P_INTB= P_BINIT=; P_BGOH=; P_BRUN=; P_BACT=; P_BGH=; P_BGSTAY=; P_BGFIRE=; P_BDONE=; P_BUNH= P_INDEP=; P_SMB=; P_INJ= P_RINIT=; P_RBOTH=; P_RACT=; P_RVIS=; P_RSMB= P_CSUB=; P_CTERM=; P_CAFTER= P_ENTRY=; P_EXIT=; P_UNEXP=; P_UNEXPH= P_DNOT=; P_DREP=; P_DDONE= P_ETC=; P_EFIRED=; P_ECAUGHT=; P_ERUN= P_DA=; P_DB=; P_DST= 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-CXX-RTTI) P_RTTI=$val ;; P-SML-DISABLE-EXCEPTIONS) P_DISEXC=$val ;; P-SML-VERSION) P_VER=$val ;; P-SML-PULLED-STDLIB) P_PULL=$val ;; P-SML-UTIL-PULLED-STDLIB) P_UPULL=$val ;; P-PTR-BITS) P_PTRB=$val ;; P-SIZE-T-BITS) P_SZTB=$val ;; P-INT-BYTES) P_INTB=$val ;; P-BASIC-INITIAL) P_BINIT=$val ;; P-BASIC-GO-HANDLED) P_BGOH=$val ;; P-BASIC-IN-RUN) P_BRUN=$val ;; P-BASIC-ACTION-GO) P_BACT=$val ;; P-BASIC-GUARD-HANDLED) P_BGH=$val ;; P-BASIC-GUARD-STAYS-RUN) P_BGSTAY=$val ;; P-BASIC-GUARD-FIRES) P_BGFIRE=$val ;; P-BASIC-IN-DONE) P_BDONE=$val ;; P-BASIC-UNHANDLED) P_BUNH=$val ;; P-INSTANCES-INDEPENDENT) P_INDEP=$val ;; P-SM-BYTES) P_SMB=$val ;; P-INJECT-HITS) P_INJ=$val ;; P-REGIONS-INITIAL) P_RINIT=$val ;; P-REGIONS-BOTH) P_RBOTH=$val ;; P-REGIONS-ACTIONS) P_RACT=$val ;; P-REGIONS-VISITED) P_RVIS=$val ;; P-SM-REGIONS-BYTES) P_RSMB=$val ;; P-COMPOSITE-SUB-RAN) P_CSUB=$val ;; P-COMPOSITE-SUB-TERM) P_CTERM=$val ;; P-COMPOSITE-AFTER) P_CAFTER=$val ;; P-ENTRY) P_ENTRY=$val ;; P-EXIT) P_EXIT=$val ;; P-UNEXPECTED) P_UNEXP=$val ;; P-UNEXPECTED-HANDLED) P_UNEXPH=$val ;; P-DEFER-NOT-YET) P_DNOT=$val ;; P-DEFER-REPLAYED) P_DREP=$val ;; P-DEFER-IN-DONE) P_DDONE=$val ;; P-EXC-TABLE-COMPILES) P_ETC=$val ;; P-EXC-FIRED) P_EFIRED=$val ;; P-EXC-IN-CAUGHT) P_ECAUGHT=$val ;; P-EXC-IN-RUN) P_ERUN=$val ;; P-DISPATCH-A) P_DA=$val ;; P-DISPATCH-B) P_DB=$val ;; P-DISPATCH-STATE) P_DST=$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 34 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 boost-sml/probe-rc "0" "$prc" check_eq boost-sml/probe-result "OK" "$P_RESULT" check_eq boost-sml/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 34 ] 2>/dev/null; then bst_ok boost-sml/probe-check-count else bst_notok boost-sml/probe-check-count "probe reported '$P_CHECKS' checks, want >= 34"; fi # --------------------------------------------------------------------------- # 3. The tripwire: the compile-time state every value below was measured under. check_eq boost-sml/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 boost-sml/cxx-exceptions "0" "$P_EXC" # ...and this is sml's own reading of that state. The PAIR is the finding, and # section 8 is where its consequence is measured. check_eq boost-sml/disable-exceptions "1" "$P_DISEXC" # RTTI is available on this driver and needs no flag. sml does not require it, # but a consumer's own actions may, so the state is recorded. check_eq boost-sml/cxx-rtti "1" "$P_RTTI" check_eq boost-sml/probe-version "1111" "$P_VER" # sml.hpp includes NOTHING, and the utility header includes only sml.hpp. Asserted # as behaviour: the probe includes each in turn, alone, and reports whether a # libc++ or libstdc++ version macro became visible. This is what makes the # package's "no dependencies" claim true on this image rather than merely printed # in its DESCRIPTION. check_eq boost-sml/pulls-no-stdlib "0" "$P_PULL" check_eq boost-sml/util-pulls-no-stdlib "0" "$P_UPULL" # The word-size facts. x86-64 reads 64 / 64 for the first two; 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 boost-sml/ptr-bits "32" "$P_PTRB" check_eq boost-sml/size-t-bits "32" "$P_SZTB" check_eq boost-sml/int-bytes "4" "$P_INTB" # --------------------------------------------------------------------------- # 4. The machine itself: transitions, actions, guards, and the ABI fact. check_eq boost-sml/initial-state "1" "$P_BINIT" check_eq boost-sml/event-handled "1" "$P_BGOH" check_eq boost-sml/transition-taken "1" "$P_BRUN" check_eq boost-sml/action-ran "1" "$P_BACT" # A FALSE guard must leave the machine where it was. Both halves are asserted: # process_event reports unhandled, AND the state did not move. A guard that was # ignored would look identical to a missing transition from the outside. check_eq boost-sml/guard-blocks "0" "$P_BGH" check_eq boost-sml/guard-stays "1" "$P_BGSTAY" # ...and a TRUE guard fires it. Without this pair the case above would also pass # on a build where no transition worked at all. check_eq boost-sml/guard-fires "1" "$P_BGFIRE" check_eq boost-sml/guard-moved "1" "$P_BDONE" check_eq boost-sml/unknown-event-unhandled "0" "$P_BUNH" check_eq boost-sml/instances-independent "1" "$P_INDEP" # THE ABI FACT, and it is the number that says what this library is. The whole # transition table lives in the TYPE; the runtime object is # `state_t current_state_[regions]` and nothing else -- so a single-region machine # is ONE BYTE and a two-region machine is two. sml.hpp:1454 widens state_t to # unsigned short above 255 states, which is the only thing that would move these. # Word-size independent: x86-64 reads 1 and 2 as well. Asserted beside # instances-independent above, because "sizeof == 1" on its own is equally # compatible with a machine that stores nothing at all. check_eq boost-sml/sizeof-sm-one-region "1" "$P_SMB" check_eq boost-sml/sizeof-sm-two-regions "2" "$P_RSMB" # --------------------------------------------------------------------------- # 5. Dependency injection, orthogonal regions, composites, entry/exit. # The dep must be the CALLER'S object: if sml copied it, the action would still # run and the caller's counter would still read 0. check_eq boost-sml/inject-through-ref "7" "$P_INJ" check_eq boost-sml/regions-initial "1" "$P_RINIT" check_eq boost-sml/regions-advanced "1" "$P_RBOTH" # ONE event, BOTH regions act -- that is what orthogonal means, and a machine # that dispatched to only the first region would still pass regions-initial. check_eq boost-sml/regions-actions "1,1" "$P_RACT" check_eq boost-sml/regions-visited "2" "$P_RVIS" check_eq boost-sml/composite-sub-ran "1" "$P_CSUB" # X is sml's terminate state; the sub-machine reaching it is what lets the parent # move on. check_eq boost-sml/composite-sub-term "1" "$P_CTERM" check_eq boost-sml/composite-parent "1" "$P_CAFTER" check_eq boost-sml/on-entry "1" "$P_ENTRY" check_eq boost-sml/on-exit "1" "$P_EXIT" # --------------------------------------------------------------------------- # 6. unexpected_event -- and a return value that does NOT mean what it looks like. check_eq boost-sml/unexpected-handler-ran "1" "$P_UNEXP" # MEASURED, and worth a case of its own precisely because it surprises: the # handler runs and process_event STILL reports the event as unhandled. The two # answers are about different things -- the return value is "did a normal # transition take this event", and unexpected_event is dispatched as an internal # event after that answer is already no. A consumer branching on the return value # to log unhandled events will log one whose handler just ran. Same on x86-64, so # this is upstream's semantics and not an arm difference. check_eq boost-sml/unexpected-returns-false "0" "$P_UNEXPH" # --------------------------------------------------------------------------- # 7. defer_queue -- the one place this port touches libc++. # # sml includes nothing, so a deferred-event queue needs a container from the # CONSUMER: sml::defer_queue. That makes this the only case in the # suite that exercises the port against the standard library at all, and the # one that would notice a libc++ whose did not work on this arm. # The event arrives too early and must be DEFERRED, not dropped -- 0 here and 1 # below is the whole assertion; either number alone is met by a machine that # simply loses the event. check_eq boost-sml/defer-held "0" "$P_DNOT" check_eq boost-sml/defer-replayed "1" "$P_DREP" check_eq boost-sml/defer-reached-done "1" "$P_DDONE" # --------------------------------------------------------------------------- # 8. THE HEADLINE FINDING -- established, not asserted from the header text. # # The probe's table names sml::exception on two states. # It COMPILES. The probe would not exist if it did not, so this case is really a # statement for the reader: the failure mode here is not a build error. check_eq boost-sml/exception-table-compiles "1" "$P_ETC" # THE LOAD-BEARING PAIR. Cross-arm control: the same source built with exceptions # ON reports P-EXC-FIRED 1 and P-EXC-IN-CAUGHT 1, measured with clang 18 on # x86-64. Here both are 0 -- the transition is unreachable. check_eq boost-sml/exception-never-fires "0" "$P_EFIRED" check_eq boost-sml/exception-not-caught "0" "$P_ECAUGHT" # ...and the machine went where the ORDINARY transition took it. Without this the # two zeros above would also pass on a machine that had stopped working entirely. check_eq boost-sml/exception-ordinary-transition-taken "1" "$P_ERUN" # --------------------------------------------------------------------------- # 9. dispatch_table.hpp -- the second shipped header -- and the C++14 floor. check_eq boost-sml/dispatch-id1 "1" "$P_DA" check_eq boost-sml/dispatch-id2 "1" "$P_DB" check_eq boost-sml/dispatch-state "1" "$P_DST" # The C++14 control. sml is documented as a C++14 library and its cmake package # exports cxx_std_14 (asserted in section 2), so a consumer is entitled to build # at 14 -- but the distro compiles at 17, so nothing else here would ever exercise # the floor. The two probes must agree case for case; a divergence would mean the # exported floor does not hold on this driver, which no payload check could see. if [ ! -x "$PT/boost-sml-probe-cxx14" ]; then bst_skip boost-sml/cxx14-present "boost-sml-probe-cxx14 not installed on this image" else bst_ok boost-sml/cxx14-present c14=$("$PT/boost-sml-probe-cxx14" 2>&1); c14rc=$? check_eq boost-sml/cxx14-rc "0" "$c14rc" c14std=; c14res=; c14fail=; c14disexc=; c14efired= oIFS=$IFS; IFS=' ' for line in $c14; do tag=${line%% *}; val=${line#* } case "$tag" in P-CXXSTD) c14std=$val ;; P-RESULT) c14res=$val ;; P-FAILURES) c14fail=$val ;; P-SML-DISABLE-EXCEPTIONS) c14disexc=$val ;; P-EXC-FIRED) c14efired=$val ;; esac done IFS=$oIFS check_eq boost-sml/cxx14-standard "201402" "$c14std" check_eq boost-sml/cxx14-result "OK" "$c14res" check_eq boost-sml/cxx14-failures "0" "$c14fail" # The finding holds at the floor too -- it is a property of the DRIVER's # exceptions state, not of the language standard, and this is what says so. check_eq boost-sml/cxx14-disable-exceptions "1" "$c14disexc" check_eq boost-sml/cxx14-exception-never-fires "0" "$c14efired" fi