This commit is contained in:
root
2025-07-11 14:19:45 +08:00
parent dcc2b6684f
commit c2e219c125
9 changed files with 1541 additions and 1 deletions

View File

@@ -0,0 +1,257 @@
#!/usr/bin/env python3
"""
构建脚本 - 将Python Core打包成独立可执行文件
"""
import os
import sys
import subprocess
import shutil
import platform
from pathlib import Path
def get_project_root():
"""获取项目根目录"""
current_file = Path(__file__).resolve()
# 从 scripts/build_python_core.py 回到项目根目录
return current_file.parent.parent
def install_dependencies():
"""安装构建依赖"""
print("📦 Installing build dependencies...")
dependencies = [
'pyinstaller>=5.0',
'requests',
'Pillow',
'opencv-python-headless', # 使用headless版本减少依赖
]
for dep in dependencies:
print(f"Installing {dep}...")
result = subprocess.run([
sys.executable, '-m', 'pip', 'install', dep
], capture_output=True, text=True)
if result.returncode != 0:
print(f"❌ Failed to install {dep}")
print(result.stderr)
return False
else:
print(f"✅ Installed {dep}")
return True
def clean_build_directory(python_core_dir):
"""清理构建目录"""
print("🧹 Cleaning build directories...")
dirs_to_clean = ['build', 'dist', '__pycache__']
for dir_name in dirs_to_clean:
dir_path = python_core_dir / dir_name
if dir_path.exists():
shutil.rmtree(dir_path)
print(f"Removed {dir_path}")
def build_executable(python_core_dir):
"""构建可执行文件"""
print("🔨 Building executable...")
spec_file = python_core_dir / 'build.spec'
if not spec_file.exists():
print(f"❌ Spec file not found: {spec_file}")
return False
# 运行PyInstaller
cmd = [
sys.executable, '-m', 'PyInstaller',
'--clean',
'--noconfirm',
str(spec_file)
]
print(f"Running: {' '.join(cmd)}")
result = subprocess.run(
cmd,
cwd=python_core_dir,
capture_output=True,
text=True
)
if result.returncode != 0:
print("❌ Build failed!")
print("STDOUT:", result.stdout)
print("STDERR:", result.stderr)
return False
print("✅ Build completed successfully!")
return True
def copy_to_sidecar_directory(project_root, python_core_dir):
"""复制可执行文件到Tauri sidecar目录"""
print("📁 Copying executable to sidecar directory...")
# 确定可执行文件名
system = platform.system().lower()
if system == 'windows':
exe_name = 'mixvideo-python-core.exe'
else:
exe_name = 'mixvideo-python-core'
# 源文件路径
source_exe = python_core_dir / 'dist' / exe_name
if not source_exe.exists():
print(f"❌ Executable not found: {source_exe}")
return False
# 目标目录
sidecar_dir = project_root / 'src-tauri' / 'binaries'
sidecar_dir.mkdir(exist_ok=True)
# 复制文件
target_exe = sidecar_dir / exe_name
shutil.copy2(source_exe, target_exe)
# 在Unix系统上设置执行权限
if system != 'windows':
os.chmod(target_exe, 0o755)
print(f"✅ Copied to: {target_exe}")
return True
def update_tauri_config(project_root):
"""更新Tauri配置文件以包含sidecar"""
print("⚙️ Updating Tauri configuration...")
import json
config_file = project_root / 'src-tauri' / 'tauri.conf.json'
if not config_file.exists():
print(f"❌ Tauri config not found: {config_file}")
return False
# 读取配置
with open(config_file, 'r', encoding='utf-8') as f:
config = json.load(f)
# 添加sidecar配置
if 'bundle' not in config:
config['bundle'] = {}
if 'externalBin' not in config['bundle']:
config['bundle']['externalBin'] = []
# 检查是否已经存在
sidecar_name = 'mixvideo-python-core'
existing = any(
item.get('name') == sidecar_name
for item in config['bundle']['externalBin']
if isinstance(item, dict)
)
if not existing:
config['bundle']['externalBin'].append({
"name": sidecar_name,
"src": f"binaries/{sidecar_name}",
"targets": ["all"]
})
# 写回配置
with open(config_file, 'w', encoding='utf-8') as f:
json.dump(config, f, indent=2, ensure_ascii=False)
print("✅ Updated Tauri configuration")
else:
print(" Sidecar already configured in Tauri")
return True
def verify_build(project_root):
"""验证构建结果"""
print("🔍 Verifying build...")
system = platform.system().lower()
exe_name = 'mixvideo-python-core.exe' if system == 'windows' else 'mixvideo-python-core'
sidecar_exe = project_root / 'src-tauri' / 'binaries' / exe_name
if not sidecar_exe.exists():
print(f"❌ Sidecar executable not found: {sidecar_exe}")
return False
# 测试执行
try:
result = subprocess.run([
str(sidecar_exe), '--version'
], capture_output=True, text=True, timeout=10)
if result.returncode == 0:
print("✅ Executable is working correctly")
print(f"Version output: {result.stdout.strip()}")
return True
else:
print(f"❌ Executable test failed: {result.stderr}")
return False
except subprocess.TimeoutExpired:
print("❌ Executable test timed out")
return False
except Exception as e:
print(f"❌ Executable test error: {e}")
return False
def main():
"""主函数"""
print("🚀 Building MixVideo V2 Python Core...")
project_root = get_project_root()
python_core_dir = project_root / 'python_core'
print(f"Project root: {project_root}")
print(f"Python core directory: {python_core_dir}")
if not python_core_dir.exists():
print(f"❌ Python core directory not found: {python_core_dir}")
sys.exit(1)
# 步骤1: 安装依赖
if not install_dependencies():
print("❌ Failed to install dependencies")
sys.exit(1)
# 步骤2: 清理构建目录
clean_build_directory(python_core_dir)
# 步骤3: 构建可执行文件
if not build_executable(python_core_dir):
print("❌ Failed to build executable")
sys.exit(1)
# 步骤4: 复制到sidecar目录
if not copy_to_sidecar_directory(project_root, python_core_dir):
print("❌ Failed to copy executable")
sys.exit(1)
# 步骤5: 更新Tauri配置
if not update_tauri_config(project_root):
print("❌ Failed to update Tauri configuration")
sys.exit(1)
# 步骤6: 验证构建
if not verify_build(project_root):
print("❌ Build verification failed")
sys.exit(1)
print("🎉 Build completed successfully!")
print("\n📋 Next steps:")
print("1. Test the sidecar: cargo tauri dev")
print("2. Build the app: cargo tauri build")
print("3. The Python core will be bundled with your app")
if __name__ == "__main__":
main()