run.sh (5858B)
1 #!/usr/bin/env bash 2 # test/hosted/run.sh — the hosted test suite: build each C case for every 3 # (target, link-mode) config in the support set with scripts/hosted.sh, run it 4 # through the shared seam test/lib/exec_target.sh, and check exit code + stdout 5 # against the oracle (<name>.expected / <name>.stdout). The first principled 6 # cross-OS hosted-exec suite; seed case is cases/hello.c. 7 # 8 # Full matrix (15 configs): 9 # linux {aa64,x64,rv64} x {musl-static, musl-dynamic, glibc} (podman: alpine 10 # for musl, debian for glibc; each routed by its exec tag) 11 # macos-aarch64 (native) 12 # windows {x64,aarch64} (VM) 13 # freebsd {amd64,aarch64,riscv64} (VM) 14 # 15 # Two verdicts per (case, config): ":build" (cc+link ok) and ":run" (right exit 16 # code + stdout). A target whose sysroot is absent SKIPs build; a config with no 17 # runner here SKIPs run. The tag carries the libc, so one flush routes every 18 # config to its runner. 19 # 20 # Default config set: Linux + macOS (fast). FreeBSD + Windows (VMs) are added by 21 # KIT_HOSTED_VM=1. env: KIT, HOSTED_CONFIGS (override the list), KIT_HOSTED_VM, 22 # EXEC_VM_KEEP_UP. 23 24 set -u 25 26 ROOT="$(cd "$(dirname "$0")/../.." && pwd)" 27 KIT="${KIT:-$ROOT/build/kit}" 28 HOSTED="$ROOT/scripts/hosted.sh" 29 CASES="$ROOT/test/hosted/cases" 30 BUILD_DIR="$ROOT/build/test/hosted" 31 32 # shellcheck source=../lib/kit_sh_report.sh 33 . "$ROOT/test/lib/kit_sh_report.sh" 34 # shellcheck source=../lib/exec_target.sh 35 . "$ROOT/test/lib/exec_target.sh" 36 kit_report_init 37 trap exec_target_teardown_all EXIT 38 39 [ -x "$KIT" ] || { echo "hosted: kit not found at $KIT (run 'make bin')" >&2; exit 2; } 40 41 # exec_target's caller contract for the linux/macos runners (VM tags ignore these). 42 have_podman=0; command -v podman >/dev/null 2>&1 && have_podman=1 43 QEMU_BIN="${QEMU_BIN:-$(command -v qemu-aarch64 2>/dev/null || true)}" 44 QEMU_RV64_BIN="${QEMU_RV64_BIN:-$(command -v qemu-riscv64 2>/dev/null || true)}" 45 have_qemu=0; [ -n "$QEMU_BIN" ] && have_qemu=1 46 case "$(uname -m 2>/dev/null)" in aarch64|arm64) is_aarch64=1 ;; *) is_aarch64=0 ;; esac 47 export have_podman QEMU_BIN QEMU_RV64_BIN have_qemu is_aarch64 48 mkdir -p "$BUILD_DIR" 49 EXEC_TARGET_MOUNT_ROOT="$BUILD_DIR"; export EXEC_TARGET_MOUNT_ROOT 50 51 # ---- config list ----------------------------------------------------------- 52 # A config is "<target>[:<mode>]". mode (static|dynamic) only varies for musl; 53 # glibc is dynamic-only, freebsd is static, windows/macos have one shape. 54 LINUX_CONFIGS="" 55 for a in aa64 x64 rv64; do 56 LINUX_CONFIGS="$LINUX_CONFIGS linux-musl-$a:static linux-musl-$a:dynamic linux-glibc-$a" 57 done 58 DEFAULT_CONFIGS="$LINUX_CONFIGS macos-aarch64" 59 VM_CONFIGS="freebsd-amd64 freebsd-aarch64 freebsd-riscv64 windows-x64 windows-aarch64" 60 if [ -n "${HOSTED_CONFIGS:-}" ]; then 61 CONFIGS="$HOSTED_CONFIGS" 62 else 63 CONFIGS="$DEFAULT_CONFIGS" 64 [ "${KIT_HOSTED_VM:-0}" = 1 ] && CONFIGS="$CONFIGS $VM_CONFIGS" 65 fi 66 67 # Link flags for a (target, mode): musl honors the mode; freebsd is self-contained 68 # static; glibc/windows/macos use their default shape (glibc is dynamic-only). 69 link_flags_for() { 70 local target="$1" mode="$2" 71 case "$target" in 72 linux-musl-*) [ "$mode" = static ] && echo -static ;; 73 freebsd-*) echo -static ;; 74 *) echo ;; 75 esac 76 } 77 78 # ---- build + queue --------------------------------------------------------- 79 H_NAME=(); H_OUT=(); H_RC=(); H_EXP=(); H_STDOUT=() 80 81 printf 'hosted: configs=%s\n' "$CONFIGS" 82 83 for case_src in "$CASES"/*.c; do 84 cbase="$(basename "${case_src%.c}")" 85 exp=0; [ -f "${case_src%.c}.expected" ] && exp="$(cat "${case_src%.c}.expected")" 86 want_out=""; [ -f "${case_src%.c}.stdout" ] && want_out="$(cat "${case_src%.c}.stdout")" 87 for config in $CONFIGS; do 88 target="${config%%:*}"; mode="${config#*:}"; [ "$mode" = "$config" ] && mode="" 89 os="${target%%-*}" 90 label="$cbase/$config" 91 sr="$("$HOSTED" path "$target" 2>/dev/null)" 92 if [ "$os" != macos ] && { [ -z "$sr" ] || [ ! -d "$sr" ]; }; then 93 kit_skip "$label:build" "missing sysroot (scripts/hosted.sh prepare $target)" 94 continue 95 fi 96 cdir="$BUILD_DIR/$(printf '%s' "$config" | tr ':/' '__')"; mkdir -p "$cdir" 97 ext=""; [ "$os" = windows ] && ext=".exe" 98 exe="$cdir/$cbase$ext" 99 # shellcheck disable=SC2046 100 if ! "$HOSTED" cc "$target" $(link_flags_for "$target" "$mode") "$case_src" -o "$exe" \ 101 > "$cdir/$cbase.cc.out" 2> "$cdir/$cbase.cc.err"; then 102 kit_fail "$label:build" "hosted.sh cc failed" 103 sed 's/^/ | /' "$cdir/$cbase.cc.err" | head -20 104 continue 105 fi 106 kit_pass "$label:build" 107 108 tag="$("$HOSTED" tag "$target")" 109 if ! exec_target_supported "$tag"; then 110 kit_skip "$label:run" "no runner for $tag" 111 continue 112 fi 113 exec_target_queue "$tag" "$label" "$exe" \ 114 "$cdir/$cbase.out" "$cdir/$cbase.err" "$cdir/$cbase.rc" 115 H_NAME+=("$label:run"); H_OUT+=("$cdir/$cbase.out") 116 H_RC+=("$cdir/$cbase.rc"); H_EXP+=("$exp"); H_STDOUT+=("$want_out") 117 done 118 done 119 120 # ---- execute + check ------------------------------------------------------- 121 exec_target_flush 122 123 i=0; n="${#H_NAME[@]}" 124 while [ "$i" -lt "$n" ]; do 125 name="${H_NAME[$i]}"; exp=$(( ${H_EXP[$i]} & 255 )) 126 rc="$(cat "${H_RC[$i]}" 2>/dev/null || echo 127)" 127 got_out="$(cat "${H_OUT[$i]}" 2>/dev/null || true)" 128 if ! case "$rc" in ''|*[!0-9-]*) false ;; *) true ;; esac; then 129 kit_fail "$name" "did not run (rc=$rc)" 130 elif [ "$(( rc & 255 ))" -ne "$exp" ]; then 131 kit_fail "$name" "expected rc $exp, got $rc" 132 elif [ -n "${H_STDOUT[$i]}" ] && [ "$got_out" != "${H_STDOUT[$i]}" ]; then 133 kit_fail "$name" "stdout mismatch" 134 printf ' want: %s\n got: %s\n' "${H_STDOUT[$i]}" "$got_out" 135 else 136 kit_pass "$name" 137 fi 138 i=$((i + 1)) 139 done 140 141 KIT_SKIP_IS_FAILURE=0 142 kit_summary test-hosted 143 kit_exit