# BST-TIMEOUT: 600 # one 14 MB probe instantiation + one pass over an 8.6 MB archive . "$BST_LIB" # re2 (meta-oe recipes-support/re2) -- Google's linear-time regular-expression # engine. Unlike a backtracking matcher it compiles a pattern to an automaton, # so it answers in time linear in the subject and REFUSES the constructs that # would need backtracking. Both halves are asserted below: the refusals are as # much the deliverable as the matches. # # --------------------------------------------------------------------------- # TWO CONSUMERS, BECAUSE THEY PROVE DIFFERENT THINGS # # The PAYLOAD blocks assert what a downstream recipe reaches -- the four # headers, the static archive and its translation units, the pkg-config file, # the CMake package -- plus the Itanium-mangled symbols and the # vtable/typeinfo/typename records that a C compile cannot produce. Those are # the on-target evidence that the C++ arm of the toolchain genuinely worked. # # data/re2-probe is a CONSUMER of that payload: a program compiled against # these headers and linked against this libre2.a and the abseil archives # beside it, built by the recipe itself so it cannot drift from what shipped. # It is what fails if the port ships an archive that answers WRONGLY rather # than one that is merely absent -- which is the whole failure mode a # file-presence check cannot see for a regexp engine. # # WHAT IS COMPARED AND WHAT IS NOT. Everything the probe prints is a function # of the library and the regexp specification alone -- match verdicts, capture # values, error codes, rewritten strings -- with no pointer, size, locale or # clock in it. So the whole stream is compared line for line against # data/re2-native.txt, the identical source built and run on x86-64, with no # tolerance band: one differing line is a defect. # # Its STDERR is deliberately not compared. RE2 logs a refused pattern through # absl's logger, and each line carries the absolute path of re2.cc in the build # tree that produced the binary -- a string that differs between the two arms by # construction and says nothing about either. # # THE ANCHORED CASES EXIST BECAUSE AN ORACLE CAN BE WRONG. A control captured # from an already-broken build would make every compared line agree with it. So # the values the RE2 documentation and the regexp specification fix -- the three # refusal codes, the UTF-8-vs-Latin-1 discrimination, the capture values, the # rewrite outputs -- are ALSO asserted against literals here. Those hold # whatever the control says. # --------------------------------------------------------------------------- # A root prefix, empty on the target and set only when this suite is validated # natively before it ships. The runner never sets it. R="${BST_RE2_ROOT:-}" ARCHIVE=$R/usr/lib/libre2.a INCDIR=$R/usr/include/re2 PC=$R/usr/lib/pkgconfig/re2.pc CMDIR=$R/usr/lib/cmake/re2 PROBE=$BST_DATA/re2-probe NATIVE=$BST_DATA/re2-native.txt if [ ! -f "$ARCHIVE" ]; then bst_skip re2/present "re2 not installed on this image" exit 0 fi bst_ok re2/present nl=' ' tmp=${TMPDIR:-/tmp} # has # A shell `case` rather than a grep: this suite has ~90 content assertions and # an exec apiece is what killed the first draft of the abseil suite on target, # where every process is a fresh instantiation of a multi-megabyte module. has() { case "$2" in *"$nl$3$nl"*) bst_ok "$1" ;; *) bst_notok "$1" "missing: $3" ;; esac } # hasnt -- substring, for "must NOT be there" hasnt() { case "$2" in *"$3"*) bst_notok "$1" "present but must not be: $3" ;; *) bst_ok "$1" ;; esac } # sub sub() { case "$2" in *"$3"*) bst_ok "$1" ;; *) bst_notok "$1" "missing: $3" ;; esac } # --- the package is whole ---------------------------------------------------- check re2/hdr-re2 test -f "$INCDIR/re2.h" check re2/hdr-set test -f "$INCDIR/set.h" check re2/hdr-filtered test -f "$INCDIR/filtered_re2.h" check re2/hdr-stringpiece test -f "$INCDIR/stringpiece.h" check re2/staticlib test -f "$ARCHIVE" # Shape, not absence: this arm converts to shared libraries per recipe # (BRINTOS-1740), so a .so here is a conversion, not a policy breach. bst_lib_shape re2/no-shared "$R/usr/lib/libre2.so*" if [ -n "$(find "$R/usr/lib" -maxdepth 1 -name 'libre2.la' 2>/dev/null)" ]; then bst_notok re2/no-la "a libtool .la is installed" else bst_ok re2/no-la fi # --- the declared surface, read out of the shipped headers ------------------- # # One `cat` per header and `case` matching over the result: the point is that # the header a downstream recipe would include really declares what it is # supposed to, not merely that a file of that name exists. H=$nl$(cat "$INCDIR/re2.h" 2>/dev/null)$nl sub re2/decl-class "$H" "class RE2 {" sub re2/decl-fullmatch "$H" "static bool FullMatchN(" sub re2/decl-partialmatch "$H" "static bool PartialMatchN(" sub re2/decl-consume "$H" "static bool ConsumeN(" sub re2/decl-replace "$H" "static bool Replace(" sub re2/decl-globalreplace "$H" "static int GlobalReplace(" sub re2/decl-extract "$H" "static bool Extract(" sub re2/decl-quotemeta "$H" "static std::string QuoteMeta(" sub re2/decl-options "$H" "class Options {" sub re2/decl-errorcode "$H" "enum ErrorCode {" # Forward-declared inside class RE2, and defined out of line further down -- # so the needle is the declaration RE2's own match entry points take, not a # class body. (The first draft asked for "class Arg {" and went red against the # shipped header, which is what the pre-ship dry-run is for.) sub re2/decl-arg "$H" "class Arg;" sub re2/decl-possiblerange "$H" "bool PossibleMatchRange(" sub re2/decl-groupnames "$H" "CapturingGroupNames()" # absl::string_view in the signatures, not re2::StringPiece: this release routes # every entry point through abseil's type, which at ABSL_OPTION_USE_STD_STRING_VIEW=1 # IS std::string_view. It is why the bbappend states -DCMAKE_CXX_STANDARD=17 -- # a consumer at a lower standard would read a different type out of this header. sub re2/decl-absl-sv "$H" "absl::string_view" # The three refusal codes the probe exercises, named in the header's own enum. sub re2/decl-errbadescape "$H" "ErrorBadEscape" sub re2/decl-errperlop "$H" "ErrorBadPerlOp" sub re2/decl-errmissparen "$H" "ErrorMissingParen" sub re2/decl-errbadutf8 "$H" "ErrorBadUTF8" S=$nl$(cat "$INCDIR/set.h" 2>/dev/null)$nl sub re2/set-class "$S" "class RE2::Set {" sub re2/set-match "$S" "bool Match(" F=$nl$(cat "$INCDIR/filtered_re2.h" 2>/dev/null)$nl sub re2/filtered-class "$F" "class FilteredRE2 {" sub re2/filtered-add "$F" "RE2::ErrorCode Add(" # stringpiece.h is a compatibility shim in this release -- it must alias # absl::string_view rather than declare a class of its own, which is the # on-target evidence that the ABI a consumer compiles against is the std one. SP=$nl$(cat "$INCDIR/stringpiece.h" 2>/dev/null)$nl sub re2/sp-alias "$SP" "using StringPiece = absl::string_view;" # --- pkg-config: how a downstream recipe finds this package at all ----------- check re2/pkgconfig test -f "$PC" if [ -f "$PC" ]; then PCC=$nl$(cat "$PC")$nl has re2/pc-name "$PCC" "Name: re2" has re2/pc-version "$PCC" "Version: 11.0.0" has re2/pc-libs "$PCC" "Libs: -pthread -L\${libdir} -lre2" has re2/pc-cflags "$PCC" "Cflags: -pthread -I\${includedir}" has re2/pc-libdir "$PCC" "libdir=/usr/lib" has re2/pc-incdir "$PCC" "includedir=/usr/include" # The Requires line is the reason abseil-cpp had to land first: without those # twelve packages a consumer's `pkg-config --libs re2` resolves to nothing and # the link fails on a screenful of absl:: references. sub re2/pc-req-base "$PCC" "absl_base" sub re2/pc-req-strings "$PCC" "absl_strings" sub re2/pc-req-sync "$PCC" "absl_synchronization" sub re2/pc-req-hashmap "$PCC" "absl_flat_hash_map" sub re2/pc-req-strfmt "$PCC" "absl_str_format" sub re2/pc-req-span "$PCC" "absl_span" fi # --- the CMake package, which is what re2's consumers actually use ---------- check re2/cm-config test -f "$CMDIR/re2Config.cmake" check re2/cm-version test -f "$CMDIR/re2ConfigVersion.cmake" check re2/cm-targets test -f "$CMDIR/re2Targets.cmake" check re2/cm-noconfig test -f "$CMDIR/re2Targets-noconfig.cmake" if [ -f "$CMDIR/re2ConfigVersion.cmake" ]; then CMV=$nl$(cat "$CMDIR/re2ConfigVersion.cmake")$nl sub re2/cm-version-11 "$CMV" "set(PACKAGE_VERSION \"11.0.0\")" fi if [ -f "$CMDIR/re2Targets.cmake" ]; then CMT=$nl$(cat "$CMDIR/re2Targets.cmake")$nl sub re2/cm-target "$CMT" "re2::re2" # find_package(absl) is propagated, not merely documented: a consumer that # got re2::re2 without it would link and then fail on absl symbols. sub re2/cm-absl-dep "$CMT" "absl::" fi if [ -f "$CMDIR/re2Targets-noconfig.cmake" ]; then CMN=$nl$(cat "$CMDIR/re2Targets-noconfig.cmake")$nl # The import record must name the ARCHIVE. A .so here would mean the CMake # package describes an artifact this image does not carry. sub re2/cm-imports-a "$CMN" "libre2.a" hasnt re2/cm-no-so "$CMN" "libre2.so" fi # --- the archive itself ------------------------------------------------------ case "$(head -c 8 "$ARCHIVE" 2>/dev/null)" in '!'*) bst_ok re2/ar-magic ;; *) bst_notok re2/ar-magic "libre2.a does not start with the ar magic" ;; esac # One pass over the whole archive, mapping every non-printable byte to a # newline and pulling out only the lines that carry a needle. That is 3 execs # for the ~40 assertions below. # # Mapped to NEWLINE rather than to a space, and matched WITHOUT `grep -o`: # busybox grep reports only the first match per line, so flattening an archive # to one long line and asking for -o loses every needle after the first and # reddens cases for symbols that are present. Measured on the abseil suite: # 34 matches under GNU grep against 19 under busybox, for the same bytes. nf=$tmp/bst-re2-needles.$$ hf=$tmp/bst-re2-hits.$$ cat > "$nf" <<'NEEDLES' bitmap256.cc.o compile.cc.o dfa.cc.o nfa.cc.o onepass.cc.o parse.cc.o prog.cc.o re2.cc.o regexp.cc.o set.cc.o filtered_re2.cc.o prefilter.cc.o prefilter_tree.cc.o simplify.cc.o tostring.cc.o unicode_casefold.cc.o unicode_groups.cc.o perl_groups.cc.o mimics_pcre.cc.o bitstate.cc.o rune.cc.o strutil.cc.o _ZN3re23RE24InitENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEERKNS0_7OptionsE _ZN3re23RE210FullMatchNENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEERKS0_PKPKNS0_3ArgEi _ZN3re23RE213PartialMatchNENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEERKS0_PKPKNS0_3ArgEi _ZN3re23RE213GlobalReplaceEPNSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEERKS0_NS1_17basic_string_viewIcS4_EE _ZN3re23RE29QuoteMetaENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE _ZNK3re23RE218PossibleMatchRangeEPNSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEES8_i _ZTVN3re214ToStringWalkerE _ZTIN3re214ToStringWalkerE _ZTSN3re214ToStringWalkerE _ZTVN3re214SimplifyWalkerE _ZTIN3re214SimplifyWalkerE _ZTSN3re214SimplifyWalkerE _ZTVN3re210PCREWalkerE _ZTIN3re210PCREWalkerE _ZTSN3re210PCREWalkerE _ZTIN3re218CaptureNamesWalkerE _ZTIN3re217NumCapturesWalkerE lts_20240116 NEEDLES cat "$ARCHIVE" 2>/dev/null | tr -c '\40-\176\n' '\n' | grep -F -f "$nf" > "$hf" 2>/dev/null HITS=$nl$(cat "$hf" 2>/dev/null)$nl rm -f "$nf" "$hf" # The 22 translation units this port compiled. Named one by one rather than # counted, so a member silently dropped by a future recipe change points at # itself instead of at a number. for m in bitmap256 bitstate compile dfa filtered_re2 mimics_pcre nfa onepass \ parse perl_groups prefilter prefilter_tree prog re2 regexp set \ simplify tostring unicode_casefold unicode_groups rune strutil; do sub "re2/member-$m" "$HITS" "$m.cc.o" done # Itanium-mangled entry points. `NSt3__1` in each is libc++'s inline namespace: # these are C++ symbols against THIS arm's standard library, which is the thing # a C compile of the same sources could not produce. sub re2/sym-init "$HITS" "_ZN3re23RE24InitENSt3__117basic_string_view" sub re2/sym-fullmatch "$HITS" "_ZN3re23RE210FullMatchN" sub re2/sym-partialmatch "$HITS" "_ZN3re23RE213PartialMatchN" sub re2/sym-globalreplace "$HITS" "_ZN3re23RE213GlobalReplaceE" sub re2/sym-quotemeta "$HITS" "_ZN3re23RE29QuoteMetaE" sub re2/sym-possiblerange "$HITS" "_ZNK3re23RE218PossibleMatchRangeE" # vtable / typeinfo / typename triples. A _ZTV/_ZTI/_ZTS record cannot be # emitted by a C compile at all, so these are the on-target evidence that the # C++ arm of the toolchain worked rather than that some bytes shipped. for t in ToStringWalker SimplifyWalker PCREWalker; do case "$t" in ToStringWalker) mang="N3re214${t}E" ;; SimplifyWalker) mang="N3re214${t}E" ;; PCREWalker) mang="N3re210${t}E" ;; esac sub "re2/vtable-$t" "$HITS" "_ZTV$mang" sub "re2/typeinfo-$t" "$HITS" "_ZTI$mang" sub "re2/typename-$t" "$HITS" "_ZTS$mang" done sub re2/typeinfo-CaptureNamesWalker "$HITS" "_ZTIN3re218CaptureNamesWalkerE" sub re2/typeinfo-NumCapturesWalker "$HITS" "_ZTIN3re217NumCapturesWalkerE" # abseil's LTS inline namespace, spelled into re2's own symbols. This is the # link-level proof that re2 was built against the abseil THIS image ships # (BRINTOS-1098, lts_2024_01_16) and not against some other copy. sub re2/absl-inline-ns "$HITS" "lts_20240116" # --- the probe: does the shipped archive ANSWER, and answer correctly? ------- if [ ! -f "$PROBE" ]; then bst_notok re2/probe-present "$PROBE missing -- the library cannot be proven to run" elif [ ! -f "$NATIVE" ]; then bst_notok re2/native-present "$NATIVE missing -- no x86-64 control for the probe run" else bst_ok re2/probe-present bst_ok re2/native-present # do_install stages data/* at 0644 and the deployed rootfs is read-only at # browser runtime, so the payload cannot be executed where it lies. Stage an # executable copy under TMPDIR, as every probe-driven suite here does. # Asserting `test -x` on the shipped payload instead would pass against a # hand-placed copy and fail against every image that ever built the package. p=$tmp/bst-re2-probe.$$ if cp "$PROBE" "$p" 2>/dev/null && chmod 0755 "$p" 2>/dev/null && [ -x "$p" ]; then bst_ok re2/probe-exec out=$("$p" 2>/dev/null); rc=$? rm -f "$p" if [ "$rc" -eq 0 ]; then bst_ok re2/probe-rc else bst_notok re2/probe-rc "probe exited $rc"; fi O=$nl$out$nl # The whole-stream gate. Kept separate from the anchored cases below, # because a probe that died before printing anything would leave those with # nothing to disagree with and could look like a shorter clean run. case "$O" in *"${nl}R2-PROBE-OK${nl}") bst_ok re2/probe-complete ;; *) bst_notok re2/probe-complete "probe did not reach its final line" ;; esac ref=$(cat "$NATIVE" 2>/dev/null) if [ "$out" = "$ref" ]; then bst_ok re2/probe-matches-x86-64 else first=$(printf '%s\n' "$out" | diff - "$NATIVE" 2>/dev/null | head -4) bst_notok re2/probe-matches-x86-64 "$(_bst_1line "${first:-output differs from the control}")" fi # --- anchored cases: literals the specification fixes, not the control --- # Matching, and the negative side of each -- a matcher that said yes to # everything would pass the positive cases alone. has re2/full-yes "$O" "R2-full-yes 1" has re2/full-no "$O" "R2-full-no 0" has re2/partial-yes "$O" "R2-partial-yes 1" has re2/partial-no "$O" "R2-partial-no 0" has re2/anchor-both "$O" "R2-anchor-both 1" # Capture groups: the count, the match, and each extracted value. `cap-m 3` # and `cap-d 1` are the point of taking them as ints -- "03" and "01" in the # subject, parsed, not sliced. has re2/cap-count "$O" "R2-cap-count 3" has re2/cap-match "$O" "R2-cap-match 1" has re2/cap-y "$O" "R2-cap-y 2024" has re2/cap-m "$O" "R2-cap-m 3" has re2/cap-d "$O" "R2-cap-d 1" has re2/cap-str-match "$O" "R2-cap-str-match 1" has re2/cap-str "$O" "R2-cap-str widget42" has re2/named-count "$O" "R2-named-count 2" has re2/named-map "$O" "R2-named-map 1:key,2:val" # UTF-8. The pair is the assertion: `.` matches one RUNE under the default # encoding and one BYTE under Latin-1, so the same six-byte subject matches # a five-dot pattern in one mode and not the other. A build with the Unicode # tables missing or the option ignored fails exactly one of these. has re2/utf8-dot "$O" "R2-utf8-dot 1" has re2/latin1-ok "$O" "R2-latin1-ok 1" has re2/latin1-dot "$O" "R2-latin1-dot 0" has re2/utf8-script "$O" "R2-utf8-script 1" has re2/utf8-bytes-a "$O" "R2-utf8-bytes-a 12" has re2/utf8-bytes-b "$O" "R2-utf8-bytes-b 6" has re2/utf8-bad-ok "$O" "R2-utf8-bad-ok 0" has re2/utf8-bad-code "$O" "R2-utf8-bad-code 13" # THE REFUSALS. RE2 is an automaton, so a backreference is not something it # merely does badly -- it cannot support it, and must say so through # ok()/error() rather than by silently matching something else or by # throwing (this arm compiles -fno-exceptions). Three DISTINCT error codes, # so the refusal is a real classification and not one catch-all reject. has re2/backref-ok "$O" "R2-backref-ok 0" has re2/backref-code "$O" "R2-backref-code 2" has re2/backref-error "$O" "R2-backref-error invalid escape sequence: \\1" has re2/backref-arg "$O" "R2-backref-arg \\1" has re2/backref-match "$O" "R2-backref-match 0" has re2/lookahead-ok "$O" "R2-lookahead-ok 0" has re2/lookahead-code "$O" "R2-lookahead-code 12" has re2/lookahead-error "$O" "R2-lookahead-error invalid perl operator: (?=" has re2/unclosed-ok "$O" "R2-unclosed-ok 0" has re2/unclosed-code "$O" "R2-unclosed-code 6" # Linear time, which is the whole reason to prefer re2 over PCRE for # untrusted patterns. (a+)+$ against 40 a's and a trailing '!' is the # classic catastrophic-backtracking input: a backtracking engine explores # 2^40 paths and never returns. That this case has a value at all -- rather # than the suite tripping its own bound -- is the assertion. has re2/linear-time "$O" "R2-linear-time 0" # Rewriting. has re2/replace-n "$O" "R2-replace-n 1" has re2/replace-out "$O" "R2-replace-out the slow brown fox" has re2/gsub-n "$O" "R2-gsub-n 3" has re2/gsub-out "$O" "R2-gsub-out a-b-c-" has re2/extract-ok "$O" "R2-extract-ok 1" has re2/extract-out "$O" "R2-extract-out 01/03/2024" has re2/quotemeta "$O" "R2-quotemeta 1\\+1\\=2\\?" # The compiled object's own reports, including the prefix-range analysis a # database index would use -- that one reaches the compiled program rather # than the parse tree. has re2/ok "$O" "R2-ok 1" has re2/pattern "$O" "R2-pattern (\\w+)@(\\w+)\\.com" has re2/groups "$O" "R2-groups 2" has re2/prog-size "$O" "R2-prog-size-positive 1" has re2/range-ok "$O" "R2-range-ok 1" has re2/range-lo "$O" "R2-range-lo abc0" else rm -f "$p" bst_notok re2/probe-exec "cannot stage an executable copy of $PROBE" fi fi