many eda additions
This commit is contained in:
256
tools/kicad-build
Executable file
256
tools/kicad-build
Executable file
@@ -0,0 +1,256 @@
|
||||
#!/usr/bin/env bash
|
||||
# kicad-build — orchestrator for circuit_toolkit + kicad-cli.
|
||||
#
|
||||
# Usage:
|
||||
# ./kicad-build <board-dir> [--erc] [--drc] [--gerber] [--bom] [--schematic] [--sim] [--all]
|
||||
#
|
||||
# Steps (all are optional, --all runs everything):
|
||||
# 1. circuit_toolkit build (generates .kicad_sch + .kicad_pcb)
|
||||
# 2. kicad-cli sch erc (electrical rules check)
|
||||
# 3. kicad-cli pcb drc (design rules check)
|
||||
# 4. kicad-cli pcb export gerber/drill/position
|
||||
# 5. kicad-cli sch export bom/pdf/netlist
|
||||
# 6. SPICE simulation (if sim/ present)
|
||||
#
|
||||
# Requires:
|
||||
# - KiCad.app installed (for pcbnew)
|
||||
# - kicad-cli on PATH (homebrew: brew install kicad)
|
||||
# - ngspice on PATH (for --sim, brew install ngspice)
|
||||
# - netlistsvg on PATH (for --schematic SVG, npm install -g netlistsvg)
|
||||
# - circuit_toolkit installed into KiCad's Python (run install-kicad-toolkit.sh)
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||
KICAD_PYTHON="$SCRIPT_DIR/kicad-python"
|
||||
KICAD_CLI=$(command -v kicad-cli 2>/dev/null || echo "")
|
||||
|
||||
# circuit_toolkit paths
|
||||
TOOLKIT_SRC="$PROJECT_ROOT/kicad-claude-toolkit/python/circuit_toolkit"
|
||||
TOOLKIT_PY="$TOOLKIT_SRC/circuit_toolkit"
|
||||
|
||||
# Output directory (relative to board dir)
|
||||
OUTPUT_DIR=""
|
||||
|
||||
usage() {
|
||||
echo "Usage: $0 <board-dir> [--erc] [--drc] [--gerber] [--bom] [--schematic] [--sim] [--all]"
|
||||
echo ""
|
||||
echo " --erc Run ERC on generated schematic"
|
||||
echo " --drc Run DRC on generated PCB"
|
||||
echo " --gerber Export Gerber + drill + position files"
|
||||
echo " --bom Generate BOM (flat + JLCPCB)"
|
||||
echo " --schematic Generate schematic SVG via netlistsvg"
|
||||
echo " --sim Run SPICE simulations (if sim/ present)"
|
||||
echo " --all Run all steps (default)"
|
||||
echo " --dry-run Print planned commands without executing"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Parse args
|
||||
BOARD_DIR=""
|
||||
DO_ERC=false
|
||||
DO_DRC=false
|
||||
DO_GERBER=false
|
||||
DO_BOM=false
|
||||
DO_SCHEMATIC=false
|
||||
DO_SIM=false
|
||||
DO_ALL=true
|
||||
DRY_RUN=false
|
||||
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
--erc) DO_ERC=true; DO_ALL=false;;
|
||||
--drc) DO_DRC=true; DO_ALL=false;;
|
||||
--gerber) DO_GERBER=true; DO_ALL=false;;
|
||||
--bom) DO_BOM=true; DO_ALL=false;;
|
||||
--schematic) DO_SCHEMATIC=true; DO_ALL=false;;
|
||||
--sim) DO_SIM=true; DO_ALL=false;;
|
||||
--dry-run) DRY_RUN=true;;
|
||||
-h|--help) usage;;
|
||||
-*)
|
||||
echo "Unknown option: $arg" >&2
|
||||
usage;;
|
||||
*)
|
||||
if [ -z "$BOARD_DIR" ]; then
|
||||
BOARD_DIR="$arg"
|
||||
else
|
||||
echo "Unexpected argument: $arg" >&2
|
||||
usage
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [ -z "$BOARD_DIR" ]; then
|
||||
echo "ERROR: board directory required" >&2
|
||||
usage
|
||||
fi
|
||||
|
||||
BOARD_DIR="$(cd "$BOARD_DIR" && pwd)"
|
||||
OUTPUT_DIR="$BOARD_DIR/output"
|
||||
|
||||
if $DO_ALL; then
|
||||
DO_ERC=true; DO_DRC=true; DO_GERBER=true; DO_BOM=true; DO_SCHEMATIC=true
|
||||
fi
|
||||
|
||||
# ── Pre-flight checks ──────────────────────────────────────────────────
|
||||
check_kicad_python() {
|
||||
if ! $DRY_RUN && ! bash "$KICAD_PYTHON" -c "import pcbnew" 2>/dev/null; then
|
||||
echo "ERROR: pcbnew not available. Run KiCad.app at least once, then retry." >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
check_kicad_cli() {
|
||||
if [ -z "$KICAD_CLI" ]; then
|
||||
echo "ERROR: kicad-cli not on PATH. Install via: brew install kicad" >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# ── Step 1: circuit_toolkit build ──────────────────────────────────────
|
||||
step_build() {
|
||||
local build_script="$BOARD_DIR/build.py"
|
||||
if [ ! -f "$build_script" ]; then
|
||||
echo "SKIP build: no build.py in $BOARD_DIR"
|
||||
return 0
|
||||
fi
|
||||
echo "=== Step 1: circuit_toolkit build ==="
|
||||
if $DRY_RUN; then
|
||||
echo " [dry-run] KiCad Python $KICAD_PYTHON"
|
||||
echo " [dry-run] cd $BOARD_DIR && $KICAD_PYTHON build.py"
|
||||
return 0
|
||||
fi
|
||||
mkdir -p "$OUTPUT_DIR"
|
||||
pushd "$BOARD_DIR" >/dev/null
|
||||
PYTHONPATH="$TOOLKIT_SRC:$PYTHONPATH" \
|
||||
bash "$KICAD_PYTHON" build.py
|
||||
popd >/dev/null
|
||||
echo " -> $OUTPUT_DIR/"
|
||||
}
|
||||
|
||||
# ── Step 2: ERC ────────────────────────────────────────────────────────
|
||||
step_erc() {
|
||||
local sch="$BOARD_DIR/*.kicad_sch"
|
||||
sch=$(ls $sch 2>/dev/null | head -1 || true)
|
||||
if [ -z "$sch" ]; then
|
||||
echo "SKIP ERC: no .kicad_sch found"
|
||||
return 0
|
||||
fi
|
||||
echo "=== Step 2: ERC ==="
|
||||
if $DRY_RUN; then
|
||||
echo " [dry-run] $KICAD_CLI sch erc $sch"
|
||||
return 0
|
||||
fi
|
||||
mkdir -p "$OUTPUT_DIR/erc"
|
||||
$KICAD_CLI sch erc "$sch" --output "$OUTPUT_DIR/erc/erc_report.txt" 2>&1 || true
|
||||
echo " -> $OUTPUT_DIR/erc/"
|
||||
}
|
||||
|
||||
# ── Step 3: DRC ────────────────────────────────────────────────────────
|
||||
step_drc() {
|
||||
local pcb="$BOARD_DIR/*.kicad_pcb"
|
||||
pcb=$(ls $pcb 2>/dev/null | head -1 || true)
|
||||
if [ -z "$pcb" ]; then
|
||||
echo "SKIP DRC: no .kicad_pcb found"
|
||||
return 0
|
||||
fi
|
||||
echo "=== Step 3: DRC ==="
|
||||
if $DRY_RUN; then
|
||||
echo " [dry-run] $KICAD_CLI pcb drc $pcb"
|
||||
return 0
|
||||
fi
|
||||
mkdir -p "$OUTPUT_DIR/drc"
|
||||
$KICAD_CLI pcb drc "$pcb" --output "$OUTPUT_DIR/drc/drc_report.html" 2>&1 || true
|
||||
echo " -> $OUTPUT_DIR/drc/"
|
||||
}
|
||||
|
||||
# ── Step 4: Gerber/Drill/Position export ──────────────────────────────
|
||||
step_gerber() {
|
||||
local pcb="$BOARD_DIR/*.kicad_pcb"
|
||||
pcb=$(ls $pcb 2>/dev/null | head -1 || true)
|
||||
if [ -z "$pcb" ]; then
|
||||
echo "SKIP gerber: no .kicad_pcb found"
|
||||
return 0
|
||||
fi
|
||||
echo "=== Step 4: Gerber + Drill + Position export ==="
|
||||
if $DRY_RUN; then
|
||||
echo " [dry-run] $KICAD_CLI pcb export gerbers $pcb --output $OUTPUT_DIR/fab/gerber"
|
||||
return 0
|
||||
fi
|
||||
mkdir -p "$OUTPUT_DIR/fab/gerber"
|
||||
$KICAD_CLI pcb export gerbers "$pcb" --output "$OUTPUT_DIR/fab/gerber" 2>&1
|
||||
$KICAD_CLI pcb export drill "$pcb" --output "$OUTPUT_DIR/fab/gerber" 2>&1
|
||||
$KICAD_CLI pcb export pos "$pcb" --output "$OUTPUT_DIR/fab/positions.csv" 2>&1
|
||||
echo " -> $OUTPUT_DIR/fab/"
|
||||
}
|
||||
|
||||
# ── Step 5: BOM + schematic PDF + netlist ─────────────────────────────
|
||||
step_bom() {
|
||||
local sch="$BOARD_DIR/*.kicad_sch"
|
||||
sch=$(ls $sch 2>/dev/null | head -1 || true)
|
||||
if [ -z "$sch" ]; then
|
||||
echo "SKIP bom: no .kicad_sch found"
|
||||
return 0
|
||||
fi
|
||||
echo "=== Step 5: BOM + schematic PDF + netlist ==="
|
||||
if $DRY_RUN; then
|
||||
echo " [dry-run] $KICAD_CLI sch export bom $sch --output $OUTPUT_DIR/fab"
|
||||
return 0
|
||||
fi
|
||||
mkdir -p "$OUTPUT_DIR/fab"
|
||||
$KICAD_CLI sch export bom "$sch" --output "$OUTPUT_DIR/fab" 2>&1
|
||||
$KICAD_CLI sch export pdf "$sch" --output "$OUTPUT_DIR/schematic.pdf" 2>&1
|
||||
$KICAD_CLI sch export "$sch" --format netlist --output "$OUTPUT_DIR/fab/netlist.xml" 2>&1
|
||||
echo " -> $OUTPUT_DIR/fab/"
|
||||
}
|
||||
|
||||
# ── Step 6: SPICE simulation ──────────────────────────────────────────
|
||||
step_sim() {
|
||||
if [ ! -d "$BOARD_DIR/sim" ]; then
|
||||
echo "SKIP sim: no sim/ directory"
|
||||
return 0
|
||||
fi
|
||||
echo "=== Step 6: SPICE simulation ==="
|
||||
if $DRY_RUN; then
|
||||
echo " [dry-run] ngspice -b $BOARD_DIR/sim/*.cir"
|
||||
return 0
|
||||
fi
|
||||
if ! command -v ngspice &>/dev/null; then
|
||||
echo "SKIP sim: ngspice not on PATH (brew install ngspice)"
|
||||
return 0
|
||||
fi
|
||||
mkdir -p "$OUTPUT_DIR/sim"
|
||||
for cir in "$BOARD_DIR"/sim/*.cir; do
|
||||
echo " sim: $(basename "$cir")"
|
||||
ngspice -b -r "$OUTPUT_DIR/sim/$(basename "$cir" .cir).raw" "$cir" 2>&1 || true
|
||||
done
|
||||
echo " -> $OUTPUT_DIR/sim/"
|
||||
}
|
||||
|
||||
# ── Run ────────────────────────────────────────────────────────────────
|
||||
echo "kicad-build: $BOARD_DIR"
|
||||
echo " KiCad Python: $KICAD_PYTHON"
|
||||
echo " kicad-cli: $KICAD_CLI"
|
||||
echo " output: $OUTPUT_DIR"
|
||||
echo ""
|
||||
|
||||
check_kicad_python
|
||||
if [ -n "$KICAD_CLI" ]; then
|
||||
: # ok
|
||||
else
|
||||
echo "WARNING: kicad-cli not found (ERC/DRC/gerber/bom will be skipped)"
|
||||
fi
|
||||
|
||||
step_build
|
||||
$DO_ERC && step_erc
|
||||
$DO_DRC && step_drc
|
||||
$DO_GERBER && step_gerber
|
||||
$DO_BOM && step_bom
|
||||
$DO_SIM && step_sim
|
||||
|
||||
echo ""
|
||||
echo "=== Done ==="
|
||||
echo "Output: $OUTPUT_DIR/"
|
||||
ls -la "$OUTPUT_DIR/" 2>/dev/null || true
|
||||
Reference in New Issue
Block a user