kit

kit
git clone https://git.ryansepassi.com/git/kit.git
Log | Files | Refs | README

selfhost.sh (7469B)


      1 #!/usr/bin/env bash
      2 # scripts/selfhost.sh — self-host orchestrator: the engine behind
      3 # `make test-selfhost`. For each target it builds a kit that RUNS on that target,
      4 # then uses that kit to compile + run a program there. The shape differs by OS
      5 # (see doc/plan/PORT.md):
      6 #
      7 #   macos    native 3-stage `make bootstrap`, then a program through stage3
      8 #   linux    native 3-stage in a podman container (emulated for a non-host arch)
      9 #   freebsd  native 3-stage in the VM
     10 #   windows  CROSS-build kit.exe on the host (the VM has no seed compiler);
     11 #            on-VM self-compile/run is a tracked follow-up (doc/plan/PORT.md)
     12 #
     13 #   selfhost.sh <selector> [DEPTH]
     14 #
     15 # selector  hosted.sh grammar (mode=selfhost; freestanding is cross-only and
     16 #           rejected). DEPTH: smoke (default) | full (toy corpus through the
     17 #           on-target kit).
     18 #
     19 # env: KIT, KIT_VM (default 1), DEPTH, KIT_SELFHOST_DRYRUN=1 (print the plan).
     20 # Provision-or-error: missing podman / VM / sysroot is a hard error, reported up
     21 # front (provision with `make provision TARGET=…`).
     22 
     23 set -u
     24 
     25 ROOT="$(cd "$(dirname "$0")/.." && pwd)"
     26 KIT="${KIT:-$ROOT/build/kit}"
     27 HOSTED="$ROOT/scripts/hosted.sh"
     28 CASES="$ROOT/test/cross/cases"
     29 BUILD_DIR="$ROOT/build/test/selfhost"
     30 SELECTOR="${1:-${TARGET:-all}}"
     31 DEPTH="${2:-${DEPTH:-smoke}}"
     32 export KIT_VM="${KIT_VM:-1}"
     33 DRYRUN="${KIT_SELFHOST_DRYRUN:-0}"
     34 
     35 mkdir -p "$BUILD_DIR"
     36 # shellcheck source=../test/lib/kit_sh_report.sh
     37 . "$ROOT/test/lib/kit_sh_report.sh"
     38 # exec_vm_supported (provisioning pre-flight) + VM teardown live in the exec seam.
     39 have_podman=0; command -v podman >/dev/null 2>&1 && have_podman=1
     40 export have_podman
     41 # shellcheck source=../test/lib/exec_target.sh
     42 . "$ROOT/test/lib/exec_target.sh"
     43 kit_report_init
     44 trap exec_target_teardown_all EXIT
     45 
     46 tok_os()   { printf '%s' "${1%%-*}"; }
     47 tok_arch() { printf '%s' "${1##*-}"; }
     48 tok_libc() { case "$1" in linux-*) local r="${1#linux-}"; printf '%s' "${r%%-*}" ;; *) printf '' ;; esac; }
     49 
     50 _host_arch_is() {  # canonical arch token -> true if it matches this host
     51   local want="$1" m; m="$(uname -m 2>/dev/null)"
     52   case "$want" in
     53     aa64) [ "$m" = arm64 ] || [ "$m" = aarch64 ] ;;
     54     x64)  [ "$m" = x86_64 ] || [ "$m" = amd64 ] ;;
     55     rv64) [ "$m" = riscv64 ] ;;
     56     *) return 1 ;;
     57   esac
     58 }
     59 
     60 # ---- provisioning pre-flight ----------------------------------------------
     61 preflight_token() {
     62   local t="$1" os arch sr
     63   os="$(tok_os "$t")"; arch="$(tok_arch "$t")"
     64   case "$os" in
     65     macos)
     66       { [ "$(uname -s)" = Darwin ] && _host_arch_is "$arch"; } || \
     67         echo "selfhost macos needs a Darwin host with matching arch" ;;
     68     linux)
     69       [ "$have_podman" = 1 ] || echo "podman required for linux self-host — install podman" ;;
     70     freebsd)
     71       case "$arch" in
     72         aa64|x64) ;;
     73         *) echo "freebsd self-host: only aa64/x64 have a native compiler in the base VM"; return ;;
     74       esac
     75       exec_vm_supported "$("$HOSTED" tag "$t")" || \
     76         echo "freebsd VM not provisioned/reachable — run 'make provision TARGET=$t KIT_VM=1'" ;;
     77     windows)
     78       sr="$("$HOSTED" path "$t" 2>/dev/null)"
     79       { [ -n "$sr" ] && [ -d "$sr" ]; } || \
     80         echo "windows UCRT sysroot missing — run 'make provision TARGET=$t'" ;;
     81   esac
     82 }
     83 
     84 # ---- step runner -----------------------------------------------------------
     85 run_step() {  # label cmd...
     86   local label="$1"; shift
     87   if [ "$DRYRUN" = 1 ]; then printf 'selfhost: PLAN %-28s %s\n' "$label" "$*"; return 0; fi
     88   printf '\nselfhost: step %s →\n' "$label"
     89   if "$@"; then kit_pass "$label"; return 0; else kit_fail "$label" "step failed (see output above)"; return 1; fi
     90 }
     91 
     92 # macOS: after the native bootstrap, prove the self-built stage3 compiles + runs
     93 # a program (the "compile and run on the target" half of self-host).
     94 macos_stage3_hello() {
     95   local s3="$ROOT/build/debug/bootstrap/stage3/kit" d="$BUILD_DIR/macos"
     96   [ -x "$s3" ] || { echo "stage3 kit missing at $s3"; return 1; }
     97   mkdir -p "$d"
     98   # -lc engages kit's hosted profile (SDK/libc discovery), like the bootstrap.
     99   "$s3" cc -lc "$CASES/hello.c" -o "$d/hello" 2>"$d/cc.err" || { sed 's/^/    | /' "$d/cc.err"; return 1; }
    100   "$d/hello" >"$d/out" 2>"$d/err" || return 1
    101   [ "$(cat "$d/out")" = "$(cat "$CASES/hello.stdout")" ]
    102 }
    103 
    104 # ---- dispatch --------------------------------------------------------------
    105 selfhost_token() {
    106   local t="$1" os arch libc toy seed_env
    107   os="$(tok_os "$t")"; arch="$(tok_arch "$t")"; libc="$(tok_libc "$t")"
    108   toy=0; [ "$DEPTH" = full ] && toy=1
    109   seed_env=""
    110   case "$os" in
    111     macos)
    112       if [ "$DEPTH" = full ]; then
    113         run_step "$t:bootstrap+toy" make -C "$ROOT" test-bootstrap-toy
    114       else
    115         run_step "$t:bootstrap" make -C "$ROOT" bootstrap-debug && \
    116           run_step "$t:run" macos_stage3_hello
    117       fi ;;
    118     linux)
    119       # Always bootstrap from a cross-compiled seed built on the host (clang,
    120       # independent of kit), never an in-container clang stage1: there is no
    121       # point exercising clang inside the target environment, and for a non-host
    122       # arch that clang would run emulated. kit_cross builds the stage1 seed
    123       # here; linux_bootstrap then runs only kit's stages 2/3 in the container.
    124       run_step "$t:seed" bash "$ROOT/scripts/kit_cross.sh" "linux-$libc-$arch" --cc=clang || return 1
    125       seed_env="KIT_LINUX_BOOT_SEED=build/kit-cross/clang/linux-$libc-$arch/kit"
    126       run_step "$t:bootstrap" env KIT_LINUX_BOOT_ARCH="$arch" KIT_LINUX_BOOT_TOY="$toy" $seed_env \
    127         bash "$ROOT/scripts/linux_bootstrap.sh" "$libc" both ;;
    128     freebsd)
    129       # Cross-build the stage1 seed on the host (clang), then bootstrap stages
    130       # 2/3 in the VM from it — clang is never run in the VM.
    131       run_step "$t:seed" bash "$ROOT/scripts/kit_cross.sh" "freebsd-$arch" --cc=clang || return 1
    132       run_step "$t:bootstrap" env KIT_FREEBSD_BOOT_TOY="$toy" \
    133         KIT_FREEBSD_BOOT_SEED="build/kit-cross/clang/freebsd-$arch/kit" \
    134         bash "$ROOT/scripts/freebsd_bootstrap.sh" "$arch" both ;;
    135     windows)
    136       run_step "$t:cross-build" bash "$ROOT/scripts/windows_cross.sh" "$arch"
    137       printf 'selfhost: NOTE %s — on-VM self-compile/run is a tracked follow-up (kit.exe cross-built here); see doc/plan/PORT.md\n' "$t" ;;
    138   esac
    139 }
    140 
    141 # ---- drive -----------------------------------------------------------------
    142 # freestanding is cross-only. If the user EXPLICITLY named it, that's an error
    143 # with a clear message; for a wildcard like `all` it is silently dropped by the
    144 # mode filter below.
    145 for it in $(printf '%s' "$SELECTOR" | tr , ' '); do
    146   case "$it" in freestanding|freestanding-*)
    147     echo "selfhost: '$it' has no hosted environment — freestanding is cross-only (use test-cross)" >&2
    148     exit 2 ;;
    149   esac
    150 done
    151 
    152 TOKENS="$("$HOSTED" expand "$SELECTOR" --mode=selfhost)" || exit 1
    153 [ -n "$TOKENS" ] || { echo "selfhost: selector '$SELECTOR' expanded to nothing" >&2; exit 2; }
    154 
    155 printf 'selfhost: DEPTH=%s KIT_VM=%s\nselfhost: targets=%s\n' \
    156   "$DEPTH" "$KIT_VM" "$(echo $TOKENS | tr '\n' ' ')"
    157 
    158 if [ "$DRYRUN" != 1 ]; then
    159   missing=0
    160   for t in $TOKENS; do
    161     reason="$(preflight_token "$t")"
    162     if [ -n "$reason" ]; then printf 'selfhost: NOT PROVISIONED  %-22s %s\n' "$t" "$reason"; missing=1; fi
    163   done
    164   if [ "$missing" = 1 ]; then
    165     echo "selfhost: aborting — provision the targets above, or narrow TARGET / set KIT_VM=0." >&2
    166     exit 1
    167   fi
    168 fi
    169 
    170 for t in $TOKENS; do selfhost_token "$t"; done
    171 
    172 KIT_SKIP_IS_FAILURE=0
    173 kit_summary test-selfhost
    174 kit_exit