reorganized build directory
This commit is contained in:
+5
-1
@@ -5,8 +5,12 @@ __pycache__/
|
|||||||
|
|
||||||
# Packaging / build outputs
|
# Packaging / build outputs
|
||||||
/build/nuitka/
|
/build/nuitka/
|
||||||
/build/lib/orisys/
|
/build/build/
|
||||||
|
/build/bdist.*/
|
||||||
|
/build/dist/
|
||||||
|
/build/lib/
|
||||||
/build/pyinstaller_work/
|
/build/pyinstaller_work/
|
||||||
|
/build/*.spec
|
||||||
/dist/
|
/dist/
|
||||||
/dist_obfuscated/
|
/dist_obfuscated/
|
||||||
/dist_pyd/
|
/dist_pyd/
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ Orisys SDK is a Python toolkit for optical tactile sensing. It provides image ca
|
|||||||
stitching, dense optical flow, Helmholtz-Hodge decomposition, contact/depth estimation,
|
stitching, dense optical flow, Helmholtz-Hodge decomposition, contact/depth estimation,
|
||||||
and several runnable examples for camera and video-based workflows.
|
and several runnable examples for camera and video-based workflows.
|
||||||
|
|
||||||
|
PyPI: [https://pypi.org/project/orisys/](https://pypi.org/project/orisys/)
|
||||||
|
|
||||||
## Repository Layout
|
## Repository Layout
|
||||||
|
|
||||||
- `src/orisys/`: installable SDK package
|
- `src/orisys/`: installable SDK package
|
||||||
@@ -93,9 +95,8 @@ cfg = orisys.export_config("ddjx01", "./config/exported_config.json")
|
|||||||
|
|
||||||
- Example entry points: `examples/README.md`
|
- Example entry points: `examples/README.md`
|
||||||
- User documentation: `docs/get_started.md`
|
- User documentation: `docs/get_started.md`
|
||||||
- Packaging helper: `build/build_nuitka.py` (compiled SDK wheel)
|
- Build launcher: `python build/run.py` (see `build/README.md`)
|
||||||
- Manylinux / PyPI binary wheel (Docker Desktop): `docs/publishing.md`
|
- Manylinux / PyPI binary wheel: `docs/publishing.md`
|
||||||
- GUI standalone folder: `build/build_gui_win.bat` → `dist/OrisysDashboard/`
|
|
||||||
- Specialized utilities: `tools/`
|
- Specialized utilities: `tools/`
|
||||||
|
|
||||||
## Publishing (Linux manylinux)
|
## Publishing (Linux manylinux)
|
||||||
@@ -103,7 +104,7 @@ cfg = orisys.export_config("ddjx01", "./config/exported_config.json")
|
|||||||
To build PyPI-compliant CPython 3.10/3.11/3.12 Linux wheels from Windows Docker Desktop:
|
To build PyPI-compliant CPython 3.10/3.11/3.12 Linux wheels from Windows Docker Desktop:
|
||||||
|
|
||||||
```powershell
|
```powershell
|
||||||
powershell -ExecutionPolicy Bypass -File .\build\build_manylinux_docker.ps1
|
python build/run.py manylinux
|
||||||
```
|
```
|
||||||
|
|
||||||
Upload candidates land in `dist/repaired/`. Full TestPyPI → PyPI steps:
|
Upload candidates land in `dist/repaired/`. Full TestPyPI → PyPI steps:
|
||||||
|
|||||||
@@ -1,44 +0,0 @@
|
|||||||
# -*- mode: python ; coding: utf-8 -*-
|
|
||||||
|
|
||||||
|
|
||||||
a = Analysis(
|
|
||||||
['D:\\repo\\orisys-sdk\\examples\\finger_pipeline_3dview.py'],
|
|
||||||
pathex=[],
|
|
||||||
binaries=[],
|
|
||||||
datas=[('D:\\repo\\orisys-sdk\\config', 'config'), ('D:\\repo\\orisys-sdk\\assets', 'assets')],
|
|
||||||
hiddenimports=['PyQt5', 'pyqtgraph', 'open3d', 'cv2', 'orisys', 'gui', 'pipeline', 'viz', 'gui.app_paths', 'plotly', '_plotly_utils', 'open3d.cpu.pybind'],
|
|
||||||
hookspath=[],
|
|
||||||
hooksconfig={},
|
|
||||||
runtime_hooks=[],
|
|
||||||
excludes=['cupy', 'cupyx', 'pyopencl', 'tkinter', 'pip', 'wheel', 'matplotlib'],
|
|
||||||
noarchive=False,
|
|
||||||
optimize=0,
|
|
||||||
)
|
|
||||||
pyz = PYZ(a.pure)
|
|
||||||
|
|
||||||
exe = EXE(
|
|
||||||
pyz,
|
|
||||||
a.scripts,
|
|
||||||
[],
|
|
||||||
exclude_binaries=True,
|
|
||||||
name='OrisysDashboard',
|
|
||||||
debug=False,
|
|
||||||
bootloader_ignore_signals=False,
|
|
||||||
strip=False,
|
|
||||||
upx=True,
|
|
||||||
console=False,
|
|
||||||
disable_windowed_traceback=False,
|
|
||||||
argv_emulation=False,
|
|
||||||
target_arch=None,
|
|
||||||
codesign_identity=None,
|
|
||||||
entitlements_file=None,
|
|
||||||
)
|
|
||||||
coll = COLLECT(
|
|
||||||
exe,
|
|
||||||
a.binaries,
|
|
||||||
a.datas,
|
|
||||||
strip=False,
|
|
||||||
upx=True,
|
|
||||||
upx_exclude=[],
|
|
||||||
name='OrisysDashboard',
|
|
||||||
)
|
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
# Build tooling
|
||||||
|
|
||||||
|
Use one launcher from the repo root:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python build/run.py <command>
|
||||||
|
```
|
||||||
|
|
||||||
|
```text
|
||||||
|
build/
|
||||||
|
run.py << single entrypoint
|
||||||
|
README.md
|
||||||
|
common/ shared helpers (paths, pyproject, verify)
|
||||||
|
wheel/ SDK .whl (Nuitka + manylinux Docker)
|
||||||
|
gui/
|
||||||
|
common/ shared PyInstaller driver
|
||||||
|
ory_finger/ ORY-FINGER dashboard
|
||||||
|
ory_mini/ ORY-MINI internal GUI (scaffold)
|
||||||
|
ory_box/ ORY-BOX internal GUI (scaffold)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Commands
|
||||||
|
|
||||||
|
### SDK wheel
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python build/run.py wheel-win
|
||||||
|
python build/run.py wheel-manylinux
|
||||||
|
|
||||||
|
# Single Version
|
||||||
|
python build/run.py wheel
|
||||||
|
# Inside Docker
|
||||||
|
python build/run.py wheel --target pypi-linux --verify
|
||||||
|
```
|
||||||
|
|
||||||
|
Direct module paths (same behavior):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python build/wheel/build.py
|
||||||
|
build\wheel\win.bat
|
||||||
|
.\build\wheel\docker\run.ps1
|
||||||
|
```
|
||||||
|
|
||||||
|
### GUI
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python build/run.py gui finger
|
||||||
|
python build/run.py gui mini # scaffold / NotImplemented
|
||||||
|
python build/run.py gui box # scaffold / NotImplemented
|
||||||
|
```
|
||||||
|
|
||||||
|
### Verify
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python build/run.py verify path\to\orisys.whl
|
||||||
|
python build/run.py verify --gui-dist --product ory_finger
|
||||||
|
```
|
||||||
|
|
||||||
|
## Outputs
|
||||||
|
|
||||||
|
- Wheels: `SDK/<version>/`, `dist/repaired/`
|
||||||
|
- Finger GUI: `dist/OrisysDashboard/`
|
||||||
|
- Future mini/box: `dist/OryMini/`, `dist/OryBox/`
|
||||||
|
|
||||||
|
Generated work files under `build/nuitka/` and `build/pyinstaller_work/` are gitignored.
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
powershell -ExecutionPolicy Bypass -File .\build\build_manylinux_docker.ps1
|
|
||||||
powershell -ExecutionPolicy Bypass -File .\build\build_manylinux_docker.ps1 -PythonVersions 3.10,3.12
|
|
||||||
@@ -1,194 +0,0 @@
|
|||||||
"""Build a CPU-only standalone Windows folder for Orisys 3D dashboard using PyInstaller.
|
|
||||||
|
|
||||||
Usage:
|
|
||||||
conda activate py310 # or a clean virtualenv with only required packages
|
|
||||||
pip install pyinstaller
|
|
||||||
python build/build_gui_win_pyinstaller.py
|
|
||||||
|
|
||||||
Output:
|
|
||||||
dist/OrisysDashboard/OrisysDashboard.exe (+ bundled Python, DLLs, config/, assets/)
|
|
||||||
"""
|
|
||||||
|
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
import os
|
|
||||||
import shutil
|
|
||||||
import subprocess
|
|
||||||
import sys
|
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
# ---------- Configuration ----------
|
|
||||||
ENTRY_SCRIPT = Path("examples/products/ory_finger/finger_pipeline_3dview.py").resolve()
|
|
||||||
EXE_NAME = "OrisysDashboard.exe"
|
|
||||||
DIST_NAME = "OrisysDashboard" # final folder name
|
|
||||||
repo_root = Path(__file__).parent.parent
|
|
||||||
|
|
||||||
# Packages you want to force-include (PyInstaller often auto-detects them, but explicit avoids misses)
|
|
||||||
HIDDEN_IMPORTS = [
|
|
||||||
"PyQt5",
|
|
||||||
"pyqtgraph",
|
|
||||||
"open3d",
|
|
||||||
"cv2",
|
|
||||||
"orisys",
|
|
||||||
"gui",
|
|
||||||
"pipeline",
|
|
||||||
"viz",
|
|
||||||
"gui.app_paths",
|
|
||||||
"plotly",
|
|
||||||
"_plotly_utils",
|
|
||||||
# sometimes needed for open3d:
|
|
||||||
"open3d.cpu.pybind", # adjust to actual module if needed
|
|
||||||
]
|
|
||||||
|
|
||||||
# Modules to completely exclude (they won't be imported anyway if absent in venv)
|
|
||||||
EXCLUDE_MODULES = [
|
|
||||||
"cupy",
|
|
||||||
"cupyx",
|
|
||||||
"pyopencl",
|
|
||||||
"tkinter",
|
|
||||||
# "unittest",
|
|
||||||
# "email",
|
|
||||||
# "http",
|
|
||||||
# "xmlrpc",
|
|
||||||
# "pkg_resources",
|
|
||||||
# "setuptools",
|
|
||||||
"pip",
|
|
||||||
"wheel",
|
|
||||||
"matplotlib", # unless you use it
|
|
||||||
# "scipy", # unless you use it
|
|
||||||
]
|
|
||||||
|
|
||||||
# Data directories: (source relative to repo root, destination in bundle)
|
|
||||||
DATA_DIRS = [
|
|
||||||
("config", "config"),
|
|
||||||
("assets", "assets"),
|
|
||||||
]
|
|
||||||
|
|
||||||
# Optional: UPX directory (download from https://upx.github.io)
|
|
||||||
UPX_DIR = r"C:\tools\upx" # set to None if not available
|
|
||||||
# ---------- End Configuration ----------
|
|
||||||
|
|
||||||
|
|
||||||
def clean_dist(dist_path: Path) -> None:
|
|
||||||
if dist_path.exists():
|
|
||||||
shutil.rmtree(dist_path)
|
|
||||||
dist_path.mkdir(parents=True)
|
|
||||||
|
|
||||||
|
|
||||||
def build_with_pyinstaller():
|
|
||||||
# Ensure we are in a clean, minimal venv
|
|
||||||
print("Using Python:", sys.executable)
|
|
||||||
|
|
||||||
# Output folder for PyInstaller work files
|
|
||||||
work_dir = Path("build/pyinstaller_work")
|
|
||||||
dist_dir = Path("dist") / DIST_NAME
|
|
||||||
clean_dist(work_dir)
|
|
||||||
clean_dist(dist_dir)
|
|
||||||
|
|
||||||
# Build PyInstaller command
|
|
||||||
cmd = [
|
|
||||||
sys.executable, "-m", "PyInstaller",
|
|
||||||
"--onedir", # produce a folder, not a single exe
|
|
||||||
"--name", DIST_NAME,
|
|
||||||
f"--distpath={dist_dir.parent}",
|
|
||||||
f"--workpath={work_dir}",
|
|
||||||
f"--specpath=build",
|
|
||||||
"--noconfirm",
|
|
||||||
"--clean",
|
|
||||||
"--windowed", # or "--console" if you need terminal
|
|
||||||
]
|
|
||||||
|
|
||||||
# Add data files
|
|
||||||
for src_rel, dest_rel in DATA_DIRS:
|
|
||||||
src = Path(src_rel).resolve()
|
|
||||||
if not src.is_dir():
|
|
||||||
raise FileNotFoundError(f"Data directory missing: {src}")
|
|
||||||
cmd.append(f"--add-data={src};{dest_rel}")
|
|
||||||
|
|
||||||
# Logo file if exists
|
|
||||||
logo = Path("logo.png")
|
|
||||||
if logo.exists():
|
|
||||||
cmd.append(f"--add-data={logo.resolve()};.")
|
|
||||||
|
|
||||||
# Hidden imports
|
|
||||||
for mod in HIDDEN_IMPORTS:
|
|
||||||
cmd.append(f"--hidden-import={mod}")
|
|
||||||
|
|
||||||
# Exclude modules
|
|
||||||
for mod in EXCLUDE_MODULES:
|
|
||||||
cmd.append(f"--exclude-module={mod}")
|
|
||||||
|
|
||||||
# UPX if available
|
|
||||||
if UPX_DIR and Path(UPX_DIR).is_dir():
|
|
||||||
cmd.append(f"--upx-dir={UPX_DIR}")
|
|
||||||
|
|
||||||
# Entry point
|
|
||||||
cmd.append(str(ENTRY_SCRIPT))
|
|
||||||
|
|
||||||
# Environment (same as Nuitka script)
|
|
||||||
env = os.environ.copy()
|
|
||||||
env["PYTHONPATH"] = str(Path("src").resolve()) + os.pathsep + str(Path("examples").resolve()) + os.pathsep + env.get("PYTHONPATH", "")
|
|
||||||
|
|
||||||
print("\n=== Running PyInstaller ===")
|
|
||||||
print("Command:", " ".join(cmd))
|
|
||||||
subprocess.check_call(cmd, env=env)
|
|
||||||
|
|
||||||
# After build, copy data directories explicitly
|
|
||||||
# PyInstaller output folder
|
|
||||||
dist_dir = Path("dist") / DIST_NAME
|
|
||||||
final_exe = dist_dir / 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 DATA_DIRS:
|
|
||||||
src = repo_root / src_rel # use an absolute reference to repo root
|
|
||||||
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) # Python 3.8+ use dirs_exist_ok=True
|
|
||||||
print(f" {src} -> {dest}")
|
|
||||||
|
|
||||||
# Copy logo
|
|
||||||
logo_src = repo_root / "logo.png"
|
|
||||||
if logo_src.is_file():
|
|
||||||
shutil.copy2(logo_src, dist_dir / "logo.png")
|
|
||||||
print(f" Logo copied")
|
|
||||||
|
|
||||||
# Write README (same as Nuitka script)
|
|
||||||
readme = dist_dir / "README.txt"
|
|
||||||
readme.write_text(
|
|
||||||
"\n".join([
|
|
||||||
"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).",
|
|
||||||
]),
|
|
||||||
encoding="utf-8",
|
|
||||||
)
|
|
||||||
|
|
||||||
print(f"\n[SUCCESS] Standalone GUI folder: {dist_dir}")
|
|
||||||
print(f"Executable: {final_exe}")
|
|
||||||
|
|
||||||
# Optional smoke test (same as Nuitka script)
|
|
||||||
try:
|
|
||||||
from verify import verify_gui_dist
|
|
||||||
print("\n=== Smoke test ===")
|
|
||||||
verify_gui_dist(dist_dir)
|
|
||||||
except Exception as exc:
|
|
||||||
print(f"\n[WARNING] Smoke test failed: {exc}")
|
|
||||||
|
|
||||||
return dist_dir
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
build_with_pyinstaller()
|
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
"""Shared build utilities used by wheel and GUI packaging targets."""
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
"""Ensure ``build/`` is importable as the root for ``common``, ``wheel``, and ``gui``."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
_BUILD_ROOT = Path(__file__).resolve().parents[1]
|
||||||
|
|
||||||
|
|
||||||
|
def ensure_build_root_on_path() -> Path:
|
||||||
|
root = str(_BUILD_ROOT)
|
||||||
|
if root not in sys.path:
|
||||||
|
sys.path.insert(0, root)
|
||||||
|
return _BUILD_ROOT
|
||||||
@@ -1,15 +1,17 @@
|
|||||||
"""Repository and Nuitka build output paths."""
|
"""Repository and packaging output paths."""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
REPO_ROOT = Path(__file__).resolve().parent.parent
|
# build/common/paths.py → parents: common, build, repo
|
||||||
|
REPO_ROOT = Path(__file__).resolve().parents[2]
|
||||||
|
|
||||||
NUITKA_COMPILE_DIR = REPO_ROOT / "build" / "nuitka" / "compile"
|
NUITKA_COMPILE_DIR = REPO_ROOT / "build" / "nuitka" / "compile"
|
||||||
NUITKA_WHEEL_DIR = REPO_ROOT / "build" / "nuitka" / "wheel"
|
NUITKA_WHEEL_DIR = REPO_ROOT / "build" / "nuitka" / "wheel"
|
||||||
NUITKA_GUI_DIR = REPO_ROOT / "build" / "nuitka" / "gui"
|
NUITKA_GUI_DIR = REPO_ROOT / "build" / "nuitka" / "gui"
|
||||||
GUI_DIST_DIR = REPO_ROOT / "dist" / "OrisysDashboard"
|
GUI_DIST_DIR = REPO_ROOT / "dist" / "OrisysDashboard"
|
||||||
|
PYINSTALLER_WORK_DIR = REPO_ROOT / "build" / "pyinstaller_work"
|
||||||
|
|
||||||
|
|
||||||
def repo_path(*parts: str) -> Path:
|
def repo_path(*parts: str) -> Path:
|
||||||
@@ -23,3 +25,7 @@ def sdk_dir(version: str) -> Path:
|
|||||||
def repaired_wheel_dir() -> Path:
|
def repaired_wheel_dir() -> Path:
|
||||||
"""Directory for auditwheel-repaired wheels under the Nuitka wheel build tree."""
|
"""Directory for auditwheel-repaired wheels under the Nuitka wheel build tree."""
|
||||||
return NUITKA_WHEEL_DIR / "repaired"
|
return NUITKA_WHEEL_DIR / "repaired"
|
||||||
|
|
||||||
|
|
||||||
|
def gui_dist_dir(dist_name: str) -> Path:
|
||||||
|
return REPO_ROOT / "dist" / dist_name
|
||||||
@@ -7,6 +7,10 @@ import subprocess
|
|||||||
import sys
|
import sys
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
|
_BUILD_ROOT = Path(__file__).resolve().parents[1]
|
||||||
|
if str(_BUILD_ROOT) not in sys.path:
|
||||||
|
sys.path.insert(0, str(_BUILD_ROOT))
|
||||||
|
|
||||||
|
|
||||||
def _load_toml(text: str) -> dict:
|
def _load_toml(text: str) -> dict:
|
||||||
if sys.version_info >= (3, 11):
|
if sys.version_info >= (3, 11):
|
||||||
@@ -90,7 +94,7 @@ def bundle_items(repo_root: Path) -> list[dict]:
|
|||||||
|
|
||||||
def bundle_data_files(repo_root: Path) -> list[tuple[str, list[str]]]:
|
def bundle_data_files(repo_root: Path) -> list[tuple[str, list[str]]]:
|
||||||
"""Map bundle items marked dev_install=true to setuptools data_files entries."""
|
"""Map bundle items marked dev_install=true to setuptools data_files entries."""
|
||||||
from wheel_layout import resolve_bundle_source_files
|
from wheel.layout import resolve_bundle_source_files
|
||||||
|
|
||||||
grouped: dict[str, list[str]] = {}
|
grouped: dict[str, list[str]] = {}
|
||||||
for item in bundle_items(repo_root):
|
for item in bundle_items(repo_root):
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
"""Post-build smoke tests for wheels and product GUI folders."""
|
||||||
|
|
||||||
|
from common.verify.gui import GUI_PRODUCTS, verify_gui_dist
|
||||||
|
from common.verify.wheel import verify_built_wheel
|
||||||
|
|
||||||
|
__all__ = ["GUI_PRODUCTS", "verify_built_wheel", "verify_gui_dist"]
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
"""CLI for packaged artifact verification."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from common.verify import GUI_PRODUCTS, verify_built_wheel, verify_gui_dist
|
||||||
|
|
||||||
|
|
||||||
|
def main(argv: list[str] | None = None) -> int:
|
||||||
|
parser = argparse.ArgumentParser(description="Verify packaged Orisys artifacts.")
|
||||||
|
parser.add_argument("--gui-dist", action="store_true", help="Verify a GUI dist folder")
|
||||||
|
parser.add_argument(
|
||||||
|
"--product",
|
||||||
|
choices=sorted(GUI_PRODUCTS),
|
||||||
|
default="ory_finger",
|
||||||
|
help="GUI product profile (default: ory_finger)",
|
||||||
|
)
|
||||||
|
parser.add_argument("--dist-dir", type=Path, default=None, help="Override GUI dist folder")
|
||||||
|
parser.add_argument("wheel", nargs="?", type=Path, help="Wheel path for import smoke test")
|
||||||
|
args = parser.parse_args(argv)
|
||||||
|
|
||||||
|
if args.gui_dist:
|
||||||
|
verify_gui_dist(args.dist_dir, product=args.product)
|
||||||
|
return 0
|
||||||
|
if args.wheel is None:
|
||||||
|
parser.error("Provide a wheel path or pass --gui-dist")
|
||||||
|
verify_built_wheel(args.wheel)
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
raise SystemExit(main())
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
"""Smoke tests for standalone GUI product folders."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import subprocess
|
||||||
|
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 GUI_DIST_DIR, gui_dist_dir
|
||||||
|
|
||||||
|
# Product profiles for GUI packaging verification.
|
||||||
|
GUI_PRODUCTS: dict[str, dict] = {
|
||||||
|
"ory_finger": {
|
||||||
|
"dist_name": "OrisysDashboard",
|
||||||
|
"exe_name": "OrisysDashboard.exe",
|
||||||
|
"required_relpaths": [
|
||||||
|
"config/dd02-ov.json",
|
||||||
|
"assets/finger/30fps-0703/mesh.npz",
|
||||||
|
"assets/finger/30fps-0703/world_to_cam.npy",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
"ory_mini": {
|
||||||
|
"dist_name": "OryMini",
|
||||||
|
"exe_name": "OryMini.exe",
|
||||||
|
"required_relpaths": [
|
||||||
|
"config/mini.json",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
"ory_box": {
|
||||||
|
"dist_name": "OryBox",
|
||||||
|
"exe_name": "OryBox.exe",
|
||||||
|
"required_relpaths": [
|
||||||
|
"config/ddjx01.json",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def verify_gui_dist(
|
||||||
|
dist_dir: Path | None = None,
|
||||||
|
*,
|
||||||
|
product: str = "ory_finger",
|
||||||
|
) -> None:
|
||||||
|
"""Smoke-test a standalone GUI folder for the given product."""
|
||||||
|
if product not in GUI_PRODUCTS:
|
||||||
|
raise ValueError(f"Unknown GUI product {product!r}; choose from {sorted(GUI_PRODUCTS)}")
|
||||||
|
|
||||||
|
profile = GUI_PRODUCTS[product]
|
||||||
|
if dist_dir is None:
|
||||||
|
dist_dir = gui_dist_dir(profile["dist_name"]) if product != "ory_finger" else GUI_DIST_DIR
|
||||||
|
dist_dir = dist_dir.resolve()
|
||||||
|
|
||||||
|
exe = dist_dir / profile["exe_name"]
|
||||||
|
if not exe.is_file():
|
||||||
|
raise FileNotFoundError(f"GUI executable not found: {exe}")
|
||||||
|
|
||||||
|
required = [dist_dir / rel for rel in profile["required_relpaths"]]
|
||||||
|
missing = [str(path.relative_to(dist_dir)) for path in required if not path.is_file()]
|
||||||
|
if missing:
|
||||||
|
raise FileNotFoundError(f"Missing bundled files in {dist_dir}: {', '.join(missing)}")
|
||||||
|
|
||||||
|
subprocess.check_call([str(exe), "--help"], cwd=dist_dir)
|
||||||
|
print(f"GUI dist smoke test OK ({product}): {exe}")
|
||||||
@@ -1,16 +1,13 @@
|
|||||||
"""Post-build smoke tests for packaged wheels and standalone GUI builds."""
|
"""Isolated venv smoke tests for packaged orisys wheels."""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import argparse
|
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
import tempfile
|
import tempfile
|
||||||
import venv
|
import venv
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from paths import GUI_DIST_DIR
|
|
||||||
|
|
||||||
# Public SDK surface that must import from a released wheel.
|
# Public SDK surface that must import from a released wheel.
|
||||||
_SMOKE_IMPORT_SCRIPT = """
|
_SMOKE_IMPORT_SCRIPT = """
|
||||||
import orisys
|
import orisys
|
||||||
@@ -59,42 +56,3 @@ def verify_built_wheel(wheel_path: Path) -> None:
|
|||||||
[python, "-m", "pip", "install", "-q", str(wheel_path)],
|
[python, "-m", "pip", "install", "-q", str(wheel_path)],
|
||||||
)
|
)
|
||||||
subprocess.check_call([python, "-c", _SMOKE_IMPORT_SCRIPT])
|
subprocess.check_call([python, "-c", _SMOKE_IMPORT_SCRIPT])
|
||||||
|
|
||||||
|
|
||||||
def verify_gui_dist(dist_dir: Path | None = None) -> None:
|
|
||||||
"""Smoke-test a standalone GUI folder produced by build_gui_win.py."""
|
|
||||||
dist_dir = (dist_dir or GUI_DIST_DIR).resolve()
|
|
||||||
exe = dist_dir / "OrisysDashboard.exe"
|
|
||||||
if not exe.is_file():
|
|
||||||
raise FileNotFoundError(f"GUI executable not found: {exe}")
|
|
||||||
|
|
||||||
required = [
|
|
||||||
dist_dir / "config" / "dd02-ov.json",
|
|
||||||
dist_dir / "assets" / "finger" / "visualization" / "world_to_cam.npy",
|
|
||||||
dist_dir / "assets" / "finger" / "30fps-0703" / "mesh.npz",
|
|
||||||
]
|
|
||||||
missing = [str(path.relative_to(dist_dir)) for path in required if not path.is_file()]
|
|
||||||
if missing:
|
|
||||||
raise FileNotFoundError(f"Missing bundled files in {dist_dir}: {', '.join(missing)}")
|
|
||||||
|
|
||||||
subprocess.check_call([str(exe), "--help"], cwd=dist_dir)
|
|
||||||
print(f"GUI dist smoke test OK: {exe}")
|
|
||||||
|
|
||||||
|
|
||||||
def main() -> None:
|
|
||||||
parser = argparse.ArgumentParser(description="Verify packaged Orisys artifacts.")
|
|
||||||
parser.add_argument("--gui-dist", action="store_true", help="Verify dist/OrisysDashboard/")
|
|
||||||
parser.add_argument("--dist-dir", type=Path, default=None, help="Override GUI dist folder")
|
|
||||||
parser.add_argument("wheel", nargs="?", type=Path, help="Wheel path for import smoke test")
|
|
||||||
args = parser.parse_args()
|
|
||||||
|
|
||||||
if args.gui_dist:
|
|
||||||
verify_gui_dist(args.dist_dir)
|
|
||||||
return
|
|
||||||
if args.wheel is None:
|
|
||||||
parser.error("Provide a wheel path or pass --gui-dist")
|
|
||||||
verify_built_wheel(args.wheel)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
||||||
@@ -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,
|
||||||
|
)
|
||||||
+104
@@ -0,0 +1,104 @@
|
|||||||
|
"""Unified Orisys packaging entrypoint.
|
||||||
|
|
||||||
|
Usage (from repo root):
|
||||||
|
|
||||||
|
python build/run.py wheel [--target internal|pypi-linux] [--verify]
|
||||||
|
python build/run.py wheel-win
|
||||||
|
python build/run.py wheel-manylinux [PowerShell args...]
|
||||||
|
python build/run.py gui finger|mini|box
|
||||||
|
python build/run.py verify [wheel.whl]
|
||||||
|
python build/run.py verify --gui-dist [--product ory_finger]
|
||||||
|
|
||||||
|
See build/README.md for details.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
_BUILD_ROOT = Path(__file__).resolve().parent
|
||||||
|
_REPO_ROOT = _BUILD_ROOT.parent
|
||||||
|
if str(_BUILD_ROOT) not in sys.path:
|
||||||
|
sys.path.insert(0, str(_BUILD_ROOT))
|
||||||
|
|
||||||
|
|
||||||
|
def _run_python(script: Path, argv: list[str]) -> int:
|
||||||
|
cmd = [sys.executable, str(script), *argv]
|
||||||
|
return subprocess.call(cmd, cwd=_REPO_ROOT)
|
||||||
|
|
||||||
|
|
||||||
|
def _cmd_wheel(argv: list[str]) -> int:
|
||||||
|
return _run_python(_BUILD_ROOT / "wheel" / "build.py", argv)
|
||||||
|
|
||||||
|
|
||||||
|
def _cmd_wheel_win(argv: list[str]) -> int:
|
||||||
|
bat = _BUILD_ROOT / "wheel" / "win.bat"
|
||||||
|
return subprocess.call(["cmd", "/c", str(bat), *argv], cwd=_REPO_ROOT)
|
||||||
|
|
||||||
|
|
||||||
|
def _cmd_manylinux(argv: list[str]) -> int:
|
||||||
|
ps1 = _BUILD_ROOT / "wheel" / "docker" / "run.ps1"
|
||||||
|
cmd = [
|
||||||
|
"powershell",
|
||||||
|
"-ExecutionPolicy",
|
||||||
|
"Bypass",
|
||||||
|
"-File",
|
||||||
|
str(ps1),
|
||||||
|
*argv,
|
||||||
|
]
|
||||||
|
return subprocess.call(cmd, cwd=_REPO_ROOT)
|
||||||
|
|
||||||
|
|
||||||
|
def _cmd_gui(argv: list[str]) -> int:
|
||||||
|
parser = argparse.ArgumentParser(prog="build/run.py gui")
|
||||||
|
parser.add_argument(
|
||||||
|
"product",
|
||||||
|
choices=("finger", "mini", "box", "ory_finger", "ory_mini", "ory_box"),
|
||||||
|
help="GUI product to package",
|
||||||
|
)
|
||||||
|
args, rest = parser.parse_known_args(argv)
|
||||||
|
product = {
|
||||||
|
"finger": "ory_finger",
|
||||||
|
"mini": "ory_mini",
|
||||||
|
"box": "ory_box",
|
||||||
|
}.get(args.product, args.product)
|
||||||
|
return _run_python(_BUILD_ROOT / "gui" / product / "build.py", rest)
|
||||||
|
|
||||||
|
|
||||||
|
def _cmd_verify(argv: list[str]) -> int:
|
||||||
|
from common.verify.cli import main as verify_main
|
||||||
|
|
||||||
|
return verify_main(argv)
|
||||||
|
|
||||||
|
|
||||||
|
def main(argv: list[str] | None = None) -> int:
|
||||||
|
argv = list(sys.argv[1:] if argv is None else argv)
|
||||||
|
if not argv or argv[0] in ("-h", "--help"):
|
||||||
|
print(__doc__)
|
||||||
|
print("Commands: wheel | wheel-win | manylinux | gui | verify")
|
||||||
|
print("Examples:")
|
||||||
|
print(" python build/run.py wheel --help")
|
||||||
|
print(" python build/run.py gui finger")
|
||||||
|
print(" python build/run.py manylinux -PythonVersions 3.10")
|
||||||
|
return 0
|
||||||
|
|
||||||
|
command, rest = argv[0], argv[1:]
|
||||||
|
dispatch = {
|
||||||
|
"wheel": _cmd_wheel,
|
||||||
|
"wheel-win": _cmd_wheel_win,
|
||||||
|
"wheel-manylinux": _cmd_manylinux,
|
||||||
|
"gui": _cmd_gui,
|
||||||
|
"verify": _cmd_verify,
|
||||||
|
}
|
||||||
|
if command not in dispatch:
|
||||||
|
print(f"Unknown command: {command!r}", file=sys.stderr)
|
||||||
|
print("Choose from: wheel, wheel-win, manylinux, gui, verify", file=sys.stderr)
|
||||||
|
return 2
|
||||||
|
return dispatch[command](rest)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
raise SystemExit(main())
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
"""Nuitka-compiled SDK wheel packaging."""
|
||||||
@@ -11,8 +11,9 @@ Targets:
|
|||||||
pypi-linux – PyPI binary wheel; requires Linux + CPython 3.10/3.11/3.12 + compliant tag.
|
pypi-linux – PyPI binary wheel; requires Linux + CPython 3.10/3.11/3.12 + compliant tag.
|
||||||
|
|
||||||
Run from repo root:
|
Run from repo root:
|
||||||
python build/build_nuitka.py
|
python build/run.py wheel
|
||||||
python build/build_nuitka.py --target pypi-linux
|
python build/run.py wheel --target pypi-linux
|
||||||
|
python build/wheel/build.py --target pypi-linux
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
@@ -24,18 +25,22 @@ import subprocess
|
|||||||
import sys
|
import sys
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from nuitka_compile import compile_orisys, copy_compiled_orisys, install_nuitka
|
_BUILD_ROOT = Path(__file__).resolve().parents[1]
|
||||||
from paths import NUITKA_COMPILE_DIR, NUITKA_WHEEL_DIR, REPO_ROOT, repaired_wheel_dir, repo_path
|
if str(_BUILD_ROOT) not in sys.path:
|
||||||
from pyproject_utils import bundle_items, extras_require, get_version, install_requires
|
sys.path.insert(0, str(_BUILD_ROOT))
|
||||||
from verify import verify_built_wheel
|
|
||||||
from wheel_build import (
|
from common.paths import NUITKA_COMPILE_DIR, NUITKA_WHEEL_DIR, REPO_ROOT, repaired_wheel_dir, repo_path
|
||||||
|
from common.pyproject_utils import bundle_items, extras_require, get_version, install_requires
|
||||||
|
from common.verify.wheel import verify_built_wheel
|
||||||
|
from wheel.compile import compile_orisys, copy_compiled_orisys, install_nuitka
|
||||||
|
from wheel.layout import copy_bundle_items, copy_orisys_blend, package_data_from_bundle
|
||||||
|
from wheel.package import (
|
||||||
is_compliant_manylinux_wheel,
|
is_compliant_manylinux_wheel,
|
||||||
publish_wheel_to_sdk,
|
publish_wheel_to_sdk,
|
||||||
repair_linux_wheel,
|
repair_linux_wheel,
|
||||||
run_bdist_wheel,
|
run_bdist_wheel,
|
||||||
write_wheel_setup,
|
write_wheel_setup,
|
||||||
)
|
)
|
||||||
from wheel_layout import copy_bundle_items, copy_orisys_blend, package_data_from_bundle
|
|
||||||
|
|
||||||
VALID_TARGETS = ("internal", "pypi-linux")
|
VALID_TARGETS = ("internal", "pypi-linux")
|
||||||
# ABI tags that manylinux images ship under /opt/python/cpXYY-cpXYY.
|
# ABI tags that manylinux images ship under /opt/python/cpXYY-cpXYY.
|
||||||
@@ -8,7 +8,11 @@ import sys
|
|||||||
from importlib.machinery import EXTENSION_SUFFIXES
|
from importlib.machinery import EXTENSION_SUFFIXES
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from paths import repo_path
|
_BUILD_ROOT = Path(__file__).resolve().parents[1]
|
||||||
|
if str(_BUILD_ROOT) not in sys.path:
|
||||||
|
sys.path.insert(0, str(_BUILD_ROOT))
|
||||||
|
|
||||||
|
from common.paths import repo_path
|
||||||
|
|
||||||
NUITKA_INCLUDE_PACKAGES = (
|
NUITKA_INCLUDE_PACKAGES = (
|
||||||
"orisys",
|
"orisys",
|
||||||
@@ -23,4 +23,4 @@ RUN for PYBIN in \
|
|||||||
done
|
done
|
||||||
|
|
||||||
WORKDIR /src
|
WORKDIR /src
|
||||||
ENTRYPOINT ["/bin/bash", "/src/build/manylinux_entrypoint.sh"]
|
ENTRYPOINT ["/bin/bash", "/src/build/wheel/docker/entrypoint.sh"]
|
||||||
@@ -42,7 +42,7 @@ for tag in "${TAG_ARRAY[@]}"; do
|
|||||||
|
|
||||||
"${PYBIN}/python" -m pip install -q --upgrade pip setuptools wheel auditwheel
|
"${PYBIN}/python" -m pip install -q --upgrade pip setuptools wheel auditwheel
|
||||||
|
|
||||||
if "${PYBIN}/python" build/build_nuitka.py --target "${TARGET}"; then
|
if "${PYBIN}/python" build/wheel/build.py --target "${TARGET}"; then
|
||||||
BUILT+=("${tag}")
|
BUILT+=("${tag}")
|
||||||
else
|
else
|
||||||
echo "[ERROR] Build failed for ${tag}" >&2
|
echo "[ERROR] Build failed for ${tag}" >&2
|
||||||
@@ -1,17 +1,11 @@
|
|||||||
# Build PyPI-compliant manylinux wheels for CPython 3.10/3.11/3.12 using Docker Desktop.
|
# Build PyPI-compliant manylinux wheels for CPython 3.10/3.11/3.12 using Docker Desktop.
|
||||||
#
|
#
|
||||||
# Usage (from repo root, PowerShell):
|
# Usage (from repo root, PowerShell):
|
||||||
# .\build\build_manylinux_docker.ps1
|
# .\build\wheel\docker\run.ps1
|
||||||
# .\build\build_manylinux_docker.ps1 -PythonVersions 3.10,3.11,3.12
|
# .\build\build_manylinux_docker.ps1 # compatibility wrapper
|
||||||
# .\build\build_manylinux_docker.ps1 -PythonVersions 3.11
|
|
||||||
# .\build\build_manylinux_docker.ps1 -SkipImageBuild
|
|
||||||
#
|
#
|
||||||
# From cmd.exe:
|
# From cmd.exe:
|
||||||
# powershell -ExecutionPolicy Bypass -File .\build\build_manylinux_docker.ps1
|
# powershell -ExecutionPolicy Bypass -File .\build\build_manylinux_docker.ps1
|
||||||
#
|
|
||||||
# Prerequisites:
|
|
||||||
# - Docker Desktop running in Linux container mode
|
|
||||||
# - Enough disk for the manylinux image (~2GB+)
|
|
||||||
|
|
||||||
param(
|
param(
|
||||||
[ValidateSet("pypi-linux", "internal")]
|
[ValidateSet("pypi-linux", "internal")]
|
||||||
@@ -39,7 +33,7 @@ function Convert-ToManylinuxTag {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$RepoRoot = (Resolve-Path (Join-Path $PSScriptRoot "..")).Path
|
$RepoRoot = (Resolve-Path (Join-Path $PSScriptRoot "..\..\..")).Path
|
||||||
Set-Location $RepoRoot
|
Set-Location $RepoRoot
|
||||||
|
|
||||||
$tags = @()
|
$tags = @()
|
||||||
@@ -67,15 +61,17 @@ if ($dockerInfo -ne "linux") {
|
|||||||
throw "Docker server OS is '$dockerInfo'. Switch Docker Desktop to Linux containers."
|
throw "Docker server OS is '$dockerInfo'. Switch Docker Desktop to Linux containers."
|
||||||
}
|
}
|
||||||
|
|
||||||
$entrypoint = Join-Path $RepoRoot "build\manylinux_entrypoint.sh"
|
$entrypoint = Join-Path $RepoRoot "build\wheel\docker\entrypoint.sh"
|
||||||
# Normalize CRLF -> LF so bash in the container can execute the script.
|
# Normalize CRLF -> LF so bash in the container can execute the script.
|
||||||
$raw = [System.IO.File]::ReadAllText($entrypoint)
|
$raw = [System.IO.File]::ReadAllText($entrypoint)
|
||||||
$normalized = $raw -replace "`r`n", "`n" -replace "`r", "`n"
|
$normalized = $raw -replace "`r`n", "`n" -replace "`r", "`n"
|
||||||
[System.IO.File]::WriteAllText($entrypoint, $normalized)
|
[System.IO.File]::WriteAllText($entrypoint, $normalized)
|
||||||
|
|
||||||
|
$dockerfile = "build/wheel/docker/Dockerfile.manylinux"
|
||||||
|
|
||||||
if (-not $SkipImageBuild) {
|
if (-not $SkipImageBuild) {
|
||||||
Write-Host "`n=== Building Docker image ==="
|
Write-Host "`n=== Building Docker image ==="
|
||||||
docker build -f build/Dockerfile.manylinux -t $ImageName .
|
docker build -f $dockerfile -t $ImageName .
|
||||||
if ($LASTEXITCODE -ne 0) {
|
if ($LASTEXITCODE -ne 0) {
|
||||||
throw "docker build failed"
|
throw "docker build failed"
|
||||||
}
|
}
|
||||||
@@ -8,7 +8,11 @@ import subprocess
|
|||||||
import sys
|
import sys
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from paths import sdk_dir
|
_BUILD_ROOT = Path(__file__).resolve().parents[1]
|
||||||
|
if str(_BUILD_ROOT) not in sys.path:
|
||||||
|
sys.path.insert(0, str(_BUILD_ROOT))
|
||||||
|
|
||||||
|
from common.paths import sdk_dir
|
||||||
|
|
||||||
# PyPI rejects bare tags like "manylinux_x86_64"; require a glibc ABI version.
|
# PyPI rejects bare tags like "manylinux_x86_64"; require a glibc ABI version.
|
||||||
_MANYLINUX_TAG_RE = re.compile(
|
_MANYLINUX_TAG_RE = re.compile(
|
||||||
@@ -3,7 +3,7 @@ REM Build Nuitka wheels for Python 3.10, 3.11, and 3.12.
|
|||||||
REM Expects conda envs named py310, py311, py312 (or set CONDA_ENVS_ROOT).
|
REM Expects conda envs named py310, py311, py312 (or set CONDA_ENVS_ROOT).
|
||||||
setlocal enabledelayedexpansion
|
setlocal enabledelayedexpansion
|
||||||
|
|
||||||
cd /d "%~dp0\.."
|
cd /d "%~dp0\..\.."
|
||||||
if errorlevel 1 (
|
if errorlevel 1 (
|
||||||
echo ERROR: Could not change to repository root.
|
echo ERROR: Could not change to repository root.
|
||||||
exit /b 1
|
exit /b 1
|
||||||
@@ -32,7 +32,7 @@ for %%E in (py310 py311 py312) do (
|
|||||||
|
|
||||||
set "OK=0"
|
set "OK=0"
|
||||||
if !HAS_CONDA! equ 1 (
|
if !HAS_CONDA! equ 1 (
|
||||||
call conda run --no-capture-output -n %%E python build\build_nuitka.py
|
call conda run --no-capture-output -n %%E python build\wheel\build.py
|
||||||
if not errorlevel 1 set "OK=1"
|
if not errorlevel 1 set "OK=1"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -44,7 +44,7 @@ for %%E in (py310 py311 py312) do (
|
|||||||
if not exist "!PYEXE!" (
|
if not exist "!PYEXE!" (
|
||||||
echo ERROR: Python not found: !PYEXE!
|
echo ERROR: Python not found: !PYEXE!
|
||||||
) else (
|
) else (
|
||||||
"!PYEXE!" build\build_nuitka.py
|
"!PYEXE!" build\wheel\build.py
|
||||||
if not errorlevel 1 set "OK=1"
|
if not errorlevel 1 set "OK=1"
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
+1
-1
@@ -31,7 +31,7 @@
|
|||||||
"dis_preset": "medium",
|
"dis_preset": "medium",
|
||||||
"dis_mode": 3,
|
"dis_mode": 3,
|
||||||
"algorithm_list": ["raft", "dis"],
|
"algorithm_list": ["raft", "dis"],
|
||||||
"hhd_downsample": 3
|
"hhd_downsample": 1
|
||||||
},
|
},
|
||||||
"contact": {
|
"contact": {
|
||||||
"min_contour_area": 3000,
|
"min_contour_area": 3000,
|
||||||
|
|||||||
@@ -31,7 +31,7 @@
|
|||||||
"dis_preset": "medium",
|
"dis_preset": "medium",
|
||||||
"dis_mode": 3,
|
"dis_mode": 3,
|
||||||
"algorithm_list": ["raft", "dis"],
|
"algorithm_list": ["raft", "dis"],
|
||||||
"hhd_downsample": 3,
|
"hhd_downsample": 1,
|
||||||
"backend": "cpu"
|
"backend": "cpu"
|
||||||
},
|
},
|
||||||
"contact": {
|
"contact": {
|
||||||
|
|||||||
+23
-19
@@ -40,35 +40,35 @@ docker run --rm hello-world
|
|||||||
|
|
||||||
## Build manylinux wheels (3.10 / 3.11 / 3.12)
|
## Build manylinux wheels (3.10 / 3.11 / 3.12)
|
||||||
|
|
||||||
From the repository root. Prefer PowerShell, or from **cmd.exe**:
|
From the repository root:
|
||||||
|
|
||||||
```bat
|
```bash
|
||||||
powershell -ExecutionPolicy Bypass -File .\build\build_manylinux_docker.ps1
|
python build/run.py manylinux
|
||||||
```
|
```
|
||||||
|
|
||||||
PowerShell:
|
Or call the Docker helper directly:
|
||||||
|
|
||||||
```powershell
|
```powershell
|
||||||
.\build\build_manylinux_docker.ps1
|
.\build\wheel\docker\run.ps1
|
||||||
```
|
```
|
||||||
|
|
||||||
Default builds **all three** ABIs. To build a subset:
|
Default builds **all three** ABIs. To build a subset:
|
||||||
|
|
||||||
```powershell
|
```powershell
|
||||||
.\build\build_manylinux_docker.ps1 -PythonVersions 3.10,3.12
|
python build/run.py manylinux -PythonVersions 3.10,3.12
|
||||||
.\build\build_manylinux_docker.ps1 -PythonVersions 3.11
|
python build/run.py manylinux -PythonVersions 3.11
|
||||||
```
|
```
|
||||||
|
|
||||||
What this does:
|
What this does:
|
||||||
|
|
||||||
1. Builds `orisys-manylinux` from [`build/Dockerfile.manylinux`](../build/Dockerfile.manylinux)
|
1. Builds `orisys-manylinux` from [`build/wheel/docker/Dockerfile.manylinux`](../build/wheel/docker/Dockerfile.manylinux)
|
||||||
(`quay.io/pypa/manylinux_2_28_x86_64` with Nuitka/auditwheel in cp310/cp311/cp312).
|
(`quay.io/pypa/manylinux_2_28_x86_64` with Nuitka/auditwheel in cp310/cp311/cp312).
|
||||||
2. Runs [`build/manylinux_entrypoint.sh`](../build/manylinux_entrypoint.sh), which loops:
|
2. Runs [`build/wheel/docker/entrypoint.sh`](../build/wheel/docker/entrypoint.sh), which loops:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
/opt/python/cp310-cp310/bin/python build/build_nuitka.py --target pypi-linux
|
/opt/python/cp310-cp310/bin/python build/wheel/build.py --target pypi-linux
|
||||||
/opt/python/cp311-cp311/bin/python build/build_nuitka.py --target pypi-linux
|
/opt/python/cp311-cp311/bin/python build/wheel/build.py --target pypi-linux
|
||||||
/opt/python/cp312-cp312/bin/python build/build_nuitka.py --target pypi-linux
|
/opt/python/cp312-cp312/bin/python build/wheel/build.py --target pypi-linux
|
||||||
```
|
```
|
||||||
|
|
||||||
3. For each ABI: Nuitka compile → wheel → `auditwheel repair` → isolated smoke test.
|
3. For each ABI: Nuitka compile → wheel → `auditwheel repair` → isolated smoke test.
|
||||||
@@ -82,13 +82,13 @@ Expect roughly **3×** the wall time of a single-ABI build.
|
|||||||
Rebuild without refreshing the image:
|
Rebuild without refreshing the image:
|
||||||
|
|
||||||
```powershell
|
```powershell
|
||||||
.\build\build_manylinux_docker.ps1 -SkipImageBuild
|
python build/run.py manylinux -SkipImageBuild
|
||||||
```
|
```
|
||||||
|
|
||||||
Equivalent manual `docker` invocation:
|
Equivalent manual `docker` invocation:
|
||||||
|
|
||||||
```powershell
|
```powershell
|
||||||
docker build -f build/Dockerfile.manylinux -t orisys-manylinux .
|
docker build -f build/wheel/docker/Dockerfile.manylinux -t orisys-manylinux .
|
||||||
docker run --rm `
|
docker run --rm `
|
||||||
-e ORISYS_BUILD_TARGET=pypi-linux `
|
-e ORISYS_BUILD_TARGET=pypi-linux `
|
||||||
-e ORISYS_PYTHON_TAGS=cp310-cp310,cp311-cp311,cp312-cp312 `
|
-e ORISYS_PYTHON_TAGS=cp310-cp310,cp311-cp311,cp312-cp312 `
|
||||||
@@ -106,7 +106,7 @@ docker run --rm `
|
|||||||
Optional re-verify on a Linux machine:
|
Optional re-verify on a Linux machine:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
python build/verify.py dist/repaired/orisys-*-cp310-cp310-manylinux_*.whl
|
python build/run.py verify dist/repaired/orisys-*-cp310-cp310-manylinux_*.whl
|
||||||
```
|
```
|
||||||
|
|
||||||
## Promote: TestPyPI then PyPI
|
## Promote: TestPyPI then PyPI
|
||||||
@@ -157,11 +157,15 @@ are not stored on developer machines.
|
|||||||
|
|
||||||
| Command | Purpose |
|
| Command | Purpose |
|
||||||
|---------|---------|
|
|---------|---------|
|
||||||
| `python build/build_nuitka.py` | Internal SDK wheel on the current host (Windows `.pyd` or Linux `.so`) |
|
| `python build/run.py wheel` | Preferred wheel builder launcher |
|
||||||
| `python build/build_nuitka.py --target pypi-linux` | Linux-only; requires manylinux + CPython 3.10–3.12 |
|
| `python build/wheel/build.py` | Direct wheel builder (current host) |
|
||||||
| `.\build\build_nuitka_win.bat` | Windows wheels for py310/py311/py312 conda envs |
|
| `python build/run.py wheel --target pypi-linux` | Linux-only; requires manylinux + CPython 3.10–3.12 |
|
||||||
| `.\build\build_manylinux_docker.ps1` | Linux manylinux wheels for 3.10/3.11/3.12 via Docker |
|
| `python build/run.py wheel-win` | Windows wheels for py310/py311/py312 conda envs |
|
||||||
|
| `python build/run.py manylinux` | manylinux Docker runner |
|
||||||
|
| `python build/run.py gui finger` | ORY-FINGER standalone GUI |
|
||||||
| `python -m build` | Pure-Python / sdist path from `pyproject.toml` (no Nuitka) |
|
| `python -m build` | Pure-Python / sdist path from `pyproject.toml` (no Nuitka) |
|
||||||
|
|
||||||
|
See also [`build/README.md`](../build/README.md) for the target-oriented layout.
|
||||||
|
|
||||||
Windows binary wheels and Linux manylinux wheels must be built on their respective
|
Windows binary wheels and Linux manylinux wheels must be built on their respective
|
||||||
platforms. Never rename a platform tag to bypass PyPI validation.
|
platforms. Never rename a platform tag to bypass PyPI validation.
|
||||||
|
|||||||
+1
-1
@@ -63,7 +63,7 @@ version = { attr = "orisys.__version__" }
|
|||||||
|
|
||||||
# Single source of truth for bundled non-code files.
|
# Single source of truth for bundled non-code files.
|
||||||
# - dev_install = true -> pip install / editable install (via setup.py)
|
# - dev_install = true -> pip install / editable install (via setup.py)
|
||||||
# - all items -> Nuitka wheel (build/wheel_layout.py)
|
# - all items -> Nuitka wheel (build/wheel/layout.py)
|
||||||
[tool.orisys.bundle]
|
[tool.orisys.bundle]
|
||||||
|
|
||||||
# Use patterns (globs) and/or files (explicit names under source/).
|
# Use patterns (globs) and/or files (explicit names under source/).
|
||||||
|
|||||||
@@ -8,6 +8,6 @@ from setuptools import setup
|
|||||||
REPO_ROOT = Path(__file__).resolve().parent
|
REPO_ROOT = Path(__file__).resolve().parent
|
||||||
sys.path.insert(0, str(REPO_ROOT / "build"))
|
sys.path.insert(0, str(REPO_ROOT / "build"))
|
||||||
|
|
||||||
from pyproject_utils import bundle_data_files
|
from common.pyproject_utils import bundle_data_files
|
||||||
|
|
||||||
setup(data_files=bundle_data_files(REPO_ROOT))
|
setup(data_files=bundle_data_files(REPO_ROOT))
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ def packaging_utils():
|
|||||||
build_dir = REPO_ROOT / "build"
|
build_dir = REPO_ROOT / "build"
|
||||||
if str(build_dir) not in sys.path:
|
if str(build_dir) not in sys.path:
|
||||||
sys.path.insert(0, str(build_dir))
|
sys.path.insert(0, str(build_dir))
|
||||||
from pyproject_utils import extras_require, install_requires, project_metadata
|
from common.pyproject_utils import extras_require, install_requires, project_metadata
|
||||||
|
|
||||||
return install_requires, extras_require, project_metadata
|
return install_requires, extras_require, project_metadata
|
||||||
|
|
||||||
@@ -33,7 +33,7 @@ def test_pyproject_dynamic_version(packaging_utils):
|
|||||||
build_dir = REPO_ROOT / "build"
|
build_dir = REPO_ROOT / "build"
|
||||||
if str(build_dir) not in sys.path:
|
if str(build_dir) not in sys.path:
|
||||||
sys.path.insert(0, str(build_dir))
|
sys.path.insert(0, str(build_dir))
|
||||||
from pyproject_utils import get_version
|
from common.pyproject_utils import get_version
|
||||||
|
|
||||||
assert get_version(REPO_ROOT) == "0.4.2"
|
assert get_version(REPO_ROOT) == "0.4.2"
|
||||||
|
|
||||||
@@ -44,7 +44,7 @@ def test_pyproject_bundle_manifest(packaging_utils):
|
|||||||
build_dir = REPO_ROOT / "build"
|
build_dir = REPO_ROOT / "build"
|
||||||
if str(build_dir) not in sys.path:
|
if str(build_dir) not in sys.path:
|
||||||
sys.path.insert(0, str(build_dir))
|
sys.path.insert(0, str(build_dir))
|
||||||
from pyproject_utils import bundle_data_files, bundle_items
|
from common.pyproject_utils import bundle_data_files, bundle_items
|
||||||
|
|
||||||
items = bundle_items(REPO_ROOT)
|
items = bundle_items(REPO_ROOT)
|
||||||
names = {item["name"] for item in items}
|
names = {item["name"] for item in items}
|
||||||
@@ -69,7 +69,7 @@ def test_bundle_explicit_files():
|
|||||||
build_dir = REPO_ROOT / "build"
|
build_dir = REPO_ROOT / "build"
|
||||||
if str(build_dir) not in sys.path:
|
if str(build_dir) not in sys.path:
|
||||||
sys.path.insert(0, str(build_dir))
|
sys.path.insert(0, str(build_dir))
|
||||||
from wheel_layout import data_file_entries_for_item, resolve_bundle_source_files
|
from wheel.layout import data_file_entries_for_item, resolve_bundle_source_files
|
||||||
|
|
||||||
item = {
|
item = {
|
||||||
"name": "configs",
|
"name": "configs",
|
||||||
|
|||||||
+12
-12
@@ -6,37 +6,37 @@ REPO_ROOT = Path(__file__).resolve().parents[1]
|
|||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def wheel_build():
|
def wheel_package():
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
build_dir = REPO_ROOT / "build"
|
build_dir = REPO_ROOT / "build"
|
||||||
if str(build_dir) not in sys.path:
|
if str(build_dir) not in sys.path:
|
||||||
sys.path.insert(0, str(build_dir))
|
sys.path.insert(0, str(build_dir))
|
||||||
import wheel_build
|
from wheel import package as wheel_package
|
||||||
|
|
||||||
return wheel_build
|
return wheel_package
|
||||||
|
|
||||||
|
|
||||||
def test_compliant_manylinux_tags(wheel_build):
|
def test_compliant_manylinux_tags(wheel_package):
|
||||||
assert wheel_build.is_compliant_manylinux_wheel(
|
assert wheel_package.is_compliant_manylinux_wheel(
|
||||||
Path("orisys-0.4.2-cp310-cp310-manylinux_2_17_x86_64.whl")
|
Path("orisys-0.4.2-cp310-cp310-manylinux_2_17_x86_64.whl")
|
||||||
)
|
)
|
||||||
assert wheel_build.is_compliant_manylinux_wheel(
|
assert wheel_package.is_compliant_manylinux_wheel(
|
||||||
Path("orisys-0.4.2-cp310-cp310-manylinux2014_x86_64.whl")
|
Path("orisys-0.4.2-cp310-cp310-manylinux2014_x86_64.whl")
|
||||||
)
|
)
|
||||||
assert wheel_build.is_compliant_manylinux_wheel(
|
assert wheel_package.is_compliant_manylinux_wheel(
|
||||||
Path("orisys-0.4.2-cp310-cp310-manylinux_2_28_x86_64.whl")
|
Path("orisys-0.4.2-cp310-cp310-manylinux_2_28_x86_64.whl")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_rejects_illegal_manylinux_tags(wheel_build):
|
def test_rejects_illegal_manylinux_tags(wheel_package):
|
||||||
assert not wheel_build.is_compliant_manylinux_wheel(
|
assert not wheel_package.is_compliant_manylinux_wheel(
|
||||||
Path("orisys-0.4.2-cp310-cp310-manylinux_x86_64.whl")
|
Path("orisys-0.4.2-cp310-cp310-manylinux_x86_64.whl")
|
||||||
)
|
)
|
||||||
assert not wheel_build.is_compliant_manylinux_wheel(
|
assert not wheel_package.is_compliant_manylinux_wheel(
|
||||||
Path("orisys-0.4.2-cp310-cp310-linux_x86_64.whl")
|
Path("orisys-0.4.2-cp310-cp310-linux_x86_64.whl")
|
||||||
)
|
)
|
||||||
assert not wheel_build.is_compliant_manylinux_wheel(
|
assert not wheel_package.is_compliant_manylinux_wheel(
|
||||||
Path("orisys-0.4.2-cp310-cp310-win_amd64.whl")
|
Path("orisys-0.4.2-cp310-cp310-win_amd64.whl")
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -47,7 +47,7 @@ def test_pyproject_no_private_classifier():
|
|||||||
build_dir = REPO_ROOT / "build"
|
build_dir = REPO_ROOT / "build"
|
||||||
if str(build_dir) not in sys.path:
|
if str(build_dir) not in sys.path:
|
||||||
sys.path.insert(0, str(build_dir))
|
sys.path.insert(0, str(build_dir))
|
||||||
from pyproject_utils import project_metadata
|
from common.pyproject_utils import project_metadata
|
||||||
|
|
||||||
classifiers = project_metadata(REPO_ROOT).get("classifiers", [])
|
classifiers = project_metadata(REPO_ROOT).get("classifiers", [])
|
||||||
assert "Private :: Do Not Upload" not in classifiers
|
assert "Private :: Do Not Upload" not in classifiers
|
||||||
|
|||||||
Reference in New Issue
Block a user