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