Build Tool
This commit is contained in:
		
							
								
								
									
										272
									
								
								build.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										272
									
								
								build.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,272 @@
 | 
				
			|||||||
 | 
					#!/usr/bin/env python3
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import os
 | 
				
			||||||
 | 
					import sys
 | 
				
			||||||
 | 
					import subprocess
 | 
				
			||||||
 | 
					import platform
 | 
				
			||||||
 | 
					from pathlib import Path
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def clear_screen():
 | 
				
			||||||
 | 
					    os.system('cls' if os.name == 'nt' else 'clear')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def print_header():
 | 
				
			||||||
 | 
					    print("=" * 60)
 | 
				
			||||||
 | 
					    print("          PYINSTALLER AUTOMATION TOOL")
 | 
				
			||||||
 | 
					    print("=" * 60)
 | 
				
			||||||
 | 
					    print()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def get_script_path():
 | 
				
			||||||
 | 
					    while True:
 | 
				
			||||||
 | 
					        script_path = input("Enter the full path to the Python script: ").strip()
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        script_path = script_path.strip('"\'')
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        if not script_path:
 | 
				
			||||||
 | 
					            print("Path cannot be empty!")
 | 
				
			||||||
 | 
					            continue
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					        script_path = Path(script_path)
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        if not script_path.exists():
 | 
				
			||||||
 | 
					            print("File not found!")
 | 
				
			||||||
 | 
					            continue
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					        if script_path.suffix.lower() != '.py':
 | 
				
			||||||
 | 
					            print("File must have .py extension!")
 | 
				
			||||||
 | 
					            continue
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					        return script_path
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def select_mode():
 | 
				
			||||||
 | 
					    print("\nCOMPILATION MODE:")
 | 
				
			||||||
 | 
					    print("1. One-file (single file)")
 | 
				
			||||||
 | 
					    print("2. One-directory (folder with dependencies)")
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    while True:
 | 
				
			||||||
 | 
					        choice = input("\nChoose mode (1-2): ").strip()
 | 
				
			||||||
 | 
					        if choice == '1':
 | 
				
			||||||
 | 
					            return True
 | 
				
			||||||
 | 
					        elif choice == '2':
 | 
				
			||||||
 | 
					            return False
 | 
				
			||||||
 | 
					        print("Invalid option! Choose 1 or 2.")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def select_console():
 | 
				
			||||||
 | 
					    print("\nEXECUTION MODE:")
 | 
				
			||||||
 | 
					    print("1. With console (default)")
 | 
				
			||||||
 | 
					    print("2. Without console (GUI/windowed)")
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    while True:
 | 
				
			||||||
 | 
					        choice = input("\nChoose mode (1-2): ").strip()
 | 
				
			||||||
 | 
					        if choice == '1':
 | 
				
			||||||
 | 
					            return False
 | 
				
			||||||
 | 
					        elif choice == '2':
 | 
				
			||||||
 | 
					            return True
 | 
				
			||||||
 | 
					        print("Invalid option! Choose 1 or 2.")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def select_optimization():
 | 
				
			||||||
 | 
					    print("\nOPTIMIZATION:")
 | 
				
			||||||
 | 
					    print("1. No optimization (default)")
 | 
				
			||||||
 | 
					    print("2. Basic optimization (-O)")
 | 
				
			||||||
 | 
					    print("3. Maximum optimization (-OO)")
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    optimizations = {'1': None, '2': '-O', '3': '-OO'}
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    while True:
 | 
				
			||||||
 | 
					        choice = input("\nChoose optimization (1-3): ").strip()
 | 
				
			||||||
 | 
					        if choice in optimizations:
 | 
				
			||||||
 | 
					            return optimizations[choice]
 | 
				
			||||||
 | 
					        print("Invalid option! Choose 1-3.")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def select_icon():
 | 
				
			||||||
 | 
					    print("\nCUSTOM ICON:")
 | 
				
			||||||
 | 
					    print("1. No custom icon")
 | 
				
			||||||
 | 
					    print("2. Use custom icon")
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    while True:
 | 
				
			||||||
 | 
					        choice = input("\nChoose option (1-2): ").strip()
 | 
				
			||||||
 | 
					        if choice == '1':
 | 
				
			||||||
 | 
					            return None
 | 
				
			||||||
 | 
					        elif choice == '2':
 | 
				
			||||||
 | 
					            icon_path = input("Enter path to .ico (Windows) or .png (Linux/Mac) file: ").strip()
 | 
				
			||||||
 | 
					            icon_path = icon_path.strip('"\'')
 | 
				
			||||||
 | 
					            if Path(icon_path).exists():
 | 
				
			||||||
 | 
					                return icon_path
 | 
				
			||||||
 | 
					            else:
 | 
				
			||||||
 | 
					                print("Icon file not found!")
 | 
				
			||||||
 | 
					                continue
 | 
				
			||||||
 | 
					        print("Invalid option! Choose 1 or 2.")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def select_additional_files():
 | 
				
			||||||
 | 
					    print("\nADDITIONAL FILES:")
 | 
				
			||||||
 | 
					    print("1. Do not add files")
 | 
				
			||||||
 | 
					    print("2. Add files/directories")
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    while True:
 | 
				
			||||||
 | 
					        choice = input("\nChoose option (1-2): ").strip()
 | 
				
			||||||
 | 
					        if choice == '1':
 | 
				
			||||||
 | 
					            return []
 | 
				
			||||||
 | 
					        elif choice == '2':
 | 
				
			||||||
 | 
					            files = []
 | 
				
			||||||
 | 
					            while True:
 | 
				
			||||||
 | 
					                file_path = input("Enter file/directory path (or Enter to finish): ").strip()
 | 
				
			||||||
 | 
					                if not file_path:
 | 
				
			||||||
 | 
					                    break
 | 
				
			||||||
 | 
					                file_path = file_path.strip('"\'')
 | 
				
			||||||
 | 
					                if Path(file_path).exists():
 | 
				
			||||||
 | 
					                    files.append(file_path)
 | 
				
			||||||
 | 
					                    print(f"File added: {file_path}")
 | 
				
			||||||
 | 
					                else:
 | 
				
			||||||
 | 
					                    print("File not found!")
 | 
				
			||||||
 | 
					            return files
 | 
				
			||||||
 | 
					        print("Invalid option! Choose 1 or 2.")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def select_upx():
 | 
				
			||||||
 | 
					    print("\nUPX COMPRESSION:")
 | 
				
			||||||
 | 
					    print("1. Enable UPX compression (smaller files)")
 | 
				
			||||||
 | 
					    print("2. Disable UPX compression")
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    while True:
 | 
				
			||||||
 | 
					        choice = input("\nChoose option (1-2): ").strip()
 | 
				
			||||||
 | 
					        if choice == '1':
 | 
				
			||||||
 | 
					            return False
 | 
				
			||||||
 | 
					        elif choice == '2':
 | 
				
			||||||
 | 
					            return True
 | 
				
			||||||
 | 
					        print("Invalid option! Choose 1 or 2.")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def build_command(script_path, onefile, windowed, optimization, icon, additional_files, noupx):
 | 
				
			||||||
 | 
					    cmd = ['pyinstaller']
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    if onefile:
 | 
				
			||||||
 | 
					        cmd.append('--onefile')
 | 
				
			||||||
 | 
					    else:
 | 
				
			||||||
 | 
					        cmd.append('--onedir')
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    if windowed:
 | 
				
			||||||
 | 
					        cmd.append('--windowed')
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    if icon:
 | 
				
			||||||
 | 
					        cmd.extend(['--icon', icon])
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    if optimization:
 | 
				
			||||||
 | 
					        cmd.extend(['--python-option', optimization])
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    if noupx:
 | 
				
			||||||
 | 
					        cmd.append('--noupx')
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    for file in additional_files:
 | 
				
			||||||
 | 
					        if os.path.isdir(file):
 | 
				
			||||||
 | 
					            cmd.extend(['--add-data', f'{file}{os.pathsep}{file}'])
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            cmd.extend(['--add-data', f'{file}{os.pathsep}.'])
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    exe_name = script_path.stem
 | 
				
			||||||
 | 
					    cmd.extend(['--name', exe_name])
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    cmd.append(str(script_path))
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    return cmd
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def run_pyinstaller(command):
 | 
				
			||||||
 | 
					    print(f"\nExecuting: {' '.join(command)}")
 | 
				
			||||||
 | 
					    print("\nCompiling... This may take a few minutes...")
 | 
				
			||||||
 | 
					    print("-" * 60)
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    try:
 | 
				
			||||||
 | 
					        result = subprocess.run(command, check=True, capture_output=True, text=True)
 | 
				
			||||||
 | 
					        print("Compilation completed successfully!")
 | 
				
			||||||
 | 
					        return True
 | 
				
			||||||
 | 
					    except subprocess.CalledProcessError as e:
 | 
				
			||||||
 | 
					        print(f"Error during compilation:")
 | 
				
			||||||
 | 
					        if e.stderr:
 | 
				
			||||||
 | 
					            print(f"Stderr: {e.stderr}")
 | 
				
			||||||
 | 
					        if e.stdout:
 | 
				
			||||||
 | 
					            print(f"Stdout: {e.stdout}")
 | 
				
			||||||
 | 
					        return False
 | 
				
			||||||
 | 
					    except FileNotFoundError:
 | 
				
			||||||
 | 
					        print("PyInstaller not found! Make sure it is installed.")
 | 
				
			||||||
 | 
					        print("Install with: pip install pyinstaller")
 | 
				
			||||||
 | 
					        return False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def show_summary(script_path, onefile, windowed, optimization, icon, additional_files, output_dir, noupx):
 | 
				
			||||||
 | 
					    clear_screen()
 | 
				
			||||||
 | 
					    print_header()
 | 
				
			||||||
 | 
					    print("COMPILATION SUMMARY:")
 | 
				
			||||||
 | 
					    print(f"Script: {script_path}")
 | 
				
			||||||
 | 
					    print(f"Mode: {'One-file' if onefile else 'One-directory'}")
 | 
				
			||||||
 | 
					    print(f"Console: {'No' if windowed else 'Yes'}")
 | 
				
			||||||
 | 
					    print(f"Optimization: {optimization or 'None'}")
 | 
				
			||||||
 | 
					    print(f"Icon: {icon or 'Default'}")
 | 
				
			||||||
 | 
					    print(f"UPX: {'Disabled' if noupx else 'Enabled'}")
 | 
				
			||||||
 | 
					    print(f"Additional files: {len(additional_files)}")
 | 
				
			||||||
 | 
					    print(f"Output directory: {output_dir}")
 | 
				
			||||||
 | 
					    print("-" * 60)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def main():
 | 
				
			||||||
 | 
					    clear_screen()
 | 
				
			||||||
 | 
					    print_header()
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    try:
 | 
				
			||||||
 | 
					        subprocess.run(['pyinstaller', '--version'], capture_output=True, check=True)
 | 
				
			||||||
 | 
					    except (subprocess.CalledProcessError, FileNotFoundError):
 | 
				
			||||||
 | 
					        print("PyInstaller not found!")
 | 
				
			||||||
 | 
					        install = input("Do you want to install PyInstaller? (y/n): ").strip().lower()
 | 
				
			||||||
 | 
					        if install == 'y':
 | 
				
			||||||
 | 
					            subprocess.run([sys.executable, '-m', 'pip', 'install', 'pyinstaller'])
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            print("Exiting...")
 | 
				
			||||||
 | 
					            return
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    script_path = get_script_path()
 | 
				
			||||||
 | 
					    onefile = select_mode()
 | 
				
			||||||
 | 
					    windowed = select_console()
 | 
				
			||||||
 | 
					    optimization = select_optimization()
 | 
				
			||||||
 | 
					    icon = select_icon()
 | 
				
			||||||
 | 
					    additional_files = select_additional_files()
 | 
				
			||||||
 | 
					    noupx = select_upx()
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    output_dir = Path("dist")
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    show_summary(script_path, onefile, windowed, optimization, icon, additional_files, output_dir, noupx)
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    confirm = input("\nProceed with compilation? (y/n): ").strip().lower()
 | 
				
			||||||
 | 
					    if confirm != 'y':
 | 
				
			||||||
 | 
					        print("Compilation canceled!")
 | 
				
			||||||
 | 
					        return
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    command = build_command(script_path, onefile, windowed, optimization, icon, additional_files, noupx)
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    success = run_pyinstaller(command)
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    if success:
 | 
				
			||||||
 | 
					        print(f"\nCompilation completed!")
 | 
				
			||||||
 | 
					        print(f"Executable generated in: {output_dir}")
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        if onefile:
 | 
				
			||||||
 | 
					            exe_name = script_path.stem
 | 
				
			||||||
 | 
					            if os.name == 'nt':
 | 
				
			||||||
 | 
					                exe_name += '.exe'
 | 
				
			||||||
 | 
					            exe_path = output_dir / exe_name
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            exe_dir = output_dir / script_path.stem
 | 
				
			||||||
 | 
					            exe_name = script_path.stem
 | 
				
			||||||
 | 
					            if os.name == 'nt':
 | 
				
			||||||
 | 
					                exe_name += '.exe'
 | 
				
			||||||
 | 
					            exe_path = exe_dir / exe_name
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        print(f"File: {exe_path}")
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        if exe_path.exists():
 | 
				
			||||||
 | 
					            print("✅ Executable successfully created!")
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            print("⚠️  Executable might not have been created correctly.")
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					    else:
 | 
				
			||||||
 | 
					        print("\nCompilation failed! Check the errors above.")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if __name__ == "__main__":
 | 
				
			||||||
 | 
					    try:
 | 
				
			||||||
 | 
					        main()
 | 
				
			||||||
 | 
					    except KeyboardInterrupt:
 | 
				
			||||||
 | 
					        print("\nProgram interrupted by user!")
 | 
				
			||||||
 | 
					    except Exception as e:
 | 
				
			||||||
 | 
					        print(f"Unexpected error: {e}")
 | 
				
			||||||
		Reference in New Issue
	
	Block a user