#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ 身体平衡评估系统后端打包脚本 基于最小测试框架的成功经验,简化打包配置 """ import os import sys import shutil import subprocess from pathlib import Path def check_dependencies(): """检查必需的依赖""" print("检查依赖模块...") required_modules = [ 'flask', 'flask_socketio', 'flask_cors', 'socketio', 'engineio', 'numpy', 'cv2', 'psutil' ] missing_modules = [] for module in required_modules: try: __import__(module) print(f"✓ {module}") except ImportError: print(f"✗ {module} (缺失)") missing_modules.append(module) if missing_modules: print(f"\n缺失模块: {', '.join(missing_modules)}") print("请运行: pip install -r requirements_build.txt") return False print("✓ 所有依赖模块检查通过") return True def create_spec_file(): """创建PyInstaller spec文件""" spec_content = ''' # -*- mode: python ; coding: utf-8 -*- a = Analysis( ['main_exe.py'], pathex=[], binaries=[ # 只包含确实存在的DLL文件 ], datas=[ ('config.ini', '.'), # 配置文件 ('data', 'data'), # 数据文件夹 ('tests', 'tests'), # 测试文件夹 ('app_minimal.py', '.'), # 应用入口文件 ], hiddenimports=[ 'flask', 'flask_socketio', 'flask_cors', 'socketio', 'engineio', 'engineio.async_drivers.threading', 'socketio.namespace', 'cv2', 'numpy', 'psutil', 'sqlite3', 'configparser', 'logging', 'threading', 'queue', 'base64', 'requests', 'click', 'colorama', 'database', 'device_manager', 'utils', 'dns', 'dns.resolver', 'dns.asyncresolver' ], hookspath=[], hooksconfig={}, runtime_hooks=[], excludes=[ 'eventlet', 'gevent', 'gevent_uwsgi' ], noarchive=False, ) pyz = PYZ(a.pure) exe = EXE( pyz, a.scripts, a.binaries, a.datas, [], name='BodyBalanceBackend', debug=False, bootloader_ignore_signals=False, strip=False, upx=True, upx_exclude=[], runtime_tmpdir=None, console=True, disable_windowed_traceback=False, argv_emulation=False, target_arch=None, codesign_identity=None, entitlements_file=None, icon=None ) ''' with open('backend.spec', 'w', encoding='utf-8') as f: f.write(spec_content) print("✓ 已创建 backend.spec 文件") def build_exe(): """构建exe文件""" print("开始构建exe文件...") # 检查依赖模块 print("\n检查依赖模块...") if not check_dependencies(): print("\n❌ 依赖检查失败,请先安装缺失的模块") return False # 检查PyInstaller是否安装 try: import PyInstaller print(f"\n✓ PyInstaller版本: {PyInstaller.__version__}") except ImportError: print("❌ PyInstaller未安装,正在安装...") subprocess.run([sys.executable, '-m', 'pip', 'install', 'pyinstaller>=6.10.0']) # 清理之前的构建 print("\n清理构建目录...") if os.path.exists('build'): shutil.rmtree('build') print("✓ 清理build目录") if os.path.exists('dist'): shutil.rmtree('dist') print("✓ 清理dist目录") # 创建spec文件 create_spec_file() # 使用spec文件构建 print("\n开始打包...") cmd = [sys.executable, '-m', 'PyInstaller', 'backend.spec', '--clean', '--noconfirm'] print(f"执行命令: {' '.join(cmd)}") result = subprocess.run(cmd, capture_output=True, text=True, encoding='utf-8') if result.returncode == 0: print("\n✓ 构建成功!") print("exe文件位置: dist/BodyBalanceBackend.exe") # 复制额外的文件 dist_path = Path('dist') if dist_path.exists(): # 复制配置文件(如果存在) config_files = ['config.ini', 'config.json'] for config_file in config_files: if os.path.exists(config_file): shutil.copy2(config_file, dist_path) print(f"✓ 复制配置文件: {config_file}") # 创建启动脚本 start_script = dist_path / 'start_backend.bat' with open(start_script, 'w', encoding='utf-8') as f: f.write('@echo off\n') f.write('chcp 65001 >nul\n') f.write('echo Starting Body Balance Backend...\n') f.write('BodyBalanceBackend.exe\n') f.write('pause\n') print("✓ 创建启动脚本: start_backend.bat") print("\n🎉 打包完成!") print("\n测试方法:") print("1. 直接运行: dist/BodyBalanceBackend.exe") print("2. 使用脚本: dist/start_backend.bat") print("3. 在浏览器中访问: http://localhost:5000") return True else: print("\n❌ 构建失败") print(f"错误信息: {result.stderr}") return False def main(): """主函数""" print("================================================") print("身体平衡评估系统 - 后端打包工具 (简化版)") print("基于最小测试框架的成功经验") print("================================================") print() try: # 检查是否在正确的目录 if not os.path.exists('main_exe.py'): print("❌ 错误:请在backend目录下运行此脚本") print("当前目录应包含 main_exe.py 文件") return # 构建exe if build_exe(): print("\n✅ 所有操作完成!") else: print("\n❌ 构建过程中出现错误") except Exception as e: print(f"❌ 错误: {e}") import traceback traceback.print_exc() input("\n按回车键退出...") if __name__ == '__main__': main()