Implement the freeroute CLI (DSN in, SES out, JAR-flag-compatible)
The pyproject declared a freeroute script but freeroute.cli was missing, so the entry point was broken. Add it: reads a Specctra .dsn, routes (rip-up on by default), writes SES. Accepts FreeRouting-compatible -de/-do flags so it can be dropped in wherever 'java -jar freerouting.jar' was called.
This commit is contained in:
parent
da91382015
commit
f01c44d807
66
src/freeroute/cli.py
Normal file
66
src/freeroute/cli.py
Normal file
@ -0,0 +1,66 @@
|
||||
"""freeroute command-line interface: Specctra DSN in, routed SES out.
|
||||
|
||||
Flag-compatible with the FreeRouting JAR (``-de`` / ``-do``) so it can be
|
||||
dropped in wherever ``java -jar freerouting.jar`` was invoked.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import sys
|
||||
|
||||
from freeroute.dsn.reader import parse_dsn
|
||||
from freeroute.route.pipeline import build_routing_result
|
||||
from freeroute.ses import write_ses
|
||||
|
||||
|
||||
def main(argv: list[str] | None = None) -> int:
|
||||
parser = argparse.ArgumentParser(
|
||||
prog="freeroute",
|
||||
description="Native PCB autorouter — route a Specctra DSN and write an SES session file.",
|
||||
)
|
||||
parser.add_argument("dsn", nargs="?", help="input Specctra .dsn file")
|
||||
parser.add_argument(
|
||||
"-de", "--design", dest="design", help="input .dsn (FreeRouting-compatible alias)"
|
||||
)
|
||||
parser.add_argument(
|
||||
"-do", "--output", dest="output", help="output .ses file (default: stdout)"
|
||||
)
|
||||
parser.add_argument(
|
||||
"-mp",
|
||||
"--max-passes",
|
||||
type=int,
|
||||
default=None,
|
||||
help="accepted for FreeRouting CLI compatibility (advisory; freeroute self-bounds passes)",
|
||||
)
|
||||
parser.add_argument("--no-rip-up", action="store_true", help="disable rip-up-and-retry")
|
||||
parser.add_argument("--layers", help="comma-separated signal layer indices to route on")
|
||||
args = parser.parse_args(argv)
|
||||
|
||||
dsn_path = args.design or args.dsn
|
||||
if not dsn_path:
|
||||
parser.error("no input DSN (pass a path, or -de <file>)")
|
||||
|
||||
layers = [int(x) for x in args.layers.split(",")] if args.layers else None
|
||||
|
||||
try:
|
||||
with open(dsn_path, encoding="utf-8") as f:
|
||||
dsn_text = f.read()
|
||||
except OSError as e:
|
||||
print(f"freeroute: cannot read {dsn_path}: {e}", file=sys.stderr)
|
||||
return 2
|
||||
|
||||
dsn = parse_dsn(dsn_text)
|
||||
result = build_routing_result(dsn, layers=layers, rip_up=not args.no_rip_up)
|
||||
ses_text = write_ses(dsn, result)
|
||||
|
||||
if args.output:
|
||||
with open(args.output, "w", encoding="utf-8") as f:
|
||||
f.write(ses_text)
|
||||
else:
|
||||
sys.stdout.write(ses_text)
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
37
tests/test_cli.py
Normal file
37
tests/test_cli.py
Normal file
@ -0,0 +1,37 @@
|
||||
"""Tests for the freeroute CLI (Specctra DSN in, SES out)."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
from freeroute.cli import main
|
||||
|
||||
FIXTURE = Path(__file__).parent / "dsn" / "fixtures" / "simple_2net.dsn"
|
||||
|
||||
|
||||
def test_cli_jar_compatible_flags_write_ses(tmp_path):
|
||||
out = tmp_path / "out.ses"
|
||||
rc = main(["-de", str(FIXTURE), "-do", str(out)])
|
||||
assert rc == 0
|
||||
text = out.read_text()
|
||||
assert text.startswith("(session")
|
||||
assert "(net" in text and "(wire" in text
|
||||
|
||||
|
||||
def test_cli_positional_input_to_stdout(capsys):
|
||||
rc = main([str(FIXTURE)])
|
||||
assert rc == 0
|
||||
assert "(session" in capsys.readouterr().out
|
||||
|
||||
|
||||
def test_cli_missing_input_errors():
|
||||
with pytest.raises(SystemExit):
|
||||
main([])
|
||||
|
||||
|
||||
def test_cli_unreadable_input_returns_2(capsys):
|
||||
rc = main(["/nonexistent/board.dsn"])
|
||||
assert rc == 2
|
||||
assert "cannot read" in capsys.readouterr().err
|
||||
Loading…
x
Reference in New Issue
Block a user