""" PyInstaller build script for Label Printer GUI Run this to create a standalone Windows executable. IMPORTANT: This script MUST be run on Windows to generate a Windows .exe file. If run on Linux/macOS, it will create a Linux/macOS binary that won't work on Windows. To build for Windows: 1. Copy this project to a Windows machine 2. Install dependencies: pip install -r requirements_windows.txt 3. Run this script: python build_exe.py 4. The Windows .exe will be created in the dist/ folder GhostScript bundling -------------------- If GhostScript is installed on this build machine the script will automatically copy the required files into conf\\ghostscript\\ before calling PyInstaller so they are embedded in LabelPrinter.exe. The target machine then needs NO separate GhostScript install. """ import os import sys import subprocess import shutil # Get the current directory script_dir = os.path.dirname(os.path.abspath(__file__)) def prepare_ghostscript(): """ Find the system GhostScript installation and copy the minimum files needed for bundling into conf\\ghostscript\\. Files copied: conf\\ghostscript\\bin\\gswin64c.exe (or 32-bit variant) conf\\ghostscript\\bin\\gsdll64.dll conf\\ghostscript\\lib\\*.ps (PostScript init files) Returns True if GhostScript was found and prepared, False otherwise. """ import glob gs_exe = None gs_dll = None gs_lib = None for pf in [r"C:\Program Files", r"C:\Program Files (x86)"]: gs_base = os.path.join(pf, "gs") if not os.path.isdir(gs_base): continue # Iterate versions newest-first for ver_dir in sorted(os.listdir(gs_base), reverse=True): bin_dir = os.path.join(gs_base, ver_dir, "bin") lib_dir = os.path.join(gs_base, ver_dir, "lib") if os.path.exists(os.path.join(bin_dir, "gswin64c.exe")): gs_exe = os.path.join(bin_dir, "gswin64c.exe") gs_dll = os.path.join(bin_dir, "gsdll64.dll") gs_lib = lib_dir break if os.path.exists(os.path.join(bin_dir, "gswin32c.exe")): gs_exe = os.path.join(bin_dir, "gswin32c.exe") gs_dll = os.path.join(bin_dir, "gsdll32.dll") gs_lib = lib_dir break if gs_exe: break if not gs_exe: print(" WARNING: GhostScript not found on this machine.") print(" The exe will still build but GhostScript will NOT be bundled.") print(" Install GhostScript from https://ghostscript.com/releases/gsdnld.html") print(" then rebuild for sharp vector-quality printing.") return False dest_bin = os.path.join("conf", "ghostscript", "bin") dest_lib = os.path.join("conf", "ghostscript", "lib") os.makedirs(dest_bin, exist_ok=True) os.makedirs(dest_lib, exist_ok=True) print(f" GhostScript found: {os.path.dirname(gs_exe)}") shutil.copy2(gs_exe, dest_bin) print(f" Copied: {os.path.basename(gs_exe)}") if os.path.exists(gs_dll): shutil.copy2(gs_dll, dest_bin) print(f" Copied: {os.path.basename(gs_dll)}") count = 0 for ps_file in glob.glob(os.path.join(gs_lib, "*.ps")): shutil.copy2(ps_file, dest_lib) count += 1 print(f" Copied {count} .ps init files from lib/") print(f" GhostScript prepared in conf\\ghostscript\\") return True if __name__ == '__main__': print("=" * 60) print("Label Printer GUI - PyInstaller Build") print("=" * 60) # Change to script directory so relative paths work os.chdir(script_dir) # Step 1: Prepare GhostScript for bundling (Windows only) if sys.platform == "win32": print("\n[1/2] Preparing GhostScript for bundling...") prepare_ghostscript() else: print("\n[1/2] Skipping GhostScript prep (non-Windows build machine)") # Step 2: Build with PyInstaller using the handcrafted spec file. # The spec's Python code auto-detects conf\\ghostscript\\ and includes it. print("\n[2/2] Building standalone executable via LabelPrinter.spec...") print("This may take a few minutes...\n") try: result = subprocess.run( ['pyinstaller', 'LabelPrinter.spec', '--distpath=./dist', '--workpath=./build', '-y'], check=True, ) print("\n" + "=" * 60) print("Build Complete!") print("=" * 60) print("\nExecutable location: ./dist/LabelPrinter.exe") print("\nBundled components:") print(" - GhostScript (vector-quality printing)") print(" - SumatraPDF (fallback printing)") print(" - SVG templates, conf files") print("\nYou can now:") print(" 1. Double-click LabelPrinter.exe to run") print(" 2. Copy the dist\\ folder to target machines") print(" 3. No extra software installation required on target machines") print("\nNote: First run may take a moment as Kivy initializes") except subprocess.CalledProcessError as e: print("\n" + "=" * 60) print("Build Failed!") print("=" * 60) print(f"\nError code: {e.returncode}") print("\nPlease check the error messages above for details.") sys.exit(1) except Exception as e: print(f"\nFatal error: {e}") sys.exit(1)