freebsd_bootstrap.sh (6964B)
1 #!/usr/bin/env bash 2 # Run the three-stage self-build (mk/bootstrap.mk) for a FreeBSD arch natively 3 # in the corresponding FreeBSD VM, from a macOS dev host. 4 # 5 # The bootstrap is a NATIVE build: `make bootstrap` keys off the VM's own uname 6 # (HOST_OS=freebsd + aarch64), so it selects the FreeBSD ELF toolchain and 7 # reaches its fixed point entirely inside the VM. No cross-compilation happens. 8 # 9 # The VM must be prepared (scripts/freebsd_vm.sh prepare <arch>) but does not 10 # need to be running -- this script starts it, waits for SSH, runs the build, 11 # and shuts it down (unless KIT_FREEBSD_VM_KEEP_UP=1). 12 # 13 # usage: scripts/freebsd_bootstrap.sh [arch] [chain] 14 # arch aarch64 (default; the only FreeBSD arch that has a native compiler 15 # installed in the base VM image today) 16 # chain both (default) | debug (-O0) | release (-O1) 17 # 18 # Env overrides: 19 # KIT_FREEBSD_VM_KEEP_UP=1 leave the VM running after the build 20 # KIT_FREEBSD_BOOT_TOY=1 also run the Toy corpus through the stage3 compiler 21 # KIT_FREEBSD_BOOT_SEED repo-relative path to a pre-built stage1 kit. When 22 # set, stage1 is this host-cross-compiled seed 23 # instead of an in-VM clang build (so clang is never 24 # exercised in the VM); only kit's stages 2/3 run 25 # there. Build one with: 26 # scripts/kit_cross.sh freebsd-<arch> --cc=clang 27 28 set -eu 29 30 ROOT="$(cd "$(dirname "$0")/.." && pwd)" 31 ARCH="${1:-aarch64}" 32 CHAIN="${2:-both}" 33 34 case "$ARCH" in 35 aarch64|arm64|aa64) ARCH=aarch64 ;; 36 amd64|x64|x86_64) ARCH=amd64 ;; 37 *) echo "freebsd_bootstrap: unsupported arch '$ARCH'" >&2; exit 2 ;; 38 esac 39 40 case "$CHAIN" in 41 both) TARGET="bootstrap" ;; 42 debug) TARGET="bootstrap-debug" ;; 43 release) TARGET="bootstrap-release" ;; 44 *) echo "freebsd_bootstrap: unknown chain '$CHAIN' (want both|debug|release)" >&2; exit 2 ;; 45 esac 46 47 KEEP_UP="${KIT_FREEBSD_VM_KEEP_UP:-0}" 48 TOY="${KIT_FREEBSD_BOOT_TOY:-0}" 49 SEED="${KIT_FREEBSD_BOOT_SEED:-}" 50 BUILD_DIR="build/freebsd-boot/$ARCH" 51 52 if [ -n "$SEED" ]; then 53 [ -x "$ROOT/$SEED" ] || { echo "freebsd_bootstrap: seed not found/executable: $ROOT/$SEED (build with scripts/kit_cross.sh freebsd-$ARCH --cc=clang)" >&2; exit 2; } 54 fi 55 56 # Hybrid (seeded) vs in-VM clang stage1. A seed feeds mk/bootstrap.mk a 57 # host-cross-compiled stage1, so clang is never run in the VM. 58 if [ -n "$SEED" ]; then 59 BUILD_LINE="\$MAKE $TARGET BUILD_DIR='$BUILD_DIR' BOOTSTRAP_SEED='/home/kit/work/$SEED' MAKE=\"\$MAKE\"" 60 CC_BANNER="echo \"stage1 seed: $SEED\"; /home/kit/work/$SEED --help >/dev/null 2>&1 && echo 'seed runs' || echo 'seed FAILED to run'" 61 else 62 BUILD_LINE="\$MAKE $TARGET BUILD_DIR='$BUILD_DIR' CC=clang AR=ar MAKE=\"\$MAKE\"" 63 CC_BANNER="clang --version | head -1" 64 fi 65 66 ssh_vm() { 67 scripts/freebsd_vm.sh ssh "$ARCH" "$@" 68 } 69 70 # Ensure VM is running. 71 vm_started=0 72 if ! scripts/freebsd_vm.sh ssh "$ARCH" true 2>/dev/null; then 73 printf 'freebsd_bootstrap: starting %s VM\n' "$ARCH" 74 scripts/freebsd_vm.sh run "$ARCH" & 75 scripts/freebsd_vm.sh wait-ssh "$ARCH" 76 vm_started=1 77 fi 78 79 # Send the source tree to the VM. Ship the working-tree content of every tracked 80 # file (not `git archive HEAD`, which only sees committed state) so an 81 # in-progress bootstrap test reflects uncommitted changes across the whole tree, 82 # not just Makefile/mk/. git ls-files keeps build/ and other untracked/ignored 83 # artifacts out -- the VM builds into /home/kit/work/build/. 84 # 85 # COPYFILE_DISABLE=1 stops macOS bsdtar from archiving each file's extended 86 # attributes (every checkout carries a `com.apple.provenance` xattr) as an 87 # AppleDouble `._<name>` pax sidecar. macOS tar merges those back on listing so 88 # they look absent here, but FreeBSD's tar extracts them as literal `._*.c` 89 # files, which the Makefile's `*.c` globs then try (and fail) to compile. 90 printf 'freebsd_bootstrap: sending source tree to %s VM\n' "$ARCH" 91 ssh_vm 'rm -rf /home/kit/work && mkdir -p /home/kit/work' 92 git -C "$ROOT" ls-files -z | (cd "$ROOT" && COPYFILE_DISABLE=1 tar --null -T - -cf -) \ 93 | ssh_vm 'tar -C /home/kit/work -xf -' 94 95 # The seed lives under build/ (gitignored), so it is not in the source tar — 96 # stream it into the VM separately. 97 if [ -n "$SEED" ]; then 98 printf 'freebsd_bootstrap: sending stage1 seed to %s VM\n' "$ARCH" 99 ssh_vm "mkdir -p /home/kit/work/$(dirname "$SEED")" 100 ssh_vm "cat > /home/kit/work/$SEED && chmod +x /home/kit/work/$SEED" < "$ROOT/$SEED" 101 fi 102 103 printf 'freebsd_bootstrap: running %s bootstrap on %s\n' "$TARGET" "$ARCH" 104 105 ssh_vm sh <<EOF 106 set -eu 107 cd /home/kit/work 108 echo "=== freebsd_bootstrap: \$(uname -m) FreeBSD / $TARGET ===" 109 $CC_BANNER 110 # FreeBSD ships BSD make as 'make'; the kit Makefile requires GNU make (gmake). 111 MAKE=\$(which gmake 2>/dev/null || which make) 112 # Capture the bootstrap result without aborting the script: a chain that fails 113 # (e.g. the -O1 release link) should still let the Toy corpus run through the 114 # stage3 of any chain that did reach its fixed point. 115 boot_rc=0 116 $BUILD_LINE || boot_rc=\$? 117 if [ "$TOY" = "1" ]; then 118 # test/toy/run.sh needs bash (it uses arrays); the FreeBSD base system ships 119 # only the POSIX /bin/sh, so a bash package must be installed (pkg install 120 # bash). Skip with a clear note rather than fail cryptically when it is not. 121 TOYSH=\$(which bash 2>/dev/null || true) 122 if [ -z "\$TOYSH" ]; then 123 echo "=== Toy corpus: SKIPPED (bash not installed; run 'pkg install bash') ===" 124 else 125 for m in debug release; do 126 s3="$BUILD_DIR/\$m/bootstrap/stage3/kit" 127 [ -x "\$s3" ] || continue 128 echo "=== Toy corpus through \$m stage3 ===" 129 KIT="\$(pwd)/\$s3" "\$TOYSH" test/toy/run.sh || true 130 done 131 fi 132 fi 133 echo "=== freebsd_bootstrap: DONE $TARGET (bootstrap rc=\$boot_rc) ===" 134 exit \$boot_rc 135 EOF 136 137 # Bring back the stage3 binaries for inspection. 138 STAGE3_OUT="$ROOT/build/freebsd-boot-$ARCH" 139 mkdir -p "$STAGE3_OUT" 140 for m in debug release; do 141 remote="$BUILD_DIR/$m/bootstrap/stage3/kit" 142 if ssh_vm test -f "/home/kit/work/$remote" 2>/dev/null; then 143 scripts/freebsd_vm.sh scp "$ARCH" \ 144 "/home/kit/work/$remote" /dev/null 2>/dev/null || true 145 # Use scp_arch directly for a real local copy. 146 local_out="$STAGE3_OUT/kit.$m" 147 port=$(scripts/freebsd_vm.sh ssh "$ARCH" 'echo $SSH_CLIENT' 2>/dev/null | awk '{print $2}' || echo 2223) 148 port=2223 149 SSH_KEY="$(ls "$HOME/.cache/kit/freebsd-vm"/*/ssh/id_ed25519 2>/dev/null | head -1)" 150 if [ -f "$SSH_KEY" ]; then 151 scp -P "$port" -i "$SSH_KEY" \ 152 -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o LogLevel=ERROR \ 153 "kit@127.0.0.1:/home/kit/work/$remote" "$local_out" 2>/dev/null || true 154 [ -f "$local_out" ] && printf 'stage3 (%s): %s\n' "$m" "$local_out" 155 fi 156 fi 157 done 158 159 if [ "$KEEP_UP" != "1" ] && [ "$vm_started" = "1" ]; then 160 printf 'freebsd_bootstrap: stopping VM\n' 161 ssh_vm 'sudo shutdown -p now' 2>/dev/null || true 162 fi