Files
DBClient/build_app.py
2026-05-21 15:46:41 -04:00

121 lines
3.4 KiB
Python

#!/usr/bin/env python3
"""
build_app.py — DBClient packaging helper
Run this script to build a distributable bundle of DBClient using PyInstaller.
Usage
-----
python build_app.py [--onefile] [--clean] [--no-upx]
Options
-------
--onefile Build a single .exe instead of a one-folder bundle
--clean Delete build/ and dist/ before building
--no-upx Disable UPX compression (useful if UPX is not installed)
Output
------
dist/DBClient/ ← one-folder bundle (default)
dist/DBClient.exe ← single-file bundle (--onefile)
"""
import argparse
import os
import shutil
import subprocess
import sys
def parse_args():
p = argparse.ArgumentParser(description="Build DBClient with PyInstaller")
p.add_argument("--onefile", action="store_true",
help="Build a single .exe (instead of one-folder)")
p.add_argument("--clean", action="store_true",
help="Remove build/ and dist/ directories first")
p.add_argument("--no-upx", action="store_true",
help="Disable UPX compression")
return p.parse_args()
def check_pyinstaller():
try:
import PyInstaller # noqa: F401
except ImportError:
print("❌ PyInstaller is not installed.")
print(" Run: pip install pyinstaller")
sys.exit(1)
print(f"✅ PyInstaller found: {PyInstaller.__version__}")
def clean_dirs():
for d in ("build", "dist", "__pycache__"):
if os.path.exists(d):
print(f"🗑 Removing {d}/")
shutil.rmtree(d)
def build(args):
cmd = [sys.executable, "-m", "PyInstaller", "DBClient.spec", "--noconfirm"]
if args.onefile:
# Patch the spec to use the single-file EXE (quick-and-dirty approach:
# just pass --onefile to PyInstaller alongside the spec — PyInstaller
# will override the COLLECT step).
cmd.append("--onefile")
print("📦 Building single-file executable…")
else:
print("📦 Building one-folder bundle…")
if args.no_upx:
cmd.append("--noupx")
print(" Command:", " ".join(cmd))
print()
result = subprocess.run(cmd, check=False)
return result.returncode
def report(args, returncode):
print()
if returncode == 0:
if args.onefile:
out = os.path.join("dist", "DBClient.exe")
else:
out = os.path.join("dist", "DBClient")
print(f"✅ Build succeeded!")
print(f" Output: {os.path.abspath(out)}")
if not args.onefile:
print(f" Run: {os.path.join(out, 'DBClient.exe')}")
else:
print(f"❌ Build failed with exit code {returncode}.")
print(" Check the output above for errors.")
print()
print("Common fixes:")
print(" • pyodbc errors → ensure the ODBC runtime is installed")
print(" • Missing module → add it to hiddenimports in DBClient.spec")
print(" • UPX errors → run with --no-upx")
def main():
args = parse_args()
# Make sure we're running from the repo root
spec = "DBClient.spec"
if not os.path.exists(spec):
print(f"❌ {spec} not found. Run this script from the DBClient root directory.")
sys.exit(1)
check_pyinstaller()
if args.clean:
clean_dirs()
returncode = build(args)
report(args, returncode)
sys.exit(returncode)
if __name__ == "__main__":
main()