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