#!/usr/bin/env bash # ============================================================================= # iPXE build script # # Usage: # ./build.sh [OPTIONS] # # Options: # -p, --platform Platform to build (default: efi-x64) # -e, --embed iPXE script to embed into binary (default: none) # -s, --script iPXE script to include as autoexec.ipxe in image # -i, --image Create a bootable FAT32 .img alongside the binary # -o, --output Output directory (default: ./build) # -j, --jobs Parallel jobs (default: nproc) # -h, --help Show this help # # Platforms: # efi-x64 x86-64 UEFI EFI application [bin-x86_64-efi/ipxe.efi] # efi-ia32 32-bit UEFI EFI application [bin-i386-efi/ipxe.efi] # efi-arm64 AArch64 UEFI EFI application [bin-arm64-efi/ipxe.efi] # snp-x64 x86-64 SNP-only EFI binary [bin-x86_64-efi/snponly.efi] # bios x86 legacy BIOS PXE [bin/undionly.kpxe] # # Examples: # ./build.sh # ./build.sh -p efi-x64 -s boot.ipxe -i # ./build.sh -p efi-x64 -e boot.ipxe # ./build.sh -p bios -o ./out -j 8 # ============================================================================= set -euo pipefail # ── Defaults ────────────────────────────────────────────────────────────────── PLATFORM="efi-x64" EMBED="" SCRIPT="" MAKE_IMAGE=0 OUTPUT_DIR="$(dirname "$0")/build" JOBS="$(nproc 2>/dev/null || sysctl -n hw.ncpu 2>/dev/null || echo 4)" SRC_DIR="$(cd "$(dirname "$0")/src" && pwd)" # ── Colour helpers ───────────────────────────────────────────────────────────── if [ -t 1 ]; then C_BOLD='\033[1m'; C_GREEN='\033[0;32m'; C_CYAN='\033[0;36m' C_YELLOW='\033[0;33m'; C_RED='\033[0;31m'; C_RESET='\033[0m' else C_BOLD=''; C_GREEN=''; C_CYAN=''; C_YELLOW=''; C_RED=''; C_RESET='' fi info() { echo -e "${C_CYAN}▶${C_RESET} $*"; } ok() { echo -e "${C_GREEN}✔${C_RESET} $*"; } warn() { echo -e "${C_YELLOW}⚠${C_RESET} $*"; } die() { echo -e "${C_RED}✘${C_RESET} $*" >&2; exit 1; } # ── Argument parsing ─────────────────────────────────────────────────────────── usage() { sed -n '3,22p' "$0" | sed 's/^# \{0,1\}//' exit 0 } while [[ $# -gt 0 ]]; do case $1 in -p|--platform) PLATFORM="$2"; shift 2 ;; -e|--embed) EMBED="$2"; shift 2 ;; -s|--script) SCRIPT="$2"; shift 2 ;; -i|--image) MAKE_IMAGE=1; shift ;; -o|--output) OUTPUT_DIR="$2"; shift 2 ;; -j|--jobs) JOBS="$2"; shift 2 ;; -h|--help) usage ;; *) die "Unknown option: $1" ;; esac done # ── Platform → make target ───────────────────────────────────────────────────── case "$PLATFORM" in efi-x64) MAKE_TARGET="bin-x86_64-efi/ipxe.efi"; OUTPUT_NAME="ipxe-x64.efi" EFI_NAME="BOOTX64.EFI" ;; efi-ia32) MAKE_TARGET="bin-i386-efi/ipxe.efi"; OUTPUT_NAME="ipxe-ia32.efi" EFI_NAME="BOOTIA32.EFI" ;; efi-arm64) MAKE_TARGET="bin-arm64-efi/ipxe.efi"; OUTPUT_NAME="ipxe-arm64.efi" EFI_NAME="BOOTAA64.EFI" ;; snp-x64) MAKE_TARGET="bin-x86_64-efi/snponly.efi"; OUTPUT_NAME="snponly-x64.efi" EFI_NAME="BOOTX64.EFI" ;; bios) MAKE_TARGET="bin/undionly.kpxe"; OUTPUT_NAME="undionly.kpxe" EFI_NAME="" ;; *) die "Unknown platform: '$PLATFORM'. Run with --help for valid platforms." ;; esac # ── Validate inputs ──────────────────────────────────────────────────────────── [[ -d "$SRC_DIR" ]] || die "src/ not found at $SRC_DIR" EMBED_ABS="" if [[ -n "$EMBED" ]]; then EMBED_ABS="$(realpath "$EMBED")" || die "Embed script not found: $EMBED" [[ -f "$EMBED_ABS" ]] || die "Embed script not found: $EMBED" fi SCRIPT_ABS="" if [[ -n "$SCRIPT" ]]; then SCRIPT_ABS="$(realpath "$SCRIPT")" || die "Script not found: $SCRIPT" [[ -f "$SCRIPT_ABS" ]] || die "Script not found: $SCRIPT" fi if [[ $MAKE_IMAGE -eq 1 ]]; then [[ -n "$EFI_NAME" ]] || die "--image is not supported for bios platform" command -v mformat >/dev/null || die "mtools not installed (apt install mtools)" command -v mcopy >/dev/null || die "mtools not installed (apt install mtools)" fi # ── Summary ──────────────────────────────────────────────────────────────────── mkdir -p "$OUTPUT_DIR" echo -e "${C_BOLD}Platform:${C_RESET} $PLATFORM → $MAKE_TARGET" echo -e "${C_BOLD}Jobs:${C_RESET} $JOBS" [[ -n "$EMBED_ABS" ]] && echo -e "${C_BOLD}Embed:${C_RESET} $EMBED_ABS" [[ -n "$SCRIPT_ABS" ]] && echo -e "${C_BOLD}Script:${C_RESET} $SCRIPT_ABS (autoexec.ipxe)" echo -e "${C_BOLD}Output:${C_RESET} $OUTPUT_DIR/$OUTPUT_NAME" [[ $MAKE_IMAGE -eq 1 ]] && echo -e "${C_BOLD}Image:${C_RESET} $OUTPUT_DIR/${OUTPUT_NAME%.efi}.img" echo "" # ── Build ────────────────────────────────────────────────────────────────────── info "Building…" if [[ -n "$EMBED_ABS" ]]; then make -C "$SRC_DIR" "$MAKE_TARGET" "EMBED=$EMBED_ABS" -j "$JOBS" else make -C "$SRC_DIR" "$MAKE_TARGET" -j "$JOBS" fi # ── Copy binary ──────────────────────────────────────────────────────────────── BUILT_BIN="$SRC_DIR/$MAKE_TARGET" [[ -f "$BUILT_BIN" ]] || die "Build finished but output not found: $BUILT_BIN" cp "$BUILT_BIN" "$OUTPUT_DIR/$OUTPUT_NAME" ok "Binary: $OUTPUT_DIR/$OUTPUT_NAME" # ── Create bootable image ────────────────────────────────────────────────────── if [[ $MAKE_IMAGE -eq 1 ]]; then IMG="$OUTPUT_DIR/${OUTPUT_NAME%.efi}.img" IMG_SIZE_MB=16 info "Creating FAT32 image ($IMG_SIZE_MB MB)…" # Blank image dd if=/dev/zero of="$IMG" bs=1M count=$IMG_SIZE_MB status=none # Format as FAT32 mformat -i "$IMG" -F -v iPXE :: # Create EFI/BOOT directory structure mmd -i "$IMG" ::EFI mmd -i "$IMG" ::EFI/BOOT # Copy EFI binary as the standard boot filename mcopy -i "$IMG" "$OUTPUT_DIR/$OUTPUT_NAME" "::EFI/BOOT/$EFI_NAME" # Copy autoexec script if provided if [[ -n "$SCRIPT_ABS" ]]; then mcopy -i "$IMG" "$SCRIPT_ABS" "::EFI/BOOT/autoexec.ipxe" ok "Script: EFI/BOOT/autoexec.ipxe" fi ok "Image: $IMG" echo "" echo -e " Flash to USB: ${C_BOLD}sudo dd if=$IMG of=/dev/sdX bs=4M status=progress${C_RESET}" fi