app
KiCad - the KiCad Bridge
Public Made by Adomby adom
Reference implementation of the KiCad bridge: multi-instance Python server, forward path via kicad-cli, reverse path via in-process plugin. Most complex of the three bundled bridges.
← Commit history
tools: build_native_release.py (unpublished native zip, spawn kind exe, verbs from describe)
2 files changed
+83
rust/.gitignore+1@@ -1 +1,2 @@ target/+dist/
tools/build_native_release.pyadded+82@@ -0,0 +1,82 @@+#!/usr/bin/env python3+"""Build the native (Rust) kicad bridge zip for Windows. Does NOT publish.++ python3 tools/build_native_release.py # builds, zips, prints sha256++Output: rust/dist/adom-bridge-kicad-native-v<ver>.zip containing kicad-bridge.exe,+bridge.json (spawn.kind exe, verbs generated from the binary's own kicad_describe) and+BRIDGE_VERSION. Version comes from rust/Cargo.toml [workspace.package].version.+The Python bridge keeps shipping through tools/build_release.py until the native binary+passes the verb runner; flipping the public manifest to this zip is a separate, human step.+"""+import hashlib, json, os, re, shutil, subprocess, sys, time, urllib.request, zipfile+from pathlib import Path++ROOT = Path(__file__).resolve().parent.parent+RUST = ROOT / "rust"+TARGET = "x86_64-pc-windows-gnu"++def sh(cmd, cwd=None):+ r = subprocess.run(cmd, cwd=cwd, text=True, capture_output=True)+ if r.returncode != 0:+ print(r.stdout[-2000:]); print(r.stderr[-4000:]); sys.exit(f"FAILED: {' '.join(cmd)}")+ return r.stdout++def version():+ m = re.search(r'^\[workspace\.package\]\s*\nversion\s*=\s*"([^"]+)"', (RUST / "Cargo.toml").read_text(), re.M)+ if not m: sys.exit("REFUSING: no workspace.package.version in rust/Cargo.toml")+ return m.group(1)++def describe_via_linux_binary():+ """Run the host build for a moment to read the verb list: bridge.json must list every verb or ab will not route it."""+ exe = RUST / "target/release/kicad-bridge"+ p = subprocess.Popen([str(exe), "--port", "47739"], stderr=subprocess.DEVNULL)+ try:+ for _ in range(40):+ try:+ req = urllib.request.Request("http://127.0.0.1:47739/command", data=b'{"command":"kicad_describe"}', headers={"Content-Type": "application/json"})+ with urllib.request.urlopen(req, timeout=2) as r:+ return json.load(r)+ except Exception:+ time.sleep(0.1)+ sys.exit("REFUSING: the host binary never answered kicad_describe")+ finally:+ p.kill()++def main():+ ver = version()+ print(f"native kicad bridge {ver}")+ sh(["cargo", "build", "--release"], cwd=RUST)+ sh(["cargo", "build", "--release", "--target", TARGET], cwd=RUST)+ exe = RUST / "target" / TARGET / "release" / "kicad-bridge.exe"+ desc = describe_via_linux_binary()+ verbs = [v["name"] for v in desc["verbs"]]+ if "kicad_describe" not in verbs or "kicad_uninstall" not in verbs:+ sys.exit("REFUSING: describe or uninstall missing from the catalog")+ py = json.loads((ROOT / "bridge.json").read_text())+ manifest = dict(py)+ manifest["version"] = ver+ manifest["spawn"] = {"kind": "exe", "entrypoint": "kicad-bridge.exe", "port": 0, "healthEndpoint": "/status", "stopMethod": "kill", "killImageName": "kicad-bridge.exe"}+ manifest.pop("dependencies", None)+ manifest["verbs"] = verbs+ manifest["runtime"] = "native"+ manifest["platforms"] = {+ "windows": {"supported": True, "notes": "Pre-built x64 kicad-bridge.exe ships in the zip."},+ "macos": {"supported": False, "notes": "Kyle's build: cargo build --release on a Mac, crates/kicad-platform/src/macos.rs."},+ "linux": {"supported": False, "notes": "Barrett's build: cargo build --release on Ubuntu, crates/kicad-platform/src/linux.rs."},+ }+ dist = RUST / "dist"; dist.mkdir(exist_ok=True)+ stage = dist / "stage"; shutil.rmtree(stage, ignore_errors=True); stage.mkdir()+ shutil.copy2(exe, stage / "kicad-bridge.exe")+ (stage / "bridge.json").write_text(json.dumps(manifest, indent=2) + "\n")+ (stage / "BRIDGE_VERSION").write_text(ver + "\n")+ zpath = dist / f"adom-bridge-kicad-native-v{ver}.zip"+ with zipfile.ZipFile(zpath, "w", zipfile.ZIP_DEFLATED) as z:+ for f in sorted(stage.iterdir()):+ z.write(f, f.name)+ sha = hashlib.sha256(zpath.read_bytes()).hexdigest()+ print(f"built {zpath.name}\n verbs : {len(verbs)}\n exe : {exe.stat().st_size} bytes\n zip : {zpath.stat().st_size} bytes\n sha256: {sha}")+ print("not published: flip the manifest by hand once the verb runner passes on the native binary")++if __name__ == "__main__":+ main()