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