95 lines
2.6 KiB
Python
95 lines
2.6 KiB
Python
from __future__ import annotations
|
|
|
|
import json
|
|
import shutil
|
|
from pathlib import Path
|
|
from typing import Iterable
|
|
|
|
|
|
def _load_json(path: Path) -> dict:
|
|
try:
|
|
raw = path.read_text(encoding="utf-8")
|
|
except OSError:
|
|
return {}
|
|
try:
|
|
data = json.loads(raw)
|
|
except json.JSONDecodeError:
|
|
return {}
|
|
return data if isinstance(data, dict) else {}
|
|
|
|
|
|
def _from_build(build_dir: Path, relative_path: object) -> Path | None:
|
|
if not isinstance(relative_path, str) or not relative_path.strip():
|
|
return None
|
|
candidate = build_dir / relative_path
|
|
if candidate.exists() and candidate.is_file():
|
|
return candidate
|
|
return None
|
|
|
|
|
|
def _collect_from_flasher_args(build_dir: Path) -> dict[str, Path]:
|
|
result: dict[str, Path] = {}
|
|
data = _load_json(build_dir / "flasher_args.json")
|
|
|
|
flash_files = data.get("flash_files")
|
|
if isinstance(flash_files, dict):
|
|
for rel in flash_files.values():
|
|
path = _from_build(build_dir, rel)
|
|
if path is not None:
|
|
result[path.name] = path
|
|
|
|
for key in ("bootloader", "app", "partition-table"):
|
|
section = data.get(key)
|
|
if not isinstance(section, dict):
|
|
continue
|
|
path = _from_build(build_dir, section.get("file"))
|
|
if path is not None:
|
|
result[path.name] = path
|
|
|
|
return result
|
|
|
|
|
|
def _find_in_build_tree(build_dir: Path, name: str) -> Path | None:
|
|
direct = build_dir / name
|
|
if direct.exists() and direct.is_file():
|
|
return direct
|
|
for path in build_dir.glob(f"**/{name}"):
|
|
if path.is_file():
|
|
return path
|
|
return None
|
|
|
|
|
|
def sync_bins_from_idf_build(
|
|
bin_dir: Path,
|
|
required_bins: Iterable[str],
|
|
build_dir: Path,
|
|
*,
|
|
overwrite: bool = False,
|
|
) -> list[tuple[Path, Path]]:
|
|
if not build_dir.exists() or not build_dir.is_dir():
|
|
return []
|
|
|
|
build_map = _collect_from_flasher_args(build_dir)
|
|
copied: list[tuple[Path, Path]] = []
|
|
seen: set[str] = set()
|
|
for raw_name in required_bins:
|
|
name = str(raw_name).strip()
|
|
if not name or name in seen:
|
|
continue
|
|
seen.add(name)
|
|
|
|
target = bin_dir / name
|
|
if target.exists() and not overwrite:
|
|
continue
|
|
|
|
source = build_map.get(name)
|
|
if source is None:
|
|
source = _find_in_build_tree(build_dir, name)
|
|
if source is None:
|
|
continue
|
|
|
|
target.parent.mkdir(parents=True, exist_ok=True)
|
|
shutil.copy2(source, target)
|
|
copied.append((source, target))
|
|
return copied
|