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