Files
dark_hal/build_all_platforms.py

230 lines
7.7 KiB
Python
Raw Permalink Normal View History

2026-03-13 12:56:43 -07:00
#!/usr/bin/env python3
"""
DarkHal 2.0 Build Script
Builds for: Windows x64, Linux x64, Linux ARM64
"""
import os
import sys
import subprocess
import platform
import argparse
from pathlib import Path
# Build configurations for the three target platforms
BUILD_CONFIGS = {
'windows-x64': {
'pyinstaller_args': [
'--onefile',
'--windowed',
'--name', 'DarkHal-2.0-windows-x64',
'--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'
],
'output_name': 'DarkHal-2.0-windows-x64.exe'
},
'linux-x64': {
'pyinstaller_args': [
'--onefile',
'--name', 'DarkHal-2.0-linux-x64',
'--add-data', 'assets/*:assets',
'--add-data', 'llm_runtime:llm_runtime',
'--hidden-import', 'tkinter',
'--hidden-import', 'tkinter.ttk',
'--hidden-import', 'torch',
'--hidden-import', 'transformers'
],
'output_name': 'DarkHal-2.0-linux-x64'
},
'linux-arm64': {
'pyinstaller_args': [
'--onefile',
'--name', 'DarkHal-2.0-linux-arm64',
'--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'
}
}
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"""
import shutil
import glob
dirs_to_remove = ['build', 'dist']
for dir_name in dirs_to_remove:
if os.path.exists(dir_name):
shutil.rmtree(dir_name)
print(f"Cleaned {dir_name}/ directory")
for spec_file in glob.glob('*.spec'):
os.remove(spec_file)
print(f"Cleaned {spec_file}")
def build_executable(target_platform):
"""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"\n{'='*50}")
print(f"Building DarkHal 2.0 for {target_platform}")
print('='*50)
# Check 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
# Create dist directory for this platform
dist_dir = f'dist/{target_platform}'
os.makedirs(dist_dir, exist_ok=True)
# Build PyInstaller command
cmd = ['pyinstaller'] + config['pyinstaller_args'] + [
'--distpath', dist_dir,
'--workpath', f'build/{target_platform}',
'--specpath', f'build/{target_platform}',
'main.py'
]
print(f"Running: {' '.join(cmd)}")
try:
result = subprocess.run(cmd, check=True)
print("✓ PyInstaller completed")
# Find the created executable
expected_path = os.path.join(dist_dir, config['output_name'])
name_without_ext = config['output_name'].replace('.exe', '')
possible_paths = [
expected_path,
os.path.join(dist_dir, name_without_ext),
os.path.join(dist_dir, name_without_ext, name_without_ext),
os.path.join(dist_dir, name_without_ext, name_without_ext + '.exe')
]
executable_path = None
for path in possible_paths:
if os.path.exists(path) and os.path.isfile(path):
executable_path = path
break
if executable_path:
# Move to correct location if needed
final_path = expected_path
if executable_path != final_path:
if os.path.exists(final_path):
os.remove(final_path)
os.rename(executable_path, final_path)
executable_path = final_path
# Make executable on Linux
if target_platform.startswith('linux'):
os.chmod(executable_path, 0o755)
file_size = os.path.getsize(executable_path) / (1024 * 1024)
print(f"✓ SUCCESS! Executable created: {executable_path}")
print(f" File size: {file_size:.1f} MB")
return True
else:
print("✗ Executable not found after build")
print("Files in dist directory:")
for root, dirs, files in os.walk(dist_dir):
for file in files:
print(f" {os.path.join(root, file)}")
return False
except subprocess.CalledProcessError as e:
print(f"✗ Build failed with exit code {e.returncode}")
return False
def main():
parser = argparse.ArgumentParser(description='DarkHal 2.0 Cross-Platform Build Tool')
parser.add_argument('target', nargs='?',
choices=['windows-x64', 'linux-x64', 'linux-arm64', 'all'],
default='all',
help='Target platform to build for')
parser.add_argument('--no-clean', action='store_true', help='Skip cleaning build files')
args = parser.parse_args()
print("DarkHal 2.0 Cross-Platform Builder")
print("Supporting: Windows x64, Linux x64, Linux ARM64")
# Install PyInstaller
if not install_pyinstaller():
sys.exit(1)
# Clean build files unless requested not to
if not args.no_clean:
clean_build_files()
# Build targets
if args.target == 'all':
targets = ['windows-x64', 'linux-x64', 'linux-arm64']
success_count = 0
for target in targets:
if build_executable(target):
success_count += 1
print(f"\n{'='*60}")
print(f"Build Summary: {success_count}/{len(targets)} platforms successful")
print('='*60)
if success_count == len(targets):
print("🎉 All builds completed successfully!")
print("\nExecutables created:")
for target in targets:
config = BUILD_CONFIGS[target]
path = f"dist/{target}/{config['output_name']}"
if os.path.exists(path):
size = os.path.getsize(path) / (1024 * 1024)
print(f" {path} ({size:.1f} MB)")
else:
print(f"{len(targets) - success_count} builds failed!")
sys.exit(1)
else:
if build_executable(args.target):
print("\n🎉 Build completed successfully!")
else:
print("\n❌ Build failed!")
sys.exit(1)
if __name__ == '__main__':
main()