. "$BST_LIB" # PEGTL (taocpp) is a header-only parser library: grammars are written as C++ # template expressions and compile straight into a recursive-descent parser, with # no generator step and no runtime. The port is 190 headers plus the # find_package(pegtl) cmake package -- ALL of it -dev. Nothing of PEGTL is # compiled: its cmake target is an INTERFACE library, so the main package is # empty and there is no archive, no shared object and no CLI. # # Three things therefore have to be asserted here that a file listing cannot # reach on its own, and they are what most of this suite is: # # THE PARSER MUST ACTUALLY REFUSE. This is the package whose job is to decide # whether input is in a language, which makes it the package for which "the # files are present" is furthest from "it works": a grammar that accepts # everything passes every existence check ever written and every smoke test # that only feeds it valid input. Section 4 runs the probe the recipe builds, # whose centre is ten inputs that must be REJECTED, beside twelve evaluated # to an exact value and three whose parse TREE is compared -- the trees are # what separate a correct grammar from one whose precedence is backwards, # which the values alone cannot do for every case. # THE SHIPPED CMAKE PACKAGE MUST NOT CARRY A FILESYSTEM FALLBACK. See section # 3. That is the difference between a usable port and an unusable one, and it # is invisible in the headers. # THE EXCEPTIONS SUBSET MUST BE THE ONE UPSTREAM DEFINES. See section 2. # # THE EXCEPTIONS STORY, because a consumer on this image has to know it and # nothing in the payload says it. This arm's C++ driver bakes -fno-exceptions # into every compile (brintos-toolchain-cross, BRINTOS_CXX_EH), and PEGTL 3.x # treats that as a FIRST-CLASS CONFIGURATION rather than an accident: 30 of its # headers guard on __cpp_exceptions. The consequences, in the order they bite: # # * THE must<> FAMILY DOES NOT EXIST. must, raise, if_must, if_must_else, # list_must, opt_must, star_must, try_catch and try_catch_type are all inside # `#if defined( __cpp_exceptions )` in rules.hpp. A grammar naming one does # not fail to link and does not throw at run time -- it fails to COMPILE, # with "'must' was not declared in this scope". This is stronger than "avoid # must<>": there is nothing to avoid, because there is nothing there. # * ORDINARY PARSING IS UNAFFECTED, and that is the whole reason this package # ports to an arm with no unwinder. parse() returns bool; a refusal is a # return value, not a throw. Everything in section 4 rests on this. # * FIVE OF THE 44 contrib HEADERS ARE UNUSABLE HERE. http.hpp, uri.hpp, # iri.hpp and integer.hpp each open with upstream's own # `#error "Exception support required for ..."`; proto3.hpp needs exceptions # too -- it is built on if_must/list_must/star_must -- but carries no such # guard, so it fails with a template cascade instead of a clear message. The # other 39 compile. Section 2 pins which is which, because a consumer reading # a directory listing cannot tell them apart and will not find out until a # build fails. # # Fork discipline (the lesson 81-cjson paid for): the runner's per-suite bound is # a budget in PROCESS SPAWNS, not in cases -- a check_out per case is a command # substitution plus an exec. So each file is read ONCE into a shell variable and # every text assertion is a fork-free `case` glob against it, and the probe is # run ONCE with its whole output kept. has/hasnot stay local to this suite so a # hot helper cannot change behaviour under the other suites. inc=/usr/include/tao cmdir=/usr/share/pegtl/cmake cm=$cmdir/pegtl-config.cmake cmv=$cmdir/pegtl-config-version.cmake PT=/usr/libexec/pegtl-tests # has -- $H is the haystack; no fork has() { case "$H" in *"$2"*) bst_ok "$1" ;; *) bst_notok "$1" "not found: $2" ;; esac; } # hasnot -- the assertion is that it is ABSENT hasnot() { case "$H" in *"$2"*) bst_notok "$1" "present but must not be: $2" ;; *) bst_ok "$1" ;; esac; } # --------------------------------------------------------------------------- # 1. Payload. One find, one variable, then globs. require ls pegtl/present H=$(find "$inc" -name '*.hpp' 2>/dev/null) n=$(printf '%s\n' "$H" | grep -c '\.hpp$') check_eq pegtl/header-count 190 "$n" nc=$(printf '%s\n' "$H" | grep -c '/contrib/[^/]*\.hpp$') check_eq pegtl/contrib-count 44 "$nc" ni=$(printf '%s\n' "$H" | grep -c '/pegtl/internal/[^/]*\.hpp$') check_eq pegtl/internal-count 87 "$ni" nt=$(printf '%s\n' "$H" | grep -c '/pegtl/[^/]*\.hpp$') check_eq pegtl/toplevel-count 42 "$nt" nu=$(printf '%s\n' "$H" | grep -c '/contrib/icu/[^/]*\.hpp$') check_eq pegtl/icu-count 4 "$nu" # The umbrella header, and the ones a grammar actually names. has pegtl/hdr-umbrella "$inc/pegtl.hpp" has pegtl/hdr-config "$inc/pegtl/config.hpp" has pegtl/hdr-version "$inc/pegtl/version.hpp" has pegtl/hdr-rules "$inc/pegtl/rules.hpp" has pegtl/hdr-ascii "$inc/pegtl/ascii.hpp" has pegtl/hdr-utf8 "$inc/pegtl/utf8.hpp" has pegtl/hdr-parse "$inc/pegtl/parse.hpp" has pegtl/hdr-normal "$inc/pegtl/normal.hpp" has pegtl/hdr-nothing "$inc/pegtl/nothing.hpp" has pegtl/hdr-position "$inc/pegtl/position.hpp" has pegtl/hdr-parse-err "$inc/pegtl/parse_error.hpp" has pegtl/hdr-memory-in "$inc/pegtl/memory_input.hpp" has pegtl/hdr-string-in "$inc/pegtl/string_input.hpp" has pegtl/hdr-file-in "$inc/pegtl/file_input.hpp" has pegtl/hdr-read-in "$inc/pegtl/read_input.hpp" has pegtl/hdr-buffer-in "$inc/pegtl/buffer_input.hpp" has pegtl/hdr-argv-in "$inc/pegtl/argv_input.hpp" has pegtl/hdr-demangle "$inc/pegtl/demangle.hpp" has pegtl/hdr-must-if "$inc/pegtl/must_if.hpp" has pegtl/hdr-visit "$inc/pegtl/visit.hpp" # The contrib headers the probe uses, and the five it cannot. has pegtl/hdr-analyze "$inc/pegtl/contrib/analyze.hpp" has pegtl/hdr-parse-tree "$inc/pegtl/contrib/parse_tree.hpp" has pegtl/hdr-json "$inc/pegtl/contrib/json.hpp" has pegtl/hdr-trace "$inc/pegtl/contrib/trace.hpp" has pegtl/hdr-abnf "$inc/pegtl/contrib/abnf.hpp" has pegtl/hdr-unescape "$inc/pegtl/contrib/unescape.hpp" has pegtl/hdr-raw-string "$inc/pegtl/contrib/raw_string.hpp" has pegtl/hdr-http "$inc/pegtl/contrib/http.hpp" has pegtl/hdr-uri "$inc/pegtl/contrib/uri.hpp" has pegtl/hdr-iri "$inc/pegtl/contrib/iri.hpp" has pegtl/hdr-integer "$inc/pegtl/contrib/integer.hpp" has pegtl/hdr-proto3 "$inc/pegtl/contrib/proto3.hpp" has pegtl/hdr-icu-utf8 "$inc/pegtl/contrib/icu/utf8.hpp" # Nothing but headers ships in the payload -- upstream installs include/ whole, # so a stray non-header would mean the tree changed shape under us. nall=$(find "$inc" -type f 2>/dev/null | grep -c .) check_eq pegtl/only-headers 190 "$nall" # --------------------------------------------------------------------------- # 2. The exceptions gate, as SHIPPED. These read the installed headers, not the # build tree, so they answer the question a consumer on this image asks. H=$(cat "$inc/pegtl/rules.hpp" 2>/dev/null) has pegtl/gate-present '#if defined( __cpp_exceptions )' has pegtl/gate-must 'struct must : internal::must< Rules... >' has pegtl/gate-raise 'struct raise : internal::raise< Exception >' has pegtl/gate-try-catch 'struct try_catch : internal::try_catch_type< parse_error, Rules... >' # The four rules that are NOT gated are the ones a grammar here is built from; # if seq/sor/star ever moved inside the guard the whole port would be empty. has pegtl/ungated-seq 'struct seq : internal::seq< Rules... >' has pegtl/ungated-sor 'struct sor : internal::sor< Rules... >' H=$(cat "$inc/pegtl/contrib/uri.hpp" 2>/dev/null) has pegtl/uri-guarded '#error "Exception support required for tao/pegtl/contrib/uri.hpp"' H=$(cat "$inc/pegtl/contrib/http.hpp" 2>/dev/null) has pegtl/http-guarded '#error "Exception support required for tao/pegtl/contrib/http.hpp"' H=$(cat "$inc/pegtl/contrib/iri.hpp" 2>/dev/null) has pegtl/iri-guarded '#error "Exception support required for tao/pegtl/contrib/iri.hpp"' H=$(cat "$inc/pegtl/contrib/integer.hpp" 2>/dev/null) has pegtl/integer-guarded '#error "Exception support required for tao/pegtl/contrib/integer.hpp"' # proto3.hpp needs exceptions for the same reason as the four above -- it is # built on if_must/list_must/star_must -- and is asserted by what it USES rather # than by whether it carries the guard, so this case stays true whether or not # upstream adds one. What a consumer needs to know is that it does not compile # here; the missing guard only decides how legible the failure is. H=$(cat "$inc/pegtl/contrib/proto3.hpp" 2>/dev/null) has pegtl/proto3-needs-exceptions 'if_must<' # --------------------------------------------------------------------------- # 3. The shipped cmake package -- the assertion this suite exists for as much as # the probe is. # # PEGTL's CMakeLists runs a try_compile at configure time to decide how to reach # std::filesystem, and it EXPORTS the answer into the find_package package: # either nothing (std::filesystem worked), or a # -DTAO_PEGTL_STD_EXPERIMENTAL_FILESYSTEM / -DTAO_PEGTL_BOOST_FILESYSTEM define, # or an INTERFACE link against stdc++fs / c++fs. On this arm the two fallbacks # and both of those libraries do not exist, so an exported fallback would make # PEGTL uncompilable for every consumer on this image -- while 190 perfectly # correct headers sat there looking fine. The three hasnot cases are the ones # that catch it. check pegtl/cmake-dir test -d "$cmdir" check pegtl/cmake-config test -f "$cm" check pegtl/cmake-version test -f "$cmv" H=$(cat "$cm" 2>/dev/null) has pegtl/cmake-target 'add_library(taocpp::pegtl INTERFACE IMPORTED)' has pegtl/cmake-cxx17 'INTERFACE_COMPILE_FEATURES "cxx_std_17"' has pegtl/cmake-incdir 'INTERFACE_INCLUDE_DIRECTORIES "${_IMPORT_PREFIX}/include"' hasnot pegtl/cmake-no-exp-fs 'TAO_PEGTL_STD_EXPERIMENTAL_FILESYSTEM' hasnot pegtl/cmake-no-boost-fs 'TAO_PEGTL_BOOST_FILESYSTEM' hasnot pegtl/cmake-no-fs-lib 'INTERFACE_LINK_LIBRARIES' H=$(cat "$cmv" 2>/dev/null) has pegtl/cmake-pkg-version 'set(PACKAGE_VERSION "3.2.7")' # write_basic_package_version_file(COMPATIBILITY SameMajorVersion) does not # write that word anywhere -- it emits the comparison. Assert the comparison, # which is the behaviour a consumer gets, rather than a label that is not there. has pegtl/cmake-samemajor 'if(PACKAGE_FIND_VERSION_MAJOR STREQUAL CVF_VERSION_MAJOR)' # --------------------------------------------------------------------------- # 4. The probe. Run ONCE; every assertion below is a glob on the captured output. # Each expected value was derived on x86-64 from the same source # (pegtl-probe --dump) and is compared byte-exactly. check pegtl/probe-shipped test -f "$PT/pegtl-probe" tmp=${TMPDIR:-/tmp} probe=$tmp/bst-pegtl-probe.$$ if cp "$PT/pegtl-probe" "$probe" 2>/dev/null && chmod +x "$probe" 2>/dev/null; then H=$("$probe" 2>&1); prc=$? check_eq pegtl/probe-rc 0 "$prc" # -- configuration the rest of the port rests on has pegtl/version 'P-VERSION 3.2.7' has pegtl/exceptions-off 'P-EXCEPTIONS 0' has pegtl/cxx17 'P-CXXSTD 201703' has pegtl/long-bits 'P-LONG-BITS 32' # -- the grammar is sound: analyze() finds no cycle that would loop forever has pegtl/analyze-calc 'P-ANALYZE 0' has pegtl/analyze-json 'P-ANALYZE-JSON 0' # -- twelve inputs evaluated to an exact value. Precedence, associativity, # parentheses, unary minus, integer division and remainder. has pegtl/eval-add 'P-EVAL 1+2 = 3' has pegtl/eval-prec 'P-EVAL 2+3*4 = 14' has pegtl/eval-paren 'P-EVAL (2+3)*4 = 20' has pegtl/eval-div 'P-EVAL 100/7 = 14' has pegtl/eval-mod 'P-EVAL 100%7 = 2' has pegtl/eval-two-mul 'P-EVAL 2*3+4*5 = 26' has pegtl/eval-neg 'P-EVAL -7+10 = 3' has pegtl/eval-neg-paren 'P-EVAL -(3+4) = -7' has pegtl/eval-spaces 'P-EVAL 1 + 2 * 3 - 4 = 3' has pegtl/eval-nested 'P-EVAL ((((5)))) = 5' has pegtl/eval-left-assoc 'P-EVAL 10-2-3 = 5' has pegtl/eval-big 'P-EVAL 1000000*1000 = 1000000000' # -- ten inputs that MUST be refused. full=0 is the whole-input grammar saying # no; prefix is how far a bare expr got, which is the closest this arm has # to the position a parse_error would have carried. has pegtl/reject-empty 'P-REJECT empty full=0 prefix=0' has pegtl/reject-trail-op 'P-REJECT trailing-op full=0 prefix=1' has pegtl/reject-lead-op 'P-REJECT leading-op full=0 prefix=0' has pegtl/reject-unclosed 'P-REJECT unclosed full=0 prefix=0' has pegtl/reject-unopened 'P-REJECT unopened full=0 prefix=3' has pegtl/reject-double-op 'P-REJECT double-op full=0 prefix=1' has pegtl/reject-letters 'P-REJECT letters full=0 prefix=1' has pegtl/reject-empty-par 'P-REJECT empty-parens full=0 prefix=0' has pegtl/reject-garbage 'P-REJECT trailing-garbage full=0 prefix=3' has pegtl/reject-bare-paren 'P-REJECT bare-paren full=0 prefix=0' # -- the parse tree. These two inputs have IDENTICAL tokens and different # meaning, so the pair is what proves the grammar groups rather than merely # accepts: '*' binds tighter in the first, the parens win in the second. has pegtl/tree-prec 'P-TREE 1+2*3 = (expr (number:1) (add_op:+) (term (number:2) (mul_op:*) (number:3)))' has pegtl/tree-group 'P-TREE (1+2)*3 = (term (expr (number:1) (add_op:+) (number:2)) (mul_op:*) (number:3))' has pegtl/tree-neg 'P-TREE -4 = (neg (number:4))' has pegtl/tree-n1 'P-TREE-NODES 1+2*3 = 7' has pegtl/tree-n2 'P-TREE-NODES (1+2)*3 = 7' has pegtl/tree-n3 'P-TREE-NODES -4 = 2' # -- upstream's OWN json grammar, run under exceptions-off. It was written for # an arm with exceptions and is one of the 39 contrib headers that still # work without them, which is the strongest evidence available that the # subset is a usable library rather than one that merely compiles. has pegtl/json-object 'P-JSON object = 1' has pegtl/json-scalar 'P-JSON scalar = 1' has pegtl/json-escape 'P-JSON string-escape = 1' has pegtl/json-no-trail 'P-JSON trailing-comma = 0' has pegtl/json-no-unq 'P-JSON unquoted-key = 0' has pegtl/json-no-unterm 'P-JSON unterminated = 0' # -- the probe's own tally, and the sentinel that says it ran to the end. A # probe killed halfway prints a prefix of the lines above and would # otherwise contribute only silence. has pegtl/probe-failures 'P-FAILURES 0' has pegtl/probe-complete 'P-END' else bst_notok pegtl/probe-stage "cannot stage $PT/pegtl-probe" fi rm -f "$probe"