kit

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

check_rv64_env.sh (12054B)


      1 #!/usr/bin/env bash
      2 # test/lib/check_rv64_env.sh — kit rv64 "doctor".
      3 #
      4 # Quick prerequisite check for the rv64 lane of the test harness. Each
      5 # checked tool/feature is reported as a one-liner with status (OK / MISSING /
      6 # UNUSABLE), what was looked for, and how to install/fix it.
      7 #
      8 # Usage:
      9 #   bash test/lib/check_rv64_env.sh         # run all checks, exit 0 if at
     10 #                                           # least one runner is available
     11 #                                           # AND the cross-compile toolchain
     12 #                                           # is usable. Exit 1 otherwise.
     13 #
     14 # Or source it from a harness:
     15 #   source test/lib/check_rv64_env.sh
     16 #   check_rv64_env                          # populates the RV64_ENV_* globals
     17 #                                           # below and prints the summary.
     18 #   rv64_runner_summary                     # one-line "ready" / "blocked: ..."
     19 #   classify_podman_rv64_error <stderr_file>
     20 #                                           # echoes a one-line diagnostic
     21 #                                           # categorizing a podman failure.
     22 #
     23 # After check_rv64_env returns, these globals are set:
     24 #   RV64_HAVE_CLANG_TARGET   0/1 — clang accepts --target=riscv64-linux-gnu
     25 #   RV64_HAVE_LLD            0/1 — ld.lld on PATH
     26 #   RV64_HAVE_QEMU           0/1 — qemu-riscv64{,-static} on PATH
     27 #   RV64_QEMU_BIN            path or empty
     28 #   RV64_HAVE_PODMAN         0/1 — podman on PATH (and not forced off)
     29 #   RV64_HAVE_NATIVE         0/1 — host is riscv64 Linux
     30 #   RV64_HAVE_ANY_RUNNER     0/1 — at least one of native/qemu/podman
     31 #   RV64_HAVE_CROSS          0/1 — clang rv64 + ld.lld both usable
     32 #   RV64_READY               0/1 — runner + cross both OK
     33 #
     34 # Honors these env knobs:
     35 #   KIT_FORCE_NO_PODMAN=1   pretend podman is missing (for diagnostic
     36 #                             dry-runs). Reported in the summary.
     37 #
     38 # Install hints are deliberately tied to the detected host OS so the
     39 # message a contributor sees is actionable on their box.
     40 
     41 # ---- platform install hints ------------------------------------------------
     42 
     43 _rv64_os_tag() {
     44     case "$(uname -s 2>/dev/null)" in
     45         Darwin) echo darwin ;;
     46         Linux)
     47             if [ -r /etc/os-release ]; then
     48                 . /etc/os-release
     49                 case "${ID:-}:${ID_LIKE:-}" in
     50                     *alpine*) echo alpine ;;
     51                     *debian*|ubuntu:*|*:*debian*) echo debian ;;
     52                     *fedora*|*rhel*|*:*rhel*|*:*fedora*) echo fedora ;;
     53                     *arch*|*:*arch*) echo arch ;;
     54                     *) echo linux ;;
     55                 esac
     56             else
     57                 echo linux
     58             fi
     59             ;;
     60         *) echo other ;;
     61     esac
     62 }
     63 
     64 _rv64_hint_qemu() {
     65     case "$(_rv64_os_tag)" in
     66         darwin) echo "use podman, or run qemu-user inside a Linux VM" ;;
     67         debian) echo "apt install qemu-user-static" ;;
     68         fedora) echo "dnf install qemu-user-static" ;;
     69         alpine) echo "apk add qemu-riscv64" ;;
     70         arch)   echo "pacman -S qemu-user-static-binfmt" ;;
     71         *)      echo "install a qemu-user package that provides qemu-riscv64" ;;
     72     esac
     73 }
     74 
     75 _rv64_hint_clang() {
     76     case "$(_rv64_os_tag)" in
     77         darwin) echo "brew install llvm (and add it to PATH)" ;;
     78         debian) echo "apt install clang lld" ;;
     79         fedora) echo "dnf install clang lld" ;;
     80         alpine) echo "apk add clang lld" ;;
     81         arch)   echo "pacman -S clang lld" ;;
     82         *)      echo "install a clang build that includes RISC-V" ;;
     83     esac
     84 }
     85 
     86 _rv64_hint_lld() {
     87     case "$(_rv64_os_tag)" in
     88         darwin) echo "brew install lld" ;;
     89         debian) echo "apt install lld" ;;
     90         fedora) echo "dnf install lld" ;;
     91         alpine) echo "apk add lld" ;;
     92         arch)   echo "pacman -S lld" ;;
     93         *)      echo "install ld.lld (LLVM linker)" ;;
     94     esac
     95 }
     96 
     97 _rv64_hint_podman_riscv64() {
     98     case "$(_rv64_os_tag)" in
     99         darwin) echo "ensure 'podman machine' is running and the VM has qemu-user binfmt for riscv64 (try 'podman machine start')" ;;
    100         linux|debian|fedora|alpine|arch) echo "register binfmt for riscv64 (e.g. 'docker run --privileged --rm tonistiigi/binfmt --install riscv64')" ;;
    101         *) echo "register binfmt riscv64 in podman's runtime environment" ;;
    102     esac
    103 }
    104 
    105 # ---- colors (degrade gracefully) -------------------------------------------
    106 
    107 if [ -t 1 ] && [ -z "${NO_COLOR:-}" ]; then
    108     _rv64_grn() { printf '\033[32m%s\033[0m' "$1"; }
    109     _rv64_red() { printf '\033[31m%s\033[0m' "$1"; }
    110     _rv64_yel() { printf '\033[33m%s\033[0m' "$1"; }
    111 else
    112     _rv64_grn() { printf '%s' "$1"; }
    113     _rv64_red() { printf '%s' "$1"; }
    114     _rv64_yel() { printf '%s' "$1"; }
    115 fi
    116 
    117 _rv64_ok()    { printf '  [%s] %s\n' "$(_rv64_grn ok)"   "$1"; }
    118 _rv64_miss()  { printf '  [%s] %s — %s\n' "$(_rv64_red MISSING)" "$1" "$2"; }
    119 _rv64_warn()  { printf '  [%s] %s — %s\n' "$(_rv64_yel WARN)"    "$1" "$2"; }
    120 
    121 # ---- individual probes -----------------------------------------------------
    122 
    123 _rv64_probe_clang() {
    124     RV64_HAVE_CLANG_TARGET=0
    125     if ! command -v clang >/dev/null 2>&1; then
    126         _rv64_miss "clang" "no 'clang' on PATH (install: $(_rv64_hint_clang))"
    127         return
    128     fi
    129     # Use -march=rv64gc so we catch builds that have the triple parser but
    130     # no RISC-V backend wired in.
    131     local err
    132     err="$(clang --target=riscv64-linux-gnu -march=rv64gc \
    133                  -c -x c - -o /dev/null </dev/null 2>&1)"
    134     if [ $? -eq 0 ]; then
    135         RV64_HAVE_CLANG_TARGET=1
    136         _rv64_ok "clang --target=riscv64-linux-gnu"
    137     else
    138         # Two distinct failure modes that we surface differently:
    139         #   - "error: unknown target triple"  → clang built without RISC-V
    140         #   - everything else                 → something else broke
    141         if printf '%s' "$err" | grep -q "unknown target"; then
    142             _rv64_miss "clang RISC-V backend" \
    143                 "clang accepts the triple but lacks RISC-V (install: $(_rv64_hint_clang))"
    144         else
    145             _rv64_miss "clang --target=riscv64-linux-gnu" \
    146                 "clang rejects the target ($(printf '%s' "$err" | head -1)). Install: $(_rv64_hint_clang)"
    147         fi
    148     fi
    149 }
    150 
    151 _rv64_probe_lld() {
    152     RV64_HAVE_LLD=0
    153     if command -v ld.lld >/dev/null 2>&1; then
    154         RV64_HAVE_LLD=1
    155         _rv64_ok "ld.lld (ELF cross-link)"
    156     else
    157         _rv64_miss "ld.lld" "not on PATH — needed to link rv64 ELF (install: $(_rv64_hint_lld))"
    158     fi
    159 }
    160 
    161 _rv64_probe_qemu() {
    162     RV64_HAVE_QEMU=0
    163     RV64_QEMU_BIN=""
    164     local bin
    165     bin="$(command -v qemu-riscv64-static 2>/dev/null \
    166         || command -v qemu-riscv64 2>/dev/null \
    167         || true)"
    168     if [ -n "$bin" ]; then
    169         RV64_HAVE_QEMU=1
    170         RV64_QEMU_BIN="$bin"
    171         _rv64_ok "qemu-riscv64 user-mode emulator ($bin)"
    172     else
    173         if [ "$(_rv64_os_tag)" = "darwin" ]; then
    174             _rv64_miss "qemu-riscv64" \
    175                 "not on PATH; macOS/Homebrew qemu provides system emulators only ($(_rv64_hint_qemu))"
    176         else
    177             _rv64_miss "qemu-riscv64" \
    178                 "not on PATH (install: $(_rv64_hint_qemu))"
    179         fi
    180     fi
    181 }
    182 
    183 _rv64_probe_podman() {
    184     RV64_HAVE_PODMAN=0
    185     if [ "${KIT_FORCE_NO_PODMAN:-0}" = "1" ]; then
    186         _rv64_warn "podman" "disabled via KIT_FORCE_NO_PODMAN=1"
    187         return
    188     fi
    189     if command -v podman >/dev/null 2>&1; then
    190         RV64_HAVE_PODMAN=1
    191         _rv64_ok "podman ($(command -v podman))"
    192     else
    193         _rv64_miss "podman" \
    194             "not on PATH. Install your platform's podman package, then ensure binfmt riscv64 is registered ($(_rv64_hint_podman_riscv64))"
    195     fi
    196 }
    197 
    198 _rv64_probe_native() {
    199     RV64_HAVE_NATIVE=0
    200     if [ "$(uname -s 2>/dev/null)" = "Linux" ] && \
    201        [ "$(uname -m 2>/dev/null)" = "riscv64" ]; then
    202         RV64_HAVE_NATIVE=1
    203         _rv64_ok "native riscv64 host (kernel can exec rv64 ELF directly)"
    204     fi
    205     # No "MISSING" line — native rv64 is one of several mutually
    206     # acceptable runners, not a strict prereq.
    207 }
    208 
    209 # ---- public entry points ---------------------------------------------------
    210 
    211 check_rv64_env() {
    212     printf 'kit rv64 environment check (host: %s/%s)\n' \
    213         "$(uname -s 2>/dev/null)" "$(uname -m 2>/dev/null)"
    214     _rv64_probe_clang
    215     _rv64_probe_lld
    216     _rv64_probe_native
    217     _rv64_probe_qemu
    218     _rv64_probe_podman
    219 
    220     RV64_HAVE_ANY_RUNNER=0
    221     if [ "${RV64_HAVE_NATIVE:-0}" -eq 1 ] || \
    222        [ "${RV64_HAVE_QEMU:-0}" -eq 1 ] || \
    223        [ "${RV64_HAVE_PODMAN:-0}" -eq 1 ]; then
    224         RV64_HAVE_ANY_RUNNER=1
    225     fi
    226     RV64_HAVE_CROSS=0
    227     if [ "${RV64_HAVE_CLANG_TARGET:-0}" -eq 1 ] && \
    228        [ "${RV64_HAVE_LLD:-0}" -eq 1 ]; then
    229         RV64_HAVE_CROSS=1
    230     fi
    231     RV64_READY=0
    232     if [ "$RV64_HAVE_ANY_RUNNER" -eq 1 ] && [ "$RV64_HAVE_CROSS" -eq 1 ]; then
    233         RV64_READY=1
    234     fi
    235     printf '\nSummary: %s\n' "$(rv64_runner_summary)"
    236 }
    237 
    238 rv64_runner_summary() {
    239     local runners="" blocked=""
    240     [ "${RV64_HAVE_NATIVE:-0}" -eq 1 ] && runners="${runners}native "
    241     [ "${RV64_HAVE_QEMU:-0}"   -eq 1 ] && runners="${runners}qemu-riscv64 "
    242     [ "${RV64_HAVE_PODMAN:-0}" -eq 1 ] && runners="${runners}podman "
    243     [ "${RV64_HAVE_CLANG_TARGET:-0}" -eq 0 ] && blocked="${blocked}clang-rv64 "
    244     [ "${RV64_HAVE_LLD:-0}"          -eq 0 ] && blocked="${blocked}ld.lld "
    245     [ "${RV64_HAVE_ANY_RUNNER:-0}"   -eq 0 ] && blocked="${blocked}no-runner "
    246     if [ "${RV64_READY:-0}" -eq 1 ]; then
    247         printf 'READY (runners: %s)' "${runners% }"
    248     elif [ -z "$runners" ] && [ -n "$blocked" ]; then
    249         printf 'BLOCKED (missing: %s)' "${blocked% }"
    250     else
    251         printf 'BLOCKED (have: %s; missing: %s)' \
    252             "${runners:-none}" "${blocked:-none}"
    253     fi
    254 }
    255 
    256 # Classify a podman stderr capture into a single-line diagnostic.
    257 # Reads from the file path passed in $1. Always echoes one line and
    258 # returns 0 — the caller picks how to render it.
    259 classify_podman_rv64_error() {
    260     local f="$1"
    261     local body=""
    262     [ -f "$f" ] && body="$(cat "$f" 2>/dev/null)"
    263     # Lowercase for matching; some podman messages vary slightly.
    264     local lc; lc="$(printf '%s' "$body" | tr '[:upper:]' '[:lower:]')"
    265 
    266     # Most common first: binfmt / qemu not registered in the podman VM
    267     # (or on Linux host). Manifests as "exec format error" or the
    268     # qemu_riscv64-binfmt magic line missing.
    269     if printf '%s' "$lc" | grep -qE "exec format error|no such file or directory.*qemu"; then
    270         printf 'podman cannot exec riscv64 ELF: binfmt/qemu not registered in podman VM. Fix: %s\n' \
    271             "$(_rv64_hint_podman_riscv64)"
    272         return 0
    273     fi
    274     # Wrong-arch cached image — podman happily ran an amd64/arm64
    275     # busybox/alpine and the rv64 ELF then died with exec format.
    276     if printf '%s' "$lc" | grep -qE "image platform .* does not match|no matching manifest"; then
    277         printf 'podman image manifest has no riscv64 variant (or cached image is wrong arch). Fix: re-pull with --platform linux/riscv64 (e.g. podman pull --platform linux/riscv64 alpine:latest)\n'
    278         return 0
    279     fi
    280     # Registry unreachable on first pull.
    281     if printf '%s' "$lc" | grep -qE "no such host|connection refused|i/o timeout|tls handshake timeout|temporary failure in name resolution"; then
    282         printf 'podman cannot reach the registry to pull a riscv64 image. Fix: check network / proxy, or pre-pull the image while online\n'
    283         return 0
    284     fi
    285     # podman machine not running (Darwin).
    286     if printf '%s' "$lc" | grep -qE "cannot connect to podman|connection refused.*podman.sock|machine .* is not running"; then
    287         printf 'podman machine is not running. Fix: podman machine start\n'
    288         return 0
    289     fi
    290     # Generic fallthrough.
    291     local first; first="$(printf '%s' "$body" | head -1)"
    292     printf 'podman riscv64 run failed: %s\n' "${first:-unknown error}"
    293 }
    294 
    295 # When invoked directly (not sourced), run the doctor and use its READY
    296 # flag to set the exit code.
    297 if [ "${BASH_SOURCE[0]:-$0}" = "$0" ]; then
    298     check_rv64_env
    299     [ "${RV64_READY:-0}" -eq 1 ] || exit 1
    300     exit 0
    301 fi