Loading files
This file is pinned to the selected release. Download the original to open it in your design tool.
#!/usr/bin/env python3
"""
pcbflow drc — cross-platform DRC runner for KiCad (roadmap Phase 4).
Ports automation/kicad/drc.sh to Python so it works on Windows/macOS/Linux, and
keeps the KI-6 guard: it REFUSES to run on a bare board with no ruleset sibling
(a bare board reports default rules as clean = phantom DRC). Pure Python 3 stdlib.
Usage:
python3 tools/drc.py <board.kicad_pcb> <ruleset.kicad_pro>
python3 tools/drc.py <board.kicad_pcb> # only if a sibling .kicad_pro exists
"""
import argparse, shutil, subprocess, sys
from pathlib import Path
def run_drc(board, ruleset=None, run=subprocess.run, which=shutil.which, timeout=300):
board = Path(board)
if not board.exists():
return {"ok": False, "reason": f"board not found: {board}"}
if not which("kicad-cli"):
return {"ok": False, "reason": "kicad-cli not found — install KiCad (KI-5)"}
pro = board.with_suffix(".kicad_pro")
if ruleset:
r = Path(ruleset)
if not r.exists():
return {"ok": False, "reason": f"ruleset not found: {r}"}
shutil.copyfile(r, pro) # put the ruleset beside the board (the point)
if not pro.exists():
return {"ok": False, "reason": ("no ruleset (.kicad_pro) beside the board — refusing "
"to run: a bare board reports DEFAULT rules as clean "
"(phantom DRC, KI-6). Pass a ruleset.")}
report = str(board.with_suffix(".drc.rpt"))
cmd = ["kicad-cli", "pcb", "drc", "--severity-error", "--exit-code-violations",
"--schematic-parity", "--output", report, str(board)]
manual = " ".join(cmd)
try:
p = run(cmd, timeout=timeout)
except subprocess.TimeoutExpired:
return {"ok": False, "violations": None, "report": report,
"reason": f"kicad-cli DRC timed out after {timeout}s — run it manually: {manual}"}
rc = getattr(p, "returncode", 0)
return {"ok": rc == 0, "violations": rc != 0, "report": report, "manual": manual}
def main():
ap = argparse.ArgumentParser(description="KiCad DRC with the phantom-DRC guard")
ap.add_argument("board")
ap.add_argument("ruleset", nargs="?")
a = ap.parse_args()
res = run_drc(a.board, a.ruleset)
if not res["ok"] and "reason" in res:
print("drc: " + res["reason"], file=sys.stderr)
sys.exit(3)
print(f"drc: report → {res['report']} ({'VIOLATIONS' if res.get('violations') else 'clean'})")
sys.exit(1 if res.get("violations") else 0)
if __name__ == "__main__":
main()
7315b96d0This file is pinned to the selected release. Download the original to open it in your design tool.