#!/usr/bin/env python3 """ DarkHal 2.0 Cross-Platform Build Script Supports: Windows (x64, ARM64), Linux (x64, ARM64), macOS (x64, ARM64) """ import os import sys import subprocess import platform import argparse from pathlib import Path # Build configurations for different platforms/architectures BUILD_CONFIGS = { 'windows-x64': { 'platform': 'win32', 'arch': 'x64', 'pyinstaller_args': [ '--onefile', '--windowed', '--icon', 'assets/logo.ico', '--add-data', 'assets/*;assets', '--add-data', 'llm_runtime;llm_runtime', '--hidden-import', 'tkinter', '--hidden-import', 'tkinter.ttk', '--hidden-import', 'torch', '--hidden-import', 'transformers', '--target-architecture', 'x86_64' ], 'output_name': 'DarkHal-2.0-windows-x64.exe' }, 'windows-arm64': { 'platform': 'win32', 'arch': 'arm64', 'pyinstaller_args': [ '--onefile', '--windowed', '--icon', 'assets/logo.ico', '--add-data', 'assets/*;assets', '--add-data', 'llm_runtime;llm_runtime', '--hidden-import', 'tkinter', '--hidden-import', 'tkinter.ttk', '--hidden-import', 'torch', '--hidden-import', 'transformers', '--target-architecture', 'arm64' ], 'output_name': 'DarkHal-2.0-windows-arm64.exe' }, 'linux-x64': { 'platform': 'linux', 'arch': 'x64', 'pyinstaller_args': [ '--onefile', '--add-data', 'assets/*:assets', '--add-data', 'llm_runtime:llm_runtime', '--hidden-import', 'tkinter', '--hidden-import', 'tkinter.ttk', '--hidden-import', 'torch', '--hidden-import', 'transformers', '--target-architecture', 'x86_64' ], 'output_name': 'DarkHal-2.0-linux-x64' }, 'linux-arm64': { 'platform': 'linux', 'arch': 'arm64', 'pyinstaller_args': [ '--onefile', '--add-data', 'assets/*:assets', '--add-data', 'llm_runtime:llm_runtime', '--hidden-import', 'tkinter', '--hidden-import', 'tkinter.ttk', '--hidden-import', 'torch', '--hidden-import', 'transformers', '--target-architecture', 'arm64' ], 'output_name': 'DarkHal-2.0-linux-arm64' }, 'macos-x64': { 'platform': 'darwin', 'arch': 'x64', 'pyinstaller_args': [ '--onefile', '--windowed', '--icon', 'assets/logo.icns', '--add-data', 'assets/*:assets', '--add-data', 'llm_runtime:llm_runtime', '--hidden-import', 'tkinter', '--hidden-import', 'tkinter.ttk', '--hidden-import', 'torch', '--hidden-import', 'transformers', '--target-architecture', 'x86_64' ], 'output_name': 'DarkHal-2.0-macos-x64' }, 'macos-arm64': { 'platform': 'darwin', 'arch': 'arm64', 'pyinstaller_args': [ '--onefile', '--windowed', '--icon', 'assets/logo.icns', '--add-data', 'assets/*:assets', '--add-data', 'llm_runtime:llm_runtime', '--hidden-import', 'tkinter', '--hidden-import', 'tkinter.ttk', '--hidden-import', 'torch', '--hidden-import', 'transformers', '--target-architecture', 'arm64' ], 'output_name': 'DarkHal-2.0-macos-arm64' } } def get_current_platform(): """Detect current platform and architecture""" system = platform.system().lower() machine = platform.machine().lower() if system == 'windows': platform_name = 'windows' elif system == 'linux': platform_name = 'linux' elif system == 'darwin': platform_name = 'macos' else: platform_name = system if machine in ['amd64', 'x86_64']: arch = 'x64' elif machine in ['arm64', 'aarch64']: arch = 'arm64' else: arch = machine return f"{platform_name}-{arch}" def install_pyinstaller(): """Install PyInstaller if not present""" try: import PyInstaller print("✓ PyInstaller already installed") return True except ImportError: print("Installing PyInstaller...") try: subprocess.check_call([sys.executable, '-m', 'pip', 'install', 'pyinstaller']) print("✓ PyInstaller installed successfully") return True except subprocess.CalledProcessError as e: print(f"✗ Failed to install PyInstaller: {e}") return False def clean_build_files(): """Clean previous build files""" dirs_to_remove = ['build', 'dist'] files_to_remove = ['*.spec'] for dir_name in dirs_to_remove: if os.path.exists(dir_name): import shutil shutil.rmtree(dir_name) print(f"Cleaned {dir_name}/ directory") import glob for pattern in files_to_remove: for file in glob.glob(pattern): os.remove(file) print(f"Cleaned {file}") def build_executable(target_platform, clean=True): """Build executable for specified platform""" if target_platform not in BUILD_CONFIGS: print(f"✗ Unknown target platform: {target_platform}") print(f"Available platforms: {', '.join(BUILD_CONFIGS.keys())}") return False config = BUILD_CONFIGS[target_platform] print(f"Building DarkHal 2.0 for {target_platform}...") print(f"Platform: {config['platform']}, Architecture: {config['arch']}") # Check if we have required files if not os.path.exists('main.py'): print("✗ main.py not found. Please run from the correct directory.") return False if not os.path.exists('llm_runtime'): print("✗ llm_runtime directory not found") return False # Clean previous builds if clean: clean_build_files() # Build PyInstaller command cmd = [ 'pyinstaller', '--name', config['output_name'].replace('.exe', '').replace('.app', ''), '--distpath', f'dist/{target_platform}', '--workpath', f'build/{target_platform}', '--specpath', f'build/{target_platform}' ] + config['pyinstaller_args'] + ['main.py'] print(f"Running: {' '.join(cmd)}") try: result = subprocess.run(cmd, check=True, capture_output=True, text=True) print("✓ Build completed successfully") # Check output file expected_output = f"dist/{target_platform}/{config['output_name']}" # PyInstaller might create without extension on some platforms possible_outputs = [ expected_output, expected_output.replace('.exe', ''), f"dist/{target_platform}/{config['output_name'].replace('.exe', '').replace('.app', '')}" ] output_file = None for possible in possible_outputs: if os.path.exists(possible): output_file = possible break if output_file: # Rename to correct name if needed if output_file != expected_output and expected_output.endswith('.exe'): os.rename(output_file, expected_output) output_file = expected_output file_size = os.path.getsize(output_file) / (1024 * 1024) # MB print(f"✓ Executable created: {output_file}") print(f" File size: {file_size:.1f} MB") return True else: print("✗ Executable not found after build") return False except subprocess.CalledProcessError as e: print(f"✗ Build failed: {e}") if e.stdout: print("STDOUT:", e.stdout) if e.stderr: print("STDERR:", e.stderr) return False def build_all_platforms(): """Build for all supported platforms""" current = get_current_platform() print(f"Current platform detected: {current}") success_count = 0 total_count = len(BUILD_CONFIGS) for platform in BUILD_CONFIGS.keys(): print(f"\n{'='*60}") print(f"Building for {platform}") print('='*60) if build_executable(platform, clean=False): success_count += 1 else: print(f"Failed to build for {platform}") print(f"\n{'='*60}") print(f"Build Summary: {success_count}/{total_count} platforms successful") print('='*60) if success_count == total_count: print("✓ All builds completed successfully!") return True else: print(f"✗ {total_count - success_count} builds failed") return False def main(): parser = argparse.ArgumentParser(description='DarkHal 2.0 Cross-Platform Build Tool') parser.add_argument('target', nargs='?', choices=list(BUILD_CONFIGS.keys()) + ['all', 'current'], default='current', help='Target platform to build for') parser.add_argument('--no-clean', action='store_true', help='Skip cleaning build files') parser.add_argument('--list', action='store_true', help='List available platforms') args = parser.parse_args() if args.list: print("Available build targets:") for platform, config in BUILD_CONFIGS.items(): print(f" {platform:15} - {config['platform']} {config['arch']}") return # Install PyInstaller if not install_pyinstaller(): return if args.target == 'all': success = build_all_platforms() elif args.target == 'current': current_platform = get_current_platform() if current_platform not in BUILD_CONFIGS: print(f"✗ Current platform {current_platform} not supported") print("Available platforms:", ', '.join(BUILD_CONFIGS.keys())) return success = build_executable(current_platform, not args.no_clean) else: success = build_executable(args.target, not args.no_clean) if success: print("\n🎉 Build completed successfully!") else: print("\n❌ Build failed!") sys.exit(1) if __name__ == '__main__': main()