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