163 lines
4.6 KiB
Python
163 lines
4.6 KiB
Python
#!/usr/bin/env python3
|
|
"""Bundle pygount wheel into the project and update pyucc.spec.
|
|
|
|
Usage: run inside your project's virtualenv/checkout root:
|
|
python tools/bundle_pygount.py
|
|
|
|
What it does:
|
|
- Locates a local `pygount-*.whl` in the current Python environment (site-packages)
|
|
- Copies it to `pyucc/_internal/`
|
|
- Updates `pyucc.spec`, adding the wheel as a `datas` entry so PyInstaller bundles it
|
|
|
|
This enables the runtime fallback added in `pyucc.core.countings_impl` to import pygount
|
|
from a bundled wheel when running on an offline target.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import os
|
|
import sys
|
|
import shutil
|
|
import glob
|
|
from pathlib import Path
|
|
import sysconfig
|
|
import site
|
|
import re
|
|
|
|
|
|
REPO_ROOT = Path(__file__).resolve().parents[1]
|
|
SPEC_PATH = REPO_ROOT / "pyucc.spec"
|
|
DEST_DIR = REPO_ROOT / "pyucc" / "_internal"
|
|
|
|
|
|
def find_local_pygount_wheel() -> Path | None:
|
|
"""Search common site-packages locations for pygount-*.whl and return first match."""
|
|
candidates = []
|
|
|
|
# Preferred: current environment's site-packages (sysconfig)
|
|
try:
|
|
purelib = sysconfig.get_paths().get("purelib")
|
|
if purelib:
|
|
candidates.append(Path(purelib))
|
|
except Exception:
|
|
pass
|
|
|
|
# site.getsitepackages() may return multiple dirs
|
|
try:
|
|
for p in site.getsitepackages():
|
|
candidates.append(Path(p))
|
|
except Exception:
|
|
pass
|
|
|
|
# Fallback: sys.path entries
|
|
for p in sys.path:
|
|
try:
|
|
candidates.append(Path(p))
|
|
except Exception:
|
|
continue
|
|
|
|
# Also check repository-local wheel folder and current working dir
|
|
try:
|
|
candidates.append(REPO_ROOT / "wheels")
|
|
except Exception:
|
|
pass
|
|
try:
|
|
candidates.append(Path.cwd())
|
|
except Exception:
|
|
pass
|
|
try:
|
|
candidates.append(DEST_DIR)
|
|
except Exception:
|
|
pass
|
|
|
|
seen = set()
|
|
for base in candidates:
|
|
if not base:
|
|
continue
|
|
base = base.resolve()
|
|
if str(base) in seen:
|
|
continue
|
|
seen.add(str(base))
|
|
pattern = str(base / "pygount-*.whl")
|
|
for f in glob.glob(pattern):
|
|
if f:
|
|
return Path(f).resolve()
|
|
|
|
return None
|
|
|
|
|
|
def copy_wheel_to_internal(wheel_path: Path) -> Path:
|
|
DEST_DIR.mkdir(parents=True, exist_ok=True)
|
|
dest = DEST_DIR / wheel_path.name
|
|
shutil.copy2(wheel_path, dest)
|
|
return dest
|
|
|
|
|
|
def patch_spec_add_datas(spec_path: Path, wheel_rel_path: str) -> bool:
|
|
"""Insert a datas tuple ('<wheel_rel_path>', '_internal') into all datas=[...] lists in the spec.
|
|
|
|
Returns True if file modified.
|
|
"""
|
|
text = spec_path.read_text(encoding="utf-8")
|
|
tuple_entry = f"('{wheel_rel_path}', '_internal'),"
|
|
|
|
if tuple_entry in text:
|
|
print("Spec already contains wheel datas entry; nothing to do.")
|
|
return False
|
|
|
|
# Find all occurrences of 'datas=[' and insert the tuple after the opening bracket
|
|
new_text = text
|
|
inserts = 0
|
|
idx = 0
|
|
while True:
|
|
m = re.search(r"\bdatas\s*=\s*\[", new_text[idx:])
|
|
if not m:
|
|
break
|
|
# m.start() relative to new_text[idx:]
|
|
start = idx + m.end()
|
|
# Insert after start
|
|
new_text = new_text[:start] + tuple_entry + new_text[start:]
|
|
inserts += 1
|
|
idx = start + len(tuple_entry)
|
|
|
|
if inserts:
|
|
backup = spec_path.with_suffix(spec_path.suffix + ".bak")
|
|
spec_path.replace(backup)
|
|
spec_path.write_text(new_text, encoding="utf-8")
|
|
print(f"Patched {spec_path} - inserted datas entry in {inserts} places (backup saved to {backup}).")
|
|
return True
|
|
|
|
print(r"No datas=\[...] occurrences found in spec; no changes made.")
|
|
return False
|
|
|
|
|
|
def main():
|
|
print(f"Repo root: {REPO_ROOT}")
|
|
|
|
wheel = find_local_pygount_wheel()
|
|
if not wheel:
|
|
print("Could not locate a local pygount wheel in this environment.")
|
|
print("Please download a pygount wheel (pygount-<ver>-py3-none-any.whl) into your environment or site-packages.")
|
|
sys.exit(2)
|
|
|
|
print(f"Found pygount wheel: {wheel}")
|
|
|
|
dest = copy_wheel_to_internal(wheel)
|
|
print(f"Copied wheel to: {dest}")
|
|
|
|
# wheel_rel_path relative to project root (use forward slashes)
|
|
wheel_rel = f"pyucc/_internal/{dest.name}"
|
|
|
|
if not SPEC_PATH.exists():
|
|
print(f"Spec file not found at {SPEC_PATH}; please run this script from project root.")
|
|
sys.exit(3)
|
|
|
|
changed = patch_spec_add_datas(SPEC_PATH, wheel_rel)
|
|
if changed:
|
|
print("Specification updated. Rebuild using: pyinstaller pyucc.spec")
|
|
else:
|
|
print("Specification unchanged.")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|