121 lines
3.4 KiB
Python
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()
|