#!/bin/sh # run-ptest — run mcpp's own cpp-test conformance corpus on target and report it # in the ptest PASS:/FAIL:/SKIP: protocol. # # The corpus is upstream's Validation Suite in the form upstream itself made # machine-scorable: cpp-test/ is test-t and test-l "created by copying necessary # files in each directory of test-t and test-l and adding the configuration file # cpp-test.exp" (doc/cpp-test.html §3.2.3.2), with each case's expected result # written into it as DejaGnu { dg-... } directives. Upstream scores them with # runtest driving gcc; this image has neither, so mcpp-dg.tcl re-implements # dg-test's scoring (see the header there) and this script is the part that # actually runs the preprocessor. # # Three phases, and the split is deliberate: tclsh is a large interpreter on this # arm and a late spawn of one has been seen to ENOMEM, so it never forks here. # It plans, the shell runs mcpp, it scores. # # Env, so the identical script produces the native oracle on a build host: # MCPP preprocessor under test (default /usr/bin/mcpp) # PTESTDIR where the payload lives (default the script's own dir) # SYSINC system include dir for <...> (default $PTESTDIR/sysinc; empty # disables -I- and lets mcpp's compiled-in path answer instead) # TCLSH tcl interpreter (default tclsh) # WORK scratch (default $TMPDIR/mcpp-ptest.$$) MCPP=${MCPP:-/usr/bin/mcpp} PTESTDIR=${PTESTDIR:-$(dirname "$0")} SYSINC=${SYSINC-$PTESTDIR/sysinc} TCLSH=${TCLSH:-tclsh} WORK=${WORK:-${TMPDIR:-/tmp}/mcpp-ptest.$$} CORPUS=$PTESTDIR/cpp-test RUNNER=$PTESTDIR/mcpp-dg.tcl fatal() { echo "ERROR: $*"; exit 1; } [ -x "$MCPP" ] || fatal "no mcpp at $MCPP" [ -f "$RUNNER" ] || fatal "no runner at $RUNNER" [ -d "$CORPUS" ] || fatal "no corpus at $CORPUS" command -v "$TCLSH" >/dev/null 2>&1 || fatal "no $TCLSH: the scorer evaluates upstream's dg-final Tcl fragments" # One writable scratch dir for the whole run. mcpp writes each test's # preprocessed output there and dg-final greps it back by bare basename, which # is why the scorer runs with that dir as its cwd -- dg-test does the same. mkdir -p "$WORK" 2>/dev/null || fatal "cannot create $WORK" [ -w "$WORK" ] || fatal "$WORK is not writable" # -I- drops mcpp's compiled-in /usr/local/include:/usr/include, so the six cases # that #include a system header see the closure staged beside the corpus and # nothing the image happens to carry. Without it the run's result would depend on # which -dev packages the image was built with, which is not what is under test. if [ -n "$SYSINC" ] && [ -d "$SYSINC" ]; then HERM="-I- -I$SYSINC" else HERM= echo "INFO: mcpp-ptest no-sysinc — system headers come from mcpp's compiled-in path" fi total_pass=0 total_fail=0 total_skip=0 total_files=0 for sub in test-t test-l; do [ -d "$CORPUS/$sub" ] || { echo "FAIL: mcpp/corpus-$sub"; total_fail=$((total_fail + 1)); continue; } w=$WORK/$sub rm -rf "$w"; mkdir -p "$w" "$TCLSH" "$RUNNER" plan "$CORPUS" "$sub" "$w" || fatal "plan failed for $sub" [ -s "$w/plan.txt" ] || fatal "empty plan for $sub" while IFS=' ' read -r name base kind args; do [ -n "$name" ] || continue [ "$kind" = preprocess ] || continue # shellcheck disable=SC2086 "$MCPP" $args $HERM -I"$CORPUS/$sub" "$CORPUS/$sub/$(basename "$name")" \ -o "$w/$base.i" > "$w/$base.out" 2> "$w/$base.err" || : done < "$w/plan.txt" "$TCLSH" "$RUNNER" score "$CORPUS" "$sub" "$w" > "$w/score.txt" || fatal "score failed for $sub" while read -r line; do case "$line" in "INFO: mcpp-dg "*) echo "$line" for kv in $line; do case "$kv" in files=*) total_files=$((total_files + ${kv#files=})) ;; pass=*) total_pass=$((total_pass + ${kv#pass=})) ;; fail=*) total_fail=$((total_fail + ${kv#fail=})) ;; skip=*) total_skip=$((total_skip + ${kv#skip=})) ;; esac done ;; *) echo "$line" ;; esac done < "$w/score.txt" done # The tally the suite asserts against its own pinned totals. A run that died a # third of the way through reports a small total here rather than a clean green # for the third that ran. echo "INFO: mcpp-ptest files=$total_files pass=$total_pass fail=$total_fail skip=$total_skip total=$((total_pass + total_fail + total_skip))" rm -rf "$WORK"