#!/usr/bin/env python3 """ Build script for creating standalone backend executable using PyInstaller. Uses --onedir mode for faster startup compared to --onefile. """ import subprocess import sys import os import shutil def clean_build_cache(script_dir): """Clean old build artifacts that may cause stale spec file issues.""" dirs_to_clean = [ os.path.join(script_dir, "build"), os.path.join(script_dir, "__pycache__"), ] files_to_clean = [ os.path.join(script_dir, "build", "backend.spec"), ] for f in files_to_clean: if os.path.exists(f): print(f"Removing old spec file: {f}") os.remove(f) for d in dirs_to_clean: pycache = os.path.join(d) if os.path.exists(pycache) and "__pycache__" in pycache: print(f"Removing cache: {pycache}") shutil.rmtree(pycache) def build(): """Build the backend executable.""" script_dir = os.path.dirname(os.path.abspath(__file__)) # Clean old build cache to avoid stale spec file issues clean_build_cache(script_dir) # PyInstaller command with --onedir for faster startup cmd = [ sys.executable, "-m", "PyInstaller", "--onedir", "--clean", # Clean PyInstaller cache before building "--name", "backend", "--distpath", "dist", "--workpath", "build", "--specpath", "build", # FastAPI and web framework "--hidden-import", "uvicorn", "--hidden-import", "uvicorn.logging", "--hidden-import", "uvicorn.loops", "--hidden-import", "uvicorn.loops.auto", "--hidden-import", "uvicorn.protocols", "--hidden-import", "uvicorn.protocols.http", "--hidden-import", "uvicorn.protocols.http.auto", "--hidden-import", "uvicorn.protocols.websockets", "--hidden-import", "uvicorn.protocols.websockets.auto", "--hidden-import", "uvicorn.lifespan", "--hidden-import", "uvicorn.lifespan.on", "--hidden-import", "uvicorn.lifespan.off", "--hidden-import", "fastapi", "--hidden-import", "starlette", "--hidden-import", "pydantic", "--hidden-import", "pydantic_core", # Database - MySQL "--hidden-import", "mysql.connector", "--hidden-import", "mysql.connector.pooling", # Database - SQLite (built-in, but ensure it's included) "--hidden-import", "sqlite3", # HTTP client "--hidden-import", "httpx", "--hidden-import", "httpcore", # Authentication "--hidden-import", "jose", "--hidden-import", "jose.jwt", "--hidden-import", "cryptography", # Excel export "--hidden-import", "openpyxl", # Multipart form handling "--hidden-import", "multipart", "--hidden-import", "python_multipart", # Environment loading "--hidden-import", "dotenv", # Timezone data "--hidden-import", "tzdata", # Application modules - only include modules that exist "--hidden-import", "app", "--hidden-import", "app.main", "--hidden-import", "app.config", "--hidden-import", "app.database", "--hidden-import", "app.routers", "--hidden-import", "app.routers.auth", "--hidden-import", "app.routers.meetings", "--hidden-import", "app.routers.ai", "--hidden-import", "app.routers.export", "--hidden-import", "app.models", "--hidden-import", "app.models.schemas", # Collect package data "--collect-data", "pydantic", "--collect-data", "uvicorn", "run_server.py" ] print("Building backend executable...") print(f"Command: {' '.join(cmd)}") result = subprocess.run(cmd, cwd=script_dir) if result.returncode != 0: print("\nBuild failed!") sys.exit(1) # Copy template directory to dist template_src = os.path.join(script_dir, "template") template_dst = os.path.join(script_dir, "dist", "backend", "template") if os.path.exists(template_src): print(f"\nCopying template directory to {template_dst}...") if os.path.exists(template_dst): shutil.rmtree(template_dst) shutil.copytree(template_src, template_dst) print("Template directory copied successfully.") else: print(f"\nWarning: Template directory not found at {template_src}") # Create empty record directory record_dst = os.path.join(script_dir, "dist", "backend", "record") os.makedirs(record_dst, exist_ok=True) print(f"Created record directory at {record_dst}") print("\nBuild successful!") print(f"Executable created at: dist/backend/backend.exe (Windows) or dist/backend/backend (Linux)") print("\nTo run:") print(" 1. Copy config.json to dist/backend/") print(" 2. Run: dist/backend/backend.exe (Windows) or ./dist/backend/backend (Linux)") if __name__ == "__main__": build()