#!/usr/bin/env bash # # build.sh -- Compile the sketch using arduino-cli # # Reads all settings from .anvil.toml. No Anvil binary required. # # Usage: # ./build.sh Compile (verify only) # ./build.sh --board mega Use a named board # ./build.sh --clean Delete build cache first # ./build.sh --verbose Show full compiler output # # Prerequisites: arduino-cli in PATH, arduino:avr core installed # Install: anvil setup (or manually: arduino-cli core install arduino:avr) set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" CONFIG="$SCRIPT_DIR/.anvil.toml" # -- Colors ---------------------------------------------------------------- if [[ -t 1 ]]; then RED=$'\033[0;31m'; GRN=$'\033[0;32m'; YLW=$'\033[0;33m' CYN=$'\033[0;36m'; BLD=$'\033[1m'; RST=$'\033[0m' else RED=''; GRN=''; YLW=''; CYN=''; BLD=''; RST='' fi ok() { echo "${GRN}ok${RST} $*"; } warn() { echo "${YLW}warn${RST} $*"; } die() { echo "${RED}FAIL${RST} $*" >&2; exit 1; } # -- Parse .anvil.toml ----------------------------------------------------- [[ -f "$CONFIG" ]] || die "No .anvil.toml found in $SCRIPT_DIR" toml_get() { (grep "^$1 " "$CONFIG" 2>/dev/null || true) | head -1 | sed 's/.*= *"\{0,1\}\([^"]*\)"\{0,1\}/\1/' | tr -d ' ' } toml_array() { (grep "^$1 " "$CONFIG" 2>/dev/null || true) | head -1 \ | sed 's/.*\[//; s/\].*//; s/"//g; s/,/ /g' | tr -s ' ' } toml_section_get() { local section="$1" key="$2" awk -v section="[$section]" -v key="$key" ' $0 == section { found=1; next } /^\[/ { found=0 } found && $1 == key && /=/ { sub(/^[^=]*= *"?/, ""); sub(/"? *$/, ""); print; exit } ' "$CONFIG" } SKETCH_NAME="$(toml_get 'name')" DEFAULT_BOARD="$(toml_get 'default')" WARNINGS="$(toml_get 'warnings')" INCLUDE_DIRS="$(toml_array 'include_dirs')" EXTRA_FLAGS="$(toml_array 'extra_flags')" [[ -n "$SKETCH_NAME" ]] || die "Could not read project name from .anvil.toml" [[ -n "$DEFAULT_BOARD" ]] || die "Could not read default board from .anvil.toml" SKETCH_DIR="$SCRIPT_DIR/$SKETCH_NAME" BUILD_DIR="$SCRIPT_DIR/.build" # -- Parse arguments ------------------------------------------------------- DO_CLEAN=0 VERBOSE="" BOARD_NAME="" while [[ $# -gt 0 ]]; do case "$1" in --board) BOARD_NAME="$2"; shift 2 ;; --clean) DO_CLEAN=1; shift ;; --verbose) VERBOSE="--verbose"; shift ;; -h|--help) echo "Usage: ./build.sh [--board NAME] [--clean] [--verbose]" echo " Compiles the sketch. Settings from .anvil.toml." echo " --board NAME selects a board from [boards.NAME]." exit 0 ;; *) die "Unknown option: $1" ;; esac done # -- Resolve board --------------------------------------------------------- ACTIVE_BOARD="${BOARD_NAME:-$DEFAULT_BOARD}" if [[ -z "$ACTIVE_BOARD" ]]; then echo "${RED}FAIL${RST} No default board set in .anvil.toml." >&2 echo "" >&2 echo " Add a default to the [build] section of .anvil.toml:" >&2 echo " default = \"uno\"" >&2 echo "" >&2 echo " And make sure a matching [boards.uno] section exists:" >&2 echo " [boards.uno]" >&2 echo " fqbn = \"arduino:avr:uno\"" >&2 echo "" >&2 echo " Or with Anvil: anvil board --default uno" >&2 echo " List boards: anvil board --listall" >&2 echo " arduino-cli board listall" >&2 exit 1 fi FQBN="$(toml_section_get "boards.$ACTIVE_BOARD" "fqbn")" if [[ -z "$FQBN" ]]; then echo "${RED}FAIL${RST} No [boards.$ACTIVE_BOARD] section in .anvil.toml." >&2 echo "" >&2 echo " Add it to .anvil.toml:" >&2 echo " [boards.$ACTIVE_BOARD]" >&2 echo " fqbn = \"arduino:avr:uno\" # replace with your board" >&2 echo "" >&2 echo " Or with Anvil: anvil board --add $ACTIVE_BOARD" >&2 echo " List boards: anvil board --listall" >&2 echo " arduino-cli board listall" >&2 exit 1 fi if [[ -n "$BOARD_NAME" ]]; then ok "Using board: $BOARD_NAME ($FQBN)" fi # -- Preflight ------------------------------------------------------------- command -v arduino-cli &>/dev/null \ || die "arduino-cli not found in PATH. Install it first." [[ -d "$SKETCH_DIR" ]] \ || die "Sketch directory not found: $SKETCH_DIR" [[ -f "$SKETCH_DIR/$SKETCH_NAME.ino" ]] \ || die "Sketch file not found: $SKETCH_DIR/$SKETCH_NAME.ino" # -- Clean ----------------------------------------------------------------- if [[ $DO_CLEAN -eq 1 ]] && [[ -d "$BUILD_DIR" ]]; then echo "${YLW}Cleaning build cache...${RST}" rm -rf "$BUILD_DIR" ok "Cache cleared." fi # -- Build include flags --------------------------------------------------- BUILD_FLAGS="" for dir in $INCLUDE_DIRS; do abs="$SCRIPT_DIR/$dir" if [[ -d "$abs" ]]; then BUILD_FLAGS="$BUILD_FLAGS -I$abs" else warn "Include directory not found: $dir" fi done for flag in $EXTRA_FLAGS; do BUILD_FLAGS="$BUILD_FLAGS $flag" done # -- Compile --------------------------------------------------------------- echo "${CYN}${BLD}Compiling ${SKETCH_NAME}...${RST}" echo " Board: $FQBN" echo " Sketch: $SKETCH_DIR" echo "" mkdir -p "$BUILD_DIR" COMPILE_ARGS=( compile --fqbn "$FQBN" --build-path "$BUILD_DIR" --warnings "$WARNINGS" ) if [[ -n "$BUILD_FLAGS" ]]; then COMPILE_ARGS+=(--build-property "compiler.cpp.extra_flags=$BUILD_FLAGS") COMPILE_ARGS+=(--build-property "compiler.c.extra_flags=$BUILD_FLAGS") fi if [[ -n "$VERBOSE" ]]; then COMPILE_ARGS+=("$VERBOSE") fi COMPILE_ARGS+=("$SKETCH_DIR") arduino-cli "${COMPILE_ARGS[@]}" || die "Compilation failed." echo "" ok "Compile succeeded." # -- Binary size ----------------------------------------------------------- ELF="$BUILD_DIR/$SKETCH_NAME.ino.elf" if [[ -f "$ELF" ]] && command -v avr-size &>/dev/null; then echo "" avr-size --mcu=atmega328p -C "$ELF" fi echo ""