230 lines
7.7 KiB
Python
230 lines
7.7 KiB
Python
|
|
#!/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()
|