boot2

Playing with the boostrap
git clone https://git.ryansepassi.com/git/boot2.git
Log | Files | Refs | README

run-suite.sh (27989B)


      1 #!/bin/sh
      2 ## tests/run-suite.sh — in-container suite runner.
      3 ##
      4 ## One invocation handles every requested fixture in a suite for $ARCH,
      5 ## instead of the host re-entering the container per fixture. The host
      6 ## runner (tests/run.sh) starts one podman process per arch and lets
      7 ## this script do all the build / execute / diff work.
      8 ##
      9 ## PASS/FAIL lines stream to stdout in the same format the host expects;
     10 ## the host greps them to update its totals, then prints the final
     11 ## summary itself. Fixture-name discovery happens here when no names
     12 ## are passed (so the host can stay agnostic about each suite's layout),
     13 ## except for m1pp: tools/lint.sh runs python on the host, so the
     14 ## host preflights lint and passes the explicit kept list down.
     15 ##
     16 ## Env: ARCH=aarch64|amd64|riscv64
     17 ## Usage: tests/run-suite.sh --suite=<m1pp|p1|scheme1|cc-util|cc-lex|cc-pp|cc-cg|cc|cc-libc|cc-ext|tcc-cc|tcc-libc> [name ...]
     18 
     19 set -eu
     20 
     21 : "${ARCH:?ARCH must be set}"
     22 
     23 . "$(dirname "$0")/lib-runner.sh"
     24 
     25 SUITE=
     26 NAMES=
     27 
     28 while [ "$#" -gt 0 ]; do
     29     case "$1" in
     30         --suite)   shift; SUITE=$1 ;;
     31         --suite=*) SUITE=${1#--suite=} ;;
     32         --) shift; while [ "$#" -gt 0 ]; do NAMES="$NAMES $1"; shift; done; break ;;
     33         -*) echo "$0: unknown flag '$1'" >&2; exit 2 ;;
     34         *) NAMES="$NAMES $1" ;;
     35     esac
     36     shift
     37 done
     38 
     39 case "$SUITE" in
     40     m1pp|p1|scheme1|cc-util|cc-lex|cc-pp|cc-cg|cc|cc-libc|cc-ext|tcc-cc|tcc-libc) ;;
     41     "") echo "$0: --suite required" >&2; exit 2 ;;
     42     *) echo "$0: unknown suite '$SUITE'" >&2; exit 2 ;;
     43 esac
     44 
     45 CC_EXTRA_FLAGS=
     46 [ "${CC_TRACE_EMIT:-0}" = "1" ] && CC_EXTRA_FLAGS="$CC_EXTRA_FLAGS --cc-trace-emit"
     47 [ "${CC_DEBUG:-0}" = "1" ]      && CC_EXTRA_FLAGS="$CC_EXTRA_FLAGS --cc-debug"
     48 
     49 ## --- m1pp suite ---------------------------------------------------------
     50 ##
     51 ## Single check: run M1pp against tests/M1pp/<name>.M1pp, diff its text
     52 ## output against tests/M1pp/<name>.expected. The suite tests macro
     53 ## expansion only — assembling the result through hex2pp is the job of
     54 ## the p1 / cc-* suites, where the input is a complete program.
     55 run_m1pp_suite() {
     56     if [ -z "$NAMES" ]; then
     57         NAMES=$(discover tests/M1pp M1pp)
     58     fi
     59     for name in $NAMES; do
     60         expected=tests/M1pp/$name.expected
     61         m1pp_src=tests/M1pp/$name.M1pp
     62 
     63         if [ ! -e "$m1pp_src" ]; then
     64             echo "  SKIP $name (no .M1pp)"
     65             continue
     66         fi
     67         if [ ! -e "$expected" ]; then
     68             echo "  SKIP $name (no .expected)"
     69             continue
     70         fi
     71         expected_content=$(cat "$expected")
     72 
     73         label="[$ARCH] $name"
     74         outfile=build/$ARCH/tests/M1pp/$name.hex2pp
     75         mkdir -p "$(dirname "$outfile")"
     76         rm -f "$outfile"
     77         "./build/$ARCH/podman/boot1/M1pp" "$m1pp_src" "$outfile" >/dev/null 2>&1 || true
     78         if [ -e "$outfile" ]; then
     79             actual=$(cat "$outfile")
     80         else
     81             actual=
     82         fi
     83 
     84         if [ "$actual" != "$expected_content" ]; then
     85             report "$label" FAIL
     86             show_diff "$expected_content" "$actual"
     87             continue
     88         fi
     89 
     90         report "$label" PASS
     91     done
     92 }
     93 
     94 ## --- p1 suite -----------------------------------------------------------
     95 
     96 run_p1_suite() {
     97     if [ -z "$NAMES" ]; then
     98         NAMES=$(discover tests/P1 P1pp)
     99     fi
    100     for name in $NAMES; do
    101         pp_src=tests/P1/$name.P1pp
    102         expected=tests/P1/$name.expected
    103         if [ ! -e "$expected" ]; then echo "  SKIP $name (no .expected)"; continue; fi
    104         if [ ! -e "$pp_src" ]; then echo "  SKIP $name (no .P1pp)"; continue; fi
    105         expected_content=$(cat "$expected")
    106 
    107         label="[$ARCH] $name"
    108         bin=build/$ARCH/tests/P1/$name
    109         log=build/$ARCH/.work/tests/P1/$name/build.log
    110         mkdir -p "$(dirname "$bin")" "$(dirname "$log")"
    111         if ! sh tests/build-p1pp.sh "$bin" "$pp_src" \
    112                 >"$log" 2>&1; then
    113             fail "$label" "" "$log"
    114             continue
    115         fi
    116         actual=$("./$bin" 2>&1 || true)
    117         if [ "$actual" = "$expected_content" ]; then
    118             report "$label" PASS
    119         else
    120             report "$label" FAIL
    121             show_diff "$expected_content" "$actual"
    122         fi
    123     done
    124 }
    125 
    126 ## --- scheme1 suite ------------------------------------------------------
    127 
    128 run_scheme1_suite() {
    129     if [ -z "$NAMES" ]; then
    130         NAMES=$(discover tests/scheme1 scm)
    131     fi
    132     for name in $NAMES; do
    133         fixture=tests/scheme1/$name.scm
    134         expected_stdout_file=tests/scheme1/$name.expected
    135         expected_exit_file=tests/scheme1/$name.expected-exit
    136 
    137         if [ ! -e "$fixture" ]; then echo "  SKIP $name (no .scm)"; continue; fi
    138         if [ -e "$expected_stdout_file" ]; then
    139             expected_stdout=$(cat "$expected_stdout_file")
    140         else
    141             expected_stdout=
    142         fi
    143         if [ -e "$expected_exit_file" ]; then
    144             expected_exit=$(cat "$expected_exit_file")
    145         else
    146             expected_exit=0
    147         fi
    148 
    149         label="[$ARCH] $name"
    150         bin=build/$ARCH/podman/boot2/scheme1
    151         if [ ! -x "$bin" ]; then
    152             report "$label" FAIL
    153             echo "    (missing $bin -- run 'make scheme1 ARCH=$ARCH')" >&2
    154             continue
    155         fi
    156 
    157         tmp_stdout=$(mktemp)
    158         if sh tests/boot-run-scheme1.sh "$fixture" >"$tmp_stdout" 2>&1; then
    159             actual_exit=0
    160         else
    161             actual_exit=$?
    162         fi
    163         actual_stdout=$(cat "$tmp_stdout")
    164         rm -f "$tmp_stdout"
    165 
    166         if [ "$actual_stdout" = "$expected_stdout" ] \
    167            && [ "$actual_exit" = "$expected_exit" ]; then
    168             report "$label" PASS
    169         else
    170             report "$label" FAIL
    171             if [ "$actual_stdout" != "$expected_stdout" ]; then
    172                 show_diff "$expected_stdout" "$actual_stdout"
    173             fi
    174             if [ "$actual_exit" != "$expected_exit" ]; then
    175                 echo "    exit: expected $expected_exit, got $actual_exit"
    176             fi
    177         fi
    178     done
    179 }
    180 
    181 ## --- cc-* suites --------------------------------------------------------
    182 
    183 # _cc_unit_suite <suite-name> <expected-ext> <layer-list>
    184 _cc_unit_suite() {
    185     suite=$1; ext=$2; layers=$3
    186     [ -n "$NAMES" ] || NAMES=$(discover tests/$suite scm)
    187     for name in $NAMES; do
    188         fixture=tests/$suite/$name.scm
    189         [ -e "$fixture" ] || { echo "  SKIP $name (no .scm)"; continue; }
    190         if [ -e "tests/$suite/$name.$ext" ]; then
    191             expout=$(cat "tests/$suite/$name.$ext")
    192         else
    193             expout=
    194         fi
    195         if [ -e "tests/$suite/$name.expected-exit" ]; then
    196             expexit=$(cat "tests/$suite/$name.expected-exit")
    197         else
    198             expexit=0
    199         fi
    200         tmp=$(mktemp)
    201         # Wrap in `sh -c` so catm failure doesn't abort under set -e and
    202         # scheme1 still runs (matches host runner's prior behavior).
    203         if sh -c "
    204             build/$ARCH/podman/boot2/catm /tmp/cc-test.scm $layers $fixture
    205             exec build/$ARCH/podman/boot2/scheme1 /tmp/cc-test.scm
    206         " >"$tmp" 2>&1; then
    207             act_exit=0
    208         else
    209             act_exit=$?
    210         fi
    211         act_out=$(cat "$tmp"); rm -f "$tmp"
    212         compare_runtime "[$ARCH] $suite/$name" "$expout" "$expexit" "$act_out" "$act_exit"
    213     done
    214 }
    215 
    216 run_cc_util_suite() {
    217     _cc_unit_suite cc-util expected "scheme1/prelude.scm cc/cc.scm"
    218 }
    219 
    220 # _cc_pipeline_suite <suite-name> <expected-ext> <layers>
    221 _cc_pipeline_suite() {
    222     suite=$1; ext=$2; layers=$3
    223     [ -n "$NAMES" ] || NAMES=$(discover tests/$suite c)
    224     for name in $NAMES; do
    225         fixture=tests/$suite/$name.c
    226         [ -e "$fixture" ] || { echo "  SKIP $name (no .c)"; continue; }
    227         if [ -e "tests/$suite/$name.$ext" ]; then
    228             expout=$(grep -v '^;;' "tests/$suite/$name.$ext" || true)
    229         else
    230             expout=
    231         fi
    232         if [ -e "tests/$suite/$name.expected-exit" ]; then
    233             expexit=$(cat "tests/$suite/$name.expected-exit")
    234         else
    235             expexit=0
    236         fi
    237         tmp=$(mktemp)
    238         if sh -c "
    239             build/$ARCH/podman/boot2/catm /tmp/cc-test.scm $layers
    240             exec build/$ARCH/podman/boot2/scheme1 /tmp/cc-test.scm $fixture
    241         " >"$tmp" 2>&1; then
    242             act_exit=0
    243         else
    244             act_exit=$?
    245         fi
    246         if [ "$expexit" != "0" ]; then
    247             act_out=
    248         else
    249             act_out=$(cat "$tmp")
    250         fi
    251         rm -f "$tmp"
    252         compare_runtime "[$ARCH] $suite/$name" "$expout" "$expexit" "$act_out" "$act_exit"
    253     done
    254 }
    255 
    256 run_cc_lex_suite() {
    257     _cc_pipeline_suite cc-lex expected \
    258         "scheme1/prelude.scm cc/cc.scm tests/cc-lex/_run-lex.scm"
    259 }
    260 
    261 # Two passes: .c fixtures via the lex+pp pipeline; the lone .scm fixture
    262 # (22-initial-defines) covers the -D mechanism the driver doesn't expose,
    263 # so it stays on the unit-suite path. NAMES is restored between passes
    264 # because _cc_pipeline_suite may have populated it via discovery.
    265 run_cc_pp_suite() {
    266     saved=$NAMES
    267     _cc_pipeline_suite cc-pp expected \
    268         "scheme1/prelude.scm cc/cc.scm tests/cc-pp/_run-pp.scm"
    269     NAMES=$saved
    270     _cc_unit_suite cc-pp expected \
    271         "scheme1/prelude.scm cc/cc.scm"
    272 }
    273 
    274 # _cc_runtime_suite <suite-name> <fixture-ext> <layers> [<fixture-as-arg?>]
    275 _cc_runtime_suite() {
    276     suite=$1; fext=$2; layers=$3; arg_pass=${4:-0}
    277     [ -n "$NAMES" ] || NAMES=$(discover tests/$suite "$fext")
    278     for name in $NAMES; do
    279         fixture=tests/$suite/$name.$fext
    280         [ -e "$fixture" ] || { echo "  SKIP $name (no .$fext)"; continue; }
    281 
    282         if [ -e tests/$suite/$name.expected ]; then
    283             expout=$(cat tests/$suite/$name.expected)
    284         else
    285             expout=
    286         fi
    287         if [ -e tests/$suite/$name.expected-exit ]; then
    288             expexit=$(cat tests/$suite/$name.expected-exit)
    289         else
    290             expexit=0
    291         fi
    292 
    293         elf=build/$ARCH/tests/$suite/$name
    294         workdir=build/$ARCH/.work/tests/$suite/$name
    295         p1pp=$workdir/$name.P1pp
    296         mkdir -p "$(dirname "$elf")" "$workdir"
    297 
    298         if [ "$arg_pass" = "1" ]; then
    299             cmd="
    300                 build/$ARCH/podman/boot2/catm /tmp/cc-test.scm $layers
    301                 exec build/$ARCH/podman/boot2/scheme1 /tmp/cc-test.scm $fixture
    302             "
    303         else
    304             cmd="
    305                 build/$ARCH/podman/boot2/catm /tmp/cc-test.scm $layers $fixture
    306                 exec build/$ARCH/podman/boot2/scheme1 /tmp/cc-test.scm
    307             "
    308         fi
    309         label="[$ARCH] $suite/$name"
    310         cg_log=$workdir/cg.log
    311         if ! sh -c "$cmd" >"$p1pp" 2>"$cg_log"; then
    312             fail "$label" "cg emission failed:" "$cg_log"
    313             continue
    314         fi
    315 
    316         p1pp_log=$workdir/p1pp.log
    317         if ! WORK_SUBPATH=tests/$suite/$name \
    318                 sh tests/build-p1pp.sh "$elf" "$p1pp" \
    319                 >"$p1pp_log" 2>&1; then
    320             fail "$label" "P1pp assemble failed:" "$p1pp_log"
    321             continue
    322         fi
    323 
    324         tmp=$(mktemp)
    325         if "./$elf" >"$tmp" 2>&1; then
    326             act_exit=0
    327         else
    328             act_exit=$?
    329         fi
    330         act_out=$(cat "$tmp"); rm -f "$tmp"
    331         compare_runtime "[$ARCH] $suite/$name" "$expout" "$expexit" "$act_out" "$act_exit"
    332     done
    333 }
    334 
    335 run_cc_cg_suite() {
    336     _cc_runtime_suite cc-cg scm \
    337         "scheme1/prelude.scm cc/cc.scm" 0
    338 }
    339 
    340 run_cc_suite() {
    341     [ -n "$NAMES" ] || NAMES=$(discover tests/cc c)
    342     for name in $NAMES; do
    343         src=tests/cc/$name.c
    344         [ -e "$src" ] || { echo "  SKIP $name (no .c)"; continue; }
    345         if [ -e tests/cc/$name.expected ]; then
    346             expout=$(cat tests/cc/$name.expected)
    347         else
    348             expout=
    349         fi
    350         if [ -e tests/cc/$name.expected-exit ]; then
    351             expexit=$(cat tests/cc/$name.expected-exit)
    352         else
    353             expexit=0
    354         fi
    355         elf=build/$ARCH/tests/cc/$name
    356         workdir=build/$ARCH/.work/tests/cc/$name
    357         p1pp=$workdir/$name.P1pp
    358         label="[$ARCH] cc/$name"
    359         mkdir -p "$(dirname "$elf")" "$workdir"
    360 
    361         cc_log=$workdir/cc.log
    362         # shellcheck disable=SC2086 # CC_EXTRA_FLAGS is intentionally word-split.
    363         if ! build/$ARCH/podman/boot2/scheme1 build/cc.scm $CC_EXTRA_FLAGS \
    364                 "$src" "$p1pp" >"$cc_log" 2>&1; then
    365             fail "$label" "cc compile failed:" "$cc_log"
    366             continue
    367         fi
    368 
    369         p1pp_log=$workdir/p1pp.log
    370         if ! WORK_SUBPATH=tests/cc/$name \
    371                 sh tests/build-p1pp.sh "$elf" "$p1pp" \
    372                 >"$p1pp_log" 2>&1; then
    373             fail "$label" "P1pp assemble failed:" "$p1pp_log"
    374             continue
    375         fi
    376 
    377         tmp=$(mktemp)
    378         if "./$elf" >"$tmp" 2>&1; then
    379             act_exit=0
    380         else
    381             act_exit=$?
    382         fi
    383         act_out=$(cat "$tmp"); rm -f "$tmp"
    384         compare_runtime "$label" "$expout" "$expexit" "$act_out" "$act_exit"
    385     done
    386 }
    387 
    388 ## --- cc-libc suite ------------------------------------------------------
    389 ##
    390 ## Mirrors run_cc_suite but links the prepended libc.P1pp into every
    391 ## fixture. Targeted red-green TDD on the cc.scm + libc combination —
    392 ## each .c is small (one feature: printf with %d, malloc round-trip,
    393 ## getenv lookup, …) so a failure isolates the bug to one symbol path.
    394 run_cc_libc_suite() {
    395     [ -n "$NAMES" ] || NAMES=$(discover tests/cc-libc c)
    396     for name in $NAMES; do
    397         src=tests/cc-libc/$name.c
    398         [ -e "$src" ] || { echo "  SKIP $name (no .c)"; continue; }
    399         if [ -e tests/cc-libc/$name.expected ]; then
    400             expout=$(cat tests/cc-libc/$name.expected)
    401         else
    402             expout=
    403         fi
    404         if [ -e tests/cc-libc/$name.expected-exit ]; then
    405             expexit=$(cat tests/cc-libc/$name.expected-exit)
    406         else
    407             expexit=0
    408         fi
    409         elf=build/$ARCH/tests/cc-libc/$name
    410         workdir=build/$ARCH/.work/tests/cc-libc/$name
    411         client_p1pp=$workdir/$name.client.P1pp
    412         label="[$ARCH] cc-libc/$name"
    413         mkdir -p "$(dirname "$elf")" "$workdir"
    414 
    415         # Compile the client TU in lib mode so it doesn't emit its
    416         # own :p1_main / :ELF_end and namespaces its anonymous string
    417         # labels under app__cc__str_N — distinct from libc.P1pp's
    418         # libc__cc__str_N.
    419         cc_log=$workdir/cc.log
    420         # shellcheck disable=SC2086 # CC_EXTRA_FLAGS is intentionally word-split.
    421         if ! build/$ARCH/podman/boot2/scheme1 build/cc.scm $CC_EXTRA_FLAGS \
    422                 --lib=app__ "$src" "$client_p1pp" \
    423                 >"$cc_log" 2>&1; then
    424             fail "$label" "cc compile failed:" "$cc_log"
    425             continue
    426         fi
    427 
    428         # catm chain: entry-libc supplies :p1_main (calls __libc_init
    429         # then main), libc.P1pp supplies the libc routines, the client
    430         # supplies :main, elf-end supplies the :ELF_end terminator.
    431         p1pp_log=$workdir/p1pp.log
    432         if ! WORK_SUBPATH=tests/cc-libc/$name \
    433                 sh tests/build-p1pp.sh "$elf" \
    434                 P1/entry-libc.P1pp build/$ARCH/podman/boot3/libc.P1pp \
    435                 "$client_p1pp" P1/elf-end.P1pp \
    436                 >"$p1pp_log" 2>&1; then
    437             fail "$label" "P1pp assemble failed:" "$p1pp_log"
    438             continue
    439         fi
    440 
    441         tmp=$(mktemp)
    442         if "./$elf" >"$tmp" 2>&1; then
    443             act_exit=0
    444         else
    445             act_exit=$?
    446         fi
    447         act_out=$(cat "$tmp"); rm -f "$tmp"
    448         compare_runtime "$label" "$expout" "$expexit" "$act_out" "$act_exit"
    449     done
    450 }
    451 
    452 ## --- cc-ext suite -------------------------------------------------------
    453 ##
    454 ## External C subset coverage via the vendored c-testsuite single-exec
    455 ## fixtures (vendor/c-testsuite/single-exec/NNNNN.c). This complements
    456 ## tests/cc, which is hand-curated; the goal here is breadth, to surface
    457 ## bugs in cc.scm against programs we did not write ourselves.
    458 ##
    459 ## Fixture spec: each program returns 0 on success and non-zero on
    460 ## failure; the .expected file pins stdout+stderr. We honest-report:
    461 ## PASS = runs + matches expected + exit 0, anything else = FAIL.
    462 ## Compile/assemble errors count as FAIL too — every regression in the
    463 ## supported subset shows up. As cc.scm grows the FAIL count drops.
    464 ##
    465 ## Pipeline switches on the .tags file: tests with `needs-libc` are
    466 ## linked through the same chain as the cc-libc suite (entry-libc +
    467 ## mes-libc + client + elf-end, with --lib=app__ to namespace string
    468 ## labels); plain tests use the bare cc -> P1pp -> ELF flow.
    469 ##
    470 ## Selection: with no name args, runs every fixture. Otherwise the args
    471 ## are basenames (e.g. 00001) under vendor/c-testsuite/single-exec/.
    472 run_cc_ext_suite() {
    473     dir=vendor/c-testsuite/single-exec
    474     [ -n "$NAMES" ] || NAMES=$(discover "$dir" c)
    475     for name in $NAMES; do
    476         src=$dir/$name.c
    477         tags=$dir/$name.c.tags
    478         expected=$dir/$name.c.expected
    479         label="[$ARCH] cc-ext/$name"
    480 
    481         [ -e "$src" ] || { echo "  SKIP $name (no .c)"; continue; }
    482 
    483         needs_libc=0
    484         if [ -e "$tags" ] && grep -q '^needs-libc$' "$tags"; then
    485             needs_libc=1
    486         fi
    487 
    488         expout=
    489         [ -e "$expected" ] && expout=$(cat "$expected")
    490         expexit=0
    491 
    492         elf=build/$ARCH/tests/cc-ext/$name
    493         workdir=build/$ARCH/.work/tests/cc-ext/$name
    494         client_p1pp=$workdir/$name.P1pp
    495         mkdir -p "$(dirname "$elf")" "$workdir"
    496 
    497         cc_log=$workdir/cc.log
    498         if [ "$needs_libc" = "1" ]; then
    499             # Lib mode: client TU compiled with --lib=app__ so it doesn't
    500             # emit its own :p1_main / :ELF_end and namespaces anonymous
    501             # string labels under app__cc__str_N (libc supplies its own).
    502             # shellcheck disable=SC2086 # CC_EXTRA_FLAGS is intentionally word-split.
    503             if ! build/$ARCH/podman/boot2/scheme1 build/cc.scm $CC_EXTRA_FLAGS \
    504                     --lib=app__ "$src" "$client_p1pp" \
    505                     >"$cc_log" 2>&1; then
    506                 fail "$label" "cc compile failed:" "$cc_log"
    507                 continue
    508             fi
    509         else
    510             # shellcheck disable=SC2086 # CC_EXTRA_FLAGS is intentionally word-split.
    511             if ! build/$ARCH/podman/boot2/scheme1 build/cc.scm $CC_EXTRA_FLAGS \
    512                     "$src" "$client_p1pp" \
    513                     >"$cc_log" 2>&1; then
    514                 fail "$label" "cc compile failed:" "$cc_log"
    515                 continue
    516             fi
    517         fi
    518 
    519         p1pp_log=$workdir/p1pp.log
    520         if [ "$needs_libc" = "1" ]; then
    521             # catm chain matches run_cc_libc_suite: entry-libc supplies
    522             # :p1_main (calls __libc_init then main), libc.P1pp the libc
    523             # routines, the client :main, elf-end the :ELF_end terminator.
    524             if ! WORK_SUBPATH=tests/cc-ext/$name \
    525                     sh tests/build-p1pp.sh "$elf" \
    526                     P1/entry-libc.P1pp build/$ARCH/podman/boot3/libc.P1pp \
    527                     "$client_p1pp" P1/elf-end.P1pp \
    528                     >"$p1pp_log" 2>&1; then
    529                 fail "$label" "P1pp assemble failed:" "$p1pp_log"
    530                 continue
    531             fi
    532         else
    533             if ! WORK_SUBPATH=tests/cc-ext/$name \
    534                     sh tests/build-p1pp.sh "$elf" "$client_p1pp" \
    535                     >"$p1pp_log" 2>&1; then
    536                 fail "$label" "P1pp assemble failed:" "$p1pp_log"
    537                 continue
    538             fi
    539         fi
    540 
    541         tmp=$(mktemp)
    542         if "./$elf" >"$tmp" 2>&1; then
    543             act_exit=0
    544         else
    545             act_exit=$?
    546         fi
    547         act_out=$(cat "$tmp"); rm -f "$tmp"
    548         compare_runtime "$label" "$expout" "$expexit" "$act_out" "$act_exit"
    549     done
    550 }
    551 
    552 ## --- tcc-cc suite -------------------------------------------------------
    553 ##
    554 ## Runs the plain tests/cc fixtures through a self-built tcc. STAGE
    555 ## selects the compiler — STAGE=2 uses tcc-tcc (twice-compiled, built
    556 ## by tcc-boot2 which was itself built by cc.scm), STAGE=3 uses
    557 ## tcc-tcc-tcc (thrice-compiled, built by tcc-tcc — the README
    558 ## endpoint, the first tcc whose machine code an actual tcc emitted).
    559 ## start.o / mem.o come from the tcc-cc tree (cross-asm and
    560 ## tcc-boot2-built respectively); they don't change between stages.
    561 run_tcc_cc_suite() {
    562     case "$ARCH" in
    563         aarch64) tcc_target=ARM64;   tcc_banner='AArch64' ;;
    564         amd64)   tcc_target=X86_64;  tcc_banner='x86_64'  ;;
    565         riscv64) tcc_target=RISCV64; tcc_banner='riscv64' ;;
    566         *)
    567             echo "  FAIL [$ARCH] tcc-cc"
    568             echo "    tcc-cc supports ARCH in {aarch64, amd64, riscv64} only" >&2
    569             return
    570             ;;
    571     esac
    572 
    573     case "${STAGE:-2}" in
    574         2) tcc=build/$ARCH/podman/boot4/tcc1; stage_tag=stage2 ;;
    575         3) tcc=build/$ARCH/podman/boot4/tcc2; stage_tag=stage3 ;;
    576         *)
    577             echo "  FAIL [$ARCH] tcc-cc"
    578             echo "    unknown STAGE='$STAGE' (expected 2 or 3)" >&2
    579             return
    580             ;;
    581     esac
    582     start=build/$ARCH/tcc/cc/start.o
    583     libtcc1=build/$ARCH/podman/boot4/libtcc1.a
    584     tcc_include=build/$ARCH/src/src/tcc/tcc-0.9.26-1147-gee75a10c/include
    585     # x86_64 only: __va_start / __va_arg intrinsics for variadic
    586     # functions. Other arches lower va_arg without out-of-line helpers.
    587     if [ "$ARCH" = "amd64" ]; then
    588         va_list=build/$ARCH/tcc/cc/va_list.o
    589     else
    590         va_list=
    591     fi
    592     if [ ! -x "$tcc" ]; then
    593         echo "  FAIL [$ARCH] tcc-cc"
    594         echo "    missing $tcc -- run 'make test SUITE=tcc-cc ARCH=$ARCH'" >&2
    595         return
    596     fi
    597     if [ ! -e "$start" ]; then
    598         echo "  FAIL [$ARCH] tcc-cc"
    599         echo "    missing $start -- run 'make test SUITE=tcc-cc ARCH=$ARCH'" >&2
    600         return
    601     fi
    602     if [ ! -e "$libtcc1" ]; then
    603         echo "  FAIL [$ARCH] tcc-cc"
    604         echo "    missing $libtcc1 -- run 'make test SUITE=tcc-cc ARCH=$ARCH'" >&2
    605         return
    606     fi
    607     if [ -n "$va_list" ] && [ ! -e "$va_list" ]; then
    608         echo "  FAIL [$ARCH] tcc-cc"
    609         echo "    missing $va_list -- run 'make test SUITE=tcc-cc ARCH=$ARCH'" >&2
    610         return
    611     fi
    612     if ! "$tcc" -version 2>/dev/null | grep "$tcc_banner" >/dev/null; then
    613         echo "  FAIL [$ARCH] tcc-cc"
    614         echo "    $tcc is not a $tcc_banner-targeted tcc; rebuild with TCC_TARGET=$tcc_target" >&2
    615         return
    616     fi
    617 
    618     [ -n "$NAMES" ] || NAMES=$(discover tests/cc c)
    619     for name in $NAMES; do
    620         src=tests/cc/$name.c
    621         [ -e "$src" ] || { echo "  SKIP $name (no .c)"; continue; }
    622         if [ -e tests/cc/$name.expected ]; then
    623             expout=$(cat tests/cc/$name.expected)
    624         else
    625             expout=
    626         fi
    627         if [ -e tests/cc/$name.expected-exit ]; then
    628             expexit=$(cat tests/cc/$name.expected-exit)
    629         else
    630             expexit=0
    631         fi
    632 
    633         elf=build/$ARCH/tests/tcc/cc/$stage_tag/$name
    634         workdir=build/$ARCH/.work/tests/tcc/cc/$stage_tag/$name
    635         label="[$ARCH] tcc-cc[$stage_tag]/$name"
    636         mkdir -p "$(dirname "$elf")" "$workdir"
    637 
    638         tcc_log=$workdir/tcc.log
    639         # shellcheck disable=SC2086 # $va_list is intentionally word-split (may be empty).
    640         if ! "$tcc" -nostdlib -I "$tcc_include" \
    641                 "$start" "$libtcc1" $va_list "$src" -o "$elf" \
    642                 >"$tcc_log" 2>&1; then
    643             fail "$label" "tcc compile/link failed:" "$tcc_log"
    644             continue
    645         fi
    646 
    647         tmp=$(mktemp)
    648         if "./$elf" >"$tmp" 2>&1; then
    649             act_exit=0
    650         else
    651             act_exit=$?
    652         fi
    653         act_out=$(cat "$tmp"); rm -f "$tmp"
    654         compare_runtime "$label" "$expout" "$expexit" "$act_out" "$act_exit"
    655     done
    656 }
    657 
    658 ## --- tcc-libc suite -----------------------------------------------------
    659 ##
    660 ## End-to-end "tcc as a real compiler" check, run through a self-built
    661 ## tcc. STAGE selects the compiler — STAGE=2 uses tcc-tcc (twice-
    662 ## compiled), STAGE=3 uses tcc-tcc-tcc (thrice-compiled, README
    663 ## endpoint). tcc-boot2 already compiled mes-libc into libc.o; for each
    664 ## tests/cc-libc fixture, the selected tcc compiles + links it against
    665 ##   start.o          per-arch entry stub: __libc_init then main then exit
    666 ##   sys_stubs.o      per-arch raw-syscall sys_* implementations
    667 ##   mem.o            mem* compiler-builtin runtime (memcpy/memmove/memset/memcmp)
    668 ##   libc.o           tcc-boot2-built mes-libc
    669 ## and runs the resulting ELF natively in the per-arch container.
    670 run_tcc_libc_suite() {
    671     case "$ARCH" in
    672         aarch64) tcc_target=ARM64;   tcc_banner='AArch64' ;;
    673         amd64)   tcc_target=X86_64;  tcc_banner='x86_64'  ;;
    674         riscv64) tcc_target=RISCV64; tcc_banner='riscv64' ;;
    675         *)
    676             echo "  FAIL [$ARCH] tcc-libc"
    677             echo "    tcc-libc supports ARCH in {aarch64, amd64, riscv64} only" >&2
    678             return
    679             ;;
    680     esac
    681 
    682     case "${STAGE:-2}" in
    683         2) tcc=build/$ARCH/podman/boot4/tcc1; stage_tag=stage2 ;;
    684         3) tcc=build/$ARCH/podman/boot4/tcc2; stage_tag=stage3 ;;
    685         *)
    686             echo "  FAIL [$ARCH] tcc-libc"
    687             echo "    unknown STAGE='$STAGE' (expected 2 or 3)" >&2
    688             return
    689             ;;
    690     esac
    691     start=build/$ARCH/tcc/libc/start.o
    692     sys_stubs=build/$ARCH/tcc/libc/sys_stubs.o
    693     libtcc1=build/$ARCH/podman/boot4/libtcc1.a
    694     libc=build/$ARCH/podman/boot5/libc.a
    695     tcc_include=build/$ARCH/src/src/tcc/tcc-0.9.26-1147-gee75a10c/include
    696     # x86_64 only: __va_start / __va_arg intrinsics for variadic
    697     # functions. mes-libc's printf family hits this directly.
    698     if [ "$ARCH" = "amd64" ]; then
    699         va_list=build/$ARCH/tcc/cc/va_list.o
    700     else
    701         va_list=
    702     fi
    703     for f in "$tcc" "$start" "$sys_stubs" "$libtcc1" "$libc"; do
    704         if [ ! -e "$f" ]; then
    705             echo "  FAIL [$ARCH] tcc-libc"
    706             echo "    missing $f -- run 'make test SUITE=tcc-libc ARCH=$ARCH'" >&2
    707             return
    708         fi
    709     done
    710     if [ -n "$va_list" ] && [ ! -e "$va_list" ]; then
    711         echo "  FAIL [$ARCH] tcc-libc"
    712         echo "    missing $va_list -- run 'make test SUITE=tcc-libc ARCH=$ARCH'" >&2
    713         return
    714     fi
    715     if ! "$tcc" -version 2>/dev/null | grep "$tcc_banner" >/dev/null; then
    716         echo "  FAIL [$ARCH] tcc-libc"
    717         echo "    $tcc is not a $tcc_banner-targeted tcc; rebuild with TCC_TARGET=$tcc_target" >&2
    718         return
    719     fi
    720 
    721     [ -n "$NAMES" ] || NAMES=$(discover tests/cc-libc c)
    722     for name in $NAMES; do
    723         src=tests/cc-libc/$name.c
    724         [ -e "$src" ] || { echo "  SKIP $name (no .c)"; continue; }
    725         if [ -e tests/cc-libc/$name.expected ]; then
    726             expout=$(cat tests/cc-libc/$name.expected)
    727         else
    728             expout=
    729         fi
    730         if [ -e tests/cc-libc/$name.expected-exit ]; then
    731             expexit=$(cat tests/cc-libc/$name.expected-exit)
    732         else
    733             expexit=0
    734         fi
    735 
    736         elf=build/$ARCH/tests/tcc/libc/$stage_tag/$name
    737         workdir=build/$ARCH/.work/tests/tcc/libc/$stage_tag/$name
    738         label="[$ARCH] tcc-libc[$stage_tag]/$name"
    739         mkdir -p "$(dirname "$elf")" "$workdir"
    740 
    741         tcc_log=$workdir/tcc.log
    742         # shellcheck disable=SC2086 # $va_list is intentionally word-split (may be empty).
    743         if ! "$tcc" -nostdlib -I "$tcc_include" \
    744                 "$start" "$sys_stubs" "$libtcc1" "$libc" $va_list "$src" -o "$elf" \
    745                 >"$tcc_log" 2>&1; then
    746             fail "$label" "tcc compile/link failed:" "$tcc_log"
    747             continue
    748         fi
    749 
    750         tmp=$(mktemp)
    751         if "./$elf" >"$tmp" 2>&1; then
    752             act_exit=0
    753         else
    754             act_exit=$?
    755         fi
    756         act_out=$(cat "$tmp"); rm -f "$tmp"
    757         compare_runtime "$label" "$expout" "$expexit" "$act_out" "$act_exit"
    758     done
    759 }
    760 
    761 case "$SUITE" in
    762     m1pp)     run_m1pp_suite ;;
    763     p1)       run_p1_suite ;;
    764     scheme1)  run_scheme1_suite ;;
    765     cc-util)  run_cc_util_suite ;;
    766     cc-lex)   run_cc_lex_suite ;;
    767     cc-pp)    run_cc_pp_suite ;;
    768     cc-cg)    run_cc_cg_suite ;;
    769     cc)       run_cc_suite ;;
    770     cc-libc)  run_cc_libc_suite ;;
    771     cc-ext)   run_cc_ext_suite ;;
    772     tcc-cc)   run_tcc_cc_suite ;;
    773     tcc-libc) run_tcc_libc_suite ;;
    774 esac