reorganized build directory
This commit is contained in:
@@ -0,0 +1 @@
|
||||
"""Standalone GUI packaging targets (PyInstaller)."""
|
||||
@@ -0,0 +1 @@
|
||||
"""Shared helpers for product GUI builds."""
|
||||
@@ -0,0 +1,141 @@
|
||||
"""Shared PyInstaller driver for product GUI standalone folders."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
from dataclasses import dataclass, field
|
||||
from pathlib import Path
|
||||
from typing import Callable
|
||||
|
||||
_BUILD_ROOT = Path(__file__).resolve().parents[2]
|
||||
if str(_BUILD_ROOT) not in sys.path:
|
||||
sys.path.insert(0, str(_BUILD_ROOT))
|
||||
|
||||
from common.paths import PYINSTALLER_WORK_DIR, REPO_ROOT, gui_dist_dir
|
||||
|
||||
|
||||
@dataclass
|
||||
class GuiBuildConfig:
|
||||
product: str
|
||||
entry_script: Path
|
||||
exe_name: str
|
||||
dist_name: str
|
||||
hidden_imports: list[str] = field(default_factory=list)
|
||||
exclude_modules: list[str] = field(default_factory=list)
|
||||
data_dirs: list[tuple[str, str]] = field(default_factory=list)
|
||||
readme_lines: list[str] = field(default_factory=list)
|
||||
windowed: bool = True
|
||||
upx_dir: str | None = r"C:\tools\upx"
|
||||
logo_name: str = "logo.png"
|
||||
work_dir: Path = PYINSTALLER_WORK_DIR
|
||||
spec_dir: Path = REPO_ROOT / "build" / "pyinstaller_work"
|
||||
|
||||
|
||||
def clean_dir(path: Path) -> None:
|
||||
if path.exists():
|
||||
shutil.rmtree(path)
|
||||
path.mkdir(parents=True)
|
||||
|
||||
|
||||
def build_with_pyinstaller(
|
||||
config: GuiBuildConfig,
|
||||
*,
|
||||
verify_fn: Callable[[Path], None] | None = None,
|
||||
) -> Path:
|
||||
"""Run PyInstaller for one GUI product and copy bundled data folders."""
|
||||
print("Using Python:", sys.executable)
|
||||
print(f"Building GUI product: {config.product}")
|
||||
|
||||
dist_dir = gui_dist_dir(config.dist_name)
|
||||
clean_dir(config.work_dir)
|
||||
clean_dir(dist_dir)
|
||||
config.spec_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
entry = config.entry_script
|
||||
if not entry.is_file():
|
||||
raise FileNotFoundError(f"GUI entry script not found: {entry}")
|
||||
|
||||
cmd = [
|
||||
sys.executable,
|
||||
"-m",
|
||||
"PyInstaller",
|
||||
"--onedir",
|
||||
"--name",
|
||||
config.dist_name,
|
||||
f"--distpath={dist_dir.parent}",
|
||||
f"--workpath={config.work_dir}",
|
||||
f"--specpath={config.spec_dir}",
|
||||
"--noconfirm",
|
||||
"--clean",
|
||||
"--windowed" if config.windowed else "--console",
|
||||
]
|
||||
|
||||
for src_rel, dest_rel in config.data_dirs:
|
||||
src = (REPO_ROOT / src_rel).resolve()
|
||||
if not src.is_dir():
|
||||
raise FileNotFoundError(f"Data directory missing: {src}")
|
||||
cmd.append(f"--add-data={src};{dest_rel}")
|
||||
|
||||
logo = REPO_ROOT / config.logo_name
|
||||
if logo.is_file():
|
||||
cmd.append(f"--add-data={logo.resolve()};.")
|
||||
|
||||
for mod in config.hidden_imports:
|
||||
cmd.append(f"--hidden-import={mod}")
|
||||
for mod in config.exclude_modules:
|
||||
cmd.append(f"--exclude-module={mod}")
|
||||
|
||||
if config.upx_dir and Path(config.upx_dir).is_dir():
|
||||
cmd.append(f"--upx-dir={config.upx_dir}")
|
||||
|
||||
cmd.append(str(entry.resolve()))
|
||||
|
||||
env = os.environ.copy()
|
||||
env["PYTHONPATH"] = (
|
||||
str((REPO_ROOT / "src").resolve())
|
||||
+ os.pathsep
|
||||
+ str((REPO_ROOT / "examples").resolve())
|
||||
+ os.pathsep
|
||||
+ env.get("PYTHONPATH", "")
|
||||
)
|
||||
|
||||
print("\n=== Running PyInstaller ===")
|
||||
print("Command:", " ".join(cmd))
|
||||
subprocess.check_call(cmd, env=env, cwd=REPO_ROOT)
|
||||
|
||||
final_exe = dist_dir / config.exe_name
|
||||
if not final_exe.is_file():
|
||||
raise RuntimeError(f"Executable not found at {final_exe}")
|
||||
|
||||
print("\n=== Copying data directories ===")
|
||||
for src_rel, dest_rel in config.data_dirs:
|
||||
src = REPO_ROOT / src_rel
|
||||
dest = dist_dir / dest_rel
|
||||
if not src.is_dir():
|
||||
raise FileNotFoundError(f"Data directory not found: {src}")
|
||||
if dest.exists():
|
||||
shutil.rmtree(dest)
|
||||
shutil.copytree(src, dest, dirs_exist_ok=True)
|
||||
print(f" {src} -> {dest}")
|
||||
|
||||
if logo.is_file():
|
||||
shutil.copy2(logo, dist_dir / config.logo_name)
|
||||
print(" Logo copied")
|
||||
|
||||
if config.readme_lines:
|
||||
(dist_dir / "README.txt").write_text("\n".join(config.readme_lines) + "\n", encoding="utf-8")
|
||||
|
||||
print(f"\n[SUCCESS] Standalone GUI folder: {dist_dir}")
|
||||
print(f"Executable: {final_exe}")
|
||||
|
||||
if verify_fn is not None:
|
||||
try:
|
||||
print("\n=== Smoke test ===")
|
||||
verify_fn(dist_dir)
|
||||
except Exception as exc:
|
||||
print(f"\n[WARNING] Smoke test failed: {exc}")
|
||||
|
||||
return dist_dir
|
||||
@@ -0,0 +1 @@
|
||||
"""ORY-BOX internal calibration/testing GUI packaging (scaffold)."""
|
||||
@@ -0,0 +1,30 @@
|
||||
"""Scaffold for ORY-BOX internal GUI packaging.
|
||||
|
||||
Not implemented yet. When ready, wire a dedicated calibration/testing entry
|
||||
script and call ``gui.common.pyinstaller_driver.build_with_pyinstaller``.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
_BUILD_ROOT = Path(__file__).resolve().parents[2]
|
||||
if str(_BUILD_ROOT) not in sys.path:
|
||||
sys.path.insert(0, str(_BUILD_ROOT))
|
||||
|
||||
from gui.ory_box.config import DIST_NAME, ENTRY_SCRIPT, make_config
|
||||
|
||||
|
||||
def main() -> None:
|
||||
config = make_config()
|
||||
raise NotImplementedError(
|
||||
"ORY-BOX GUI packaging is a scaffold only. "
|
||||
f"Planned dist={DIST_NAME!r}, entry={ENTRY_SCRIPT}. "
|
||||
"Implement the internal calibration/testing UI entry, then call "
|
||||
"gui.common.pyinstaller_driver.build_with_pyinstaller(config)."
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,63 @@
|
||||
"""Product configuration scaffold for the ORY-BOX internal GUI."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
_BUILD_ROOT = Path(__file__).resolve().parents[2]
|
||||
if str(_BUILD_ROOT) not in sys.path:
|
||||
sys.path.insert(0, str(_BUILD_ROOT))
|
||||
|
||||
from common.paths import REPO_ROOT
|
||||
from gui.common.pyinstaller_driver import GuiBuildConfig
|
||||
|
||||
ENTRY_SCRIPT = REPO_ROOT / "examples" / "products" / "ory_box" / "basic_pipeline.py"
|
||||
EXE_NAME = "OryBox.exe"
|
||||
DIST_NAME = "OryBox"
|
||||
|
||||
HIDDEN_IMPORTS = [
|
||||
"PyQt5",
|
||||
"pyqtgraph",
|
||||
"cv2",
|
||||
"orisys",
|
||||
"gui",
|
||||
"pipeline",
|
||||
"viz",
|
||||
]
|
||||
|
||||
EXCLUDE_MODULES = [
|
||||
"cupy",
|
||||
"cupyx",
|
||||
"pyopencl",
|
||||
"open3d",
|
||||
"tkinter",
|
||||
"pip",
|
||||
"wheel",
|
||||
]
|
||||
|
||||
DATA_DIRS = [
|
||||
("config", "config"),
|
||||
]
|
||||
|
||||
README_LINES = [
|
||||
"ORY-BOX internal calibration/testing GUI (scaffold)",
|
||||
"",
|
||||
"This product packaging target is reserved for a future internal tool.",
|
||||
"Entry point placeholder:",
|
||||
f" {ENTRY_SCRIPT.as_posix()}",
|
||||
]
|
||||
|
||||
|
||||
def make_config() -> GuiBuildConfig:
|
||||
return GuiBuildConfig(
|
||||
product="ory_box",
|
||||
entry_script=ENTRY_SCRIPT,
|
||||
exe_name=EXE_NAME,
|
||||
dist_name=DIST_NAME,
|
||||
hidden_imports=list(HIDDEN_IMPORTS),
|
||||
exclude_modules=list(EXCLUDE_MODULES),
|
||||
data_dirs=list(DATA_DIRS),
|
||||
readme_lines=list(README_LINES),
|
||||
windowed=False,
|
||||
)
|
||||
@@ -0,0 +1 @@
|
||||
"""ORY-FINGER standalone GUI packaging."""
|
||||
@@ -0,0 +1,31 @@
|
||||
"""Build the ORY-FINGER standalone Windows GUI folder with PyInstaller.
|
||||
|
||||
Usage:
|
||||
python build/run.py gui finger
|
||||
python build/gui/ory_finger/build.py
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
_BUILD_ROOT = Path(__file__).resolve().parents[2]
|
||||
if str(_BUILD_ROOT) not in sys.path:
|
||||
sys.path.insert(0, str(_BUILD_ROOT))
|
||||
|
||||
from common.verify.gui import verify_gui_dist
|
||||
from gui.common.pyinstaller_driver import build_with_pyinstaller
|
||||
from gui.ory_finger.config import make_config
|
||||
|
||||
|
||||
def main() -> None:
|
||||
config = make_config()
|
||||
build_with_pyinstaller(
|
||||
config,
|
||||
verify_fn=lambda dist_dir: verify_gui_dist(dist_dir, product="ory_finger"),
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,75 @@
|
||||
"""Product configuration for the ORY-FINGER Windows dashboard."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
_BUILD_ROOT = Path(__file__).resolve().parents[2]
|
||||
if str(_BUILD_ROOT) not in sys.path:
|
||||
sys.path.insert(0, str(_BUILD_ROOT))
|
||||
|
||||
from common.paths import REPO_ROOT
|
||||
from gui.common.pyinstaller_driver import GuiBuildConfig
|
||||
|
||||
ENTRY_SCRIPT = REPO_ROOT / "examples" / "products" / "ory_finger" / "finger_pipeline_3dview.py"
|
||||
EXE_NAME = "OrisysDashboard.exe"
|
||||
DIST_NAME = "OrisysDashboard"
|
||||
|
||||
HIDDEN_IMPORTS = [
|
||||
"PyQt5",
|
||||
"pyqtgraph",
|
||||
"open3d",
|
||||
"cv2",
|
||||
"orisys",
|
||||
"gui",
|
||||
"pipeline",
|
||||
"viz",
|
||||
"gui.app_paths",
|
||||
"plotly",
|
||||
"_plotly_utils",
|
||||
"open3d.cpu.pybind",
|
||||
]
|
||||
|
||||
EXCLUDE_MODULES = [
|
||||
"cupy",
|
||||
"cupyx",
|
||||
"pyopencl",
|
||||
"tkinter",
|
||||
"pip",
|
||||
"wheel",
|
||||
"matplotlib",
|
||||
]
|
||||
|
||||
DATA_DIRS = [
|
||||
("config", "config"),
|
||||
("assets", "assets"),
|
||||
]
|
||||
|
||||
README_LINES = [
|
||||
"Orisys Finger Dashboard (standalone)",
|
||||
"",
|
||||
"Run:",
|
||||
f" {EXE_NAME}",
|
||||
f" {EXE_NAME} --video path\\to\\video.mp4",
|
||||
f" {EXE_NAME} --config config\\dd02-ov.json",
|
||||
"",
|
||||
"Bundled folders:",
|
||||
" config/ sensor JSON configs",
|
||||
" assets/ finger mesh and visualization data",
|
||||
"",
|
||||
"CPU-only build (no CUDA/CuPy).",
|
||||
]
|
||||
|
||||
|
||||
def make_config() -> GuiBuildConfig:
|
||||
return GuiBuildConfig(
|
||||
product="ory_finger",
|
||||
entry_script=ENTRY_SCRIPT,
|
||||
exe_name=EXE_NAME,
|
||||
dist_name=DIST_NAME,
|
||||
hidden_imports=list(HIDDEN_IMPORTS),
|
||||
exclude_modules=list(EXCLUDE_MODULES),
|
||||
data_dirs=list(DATA_DIRS),
|
||||
readme_lines=list(README_LINES),
|
||||
)
|
||||
@@ -0,0 +1 @@
|
||||
"""ORY-MINI internal calibration/testing GUI packaging (scaffold)."""
|
||||
@@ -0,0 +1,30 @@
|
||||
"""Scaffold for ORY-MINI internal GUI packaging.
|
||||
|
||||
Not implemented yet. When ready, wire a dedicated calibration/testing entry
|
||||
script and call ``gui.common.pyinstaller_driver.build_with_pyinstaller``.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
_BUILD_ROOT = Path(__file__).resolve().parents[2]
|
||||
if str(_BUILD_ROOT) not in sys.path:
|
||||
sys.path.insert(0, str(_BUILD_ROOT))
|
||||
|
||||
from gui.ory_mini.config import DIST_NAME, ENTRY_SCRIPT, make_config
|
||||
|
||||
|
||||
def main() -> None:
|
||||
config = make_config()
|
||||
raise NotImplementedError(
|
||||
"ORY-MINI GUI packaging is a scaffold only. "
|
||||
f"Planned dist={DIST_NAME!r}, entry={ENTRY_SCRIPT}. "
|
||||
"Implement the internal calibration/testing UI entry, then call "
|
||||
"gui.common.pyinstaller_driver.build_with_pyinstaller(config)."
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,63 @@
|
||||
"""Product configuration scaffold for the ORY-MINI internal GUI."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
_BUILD_ROOT = Path(__file__).resolve().parents[2]
|
||||
if str(_BUILD_ROOT) not in sys.path:
|
||||
sys.path.insert(0, str(_BUILD_ROOT))
|
||||
|
||||
from common.paths import REPO_ROOT
|
||||
from gui.common.pyinstaller_driver import GuiBuildConfig
|
||||
|
||||
ENTRY_SCRIPT = REPO_ROOT / "examples" / "products" / "ory_mini" / "basic_pipeline.py"
|
||||
EXE_NAME = "OryMini.exe"
|
||||
DIST_NAME = "OryMini"
|
||||
|
||||
HIDDEN_IMPORTS = [
|
||||
"PyQt5",
|
||||
"pyqtgraph",
|
||||
"cv2",
|
||||
"orisys",
|
||||
"gui",
|
||||
"pipeline",
|
||||
"viz",
|
||||
]
|
||||
|
||||
EXCLUDE_MODULES = [
|
||||
"cupy",
|
||||
"cupyx",
|
||||
"pyopencl",
|
||||
"open3d",
|
||||
"tkinter",
|
||||
"pip",
|
||||
"wheel",
|
||||
]
|
||||
|
||||
DATA_DIRS = [
|
||||
("config", "config"),
|
||||
]
|
||||
|
||||
README_LINES = [
|
||||
"ORY-MINI internal calibration/testing GUI (scaffold)",
|
||||
"",
|
||||
"This product packaging target is reserved for a future internal tool.",
|
||||
"Entry point placeholder:",
|
||||
f" {ENTRY_SCRIPT.as_posix()}",
|
||||
]
|
||||
|
||||
|
||||
def make_config() -> GuiBuildConfig:
|
||||
return GuiBuildConfig(
|
||||
product="ory_mini",
|
||||
entry_script=ENTRY_SCRIPT,
|
||||
exe_name=EXE_NAME,
|
||||
dist_name=DIST_NAME,
|
||||
hidden_imports=list(HIDDEN_IMPORTS),
|
||||
exclude_modules=list(EXCLUDE_MODULES),
|
||||
data_dirs=list(DATA_DIRS),
|
||||
readme_lines=list(README_LINES),
|
||||
windowed=False,
|
||||
)
|
||||
Reference in New Issue
Block a user