140 lines
5.0 KiB
Python
140 lines
5.0 KiB
Python
"""Copy bundled non-code files into the Nuitka wheel tree."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import shutil
|
|
from pathlib import Path
|
|
|
|
# Required files that must ship with the wheel for ORY-FINGER runtime/GUI.
|
|
REQUIRED_ASSET_RELPATHS = (
|
|
"finger/assets/30fps-0703/mesh.npz",
|
|
"finger/assets/30fps-0703/camera_intrinsics.npz",
|
|
"finger/assets/30fps-0703/world_to_cam.npy",
|
|
"finger/assets/visualization/world_to_cam.npy",
|
|
)
|
|
|
|
|
|
def resolve_bundle_source_files(repo_root: Path, item: dict) -> list[Path]:
|
|
"""Resolve explicit files and/or glob patterns for one bundle item."""
|
|
src_dir = repo_root / item["source"]
|
|
patterns = item.get("patterns", [])
|
|
files = item.get("files", [])
|
|
if not patterns and not files:
|
|
raise ValueError(f"Bundle item {item['name']!r} requires patterns and/or files")
|
|
|
|
seen: set[Path] = set()
|
|
resolved: list[Path] = []
|
|
|
|
def add(path: Path) -> None:
|
|
key = path.resolve()
|
|
if key not in seen:
|
|
seen.add(key)
|
|
resolved.append(path)
|
|
|
|
for pattern in patterns:
|
|
for path in sorted(src_dir.glob(pattern)):
|
|
if path.is_file():
|
|
add(path)
|
|
|
|
for name in files:
|
|
path = src_dir / name
|
|
if not path.is_file():
|
|
raise FileNotFoundError(f"Bundle file not found for {item['name']}: {path}")
|
|
add(path)
|
|
|
|
return resolved
|
|
|
|
|
|
def verify_required_assets(repo_root: Path, items: list[dict]) -> None:
|
|
"""Fail early when required finger assets are missing from the bundle."""
|
|
asset_items = [item for item in items if item.get("name") == "sensors"]
|
|
if not asset_items:
|
|
return
|
|
|
|
resolved: set[str] = set()
|
|
for item in asset_items:
|
|
src_dir = (repo_root / item["source"]).resolve()
|
|
for path in resolve_bundle_source_files(repo_root, item):
|
|
try:
|
|
rel = path.resolve().relative_to(src_dir).as_posix()
|
|
except ValueError:
|
|
continue
|
|
resolved.add(rel)
|
|
|
|
missing = [rel for rel in REQUIRED_ASSET_RELPATHS if rel not in resolved]
|
|
if missing:
|
|
joined = ", ".join(missing)
|
|
raise FileNotFoundError(
|
|
"Required finger assets are missing from the wheel bundle. "
|
|
f"Missing: {joined}. Update [[tool.orisys.bundle.items]] patterns "
|
|
"or restore assets under sensors/finger/assets/ before building."
|
|
)
|
|
|
|
|
|
def package_data_paths_for_item(item: dict) -> list[str]:
|
|
"""Return setuptools package_data paths relative to the orisys package."""
|
|
target = str(item["target"]).replace("\\", "/")
|
|
entries = [f"{target}/{pattern}" for pattern in item.get("patterns", [])]
|
|
entries.extend(f"{target}/{name}" for name in item.get("files", []))
|
|
return entries
|
|
|
|
|
|
def data_file_entries_for_item(item: dict) -> list[str]:
|
|
"""Return setuptools data_files paths relative to the repo root."""
|
|
source = str(item["source"]).replace("\\", "/")
|
|
entries = [f"{source}/{pattern}" for pattern in item.get("patterns", [])]
|
|
entries.extend(f"{source}/{name}" for name in item.get("files", []))
|
|
return entries
|
|
|
|
|
|
def copy_bundle_items(repo_root: Path, pkg_dir: Path, items: list[dict]) -> dict[str, int]:
|
|
verify_required_assets(repo_root, items)
|
|
copied_counts: dict[str, int] = {}
|
|
for item in items:
|
|
name = item["name"]
|
|
src_dir = repo_root / item["source"]
|
|
dst_dir = pkg_dir / item["target"]
|
|
dst_dir.mkdir(parents=True, exist_ok=True)
|
|
|
|
copied = 0
|
|
for src_file in resolve_bundle_source_files(repo_root, item):
|
|
if item.get("preserve_tree", False):
|
|
dst_file = dst_dir / src_file.relative_to(src_dir)
|
|
dst_file.parent.mkdir(parents=True, exist_ok=True)
|
|
else:
|
|
dst_file = dst_dir / src_file.name
|
|
shutil.copy(src_file, dst_file)
|
|
copied += 1
|
|
print(f"Copied {name}: {src_file}")
|
|
|
|
if name == "examples":
|
|
(dst_dir / "__init__.py").write_text("# Examples for orisys package\n", encoding="utf-8")
|
|
|
|
copied_counts[name] = copied
|
|
return copied_counts
|
|
|
|
|
|
def package_data_from_bundle(ext_suffix: str, items: list[dict]) -> dict[str, list[str]]:
|
|
package_data: dict[str, list[str]] = {
|
|
"orisys": [f"*{ext_suffix}"],
|
|
"orisys.examples": ["*.py", "README.md", "viz/**/*.py", "pipeline/**/*.py", "gui/**/*.py"],
|
|
"orisys_blend": ["*.py"],
|
|
}
|
|
for item in items:
|
|
if item["name"] == "examples":
|
|
continue
|
|
package_data["orisys"].extend(package_data_paths_for_item(item))
|
|
return package_data
|
|
|
|
|
|
def copy_orisys_blend(repo_root: Path, wheel_root: Path) -> int:
|
|
"""Copy pure-Python orisys_blend (Numba kernels) next to the Nuitka orisys .pyd."""
|
|
src = repo_root / "src" / "orisys_blend"
|
|
dst = wheel_root / "orisys_blend"
|
|
if dst.exists():
|
|
shutil.rmtree(dst)
|
|
shutil.copytree(src, dst)
|
|
count = sum(1 for p in dst.rglob("*.py") if p.is_file())
|
|
print(f"Copied orisys_blend ({count} .py files) to {dst}")
|
|
return count
|