kit

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

hosted.sh (14960B)


      1 #!/usr/bin/env bash
      2 # scripts/hosted.sh — one front-end for provisioning sysroots and
      3 # cross-compiling+linking against them, across kit's hosted support set. Wraps
      4 # the per-OS provisioners (it does not replace them):
      5 #   FreeBSD  -> scripts/freebsd_sysroot.sh        (base.txz extract)
      6 #   Windows  -> scripts/llvm_mingw_sysroot.sh     (llvm-mingw UCRT)
      7 #   Android  -> scripts/android_ndk_sysroot.sh    (Android NDK sysroot)
      8 #   Linux    -> test/libc/{glibc,musl}/extract.sh (podman container extract)
      9 #   macOS    -> host SDK (native; no cross sysroot)
     10 #
     11 # Targets: <os>[-<libc>]-<arch>   (canonical arch tokens: aa64/x64/rv64[/rv32])
     12 #   linux-glibc-{aa64,x64,rv64}  linux-musl-{aa64,x64,rv64}
     13 #   freebsd-{aa64,x64,rv64}  windows-{aa64,x64}  android-aa64  macos-aa64
     14 #   freestanding-{aa64,x64,rv64,rv32,arm32}   (no OS — cross only, bare-metal exec)
     15 # Long arch spellings (aarch64/amd64/x86_64/riscv64) are accepted as input
     16 # aliases; canon_token normalizes them to the short forms above.
     17 #
     18 # Commands:
     19 #   doctor                     support set + what is provisioned here
     20 #   list [selector]            print expanded canonical tokens, one per line
     21 #   expand <selector> [--mode=cross|selfhost]
     22 #                              like list, but mode-filtered (selfhost drops
     23 #                              freestanding) and KIT_VM-filtered
     24 #   modes <target>             print which modes apply ("cross" / "cross selfhost")
     25 #   prepare <target>|all       provision the sysroot for a target
     26 #   path <target>              print the sysroot dir ("" for macOS/freestanding)
     27 #   triple <target>            print the kit -target triple
     28 #   tag <target>               print the test/lib/exec_target.sh exec tag
     29 #   cc <target> [kit-cc-args]  compile+link: kit cc -target T --sysroot S [...]
     30 #
     31 # Selector grammar (list/expand): all | <os> | linux-<libc> | <full-token> |
     32 # comma-list of any of those. <os> is linux/freebsd/windows/macos/freestanding.
     33 #
     34 # env: KIT (default build/kit), KIT_VM (default 1; 0 drops freebsd/windows from
     35 # expand), plus the per-OS provisioners' own env.
     36 
     37 set -u
     38 
     39 ROOT="$(cd "$(dirname "$0")/.." && pwd)"
     40 KIT="${KIT:-$ROOT/build/kit}"
     41 
     42 SUPPORT_SET_FALLBACK="
     43 linux-glibc-aa64 linux-musl-aa64 freebsd-aa64 android-aa64 freestanding-aa64
     44 linux-glibc-x64 linux-musl-x64 freebsd-x64 freestanding-x64
     45 linux-glibc-rv64 linux-musl-rv64 freebsd-rv64 freestanding-rv64
     46 freestanding-rv32 freestanding-arm32
     47 windows-aa64 windows-x64 macos-aa64 macos-x64
     48 "
     49 
     50 # A built candidate is the authority for selectors/triples.  Keep the literal
     51 # fallback only for bootstrap/provisioning before `make bin`; once `kit
     52 # targets` is available, list/expand/doctor consume its versioned TSV registry.
     53 # iOS/Wasm profiles are intentionally filtered here because this script has no
     54 # provision/run personality for them yet; they remain visible in `kit targets`.
     55 TARGET_REGISTRY_TSV=""
     56 if [ -x "$KIT" ]; then
     57   TARGET_REGISTRY_TSV="$("$KIT" targets --format=tsv 2>/dev/null || true)"
     58 fi
     59 SUPPORT_SET="$SUPPORT_SET_FALLBACK"
     60 if [ -n "$TARGET_REGISTRY_TSV" ]; then
     61   registry_support_set="$(printf '%s\n' "$TARGET_REGISTRY_TSV" | awk -F '\t' '
     62     $1 ~ /^linux-(glibc|musl)-(aa64|x64|rv64)$/ ||
     63     $1 ~ /^freebsd-(aa64|x64|rv64)$/ ||
     64     $1 ~ /^windows-(aa64|x64)$/ ||
     65     $1 == "android-aa64" ||
     66     $1 ~ /^macos-(aa64|x64)$/ ||
     67     $1 ~ /^freestanding-(aa64|x64|rv64|rv32|arm32)$/ { print $1 }
     68   ')"
     69   [ -n "$registry_support_set" ] && SUPPORT_SET="$registry_support_set"
     70 fi
     71 
     72 die() { printf 'hosted: %s\n' "$*" >&2; exit 1; }
     73 
     74 # ---- target parsing --------------------------------------------------------
     75 # Sets globals: T_OS, T_LIBC (linux only), T_ARCH (canonical per-OS token).
     76 parse_target() {
     77   local t="$1" os rest a
     78   os="${t%%-*}"; rest="${t#*-}"
     79   T_OS="$os"; T_LIBC=""; T_ARCH=""
     80   case "$os" in
     81     linux)
     82       T_LIBC="${rest%%-*}"; a="${rest#*-}"
     83       case "$T_LIBC" in glibc|musl) ;; *) die "bad linux libc in '$t' (want glibc|musl)";; esac
     84       case "$a" in
     85         aa64|aarch64|arm64) T_ARCH=aarch64 ;;
     86         x64|x86_64|amd64)   T_ARCH=x64 ;;
     87         rv64|riscv64)       T_ARCH=rv64 ;;
     88         *) die "bad linux arch '$a' in '$t'" ;;
     89       esac ;;
     90     freebsd)
     91       case "$rest" in
     92         amd64|x64|x86_64)   T_ARCH=amd64 ;;
     93         aarch64|arm64|aa64) T_ARCH=aarch64 ;;
     94         riscv64|rv64)       T_ARCH=riscv64 ;;
     95         *) die "bad freebsd arch '$rest' in '$t'" ;;
     96       esac ;;
     97     windows)
     98       case "$rest" in
     99         x64|x86_64|amd64)   T_ARCH=x64 ;;
    100         aarch64|arm64|aa64) T_ARCH=aarch64 ;;
    101         *) die "bad windows arch '$rest' in '$t'" ;;
    102       esac ;;
    103     android)
    104       case "$rest" in
    105         aarch64|arm64|aa64) T_ARCH=aarch64 ;;
    106         *) die "bad android arch '$rest' in '$t' (want aa64)" ;;
    107       esac ;;
    108     macos)
    109       case "$rest" in
    110         aarch64|arm64|aa64) T_ARCH=aarch64 ;;
    111         x64|x86_64|amd64)   T_ARCH=x64 ;;
    112         *) die "bad macos arch '$rest' in '$t' (want aa64|x64)" ;;
    113       esac ;;
    114     freestanding)
    115       case "$rest" in
    116         aarch64|arm64|aa64) T_ARCH=aarch64 ;;
    117         x64|x86_64|amd64)   T_ARCH=x64 ;;
    118         rv64|riscv64)       T_ARCH=rv64 ;;
    119         rv32|riscv32)       T_ARCH=rv32 ;;
    120         arm32|arm|armv7m|armv7em|cortex-m3|cortex-m4|cortex-m7) T_ARCH=arm32 ;;
    121         *) die "bad freestanding arch '$rest' in '$t'" ;;
    122       esac ;;
    123     *) die "unknown os in target '$t'" ;;
    124   esac
    125 }
    126 
    127 triple_of() {
    128   local canonical registry_triple
    129   parse_target "$1"
    130   canonical="$(canon_token "$1")"
    131   if [ -n "$TARGET_REGISTRY_TSV" ]; then
    132     registry_triple="$(printf '%s\n' "$TARGET_REGISTRY_TSV" | awk -F '\t' \
    133       -v selector="$canonical" '$1 == selector { print $2; exit }')"
    134     if [ -n "$registry_triple" ]; then
    135       printf '%s\n' "$registry_triple"
    136       return
    137     fi
    138   fi
    139   case "$T_OS" in
    140     linux)
    141       local suf=gnu; [ "$T_LIBC" = musl ] && suf=musl
    142       case "$T_ARCH" in
    143         aarch64) echo "aarch64-linux-$suf" ;;
    144         x64)     echo "x86_64-linux-$suf" ;;
    145         rv64)    echo "riscv64-linux-$suf" ;;
    146       esac ;;
    147     freebsd)
    148       case "$T_ARCH" in
    149         amd64)   echo x86_64-freebsd ;;
    150         aarch64) echo aarch64-freebsd ;;
    151         riscv64) echo riscv64-freebsd ;;
    152       esac ;;
    153     windows)
    154       case "$T_ARCH" in
    155         x64)     echo x86_64-windows ;;
    156         aarch64) echo aarch64-windows ;;
    157       esac ;;
    158     android)
    159       case "$T_ARCH" in
    160         aarch64) echo aarch64-linux-android21 ;;
    161       esac ;;
    162     macos)
    163       case "$T_ARCH" in
    164         aarch64) echo aarch64-apple-darwin ;;
    165         x64)     echo x86_64-apple-darwin ;;
    166       esac ;;
    167     freestanding)
    168       case "$T_ARCH" in
    169         aarch64) echo aarch64-none-elf ;;
    170         x64)     echo x86_64-none-elf ;;
    171         rv64)    echo riscv64-none-elf ;;
    172         rv32)    echo riscv32-none-elf ;;
    173         arm32)   echo arm-none-eabi ;;
    174       esac ;;
    175   esac
    176 }
    177 
    178 # exec_target tag (test/lib/exec_target.sh). Linux/macOS use aa64/x64/rv64;
    179 # FreeBSD uses amd64/aarch64/riscv64; Windows uses x64/aarch64.
    180 tag_of() {
    181   parse_target "$1"
    182   local ea
    183   case "$T_ARCH" in aarch64) ea=aa64 ;; x64) ea=x64 ;; rv64) ea=rv64 ;; *) ea="$T_ARCH" ;; esac
    184   case "$T_OS" in
    185     linux)   [ "$T_LIBC" = glibc ] && echo "$ea-linux-glibc" || echo "$ea-linux" ;;
    186     macos)   echo "$ea-macos" ;;
    187     freebsd) echo "$T_ARCH-freebsd" ;;
    188     windows) echo "$T_ARCH-windows" ;;
    189     android) echo "$ea-android" ;;
    190     freestanding) echo "$ea-freestanding" ;;
    191   esac
    192 }
    193 
    194 # Canonical token (<os>[-<libc>]-<short-arch>) for any input/alias spelling.
    195 canon_token() {
    196   parse_target "$1"
    197   local ea
    198   case "$T_ARCH" in
    199     aarch64) ea=aa64 ;; x64|amd64) ea=x64 ;; rv64|riscv64) ea=rv64 ;;
    200     rv32) ea=rv32 ;; arm32) ea=arm32 ;; *) ea="$T_ARCH" ;;
    201   esac
    202   case "$T_OS" in
    203     linux) printf 'linux-%s-%s' "$T_LIBC" "$ea" ;;
    204     *)     printf '%s-%s' "$T_OS" "$ea" ;;
    205   esac
    206 }
    207 
    208 # Which modes apply to a target. Freestanding has no hosted environment, so a
    209 # compiler cannot run on it: cross only.
    210 modes_of() {
    211   parse_target "$1"
    212   case "$T_OS" in
    213     freestanding) echo cross ;;
    214     android) echo cross ;;
    215     macos)
    216       # Self-host runs the freshly built kit on the host. A non-host macOS arch
    217       # (e.g. an x86_64 kit on Apple silicon) would run under Rosetta, but that
    218       # path isn't validated yet — so only the host-arch macOS target self-hosts.
    219       local ha; ha="$(uname -m 2>/dev/null)"
    220       case "$T_ARCH:$ha" in
    221         aarch64:arm64|aarch64:aarch64|x64:x86_64|x64:amd64) echo "cross selfhost" ;;
    222         *) echo cross ;;
    223       esac ;;
    224     *) echo "cross selfhost" ;;
    225   esac
    226 }
    227 
    228 _linux_sysroot_dir() { # libc arch
    229   local libc="$1" arch="$2" suf=""
    230   case "$arch" in x64) suf=-x64 ;; rv64) suf=-rv64 ;; esac
    231   printf '%s/sysroots/linux-%s%s' "${XDG_CACHE_HOME:-$HOME/.cache}/kit" "$libc" "$suf"
    232 }
    233 
    234 path_of() {
    235   parse_target "$1"
    236   case "$T_OS" in
    237     linux)   _linux_sysroot_dir "$T_LIBC" "$T_ARCH" ;;
    238     freebsd) "$ROOT/scripts/freebsd_sysroot.sh" path "$T_ARCH" 2>/dev/null ;;
    239     windows) "$ROOT/scripts/llvm_mingw_sysroot.sh" path "$T_ARCH" 2>/dev/null ;;
    240     android) "$ROOT/scripts/android_ndk_sysroot.sh" path "$T_ARCH" 2>/dev/null ;;
    241     macos)   echo "" ;;
    242     freestanding) echo "" ;;
    243   esac
    244 }
    245 
    246 # ---- prepare ---------------------------------------------------------------
    247 prepare_one() {
    248   parse_target "$1"
    249   case "$T_OS" in
    250     linux)
    251       command -v podman >/dev/null 2>&1 || die "podman required for linux sysroots"
    252       bash "$ROOT/test/libc/$T_LIBC/extract.sh" -a "$T_ARCH" ;;
    253     freebsd) "$ROOT/scripts/freebsd_sysroot.sh" "$T_ARCH" ;;
    254     windows) "$ROOT/scripts/llvm_mingw_sysroot.sh" prepare "$T_ARCH" ;;
    255     android) "$ROOT/scripts/android_ndk_sysroot.sh" prepare "$T_ARCH" ;;
    256     macos)   printf 'macos: native host SDK, nothing to prepare\n' ;;
    257     freestanding)
    258       printf 'freestanding: no sysroot to provision; run-time prereq is qemu-system-%s\n' \
    259         "$(triple_of "$1" | sed 's/-none-elf//')" ;;
    260   esac
    261 }
    262 
    263 # ---- cc --------------------------------------------------------------------
    264 # cc <target> [extra kit cc args...]  — adds -target, --sysroot, and any
    265 # OS-mandatory flags; everything else (sources, -o, -O, -static, ...) passes
    266 # through. macOS compiles native (no -target/--sysroot).
    267 cc_target() {
    268   local target="$1"; shift
    269   parse_target "$target"
    270   [ -x "$KIT" ] || die "kit not found at $KIT (run 'make bin')"
    271   local triple sysroot; triple="$(triple_of "$target")"; sysroot="$(path_of "$target")"
    272   local args=()
    273   if [ "$T_OS" = macos ]; then
    274     # Kit discovers and applies the native Xcode/CLT SDK itself. A native-arch
    275     # build omits -target so the host default applies; a cross to the other
    276     # macOS arch (e.g. x86_64 on Apple silicon, run via Rosetta) needs only the
    277     # explicit target. No SDK-discovery helper is part of this path.
    278     local ha; ha="$(uname -m 2>/dev/null)"
    279     case "$T_ARCH:$ha" in
    280       x64:arm64|x64:aarch64|aarch64:x86_64|aarch64:amd64) args+=(-target "$triple") ;;
    281     esac
    282   else
    283     args+=(-target "$triple")
    284     [ -n "$sysroot" ] && args+=(--sysroot "$sysroot")
    285     [ "$T_OS" = windows ] && args+=(-mconsole)   # console exit-code semantics
    286   fi
    287   exec "$KIT" cc "${args[@]}" "$@"
    288 }
    289 
    290 # ---- doctor ----------------------------------------------------------------
    291 doctor() {
    292   printf 'host: %s/%s\n' "$(uname -s 2>/dev/null)" "$(uname -m 2>/dev/null)"
    293   printf 'kit:  %s%s\n' "$KIT" "$([ -x "$KIT" ] && echo '' || echo ' (missing)')"
    294   printf 'support set (sysroot provisioned?):\n'
    295   local t sr ok
    296   for t in $SUPPORT_SET; do
    297     sr="$(path_of "$t")"
    298     case "$t" in
    299       macos-*)        ok='native' ;;
    300       freestanding-*) ok='bare (qemu-system)' ;;
    301       *) if [ -n "$sr" ] && [ -d "$sr" ]; then ok='yes'; else ok='no'; fi ;;
    302     esac
    303     printf '  %-22s triple=%-22s tag=%-16s sysroot=%s\n' \
    304       "$t" "$(triple_of "$t")" "$(tag_of "$t")" "$ok"
    305   done
    306 }
    307 
    308 # ---- selector expansion ----------------------------------------------------
    309 # _match_one SELECTOR-ITEM -> space-separated canonical tokens (no filters).
    310 _match_one() {
    311   local it="$1" t
    312   case "$it" in
    313     linux|freebsd|windows|android|macos|freestanding)
    314       for t in $SUPPORT_SET; do case "$t" in "$it"-*) printf '%s ' "$t" ;; esac; done
    315       return ;;
    316     linux-musl|linux-glibc)
    317       for t in $SUPPORT_SET; do case "$t" in "$it"-*) printf '%s ' "$t" ;; esac; done
    318       return ;;
    319   esac
    320   local c x; c="$(canon_token "$it")" || die "bad target '$it'"
    321   for x in $SUPPORT_SET; do
    322     [ "$x" = "$c" ] && { printf '%s ' "$c"; return; }
    323   done
    324   die "unknown target '$it' (not in support set; see '$0 list')"
    325 }
    326 
    327 # _expand_raw SELECTOR -> deduped canonical tokens (commas -> spaces; no
    328 # filters). Returns non-zero (so callers can `|| die`) on an unknown token —
    329 # _match_one's die exits only its own subshell, so we check each item's status.
    330 _expand_raw() {
    331   local sel item m raw="" t out=""
    332   sel="$(printf '%s' "${1:-all}" | tr , ' ')"
    333   for item in $sel; do
    334     if [ "$item" = all ]; then raw="$raw $SUPPORT_SET"; continue; fi
    335     m="$(_match_one "$item")" || return 1
    336     raw="$raw $m"
    337   done
    338   for t in $raw; do case " $out " in *" $t "*) ;; *) out="$out $t" ;; esac; done
    339   printf '%s' "${out# }"
    340 }
    341 
    342 # cmd_expand SELECTOR [--mode=cross|selfhost] -> filtered tokens, one per line.
    343 # selfhost mode drops freestanding; KIT_VM=0 drops freebsd/windows.
    344 cmd_expand() {
    345   local sel="" mode="" a t toks
    346   for a in "$@"; do case "$a" in --mode=*) mode="${a#--mode=}" ;; *) sel="$a" ;; esac; done
    347   toks="$(_expand_raw "$sel")" || exit 1
    348   for t in $toks; do
    349     case "$t" in freebsd-*|windows-*) [ "${KIT_VM:-1}" = 0 ] && continue ;; esac
    350     # selfhost mode keeps only targets whose modes_of advertises selfhost. This
    351     # drops freestanding (cross-only) and a non-host-arch macOS target, whose
    352     # self-host runs the built kit under emulation and isn't validated yet.
    353     if [ "$mode" = selfhost ]; then
    354       case " $(modes_of "$t") " in *" selfhost "*) ;; *) continue ;; esac
    355     fi
    356     printf '%s\n' "$t"
    357   done
    358 }
    359 
    360 cmd="${1:-}"
    361 case "$cmd" in
    362   doctor) doctor ;;
    363   list)   shift; toks="$(_expand_raw "${1:-all}")" || exit 1; for t in $toks; do printf '%s\n' "$t"; done ;;
    364   expand) shift; [ $# -ge 1 ] || die "usage: $0 expand <selector> [--mode=cross|selfhost]"; cmd_expand "$@" ;;
    365   modes)  [ $# -eq 2 ] || die "usage: $0 modes <target>"; modes_of "$2" ;;
    366   prepare)
    367     [ $# -ge 2 ] || die "usage: $0 prepare <selector>|all"
    368     shift
    369     toks="$(_expand_raw "$1")" || exit 1
    370     for t in $toks; do prepare_one "$t"; done ;;
    371   path)   [ $# -eq 2 ] || die "usage: $0 path <target>"; path_of "$2" ;;
    372   triple) [ $# -eq 2 ] || die "usage: $0 triple <target>"; triple_of "$2" ;;
    373   tag)    [ $# -eq 2 ] || die "usage: $0 tag <target>"; tag_of "$2" ;;
    374   cc)     [ $# -ge 2 ] || die "usage: $0 cc <target> [kit-cc-args]"; shift; cc_target "$@" ;;
    375   -h|--help|help|"") sed -n '2,40p' "$0" | sed 's/^# \{0,1\}//' ;;
    376   *) die "unknown command '$cmd' (try: doctor list expand modes prepare path triple tag cc)" ;;
    377 esac