kit_cross.sh (7323B)
1 #!/usr/bin/env bash 2 # scripts/kit_cross.sh — build a runnable `kit` binary for a hosted support-set 3 # target, using either clang (independent toolchain, clang + lld end to end) or 4 # kit itself (dogfood) as the cross-compiler. The general form of the old 5 # windows_cross.sh; see doc/plan/PORT.md. 6 # 7 # kit_cross.sh <target> [--cc=clang|kit] [--verify] 8 # 9 # target a hosted token: linux-{glibc,musl}-{aa64,x64,rv64} | freebsd-{aa64, 10 # x64,rv64} | windows-{aa64,x64} | macos-aa64. (freestanding is excluded 11 # — kit needs an OS to run on.) 12 # --cc toolchain backend (default: kit). 13 # --verify after building, run the cross-built kit on the target via the exec 14 # seam (kit --version) to confirm it executes there. 15 # 16 # Output: build/kit-cross/<toolchain>/<target>/kit[.exe] 17 # 18 # env: KIT (default build/kit), KIT_CROSS_OPT (codegen opt, default -O0). 19 20 set -eu 21 22 ROOT="$(cd "$(dirname "$0")/.." && pwd)" 23 cd "$ROOT" 24 HOSTED="$ROOT/scripts/hosted.sh" 25 KIT="${KIT:-$ROOT/build/kit}" 26 27 die() { printf 'kit_cross: %s\n' "$*" >&2; exit 1; } 28 29 TARGET=""; TC="kit"; VERIFY=0 30 for a in "$@"; do 31 case "$a" in 32 --cc=*) TC="${a#--cc=}" ;; 33 --verify) VERIFY=1 ;; 34 -*) die "unknown flag '$a'" ;; 35 *) TARGET="$a" ;; 36 esac 37 done 38 [ -n "$TARGET" ] || die "usage: kit_cross.sh <target> [--cc=clang|kit] [--verify]" 39 case "$TC" in clang|kit) ;; *) die "bad --cc '$TC' (want clang|kit)" ;; esac 40 41 # ---- resolve target -------------------------------------------------------- 42 os="${TARGET%%-*}" 43 case "$os" in freestanding) die "$TARGET is freestanding — kit needs an OS to run on" ;; esac 44 triple_kit="$("$HOSTED" triple "$TARGET")" || die "bad target '$TARGET'" 45 sysroot="$("$HOSTED" path "$TARGET" 2>/dev/null || true)" 46 # macos is the degenerate native case: hosted.sh returns no sysroot, so the 47 # "sysroot" is the host SDK (both backends consume it: clang via -isysroot, kit 48 # via --sysroot). kit's internal OS tag for macOS is `darwin`, not `macos`. 49 hos="$os" 50 if [ "$os" = macos ]; then 51 hos=darwin 52 [ -n "$sysroot" ] || sysroot="$(xcrun --show-sdk-path 2>/dev/null || true)" 53 fi 54 arch_tok="${TARGET##*-}" 55 case "$arch_tok" in 56 aa64|aarch64) harch=aarch64; carch=aarch64 ;; 57 x64|x86_64|amd64) harch=x86_64; carch=x86_64 ;; 58 rv64|riscv64) harch=rv64; carch=riscv64 ;; 59 *) die "bad arch in '$TARGET'" ;; 60 esac 61 62 # clang target triple (differs from kit's for windows: mingw gnu). 63 clang_triple() { 64 case "$os" in 65 linux) echo "$triple_kit" ;; # carch-linux-{gnu,musl} 66 freebsd) echo "${carch}-unknown-freebsd${KIT_FREEBSD_CLANG_VER:-14}" ;; 67 windows) echo "${carch}-w64-windows-gnu" ;; 68 macos) echo "" ;; # native 69 esac 70 } 71 72 BUILD_DIR="build/kit-cross/$TC/$TARGET" 73 TCDIR="$ROOT/$BUILD_DIR/toolchain" 74 mkdir -p "$TCDIR" 75 76 # ---- toolchain wrappers ---------------------------------------------------- 77 if [ "$TC" = kit ]; then 78 [ -x "$KIT" ] || die "kit not found at $KIT (run 'make bin')" 79 cat > "$TCDIR/cc" <<EOF 80 #!/bin/sh 81 exec "$KIT" cc -target $triple_kit ${sysroot:+--sysroot "$sysroot"} "\$@" 82 EOF 83 chmod +x "$TCDIR/cc" 84 for t in ar ranlib as ld; do ln -sf "$KIT" "$TCDIR/$t"; done 85 CC_W="$TCDIR/cc"; AR_W="$TCDIR/ar" 86 else 87 command -v clang >/dev/null 2>&1 || die "clang not found" 88 ct="$(clang_triple)" 89 if [ "$os" = macos ]; then 90 sdk="$(xcrun --show-sdk-path 2>/dev/null || true)" 91 # -arch selects the slice (x86_64 cross-builds on an arm64 host run under 92 # Rosetta); kit's `aarch64` tag is Apple's `arm64`. Without this the wrapper 93 # silently builds the native slice while env.mk selects HOST_ARCH sources. 94 case "$carch" in 95 aarch64) maarch=arm64 ;; 96 x86_64) maarch=x86_64 ;; 97 *) die "bad macos arch '$carch'" ;; 98 esac 99 cat > "$TCDIR/cc" <<EOF 100 #!/bin/sh 101 exec clang -arch $maarch ${sdk:+-isysroot "$sdk"} "\$@" 102 EOF 103 else 104 command -v ld.lld >/dev/null 2>&1 || command -v lld >/dev/null 2>&1 || die "lld not found (needed for clang cross-link)" 105 [ -n "$sysroot" ] && [ -d "$sysroot" ] || die "sysroot missing for $TARGET — run 'make provision TARGET=$TARGET'" 106 extra="" 107 if [ "$os" = windows ]; then 108 # llvm-mingw's runtime is compiler-rt + libunwind (not libgcc), so select 109 # them explicitly and compose a resource dir = host clang's headers + the 110 # sysroot's staged compiler-rt builtins (lib/windows). These are toolchain 111 # runtime-selection flags, not source workarounds (the windows sources are 112 # plain-clang-clean). 113 [ -d "$sysroot/lib/windows" ] || \ 114 die "windows sysroot lacks compiler-rt builtins (lib/windows) — re-run 'make windows-ucrt-sysroots'" 115 RD="$TCDIR/clang-rt"; mkdir -p "$RD/lib" 116 ln -sfn "$(clang -print-resource-dir)/include" "$RD/include" 117 ln -sfn "$sysroot/lib/windows" "$RD/lib/windows" 118 extra="-rtlib=compiler-rt -unwindlib=libunwind -resource-dir $RD" 119 fi 120 cat > "$TCDIR/cc" <<EOF 121 #!/bin/sh 122 exec clang --target=$ct --sysroot "$sysroot" -fuse-ld=lld $extra -Wno-unused-command-line-argument "\$@" 123 EOF 124 fi 125 chmod +x "$TCDIR/cc" 126 CC_W="$TCDIR/cc" 127 AR_W="$(command -v llvm-ar 2>/dev/null || command -v ar)" 128 fi 129 130 # ---- build ----------------------------------------------------------------- 131 # kit cc emits no -MMD deps, so wipe the object tree (keep the toolchain) for a 132 # correct full rebuild; harmless for the clang backend too. 133 find "$BUILD_DIR" -mindepth 1 -maxdepth 1 ! -name toolchain -exec rm -rf {} + 2>/dev/null || true 134 JOBS="$(getconf _NPROCESSORS_ONLN 2>/dev/null || echo 4)" 135 136 printf 'kit_cross: building kit for %s via %s (triple=%s)\n' "$TARGET" "$TC" \ 137 "$([ "$TC" = clang ] && clang_triple || echo "$triple_kit")" 138 139 # RELEASE=1 enables -ffunction-sections/-fdata-sections; dead-section GC then 140 # drops the dead sections target headers' unused static inlines pull in. -O0 141 # codegen + asserts on (HOST_MODE_CPPFLAGS=) keeps the cross-build fast but 142 # checked. The sysroot rides the cc wrapper, so env.mk's host -isysroot is 143 # cleared. The GC flag is linker-specific: GNU/lld (and kit ld) take 144 # --gc-sections; Apple ld (clang on darwin) takes -dead_strip. 145 GC_LDFLAG="-Wl,--gc-sections" 146 if [ "$TC" = clang ] && [ "$os" = macos ]; then GC_LDFLAG="-Wl,-dead_strip"; fi 147 make bin -j"$JOBS" \ 148 BUILD_DIR="$BUILD_DIR" \ 149 HOST_OS="$hos" HOST_ARCH="$harch" \ 150 RELEASE=1 HOST_OPTFLAGS="${KIT_CROSS_OPT:--O0}" HOST_MODE_CPPFLAGS= \ 151 HOST_MODE_LDFLAGS="$GC_LDFLAG" \ 152 HOST_SYSROOT_CFLAGS= HOST_SYSROOT_LDFLAGS= \ 153 KIT_UPDATE_INDEX_URL="${KIT_UPDATE_INDEX_URL:-https://example.invalid/kit-development.index}" \ 154 KIT_RELEASE_PUBKEYS="${KIT_RELEASE_PUBKEYS:-test/dist/keys/nonrelease.pub}" \ 155 KIT_RELEASE_ALLOW_TEST_KEY="${KIT_RELEASE_ALLOW_TEST_KEY:-1}" \ 156 CC="$CC_W" AR="$AR_W" 157 158 OUT="$BUILD_DIR/kit" 159 if [ "$os" = windows ]; then 160 # Both clang and kit auto-append .exe to a -o output that has no extension for 161 # a windows target, so the linker writes kit.exe directly (no copy needed). 162 OUT="$BUILD_DIR/kit.exe" 163 fi 164 printf 'kit_cross: built %s\n' "$OUT" 165 file "$OUT" 2>/dev/null || true 166 167 # ---- verify (optional): run the cross-built kit on the target -------------- 168 if [ "$VERIFY" = 1 ]; then 169 printf 'kit_cross: verifying %s runs on %s\n' "$OUT" "$TARGET" 170 bash "$ROOT/scripts/kit_cross_verify.sh" "$TARGET" "$OUT" 171 fi