kit

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

freebsd_sysroot.sh (6131B)


      1 #!/usr/bin/env bash
      2 # Build FreeBSD cross-compile sysroots from the official base.txz dist sets.
      3 #
      4 # Reproducible and VM-free: downloads (and caches) the base.txz for an arch,
      5 # verifies it against the release MANIFEST, then extracts the curated subset
      6 # kit -- and a clang/lld reference build -- needs. Both the downloaded tarball
      7 # and the extracted sysroot live under the XDG cache so `make clean` does not
      8 # wipe them.
      9 #
     10 # usage:
     11 #   scripts/freebsd_sysroot.sh <arch>|all     extract (cached unless FORCE=1)
     12 #   scripts/freebsd_sysroot.sh path <arch>    print the sysroot dir
     13 #
     14 # arches: amd64|x64 | aarch64|arm64 | riscv64|rv64
     15 #
     16 # env:
     17 #   KIT_FREEBSD_RELEASE         release tag (default: 15.0-RELEASE)
     18 #   KIT_FREEBSD_SYSROOT_CACHE   sysroot extract root (default: $XDG_CACHE_HOME/kit/sysroots/freebsd/<release>)
     19 #   KIT_FREEBSD_DIST_CACHE      download tarball cache (default: $XDG_CACHE_HOME/kit/freebsd-dist/<release>)
     20 #   KIT_FREEBSD_DIST_URL        mirror base (default: https://download.freebsd.org/releases)
     21 #   FORCE=1                     re-download / re-extract even if cached
     22 
     23 set -eu
     24 
     25 RELEASE="${KIT_FREEBSD_RELEASE:-15.0-RELEASE}"
     26 XDG_CACHE_HOME="${XDG_CACHE_HOME:-$HOME/.cache}"
     27 CACHE="${KIT_FREEBSD_SYSROOT_CACHE:-$XDG_CACHE_HOME/kit/sysroots/freebsd/$RELEASE}"
     28 DIST_CACHE="${KIT_FREEBSD_DIST_CACHE:-$XDG_CACHE_HOME/kit/freebsd-dist/$RELEASE}"
     29 MIRROR="${KIT_FREEBSD_DIST_URL:-https://download.freebsd.org/releases}"
     30 
     31 die() { printf 'freebsd-sysroot: %s\n' "$*" >&2; exit 1; }
     32 
     33 canon_arch() {
     34   case "${1:-}" in
     35     amd64|x64|x86_64) echo amd64 ;;
     36     aarch64|arm64|aa64) echo aarch64 ;;
     37     riscv64|rv64) echo riscv64 ;;
     38     *) die "unknown arch '${1:-}'" ;;
     39   esac
     40 }
     41 
     42 # FreeBSD dist layout is <machine>/<machine_arch>/<release>/base.txz.
     43 dist_path() {
     44   case "$(canon_arch "$1")" in
     45     amd64) echo amd64/amd64 ;;
     46     aarch64) echo arm64/aarch64 ;;
     47     riscv64) echo riscv/riscv64 ;;
     48   esac
     49 }
     50 
     51 sysroot_dir() { printf '%s/%s\n' "$CACHE" "$(canon_arch "$1")"; }
     52 
     53 # Curated members pulled out of base.txz. Headers + crt objects + the static
     54 # and shared libc, the FreeBSD-15 libsys split, and the compiler runtime
     55 # (libcompiler_rt / libgcc) whose helpers libc references -- e.g. rv64's
     56 # binary128 __multf3. The libgcc.a / libgcc_s.so symlinks and their targets
     57 # (libcompiler_rt.a, lib/libgcc_s.so.1) are listed together so they resolve.
     58 # Missing optional members (some arches lack a given crt variant) are
     59 # tolerated; a missing libc.a is fatal.
     60 SYSROOT_MEMBERS=(
     61   ./usr/include
     62   ./usr/lib/crt1.o ./usr/lib/crti.o ./usr/lib/crtn.o ./usr/lib/Scrt1.o
     63   ./usr/lib/crtbegin.o ./usr/lib/crtbeginT.o ./usr/lib/crtbeginS.o
     64   ./usr/lib/crtend.o ./usr/lib/crtendS.o
     65   ./usr/lib/libc.a ./usr/lib/libsys.a ./usr/lib/libm.a
     66   ./usr/lib/libssp_nonshared.a ./usr/lib/libc_nonshared.a
     67   ./usr/lib/libcompiler_rt.a ./usr/lib/libgcc.a ./usr/lib/libgcc_eh.a
     68   ./usr/lib/libgcc_s.so
     69   ./usr/lib/libpthread.a ./usr/lib/libthr.a
     70   ./usr/lib/libexecinfo.a ./usr/lib/libexecinfo.so ./usr/lib/libexecinfo.so.1
     71   ./usr/lib/libkvm.a ./usr/lib/libkvm.so
     72   ./usr/lib/libmemstat.a ./usr/lib/libmemstat.so ./usr/lib/libmemstat.so.3
     73   ./usr/lib/libprocstat.a ./usr/lib/libprocstat.so ./usr/lib/libprocstat.so.1
     74   ./usr/lib/libdevstat.a ./usr/lib/libdevstat.so
     75   ./usr/lib/libutil.a ./usr/lib/libutil.so
     76   ./usr/lib/librt.a ./usr/lib/librt.so
     77   ./lib/libc.so.7 ./lib/libsys.so.7 ./lib/libgcc_s.so.1
     78   ./lib/libpthread.so.3 ./lib/libthr.so.3
     79   ./lib/libkvm.so.7 ./lib/libdevstat.so.7 ./lib/libutil.so.10 ./lib/librt.so.1
     80 )
     81 
     82 fetch_txz() {
     83   local arch="$1" dp url txz want got
     84   dp="$(dist_path "$arch")"
     85   url="$MIRROR/$dp/$RELEASE/base.txz"
     86   txz="$DIST_CACHE/$arch-base.txz"
     87   mkdir -p "$DIST_CACHE"
     88   if [ "${FORCE:-0}" = 1 ] || [ ! -f "$txz" ]; then
     89     printf 'download %s\n' "$url" >&2
     90     curl -fL --retry 3 -o "$txz.tmp" "$url" || die "download failed: $url"
     91     mv "$txz.tmp" "$txz"
     92   fi
     93   # Verify against the release MANIFEST (sha256 in column 2). Best effort: if
     94   # the MANIFEST is unreachable we proceed with the cached tarball.
     95   want="$(curl -fsL "$MIRROR/$dp/$RELEASE/MANIFEST" 2>/dev/null \
     96             | awk '$1=="base.txz"{print $2}')"
     97   if [ -n "$want" ]; then
     98     got="$(shasum -a 256 "$txz" | awk '{print $1}')"
     99     [ "$want" = "$got" ] || die "base.txz sha256 mismatch for $arch ($got != $want); rm $txz and retry"
    100     printf 'verified base.txz sha256 %s\n' "$got" >&2
    101   fi
    102   printf '%s\n' "$txz"
    103 }
    104 
    105 build_sysroot() {
    106   local arch dst txz
    107   arch="$(canon_arch "$1")"
    108   dst="$(sysroot_dir "$arch")"
    109   if [ "${FORCE:-0}" != 1 ] && [ -f "$dst/usr/lib/libc.a" ]; then
    110     printf 'sysroot cached: %s\n' "$dst"
    111     return 0
    112   fi
    113   txz="$(fetch_txz "$arch")"
    114   rm -rf "$dst"
    115   mkdir -p "$dst"
    116   printf 'extract sysroot %s -> %s\n' "$arch" "$dst"
    117   # bsdtar/GNU tar both extract named members (and whole dirs); a member absent
    118   # from the archive is a warning + nonzero exit, not a stop -- tolerate it and
    119   # gate on libc.a instead.
    120   tar -xf "$txz" -C "$dst" "${SYSROOT_MEMBERS[@]}" 2>/dev/null || true
    121   [ -f "$dst/usr/lib/libc.a" ] || die "extraction incomplete for $arch (no usr/lib/libc.a)"
    122   # FreeBSD uses libthr as the real thread library; libpthread is a symlink.
    123   # Recreate the symlinks so -lpthread/-lthr resolve correctly.
    124   if [ -f "$dst/lib/libthr.so.3" ]; then
    125     [ -e "$dst/usr/lib/libthr.so" ]    || ln -s ../../lib/libthr.so.3    "$dst/usr/lib/libthr.so"
    126     [ -e "$dst/usr/lib/libpthread.so" ] || ln -s ../../lib/libthr.so.3   "$dst/usr/lib/libpthread.so"
    127   fi
    128   if [ -f "$dst/lib/libpthread.so.3" ]; then
    129     [ -e "$dst/usr/lib/libpthread.so" ] || ln -s ../../lib/libpthread.so.3 "$dst/usr/lib/libpthread.so"
    130   fi
    131   if [ -f "$dst/usr/lib/libthr.a" ] && [ ! -e "$dst/usr/lib/libpthread.a" ]; then
    132     ln -s libthr.a "$dst/usr/lib/libpthread.a"
    133   fi
    134   printf 'sysroot ready: %s\n' "$dst"
    135 }
    136 
    137 cmd="${1:-}"
    138 case "$cmd" in
    139   ""|-h|--help|help)
    140     sed -n '2,24p' "$0" | sed 's/^# \{0,1\}//'
    141     ;;
    142   path)
    143     [ $# -eq 2 ] || die "usage: $0 path <arch>"
    144     sysroot_dir "$2"
    145     ;;
    146   all)
    147     for a in amd64 aarch64 riscv64; do build_sysroot "$a"; done
    148     ;;
    149   *)
    150     build_sysroot "$cmd"
    151     ;;
    152 esac